diff --git a/.ruff.toml b/.ruff.toml index 256928e8a3..43f94d2a6f 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -94,6 +94,7 @@ ignore = [ "PLR5501", # Use `elif` instead of `else` then `if`, to reduce indentation "PLR6104", # Use `{operator}` to perform an augmented assignment directly "PLR6301", # Method `{method_name}` could be a function, class method, or static method + "PLW0717", # Try clause contains too many statements ({} > 5) "PLW2901", # Outer {outer_kind} variable `{name}` overwritten by inner {inner_kind} target # flake8-use-pathlib "PTH100", # `os.path.abspath()` should be replaced by `Path.resolve()` @@ -140,6 +141,10 @@ ignore = [ "RUF027", # Possible f-string without an `f` prefix "RUF039", # First argument to {call} is not raw string "RUF052", # Local dummy variable `{}` is accessed + "RUF067", # `__init__` module should only contain docstrings and re-exports + "RUF069", # Unreliable floating point equality comparison `{}` + "RUF105", # `noqa` comment used instead of `{}` + "RUF201", # Rule code used instead of name in `{}` ] external = [ # Whitelist for RUF100 unknown code warnings "SIM113", @@ -371,6 +376,13 @@ logger-objects = [ "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"] +# these tests need old-style type comments +"tests/roots/test-ext-autodoc/target/autodoc_type_aliases.py" = ["PYI033"] +"tests/roots/test-ext-autodoc/target/cached_property.py" = ["PYI033"] +"tests/roots/test-ext-autodoc/target/properties.py" = ["PYI033"] +"tests/roots/test-ext-autodoc/target/typed_vars.py" = ["PYI033"] +"tests/roots/test-ext-autodoc/target/typehints.py" = ["PYI033"] + "utils/*" = [ "T201", # whitelist ``print`` for stdout messages "ANN", # utilities don't need annotations diff --git a/pyproject.toml b/pyproject.toml index 5da3d429c5..87701f01f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -112,7 +112,7 @@ docs = [ "sphinxcontrib-websupport", ] lint = [ - "ruff==0.14.9", + "ruff==0.17.1", "sphinx-lint>=0.9", ] package = [ diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 6626c3ece0..210ca1c507 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -34,7 +34,7 @@ if _in_development: try: if ret := subprocess.run( - ('git', 'rev-parse', '--short', 'HEAD'), + ('git', 'rev-parse', '--short', 'HEAD'), # NoQA: S607 capture_output=True, check=False, cwd=package_dir, diff --git a/sphinx/domains/c/_parser.py b/sphinx/domains/c/_parser.py index c59352b6ee..17800e55f9 100644 --- a/sphinx/domains/c/_parser.py +++ b/sphinx/domains/c/_parser.py @@ -508,7 +508,7 @@ class DefinitionParser(BaseParser): rooted = False if self.skip_string('.'): rooted = True - while 1: + while True: self.skip_ws() if not self.match(identifier_re): self.fail('Expected identifier in nested name.') @@ -586,7 +586,7 @@ class DefinitionParser(BaseParser): args = [] self.skip_ws() if not self.skip_string(')'): - while 1: + while True: self.skip_ws() if self.skip_string('...'): args.append(ASTFunctionParameter(None, True)) @@ -623,7 +623,7 @@ class DefinitionParser(BaseParser): volatile = None const = None attrs = [] - while 1: # accept any permutation of a subset of some decl-specs + while True: # accept any permutation of a subset of some decl-specs self.skip_ws() if not storage: if outer == 'member': @@ -722,7 +722,7 @@ class DefinitionParser(BaseParser): else: decl_id = None array_ops = [] - while 1: + while True: self.skip_ws() if typed and self.skip_string('['): self.skip_ws() @@ -789,7 +789,7 @@ class DefinitionParser(BaseParser): volatile = False const = False attrs = [] - while 1: + while True: if not restrict: restrict = self.skip_word_and_ws('restrict') if restrict: @@ -972,7 +972,7 @@ class DefinitionParser(BaseParser): if self.skip_string(')'): return ASTMacro(ident, []) args = [] - while 1: + while True: self.skip_ws() if self.skip_string('...'): args.append(ASTMacroParameter(None, True)) diff --git a/sphinx/domains/cpp/_parser.py b/sphinx/domains/cpp/_parser.py index 44e282a17a..b6dad6f85d 100644 --- a/sphinx/domains/cpp/_parser.py +++ b/sphinx/domains/cpp/_parser.py @@ -895,7 +895,7 @@ class DefinitionParser(BaseParser): prev_errors = [] template_args: list[ASTType | ASTTemplateArgConstant] = [] pack_expansion = False - while 1: + while True: pos = self.pos parsed_comma = False parsed_end = False @@ -955,7 +955,7 @@ class DefinitionParser(BaseParser): rooted = False if self.skip_string('::'): rooted = True - while 1: + while True: self.skip_ws() if len(names) > 0: template = self.skip_word_and_ws('template') @@ -1158,7 +1158,7 @@ class DefinitionParser(BaseParser): args = [] self.skip_ws() if not self.skip_string(')'): - while 1: + while True: self.skip_ws() if self.skip_string('...'): args.append(ASTFunctionParameter(None, True)) @@ -1264,7 +1264,7 @@ class DefinitionParser(BaseParser): const = None friend = None attrs = [] - while 1: # accept any permutation of a subset of some decl-specs + while True: # accept any permutation of a subset of some decl-specs self.skip_ws() if not const and typed: const = self.skip_word('const') @@ -1410,7 +1410,7 @@ class DefinitionParser(BaseParser): else: decl_id = None array_ops = [] - while 1: + while True: self.skip_ws() if typed and self.skip_string('['): self.skip_ws() @@ -1450,7 +1450,7 @@ class DefinitionParser(BaseParser): volatile = False const = False attr_list = [] - while 1: + while True: if not volatile: volatile = self.skip_word_and_ws('volatile') if volatile: @@ -1536,7 +1536,7 @@ class DefinitionParser(BaseParser): else: volatile = False const = False - while 1: + while True: if not volatile: volatile = self.skip_word_and_ws('volatile') if volatile: @@ -1776,7 +1776,7 @@ class DefinitionParser(BaseParser): bases = [] self.skip_ws() if self.skip_string(':'): - while 1: + while True: self.skip_ws() visibility = None virtual = False @@ -1951,7 +1951,7 @@ class DefinitionParser(BaseParser): # for sure it must be a template introduction now params = [] - while 1: + while True: self.skip_ws() parameter_pack = self.skip_string('...') self.skip_ws() @@ -2032,7 +2032,7 @@ class DefinitionParser(BaseParser): self, object_type: str ) -> ASTTemplateDeclarationPrefix | None: templates: list[ASTTemplateParams | ASTTemplateIntroduction] = [] - while 1: + while True: self.skip_ws() # the saved position is only used to provide a better error message params: ASTTemplateParams | ASTTemplateIntroduction | None = None diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index c5cc77139f..a5cb87e8eb 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -576,7 +576,7 @@ class BuildEnvironment: @property def temp_data(self) -> _CurrentDocument: - """Returns the temporary data storage for the current document. + """The temporary data storage for the current document. Kept for backwards compatibility. """ @@ -584,12 +584,12 @@ class BuildEnvironment: @property def docname(self) -> str: - """Returns the docname of the document currently being parsed.""" + """The docname of the document currently being parsed.""" return self.current_document.docname @property def parser(self) -> Parser: - """Returns the parser being used for to parse the current document.""" + """The parser being used for to parse the current document.""" if (parser := self.current_document._parser) is not None: return parser msg = 'parser' diff --git a/sphinx/environment/collectors/metadata.py b/sphinx/environment/collectors/metadata.py index 8936341a91..f318d5ade6 100644 --- a/sphinx/environment/collectors/metadata.py +++ b/sphinx/environment/collectors/metadata.py @@ -54,7 +54,7 @@ class MetadataCollector(EnvironmentCollector): md[field_name.astext()] = field_body.astext() elif isinstance(node, nodes.TextElement): # other children must be TextElement - # see: https://docutils.sourceforge.io/docs/ref/doctree.html#bibliographic-elements # NoQA: E501 + # see: https://docutils.sourceforge.io/docs/ref/doctree.html#bibliographic-elements md[node.__class__.__name__] = node.astext() for name, value in md.items(): diff --git a/sphinx/ext/apidoc/_generate.py b/sphinx/ext/apidoc/_generate.py index 338b74d940..131f53cad3 100644 --- a/sphinx/ext/apidoc/_generate.py +++ b/sphinx/ext/apidoc/_generate.py @@ -305,7 +305,8 @@ def recurse_tree( # we are in a package with something to document if subs or len(files) > 1 or not is_skipped_package(root, opts): subpackage = ( - root.removeprefix(str(root_path)) + root + .removeprefix(str(root_path)) .lstrip(os.path.sep) .replace(os.path.sep, '.') ) diff --git a/sphinx/ext/autodoc/_dynamic/_importer.py b/sphinx/ext/autodoc/_dynamic/_importer.py index 10d885a080..276c5c22af 100644 --- a/sphinx/ext/autodoc/_dynamic/_importer.py +++ b/sphinx/ext/autodoc/_dynamic/_importer.py @@ -171,8 +171,10 @@ def _import_from_module_and_path( if obj_path: dotted_objpath = '.'.join(obj_path) err_parts = [ - f'autodoc: failed to import {dotted_objpath!r} ' - f'from module {module_name!r}' + ( + f'autodoc: failed to import {dotted_objpath!r} ' + f'from module {module_name!r}' + ) ] else: err_parts = [f'autodoc: failed to import {module_name!r}'] diff --git a/sphinx/ext/autodoc/_dynamic/_member_finder.py b/sphinx/ext/autodoc/_dynamic/_member_finder.py index 0335cac50f..9fc54467d8 100644 --- a/sphinx/ext/autodoc/_dynamic/_member_finder.py +++ b/sphinx/ext/autodoc/_dynamic/_member_finder.py @@ -4,11 +4,9 @@ import operator import re import sys from enum import Enum -from typing import TYPE_CHECKING, Literal, NewType, TypeVar +from typing import TYPE_CHECKING, NewType, TypeVar from sphinx.errors import PycodeError -from sphinx.events import EventManager -from sphinx.ext.autodoc._directive_options import _AutoDocumenterOptions from sphinx.ext.autodoc._dynamic._loader import _load_object_by_name from sphinx.ext.autodoc._dynamic._mock import ismock, undecorate from sphinx.ext.autodoc._property_types import _ClassDefProperties, _ModuleProperties diff --git a/sphinx/ext/autodoc/_dynamic/_mock.py b/sphinx/ext/autodoc/_dynamic/_mock.py index e45d03a877..99314b7a11 100644 --- a/sphinx/ext/autodoc/_dynamic/_mock.py +++ b/sphinx/ext/autodoc/_dynamic/_mock.py @@ -14,7 +14,7 @@ from sphinx.ext.autodoc._shared import LOGGER from sphinx.util.inspect import isboundmethod, safe_getattr if TYPE_CHECKING: - from collections.abc import Iterator, Sequence, Set + from collections.abc import Generator, Iterator, Sequence, Set from typing import Any from typing_extensions import TypeIs @@ -157,7 +157,7 @@ class MockFinder(MetaPathFinder): @contextlib.contextmanager -def mock(modnames: Sequence[str]) -> Iterator[None]: +def mock(modnames: Sequence[str]) -> Generator[None, None, None]: """Insert mock modules during context:: with mock(['target.module.name']): diff --git a/sphinx/ext/autodoc/_legacy_class_based/_documenters.py b/sphinx/ext/autodoc/_legacy_class_based/_documenters.py index 456c57abd4..c70acecf13 100644 --- a/sphinx/ext/autodoc/_legacy_class_based/_documenters.py +++ b/sphinx/ext/autodoc/_legacy_class_based/_documenters.py @@ -201,7 +201,7 @@ class Documenter: @property def documenters(self) -> dict[str, type[Documenter]]: - """Returns registered Documenter classes""" + """Registered Documenter classes""" return self.env._registry.documenters def add_line(self, line: str, source: str, *lineno: int) -> None: diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index a4f9ef788a..ab547b27f3 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -587,7 +587,8 @@ class IndexBuilder: # stemmer. For example, SearchChinese reuses english-stemmer.js, # which defines EnglishStemmer. stemmer_class = ( - self.lang.js_stemmer_rawcode.removesuffix('-stemmer.js') + self.lang.js_stemmer_rawcode + .removesuffix('-stemmer.js') .title() .replace('_', '') .replace('-', '') diff --git a/sphinx/transforms/i18n.py b/sphinx/transforms/i18n.py index 3be280859c..b26a9217fa 100644 --- a/sphinx/transforms/i18n.py +++ b/sphinx/transforms/i18n.py @@ -420,7 +420,8 @@ class Locale(SphinxTransform): while (catalog := catalog._fallback) is not None: # type: ignore[attr-defined] catalogues.append(getattr(catalog, '_catalog', None)) merged: dict[str, str] = {} - for catalogue in filter(None, reversed(catalogues)): # type: dict[str, str] + catalogue: dict[str, str] + for catalogue in filter(None, reversed(catalogues)): merged |= catalogue # phase1: replace reference ids with translated names diff --git a/sphinx/util/cfamily.py b/sphinx/util/cfamily.py index 6595859c62..96a4436c03 100644 --- a/sphinx/util/cfamily.py +++ b/sphinx/util/cfamily.py @@ -480,7 +480,7 @@ class BaseParser: if not self.skip_string_and_ws('('): self.fail("Expected '(' after '__attribute__('.") attrs = [] - while 1: + while True: if self.match(identifier_re): name = self.matched_text exprs = self._parse_paren_expression_list() diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index a992e925e0..12eb015b27 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -33,7 +33,7 @@ report_re = re.compile( ) if TYPE_CHECKING: - from collections.abc import Iterator, Mapping, Sequence + from collections.abc import Generator, Mapping, Sequence from types import ModuleType, TracebackType from typing import Any, Protocol @@ -95,7 +95,7 @@ additional_nodes: set[type[Element]] = set() @contextmanager -def docutils_namespace() -> Iterator[None]: +def docutils_namespace() -> Generator[None, None, None]: """Create namespace for reST parsers.""" try: _directives = copy(directives._directives) # type: ignore[attr-defined] @@ -173,7 +173,7 @@ def unregister_node(node: type[Element]) -> None: @contextmanager -def patched_get_language() -> Iterator[None]: +def patched_get_language() -> Generator[None, None, None]: """Patch docutils.languages.get_language() temporarily. This ignores the second argument ``reporter`` to suppress warnings. @@ -195,7 +195,7 @@ def patched_get_language() -> Iterator[None]: @contextmanager -def patched_rst_get_language() -> Iterator[None]: +def patched_rst_get_language() -> Generator[None, None, None]: """Patch docutils.parsers.rst.languages.get_language(). Starting from docutils 0.17, get_language() in ``rst.languages`` also has a reporter, which needs to be disabled temporarily. @@ -221,7 +221,9 @@ def patched_rst_get_language() -> Iterator[None]: @contextmanager -def using_user_docutils_conf(confdir: str | os.PathLike[str] | None) -> Iterator[None]: +def using_user_docutils_conf( + confdir: str | os.PathLike[str] | None, +) -> Generator[None, None, None]: """Let docutils know the location of ``docutils.conf`` for Sphinx.""" try: docutils_config = os.environ.get('DOCUTILSCONFIG', None) @@ -237,7 +239,9 @@ def using_user_docutils_conf(confdir: str | os.PathLike[str] | None) -> Iterator @contextmanager -def patch_docutils(confdir: str | os.PathLike[str] | None = None) -> Iterator[None]: +def patch_docutils( + confdir: str | os.PathLike[str] | None = None, +) -> Generator[None, None, None]: """Patch to docutils temporarily.""" with ( patched_get_language(), @@ -436,7 +440,9 @@ class NullReporter(Reporter): @contextmanager -def switch_source_input(state: State[list[str]], content: StringList) -> Iterator[None]: +def switch_source_input( + state: State[list[str]], content: StringList +) -> Generator[None, None, None]: """Switch current source input of state temporarily.""" try: # remember the original ``get_source_and_line()`` method diff --git a/sphinx/util/logging.py b/sphinx/util/logging.py index 9ad035c49a..a64a1416bf 100644 --- a/sphinx/util/logging.py +++ b/sphinx/util/logging.py @@ -16,7 +16,7 @@ from sphinx._cli.util.colour import colourise from sphinx.errors import SphinxWarning if TYPE_CHECKING: - from collections.abc import Iterator, Mapping, Sequence, Set + from collections.abc import Generator, Mapping, Sequence, Set from typing import IO, Any, NoReturn from docutils.nodes import Node @@ -253,7 +253,7 @@ class MemoryHandler(logging.handlers.BufferingHandler): @contextmanager -def pending_warnings() -> Iterator[logging.Handler]: +def pending_warnings() -> Generator[logging.Handler, None, None]: """Context manager to postpone logging warnings temporarily. Similar to :func:`pending_logging`. @@ -281,7 +281,7 @@ def pending_warnings() -> Iterator[logging.Handler]: @contextmanager -def suppress_logging() -> Iterator[MemoryHandler]: +def suppress_logging() -> Generator[MemoryHandler, None, None]: """Context manager to suppress logging all logs temporarily. For example:: @@ -310,7 +310,7 @@ def suppress_logging() -> Iterator[MemoryHandler]: @contextmanager -def pending_logging() -> Iterator[MemoryHandler]: +def pending_logging() -> Generator[MemoryHandler, None, None]: """Context manager to postpone logging all logs temporarily. For example:: @@ -333,7 +333,7 @@ skip_warningiserror = nullcontext # Deprecate in Sphinx 10 @contextmanager -def prefixed_warnings(prefix: str) -> Iterator[None]: +def prefixed_warnings(prefix: str) -> Generator[None, None, None]: """Context manager to prepend prefix to all warning log records temporarily. For example:: @@ -383,11 +383,12 @@ class LogCollector: self.logs: list[logging.LogRecord] = [] @contextmanager - def collect(self) -> Iterator[None]: + def collect(self) -> Generator[None, None, None]: with pending_logging() as memhandler: - yield - - self.logs = memhandler.clear() + try: + yield + finally: + self.logs = memhandler.clear() class InfoFilter(logging.Filter): diff --git a/sphinx/util/parsing.py b/sphinx/util/parsing.py index aa6c34251c..6c20810859 100644 --- a/sphinx/util/parsing.py +++ b/sphinx/util/parsing.py @@ -9,7 +9,7 @@ from docutils.nodes import Element from docutils.statemachine import StringList, string2lines if TYPE_CHECKING: - from collections.abc import Iterator + from collections.abc import Generator from docutils.nodes import Node from docutils.parsers.rst.states import RSTState @@ -73,7 +73,7 @@ def nested_parse_to_nodes( @contextlib.contextmanager -def _fresh_title_style_context(state: RSTState) -> Iterator[None]: +def _fresh_title_style_context(state: RSTState) -> Generator[None, None, None]: # hack around title style bookkeeping memo = state.memo surrounding_title_styles: list[str | tuple[str, str]] = memo.title_styles diff --git a/sphinx/util/rst.py b/sphinx/util/rst.py index 485f369766..a967e79e46 100644 --- a/sphinx/util/rst.py +++ b/sphinx/util/rst.py @@ -18,7 +18,7 @@ from sphinx.locale import __ from sphinx.util import docutils, logging if TYPE_CHECKING: - from collections.abc import Iterator + from collections.abc import Generator from docutils.statemachine import StringList from jinja2 import Environment @@ -63,7 +63,7 @@ def heading(env: Environment, text: str, level: int = 1) -> str: @contextmanager -def default_role(docname: str, name: str) -> Iterator[None]: +def default_role(docname: str, name: str) -> Generator[None, None, None]: if name: dummy_reporter = Reporter('', 4, 4) role_fn, _ = roles.role(name, english, 0, dummy_reporter) # type: ignore[arg-type] @@ -72,9 +72,10 @@ def default_role(docname: str, name: str) -> Iterator[None]: else: logger.warning(__('default role %s not found'), name, location=docname) - yield - - docutils.unregister_role('') + try: + yield + finally: + docutils.unregister_role('') def _prepend_prologue(content: StringList, prologue: str) -> None: diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 141eb8eae7..d5cac29020 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -277,7 +277,7 @@ class TableCell: @property def width(self) -> int: - """Returns the cell width.""" + """The cell width.""" width = 0 while self.table.cells[self.row, self.col + width] == self.cell_id: width += 1 @@ -285,7 +285,7 @@ class TableCell: @property def height(self) -> int: - """Returns the cell height.""" + """The cell height.""" height = 0 while self.table.cells[self.row + height, self.col] == self.cell_id: height += 1 @@ -625,7 +625,7 @@ class LaTeXTranslator(SphinxTranslator): @property def table(self) -> Table | None: - """Get current table.""" + """The current table.""" if self.tables: return self.tables[-1] else: @@ -2504,7 +2504,8 @@ class LaTeXTranslator(SphinxTranslator): # mainly, %, #, {, } and \ need escaping via a \ escape # in \href, the tilde is allowed and must be represented literally return ( - self.encode(text) + self + .encode(text) .replace(r'\textasciitilde{}', '~') .replace(r'\sphinxhyphen{}', '-') .replace(r'\textquotesingle{}', "'") diff --git a/tests/roots/test-ext-autodoc/target/enums.py b/tests/roots/test-ext-autodoc/target/enums.py index 9d6bcdbc97..bcfb86503b 100644 --- a/tests/roots/test-ext-autodoc/target/enums.py +++ b/tests/roots/test-ext-autodoc/target/enums.py @@ -1,4 +1,4 @@ -# ruff: NoQA: PIE796 +# ruff: noqa: PIE796 import enum from typing import final diff --git a/tests/test_builders/test_build_all.py b/tests/test_builders/test_build_all.py index 41434bd6c7..ed380f194f 100644 --- a/tests/test_builders/test_build_all.py +++ b/tests/test_builders/test_build_all.py @@ -12,8 +12,6 @@ from unittest import mock import pytest -from sphinx.testing.util import SphinxTestApp - if TYPE_CHECKING: from collections.abc import Callable from pathlib import Path diff --git a/tests/test_builders/test_build_epub.py b/tests/test_builders/test_build_epub.py index 28368609e4..728c0e2066 100644 --- a/tests/test_builders/test_build_epub.py +++ b/tests/test_builders/test_build_epub.py @@ -14,7 +14,6 @@ from typing import TYPE_CHECKING import pytest from sphinx.builders.epub3 import _XML_NAME_PATTERN -from sphinx.testing.util import SphinxTestApp if TYPE_CHECKING: from collections.abc import Iterator diff --git a/tests/test_builders/test_build_gettext.py b/tests/test_builders/test_build_gettext.py index 8a1827ba28..49643a941f 100644 --- a/tests/test_builders/test_build_gettext.py +++ b/tests/test_builders/test_build_gettext.py @@ -13,7 +13,6 @@ from typing import TYPE_CHECKING import pytest from sphinx.builders.gettext import Catalog, MsgOrigin -from sphinx.testing.util import SphinxTestApp if TYPE_CHECKING: from sphinx.testing.util import SphinxTestApp @@ -318,10 +317,12 @@ def test_gettext_literalblock_additional(app: SphinxTestApp) -> None: 'literal-block\\nin list', 'test_code_for_noqa()\\ncontinued()', 'doctest blocks', - '>>> import sys # sys importing\\n>>> def main(): # define main ' - "function\\n... sys.stdout.write('hello') # call write method of " - "stdout object\\n>>>\\n>>> if __name__ == '__main__': # if run this py " - 'file as python script\\n... main() # call main', + ( + '>>> import sys # sys importing\\n>>> def main(): # define main ' + "function\\n... sys.stdout.write('hello') # call write method of " + "stdout object\\n>>>\\n>>> if __name__ == '__main__': # if run this py " + 'file as python script\\n... main() # call main' + ), ] diff --git a/tests/test_builders/test_build_html.py b/tests/test_builders/test_build_html.py index 5485f01fdc..b485ce3be3 100644 --- a/tests/test_builders/test_build_html.py +++ b/tests/test_builders/test_build_html.py @@ -371,9 +371,11 @@ def test_html_raw_directive(app: SphinxTestApp) -> None: True, ), ( - ".//link[@href='_static/alternate1.css']" - "[@rel='alternate stylesheet']" - "[@title='Alternate']", + ( + ".//link[@href='_static/alternate1.css']" + "[@rel='alternate stylesheet']" + "[@title='Alternate']" + ), '', True, ), @@ -388,16 +390,20 @@ def test_html_raw_directive(app: SphinxTestApp) -> None: True, ), ( - ".//link[@href='_static/more_default.css']" - "[@rel='stylesheet']" - "[@title='Default']", + ( + ".//link[@href='_static/more_default.css']" + "[@rel='stylesheet']" + "[@title='Default']" + ), '', True, ), ( - ".//link[@href='_static/more_alternate1.css']" - "[@rel='alternate stylesheet']" - "[@title='Alternate']", + ( + ".//link[@href='_static/more_alternate1.css']" + "[@rel='alternate stylesheet']" + "[@title='Alternate']" + ), '', True, ), diff --git a/tests/test_builders/test_build_html_5_output.py b/tests/test_builders/test_build_html_5_output.py index 9ab869153e..0d3589b1a3 100644 --- a/tests/test_builders/test_build_html_5_output.py +++ b/tests/test_builders/test_build_html_5_output.py @@ -141,14 +141,18 @@ def tail_check(check: str) -> Callable[[Iterable[Element]], Literal[True]]: ('markup.html', ".//li/p/code/em/span[@class='pre']", '^i$'), ( 'markup.html', - ".//a[@href='https://peps.python.org/pep-0008/']" - "[@class='pep reference external']/strong", + ( + ".//a[@href='https://peps.python.org/pep-0008/']" + "[@class='pep reference external']/strong" + ), 'PEP 8', ), ( 'markup.html', - ".//a[@href='https://peps.python.org/pep-0008/']" - "[@class='pep reference external']/strong", + ( + ".//a[@href='https://peps.python.org/pep-0008/']" + "[@class='pep reference external']/strong" + ), 'Python Enhancement Proposal #8', ), ( @@ -163,8 +167,10 @@ def tail_check(check: str) -> Callable[[Iterable[Element]], Literal[True]]: ), ( 'markup.html', - ".//a[@href='objects.html#envvar-HOME']" - "[@class='reference internal']/code/span[@class='pre']", + ( + ".//a[@href='objects.html#envvar-HOME']" + "[@class='reference internal']/code/span[@class='pre']" + ), 'HOME', ), ( @@ -174,8 +180,10 @@ def tail_check(check: str) -> Callable[[Iterable[Element]], Literal[True]]: ), ( 'markup.html', - ".//a[@href='#grammar-token-try_stmt']" - "[@class='reference internal']/code/span", + ( + ".//a[@href='#grammar-token-try_stmt']" + "[@class='reference internal']/code/span" + ), '^statement$', ), ( @@ -195,8 +203,10 @@ def tail_check(check: str) -> Callable[[Iterable[Element]], Literal[True]]: ), ( 'markup.html', - ".//a[@href='objects.html#cmdoption-python-c']" - "[@class='reference internal']/code/span[@class='pre']", + ( + ".//a[@href='objects.html#cmdoption-python-c']" + "[@class='reference internal']/code/span[@class='pre']" + ), '-c', ), # abbreviations @@ -412,8 +422,10 @@ def tail_check(check: str) -> Callable[[Iterable[Element]], Literal[True]]: ), ( 'objects.html', - ".//a[@class='reference internal'][@href='#cmdoption-perl-arg-create-auth-token']" - '/code/span', + ( + ".//a[@class='reference internal'][@href='#cmdoption-perl-arg-create-auth-token']" + '/code/span' + ), 'create-auth-token', ), ( diff --git a/tests/test_builders/test_build_html_copyright.py b/tests/test_builders/test_build_html_copyright.py index 29c14fd12a..ef5d2d8428 100644 --- a/tests/test_builders/test_build_html_copyright.py +++ b/tests/test_builders/test_build_html_copyright.py @@ -5,8 +5,6 @@ from typing import TYPE_CHECKING import pytest -from sphinx.testing.util import SphinxTestApp - if TYPE_CHECKING: from collections.abc import Iterator diff --git a/tests/test_builders/test_build_linkcheck.py b/tests/test_builders/test_build_linkcheck.py index b54cc97268..1baf51145b 100644 --- a/tests/test_builders/test_build_linkcheck.py +++ b/tests/test_builders/test_build_linkcheck.py @@ -28,7 +28,6 @@ from sphinx.builders.linkcheck import ( compile_linkcheck_allowed_redirects, ) from sphinx.errors import ConfigError -from sphinx.testing.util import SphinxTestApp from sphinx.util import requests from sphinx.util._pathlib import _StrPath diff --git a/tests/test_builders/test_build_text.py b/tests/test_builders/test_build_text.py index bb3d18a509..b6681327cc 100644 --- a/tests/test_builders/test_build_text.py +++ b/tests/test_builders/test_build_text.py @@ -7,7 +7,6 @@ from typing import TYPE_CHECKING import pytest from docutils.utils import column_width -from sphinx.testing.util import SphinxTestApp from sphinx.writers.text import MAXWIDTH, Cell, Table if TYPE_CHECKING: diff --git a/tests/test_config/test_copyright.py b/tests/test_config/test_copyright.py index 407743045f..ccc3f5ac9e 100644 --- a/tests/test_config/test_copyright.py +++ b/tests/test_config/test_copyright.py @@ -12,7 +12,6 @@ from sphinx.config import ( correct_copyright_year, evaluate_copyright_placeholders, ) -from sphinx.testing.util import SphinxTestApp if TYPE_CHECKING: from collections.abc import Callable, Iterator diff --git a/tests/test_directives/test_directive_productionlist.py b/tests/test_directives/test_directive_productionlist.py index 7dfa3dcf3b..b127936ca1 100644 --- a/tests/test_directives/test_directive_productionlist.py +++ b/tests/test_directives/test_directive_productionlist.py @@ -7,7 +7,7 @@ from docutils import nodes from sphinx.addnodes import pending_xref from sphinx.testing import restructuredtext -from sphinx.testing.util import SphinxTestApp, assert_node, etree_parse +from sphinx.testing.util import assert_node, etree_parse TYPE_CHECKING = False if TYPE_CHECKING: diff --git a/tests/test_ext_autodoc/test_ext_autodoc.py b/tests/test_ext_autodoc/test_ext_autodoc.py index 0d2c29d13f..f7ab21b140 100644 --- a/tests/test_ext_autodoc/test_ext_autodoc.py +++ b/tests/test_ext_autodoc/test_ext_autodoc.py @@ -2190,8 +2190,10 @@ def test_autodoc_GenericAlias() -> None: '', ' A list of Class', '', - ' alias of :py:class:`~typing.List`\\ ' - '[:py:class:`~target.genericalias.Class`]', + ( + ' alias of :py:class:`~typing.List`\\ ' + '[:py:class:`~target.genericalias.Class`]' + ), '', '', '.. py:data:: T', @@ -2450,8 +2452,10 @@ def test_autodoc_Annotated() -> None: '', ' Type alias for a validated string.', '', - ' alias of :py:class:`~typing.Annotated`\\ [:py:class:`str`, ' - ':py:class:`~target.annotated.FuncValidator`\\ (func=\\ :py:class:`~target.annotated.validate`)]', + ( + ' alias of :py:class:`~typing.Annotated`\\ [:py:class:`str`, ' + ':py:class:`~target.annotated.FuncValidator`\\ (func=\\ :py:class:`~target.annotated.validate`)]' + ), '', '', ".. py:function:: hello(name: ~typing.Annotated[str, 'attribute']) -> None", diff --git a/tests/test_ext_autodoc/test_ext_autodoc_preserve_defaults.py b/tests/test_ext_autodoc/test_ext_autodoc_preserve_defaults.py index 6246d6bf2e..fdc18e2be2 100644 --- a/tests/test_ext_autodoc/test_ext_autodoc_preserve_defaults.py +++ b/tests/test_ext_autodoc/test_ext_autodoc_preserve_defaults.py @@ -130,8 +130,10 @@ def test_preserve_defaults_special_constructs() -> None: '.. py:module:: target.preserve_defaults_special_constructs', '', '', - '.. py:class:: DataClass(' - 'a: int, b: object = , c: list[int] = )', + ( + '.. py:class:: DataClass(' + 'a: int, b: object = , c: list[int] = )' + ), ' :module: target.preserve_defaults_special_constructs', '', ' docstring', @@ -143,8 +145,10 @@ def test_preserve_defaults_special_constructs() -> None: ' docstring', '', '', - '.. py:class:: MyNamedTuple1(' - 'a: int, b: object = , c: list[int] = [1, 2, 3])', + ( + '.. py:class:: MyNamedTuple1(' + 'a: int, b: object = , c: list[int] = [1, 2, 3])' + ), ' :module: target.preserve_defaults_special_constructs', '', ' docstring', diff --git a/tests/test_ext_autodoc/test_ext_autodoc_typehints.py b/tests/test_ext_autodoc/test_ext_autodoc_typehints.py index c1d7a7e401..1a0a7d9fdd 100644 --- a/tests/test_ext_autodoc/test_ext_autodoc_typehints.py +++ b/tests/test_ext_autodoc/test_ext_autodoc_typehints.py @@ -13,7 +13,7 @@ from sphinx.testing import restructuredtext from tests.test_ext_autodoc.autodoc_util import do_autodoc if TYPE_CHECKING: - from collections.abc import Iterator + from collections.abc import Generator from pathlib import Path from sphinx.testing.util import SphinxTestApp @@ -22,7 +22,7 @@ pytestmark = pytest.mark.usefixtures('inject_autodoc_root_into_sys_path') @contextmanager -def overwrite_file(path: Path, content: str) -> Iterator[None]: +def overwrite_file(path: Path, content: str) -> Generator[None, None, None]: current_content = path.read_bytes() if path.exists() else None try: path.write_text(content, encoding='utf-8') @@ -135,8 +135,10 @@ def test_autodoc_typehints_signature() -> None: " alias of TypeVar('T', bound=\\ :py:class:`~pathlib.PurePosixPath`)", '', '', - '.. py:function:: complex_func(arg1: str, arg2: List[int], arg3: Tuple[int, ' - 'Union[str, Unknown]] = None, *args: str, **kwargs: str) -> None', + ( + '.. py:function:: complex_func(arg1: str, arg2: List[int], arg3: Tuple[int, ' + 'Union[str, Unknown]] = None, *args: str, **kwargs: str) -> None' + ), ' :module: target.typehints', '', '', @@ -774,8 +776,10 @@ def test_autodoc_typehints_format_fully_qualified() -> None: " alias of TypeVar('T', bound=\\ :py:class:`pathlib.PurePosixPath`)", '', '', - '.. py:function:: complex_func(arg1: str, arg2: List[int], arg3: Tuple[int, ' - 'Union[str, Unknown]] = None, *args: str, **kwargs: str) -> None', + ( + '.. py:function:: complex_func(arg1: str, arg2: List[int], arg3: Tuple[int, ' + 'Union[str, Unknown]] = None, *args: str, **kwargs: str) -> None' + ), ' :module: target.typehints', '', '', diff --git a/tests/test_extensions/test_ext_githubpages.py b/tests/test_extensions/test_ext_githubpages.py index 9445bf7133..f353f79502 100644 --- a/tests/test_extensions/test_ext_githubpages.py +++ b/tests/test_extensions/test_ext_githubpages.py @@ -6,8 +6,6 @@ from typing import TYPE_CHECKING import pytest -from sphinx.testing.util import SphinxTestApp - if TYPE_CHECKING: from sphinx.testing.util import SphinxTestApp diff --git a/tests/test_transforms/test_transforms_post_transforms.py b/tests/test_transforms/test_transforms_post_transforms.py index 2b839af336..de0b5510f2 100644 --- a/tests/test_transforms/test_transforms_post_transforms.py +++ b/tests/test_transforms/test_transforms_post_transforms.py @@ -10,7 +10,7 @@ from docutils import nodes from sphinx import addnodes from sphinx.addnodes import SIG_ELEMENTS -from sphinx.testing.util import SphinxTestApp, assert_node +from sphinx.testing.util import assert_node from sphinx.transforms.post_transforms import SigElementFallbackTransform from sphinx.util.docutils import new_document diff --git a/tests/test_transforms/test_unreferenced_footnotes.py b/tests/test_transforms/test_unreferenced_footnotes.py index 8e51ce020d..c9810ad607 100644 --- a/tests/test_transforms/test_unreferenced_footnotes.py +++ b/tests/test_transforms/test_unreferenced_footnotes.py @@ -5,7 +5,6 @@ from __future__ import annotations from typing import TYPE_CHECKING from sphinx._cli.util.errors import strip_escape_sequences -from sphinx.testing.util import SphinxTestApp if TYPE_CHECKING: from pathlib import Path diff --git a/tests/test_util/test_util_i18n.py b/tests/test_util/test_util_i18n.py index 7aeeace369..14db179b19 100644 --- a/tests/test_util/test_util_i18n.py +++ b/tests/test_util/test_util_i18n.py @@ -2,7 +2,7 @@ from __future__ import annotations -import datetime +import datetime # ruff: ignore[unconventional-import-alias] import os import time from pathlib import Path diff --git a/tests/test_util/test_util_inspect.py b/tests/test_util/test_util_inspect.py index 4018a9632a..f17750eda5 100644 --- a/tests/test_util/test_util_inspect.py +++ b/tests/test_util/test_util_inspect.py @@ -3,7 +3,7 @@ from __future__ import annotations import ast -import datetime +import datetime as dt import enum import functools import types @@ -127,7 +127,7 @@ def test_TypeAliasForwardRef(): sig_str = stringify_annotation(alias, 'fully-qualified-except-typing') assert sig_str == "TypeAliasForwardRef('example') | None" - alias = None | alias # NoQA: RUF036 + alias = None | alias sig_str = stringify_annotation(alias, 'fully-qualified-except-typing') assert sig_str == "None | TypeAliasForwardRef('example')" @@ -907,7 +907,7 @@ def test_isattributedescriptor() -> None: assert not inspect.isattributedescriptor(object.__init__) # WrapperDescriptorType assert not inspect.isattributedescriptor(dict.__dict__['fromkeys']) # ClassMethodDescriptorType assert inspect.isattributedescriptor(types.FrameType.f_locals) # GetSetDescriptorType - assert inspect.isattributedescriptor(datetime.timedelta.days) # MemberDescriptorType + assert inspect.isattributedescriptor(dt.timedelta.days) # MemberDescriptorType # fmt: on try: diff --git a/tests/test_util/test_util_logging.py b/tests/test_util/test_util_logging.py index cd2a2eb90b..532726b0d4 100644 --- a/tests/test_util/test_util_logging.py +++ b/tests/test_util/test_util_logging.py @@ -21,7 +21,7 @@ from tests.utils import TEST_ROOTS_DIR if TYPE_CHECKING: import io - from collections.abc import Iterator + from collections.abc import Generator from sphinx.testing.util import SphinxTestApp @@ -283,7 +283,7 @@ def test_pending_warnings(app: SphinxTestApp) -> None: @contextmanager -def force_colors() -> Iterator[None]: +def force_colors() -> Generator[None, None, None]: forcecolor = os.environ.get('FORCE_COLOR', None) try: diff --git a/tests/test_util/typing_test_data.py b/tests/test_util/typing_test_data.py index d7ca4cf1c5..696132f921 100644 --- a/tests/test_util/typing_test_data.py +++ b/tests/test_util/typing_test_data.py @@ -16,7 +16,7 @@ T_co = TypeVar('T_co', covariant=True) T_contra = TypeVar('T_contra', contravariant=True) -def f2(x: List[T], y: List[T_co], z: T) -> List[T_contra]: # pyright: ignore[reportInvalidTypeVarUse] +def f2(x: List[T], y: List[T_co], z: T) -> List[T_contra]: # pyright: ignore[reportInvalidTypeVarUse] # NoQA: UP047 pass diff --git a/tests/test_writers/test_api_translator.py b/tests/test_writers/test_api_translator.py index 1220192528..802ef47530 100644 --- a/tests/test_writers/test_api_translator.py +++ b/tests/test_writers/test_api_translator.py @@ -7,8 +7,6 @@ from typing import TYPE_CHECKING import pytest -from sphinx.testing.util import SphinxTestApp - if TYPE_CHECKING: from collections.abc import Iterator from pathlib import Path diff --git a/tests/utils.py b/tests/utils.py index db34b51727..86d5dd816b 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -19,7 +19,7 @@ from urllib.parse import urlparse from docutils import nodes if TYPE_CHECKING: - from collections.abc import Iterator + from collections.abc import Generator from http.server import HTTPServer from socketserver import BaseRequestHandler from typing import Final @@ -77,7 +77,7 @@ def http_server( *, tls_enabled: bool = False, port: int = 0, -) -> Iterator[HTTPServer]: +) -> Generator[HTTPServer, None, None]: server_cls = HttpsServerThread if tls_enabled else HttpServerThread server_thread = server_cls(handler, port=port) server_thread.start() @@ -91,7 +91,7 @@ def http_server( @contextmanager -def rewrite_hyperlinks(app: Sphinx, server: HTTPServer) -> Iterator[None]: +def rewrite_hyperlinks(app: Sphinx, server: HTTPServer) -> Generator[None, None, None]: """Rewrite hyperlinks that refer to network location 'localhost:7777', allowing that location to vary dynamically with the arbitrary test HTTP server port assigned during unit testing. @@ -111,8 +111,10 @@ def rewrite_hyperlinks(app: Sphinx, server: HTTPServer) -> Iterator[None]: return parsed_uri._replace(netloc=replacement_netloc).geturl() listener_id = app.connect('linkcheck-process-uri', rewrite_hyperlink) - yield - app.disconnect(listener_id) + try: + yield + finally: + app.disconnect(listener_id) @contextmanager @@ -122,7 +124,7 @@ def serve_application( *, tls_enabled: bool = False, port: int = 0, -) -> Iterator[str]: +) -> Generator[str, None, None]: """Prepare a temporary server to handle HTTP requests related to the links found in a Sphinx application project. diff --git a/utils/bump_docker.py b/utils/bump_docker.py index c36d8a8836..627bea5a05 100755 --- a/utils/bump_docker.py +++ b/utils/bump_docker.py @@ -42,7 +42,7 @@ for file in DOCKERFILE_BASE, DOCKERFILE_LATEXPDF: def git(*args: str) -> None: subprocess.run( - ('git', *args), + ('git', *args), # NoQA: S607 cwd=DOCKER_ROOT, check=True, text=True, diff --git a/utils/bump_version.py b/utils/bump_version.py index c6c421a2f2..72aed64694 100755 --- a/utils/bump_version.py +++ b/utils/bump_version.py @@ -12,7 +12,7 @@ from pathlib import Path from typing import TYPE_CHECKING if TYPE_CHECKING: - from collections.abc import Iterator, Sequence + from collections.abc import Generator, Sequence from typing import Literal @@ -164,7 +164,7 @@ class Skip(Exception): @contextmanager -def processing(message: str) -> Iterator[None]: +def processing(message: str) -> Generator[None, None, None]: try: print(message + ' ... ', end='') yield