Merge branch 'py-fixes' into 'master'

Python formatting improvements

See merge request GNOME/glib!1757
This commit is contained in:
Sebastian Dröge 2020-11-20 18:10:40 +00:00
commit fa8a39c6c6
27 changed files with 6995 additions and 4918 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(r"(^|\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(r"(^|\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

@ -11,105 +11,115 @@
import argparse
import datetime
import json
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

@ -2,5 +2,8 @@
set -e
# Disable formatting warnings in flake8, as we use `black` to handle that.
formatting_warnings=E101,E111,E114,E115,E116,E117,E12,E13,E2,E3,E401,E5,E70,W1,W2,W3,W5
# shellcheck disable=SC2046
flake8 --max-line-length=88 $(git ls-files '*.py')
flake8 --max-line-length=88 --ignore="$formatting_warnings" $(git ls-files '*.py')

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

@ -18,13 +18,18 @@
#
# Author: Xavier Claessens <xavier.claessens@collabora.com>
import os
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

@ -19,17 +19,19 @@
#
# Author: David Zeuthen <davidz@redhat.com>
import sys
import re
from os import path
from . import config
from . import utils
from . import dbustypes
from . import parser
# Disable line length warnings as wrapping the Docbook templates would be hard
# flake8: noqa: E501
# ----------------------------------------------------------------------------------------------------
class DocbookCodeGenerator:
def __init__(self, ifaces):
self.ifaces = ifaces
@ -57,23 +59,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 +108,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 +147,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 +330,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 +353,73 @@ 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 +428,49 @@ 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,294 @@ 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
# 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 +416,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)
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,34 +404,44 @@ 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('_', '-')
# 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_'
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_"
# 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:
a.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
# FIXME: for now we only support 'false' and 'const' on the signal itself, see #674913 and
# 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 +449,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 +464,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 +488,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

@ -19,22 +19,22 @@
#
# Author: David Zeuthen <davidz@redhat.com>
import sys
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 +51,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 +74,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 +142,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 +219,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 doc_string != None:
if "name" in attrs and attrs["name"] in self.doc_comment_params:
doc_string = self.doc_comment_params[attrs["name"]]
if doc_string is not 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 +247,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 doc_string != None:
if "name" in attrs and attrs["name"] in self.doc_comment_params:
doc_string = self.doc_comment_params[attrs["name"]]
if doc_string is not 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 +268,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 +277,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 +297,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

@ -23,49 +23,58 @@ import distutils.version
import os
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 +84,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 +108,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 +127,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

@ -26,13 +26,16 @@ import shutil
import subprocess
import sys
import tempfile
import textwrap
import unittest
import taptestrunner
Result = collections.namedtuple('Result', ('info', 'out', 'err', 'subs'))
# Disable line length warnings as wrapping the C code templates would be hard
# flake8: noqa: E501
Result = collections.namedtuple("Result", ("info", "out", "err", "subs"))
class TestCodegen(unittest.TestCase):
@ -47,22 +50,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 +81,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 +283,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 +298,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 +317,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 +335,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 +385,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 +504,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

@ -8,14 +8,13 @@
#
# See issue #1580
import io
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 func not 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 "GLIB_TEST_COMPILATION" not 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

@ -30,147 +30,157 @@ import sys
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

@ -4,48 +4,57 @@ import sys
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"))
except:
val = read_global_var("quarks")
max_q = long(read_global_var("quark_seq_id"))
except Exception:
try:
val = read_global_var ("g_quarks")
max_q = long(read_global_var ("g_quark_seq_id"))
except:
return None;
val = read_global_var("g_quarks")
max_q = long(read_global_var("g_quark_seq_id"))
except Exception:
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 +70,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 +100,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())
@ -119,23 +131,23 @@ class GHashPrinter:
def next(self):
if self.ht == 0:
raise StopIteration
if self.value != None:
if self.value is not None:
v = self.value
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 +156,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")
except:
string_hash = read_global_var("g_str_hash")
except Exception:
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 is not 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 +206,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 +261,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())
while long(l) != 0:
self.do_iter (arg, l["data"], command)
l = l["next"]
def slist_iterator(self, arg, container, command):
list_element = container.cast(gdb.lookup_type("GSList").pointer())
while long(list_element) != 0:
self.do_iter(arg, list_element["data"], command)
list_element = list_element["next"]
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)
l = l["next"]
def list_iterator(self, arg, container, command):
list_element = container.cast(gdb.lookup_type("GList").pointer())
while long(list_element) != 0:
self.do_iter(arg, list_element["data"], command)
list_element = list_element["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 +288,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

@ -4,21 +4,25 @@
#
# ./update-gtranslit.py /path/to/glibc/localedata/locales > gtranslit-data.h
import sys, os
import os
import sys
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 +31,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:
@ -62,31 +68,32 @@ class Mapping:
# Scans a string like
#
# <U00C4> "<U0041><U0308>";"<U0041><U0045>" % LATIN CAPITAL LETTER A WITH DIAERESIS.
# <U00C4> "<U0041><U0308>";"<U0041><U0045>" % \
# LATIN CAPITAL LETTER A WITH DIAERESIS.
#
# and adds the first all-ascii choice (or IGNORE) to the 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 +116,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 +143,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 +163,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 +207,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,10 +225,13 @@ class Chain:
return self.serialised
# Chain cache -- allows sharing of common chains
chains = {}
def get_chain(name):
if not name in chains:
if name not in chains:
chains[name] = Chain(name)
return chains[name]
@ -227,10 +240,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 +258,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 +300,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 +312,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 +323,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 +344,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,23 +364,21 @@ class Serialiser:
else:
ascii_range = encode_range(existing, existing + len(value))
mappings_table.append ((src_range, ascii_range))
mapping_end = len(mappings_table)
mappings_table.append((src_range, ascii_range))
for chain in self.chains:
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 +386,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 +447,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

@ -1,4 +1,3 @@
import os.path
import gdb
import glib_gdb
import sys
@ -7,25 +6,29 @@ 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"])
if typenode is not None:
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"])):
if typenode is not 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"])):
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)
if array is not None:
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 array != None:
array.append (v)
if v is not None and g_type_name_from_instance(v):
if array is not None:
array.append(v)
return v
return None
except ValueError:
return None
def append (self, array, obj):
if obj != None:
array.append (obj)
def append(self, array, obj):
if obj is not None:
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,11 @@ import unittest
import taptestrunner
Result = collections.namedtuple('Result', ('info', 'out', 'err', 'subs'))
# Disable line length warnings as wrapping the C code templates would be hard
# flake8: noqa: E501
Result = collections.namedtuple("Result", ("info", "out", "err", "subs"))
class TestGenmarshal(unittest.TestCase):
@ -47,22 +51,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 +78,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 +170,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 +228,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 +275,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 +328,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 +379,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 +393,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 +429,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 +520,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 +535,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 +571,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 +662,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 +678,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 +714,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 +805,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

