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
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2021-01-23 14:44:13 +0300
committerTakeshi KOMIYA <i.tkomiya@gmail.com>2021-01-23 14:44:13 +0300
commitccf4ae37298d9088fa7bc1ad8d311be203aa795c (patch)
tree90e9dbc0f5eb01dc3415719e737597f44c68ad4d /sphinx/util
parent65a69965a1dba7e8f61caf53d3d8a9251eab4e19 (diff)
parent37fc43a4d389e173902207e753f4c9e28465e454 (diff)
Merge branch 'master' into 8510_html_logo_url
Diffstat (limited to 'sphinx/util')
-rw-r--r--sphinx/util/__init__.py205
-rw-r--r--sphinx/util/build_phase.py2
-rw-r--r--sphinx/util/cfamily.py25
-rw-r--r--sphinx/util/compat.py29
-rw-r--r--sphinx/util/console.py2
-rw-r--r--sphinx/util/docfields.py33
-rw-r--r--sphinx/util/docstrings.py2
-rw-r--r--sphinx/util/docutils.py43
-rw-r--r--sphinx/util/fileutil.py7
-rw-r--r--sphinx/util/i18n.py97
-rw-r--r--sphinx/util/images.py10
-rw-r--r--sphinx/util/inspect.py211
-rw-r--r--sphinx/util/inventory.py7
-rw-r--r--sphinx/util/jsdump.py2
-rw-r--r--sphinx/util/jsonimpl.py45
-rw-r--r--sphinx/util/logging.py9
-rw-r--r--sphinx/util/matching.py2
-rw-r--r--sphinx/util/math.py2
-rw-r--r--sphinx/util/nodes.py41
-rw-r--r--sphinx/util/osutil.py33
-rw-r--r--sphinx/util/parallel.py2
-rw-r--r--sphinx/util/png.py2
-rw-r--r--sphinx/util/pycompat.py68
-rw-r--r--sphinx/util/requests.py2
-rw-r--r--sphinx/util/rst.py2
-rw-r--r--sphinx/util/smartypants.py5
-rw-r--r--sphinx/util/stemmer/__init__.py2
-rw-r--r--sphinx/util/tags.py2
-rw-r--r--sphinx/util/template.py2
-rw-r--r--sphinx/util/texescape.py12
-rw-r--r--sphinx/util/typing.py141
31 files changed, 190 insertions, 857 deletions
diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py
index c289f5cf8..6ae5e6162 100644
--- a/sphinx/util/__init__.py
+++ b/sphinx/util/__init__.py
@@ -4,11 +4,10 @@
Utility functions for Sphinx.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
-import fnmatch
import functools
import hashlib
import os
@@ -19,20 +18,17 @@ import tempfile
import traceback
import unicodedata
import warnings
-from codecs import BOM_UTF8
-from collections import deque
from datetime import datetime
from importlib import import_module
from os import path
from time import mktime, strptime
-from typing import IO, Any, Callable, Dict, Iterable, Iterator, List, Pattern, Set, Tuple
+from typing import (IO, TYPE_CHECKING, Any, Callable, Dict, Iterable, Iterator, List, Pattern,
+ Set, Tuple, Type)
from urllib.parse import parse_qsl, quote_plus, urlencode, urlsplit, urlunsplit
-from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
-from sphinx.errors import (ExtensionError, FiletypeNotFoundError, PycodeError,
- SphinxParallelError)
+from sphinx.deprecation import RemovedInSphinx50Warning
+from sphinx.errors import ExtensionError, FiletypeNotFoundError, SphinxParallelError
from sphinx.locale import __
-from sphinx.util import smartypants # noqa
from sphinx.util import logging
from sphinx.util.console import bold, colorize, strip_colors, term_width_line # type: ignore
from sphinx.util.matching import patfilter # noqa
@@ -41,13 +37,10 @@ from sphinx.util.nodes import (caption_ref_re, explicit_title_re, # noqa
# import other utilities; partly for backwards compatibility, so don't
# prune unused ones indiscriminately
from sphinx.util.osutil import (SEP, copyfile, copytimes, ensuredir, make_filename, # noqa
- movefile, mtimes_of_files, os_path, relative_uri, walk)
+ movefile, mtimes_of_files, os_path, relative_uri)
from sphinx.util.typing import PathMatcher
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
-
+if TYPE_CHECKING:
from sphinx.application import Sphinx
@@ -98,23 +91,6 @@ def get_matching_files(dirname: str,
yield filename
-def get_matching_docs(dirname: str, suffixes: List[str],
- exclude_matchers: Tuple[PathMatcher, ...] = ()) -> Iterable[str]:
- """Get all file names (without suffixes) matching a suffix in a directory,
- recursively.
-
- Exclude files and dirs matching a pattern in *exclude_patterns*.
- """
- warnings.warn('get_matching_docs() is now deprecated. Use get_matching_files() instead.',
- RemovedInSphinx40Warning, stacklevel=2)
- suffixpatterns = ['*' + s for s in suffixes]
- for filename in get_matching_files(dirname, exclude_matchers):
- for suffixpattern in suffixpatterns:
- if fnmatch.fnmatch(filename, suffixpattern):
- yield filename[:-len(suffixpattern) + 1]
- break
-
-
def get_filetype(source_suffix: Dict[str, str], filename: str) -> str:
for suffix, filetype in source_suffix.items():
if filename.endswith(suffix):
@@ -272,53 +248,6 @@ def save_traceback(app: "Sphinx") -> str:
return path
-def get_module_source(modname: str) -> Tuple[str, str]:
- """Try to find the source code for a module.
-
- Can return ('file', 'filename') in which case the source is in the given
- file, or ('string', 'source') which which case the source is the string.
- """
- warnings.warn('get_module_source() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- try:
- mod = import_module(modname)
- except Exception as err:
- raise PycodeError('error importing %r' % modname, err) from err
- filename = getattr(mod, '__file__', None)
- loader = getattr(mod, '__loader__', None)
- if loader and getattr(loader, 'get_filename', None):
- try:
- filename = loader.get_filename(modname)
- except Exception as err:
- raise PycodeError('error getting filename for %r' % filename, err) from err
- if filename is None and loader:
- try:
- filename = loader.get_source(modname)
- if filename:
- return 'string', filename
- except Exception as err:
- raise PycodeError('error getting source for %r' % modname, err) from err
- if filename is None:
- raise PycodeError('no source found for module %r' % modname)
- filename = path.normpath(path.abspath(filename))
- lfilename = filename.lower()
- if lfilename.endswith('.pyo') or lfilename.endswith('.pyc'):
- filename = filename[:-1]
- if not path.isfile(filename) and path.isfile(filename + 'w'):
- filename += 'w'
- elif not (lfilename.endswith('.py') or lfilename.endswith('.pyw')):
- raise PycodeError('source is not a .py file: %r' % filename)
- elif ('.egg' + os.path.sep) in filename:
- pat = '(?<=\\.egg)' + re.escape(os.path.sep)
- eggpath, _ = re.split(pat, filename, 1)
- if path.isfile(eggpath):
- return 'file', filename
-
- if not path.isfile(filename):
- raise PycodeError('source file is not present: %r' % filename)
- return 'file', filename
-
-
def get_full_modname(modname: str, attribute: str) -> str:
if modname is None:
# Prevents a TypeError: if the last getattr() call will return None
@@ -340,58 +269,6 @@ def get_full_modname(modname: str, attribute: str) -> str:
_coding_re = re.compile(r'coding[:=]\s*([-\w.]+)')
-def detect_encoding(readline: Callable[[], bytes]) -> str:
- """Like tokenize.detect_encoding() from Py3k, but a bit simplified."""
- warnings.warn('sphinx.util.detect_encoding() is deprecated',
- RemovedInSphinx40Warning, stacklevel=2)
-
- def read_or_stop() -> bytes:
- try:
- return readline()
- except StopIteration:
- return None
-
- def get_normal_name(orig_enc: str) -> str:
- """Imitates get_normal_name in tokenizer.c."""
- # Only care about the first 12 characters.
- enc = orig_enc[:12].lower().replace('_', '-')
- if enc == 'utf-8' or enc.startswith('utf-8-'):
- return 'utf-8'
- if enc in ('latin-1', 'iso-8859-1', 'iso-latin-1') or \
- enc.startswith(('latin-1-', 'iso-8859-1-', 'iso-latin-1-')):
- return 'iso-8859-1'
- return orig_enc
-
- def find_cookie(line: bytes) -> str:
- try:
- line_string = line.decode('ascii')
- except UnicodeDecodeError:
- return None
-
- matches = _coding_re.findall(line_string)
- if not matches:
- return None
- return get_normal_name(matches[0])
-
- default = sys.getdefaultencoding()
- first = read_or_stop()
- if first and first.startswith(BOM_UTF8):
- first = first[3:]
- default = 'utf-8-sig'
- if not first:
- return default
- encoding = find_cookie(first)
- if encoding:
- return encoding
- second = read_or_stop()
- if not second:
- return default
- encoding = find_cookie(second)
- if encoding:
- return encoding
- return default
-
-
class UnicodeDecodeErrorHandler:
"""Custom error handler for open() that warns and replaces."""
@@ -460,39 +337,6 @@ def parselinenos(spec: str, total: int) -> List[int]:
return items
-def force_decode(string: str, encoding: str) -> str:
- """Forcibly get a unicode string out of a bytestring."""
- warnings.warn('force_decode() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- if isinstance(string, bytes):
- try:
- if encoding:
- string = string.decode(encoding)
- else:
- # try decoding with utf-8, should only work for real UTF-8
- string = string.decode()
- except UnicodeError:
- # last resort -- can't fail
- string = string.decode('latin1')
- return string
-
-
-class attrdict(dict):
- def __init__(self, *args: Any, **kwargs: Any) -> None:
- super().__init__(*args, **kwargs)
- warnings.warn('The attrdict class is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
-
- def __getattr__(self, key: str) -> str:
- return self[key]
-
- def __setattr__(self, key: str, val: str) -> None:
- self[key] = val
-
- def __delattr__(self, key: str) -> None:
- del self[key]
-
-
def rpartition(s: str, t: str) -> Tuple[str, str]:
"""Similar to str.rpartition from 2.5, but doesn't return the separator."""
warnings.warn('rpartition() is now deprecated.', RemovedInSphinx50Warning, stacklevel=2)
@@ -542,41 +386,6 @@ def format_exception_cut_frames(x: int = 1) -> str:
return ''.join(res)
-class PeekableIterator:
- """
- An iterator which wraps any iterable and makes it possible to peek to see
- what's the next item.
- """
- def __init__(self, iterable: Iterable) -> None:
- self.remaining = deque() # type: deque
- self._iterator = iter(iterable)
- warnings.warn('PeekableIterator is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
-
- def __iter__(self) -> "PeekableIterator":
- return self
-
- def __next__(self) -> Any:
- """Return the next item from the iterator."""
- if self.remaining:
- return self.remaining.popleft()
- return next(self._iterator)
-
- next = __next__ # Python 2 compatibility
-
- def push(self, item: Any) -> None:
- """Push the `item` on the internal stack, it will be returned on the
- next :meth:`next` call.
- """
- self.remaining.append(item)
-
- def peek(self) -> Any:
- """Return the next item without changing the state of the iterator."""
- item = next(self)
- self.push(item)
- return item
-
-
def import_object(objname: str, source: str = None) -> Any:
"""Import python object by qualname."""
try:
diff --git a/sphinx/util/build_phase.py b/sphinx/util/build_phase.py
index d6193b400..07a5ee7cd 100644
--- a/sphinx/util/build_phase.py
+++ b/sphinx/util/build_phase.py
@@ -4,7 +4,7 @@
Build phase of Sphinx application.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/cfamily.py b/sphinx/util/cfamily.py
index 17c335b60..31244852c 100644
--- a/sphinx/util/cfamily.py
+++ b/sphinx/util/cfamily.py
@@ -4,12 +4,11 @@
Utility functions common to the C and C++ domains.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
-import warnings
from copy import deepcopy
from typing import Any, Callable, List, Match, Optional, Pattern, Tuple, Union
@@ -17,7 +16,6 @@ from docutils import nodes
from docutils.nodes import TextElement
from sphinx.config import Config
-from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.util import logging
logger = logging.getLogger(__name__)
@@ -85,12 +83,7 @@ def verify_description_mode(mode: str) -> None:
class NoOldIdError(Exception):
# Used to avoid implementing unneeded id generation for old id schemes.
- @property
- def description(self) -> str:
- warnings.warn('%s.description is deprecated. '
- 'Coerce the instance to a string instead.' % self.__class__.__name__,
- RemovedInSphinx40Warning, stacklevel=2)
- return str(self)
+ pass
class ASTBaseBase:
@@ -213,21 +206,11 @@ class ASTBaseParenExprList(ASTBaseBase):
################################################################################
class UnsupportedMultiCharacterCharLiteral(Exception):
- @property
- def decoded(self) -> str:
- warnings.warn('%s.decoded is deprecated. '
- 'Coerce the instance to a string instead.' % self.__class__.__name__,
- RemovedInSphinx40Warning, stacklevel=2)
- return str(self)
+ pass
class DefinitionError(Exception):
- @property
- def description(self) -> str:
- warnings.warn('%s.description is deprecated. '
- 'Coerce the instance to a string instead.' % self.__class__.__name__,
- RemovedInSphinx40Warning, stacklevel=2)
- return str(self)
+ pass
class BaseParser:
diff --git a/sphinx/util/compat.py b/sphinx/util/compat.py
index 7c55c4ec7..9be80358e 100644
--- a/sphinx/util/compat.py
+++ b/sphinx/util/compat.py
@@ -4,22 +4,14 @@
modules for backward compatibility
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys
-import warnings
-from typing import Any, Dict
+from typing import TYPE_CHECKING, Any, Dict
-from docutils.utils import get_source_line
-
-from sphinx import addnodes
-from sphinx.deprecation import RemovedInSphinx40Warning
-from sphinx.transforms import SphinxTransform
-
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.application import Sphinx
@@ -36,22 +28,7 @@ def register_application_for_autosummary(app: "Sphinx") -> None:
autosummary._app = app
-class IndexEntriesMigrator(SphinxTransform):
- """Migrating indexentries from old style (4columns) to new style (5columns)."""
- default_priority = 700
-
- def apply(self, **kwargs: Any) -> None:
- for node in self.document.traverse(addnodes.index):
- for i, entries in enumerate(node['entries']):
- if len(entries) == 4:
- source, line = get_source_line(node)
- warnings.warn('An old styled index node found: %r at (%s:%s)' %
- (node, source, line), RemovedInSphinx40Warning, stacklevel=2)
- node['entries'][i] = entries + (None,)
-
-
def setup(app: "Sphinx") -> Dict[str, Any]:
- app.add_transform(IndexEntriesMigrator)
app.connect('builder-inited', register_application_for_autosummary)
return {
diff --git a/sphinx/util/console.py b/sphinx/util/console.py
index 579a95134..3ea5b9573 100644
--- a/sphinx/util/console.py
+++ b/sphinx/util/console.py
@@ -4,7 +4,7 @@
Format colored console output.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py
index 404bb127f..79abfe14b 100644
--- a/sphinx/util/docfields.py
+++ b/sphinx/util/docfields.py
@@ -5,24 +5,19 @@
"Doc fields" are reST field lists in object descriptions that will
be domain-specifically transformed to a more appealing presentation.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
-import warnings
-from typing import Any, Dict, List, Tuple, Union, cast
+from typing import TYPE_CHECKING, Any, Dict, List, Tuple, Type, Union, cast
from docutils import nodes
from docutils.nodes import Node
from sphinx import addnodes
-from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.util.typing import TextlikeNode
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
-
+if TYPE_CHECKING:
from sphinx.directive import ObjectDescription
from sphinx.environment import BuildEnvironment
@@ -219,26 +214,7 @@ class DocFieldTransformer:
def __init__(self, directive: "ObjectDescription") -> None:
self.directive = directive
- try:
- self.typemap = directive.get_field_type_map()
- except Exception:
- # for 3rd party extensions directly calls this transformer.
- warnings.warn('DocFieldTransformer expects given directive object is a subclass '
- 'of ObjectDescription.', RemovedInSphinx40Warning, stacklevel=2)
- self.typemap = self.preprocess_fieldtypes(directive.__class__.doc_field_types)
-
- def preprocess_fieldtypes(self, types: List[Field]) -> Dict[str, Tuple[Field, bool]]:
- warnings.warn('DocFieldTransformer.preprocess_fieldtypes() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- typemap = {}
- for fieldtype in types:
- for name in fieldtype.names:
- typemap[name] = fieldtype, False
- if fieldtype.is_typed:
- typed_field = cast(TypedField, fieldtype)
- for name in typed_field.typenames:
- typemap[name] = typed_field, True
- return typemap
+ self.typemap = directive.get_field_type_map()
def transform_all(self, node: addnodes.desc_content) -> None:
"""Transform all field list children of a node."""
@@ -295,6 +271,7 @@ class DocFieldTransformer:
self.directive.domain,
target,
contnode=content[0],
+ env=self.directive.state.document.settings.env
)
if _is_single_paragraph(field_body):
paragraph = cast(nodes.paragraph, field_body[0])
diff --git a/sphinx/util/docstrings.py b/sphinx/util/docstrings.py
index 206986bd0..ac778af87 100644
--- a/sphinx/util/docstrings.py
+++ b/sphinx/util/docstrings.py
@@ -4,7 +4,7 @@
Utilities for docstring processing.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py
index 7831f267c..a3e49e6d7 100644
--- a/sphinx/util/docutils.py
+++ b/sphinx/util/docutils.py
@@ -4,7 +4,7 @@
Utility functions for docutils.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -15,7 +15,8 @@ from copy import copy
from distutils.version import LooseVersion
from os import path
from types import ModuleType
-from typing import IO, Any, Callable, Dict, Generator, List, Optional, Set, Tuple, cast
+from typing import (IO, TYPE_CHECKING, Any, Callable, Dict, Generator, List, Optional, Set,
+ Tuple, Type, cast)
import docutils
from docutils import nodes
@@ -27,16 +28,14 @@ from docutils.statemachine import State, StateMachine, StringList
from docutils.utils import Reporter, unescape
from sphinx.errors import SphinxError
+from sphinx.locale import _
from sphinx.util import logging
from sphinx.util.typing import RoleFunction
logger = logging.getLogger(__name__)
report_re = re.compile('^(.+?:(?:\\d+)?): \\((DEBUG|INFO|WARNING|ERROR|SEVERE)/(\\d+)?\\) ')
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
-
+if TYPE_CHECKING:
from sphinx.builders import Builder
from sphinx.config import Config
from sphinx.environment import BuildEnvironment
@@ -145,7 +144,7 @@ def patched_get_language() -> Generator[None, None, None]:
@contextmanager
-def using_user_docutils_conf(confdir: str) -> Generator[None, None, None]:
+def using_user_docutils_conf(confdir: Optional[str]) -> Generator[None, None, None]:
"""Let docutils know the location of ``docutils.conf`` for Sphinx."""
try:
docutilsconfig = os.environ.get('DOCUTILSCONFIG', None)
@@ -161,7 +160,7 @@ def using_user_docutils_conf(confdir: str) -> Generator[None, None, None]:
@contextmanager
-def patch_docutils(confdir: str = None) -> Generator[None, None, None]:
+def patch_docutils(confdir: Optional[str] = None) -> Generator[None, None, None]:
"""Patch to docutils temporarily."""
with patched_get_language(), using_user_docutils_conf(confdir):
yield
@@ -210,6 +209,8 @@ class sphinx_domains:
element = getattr(domain, type)(name)
if element is not None:
return element, []
+ else:
+ logger.warning(_('unknown directive or role name: %s:%s'), domain_name, name)
# else look in the default domain
else:
def_domain = self.env.temp_data.get('default_domain')
@@ -347,15 +348,15 @@ class SphinxRole:
.. note:: The subclasses of this class might not work with docutils.
This class is strongly coupled with Sphinx.
"""
- name = None #: The role name actually used in the document.
- rawtext = None #: A string containing the entire interpreted text input.
- text = None #: The interpreted text content.
- lineno = None #: The line number where the interpreted text begins.
- inliner = None #: The ``docutils.parsers.rst.states.Inliner`` object.
- options = None #: A dictionary of directive options for customization
- #: (from the "role" directive).
- content = None #: A list of strings, the directive content for customization
- #: (from the "role" directive).
+ name: str #: The role name actually used in the document.
+ rawtext: str #: A string containing the entire interpreted text input.
+ text: str #: The interpreted text content.
+ lineno: int #: The line number where the interpreted text begins.
+ inliner: Inliner #: The ``docutils.parsers.rst.states.Inliner`` object.
+ options: Dict #: A dictionary of directive options for customization
+ #: (from the "role" directive).
+ content: List[str] #: A list of strings, the directive content for customization
+ #: (from the "role" directive).
def __call__(self, name: str, rawtext: str, text: str, lineno: int,
inliner: Inliner, options: Dict = {}, content: List[str] = []
@@ -408,10 +409,10 @@ class ReferenceRole(SphinxRole):
the role. The parsed result; link title and target will be stored to
``self.title`` and ``self.target``.
"""
- has_explicit_title = None #: A boolean indicates the role has explicit title or not.
- disabled = False #: A boolean indicates the reference is disabled.
- title = None #: The link title for the interpreted text.
- target = None #: The link target for the interpreted text.
+ has_explicit_title: bool #: A boolean indicates the role has explicit title or not.
+ disabled: bool #: A boolean indicates the reference is disabled.
+ title: str #: The link title for the interpreted text.
+ target: str #: The link target for the interpreted text.
# \x00 means the "<" was backslash-escaped
explicit_title_re = re.compile(r'^(.+?)\s*(?<!\x00)<(.*?)>$', re.DOTALL)
diff --git a/sphinx/util/fileutil.py b/sphinx/util/fileutil.py
index eec1ae463..37c038855 100644
--- a/sphinx/util/fileutil.py
+++ b/sphinx/util/fileutil.py
@@ -4,21 +4,20 @@
File utility functions for Sphinx.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import os
import posixpath
-from typing import Callable, Dict
+from typing import TYPE_CHECKING, Callable, Dict
from docutils.utils import relative_path
from sphinx.util.osutil import copyfile, ensuredir
from sphinx.util.typing import PathMatcher
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.util.template import BaseRenderer
diff --git a/sphinx/util/i18n.py b/sphinx/util/i18n.py
index 41407f4e1..3a5aca58e 100644
--- a/sphinx/util/i18n.py
+++ b/sphinx/util/i18n.py
@@ -4,37 +4,36 @@
Builder superclass for all builders.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
-import gettext
+
import os
import re
-import warnings
-from collections import namedtuple
from datetime import datetime, timezone
from os import path
-from typing import Callable, Generator, List, Set, Tuple, Union
+from typing import TYPE_CHECKING, Callable, Generator, List, NamedTuple, Tuple, Union
import babel.dates
from babel.messages.mofile import write_mo
from babel.messages.pofile import read_po
-from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.errors import SphinxError
from sphinx.locale import __
from sphinx.util import logging
-from sphinx.util.matching import Matcher
from sphinx.util.osutil import SEP, canon_path, relpath
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.environment import BuildEnvironment
logger = logging.getLogger(__name__)
-LocaleFileInfoBase = namedtuple('CatalogInfo', 'base_dir,domain,charset')
+
+class LocaleFileInfoBase(NamedTuple):
+ base_dir: str
+ domain: str
+ charset: str
class CatalogInfo(LocaleFileInfoBase):
@@ -117,17 +116,6 @@ class CatalogRepository:
yield CatalogInfo(basedir, domain, self.encoding)
-def find_catalog(docname: str, compaction: bool) -> str:
- warnings.warn('find_catalog() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- if compaction:
- ret = docname.split(SEP, 1)[0]
- else:
- ret = docname
-
- return ret
-
-
def docname_to_domain(docname: str, compation: Union[bool, str]) -> str:
"""Convert docname to domain for catalogs."""
if isinstance(compation, str):
@@ -138,69 +126,6 @@ def docname_to_domain(docname: str, compation: Union[bool, str]) -> str:
return docname
-def find_catalog_files(docname: str, srcdir: str, locale_dirs: List[str],
- lang: str, compaction: bool) -> List[str]:
- warnings.warn('find_catalog_files() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- if not(lang and locale_dirs):
- return []
-
- domain = find_catalog(docname, compaction)
- files = [gettext.find(domain, path.join(srcdir, dir_), [lang])
- for dir_ in locale_dirs]
- files = [relpath(f, srcdir) for f in files if f]
- return files
-
-
-def find_catalog_source_files(locale_dirs: List[str], locale: str, domains: List[str] = None,
- charset: str = 'utf-8', force_all: bool = False,
- excluded: Matcher = Matcher([])) -> Set[CatalogInfo]:
- """
- :param list locale_dirs:
- list of path as `['locale_dir1', 'locale_dir2', ...]` to find
- translation catalogs. Each path contains a structure such as
- `<locale>/LC_MESSAGES/domain.po`.
- :param str locale: a language as `'en'`
- :param list domains: list of domain names to get. If empty list or None
- is specified, get all domain names. default is None.
- :param boolean force_all:
- Set True if you want to get all catalogs rather than updated catalogs.
- default is False.
- :return: [CatalogInfo(), ...]
- """
- warnings.warn('find_catalog_source_files() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
-
- catalogs = set() # type: Set[CatalogInfo]
-
- if not locale:
- return catalogs # locale is not specified
-
- for locale_dir in locale_dirs:
- if not locale_dir:
- continue # skip system locale directory
-
- base_dir = path.join(locale_dir, locale, 'LC_MESSAGES')
-
- if not path.exists(base_dir):
- continue # locale path is not found
-
- for dirpath, dirnames, filenames in os.walk(base_dir, followlinks=True):
- filenames = [f for f in filenames if f.endswith('.po')]
- for filename in filenames:
- if excluded(path.join(relpath(dirpath, base_dir), filename)):
- continue
- base = path.splitext(filename)[0]
- domain = relpath(path.join(dirpath, base), base_dir).replace(path.sep, SEP)
- if domains and domain not in domains:
- continue
- cat = CatalogInfo(base_dir, domain, charset)
- if force_all or cat.is_outdated():
- catalogs.add(cat)
-
- return catalogs
-
-
# date_format mappings: ustrftime() to bable.dates.format_datetime()
date_format_mappings = {
'%a': 'EEE', # Weekday as locale’s abbreviated name.
@@ -236,7 +161,9 @@ date_format_mappings = {
'%X': 'medium', # Locale’s appropriate time representation.
'%y': 'YY', # Year without century as a zero-padded decimal number.
'%Y': 'yyyy', # Year with century as a decimal number.
- '%Z': 'zzzz', # Time zone name (no characters if no time zone exists).
+ '%Z': 'zzz', # Time zone name (no characters if no time zone exists).
+ '%z': 'ZZZ', # UTC offset in the form ±HHMM[SS[.ffffff]]
+ # (empty string if the object is naive).
'%%': '%',
}
diff --git a/sphinx/util/images.py b/sphinx/util/images.py
index 0ddf64908..8c32e3414 100644
--- a/sphinx/util/images.py
+++ b/sphinx/util/images.py
@@ -4,7 +4,7 @@
Image utility functions for Sphinx.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -31,9 +31,11 @@ mime_suffixes = OrderedDict([
('.ai', 'application/illustrator'),
])
-DataURI = NamedTuple('DataURI', [('mimetype', str),
- ('charset', str),
- ('data', bytes)])
+
+class DataURI(NamedTuple):
+ mimetype: str
+ charset: str
+ data: bytes
def get_image_size(filename: str) -> Optional[Tuple[int, int]]:
diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py
index c730fefdb..f797339cf 100644
--- a/sphinx/util/inspect.py
+++ b/sphinx/util/inspect.py
@@ -4,7 +4,7 @@
Helpers for inspecting Python modules.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -20,10 +20,10 @@ 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, List, Mapping, Optional, Sequence, Tuple, cast
+from typing import Any, Callable, Dict, Mapping, Optional, Sequence, Tuple, cast
-from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
-from sphinx.pycode.ast import ast # for py35-37
+from sphinx.deprecation import RemovedInSphinx50Warning
+from sphinx.pycode.ast import ast # for py36-37
from sphinx.pycode.ast import unparse as ast_unparse
from sphinx.util import logging
from sphinx.util.typing import ForwardRef
@@ -36,6 +36,10 @@ else:
MethodDescriptorType = type(str.join)
WrapperDescriptorType = type(dict.__dict__['fromkeys'])
+if False:
+ # For type annotation
+ from typing import Type # NOQA
+
logger = logging.getLogger(__name__)
memory_address_re = re.compile(r' at 0x[0-9a-f]{8,16}(?=>)', re.IGNORECASE)
@@ -166,11 +170,24 @@ def getannotations(obj: Any) -> Mapping[str, Any]:
return {}
+def getmro(obj: Any) -> Tuple["Type", ...]:
+ """Get __mro__ from given *obj* safely.
+
+ Raises AttributeError if given *obj* raises an error on accessing __mro__.
+ """
+ __mro__ = safe_getattr(obj, '__mro__', None)
+ if isinstance(__mro__, tuple):
+ return __mro__
+ else:
+ return tuple()
+
+
def getslots(obj: Any) -> Optional[Dict]:
"""Get __slots__ attribute of the class as dict.
Return None if gienv *obj* does not have __slots__.
Raises AttributeError if given *obj* raises an error on accessing __slots__.
+ Raises TypeError if given *obj* is not a class.
Raises ValueError if given *obj* have invalid __slots__.
"""
if not inspect.isclass(obj):
@@ -322,7 +339,7 @@ def is_singledispatch_method(obj: Any) -> bool:
try:
from functools import singledispatchmethod # type: ignore
return isinstance(obj, singledispatchmethod)
- except ImportError: # py35-37
+ except ImportError: # py36-37
return False
@@ -355,7 +372,7 @@ def iscoroutinefunction(obj: Any) -> bool:
def isproperty(obj: Any) -> bool:
"""Check if the object is property."""
- if sys.version_info > (3, 8):
+ if sys.version_info >= (3, 8):
from functools import cached_property # cached_property is available since py3.8
if isinstance(obj, cached_property):
return True
@@ -400,23 +417,6 @@ def safe_getattr(obj: Any, name: str, *defargs: Any) -> Any:
raise AttributeError(name) from exc
-def safe_getmembers(object: Any, predicate: Callable[[str], bool] = None,
- attr_getter: Callable = safe_getattr) -> List[Tuple[str, Any]]:
- """A version of inspect.getmembers() that uses safe_getattr()."""
- warnings.warn('safe_getmembers() is deprecated', RemovedInSphinx40Warning, stacklevel=2)
-
- results = [] # type: List[Tuple[str, Any]]
- for key in dir(object):
- try:
- value = attr_getter(object, key, None)
- except AttributeError:
- continue
- if not predicate or predicate(value):
- results.append((key, value))
- results.sort()
- return results
-
-
def object_description(object: Any) -> str:
"""A repr() implementation that returns text safe to use in reST context."""
if isinstance(object, dict):
@@ -482,6 +482,19 @@ def is_builtin_class_method(obj: Any, attr_name: str) -> bool:
return getattr(builtins, name, None) is cls
+class DefaultValue:
+ """A simple wrapper for default value of the parameters of overload functions."""
+
+ def __init__(self, value: str) -> None:
+ self.value = value
+
+ def __eq__(self, other: object) -> bool:
+ return self.value == other
+
+ def __repr__(self) -> str:
+ return self.value
+
+
def _should_unwrap(subject: Callable) -> bool:
"""Check the function should be unwrapped on getting signature."""
if (safe_getattr(subject, '__globals__', None) and
@@ -687,7 +700,7 @@ def signature_from_ast(node: ast.FunctionDef, code: str = '') -> inspect.Signatu
if defaults[i] is Parameter.empty:
default = Parameter.empty
else:
- default = ast_unparse(defaults[i], code)
+ default = DefaultValue(ast_unparse(defaults[i], code))
annotation = ast_unparse(arg.annotation, code) or Parameter.empty
params.append(Parameter(arg.arg, Parameter.POSITIONAL_ONLY,
@@ -697,7 +710,7 @@ def signature_from_ast(node: ast.FunctionDef, code: str = '') -> inspect.Signatu
if defaults[i + posonlyargs] is Parameter.empty:
default = Parameter.empty
else:
- default = ast_unparse(defaults[i + posonlyargs], code)
+ default = DefaultValue(ast_unparse(defaults[i + posonlyargs], code))
annotation = ast_unparse(arg.annotation, code) or Parameter.empty
params.append(Parameter(arg.arg, Parameter.POSITIONAL_OR_KEYWORD,
@@ -724,154 +737,6 @@ def signature_from_ast(node: ast.FunctionDef, code: str = '') -> inspect.Signatu
return inspect.Signature(params, return_annotation=return_annotation)
-class Signature:
- """The Signature object represents the call signature of a callable object and
- its return annotation.
- """
-
- empty = inspect.Signature.empty
-
- def __init__(self, subject: Callable, bound_method: bool = False,
- has_retval: bool = True) -> None:
- warnings.warn('sphinx.util.inspect.Signature() is deprecated',
- RemovedInSphinx40Warning, stacklevel=2)
-
- # check subject is not a built-in class (ex. int, str)
- if (isinstance(subject, type) and
- is_builtin_class_method(subject, "__new__") and
- is_builtin_class_method(subject, "__init__")):
- raise TypeError("can't compute signature for built-in type {}".format(subject))
-
- self.subject = subject
- self.has_retval = has_retval
- self.partialmethod_with_noargs = False
-
- try:
- self.signature = inspect.signature(subject) # type: Optional[inspect.Signature]
- except IndexError:
- # Until python 3.6.4, cpython has been crashed on inspection for
- # partialmethods not having any arguments.
- # https://bugs.python.org/issue33009
- if hasattr(subject, '_partialmethod'):
- self.signature = None
- self.partialmethod_with_noargs = True
- else:
- raise
-
- try:
- self.annotations = typing.get_type_hints(subject)
- except Exception:
- # get_type_hints() does not support some kind of objects like partial,
- # ForwardRef and so on. For them, it raises an exception. In that case,
- # we try to build annotations from argspec.
- self.annotations = {}
-
- if bound_method:
- # client gives a hint that the subject is a bound method
-
- if inspect.ismethod(subject):
- # inspect.signature already considers the subject is bound method.
- # So it is not need to skip first argument.
- self.skip_first_argument = False
- else:
- self.skip_first_argument = True
- else:
- # inspect.signature recognizes type of method properly without any hints
- self.skip_first_argument = False
-
- @property
- def parameters(self) -> Mapping:
- if self.partialmethod_with_noargs:
- return {}
- else:
- return self.signature.parameters
-
- @property
- def return_annotation(self) -> Any:
- if self.signature:
- if self.has_retval:
- return self.signature.return_annotation
- else:
- return Parameter.empty
- else:
- return None
-
- def format_args(self, show_annotation: bool = True) -> str:
- def get_annotation(param: Parameter) -> Any:
- if isinstance(param.annotation, str) and param.name in self.annotations:
- return self.annotations[param.name]
- else:
- return param.annotation
-
- args = []
- last_kind = None
- for i, param in enumerate(self.parameters.values()):
- # skip first argument if subject is bound method
- if self.skip_first_argument and i == 0:
- continue
-
- arg = StringIO()
-
- # insert '*' between POSITIONAL args and KEYWORD_ONLY args::
- # func(a, b, *, c, d):
- if param.kind == param.KEYWORD_ONLY and last_kind in (param.POSITIONAL_OR_KEYWORD,
- param.POSITIONAL_ONLY,
- None):
- args.append('*')
-
- if param.kind in (param.POSITIONAL_ONLY,
- param.POSITIONAL_OR_KEYWORD,
- param.KEYWORD_ONLY):
- arg.write(param.name)
- if show_annotation and param.annotation is not param.empty:
- arg.write(': ')
- arg.write(stringify_annotation(get_annotation(param)))
- if param.default is not param.empty:
- if param.annotation is param.empty or show_annotation is False:
- arg.write('=')
- arg.write(object_description(param.default))
- else:
- arg.write(' = ')
- arg.write(object_description(param.default))
- elif param.kind == param.VAR_POSITIONAL:
- arg.write('*')
- arg.write(param.name)
- if show_annotation and param.annotation is not param.empty:
- arg.write(': ')
- arg.write(stringify_annotation(get_annotation(param)))
- elif param.kind == param.VAR_KEYWORD:
- arg.write('**')
- arg.write(param.name)
- if show_annotation and param.annotation is not param.empty:
- arg.write(': ')
- arg.write(stringify_annotation(get_annotation(param)))
-
- args.append(arg.getvalue())
- last_kind = param.kind
-
- if self.return_annotation is Parameter.empty or show_annotation is False:
- return '(%s)' % ', '.join(args)
- else:
- if 'return' in self.annotations:
- annotation = stringify_annotation(self.annotations['return'])
- else:
- annotation = stringify_annotation(self.return_annotation)
-
- return '(%s) -> %s' % (', '.join(args), annotation)
-
- def format_annotation(self, annotation: Any) -> str:
- """Return formatted representation of a type annotation."""
- return stringify_annotation(annotation)
-
- def format_annotation_new(self, annotation: Any) -> str:
- """format_annotation() for py37+"""
- return stringify_annotation(annotation)
-
- def format_annotation_old(self, annotation: Any) -> str:
- """format_annotation() for py36 or below"""
- return stringify_annotation(annotation)
-
-
def getdoc(obj: Any, attrgetter: Callable = safe_getattr,
allow_inherited: bool = False, cls: Any = None, name: str = None) -> str:
"""Get the docstring for the object.
diff --git a/sphinx/util/inventory.py b/sphinx/util/inventory.py
index 6d3b5f455..40ac87997 100644
--- a/sphinx/util/inventory.py
+++ b/sphinx/util/inventory.py
@@ -4,13 +4,13 @@
Inventory utility functions for Sphinx.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import os
import re
import zlib
-from typing import IO, Callable, Iterator
+from typing import IO, TYPE_CHECKING, Callable, Iterator
from sphinx.util import logging
from sphinx.util.typing import Inventory
@@ -18,8 +18,7 @@ from sphinx.util.typing import Inventory
BUFSIZE = 16 * 1024
logger = logging.getLogger(__name__)
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.builders import Builder
from sphinx.environment import BuildEnvironment
diff --git a/sphinx/util/jsdump.py b/sphinx/util/jsdump.py
index 0cd3c8b8c..114fd7075 100644
--- a/sphinx/util/jsdump.py
+++ b/sphinx/util/jsdump.py
@@ -5,7 +5,7 @@
This module implements a simple JavaScript serializer.
Uses the basestring encode function from simplejson by Bob Ippolito.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/jsonimpl.py b/sphinx/util/jsonimpl.py
deleted file mode 100644
index 4cb3bc93f..000000000
--- a/sphinx/util/jsonimpl.py
+++ /dev/null
@@ -1,45 +0,0 @@
-"""
- sphinx.util.jsonimpl
- ~~~~~~~~~~~~~~~~~~~~
-
- JSON serializer implementation wrapper.
-
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-
-import json
-import warnings
-from collections import UserString
-from typing import IO, Any
-
-from sphinx.deprecation import RemovedInSphinx40Warning
-
-warnings.warn('sphinx.util.jsonimpl is deprecated',
- RemovedInSphinx40Warning, stacklevel=2)
-
-
-class SphinxJSONEncoder(json.JSONEncoder):
- """JSONEncoder subclass that forces translation proxies."""
- def default(self, obj: Any) -> str:
- if isinstance(obj, UserString):
- return str(obj)
- return super().default(obj)
-
-
-def dump(obj: Any, fp: IO, *args: Any, **kwargs: Any) -> None:
- kwargs['cls'] = SphinxJSONEncoder
- json.dump(obj, fp, *args, **kwargs)
-
-
-def dumps(obj: Any, *args: Any, **kwargs: Any) -> str:
- kwargs['cls'] = SphinxJSONEncoder
- return json.dumps(obj, *args, **kwargs)
-
-
-def load(*args: Any, **kwargs: Any) -> Any:
- return json.load(*args, **kwargs)
-
-
-def loads(*args: Any, **kwargs: Any) -> Any:
- return json.loads(*args, **kwargs)
diff --git a/sphinx/util/logging.py b/sphinx/util/logging.py
index 0f2a8b6f0..1c480e017 100644
--- a/sphinx/util/logging.py
+++ b/sphinx/util/logging.py
@@ -4,7 +4,7 @@
Logging utility functions for Sphinx.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -12,7 +12,7 @@ import logging
import logging.handlers
from collections import defaultdict
from contextlib import contextmanager
-from typing import IO, Any, Dict, Generator, List, Tuple, Union
+from typing import IO, TYPE_CHECKING, Any, Dict, Generator, List, Tuple, Type, Union
from docutils import nodes
from docutils.nodes import Node
@@ -21,10 +21,7 @@ from docutils.utils import get_source_line
from sphinx.errors import SphinxWarning
from sphinx.util.console import colorize
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
-
+if TYPE_CHECKING:
from sphinx.application import Sphinx
diff --git a/sphinx/util/matching.py b/sphinx/util/matching.py
index 2d37866a6..d33ae0333 100644
--- a/sphinx/util/matching.py
+++ b/sphinx/util/matching.py
@@ -4,7 +4,7 @@
Pattern-matching utility functions for Sphinx.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/math.py b/sphinx/util/math.py
index 2a9bd66d7..229e09d36 100644
--- a/sphinx/util/math.py
+++ b/sphinx/util/math.py
@@ -4,7 +4,7 @@
Utility functions for math.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py
index 859760dcf..944fd3ecb 100644
--- a/sphinx/util/nodes.py
+++ b/sphinx/util/nodes.py
@@ -4,14 +4,13 @@
Docutils node-related utility functions for Sphinx.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
import unicodedata
-import warnings
-from typing import Any, Callable, Iterable, List, Set, Tuple, cast
+from typing import TYPE_CHECKING, Any, Callable, Iterable, List, Set, Tuple, Type, cast
from docutils import nodes
from docutils.nodes import Element, Node
@@ -20,14 +19,10 @@ from docutils.parsers.rst.states import Inliner
from docutils.statemachine import StringList
from sphinx import addnodes
-from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.locale import __
from sphinx.util import logging
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
-
+if TYPE_CHECKING:
from sphinx.builders import Builder
from sphinx.domain import IndexEntry
from sphinx.environment import BuildEnvironment
@@ -201,6 +196,10 @@ def is_translatable(node: Node) -> bool:
if isinstance(node, addnodes.translatable):
return True
+ # image node marked as translatable or having alt text
+ if isinstance(node, nodes.image) and (node.get('translatable') or node.get('alt')):
+ return True
+
if isinstance(node, nodes.Inline) and 'translatable' not in node: # type: ignore
# inline node must not be translated if 'translatable' is not set
return False
@@ -228,9 +227,6 @@ def is_translatable(node: Node) -> bool:
return False
return True
- if isinstance(node, nodes.image) and node.get('translatable'):
- return True
-
if isinstance(node, addnodes.meta):
return True
if is_pending_meta(node):
@@ -264,10 +260,13 @@ def extract_messages(doctree: Element) -> Iterable[Tuple[Element, str]]:
msg = node.rawsource
if not msg:
msg = node.astext()
- elif isinstance(node, IMAGE_TYPE_NODES):
- msg = '.. image:: %s' % node['uri']
+ elif isinstance(node, nodes.image):
if node.get('alt'):
- msg += '\n :alt: %s' % node['alt']
+ yield node, node['alt']
+ if node.get('translatable'):
+ msg = '.. image:: %s' % node['uri']
+ else:
+ msg = None
elif isinstance(node, META_TYPE_NODES):
msg = node.rawcontent
elif isinstance(node, nodes.pending) and is_pending_meta(node):
@@ -280,12 +279,6 @@ def extract_messages(doctree: Element) -> Iterable[Tuple[Element, str]]:
yield node, msg
-def find_source_node(node: Element) -> str:
- warnings.warn('find_source_node() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- return get_node_source(node)
-
-
def get_node_source(node: Element) -> str:
for pnode in traverse_parent(node):
if pnode.source:
@@ -613,10 +606,12 @@ def process_only_nodes(document: Node, tags: "Tags") -> None:
node.replace_self(nodes.comment())
-# monkey-patch Element.copy to copy the rawsource and line
-# for docutils-0.14 or older versions.
-
def _new_copy(self: Element) -> Element:
+ """monkey-patch Element.copy to copy the rawsource and line
+ for docutils-0.16 or older versions.
+
+ refs: https://sourceforge.net/p/docutils/patches/165/
+ """
newnode = self.__class__(self.rawsource, **self.attributes)
if isinstance(self, nodes.Element):
newnode.source = self.source
diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py
index 59fd059e6..5ee2eccc8 100644
--- a/sphinx/util/osutil.py
+++ b/sphinx/util/osutil.py
@@ -4,12 +4,11 @@
Operating system-related utility functions for Sphinx.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import contextlib
-import errno
import filecmp
import os
import re
@@ -18,9 +17,9 @@ import sys
import warnings
from io import StringIO
from os import path
-from typing import Any, Generator, Iterator, List, Optional, Tuple
+from typing import Any, Generator, Iterator, List, Optional, Type
-from sphinx.deprecation import RemovedInSphinx40Warning
+from sphinx.deprecation import RemovedInSphinx50Warning
try:
# for ALT Linux (#6712)
@@ -28,15 +27,6 @@ try:
except ImportError:
Path = None # type: ignore
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
-
-# Errnos that we need.
-EEXIST = getattr(errno, 'EEXIST', 0) # RemovedInSphinx40Warning
-ENOENT = getattr(errno, 'ENOENT', 0) # RemovedInSphinx40Warning
-EPIPE = getattr(errno, 'EPIPE', 0) # RemovedInSphinx40Warning
-EINVAL = getattr(errno, 'EINVAL', 0) # RemovedInSphinx40Warning
# SEP separates path elements in the canonical file names
#
@@ -83,13 +73,6 @@ def ensuredir(path: str) -> None:
os.makedirs(path, exist_ok=True)
-def walk(top: str, topdown: bool = True, followlinks: bool = False) -> Iterator[Tuple[str, List[str], List[str]]]: # NOQA
- warnings.warn('sphinx.util.osutil.walk() is deprecated for removal. '
- 'Please use os.walk() instead.',
- RemovedInSphinx40Warning, stacklevel=2)
- return os.walk(top, topdown=topdown, followlinks=followlinks)
-
-
def mtimes_of_files(dirnames: List[str], suffix: str) -> Iterator[float]:
for dirname in dirnames:
for root, dirs, files in os.walk(dirname):
@@ -103,6 +86,9 @@ def mtimes_of_files(dirnames: List[str], suffix: str) -> Iterator[float]:
def movefile(source: str, dest: str) -> None:
"""Move a file, removing the destination if it exists."""
+ warnings.warn('sphinx.util.osutil.movefile() is deprecated for removal. '
+ 'Please use os.replace() instead.',
+ RemovedInSphinx50Warning, stacklevel=2)
if os.path.exists(dest):
try:
os.unlink(dest)
@@ -175,13 +161,6 @@ def abspath(pathdir: str) -> str:
return pathdir
-def getcwd() -> str:
- warnings.warn('sphinx.util.osutil.getcwd() is deprecated. '
- 'Please use os.getcwd() instead.',
- RemovedInSphinx40Warning, stacklevel=2)
- return os.getcwd()
-
-
@contextlib.contextmanager
def cd(target_dir: str) -> Generator[None, None, None]:
cwd = os.getcwd()
diff --git a/sphinx/util/parallel.py b/sphinx/util/parallel.py
index ddcdaa316..ab27a5128 100644
--- a/sphinx/util/parallel.py
+++ b/sphinx/util/parallel.py
@@ -4,7 +4,7 @@
Parallel building utilities.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/png.py b/sphinx/util/png.py
index 3dd22d7ff..2ab5a836f 100644
--- a/sphinx/util/png.py
+++ b/sphinx/util/png.py
@@ -4,7 +4,7 @@
PNG image manipulation helpers.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py
index 6474cf141..bb0c0b685 100644
--- a/sphinx/util/pycompat.py
+++ b/sphinx/util/pycompat.py
@@ -4,32 +4,25 @@
Stuff for Python version compatibility.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
-import html
-import io
-import sys
-import textwrap
import warnings
from typing import Any, Callable
-from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
-from sphinx.locale import __
-from sphinx.util import logging
-from sphinx.util.console import terminal_safe
-from sphinx.util.typing import NoneType
-
-logger = logging.getLogger(__name__)
-
+from sphinx.deprecation import RemovedInSphinx60Warning
# ------------------------------------------------------------------------------
# Python 2/3 compatibility
+
# convert_with_2to3():
# support for running 2to3 over config files
def convert_with_2to3(filepath: str) -> str:
+ warnings.warn('convert_with_2to3() is deprecated',
+ RemovedInSphinx60Warning, stacklevel=2)
+
try:
from lib2to3.pgen2.parse import ParseError
from lib2to3.refactor import RefactoringTool, get_fixers_from_package
@@ -53,57 +46,14 @@ def convert_with_2to3(filepath: str) -> str:
return str(tree)
-class UnicodeMixin:
- """Mixin class to handle defining the proper __str__/__unicode__
- methods in Python 2 or 3.
-
- .. deprecated:: 2.0
- """
- def __str__(self) -> str:
- warnings.warn('UnicodeMixin is deprecated',
- RemovedInSphinx40Warning, stacklevel=2)
- return self.__unicode__() # type: ignore
-
-
def execfile_(filepath: str, _globals: Any, open: Callable = open) -> None:
+ warnings.warn('execfile_() is deprecated',
+ RemovedInSphinx60Warning, stacklevel=2)
from sphinx.util.osutil import fs_encoding
with open(filepath, 'rb') as f:
source = f.read()
# compile to a code object, handle syntax errors
filepath_enc = filepath.encode(fs_encoding)
- try:
- code = compile(source, filepath_enc, 'exec')
- except SyntaxError:
- # maybe the file uses 2.x syntax; try to refactor to
- # 3.x syntax using 2to3
- source = convert_with_2to3(filepath)
- code = compile(source, filepath_enc, 'exec')
- # TODO: When support for evaluating Python 2 syntax is removed,
- # deprecate convert_with_2to3().
- logger.warning(__('Support for evaluating Python 2 syntax is deprecated '
- 'and will be removed in Sphinx 4.0. '
- 'Convert %s to Python 3 syntax.'),
- filepath)
+ code = compile(source, filepath_enc, 'exec')
exec(code, _globals)
-
-
-deprecated_alias('sphinx.util.pycompat',
- {
- 'NoneType': NoneType,
- 'TextIOWrapper': io.TextIOWrapper,
- 'htmlescape': html.escape,
- 'indent': textwrap.indent,
- 'terminal_safe': terminal_safe,
- 'sys_encoding': sys.getdefaultencoding(),
- 'u': '',
- },
- RemovedInSphinx40Warning,
- {
- 'NoneType': 'sphinx.util.typing.NoneType',
- 'TextIOWrapper': 'io.TextIOWrapper',
- 'htmlescape': 'html.escape',
- 'indent': 'textwrap.indent',
- 'terminal_safe': 'sphinx.util.console.terminal_safe',
- 'sys_encoding': 'sys.getdefaultencoding',
- })
diff --git a/sphinx/util/requests.py b/sphinx/util/requests.py
index ca570249a..8ae435d41 100644
--- a/sphinx/util/requests.py
+++ b/sphinx/util/requests.py
@@ -4,7 +4,7 @@
Simple requests package loader
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/rst.py b/sphinx/util/rst.py
index b67b68c4a..79ede3432 100644
--- a/sphinx/util/rst.py
+++ b/sphinx/util/rst.py
@@ -4,7 +4,7 @@
reST helper functions.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/smartypants.py b/sphinx/util/smartypants.py
index ec6b2172c..ad13fcaad 100644
--- a/sphinx/util/smartypants.py
+++ b/sphinx/util/smartypants.py
@@ -26,12 +26,17 @@
"""
import re
+import warnings
from typing import Generator, Iterable, Tuple
from docutils.utils import smartquotes
+from sphinx.deprecation import RemovedInSphinx60Warning
from sphinx.util.docutils import __version_info__ as docutils_version
+warnings.warn('sphinx.util.smartypants is deprecated.',
+ RemovedInSphinx60Warning)
+
langquotes = {'af': '“”‘’',
'af-x-altquot': '„”‚’',
'bg': '„“‚‘', # Bulgarian, https://bg.wikipedia.org/wiki/Кавички
diff --git a/sphinx/util/stemmer/__init__.py b/sphinx/util/stemmer/__init__.py
index 94aaa136d..6470dfe2b 100644
--- a/sphinx/util/stemmer/__init__.py
+++ b/sphinx/util/stemmer/__init__.py
@@ -4,7 +4,7 @@
Word stemming utilities for Sphinx.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/tags.py b/sphinx/util/tags.py
index 1662a06f0..c50231220 100644
--- a/sphinx/util/tags.py
+++ b/sphinx/util/tags.py
@@ -2,7 +2,7 @@
sphinx.util.tags
~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/template.py b/sphinx/util/template.py
index 8785928a9..a61008602 100644
--- a/sphinx/util/template.py
+++ b/sphinx/util/template.py
@@ -4,7 +4,7 @@
Templates utility functions for Sphinx.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/texescape.py b/sphinx/util/texescape.py
index 98e763aaf..7323d2c27 100644
--- a/sphinx/util/texescape.py
+++ b/sphinx/util/texescape.py
@@ -4,15 +4,13 @@
TeX escaping helper.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
from typing import Dict
-from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
-
tex_replacements = [
# map TeX special chars
('$', r'\$'),
@@ -108,14 +106,6 @@ _tex_hlescape_map = {} # type: Dict[int, str]
_tex_hlescape_map_without_unicode = {} # type: Dict[int, str]
-deprecated_alias('sphinx.util.texescape',
- {
- 'tex_escape_map': _tex_escape_map,
- 'tex_hl_escape_map_new': _tex_hlescape_map,
- },
- RemovedInSphinx40Warning)
-
-
def escape(s: str, latex_engine: str = None) -> str:
"""Escape text for LaTeX output."""
if latex_engine in ('lualatex', 'xelatex'):
diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py
index 222e2edf2..ae9a3de28 100644
--- a/sphinx/util/typing.py
+++ b/sphinx/util/typing.py
@@ -4,12 +4,13 @@
The composit types for Sphinx.
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys
import typing
+from struct import Struct
from typing import Any, Callable, Dict, Generator, List, Optional, Tuple, TypeVar, Union
from docutils import nodes
@@ -21,7 +22,7 @@ else:
from typing import _ForwardRef # type: ignore
class ForwardRef:
- """A pseudo ForwardRef class for py35 and py36."""
+ """A pseudo ForwardRef class for py36."""
def __init__(self, arg: Any, is_argument: bool = True) -> None:
self.arg = arg
@@ -75,9 +76,6 @@ def get_type_hints(obj: Any, globalns: Dict = None, localns: Dict = None) -> Dic
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:
@@ -94,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'):
@@ -153,6 +154,8 @@ def _restify_py37(cls: Optional["Type"]) -> str:
return ':obj:`%s`' % cls._name
else:
return ':obj:`%s.%s`' % (cls.__module__, cls._name)
+ elif isinstance(cls, ForwardRef):
+ return ':class:`%s`' % cls.__forward_arg__
else:
# not a class (ex. TypeVar)
return ':obj:`%s.%s`' % (cls.__module__, cls.__name__)
@@ -177,7 +180,7 @@ def _restify_py36(cls: Optional["Type"]) -> str:
qualname = repr(cls)
if (isinstance(cls, typing.TupleMeta) and # type: ignore
- not hasattr(cls, '__tuple_params__')): # for Python 3.6
+ not hasattr(cls, '__tuple_params__')):
params = cls.__args__
if params:
param_str = ', '.join(restify(p) for p in params)
@@ -185,40 +188,22 @@ def _restify_py36(cls: Optional["Type"]) -> str:
else:
return ':class:`%s`' % qualname
elif isinstance(cls, typing.GenericMeta):
- params = None
- if hasattr(cls, '__args__'):
- # for Python 3.5.2+
- if cls.__args__ is None or len(cls.__args__) <= 2: # type: ignore # NOQA
- params = cls.__args__ # type: ignore
- elif cls.__origin__ == Generator: # type: ignore
- params = cls.__args__ # type: ignore
- else: # typing.Callable
- args = ', '.join(restify(arg) for arg in cls.__args__[:-1]) # type: ignore
- result = restify(cls.__args__[-1]) # type: ignore
- return ':class:`%s`\\ [[%s], %s]' % (qualname, args, result)
- elif hasattr(cls, '__parameters__'):
- # for Python 3.5.0 and 3.5.1
- params = cls.__parameters__ # type: ignore
+ if cls.__args__ is None or len(cls.__args__) <= 2: # type: ignore # NOQA
+ params = cls.__args__ # type: ignore
+ elif cls.__origin__ == Generator: # type: ignore
+ params = cls.__args__ # type: ignore
+ else: # typing.Callable
+ args = ', '.join(restify(arg) for arg in cls.__args__[:-1]) # type: ignore
+ result = restify(cls.__args__[-1]) # type: ignore
+ return ':class:`%s`\\ [[%s], %s]' % (qualname, args, result)
if params:
param_str = ', '.join(restify(p) for p in params)
return ':class:`%s`\\ [%s]' % (qualname, param_str)
else:
return ':class:`%s`' % qualname
- elif (hasattr(typing, 'UnionMeta') and
- isinstance(cls, typing.UnionMeta) and # type: ignore
- hasattr(cls, '__union_params__')): # for Python 3.5
- params = cls.__union_params__
- if params is not None:
- if len(params) == 2 and params[1] is NoneType:
- return ':obj:`Optional`\\ [%s]' % restify(params[0])
- else:
- param_str = ', '.join(restify(p) for p in params)
- return ':obj:`%s`\\ [%s]' % (qualname, param_str)
- else:
- return ':obj:`%s`' % qualname
elif (hasattr(cls, '__origin__') and
- cls.__origin__ is typing.Union): # for Python 3.5.2+
+ cls.__origin__ is typing.Union):
params = cls.__args__
if params is not None:
if len(params) > 1 and params[-1] is NoneType:
@@ -232,31 +217,6 @@ def _restify_py36(cls: Optional["Type"]) -> str:
return ':obj:`Union`\\ [%s]' % param_str
else:
return ':obj:`Union`'
- elif (isinstance(cls, typing.CallableMeta) and # type: ignore
- getattr(cls, '__args__', None) is not None and
- hasattr(cls, '__result__')): # for Python 3.5
- # Skipped in the case of plain typing.Callable
- args = cls.__args__
- if args is None:
- return qualname
- elif args is Ellipsis:
- args_str = '...'
- else:
- formatted_args = (restify(a) for a in args) # type: ignore
- args_str = '[%s]' % ', '.join(formatted_args)
-
- return ':class:`%s`\\ [%s, %s]' % (qualname, args_str, stringify(cls.__result__))
- elif (isinstance(cls, typing.TupleMeta) and # type: ignore
- hasattr(cls, '__tuple_params__') and
- hasattr(cls, '__tuple_use_ellipsis__')): # for Python 3.5
- params = cls.__tuple_params__
- if params is not None:
- param_strings = [restify(p) for p in params]
- if cls.__tuple_use_ellipsis__:
- param_strings.append('...')
- return ':class:`%s`\\ [%s]' % (qualname, ', '.join(param_strings))
- else:
- return ':class:`%s`' % qualname
elif hasattr(cls, '__qualname__'):
if cls.__module__ == 'typing':
return ':class:`%s`' % cls.__qualname__
@@ -303,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)
@@ -363,7 +326,7 @@ def _stringify_py37(annotation: Any) -> str:
def _stringify_py36(annotation: Any) -> str:
- """stringify() for py35 and py36."""
+ """stringify() for py36."""
module = getattr(annotation, '__module__', None)
if module == 'typing':
if getattr(annotation, '_name', None):
@@ -391,35 +354,20 @@ def _stringify_py36(annotation: Any) -> str:
return qualname
elif isinstance(annotation, typing.GenericMeta):
params = None
- if hasattr(annotation, '__args__'):
- # for Python 3.5.2+
- if annotation.__args__ is None or len(annotation.__args__) <= 2: # type: ignore # NOQA
- params = annotation.__args__ # type: ignore
- elif annotation.__origin__ == Generator: # type: ignore
- params = annotation.__args__ # type: ignore
- else: # typing.Callable
- args = ', '.join(stringify(arg) for arg
- in annotation.__args__[:-1]) # type: ignore
- result = stringify(annotation.__args__[-1]) # type: ignore
- return '%s[[%s], %s]' % (qualname, args, result)
- elif hasattr(annotation, '__parameters__'):
- # for Python 3.5.0 and 3.5.1
- params = annotation.__parameters__ # type: ignore
+ if annotation.__args__ is None or len(annotation.__args__) <= 2: # type: ignore # NOQA
+ params = annotation.__args__ # type: ignore
+ elif annotation.__origin__ == Generator: # type: ignore
+ params = annotation.__args__ # type: ignore
+ else: # typing.Callable
+ args = ', '.join(stringify(arg) for arg
+ in annotation.__args__[:-1]) # type: ignore
+ result = stringify(annotation.__args__[-1]) # type: ignore
+ return '%s[[%s], %s]' % (qualname, args, result)
if params is not None:
param_str = ', '.join(stringify(p) for p in params)
return '%s[%s]' % (qualname, param_str)
- elif (hasattr(typing, 'UnionMeta') and
- isinstance(annotation, typing.UnionMeta) and # type: ignore
- hasattr(annotation, '__union_params__')): # for Python 3.5
- params = annotation.__union_params__
- if params is not None:
- if len(params) == 2 and params[1] is NoneType:
- return 'Optional[%s]' % stringify(params[0])
- else:
- param_str = ', '.join(stringify(p) for p in params)
- return '%s[%s]' % (qualname, param_str)
elif (hasattr(annotation, '__origin__') and
- annotation.__origin__ is typing.Union): # for Python 3.5.2+
+ annotation.__origin__ is typing.Union):
params = annotation.__args__
if params is not None:
if len(params) > 1 and params[-1] is NoneType:
@@ -431,30 +379,5 @@ def _stringify_py36(annotation: Any) -> str:
else:
param_str = ', '.join(stringify(p) for p in params)
return 'Union[%s]' % param_str
- elif (isinstance(annotation, typing.CallableMeta) and # type: ignore
- getattr(annotation, '__args__', None) is not None and
- hasattr(annotation, '__result__')): # for Python 3.5
- # Skipped in the case of plain typing.Callable
- args = annotation.__args__
- if args is None:
- return qualname
- elif args is Ellipsis:
- args_str = '...'
- else:
- formatted_args = (stringify(a) for a in args)
- args_str = '[%s]' % ', '.join(formatted_args)
- return '%s[%s, %s]' % (qualname,
- args_str,
- stringify(annotation.__result__))
- elif (isinstance(annotation, typing.TupleMeta) and # type: ignore
- hasattr(annotation, '__tuple_params__') and
- hasattr(annotation, '__tuple_use_ellipsis__')): # for Python 3.5
- params = annotation.__tuple_params__
- if params is not None:
- param_strings = [stringify(p) for p in params]
- if annotation.__tuple_use_ellipsis__:
- param_strings.append('...')
- return '%s[%s]' % (qualname,
- ', '.join(param_strings))
return qualname