diff --git a/_static/css/custom.css b/_static/css/custom.css index 3bfde70068d1a..e70233c5fe1db 100644 --- a/_static/css/custom.css +++ b/_static/css/custom.css @@ -1,3 +1,8 @@ +.graphviz { + margin-top: 10px; + margin-bottom: 10px; +} + /* adds scrollbar to sidenav */ .wy-side-scroll { width: auto; diff --git a/process_links.py b/process_links.py index b7f79ebe3d65b..0a6243e5d0894 100644 --- a/process_links.py +++ b/process_links.py @@ -58,7 +58,35 @@ def create_links(doc: str) -> str: def process_docstring(app, what, name, obj, options, lines): - # print('d', what, name, obj, options) + if what == "class": + # hacky approach to detect nested classes, eg QgsCallout.QgsCalloutContext + is_nested = len(name.split(".")) > 3 + if not is_nested: + # remove docstring part, we've already included it in the page header + # only leave the __init__ methods + init_idx = 0 + class_name = name.split(".")[-1] + for init_idx, line in enumerate(lines): + if re.match(rf"^{class_name}\(", line): + break + + lines[:] = lines[init_idx:] + lines_out = [] + # loop through remaining lines, which are the constructors. Format + # these up so they look like proper __init__ method documentation + for i, line in enumerate(lines): + if re.match(rf"^{class_name}\(", line): + lines_out.append( + re.sub(rf"\b{class_name}\(", ".. py:method:: __init__(", line) + ) + lines_out.append(" :noindex:") + lines_out.append("") + else: + lines_out.append(" " + line) + + lines[:] = lines_out[:] + return + for i in range(len(lines)): # fix seealso diff --git a/rst/qgis_pydoc_template.txt b/rst/qgis_pydoc_template.txt index 66423a0f51459..aea580c0d5fd6 100644 --- a/rst/qgis_pydoc_template.txt +++ b/rst/qgis_pydoc_template.txt @@ -7,10 +7,10 @@ Class: $CLASS $HEADER_CONTENT +$TABLE_OF_CONTENTS + .. autoclass:: qgis.$PACKAGE.$CLASS :special-members: __init__ :members: :undoc-members: :exclude-members: $EXCLUDE_METHODS - - $TABLE_OF_CONTENTS diff --git a/scripts/make_api_rst.py b/scripts/make_api_rst.py index 87672849d907a..659172425fc50 100755 --- a/scripts/make_api_rst.py +++ b/scripts/make_api_rst.py @@ -100,7 +100,10 @@ def _recursive_substitute(self, **kws): raise ValueError("Max recursion depth exceeded") self.depth += 1 - result = super().safe_substitute(**kws) + try: + result = super().safe_substitute(**kws) + except RecursionError: + return self.template if "$" in result: return self.__class__(result)._recursive_substitute(**kws) @@ -178,35 +181,35 @@ def _recursive_substitute(self, **kws): """ -class_header = """ +inheritance_diagram = """ .. inheritance-diagram:: qgis.$PACKAGE.$CLASS :parts: 1 """ class_toc = """ - .. autoautosummary:: qgis.$PACKAGE.$CLASS - :enums: - :nosignatures: - :exclude-members: $EXCLUDE_METHODS - - .. autoautosummary:: qgis.$PACKAGE.$CLASS - :methods: - :nosignatures: - :exclude-members: $EXCLUDE_METHODS - - .. autoautosummary:: qgis.$PACKAGE.$CLASS - :static_methods: - :nosignatures: - :exclude-members: $EXCLUDE_METHODS - - .. autoautosummary:: qgis.$PACKAGE.$CLASS - :signals: - :nosignatures: - :exclude-members: $EXCLUDE_METHODS - - .. autoautosummary:: qgis.$PACKAGE.$CLASS - :attributes: - :exclude-members: $EXCLUDE_METHODS +.. autoautosummary:: qgis.$PACKAGE.$CLASS + :enums: + :nosignatures: + :exclude-members: $EXCLUDE_METHODS + +.. autoautosummary:: qgis.$PACKAGE.$CLASS + :methods: + :nosignatures: + :exclude-members: $EXCLUDE_METHODS + +.. autoautosummary:: qgis.$PACKAGE.$CLASS + :static_methods: + :nosignatures: + :exclude-members: $EXCLUDE_METHODS + +.. autoautosummary:: qgis.$PACKAGE.$CLASS + :signals: + :nosignatures: + :exclude-members: $EXCLUDE_METHODS + +.. autoautosummary:: qgis.$PACKAGE.$CLASS + :attributes: + :exclude-members: $EXCLUDE_METHODS """ MODULE_TOC_MAX_COLUMN_SIZES = [300, 500] @@ -318,10 +321,7 @@ def generate_docs(): header = "" toc = "" - if inspect.isclass(_class): - header = class_header - toc = class_toc - + bases_and_subclass_header = "" if hasattr(_class, "__bases__") and _class.__bases__: def export_bases(_b): @@ -342,22 +342,46 @@ def export_bases(_b): base_header = export_bases(_class) if base_header: - header += "\n" + write_header("Base classes") - header += f"\n+{'-' * MODULE_TOC_MAX_COLUMN_SIZES[0]}+{'-' * MODULE_TOC_MAX_COLUMN_SIZES[1]}+\n" - header += base_header + bases_and_subclass_header += "\n" + write_header("Base classes", 2) + bases_and_subclass_header += f"\n+{'-' * MODULE_TOC_MAX_COLUMN_SIZES[0]}+{'-' * MODULE_TOC_MAX_COLUMN_SIZES[1]}+\n" + bases_and_subclass_header += base_header if hasattr(_class, "__subclasses__") and _class.__subclasses__(): - header += "\n" + write_header("Subclasses") - header += f"\n+{'-' * MODULE_TOC_MAX_COLUMN_SIZES[0]}+{'-' * MODULE_TOC_MAX_COLUMN_SIZES[1]}+\n" + bases_and_subclass_header += "\n" + write_header("Subclasses", 2) + bases_and_subclass_header += f"\n+{'-' * MODULE_TOC_MAX_COLUMN_SIZES[0]}+{'-' * MODULE_TOC_MAX_COLUMN_SIZES[1]}+\n" for subclass in _class.__subclasses__(): - header += make_table_row( + bases_and_subclass_header += make_table_row( [ f"`{subclass.__name__} <{subclass.__name__}.html>`_", extract_summary(subclass.__doc__), ] ) + if inspect.isclass(_class): + class_doc = _class.__doc__ + # only keep the actual class doc string part. SIP will + # append the constructor signatures and docs at the end + # of the class doc, so let's trim those off. + # They'll get included later in the actual listing of + # class methods + if class_doc: + lines = class_doc.split("\n") + init_idx = 0 + for init_idx, line in enumerate(lines): + if re.match(rf"^{_class.__name__}\(", line): + break + + header = "\n".join(lines[:init_idx]) + + if bases_and_subclass_header: + if header: + header += "\n\n" + header += write_header("Class Hierarchy") + header += inheritance_diagram + header += bases_and_subclass_header + toc = class_toc + for method in dir(_class): if not hasattr(_class, method): continue