summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2020-11-10 23:21:51 +0900
committerTakeshi KOMIYA <i.tkomiya@gmail.com>2020-11-10 23:21:51 +0900
commit1254b8011347b309af122dbe91a9439cc56f4381 (patch)
tree241b3f47ed71c3500330690118de5a7c7e55e2d4
parent7299d589c61d3558efb9777d03454f8e2a3dafd1 (diff)
parent229e11c488fc1fbd15b0a209782aa94dc6abdf58 (diff)
downloadsphinx-git-1254b8011347b309af122dbe91a9439cc56f4381.tar.gz
Merge branch '3.x'
-rw-r--r--.github/workflows/main.yml13
-rw-r--r--CHANGES11
-rw-r--r--sphinx/domains/c.py1
-rw-r--r--sphinx/ext/autodoc/__init__.py78
-rw-r--r--sphinx/ext/autosummary/__init__.py7
-rw-r--r--sphinx/pycode/__init__.py13
-rw-r--r--sphinx/util/inspect.py4
-rw-r--r--sphinx/util/typing.py2
-rw-r--r--tests/test_build_linkcheck.py32
-rw-r--r--tests/test_ext_autodoc_configs.py2
-rw-r--r--tests/test_ext_intersphinx.py30
-rw-r--r--tests/test_util_inspect.py11
-rw-r--r--tests/utils.py27
-rw-r--r--utils/release-checklist8
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
diff --git a/CHANGES b/CHANGES
index a9e91691c..b674c91dc 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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``