SCons: Format buildsystem files with psf/black
Configured for a max line length of 120 characters.
psf/black is very opinionated and purposely doesn't leave much room for
configuration. The output is mostly OK so that should be fine for us,
but some things worth noting:
- Manually wrapped strings will be reflowed, so by using a line length
of 120 for the sake of preserving readability for our long command
calls, it also means that some manually wrapped strings are back on
the same line and should be manually merged again.
- Code generators using string concatenation extensively look awful,
since black puts each operand on a single line. We need to refactor
these generators to use more pythonic string formatting, for which
many options are available (`%`, `format` or f-strings).
- CI checks and a pre-commit hook will be added to ensure that future
buildsystem changes are well-formatted.
(cherry picked from commit cd4e46ee65)
This commit is contained in:
@ -21,7 +21,7 @@ def write_string(_f, text, newline=True):
|
||||
for t in range(tab):
|
||||
_f.write("\t")
|
||||
_f.write(text)
|
||||
if (newline):
|
||||
if newline:
|
||||
_f.write("\n")
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ def escape(ret):
|
||||
ret = ret.replace("<", ">")
|
||||
ret = ret.replace(">", "<")
|
||||
ret = ret.replace("'", "'")
|
||||
ret = ret.replace("\"", """)
|
||||
ret = ret.replace('"', """)
|
||||
return ret
|
||||
|
||||
|
||||
@ -43,25 +43,26 @@ def dec_tab():
|
||||
global tab
|
||||
tab -= 1
|
||||
|
||||
|
||||
write_string(f, '<?xml version="1.0" encoding="UTF-8" ?>')
|
||||
write_string(f, '<doc version="' + new_doc.attrib["version"] + '">')
|
||||
|
||||
|
||||
def get_tag(node, name):
|
||||
tag = ""
|
||||
if (name in node.attrib):
|
||||
tag = ' ' + name + '="' + escape(node.attrib[name]) + '" '
|
||||
if name in node.attrib:
|
||||
tag = " " + name + '="' + escape(node.attrib[name]) + '" '
|
||||
return tag
|
||||
|
||||
|
||||
def find_method_descr(old_class, name):
|
||||
|
||||
methods = old_class.find("methods")
|
||||
if(methods != None and len(list(methods)) > 0):
|
||||
if methods != None and len(list(methods)) > 0:
|
||||
for m in list(methods):
|
||||
if (m.attrib["name"] == name):
|
||||
if m.attrib["name"] == name:
|
||||
description = m.find("description")
|
||||
if (description != None and description.text.strip() != ""):
|
||||
if description != None and description.text.strip() != "":
|
||||
return description.text
|
||||
|
||||
return None
|
||||
@ -70,11 +71,11 @@ def find_method_descr(old_class, name):
|
||||
def find_signal_descr(old_class, name):
|
||||
|
||||
signals = old_class.find("signals")
|
||||
if(signals != None and len(list(signals)) > 0):
|
||||
if signals != None and len(list(signals)) > 0:
|
||||
for m in list(signals):
|
||||
if (m.attrib["name"] == name):
|
||||
if m.attrib["name"] == name:
|
||||
description = m.find("description")
|
||||
if (description != None and description.text.strip() != ""):
|
||||
if description != None and description.text.strip() != "":
|
||||
return description.text
|
||||
|
||||
return None
|
||||
@ -82,13 +83,13 @@ def find_signal_descr(old_class, name):
|
||||
|
||||
def find_constant_descr(old_class, name):
|
||||
|
||||
if (old_class is None):
|
||||
if old_class is None:
|
||||
return None
|
||||
constants = old_class.find("constants")
|
||||
if(constants != None and len(list(constants)) > 0):
|
||||
if constants != None and len(list(constants)) > 0:
|
||||
for m in list(constants):
|
||||
if (m.attrib["name"] == name):
|
||||
if (m.text.strip() != ""):
|
||||
if m.attrib["name"] == name:
|
||||
if m.text.strip() != "":
|
||||
return m.text
|
||||
return None
|
||||
|
||||
@ -96,35 +97,35 @@ def find_constant_descr(old_class, name):
|
||||
def write_class(c):
|
||||
class_name = c.attrib["name"]
|
||||
print("Parsing Class: " + class_name)
|
||||
if (class_name in old_classes):
|
||||
if class_name in old_classes:
|
||||
old_class = old_classes[class_name]
|
||||
else:
|
||||
old_class = None
|
||||
|
||||
category = get_tag(c, "category")
|
||||
inherits = get_tag(c, "inherits")
|
||||
write_string(f, '<class name="' + class_name + '" ' + category + inherits + '>')
|
||||
write_string(f, '<class name="' + class_name + '" ' + category + inherits + ">")
|
||||
inc_tab()
|
||||
|
||||
write_string(f, "<brief_description>")
|
||||
|
||||
if (old_class != None):
|
||||
if old_class != None:
|
||||
old_brief_descr = old_class.find("brief_description")
|
||||
if (old_brief_descr != None):
|
||||
if old_brief_descr != None:
|
||||
write_string(f, escape(old_brief_descr.text.strip()))
|
||||
|
||||
write_string(f, "</brief_description>")
|
||||
|
||||
write_string(f, "<description>")
|
||||
if (old_class != None):
|
||||
if old_class != None:
|
||||
old_descr = old_class.find("description")
|
||||
if (old_descr != None):
|
||||
if old_descr != None:
|
||||
write_string(f, escape(old_descr.text.strip()))
|
||||
|
||||
write_string(f, "</description>")
|
||||
|
||||
methods = c.find("methods")
|
||||
if(methods != None and len(list(methods)) > 0):
|
||||
if methods != None and len(list(methods)) > 0:
|
||||
|
||||
write_string(f, "<methods>")
|
||||
inc_tab()
|
||||
@ -132,35 +133,46 @@ def write_class(c):
|
||||
for m in list(methods):
|
||||
qualifiers = get_tag(m, "qualifiers")
|
||||
|
||||
write_string(f, '<method name="' + escape(m.attrib["name"]) + '" ' + qualifiers + '>')
|
||||
write_string(f, '<method name="' + escape(m.attrib["name"]) + '" ' + qualifiers + ">")
|
||||
inc_tab()
|
||||
|
||||
for a in list(m):
|
||||
if (a.tag == "return"):
|
||||
if a.tag == "return":
|
||||
typ = get_tag(a, "type")
|
||||
write_string(f, '<return' + typ + '>')
|
||||
write_string(f, '</return>')
|
||||
elif (a.tag == "argument"):
|
||||
write_string(f, "<return" + typ + ">")
|
||||
write_string(f, "</return>")
|
||||
elif a.tag == "argument":
|
||||
|
||||
default = get_tag(a, "default")
|
||||
|
||||
write_string(f, '<argument index="' + a.attrib["index"] + '" name="' + escape(a.attrib["name"]) + '" type="' + a.attrib["type"] + '"' + default + '>')
|
||||
write_string(f, '</argument>')
|
||||
write_string(
|
||||
f,
|
||||
'<argument index="'
|
||||
+ a.attrib["index"]
|
||||
+ '" name="'
|
||||
+ escape(a.attrib["name"])
|
||||
+ '" type="'
|
||||
+ a.attrib["type"]
|
||||
+ '"'
|
||||
+ default
|
||||
+ ">",
|
||||
)
|
||||
write_string(f, "</argument>")
|
||||
|
||||
write_string(f, '<description>')
|
||||
if (old_class != None):
|
||||
write_string(f, "<description>")
|
||||
if old_class != None:
|
||||
old_method_descr = find_method_descr(old_class, m.attrib["name"])
|
||||
if (old_method_descr):
|
||||
if old_method_descr:
|
||||
write_string(f, escape(escape(old_method_descr.strip())))
|
||||
|
||||
write_string(f, '</description>')
|
||||
write_string(f, "</description>")
|
||||
dec_tab()
|
||||
write_string(f, "</method>")
|
||||
dec_tab()
|
||||
write_string(f, "</methods>")
|
||||
|
||||
signals = c.find("signals")
|
||||
if(signals != None and len(list(signals)) > 0):
|
||||
if signals != None and len(list(signals)) > 0:
|
||||
|
||||
write_string(f, "<signals>")
|
||||
inc_tab()
|
||||
@ -171,24 +183,33 @@ def write_class(c):
|
||||
inc_tab()
|
||||
|
||||
for a in list(m):
|
||||
if (a.tag == "argument"):
|
||||
if a.tag == "argument":
|
||||
|
||||
write_string(f, '<argument index="' + a.attrib["index"] + '" name="' + escape(a.attrib["name"]) + '" type="' + a.attrib["type"] + '">')
|
||||
write_string(f, '</argument>')
|
||||
write_string(
|
||||
f,
|
||||
'<argument index="'
|
||||
+ a.attrib["index"]
|
||||
+ '" name="'
|
||||
+ escape(a.attrib["name"])
|
||||
+ '" type="'
|
||||
+ a.attrib["type"]
|
||||
+ '">',
|
||||
)
|
||||
write_string(f, "</argument>")
|
||||
|
||||
write_string(f, '<description>')
|
||||
if (old_class != None):
|
||||
write_string(f, "<description>")
|
||||
if old_class != None:
|
||||
old_signal_descr = find_signal_descr(old_class, m.attrib["name"])
|
||||
if (old_signal_descr):
|
||||
if old_signal_descr:
|
||||
write_string(f, escape(old_signal_descr.strip()))
|
||||
write_string(f, '</description>')
|
||||
write_string(f, "</description>")
|
||||
dec_tab()
|
||||
write_string(f, "</signal>")
|
||||
dec_tab()
|
||||
write_string(f, "</signals>")
|
||||
|
||||
constants = c.find("constants")
|
||||
if(constants != None and len(list(constants)) > 0):
|
||||
if constants != None and len(list(constants)) > 0:
|
||||
|
||||
write_string(f, "<constants>")
|
||||
inc_tab()
|
||||
@ -197,7 +218,7 @@ def write_class(c):
|
||||
|
||||
write_string(f, '<constant name="' + escape(m.attrib["name"]) + '" value="' + m.attrib["value"] + '">')
|
||||
old_constant_descr = find_constant_descr(old_class, m.attrib["name"])
|
||||
if (old_constant_descr):
|
||||
if old_constant_descr:
|
||||
write_string(f, escape(old_constant_descr.strip()))
|
||||
write_string(f, "</constant>")
|
||||
|
||||
@ -207,9 +228,10 @@ def write_class(c):
|
||||
dec_tab()
|
||||
write_string(f, "</class>")
|
||||
|
||||
|
||||
for c in list(old_doc):
|
||||
old_classes[c.attrib["name"]] = c
|
||||
|
||||
for c in list(new_doc):
|
||||
write_class(c)
|
||||
write_string(f, '</doc>\n')
|
||||
write_string(f, "</doc>\n")
|
||||
|
||||
@ -13,75 +13,74 @@ import xml.etree.ElementTree as ET
|
||||
################################################################################
|
||||
|
||||
flags = {
|
||||
'c': platform.platform() != 'Windows', # Disable by default on windows, since we use ANSI escape codes
|
||||
'b': False,
|
||||
'g': False,
|
||||
's': False,
|
||||
'u': False,
|
||||
'h': False,
|
||||
'p': False,
|
||||
'o': True,
|
||||
'i': False,
|
||||
'a': True,
|
||||
'e': False,
|
||||
"c": platform.platform() != "Windows", # Disable by default on windows, since we use ANSI escape codes
|
||||
"b": False,
|
||||
"g": False,
|
||||
"s": False,
|
||||
"u": False,
|
||||
"h": False,
|
||||
"p": False,
|
||||
"o": True,
|
||||
"i": False,
|
||||
"a": True,
|
||||
"e": False,
|
||||
}
|
||||
flag_descriptions = {
|
||||
'c': 'Toggle colors when outputting.',
|
||||
'b': 'Toggle showing only not fully described classes.',
|
||||
'g': 'Toggle showing only completed classes.',
|
||||
's': 'Toggle showing comments about the status.',
|
||||
'u': 'Toggle URLs to docs.',
|
||||
'h': 'Show help and exit.',
|
||||
'p': 'Toggle showing percentage as well as counts.',
|
||||
'o': 'Toggle overall column.',
|
||||
'i': 'Toggle collapse of class items columns.',
|
||||
'a': 'Toggle showing all items.',
|
||||
'e': 'Toggle hiding empty items.',
|
||||
"c": "Toggle colors when outputting.",
|
||||
"b": "Toggle showing only not fully described classes.",
|
||||
"g": "Toggle showing only completed classes.",
|
||||
"s": "Toggle showing comments about the status.",
|
||||
"u": "Toggle URLs to docs.",
|
||||
"h": "Show help and exit.",
|
||||
"p": "Toggle showing percentage as well as counts.",
|
||||
"o": "Toggle overall column.",
|
||||
"i": "Toggle collapse of class items columns.",
|
||||
"a": "Toggle showing all items.",
|
||||
"e": "Toggle hiding empty items.",
|
||||
}
|
||||
long_flags = {
|
||||
'colors': 'c',
|
||||
'use-colors': 'c',
|
||||
|
||||
'bad': 'b',
|
||||
'only-bad': 'b',
|
||||
|
||||
'good': 'g',
|
||||
'only-good': 'g',
|
||||
|
||||
'comments': 's',
|
||||
'status': 's',
|
||||
|
||||
'urls': 'u',
|
||||
'gen-url': 'u',
|
||||
|
||||
'help': 'h',
|
||||
|
||||
'percent': 'p',
|
||||
'use-percentages': 'p',
|
||||
|
||||
'overall': 'o',
|
||||
'use-overall': 'o',
|
||||
|
||||
'items': 'i',
|
||||
'collapse': 'i',
|
||||
|
||||
'all': 'a',
|
||||
|
||||
'empty': 'e',
|
||||
"colors": "c",
|
||||
"use-colors": "c",
|
||||
"bad": "b",
|
||||
"only-bad": "b",
|
||||
"good": "g",
|
||||
"only-good": "g",
|
||||
"comments": "s",
|
||||
"status": "s",
|
||||
"urls": "u",
|
||||
"gen-url": "u",
|
||||
"help": "h",
|
||||
"percent": "p",
|
||||
"use-percentages": "p",
|
||||
"overall": "o",
|
||||
"use-overall": "o",
|
||||
"items": "i",
|
||||
"collapse": "i",
|
||||
"all": "a",
|
||||
"empty": "e",
|
||||
}
|
||||
table_columns = ['name', 'brief_description', 'description', 'methods', 'constants', 'members', 'signals', 'theme_items']
|
||||
table_column_names = ['Name', 'Brief Desc.', 'Desc.', 'Methods', 'Constants', 'Members', 'Signals', 'Theme Items']
|
||||
table_columns = [
|
||||
"name",
|
||||
"brief_description",
|
||||
"description",
|
||||
"methods",
|
||||
"constants",
|
||||
"members",
|
||||
"signals",
|
||||
"theme_items",
|
||||
]
|
||||
table_column_names = ["Name", "Brief Desc.", "Desc.", "Methods", "Constants", "Members", "Signals", "Theme Items"]
|
||||
colors = {
|
||||
'name': [36], # cyan
|
||||
'part_big_problem': [4, 31], # underline, red
|
||||
'part_problem': [31], # red
|
||||
'part_mostly_good': [33], # yellow
|
||||
'part_good': [32], # green
|
||||
'url': [4, 34], # underline, blue
|
||||
'section': [1, 4], # bold, underline
|
||||
'state_off': [36], # cyan
|
||||
'state_on': [1, 35], # bold, magenta/plum
|
||||
'bold': [1], # bold
|
||||
"name": [36], # cyan
|
||||
"part_big_problem": [4, 31], # underline, red
|
||||
"part_problem": [31], # red
|
||||
"part_mostly_good": [33], # yellow
|
||||
"part_good": [32], # green
|
||||
"url": [4, 34], # underline, blue
|
||||
"section": [1, 4], # bold, underline
|
||||
"state_off": [36], # cyan
|
||||
"state_on": [1, 35], # bold, magenta/plum
|
||||
"bold": [1], # bold
|
||||
}
|
||||
overall_progress_description_weigth = 10
|
||||
|
||||
@ -90,6 +89,7 @@ overall_progress_description_weigth = 10
|
||||
# Utils #
|
||||
################################################################################
|
||||
|
||||
|
||||
def validate_tag(elem, tag):
|
||||
if elem.tag != tag:
|
||||
print('Tag mismatch, expected "' + tag + '", got ' + elem.tag)
|
||||
@ -97,36 +97,38 @@ def validate_tag(elem, tag):
|
||||
|
||||
|
||||
def color(color, string):
|
||||
if flags['c'] and terminal_supports_color():
|
||||
color_format = ''
|
||||
if flags["c"] and terminal_supports_color():
|
||||
color_format = ""
|
||||
for code in colors[color]:
|
||||
color_format += '\033[' + str(code) + 'm'
|
||||
return color_format + string + '\033[0m'
|
||||
color_format += "\033[" + str(code) + "m"
|
||||
return color_format + string + "\033[0m"
|
||||
else:
|
||||
return string
|
||||
|
||||
ansi_escape = re.compile(r'\x1b[^m]*m')
|
||||
|
||||
ansi_escape = re.compile(r"\x1b[^m]*m")
|
||||
|
||||
|
||||
def nonescape_len(s):
|
||||
return len(ansi_escape.sub('', s))
|
||||
return len(ansi_escape.sub("", s))
|
||||
|
||||
|
||||
def terminal_supports_color():
|
||||
p = sys.platform
|
||||
supported_platform = p != 'Pocket PC' and (p != 'win32' or
|
||||
'ANSICON' in os.environ)
|
||||
supported_platform = p != "Pocket PC" and (p != "win32" or "ANSICON" in os.environ)
|
||||
|
||||
is_a_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()
|
||||
is_a_tty = hasattr(sys.stdout, "isatty") and sys.stdout.isatty()
|
||||
if not supported_platform or not is_a_tty:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
################################################################################
|
||||
# Classes #
|
||||
################################################################################
|
||||
|
||||
class ClassStatusProgress:
|
||||
|
||||
class ClassStatusProgress:
|
||||
def __init__(self, described=0, total=0):
|
||||
self.described = described
|
||||
self.total = total
|
||||
@ -143,42 +145,41 @@ class ClassStatusProgress:
|
||||
return self.described >= self.total
|
||||
|
||||
def to_configured_colored_string(self):
|
||||
if flags['p']:
|
||||
return self.to_colored_string('{percent}% ({has}/{total})', '{pad_percent}{pad_described}{s}{pad_total}')
|
||||
if flags["p"]:
|
||||
return self.to_colored_string("{percent}% ({has}/{total})", "{pad_percent}{pad_described}{s}{pad_total}")
|
||||
else:
|
||||
return self.to_colored_string()
|
||||
|
||||
def to_colored_string(self, format='{has}/{total}', pad_format='{pad_described}{s}{pad_total}'):
|
||||
def to_colored_string(self, format="{has}/{total}", pad_format="{pad_described}{s}{pad_total}"):
|
||||
ratio = float(self.described) / float(self.total) if self.total != 0 else 1
|
||||
percent = int(round(100 * ratio))
|
||||
s = format.format(has=str(self.described), total=str(self.total), percent=str(percent))
|
||||
if self.described >= self.total:
|
||||
s = color('part_good', s)
|
||||
s = color("part_good", s)
|
||||
elif self.described >= self.total / 4 * 3:
|
||||
s = color('part_mostly_good', s)
|
||||
s = color("part_mostly_good", s)
|
||||
elif self.described > 0:
|
||||
s = color('part_problem', s)
|
||||
s = color("part_problem", s)
|
||||
else:
|
||||
s = color('part_big_problem', s)
|
||||
s = color("part_big_problem", s)
|
||||
pad_size = max(len(str(self.described)), len(str(self.total)))
|
||||
pad_described = ''.ljust(pad_size - len(str(self.described)))
|
||||
pad_percent = ''.ljust(3 - len(str(percent)))
|
||||
pad_total = ''.ljust(pad_size - len(str(self.total)))
|
||||
pad_described = "".ljust(pad_size - len(str(self.described)))
|
||||
pad_percent = "".ljust(3 - len(str(percent)))
|
||||
pad_total = "".ljust(pad_size - len(str(self.total)))
|
||||
return pad_format.format(pad_described=pad_described, pad_total=pad_total, pad_percent=pad_percent, s=s)
|
||||
|
||||
|
||||
class ClassStatus:
|
||||
|
||||
def __init__(self, name=''):
|
||||
def __init__(self, name=""):
|
||||
self.name = name
|
||||
self.has_brief_description = True
|
||||
self.has_description = True
|
||||
self.progresses = {
|
||||
'methods': ClassStatusProgress(),
|
||||
'constants': ClassStatusProgress(),
|
||||
'members': ClassStatusProgress(),
|
||||
'theme_items': ClassStatusProgress(),
|
||||
'signals': ClassStatusProgress()
|
||||
"methods": ClassStatusProgress(),
|
||||
"constants": ClassStatusProgress(),
|
||||
"members": ClassStatusProgress(),
|
||||
"theme_items": ClassStatusProgress(),
|
||||
"signals": ClassStatusProgress(),
|
||||
}
|
||||
|
||||
def __add__(self, other):
|
||||
@ -208,66 +209,70 @@ class ClassStatus:
|
||||
|
||||
def make_output(self):
|
||||
output = {}
|
||||
output['name'] = color('name', self.name)
|
||||
output["name"] = color("name", self.name)
|
||||
|
||||
ok_string = color('part_good', 'OK')
|
||||
missing_string = color('part_big_problem', 'MISSING')
|
||||
ok_string = color("part_good", "OK")
|
||||
missing_string = color("part_big_problem", "MISSING")
|
||||
|
||||
output['brief_description'] = ok_string if self.has_brief_description else missing_string
|
||||
output['description'] = ok_string if self.has_description else missing_string
|
||||
output["brief_description"] = ok_string if self.has_brief_description else missing_string
|
||||
output["description"] = ok_string if self.has_description else missing_string
|
||||
|
||||
description_progress = ClassStatusProgress(
|
||||
(self.has_brief_description + self.has_description) * overall_progress_description_weigth,
|
||||
2 * overall_progress_description_weigth
|
||||
2 * overall_progress_description_weigth,
|
||||
)
|
||||
items_progress = ClassStatusProgress()
|
||||
|
||||
for k in ['methods', 'constants', 'members', 'signals', 'theme_items']:
|
||||
for k in ["methods", "constants", "members", "signals", "theme_items"]:
|
||||
items_progress += self.progresses[k]
|
||||
output[k] = self.progresses[k].to_configured_colored_string()
|
||||
|
||||
output['items'] = items_progress.to_configured_colored_string()
|
||||
output["items"] = items_progress.to_configured_colored_string()
|
||||
|
||||
output['overall'] = (description_progress + items_progress).to_colored_string(color('bold', '{percent}%'), '{pad_percent}{s}')
|
||||
output["overall"] = (description_progress + items_progress).to_colored_string(
|
||||
color("bold", "{percent}%"), "{pad_percent}{s}"
|
||||
)
|
||||
|
||||
if self.name.startswith('Total'):
|
||||
output['url'] = color('url', 'https://docs.godotengine.org/en/latest/classes/')
|
||||
if flags['s']:
|
||||
output['comment'] = color('part_good', 'ALL OK')
|
||||
if self.name.startswith("Total"):
|
||||
output["url"] = color("url", "https://docs.godotengine.org/en/latest/classes/")
|
||||
if flags["s"]:
|
||||
output["comment"] = color("part_good", "ALL OK")
|
||||
else:
|
||||
output['url'] = color('url', 'https://docs.godotengine.org/en/latest/classes/class_{name}.html'.format(name=self.name.lower()))
|
||||
output["url"] = color(
|
||||
"url", "https://docs.godotengine.org/en/latest/classes/class_{name}.html".format(name=self.name.lower())
|
||||
)
|
||||
|
||||
if flags['s'] and not flags['g'] and self.is_ok():
|
||||
output['comment'] = color('part_good', 'ALL OK')
|
||||
if flags["s"] and not flags["g"] and self.is_ok():
|
||||
output["comment"] = color("part_good", "ALL OK")
|
||||
|
||||
return output
|
||||
|
||||
@staticmethod
|
||||
def generate_for_class(c):
|
||||
status = ClassStatus()
|
||||
status.name = c.attrib['name']
|
||||
status.name = c.attrib["name"]
|
||||
|
||||
for tag in list(c):
|
||||
|
||||
if tag.tag == 'brief_description':
|
||||
if tag.tag == "brief_description":
|
||||
status.has_brief_description = len(tag.text.strip()) > 0
|
||||
|
||||
elif tag.tag == 'description':
|
||||
elif tag.tag == "description":
|
||||
status.has_description = len(tag.text.strip()) > 0
|
||||
|
||||
elif tag.tag in ['methods', 'signals']:
|
||||
elif tag.tag in ["methods", "signals"]:
|
||||
for sub_tag in list(tag):
|
||||
descr = sub_tag.find('description')
|
||||
descr = sub_tag.find("description")
|
||||
status.progresses[tag.tag].increment(len(descr.text.strip()) > 0)
|
||||
elif tag.tag in ['constants', 'members', 'theme_items']:
|
||||
elif tag.tag in ["constants", "members", "theme_items"]:
|
||||
for sub_tag in list(tag):
|
||||
if not sub_tag.text is None:
|
||||
status.progresses[tag.tag].increment(len(sub_tag.text.strip()) > 0)
|
||||
|
||||
elif tag.tag in ['tutorials']:
|
||||
elif tag.tag in ["tutorials"]:
|
||||
pass # Ignore those tags for now
|
||||
|
||||
elif tag.tag in ['theme_items']:
|
||||
elif tag.tag in ["theme_items"]:
|
||||
pass # Ignore those tags, since they seem to lack description at all
|
||||
|
||||
else:
|
||||
@ -286,63 +291,69 @@ merged_file = ""
|
||||
|
||||
for arg in sys.argv[1:]:
|
||||
try:
|
||||
if arg.startswith('--'):
|
||||
if arg.startswith("--"):
|
||||
flags[long_flags[arg[2:]]] = not flags[long_flags[arg[2:]]]
|
||||
elif arg.startswith('-'):
|
||||
elif arg.startswith("-"):
|
||||
for f in arg[1:]:
|
||||
flags[f] = not flags[f]
|
||||
elif os.path.isdir(arg):
|
||||
for f in os.listdir(arg):
|
||||
if f.endswith('.xml'):
|
||||
input_file_list.append(os.path.join(arg, f));
|
||||
if f.endswith(".xml"):
|
||||
input_file_list.append(os.path.join(arg, f))
|
||||
else:
|
||||
input_class_list.append(arg)
|
||||
except KeyError:
|
||||
print("Unknown command line flag: " + arg)
|
||||
sys.exit(1)
|
||||
|
||||
if flags['i']:
|
||||
for r in ['methods', 'constants', 'members', 'signals', 'theme_items']:
|
||||
if flags["i"]:
|
||||
for r in ["methods", "constants", "members", "signals", "theme_items"]:
|
||||
index = table_columns.index(r)
|
||||
del table_column_names[index]
|
||||
del table_columns[index]
|
||||
table_column_names.append('Items')
|
||||
table_columns.append('items')
|
||||
table_column_names.append("Items")
|
||||
table_columns.append("items")
|
||||
|
||||
if flags['o'] == (not flags['i']):
|
||||
table_column_names.append(color('bold', 'Overall'))
|
||||
table_columns.append('overall')
|
||||
if flags["o"] == (not flags["i"]):
|
||||
table_column_names.append(color("bold", "Overall"))
|
||||
table_columns.append("overall")
|
||||
|
||||
if flags['u']:
|
||||
table_column_names.append('Docs URL')
|
||||
table_columns.append('url')
|
||||
if flags["u"]:
|
||||
table_column_names.append("Docs URL")
|
||||
table_columns.append("url")
|
||||
|
||||
|
||||
################################################################################
|
||||
# Help #
|
||||
################################################################################
|
||||
|
||||
if len(input_file_list) < 1 or flags['h']:
|
||||
if not flags['h']:
|
||||
print(color('section', 'Invalid usage') + ': Please specify a classes directory')
|
||||
print(color('section', 'Usage') + ': doc_status.py [flags] <classes_dir> [class names]')
|
||||
print('\t< and > signify required parameters, while [ and ] signify optional parameters.')
|
||||
print(color('section', 'Available flags') + ':')
|
||||
if len(input_file_list) < 1 or flags["h"]:
|
||||
if not flags["h"]:
|
||||
print(color("section", "Invalid usage") + ": Please specify a classes directory")
|
||||
print(color("section", "Usage") + ": doc_status.py [flags] <classes_dir> [class names]")
|
||||
print("\t< and > signify required parameters, while [ and ] signify optional parameters.")
|
||||
print(color("section", "Available flags") + ":")
|
||||
possible_synonym_list = list(long_flags)
|
||||
possible_synonym_list.sort()
|
||||
flag_list = list(flags)
|
||||
flag_list.sort()
|
||||
for flag in flag_list:
|
||||
synonyms = [color('name', '-' + flag)]
|
||||
synonyms = [color("name", "-" + flag)]
|
||||
for synonym in possible_synonym_list:
|
||||
if long_flags[synonym] == flag:
|
||||
synonyms.append(color('name', '--' + synonym))
|
||||
synonyms.append(color("name", "--" + synonym))
|
||||
|
||||
print(('{synonyms} (Currently ' + color('state_' + ('on' if flags[flag] else 'off'), '{value}') + ')\n\t{description}').format(
|
||||
synonyms=', '.join(synonyms),
|
||||
value=('on' if flags[flag] else 'off'),
|
||||
description=flag_descriptions[flag]
|
||||
))
|
||||
print(
|
||||
(
|
||||
"{synonyms} (Currently "
|
||||
+ color("state_" + ("on" if flags[flag] else "off"), "{value}")
|
||||
+ ")\n\t{description}"
|
||||
).format(
|
||||
synonyms=", ".join(synonyms),
|
||||
value=("on" if flags[flag] else "off"),
|
||||
description=flag_descriptions[flag],
|
||||
)
|
||||
)
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
@ -357,21 +368,21 @@ for file in input_file_list:
|
||||
tree = ET.parse(file)
|
||||
doc = tree.getroot()
|
||||
|
||||
if 'version' not in doc.attrib:
|
||||
if "version" not in doc.attrib:
|
||||
print('Version missing from "doc"')
|
||||
sys.exit(255)
|
||||
|
||||
version = doc.attrib['version']
|
||||
version = doc.attrib["version"]
|
||||
|
||||
if doc.attrib['name'] in class_names:
|
||||
if doc.attrib["name"] in class_names:
|
||||
continue
|
||||
class_names.append(doc.attrib['name'])
|
||||
classes[doc.attrib['name']] = doc
|
||||
class_names.append(doc.attrib["name"])
|
||||
classes[doc.attrib["name"]] = doc
|
||||
|
||||
class_names.sort()
|
||||
|
||||
if len(input_class_list) < 1:
|
||||
input_class_list = ['*']
|
||||
input_class_list = ["*"]
|
||||
|
||||
filtered_classes = set()
|
||||
for pattern in input_class_list:
|
||||
@ -384,23 +395,23 @@ filtered_classes.sort()
|
||||
################################################################################
|
||||
|
||||
table = [table_column_names]
|
||||
table_row_chars = '| - '
|
||||
table_column_chars = '|'
|
||||
table_row_chars = "| - "
|
||||
table_column_chars = "|"
|
||||
|
||||
total_status = ClassStatus('Total')
|
||||
total_status = ClassStatus("Total")
|
||||
|
||||
for cn in filtered_classes:
|
||||
|
||||
c = classes[cn]
|
||||
validate_tag(c, 'class')
|
||||
validate_tag(c, "class")
|
||||
status = ClassStatus.generate_for_class(c)
|
||||
|
||||
total_status = total_status + status
|
||||
|
||||
if (flags['b'] and status.is_ok()) or (flags['g'] and not status.is_ok()) or (not flags['a']):
|
||||
if (flags["b"] and status.is_ok()) or (flags["g"] and not status.is_ok()) or (not flags["a"]):
|
||||
continue
|
||||
|
||||
if flags['e'] and status.is_empty():
|
||||
if flags["e"] and status.is_empty():
|
||||
continue
|
||||
|
||||
out = status.make_output()
|
||||
@ -409,10 +420,10 @@ for cn in filtered_classes:
|
||||
if column in out:
|
||||
row.append(out[column])
|
||||
else:
|
||||
row.append('')
|
||||
row.append("")
|
||||
|
||||
if 'comment' in out and out['comment'] != '':
|
||||
row.append(out['comment'])
|
||||
if "comment" in out and out["comment"] != "":
|
||||
row.append(out["comment"])
|
||||
|
||||
table.append(row)
|
||||
|
||||
@ -421,22 +432,22 @@ for cn in filtered_classes:
|
||||
# Print output table #
|
||||
################################################################################
|
||||
|
||||
if len(table) == 1 and flags['a']:
|
||||
print(color('part_big_problem', 'No classes suitable for printing!'))
|
||||
if len(table) == 1 and flags["a"]:
|
||||
print(color("part_big_problem", "No classes suitable for printing!"))
|
||||
sys.exit(0)
|
||||
|
||||
if len(table) > 2 or not flags['a']:
|
||||
total_status.name = 'Total = {0}'.format(len(table) - 1)
|
||||
if len(table) > 2 or not flags["a"]:
|
||||
total_status.name = "Total = {0}".format(len(table) - 1)
|
||||
out = total_status.make_output()
|
||||
row = []
|
||||
for column in table_columns:
|
||||
if column in out:
|
||||
row.append(out[column])
|
||||
else:
|
||||
row.append('')
|
||||
row.append("")
|
||||
table.append(row)
|
||||
|
||||
if flags['a']:
|
||||
if flags["a"]:
|
||||
# Duplicate the headers at the bottom of the table so they can be viewed
|
||||
# without having to scroll back to the top.
|
||||
table.append(table_column_names)
|
||||
@ -451,7 +462,9 @@ for row in table:
|
||||
|
||||
divider_string = table_row_chars[0]
|
||||
for cell_i in range(len(table[0])):
|
||||
divider_string += table_row_chars[1] + table_row_chars[2] * (table_column_sizes[cell_i]) + table_row_chars[1] + table_row_chars[0]
|
||||
divider_string += (
|
||||
table_row_chars[1] + table_row_chars[2] * (table_column_sizes[cell_i]) + table_row_chars[1] + table_row_chars[0]
|
||||
)
|
||||
print(divider_string)
|
||||
|
||||
for row_i, row in enumerate(table):
|
||||
@ -461,7 +474,11 @@ for row_i, row in enumerate(table):
|
||||
if cell_i == 0:
|
||||
row_string += table_row_chars[3] + cell + table_row_chars[3] * (padding_needed - 1)
|
||||
else:
|
||||
row_string += table_row_chars[3] * int(math.floor(float(padding_needed) / 2)) + cell + table_row_chars[3] * int(math.ceil(float(padding_needed) / 2))
|
||||
row_string += (
|
||||
table_row_chars[3] * int(math.floor(float(padding_needed) / 2))
|
||||
+ cell
|
||||
+ table_row_chars[3] * int(math.ceil(float(padding_needed) / 2))
|
||||
)
|
||||
row_string += table_column_chars
|
||||
|
||||
print(row_string)
|
||||
@ -474,5 +491,5 @@ for row_i, row in enumerate(table):
|
||||
|
||||
print(divider_string)
|
||||
|
||||
if total_status.is_ok() and not flags['g']:
|
||||
print('All listed classes are ' + color('part_good', 'OK') + '!')
|
||||
if total_status.is_ok() and not flags["g"]:
|
||||
print("All listed classes are " + color("part_good", "OK") + "!")
|
||||
|
||||
@ -7,10 +7,12 @@ import xml.etree.ElementTree as ET
|
||||
from collections import OrderedDict
|
||||
|
||||
# Uncomment to do type checks. I have it commented out so it works below Python 3.5
|
||||
#from typing import List, Dict, TextIO, Tuple, Iterable, Optional, DefaultDict, Any, Union
|
||||
# from typing import List, Dict, TextIO, Tuple, Iterable, Optional, DefaultDict, Any, Union
|
||||
|
||||
# http(s)://docs.godotengine.org/<langcode>/<tag>/path/to/page.html(#fragment-tag)
|
||||
GODOT_DOCS_PATTERN = re.compile(r'^http(?:s)?://docs\.godotengine\.org/(?:[a-zA-Z0-9.\-_]*)/(?:[a-zA-Z0-9.\-_]*)/(.*)\.html(#.*)?$')
|
||||
GODOT_DOCS_PATTERN = re.compile(
|
||||
r"^http(?:s)?://docs\.godotengine\.org/(?:[a-zA-Z0-9.\-_]*)/(?:[a-zA-Z0-9.\-_]*)/(.*)\.html(#.*)?$"
|
||||
)
|
||||
|
||||
|
||||
def print_error(error, state): # type: (str, State) -> None
|
||||
@ -37,7 +39,9 @@ class TypeName:
|
||||
|
||||
|
||||
class PropertyDef:
|
||||
def __init__(self, name, type_name, setter, getter, text, default_value, overridden): # type: (str, TypeName, Optional[str], Optional[str], Optional[str], Optional[str], Optional[bool]) -> None
|
||||
def __init__(
|
||||
self, name, type_name, setter, getter, text, default_value, overridden
|
||||
): # type: (str, TypeName, Optional[str], Optional[str], Optional[str], Optional[str], Optional[bool]) -> None
|
||||
self.name = name
|
||||
self.type_name = type_name
|
||||
self.setter = setter
|
||||
@ -46,6 +50,7 @@ class PropertyDef:
|
||||
self.default_value = default_value
|
||||
self.overridden = overridden
|
||||
|
||||
|
||||
class ParameterDef:
|
||||
def __init__(self, name, type_name, default_value): # type: (str, TypeName, Optional[str]) -> None
|
||||
self.name = name
|
||||
@ -61,7 +66,9 @@ class SignalDef:
|
||||
|
||||
|
||||
class MethodDef:
|
||||
def __init__(self, name, return_type, parameters, description, qualifiers): # type: (str, TypeName, List[ParameterDef], Optional[str], Optional[str]) -> None
|
||||
def __init__(
|
||||
self, name, return_type, parameters, description, qualifiers
|
||||
): # type: (str, TypeName, List[ParameterDef], Optional[str], Optional[str]) -> None
|
||||
self.name = name
|
||||
self.return_type = return_type
|
||||
self.parameters = parameters
|
||||
@ -144,10 +151,12 @@ class State:
|
||||
getter = property.get("getter") or None
|
||||
default_value = property.get("default") or None
|
||||
if default_value is not None:
|
||||
default_value = '``{}``'.format(default_value)
|
||||
default_value = "``{}``".format(default_value)
|
||||
overridden = property.get("override") or False
|
||||
|
||||
property_def = PropertyDef(property_name, type_name, setter, getter, property.text, default_value, overridden)
|
||||
property_def = PropertyDef(
|
||||
property_name, type_name, setter, getter, property.text, default_value, overridden
|
||||
)
|
||||
class_def.properties[property_name] = property_def
|
||||
|
||||
methods = class_root.find("methods")
|
||||
@ -246,8 +255,6 @@ class State:
|
||||
if link.text is not None:
|
||||
class_def.tutorials.append(link.text)
|
||||
|
||||
|
||||
|
||||
def sort_classes(self): # type: () -> None
|
||||
self.classes = OrderedDict(sorted(self.classes.items(), key=lambda t: t[0]))
|
||||
|
||||
@ -273,7 +280,11 @@ def main(): # type: () -> None
|
||||
parser.add_argument("path", nargs="+", help="A path to an XML file or a directory containing XML files to parse.")
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument("--output", "-o", default=".", help="The directory to save output .rst files in.")
|
||||
group.add_argument("--dry-run", action="store_true", help="If passed, no output will be generated and XML files are only checked for errors.")
|
||||
group.add_argument(
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="If passed, no output will be generated and XML files are only checked for errors.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
print("Checking for errors in the XML class reference...")
|
||||
@ -285,15 +296,15 @@ def main(): # type: () -> None
|
||||
if path.endswith(os.sep):
|
||||
path = path[:-1]
|
||||
|
||||
if os.path.basename(path) == 'modules':
|
||||
if os.path.basename(path) == "modules":
|
||||
for subdir, dirs, _ in os.walk(path):
|
||||
if 'doc_classes' in dirs:
|
||||
doc_dir = os.path.join(subdir, 'doc_classes')
|
||||
class_file_names = (f for f in os.listdir(doc_dir) if f.endswith('.xml'))
|
||||
if "doc_classes" in dirs:
|
||||
doc_dir = os.path.join(subdir, "doc_classes")
|
||||
class_file_names = (f for f in os.listdir(doc_dir) if f.endswith(".xml"))
|
||||
file_list += (os.path.join(doc_dir, f) for f in class_file_names)
|
||||
|
||||
elif os.path.isdir(path):
|
||||
file_list += (os.path.join(path, f) for f in os.listdir(path) if f.endswith('.xml'))
|
||||
file_list += (os.path.join(path, f) for f in os.listdir(path) if f.endswith(".xml"))
|
||||
|
||||
elif os.path.isfile(path):
|
||||
if not path.endswith(".xml"):
|
||||
@ -313,7 +324,7 @@ def main(): # type: () -> None
|
||||
continue
|
||||
doc = tree.getroot()
|
||||
|
||||
if 'version' not in doc.attrib:
|
||||
if "version" not in doc.attrib:
|
||||
print_error("Version missing from 'doc', file: {}".format(cur_file), state)
|
||||
continue
|
||||
|
||||
@ -342,13 +353,14 @@ def main(): # type: () -> None
|
||||
print("Errors were found in the class reference XML. Please check the messages above.")
|
||||
exit(1)
|
||||
|
||||
|
||||
def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, State, bool, str) -> None
|
||||
class_name = class_def.name
|
||||
|
||||
if dry_run:
|
||||
f = open(os.devnull, "w", encoding="utf-8")
|
||||
else:
|
||||
f = open(os.path.join(output_dir, "class_" + class_name.lower() + '.rst'), 'w', encoding='utf-8')
|
||||
f = open(os.path.join(output_dir, "class_" + class_name.lower() + ".rst"), "w", encoding="utf-8")
|
||||
|
||||
# Warn contributors not to edit this file directly
|
||||
f.write(":github_url: hide\n\n")
|
||||
@ -357,13 +369,13 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S
|
||||
f.write(".. The source is found in doc/classes or modules/<name>/doc_classes.\n\n")
|
||||
|
||||
f.write(".. _class_" + class_name + ":\n\n")
|
||||
f.write(make_heading(class_name, '='))
|
||||
f.write(make_heading(class_name, "="))
|
||||
|
||||
# Inheritance tree
|
||||
# Ascendants
|
||||
if class_def.inherits:
|
||||
inh = class_def.inherits.strip()
|
||||
f.write('**Inherits:** ')
|
||||
f.write("**Inherits:** ")
|
||||
first = True
|
||||
while inh in state.classes:
|
||||
if not first:
|
||||
@ -386,7 +398,7 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S
|
||||
inherited.append(c.name)
|
||||
|
||||
if len(inherited):
|
||||
f.write('**Inherited By:** ')
|
||||
f.write("**Inherited By:** ")
|
||||
for i, child in enumerate(inherited):
|
||||
if i > 0:
|
||||
f.write(", ")
|
||||
@ -398,20 +410,20 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S
|
||||
f.write(rstize_text(class_def.brief_description.strip(), state) + "\n\n")
|
||||
|
||||
# Class description
|
||||
if class_def.description is not None and class_def.description.strip() != '':
|
||||
f.write(make_heading('Description', '-'))
|
||||
if class_def.description is not None and class_def.description.strip() != "":
|
||||
f.write(make_heading("Description", "-"))
|
||||
f.write(rstize_text(class_def.description.strip(), state) + "\n\n")
|
||||
|
||||
# Online tutorials
|
||||
if len(class_def.tutorials) > 0:
|
||||
f.write(make_heading('Tutorials', '-'))
|
||||
f.write(make_heading("Tutorials", "-"))
|
||||
for t in class_def.tutorials:
|
||||
link = t.strip()
|
||||
f.write("- " + make_url(link) + "\n\n")
|
||||
|
||||
# Properties overview
|
||||
if len(class_def.properties) > 0:
|
||||
f.write(make_heading('Properties', '-'))
|
||||
f.write(make_heading("Properties", "-"))
|
||||
ml = [] # type: List[Tuple[str, str, str]]
|
||||
for property_def in class_def.properties.values():
|
||||
type_rst = property_def.type_name.to_rst(state)
|
||||
@ -425,7 +437,7 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S
|
||||
|
||||
# Methods overview
|
||||
if len(class_def.methods) > 0:
|
||||
f.write(make_heading('Methods', '-'))
|
||||
f.write(make_heading("Methods", "-"))
|
||||
ml = []
|
||||
for method_list in class_def.methods.values():
|
||||
for m in method_list:
|
||||
@ -434,7 +446,7 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S
|
||||
|
||||
# Theme properties
|
||||
if class_def.theme_items is not None and len(class_def.theme_items) > 0:
|
||||
f.write(make_heading('Theme Properties', '-'))
|
||||
f.write(make_heading("Theme Properties", "-"))
|
||||
pl = []
|
||||
for theme_item_list in class_def.theme_items.values():
|
||||
for theme_item in theme_item_list:
|
||||
@ -443,30 +455,30 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S
|
||||
|
||||
# Signals
|
||||
if len(class_def.signals) > 0:
|
||||
f.write(make_heading('Signals', '-'))
|
||||
f.write(make_heading("Signals", "-"))
|
||||
index = 0
|
||||
|
||||
for signal in class_def.signals.values():
|
||||
if index != 0:
|
||||
f.write('----\n\n')
|
||||
f.write("----\n\n")
|
||||
|
||||
f.write(".. _class_{}_signal_{}:\n\n".format(class_name, signal.name))
|
||||
_, signature = make_method_signature(class_def, signal, False, state)
|
||||
f.write("- {}\n\n".format(signature))
|
||||
|
||||
if signal.description is not None and signal.description.strip() != '':
|
||||
f.write(rstize_text(signal.description.strip(), state) + '\n\n')
|
||||
if signal.description is not None and signal.description.strip() != "":
|
||||
f.write(rstize_text(signal.description.strip(), state) + "\n\n")
|
||||
|
||||
index += 1
|
||||
|
||||
# Enums
|
||||
if len(class_def.enums) > 0:
|
||||
f.write(make_heading('Enumerations', '-'))
|
||||
f.write(make_heading("Enumerations", "-"))
|
||||
index = 0
|
||||
|
||||
for e in class_def.enums.values():
|
||||
if index != 0:
|
||||
f.write('----\n\n')
|
||||
f.write("----\n\n")
|
||||
|
||||
f.write(".. _enum_{}_{}:\n\n".format(class_name, e.name))
|
||||
# Sphinx seems to divide the bullet list into individual <ul> tags if we weave the labels into it.
|
||||
@ -479,16 +491,16 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S
|
||||
f.write("enum **{}**:\n\n".format(e.name))
|
||||
for value in e.values.values():
|
||||
f.write("- **{}** = **{}**".format(value.name, value.value))
|
||||
if value.text is not None and value.text.strip() != '':
|
||||
f.write(' --- ' + rstize_text(value.text.strip(), state))
|
||||
if value.text is not None and value.text.strip() != "":
|
||||
f.write(" --- " + rstize_text(value.text.strip(), state))
|
||||
|
||||
f.write('\n\n')
|
||||
f.write("\n\n")
|
||||
|
||||
index += 1
|
||||
|
||||
# Constants
|
||||
if len(class_def.constants) > 0:
|
||||
f.write(make_heading('Constants', '-'))
|
||||
f.write(make_heading("Constants", "-"))
|
||||
# Sphinx seems to divide the bullet list into individual <ul> tags if we weave the labels into it.
|
||||
# As such I'll put them all above the list. Won't be perfect but better than making the list visually broken.
|
||||
for constant in class_def.constants.values():
|
||||
@ -496,14 +508,14 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S
|
||||
|
||||
for constant in class_def.constants.values():
|
||||
f.write("- **{}** = **{}**".format(constant.name, constant.value))
|
||||
if constant.text is not None and constant.text.strip() != '':
|
||||
f.write(' --- ' + rstize_text(constant.text.strip(), state))
|
||||
if constant.text is not None and constant.text.strip() != "":
|
||||
f.write(" --- " + rstize_text(constant.text.strip(), state))
|
||||
|
||||
f.write('\n\n')
|
||||
f.write("\n\n")
|
||||
|
||||
# Property descriptions
|
||||
if any(not p.overridden for p in class_def.properties.values()) > 0:
|
||||
f.write(make_heading('Property Descriptions', '-'))
|
||||
f.write(make_heading("Property Descriptions", "-"))
|
||||
index = 0
|
||||
|
||||
for property_def in class_def.properties.values():
|
||||
@ -511,36 +523,36 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S
|
||||
continue
|
||||
|
||||
if index != 0:
|
||||
f.write('----\n\n')
|
||||
f.write("----\n\n")
|
||||
|
||||
f.write(".. _class_{}_property_{}:\n\n".format(class_name, property_def.name))
|
||||
f.write('- {} **{}**\n\n'.format(property_def.type_name.to_rst(state), property_def.name))
|
||||
f.write("- {} **{}**\n\n".format(property_def.type_name.to_rst(state), property_def.name))
|
||||
|
||||
info = []
|
||||
if property_def.default_value is not None:
|
||||
info.append(("*Default*", property_def.default_value))
|
||||
if property_def.setter is not None and not property_def.setter.startswith("_"):
|
||||
info.append(("*Setter*", property_def.setter + '(value)'))
|
||||
info.append(("*Setter*", property_def.setter + "(value)"))
|
||||
if property_def.getter is not None and not property_def.getter.startswith("_"):
|
||||
info.append(('*Getter*', property_def.getter + '()'))
|
||||
info.append(("*Getter*", property_def.getter + "()"))
|
||||
|
||||
if len(info) > 0:
|
||||
format_table(f, info)
|
||||
|
||||
if property_def.text is not None and property_def.text.strip() != '':
|
||||
f.write(rstize_text(property_def.text.strip(), state) + '\n\n')
|
||||
if property_def.text is not None and property_def.text.strip() != "":
|
||||
f.write(rstize_text(property_def.text.strip(), state) + "\n\n")
|
||||
|
||||
index += 1
|
||||
|
||||
# Method descriptions
|
||||
if len(class_def.methods) > 0:
|
||||
f.write(make_heading('Method Descriptions', '-'))
|
||||
f.write(make_heading("Method Descriptions", "-"))
|
||||
index = 0
|
||||
|
||||
for method_list in class_def.methods.values():
|
||||
for i, m in enumerate(method_list):
|
||||
if index != 0:
|
||||
f.write('----\n\n')
|
||||
f.write("----\n\n")
|
||||
|
||||
if i == 0:
|
||||
f.write(".. _class_{}_method_{}:\n\n".format(class_name, m.name))
|
||||
@ -548,8 +560,8 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S
|
||||
ret_type, signature = make_method_signature(class_def, m, False, state)
|
||||
f.write("- {} {}\n\n".format(ret_type, signature))
|
||||
|
||||
if m.description is not None and m.description.strip() != '':
|
||||
f.write(rstize_text(m.description.strip(), state) + '\n\n')
|
||||
if m.description is not None and m.description.strip() != "":
|
||||
f.write(rstize_text(m.description.strip(), state) + "\n\n")
|
||||
|
||||
index += 1
|
||||
|
||||
@ -558,29 +570,29 @@ def escape_rst(text, until_pos=-1): # type: (str) -> str
|
||||
# Escape \ character, otherwise it ends up as an escape character in rst
|
||||
pos = 0
|
||||
while True:
|
||||
pos = text.find('\\', pos, until_pos)
|
||||
pos = text.find("\\", pos, until_pos)
|
||||
if pos == -1:
|
||||
break
|
||||
text = text[:pos] + "\\\\" + text[pos + 1:]
|
||||
text = text[:pos] + "\\\\" + text[pos + 1 :]
|
||||
pos += 2
|
||||
|
||||
# Escape * character to avoid interpreting it as emphasis
|
||||
pos = 0
|
||||
while True:
|
||||
pos = text.find('*', pos, until_pos)
|
||||
pos = text.find("*", pos, until_pos)
|
||||
if pos == -1:
|
||||
break
|
||||
text = text[:pos] + "\*" + text[pos + 1:]
|
||||
text = text[:pos] + "\*" + text[pos + 1 :]
|
||||
pos += 2
|
||||
|
||||
# Escape _ character at the end of a word to avoid interpreting it as an inline hyperlink
|
||||
pos = 0
|
||||
while True:
|
||||
pos = text.find('_', pos, until_pos)
|
||||
pos = text.find("_", pos, until_pos)
|
||||
if pos == -1:
|
||||
break
|
||||
if not text[pos + 1].isalnum(): # don't escape within a snake_case word
|
||||
text = text[:pos] + "\_" + text[pos + 1:]
|
||||
text = text[:pos] + "\_" + text[pos + 1 :]
|
||||
pos += 2
|
||||
else:
|
||||
pos += 1
|
||||
@ -592,16 +604,16 @@ def rstize_text(text, state): # type: (str, State) -> str
|
||||
# Linebreak + tabs in the XML should become two line breaks unless in a "codeblock"
|
||||
pos = 0
|
||||
while True:
|
||||
pos = text.find('\n', pos)
|
||||
pos = text.find("\n", pos)
|
||||
if pos == -1:
|
||||
break
|
||||
|
||||
pre_text = text[:pos]
|
||||
indent_level = 0
|
||||
while text[pos + 1] == '\t':
|
||||
while text[pos + 1] == "\t":
|
||||
pos += 1
|
||||
indent_level += 1
|
||||
post_text = text[pos + 1:]
|
||||
post_text = text[pos + 1 :]
|
||||
|
||||
# Handle codeblocks
|
||||
if post_text.startswith("[codeblock]"):
|
||||
@ -610,28 +622,33 @@ def rstize_text(text, state): # type: (str, State) -> str
|
||||
print_error("[codeblock] without a closing tag, file: {}".format(state.current_class), state)
|
||||
return ""
|
||||
|
||||
code_text = post_text[len("[codeblock]"):end_pos]
|
||||
code_text = post_text[len("[codeblock]") : end_pos]
|
||||
post_text = post_text[end_pos:]
|
||||
|
||||
# Remove extraneous tabs
|
||||
code_pos = 0
|
||||
while True:
|
||||
code_pos = code_text.find('\n', code_pos)
|
||||
code_pos = code_text.find("\n", code_pos)
|
||||
if code_pos == -1:
|
||||
break
|
||||
|
||||
to_skip = 0
|
||||
while code_pos + to_skip + 1 < len(code_text) and code_text[code_pos + to_skip + 1] == '\t':
|
||||
while code_pos + to_skip + 1 < len(code_text) and code_text[code_pos + to_skip + 1] == "\t":
|
||||
to_skip += 1
|
||||
|
||||
if to_skip > indent_level:
|
||||
print_error("Four spaces should be used for indentation within [codeblock], file: {}".format(state.current_class), state)
|
||||
print_error(
|
||||
"Four spaces should be used for indentation within [codeblock], file: {}".format(
|
||||
state.current_class
|
||||
),
|
||||
state,
|
||||
)
|
||||
|
||||
if len(code_text[code_pos + to_skip + 1:]) == 0:
|
||||
if len(code_text[code_pos + to_skip + 1 :]) == 0:
|
||||
code_text = code_text[:code_pos] + "\n"
|
||||
code_pos += 1
|
||||
else:
|
||||
code_text = code_text[:code_pos] + "\n " + code_text[code_pos + to_skip + 1:]
|
||||
code_text = code_text[:code_pos] + "\n " + code_text[code_pos + to_skip + 1 :]
|
||||
code_pos += 5 - to_skip
|
||||
|
||||
text = pre_text + "\n[codeblock]" + code_text + post_text
|
||||
@ -642,7 +659,7 @@ def rstize_text(text, state): # type: (str, State) -> str
|
||||
text = pre_text + "\n\n" + post_text
|
||||
pos += 2
|
||||
|
||||
next_brac_pos = text.find('[')
|
||||
next_brac_pos = text.find("[")
|
||||
text = escape_rst(text, next_brac_pos)
|
||||
|
||||
# Handle [tags]
|
||||
@ -654,54 +671,59 @@ def rstize_text(text, state): # type: (str, State) -> str
|
||||
tag_depth = 0
|
||||
previous_pos = 0
|
||||
while True:
|
||||
pos = text.find('[', pos)
|
||||
pos = text.find("[", pos)
|
||||
if inside_url and (pos > previous_pos):
|
||||
url_has_name = True
|
||||
if pos == -1:
|
||||
break
|
||||
|
||||
endq_pos = text.find(']', pos + 1)
|
||||
endq_pos = text.find("]", pos + 1)
|
||||
if endq_pos == -1:
|
||||
break
|
||||
|
||||
pre_text = text[:pos]
|
||||
post_text = text[endq_pos + 1:]
|
||||
tag_text = text[pos + 1:endq_pos]
|
||||
post_text = text[endq_pos + 1 :]
|
||||
tag_text = text[pos + 1 : endq_pos]
|
||||
|
||||
escape_post = False
|
||||
|
||||
if tag_text in state.classes:
|
||||
if tag_text == state.current_class:
|
||||
# We don't want references to the same class
|
||||
tag_text = '``{}``'.format(tag_text)
|
||||
tag_text = "``{}``".format(tag_text)
|
||||
else:
|
||||
tag_text = make_type(tag_text, state)
|
||||
escape_post = True
|
||||
else: # command
|
||||
cmd = tag_text
|
||||
space_pos = tag_text.find(' ')
|
||||
if cmd == '/codeblock':
|
||||
tag_text = ''
|
||||
space_pos = tag_text.find(" ")
|
||||
if cmd == "/codeblock":
|
||||
tag_text = ""
|
||||
tag_depth -= 1
|
||||
inside_code = False
|
||||
# Strip newline if the tag was alone on one
|
||||
if pre_text[-1] == '\n':
|
||||
if pre_text[-1] == "\n":
|
||||
pre_text = pre_text[:-1]
|
||||
elif cmd == '/code':
|
||||
tag_text = '``'
|
||||
elif cmd == "/code":
|
||||
tag_text = "``"
|
||||
tag_depth -= 1
|
||||
inside_code = False
|
||||
escape_post = True
|
||||
elif inside_code:
|
||||
tag_text = '[' + tag_text + ']'
|
||||
elif cmd.find('html') == 0:
|
||||
param = tag_text[space_pos + 1:]
|
||||
tag_text = "[" + tag_text + "]"
|
||||
elif cmd.find("html") == 0:
|
||||
param = tag_text[space_pos + 1 :]
|
||||
tag_text = param
|
||||
elif cmd.startswith('method') or cmd.startswith('member') or cmd.startswith('signal') or cmd.startswith('constant'):
|
||||
param = tag_text[space_pos + 1:]
|
||||
elif (
|
||||
cmd.startswith("method")
|
||||
or cmd.startswith("member")
|
||||
or cmd.startswith("signal")
|
||||
or cmd.startswith("constant")
|
||||
):
|
||||
param = tag_text[space_pos + 1 :]
|
||||
|
||||
if param.find('.') != -1:
|
||||
ss = param.split('.')
|
||||
if param.find(".") != -1:
|
||||
ss = param.split(".")
|
||||
if len(ss) > 2:
|
||||
print_error("Bad reference: '{}', file: {}".format(param, state.current_class), state)
|
||||
class_param, method_param = ss
|
||||
@ -734,7 +756,7 @@ def rstize_text(text, state): # type: (str, State) -> str
|
||||
# Search in the current class
|
||||
search_class_defs = [class_def]
|
||||
|
||||
if param.find('.') == -1:
|
||||
if param.find(".") == -1:
|
||||
# Also search in @GlobalScope as a last resort if no class was specified
|
||||
search_class_defs.append(state.classes["@GlobalScope"])
|
||||
|
||||
@ -755,66 +777,71 @@ def rstize_text(text, state): # type: (str, State) -> str
|
||||
ref_type = "_constant"
|
||||
|
||||
else:
|
||||
print_error("Unresolved type reference '{}' in method reference '{}', file: {}".format(class_param, param, state.current_class), state)
|
||||
print_error(
|
||||
"Unresolved type reference '{}' in method reference '{}', file: {}".format(
|
||||
class_param, param, state.current_class
|
||||
),
|
||||
state,
|
||||
)
|
||||
|
||||
repl_text = method_param
|
||||
if class_param != state.current_class:
|
||||
repl_text = "{}.{}".format(class_param, method_param)
|
||||
tag_text = ':ref:`{}<class_{}{}_{}>`'.format(repl_text, class_param, ref_type, method_param)
|
||||
tag_text = ":ref:`{}<class_{}{}_{}>`".format(repl_text, class_param, ref_type, method_param)
|
||||
escape_post = True
|
||||
elif cmd.find('image=') == 0:
|
||||
elif cmd.find("image=") == 0:
|
||||
tag_text = "" # ''
|
||||
elif cmd.find('url=') == 0:
|
||||
elif cmd.find("url=") == 0:
|
||||
url_link = cmd[4:]
|
||||
tag_text = '`'
|
||||
tag_text = "`"
|
||||
tag_depth += 1
|
||||
inside_url = True
|
||||
url_has_name = False
|
||||
elif cmd == '/url':
|
||||
tag_text = ('' if url_has_name else url_link) + " <" + url_link + ">`_"
|
||||
elif cmd == "/url":
|
||||
tag_text = ("" if url_has_name else url_link) + " <" + url_link + ">`_"
|
||||
tag_depth -= 1
|
||||
escape_post = True
|
||||
inside_url = False
|
||||
url_has_name = False
|
||||
elif cmd == 'center':
|
||||
elif cmd == "center":
|
||||
tag_depth += 1
|
||||
tag_text = ''
|
||||
elif cmd == '/center':
|
||||
tag_text = ""
|
||||
elif cmd == "/center":
|
||||
tag_depth -= 1
|
||||
tag_text = ''
|
||||
elif cmd == 'codeblock':
|
||||
tag_text = ""
|
||||
elif cmd == "codeblock":
|
||||
tag_depth += 1
|
||||
tag_text = '\n::\n'
|
||||
tag_text = "\n::\n"
|
||||
inside_code = True
|
||||
elif cmd == 'br':
|
||||
elif cmd == "br":
|
||||
# Make a new paragraph instead of a linebreak, rst is not so linebreak friendly
|
||||
tag_text = '\n\n'
|
||||
tag_text = "\n\n"
|
||||
# Strip potential leading spaces
|
||||
while post_text[0] == ' ':
|
||||
while post_text[0] == " ":
|
||||
post_text = post_text[1:]
|
||||
elif cmd == 'i' or cmd == '/i':
|
||||
elif cmd == "i" or cmd == "/i":
|
||||
if cmd == "/i":
|
||||
tag_depth -= 1
|
||||
else:
|
||||
tag_depth += 1
|
||||
tag_text = '*'
|
||||
elif cmd == 'b' or cmd == '/b':
|
||||
tag_text = "*"
|
||||
elif cmd == "b" or cmd == "/b":
|
||||
if cmd == "/b":
|
||||
tag_depth -= 1
|
||||
else:
|
||||
tag_depth += 1
|
||||
tag_text = '**'
|
||||
elif cmd == 'u' or cmd == '/u':
|
||||
tag_text = "**"
|
||||
elif cmd == "u" or cmd == "/u":
|
||||
if cmd == "/u":
|
||||
tag_depth -= 1
|
||||
else:
|
||||
tag_depth += 1
|
||||
tag_text = ''
|
||||
elif cmd == 'code':
|
||||
tag_text = '``'
|
||||
tag_text = ""
|
||||
elif cmd == "code":
|
||||
tag_text = "``"
|
||||
tag_depth += 1
|
||||
inside_code = True
|
||||
elif cmd.startswith('enum '):
|
||||
elif cmd.startswith("enum "):
|
||||
tag_text = make_enum(cmd[5:], state)
|
||||
escape_post = True
|
||||
else:
|
||||
@ -823,24 +850,24 @@ def rstize_text(text, state): # type: (str, State) -> str
|
||||
|
||||
# Properly escape things like `[Node]s`
|
||||
if escape_post and post_text and (post_text[0].isalnum() or post_text[0] == "("): # not punctuation, escape
|
||||
post_text = '\ ' + post_text
|
||||
post_text = "\ " + post_text
|
||||
|
||||
next_brac_pos = post_text.find('[', 0)
|
||||
next_brac_pos = post_text.find("[", 0)
|
||||
iter_pos = 0
|
||||
while not inside_code:
|
||||
iter_pos = post_text.find('*', iter_pos, next_brac_pos)
|
||||
iter_pos = post_text.find("*", iter_pos, next_brac_pos)
|
||||
if iter_pos == -1:
|
||||
break
|
||||
post_text = post_text[:iter_pos] + "\*" + post_text[iter_pos + 1:]
|
||||
post_text = post_text[:iter_pos] + "\*" + post_text[iter_pos + 1 :]
|
||||
iter_pos += 2
|
||||
|
||||
iter_pos = 0
|
||||
while not inside_code:
|
||||
iter_pos = post_text.find('_', iter_pos, next_brac_pos)
|
||||
iter_pos = post_text.find("_", iter_pos, next_brac_pos)
|
||||
if iter_pos == -1:
|
||||
break
|
||||
if not post_text[iter_pos + 1].isalnum(): # don't escape within a snake_case word
|
||||
post_text = post_text[:iter_pos] + "\_" + post_text[iter_pos + 1:]
|
||||
post_text = post_text[:iter_pos] + "\_" + post_text[iter_pos + 1 :]
|
||||
iter_pos += 2
|
||||
else:
|
||||
iter_pos += 1
|
||||
@ -862,7 +889,7 @@ def format_table(f, data, remove_empty_columns=False): # type: (TextIO, Iterabl
|
||||
column_sizes = [0] * len(data[0])
|
||||
for row in data:
|
||||
for i, text in enumerate(row):
|
||||
text_length = len(text or '')
|
||||
text_length = len(text or "")
|
||||
if text_length > column_sizes[i]:
|
||||
column_sizes[i] = text_length
|
||||
|
||||
@ -879,16 +906,16 @@ def format_table(f, data, remove_empty_columns=False): # type: (TextIO, Iterabl
|
||||
for i, text in enumerate(row):
|
||||
if column_sizes[i] == 0 and remove_empty_columns:
|
||||
continue
|
||||
row_text += " " + (text or '').ljust(column_sizes[i]) + " |"
|
||||
row_text += " " + (text or "").ljust(column_sizes[i]) + " |"
|
||||
row_text += "\n"
|
||||
f.write(row_text)
|
||||
f.write(sep)
|
||||
f.write('\n')
|
||||
f.write("\n")
|
||||
|
||||
|
||||
def make_type(t, state): # type: (str, State) -> str
|
||||
if t in state.classes:
|
||||
return ':ref:`{0}<class_{0}>`'.format(t)
|
||||
return ":ref:`{0}<class_{0}>`".format(t)
|
||||
print_error("Unresolved type '{}', file: {}".format(t, state.current_class), state)
|
||||
return t
|
||||
|
||||
@ -897,7 +924,7 @@ def make_enum(t, state): # type: (str, State) -> str
|
||||
p = t.find(".")
|
||||
if p >= 0:
|
||||
c = t[0:p]
|
||||
e = t[p + 1:]
|
||||
e = t[p + 1 :]
|
||||
# Variant enums live in GlobalScope but still use periods.
|
||||
if c == "Variant":
|
||||
c = "@GlobalScope"
|
||||
@ -909,7 +936,7 @@ def make_enum(t, state): # type: (str, State) -> str
|
||||
c = "@GlobalScope"
|
||||
|
||||
if not c in state.classes and c.startswith("_"):
|
||||
c = c[1:] # Remove the underscore prefix
|
||||
c = c[1:] # Remove the underscore prefix
|
||||
|
||||
if c in state.classes and e in state.classes[c].enums:
|
||||
return ":ref:`{0}<enum_{1}_{0}>`".format(e, c)
|
||||
@ -921,7 +948,9 @@ def make_enum(t, state): # type: (str, State) -> str
|
||||
return t
|
||||
|
||||
|
||||
def make_method_signature(class_def, method_def, make_ref, state): # type: (ClassDef, Union[MethodDef, SignalDef], bool, State) -> Tuple[str, str]
|
||||
def make_method_signature(
|
||||
class_def, method_def, make_ref, state
|
||||
): # type: (ClassDef, Union[MethodDef, SignalDef], bool, State) -> Tuple[str, str]
|
||||
ret_type = " "
|
||||
|
||||
ref_type = "signal"
|
||||
@ -936,34 +965,34 @@ def make_method_signature(class_def, method_def, make_ref, state): # type: (Cla
|
||||
else:
|
||||
out += "**{}** ".format(method_def.name)
|
||||
|
||||
out += '**(**'
|
||||
out += "**(**"
|
||||
for i, arg in enumerate(method_def.parameters):
|
||||
if i > 0:
|
||||
out += ', '
|
||||
out += ", "
|
||||
else:
|
||||
out += ' '
|
||||
out += " "
|
||||
|
||||
out += "{} {}".format(arg.type_name.to_rst(state), arg.name)
|
||||
|
||||
if arg.default_value is not None:
|
||||
out += '=' + arg.default_value
|
||||
out += "=" + arg.default_value
|
||||
|
||||
if isinstance(method_def, MethodDef) and method_def.qualifiers is not None and 'vararg' in method_def.qualifiers:
|
||||
if isinstance(method_def, MethodDef) and method_def.qualifiers is not None and "vararg" in method_def.qualifiers:
|
||||
if len(method_def.parameters) > 0:
|
||||
out += ', ...'
|
||||
out += ", ..."
|
||||
else:
|
||||
out += ' ...'
|
||||
out += " ..."
|
||||
|
||||
out += ' **)**'
|
||||
out += " **)**"
|
||||
|
||||
if isinstance(method_def, MethodDef) and method_def.qualifiers is not None:
|
||||
out += ' ' + method_def.qualifiers
|
||||
out += " " + method_def.qualifiers
|
||||
|
||||
return ret_type, out
|
||||
|
||||
|
||||
def make_heading(title, underline): # type: (str, str) -> str
|
||||
return title + '\n' + (underline * len(title)) + "\n\n"
|
||||
return title + "\n" + (underline * len(title)) + "\n\n"
|
||||
|
||||
|
||||
def make_url(link): # type: (str) -> str
|
||||
@ -987,5 +1016,5 @@ def make_url(link): # type: (str) -> str
|
||||
return "`" + link + " <" + link + ">`_"
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user