From ef4c546103b44ca0714b330797abccebfa08fb01 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:24:22 +0100 Subject: [PATCH] MNT: update vendored docs files Copied from numpydoc 1.6.0. --- docs/sphinxext/docscrape.py | 26 ++++++++-------- docs/sphinxext/docscrape_sphinx.py | 30 +++++++------------ docs/sphinxext/numpydoc.py | 48 +++++++++++++++++++----------- 3 files changed, 56 insertions(+), 48 deletions(-) diff --git a/docs/sphinxext/docscrape.py b/docs/sphinxext/docscrape.py index e5c07f59ded..315be6c356b 100644 --- a/docs/sphinxext/docscrape.py +++ b/docs/sphinxext/docscrape.py @@ -11,12 +11,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): @@ -408,7 +403,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) @@ -631,7 +626,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): @@ -728,7 +722,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" @@ -742,10 +744,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) diff --git a/docs/sphinxext/docscrape_sphinx.py b/docs/sphinxext/docscrape_sphinx.py index 9a62cff9ce7..26a8e6b3764 100644 --- a/docs/sphinxext/docscrape_sphinx.py +++ b/docs/sphinxext/docscrape_sphinx.py @@ -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 @@ -407,20 +408,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() @@ -430,11 +421,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, + ) diff --git a/docs/sphinxext/numpydoc.py b/docs/sphinxext/numpydoc.py index e5bc563d444..3af2f11faa8 100644 --- a/docs/sphinxext/numpydoc.py +++ b/docs/sphinxext/numpydoc.py @@ -24,17 +24,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__ @@ -149,6 +149,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"] = [ @@ -207,7 +211,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 " @@ -285,6 +301,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) @@ -310,17 +327,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): @@ -335,6 +344,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 @@ -390,7 +404,7 @@ def match_items(lines, content_old): This function tries to match the lines in ``lines`` with the items (source file references and line numbers) in ``content_old``. The ``mangle_docstrings`` function changes the actual docstrings, but doesn't - keep track of where each line came from. The mangling does many operations + keep track of where each line came from. The manging does many operations on the original lines, which are hard to track afterwards. Many of the line changes come from deleting or inserting blank lines. This