mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '3.x' into escape-combined-args-kwargs
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
from .foo import bar
|
||||
|
||||
class foo:
|
||||
"""docstring of target.name_conflict::foo."""
|
||||
pass
|
||||
2
tests/roots/test-ext-autodoc/target/name_conflict/foo.py
Normal file
2
tests/roots/test-ext-autodoc/target/name_conflict/foo.py
Normal file
@@ -0,0 +1,2 @@
|
||||
class bar:
|
||||
"""docstring of target.name_conflict.foo::bar."""
|
||||
11
tests/roots/test-ext-autodoc/target/name_mangling.py
Normal file
11
tests/roots/test-ext-autodoc/target/name_mangling.py
Normal file
@@ -0,0 +1,11 @@
|
||||
class Foo:
|
||||
#: name of Foo
|
||||
__name = None
|
||||
__age = None
|
||||
|
||||
|
||||
class Bar(Foo):
|
||||
__address = None
|
||||
|
||||
#: a member having mangled-like name
|
||||
_Baz__email = None
|
||||
@@ -7,7 +7,7 @@ def sum(x: int, y: int) -> int:
|
||||
|
||||
|
||||
@overload
|
||||
def sum(x: float, y: float) -> float:
|
||||
def sum(x: "float", y: "float") -> "float":
|
||||
...
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class Math:
|
||||
...
|
||||
|
||||
@overload
|
||||
def sum(self, x: float, y: float) -> float:
|
||||
def sum(self, x: "float", y: "float") -> "float":
|
||||
...
|
||||
|
||||
@overload
|
||||
@@ -49,7 +49,7 @@ class Foo:
|
||||
...
|
||||
|
||||
@overload
|
||||
def __new__(cls, x: str, y: str) -> "Foo":
|
||||
def __new__(cls, x: "str", y: "str") -> "Foo":
|
||||
...
|
||||
|
||||
def __new__(cls, x, y):
|
||||
@@ -64,7 +64,7 @@ class Bar:
|
||||
...
|
||||
|
||||
@overload
|
||||
def __init__(cls, x: str, y: str) -> None:
|
||||
def __init__(cls, x: "str", y: "str") -> "None":
|
||||
...
|
||||
|
||||
def __init__(cls, x, y):
|
||||
@@ -77,7 +77,7 @@ class Meta(type):
|
||||
...
|
||||
|
||||
@overload
|
||||
def __call__(cls, x: str, y: str) -> Any:
|
||||
def __call__(cls, x: "str", y: "str") -> "Any":
|
||||
...
|
||||
|
||||
def __call__(cls, x, y):
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
# for py32 or above
|
||||
from contextlib import contextmanager
|
||||
from functools import lru_cache
|
||||
from typing import Generator
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def slow_function(message, timeout):
|
||||
"""This function is slow."""
|
||||
print(message)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def feeling_good(x: int, y: int) -> Generator:
|
||||
"""You'll feel better in this context!"""
|
||||
yield
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
from os import path # NOQA
|
||||
from typing import Union
|
||||
|
||||
|
||||
class Foo:
|
||||
class Bar:
|
||||
pass
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def bar(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def baz(self):
|
||||
pass
|
||||
|
||||
|
||||
def bar(x: Union[int, str], y: int = 1) -> None:
|
||||
pass
|
||||
11
tests/roots/test-ext-autosummary-filename-map/conf.py
Normal file
11
tests/roots/test-ext-autosummary-filename-map/conf.py
Normal file
@@ -0,0 +1,11 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
extensions = ['sphinx.ext.autosummary']
|
||||
autosummary_generate = True
|
||||
autosummary_filename_map = {
|
||||
"autosummary_dummy_module": "module_mangled",
|
||||
"autosummary_dummy_module.bar": "bar"
|
||||
}
|
||||
9
tests/roots/test-ext-autosummary-filename-map/index.rst
Normal file
9
tests/roots/test-ext-autosummary-filename-map/index.rst
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:caption: An autosummary
|
||||
|
||||
autosummary_dummy_module
|
||||
autosummary_dummy_module.Foo
|
||||
autosummary_dummy_module.Foo.bar
|
||||
autosummary_dummy_module.bar
|
||||
@@ -2,7 +2,16 @@ from os import path # NOQA
|
||||
from typing import Union
|
||||
|
||||
|
||||
#: module variable
|
||||
CONSTANT1 = None
|
||||
CONSTANT2 = None
|
||||
|
||||
|
||||
class Foo:
|
||||
#: class variable
|
||||
CONSTANT3 = None
|
||||
CONSTANT4 = None
|
||||
|
||||
class Bar:
|
||||
pass
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ same type links
|
||||
|
||||
link to :term:`Some term` and :term:`Some other term`.
|
||||
|
||||
link to :ref:`i18n-role-xref` and :ref:`same-type-links`.
|
||||
link to :ref:`i18n-role-xref`, :ref:`same-type-links` and :ref:`label <same-type-links>`.
|
||||
|
||||
link to :doc:`index` and :doc:`glossary_terms`.
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ msgstr "SAME TYPE LINKS"
|
||||
msgid "link to :term:`Some term` and :term:`Some other term`."
|
||||
msgstr "LINK TO :term:`SOME OTHER NEW TERM` AND :term:`SOME NEW TERM`."
|
||||
|
||||
msgid "link to :ref:`i18n-role-xref` and :ref:`same-type-links`."
|
||||
msgstr "LINK TO :ref:`same-type-links` AND :ref:`i18n-role-xref`."
|
||||
msgid "link to :ref:`i18n-role-xref`, :ref:`same-type-links` and :ref:`label <same-type-links>`."
|
||||
msgstr "LINK TO :ref:`LABEL <i18n-role-xref>` AND :ref:`same-type-links` AND :ref:`same-type-links`."
|
||||
|
||||
msgid "link to :doc:`index` and :doc:`glossary_terms`."
|
||||
msgstr "LINK TO :doc:`glossary_terms` AND :doc:`index`."
|
||||
|
||||
@@ -69,4 +69,4 @@ subsubsection
|
||||
|
||||
otherdoc
|
||||
|
||||
* Embeded standalone hyperlink reference(refs: #5948): `subsection <section1_>`_.
|
||||
* Embedded standalone hyperlink reference(refs: #5948): `subsection <section1_>`_.
|
||||
|
||||
@@ -11,6 +11,8 @@ Some additional anchors to exercise ignore code
|
||||
* `Example Bar invalid <https://www.google.com/#top>`_
|
||||
* `Example anchor invalid <http://www.sphinx-doc.org/en/1.7/intro.html#does-not-exist>`_
|
||||
* `Complete nonsense <https://localhost:7777/doesnotexist>`_
|
||||
* `Example valid local file <conf.py>`_
|
||||
* `Example invalid local file <path/to/notfound>`_
|
||||
|
||||
.. image:: https://www.google.com/image.png
|
||||
.. figure:: https://www.google.com/image2.png
|
||||
|
||||
0
tests/roots/test-reST-code-block/conf.py
Normal file
0
tests/roots/test-reST-code-block/conf.py
Normal file
7
tests/roots/test-reST-code-block/index.rst
Normal file
7
tests/roots/test-reST-code-block/index.rst
Normal file
@@ -0,0 +1,7 @@
|
||||
.. code-block::
|
||||
:linenos:
|
||||
|
||||
def hello(name)
|
||||
print("hello", name)
|
||||
|
||||
hello("Sphinx")
|
||||
@@ -32,14 +32,11 @@ Contents:
|
||||
Latest reference <http://sphinx-doc.org/latest/>
|
||||
Python <http://python.org/>
|
||||
|
||||
self
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
@@ -16,6 +16,7 @@ Contents:
|
||||
foo
|
||||
bar
|
||||
http://sphinx-doc.org/
|
||||
self
|
||||
|
||||
.. only:: html
|
||||
|
||||
|
||||
@@ -391,6 +391,6 @@ def test_run_epubcheck(app):
|
||||
subprocess.run(['java', '-jar', epubcheck, app.outdir / 'SphinxTests.epub'],
|
||||
stdout=PIPE, stderr=PIPE, check=True)
|
||||
except CalledProcessError as exc:
|
||||
print(exc.stdout)
|
||||
print(exc.stderr)
|
||||
print(exc.stdout.decode('utf-8'))
|
||||
print(exc.stderr.decode('utf-8'))
|
||||
assert False, 'epubcheck exited with return code %s' % exc.returncode
|
||||
|
||||
@@ -357,7 +357,6 @@ def test_html4_output(app, status, warning):
|
||||
"[@class='reference external']", ''),
|
||||
(".//li/p/a[@href='genindex.html']/span", 'Index'),
|
||||
(".//li/p/a[@href='py-modindex.html']/span", 'Module Index'),
|
||||
(".//li/p/a[@href='search.html']/span", 'Search Page'),
|
||||
# custom sidebar only for contents
|
||||
(".//h4", 'Contents sidebar'),
|
||||
# custom JavaScript
|
||||
@@ -1575,3 +1574,21 @@ def test_html_scaled_image_link(app):
|
||||
assert re.search('\n<img alt="_images/img.png" class="no-scaled-link"'
|
||||
' src="_images/img.png" style="[^"]+" />',
|
||||
context)
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='reST-code-block',
|
||||
confoverrides={'html_codeblock_linenos_style': 'table'})
|
||||
def test_html_codeblock_linenos_style_table(app):
|
||||
app.build()
|
||||
content = (app.outdir / 'index.html').read_text()
|
||||
|
||||
assert '<div class="linenodiv"><pre>1\n2\n3\n4</pre></div>' in content
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='reST-code-block',
|
||||
confoverrides={'html_codeblock_linenos_style': 'inline'})
|
||||
def test_html_codeblock_linenos_style_inline(app):
|
||||
app.build()
|
||||
content = (app.outdir / 'index.html').read_text()
|
||||
|
||||
assert '<span class="lineno">1 </span>' in content
|
||||
|
||||
@@ -64,8 +64,8 @@ def compile_latex_document(app, filename='python.tex'):
|
||||
'-output-directory=%s' % app.config.latex_engine,
|
||||
filename]
|
||||
subprocess.run(args, stdout=PIPE, stderr=PIPE, check=True)
|
||||
except OSError: # most likely the latex executable was not found
|
||||
raise pytest.skip.Exception
|
||||
except OSError as exc: # most likely the latex executable was not found
|
||||
raise pytest.skip.Exception from exc
|
||||
except CalledProcessError as exc:
|
||||
print(exc.stdout)
|
||||
print(exc.stderr)
|
||||
@@ -1477,7 +1477,7 @@ def test_latex_labels(app, status, warning):
|
||||
r'\label{\detokenize{otherdoc:otherdoc}}'
|
||||
r'\label{\detokenize{otherdoc::doc}}' in result)
|
||||
|
||||
# Embeded standalone hyperlink reference (refs: #5948)
|
||||
# Embedded standalone hyperlink reference (refs: #5948)
|
||||
assert result.count(r'\label{\detokenize{index:section1}}') == 1
|
||||
|
||||
|
||||
@@ -1545,7 +1545,7 @@ def test_texescape_for_unicode_supported_engine(app, status, warning):
|
||||
assert 'superscript: ⁰, ¹' in result
|
||||
assert 'subscript: ₀, ₁' in result
|
||||
|
||||
|
||||
|
||||
@pytest.mark.sphinx('latex', testroot='basic',
|
||||
confoverrides={'latex_elements': {'extrapackages': r'\usepackage{foo}'}})
|
||||
def test_latex_elements_extrapackages(app, status, warning):
|
||||
|
||||
@@ -30,7 +30,9 @@ def test_defaults(app, status, warning):
|
||||
# images should fail
|
||||
assert "Not Found for url: https://www.google.com/image.png" in content
|
||||
assert "Not Found for url: https://www.google.com/image2.png" in content
|
||||
assert len(content.splitlines()) == 5
|
||||
# looking for local file should fail
|
||||
assert "[broken] path/to/notfound" in content
|
||||
assert len(content.splitlines()) == 6
|
||||
|
||||
|
||||
@pytest.mark.sphinx('linkcheck', testroot='linkcheck', freshenv=True)
|
||||
@@ -47,8 +49,8 @@ def test_defaults_json(app, status, warning):
|
||||
"info"]:
|
||||
assert attr in row
|
||||
|
||||
assert len(content.splitlines()) == 8
|
||||
assert len(rows) == 8
|
||||
assert len(content.splitlines()) == 10
|
||||
assert len(rows) == 10
|
||||
# the output order of the rows is not stable
|
||||
# due to possible variance in network latency
|
||||
rowsby = {row["uri"]:row for row in rows}
|
||||
@@ -69,7 +71,7 @@ def test_defaults_json(app, status, warning):
|
||||
assert dnerow['uri'] == 'https://localhost:7777/doesnotexist'
|
||||
assert rowsby['https://www.google.com/image2.png'] == {
|
||||
'filename': 'links.txt',
|
||||
'lineno': 16,
|
||||
'lineno': 18,
|
||||
'status': 'broken',
|
||||
'code': 0,
|
||||
'uri': 'https://www.google.com/image2.png',
|
||||
@@ -92,7 +94,8 @@ def test_defaults_json(app, status, warning):
|
||||
'https://localhost:7777/doesnotexist',
|
||||
'http://www.sphinx-doc.org/en/1.7/intro.html#',
|
||||
'https://www.google.com/image.png',
|
||||
'https://www.google.com/image2.png']
|
||||
'https://www.google.com/image2.png',
|
||||
'path/to/notfound']
|
||||
})
|
||||
def test_anchors_ignored(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
@@ -58,8 +58,8 @@ def test_texinfo(app, status, warning):
|
||||
try:
|
||||
args = ['makeinfo', '--no-split', 'sphinxtests.texi']
|
||||
subprocess.run(args, stdout=PIPE, stderr=PIPE, cwd=app.outdir, check=True)
|
||||
except OSError:
|
||||
raise pytest.skip.Exception # most likely makeinfo was not found
|
||||
except OSError as exc:
|
||||
raise pytest.skip.Exception from exc # most likely makeinfo was not found
|
||||
except CalledProcessError as exc:
|
||||
print(exc.stdout)
|
||||
print(exc.stderr)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import pytest
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.addnodes import desc
|
||||
from sphinx.domains.c import DefinitionParser, DefinitionError
|
||||
from sphinx.domains.c import _max_id, _id_prefix, Symbol
|
||||
from sphinx.testing import restructuredtext
|
||||
@@ -469,6 +470,8 @@ def test_attributes():
|
||||
check('member', '__attribute__(()) int f', {1: 'f'})
|
||||
check('member', '__attribute__((a)) int f', {1: 'f'})
|
||||
check('member', '__attribute__((a, b)) int f', {1: 'f'})
|
||||
check('member', '__attribute__((optimize(3))) int f', {1: 'f'})
|
||||
check('member', '__attribute__((format(printf, 1, 2))) int f', {1: 'f'})
|
||||
# style: user-defined id
|
||||
check('member', 'id_attr int f', {1: 'f'})
|
||||
# style: user-defined paren
|
||||
@@ -588,3 +591,13 @@ def test_cvar(app):
|
||||
domain = app.env.get_domain('c')
|
||||
entry = domain.objects.get('PyClass_Type')
|
||||
assert entry == ('index', 'c.PyClass_Type', 'var')
|
||||
|
||||
|
||||
def test_noindexentry(app):
|
||||
text = (".. c:function:: void f()\n"
|
||||
".. c:function:: void g()\n"
|
||||
" :noindexentry:\n")
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
|
||||
assert_node(doctree[0], addnodes.index, entries=[('single', 'f (C function)', 'c.f', '', None)])
|
||||
assert_node(doctree[2], addnodes.index, entries=[])
|
||||
|
||||
@@ -14,8 +14,11 @@ import pytest
|
||||
|
||||
import sphinx.domains.cpp as cppDomain
|
||||
from sphinx import addnodes
|
||||
from sphinx.addnodes import desc
|
||||
from sphinx.domains.cpp import DefinitionParser, DefinitionError, NoOldIdError
|
||||
from sphinx.domains.cpp import Symbol, _max_id, _id_prefix
|
||||
from sphinx.testing import restructuredtext
|
||||
from sphinx.testing.util import assert_node
|
||||
from sphinx.util import docutils
|
||||
|
||||
|
||||
@@ -897,6 +900,8 @@ def test_attributes():
|
||||
check('member', '__attribute__(()) int f', {1: 'f__i', 2: '1f'})
|
||||
check('member', '__attribute__((a)) int f', {1: 'f__i', 2: '1f'})
|
||||
check('member', '__attribute__((a, b)) int f', {1: 'f__i', 2: '1f'})
|
||||
check('member', '__attribute__((optimize(3))) int f', {1: 'f__i', 2: '1f'})
|
||||
check('member', '__attribute__((format(printf, 1, 2))) int f', {1: 'f__i', 2: '1f'})
|
||||
# style: user-defined id
|
||||
check('member', 'id_attr int f', {1: 'f__i', 2: '1f'})
|
||||
# style: user-defined paren
|
||||
@@ -1209,3 +1214,13 @@ not found in `{test}`
|
||||
assert any_role.classes == cpp_any_role.classes, expect
|
||||
assert any_role.classes == expr_role.content_classes['a'], expect
|
||||
assert any_role.classes == texpr_role.content_classes['a'], expect
|
||||
|
||||
|
||||
def test_noindexentry(app):
|
||||
text = (".. cpp:function:: void f()\n"
|
||||
".. cpp:function:: void g()\n"
|
||||
" :noindexentry:\n")
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
|
||||
assert_node(doctree[0], addnodes.index, entries=[('single', 'f (C++ function)', '_CPPv41fv', '', None)])
|
||||
assert_node(doctree[2], addnodes.index, entries=[])
|
||||
|
||||
@@ -218,3 +218,13 @@ def test_js_data(app):
|
||||
assert_node(doctree[0], addnodes.index,
|
||||
entries=[("single", "name (global variable or constant)", "name", "", None)])
|
||||
assert_node(doctree[1], addnodes.desc, domain="js", objtype="data", noindex=False)
|
||||
|
||||
|
||||
def test_noindexentry(app):
|
||||
text = (".. js:function:: f()\n"
|
||||
".. js:function:: g()\n"
|
||||
" :noindexentry:\n")
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
|
||||
assert_node(doctree[0], addnodes.index, entries=[('single', 'f() (built-in function)', 'f', '', None)])
|
||||
assert_node(doctree[2], addnodes.index, entries=[])
|
||||
|
||||
@@ -236,18 +236,18 @@ def test_get_full_qualified_name():
|
||||
assert domain.get_full_qualified_name(node) == 'module1.Class.func'
|
||||
|
||||
|
||||
def test_parse_annotation():
|
||||
doctree = _parse_annotation("int")
|
||||
def test_parse_annotation(app):
|
||||
doctree = _parse_annotation("int", app.env)
|
||||
assert_node(doctree, ([pending_xref, "int"],))
|
||||
assert_node(doctree[0], pending_xref, refdomain="py", reftype="class", reftarget="int")
|
||||
|
||||
doctree = _parse_annotation("List[int]")
|
||||
doctree = _parse_annotation("List[int]", app.env)
|
||||
assert_node(doctree, ([pending_xref, "List"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[pending_xref, "int"],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
doctree = _parse_annotation("Tuple[int, int]")
|
||||
doctree = _parse_annotation("Tuple[int, int]", app.env)
|
||||
assert_node(doctree, ([pending_xref, "Tuple"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[pending_xref, "int"],
|
||||
@@ -255,14 +255,14 @@ def test_parse_annotation():
|
||||
[pending_xref, "int"],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
doctree = _parse_annotation("Tuple[()]")
|
||||
doctree = _parse_annotation("Tuple[()]", app.env)
|
||||
assert_node(doctree, ([pending_xref, "Tuple"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[desc_sig_punctuation, "("],
|
||||
[desc_sig_punctuation, ")"],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
doctree = _parse_annotation("Callable[[int, int], int]")
|
||||
doctree = _parse_annotation("Callable[[int, int], int]", app.env)
|
||||
assert_node(doctree, ([pending_xref, "Callable"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[desc_sig_punctuation, "["],
|
||||
@@ -275,12 +275,11 @@ def test_parse_annotation():
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
# None type makes an object-reference (not a class reference)
|
||||
doctree = _parse_annotation("None")
|
||||
doctree = _parse_annotation("None", app.env)
|
||||
assert_node(doctree, ([pending_xref, "None"],))
|
||||
assert_node(doctree[0], pending_xref, refdomain="py", reftype="obj", reftarget="None")
|
||||
|
||||
|
||||
|
||||
def test_pyfunction_signature(app):
|
||||
text = ".. py:function:: hello(name: str) -> str"
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
@@ -458,14 +457,22 @@ def test_pyobject_prefix(app):
|
||||
|
||||
|
||||
def test_pydata(app):
|
||||
text = ".. py:data:: var\n"
|
||||
text = (".. py:module:: example\n"
|
||||
".. py:data:: var\n"
|
||||
" :type: int\n")
|
||||
domain = app.env.get_domain('py')
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, desc_name, "var"],
|
||||
assert_node(doctree, (nodes.target,
|
||||
addnodes.index,
|
||||
addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_addname, "example."],
|
||||
[desc_name, "var"],
|
||||
[desc_annotation, (": ",
|
||||
[pending_xref, "int"])])],
|
||||
[desc_content, ()])]))
|
||||
assert 'var' in domain.objects
|
||||
assert domain.objects['var'] == ('index', 'var', 'data')
|
||||
assert_node(doctree[3][0][2][1], pending_xref, **{"py:module": "example"})
|
||||
assert 'example.var' in domain.objects
|
||||
assert domain.objects['example.var'] == ('index', 'example.var', 'data')
|
||||
|
||||
|
||||
def test_pyfunction(app):
|
||||
@@ -679,7 +686,7 @@ def test_pyattribute(app):
|
||||
text = (".. py:class:: Class\n"
|
||||
"\n"
|
||||
" .. py:attribute:: attr\n"
|
||||
" :type: str\n"
|
||||
" :type: Optional[str]\n"
|
||||
" :value: ''\n")
|
||||
domain = app.env.get_domain('py')
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
@@ -692,9 +699,14 @@ def test_pyattribute(app):
|
||||
entries=[('single', 'attr (Class attribute)', 'Class.attr', '', None)])
|
||||
assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "attr"],
|
||||
[desc_annotation, (": ",
|
||||
[pending_xref, "str"])],
|
||||
[pending_xref, "Optional"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[pending_xref, "str"],
|
||||
[desc_sig_punctuation, "]"])],
|
||||
[desc_annotation, " = ''"])],
|
||||
[desc_content, ()]))
|
||||
assert_node(doctree[1][1][1][0][1][1], pending_xref, **{"py:class": "Class"})
|
||||
assert_node(doctree[1][1][1][0][1][3], pending_xref, **{"py:class": "Class"})
|
||||
assert 'Class.attr' in domain.objects
|
||||
assert domain.objects['Class.attr'] == ('index', 'Class.attr', 'attribute')
|
||||
|
||||
@@ -796,3 +808,19 @@ def test_modindex_common_prefix(app):
|
||||
)
|
||||
|
||||
|
||||
def test_noindexentry(app):
|
||||
text = (".. py:function:: f()\n"
|
||||
".. py:function:: g()\n"
|
||||
" :noindexentry:\n")
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
|
||||
assert_node(doctree[0], addnodes.index, entries=[('pair', 'built-in function; f()', 'f', '', None)])
|
||||
assert_node(doctree[2], addnodes.index, entries=[])
|
||||
|
||||
text = (".. py:class:: f\n"
|
||||
".. py:class:: g\n"
|
||||
" :noindexentry:\n")
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
|
||||
assert_node(doctree[0], addnodes.index, entries=[('single', 'f (built-in class)', 'f', '', None)])
|
||||
assert_node(doctree[2], addnodes.index, entries=[])
|
||||
|
||||
@@ -25,12 +25,14 @@ def test_create_single_index(app):
|
||||
".. index:: ёлка\n"
|
||||
".. index:: תירבע\n"
|
||||
".. index:: 9-symbol\n"
|
||||
".. index:: &-symbol\n")
|
||||
".. index:: &-symbol\n"
|
||||
".. index:: £100\n")
|
||||
restructuredtext.parse(app, text)
|
||||
index = IndexEntries(app.env).create_index(app.builder)
|
||||
assert len(index) == 6
|
||||
assert index[0] == ('Symbols', [('&-symbol', [[('', '#index-9')], [], None]),
|
||||
('9-symbol', [[('', '#index-8')], [], None])])
|
||||
('9-symbol', [[('', '#index-8')], [], None]),
|
||||
('£100', [[('', '#index-10')], [], None])])
|
||||
assert index[1] == ('D', [('docutils', [[('', '#index-0')], [], None])])
|
||||
assert index[2] == ('P', [('pip', [[], [('install', [('', '#index-2')]),
|
||||
('upgrade', [('', '#index-3')])], None]),
|
||||
|
||||
@@ -41,7 +41,8 @@ def test_process_doc(app):
|
||||
assert_node(toctree[0][1][0], addnodes.toctree,
|
||||
caption="Table of Contents", glob=False, hidden=False,
|
||||
titlesonly=False, maxdepth=2, numbered=999,
|
||||
entries=[(None, 'foo'), (None, 'bar'), (None, 'http://sphinx-doc.org/')],
|
||||
entries=[(None, 'foo'), (None, 'bar'), (None, 'http://sphinx-doc.org/'),
|
||||
(None, 'self')],
|
||||
includefiles=['foo', 'bar'])
|
||||
|
||||
# only branch
|
||||
@@ -219,7 +220,9 @@ def test_get_toctree_for(app):
|
||||
([list_item, ([compact_paragraph, reference, "foo"],
|
||||
bullet_list)],
|
||||
[list_item, compact_paragraph, reference, "bar"],
|
||||
[list_item, compact_paragraph, reference, "http://sphinx-doc.org/"]))
|
||||
[list_item, compact_paragraph, reference, "http://sphinx-doc.org/"],
|
||||
[list_item, compact_paragraph, reference,
|
||||
"Welcome to Sphinx Tests’s documentation!"]))
|
||||
assert_node(toctree[1][0][1],
|
||||
([list_item, compact_paragraph, reference, "quux"],
|
||||
[list_item, compact_paragraph, reference, "foo.1"],
|
||||
@@ -231,6 +234,7 @@ def test_get_toctree_for(app):
|
||||
assert_node(toctree[1][0][1][2][0][0], reference, refuri="foo#foo-2", secnumber=[1, 3])
|
||||
assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=[2])
|
||||
assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/")
|
||||
assert_node(toctree[1][3][0][0], reference, refuri="")
|
||||
|
||||
assert_node(toctree[2],
|
||||
[bullet_list, list_item, compact_paragraph, reference, "baz"])
|
||||
@@ -255,10 +259,13 @@ def test_get_toctree_for_collapse(app):
|
||||
assert_node(toctree[1],
|
||||
([list_item, compact_paragraph, reference, "foo"],
|
||||
[list_item, compact_paragraph, reference, "bar"],
|
||||
[list_item, compact_paragraph, reference, "http://sphinx-doc.org/"]))
|
||||
[list_item, compact_paragraph, reference, "http://sphinx-doc.org/"],
|
||||
[list_item, compact_paragraph, reference,
|
||||
"Welcome to Sphinx Tests’s documentation!"]))
|
||||
assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=[1])
|
||||
assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=[2])
|
||||
assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/")
|
||||
assert_node(toctree[1][3][0][0], reference, refuri="")
|
||||
|
||||
assert_node(toctree[2],
|
||||
[bullet_list, list_item, compact_paragraph, reference, "baz"])
|
||||
@@ -285,7 +292,9 @@ def test_get_toctree_for_maxdepth(app):
|
||||
([list_item, ([compact_paragraph, reference, "foo"],
|
||||
bullet_list)],
|
||||
[list_item, compact_paragraph, reference, "bar"],
|
||||
[list_item, compact_paragraph, reference, "http://sphinx-doc.org/"]))
|
||||
[list_item, compact_paragraph, reference, "http://sphinx-doc.org/"],
|
||||
[list_item, compact_paragraph, reference,
|
||||
"Welcome to Sphinx Tests’s documentation!"]))
|
||||
assert_node(toctree[1][0][1],
|
||||
([list_item, compact_paragraph, reference, "quux"],
|
||||
[list_item, ([compact_paragraph, reference, "foo.1"],
|
||||
@@ -302,6 +311,7 @@ def test_get_toctree_for_maxdepth(app):
|
||||
assert_node(toctree[1][0][1][2][0][0], reference, refuri="foo#foo-2", secnumber=[1, 3])
|
||||
assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=[2])
|
||||
assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/")
|
||||
assert_node(toctree[1][3][0][0], reference, refuri="")
|
||||
|
||||
assert_node(toctree[2],
|
||||
[bullet_list, list_item, compact_paragraph, reference, "baz"])
|
||||
@@ -327,7 +337,9 @@ def test_get_toctree_for_includehidden(app):
|
||||
([list_item, ([compact_paragraph, reference, "foo"],
|
||||
bullet_list)],
|
||||
[list_item, compact_paragraph, reference, "bar"],
|
||||
[list_item, compact_paragraph, reference, "http://sphinx-doc.org/"]))
|
||||
[list_item, compact_paragraph, reference, "http://sphinx-doc.org/"],
|
||||
[list_item, compact_paragraph, reference,
|
||||
"Welcome to Sphinx Tests’s documentation!"]))
|
||||
assert_node(toctree[1][0][1],
|
||||
([list_item, compact_paragraph, reference, "quux"],
|
||||
[list_item, compact_paragraph, reference, "foo.1"],
|
||||
|
||||
@@ -121,7 +121,6 @@ def test_pep_0420_enabled_separate(make_app, apidoc):
|
||||
|
||||
with open(outdir / 'a.b.c.rst') as f:
|
||||
rst = f.read()
|
||||
|
||||
assert ".. toctree::\n :maxdepth: 4\n\n a.b.c.d\n" in rst
|
||||
|
||||
with open(outdir / 'a.b.e.rst') as f:
|
||||
@@ -509,7 +508,6 @@ def test_package_file(tempdir):
|
||||
" :undoc-members:\n"
|
||||
" :show-inheritance:\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"Module contents\n"
|
||||
"---------------\n"
|
||||
"\n"
|
||||
@@ -595,8 +593,7 @@ def test_package_file_module_first(tempdir):
|
||||
".. automodule:: testpkg.example\n"
|
||||
" :members:\n"
|
||||
" :undoc-members:\n"
|
||||
" :show-inheritance:\n"
|
||||
"\n")
|
||||
" :show-inheritance:\n")
|
||||
|
||||
|
||||
def test_package_file_without_submodules(tempdir):
|
||||
@@ -639,5 +636,4 @@ def test_namespace_package_file(tempdir):
|
||||
".. automodule:: testpkg.example\n"
|
||||
" :members:\n"
|
||||
" :undoc-members:\n"
|
||||
" :show-inheritance:\n"
|
||||
"\n")
|
||||
" :show-inheritance:\n")
|
||||
|
||||
@@ -121,15 +121,16 @@ def test_parse_name(app):
|
||||
verify('class', 'Base', ('test_ext_autodoc', ['Base'], None, None))
|
||||
|
||||
# for members
|
||||
directive.env.ref_context['py:module'] = 'foo'
|
||||
verify('method', 'util.SphinxTestApp.cleanup',
|
||||
('foo', ['util', 'SphinxTestApp', 'cleanup'], None, None))
|
||||
directive.env.ref_context['py:module'] = 'util'
|
||||
directive.env.ref_context['py:module'] = 'sphinx.testing.util'
|
||||
verify('method', 'SphinxTestApp.cleanup',
|
||||
('sphinx.testing.util', ['SphinxTestApp', 'cleanup'], None, None))
|
||||
directive.env.ref_context['py:module'] = 'sphinx.testing.util'
|
||||
directive.env.ref_context['py:class'] = 'Foo'
|
||||
directive.env.temp_data['autodoc:class'] = 'SphinxTestApp'
|
||||
verify('method', 'cleanup', ('util', ['SphinxTestApp', 'cleanup'], None, None))
|
||||
verify('method', 'cleanup',
|
||||
('sphinx.testing.util', ['SphinxTestApp', 'cleanup'], None, None))
|
||||
verify('method', 'SphinxTestApp.cleanup',
|
||||
('util', ['SphinxTestApp', 'cleanup'], None, None))
|
||||
('sphinx.testing.util', ['SphinxTestApp', 'cleanup'], None, None))
|
||||
|
||||
|
||||
def test_format_signature(app):
|
||||
@@ -800,14 +801,14 @@ def test_autodoc_inner_class(app):
|
||||
actual = do_autodoc(app, 'class', 'target.Outer.Inner', options)
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:class:: Outer.Inner()',
|
||||
' :module: target',
|
||||
'.. py:class:: Inner()',
|
||||
' :module: target.Outer',
|
||||
'',
|
||||
' Foo',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: Outer.Inner.meth()',
|
||||
' :module: target',
|
||||
' .. py:method:: Inner.meth()',
|
||||
' :module: target.Outer',
|
||||
'',
|
||||
' Foo',
|
||||
'',
|
||||
@@ -1046,7 +1047,7 @@ def test_class_attributes(app):
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_instance_attributes(app):
|
||||
def test_autoclass_instance_attributes(app):
|
||||
options = {"members": None}
|
||||
actual = do_autodoc(app, 'class', 'target.InstAttCls', options)
|
||||
assert list(actual) == [
|
||||
@@ -1119,6 +1120,19 @@ def test_instance_attributes(app):
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_autoattribute_instance_attributes(app):
|
||||
actual = do_autodoc(app, 'attribute', 'target.InstAttCls.ia1')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:attribute:: InstAttCls.ia1',
|
||||
' :module: target',
|
||||
'',
|
||||
' Doc comment for instance attribute InstAttCls.ia1',
|
||||
''
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_slots(app):
|
||||
options = {"members": None,
|
||||
@@ -1266,7 +1280,7 @@ def test_automethod_for_decorated(app):
|
||||
actual = do_autodoc(app, 'method', 'target.decorator.Bar.meth')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:method:: Bar.meth()',
|
||||
'.. py:method:: Bar.meth(name=None, age=None)',
|
||||
' :module: target.decorator',
|
||||
'',
|
||||
]
|
||||
@@ -1431,7 +1445,7 @@ def test_coroutine(app):
|
||||
actual = do_autodoc(app, 'function', 'target.coroutine.sync_func')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:function:: sync_func(*args, **kwargs)',
|
||||
'.. py:function:: sync_func()',
|
||||
' :module: target.coroutine',
|
||||
'',
|
||||
]
|
||||
@@ -1881,6 +1895,43 @@ def test_overload(app):
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_pymodule_for_ModuleLevelDocumenter(app):
|
||||
app.env.ref_context['py:module'] = 'target.classes'
|
||||
actual = do_autodoc(app, 'class', 'Foo')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:class:: Foo()',
|
||||
' :module: target.classes',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_pymodule_for_ClassLevelDocumenter(app):
|
||||
app.env.ref_context['py:module'] = 'target.methods'
|
||||
actual = do_autodoc(app, 'method', 'Base.meth')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:method:: Base.meth()',
|
||||
' :module: target.methods',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_pyclass_for_ClassLevelDocumenter(app):
|
||||
app.env.ref_context['py:module'] = 'target.methods'
|
||||
app.env.ref_context['py:class'] = 'Base'
|
||||
actual = do_autodoc(app, 'method', 'meth')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:method:: Base.meth()',
|
||||
' :module: target.methods',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('dummy', testroot='ext-autodoc')
|
||||
def test_autodoc(app, status, warning):
|
||||
app.builder.build_all()
|
||||
@@ -1899,3 +1950,71 @@ my_name
|
||||
|
||||
alias of bug2437.autodoc_dummy_foo.Foo"""
|
||||
assert warning.getvalue() == ''
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_name_conflict(app):
|
||||
actual = do_autodoc(app, 'class', 'target.name_conflict.foo')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:class:: foo()',
|
||||
' :module: target.name_conflict',
|
||||
'',
|
||||
' docstring of target.name_conflict::foo.',
|
||||
'',
|
||||
]
|
||||
|
||||
actual = do_autodoc(app, 'class', 'target.name_conflict.foo.bar')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:class:: bar()',
|
||||
' :module: target.name_conflict.foo',
|
||||
'',
|
||||
' docstring of target.name_conflict.foo::bar.',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_name_mangling(app):
|
||||
options = {"members": None,
|
||||
"undoc-members": None,
|
||||
"private-members": None}
|
||||
actual = do_autodoc(app, 'module', 'target.name_mangling', options)
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:module:: target.name_mangling',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: Bar()',
|
||||
' :module: target.name_mangling',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Bar._Baz__email',
|
||||
' :module: target.name_mangling',
|
||||
' :value: None',
|
||||
'',
|
||||
' a member having mangled-like name',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Bar.__address',
|
||||
' :module: target.name_mangling',
|
||||
' :value: None',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: Foo()',
|
||||
' :module: target.name_mangling',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Foo.__age',
|
||||
' :module: target.name_mangling',
|
||||
' :value: None',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Foo.__name',
|
||||
' :module: target.name_mangling',
|
||||
' :value: None',
|
||||
'',
|
||||
' name of Foo',
|
||||
'',
|
||||
]
|
||||
|
||||
@@ -85,8 +85,8 @@ def test_methoddescriptor(app):
|
||||
actual = do_autodoc(app, 'function', 'builtins.int.__add__')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:function:: int.__add__(self, value, /)',
|
||||
' :module: builtins',
|
||||
'.. py:function:: __add__(self, value, /)',
|
||||
' :module: builtins.int',
|
||||
'',
|
||||
' Return self+value.',
|
||||
'',
|
||||
@@ -98,7 +98,7 @@ def test_decorated(app):
|
||||
actual = do_autodoc(app, 'function', 'target.decorator.foo')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:function:: foo()',
|
||||
'.. py:function:: foo(name=None, age=None)',
|
||||
' :module: target.decorator',
|
||||
'',
|
||||
]
|
||||
@@ -146,3 +146,16 @@ def test_wrapped_function(app):
|
||||
' This function is slow.',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_wrapped_function_contextmanager(app):
|
||||
actual = do_autodoc(app, 'function', 'target.wrappedfunction.feeling_good')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:function:: feeling_good(x: int, y: int) -> Generator',
|
||||
' :module: target.wrappedfunction',
|
||||
'',
|
||||
" You'll feel better in this context!",
|
||||
'',
|
||||
]
|
||||
|
||||
@@ -13,6 +13,8 @@ import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from sphinx.testing import restructuredtext
|
||||
|
||||
from test_ext_autodoc import do_autodoc
|
||||
|
||||
IS_PYPY = platform.python_implementation() == 'PyPy'
|
||||
@@ -633,6 +635,12 @@ def test_autodoc_typehints_description(app):
|
||||
in context)
|
||||
|
||||
|
||||
@pytest.mark.sphinx('text', testroot='ext-autodoc',
|
||||
confoverrides={'autodoc_typehints': "description"})
|
||||
def test_autodoc_typehints_description_for_invalid_node(app):
|
||||
text = ".. py:function:: hello; world"
|
||||
restructuredtext.parse(app, text) # raises no error
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_autodoc_default_options(app):
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
import abc
|
||||
import sys
|
||||
from importlib import import_module
|
||||
from typing import TypeVar
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -39,6 +40,7 @@ def test_MockObject():
|
||||
assert isinstance(mock.attr1.attr2, _MockObject)
|
||||
assert isinstance(mock.attr1.attr2.meth(), _MockObject)
|
||||
|
||||
# subclassing
|
||||
class SubClass(mock.SomeClass):
|
||||
"""docstring of SubClass"""
|
||||
|
||||
@@ -51,6 +53,16 @@ def test_MockObject():
|
||||
assert obj.method() == "string"
|
||||
assert isinstance(obj.other_method(), SubClass)
|
||||
|
||||
# parametrized type
|
||||
T = TypeVar('T')
|
||||
|
||||
class SubClass2(mock.SomeClass[T]):
|
||||
"""docstring of SubClass"""
|
||||
|
||||
obj2 = SubClass2()
|
||||
assert SubClass2.__doc__ == "docstring of SubClass"
|
||||
assert isinstance(obj2, SubClass2)
|
||||
|
||||
|
||||
def test_mock():
|
||||
modname = 'sphinx.unknown'
|
||||
|
||||
@@ -97,7 +97,7 @@ def test_extract_summary(capsys):
|
||||
|
||||
# abbreviations
|
||||
doc = ['Blabla, i.e. bla.']
|
||||
assert extract_summary(doc, document) == 'Blabla, i.e.'
|
||||
assert extract_summary(doc, document) == ' '.join(doc)
|
||||
|
||||
# literal
|
||||
doc = ['blah blah::']
|
||||
@@ -108,6 +108,12 @@ def test_extract_summary(capsys):
|
||||
'=========']
|
||||
assert extract_summary(doc, document) == 'blah blah'
|
||||
|
||||
# hyperlink target
|
||||
doc = ['Do `this <https://www.sphinx-doc.org/>`_ and that. '
|
||||
'blah blah blah.']
|
||||
assert (extract_summary(doc, document) ==
|
||||
'Do `this <https://www.sphinx-doc.org/>`_ and that.')
|
||||
|
||||
_, err = capsys.readouterr()
|
||||
assert err == ''
|
||||
|
||||
@@ -202,17 +208,17 @@ def test_autosummary_generate_content_for_module(app):
|
||||
assert template.render.call_args[0][0] == 'module'
|
||||
|
||||
context = template.render.call_args[0][1]
|
||||
assert context['members'] == ['Exc', 'Foo', '_Baz', '_Exc', '__builtins__',
|
||||
'__cached__', '__doc__', '__file__', '__name__',
|
||||
'__package__', '_quux', 'bar', 'qux']
|
||||
assert context['members'] == ['CONSTANT1', 'CONSTANT2', 'Exc', 'Foo', '_Baz', '_Exc',
|
||||
'__builtins__', '__cached__', '__doc__', '__file__',
|
||||
'__name__', '__package__', '_quux', 'bar', 'qux']
|
||||
assert context['functions'] == ['bar']
|
||||
assert context['all_functions'] == ['_quux', 'bar']
|
||||
assert context['classes'] == ['Foo']
|
||||
assert context['all_classes'] == ['Foo', '_Baz']
|
||||
assert context['exceptions'] == ['Exc']
|
||||
assert context['all_exceptions'] == ['Exc', '_Exc']
|
||||
assert context['attributes'] == ['qux']
|
||||
assert context['all_attributes'] == ['qux']
|
||||
assert context['attributes'] == ['CONSTANT1', 'qux']
|
||||
assert context['all_attributes'] == ['CONSTANT1', 'qux']
|
||||
assert context['fullname'] == 'autosummary_dummy_module'
|
||||
assert context['module'] == 'autosummary_dummy_module'
|
||||
assert context['objname'] == ''
|
||||
@@ -233,8 +239,9 @@ def test_autosummary_generate_content_for_module_skipped(app):
|
||||
generate_autosummary_content('autosummary_dummy_module', autosummary_dummy_module, None,
|
||||
template, None, False, app, False, {})
|
||||
context = template.render.call_args[0][1]
|
||||
assert context['members'] == ['_Baz', '_Exc', '__builtins__', '__cached__', '__doc__',
|
||||
'__file__', '__name__', '__package__', '_quux', 'qux']
|
||||
assert context['members'] == ['CONSTANT1', 'CONSTANT2', '_Baz', '_Exc', '__builtins__',
|
||||
'__cached__', '__doc__', '__file__', '__name__',
|
||||
'__package__', '_quux', 'qux']
|
||||
assert context['functions'] == []
|
||||
assert context['classes'] == []
|
||||
assert context['exceptions'] == []
|
||||
@@ -250,18 +257,18 @@ def test_autosummary_generate_content_for_module_imported_members(app):
|
||||
assert template.render.call_args[0][0] == 'module'
|
||||
|
||||
context = template.render.call_args[0][1]
|
||||
assert context['members'] == ['Exc', 'Foo', 'Union', '_Baz', '_Exc', '__builtins__',
|
||||
'__cached__', '__doc__', '__file__', '__loader__',
|
||||
'__name__', '__package__', '__spec__', '_quux',
|
||||
'bar', 'path', 'qux']
|
||||
assert context['members'] == ['CONSTANT1', 'CONSTANT2', 'Exc', 'Foo', 'Union', '_Baz',
|
||||
'_Exc', '__builtins__', '__cached__', '__doc__',
|
||||
'__file__', '__loader__', '__name__', '__package__',
|
||||
'__spec__', '_quux', 'bar', 'path', 'qux']
|
||||
assert context['functions'] == ['bar']
|
||||
assert context['all_functions'] == ['_quux', 'bar']
|
||||
assert context['classes'] == ['Foo']
|
||||
assert context['all_classes'] == ['Foo', '_Baz']
|
||||
assert context['exceptions'] == ['Exc']
|
||||
assert context['all_exceptions'] == ['Exc', '_Exc']
|
||||
assert context['attributes'] == ['qux']
|
||||
assert context['all_attributes'] == ['qux']
|
||||
assert context['attributes'] == ['CONSTANT1', 'qux']
|
||||
assert context['all_attributes'] == ['CONSTANT1', 'qux']
|
||||
assert context['fullname'] == 'autosummary_dummy_module'
|
||||
assert context['module'] == 'autosummary_dummy_module'
|
||||
assert context['objname'] == ''
|
||||
@@ -301,6 +308,11 @@ def test_autosummary_generate(app, status, warning):
|
||||
' \n'
|
||||
' Foo\n'
|
||||
' \n' in module)
|
||||
assert (' .. autosummary::\n'
|
||||
' \n'
|
||||
' CONSTANT1\n'
|
||||
' qux\n'
|
||||
' \n' in module)
|
||||
|
||||
Foo = (app.srcdir / 'generated' / 'autosummary_dummy_module.Foo.rst').read_text()
|
||||
assert '.. automethod:: __init__' in Foo
|
||||
@@ -311,6 +323,8 @@ def test_autosummary_generate(app, status, warning):
|
||||
' \n' in Foo)
|
||||
assert (' .. autosummary::\n'
|
||||
' \n'
|
||||
' ~Foo.CONSTANT3\n'
|
||||
' ~Foo.CONSTANT4\n'
|
||||
' ~Foo.baz\n'
|
||||
' \n' in Foo)
|
||||
|
||||
@@ -380,6 +394,20 @@ def test_autosummary_recursive(app, status, warning):
|
||||
assert 'package.package.module' in content
|
||||
|
||||
|
||||
@pytest.mark.sphinx('dummy', testroot='ext-autosummary-filename-map')
|
||||
def test_autosummary_filename_map(app, status, warning):
|
||||
app.build()
|
||||
|
||||
assert (app.srcdir / 'generated' / 'module_mangled.rst').exists()
|
||||
assert not (app.srcdir / 'generated' / 'autosummary_dummy_module.rst').exists()
|
||||
assert (app.srcdir / 'generated' / 'bar.rst').exists()
|
||||
assert not (app.srcdir / 'generated' / 'autosummary_dummy_module.bar.rst').exists()
|
||||
assert (app.srcdir / 'generated' / 'autosummary_dummy_module.Foo.rst').exists()
|
||||
|
||||
html_warnings = app._warning.getvalue()
|
||||
assert html_warnings == ''
|
||||
|
||||
|
||||
@pytest.mark.sphinx('latex', **default_kw)
|
||||
def test_autosummary_latex_table_colspec(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
@@ -109,7 +109,7 @@ def test_inheritance_diagram(app, status, warning):
|
||||
('dummy.test.B', 'dummy.test.B', [], None)
|
||||
]
|
||||
|
||||
# inheritance diagram with 2 top classes and specifiying the entire module
|
||||
# inheritance diagram with 2 top classes and specifying the entire module
|
||||
# rendering should be
|
||||
#
|
||||
# A
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
from unittest import TestCase, mock
|
||||
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.testing.util import simple_decorator
|
||||
from sphinx.ext.napoleon import _process_docstring, _skip_member, Config, setup
|
||||
|
||||
|
||||
@@ -49,6 +51,11 @@ class SampleClass:
|
||||
def __special_undoc__(self):
|
||||
pass
|
||||
|
||||
@simple_decorator
|
||||
def __decorated_func__(self):
|
||||
"""doc"""
|
||||
pass
|
||||
|
||||
|
||||
class SampleError(Exception):
|
||||
def _private_doc(self):
|
||||
@@ -129,16 +136,25 @@ class SkipMemberTest(TestCase):
|
||||
self.assertEqual(None, _skip_member(app, what, member, obj, skip,
|
||||
mock.Mock()))
|
||||
else:
|
||||
self.assertFalse(_skip_member(app, what, member, obj, skip,
|
||||
mock.Mock()))
|
||||
self.assertIs(_skip_member(app, what, member, obj, skip,
|
||||
mock.Mock()), False)
|
||||
setattr(app.config, config_name, False)
|
||||
self.assertEqual(None, _skip_member(app, what, member, obj, skip,
|
||||
mock.Mock()))
|
||||
|
||||
def test_namedtuple(self):
|
||||
self.assertSkip('class', '_asdict',
|
||||
SampleNamedTuple._asdict, False,
|
||||
'napoleon_include_private_with_doc')
|
||||
if sys.version_info < (3, 7):
|
||||
self.assertSkip('class', '_asdict',
|
||||
SampleNamedTuple._asdict, False,
|
||||
'napoleon_include_private_with_doc')
|
||||
else:
|
||||
# Since python 3.7, namedtuple._asdict() has not been documented
|
||||
# because there is no way to check the method is a member of the
|
||||
# namedtuple class. This testcase confirms only it does not
|
||||
# raise an error on building document (refs: #1455)
|
||||
self.assertSkip('class', '_asdict',
|
||||
SampleNamedTuple._asdict, True,
|
||||
'napoleon_include_private_with_doc')
|
||||
|
||||
def test_class_private_doc(self):
|
||||
self.assertSkip('class', '_private_doc',
|
||||
@@ -160,6 +176,11 @@ class SkipMemberTest(TestCase):
|
||||
SampleClass.__special_undoc__, True,
|
||||
'napoleon_include_special_with_doc')
|
||||
|
||||
def test_class_decorated_doc(self):
|
||||
self.assertSkip('class', '__decorated_func__',
|
||||
SampleClass.__decorated_func__, False,
|
||||
'napoleon_include_special_with_doc')
|
||||
|
||||
def test_exception_private_doc(self):
|
||||
self.assertSkip('exception', '_private_doc',
|
||||
SampleError._private_doc, False,
|
||||
|
||||
@@ -20,6 +20,12 @@ import pytest
|
||||
|
||||
from sphinx.ext.napoleon import Config
|
||||
from sphinx.ext.napoleon.docstring import GoogleDocstring, NumpyDocstring
|
||||
from sphinx.ext.napoleon.docstring import (
|
||||
_tokenize_type_spec,
|
||||
_recombine_set_tokens,
|
||||
_convert_numpy_type_spec,
|
||||
_token_type
|
||||
)
|
||||
|
||||
|
||||
class NamedtupleSubclass(namedtuple('NamedtupleSubclass', ('attr1', 'attr2'))):
|
||||
@@ -57,19 +63,22 @@ class NamedtupleSubclassTest(BaseDocstringTest):
|
||||
Sample namedtuple subclass
|
||||
|
||||
.. attribute:: attr1
|
||||
:type: Arbitrary type
|
||||
|
||||
Quick description of attr1
|
||||
|
||||
:type: Arbitrary type
|
||||
|
||||
.. attribute:: attr2
|
||||
:type: Another arbitrary type
|
||||
|
||||
Quick description of attr2
|
||||
|
||||
:type: Another arbitrary type
|
||||
|
||||
.. attribute:: attr3
|
||||
:type: Type
|
||||
|
||||
Adds a newline after the type
|
||||
|
||||
:type: Type
|
||||
"""
|
||||
|
||||
self.assertEqual(expected, actual)
|
||||
@@ -413,9 +422,10 @@ Attributes:
|
||||
actual = str(GoogleDocstring(docstring))
|
||||
expected = """\
|
||||
.. attribute:: in_attr
|
||||
:type: :class:`numpy.ndarray`
|
||||
|
||||
super-dooper attribute
|
||||
|
||||
:type: :class:`numpy.ndarray`
|
||||
"""
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
@@ -427,9 +437,10 @@ Attributes:
|
||||
actual = str(GoogleDocstring(docstring))
|
||||
expected = """\
|
||||
.. attribute:: in_attr
|
||||
:type: numpy.ndarray
|
||||
|
||||
super-dooper attribute
|
||||
|
||||
:type: numpy.ndarray
|
||||
"""
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
@@ -1992,6 +2003,154 @@ definition_after_normal_text : int
|
||||
actual = str(NumpyDocstring(docstring, config))
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_token_type(self):
|
||||
tokens = (
|
||||
("1", "literal"),
|
||||
("'string'", "literal"),
|
||||
('"another_string"', "literal"),
|
||||
("{1, 2}", "literal"),
|
||||
("{'va{ue', 'set'}", "literal"),
|
||||
("optional", "control"),
|
||||
("default", "control"),
|
||||
(", ", "delimiter"),
|
||||
(" of ", "delimiter"),
|
||||
(" or ", "delimiter"),
|
||||
(": ", "delimiter"),
|
||||
("True", "obj"),
|
||||
("None", "obj"),
|
||||
("name", "obj"),
|
||||
(":py:class:`Enum`", "reference"),
|
||||
)
|
||||
|
||||
for token, expected in tokens:
|
||||
actual = _token_type(token)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_tokenize_type_spec(self):
|
||||
specs = (
|
||||
"str",
|
||||
"int or float or None, optional",
|
||||
'{"F", "C", "N"}',
|
||||
"{'F', 'C', 'N'}, default: 'F'",
|
||||
"{'F', 'C', 'N or C'}, default 'F'",
|
||||
'"ma{icious"',
|
||||
r"'with \'quotes\''",
|
||||
)
|
||||
|
||||
tokens = (
|
||||
["str"],
|
||||
["int", " or ", "float", " or ", "None", ", ", "optional"],
|
||||
["{", '"F"', ", ", '"C"', ", ", '"N"', "}"],
|
||||
["{", "'F'", ", ", "'C'", ", ", "'N'", "}", ", ", "default", ": ", "'F'"],
|
||||
["{", "'F'", ", ", "'C'", ", ", "'N or C'", "}", ", ", "default", " ", "'F'"],
|
||||
['"ma{icious"'],
|
||||
[r"'with \'quotes\''"],
|
||||
)
|
||||
|
||||
for spec, expected in zip(specs, tokens):
|
||||
actual = _tokenize_type_spec(spec)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_recombine_set_tokens(self):
|
||||
tokens = (
|
||||
["{", "1", ", ", "2", "}"],
|
||||
["{", '"F"', ", ", '"C"', ", ", '"N"', "}", ", ", "optional"],
|
||||
["{", "'F'", ", ", "'C'", ", ", "'N'", "}", ", ", "default", ": ", "None"],
|
||||
)
|
||||
|
||||
combined_tokens = (
|
||||
["{1, 2}"],
|
||||
['{"F", "C", "N"}', ", ", "optional"],
|
||||
["{'F', 'C', 'N'}", ", ", "default", ": ", "None"],
|
||||
)
|
||||
|
||||
for tokens_, expected in zip(tokens, combined_tokens):
|
||||
actual = _recombine_set_tokens(tokens_)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_recombine_set_tokens_invalid(self):
|
||||
tokens = (
|
||||
["{", "1", ", ", "2"],
|
||||
['"F"', ", ", '"C"', ", ", '"N"', "}", ", ", "optional"],
|
||||
["{", "1", ", ", "2", ", ", "default", ": ", "None"],
|
||||
)
|
||||
combined_tokens = (
|
||||
["{1, 2"],
|
||||
['"F"', ", ", '"C"', ", ", '"N"', "}", ", ", "optional"],
|
||||
["{1, 2", ", ", "default", ": ", "None"],
|
||||
)
|
||||
|
||||
for tokens_, expected in zip(tokens, combined_tokens):
|
||||
actual = _recombine_set_tokens(tokens_)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_convert_numpy_type_spec(self):
|
||||
translations = {
|
||||
"DataFrame": "pandas.DataFrame",
|
||||
}
|
||||
|
||||
specs = (
|
||||
"",
|
||||
"optional",
|
||||
"str, optional",
|
||||
"int or float or None, default: None",
|
||||
'{"F", "C", "N"}',
|
||||
"{'F', 'C', 'N'}, default: 'N'",
|
||||
"DataFrame, optional",
|
||||
)
|
||||
|
||||
converted = (
|
||||
"",
|
||||
"*optional*",
|
||||
":class:`str`, *optional*",
|
||||
":class:`int` or :class:`float` or :obj:`None`, *default*: :obj:`None`",
|
||||
'``{"F", "C", "N"}``',
|
||||
"``{'F', 'C', 'N'}``, *default*: ``'N'``",
|
||||
":class:`pandas.DataFrame`, *optional*",
|
||||
)
|
||||
|
||||
for spec, expected in zip(specs, converted):
|
||||
actual = _convert_numpy_type_spec(spec, translations=translations)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_parameter_types(self):
|
||||
docstring = dedent("""\
|
||||
Parameters
|
||||
----------
|
||||
param1 : DataFrame
|
||||
the data to work on
|
||||
param2 : int or float or None
|
||||
a parameter with different types
|
||||
param3 : dict-like, optional
|
||||
a optional mapping
|
||||
param4 : int or float or None, optional
|
||||
a optional parameter with different types
|
||||
param5 : {"F", "C", "N"}, optional
|
||||
a optional parameter with fixed values
|
||||
""")
|
||||
expected = dedent("""\
|
||||
:param param1: the data to work on
|
||||
:type param1: DataFrame
|
||||
:param param2: a parameter with different types
|
||||
:type param2: :class:`int` or :class:`float` or :obj:`None`
|
||||
:param param3: a optional mapping
|
||||
:type param3: :term:`dict-like <mapping>`, *optional*
|
||||
:param param4: a optional parameter with different types
|
||||
:type param4: :class:`int` or :class:`float` or :obj:`None`, *optional*
|
||||
:param param5: a optional parameter with fixed values
|
||||
:type param5: ``{"F", "C", "N"}``, *optional*
|
||||
""")
|
||||
translations = {
|
||||
"dict-like": ":term:`dict-like <mapping>`",
|
||||
}
|
||||
config = Config(
|
||||
napoleon_use_param=True,
|
||||
napoleon_use_rtype=True,
|
||||
napoleon_type_aliases=translations,
|
||||
)
|
||||
actual = str(NumpyDocstring(docstring, config))
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_keywords_with_types(self):
|
||||
docstring = """\
|
||||
Do as you please
|
||||
@@ -2019,9 +2178,31 @@ def warns(warning, match):
|
||||
warnings = [w for w in raw_warnings.split("\n") if w.strip()]
|
||||
|
||||
assert len(warnings) == 1 and all(match_re.match(w) for w in warnings)
|
||||
warning.truncate(0)
|
||||
|
||||
|
||||
class TestNumpyDocstring:
|
||||
def test_token_type_invalid(self, warning):
|
||||
tokens = (
|
||||
"{1, 2",
|
||||
"}",
|
||||
"'abc",
|
||||
"def'",
|
||||
'"ghi',
|
||||
'jkl"',
|
||||
)
|
||||
errors = (
|
||||
r".+: invalid value set \(missing closing brace\):",
|
||||
r".+: invalid value set \(missing opening brace\):",
|
||||
r".+: malformed string literal \(missing closing quote\):",
|
||||
r".+: malformed string literal \(missing opening quote\):",
|
||||
r".+: malformed string literal \(missing closing quote\):",
|
||||
r".+: malformed string literal \(missing opening quote\):",
|
||||
)
|
||||
for token, error in zip(tokens, errors):
|
||||
with warns(warning, match=error):
|
||||
_token_type(token)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
["spec", "pattern"],
|
||||
(
|
||||
|
||||
@@ -956,9 +956,9 @@ def test_xml_role_xref(app):
|
||||
'glossary_terms#term-Some-term'])
|
||||
assert_elem(
|
||||
para2[1],
|
||||
['LINK TO', 'SAME TYPE LINKS', 'AND',
|
||||
"I18N ROCK'N ROLE XREF", '.'],
|
||||
['same-type-links', 'i18n-role-xref'])
|
||||
['LINK TO', 'LABEL', 'AND',
|
||||
'SAME TYPE LINKS', 'AND', 'SAME TYPE LINKS', '.'],
|
||||
['i18n-role-xref', 'same-type-links', 'same-type-links'])
|
||||
assert_elem(
|
||||
para2[2],
|
||||
['LINK TO', 'I18N WITH GLOSSARY TERMS', 'AND', 'CONTENTS', '.'],
|
||||
|
||||
@@ -50,7 +50,7 @@ def test_RSTParser_prolog_epilog(RSTStateMachine, app):
|
||||
(content, _), _ = RSTStateMachine().run.call_args
|
||||
assert list(content.xitems()) == [('dummy.rst', 0, 'hello Sphinx world'),
|
||||
('dummy.rst', 1, 'Sphinx is a document generator'),
|
||||
('<generated>', 0, ''),
|
||||
('dummy.rst', 2, ''),
|
||||
('<rst_epilog>', 0, 'this is rst_epilog'),
|
||||
('<rst_epilog>', 1, 'good-bye reST!')]
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ def test_signature_partialmethod():
|
||||
|
||||
def test_signature_annotations():
|
||||
from typing_test_data import (f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10,
|
||||
f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, Node)
|
||||
f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, f21, Node)
|
||||
|
||||
# Class annotations
|
||||
sig = inspect.signature(f0)
|
||||
@@ -214,6 +214,10 @@ def test_signature_annotations():
|
||||
sig = inspect.signature(f19)
|
||||
assert stringify_signature(sig) == '(*args: int, **kwargs: str)'
|
||||
|
||||
# default value is inspect.Signature.empty
|
||||
sig = inspect.signature(f21)
|
||||
assert stringify_signature(sig) == "(arg1='whatever', arg2)"
|
||||
|
||||
# type hints by string
|
||||
sig = inspect.signature(Node.children)
|
||||
if (3, 5, 0) <= sys.version_info < (3, 5, 3):
|
||||
|
||||
@@ -32,7 +32,7 @@ def test_append_epilog(app):
|
||||
|
||||
assert list(content.xitems()) == [('dummy.rst', 0, 'hello Sphinx world'),
|
||||
('dummy.rst', 1, 'Sphinx is a document generator'),
|
||||
('<generated>', 0, ''),
|
||||
('dummy.rst', 2, ''),
|
||||
('<rst_epilog>', 0, 'this is rst_epilog'),
|
||||
('<rst_epilog>', 1, 'good-bye reST!')]
|
||||
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
|
||||
import sys
|
||||
from numbers import Integral
|
||||
from typing import Any, Dict, List, TypeVar, Union, Callable, Tuple, Optional, Generic
|
||||
from typing import (
|
||||
Any, Dict, Generator, List, TypeVar, Union, Callable, Tuple, Optional, Generic
|
||||
)
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -48,6 +50,7 @@ def test_stringify_type_hints_containers():
|
||||
assert stringify(Tuple[str, ...]) == "Tuple[str, ...]"
|
||||
assert stringify(List[Dict[str, Tuple]]) == "List[Dict[str, Tuple]]"
|
||||
assert stringify(MyList[Tuple[int, int]]) == "test_util_typing.MyList[Tuple[int, int]]"
|
||||
assert stringify(Generator[None, None, None]) == "Generator[None, None, None]"
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 9), reason='python 3.9+ is required.')
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from inspect import Signature
|
||||
from numbers import Integral
|
||||
from typing import Any, Dict, List, TypeVar, Union, Callable, Tuple, Optional
|
||||
|
||||
@@ -100,6 +101,9 @@ def f20() -> Optional[Union[int, str]]:
|
||||
pass
|
||||
|
||||
|
||||
def f21(arg1='whatever', arg2=Signature.empty):
|
||||
pass
|
||||
|
||||
|
||||
class Node:
|
||||
def __init__(self, parent: Optional['Node']) -> None:
|
||||
|
||||
Reference in New Issue
Block a user