diff options
author | Takeshi KOMIYA <i.tkomiya@gmail.com> | 2021-01-23 14:44:13 +0300 |
---|---|---|
committer | Takeshi KOMIYA <i.tkomiya@gmail.com> | 2021-01-23 14:44:13 +0300 |
commit | ccf4ae37298d9088fa7bc1ad8d311be203aa795c (patch) | |
tree | 90e9dbc0f5eb01dc3415719e737597f44c68ad4d /sphinx/util | |
parent | 65a69965a1dba7e8f61caf53d3d8a9251eab4e19 (diff) | |
parent | 37fc43a4d389e173902207e753f4c9e28465e454 (diff) |
Merge branch 'master' into 8510_html_logo_url
Diffstat (limited to 'sphinx/util')
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 |