File: C:/Users/fred/anaconda3/Lib/site-packages/numpydoc/tests/test_numpydoc.py
import pytest
from collections import defaultdict
from io import StringIO
from pathlib import PosixPath
from copy import deepcopy
from docutils import nodes
from numpydoc.numpydoc import (
mangle_docstrings,
_clean_text_signature,
update_config,
clean_backrefs,
)
from numpydoc.xref import DEFAULT_LINKS
from sphinx.ext.autodoc import ALL
from sphinx.util import logging
class MockConfig:
numpydoc_use_plots = False
numpydoc_show_class_members = True
numpydoc_show_inherited_class_members = True
numpydoc_class_members_toctree = True
numpydoc_xref_param_type = False
numpydoc_xref_aliases = {}
numpydoc_xref_aliases_complete = deepcopy(DEFAULT_LINKS)
numpydoc_xref_ignore = set()
templates_path = []
numpydoc_citation_re = "[a-z0-9_.-]+"
numpydoc_attributes_as_param_list = True
numpydoc_validation_checks = set()
numpydoc_validation_exclude = set()
numpydoc_validation_overrides = dict()
class MockBuilder:
config = MockConfig()
class MockApp:
config = MockConfig()
builder = MockBuilder()
translator = None
def __init__(self):
self.builder.app = self
# Attrs required for logging
self.verbosity = 2
self._warncount = 0
self.warningiserror = False
def test_mangle_docstrings_basic():
s = """
A top section before
.. autoclass:: str
"""
lines = s.split("\n")
mangle_docstrings(MockApp(), "class", "str", str, {}, lines)
assert "rpartition" in [x.strip() for x in lines]
lines = s.split("\n")
mangle_docstrings(MockApp(), "class", "str", str, {"members": ["upper"]}, lines)
assert "rpartition" not in [x.strip() for x in lines]
assert "upper" in [x.strip() for x in lines]
lines = s.split("\n")
mangle_docstrings(MockApp(), "class", "str", str, {"exclude-members": ALL}, lines)
assert "rpartition" not in [x.strip() for x in lines]
assert "upper" not in [x.strip() for x in lines]
lines = s.split("\n")
mangle_docstrings(
MockApp(), "class", "str", str, {"exclude-members": ["upper"]}, lines
)
assert "rpartition" in [x.strip() for x in lines]
assert "upper" not in [x.strip() for x in lines]
def test_mangle_docstrings_inherited_class_members():
# if subclass docs are rendered, this PosixPath should have Path.samefile
p = """
A top section before
.. autoclass:: pathlib.PosixPath
"""
lines = p.split("\n")
app = MockApp()
mangle_docstrings(app, "class", "pathlib.PosixPath", PosixPath, {}, lines)
lines = [x.strip() for x in lines]
assert "samefile" in lines
app.config.numpydoc_show_inherited_class_members = False
lines = p.split("\n")
mangle_docstrings(app, "class", "pathlib.PosixPath", PosixPath, {}, lines)
lines = [x.strip() for x in lines]
assert "samefile" not in lines
app.config.numpydoc_show_inherited_class_members = dict()
lines = p.split("\n")
mangle_docstrings(app, "class", "pathlib.PosixPath", PosixPath, {}, lines)
lines = [x.strip() for x in lines]
assert "samefile" in lines
app.config.numpydoc_show_inherited_class_members = defaultdict(bool)
lines = p.split("\n")
mangle_docstrings(app, "class", "pathlib.PosixPath", PosixPath, {}, lines)
lines = [x.strip() for x in lines]
assert "samefile" not in lines
def test_clean_text_signature():
assert _clean_text_signature(None) is None
assert _clean_text_signature("func($self)") == "func()"
assert (
_clean_text_signature("func($self, *args, **kwargs)") == "func(*args, **kwargs)"
)
assert _clean_text_signature("($self)") == "()"
assert _clean_text_signature("()") == "()"
assert _clean_text_signature("func()") == "func()"
assert (
_clean_text_signature("func($self, /, *args, **kwargs)")
== "func(*args, **kwargs)"
)
assert (
_clean_text_signature("func($self, other, /, *args, **kwargs)")
== "func(other, *args, **kwargs)"
)
assert _clean_text_signature("($module)") == "()"
assert _clean_text_signature("func($type)") == "func()"
assert (
_clean_text_signature('func($self, foo="hello world")')
== 'func(foo="hello world")'
)
assert (
_clean_text_signature("func($self, foo='hello world')")
== "func(foo='hello world')"
)
assert _clean_text_signature('func(foo="hello world")') == 'func(foo="hello world")'
assert _clean_text_signature('func(foo="$self")') == 'func(foo="$self")'
assert _clean_text_signature('func($self, foo="$self")') == 'func(foo="$self")'
assert _clean_text_signature("func(self, other)") == "func(self, other)"
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
({"SA01", "EX01"}, ("SA01", "EX01"), []),
# Validation on with only one activated check
({"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() == ""
@pytest.mark.parametrize("overrides", [{}, {"SS02"}, {"SS02", "SS03"}])
def test_mangle_docstrings_overrides(overrides):
def process_something_noop_function():
"""Process something."""
app = MockApp()
app.config.numpydoc_validation_checks = {"all"}
app.config.numpydoc_validation_overrides = {
check: [r"^Process "] # overrides are regex on docstring content
for check in overrides
}
update_config(app)
# Setup for catching warnings
status, warning = StringIO(), StringIO()
logging.setup(app, status, warning)
# Run mangle docstrings on process_something_noop_function
mangle_docstrings(
app,
"function",
process_something_noop_function.__name__,
process_something_noop_function,
None,
process_something_noop_function.__doc__.split("\n"),
)
findings = warning.getvalue()
assert " EX01: " in findings # should always be there
for check in overrides:
assert f" {check}: " not in findings
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=r"\['shouldnt-be-a-str'\]"):
update_config(app)
def test_clean_backrefs():
"""Check ids are not cleaned from inline backrefs."""
par = nodes.paragraph(rawsource="", text="")
inline_ref = nodes.inline(rawsource="", text="", ids=["id1"])
inline_ref += nodes.reference(rawsource="", text="[1]", refid="r123-1")
citation = nodes.citation(
rawsource="", docname="index", backrefs=["id1"], ids=["r123-1"]
)
citation += nodes.label("1")
citation += nodes.paragraph(rawsource="", text="Author. Title.")
par += inline_ref
par += citation
clean_backrefs(app=MockApp(), doc=par, docname="index")
assert "id1" in citation["backrefs"]
if __name__ == "__main__":
import pytest
pytest.main()