Merge branch '4.x'

This commit is contained in:
Takeshi KOMIYA
2021-11-28 01:09:10 +09:00
10 changed files with 137 additions and 28 deletions

24
CHANGES
View File

@@ -43,6 +43,10 @@ Features added
* #9815: html theme: Wrap sidebar components in div to allow customizing their
layout via CSS
* #9831: Autosummary now documents only the members specified in a module's
``__all__`` attribute if :confval:`autosummary_ignore_module_all` is set to
``False``. The default behaviour is unchanged. Autogen also now supports
this behavior with the ``--respect-module-all`` switch.
Bugs fixed
----------
@@ -54,7 +58,7 @@ Bugs fixed
Testing
--------
Release 4.3.1 (in development)
Release 4.3.2 (in development)
==============================
Dependencies
@@ -69,6 +73,18 @@ Deprecated
Features added
--------------
Bugs fixed
----------
Testing
--------
Release 4.3.1 (released Nov 28, 2021)
=====================================
Features added
--------------
* #9864: mathjax: Support chnaging the loading method of MathJax to "defer" via
:confval:`mathjax_options`
@@ -79,14 +95,14 @@ Bugs fixed
decorated by functools.lru_cache
* #9879: autodoc: AttributeError is raised on building document for an object
having invalid __doc__ atribute
* #9844: autodoc: Failed to process a function wrapped with functools.partial if
:confval:`autodoc_preserve_defaults` enabled
* #9872: html: Class namespace collision between autodoc signatures and
docutils-0.17
* #9868: imgmath: Crashed if the dvisvgm command failed to convert equation
* #9864: mathjax: Failed to render equations via MathJax v2. The loading method
of MathJax is back to "async" method again
Testing
--------
Release 4.3.0 (released Nov 11, 2021)
=====================================

View File

@@ -39,6 +39,10 @@ Options
Document imported members.
.. option:: -a, --respect-module-all
Document exactly the members in a module's ``__all__`` attribute.
Example
-------

View File

@@ -201,6 +201,25 @@ also use these config values:
.. versionadded:: 2.1
.. versionchanged:: 4.4
If ``autosummary_ignore_module_all`` is ``False``, this configuration
value is ignored for members listed in ``__all__``.
.. confval:: autosummary_ignore_module_all
If ``False`` and a module has the ``__all__`` attribute set, autosummary
documents every member listed in ``__all__`` and no others. Default is
``True``
Note that if an imported member is listed in ``__all__``, it will be
documented regardless of the value of ``autosummary_imported_members``. To
match the behaviour of ``from module import *``, set
``autosummary_ignore_module_all`` to False and
``autosummary_imported_members`` to True.
.. versionadded:: 4.4
.. confval:: autosummary_filename_map
A dict mapping object names to filenames. This is necessary to avoid

View File

@@ -73,7 +73,7 @@ def update_defvalue(app: Sphinx, obj: Any, bound_method: bool) -> None:
lines = inspect.getsource(obj).splitlines()
if lines[0].startswith((' ', r'\t')):
lines.insert(0, '') # insert a dummy line to follow what get_function_def() does.
except OSError:
except (OSError, TypeError):
lines = []
try:

View File

@@ -826,5 +826,6 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('autosummary_mock_imports',
lambda config: config.autodoc_mock_imports, 'env')
app.add_config_value('autosummary_imported_members', [], False, [bool])
app.add_config_value('autosummary_ignore_module_all', True, 'env', bool)
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}

View File

