mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '3.x'
This commit is contained in:
7
CHANGES
7
CHANGES
@@ -75,6 +75,7 @@ Features added
|
||||
* #8022: autodoc: autodata and autoattribute directives does not show right-hand
|
||||
value of the variable if docstring contains ``:meta hide-value:`` in
|
||||
info-field-list
|
||||
* #8619: html: kbd role generates customizable HTML tags for compound keys
|
||||
* #8132: Add :confval:`project_copyright` as an alias of :confval:`copyright`
|
||||
|
||||
Bugs fixed
|
||||
@@ -84,10 +85,12 @@ Bugs fixed
|
||||
class
|
||||
* #8592: autodoc: ``:meta public:`` does not effect to variables
|
||||
* #8594: autodoc: empty __all__ attribute is ignored
|
||||
* #8315: autodoc: Failed to resolve struct.Struct type annotation
|
||||
* #8306: autosummary: mocked modules are documented as empty page when using
|
||||
:recursive: option
|
||||
* #8618: html: kbd role produces incorrect HTML when compound-key separators (-,
|
||||
+ or ^) are used as keystrokes
|
||||
* #8629: html: A type warning for html_use_opensearch is shown twice
|
||||
* #8094: texinfo: image files on the different directory with document are not
|
||||
copied
|
||||
|
||||
@@ -113,6 +116,10 @@ Bugs fixed
|
||||
----------
|
||||
|
||||
* #8164: autodoc: Classes that inherit mocked class are not documented
|
||||
* #8602: autodoc: The ``autodoc-process-docstring`` event is emitted to the
|
||||
non-datadescriptors unexpectedly
|
||||
* #8616: autodoc: AttributeError is raised on non-class object is passed to
|
||||
autoclass directive
|
||||
|
||||
Testing
|
||||
--------
|
||||
|
||||
@@ -452,9 +452,6 @@ class StandaloneHTMLBuilder(Builder):
|
||||
logo = path.basename(self.config.html_logo) if self.config.html_logo else ''
|
||||
favicon = path.basename(self.config.html_favicon) if self.config.html_favicon else ''
|
||||
|
||||
if not isinstance(self.config.html_use_opensearch, str):
|
||||
logger.warning(__('html_use_opensearch config value must now be a string'))
|
||||
|
||||
self.relations = self.env.collect_relations()
|
||||
|
||||
rellinks = [] # type: List[Tuple[str, str, str, str]]
|
||||
|
||||
@@ -28,7 +28,7 @@ class KeyboardTransform(SphinxPostTransform):
|
||||
|
||||
After::
|
||||
|
||||
<literal class="kbd">
|
||||
<literal class="kbd compound">
|
||||
<literal class="kbd">
|
||||
Control
|
||||
-
|
||||
@@ -46,6 +46,7 @@ class KeyboardTransform(SphinxPostTransform):
|
||||
if len(parts) == 1:
|
||||
continue
|
||||
|
||||
node['classes'].append('compound')
|
||||
node.pop()
|
||||
while parts:
|
||||
key = parts.pop(0)
|
||||
|
||||
@@ -1180,6 +1180,8 @@ class DocstringSignatureMixin:
|
||||
valid_names.extend(cls.__name__ for cls in self.object.__mro__)
|
||||
|
||||
docstrings = self.get_doc()
|
||||
if docstrings is None:
|
||||
return None, None
|
||||
self._new_docstrings = docstrings[:]
|
||||
self._signatures = []
|
||||
result = None
|
||||
@@ -1671,7 +1673,10 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
||||
def add_content(self, more_content: Optional[StringList], no_docstring: bool = False
|
||||
) -> None:
|
||||
if self.doc_as_attr:
|
||||
more_content = StringList([_('alias of %s') % restify(self.object)], source='')
|
||||
try:
|
||||
more_content = StringList([_('alias of %s') % restify(self.object)], source='')
|
||||
except AttributeError:
|
||||
pass # Invalid class object is passed.
|
||||
|
||||
super().add_content(more_content)
|
||||
|
||||
@@ -2137,15 +2142,24 @@ class NonDataDescriptorMixin(DataDocumenterMixinBase):
|
||||
and :value: header will be suppressed unexpectedly.
|
||||
"""
|
||||
|
||||
def import_object(self, raiseerror: bool = False) -> bool:
|
||||
ret = super().import_object(raiseerror) # type: ignore
|
||||
if ret and not inspect.isattributedescriptor(self.object):
|
||||
self.non_data_descriptor = True
|
||||
else:
|
||||
self.non_data_descriptor = False
|
||||
|
||||
return ret
|
||||
|
||||
def should_suppress_value_header(self) -> bool:
|
||||
return (inspect.isattributedescriptor(self.object) or
|
||||
return (not getattr(self, 'non_data_descriptor', False) or
|
||||
super().should_suppress_directive_header())
|
||||
|
||||
def get_doc(self, ignore: int = None) -> Optional[List[List[str]]]:
|
||||
if not inspect.isattributedescriptor(self.object):
|
||||
if getattr(self, 'non_data_descriptor', False):
|
||||
# the docstring of non datadescriptor is very probably the wrong thing
|
||||
# to display
|
||||
return []
|
||||
return None
|
||||
else:
|
||||
return super().get_doc(ignore) # type: ignore
|
||||
|
||||
@@ -2298,6 +2312,12 @@ class UninitializedInstanceAttributeMixin(DataDocumenterMixinBase):
|
||||
return (self.object is UNINITIALIZED_ATTR or
|
||||
super().should_suppress_value_header())
|
||||
|
||||
def get_doc(self, ignore: int = None) -> Optional[List[List[str]]]:
|
||||
if self.object is UNINITIALIZED_ATTR:
|
||||
return None
|
||||
else:
|
||||
return super().get_doc(ignore) # type: ignore
|
||||
|
||||
|
||||
class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type: ignore
|
||||
TypeVarMixin, RuntimeInstanceAttributeMixin,
|
||||
@@ -2398,9 +2418,10 @@ class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type:
|
||||
return True
|
||||
else:
|
||||
doc = self.get_doc()
|
||||
metadata = extract_metadata('\n'.join(sum(doc, [])))
|
||||
if 'hide-value' in metadata:
|
||||
return True
|
||||
if doc:
|
||||
metadata = extract_metadata('\n'.join(sum(doc, [])))
|
||||
if 'hide-value' in metadata:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
import sys
|
||||
import typing
|
||||
from struct import Struct
|
||||
from typing import Any, Callable, Dict, Generator, List, Optional, Tuple, TypeVar, Union
|
||||
|
||||
from docutils import nodes
|
||||
@@ -91,6 +92,9 @@ def restify(cls: Optional["Type"]) -> str:
|
||||
return ':obj:`None`'
|
||||
elif cls is Ellipsis:
|
||||
return '...'
|
||||
elif cls is Struct:
|
||||
# Before Python 3.9, struct.Struct class has incorrect __module__.
|
||||
return ':class:`struct.Struct`'
|
||||
elif inspect.isNewType(cls):
|
||||
return ':class:`%s`' % cls.__name__
|
||||
elif cls.__module__ in ('__builtin__', 'builtins'):
|
||||
@@ -259,6 +263,9 @@ def stringify(annotation: Any) -> str:
|
||||
return annotation.__qualname__
|
||||
elif annotation is Ellipsis:
|
||||
return '...'
|
||||
elif annotation is Struct:
|
||||
# Before Python 3.9, struct.Struct class has incorrect __module__.
|
||||
return 'struct.Struct'
|
||||
|
||||
if sys.version_info >= (3, 7): # py37+
|
||||
return _stringify_py37(annotation)
|
||||
|
||||
@@ -34,6 +34,23 @@ def test_process_docstring(app):
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_process_docstring_for_nondatadescriptor(app):
|
||||
def on_process_docstring(app, what, name, obj, options, lines):
|
||||
raise
|
||||
|
||||
app.connect('autodoc-process-docstring', on_process_docstring)
|
||||
|
||||
actual = do_autodoc(app, 'attribute', 'target.AttCls.a1')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:attribute:: AttCls.a1',
|
||||
' :module: target',
|
||||
' :value: hello world',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_cut_lines(app):
|
||||
app.connect('autodoc-process-docstring',
|
||||
|
||||
@@ -378,48 +378,45 @@ def test_autosummary_generate_overwrite2(app_params, make_app):
|
||||
|
||||
|
||||
@pytest.mark.sphinx('dummy', testroot='ext-autosummary-recursive')
|
||||
@pytest.mark.usefixtures("rollback_sysmodules")
|
||||
def test_autosummary_recursive(app, status, warning):
|
||||
try:
|
||||
app.build()
|
||||
sys.modules.pop('package', None) # unload target module to clear the module cache
|
||||
|
||||
# autosummary having :recursive: option
|
||||
assert (app.srcdir / 'generated' / 'package.rst').exists()
|
||||
assert (app.srcdir / 'generated' / 'package.module.rst').exists()
|
||||
assert (app.srcdir / 'generated' / 'package.module_importfail.rst').exists() is False
|
||||
assert (app.srcdir / 'generated' / 'package.package.rst').exists()
|
||||
assert (app.srcdir / 'generated' / 'package.package.module.rst').exists()
|
||||
app.build()
|
||||
|
||||
# autosummary not having :recursive: option
|
||||
assert (app.srcdir / 'generated' / 'package2.rst').exists()
|
||||
assert (app.srcdir / 'generated' / 'package2.module.rst').exists() is False
|
||||
# autosummary having :recursive: option
|
||||
assert (app.srcdir / 'generated' / 'package.rst').exists()
|
||||
assert (app.srcdir / 'generated' / 'package.module.rst').exists()
|
||||
assert (app.srcdir / 'generated' / 'package.module_importfail.rst').exists() is False
|
||||
assert (app.srcdir / 'generated' / 'package.package.rst').exists()
|
||||
assert (app.srcdir / 'generated' / 'package.package.module.rst').exists()
|
||||
|
||||
# Check content of recursively generated stub-files
|
||||
content = (app.srcdir / 'generated' / 'package.rst').read_text()
|
||||
assert 'package.module' in content
|
||||
assert 'package.package' in content
|
||||
assert 'package.module_importfail' in content
|
||||
# autosummary not having :recursive: option
|
||||
assert (app.srcdir / 'generated' / 'package2.rst').exists()
|
||||
assert (app.srcdir / 'generated' / 'package2.module.rst').exists() is False
|
||||
|
||||
content = (app.srcdir / 'generated' / 'package.package.rst').read_text()
|
||||
assert 'package.package.module' in content
|
||||
finally:
|
||||
sys.modules.pop('package.package', None)
|
||||
sys.modules.pop('package.package.module', None)
|
||||
# Check content of recursively generated stub-files
|
||||
content = (app.srcdir / 'generated' / 'package.rst').read_text()
|
||||
assert 'package.module' in content
|
||||
assert 'package.package' in content
|
||||
assert 'package.module_importfail' in content
|
||||
|
||||
content = (app.srcdir / 'generated' / 'package.package.rst').read_text()
|
||||
assert 'package.package.module' in content
|
||||
|
||||
|
||||
@pytest.mark.sphinx('dummy', testroot='ext-autosummary-recursive',
|
||||
srcdir='test_autosummary_recursive_skips_mocked_modules',
|
||||
confoverrides={'autosummary_mock_imports': ['package.package']})
|
||||
@pytest.mark.usefixtures("rollback_sysmodules")
|
||||
def test_autosummary_recursive_skips_mocked_modules(app, status, warning):
|
||||
try:
|
||||
app.build()
|
||||
sys.modules.pop('package', None) # unload target module to clear the module cache
|
||||
app.build()
|
||||
|
||||
assert (app.srcdir / 'generated' / 'package.rst').exists()
|
||||
assert (app.srcdir / 'generated' / 'package.module.rst').exists()
|
||||
assert (app.srcdir / 'generated' / 'package.package.rst').exists() is False
|
||||
assert (app.srcdir / 'generated' / 'package.package.module.rst').exists() is False
|
||||
finally:
|
||||
sys.modules.pop('package.package', None)
|
||||
sys.modules.pop('package.package.module', None)
|
||||
assert (app.srcdir / 'generated' / 'package.rst').exists()
|
||||
assert (app.srcdir / 'generated' / 'package.module.rst').exists()
|
||||
assert (app.srcdir / 'generated' / 'package.package.rst').exists() is False
|
||||
assert (app.srcdir / 'generated' / 'package.package.module.rst').exists() is False
|
||||
|
||||
|
||||
@pytest.mark.sphinx('dummy', testroot='ext-autosummary-filename-map')
|
||||
|
||||
@@ -244,7 +244,7 @@ def get_verifier(verify, verify_re):
|
||||
# kbd role
|
||||
'verify',
|
||||
':kbd:`Control+X`',
|
||||
('<p><kbd class="kbd docutils literal notranslate">'
|
||||
('<p><kbd class="kbd compound docutils literal notranslate">'
|
||||
'<kbd class="kbd docutils literal notranslate">Control</kbd>'
|
||||
'+'
|
||||
'<kbd class="kbd docutils literal notranslate">X</kbd>'
|
||||
@@ -255,7 +255,7 @@ def get_verifier(verify, verify_re):
|
||||
# kbd role
|
||||
'verify',
|
||||
':kbd:`Alt+^`',
|
||||
('<p><kbd class="kbd docutils literal notranslate">'
|
||||
('<p><kbd class="kbd compound docutils literal notranslate">'
|
||||
'<kbd class="kbd docutils literal notranslate">Alt</kbd>'
|
||||
'+'
|
||||
'<kbd class="kbd docutils literal notranslate">^</kbd>'
|
||||
@@ -266,7 +266,7 @@ def get_verifier(verify, verify_re):
|
||||
# kbd role
|
||||
'verify',
|
||||
':kbd:`M-x M-s`',
|
||||
('<p><kbd class="kbd docutils literal notranslate">'
|
||||
('<p><kbd class="kbd compound docutils literal notranslate">'
|
||||
'<kbd class="kbd docutils literal notranslate">M</kbd>'
|
||||
'-'
|
||||
'<kbd class="kbd docutils literal notranslate">x</kbd>'
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
import sys
|
||||
from numbers import Integral
|
||||
from struct import Struct
|
||||
from typing import (Any, Callable, Dict, Generator, List, NewType, Optional, Tuple, TypeVar,
|
||||
Union)
|
||||
|
||||
@@ -43,6 +44,7 @@ def test_restify():
|
||||
assert restify(str) == ":class:`str`"
|
||||
assert restify(None) == ":obj:`None`"
|
||||
assert restify(Integral) == ":class:`numbers.Integral`"
|
||||
assert restify(Struct) == ":class:`struct.Struct`"
|
||||
assert restify(Any) == ":obj:`Any`"
|
||||
|
||||
|
||||
@@ -124,6 +126,7 @@ def test_stringify():
|
||||
assert stringify(str) == "str"
|
||||
assert stringify(None) == "None"
|
||||
assert stringify(Integral) == "numbers.Integral"
|
||||
assert restify(Struct) == ":class:`struct.Struct`"
|
||||
assert stringify(Any) == "Any"
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user