diff --git a/.github/workflows/linkcheck.yaml b/.github/workflows/linkcheck.yaml index 96cf9040..d38d748d 100644 --- a/.github/workflows/linkcheck.yaml +++ b/.github/workflows/linkcheck.yaml @@ -27,7 +27,7 @@ jobs: python-version: "3.12" - name: Install deps - run: pip install -r docs/requirements.txt + run: pip install . -r docs/requirements.txt - name: make linkcheck run: | diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 6bf99912..98501d58 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -14,4 +14,5 @@ build: python: install: + - path: . - requirements: docs/requirements.txt diff --git a/docs/extensions/serverprocess_documenter.py b/docs/extensions/serverprocess_documenter.py index 1339aa7a..567ab857 100644 --- a/docs/extensions/serverprocess_documenter.py +++ b/docs/extensions/serverprocess_documenter.py @@ -1,86 +1,56 @@ """ -A modified version of https://github.com/jupyterhub/autodoc-traits/tree/1.2.2 -for documenting trait fields that are used to configure another object, but -where the traitlet cannot be set directly. - -This is used to generate the Server Process options documentation: +A custom Sphinx directive to generate the Server Process options documentation: https://github.com/jupyterhub/jupyter-server-proxy/blob/main/docs/source/server-process.md """ +import importlib +from textwrap import dedent + +from docutils import nodes from sphinx.application import Sphinx -from sphinx.ext.autodoc import ( - SUPPRESS, - AttributeDocumenter, - ClassDocumenter, - ObjectMember, -) +from sphinx.util.docutils import SphinxDirective from sphinx.util.typing import ExtensionMetadata -from traitlets import MetaHasTraits, TraitType, Undefined - - -class ServerProcessConfigurableDocumenter(ClassDocumenter): - """ - A modified version of autodoc_traits.ConfigurableDocumenter that only documents - the traits in this class, not the inherited traits. - https://github.com/jupyterhub/autodoc-traits/blob/1.2.2/autodoc_traits.py#L20-L122 - """ - - objtype = "serverprocessconfigurable" - directivetype = "class" - priority = 100 # higher priority than ClassDocumenter's 10 - - @classmethod - def can_document_member(cls, member, membername, isattr, parent): - return isinstance(member, MetaHasTraits) - - def get_object_members(self, want_all): - """ - Only document members in this class - """ - config_trait_members = self.object.class_traits(config=True).items() - members = [ObjectMember(name, trait) for (name, trait) in config_trait_members] - return False, members +from traitlets import Undefined - def should_suppress_directive_header(): - return True - def add_directive_header(self, sig): - print(f"{sig=}") - self.options.annotation = SUPPRESS - super().add_directive_header(sig) +class ServerProcessDirective(SphinxDirective): + """A directive to say hello!""" + required_arguments = 2 -class ServerProcessTraitDocumenter(AttributeDocumenter): - """ - A modified version of autodoc_traits.TraitDocumenter that omits the c.ClassName prefix - https://github.com/jupyterhub/autodoc-traits/blob/1.2.2/autodoc_traits.py#L125-L203 - """ + def run(self) -> list[nodes.Node]: + module = importlib.import_module(self.arguments[0], ".") + cls = getattr(module, self.arguments[1]) + config_trait_members = cls.class_traits(config=True).items() - objtype = "serverprocesstrait" - directivetype = "attribute" - priority = 100 # AttributeDocumenter has 10 - member_order = 0 # AttributeDocumenter has 60 + doc = [] - @classmethod - def can_document_member(cls, member, membername, isattr, parent): - return isinstance(member, TraitType) + for name, trait in config_trait_members: + default_value = trait.default_value + if default_value is Undefined: + default_value = "" + else: + default_value = repr(default_value) + traitlets_type = trait.__class__.__name__ - def add_directive_header(self, sig): - default_value = self.object.default_value - if default_value is Undefined: - default_value = "" - else: - default_value = repr(default_value) + help = self.parse_text_to_nodes(dedent(trait.metadata.get("help", ""))) - traitlets_type = self.object.__class__.__name__ - self.options.annotation = f"{traitlets_type}({default_value})" - super().add_directive_header(sig) + definition = nodes.definition_list_item( + "", + nodes.term( + "", + "", + nodes.strong(text=f"{name}"), + nodes.emphasis(text=f" {traitlets_type}({default_value})"), + ), + nodes.definition("", *help), + ) + doc.append(nodes.definition_list("", definition)) + return doc def setup(app: Sphinx) -> ExtensionMetadata: - app.setup_extension("sphinx.ext.autodoc") - app.add_autodocumenter(ServerProcessConfigurableDocumenter) - app.add_autodocumenter(ServerProcessTraitDocumenter) + app.add_directive("serverprocess", ServerProcessDirective) return { "version": "0.1", "parallel_read_safe": True, diff --git a/docs/requirements.txt b/docs/requirements.txt index 63f4be10..cc731455 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,5 @@ myst-parser +sphinx>=7.4 sphinx-autobuild sphinx-book-theme sphinx-copybutton diff --git a/docs/source/server-process.md b/docs/source/server-process.md index 9e9cf316..6cc95922 100644 --- a/docs/source/server-process.md +++ b/docs/source/server-process.md @@ -17,7 +17,7 @@ pairs. ```{eval-rst} -.. autoserverprocessconfigurable:: jupyter_server_proxy.config.ServerProcess +.. serverprocess:: jupyter_server_proxy.config ServerProcess ``` ## Specifying config via traitlets