diff --git a/.ruff.toml b/.ruff.toml index ce1949862..71f1a6c4a 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -347,9 +347,9 @@ select = [ ] # these tests need old ``typing`` generic aliases -"tests/roots/test-ext-autodoc/target/genericalias.py" = ["UP006", "UP007", "UP035"] -"tests/test_util/test_util_typing.py" = ["RUF036", "UP006", "UP007", "UP035"] -"tests/test_util/typing_test_data.py" = ["FA100", "I002", "PYI030", "UP006", "UP007", "UP035"] +"tests/roots/test-ext-autodoc/target/genericalias.py" = ["UP006", "UP007", "UP035", "UP045"] +"tests/test_util/test_util_typing.py" = ["RUF036", "UP006", "UP007", "UP035", "UP045"] +"tests/test_util/typing_test_data.py" = ["FA100", "I002", "PYI030", "UP006", "UP007", "UP035", "UP045"] "utils/*" = [ "T201", # whitelist ``print`` for stdout messages diff --git a/pyproject.toml b/pyproject.toml index 131a0a083..2ae3a8d60 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,7 +81,7 @@ docs = [ "sphinxcontrib-websupport", ] lint = [ - "ruff==0.8.6", + "ruff==0.9.0", "mypy==1.14.1", "sphinx-lint>=0.9", "types-colorama==0.4.15.20240311", diff --git a/sphinx/domains/c/__init__.py b/sphinx/domains/c/__init__.py index b122da164..a27357053 100644 --- a/sphinx/domains/c/__init__.py +++ b/sphinx/domains/c/__init__.py @@ -865,7 +865,7 @@ class CDomain(Domain): 'Unparseable C cross-reference: %r\n%s', target, e, location=node ) return None, None - parent_key: LookupKey = node.get('c:parent_key', None) + parent_key: LookupKey | None = node.get('c:parent_key', None) root_symbol = self.data['root_symbol'] if parent_key: parent_symbol: Symbol = root_symbol.direct_lookup(parent_key) diff --git a/sphinx/domains/c/_symbol.py b/sphinx/domains/c/_symbol.py index d4662b07f..2cccd4148 100644 --- a/sphinx/domains/c/_symbol.py +++ b/sphinx/domains/c/_symbol.py @@ -31,18 +31,35 @@ class _DuplicateSymbolError(Exception): class SymbolLookupResult: + __slots__ = 'symbols', 'parent_symbol', 'ident' + + symbols: Iterable[Symbol] + parent_symbol: Symbol + ident_or_op: ASTIdentifier + def __init__( - self, symbols: Sequence[Symbol], parentSymbol: Symbol, ident: ASTIdentifier + self, symbols: Iterable[Symbol], parent_symbol: Symbol, ident: ASTIdentifier ) -> None: self.symbols = symbols - self.parentSymbol = parentSymbol + self.parent_symbol = parent_symbol self.ident = ident + @property + def parentSymbol(self) -> Symbol: + return self.parent_symbol + class LookupKey: - def __init__(self, data: list[tuple[ASTIdentifier, str]]) -> None: + __slots__ = ('data',) + + data: Sequence[tuple[ASTIdentifier, str]] + + def __init__(self, data: Sequence[tuple[ASTIdentifier, str]], /) -> None: self.data = data + def __repr__(self) -> str: + return f'LookupKey({self.data!r})' + def __str__(self) -> str: inner = ', '.join(f'({ident}, {id_})' for ident, id_ in self.data) return f'[{inner}]' @@ -226,14 +243,11 @@ class Symbol: while s.parent: symbols.append(s) s = s.parent - symbols.reverse() - key = [] - for s in symbols: - if s.declaration is not None: - # TODO: do we need the ID? - key.append((s.ident, s.declaration.get_newest_id())) - else: - key.append((s.ident, None)) + key = [ + # TODO: do we need the ID? + (s.ident, None if s.declaration is None else s.declaration.get_newest_id()) + for s in reversed(symbols) + ] return LookupKey(key) def get_full_nested_name(self) -> ASTNestedName: @@ -382,7 +396,7 @@ class Symbol: Symbol.debug_print(f'location: {docname}:{line}') Symbol.debug_indent -= 1 symbol = Symbol( - parent=lookup_result.parentSymbol, + parent=lookup_result.parent_symbol, ident=lookup_result.ident, declaration=declaration, docname=docname, @@ -433,7 +447,7 @@ class Symbol: if Symbol.debug_lookup: Symbol.debug_print('begin: creating candidate symbol') symbol = Symbol( - parent=lookup_result.parentSymbol, + parent=lookup_result.parent_symbol, ident=lookup_result.ident, declaration=declaration, docname=docname, diff --git a/sphinx/domains/cpp/__init__.py b/sphinx/domains/cpp/__init__.py index 08c596b03..1048a4bc0 100644 --- a/sphinx/domains/cpp/__init__.py +++ b/sphinx/domains/cpp/__init__.py @@ -628,6 +628,7 @@ class AliasNode(nodes.Element): super().__init__() self.sig = sig self.aliasOptions = aliasOptions + self.parentKey: LookupKey if env is not None: if env.current_document.cpp_parent_symbol is None: root = env.domaindata['cpp']['root_symbol'] @@ -1092,7 +1093,7 @@ class CPPDomain(Domain): 'Unparseable C++ cross-reference: %r\n%s', target, ex, location=node ) return None, None - parent_key: LookupKey = node.get('cpp:parent_key', None) + parent_key: LookupKey | None = node.get('cpp:parent_key', None) root_symbol = self.data['root_symbol'] if parent_key: parent_symbol: Symbol = root_symbol.direct_lookup(parent_key) @@ -1288,7 +1289,7 @@ class CPPDomain(Domain): target = node.get('reftarget', None) if target is None: return None - parent_key: LookupKey = node.get('cpp:parent_key', None) + parent_key: LookupKey | None = node.get('cpp:parent_key', None) if parent_key is None or len(parent_key.data) <= 0: return None diff --git a/sphinx/domains/cpp/_symbol.py b/sphinx/domains/cpp/_symbol.py index 87505ca67..718467420 100644 --- a/sphinx/domains/cpp/_symbol.py +++ b/sphinx/domains/cpp/_symbol.py @@ -17,7 +17,7 @@ from sphinx.locale import __ from sphinx.util import logging if TYPE_CHECKING: - from collections.abc import Callable, Iterable, Iterator + from collections.abc import Callable, Iterable, Iterator, Sequence from sphinx.environment import BuildEnvironment @@ -36,32 +36,82 @@ class _DuplicateSymbolError(Exception): class SymbolLookupResult: + __slots__ = ( + 'symbols', + 'parent_symbol', + 'ident_or_op', + 'template_params', + 'template_args', + ) + + symbols: Iterable[Symbol] + parent_symbol: Symbol + ident_or_op: ASTIdentifier | ASTOperator + template_params: Any + template_args: ASTTemplateArgs + def __init__( self, - symbols: Iterator[Symbol], - parentSymbol: Symbol, - identOrOp: ASTIdentifier | ASTOperator, - templateParams: Any, - templateArgs: ASTTemplateArgs, + symbols: Iterable[Symbol], + parent_symbol: Symbol, + ident_or_op: ASTIdentifier | ASTOperator, + template_params: Any, + template_args: ASTTemplateArgs, ) -> None: self.symbols = symbols - self.parentSymbol = parentSymbol - self.identOrOp = identOrOp - self.templateParams = templateParams - self.templateArgs = templateArgs + self.parent_symbol = parent_symbol + self.ident_or_op = ident_or_op + self.template_params = template_params + self.template_args = template_args + + @property + def parentSymbol(self) -> Symbol: + return self.parent_symbol + + @property + def identOrOp(self) -> ASTIdentifier | ASTOperator: + return self.ident_or_op + + @property + def templateParams(self) -> Any: + return self.template_params + + @property + def templateArgs(self) -> ASTTemplateArgs: + return self.template_args class LookupKey: + __slots__ = ('data',) + + data: Sequence[ + tuple[ + ASTNestedNameElement, + ASTTemplateParams | ASTTemplateIntroduction, + str | None, + ] + ] + def __init__( self, - data: list[ + data: Sequence[ tuple[ - ASTNestedNameElement, ASTTemplateParams | ASTTemplateIntroduction, str + ASTNestedNameElement, + ASTTemplateParams | ASTTemplateIntroduction, + str | None, ] ], + /, ) -> None: self.data = data + def __repr__(self) -> str: + return f'LookupKey({self.data!r})' + + def __str__(self) -> str: + inner = ', '.join(f'({ident}, {id_})' for ident, _, id_ in self.data) + return f'[{inner}]' + def _is_specialization( template_params: ASTTemplateParams | ASTTemplateIntroduction, @@ -279,14 +329,14 @@ class Symbol: while s.parent: symbols.append(s) s = s.parent - symbols.reverse() - key = [] - for s in symbols: - nne = ASTNestedNameElement(s.identOrOp, s.templateArgs) - if s.declaration is not None: - key.append((nne, s.templateParams, s.declaration.get_newest_id())) - else: - key.append((nne, s.templateParams, None)) + key = [ + ( + ASTNestedNameElement(s.identOrOp, s.templateArgs), + s.templateParams, + None if s.declaration is None else s.declaration.get_newest_id(), + ) + for s in reversed(symbols) + ] return LookupKey(key) def get_full_nested_name(self) -> ASTNestedName: @@ -438,7 +488,7 @@ class Symbol: recurse_in_anon: bool, correct_primary_template_args: bool, search_in_siblings: bool, - ) -> SymbolLookupResult: + ) -> SymbolLookupResult | None: # ancestor_lookup_type: if not None, specifies the target type of the lookup if Symbol.debug_lookup: Symbol.debug_indent += 1 @@ -650,17 +700,17 @@ class Symbol: if Symbol.debug_lookup: Symbol.debug_print('_add_symbols, result, no symbol:') Symbol.debug_indent += 1 - Symbol.debug_print('template_params:', lookup_result.templateParams) - Symbol.debug_print('ident_or_op: ', lookup_result.identOrOp) - Symbol.debug_print('template_args: ', lookup_result.templateArgs) + Symbol.debug_print('template_params:', lookup_result.template_params) + Symbol.debug_print('ident_or_op: ', lookup_result.ident_or_op) + Symbol.debug_print('template_args: ', lookup_result.template_args) Symbol.debug_print('declaration: ', declaration) Symbol.debug_print(f'location: {docname}:{line}') Symbol.debug_indent -= 1 symbol = Symbol( - parent=lookup_result.parentSymbol, - identOrOp=lookup_result.identOrOp, - templateParams=lookup_result.templateParams, - templateArgs=lookup_result.templateArgs, + parent=lookup_result.parent_symbol, + identOrOp=lookup_result.ident_or_op, + templateParams=lookup_result.template_params, + templateArgs=lookup_result.template_args, declaration=declaration, docname=docname, line=line, @@ -709,10 +759,10 @@ class Symbol: if Symbol.debug_lookup: Symbol.debug_print('begin: creating candidate symbol') symbol = Symbol( - parent=lookup_result.parentSymbol, - identOrOp=lookup_result.identOrOp, - templateParams=lookup_result.templateParams, - templateArgs=lookup_result.templateArgs, + parent=lookup_result.parent_symbol, + identOrOp=lookup_result.ident_or_op, + templateParams=lookup_result.template_params, + templateArgs=lookup_result.template_args, declaration=declaration, docname=docname, line=line, @@ -1117,13 +1167,13 @@ class Symbol: Symbol.debug_indent -= 2 return res, None - if lookup_result.parentSymbol.declaration is not None: - if lookup_result.parentSymbol.declaration.objectType == 'templateParam': + if lookup_result.parent_symbol.declaration is not None: + if lookup_result.parent_symbol.declaration.objectType == 'templateParam': return None, 'templateParamInQualified' # try without template params and args - symbol = lookup_result.parentSymbol._find_first_named_symbol( - lookup_result.identOrOp, + symbol = lookup_result.parent_symbol._find_first_named_symbol( + lookup_result.ident_or_op, None, None, template_shorthand=templateShorthand, @@ -1186,10 +1236,10 @@ class Symbol: return None query_symbol = Symbol( - parent=lookup_result.parentSymbol, - identOrOp=lookup_result.identOrOp, - templateParams=lookup_result.templateParams, - templateArgs=lookup_result.templateArgs, + parent=lookup_result.parent_symbol, + identOrOp=lookup_result.ident_or_op, + templateParams=lookup_result.template_params, + templateArgs=lookup_result.template_args, declaration=declaration, docname='fakeDocnameForQuery', line=42, diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 684ccec90..379f68910 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -304,6 +304,14 @@ class ObjectMember: represent each member of the object. """ + __slots__ = '__name__', 'object', 'docstring', 'class_', 'skipped' + + __name__: str + object: Any + docstring: str | None + class_: Any + skipped: bool + def __init__( self, name: str, @@ -316,8 +324,19 @@ class ObjectMember: self.__name__ = name self.object = obj self.docstring = docstring - self.skipped = skipped self.class_ = class_ + self.skipped = skipped + + def __repr__(self) -> str: + return ( + f'ObjectMember(' + f'name={self.__name__!r}, ' + f'obj={self.object!r}, ' + f'docstring={self.docstring!r}, ' + f'class_={self.class_!r}, ' + f'skipped={self.skipped!r}' + f')' + ) class Documenter: diff --git a/tests/roots/test-ext-autodoc/target/autodoc_type_aliases.py b/tests/roots/test-ext-autodoc/target/autodoc_type_aliases.py index 4ba377067..3a1500207 100644 --- a/tests/roots/test-ext-autodoc/target/autodoc_type_aliases.py +++ b/tests/roots/test-ext-autodoc/target/autodoc_type_aliases.py @@ -12,7 +12,7 @@ variable: myint variable2 = None # type: myint #: docstring -variable3: Optional[myint] # NoQA: UP007 +variable3: Optional[myint] # NoQA: UP045 def read(r: io.BytesIO) -> io.StringIO: diff --git a/tests/test_events.py b/tests/test_events.py index 349b3dde5..d60129891 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -2,6 +2,8 @@ from __future__ import annotations +from types import SimpleNamespace + import pytest from sphinx.errors import ExtensionError @@ -24,16 +26,11 @@ def test_event_priority(): assert result == [3, 1, 2, 5, 4] -class FakeApp: - def __init__(self, pdb: bool = False): - self.pdb = pdb - - def test_event_allowed_exceptions(): def raise_error(app): raise RuntimeError - app = FakeApp() # pass a dummy object as an app + app = SimpleNamespace(pdb=False) # pass a dummy object as an app events = EventManager(app) # type: ignore[arg-type] events.connect('builder-inited', raise_error, priority=500) @@ -50,7 +47,7 @@ def test_event_pdb(): def raise_error(app): raise RuntimeError - app = FakeApp(pdb=True) # pass a dummy object as an app + app = SimpleNamespace(pdb=True) # pass a dummy object as an app events = EventManager(app) # type: ignore[arg-type] events.connect('builder-inited', raise_error, priority=500) diff --git a/tests/test_util/test_util_inspect.py b/tests/test_util/test_util_inspect.py index 68e40ec78..6cea49ba3 100644 --- a/tests/test_util/test_util_inspect.py +++ b/tests/test_util/test_util_inspect.py @@ -96,7 +96,7 @@ def test_TypeAliasForwardRef(): sig_str = stringify_annotation(alias, 'fully-qualified-except-typing') assert sig_str == "TypeAliasForwardRef('example')" - alias = Optional[alias] # NoQA: UP007 + alias = Optional[alias] # NoQA: UP045 sig_str = stringify_annotation(alias, 'fully-qualified-except-typing') assert sig_str == "TypeAliasForwardRef('example') | None"