Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MNT: update vendored docs files #834

Merged
merged 2 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .codespellrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[codespell]
skip = .git,*.pdf,*.svg,viz-report.html
skip = .git,*.pdf,*.svg,numpydoc.py,viz-report.html
# objekt - used in the code purposefully different from object
# nd - import scipy.ndimage as nd
ignore-words-list = objekt,nd
41 changes: 29 additions & 12 deletions docs/sphinxext/docscrape.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Extract reference documentation from the NumPy source tree.

"""

import inspect
import textwrap
import re
Expand All @@ -11,12 +12,7 @@
import copy
import sys


# TODO: Remove try-except when support for Python 3.7 is dropped
try:
from functools import cached_property
except ImportError: # cached_property added in Python 3.8
cached_property = property
from functools import cached_property


def strip_blank_lines(l):
Expand Down Expand Up @@ -408,7 +404,7 @@ def _parse(self):
msg = "Docstring contains a Receives section but not Yields."
raise ValueError(msg)

for (section, content) in sections:
for section, content in sections:
if not section.startswith(".."):
section = (s.capitalize() for s in section.split(" "))
section = " ".join(section)
Expand Down Expand Up @@ -631,7 +627,6 @@ def __init__(self, obj, doc=None, config=None):


class ClassDoc(NumpyDocString):

extra_public_methods = ["__call__"]

def __init__(self, cls, doc=None, modulename="", func_doc=FunctionDoc, config=None):
Expand Down Expand Up @@ -711,6 +706,7 @@ def properties(self):
for name, func in inspect.getmembers(self._cls)
if (
not name.startswith("_")
and not self._should_skip_member(name, self._cls)
and (
func is None
or isinstance(func, (property, cached_property))
Expand All @@ -720,6 +716,19 @@ def properties(self):
)
]

@staticmethod
def _should_skip_member(name, klass):
if (
# Namedtuples should skip everything in their ._fields as the
# docstrings for each of the members is: "Alias for field number X"
issubclass(klass, tuple)
and hasattr(klass, "_asdict")
and hasattr(klass, "_fields")
and name in klass._fields
):
return True
return False

def _is_show_member(self, name):
if self.show_inherited_members:
return True # show all class members
Expand All @@ -728,7 +737,15 @@ def _is_show_member(self, name):
return True


def get_doc_object(obj, what=None, doc=None, config=None):
def get_doc_object(
obj,
what=None,
doc=None,
config=None,
class_doc=ClassDoc,
func_doc=FunctionDoc,
obj_doc=ObjDoc,
):
if what is None:
if inspect.isclass(obj):
what = "class"
Expand All @@ -742,10 +759,10 @@ def get_doc_object(obj, what=None, doc=None, config=None):
config = {}

if what == "class":
return ClassDoc(obj, func_doc=FunctionDoc, doc=doc, config=config)
return class_doc(obj, func_doc=func_doc, doc=doc, config=config)
elif what in ("function", "method"):
return FunctionDoc(obj, doc=doc, config=config)
return func_doc(obj, doc=doc, config=config)
else:
if doc is None:
doc = pydoc.getdoc(obj)
return ObjDoc(obj, doc, config=config)
return obj_doc(obj, doc, config=config)
38 changes: 16 additions & 22 deletions docs/sphinxext/docscrape_sphinx.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from sphinx.jinja2glue import BuiltinTemplateLoader

from .docscrape import NumpyDocString, FunctionDoc, ClassDoc, ObjDoc
from .docscrape import get_doc_object as get_doc_object_orig
from .xref import make_xref


Expand Down Expand Up @@ -372,9 +373,11 @@ def __str__(self, indent=0, func_role="obj"):
"notes": self._str_section("Notes"),
"references": self._str_references(),
"examples": self._str_examples(),
"attributes": self._str_param_list("Attributes", fake_autosummary=True)
if self.attributes_as_param_list
else self._str_member_list("Attributes"),
"attributes": (
self._str_param_list("Attributes", fake_autosummary=True)
if self.attributes_as_param_list
else self._str_member_list("Attributes")
),
"methods": self._str_member_list("Methods"),
}
ns = {k: "\n".join(v) for k, v in ns.items()}
Expand Down Expand Up @@ -407,20 +410,10 @@ def __init__(self, obj, doc=None, config=None):
ObjDoc.__init__(self, obj, doc=doc, config=config)


# TODO: refactor to use docscrape.get_doc_object
def get_doc_object(obj, what=None, doc=None, config=None, builder=None):
if what is None:
if inspect.isclass(obj):
what = "class"
elif inspect.ismodule(obj):
what = "module"
elif isinstance(obj, Callable):
what = "function"
else:
what = "object"

if config is None:
config = {}

template_dirs = [os.path.join(os.path.dirname(__file__), "templates")]
if builder is not None:
template_loader = BuiltinTemplateLoader()
Expand All @@ -430,11 +423,12 @@ def get_doc_object(obj, what=None, doc=None, config=None, builder=None):
template_env = SandboxedEnvironment(loader=template_loader)
config["template"] = template_env.get_template("numpydoc_docstring.rst")

if what == "class":
return SphinxClassDoc(obj, func_doc=SphinxFunctionDoc, doc=doc, config=config)
elif what in ("function", "method"):
return SphinxFunctionDoc(obj, doc=doc, config=config)
else:
if doc is None:
doc = pydoc.getdoc(obj)
return SphinxObjDoc(obj, doc, config=config)
return get_doc_object_orig(
obj,
what=what,
doc=doc,
config=config,
class_doc=SphinxClassDoc,
func_doc=SphinxFunctionDoc,
obj_doc=SphinxObjDoc,
)
47 changes: 31 additions & 16 deletions docs/sphinxext/numpydoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
.. [1] https://github.com/numpy/numpydoc

"""

