mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '4.x' into fix-footnote-in-info
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Test module for napoleon PEP 526 compatiblity with google style
|
||||
Test module for napoleon PEP 526 compatibility with google style
|
||||
"""
|
||||
|
||||
module_level_var: int = 99
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Test module for napoleon PEP 526 compatiblity with numpy style
|
||||
Test module for napoleon PEP 526 compatibility with numpy style
|
||||
"""
|
||||
|
||||
module_level_var: int = 99
|
||||
|
||||
@@ -114,6 +114,20 @@ class InnerChild(Outer.Inner):
|
||||
|
||||
|
||||
class DocstringSig(object):
|
||||
def __new__(cls, *new_args, **new_kwargs):
|
||||
"""__new__(cls, d, e=1) -> DocstringSig
|
||||
First line of docstring
|
||||
|
||||
rest of docstring
|
||||
"""
|
||||
|
||||
def __init__(self, *init_args, **init_kwargs):
|
||||
"""__init__(self, a, b=1) -> None
|
||||
First line of docstring
|
||||
|
||||
rest of docstring
|
||||
"""
|
||||
|
||||
def meth(self):
|
||||
"""meth(FOO, BAR=1) -> BAZ
|
||||
First line of docstring
|
||||
|
||||
@@ -29,7 +29,14 @@ class Quux(List[Union[int, float]]):
|
||||
pass
|
||||
|
||||
|
||||
class Corge(Quux):
|
||||
pass
|
||||
|
||||
|
||||
Alias = Foo
|
||||
|
||||
#: docstring
|
||||
OtherAlias = Bar
|
||||
|
||||
#: docstring
|
||||
IntAlias = int
|
||||
|
||||
@@ -17,6 +17,10 @@ class AsyncClass:
|
||||
"""A documented coroutine staticmethod"""
|
||||
pass
|
||||
|
||||
async def do_asyncgen(self):
|
||||
"""A documented async generator"""
|
||||
yield
|
||||
|
||||
|
||||
async def _other_coro_func():
|
||||
return "run"
|
||||
|
||||
@@ -8,6 +8,10 @@ def func():
|
||||
async def coroutinefunc():
|
||||
pass
|
||||
|
||||
|
||||
async def asyncgenerator():
|
||||
yield
|
||||
|
||||
partial_func = partial(func)
|
||||
partial_coroutinefunc = partial(coroutinefunc)
|
||||
|
||||
|
||||
@@ -22,6 +22,10 @@ def func(arg: missing_module.Class):
|
||||
|
||||
class TestAutodoc(object):
|
||||
"""TestAutodoc docstring."""
|
||||
|
||||
#: docstring
|
||||
Alias = missing_module2.Class
|
||||
|
||||
@missing_name
|
||||
def decoratedMethod(self):
|
||||
"""TestAutodoc::decoratedMethod docstring"""
|
||||
@@ -34,3 +38,6 @@ class Inherited(missing_module.Class):
|
||||
|
||||
|
||||
sphinx.missing_module4.missing_function(len(missing_name2))
|
||||
|
||||
#: docstring
|
||||
Alias = missing_module2.Class
|
||||
|
||||
@@ -6,14 +6,15 @@ SENTINEL = object()
|
||||
|
||||
|
||||
def foo(name: str = CONSTANT,
|
||||
sentinal: Any = SENTINEL,
|
||||
now: datetime = datetime.now()) -> None:
|
||||
sentinel: Any = SENTINEL,
|
||||
now: datetime = datetime.now(),
|
||||
color: int = 0xFFFFFF) -> None:
|
||||
"""docstring"""
|
||||
|
||||
|
||||
class Class:
|
||||
"""docstring"""
|
||||
|
||||
def meth(self, name: str = CONSTANT, sentinal: Any = SENTINEL,
|
||||
now: datetime = datetime.now()) -> None:
|
||||
def meth(self, name: str = CONSTANT, sentinel: Any = SENTINEL,
|
||||
now: datetime = datetime.now(), color: int = 0xFFFFFF) -> None:
|
||||
"""docstring"""
|
||||
|
||||
@@ -2,5 +2,10 @@ class Foo:
|
||||
"""docstring"""
|
||||
|
||||
@property
|
||||
def prop(self) -> int:
|
||||
def prop1(self) -> int:
|
||||
"""docstring"""
|
||||
|
||||
@classmethod
|
||||
@property
|
||||
def prop2(self) -> int:
|
||||
"""docstring"""
|
||||
|
||||
@@ -10,6 +10,7 @@ class Bar:
|
||||
__slots__ = {'attr1': 'docstring of attr1',
|
||||
'attr2': 'docstring of attr2',
|
||||
'attr3': None}
|
||||
__annotations__ = {'attr1': int}
|
||||
|
||||
def __init__(self):
|
||||
self.attr2 = None #: docstring of instance attr2
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
from typing import Any, Tuple, Union
|
||||
|
||||
CONST1: int
|
||||
CONST2: int = 1
|
||||
|
||||
|
||||
def incr(a: int, b: int = 1) -> int:
|
||||
return a + b
|
||||
@@ -11,6 +14,9 @@ def decr(a, b = 1):
|
||||
|
||||
|
||||
class Math:
|
||||
CONST1: int
|
||||
CONST2: int = 1
|
||||
|
||||
def __init__(self, s: str, o: Any = None) -> None:
|
||||
pass
|
||||
|
||||
@@ -32,6 +38,10 @@ class Math:
|
||||
# type: (...) -> None
|
||||
return
|
||||
|
||||
@property
|
||||
def prop(self) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def tuple_args(x: Tuple[int, Union[int, str]]) -> Tuple[int, int]:
|
||||
pass
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
from os import path # NOQA
|
||||
from typing import Union
|
||||
|
||||
__all__ = [
|
||||
"CONSTANT1",
|
||||
"Exc",
|
||||
"Foo",
|
||||
"_Baz",
|
||||
"bar",
|
||||
"qux",
|
||||
"path",
|
||||
]
|
||||
|
||||
#: module variable
|
||||
CONSTANT1 = None
|
||||
CONSTANT2 = None
|
||||
@@ -48,3 +58,5 @@ class _Exc(Exception):
|
||||
|
||||
#: a module-level attribute
|
||||
qux = 2
|
||||
#: a module-level attribute that has been excluded from __all__
|
||||
quuz = 2
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
extensions = ['sphinx.ext.extlinks']
|
||||
extlinks = {
|
||||
'user': ('https://github.com/%s', '@%s'),
|
||||
'repo': ('https://github.com/%s', 'project %s'),
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
test-ext-extlinks-hardcoded-urls
|
||||
================================
|
||||
|
||||
.. Links generated by extlinks extension should not raise any warnings.
|
||||
.. Only hardcoded URLs are affected.
|
||||
|
||||
:user:`octocat`
|
||||
|
||||
:repo:`sphinx-doc/sphinx`
|
||||
|
||||
.. hardcoded replaceable link which can be replaced as
|
||||
.. :repo:`octocat` or :user:`octocat`
|
||||
|
||||
https://github.com/octocat
|
||||
|
||||
`inline replaceable link <https://github.com/octocat>`_
|
||||
|
||||
`replaceable link`_
|
||||
|
||||
.. hyperlinks
|
||||
|
||||
.. _replaceable link: https://github.com/octocat
|
||||
2
tests/roots/test-ext-extlinks-hardcoded-urls/conf.py
Normal file
2
tests/roots/test-ext-extlinks-hardcoded-urls/conf.py
Normal file
@@ -0,0 +1,2 @@
|
||||
extensions = ['sphinx.ext.extlinks']
|
||||
extlinks = {'issue': ('https://github.com/sphinx-doc/sphinx/issues/%s', 'issue %s')}
|
||||
28
tests/roots/test-ext-extlinks-hardcoded-urls/index.rst
Normal file
28
tests/roots/test-ext-extlinks-hardcoded-urls/index.rst
Normal file
@@ -0,0 +1,28 @@
|
||||
test-ext-extlinks-hardcoded-urls
|
||||
================================
|
||||
|
||||
.. Links generated by extlinks extension should not raise any warnings.
|
||||
.. Only hardcoded URLs are affected.
|
||||
|
||||
:issue:`1`
|
||||
|
||||
.. hardcoded replaceable link
|
||||
|
||||
https://github.com/sphinx-doc/sphinx/issues/1
|
||||
|
||||
`inline replaceable link <https://github.com/sphinx-doc/sphinx/issues/1>`_
|
||||
|
||||
`replaceable link`_
|
||||
|
||||
.. hardcoded non-replaceable link
|
||||
|
||||
https://github.com/sphinx-doc/sphinx/pulls/1
|
||||
|
||||
`inline non-replaceable link <https://github.com/sphinx-doc/sphinx/pulls/1>`_
|
||||
|
||||
`non-replaceable link`_
|
||||
|
||||
.. hyperlinks
|
||||
|
||||
.. _replaceable link: https://github.com/sphinx-doc/sphinx/issues/1
|
||||
.. _non-replaceable link: https://github.com/sphinx-doc/sphinx/pulls/1
|
||||
@@ -103,7 +103,7 @@ Javascript items
|
||||
.. js:function:: bar.baz(href, callback[, errback])
|
||||
|
||||
:param string href: The location of the resource.
|
||||
:param callback: Get's called with the data returned by the resource.
|
||||
:param callback: Gets called with the data returned by the resource.
|
||||
:throws InvalidHref: If the `href` is invalid.
|
||||
:returns: `undefined`
|
||||
|
||||
|
||||
1
tests/roots/test-html_signaturereturn_icon/conf.py
Normal file
1
tests/roots/test-html_signaturereturn_icon/conf.py
Normal file
@@ -0,0 +1 @@
|
||||
extensions = ['sphinx.ext.autodoc']
|
||||
4
tests/roots/test-html_signaturereturn_icon/index.rst
Normal file
4
tests/roots/test-html_signaturereturn_icon/index.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
test-html_signaturereturn_icon
|
||||
==============================
|
||||
|
||||
.. py:function:: foo(a: bool, b: int) -> str
|
||||
0
tests/roots/test-latex-container/conf.py
Normal file
0
tests/roots/test-latex-container/conf.py
Normal file
4
tests/roots/test-latex-container/index.rst
Normal file
4
tests/roots/test-latex-container/index.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
.. container:: classname
|
||||
|
||||
text
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
Broken link
|
||||
===========
|
||||
|
||||
Some links are `broken <https://www.sphinx-doc.org/this-is-another-broken-link>`__
|
||||
but sometimes not worrying about some broken links is a valid strategy.
|
||||
@@ -0,0 +1,5 @@
|
||||
Broken link
|
||||
===========
|
||||
|
||||
Some links are `broken <https://www.sphinx-doc.org/this-is-a-broken-link>`__
|
||||
but sometimes not worrying about some broken links is a valid strategy.
|
||||
5
tests/roots/test-linkcheck-documents_exclude/conf.py
Normal file
5
tests/roots/test-linkcheck-documents_exclude/conf.py
Normal file
@@ -0,0 +1,5 @@
|
||||
exclude_patterns = ['_build']
|
||||
linkcheck_exclude_documents = [
|
||||
'^broken_link$',
|
||||
'br[0-9]ken_link',
|
||||
]
|
||||
3
tests/roots/test-linkcheck-documents_exclude/index.rst
Normal file
3
tests/roots/test-linkcheck-documents_exclude/index.rst
Normal file
@@ -0,0 +1,3 @@
|
||||
.. toctree::
|
||||
broken_link
|
||||
br0ken_link
|
||||
@@ -0,0 +1 @@
|
||||
exclude_patterns = ['_build']
|
||||
@@ -0,0 +1,2 @@
|
||||
`local server1 <http://localhost:7777/path1>`_
|
||||
`local server2 <http://localhost:7777/path2>`_
|
||||
@@ -13,8 +13,7 @@ Some additional anchors to exercise ignore code
|
||||
* `Complete nonsense <https://localhost:7777/doesnotexist>`_
|
||||
* `Example valid local file <conf.py>`_
|
||||
* `Example invalid local file <path/to/notfound>`_
|
||||
* https://github.com/sphinx-doc/sphinx#documentation
|
||||
* https://github.com/sphinx-doc/sphinx#user-content-testing
|
||||
* https://github.com/sphinx-doc/sphinx/blob/4.x/sphinx/__init__.py#L2
|
||||
|
||||
.. image:: https://www.google.com/image.png
|
||||
.. figure:: https://www.google.com/image2.png
|
||||
|
||||
4
tests/roots/test-local-logo/conf.py
Normal file
4
tests/roots/test-local-logo/conf.py
Normal file
@@ -0,0 +1,4 @@
|
||||
latex_documents = [
|
||||
('index', 'test.tex', 'The basic Sphinx documentation for testing', 'Sphinx', 'report')
|
||||
]
|
||||
html_logo = "images/img.png"
|
||||
BIN
tests/roots/test-local-logo/images/img.png
Normal file
BIN
tests/roots/test-local-logo/images/img.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 65 KiB |
32
tests/roots/test-local-logo/index.rst
Normal file
32
tests/roots/test-local-logo/index.rst
Normal file
@@ -0,0 +1,32 @@
|
||||
The basic Sphinx documentation for testing
|
||||
==========================================
|
||||
|
||||
Sphinx is a tool that makes it easy to create intelligent and beautiful
|
||||
documentation for Python projects (or other documents consisting of multiple
|
||||
reStructuredText sources), written by Georg Brandl. It was originally created
|
||||
for the new Python documentation, and has excellent facilities for Python
|
||||
project documentation, but C/C++ is supported as well, and more languages are
|
||||
planned.
|
||||
|
||||
Sphinx uses reStructuredText as its markup language, and many of its strengths
|
||||
come from the power and straightforwardness of reStructuredText and its parsing
|
||||
and translating suite, the Docutils.
|
||||
|
||||
features
|
||||
--------
|
||||
|
||||
Among its features are the following:
|
||||
|
||||
* Output formats: HTML (including derivative formats such as HTML Help, Epub
|
||||
and Qt Help), plain text, manual pages and LaTeX or direct PDF output
|
||||
using rst2pdf
|
||||
* Extensive cross-references: semantic markup and automatic links
|
||||
for functions, classes, glossary terms and similar pieces of information
|
||||
* Hierarchical structure: easy definition of a document tree, with automatic
|
||||
links to siblings, parents and children
|
||||
* Automatic indices: general index as well as a module index
|
||||
* Code handling: automatic highlighting using the Pygments highlighter
|
||||
* Flexible HTML output using the Jinja 2 templating engine
|
||||
* Various extensions are available, e.g. for automatic testing of snippets
|
||||
and inclusion of appropriately formatted docstrings
|
||||
* Setuptools integration
|
||||
5
tests/roots/test-remote-logo/conf.py
Normal file
5
tests/roots/test-remote-logo/conf.py
Normal file
@@ -0,0 +1,5 @@
|
||||
latex_documents = [
|
||||
('index', 'test.tex', 'The basic Sphinx documentation for testing', 'Sphinx', 'report')
|
||||
]
|
||||
html_logo = "https://www.python.org/static/img/python-logo.png"
|
||||
html_favicon = "https://www.python.org/static/favicon.ico"
|
||||
32
tests/roots/test-remote-logo/index.rst
Normal file
32
tests/roots/test-remote-logo/index.rst
Normal file
@@ -0,0 +1,32 @@
|
||||
The basic Sphinx documentation for testing
|
||||
==========================================
|
||||
|
||||
Sphinx is a tool that makes it easy to create intelligent and beautiful
|
||||
documentation for Python projects (or other documents consisting of multiple
|
||||
reStructuredText sources), written by Georg Brandl. It was originally created
|
||||
for the new Python documentation, and has excellent facilities for Python
|
||||
project documentation, but C/C++ is supported as well, and more languages are
|
||||
planned.
|
||||
|
||||
Sphinx uses reStructuredText as its markup language, and many of its strengths
|
||||
come from the power and straightforwardness of reStructuredText and its parsing
|
||||
and translating suite, the Docutils.
|
||||
|
||||
features
|
||||
--------
|
||||
|
||||
Among its features are the following:
|
||||
|
||||
* Output formats: HTML (including derivative formats such as HTML Help, Epub
|
||||
and Qt Help), plain text, manual pages and LaTeX or direct PDF output
|
||||
using rst2pdf
|
||||
* Extensive cross-references: semantic markup and automatic links
|
||||
for functions, classes, glossary terms and similar pieces of information
|
||||
* Hierarchical structure: easy definition of a document tree, with automatic
|
||||
links to siblings, parents and children
|
||||
* Automatic indices: general index as well as a module index
|
||||
* Code handling: automatic highlighting using the Pygments highlighter
|
||||
* Flexible HTML output using the Jinja 2 templating engine
|
||||
* Various extensions are available, e.g. for automatic testing of snippets
|
||||
and inclusion of appropriately formatted docstrings
|
||||
* Setuptools integration
|
||||
0
tests/roots/test-root/file_with_special_#_chars.xyz
Normal file
0
tests/roots/test-root/file_with_special_#_chars.xyz
Normal file
@@ -32,7 +32,7 @@ footnotes in table
|
||||
:header-rows: 1
|
||||
|
||||
* - name [#]_
|
||||
- desription
|
||||
- description
|
||||
* - VIDIOC_CROPCAP
|
||||
- Information about VIDIOC_CROPCAP [#]_
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ Testing downloadable files
|
||||
|
||||
Download :download:`img.png` here.
|
||||
Download :download:`this <subdir/img.png>` there.
|
||||
Download :download:`file with special characters <file_with_special_#_chars.xyz>`.
|
||||
|
||||
Test file and literal inclusion
|
||||
===============================
|
||||
|
||||
@@ -243,7 +243,7 @@ Figures
|
||||
|
||||
My description paragraph of the figure.
|
||||
|
||||
Description paragraph is wraped with legend node.
|
||||
Description paragraph is wrapped with legend node.
|
||||
|
||||
.. figure:: rimg.png
|
||||
:align: right
|
||||
|
||||
@@ -129,7 +129,7 @@ Javascript items
|
||||
.. js:function:: bar.baz(href, callback[, errback])
|
||||
|
||||
:param string href: The location of the resource.
|
||||
:param callback: Get's called with the data returned by the resource.
|
||||
:param callback: Gets called with the data returned by the resource.
|
||||
:throws InvalidHref: If the `href` is invalid.
|
||||
:returns: `undefined`
|
||||
|
||||
|
||||
@@ -2,3 +2,7 @@ test-smartquotes
|
||||
================
|
||||
|
||||
-- "Sphinx" is a tool that makes it easy ...
|
||||
|
||||
.. toctree::
|
||||
|
||||
literals
|
||||
|
||||
12
tests/roots/test-smartquotes/literals.rst
Normal file
12
tests/roots/test-smartquotes/literals.rst
Normal file
@@ -0,0 +1,12 @@
|
||||
literals
|
||||
========
|
||||
|
||||
.. role:: python(code)
|
||||
:language: python
|
||||
.. default-role:: python
|
||||
|
||||
Standard :code:`code role with 'quotes'`
|
||||
|
||||
This is a Python :python:`{'code': 'role', 'with': 'quotes'}`.
|
||||
|
||||
This is a ``literal with 'quotes'``
|
||||
@@ -346,7 +346,7 @@ def test_epub_css_files(app):
|
||||
content = (app.outdir / 'index.xhtml').read_text()
|
||||
assert '<link rel="stylesheet" type="text/css" href="_static/css/epub.css" />' in content
|
||||
|
||||
# files in html_css_files are not outputed
|
||||
# files in html_css_files are not outputted
|
||||
assert ('<link rel="stylesheet" type="text/css" href="_static/css/style.css" />'
|
||||
not in content)
|
||||
assert ('<link media="print" rel="stylesheet" title="title" type="text/css" '
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
|
||||
import os
|
||||
import re
|
||||
from distutils.version import LooseVersion
|
||||
from itertools import chain, cycle
|
||||
from unittest.mock import ANY, call, patch
|
||||
|
||||
import pygments
|
||||
import pytest
|
||||
from html5lib import HTMLParser
|
||||
from packaging import version
|
||||
|
||||
from sphinx.builders.html import validate_html_extra_path, validate_html_static_path
|
||||
from sphinx.errors import ConfigError
|
||||
@@ -30,6 +30,9 @@ else:
|
||||
FIGURE_CAPTION = ".//figure/figcaption/p"
|
||||
|
||||
|
||||
PYGMENTS_VERSION = version.parse(pygments.__version__).release
|
||||
|
||||
|
||||
ENV_WARNINGS = """\
|
||||
%(root)s/autodoc_fodder.py:docstring of autodoc_fodder.MarkupError:\\d+: \
|
||||
WARNING: Explicit markup ends without a blank line; unexpected unindent.
|
||||
@@ -222,9 +225,9 @@ def test_html4_output(app, status, warning):
|
||||
(".//a[@href='https://www.python.org/dev/peps/pep-0008']"
|
||||
"[@class='pep reference external']/strong",
|
||||
'Python Enhancement Proposal #8'),
|
||||
(".//a[@href='https://tools.ietf.org/html/rfc1.html']"
|
||||
(".//a[@href='https://datatracker.ietf.org/doc/html/rfc1.html']"
|
||||
"[@class='rfc reference external']/strong", 'RFC 1'),
|
||||
(".//a[@href='https://tools.ietf.org/html/rfc1.html']"
|
||||
(".//a[@href='https://datatracker.ietf.org/doc/html/rfc1.html']"
|
||||
"[@class='rfc reference external']/strong", 'Request for Comments #1'),
|
||||
(".//a[@href='objects.html#envvar-HOME']"
|
||||
"[@class='reference internal']/code/span[@class='pre']", 'HOME'),
|
||||
@@ -456,6 +459,12 @@ def test_html_download(app):
|
||||
assert (app.outdir / matched.group(1)).exists()
|
||||
assert matched.group(1) == filename
|
||||
|
||||
pattern = ('<a class="reference download internal" download="" '
|
||||
'href="(_downloads/.*/)(file_with_special_%23_chars.xyz)">')
|
||||
matched = re.search(pattern, result)
|
||||
assert matched
|
||||
assert (app.outdir / matched.group(1) / "file_with_special_#_chars.xyz").exists()
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='roles-download')
|
||||
def test_html_download_role(app, status, warning):
|
||||
@@ -1330,6 +1339,25 @@ def test_html_remote_images(app, status, warning):
|
||||
assert not (app.outdir / 'python-logo.png').exists()
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='remote-logo')
|
||||
def test_html_remote_logo(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
result = (app.outdir / 'index.html').read_text()
|
||||
assert ('<img class="logo" src="https://www.python.org/static/img/python-logo.png" alt="Logo"/>' in result)
|
||||
assert ('<link rel="shortcut icon" href="https://www.python.org/static/favicon.ico"/>' in result)
|
||||
assert not (app.outdir / 'python-logo.png').exists()
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='local-logo')
|
||||
def test_html_local_logo(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
result = (app.outdir / 'index.html').read_text()
|
||||
assert ('<img class="logo" src="_static/img.png" alt="Logo"/>' in result)
|
||||
assert (app.outdir / '_static/img.png').exists()
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='basic')
|
||||
def test_html_sidebar(app, status, warning):
|
||||
ctx = {}
|
||||
@@ -1551,8 +1579,7 @@ def test_html_codeblock_linenos_style_table(app):
|
||||
app.build()
|
||||
content = (app.outdir / 'index.html').read_text()
|
||||
|
||||
pygments_version = tuple(LooseVersion(pygments.__version__).version)
|
||||
if pygments_version >= (2, 8):
|
||||
if PYGMENTS_VERSION >= (2, 8):
|
||||
assert ('<div class="linenodiv"><pre><span class="normal">1</span>\n'
|
||||
'<span class="normal">2</span>\n'
|
||||
'<span class="normal">3</span>\n'
|
||||
@@ -1567,8 +1594,7 @@ def test_html_codeblock_linenos_style_inline(app):
|
||||
app.build()
|
||||
content = (app.outdir / 'index.html').read_text()
|
||||
|
||||
pygments_version = tuple(LooseVersion(pygments.__version__).version)
|
||||
if pygments_version > (2, 7):
|
||||
if PYGMENTS_VERSION > (2, 7):
|
||||
assert '<span class="linenos">1</span>' in content
|
||||
else:
|
||||
assert '<span class="lineno">1 </span>' in content
|
||||
@@ -1625,3 +1651,11 @@ def test_html_permalink_icon(app):
|
||||
assert ('<h1>The basic Sphinx documentation for testing<a class="headerlink" '
|
||||
'href="#the-basic-sphinx-documentation-for-testing" '
|
||||
'title="Permalink to this headline"><span>[PERMALINK]</span></a></h1>' in content)
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='html_signaturereturn_icon')
|
||||
def test_html_signaturereturn_icon(app):
|
||||
app.build()
|
||||
content = (app.outdir / 'index.html').read_text()
|
||||
|
||||
assert ('<span class="sig-return-icon">→</span>' in content)
|
||||
|
||||
@@ -1599,3 +1599,11 @@ def test_latex_elements_extrapackages(app, status, warning):
|
||||
def test_latex_nested_tables(app, status, warning):
|
||||
app.builder.build_all()
|
||||
assert '' == warning.getvalue()
|
||||
|
||||
|
||||
@pytest.mark.sphinx('latex', testroot='latex-container')
|
||||
def test_latex_container(app, status, warning):
|
||||
app.builder.build_all()
|
||||
result = (app.outdir / 'python.tex').read_text()
|
||||
assert r'\begin{sphinxuseclass}{classname}' in result
|
||||
assert r'\end{sphinxuseclass}' in result
|
||||
|
||||
@@ -23,6 +23,7 @@ import pytest
|
||||
import requests
|
||||
|
||||
from sphinx.builders.linkcheck import HyperlinkAvailabilityCheckWorker, RateLimit
|
||||
from sphinx.testing.util import strip_escseq
|
||||
from sphinx.util.console import strip_colors
|
||||
|
||||
from .utils import CERT_FILE, http_server, https_server
|
||||
@@ -65,8 +66,8 @@ def test_defaults_json(app):
|
||||
"info"]:
|
||||
assert attr in row
|
||||
|
||||
assert len(content.splitlines()) == 12
|
||||
assert len(rows) == 12
|
||||
assert len(content.splitlines()) == 11
|
||||
assert len(rows) == 11
|
||||
# the output order of the rows is not stable
|
||||
# due to possible variance in network latency
|
||||
rowsby = {row["uri"]: row for row in rows}
|
||||
@@ -87,7 +88,7 @@ def test_defaults_json(app):
|
||||
assert dnerow['uri'] == 'https://localhost:7777/doesnotexist'
|
||||
assert rowsby['https://www.google.com/image2.png'] == {
|
||||
'filename': 'links.txt',
|
||||
'lineno': 20,
|
||||
'lineno': 19,
|
||||
'status': 'broken',
|
||||
'code': 0,
|
||||
'uri': 'https://www.google.com/image2.png',
|
||||
@@ -101,10 +102,6 @@ def test_defaults_json(app):
|
||||
# images should fail
|
||||
assert "Not Found for url: https://www.google.com/image.png" in \
|
||||
rowsby["https://www.google.com/image.png"]["info"]
|
||||
# The anchor of the URI for github.com is automatically modified
|
||||
assert 'https://github.com/sphinx-doc/sphinx#documentation' not in rowsby
|
||||
assert 'https://github.com/sphinx-doc/sphinx#user-content-documentation' in rowsby
|
||||
assert 'https://github.com/sphinx-doc/sphinx#user-content-testing' in rowsby
|
||||
|
||||
|
||||
@pytest.mark.sphinx(
|
||||
@@ -254,7 +251,7 @@ def make_redirect_handler(*, support_head):
|
||||
|
||||
|
||||
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver', freshenv=True)
|
||||
def test_follows_redirects_on_HEAD(app, capsys):
|
||||
def test_follows_redirects_on_HEAD(app, capsys, warning):
|
||||
with http_server(make_redirect_handler(support_head=True)):
|
||||
app.build()
|
||||
stdout, stderr = capsys.readouterr()
|
||||
@@ -269,10 +266,11 @@ def test_follows_redirects_on_HEAD(app, capsys):
|
||||
127.0.0.1 - - [] "HEAD /?redirected=1 HTTP/1.1" 204 -
|
||||
"""
|
||||
)
|
||||
assert warning.getvalue() == ''
|
||||
|
||||
|
||||
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver', freshenv=True)
|
||||
def test_follows_redirects_on_GET(app, capsys):
|
||||
def test_follows_redirects_on_GET(app, capsys, warning):
|
||||
with http_server(make_redirect_handler(support_head=False)):
|
||||
app.build()
|
||||
stdout, stderr = capsys.readouterr()
|
||||
@@ -288,6 +286,28 @@ def test_follows_redirects_on_GET(app, capsys):
|
||||
127.0.0.1 - - [] "GET /?redirected=1 HTTP/1.1" 204 -
|
||||
"""
|
||||
)
|
||||
assert warning.getvalue() == ''
|
||||
|
||||
|
||||
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver-warn-redirects',
|
||||
freshenv=True, confoverrides={
|
||||
'linkcheck_allowed_redirects': {'http://localhost:7777/.*1': '.*'}
|
||||
})
|
||||
def test_linkcheck_allowed_redirects(app, warning):
|
||||
with http_server(make_redirect_handler(support_head=False)):
|
||||
app.build()
|
||||
|
||||
with open(app.outdir / 'output.json') as fp:
|
||||
records = [json.loads(l) for l in fp.readlines()]
|
||||
|
||||
assert len(records) == 2
|
||||
result = {r["uri"]: r["status"] for r in records}
|
||||
assert result["http://localhost:7777/path1"] == "working"
|
||||
assert result["http://localhost:7777/path2"] == "redirected"
|
||||
|
||||
assert ("index.rst:1: WARNING: redirect http://localhost:7777/path2 - with Found to "
|
||||
"http://localhost:7777/?redirected=1\n" in strip_escseq(warning.getvalue()))
|
||||
assert len(warning.getvalue().splitlines()) == 1
|
||||
|
||||
|
||||
class OKHandler(http.server.BaseHTTPRequestHandler):
|
||||
@@ -605,3 +625,30 @@ def test_get_after_head_raises_connection_error(app):
|
||||
"uri": "http://localhost:7777/",
|
||||
"info": "",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-documents_exclude', freshenv=True)
|
||||
def test_linkcheck_exclude_documents(app):
|
||||
app.build()
|
||||
|
||||
with open(app.outdir / 'output.json') as fp:
|
||||
content = [json.loads(record) for record in fp]
|
||||
|
||||
assert content == [
|
||||
{
|
||||
'filename': 'broken_link.rst',
|
||||
'lineno': 4,
|
||||
'status': 'ignored',
|
||||
'code': 0,
|
||||
'uri': 'https://www.sphinx-doc.org/this-is-a-broken-link',
|
||||
'info': 'broken_link matched ^broken_link$ from linkcheck_exclude_documents',
|
||||
},
|
||||
{
|
||||
'filename': 'br0ken_link.rst',
|
||||
'lineno': 4,
|
||||
'status': 'ignored',
|
||||
'code': 0,
|
||||
'uri': 'https://www.sphinx-doc.org/this-is-another-broken-link',
|
||||
'info': 'br0ken_link matched br[0-9]ken_link from linkcheck_exclude_documents',
|
||||
},
|
||||
]
|
||||
|
||||
@@ -23,6 +23,9 @@ def test_all(app, status, warning):
|
||||
assert r'\fBprint \fP\fIi\fP\fB\en\fP' in content
|
||||
assert r'\fBmanpage\en\fP' in content
|
||||
|
||||
# heading (title + description)
|
||||
assert r'sphinxtests \- Sphinx <Tests> 0.6alpha1' in content
|
||||
|
||||
# term of definition list including nodes.strong
|
||||
assert '\n.B term1\n' in content
|
||||
assert '\nterm2 (\\fBstronged partially\\fP)\n' in content
|
||||
@@ -35,6 +38,15 @@ def test_all(app, status, warning):
|
||||
assert 'Footnotes' not in content
|
||||
|
||||
|
||||
@pytest.mark.sphinx('man', testroot='basic',
|
||||
confoverrides={'man_pages': [('index', 'title', None, [], 1)]})
|
||||
def test_man_pages_empty_description(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
content = (app.outdir / 'title.1').read_text()
|
||||
assert r'title \-' not in content
|
||||
|
||||
|
||||
@pytest.mark.sphinx('man', testroot='basic',
|
||||
confoverrides={'man_make_section_directory': True})
|
||||
def test_man_make_section_directory(app, status, warning):
|
||||
|
||||
@@ -120,3 +120,28 @@ def test_texinfo_footnote(app, status, warning):
|
||||
|
||||
output = (app.outdir / 'python.texi').read_text()
|
||||
assert 'First footnote: @footnote{First}' in output
|
||||
|
||||
|
||||
@pytest.mark.sphinx('texinfo')
|
||||
def test_texinfo_xrefs(app, status, warning):
|
||||
app.builder.build_all()
|
||||
output = (app.outdir / 'sphinxtests.texi').read_text()
|
||||
assert re.search(r'@ref{\w+,,--plugin\.option}', output)
|
||||
|
||||
# Now rebuild it without xrefs
|
||||
app.config.texinfo_cross_references = False
|
||||
app.builder.build_all()
|
||||
output = (app.outdir / 'sphinxtests.texi').read_text()
|
||||
assert not re.search(r'@ref{\w+,,--plugin\.option}', output)
|
||||
assert 'Link to perl +p, --ObjC++, --plugin.option, create-auth-token, arg and -j' in output
|
||||
|
||||
|
||||
@pytest.mark.sphinx('texinfo', testroot='root')
|
||||
def test_texinfo_samp_with_variable(app, status, warning):
|
||||
app.build()
|
||||
|
||||
output = (app.outdir / 'sphinxtests.texi').read_text()
|
||||
|
||||
assert '@code{@var{variable_only}}' in output
|
||||
assert '@code{@var{variable} and text}' in output
|
||||
assert '@code{Show @var{variable} in the middle}' in output
|
||||
|
||||
@@ -15,16 +15,20 @@ import pytest
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.addnodes import desc
|
||||
from sphinx.domains.c import DefinitionError, DefinitionParser, Symbol, _id_prefix, _max_id
|
||||
from sphinx.domains.c import (DefinitionError, DefinitionParser, Symbol, _id_prefix,
|
||||
_macroKeywords, _max_id)
|
||||
from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping
|
||||
from sphinx.testing import restructuredtext
|
||||
from sphinx.testing.util import assert_node
|
||||
|
||||
|
||||
class Config:
|
||||
c_id_attributes = ["id_attr", 'LIGHTGBM_C_EXPORT']
|
||||
c_paren_attributes = ["paren_attr"]
|
||||
c_extra_keywords = _macroKeywords
|
||||
|
||||
|
||||
def parse(name, string):
|
||||
class Config:
|
||||
c_id_attributes = ["id_attr", 'LIGHTGBM_C_EXPORT']
|
||||
c_paren_attributes = ["paren_attr"]
|
||||
parser = DefinitionParser(string, location=None, config=Config())
|
||||
parser.allowFallbackExpressionParsing = False
|
||||
ast = parser.parse_declaration(name, name)
|
||||
@@ -114,9 +118,6 @@ def check(name, input, idDict, output=None, key=None, asTextOutput=None):
|
||||
|
||||
def test_domain_c_ast_expressions():
|
||||
def exprCheck(expr, output=None):
|
||||
class Config:
|
||||
c_id_attributes = ["id_attr"]
|
||||
c_paren_attributes = ["paren_attr"]
|
||||
parser = DefinitionParser(expr, location=None, config=Config())
|
||||
parser.allowFallbackExpressionParsing = False
|
||||
ast = parser.parse_expression()
|
||||
@@ -274,6 +275,62 @@ def test_domain_c_ast_expressions():
|
||||
exprCheck('a or_eq 5')
|
||||
|
||||
|
||||
def test_domain_c_ast_fundamental_types():
|
||||
def types():
|
||||
def signed(t):
|
||||
yield t
|
||||
yield 'signed ' + t
|
||||
yield 'unsigned ' + t
|
||||
|
||||
# integer types
|
||||
# -------------
|
||||
yield 'void'
|
||||
yield from ('_Bool', 'bool')
|
||||
yield from signed('char')
|
||||
yield from signed('short')
|
||||
yield from signed('short int')
|
||||
yield from signed('int')
|
||||
yield from ('signed', 'unsigned')
|
||||
yield from signed('long')
|
||||
yield from signed('long int')
|
||||
yield from signed('long long')
|
||||
yield from signed('long long int')
|
||||
yield from ('__int128', '__uint128')
|
||||
# extensions
|
||||
for t in ('__int8', '__int16', '__int32', '__int64', '__int128'):
|
||||
yield from signed(t)
|
||||
|
||||
# floating point types
|
||||
# --------------------
|
||||
yield from ('_Decimal32', '_Decimal64', '_Decimal128')
|
||||
for f in ('float', 'double', 'long double'):
|
||||
yield f
|
||||
yield from (f + " _Complex", f + " complex")
|
||||
yield from ("_Complex " + f, "complex " + f)
|
||||
yield from ("_Imaginary " + f, "imaginary " + f)
|
||||
# extensions
|
||||
# https://gcc.gnu.org/onlinedocs/gcc/Floating-Types.html#Floating-Types
|
||||
yield from ('__float80', '_Float64x',
|
||||
'__float128', '_Float128',
|
||||
'__ibm128')
|
||||
# https://gcc.gnu.org/onlinedocs/gcc/Half-Precision.html#Half-Precision
|
||||
yield '__fp16'
|
||||
|
||||
# fixed-point types (extension)
|
||||
# -----------------------------
|
||||
# https://gcc.gnu.org/onlinedocs/gcc/Fixed-Point.html#Fixed-Point
|
||||
for sat in ('', '_Sat '):
|
||||
for t in ('_Fract', 'fract', '_Accum', 'accum'):
|
||||
for size in ('short ', '', 'long ', 'long long '):
|
||||
for tt in signed(size + t):
|
||||
yield sat + tt
|
||||
|
||||
for t in types():
|
||||
input = "{key}%s foo" % t
|
||||
output = ' '.join(input.split())
|
||||
check('type', input, {1: 'foo'}, key='typedef', output=output)
|
||||
|
||||
|
||||
def test_domain_c_ast_type_definitions():
|
||||
check('type', "{key}T", {1: "T"})
|
||||
|
||||
@@ -527,6 +584,16 @@ def test_domain_c_ast_attributes():
|
||||
check('function', 'LIGHTGBM_C_EXPORT int LGBM_BoosterFree(int handle)',
|
||||
{1: 'LGBM_BoosterFree'})
|
||||
|
||||
|
||||
def test_extra_keywords():
|
||||
with pytest.raises(DefinitionError,
|
||||
match='Expected identifier, got user-defined keyword: complex.'):
|
||||
parse('function', 'void f(int complex)')
|
||||
with pytest.raises(DefinitionError,
|
||||
match='Expected identifier, got user-defined keyword: complex.'):
|
||||
parse('function', 'void complex(void)')
|
||||
|
||||
|
||||
# def test_print():
|
||||
# # used for getting all the ids out for checking
|
||||
# for a in ids:
|
||||
|
||||
@@ -123,7 +123,9 @@ def test_domain_cpp_ast_fundamental_types():
|
||||
def makeIdV1():
|
||||
if t == 'decltype(auto)':
|
||||
return None
|
||||
id = t.replace(" ", "-").replace("long", "l").replace("int", "i")
|
||||
id = t.replace(" ", "-").replace("long", "l")
|
||||
if "__int" not in t:
|
||||
id = id.replace("int", "i")
|
||||
id = id.replace("bool", "b").replace("char", "c")
|
||||
id = id.replace("wc_t", "wchar_t").replace("c16_t", "char16_t")
|
||||
id = id.replace("c8_t", "char8_t")
|
||||
@@ -135,7 +137,9 @@ def test_domain_cpp_ast_fundamental_types():
|
||||
if t == "std::nullptr_t":
|
||||
id = "NSt9nullptr_tE"
|
||||
return "1f%s" % id
|
||||
check("function", "void f(%s arg)" % t, {1: makeIdV1(), 2: makeIdV2()})
|
||||
input = "void f(%s arg)" % t.replace(' ', ' ')
|
||||
output = "void f(%s arg)" % t
|
||||
check("function", input, {1: makeIdV1(), 2: makeIdV2()}, output=output)
|
||||
|
||||
|
||||
def test_domain_cpp_ast_expressions():
|
||||
@@ -635,6 +639,9 @@ def test_domain_cpp_ast_function_definitions():
|
||||
# from #8960
|
||||
check('function', 'void f(void (*p)(int, double), int i)', {2: '1fPFvidEi'})
|
||||
|
||||
# from #9535 comment
|
||||
check('function', 'void f(void (*p)(int) = &foo)', {2: '1fPFviE'})
|
||||
|
||||
|
||||
def test_domain_cpp_ast_operators():
|
||||
check('function', 'void operator new()', {1: "new-operator", 2: "nwv"})
|
||||
|
||||
@@ -15,7 +15,8 @@ from docutils import nodes
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.addnodes import (desc, desc_annotation, desc_content, desc_name, desc_parameter,
|
||||
desc_parameterlist, desc_signature)
|
||||
desc_parameterlist, desc_sig_keyword, desc_sig_name,
|
||||
desc_sig_space, desc_signature)
|
||||
from sphinx.domains.javascript import JavaScriptDomain
|
||||
from sphinx.testing import restructuredtext
|
||||
from sphinx.testing.util import assert_node
|
||||
@@ -184,11 +185,11 @@ def test_js_function(app):
|
||||
text = ".. js:function:: sum(a, b)"
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_name, "sum"],
|
||||
[desc, ([desc_signature, ([desc_name, ([desc_sig_name, "sum"])],
|
||||
desc_parameterlist)],
|
||||
[desc_content, ()])]))
|
||||
assert_node(doctree[1][0][1], [desc_parameterlist, ([desc_parameter, "a"],
|
||||
[desc_parameter, "b"])])
|
||||
assert_node(doctree[1][0][1], [desc_parameterlist, ([desc_parameter, ([desc_sig_name, "a"])],
|
||||
[desc_parameter, ([desc_sig_name, "b"])])])
|
||||
assert_node(doctree[0], addnodes.index,
|
||||
entries=[("single", "sum() (built-in function)", "sum", "", None)])
|
||||
assert_node(doctree[1], addnodes.desc, domain="js", objtype="function", noindex=False)
|
||||
@@ -198,8 +199,9 @@ def test_js_class(app):
|
||||
text = ".. js:class:: Application"
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_annotation, "class "],
|
||||
[desc_name, "Application"],
|
||||
[desc, ([desc_signature, ([desc_annotation, ([desc_sig_keyword, 'class'],
|
||||
desc_sig_space)],
|
||||
[desc_name, ([desc_sig_name, "Application"])],
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()])]))
|
||||
assert_node(doctree[0], addnodes.index,
|
||||
@@ -211,7 +213,7 @@ def test_js_data(app):
|
||||
text = ".. js:data:: name"
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, desc_name, "name"],
|
||||
[desc, ([desc_signature, ([desc_name, ([desc_sig_name, "name"])])],
|
||||
[desc_content, ()])]))
|
||||
assert_node(doctree[0], addnodes.index,
|
||||
entries=[("single", "name (global variable or constant)", "name", "", None)])
|
||||
|
||||
@@ -18,8 +18,10 @@ from docutils import nodes
|
||||
from sphinx import addnodes
|
||||
from sphinx.addnodes import (desc, desc_addname, desc_annotation, desc_content, desc_name,
|
||||
desc_optional, desc_parameter, desc_parameterlist, desc_returns,
|
||||
desc_sig_name, desc_sig_operator, desc_sig_punctuation,
|
||||
desc_signature, pending_xref)
|
||||
desc_sig_keyword, desc_sig_literal_number,
|
||||
desc_sig_literal_string, desc_sig_name, desc_sig_operator,
|
||||
desc_sig_punctuation, desc_sig_space, desc_signature,
|
||||
pending_xref)
|
||||
from sphinx.domains import IndexEntry
|
||||
from sphinx.domains.python import (PythonDomain, PythonModuleIndex, _parse_annotation,
|
||||
_pseudo_parse_arglist, py_sig_re)
|
||||
@@ -290,7 +292,8 @@ def test_parse_annotation(app):
|
||||
assert_node(doctree, ([pending_xref, "Tuple"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[pending_xref, "int"],
|
||||
[desc_sig_punctuation, ", "],
|
||||
[desc_sig_punctuation, ","],
|
||||
desc_sig_space,
|
||||
[pending_xref, "int"],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
@@ -305,19 +308,33 @@ def test_parse_annotation(app):
|
||||
assert_node(doctree, ([pending_xref, "Tuple"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[pending_xref, "int"],
|
||||
[desc_sig_punctuation, ", "],
|
||||
[desc_sig_punctuation, ","],
|
||||
desc_sig_space,
|
||||
[desc_sig_punctuation, "..."],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
doctree = _parse_annotation("Callable[[int, int], int]", app.env)
|
||||
print(doctree)
|
||||
assert_node(doctree, ([pending_xref, "Callable"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[desc_sig_punctuation, "["],
|
||||
[pending_xref, "int"],
|
||||
[desc_sig_punctuation, ", "],
|
||||
[desc_sig_punctuation, ","],
|
||||
desc_sig_space,
|
||||
[pending_xref, "int"],
|
||||
[desc_sig_punctuation, "]"],
|
||||
[desc_sig_punctuation, ", "],
|
||||
[desc_sig_punctuation, ","],
|
||||
desc_sig_space,
|
||||
[pending_xref, "int"],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
doctree = _parse_annotation("Callable[[], int]", app.env)
|
||||
assert_node(doctree, ([pending_xref, "Callable"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[desc_sig_punctuation, "["],
|
||||
[desc_sig_punctuation, "]"],
|
||||
[desc_sig_punctuation, ","],
|
||||
desc_sig_space,
|
||||
[pending_xref, "int"],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
@@ -333,6 +350,42 @@ def test_parse_annotation(app):
|
||||
assert_node(doctree[0], pending_xref, refdomain="py", reftype="obj", reftarget="None")
|
||||
|
||||
|
||||
def test_parse_annotation_suppress(app):
|
||||
doctree = _parse_annotation("~typing.Dict[str, str]", app.env)
|
||||
assert_node(doctree, ([pending_xref, "Dict"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[pending_xref, "str"],
|
||||
[desc_sig_punctuation, ","],
|
||||
desc_sig_space,
|
||||
[pending_xref, "str"],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
assert_node(doctree[0], pending_xref, refdomain="py", reftype="class", reftarget="typing.Dict")
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
|
||||
def test_parse_annotation_Literal(app):
|
||||
doctree = _parse_annotation("Literal[True, False]", app.env)
|
||||
assert_node(doctree, ([pending_xref, "Literal"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[desc_sig_keyword, "True"],
|
||||
[desc_sig_punctuation, ","],
|
||||
desc_sig_space,
|
||||
[desc_sig_keyword, "False"],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
doctree = _parse_annotation("typing.Literal[0, 1, 'abc']", app.env)
|
||||
assert_node(doctree, ([pending_xref, "typing.Literal"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[desc_sig_literal_number, "0"],
|
||||
[desc_sig_punctuation, ","],
|
||||
desc_sig_space,
|
||||
[desc_sig_literal_number, "1"],
|
||||
[desc_sig_punctuation, ","],
|
||||
desc_sig_space,
|
||||
[desc_sig_literal_string, "'abc'"],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
|
||||
def test_pyfunction_signature(app):
|
||||
text = ".. py:function:: hello(name: str) -> str"
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
@@ -346,7 +399,7 @@ def test_pyfunction_signature(app):
|
||||
assert_node(doctree[1][0][1],
|
||||
[desc_parameterlist, desc_parameter, ([desc_sig_name, "name"],
|
||||
[desc_sig_punctuation, ":"],
|
||||
" ",
|
||||
desc_sig_space,
|
||||
[nodes.inline, pending_xref, "str"])])
|
||||
|
||||
|
||||
@@ -364,7 +417,7 @@ def test_pyfunction_signature_full(app):
|
||||
assert_node(doctree[1][0][1],
|
||||
[desc_parameterlist, ([desc_parameter, ([desc_sig_name, "a"],
|
||||
[desc_sig_punctuation, ":"],
|
||||
" ",
|
||||
desc_sig_space,
|
||||
[desc_sig_name, pending_xref, "str"])],
|
||||
[desc_parameter, ([desc_sig_name, "b"],
|
||||
[desc_sig_operator, "="],
|
||||
@@ -372,28 +425,28 @@ def test_pyfunction_signature_full(app):
|
||||
[desc_parameter, ([desc_sig_operator, "*"],
|
||||
[desc_sig_name, "args"],
|
||||
[desc_sig_punctuation, ":"],
|
||||
" ",
|
||||
desc_sig_space,
|
||||
[desc_sig_name, pending_xref, "str"])],
|
||||
[desc_parameter, ([desc_sig_name, "c"],
|
||||
[desc_sig_punctuation, ":"],
|
||||
" ",
|
||||
desc_sig_space,
|
||||
[desc_sig_name, pending_xref, "bool"],
|
||||
" ",
|
||||
desc_sig_space,
|
||||
[desc_sig_operator, "="],
|
||||
" ",
|
||||
desc_sig_space,
|
||||
[nodes.inline, "True"])],
|
||||
[desc_parameter, ([desc_sig_name, "d"],
|
||||
[desc_sig_punctuation, ":"],
|
||||
" ",
|
||||
desc_sig_space,
|
||||
[desc_sig_name, pending_xref, "tuple"],
|
||||
" ",
|
||||
desc_sig_space,
|
||||
[desc_sig_operator, "="],
|
||||
" ",
|
||||
desc_sig_space,
|
||||
[nodes.inline, "(1, 2)"])],
|
||||
[desc_parameter, ([desc_sig_operator, "**"],
|
||||
[desc_sig_name, "kwargs"],
|
||||
[desc_sig_punctuation, ":"],
|
||||
" ",
|
||||
desc_sig_space,
|
||||
[desc_sig_name, pending_xref, "str"])])])
|
||||
|
||||
|
||||
@@ -452,11 +505,11 @@ def test_pyfunction_with_union_type_operator(app):
|
||||
assert_node(doctree[1][0][1],
|
||||
[desc_parameterlist, ([desc_parameter, ([desc_sig_name, "age"],
|
||||
[desc_sig_punctuation, ":"],
|
||||
" ",
|
||||
desc_sig_space,
|
||||
[desc_sig_name, ([pending_xref, "int"],
|
||||
" ",
|
||||
desc_sig_space,
|
||||
[desc_sig_punctuation, "|"],
|
||||
" ",
|
||||
desc_sig_space,
|
||||
[pending_xref, "None"])])])])
|
||||
|
||||
|
||||
@@ -471,16 +524,16 @@ def test_optional_pyfunction_signature(app):
|
||||
assert_node(doctree[1], addnodes.desc, desctype="function",
|
||||
domain="py", objtype="function", noindex=False)
|
||||
assert_node(doctree[1][0][1],
|
||||
([desc_parameter, "source"],
|
||||
[desc_optional, ([desc_parameter, "filename"],
|
||||
[desc_optional, desc_parameter, "symbol"])]))
|
||||
([desc_parameter, ([desc_sig_name, "source"])],
|
||||
[desc_optional, ([desc_parameter, ([desc_sig_name, "filename"])],
|
||||
[desc_optional, desc_parameter, ([desc_sig_name, "symbol"])])]))
|
||||
|
||||
|
||||
def test_pyexception_signature(app):
|
||||
text = ".. py:exception:: builtins.IOError"
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_annotation, "exception "],
|
||||
[desc, ([desc_signature, ([desc_annotation, ('exception', desc_sig_space)],
|
||||
[desc_addname, "builtins."],
|
||||
[desc_name, "IOError"])],
|
||||
desc_content)]))
|
||||
@@ -495,9 +548,15 @@ def test_pydata_signature(app):
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_name, "version"],
|
||||
[desc_annotation, (": ",
|
||||
[desc_annotation, ([desc_sig_punctuation, ':'],
|
||||
desc_sig_space,
|
||||
[pending_xref, "int"])],
|
||||
[desc_annotation, " = 1"])],
|
||||
[desc_annotation, (
|
||||
desc_sig_space,
|
||||
[desc_sig_punctuation, '='],
|
||||
desc_sig_space,
|
||||
"1")]
|
||||
)],
|
||||
desc_content)]))
|
||||
assert_node(doctree[1], addnodes.desc, desctype="data",
|
||||
domain="py", objtype="data", noindex=False)
|
||||
@@ -509,7 +568,8 @@ def test_pydata_signature_old(app):
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_name, "version"],
|
||||
[desc_annotation, " = 1"])],
|
||||
[desc_annotation, (desc_sig_space,
|
||||
"= 1")])],
|
||||
desc_content)]))
|
||||
assert_node(doctree[1], addnodes.desc, desctype="data",
|
||||
domain="py", objtype="data", noindex=False)
|
||||
@@ -521,11 +581,12 @@ def test_pydata_with_union_type_operator(app):
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree[1][0],
|
||||
([desc_name, "version"],
|
||||
[desc_annotation, (": ",
|
||||
[desc_annotation, ([desc_sig_punctuation, ':'],
|
||||
desc_sig_space,
|
||||
[pending_xref, "int"],
|
||||
" ",
|
||||
desc_sig_space,
|
||||
[desc_sig_punctuation, "|"],
|
||||
" ",
|
||||
desc_sig_space,
|
||||
[pending_xref, "str"])]))
|
||||
|
||||
|
||||
@@ -536,7 +597,7 @@ def test_pyobject_prefix(app):
|
||||
" .. py:method:: FooBar.say")
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_annotation, "class "],
|
||||
[desc, ([desc_signature, ([desc_annotation, ('class', desc_sig_space)],
|
||||
[desc_name, "Foo"])],
|
||||
[desc_content, (addnodes.index,
|
||||
desc,
|
||||
@@ -557,10 +618,11 @@ def test_pydata(app):
|
||||
addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_addname, "example."],
|
||||
[desc_name, "var"],
|
||||
[desc_annotation, (": ",
|
||||
[desc_annotation, ([desc_sig_punctuation, ':'],
|
||||
desc_sig_space,
|
||||
[pending_xref, "int"])])],
|
||||
[desc_content, ()])]))
|
||||
assert_node(doctree[3][0][2][1], pending_xref, **{"py:module": "example"})
|
||||
assert_node(doctree[3][0][2][2], pending_xref, **{"py:module": "example"})
|
||||
assert 'example.var' in domain.objects
|
||||
assert domain.objects['example.var'] == ('index', 'example.var', 'data', False)
|
||||
|
||||
@@ -579,7 +641,8 @@ def test_pyfunction(app):
|
||||
nodes.target,
|
||||
addnodes.index,
|
||||
addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_annotation, "async "],
|
||||
[desc, ([desc_signature, ([desc_annotation, ([desc_sig_keyword, 'async'],
|
||||
desc_sig_space)],
|
||||
[desc_addname, "example."],
|
||||
[desc_name, "func2"],
|
||||
[desc_parameterlist, ()])],
|
||||
@@ -604,11 +667,14 @@ def test_pyclass_options(app):
|
||||
domain = app.env.get_domain('py')
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_annotation, "class "],
|
||||
[desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)],
|
||||
[desc_name, "Class1"])],
|
||||
[desc_content, ()])],
|
||||
addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_annotation, "final class "],
|
||||
[desc, ([desc_signature, ([desc_annotation, ("final",
|
||||
desc_sig_space,
|
||||
"class",
|
||||
desc_sig_space)],
|
||||
[desc_name, "Class2"])],
|
||||
[desc_content, ()])]))
|
||||
|
||||
@@ -644,7 +710,7 @@ def test_pymethod_options(app):
|
||||
domain = app.env.get_domain('py')
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_annotation, "class "],
|
||||
[desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)],
|
||||
[desc_name, "Class"])],
|
||||
[desc_content, (addnodes.index,
|
||||
desc,
|
||||
@@ -673,7 +739,7 @@ def test_pymethod_options(app):
|
||||
# :classmethod:
|
||||
assert_node(doctree[1][1][2], addnodes.index,
|
||||
entries=[('single', 'meth2() (Class class method)', 'Class.meth2', '', None)])
|
||||
assert_node(doctree[1][1][3], ([desc_signature, ([desc_annotation, "classmethod "],
|
||||
assert_node(doctree[1][1][3], ([desc_signature, ([desc_annotation, ("classmethod", desc_sig_space)],
|
||||
[desc_name, "meth2"],
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
@@ -683,7 +749,7 @@ def test_pymethod_options(app):
|
||||
# :staticmethod:
|
||||
assert_node(doctree[1][1][4], addnodes.index,
|
||||
entries=[('single', 'meth3() (Class static method)', 'Class.meth3', '', None)])
|
||||
assert_node(doctree[1][1][5], ([desc_signature, ([desc_annotation, "static "],
|
||||
assert_node(doctree[1][1][5], ([desc_signature, ([desc_annotation, ("static", desc_sig_space)],
|
||||
[desc_name, "meth3"],
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
@@ -693,7 +759,7 @@ def test_pymethod_options(app):
|
||||
# :async:
|
||||
assert_node(doctree[1][1][6], addnodes.index,
|
||||
entries=[('single', 'meth4() (Class method)', 'Class.meth4', '', None)])
|
||||
assert_node(doctree[1][1][7], ([desc_signature, ([desc_annotation, "async "],
|
||||
assert_node(doctree[1][1][7], ([desc_signature, ([desc_annotation, ("async", desc_sig_space)],
|
||||
[desc_name, "meth4"],
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
@@ -702,8 +768,8 @@ def test_pymethod_options(app):
|
||||
|
||||
# :property:
|
||||
assert_node(doctree[1][1][8], addnodes.index,
|
||||
entries=[('single', 'meth5() (Class property)', 'Class.meth5', '', None)])
|
||||
assert_node(doctree[1][1][9], ([desc_signature, ([desc_annotation, "property "],
|
||||
entries=[('single', 'meth5 (Class property)', 'Class.meth5', '', None)])
|
||||
assert_node(doctree[1][1][9], ([desc_signature, ([desc_annotation, ("property", desc_sig_space)],
|
||||
[desc_name, "meth5"])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth5' in domain.objects
|
||||
@@ -712,7 +778,7 @@ def test_pymethod_options(app):
|
||||
# :abstractmethod:
|
||||
assert_node(doctree[1][1][10], addnodes.index,
|
||||
entries=[('single', 'meth6() (Class method)', 'Class.meth6', '', None)])
|
||||
assert_node(doctree[1][1][11], ([desc_signature, ([desc_annotation, "abstract "],
|
||||
assert_node(doctree[1][1][11], ([desc_signature, ([desc_annotation, ("abstract", desc_sig_space)],
|
||||
[desc_name, "meth6"],
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
@@ -722,7 +788,7 @@ def test_pymethod_options(app):
|
||||
# :final:
|
||||
assert_node(doctree[1][1][12], addnodes.index,
|
||||
entries=[('single', 'meth7() (Class method)', 'Class.meth7', '', None)])
|
||||
assert_node(doctree[1][1][13], ([desc_signature, ([desc_annotation, "final "],
|
||||
assert_node(doctree[1][1][13], ([desc_signature, ([desc_annotation, ("final", desc_sig_space)],
|
||||
[desc_name, "meth7"],
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
@@ -737,13 +803,13 @@ def test_pyclassmethod(app):
|
||||
domain = app.env.get_domain('py')
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_annotation, "class "],
|
||||
[desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)],
|
||||
[desc_name, "Class"])],
|
||||
[desc_content, (addnodes.index,
|
||||
desc)])]))
|
||||
assert_node(doctree[1][1][0], addnodes.index,
|
||||
entries=[('single', 'meth() (Class class method)', 'Class.meth', '', None)])
|
||||
assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, "classmethod "],
|
||||
assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, ("classmethod", desc_sig_space)],
|
||||
[desc_name, "meth"],
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
@@ -758,13 +824,13 @@ def test_pystaticmethod(app):
|
||||
domain = app.env.get_domain('py')
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_annotation, "class "],
|
||||
[desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)],
|
||||
[desc_name, "Class"])],
|
||||
[desc_content, (addnodes.index,
|
||||
desc)])]))
|
||||
assert_node(doctree[1][1][0], addnodes.index,
|
||||
entries=[('single', 'meth() (Class static method)', 'Class.meth', '', None)])
|
||||
assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, "static "],
|
||||
assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, ("static", desc_sig_space)],
|
||||
[desc_name, "meth"],
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
@@ -781,22 +847,27 @@ def test_pyattribute(app):
|
||||
domain = app.env.get_domain('py')
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_annotation, "class "],
|
||||
[desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)],
|
||||
[desc_name, "Class"])],
|
||||
[desc_content, (addnodes.index,
|
||||
desc)])]))
|
||||
assert_node(doctree[1][1][0], addnodes.index,
|
||||
entries=[('single', 'attr (Class attribute)', 'Class.attr', '', None)])
|
||||
assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "attr"],
|
||||
[desc_annotation, (": ",
|
||||
[desc_annotation, ([desc_sig_punctuation, ':'],
|
||||
desc_sig_space,
|
||||
[pending_xref, "Optional"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[pending_xref, "str"],
|
||||
[desc_sig_punctuation, "]"])],
|
||||
[desc_annotation, " = ''"])],
|
||||
[desc_annotation, (desc_sig_space,
|
||||
[desc_sig_punctuation, '='],
|
||||
desc_sig_space,
|
||||
"''")]
|
||||
)],
|
||||
[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_node(doctree[1][1][1][0][1][2], pending_xref, **{"py:class": "Class"})
|
||||
assert_node(doctree[1][1][1][0][1][4], pending_xref, **{"py:class": "Class"})
|
||||
assert 'Class.attr' in domain.objects
|
||||
assert domain.objects['Class.attr'] == ('index', 'Class.attr', 'attribute', False)
|
||||
|
||||
@@ -804,24 +875,44 @@ def test_pyattribute(app):
|
||||
def test_pyproperty(app):
|
||||
text = (".. py:class:: Class\n"
|
||||
"\n"
|
||||
" .. py:property:: prop\n"
|
||||
" .. py:property:: prop1\n"
|
||||
" :abstractmethod:\n"
|
||||
" :type: str\n"
|
||||
"\n"
|
||||
" .. py:property:: prop2\n"
|
||||
" :classmethod:\n"
|
||||
" :type: str\n")
|
||||
domain = app.env.get_domain('py')
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_annotation, "class "],
|
||||
[desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)],
|
||||
[desc_name, "Class"])],
|
||||
[desc_content, (addnodes.index,
|
||||
desc,
|
||||
addnodes.index,
|
||||
desc)])]))
|
||||
assert_node(doctree[1][1][0], addnodes.index,
|
||||
entries=[('single', 'prop (Class property)', 'Class.prop', '', None)])
|
||||
assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, "abstract property "],
|
||||
[desc_name, "prop"],
|
||||
[desc_annotation, ": str"])],
|
||||
entries=[('single', 'prop1 (Class property)', 'Class.prop1', '', None)])
|
||||
assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, ("abstract", desc_sig_space,
|
||||
"property", desc_sig_space)],
|
||||
[desc_name, "prop1"],
|
||||
[desc_annotation, ([desc_sig_punctuation, ':'],
|
||||
desc_sig_space,
|
||||
[pending_xref, "str"])])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.prop' in domain.objects
|
||||
assert domain.objects['Class.prop'] == ('index', 'Class.prop', 'property', False)
|
||||
assert_node(doctree[1][1][2], addnodes.index,
|
||||
entries=[('single', 'prop2 (Class property)', 'Class.prop2', '', None)])
|
||||
assert_node(doctree[1][1][3], ([desc_signature, ([desc_annotation, ("class", desc_sig_space,
|
||||
"property", desc_sig_space)],
|
||||
[desc_name, "prop2"],
|
||||
[desc_annotation, ([desc_sig_punctuation, ':'],
|
||||
desc_sig_space,
|
||||
[pending_xref, "str"])])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.prop1' in domain.objects
|
||||
assert domain.objects['Class.prop1'] == ('index', 'Class.prop1', 'property', False)
|
||||
assert 'Class.prop2' in domain.objects
|
||||
assert domain.objects['Class.prop2'] == ('index', 'Class.prop2', 'property', False)
|
||||
|
||||
|
||||
def test_pydecorator_signature(app):
|
||||
@@ -860,7 +951,7 @@ def test_canonical(app):
|
||||
domain = app.env.get_domain('py')
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_annotation, "class "],
|
||||
[desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)],
|
||||
[desc_addname, "io."],
|
||||
[desc_name, "StringIO"])],
|
||||
desc_content)]))
|
||||
@@ -918,7 +1009,7 @@ def test_info_field_list(app):
|
||||
assert_node(doctree, (nodes.target,
|
||||
addnodes.index,
|
||||
addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_annotation, "class "],
|
||||
[desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)],
|
||||
[desc_addname, "example."],
|
||||
[desc_name, "Class"])],
|
||||
[desc_content, nodes.field_list, nodes.field])]))
|
||||
@@ -1009,7 +1100,7 @@ def test_info_field_list_piped_type(app):
|
||||
(nodes.target,
|
||||
addnodes.index,
|
||||
addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_annotation, "class "],
|
||||
[desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)],
|
||||
[desc_addname, "example."],
|
||||
[desc_name, "Class"])],
|
||||
[desc_content, nodes.field_list, nodes.field, (nodes.field_name,
|
||||
@@ -1031,6 +1122,42 @@ def test_info_field_list_piped_type(app):
|
||||
**{"py:module": "example", "py:class": "Class"})
|
||||
|
||||
|
||||
def test_info_field_list_Literal(app):
|
||||
text = (".. py:module:: example\n"
|
||||
".. py:class:: Class\n"
|
||||
"\n"
|
||||
" :param age: blah blah\n"
|
||||
" :type age: Literal['foo', 'bar', 'baz']\n")
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
|
||||
assert_node(doctree,
|
||||
(nodes.target,
|
||||
addnodes.index,
|
||||
addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)],
|
||||
[desc_addname, "example."],
|
||||
[desc_name, "Class"])],
|
||||
[desc_content, nodes.field_list, nodes.field, (nodes.field_name,
|
||||
nodes.field_body)])]))
|
||||
assert_node(doctree[3][1][0][0][1],
|
||||
([nodes.paragraph, ([addnodes.literal_strong, "age"],
|
||||
" (",
|
||||
[pending_xref, addnodes.literal_emphasis, "Literal"],
|
||||
[addnodes.literal_emphasis, "["],
|
||||
[addnodes.literal_emphasis, "'foo'"],
|
||||
[addnodes.literal_emphasis, ", "],
|
||||
[addnodes.literal_emphasis, "'bar'"],
|
||||
[addnodes.literal_emphasis, ", "],
|
||||
[addnodes.literal_emphasis, "'baz'"],
|
||||
[addnodes.literal_emphasis, "]"],
|
||||
")",
|
||||
" -- ",
|
||||
"blah blah")],))
|
||||
assert_node(doctree[3][1][0][0][1][0][2], pending_xref,
|
||||
refdomain="py", reftype="class", reftarget="Literal",
|
||||
**{"py:module": "example", "py:class": "Class"})
|
||||
|
||||
|
||||
def test_info_field_list_var(app):
|
||||
text = (".. py:class:: Class\n"
|
||||
"\n"
|
||||
@@ -1055,6 +1182,28 @@ def test_info_field_list_var(app):
|
||||
refdomain="py", reftype="class", reftarget="int", **{"py:class": "Class"})
|
||||
|
||||
|
||||
def test_type_field(app):
|
||||
text = (".. py:data:: var1\n"
|
||||
" :type: .int\n"
|
||||
".. py:data:: var2\n"
|
||||
" :type: ~builtins.int\n")
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_name, "var1"],
|
||||
[desc_annotation, ([desc_sig_punctuation, ':'],
|
||||
desc_sig_space,
|
||||
[pending_xref, "int"])])],
|
||||
[desc_content, ()])],
|
||||
addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_name, "var2"],
|
||||
[desc_annotation, ([desc_sig_punctuation, ':'],
|
||||
desc_sig_space,
|
||||
[pending_xref, "int"])])],
|
||||
[desc_content, ()])]))
|
||||
assert_node(doctree[1][0][1][2], pending_xref, reftarget='int', refspecific=True)
|
||||
assert_node(doctree[3][0][1][2], pending_xref, reftarget='builtins.int', refspecific=False)
|
||||
|
||||
|
||||
@pytest.mark.sphinx(freshenv=True)
|
||||
def test_module_index(app):
|
||||
text = (".. py:module:: docutils\n"
|
||||
|
||||
@@ -34,7 +34,7 @@ def test_event_allowed_exceptions():
|
||||
events = EventManager(object()) # pass an dummy object as an app
|
||||
events.connect('builder-inited', raise_error, priority=500)
|
||||
|
||||
# all errors are conveted to ExtensionError
|
||||
# all errors are converted to ExtensionError
|
||||
with pytest.raises(ExtensionError):
|
||||
events.emit('builder-inited')
|
||||
|
||||
|
||||
@@ -635,6 +635,8 @@ def test_namespace_package_file(tempdir):
|
||||
assert content == ("testpkg namespace\n"
|
||||
"=================\n"
|
||||
"\n"
|
||||
".. py:module:: testpkg\n"
|
||||
"\n"
|
||||
"Submodules\n"
|
||||
"----------\n"
|
||||
"\n"
|
||||
|
||||
@@ -984,7 +984,7 @@ def test_autodoc_inner_class(app):
|
||||
' .. py:attribute:: Outer.factory',
|
||||
' :module: target',
|
||||
'',
|
||||
' alias of :class:`dict`'
|
||||
' alias of :py:class:`dict`'
|
||||
]
|
||||
|
||||
actual = do_autodoc(app, 'class', 'target.Outer.Inner', options)
|
||||
@@ -1009,7 +1009,7 @@ def test_autodoc_inner_class(app):
|
||||
'',
|
||||
'.. py:class:: InnerChild()',
|
||||
' :module: target', '',
|
||||
' Bases: :class:`target.Outer.Inner`',
|
||||
' Bases: :py:class:`target.Outer.Inner`',
|
||||
'',
|
||||
' InnerChild docstring',
|
||||
'',
|
||||
@@ -1084,6 +1084,7 @@ def test_autodoc_cached_property(app):
|
||||
'',
|
||||
' .. py:property:: Foo.prop',
|
||||
' :module: target.cached_property',
|
||||
' :type: int',
|
||||
'',
|
||||
]
|
||||
|
||||
@@ -1358,6 +1359,7 @@ def test_slots(app):
|
||||
'',
|
||||
' .. py:attribute:: Bar.attr1',
|
||||
' :module: target.slots',
|
||||
' :type: int',
|
||||
'',
|
||||
' docstring of attr1',
|
||||
'',
|
||||
@@ -1399,15 +1401,15 @@ def test_enum_class(app):
|
||||
options = {"members": None}
|
||||
actual = do_autodoc(app, 'class', 'target.enums.EnumCls', options)
|
||||
|
||||
if sys.version_info < (3, 10):
|
||||
sig = '(value)'
|
||||
if sys.version_info > (3, 11):
|
||||
args = ('(value, names=None, *, module=None, qualname=None, '
|
||||
'type=None, start=1, boundary=None)')
|
||||
else:
|
||||
sig = ('(value, names=None, *, module=None, qualname=None, type=None, start=1, '
|
||||
'boundary=None)')
|
||||
args = '(value)'
|
||||
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:class:: EnumCls%s' % sig,
|
||||
'.. py:class:: EnumCls' + args,
|
||||
' :module: target.enums',
|
||||
'',
|
||||
' this is enum class',
|
||||
@@ -1625,59 +1627,6 @@ def test_bound_method(app):
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_coroutine(app):
|
||||
actual = do_autodoc(app, 'function', 'target.functions.coroutinefunc')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:function:: coroutinefunc()',
|
||||
' :module: target.functions',
|
||||
' :async:',
|
||||
'',
|
||||
]
|
||||
|
||||
options = {"members": None}
|
||||
actual = do_autodoc(app, 'class', 'target.coroutine.AsyncClass', options)
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:class:: AsyncClass()',
|
||||
' :module: target.coroutine',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: AsyncClass.do_coroutine()',
|
||||
' :module: target.coroutine',
|
||||
' :async:',
|
||||
'',
|
||||
' A documented coroutine function',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: AsyncClass.do_coroutine2()',
|
||||
' :module: target.coroutine',
|
||||
' :async:',
|
||||
' :classmethod:',
|
||||
'',
|
||||
' A documented coroutine classmethod',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: AsyncClass.do_coroutine3()',
|
||||
' :module: target.coroutine',
|
||||
' :async:',
|
||||
' :staticmethod:',
|
||||
'',
|
||||
' A documented coroutine staticmethod',
|
||||
'',
|
||||
]
|
||||
|
||||
# force-synchronized wrapper
|
||||
actual = do_autodoc(app, 'function', 'target.coroutine.sync_func')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:function:: sync_func()',
|
||||
' :module: target.coroutine',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_partialmethod(app):
|
||||
expected = [
|
||||
@@ -1756,7 +1705,7 @@ def test_autodoc_typed_instance_variables(app):
|
||||
'.. py:attribute:: Alias',
|
||||
' :module: target.typed_vars',
|
||||
'',
|
||||
' alias of :class:`target.typed_vars.Derived`',
|
||||
' alias of :py:class:`target.typed_vars.Derived`',
|
||||
'',
|
||||
'.. py:class:: Class()',
|
||||
' :module: target.typed_vars',
|
||||
@@ -1921,12 +1870,15 @@ def test_autodoc_GenericAlias(app):
|
||||
' .. py:attribute:: Class.T',
|
||||
' :module: target.genericalias',
|
||||
'',
|
||||
' alias of :class:`~typing.List`\\ [:class:`int`]',
|
||||
' A list of int',
|
||||
'',
|
||||
' alias of :py:class:`~typing.List`\\ [:py:class:`int`]',
|
||||
'',
|
||||
'.. py:attribute:: T',
|
||||
' :module: target.genericalias',
|
||||
'',
|
||||
' alias of :class:`~typing.List`\\ [:class:`int`]',
|
||||
' A list of int',
|
||||
'',
|
||||
]
|
||||
else:
|
||||
assert list(actual) == [
|
||||
@@ -1943,7 +1895,7 @@ def test_autodoc_GenericAlias(app):
|
||||
'',
|
||||
' A list of int',
|
||||
'',
|
||||
' alias of :class:`~typing.List`\\ [:class:`int`]',
|
||||
' alias of :py:class:`~typing.List`\\ [:py:class:`int`]',
|
||||
'',
|
||||
'',
|
||||
'.. py:data:: T',
|
||||
@@ -1951,7 +1903,7 @@ def test_autodoc_GenericAlias(app):
|
||||
'',
|
||||
' A list of int',
|
||||
'',
|
||||
' alias of :class:`~typing.List`\\ [:class:`int`]',
|
||||
' alias of :py:class:`~typing.List`\\ [:py:class:`int`]',
|
||||
'',
|
||||
]
|
||||
|
||||
@@ -1983,7 +1935,7 @@ def test_autodoc_TypeVar(app):
|
||||
'',
|
||||
' T6',
|
||||
'',
|
||||
' alias of :class:`int`',
|
||||
' alias of :py:class:`int`',
|
||||
'',
|
||||
'',
|
||||
'.. py:data:: T1',
|
||||
@@ -2023,7 +1975,7 @@ def test_autodoc_TypeVar(app):
|
||||
'',
|
||||
' T6',
|
||||
'',
|
||||
' alias of :class:`int`',
|
||||
' alias of :py:class:`int`',
|
||||
'',
|
||||
'',
|
||||
'.. py:data:: T7',
|
||||
@@ -2031,7 +1983,7 @@ def test_autodoc_TypeVar(app):
|
||||
'',
|
||||
' T7',
|
||||
'',
|
||||
" alias of TypeVar('T7', bound=\\ :class:`int`)",
|
||||
" alias of TypeVar('T7', bound=\\ :py:class:`int`)",
|
||||
'',
|
||||
]
|
||||
|
||||
@@ -2165,6 +2117,9 @@ def test_singledispatchmethod_automethod(app):
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info > (3, 11),
|
||||
reason=('cython does not support python-3.11 yet. '
|
||||
'see https://github.com/cython/cython/issues/4365'))
|
||||
@pytest.mark.skipif(pyximport is None, reason='cython is not installed')
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_cython(app):
|
||||
|
||||
@@ -129,6 +129,7 @@ def test_autoattribute_slots_variable_dict(app):
|
||||
'',
|
||||
'.. py:attribute:: Bar.attr1',
|
||||
' :module: target.slots',
|
||||
' :type: int',
|
||||
'',
|
||||
' docstring of attr1',
|
||||
'',
|
||||
@@ -167,7 +168,7 @@ def test_autoattribute_GenericAlias(app):
|
||||
'',
|
||||
' A list of int',
|
||||
'',
|
||||
' alias of :class:`~typing.List`\\ [:class:`int`]',
|
||||
' alias of :py:class:`~typing.List`\\ [:py:class:`int`]',
|
||||
'',
|
||||
]
|
||||
|
||||
@@ -182,7 +183,7 @@ def test_autoattribute_NewType(app):
|
||||
'',
|
||||
' T6',
|
||||
'',
|
||||
' alias of :class:`int`',
|
||||
' alias of :py:class:`int`',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@@ -212,12 +212,20 @@ def test_properties(app):
|
||||
' docstring',
|
||||
'',
|
||||
'',
|
||||
' .. py:property:: Foo.prop',
|
||||
' .. py:property:: Foo.prop1',
|
||||
' :module: target.properties',
|
||||
' :type: int',
|
||||
'',
|
||||
' docstring',
|
||||
'',
|
||||
'',
|
||||
' .. py:property:: Foo.prop2',
|
||||
' :module: target.properties',
|
||||
' :classmethod:',
|
||||
' :type: int',
|
||||
'',
|
||||
' docstring',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@@ -235,6 +243,7 @@ def test_slots_attribute(app):
|
||||
'',
|
||||
' .. py:attribute:: Bar.attr1',
|
||||
' :module: target.slots',
|
||||
' :type: int',
|
||||
'',
|
||||
' docstring of attr1',
|
||||
'',
|
||||
@@ -257,14 +266,29 @@ def test_show_inheritance_for_subclass_of_generic_type(app):
|
||||
'.. py:class:: Quux(iterable=(), /)',
|
||||
' :module: target.classes',
|
||||
'',
|
||||
' Bases: :class:`~typing.List`\\ '
|
||||
'[:obj:`~typing.Union`\\ [:class:`int`, :class:`float`]]',
|
||||
' Bases: :py:class:`~typing.List`\\ '
|
||||
'[:py:obj:`~typing.Union`\\ [:py:class:`int`, :py:class:`float`]]',
|
||||
'',
|
||||
' A subclass of List[Union[int, float]]',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.')
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_show_inheritance_for_decendants_of_generic_type(app):
|
||||
options = {'show-inheritance': None}
|
||||
actual = do_autodoc(app, 'class', 'target.classes.Corge', options)
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:class:: Corge(iterable=(), /)',
|
||||
' :module: target.classes',
|
||||
'',
|
||||
' Bases: :py:class:`target.classes.Quux`',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_autodoc_process_bases(app):
|
||||
def autodoc_process_bases(app, name, obj, options, bases):
|
||||
@@ -288,7 +312,7 @@ def test_autodoc_process_bases(app):
|
||||
'.. py:class:: Quux(*args, **kwds)',
|
||||
' :module: target.classes',
|
||||
'',
|
||||
' Bases: :class:`int`, :class:`str`',
|
||||
' Bases: :py:class:`int`, :py:class:`str`',
|
||||
'',
|
||||
' A subclass of List[Union[int, float]]',
|
||||
'',
|
||||
@@ -299,7 +323,7 @@ def test_autodoc_process_bases(app):
|
||||
'.. py:class:: Quux(iterable=(), /)',
|
||||
' :module: target.classes',
|
||||
'',
|
||||
' Bases: :class:`int`, :class:`str`',
|
||||
' Bases: :py:class:`int`, :py:class:`str`',
|
||||
'',
|
||||
' A subclass of List[Union[int, float]]',
|
||||
'',
|
||||
@@ -367,7 +391,7 @@ def test_class_alias(app):
|
||||
'.. py:attribute:: Alias',
|
||||
' :module: target.classes',
|
||||
'',
|
||||
' alias of :class:`target.classes.Foo`',
|
||||
' alias of :py:class:`target.classes.Foo`',
|
||||
]
|
||||
|
||||
|
||||
@@ -381,3 +405,57 @@ def test_class_alias_having_doccomment(app):
|
||||
' docstring',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
def test_class_alias_for_imported_object_having_doccomment(app):
|
||||
actual = do_autodoc(app, 'class', 'target.classes.IntAlias')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:attribute:: IntAlias',
|
||||
' :module: target.classes',
|
||||
'',
|
||||
' docstring',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_coroutine(app):
|
||||
options = {"members": None}
|
||||
actual = do_autodoc(app, 'class', 'target.coroutine.AsyncClass', options)
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:class:: AsyncClass()',
|
||||
' :module: target.coroutine',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: AsyncClass.do_asyncgen()',
|
||||
' :module: target.coroutine',
|
||||
' :async:',
|
||||
'',
|
||||
' A documented async generator',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: AsyncClass.do_coroutine()',
|
||||
' :module: target.coroutine',
|
||||
' :async:',
|
||||
'',
|
||||
' A documented coroutine function',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: AsyncClass.do_coroutine2()',
|
||||
' :module: target.coroutine',
|
||||
' :async:',
|
||||
' :classmethod:',
|
||||
'',
|
||||
' A documented coroutine classmethod',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: AsyncClass.do_coroutine3()',
|
||||
' :module: target.coroutine',
|
||||
' :async:',
|
||||
' :staticmethod:',
|
||||
'',
|
||||
' A documented coroutine staticmethod',
|
||||
'',
|
||||
]
|
||||
|
||||
@@ -96,7 +96,7 @@ def test_autodata_GenericAlias(app):
|
||||
'',
|
||||
' A list of int',
|
||||
'',
|
||||
' alias of :class:`~typing.List`\\ [:class:`int`]',
|
||||
' alias of :py:class:`~typing.List`\\ [:py:class:`int`]',
|
||||
'',
|
||||
]
|
||||
|
||||
@@ -111,7 +111,7 @@ def test_autodata_NewType(app):
|
||||
'',
|
||||
' T6',
|
||||
'',
|
||||
' alias of :class:`int`',
|
||||
' alias of :py:class:`int`',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@@ -168,3 +168,38 @@ def test_wrapped_function_contextmanager(app):
|
||||
" You'll feel better in this context!",
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_coroutine(app):
|
||||
actual = do_autodoc(app, 'function', 'target.functions.coroutinefunc')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:function:: coroutinefunc()',
|
||||
' :module: target.functions',
|
||||
' :async:',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_synchronized_coroutine(app):
|
||||
actual = do_autodoc(app, 'function', 'target.coroutine.sync_func')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:function:: sync_func()',
|
||||
' :module: target.coroutine',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_async_generator(app):
|
||||
actual = do_autodoc(app, 'function', 'target.functions.asyncgenerator')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:function:: asyncgenerator()',
|
||||
' :module: target.functions',
|
||||
' :async:',
|
||||
'',
|
||||
]
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from .test_ext_autodoc import do_autodoc
|
||||
@@ -16,13 +18,41 @@ from .test_ext_autodoc import do_autodoc
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_properties(app):
|
||||
actual = do_autodoc(app, 'property', 'target.properties.Foo.prop')
|
||||
actual = do_autodoc(app, 'property', 'target.properties.Foo.prop1')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:property:: Foo.prop',
|
||||
'.. py:property:: Foo.prop1',
|
||||
' :module: target.properties',
|
||||
' :type: int',
|
||||
'',
|
||||
' docstring',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_class_properties(app):
|
||||
actual = do_autodoc(app, 'property', 'target.properties.Foo.prop2')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:property:: Foo.prop2',
|
||||
' :module: target.properties',
|
||||
' :classmethod:',
|
||||
' :type: int',
|
||||
'',
|
||||
' docstring',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_cached_properties(app):
|
||||
actual = do_autodoc(app, 'property', 'target.cached_property.Foo.prop')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:property:: Foo.prop',
|
||||
' :module: target.cached_property',
|
||||
' :type: int',
|
||||
'',
|
||||
]
|
||||
|
||||
@@ -287,14 +287,34 @@ def test_autodoc_inherit_docstrings(app):
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_autodoc_docstring_signature(app):
|
||||
options = {"members": None}
|
||||
options = {"members": None, "special-members": "__init__, __new__"}
|
||||
actual = do_autodoc(app, 'class', 'target.DocstringSig', options)
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:class:: DocstringSig()',
|
||||
# FIXME: Ideally this would instead be: `DocstringSig(d, e=1)` but
|
||||
# currently `ClassDocumenter` does not apply the docstring signature
|
||||
# logic when extracting a signature from a __new__ or __init__ method.
|
||||
'.. py:class:: DocstringSig(*new_args, **new_kwargs)',
|
||||
' :module: target',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: DocstringSig.__init__(self, a, b=1) -> None',
|
||||
' :module: target',
|
||||
'',
|
||||
' First line of docstring',
|
||||
'',
|
||||
' rest of docstring',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: DocstringSig.__new__(cls, d, e=1) -> DocstringSig',
|
||||
' :module: target',
|
||||
' :staticmethod:',
|
||||
'',
|
||||
' First line of docstring',
|
||||
'',
|
||||
' rest of docstring',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: DocstringSig.meth(FOO, BAR=1) -> BAZ',
|
||||
' :module: target',
|
||||
'',
|
||||
@@ -331,10 +351,31 @@ def test_autodoc_docstring_signature(app):
|
||||
actual = do_autodoc(app, 'class', 'target.DocstringSig', options)
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:class:: DocstringSig()',
|
||||
'.. py:class:: DocstringSig(*new_args, **new_kwargs)',
|
||||
' :module: target',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: DocstringSig.__init__(*init_args, **init_kwargs)',
|
||||
' :module: target',
|
||||
'',
|
||||
' __init__(self, a, b=1) -> None',
|
||||
' First line of docstring',
|
||||
'',
|
||||
' rest of docstring',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: DocstringSig.__new__(cls, *new_args, **new_kwargs)',
|
||||
' :module: target',
|
||||
' :staticmethod:',
|
||||
'',
|
||||
' __new__(cls, d, e=1) -> DocstringSig',
|
||||
' First line of docstring',
|
||||
'',
|
||||
' rest of docstring',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: DocstringSig.meth()',
|
||||
' :module: target',
|
||||
'',
|
||||
@@ -495,7 +536,7 @@ def test_mocked_module_imports(app, warning):
|
||||
sys.modules.pop('target', None) # unload target module to clear the module cache
|
||||
|
||||
# no autodoc_mock_imports
|
||||
options = {"members": 'TestAutodoc,decoratedFunction,func'}
|
||||
options = {"members": 'TestAutodoc,decoratedFunction,func,Alias'}
|
||||
actual = do_autodoc(app, 'module', 'target.need_mocks', options)
|
||||
assert list(actual) == []
|
||||
assert "autodoc: failed to import module 'need_mocks'" in warning.getvalue()
|
||||
@@ -516,12 +557,24 @@ def test_mocked_module_imports(app, warning):
|
||||
'.. py:module:: target.need_mocks',
|
||||
'',
|
||||
'',
|
||||
'.. py:data:: Alias',
|
||||
' :module: target.need_mocks',
|
||||
'',
|
||||
' docstring',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: TestAutodoc()',
|
||||
' :module: target.need_mocks',
|
||||
'',
|
||||
' TestAutodoc docstring.',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: TestAutodoc.Alias',
|
||||
' :module: target.need_mocks',
|
||||
'',
|
||||
' docstring',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: TestAutodoc.decoratedMethod()',
|
||||
' :module: target.need_mocks',
|
||||
'',
|
||||
@@ -554,10 +607,26 @@ def test_autodoc_typehints_signature(app):
|
||||
'.. py:module:: target.typehints',
|
||||
'',
|
||||
'',
|
||||
'.. py:data:: CONST1',
|
||||
' :module: target.typehints',
|
||||
' :type: int',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: Math(s: str, o: Optional[Any] = None)',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Math.CONST1',
|
||||
' :module: target.typehints',
|
||||
' :type: int',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Math.CONST2',
|
||||
' :module: target.typehints',
|
||||
' :type: int',
|
||||
' :value: 1',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: Math.decr(a: int, b: int = 1) -> int',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
@@ -574,6 +643,11 @@ def test_autodoc_typehints_signature(app):
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
'',
|
||||
' .. py:property:: Math.prop',
|
||||
' :module: target.typehints',
|
||||
' :type: int',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: NewAnnotation(i: int)',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
@@ -620,10 +694,23 @@ def test_autodoc_typehints_none(app):
|
||||
'.. py:module:: target.typehints',
|
||||
'',
|
||||
'',
|
||||
'.. py:data:: CONST1',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: Math(s, o=None)',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Math.CONST1',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Math.CONST2',
|
||||
' :module: target.typehints',
|
||||
' :value: 1',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: Math.decr(a, b=1)',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
@@ -640,6 +727,10 @@ def test_autodoc_typehints_none(app):
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
'',
|
||||
' .. py:property:: Math.prop',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: NewAnnotation(i)',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
@@ -746,7 +837,7 @@ def test_autodoc_typehints_description(app):
|
||||
' Tuple[int, int]\n'
|
||||
in context)
|
||||
|
||||
# Overloads still get displyed in the signature
|
||||
# Overloads still get displayed in the signature
|
||||
assert ('target.overload.sum(x: int, y: int = 0) -> int\n'
|
||||
'target.overload.sum(x: float, y: float = 0.0) -> float\n'
|
||||
'target.overload.sum(x: str, y: str = None) -> str\n'
|
||||
@@ -765,6 +856,10 @@ def test_autodoc_typehints_description_no_undoc(app):
|
||||
(app.srcdir / 'index.rst').write_text(
|
||||
'.. autofunction:: target.typehints.incr\n'
|
||||
'\n'
|
||||
'.. autofunction:: target.typehints.decr\n'
|
||||
'\n'
|
||||
' :returns: decremented number\n'
|
||||
'\n'
|
||||
'.. autofunction:: target.typehints.tuple_args\n'
|
||||
'\n'
|
||||
' :param x: arg\n'
|
||||
@@ -773,6 +868,14 @@ def test_autodoc_typehints_description_no_undoc(app):
|
||||
app.build()
|
||||
context = (app.outdir / 'index.txt').read_text()
|
||||
assert ('target.typehints.incr(a, b=1)\n'
|
||||
'\n'
|
||||
'target.typehints.decr(a, b=1)\n'
|
||||
'\n'
|
||||
' Returns:\n'
|
||||
' decremented number\n'
|
||||
'\n'
|
||||
' Return type:\n'
|
||||
' int\n'
|
||||
'\n'
|
||||
'target.typehints.tuple_args(x)\n'
|
||||
'\n'
|
||||
@@ -877,7 +980,7 @@ def test_autodoc_typehints_both(app):
|
||||
' Tuple[int, int]\n'
|
||||
in context)
|
||||
|
||||
# Overloads still get displyed in the signature
|
||||
# Overloads still get displayed in the signature
|
||||
assert ('target.overload.sum(x: int, y: int = 0) -> int\n'
|
||||
'target.overload.sum(x: float, y: float = 0.0) -> float\n'
|
||||
'target.overload.sum(x: str, y: str = None) -> str\n'
|
||||
@@ -1039,6 +1142,99 @@ def test_autodoc_typehints_description_and_type_aliases(app):
|
||||
' myint\n' == context)
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc',
|
||||
confoverrides={'autodoc_unqualified_typehints': True})
|
||||
def test_autodoc_unqualified_typehints(app):
|
||||
if sys.version_info < (3, 7):
|
||||
Any = 'Any'
|
||||
else:
|
||||
Any = '~typing.Any'
|
||||
|
||||
options = {"members": None,
|
||||
"undoc-members": None}
|
||||
actual = do_autodoc(app, 'module', 'target.typehints', options)
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:module:: target.typehints',
|
||||
'',
|
||||
'',
|
||||
'.. py:data:: CONST1',
|
||||
' :module: target.typehints',
|
||||
' :type: int',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: Math(s: str, o: ~typing.Optional[%s] = None)' % Any,
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Math.CONST1',
|
||||
' :module: target.typehints',
|
||||
' :type: int',
|
||||
'',
|
||||
'',
|
||||
' .. py:attribute:: Math.CONST2',
|
||||
' :module: target.typehints',
|
||||
' :type: int',
|
||||
' :value: 1',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: Math.decr(a: int, b: int = 1) -> int',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: Math.horse(a: str, b: int) -> None',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: Math.incr(a: int, b: int = 1) -> int',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: Math.nothing() -> None',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
'',
|
||||
' .. py:property:: Math.prop',
|
||||
' :module: target.typehints',
|
||||
' :type: int',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: NewAnnotation(i: int)',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: NewComment(i: int)',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: SignatureFromMetaclass(a: int)',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
'',
|
||||
'.. py:function:: complex_func(arg1: str, arg2: List[int], arg3: Tuple[int, '
|
||||
'Union[str, Unknown]] = None, *args: str, **kwargs: str) -> None',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
'',
|
||||
'.. py:function:: decr(a: int, b: int = 1) -> int',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
'',
|
||||
'.. py:function:: incr(a: int, b: int = 1) -> int',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
'',
|
||||
'.. py:function:: missing_attr(c, a: str, b: Optional[str] = None) -> str',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
'',
|
||||
'.. py:function:: tuple_args(x: ~typing.Tuple[int, ~typing.Union[int, str]]) '
|
||||
'-> ~typing.Tuple[int, int]',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_autodoc_default_options(app):
|
||||
# no settings
|
||||
|
||||
@@ -146,6 +146,7 @@ def test_ismock():
|
||||
|
||||
assert ismock(mod1) is True
|
||||
assert ismock(mod1.Class) is True
|
||||
assert ismock(mod1.submod.Class) is True
|
||||
assert ismock(Inherited) is False
|
||||
|
||||
assert ismock(mod2) is False
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from .test_ext_autodoc import do_autodoc
|
||||
@@ -16,6 +18,11 @@ from .test_ext_autodoc import do_autodoc
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc',
|
||||
confoverrides={'autodoc_preserve_defaults': True})
|
||||
def test_preserve_defaults(app):
|
||||
if sys.version_info < (3, 8):
|
||||
color = "16777215"
|
||||
else:
|
||||
color = "0xFFFFFF"
|
||||
|
||||
options = {"members": None}
|
||||
actual = do_autodoc(app, 'module', 'target.preserve_defaults', options)
|
||||
assert list(actual) == [
|
||||
@@ -29,15 +36,15 @@ def test_preserve_defaults(app):
|
||||
' docstring',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: Class.meth(name: str = CONSTANT, sentinal: Any = SENTINEL, '
|
||||
'now: datetime.datetime = datetime.now()) -> None',
|
||||
' .. py:method:: Class.meth(name: str = CONSTANT, sentinel: Any = SENTINEL, '
|
||||
'now: datetime.datetime = datetime.now(), color: int = %s) -> None' % color,
|
||||
' :module: target.preserve_defaults',
|
||||
'',
|
||||
' docstring',
|
||||
'',
|
||||
'',
|
||||
'.. py:function:: foo(name: str = CONSTANT, sentinal: Any = SENTINEL, now: '
|
||||
'datetime.datetime = datetime.now()) -> None',
|
||||
'.. py:function:: foo(name: str = CONSTANT, sentinel: Any = SENTINEL, now: '
|
||||
'datetime.datetime = datetime.now(), color: int = %s) -> None' % color,
|
||||
' :module: target.preserve_defaults',
|
||||
'',
|
||||
' docstring',
|
||||
|
||||
@@ -63,7 +63,7 @@ def test_mangle_signature():
|
||||
(a=1, b=2, c=3) :: ([a, b, c])
|
||||
(a=1, b=<SomeClass: a, b, c>, c=3) :: ([a, b, c])
|
||||
(a=1, b=T(a=1, b=2), c=3) :: ([a, b, c])
|
||||
(a: int, b: int) -> str :: (a, b)
|
||||
(a: Tuple[int, str], b: int) -> str :: (a, b)
|
||||
"""
|
||||
|
||||
TEST = [[y.strip() for y in x.split("::")] for x in TEST.split("\n")
|
||||
@@ -109,6 +109,11 @@ def test_extract_summary(capsys):
|
||||
'=========']
|
||||
assert extract_summary(doc, document) == 'blah blah'
|
||||
|
||||
doc = ['=========',
|
||||
'blah blah',
|
||||
'=========']
|
||||
assert extract_summary(doc, document) == 'blah blah'
|
||||
|
||||
# hyperlink target
|
||||
doc = ['Do `this <https://www.sphinx-doc.org/>`_ and that. '
|
||||
'blah blah blah.']
|
||||
@@ -211,14 +216,42 @@ def test_autosummary_generate_content_for_module(app):
|
||||
|
||||
context = template.render.call_args[0][1]
|
||||
assert context['members'] == ['CONSTANT1', 'CONSTANT2', 'Exc', 'Foo', '_Baz', '_Exc',
|
||||
'__builtins__', '__cached__', '__doc__', '__file__',
|
||||
'__name__', '__package__', '_quux', 'bar', 'qux']
|
||||
'__all__', '__builtins__', '__cached__', '__doc__',
|
||||
'__file__', '__name__', '__package__', '_quux', 'bar',
|
||||
'quuz', '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'] == ['CONSTANT1', 'qux', 'quuz']
|
||||
assert context['all_attributes'] == ['CONSTANT1', 'qux', 'quuz']
|
||||
assert context['fullname'] == 'autosummary_dummy_module'
|
||||
assert context['module'] == 'autosummary_dummy_module'
|
||||
assert context['objname'] == ''
|
||||
assert context['name'] == ''
|
||||
assert context['objtype'] == 'module'
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='ext-autosummary')
|
||||
def test_autosummary_generate_content_for_module___all__(app):
|
||||
import autosummary_dummy_module
|
||||
template = Mock()
|
||||
app.config.autosummary_ignore_module_all = False
|
||||
|
||||
generate_autosummary_content('autosummary_dummy_module', autosummary_dummy_module, None,
|
||||
template, None, False, app, False, {})
|
||||
assert template.render.call_args[0][0] == 'module'
|
||||
|
||||
context = template.render.call_args[0][1]
|
||||
assert context['members'] == ['CONSTANT1', 'Exc', 'Foo', '_Baz', 'bar', 'qux', 'path']
|
||||
assert context['functions'] == ['bar']
|
||||
assert context['all_functions'] == ['bar']
|
||||
assert context['classes'] == ['Foo']
|
||||
assert context['all_classes'] == ['Foo', '_Baz']
|
||||
assert context['exceptions'] == ['Exc']
|
||||
assert context['all_exceptions'] == ['Exc']
|
||||
assert context['attributes'] == ['CONSTANT1', 'qux']
|
||||
assert context['all_attributes'] == ['CONSTANT1', 'qux']
|
||||
assert context['fullname'] == 'autosummary_dummy_module'
|
||||
@@ -241,9 +274,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'] == ['CONSTANT1', 'CONSTANT2', '_Baz', '_Exc', '__builtins__',
|
||||
'__cached__', '__doc__', '__file__', '__name__',
|
||||
'__package__', '_quux', 'qux']
|
||||
assert context['members'] == ['CONSTANT1', 'CONSTANT2', '_Baz', '_Exc', '__all__',
|
||||
'__builtins__', '__cached__', '__doc__', '__file__',
|
||||
'__name__', '__package__', '_quux', 'quuz', 'qux']
|
||||
assert context['functions'] == []
|
||||
assert context['classes'] == []
|
||||
assert context['exceptions'] == []
|
||||
@@ -260,17 +293,17 @@ def test_autosummary_generate_content_for_module_imported_members(app):
|
||||
|
||||
context = template.render.call_args[0][1]
|
||||
assert context['members'] == ['CONSTANT1', 'CONSTANT2', 'Exc', 'Foo', 'Union', '_Baz',
|
||||
'_Exc', '__builtins__', '__cached__', '__doc__',
|
||||
'_Exc', '__all__', '__builtins__', '__cached__', '__doc__',
|
||||
'__file__', '__loader__', '__name__', '__package__',
|
||||
'__spec__', '_quux', 'bar', 'path', 'qux']
|
||||
'__spec__', '_quux', 'bar', 'path', 'quuz', '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'] == ['CONSTANT1', 'qux']
|
||||
assert context['all_attributes'] == ['CONSTANT1', 'qux']
|
||||
assert context['attributes'] == ['CONSTANT1', 'qux', 'quuz']
|
||||
assert context['all_attributes'] == ['CONSTANT1', 'qux', 'quuz']
|
||||
assert context['fullname'] == 'autosummary_dummy_module'
|
||||
assert context['module'] == 'autosummary_dummy_module'
|
||||
assert context['objname'] == ''
|
||||
@@ -308,6 +341,7 @@ def test_autosummary_generate(app, status, warning):
|
||||
assert doctree[3][0][0][2][5].astext() == 'autosummary_dummy_module.qux\n\na module-level attribute'
|
||||
|
||||
module = (app.srcdir / 'generated' / 'autosummary_dummy_module.rst').read_text()
|
||||
|
||||
assert (' .. autosummary::\n'
|
||||
' \n'
|
||||
' Foo\n'
|
||||
@@ -316,6 +350,7 @@ def test_autosummary_generate(app, status, warning):
|
||||
' \n'
|
||||
' CONSTANT1\n'
|
||||
' qux\n'
|
||||
' quuz\n'
|
||||
' \n' in module)
|
||||
|
||||
Foo = (app.srcdir / 'generated' / 'autosummary_dummy_module.Foo.rst').read_text()
|
||||
|
||||
36
tests/test_ext_extlinks.py
Normal file
36
tests/test_ext_extlinks.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-extlinks-hardcoded-urls')
|
||||
def test_replaceable_uris_emit_extlinks_warnings(app, warning):
|
||||
app.build()
|
||||
warning_output = warning.getvalue()
|
||||
# there should be exactly three warnings for replaceable URLs
|
||||
message = (
|
||||
"WARNING: hardcoded link 'https://github.com/sphinx-doc/sphinx/issues/1' "
|
||||
"could be replaced by an extlink (try using ':issue:`1`' instead)"
|
||||
)
|
||||
assert f"index.rst:11: {message}" in warning_output
|
||||
assert f"index.rst:13: {message}" in warning_output
|
||||
assert f"index.rst:15: {message}" in warning_output
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-extlinks-hardcoded-urls-multiple-replacements')
|
||||
def test_all_replacements_suggested_if_multiple_replacements_possible(app, warning):
|
||||
app.build()
|
||||
warning_output = warning.getvalue()
|
||||
# there should be six warnings for replaceable URLs, three pairs per link
|
||||
message = (
|
||||
"WARNING: hardcoded link 'https://github.com/octocat' "
|
||||
"could be replaced by an extlink (try using ':user:`octocat`' instead)"
|
||||
)
|
||||
assert f"index.rst:14: {message}" in warning_output
|
||||
assert f"index.rst:16: {message}" in warning_output
|
||||
assert f"index.rst:18: {message}" in warning_output
|
||||
message = (
|
||||
"WARNING: hardcoded link 'https://github.com/octocat' "
|
||||
"could be replaced by an extlink (try using ':repo:`octocat`' instead)"
|
||||
)
|
||||
assert f"index.rst:14: {message}" in warning_output
|
||||
assert f"index.rst:16: {message}" in warning_output
|
||||
assert f"index.rst:18: {message}" in warning_output
|
||||
@@ -42,6 +42,12 @@ def reference_check(app, *args, **kwds):
|
||||
return missing_reference(app, app.env, node, contnode)
|
||||
|
||||
|
||||
def set_config(app, mapping):
|
||||
app.config.intersphinx_mapping = mapping
|
||||
app.config.intersphinx_cache_limit = 0
|
||||
app.config.intersphinx_disabled_reftypes = []
|
||||
|
||||
|
||||
@mock.patch('sphinx.ext.intersphinx.InventoryFile')
|
||||
@mock.patch('sphinx.ext.intersphinx._read_from_url')
|
||||
def test_fetch_inventory_redirection(_read_from_url, InventoryFile, app, status, warning):
|
||||
@@ -90,13 +96,12 @@ def test_fetch_inventory_redirection(_read_from_url, InventoryFile, app, status,
|
||||
def test_missing_reference(tempdir, app, status, warning):
|
||||
inv_file = tempdir / 'inventory'
|
||||
inv_file.write_bytes(inventory_v2)
|
||||
app.config.intersphinx_mapping = {
|
||||
set_config(app, {
|
||||
'https://docs.python.org/': inv_file,
|
||||
'py3k': ('https://docs.python.org/py3k/', inv_file),
|
||||
'py3krel': ('py3k', inv_file), # relative path
|
||||
'py3krelparent': ('../../py3k', inv_file), # relative path, parent dir
|
||||
}
|
||||
app.config.intersphinx_cache_limit = 0
|
||||
})
|
||||
|
||||
# load the inventory and check if it's done correctly
|
||||
normalize_intersphinx_mapping(app, app.config)
|
||||
@@ -133,12 +138,12 @@ def test_missing_reference(tempdir, app, status, warning):
|
||||
refexplicit=True)
|
||||
assert rn[0].astext() == 'py3k:module2'
|
||||
|
||||
# prefix given, target not found and nonexplicit title: prefix is stripped
|
||||
# prefix given, target not found and nonexplicit title: prefix is not stripped
|
||||
node, contnode = fake_node('py', 'mod', 'py3k:unknown', 'py3k:unknown',
|
||||
refexplicit=False)
|
||||
rn = missing_reference(app, app.env, node, contnode)
|
||||
assert rn is None
|
||||
assert contnode[0].astext() == 'unknown'
|
||||
assert contnode[0].astext() == 'py3k:unknown'
|
||||
|
||||
# prefix given, target not found and explicit title: nothing is changed
|
||||
node, contnode = fake_node('py', 'mod', 'py3k:unknown', 'py3k:unknown',
|
||||
@@ -169,10 +174,9 @@ def test_missing_reference(tempdir, app, status, warning):
|
||||
def test_missing_reference_pydomain(tempdir, app, status, warning):
|
||||
inv_file = tempdir / 'inventory'
|
||||
inv_file.write_bytes(inventory_v2)
|
||||
app.config.intersphinx_mapping = {
|
||||
set_config(app, {
|
||||
'https://docs.python.org/': inv_file,
|
||||
}
|
||||
app.config.intersphinx_cache_limit = 0
|
||||
})
|
||||
|
||||
# load the inventory and check if it's done correctly
|
||||
normalize_intersphinx_mapping(app, app.config)
|
||||
@@ -196,14 +200,23 @@ def test_missing_reference_pydomain(tempdir, app, status, warning):
|
||||
rn = missing_reference(app, app.env, node, contnode)
|
||||
assert rn.astext() == 'Foo.bar'
|
||||
|
||||
# term reference (normal)
|
||||
node, contnode = fake_node('std', 'term', 'a term', 'a term')
|
||||
rn = missing_reference(app, app.env, node, contnode)
|
||||
assert rn.astext() == 'a term'
|
||||
|
||||
# term reference (case insensitive)
|
||||
node, contnode = fake_node('std', 'term', 'A TERM', 'A TERM')
|
||||
rn = missing_reference(app, app.env, node, contnode)
|
||||
assert rn.astext() == 'A TERM'
|
||||
|
||||
|
||||
def test_missing_reference_stddomain(tempdir, app, status, warning):
|
||||
inv_file = tempdir / 'inventory'
|
||||
inv_file.write_bytes(inventory_v2)
|
||||
app.config.intersphinx_mapping = {
|
||||
set_config(app, {
|
||||
'cmd': ('https://docs.python.org/', inv_file),
|
||||
}
|
||||
app.config.intersphinx_cache_limit = 0
|
||||
})
|
||||
|
||||
# load the inventory and check if it's done correctly
|
||||
normalize_intersphinx_mapping(app, app.config)
|
||||
@@ -232,10 +245,9 @@ def test_missing_reference_stddomain(tempdir, app, status, warning):
|
||||
def test_missing_reference_cppdomain(tempdir, app, status, warning):
|
||||
inv_file = tempdir / 'inventory'
|
||||
inv_file.write_bytes(inventory_v2)
|
||||
app.config.intersphinx_mapping = {
|
||||
set_config(app, {
|
||||
'https://docs.python.org/': inv_file,
|
||||
}
|
||||
app.config.intersphinx_cache_limit = 0
|
||||
})
|
||||
|
||||
# load the inventory and check if it's done correctly
|
||||
normalize_intersphinx_mapping(app, app.config)
|
||||
@@ -259,10 +271,9 @@ def test_missing_reference_cppdomain(tempdir, app, status, warning):
|
||||
def test_missing_reference_jsdomain(tempdir, app, status, warning):
|
||||
inv_file = tempdir / 'inventory'
|
||||
inv_file.write_bytes(inventory_v2)
|
||||
app.config.intersphinx_mapping = {
|
||||
set_config(app, {
|
||||
'https://docs.python.org/': inv_file,
|
||||
}
|
||||
app.config.intersphinx_cache_limit = 0
|
||||
})
|
||||
|
||||
# load the inventory and check if it's done correctly
|
||||
normalize_intersphinx_mapping(app, app.config)
|
||||
@@ -281,14 +292,75 @@ def test_missing_reference_jsdomain(tempdir, app, status, warning):
|
||||
assert rn.astext() == 'baz()'
|
||||
|
||||
|
||||
def test_missing_reference_disabled_domain(tempdir, app, status, warning):
|
||||
inv_file = tempdir / 'inventory'
|
||||
inv_file.write_bytes(inventory_v2)
|
||||
set_config(app, {
|
||||
'inv': ('https://docs.python.org/', inv_file),
|
||||
})
|
||||
|
||||
# load the inventory and check if it's done correctly
|
||||
normalize_intersphinx_mapping(app, app.config)
|
||||
load_mappings(app)
|
||||
|
||||
def case(*, term, doc, py):
|
||||
def assert_(rn, expected):
|
||||
if expected is None:
|
||||
assert rn is None
|
||||
else:
|
||||
assert rn.astext() == expected
|
||||
|
||||
kwargs = {}
|
||||
|
||||
node, contnode = fake_node('std', 'term', 'a term', 'a term', **kwargs)
|
||||
rn = missing_reference(app, app.env, node, contnode)
|
||||
assert_(rn, 'a term' if term else None)
|
||||
|
||||
node, contnode = fake_node('std', 'term', 'inv:a term', 'a term', **kwargs)
|
||||
rn = missing_reference(app, app.env, node, contnode)
|
||||
assert_(rn, 'a term')
|
||||
|
||||
node, contnode = fake_node('std', 'doc', 'docname', 'docname', **kwargs)
|
||||
rn = missing_reference(app, app.env, node, contnode)
|
||||
assert_(rn, 'docname' if doc else None)
|
||||
|
||||
node, contnode = fake_node('std', 'doc', 'inv:docname', 'docname', **kwargs)
|
||||
rn = missing_reference(app, app.env, node, contnode)
|
||||
assert_(rn, 'docname')
|
||||
|
||||
# an arbitrary ref in another domain
|
||||
node, contnode = fake_node('py', 'func', 'module1.func', 'func()', **kwargs)
|
||||
rn = missing_reference(app, app.env, node, contnode)
|
||||
assert_(rn, 'func()' if py else None)
|
||||
|
||||
node, contnode = fake_node('py', 'func', 'inv:module1.func', 'func()', **kwargs)
|
||||
rn = missing_reference(app, app.env, node, contnode)
|
||||
assert_(rn, 'func()')
|
||||
|
||||
# the base case, everything should resolve
|
||||
assert app.config.intersphinx_disabled_reftypes == []
|
||||
case(term=True, doc=True, py=True)
|
||||
|
||||
# disabled a single ref type
|
||||
app.config.intersphinx_disabled_reftypes = ['std:doc']
|
||||
case(term=True, doc=False, py=True)
|
||||
|
||||
# disabled a whole domain
|
||||
app.config.intersphinx_disabled_reftypes = ['std:*']
|
||||
case(term=False, doc=False, py=True)
|
||||
|
||||
# disabled all domains
|
||||
app.config.intersphinx_disabled_reftypes = ['*']
|
||||
case(term=False, doc=False, py=False)
|
||||
|
||||
|
||||
@pytest.mark.xfail(os.name != 'posix', reason="Path separator mismatch issue")
|
||||
def test_inventory_not_having_version(tempdir, app, status, warning):
|
||||
inv_file = tempdir / 'inventory'
|
||||
inv_file.write_bytes(inventory_v2_not_having_version)
|
||||
app.config.intersphinx_mapping = {
|
||||
set_config(app, {
|
||||
'https://docs.python.org/': inv_file,
|
||||
}
|
||||
app.config.intersphinx_cache_limit = 0
|
||||
})
|
||||
|
||||
# load the inventory and check if it's done correctly
|
||||
normalize_intersphinx_mapping(app, app.config)
|
||||
@@ -308,16 +380,15 @@ def test_load_mappings_warnings(tempdir, app, status, warning):
|
||||
"""
|
||||
inv_file = tempdir / 'inventory'
|
||||
inv_file.write_bytes(inventory_v2)
|
||||
app.config.intersphinx_mapping = {
|
||||
set_config(app, {
|
||||
'https://docs.python.org/': inv_file,
|
||||
'py3k': ('https://docs.python.org/py3k/', inv_file),
|
||||
'repoze.workflow': ('http://docs.repoze.org/workflow/', inv_file),
|
||||
'django-taggit': ('http://django-taggit.readthedocs.org/en/latest/',
|
||||
inv_file),
|
||||
12345: ('http://www.sphinx-doc.org/en/stable/', inv_file),
|
||||
}
|
||||
})
|
||||
|
||||
app.config.intersphinx_cache_limit = 0
|
||||
# load the inventory and check if it's done correctly
|
||||
normalize_intersphinx_mapping(app, app.config)
|
||||
load_mappings(app)
|
||||
@@ -327,7 +398,7 @@ def test_load_mappings_warnings(tempdir, app, status, warning):
|
||||
def test_load_mappings_fallback(tempdir, app, status, warning):
|
||||
inv_file = tempdir / 'inventory'
|
||||
inv_file.write_bytes(inventory_v2)
|
||||
app.config.intersphinx_cache_limit = 0
|
||||
set_config(app, {})
|
||||
|
||||
# connect to invalid path
|
||||
app.config.intersphinx_mapping = {
|
||||
|
||||
@@ -221,6 +221,7 @@ def test_mathjax3_config(app, status, warning):
|
||||
|
||||
content = (app.outdir / 'index.html').read_text()
|
||||
assert MATHJAX_URL in content
|
||||
assert ('<script defer="defer" src="%s">' % MATHJAX_URL in content)
|
||||
assert ('<script>window.MathJax = {"extensions": ["tex2jax.js"]}</script>' in content)
|
||||
|
||||
|
||||
@@ -231,12 +232,35 @@ def test_mathjax2_config(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
content = (app.outdir / 'index.html').read_text()
|
||||
assert MATHJAX_URL in content
|
||||
assert ('<script async="async" src="%s">' % MATHJAX_URL in content)
|
||||
assert ('<script type="text/x-mathjax-config">'
|
||||
'MathJax.Hub.Config({"extensions": ["tex2jax.js"]})'
|
||||
'</script>' in content)
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-math',
|
||||
confoverrides={'extensions': ['sphinx.ext.mathjax'],
|
||||
'mathjax_options': {'async': 'async'},
|
||||
'mathjax3_config': {'extensions': ['tex2jax.js']}})
|
||||
def test_mathjax_options_async_for_mathjax3(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
content = (app.outdir / 'index.html').read_text()
|
||||
assert MATHJAX_URL in content
|
||||
assert ('<script async="async" src="%s">' % MATHJAX_URL in content)
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-math',
|
||||
confoverrides={'extensions': ['sphinx.ext.mathjax'],
|
||||
'mathjax_options': {'defer': 'defer'},
|
||||
'mathjax2_config': {'extensions': ['tex2jax.js']}})
|
||||
def test_mathjax_options_defer_for_mathjax2(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
content = (app.outdir / 'index.html').read_text()
|
||||
assert ('<script defer="defer" src="%s">' % MATHJAX_URL in content)
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-math',
|
||||
confoverrides={'extensions': ['sphinx.ext.mathjax']})
|
||||
def test_mathjax_is_installed_only_if_document_having_math(app, status, warning):
|
||||
@@ -256,3 +280,16 @@ def test_mathjax_is_not_installed_if_no_equations(app, status, warning):
|
||||
|
||||
content = (app.outdir / 'index.html').read_text()
|
||||
assert 'MathJax.js' not in content
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-math',
|
||||
confoverrides={'extensions': ['sphinx.ext.mathjax']})
|
||||
def test_mathjax_is_installed_if_no_equations_when_forced(app, status, warning):
|
||||
app.set_html_assets_policy('always')
|
||||
app.builder.build_all()
|
||||
|
||||
content = (app.outdir / 'index.html').read_text()
|
||||
assert MATHJAX_URL in content
|
||||
|
||||
content = (app.outdir / 'nomath.html').read_text()
|
||||
assert MATHJAX_URL in content
|
||||
|
||||
31
tests/test_extension.py
Normal file
31
tests/test_extension.py
Normal file
@@ -0,0 +1,31 @@
|
||||
"""
|
||||
test_extension
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Test sphinx.extension module.
|
||||
|
||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from sphinx.errors import VersionRequirementError
|
||||
from sphinx.extension import Extension, verify_needs_extensions
|
||||
|
||||
|
||||
def test_needs_extensions(app):
|
||||
# empty needs_extensions
|
||||
assert app.config.needs_extensions == {}
|
||||
verify_needs_extensions(app, app.config)
|
||||
|
||||
# needs_extensions fulfilled
|
||||
app.config.needs_extensions = {'test.extension': '3.9'}
|
||||
app.extensions['test.extension'] = Extension('test.extension', 'test.extension', version='3.10')
|
||||
verify_needs_extensions(app, app.config)
|
||||
|
||||
# needs_extensions not fulfilled
|
||||
app.config.needs_extensions = {'test.extension': '3.11'}
|
||||
app.extensions['test.extension'] = Extension('test.extension', 'test.extension', version='3.10')
|
||||
with pytest.raises(VersionRequirementError):
|
||||
verify_needs_extensions(app, app.config)
|
||||
@@ -12,6 +12,7 @@
|
||||
import os
|
||||
import re
|
||||
|
||||
import pygments
|
||||
import pytest
|
||||
from babel.messages import mofile, pofile
|
||||
from babel.messages.catalog import Catalog
|
||||
@@ -30,6 +31,8 @@ sphinx_intl = pytest.mark.sphinx(
|
||||
},
|
||||
)
|
||||
|
||||
pygments_version = tuple(int(v) for v in pygments.__version__.split('.'))
|
||||
|
||||
|
||||
def read_po(pathname):
|
||||
with pathname.open() as f:
|
||||
@@ -622,7 +625,7 @@ def test_html_meta(app):
|
||||
assert expected_expr in result
|
||||
expected_expr = '<meta content="I18N, SPHINX, MARKUP" name="keywords" />'
|
||||
assert expected_expr in result
|
||||
expected_expr = '<p class="caption"><span class="caption-text">HIDDEN TOC</span></p>'
|
||||
expected_expr = '<p class="caption" role="heading"><span class="caption-text">HIDDEN TOC</span></p>'
|
||||
assert expected_expr in result
|
||||
|
||||
|
||||
@@ -1060,8 +1063,13 @@ def test_additional_targets_should_not_be_translated(app):
|
||||
assert_count(expected_expr, result, 1)
|
||||
|
||||
# C code block with lang should not be translated but be *C* highlighted
|
||||
expected_expr = ("""<span class="cp">#include</span> """
|
||||
"""<span class="cpf"><stdio.h></span>""")
|
||||
if pygments_version < (2, 10, 0):
|
||||
expected_expr = ("""<span class="cp">#include</span> """
|
||||
"""<span class="cpf"><stdio.h></span>""")
|
||||
else:
|
||||
expected_expr = ("""<span class="cp">#include</span>"""
|
||||
"""<span class="w"> </span>"""
|
||||
"""<span class="cpf"><stdio.h></span>""")
|
||||
assert_count(expected_expr, result, 1)
|
||||
|
||||
# literal block in list item should not be translated
|
||||
@@ -1138,8 +1146,13 @@ def test_additional_targets_should_be_translated(app):
|
||||
assert_count(expected_expr, result, 1)
|
||||
|
||||
# C code block with lang should be translated and be *C* highlighted
|
||||
expected_expr = ("""<span class="cp">#include</span> """
|
||||
"""<span class="cpf"><STDIO.H></span>""")
|
||||
if pygments_version < (2, 10, 0):
|
||||
expected_expr = ("""<span class="cp">#include</span> """
|
||||
"""<span class="cpf"><STDIO.H></span>""")
|
||||
else:
|
||||
expected_expr = ("""<span class="cp">#include</span>"""
|
||||
"""<span class="w"> </span>"""
|
||||
"""<span class="cpf"><STDIO.H></span>""")
|
||||
assert_count(expected_expr, result, 1)
|
||||
|
||||
# literal block in list item should be translated
|
||||
@@ -1288,6 +1301,44 @@ def getwarning(warnings):
|
||||
return strip_escseq(warnings.getvalue().replace(os.sep, '/'))
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='basic',
|
||||
srcdir='gettext_allow_fuzzy_translations',
|
||||
confoverrides={
|
||||
'language': 'de',
|
||||
'gettext_allow_fuzzy_translations': True
|
||||
})
|
||||
def test_gettext_allow_fuzzy_translations(app):
|
||||
locale_dir = app.srcdir / 'locales' / 'de' / 'LC_MESSAGES'
|
||||
locale_dir.makedirs()
|
||||
with (locale_dir / 'index.po').open('wb') as f:
|
||||
catalog = Catalog()
|
||||
catalog.add('features', 'FEATURES', flags=('fuzzy',))
|
||||
pofile.write_po(f, catalog)
|
||||
|
||||
app.build()
|
||||
content = (app.outdir / 'index.html').read_text()
|
||||
assert 'FEATURES' in content
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='basic',
|
||||
srcdir='gettext_disallow_fuzzy_translations',
|
||||
confoverrides={
|
||||
'language': 'de',
|
||||
'gettext_allow_fuzzy_translations': False
|
||||
})
|
||||
def test_gettext_disallow_fuzzy_translations(app):
|
||||
locale_dir = app.srcdir / 'locales' / 'de' / 'LC_MESSAGES'
|
||||
locale_dir.makedirs()
|
||||
with (locale_dir / 'index.po').open('wb') as f:
|
||||
catalog = Catalog()
|
||||
catalog.add('features', 'FEATURES', flags=('fuzzy',))
|
||||
pofile.write_po(f, catalog)
|
||||
|
||||
app.build()
|
||||
content = (app.outdir / 'index.html').read_text()
|
||||
assert 'FEATURES' not in content
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='basic', confoverrides={'language': 'de'})
|
||||
def test_customize_system_message(make_app, app_params, sphinx_test_tempdir):
|
||||
try:
|
||||
|
||||
@@ -36,7 +36,7 @@ def settings(app):
|
||||
settings.env = app.builder.env
|
||||
settings.env.temp_data['docname'] = 'dummy'
|
||||
settings.contentsname = 'dummy'
|
||||
settings.rfc_base_url = 'http://tools.ietf.org/html/'
|
||||
settings.rfc_base_url = 'http://datatracker.ietf.org/doc/html/'
|
||||
domain_context = sphinx_domains(settings.env)
|
||||
domain_context.enable()
|
||||
yield settings
|
||||
@@ -181,10 +181,10 @@ def get_verifier(verify, verify_re):
|
||||
'verify',
|
||||
':rfc:`2324`',
|
||||
('<p><span class="target" id="index-0"></span><a class="rfc reference external" '
|
||||
'href="http://tools.ietf.org/html/rfc2324.html"><strong>RFC 2324</strong></a></p>'),
|
||||
'href="http://datatracker.ietf.org/doc/html/rfc2324.html"><strong>RFC 2324</strong></a></p>'),
|
||||
('\\sphinxAtStartPar\n'
|
||||
'\\index{RFC@\\spxentry{RFC}!RFC 2324@\\spxentry{RFC 2324}}'
|
||||
'\\sphinxhref{http://tools.ietf.org/html/rfc2324.html}'
|
||||
'\\sphinxhref{http://datatracker.ietf.org/doc/html/rfc2324.html}'
|
||||
'{\\sphinxstylestrong{RFC 2324}}')
|
||||
),
|
||||
(
|
||||
@@ -192,11 +192,11 @@ def get_verifier(verify, verify_re):
|
||||
'verify',
|
||||
':rfc:`2324#id1`',
|
||||
('<p><span class="target" id="index-0"></span><a class="rfc reference external" '
|
||||
'href="http://tools.ietf.org/html/rfc2324.html#id1">'
|
||||
'href="http://datatracker.ietf.org/doc/html/rfc2324.html#id1">'
|
||||
'<strong>RFC 2324#id1</strong></a></p>'),
|
||||
('\\sphinxAtStartPar\n'
|
||||
'\\index{RFC@\\spxentry{RFC}!RFC 2324\\#id1@\\spxentry{RFC 2324\\#id1}}'
|
||||
'\\sphinxhref{http://tools.ietf.org/html/rfc2324.html\\#id1}'
|
||||
'\\sphinxhref{http://datatracker.ietf.org/doc/html/rfc2324.html\\#id1}'
|
||||
'{\\sphinxstylestrong{RFC 2324\\#id1}}')
|
||||
),
|
||||
(
|
||||
|
||||
@@ -53,8 +53,9 @@ from sphinx.pycode import ast
|
||||
("+ a", "+ a"), # UAdd
|
||||
("- 1", "- 1"), # UnaryOp
|
||||
("- a", "- a"), # USub
|
||||
("(1, 2, 3)", "(1, 2, 3)"), # Tuple
|
||||
("(1, 2, 3)", "(1, 2, 3)"), # Tuple
|
||||
("()", "()"), # Tuple (empty)
|
||||
("(1,)", "(1,)"), # Tuple (single item)
|
||||
])
|
||||
def test_unparse(source, expected):
|
||||
module = ast.parse(source)
|
||||
|
||||
@@ -224,7 +224,7 @@ def test_class():
|
||||
'\n'
|
||||
' def __init__(self):\n'
|
||||
' self.a = 1 + 1 #: comment3\n'
|
||||
' self.attr2 = 1 + 1 #: overrided\n'
|
||||
' self.attr2 = 1 + 1 #: overridden\n'
|
||||
' b = 1 + 1 #: comment5\n'
|
||||
'\n'
|
||||
' def some_method(self):\n'
|
||||
@@ -233,7 +233,7 @@ def test_class():
|
||||
parser.parse()
|
||||
assert parser.comments == {('Foo', 'attr1'): 'comment1',
|
||||
('Foo', 'a'): 'comment3',
|
||||
('Foo', 'attr2'): 'overrided'}
|
||||
('Foo', 'attr2'): 'overridden'}
|
||||
assert parser.definitions == {'Foo': ('class', 1, 11),
|
||||
'Foo.__init__': ('def', 5, 8),
|
||||
'Foo.some_method': ('def', 10, 11)}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
import time
|
||||
from io import StringIO
|
||||
from os import path
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -250,3 +251,18 @@ def test_extensions(tempdir):
|
||||
ns = {}
|
||||
exec(conffile.read_text(), ns)
|
||||
assert ns['extensions'] == ['foo', 'bar', 'baz']
|
||||
|
||||
|
||||
def test_exits_when_existing_confpy(monkeypatch):
|
||||
# The code detects existing conf.py with path.isfile()
|
||||
# so we mock it as True with pytest's monkeypatch
|
||||
def mock_isfile(path):
|
||||
return True
|
||||
monkeypatch.setattr(path, 'isfile', mock_isfile)
|
||||
|
||||
qs.term_input = mock_input({
|
||||
'Please enter a new root path (or just Enter to exit)': ''
|
||||
})
|
||||
d = {}
|
||||
with pytest.raises(SystemExit):
|
||||
qs.ask_user(d)
|
||||
|
||||
@@ -66,7 +66,11 @@ test that non-comments are indexed: fermion
|
||||
def test_objects_are_escaped(app, status, warning):
|
||||
app.builder.build_all()
|
||||
index = jsload(app.outdir / 'searchindex.js')
|
||||
assert 'n::Array<T, d>' in index.get('objects').get('') # n::Array<T,d> is escaped
|
||||
for item in index.get('objects').get(''):
|
||||
if item[-1] == 'n::Array<T, d>': # n::Array<T,d> is escaped
|
||||
break
|
||||
else:
|
||||
assert False, index.get('objects').get('')
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='search')
|
||||
@@ -129,50 +133,58 @@ def test_term_in_raw_directive(app, status, warning):
|
||||
|
||||
|
||||
def test_IndexBuilder():
|
||||
domain = DummyDomain([('objname', 'objdispname', 'objtype', 'docname', '#anchor', 1),
|
||||
('objname2', 'objdispname2', 'objtype2', 'docname2', '', -1)])
|
||||
env = DummyEnvironment('1.0', {'dummy': domain})
|
||||
domain1 = DummyDomain([('objname1', 'objdispname1', 'objtype1', 'docname1_1', '#anchor', 1),
|
||||
('objname2', 'objdispname2', 'objtype2', 'docname1_2', '', -1)])
|
||||
domain2 = DummyDomain([('objname1', 'objdispname1', 'objtype1', 'docname2_1', '#anchor', 1),
|
||||
('objname2', 'objdispname2', 'objtype2', 'docname2_2', '', -1)])
|
||||
env = DummyEnvironment('1.0', {'dummy1': domain1, 'dummy2': domain2})
|
||||
doc = utils.new_document(b'test data', settings)
|
||||
doc['file'] = 'dummy'
|
||||
parser.parse(FILE_CONTENTS, doc)
|
||||
|
||||
# feed
|
||||
index = IndexBuilder(env, 'en', {}, None)
|
||||
index.feed('docname', 'filename', 'title', doc)
|
||||
index.feed('docname2', 'filename2', 'title2', doc)
|
||||
assert index._titles == {'docname': 'title', 'docname2': 'title2'}
|
||||
assert index._filenames == {'docname': 'filename', 'docname2': 'filename2'}
|
||||
index.feed('docname1_1', 'filename1_1', 'title1_1', doc)
|
||||
index.feed('docname1_2', 'filename1_2', 'title1_2', doc)
|
||||
index.feed('docname2_1', 'filename2_1', 'title2_1', doc)
|
||||
index.feed('docname2_2', 'filename2_2', 'title2_2', doc)
|
||||
assert index._titles == {'docname1_1': 'title1_1', 'docname1_2': 'title1_2',
|
||||
'docname2_1': 'title2_1', 'docname2_2': 'title2_2'}
|
||||
assert index._filenames == {'docname1_1': 'filename1_1', 'docname1_2': 'filename1_2',
|
||||
'docname2_1': 'filename2_1', 'docname2_2': 'filename2_2'}
|
||||
assert index._mapping == {
|
||||
'ar': {'docname', 'docname2'},
|
||||
'fermion': {'docname', 'docname2'},
|
||||
'comment': {'docname', 'docname2'},
|
||||
'non': {'docname', 'docname2'},
|
||||
'index': {'docname', 'docname2'},
|
||||
'test': {'docname', 'docname2'}
|
||||
'ar': {'docname1_1', 'docname1_2', 'docname2_1', 'docname2_2'},
|
||||
'fermion': {'docname1_1', 'docname1_2', 'docname2_1', 'docname2_2'},
|
||||
'comment': {'docname1_1', 'docname1_2', 'docname2_1', 'docname2_2'},
|
||||
'non': {'docname1_1', 'docname1_2', 'docname2_1', 'docname2_2'},
|
||||
'index': {'docname1_1', 'docname1_2', 'docname2_1', 'docname2_2'},
|
||||
'test': {'docname1_1', 'docname1_2', 'docname2_1', 'docname2_2'}
|
||||
}
|
||||
assert index._title_mapping == {'section_titl': {'docname', 'docname2'}}
|
||||
assert index._title_mapping == {'section_titl': {'docname1_1', 'docname1_2', 'docname2_1', 'docname2_2'}}
|
||||
assert index._objtypes == {}
|
||||
assert index._objnames == {}
|
||||
|
||||
# freeze
|
||||
assert index.freeze() == {
|
||||
'docnames': ('docname', 'docname2'),
|
||||
'docnames': ('docname1_1', 'docname1_2', 'docname2_1', 'docname2_2'),
|
||||
'envversion': '1.0',
|
||||
'filenames': ['filename', 'filename2'],
|
||||
'objects': {'': {'objdispname': (0, 0, 1, '#anchor')}},
|
||||
'objnames': {0: ('dummy', 'objtype', 'objtype')},
|
||||
'objtypes': {0: 'dummy:objtype'},
|
||||
'terms': {'ar': [0, 1],
|
||||
'comment': [0, 1],
|
||||
'fermion': [0, 1],
|
||||
'index': [0, 1],
|
||||
'non': [0, 1],
|
||||
'test': [0, 1]},
|
||||
'titles': ('title', 'title2'),
|
||||
'titleterms': {'section_titl': [0, 1]}
|
||||
'filenames': ['filename1_1', 'filename1_2', 'filename2_1', 'filename2_2'],
|
||||
'objects': {'': [(0, 0, 1, '#anchor', 'objdispname1'),
|
||||
(2, 1, 1, '#anchor', 'objdispname1')]},
|
||||
'objnames': {0: ('dummy1', 'objtype1', 'objtype1'), 1: ('dummy2', 'objtype1', 'objtype1')},
|
||||
'objtypes': {0: 'dummy1:objtype1', 1: 'dummy2:objtype1'},
|
||||
'terms': {'ar': [0, 1, 2, 3],
|
||||
'comment': [0, 1, 2, 3],
|
||||
'fermion': [0, 1, 2, 3],
|
||||
'index': [0, 1, 2, 3],
|
||||
'non': [0, 1, 2, 3],
|
||||
'test': [0, 1, 2, 3]},
|
||||
'titles': ('title1_1', 'title1_2', 'title2_1', 'title2_2'),
|
||||
'titleterms': {'section_titl': [0, 1, 2, 3]}
|
||||
}
|
||||
assert index._objtypes == {('dummy', 'objtype'): 0}
|
||||
assert index._objnames == {0: ('dummy', 'objtype', 'objtype')}
|
||||
assert index._objtypes == {('dummy1', 'objtype1'): 0, ('dummy2', 'objtype1'): 1}
|
||||
assert index._objnames == {0: ('dummy1', 'objtype1', 'objtype1'),
|
||||
1: ('dummy2', 'objtype1', 'objtype1')}
|
||||
|
||||
# dump / load
|
||||
stream = BytesIO()
|
||||
@@ -195,40 +207,41 @@ def test_IndexBuilder():
|
||||
assert index2._objnames == index._objnames
|
||||
|
||||
# prune
|
||||
index.prune(['docname2'])
|
||||
assert index._titles == {'docname2': 'title2'}
|
||||
assert index._filenames == {'docname2': 'filename2'}
|
||||
index.prune(['docname1_2', 'docname2_2'])
|
||||
assert index._titles == {'docname1_2': 'title1_2', 'docname2_2': 'title2_2'}
|
||||
assert index._filenames == {'docname1_2': 'filename1_2', 'docname2_2': 'filename2_2'}
|
||||
assert index._mapping == {
|
||||
'ar': {'docname2'},
|
||||
'fermion': {'docname2'},
|
||||
'comment': {'docname2'},
|
||||
'non': {'docname2'},
|
||||
'index': {'docname2'},
|
||||
'test': {'docname2'}
|
||||
'ar': {'docname1_2', 'docname2_2'},
|
||||
'fermion': {'docname1_2', 'docname2_2'},
|
||||
'comment': {'docname1_2', 'docname2_2'},
|
||||
'non': {'docname1_2', 'docname2_2'},
|
||||
'index': {'docname1_2', 'docname2_2'},
|
||||
'test': {'docname1_2', 'docname2_2'}
|
||||
}
|
||||
assert index._title_mapping == {'section_titl': {'docname2'}}
|
||||
assert index._objtypes == {('dummy', 'objtype'): 0}
|
||||
assert index._objnames == {0: ('dummy', 'objtype', 'objtype')}
|
||||
assert index._title_mapping == {'section_titl': {'docname1_2', 'docname2_2'}}
|
||||
assert index._objtypes == {('dummy1', 'objtype1'): 0, ('dummy2', 'objtype1'): 1}
|
||||
assert index._objnames == {0: ('dummy1', 'objtype1', 'objtype1'), 1: ('dummy2', 'objtype1', 'objtype1')}
|
||||
|
||||
# freeze after prune
|
||||
assert index.freeze() == {
|
||||
'docnames': ('docname2',),
|
||||
'docnames': ('docname1_2', 'docname2_2'),
|
||||
'envversion': '1.0',
|
||||
'filenames': ['filename2'],
|
||||
'filenames': ['filename1_2', 'filename2_2'],
|
||||
'objects': {},
|
||||
'objnames': {0: ('dummy', 'objtype', 'objtype')},
|
||||
'objtypes': {0: 'dummy:objtype'},
|
||||
'terms': {'ar': 0,
|
||||
'comment': 0,
|
||||
'fermion': 0,
|
||||
'index': 0,
|
||||
'non': 0,
|
||||
'test': 0},
|
||||
'titles': ('title2',),
|
||||
'titleterms': {'section_titl': 0}
|
||||
'objnames': {0: ('dummy1', 'objtype1', 'objtype1'), 1: ('dummy2', 'objtype1', 'objtype1')},
|
||||
'objtypes': {0: 'dummy1:objtype1', 1: 'dummy2:objtype1'},
|
||||
'terms': {'ar': [0, 1],
|
||||
'comment': [0, 1],
|
||||
'fermion': [0, 1],
|
||||
'index': [0, 1],
|
||||
'non': [0, 1],
|
||||
'test': [0, 1]},
|
||||
'titles': ('title1_2', 'title2_2'),
|
||||
'titleterms': {'section_titl': [0, 1]}
|
||||
}
|
||||
assert index._objtypes == {('dummy', 'objtype'): 0}
|
||||
assert index._objnames == {0: ('dummy', 'objtype', 'objtype')}
|
||||
assert index._objtypes == {('dummy1', 'objtype1'): 0, ('dummy2', 'objtype1'): 1}
|
||||
assert index._objnames == {0: ('dummy1', 'objtype1', 'objtype1'),
|
||||
1: ('dummy2', 'objtype1', 'objtype1')}
|
||||
|
||||
|
||||
def test_IndexBuilder_lookup():
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from html5lib import HTMLParser
|
||||
|
||||
|
||||
@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True)
|
||||
@@ -19,6 +20,24 @@ def test_basic(app, status, warning):
|
||||
assert '<p>– “Sphinx” is a tool that makes it easy …</p>' in content
|
||||
|
||||
|
||||
@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True)
|
||||
def test_literals(app, status, warning):
|
||||
app.build()
|
||||
|
||||
with (app.outdir / 'literals.html').open() as html_file:
|
||||
etree = HTMLParser(namespaceHTMLElements=False).parse(html_file)
|
||||
|
||||
for code_element in etree.iter('code'):
|
||||
code_text = ''.join(code_element.itertext())
|
||||
|
||||
if code_text.startswith('code role'):
|
||||
assert "'quotes'" in code_text
|
||||
elif code_text.startswith('{'):
|
||||
assert code_text == "{'code': 'role', 'with': 'quotes'}"
|
||||
elif code_text.startswith('literal'):
|
||||
assert code_text == "literal with 'quotes'"
|
||||
|
||||
|
||||
@pytest.mark.sphinx(buildername='text', testroot='smartquotes', freshenv=True)
|
||||
def test_text_builder(app, status, warning):
|
||||
app.build()
|
||||
|
||||
@@ -26,7 +26,7 @@ def test_separate_metadata():
|
||||
assert docstring == ':param baz:\n'
|
||||
assert metadata == {'foo': 'bar'}
|
||||
|
||||
# field_list like text following just after paragaph is not a field_list
|
||||
# field_list like text following just after paragraph is not a field_list
|
||||
text = ("blah blah blah\n"
|
||||
":meta foo: bar\n"
|
||||
":meta baz:\n")
|
||||
|
||||
@@ -45,7 +45,7 @@ def test_SphinxFileOutput(tmpdir):
|
||||
output.write(content)
|
||||
os.utime(filename, (0, 0))
|
||||
|
||||
# overrite it again
|
||||
# overwrite it again
|
||||
output.write(content)
|
||||
assert os.stat(filename).st_mtime != 0 # updated
|
||||
|
||||
@@ -55,11 +55,11 @@ def test_SphinxFileOutput(tmpdir):
|
||||
output.write(content)
|
||||
os.utime(filename, (0, 0))
|
||||
|
||||
# overrite it again
|
||||
# overwrite it again
|
||||
output.write(content)
|
||||
assert os.stat(filename).st_mtime == 0 # not updated
|
||||
|
||||
# overrite it again (content changed)
|
||||
# overwrite it again (content changed)
|
||||
output.write(content + "; content change")
|
||||
assert os.stat(filename).st_mtime != 0 # updated
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class DummyTemplateLoader(BuiltinTemplateLoader):
|
||||
super().__init__()
|
||||
builder = mock.Mock()
|
||||
builder.config.templates_path = []
|
||||
builder.app.translater = None
|
||||
builder.app.translator = None
|
||||
self.init(builder)
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ import sys
|
||||
import types
|
||||
from inspect import Parameter
|
||||
|
||||
import _testcapi
|
||||
import pytest
|
||||
|
||||
from sphinx.util import inspect
|
||||
@@ -215,11 +214,8 @@ def test_signature_annotations():
|
||||
|
||||
# optional union
|
||||
sig = inspect.signature(f20)
|
||||
if sys.version_info < (3, 7):
|
||||
assert stringify_signature(sig) in ('() -> Optional[Union[int, str]]',
|
||||
'() -> Optional[Union[str, int]]')
|
||||
else:
|
||||
assert stringify_signature(sig) == '() -> Optional[Union[int, str]]'
|
||||
assert stringify_signature(sig) in ('() -> Optional[Union[int, str]]',
|
||||
'() -> Optional[Union[str, int]]')
|
||||
|
||||
# Any
|
||||
sig = inspect.signature(f14)
|
||||
@@ -263,6 +259,10 @@ def test_signature_annotations():
|
||||
sig = inspect.signature(f7)
|
||||
assert stringify_signature(sig, show_return_annotation=False) == '(x: Optional[int] = None, y: dict = {})'
|
||||
|
||||
# unqualified_typehints is True
|
||||
sig = inspect.signature(f7)
|
||||
assert stringify_signature(sig, unqualified_typehints=True) == '(x: ~typing.Optional[int] = None, y: dict = {}) -> None'
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
|
||||
@pytest.mark.sphinx(testroot='ext-autodoc')
|
||||
@@ -630,8 +630,6 @@ def test_isattributedescriptor(app):
|
||||
def __get__(self, obj, typ=None):
|
||||
pass
|
||||
|
||||
testinstancemethod = _testcapi.instancemethod(str.__repr__)
|
||||
|
||||
assert inspect.isattributedescriptor(Base.prop) is True # property
|
||||
assert inspect.isattributedescriptor(Base.meth) is False # method
|
||||
assert inspect.isattributedescriptor(Base.staticmeth) is False # staticmethod
|
||||
@@ -642,7 +640,16 @@ def test_isattributedescriptor(app):
|
||||
assert inspect.isattributedescriptor(dict.__dict__['fromkeys']) is False # ClassMethodDescriptorType # NOQA
|
||||
assert inspect.isattributedescriptor(types.FrameType.f_locals) is True # GetSetDescriptorType # NOQA
|
||||
assert inspect.isattributedescriptor(datetime.timedelta.days) is True # MemberDescriptorType # NOQA
|
||||
assert inspect.isattributedescriptor(testinstancemethod) is False # instancemethod (C-API) # NOQA
|
||||
|
||||
try:
|
||||
# _testcapi module cannot be importable in some distro
|
||||
# refs: https://github.com/sphinx-doc/sphinx/issues/9868
|
||||
import _testcapi
|
||||
|
||||
testinstancemethod = _testcapi.instancemethod(str.__repr__)
|
||||
assert inspect.isattributedescriptor(testinstancemethod) is False # instancemethod (C-API) # NOQA
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def test_isproperty(app):
|
||||
@@ -680,6 +687,25 @@ def test_unpartial():
|
||||
assert inspect.unpartial(func3) is func1
|
||||
|
||||
|
||||
def test_getdoc_inherited_classmethod():
|
||||
class Foo:
|
||||
@classmethod
|
||||
def meth(self):
|
||||
"""
|
||||
docstring
|
||||
indented text
|
||||
"""
|
||||
|
||||
class Bar(Foo):
|
||||
@classmethod
|
||||
def meth(self):
|
||||
# inherited classmethod
|
||||
pass
|
||||
|
||||
assert inspect.getdoc(Bar.meth, getattr, False, Bar, "meth") is None
|
||||
assert inspect.getdoc(Bar.meth, getattr, True, Bar, "meth") == Foo.meth.__doc__
|
||||
|
||||
|
||||
def test_getdoc_inherited_decorated_method():
|
||||
class Foo:
|
||||
def meth(self):
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
|
||||
import codecs
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
from docutils import nodes
|
||||
@@ -131,6 +129,7 @@ def test_is_suppressed_warning():
|
||||
assert is_suppressed_warning("ref", "option", suppress_warnings) is True
|
||||
assert is_suppressed_warning("files", "image", suppress_warnings) is True
|
||||
assert is_suppressed_warning("files", "stylesheet", suppress_warnings) is True
|
||||
assert is_suppressed_warning("rest", None, suppress_warnings) is False
|
||||
assert is_suppressed_warning("rest", "syntax", suppress_warnings) is False
|
||||
assert is_suppressed_warning("rest", "duplicated_labels", suppress_warnings) is True
|
||||
|
||||
@@ -143,33 +142,39 @@ def test_suppress_warnings(app, status, warning):
|
||||
|
||||
app.config.suppress_warnings = []
|
||||
warning.truncate(0)
|
||||
logger.warning('message0', type='test')
|
||||
logger.warning('message1', type='test', subtype='logging')
|
||||
logger.warning('message2', type='test', subtype='crash')
|
||||
logger.warning('message3', type='actual', subtype='logging')
|
||||
assert 'message0' in warning.getvalue()
|
||||
assert 'message1' in warning.getvalue()
|
||||
assert 'message2' in warning.getvalue()
|
||||
assert 'message3' in warning.getvalue()
|
||||
assert app._warncount == 3
|
||||
assert app._warncount == 4
|
||||
|
||||
app.config.suppress_warnings = ['test']
|
||||
warning.truncate(0)
|
||||
logger.warning('message0', type='test')
|
||||
logger.warning('message1', type='test', subtype='logging')
|
||||
logger.warning('message2', type='test', subtype='crash')
|
||||
logger.warning('message3', type='actual', subtype='logging')
|
||||
assert 'message0' not in warning.getvalue()
|
||||
assert 'message1' not in warning.getvalue()
|
||||
assert 'message2' not in warning.getvalue()
|
||||
assert 'message3' in warning.getvalue()
|
||||
assert app._warncount == 4
|
||||
assert app._warncount == 5
|
||||
|
||||
app.config.suppress_warnings = ['test.logging']
|
||||
warning.truncate(0)
|
||||
logger.warning('message0', type='test')
|
||||
logger.warning('message1', type='test', subtype='logging')
|
||||
logger.warning('message2', type='test', subtype='crash')
|
||||
logger.warning('message3', type='actual', subtype='logging')
|
||||
assert 'message0' in warning.getvalue()
|
||||
assert 'message1' not in warning.getvalue()
|
||||
assert 'message2' in warning.getvalue()
|
||||
assert 'message3' in warning.getvalue()
|
||||
assert app._warncount == 6
|
||||
assert app._warncount == 8
|
||||
|
||||
|
||||
def test_warningiserror(app, status, warning):
|
||||
@@ -272,7 +277,7 @@ def test_pending_warnings(app, status, warning):
|
||||
|
||||
logger.warning('message1')
|
||||
with logging.pending_warnings():
|
||||
# not logged yet (bufferred) in here
|
||||
# not logged yet (buffered) in here
|
||||
logger.warning('message2')
|
||||
logger.warning('message3')
|
||||
assert 'WARNING: message1' in warning.getvalue()
|
||||
@@ -311,8 +316,6 @@ def test_colored_logs(app, status, warning):
|
||||
|
||||
|
||||
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||
@pytest.mark.xfail(platform.system() == 'Darwin' and sys.version_info > (3, 8),
|
||||
reason="Not working on macOS and py38")
|
||||
def test_logging_in_ParallelTasks(app, status, warning):
|
||||
logging.setup(app, status, warning)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -60,31 +60,31 @@ def test_NodeMatcher():
|
||||
|
||||
# search by node class
|
||||
matcher = NodeMatcher(nodes.paragraph)
|
||||
assert len(doctree.traverse(matcher)) == 3
|
||||
assert len(list(doctree.traverse(matcher))) == 3
|
||||
|
||||
# search by multiple node classes
|
||||
matcher = NodeMatcher(nodes.paragraph, nodes.literal_block)
|
||||
assert len(doctree.traverse(matcher)) == 4
|
||||
assert len(list(doctree.traverse(matcher))) == 4
|
||||
|
||||
# search by node attribute
|
||||
matcher = NodeMatcher(block=1)
|
||||
assert len(doctree.traverse(matcher)) == 1
|
||||
assert len(list(doctree.traverse(matcher))) == 1
|
||||
|
||||
# search by node attribute (Any)
|
||||
matcher = NodeMatcher(block=Any)
|
||||
assert len(doctree.traverse(matcher)) == 3
|
||||
assert len(list(doctree.traverse(matcher))) == 3
|
||||
|
||||
# search by both class and attribute
|
||||
matcher = NodeMatcher(nodes.paragraph, block=Any)
|
||||
assert len(doctree.traverse(matcher)) == 2
|
||||
assert len(list(doctree.traverse(matcher))) == 2
|
||||
|
||||
# mismatched
|
||||
matcher = NodeMatcher(nodes.title)
|
||||
assert len(doctree.traverse(matcher)) == 0
|
||||
assert len(list(doctree.traverse(matcher))) == 0
|
||||
|
||||
# search with Any does not match to Text node
|
||||
matcher = NodeMatcher(blah=Any)
|
||||
assert len(doctree.traverse(matcher)) == 0
|
||||
assert len(list(doctree.traverse(matcher))) == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
@@ -17,6 +17,7 @@ from typing import (Any, Callable, Dict, Generator, List, NewType, Optional, Tup
|
||||
|
||||
import pytest
|
||||
|
||||
from sphinx.ext.autodoc import mock
|
||||
from sphinx.util.typing import restify, stringify
|
||||
|
||||
|
||||
@@ -41,65 +42,70 @@ class BrokenType:
|
||||
|
||||
|
||||
def test_restify():
|
||||
assert restify(int) == ":class:`int`"
|
||||
assert restify(str) == ":class:`str`"
|
||||
assert restify(None) == ":obj:`None`"
|
||||
assert restify(Integral) == ":class:`numbers.Integral`"
|
||||
assert restify(Struct) == ":class:`struct.Struct`"
|
||||
assert restify(TracebackType) == ":class:`types.TracebackType`"
|
||||
assert restify(Any) == ":obj:`~typing.Any`"
|
||||
assert restify(int) == ":py:class:`int`"
|
||||
assert restify(str) == ":py:class:`str`"
|
||||
assert restify(None) == ":py:obj:`None`"
|
||||
assert restify(Integral) == ":py:class:`numbers.Integral`"
|
||||
assert restify(Struct) == ":py:class:`struct.Struct`"
|
||||
assert restify(TracebackType) == ":py:class:`types.TracebackType`"
|
||||
assert restify(Any) == ":py:obj:`~typing.Any`"
|
||||
assert restify('str') == "str"
|
||||
|
||||
|
||||
def test_restify_type_hints_containers():
|
||||
assert restify(List) == ":class:`~typing.List`"
|
||||
assert restify(Dict) == ":class:`~typing.Dict`"
|
||||
assert restify(List[int]) == ":class:`~typing.List`\\ [:class:`int`]"
|
||||
assert restify(List[str]) == ":class:`~typing.List`\\ [:class:`str`]"
|
||||
assert restify(Dict[str, float]) == (":class:`~typing.Dict`\\ "
|
||||
"[:class:`str`, :class:`float`]")
|
||||
assert restify(Tuple[str, str, str]) == (":class:`~typing.Tuple`\\ "
|
||||
"[:class:`str`, :class:`str`, :class:`str`]")
|
||||
assert restify(Tuple[str, ...]) == ":class:`~typing.Tuple`\\ [:class:`str`, ...]"
|
||||
assert restify(List[Dict[str, Tuple]]) == (":class:`~typing.List`\\ "
|
||||
"[:class:`~typing.Dict`\\ "
|
||||
"[:class:`str`, :class:`~typing.Tuple`]]")
|
||||
assert restify(MyList[Tuple[int, int]]) == (":class:`tests.test_util_typing.MyList`\\ "
|
||||
"[:class:`~typing.Tuple`\\ "
|
||||
"[:class:`int`, :class:`int`]]")
|
||||
assert restify(Generator[None, None, None]) == (":class:`~typing.Generator`\\ "
|
||||
"[:obj:`None`, :obj:`None`, :obj:`None`]")
|
||||
assert restify(List) == ":py:class:`~typing.List`"
|
||||
assert restify(Dict) == ":py:class:`~typing.Dict`"
|
||||
assert restify(List[int]) == ":py:class:`~typing.List`\\ [:py:class:`int`]"
|
||||
assert restify(List[str]) == ":py:class:`~typing.List`\\ [:py:class:`str`]"
|
||||
assert restify(Dict[str, float]) == (":py:class:`~typing.Dict`\\ "
|
||||
"[:py:class:`str`, :py:class:`float`]")
|
||||
assert restify(Tuple[str, str, str]) == (":py:class:`~typing.Tuple`\\ "
|
||||
"[:py:class:`str`, :py:class:`str`, "
|
||||
":py:class:`str`]")
|
||||
assert restify(Tuple[str, ...]) == ":py:class:`~typing.Tuple`\\ [:py:class:`str`, ...]"
|
||||
assert restify(Tuple[()]) == ":py:class:`~typing.Tuple`\\ [()]"
|
||||
assert restify(List[Dict[str, Tuple]]) == (":py:class:`~typing.List`\\ "
|
||||
"[:py:class:`~typing.Dict`\\ "
|
||||
"[:py:class:`str`, :py:class:`~typing.Tuple`]]")
|
||||
assert restify(MyList[Tuple[int, int]]) == (":py:class:`tests.test_util_typing.MyList`\\ "
|
||||
"[:py:class:`~typing.Tuple`\\ "
|
||||
"[:py:class:`int`, :py:class:`int`]]")
|
||||
assert restify(Generator[None, None, None]) == (":py:class:`~typing.Generator`\\ "
|
||||
"[:py:obj:`None`, :py:obj:`None`, "
|
||||
":py:obj:`None`]")
|
||||
|
||||
|
||||
def test_restify_type_hints_Callable():
|
||||
assert restify(Callable) == ":class:`~typing.Callable`"
|
||||
assert restify(Callable) == ":py:class:`~typing.Callable`"
|
||||
|
||||
if sys.version_info >= (3, 7):
|
||||
assert restify(Callable[[str], int]) == (":class:`~typing.Callable`\\ "
|
||||
"[[:class:`str`], :class:`int`]")
|
||||
assert restify(Callable[..., int]) == (":class:`~typing.Callable`\\ "
|
||||
"[[...], :class:`int`]")
|
||||
assert restify(Callable[[str], int]) == (":py:class:`~typing.Callable`\\ "
|
||||
"[[:py:class:`str`], :py:class:`int`]")
|
||||
assert restify(Callable[..., int]) == (":py:class:`~typing.Callable`\\ "
|
||||
"[[...], :py:class:`int`]")
|
||||
else:
|
||||
assert restify(Callable[[str], int]) == (":class:`~typing.Callable`\\ "
|
||||
"[:class:`str`, :class:`int`]")
|
||||
assert restify(Callable[..., int]) == (":class:`~typing.Callable`\\ "
|
||||
"[..., :class:`int`]")
|
||||
assert restify(Callable[[str], int]) == (":py:class:`~typing.Callable`\\ "
|
||||
"[:py:class:`str`, :py:class:`int`]")
|
||||
assert restify(Callable[..., int]) == (":py:class:`~typing.Callable`\\ "
|
||||
"[..., :py:class:`int`]")
|
||||
|
||||
|
||||
def test_restify_type_hints_Union():
|
||||
assert restify(Optional[int]) == ":obj:`~typing.Optional`\\ [:class:`int`]"
|
||||
assert restify(Union[str, None]) == ":obj:`~typing.Optional`\\ [:class:`str`]"
|
||||
assert restify(Union[int, str]) == ":obj:`~typing.Union`\\ [:class:`int`, :class:`str`]"
|
||||
assert restify(Optional[int]) == ":py:obj:`~typing.Optional`\\ [:py:class:`int`]"
|
||||
assert restify(Union[str, None]) == ":py:obj:`~typing.Optional`\\ [:py:class:`str`]"
|
||||
assert restify(Union[int, str]) == (":py:obj:`~typing.Union`\\ "
|
||||
"[:py:class:`int`, :py:class:`str`]")
|
||||
|
||||
if sys.version_info >= (3, 7):
|
||||
assert restify(Union[int, Integral]) == (":obj:`~typing.Union`\\ "
|
||||
"[:class:`int`, :class:`numbers.Integral`]")
|
||||
assert restify(Union[int, Integral]) == (":py:obj:`~typing.Union`\\ "
|
||||
"[:py:class:`int`, :py:class:`numbers.Integral`]")
|
||||
assert (restify(Union[MyClass1, MyClass2]) ==
|
||||
(":obj:`~typing.Union`\\ "
|
||||
"[:class:`tests.test_util_typing.MyClass1`, "
|
||||
":class:`tests.test_util_typing.<MyClass2>`]"))
|
||||
(":py:obj:`~typing.Union`\\ "
|
||||
"[:py:class:`tests.test_util_typing.MyClass1`, "
|
||||
":py:class:`tests.test_util_typing.<MyClass2>`]"))
|
||||
else:
|
||||
assert restify(Union[int, Integral]) == ":class:`numbers.Integral`"
|
||||
assert restify(Union[MyClass1, MyClass2]) == ":class:`tests.test_util_typing.MyClass1`"
|
||||
assert restify(Union[int, Integral]) == ":py:class:`numbers.Integral`"
|
||||
assert restify(Union[MyClass1, MyClass2]) == ":py:class:`tests.test_util_typing.MyClass1`"
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.')
|
||||
@@ -108,108 +114,220 @@ def test_restify_type_hints_typevars():
|
||||
T_co = TypeVar('T_co', covariant=True)
|
||||
T_contra = TypeVar('T_contra', contravariant=True)
|
||||
|
||||
assert restify(T) == ":obj:`tests.test_util_typing.T`"
|
||||
assert restify(T_co) == ":obj:`tests.test_util_typing.T_co`"
|
||||
assert restify(T_contra) == ":obj:`tests.test_util_typing.T_contra`"
|
||||
assert restify(List[T]) == ":class:`~typing.List`\\ [:obj:`tests.test_util_typing.T`]"
|
||||
assert restify(MyInt) == ":class:`MyInt`"
|
||||
assert restify(T) == ":py:obj:`tests.test_util_typing.T`"
|
||||
assert restify(T_co) == ":py:obj:`tests.test_util_typing.T_co`"
|
||||
assert restify(T_contra) == ":py:obj:`tests.test_util_typing.T_contra`"
|
||||
assert restify(List[T]) == ":py:class:`~typing.List`\\ [:py:obj:`tests.test_util_typing.T`]"
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
assert restify(MyInt) == ":py:class:`tests.test_util_typing.MyInt`"
|
||||
else:
|
||||
assert restify(MyInt) == ":py:class:`MyInt`"
|
||||
|
||||
|
||||
def test_restify_type_hints_custom_class():
|
||||
assert restify(MyClass1) == ":class:`tests.test_util_typing.MyClass1`"
|
||||
assert restify(MyClass2) == ":class:`tests.test_util_typing.<MyClass2>`"
|
||||
assert restify(MyClass1) == ":py:class:`tests.test_util_typing.MyClass1`"
|
||||
assert restify(MyClass2) == ":py:class:`tests.test_util_typing.<MyClass2>`"
|
||||
|
||||
|
||||
def test_restify_type_hints_alias():
|
||||
MyStr = str
|
||||
MyTuple = Tuple[str, str]
|
||||
assert restify(MyStr) == ":class:`str`"
|
||||
assert restify(MyTuple) == ":class:`~typing.Tuple`\\ [:class:`str`, :class:`str`]"
|
||||
assert restify(MyStr) == ":py:class:`str`"
|
||||
assert restify(MyTuple) == ":py:class:`~typing.Tuple`\\ [:py:class:`str`, :py:class:`str`]"
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.')
|
||||
def test_restify_type_ForwardRef():
|
||||
from typing import ForwardRef # type: ignore
|
||||
assert restify(ForwardRef("myint")) == ":class:`myint`"
|
||||
assert restify(ForwardRef("myint")) == ":py:class:`myint`"
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
|
||||
def test_restify_type_Literal():
|
||||
from typing import Literal # type: ignore
|
||||
assert restify(Literal[1, "2", "\r"]) == ":obj:`~typing.Literal`\\ [1, '2', '\\r']"
|
||||
assert restify(Literal[1, "2", "\r"]) == ":py:obj:`~typing.Literal`\\ [1, '2', '\\r']"
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 9), reason='python 3.9+ is required.')
|
||||
def test_restify_pep_585():
|
||||
assert restify(list[str]) == ":py:class:`list`\\ [:py:class:`str`]" # type: ignore
|
||||
assert restify(dict[str, str]) == (":py:class:`dict`\\ " # type: ignore
|
||||
"[:py:class:`str`, :py:class:`str`]")
|
||||
assert restify(dict[str, tuple[int, ...]]) == (":py:class:`dict`\\ " # type: ignore
|
||||
"[:py:class:`str`, :py:class:`tuple`\\ "
|
||||
"[:py:class:`int`, ...]]")
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 10), reason='python 3.10+ is required.')
|
||||
def test_restify_type_union_operator():
|
||||
assert restify(int | None) == "Optional[:class:`int`]" # type: ignore
|
||||
assert restify(int | str) == ":class:`int` | :class:`str`" # type: ignore
|
||||
assert restify(int | str | None) == "Optional[:class:`int` | :class:`str`]" # type: ignore
|
||||
assert restify(int | None) == ":py:class:`int` | :py:obj:`None`" # type: ignore
|
||||
assert restify(int | str) == ":py:class:`int` | :py:class:`str`" # type: ignore
|
||||
assert restify(int | str | None) == (":py:class:`int` | :py:class:`str` | " # type: ignore
|
||||
":py:obj:`None`")
|
||||
|
||||
|
||||
def test_restify_broken_type_hints():
|
||||
assert restify(BrokenType) == ':class:`tests.test_util_typing.BrokenType`'
|
||||
assert restify(BrokenType) == ':py:class:`tests.test_util_typing.BrokenType`'
|
||||
|
||||
|
||||
def test_restify_mock():
|
||||
with mock(['unknown']):
|
||||
import unknown
|
||||
assert restify(unknown.secret.Class) == ':py:class:`unknown.secret.Class`'
|
||||
|
||||
|
||||
def test_stringify():
|
||||
assert stringify(int) == "int"
|
||||
assert stringify(str) == "str"
|
||||
assert stringify(None) == "None"
|
||||
assert stringify(Integral) == "numbers.Integral"
|
||||
assert stringify(Struct) == "struct.Struct"
|
||||
assert stringify(TracebackType) == "types.TracebackType"
|
||||
assert stringify(Any) == "Any"
|
||||
assert stringify(int, False) == "int"
|
||||
assert stringify(int, True) == "int"
|
||||
|
||||
assert stringify(str, False) == "str"
|
||||
assert stringify(str, True) == "str"
|
||||
|
||||
assert stringify(None, False) == "None"
|
||||
assert stringify(None, True) == "None"
|
||||
|
||||
assert stringify(Integral, False) == "numbers.Integral"
|
||||
assert stringify(Integral, True) == "~numbers.Integral"
|
||||
|
||||
assert stringify(Struct, False) == "struct.Struct"
|
||||
assert stringify(Struct, True) == "~struct.Struct"
|
||||
|
||||
assert stringify(TracebackType, False) == "types.TracebackType"
|
||||
assert stringify(TracebackType, True) == "~types.TracebackType"
|
||||
|
||||
assert stringify(Any, False) == "Any"
|
||||
assert stringify(Any, True) == "~typing.Any"
|
||||
|
||||
|
||||
def test_stringify_type_hints_containers():
|
||||
assert stringify(List) == "List"
|
||||
assert stringify(Dict) == "Dict"
|
||||
assert stringify(List[int]) == "List[int]"
|
||||
assert stringify(List[str]) == "List[str]"
|
||||
assert stringify(Dict[str, float]) == "Dict[str, float]"
|
||||
assert stringify(Tuple[str, str, str]) == "Tuple[str, str, str]"
|
||||
assert stringify(Tuple[str, ...]) == "Tuple[str, ...]"
|
||||
assert stringify(List[Dict[str, Tuple]]) == "List[Dict[str, Tuple]]"
|
||||
assert stringify(MyList[Tuple[int, int]]) == "tests.test_util_typing.MyList[Tuple[int, int]]"
|
||||
assert stringify(Generator[None, None, None]) == "Generator[None, None, None]"
|
||||
assert stringify(List, False) == "List"
|
||||
assert stringify(List, True) == "~typing.List"
|
||||
|
||||
assert stringify(Dict, False) == "Dict"
|
||||
assert stringify(Dict, True) == "~typing.Dict"
|
||||
|
||||
assert stringify(List[int], False) == "List[int]"
|
||||
assert stringify(List[int], True) == "~typing.List[int]"
|
||||
|
||||
assert stringify(List[str], False) == "List[str]"
|
||||
assert stringify(List[str], True) == "~typing.List[str]"
|
||||
|
||||
assert stringify(Dict[str, float], False) == "Dict[str, float]"
|
||||
assert stringify(Dict[str, float], True) == "~typing.Dict[str, float]"
|
||||
|
||||
assert stringify(Tuple[str, str, str], False) == "Tuple[str, str, str]"
|
||||
assert stringify(Tuple[str, str, str], True) == "~typing.Tuple[str, str, str]"
|
||||
|
||||
assert stringify(Tuple[str, ...], False) == "Tuple[str, ...]"
|
||||
assert stringify(Tuple[str, ...], True) == "~typing.Tuple[str, ...]"
|
||||
|
||||
assert stringify(Tuple[()], False) == "Tuple[()]"
|
||||
assert stringify(Tuple[()], True) == "~typing.Tuple[()]"
|
||||
|
||||
assert stringify(List[Dict[str, Tuple]], False) == "List[Dict[str, Tuple]]"
|
||||
assert stringify(List[Dict[str, Tuple]], True) == "~typing.List[~typing.Dict[str, ~typing.Tuple]]"
|
||||
|
||||
assert stringify(MyList[Tuple[int, int]], False) == "tests.test_util_typing.MyList[Tuple[int, int]]"
|
||||
assert stringify(MyList[Tuple[int, int]], True) == "~tests.test_util_typing.MyList[~typing.Tuple[int, int]]"
|
||||
|
||||
assert stringify(Generator[None, None, None], False) == "Generator[None, None, None]"
|
||||
assert stringify(Generator[None, None, None], True) == "~typing.Generator[None, None, None]"
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 9), reason='python 3.9+ is required.')
|
||||
def test_stringify_type_hints_pep_585():
|
||||
assert stringify(list[int], False) == "list[int]"
|
||||
assert stringify(list[int], True) == "list[int]"
|
||||
|
||||
assert stringify(list[str], False) == "list[str]"
|
||||
assert stringify(list[str], True) == "list[str]"
|
||||
|
||||
assert stringify(dict[str, float], False) == "dict[str, float]"
|
||||
assert stringify(dict[str, float], True) == "dict[str, float]"
|
||||
|
||||
assert stringify(tuple[str, str, str], False) == "tuple[str, str, str]"
|
||||
assert stringify(tuple[str, str, str], True) == "tuple[str, str, str]"
|
||||
|
||||
assert stringify(tuple[str, ...], False) == "tuple[str, ...]"
|
||||
assert stringify(tuple[str, ...], True) == "tuple[str, ...]"
|
||||
|
||||
assert stringify(tuple[()], False) == "tuple[()]"
|
||||
assert stringify(tuple[()], True) == "tuple[()]"
|
||||
|
||||
assert stringify(list[dict[str, tuple]], False) == "list[dict[str, tuple]]"
|
||||
assert stringify(list[dict[str, tuple]], True) == "list[dict[str, tuple]]"
|
||||
|
||||
assert stringify(type[int], False) == "type[int]"
|
||||
assert stringify(type[int], True) == "type[int]"
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 9), reason='python 3.9+ is required.')
|
||||
def test_stringify_Annotated():
|
||||
from typing import Annotated # type: ignore
|
||||
assert stringify(Annotated[str, "foo", "bar"]) == "str" # NOQA
|
||||
assert stringify(Annotated[str, "foo", "bar"], False) == "str" # NOQA
|
||||
assert stringify(Annotated[str, "foo", "bar"], True) == "str" # NOQA
|
||||
|
||||
|
||||
def test_stringify_type_hints_string():
|
||||
assert stringify("int") == "int"
|
||||
assert stringify("str") == "str"
|
||||
assert stringify(List["int"]) == "List[int]"
|
||||
assert stringify("Tuple[str]") == "Tuple[str]"
|
||||
assert stringify("unknown") == "unknown"
|
||||
assert stringify("int", False) == "int"
|
||||
assert stringify("int", True) == "int"
|
||||
|
||||
assert stringify("str", False) == "str"
|
||||
assert stringify("str", True) == "str"
|
||||
|
||||
assert stringify(List["int"], False) == "List[int]"
|
||||
assert stringify(List["int"], True) == "~typing.List[int]"
|
||||
|
||||
assert stringify("Tuple[str]", False) == "Tuple[str]"
|
||||
assert stringify("Tuple[str]", True) == "Tuple[str]"
|
||||
|
||||
assert stringify("unknown", False) == "unknown"
|
||||
assert stringify("unknown", True) == "unknown"
|
||||
|
||||
|
||||
def test_stringify_type_hints_Callable():
|
||||
assert stringify(Callable) == "Callable"
|
||||
assert stringify(Callable, False) == "Callable"
|
||||
assert stringify(Callable, True) == "~typing.Callable"
|
||||
|
||||
if sys.version_info >= (3, 7):
|
||||
assert stringify(Callable[[str], int]) == "Callable[[str], int]"
|
||||
assert stringify(Callable[..., int]) == "Callable[[...], int]"
|
||||
assert stringify(Callable[[str], int], False) == "Callable[[str], int]"
|
||||
assert stringify(Callable[[str], int], True) == "~typing.Callable[[str], int]"
|
||||
|
||||
assert stringify(Callable[..., int], False) == "Callable[[...], int]"
|
||||
assert stringify(Callable[..., int], True) == "~typing.Callable[[...], int]"
|
||||
else:
|
||||
assert stringify(Callable[[str], int]) == "Callable[str, int]"
|
||||
assert stringify(Callable[..., int]) == "Callable[..., int]"
|
||||
assert stringify(Callable[[str], int], False) == "Callable[str, int]"
|
||||
assert stringify(Callable[[str], int], True) == "~typing.Callable[str, int]"
|
||||
|
||||
assert stringify(Callable[..., int], False) == "Callable[..., int]"
|
||||
assert stringify(Callable[..., int], True) == "~typing.Callable[..., int]"
|
||||
|
||||
|
||||
def test_stringify_type_hints_Union():
|
||||
assert stringify(Optional[int]) == "Optional[int]"
|
||||
assert stringify(Union[str, None]) == "Optional[str]"
|
||||
assert stringify(Union[int, str]) == "Union[int, str]"
|
||||
assert stringify(Optional[int], False) == "Optional[int]"
|
||||
assert stringify(Optional[int], True) == "~typing.Optional[int]"
|
||||
|
||||
assert stringify(Union[str, None], False) == "Optional[str]"
|
||||
assert stringify(Union[str, None], True) == "~typing.Optional[str]"
|
||||
|
||||
assert stringify(Union[int, str], False) == "Union[int, str]"
|
||||
assert stringify(Union[int, str], True) == "~typing.Union[int, str]"
|
||||
|
||||
if sys.version_info >= (3, 7):
|
||||
assert stringify(Union[int, Integral]) == "Union[int, numbers.Integral]"
|
||||
assert (stringify(Union[MyClass1, MyClass2]) ==
|
||||
assert stringify(Union[int, Integral], False) == "Union[int, numbers.Integral]"
|
||||
assert stringify(Union[int, Integral], True) == "~typing.Union[int, ~numbers.Integral]"
|
||||
|
||||
assert (stringify(Union[MyClass1, MyClass2], False) ==
|
||||
"Union[tests.test_util_typing.MyClass1, tests.test_util_typing.<MyClass2>]")
|
||||
assert (stringify(Union[MyClass1, MyClass2], True) ==
|
||||
"~typing.Union[~tests.test_util_typing.MyClass1, ~tests.test_util_typing.<MyClass2>]")
|
||||
else:
|
||||
assert stringify(Union[int, Integral]) == "numbers.Integral"
|
||||
assert stringify(Union[MyClass1, MyClass2]) == "tests.test_util_typing.MyClass1"
|
||||
assert stringify(Union[int, Integral], False) == "numbers.Integral"
|
||||
assert stringify(Union[int, Integral], True) == "~numbers.Integral"
|
||||
|
||||
assert stringify(Union[MyClass1, MyClass2], False) == "tests.test_util_typing.MyClass1"
|
||||
assert stringify(Union[MyClass1, MyClass2], True) == "~tests.test_util_typing.MyClass1"
|
||||
|
||||
|
||||
def test_stringify_type_hints_typevars():
|
||||
@@ -218,43 +336,83 @@ def test_stringify_type_hints_typevars():
|
||||
T_contra = TypeVar('T_contra', contravariant=True)
|
||||
|
||||
if sys.version_info < (3, 7):
|
||||
assert stringify(T) == "T"
|
||||
assert stringify(T_co) == "T_co"
|
||||
assert stringify(T_contra) == "T_contra"
|
||||
assert stringify(List[T]) == "List[T]"
|
||||
else:
|
||||
assert stringify(T) == "tests.test_util_typing.T"
|
||||
assert stringify(T_co) == "tests.test_util_typing.T_co"
|
||||
assert stringify(T_contra) == "tests.test_util_typing.T_contra"
|
||||
assert stringify(List[T]) == "List[tests.test_util_typing.T]"
|
||||
assert stringify(T, False) == "T"
|
||||
assert stringify(T, True) == "T"
|
||||
|
||||
assert stringify(MyInt) == "MyInt"
|
||||
assert stringify(T_co, False) == "T_co"
|
||||
assert stringify(T_co, True) == "T_co"
|
||||
|
||||
assert stringify(T_contra, False) == "T_contra"
|
||||
assert stringify(T_contra, True) == "T_contra"
|
||||
|
||||
assert stringify(List[T], False) == "List[T]"
|
||||
assert stringify(List[T], True) == "~typing.List[T]"
|
||||
else:
|
||||
assert stringify(T, False) == "tests.test_util_typing.T"
|
||||
assert stringify(T, True) == "~tests.test_util_typing.T"
|
||||
|
||||
assert stringify(T_co, False) == "tests.test_util_typing.T_co"
|
||||
assert stringify(T_co, True) == "~tests.test_util_typing.T_co"
|
||||
|
||||
assert stringify(T_contra, False) == "tests.test_util_typing.T_contra"
|
||||
assert stringify(T_contra, True) == "~tests.test_util_typing.T_contra"
|
||||
|
||||
assert stringify(List[T], False) == "List[tests.test_util_typing.T]"
|
||||
assert stringify(List[T], True) == "~typing.List[~tests.test_util_typing.T]"
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
assert stringify(MyInt, False) == "tests.test_util_typing.MyInt"
|
||||
assert stringify(MyInt, True) == "~tests.test_util_typing.MyInt"
|
||||
else:
|
||||
assert stringify(MyInt, False) == "MyInt"
|
||||
assert stringify(MyInt, True) == "MyInt"
|
||||
|
||||
|
||||
def test_stringify_type_hints_custom_class():
|
||||
assert stringify(MyClass1) == "tests.test_util_typing.MyClass1"
|
||||
assert stringify(MyClass2) == "tests.test_util_typing.<MyClass2>"
|
||||
assert stringify(MyClass1, False) == "tests.test_util_typing.MyClass1"
|
||||
assert stringify(MyClass1, True) == "~tests.test_util_typing.MyClass1"
|
||||
|
||||
assert stringify(MyClass2, False) == "tests.test_util_typing.<MyClass2>"
|
||||
assert stringify(MyClass2, True) == "~tests.test_util_typing.<MyClass2>"
|
||||
|
||||
|
||||
def test_stringify_type_hints_alias():
|
||||
MyStr = str
|
||||
MyTuple = Tuple[str, str]
|
||||
assert stringify(MyStr) == "str"
|
||||
assert stringify(MyTuple) == "Tuple[str, str]" # type: ignore
|
||||
|
||||
assert stringify(MyStr, False) == "str"
|
||||
assert stringify(MyStr, True) == "str"
|
||||
|
||||
assert stringify(MyTuple, False) == "Tuple[str, str]" # type: ignore
|
||||
assert stringify(MyTuple, True) == "~typing.Tuple[str, str]" # type: ignore
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
|
||||
def test_stringify_type_Literal():
|
||||
from typing import Literal # type: ignore
|
||||
assert stringify(Literal[1, "2", "\r"]) == "Literal[1, '2', '\\r']"
|
||||
assert stringify(Literal[1, "2", "\r"], False) == "Literal[1, '2', '\\r']"
|
||||
assert stringify(Literal[1, "2", "\r"], True) == "~typing.Literal[1, '2', '\\r']"
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 10), reason='python 3.10+ is required.')
|
||||
def test_stringify_type_union_operator():
|
||||
assert stringify(int | None) == "Optional[int]" # type: ignore
|
||||
assert stringify(int | str) == "int | str" # type: ignore
|
||||
assert stringify(int | str | None) == "Optional[int | str]" # type: ignore
|
||||
assert stringify(int | None, False) == "int | None" # type: ignore
|
||||
assert stringify(int | None, True) == "int | None" # type: ignore
|
||||
|
||||
assert stringify(int | str, False) == "int | str" # type: ignore
|
||||
assert stringify(int | str, True) == "int | str" # type: ignore
|
||||
|
||||
assert stringify(int | str | None, False) == "int | str | None" # type: ignore
|
||||
assert stringify(int | str | None, True) == "int | str | None" # type: ignore
|
||||
|
||||
|
||||
def test_stringify_broken_type_hints():
|
||||
assert stringify(BrokenType) == 'tests.test_util_typing.BrokenType'
|
||||
assert stringify(BrokenType, False) == 'tests.test_util_typing.BrokenType'
|
||||
assert stringify(BrokenType, True) == '~tests.test_util_typing.BrokenType'
|
||||
|
||||
|
||||
def test_stringify_mock():
|
||||
with mock(['unknown']):
|
||||
import unknown
|
||||
assert stringify(unknown.secret.Class, False) == 'unknown.secret.Class'
|
||||
assert stringify(unknown.secret.Class, True) == 'unknown.secret.Class'
|
||||
|
||||
@@ -11,12 +11,18 @@
|
||||
import pickle
|
||||
|
||||
import pytest
|
||||
from docutils.parsers.rst.directives.html import MetaBody
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.testing.util import SphinxTestApp
|
||||
from sphinx.versioning import add_uids, get_ratio, merge_doctrees
|
||||
|
||||
try:
|
||||
from docutils.nodes import meta
|
||||
except ImportError:
|
||||
# docutils-0.18.0 or older
|
||||
from docutils.parsers.rst.directives.html import MetaBody
|
||||
meta = MetaBody.meta
|
||||
|
||||
app = original = original_uids = None
|
||||
|
||||
|
||||
@@ -64,7 +70,7 @@ def test_picklablility():
|
||||
copy.settings.warning_stream = None
|
||||
copy.settings.env = None
|
||||
copy.settings.record_dependencies = None
|
||||
for metanode in copy.traverse(MetaBody.meta):
|
||||
for metanode in copy.traverse(meta):
|
||||
metanode.__class__ = addnodes.meta
|
||||
loaded = pickle.loads(pickle.dumps(copy, pickle.HIGHEST_PROTOCOL))
|
||||
assert all(getattr(n, 'uid', False) for n in loaded.traverse(is_paragraph))
|
||||
|
||||
Reference in New Issue
Block a user