py: Reformat all Python files consistently

This commit is the unmodified results of running
```
black $(git ls-files '*.py')
```
with black version 19.10b0. See #2046.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
This commit is contained in:
Philip Withnall 2020-11-17 15:07:09 +00:00
parent 053d2ae2b4
commit 905b22a17e
26 changed files with 6928 additions and 4875 deletions

View File

@ -23,18 +23,18 @@ import sys
# thats conventionally used as a way of marking a workaround which needs to
# be merged for now, but is to be grepped for and reverted or reworked later.
BANNED_KEYWORDS = [
'TO' + 'DO',
'X' + 'XX',
'W' + 'IP',
"TO" + "DO",
"X" + "XX",
"W" + "IP",
]
def main():
parser = argparse.ArgumentParser(
description='Check a range of commits to ensure they dont contain '
'banned keywords.')
parser.add_argument('commits',
help='SHA to diff from, or range of commits to diff')
description="Check a range of commits to ensure they dont contain "
"banned keywords."
)
parser.add_argument("commits", help="SHA to diff from, or range of commits to diff")
args = parser.parse_args()
banned_words_seen = set()
@ -43,47 +43,55 @@ def main():
# Check the log messages for banned words.
log_process = subprocess.run(
['git', 'log', '--no-color', args.commits + '..HEAD'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8',
check=True)
log_lines = log_process.stdout.strip().split('\n')
["git", "log", "--no-color", args.commits + "..HEAD"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
check=True,
)
log_lines = log_process.stdout.strip().split("\n")
for line in log_lines:
for keyword in BANNED_KEYWORDS:
if re.search('(^|\W+){}(\W+|$)'.format(keyword), line):
if re.search("(^|\W+){}(\W+|$)".format(keyword), line):
banned_words_seen.add(keyword)
seen_in_log = True
# Check the diff for banned words.
diff_process = subprocess.run(
['git', 'diff', '-U0', '--no-color', args.commits],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8',
check=True)
diff_lines = diff_process.stdout.strip().split('\n')
["git", "diff", "-U0", "--no-color", args.commits],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
check=True,
)
diff_lines = diff_process.stdout.strip().split("\n")
for line in diff_lines:
if not line.startswith('+ '):
if not line.startswith("+ "):
continue
for keyword in BANNED_KEYWORDS:
if re.search('(^|\W+){}(\W+|$)'.format(keyword), line):
if re.search("(^|\W+){}(\W+|$)".format(keyword), line):
banned_words_seen.add(keyword)
seen_in_diff = True
if banned_words_seen:
if seen_in_log and seen_in_diff:
where = 'commit message and diff'
where = "commit message and diff"
elif seen_in_log:
where = 'commit message'
where = "commit message"
elif seen_in_diff:
where = 'commit diff'
where = "commit diff"
print('Saw banned keywords in a {}: {}. '
'This indicates the branch is a work in progress and should not '
'be merged in its current '
'form.'.format(where, ', '.join(banned_words_seen)))
print(
"Saw banned keywords in a {}: {}. "
"This indicates the branch is a work in progress and should not "
"be merged in its current "
"form.".format(where, ", ".join(banned_words_seen))
)
sys.exit(1)
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@ -15,101 +15,112 @@ import os
import sys
import xml.etree.ElementTree as ET
aparser = argparse.ArgumentParser(description='Turns a Meson test log into a JUnit report')
aparser.add_argument('--project-name', metavar='NAME',
help='The project name',
default='unknown')
aparser.add_argument('--job-id', metavar='ID',
help='The job ID for the report',
default='Unknown')
aparser.add_argument('--branch', metavar='NAME',
help='Branch of the project being tested',
default='master')
aparser.add_argument('--output', metavar='FILE',
help='The output file, stdout by default',
type=argparse.FileType('w', encoding='UTF-8'),
default=sys.stdout)
aparser.add_argument('infile', metavar='FILE',
help='The input testlog.json, stdin by default',
type=argparse.FileType('r', encoding='UTF-8'),
default=sys.stdin)
aparser = argparse.ArgumentParser(
description="Turns a Meson test log into a JUnit report"
)
aparser.add_argument(
"--project-name", metavar="NAME", help="The project name", default="unknown"
)
aparser.add_argument(
"--job-id", metavar="ID", help="The job ID for the report", default="Unknown"
)
aparser.add_argument(
"--branch",
metavar="NAME",
help="Branch of the project being tested",
default="master",
)
aparser.add_argument(
"--output",
metavar="FILE",
help="The output file, stdout by default",
type=argparse.FileType("w", encoding="UTF-8"),
default=sys.stdout,
)
aparser.add_argument(
"infile",
metavar="FILE",
help="The input testlog.json, stdin by default",
type=argparse.FileType("r", encoding="UTF-8"),
default=sys.stdin,
)
args = aparser.parse_args()
outfile = args.output
testsuites = ET.Element('testsuites')
testsuites.set('id', '{}/{}'.format(args.job_id, args.branch))
testsuites.set('package', args.project_name)
testsuites.set('timestamp', datetime.datetime.utcnow().isoformat())
testsuites = ET.Element("testsuites")
testsuites.set("id", "{}/{}".format(args.job_id, args.branch))
testsuites.set("package", args.project_name)
testsuites.set("timestamp", datetime.datetime.utcnow().isoformat())
suites = {}
for line in args.infile:
data = json.loads(line)
(full_suite, unit_name) = data['name'].split(' / ')
(full_suite, unit_name) = data["name"].split(" / ")
try:
(project_name, suite_name) = full_suite.split(':')
(project_name, suite_name) = full_suite.split(":")
except ValueError:
project_name = full_suite
suite_name = full_suite
duration = data['duration']
return_code = data['returncode']
log = data['stdout']
log_stderr = data.get('stderr', '')
duration = data["duration"]
return_code = data["returncode"]
log = data["stdout"]
log_stderr = data.get("stderr", "")
unit = {
'suite': suite_name,
'name': unit_name,
'duration': duration,
'returncode': return_code,
'stdout': log,
'stderr': log_stderr,
"suite": suite_name,
"name": unit_name,
"duration": duration,
"returncode": return_code,
"stdout": log,
"stderr": log_stderr,
}
units = suites.setdefault(suite_name, [])
units.append(unit)
for name, units in suites.items():
print('Processing suite {} (units: {})'.format(name, len(units)))
print("Processing suite {} (units: {})".format(name, len(units)))
def if_failed(unit):
if unit['returncode'] != 0:
if unit["returncode"] != 0:
return True
return False
def if_succeded(unit):
if unit['returncode'] == 0:
if unit["returncode"] == 0:
return True
return False
successes = list(filter(if_succeded, units))
failures = list(filter(if_failed, units))
print(' - {}: {} pass, {} fail'.format(name, len(successes), len(failures)))
print(" - {}: {} pass, {} fail".format(name, len(successes), len(failures)))
testsuite = ET.SubElement(testsuites, 'testsuite')
testsuite.set('name', '{}/{}'.format(args.project_name, name))
testsuite.set('tests', str(len(units)))
testsuite.set('errors', str(len(failures)))
testsuite.set('failures', str(len(failures)))
testsuite = ET.SubElement(testsuites, "testsuite")
testsuite.set("name", "{}/{}".format(args.project_name, name))
testsuite.set("tests", str(len(units)))
testsuite.set("errors", str(len(failures)))
testsuite.set("failures", str(len(failures)))
for unit in successes:
testcase = ET.SubElement(testsuite, 'testcase')
testcase.set('classname', '{}/{}'.format(args.project_name, unit['suite']))
testcase.set('name', unit['name'])
testcase.set('time', str(unit['duration']))
testcase = ET.SubElement(testsuite, "testcase")
testcase.set("classname", "{}/{}".format(args.project_name, unit["suite"]))
testcase.set("name", unit["name"])
testcase.set("time", str(unit["duration"]))
for unit in failures:
testcase = ET.SubElement(testsuite, 'testcase')
testcase.set('classname', '{}/{}'.format(args.project_name, unit['suite']))
testcase.set('name', unit['name'])
testcase.set('time', str(unit['duration']))
testcase = ET.SubElement(testsuite, "testcase")
testcase.set("classname", "{}/{}".format(args.project_name, unit["suite"]))
testcase.set("name", unit["name"])
testcase.set("time", str(unit["duration"]))
failure = ET.SubElement(testcase, 'failure')
failure.set('classname', '{}/{}'.format(args.project_name, unit['suite']))
failure.set('name', unit['name'])
failure.set('type', 'error')
failure.text = unit['stdout'] + '\n' + unit['stderr']
failure = ET.SubElement(testcase, "failure")
failure.set("classname", "{}/{}".format(args.project_name, unit["suite"]))
failure.set("name", unit["name"])
failure.set("type", "error")
failure.text = unit["stdout"] + "\n" + unit["stderr"]
output = ET.tostring(testsuites, encoding='unicode')
output = ET.tostring(testsuites, encoding="unicode")
outfile.write(output)

View File

@ -33,50 +33,76 @@ else:
def main():
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('-i', action='store_true', default=False,
help='apply edits to files instead of displaying a '
'diff')
parser.add_argument('-p', metavar='NUM', default=0,
help='strip the smallest prefix containing P slashes')
parser.add_argument('-regex', metavar='PATTERN', default=None,
help='custom pattern selecting file paths to reformat '
'(case sensitive, overrides -iregex)')
parser.add_argument('-iregex', metavar='PATTERN',
default=r'.*\.(cpp|cc|c\+\+|cxx|c|cl|h|hh|hpp|m|mm|inc'
r'|js|ts|proto|protodevel|java|cs)',
help='custom pattern selecting file paths to reformat '
'(case insensitive, overridden by -regex)')
parser.add_argument('-sort-includes', action='store_true', default=False,
help='let clang-format sort include blocks')
parser.add_argument('-v', '--verbose', action='store_true',
help='be more verbose, ineffective without -i')
parser.add_argument('-style',
help='formatting style to apply (LLVM, Google, '
'Chromium, Mozilla, WebKit)')
parser.add_argument('-binary', default='clang-format',
help='location of binary to use for clang-format')
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument(
"-i",
action="store_true",
default=False,
help="apply edits to files instead of displaying a " "diff",
)
parser.add_argument(
"-p",
metavar="NUM",
default=0,
help="strip the smallest prefix containing P slashes",
)
parser.add_argument(
"-regex",
metavar="PATTERN",
default=None,
help="custom pattern selecting file paths to reformat "
"(case sensitive, overrides -iregex)",
)
parser.add_argument(
"-iregex",
metavar="PATTERN",
default=r".*\.(cpp|cc|c\+\+|cxx|c|cl|h|hh|hpp|m|mm|inc"
r"|js|ts|proto|protodevel|java|cs)",
help="custom pattern selecting file paths to reformat "
"(case insensitive, overridden by -regex)",
)
parser.add_argument(
"-sort-includes",
action="store_true",
default=False,
help="let clang-format sort include blocks",
)
parser.add_argument(
"-v",
"--verbose",
action="store_true",
help="be more verbose, ineffective without -i",
)
parser.add_argument(
"-style",
help="formatting style to apply (LLVM, Google, " "Chromium, Mozilla, WebKit)",
)
parser.add_argument(
"-binary",
default="clang-format",
help="location of binary to use for clang-format",
)
args = parser.parse_args()
# Extract changed lines for each file.
filename = None
lines_by_file = {}
for line in sys.stdin:
match = re.search(r'^\+\+\+\ (.*?/){%s}(\S*)' % args.p, line)
match = re.search(r"^\+\+\+\ (.*?/){%s}(\S*)" % args.p, line)
if match:
filename = match.group(2)
if filename is None:
continue
if args.regex is not None:
if not re.match('^%s$' % args.regex, filename):
if not re.match("^%s$" % args.regex, filename):
continue
else:
if not re.match('^%s$' % args.iregex, filename, re.IGNORECASE):
if not re.match("^%s$" % args.iregex, filename, re.IGNORECASE):
continue
match = re.search(r'^@@.*\+(\d+)(,(\d+))?', line)
match = re.search(r"^@@.*\+(\d+)(,(\d+))?", line)
if match:
start_line = int(match.group(1))
line_count = 1
@ -86,7 +112,8 @@ def main():
continue
end_line = start_line + line_count - 1
lines_by_file.setdefault(filename, []).extend(
['-lines', str(start_line) + ':' + str(end_line)])
["-lines", str(start_line) + ":" + str(end_line)]
)
# Reformat files containing changes in place.
# We need to count amount of bytes generated in the output of
@ -95,20 +122,22 @@ def main():
format_line_counter = 0
for filename, lines in lines_by_file.items():
if args.i and args.verbose:
print('Formatting {}'.format(filename))
print("Formatting {}".format(filename))
command = [args.binary, filename]
if args.i:
command.append('-i')
command.append("-i")
if args.sort_includes:
command.append('-sort-includes')
command.append("-sort-includes")
command.extend(lines)
if args.style:
command.extend(['-style', args.style])
p = subprocess.Popen(command,
stdout=subprocess.PIPE,
stderr=None,
stdin=subprocess.PIPE,
universal_newlines=True)
command.extend(["-style", args.style])
p = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=None,
stdin=subprocess.PIPE,
universal_newlines=True,
)
stdout, _ = p.communicate()
if p.returncode != 0:
sys.exit(p.returncode)
@ -117,11 +146,15 @@ def main():
with open(filename) as f:
code = f.readlines()
formatted_code = StringIO(stdout).readlines()
diff = difflib.unified_diff(code, formatted_code,
filename, filename,
'(before formatting)',
'(after formatting)')
diff_string = ''.join(diff)
diff = difflib.unified_diff(
code,
formatted_code,
filename,
filename,
"(before formatting)",
"(after formatting)",
)
diff_string = "".join(diff)
if diff_string:
format_line_counter += sys.stdout.write(diff_string)
@ -129,5 +162,5 @@ def main():
sys.exit(1)
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@ -21,10 +21,14 @@
import sys
if len(sys.argv) < 3:
print('Usage: {} <output file> <input file 1> ...'.format(os.path.basename(sys.argv[0])))
sys.exit(1)
print(
"Usage: {} <output file> <input file 1> ...".format(
os.path.basename(sys.argv[0])
)
)
sys.exit(1)
with open(sys.argv[1], 'w') as outfile:
with open(sys.argv[1], "w") as outfile:
for fname in sys.argv[2:]:
with open(fname) as infile:
for line in infile:

View File

@ -3,14 +3,14 @@
import sys
if len(sys.argv) < 4:
print('Usage: {0} <filename> <variable> <output>')
print("Usage: {0} <filename> <variable> <output>")
with open(sys.argv[1], 'rb') as f:
in_data = f.read().decode('utf-8', 'backslashreplace')
b = [r'\x{:02x}'.format(ord(c)) for c in in_data]
with open(sys.argv[1], "rb") as f:
in_data = f.read().decode("utf-8", "backslashreplace")
b = [r"\x{:02x}".format(ord(c)) for c in in_data]
out_data = "const char {0}[] = \"".format(sys.argv[2])
out_data += "".join(b) + "\";"
out_data = 'const char {0}[] = "'.format(sys.argv[2])
out_data += "".join(b) + '";'
with open(sys.argv[3], 'w') as f:
with open(sys.argv[3], "w") as f:
f.write(out_data)

View File

