Skip to content

Commit

Permalink
Replace custom autodoc parser with a dedicated directive
Browse files Browse the repository at this point in the history
  • Loading branch information
manics committed Oct 27, 2024
1 parent 90c5fd0 commit 640f168
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 68 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/linkcheck.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down
1 change: 1 addition & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ build:

python:
install:
- path: .
- requirements: docs/requirements.txt
102 changes: 36 additions & 66 deletions docs/extensions/serverprocess_documenter.py
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
1 change: 1 addition & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
myst-parser
sphinx>=7.4
sphinx-autobuild
sphinx-book-theme
sphinx-copybutton
Expand Down
2 changes: 1 addition & 1 deletion docs/source/server-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pairs.

```{eval-rst}
.. autoserverprocessconfigurable:: jupyter_server_proxy.config.ServerProcess
.. serverprocess:: jupyter_server_proxy.config ServerProcess
```

## Specifying config via traitlets
Expand Down

0 comments on commit 640f168

Please sign in to comment.