diff options
author | Takeshi KOMIYA <i.tkomiya@gmail.com> | 2020-11-10 23:21:51 +0900 |
---|---|---|
committer | Takeshi KOMIYA <i.tkomiya@gmail.com> | 2020-11-10 23:21:51 +0900 |
commit | 1254b8011347b309af122dbe91a9439cc56f4381 (patch) | |
tree | 241b3f47ed71c3500330690118de5a7c7e55e2d4 | |
parent | 7299d589c61d3558efb9777d03454f8e2a3dafd1 (diff) | |
parent | 229e11c488fc1fbd15b0a209782aa94dc6abdf58 (diff) | |
download | sphinx-git-1254b8011347b309af122dbe91a9439cc56f4381.tar.gz |
Merge branch '3.x'
-rw-r--r-- | .github/workflows/main.yml | 13 | ||||
-rw-r--r-- | CHANGES | 11 | ||||
-rw-r--r-- | sphinx/domains/c.py | 1 | ||||
-rw-r--r-- | sphinx/ext/autodoc/__init__.py | 78 | ||||
-rw-r--r-- | sphinx/ext/autosummary/__init__.py | 7 | ||||
-rw-r--r-- | sphinx/pycode/__init__.py | 13 | ||||
-rw-r--r-- | sphinx/util/inspect.py | 4 | ||||
-rw-r--r-- | sphinx/util/typing.py | 2 | ||||
-rw-r--r-- | tests/test_build_linkcheck.py | 32 | ||||
-rw-r--r-- | tests/test_ext_autodoc_configs.py | 2 | ||||
-rw-r--r-- | tests/test_ext_intersphinx.py | 30 | ||||
-rw-r--r-- | tests/test_util_inspect.py | 11 | ||||
-rw-r--r-- | tests/utils.py | 27 | ||||
-rw-r--r-- | utils/release-checklist | 8 |
14 files changed, 138 insertions, 101 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f277255cf..9e4dd2ade 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,11 +4,12 @@ on: [push, pull_request] jobs: ubuntu: - runs-on: ubuntu-16.04 + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: name: [py36, py37, py38, py39] + os: [ubuntu-16.04] include: - name: py36 python: 3.6 @@ -23,6 +24,10 @@ jobs: python: 3.9 docutils: du16 coverage: "--cov ./ --cov-append --cov-config setup.cfg" + - name: py310-dev + python: 3.10-dev + docutils: du16 + os: ubuntu-latest # required env: PYTEST_ADDOPTS: ${{ matrix.coverage }} @@ -30,6 +35,12 @@ jobs: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python }} uses: actions/setup-python@v2 + if: "!endsWith(matrix.python, '-dev')" + with: + python-version: ${{ matrix.python }} + - name: Set up Python ${{ matrix.python }} (deadsnakes) + uses: deadsnakes/action@v1.0.0 + if: endsWith(matrix.python, '-dev') with: python-version: ${{ matrix.python }} - name: Check Python version @@ -72,6 +72,11 @@ Features added * #8119: autodoc: Allow to determine whether a member not included in ``__all__`` attribute of the module should be documented or not via :event:`autodoc-skip-member` event +* #8219: autodoc: Parameters for generic class are not shown when super class is + a generic class and show-inheritance option is given (in Python 3.7 or above) +* autodoc: Add ``Documenter.config`` as a shortcut to access the config object +* autodoc: Add Optional[t] to annotation of function and method if a default + value equal to None is set. * #6914: Add a new event :event:`warn-missing-reference` to custom warning messages when failed to resolve a cross-reference * #6914: Emit a detailed warning when failed to resolve a ``:ref:`` reference @@ -105,8 +110,10 @@ Features added Bugs fixed ---------- -* #8219: autodoc: Parameters for generic class are not shown when super class is - a generic class and show-inheritance option is given (in Python 3.7 or above) +* #8372: autodoc: autoclass directive became slower than Sphinx-3.2 +* #7727: autosummary: raise PycodeError when documenting python package + without __init__.py +* #8364: C, properly initialize attributes in empty symbols. Testing -------- diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index e3570b593..5896f4a80 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -1533,6 +1533,7 @@ class Symbol: self.declaration = declaration self.declaration.symbol = self self.docname = docname + self.line = line self._assert_invariants() # and symbol addition should be done as well self._add_function_params() diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 9a6417a2e..cf92b0137 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -323,6 +323,7 @@ class Documenter: def __init__(self, directive: "DocumenterBridge", name: str, indent: str = '') -> None: self.directive = directive + self.config = directive.env.config self.env = directive.env # type: BuildEnvironment self.options = directive.genopt self.name = name @@ -393,7 +394,7 @@ class Documenter: modname = None parents = [] - with mock(self.env.config.autodoc_mock_imports): + with mock(self.config.autodoc_mock_imports): self.modname, self.objpath = self.resolve_name(modname, parents, path, base) if not self.modname: @@ -411,11 +412,11 @@ class Documenter: Returns True if successful, False if an error occurred. """ - with mock(self.env.config.autodoc_mock_imports): + with mock(self.config.autodoc_mock_imports): try: ret = import_object(self.modname, self.objpath, self.objtype, attrgetter=self.get_attr, - warningiserror=self.env.config.autodoc_warningiserror) + warningiserror=self.config.autodoc_warningiserror) self.module, self.parent, self.object_name, self.object = ret return True except ImportError as exc: @@ -539,8 +540,7 @@ class Documenter: warnings.warn("The 'ignore' argument to autodoc.%s.get_doc() is deprecated." % self.__class__.__name__, RemovedInSphinx50Warning, stacklevel=2) - docstring = getdoc(self.object, self.get_attr, - self.env.config.autodoc_inherit_docstrings, + docstring = getdoc(self.object, self.get_attr, self.config.autodoc_inherit_docstrings, self.parent, self.object_name) if docstring: tab_width = self.directive.state.document.settings.tab_width @@ -682,7 +682,7 @@ class Documenter: else: isattr = False - doc = getdoc(member, self.get_attr, self.env.config.autodoc_inherit_docstrings, + doc = getdoc(member, self.get_attr, self.config.autodoc_inherit_docstrings, self.parent, self.object_name) if not isinstance(doc, str): # Ignore non-string __doc__ @@ -810,7 +810,7 @@ class Documenter: documenter = classes[-1](self.directive, full_mname, self.indent) memberdocumenters.append((documenter, isattr)) - member_order = self.options.member_order or self.env.config.autodoc_member_order + member_order = self.options.member_order or self.config.autodoc_member_order memberdocumenters = self.sort_members(memberdocumenters, member_order) for documenter, isattr in memberdocumenters: @@ -1194,7 +1194,7 @@ class DocstringSignatureMixin: return super().get_doc(ignore) # type: ignore def format_signature(self, **kwargs: Any) -> str: - if self.args is None and self.env.config.autodoc_docstring_signature: # type: ignore + if self.args is None and self.config.autodoc_docstring_signature: # type: ignore # only act if a signature is not explicitly given already, and if # the feature is enabled result = self._find_signature() @@ -1213,7 +1213,7 @@ class DocstringStripSignatureMixin(DocstringSignatureMixin): feature of stripping any function signature from the docstring. """ def format_signature(self, **kwargs: Any) -> str: - if self.args is None and self.env.config.autodoc_docstring_signature: # type: ignore + if self.args is None and self.config.autodoc_docstring_signature: # type: ignore # only act if a signature is not explicitly given already, and if # the feature is enabled result = self._find_signature() @@ -1240,13 +1240,12 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ (inspect.isroutine(member) and isinstance(parent, ModuleDocumenter))) def format_args(self, **kwargs: Any) -> str: - if self.env.config.autodoc_typehints in ('none', 'description'): + if self.config.autodoc_typehints in ('none', 'description'): kwargs.setdefault('show_annotation', False) try: self.env.app.emit('autodoc-before-process-signature', self.object, False) - sig = inspect.signature(self.object, - type_aliases=self.env.config.autodoc_type_aliases) + sig = inspect.signature(self.object, type_aliases=self.config.autodoc_type_aliases) args = stringify_signature(sig, **kwargs) except TypeError as exc: logger.warning(__("Failed to get a function signature for %s: %s"), @@ -1255,7 +1254,7 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ except ValueError: args = '' - if self.env.config.strip_signature_backslash: + if self.config.strip_signature_backslash: # escape backslashes for reST args = args.replace('\\', '\\\\') return args @@ -1274,7 +1273,7 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ sigs = [] if (self.analyzer and '.'.join(self.objpath) in self.analyzer.overloads and - self.env.config.autodoc_typehints == 'signature'): + self.config.autodoc_typehints == 'signature'): # Use signatures for overloaded functions instead of the implementation function. overloaded = True else: @@ -1298,7 +1297,7 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ __globals__ = safe_getattr(self.object, '__globals__', {}) for overload in self.analyzer.overloads.get('.'.join(self.objpath)): overload = evaluate_signature(overload, __globals__, - self.env.config.autodoc_type_aliases) + self.config.autodoc_type_aliases) sig = stringify_signature(overload, **kwargs) sigs.append(sig) @@ -1308,7 +1307,7 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ def annotate_to_first_argument(self, func: Callable, typ: Type) -> None: """Annotate type hint to the first argument of function if needed.""" try: - sig = inspect.signature(func, type_aliases=self.env.config.autodoc_type_aliases) + sig = inspect.signature(func, type_aliases=self.config.autodoc_type_aliases) except TypeError as exc: logger.warning(__("Failed to get a function signature for %s: %s"), self.fullname, exc) @@ -1440,7 +1439,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: self.env.app.emit('autodoc-before-process-signature', call, True) try: sig = inspect.signature(call, bound_method=True, - type_aliases=self.env.config.autodoc_type_aliases) + type_aliases=self.config.autodoc_type_aliases) return type(self.object), '__call__', sig except ValueError: pass @@ -1456,7 +1455,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: self.env.app.emit('autodoc-before-process-signature', new, True) try: sig = inspect.signature(new, bound_method=True, - type_aliases=self.env.config.autodoc_type_aliases) + type_aliases=self.config.autodoc_type_aliases) return self.object, '__new__', sig except ValueError: pass @@ -1467,7 +1466,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: self.env.app.emit('autodoc-before-process-signature', init, True) try: sig = inspect.signature(init, bound_method=True, - type_aliases=self.env.config.autodoc_type_aliases) + type_aliases=self.config.autodoc_type_aliases) return self.object, '__init__', sig except ValueError: pass @@ -1479,7 +1478,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: self.env.app.emit('autodoc-before-process-signature', self.object, False) try: sig = inspect.signature(self.object, bound_method=False, - type_aliases=self.env.config.autodoc_type_aliases) + type_aliases=self.config.autodoc_type_aliases) return None, None, sig except ValueError: pass @@ -1489,7 +1488,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: return None, None, None def format_args(self, **kwargs: Any) -> str: - if self.env.config.autodoc_typehints in ('none', 'description'): + if self.config.autodoc_typehints in ('none', 'description'): kwargs.setdefault('show_annotation', False) try: @@ -1513,13 +1512,13 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: sigs = [] overloads = self.get_overloaded_signatures() - if overloads and self.env.config.autodoc_typehints == 'signature': + if overloads and self.config.autodoc_typehints == 'signature': # Use signatures for overloaded methods instead of the implementation method. method = safe_getattr(self._signature_class, self._signature_method_name, None) __globals__ = safe_getattr(method, '__globals__', {}) for overload in overloads: overload = evaluate_signature(overload, __globals__, - self.env.config.autodoc_type_aliases) + self.config.autodoc_type_aliases) parameters = list(overload.parameters.values()) overload = overload.replace(parameters=parameters[1:], @@ -1540,6 +1539,9 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: qualname = '.'.join([cls.__qualname__, self._signature_method_name]) if qualname in analyzer.overloads: return analyzer.overloads.get(qualname) + elif qualname in analyzer.tagorder: + # the constructor is defined in the class, but not overrided. + return [] except PycodeError: pass @@ -1575,7 +1577,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: if lines is not None: return lines - content = self.env.config.autoclass_content + content = self.config.autoclass_content docstrings = [] attrdocstring = self.get_attr(self.object, '__doc__', None) @@ -1587,7 +1589,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: if content in ('both', 'init'): __init__ = self.get_attr(self.object, '__init__', None) initdocstring = getdoc(__init__, self.get_attr, - self.env.config.autodoc_inherit_docstrings, + self.config.autodoc_inherit_docstrings, self.parent, self.object_name) # for new-style classes, no __init__ means default __init__ if (initdocstring is not None and @@ -1598,7 +1600,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: # try __new__ __new__ = self.get_attr(self.object, '__new__', None) initdocstring = getdoc(__new__, self.get_attr, - self.env.config.autodoc_inherit_docstrings, + self.config.autodoc_inherit_docstrings, self.parent, self.object_name) # for new-style classes, no __new__ means default __new__ if (initdocstring is not None and @@ -1863,7 +1865,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: return ret def format_args(self, **kwargs: Any) -> str: - if self.env.config.autodoc_typehints in ('none', 'description'): + if self.config.autodoc_typehints in ('none', 'description'): kwargs.setdefault('show_annotation', False) try: @@ -1877,11 +1879,11 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: if inspect.isstaticmethod(self.object, cls=self.parent, name=self.object_name): self.env.app.emit('autodoc-before-process-signature', self.object, False) sig = inspect.signature(self.object, bound_method=False, - type_aliases=self.env.config.autodoc_type_aliases) + type_aliases=self.config.autodoc_type_aliases) else: self.env.app.emit('autodoc-before-process-signature', self.object, True) sig = inspect.signature(self.object, bound_method=True, - type_aliases=self.env.config.autodoc_type_aliases) + type_aliases=self.config.autodoc_type_aliases) args = stringify_signature(sig, **kwargs) except TypeError as exc: logger.warning(__("Failed to get a method signature for %s: %s"), @@ -1890,7 +1892,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: except ValueError: args = '' - if self.env.config.strip_signature_backslash: + if self.config.strip_signature_backslash: # escape backslashes for reST args = args.replace('\\', '\\\\') return args @@ -1918,7 +1920,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: sigs = [] if (self.analyzer and '.'.join(self.objpath) in self.analyzer.overloads and - self.env.config.autodoc_typehints == 'signature'): + self.config.autodoc_typehints == 'signature'): # Use signatures for overloaded methods instead of the implementation method. overloaded = True else: @@ -1944,7 +1946,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: __globals__ = safe_getattr(self.object, '__globals__', {}) for overload in self.analyzer.overloads.get('.'.join(self.objpath)): overload = evaluate_signature(overload, __globals__, - self.env.config.autodoc_type_aliases) + self.config.autodoc_type_aliases) if not inspect.isstaticmethod(self.object, cls=self.parent, name=self.object_name): @@ -1958,7 +1960,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: def annotate_to_first_argument(self, func: Callable, typ: Type) -> None: """Annotate type hint to the first argument of function if needed.""" try: - sig = inspect.signature(func, type_aliases=self.env.config.autodoc_type_aliases) + sig = inspect.signature(func, type_aliases=self.config.autodoc_type_aliases) except TypeError as exc: logger.warning(__("Failed to get a method signature for %s: %s"), self.fullname, exc) @@ -2110,11 +2112,11 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): # Disable `autodoc_inherit_docstring` temporarily to avoid to obtain # a docstring from the value which descriptor returns unexpectedly. # ref: https://github.com/sphinx-doc/sphinx/issues/7805 - orig = self.env.config.autodoc_inherit_docstrings - self.env.config.autodoc_inherit_docstrings = False # type: ignore + orig = self.config.autodoc_inherit_docstrings + self.config.autodoc_inherit_docstrings = False # type: ignore return super().get_doc(ignore) finally: - self.env.config.autodoc_inherit_docstrings = orig # type: ignore + self.config.autodoc_inherit_docstrings = orig # type: ignore def add_content(self, more_content: Any, no_docstring: bool = False) -> None: if not self._datadescriptor: @@ -2223,11 +2225,11 @@ class SlotsAttributeDocumenter(AttributeDocumenter): self.objtype = 'attribute' self._datadescriptor = True - with mock(self.env.config.autodoc_mock_imports): + with mock(self.config.autodoc_mock_imports): try: ret = import_object(self.modname, self.objpath[:-1], 'class', attrgetter=self.get_attr, - warningiserror=self.env.config.autodoc_warningiserror) + warningiserror=self.config.autodoc_warningiserror) self.module, _, _, self.parent = ret return True except ImportError as exc: diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index fba270acf..ee1785251 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -72,6 +72,7 @@ from docutils.statemachine import StringList import sphinx from sphinx import addnodes from sphinx.application import Sphinx +from sphinx.config import Config from sphinx.deprecation import RemovedInSphinx50Warning from sphinx.environment import BuildEnvironment from sphinx.environment.adapters.toctree import TocTree @@ -173,8 +174,10 @@ class FakeDirective(DocumenterBridge): def __init__(self) -> None: settings = Struct(tab_width=8) document = Struct(settings=settings) + env = BuildEnvironment() + env.config = Config() state = Struct(document=document) - super().__init__({}, None, Options(), 0, state) # type: ignore + super().__init__(env, None, Options(), 0, state) def get_documenter(app: Sphinx, obj: Any, parent: Any) -> "Type[Documenter]": @@ -664,7 +667,7 @@ def import_ivar_by_name(name: str, prefixes: List[str] = [None]) -> Tuple[str, A analyzer = ModuleAnalyzer.for_module(modname) if (qualname, attr) in analyzer.find_attr_docs(): return real_name + "." + attr, INSTANCEATTR, obj, modname - except (ImportError, ValueError): + except (ImportError, ValueError, PycodeError): pass raise ImportError diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py index 943c77244..8bc1ba6c1 100644 --- a/sphinx/pycode/__init__.py +++ b/sphinx/pycode/__init__.py @@ -139,9 +139,13 @@ class ModuleAnalyzer: self.overloads = None # type: Dict[str, List[Signature]] self.tagorder = None # type: Dict[str, int] self.tags = None # type: Dict[str, Tuple[str, int, int]] + self._parsed = False def parse(self) -> None: """Parse the source code.""" + if self._parsed: + return None + try: parser = Parser(self.code) parser.parse() @@ -158,19 +162,16 @@ class ModuleAnalyzer: self.overloads = parser.overloads self.tags = parser.definitions self.tagorder = parser.deforders + self._parsed = True except Exception as exc: raise PycodeError('parsing %r failed: %r' % (self.srcname, exc)) from exc def find_attr_docs(self) -> Dict[Tuple[str, str], List[str]]: """Find class and module-level attributes and their documentation.""" - if self.attr_docs is None: - self.parse() - + self.parse() return self.attr_docs def find_tags(self) -> Dict[str, Tuple[str, int, int]]: """Find class, function and method definitions and their location.""" - if self.tags is None: - self.parse() - + self.parse() return self.tags diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index 6912765e6..b8a149d05 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -453,10 +453,10 @@ def signature(subject: Callable, bound_method: bool = False, follow_wrapped: boo raise try: - # Update unresolved annotations using ``get_type_hints()``. + # Resolve annotations using ``get_type_hints()`` and type_aliases. annotations = typing.get_type_hints(subject, None, type_aliases) for i, param in enumerate(parameters): - if isinstance(param.annotation, str) and param.name in annotations: + if param.name in annotations: parameters[i] = param.replace(annotation=annotations[param.name]) if 'return' in annotations: return_annotation = annotations['return'] diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index ab02f1f6b..ebd6518e0 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -215,7 +215,7 @@ def stringify(annotation: Any) -> str: if isinstance(annotation, str): if annotation.startswith("'") and annotation.endswith("'"): # might be a double Forward-ref'ed type. Go unquoting. - return annotation[1:-2] + return annotation[1:-1] else: return annotation elif isinstance(annotation, TypeVar): diff --git a/tests/test_build_linkcheck.py b/tests/test_build_linkcheck.py index a78587668..908319e6b 100644 --- a/tests/test_build_linkcheck.py +++ b/tests/test_build_linkcheck.py @@ -11,10 +11,11 @@ import http.server import json import re -import threading from unittest import mock import pytest +from utils import http_server + @pytest.mark.sphinx('linkcheck', testroot='linkcheck', freshenv=True) def test_defaults(app, status, warning): @@ -110,12 +111,12 @@ def test_anchors_ignored(app, status, warning): @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: + class InternalServerErrorHandler(http.server.BaseHTTPRequestHandler): + def do_GET(self): + self.send_error(500, "Internal Server Error") + + with http_server(InternalServerErrorHandler): 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: " @@ -177,22 +178,3 @@ 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_ext_autodoc_configs.py b/tests/test_ext_autodoc_configs.py index a0eba20c8..19936c5ae 100644 --- a/tests/test_ext_autodoc_configs.py +++ b/tests/test_ext_autodoc_configs.py @@ -490,7 +490,7 @@ def test_autodoc_typehints_signature(app): '.. py:module:: target.typehints', '', '', - '.. py:class:: Math(s: str, o: Any = None)', + '.. py:class:: Math(s: str, o: Optional[Any] = None)', ' :module: target.typehints', '', '', diff --git a/tests/test_ext_intersphinx.py b/tests/test_ext_intersphinx.py index 53faa7a37..a88f64c71 100644 --- a/tests/test_ext_intersphinx.py +++ b/tests/test_ext_intersphinx.py @@ -8,13 +8,13 @@ :license: BSD, see LICENSE for details. """ +import http.server import os import unittest from io import BytesIO from unittest import mock import pytest -import requests from docutils import nodes from test_util_inventory import inventory_v2, inventory_v2_not_having_version @@ -25,6 +25,8 @@ from sphinx.ext.intersphinx import ( ) from sphinx.ext.intersphinx import setup as intersphinx_setup +from utils import http_server + def fake_node(domain, type, target, content, **attrs): contnode = nodes.emphasis(content, content) @@ -433,24 +435,22 @@ def test_inspect_main_file(capsys, tempdir): assert stderr == "" -@mock.patch('requests.get') -def test_inspect_main_url(fake_get, capsys): +def test_inspect_main_url(capsys): """inspect_main interface, with url argument""" - raw = BytesIO(inventory_v2) - real_read = raw.read + class InventoryHandler(http.server.BaseHTTPRequestHandler): + def do_GET(self): + self.send_response(200, "OK") + self.end_headers() + self.wfile.write(inventory_v2) - def fake_read(*args, **kwargs): - return real_read() + def log_message(*args, **kwargs): + # Silenced. + pass - raw.read = fake_read - url = 'http://hostname/' + INVENTORY_FILENAME - resp = requests.Response() - resp.status_code = 200 - resp.url = url - resp.raw = raw - fake_get.return_value = resp + url = 'http://localhost:7777/' + INVENTORY_FILENAME - inspect_main([url]) + with http_server(InventoryHandler): + inspect_main([url]) stdout, stderr = capsys.readouterr() assert stdout.startswith("c:function\n") diff --git a/tests/test_util_inspect.py b/tests/test_util_inspect.py index 7ffe95982..8103050f5 100644 --- a/tests/test_util_inspect.py +++ b/tests/test_util_inspect.py @@ -162,7 +162,7 @@ def test_signature_annotations(): # Space around '=' for defaults sig = inspect.signature(f7) - assert stringify_signature(sig) == '(x: int = None, y: dict = {}) -> None' + assert stringify_signature(sig) == '(x: Optional[int] = None, y: dict = {}) -> None' # Callable types sig = inspect.signature(f8) @@ -177,7 +177,10 @@ def test_signature_annotations(): # Instance annotations sig = inspect.signature(f11) - assert stringify_signature(sig) == '(x: CustomAnnotation, y: 123) -> None' + if sys.version_info < (3, 10): + assert stringify_signature(sig) == '(x: CustomAnnotation, y: 123) -> None' + else: + assert stringify_signature(sig) == '(x: CustomAnnotation(), y: 123) -> None' # tuple with more than two items sig = inspect.signature(f12) @@ -223,7 +226,7 @@ def test_signature_annotations(): assert stringify_signature(sig) == '(self) -> List[typing_test_data.Node]' sig = inspect.signature(Node.__init__) - assert stringify_signature(sig) == '(self, parent: Optional[Node]) -> None' + assert stringify_signature(sig) == '(self, parent: Optional[typing_test_data.Node]) -> None' # show_annotation is False sig = inspect.signature(f7) @@ -231,7 +234,7 @@ def test_signature_annotations(): # show_return_annotation is False sig = inspect.signature(f7) - assert stringify_signature(sig, show_return_annotation=False) == '(x: int = None, y: dict = {})' + assert stringify_signature(sig, show_return_annotation=False) == '(x: Optional[int] = None, y: dict = {})' @pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.') diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 000000000..182dc1df0 --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,27 @@ +import contextlib +import http.server +import threading + + +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() + + +@contextlib.contextmanager +def http_server(handler): + server_thread = HttpServerThread(handler, daemon=True) + server_thread.start() + try: + yield server_thread + finally: + server_thread.terminate() diff --git a/utils/release-checklist b/utils/release-checklist index 582d26685..dad83c850 100644 --- a/utils/release-checklist +++ b/utils/release-checklist @@ -4,7 +4,7 @@ Release checklist for stable releases ------------------- -* open https://travis-ci.org/sphinx-doc/sphinx/branches and check **X.Y** branch is green +* open https://github.com/sphinx-doc/sphinx/actions?query=branch:X.Y.x and all tests has passed * Run ``git fetch; git status`` and check nothing changed * ``python utils/bump_version.py X.Y.Z`` * Check diff by ``git diff`` @@ -28,7 +28,7 @@ for stable releases for first beta releases ----------------------- -* open https://travis-ci.org/sphinx-doc/sphinx/branches and check **master** branch is green +* open https://github.com/sphinx-doc/sphinx/actions?query=branch:master and all tests has passed * Run ``git fetch; git status`` and check nothing changed * Run ``python setup.py extract_messages`` * Run ``(cd sphinx/locale; tx push -s)`` @@ -58,7 +58,7 @@ for first beta releases for other beta releases ----------------------- -* open https://travis-ci.org/sphinx-doc/sphinx/branches and check **X.Y** branch is green +* open https://github.com/sphinx-doc/sphinx/actions?query=branch:X.x and all tests has passed * Run ``git fetch; git status`` and check nothing changed * ``python utils/bump_version.py X.Y.0bN`` * Check diff by ``git diff`` @@ -81,7 +81,7 @@ for other beta releases for major releases ------------------ -* open https://travis-ci.org/sphinx-doc/sphinx/branches and check **X.Y** branch is green +* open https://github.com/sphinx-doc/sphinx/actions?query=branch:X.x and all tests has passed * Run ``git fetch; git status`` and check nothing changed * Run ``(cd sphinx/locale; tx pull -a -f)`` * Run ``python setup.py compile_catalog`` |