@ -21,7 +21,9 @@
import os
builddir = os.environ.get('UNINSTALLED_GLIB_BUILDDIR')
builddir = os.environ.get("UNINSTALLED_GLIB_BUILDDIR")
if builddir is not None:
__path__.append(os.path.abspath(os.path.join(builddir, 'gio', 'gdbus-2.0', 'codegen')))
__path__.append(
os.path.abspath(os.path.join(builddir, "gio", "gdbus-2.0", "codegen"))
)

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,7 @@ from . import parser
# ----------------------------------------------------------------------------------------------------
class DocbookCodeGenerator:
def __init__(self, ifaces):
self.ifaces = ifaces
@ -57,23 +58,36 @@ class DocbookCodeGenerator:
max_signature_len = max(len(a.signature), max_signature_len)
if in_synopsis:
self.out.write('<link linkend="gdbus-method-%s.%s">%s</link>%*s ('
%(utils.dots_to_hyphens(i.name), m.name, m.name, max_method_len - len(m.name), ''))
self.out.write(
'<link linkend="gdbus-method-%s.%s">%s</link>%*s ('
% (
utils.dots_to_hyphens(i.name),
m.name,
m.name,
max_method_len - len(m.name),
"",
)
)
else:
self.out.write('%s%*s ('
%(m.name, max_method_len - len(m.name), ''))
self.out.write("%s%*s (" % (m.name, max_method_len - len(m.name), ""))
count = 0
for a in m.in_args:
if (count > 0):
self.out.write(',\n%*s'%(max_method_len + 2, ''))
self.out.write('IN %s%*s %s'%(a.signature, max_signature_len - len(a.signature), '', a.name))
if count > 0:
self.out.write(",\n%*s" % (max_method_len + 2, ""))
self.out.write(
"IN %s%*s %s"
% (a.signature, max_signature_len - len(a.signature), "", a.name)
)
count = count + 1
for a in m.out_args:
if (count > 0):
self.out.write(',\n%*s'%(max_method_len + 2, ''))
self.out.write('OUT %s%*s %s'%(a.signature, max_signature_len - len(a.signature), '', a.name))
if count > 0:
self.out.write(",\n%*s" % (max_method_len + 2, ""))
self.out.write(
"OUT %s%*s %s"
% (a.signature, max_signature_len - len(a.signature), "", a.name)
)
count = count + 1
self.out.write(');\n')
self.out.write(");\n")
def print_signal_prototype(self, i, s, in_synopsis):
max_signal_len = 0
@ -93,18 +107,28 @@ class DocbookCodeGenerator:
max_signature_len = max(len(a.signature), max_signature_len)
if in_synopsis:
self.out.write('<link linkend="gdbus-signal-%s.%s">%s</link>%*s ('
%(utils.dots_to_hyphens(i.name), s.name, s.name, max_signal_len - len(s.name), ''))
self.out.write(
'<link linkend="gdbus-signal-%s.%s">%s</link>%*s ('
% (
utils.dots_to_hyphens(i.name),
s.name,
s.name,
max_signal_len - len(s.name),
"",
)
)
else:
self.out.write('%s%*s ('
%(s.name, max_signal_len - len(s.name), ''))
self.out.write("%s%*s (" % (s.name, max_signal_len - len(s.name), ""))
count = 0
for a in s.args:
if (count > 0):
self.out.write(',\n%*s'%(max_signal_len + 2, ''))
self.out.write('%s%*s %s'%(a.signature, max_signature_len - len(a.signature), '', a.name))
if count > 0:
self.out.write(",\n%*s" % (max_signal_len + 2, ""))
self.out.write(
"%s%*s %s"
% (a.signature, max_signature_len - len(a.signature), "", a.name)
)
count = count + 1
self.out.write(');\n')
self.out.write(");\n")
def print_property_prototype(self, i, p, in_synopsis):
max_property_len = 0
@ -122,109 +146,181 @@ class DocbookCodeGenerator:
max_signature_len = max(len(p.signature), max_signature_len)
if in_synopsis:
self.out.write('<link linkend="gdbus-property-%s.%s">%s</link>%*s'
%(utils.dots_to_hyphens(i.name), p.name, p.name, max_property_len - len(p.name), ''))
self.out.write(
'<link linkend="gdbus-property-%s.%s">%s</link>%*s'
% (
utils.dots_to_hyphens(i.name),
p.name,
p.name,
max_property_len - len(p.name),
"",
)
)
else:
self.out.write('%s%*s'
%(p.name, max_property_len - len(p.name), ''))
self.out.write("%s%*s" % (p.name, max_property_len - len(p.name), ""))
if p.readable and p.writable:
access = 'readwrite'
access = "readwrite"
elif p.readable:
access = 'readable '
access = "readable "
else:
access = 'writable '
self.out.write(' %s %s\n'%(access, p.signature))
access = "writable "
self.out.write(" %s %s\n" % (access, p.signature))
def print_synopsis_methods(self, i):
self.out.write(' <refsynopsisdiv role="synopsis">\n'%())
self.out.write(' <title role="synopsis.title">Methods</title>\n'%())
self.out.write(' <synopsis>\n'%())
self.out.write(' <refsynopsisdiv role="synopsis">\n' % ())
self.out.write(' <title role="synopsis.title">Methods</title>\n' % ())
self.out.write(" <synopsis>\n" % ())
for m in i.methods:
self.print_method_prototype(i, m, in_synopsis=True)
self.out.write('</synopsis>\n'%())
self.out.write(' </refsynopsisdiv>\n'%())
self.out.write("</synopsis>\n" % ())
self.out.write(" </refsynopsisdiv>\n" % ())
def print_synopsis_signals(self, i):
self.out.write(' <refsect1 role="signal_proto">\n'%())
self.out.write(' <title role="signal_proto.title">Signals</title>\n'%())
self.out.write(' <synopsis>\n'%())
self.out.write(' <refsect1 role="signal_proto">\n' % ())
self.out.write(' <title role="signal_proto.title">Signals</title>\n' % ())
self.out.write(" <synopsis>\n" % ())
for s in i.signals:
self.print_signal_prototype(i, s, in_synopsis=True)
self.out.write('</synopsis>\n'%())
self.out.write(' </refsect1>\n'%())
self.out.write("</synopsis>\n" % ())
self.out.write(" </refsect1>\n" % ())
def print_synopsis_properties(self, i):
self.out.write(' <refsect1 role="properties">\n'%())
self.out.write(' <title role="properties.title">Properties</title>\n'%())
self.out.write(' <synopsis>\n'%())
self.out.write(' <refsect1 role="properties">\n' % ())
self.out.write(' <title role="properties.title">Properties</title>\n' % ())
self.out.write(" <synopsis>\n" % ())
for p in i.properties:
self.print_property_prototype(i, p, in_synopsis=True)
self.out.write('</synopsis>\n'%())
self.out.write(' </refsect1>\n'%())
self.out.write("</synopsis>\n" % ())
self.out.write(" </refsect1>\n" % ())
def print_method(self, i, m):
self.out.write('<refsect2 role="method" id="gdbus-method-%s.%s">\n'%(utils.dots_to_hyphens(i.name), m.name))
self.out.write(' <title>The %s() method</title>\n'%(m.name))
self.out.write(' <indexterm zone="gdbus-method-%s.%s"><primary sortas="%s.%s">%s.%s()</primary></indexterm>\n'%(utils.dots_to_hyphens(i.name), m.name, i.name_without_prefix, m.name, i.name, m.name))
self.out.write('<programlisting>\n')
self.out.write(
'<refsect2 role="method" id="gdbus-method-%s.%s">\n'
% (utils.dots_to_hyphens(i.name), m.name)
)
self.out.write(" <title>The %s() method</title>\n" % (m.name))
self.out.write(
' <indexterm zone="gdbus-method-%s.%s"><primary sortas="%s.%s">%s.%s()</primary></indexterm>\n'
% (
utils.dots_to_hyphens(i.name),
m.name,
i.name_without_prefix,
m.name,
i.name,
m.name,
)
)
self.out.write("<programlisting>\n")
self.print_method_prototype(i, m, in_synopsis=False)
self.out.write('</programlisting>\n')
self.out.write('%s\n'%(self.expand_paras(m.doc_string, True)))
self.out.write("</programlisting>\n")
self.out.write("%s\n" % (self.expand_paras(m.doc_string, True)))
if m.in_args or m.out_args:
self.out.write('<variablelist role="params">\n')
for a in m.in_args:
self.out.write('<varlistentry>\n'%())
self.out.write(' <term><literal>IN %s <parameter>%s</parameter></literal>:</term>\n'%(a.signature, a.name))
self.out.write(' <listitem>%s</listitem>\n'%(self.expand_paras(a.doc_string, True)))
self.out.write('</varlistentry>\n'%())
self.out.write("<varlistentry>\n" % ())
self.out.write(
" <term><literal>IN %s <parameter>%s</parameter></literal>:</term>\n"
% (a.signature, a.name)
)
self.out.write(
" <listitem>%s</listitem>\n"
% (self.expand_paras(a.doc_string, True))
)
self.out.write("</varlistentry>\n" % ())
for a in m.out_args:
self.out.write('<varlistentry>\n'%())
self.out.write(' <term><literal>OUT %s <parameter>%s</parameter></literal>:</term>\n'%(a.signature, a.name))
self.out.write(' <listitem>%s</listitem>\n'%(self.expand_paras(a.doc_string, True)))
self.out.write('</varlistentry>\n'%())
self.out.write('</variablelist>\n')
self.out.write("<varlistentry>\n" % ())
self.out.write(
" <term><literal>OUT %s <parameter>%s</parameter></literal>:</term>\n"
% (a.signature, a.name)
)
self.out.write(
" <listitem>%s</listitem>\n"
% (self.expand_paras(a.doc_string, True))
)
self.out.write("</varlistentry>\n" % ())
self.out.write("</variablelist>\n")
if len(m.since) > 0:
self.out.write('<para role="since">Since %s</para>\n'%(m.since))
self.out.write('<para role="since">Since %s</para>\n' % (m.since))
if m.deprecated:
self.out.write('<warning><para>The %s() method is deprecated.</para></warning>'%(m.name))
self.out.write('</refsect2>\n')
self.out.write(
"<warning><para>The %s() method is deprecated.</para></warning>"
% (m.name)
)
self.out.write("</refsect2>\n")
def print_signal(self, i, s):
self.out.write('<refsect2 role="signal" id="gdbus-signal-%s.%s">\n'%(utils.dots_to_hyphens(i.name), s.name))
self.out.write(' <title>The "%s" signal</title>\n'%(s.name))
self.out.write(' <indexterm zone="gdbus-signal-%s.%s"><primary sortas="%s::%s">%s::%s</primary></indexterm>\n'%(utils.dots_to_hyphens(i.name), s.name, i.name_without_prefix, s.name, i.name, s.name))
self.out.write('<programlisting>\n')
self.out.write(
'<refsect2 role="signal" id="gdbus-signal-%s.%s">\n'
% (utils.dots_to_hyphens(i.name), s.name)
)
self.out.write(' <title>The "%s" signal</title>\n' % (s.name))
self.out.write(
' <indexterm zone="gdbus-signal-%s.%s"><primary sortas="%s::%s">%s::%s</primary></indexterm>\n'
% (
utils.dots_to_hyphens(i.name),
s.name,
i.name_without_prefix,
s.name,
i.name,
s.name,
)
)
self.out.write("<programlisting>\n")
self.print_signal_prototype(i, s, in_synopsis=False)
self.out.write('</programlisting>\n')
self.out.write('%s\n'%(self.expand_paras(s.doc_string, True)))
self.out.write("</programlisting>\n")
self.out.write("%s\n" % (self.expand_paras(s.doc_string, True)))
if s.args:
self.out.write('<variablelist role="params">\n')
for a in s.args:
self.out.write('<varlistentry>\n'%())
self.out.write(' <term><literal>%s <parameter>%s</parameter></literal>:</term>\n'%(a.signature, a.name))
self.out.write(' <listitem>%s</listitem>\n'%(self.expand_paras(a.doc_string, True)))
self.out.write('</varlistentry>\n'%())
self.out.write('</variablelist>\n')
self.out.write("<varlistentry>\n" % ())
self.out.write(
" <term><literal>%s <parameter>%s</parameter></literal>:</term>\n"
% (a.signature, a.name)
)
self.out.write(
" <listitem>%s</listitem>\n"
% (self.expand_paras(a.doc_string, True))
)
self.out.write("</varlistentry>\n" % ())
self.out.write("</variablelist>\n")
if len(s.since) > 0:
self.out.write('<para role="since">Since %s</para>\n'%(s.since))
self.out.write('<para role="since">Since %s</para>\n' % (s.since))
if s.deprecated:
self.out.write('<warning><para>The "%s" signal is deprecated.</para></warning>'%(s.name))
self.out.write('</refsect2>\n')
self.out.write(
'<warning><para>The "%s" signal is deprecated.</para></warning>'
% (s.name)
)
self.out.write("</refsect2>\n")
def print_property(self, i, p):
self.out.write('<refsect2 role="property" id="gdbus-property-%s.%s">\n'%(utils.dots_to_hyphens(i.name), p.name))
self.out.write(' <title>The "%s" property</title>\n'%(p.name))
self.out.write(' <indexterm zone="gdbus-property-%s.%s"><primary sortas="%s:%s">%s:%s</primary></indexterm>\n'%(utils.dots_to_hyphens(i.name), p.name, i.name_without_prefix, p.name, i.name, p.name))
self.out.write('<programlisting>\n')
self.out.write(
'<refsect2 role="property" id="gdbus-property-%s.%s">\n'
% (utils.dots_to_hyphens(i.name), p.name)
)
self.out.write(' <title>The "%s" property</title>\n' % (p.name))
self.out.write(
' <indexterm zone="gdbus-property-%s.%s"><primary sortas="%s:%s">%s:%s</primary></indexterm>\n'
% (
utils.dots_to_hyphens(i.name),
p.name,
i.name_without_prefix,
p.name,
i.name,
p.name,
)
)
self.out.write("<programlisting>\n")
self.print_property_prototype(i, p, in_synopsis=False)
self.out.write('</programlisting>\n')
self.out.write('%s\n'%(self.expand_paras(p.doc_string, True)))
self.out.write("</programlisting>\n")
self.out.write("%s\n" % (self.expand_paras(p.doc_string, True)))
if len(p.since) > 0:
self.out.write('<para role="since">Since %s</para>\n'%(p.since))
self.out.write('<para role="since">Since %s</para>\n' % (p.since))
if p.deprecated:
self.out.write('<warning><para>The "%s" property is deprecated.</para></warning>'%(p.name))
self.out.write('</refsect2>\n')
self.out.write(
'<warning><para>The "%s" property is deprecated.</para></warning>'
% (p.name)
)
self.out.write("</refsect2>\n")
def expand(self, s, expandParamsAndConstants):
for key in self.expand_member_dict_keys:
@ -233,9 +329,17 @@ class DocbookCodeGenerator:
s = s.replace(key, self.expand_iface_dict[key])
if expandParamsAndConstants:
# replace @foo with <parameter>foo</parameter>
s = re.sub('@[a-zA-Z0-9_]*', lambda m: '<parameter>' + m.group(0)[1:] + '</parameter>', s)
s = re.sub(
"@[a-zA-Z0-9_]*",
lambda m: "<parameter>" + m.group(0)[1:] + "</parameter>",
s,
)
# replace e.g. %TRUE with <constant>TRUE</constant>
s = re.sub('%[a-zA-Z0-9_]*', lambda m: '<constant>' + m.group(0)[1:] + '</constant>', s)
s = re.sub(
"%[a-zA-Z0-9_]*",
lambda m: "<constant>" + m.group(0)[1:] + "</constant>",
s,
)
return s
def expand_paras(self, s, expandParamsAndConstants):
@ -248,44 +352,75 @@ class DocbookCodeGenerator:
self.expand_member_dict = {}
self.expand_iface_dict = {}
for i in self.ifaces:
key = '#%s'%(i.name)
value = '<link linkend="gdbus-interface-%s.top_of_page">%s</link>'%(utils.dots_to_hyphens(i.name), i.name)
key = "#%s" % (i.name)
value = '<link linkend="gdbus-interface-%s.top_of_page">%s</link>' % (
utils.dots_to_hyphens(i.name),
i.name,
)
self.expand_iface_dict[key] = value
for m in i.methods:
key = '%s.%s()'%(i.name, m.name)
value = '<link linkend="gdbus-method-%s.%s">%s()</link>'%(utils.dots_to_hyphens(i.name), m.name, m.name)
key = "%s.%s()" % (i.name, m.name)
value = '<link linkend="gdbus-method-%s.%s">%s()</link>' % (
utils.dots_to_hyphens(i.name),
m.name,
m.name,
)
self.expand_member_dict[key] = value
for s in i.signals:
key = '#%s::%s'%(i.name, s.name)
value = '<link linkend="gdbus-signal-%s.%s">"%s"</link>'%(utils.dots_to_hyphens(i.name), s.name, s.name)
key = "#%s::%s" % (i.name, s.name)
value = '<link linkend="gdbus-signal-%s.%s">"%s"</link>' % (
utils.dots_to_hyphens(i.name),
s.name,
s.name,
)
self.expand_member_dict[key] = value
for p in i.properties:
key = '#%s:%s'%(i.name, p.name)
value = '<link linkend="gdbus-property-%s.%s">"%s"</link>'%(utils.dots_to_hyphens(i.name), p.name, p.name)
key = "#%s:%s" % (i.name, p.name)
value = '<link linkend="gdbus-property-%s.%s">"%s"</link>' % (
utils.dots_to_hyphens(i.name),
p.name,
p.name,
)
self.expand_member_dict[key] = value
# Make sure to expand the keys in reverse order so e.g. #org.foo.Iface:MediaCompat
# is evaluated before #org.foo.Iface:Media ...
self.expand_member_dict_keys = sorted(self.expand_member_dict.keys(), reverse=True)
self.expand_iface_dict_keys = sorted(self.expand_iface_dict.keys(), reverse=True)
self.expand_member_dict_keys = sorted(
self.expand_member_dict.keys(), reverse=True
)
self.expand_iface_dict_keys = sorted(
self.expand_iface_dict.keys(), reverse=True
)
def generate(self, docbook, outdir):
for i in self.ifaces:
self.out = open(path.join(outdir, '%s-%s.xml'%(docbook, i.name)), 'w')
self.out.write(''%())
self.out.write('<?xml version="1.0" encoding="utf-8"?>\n'%())
self.out.write('<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"\n'%())
self.out.write(' "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [\n'%())
self.out.write(']>\n'%())
self.out.write('<refentry id="gdbus-%s">\n'%(i.name))
self.out.write(' <refmeta>'%())
self.out.write(' <refentrytitle role="top_of_page" id="gdbus-interface-%s.top_of_page">%s</refentrytitle>\n'%(utils.dots_to_hyphens(i.name), i.name))
self.out.write(' <indexterm zone="gdbus-interface-%s.top_of_page"><primary sortas="%s">%s</primary></indexterm>\n'%(utils.dots_to_hyphens(i.name), i.name_without_prefix, i.name))
self.out.write(' </refmeta>'%())
self.out = open(path.join(outdir, "%s-%s.xml" % (docbook, i.name)), "w")
self.out.write("" % ())
self.out.write('<?xml version="1.0" encoding="utf-8"?>\n' % ())
self.out.write(
'<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"\n'
% ()
)
self.out.write(
' "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [\n'
% ()
)
self.out.write("]>\n" % ())
self.out.write('<refentry id="gdbus-%s">\n' % (i.name))
self.out.write(" <refmeta>" % ())
self.out.write(
' <refentrytitle role="top_of_page" id="gdbus-interface-%s.top_of_page">%s</refentrytitle>\n'
% (utils.dots_to_hyphens(i.name), i.name)
)
self.out.write(
' <indexterm zone="gdbus-interface-%s.top_of_page"><primary sortas="%s">%s</primary></indexterm>\n'
% (utils.dots_to_hyphens(i.name), i.name_without_prefix, i.name)
)
self.out.write(" </refmeta>" % ())
self.out.write(' <refnamediv>'%())
self.out.write(' <refname>%s</refname>'%(i.name))
self.out.write(' <refpurpose>%s</refpurpose>'%(i.doc_string_brief))
self.out.write(' </refnamediv>'%())
self.out.write(" <refnamediv>" % ())
self.out.write(" <refname>%s</refname>" % (i.name))
self.out.write(" <refpurpose>%s</refpurpose>" % (i.doc_string_brief))
self.out.write(" </refnamediv>" % ())
if len(i.methods) > 0:
self.print_synopsis_methods(i)
@ -294,36 +429,53 @@ class DocbookCodeGenerator:
if len(i.properties) > 0:
self.print_synopsis_properties(i)
self.out.write('<refsect1 role="desc" id="gdbus-interface-%s">\n'%(utils.dots_to_hyphens(i.name)))
self.out.write(' <title role="desc.title">Description</title>\n'%())
self.out.write(' %s\n'%(self.expand_paras(i.doc_string, True)))
self.out.write(
'<refsect1 role="desc" id="gdbus-interface-%s">\n'
% (utils.dots_to_hyphens(i.name))
)
self.out.write(' <title role="desc.title">Description</title>\n' % ())
self.out.write(" %s\n" % (self.expand_paras(i.doc_string, True)))
if len(i.since) > 0:
self.out.write(' <para role="since">Since %s</para>\n'%(i.since))
self.out.write(' <para role="since">Since %s</para>\n' % (i.since))
if i.deprecated:
self.out.write('<warning><para>The %s interface is deprecated.</para></warning>'%(i.name))
self.out.write('</refsect1>\n'%())
self.out.write(
"<warning><para>The %s interface is deprecated.</para></warning>"
% (i.name)
)
self.out.write("</refsect1>\n" % ())
if len(i.methods) > 0:
self.out.write('<refsect1 role="details" id="gdbus-methods-%s">\n'%(i.name))
self.out.write(' <title role="details.title">Method Details</title>\n'%())
self.out.write(
'<refsect1 role="details" id="gdbus-methods-%s">\n' % (i.name)
)
self.out.write(
' <title role="details.title">Method Details</title>\n' % ()
)
for m in i.methods:
self.print_method(i, m)
self.out.write('</refsect1>\n'%())
self.out.write("</refsect1>\n" % ())
if len(i.signals) > 0:
self.out.write('<refsect1 role="details" id="gdbus-signals-%s">\n'%(i.name))
self.out.write(' <title role="details.title">Signal Details</title>\n'%())
self.out.write(
'<refsect1 role="details" id="gdbus-signals-%s">\n' % (i.name)
)
self.out.write(
' <title role="details.title">Signal Details</title>\n' % ()
)
for s in i.signals:
self.print_signal(i, s)
self.out.write('</refsect1>\n'%())
self.out.write("</refsect1>\n" % ())
if len(i.properties) > 0:
self.out.write('<refsect1 role="details" id="gdbus-properties-%s">\n'%(i.name))
self.out.write(' <title role="details.title">Property Details</title>\n'%())
self.out.write(
'<refsect1 role="details" id="gdbus-properties-%s">\n' % (i.name)
)
self.out.write(
' <title role="details.title">Property Details</title>\n' % ()
)
for s in i.properties:
self.print_property(i, s)
self.out.write('</refsect1>\n'%())
self.out.write('</refentry>\n')
self.out.write('\n')
self.out.write("</refsect1>\n" % ())
self.out.write("</refentry>\n")
self.out.write("\n")

View File

@ -32,30 +32,35 @@ from . import codegen
from . import codegen_docbook
from .utils import print_error, print_warning
def find_arg(arg_list, arg_name):
for a in arg_list:
if a.name == arg_name:
return a
return None
def find_method(iface, method):
for m in iface.methods:
if m.name == method:
return m
return None
def find_signal(iface, signal):
for m in iface.signals:
if m.name == signal:
return m
return None
def find_prop(iface, prop):
for m in iface.properties:
if m.name == prop:
return m
return None
def apply_annotation(iface_list, iface, method, signal, prop, arg, key, value):
iface_obj = None
for i in iface_list:
@ -74,10 +79,14 @@ def apply_annotation(iface_list, iface, method, signal, prop, arg, key, value):
print_error('No method "{}" on interface "{}"'.format(method, iface))
if arg:
arg_obj = find_arg(method_obj.in_args, arg)
if (arg_obj is None):
if arg_obj is None:
arg_obj = find_arg(method_obj.out_args, arg)
if (arg_obj is None):
print_error('No arg "{}" on method "{}" on interface "{}"'.format(arg, method, iface))
if arg_obj is None:
print_error(
'No arg "{}" on method "{}" on interface "{}"'.format(
arg, method, iface
)
)
target_obj = arg_obj
else:
target_obj = method_obj
@ -87,8 +96,12 @@ def apply_annotation(iface_list, iface, method, signal, prop, arg, key, value):
print_error('No signal "{}" on interface "{}"'.format(signal, iface))
if arg:
arg_obj = find_arg(signal_obj.args, arg)
if (arg_obj is None):
print_error('No arg "{}" on signal "{}" on interface "{}"'.format(arg, signal, iface))
if arg_obj is None:
print_error(
'No arg "{}" on signal "{}" on interface "{}"'.format(
arg, signal, iface
)
)
target_obj = arg_obj
else:
target_obj = signal_obj
@ -105,198 +118,287 @@ def apply_annotation(iface_list, iface, method, signal, prop, arg, key, value):
def apply_annotations(iface_list, annotation_list):
# apply annotations given on the command line
for (what, key, value) in annotation_list:
pos = what.find('::')
pos = what.find("::")
if pos != -1:
# signal
iface = what[0:pos];
signal = what[pos + 2:]
pos = signal.find('[')
iface = what[0:pos]
signal = what[pos + 2 :]
pos = signal.find("[")
if pos != -1:
arg = signal[pos + 1:]
arg = signal[pos + 1 :]
signal = signal[0:pos]
pos = arg.find(']')
pos = arg.find("]")
arg = arg[0:pos]
apply_annotation(iface_list, iface, None, signal, None, arg, key, value)
else:
apply_annotation(iface_list, iface, None, signal, None, None, key, value)
apply_annotation(
iface_list, iface, None, signal, None, None, key, value
)
else:
pos = what.find(':')
pos = what.find(":")
if pos != -1:
# property
iface = what[0:pos];
prop = what[pos + 1:]
iface = what[0:pos]
prop = what[pos + 1 :]
apply_annotation(iface_list, iface, None, None, prop, None, key, value)
else:
pos = what.find('()')
pos = what.find("()")
if pos != -1:
# method
combined = what[0:pos]
pos = combined.rfind('.')
pos = combined.rfind(".")
iface = combined[0:pos]
method = combined[pos + 1:]
pos = what.find('[')
method = combined[pos + 1 :]
pos = what.find("[")
if pos != -1:
arg = what[pos + 1:]
pos = arg.find(']')
arg = what[pos + 1 :]
pos = arg.find("]")
arg = arg[0:pos]
apply_annotation(iface_list, iface, method, None, None, arg, key, value)
apply_annotation(
iface_list, iface, method, None, None, arg, key, value
)
else:
apply_annotation(iface_list, iface, method, None, None, None, key, value)
apply_annotation(
iface_list, iface, method, None, None, None, key, value
)
else:
# must be an interface
iface = what
apply_annotation(iface_list, iface, None, None, None, None, key, value)
apply_annotation(
iface_list, iface, None, None, None, None, key, value
)
def codegen_main():
arg_parser = argparse.ArgumentParser(description='D-Bus code and documentation generator')
arg_parser.add_argument('files', metavar='FILE', nargs='+',
help='D-Bus introspection XML file')
arg_parser.add_argument('--xml-files', metavar='FILE', action='append', default=[],
help=argparse.SUPPRESS)
arg_parser.add_argument('--interface-prefix', metavar='PREFIX', default='',
help='String to strip from D-Bus interface names for code and docs')
arg_parser.add_argument('--c-namespace', metavar='NAMESPACE', default='',
help='The namespace to use for generated C code')
arg_parser.add_argument('--c-generate-object-manager', action='store_true',
help='Generate a GDBusObjectManagerClient subclass when generating C code')
arg_parser.add_argument('--c-generate-autocleanup', choices=['none', 'objects', 'all'], default='objects',
help='Generate autocleanup support')
arg_parser.add_argument('--generate-docbook', metavar='OUTFILES',
help='Generate Docbook in OUTFILES-org.Project.IFace.xml')
arg_parser.add_argument('--pragma-once', action='store_true',
help='Use "pragma once" as the inclusion guard')
arg_parser.add_argument('--annotate', nargs=3, action='append', metavar='WHAT KEY VALUE',
help='Add annotation (may be used several times)')
arg_parser.add_argument('--glib-min-required', metavar='VERSION',
help='Minimum version of GLib to be supported by the outputted code (default: 2.30)')
arg_parser.add_argument('--glib-max-allowed', metavar='VERSION',
help='Maximum version of GLib to be used by the outputted code (default: current GLib version)')
arg_parser.add_argument('--symbol-decorator',
help='Macro used to decorate a symbol in the outputted header, possibly to export symbols')
arg_parser.add_argument('--symbol-decorator-header',
help='Additional header required for decorator specified by --symbol-decorator')
arg_parser.add_argument('--symbol-decorator-define',
help='Additional define required for decorator specified by --symbol-decorator')
arg_parser = argparse.ArgumentParser(
description="D-Bus code and documentation generator"
)
arg_parser.add_argument(
"files", metavar="FILE", nargs="+", help="D-Bus introspection XML file"
)
arg_parser.add_argument(
"--xml-files",
metavar="FILE",
action="append",
default=[],
help=argparse.SUPPRESS,
)
arg_parser.add_argument(
"--interface-prefix",
metavar="PREFIX",
default="",
help="String to strip from D-Bus interface names for code and docs",
)
arg_parser.add_argument(
"--c-namespace",
metavar="NAMESPACE",
default="",
help="The namespace to use for generated C code",
)
arg_parser.add_argument(
"--c-generate-object-manager",
action="store_true",
help="Generate a GDBusObjectManagerClient subclass when generating C code",
)
arg_parser.add_argument(
"--c-generate-autocleanup",
choices=["none", "objects", "all"],
default="objects",
help="Generate autocleanup support",
)
arg_parser.add_argument(
"--generate-docbook",
metavar="OUTFILES",
help="Generate Docbook in OUTFILES-org.Project.IFace.xml",
)
arg_parser.add_argument(
"--pragma-once",
action="store_true",
help='Use "pragma once" as the inclusion guard',
)
arg_parser.add_argument(
"--annotate",
nargs=3,
action="append",
metavar="WHAT KEY VALUE",
help="Add annotation (may be used several times)",
)
arg_parser.add_argument(
"--glib-min-required",
metavar="VERSION",
help="Minimum version of GLib to be supported by the outputted code (default: 2.30)",
)
arg_parser.add_argument(
"--glib-max-allowed",
metavar="VERSION",
help="Maximum version of GLib to be used by the outputted code (default: current GLib version)",
)
arg_parser.add_argument(
"--symbol-decorator",
help="Macro used to decorate a symbol in the outputted header, possibly to export symbols",
)
arg_parser.add_argument(
"--symbol-decorator-header",
help="Additional header required for decorator specified by --symbol-decorator",
)
arg_parser.add_argument(
"--symbol-decorator-define",
help="Additional define required for decorator specified by --symbol-decorator",
)
group = arg_parser.add_mutually_exclusive_group()
group.add_argument('--generate-c-code', metavar='OUTFILES',
help='Generate C code in OUTFILES.[ch]')
group.add_argument('--header', action='store_true',
help='Generate C headers')
group.add_argument('--body', action='store_true',
help='Generate C code')
group.add_argument('--interface-info-header', action='store_true',
help='Generate GDBusInterfaceInfo C header')
group.add_argument('--interface-info-body', action='store_true',
help='Generate GDBusInterfaceInfo C code')
group.add_argument(
"--generate-c-code", metavar="OUTFILES", help="Generate C code in OUTFILES.[ch]"
)
group.add_argument("--header", action="store_true", help="Generate C headers")
group.add_argument("--body", action="store_true", help="Generate C code")
group.add_argument(
"--interface-info-header",
action="store_true",
help="Generate GDBusInterfaceInfo C header",
)
group.add_argument(
"--interface-info-body",
action="store_true",
help="Generate GDBusInterfaceInfo C code",
)
group = arg_parser.add_mutually_exclusive_group()
group.add_argument('--output', metavar='FILE',
help='Write output into the specified file')
group.add_argument('--output-directory', metavar='OUTDIR', default='',
help='Location to output generated files')
group.add_argument(
"--output", metavar="FILE", help="Write output into the specified file"
)
group.add_argument(
"--output-directory",
metavar="OUTDIR",
default="",
help="Location to output generated files",
)
args = arg_parser.parse_args();
args = arg_parser.parse_args()
if len(args.xml_files) > 0:
print_warning('The "--xml-files" option is deprecated; use positional arguments instead')
print_warning(
'The "--xml-files" option is deprecated; use positional arguments instead'
)
if ((args.generate_c_code is not None or args.generate_docbook is not None) and
args.output is not None):
print_error('Using --generate-c-code or --generate-docbook and '
'--output at the same time is not allowed')
if (
args.generate_c_code is not None or args.generate_docbook is not None
) and args.output is not None:
print_error(
"Using --generate-c-code or --generate-docbook and "
"--output at the same time is not allowed"
)
if args.generate_c_code:
header_name = args.generate_c_code + '.h'
header_name = args.generate_c_code + ".h"
h_file = os.path.join(args.output_directory, header_name)
args.header = True
c_file = os.path.join(args.output_directory, args.generate_c_code + '.c')
c_file = os.path.join(args.output_directory, args.generate_c_code + ".c")
args.body = True
elif args.header:
if args.output is None:
print_error('Using --header requires --output')
print_error("Using --header requires --output")
h_file = args.output
header_name = os.path.basename(h_file)
elif args.body:
if args.output is None:
print_error('Using --body requires --output')
print_error("Using --body requires --output")
c_file = args.output
header_name = os.path.splitext(os.path.basename(c_file))[0] + '.h'
header_name = os.path.splitext(os.path.basename(c_file))[0] + ".h"
elif args.interface_info_header:
if args.output is None:
print_error('Using --interface-info-header requires --output')
print_error("Using --interface-info-header requires --output")
if args.c_generate_object_manager:
print_error('--c-generate-object-manager is incompatible with '
'--interface-info-header')
print_error(
"--c-generate-object-manager is incompatible with "
"--interface-info-header"
)
h_file = args.output
header_name = os.path.basename(h_file)
elif args.interface_info_body:
if args.output is None:
print_error('Using --interface-info-body requires --output')
print_error("Using --interface-info-body requires --output")
if args.c_generate_object_manager:
print_error('--c-generate-object-manager is incompatible with '
'--interface-info-body')
print_error(
"--c-generate-object-manager is incompatible with "
"--interface-info-body"
)
c_file = args.output
header_name = os.path.splitext(os.path.basename(c_file))[0] + '.h'
header_name = os.path.splitext(os.path.basename(c_file))[0] + ".h"
# Check the minimum GLib version. The minimum --glib-min-required is 2.30,
# because thats when gdbus-codegen was introduced. Support 1, 2 or 3
# component versions, but ignore the micro component if its present.
if args.glib_min_required:
try:
parts = args.glib_min_required.split('.', 3)
glib_min_required = (int(parts[0]),
int(parts[1] if len(parts) > 1 else 0))
parts = args.glib_min_required.split(".", 3)
glib_min_required = (int(parts[0]), int(parts[1] if len(parts) > 1 else 0))
# Ignore micro component, but still validate it:
_ = int(parts[2] if len(parts) > 2 else 0)
except (ValueError, IndexError):
print_error('Unrecognized --glib-min-required string {}'.format(
args.glib_min_required))
print_error(
"Unrecognized --glib-min-required string {}".format(
args.glib_min_required
)
)
if glib_min_required < (2, 30):
print_error('Invalid --glib-min-required string {}: minimum '
'version is 2.30'.format(args.glib_min_required))
print_error(
"Invalid --glib-min-required string {}: minimum "
"version is 2.30".format(args.glib_min_required)
)
else:
glib_min_required = (2, 30)
# And the maximum GLib version.
if args.glib_max_allowed:
try:
parts = args.glib_max_allowed.split('.', 3)
glib_max_allowed = (int(parts[0]),
int(parts[1] if len(parts) > 1 else 0))
parts = args.glib_max_allowed.split(".", 3)
glib_max_allowed = (int(parts[0]), int(parts[1] if len(parts) > 1 else 0))
# Ignore micro component, but still validate it:
_ = int(parts[2] if len(parts) > 2 else 0)
except (ValueError, IndexError):
print_error('Unrecognized --glib-max-allowed string {}'.format(
args.glib_max_allowed))
print_error(
"Unrecognized --glib-max-allowed string {}".format(
args.glib_max_allowed
)
)
else:
glib_max_allowed = (config.MAJOR_VERSION, config.MINOR_VERSION)
# Only allow --symbol-decorator-define and --symbol-decorator-header if --symbol-decorator is used
if args.symbol_decorator is None:
if args.symbol_decorator_header or args.symbol_decorator_define:
print_error('--symbol-decorator-define and --symbol-decorator-header must be used with --symbol-decorator')
print_error(
"--symbol-decorator-define and --symbol-decorator-header must be used with --symbol-decorator"
)
# Round --glib-max-allowed up to the next stable release.
glib_max_allowed = \
(glib_max_allowed[0], glib_max_allowed[1] + (glib_max_allowed[1] % 2))
glib_max_allowed = (
glib_max_allowed[0],
glib_max_allowed[1] + (glib_max_allowed[1] % 2),
)
if glib_max_allowed < glib_min_required:
print_error('Invalid versions: --glib-min-required ({}) must be '
'less than or equal to --glib-max-allowed ({})'.format(glib_min_required, glib_max_allowed))
print_error(
"Invalid versions: --glib-min-required ({}) must be "
"less than or equal to --glib-max-allowed ({})".format(
glib_min_required, glib_max_allowed
)
)
all_ifaces = []
input_files_basenames = []
for fname in sorted(args.files + args.xml_files):
with open(fname, 'rb') as f:
with open(fname, "rb") as f:
xml_data = f.read()
parsed_ifaces = parser.parse_dbus_xml(xml_data,
h_type_implies_unix_fd=(glib_min_required >= (2, 64)))
parsed_ifaces = parser.parse_dbus_xml(
xml_data, h_type_implies_unix_fd=(glib_min_required >= (2, 64))
)
all_ifaces.extend(parsed_ifaces)
input_files_basenames.append(os.path.basename(fname))
@ -307,63 +409,72 @@ def codegen_main():
i.post_process(args.interface_prefix, args.c_namespace)
docbook = args.generate_docbook
docbook_gen = codegen_docbook.DocbookCodeGenerator(all_ifaces);
docbook_gen = codegen_docbook.DocbookCodeGenerator(all_ifaces)
if docbook:
ret = docbook_gen.generate(docbook, args.output_directory)
if args.header:
with open(h_file, 'w') as outfile:
gen = codegen.HeaderCodeGenerator(all_ifaces,
args.c_namespace,
args.c_generate_object_manager,
args.c_generate_autocleanup,
header_name,
input_files_basenames,
args.pragma_once,
glib_min_required,
args.symbol_decorator,
args.symbol_decorator_header,
outfile)
with open(h_file, "w") as outfile:
gen = codegen.HeaderCodeGenerator(
all_ifaces,
args.c_namespace,
args.c_generate_object_manager,
args.c_generate_autocleanup,
header_name,
input_files_basenames,
args.pragma_once,
glib_min_required,
args.symbol_decorator,
args.symbol_decorator_header,
outfile,
)
gen.generate()
if args.body:
with open(c_file, 'w') as outfile:
gen = codegen.CodeGenerator(all_ifaces,
args.c_namespace,
args.c_generate_object_manager,
header_name,
input_files_basenames,
docbook_gen,
glib_min_required,
args.symbol_decorator_define,
outfile)
with open(c_file, "w") as outfile:
gen = codegen.CodeGenerator(
all_ifaces,
args.c_namespace,
args.c_generate_object_manager,
header_name,
input_files_basenames,
docbook_gen,
glib_min_required,
args.symbol_decorator_define,
outfile,
)
gen.generate()
if args.interface_info_header:
with open(h_file, 'w') as outfile:
gen = codegen.InterfaceInfoHeaderCodeGenerator(all_ifaces,
args.c_namespace,
header_name,
input_files_basenames,
args.pragma_once,
glib_min_required,
args.symbol_decorator,
args.symbol_decorator_header,
outfile)
with open(h_file, "w") as outfile:
gen = codegen.InterfaceInfoHeaderCodeGenerator(
all_ifaces,
args.c_namespace,
header_name,
input_files_basenames,
args.pragma_once,
glib_min_required,
args.symbol_decorator,
args.symbol_decorator_header,
outfile,
)
gen.generate()
if args.interface_info_body:
with open(c_file, 'w') as outfile:
gen = codegen.InterfaceInfoBodyCodeGenerator(all_ifaces,
args.c_namespace,
header_name,
input_files_basenames,
glib_min_required,
args.symbol_decorator_define,
outfile)
with open(c_file, "w") as outfile:
gen = codegen.InterfaceInfoBodyCodeGenerator(
all_ifaces,
args.c_namespace,
header_name,
input_files_basenames,
glib_min_required,
args.symbol_decorator_define,
outfile,
)
gen.generate()
sys.exit(0)
if __name__ == "__main__":
codegen_main()

