mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '4.x'
This commit is contained in:
commit
8350736c27
51
.github/workflows/transifex.yml
vendored
Normal file
51
.github/workflows/transifex.yml
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
name: Sync translations on repository and transifex.com
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * SUN"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
push:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: 4.x
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
- name: Install dependencies
|
||||
run: pip install -U babel jinja2 transifex-client
|
||||
- name: Extract translations from source code
|
||||
run: python setup.py extract_messages
|
||||
- name: Push translations to transifex.com
|
||||
run: cd sphinx/locale && tx push -s --no-interactive --parallel
|
||||
env:
|
||||
TX_TOKEN: ${{ secrets.TX_TOKEN }}
|
||||
|
||||
pull:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: 4.x
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
- name: Install dependencies
|
||||
run: pip install -U babel jinja2 transifex-client
|
||||
- name: Extract translations from source code
|
||||
run: python setup.py extract_messages
|
||||
- name: Pull translations to transifex.com
|
||||
run: cd sphinx/locale && tx pull -a -f --no-interactive --parallel
|
||||
env:
|
||||
TX_TOKEN: ${{ secrets.TX_TOKEN }}
|
||||
- name: Compile message catalogs
|
||||
run: python setup.py compile_catalog
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v3
|
||||
with:
|
||||
commit-message: 'Update message catalogs'
|
||||
branch: bot/pull-translations
|
||||
title: Update message catalogs
|
7
CHANGES
7
CHANGES
@ -33,6 +33,7 @@ Incompatible changes
|
||||
Deprecated
|
||||
----------
|
||||
|
||||
* ``sphinx.application.Sphinx.html_theme``
|
||||
* ``sphinx.util.docstrings.extract_metadata()``
|
||||
|
||||
Features added
|
||||
@ -45,6 +46,9 @@ Features added
|
||||
allows you to define an alias for a class with module name like
|
||||
``foo.bar.BazClass``
|
||||
* #9175: autodoc: Special member is not documented in the module
|
||||
* #9195: autodoc: The arguments of ``typing.Literal`` are wrongly rendered
|
||||
* #9185: autodoc: :confval:`autodoc_typehints` allows ``'both'`` setting to
|
||||
allow typehints to be included both in the signature and description
|
||||
* #3257: autosummary: Support instance attributes for classes
|
||||
* #9129: html search: Show search summaries when html_copy_source = False
|
||||
* #9120: html theme: Eliminate prompt characters of code-block from copyable
|
||||
@ -62,6 +66,7 @@ Bugs fixed
|
||||
* #8872: autodoc: stacked singledispatches are wrongly rendered
|
||||
* #8597: autodoc: a docsting having metadata only should be treated as
|
||||
undocumented
|
||||
* #9185: autodoc: typehints for overloaded functions and methods are inaccurate
|
||||
|
||||
Testing
|
||||
--------
|
||||
@ -84,6 +89,8 @@ Features added
|
||||
Bugs fixed
|
||||
----------
|
||||
|
||||
* #9210: viewcode: crashed if non importable modules found on parallel build
|
||||
|
||||
Testing
|
||||
--------
|
||||
|
||||
|
@ -22,6 +22,11 @@ The following is a list of deprecated interfaces.
|
||||
- (will be) Removed
|
||||
- Alternatives
|
||||
|
||||
* - ``sphinx.application.Sphinx.html_theme``
|
||||
- 4.1
|
||||
- 6.0
|
||||
- ``sphinx.registry.SphinxComponentRegistry.html_themes``
|
||||
|
||||
* - ``sphinx.util.docstrings.extract_metadata()``
|
||||
- 4.1
|
||||
- 6.0
|
||||
|
@ -573,15 +573,27 @@ There are also config values that you can set:
|
||||
This value controls how to represent typehints. The setting takes the
|
||||
following values:
|
||||
|
||||
* ``'signature'`` -- Show typehints as its signature (default)
|
||||
* ``'description'`` -- Show typehints as content of function or method
|
||||
* ``'signature'`` -- Show typehints in the signature (default)
|
||||
* ``'description'`` -- Show typehints as content of the function or method
|
||||
The typehints of overloaded functions or methods will still be represented
|
||||
in the signature.
|
||||
* ``'none'`` -- Do not show typehints
|
||||
* ``'both'`` -- Show typehints in the signature and as content of
|
||||
the function or method
|
||||
|
||||
Overloaded functions or methods will not have typehints included in the
|
||||
description because it is impossible to accurately represent all possible
|
||||
overloads as a list of parameters.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
.. versionadded:: 3.0
|
||||
|
||||
New option ``'description'`` is added.
|
||||
|
||||
.. versionadded:: 4.1
|
||||
|
||||
New option ``'both'`` is added.
|
||||
|
||||
.. confval:: autodoc_typehints_description_target
|
||||
|
||||
This value controls whether the types of undocumented parameters and return
|
||||
|
@ -14,6 +14,7 @@ import os
|
||||
import pickle
|
||||
import platform
|
||||
import sys
|
||||
import warnings
|
||||
from collections import deque
|
||||
from io import StringIO
|
||||
from os import path
|
||||
@ -29,6 +30,7 @@ from pygments.lexer import Lexer
|
||||
import sphinx
|
||||
from sphinx import locale, package_dir
|
||||
from sphinx.config import Config
|
||||
from sphinx.deprecation import RemovedInSphinx60Warning
|
||||
from sphinx.domains import Domain, Index
|
||||
from sphinx.environment import BuildEnvironment
|
||||
from sphinx.environment.collectors import EnvironmentCollector
|
||||
@ -145,7 +147,6 @@ class Sphinx:
|
||||
self.env: Optional[BuildEnvironment] = None
|
||||
self.project: Optional[Project] = None
|
||||
self.registry = SphinxComponentRegistry()
|
||||
self.html_themes: Dict[str, str] = {}
|
||||
|
||||
# validate provided directories
|
||||
self.srcdir = abspath(srcdir)
|
||||
@ -1184,13 +1185,13 @@ class Sphinx:
|
||||
def add_html_theme(self, name: str, theme_path: str) -> None:
|
||||
"""Register a HTML Theme.
|
||||
|
||||
The *name* is a name of theme, and *path* is a full path to the theme
|
||||
(refs: :ref:`distribute-your-theme`).
|
||||
The *name* is a name of theme, and *theme_path* is a full path to the
|
||||
theme (refs: :ref:`distribute-your-theme`).
|
||||
|
||||
.. versionadded:: 1.6
|
||||
"""
|
||||
logger.debug('[app] adding HTML theme: %r, %r', name, theme_path)
|
||||
self.html_themes[name] = theme_path
|
||||
self.registry.add_html_theme(name, theme_path)
|
||||
|
||||
def add_html_math_renderer(self, name: str,
|
||||
inline_renderers: Tuple[Callable, Callable] = None,
|
||||
@ -1257,6 +1258,12 @@ class Sphinx:
|
||||
|
||||
return True
|
||||
|
||||
@property
|
||||
def html_themes(self) -> Dict[str, str]:
|
||||
warnings.warn('app.html_themes is deprecated.',
|
||||
RemovedInSphinx60Warning)
|
||||
return self.registry.html_themes
|
||||
|
||||
|
||||
class TemplateBridge:
|
||||
"""
|
||||
|
@ -1315,7 +1315,7 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
||||
sigs = []
|
||||
if (self.analyzer and
|
||||
'.'.join(self.objpath) in self.analyzer.overloads and
|
||||
self.config.autodoc_typehints == 'signature'):
|
||||
self.config.autodoc_typehints != 'none'):
|
||||
# Use signatures for overloaded functions instead of the implementation function.
|
||||
overloaded = True
|
||||
else:
|
||||
@ -1561,7 +1561,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
||||
sigs = []
|
||||
|
||||
overloads = self.get_overloaded_signatures()
|
||||
if overloads and self.config.autodoc_typehints == 'signature':
|
||||
if overloads and self.config.autodoc_typehints != 'none':
|
||||
# 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__', {})
|
||||
@ -2109,7 +2109,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
||||
sigs = []
|
||||
if (self.analyzer and
|
||||
'.'.join(self.objpath) in self.analyzer.overloads and
|
||||
self.config.autodoc_typehints == 'signature'):
|
||||
self.config.autodoc_typehints != 'none'):
|
||||
# Use signatures for overloaded methods instead of the implementation method.
|
||||
overloaded = True
|
||||
else:
|
||||
@ -2666,7 +2666,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
app.add_config_value('autodoc_docstring_signature', True, True)
|
||||
app.add_config_value('autodoc_mock_imports', [], True)
|
||||
app.add_config_value('autodoc_typehints', "signature", True,
|
||||
ENUM("signature", "description", "none"))
|
||||
ENUM("signature", "description", "none", "both"))
|
||||
app.add_config_value('autodoc_typehints_description_target', 'all', True,
|
||||
ENUM('all', 'documented'))
|
||||
app.add_config_value('autodoc_type_aliases', {}, True)
|
||||
|
@ -40,7 +40,7 @@ def record_typehints(app: Sphinx, objtype: str, name: str, obj: Any,
|
||||
def merge_typehints(app: Sphinx, domain: str, objtype: str, contentnode: Element) -> None:
|
||||
if domain != 'py':
|
||||
return
|
||||
if app.config.autodoc_typehints != 'description':
|
||||
if app.config.autodoc_typehints not in ('both', 'description'):
|
||||
return
|
||||
|
||||
try:
|
||||
|
@ -150,10 +150,11 @@ def env_merge_info(app: Sphinx, env: BuildEnvironment, docnames: Iterable[str],
|
||||
if modname not in env._viewcode_modules: # type: ignore
|
||||
env._viewcode_modules[modname] = entry # type: ignore
|
||||
else:
|
||||
used = env._viewcode_modules[modname][2] # type: ignore
|
||||
for fullname, docname in entry[2].items():
|
||||
if fullname not in used:
|
||||
used[fullname] = docname
|
||||
if env._viewcode_modules[modname]: # type: ignore
|
||||
used = env._viewcode_modules[modname][2] # type: ignore
|
||||
for fullname, docname in entry[2].items():
|
||||
if fullname not in used:
|
||||
used[fullname] = docname
|
||||
|
||||
|
||||
def env_purge_doc(app: Sphinx, env: BuildEnvironment, docname: str) -> None:
|
||||
|
@ -23,7 +23,7 @@ from sphinx.util import logging
|
||||
from sphinx.util.osutil import mtimes_of_files
|
||||
|
||||
try:
|
||||
from jinja2.utils import pass_context # type: ignore # jinja2-3.0 or above
|
||||
from jinja2.utils import pass_context
|
||||
except ImportError:
|
||||
from jinja2 import contextfunction as pass_context
|
||||
|
||||
|
@ -93,6 +93,9 @@ class SphinxComponentRegistry:
|
||||
self.html_inline_math_renderers: Dict[str, Tuple[Callable, Callable]] = {}
|
||||
self.html_block_math_renderers: Dict[str, Tuple[Callable, Callable]] = {}
|
||||
|
||||
#: HTML themes
|
||||
self.html_themes: Dict[str, str] = {}
|
||||
|
||||
#: js_files; list of JS paths or URLs
|
||||
self.js_files: List[Tuple[str, Dict[str, Any]]] = []
|
||||
|
||||
@ -403,6 +406,9 @@ class SphinxComponentRegistry:
|
||||
self.html_inline_math_renderers[name] = inline_renderers
|
||||
self.html_block_math_renderers[name] = block_renderers
|
||||
|
||||
def add_html_theme(self, name: str, theme_path: str) -> None:
|
||||
self.html_themes[name] = theme_path
|
||||
|
||||
def load_extension(self, app: "Sphinx", extname: str) -> None:
|
||||
"""Load a Sphinx extension."""
|
||||
if extname in app.extensions: # already loaded
|
||||
|
@ -155,7 +155,7 @@ class HTMLThemeFactory:
|
||||
|
||||
def __init__(self, app: "Sphinx") -> None:
|
||||
self.app = app
|
||||
self.themes = app.html_themes
|
||||
self.themes = app.registry.html_themes
|
||||
self.load_builtin_themes()
|
||||
if getattr(app.config, 'html_theme_path', None):
|
||||
self.load_additional_themes(app.config.html_theme_path)
|
||||
|
@ -235,7 +235,7 @@ def save_traceback(app: "Sphinx") -> str:
|
||||
platform.python_version(),
|
||||
platform.python_implementation(),
|
||||
docutils.__version__, docutils.__version_details__,
|
||||
jinja2.__version__, # type: ignore
|
||||
jinja2.__version__,
|
||||
last_msgs)).encode())
|
||||
if app is not None:
|
||||
for ext in app.extensions.values():
|
||||
|
@ -24,7 +24,7 @@ from sphinx.locale import __
|
||||
from sphinx.util import docutils, logging
|
||||
|
||||
try:
|
||||
from jinja2.utils import pass_environment # type: ignore # jinja2-3.0 or above
|
||||
from jinja2.utils import pass_environment
|
||||
except ImportError:
|
||||
from jinja2 import environmentfilter as pass_environment
|
||||
|
||||
@ -61,7 +61,7 @@ def textwidth(text: str, widechars: str = 'WF') -> int:
|
||||
def heading(env: Environment, text: str, level: int = 1) -> str:
|
||||
"""Create a heading for *level*."""
|
||||
assert level <= 3
|
||||
width = textwidth(text, WIDECHARS[env.language]) # type: ignore
|
||||
width = textwidth(text, WIDECHARS[env.language])
|
||||
sectioning_char = SECTIONING_CHARS[level - 1]
|
||||
return '%s\n%s' % (text, sectioning_char * width)
|
||||
|
||||
|
@ -69,18 +69,18 @@ class Tags:
|
||||
|
||||
def eval_node(node: Node) -> bool:
|
||||
if isinstance(node, nodes.CondExpr):
|
||||
if eval_node(node.test): # type: ignore
|
||||
return eval_node(node.expr1) # type: ignore
|
||||
if eval_node(node.test):
|
||||
return eval_node(node.expr1)
|
||||
else:
|
||||
return eval_node(node.expr2) # type: ignore
|
||||
return eval_node(node.expr2)
|
||||
elif isinstance(node, nodes.And):
|
||||
return eval_node(node.left) and eval_node(node.right) # type: ignore
|
||||
return eval_node(node.left) and eval_node(node.right)
|
||||
elif isinstance(node, nodes.Or):
|
||||
return eval_node(node.left) or eval_node(node.right) # type: ignore
|
||||
return eval_node(node.left) or eval_node(node.right)
|
||||
elif isinstance(node, nodes.Not):
|
||||
return not eval_node(node.node) # type: ignore
|
||||
return not eval_node(node.node)
|
||||
elif isinstance(node, nodes.Name):
|
||||
return self.tags.get(node.name, False) # type: ignore
|
||||
return self.tags.get(node.name, False)
|
||||
else:
|
||||
raise ValueError('invalid node, check parsing')
|
||||
|
||||
|
@ -153,6 +153,7 @@ def _restify_py37(cls: Optional[Type]) -> str:
|
||||
else:
|
||||
text = restify(cls.__origin__)
|
||||
|
||||
origin = getattr(cls, '__origin__', None)
|
||||
if not hasattr(cls, '__args__'):
|
||||
pass
|
||||
elif all(is_system_TypeVar(a) for a in cls.__args__):
|
||||
@ -161,6 +162,8 @@ def _restify_py37(cls: Optional[Type]) -> str:
|
||||
elif cls.__module__ == 'typing' and cls._name == 'Callable':
|
||||
args = ', '.join(restify(a) for a in cls.__args__[:-1])
|
||||
text += r"\ [[%s], %s]" % (args, restify(cls.__args__[-1]))
|
||||
elif cls.__module__ == 'typing' and getattr(origin, '_name', None) == 'Literal':
|
||||
text += r"\ [%s]" % ', '.join(repr(a) for a in cls.__args__)
|
||||
elif cls.__args__:
|
||||
text += r"\ [%s]" % ", ".join(restify(a) for a in cls.__args__)
|
||||
|
||||
@ -362,6 +365,9 @@ def _stringify_py37(annotation: Any) -> str:
|
||||
args = ', '.join(stringify(a) for a in annotation.__args__[:-1])
|
||||
returns = stringify(annotation.__args__[-1])
|
||||
return '%s[[%s], %s]' % (qualname, args, returns)
|
||||
elif qualname == 'Literal':
|
||||
args = ', '.join(repr(a) for a in annotation.__args__)
|
||||
return '%s[%s]' % (qualname, args)
|
||||
elif str(annotation).startswith('typing.Annotated'): # for py39+
|
||||
return stringify(annotation.__args__[0])
|
||||
elif all(is_system_TypeVar(a) for a in annotation.__args__):
|
||||
|
@ -10,4 +10,6 @@
|
||||
|
||||
.. autofunction:: target.typehints.incr
|
||||
|
||||
.. autofunction:: target.overload.sum
|
||||
|
||||
.. autofunction:: target.typehints.tuple_args
|
||||
|
@ -695,6 +695,14 @@ def test_autodoc_typehints_description(app):
|
||||
' Tuple[int, int]\n'
|
||||
in context)
|
||||
|
||||
# Overloads still get displyed in the signature
|
||||
assert ('target.overload.sum(x: int, y: int = 0) -> int\n'
|
||||
'target.overload.sum(x: float, y: float = 0.0) -> float\n'
|
||||
'target.overload.sum(x: str, y: str = None) -> str\n'
|
||||
'\n'
|
||||
' docstring\n'
|
||||
in context)
|
||||
|
||||
|
||||
@pytest.mark.sphinx('text', testroot='ext-autodoc',
|
||||
confoverrides={'autodoc_typehints': "description",
|
||||
@ -787,6 +795,46 @@ def test_autodoc_typehints_description_for_invalid_node(app):
|
||||
restructuredtext.parse(app, text) # raises no error
|
||||
|
||||
|
||||
@pytest.mark.sphinx('text', testroot='ext-autodoc',
|
||||
confoverrides={'autodoc_typehints': "both"})
|
||||
def test_autodoc_typehints_both(app):
|
||||
(app.srcdir / 'index.rst').write_text(
|
||||
'.. autofunction:: target.typehints.incr\n'
|
||||
'\n'
|
||||
'.. autofunction:: target.typehints.tuple_args\n'
|
||||
'\n'
|
||||
'.. autofunction:: target.overload.sum\n'
|
||||
)
|
||||
app.build()
|
||||
context = (app.outdir / 'index.txt').read_text()
|
||||
assert ('target.typehints.incr(a: int, b: int = 1) -> int\n'
|
||||
'\n'
|
||||
' Parameters:\n'
|
||||
' * **a** (*int*) --\n'
|
||||
'\n'
|
||||
' * **b** (*int*) --\n'
|
||||
'\n'
|
||||
' Return type:\n'
|
||||
' int\n'
|
||||
in context)
|
||||
assert ('target.typehints.tuple_args(x: Tuple[int, Union[int, str]]) -> Tuple[int, int]\n'
|
||||
'\n'
|
||||
' Parameters:\n'
|
||||
' **x** (*Tuple**[**int**, **Union**[**int**, **str**]**]*) --\n'
|
||||
'\n'
|
||||
' Return type:\n'
|
||||
' Tuple[int, int]\n'
|
||||
in context)
|
||||
|
||||
# Overloads still get displyed in the signature
|
||||
assert ('target.overload.sum(x: int, y: int = 0) -> int\n'
|
||||
'target.overload.sum(x: float, y: float = 0.0) -> float\n'
|
||||
'target.overload.sum(x: str, y: str = None) -> str\n'
|
||||
'\n'
|
||||
' docstring\n'
|
||||
in context)
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.')
|
||||
@pytest.mark.sphinx('text', testroot='ext-autodoc')
|
||||
def test_autodoc_type_aliases(app):
|
||||
|
@ -214,7 +214,11 @@ def test_signature_annotations():
|
||||
|
||||
# optional union
|
||||
sig = inspect.signature(f20)
|
||||
assert stringify_signature(sig) == '() -> Optional[Union[int, str]]'
|
||||
if sys.version_info < (3, 7):
|
||||
assert stringify_signature(sig) in ('() -> Optional[Union[int, str]]',
|
||||
'() -> Optional[Union[str, int]]')
|
||||
else:
|
||||
assert stringify_signature(sig) == '() -> Optional[Union[int, str]]'
|
||||
|
||||
# Any
|
||||
sig = inspect.signature(f14)
|
||||
|
@ -133,6 +133,12 @@ def test_restify_type_ForwardRef():
|
||||
assert restify(ForwardRef("myint")) == ":class:`myint`"
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
|
||||
def test_restify_type_Literal():
|
||||
from typing import Literal # type: ignore
|
||||
assert restify(Literal[1, "2", "\r"]) == ":obj:`~typing.Literal`\\ [1, '2', '\\r']"
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 10), reason='python 3.10+ is required.')
|
||||
def test_restify_type_union_operator():
|
||||
assert restify(int | None) == "Optional[:class:`int`]" # type: ignore
|
||||
@ -237,6 +243,12 @@ def test_stringify_type_hints_alias():
|
||||
assert stringify(MyTuple) == "Tuple[str, str]" # type: ignore
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
|
||||
def test_stringify_type_Literal():
|
||||
from typing import Literal # type: ignore
|
||||
assert stringify(Literal[1, "2", "\r"]) == "Literal[1, '2', '\\r']"
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 10), reason='python 3.10+ is required.')
|
||||
def test_stringify_type_union_operator():
|
||||
assert stringify(int | None) == "Optional[int]" # type: ignore
|
||||
|
@ -30,8 +30,6 @@ for first beta releases
|
||||
|
||||
* 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)``
|
||||
* ``python utils/bump_version.py X.Y.0b1``
|
||||
* Check diff by ``git diff``
|
||||
* ``git commit -am 'Bump to X.Y.0 beta1'``
|
||||
@ -83,8 +81,6 @@ for major releases
|
||||
|
||||
* 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``
|
||||
* Run ``git add sphinx``
|
||||
* Run ``git commit -am 'Update message catalogs'``
|
||||
* ``python utils/bump_version.py X.Y.0``
|
||||
|
Loading…
Reference in New Issue
Block a user