Skip to content

Commit

Permalink
Merge pull request #139 from jbms/fix-138
Browse files Browse the repository at this point in the history
Fix several issues with nav_adapt and add unit tests
  • Loading branch information
jbms authored Jul 28, 2022
2 parents 5e70669 + f889aad commit bf16975
Show file tree
Hide file tree
Showing 50 changed files with 2,450 additions and 1,001 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ jobs:
- run: npm run check
- name: Install Python packaging & linting tools
run: python -m pip install -r dev-requirements.txt -r requirements.txt
- run: mypy sphinx_immaterial/**/*.py
- run: mypy sphinx_immaterial/**/*.py tests/**/*.py
shell: bash
- run: pylint sphinx_immaterial/**/*.py
shell: bash
- run: black --diff --color sphinx_immaterial/**/*.py
- run: black --diff --color sphinx_immaterial/**/*.py tests/**/*.py
shell: bash
- name: Check for dirty working directory
run: git diff --exit-code
Expand Down
1 change: 1 addition & 0 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ types-PyYAML
docutils-stubs
types-jsonschema
pytest
pytest-snapshot
3 changes: 1 addition & 2 deletions sphinx_immaterial/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,7 @@ def setup(app):
app.connect("config-inited", _config_inited)

app.setup_extension(apidoc_formatting.__name__)
app.setup_extension("sphinx_immaterial.apidoc.python.domain_fixes")
app.setup_extension("sphinx_immaterial.apidoc.python.type_annotation_transforms")
app.setup_extension("sphinx_immaterial.apidoc.python.default")
app.setup_extension("sphinx_immaterial.apidoc.cpp.default")
app.setup_extension(nav_adapt.__name__)
app.setup_extension("sphinx_immaterial.postprocess_html")
Expand Down
2 changes: 1 addition & 1 deletion sphinx_immaterial/apidoc/cpp/cpp_resolve_c_xrefs.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def run(
c_parent_key = self.env.ref_context.get("c:parent_key")
if c_parent_key is not None:
for node in nodes:
for refnode in node.traverse(condition=sphinx.addnodes.pending_xref):
for refnode in node.findall(condition=sphinx.addnodes.pending_xref):
if refnode.get("refdomain") == "cpp":
refnode["c:parent_key"] = c_parent_key
return nodes, messages
Expand Down
6 changes: 3 additions & 3 deletions sphinx_immaterial/apidoc/cpp/parameter_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,12 @@ def add_replacement(
param_node_copy.line = line
sig_param_nodes[name] = param_node_copy
del name_node[node_identifier_key]
for name_node_copy in param_node_copy.traverse(condition=type(name_node)):
for name_node_copy in param_node_copy.findall(condition=type(name_node)):
if name_node_copy.get(node_identifier_key):
return name_node_copy
raise ValueError("Could not locate name node within parameter")

for sig_param_node in signode.traverse(condition=sphinx.addnodes.desc_sig_name):
for sig_param_node in signode.findall(condition=sphinx.addnodes.desc_sig_name):
desc_param_node = sig_param_node.parent
if not isinstance(desc_param_node, sphinx.addnodes.desc_parameter):
continue
Expand All @@ -212,7 +212,7 @@ def add_replacement(
new_sig_param_node = add_replacement(sig_param_node, sig_param_node.parent)
new_sig_param_node["classes"].append("sig-name")

for desc_sig_name_node in signode.traverse(condition=sphinx.addnodes.desc_sig_name):
for desc_sig_name_node in signode.findall(condition=sphinx.addnodes.desc_sig_name):
parent = desc_sig_name_node.parent
if not isinstance(parent, sphinx.addnodes.desc_name):
continue
Expand Down
2 changes: 1 addition & 1 deletion sphinx_immaterial/apidoc/cpp/signodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def describe_signature_as_introducer(
orig_describe_signature_as_introducer(
self, fake_parent, mode, env, symbol, lineSpec
)
for x in fake_parent.traverse(condition=sphinx.addnodes.desc_name):
for x in fake_parent.findall(condition=sphinx.addnodes.desc_name):
# Ensure template parameter names aren't styled as the main entity
# name.
x["classes"].append("sig-name-nonprimary")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def _strip_namespaces_from_signature(
):
# Collect nodes to remove first, then remove them in reverse order.
removals = []
for child in node.traverse(condition=sphinx.addnodes.desc_sig_name):
for child in node.findall(condition=sphinx.addnodes.desc_sig_name):
parent = child.parent
if not isinstance(parent, sphinx.addnodes.pending_xref):
continue
Expand Down
4 changes: 2 additions & 2 deletions sphinx_immaterial/apidoc/format_signatures.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class CollectSignaturesTransform(sphinx.transforms.SphinxTransform):

def apply(self, **kwargs: Any) -> None:
collected_signatures = _get_collected_signatures(self.env)
for node in self.document.traverse(sphinx.addnodes.desc_signature):
for node in self.document.findall(sphinx.addnodes.desc_signature):
parent = node.parent
domain: str = parent.get("domain")
objtype: str = parent.get("objtype")
Expand Down Expand Up @@ -249,7 +249,7 @@ def apply(self, **kwargs: Any) -> None:
formatted_signatures = getattr(self.env, _FORMATTED_SIGNATURES, None)
if formatted_signatures is None:
return
for node in self.document.traverse(sphinx.addnodes.desc_signature):
for node in self.document.findall(sphinx.addnodes.desc_signature):
signature_id = node.get(_SIGNATURE_FORMAT_ID)
if signature_id is None:
continue
Expand Down
26 changes: 11 additions & 15 deletions sphinx_immaterial/apidoc/object_toc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,19 @@
import docutils.nodes
import sphinx.addnodes
import sphinx.application
import sphinx.environment.collectors.toctree
from sphinx.environment.collectors.toctree import TocTreeCollector

from . import apidoc_formatting


def _monkey_patch_toc_tree_process_doc(app: sphinx.application.Sphinx):
"""Enables support for also finding Sphinx domain objects.
Args:
app: Sphinx application.
"""
TocTreeCollector = sphinx.environment.collectors.toctree.TocTreeCollector
def _monkey_patch_toc_tree_process_doc():
"""Enables support for also finding Sphinx domain objects."""

# Apply the monkey patch
orig_process_doc = TocTreeCollector.process_doc

def _make_section_from_desc(
app: sphinx.application.Sphinx,
source: sphinx.addnodes.desc,
) -> Optional[docutils.nodes.section]:
env = app.env
Expand Down Expand Up @@ -51,7 +47,7 @@ def _make_section_from_desc(
title = signature.get("toc_title", None)
if not title:
title = ""
for child in signature.traverse():
for child in signature.findall():
if isinstance(
child, (sphinx.addnodes.desc_name, sphinx.addnodes.desc_addname)
):
Expand Down Expand Up @@ -118,7 +114,7 @@ def _make_section_from_term(
return section

def _patched_process_doc(
self: sphinx.environment.collectors.toctree.TocTreeCollector,
self: TocTreeCollector,
app: sphinx.application.Sphinx,
doctree: docutils.nodes.document,
) -> None:
Expand Down Expand Up @@ -149,7 +145,7 @@ def _collect(
return
elif isinstance(source, sphinx.addnodes.desc):
# Object description. Try to create synthetic section.
new_node = _make_section_from_desc(source)
new_node = _make_section_from_desc(app, source)
if new_node is not None:
target += new_node
target = new_node
Expand Down Expand Up @@ -182,6 +178,10 @@ def _collect(

TocTreeCollector.process_doc = _patched_process_doc # type: ignore

_monkey_patch_toc_tree_process_doc()

def setup(app: sphinx.application.Sphinx):

# TocTreeCollector is registered before our extension is. In order for the
# monkey patching to take effect, we need to unregister it and re-register it.
for read_listener in app.events.listeners["doctree-read"]:
Expand All @@ -190,10 +190,6 @@ def _collect(
obj.disable(app)
app.add_env_collector(TocTreeCollector)
break


def setup(app: sphinx.application.Sphinx):
_monkey_patch_toc_tree_process_doc(app)
return {
"parallel_read_safe": True,
"parallel_write_safe": True,
Expand Down
31 changes: 31 additions & 0 deletions sphinx_immaterial/apidoc/python/annotation_style.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from typing import Optional, List

import docutils.nodes
import sphinx.domains.python
import sphinx.environment


def ensure_wrapped_in_desc_type(
nodes: List[docutils.nodes.Node],
) -> List[docutils.nodes.Node]:
if len(nodes) != 1 or not isinstance(nodes[0], sphinx.addnodes.desc_type):
nodes = [sphinx.addnodes.desc_type("", "", *nodes)]
return nodes


def _monkey_patch_python_parse_annotation():
"""Ensures that type annotations in signatures are wrapped in `desc_type`.
This allows them to be distinguished from parameter names in CSS rules.
"""
orig_parse_annotation = sphinx.domains.python._parse_annotation

def parse_annotation(
annotation: str, env: Optional[sphinx.environment.BuildEnvironment] = None
) -> List[docutils.nodes.Node]:
return ensure_wrapped_in_desc_type(orig_parse_annotation(annotation, env))

sphinx.domains.python._parse_annotation = parse_annotation


_monkey_patch_python_parse_annotation()
18 changes: 9 additions & 9 deletions sphinx_immaterial/apidoc/python/apigen.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ def _get_overloads_from_documenter(


def _has_default_value(node: sphinx.addnodes.desc_parameter):
for sub_node in node.traverse(condition=docutils.nodes.literal):
for sub_node in node.findall(condition=docutils.nodes.literal):
if "default_value" in sub_node.get("classes"):
return True
return False
Expand All @@ -229,7 +229,7 @@ def _must_shorten():
return len(node.astext()) > column_limit

parameterlist: Optional[sphinx.addnodes.desc_parameterlist] = None
for parameterlist in node.traverse(condition=sphinx.addnodes.desc_parameterlist):
for parameterlist in node.findall(condition=sphinx.addnodes.desc_parameterlist):
break

if parameterlist is None:
Expand Down Expand Up @@ -409,7 +409,7 @@ def _ensure_module_name_in_signature(signode: sphinx.addnodes.desc_signature) ->
:param signode: Signature to modify in place.
"""
for node in signode.traverse(condition=sphinx.addnodes.desc_addname):
for node in signode.findall(condition=sphinx.addnodes.desc_addname):
modname = signode.get("module")
if modname and not node.astext().startswith(modname + "."):
node.insert(0, docutils.nodes.Text(modname + "."))
Expand Down Expand Up @@ -463,7 +463,7 @@ def _mark_subscript_parameterlist(signode: sphinx.addnodes.desc_signature) -> No
:param node: Signature to modify in place.
"""
for sub_node in signode.traverse(condition=sphinx.addnodes.desc_parameterlist):
for sub_node in signode.findall(condition=sphinx.addnodes.desc_parameterlist):
sub_node["parens"] = ("[", "]")


Expand All @@ -476,13 +476,13 @@ def _clean_init_signature(signode: sphinx.addnodes.desc_signature) -> None:
:param node: Signature to modify in place.
"""
# Remove first parameter.
for param in signode.traverse(condition=sphinx.addnodes.desc_parameter):
for param in signode.findall(condition=sphinx.addnodes.desc_parameter):
if param.children[0].astext() == "self":
param.parent.remove(param)
break

# Remove return type.
for node in signode.traverse(condition=sphinx.addnodes.desc_returns):
for node in signode.findall(condition=sphinx.addnodes.desc_returns):
node.parent.remove(node)


Expand All @@ -496,7 +496,7 @@ def _clean_class_getitem_signature(signode: sphinx.addnodes.desc_signature) -> N
"""
# Remove `static` prefix
for prefix in signode.traverse(condition=sphinx.addnodes.desc_annotation):
for prefix in signode.findall(condition=sphinx.addnodes.desc_annotation):
prefix.parent.remove(prefix)
break

Expand Down Expand Up @@ -624,7 +624,7 @@ def _generate_entity_summary(
)
for sig_node in cast(List[sphinx.addnodes.desc_signature], objdesc.children[:-1]):
# Insert a link around the `desc_name` field
for sub_node in sig_node.traverse(condition=sphinx.addnodes.desc_name):
for sub_node in sig_node.findall(condition=sphinx.addnodes.desc_name):
if include_in_toc:
sub_node["classes"].append("pseudo-toc-entry")
xref_node = sphinx.addnodes.pending_xref(
Expand Down Expand Up @@ -726,7 +726,7 @@ def _merge_summary_nodes_into(
"""

sections: Dict[str, docutils.nodes.section] = {}
for section in contentnode.traverse(condition=docutils.nodes.section):
for section in contentnode.findall(condition=docutils.nodes.section):
if section["ids"]:
sections[section["ids"][0]] = section

Expand Down
34 changes: 34 additions & 0 deletions sphinx_immaterial/apidoc/python/attribute_style.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from typing import Type, Tuple

import docutils.nodes
import sphinx.addnodes
import sphinx.domains.python


def _monkey_patch_pyattribute_handle_signature(
directive_cls: Type[sphinx.domains.python.PyObject],
):
"""Modifies PyAttribute or PyVariable to improve styling of signature."""

def handle_signature(
self, sig: str, signode: sphinx.addnodes.desc_signature
) -> Tuple[str, str]:
result = super(directive_cls, self).handle_signature(sig, signode)
typ = self.options.get("type")
if typ:
signode += sphinx.addnodes.desc_sig_punctuation("", " : ")
signode += sphinx.domains.python._parse_annotation(typ, self.env)

value = self.options.get("value")
if value:
signode += sphinx.addnodes.desc_sig_punctuation("", " = ")
signode += docutils.nodes.literal(
text=value, classes=["code", "python"], language="python"
)
return result

directive_cls.handle_signature = handle_signature # type: ignore


_monkey_patch_pyattribute_handle_signature(sphinx.domains.python.PyAttribute)
_monkey_patch_pyattribute_handle_signature(sphinx.domains.python.PyVariable)
3 changes: 3 additions & 0 deletions sphinx_immaterial/apidoc/python/autodoc_property_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,6 @@ def handle_signature(
return fullname, prefix

PyProperty.handle_signature = handle_signature


apply_property_documenter_type_annotation_fix()
27 changes: 27 additions & 0 deletions sphinx_immaterial/apidoc/python/default.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import sphinx.application

from . import annotation_style
from . import object_ids
from . import synopses
from . import parameter_objects
from . import style_default_values_as_code
from . import subscript_methods
from . import attribute_style
from . import napoleon_admonition_classes
from . import strip_property_prefix
from . import section_titles
from . import autodoc_property_type
from . import type_annotation_transforms
from . import strip_self_and_return_type_annotations


def setup(app: sphinx.application.Sphinx):
app.setup_extension(parameter_objects.__name__)
app.setup_extension(strip_property_prefix.__name__)
app.setup_extension(type_annotation_transforms.__name__)
app.setup_extension(strip_self_and_return_type_annotations.__name__)

return {
"parallel_read_safe": True,
"parallel_write_safe": True,
}
Loading

0 comments on commit bf16975

Please sign in to comment.