View File

@ -22,22 +22,30 @@
from . import utils
from .utils import print_error
class Annotation:
def __init__(self, key, value):
self.key = key
self.value = value
self.annotations = []
self.since = ''
self.since = ""
def post_process(self, interface_prefix, cns, cns_upper, cns_lower, container):
key = self.key
overridden_key = utils.lookup_annotation(self.annotations, 'org.gtk.GDBus.C.Name')
overridden_key = utils.lookup_annotation(
self.annotations, "org.gtk.GDBus.C.Name"
)
if utils.is_ugly_case(overridden_key):
self.key_lower = overridden_key.lower()
else:
if overridden_key:
key = overridden_key
self.key_lower = utils.camel_case_to_uscore(key).lower().replace('-', '_').replace('.', '_')
self.key_lower = (
utils.camel_case_to_uscore(key)
.lower()
.replace("-", "_")
.replace(".", "_")
)
if len(self.since) == 0:
self.since = utils.lookup_since(self.annotations)
@ -47,13 +55,14 @@ class Annotation:
for a in self.annotations:
a.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
class Arg:
def __init__(self, name, signature):
self.name = name
self.signature = signature
self.annotations = []
self.doc_string = ''
self.since = ''
self.doc_string = ""
self.since = ""
def post_process(self, interface_prefix, cns, cns_upper, cns_lower, arg_number):
if len(self.doc_string) == 0:
@ -62,195 +71,198 @@ class Arg:
self.since = utils.lookup_since(self.annotations)
if self.name is None:
self.name = 'unnamed_arg%d'%arg_number
self.name = "unnamed_arg%d" % arg_number
# default to GVariant
self.ctype_in_g = 'GVariant *'
self.ctype_in = 'GVariant *'
self.ctype_in_dup = 'GVariant *'
self.ctype_out = 'GVariant **'
self.gtype = 'G_TYPE_VARIANT'
self.free_func = 'g_variant_unref'
self.format_in = '@' + self.signature
self.format_out = '@' + self.signature
self.gvariant_get = 'XXX'
self.gvalue_get = 'g_value_get_variant'
self.array_annotation = ''
self.ctype_in_g = "GVariant *"
self.ctype_in = "GVariant *"
self.ctype_in_dup = "GVariant *"
self.ctype_out = "GVariant **"
self.gtype = "G_TYPE_VARIANT"
self.free_func = "g_variant_unref"
self.format_in = "@" + self.signature
self.format_out = "@" + self.signature
self.gvariant_get = "XXX"
self.gvalue_get = "g_value_get_variant"
self.array_annotation = ""
if not utils.lookup_annotation(self.annotations, 'org.gtk.GDBus.C.ForceGVariant'):
if self.signature == 'b':
self.ctype_in_g = 'gboolean '
self.ctype_in = 'gboolean '
self.ctype_out = 'gboolean *'
self.gtype = 'G_TYPE_BOOLEAN'
if not utils.lookup_annotation(
self.annotations, "org.gtk.GDBus.C.ForceGVariant"
):
if self.signature == "b":
self.ctype_in_g = "gboolean "
self.ctype_in = "gboolean "
self.ctype_out = "gboolean *"
self.gtype = "G_TYPE_BOOLEAN"
self.free_func = None
self.format_in = 'b'
self.format_out = 'b'
self.gvariant_get = 'g_variant_get_boolean'
self.gvalue_get = 'g_value_get_boolean'
elif self.signature == 'y':
self.ctype_in_g = 'guchar '
self.ctype_in = 'guchar '
self.ctype_out = 'guchar *'
self.gtype = 'G_TYPE_UCHAR'
self.format_in = "b"
self.format_out = "b"
self.gvariant_get = "g_variant_get_boolean"
self.gvalue_get = "g_value_get_boolean"
elif self.signature == "y":
self.ctype_in_g = "guchar "
self.ctype_in = "guchar "
self.ctype_out = "guchar *"
self.gtype = "G_TYPE_UCHAR"
self.free_func = None
self.format_in = 'y'
self.format_out = 'y'
self.gvariant_get = 'g_variant_get_byte'
self.gvalue_get = 'g_value_get_uchar'
elif self.signature == 'n':
self.ctype_in_g = 'gint '
self.ctype_in = 'gint16 '
self.ctype_out = 'gint16 *'
self.gtype = 'G_TYPE_INT'
self.format_in = "y"
self.format_out = "y"
self.gvariant_get = "g_variant_get_byte"
self.gvalue_get = "g_value_get_uchar"
elif self.signature == "n":
self.ctype_in_g = "gint "
self.ctype_in = "gint16 "
self.ctype_out = "gint16 *"
self.gtype = "G_TYPE_INT"
self.free_func = None
self.format_in = 'n'
self.format_out = 'n'
self.gvariant_get = 'g_variant_get_int16'
self.gvalue_get = 'g_value_get_int'
elif self.signature == 'q':
self.ctype_in_g = 'guint '
self.ctype_in = 'guint16 '
self.ctype_out = 'guint16 *'
self.gtype = 'G_TYPE_UINT'
self.format_in = "n"
self.format_out = "n"
self.gvariant_get = "g_variant_get_int16"
self.gvalue_get = "g_value_get_int"
elif self.signature == "q":
self.ctype_in_g = "guint "
self.ctype_in = "guint16 "
self.ctype_out = "guint16 *"
self.gtype = "G_TYPE_UINT"
self.free_func = None
self.format_in = 'q'
self.format_out = 'q'
self.gvariant_get = 'g_variant_get_uint16'
self.gvalue_get = 'g_value_get_uint'
elif self.signature == 'i':
self.ctype_in_g = 'gint '
self.ctype_in = 'gint '
self.ctype_out = 'gint *'
self.gtype = 'G_TYPE_INT'
self.format_in = "q"
self.format_out = "q"
self.gvariant_get = "g_variant_get_uint16"
self.gvalue_get = "g_value_get_uint"
elif self.signature == "i":
self.ctype_in_g = "gint "
self.ctype_in = "gint "
self.ctype_out = "gint *"
self.gtype = "G_TYPE_INT"
self.free_func = None
self.format_in = 'i'
self.format_out = 'i'
self.gvariant_get = 'g_variant_get_int32'
self.gvalue_get = 'g_value_get_int'
elif self.signature == 'u':
self.ctype_in_g = 'guint '
self.ctype_in = 'guint '
self.ctype_out = 'guint *'
self.gtype = 'G_TYPE_UINT'
self.format_in = "i"
self.format_out = "i"
self.gvariant_get = "g_variant_get_int32"
self.gvalue_get = "g_value_get_int"
elif self.signature == "u":
self.ctype_in_g = "guint "
self.ctype_in = "guint "
self.ctype_out = "guint *"
self.gtype = "G_TYPE_UINT"
self.free_func = None
self.format_in = 'u'
self.format_out = 'u'
self.gvariant_get = 'g_variant_get_uint32'
self.gvalue_get = 'g_value_get_uint'
elif self.signature == 'x':
self.ctype_in_g = 'gint64 '
self.ctype_in = 'gint64 '
self.ctype_out = 'gint64 *'
self.gtype = 'G_TYPE_INT64'
self.format_in = "u"
self.format_out = "u"
self.gvariant_get = "g_variant_get_uint32"
self.gvalue_get = "g_value_get_uint"
elif self.signature == "x":
self.ctype_in_g = "gint64 "
self.ctype_in = "gint64 "
self.ctype_out = "gint64 *"
self.gtype = "G_TYPE_INT64"
self.free_func = None
self.format_in = 'x'
self.format_out = 'x'
self.gvariant_get = 'g_variant_get_int64'
self.gvalue_get = 'g_value_get_int64'
elif self.signature == 't':
self.ctype_in_g = 'guint64 '
self.ctype_in = 'guint64 '
self.ctype_out = 'guint64 *'
self.gtype = 'G_TYPE_UINT64'
self.format_in = "x"
self.format_out = "x"
self.gvariant_get = "g_variant_get_int64"
self.gvalue_get = "g_value_get_int64"
elif self.signature == "t":
self.ctype_in_g = "guint64 "
self.ctype_in = "guint64 "
self.ctype_out = "guint64 *"
self.gtype = "G_TYPE_UINT64"
self.free_func = None
self.format_in = 't'
self.format_out = 't'
self.gvariant_get = 'g_variant_get_uint64'
self.gvalue_get = 'g_value_get_uint64'
elif self.signature == 'd':
self.ctype_in_g = 'gdouble '
self.ctype_in = 'gdouble '
self.ctype_out = 'gdouble *'
self.gtype = 'G_TYPE_DOUBLE'
self.format_in = "t"
self.format_out = "t"
self.gvariant_get = "g_variant_get_uint64"
self.gvalue_get = "g_value_get_uint64"
elif self.signature == "d":
self.ctype_in_g = "gdouble "
self.ctype_in = "gdouble "
self.ctype_out = "gdouble *"
self.gtype = "G_TYPE_DOUBLE"
self.free_func = None
self.format_in = 'd'
self.format_out = 'd'
self.gvariant_get = 'g_variant_get_double'
self.gvalue_get = 'g_value_get_double'
elif self.signature == 's':
self.ctype_in_g = 'const gchar *'
self.ctype_in = 'const gchar *'
self.ctype_in_dup = 'gchar *'
self.ctype_out = 'gchar **'
self.gtype = 'G_TYPE_STRING'
self.free_func = 'g_free'
self.format_in = 's'
self.format_out = 's'
self.gvariant_get = 'g_variant_get_string'
self.gvalue_get = 'g_value_get_string'
elif self.signature == 'o':
self.ctype_in_g = 'const gchar *'
self.ctype_in = 'const gchar *'
self.ctype_in_dup = 'gchar *'
self.ctype_out = 'gchar **'
self.gtype = 'G_TYPE_STRING'
self.free_func = 'g_free'
self.format_in = 'o'
self.format_out = 'o'
self.gvariant_get = 'g_variant_get_string'
self.gvalue_get = 'g_value_get_string'
elif self.signature == 'g':
self.ctype_in_g = 'const gchar *'
self.ctype_in = 'const gchar *'
self.ctype_in_dup = 'gchar *'
self.ctype_out = 'gchar **'
self.gtype = 'G_TYPE_STRING'
self.free_func = 'g_free'
self.format_in = 'g'
self.format_out = 'g'
self.gvariant_get = 'g_variant_get_string'
self.gvalue_get = 'g_value_get_string'
elif self.signature == 'ay':
self.ctype_in_g = 'const gchar *'
self.ctype_in = 'const gchar *'
self.ctype_in_dup = 'gchar *'
self.ctype_out = 'gchar **'
self.gtype = 'G_TYPE_STRING'
self.free_func = 'g_free'
self.format_in = '^ay'
self.format_out = '^ay'
self.gvariant_get = 'g_variant_get_bytestring'
self.gvalue_get = 'g_value_get_string'
elif self.signature == 'as':
self.ctype_in_g = 'const gchar *const *'
self.ctype_in = 'const gchar *const *'
self.ctype_in_dup = 'gchar **'
self.ctype_out = 'gchar ***'
self.gtype = 'G_TYPE_STRV'
self.free_func = 'g_strfreev'
self.format_in = '^as'
self.format_out = '^as'
self.gvariant_get = 'g_variant_get_strv'
self.gvalue_get = 'g_value_get_boxed'
self.array_annotation = '(array zero-terminated=1)'
elif self.signature == 'ao':
self.ctype_in_g = 'const gchar *const *'
self.ctype_in = 'const gchar *const *'
self.ctype_in_dup = 'gchar **'
self.ctype_out = 'gchar ***'
self.gtype = 'G_TYPE_STRV'
self.free_func = 'g_strfreev'
self.format_in = '^ao'
self.format_out = '^ao'
self.gvariant_get = 'g_variant_get_objv'
self.gvalue_get = 'g_value_get_boxed'
self.array_annotation = '(array zero-terminated=1)'
elif self.signature == 'aay':
self.ctype_in_g = 'const gchar *const *'
self.ctype_in = 'const gchar *const *'
self.ctype_in_dup = 'gchar **'
self.ctype_out = 'gchar ***'
self.gtype = 'G_TYPE_STRV'
self.free_func = 'g_strfreev'
self.format_in = '^aay'
self.format_out = '^aay'
self.gvariant_get = 'g_variant_get_bytestring_array'
self.gvalue_get = 'g_value_get_boxed'
self.array_annotation = '(array zero-terminated=1)'
self.format_in = "d"
self.format_out = "d"
self.gvariant_get = "g_variant_get_double"
self.gvalue_get = "g_value_get_double"
elif self.signature == "s":
self.ctype_in_g = "const gchar *"
self.ctype_in = "const gchar *"
self.ctype_in_dup = "gchar *"
self.ctype_out = "gchar **"
self.gtype = "G_TYPE_STRING"
self.free_func = "g_free"
self.format_in = "s"
self.format_out = "s"
self.gvariant_get = "g_variant_get_string"
self.gvalue_get = "g_value_get_string"
elif self.signature == "o":
self.ctype_in_g = "const gchar *"
self.ctype_in = "const gchar *"
self.ctype_in_dup = "gchar *"
self.ctype_out = "gchar **"
self.gtype = "G_TYPE_STRING"
self.free_func = "g_free"
self.format_in = "o"
self.format_out = "o"
self.gvariant_get = "g_variant_get_string"
self.gvalue_get = "g_value_get_string"
elif self.signature == "g":
self.ctype_in_g = "const gchar *"
self.ctype_in = "const gchar *"
self.ctype_in_dup = "gchar *"
self.ctype_out = "gchar **"
self.gtype = "G_TYPE_STRING"
self.free_func = "g_free"
self.format_in = "g"
self.format_out = "g"
self.gvariant_get = "g_variant_get_string"
self.gvalue_get = "g_value_get_string"
elif self.signature == "ay":
self.ctype_in_g = "const gchar *"
self.ctype_in = "const gchar *"
self.ctype_in_dup = "gchar *"
self.ctype_out = "gchar **"
self.gtype = "G_TYPE_STRING"
self.free_func = "g_free"
self.format_in = "^ay"
self.format_out = "^ay"
self.gvariant_get = "g_variant_get_bytestring"
self.gvalue_get = "g_value_get_string"
elif self.signature == "as":
self.ctype_in_g = "const gchar *const *"
self.ctype_in = "const gchar *const *"
self.ctype_in_dup = "gchar **"
self.ctype_out = "gchar ***"
self.gtype = "G_TYPE_STRV"
self.free_func = "g_strfreev"
self.format_in = "^as"
self.format_out = "^as"
self.gvariant_get = "g_variant_get_strv"
self.gvalue_get = "g_value_get_boxed"
self.array_annotation = "(array zero-terminated=1)"
elif self.signature == "ao":
self.ctype_in_g = "const gchar *const *"
self.ctype_in = "const gchar *const *"
self.ctype_in_dup = "gchar **"
self.ctype_out = "gchar ***"
self.gtype = "G_TYPE_STRV"
self.free_func = "g_strfreev"
self.format_in = "^ao"
self.format_out = "^ao"
self.gvariant_get = "g_variant_get_objv"
self.gvalue_get = "g_value_get_boxed"
self.array_annotation = "(array zero-terminated=1)"
elif self.signature == "aay":
self.ctype_in_g = "const gchar *const *"
self.ctype_in = "const gchar *const *"
self.ctype_in_dup = "gchar **"
self.ctype_out = "gchar ***"
self.gtype = "G_TYPE_STRV"
self.free_func = "g_strfreev"
self.format_in = "^aay"
self.format_out = "^aay"
self.gvariant_get = "g_variant_get_bytestring_array"
self.gvalue_get = "g_value_get_boxed"
self.array_annotation = "(array zero-terminated=1)"
for a in self.annotations:
a.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
class Method:
def __init__(self, name, h_type_implies_unix_fd=True):
self.name = name
@ -258,12 +270,14 @@ class Method:
self.in_args = []
self.out_args = []
self.annotations = []
self.doc_string = ''
self.since = ''
self.doc_string = ""
self.since = ""
self.deprecated = False
self.unix_fd = False
def post_process(self, interface_prefix, cns, cns_upper, cns_lower, containing_iface):
def post_process(
self, interface_prefix, cns, cns_upper, cns_lower, containing_iface
):
if len(self.doc_string) == 0:
self.doc_string = utils.lookup_docs(self.annotations)
if len(self.since) == 0:
@ -272,47 +286,55 @@ class Method:
self.since = containing_iface.since
name = self.name
overridden_name = utils.lookup_annotation(self.annotations, 'org.gtk.GDBus.C.Name')
overridden_name = utils.lookup_annotation(
self.annotations, "org.gtk.GDBus.C.Name"
)
if utils.is_ugly_case(overridden_name):
self.name_lower = overridden_name.lower()
else:
if overridden_name:
name = overridden_name
self.name_lower = utils.camel_case_to_uscore(name).lower().replace('-', '_')
self.name_hyphen = self.name_lower.replace('_', '-')
self.name_lower = utils.camel_case_to_uscore(name).lower().replace("-", "_")
self.name_hyphen = self.name_lower.replace("_", "-")
arg_count = 0
for a in self.in_args:
a.post_process(interface_prefix, cns, cns_upper, cns_lower, arg_count)
arg_count += 1
if self.h_type_implies_unix_fd and 'h' in a.signature:
if self.h_type_implies_unix_fd and "h" in a.signature:
self.unix_fd = True
for a in self.out_args:
a.post_process(interface_prefix, cns, cns_upper, cns_lower, arg_count)
arg_count += 1
if self.h_type_implies_unix_fd and 'h' in a.signature:
if self.h_type_implies_unix_fd and "h" in a.signature:
self.unix_fd = True
if utils.lookup_annotation(self.annotations, 'org.freedesktop.DBus.Deprecated') == 'true':
if (
utils.lookup_annotation(self.annotations, "org.freedesktop.DBus.Deprecated")
== "true"
):
self.deprecated = True
if utils.lookup_annotation(self.annotations, 'org.gtk.GDBus.C.UnixFD'):
if utils.lookup_annotation(self.annotations, "org.gtk.GDBus.C.UnixFD"):
self.unix_fd = True
for a in self.annotations:
a.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
class Signal:
def __init__(self, name):
self.name = name
self.args = []
self.annotations = []
self.doc_string = ''
self.since = ''
self.doc_string = ""
self.since = ""
self.deprecated = False
def post_process(self, interface_prefix, cns, cns_upper, cns_lower, containing_iface):
def post_process(
self, interface_prefix, cns, cns_upper, cns_lower, containing_iface
):
if len(self.doc_string) == 0:
self.doc_string = utils.lookup_docs(self.annotations)
if len(self.since) == 0:
@ -321,51 +343,59 @@ class Signal:
self.since = containing_iface.since
name = self.name
overridden_name = utils.lookup_annotation(self.annotations, 'org.gtk.GDBus.C.Name')
overridden_name = utils.lookup_annotation(
self.annotations, "org.gtk.GDBus.C.Name"
)
if utils.is_ugly_case(overridden_name):
self.name_lower = overridden_name.lower()
else:
if overridden_name:
name = overridden_name
self.name_lower = utils.camel_case_to_uscore(name).lower().replace('-', '_')
self.name_hyphen = self.name_lower.replace('_', '-')
self.name_lower = utils.camel_case_to_uscore(name).lower().replace("-", "_")
self.name_hyphen = self.name_lower.replace("_", "-")
arg_count = 0
for a in self.args:
a.post_process(interface_prefix, cns, cns_upper, cns_lower, arg_count)
arg_count += 1
if utils.lookup_annotation(self.annotations, 'org.freedesktop.DBus.Deprecated') == 'true':
if (
utils.lookup_annotation(self.annotations, "org.freedesktop.DBus.Deprecated")
== "true"
):
self.deprecated = True
for a in self.annotations:
a.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
class Property:
def __init__(self, name, signature, access):
self.name = name
self.signature = signature
self.access = access
self.annotations = []
self.arg = Arg('value', self.signature)
self.arg = Arg("value", self.signature)
self.arg.annotations = self.annotations
self.readable = False
self.writable = False
if self.access == 'readwrite':
if self.access == "readwrite":
self.readable = True
self.writable = True
elif self.access == 'read':
elif self.access == "read":
self.readable = True
elif self.access == 'write':
elif self.access == "write":
self.writable = True
else:
print_error('Invalid access type "{}"'.format(self.access))
self.doc_string = ''
self.since = ''
self.doc_string = ""
self.since = ""
self.deprecated = False
self.emits_changed_signal = True
def post_process(self, interface_prefix, cns, cns_upper, cns_lower, containing_iface):
def post_process(
self, interface_prefix, cns, cns_upper, cns_lower, containing_iface
):
if len(self.doc_string) == 0:
self.doc_string = utils.lookup_docs(self.annotations)
if len(self.since) == 0:
@ -374,23 +404,28 @@ class Property:
self.since = containing_iface.since
name = self.name
overridden_name = utils.lookup_annotation(self.annotations, 'org.gtk.GDBus.C.Name')
overridden_name = utils.lookup_annotation(
self.annotations, "org.gtk.GDBus.C.Name"
)
if utils.is_ugly_case(overridden_name):
self.name_lower = overridden_name.lower()
else:
if overridden_name:
name = overridden_name
self.name_lower = utils.camel_case_to_uscore(name).lower().replace('-', '_')
self.name_hyphen = self.name_lower.replace('_', '-')
self.name_lower = utils.camel_case_to_uscore(name).lower().replace("-", "_")
self.name_hyphen = self.name_lower.replace("_", "-")
# don't clash with the GType getter, e.g.: GType foo_bar_get_type (void); G_GNUC_CONST
if self.name_lower == 'type':
self.name_lower = 'type_'
if self.name_lower == "type":
self.name_lower = "type_"
# recalculate arg
self.arg.annotations = self.annotations
self.arg.post_process(interface_prefix, cns, cns_upper, cns_lower, 0)
if utils.lookup_annotation(self.annotations, 'org.freedesktop.DBus.Deprecated') == 'true':
if (
utils.lookup_annotation(self.annotations, "org.freedesktop.DBus.Deprecated")
== "true"
):
self.deprecated = True
for a in self.annotations:
@ -399,9 +434,12 @@ class Property:
# FIXME: for now we only support 'false' and 'const' on the signal itself, see #674913 and
# http://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format
# for details
if utils.lookup_annotation(self.annotations, 'org.freedesktop.DBus.Property.EmitsChangedSignal') in ('false', 'const'):
if utils.lookup_annotation(
self.annotations, "org.freedesktop.DBus.Property.EmitsChangedSignal"
) in ("false", "const"):
self.emits_changed_signal = False
class Interface:
def __init__(self, name):
self.name = name
@ -409,9 +447,9 @@ class Interface:
self.signals = []
self.properties = []
self.annotations = []
self.doc_string = ''
self.doc_string_brief = ''
self.since = ''
self.doc_string = ""
self.doc_string_brief = ""
self.since = ""
self.deprecated = False
def post_process(self, interface_prefix, c_namespace):
@ -424,21 +462,23 @@ class Interface:
if len(c_namespace) > 0:
if utils.is_ugly_case(c_namespace):
cns = c_namespace.replace('_', '')
cns_upper = c_namespace.upper() + '_'
cns_lower = c_namespace.lower() + '_'
cns = c_namespace.replace("_", "")
cns_upper = c_namespace.upper() + "_"
cns_lower = c_namespace.lower() + "_"
else:
cns = c_namespace
cns_upper = utils.camel_case_to_uscore(c_namespace).upper() + '_'
cns_lower = utils.camel_case_to_uscore(c_namespace).lower() + '_'
cns_upper = utils.camel_case_to_uscore(c_namespace).upper() + "_"
cns_lower = utils.camel_case_to_uscore(c_namespace).lower() + "_"
else:
cns = ''
cns_upper = ''
cns_lower = ''
cns = ""
cns_upper = ""
cns_lower = ""
overridden_name = utils.lookup_annotation(self.annotations, 'org.gtk.GDBus.C.Name')
overridden_name = utils.lookup_annotation(
self.annotations, "org.gtk.GDBus.C.Name"
)
if utils.is_ugly_case(overridden_name):
name = overridden_name.replace('_', '')
name = overridden_name.replace("_", "")
name_with_ns = cns + name
self.name_without_prefix = name
self.camel_name = name_with_ns
@ -446,25 +486,28 @@ class Interface:
self.name_lower = cns_lower + overridden_name.lower()
self.name_upper = overridden_name.upper()
#print_error('handle Ugly_Case "{}"'.format(overridden_name))
# print_error('handle Ugly_Case "{}"'.format(overridden_name))
else:
if overridden_name:
name = overridden_name
else:
name = self.name
if name.startswith(interface_prefix):
name = name[len(interface_prefix):]
name = name[len(interface_prefix) :]
self.name_without_prefix = name
name = utils.strip_dots(name)
name_with_ns = utils.strip_dots(cns + '.' + name)
name_with_ns = utils.strip_dots(cns + "." + name)
self.camel_name = name_with_ns
self.ns_upper = cns_upper
self.name_lower = cns_lower + utils.camel_case_to_uscore(name)
self.name_upper = utils.camel_case_to_uscore(name).upper()
self.name_hyphen = self.name_upper.lower().replace('_', '-')
self.name_hyphen = self.name_upper.lower().replace("_", "-")
if utils.lookup_annotation(self.annotations, 'org.freedesktop.DBus.Deprecated') == 'true':
if (
utils.lookup_annotation(self.annotations, "org.freedesktop.DBus.Deprecated")
== "true"
):
self.deprecated = True
for m in self.methods:

View File

@ -25,16 +25,17 @@ import xml.parsers.expat
from . import dbustypes
from .utils import print_error
class DBusXMLParser:
STATE_TOP = 'top'
STATE_NODE = 'node'
STATE_INTERFACE = 'interface'
STATE_METHOD = 'method'
STATE_SIGNAL = 'signal'
STATE_PROPERTY = 'property'
STATE_ARG = 'arg'
STATE_ANNOTATION = 'annotation'
STATE_IGNORED = 'ignored'
STATE_TOP = "top"
STATE_NODE = "node"
STATE_INTERFACE = "interface"
STATE_METHOD = "method"
STATE_SIGNAL = "signal"
STATE_PROPERTY = "property"
STATE_ARG = "arg"
STATE_ANNOTATION = "annotation"
STATE_IGNORED = "ignored"
def __init__(self, xml_data, h_type_implies_unix_fd=True):
self._parser = xml.parsers.expat.ParserCreate()
@ -51,21 +52,22 @@ class DBusXMLParser:
self._cur_object = None
self._cur_object_stack = []
self.doc_comment_last_symbol = ''
self.doc_comment_last_symbol = ""
self._h_type_implies_unix_fd = h_type_implies_unix_fd
self._parser.Parse(xml_data)
COMMENT_STATE_BEGIN = 'begin'
COMMENT_STATE_PARAMS = 'params'
COMMENT_STATE_BODY = 'body'
COMMENT_STATE_SKIP = 'skip'
COMMENT_STATE_BEGIN = "begin"
COMMENT_STATE_PARAMS = "params"
COMMENT_STATE_BODY = "body"
COMMENT_STATE_SKIP = "skip"
def handle_comment(self, data):
comment_state = DBusXMLParser.COMMENT_STATE_BEGIN;
lines = data.split('\n')
symbol = ''
body = ''
comment_state = DBusXMLParser.COMMENT_STATE_BEGIN
lines = data.split("\n")
symbol = ""
body = ""
in_para = False
params = {}
for line in lines:
@ -73,59 +75,59 @@ class DBusXMLParser:
line = line.lstrip()
if comment_state == DBusXMLParser.COMMENT_STATE_BEGIN:
if len(line) > 0:
colon_index = line.find(': ')
colon_index = line.find(": ")
if colon_index == -1:
if line.endswith(':'):
symbol = line[0:len(line)-1]
if line.endswith(":"):
symbol = line[0 : len(line) - 1]
comment_state = DBusXMLParser.COMMENT_STATE_PARAMS
else:
comment_state = DBusXMLParser.COMMENT_STATE_SKIP
else:
symbol = line[0:colon_index]
rest_of_line = line[colon_index+2:].strip()
rest_of_line = line[colon_index + 2 :].strip()
if len(rest_of_line) > 0:
body += '<para>' + rest_of_line + '</para>'
body += "<para>" + rest_of_line + "</para>"
comment_state = DBusXMLParser.COMMENT_STATE_PARAMS
elif comment_state == DBusXMLParser.COMMENT_STATE_PARAMS:
if line.startswith('@'):
colon_index = line.find(': ')
if line.startswith("@"):
colon_index = line.find(": ")
if colon_index == -1:
comment_state = DBusXMLParser.COMMENT_STATE_BODY
if not in_para:
body += '<para>'
body += "<para>"
in_para = True
body += orig_line + '\n'
body += orig_line + "\n"
else:
param = line[1:colon_index]
docs = line[colon_index + 2:]
docs = line[colon_index + 2 :]
params[param] = docs
else:
comment_state = DBusXMLParser.COMMENT_STATE_BODY
if len(line) > 0:
if not in_para:
body += '<para>'
body += "<para>"
in_para = True
body += orig_line + '\n'
body += orig_line + "\n"
elif comment_state == DBusXMLParser.COMMENT_STATE_BODY:
if len(line) > 0:
if not in_para:
body += '<para>'
body += "<para>"
in_para = True
body += orig_line + '\n'
body += orig_line + "\n"
else:
if in_para:
body += '</para>'
body += "</para>"
in_para = False
if in_para:
body += '</para>'
body += "</para>"
if symbol != '':
if symbol != "":
self.doc_comment_last_symbol = symbol
self.doc_comment_params = params
self.doc_comment_body = body
def handle_char_data(self, data):
#print 'char_data=%s'%data
# print 'char_data=%s'%data
pass
def handle_start_element(self, name, attrs):
@ -141,77 +143,76 @@ class DBusXMLParser:
elif self.state == DBusXMLParser.STATE_NODE:
if name == DBusXMLParser.STATE_INTERFACE:
self.state = DBusXMLParser.STATE_INTERFACE
iface = dbustypes.Interface(attrs['name'])
iface = dbustypes.Interface(attrs["name"])
self._cur_object = iface
self.parsed_interfaces.append(iface)
elif name == DBusXMLParser.STATE_ANNOTATION:
self.state = DBusXMLParser.STATE_ANNOTATION
anno = dbustypes.Annotation(attrs['name'], attrs['value'])
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
self._cur_object.annotations.append(anno)
self._cur_object = anno
else:
self.state = DBusXMLParser.STATE_IGNORED
# assign docs, if any
if 'name' in attrs and self.doc_comment_last_symbol == attrs['name']:
if "name" in attrs and self.doc_comment_last_symbol == attrs["name"]:
self._cur_object.doc_string = self.doc_comment_body
if 'short_description' in self.doc_comment_params:
short_description = self.doc_comment_params['short_description']
if "short_description" in self.doc_comment_params:
short_description = self.doc_comment_params["short_description"]
self._cur_object.doc_string_brief = short_description
if 'since' in self.doc_comment_params:
self._cur_object.since = \
self.doc_comment_params['since'].strip()
if "since" in self.doc_comment_params:
self._cur_object.since = self.doc_comment_params["since"].strip()
elif self.state == DBusXMLParser.STATE_INTERFACE:
if name == DBusXMLParser.STATE_METHOD:
self.state = DBusXMLParser.STATE_METHOD
method = dbustypes.Method(attrs['name'],
h_type_implies_unix_fd=self._h_type_implies_unix_fd)
method = dbustypes.Method(
attrs["name"], h_type_implies_unix_fd=self._h_type_implies_unix_fd
)
self._cur_object.methods.append(method)
self._cur_object = method
elif name == DBusXMLParser.STATE_SIGNAL:
self.state = DBusXMLParser.STATE_SIGNAL
signal = dbustypes.Signal(attrs['name'])
signal = dbustypes.Signal(attrs["name"])
self._cur_object.signals.append(signal)
self._cur_object = signal
elif name == DBusXMLParser.STATE_PROPERTY:
self.state = DBusXMLParser.STATE_PROPERTY
prop = dbustypes.Property(attrs['name'], attrs['type'], attrs['access'])
prop = dbustypes.Property(attrs["name"], attrs["type"], attrs["access"])
self._cur_object.properties.append(prop)
self._cur_object = prop
elif name == DBusXMLParser.STATE_ANNOTATION:
self.state = DBusXMLParser.STATE_ANNOTATION
anno = dbustypes.Annotation(attrs['name'], attrs['value'])
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
self._cur_object.annotations.append(anno)
self._cur_object = anno
else:
self.state = DBusXMLParser.STATE_IGNORED
# assign docs, if any
if 'name' in attrs and self.doc_comment_last_symbol == attrs['name']:
if "name" in attrs and self.doc_comment_last_symbol == attrs["name"]:
self._cur_object.doc_string = self.doc_comment_body
if 'since' in self.doc_comment_params:
self._cur_object.since = \
self.doc_comment_params['since'].strip()
if "since" in self.doc_comment_params:
self._cur_object.since = self.doc_comment_params["since"].strip()
elif self.state == DBusXMLParser.STATE_METHOD:
if name == DBusXMLParser.STATE_ARG:
self.state = DBusXMLParser.STATE_ARG
arg_name = None
if 'name' in attrs:
arg_name = attrs['name']
arg = dbustypes.Arg(arg_name, attrs['type'])
direction = attrs.get('direction', 'in')
if direction == 'in':
if "name" in attrs:
arg_name = attrs["name"]
arg = dbustypes.Arg(arg_name, attrs["type"])
direction = attrs.get("direction", "in")
if direction == "in":
self._cur_object.in_args.append(arg)
elif direction == 'out':
elif direction == "out":
self._cur_object.out_args.append(arg)
else:
print_error('Invalid direction "{}"'.format(direction))
self._cur_object = arg
elif name == DBusXMLParser.STATE_ANNOTATION:
self.state = DBusXMLParser.STATE_ANNOTATION
anno = dbustypes.Annotation(attrs['name'], attrs['value'])
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
self._cur_object.annotations.append(anno)
self._cur_object = anno
else:
@ -219,26 +220,27 @@ class DBusXMLParser:
# assign docs, if any
if self.doc_comment_last_symbol == old_cur_object.name:
if 'name' in attrs and attrs['name'] in self.doc_comment_params:
doc_string = self.doc_comment_params[attrs['name']]
if "name" in attrs and attrs["name"] in self.doc_comment_params:
doc_string = self.doc_comment_params[attrs["name"]]
if doc_string != None:
self._cur_object.doc_string = doc_string
if 'since' in self.doc_comment_params:
self._cur_object.since = \
self.doc_comment_params['since'].strip()
if "since" in self.doc_comment_params:
self._cur_object.since = self.doc_comment_params[
"since"
].strip()
elif self.state == DBusXMLParser.STATE_SIGNAL:
if name == DBusXMLParser.STATE_ARG:
self.state = DBusXMLParser.STATE_ARG
arg_name = None
if 'name' in attrs:
arg_name = attrs['name']
arg = dbustypes.Arg(arg_name, attrs['type'])
if "name" in attrs:
arg_name = attrs["name"]
arg = dbustypes.Arg(arg_name, attrs["type"])
self._cur_object.args.append(arg)
self._cur_object = arg
elif name == DBusXMLParser.STATE_ANNOTATION:
self.state = DBusXMLParser.STATE_ANNOTATION
anno = dbustypes.Annotation(attrs['name'], attrs['value'])
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
self._cur_object.annotations.append(anno)
self._cur_object = anno
else:
@ -246,18 +248,19 @@ class DBusXMLParser:
# assign docs, if any
if self.doc_comment_last_symbol == old_cur_object.name:
if 'name' in attrs and attrs['name'] in self.doc_comment_params:
doc_string = self.doc_comment_params[attrs['name']]
if "name" in attrs and attrs["name"] in self.doc_comment_params:
doc_string = self.doc_comment_params[attrs["name"]]
if doc_string != None:
self._cur_object.doc_string = doc_string
if 'since' in self.doc_comment_params:
self._cur_object.since = \
self.doc_comment_params['since'].strip()
if "since" in self.doc_comment_params:
self._cur_object.since = self.doc_comment_params[
"since"
].strip()
elif self.state == DBusXMLParser.STATE_PROPERTY:
if name == DBusXMLParser.STATE_ANNOTATION:
self.state = DBusXMLParser.STATE_ANNOTATION
anno = dbustypes.Annotation(attrs['name'], attrs['value'])
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
self._cur_object.annotations.append(anno)
self._cur_object = anno
else:
@ -266,7 +269,7 @@ class DBusXMLParser:
elif self.state == DBusXMLParser.STATE_ARG:
if name == DBusXMLParser.STATE_ANNOTATION:
self.state = DBusXMLParser.STATE_ANNOTATION
anno = dbustypes.Annotation(attrs['name'], attrs['value'])
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
self._cur_object.annotations.append(anno)
self._cur_object = anno
else:
@ -275,14 +278,18 @@ class DBusXMLParser:
elif self.state == DBusXMLParser.STATE_ANNOTATION:
if name == DBusXMLParser.STATE_ANNOTATION:
self.state = DBusXMLParser.STATE_ANNOTATION
anno = dbustypes.Annotation(attrs['name'], attrs['value'])
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
self._cur_object.annotations.append(anno)
self._cur_object = anno
else:
self.state = DBusXMLParser.STATE_IGNORED
else:
print_error('Unhandled state "{}" while entering element with name "{}"'.format(self.state, name))
print_error(
'Unhandled state "{}" while entering element with name "{}"'.format(
self.state, name
)
)
self.state_stack.append(old_state)
self._cur_object_stack.append(old_cur_object)
@ -291,6 +298,7 @@ class DBusXMLParser:
self.state = self.state_stack.pop()
self._cur_object = self._cur_object_stack.pop()
def parse_dbus_xml(xml_data, h_type_implies_unix_fd):
parser = DBusXMLParser(xml_data, h_type_implies_unix_fd)
return parser.parsed_interfaces

View File

@ -25,47 +25,55 @@ import sys
# pylint: disable=too-few-public-methods
class Color:
'''ANSI Terminal colors'''
GREEN = '\033[1;32m'
BLUE = '\033[1;34m'
YELLOW = '\033[1;33m'
RED = '\033[1;31m'
END = '\033[0m'
"""ANSI Terminal colors"""
def print_color(msg, color=Color.END, prefix='MESSAGE'):
'''Print a string with a color prefix'''
GREEN = "\033[1;32m"
BLUE = "\033[1;34m"
YELLOW = "\033[1;33m"
RED = "\033[1;31m"
END = "\033[0m"
def print_color(msg, color=Color.END, prefix="MESSAGE"):
"""Print a string with a color prefix"""
if os.isatty(sys.stderr.fileno()):
real_prefix = '{start}{prefix}{end}'.format(start=color, prefix=prefix, end=Color.END)
real_prefix = "{start}{prefix}{end}".format(
start=color, prefix=prefix, end=Color.END
)
else:
real_prefix = prefix
sys.stderr.write('{prefix}: {msg}\n'.format(prefix=real_prefix, msg=msg))
sys.stderr.write("{prefix}: {msg}\n".format(prefix=real_prefix, msg=msg))
def print_error(msg):
'''Print an error, and terminate'''
print_color(msg, color=Color.RED, prefix='ERROR')
"""Print an error, and terminate"""
print_color(msg, color=Color.RED, prefix="ERROR")
sys.exit(1)
def print_warning(msg, fatal=False):
'''Print a warning, and optionally terminate'''
"""Print a warning, and optionally terminate"""
if fatal:
color = Color.RED
prefix = 'ERROR'
prefix = "ERROR"
else:
color = Color.YELLOW
prefix = 'WARNING'
prefix = "WARNING"
print_color(msg, color, prefix)
if fatal:
sys.exit(1)
def print_info(msg):
'''Print a message'''
print_color(msg, color=Color.GREEN, prefix='INFO')
"""Print a message"""
print_color(msg, color=Color.GREEN, prefix="INFO")
def strip_dots(s):
ret = ''
ret = ""
force_upper = False
for c in s:
if c == '.':
if c == ".":
force_upper = True
else:
if force_upper:
@ -75,19 +83,21 @@ def strip_dots(s):
ret += c
return ret
def dots_to_hyphens(s):
return s.replace('.', '-')
return s.replace(".", "-")
def camel_case_to_uscore(s):
ret = ''
ret = ""
insert_uscore = False
prev_was_lower = False
initial = True;
initial = True
for c in s:
# Keep initial underscores in camel case
if initial and c == '_':
ret += '_'
continue;
if initial and c == "_":
ret += "_"
continue
initial = False
if c.isupper():
@ -97,16 +107,18 @@ def camel_case_to_uscore(s):
else:
prev_was_lower = True
if insert_uscore:
ret += '_'
ret += "_"
ret += c.lower()
insert_uscore = False
return ret
def is_ugly_case(s):
if s and s.find('_') > 0:
if s and s.find("_") > 0:
return True
return False
def lookup_annotation(annotations, key):
if annotations:
for a in annotations:
@ -114,35 +126,39 @@ def lookup_annotation(annotations, key):
return a.value
return None
def lookup_docs(annotations):
s = lookup_annotation(annotations, 'org.gtk.GDBus.DocString')
s = lookup_annotation(annotations, "org.gtk.GDBus.DocString")
if s is None:
return ''
return ""
else:
return s
def lookup_since(annotations):
s = lookup_annotation(annotations, 'org.gtk.GDBus.Since')
s = lookup_annotation(annotations, "org.gtk.GDBus.Since")
if s is None:
return ''
return ""
else:
return s
def lookup_brief_docs(annotations):
s = lookup_annotation(annotations, 'org.gtk.GDBus.DocString.Short')
s = lookup_annotation(annotations, "org.gtk.GDBus.DocString.Short")
if s is None:
return ''
return ""
else:
return s
def version_cmp_key(key):
# If the 'since' version is 'UNRELEASED', compare higher than anything else
# If it is empty put a 0 in its place as this will
# allow LooseVersion to work and will always compare lower.
if key[0] == 'UNRELEASED':
v = '9999'
if key[0] == "UNRELEASED":
v = "9999"
elif key[0]:
v = str(key[0])
else:
v = '0'
v = "0"
return (distutils.version.LooseVersion(v), key[1])

View File

@ -4,6 +4,6 @@ import os
import subprocess
import sys
if not os.environ.get('DESTDIR'):
print('GIO module cache creation...')
subprocess.call([sys.argv[1], sys.argv[2]])
if not os.environ.get("DESTDIR"):
print("GIO module cache creation...")
subprocess.call([sys.argv[1], sys.argv[2]])

View File

