sphinx-autodoc-typehints
sphinx-autodoc-typehints copied to clipboard
2.1.0: pytest fails in `tests/test_sphinx_autodoc_typehints.py::test_always_document_param_types[doc_param_type]` unit and pytest warnings
I'm packaging your module as an rpm package so I'm using the typical PEP517 based build, install and test cycle used on building packages from non-root account.
python3 -sBm build -w --no-isolation- because I'm calling
buildwith--no-isolationI'm using during all processes only locally installed modules - install .whl file in </install/prefix> using
installermodule - run pytest with $PYTHONPATH pointing to sitearch and sitelib inside </install/prefix>
- build is performed in env which is
cut off from access to the public network(pytest is executed with-m "not network")
Here is pytest output:
+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-sphinx-autodoc-typehints-2.1.0-2.fc37.x86_64/usr/lib64/python3.10/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-sphinx-autodoc-typehints-2.1.0-2.fc37.x86_64/usr/lib/python3.10/site-packages
+ /usr/bin/pytest -ra -m 'not network'
==================================================================================== test session starts ====================================================================================
platform linux -- Python 3.10.14, pytest-8.1.1, pluggy-1.4.0
rootdir: /home/tkloczko/rpmbuild/BUILD/sphinx-autodoc-typehints-2.1.0
configfile: pyproject.toml
testpaths: tests
collected 306 items
tests/test_integration.py ........................................................................................................................................................ [ 49%]
tests/test_sphinx_autodoc_typehints.py ..............................................................................................................................F............... [ 96%]
........... [ 99%]
tests/test_version.py . [100%]
========================================================================================= FAILURES ==========================================================================================
_____________________________________________________________________ test_always_document_param_types[doc_param_type] ______________________________________________________________________
app = <SphinxTestApp buildername='text'>, status = <_io.StringIO object at 0x7f8ca2204670>, warning = <_io.StringIO object at 0x7f8ca22056c0>, always_document_param_types = True
@pytest.mark.parametrize("always_document_param_types", [True, False], ids=["doc_param_type", "no_doc_param_type"])
@pytest.mark.sphinx("text", testroot="dummy")
@patch("sphinx.writers.text.MAXWIDTH", 2000)
def test_always_document_param_types(
app: SphinxTestApp,
status: StringIO,
warning: StringIO,
always_document_param_types: bool,
) -> None:
set_python_path()
app.config.always_document_param_types = always_document_param_types # type: ignore[attr-defined] # create flag
app.config.autodoc_mock_imports = ["mailbox"] # type: ignore[attr-defined] # create flag
# Prevent "document isn't included in any toctree" warnings
for f in Path(app.srcdir).glob("*.rst"):
f.unlink()
(Path(app.srcdir) / "index.rst").write_text(
dedent(
"""
.. autofunction:: dummy_module.undocumented_function
.. autoclass:: dummy_module.DataClass
:undoc-members:
:special-members: __init__
""",
),
)
app.build()
assert "build succeeded" in status.getvalue() # Build succeeded
assert not warning.getvalue().strip()
format_args = {}
for indentation_level in range(2):
key = f"undoc_params_{indentation_level}"
if always_document_param_types:
format_args[key] = indent('\n\n Parameters:\n **x** ("int")', " " * indentation_level)
else:
format_args[key] = ""
contents = (Path(app.srcdir) / "_build/text/index.txt").read_text()
expected_contents = """\
dummy_module.undocumented_function(x)
Hi{undoc_params_0}
Return type:
"str"
class dummy_module.DataClass(x)
Class docstring.{undoc_params_0}
__init__(x){undoc_params_1}
"""
expected_contents = dedent(expected_contents).format(**format_args)
> assert contents == expected_contents
E assert 'dummy_module... ("int") --\n' == 'dummy_module...x** ("int")\n'
E
E Skipping 70 identical leading characters in diff, use -v to show
E - ** ("int")
E + ** ("int") --
E ? +++
E
E Return type:...
E
E ...Full output truncated (17 lines hidden), use '-vv' to show
tests/test_sphinx_autodoc_typehints.py:615: AssertionError
--------------------------------------------------------------------------------- Captured stdout teardown ----------------------------------------------------------------------------------
# testroot: root
# builder: text
# srcdir: /tmp/pytest-of-tkloczko/pytest-85/dummy
# outdir: /tmp/pytest-of-tkloczko/pytest-85/dummy/_build/text
# status:
Running Sphinx v7.2.6
building [mo]: targets for 0 po files that are out of date
writing output...
building [text]: targets for 5 source files that are out of date
updating environment: [new config] 1 added, 0 changed, 0 removed
reading sources... [100%] index
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
preparing documents... done
copying assets... done
writing output... [100%] index
build succeeded.
The text files are in ../../../../../tmp/pytest-of-tkloczko/pytest-85/dummy/_build/text.
# warning:
===================================================================================== warnings summary ======================================================================================
tests/conftest.py:10
/home/tkloczko/rpmbuild/BUILD/sphinx-autodoc-typehints-2.1.0/tests/conftest.py:10: RemovedInSphinx90Warning: 'sphinx.testing.path' is deprecated. Use 'os.path' or 'pathlib' instead.
from sphinx.testing.path import path
tests/test_integration.py: 112 warnings
tests/test_sphinx_autodoc_typehints.py: 82 warnings
/home/tkloczko/rpmbuild/BUILDROOT/python-sphinx-autodoc-typehints-2.1.0-2.fc37.x86_64/usr/lib/python3.10/site-packages/sphinx_autodoc_typehints/__init__.py:806: DeprecationWarning: The frontend.OptionParser class will be replaced by a subclass of argparse.ArgumentParser in Docutils 0.21 or later.
settings = OptionParser(components=(RSTParser,)).get_default_values()
tests/test_integration.py: 7728 warnings
tests/test_sphinx_autodoc_typehints.py: 5658 warnings
/usr/lib64/python3.10/optparse.py:1000: DeprecationWarning: The frontend.Option class will be removed in Docutils 0.21 or later.
option = self.option_class(*args, **kwargs)
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================================================================================== short test summary info ==================================================================================
FAILED tests/test_sphinx_autodoc_typehints.py::test_always_document_param_types[doc_param_type] - assert 'dummy_module... ("int") --\n' == 'dummy_module...x** ("int")\n'
====================================================================== 1 failed, 305 passed, 13581 warnings in 18.10s =======================================================================
List of installed modules in build env:
Package Version
----------------------------- -----------
alabaster 0.7.16
attrs 23.2.0
Babel 2.14.0
build 1.2.1
certifi 2023.7.22
charset-normalizer 3.3.2
docutils 0.20.1
exceptiongroup 1.1.3
hatch-vcs 0.4.0
hatchling 1.24.0
idna 3.7
imagesize 1.4.1
importlib_metadata 7.1.0
iniconfig 2.0.0
installer 0.7.0
Jinja2 3.1.3
jsonschema 4.20.0
jsonschema-specifications 2023.12.1
MarkupSafe 2.1.5
packaging 24.0
pathspec 0.12.1
pluggy 1.4.0
Pygments 2.17.2
pyproject_hooks 1.0.0
pytest 8.1.1
python-dateutil 2.9.0.post0
referencing 0.32.0
requests 2.31.0
rpds-py 0.18.0
setuptools 69.4.0
setuptools-scm 8.0.4
snowballstemmer 2.2.0
Sphinx 7.2.6
sphinxcontrib-applehelp 1.0.8
sphinxcontrib-devhelp 1.0.6
sphinxcontrib-htmlhelp 2.0.5
sphinxcontrib-jsmath 1.0.1
sphinxcontrib-qthelp 1.0.7
sphinxcontrib-serializinghtml 1.1.10
sphobjinv 2.3.1
tokenize_rt 5.2.0
tomli 2.0.1
trove-classifiers 2024.4.16
typing_extensions 4.11.0
urllib3 2.2.1
wheel 0.43.0
zipp 3.18.1
Please let me know if you need more details or want me to perform some diagnostics.
Gentle ping .. any update? 🤔
BTW looks like filtering sphinx-autodoc-typehints code over pyupgrade --py38-plus causes that test suite starts failing in new units.
Here is pytest output
+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-sphinx-autodoc-typehints-2.1.0-2.fc37.x86_64/usr/lib64/python3.10/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-sphinx-autodoc-typehints-2.1.0-2.fc37.x86_64/usr/lib/python3.10/site-packages
+ /usr/bin/pytest -ra -m 'not network'
==================================================================================== test session starts ====================================================================================
platform linux -- Python 3.10.14, pytest-8.1.1, pluggy-1.4.0
benchmark: 4.0.0 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
rootdir: /home/tkloczko/rpmbuild/BUILD/sphinx-autodoc-typehints-2.1.0
configfile: pyproject.toml
testpaths: tests
plugins: timeout-2.3.1, xdist-3.6.1, benchmark-4.0.0, hypothesis-6.100.0
collected 306 items
tests/test_integration.py ................FF....................................FF....................................FF....................................FF.................... [ 49%]
tests/test_sphinx_autodoc_typehints.py ..............................................................................................................................F............... [ 96%]
........... [ 99%]
tests/test_version.py . [100%]
========================================================================================= FAILURES ==========================================================================================
_____________________________________________________________________ test_integration[default_conf-func_with_overload] _____________________________________________________________________
app = <SphinxTestApp buildername='text'>, status = <_io.StringIO object at 0x7f57e6959090>, warning = <_io.StringIO object at 0x7f57e6959240>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f57e69c4760>, val = <function func_with_overload at 0x7f57e7a71360>, conf_run = 'default_conf'
@pytest.mark.parametrize("val", [x for x in globals().values() if hasattr(x, "EXPECTED")])
@pytest.mark.parametrize("conf_run", ["default_conf", "prolog_conf", "epilog_conf", "bothlog_conf"])
@pytest.mark.sphinx("text", testroot="integration")
def test_integration(
app: SphinxTestApp, status: StringIO, warning: StringIO, monkeypatch: pytest.MonkeyPatch, val: Any, conf_run: str
) -> None:
if isclass(val) and issubclass(val, BaseException):
template = AUTO_EXCEPTION
elif isclass(val):
template = AUTO_CLASS
else:
template = AUTO_FUNCTION
(Path(app.srcdir) / "index.rst").write_text(template.format(val.__name__))
app.config.__dict__.update(configs[conf_run])
app.config.__dict__.update(val.OPTIONS)
monkeypatch.setitem(sys.modules, "mod", sys.modules[__name__])
app.build()
assert "build succeeded" in status.getvalue() # Build succeeded
regexp = getattr(val, "WARNING", None)
value = warning.getvalue().strip()
if regexp:
msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}"
assert re.search(regexp, value), msg
else:
assert not value
result = (Path(app.srcdir) / "_build/text/index.txt").read_text()
expected = val.EXPECTED
if LT_PY310:
expected = expected.replace("NewType", "NewType()")
try:
> assert result.strip() == dedent(expected).strip()
E assert 'mod.func_wit...n "None"' == 'mod.func_wit...n "None"'
E
E Skipping 159 identical leading characters in diff, use -v to show
E - * **a** ("Union"["int", "str"]) -- The first thing
E ? -------- ^ -
E + * **a** ("int" | "str") -- The first thing
E ? ^^
E ...
E
E ...Full output truncated (7 lines hidden), use '-vv' to show
tests/test_integration.py:1394: AssertionError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
@expected(
"""
mod.func_with_overload(a, b)
f does the thing. The arguments can either be ints or strings but
they must both have the same type.
Parameters:
* **a** ("int" | "str") -- The first thing
* **b** ("int" | "str") -- The second thing
Return type:
"None"
"""
)
--------------------------------------------------------------------------------- Captured stdout teardown ----------------------------------------------------------------------------------
# testroot: root
# builder: text
# srcdir: /tmp/pytest-of-tkloczko/pytest-140/integration
# outdir: /tmp/pytest-of-tkloczko/pytest-140/integration/_build/text
# status:
Running Sphinx v7.2.6
building [mo]: targets for 0 po files that are out of date
writing output...
building [text]: targets for 0 source files that are out of date
updating environment: [new config] 1 added, 0 changed, 0 removed
reading sources... [100%] index
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
preparing documents... done
copying assets... done
writing output... [100%] index
build succeeded.
The text files are in ../../../../../tmp/pytest-of-tkloczko/pytest-140/integration/_build/text.
# warning:
___________________________________________________________________ test_integration[default_conf-TestClassAttributeDocs] ___________________________________________________________________
app = <SphinxTestApp buildername='text'>, status = <_io.StringIO object at 0x7f57e6a06830>, warning = <_io.StringIO object at 0x7f57e6a075b0>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f57e6796bf0>, val = <class 'test_integration.TestClassAttributeDocs'>, conf_run = 'default_conf'
@pytest.mark.parametrize("val", [x for x in globals().values() if hasattr(x, "EXPECTED")])
@pytest.mark.parametrize("conf_run", ["default_conf", "prolog_conf", "epilog_conf", "bothlog_conf"])
@pytest.mark.sphinx("text", testroot="integration")
def test_integration(
app: SphinxTestApp, status: StringIO, warning: StringIO, monkeypatch: pytest.MonkeyPatch, val: Any, conf_run: str
) -> None:
if isclass(val) and issubclass(val, BaseException):
template = AUTO_EXCEPTION
elif isclass(val):
template = AUTO_CLASS
else:
template = AUTO_FUNCTION
(Path(app.srcdir) / "index.rst").write_text(template.format(val.__name__))
app.config.__dict__.update(configs[conf_run])
app.config.__dict__.update(val.OPTIONS)
monkeypatch.setitem(sys.modules, "mod", sys.modules[__name__])
app.build()
assert "build succeeded" in status.getvalue() # Build succeeded
regexp = getattr(val, "WARNING", None)
value = warning.getvalue().strip()
if regexp:
msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}"
assert re.search(regexp, value), msg
else:
assert not value
result = (Path(app.srcdir) / "_build/text/index.txt").read_text()
expected = val.EXPECTED
if LT_PY310:
expected = expected.replace("NewType", "NewType()")
try:
> assert result.strip() == dedent(expected).strip()
E assert 'class mod.Te... An attribute' == 'class mod.Te... An attribute'
E
E Skipping 46 identical leading characters in diff, use -v to show
E - code: "Optional"["CodeType"]
E + code: "CodeType" | "None"
E
E An attribute
tests/test_integration.py:1394: AssertionError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
@expected(
"""
class mod.TestClassAttributeDocs
A class
code: "CodeType" | "None"
An attribute
"""
)
--------------------------------------------------------------------------------- Captured stdout teardown ----------------------------------------------------------------------------------
# testroot: root
# builder: text
# srcdir: /tmp/pytest-of-tkloczko/pytest-140/integration
# outdir: /tmp/pytest-of-tkloczko/pytest-140/integration/_build/text
# status:
Running Sphinx v7.2.6
building [mo]: targets for 0 po files that are out of date
writing output...
building [text]: targets for 0 source files that are out of date
updating environment: [new config] 1 added, 0 changed, 0 removed
reading sources... [100%] index
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
preparing documents... done
copying assets... done
writing output... [100%] index
build succeeded.
The text files are in ../../../../../tmp/pytest-of-tkloczko/pytest-140/integration/_build/text.
# warning:
_____________________________________________________________________ test_integration[prolog_conf-func_with_overload] ______________________________________________________________________
app = <SphinxTestApp buildername='text'>, status = <_io.StringIO object at 0x7f57e6c9d5a0>, warning = <_io.StringIO object at 0x7f57e6959ea0>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f57e696b1f0>, val = <function func_with_overload at 0x7f57e7a71360>, conf_run = 'prolog_conf'
@pytest.mark.parametrize("val", [x for x in globals().values() if hasattr(x, "EXPECTED")])
@pytest.mark.parametrize("conf_run", ["default_conf", "prolog_conf", "epilog_conf", "bothlog_conf"])
@pytest.mark.sphinx("text", testroot="integration")
def test_integration(
app: SphinxTestApp, status: StringIO, warning: StringIO, monkeypatch: pytest.MonkeyPatch, val: Any, conf_run: str
) -> None:
if isclass(val) and issubclass(val, BaseException):
template = AUTO_EXCEPTION
elif isclass(val):
template = AUTO_CLASS
else:
template = AUTO_FUNCTION
(Path(app.srcdir) / "index.rst").write_text(template.format(val.__name__))
app.config.__dict__.update(configs[conf_run])
app.config.__dict__.update(val.OPTIONS)
monkeypatch.setitem(sys.modules, "mod", sys.modules[__name__])
app.build()
assert "build succeeded" in status.getvalue() # Build succeeded
regexp = getattr(val, "WARNING", None)
value = warning.getvalue().strip()
if regexp:
msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}"
assert re.search(regexp, value), msg
else:
assert not value
result = (Path(app.srcdir) / "_build/text/index.txt").read_text()
expected = val.EXPECTED
if LT_PY310:
expected = expected.replace("NewType", "NewType()")
try:
> assert result.strip() == dedent(expected).strip()
E assert 'mod.func_wit...n "None"' == 'mod.func_wit...n "None"'
E
E Skipping 159 identical leading characters in diff, use -v to show
E - * **a** ("Union"["int", "str"]) -- The first thing
E ? -------- ^ -
E + * **a** ("int" | "str") -- The first thing
E ? ^^
E ...
E
E ...Full output truncated (7 lines hidden), use '-vv' to show
tests/test_integration.py:1394: AssertionError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
@expected(
"""
mod.func_with_overload(a, b)
f does the thing. The arguments can either be ints or strings but
they must both have the same type.
Parameters:
* **a** ("int" | "str") -- The first thing
* **b** ("int" | "str") -- The second thing
Return type:
"None"
"""
)
--------------------------------------------------------------------------------- Captured stdout teardown ----------------------------------------------------------------------------------
# testroot: root
# builder: text
# srcdir: /tmp/pytest-of-tkloczko/pytest-140/integration
# outdir: /tmp/pytest-of-tkloczko/pytest-140/integration/_build/text
# status:
Running Sphinx v7.2.6
building [mo]: targets for 0 po files that are out of date
writing output...
building [text]: targets for 0 source files that are out of date
updating environment: [new config] 1 added, 0 changed, 0 removed
reading sources... [100%] index
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
preparing documents... done
copying assets... done
writing output... [100%] index
build succeeded.
The text files are in ../../../../../tmp/pytest-of-tkloczko/pytest-140/integration/_build/text.
# warning:
___________________________________________________________________ test_integration[prolog_conf-TestClassAttributeDocs] ____________________________________________________________________
app = <SphinxTestApp buildername='text'>, status = <_io.StringIO object at 0x7f57e695aa70>, warning = <_io.StringIO object at 0x7f57e695b7f0>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f57e633d6c0>, val = <class 'test_integration.TestClassAttributeDocs'>, conf_run = 'prolog_conf'
@pytest.mark.parametrize("val", [x for x in globals().values() if hasattr(x, "EXPECTED")])
@pytest.mark.parametrize("conf_run", ["default_conf", "prolog_conf", "epilog_conf", "bothlog_conf"])
@pytest.mark.sphinx("text", testroot="integration")
def test_integration(
app: SphinxTestApp, status: StringIO, warning: StringIO, monkeypatch: pytest.MonkeyPatch, val: Any, conf_run: str
) -> None:
if isclass(val) and issubclass(val, BaseException):
template = AUTO_EXCEPTION
elif isclass(val):
template = AUTO_CLASS
else:
template = AUTO_FUNCTION
(Path(app.srcdir) / "index.rst").write_text(template.format(val.__name__))
app.config.__dict__.update(configs[conf_run])
app.config.__dict__.update(val.OPTIONS)
monkeypatch.setitem(sys.modules, "mod", sys.modules[__name__])
app.build()
assert "build succeeded" in status.getvalue() # Build succeeded
regexp = getattr(val, "WARNING", None)
value = warning.getvalue().strip()
if regexp:
msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}"
assert re.search(regexp, value), msg
else:
assert not value
result = (Path(app.srcdir) / "_build/text/index.txt").read_text()
expected = val.EXPECTED
if LT_PY310:
expected = expected.replace("NewType", "NewType()")
try:
> assert result.strip() == dedent(expected).strip()
E assert 'class mod.Te... An attribute' == 'class mod.Te... An attribute'
E
E Skipping 46 identical leading characters in diff, use -v to show
E - code: "Optional"["CodeType"]
E + code: "CodeType" | "None"
E
E An attribute
tests/test_integration.py:1394: AssertionError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
@expected(
"""
class mod.TestClassAttributeDocs
A class
code: "CodeType" | "None"
An attribute
"""
)
--------------------------------------------------------------------------------- Captured stdout teardown ----------------------------------------------------------------------------------
# testroot: root
# builder: text
# srcdir: /tmp/pytest-of-tkloczko/pytest-140/integration
# outdir: /tmp/pytest-of-tkloczko/pytest-140/integration/_build/text
# status:
Running Sphinx v7.2.6
building [mo]: targets for 0 po files that are out of date
writing output...
building [text]: targets for 0 source files that are out of date
updating environment: [new config] 1 added, 0 changed, 0 removed
reading sources... [100%] index
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
preparing documents... done
copying assets... done
writing output... [100%] index
build succeeded.
The text files are in ../../../../../tmp/pytest-of-tkloczko/pytest-140/integration/_build/text.
# warning:
_____________________________________________________________________ test_integration[epilog_conf-func_with_overload] ______________________________________________________________________
app = <SphinxTestApp buildername='text'>, status = <_io.StringIO object at 0x7f57e6de7910>, warning = <_io.StringIO object at 0x7f57e66f3400>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f57e6511e70>, val = <function func_with_overload at 0x7f57e7a71360>, conf_run = 'epilog_conf'
@pytest.mark.parametrize("val", [x for x in globals().values() if hasattr(x, "EXPECTED")])
@pytest.mark.parametrize("conf_run", ["default_conf", "prolog_conf", "epilog_conf", "bothlog_conf"])
@pytest.mark.sphinx("text", testroot="integration")
def test_integration(
app: SphinxTestApp, status: StringIO, warning: StringIO, monkeypatch: pytest.MonkeyPatch, val: Any, conf_run: str
) -> None:
if isclass(val) and issubclass(val, BaseException):
template = AUTO_EXCEPTION
elif isclass(val):
template = AUTO_CLASS
else:
template = AUTO_FUNCTION
(Path(app.srcdir) / "index.rst").write_text(template.format(val.__name__))
app.config.__dict__.update(configs[conf_run])
app.config.__dict__.update(val.OPTIONS)
monkeypatch.setitem(sys.modules, "mod", sys.modules[__name__])
app.build()
assert "build succeeded" in status.getvalue() # Build succeeded
regexp = getattr(val, "WARNING", None)
value = warning.getvalue().strip()
if regexp:
msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}"
assert re.search(regexp, value), msg
else:
assert not value
result = (Path(app.srcdir) / "_build/text/index.txt").read_text()
expected = val.EXPECTED
if LT_PY310:
expected = expected.replace("NewType", "NewType()")
try:
> assert result.strip() == dedent(expected).strip()
E assert 'mod.func_wit...n "None"' == 'mod.func_wit...n "None"'
E
E Skipping 159 identical leading characters in diff, use -v to show
E - * **a** ("Union"["int", "str"]) -- The first thing
E ? -------- ^ -
E + * **a** ("int" | "str") -- The first thing
E ? ^^
E ...
E
E ...Full output truncated (7 lines hidden), use '-vv' to show
tests/test_integration.py:1394: AssertionError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
@expected(
"""
mod.func_with_overload(a, b)
f does the thing. The arguments can either be ints or strings but
they must both have the same type.
Parameters:
* **a** ("int" | "str") -- The first thing
* **b** ("int" | "str") -- The second thing
Return type:
"None"
"""
)
--------------------------------------------------------------------------------- Captured stdout teardown ----------------------------------------------------------------------------------
# testroot: root
# builder: text
# srcdir: /tmp/pytest-of-tkloczko/pytest-140/integration
# outdir: /tmp/pytest-of-tkloczko/pytest-140/integration/_build/text
# status:
Running Sphinx v7.2.6
building [mo]: targets for 0 po files that are out of date
writing output...
building [text]: targets for 0 source files that are out of date
updating environment: [new config] 1 added, 0 changed, 0 removed
reading sources... [100%] index
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
preparing documents... done
copying assets... done
writing output... [100%] index
build succeeded.
The text files are in ../../../../../tmp/pytest-of-tkloczko/pytest-140/integration/_build/text.
# warning:
___________________________________________________________________ test_integration[epilog_conf-TestClassAttributeDocs] ____________________________________________________________________
app = <SphinxTestApp buildername='text'>, status = <_io.StringIO object at 0x7f57e6a06c20>, warning = <_io.StringIO object at 0x7f57e6c9e5f0>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f57e61ba2f0>, val = <class 'test_integration.TestClassAttributeDocs'>, conf_run = 'epilog_conf'
@pytest.mark.parametrize("val", [x for x in globals().values() if hasattr(x, "EXPECTED")])
@pytest.mark.parametrize("conf_run", ["default_conf", "prolog_conf", "epilog_conf", "bothlog_conf"])
@pytest.mark.sphinx("text", testroot="integration")
def test_integration(
app: SphinxTestApp, status: StringIO, warning: StringIO, monkeypatch: pytest.MonkeyPatch, val: Any, conf_run: str
) -> None:
if isclass(val) and issubclass(val, BaseException):
template = AUTO_EXCEPTION
elif isclass(val):
template = AUTO_CLASS
else:
template = AUTO_FUNCTION
(Path(app.srcdir) / "index.rst").write_text(template.format(val.__name__))
app.config.__dict__.update(configs[conf_run])
app.config.__dict__.update(val.OPTIONS)
monkeypatch.setitem(sys.modules, "mod", sys.modules[__name__])
app.build()
assert "build succeeded" in status.getvalue() # Build succeeded
regexp = getattr(val, "WARNING", None)
value = warning.getvalue().strip()
if regexp:
msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}"
assert re.search(regexp, value), msg
else:
assert not value
result = (Path(app.srcdir) / "_build/text/index.txt").read_text()
expected = val.EXPECTED
if LT_PY310:
expected = expected.replace("NewType", "NewType()")
try:
> assert result.strip() == dedent(expected).strip()
E assert 'class mod.Te... An attribute' == 'class mod.Te... An attribute'
E
E Skipping 46 identical leading characters in diff, use -v to show
E - code: "Optional"["CodeType"]
E + code: "CodeType" | "None"
E
E An attribute
tests/test_integration.py:1394: AssertionError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
@expected(
"""
class mod.TestClassAttributeDocs
A class
code: "CodeType" | "None"
An attribute
"""
)
--------------------------------------------------------------------------------- Captured stdout teardown ----------------------------------------------------------------------------------
# testroot: root
# builder: text
# srcdir: /tmp/pytest-of-tkloczko/pytest-140/integration
# outdir: /tmp/pytest-of-tkloczko/pytest-140/integration/_build/text
# status:
Running Sphinx v7.2.6
building [mo]: targets for 0 po files that are out of date
writing output...
building [text]: targets for 0 source files that are out of date
updating environment: [new config] 1 added, 0 changed, 0 removed
reading sources... [100%] index
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
preparing documents... done
copying assets... done
writing output... [100%] index
build succeeded.
The text files are in ../../../../../tmp/pytest-of-tkloczko/pytest-140/integration/_build/text.
# warning:
_____________________________________________________________________ test_integration[bothlog_conf-func_with_overload] _____________________________________________________________________
app = <SphinxTestApp buildername='text'>, status = <_io.StringIO object at 0x7f57e695bd00>, warning = <_io.StringIO object at 0x7f57e695ab00>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f57e5f47e80>, val = <function func_with_overload at 0x7f57e7a71360>, conf_run = 'bothlog_conf'
@pytest.mark.parametrize("val", [x for x in globals().values() if hasattr(x, "EXPECTED")])
@pytest.mark.parametrize("conf_run", ["default_conf", "prolog_conf", "epilog_conf", "bothlog_conf"])
@pytest.mark.sphinx("text", testroot="integration")
def test_integration(
app: SphinxTestApp, status: StringIO, warning: StringIO, monkeypatch: pytest.MonkeyPatch, val: Any, conf_run: str
) -> None:
if isclass(val) and issubclass(val, BaseException):
template = AUTO_EXCEPTION
elif isclass(val):
template = AUTO_CLASS
else:
template = AUTO_FUNCTION
(Path(app.srcdir) / "index.rst").write_text(template.format(val.__name__))
app.config.__dict__.update(configs[conf_run])
app.config.__dict__.update(val.OPTIONS)
monkeypatch.setitem(sys.modules, "mod", sys.modules[__name__])
app.build()
assert "build succeeded" in status.getvalue() # Build succeeded
regexp = getattr(val, "WARNING", None)
value = warning.getvalue().strip()
if regexp:
msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}"
assert re.search(regexp, value), msg
else:
assert not value
result = (Path(app.srcdir) / "_build/text/index.txt").read_text()
expected = val.EXPECTED
if LT_PY310:
expected = expected.replace("NewType", "NewType()")
try:
> assert result.strip() == dedent(expected).strip()
E assert 'mod.func_wit...n "None"' == 'mod.func_wit...n "None"'
E
E Skipping 159 identical leading characters in diff, use -v to show
E - * **a** ("Union"["int", "str"]) -- The first thing
E ? -------- ^ -
E + * **a** ("int" | "str") -- The first thing
E ? ^^
E ...
E
E ...Full output truncated (7 lines hidden), use '-vv' to show
tests/test_integration.py:1394: AssertionError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
@expected(
"""
mod.func_with_overload(a, b)
f does the thing. The arguments can either be ints or strings but
they must both have the same type.
Parameters:
* **a** ("int" | "str") -- The first thing
* **b** ("int" | "str") -- The second thing
Return type:
"None"
"""
)
--------------------------------------------------------------------------------- Captured stdout teardown ----------------------------------------------------------------------------------
# testroot: root
# builder: text
# srcdir: /tmp/pytest-of-tkloczko/pytest-140/integration
# outdir: /tmp/pytest-of-tkloczko/pytest-140/integration/_build/text
# status:
Running Sphinx v7.2.6
building [mo]: targets for 0 po files that are out of date
writing output...
building [text]: targets for 0 source files that are out of date
updating environment: [new config] 1 added, 0 changed, 0 removed
reading sources... [100%] index
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
preparing documents... done
copying assets... done
writing output... [100%] index
build succeeded.
The text files are in ../../../../../tmp/pytest-of-tkloczko/pytest-140/integration/_build/text.
# warning:
___________________________________________________________________ test_integration[bothlog_conf-TestClassAttributeDocs] ___________________________________________________________________
app = <SphinxTestApp buildername='text'>, status = <_io.StringIO object at 0x7f57e6958e50>, warning = <_io.StringIO object at 0x7f57e695b0a0>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f57e609c850>, val = <class 'test_integration.TestClassAttributeDocs'>, conf_run = 'bothlog_conf'
@pytest.mark.parametrize("val", [x for x in globals().values() if hasattr(x, "EXPECTED")])
@pytest.mark.parametrize("conf_run", ["default_conf", "prolog_conf", "epilog_conf", "bothlog_conf"])
@pytest.mark.sphinx("text", testroot="integration")
def test_integration(
app: SphinxTestApp, status: StringIO, warning: StringIO, monkeypatch: pytest.MonkeyPatch, val: Any, conf_run: str
) -> None:
if isclass(val) and issubclass(val, BaseException):
template = AUTO_EXCEPTION
elif isclass(val):
template = AUTO_CLASS
else:
template = AUTO_FUNCTION
(Path(app.srcdir) / "index.rst").write_text(template.format(val.__name__))
app.config.__dict__.update(configs[conf_run])
app.config.__dict__.update(val.OPTIONS)
monkeypatch.setitem(sys.modules, "mod", sys.modules[__name__])
app.build()
assert "build succeeded" in status.getvalue() # Build succeeded
regexp = getattr(val, "WARNING", None)
value = warning.getvalue().strip()
if regexp:
msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}"
assert re.search(regexp, value), msg
else:
assert not value
result = (Path(app.srcdir) / "_build/text/index.txt").read_text()
expected = val.EXPECTED
if LT_PY310:
expected = expected.replace("NewType", "NewType()")
try:
> assert result.strip() == dedent(expected).strip()
E assert 'class mod.Te... An attribute' == 'class mod.Te... An attribute'
E
E Skipping 46 identical leading characters in diff, use -v to show
E - code: "Optional"["CodeType"]
E + code: "CodeType" | "None"
E
E An attribute
tests/test_integration.py:1394: AssertionError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
@expected(
"""
class mod.TestClassAttributeDocs
A class
code: "CodeType" | "None"
An attribute
"""
)
--------------------------------------------------------------------------------- Captured stdout teardown ----------------------------------------------------------------------------------
# testroot: root
# builder: text
# srcdir: /tmp/pytest-of-tkloczko/pytest-140/integration
# outdir: /tmp/pytest-of-tkloczko/pytest-140/integration/_build/text
# status:
Running Sphinx v7.2.6
building [mo]: targets for 0 po files that are out of date
writing output...
building [text]: targets for 0 source files that are out of date
updating environment: [new config] 1 added, 0 changed, 0 removed
reading sources... [100%] index
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
preparing documents... done
copying assets... done
writing output... [100%] index
build succeeded.
The text files are in ../../../../../tmp/pytest-of-tkloczko/pytest-140/integration/_build/text.
# warning:
_____________________________________________________________________ test_always_document_param_types[doc_param_type] ______________________________________________________________________
app = <SphinxTestApp buildername='text'>, status = <_io.StringIO object at 0x7f57e6a069e0>, warning = <_io.StringIO object at 0x7f57e6a06950>, always_document_param_types = True
@pytest.mark.parametrize("always_document_param_types", [True, False], ids=["doc_param_type", "no_doc_param_type"])
@pytest.mark.sphinx("text", testroot="dummy")
@patch("sphinx.writers.text.MAXWIDTH", 2000)
def test_always_document_param_types(
app: SphinxTestApp,
status: StringIO,
warning: StringIO,
always_document_param_types: bool,
) -> None:
set_python_path()
app.config.always_document_param_types = always_document_param_types # type: ignore[attr-defined] # create flag
app.config.autodoc_mock_imports = ["mailbox"] # type: ignore[attr-defined] # create flag
# Prevent "document isn't included in any toctree" warnings
for f in Path(app.srcdir).glob("*.rst"):
f.unlink()
(Path(app.srcdir) / "index.rst").write_text(
dedent(
"""
.. autofunction:: dummy_module.undocumented_function
.. autoclass:: dummy_module.DataClass
:undoc-members:
:special-members: __init__
""",
),
)
app.build()
assert "build succeeded" in status.getvalue() # Build succeeded
assert not warning.getvalue().strip()
format_args = {}
for indentation_level in range(2):
key = f"undoc_params_{indentation_level}"
if always_document_param_types:
format_args[key] = indent('\n\n Parameters:\n **x** ("int")', " " * indentation_level)
else:
format_args[key] = ""
contents = (Path(app.srcdir) / "_build/text/index.txt").read_text()
expected_contents = """\
dummy_module.undocumented_function(x)
Hi{undoc_params_0}
Return type:
"str"
class dummy_module.DataClass(x)
Class docstring.{undoc_params_0}
__init__(x){undoc_params_1}
"""
expected_contents = dedent(expected_contents).format(**format_args)
> assert contents == expected_contents
E assert 'dummy_module... ("int") --\n' == 'dummy_module...x** ("int")\n'
E
E Skipping 70 identical leading characters in diff, use -v to show
E - ** ("int")
E + ** ("int") --
E ? +++
E
E Return type:...
E
E ...Full output truncated (17 lines hidden), use '-vv' to show
tests/test_sphinx_autodoc_typehints.py:615: AssertionError
--------------------------------------------------------------------------------- Captured stdout teardown ----------------------------------------------------------------------------------
# testroot: root
# builder: text
# srcdir: /tmp/pytest-of-tkloczko/pytest-140/dummy
# outdir: /tmp/pytest-of-tkloczko/pytest-140/dummy/_build/text
# status:
Running Sphinx v7.2.6
building [mo]: targets for 0 po files that are out of date
writing output...
building [text]: targets for 5 source files that are out of date
updating environment: [new config] 1 added, 0 changed, 0 removed
reading sources... [100%] index
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
preparing documents... done
copying assets... done
writing output... [100%] index
build succeeded.
The text files are in ../../../../../tmp/pytest-of-tkloczko/pytest-140/dummy/_build/text.
# warning:
===================================================================================== warnings summary ======================================================================================
tests/conftest.py:10
/home/tkloczko/rpmbuild/BUILD/sphinx-autodoc-typehints-2.1.0/tests/conftest.py:10: RemovedInSphinx90Warning: 'sphinx.testing.path' is deprecated. Use 'os.path' or 'pathlib' instead.
from sphinx.testing.path import path
tests/test_integration.py: 112 warnings
tests/test_sphinx_autodoc_typehints.py: 82 warnings
/home/tkloczko/rpmbuild/BUILDROOT/python-sphinx-autodoc-typehints-2.1.0-2.fc37.x86_64/usr/lib/python3.10/site-packages/sphinx_autodoc_typehints/__init__.py:806: DeprecationWarning: The frontend.OptionParser class will be replaced by a subclass of argparse.ArgumentParser in Docutils 0.21 or later.
settings = OptionParser(components=(RSTParser,)).get_default_values()
tests/test_integration.py: 7728 warnings
tests/test_sphinx_autodoc_typehints.py: 5658 warnings
/usr/lib64/python3.10/optparse.py:1000: DeprecationWarning: The frontend.Option class will be removed in Docutils 0.21 or later.
option = self.option_class(*args, **kwargs)
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================================================================================== short test summary info ==================================================================================
FAILED tests/test_integration.py::test_integration[default_conf-func_with_overload] - assert 'mod.func_wit...n "None"' == 'mod.func_wit...n "None"'
FAILED tests/test_integration.py::test_integration[default_conf-TestClassAttributeDocs] - assert 'class mod.Te... An attribute' == 'class mod.Te... An attribute'
FAILED tests/test_integration.py::test_integration[prolog_conf-func_with_overload] - assert 'mod.func_wit...n "None"' == 'mod.func_wit...n "None"'
FAILED tests/test_integration.py::test_integration[prolog_conf-TestClassAttributeDocs] - assert 'class mod.Te... An attribute' == 'class mod.Te... An attribute'
FAILED tests/test_integration.py::test_integration[epilog_conf-func_with_overload] - assert 'mod.func_wit...n "None"' == 'mod.func_wit...n "None"'
FAILED tests/test_integration.py::test_integration[epilog_conf-TestClassAttributeDocs] - assert 'class mod.Te... An attribute' == 'class mod.Te... An attribute'
FAILED tests/test_integration.py::test_integration[bothlog_conf-func_with_overload] - assert 'mod.func_wit...n "None"' == 'mod.func_wit...n "None"'
FAILED tests/test_integration.py::test_integration[bothlog_conf-TestClassAttributeDocs] - assert 'class mod.Te... An attribute' == 'class mod.Te... An attribute'
FAILED tests/test_sphinx_autodoc_typehints.py::test_always_document_param_types[doc_param_type] - assert 'dummy_module... ("int") --\n' == 'dummy_module...x** ("int")\n'
====================================================================== 9 failed, 297 passed, 13581 warnings in 19.02s =======================================================================
And here is patch generated by pyupgrade:
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -149,10 +149,10 @@
:param z: baz
"""
- def __init__(self, x: bool, y: int, z: Optional[str] = None) -> None: # noqa: UP007
+ def __init__(self, x: bool, y: int, z: str | None = None) -> None: # noqa: UP007
pass
- def a_method(self, x: bool, y: int, z: Optional[str] = None) -> str: # noqa: UP007
+ def a_method(self, x: bool, y: int, z: str | None = None) -> str: # noqa: UP007
"""
Method docstring.
@@ -183,7 +183,7 @@
"""
@classmethod
- def a_classmethod(cls, x: bool, y: int, z: Optional[str] = None) -> str: # noqa: UP007
+ def a_classmethod(cls, x: bool, y: int, z: str | None = None) -> str: # noqa: UP007
"""
Classmethod docstring.
@@ -193,7 +193,7 @@
"""
@staticmethod
- def a_staticmethod(x: bool, y: int, z: Optional[str] = None) -> str: # noqa: UP007
+ def a_staticmethod(x: bool, y: int, z: str | None = None) -> str: # noqa: UP007
"""
Staticmethod docstring.
@@ -271,7 +271,7 @@
bytes
""",
)
-def function(x: bool, y: int, z_: Optional[str] = None) -> str: # noqa: ARG001, UP007
+def function(x: bool, y: int, z_: str | None = None) -> str: # noqa: ARG001, UP007
"""
Function docstring.
@@ -648,7 +648,7 @@
"None"
""",
)
-def func_with_overload(a: Union[int, str], b: Union[int, str]) -> None: # noqa: ARG001, UP007
+def func_with_overload(a: int | str, b: int | str) -> None: # noqa: ARG001, UP007
"""
f does the thing. The arguments can either be ints or strings but they must
both have the same type.
@@ -676,7 +676,7 @@
class TestClassAttributeDocs:
"""A class"""
- code: Optional[CodeType] # noqa: UP007
+ code: CodeType | None # noqa: UP007
"""An attribute"""
It would be good to adapt code to be able filter it over pyupgrade or open the the ticket that this tools messes with the module code.