@ -30,147 +30,157 @@ import sys
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

@ -27,9 +27,14 @@ import sys
import argparse
# Disable line length warnings as wrapping the test templates would be hard
# flake8: noqa: E501
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 +44,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 +97,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 +112,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 +152,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 +170,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.
#
@ -150,9 +187,9 @@ tr_TR.UTF-8\tI\u0307\ti\tI\u0307\tI\u0307\t# I => LATIN SMALL LETTER DOTLESS I
\t\u03b1\u0345\u0314\t\u03b1\u0345\u0314\t\u0391\u0345\u0314\t\u0391\u0314\u0399\t
\t\u03b1\u0314\u0345\t\u03b1\u0314\u0345\t\u0391\u0314\u0345\t\u0391\u0314\u0399\t
# Handling of final and nonfinal sigma
\tΜΆΙΟΣ μάιος Μάιος ΜΆΙΟΣ
\tΜΆΙΟΣ μάιος Μάιος ΜΆΙΟΣ
\ΙΓΜΑ σιγμα Σιγμα ΣΙΓΜΑ
\tΜΆΙΟΣ μάιος Μάιος ΜΆΙΟΣ \t
\tΜΆΙΟΣ μάιος Μάιος ΜΆΙΟΣ\t
\ΙΓΜΑ σιγμα Σιγμα ΣΙΓΜΑ\t
# Lithuanian rule of i followed by letter with dot. Not at all sure
# about the titlecase part here
lt_LT\ti\u0117\ti\u0117\tIe\tIE\t
@ -181,9 +218,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