From f8e8b4745eb560d03ba2089a0f39632e7a3dc938 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 13 Jan 2024 22:12:16 +0000 Subject: [PATCH] Enable ANN001, ANN102, ANN202 --- .ruff.toml | 6 +-- sphinx/addnodes.py | 2 +- sphinx/builders/html/__init__.py | 4 +- sphinx/builders/html/_assets.py | 30 +++++++------- sphinx/config.py | 31 ++++++++------- sphinx/deprecation.py | 2 +- sphinx/directives/other.py | 4 +- sphinx/domains/c.py | 18 ++++----- sphinx/domains/cpp.py | 10 ++--- sphinx/domains/python.py | 20 +++++----- sphinx/environment/__init__.py | 10 ++--- sphinx/ext/autodoc/__init__.py | 53 +++++++++++++++---------- sphinx/ext/autodoc/preserve_defaults.py | 2 +- sphinx/ext/napoleon/docstring.py | 18 +++++---- sphinx/locale/__init__.py | 12 +++--- sphinx/pycode/__init__.py | 9 +++-- sphinx/pycode/ast.py | 4 +- sphinx/util/__init__.py | 4 +- sphinx/util/_pathlib.py | 37 ++++++++--------- sphinx/util/docutils.py | 10 ++--- sphinx/util/inventory.py | 14 +++++-- sphinx/util/osutil.py | 13 ++++-- sphinx/util/template.py | 8 +++- sphinx/util/typing.py | 4 +- 24 files changed, 181 insertions(+), 144 deletions(-) diff --git a/.ruff.toml b/.ruff.toml index a50a8fbe7..b3d92b55a 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -33,13 +33,13 @@ select = [ # airflow ('AIR') # Airflow is not used in Sphinx # flake8-annotations ('ANN') -# "ANN001", # Missing type annotation for function argument `{name}` + "ANN001", # Missing type annotation for function argument `{name}` "ANN002", # Missing type annotation for `*{name}` "ANN003", # Missing type annotation for `**{name}` # "ANN101", # Missing type annotation for `{name}` in method -# "ANN102", # Missing type annotation for `{name}` in classmethod + "ANN102", # Missing type annotation for `{name}` in classmethod "ANN201", # Missing return type annotation for public function `{name}` -# "ANN202", # Missing return type annotation for private function `{name}` + "ANN202", # Missing return type annotation for private function `{name}` # "ANN204", # Missing return type annotation for special method `{name}` "ANN205", # Missing return type annotation for staticmethod `{name}` "ANN206", # Missing return type annotation for classmethod `{name}` diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 149e8c685..75bf8e5b2 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -20,7 +20,7 @@ _DEPRECATED_OBJECTS = { } -def __getattr__(name): +def __getattr__(name: str) -> Any: if name not in _DEPRECATED_OBJECTS: msg = f'module {__name__!r} has no attribute {name!r}' raise AttributeError(msg) diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py index 0a6a0a8f0..2a2f88dcc 100644 --- a/sphinx/builders/html/__init__.py +++ b/sphinx/builders/html/__init__.py @@ -113,7 +113,7 @@ class BuildInfo: """ @classmethod - def load(cls, f: IO) -> BuildInfo: + def load(cls: type[BuildInfo], f: IO) -> BuildInfo: try: lines = f.readlines() assert lines[0].rstrip() == '# Sphinx build info version 1' @@ -1372,7 +1372,7 @@ _DEPRECATED_OBJECTS = { } -def __getattr__(name): +def __getattr__(name: str) -> Any: if name not in _DEPRECATED_OBJECTS: msg = f'module {__name__!r} has no attribute {name!r}' raise AttributeError(msg) diff --git a/sphinx/builders/html/_assets.py b/sphinx/builders/html/_assets.py index c2fb691cb..699a160ee 100644 --- a/sphinx/builders/html/_assets.py +++ b/sphinx/builders/html/_assets.py @@ -3,7 +3,7 @@ from __future__ import annotations import os import warnings import zlib -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any, NoReturn from sphinx.deprecation import RemovedInSphinx90Warning from sphinx.errors import ThemeError @@ -29,13 +29,13 @@ class _CascadingStyleSheet: object.__setattr__(self, 'priority', priority) object.__setattr__(self, 'attributes', {'rel': rel, 'type': type} | attributes) - def __str__(self): + def __str__(self) -> str: attr = ', '.join(f'{k}={v!r}' for k, v in self.attributes.items()) return (f'{self.__class__.__name__}({self.filename!r}, ' f'priority={self.priority}, ' f'{attr})') - def __eq__(self, other): + def __eq__(self, other: object) -> bool: if isinstance(other, str): warnings.warn('The str interface for _CascadingStyleSheet objects is deprecated. ' 'Use css.filename instead.', RemovedInSphinx90Warning, stacklevel=2) @@ -46,23 +46,23 @@ class _CascadingStyleSheet: and self.priority == other.priority and self.attributes == other.attributes) - def __hash__(self): + def __hash__(self) -> int: return hash((self.filename, self.priority, *sorted(self.attributes.items()))) - def __setattr__(self, key, value): + def __setattr__(self, key: str, value: Any) -> NoReturn: msg = f'{self.__class__.__name__} is immutable' raise AttributeError(msg) - def __delattr__(self, key): + def __delattr__(self, key: str) -> NoReturn: msg = f'{self.__class__.__name__} is immutable' raise AttributeError(msg) - def __getattr__(self, key): + def __getattr__(self, key: str) -> str: warnings.warn('The str interface for _CascadingStyleSheet objects is deprecated. ' 'Use css.filename instead.', RemovedInSphinx90Warning, stacklevel=2) return getattr(os.fspath(self.filename), key) - def __getitem__(self, key): + def __getitem__(self, key: int | slice) -> str: warnings.warn('The str interface for _CascadingStyleSheet objects is deprecated. ' 'Use css.filename instead.', RemovedInSphinx90Warning, stacklevel=2) return os.fspath(self.filename)[key] @@ -83,7 +83,7 @@ class _JavaScript: object.__setattr__(self, 'priority', priority) object.__setattr__(self, 'attributes', attributes) - def __str__(self): + def __str__(self) -> str: attr = '' if self.attributes: attr = ', ' + ', '.join(f'{k}={v!r}' for k, v in self.attributes.items()) @@ -91,7 +91,7 @@ class _JavaScript: f'priority={self.priority}' f'{attr})') - def __eq__(self, other): + def __eq__(self, other: object) -> bool: if isinstance(other, str): warnings.warn('The str interface for _JavaScript objects is deprecated. ' 'Use js.filename instead.', RemovedInSphinx90Warning, stacklevel=2) @@ -102,23 +102,23 @@ class _JavaScript: and self.priority == other.priority and self.attributes == other.attributes) - def __hash__(self): + def __hash__(self) -> int: return hash((self.filename, self.priority, *sorted(self.attributes.items()))) - def __setattr__(self, key, value): + def __setattr__(self, key: str, value: Any) -> NoReturn: msg = f'{self.__class__.__name__} is immutable' raise AttributeError(msg) - def __delattr__(self, key): + def __delattr__(self, key: str) -> NoReturn: msg = f'{self.__class__.__name__} is immutable' raise AttributeError(msg) - def __getattr__(self, key): + def __getattr__(self, key: str) -> str: warnings.warn('The str interface for _JavaScript objects is deprecated. ' 'Use js.filename instead.', RemovedInSphinx90Warning, stacklevel=2) return getattr(os.fspath(self.filename), key) - def __getitem__(self, key): + def __getitem__(self, key: int | slice) -> str: warnings.warn('The str interface for _JavaScript objects is deprecated. ' 'Use js.filename instead.', RemovedInSphinx90Warning, stacklevel=2) return os.fspath(self.filename)[key] diff --git a/sphinx/config.py b/sphinx/config.py index 85524ab06..28d19dba9 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -8,7 +8,7 @@ import traceback import types import warnings from os import getenv, path -from typing import TYPE_CHECKING, Any, Literal, NamedTuple +from typing import TYPE_CHECKING, Any, Literal, NamedTuple, Union from sphinx.deprecation import RemovedInSphinx90Warning from sphinx.errors import ConfigError, ExtensionError @@ -81,18 +81,21 @@ class ENUM: return value in self.candidates +_OptValidTypes = Union[tuple[()], tuple[type, ...], frozenset[type], ENUM] + + class _Opt: __slots__ = 'default', 'rebuild', 'valid_types' default: Any rebuild: _ConfigRebuild - valid_types: tuple[()] | tuple[type, ...] | frozenset[type] | ENUM + valid_types: _OptValidTypes def __init__( self, default: Any, rebuild: _ConfigRebuild, - valid_types: tuple[()] | tuple[type, ...] | frozenset[type] | ENUM, + valid_types: _OptValidTypes, ) -> None: """Configuration option type for Sphinx. @@ -106,7 +109,7 @@ class _Opt: super().__setattr__('rebuild', rebuild) super().__setattr__('valid_types', valid_types) - def __repr__(self): + def __repr__(self) -> str: return ( f'{self.__class__.__qualname__}(' f'default={self.default!r}, ' @@ -114,45 +117,45 @@ class _Opt: f'valid_types={self.valid_types!r})' ) - def __eq__(self, other): - if self.__class__ is other.__class__: + def __eq__(self, other: object) -> bool: + if isinstance(other, _Opt): self_tpl = (self.default, self.rebuild, self.valid_types) other_tpl = (other.default, other.rebuild, other.valid_types) return self_tpl == other_tpl return NotImplemented - def __lt__(self, other): + def __lt__(self, other: _Opt) -> bool: if self.__class__ is other.__class__: self_tpl = (self.default, self.rebuild, self.valid_types) other_tpl = (other.default, other.rebuild, other.valid_types) return self_tpl > other_tpl return NotImplemented - def __hash__(self): + def __hash__(self) -> int: return hash((self.default, self.rebuild, self.valid_types)) - def __setattr__(self, key, value): + def __setattr__(self, key: str, value: Any) -> None: if key in {'default', 'rebuild', 'valid_types'}: msg = f'{self.__class__.__name__!r} object does not support assignment to {key!r}' raise TypeError(msg) super().__setattr__(key, value) - def __delattr__(self, key): + def __delattr__(self, key: str) -> None: if key in {'default', 'rebuild', 'valid_types'}: msg = f'{self.__class__.__name__!r} object does not support deletion of {key!r}' raise TypeError(msg) super().__delattr__(key) - def __getstate__(self): + def __getstate__(self) -> tuple[Any, _ConfigRebuild, _OptValidTypes]: return self.default, self.rebuild, self.valid_types - def __setstate__(self, state): + def __setstate__(self, state: tuple[Any, _ConfigRebuild, _OptValidTypes]) -> None: default, rebuild, valid_types = state super().__setattr__('default', default) super().__setattr__('rebuild', rebuild) super().__setattr__('valid_types', valid_types) - def __getitem__(self, item): + def __getitem__(self, item: int | slice) -> Any: warnings.warn( f'The {self.__class__.__name__!r} object tuple interface is deprecated, ' "use attribute access instead for 'default', 'rebuild', and 'valid_types'.", @@ -283,7 +286,7 @@ class Config: return self._overrides @classmethod - def read(cls, confdir: str | os.PathLike[str], overrides: dict | None = None, + def read(cls: type[Config], confdir: str | os.PathLike[str], overrides: dict | None = None, tags: Tags | None = None) -> Config: """Create a Config object from configuration file.""" filename = path.join(confdir, CONFIG_FILENAME) diff --git a/sphinx/deprecation.py b/sphinx/deprecation.py index 8a242d7da..e36f4dac4 100644 --- a/sphinx/deprecation.py +++ b/sphinx/deprecation.py @@ -35,7 +35,7 @@ def _deprecation_warning( } - def __getattr__(name): + def __getattr__(name: str) -> Any: if name not in _DEPRECATED_OBJECTS: msg = f'module {__name__!r} has no attribute {name!r}' raise AttributeError(msg) diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py index 65cd90b65..7639406f9 100644 --- a/sphinx/directives/other.py +++ b/sphinx/directives/other.py @@ -378,7 +378,7 @@ class Include(BaseInclude, SphinxDirective): # In the future, docutils will hopefully offer a way for Sphinx # to provide the RST parser to use # when parsing RST text that comes in via Include directive. - def _insert_input(include_lines, source): + def _insert_input(include_lines: list[str], source: str) -> None: # First, we need to combine the lines back into text so that # we can send it with the include-read event. # In docutils 0.18 and later, there are two lines at the end @@ -405,7 +405,7 @@ class Include(BaseInclude, SphinxDirective): # Only enable this patch if there are listeners for 'include-read'. if self.env.app.events.listeners.get('include-read'): # See https://github.com/python/mypy/issues/2427 for details on the mypy issue - self.state_machine.insert_input = _insert_input # type: ignore[method-assign] + self.state_machine.insert_input = _insert_input # type: ignore[assignment] if self.arguments[0].startswith('<') and \ self.arguments[0].endswith('>'): diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 868951868..01f4e9fc9 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -365,14 +365,14 @@ class ASTIdExpression(ASTExpression): class ASTParenExpr(ASTExpression): - def __init__(self, expr): + def __init__(self, expr: ASTExpression) -> None: self.expr = expr def _stringify(self, transform: StringifyTransform) -> str: return '(' + transform(self.expr) + ')' def get_id(self, version: int) -> str: - return self.expr.get_id(version) + return self.expr.get_id(version) # type: ignore[attr-defined] def describe_signature(self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol) -> None: @@ -433,7 +433,7 @@ class ASTPostfixDec(ASTPostfixOp): class ASTPostfixMemberOfPointer(ASTPostfixOp): - def __init__(self, name): + def __init__(self, name: ASTNestedName) -> None: self.name = name def _stringify(self, transform: StringifyTransform) -> str: @@ -488,7 +488,7 @@ class ASTUnaryOpExpr(ASTExpression): class ASTSizeofType(ASTExpression): - def __init__(self, typ): + def __init__(self, typ: ASTType) -> None: self.typ = typ def _stringify(self, transform: StringifyTransform) -> str: @@ -1547,7 +1547,7 @@ class Symbol: def __copy__(self): raise AssertionError # shouldn't happen - def __deepcopy__(self, memo): + def __deepcopy__(self, memo: Any) -> Symbol: if self.parent: raise AssertionError # shouldn't happen # the domain base class makes a copy of the initial data, which is fine @@ -2457,7 +2457,7 @@ class DefinitionParser(BaseParser): # additive = multiplicative +, - # multiplicative = pm *, /, % # pm = cast .*, ->* - def _parse_bin_op_expr(self, opId): + def _parse_bin_op_expr(self: DefinitionParser, opId: int) -> ASTExpression: if opId + 1 == len(_expression_bin_ops): def parser() -> ASTExpression: return self._parse_cast_expression() @@ -2493,7 +2493,7 @@ class DefinitionParser(BaseParser): self.pos = pos if not oneMore: break - return ASTBinOpExpr(exprs, ops) + return ASTBinOpExpr(exprs, ops) # type: ignore[return-value] return _parse_bin_op_expr(self, 0) def _parse_conditional_expression_tail(self, orExprHead: Any) -> ASTExpression | None: @@ -2827,7 +2827,7 @@ class DefinitionParser(BaseParser): size = None else: - def parser(): + def parser() -> ASTExpression: return self._parse_expression() size = self._parse_expression_fallback([']'], parser) self.skip_ws() @@ -2951,7 +2951,7 @@ class DefinitionParser(BaseParser): self.fail("Internal error, initializer for outer '%s' not " "implemented." % outer) - def parser(): + def parser() -> ASTExpression: return self._parse_assignment_expression() value = self._parse_expression_fallback(fallbackEnd, parser, allow=allowFallback) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 416d3ec3f..f5622bef2 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -3,7 +3,7 @@ from __future__ import annotations import re -from typing import TYPE_CHECKING, Any, Callable, TypeVar +from typing import TYPE_CHECKING, Any, Callable, NoReturn, TypeVar from docutils import nodes from docutils.parsers.rst import directives @@ -4228,10 +4228,10 @@ class Symbol: debug_lookup = False # overridden by the corresponding config value debug_show_tree = False # overridden by the corresponding config value - def __copy__(self): + def __copy__(self) -> NoReturn: raise AssertionError # shouldn't happen - def __deepcopy__(self, memo): + def __deepcopy__(self, memo: Any) -> Symbol: if self.parent: raise AssertionError # shouldn't happen # the domain base class makes a copy of the initial data, which is fine @@ -4856,7 +4856,7 @@ class Symbol: Symbol.debug_print("merge_with:") assert other is not None - def unconditionalAdd(self, otherChild): + def unconditionalAdd(self: Symbol, otherChild: Symbol) -> None: # TODO: hmm, should we prune by docnames? self._children.append(otherChild) otherChild.parent = self @@ -8232,7 +8232,7 @@ def setup(app: Sphinx) -> dict[str, Any]: app.add_config_value("cpp_debug_lookup", False, '') app.add_config_value("cpp_debug_show_tree", False, '') - def initStuff(app): + def initStuff(app: Sphinx) -> None: Symbol.debug_lookup = app.config.cpp_debug_lookup Symbol.debug_show_tree = app.config.cpp_debug_show_tree app.config.cpp_index_common_prefix.sort(reverse=True) diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index fcd2a73ce..ebf6dd9f7 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -11,6 +11,7 @@ import operator import re import token import typing +from collections import deque from inspect import Parameter from typing import TYPE_CHECKING, Any, NamedTuple, cast @@ -323,16 +324,17 @@ class _TypeParameterListParser(TokenProcessor): self.type_params.append(type_param) def _build_identifier(self, tokens: list[Token]) -> str: - from itertools import chain, tee + from itertools import chain, islice - def pairwise(iterable): - a, b = tee(iterable) - next(b, None) - return zip(a, b) - - def triplewise(iterable): - for (a, _z), (b, c) in pairwise(pairwise(iterable)): - yield a, b, c + def triplewise(iterable: Iterable[Token]) -> Iterator[tuple[Token, ...]]: + # sliding_window('ABCDEFG', 4) --> ABCD BCDE CDEF DEFG + it = iter(iterable) + window = deque(islice(it, 3), maxlen=3) + if len(window) == 3: + yield tuple(window) + for x in it: + window.append(x) + yield tuple(window) idents: list[str] = [] tokens: Iterable[Token] = iter(tokens) # type: ignore[no-redef] diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index 72753e240..cc015c343 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -9,7 +9,7 @@ import time from collections import defaultdict from copy import copy from os import path -from typing import TYPE_CHECKING, Any, Callable +from typing import TYPE_CHECKING, Any, Callable, NoReturn from sphinx import addnodes from sphinx.environment.adapters import toctree as toctree_adapters @@ -126,10 +126,10 @@ if TYPE_CHECKING: @overload def __getitem__(self, key: str) -> Domain: ... # NoQA: E704 def __getitem__(self, key): raise NotImplementedError # NoQA: E704 - def __setitem__(self, key, value): raise NotImplementedError # NoQA: E704 - def __delitem__(self, key): raise NotImplementedError # NoQA: E704 - def __iter__(self): raise NotImplementedError # NoQA: E704 - def __len__(self): raise NotImplementedError # NoQA: E704 + def __setitem__(self, key: str, value: Domain) -> NoReturn: raise NotImplementedError # NoQA: E704 + def __delitem__(self, key: str) -> NoReturn: raise NotImplementedError # NoQA: E704 + def __iter__(self) -> NoReturn: raise NotImplementedError # NoQA: E704 + def __len__(self) -> NoReturn: raise NotImplementedError # NoQA: E704 else: _DomainsType = dict diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 2b614e3c9..3aaa927bb 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -277,7 +277,7 @@ class ObjectMember: self.skipped = skipped self.class_ = class_ - def __getitem__(self, index): + def __getitem__(self, index: int) -> Any: warnings.warn('The tuple interface of ObjectMember is deprecated. ' 'Use (obj.__name__, obj.object) instead.', RemovedInSphinx80Warning, stacklevel=2) @@ -321,8 +321,9 @@ class Documenter: return autodoc_attrgetter(self.env.app, obj, name, *defargs) @classmethod - def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any, - ) -> bool: + def can_document_member( + cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any, + ) -> bool: """Called to see if a member can be documented by this Documenter.""" msg = 'must be implemented in subclasses' raise NotImplementedError(msg) @@ -999,8 +1000,9 @@ class ModuleDocumenter(Documenter): self.add_line(line, src[0], src[1]) @classmethod - def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any, - ) -> bool: + def can_document_member( + cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any, + ) -> bool: # don't document submodules automatically return False @@ -1292,8 +1294,9 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ member_order = 30 @classmethod - def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any, - ) -> bool: + def can_document_member( + cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any, + ) -> bool: # supports functions, builtins and bound methods exported at the module level return (inspect.isfunction(member) or inspect.isbuiltin(member) or (inspect.isroutine(member) and isinstance(parent, ModuleDocumenter))) @@ -1395,7 +1398,7 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ if len(sig.parameters) == 0: return None - def dummy(): + def dummy() -> None: pass params = list(sig.parameters.values()) @@ -1483,8 +1486,9 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: merge_members_option(self.options) @classmethod - def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any, - ) -> bool: + def can_document_member( + cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any, + ) -> bool: return isinstance(member, type) or ( isattr and (inspect.isNewType(member) or isinstance(member, TypeVar))) @@ -1913,8 +1917,9 @@ class ExceptionDocumenter(ClassDocumenter): priority = ClassDocumenter.priority + 5 @classmethod - def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any, - ) -> bool: + def can_document_member( + cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any, + ) -> bool: try: return isinstance(member, type) and issubclass(member, BaseException) except TypeError as exc: @@ -2026,8 +2031,9 @@ class DataDocumenter(GenericAliasMixin, option_spec["no-value"] = bool_option @classmethod - def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any, - ) -> bool: + def can_document_member( + cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any, + ) -> bool: return isinstance(parent, ModuleDocumenter) and isattr def update_annotations(self, parent: Any) -> None: @@ -2144,8 +2150,9 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: priority = 1 # must be more than FunctionDocumenter @classmethod - def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any, - ) -> bool: + def can_document_member( + cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any, + ) -> bool: return inspect.isroutine(member) and not isinstance(parent, ModuleDocumenter) def import_object(self, raiseerror: bool = False) -> bool: @@ -2295,7 +2302,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: if len(sig.parameters) == 1: return None - def dummy(): + def dummy() -> None: pass params = list(sig.parameters.values()) @@ -2574,8 +2581,9 @@ class AttributeDocumenter(GenericAliasMixin, SlotsMixin, # type: ignore[misc] return inspect.isfunction(obj) or inspect.isbuiltin(obj) or inspect.ismethod(obj) @classmethod - def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any, - ) -> bool: + def can_document_member( + cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any, + ) -> bool: if isinstance(parent, ModuleDocumenter): return False if inspect.isattributedescriptor(member): @@ -2722,8 +2730,9 @@ class PropertyDocumenter(DocstringStripSignatureMixin, # type: ignore[misc] priority = AttributeDocumenter.priority + 1 @classmethod - def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any, - ) -> bool: + def can_document_member( + cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any, + ) -> bool: if isinstance(parent, ClassDocumenter): if inspect.isproperty(member): return True @@ -2797,7 +2806,7 @@ class PropertyDocumenter(DocstringStripSignatureMixin, # type: ignore[misc] except ValueError: pass - def _get_property_getter(self): + def _get_property_getter(self) -> Callable | None: if safe_getattr(self.object, 'fget', None): # property return self.object.fget if safe_getattr(self.object, 'func', None): # cached_property diff --git a/sphinx/ext/autodoc/preserve_defaults.py b/sphinx/ext/autodoc/preserve_defaults.py index 5305c6ac9..13fda26b5 100644 --- a/sphinx/ext/autodoc/preserve_defaults.py +++ b/sphinx/ext/autodoc/preserve_defaults.py @@ -96,7 +96,7 @@ def _get_arguments(obj: Any, /) -> ast.arguments | None: return _get_arguments_inner(subject) -def _is_lambda(x, /): +def _is_lambda(x: Any, /) -> bool: return isinstance(x, types.LambdaType) and x.__name__ == _LAMBDA_NAME diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py index 93052e79a..6841e676f 100644 --- a/sphinx/ext/napoleon/docstring.py +++ b/sphinx/ext/napoleon/docstring.py @@ -14,6 +14,8 @@ from sphinx.util import logging from sphinx.util.typing import get_type_hints, stringify_annotation if TYPE_CHECKING: + from collections.abc import Iterator + from sphinx.application import Sphinx from sphinx.config import Config as SphinxConfig @@ -888,7 +890,7 @@ def _recombine_set_tokens(tokens: list[str]) -> list[str]: token_queue = collections.deque(tokens) keywords = ("optional", "default") - def takewhile_set(tokens): + def takewhile_set(tokens: collections.deque[str]) -> Iterator[str]: open_braces = 0 previous_token = None while True: @@ -924,7 +926,7 @@ def _recombine_set_tokens(tokens: list[str]) -> list[str]: if open_braces == 0: break - def combine_set(tokens): + def combine_set(tokens: collections.deque[str]) -> Iterator[str]: while True: try: token = tokens.popleft() @@ -941,7 +943,7 @@ def _recombine_set_tokens(tokens: list[str]) -> list[str]: def _tokenize_type_spec(spec: str) -> list[str]: - def postprocess(item): + def postprocess(item: str) -> list[str]: if _default_regex.match(item): default = item[:7] # can't be separated by anything other than a single space @@ -962,7 +964,7 @@ def _tokenize_type_spec(spec: str) -> list[str]: def _token_type(token: str, location: str | None = None) -> str: - def is_numeric(token): + def is_numeric(token: str) -> bool: try: # use complex to make sure every numeric value is detected as literal complex(token) @@ -1026,7 +1028,7 @@ def _convert_numpy_type_spec( if translations is None: translations = {} - def convert_obj(obj, translations, default_translation): + def convert_obj(obj: str, translations: dict[str, str], default_translation: str) -> str: translation = translations.get(obj, obj) # use :class: (the default) only if obj is not a standard singleton @@ -1269,7 +1271,7 @@ class NumpyDocstring(GoogleDocstring): func_name1, func_name2, :meth:`func_name`, func_name3 """ - items = [] + items: list[tuple[str, list[str], str | None]] = [] def parse_item_name(text: str) -> tuple[str, str | None]: """Match ':role:`name`' or 'name'""" @@ -1289,7 +1291,9 @@ class NumpyDocstring(GoogleDocstring): items.append((name, rest.copy(), role)) rest.clear() - def translate(func, description, role): + def translate( + func: str, description: list[str], role: str | None, + ) -> tuple[str, list[str], str | None]: translations = self._config.napoleon_type_aliases if role is not None or not translations: return func, description, role diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py index fc2520457..24e1e7e20 100644 --- a/sphinx/locale/__init__.py +++ b/sphinx/locale/__init__.py @@ -71,22 +71,22 @@ class _TranslationProxy: def __rmul__(self, other: Any) -> str: return other * self.__str__() - def __hash__(self): + def __hash__(self) -> int: return hash(self.__str__()) - def __eq__(self, other): + def __eq__(self, other: object) -> bool: return self.__str__() == other - def __lt__(self, string): + def __lt__(self, string: str) -> bool: return self.__str__() < string - def __contains__(self, char): + def __contains__(self, char: str) -> bool: return char in self.__str__() - def __len__(self): + def __len__(self) -> int: return len(self.__str__()) - def __getitem__(self, index): + def __getitem__(self, index: int | slice) -> str: return self.__str__()[index] diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py index 55835ecec..f5d3c56b2 100644 --- a/sphinx/pycode/__init__.py +++ b/sphinx/pycode/__init__.py @@ -69,12 +69,13 @@ class ModuleAnalyzer: return filename, None @classmethod - def for_string(cls, string: str, modname: str, srcname: str = '', - ) -> ModuleAnalyzer: + def for_string( + cls: type[ModuleAnalyzer], string: str, modname: str, srcname: str = '', + ) -> ModuleAnalyzer: return cls(string, modname, srcname) @classmethod - def for_file(cls, filename: str, modname: str) -> ModuleAnalyzer: + def for_file(cls: type[ModuleAnalyzer], filename: str, modname: str) -> ModuleAnalyzer: if ('file', filename) in cls.cache: return cls.cache['file', filename] try: @@ -87,7 +88,7 @@ class ModuleAnalyzer: return obj @classmethod - def for_module(cls, modname: str) -> ModuleAnalyzer: + def for_module(cls: type[ModuleAnalyzer], modname: str) -> ModuleAnalyzer: if ('module', modname) in cls.cache: entry = cls.cache['module', modname] if isinstance(entry, PycodeError): diff --git a/sphinx/pycode/ast.py b/sphinx/pycode/ast.py index c818fb17b..5f20e5cf6 100644 --- a/sphinx/pycode/ast.py +++ b/sphinx/pycode/ast.py @@ -3,7 +3,7 @@ from __future__ import annotations import ast -from typing import overload +from typing import NoReturn, overload OPERATORS: dict[type[ast.AST], str] = { ast.Add: "+", @@ -183,5 +183,5 @@ class _UnparseVisitor(ast.NodeVisitor): else: return "(" + ", ".join(self.visit(e) for e in node.elts) + ")" - def generic_visit(self, node): + def generic_visit(self, node: ast.AST) -> NoReturn: raise NotImplementedError('Unable to parse %s object' % type(node).__name__) diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index f4f7279d5..ddb2dd798 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -256,7 +256,7 @@ def isurl(url: str) -> bool: return bool(url) and '://' in url -def _xml_name_checker(): +def _xml_name_checker() -> re.Pattern[str]: # to prevent import cycles from sphinx.builders.epub3 import _XML_NAME_PATTERN @@ -285,7 +285,7 @@ _DEPRECATED_OBJECTS = { } -def __getattr__(name): +def __getattr__(name: str) -> Any: if name not in _DEPRECATED_OBJECTS: msg = f'module {__name__!r} has no attribute {name!r}' raise AttributeError(msg) diff --git a/sphinx/util/_pathlib.py b/sphinx/util/_pathlib.py index 59980e961..42cdfc574 100644 --- a/sphinx/util/_pathlib.py +++ b/sphinx/util/_pathlib.py @@ -5,6 +5,7 @@ from __future__ import annotations import sys import warnings from pathlib import Path, PosixPath, PurePath, WindowsPath +from typing import Any from sphinx.deprecation import RemovedInSphinx80Warning @@ -21,34 +22,34 @@ _MSG = ( if sys.platform == 'win32': class _StrPath(WindowsPath): - def replace(self, old, new, count=-1, /): + def replace(self, old: str, new: str, count: int = -1, /) -> str: # type: ignore[override] # replace exists in both Path and str; # in Path it makes filesystem changes, so we use the safer str version warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2) return self.__str__().replace(old, new, count) - def __getattr__(self, item): + def __getattr__(self, item: str) -> Any: if item in _STR_METHODS: warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2) return getattr(self.__str__(), item) msg = f'{_PATH_NAME!r} has no attribute {item!r}' raise AttributeError(msg) - def __add__(self, other): + def __add__(self, other: str) -> str: warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2) return self.__str__() + other - def __bool__(self): + def __bool__(self) -> bool: if not self.__str__(): warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2) return False return True - def __contains__(self, item): + def __contains__(self, item: str) -> bool: warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2) return item in self.__str__() - def __eq__(self, other): + def __eq__(self, other: object) -> bool: if isinstance(other, PurePath): return super().__eq__(other) if isinstance(other, str): @@ -56,46 +57,46 @@ if sys.platform == 'win32': return self.__str__() == other return NotImplemented - def __hash__(self): + def __hash__(self) -> int: return super().__hash__() - def __getitem__(self, item): + def __getitem__(self, item: int | slice) -> str: warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2) return self.__str__()[item] - def __len__(self): + def __len__(self) -> int: warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2) return len(self.__str__()) else: class _StrPath(PosixPath): - def replace(self, old, new, count=-1, /): + def replace(self, old: str, new: str, count: int = -1, /) -> str: # type: ignore[override] # replace exists in both Path and str; # in Path it makes filesystem changes, so we use the safer str version warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2) return self.__str__().replace(old, new, count) - def __getattr__(self, item): + def __getattr__(self, item: str) -> Any: if item in _STR_METHODS: warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2) return getattr(self.__str__(), item) msg = f'{_PATH_NAME!r} has no attribute {item!r}' raise AttributeError(msg) - def __add__(self, other): + def __add__(self, other: str) -> str: warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2) return self.__str__() + other - def __bool__(self): + def __bool__(self) -> bool: if not self.__str__(): warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2) return False return True - def __contains__(self, item): + def __contains__(self, item: str) -> bool: warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2) return item in self.__str__() - def __eq__(self, other): + def __eq__(self, other: PurePath | str) -> bool: if isinstance(other, PurePath): return super().__eq__(other) if isinstance(other, str): @@ -103,13 +104,13 @@ else: return self.__str__() == other return NotImplemented - def __hash__(self): + def __hash__(self) -> int: return super().__hash__() - def __getitem__(self, item): + def __getitem__(self, item: int | slice) -> str: warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2) return self.__str__()[item] - def __len__(self): + def __len__(self) -> int: warnings.warn(_MSG, RemovedInSphinx80Warning, stacklevel=2) return len(self.__str__()) diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index a86241740..01c0308da 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -44,7 +44,7 @@ _DEPRECATED_OBJECTS = { } -def __getattr__(name): +def __getattr__(name: str) -> Any: if name not in _DEPRECATED_OBJECTS: msg = f'module {__name__!r} has no attribute {name!r}' raise AttributeError(msg) @@ -199,15 +199,15 @@ def using_user_docutils_conf(confdir: str | None) -> Generator[None, None, None] @contextmanager def du19_footnotes() -> Generator[None, None, None]: - def visit_footnote(self, node): + def visit_footnote(self: HTMLTranslator, node: Element) -> None: label_style = self.settings.footnote_references - if not isinstance(node.previous_sibling(), type(node)): + if not isinstance(node.previous_sibling(), type(node)): # type: ignore[attr-defined] self.body.append(f'\n') if not isinstance(node.next_node(descend=False, siblings=True), type(node)): @@ -354,7 +354,7 @@ class WarningStream: class LoggingReporter(Reporter): @classmethod - def from_reporter(cls, reporter: Reporter) -> LoggingReporter: + def from_reporter(cls: type[LoggingReporter], reporter: Reporter) -> LoggingReporter: """Create an instance of LoggingReporter from other reporter object.""" return cls(reporter.source, reporter.report_level, reporter.halt_level, reporter.debug_flag, reporter.error_handler) diff --git a/sphinx/util/inventory.py b/sphinx/util/inventory.py index 89f0070b8..2b466b79d 100644 --- a/sphinx/util/inventory.py +++ b/sphinx/util/inventory.py @@ -77,7 +77,7 @@ class InventoryFileReader: class InventoryFile: @classmethod - def load(cls, stream: IO, uri: str, joinfunc: Callable) -> Inventory: + def load(cls: type[InventoryFile], stream: IO, uri: str, joinfunc: Callable) -> Inventory: reader = InventoryFileReader(stream) line = reader.readline().rstrip() if line == '# Sphinx inventory version 1': @@ -88,7 +88,9 @@ class InventoryFile: raise ValueError('invalid inventory header: %s' % line) @classmethod - def load_v1(cls, stream: InventoryFileReader, uri: str, join: Callable) -> Inventory: + def load_v1( + cls: type[InventoryFile], stream: InventoryFileReader, uri: str, join: Callable, + ) -> Inventory: invdata: Inventory = {} projname = stream.readline().rstrip()[11:] version = stream.readline().rstrip()[11:] @@ -106,7 +108,9 @@ class InventoryFile: return invdata @classmethod - def load_v2(cls, stream: InventoryFileReader, uri: str, join: Callable) -> Inventory: + def load_v2( + cls: type[InventoryFile], stream: InventoryFileReader, uri: str, join: Callable, + ) -> Inventory: invdata: Inventory = {} projname = stream.readline().rstrip()[11:] version = stream.readline().rstrip()[11:] @@ -140,7 +144,9 @@ class InventoryFile: return invdata @classmethod - def dump(cls, filename: str, env: BuildEnvironment, builder: Builder) -> None: + def dump( + cls: type[InventoryFile], filename: str, env: BuildEnvironment, builder: Builder, + ) -> None: def escape(string: str) -> str: return re.sub("\\s+", " ", string) diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py index c6adbe40f..84aee4359 100644 --- a/sphinx/util/osutil.py +++ b/sphinx/util/osutil.py @@ -17,6 +17,7 @@ from sphinx.deprecation import _deprecation_warning if TYPE_CHECKING: from collections.abc import Iterator + from types import TracebackType # SEP separates path elements in the canonical file names # @@ -131,15 +132,21 @@ abspath = path.abspath class _chdir: """Remove this fall-back once support for Python 3.10 is removed.""" - def __init__(self, target_dir: str, /): + def __init__(self, target_dir: str, /) -> None: self.path = target_dir self._dirs: list[str] = [] - def __enter__(self): + def __enter__(self) -> None: self._dirs.append(os.getcwd()) os.chdir(self.path) - def __exit__(self, _exc_type, _exc_value, _traceback, /): + def __exit__( + self, + type: type[BaseException] | None, + value: BaseException | None, + traceback: TracebackType | None, + /, + ) -> None: os.chdir(self._dirs.pop()) diff --git a/sphinx/util/template.py b/sphinx/util/template.py index a16a7a127..e379a51cc 100644 --- a/sphinx/util/template.py +++ b/sphinx/util/template.py @@ -47,7 +47,9 @@ class FileRenderer(BaseRenderer): super().__init__(loader) @classmethod - def render_from_file(cls, filename: str, context: dict[str, Any]) -> str: + def render_from_file( + cls: type[FileRenderer], filename: str, context: dict[str, Any], + ) -> str: dirname = os.path.dirname(filename) basename = os.path.basename(filename) return cls(dirname).render(basename, context) @@ -60,7 +62,9 @@ class SphinxRenderer(FileRenderer): super().__init__(template_path) @classmethod - def render_from_file(cls, filename: str, context: dict[str, Any]) -> str: + def render_from_file( + cls: type[FileRenderer], filename: str, context: dict[str, Any], + ) -> str: return FileRenderer.render_from_file(filename, context) diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index 1f5b5d864..67e7fc1d3 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -389,7 +389,7 @@ def stringify_annotation( elif qualname == 'Literal': from sphinx.util.inspect import isenumattribute # lazy loading - def format_literal_arg(arg): + def format_literal_arg(arg: Any) -> str: if isenumattribute(arg): enumcls = arg.__class__ @@ -429,7 +429,7 @@ _DEPRECATED_OBJECTS = { } -def __getattr__(name): +def __getattr__(name: str) -> Any: if name not in _DEPRECATED_OBJECTS: msg = f'module {__name__!r} has no attribute {name!r}' raise AttributeError(msg)