@ -32,7 +32,7 @@ import unittest
import taptestrunner
Result = collections.namedtuple('Result', ('info', 'out', 'err', 'subs'))
Result = collections.namedtuple("Result", ("info", "out", "err", "subs"))
class TestCodegen(unittest.TestCase):
@ -47,22 +47,27 @@ class TestCodegen(unittest.TestCase):
parsing and generation code of gdbus-codegen into separate unit tests, and
just test command line behaviour in this integration test.
"""
# Track the cwd, we want to back out to that to clean up our tempdir
cwd = ''
cwd = ""
def setUp(self):
self.timeout_seconds = 10 # seconds per test
self.tmpdir = tempfile.TemporaryDirectory()
self.cwd = os.getcwd()
os.chdir(self.tmpdir.name)
print('tmpdir:', self.tmpdir.name)
if 'G_TEST_BUILDDIR' in os.environ:
self.__codegen = \
os.path.join(os.environ['G_TEST_BUILDDIR'], '..',
'gdbus-2.0', 'codegen', 'gdbus-codegen')
print("tmpdir:", self.tmpdir.name)
if "G_TEST_BUILDDIR" in os.environ:
self.__codegen = os.path.join(
os.environ["G_TEST_BUILDDIR"],
"..",
"gdbus-2.0",
"codegen",
"gdbus-codegen",
)
else:
self.__codegen = shutil.which('gdbus-codegen')
print('codegen:', self.__codegen)
self.__codegen = shutil.which("gdbus-codegen")
print("codegen:", self.__codegen)
def tearDown(self):
os.chdir(self.cwd)
@ -73,201 +78,200 @@ class TestCodegen(unittest.TestCase):
# shebang lines are not supported on native
# Windows consoles
if os.name == 'nt':
if os.name == "nt":
argv.insert(0, sys.executable)
argv.extend(args)
print('Running:', argv)
print("Running:", argv)
env = os.environ.copy()
env['LC_ALL'] = 'C.UTF-8'
print('Environment:', env)
env["LC_ALL"] = "C.UTF-8"
print("Environment:", env)
# We want to ensure consistent line endings...
info = subprocess.run(argv, timeout=self.timeout_seconds,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
universal_newlines=True)
info = subprocess.run(
argv,
timeout=self.timeout_seconds,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
universal_newlines=True,
)
info.check_returncode()
out = info.stdout.strip()
err = info.stderr.strip()
# Known substitutions for standard boilerplate
subs = {
'standard_top_comment':
'/*\n'
' * This file is generated by gdbus-codegen, do not modify it.\n'
' *\n'
' * The license of this code is the same as for the D-Bus interface description\n'
' * it was derived from. Note that it links to GLib, so must comply with the\n'
' * LGPL linking clauses.\n'
' */',
'standard_config_h_include':
'#ifdef HAVE_CONFIG_H\n'
'# include "config.h"\n'
'#endif',
'standard_header_includes':
'#include <string.h>\n'
'#ifdef G_OS_UNIX\n'
'# include <gio/gunixfdlist.h>\n'
'#endif',
'standard_typedefs_and_helpers':
'typedef struct\n'
'{\n'
' GDBusArgInfo parent_struct;\n'
' gboolean use_gvariant;\n'
'} _ExtendedGDBusArgInfo;\n'
'\n'
'typedef struct\n'
'{\n'
' GDBusMethodInfo parent_struct;\n'
' const gchar *signal_name;\n'
' gboolean pass_fdlist;\n'
'} _ExtendedGDBusMethodInfo;\n'
'\n'
'typedef struct\n'
'{\n'
' GDBusSignalInfo parent_struct;\n'
' const gchar *signal_name;\n'
'} _ExtendedGDBusSignalInfo;\n'
'\n'
'typedef struct\n'
'{\n'
' GDBusPropertyInfo parent_struct;\n'
' const gchar *hyphen_name;\n'
' guint use_gvariant : 1;\n'
' guint emits_changed_signal : 1;\n'
'} _ExtendedGDBusPropertyInfo;\n'
'\n'
'typedef struct\n'
'{\n'
' GDBusInterfaceInfo parent_struct;\n'
' const gchar *hyphen_name;\n'
'} _ExtendedGDBusInterfaceInfo;\n'
'\n'
'typedef struct\n'
'{\n'
' const _ExtendedGDBusPropertyInfo *info;\n'
' guint prop_id;\n'
' GValue orig_value; /* the value before the change */\n'
'} ChangedProperty;\n'
'\n'
'static void\n'
'_changed_property_free (ChangedProperty *data)\n'
'{\n'
' g_value_unset (&data->orig_value);\n'
' g_free (data);\n'
'}\n'
'\n'
'static gboolean\n'
'_g_strv_equal0 (gchar **a, gchar **b)\n'
'{\n'
' gboolean ret = FALSE;\n'
' guint n;\n'
' if (a == NULL && b == NULL)\n'
' {\n'
' ret = TRUE;\n'
' goto out;\n'
' }\n'
' if (a == NULL || b == NULL)\n'
' goto out;\n'
' if (g_strv_length (a) != g_strv_length (b))\n'
' goto out;\n'
' for (n = 0; a[n] != NULL; n++)\n'
' if (g_strcmp0 (a[n], b[n]) != 0)\n'
' goto out;\n'
' ret = TRUE;\n'
'out:\n'
' return ret;\n'
'}\n'
'\n'
'static gboolean\n'
'_g_variant_equal0 (GVariant *a, GVariant *b)\n'
'{\n'
' gboolean ret = FALSE;\n'
' if (a == NULL && b == NULL)\n'
' {\n'
' ret = TRUE;\n'
' goto out;\n'
' }\n'
' if (a == NULL || b == NULL)\n'
' goto out;\n'
' ret = g_variant_equal (a, b);\n'
'out:\n'
' return ret;\n'
'}\n'
'\n'
'G_GNUC_UNUSED static gboolean\n'
'_g_value_equal (const GValue *a, const GValue *b)\n'
'{\n'
' gboolean ret = FALSE;\n'
' g_assert (G_VALUE_TYPE (a) == G_VALUE_TYPE (b));\n'
' switch (G_VALUE_TYPE (a))\n'
' {\n'
' case G_TYPE_BOOLEAN:\n'
' ret = (g_value_get_boolean (a) == g_value_get_boolean (b));\n'
' break;\n'
' case G_TYPE_UCHAR:\n'
' ret = (g_value_get_uchar (a) == g_value_get_uchar (b));\n'
' break;\n'
' case G_TYPE_INT:\n'
' ret = (g_value_get_int (a) == g_value_get_int (b));\n'
' break;\n'
' case G_TYPE_UINT:\n'
' ret = (g_value_get_uint (a) == g_value_get_uint (b));\n'
' break;\n'
' case G_TYPE_INT64:\n'
' ret = (g_value_get_int64 (a) == g_value_get_int64 (b));\n'
' break;\n'
' case G_TYPE_UINT64:\n'
' ret = (g_value_get_uint64 (a) == g_value_get_uint64 (b));\n'
' break;\n'
' case G_TYPE_DOUBLE:\n'
' {\n'
' /* Avoid -Wfloat-equal warnings by doing a direct bit compare */\n'
' gdouble da = g_value_get_double (a);\n'
' gdouble db = g_value_get_double (b);\n'
' ret = memcmp (&da, &db, sizeof (gdouble)) == 0;\n'
' }\n'
' break;\n'
' case G_TYPE_STRING:\n'
' ret = (g_strcmp0 (g_value_get_string (a), g_value_get_string (b)) == 0);\n'
' break;\n'
' case G_TYPE_VARIANT:\n'
' ret = _g_variant_equal0 (g_value_get_variant (a), g_value_get_variant (b));\n'
' break;\n'
' default:\n'
' if (G_VALUE_TYPE (a) == G_TYPE_STRV)\n'
' ret = _g_strv_equal0 (g_value_get_boxed (a), g_value_get_boxed (b));\n'
' else\n'
' g_critical ("_g_value_equal() does not handle type %s", g_type_name (G_VALUE_TYPE (a)));\n'
' break;\n'
' }\n'
' return ret;\n'
'}',
"standard_top_comment": "/*\n"
" * This file is generated by gdbus-codegen, do not modify it.\n"
" *\n"
" * The license of this code is the same as for the D-Bus interface description\n"
" * it was derived from. Note that it links to GLib, so must comply with the\n"
" * LGPL linking clauses.\n"
" */",
"standard_config_h_include": "#ifdef HAVE_CONFIG_H\n"
'# include "config.h"\n'
"#endif",
"standard_header_includes": "#include <string.h>\n"
"#ifdef G_OS_UNIX\n"
"# include <gio/gunixfdlist.h>\n"
"#endif",
"standard_typedefs_and_helpers": "typedef struct\n"
"{\n"
" GDBusArgInfo parent_struct;\n"
" gboolean use_gvariant;\n"
"} _ExtendedGDBusArgInfo;\n"
"\n"
"typedef struct\n"
"{\n"
" GDBusMethodInfo parent_struct;\n"
" const gchar *signal_name;\n"
" gboolean pass_fdlist;\n"
"} _ExtendedGDBusMethodInfo;\n"
"\n"
"typedef struct\n"
"{\n"
" GDBusSignalInfo parent_struct;\n"
" const gchar *signal_name;\n"
"} _ExtendedGDBusSignalInfo;\n"
"\n"
"typedef struct\n"
"{\n"
" GDBusPropertyInfo parent_struct;\n"
" const gchar *hyphen_name;\n"
" guint use_gvariant : 1;\n"
" guint emits_changed_signal : 1;\n"
"} _ExtendedGDBusPropertyInfo;\n"
"\n"
"typedef struct\n"
"{\n"
" GDBusInterfaceInfo parent_struct;\n"
" const gchar *hyphen_name;\n"
"} _ExtendedGDBusInterfaceInfo;\n"
"\n"
"typedef struct\n"
"{\n"
" const _ExtendedGDBusPropertyInfo *info;\n"
" guint prop_id;\n"
" GValue orig_value; /* the value before the change */\n"
"} ChangedProperty;\n"
"\n"
"static void\n"
"_changed_property_free (ChangedProperty *data)\n"
"{\n"
" g_value_unset (&data->orig_value);\n"
" g_free (data);\n"
"}\n"
"\n"
"static gboolean\n"
"_g_strv_equal0 (gchar **a, gchar **b)\n"
"{\n"
" gboolean ret = FALSE;\n"
" guint n;\n"
" if (a == NULL && b == NULL)\n"
" {\n"
" ret = TRUE;\n"
" goto out;\n"
" }\n"
" if (a == NULL || b == NULL)\n"
" goto out;\n"
" if (g_strv_length (a) != g_strv_length (b))\n"
" goto out;\n"
" for (n = 0; a[n] != NULL; n++)\n"
" if (g_strcmp0 (a[n], b[n]) != 0)\n"
" goto out;\n"
" ret = TRUE;\n"
"out:\n"
" return ret;\n"
"}\n"
"\n"
"static gboolean\n"
"_g_variant_equal0 (GVariant *a, GVariant *b)\n"
"{\n"
" gboolean ret = FALSE;\n"
" if (a == NULL && b == NULL)\n"
" {\n"
" ret = TRUE;\n"
" goto out;\n"
" }\n"
" if (a == NULL || b == NULL)\n"
" goto out;\n"
" ret = g_variant_equal (a, b);\n"
"out:\n"
" return ret;\n"
"}\n"
"\n"
"G_GNUC_UNUSED static gboolean\n"
"_g_value_equal (const GValue *a, const GValue *b)\n"
"{\n"
" gboolean ret = FALSE;\n"
" g_assert (G_VALUE_TYPE (a) == G_VALUE_TYPE (b));\n"
" switch (G_VALUE_TYPE (a))\n"
" {\n"
" case G_TYPE_BOOLEAN:\n"
" ret = (g_value_get_boolean (a) == g_value_get_boolean (b));\n"
" break;\n"
" case G_TYPE_UCHAR:\n"
" ret = (g_value_get_uchar (a) == g_value_get_uchar (b));\n"
" break;\n"
" case G_TYPE_INT:\n"
" ret = (g_value_get_int (a) == g_value_get_int (b));\n"
" break;\n"
" case G_TYPE_UINT:\n"
" ret = (g_value_get_uint (a) == g_value_get_uint (b));\n"
" break;\n"
" case G_TYPE_INT64:\n"
" ret = (g_value_get_int64 (a) == g_value_get_int64 (b));\n"
" break;\n"
" case G_TYPE_UINT64:\n"
" ret = (g_value_get_uint64 (a) == g_value_get_uint64 (b));\n"
" break;\n"
" case G_TYPE_DOUBLE:\n"
" {\n"
" /* Avoid -Wfloat-equal warnings by doing a direct bit compare */\n"
" gdouble da = g_value_get_double (a);\n"
" gdouble db = g_value_get_double (b);\n"
" ret = memcmp (&da, &db, sizeof (gdouble)) == 0;\n"
" }\n"
" break;\n"
" case G_TYPE_STRING:\n"
" ret = (g_strcmp0 (g_value_get_string (a), g_value_get_string (b)) == 0);\n"
" break;\n"
" case G_TYPE_VARIANT:\n"
" ret = _g_variant_equal0 (g_value_get_variant (a), g_value_get_variant (b));\n"
" break;\n"
" default:\n"
" if (G_VALUE_TYPE (a) == G_TYPE_STRV)\n"
" ret = _g_strv_equal0 (g_value_get_boxed (a), g_value_get_boxed (b));\n"
" else\n"
' g_critical ("_g_value_equal() does not handle type %s", g_type_name (G_VALUE_TYPE (a)));\n'
" break;\n"
" }\n"
" return ret;\n"
"}",
}
result = Result(info, out, err, subs)
print('Output:', result.out)
print("Output:", result.out)
return result
def runCodegenWithInterface(self, interface_contents, *args):
with tempfile.NamedTemporaryFile(dir=self.tmpdir.name,
suffix='.xml',
delete=False) as interface_file:
with tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, suffix=".xml", delete=False
) as interface_file:
# Write out the interface.
interface_file.write(interface_contents.encode('utf-8'))
print(interface_file.name + ':', interface_contents)
interface_file.write(interface_contents.encode("utf-8"))
print(interface_file.name + ":", interface_contents)
interface_file.flush()
return self.runCodegen(interface_file.name, *args)
def test_help(self):
"""Test the --help argument."""
result = self.runCodegen('--help')
self.assertIn('usage: gdbus-codegen', result.out)
result = self.runCodegen("--help")
self.assertIn("usage: gdbus-codegen", result.out)
def test_no_args(self):
"""Test running with no arguments at all."""
@ -276,11 +280,10 @@ class TestCodegen(unittest.TestCase):
def test_empty_interface_header(self):
"""Test generating a header with an empty interface file."""
result = self.runCodegenWithInterface('',
'--output', '/dev/stdout',
'--header')
self.assertEqual('', result.err)
self.assertEqual('''{standard_top_comment}
result = self.runCodegenWithInterface("", "--output", "/dev/stdout", "--header")
self.assertEqual("", result.err)
self.assertEqual(
"""{standard_top_comment}
#ifndef __STDOUT__
#define __STDOUT__
@ -292,16 +295,18 @@ G_BEGIN_DECLS
G_END_DECLS
#endif /* __STDOUT__ */'''.format(**result.subs),
result.out.strip())
#endif /* __STDOUT__ */""".format(
**result.subs
),
result.out.strip(),
)
def test_empty_interface_body(self):
"""Test generating a body with an empty interface file."""
result = self.runCodegenWithInterface('',
'--output', '/dev/stdout',
'--body')
self.assertEqual('', result.err)
self.assertEqual('''{standard_top_comment}
result = self.runCodegenWithInterface("", "--output", "/dev/stdout", "--body")
self.assertEqual("", result.err)
self.assertEqual(
"""{standard_top_comment}
{standard_config_h_include}
@ -309,12 +314,15 @@ G_END_DECLS
{standard_header_includes}
{standard_typedefs_and_helpers}'''.format(**result.subs),
result.out.strip())
{standard_typedefs_and_helpers}""".format(
**result.subs
),
result.out.strip(),
)
def test_reproducible(self):
"""Test builds are reproducible regardless of file ordering."""
xml_contents1 = '''
xml_contents1 = """
<node>
<interface name="com.acme.Coyote">
<method name="Run"/>
@ -324,40 +332,49 @@ G_END_DECLS
<property name="Mood" type="s" access="read"/>
</interface>
</node>
'''
"""
xml_contents2 = '''
xml_contents2 = """
<node>
<interface name="org.project.Bar.Frobnicator">
<method name="RandomMethod"/>
</interface>
</node>
'''
"""
with tempfile.NamedTemporaryFile(dir=self.tmpdir.name,
suffix='1.xml', delete=False) as xml_file1, \
tempfile.NamedTemporaryFile(dir=self.tmpdir.name,
suffix='2.xml', delete=False) as xml_file2:
with tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, suffix="1.xml", delete=False
) as xml_file1, tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, suffix="2.xml", delete=False
) as xml_file2:
# Write out the interfaces.
xml_file1.write(xml_contents1.encode('utf-8'))
xml_file2.write(xml_contents2.encode('utf-8'))
xml_file1.write(xml_contents1.encode("utf-8"))
xml_file2.write(xml_contents2.encode("utf-8"))
xml_file1.flush()
xml_file2.flush()
# Repeat this for headers and bodies.
for header_or_body in ['--header', '--body']:
for header_or_body in ["--header", "--body"]:
# Run gdbus-codegen with the interfaces in one order, and then
# again in another order.
result1 = self.runCodegen(xml_file1.name, xml_file2.name,
'--output', '/dev/stdout',
header_or_body)
self.assertEqual('', result1.err)
result1 = self.runCodegen(
xml_file1.name,
xml_file2.name,
"--output",
"/dev/stdout",
header_or_body,
)
self.assertEqual("", result1.err)
result2 = self.runCodegen(xml_file2.name, xml_file1.name,
'--output', '/dev/stdout',
header_or_body)
self.assertEqual('', result2.err)
result2 = self.runCodegen(
xml_file2.name,
xml_file1.name,
"--output",
"/dev/stdout",
header_or_body,
)
self.assertEqual("", result2.err)
# The output should be the same.
self.assertEqual(result1.out, result2.out)
@ -365,94 +382,108 @@ G_END_DECLS
def test_glib_min_required_invalid(self):
"""Test running with an invalid --glib-min-required."""
with self.assertRaises(subprocess.CalledProcessError):
self.runCodegenWithInterface('',
'--output', '/dev/stdout',
'--body',
'--glib-min-required', 'hello mum')
self.runCodegenWithInterface(
"",
"--output",
"/dev/stdout",
"--body",
"--glib-min-required",
"hello mum",
)
def test_glib_min_required_too_low(self):
"""Test running with a --glib-min-required which is too low (and hence
probably a typo)."""
with self.assertRaises(subprocess.CalledProcessError):
self.runCodegenWithInterface('',
'--output', '/dev/stdout',
'--body',
'--glib-min-required', '2.6')
self.runCodegenWithInterface(
"", "--output", "/dev/stdout", "--body", "--glib-min-required", "2.6"
)
def test_glib_min_required_major_only(self):
"""Test running with a --glib-min-required which contains only a major version."""
result = self.runCodegenWithInterface('',
'--output', '/dev/stdout',
'--header',
'--glib-min-required', '3',
'--glib-max-allowed', '3.2')
self.assertEqual('', result.err)
self.assertNotEqual('', result.out.strip())
result = self.runCodegenWithInterface(
"",
"--output",
"/dev/stdout",
"--header",
"--glib-min-required",
"3",
"--glib-max-allowed",
"3.2",
)
self.assertEqual("", result.err)
self.assertNotEqual("", result.out.strip())
def test_glib_min_required_with_micro(self):
"""Test running with a --glib-min-required which contains a micro version."""
result = self.runCodegenWithInterface('',
'--output', '/dev/stdout',
'--header',
'--glib-min-required', '2.46.2')
self.assertEqual('', result.err)
self.assertNotEqual('', result.out.strip())
result = self.runCodegenWithInterface(
"", "--output", "/dev/stdout", "--header", "--glib-min-required", "2.46.2"
)
self.assertEqual("", result.err)
self.assertNotEqual("", result.out.strip())
def test_glib_max_allowed_too_low(self):
"""Test running with a --glib-max-allowed which is too low (and hence
probably a typo)."""
with self.assertRaises(subprocess.CalledProcessError):
self.runCodegenWithInterface('',
'--output', '/dev/stdout',
'--body',
'--glib-max-allowed', '2.6')
self.runCodegenWithInterface(
"", "--output", "/dev/stdout", "--body", "--glib-max-allowed", "2.6"
)
def test_glib_max_allowed_major_only(self):
"""Test running with a --glib-max-allowed which contains only a major version."""
result = self.runCodegenWithInterface('',
'--output', '/dev/stdout',
'--header',
'--glib-max-allowed', '3')
self.assertEqual('', result.err)
self.assertNotEqual('', result.out.strip())
result = self.runCodegenWithInterface(
"", "--output", "/dev/stdout", "--header", "--glib-max-allowed", "3"
)
self.assertEqual("", result.err)
self.assertNotEqual("", result.out.strip())
def test_glib_max_allowed_with_micro(self):
"""Test running with a --glib-max-allowed which contains a micro version."""
result = self.runCodegenWithInterface('',
'--output', '/dev/stdout',
'--header',
'--glib-max-allowed', '2.46.2')
self.assertEqual('', result.err)
self.assertNotEqual('', result.out.strip())
result = self.runCodegenWithInterface(
"", "--output", "/dev/stdout", "--header", "--glib-max-allowed", "2.46.2"
)
self.assertEqual("", result.err)
self.assertNotEqual("", result.out.strip())
def test_glib_max_allowed_unstable(self):
"""Test running with a --glib-max-allowed which is unstable. It should
be rounded up to the next stable version number, and hence should not
end up less than --glib-min-required."""
result = self.runCodegenWithInterface('',
'--output', '/dev/stdout',
'--header',
'--glib-max-allowed', '2.63',
'--glib-min-required', '2.64')
self.assertEqual('', result.err)
self.assertNotEqual('', result.out.strip())
result = self.runCodegenWithInterface(
"",
"--output",
"/dev/stdout",
"--header",
"--glib-max-allowed",
"2.63",
"--glib-min-required",
"2.64",
)
self.assertEqual("", result.err)
self.assertNotEqual("", result.out.strip())
def test_glib_max_allowed_less_than_min_required(self):
"""Test running with a --glib-max-allowed which is less than
--glib-min-required."""
with self.assertRaises(subprocess.CalledProcessError):
self.runCodegenWithInterface('',
'--output', '/dev/stdout',
'--body',
'--glib-max-allowed', '2.62',
'--glib-min-required', '2.64')
self.runCodegenWithInterface(
"",
"--output",
"/dev/stdout",
"--body",
"--glib-max-allowed",
"2.62",
"--glib-min-required",
"2.64",
)
def test_unix_fd_types_and_annotations(self):
"""Test an interface with `h` arguments, no annotation, and GLib < 2.64.
See issue #1726.
"""
interface_xml = '''
interface_xml = """
<node>
<interface name="FDPassing">
<method name="HelloFD">
@ -470,71 +501,87 @@ G_END_DECLS
<arg name="files" type="a{sh}" direction="in"/>
</method>
</interface>
</node>'''
</node>"""
# Try without specifying --glib-min-required.
result = self.runCodegenWithInterface(interface_xml,
'--output', '/dev/stdout',
'--header')
self.assertEqual('', result.err)
self.assertEqual(result.out.strip().count('GUnixFDList'), 6)
result = self.runCodegenWithInterface(
interface_xml, "--output", "/dev/stdout", "--header"
)
self.assertEqual("", result.err)
self.assertEqual(result.out.strip().count("GUnixFDList"), 6)
# Specify an old --glib-min-required.
result = self.runCodegenWithInterface(interface_xml,
'--output', '/dev/stdout',
'--header',
'--glib-min-required', '2.32')
self.assertEqual('', result.err)
self.assertEqual(result.out.strip().count('GUnixFDList'), 6)
result = self.runCodegenWithInterface(
interface_xml,
"--output",
"/dev/stdout",
"--header",
"--glib-min-required",
"2.32",
)
self.assertEqual("", result.err)
self.assertEqual(result.out.strip().count("GUnixFDList"), 6)
# Specify a --glib-min-required ≥ 2.64. There should be more
# mentions of `GUnixFDList` now, since the annotation is not needed to
# trigger its use.
result = self.runCodegenWithInterface(interface_xml,
'--output', '/dev/stdout',
'--header',
'--glib-min-required', '2.64')
self.assertEqual('', result.err)
self.assertEqual(result.out.strip().count('GUnixFDList'), 18)
result = self.runCodegenWithInterface(
interface_xml,
"--output",
"/dev/stdout",
"--header",
"--glib-min-required",
"2.64",
)
self.assertEqual("", result.err)
self.assertEqual(result.out.strip().count("GUnixFDList"), 18)
def test_call_flags_and_timeout_method_args(self):
"""Test that generated method call functions have @call_flags and
@timeout_msec args if and only if GLib >= 2.64.
"""
interface_xml = '''
interface_xml = """
<node>
<interface name="org.project.UsefulInterface">
<method name="UsefulMethod"/>
</interface>
</node>'''
</node>"""
# Try without specifying --glib-min-required.
result = self.runCodegenWithInterface(interface_xml,
'--output', '/dev/stdout',
'--header')
self.assertEqual('', result.err)
self.assertEqual(result.out.strip().count('GDBusCallFlags call_flags,'), 0)
self.assertEqual(result.out.strip().count('gint timeout_msec,'), 0)
result = self.runCodegenWithInterface(
interface_xml, "--output", "/dev/stdout", "--header"
)
self.assertEqual("", result.err)
self.assertEqual(result.out.strip().count("GDBusCallFlags call_flags,"), 0)
self.assertEqual(result.out.strip().count("gint timeout_msec,"), 0)
# Specify an old --glib-min-required.
result = self.runCodegenWithInterface(interface_xml,
'--output', '/dev/stdout',
'--header',
'--glib-min-required', '2.32')
self.assertEqual('', result.err)
self.assertEqual(result.out.strip().count('GDBusCallFlags call_flags,'), 0)
self.assertEqual(result.out.strip().count('gint timeout_msec,'), 0)
result = self.runCodegenWithInterface(
interface_xml,
"--output",
"/dev/stdout",
"--header",
"--glib-min-required",
"2.32",
)
self.assertEqual("", result.err)
self.assertEqual(result.out.strip().count("GDBusCallFlags call_flags,"), 0)
self.assertEqual(result.out.strip().count("gint timeout_msec,"), 0)
# Specify a --glib-min-required ≥ 2.64. The two arguments should be
# present for both the async and sync method call functions.
result = self.runCodegenWithInterface(interface_xml,
'--output', '/dev/stdout',
'--header',
'--glib-min-required', '2.64')
self.assertEqual('', result.err)
self.assertEqual(result.out.strip().count('GDBusCallFlags call_flags,'), 2)
self.assertEqual(result.out.strip().count('gint timeout_msec,'), 2)
result = self.runCodegenWithInterface(
interface_xml,
"--output",
"/dev/stdout",
"--header",
"--glib-min-required",
"2.64",
)
self.assertEqual("", result.err)
self.assertEqual(result.out.strip().count("GDBusCallFlags call_flags,"), 2)
self.assertEqual(result.out.strip().count("gint timeout_msec,"), 2)
if __name__ == '__main__':
if __name__ == "__main__":
unittest.main(testRunner=taptestrunner.TAPTestRunner())

View File

@ -13,9 +13,9 @@ import string
import sys
if len(sys.argv) != 2:
raise SystemExit('Usage: %s <output-file>' % sys.argv[0])
raise SystemExit("Usage: %s <output-file>" % sys.argv[0])
with open(sys.argv[1], 'w', newline='\n') as f:
with open(sys.argv[1], "w", newline="\n") as f:
for count in range(12):
for c in string.ascii_lowercase:
f.write("%s\n" % (c * 100))

View File

@ -5,7 +5,7 @@ import sys
import re
import os
debug = os.getenv('GIO_GENTYPEFUNCS_DEBUG') is not None
debug = os.getenv("GIO_GENTYPEFUNCS_DEBUG") is not None
out_file = sys.argv[1]
in_files = sys.argv[2:]
@ -13,32 +13,37 @@ in_files = sys.argv[2:]
funcs = []
if debug: print ('Output file: ', out_file)
if debug:
print("Output file: ", out_file)
if debug: print (len(in_files), 'input files')
if debug:
print(len(in_files), "input files")
for filename in in_files:
if debug: print ('Input file: ', filename)
with open(filename, 'rb') as f:
for line in f:
line = line.rstrip(b'\n').rstrip(b'\r')
# print line
match = re.search(b'\bg_[a-zA-Z0-9_]*_get_type\b', line)
if match:
func = match.group(0)
if not func in funcs:
funcs.append(func)
if debug: print ('Found ', func)
if debug:
print("Input file: ", filename)
with open(filename, "rb") as f:
for line in f:
line = line.rstrip(b"\n").rstrip(b"\r")
# print line
match = re.search(b"\bg_[a-zA-Z0-9_]*_get_type\b", line)
if match:
func = match.group(0)
if not func in funcs:
funcs.append(func)
if debug:
print("Found ", func)
file_output = 'G_GNUC_BEGIN_IGNORE_DEPRECATIONS\n'
file_output = "G_GNUC_BEGIN_IGNORE_DEPRECATIONS\n"
funcs = sorted(funcs)
for f in funcs:
if f not in ['g_io_extension_get_type', 'g_settings_backend_get_type']:
file_output += '*tp++ = {0} ();\n'.format(f)
if f not in ["g_io_extension_get_type", "g_settings_backend_get_type"]:
file_output += "*tp++ = {0} ();\n".format(f)
if debug: print (len(funcs), 'functions')
if debug:
print(len(funcs), "functions")
ofile = open(out_file, "w")
ofile.write(file_output)

View File

@ -23,29 +23,33 @@ import sys
import tempfile
import subprocess
if not 'GLIB_TEST_COMPILATION' in os.environ:
print('''Test disabled because GLIB_TEST_COMPILATION is not set in the env.
if not "GLIB_TEST_COMPILATION" in os.environ:
print(
"""Test disabled because GLIB_TEST_COMPILATION is not set in the env.
If you wish to run this test, set GLIB_TEST_COMPILATION=1 in the env,
and make sure you have glib build dependencies installed, including
meson.''')
sys.exit(77)
meson."""
)
sys.exit(77)
if len(sys.argv) != 2:
print('Usage: {} <gio-2.0.pc dir>'.format(os.path.basename(sys.argv[0])))
sys.exit(1)
print("Usage: {} <gio-2.0.pc dir>".format(os.path.basename(sys.argv[0])))
sys.exit(1)
test_dir = os.path.dirname(sys.argv[0])
with tempfile.TemporaryDirectory() as builddir:
env = os.environ.copy()
env['PKG_CONFIG_PATH'] = sys.argv[1]
sourcedir = os.path.join(test_dir, 'static-link')
env = os.environ.copy()
env["PKG_CONFIG_PATH"] = sys.argv[1]
sourcedir = os.path.join(test_dir, "static-link")
# Ensure we can static link and run a test app
subprocess.check_call(['meson', sourcedir, builddir], env=env)
subprocess.check_call(['ninja', '-C', builddir, 'test'], env=env)
# FIXME: This probably only works on Linux
out = subprocess.check_output(['ldd', os.path.join(builddir, 'test-static-link')], env=env).decode()
if 'libgio' in out:
print('test-static-link is dynamically linked on libgio')
exit(1)
# Ensure we can static link and run a test app
subprocess.check_call(["meson", sourcedir, builddir], env=env)
subprocess.check_call(["ninja", "-C", builddir, "test"], env=env)
# FIXME: This probably only works on Linux
out = subprocess.check_output(
["ldd", os.path.join(builddir, "test-static-link")], env=env
).decode()
if "libgio" in out:
print("test-static-link is dynamically linked on libgio")
exit(1)

View File

