diff options
| author | Takeshi KOMIYA <i.tkomiya@gmail.com> | 2020-10-24 17:18:12 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-10-24 17:18:12 +0900 |
| commit | 2a77eb07d008e004348ee67a759263754dfc66c0 (patch) | |
| tree | 7fbdafa1fae22a39e6532fc5965551286777352f /tests | |
| parent | de6c32eee61ae94a88773e7866c46014a86f3c9b (diff) | |
| parent | 0476e1cea93b587e3c0ab295aa20b2b9f0f81a34 (diff) | |
| download | sphinx-git-2a77eb07d008e004348ee67a759263754dfc66c0.tar.gz | |
Merge branch '3.x' into 3.2.x_to_3.x
Diffstat (limited to 'tests')
26 files changed, 309 insertions, 37 deletions
diff --git a/tests/roots/test-circular/conf.py b/tests/roots/test-circular/conf.py index 027d21cda..a45d22e28 100644 --- a/tests/roots/test-circular/conf.py +++ b/tests/roots/test-circular/conf.py @@ -1 +1 @@ -exclude_patterns = ['_build']
+exclude_patterns = ['_build'] diff --git a/tests/roots/test-ext-autodoc/target/annotations.py b/tests/roots/test-ext-autodoc/target/annotations.py new file mode 100644 index 000000000..667149b26 --- /dev/null +++ b/tests/roots/test-ext-autodoc/target/annotations.py @@ -0,0 +1,25 @@ +from __future__ import annotations +from typing import overload + + +myint = int + + +def sum(x: myint, y: myint) -> myint: + """docstring""" + return x + y + + +@overload +def mult(x: myint, y: myint) -> myint: + ... + + +@overload +def mult(x: float, y: float) -> float: + ... + + +def mult(x, y): + """docstring""" + return x, y diff --git a/tests/roots/test-ext-autodoc/target/cached_property.py b/tests/roots/test-ext-autodoc/target/cached_property.py new file mode 100644 index 000000000..63ec09f8e --- /dev/null +++ b/tests/roots/test-ext-autodoc/target/cached_property.py @@ -0,0 +1,7 @@ +from functools import cached_property + + +class Foo: + @cached_property + def prop(self) -> int: + return 1 diff --git a/tests/roots/test-ext-autodoc/target/generic_class.py b/tests/roots/test-ext-autodoc/target/generic_class.py new file mode 100644 index 000000000..cf4c5ed37 --- /dev/null +++ b/tests/roots/test-ext-autodoc/target/generic_class.py @@ -0,0 +1,12 @@ +from typing import TypeVar, Generic + + +T = TypeVar('T') + + +# Test that typing.Generic's __new__ method does not mask our class' +# __init__ signature. +class A(Generic[T]): + """docstring for A""" + def __init__(self, a, b=None): + pass diff --git a/tests/roots/test-ext-autodoc/target/overload2.py b/tests/roots/test-ext-autodoc/target/overload2.py new file mode 100644 index 000000000..e901f791b --- /dev/null +++ b/tests/roots/test-ext-autodoc/target/overload2.py @@ -0,0 +1,5 @@ +from target.overload import Bar + + +class Baz(Bar): + pass diff --git a/tests/roots/test-intl/xx/LC_MESSAGES/toctree.po b/tests/roots/test-intl/xx/LC_MESSAGES/toctree.po index 8ca1dc52c..62cccdfc1 100644 --- a/tests/roots/test-intl/xx/LC_MESSAGES/toctree.po +++ b/tests/roots/test-intl/xx/LC_MESSAGES/toctree.po @@ -1,5 +1,5 @@ # SOME DESCRIPTIVE TITLE. -# Copyright (C) +# Copyright (C) # This file is distributed under the same license as the Sphinx intl <Tests> package. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # diff --git a/tests/roots/test-linkcheck-localserver/conf.py b/tests/roots/test-linkcheck-localserver/conf.py new file mode 100644 index 000000000..2ba1f85e8 --- /dev/null +++ b/tests/roots/test-linkcheck-localserver/conf.py @@ -0,0 +1,2 @@ +exclude_patterns = ['_build'] +linkcheck_anchors = True diff --git a/tests/roots/test-linkcheck-localserver/index.rst b/tests/roots/test-linkcheck-localserver/index.rst new file mode 100644 index 000000000..807fe964b --- /dev/null +++ b/tests/roots/test-linkcheck-localserver/index.rst @@ -0,0 +1 @@ +`local server <http://localhost:7777/#anchor>`_ diff --git a/tests/roots/test-numbered-circular/conf.py b/tests/roots/test-numbered-circular/conf.py index 027d21cda..a45d22e28 100644 --- a/tests/roots/test-numbered-circular/conf.py +++ b/tests/roots/test-numbered-circular/conf.py @@ -1 +1 @@ -exclude_patterns = ['_build']
+exclude_patterns = ['_build'] diff --git a/tests/roots/test-theming/MANIFEST.in b/tests/roots/test-theming/MANIFEST.in index 0e977e756..0ace41cb7 100644 --- a/tests/roots/test-theming/MANIFEST.in +++ b/tests/roots/test-theming/MANIFEST.in @@ -1,2 +1,2 @@ -recursive-include test_theme *.conf
-
+recursive-include test_theme *.conf + diff --git a/tests/roots/test-theming/conf.py b/tests/roots/test-theming/conf.py index 062b9cf83..0db7cf035 100644 --- a/tests/roots/test-theming/conf.py +++ b/tests/roots/test-theming/conf.py @@ -1,3 +1,3 @@ -html_theme = 'test-theme'
-html_theme_path = ['.', 'test_theme']
-exclude_patterns = ['_build']
+html_theme = 'test-theme' +html_theme_path = ['.', 'test_theme'] +exclude_patterns = ['_build'] diff --git a/tests/roots/test-theming/index.rst b/tests/roots/test-theming/index.rst index 18a9a4e2f..214dcd7eb 100644 --- a/tests/roots/test-theming/index.rst +++ b/tests/roots/test-theming/index.rst @@ -1,5 +1,5 @@ -=======
-Theming
-=======
-
-
+======= +Theming +======= + + diff --git a/tests/roots/test-theming/setup.py b/tests/roots/test-theming/setup.py index e62007f36..02ee259c0 100644 --- a/tests/roots/test-theming/setup.py +++ b/tests/roots/test-theming/setup.py @@ -1,11 +1,11 @@ -from setuptools import setup, find_packages
-
-setup(
- name='test-theme',
- packages=find_packages(),
- include_package_data=True,
- entry_points="""
- [sphinx_themes]
- path = test_theme:get_path
- """,
-)
+from setuptools import setup, find_packages + +setup( + name='test-theme', + packages=find_packages(), + include_package_data=True, + entry_points=""" + [sphinx_themes] + path = test_theme:get_path + """, +) diff --git a/tests/roots/test-theming/test_theme/__init__.py b/tests/roots/test-theming/test_theme/__init__.py index 398408c5d..13bdc4b2c 100644 --- a/tests/roots/test-theming/test_theme/__init__.py +++ b/tests/roots/test-theming/test_theme/__init__.py @@ -1,5 +1,5 @@ -import os
-
-
-def get_path():
- return os.path.dirname(os.path.abspath(__file__))
+import os + + +def get_path(): + return os.path.dirname(os.path.abspath(__file__)) diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index 1c86b8daa..074602ea9 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -174,3 +174,21 @@ def test_gettext_template_msgid_order_in_sphinxpot(app): 'msgid "This is Template 2\\.".*'), result, flags=re.S) + + +@pytest.mark.sphinx( + 'gettext', srcdir='root-gettext', + confoverrides={'gettext_compact': 'documentation'}) +def test_build_single_pot(app): + app.builder.build_all() + + assert (app.outdir / 'documentation.pot').isfile() + + result = (app.outdir / 'documentation.pot').read_text() + assert re.search( + ('msgid "Todo".*' + 'msgid "Like footnotes.".*' + 'msgid "The minute.".*' + 'msgid "Generated section".*'), + result, + flags=re.S) diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 1efc6c14a..8cd541481 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -421,6 +421,11 @@ def test_html5_output(app, cached_etree_parse, fname, expect): check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect) +@pytest.mark.sphinx('html', parallel=2) +def test_html_parallel(app): + app.build() + + @pytest.mark.skipif(docutils.__version_info__ < (0, 13), reason='docutils-0.13 or above is required') @pytest.mark.sphinx('html') diff --git a/tests/test_build_linkcheck.py b/tests/test_build_linkcheck.py index 7d85f10c5..a78587668 100644 --- a/tests/test_build_linkcheck.py +++ b/tests/test_build_linkcheck.py @@ -8,8 +8,10 @@ :license: BSD, see LICENSE for details. """ +import http.server import json import re +import threading from unittest import mock import pytest @@ -106,6 +108,21 @@ def test_anchors_ignored(app, status, warning): # expect all ok when excluding #top assert not content +@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver', freshenv=True) +def test_raises_for_invalid_status(app, status, warning): + server_thread = HttpServerThread(InternalServerErrorHandler, daemon=True) + server_thread.start() + try: + app.builder.build_all() + finally: + server_thread.terminate() + content = (app.outdir / 'output.txt').read_text() + assert content == ( + "index.rst:1: [broken] http://localhost:7777/#anchor: " + "500 Server Error: Internal Server Error " + "for url: http://localhost:7777/\n" + ) + @pytest.mark.sphinx( 'linkcheck', testroot='linkcheck', freshenv=True, @@ -160,3 +177,22 @@ def test_linkcheck_request_headers(app, status, warning): assert headers["X-Secret"] == "open sesami" else: assert headers["Accept"] == "text/html,application/xhtml+xml;q=0.9,*/*;q=0.8" + + +class HttpServerThread(threading.Thread): + def __init__(self, handler, *args, **kwargs): + super().__init__(*args, **kwargs) + self.server = http.server.HTTPServer(("localhost", 7777), handler) + + def run(self): + self.server.serve_forever(poll_interval=0.01) + + def terminate(self): + self.server.shutdown() + self.server.server_close() + self.join() + + +class InternalServerErrorHandler(http.server.BaseHTTPRequestHandler): + def do_GET(self): + self.send_error(500, "Internal Server Error") diff --git a/tests/test_build_manpage.py b/tests/test_build_manpage.py index c3c41a2ae..d4b1a320e 100644 --- a/tests/test_build_manpage.py +++ b/tests/test_build_manpage.py @@ -30,6 +30,13 @@ def test_all(app, status, warning): assert 'Footnotes' not in content +@pytest.mark.sphinx('man', testroot='basic', + confoverrides={'man_make_section_directory': True}) +def test_man_make_section_directory(app, status, warning): + app.build() + assert (app.outdir / '1' / 'python.1').exists() + + @pytest.mark.sphinx('man', testroot='directive-code') def test_captioned_code_block(app, status, warning): app.builder.build_all() diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index b98f37912..8040af9cc 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -386,6 +386,19 @@ def test_pyfunction_signature_full_py38(app): [desc_parameter, desc_sig_operator, "/"])]) +@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.') +def test_pyfunction_with_number_literals(app): + text = ".. py:function:: hello(age=0x10, height=1_6_0)" + doctree = restructuredtext.parse(app, text) + assert_node(doctree[1][0][1], + [desc_parameterlist, ([desc_parameter, ([desc_sig_name, "age"], + [desc_sig_operator, "="], + [nodes.inline, "0x10"])], + [desc_parameter, ([desc_sig_name, "height"], + [desc_sig_operator, "="], + [nodes.inline, "1_6_0"])])]) + + def test_optional_pyfunction_signature(app): text = ".. py:function:: compile(source [, filename [, symbol]]) -> ast object" doctree = restructuredtext.parse(app, text) diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index 90a2ec95a..9cb54de5b 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -290,6 +290,21 @@ def test_format_signature(app): '(b, c=42, *d, **e)' +@pytest.mark.skipif(sys.version_info < (3, 5), reason='typing is available since python3.5.') +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_process_signature_typing_generic(app): + actual = do_autodoc(app, 'class', 'target.generic_class.A', {}) + + assert list(actual) == [ + '', + '.. py:class:: A(a, b=None)', + ' :module: target.generic_class', + '', + ' docstring for A', + '', + ] + + def test_autodoc_process_signature_typehints(app): captured = [] @@ -881,6 +896,26 @@ def test_autodoc_descriptor(app): ] +@pytest.mark.skipif(sys.version_info < (3, 8), + reason='cached_property is available since python3.8.') +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_cached_property(app): + options = {"members": None, + "undoc-members": True} + actual = do_autodoc(app, 'class', 'target.cached_property.Foo', options) + assert list(actual) == [ + '', + '.. py:class:: Foo()', + ' :module: target.cached_property', + '', + '', + ' .. py:method:: Foo.prop', + ' :module: target.cached_property', + ' :property:', + '', + ] + + @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_member_order(app): # case member-order='bysource' @@ -1968,6 +2003,22 @@ def test_overload(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_overload2(app): + options = {"members": None} + actual = do_autodoc(app, 'module', 'target.overload2', options) + assert list(actual) == [ + '', + '.. py:module:: target.overload2', + '', + '', + '.. py:class:: Baz(x: int, y: int)', + ' Baz(x: str, y: str)', + ' :module: target.overload2', + '', + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') def test_pymodule_for_ModuleLevelDocumenter(app): app.env.ref_context['py:module'] = 'target.classes' actual = do_autodoc(app, 'class', 'Foo') diff --git a/tests/test_ext_autodoc_configs.py b/tests/test_ext_autodoc_configs.py index 3d0bfd395..7d51b7f0e 100644 --- a/tests/test_ext_autodoc_configs.py +++ b/tests/test_ext_autodoc_configs.py @@ -642,6 +642,54 @@ def test_autodoc_typehints_description_for_invalid_node(app): restructuredtext.parse(app, text) # raises no error +@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.') +@pytest.mark.sphinx('text', testroot='ext-autodoc') +def test_autodoc_type_aliases(app): + # default + options = {"members": None} + actual = do_autodoc(app, 'module', 'target.annotations', options) + assert list(actual) == [ + '', + '.. py:module:: target.annotations', + '', + '', + '.. py:function:: mult(x: int, y: int) -> int', + ' mult(x: float, y: float) -> float', + ' :module: target.annotations', + '', + ' docstring', + '', + '', + '.. py:function:: sum(x: int, y: int) -> int', + ' :module: target.annotations', + '', + ' docstring', + '', + ] + + # define aliases + app.config.autodoc_type_aliases = {'myint': 'myint'} + actual = do_autodoc(app, 'module', 'target.annotations', options) + assert list(actual) == [ + '', + '.. py:module:: target.annotations', + '', + '', + '.. py:function:: mult(x: myint, y: myint) -> myint', + ' mult(x: float, y: float) -> float', + ' :module: target.annotations', + '', + ' docstring', + '', + '', + '.. py:function:: sum(x: myint, y: myint) -> myint', + ' :module: target.annotations', + '', + ' docstring', + '', + ] + + @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_default_options(app): # no settings diff --git a/tests/test_ext_autodoc_events.py b/tests/test_ext_autodoc_events.py index 4e8348abc..7ddc952ab 100644 --- a/tests/test_ext_autodoc_events.py +++ b/tests/test_ext_autodoc_events.py @@ -28,7 +28,8 @@ def test_process_docstring(app): '.. py:function:: func()', ' :module: target.process_docstring', '', - ' my docstring' + ' my docstring', + '', ] diff --git a/tests/test_intl.py b/tests/test_intl.py index 1d1282baa..c0b87d5ce 100644 --- a/tests/test_intl.py +++ b/tests/test_intl.py @@ -14,8 +14,10 @@ import re import pytest from babel.messages import pofile, mofile +from babel.messages.catalog import Catalog from docutils import nodes +from sphinx import locale from sphinx.testing.util import ( path, etree_parse, strip_escseq, assert_re_search, assert_not_re_search, assert_startswith, assert_node @@ -1289,3 +1291,30 @@ def test_image_glob_intl_using_figure_language_filename(app): def getwarning(warnings): return strip_escseq(warnings.getvalue().replace(os.sep, '/')) + + +@pytest.mark.sphinx('html', testroot='basic', confoverrides={'language': 'de'}) +def test_customize_system_message(make_app, app_params, sphinx_test_tempdir): + try: + # clear translators cache + locale.translators.clear() + + # prepare message catalog (.po) + locale_dir = sphinx_test_tempdir / 'basic' / 'locales' / 'de' / 'LC_MESSAGES' + locale_dir.makedirs() + with (locale_dir / 'sphinx.po').open('wb') as f: + catalog = Catalog() + catalog.add('Quick search', 'QUICK SEARCH') + pofile.write_po(f, catalog) + + # construct application and convert po file to .mo + args, kwargs = app_params + app = make_app(*args, **kwargs) + assert (locale_dir / 'sphinx.mo').exists() + assert app.translator.gettext('Quick search') == 'QUICK SEARCH' + + app.build() + content = (app.outdir / 'index.html').read_text() + assert 'QUICK SEARCH' in content + finally: + locale.translators.clear() diff --git a/tests/test_pycode.py b/tests/test_pycode.py index 458e813f6..20f01f17d 100644 --- a/tests/test_pycode.py +++ b/tests/test_pycode.py @@ -26,7 +26,7 @@ def test_ModuleAnalyzer_get_module_source(): ModuleAnalyzer.get_module_source('builtins') with pytest.raises(PycodeError): ModuleAnalyzer.get_module_source('itertools') - + def test_ModuleAnalyzer_for_string(): analyzer = ModuleAnalyzer.for_string('print("Hello world")', 'module_name') diff --git a/tests/test_pycode_ast.py b/tests/test_pycode_ast.py index 9b12d24d5..bbff64dd0 100644 --- a/tests/test_pycode_ast.py +++ b/tests/test_pycode_ast.py @@ -53,12 +53,12 @@ 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) ]) def test_unparse(source, expected): module = ast.parse(source) - assert ast.unparse(module.body[0].value) == expected + assert ast.unparse(module.body[0].value, source) == expected def test_unparse_None(): @@ -66,8 +66,12 @@ def test_unparse_None(): @pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.') -def test_unparse_py38(): - source = "lambda x=0, /, y=1, *args, z, **kwargs: x + y + z" - expected = "lambda x=0, /, y=1, *args, z, **kwargs: ..." +@pytest.mark.parametrize('source,expected', [ + ("lambda x=0, /, y=1, *args, z, **kwargs: x + y + z", + "lambda x=0, /, y=1, *args, z, **kwargs: ..."), # posonlyargs + ("0x1234", "0x1234"), # Constant + ("1_000_000", "1_000_000"), # Constant +]) +def test_unparse_py38(source, expected): module = ast.parse(source) - assert ast.unparse(module.body[0].value) == expected + assert ast.unparse(module.body[0].value, source) == expected diff --git a/tests/test_util_typing.py b/tests/test_util_typing.py index 932fdbfc0..3f8ddbb37 100644 --- a/tests/test_util_typing.py +++ b/tests/test_util_typing.py @@ -32,6 +32,10 @@ class MyList(List[T]): pass +class BrokenType: + __args__ = int + + def test_stringify(): assert stringify(int) == "int" assert stringify(str) == "str" @@ -113,3 +117,7 @@ def test_stringify_type_hints_alias(): MyTuple = Tuple[str, str] assert stringify(MyStr) == "str" assert stringify(MyTuple) == "Tuple[str, str]" # type: ignore + + +def test_stringify_broken_type_hints(): + assert stringify(BrokenType) == 'test_util_typing.BrokenType' |
