summaryrefslogtreecommitdiffstats
path: root/doc/tools/doc_status.py
diff options
context:
space:
mode:
authorRémi Verschelde <rverschelde@gmail.com>2020-03-30 08:28:32 +0200
committerRémi Verschelde <rverschelde@gmail.com>2020-03-30 09:05:53 +0200
commitcd4e46ee65dab6baa6a143bf3b3f64244be36712 (patch)
tree689e53265691e782ae35a961445c975219e1155d /doc/tools/doc_status.py
parent0168709978154a89f137b44f33647e5d28a46250 (diff)
downloadredot-engine-cd4e46ee65dab6baa6a143bf3b3f64244be36712.tar.gz
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.
Diffstat (limited to 'doc/tools/doc_status.py')
-rwxr-xr-xdoc/tools/doc_status.py351
1 files changed, 184 insertions, 167 deletions
diff --git a/doc/tools/doc_status.py b/doc/tools/doc_status.py
index e6e6d5f606..629b5a032b 100755
--- a/doc/tools/doc_status.py
+++ b/doc/tools/doc_status.py
@@ -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))
-
- 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]
- ))
+ 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],
+ )
+ )
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") + "!")