@ -31,146 +31,155 @@ import base64
from io import StringIO
# Log modes
class LogMode(object) :
LogToError, LogToDiagnostics, LogToYAML, LogToAttachment = range(4)
class LogMode(object):
LogToError, LogToDiagnostics, LogToYAML, LogToAttachment = range(4)
class TAPTestResult(unittest.TestResult):
def __init__(self, output_stream, error_stream, message_log, test_output_log):
super(TAPTestResult, self).__init__(self, output_stream)
self.output_stream = output_stream
self.error_stream = error_stream
self.orig_stdout = None
self.orig_stderr = None
self.message = None
self.test_output = None
self.message_log = message_log
self.test_output_log = test_output_log
self.output_stream.write("TAP version 13\n")
self._set_streams()
def __init__(self, output_stream, error_stream, message_log, test_output_log):
super(TAPTestResult, self).__init__(self, output_stream)
self.output_stream = output_stream
self.error_stream = error_stream
self.orig_stdout = None
self.orig_stderr = None
self.message = None
self.test_output = None
self.message_log = message_log
self.test_output_log = test_output_log
self.output_stream.write("TAP version 13\n")
self._set_streams()
def printErrors(self):
self.print_raw("1..%d\n" % self.testsRun)
self._reset_streams()
def printErrors(self):
self.print_raw("1..%d\n" % self.testsRun)
self._reset_streams()
def _set_streams(self):
self.orig_stdout = sys.stdout
self.orig_stderr = sys.stderr
if self.message_log == LogMode.LogToError:
self.message = self.error_stream
else:
self.message = StringIO()
if self.test_output_log == LogMode.LogToError:
self.test_output = self.error_stream
else:
self.test_output = StringIO()
def _set_streams(self):
self.orig_stdout = sys.stdout
self.orig_stderr = sys.stderr
if self.message_log == LogMode.LogToError:
self.message = self.error_stream
else:
self.message = StringIO()
if self.test_output_log == LogMode.LogToError:
self.test_output = self.error_stream
else:
self.test_output = StringIO()
if self.message_log == self.test_output_log:
self.test_output = self.message
sys.stdout = sys.stderr = self.test_output
if self.message_log == self.test_output_log:
self.test_output = self.message
sys.stdout = sys.stderr = self.test_output
def _reset_streams(self):
sys.stdout = self.orig_stdout
sys.stderr = self.orig_stderr
def _reset_streams(self):
sys.stdout = self.orig_stdout
sys.stderr = self.orig_stderr
def print_raw(self, text):
self.output_stream.write(text)
self.output_stream.flush()
def print_raw(self, text):
self.output_stream.write(text)
self.output_stream.flush()
def print_result(self, result, test, directive=None):
self.output_stream.write("%s %d %s" % (result, self.testsRun, test.id()))
if directive:
self.output_stream.write(" # " + directive)
self.output_stream.write("\n")
self.output_stream.flush()
def print_result(self, result, test, directive = None):
self.output_stream.write("%s %d %s" % (result, self.testsRun, test.id()))
if directive:
self.output_stream.write(" # " + directive)
self.output_stream.write("\n")
self.output_stream.flush()
def ok(self, test, directive=None):
self.print_result("ok", test, directive)
def ok(self, test, directive = None):
self.print_result("ok", test, directive)
def not_ok(self, test):
self.print_result("not ok", test)
def not_ok(self, test):
self.print_result("not ok", test)
def startTest(self, test):
super(TAPTestResult, self).startTest(test)
def startTest(self, test):
super(TAPTestResult, self).startTest(test)
def stopTest(self, test):
super(TAPTestResult, self).stopTest(test)
if self.message_log == self.test_output_log:
logs = [(self.message_log, self.message, "output")]
else:
logs = [
(self.test_output_log, self.test_output, "test_output"),
(self.message_log, self.message, "message"),
]
for log_mode, log, log_name in logs:
if log_mode != LogMode.LogToError:
output = log.getvalue()
if len(output):
if log_mode == LogMode.LogToYAML:
self.print_raw(" ---\n")
self.print_raw(" " + log_name + ": |\n")
self.print_raw(
" " + output.rstrip().replace("\n", "\n ") + "\n"
)
self.print_raw(" ...\n")
elif log_mode == LogMode.LogToAttachment:
self.print_raw(" ---\n")
self.print_raw(" " + log_name + ":\n")
self.print_raw(" File-Name: " + log_name + ".txt\n")
self.print_raw(" File-Type: text/plain\n")
self.print_raw(
" File-Content: " + base64.b64encode(output) + "\n"
)
self.print_raw(" ...\n")
else:
self.print_raw(
"# " + output.rstrip().replace("\n", "\n# ") + "\n"
)
# Truncate doesn't change the current stream position.
# Seek to the beginning to avoid extensions on subsequent writes.
log.seek(0)
log.truncate(0)
def stopTest(self, test):
super(TAPTestResult, self).stopTest(test)
if self.message_log == self.test_output_log:
logs = [(self.message_log, self.message, "output")]
else:
logs = [
(self.test_output_log, self.test_output, "test_output"),
(self.message_log, self.message, "message")
]
for log_mode, log, log_name in logs:
if log_mode != LogMode.LogToError:
output = log.getvalue()
if len(output):
if log_mode == LogMode.LogToYAML:
self.print_raw(" ---\n")
self.print_raw(" " + log_name + ": |\n")
self.print_raw(" " + output.rstrip().replace("\n", "\n ") + "\n")
self.print_raw(" ...\n")
elif log_mode == LogMode.LogToAttachment:
self.print_raw(" ---\n")
self.print_raw(" " + log_name + ":\n")
self.print_raw(" File-Name: " + log_name + ".txt\n")
self.print_raw(" File-Type: text/plain\n")
self.print_raw(" File-Content: " + base64.b64encode(output) + "\n")
self.print_raw(" ...\n")
else:
self.print_raw("# " + output.rstrip().replace("\n", "\n# ") + "\n")
# Truncate doesn't change the current stream position.
# Seek to the beginning to avoid extensions on subsequent writes.
log.seek(0)
log.truncate(0)
def addSuccess(self, test):
super(TAPTestResult, self).addSuccess(test)
self.ok(test)
def addSuccess(self, test):
super(TAPTestResult, self).addSuccess(test)
self.ok(test)
def addError(self, test, err):
super(TAPTestResult, self).addError(test, err)
self.message.write(self.errors[-1][1] + "\n")
self.not_ok(test)
def addError(self, test, err):
super(TAPTestResult, self).addError(test, err)
self.message.write(self.errors[-1][1] + "\n")
self.not_ok(test)
def addFailure(self, test, err):
super(TAPTestResult, self).addFailure(test, err)
self.message.write(self.failures[-1][1] + "\n")
self.not_ok(test)
def addFailure(self, test, err):
super(TAPTestResult, self).addFailure(test, err)
self.message.write(self.failures[-1][1] + "\n")
self.not_ok(test)
def addSkip(self, test, reason):
super(TAPTestResult, self).addSkip(test, reason)
self.ok(test, "SKIP " + reason)
def addSkip(self, test, reason):
super(TAPTestResult, self).addSkip(test, reason)
self.ok(test, "SKIP " + reason)
def addExpectedFailure(self, test, err):
super(TAPTestResult, self).addExpectedFailure(test, err)
self.ok(test)
def addExpectedFailure(self, test, err):
super(TAPTestResult, self).addExpectedFailure(test, err)
self.ok(test)
def addUnexpectedSuccess(self, test):
super(TAPTestResult, self).addUnexpectedSuccess(test)
self.message.write("Unexpected success" + "\n")
self.not_ok(test)
def addUnexpectedSuccess(self, test):
super(TAPTestResult, self).addUnexpectedSuccess(test)
self.message.write("Unexpected success" + "\n")
self.not_ok(test)
class TAPTestRunner(object):
def __init__(self,
message_log = LogMode.LogToYAML,
test_output_log = LogMode.LogToDiagnostics,
output_stream = sys.stdout, error_stream = sys.stderr):
self.output_stream = output_stream
self.error_stream = error_stream
self.message_log = message_log
self.test_output_log = test_output_log
def __init__(
self,
message_log=LogMode.LogToYAML,
test_output_log=LogMode.LogToDiagnostics,
output_stream=sys.stdout,
error_stream=sys.stderr,
):
self.output_stream = output_stream
self.error_stream = error_stream
self.message_log = message_log
self.test_output_log = test_output_log
def run(self, test):
result = TAPTestResult(
self.output_stream,
self.error_stream,
self.message_log,
self.test_output_log)
test(result)
result.printErrors()
def run(self, test):
result = TAPTestResult(
self.output_stream,
self.error_stream,
self.message_log,
self.test_output_log,
)
test(result)
result.printErrors()
return result
return result

View File

@ -5,47 +5,55 @@ if sys.version_info[0] >= 3:
long = int
# This is not quite right, as local vars may override symname
def read_global_var (symname):
def read_global_var(symname):
return gdb.selected_frame().read_var(symname)
def g_quark_to_string (quark):
def g_quark_to_string(quark):
if quark is None:
return None
quark = long(quark)
if quark == 0:
return None
try:
val = read_global_var ("quarks")
max_q = long(read_global_var ("quark_seq_id"))
val = read_global_var("quarks")
max_q = long(read_global_var("quark_seq_id"))
except:
try:
val = read_global_var ("g_quarks")
max_q = long(read_global_var ("g_quark_seq_id"))
val = read_global_var("g_quarks")
max_q = long(read_global_var("g_quark_seq_id"))
except:
return None;
return None
if quark < max_q:
return val[quark].string()
return None
# We override the node printers too, so that node->next is not expanded
class GListNodePrinter:
"Prints a GList node"
def __init__ (self, val):
def __init__(self, val):
self.val = val
def to_string (self):
return "{data=%s, next=0x%x, prev=0x%x}" % (str(self.val["data"]), long(self.val["next"]), long(self.val["prev"]))
def to_string(self):
return "{data=%s, next=0x%x, prev=0x%x}" % (
str(self.val["data"]),
long(self.val["next"]),
long(self.val["prev"]),
)
class GSListNodePrinter:
"Prints a GSList node"
def __init__ (self, val):
def __init__(self, val):
self.val = val
def to_string (self):
def to_string(self):
return "{data=%s, next=0x%x}" % (str(self.val["data"]), long(self.val["next"]))
class GListPrinter:
"Prints a GList"
@ -61,27 +69,28 @@ class GListPrinter:
def next(self):
if self.link == 0:
raise StopIteration
data = self.link['data']
self.link = self.link['next']
data = self.link["data"]
self.link = self.link["next"]
count = self.count
self.count = self.count + 1
return ('[%d]' % count, data)
return ("[%d]" % count, data)
__next__ = next
def __init__ (self, val, listtype):
def __init__(self, val, listtype):
self.val = val
self.listtype = listtype
def children(self):
return self._iterator(self.val, self.listtype)
def to_string (self):
return "0x%x" % (long(self.val))
def to_string(self):
return "0x%x" % (long(self.val))
def display_hint (self):
def display_hint(self):
return "array"
class GHashPrinter:
"Prints a GHashTable"
@ -90,7 +99,9 @@ class GHashPrinter:
def __init__(self, ptr, big_items):
self._big_items = big_items
self._gpointer_type = gdb.lookup_type("gpointer")
item_type = self._gpointer_type if self._big_items else gdb.lookup_type("guint")
item_type = (
self._gpointer_type if self._big_items else gdb.lookup_type("guint")
)
self._items = ptr.cast(item_type.pointer())
@ -124,18 +135,18 @@ class GHashPrinter:
self.value = None
return v
while long(self.pos) < long(self.size):
if long (self.hashes[self.pos]) >= 2:
if long(self.hashes[self.pos]) >= 2:
key = self.keys[self.pos]
val = self.values[self.pos]
if self.keys_are_strings:
key = key.cast (gdb.lookup_type("char").pointer())
key = key.cast(gdb.lookup_type("char").pointer())
# Queue value for next result
self.value = ('[%dv]'% (self.pos), val)
self.value = ("[%dv]" % (self.pos), val)
# Increment pos and return key
key = ('[%dk]'% (self.pos), key)
key = ("[%dk]" % (self.pos), key)
self.pos += 1
return key
@ -144,33 +155,38 @@ class GHashPrinter:
__next__ = next
def __init__ (self, val):
def __init__(self, val):
self.val = val
self.keys_are_strings = False
try:
string_hash = read_global_var ("g_str_hash")
string_hash = read_global_var("g_str_hash")
except:
string_hash = None
if self.val != 0 and string_hash != None and self.val["hash_func"] == string_hash:
if (
self.val != 0
and string_hash != None
and self.val["hash_func"] == string_hash
):
self.keys_are_strings = True
def children(self):
return self._iterator(self.val, self.keys_are_strings)
def to_string (self):
return "0x%x" % (long(self.val))
def to_string(self):
return "0x%x" % (long(self.val))
def display_hint (self):
def display_hint(self):
return "map"
def pretty_printer_lookup (val):
def pretty_printer_lookup(val):
# None yet, want things like hash table and list
type = val.type.unqualified()
# If it points to a reference, get the reference.
if type.code == gdb.TYPE_CODE_REF:
type = type.target ()
type = type.target()
if type.code == gdb.TYPE_CODE_PTR:
type = type.target().unqualified()
@ -189,52 +205,54 @@ def pretty_printer_lookup (val):
return GListPrinter(val, "GSList")
return None
def register (obj):
def register(obj):
if obj is None:
obj = gdb
obj.pretty_printers.append(pretty_printer_lookup)
class ForeachCommand (gdb.Command):
class ForeachCommand(gdb.Command):
"""Foreach on list"""
def __init__ (self):
super (ForeachCommand, self).__init__ ("gforeach",
gdb.COMMAND_DATA,
gdb.COMPLETE_SYMBOL)
def __init__(self):
super(ForeachCommand, self).__init__(
"gforeach", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL
)
def valid_name (self, name):
def valid_name(self, name):
if not name[0].isalpha():
return False
return True
def parse_args (self, arg):
def parse_args(self, arg):
i = arg.find(" ")
if i <= 0:
raise Exception ("No var specified")
raise Exception("No var specified")
var = arg[:i]
if not self.valid_name(var):
raise Exception ("Invalid variable name")
raise Exception("Invalid variable name")
while i < len (arg) and arg[i].isspace():
while i < len(arg) and arg[i].isspace():
i = i + 1
if arg[i:i+2] != "in":
raise Exception ("Invalid syntax, missing in")
if arg[i : i + 2] != "in":
raise Exception("Invalid syntax, missing in")
i = i + 2
while i < len (arg) and arg[i].isspace():
while i < len(arg) and arg[i].isspace():
i = i + 1
colon = arg.find (":", i)
colon = arg.find(":", i)
if colon == -1:
raise Exception ("Invalid syntax, missing colon")
raise Exception("Invalid syntax, missing colon")
val = arg[i:colon]
colon = colon + 1
while colon < len (arg) and arg[colon].isspace():
while colon < len(arg) and arg[colon].isspace():
colon = colon + 1
command = arg[colon:]
@ -242,25 +260,25 @@ class ForeachCommand (gdb.Command):
return (var, val, command)
def do_iter(self, arg, item, command):
item = item.cast (gdb.lookup_type("void").pointer())
item = item.cast(gdb.lookup_type("void").pointer())
item = long(item)
to_eval = "set $%s = (void *)0x%x\n"%(arg, item)
to_eval = "set $%s = (void *)0x%x\n" % (arg, item)
gdb.execute(to_eval)
gdb.execute(command)
def slist_iterator (self, arg, container, command):
l = container.cast (gdb.lookup_type("GSList").pointer())
def slist_iterator(self, arg, container, command):
l = container.cast(gdb.lookup_type("GSList").pointer())
while long(l) != 0:
self.do_iter (arg, l["data"], command)
self.do_iter(arg, l["data"], command)
l = l["next"]
def list_iterator (self, arg, container, command):
l = container.cast (gdb.lookup_type("GList").pointer())
def list_iterator(self, arg, container, command):
l = container.cast(gdb.lookup_type("GList").pointer())
while long(l) != 0:
self.do_iter (arg, l["data"], command)
self.do_iter(arg, l["data"], command)
l = l["next"]
def pick_iterator (self, container):
def pick_iterator(self, container):
t = container.type.unqualified()
if t.code == gdb.TYPE_CODE_PTR:
t = t.target().unqualified()
@ -269,12 +287,13 @@ class ForeachCommand (gdb.Command):
return self.slist_iterator
if t == "GList":
return self.list_iterator
raise Exception("Invalid container type %s"%(str(container.type)))
raise Exception("Invalid container type %s" % (str(container.type)))
def invoke (self, arg, from_tty):
def invoke(self, arg, from_tty):
(var, container, command) = self.parse_args(arg)
container = gdb.parse_and_eval (container)
container = gdb.parse_and_eval(container)
func = self.pick_iterator(container)
func(var, container, command)
ForeachCommand ()
ForeachCommand()

View File

@ -10,15 +10,16 @@ localedir = sys.argv[1]
# returns true if the name looks like a POSIX locale name
def looks_like_locale(name):
name, _, variant = name.partition('@')
name, _, variant = name.partition("@")
if '_' not in name:
if "_" not in name:
return False
lang, _, land = name.partition('_')
lang, _, land = name.partition("_")
return len(lang) == 2 or len(lang) == 3 and len(land) == 2
# handles <U1234> style escapes
def unescape(string):
chunks = []
@ -27,27 +28,29 @@ def unescape(string):
i = 0
while i < n:
start_escape = string.find('<', i)
start_escape = string.find("<", i)
if start_escape == -1:
chunks.append(string[i:])
break
assert string[start_escape:start_escape + 2] == '<U'
assert string[start_escape : start_escape + 2] == "<U"
start_escape += 2
end_escape = string.find('>', start_escape)
end_escape = string.find(">", start_escape)
assert end_escape != -1
chunks.append(chr(int(string[start_escape:end_escape], 16)))
i = end_escape + 1
return ''.join(chunks)
return "".join(chunks)
# Checks if a string is ascii
def is_ascii(string):
return all(ord(c) < 0x80 for c in string)
# A Mapping is a map from non-ascii strings to ascii strings.
#
# It corresponds to a sequence of one or more mapping lines:
@ -68,25 +71,25 @@ class Mapping:
# dictionary, with the origin string as the key. In the case of
# IGNORE, stores the empty string.
def consider_mapping_line(self, line):
key, value, rest = (line + ' % comment').split(maxsplit=2)
key, value, rest = (line + " % comment").split(maxsplit=2)
key = unescape(key)
for alternative in value.split(';'):
for alternative in value.split(";"):
if alternative[0] == '"' and alternative[-1] == '"':
unescaped = unescape(alternative[1:-1])
if is_ascii(unescaped):
self.mapping[key] = unescaped
break
elif alternative[0] == '<' and alternative[-1] == '>':
elif alternative[0] == "<" and alternative[-1] == ">":
unescaped = unescape(alternative)
if is_ascii(unescaped):
self.mapping[key] = unescaped
break
elif alternative == 'IGNORE':
self.mapping[key] = ''
elif alternative == "IGNORE":
self.mapping[key] = ""
break
# Performs a normal dictionary merge, but ensures that there are no
@ -109,6 +112,7 @@ class Mapping:
return self.serialised
# A Chain is a sequence of mappings and chains.
#
# A chain contains another chain whenever "copy" or "include" is
@ -135,16 +139,16 @@ class Chain:
in_lc_ctype = False
in_translit = False
fp = open(filename, encoding='ascii', errors='surrogateescape')
fp = open(filename, encoding="ascii", errors="surrogateescape")
for line in fp:
line = line.strip()
if in_lc_ctype:
if line == 'END LC_CTYPE':
if line == "END LC_CTYPE":
break
if line.startswith('copy') or line.startswith('include'):
if line.startswith("copy") or line.startswith("include"):
if current_mapping:
self.chain.append(current_mapping)
@ -155,29 +159,29 @@ class Chain:
current_mapping = None
elif line == 'translit_start':
elif line == "translit_start":
in_translit = True
elif line == 'translit_end':
elif line == "translit_end":
in_translit = False
elif in_translit and line.startswith('<U'):
elif in_translit and line.startswith("<U"):
if not current_mapping:
current_mapping = Mapping()
current_mapping.consider_mapping_line(line)
elif line == '' or line.startswith('%'):
elif line == "" or line.startswith("%"):
pass
elif 'default_missing <U003F>':
elif "default_missing <U003F>":
pass
elif in_translit:
print('unknown line:', line)
print("unknown line:", line)
assert False
elif line == 'LC_CTYPE':
elif line == "LC_CTYPE":
in_lc_ctype = True
if current_mapping:
@ -199,7 +203,9 @@ class Chain:
i = 0
while i < len(self.chain) - 1:
if isinstance(self.chain[i], Mapping) and isinstance(self.chain[i + 1], Mapping):
if isinstance(self.chain[i], Mapping) and isinstance(
self.chain[i + 1], Mapping
):
# We have two mappings in a row. Try to merge them.
self.chain[i].merge_mapping(self.chain[i + 1])
del self.chain[i + 1]
@ -215,8 +221,11 @@ class Chain:
return self.serialised
# Chain cache -- allows sharing of common chains
chains = {}
def get_chain(name):
if not name in chains:
chains[name] = Chain(name)
@ -227,10 +236,11 @@ def get_chain(name):
# Remove the country name from a locale, preserving variant
# eg: 'sr_RS@latin' -> 'sr@latin'
def remove_country(string):
base, at, variant = string.partition('@')
lang, _, land = base.partition('_')
base, at, variant = string.partition("@")
lang, _, land = base.partition("_")
return lang + at + variant
def encode_range(start, end):
assert start <= end
length = end - start
@ -244,8 +254,10 @@ def encode_range(start, end):
return result
def c_pair_array(array):
return '{ ' + ', '.join ('{ %u, %u }' % pair for pair in array) + ' };'
return "{ " + ", ".join("{ %u, %u }" % pair for pair in array) + " };"
class Serialiser:
def __init__(self):
@ -284,7 +296,9 @@ class Serialiser:
languages = list(set(remove_country(locale) for locale in self.locales))
for language in languages:
locales = [locale for locale in self.locales if remove_country(locale) == language]
locales = [
locale for locale in self.locales if remove_country(locale) == language
]
item_id = self.locales[locales[0]]
if all(self.locales[locale] == item_id for locale in locales):
@ -294,8 +308,8 @@ class Serialiser:
# Check if a variant is the same as the non-variant form
# eg: 'de@euro' and 'de'
for variant in list(locale for locale in self.locales if '@' in locale):
base, _, _ = variant.partition('@')
for variant in list(locale for locale in self.locales if "@" in locale):
base, _, _ = variant.partition("@")
if base in self.locales and self.locales[base] == self.locales[variant]:
del self.locales[variant]
@ -305,19 +319,19 @@ class Serialiser:
del self.locales[locale]
def to_c(self):
src_table = ''
ascii_table = ''
src_table = ""
ascii_table = ""
mappings_table = []
mapping_ranges = []
chains_table = []
chain_starts = []
locale_names = ''
locale_names = ""
locale_index = []
max_lookup = 0
max_localename = 0
for mapping in self.mappings:
mapping_ranges.append ((len(mappings_table), len(mapping)))
mapping_ranges.append((len(mappings_table), len(mapping)))
for key in sorted(mapping):
if len(key) == 1 and ord(key[0]) < 0x8000:
@ -326,7 +340,7 @@ class Serialiser:
existing = src_table.find(key)
if existing == -1:
start = len(src_table)
assert all(ord(c) <= 0x10ffff for c in key)
assert all(ord(c) <= 0x10FFFF for c in key)
src_table += key
src_range = encode_range(start, len(src_table))
max_lookup = max(max_lookup, len(key))
@ -346,7 +360,7 @@ class Serialiser:
else:
ascii_range = encode_range(existing, existing + len(value))
mappings_table.append ((src_range, ascii_range))
mappings_table.append((src_range, ascii_range))
mapping_end = len(mappings_table)
@ -354,15 +368,15 @@ class Serialiser:
chain_starts.append(len(chains_table))
for item_id in reversed(chain):
assert item_id < 0xff
assert item_id < 0xFF
chains_table.append(item_id)
chains_table.append(0xff)
chains_table.append(0xFF)
for locale in sorted(self.locales):
max_localename = max(max_localename, len(locale))
name_offset = len(locale_names)
assert all(ord(c) <= 0x7f for c in locale)
locale_names += (locale + '\0')
assert all(ord(c) <= 0x7F for c in locale)
locale_names += locale + "\0"
item_id = self.locales[locale]
@ -370,30 +384,60 @@ class Serialiser:
assert item_id < 256
locale_index.append((name_offset, item_id))
print('/* Generated by update-gtranslit.py */')
print('#define MAX_KEY_SIZE', max_lookup)
print('#define MAX_LOCALE_NAME', max_localename)
print('static const gunichar src_table[] = {', ', '.join(str(ord(c)) for c in src_table), '};')
print("/* Generated by update-gtranslit.py */")
print("#define MAX_KEY_SIZE", max_lookup)
print("#define MAX_LOCALE_NAME", max_localename)
print(
"static const gunichar src_table[] = {",
", ".join(str(ord(c)) for c in src_table),
"};",
)
# cannot do this in plain ascii because of trigraphs... :(
print('static const gchar ascii_table[] = {', ', '.join(str(ord(c)) for c in ascii_table), '};')
print('static const struct mapping_entry mappings_table[] =', c_pair_array (mappings_table))
print('static const struct mapping_range mapping_ranges[] =', c_pair_array (mapping_ranges))
print('static const guint8 chains_table[] = {', ', '.join(str(i) for i in chains_table), '};')
print('static const guint8 chain_starts[] = {', ', '.join(str(i) for i in chain_starts), '};')
print('static const gchar locale_names[] = "' + locale_names.replace('\0', '\\0') + '";')
print('static const struct locale_entry locale_index[] = ', c_pair_array (locale_index))
print('static const guint8 default_item_id = %u;' % (self.default,))
print(
"static const gchar ascii_table[] = {",
", ".join(str(ord(c)) for c in ascii_table),
"};",
)
print(
"static const struct mapping_entry mappings_table[] =",
c_pair_array(mappings_table),
)
print(
"static const struct mapping_range mapping_ranges[] =",
c_pair_array(mapping_ranges),
)
print(
"static const guint8 chains_table[] = {",
", ".join(str(i) for i in chains_table),
"};",
)
print(
"static const guint8 chain_starts[] = {",
", ".join(str(i) for i in chain_starts),
"};",
)
print(
'static const gchar locale_names[] = "'
+ locale_names.replace("\0", "\\0")
+ '";'
)
print(
"static const struct locale_entry locale_index[] = ",
c_pair_array(locale_index),
)
print("static const guint8 default_item_id = %u;" % (self.default,))
def dump(self):
print(self.mappings)
print(self.chains)
print(self.locales)
locales = []
for name in os.listdir(localedir):
if looks_like_locale(name):
chain = get_chain(name)
locales.append (chain)
locales.append(chain)
chain.links += 1
serialiser = Serialiser()
@ -401,8 +445,8 @@ serialiser = Serialiser()
for locale in locales:
serialiser.add_locale(locale.name, locale.serialise(serialiser))
i18n = get_chain('i18n').serialise(serialiser)
combining = get_chain('translit_combining').serialise(serialiser)
i18n = get_chain("i18n").serialise(serialiser)
combining = get_chain("translit_combining").serialise(serialiser)
serialiser.add_default(serialiser.add_chain([i18n, combining]))
serialiser.optimise_locales()

View File