@@ -28,7 +28,7 @@ import sys
import warnings
from gettext import NullTranslations
from os import path
from typing import Any, Dict, List, NamedTuple, Set, Tuple, Type, Union
from typing import Any, Dict, List, NamedTuple, Sequence, Set, Tuple, Type, Union
from jinja2 import TemplateNotFound
from jinja2.sandbox import SandboxedEnvironment
@@ -46,7 +46,7 @@ from sphinx.locale import __
from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.registry import SphinxComponentRegistry
from sphinx.util import logging, rst, split_full_qualified_name
from sphinx.util.inspect import safe_getattr
from sphinx.util.inspect import getall, safe_getattr
from sphinx.util.osutil import ensuredir
from sphinx.util.template import SphinxTemplateLoader
@@ -68,6 +68,7 @@ class DummyApplication:
self.config.add('autosummary_context', {}, True, None)
self.config.add('autosummary_filename_map', {}, True, None)
self.config.add('autosummary_ignore_module_all', True, 'env', bool)
self.config.init_values()
def emit_firstresult(self, *args: Any) -> None:
@@ -192,7 +193,7 @@ class ModuleScanner:
def scan(self, imported_members: bool) -> List[str]:
members = []
for name in dir(self.object):
for name in members_of(self.object, self.app.config):
try:
value = safe_getattr(self.object, name)
except AttributeError:
@@ -212,16 +213,31 @@ class ModuleScanner:
except AttributeError:
imported = False
respect_module_all = not self.app.config.autosummary_ignore_module_all
if imported_members:
# list all members up
members.append(name)
elif imported is False:
# list not-imported members up
# list not-imported members
members.append(name)
elif '__all__' in dir(self.object) and respect_module_all:
# list members that have __all__ set
members.append(name)
return members
def members_of(obj: Any, conf: Config) -> Sequence[str]:
"""Get the members of ``obj``, possibly ignoring the ``__all__`` module attribute
Follows the ``conf.autosummary_ignore_module_all`` setting."""
if conf.autosummary_ignore_module_all:
return dir(obj)
else:
return getall(obj) or dir(obj)
def generate_autosummary_content(name: str, obj: Any, parent: Any,
template: AutosummaryRenderer, template_name: str,
imported_members: bool, app: Any,
@@ -245,7 +261,7 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
def get_module_members(obj: Any) -> Dict[str, Any]:
members = {}
for name in dir(obj):
for name in members_of(obj, app.config):
try:
members[name] = safe_getattr(obj, name)
except AttributeError:
@@ -630,6 +646,10 @@ The format of the autosummary directive is documented in the
dest='imported_members', default=False,
help=__('document imported members (default: '
'%(default)s)'))
parser.add_argument('-a', '--respect-module-all', action='store_true',
dest='respect_module_all', default=False,
help=__('document exactly the members in module __all__ attribute. '
'(default: %(default)s)'))
return parser
@@ -646,6 +666,7 @@ def main(argv: List[str] = sys.argv[1:]) -> None:
if args.templates:
app.config.templates_path.append(path.abspath(args.templates))
app.config.autosummary_ignore_module_all = not args.respect_module_all # type: ignore
generate_autosummary_docs(args.source_file, args.output_dir,
'.' + args.suffix,

View File

@@ -12,7 +12,6 @@ import posixpath
import re
import shutil
import subprocess
import sys
import tempfile
from os import path
from subprocess import PIPE, CalledProcessError
@@ -43,11 +42,11 @@ templates_path = path.join(package_dir, 'templates', 'imgmath')
class MathExtError(SphinxError):
category = 'Math extension error'
def __init__(self, msg: str, stderr: bytes = None, stdout: bytes = None) -> None:
def __init__(self, msg: str, stderr: str = None, stdout: str = None) -> None:
if stderr:
msg += '\n[stderr]\n' + stderr.decode(sys.getdefaultencoding(), 'replace')
msg += '\n[stderr]\n' + stderr
if stdout:
msg += '\n[stdout]\n' + stdout.decode(sys.getdefaultencoding(), 'replace')
msg += '\n[stdout]\n' + stdout
super().__init__(msg)
@@ -135,7 +134,8 @@ def compile_math(latex: str, builder: Builder) -> str:
command.append('math.tex')
try:
subprocess.run(command, stdout=PIPE, stderr=PIPE, cwd=tempdir, check=True)
subprocess.run(command, stdout=PIPE, stderr=PIPE, cwd=tempdir, check=True,
encoding='ascii')
return path.join(tempdir, 'math.dvi')
except OSError as exc:
logger.warning(__('LaTeX command %r cannot be run (needed for math '

View File

@@ -1,6 +1,16 @@
from os import path # NOQA
from typing import Union
__all__ = [
"CONSTANT1",
"Exc",
"Foo",
"_Baz",
"bar",
"qux",
"path",
]
#: module variable
CONSTANT1 = None
CONSTANT2 = None
@@ -48,3 +58,5 @@ class _Exc(Exception):
#: a module-level attribute
qux = 2
#: a module-level attribute that has been excluded from __all__
quuz = 2

View File

@@ -216,14 +216,42 @@ def test_autosummary_generate_content_for_module(app):
context = template.render.call_args[0][1]
assert context['members'] == ['CONSTANT1', 'CONSTANT2', 'Exc', 'Foo', '_Baz', '_Exc',
'__builtins__', '__cached__', '__doc__', '__file__',
'__name__', '__package__', '_quux', 'bar', 'qux']
'__all__', '__builtins__', '__cached__', '__doc__',
'__file__', '__name__', '__package__', '_quux', 'bar',
'quuz', 'qux']
assert context['functions'] == ['bar']
assert context['all_functions'] == ['_quux', 'bar']
assert context['classes'] == ['Foo']
assert context['all_classes'] == ['Foo', '_Baz']
assert context['exceptions'] == ['Exc']
assert context['all_exceptions'] == ['Exc', '_Exc']
assert context['attributes'] == ['CONSTANT1', 'qux', 'quuz']
assert context['all_attributes'] == ['CONSTANT1', 'qux', 'quuz']
assert context['fullname'] == 'autosummary_dummy_module'
assert context['module'] == 'autosummary_dummy_module'
assert context['objname'] == ''
assert context['name'] == ''
assert context['objtype'] == 'module'
@pytest.mark.sphinx(testroot='ext-autosummary')
def test_autosummary_generate_content_for_module___all__(app):
import autosummary_dummy_module
template = Mock()
app.config.autosummary_ignore_module_all = False
generate_autosummary_content('autosummary_dummy_module', autosummary_dummy_module, None,
template, None, False, app, False, {})
assert template.render.call_args[0][0] == 'module'
context = template.render.call_args[0][1]
assert context['members'] == ['CONSTANT1', 'Exc', 'Foo', '_Baz', 'bar', 'qux', 'path']
assert context['functions'] == ['bar']
assert context['all_functions'] == ['bar']
assert context['classes'] == ['Foo']
assert context['all_classes'] == ['Foo', '_Baz']
assert context['exceptions'] == ['Exc']
assert context['all_exceptions'] == ['Exc']
assert context['attributes'] == ['CONSTANT1', 'qux']
assert context['all_attributes'] == ['CONSTANT1', 'qux']
assert context['fullname'] == 'autosummary_dummy_module'
@@ -246,9 +274,9 @@ def test_autosummary_generate_content_for_module_skipped(app):
generate_autosummary_content('autosummary_dummy_module', autosummary_dummy_module, None,
template, None, False, app, False, {})
context = template.render.call_args[0][1]
assert context['members'] == ['CONSTANT1', 'CONSTANT2', '_Baz', '_Exc', '__builtins__',
'__cached__', '__doc__', '__file__', '__name__',
'__package__', '_quux', 'qux']
assert context['members'] == ['CONSTANT1', 'CONSTANT2', '_Baz', '_Exc', '__all__',
'__builtins__', '__cached__', '__doc__', '__file__',
'__name__', '__package__', '_quux', 'quuz', 'qux']
assert context['functions'] == []
assert context['classes'] == []
assert context['exceptions'] == []
@@ -265,17 +293,17 @@ def test_autosummary_generate_content_for_module_imported_members(app):
context = template.render.call_args[0][1]
assert context['members'] == ['CONSTANT1', 'CONSTANT2', 'Exc', 'Foo', 'Union', '_Baz',
'_Exc', '__builtins__', '__cached__', '__doc__',
'_Exc', '__all__', '__builtins__', '__cached__', '__doc__',
'__file__', '__loader__', '__name__', '__package__',
'__spec__', '_quux', 'bar', 'path', 'qux']
'__spec__', '_quux', 'bar', 'path', 'quuz', 'qux']
assert context['functions'] == ['bar']
assert context['all_functions'] == ['_quux', 'bar']
assert context['classes'] == ['Foo']
assert context['all_classes'] == ['Foo', '_Baz']
assert context['exceptions'] == ['Exc']
assert context['all_exceptions'] == ['Exc', '_Exc']
assert context['attributes'] == ['CONSTANT1', 'qux']
assert context['all_attributes'] == ['CONSTANT1', 'qux']
assert context['attributes'] == ['CONSTANT1', 'qux', 'quuz']
assert context['all_attributes'] == ['CONSTANT1', 'qux', 'quuz']
assert context['fullname'] == 'autosummary_dummy_module'
assert context['module'] == 'autosummary_dummy_module'
assert context['objname'] == ''
@@ -313,6 +341,7 @@ def test_autosummary_generate(app, status, warning):
assert doctree[3][0][0][2][5].astext() == 'autosummary_dummy_module.qux\n\na module-level attribute'
module = (app.srcdir / 'generated' / 'autosummary_dummy_module.rst').read_text()
assert (' .. autosummary::\n'
' \n'
' Foo\n'
@@ -321,6 +350,7 @@ def test_autosummary_generate(app, status, warning):
' \n'
' CONSTANT1\n'
' qux\n'
' quuz\n'
' \n' in module)
Foo = (app.srcdir / 'generated' / 'autosummary_dummy_module.Foo.rst').read_text()

View File

@@ -16,7 +16,6 @@ import sys
import types
from inspect import Parameter
import _testcapi
import pytest
from sphinx.util import inspect
@@ -627,8 +626,6 @@ def test_isattributedescriptor(app):
def __get__(self, obj, typ=None):
pass
testinstancemethod = _testcapi.instancemethod(str.__repr__)
assert inspect.isattributedescriptor(Base.prop) is True # property
assert inspect.isattributedescriptor(Base.meth) is False # method
assert inspect.isattributedescriptor(Base.staticmeth) is False # staticmethod
@@ -639,7 +636,16 @@ def test_isattributedescriptor(app):
assert inspect.isattributedescriptor(dict.__dict__['fromkeys']) is False # ClassMethodDescriptorType # NOQA
assert inspect.isattributedescriptor(types.FrameType.f_locals) is True # GetSetDescriptorType # NOQA
assert inspect.isattributedescriptor(datetime.timedelta.days) is True # MemberDescriptorType # NOQA
assert inspect.isattributedescriptor(testinstancemethod) is False # instancemethod (C-API) # NOQA
try:
# _testcapi module cannot be importable in some distro
# refs: https://github.com/sphinx-doc/sphinx/issues/9868
import _testcapi
testinstancemethod = _testcapi.instancemethod(str.__repr__)
assert inspect.isattributedescriptor(testinstancemethod) is False # instancemethod (C-API) # NOQA
except ImportError:
pass
def test_isproperty(app):