summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoss Barnowski <rossbar@berkeley.edu>2021-02-08 05:26:30 -0800
committerGitHub <noreply@github.com>2021-02-08 08:26:30 -0500
commitce6666ac82b6098b3d1b0909dc7a5eaef880b290 (patch)
tree1fa867e87e7cb0bd8bc7e907a2a8e55ed6034d01
parentda816b5b4e876dfb24807dfd3c12f0efe6f27cb7 (diff)
downloadnumpydoc-ce6666ac82b6098b3d1b0909dc7a5eaef880b290.tar.gz
ENH: Enable validation during sphinx-build process (#302)
* WIP: Move get_doc_obj to docscrape * WIP: mv _obj property to NumpyDocString * Proof-of-concept: Docstring attrs covered by refactor. Running the test suite on this patch demonstrates that refactoring the boundary between NumpyDocString and SphinxDocString provides the necessary info to (potentially) do away with the validate.Docstring class. * NOTE TO SELF: get_doc_object in docscrape_sphinx * Docstring -> Validator. * Activate validation during sphinx-build. Add a conf option to turn on/off. TODO: test * Replace logger.warn with warning. logger.warn is apparently deprecated * DOC: Add numpydoc_validate to conf docs. * Add mechanism for validation check selection. Adds a config option with a set to allow users to select which validation checks are used. Default is an empty set, which means none of the validation checks raise warnings during the build process. Add documentation for new option and activate in the doc build. * TST: modify how MockApp sets builder app. * TST: Add test of validation warnings. * Specify some sensible validation defaults. * Add docstring name to validation warnings. * Add all keyword to validation_check configuration. More flexibility in configuring which validation checks to run during sphinx build. If 'all' is present, treat the rest of the set as a blocklist, else an allowlist. * Fix failing test. * Make validation error mapping easier to read. * Add check for invalid error codes in configuration. plus test. * Add feature to exclude patterns from docstring validation. Modify updated config name to avoid sphinx warning. Add documentation for exclusion config value. * Be explicit about regex syntax for exclude config val Co-authored-by: Eric Larson <larson.eric.d@gmail.com> * Rm redundant numpydoc_validate config param. Co-authored-by: Eric Larson <larson.eric.d@gmail.com>
-rw-r--r--doc/conf.py2
-rw-r--r--doc/install.rst36
-rw-r--r--doc/validation.rst18
-rw-r--r--numpydoc/__main__.py6
-rw-r--r--numpydoc/docscrape.py37
-rw-r--r--numpydoc/docscrape_sphinx.py16
-rw-r--r--numpydoc/numpydoc.py52
-rw-r--r--numpydoc/tests/test_docscrape.py3
-rw-r--r--numpydoc/tests/test_numpydoc.py111
-rw-r--r--numpydoc/tests/test_validate.py6
-rw-r--r--numpydoc/validate.py65
11 files changed, 299 insertions, 53 deletions
diff --git a/doc/conf.py b/doc/conf.py
index e919763..185b6b1 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -84,6 +84,8 @@ version = re.sub(r'(\d+\.\d+)\.\d+(.*)', r'\1\2', numpydoc.__version__)
version = re.sub(r'(\.dev\d+).*?$', r'\1', version)
numpydoc_xref_param_type = True
numpydoc_xref_ignore = {'optional', 'type_without_description', 'BadException'}
+# Run docstring validation as part of build process
+numpydoc_validation_checks = {"all", "GL01", "SA04", "RT03"}
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/doc/install.rst b/doc/install.rst
index 6139efe..82c7d6b 100644
--- a/doc/install.rst
+++ b/doc/install.rst
@@ -96,6 +96,42 @@ numpydoc_xref_ignore : set or ``"all"``
desired cross reference mappings in ``numpydoc_xref_aliases`` and setting
``numpydoc_xref_ignore="all"`` is more convenient than explicitly listing
terms to ignore in a set.
+numpydoc_validation_checks : set
+ The set of validation checks to report during the sphinx build process.
+ The default is an empty set, so docstring validation is not run by
+ default.
+ If ``"all"`` is in the set, then the results of all of the
+ :ref:`built-in validation checks <validation_checks>` are reported.
+ If the set includes ``"all"`` and additional error codes, then all
+ validation checks *except* the listed error codes will be run.
+ If the set contains *only* individual error codes, then only those checks
+ will be run.
+ For example::
+
+ # Report warnings for all validation checks
+ numpydoc_validation_checks = {"all"}
+
+ # Report warnings for all checks *except* for GL01, GL02, and GL05
+ numpydoc_validation_checks = {"all", "GL01", "GL02", "GL05"}
+
+ # Only report warnings for the SA01 and EX01 checks
+ numpydoc_validation_checks = {"SA01", "EX01"}
+numpydoc_validation_exclude : set
+ A container of strings using :py:mod:`re` syntax specifying patterns to
+ ignore for docstring validation.
+ For example, to skip docstring validation for all objects in
+ ``mypkg.mymodule``::
+
+ numpydoc_validation_exclude = {"mypkg.mymodule."}
+
+ If you wanted to also skip getter methods of ``MyClass``::
+
+ numpydoc_validation_exclude = {r"mypkg\.mymodule\.", r"MyClass\.get$"}
+
+ The default is an empty set meaning no objects are excluded from docstring
+ validation.
+ Only has an effect when docstring validation is activated, i.e.
+ ``numpydoc_validation_checks`` is not an empty set.
numpydoc_edit_link : bool
.. deprecated:: 0.7.0
diff --git a/doc/validation.rst b/doc/validation.rst
index 3672d83..e1b2ee0 100644
--- a/doc/validation.rst
+++ b/doc/validation.rst
@@ -15,3 +15,21 @@ For an exhaustive validation of the formatting of the docstring, use the
``--validate`` parameter. This will report the errors detected, such as
incorrect capitalization, wrong order of the sections, and many other
issues.
+
+.. _validation_checks:
+
+Built-in Validation Checks
+--------------------------
+
+The ``numpydoc.validation`` module provides a mapping with all of the checks
+that are run as part of the validation procedure.
+The mapping is of the form: ``error_code : <explanation>`` where ``error_code``
+provides a shorthand for the check being run, and ``<explanation>`` provides
+a more detailed message. For example::
+
+ "EX01" : "No examples section found"
+
+The full mapping of validation checks is given below.
+
+.. literalinclude:: ../numpydoc/validate.py
+ :lines: 36-90
diff --git a/numpydoc/__main__.py b/numpydoc/__main__.py
index 534e9e0..f342cc3 100644
--- a/numpydoc/__main__.py
+++ b/numpydoc/__main__.py
@@ -6,13 +6,13 @@ import argparse
import ast
from .docscrape_sphinx import get_doc_object
-from .validate import validate, Docstring
+from .validate import validate, Validator
def render_object(import_path, config=None):
"""Test numpydoc docstring generation for a given object"""
- # TODO: Move Docstring._load_obj to a better place than validate
- print(get_doc_object(Docstring(import_path).obj,
+ # TODO: Move Validator._load_obj to a better place than validate
+ print(get_doc_object(Validator._load_obj(import_path),
config=dict(config or [])))
return 0
diff --git a/numpydoc/docscrape.py b/numpydoc/docscrape.py
index 15b4e10..395801e 100644
--- a/numpydoc/docscrape.py
+++ b/numpydoc/docscrape.py
@@ -411,8 +411,16 @@ class NumpyDocString(Mapping):
else:
self[section] = content
+ @property
+ def _obj(self):
+ if hasattr(self, '_cls'):
+ return self._cls
+ elif hasattr(self, '_f'):
+ return self._f
+ return None
+
def _error_location(self, msg, error=True):
- if hasattr(self, '_obj') and self._obj is not None:
+ if self._obj is not None:
# we know where the docs came from:
try:
filename = inspect.getsourcefile(self._obj)
@@ -581,6 +589,12 @@ class FunctionDoc(NumpyDocString):
return out
+class ObjDoc(NumpyDocString):
+ def __init__(self, obj, doc=None, config={}):
+ self._f = obj
+ NumpyDocString.__init__(self, doc, config=config)
+
+
class ClassDoc(NumpyDocString):
extra_public_methods = ['__call__']
@@ -663,3 +677,24 @@ class ClassDoc(NumpyDocString):
if name not in self._cls.__dict__:
return False # class member is inherited, we do not show it
return True
+
+
+def get_doc_object(obj, what=None, doc=None, config={}):
+ 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 what == 'class':
+ return ClassDoc(obj, func_doc=FunctionDoc, doc=doc, config=config)
+ elif what in ('function', 'method'):
+ return FunctionDoc(obj, doc=doc, config=config)
+ else:
+ if doc is None:
+ doc = pydoc.getdoc(obj)
+ return ObjDoc(obj, doc, config=config)
diff --git a/numpydoc/docscrape_sphinx.py b/numpydoc/docscrape_sphinx.py
index 38a49ba..b109fe7 100644
--- a/numpydoc/docscrape_sphinx.py
+++ b/numpydoc/docscrape_sphinx.py
@@ -10,7 +10,7 @@ from jinja2.sandbox import SandboxedEnvironment
import sphinx
from sphinx.jinja2glue import BuiltinTemplateLoader
-from .docscrape import NumpyDocString, FunctionDoc, ClassDoc
+from .docscrape import NumpyDocString, FunctionDoc, ClassDoc, ObjDoc
from .xref import make_xref
@@ -229,14 +229,6 @@ class SphinxDocString(NumpyDocString):
return out
- @property
- def _obj(self):
- if hasattr(self, '_cls'):
- return self._cls
- elif hasattr(self, '_f'):
- return self._f
- return None
-
def _str_member_list(self, name):
"""
Generate a member listing, autosummary:: table where possible,
@@ -411,13 +403,13 @@ class SphinxClassDoc(SphinxDocString, ClassDoc):
ClassDoc.__init__(self, obj, doc=doc, func_doc=None, config=config)
-class SphinxObjDoc(SphinxDocString):
+class SphinxObjDoc(SphinxDocString, ObjDoc):
def __init__(self, obj, doc=None, config={}):
- self._f = obj
self.load_config(config)
- SphinxDocString.__init__(self, doc, config=config)
+ 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={}, builder=None):
if what is None:
if inspect.isclass(obj):
diff --git a/numpydoc/numpydoc.py b/numpydoc/numpydoc.py
index 93cd975..87a8168 100644
--- a/numpydoc/numpydoc.py
+++ b/numpydoc/numpydoc.py
@@ -34,6 +34,7 @@ if sphinx.__version__ < '1.6.5':
raise RuntimeError("Sphinx 1.6.5 or newer is required")
from .docscrape_sphinx import get_doc_object
+from .validate import validate, ERROR_MSGS
from .xref import DEFAULT_LINKS
from . import __version__
@@ -173,6 +174,28 @@ def mangle_docstrings(app, what, name, obj, options, lines):
logger.error('[numpydoc] While processing docstring for %r', name)
raise
+ if app.config.numpydoc_validation_checks:
+ # If the user has supplied patterns to ignore via the
+ # numpydoc_validation_exclude config option, skip validation for
+ # any objs whose name matches any of the patterns
+ excluder = app.config.numpydoc_validation_excluder
+ exclude_from_validation = excluder.search(name) if excluder else False
+ if not exclude_from_validation:
+ # 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"]
+ if {err[0] for err in errors} & app.config.numpydoc_validation_checks:
+ msg = (
+ f"[numpydoc] Validation warnings while processing "
+ f"docstring for {name!r}:\n"
+ )
+ for err in errors:
+ if err[0] in app.config.numpydoc_validation_checks:
+ msg += f" {err[0]}: {err[1]}\n"
+ logger.warning(msg)
+
+
if (app.config.numpydoc_edit_link and hasattr(obj, '__name__') and
obj.__name__):
if hasattr(obj, '__module__'):
@@ -254,6 +277,8 @@ def setup(app, get_doc_object_=get_doc_object):
app.add_config_value('numpydoc_xref_param_type', False, True)
app.add_config_value('numpydoc_xref_aliases', dict(), True)
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)
# Extra mangling domains
app.add_domain(NumpyPythonDomain)
@@ -278,6 +303,33 @@ def update_config(app, config=None):
numpydoc_xref_aliases_complete[key] = value
config.numpydoc_xref_aliases_complete = numpydoc_xref_aliases_complete
+ # 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}"
+ )
+
+ # Generate the regexp for docstrings to ignore during validation
+ if isinstance(config.numpydoc_validation_exclude, str):
+ raise ValueError(
+ f"numpydoc_validation_exclude must be a container of strings, "
+ f"e.g. [{config.numpydoc_validation_exclude!r}]."
+ )
+ config.numpydoc_validation_excluder = None
+ if config.numpydoc_validation_exclude:
+ exclude_expr = re.compile(
+ r"|".join(exp for exp in config.numpydoc_validation_exclude)
+ )
+ config.numpydoc_validation_excluder = exclude_expr
+
# ------------------------------------------------------------------------------
# Docstring-mangling domains
diff --git a/numpydoc/tests/test_docscrape.py b/numpydoc/tests/test_docscrape.py
index 080e8ce..0706b6d 100644
--- a/numpydoc/tests/test_docscrape.py
+++ b/numpydoc/tests/test_docscrape.py
@@ -1497,6 +1497,9 @@ def test_xref():
def __init__(self, a, b):
self.numpydoc_xref_aliases = a
self.numpydoc_xref_aliases_complete = b
+ # numpydoc.update_config fails if this config option not present
+ self.numpydoc_validation_checks = set()
+ self.numpydoc_validation_exclude = set()
xref_aliases_complete = deepcopy(DEFAULT_LINKS)
for key in xref_aliases:
diff --git a/numpydoc/tests/test_numpydoc.py b/numpydoc/tests/test_numpydoc.py
index 77e7540..26ff3d2 100644
--- a/numpydoc/tests/test_numpydoc.py
+++ b/numpydoc/tests/test_numpydoc.py
@@ -1,8 +1,13 @@
# -*- encoding:utf-8 -*-
+import pytest
+from io import StringIO
from copy import deepcopy
-from numpydoc.numpydoc import mangle_docstrings, _clean_text_signature
+from numpydoc.numpydoc import (
+ mangle_docstrings, _clean_text_signature, update_config
+)
from numpydoc.xref import DEFAULT_LINKS
from sphinx.ext.autodoc import ALL
+from sphinx.util import logging
class MockConfig():
@@ -19,6 +24,8 @@ class MockConfig():
numpydoc_edit_link = False
numpydoc_citation_re = '[a-z0-9_.-]+'
numpydoc_attributes_as_param_list = True
+ numpydoc_validation_checks = set()
+ numpydoc_validation_exclude = set()
class MockBuilder():
@@ -30,9 +37,12 @@ class MockApp():
builder = MockBuilder()
translator = None
-
-app = MockApp()
-app.builder.app = app
+ def __init__(self):
+ self.builder.app = self
+ # Attrs required for logging
+ self.verbosity = 2
+ self._warncount = 0
+ self.warningiserror = False
def test_mangle_docstrings():
@@ -92,6 +102,99 @@ def test_clean_text_signature():
assert _clean_text_signature('func($self, *args)') == 'func(*args)'
+@pytest.fixture
+def f():
+ def _function_without_seealso_and_examples():
+ """
+ A function whose docstring has no examples or see also section.
+
+ Expect SA01 and EX01 errors if validation enabled.
+ """
+ pass
+ return _function_without_seealso_and_examples
+
+
+@pytest.mark.parametrize(
+ (
+ 'numpydoc_validation_checks',
+ 'expected_warn',
+ 'non_warnings',
+ ),
+ (
+ # Validation configured off - expect no warnings
+ (set(), [], []),
+ # Validation on with expected warnings
+ (set(['SA01', 'EX01']), ('SA01', 'EX01'), []),
+ # Validation on with only one activated check
+ (set(['SA01']), ('SA01',), ('EX01',)),
+ ),
+)
+def test_mangle_docstring_validation_warnings(
+ f,
+ numpydoc_validation_checks,
+ expected_warn,
+ non_warnings,
+):
+ app = MockApp()
+ # Set up config for test
+ app.config.numpydoc_validation_checks = numpydoc_validation_checks
+ # Update configuration
+ update_config(app)
+ # Set up logging
+ status, warning = StringIO(), StringIO()
+ logging.setup(app, status, warning)
+ # Run mangle docstrings with the above configuration
+ mangle_docstrings(app, 'function', 'f', f, None, f.__doc__.split('\n'))
+ # Assert that all (and only) expected warnings are logged
+ warnings = warning.getvalue()
+ for w in expected_warn:
+ assert w in warnings
+ for w in non_warnings:
+ assert w not in warnings
+
+
+def test_mangle_docstring_validation_exclude():
+ def function_with_bad_docstring():
+ """
+ This docstring will raise docstring validation warnings."""
+ app = MockApp()
+ app.config.numpydoc_validation_checks = {"all"}
+ app.config.numpydoc_validation_exclude = [r"_bad_"]
+ # Call update_config to construct regexp from config value
+ update_config(app)
+ # Setup for catching warnings
+ status, warning = StringIO(), StringIO()
+ logging.setup(app, status, warning)
+ # Run mangle docstrings on function_with_bad_docstring
+ mangle_docstrings(
+ app,
+ 'function',
+ function_with_bad_docstring.__name__,
+ function_with_bad_docstring,
+ None,
+ function_with_bad_docstring.__doc__.split('\n'),
+ )
+ # Validation is skipped due to exclude pattern matching fn name, therefore
+ # no warnings expected
+ assert warning.getvalue() == ""
+
+
+def test_update_config_invalid_validation_set():
+ app = MockApp()
+ # Results in {'a', 'l'} instead of {"all"}
+ app.config.numpydoc_validation_checks = set("all")
+ with pytest.raises(ValueError, match="Unrecognized validation code"):
+ update_config(app)
+
+
+def test_update_config_exclude_str():
+ app = MockApp()
+ app.config.numpydoc_validation_checks = set()
+ app.config.numpydoc_validation_exclude = "shouldnt-be-a-str"
+ with pytest.raises(ValueError, match="\['shouldnt-be-a-str'\]"):
+ update_config(app)
+
+
if __name__ == "__main__":
import pytest
pytest.main()
diff --git a/numpydoc/tests/test_validate.py b/numpydoc/tests/test_validate.py
index b8f4d87..704a25e 100644
--- a/numpydoc/tests/test_validate.py
+++ b/numpydoc/tests/test_validate.py
@@ -1312,12 +1312,12 @@ class TestValidator:
assert msg in " ".join(err[1] for err in result["errors"])
-class TestDocstringClass:
+class TestValidatorClass:
@pytest.mark.parametrize("invalid_name", ["unknown_mod", "unknown_mod.MyClass"])
def test_raises_for_invalid_module_name(self, invalid_name):
msg = 'No module can be imported from "{}"'.format(invalid_name)
with pytest.raises(ImportError, match=msg):
- numpydoc.validate.Docstring(invalid_name)
+ numpydoc.validate.Validator._load_obj(invalid_name)
@pytest.mark.parametrize(
"invalid_name", ["datetime.BadClassName", "datetime.bad_method_name"]
@@ -1327,4 +1327,4 @@ class TestDocstringClass:
obj_name, invalid_attr_name = name_components[-2], name_components[-1]
msg = "'{}' has no attribute '{}'".format(obj_name, invalid_attr_name)
with pytest.raises(AttributeError, match=msg):
- numpydoc.validate.Docstring(invalid_name)
+ numpydoc.validate.Validator._load_obj(invalid_name)
diff --git a/numpydoc/validate.py b/numpydoc/validate.py
index be50885..b33ea78 100644
--- a/numpydoc/validate.py
+++ b/numpydoc/validate.py
@@ -12,7 +12,7 @@ import inspect
import pydoc
import re
import textwrap
-from .docscrape import NumpyDocString
+from .docscrape import get_doc_object
DIRECTIVES = ["versionadded", "versionchanged", "deprecated"]
@@ -34,58 +34,58 @@ ALLOWED_SECTIONS = [
]
ERROR_MSGS = {
"GL01": "Docstring text (summary) should start in the line immediately "
- "after the opening quotes (not in the same line, or leaving a "
- "blank line in between)",
+ "after the opening quotes (not in the same line, or leaving a "
+ "blank line in between)",
"GL02": "Closing quotes should be placed in the line after the last text "
- "in the docstring (do not close the quotes in the same line as "
- "the text, or leave a blank line between the last text and the "
- "quotes)",
+ "in the docstring (do not close the quotes in the same line as "
+ "the text, or leave a blank line between the last text and the "
+ "quotes)",
"GL03": "Double line break found; please use only one blank line to "
- "separate sections or paragraphs, and do not leave blank lines "
- "at the end of docstrings",
+ "separate sections or paragraphs, and do not leave blank lines "
+ "at the end of docstrings",
"GL05": 'Tabs found at the start of line "{line_with_tabs}", please use '
- "whitespace only",
+ "whitespace only",
"GL06": 'Found unknown section "{section}". Allowed sections are: '
- "{allowed_sections}",
+ "{allowed_sections}",
"GL07": "Sections are in the wrong order. Correct order is: {correct_sections}",
"GL08": "The object does not have a docstring",
"GL09": "Deprecation warning should precede extended summary",
"GL10": "reST directives {directives} must be followed by two colons",
"SS01": "No summary found (a short summary in a single line should be "
- "present at the beginning of the docstring)",
+ "present at the beginning of the docstring)",
"SS02": "Summary does not start with a capital letter",
"SS03": "Summary does not end with a period",
"SS04": "Summary contains heading whitespaces",
"SS05": "Summary must start with infinitive verb, not third person "
- '(e.g. use "Generate" instead of "Generates")',
+ '(e.g. use "Generate" instead of "Generates")',
"SS06": "Summary should fit in a single line",
"ES01": "No extended summary found",
"PR01": "Parameters {missing_params} not documented",
"PR02": "Unknown parameters {unknown_params}",
"PR03": "Wrong parameters order. Actual: {actual_params}. "
- "Documented: {documented_params}",
+ "Documented: {documented_params}",
"PR04": 'Parameter "{param_name}" has no type',
"PR05": 'Parameter "{param_name}" type should not finish with "."',
"PR06": 'Parameter "{param_name}" type should use "{right_type}" instead '
- 'of "{wrong_type}"',
+ 'of "{wrong_type}"',
"PR07": 'Parameter "{param_name}" has no description',
"PR08": 'Parameter "{param_name}" description should start with a '
- "capital letter",
+ "capital letter",
"PR09": 'Parameter "{param_name}" description should finish with "."',
"PR10": 'Parameter "{param_name}" requires a space before the colon '
- "separating the parameter name and type",
+ "separating the parameter name and type",
"RT01": "No Returns section found",
"RT02": "The first line of the Returns section should contain only the "
- "type, unless multiple values are being returned",
+ "type, unless multiple values are being returned",
"RT03": "Return value has no description",
"RT04": "Return value description should start with a capital letter",
"RT05": 'Return value description should finish with "."',
"YD01": "No Yields section found",
"SA01": "See Also section not found",
"SA02": "Missing period at end of description for See Also "
- '"{reference_name}" reference',
+ '"{reference_name}" reference',
"SA03": "Description should be capitalized for See Also "
- '"{reference_name}" reference',
+ '"{reference_name}" reference',
"SA04": 'Missing description for See Also "{reference_name}" reference',
"EX01": "No examples section found",
}
@@ -121,16 +121,18 @@ def error(code, **kwargs):
return (code, ERROR_MSGS[code].format(**kwargs))
-class Docstring:
+class Validator:
# TODO Can all this class be merged into NumpyDocString?
- def __init__(self, name):
- self.name = name
- obj = self._load_obj(name)
- self.obj = obj
- self.code_obj = inspect.unwrap(obj)
- self.raw_doc = obj.__doc__ or ""
- self.clean_doc = pydoc.getdoc(obj)
- self.doc = NumpyDocString(self.clean_doc)
+ def __init__(self, doc_object):
+ self.doc = doc_object
+ self.obj = self.doc._obj
+ self.code_obj = inspect.unwrap(self.obj)
+ self.raw_doc = self.obj.__doc__ or ""
+ self.clean_doc = pydoc.getdoc(self.obj)
+
+ @property
+ def name(self):
+ return '.'.join([self.obj.__module__, self.obj.__name__])
@staticmethod
def _load_obj(name):
@@ -149,7 +151,7 @@ class Docstring:
Examples
--------
- >>> Docstring._load_obj('datetime.datetime')
+ >>> Validator._load_obj('datetime.datetime')
<class 'datetime.datetime'>
"""
for maxsplit in range(0, name.count(".") + 1):
@@ -468,7 +470,10 @@ def validate(obj_name):
they are validated, are not documented more than in the source code of this
function.
"""
- doc = Docstring(obj_name)
+ if isinstance(obj_name, str):
+ doc = Validator(get_doc_object(Validator._load_obj(obj_name)))
+ else:
+ doc = Validator(obj_name)
errs = []
if not doc.raw_doc: