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

View File

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

View File

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

View File

@ -18,13 +18,18 @@
# #
# Author: Xavier Claessens <xavier.claessens@collabora.com> # Author: Xavier Claessens <xavier.claessens@collabora.com>
import os
import sys import sys
if len(sys.argv) < 3: if len(sys.argv) < 3:
print('Usage: {} <output file> <input file 1> ...'.format(os.path.basename(sys.argv[0]))) print(
"Usage: {} <output file> <input file 1> ...".format(
os.path.basename(sys.argv[0])
)
)
sys.exit(1) 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:]: for fname in sys.argv[2:]:
with open(fname) as infile: with open(fname) as infile:
for line in infile: for line in infile:

View File

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

View File

@ -21,7 +21,9 @@
import os import os
builddir = os.environ.get('UNINSTALLED_GLIB_BUILDDIR') builddir = os.environ.get("UNINSTALLED_GLIB_BUILDDIR")
if builddir is not None: 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> # Author: David Zeuthen <davidz@redhat.com>
import sys
import re import re
from os import path from os import path
from . import config
from . import utils 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: class DocbookCodeGenerator:
def __init__(self, ifaces): def __init__(self, ifaces):
self.ifaces = ifaces self.ifaces = ifaces
@ -57,23 +59,36 @@ class DocbookCodeGenerator:
max_signature_len = max(len(a.signature), max_signature_len) max_signature_len = max(len(a.signature), max_signature_len)
if in_synopsis: if in_synopsis:
self.out.write('<link linkend="gdbus-method-%s.%s">%s</link>%*s (' self.out.write(
%(utils.dots_to_hyphens(i.name), m.name, m.name, max_method_len - len(m.name), '')) '<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: else:
self.out.write('%s%*s (' self.out.write("%s%*s (" % (m.name, max_method_len - len(m.name), ""))
%(m.name, max_method_len - len(m.name), ''))
count = 0 count = 0
for a in m.in_args: for a in m.in_args:
if (count > 0): if count > 0:
self.out.write(',\n%*s'%(max_method_len + 2, '')) 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)) self.out.write(
"IN %s%*s %s"
% (a.signature, max_signature_len - len(a.signature), "", a.name)
)
count = count + 1 count = count + 1
for a in m.out_args: for a in m.out_args:
if (count > 0): if count > 0:
self.out.write(',\n%*s'%(max_method_len + 2, '')) 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)) self.out.write(
"OUT %s%*s %s"
% (a.signature, max_signature_len - len(a.signature), "", a.name)
)
count = count + 1 count = count + 1
self.out.write(');\n') self.out.write(");\n")
def print_signal_prototype(self, i, s, in_synopsis): def print_signal_prototype(self, i, s, in_synopsis):
max_signal_len = 0 max_signal_len = 0
@ -93,18 +108,28 @@ class DocbookCodeGenerator:
max_signature_len = max(len(a.signature), max_signature_len) max_signature_len = max(len(a.signature), max_signature_len)
if in_synopsis: if in_synopsis:
self.out.write('<link linkend="gdbus-signal-%s.%s">%s</link>%*s (' self.out.write(
%(utils.dots_to_hyphens(i.name), s.name, s.name, max_signal_len - len(s.name), '')) '<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: else:
self.out.write('%s%*s (' self.out.write("%s%*s (" % (s.name, max_signal_len - len(s.name), ""))
%(s.name, max_signal_len - len(s.name), ''))
count = 0 count = 0
for a in s.args: for a in s.args:
if (count > 0): if count > 0:
self.out.write(',\n%*s'%(max_signal_len + 2, '')) 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)) self.out.write(
"%s%*s %s"
% (a.signature, max_signature_len - len(a.signature), "", a.name)
)
count = count + 1 count = count + 1
self.out.write(');\n') self.out.write(");\n")
def print_property_prototype(self, i, p, in_synopsis): def print_property_prototype(self, i, p, in_synopsis):
max_property_len = 0 max_property_len = 0
@ -122,109 +147,181 @@ class DocbookCodeGenerator:
max_signature_len = max(len(p.signature), max_signature_len) max_signature_len = max(len(p.signature), max_signature_len)
if in_synopsis: if in_synopsis:
self.out.write('<link linkend="gdbus-property-%s.%s">%s</link>%*s' self.out.write(
%(utils.dots_to_hyphens(i.name), p.name, p.name, max_property_len - len(p.name), '')) '<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: else:
self.out.write('%s%*s' self.out.write("%s%*s" % (p.name, max_property_len - len(p.name), ""))
%(p.name, max_property_len - len(p.name), ''))
if p.readable and p.writable: if p.readable and p.writable:
access = 'readwrite' access = "readwrite"
elif p.readable: elif p.readable:
access = 'readable ' access = "readable "
else: else:
access = 'writable ' access = "writable "
self.out.write(' %s %s\n'%(access, p.signature)) self.out.write(" %s %s\n" % (access, p.signature))
def print_synopsis_methods(self, i): def print_synopsis_methods(self, i):
self.out.write(' <refsynopsisdiv role="synopsis">\n'%()) self.out.write(' <refsynopsisdiv role="synopsis">\n')
self.out.write(' <title role="synopsis.title">Methods</title>\n'%()) self.out.write(' <title role="synopsis.title">Methods</title>\n')
self.out.write(' <synopsis>\n'%()) self.out.write(" <synopsis>\n")
for m in i.methods: for m in i.methods:
self.print_method_prototype(i, m, in_synopsis=True) self.print_method_prototype(i, m, in_synopsis=True)
self.out.write('</synopsis>\n'%()) self.out.write("</synopsis>\n")
self.out.write(' </refsynopsisdiv>\n'%()) self.out.write(" </refsynopsisdiv>\n")
def print_synopsis_signals(self, i): def print_synopsis_signals(self, i):
self.out.write(' <refsect1 role="signal_proto">\n'%()) self.out.write(' <refsect1 role="signal_proto">\n')
self.out.write(' <title role="signal_proto.title">Signals</title>\n'%()) self.out.write(' <title role="signal_proto.title">Signals</title>\n')
self.out.write(' <synopsis>\n'%()) self.out.write(" <synopsis>\n")
for s in i.signals: for s in i.signals:
self.print_signal_prototype(i, s, in_synopsis=True) self.print_signal_prototype(i, s, in_synopsis=True)
self.out.write('</synopsis>\n'%()) self.out.write("</synopsis>\n")
self.out.write(' </refsect1>\n'%()) self.out.write(" </refsect1>\n")
def print_synopsis_properties(self, i): def print_synopsis_properties(self, i):
self.out.write(' <refsect1 role="properties">\n'%()) self.out.write(' <refsect1 role="properties">\n')
self.out.write(' <title role="properties.title">Properties</title>\n'%()) self.out.write(' <title role="properties.title">Properties</title>\n')
self.out.write(' <synopsis>\n'%()) self.out.write(" <synopsis>\n")
for p in i.properties: for p in i.properties:
self.print_property_prototype(i, p, in_synopsis=True) self.print_property_prototype(i, p, in_synopsis=True)
self.out.write('</synopsis>\n'%()) self.out.write("</synopsis>\n")
self.out.write(' </refsect1>\n'%()) self.out.write(" </refsect1>\n")
def print_method(self, i, m): 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(
self.out.write(' <title>The %s() method</title>\n'%(m.name)) '<refsect2 role="method" id="gdbus-method-%s.%s">\n'
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)) % (utils.dots_to_hyphens(i.name), m.name)
self.out.write('<programlisting>\n') )
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.print_method_prototype(i, m, in_synopsis=False)
self.out.write('</programlisting>\n') self.out.write("</programlisting>\n")
self.out.write('%s\n'%(self.expand_paras(m.doc_string, True))) self.out.write("%s\n" % (self.expand_paras(m.doc_string, True)))
if m.in_args or m.out_args: if m.in_args or m.out_args:
self.out.write('<variablelist role="params">\n') self.out.write('<variablelist role="params">\n')
for a in m.in_args: for a in m.in_args:
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(
self.out.write(' <listitem>%s</listitem>\n'%(self.expand_paras(a.doc_string, True))) " <term><literal>IN %s <parameter>%s</parameter></literal>:</term>\n"
self.out.write('</varlistentry>\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: for a in m.out_args:
self.out.write('<varlistentry>\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(
self.out.write(' <listitem>%s</listitem>\n'%(self.expand_paras(a.doc_string, True))) " <term><literal>OUT %s <parameter>%s</parameter></literal>:</term>\n"
self.out.write('</varlistentry>\n'%()) % (a.signature, a.name)
self.out.write('</variablelist>\n') )
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: 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: if m.deprecated:
self.out.write('<warning><para>The %s() method is deprecated.</para></warning>'%(m.name)) self.out.write(
self.out.write('</refsect2>\n') "<warning><para>The %s() method is deprecated.</para></warning>"
% (m.name)
)
self.out.write("</refsect2>\n")
def print_signal(self, i, s): 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(
'<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(' <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(
self.out.write('<programlisting>\n') ' <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.print_signal_prototype(i, s, in_synopsis=False)
self.out.write('</programlisting>\n') self.out.write("</programlisting>\n")
self.out.write('%s\n'%(self.expand_paras(s.doc_string, True))) self.out.write("%s\n" % (self.expand_paras(s.doc_string, True)))
if s.args: if s.args:
self.out.write('<variablelist role="params">\n') self.out.write('<variablelist role="params">\n')
for a in s.args: for a in s.args:
self.out.write('<varlistentry>\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(
self.out.write(' <listitem>%s</listitem>\n'%(self.expand_paras(a.doc_string, True))) " <term><literal>%s <parameter>%s</parameter></literal>:</term>\n"
self.out.write('</varlistentry>\n'%()) % (a.signature, a.name)
self.out.write('</variablelist>\n') )
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: 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: if s.deprecated:
self.out.write('<warning><para>The "%s" signal is deprecated.</para></warning>'%(s.name)) self.out.write(
self.out.write('</refsect2>\n') '<warning><para>The "%s" signal is deprecated.</para></warning>'
% (s.name)
)
self.out.write("</refsect2>\n")
def print_property(self, i, p): 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(
'<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(' <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(
self.out.write('<programlisting>\n') ' <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.print_property_prototype(i, p, in_synopsis=False)
self.out.write('</programlisting>\n') self.out.write("</programlisting>\n")
self.out.write('%s\n'%(self.expand_paras(p.doc_string, True))) self.out.write("%s\n" % (self.expand_paras(p.doc_string, True)))
if len(p.since) > 0: 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: if p.deprecated:
self.out.write('<warning><para>The "%s" property is deprecated.</para></warning>'%(p.name)) self.out.write(
self.out.write('</refsect2>\n') '<warning><para>The "%s" property is deprecated.</para></warning>'
% (p.name)
)
self.out.write("</refsect2>\n")
def expand(self, s, expandParamsAndConstants): def expand(self, s, expandParamsAndConstants):
for key in self.expand_member_dict_keys: for key in self.expand_member_dict_keys:
@ -233,9 +330,17 @@ class DocbookCodeGenerator:
s = s.replace(key, self.expand_iface_dict[key]) s = s.replace(key, self.expand_iface_dict[key])
if expandParamsAndConstants: if expandParamsAndConstants:
# replace @foo with <parameter>foo</parameter> # 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> # 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 return s
def expand_paras(self, s, expandParamsAndConstants): def expand_paras(self, s, expandParamsAndConstants):
@ -248,44 +353,73 @@ class DocbookCodeGenerator:
self.expand_member_dict = {} self.expand_member_dict = {}
self.expand_iface_dict = {} self.expand_iface_dict = {}
for i in self.ifaces: for i in self.ifaces:
key = '#%s'%(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) 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 self.expand_iface_dict[key] = value
for m in i.methods: for m in i.methods:
key = '%s.%s()'%(i.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) 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 self.expand_member_dict[key] = value
for s in i.signals: for s in i.signals:
key = '#%s::%s'%(i.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) 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 self.expand_member_dict[key] = value
for p in i.properties: for p in i.properties:
key = '#%s:%s'%(i.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) 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 self.expand_member_dict[key] = value
# Make sure to expand the keys in reverse order so e.g. #org.foo.Iface:MediaCompat # Make sure to expand the keys in reverse order so e.g. #org.foo.Iface:MediaCompat
# is evaluated before #org.foo.Iface:Media ... # is evaluated before #org.foo.Iface:Media ...
self.expand_member_dict_keys = sorted(self.expand_member_dict.keys(), reverse=True) self.expand_member_dict_keys = sorted(
self.expand_iface_dict_keys = sorted(self.expand_iface_dict.keys(), reverse=True) 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): def generate(self, docbook, outdir):
for i in self.ifaces: for i in self.ifaces:
self.out = open(path.join(outdir, '%s-%s.xml'%(docbook, i.name)), 'w') self.out = open(path.join(outdir, "%s-%s.xml" % (docbook, i.name)), "w")
self.out.write(''%()) self.out.write("")
self.out.write('<?xml version="1.0" encoding="utf-8"?>\n'%()) 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(
self.out.write(' "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [\n'%()) '<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"\n'
self.out.write(']>\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('<refentry id="gdbus-%s">\n' % (i.name))
self.out.write(' <refmeta>'%()) 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(
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)) ' <refentrytitle role="top_of_page" id="gdbus-interface-%s.top_of_page">%s</refentrytitle>\n'
self.out.write(' </refmeta>'%()) % (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(" <refnamediv>")
self.out.write(' <refname>%s</refname>'%(i.name)) self.out.write(" <refname>%s</refname>" % (i.name))
self.out.write(' <refpurpose>%s</refpurpose>'%(i.doc_string_brief)) self.out.write(" <refpurpose>%s</refpurpose>" % (i.doc_string_brief))
self.out.write(' </refnamediv>'%()) self.out.write(" </refnamediv>")
if len(i.methods) > 0: if len(i.methods) > 0:
self.print_synopsis_methods(i) self.print_synopsis_methods(i)
@ -294,36 +428,49 @@ class DocbookCodeGenerator:
if len(i.properties) > 0: if len(i.properties) > 0:
self.print_synopsis_properties(i) 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(
self.out.write(' <title role="desc.title">Description</title>\n'%()) '<refsect1 role="desc" id="gdbus-interface-%s">\n'
self.out.write(' %s\n'%(self.expand_paras(i.doc_string, True))) % (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: 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: if i.deprecated:
self.out.write('<warning><para>The %s interface is deprecated.</para></warning>'%(i.name)) self.out.write(
self.out.write('</refsect1>\n'%()) "<warning><para>The %s interface is deprecated.</para></warning>"
% (i.name)
)
self.out.write("</refsect1>\n")
if len(i.methods) > 0: if len(i.methods) > 0:
self.out.write('<refsect1 role="details" id="gdbus-methods-%s">\n'%(i.name)) self.out.write(
self.out.write(' <title role="details.title">Method Details</title>\n'%()) '<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: for m in i.methods:
self.print_method(i, m) self.print_method(i, m)
self.out.write('</refsect1>\n'%()) self.out.write("</refsect1>\n")
if len(i.signals) > 0: if len(i.signals) > 0:
self.out.write('<refsect1 role="details" id="gdbus-signals-%s">\n'%(i.name)) self.out.write(
self.out.write(' <title role="details.title">Signal Details</title>\n'%()) '<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: for s in i.signals:
self.print_signal(i, s) self.print_signal(i, s)
self.out.write('</refsect1>\n'%()) self.out.write("</refsect1>\n")
if len(i.properties) > 0: if len(i.properties) > 0:
self.out.write('<refsect1 role="details" id="gdbus-properties-%s">\n'%(i.name)) self.out.write(
self.out.write(' <title role="details.title">Property Details</title>\n'%()) '<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: for s in i.properties:
self.print_property(i, s) self.print_property(i, s)
self.out.write('</refsect1>\n'%()) self.out.write("</refsect1>\n")
self.out.write('</refentry>\n')
self.out.write('\n')
self.out.write("</refentry>\n")
self.out.write("\n")

View File

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

View File

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

View File

@ -19,22 +19,22 @@
# #
# Author: David Zeuthen <davidz@redhat.com> # Author: David Zeuthen <davidz@redhat.com>
import sys
import xml.parsers.expat import xml.parsers.expat
from . import dbustypes from . import dbustypes
from .utils import print_error from .utils import print_error
class DBusXMLParser: class DBusXMLParser:
STATE_TOP = 'top' STATE_TOP = "top"
STATE_NODE = 'node' STATE_NODE = "node"
STATE_INTERFACE = 'interface' STATE_INTERFACE = "interface"
STATE_METHOD = 'method' STATE_METHOD = "method"
STATE_SIGNAL = 'signal' STATE_SIGNAL = "signal"
STATE_PROPERTY = 'property' STATE_PROPERTY = "property"
STATE_ARG = 'arg' STATE_ARG = "arg"
STATE_ANNOTATION = 'annotation' STATE_ANNOTATION = "annotation"
STATE_IGNORED = 'ignored' STATE_IGNORED = "ignored"
def __init__(self, xml_data, h_type_implies_unix_fd=True): def __init__(self, xml_data, h_type_implies_unix_fd=True):
self._parser = xml.parsers.expat.ParserCreate() self._parser = xml.parsers.expat.ParserCreate()
@ -51,21 +51,22 @@ class DBusXMLParser:
self._cur_object = None self._cur_object = None
self._cur_object_stack = [] 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._h_type_implies_unix_fd = h_type_implies_unix_fd
self._parser.Parse(xml_data) self._parser.Parse(xml_data)
COMMENT_STATE_BEGIN = 'begin' COMMENT_STATE_BEGIN = "begin"
COMMENT_STATE_PARAMS = 'params' COMMENT_STATE_PARAMS = "params"
COMMENT_STATE_BODY = 'body' COMMENT_STATE_BODY = "body"
COMMENT_STATE_SKIP = 'skip' COMMENT_STATE_SKIP = "skip"
def handle_comment(self, data): def handle_comment(self, data):
comment_state = DBusXMLParser.COMMENT_STATE_BEGIN; comment_state = DBusXMLParser.COMMENT_STATE_BEGIN
lines = data.split('\n') lines = data.split("\n")
symbol = '' symbol = ""
body = '' body = ""
in_para = False in_para = False
params = {} params = {}
for line in lines: for line in lines:
@ -73,9 +74,9 @@ class DBusXMLParser:
line = line.lstrip() line = line.lstrip()
if comment_state == DBusXMLParser.COMMENT_STATE_BEGIN: if comment_state == DBusXMLParser.COMMENT_STATE_BEGIN:
if len(line) > 0: if len(line) > 0:
colon_index = line.find(': ') colon_index = line.find(": ")
if colon_index == -1: if colon_index == -1:
if line.endswith(':'): if line.endswith(":"):
symbol = line[0 : len(line) - 1] symbol = line[0 : len(line) - 1]
comment_state = DBusXMLParser.COMMENT_STATE_PARAMS comment_state = DBusXMLParser.COMMENT_STATE_PARAMS
else: else:
@ -84,17 +85,17 @@ class DBusXMLParser:
symbol = line[0:colon_index] 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: if len(rest_of_line) > 0:
body += '<para>' + rest_of_line + '</para>' body += "<para>" + rest_of_line + "</para>"
comment_state = DBusXMLParser.COMMENT_STATE_PARAMS comment_state = DBusXMLParser.COMMENT_STATE_PARAMS
elif comment_state == DBusXMLParser.COMMENT_STATE_PARAMS: elif comment_state == DBusXMLParser.COMMENT_STATE_PARAMS:
if line.startswith('@'): if line.startswith("@"):
colon_index = line.find(': ') colon_index = line.find(": ")
if colon_index == -1: if colon_index == -1:
comment_state = DBusXMLParser.COMMENT_STATE_BODY comment_state = DBusXMLParser.COMMENT_STATE_BODY
if not in_para: if not in_para:
body += '<para>' body += "<para>"
in_para = True in_para = True
body += orig_line + '\n' body += orig_line + "\n"
else: else:
param = line[1:colon_index] param = line[1:colon_index]
docs = line[colon_index + 2 :] docs = line[colon_index + 2 :]
@ -103,23 +104,23 @@ class DBusXMLParser:
comment_state = DBusXMLParser.COMMENT_STATE_BODY comment_state = DBusXMLParser.COMMENT_STATE_BODY
if len(line) > 0: if len(line) > 0:
if not in_para: if not in_para:
body += '<para>' body += "<para>"
in_para = True in_para = True
body += orig_line + '\n' body += orig_line + "\n"
elif comment_state == DBusXMLParser.COMMENT_STATE_BODY: elif comment_state == DBusXMLParser.COMMENT_STATE_BODY:
if len(line) > 0: if len(line) > 0:
if not in_para: if not in_para:
body += '<para>' body += "<para>"
in_para = True in_para = True
body += orig_line + '\n' body += orig_line + "\n"
else: else:
if in_para: if in_para:
body += '</para>' body += "</para>"
in_para = False in_para = False
if in_para: if in_para:
body += '</para>' body += "</para>"
if symbol != '': if symbol != "":
self.doc_comment_last_symbol = symbol self.doc_comment_last_symbol = symbol
self.doc_comment_params = params self.doc_comment_params = params
self.doc_comment_body = body self.doc_comment_body = body
@ -141,77 +142,76 @@ class DBusXMLParser:
elif self.state == DBusXMLParser.STATE_NODE: elif self.state == DBusXMLParser.STATE_NODE:
if name == DBusXMLParser.STATE_INTERFACE: if name == DBusXMLParser.STATE_INTERFACE:
self.state = DBusXMLParser.STATE_INTERFACE self.state = DBusXMLParser.STATE_INTERFACE
iface = dbustypes.Interface(attrs['name']) iface = dbustypes.Interface(attrs["name"])
self._cur_object = iface self._cur_object = iface
self.parsed_interfaces.append(iface) self.parsed_interfaces.append(iface)
elif name == DBusXMLParser.STATE_ANNOTATION: elif name == DBusXMLParser.STATE_ANNOTATION:
self.state = 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.annotations.append(anno)
self._cur_object = anno self._cur_object = anno
else: else:
self.state = DBusXMLParser.STATE_IGNORED self.state = DBusXMLParser.STATE_IGNORED
# assign docs, if any # 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 self._cur_object.doc_string = self.doc_comment_body
if 'short_description' in self.doc_comment_params: if "short_description" in self.doc_comment_params:
short_description = self.doc_comment_params['short_description'] short_description = self.doc_comment_params["short_description"]
self._cur_object.doc_string_brief = short_description self._cur_object.doc_string_brief = short_description
if 'since' in self.doc_comment_params: if "since" in self.doc_comment_params:
self._cur_object.since = \ self._cur_object.since = self.doc_comment_params["since"].strip()
self.doc_comment_params['since'].strip()
elif self.state == DBusXMLParser.STATE_INTERFACE: elif self.state == DBusXMLParser.STATE_INTERFACE:
if name == DBusXMLParser.STATE_METHOD: if name == DBusXMLParser.STATE_METHOD:
self.state = DBusXMLParser.STATE_METHOD self.state = DBusXMLParser.STATE_METHOD
method = dbustypes.Method(attrs['name'], method = dbustypes.Method(
h_type_implies_unix_fd=self._h_type_implies_unix_fd) attrs["name"], h_type_implies_unix_fd=self._h_type_implies_unix_fd
)
self._cur_object.methods.append(method) self._cur_object.methods.append(method)
self._cur_object = method self._cur_object = method
elif name == DBusXMLParser.STATE_SIGNAL: elif name == DBusXMLParser.STATE_SIGNAL:
self.state = 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.signals.append(signal)
self._cur_object = signal self._cur_object = signal
elif name == DBusXMLParser.STATE_PROPERTY: elif name == DBusXMLParser.STATE_PROPERTY:
self.state = 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.properties.append(prop)
self._cur_object = prop self._cur_object = prop
elif name == DBusXMLParser.STATE_ANNOTATION: elif name == DBusXMLParser.STATE_ANNOTATION:
self.state = 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.annotations.append(anno)
self._cur_object = anno self._cur_object = anno
else: else:
self.state = DBusXMLParser.STATE_IGNORED self.state = DBusXMLParser.STATE_IGNORED
# assign docs, if any # 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 self._cur_object.doc_string = self.doc_comment_body
if 'since' in self.doc_comment_params: if "since" in self.doc_comment_params:
self._cur_object.since = \ self._cur_object.since = self.doc_comment_params["since"].strip()
self.doc_comment_params['since'].strip()
elif self.state == DBusXMLParser.STATE_METHOD: elif self.state == DBusXMLParser.STATE_METHOD:
if name == DBusXMLParser.STATE_ARG: if name == DBusXMLParser.STATE_ARG:
self.state = DBusXMLParser.STATE_ARG self.state = DBusXMLParser.STATE_ARG
arg_name = None arg_name = None
if 'name' in attrs: if "name" in attrs:
arg_name = attrs['name'] arg_name = attrs["name"]
arg = dbustypes.Arg(arg_name, attrs['type']) arg = dbustypes.Arg(arg_name, attrs["type"])
direction = attrs.get('direction', 'in') direction = attrs.get("direction", "in")
if direction == 'in': if direction == "in":
self._cur_object.in_args.append(arg) self._cur_object.in_args.append(arg)
elif direction == 'out': elif direction == "out":
self._cur_object.out_args.append(arg) self._cur_object.out_args.append(arg)
else: else:
print_error('Invalid direction "{}"'.format(direction)) print_error('Invalid direction "{}"'.format(direction))
self._cur_object = arg self._cur_object = arg
elif name == DBusXMLParser.STATE_ANNOTATION: elif name == DBusXMLParser.STATE_ANNOTATION:
self.state = 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.annotations.append(anno)
self._cur_object = anno self._cur_object = anno
else: else:
@ -219,26 +219,27 @@ class DBusXMLParser:
# assign docs, if any # assign docs, if any
if self.doc_comment_last_symbol == old_cur_object.name: if self.doc_comment_last_symbol == old_cur_object.name:
if 'name' in attrs and attrs['name'] in self.doc_comment_params: if "name" in attrs and attrs["name"] in self.doc_comment_params:
doc_string = self.doc_comment_params[attrs['name']] doc_string = self.doc_comment_params[attrs["name"]]
if doc_string != None: if doc_string is not None:
self._cur_object.doc_string = doc_string self._cur_object.doc_string = doc_string
if 'since' in self.doc_comment_params: if "since" in self.doc_comment_params:
self._cur_object.since = \ self._cur_object.since = self.doc_comment_params[
self.doc_comment_params['since'].strip() "since"
].strip()
elif self.state == DBusXMLParser.STATE_SIGNAL: elif self.state == DBusXMLParser.STATE_SIGNAL:
if name == DBusXMLParser.STATE_ARG: if name == DBusXMLParser.STATE_ARG:
self.state = DBusXMLParser.STATE_ARG self.state = DBusXMLParser.STATE_ARG
arg_name = None arg_name = None
if 'name' in attrs: if "name" in attrs:
arg_name = attrs['name'] arg_name = attrs["name"]
arg = dbustypes.Arg(arg_name, attrs['type']) arg = dbustypes.Arg(arg_name, attrs["type"])
self._cur_object.args.append(arg) self._cur_object.args.append(arg)
self._cur_object = arg self._cur_object = arg
elif name == DBusXMLParser.STATE_ANNOTATION: elif name == DBusXMLParser.STATE_ANNOTATION:
self.state = 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.annotations.append(anno)
self._cur_object = anno self._cur_object = anno
else: else:
@ -246,18 +247,19 @@ class DBusXMLParser:
# assign docs, if any # assign docs, if any
if self.doc_comment_last_symbol == old_cur_object.name: if self.doc_comment_last_symbol == old_cur_object.name:
if 'name' in attrs and attrs['name'] in self.doc_comment_params: if "name" in attrs and attrs["name"] in self.doc_comment_params:
doc_string = self.doc_comment_params[attrs['name']] doc_string = self.doc_comment_params[attrs["name"]]
if doc_string != None: if doc_string is not None:
self._cur_object.doc_string = doc_string self._cur_object.doc_string = doc_string
if 'since' in self.doc_comment_params: if "since" in self.doc_comment_params:
self._cur_object.since = \ self._cur_object.since = self.doc_comment_params[
self.doc_comment_params['since'].strip() "since"
].strip()
elif self.state == DBusXMLParser.STATE_PROPERTY: elif self.state == DBusXMLParser.STATE_PROPERTY:
if name == DBusXMLParser.STATE_ANNOTATION: if name == DBusXMLParser.STATE_ANNOTATION:
self.state = 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.annotations.append(anno)
self._cur_object = anno self._cur_object = anno
else: else:
@ -266,7 +268,7 @@ class DBusXMLParser:
elif self.state == DBusXMLParser.STATE_ARG: elif self.state == DBusXMLParser.STATE_ARG:
if name == DBusXMLParser.STATE_ANNOTATION: if name == DBusXMLParser.STATE_ANNOTATION:
self.state = 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.annotations.append(anno)
self._cur_object = anno self._cur_object = anno
else: else:
@ -275,14 +277,18 @@ class DBusXMLParser:
elif self.state == DBusXMLParser.STATE_ANNOTATION: elif self.state == DBusXMLParser.STATE_ANNOTATION:
if name == DBusXMLParser.STATE_ANNOTATION: if name == DBusXMLParser.STATE_ANNOTATION:
self.state = 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.annotations.append(anno)
self._cur_object = anno self._cur_object = anno
else: else:
self.state = DBusXMLParser.STATE_IGNORED self.state = DBusXMLParser.STATE_IGNORED
else: 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.state_stack.append(old_state)
self._cur_object_stack.append(old_cur_object) self._cur_object_stack.append(old_cur_object)
@ -291,6 +297,7 @@ class DBusXMLParser:
self.state = self.state_stack.pop() self.state = self.state_stack.pop()
self._cur_object = self._cur_object_stack.pop() self._cur_object = self._cur_object_stack.pop()
def parse_dbus_xml(xml_data, h_type_implies_unix_fd): def parse_dbus_xml(xml_data, h_type_implies_unix_fd):
parser = DBusXMLParser(xml_data, h_type_implies_unix_fd) parser = DBusXMLParser(xml_data, h_type_implies_unix_fd)
return parser.parsed_interfaces return parser.parsed_interfaces

View File

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

View File

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

View File

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

View File

@ -8,14 +8,13 @@
# #
# See issue #1580 # See issue #1580
import io
import string import string
import sys import sys
if len(sys.argv) != 2: 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 count in range(12):
for c in string.ascii_lowercase: for c in string.ascii_lowercase:
f.write("%s\n" % (c * 100)) f.write("%s\n" % (c * 100))

View File

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

View File

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

View File

@ -30,6 +30,7 @@ import sys
import base64 import base64
from io import StringIO from io import StringIO
# Log modes # Log modes
class LogMode(object): class LogMode(object):
LogToError, LogToDiagnostics, LogToYAML, LogToAttachment = range(4) LogToError, LogToDiagnostics, LogToYAML, LogToAttachment = range(4)
@ -73,7 +74,6 @@ class TAPTestResult(unittest.TestResult):
sys.stdout = self.orig_stdout sys.stdout = self.orig_stdout
sys.stderr = self.orig_stderr sys.stderr = self.orig_stderr
def print_raw(self, text): def print_raw(self, text):
self.output_stream.write(text) self.output_stream.write(text)
self.output_stream.flush() self.output_stream.flush()
@ -101,7 +101,7 @@ class TAPTestResult(unittest.TestResult):
else: else:
logs = [ logs = [
(self.test_output_log, self.test_output, "test_output"), (self.test_output_log, self.test_output, "test_output"),
(self.message_log, self.message, "message") (self.message_log, self.message, "message"),
] ]
for log_mode, log, log_name in logs: for log_mode, log, log_name in logs:
if log_mode != LogMode.LogToError: if log_mode != LogMode.LogToError:
@ -110,17 +110,23 @@ class TAPTestResult(unittest.TestResult):
if log_mode == LogMode.LogToYAML: if log_mode == LogMode.LogToYAML:
self.print_raw(" ---\n") self.print_raw(" ---\n")
self.print_raw(" " + log_name + ": |\n") self.print_raw(" " + log_name + ": |\n")
self.print_raw(" " + output.rstrip().replace("\n", "\n ") + "\n") self.print_raw(
" " + output.rstrip().replace("\n", "\n ") + "\n"
)
self.print_raw(" ...\n") self.print_raw(" ...\n")
elif log_mode == LogMode.LogToAttachment: elif log_mode == LogMode.LogToAttachment:
self.print_raw(" ---\n") self.print_raw(" ---\n")
self.print_raw(" " + log_name + ":\n") self.print_raw(" " + log_name + ":\n")
self.print_raw(" File-Name: " + log_name + ".txt\n") self.print_raw(" File-Name: " + log_name + ".txt\n")
self.print_raw(" File-Type: text/plain\n") self.print_raw(" File-Type: text/plain\n")
self.print_raw(" File-Content: " + base64.b64encode(output) + "\n") self.print_raw(
" File-Content: " + base64.b64encode(output) + "\n"
)
self.print_raw(" ...\n") self.print_raw(" ...\n")
else: else:
self.print_raw("# " + output.rstrip().replace("\n", "\n# ") + "\n") self.print_raw(
"# " + output.rstrip().replace("\n", "\n# ") + "\n"
)
# Truncate doesn't change the current stream position. # Truncate doesn't change the current stream position.
# Seek to the beginning to avoid extensions on subsequent writes. # Seek to the beginning to avoid extensions on subsequent writes.
log.seek(0) log.seek(0)
@ -155,10 +161,13 @@ class TAPTestResult(unittest.TestResult):
class TAPTestRunner(object): class TAPTestRunner(object):
def __init__(self, def __init__(
self,
message_log=LogMode.LogToYAML, message_log=LogMode.LogToYAML,
test_output_log=LogMode.LogToDiagnostics, test_output_log=LogMode.LogToDiagnostics,
output_stream = sys.stdout, error_stream = sys.stderr): output_stream=sys.stdout,
error_stream=sys.stderr,
):
self.output_stream = output_stream self.output_stream = output_stream
self.error_stream = error_stream self.error_stream = error_stream
self.message_log = message_log self.message_log = message_log
@ -169,7 +178,8 @@ class TAPTestRunner(object):
self.output_stream, self.output_stream,
self.error_stream, self.error_stream,
self.message_log, self.message_log,
self.test_output_log) self.test_output_log,
)
test(result) test(result)
result.printErrors() result.printErrors()

View File

@ -4,10 +4,12 @@ import sys
if sys.version_info[0] >= 3: if sys.version_info[0] >= 3:
long = int long = int
# This is not quite right, as local vars may override symname # 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) return gdb.selected_frame().read_var(symname)
def g_quark_to_string(quark): def g_quark_to_string(quark):
if quark is None: if quark is None:
return None return None
@ -17,16 +19,17 @@ def g_quark_to_string (quark):
try: try:
val = read_global_var("quarks") val = read_global_var("quarks")
max_q = long(read_global_var("quark_seq_id")) max_q = long(read_global_var("quark_seq_id"))
except: except Exception:
try: try:
val = read_global_var("g_quarks") val = read_global_var("g_quarks")
max_q = long(read_global_var("g_quark_seq_id")) max_q = long(read_global_var("g_quark_seq_id"))
except: except Exception:
return None; return None
if quark < max_q: if quark < max_q:
return val[quark].string() return val[quark].string()
return None return None
# We override the node printers too, so that node->next is not expanded # We override the node printers too, so that node->next is not expanded
class GListNodePrinter: class GListNodePrinter:
"Prints a GList node" "Prints a GList node"
@ -35,7 +38,12 @@ class GListNodePrinter:
self.val = val self.val = val
def to_string(self): 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"])) return "{data=%s, next=0x%x, prev=0x%x}" % (
str(self.val["data"]),
long(self.val["next"]),
long(self.val["prev"]),
)
class GSListNodePrinter: class GSListNodePrinter:
"Prints a GSList node" "Prints a GSList node"
@ -46,6 +54,7 @@ class GSListNodePrinter:
def to_string(self): def to_string(self):
return "{data=%s, next=0x%x}" % (str(self.val["data"]), long(self.val["next"])) return "{data=%s, next=0x%x}" % (str(self.val["data"]), long(self.val["next"]))
class GListPrinter: class GListPrinter:
"Prints a GList" "Prints a GList"
@ -61,11 +70,11 @@ class GListPrinter:
def next(self): def next(self):
if self.link == 0: if self.link == 0:
raise StopIteration raise StopIteration
data = self.link['data'] data = self.link["data"]
self.link = self.link['next'] self.link = self.link["next"]
count = self.count count = self.count
self.count = self.count + 1 self.count = self.count + 1
return ('[%d]' % count, data) return ("[%d]" % count, data)
__next__ = next __next__ = next
@ -82,6 +91,7 @@ class GListPrinter:
def display_hint(self): def display_hint(self):
return "array" return "array"
class GHashPrinter: class GHashPrinter:
"Prints a GHashTable" "Prints a GHashTable"
@ -90,7 +100,9 @@ class GHashPrinter:
def __init__(self, ptr, big_items): def __init__(self, ptr, big_items):
self._big_items = big_items self._big_items = big_items
self._gpointer_type = gdb.lookup_type("gpointer") 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()) self._items = ptr.cast(item_type.pointer())
@ -119,7 +131,7 @@ class GHashPrinter:
def next(self): def next(self):
if self.ht == 0: if self.ht == 0:
raise StopIteration raise StopIteration
if self.value != None: if self.value is not None:
v = self.value v = self.value
self.value = None self.value = None
return v return v
@ -132,10 +144,10 @@ class GHashPrinter:
key = key.cast(gdb.lookup_type("char").pointer()) key = key.cast(gdb.lookup_type("char").pointer())
# Queue value for next result # Queue value for next result
self.value = ('[%dv]'% (self.pos), val) self.value = ("[%dv]" % (self.pos), val)
# Increment pos and return key # Increment pos and return key
key = ('[%dk]'% (self.pos), key) key = ("[%dk]" % (self.pos), key)
self.pos += 1 self.pos += 1
return key return key
@ -149,9 +161,13 @@ class GHashPrinter:
self.keys_are_strings = False self.keys_are_strings = False
try: try:
string_hash = read_global_var("g_str_hash") string_hash = read_global_var("g_str_hash")
except: except Exception:
string_hash = None 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 self.keys_are_strings = True
def children(self): def children(self):
@ -163,6 +179,7 @@ class GHashPrinter:
def display_hint(self): def display_hint(self):
return "map" return "map"
def pretty_printer_lookup(val): def pretty_printer_lookup(val):
# None yet, want things like hash table and list # None yet, want things like hash table and list
@ -189,19 +206,21 @@ def pretty_printer_lookup (val):
return GListPrinter(val, "GSList") return GListPrinter(val, "GSList")
return None return None
def register(obj): def register(obj):
if obj is None: if obj is None:
obj = gdb obj = gdb
obj.pretty_printers.append(pretty_printer_lookup) obj.pretty_printers.append(pretty_printer_lookup)
class ForeachCommand(gdb.Command): class ForeachCommand(gdb.Command):
"""Foreach on list""" """Foreach on list"""
def __init__(self): def __init__(self):
super (ForeachCommand, self).__init__ ("gforeach", super(ForeachCommand, self).__init__(
gdb.COMMAND_DATA, "gforeach", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL
gdb.COMPLETE_SYMBOL) )
def valid_name(self, name): def valid_name(self, name):
if not name[0].isalpha(): if not name[0].isalpha():
@ -249,16 +268,16 @@ class ForeachCommand (gdb.Command):
gdb.execute(command) gdb.execute(command)
def slist_iterator(self, arg, container, command): def slist_iterator(self, arg, container, command):
l = container.cast (gdb.lookup_type("GSList").pointer()) list_element = container.cast(gdb.lookup_type("GSList").pointer())
while long(l) != 0: while long(list_element) != 0:
self.do_iter (arg, l["data"], command) self.do_iter(arg, list_element["data"], command)
l = l["next"] list_element = list_element["next"]
def list_iterator(self, arg, container, command): def list_iterator(self, arg, container, command):
l = container.cast (gdb.lookup_type("GList").pointer()) list_element = container.cast(gdb.lookup_type("GList").pointer())
while long(l) != 0: while long(list_element) != 0:
self.do_iter (arg, l["data"], command) self.do_iter(arg, list_element["data"], command)
l = l["next"] list_element = list_element["next"]
def pick_iterator(self, container): def pick_iterator(self, container):
t = container.type.unqualified() t = container.type.unqualified()
@ -277,4 +296,5 @@ class ForeachCommand (gdb.Command):
func = self.pick_iterator(container) func = self.pick_iterator(container)
func(var, container, command) func(var, container, command)
ForeachCommand() ForeachCommand()

View File

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

View File

@ -1,4 +1,3 @@
import os.path
import gdb import gdb
import glib_gdb import glib_gdb
import sys import sys
@ -7,20 +6,24 @@ if sys.version_info[0] >= 3:
long = int long = int
else: else:
import itertools import itertools
map = itertools.imap map = itertools.imap
# FrameDecorator is new in gdb 7.7, so we adapt to its absence. # FrameDecorator is new in gdb 7.7, so we adapt to its absence.
try: try:
import gdb.FrameDecorator import gdb.FrameDecorator
HAVE_GDB_FRAMEDECORATOR = True HAVE_GDB_FRAMEDECORATOR = True
FrameDecorator = gdb.FrameDecorator.FrameDecorator FrameDecorator = gdb.FrameDecorator.FrameDecorator
except ImportError: except ImportError:
HAVE_GDB_FRAMEDECORATOR = False HAVE_GDB_FRAMEDECORATOR = False
# This is not quite right, as local vars may override symname # 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) return gdb.selected_frame().read_var(symname)
def g_type_to_typenode(gtype): def g_type_to_typenode(gtype):
def lookup_fundamental_type(typenode): def lookup_fundamental_type(typenode):
if typenode == 0: if typenode == 0:
@ -38,12 +41,14 @@ def g_type_to_typenode (gtype):
typenode = lookup_fundamental_type(typenode) typenode = lookup_fundamental_type(typenode)
return typenode return typenode
def g_type_to_name(gtype): def g_type_to_name(gtype):
typenode = g_type_to_typenode(gtype) typenode = g_type_to_typenode(gtype)
if typenode != None: if typenode is not None:
return glib_gdb.g_quark_to_string(typenode["qname"]) return glib_gdb.g_quark_to_string(typenode["qname"])
return None return None
def is_g_type_instance(val): def is_g_type_instance(val):
def is_g_type_instance_helper(type): def is_g_type_instance_helper(type):
if str(type) == "GTypeInstance": if str(type) == "GTypeInstance":
@ -68,6 +73,7 @@ def is_g_type_instance (val):
type = type.target() 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: if long(instance) != 0:
try: try:
@ -80,6 +86,7 @@ def g_type_name_from_instance (instance):
pass pass
return None return None
class GTypePrettyPrinter: class GTypePrettyPrinter:
"Prints a GType instance pointer" "Prints a GType instance pointer"
@ -92,12 +99,14 @@ class GTypePrettyPrinter:
return ("0x%x [%s]") % (long(self.val), name) return ("0x%x [%s]") % (long(self.val), name)
return ("0x%x") % (long(self.val)) return ("0x%x") % (long(self.val))
def is_g_type_class_instance(val): def is_g_type_class_instance(val):
type = val.type type = val.type
if type.code != gdb.TYPE_CODE_PTR: if type.code != gdb.TYPE_CODE_PTR:
return False return False
return str(type.target()) == "GTypeClass" return str(type.target()) == "GTypeClass"
class GTypeHandlePrettyPrinter: class GTypeHandlePrettyPrinter:
"Prints a GType instance" "Prints a GType instance"
@ -107,7 +116,7 @@ class GTypeHandlePrettyPrinter:
def to_string(self): def to_string(self):
typenode = g_type_to_typenode(self.val) typenode = g_type_to_typenode(self.val)
if typenode != None: if typenode is not None:
name = glib_gdb.g_quark_to_string(typenode["qname"]) name = glib_gdb.g_quark_to_string(typenode["qname"])
s = ("0x%x [%s%s") % (long(self.val), self.hint, name) s = ("0x%x [%s%s") % (long(self.val), self.hint, name)
for i in range(1, int(typenode["n_supers"])): for i in range(1, int(typenode["n_supers"])):
@ -121,6 +130,7 @@ class GTypeHandlePrettyPrinter:
else: else:
return ("0x%x") % (long(self.val)) return ("0x%x") % (long(self.val))
def pretty_printer_lookup(val): def pretty_printer_lookup(val):
if is_g_type_instance(val): if is_g_type_instance(val):
return GTypePrettyPrinter(val) return GTypePrettyPrinter(val)
@ -131,6 +141,7 @@ def pretty_printer_lookup (val):
return None return None
def get_signal_name(id): def get_signal_name(id):
if id is None: if id is None:
return None return None
@ -144,9 +155,11 @@ def get_signal_name (id):
return val[id]["name"].string() return val[id]["name"].string()
return None return None
def frame_name(frame): def frame_name(frame):
return str(frame.function()) return str(frame.function())
def frame_var(frame, var): def frame_var(frame, var):
return frame.inferior_frame().read_var(var) return frame.inferior_frame().read_var(var)
@ -165,7 +178,7 @@ class SignalFrame(FrameDecorator):
v = frame_var(frame, name) v = frame_var(frame, name)
if v is None or v.is_optimized_out: if v is None or v.is_optimized_out:
return None return None
if array != None: if array is not None:
array.append(v) array.append(v)
return v return v
except ValueError: except ValueError:
@ -178,8 +191,8 @@ class SignalFrame(FrameDecorator):
return None 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 # Ensure this is a somewhat correct object pointer
if v != None and g_type_name_from_instance (v): if v is not None and g_type_name_from_instance(v):
if array != None: if array is not None:
array.append(v) array.append(v)
return v return v
return None return None
@ -187,14 +200,14 @@ class SignalFrame(FrameDecorator):
return None return None
def append(self, array, obj): def append(self, array, obj):
if obj != None: if obj is not None:
array.append(obj) array.append(obj)
def or_join_array(self, array): def or_join_array(self, array):
if len(array) == 0: if len(array) == 0:
return "???" return "???"
else: else:
return ' or '.join(set(map(str, array))) return " or ".join(set(map(str, array)))
def get_detailed_signal_from_frame(self, frame, signal): def get_detailed_signal_from_frame(self, frame, signal):
detail = self.read_var(frame, "detail") detail = self.read_var(frame, "detail")
@ -221,7 +234,9 @@ class SignalFrame(FrameDecorator):
if name == "g_signal_emitv": 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: if instance_and_params:
instance = instance_and_params[0]["v_pointer"].cast (gdb.Type("GObject").pointer()) instance = instance_and_params[0]["v_pointer"].cast(
gdb.Type("GObject").pointer()
)
self.append(instances, instance) self.append(instances, instance)
id = self.read_var(frame, "signal_id") id = self.read_var(frame, "signal_id")
signal = get_signal_name(id) signal = get_signal_name(id)
@ -253,6 +268,7 @@ class SignalFrame(FrameDecorator):
def describe(self, stream, full): def describe(self, stream, full):
stream.write(" " + self.function() + "\n") stream.write(" " + self.function() + "\n")
class GFrameDecorator: class GFrameDecorator:
def __init__(self, iter): def __init__(self, iter):
self.queue = [] self.queue = []
@ -296,11 +312,13 @@ class GFrameDecorator:
break break
end = emission + 1 end = emission + 1
while end < len(self.queue): while end < len(self.queue):
if frame_name(self.queue[end]) in ["g_signal_emitv", if frame_name(self.queue[end]) in [
"g_signal_emitv",
"g_signal_emit_valist", "g_signal_emit_valist",
"g_signal_emit", "g_signal_emit",
"g_signal_emit_by_name", "g_signal_emit_by_name",
"_g_closure_invoke_va"]: "_g_closure_invoke_va",
]:
end = end + 1 end = end + 1
else: else:
break break
@ -314,14 +332,16 @@ class GFrameDecorator:
def __next__(self): def __next__(self):
return self.next() return self.next()
class GFrameFilter(object): class GFrameFilter(object):
name = 'glib' name = "glib"
enabled = True enabled = True
priority = 100 priority = 100
def filter(self, iterator): def filter(self, iterator):
return GFrameDecorator(iterator) return GFrameDecorator(iterator)
def register(obj): def register(obj):
if obj is None: if obj is None:
obj = gdb obj = gdb

View File

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

View File

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

View File

@ -30,6 +30,7 @@ import sys
import base64 import base64
from io import StringIO from io import StringIO
# Log modes # Log modes
class LogMode(object): class LogMode(object):
LogToError, LogToDiagnostics, LogToYAML, LogToAttachment = range(4) LogToError, LogToDiagnostics, LogToYAML, LogToAttachment = range(4)
@ -73,7 +74,6 @@ class TAPTestResult(unittest.TestResult):
sys.stdout = self.orig_stdout sys.stdout = self.orig_stdout
sys.stderr = self.orig_stderr sys.stderr = self.orig_stderr
def print_raw(self, text): def print_raw(self, text):
self.output_stream.write(text) self.output_stream.write(text)
self.output_stream.flush() self.output_stream.flush()
@ -101,7 +101,7 @@ class TAPTestResult(unittest.TestResult):
else: else:
logs = [ logs = [
(self.test_output_log, self.test_output, "test_output"), (self.test_output_log, self.test_output, "test_output"),
(self.message_log, self.message, "message") (self.message_log, self.message, "message"),
] ]
for log_mode, log, log_name in logs: for log_mode, log, log_name in logs:
if log_mode != LogMode.LogToError: if log_mode != LogMode.LogToError:
@ -110,17 +110,23 @@ class TAPTestResult(unittest.TestResult):
if log_mode == LogMode.LogToYAML: if log_mode == LogMode.LogToYAML:
self.print_raw(" ---\n") self.print_raw(" ---\n")
self.print_raw(" " + log_name + ": |\n") self.print_raw(" " + log_name + ": |\n")
self.print_raw(" " + output.rstrip().replace("\n", "\n ") + "\n") self.print_raw(
" " + output.rstrip().replace("\n", "\n ") + "\n"
)
self.print_raw(" ...\n") self.print_raw(" ...\n")
elif log_mode == LogMode.LogToAttachment: elif log_mode == LogMode.LogToAttachment:
self.print_raw(" ---\n") self.print_raw(" ---\n")
self.print_raw(" " + log_name + ":\n") self.print_raw(" " + log_name + ":\n")
self.print_raw(" File-Name: " + log_name + ".txt\n") self.print_raw(" File-Name: " + log_name + ".txt\n")
self.print_raw(" File-Type: text/plain\n") self.print_raw(" File-Type: text/plain\n")
self.print_raw(" File-Content: " + base64.b64encode(output) + "\n") self.print_raw(
" File-Content: " + base64.b64encode(output) + "\n"
)
self.print_raw(" ...\n") self.print_raw(" ...\n")
else: else:
self.print_raw("# " + output.rstrip().replace("\n", "\n# ") + "\n") self.print_raw(
"# " + output.rstrip().replace("\n", "\n# ") + "\n"
)
# Truncate doesn't change the current stream position. # Truncate doesn't change the current stream position.
# Seek to the beginning to avoid extensions on subsequent writes. # Seek to the beginning to avoid extensions on subsequent writes.
log.seek(0) log.seek(0)
@ -155,10 +161,13 @@ class TAPTestResult(unittest.TestResult):
class TAPTestRunner(object): class TAPTestRunner(object):
def __init__(self, def __init__(
self,
message_log=LogMode.LogToYAML, message_log=LogMode.LogToYAML,
test_output_log=LogMode.LogToDiagnostics, test_output_log=LogMode.LogToDiagnostics,
output_stream = sys.stdout, error_stream = sys.stderr): output_stream=sys.stdout,
error_stream=sys.stderr,
):
self.output_stream = output_stream self.output_stream = output_stream
self.error_stream = error_stream self.error_stream = error_stream
self.message_log = message_log self.message_log = message_log
@ -169,7 +178,8 @@ class TAPTestRunner(object):
self.output_stream, self.output_stream,
self.error_stream, self.error_stream,
self.message_log, self.message_log,
self.test_output_log) self.test_output_log,
)
test(result) test(result)
result.printErrors() result.printErrors()

View File

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

View File

@ -27,9 +27,14 @@ import sys
import argparse import argparse
# Disable line length warnings as wrapping the test templates would be hard
# flake8: noqa: E501
def main(argv): def main(argv):
parser = argparse.ArgumentParser( 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("UNICODE-VERSION")
parser.add_argument("UnicodeData.txt") parser.add_argument("UnicodeData.txt")
parser.add_argument("SpecialCasing.txt") parser.add_argument("SpecialCasing.txt")
@ -39,9 +44,23 @@ def main(argv):
filename_casing = getattr(args, "SpecialCasing.txt") filename_casing = getattr(args, "SpecialCasing.txt")
# Names of fields in Unicode data table. # Names of fields in Unicode data table.
CODE, NAME, CATEGORY, COMBINING_CLASSES, BIDI_CATEGORY, DECOMPOSITION, \ (
DECIMAL_VALUE, DIGIT_VALUE, NUMERIC_VALUE, MIRRORED, OLD_NAME, \ CODE,
COMMENT, UPPER, LOWER, TITLE = range(15) 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 # Names of fields in the SpecialCasing table
CASE_CODE, CASE_LOWER, CASE_TITLE, CASE_UPPER, CASE_CONDITION = range(5) 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(";")] fields = [f.strip() for f in line.split(";")]
if len(fields) != 15: if len(fields) != 15:
raise SystemExit( raise SystemExit(
"Entry for %s has wrong number of fields (%d)" % ( "Entry for %s has wrong number of fields (%d)"
fields[CODE], len(fields))) % (fields[CODE], len(fields))
)
code = int(fields[CODE], 16) code = int(fields[CODE], 16)
@ -92,8 +112,23 @@ def main(argv):
else: else:
# The gap represents undefined characters. Only the type # The gap represents undefined characters. Only the type
# matters. # matters.
gfields = ['', '', 'Cn', '0', '', '', '', '', '', '', '', gfields = [
'', '', '', ''] "",
"",
"Cn",
"0",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
]
last_code += 1 last_code += 1
while last_code < code: while last_code < code:
@ -117,8 +152,9 @@ def main(argv):
fields = [f.strip() for f in line.split(";")] fields = [f.strip() for f in line.split(";")]
if len(fields) not in (4, 5): if len(fields) not in (4, 5):
raise SystemExit( raise SystemExit(
"Entry for %s has wrong number of fields (%d)" % ( "Entry for %s has wrong number of fields (%d)"
fields[CASE_CODE], len(fields))) % (fields[CASE_CODE], len(fields))
)
if len(fields) == 5: if len(fields) == 5:
# Ignore conditional special cases - we'll handle them manually # Ignore conditional special cases - we'll handle them manually
@ -134,7 +170,8 @@ def main(argv):
def print_tests(version, upper, title, lower): def print_tests(version, upper, title, lower):
print("""\ print(
"""\
# Test cases generated from Unicode {} data # Test cases generated from Unicode {} data
# by gen-casemap-txt.py. Do not edit. # 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\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 \t\u03b1\u0314\u0345\t\u03b1\u0314\u0345\t\u0391\u0314\u0345\t\u0391\u0314\u0399\t
# Handling of final and nonfinal sigma # 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 # Lithuanian rule of i followed by letter with dot. Not at all sure
# about the titlecase part here # about the titlecase part here
lt_LT\ti\u0117\ti\u0117\tIe\tIE\t 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 \ta\ufb04\ta\ufb04\tAffl\tAFFL\t# FB04
# #
# Now the automatic tests # Now the automatic tests
#""".format(version)) #""".format(
version
)
)
for i in range(0x10ffff): for i in range(0x10FFFF):
if i == 0x3A3: if i == 0x3A3:
# Greek sigma needs special tests # Greek sigma needs special tests
continue continue