Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sphinx-doc/sphinx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/sphinx
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2020-11-20 19:01:36 +0300
committerTakeshi KOMIYA <i.tkomiya@gmail.com>2020-11-20 19:01:36 +0300
commit3a4ae2092a6f236e2d71fdedb4b66f594e30d4e7 (patch)
treeafa4f774e0f8660d5ce247f01cace9deaa848d58 /sphinx
parentf3a6004f822b05e352372a77f449332ad230d21e (diff)
parent18b2707b2ac621f23f8ee6a7ca19bf1590be7826 (diff)
Merge branch '3.x'
Diffstat (limited to 'sphinx')
-rw-r--r--sphinx/application.py6
-rw-r--r--sphinx/builders/html/__init__.py1
-rw-r--r--sphinx/builders/linkcheck.py15
-rw-r--r--sphinx/ext/autodoc/__init__.py144
-rw-r--r--sphinx/ext/autodoc/importer.py19
-rw-r--r--sphinx/ext/autosummary/generate.py6
-rw-r--r--sphinx/themes/basic/search.html1
-rw-r--r--sphinx/util/inspect.py45
-rw-r--r--sphinx/util/requests.py5
-rw-r--r--sphinx/util/typing.py23
10 files changed, 163 insertions, 102 deletions
diff --git a/sphinx/application.py b/sphinx/application.py
index 7661b82d6..b0834fef7 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -130,7 +130,7 @@ class Sphinx:
:ivar outdir: Directory for storing build documents.
"""
- def __init__(self, srcdir: str, confdir: str, outdir: str, doctreedir: str,
+ def __init__(self, srcdir: str, confdir: Optional[str], outdir: str, doctreedir: str,
buildername: str, confoverrides: Dict = None,
status: IO = sys.stdout, warning: IO = sys.stderr,
freshenv: bool = False, warningiserror: bool = False, tags: List[str] = None,
@@ -289,8 +289,8 @@ class Sphinx:
if catalog.domain == 'sphinx' and catalog.is_outdated():
catalog.write_mo(self.config.language)
- locale_dirs = [None] # type: List[Optional[str]]
- locale_dirs += list(repo.locale_dirs)
+ locale_dirs = list(repo.locale_dirs) # type: List[Optional[str]]
+ locale_dirs += [None]
locale_dirs += [path.join(package_dir, 'locale')]
self.translator, has_translation = locale.init(locale_dirs, self.config.language)
diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py
index 0af5dde28..49dac78d5 100644
--- a/sphinx/builders/html/__init__.py
+++ b/sphinx/builders/html/__init__.py
@@ -295,7 +295,6 @@ class StandaloneHTMLBuilder(Builder):
self.add_js_file('jquery.js')
self.add_js_file('underscore.js')
self.add_js_file('doctools.js')
- self.add_js_file('language_data.js')
for filename, attrs in self.app.registry.js_files:
self.add_js_file(filename, **attrs)
diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py
index 40e54a8d7..1dc0337c3 100644
--- a/sphinx/builders/linkcheck.py
+++ b/sphinx/builders/linkcheck.py
@@ -28,7 +28,6 @@ from sphinx.locale import __
from sphinx.util import encode_uri, logging, requests
from sphinx.util.console import darkgray, darkgreen, purple, red, turquoise # type: ignore
from sphinx.util.nodes import get_node_line
-from sphinx.util.requests import is_ssl_error
logger = logging.getLogger(__name__)
@@ -108,9 +107,7 @@ class CheckExternalLinksBuilder(Builder):
self.workers.append(thread)
def check_thread(self) -> None:
- kwargs = {
- 'allow_redirects': True,
- } # type: Dict
+ kwargs = {}
if self.app.config.linkcheck_timeout:
kwargs['timeout'] = self.app.config.linkcheck_timeout
@@ -171,8 +168,9 @@ class CheckExternalLinksBuilder(Builder):
try:
# try a HEAD request first, which should be easier on
# the server and the network
- response = requests.head(req_url, config=self.app.config,
- auth=auth_info, **kwargs)
+ response = requests.head(req_url, allow_redirects=True,
+ config=self.app.config, auth=auth_info,
+ **kwargs)
response.raise_for_status()
except HTTPError:
# retry with GET request if that fails, some servers
@@ -190,10 +188,7 @@ class CheckExternalLinksBuilder(Builder):
else:
return 'broken', str(err), 0
except Exception as err:
- if is_ssl_error(err):
- return 'ignored', str(err), 0
- else:
- return 'broken', str(err), 0
+ return 'broken', str(err), 0
if response.url.rstrip('/') == req_url.rstrip('/'):
return 'working', '', 0
else:
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 73d3c75ef..a6cd850dc 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -16,7 +16,7 @@ import warnings
from inspect import Parameter, Signature
from types import ModuleType
from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterator, List, Optional, Sequence,
- Set, Tuple, Type, TypeVar, Union, get_type_hints)
+ Set, Tuple, Type, TypeVar, Union)
from docutils.statemachine import StringList
@@ -33,7 +33,7 @@ from sphinx.util import inspect, logging
from sphinx.util.docstrings import extract_metadata, prepare_docstring
from sphinx.util.inspect import (evaluate_signature, getdoc, object_description, safe_getattr,
stringify_signature)
-from sphinx.util.typing import restify
+from sphinx.util.typing import get_type_hints, restify
from sphinx.util.typing import stringify as stringify_typehint
if TYPE_CHECKING:
@@ -954,7 +954,7 @@ class ModuleDocumenter(Documenter):
def __init__(self, *args: Any) -> None:
super().__init__(*args)
merge_members_option(self.options)
- self.__all__ = None
+ self.__all__ = None # type: Optional[Sequence[str]]
@classmethod
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
@@ -978,26 +978,16 @@ class ModuleDocumenter(Documenter):
return ret
def import_object(self, raiseerror: bool = False) -> bool:
- def is_valid_module_all(__all__: Any) -> bool:
- """Check the given *__all__* is valid for a module."""
- if (isinstance(__all__, (list, tuple)) and
- all(isinstance(e, str) for e in __all__)):
- return True
- else:
- return False
-
ret = super().import_object(raiseerror)
- if not self.options.ignore_module_all:
- __all__ = getattr(self.object, '__all__', None)
- if is_valid_module_all(__all__):
- # valid __all__ found. copy it to self.__all__
- self.__all__ = __all__
- elif __all__:
- # invalid __all__ found.
- logger.warning(__('__all__ should be a list of strings, not %r '
- '(in module %s) -- ignoring __all__') %
- (__all__, self.fullname), type='autodoc')
+ try:
+ if not self.options.ignore_module_all:
+ self.__all__ = inspect.getall(self.object)
+ except ValueError as exc:
+ # invalid __all__ found.
+ logger.warning(__('__all__ should be a list of strings, not %r '
+ '(in module %s) -- ignoring __all__') %
+ (exc.args[0], self.fullname), type='autodoc')
return ret
@@ -1670,28 +1660,41 @@ class DataDocumenter(ModuleLevelDocumenter):
priority = -10
option_spec = dict(ModuleLevelDocumenter.option_spec)
option_spec["annotation"] = annotation_option
+ option_spec["no-value"] = bool_option
@classmethod
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
) -> bool:
return isinstance(parent, ModuleDocumenter) and isattr
+ def import_object(self, raiseerror: bool = False) -> bool:
+ try:
+ return super().import_object(raiseerror=True)
+ except ImportError as exc:
+ # annotation only instance variable (PEP-526)
+ try:
+ self.parent = importlib.import_module(self.modname)
+ annotations = get_type_hints(self.parent, None,
+ self.config.autodoc_type_aliases)
+ if self.objpath[-1] in annotations:
+ self.object = UNINITIALIZED_ATTR
+ return True
+ except ImportError:
+ pass
+
+ if raiseerror:
+ raise
+ else:
+ logger.warning(exc.args[0], type='autodoc', subtype='import_object')
+ self.env.note_reread()
+ return False
+
def add_directive_header(self, sig: str) -> None:
super().add_directive_header(sig)
sourcename = self.get_sourcename()
if not self.options.annotation:
# obtain annotation for this data
- try:
- annotations = get_type_hints(self.parent)
- except NameError:
- # Failed to evaluate ForwardRef (maybe TYPE_CHECKING)
- annotations = safe_getattr(self.parent, '__annotations__', {})
- except TypeError:
- annotations = {}
- except KeyError:
- # a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084)
- annotations = {}
-
+ annotations = get_type_hints(self.parent, None, self.config.autodoc_type_aliases)
if self.objpath[-1] in annotations:
objrepr = stringify_typehint(annotations.get(self.objpath[-1]))
self.add_line(' :type: ' + objrepr, sourcename)
@@ -1702,7 +1705,7 @@ class DataDocumenter(ModuleLevelDocumenter):
sourcename)
try:
- if self.object is UNINITIALIZED_ATTR:
+ if self.object is UNINITIALIZED_ATTR or self.options.no_value:
pass
else:
objrepr = object_description(self.object)
@@ -1722,6 +1725,13 @@ class DataDocumenter(ModuleLevelDocumenter):
return self.get_attr(self.parent or self.object, '__module__', None) \
or self.modname
+ def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
+ if self.object is UNINITIALIZED_ATTR:
+ # suppress docstring of the value
+ super().add_content(more_content, no_docstring=True)
+ else:
+ super().add_content(more_content, no_docstring=no_docstring)
+
class DataDeclarationDocumenter(DataDocumenter):
"""
@@ -1735,30 +1745,10 @@ class DataDeclarationDocumenter(DataDocumenter):
# must be higher than AttributeDocumenter
priority = 11
- @classmethod
- def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
- ) -> bool:
- """This documents only INSTANCEATTR members."""
- return (isinstance(parent, ModuleDocumenter) and
- isattr and
- member is INSTANCEATTR)
-
- def import_object(self, raiseerror: bool = False) -> bool:
- """Never import anything."""
- # disguise as a data
- self.objtype = 'data'
- self.object = UNINITIALIZED_ATTR
- try:
- # import module to obtain type annotation
- self.parent = importlib.import_module(self.modname)
- except ImportError:
- pass
-
- return True
-
- def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
- """Never try to get a docstring from the object."""
- super().add_content(more_content, no_docstring=True)
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
+ warnings.warn("%s is deprecated." % self.__class__.__name__,
+ RemovedInSphinx50Warning, stacklevel=2)
+ super().__init__(*args, **kwargs)
class GenericAliasDocumenter(DataDocumenter):
@@ -1998,6 +1988,7 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
member_order = 60
option_spec = dict(ModuleLevelDocumenter.option_spec)
option_spec["annotation"] = annotation_option
+ option_spec["no-value"] = bool_option
# must be higher than the MethodDocumenter, else it will recognize
# some non-data descriptors as methods
@@ -2024,6 +2015,22 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
def isinstanceattribute(self) -> bool:
"""Check the subject is an instance attribute."""
+ # uninitialized instance variable (PEP-526)
+ with mock(self.config.autodoc_mock_imports):
+ try:
+ ret = import_object(self.modname, self.objpath[:-1], 'class',
+ attrgetter=self.get_attr,
+ warningiserror=self.config.autodoc_warningiserror)
+ self.parent = ret[3]
+ annotations = get_type_hints(self.parent, None,
+ self.config.autodoc_type_aliases)
+ if self.objpath[-1] in annotations:
+ self.object = UNINITIALIZED_ATTR
+ return True
+ except ImportError:
+ pass
+
+ # An instance variable defined inside __init__().
try:
analyzer = ModuleAnalyzer.for_module(self.modname)
attr_docs = analyzer.find_attr_docs()
@@ -2034,7 +2041,9 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
return False
except PycodeError:
- return False
+ pass
+
+ return False
def import_object(self, raiseerror: bool = False) -> bool:
try:
@@ -2069,17 +2078,7 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
sourcename = self.get_sourcename()
if not self.options.annotation:
# obtain type annotation for this attribute
- try:
- annotations = get_type_hints(self.parent)
- except NameError:
- # Failed to evaluate ForwardRef (maybe TYPE_CHECKING)
- annotations = safe_getattr(self.parent, '__annotations__', {})
- except TypeError:
- annotations = {}
- except KeyError:
- # a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084)
- annotations = {}
-
+ annotations = get_type_hints(self.parent, None, self.config.autodoc_type_aliases)
if self.objpath[-1] in annotations:
objrepr = stringify_typehint(annotations.get(self.objpath[-1]))
self.add_line(' :type: ' + objrepr, sourcename)
@@ -2092,7 +2091,7 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
# data descriptors do not have useful values
if not self._datadescriptor:
try:
- if self.object is INSTANCEATTR:
+ if self.object is INSTANCEATTR or self.options.no_value:
pass
else:
objrepr = object_description(self.object)
@@ -2244,8 +2243,8 @@ class SlotsAttributeDocumenter(AttributeDocumenter):
% self.__class__.__name__,
RemovedInSphinx50Warning, stacklevel=2)
name = self.objpath[-1]
- __slots__ = safe_getattr(self.parent, '__slots__', [])
- if isinstance(__slots__, dict) and isinstance(__slots__.get(name), str):
+ __slots__ = inspect.getslots(self.parent)
+ if __slots__ and isinstance(__slots__.get(name, None), str):
docstring = prepare_docstring(__slots__[name])
return [docstring]
else:
@@ -2280,7 +2279,6 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_autodocumenter(ClassDocumenter)
app.add_autodocumenter(ExceptionDocumenter)
app.add_autodocumenter(DataDocumenter)
- app.add_autodocumenter(DataDeclarationDocumenter)
app.add_autodocumenter(GenericAliasDocumenter)
app.add_autodocumenter(TypeVarDocumenter)
app.add_autodocumenter(FunctionDocumenter)
diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py
index df5614f99..e61874c47 100644
--- a/sphinx/ext/autodoc/importer.py
+++ b/sphinx/ext/autodoc/importer.py
@@ -15,7 +15,7 @@ from typing import Any, Callable, Dict, List, Mapping, NamedTuple, Optional, Tup
from sphinx.pycode import ModuleAnalyzer
from sphinx.util import logging
-from sphinx.util.inspect import isclass, isenumclass, safe_getattr
+from sphinx.util.inspect import getslots, isclass, isenumclass, safe_getattr
if False:
# For type annotation
@@ -203,14 +203,15 @@ def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
members[name] = Attribute(name, True, value)
# members in __slots__
- if isclass(subject) and getattr(subject, '__slots__', None) is not None:
- from sphinx.ext.autodoc import SLOTSATTR
-
- slots = subject.__slots__
- if isinstance(slots, str):
- slots = [slots]
- for name in slots:
- members[name] = Attribute(name, True, SLOTSATTR)
+ try:
+ __slots__ = getslots(subject)
+ if __slots__:
+ from sphinx.ext.autodoc import SLOTSATTR
+
+ for name in __slots__:
+ members[name] = Attribute(name, True, SLOTSATTR)
+ except (TypeError, ValueError):
+ pass
# other members
for name in dir(subject):
diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py
index a098177a4..a66aa350a 100644
--- a/sphinx/ext/autosummary/generate.py
+++ b/sphinx/ext/autosummary/generate.py
@@ -81,8 +81,7 @@ class AutosummaryEntry(NamedTuple):
def setup_documenters(app: Any) -> None:
- from sphinx.ext.autodoc import (AttributeDocumenter, ClassDocumenter,
- DataDeclarationDocumenter, DataDocumenter,
+ from sphinx.ext.autodoc import (AttributeDocumenter, ClassDocumenter, DataDocumenter,
DecoratorDocumenter, ExceptionDocumenter,
FunctionDocumenter, GenericAliasDocumenter,
InstanceAttributeDocumenter, MethodDocumenter,
@@ -92,8 +91,7 @@ def setup_documenters(app: Any) -> None:
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter,
- SlotsAttributeDocumenter, DataDeclarationDocumenter, GenericAliasDocumenter,
- SingledispatchFunctionDocumenter,
+ SlotsAttributeDocumenter, GenericAliasDocumenter, SingledispatchFunctionDocumenter,
] # type: List[Type[Documenter]]
for documenter in documenters:
app.registry.add_documenter(documenter.objtype, documenter)
diff --git a/sphinx/themes/basic/search.html b/sphinx/themes/basic/search.html
index 2673369f2..cf574f8d5 100644
--- a/sphinx/themes/basic/search.html
+++ b/sphinx/themes/basic/search.html
@@ -12,6 +12,7 @@
{%- block scripts %}
{{ super() }}
<script src="{{ pathto('_static/searchtools.js', 1) }}"></script>
+ <script src="{{ pathto('_static/language_data.js', 1) }}"></script>
{%- endblock %}
{% block extrahead %}
<script src="{{ pathto('searchindex.js', 1) }}" defer></script>
diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py
index d1e9c4435..beaed6099 100644
--- a/sphinx/util/inspect.py
+++ b/sphinx/util/inspect.py
@@ -20,7 +20,7 @@ import warnings
from functools import partial, partialmethod
from inspect import Parameter, isclass, ismethod, ismethoddescriptor, ismodule # NOQA
from io import StringIO
-from typing import Any, Callable, Dict, cast
+from typing import Any, Callable, Dict, Optional, Sequence, cast
from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.pycode.ast import ast # for py36-37
@@ -107,7 +107,11 @@ def getargspec(func: Callable) -> Any:
def unwrap(obj: Any) -> Any:
"""Get an original object from wrapped object (wrapped functions)."""
try:
- return inspect.unwrap(obj)
+ if hasattr(obj, '__sphinx_mock__'):
+ # Skip unwrapping mock object to avoid RecursionError
+ return obj
+ else:
+ return inspect.unwrap(obj)
except ValueError:
# might be a mock object
return obj
@@ -133,6 +137,43 @@ def unwrap_all(obj: Any, *, stop: Callable = None) -> Any:
return obj
+def getall(obj: Any) -> Optional[Sequence[str]]:
+ """Get __all__ attribute of the module as dict.
+
+ Return None if given *obj* does not have __all__.
+ Raises ValueError if given *obj* have invalid __all__.
+ """
+ __all__ = safe_getattr(obj, '__all__', None)
+ if __all__ is None:
+ return None
+ else:
+ if (isinstance(__all__, (list, tuple)) and all(isinstance(e, str) for e in __all__)):
+ return __all__
+ else:
+ raise ValueError(__all__)
+
+
+def getslots(obj: Any) -> Optional[Dict]:
+ """Get __slots__ attribute of the class as dict.
+
+ Return None if gienv *obj* does not have __slots__.
+ """
+ if not inspect.isclass(obj):
+ raise TypeError
+
+ __slots__ = safe_getattr(obj, '__slots__', None)
+ if __slots__ is None:
+ return None
+ elif isinstance(__slots__, dict):
+ return __slots__
+ elif isinstance(__slots__, str):
+ return {__slots__: None}
+ elif isinstance(__slots__, (list, tuple)):
+ return {e: None for e in __slots__}
+ else:
+ raise ValueError
+
+
def isenumclass(x: Any) -> bool:
"""Check if the object is subclass of enum."""
return inspect.isclass(x) and issubclass(x, enum.Enum)
diff --git a/sphinx/util/requests.py b/sphinx/util/requests.py
index b3fc8bc35..ca570249a 100644
--- a/sphinx/util/requests.py
+++ b/sphinx/util/requests.py
@@ -18,6 +18,7 @@ import requests
import sphinx
from sphinx.config import Config
+from sphinx.deprecation import RemovedInSphinx50Warning
try:
from requests.packages.urllib3.exceptions import SSLError
@@ -43,6 +44,10 @@ useragent_header = [('User-Agent',
def is_ssl_error(exc: Exception) -> bool:
"""Check an exception is SSLError."""
+ warnings.warn(
+ "is_ssl_error() is outdated and likely returns incorrect results "
+ "for modern versions of Requests.",
+ RemovedInSphinx50Warning)
if isinstance(exc, SSLError):
return True
else:
diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py
index 55af67829..9daa4b28a 100644
--- a/sphinx/util/typing.py
+++ b/sphinx/util/typing.py
@@ -57,6 +57,29 @@ TitleGetter = Callable[[nodes.Node], str]
Inventory = Dict[str, Dict[str, Tuple[str, str, str, str]]]
+def get_type_hints(obj: Any, globalns: Dict = None, localns: Dict = None) -> Dict[str, Any]:
+ """Return a dictionary containing type hints for a function, method, module or class object.
+
+ This is a simple wrapper of `typing.get_type_hints()` that does not raise an error on
+ runtime.
+ """
+ from sphinx.util.inspect import safe_getattr # lazy loading
+
+ try:
+ return typing.get_type_hints(obj, None, localns)
+ except NameError:
+ # Failed to evaluate ForwardRef (maybe TYPE_CHECKING)
+ return safe_getattr(obj, '__annotations__', {})
+ except TypeError:
+ return {}
+ except KeyError:
+ # a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084)
+ return {}
+ except AttributeError:
+ # AttributeError is raised on 3.5.2 (fixed by 3.5.3)
+ return {}
+
+
def is_system_TypeVar(typ: Any) -> bool:
"""Check *typ* is system defined TypeVar."""
modname = getattr(typ, '__module__', '')