From bbedc0abd2b110cce36d3e784ad5dae6174a17ab Mon Sep 17 00:00:00 2001 From: zoldalma <46655437+zoldalma999@users.noreply.github.com> Date: Sun, 20 Oct 2024 20:19:21 +0200 Subject: [PATCH] Generate reST/ref docs from python or stub files --- buildconfig/stubs/gen_stubs.py | 2 + buildconfig/stubs/pygame/__init__.pyi | 2 +- docs/reST/conf.py | 19 ++++- docs/reST/ext/documenters.py | 112 ++++++++++++++++++++++++++ docs/reST/ext/indexer.py | 2 +- pyproject.toml | 1 + src_py/camera.py | 4 +- 7 files changed, 135 insertions(+), 7 deletions(-) create mode 100644 docs/reST/ext/documenters.py diff --git a/buildconfig/stubs/gen_stubs.py b/buildconfig/stubs/gen_stubs.py index 488ba2cfc7..7b2c60d4c7 100644 --- a/buildconfig/stubs/gen_stubs.py +++ b/buildconfig/stubs/gen_stubs.py @@ -131,6 +131,8 @@ def get_all(mod: Any): f.write(misc_stubs) for mod, items in pygame_all_imports.items(): + if mod == "pygame": + mod = "." if len(items) <= 4: # try to write imports in a single line if it can fit the line limit import_items = (f"{string} as {string}" for string in items) diff --git a/buildconfig/stubs/pygame/__init__.pyi b/buildconfig/stubs/pygame/__init__.pyi index ffb58f4cdb..e1e404ed37 100644 --- a/buildconfig/stubs/pygame/__init__.pyi +++ b/buildconfig/stubs/pygame/__init__.pyi @@ -2,7 +2,7 @@ # A script to auto-generate locals.pyi, constants.pyi and __init__.pyi typestubs # IMPORTANT NOTE: Do not edit this file by hand! -from pygame import ( +from . import ( display as display, draw as draw, event as event, diff --git a/docs/reST/conf.py b/docs/reST/conf.py index 136a723ae3..25b927c8e9 100644 --- a/docs/reST/conf.py +++ b/docs/reST/conf.py @@ -10,7 +10,7 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys, os +import sys, os, pathlib # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -23,11 +23,22 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', - 'sphinx.ext.coverage', 'ext.headers', 'ext.boilerplate', - 'ext.customversion', 'ext.edit_on_github'] +extensions = ['autoapi.extension', 'ext.headers', 'ext.boilerplate', + 'ext.customversion', 'ext.edit_on_github', 'ext.documenters'] +autoapi_dirs = [ + pathlib.Path(os.path.abspath(".")).parent.parent / 'buildconfig' / 'stubs' / 'pygame' / '', + pathlib.Path(os.path.abspath(".")).parent.parent / 'src_py' / '', +] + +autoapi_options = ['members', 'undoc-members'] +autoapi_ignore = ["*controller.py", "*_sdl2/window.py", "*freetype.py", "*ftfont.py", "*__pyinstaller*", "*__init__.py", "*__briefcase*"] +autoapi_generate_api_docs = False +autodoc_typehints = 'none' +suppress_warnings = ['autoapi.python_import_resolution'] +autodoc_member_order = 'bysource' + # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/docs/reST/ext/documenters.py b/docs/reST/ext/documenters.py new file mode 100644 index 0000000000..53514d112f --- /dev/null +++ b/docs/reST/ext/documenters.py @@ -0,0 +1,112 @@ +import autoapi +import autoapi.documenters +from autoapi._objects import PythonClass + + +def build_signatures(object): + name = object.short_name + + if isinstance(object, PythonClass): + if object.constructor is not None: + object = object.constructor + object.obj["return_annotation"] = name + object.obj["overloads"] = [ + (arg, name) for arg, _ in object.obj["overloads"] + ] + + else: + for child in object.children: + if child.short_name == "__new__": + object = child + break + + if object is None: + return + + sigs = [(object.obj["args"], object.obj["return_annotation"])] + sigs.extend(object.obj["overloads"]) + + for args, ret in sigs: + arg_string = "" + for modifier, arg_name, _, default in args: + modifier = modifier or "" + arg_name = arg_name or "" + default = default or "" + + if default: + default = "=" + default + arg_string += f", {modifier}{arg_name}{default}" + + if arg_string: + arg_string = arg_string[2:] + + if ret.count("[") > 2 or ret.count(",") > 3: + ret = "..." + + yield f"| :sg:`{name}({arg_string}) -> {ret}`" + + +class AutopgDocumenter(autoapi.documenters.AutoapiDocumenter): + def format_signature(self, **kwargs): + return "" + + def get_doc(self, encoding=None, ignore=1): + if self.object.docstring: + return super().get_doc(encoding, ignore) + + # If we don't already have docs, check if a python implementation exists of this + # module and return its docstring if it does + python_object = self.env.autoapi_all_objects.get( + self.object.id.replace("pygame", "src_py"), None + ) + if python_object is not None: + return [python_object.docstring.splitlines()] + + return [""] + + def process_doc(self, docstrings: list[str]): + for docstring in docstrings: + if not docstring: + continue + + yield f"| :sl:`{docstring[0]}`" + + if "args" in self.object.obj or hasattr(self.object, "constructor"): + yield from build_signatures(self.object) + else: + annotation = self.object.obj.get("annotation", None) + if annotation is not None: + if annotation.count("[") > 2 or annotation.count(",") > 3: + annotation = "..." + yield f"| :sg:`{self.object.short_name} -> {annotation}`" + + yield from docstring[1:] + + yield "" + + +def setup(app): + names = [ + "function", + "property", + "decorator", + "class", + "method", + "data", + "attribute", + "module", + "exception", + ] + + for name in names: + capitalized = name.capitalize() + app.add_autodocumenter( + type( + f"Autopg{capitalized}Documenter", + ( + AutopgDocumenter, + getattr(autoapi.documenters, f"Autoapi{capitalized}Documenter"), + ), + {"objtype": f"pg{name}"}, + ) + ) diff --git a/docs/reST/ext/indexer.py b/docs/reST/ext/indexer.py index 2ece8dfd64..1eab793211 100644 --- a/docs/reST/ext/indexer.py +++ b/docs/reST/ext/indexer.py @@ -65,7 +65,6 @@ def collect_document_info(app, doctree): class CollectInfo(Visitor): - """Records the information for a document""" desctypes = { @@ -74,6 +73,7 @@ class CollectInfo(Visitor): "exception", "class", "attribute", + "property", "method", "staticmethod", "classmethod", diff --git a/pyproject.toml b/pyproject.toml index 476a08242e..8713c3d411 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,6 +59,7 @@ requires = [ "ninja<=1.11.1.1", "cython<=3.0.11", "sphinx<=7.2.6", + "autoapi<=3.3.2", ] build-backend = 'mesonpy' diff --git a/src_py/camera.py b/src_py/camera.py index 2a5db6c4f2..971efd9031 100644 --- a/src_py/camera.py +++ b/src_py/camera.py @@ -56,7 +56,9 @@ def _pre_init_placeholder_varargs(*_, **__): class _PreInitPlaceholderCamera(AbstractCamera): - __init__ = _pre_init_placeholder_varargs + def __init__(self, *args, **kwargs): + _pre_init_placeholder() + start = _pre_init_placeholder_varargs stop = _pre_init_placeholder_varargs get_controls = _pre_init_placeholder_varargs