@ -7,25 +7,28 @@ if sys.version_info[0] >= 3:
long = int
else:
import itertools
map = itertools.imap
# FrameDecorator is new in gdb 7.7, so we adapt to its absence.
try:
import gdb.FrameDecorator
HAVE_GDB_FRAMEDECORATOR = True
FrameDecorator = gdb.FrameDecorator.FrameDecorator
except ImportError:
HAVE_GDB_FRAMEDECORATOR = False
# This is not quite right, as local vars may override symname
def read_global_var (symname):
def read_global_var(symname):
return gdb.selected_frame().read_var(symname)
def g_type_to_typenode (gtype):
def lookup_fundamental_type (typenode):
def g_type_to_typenode(gtype):
def lookup_fundamental_type(typenode):
if typenode == 0:
return None
val = read_global_var ("static_fundamental_type_nodes")
val = read_global_var("static_fundamental_type_nodes")
if val is None:
return None
return val[typenode >> 2].address
@ -33,19 +36,21 @@ def g_type_to_typenode (gtype):
gtype = long(gtype)
typenode = gtype - gtype % 4
if typenode > (255 << 2):
typenode = gdb.Value(typenode).cast (gdb.lookup_type("TypeNode").pointer())
typenode = gdb.Value(typenode).cast(gdb.lookup_type("TypeNode").pointer())
else:
typenode = lookup_fundamental_type (typenode)
typenode = lookup_fundamental_type(typenode)
return typenode
def g_type_to_name (gtype):
def g_type_to_name(gtype):
typenode = g_type_to_typenode(gtype)
if typenode != None:
return glib_gdb.g_quark_to_string (typenode["qname"])
return glib_gdb.g_quark_to_string(typenode["qname"])
return None
def is_g_type_instance (val):
def is_g_type_instance_helper (type):
def is_g_type_instance(val):
def is_g_type_instance_helper(type):
if str(type) == "GTypeInstance":
return True
@ -56,7 +61,7 @@ def is_g_type_instance (val):
return False
fields = type.fields()
if len (fields) < 1:
if len(fields) < 1:
return False
first_field = fields[0]
@ -66,51 +71,55 @@ def is_g_type_instance (val):
if type.code != gdb.TYPE_CODE_PTR:
return False
type = type.target()
return is_g_type_instance_helper (type)
return is_g_type_instance_helper(type)
def g_type_name_from_instance (instance):
def g_type_name_from_instance(instance):
if long(instance) != 0:
try:
inst = instance.cast (gdb.lookup_type("GTypeInstance").pointer())
inst = instance.cast(gdb.lookup_type("GTypeInstance").pointer())
klass = inst["g_class"]
gtype = klass["g_type"]
name = g_type_to_name (gtype)
name = g_type_to_name(gtype)
return name
except RuntimeError:
pass
return None
class GTypePrettyPrinter:
"Prints a GType instance pointer"
def __init__ (self, val):
def __init__(self, val):
self.val = val
def to_string (self):
name = g_type_name_from_instance (self.val)
def to_string(self):
name = g_type_name_from_instance(self.val)
if name:
return ("0x%x [%s]")% (long(self.val), name)
return ("0x%x") % (long(self.val))
return ("0x%x [%s]") % (long(self.val), name)
return ("0x%x") % (long(self.val))
def is_g_type_class_instance (val):
def is_g_type_class_instance(val):
type = val.type
if type.code != gdb.TYPE_CODE_PTR:
return False
return str(type.target()) == "GTypeClass"
class GTypeHandlePrettyPrinter:
"Prints a GType instance"
def __init__ (self, val, hint = ""):
def __init__(self, val, hint=""):
self.val = val
self.hint = hint
def to_string (self):
def to_string(self):
typenode = g_type_to_typenode(self.val)
if typenode != None:
name = glib_gdb.g_quark_to_string (typenode["qname"])
s = ("0x%x [%s%s")% (long(self.val), self.hint, name)
for i in range (1, int(typenode["n_supers"])):
name = glib_gdb.g_quark_to_string(typenode["qname"])
s = ("0x%x [%s%s") % (long(self.val), self.hint, name)
for i in range(1, int(typenode["n_supers"])):
node = g_type_to_typenode(typenode["supers"][i])
if node:
name = glib_gdb.g_quark_to_string(node["qname"])
@ -119,163 +128,170 @@ class GTypeHandlePrettyPrinter:
s += "/" + name
return s + "]"
else:
return ("0x%x") % (long(self.val))
return ("0x%x") % (long(self.val))
def pretty_printer_lookup (val):
if is_g_type_instance (val):
return GTypePrettyPrinter (val)
def pretty_printer_lookup(val):
if is_g_type_instance(val):
return GTypePrettyPrinter(val)
if str(val.type) == "GType":
return GTypeHandlePrettyPrinter (val)
if is_g_type_class_instance (val):
return GTypeHandlePrettyPrinter (val["g_type"], "g_type: ")
return GTypeHandlePrettyPrinter(val)
if is_g_type_class_instance(val):
return GTypeHandlePrettyPrinter(val["g_type"], "g_type: ")
return None
def get_signal_name (id):
def get_signal_name(id):
if id is None:
return None
id = long(id)
if id == 0:
return None
val = read_global_var ("g_signal_nodes")
max_s = read_global_var ("g_n_signal_nodes")
val = read_global_var("g_signal_nodes")
max_s = read_global_var("g_n_signal_nodes")
max_s = long(max_s)
if id < max_s:
return val[id]["name"].string()
return None
def frame_name(frame):
return str(frame.function())
def frame_var(frame, var):
return frame.inferior_frame().read_var(var)
class SignalFrame(FrameDecorator):
def __init__ (self, frames):
def __init__(self, frames):
FrameDecorator.__init__(self, frames[-1])
self.frame = frames[-1]
self.frames = frames
def name (self):
def name(self):
return "signal-emission"
def read_var (self, frame, name, array = None):
def read_var(self, frame, name, array=None):
try:
v = frame_var (frame, name)
v = frame_var(frame, name)
if v is None or v.is_optimized_out:
return None
if array != None:
array.append (v)
array.append(v)
return v
except ValueError:
return None
def read_object (self, frame, name, array = None):
def read_object(self, frame, name, array=None):
try:
v = frame_var (frame, name)
v = frame_var(frame, name)
if v is None or v.is_optimized_out:
return None
v = v.cast (gdb.lookup_type("GObject").pointer())
v = v.cast(gdb.lookup_type("GObject").pointer())
# Ensure this is a somewhat correct object pointer
if v != None and g_type_name_from_instance (v):
if v != None and g_type_name_from_instance(v):
if array != None:
array.append (v)
array.append(v)
return v
return None
except ValueError:
return None
def append (self, array, obj):
def append(self, array, obj):
if obj != None:
array.append (obj)
array.append(obj)
def or_join_array (self, array):
def or_join_array(self, array):
if len(array) == 0:
return "???"
else:
return ' or '.join(set(map(str, array)))
return " or ".join(set(map(str, array)))
def get_detailed_signal_from_frame(self, frame, signal):
detail = self.read_var (frame, "detail")
detail = glib_gdb.g_quark_to_string (detail)
detail = self.read_var(frame, "detail")
detail = glib_gdb.g_quark_to_string(detail)
if detail is not None:
return signal + ":" + detail
else:
return detail
def function (self):
def function(self):
instances = []
signals = []
for frame in self.frames:
name = frame_name(frame)
if name == "signal_emit_unlocked_R":
self.read_object (frame, "instance", instances)
node = self.read_var (frame, "node")
self.read_object(frame, "instance", instances)
node = self.read_var(frame, "node")
if node:
signal = node["name"].string()
signal = self.get_detailed_signal_from_frame(frame, signal)
self.append(signals, signal)
if name == "g_signal_emitv":
instance_and_params = self.read_var (frame, "instance_and_params")
instance_and_params = self.read_var(frame, "instance_and_params")
if instance_and_params:
instance = instance_and_params[0]["v_pointer"].cast (gdb.Type("GObject").pointer())
self.append (instances, instance)
id = self.read_var (frame, "signal_id")
signal = get_signal_name (id)
instance = instance_and_params[0]["v_pointer"].cast(
gdb.Type("GObject").pointer()
)
self.append(instances, instance)
id = self.read_var(frame, "signal_id")
signal = get_signal_name(id)
if signal:
signal = self.get_detailed_signal_from_frame(frame, signal)
self.append (signals, signal)
self.append(signals, signal)
if name == "g_signal_emit_valist" or name == "g_signal_emit":
self.read_object (frame, "instance", instances)
id = self.read_var (frame, "signal_id")
signal = get_signal_name (id)
self.read_object(frame, "instance", instances)
id = self.read_var(frame, "signal_id")
signal = get_signal_name(id)
if signal:
signal = self.get_detailed_signal_from_frame(frame, signal)
self.append (signals, signal)
self.append(signals, signal)
if name == "g_signal_emit_by_name":
self.read_object (frame, "instance", instances)
self.read_var (frame, "detailed_signal", signals)
self.read_object(frame, "instance", instances)
self.read_var(frame, "detailed_signal", signals)
break
instance = self.or_join_array (instances)
signal = self.or_join_array (signals)
instance = self.or_join_array(instances)
signal = self.or_join_array(signals)
return "<emit signal %s on instance %s>" % (signal, instance)
return "<emit signal %s on instance %s>" % (signal, instance)
def elided (self):
def elided(self):
return self.frames[0:-1]
def describe (self, stream, full):
stream.write (" " + self.function () + "\n")
def describe(self, stream, full):
stream.write(" " + self.function() + "\n")
class GFrameDecorator:
def __init__ (self, iter):
def __init__(self, iter):
self.queue = []
self.iter = iter
def __iter__ (self):
def __iter__(self):
return self
def fill (self):
def fill(self):
while len(self.queue) <= 8:
try:
f = next(self.iter)
self.queue.append (f)
self.queue.append(f)
except StopIteration:
return
def find_signal_emission (self):
for i in range (min (len(self.queue), 3)):
def find_signal_emission(self):
for i in range(min(len(self.queue), 3)):
if frame_name(self.queue[i]) == "signal_emit_unlocked_R":
return i
return -1
def next (self):
def next(self):
# Ensure we have enough frames for a full signal emission
self.fill()
@ -283,24 +299,26 @@ class GFrameDecorator:
if len(self.queue) == 0:
raise StopIteration
emission = self.find_signal_emission ()
emission = self.find_signal_emission()
if emission > 0:
start = emission
while True:
if start == 0:
break
prev_name = frame_name(self.queue[start-1])
prev_name = frame_name(self.queue[start - 1])
if prev_name.find("_marshal_") >= 0 or prev_name == "g_closure_invoke":
start = start - 1
else:
break
end = emission + 1
while end < len(self.queue):
if frame_name(self.queue[end]) in ["g_signal_emitv",
"g_signal_emit_valist",
"g_signal_emit",
"g_signal_emit_by_name",
"_g_closure_invoke_va"]:
if frame_name(self.queue[end]) in [
"g_signal_emitv",
"g_signal_emit_valist",
"g_signal_emit",
"g_signal_emit_by_name",
"_g_closure_invoke_va",
]:
end = end + 1
else:
break
@ -311,18 +329,20 @@ class GFrameDecorator:
return self.queue.pop(0)
def __next__ (self):
def __next__(self):
return self.next()
class GFrameFilter(object):
name = 'glib'
name = "glib"
enabled = True
priority = 100
def filter(self, iterator):
return GFrameDecorator(iterator)
def register (obj):
def register(obj):
if obj is None:
obj = gdb

View File

@ -32,7 +32,7 @@ import unittest
import taptestrunner
Result = collections.namedtuple('Result', ('info', 'out', 'err', 'subs'))
Result = collections.namedtuple("Result", ("info", "out", "err", "subs"))
class TestGenmarshal(unittest.TestCase):
@ -47,22 +47,23 @@ class TestGenmarshal(unittest.TestCase):
parsing and generation code out into a library and unit test that, and
convert this test to just check command line behaviour.
"""
# Track the cwd, we want to back out to that to clean up our tempdir
cwd = ''
cwd = ""
def setUp(self):
self.timeout_seconds = 10 # seconds per test
self.tmpdir = tempfile.TemporaryDirectory()
self.cwd = os.getcwd()
os.chdir(self.tmpdir.name)
print('tmpdir:', self.tmpdir.name)
if 'G_TEST_BUILDDIR' in os.environ:
self.__genmarshal = \
os.path.join(os.environ['G_TEST_BUILDDIR'], '..',
'glib-genmarshal')
print("tmpdir:", self.tmpdir.name)
if "G_TEST_BUILDDIR" in os.environ:
self.__genmarshal = os.path.join(
os.environ["G_TEST_BUILDDIR"], "..", "glib-genmarshal"
)
else:
self.__genmarshal = shutil.which('glib-genmarshal')
print('genmarshal:', self.__genmarshal)
self.__genmarshal = shutil.which("glib-genmarshal")
print("genmarshal:", self.__genmarshal)
def tearDown(self):
os.chdir(self.cwd)
@ -73,48 +74,53 @@ class TestGenmarshal(unittest.TestCase):
# shebang lines are not supported on native
# Windows consoles
if os.name == 'nt':
if os.name == "nt":
argv.insert(0, sys.executable)
argv.extend(args)
print('Running:', argv)
print("Running:", argv)
env = os.environ.copy()
env['LC_ALL'] = 'C.UTF-8'
print('Environment:', env)
env["LC_ALL"] = "C.UTF-8"
print("Environment:", env)
# We want to ensure consistent line endings...
info = subprocess.run(argv, timeout=self.timeout_seconds,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
universal_newlines=True)
info = subprocess.run(
argv,
timeout=self.timeout_seconds,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
universal_newlines=True,
)
info.check_returncode()
out = info.stdout.strip()
err = info.stderr.strip()
# Known substitutions for standard boilerplate
subs = {
'standard_top_comment':
'This file is generated by glib-genmarshal, do not modify '
'it. This code is licensed under the same license as the '
'containing project. Note that it links to GLib, so must '
'comply with the LGPL linking clauses.',
'standard_top_pragma': dedent(
'''
"standard_top_comment": "This file is generated by glib-genmarshal, do not modify "
"it. This code is licensed under the same license as the "
"containing project. Note that it links to GLib, so must "
"comply with the LGPL linking clauses.",
"standard_top_pragma": dedent(
"""
#ifndef __G_CCLOSURE_USER_MARSHAL_MARSHAL_H__
#define __G_CCLOSURE_USER_MARSHAL_MARSHAL_H__
''').strip(),
'standard_bottom_pragma': dedent(
'''
"""
).strip(),
"standard_bottom_pragma": dedent(
"""
#endif /* __G_CCLOSURE_USER_MARSHAL_MARSHAL_H__ */
''').strip(),
'standard_includes': dedent(
'''
"""
).strip(),
"standard_includes": dedent(
"""
#include <glib-object.h>
''').strip(),
'standard_marshal_peek_defines': dedent(
'''
"""
).strip(),
"standard_marshal_peek_defines": dedent(
"""
#ifdef G_ENABLE_DEBUG
#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
#define g_marshal_value_peek_char(v) g_value_get_schar (v)
@ -160,54 +166,53 @@ class TestGenmarshal(unittest.TestCase):
#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer
#endif /* !G_ENABLE_DEBUG */
''').strip(),
"""
).strip(),
}
result = Result(info, out, err, subs)
print('Output:', result.out)
print("Output:", result.out)
return result
def runGenmarshalWithList(self, list_contents, *args):
with tempfile.NamedTemporaryFile(dir=self.tmpdir.name,
suffix='.list',
delete=False) as list_file:
with tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, suffix=".list", delete=False
) as list_file:
# Write out the list.
list_file.write(list_contents.encode('utf-8'))
print(list_file.name + ':', list_contents)
list_file.write(list_contents.encode("utf-8"))
print(list_file.name + ":", list_contents)
list_file.flush()
header_result = self.runGenmarshal(list_file.name,
'--header', *args)
body_result = self.runGenmarshal(list_file.name,
'--body', *args)
header_result = self.runGenmarshal(list_file.name, "--header", *args)
body_result = self.runGenmarshal(list_file.name, "--body", *args)
header_result.subs['list_path'] = list_file.name
body_result.subs['list_path'] = list_file.name
header_result.subs["list_path"] = list_file.name
body_result.subs["list_path"] = list_file.name
return (header_result, body_result)
def test_help(self):
"""Test the --help argument."""
result = self.runGenmarshal('--help')
self.assertIn('usage: glib-genmarshal', result.out)
result = self.runGenmarshal("--help")
self.assertIn("usage: glib-genmarshal", result.out)
def test_no_args(self):
"""Test running with no arguments at all."""
result = self.runGenmarshal()
self.assertEqual('', result.err)
self.assertEqual('', result.out)
self.assertEqual("", result.err)
self.assertEqual("", result.out)
def test_empty_list(self):
"""Test running with an empty list."""
(header_result, body_result) = \
self.runGenmarshalWithList('', '--quiet')
(header_result, body_result) = self.runGenmarshalWithList("", "--quiet")
self.assertEqual('', header_result.err)
self.assertEqual('', body_result.err)
self.assertEqual("", header_result.err)
self.assertEqual("", body_result.err)
self.assertEqual(dedent(
'''
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_top_pragma}
@ -219,28 +224,39 @@ class TestGenmarshal(unittest.TestCase):
G_END_DECLS
{standard_bottom_pragma}
''').strip().format(**header_result.subs),
header_result.out.strip())
"""
)
.strip()
.format(**header_result.subs),
header_result.out.strip(),
)
self.assertEqual(dedent(
'''
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_includes}
{standard_marshal_peek_defines}
''').strip().format(**body_result.subs),
body_result.out.strip())
"""
)
.strip()
.format(**body_result.subs),
body_result.out.strip(),
)
def test_void_boolean(self):
"""Test running with a basic VOID:BOOLEAN list."""
(header_result, body_result) = \
self.runGenmarshalWithList('VOID:BOOLEAN', '--quiet')
(header_result, body_result) = self.runGenmarshalWithList(
"VOID:BOOLEAN", "--quiet"
)
self.assertEqual('', header_result.err)
self.assertEqual('', body_result.err)
self.assertEqual("", header_result.err)
self.assertEqual("", body_result.err)
self.assertEqual(dedent(
'''
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_top_pragma}
@ -255,28 +271,39 @@ class TestGenmarshal(unittest.TestCase):
G_END_DECLS
{standard_bottom_pragma}
''').strip().format(**header_result.subs),
header_result.out.strip())
"""
)
.strip()
.format(**header_result.subs),
header_result.out.strip(),
)
self.assertEqual(dedent(
'''
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_includes}
{standard_marshal_peek_defines}
''').strip().format(**body_result.subs),
body_result.out.strip())
"""
)
.strip()
.format(**body_result.subs),
body_result.out.strip(),
)
def test_void_boolean_int64(self):
"""Test running with a non-trivial VOID:BOOLEAN,INT64 list."""
(header_result, body_result) = \
self.runGenmarshalWithList('VOID:BOOLEAN,INT64', '--quiet')
(header_result, body_result) = self.runGenmarshalWithList(
"VOID:BOOLEAN,INT64", "--quiet"
)
self.assertEqual('', header_result.err)
self.assertEqual('', body_result.err)
self.assertEqual("", header_result.err)
self.assertEqual("", body_result.err)
self.assertEqual(dedent(
'''
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_top_pragma}
@ -297,11 +324,16 @@ class TestGenmarshal(unittest.TestCase):
G_END_DECLS
{standard_bottom_pragma}
''').strip().format(**header_result.subs),
header_result.out.strip())
"""
)
.strip()
.format(**header_result.subs),
header_result.out.strip(),
)
self.assertEqual(dedent(
'''
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_includes}
@ -343,8 +375,12 @@ class TestGenmarshal(unittest.TestCase):
g_marshal_value_peek_int64 (param_values + 2),
data2);
}}
''').strip().format(**body_result.subs),
body_result.out.strip())
"""
)
.strip()
.format(**body_result.subs),
body_result.out.strip(),
)
def test_void_variant_nostdinc_valist_marshaller(self):
"""Test running with a basic VOID:VARIANT list, but without the
@ -353,15 +389,16 @@ class TestGenmarshal(unittest.TestCase):
See issue #1793.
"""
(header_result, body_result) = \
self.runGenmarshalWithList('VOID:VARIANT', '--quiet', '--nostdinc',
'--valist-marshaller')
(header_result, body_result) = self.runGenmarshalWithList(
"VOID:VARIANT", "--quiet", "--nostdinc", "--valist-marshaller"
)
self.assertEqual('', header_result.err)
self.assertEqual('', body_result.err)
self.assertEqual("", header_result.err)
self.assertEqual("", body_result.err)
self.assertEqual(dedent(
'''
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_top_pragma}
@ -388,11 +425,16 @@ class TestGenmarshal(unittest.TestCase):
G_END_DECLS
{standard_bottom_pragma}
''').strip().format(**header_result.subs),
header_result.out.strip())
"""
)
.strip()
.format(**header_result.subs),
header_result.out.strip(),
)
self.assertEqual(dedent(
'''
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_marshal_peek_defines}
@ -474,8 +516,12 @@ class TestGenmarshal(unittest.TestCase):
if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
g_variant_unref (arg0);
}}
''').strip().format(**body_result.subs),
body_result.out.strip())
"""
)
.strip()
.format(**body_result.subs),
body_result.out.strip(),
)
def test_void_string_nostdinc(self):
"""Test running with a basic VOID:STRING list, but without the
@ -485,15 +531,16 @@ class TestGenmarshal(unittest.TestCase):
See issue #1792.
"""
(header_result, body_result) = \
self.runGenmarshalWithList('VOID:STRING', '--quiet', '--nostdinc',
'--valist-marshaller')
(header_result, body_result) = self.runGenmarshalWithList(
"VOID:STRING", "--quiet", "--nostdinc", "--valist-marshaller"
)
self.assertEqual('', header_result.err)
self.assertEqual('', body_result.err)
self.assertEqual("", header_result.err)
self.assertEqual("", body_result.err)
self.assertEqual(dedent(
'''
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_top_pragma}
@ -520,11 +567,16 @@ class TestGenmarshal(unittest.TestCase):
G_END_DECLS
{standard_bottom_pragma}
''').strip().format(**header_result.subs),
header_result.out.strip())
"""
)
.strip()
.format(**header_result.subs),
header_result.out.strip(),
)
self.assertEqual(dedent(
'''
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_marshal_peek_defines}
@ -606,8 +658,12 @@ class TestGenmarshal(unittest.TestCase):
if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
g_free (arg0);
}}
''').strip().format(**body_result.subs),
body_result.out.strip())
"""
)
.strip()
.format(**body_result.subs),
body_result.out.strip(),
)
def test_void_param_nostdinc(self):
"""Test running with a basic VOID:PARAM list, but without the
@ -618,15 +674,16 @@ class TestGenmarshal(unittest.TestCase):
See issue #1792.
"""
self.maxDiff = None # TODO
(header_result, body_result) = \
self.runGenmarshalWithList('VOID:PARAM', '--quiet', '--nostdinc',
'--valist-marshaller')
(header_result, body_result) = self.runGenmarshalWithList(
"VOID:PARAM", "--quiet", "--nostdinc", "--valist-marshaller"
)
self.assertEqual('', header_result.err)
self.assertEqual('', body_result.err)
self.assertEqual("", header_result.err)
self.assertEqual("", body_result.err)
self.assertEqual(dedent(
'''
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_top_pragma}
@ -653,11 +710,16 @@ class TestGenmarshal(unittest.TestCase):
G_END_DECLS
{standard_bottom_pragma}
''').strip().format(**header_result.subs),
header_result.out.strip())
"""
)
.strip()
.format(**header_result.subs),
header_result.out.strip(),
)
self.assertEqual(dedent(
'''
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_marshal_peek_defines}
@ -739,9 +801,13 @@ class TestGenmarshal(unittest.TestCase):
if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
g_param_spec_unref (arg0);
}}
''').strip().format(**body_result.subs),
body_result.out.strip())
"""
)
.strip()
.format(**body_result.subs),
body_result.out.strip(),
)
if __name__ == '__main__':
if __name__ == "__main__":
unittest.main(testRunner=taptestrunner.TAPTestRunner())

View File

@ -32,7 +32,7 @@ import unittest
import taptestrunner
Result = collections.namedtuple('Result', ('info', 'out', 'err', 'subs'))
Result = collections.namedtuple("Result", ("info", "out", "err", "subs"))
class TestMkenums(unittest.TestCase):
@ -47,8 +47,9 @@ class TestMkenums(unittest.TestCase):
parsing and generation code out into a library and unit test that, and
convert this test to just check command line behaviour.
"""
# Track the cwd, we want to back out to that to clean up our tempdir
cwd = ''
cwd = ""
rspfile = False
def setUp(self):
@ -56,14 +57,14 @@ class TestMkenums(unittest.TestCase):
self.tmpdir = tempfile.TemporaryDirectory()
self.cwd = os.getcwd()
os.chdir(self.tmpdir.name)
print('tmpdir:', self.tmpdir.name)
if 'G_TEST_BUILDDIR' in os.environ:
self.__mkenums = \
os.path.join(os.environ['G_TEST_BUILDDIR'], '..',
'glib-mkenums')
print("tmpdir:", self.tmpdir.name)
if "G_TEST_BUILDDIR" in os.environ:
self.__mkenums = os.path.join(
os.environ["G_TEST_BUILDDIR"], "..", "glib-mkenums"
)
else:
self.__mkenums = shutil.which('glib-mkenums')
print('rspfile: {}, mkenums:'.format(self.rspfile), self.__mkenums)
self.__mkenums = shutil.which("glib-mkenums")
print("rspfile: {}, mkenums:".format(self.rspfile), self.__mkenums)
def tearDown(self):
os.chdir(self.cwd)
@ -71,10 +72,12 @@ class TestMkenums(unittest.TestCase):
def _write_rspfile(self, argv):
import shlex
with tempfile.NamedTemporaryFile(dir=self.tmpdir.name, mode='w',
delete=False) as f:
contents = ' '.join([shlex.quote(arg) for arg in argv])
print('Response file contains:', contents)
with tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, mode="w", delete=False
) as f:
contents = " ".join([shlex.quote(arg) for arg in argv])
print("Response file contains:", contents)
f.write(contents)
f.flush()
return f.name
@ -82,60 +85,62 @@ class TestMkenums(unittest.TestCase):
def runMkenums(self, *args):
if self.rspfile:
rspfile = self._write_rspfile(args)
args = ['@' + rspfile]
args = ["@" + rspfile]
argv = [self.__mkenums]
# shebang lines are not supported on native
# Windows consoles
if os.name == 'nt':
if os.name == "nt":
argv.insert(0, sys.executable)
argv.extend(args)
print('Running:', argv)
print("Running:", argv)
env = os.environ.copy()
env['LC_ALL'] = 'C.UTF-8'
print('Environment:', env)
env["LC_ALL"] = "C.UTF-8"
print("Environment:", env)
# We want to ensure consistent line endings...
info = subprocess.run(argv, timeout=self.timeout_seconds,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
universal_newlines=True)
info = subprocess.run(
argv,
timeout=self.timeout_seconds,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
universal_newlines=True,
)
info.check_returncode()
out = info.stdout.strip()
err = info.stderr.strip()
# Known substitutions for standard boilerplate
subs = {
'standard_top_comment':
'This file is generated by glib-mkenums, do not modify '
'it. This code is licensed under the same license as the '
'containing project. Note that it links to GLib, so must '
'comply with the LGPL linking clauses.',
'standard_bottom_comment': 'Generated data ends here'
"standard_top_comment": "This file is generated by glib-mkenums, do not modify "
"it. This code is licensed under the same license as the "
"containing project. Note that it links to GLib, so must "
"comply with the LGPL linking clauses.",
"standard_bottom_comment": "Generated data ends here",
}
result = Result(info, out, err, subs)
print('Output:', result.out)
print("Output:", result.out)
return result
def runMkenumsWithTemplate(self, template_contents, *args):
with tempfile.NamedTemporaryFile(dir=self.tmpdir.name,
suffix='.template',
delete=False) as template_file:
with tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, suffix=".template", delete=False
) as template_file:
# Write out the template.
template_file.write(template_contents.encode('utf-8'))
print(template_file.name + ':', template_contents)
template_file.write(template_contents.encode("utf-8"))
print(template_file.name + ":", template_contents)
template_file.flush()
return self.runMkenums('--template', template_file.name, *args)
return self.runMkenums("--template", template_file.name, *args)
def runMkenumsWithAllSubstitutions(self, *args):
'''Run glib-mkenums with a template which outputs all substitutions.'''
template_contents = '''
"""Run glib-mkenums with a template which outputs all substitutions."""
template_contents = """
/*** BEGIN file-header ***/
file-header
/*** END file-header ***/
@ -203,51 +208,66 @@ comment: @comment@
/*** BEGIN file-tail ***/
file-tail
/*** END file-tail ***/
'''
"""
return self.runMkenumsWithTemplate(template_contents, *args)
def runMkenumsWithHeader(self, h_contents, encoding='utf-8'):
with tempfile.NamedTemporaryFile(dir=self.tmpdir.name,
suffix='.h',
delete=False) as h_file:
def runMkenumsWithHeader(self, h_contents, encoding="utf-8"):
with tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, suffix=".h", delete=False
) as h_file:
# Write out the header to be scanned.
h_file.write(h_contents.encode(encoding))
print(h_file.name + ':', h_contents)
print(h_file.name + ":", h_contents)
h_file.flush()
# Run glib-mkenums with a template which outputs all substitutions.
result = self.runMkenumsWithAllSubstitutions(h_file.name)
# Known substitutions for generated filenames.
result.subs.update({
'filename': h_file.name,
'basename': os.path.basename(h_file.name),
})
result.subs.update(
{"filename": h_file.name, "basename": os.path.basename(h_file.name),}
)
return result
def assertSingleEnum(self, result, enum_name_camel, enum_name_lower,
enum_name_upper, enum_name_short, enum_prefix,
enum_since, type_lower, type_camel, type_upper,
value_name, value_nick, value_num):
def assertSingleEnum(
self,
result,
enum_name_camel,
enum_name_lower,
enum_name_upper,
enum_name_short,
enum_prefix,
enum_since,
type_lower,
type_camel,
type_upper,
value_name,
value_nick,
value_num,
):
"""Assert that out (from runMkenumsWithHeader()) contains a single
enum and value matching the given arguments."""
subs = dict({
'enum_name_camel': enum_name_camel,
'enum_name_lower': enum_name_lower,
'enum_name_upper': enum_name_upper,
'enum_name_short': enum_name_short,
'enum_prefix': enum_prefix,
'enum_since': enum_since,
'type_lower': type_lower,
'type_camel': type_camel,
'type_upper': type_upper,
'value_name': value_name,
'value_nick': value_nick,
'value_num': value_num,
}, **result.subs)
subs = dict(
{
"enum_name_camel": enum_name_camel,
"enum_name_lower": enum_name_lower,
"enum_name_upper": enum_name_upper,
"enum_name_short": enum_name_short,
"enum_prefix": enum_prefix,
"enum_since": enum_since,
"type_lower": type_lower,
"type_camel": type_camel,
"type_upper": type_upper,
"value_name": value_name,
"value_nick": value_nick,
"value_num": value_num,
},
**result.subs
)
self.assertEqual('''
self.assertEqual(
"""
comment
comment: {standard_top_comment}
@ -297,38 +317,51 @@ file-tail
comment
comment: {standard_bottom_comment}
'''.format(**subs).strip(), result.out)
""".format(
**subs
).strip(),
result.out,
)
def test_help(self):
"""Test the --help argument."""
result = self.runMkenums('--help')
self.assertIn('usage: glib-mkenums', result.out)
result = self.runMkenums("--help")
self.assertIn("usage: glib-mkenums", result.out)
def test_no_args(self):
"""Test running with no arguments at all."""
result = self.runMkenums()
self.assertEqual('', result.err)
self.assertEqual('''/* {standard_top_comment} */
self.assertEqual("", result.err)
self.assertEqual(
"""/* {standard_top_comment} */
/* {standard_bottom_comment} */'''.format(**result.subs),
result.out.strip())
/* {standard_bottom_comment} */""".format(
**result.subs
),
result.out.strip(),
)
def test_empty_template(self):
"""Test running with an empty template and no header files."""
result = self.runMkenumsWithTemplate('')
self.assertEqual('', result.err)
self.assertEqual('''/* {standard_top_comment} */
result = self.runMkenumsWithTemplate("")
self.assertEqual("", result.err)
self.assertEqual(
"""/* {standard_top_comment} */
/* {standard_bottom_comment} */'''.format(**result.subs),
result.out.strip())
/* {standard_bottom_comment} */""".format(
**result.subs
),
result.out.strip(),
)
def test_no_headers(self):
"""Test running with a complete template, but no header files."""
result = self.runMkenumsWithAllSubstitutions()
self.assertEqual('', result.err)
self.assertEqual('''
self.assertEqual("", result.err)
self.assertEqual(
"""
comment
comment: {standard_top_comment}
@ -338,13 +371,18 @@ file-tail
comment
comment: {standard_bottom_comment}
'''.format(**result.subs).strip(), result.out)
""".format(
**result.subs
).strip(),
result.out,
)
def test_empty_header(self):
"""Test an empty header."""
result = self.runMkenumsWithHeader('')
self.assertEqual('', result.err)
self.assertEqual('''
result = self.runMkenumsWithHeader("")
self.assertEqual("", result.err)
self.assertEqual(
"""
comment
comment: {standard_top_comment}
@ -354,94 +392,134 @@ file-tail
comment
comment: {standard_bottom_comment}
'''.format(**result.subs).strip(), result.out)
""".format(
**result.subs
).strip(),
result.out,
)
def test_enum_name(self):
"""Test typedefs with an enum and a typedef name. Bug #794506."""
h_contents = '''
h_contents = """
typedef enum _SomeEnumIdentifier {
ENUM_VALUE
} SomeEnumIdentifier;
'''
"""
result = self.runMkenumsWithHeader(h_contents)
self.assertEqual('', result.err)
self.assertSingleEnum(result, 'SomeEnumIdentifier',
'some_enum_identifier', 'SOME_ENUM_IDENTIFIER',
'ENUM_IDENTIFIER', 'SOME', '', 'enum', 'Enum',
'ENUM', 'ENUM_VALUE', 'value', '0')
self.assertEqual("", result.err)
self.assertSingleEnum(
result,
"SomeEnumIdentifier",
"some_enum_identifier",
"SOME_ENUM_IDENTIFIER",
"ENUM_IDENTIFIER",
"SOME",
"",
"enum",
"Enum",
"ENUM",
"ENUM_VALUE",
"value",
"0",
)
def test_non_utf8_encoding(self):
"""Test source files with non-UTF-8 encoding. Bug #785113."""
h_contents = '''
h_contents = """
/* Copyright © La Peña */
typedef enum {
ENUM_VALUE
} SomeEnumIdentifier;
'''
result = self.runMkenumsWithHeader(h_contents, encoding='iso-8859-1')
self.assertIn('WARNING: UnicodeWarning: ', result.err)
self.assertSingleEnum(result, 'SomeEnumIdentifier',
'some_enum_identifier', 'SOME_ENUM_IDENTIFIER',
'ENUM_IDENTIFIER', 'SOME', '', 'enum', 'Enum',
'ENUM', 'ENUM_VALUE', 'value', '0')
"""
result = self.runMkenumsWithHeader(h_contents, encoding="iso-8859-1")
self.assertIn("WARNING: UnicodeWarning: ", result.err)
self.assertSingleEnum(
result,
"SomeEnumIdentifier",
"some_enum_identifier",
"SOME_ENUM_IDENTIFIER",
"ENUM_IDENTIFIER",
"SOME",
"",
"enum",
"Enum",
"ENUM",
"ENUM_VALUE",
"value",
"0",
)
def test_reproducible(self):
"""Test builds are reproducible regardless of file ordering.
Bug #691436."""
template_contents = 'template'
template_contents = "template"
h_contents1 = '''
h_contents1 = """
typedef enum {
FIRST,
} Header1;
'''
"""
h_contents2 = '''
h_contents2 = """
typedef enum {
SECOND,
} Header2;
'''
"""
with tempfile.NamedTemporaryFile(dir=self.tmpdir.name,
suffix='1.h', delete=False) as h_file1, \
tempfile.NamedTemporaryFile(dir=self.tmpdir.name,
suffix='2.h', delete=False) as h_file2:
with tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, suffix="1.h", delete=False
) as h_file1, tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, suffix="2.h", delete=False
) as h_file2:
# Write out the headers.
h_file1.write(h_contents1.encode('utf-8'))
h_file2.write(h_contents2.encode('utf-8'))
h_file1.write(h_contents1.encode("utf-8"))
h_file2.write(h_contents2.encode("utf-8"))
h_file1.flush()
h_file2.flush()
# Run glib-mkenums with the headers in one order, and then again
# in another order.
result1 = self.runMkenumsWithTemplate(template_contents,
h_file1.name, h_file2.name)
self.assertEqual('', result1.err)
result1 = self.runMkenumsWithTemplate(
template_contents, h_file1.name, h_file2.name
)
self.assertEqual("", result1.err)
result2 = self.runMkenumsWithTemplate(template_contents,
h_file2.name, h_file1.name)
self.assertEqual('', result2.err)
result2 = self.runMkenumsWithTemplate(
template_contents, h_file2.name, h_file1.name
)
self.assertEqual("", result2.err)
# The output should be the same.
self.assertEqual(result1.out, result2.out)
def test_no_nick(self):
"""Test trigraphs with a desc but no nick. Issue #1360."""
h_contents = '''
h_contents = """
typedef enum {
GEGL_SAMPLER_NEAREST = 0, /*< desc="nearest" >*/
} GeglSamplerType;
'''
"""
result = self.runMkenumsWithHeader(h_contents)
self.assertEqual('', result.err)
self.assertSingleEnum(result, 'GeglSamplerType',
'gegl_sampler_type', 'GEGL_SAMPLER_TYPE',
'SAMPLER_TYPE', 'GEGL', '', 'enum', 'Enum',
'ENUM', 'GEGL_SAMPLER_NEAREST', 'nearest', '0')
self.assertEqual("", result.err)
self.assertSingleEnum(
result,
"GeglSamplerType",
"gegl_sampler_type",
"GEGL_SAMPLER_TYPE",
"SAMPLER_TYPE",
"GEGL",
"",
"enum",
"Enum",
"ENUM",
"GEGL_SAMPLER_NEAREST",
"nearest",
"0",
)
def test_filename_basename_in_fhead_ftail(self):
template_contents = '''
template_contents = """
/*** BEGIN file-header ***/
file-header
filename: @filename@
@ -457,18 +535,21 @@ comment: @comment@
file-tail
filename: @filename@
basename: @basename@
/*** END file-tail ***/'''
/*** END file-tail ***/"""
result = self.runMkenumsWithTemplate(template_contents)
self.assertEqual(
textwrap.dedent(
'''
"""
WARNING: @filename@ used in file-header section.
WARNING: @basename@ used in file-header section.
WARNING: @filename@ used in file-tail section.
WARNING: @basename@ used in file-tail section.
''').strip(),
result.err)
self.assertEqual('''
"""
).strip(),
result.err,
)
self.assertEqual(
"""
comment
comment: {standard_top_comment}
@ -482,27 +563,44 @@ basename: @basename@
comment
comment: {standard_bottom_comment}
'''.format(**result.subs).strip(), result.out)
""".format(
**result.subs
).strip(),
result.out,
)
def test_since(self):
"""Test user-provided 'since' version handling
https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1492"""
h_contents = '''
h_contents = """
typedef enum { /*< since=1.0 >*/
QMI_WMS_MESSAGE_PROTOCOL_CDMA = 0,
} QmiWmsMessageProtocol;
'''
"""
result = self.runMkenumsWithHeader(h_contents)
self.assertEqual('', result.err)
self.assertSingleEnum(result, 'QmiWmsMessageProtocol',
'qmi_wms_message_protocol', 'QMI_WMS_MESSAGE_PROTOCOL',
'WMS_MESSAGE_PROTOCOL', 'QMI', '1.0', 'enum', 'Enum',
'ENUM', 'QMI_WMS_MESSAGE_PROTOCOL_CDMA', 'cdma', '0')
self.assertEqual("", result.err)
self.assertSingleEnum(
result,
"QmiWmsMessageProtocol",
"qmi_wms_message_protocol",
"QMI_WMS_MESSAGE_PROTOCOL",
"WMS_MESSAGE_PROTOCOL",
"QMI",
"1.0",
"enum",
"Enum",
"ENUM",
"QMI_WMS_MESSAGE_PROTOCOL_CDMA",
"cdma",
"0",
)
class TestRspMkenums(TestMkenums):
'''Run all tests again in @rspfile mode'''
"""Run all tests again in @rspfile mode"""
rspfile = True
if __name__ == '__main__':
if __name__ == "__main__":
unittest.main(testRunner=taptestrunner.TAPTestRunner())