from copy import deepcopy
import re
import pydoc
Expand All @@ -24,17 +25,17 @@
import hashlib
import itertools

from docutils.nodes import citation, Text, section, comment, reference
from docutils.nodes import citation, Text, section, comment, reference, inline
import sphinx
from sphinx.addnodes import pending_xref, desc_content
from sphinx.util import logging
from sphinx.errors import ExtensionError

if sphinx.__version__ < "4.2":
raise RuntimeError("Sphinx 4.2 or newer is required")
if sphinx.__version__ < "5":
raise RuntimeError("Sphinx 5 or newer is required")

from .docscrape_sphinx import get_doc_object
from .validate import validate, ERROR_MSGS
from .validate import validate, ERROR_MSGS, get_validation_checks
from .xref import DEFAULT_LINKS
from . import __version__

Expand Down Expand Up @@ -149,6 +150,10 @@ def clean_backrefs(app, doc, docname):
for ref in _traverse_or_findall(doc, reference, descend=True):
for id_ in ref["ids"]:
known_ref_ids.add(id_)
# some extensions produce backrefs to inline elements
for ref in _traverse_or_findall(doc, inline, descend=True):
for id_ in ref["ids"]:
known_ref_ids.add(id_)
for citation_node in _traverse_or_findall(doc, citation, descend=True):
# remove backrefs to non-existent refs
citation_node["backrefs"] = [
Expand Down Expand Up @@ -207,7 +212,19 @@ def mangle_docstrings(app, what, name, obj, options, lines):
# TODO: Currently, all validation checks are run and only those
# selected via config are reported. It would be more efficient to
# only run the selected checks.
errors = validate(doc)["errors"]
report = validate(doc)
errors = [
err
for err in report["errors"]
if not (
(
overrides := app.config.numpydoc_validation_overrides.get(
err[0]
)
)
and re.search(overrides, report["docstring"])
)
]
if {err[0] for err in errors} & app.config.numpydoc_validation_checks:
msg = (
f"[numpydoc] Validation warnings while processing "
Expand Down Expand Up @@ -285,6 +302,7 @@ def setup(app, get_doc_object_=get_doc_object):
app.add_config_value("numpydoc_xref_ignore", set(), True)
app.add_config_value("numpydoc_validation_checks", set(), True)
app.add_config_value("numpydoc_validation_exclude", set(), False)
app.add_config_value("numpydoc_validation_overrides", dict(), False)

# Extra mangling domains
app.add_domain(NumpyPythonDomain)
Expand All @@ -310,17 +328,9 @@ def update_config(app, config=None):

# Processing to determine whether numpydoc_validation_checks is treated
# as a blocklist or allowlist
valid_error_codes = set(ERROR_MSGS.keys())
if "all" in config.numpydoc_validation_checks:
block = deepcopy(config.numpydoc_validation_checks)
config.numpydoc_validation_checks = valid_error_codes - block
# Ensure that the validation check set contains only valid error codes
invalid_error_codes = config.numpydoc_validation_checks - valid_error_codes
if invalid_error_codes:
raise ValueError(
f"Unrecognized validation code(s) in numpydoc_validation_checks "
f"config value: {invalid_error_codes}"
)
config.numpydoc_validation_checks = get_validation_checks(
config.numpydoc_validation_checks
)

# Generate the regexp for docstrings to ignore during validation
if isinstance(config.numpydoc_validation_exclude, str):
Expand All @@ -335,6 +345,11 @@ def update_config(app, config=None):
)
config.numpydoc_validation_excluder = exclude_expr

for check, patterns in config.numpydoc_validation_overrides.items():
config.numpydoc_validation_overrides[check] = re.compile(
r"|".join(exp for exp in patterns)
)


# ------------------------------------------------------------------------------
# Docstring-mangling domains
Expand Down
Loading