View File

@ -31,146 +31,155 @@ import base64
from io import StringIO
# Log modes
class LogMode(object) :
LogToError, LogToDiagnostics, LogToYAML, LogToAttachment = range(4)
class LogMode(object):
LogToError, LogToDiagnostics, LogToYAML, LogToAttachment = range(4)
class TAPTestResult(unittest.TestResult):
def __init__(self, output_stream, error_stream, message_log, test_output_log):
super(TAPTestResult, self).__init__(self, output_stream)
self.output_stream = output_stream
self.error_stream = error_stream
self.orig_stdout = None
self.orig_stderr = None
self.message = None
self.test_output = None
self.message_log = message_log
self.test_output_log = test_output_log
self.output_stream.write("TAP version 13\n")
self._set_streams()
def __init__(self, output_stream, error_stream, message_log, test_output_log):
super(TAPTestResult, self).__init__(self, output_stream)
self.output_stream = output_stream
self.error_stream = error_stream
self.orig_stdout = None
self.orig_stderr = None
self.message = None
self.test_output = None
self.message_log = message_log
self.test_output_log = test_output_log
self.output_stream.write("TAP version 13\n")
self._set_streams()
def printErrors(self):
self.print_raw("1..%d\n" % self.testsRun)
self._reset_streams()
def printErrors(self):
self.print_raw("1..%d\n" % self.testsRun)
self._reset_streams()
def _set_streams(self):
self.orig_stdout = sys.stdout
self.orig_stderr = sys.stderr
if self.message_log == LogMode.LogToError:
self.message = self.error_stream
else:
self.message = StringIO()
if self.test_output_log == LogMode.LogToError:
self.test_output = self.error_stream
else:
self.test_output = StringIO()
def _set_streams(self):
self.orig_stdout = sys.stdout
self.orig_stderr = sys.stderr
if self.message_log == LogMode.LogToError:
self.message = self.error_stream
else:
self.message = StringIO()
if self.test_output_log == LogMode.LogToError:
self.test_output = self.error_stream
else:
self.test_output = StringIO()
if self.message_log == self.test_output_log:
self.test_output = self.message
sys.stdout = sys.stderr = self.test_output
if self.message_log == self.test_output_log:
self.test_output = self.message
sys.stdout = sys.stderr = self.test_output
def _reset_streams(self):
sys.stdout = self.orig_stdout
sys.stderr = self.orig_stderr
def _reset_streams(self):
sys.stdout = self.orig_stdout
sys.stderr = self.orig_stderr
def print_raw(self, text):
self.output_stream.write(text)
self.output_stream.flush()
def print_raw(self, text):
self.output_stream.write(text)
self.output_stream.flush()
def print_result(self, result, test, directive=None):
self.output_stream.write("%s %d %s" % (result, self.testsRun, test.id()))
if directive:
self.output_stream.write(" # " + directive)
self.output_stream.write("\n")
self.output_stream.flush()
def print_result(self, result, test, directive = None):
self.output_stream.write("%s %d %s" % (result, self.testsRun, test.id()))
if directive:
self.output_stream.write(" # " + directive)
self.output_stream.write("\n")
self.output_stream.flush()
def ok(self, test, directive=None):
self.print_result("ok", test, directive)
def ok(self, test, directive = None):
self.print_result("ok", test, directive)
def not_ok(self, test):
self.print_result("not ok", test)
def not_ok(self, test):
self.print_result("not ok", test)
def startTest(self, test):
super(TAPTestResult, self).startTest(test)
def startTest(self, test):
super(TAPTestResult, self).startTest(test)
def stopTest(self, test):
super(TAPTestResult, self).stopTest(test)
if self.message_log == self.test_output_log:
logs = [(self.message_log, self.message, "output")]
else:
logs = [
(self.test_output_log, self.test_output, "test_output"),
(self.message_log, self.message, "message"),
]
for log_mode, log, log_name in logs:
if log_mode != LogMode.LogToError:
output = log.getvalue()
if len(output):
if log_mode == LogMode.LogToYAML:
self.print_raw(" ---\n")
self.print_raw(" " + log_name + ": |\n")
self.print_raw(
" " + output.rstrip().replace("\n", "\n ") + "\n"
)
self.print_raw(" ...\n")
elif log_mode == LogMode.LogToAttachment:
self.print_raw(" ---\n")
self.print_raw(" " + log_name + ":\n")
self.print_raw(" File-Name: " + log_name + ".txt\n")
self.print_raw(" File-Type: text/plain\n")
self.print_raw(
" File-Content: " + base64.b64encode(output) + "\n"
)
self.print_raw(" ...\n")
else:
self.print_raw(
"# " + output.rstrip().replace("\n", "\n# ") + "\n"
)
# Truncate doesn't change the current stream position.
# Seek to the beginning to avoid extensions on subsequent writes.
log.seek(0)
log.truncate(0)
def stopTest(self, test):
super(TAPTestResult, self).stopTest(test)
if self.message_log == self.test_output_log:
logs = [(self.message_log, self.message, "output")]
else:
logs = [
(self.test_output_log, self.test_output, "test_output"),
(self.message_log, self.message, "message")
]
for log_mode, log, log_name in logs:
if log_mode != LogMode.LogToError:
output = log.getvalue()
if len(output):
if log_mode == LogMode.LogToYAML:
self.print_raw(" ---\n")
self.print_raw(" " + log_name + ": |\n")
self.print_raw(" " + output.rstrip().replace("\n", "\n ") + "\n")
self.print_raw(" ...\n")
elif log_mode == LogMode.LogToAttachment:
self.print_raw(" ---\n")
self.print_raw(" " + log_name + ":\n")
self.print_raw(" File-Name: " + log_name + ".txt\n")
self.print_raw(" File-Type: text/plain\n")
self.print_raw(" File-Content: " + base64.b64encode(output) + "\n")
self.print_raw(" ...\n")
else:
self.print_raw("# " + output.rstrip().replace("\n", "\n# ") + "\n")
# Truncate doesn't change the current stream position.
# Seek to the beginning to avoid extensions on subsequent writes.
log.seek(0)
log.truncate(0)
def addSuccess(self, test):
super(TAPTestResult, self).addSuccess(test)
self.ok(test)
def addSuccess(self, test):
super(TAPTestResult, self).addSuccess(test)
self.ok(test)
def addError(self, test, err):
super(TAPTestResult, self).addError(test, err)
self.message.write(self.errors[-1][1] + "\n")
self.not_ok(test)
def addError(self, test, err):
super(TAPTestResult, self).addError(test, err)
self.message.write(self.errors[-1][1] + "\n")
self.not_ok(test)
def addFailure(self, test, err):
super(TAPTestResult, self).addFailure(test, err)
self.message.write(self.failures[-1][1] + "\n")
self.not_ok(test)
def addFailure(self, test, err):
super(TAPTestResult, self).addFailure(test, err)
self.message.write(self.failures[-1][1] + "\n")
self.not_ok(test)
def addSkip(self, test, reason):
super(TAPTestResult, self).addSkip(test, reason)
self.ok(test, "SKIP " + reason)
def addSkip(self, test, reason):
super(TAPTestResult, self).addSkip(test, reason)
self.ok(test, "SKIP " + reason)
def addExpectedFailure(self, test, err):
super(TAPTestResult, self).addExpectedFailure(test, err)
self.ok(test)
def addExpectedFailure(self, test, err):
super(TAPTestResult, self).addExpectedFailure(test, err)
self.ok(test)
def addUnexpectedSuccess(self, test):
super(TAPTestResult, self).addUnexpectedSuccess(test)
self.message.write("Unexpected success" + "\n")
self.not_ok(test)
def addUnexpectedSuccess(self, test):
super(TAPTestResult, self).addUnexpectedSuccess(test)
self.message.write("Unexpected success" + "\n")
self.not_ok(test)
class TAPTestRunner(object):
def __init__(self,
message_log = LogMode.LogToYAML,
test_output_log = LogMode.LogToDiagnostics,
output_stream = sys.stdout, error_stream = sys.stderr):
self.output_stream = output_stream
self.error_stream = error_stream
self.message_log = message_log
self.test_output_log = test_output_log
def __init__(
self,
message_log=LogMode.LogToYAML,
test_output_log=LogMode.LogToDiagnostics,
output_stream=sys.stdout,
error_stream=sys.stderr,
):
self.output_stream = output_stream
self.error_stream = error_stream
self.message_log = message_log
self.test_output_log = test_output_log
def run(self, test):
result = TAPTestResult(
self.output_stream,
self.error_stream,
self.message_log,
self.test_output_log)
test(result)
result.printErrors()
def run(self, test):
result = TAPTestResult(
self.output_stream,
self.error_stream,
self.message_log,
self.test_output_log,
)
test(result)
result.printErrors()
return result
return result

View File

@ -29,14 +29,16 @@ import argparse
def main(argv):
parser = argparse.ArgumentParser(
description="Generate test cases for casefolding from Unicode data")
description="Generate test cases for casefolding from Unicode data"
)
parser.add_argument("UNICODE-VERSION")
parser.add_argument("CaseFolding.txt")
args = parser.parse_args(argv[1:])
version = getattr(args, "UNICODE-VERSION")
filename = getattr(args, "CaseFolding.txt")
print("""\
print(
"""\
# Test cases generated from Unicode {} data
# by gen-casefold-txt.py. Do not edit.
#
@ -45,7 +47,10 @@ def main(argv):
AaBbCc@@\taabbcc@@
#
# Now the automatic tests
#""".format(version))
#""".format(
version
)
)
# Names of fields in the CaseFolding table
CODE, STATUS, MAPPING = range(3)
@ -60,8 +65,9 @@ AaBbCc@@\taabbcc@@
fields = [f.strip() for f in line.split(";", 3)[:3]]
if len(fields) != 3:
raise SystemExit(
"Entry for %s has wrong number of fields (%d)" % (
fields[CODE], len(fields)))
"Entry for %s has wrong number of fields (%d)"
% (fields[CODE], len(fields))
)
status = fields[STATUS]
# skip simple and Turkic mappings
@ -69,8 +75,7 @@ AaBbCc@@\taabbcc@@
continue
code = chr(int(fields[CODE], 16))
values = "".join(
[chr(int(v, 16)) for v in fields[MAPPING].split()])
values = "".join([chr(int(v, 16)) for v in fields[MAPPING].split()])
print("{}\t{}".format(code, values))

View File

@ -29,7 +29,8 @@ import argparse
def main(argv):
parser = argparse.ArgumentParser(
description="Generate test cases for case mapping from Unicode data")
description="Generate test cases for case mapping from Unicode data"
)
parser.add_argument("UNICODE-VERSION")
parser.add_argument("UnicodeData.txt")
parser.add_argument("SpecialCasing.txt")
@ -39,9 +40,23 @@ def main(argv):
filename_casing = getattr(args, "SpecialCasing.txt")
# Names of fields in Unicode data table.
CODE, NAME, CATEGORY, COMBINING_CLASSES, BIDI_CATEGORY, DECOMPOSITION, \
DECIMAL_VALUE, DIGIT_VALUE, NUMERIC_VALUE, MIRRORED, OLD_NAME, \
COMMENT, UPPER, LOWER, TITLE = range(15)
(
CODE,
NAME,
CATEGORY,
COMBINING_CLASSES,
BIDI_CATEGORY,
DECOMPOSITION,
DECIMAL_VALUE,
DIGIT_VALUE,
NUMERIC_VALUE,
MIRRORED,
OLD_NAME,
COMMENT,
UPPER,
LOWER,
TITLE,
) = range(15)
# Names of fields in the SpecialCasing table
CASE_CODE, CASE_LOWER, CASE_TITLE, CASE_UPPER, CASE_CONDITION = range(5)
@ -78,8 +93,9 @@ def main(argv):
fields = [f.strip() for f in line.split(";")]
if len(fields) != 15:
raise SystemExit(
"Entry for %s has wrong number of fields (%d)" % (
fields[CODE], len(fields)))
"Entry for %s has wrong number of fields (%d)"
% (fields[CODE], len(fields))
)
code = int(fields[CODE], 16)
@ -92,8 +108,23 @@ def main(argv):
else:
# The gap represents undefined characters. Only the type
# matters.
gfields = ['', '', 'Cn', '0', '', '', '', '', '', '', '',
'', '', '', '']
gfields = [
"",
"",
"Cn",
"0",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
]
last_code += 1
while last_code < code:
@ -117,8 +148,9 @@ def main(argv):
fields = [f.strip() for f in line.split(";")]
if len(fields) not in (4, 5):
raise SystemExit(
"Entry for %s has wrong number of fields (%d)" % (
fields[CASE_CODE], len(fields)))
"Entry for %s has wrong number of fields (%d)"
% (fields[CASE_CODE], len(fields))
)
if len(fields) == 5:
# Ignore conditional special cases - we'll handle them manually
@ -134,7 +166,8 @@ def main(argv):
def print_tests(version, upper, title, lower):
print("""\
print(
"""\
# Test cases generated from Unicode {} data
# by gen-casemap-txt.py. Do not edit.
#
@ -181,9 +214,12 @@ lt_LT.UTF-8\t\u012e\u0301\t\u012f\u0307\u0301\t\u012e\u0301\t\u012e\u0301\t # LA
\ta\ufb04\ta\ufb04\tAffl\tAFFL\t# FB04
#
# Now the automatic tests
#""".format(version))
#""".format(
version
)
)
for i in range(0x10ffff):
for i in range(0x10FFFF):
if i == 0x3A3:
# Greek sigma needs special tests
continue