From 4588653b2497ed25976b7aaff225b889fb476756 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Wed, 31 Jan 2024 21:12:33 +0100 Subject: [PATCH] Migrate from autoflake, black, isort, pyupgrade, flake8 and pydocstyle, to ruff ruff is faster and handle everything we had prior. isort configuration done based on the indication from https://github.com/astral-sh/ruff/issues/4670, previousely based on reorder-python-import (#11896) flake8-docstrings was a wrapper around pydocstyle (now archived) that explicitly asks to use ruff in https://github.com/PyCQA/pydocstyle/pull/658. flake8-typing-import is useful mainly for project that support python 3.7 and the one useful check will be implemented in https://github.com/astral-sh/ruff/issues/2302 We need to keep blacken-doc because ruff does not handle detection of python code inside .md and .rst. The direct link to the repo is now used to avoid a redirection. Manual fixes: - Lines that became too long - % formatting that was not done automatically - type: ignore that were moved around - noqa of hard to fix issues (UP031 generally) - fmt: off and fmt: on that is not really identical between black and ruff - autofix re-order in pre-commit from faster to slower Co-authored-by: Ran Benita --- .pre-commit-config.yaml | 46 ++------- README.rst | 3 - bench/bench.py | 1 + bench/bench_argcomplete.py | 1 + bench/skip.py | 1 + bench/unit_test.py | 1 + doc/en/conf.py | 1 + .../global_testmodule_config/conftest.py | 1 + doc/en/example/assertion/test_failures.py | 1 + doc/en/example/multipython.py | 21 ++-- doc/en/example/xfail_demo.py | 1 + extra/get_issues.py | 1 + pyproject.toml | 52 ++++++++++ scripts/generate-gh-release-notes.py | 2 +- scripts/prepare-release-pr.py | 2 +- scripts/towncrier-draft-to-file.py | 2 +- scripts/update-plugin-list.py | 7 +- src/_pytest/_argcomplete.py | 2 +- src/_pytest/_code/__init__.py | 1 + src/_pytest/_code/code.py | 56 +++++------ src/_pytest/_code/source.py | 10 +- src/_pytest/_io/__init__.py | 1 + src/_pytest/_io/pprint.py | 2 +- src/_pytest/_io/saferepr.py | 5 +- src/_pytest/_io/terminalwriter.py | 7 +- src/_pytest/_io/wcwidth.py | 2 +- src/_pytest/_py/error.py | 1 + src/_pytest/_py/path.py | 43 ++++---- src/_pytest/assertion/__init__.py | 2 +- src/_pytest/assertion/rewrite.py | 37 +++---- src/_pytest/assertion/truncate.py | 1 + src/_pytest/assertion/util.py | 7 +- src/_pytest/cacheprovider.py | 9 +- src/_pytest/capture.py | 11 +-- src/_pytest/compat.py | 9 +- src/_pytest/config/__init__.py | 29 +++--- src/_pytest/config/argparsing.py | 7 +- src/_pytest/config/findpaths.py | 7 +- src/_pytest/debugging.py | 6 +- src/_pytest/deprecated.py | 1 + src/_pytest/doctest.py | 11 ++- src/_pytest/faulthandler.py | 3 +- src/_pytest/fixtures.py | 54 +++++----- src/_pytest/helpconfig.py | 13 +-- src/_pytest/hookspec.py | 3 +- src/_pytest/junitxml.py | 12 +-- src/_pytest/legacypath.py | 3 +- src/_pytest/logging.py | 17 ++-- src/_pytest/main.py | 12 ++- src/_pytest/mark/__init__.py | 5 +- src/_pytest/mark/expression.py | 1 + src/_pytest/mark/structures.py | 31 +++--- src/_pytest/monkeypatch.py | 23 ++--- src/_pytest/nodes.py | 15 +-- src/_pytest/outcomes.py | 3 +- src/_pytest/pastebin.py | 5 +- src/_pytest/pathlib.py | 25 +++-- src/_pytest/pytester.py | 50 +++++----- src/_pytest/python.py | 50 +++++----- src/_pytest/python_api.py | 30 +++--- src/_pytest/recwarn.py | 17 ++-- src/_pytest/reports.py | 26 +++-- src/_pytest/runner.py | 4 +- src/_pytest/scope.py | 1 + src/_pytest/setuponly.py | 4 +- src/_pytest/setupplan.py | 2 +- src/_pytest/skipping.py | 6 +- src/_pytest/stash.py | 1 + src/_pytest/stepwise.py | 3 +- src/_pytest/terminal.py | 21 ++-- src/_pytest/threadexception.py | 2 +- src/_pytest/timing.py | 1 + src/_pytest/tmpdir.py | 6 +- src/_pytest/unittest.py | 13 +-- src/_pytest/unraisableexception.py | 2 +- src/_pytest/warning_types.py | 8 +- src/_pytest/warnings.py | 6 +- src/py.py | 1 + src/pytest/__init__.py | 1 + src/pytest/__main__.py | 1 + testing/_py/test_local.py | 6 +- testing/acceptance_test.py | 2 +- testing/code/test_code.py | 2 +- testing/code/test_excinfo.py | 25 ++--- testing/conftest.py | 3 +- testing/deprecated_test.py | 2 +- .../acceptance/fixture_mock_integration.py | 1 + .../collect_stats/generate_folders.py | 1 + .../unittest/test_unittest_asyncio.py | 1 + .../unittest/test_unittest_asynctest.py | 1 + testing/io/test_pprint.py | 4 +- testing/io/test_saferepr.py | 14 +-- testing/io/test_terminalwriter.py | 5 +- testing/io/test_wcwidth.py | 2 +- testing/logging/test_fixture.py | 3 +- testing/logging/test_reporting.py | 68 +++++-------- testing/python/approx.py | 9 +- testing/python/collect.py | 10 +- testing/python/fixtures.py | 20 ++-- testing/python/integration.py | 2 +- testing/python/metafunc.py | 15 ++- testing/python/raises.py | 2 +- testing/test_argcomplete.py | 5 +- testing/test_assertion.py | 15 +-- testing/test_assertrewrite.py | 21 ++-- testing/test_cacheprovider.py | 39 +++----- testing/test_capture.py | 61 +++++------- testing/test_collection.py | 20 ++-- testing/test_compat.py | 5 +- testing/test_config.py | 36 +++---- testing/test_conftest.py | 4 +- testing/test_debugging.py | 10 +- testing/test_doctest.py | 98 +++++++------------ testing/test_error_diffs.py | 3 +- testing/test_faulthandler.py | 2 +- testing/test_findpaths.py | 2 +- testing/test_helpconfig.py | 2 +- testing/test_junitxml.py | 55 +++++------ testing/test_legacypath.py | 2 +- testing/test_link_resolve.py | 6 +- testing/test_main.py | 18 ++-- testing/test_mark.py | 18 ++-- testing/test_mark_expression.py | 2 +- testing/test_monkeypatch.py | 4 +- testing/test_nodes.py | 7 +- testing/test_parseopt.py | 12 +-- testing/test_pastebin.py | 2 +- testing/test_pathlib.py | 6 +- testing/test_pluginmanager.py | 2 +- testing/test_pytester.py | 10 +- testing/test_python_path.py | 2 +- testing/test_recwarn.py | 4 +- testing/test_reports.py | 16 ++- testing/test_runner.py | 7 +- testing/test_runner_xunit.py | 8 +- testing/test_scope.py | 2 +- testing/test_session.py | 2 +- testing/test_setuponly.py | 2 +- testing/test_skipping.py | 19 ++-- testing/test_stash.py | 2 +- testing/test_stepwise.py | 2 +- testing/test_terminal.py | 43 ++++---- testing/test_threadexception.py | 2 +- testing/test_tmpdir.py | 15 ++- testing/test_unittest.py | 38 +++---- testing/test_unraisableexception.py | 3 +- testing/test_warning_types.py | 5 +- testing/test_warnings.py | 17 ++-- tox.ini | 24 ----- 149 files changed, 799 insertions(+), 971 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 524296477..032fe8c4d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,14 +1,10 @@ repos: -- repo: https://github.com/psf/black - rev: 24.1.1 - hooks: - - id: black - args: [--safe, --quiet] -- repo: https://github.com/asottile/blacken-docs - rev: 1.16.0 - hooks: - - id: blacken-docs - additional_dependencies: [black==24.1.1] +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: "v0.1.15" + hooks: + - id: ruff + args: ["--fix"] + - id: ruff-format - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 hooks: @@ -20,33 +16,11 @@ repos: - id: debug-statements exclude: _pytest/(debugging|hookspec).py language_version: python3 -- repo: https://github.com/PyCQA/autoflake - rev: v2.2.1 +- repo: https://github.com/adamchainz/blacken-docs + rev: 1.16.0 hooks: - - id: autoflake - name: autoflake - args: ["--in-place", "--remove-unused-variables", "--remove-all-unused-imports"] - language: python - files: \.py$ -- repo: https://github.com/PyCQA/flake8 - rev: 7.0.0 - hooks: - - id: flake8 - language_version: python3 - additional_dependencies: - - flake8-typing-imports==1.12.0 - - flake8-docstrings==1.5.0 -- repo: https://github.com/pycqa/isort - rev: 5.13.2 - hooks: - - id: isort - name: isort - args: [--force-single-line, --profile=black] -- repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 - hooks: - - id: pyupgrade - args: [--py38-plus] + - id: blacken-docs + additional_dependencies: [black==24.1.1] - repo: https://github.com/asottile/setup-cfg-fmt rev: v2.5.0 hooks: diff --git a/README.rst b/README.rst index 6e4772b04..fba23872e 100644 --- a/README.rst +++ b/README.rst @@ -27,9 +27,6 @@ :target: https://results.pre-commit.ci/latest/github/pytest-dev/pytest/main :alt: pre-commit.ci status -.. image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://github.com/psf/black - .. image:: https://www.codetriage.com/pytest-dev/pytest/badges/users.svg :target: https://www.codetriage.com/pytest-dev/pytest diff --git a/bench/bench.py b/bench/bench.py index c314b4f56..91e380a80 100644 --- a/bench/bench.py +++ b/bench/bench.py @@ -1,5 +1,6 @@ import sys + if __name__ == "__main__": import cProfile import pstats diff --git a/bench/bench_argcomplete.py b/bench/bench_argcomplete.py index 335733df7..459a12f93 100644 --- a/bench/bench_argcomplete.py +++ b/bench/bench_argcomplete.py @@ -4,6 +4,7 @@ # FastFilesCompleter 0.7383 1.0760 import timeit + imports = [ "from argcomplete.completers import FilesCompleter as completer", "from _pytest._argcomplete import FastFilesCompleter as completer", diff --git a/bench/skip.py b/bench/skip.py index f0c9d1ddb..fd5c292d9 100644 --- a/bench/skip.py +++ b/bench/skip.py @@ -1,5 +1,6 @@ import pytest + SKIP = True diff --git a/bench/unit_test.py b/bench/unit_test.py index ad52069db..d3db111e1 100644 --- a/bench/unit_test.py +++ b/bench/unit_test.py @@ -1,5 +1,6 @@ from unittest import TestCase # noqa: F401 + for i in range(15000): exec( f""" diff --git a/doc/en/conf.py b/doc/en/conf.py index bb5737d7a..cf889eb7a 100644 --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -23,6 +23,7 @@ from typing import TYPE_CHECKING from _pytest import __version__ as version + if TYPE_CHECKING: import sphinx.application diff --git a/doc/en/example/assertion/global_testmodule_config/conftest.py b/doc/en/example/assertion/global_testmodule_config/conftest.py index 7cdf18cdb..4aa7ec23b 100644 --- a/doc/en/example/assertion/global_testmodule_config/conftest.py +++ b/doc/en/example/assertion/global_testmodule_config/conftest.py @@ -2,6 +2,7 @@ import os.path import pytest + mydir = os.path.dirname(__file__) diff --git a/doc/en/example/assertion/test_failures.py b/doc/en/example/assertion/test_failures.py index 350518b43..19d862f60 100644 --- a/doc/en/example/assertion/test_failures.py +++ b/doc/en/example/assertion/test_failures.py @@ -1,6 +1,7 @@ import os.path import shutil + failure_demo = os.path.join(os.path.dirname(__file__), "failure_demo.py") pytest_plugins = ("pytester",) diff --git a/doc/en/example/multipython.py b/doc/en/example/multipython.py index 1354cb37c..861ae9e52 100644 --- a/doc/en/example/multipython.py +++ b/doc/en/example/multipython.py @@ -7,6 +7,7 @@ import textwrap import pytest + pythonlist = ["python3.9", "python3.10", "python3.11"] @@ -32,14 +33,12 @@ class Python: dumpfile = self.picklefile.with_name("dump.py") dumpfile.write_text( textwrap.dedent( - r""" + rf""" import pickle - f = open({!r}, 'wb') - s = pickle.dump({!r}, f, protocol=2) + f = open({str(self.picklefile)!r}, 'wb') + s = pickle.dump({obj!r}, f, protocol=2) f.close() - """.format( - str(self.picklefile), obj - ) + """ ) ) subprocess.run((self.pythonpath, str(dumpfile)), check=True) @@ -48,17 +47,15 @@ class Python: loadfile = self.picklefile.with_name("load.py") loadfile.write_text( textwrap.dedent( - r""" + rf""" import pickle - f = open({!r}, 'rb') + f = open({str(self.picklefile)!r}, 'rb') obj = pickle.load(f) f.close() - res = eval({!r}) + res = eval({expression!r}) if not res: raise SystemExit(1) - """.format( - str(self.picklefile), expression - ) + """ ) ) print(loadfile) diff --git a/doc/en/example/xfail_demo.py b/doc/en/example/xfail_demo.py index 01e6da1ad..1040c8929 100644 --- a/doc/en/example/xfail_demo.py +++ b/doc/en/example/xfail_demo.py @@ -1,5 +1,6 @@ import pytest + xfail = pytest.mark.xfail diff --git a/extra/get_issues.py b/extra/get_issues.py index 4aaa3c3ec..716233ccb 100644 --- a/extra/get_issues.py +++ b/extra/get_issues.py @@ -3,6 +3,7 @@ from pathlib import Path import requests + issues_url = "https://api.github.com/repos/pytest-dev/pytest/issues" diff --git a/pyproject.toml b/pyproject.toml index d45597b77..4a7e58fdb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -123,3 +123,55 @@ target-version = ['py38'] [tool.check-wheel-contents] # W009: Wheel contains multiple toplevel library entries ignore = "W009" + +[tool.ruff] +src = ["src"] +line-length = 88 +select = [ + "D", # pydocstyle + "E", # pycodestyle + "F", # pyflakes + "I", # isort + "UP", # pyupgrade + "W", # pycodestyle +] +ignore = [ + # pycodestyle ignore + # pytest can do weird low-level things, and we usually know + # what we're doing when we use type(..) is ... + "E721", # Do not compare types, use `isinstance()` + # pydocstyle ignore + "D100", # Missing docstring in public module + "D101", # Missing docstring in public class + "D102", # Missing docstring in public method + "D103", # Missing docstring in public function + "D104", # Missing docstring in public package + "D105", # Missing docstring in magic method + "D106", # Missing docstring in public nested class + "D107", # Missing docstring in `__init__` + "D209", # [*] Multi-line docstring closing quotes should be on a separate line + "D205", # 1 blank line required between summary line and description + "D400", # First line should end with a period + "D401", # First line of docstring should be in imperative mood + "D402", # First line should not be the function's signature + "D404", # First word of the docstring should not be "This" + "D415", # First line should end with a period, question mark, or exclamation point +] + +[tool.ruff.format] +docstring-code-format = true + +[tool.ruff.lint.pycodestyle] +# In order to be able to format for 88 char in ruff format +max-line-length = 120 + +[tool.ruff.lint.pydocstyle] +convention = "pep257" + +[tool.ruff.lint.isort] +force-single-line = true +combine-as-imports = true +force-sort-within-sections = true +order-by-type = false +known-local-folder = ["pytest", "_pytest"] +lines-after-imports = 2 diff --git a/scripts/generate-gh-release-notes.py b/scripts/generate-gh-release-notes.py index d22a5cf4c..c27f5774b 100644 --- a/scripts/generate-gh-release-notes.py +++ b/scripts/generate-gh-release-notes.py @@ -8,9 +8,9 @@ our CHANGELOG) into Markdown (which is required by GitHub Releases). Requires Python3.6+. """ +from pathlib import Path import re import sys -from pathlib import Path from typing import Sequence import pypandoc diff --git a/scripts/prepare-release-pr.py b/scripts/prepare-release-pr.py index ce8242a74..d2216b6fc 100644 --- a/scripts/prepare-release-pr.py +++ b/scripts/prepare-release-pr.py @@ -14,8 +14,8 @@ After that, it will create a release using the `release` tox environment, and pu `pytest bot ` commit author. """ import argparse -import re from pathlib import Path +import re from subprocess import check_call from subprocess import check_output from subprocess import run diff --git a/scripts/towncrier-draft-to-file.py b/scripts/towncrier-draft-to-file.py index 7b2748aa8..f771295a0 100644 --- a/scripts/towncrier-draft-to-file.py +++ b/scripts/towncrier-draft-to-file.py @@ -1,6 +1,6 @@ # mypy: disallow-untyped-defs -import sys from subprocess import call +import sys def main() -> int: diff --git a/scripts/update-plugin-list.py b/scripts/update-plugin-list.py index 287073f57..7c5ac9277 100644 --- a/scripts/update-plugin-list.py +++ b/scripts/update-plugin-list.py @@ -11,13 +11,14 @@ from typing import TypedDict import packaging.version import platformdirs -import tabulate -import wcwidth from requests_cache import CachedResponse from requests_cache import CachedSession from requests_cache import OriginalResponse from requests_cache import SQLiteCache +import tabulate from tqdm import tqdm +import wcwidth + FILE_HEAD = r""" .. Note this file is autogenerated by scripts/update-plugin-list.py - usually weekly via github action @@ -85,7 +86,6 @@ def project_response_with_refresh( force refresh in case of last serial mismatch """ - response = session.get(f"https://pypi.org/pypi/{name}/json") if int(response.headers.get("X-PyPI-Last-Serial", -1)) != last_serial: response = session.get(f"https://pypi.org/pypi/{name}/json", refresh=True) @@ -184,7 +184,6 @@ def iter_plugins() -> Iterator[PluginInfo]: def plugin_definitions(plugins: Iterable[PluginInfo]) -> Iterator[str]: """Return RST for the plugin list that fits better on a vertical page.""" - for plugin in plugins: yield dedent( f""" diff --git a/src/_pytest/_argcomplete.py b/src/_pytest/_argcomplete.py index c2ec1797f..c24f92520 100644 --- a/src/_pytest/_argcomplete.py +++ b/src/_pytest/_argcomplete.py @@ -63,9 +63,9 @@ If things do not work right away: """ import argparse +from glob import glob import os import sys -from glob import glob from typing import Any from typing import List from typing import Optional diff --git a/src/_pytest/_code/__init__.py b/src/_pytest/_code/__init__.py index f82c3d2b0..b0a418e95 100644 --- a/src/_pytest/_code/__init__.py +++ b/src/_pytest/_code/__init__.py @@ -10,6 +10,7 @@ from .code import TracebackEntry from .source import getrawcode from .source import Source + __all__ = [ "Code", "ExceptionInfo", diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 8b38a8e78..0662eb8cb 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -2,14 +2,14 @@ import ast import dataclasses import inspect -import os -import re -import sys -import traceback from inspect import CO_VARARGS from inspect import CO_VARKEYWORDS from io import StringIO +import os from pathlib import Path +import re +import sys +import traceback from traceback import format_exception_only from types import CodeType from types import FrameType @@ -51,6 +51,7 @@ from _pytest.deprecated import check_ispytest from _pytest.pathlib import absolutepath from _pytest.pathlib import bestrelpath + if sys.version_info[:2] < (3, 11): from exceptiongroup import BaseExceptionGroup @@ -278,9 +279,9 @@ class TracebackEntry: Mostly for internal use. """ - tbh: Union[bool, Callable[[Optional[ExceptionInfo[BaseException]]], bool]] = ( - False - ) + tbh: Union[ + bool, Callable[[Optional[ExceptionInfo[BaseException]]], bool] + ] = False for maybe_ns_dct in (self.frame.f_locals, self.frame.f_globals): # in normal cases, f_locals and f_globals are dictionaries # however via `exec(...)` / `eval(...)` they can be other types @@ -377,10 +378,12 @@ class Traceback(List[TracebackEntry]): return self @overload - def __getitem__(self, key: "SupportsIndex") -> TracebackEntry: ... + def __getitem__(self, key: "SupportsIndex") -> TracebackEntry: + ... @overload - def __getitem__(self, key: slice) -> "Traceback": ... + def __getitem__(self, key: slice) -> "Traceback": + ... def __getitem__( self, key: Union["SupportsIndex", slice] @@ -485,9 +488,10 @@ class ExceptionInfo(Generic[E]): .. versionadded:: 7.4 """ - assert ( - exception.__traceback__ - ), "Exceptions passed to ExcInfo.from_exception(...) must have a non-None __traceback__." + assert exception.__traceback__, ( + "Exceptions passed to ExcInfo.from_exception(...)" + " must have a non-None __traceback__." + ) exc_info = (type(exception), exception, exception.__traceback__) return cls.from_exc_info(exc_info, exprinfo) @@ -586,9 +590,7 @@ class ExceptionInfo(Generic[E]): def __repr__(self) -> str: if self._excinfo is None: return "" - return "<{} {} tblen={}>".format( - self.__class__.__name__, saferepr(self._excinfo[1]), len(self.traceback) - ) + return f"<{self.__class__.__name__} {saferepr(self._excinfo[1])} tblen={len(self.traceback)}>" def exconly(self, tryshort: bool = False) -> str: """Return the exception as a string. @@ -1016,13 +1018,8 @@ class FormattedExcinfo: extraline: Optional[str] = ( "!!! Recursion error detected, but an error occurred locating the origin of recursion.\n" " The following exception happened when comparing locals in the stack frame:\n" - " {exc_type}: {exc_msg}\n" - " Displaying first and last {max_frames} stack frames out of {total}." - ).format( - exc_type=type(e).__name__, - exc_msg=str(e), - max_frames=max_frames, - total=len(traceback), + f" {type(e).__name__}: {str(e)}\n" + f" Displaying first and last {max_frames} stack frames out of {len(traceback)}." ) # Type ignored because adding two instances of a List subtype # currently incorrectly has type List instead of the subtype. @@ -1054,13 +1051,13 @@ class FormattedExcinfo: # full support for exception groups added to ExceptionInfo. # See https://github.com/pytest-dev/pytest/issues/9159 if isinstance(e, BaseExceptionGroup): - reprtraceback: Union[ReprTracebackNative, ReprTraceback] = ( - ReprTracebackNative( - traceback.format_exception( - type(excinfo_.value), - excinfo_.value, - excinfo_.traceback[0]._rawentry, - ) + reprtraceback: Union[ + ReprTracebackNative, ReprTraceback + ] = ReprTracebackNative( + traceback.format_exception( + type(excinfo_.value), + excinfo_.value, + excinfo_.traceback[0]._rawentry, ) ) else: @@ -1229,7 +1226,6 @@ class ReprEntry(TerminalRepr): the "E" prefix) using syntax highlighting, taking care to not highlighting the ">" character, as doing so might break line continuations. """ - if not self.lines: return diff --git a/src/_pytest/_code/source.py b/src/_pytest/_code/source.py index a85b14371..69f580e20 100644 --- a/src/_pytest/_code/source.py +++ b/src/_pytest/_code/source.py @@ -1,11 +1,10 @@ # mypy: allow-untyped-defs import ast +from bisect import bisect_right import inspect import textwrap import tokenize import types -import warnings -from bisect import bisect_right from typing import Iterable from typing import Iterator from typing import List @@ -13,6 +12,7 @@ from typing import Optional from typing import overload from typing import Tuple from typing import Union +import warnings class Source: @@ -47,10 +47,12 @@ class Source: __hash__ = None # type: ignore @overload - def __getitem__(self, key: int) -> str: ... + def __getitem__(self, key: int) -> str: + ... @overload - def __getitem__(self, key: slice) -> "Source": ... + def __getitem__(self, key: slice) -> "Source": + ... def __getitem__(self, key: Union[int, slice]) -> Union[str, "Source"]: if isinstance(key, int): diff --git a/src/_pytest/_io/__init__.py b/src/_pytest/_io/__init__.py index a804cb549..db001e918 100644 --- a/src/_pytest/_io/__init__.py +++ b/src/_pytest/_io/__init__.py @@ -1,6 +1,7 @@ from .terminalwriter import get_terminal_width from .terminalwriter import TerminalWriter + __all__ = [ "TerminalWriter", "get_terminal_width", diff --git a/src/_pytest/_io/pprint.py b/src/_pytest/_io/pprint.py index 6ec5d5401..75e9a7123 100644 --- a/src/_pytest/_io/pprint.py +++ b/src/_pytest/_io/pprint.py @@ -15,9 +15,9 @@ # useful, thank small children who sleep at night. import collections as _collections import dataclasses as _dataclasses +from io import StringIO as _StringIO import re import types as _types -from io import StringIO as _StringIO from typing import Any from typing import Callable from typing import Dict diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index c51578ed4..9f33fced6 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -19,8 +19,8 @@ def _format_repr_exception(exc: BaseException, obj: object) -> str: raise except BaseException as exc: exc_info = f"unpresentable exception ({_try_repr_or_str(exc)})" - return "<[{} raised in repr()] {} object at 0x{:x}>".format( - exc_info, type(obj).__name__, id(obj) + return ( + f"<[{exc_info} raised in repr()] {type(obj).__name__} object at 0x{id(obj):x}>" ) @@ -108,7 +108,6 @@ def saferepr( This function is a wrapper around the Repr/reprlib functionality of the stdlib. """ - return SafeRepr(maxsize, use_ascii).repr(obj) diff --git a/src/_pytest/_io/terminalwriter.py b/src/_pytest/_io/terminalwriter.py index 89221796a..16449b780 100644 --- a/src/_pytest/_io/terminalwriter.py +++ b/src/_pytest/_io/terminalwriter.py @@ -11,6 +11,7 @@ from typing import TextIO from .wcwidth import wcswidth + # This code was initially copied from py 1.8.1, file _io/terminalwriter.py. @@ -183,9 +184,7 @@ class TerminalWriter: """ if indents and len(indents) != len(lines): raise ValueError( - "indents size ({}) should have same size as lines ({})".format( - len(indents), len(lines) - ) + f"indents size ({len(indents)}) should have same size as lines ({len(lines)})" ) if not indents: indents = [""] * len(lines) @@ -210,8 +209,8 @@ class TerminalWriter: from pygments.lexers.python import PythonLexer as Lexer elif lexer == "diff": from pygments.lexers.diff import DiffLexer as Lexer - import pygments.util from pygments import highlight + import pygments.util except ImportError: return source else: diff --git a/src/_pytest/_io/wcwidth.py b/src/_pytest/_io/wcwidth.py index e5c7bf4d8..538031335 100644 --- a/src/_pytest/_io/wcwidth.py +++ b/src/_pytest/_io/wcwidth.py @@ -1,5 +1,5 @@ -import unicodedata from functools import lru_cache +import unicodedata @lru_cache(100) diff --git a/src/_pytest/_py/error.py b/src/_pytest/_py/error.py index 4b08d3b7a..68f1eed7e 100644 --- a/src/_pytest/_py/error.py +++ b/src/_pytest/_py/error.py @@ -9,6 +9,7 @@ from typing import Callable from typing import TYPE_CHECKING from typing import TypeVar + if TYPE_CHECKING: from typing_extensions import ParamSpec diff --git a/src/_pytest/_py/path.py b/src/_pytest/_py/path.py index 44262a524..af02d67b9 100644 --- a/src/_pytest/_py/path.py +++ b/src/_pytest/_py/path.py @@ -3,15 +3,11 @@ from __future__ import annotations import atexit +from contextlib import contextmanager import fnmatch import importlib.util import io import os -import posixpath -import sys -import uuid -import warnings -from contextlib import contextmanager from os.path import abspath from os.path import dirname from os.path import exists @@ -20,18 +16,23 @@ from os.path import isdir from os.path import isfile from os.path import islink from os.path import normpath +import posixpath from stat import S_ISDIR from stat import S_ISLNK from stat import S_ISREG +import sys from typing import Any from typing import Callable from typing import cast from typing import Literal from typing import overload from typing import TYPE_CHECKING +import uuid +import warnings from . import error + # Moved from local.py. iswin32 = sys.platform == "win32" or (getattr(os, "_name", False) == "nt") @@ -204,10 +205,12 @@ class Stat: if TYPE_CHECKING: @property - def size(self) -> int: ... + def size(self) -> int: + ... @property - def mtime(self) -> float: ... + def mtime(self) -> float: + ... def __getattr__(self, name: str) -> Any: return getattr(self._osstatresult, "st_" + name) @@ -674,7 +677,7 @@ class LocalPath: else: kw.setdefault("dirname", dirname) kw.setdefault("sep", self.sep) - obj.strpath = normpath("%(dirname)s%(sep)s%(basename)s" % kw) + obj.strpath = normpath("{dirname}{sep}{basename}".format(**kw)) return obj def _getbyspec(self, spec: str) -> list[str]: @@ -759,7 +762,10 @@ class LocalPath: # expected "Callable[[str, Any, Any], TextIOWrapper]" [arg-type] # Which seems incorrect, given io.open supports the given argument types. return error.checked_call( - io.open, self.strpath, mode, encoding=encoding # type:ignore[arg-type] + io.open, + self.strpath, + mode, + encoding=encoding, # type:ignore[arg-type] ) return error.checked_call(open, self.strpath, mode) @@ -778,11 +784,11 @@ class LocalPath: valid checkers:: - file=1 # is a file - file=0 # is not a file (may not even exist) - dir=1 # is a dir - link=1 # is a link - exists=1 # exists + file = 1 # is a file + file = 0 # is not a file (may not even exist) + dir = 1 # is a dir + link = 1 # is a link + exists = 1 # exists You can specify multiple checker definitions, for example:: @@ -960,10 +966,12 @@ class LocalPath: return p @overload - def stat(self, raising: Literal[True] = ...) -> Stat: ... + def stat(self, raising: Literal[True] = ...) -> Stat: + ... @overload - def stat(self, raising: Literal[False]) -> Stat | None: ... + def stat(self, raising: Literal[False]) -> Stat | None: + ... def stat(self, raising: bool = True) -> Stat | None: """Return an os.stat() tuple.""" @@ -1275,7 +1283,8 @@ class LocalPath: # error: Argument 1 has incompatible type overloaded function; expected "Callable[[str], str]" [arg-type] # Which seems incorrect, given tempfile.mkdtemp supports the given argument types. path = error.checked_call( - tempfile.mkdtemp, dir=str(rootdir) # type:ignore[arg-type] + tempfile.mkdtemp, + dir=str(rootdir), # type:ignore[arg-type] ) return cls(path) diff --git a/src/_pytest/assertion/__init__.py b/src/_pytest/assertion/__init__.py index 933656978..ea71230e1 100644 --- a/src/_pytest/assertion/__init__.py +++ b/src/_pytest/assertion/__init__.py @@ -16,6 +16,7 @@ from _pytest.config import hookimpl from _pytest.config.argparsing import Parser from _pytest.nodes import Item + if TYPE_CHECKING: from _pytest.main import Session @@ -129,7 +130,6 @@ def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: reporting via the pytest_assertrepr_compare hook. This sets up this custom comparison for the test. """ - ihook = item.ihook def callbinrepr(op, left: object, right: object) -> Optional[str]: diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index c24263e06..0ab6eaa13 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -1,6 +1,7 @@ """Rewrite assertion AST to produce nice error messages.""" import ast +from collections import defaultdict import errno import functools import importlib.abc @@ -10,13 +11,12 @@ import io import itertools import marshal import os +from pathlib import Path +from pathlib import PurePath import struct import sys import tokenize import types -from collections import defaultdict -from pathlib import Path -from pathlib import PurePath from typing import Callable from typing import Dict from typing import IO @@ -40,6 +40,7 @@ from _pytest.pathlib import absolutepath from _pytest.pathlib import fnmatch_ex from _pytest.stash import StashKey + # fmt: off from _pytest.assertion.util import format_explanation as _format_explanation # noqa:F401, isort:skip # fmt:on @@ -671,9 +672,9 @@ class AssertionRewriter(ast.NodeVisitor): self.enable_assertion_pass_hook = False self.source = source self.scope: tuple[ast.AST, ...] = () - self.variables_overwrite: defaultdict[tuple[ast.AST, ...], Dict[str, str]] = ( - defaultdict(dict) - ) + self.variables_overwrite: defaultdict[ + tuple[ast.AST, ...], Dict[str, str] + ] = defaultdict(dict) def run(self, mod: ast.Module) -> None: """Find all assert statements in *mod* and rewrite them.""" @@ -1019,9 +1020,7 @@ class AssertionRewriter(ast.NodeVisitor): ] ): pytest_temp = self.variable() - self.variables_overwrite[self.scope][ - v.left.target.id - ] = v.left # type:ignore[assignment] + self.variables_overwrite[self.scope][v.left.target.id] = v.left # type:ignore[assignment] v.left.target.id = pytest_temp self.push_format_context() res, expl = self.visit(v) @@ -1065,9 +1064,7 @@ class AssertionRewriter(ast.NodeVisitor): if isinstance(arg, ast.Name) and arg.id in self.variables_overwrite.get( self.scope, {} ): - arg = self.variables_overwrite[self.scope][ - arg.id - ] # type:ignore[assignment] + arg = self.variables_overwrite[self.scope][arg.id] # type:ignore[assignment] res, expl = self.visit(arg) arg_expls.append(expl) new_args.append(res) @@ -1075,9 +1072,7 @@ class AssertionRewriter(ast.NodeVisitor): if isinstance( keyword.value, ast.Name ) and keyword.value.id in self.variables_overwrite.get(self.scope, {}): - keyword.value = self.variables_overwrite[self.scope][ - keyword.value.id - ] # type:ignore[assignment] + keyword.value = self.variables_overwrite[self.scope][keyword.value.id] # type:ignore[assignment] res, expl = self.visit(keyword.value) new_kwargs.append(ast.keyword(keyword.arg, res)) if keyword.arg: @@ -1114,13 +1109,9 @@ class AssertionRewriter(ast.NodeVisitor): if isinstance( comp.left, ast.Name ) and comp.left.id in self.variables_overwrite.get(self.scope, {}): - comp.left = self.variables_overwrite[self.scope][ - comp.left.id - ] # type:ignore[assignment] + comp.left = self.variables_overwrite[self.scope][comp.left.id] # type:ignore[assignment] if isinstance(comp.left, ast.NamedExpr): - self.variables_overwrite[self.scope][ - comp.left.target.id - ] = comp.left # type:ignore[assignment] + self.variables_overwrite[self.scope][comp.left.target.id] = comp.left # type:ignore[assignment] left_res, left_expl = self.visit(comp.left) if isinstance(comp.left, (ast.Compare, ast.BoolOp)): left_expl = f"({left_expl})" @@ -1138,9 +1129,7 @@ class AssertionRewriter(ast.NodeVisitor): and next_operand.target.id == left_res.id ): next_operand.target.id = self.variable() - self.variables_overwrite[self.scope][ - left_res.id - ] = next_operand # type:ignore[assignment] + self.variables_overwrite[self.scope][left_res.id] = next_operand # type:ignore[assignment] next_res, next_expl = self.visit(next_operand) if isinstance(next_operand, (ast.Compare, ast.BoolOp)): next_expl = f"({next_expl})" diff --git a/src/_pytest/assertion/truncate.py b/src/_pytest/assertion/truncate.py index 1e5865672..902d4baf8 100644 --- a/src/_pytest/assertion/truncate.py +++ b/src/_pytest/assertion/truncate.py @@ -11,6 +11,7 @@ from _pytest.assertion import util from _pytest.config import Config from _pytest.nodes import Item + DEFAULT_MAX_LINES = 8 DEFAULT_MAX_CHARS = 8 * 80 USAGE_MSG = "use '-vv' to show" diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index d2f67994c..33dcd4628 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -15,13 +15,14 @@ from typing import Protocol from typing import Sequence from unicodedata import normalize -import _pytest._code from _pytest import outcomes +import _pytest._code from _pytest._io.pprint import PrettyPrinter from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr_unlimited from _pytest.config import Config + # The _reprcompare attribute on the util module is used by the new assertion # interpretation code and assertion rewriter to detect this plugin was # loaded and in turn call the hooks defined here as part of the @@ -302,8 +303,8 @@ def _diff_text(left: str, right: str, verbose: int = 0) -> List[str]: if i > 42: i -= 10 # Provide some context explanation += [ - "Skipping {} identical trailing " - "characters in diff, use -v to show".format(i) + f"Skipping {i} identical trailing " + "characters in diff, use -v to show" ] left = left[:-i] right = right[:-i] diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 87ef38b5e..5ccd2168d 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -32,6 +32,7 @@ from _pytest.nodes import Directory from _pytest.nodes import File from _pytest.reports import TestReport + README_CONTENT = """\ # pytest cache directory # @@ -368,15 +369,13 @@ class LFPlugin: noun = "failure" if self._previously_failed_count == 1 else "failures" suffix = " first" if self.config.getoption("failedfirst") else "" - self._report_status = "rerun previous {count} {noun}{suffix}".format( - count=self._previously_failed_count, suffix=suffix, noun=noun + self._report_status = ( + f"rerun previous {self._previously_failed_count} {noun}{suffix}" ) if self._skipped_files > 0: files_noun = "file" if self._skipped_files == 1 else "files" - self._report_status += " (skipped {files} {files_noun})".format( - files=self._skipped_files, files_noun=files_noun - ) + self._report_status += f" (skipped {self._skipped_files} {files_noun})" else: self._report_status = "no previously failed tests, " if self.config.getoption("last_failed_no_failures") == "none": diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index c89763344..b9e095028 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -4,9 +4,9 @@ import abc import collections import contextlib import io +from io import UnsupportedOperation import os import sys -from io import UnsupportedOperation from tempfile import TemporaryFile from types import TracebackType from typing import Any @@ -39,6 +39,7 @@ from _pytest.nodes import File from _pytest.nodes import Item from _pytest.reports import CollectReport + _CaptureMethod = Literal["fd", "sys", "no", "tee-sys"] @@ -790,9 +791,7 @@ class CaptureManager: current_fixture = self._capture_fixture.request.fixturename requested_fixture = capture_fixture.request.fixturename capture_fixture.request.raiseerror( - "cannot use {} and {} at the same time".format( - requested_fixture, current_fixture - ) + f"cannot use {requested_fixture} and {current_fixture} at the same time" ) self._capture_fixture = capture_fixture @@ -988,7 +987,6 @@ def capsys(request: SubRequest) -> Generator[CaptureFixture[str], None, None]: Returns an instance of :class:`CaptureFixture[str] `. Example: - .. code-block:: python def test_output(capsys): @@ -1016,7 +1014,6 @@ def capsysbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None, Returns an instance of :class:`CaptureFixture[bytes] `. Example: - .. code-block:: python def test_output(capsysbinary): @@ -1044,7 +1041,6 @@ def capfd(request: SubRequest) -> Generator[CaptureFixture[str], None, None]: Returns an instance of :class:`CaptureFixture[str] `. Example: - .. code-block:: python def test_system_echo(capfd): @@ -1072,7 +1068,6 @@ def capfdbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None, N Returns an instance of :class:`CaptureFixture[bytes] `. Example: - .. code-block:: python def test_system_echo(capfdbinary): diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index 821b422f3..32e635255 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -6,17 +6,18 @@ import dataclasses import enum import functools import inspect -import os -import sys from inspect import Parameter from inspect import signature +import os from pathlib import Path +import sys from typing import Any from typing import Callable from typing import Final from typing import NoReturn from typing import TypeVar + _T = TypeVar("_T") _S = TypeVar("_S") @@ -243,9 +244,7 @@ def get_real_func(obj): from _pytest._io.saferepr import saferepr raise ValueError( - ("could not find real function of {start}\nstopped at {current}").format( - start=saferepr(start_obj), current=saferepr(obj) - ) + f"could not find real function of {saferepr(start_obj)}\nstopped at {saferepr(obj)}" ) if isinstance(obj, functools.partial): obj = obj.func diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 7bab69b7a..8c2f05239 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -5,18 +5,17 @@ import collections.abc import copy import dataclasses import enum +from functools import lru_cache import glob import importlib.metadata import inspect import os +from pathlib import Path import re import shlex import sys -import types -import warnings -from functools import lru_cache -from pathlib import Path from textwrap import dedent +import types from types import FunctionType from typing import Any from typing import Callable @@ -37,6 +36,7 @@ from typing import Tuple from typing import Type from typing import TYPE_CHECKING from typing import Union +import warnings from pluggy import HookimplMarker from pluggy import HookimplOpts @@ -44,15 +44,15 @@ from pluggy import HookspecMarker from pluggy import HookspecOpts from pluggy import PluginManager -import _pytest._code -import _pytest.deprecated -import _pytest.hookspec from .exceptions import PrintHelp as PrintHelp from .exceptions import UsageError as UsageError from .findpaths import determine_setup +import _pytest._code from _pytest._code import ExceptionInfo from _pytest._code import filter_traceback from _pytest._io import TerminalWriter +import _pytest.deprecated +import _pytest.hookspec from _pytest.outcomes import fail from _pytest.outcomes import Skipped from _pytest.pathlib import absolutepath @@ -65,6 +65,7 @@ from _pytest.stash import Stash from _pytest.warning_types import PytestConfigWarning from _pytest.warning_types import warn_explicit_for + if TYPE_CHECKING: from .argparsing import Argument from .argparsing import Parser @@ -813,7 +814,7 @@ class PytestPluginManager(PluginManager): def _get_plugin_specs_as_list( - specs: Union[None, types.ModuleType, str, Sequence[str]] + specs: Union[None, types.ModuleType, str, Sequence[str]], ) -> List[str]: """Parse a plugins specification into a list of plugin names.""" # None means empty. @@ -1374,12 +1375,7 @@ class Config: if Version(minver) > Version(pytest.__version__): raise pytest.UsageError( - "%s: 'minversion' requires pytest-%s, actual pytest-%s'" - % ( - self.inipath, - minver, - pytest.__version__, - ) + f"{self.inipath}: 'minversion' requires pytest-{minver}, actual pytest-{pytest.__version__}'" ) def _validate_config_options(self) -> None: @@ -1614,9 +1610,7 @@ class Config: key, user_ini_value = ini_config.split("=", 1) except ValueError as e: raise UsageError( - "-o/--override-ini expects option=value style (got: {!r}).".format( - ini_config - ) + f"-o/--override-ini expects option=value style (got: {ini_config!r})." ) from e else: if key == name: @@ -1674,7 +1668,6 @@ class Config: can be used to explicitly use the global verbosity level. Example: - .. code-block:: ini # content of pytest.ini diff --git a/src/_pytest/config/argparsing.py b/src/_pytest/config/argparsing.py index a31278f32..18cf4600a 100644 --- a/src/_pytest/config/argparsing.py +++ b/src/_pytest/config/argparsing.py @@ -1,8 +1,8 @@ # mypy: allow-untyped-defs import argparse +from gettext import gettext import os import sys -from gettext import gettext from typing import Any from typing import Callable from typing import cast @@ -21,6 +21,7 @@ import _pytest._io from _pytest.config.exceptions import UsageError from _pytest.deprecated import check_ispytest + FILE_OR_DIR = "file_or_dir" @@ -216,7 +217,7 @@ class Parser: def get_ini_default_for_type( - type: Optional[Literal["string", "paths", "pathlist", "args", "linelist", "bool"]] + type: Optional[Literal["string", "paths", "pathlist", "args", "linelist", "bool"]], ) -> Any: """ Used by addini to get the default value for a given ini-option type, when @@ -448,7 +449,7 @@ class MyOptionParser(argparse.ArgumentParser): ) -> Optional[Tuple[Optional[argparse.Action], str, Optional[str]]]: if not arg_string: return None - if not arg_string[0] in self.prefix_chars: + if arg_string[0] not in self.prefix_chars: return None if arg_string in self._option_string_actions: action = self._option_string_actions[arg_string] diff --git a/src/_pytest/config/findpaths.py b/src/_pytest/config/findpaths.py index 8e69a46fd..96f353651 100644 --- a/src/_pytest/config/findpaths.py +++ b/src/_pytest/config/findpaths.py @@ -1,6 +1,6 @@ import os -import sys from pathlib import Path +import sys from typing import Dict from typing import Iterable from typing import List @@ -37,7 +37,6 @@ def load_config_dict_from_file( Return None if the file does not contain valid pytest configuration. """ - # Configuration from ini files are obtained from the [pytest] section, if present. if filepath.suffix == ".ini": iniconfig = _parse_ini_config(filepath) @@ -213,9 +212,7 @@ def determine_setup( rootdir = absolutepath(os.path.expandvars(rootdir_cmd_arg)) if not rootdir.is_dir(): raise UsageError( - "Directory '{}' not found. Check your '--rootdir' option.".format( - rootdir - ) + f"Directory '{rootdir}' not found. Check your '--rootdir' option." ) assert rootdir is not None return rootdir, inipath, inicfg or {} diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index aaac3e1b9..cb157cd67 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -4,7 +4,6 @@ import argparse import functools import sys import types -import unittest from typing import Any from typing import Callable from typing import Generator @@ -14,6 +13,7 @@ from typing import Tuple from typing import Type from typing import TYPE_CHECKING from typing import Union +import unittest from _pytest import outcomes from _pytest._code import ExceptionInfo @@ -26,6 +26,7 @@ from _pytest.config.exceptions import UsageError from _pytest.nodes import Node from _pytest.reports import BaseReport + if TYPE_CHECKING: from _pytest.capture import CaptureManager from _pytest.runner import CallInfo @@ -264,8 +265,7 @@ class pytestPDB: elif capturing: tw.sep( ">", - "PDB %s (IO-capturing turned off for %s)" - % (method, capturing), + f"PDB {method} (IO-capturing turned off for {capturing})", ) else: tw.sep(">", f"PDB {method}") diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 0f611aa5c..56271c957 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -15,6 +15,7 @@ from _pytest.warning_types import PytestDeprecationWarning from _pytest.warning_types import PytestRemovedIn9Warning from _pytest.warning_types import UnformattedWarning + # set of plugins which have been integrated into the core; we use this list to ignore # them during registration to avoid conflicts DEPRECATED_EXTERNAL_PLUGINS = { diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 6a5bc7577..fdf84d300 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -1,16 +1,15 @@ # mypy: allow-untyped-defs """Discover and run doctests in modules and test files.""" import bdb +from contextlib import contextmanager import functools import inspect import os +from pathlib import Path import platform import sys import traceback import types -import warnings -from contextlib import contextmanager -from pathlib import Path from typing import Any from typing import Callable from typing import Dict @@ -24,6 +23,7 @@ from typing import Tuple from typing import Type from typing import TYPE_CHECKING from typing import Union +import warnings from _pytest import outcomes from _pytest._code.code import ExceptionInfo @@ -45,6 +45,7 @@ from _pytest.python import Module from _pytest.python_api import approx from _pytest.warning_types import PytestWarning + if TYPE_CHECKING: import doctest @@ -486,9 +487,9 @@ def _patch_unwrap_mock_aware() -> Generator[None, None, None]: return real_unwrap(func, stop=lambda obj: _is_mocked(obj) or _stop(func)) except Exception as e: warnings.warn( - "Got %r when unwrapping %r. This is usually caused " + f"Got {e!r} when unwrapping {func!r}. This is usually caused " "by a violation of Python's object protocol; see e.g. " - "https://github.com/pytest-dev/pytest/issues/5080" % (e, func), + "https://github.com/pytest-dev/pytest/issues/5080", PytestWarning, ) raise diff --git a/src/_pytest/faulthandler.py b/src/_pytest/faulthandler.py index 824aec58e..083bcb837 100644 --- a/src/_pytest/faulthandler.py +++ b/src/_pytest/faulthandler.py @@ -2,11 +2,12 @@ import os import sys from typing import Generator -import pytest from _pytest.config import Config from _pytest.config.argparsing import Parser from _pytest.nodes import Item from _pytest.stash import StashKey +import pytest + fault_handler_original_stderr_fd_key = StashKey[int]() fault_handler_stderr_fd_key = StashKey[int]() diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 7000cdab8..a4e03f80c 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -1,13 +1,12 @@ # mypy: allow-untyped-defs import abc +from collections import defaultdict +from collections import deque +from contextlib import suppress import dataclasses import functools import inspect import os -import warnings -from collections import defaultdict -from collections import deque -from contextlib import suppress from pathlib import Path from typing import AbstractSet from typing import Any @@ -31,6 +30,7 @@ from typing import Tuple from typing import TYPE_CHECKING from typing import TypeVar from typing import Union +import warnings import _pytest from _pytest import nodes @@ -67,6 +67,7 @@ from _pytest.scope import _ScopeName from _pytest.scope import HIGH_SCOPES from _pytest.scope import Scope + if TYPE_CHECKING: from typing import Deque @@ -601,13 +602,9 @@ class FixtureRequest(abc.ABC): fixtures_not_supported = getattr(funcitem, "nofuncargs", False) if has_params and fixtures_not_supported: msg = ( - "{name} does not support fixtures, maybe unittest.TestCase subclass?\n" - "Node id: {nodeid}\n" - "Function type: {typename}" - ).format( - name=funcitem.name, - nodeid=funcitem.nodeid, - typename=type(funcitem).__name__, + f"{funcitem.name} does not support fixtures, maybe unittest.TestCase subclass?\n" + f"Node id: {funcitem.nodeid}\n" + f"Function type: {type(funcitem).__name__}" ) fail(msg, pytrace=False) if has_params: @@ -740,9 +737,7 @@ class SubRequest(FixtureRequest): if node is None and scope is Scope.Class: # Fallback to function item itself. node = self._pyfuncitem - assert node, 'Could not obtain a node for scope "{}" for function {!r}'.format( - scope, self._pyfuncitem - ) + assert node, f'Could not obtain a node for scope "{scope}" for function {self._pyfuncitem!r}' return node def _check_scope( @@ -845,8 +840,8 @@ class FixtureLookupError(LookupError): if faclist: available.add(name) if self.argname in available: - msg = " recursive dependency involving fixture '{}' detected".format( - self.argname + msg = ( + f" recursive dependency involving fixture '{self.argname}' detected" ) else: msg = f"fixture '{self.argname}' not found" @@ -940,15 +935,13 @@ def _eval_scope_callable( result = scope_callable(fixture_name=fixture_name, config=config) # type: ignore[call-arg] except Exception as e: raise TypeError( - "Error evaluating {} while defining fixture '{}'.\n" - "Expected a function with the signature (*, fixture_name, config)".format( - scope_callable, fixture_name - ) + f"Error evaluating {scope_callable} while defining fixture '{fixture_name}'.\n" + "Expected a function with the signature (*, fixture_name, config)" ) from e if not isinstance(result, str): fail( - "Expected {} to return a 'str' while defining fixture '{}', but it returned:\n" - "{!r}".format(scope_callable, fixture_name, result), + f"Expected {scope_callable} to return a 'str' while defining fixture '{fixture_name}', but it returned:\n" + f"{result!r}", pytrace=False, ) return result @@ -1090,9 +1083,7 @@ class FixtureDef(Generic[FixtureValue]): return request.param_index if not hasattr(request, "param") else request.param def __repr__(self) -> str: - return "".format( - self.argname, self.scope, self.baseid - ) + return f"" def resolve_fixture_function( @@ -1113,7 +1104,8 @@ def resolve_fixture_function( # Handle the case where fixture is defined not in a test class, but some other class # (for example a plugin class with a fixture), see #2270. if hasattr(fixturefunc, "__self__") and not isinstance( - request.instance, fixturefunc.__self__.__class__ # type: ignore[union-attr] + request.instance, + fixturefunc.__self__.__class__, # type: ignore[union-attr] ): return fixturefunc fixturefunc = getimfunc(fixturedef.func) @@ -1208,9 +1200,7 @@ class FixtureFunctionMarker: if name == "request": location = getlocation(function) fail( - "'request' is a reserved word for fixtures, use another name:\n {}".format( - location - ), + f"'request' is a reserved word for fixtures, use another name:\n {location}", pytrace=False, ) @@ -1230,7 +1220,8 @@ def fixture( Union[Sequence[Optional[object]], Callable[[Any], Optional[object]]] ] = ..., name: Optional[str] = ..., -) -> FixtureFunction: ... +) -> FixtureFunction: + ... @overload @@ -1244,7 +1235,8 @@ def fixture( # noqa: F811 Union[Sequence[Optional[object]], Callable[[Any], Optional[object]]] ] = ..., name: Optional[str] = None, -) -> FixtureFunctionMarker: ... +) -> FixtureFunctionMarker: + ... def fixture( # noqa: F811 diff --git a/src/_pytest/helpconfig.py b/src/_pytest/helpconfig.py index 4f7d35172..aa8bf65c7 100644 --- a/src/_pytest/helpconfig.py +++ b/src/_pytest/helpconfig.py @@ -1,19 +1,19 @@ # mypy: allow-untyped-defs """Version info, help messages, tracing configuration.""" +from argparse import Action import os import sys -from argparse import Action from typing import Generator from typing import List from typing import Optional from typing import Union -import pytest from _pytest.config import Config from _pytest.config import ExitCode from _pytest.config import PrintHelp from _pytest.config.argparsing import Parser from _pytest.terminal import TerminalReporter +import pytest class HelpAction(Action): @@ -109,9 +109,8 @@ def pytest_cmdline_parse() -> Generator[None, Config, Config]: path = config.option.debug debugfile = open(path, "w", encoding="utf-8") debugfile.write( - "versions pytest-%s, " - "python-%s\ninvocation_dir=%s\ncwd=%s\nargs=%s\n\n" - % ( + "versions pytest-{}, " + "python-{}\ninvocation_dir={}\ncwd={}\nargs={}\n\n".format( pytest.__version__, ".".join(map(str, sys.version_info)), config.invocation_params.dir, @@ -137,9 +136,7 @@ def pytest_cmdline_parse() -> Generator[None, Config, Config]: def showversion(config: Config) -> None: if config.option.version > 1: sys.stdout.write( - "This is pytest version {}, imported from {}\n".format( - pytest.__version__, pytest.__file__ - ) + f"This is pytest version {pytest.__version__}, imported from {pytest.__file__}\n" ) plugininfo = getpluginversioninfo(config) if plugininfo: diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index 0aace8c34..58f4986ec 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -14,10 +14,11 @@ from typing import Union from pluggy import HookspecMarker + if TYPE_CHECKING: import pdb - import warnings from typing import Literal + import warnings from _pytest._code.code import ExceptionInfo from _pytest._code.code import ExceptionRepr diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index a198e1d23..7a952231c 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -7,12 +7,11 @@ Based on initial code from Ross Lawley. Output conforms to https://github.com/jenkinsci/xunit-plugin/blob/master/src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd """ +from datetime import datetime import functools import os import platform import re -import xml.etree.ElementTree as ET -from datetime import datetime from typing import Callable from typing import Dict from typing import List @@ -20,8 +19,8 @@ from typing import Match from typing import Optional from typing import Tuple from typing import Union +import xml.etree.ElementTree as ET -import pytest from _pytest import nodes from _pytest import timing from _pytest._code.code import ExceptionRepr @@ -33,6 +32,8 @@ from _pytest.fixtures import FixtureRequest from _pytest.reports import TestReport from _pytest.stash import StashKey from _pytest.terminal import TerminalReporter +import pytest + xml_key = StashKey["LogXML"]() @@ -273,9 +274,7 @@ def _warn_incompatibility_with_xunit2( if xml is not None and xml.family not in ("xunit1", "legacy"): request.node.warn( PytestWarning( - "{fixture_name} is incompatible with junit_family '{family}' (use 'legacy' or 'xunit1')".format( - fixture_name=fixture_name, family=xml.family - ) + f"{fixture_name} is incompatible with junit_family '{xml.family}' (use 'legacy' or 'xunit1')" ) ) @@ -367,7 +366,6 @@ def record_testsuite_property(request: FixtureRequest) -> Callable[[str, object] `pytest-xdist `__ plugin. See :issue:`7767` for details. """ - __tracebackhide__ = True def record_func(name: str, value: object) -> None: diff --git a/src/_pytest/legacypath.py b/src/_pytest/legacypath.py index 5dafea402..b56f3a6fb 100644 --- a/src/_pytest/legacypath.py +++ b/src/_pytest/legacypath.py @@ -2,9 +2,9 @@ """Add backward compatibility support for the legacy py path type.""" import dataclasses import os +from pathlib import Path import shlex import subprocess -from pathlib import Path from typing import Final from typing import final from typing import List @@ -34,6 +34,7 @@ from _pytest.pytester import RunResult from _pytest.terminal import TerminalReporter from _pytest.tmpdir import TempPathFactory + if TYPE_CHECKING: import pexpect diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 5565a6b3f..aa3a33e63 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -1,17 +1,17 @@ # mypy: allow-untyped-defs """Access and control log capturing.""" -import io -import logging -import os -import re from contextlib import contextmanager from contextlib import nullcontext from datetime import datetime from datetime import timedelta from datetime import timezone +import io from io import StringIO +import logging from logging import LogRecord +import os from pathlib import Path +import re from types import TracebackType from typing import AbstractSet from typing import Dict @@ -44,6 +44,7 @@ from _pytest.main import Session from _pytest.stash import StashKey from _pytest.terminal import TerminalReporter + if TYPE_CHECKING: logging_StreamHandler = logging.StreamHandler[StringIO] else: @@ -116,7 +117,6 @@ class ColoredLevelFormatter(DatetimeFormatter): .. warning:: This is an experimental API. """ - assert self._fmt is not None levelname_fmt_match = self.LEVELNAME_FMT_REGEX.search(self._fmt) if not levelname_fmt_match: @@ -183,7 +183,6 @@ class PercentStyleMultiline(logging.PercentStyle): 0 (auto-indent turned off) or >0 (explicitly set indentation position). """ - if auto_indent_option is None: return 0 elif isinstance(auto_indent_option, bool): @@ -625,9 +624,9 @@ def get_log_level_for_setting(config: Config, *setting_names: str) -> Optional[i except ValueError as e: # Python logging does not recognise this as a logging level raise UsageError( - "'{}' is not recognized as a logging level name for " - "'{}'. Please consider passing the " - "logging level num instead.".format(log_level, setting_name) + f"'{log_level}' is not recognized as a logging level name for " + f"'{setting_name}'. Please consider passing the " + "logging level num instead." ) from e diff --git a/src/_pytest/main.py b/src/_pytest/main.py index a75e53a0c..4907a4256 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -6,9 +6,8 @@ import fnmatch import functools import importlib import os -import sys -import warnings from pathlib import Path +import sys from typing import AbstractSet from typing import Callable from typing import Dict @@ -23,11 +22,12 @@ from typing import overload from typing import Sequence from typing import Tuple from typing import Union +import warnings import pluggy -import _pytest._code from _pytest import nodes +import _pytest._code from _pytest.config import Config from _pytest.config import directory_arg from _pytest.config import ExitCode @@ -722,12 +722,14 @@ class Session(nodes.Collector): @overload def perform_collect( self, args: Optional[Sequence[str]] = ..., genitems: "Literal[True]" = ... - ) -> Sequence[nodes.Item]: ... + ) -> Sequence[nodes.Item]: + ... @overload def perform_collect( # noqa: F811 self, args: Optional[Sequence[str]] = ..., genitems: bool = ... - ) -> Sequence[Union[nodes.Item, nodes.Collector]]: ... + ) -> Sequence[Union[nodes.Item, nodes.Collector]]: + ... def perform_collect( # noqa: F811 self, args: Optional[Sequence[str]] = None, genitems: bool = True diff --git a/src/_pytest/mark/__init__.py b/src/_pytest/mark/__init__.py index ec5023c91..77dabd95d 100644 --- a/src/_pytest/mark/__init__.py +++ b/src/_pytest/mark/__init__.py @@ -24,6 +24,7 @@ from _pytest.config import UsageError from _pytest.config.argparsing import Parser from _pytest.stash import StashKey + if TYPE_CHECKING: from _pytest.nodes import Item @@ -268,8 +269,8 @@ def pytest_configure(config: Config) -> None: if empty_parameterset not in ("skip", "xfail", "fail_at_collect", None, ""): raise UsageError( - "{!s} must be one of skip, xfail or fail_at_collect" - " but it is {!r}".format(EMPTY_PARAMETERSET_OPTION, empty_parameterset) + f"{EMPTY_PARAMETERSET_OPTION!s} must be one of skip, xfail or fail_at_collect" + f" but it is {empty_parameterset!r}" ) diff --git a/src/_pytest/mark/expression.py b/src/_pytest/mark/expression.py index 5fa0ccaa7..78b7fda69 100644 --- a/src/_pytest/mark/expression.py +++ b/src/_pytest/mark/expression.py @@ -27,6 +27,7 @@ from typing import NoReturn from typing import Optional from typing import Sequence + __all__ = [ "Expression", "ParseError", diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 544e1dccf..7c8501d73 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -2,7 +2,6 @@ import collections.abc import dataclasses import inspect -import warnings from typing import Any from typing import Callable from typing import Collection @@ -22,6 +21,7 @@ from typing import Type from typing import TYPE_CHECKING from typing import TypeVar from typing import Union +import warnings from .._code import getfslineno from ..compat import ascii_escaped @@ -33,6 +33,7 @@ from _pytest.deprecated import MARKED_FIXTURE from _pytest.outcomes import fail from _pytest.warning_types import PytestUnknownMarkWarning + if TYPE_CHECKING: from ..nodes import Node @@ -112,7 +113,6 @@ class ParameterSet(NamedTuple): Enforce tuple wrapping so single argument tuple values don't get decomposed and break tests. """ - if isinstance(parameterset, cls): return parameterset if force_tuple: @@ -272,8 +272,8 @@ class MarkDecorator: ``MarkDecorators`` are created with ``pytest.mark``:: - mark1 = pytest.mark.NAME # Simple MarkDecorator - mark2 = pytest.mark.NAME(name1=value) # Parametrized MarkDecorator + mark1 = pytest.mark.NAME # Simple MarkDecorator + mark2 = pytest.mark.NAME(name1=value) # Parametrized MarkDecorator and can then be applied as decorators to test functions:: @@ -394,7 +394,7 @@ def get_unpacked_marks( def normalize_mark_list( - mark_list: Iterable[Union[Mark, MarkDecorator]] + mark_list: Iterable[Union[Mark, MarkDecorator]], ) -> Iterable[Mark]: """ Normalize an iterable of Mark or MarkDecorator objects into a list of marks @@ -434,10 +434,12 @@ if TYPE_CHECKING: class _SkipMarkDecorator(MarkDecorator): @overload # type: ignore[override,misc,no-overload-impl] - def __call__(self, arg: Markable) -> Markable: ... + def __call__(self, arg: Markable) -> Markable: + ... @overload - def __call__(self, reason: str = ...) -> "MarkDecorator": ... + def __call__(self, reason: str = ...) -> "MarkDecorator": + ... class _SkipifMarkDecorator(MarkDecorator): def __call__( # type: ignore[override] @@ -445,11 +447,13 @@ if TYPE_CHECKING: condition: Union[str, bool] = ..., *conditions: Union[str, bool], reason: str = ..., - ) -> MarkDecorator: ... + ) -> MarkDecorator: + ... class _XfailMarkDecorator(MarkDecorator): @overload # type: ignore[override,misc,no-overload-impl] - def __call__(self, arg: Markable) -> Markable: ... + def __call__(self, arg: Markable) -> Markable: + ... @overload def __call__( @@ -462,7 +466,8 @@ if TYPE_CHECKING: None, Type[BaseException], Tuple[Type[BaseException], ...] ] = ..., strict: bool = ..., - ) -> MarkDecorator: ... + ) -> MarkDecorator: + ... class _ParametrizeMarkDecorator(MarkDecorator): def __call__( # type: ignore[override] @@ -478,7 +483,8 @@ if TYPE_CHECKING: ] ] = ..., scope: Optional[_ScopeName] = ..., - ) -> MarkDecorator: ... + ) -> MarkDecorator: + ... class _UsefixturesMarkDecorator(MarkDecorator): def __call__(self, *fixtures: str) -> MarkDecorator: # type: ignore[override] @@ -498,9 +504,10 @@ class MarkGenerator: import pytest + @pytest.mark.slowtest def test_function(): - pass + pass applies a 'slowtest' :class:`Mark` on ``test_function``. """ diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index 7b678017f..e96a93868 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -1,10 +1,9 @@ # mypy: allow-untyped-defs """Monkeypatching and mocking functionality.""" +from contextlib import contextmanager import os import re import sys -import warnings -from contextlib import contextmanager from typing import Any from typing import final from typing import Generator @@ -16,10 +15,12 @@ from typing import overload from typing import Tuple from typing import TypeVar from typing import Union +import warnings from _pytest.fixtures import fixture from _pytest.warning_types import PytestWarning + RE_IMPORT_ERROR_NAME = re.compile(r"^No module named (.*)$") @@ -90,9 +91,7 @@ def annotated_getattr(obj: object, name: str, ann: str) -> object: obj = getattr(obj, name) except AttributeError as e: raise AttributeError( - "{!r} object at {} has no attribute {!r}".format( - type(obj).__name__, ann, name - ) + f"{type(obj).__name__!r} object at {ann} has no attribute {name!r}" ) from e return obj @@ -142,7 +141,6 @@ class MonkeyPatch: which undoes any patching done inside the ``with`` block upon exit. Example: - .. code-block:: python import functools @@ -169,7 +167,8 @@ class MonkeyPatch: name: object, value: Notset = ..., raising: bool = ..., - ) -> None: ... + ) -> None: + ... @overload def setattr( @@ -178,7 +177,8 @@ class MonkeyPatch: name: str, value: object, raising: bool = ..., - ) -> None: ... + ) -> None: + ... def setattr( self, @@ -320,10 +320,8 @@ class MonkeyPatch: if not isinstance(value, str): warnings.warn( # type: ignore[unreachable] PytestWarning( - "Value of environment variable {name} type should be str, but got " - "{value!r} (type: {type}); converted to str implicitly".format( - name=name, value=value, type=type(value).__name__ - ) + f"Value of environment variable {name} type should be str, but got " + f"{value!r} (type: {type(value).__name__}); converted to str implicitly" ), stacklevel=2, ) @@ -343,7 +341,6 @@ class MonkeyPatch: def syspath_prepend(self, path) -> None: """Prepend ``path`` to ``sys.path`` list of import locations.""" - if self._savesyspath is None: self._savesyspath = sys.path[:] sys.path.insert(0, str(path)) diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 5260b003f..48f7d5841 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -1,9 +1,8 @@ # mypy: allow-untyped-defs import abc -import os -import warnings from functools import cached_property from inspect import signature +import os from pathlib import Path from typing import Any from typing import Callable @@ -20,6 +19,7 @@ from typing import Type from typing import TYPE_CHECKING from typing import TypeVar from typing import Union +import warnings import pluggy @@ -39,6 +39,7 @@ from _pytest.pathlib import commonpath from _pytest.stash import Stash from _pytest.warning_types import PytestWarning + if TYPE_CHECKING: # Imported here due to circular import. from _pytest._code.code import _TracebackStyle @@ -229,9 +230,7 @@ class Node(abc.ABC, metaclass=NodeMeta): # enforce type checks here to avoid getting a generic type error later otherwise. if not isinstance(warning, Warning): raise ValueError( - "warning must be an instance of Warning or subclass, got {!r}".format( - warning - ) + f"warning must be an instance of Warning or subclass, got {warning!r}" ) path, lineno = get_fslocation_from_item(self) assert lineno is not None @@ -326,10 +325,12 @@ class Node(abc.ABC, metaclass=NodeMeta): yield node, mark @overload - def get_closest_marker(self, name: str) -> Optional[Mark]: ... + def get_closest_marker(self, name: str) -> Optional[Mark]: + ... @overload - def get_closest_marker(self, name: str, default: Mark) -> Mark: ... + def get_closest_marker(self, name: str, default: Mark) -> Mark: + ... def get_closest_marker( self, name: str, default: Optional[Mark] = None diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index eb51e7f7e..e2a816f58 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -239,8 +239,7 @@ def importorskip( if verattr is None or Version(verattr) < Version(minversion): raise Skipped( - "module %r has __version__ %r, required is: %r" - % (modname, verattr, minversion), + f"module {modname!r} has __version__ {verattr!r}, required is: {minversion!r}", allow_module_level=True, ) return mod diff --git a/src/_pytest/pastebin.py b/src/_pytest/pastebin.py index 440d6ec5f..98ba5c9c1 100644 --- a/src/_pytest/pastebin.py +++ b/src/_pytest/pastebin.py @@ -1,16 +1,17 @@ # mypy: allow-untyped-defs """Submit failure or test session information to a pastebin service.""" -import tempfile from io import StringIO +import tempfile from typing import IO from typing import Union -import pytest from _pytest.config import Config from _pytest.config import create_terminal_writer from _pytest.config.argparsing import Parser from _pytest.stash import StashKey from _pytest.terminal import TerminalReporter +import pytest + pastebinfile_key = StashKey[IO[bytes]]() diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index 929d8c88e..1e0891153 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -1,21 +1,16 @@ # mypy: allow-untyped-defs import atexit import contextlib -import fnmatch -import importlib.util -import itertools -import os -import shutil -import sys -import types -import uuid -import warnings from enum import Enum from errno import EBADF from errno import ELOOP from errno import ENOENT from errno import ENOTDIR +import fnmatch from functools import partial +import importlib.util +import itertools +import os from os.path import expanduser from os.path import expandvars from os.path import isabs @@ -23,6 +18,9 @@ from os.path import sep from pathlib import Path from pathlib import PurePath from posixpath import sep as posix_sep +import shutil +import sys +import types from types import ModuleType from typing import Callable from typing import Dict @@ -35,11 +33,14 @@ from typing import Tuple from typing import Type from typing import TypeVar from typing import Union +import uuid +import warnings from _pytest.compat import assert_never from _pytest.outcomes import skip from _pytest.warning_types import PytestWarning + LOCK_TIMEOUT = 60 * 60 * 24 * 3 @@ -102,9 +103,7 @@ def on_rm_rf_error( if func not in (os.open,): warnings.warn( PytestWarning( - "(rm_rf) unknown function {} when removing {}:\n{}: {}".format( - func, path, type(exc), exc - ) + f"(rm_rf) unknown function {func} when removing {path}:\n{type(exc)}: {exc}" ) ) return False @@ -243,7 +242,7 @@ def make_numbered_dir(root: Path, prefix: str, mode: int = 0o700) -> Path: else: raise OSError( "could not create numbered dir with prefix " - "{prefix} in {root} after 10 tries".format(prefix=prefix, root=root) + f"{prefix} in {root} after 10 tries" ) diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index ae8709e5e..5190bc35a 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -5,19 +5,19 @@ PYTEST_DONT_REWRITE """ import collections.abc import contextlib +from fnmatch import fnmatch import gc import importlib +from io import StringIO import locale import os +from pathlib import Path import platform import re import shutil import subprocess import sys import traceback -from fnmatch import fnmatch -from io import StringIO -from pathlib import Path from typing import Any from typing import Callable from typing import Dict @@ -70,6 +70,7 @@ from _pytest.reports import TestReport from _pytest.tmpdir import TempPathFactory from _pytest.warning_types import PytestWarning + if TYPE_CHECKING: import pexpect @@ -187,7 +188,7 @@ class LsofFdLeakChecker: "*** After:", *(str(f) for f in lines2), "***** %s FD leakage detected" % len(leaked_files), - "*** function %s:%s: %s " % item.location, + "*** function {}:{}: {} ".format(*item.location), "See issue #2366", ] item.warn(PytestWarning("\n".join(error))) @@ -244,7 +245,8 @@ class RecordedHookCall: if TYPE_CHECKING: # The class has undetermined attributes, this tells mypy about it. - def __getattr__(self, key: str): ... + def __getattr__(self, key: str): + ... @final @@ -325,13 +327,15 @@ class HookRecorder: def getreports( self, names: "Literal['pytest_collectreport']", - ) -> Sequence[CollectReport]: ... + ) -> Sequence[CollectReport]: + ... @overload def getreports( self, names: "Literal['pytest_runtest_logreport']", - ) -> Sequence[TestReport]: ... + ) -> Sequence[TestReport]: + ... @overload def getreports( @@ -340,7 +344,8 @@ class HookRecorder: "pytest_collectreport", "pytest_runtest_logreport", ), - ) -> Sequence[Union[CollectReport, TestReport]]: ... + ) -> Sequence[Union[CollectReport, TestReport]]: + ... def getreports( self, @@ -372,14 +377,12 @@ class HookRecorder: values.append(rep) if not values: raise ValueError( - "could not find test report matching %r: " - "no test reports at all!" % (inamepart,) + f"could not find test report matching {inamepart!r}: " + "no test reports at all!" ) if len(values) > 1: raise ValueError( - "found 2 or more testreports matching {!r}: {}".format( - inamepart, values - ) + f"found 2 or more testreports matching {inamepart!r}: {values}" ) return values[0] @@ -387,13 +390,15 @@ class HookRecorder: def getfailures( self, names: "Literal['pytest_collectreport']", - ) -> Sequence[CollectReport]: ... + ) -> Sequence[CollectReport]: + ... @overload def getfailures( self, names: "Literal['pytest_runtest_logreport']", - ) -> Sequence[TestReport]: ... + ) -> Sequence[TestReport]: + ... @overload def getfailures( @@ -402,7 +407,8 @@ class HookRecorder: "pytest_collectreport", "pytest_runtest_logreport", ), - ) -> Sequence[Union[CollectReport, TestReport]]: ... + ) -> Sequence[Union[CollectReport, TestReport]]: + ... def getfailures( self, @@ -801,7 +807,6 @@ class Pytester: The first created file. Examples: - .. code-block:: python pytester.makefile(".txt", "line1", "line2") @@ -855,7 +860,6 @@ class Pytester: existing files. Examples: - .. code-block:: python def test_something(pytester): @@ -875,7 +879,6 @@ class Pytester: existing files. Examples: - .. code-block:: python def test_something(pytester): @@ -1265,9 +1268,7 @@ class Pytester: for item in items: if item.name == funcname: return item - assert 0, "{!r} item not found in module:\n{}\nitems: {}".format( - funcname, source, items - ) + assert 0, f"{funcname!r} item not found in module:\n{source}\nitems: {items}" def getitems(self, source: Union[str, "os.PathLike[str]"]) -> List[Item]: """Return all test items collected from the module. @@ -1426,10 +1427,7 @@ class Pytester: def handle_timeout() -> None: __tracebackhide__ = True - timeout_message = ( - "{seconds} second timeout expired running:" - " {command}".format(seconds=timeout, command=cmdargs) - ) + timeout_message = f"{timeout} second timeout expired running: {cmdargs}" popen.kill() popen.wait() diff --git a/src/_pytest/python.py b/src/_pytest/python.py index d069d7038..d0d744733 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -1,18 +1,17 @@ # mypy: allow-untyped-defs """Python test discovery, setup and run of test functions.""" import abc +from collections import Counter +from collections import defaultdict import dataclasses import enum import fnmatch +from functools import partial import inspect import itertools import os -import types -import warnings -from collections import Counter -from collections import defaultdict -from functools import partial from pathlib import Path +import types from typing import Any from typing import Callable from typing import Dict @@ -29,6 +28,7 @@ from typing import Sequence from typing import Set from typing import Tuple from typing import Union +import warnings import _pytest from _pytest import fixtures @@ -81,6 +81,7 @@ from _pytest.warning_types import PytestCollectionWarning from _pytest.warning_types import PytestReturnNotNoneWarning from _pytest.warning_types import PytestUnhandledCoroutineWarning + _PYTEST_DIR = Path(_pytest.__file__).parent @@ -263,8 +264,8 @@ def pytest_pycollect_makeitem( elif getattr(obj, "__test__", True): if is_generator(obj): res: Function = Function.from_parent(collector, name=name) - reason = "yield tests were removed in pytest 4.0 - {name} will be ignored".format( - name=name + reason = ( + f"yield tests were removed in pytest 4.0 - {name} will be ignored" ) res.add_marker(MARK_GEN.xfail(run=False, reason=reason)) res.warn(PytestCollectionWarning(reason)) @@ -524,12 +525,12 @@ def importtestmodule( except ImportPathMismatchError as e: raise nodes.Collector.CollectError( "import file mismatch:\n" - "imported module %r has this __file__ attribute:\n" - " %s\n" + "imported module {!r} has this __file__ attribute:\n" + " {}\n" "which is not the same as the test file we want to collect:\n" - " %s\n" + " {}\n" "HINT: remove __pycache__ / .pyc files and/or use a " - "unique basename for your test file modules" % e.args + "unique basename for your test file modules".format(*e.args) ) from e except ImportError as e: exc_info = ExceptionInfo.from_current() @@ -542,10 +543,10 @@ def importtestmodule( ) formatted_tb = str(exc_repr) raise nodes.Collector.CollectError( - "ImportError while importing test module '{path}'.\n" + f"ImportError while importing test module '{path}'.\n" "Hint: make sure your test modules/packages have valid Python names.\n" "Traceback:\n" - "{traceback}".format(path=path, traceback=formatted_tb) + f"{formatted_tb}" ) from e except skip.Exception as e: if e.allow_module_level: @@ -765,9 +766,8 @@ class Class(PyCollector): assert self.parent is not None self.warn( PytestCollectionWarning( - "cannot collect test class %r because it has a " - "__init__ constructor (from: %s)" - % (self.obj.__name__, self.parent.nodeid) + f"cannot collect test class {self.obj.__name__!r} because it has a " + f"__init__ constructor (from: {self.parent.nodeid})" ) ) return [] @@ -775,9 +775,8 @@ class Class(PyCollector): assert self.parent is not None self.warn( PytestCollectionWarning( - "cannot collect test class %r because it has a " - "__new__ constructor (from: %s)" - % (self.obj.__name__, self.parent.nodeid) + f"cannot collect test class {self.obj.__name__!r} because it has a " + f"__new__ constructor (from: {self.parent.nodeid})" ) ) return [] @@ -1432,17 +1431,14 @@ class Metafunc: for arg in indirect: if arg not in argnames: fail( - "In {}: indirect fixture '{}' doesn't exist".format( - self.function.__name__, arg - ), + f"In {self.function.__name__}: indirect fixture '{arg}' doesn't exist", pytrace=False, ) arg_directness[arg] = "indirect" else: fail( - "In {func}: expected Sequence or boolean for indirect, got {type}".format( - type=type(indirect).__name__, func=self.function.__name__ - ), + f"In {self.function.__name__}: expected Sequence or boolean" + f" for indirect, got {type(indirect).__name__}", pytrace=False, ) return arg_directness @@ -1464,9 +1460,7 @@ class Metafunc: if arg not in self.fixturenames: if arg in default_arg_names: fail( - "In {}: function already takes an argument '{}' with a default value".format( - func_name, arg - ), + f"In {func_name}: function already takes an argument '{arg}' with a default value", pytrace=False, ) else: diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index f398902ff..bd3de2897 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -1,10 +1,10 @@ # mypy: allow-untyped-defs -import math -import pprint from collections.abc import Collection from collections.abc import Sized from decimal import Decimal +import math from numbers import Complex +import pprint from types import TracebackType from typing import Any from typing import Callable @@ -27,6 +27,7 @@ import _pytest._code from _pytest.compat import STRING_TYPES from _pytest.outcomes import fail + if TYPE_CHECKING: from numpy import ndarray @@ -238,9 +239,7 @@ class ApproxMapping(ApproxBase): with numeric values (the keys can be anything).""" def __repr__(self) -> str: - return "approx({!r})".format( - {k: self._approx_scalar(v) for k, v in self.expected.items()} - ) + return f"approx({({k: self._approx_scalar(v) for k, v in self.expected.items()})!r})" def _repr_compare(self, other_side: Mapping[object, float]) -> List[str]: import math @@ -315,9 +314,7 @@ class ApproxSequenceLike(ApproxBase): seq_type = type(self.expected) if seq_type not in (tuple, list): seq_type = list - return "approx({!r})".format( - seq_type(self._approx_scalar(x) for x in self.expected) - ) + return f"approx({seq_type(self._approx_scalar(x) for x in self.expected)!r})" def _repr_compare(self, other_side: Sequence[float]) -> List[str]: import math @@ -697,7 +694,6 @@ def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase: ``approx`` falls back to strict equality for nonnumeric types instead of raising ``TypeError``. """ - # Delegate the comparison to a class that knows how to deal with the type # of the expected value (e.g. int, float, list, dict, numpy.array, etc). # @@ -779,7 +775,8 @@ def raises( expected_exception: Union[Type[E], Tuple[Type[E], ...]], *, match: Optional[Union[str, Pattern[str]]] = ..., -) -> "RaisesContext[E]": ... +) -> "RaisesContext[E]": + ... @overload @@ -788,7 +785,8 @@ def raises( # noqa: F811 func: Callable[..., Any], *args: Any, **kwargs: Any, -) -> _pytest._code.ExceptionInfo[E]: ... +) -> _pytest._code.ExceptionInfo[E]: + ... def raises( # noqa: F811 @@ -837,10 +835,10 @@ def raises( # noqa: F811 The ``match`` argument searches the formatted exception string, which includes any `PEP-678 `__ ``__notes__``: - >>> with pytest.raises(ValueError, match=r'had a note added'): # doctest: +SKIP - ... e = ValueError("value must be 42") - ... e.add_note("had a note added") - ... raise e + >>> with pytest.raises(ValueError, match=r"had a note added"): # doctest: +SKIP + ... e = ValueError("value must be 42") + ... e.add_note("had a note added") + ... raise e The context manager produces an :class:`ExceptionInfo` object which can be used to inspect the details of the captured exception:: @@ -855,7 +853,7 @@ def raises( # noqa: F811 Given that ``pytest.raises`` matches subclasses, be wary of using it to match :class:`Exception` like this:: with pytest.raises(Exception): # Careful, this will catch ANY exception raised. - some_function() + some_function() Because :class:`Exception` is the base class of almost all exceptions, it is easy for this to hide real bugs, where the user wrote this expecting a specific exception, but some other exception is being diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index e8c8aff8b..ddd4c9a7b 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -1,8 +1,7 @@ # mypy: allow-untyped-defs """Record warnings during test function execution.""" -import re -import warnings from pprint import pformat +import re from types import TracebackType from typing import Any from typing import Callable @@ -17,11 +16,13 @@ from typing import Tuple from typing import Type from typing import TypeVar from typing import Union +import warnings from _pytest.deprecated import check_ispytest from _pytest.fixtures import fixture from _pytest.outcomes import fail + T = TypeVar("T") @@ -41,13 +42,15 @@ def recwarn() -> Generator["WarningsRecorder", None, None]: @overload def deprecated_call( *, match: Optional[Union[str, Pattern[str]]] = ... -) -> "WarningsRecorder": ... +) -> "WarningsRecorder": + ... @overload def deprecated_call( # noqa: F811 func: Callable[..., T], *args: Any, **kwargs: Any -) -> T: ... +) -> T: + ... def deprecated_call( # noqa: F811 @@ -89,7 +92,8 @@ def warns( expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]] = ..., *, match: Optional[Union[str, Pattern[str]]] = ..., -) -> "WarningsChecker": ... +) -> "WarningsChecker": + ... @overload @@ -98,7 +102,8 @@ def warns( # noqa: F811 func: Callable[..., T], *args: Any, **kwargs: Any, -) -> T: ... +) -> T: + ... def warns( # noqa: F811 diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index 44eaec908..7cdb70e32 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -1,7 +1,7 @@ # mypy: allow-untyped-defs import dataclasses -import os from io import StringIO +import os from pprint import pprint from typing import Any from typing import cast @@ -37,6 +37,7 @@ from _pytest.nodes import Collector from _pytest.nodes import Item from _pytest.outcomes import skip + if TYPE_CHECKING: from _pytest.runner import CallInfo @@ -46,7 +47,7 @@ def getworkerinfoline(node): return node._workerinfocache except AttributeError: d = node.workerinfo - ver = "%s.%s.%s" % d["version_info"][:3] + ver = "{}.{}.{}".format(*d["version_info"][:3]) node._workerinfocache = s = "[{}] {} -- Python {} {}".format( d["id"], d["sysplatform"], ver, d["executable"] ) @@ -71,7 +72,8 @@ class BaseReport: if TYPE_CHECKING: # Can have arbitrary fields given to __init__(). - def __getattr__(self, key: str) -> Any: ... + def __getattr__(self, key: str) -> Any: + ... def toterminal(self, out: TerminalWriter) -> None: if hasattr(self, "node"): @@ -313,9 +315,7 @@ class TestReport(BaseReport): self.__dict__.update(extra) def __repr__(self) -> str: - return "<{} {!r} when={!r} outcome={!r}>".format( - self.__class__.__name__, self.nodeid, self.when, self.outcome - ) + return f"<{self.__class__.__name__} {self.nodeid!r} when={self.when!r} outcome={self.outcome!r}>" @classmethod def from_item_and_call(cls, item: Item, call: "CallInfo[None]") -> "TestReport": @@ -430,9 +430,7 @@ class CollectReport(BaseReport): return (self.fspath, None, self.fspath) def __repr__(self) -> str: - return "".format( - self.nodeid, len(self.result), self.outcome - ) + return f"" class CollectErrorRepr(TerminalRepr): @@ -444,7 +442,7 @@ class CollectErrorRepr(TerminalRepr): def pytest_report_to_serializable( - report: Union[CollectReport, TestReport] + report: Union[CollectReport, TestReport], ) -> Optional[Dict[str, Any]]: if isinstance(report, (TestReport, CollectReport)): data = report._to_json() @@ -476,7 +474,7 @@ def _report_to_json(report: BaseReport) -> Dict[str, Any]: """ def serialize_repr_entry( - entry: Union[ReprEntry, ReprEntryNative] + entry: Union[ReprEntry, ReprEntryNative], ) -> Dict[str, Any]: data = dataclasses.asdict(entry) for key, value in data.items(): @@ -608,9 +606,9 @@ def _report_kwargs_from_json(reportdict: Dict[str, Any]) -> Dict[str, Any]: description, ) ) - exception_info: Union[ExceptionChainRepr, ReprExceptionInfo] = ( - ExceptionChainRepr(chain) - ) + exception_info: Union[ + ExceptionChainRepr, ReprExceptionInfo + ] = ExceptionChainRepr(chain) else: exception_info = ReprExceptionInfo( reprtraceback=reprtraceback, diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 44fded69b..d25fdb738 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -37,6 +37,7 @@ from _pytest.outcomes import OutcomeException from _pytest.outcomes import Skipped from _pytest.outcomes import TEST_OUTCOME + if sys.version_info[:2] < (3, 11): from exceptiongroup import BaseExceptionGroup @@ -94,8 +95,7 @@ def pytest_terminal_summary(terminalreporter: "TerminalReporter") -> None: if verbose < 2 and rep.duration < durations_min: tr.write_line("") tr.write_line( - "(%s durations < %gs hidden. Use -vv to show these durations.)" - % (len(dlist) - i, durations_min) + f"({len(dlist) - i} durations < {durations_min:g}s hidden. Use -vv to show these durations.)" ) break tr.write_line(f"{rep.duration:02.2f}s {rep.when:<8} {rep.nodeid}") diff --git a/src/_pytest/scope.py b/src/_pytest/scope.py index d15c12705..2c6e23208 100644 --- a/src/_pytest/scope.py +++ b/src/_pytest/scope.py @@ -13,6 +13,7 @@ from functools import total_ordering from typing import Literal from typing import Optional + _ScopeName = Literal["session", "package", "module", "class", "function"] diff --git a/src/_pytest/setuponly.py b/src/_pytest/setuponly.py index 0f1045806..c87de1e32 100644 --- a/src/_pytest/setuponly.py +++ b/src/_pytest/setuponly.py @@ -2,7 +2,6 @@ from typing import Generator from typing import Optional from typing import Union -import pytest from _pytest._io.saferepr import saferepr from _pytest.config import Config from _pytest.config import ExitCode @@ -10,6 +9,7 @@ from _pytest.config.argparsing import Parser from _pytest.fixtures import FixtureDef from _pytest.fixtures import SubRequest from _pytest.scope import Scope +import pytest def pytest_addoption(parser: Parser) -> None: @@ -74,7 +74,7 @@ def _show_fixture_action( scope_indent = list(reversed(Scope)).index(fixturedef._scope) tw.write(" " * 2 * scope_indent) tw.write( - "{step} {scope} {fixture}".format( + "{step} {scope} {fixture}".format( # noqa: UP032 (Readability) step=msg.ljust(8), # align the output to TEARDOWN scope=fixturedef.scope[0].upper(), fixture=fixturedef.argname, diff --git a/src/_pytest/setupplan.py b/src/_pytest/setupplan.py index 1a4ebdd99..13c0df84e 100644 --- a/src/_pytest/setupplan.py +++ b/src/_pytest/setupplan.py @@ -1,12 +1,12 @@ from typing import Optional from typing import Union -import pytest from _pytest.config import Config from _pytest.config import ExitCode from _pytest.config.argparsing import Parser from _pytest.fixtures import FixtureDef from _pytest.fixtures import SubRequest +import pytest def pytest_addoption(parser: Parser) -> None: diff --git a/src/_pytest/skipping.py b/src/_pytest/skipping.py index 617f7bb85..4799ae649 100644 --- a/src/_pytest/skipping.py +++ b/src/_pytest/skipping.py @@ -1,11 +1,11 @@ # mypy: allow-untyped-defs """Support for skip/xfail functions and markers.""" +from collections.abc import Mapping import dataclasses import os import platform import sys import traceback -from collections.abc import Mapping from typing import Generator from typing import Optional from typing import Tuple @@ -105,9 +105,7 @@ def evaluate_condition(item: Item, mark: Mark, condition: object) -> Tuple[bool, ): if not isinstance(dictionary, Mapping): raise ValueError( - "pytest_markeval_namespace() needs to return a dict, got {!r}".format( - dictionary - ) + f"pytest_markeval_namespace() needs to return a dict, got {dictionary!r}" ) globals_.update(dictionary) if hasattr(item, "obj"): diff --git a/src/_pytest/stash.py b/src/_pytest/stash.py index 7b111981b..e61d75b95 100644 --- a/src/_pytest/stash.py +++ b/src/_pytest/stash.py @@ -5,6 +5,7 @@ from typing import Generic from typing import TypeVar from typing import Union + __all__ = ["Stash", "StashKey"] diff --git a/src/_pytest/stepwise.py b/src/_pytest/stepwise.py index 74ad9dbd4..3ebebc288 100644 --- a/src/_pytest/stepwise.py +++ b/src/_pytest/stepwise.py @@ -2,12 +2,13 @@ from typing import List from typing import Optional from typing import TYPE_CHECKING -import pytest from _pytest import nodes from _pytest.config import Config from _pytest.config.argparsing import Parser from _pytest.main import Session from _pytest.reports import TestReport +import pytest + if TYPE_CHECKING: from _pytest.cacheprovider import Cache diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 42b85b56d..62dafe703 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -4,16 +4,15 @@ This is a good source for looking at the various reporting hooks. """ import argparse +from collections import Counter import dataclasses import datetime +from functools import partial import inspect +from pathlib import Path import platform import sys import textwrap -import warnings -from collections import Counter -from functools import partial -from pathlib import Path from typing import Any from typing import Callable from typing import ClassVar @@ -31,16 +30,17 @@ from typing import TextIO from typing import Tuple from typing import TYPE_CHECKING from typing import Union +import warnings import pluggy -import _pytest._version from _pytest import nodes from _pytest import timing from _pytest._code import ExceptionInfo from _pytest._code.code import ExceptionRepr from _pytest._io import TerminalWriter from _pytest._io.wcwidth import wcswidth +import _pytest._version from _pytest.assertion.util import running_on_ci from _pytest.config import _PluggyPlugin from _pytest.config import Config @@ -55,6 +55,7 @@ from _pytest.reports import BaseReport from _pytest.reports import CollectReport from _pytest.reports import TestReport + if TYPE_CHECKING: from _pytest.main import Session @@ -671,8 +672,8 @@ class TerminalReporter: return f" [ {collected} / {collected} ]" else: if collected: - return " [{:3d}%]".format( - len(self._progress_nodeids_reported) * 100 // collected + return ( + f" [{len(self._progress_nodeids_reported) * 100 // collected:3d}%]" ) return " [100%]" @@ -757,9 +758,7 @@ class TerminalReporter: if pypy_version_info: verinfo = ".".join(map(str, pypy_version_info[:3])) msg += f"[pypy-{verinfo}-{pypy_version_info[3]}]" - msg += ", pytest-{}, pluggy-{}".format( - _pytest._version.version, pluggy.__version__ - ) + msg += f", pytest-{_pytest._version.version}, pluggy-{pluggy.__version__}" if ( self.verbosity > 0 or self.config.option.debug @@ -1464,7 +1463,7 @@ def _plugin_nameversions(plugininfo) -> List[str]: values: List[str] = [] for plugin, dist in plugininfo: # Gets us name and version! - name = "{dist.project_name}-{dist.version}".format(dist=dist) + name = f"{dist.project_name}-{dist.version}" # Questionable convenience, but it keeps things short. if name.startswith("pytest-"): name = name[7:] diff --git a/src/_pytest/threadexception.py b/src/_pytest/threadexception.py index 0b5902d66..09faf661b 100644 --- a/src/_pytest/threadexception.py +++ b/src/_pytest/threadexception.py @@ -1,12 +1,12 @@ import threading import traceback -import warnings from types import TracebackType from typing import Any from typing import Callable from typing import Generator from typing import Optional from typing import Type +import warnings import pytest diff --git a/src/_pytest/timing.py b/src/_pytest/timing.py index 096a67768..0541dc8e0 100644 --- a/src/_pytest/timing.py +++ b/src/_pytest/timing.py @@ -10,4 +10,5 @@ from time import perf_counter from time import sleep from time import time + __all__ = ["perf_counter", "sleep", "time"] diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index 75391adbc..1cb9fbbe0 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -2,10 +2,10 @@ """Support for providing temporary directories to test functions.""" import dataclasses import os -import re -import tempfile from pathlib import Path +import re from shutil import rmtree +import tempfile from typing import Any from typing import Dict from typing import final @@ -32,6 +32,7 @@ from _pytest.nodes import Item from _pytest.reports import TestReport from _pytest.stash import StashKey + tmppath_result_key = StashKey[Dict[str, bool]]() RetentionType = Literal["all", "failed", "none"] @@ -268,7 +269,6 @@ def tmp_path( The returned object is a :class:`pathlib.Path` object. """ - path = _mk_tmp(request, tmp_path_factory) yield path diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index 3bdb0610e..29a53ad5c 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -15,7 +15,6 @@ from typing import TYPE_CHECKING from typing import Union import _pytest._code -import pytest from _pytest.compat import getimfunc from _pytest.compat import is_async_function from _pytest.config import hookimpl @@ -30,6 +29,8 @@ from _pytest.python import Class from _pytest.python import Function from _pytest.python import Module from _pytest.runner import CallInfo +import pytest + if TYPE_CHECKING: import unittest @@ -203,7 +204,9 @@ class TestCaseFunction(Function): # Unwrap potential exception info (see twisted trial support below). rawexcinfo = getattr(rawexcinfo, "_rawexcinfo", rawexcinfo) try: - excinfo = _pytest._code.ExceptionInfo[BaseException].from_exc_info(rawexcinfo) # type: ignore[arg-type] + excinfo = _pytest._code.ExceptionInfo[BaseException].from_exc_info( + rawexcinfo # type: ignore[arg-type] + ) # Invoke the attributes to trigger storing the traceback # trial causes some issue there. excinfo.value @@ -223,7 +226,7 @@ class TestCaseFunction(Function): except BaseException: fail( "ERROR: Unknown Incompatible Exception " - "representation:\n%r" % (rawexcinfo,), + f"representation:\n{rawexcinfo!r}", pytrace=False, ) except KeyboardInterrupt: @@ -348,9 +351,7 @@ def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> None: # handled internally, and doesn't reach here. unittest = sys.modules.get("unittest") if ( - unittest - and call.excinfo - and isinstance(call.excinfo.value, unittest.SkipTest) # type: ignore[attr-defined] + unittest and call.excinfo and isinstance(call.excinfo.value, unittest.SkipTest) # type: ignore[attr-defined] ): excinfo = call.excinfo call2 = CallInfo[None].from_call( diff --git a/src/_pytest/unraisableexception.py b/src/_pytest/unraisableexception.py index 8c0a2d9ae..f649267ab 100644 --- a/src/_pytest/unraisableexception.py +++ b/src/_pytest/unraisableexception.py @@ -1,12 +1,12 @@ import sys import traceback -import warnings from types import TracebackType from typing import Any from typing import Callable from typing import Generator from typing import Optional from typing import Type +import warnings import pytest diff --git a/src/_pytest/warning_types.py b/src/_pytest/warning_types.py index 6c109b03f..a5884f295 100644 --- a/src/_pytest/warning_types.py +++ b/src/_pytest/warning_types.py @@ -1,12 +1,12 @@ import dataclasses import inspect -import warnings from types import FunctionType from typing import Any from typing import final from typing import Generic from typing import Type from typing import TypeVar +import warnings class PytestWarning(UserWarning): @@ -73,11 +73,7 @@ class PytestExperimentalApiWarning(PytestWarning, FutureWarning): @classmethod def simple(cls, apiname: str) -> "PytestExperimentalApiWarning": - return cls( - "{apiname} is an experimental api that may change over time".format( - apiname=apiname - ) - ) + return cls(f"{apiname} is an experimental api that may change over time") @final diff --git a/src/_pytest/warnings.py b/src/_pytest/warnings.py index de4b0830f..22590892f 100644 --- a/src/_pytest/warnings.py +++ b/src/_pytest/warnings.py @@ -1,18 +1,18 @@ # mypy: allow-untyped-defs -import sys -import warnings from contextlib import contextmanager +import sys from typing import Generator from typing import Literal from typing import Optional +import warnings -import pytest from _pytest.config import apply_warning_filters from _pytest.config import Config from _pytest.config import parse_warning_filter from _pytest.main import Session from _pytest.nodes import Item from _pytest.terminal import TerminalReporter +import pytest def pytest_configure(config: Config) -> None: diff --git a/src/py.py b/src/py.py index c99790336..d1c39d203 100644 --- a/src/py.py +++ b/src/py.py @@ -6,6 +6,7 @@ import sys import _pytest._py.error as error import _pytest._py.path as path + sys.modules["py.error"] = error sys.modules["py.path"] = path diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py index 449cb39b8..20829aa58 100644 --- a/src/pytest/__init__.py +++ b/src/pytest/__init__.py @@ -81,6 +81,7 @@ from _pytest.warning_types import PytestUnknownMarkWarning from _pytest.warning_types import PytestUnraisableExceptionWarning from _pytest.warning_types import PytestWarning + set_trace = __pytestPDB.set_trace diff --git a/src/pytest/__main__.py b/src/pytest/__main__.py index 9e08e3ebc..e4cb67d5d 100644 --- a/src/pytest/__main__.py +++ b/src/pytest/__main__.py @@ -2,5 +2,6 @@ import pytest + if __name__ == "__main__": raise SystemExit(pytest.console_main()) diff --git a/testing/_py/test_local.py b/testing/_py/test_local.py index b97080ed0..57c953f17 100644 --- a/testing/_py/test_local.py +++ b/testing/_py/test_local.py @@ -4,8 +4,8 @@ import multiprocessing import os import sys import time -import warnings from unittest import mock +import warnings from py import error from py.path import local @@ -183,7 +183,7 @@ class CommonFSTests: def test_listdir_filter(self, path1): p = path1.listdir(lambda x: x.check(dir=1)) assert path1.join("sampledir") in p - assert not path1.join("samplefile") in p + assert path1.join("samplefile") not in p def test_listdir_sorted(self, path1): p = path1.listdir(lambda x: x.check(basestarts="sample"), sort=True) @@ -203,7 +203,7 @@ class CommonFSTests: for i in path1.visit(None, lambda x: x.basename != "sampledir"): lst.append(i.relto(path1)) assert "sampledir" in lst - assert not path1.sep.join(["sampledir", "otherfile"]) in lst + assert path1.sep.join(["sampledir", "otherfile"]) not in lst @pytest.mark.parametrize( "fil", diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 64e65f313..0264aa288 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -6,10 +6,10 @@ import subprocess import sys import types -import pytest from _pytest.config import ExitCode from _pytest.pathlib import symlink_or_skip from _pytest.pytester import Pytester +import pytest def prepend_pythonpath(*dirs) -> str: diff --git a/testing/code/test_code.py b/testing/code/test_code.py index a59af4a49..57ab4cdfd 100644 --- a/testing/code/test_code.py +++ b/testing/code/test_code.py @@ -4,13 +4,13 @@ import sys from types import FrameType from unittest import mock -import pytest from _pytest._code import Code from _pytest._code import ExceptionInfo from _pytest._code import Frame from _pytest._code import Source from _pytest._code.code import ExceptionChainRepr from _pytest._code.code import ReprFuncArgs +import pytest def test_ne() -> None: diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index 36625cfb5..846e23b2a 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -4,16 +4,15 @@ from __future__ import annotations import importlib import io import operator +from pathlib import Path import queue import re import sys import textwrap -from pathlib import Path from typing import Any from typing import TYPE_CHECKING import _pytest._code -import pytest from _pytest._code.code import ExceptionChainRepr from _pytest._code.code import ExceptionInfo from _pytest._code.code import FormattedExcinfo @@ -23,6 +22,8 @@ from _pytest.pathlib import bestrelpath from _pytest.pathlib import import_path from _pytest.pytester import LineMatcher from _pytest.pytester import Pytester +import pytest + if TYPE_CHECKING: from _pytest._code.code import _TracebackStyle @@ -1173,9 +1174,7 @@ raise ValueError() "funcargs": funcargs, "tbfilter": tbfilter, }, - id="style={},showlocals={},funcargs={},tbfilter={}".format( - style, showlocals, funcargs, tbfilter - ), + id=f"style={style},showlocals={showlocals},funcargs={funcargs},tbfilter={tbfilter}", ) for style in ["long", "short", "line", "no", "native", "value", "auto"] for showlocals in (True, False) @@ -1339,7 +1338,7 @@ raise ValueError() """ raise_suffix = " from None" if mode == "from_none" else "" mod = importasmod( - """ + f""" def f(): try: g() @@ -1347,9 +1346,7 @@ raise ValueError() raise AttributeError(){raise_suffix} def g(): raise ValueError() - """.format( - raise_suffix=raise_suffix - ) + """ ) excinfo = pytest.raises(AttributeError, mod.f) r = excinfo.getrepr(style="long", chain=mode != "explicit_suppress") @@ -1361,9 +1358,7 @@ raise ValueError() assert tw_mock.lines[2] == " try:" assert tw_mock.lines[3] == " g()" assert tw_mock.lines[4] == " except Exception:" - assert tw_mock.lines[5] == "> raise AttributeError(){}".format( - raise_suffix - ) + assert tw_mock.lines[5] == f"> raise AttributeError(){raise_suffix}" assert tw_mock.lines[6] == "E AttributeError" assert tw_mock.lines[7] == "" line = tw_mock.get_write_msg(8) @@ -1394,7 +1389,7 @@ raise ValueError() """ exc_handling_code = " from e" if reason == "cause" else "" mod = importasmod( - """ + f""" def f(): try: g() @@ -1402,9 +1397,7 @@ raise ValueError() raise RuntimeError('runtime problem'){exc_handling_code} def g(): raise ValueError('invalid value') - """.format( - exc_handling_code=exc_handling_code - ) + """ ) with pytest.raises(RuntimeError) as excinfo: diff --git a/testing/conftest.py b/testing/conftest.py index 0912285d8..b7e2d6111 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -5,9 +5,10 @@ import sys from typing import Generator from typing import List -import pytest from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import Pytester +import pytest + if sys.gettrace(): diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 752ccc91b..52752d4e8 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -1,7 +1,7 @@ # mypy: allow-untyped-defs -import pytest from _pytest import deprecated from _pytest.pytester import Pytester +import pytest from pytest import PytestDeprecationWarning diff --git a/testing/example_scripts/acceptance/fixture_mock_integration.py b/testing/example_scripts/acceptance/fixture_mock_integration.py index d607e959d..36e711f40 100644 --- a/testing/example_scripts/acceptance/fixture_mock_integration.py +++ b/testing/example_scripts/acceptance/fixture_mock_integration.py @@ -4,6 +4,7 @@ from unittest import mock import pytest + config = {"mykey": "ORIGINAL"} diff --git a/testing/example_scripts/perf_examples/collect_stats/generate_folders.py b/testing/example_scripts/perf_examples/collect_stats/generate_folders.py index 3d8e77e88..17085e50b 100644 --- a/testing/example_scripts/perf_examples/collect_stats/generate_folders.py +++ b/testing/example_scripts/perf_examples/collect_stats/generate_folders.py @@ -2,6 +2,7 @@ import argparse import pathlib + HERE = pathlib.Path(__file__).parent TEST_CONTENT = (HERE / "template_test.py").read_bytes() diff --git a/testing/example_scripts/unittest/test_unittest_asyncio.py b/testing/example_scripts/unittest/test_unittest_asyncio.py index f0c394ee3..a82ddaebc 100644 --- a/testing/example_scripts/unittest/test_unittest_asyncio.py +++ b/testing/example_scripts/unittest/test_unittest_asyncio.py @@ -2,6 +2,7 @@ from typing import List from unittest import IsolatedAsyncioTestCase + teardowns: List[None] = [] diff --git a/testing/example_scripts/unittest/test_unittest_asynctest.py b/testing/example_scripts/unittest/test_unittest_asynctest.py index a5a0ec22c..b3f03e325 100644 --- a/testing/example_scripts/unittest/test_unittest_asynctest.py +++ b/testing/example_scripts/unittest/test_unittest_asynctest.py @@ -5,6 +5,7 @@ from typing import List import asynctest + teardowns: List[None] = [] diff --git a/testing/io/test_pprint.py b/testing/io/test_pprint.py index 3432c63f6..15fe66112 100644 --- a/testing/io/test_pprint.py +++ b/testing/io/test_pprint.py @@ -1,16 +1,16 @@ -import textwrap from collections import ChainMap from collections import Counter from collections import defaultdict from collections import deque from collections import OrderedDict from dataclasses import dataclass +import textwrap from types import MappingProxyType from types import SimpleNamespace from typing import Any -import pytest from _pytest._io.pprint import PrettyPrinter +import pytest @dataclass diff --git a/testing/io/test_saferepr.py b/testing/io/test_saferepr.py index 4091747d5..eefa3f72b 100644 --- a/testing/io/test_saferepr.py +++ b/testing/io/test_saferepr.py @@ -1,8 +1,8 @@ # mypy: allow-untyped-defs -import pytest from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr_unlimited +import pytest def test_simple_repr(): @@ -59,9 +59,7 @@ def test_exceptions() -> None: obj = BrokenRepr(BrokenReprException("omg even worse")) s2 = saferepr(obj) assert s2 == ( - "<[unpresentable exception ({!s}) raised in repr()] BrokenRepr object at 0x{:x}>".format( - exp_exc, id(obj) - ) + f"<[unpresentable exception ({exp_exc!s}) raised in repr()] BrokenRepr object at 0x{id(obj):x}>" ) @@ -99,14 +97,12 @@ def test_baseexception(): baseexc_str = BaseException("__str__") obj = BrokenObj(RaisingOnStrRepr([BaseException])) assert saferepr(obj) == ( - "<[unpresentable exception ({!r}) " - "raised in repr()] BrokenObj object at 0x{:x}>".format(baseexc_str, id(obj)) + f"<[unpresentable exception ({baseexc_str!r}) " + f"raised in repr()] BrokenObj object at 0x{id(obj):x}>" ) obj = BrokenObj(RaisingOnStrRepr([RaisingOnStrRepr([BaseException])])) assert saferepr(obj) == ( - "<[{!r} raised in repr()] BrokenObj object at 0x{:x}>".format( - baseexc_str, id(obj) - ) + f"<[{baseexc_str!r} raised in repr()] BrokenObj object at 0x{id(obj):x}>" ) with pytest.raises(KeyboardInterrupt): diff --git a/testing/io/test_terminalwriter.py b/testing/io/test_terminalwriter.py index 0b83b5531..afa8d5cae 100644 --- a/testing/io/test_terminalwriter.py +++ b/testing/io/test_terminalwriter.py @@ -1,17 +1,18 @@ # mypy: allow-untyped-defs import io import os +from pathlib import Path import re import shutil import sys -from pathlib import Path from typing import Generator from typing import Optional from unittest import mock -import pytest from _pytest._io import terminalwriter from _pytest.monkeypatch import MonkeyPatch +import pytest + # These tests were initially copied from py 1.8.1. diff --git a/testing/io/test_wcwidth.py b/testing/io/test_wcwidth.py index 7cc74df5d..9ed2a6d3c 100644 --- a/testing/io/test_wcwidth.py +++ b/testing/io/test_wcwidth.py @@ -1,6 +1,6 @@ -import pytest from _pytest._io.wcwidth import wcswidth from _pytest._io.wcwidth import wcwidth +import pytest @pytest.mark.parametrize( diff --git a/testing/logging/test_fixture.py b/testing/logging/test_fixture.py index f4912aecc..2e16913f0 100644 --- a/testing/logging/test_fixture.py +++ b/testing/logging/test_fixture.py @@ -3,9 +3,10 @@ import logging from typing import Iterator -import pytest from _pytest.logging import caplog_records_key from _pytest.pytester import Pytester +import pytest + logger = logging.getLogger(__name__) sublogger = logging.getLogger(__name__ + ".baz") diff --git a/testing/logging/test_reporting.py b/testing/logging/test_reporting.py index f6ac9c3b2..7ef334950 100644 --- a/testing/logging/test_reporting.py +++ b/testing/logging/test_reporting.py @@ -4,12 +4,12 @@ import os import re from typing import cast -import pytest from _pytest.capture import CaptureManager from _pytest.config import ExitCode from _pytest.fixtures import FixtureRequest from _pytest.pytester import Pytester from _pytest.terminal import TerminalReporter +import pytest def test_nothing_logged(pytester: Pytester) -> None: @@ -177,13 +177,11 @@ def test_teardown_logging(pytester: Pytester) -> None: def test_log_cli_enabled_disabled(pytester: Pytester, enabled: bool) -> None: msg = "critical message logged by test" pytester.makepyfile( - """ + f""" import logging def test_log_cli(): - logging.critical("{}") - """.format( - msg - ) + logging.critical("{msg}") + """ ) if enabled: pytester.makeini( @@ -710,13 +708,11 @@ def test_log_file_ini(pytester: Pytester) -> None: log_file = str(pytester.path.joinpath("pytest.log")) pytester.makeini( - """ + f""" [pytest] - log_file={} + log_file={log_file} log_file_level=WARNING - """.format( - log_file - ) + """ ) pytester.makepyfile( """ @@ -749,13 +745,11 @@ def test_log_file_ini_level(pytester: Pytester) -> None: log_file = str(pytester.path.joinpath("pytest.log")) pytester.makeini( - """ + f""" [pytest] - log_file={} + log_file={log_file} log_file_level = INFO - """.format( - log_file - ) + """ ) pytester.makepyfile( """ @@ -788,13 +782,11 @@ def test_log_file_unicode(pytester: Pytester) -> None: log_file = str(pytester.path.joinpath("pytest.log")) pytester.makeini( - """ + f""" [pytest] - log_file={} + log_file={log_file} log_file_level = INFO - """.format( - log_file - ) + """ ) pytester.makepyfile( """\ @@ -832,8 +824,8 @@ def test_live_logging_suspends_capture( is installed. """ import contextlib - import logging from functools import partial + import logging from _pytest.logging import _LiveLoggingStreamHandler @@ -924,13 +916,11 @@ def test_collection_logging_to_file(pytester: Pytester) -> None: log_file = str(pytester.path.joinpath("pytest.log")) pytester.makeini( - """ + f""" [pytest] - log_file={} + log_file={log_file} log_file_level = INFO - """.format( - log_file - ) + """ ) pytester.makepyfile( @@ -962,14 +952,12 @@ def test_log_in_hooks(pytester: Pytester) -> None: log_file = str(pytester.path.joinpath("pytest.log")) pytester.makeini( - """ + f""" [pytest] - log_file={} + log_file={log_file} log_file_level = INFO log_cli=true - """.format( - log_file - ) + """ ) pytester.makeconftest( """ @@ -998,14 +986,12 @@ def test_log_in_runtest_logreport(pytester: Pytester) -> None: log_file = str(pytester.path.joinpath("pytest.log")) pytester.makeini( - """ + f""" [pytest] - log_file={} + log_file={log_file} log_file_level = INFO log_cli=true - """.format( - log_file - ) + """ ) pytester.makeconftest( """ @@ -1039,19 +1025,17 @@ def test_log_set_path(pytester: Pytester) -> None: """ ) pytester.makeconftest( - """ + f""" import os import pytest @pytest.hookimpl(wrapper=True, tryfirst=True) def pytest_runtest_setup(item): config = item.config logging_plugin = config.pluginmanager.get_plugin("logging-plugin") - report_file = os.path.join({}, item._request.node.name) + report_file = os.path.join({repr(report_dir_base)}, item._request.node.name) logging_plugin.set_log_path(report_file) return (yield) - """.format( - repr(report_dir_base) - ) + """ ) pytester.makepyfile( """ diff --git a/testing/python/approx.py b/testing/python/approx.py index 132855b04..079667bd0 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -1,18 +1,19 @@ # mypy: allow-untyped-defs -import operator from contextlib import contextmanager from decimal import Decimal from fractions import Fraction from math import sqrt +import operator from operator import eq from operator import ne from typing import Optional -import pytest from _pytest.pytester import Pytester from _pytest.python_api import _recursive_sequence_map +import pytest from pytest import approx + inf, nan = float("inf"), float("nan") @@ -38,9 +39,7 @@ def mocked_doctest_runner(monkeypatch): class MyDocTestRunner(doctest.DocTestRunner): def report_failure(self, out, test, example, got): raise AssertionError( - "'{}' evaluates to '{}', not '{}'".format( - example.source.strip(), got.strip(), example.want.strip() - ) + f"'{example.source.strip()}' evaluates to '{got.strip()}', not '{example.want.strip()}'" ) return MyDocTestRunner() diff --git a/testing/python/collect.py b/testing/python/collect.py index 683b62812..4d1f97b34 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -6,7 +6,6 @@ from typing import Any from typing import Dict import _pytest._code -import pytest from _pytest.config import ExitCode from _pytest.main import Session from _pytest.monkeypatch import MonkeyPatch @@ -14,6 +13,7 @@ from _pytest.nodes import Collector from _pytest.pytester import Pytester from _pytest.python import Class from _pytest.python import Function +import pytest class TestModule: @@ -54,13 +54,11 @@ class TestModule: monkeypatch.syspath_prepend(str(root1)) p.write_text( textwrap.dedent( - """\ + f"""\ import x456 def test(): - assert x456.__file__.startswith({!r}) - """.format( - str(root2) - ) + assert x456.__file__.startswith({str(root2)!r}) + """ ), encoding="utf-8", ) diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index d232742ea..62f25cb0b 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -1,10 +1,9 @@ # mypy: allow-untyped-defs import os +from pathlib import Path import sys import textwrap -from pathlib import Path -import pytest from _pytest.compat import getfuncargnames from _pytest.config import ExitCode from _pytest.fixtures import deduplicate_names @@ -13,6 +12,7 @@ from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import get_public_names from _pytest.pytester import Pytester from _pytest.python import Function +import pytest def test_getfuncargnames_functions(): @@ -1288,7 +1288,7 @@ class TestFixtureUsages: @pytest.mark.parametrize("scope", ["function", "session"]) def test_parameters_without_eq_semantics(self, scope, pytester: Pytester) -> None: pytester.makepyfile( - """ + f""" class NoEq1: # fails on `a == b` statement def __eq__(self, _): raise RuntimeError @@ -1310,9 +1310,7 @@ class TestFixtureUsages: def test2(no_eq): pass - """.format( - scope=scope - ) + """ ) result = pytester.runpytest() result.stdout.fnmatch_lines(["*4 passed*"]) @@ -2199,7 +2197,7 @@ class TestAutouseManagement: pass def test_check(): assert values == ["new1", "new2", "fin2", "fin1"] - """ + """ # noqa: UP031 (python syntax issues) % locals() ) reprec = pytester.inline_run("-s") @@ -3087,8 +3085,8 @@ class TestFixtureMarker: pass def test_other(): pass - """ - % {"scope": scope} + """ # noqa: UP031 (python syntax issues) + % {"scope": scope} # noqa: UP031 (python syntax issues) ) reprec = pytester.inline_run("-lvs") reprec.assertoutcome(passed=3) @@ -3287,7 +3285,7 @@ class TestRequestScopeAccess: assert request.config def test_func(): pass - """ + """ # noqa: UP031 (python syntax issues) % (scope, ok.split(), error.split()) ) reprec = pytester.inline_run("-l") @@ -3308,7 +3306,7 @@ class TestRequestScopeAccess: assert request.config def test_func(arg): pass - """ + """ # noqa: UP031 (python syntax issues) % (scope, ok.split(), error.split()) ) reprec = pytester.inline_run() diff --git a/testing/python/integration.py b/testing/python/integration.py index 3ff92d92f..a6c14ece4 100644 --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -1,9 +1,9 @@ # mypy: allow-untyped-defs -import pytest from _pytest._code import getfslineno from _pytest.fixtures import getfixturemarker from _pytest.pytester import Pytester from _pytest.python import Function +import pytest def test_wrapped_getfslineno() -> None: diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 8f41975f3..e132725e1 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -14,7 +14,6 @@ from typing import Sequence from typing import Tuple from typing import Union -import pytest from _pytest import fixtures from _pytest import python from _pytest.compat import getfuncargnames @@ -24,6 +23,8 @@ from _pytest.pytester import Pytester from _pytest.python import Function from _pytest.python import IdMaker from _pytest.scope import Scope +import pytest + # import hypothesis # from hypothesis import strategies @@ -1941,7 +1942,7 @@ class TestMarkersWithParametrization: @pytest.mark.parametrize("strict", [True, False]) def test_xfail_passing_is_xpass(self, pytester: Pytester, strict: bool) -> None: - s = """ + s = f""" import pytest m = pytest.mark.xfail("sys.version_info > (0, 0, 0)", reason="some bug", strict={strict}) @@ -1953,9 +1954,7 @@ class TestMarkersWithParametrization: ]) def test_increment(n, expected): assert n + 1 == expected - """.format( - strict=strict - ) + """ pytester.makepyfile(s) reprec = pytester.inline_run() passed, failed = (2, 1) if strict else (3, 0) @@ -2006,7 +2005,7 @@ class TestMarkersWithParametrization: @pytest.mark.parametrize("strict", [True, False]) def test_parametrize_marked_value(self, pytester: Pytester, strict: bool) -> None: - s = """ + s = f""" import pytest @pytest.mark.parametrize(("n", "expected"), [ @@ -2021,9 +2020,7 @@ class TestMarkersWithParametrization: ]) def test_increment(n, expected): assert n + 1 == expected - """.format( - strict=strict - ) + """ pytester.makepyfile(s) reprec = pytester.inline_run() passed, failed = (0, 2) if strict else (2, 0) diff --git a/testing/python/raises.py b/testing/python/raises.py index 369a66a9f..35d7f1a44 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -2,9 +2,9 @@ import re import sys -import pytest from _pytest.outcomes import Failed from _pytest.pytester import Pytester +import pytest class TestRaises: diff --git a/testing/test_argcomplete.py b/testing/test_argcomplete.py index 6813d8f13..0c41c0286 100644 --- a/testing/test_argcomplete.py +++ b/testing/test_argcomplete.py @@ -1,10 +1,11 @@ # mypy: allow-untyped-defs +from pathlib import Path import subprocess import sys -from pathlib import Path -import pytest from _pytest.monkeypatch import MonkeyPatch +import pytest + # Test for _argcomplete but not specific for any application. diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 640890e9a..f8196efea 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -9,14 +9,14 @@ from typing import Optional import attr -import _pytest.assertion as plugin -import pytest from _pytest import outcomes +import _pytest.assertion as plugin from _pytest.assertion import truncate from _pytest.assertion import util from _pytest.config import Config as _Config from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import Pytester +import pytest def mock_config(verbose: int = 0, assertion_override: Optional[int] = None): @@ -182,11 +182,9 @@ class TestImportHookInstallation: """ plugins = '"ham"' if mode == "str" else '["ham"]' contents = { - "conftest.py": """ + "conftest.py": f""" pytest_plugins = {plugins} - """.format( - plugins=plugins - ), + """, "ham.py": """ import pytest """, @@ -854,9 +852,7 @@ class TestAssert_reprcompare: assert "raised in repr" in expl[0] assert expl[2:] == [ "(pytest_assertion plugin: representation of details failed:" - " {}:{}: ValueError: 42.".format( - __file__, A.__repr__.__code__.co_firstlineno + 1 - ), + f" {__file__}:{A.__repr__.__code__.co_firstlineno + 1}: ValueError: 42.", " Probably an object has a faulty __repr__.)", ] @@ -1399,7 +1395,6 @@ class TestTruncateExplanation: def test_full_output_truncated(self, monkeypatch, pytester: Pytester) -> None: """Test against full runpytest() output.""" - line_count = 7 line_len = 100 expected_truncated_lines = 2 diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 0d1f9854b..6e1a9d54e 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -1,17 +1,16 @@ # mypy: allow-untyped-defs import ast import errno +from functools import partial import glob import importlib import marshal import os +from pathlib import Path import py_compile import stat import sys import textwrap -import zipfile -from functools import partial -from pathlib import Path from typing import cast from typing import Dict from typing import Generator @@ -20,9 +19,9 @@ from typing import Mapping from typing import Optional from typing import Set from unittest import mock +import zipfile import _pytest._code -import pytest from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE from _pytest.assertion import util from _pytest.assertion.rewrite import _get_assertion_exprs @@ -36,6 +35,7 @@ from _pytest.config import Config from _pytest.config import ExitCode from _pytest.pathlib import make_numbered_dir from _pytest.pytester import Pytester +import pytest def rewrite(src: str) -> ast.Module: @@ -781,11 +781,10 @@ class TestRewriteOnImport: f.close() z.chmod(256) pytester.makepyfile( - """ + f""" import sys - sys.path.append(%r) + sys.path.append({z_fn!r}) import test_gum.test_lizard""" - % (z_fn,) ) assert pytester.runpytest().ret == ExitCode.NO_TESTS_COLLECTED @@ -1857,10 +1856,10 @@ class TestAssertionPass: result.assert_outcomes(passed=1) +# fmt: off @pytest.mark.parametrize( ("src", "expected"), ( - # fmt: off pytest.param(b"", {}, id="trivial"), pytest.param( b"def x(): assert 1\n", @@ -1937,9 +1936,9 @@ class TestAssertionPass: {1: "5"}, id="no newline at end of file", ), - # fmt: on ), ) +# fmt: on def test_get_assertion_exprs(src, expected) -> None: assert _get_assertion_exprs(src) == expected @@ -2035,9 +2034,7 @@ class TestPyCacheDir: assert test_foo_pyc.is_file() # normal file: not touched by pytest, normal cache tag - bar_init_pyc = get_cache_dir(bar_init) / "__init__.{cache_tag}.pyc".format( - cache_tag=sys.implementation.cache_tag - ) + bar_init_pyc = get_cache_dir(bar_init) / f"__init__.{sys.implementation.cache_tag}.pyc" assert bar_init_pyc.is_file() diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index 73f5687d3..c020b77f9 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -1,15 +1,16 @@ # mypy: allow-untyped-defs import os -import shutil from pathlib import Path +import shutil from typing import Generator from typing import List -import pytest from _pytest.config import ExitCode from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import Pytester from _pytest.tmpdir import TempPathFactory +import pytest + pytest_plugins = ("pytester",) @@ -134,12 +135,10 @@ class TestNewAPI: def test_custom_rel_cache_dir(self, pytester: Pytester) -> None: rel_cache_dir = os.path.join("custom_cache_dir", "subdir") pytester.makeini( - """ + f""" [pytest] - cache_dir = {cache_dir} - """.format( - cache_dir=rel_cache_dir - ) + cache_dir = {rel_cache_dir} + """ ) pytester.makepyfile(test_errored="def test_error():\n assert False") pytester.runpytest() @@ -151,12 +150,10 @@ class TestNewAPI: tmp = tmp_path_factory.mktemp("tmp") abs_cache_dir = tmp / "custom_cache_dir" pytester.makeini( - """ + f""" [pytest] - cache_dir = {cache_dir} - """.format( - cache_dir=abs_cache_dir - ) + cache_dir = {abs_cache_dir} + """ ) pytester.makepyfile(test_errored="def test_error():\n assert False") pytester.runpytest() @@ -170,9 +167,7 @@ class TestNewAPI: """ [pytest] cache_dir = {cache_dir} - """.format( - cache_dir="$env_var" - ) + """.format(cache_dir="$env_var") ) pytester.makepyfile(test_errored="def test_error():\n assert False") pytester.runpytest() @@ -201,12 +196,10 @@ def test_cache_reportheader_external_abspath( pytester.makepyfile("def test_hello(): pass") pytester.makeini( - """ + f""" [pytest] - cache_dir = {abscache} - """.format( - abscache=external_cache - ) + cache_dir = {external_cache} + """ ) result = pytester.runpytest("-v") result.stdout.fnmatch_lines([f"cachedir: {external_cache}"]) @@ -646,13 +639,11 @@ class TestLastFailed: assert result.ret == 1 pytester.makepyfile( - """ + f""" import pytest @pytest.{mark} def test(): assert 0 - """.format( - mark=mark - ) + """ ) result = pytester.runpytest() assert result.ret == 0 diff --git a/testing/test_capture.py b/testing/test_capture.py index c49eeb673..0521c3b6b 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -1,17 +1,16 @@ # mypy: allow-untyped-defs import contextlib import io +from io import UnsupportedOperation import os import subprocess import sys import textwrap -from io import UnsupportedOperation from typing import BinaryIO from typing import cast from typing import Generator from typing import TextIO -import pytest from _pytest import capture from _pytest.capture import _get_multicapture from _pytest.capture import CaptureFixture @@ -21,6 +20,8 @@ from _pytest.capture import MultiCapture from _pytest.config import ExitCode from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import Pytester +import pytest + # note: py.io capture tests where copied from # pylib 1.4.20.dev2 (rev 13d9af95547e) @@ -503,13 +504,11 @@ class TestCaptureFixture: self, pytester: Pytester, method ) -> None: p = pytester.makepyfile( - """\ - def test_hello(cap{}): + f"""\ + def test_hello(cap{method}): print("xxx42xxx") assert 0 - """.format( - method - ) + """ ) result = pytester.runpytest(p) result.stdout.fnmatch_lines(["xxx42xxx"]) @@ -624,7 +623,7 @@ class TestCaptureFixture: self, pytester: Pytester, fixture: str, no_capture: bool ) -> None: pytester.makepyfile( - """\ + f"""\ def test_disabled({fixture}): print('captured before') with {fixture}.disabled(): @@ -634,9 +633,7 @@ class TestCaptureFixture: def test_normal(): print('test_normal executed') - """.format( - fixture=fixture - ) + """ ) args = ("-s",) if no_capture else () result = pytester.runpytest_subprocess(*args) @@ -681,7 +678,7 @@ class TestCaptureFixture: """Ensure that capsys and capfd can be used by other fixtures during setup and teardown.""" pytester.makepyfile( - """\ + f"""\ import sys import pytest @@ -703,9 +700,7 @@ class TestCaptureFixture: out, err = captured_print assert out == 'stdout contents begin\\n' assert err == 'stderr contents begin\\n' - """.format( - fixture=fixture - ) + """ ) result = pytester.runpytest_subprocess() result.stdout.fnmatch_lines(["*1 passed*"]) @@ -718,7 +713,7 @@ class TestCaptureFixture: ) -> None: """Ensure we can access setup and teardown buffers from teardown when using capsys/capfd (##3033)""" pytester.makepyfile( - """\ + f"""\ import sys import pytest import os @@ -735,9 +730,7 @@ class TestCaptureFixture: def test_a(fix): print("call out") sys.stderr.write("call err\\n") - """.format( - cap=cap - ) + """ ) reprec = pytester.inline_run() reprec.assertoutcome(passed=1) @@ -1049,16 +1042,12 @@ class TestFDCapture: pytest.raises(AssertionError, cap.suspend) assert repr(cap) == ( - "".format( - cap.targetfd_save, cap.tmpfile - ) + f"" ) # Should not crash with missing "_old". assert isinstance(cap.syscapture, capture.SysCapture) assert repr(cap.syscapture) == ( - " _state='done' tmpfile={!r}>".format( - cap.syscapture.tmpfile - ) + f" _state='done' tmpfile={cap.syscapture.tmpfile!r}>" ) def test_capfd_sys_stdout_mode(self, capfd) -> None: @@ -1199,7 +1188,6 @@ class TestTeeStdCapture(TestStdCapture): def test_capturing_error_recursive(self) -> None: r"""For TeeStdCapture since we passthrough stderr/stdout, cap1 should get all output, while cap2 should only get "cap2\n".""" - with self.getcapture() as cap1: print("cap1") with self.getcapture() as cap2: @@ -1394,28 +1382,27 @@ def test_close_and_capture_again(pytester: Pytester) -> None: def test_capturing_and_logging_fundamentals(pytester: Pytester, method: str) -> None: # here we check a fundamental feature p = pytester.makepyfile( - """ + f""" import sys, os, logging from _pytest import capture cap = capture.MultiCapture( in_=None, out=None, - err=capture.%s, + err=capture.{method}, ) cap.start_capturing() logging.warning("hello1") outerr = cap.readouterr() - print("suspend, captured %%s" %%(outerr,)) + print("suspend, captured %s" %(outerr,)) logging.warning("hello2") cap.pop_outerr_to_orig() logging.warning("hello3") outerr = cap.readouterr() - print("suspend2, captured %%s" %% (outerr,)) + print("suspend2, captured %s" % (outerr,)) """ - % (method,) ) result = pytester.runpython(p) result.stdout.fnmatch_lines( @@ -1581,16 +1568,16 @@ def test_capture_with_live_logging( # capture should work with live cli logging pytester.makepyfile( - """ + f""" import logging import sys logger = logging.getLogger(__name__) - def test_capture({0}): + def test_capture({capture_fixture}): print("hello") sys.stderr.write("world\\n") - captured = {0}.readouterr() + captured = {capture_fixture}.readouterr() assert captured.out == "hello\\n" assert captured.err == "world\\n" @@ -1598,11 +1585,9 @@ def test_capture_with_live_logging( print("next") logging.info("something") - captured = {0}.readouterr() + captured = {capture_fixture}.readouterr() assert captured.out == "next\\n" - """.format( - capture_fixture - ) + """ ) result = pytester.runpytest_subprocess("--log-cli-level=INFO") diff --git a/testing/test_collection.py b/testing/test_collection.py index f1552b6ae..8e41e0fae 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -1,13 +1,12 @@ # mypy: allow-untyped-defs import os +from pathlib import Path import pprint import shutil import sys import textwrap -from pathlib import Path from typing import List -import pytest from _pytest.config import ExitCode from _pytest.fixtures import FixtureRequest from _pytest.main import _in_venv @@ -17,6 +16,7 @@ from _pytest.nodes import Item from _pytest.pathlib import symlink_or_skip from _pytest.pytester import HookRecorder from _pytest.pytester import Pytester +import pytest def ensure_file(file_path: Path) -> Path: @@ -1282,21 +1282,19 @@ def test_collect_with_chdir_during_import(pytester: Pytester) -> None: subdir = pytester.mkdir("sub") pytester.path.joinpath("conftest.py").write_text( textwrap.dedent( - """ + f""" import os - os.chdir(%r) + os.chdir({str(subdir)!r}) """ - % (str(subdir),) ), encoding="utf-8", ) pytester.makepyfile( - """ + f""" def test_1(): import os - assert os.getcwd() == %r + assert os.getcwd() == {str(subdir)!r} """ - % (str(subdir),) ) result = pytester.runpytest() result.stdout.fnmatch_lines(["*1 passed in*"]) @@ -1639,13 +1637,11 @@ class TestImportModeImportlib: pytester.makepyfile( **{ "tests/conftest.py": "", - "tests/test_foo.py": """ + "tests/test_foo.py": f""" import sys def test_check(): assert r"{tests_dir}" not in sys.path - """.format( - tests_dir=tests_dir - ), + """, } ) result = pytester.runpytest("-v", "--import-mode=importlib") diff --git a/testing/test_compat.py b/testing/test_compat.py index 53d3df14a..4ea905354 100644 --- a/testing/test_compat.py +++ b/testing/test_compat.py @@ -1,13 +1,12 @@ # mypy: allow-untyped-defs import enum -import sys from functools import cached_property from functools import partial from functools import wraps +import sys from typing import TYPE_CHECKING from typing import Union -import pytest from _pytest.compat import _PytestWrapper from _pytest.compat import assert_never from _pytest.compat import get_real_func @@ -16,6 +15,8 @@ from _pytest.compat import safe_getattr from _pytest.compat import safe_isclass from _pytest.outcomes import OutcomeException from _pytest.pytester import Pytester +import pytest + if TYPE_CHECKING: from typing_extensions import Literal diff --git a/testing/test_config.py b/testing/test_config.py index df109e5b6..f2651dbeb 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -2,10 +2,10 @@ import dataclasses import importlib.metadata import os +from pathlib import Path import re import sys import textwrap -from pathlib import Path from typing import Any from typing import Dict from typing import List @@ -15,7 +15,6 @@ from typing import Type from typing import Union import _pytest._code -import pytest from _pytest.config import _get_plugin_specs_as_list from _pytest.config import _iter_rewritable_modules from _pytest.config import _strtobool @@ -32,6 +31,7 @@ from _pytest.config.findpaths import locate_config from _pytest.monkeypatch import MonkeyPatch from _pytest.pathlib import absolutepath from _pytest.pytester import Pytester +import pytest class TestParseIni: @@ -51,12 +51,10 @@ class TestParseIni: monkeypatch.chdir(sub) (tmp_path / filename).write_text( textwrap.dedent( - """\ + f"""\ [{section}] name = value - """.format( - section=section - ) + """ ), encoding="utf-8", ) @@ -126,12 +124,10 @@ class TestParseIni: def test_ini_names(self, pytester: Pytester, name, section) -> None: pytester.path.joinpath(name).write_text( textwrap.dedent( - """ + f""" [{section}] minversion = 3.36 - """.format( - section=section - ) + """ ), encoding="utf-8", ) @@ -865,7 +861,6 @@ class TestConfigAPI: """Tests the default values for configuration based on config type """ - pytester.makeconftest( """ def pytest_addoption(parser): @@ -1676,11 +1671,9 @@ class TestOverrideIniArgs: section = "[pytest]" if name != "setup.cfg" else "[tool:pytest]" pytester.path.joinpath(name).write_text( textwrap.dedent( - """ + f""" {section} - custom = 1.0""".format( - section=section - ) + custom = 1.0""" ), encoding="utf-8", ) @@ -1845,8 +1838,8 @@ class TestOverrideIniArgs: result = pytester.runpytest("cache_dir=ignored") result.stderr.fnmatch_lines( [ - "%s: error: argument -o/--override-ini: expected one argument (via addopts config)" - % (pytester._request.config._parser.optparser.prog,) + f"{pytester._request.config._parser.optparser.prog}: error: " + f"argument -o/--override-ini: expected one argument (via addopts config)" ] ) assert result.ret == _pytest.config.ExitCode.USAGE_ERROR @@ -1934,8 +1927,8 @@ def test_help_and_version_after_argument_error(pytester: Pytester) -> None: result.stderr.fnmatch_lines( [ "ERROR: usage: *", - "%s: error: argument --invalid-option-should-allow-for-help: expected one argument" - % (pytester._request.config._parser.optparser.prog,), + f"{pytester._request.config._parser.optparser.prog}: error: " + f"argument --invalid-option-should-allow-for-help: expected one argument", ] ) # Does not display full/default help. @@ -2094,7 +2087,6 @@ class TestPytestPluginsVariable: self, pytester: Pytester, use_pyargs: bool ) -> None: """When using --pyargs, do not emit the warning about non-top-level conftest warnings (#4039, #4044)""" - files = { "src/pkg/__init__.py": "", "src/pkg/conftest.py": "", @@ -2109,9 +2101,7 @@ class TestPytestPluginsVariable: args = ("--pyargs", "pkg") if use_pyargs else () res = pytester.runpytest(*args) assert res.ret == (0 if use_pyargs else 2) - msg = ( - "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported" - ) + msg = "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported" if use_pyargs: assert msg not in res.stdout.str() else: diff --git a/testing/test_conftest.py b/testing/test_conftest.py index 33d2e38dc..bb74fa75d 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -1,7 +1,7 @@ # mypy: allow-untyped-defs import os -import textwrap from pathlib import Path +import textwrap from typing import cast from typing import Dict from typing import Generator @@ -10,13 +10,13 @@ from typing import Optional from typing import Sequence from typing import Union -import pytest from _pytest.config import ExitCode from _pytest.config import PytestPluginManager from _pytest.monkeypatch import MonkeyPatch from _pytest.pathlib import symlink_or_skip from _pytest.pytester import Pytester from _pytest.tmpdir import TempPathFactory +import pytest def ConftestWithSetinitial(path) -> PytestPluginManager: diff --git a/testing/test_debugging.py b/testing/test_debugging.py index c0b99797b..02ad700a6 100644 --- a/testing/test_debugging.py +++ b/testing/test_debugging.py @@ -4,10 +4,11 @@ import sys from typing import List import _pytest._code -import pytest from _pytest.debugging import _validate_usepdb_cls from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import Pytester +import pytest + _ENVIRON_PYTHONBREAKPOINT = os.environ.get("PYTHONBREAKPOINT", "") @@ -1186,7 +1187,7 @@ def test_quit_with_swallowed_SystemExit(pytester: Pytester) -> None: def test_pdb_suspends_fixture_capturing(pytester: Pytester, fixture: str) -> None: """Using "-s" with pytest should suspend/resume fixture capturing.""" p1 = pytester.makepyfile( - """ + f""" def test_inner({fixture}): import sys @@ -1201,9 +1202,7 @@ def test_pdb_suspends_fixture_capturing(pytester: Pytester, fixture: str) -> Non out, err = {fixture}.readouterr() assert out =="out_inner_before\\nout_inner_after\\n" assert err =="err_inner_before\\nerr_inner_after\\n" - """.format( - fixture=fixture - ) + """ ) child = pytester.spawn_pytest(str(p1) + " -s") @@ -1276,7 +1275,6 @@ def test_pdbcls_via_local_module(pytester: Pytester) -> None: def test_raises_bdbquit_with_eoferror(pytester: Pytester) -> None: """It is not guaranteed that DontReadFromInput's read is called.""" - p1 = pytester.makepyfile( """ def input_without_read(*args, **kwargs): diff --git a/testing/test_doctest.py b/testing/test_doctest.py index 110e0557c..835e0e1e6 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -1,12 +1,11 @@ # mypy: allow-untyped-defs import inspect +from pathlib import Path import sys import textwrap -from pathlib import Path from typing import Callable from typing import Optional -import pytest from _pytest.doctest import _get_checker from _pytest.doctest import _is_main_py from _pytest.doctest import _is_mocked @@ -16,6 +15,7 @@ from _pytest.doctest import DoctestItem from _pytest.doctest import DoctestModule from _pytest.doctest import DoctestTextfile from _pytest.pytester import Pytester +import pytest class TestDoctests: @@ -183,19 +183,15 @@ class TestDoctests: def test_encoding(self, pytester, test_string, encoding): """Test support for doctest_encoding ini option.""" pytester.makeini( - """ + f""" [pytest] - doctest_encoding={} - """.format( - encoding - ) - ) - doctest = """ - >>> "{}" - {} - """.format( - test_string, repr(test_string) + doctest_encoding={encoding} + """ ) + doctest = f""" + >>> "{test_string}" + {repr(test_string)} + """ fn = pytester.path / "test_encoding.txt" fn.write_text(doctest, encoding=encoding) @@ -902,23 +898,19 @@ class TestLiterals: comment = "#doctest: +ALLOW_UNICODE" pytester.maketxtfile( - test_doc=""" + test_doc=f""" >>> b'12'.decode('ascii') {comment} '12' - """.format( - comment=comment - ) + """ ) pytester.makepyfile( - foo=""" + foo=f""" def foo(): ''' >>> b'12'.decode('ascii') {comment} '12' ''' - """.format( - comment=comment - ) + """ ) reprec = pytester.inline_run("--doctest-modules") reprec.assertoutcome(passed=2) @@ -941,23 +933,19 @@ class TestLiterals: comment = "#doctest: +ALLOW_BYTES" pytester.maketxtfile( - test_doc=""" + test_doc=f""" >>> b'foo' {comment} 'foo' - """.format( - comment=comment - ) + """ ) pytester.makepyfile( - foo=""" + foo=f""" def foo(): ''' >>> b'foo' {comment} 'foo' ''' - """.format( - comment=comment - ) + """ ) reprec = pytester.inline_run("--doctest-modules") reprec.assertoutcome(passed=2) @@ -1034,7 +1022,7 @@ class TestLiterals: comment = "#doctest: +NUMBER" pytester.maketxtfile( - test_doc=""" + test_doc=f""" Scalars: @@ -1086,9 +1074,7 @@ class TestLiterals: >>> 'abc' {comment} 'abc' >>> None {comment} - """.format( - comment=comment - ) + """ ) reprec = pytester.inline_run() reprec.assertoutcome(passed=1) @@ -1116,12 +1102,10 @@ class TestLiterals: ) def test_number_non_matches(self, pytester, expression, output): pytester.maketxtfile( - test_doc=""" + test_doc=f""" >>> {expression} #doctest: +NUMBER {output} - """.format( - expression=expression, output=output - ) + """ ) reprec = pytester.inline_run() reprec.assertoutcome(passed=0, failed=1) @@ -1302,15 +1286,13 @@ class TestDoctestAutoUseFixtures: See #1057 and #1100. """ pytester.makeconftest( - """ + f""" import pytest @pytest.fixture(autouse=True, scope="{scope}") def auto(request): return 99 - """.format( - scope=scope - ) + """ ) pytester.makepyfile( test_1=''' @@ -1338,15 +1320,13 @@ class TestDoctestAutoUseFixtures: See #1057 and #1100. """ pytester.makeconftest( - """ + f""" import pytest @pytest.fixture(autouse={autouse}, scope="{scope}") def auto(request): return 99 - """.format( - scope=scope, autouse=autouse - ) + """ ) if use_fixture_in_doctest: pytester.maketxtfile( @@ -1372,7 +1352,7 @@ class TestDoctestAutoUseFixtures: behave as expected when requested for a doctest item. """ pytester.makeconftest( - """ + f""" import pytest @pytest.fixture(autouse=True, scope="{scope}") @@ -1384,9 +1364,7 @@ class TestDoctestAutoUseFixtures: if "{scope}" == 'function': assert request.function is None return 99 - """.format( - scope=scope - ) + """ ) pytester.maketxtfile( test_doc=""" @@ -1409,16 +1387,14 @@ class TestDoctestNamespaceFixture: simple text file doctest """ pytester.makeconftest( - """ + f""" import pytest import contextlib @pytest.fixture(autouse=True, scope="{scope}") def add_contextlib(doctest_namespace): doctest_namespace['cl'] = contextlib - """.format( - scope=scope - ) + """ ) p = pytester.maketxtfile( """ @@ -1436,16 +1412,14 @@ class TestDoctestNamespaceFixture: simple Python file docstring doctest """ pytester.makeconftest( - """ + f""" import pytest import contextlib @pytest.fixture(autouse=True, scope="{scope}") def add_contextlib(doctest_namespace): doctest_namespace['cl'] = contextlib - """.format( - scope=scope - ) + """ ) p = pytester.makepyfile( """ @@ -1548,16 +1522,14 @@ class TestDoctestReportingOption: def test_doctest_mock_objects_dont_recurse_missbehaved(mock_module, pytester: Pytester): pytest.importorskip(mock_module) pytester.makepyfile( - """ + f""" from {mock_module} import call class Example(object): ''' >>> 1 + 1 2 ''' - """.format( - mock_module=mock_module - ) + """ ) result = pytester.runpytest("--doctest-modules") result.stdout.fnmatch_lines(["* 1 passed *"]) @@ -1572,7 +1544,7 @@ class Broken: "stop", [None, _is_mocked, lambda f: None, lambda f: False, lambda f: True] ) def test_warning_on_unwrap_of_broken_object( - stop: Optional[Callable[[object], object]] + stop: Optional[Callable[[object], object]], ) -> None: bad_instance = Broken() assert inspect.unwrap.__module__ == "inspect" @@ -1606,7 +1578,7 @@ def test_is_setup_py_different_encoding(tmp_path: Path, mod: str) -> None: setup_py = tmp_path.joinpath("setup.py") contents = ( "# -*- coding: cp1252 -*-\n" - 'from {} import setup; setup(name="foo", description="€")\n'.format(mod) + f'from {mod} import setup; setup(name="foo", description="€")\n' ) setup_py.write_bytes(contents.encode("cp1252")) assert _is_setup_py(setup_py) diff --git a/testing/test_error_diffs.py b/testing/test_error_diffs.py index 6494a44fb..f290eb167 100644 --- a/testing/test_error_diffs.py +++ b/testing/test_error_diffs.py @@ -5,8 +5,9 @@ See https://github.com/pytest-dev/pytest/issues/3333 for details. """ -import pytest from _pytest.pytester import Pytester +import pytest + TESTCASES = [ pytest.param( diff --git a/testing/test_faulthandler.py b/testing/test_faulthandler.py index b7142835d..a3363de98 100644 --- a/testing/test_faulthandler.py +++ b/testing/test_faulthandler.py @@ -2,8 +2,8 @@ import io import sys -import pytest from _pytest.pytester import Pytester +import pytest def test_enabled(pytester: Pytester) -> None: diff --git a/testing/test_findpaths.py b/testing/test_findpaths.py index 0759ba551..260b9d07c 100644 --- a/testing/test_findpaths.py +++ b/testing/test_findpaths.py @@ -3,12 +3,12 @@ import os from pathlib import Path from textwrap import dedent -import pytest from _pytest.config import UsageError from _pytest.config.findpaths import get_common_ancestor from _pytest.config.findpaths import get_dirs_from_args from _pytest.config.findpaths import is_fs_root from _pytest.config.findpaths import load_config_dict_from_file +import pytest class TestLoadConfigDictFromFile: diff --git a/testing/test_helpconfig.py b/testing/test_helpconfig.py index 272d4d290..4906ef5c8 100644 --- a/testing/test_helpconfig.py +++ b/testing/test_helpconfig.py @@ -1,7 +1,7 @@ # mypy: allow-untyped-defs -import pytest from _pytest.config import ExitCode from _pytest.pytester import Pytester +import pytest def test_version_verbose(pytester: Pytester, pytestconfig, monkeypatch) -> None: diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 88749c0c8..9b8030e7d 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -1,8 +1,8 @@ # mypy: allow-untyped-defs -import os -import platform from datetime import datetime +import os from pathlib import Path +import platform from typing import cast from typing import List from typing import Optional @@ -13,7 +13,6 @@ from xml.dom import minidom import xmlschema -import pytest from _pytest.config import Config from _pytest.junitxml import bin_xml_escape from _pytest.junitxml import LogXML @@ -23,6 +22,7 @@ from _pytest.pytester import RunResult from _pytest.reports import BaseReport from _pytest.reports import TestReport from _pytest.stash import Stash +import pytest @pytest.fixture(scope="session") @@ -1283,12 +1283,10 @@ def test_record_fixtures_without_junitxml( pytester: Pytester, fixture_name: str ) -> None: pytester.makepyfile( - """ + f""" def test_record({fixture_name}): {fixture_name}("foo", "bar") - """.format( - fixture_name=fixture_name - ) + """ ) result = pytester.runpytest() assert result.ret == 0 @@ -1336,7 +1334,7 @@ def test_record_fixtures_xunit2( """ ) pytester.makepyfile( - """ + f""" import pytest @pytest.fixture @@ -1344,9 +1342,7 @@ def test_record_fixtures_xunit2( {fixture_name}("bar", 1) def test_record({fixture_name}, other): {fixture_name}("foo", "<1"); - """.format( - fixture_name=fixture_name - ) + """ ) result, dom = run_and_parse(family=None) @@ -1356,10 +1352,8 @@ def test_record_fixtures_xunit2( "*test_record_fixtures_xunit2.py:6:*record_xml_attribute is an experimental feature" ) expected_lines = [ - "*test_record_fixtures_xunit2.py:6:*{fixture_name} is incompatible " - "with junit_family 'xunit2' (use 'legacy' or 'xunit1')".format( - fixture_name=fixture_name - ) + f"*test_record_fixtures_xunit2.py:6:*{fixture_name} is incompatible " + "with junit_family 'xunit2' (use 'legacy' or 'xunit1')" ] result.stdout.fnmatch_lines(expected_lines) @@ -1476,7 +1470,12 @@ def test_fancy_items_regression(pytester: Pytester, run_and_parse: RunAndParse) result.stdout.no_fnmatch_line("*INTERNALERROR*") - items = sorted("%(classname)s %(name)s" % x for x in dom.find_by_tag("testcase")) + items = sorted( + "%(classname)s %(name)s" % x # noqa: UP031 + # dom is a DomNode not a mapping, it's not possible to ** it. + for x in dom.find_by_tag("testcase") + ) + import pprint pprint.pprint(items) @@ -1611,13 +1610,11 @@ def test_set_suite_name( ) -> None: if suite_name: pytester.makeini( - """ + f""" [pytest] junit_suite_name={suite_name} - junit_family={family} - """.format( - suite_name=suite_name, family=xunit_family - ) + junit_family={xunit_family} + """ ) expected = suite_name else: @@ -1698,14 +1695,12 @@ def test_logging_passing_tests_disabled_does_not_log_test_output( pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makeini( - """ + f""" [pytest] junit_log_passing_tests=False junit_logging=system-out - junit_family={family} - """.format( - family=xunit_family - ) + junit_family={xunit_family} + """ ) pytester.makepyfile( """ @@ -1735,13 +1730,11 @@ def test_logging_passing_tests_disabled_logs_output_for_failing_test_issue5430( xunit_family: str, ) -> None: pytester.makeini( - """ + f""" [pytest] junit_log_passing_tests=False - junit_family={family} - """.format( - family=xunit_family - ) + junit_family={xunit_family} + """ ) pytester.makepyfile( """ diff --git a/testing/test_legacypath.py b/testing/test_legacypath.py index 72899b60d..6b933a6d9 100644 --- a/testing/test_legacypath.py +++ b/testing/test_legacypath.py @@ -1,11 +1,11 @@ # mypy: allow-untyped-defs from pathlib import Path -import pytest from _pytest.fixtures import TopRequest from _pytest.legacypath import LEGACY_PATH from _pytest.legacypath import TempdirFactory from _pytest.legacypath import Testdir +import pytest def test_item_fspath(pytester: pytest.Pytester) -> None: diff --git a/testing/test_link_resolve.py b/testing/test_link_resolve.py index c9d307f88..0461cd755 100644 --- a/testing/test_link_resolve.py +++ b/testing/test_link_resolve.py @@ -1,11 +1,11 @@ # mypy: allow-untyped-defs +from contextlib import contextmanager import os.path +from pathlib import Path +from string import ascii_lowercase import subprocess import sys import textwrap -from contextlib import contextmanager -from pathlib import Path -from string import ascii_lowercase from _pytest.pytester import Pytester diff --git a/testing/test_main.py b/testing/test_main.py index b1abf2c71..d92fa21d3 100644 --- a/testing/test_main.py +++ b/testing/test_main.py @@ -1,17 +1,17 @@ # mypy: allow-untyped-defs import argparse import os +from pathlib import Path import re import sys -from pathlib import Path from typing import Optional -import pytest from _pytest.config import ExitCode from _pytest.config import UsageError from _pytest.main import resolve_collection_argument from _pytest.main import validate_basetemp from _pytest.pytester import Pytester +import pytest @pytest.mark.parametrize( @@ -25,19 +25,17 @@ from _pytest.pytester import Pytester def test_wrap_session_notify_exception(ret_exc, pytester: Pytester) -> None: returncode, exc = ret_exc c1 = pytester.makeconftest( - """ + f""" import pytest def pytest_sessionstart(): - raise {exc}("boom") + raise {exc.__name__}("boom") def pytest_internalerror(excrepr, excinfo): returncode = {returncode!r} if returncode is not False: pytest.exit("exiting after %s..." % excinfo.typename, returncode={returncode!r}) - """.format( - returncode=returncode, exc=exc.__name__ - ) + """ ) result = pytester.runpytest() if returncode: @@ -85,13 +83,11 @@ def test_wrap_session_exit_sessionfinish( returncode: Optional[int], pytester: Pytester ) -> None: pytester.makeconftest( - """ + f""" import pytest def pytest_sessionfinish(): pytest.exit(reason="exit_pytest_sessionfinish", returncode={returncode}) - """.format( - returncode=returncode - ) + """ ) result = pytester.runpytest() if returncode: diff --git a/testing/test_mark.py b/testing/test_mark.py index e37fc6c53..4604baafd 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -5,13 +5,13 @@ from typing import List from typing import Optional from unittest import mock -import pytest from _pytest.config import ExitCode from _pytest.mark import MarkGenerator from _pytest.mark.structures import EMPTY_PARAMETERSET_OPTION from _pytest.nodes import Collector from _pytest.nodes import Node from _pytest.pytester import Pytester +import pytest class TestMark: @@ -934,12 +934,10 @@ def test_parameterset_for_parametrize_marks( ) -> None: if mark is not None: pytester.makeini( - """ + f""" [pytest] - {}={} - """.format( - EMPTY_PARAMETERSET_OPTION, mark - ) + {EMPTY_PARAMETERSET_OPTION}={mark} + """ ) config = pytester.parseconfig() @@ -959,12 +957,10 @@ def test_parameterset_for_parametrize_marks( def test_parameterset_for_fail_at_collect(pytester: Pytester) -> None: pytester.makeini( - """ + f""" [pytest] - {}=fail_at_collect - """.format( - EMPTY_PARAMETERSET_OPTION - ) + {EMPTY_PARAMETERSET_OPTION}=fail_at_collect + """ ) config = pytester.parseconfig() diff --git a/testing/test_mark_expression.py b/testing/test_mark_expression.py index f3643e7b4..a7a9cf304 100644 --- a/testing/test_mark_expression.py +++ b/testing/test_mark_expression.py @@ -1,8 +1,8 @@ from typing import Callable -import pytest from _pytest.mark.expression import Expression from _pytest.mark.expression import ParseError +import pytest def evaluate(input: str, matcher: Callable[[str], bool]) -> bool: diff --git a/testing/test_monkeypatch.py b/testing/test_monkeypatch.py index 3a1045d67..12be774be 100644 --- a/testing/test_monkeypatch.py +++ b/testing/test_monkeypatch.py @@ -1,16 +1,16 @@ # mypy: allow-untyped-defs import os +from pathlib import Path import re import sys import textwrap -from pathlib import Path from typing import Dict from typing import Generator from typing import Type -import pytest from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import Pytester +import pytest @pytest.fixture diff --git a/testing/test_nodes.py b/testing/test_nodes.py index 4efb39e03..e019f163c 100644 --- a/testing/test_nodes.py +++ b/testing/test_nodes.py @@ -1,15 +1,15 @@ # mypy: allow-untyped-defs -import re -import warnings from pathlib import Path +import re from typing import cast from typing import Type +import warnings -import pytest from _pytest import nodes from _pytest.outcomes import OutcomeException from _pytest.pytester import Pytester from _pytest.warning_types import PytestWarning +import pytest def test_node_from_parent_disallowed_arguments() -> None: @@ -39,7 +39,6 @@ def test_subclassing_both_item_and_collector_deprecated( Verifies we warn on diamond inheritance as well as correctly managing legacy inheritance constructors with missing args as found in plugins. """ - # We do not expect any warnings messages to issued during class definition. with warnings.catch_warnings(): warnings.simplefilter("error") diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index b2d547931..4678d8bdb 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -2,16 +2,16 @@ import argparse import locale import os +from pathlib import Path import shlex import subprocess import sys -from pathlib import Path -import pytest from _pytest.config import argparsing as parseopt from _pytest.config.exceptions import UsageError from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import Pytester +import pytest @pytest.fixture @@ -315,9 +315,7 @@ def test_argcomplete(pytester: Pytester, monkeypatch: MonkeyPatch) -> None: # http://stackoverflow.com/q/12589419/1307905 # so we use bash fp.write( - 'COMP_WORDBREAKS="$COMP_WORDBREAKS" {} -m pytest 8>&1 9>&2'.format( - shlex.quote(sys.executable) - ) + f'COMP_WORDBREAKS="$COMP_WORDBREAKS" {shlex.quote(sys.executable)} -m pytest 8>&1 9>&2' ) # alternative would be extended Pytester.{run(),_run(),popen()} to be able # to handle a keyword argument env that replaces os.environ in popen or @@ -335,9 +333,7 @@ def test_argcomplete(pytester: Pytester, monkeypatch: MonkeyPatch) -> None: pytest.skip("argcomplete not available") elif not result.stdout.str(): pytest.skip( - "bash provided no output on stdout, argcomplete not available? (stderr={!r})".format( - result.stderr.str() - ) + f"bash provided no output on stdout, argcomplete not available? (stderr={result.stderr.str()!r})" ) else: result.stdout.fnmatch_lines(["--funcargs", "--fulltrace"]) diff --git a/testing/test_pastebin.py b/testing/test_pastebin.py index 277454f9b..651a04da8 100644 --- a/testing/test_pastebin.py +++ b/testing/test_pastebin.py @@ -4,9 +4,9 @@ import io from typing import List from typing import Union -import pytest from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import Pytester +import pytest class TestPasteCapture: diff --git a/testing/test_pathlib.py b/testing/test_pathlib.py index 99cd48503..075259009 100644 --- a/testing/test_pathlib.py +++ b/testing/test_pathlib.py @@ -1,17 +1,16 @@ # mypy: allow-untyped-defs import errno import os.path +from pathlib import Path import pickle import sys -import unittest.mock -from pathlib import Path from textwrap import dedent from types import ModuleType from typing import Any from typing import Generator from typing import Iterator +import unittest.mock -import pytest from _pytest.monkeypatch import MonkeyPatch from _pytest.pathlib import bestrelpath from _pytest.pathlib import commonpath @@ -31,6 +30,7 @@ from _pytest.pathlib import symlink_or_skip from _pytest.pathlib import visit from _pytest.pytester import Pytester from _pytest.tmpdir import TempPathFactory +import pytest class TestFNMatcherPort: diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index ea44d8748..f68f143f4 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -5,7 +5,6 @@ import sys import types from typing import List -import pytest from _pytest.config import Config from _pytest.config import ExitCode from _pytest.config import PytestPluginManager @@ -14,6 +13,7 @@ from _pytest.main import Session from _pytest.monkeypatch import MonkeyPatch from _pytest.pathlib import import_path from _pytest.pytester import Pytester +import pytest @pytest.fixture diff --git a/testing/test_pytester.py b/testing/test_pytester.py index e35fb5294..84fb96ccb 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -6,16 +6,16 @@ import time from types import ModuleType from typing import List -import _pytest.pytester as pytester_mod -import pytest from _pytest.config import ExitCode from _pytest.config import PytestPluginManager from _pytest.monkeypatch import MonkeyPatch +import _pytest.pytester as pytester_mod from _pytest.pytester import HookRecorder from _pytest.pytester import LineMatcher from _pytest.pytester import Pytester from _pytest.pytester import SysModulesSnapshot from _pytest.pytester import SysPathsSnapshot +import pytest def test_make_hook_recorder(pytester: Pytester) -> None: @@ -705,15 +705,13 @@ def test_spawn_uses_tmphome(pytester: Pytester) -> None: pytester._monkeypatch.setenv("CUSTOMENV", "42") p1 = pytester.makepyfile( - """ + f""" import os def test(): assert os.environ["HOME"] == {tmphome!r} assert os.environ["CUSTOMENV"] == "42" - """.format( - tmphome=tmphome - ) + """ ) child = pytester.spawn_pytest(str(p1)) out = child.read() diff --git a/testing/test_python_path.py b/testing/test_python_path.py index 4054134b4..73a872568 100644 --- a/testing/test_python_path.py +++ b/testing/test_python_path.py @@ -5,8 +5,8 @@ from typing import Generator from typing import List from typing import Optional -import pytest from _pytest.pytester import Pytester +import pytest @pytest.fixture() diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index 9d46c1fc8..e2b2eb8a2 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -1,12 +1,12 @@ # mypy: allow-untyped-defs -import warnings from typing import List from typing import Optional from typing import Type +import warnings -import pytest from _pytest.pytester import Pytester from _pytest.recwarn import WarningsRecorder +import pytest def test_recwarn_stacklevel(recwarn: WarningsRecorder) -> None: diff --git a/testing/test_reports.py b/testing/test_reports.py index d65358a8f..2de5ae600 100644 --- a/testing/test_reports.py +++ b/testing/test_reports.py @@ -2,7 +2,6 @@ from typing import Sequence from typing import Union -import pytest from _pytest._code.code import ExceptionChainRepr from _pytest._code.code import ExceptionRepr from _pytest.config import Config @@ -10,6 +9,7 @@ from _pytest.pytester import Pytester from _pytest.python_api import approx from _pytest.reports import CollectReport from _pytest.reports import TestReport +import pytest class TestReportSerialization: @@ -279,7 +279,7 @@ class TestReportSerialization: ) -> None: """Check serialization/deserialization of report objects containing chained exceptions (#5786)""" pytester.makepyfile( - """ + f""" def foo(): raise ValueError('value error') def test_a(): @@ -287,18 +287,16 @@ class TestReportSerialization: foo() except ValueError as e: raise RuntimeError('runtime error') from e - if {error_during_import}: + if {report_class is CollectReport}: test_a() - """.format( - error_during_import=report_class is CollectReport - ) + """ ) reprec = pytester.inline_run() if report_class is TestReport: - reports: Union[Sequence[TestReport], Sequence[CollectReport]] = ( - reprec.getreports("pytest_runtest_logreport") - ) + reports: Union[ + Sequence[TestReport], Sequence[CollectReport] + ] = reprec.getreports("pytest_runtest_logreport") # we have 3 reports: setup/call/teardown assert len(reports) == 3 # get the call report diff --git a/testing/test_runner.py b/testing/test_runner.py index d2007647e..6b2b3105b 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -1,16 +1,15 @@ # mypy: allow-untyped-defs +from functools import partial import inspect import os +from pathlib import Path import sys import types -from functools import partial -from pathlib import Path from typing import Dict from typing import List from typing import Tuple from typing import Type -import pytest from _pytest import outcomes from _pytest import reports from _pytest import runner @@ -20,6 +19,8 @@ from _pytest.config import ExitCode from _pytest.monkeypatch import MonkeyPatch from _pytest.outcomes import OutcomeException from _pytest.pytester import Pytester +import pytest + if sys.version_info[:2] < (3, 11): from exceptiongroup import ExceptionGroup diff --git a/testing/test_runner_xunit.py b/testing/test_runner_xunit.py index dbdb51d24..8076e20bc 100644 --- a/testing/test_runner_xunit.py +++ b/testing/test_runner_xunit.py @@ -2,8 +2,8 @@ """Test correct setup/teardowns at module, class, and instance level.""" from typing import List -import pytest from _pytest.pytester import Pytester +import pytest def test_module_and_function_setup(pytester: Pytester) -> None: @@ -255,7 +255,7 @@ def test_setup_teardown_function_level_with_optional_argument( sys, "trace_setups_teardowns", trace_setups_teardowns, raising=False ) p = pytester.makepyfile( - """ + f""" import pytest import sys @@ -276,9 +276,7 @@ def test_setup_teardown_function_level_with_optional_argument( def test_method_1(self): pass def test_method_2(self): pass - """.format( - arg=arg - ) + """ ) result = pytester.inline_run(p) result.assertoutcome(passed=4) diff --git a/testing/test_scope.py b/testing/test_scope.py index 09ee1343a..1727c2ee1 100644 --- a/testing/test_scope.py +++ b/testing/test_scope.py @@ -1,7 +1,7 @@ import re -import pytest from _pytest.scope import Scope +import pytest def test_ordering() -> None: diff --git a/testing/test_session.py b/testing/test_session.py index 232e9c834..8624af478 100644 --- a/testing/test_session.py +++ b/testing/test_session.py @@ -1,8 +1,8 @@ # mypy: allow-untyped-defs -import pytest from _pytest.config import ExitCode from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import Pytester +import pytest class SessionTests: diff --git a/testing/test_setuponly.py b/testing/test_setuponly.py index 0f70383a6..8638f5a61 100644 --- a/testing/test_setuponly.py +++ b/testing/test_setuponly.py @@ -1,9 +1,9 @@ # mypy: allow-untyped-defs import sys -import pytest from _pytest.config import ExitCode from _pytest.pytester import Pytester +import pytest @pytest.fixture(params=["--setup-only", "--setup-plan", "--setup-show"], scope="module") diff --git a/testing/test_skipping.py b/testing/test_skipping.py index 346dfa44e..4391c0642 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -2,12 +2,12 @@ import sys import textwrap -import pytest from _pytest.pytester import Pytester from _pytest.runner import runtestprotocol from _pytest.skipping import evaluate_skip_marks from _pytest.skipping import evaluate_xfail_marks from _pytest.skipping import pytest_runtest_setup +import pytest class TestEvaluation: @@ -76,14 +76,13 @@ class TestEvaluation: ] for i in range(0, 2): item = pytester.getitem( - """ + f""" import pytest - %s - %s + {lines[i]} + {lines[(i + 1) % 2]} def test_func(): pass """ - % (lines[i], lines[(i + 1) % 2]) ) skipped = evaluate_skip_marks(item) assert skipped @@ -607,7 +606,7 @@ class TestXFail: @pytest.mark.xfail(raises=%s) def test_raises(): raise %s() - """ + """ # noqa: UP031 (python syntax issues) % (expected, actual) ) result = pytester.runpytest(p) @@ -909,7 +908,7 @@ class TestSkipif: @pytest.mark.skipif(%(params)s) def test_that(): assert 0 - """ + """ # noqa: UP031 (python syntax issues) % dict(params=params) ) result = pytester.runpytest(p, "-s", "-rs") @@ -935,15 +934,13 @@ class TestSkipif: self, pytester: Pytester, marker, msg1, msg2 ) -> None: pytester.makepyfile( - test_foo=""" + test_foo=f""" import pytest @pytest.mark.{marker}(False, reason='first_condition') @pytest.mark.{marker}(True, reason='second_condition') def test_foobar(): assert 1 - """.format( - marker=marker - ) + """ ) result = pytester.runpytest("-s", "-rsxX") result.stdout.fnmatch_lines( diff --git a/testing/test_stash.py b/testing/test_stash.py index 2c9df4832..e523c4e6f 100644 --- a/testing/test_stash.py +++ b/testing/test_stash.py @@ -1,6 +1,6 @@ -import pytest from _pytest.stash import Stash from _pytest.stash import StashKey +import pytest def test_stash() -> None: diff --git a/testing/test_stepwise.py b/testing/test_stepwise.py index 4aa72d024..472afea66 100644 --- a/testing/test_stepwise.py +++ b/testing/test_stepwise.py @@ -1,11 +1,11 @@ # mypy: allow-untyped-defs from pathlib import Path -import pytest from _pytest.cacheprovider import Cache from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import Pytester from _pytest.stepwise import STEPWISE_CACHE_DIR +import pytest @pytest.fixture diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 283722887..0eb50926e 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -1,11 +1,11 @@ # mypy: allow-untyped-defs """Terminal reporting of the full testing process.""" import collections +from io import StringIO import os +from pathlib import Path import sys import textwrap -from io import StringIO -from pathlib import Path from types import SimpleNamespace from typing import cast from typing import Dict @@ -14,10 +14,8 @@ from typing import Tuple import pluggy -import _pytest.config -import _pytest.terminal -import pytest from _pytest._io.wcwidth import wcswidth +import _pytest.config from _pytest.config import Config from _pytest.config import ExitCode from _pytest.monkeypatch import MonkeyPatch @@ -25,6 +23,7 @@ from _pytest.pytester import Pytester from _pytest.reports import BaseReport from _pytest.reports import CollectReport from _pytest.reports import TestReport +import _pytest.terminal from _pytest.terminal import _folded_skips from _pytest.terminal import _format_trimmed from _pytest.terminal import _get_line_with_reprcrash_message @@ -32,6 +31,8 @@ from _pytest.terminal import _get_raw_skip_reason from _pytest.terminal import _plugin_nameversions from _pytest.terminal import getreportopt from _pytest.terminal import TerminalReporter +import pytest + DistInfo = collections.namedtuple("DistInfo", ["project_name", "version"]) @@ -156,7 +157,6 @@ class TestTerminal: self, pytester: Pytester, monkeypatch: MonkeyPatch ) -> None: """Test for "collecting" being updated after 0.5s""" - pytester.makepyfile( **{ "test1.py": """ @@ -869,13 +869,7 @@ class TestTerminalFunctional: result.stdout.fnmatch_lines( [ "*===== test session starts ====*", - "platform %s -- Python %s*pytest-%s**pluggy-%s" - % ( - sys.platform, - verinfo, - pytest.__version__, - pluggy.__version__, - ), + f"platform {sys.platform} -- Python {verinfo}*pytest-{pytest.__version__}**pluggy-{pluggy.__version__}", "*test_header_trailer_info.py .*", "=* 1 passed*in *.[0-9][0-9]s *=", ] @@ -896,13 +890,7 @@ class TestTerminalFunctional: result = pytester.runpytest("--no-header") verinfo = ".".join(map(str, sys.version_info[:3])) result.stdout.no_fnmatch_line( - "platform %s -- Python %s*pytest-%s**pluggy-%s" - % ( - sys.platform, - verinfo, - pytest.__version__, - pluggy.__version__, - ) + f"platform {sys.platform} -- Python {verinfo}*pytest-{pytest.__version__}**pluggy-{pluggy.__version__}" ) if request.config.pluginmanager.list_plugin_distinfo(): result.stdout.no_fnmatch_line("plugins: *") @@ -943,12 +931,10 @@ class TestTerminalFunctional: tests = pytester.path.joinpath("tests") tests.mkdir() pytester.makepyprojecttoml( - """ + f""" [tool.pytest.ini_options] - testpaths = ['{}'] - """.format( - tests - ) + testpaths = ['{tests}'] + """ ) result = pytester.runpytest() result.stdout.fnmatch_lines( @@ -2413,7 +2399,12 @@ def test_line_with_reprcrash(monkeypatch: MonkeyPatch) -> None: __tracebackhide__ = True if msg: rep.longrepr.reprcrash.message = msg # type: ignore - actual = _get_line_with_reprcrash_message(config, rep(), DummyTerminalWriter(), {}) # type: ignore + actual = _get_line_with_reprcrash_message( + config, # type: ignore[arg-type] + rep(), # type: ignore[arg-type] + DummyTerminalWriter(), # type: ignore[arg-type] + {}, + ) assert actual == expected if actual != f"{mocked_verbose_word} {mocked_pos}": diff --git a/testing/test_threadexception.py b/testing/test_threadexception.py index fd9a091cc..99837b94e 100644 --- a/testing/test_threadexception.py +++ b/testing/test_threadexception.py @@ -1,5 +1,5 @@ -import pytest from _pytest.pytester import Pytester +import pytest @pytest.mark.filterwarnings("default::pytest.PytestUnhandledThreadExceptionWarning") diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 42df66088..331ee7da6 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -1,16 +1,15 @@ # mypy: allow-untyped-defs import dataclasses import os +from pathlib import Path import stat import sys -import warnings -from pathlib import Path from typing import Callable from typing import cast from typing import List from typing import Union +import warnings -import pytest from _pytest import pathlib from _pytest.config import Config from _pytest.monkeypatch import MonkeyPatch @@ -24,6 +23,7 @@ from _pytest.pathlib import rm_rf from _pytest.pytester import Pytester from _pytest.tmpdir import get_user from _pytest.tmpdir import TempPathFactory +import pytest def test_tmp_path_fixture(pytester: Pytester) -> None: @@ -242,12 +242,10 @@ testdata = [ def test_mktemp(pytester: Pytester, basename: str, is_ok: bool) -> None: mytemp = pytester.mkdir("mytemp") p = pytester.makepyfile( - """ + f""" def test_abs_path(tmp_path_factory): - tmp_path_factory.mktemp('{}', numbered=False) - """.format( - basename - ) + tmp_path_factory.mktemp('{basename}', numbered=False) + """ ) result = pytester.runpytest(p, "--basetemp=%s" % mytemp) @@ -338,7 +336,6 @@ def test_tmp_path_fallback_uid_not_found(pytester: Pytester) -> None: """Test that tmp_path works even if the current process's user id does not correspond to a valid user. """ - pytester.makepyfile( """ def test_some(tmp_path): diff --git a/testing/test_unittest.py b/testing/test_unittest.py index fc967d992..b5d182c14 100644 --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -3,10 +3,10 @@ import gc import sys from typing import List -import pytest from _pytest.config import ExitCode from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import Pytester +import pytest def test_simple_unittest(pytester: Pytester) -> None: @@ -353,22 +353,21 @@ def test_setup_class(pytester: Pytester) -> None: @pytest.mark.parametrize("type", ["Error", "Failure"]) def test_testcase_adderrorandfailure_defers(pytester: Pytester, type: str) -> None: pytester.makepyfile( - """ + f""" from unittest import TestCase import pytest class MyTestCase(TestCase): def run(self, result): excinfo = pytest.raises(ZeroDivisionError, lambda: 0/0) try: - result.add%s(self, excinfo._excinfo) + result.add{type}(self, excinfo._excinfo) except KeyboardInterrupt: raise except: - pytest.fail("add%s should not raise") + pytest.fail("add{type} should not raise") def test_hello(self): pass """ - % (type, type) ) result = pytester.runpytest() result.stdout.no_fnmatch_line("*should not raise*") @@ -400,14 +399,13 @@ def test_testcase_custom_exception_info(pytester: Pytester, type: str) -> None: mp.setattr(_pytest._code, 'ExceptionInfo', FakeExceptionInfo) try: excinfo = excinfo._excinfo - result.add%(type)s(self, excinfo) + result.add{type}(self, excinfo) finally: mp.undo() def test_hello(self): pass - """ - % locals() + """.format(**locals()) ) result = pytester.runpytest() result.stdout.fnmatch_lines( @@ -834,7 +832,7 @@ def test_unittest_expected_failure_for_passing_test_is_fail( @pytest.mark.parametrize("stmt", ["return", "yield"]) def test_unittest_setup_interaction(pytester: Pytester, stmt: str) -> None: pytester.makepyfile( - """ + f""" import unittest import pytest class MyTestCase(unittest.TestCase): @@ -856,9 +854,7 @@ def test_unittest_setup_interaction(pytester: Pytester, stmt: str) -> None: def test_classattr(self): assert self.__class__.hello == "world" - """.format( - stmt=stmt - ) + """ ) result = pytester.runpytest() result.stdout.fnmatch_lines(["*3 passed*"]) @@ -1063,7 +1059,7 @@ def test_usefixtures_marker_on_unittest(base, pytester: Pytester) -> None: ) pytester.makepyfile( - """ + f""" import pytest import {module} @@ -1082,9 +1078,7 @@ def test_usefixtures_marker_on_unittest(base, pytester: Pytester) -> None: assert self.fixture2 - """.format( - module=module, base=base - ) + """ ) result = pytester.runpytest("-s") @@ -1253,7 +1247,7 @@ def test_pdb_teardown_skipped_for_functions( monkeypatch.setattr(pytest, "track_pdb_teardown_skipped", tracked, raising=False) pytester.makepyfile( - """ + f""" import unittest import pytest @@ -1269,9 +1263,7 @@ def test_pdb_teardown_skipped_for_functions( def test_1(self): pass - """.format( - mark=mark - ) + """ ) result = pytester.runpytest_inprocess("--pdb") result.stdout.fnmatch_lines("* 1 skipped in *") @@ -1290,7 +1282,7 @@ def test_pdb_teardown_skipped_for_classes( monkeypatch.setattr(pytest, "track_pdb_teardown_skipped", tracked, raising=False) pytester.makepyfile( - """ + f""" import unittest import pytest @@ -1306,9 +1298,7 @@ def test_pdb_teardown_skipped_for_classes( def test_1(self): pass - """.format( - mark=mark - ) + """ ) result = pytester.runpytest_inprocess("--pdb") result.stdout.fnmatch_lines("* 1 skipped in *") diff --git a/testing/test_unraisableexception.py b/testing/test_unraisableexception.py index d255adb2b..1657cfe4a 100644 --- a/testing/test_unraisableexception.py +++ b/testing/test_unraisableexception.py @@ -1,7 +1,8 @@ import sys -import pytest from _pytest.pytester import Pytester +import pytest + PYPY = hasattr(sys, "pypy_version_info") diff --git a/testing/test_warning_types.py b/testing/test_warning_types.py index 0ea3f2976..a50d278bd 100644 --- a/testing/test_warning_types.py +++ b/testing/test_warning_types.py @@ -1,9 +1,9 @@ # mypy: allow-untyped-defs import inspect -import pytest from _pytest import warning_types from _pytest.pytester import Pytester +import pytest @pytest.mark.parametrize( @@ -43,5 +43,6 @@ def test_pytest_warnings_repr_integration_test(pytester: Pytester) -> None: def test_warn_explicit_for_annotates_errors_with_location(): with pytest.raises(Warning, match="(?m)test\n at .*python_api.py:\\d+"): warning_types.warn_explicit_for( - pytest.raises, warning_types.PytestWarning("test") # type: ignore + pytest.raises, # type: ignore[arg-type] + warning_types.PytestWarning("test"), ) diff --git a/testing/test_warnings.py b/testing/test_warnings.py index 2f0ab9b54..5a8a98015 100644 --- a/testing/test_warnings.py +++ b/testing/test_warnings.py @@ -1,14 +1,15 @@ # mypy: allow-untyped-defs import os import sys -import warnings from typing import List from typing import Optional from typing import Tuple +import warnings -import pytest from _pytest.fixtures import FixtureRequest from _pytest.pytester import Pytester +import pytest + WARNINGS_SUMMARY_HEADER = "warnings summary" @@ -20,13 +21,11 @@ def pyfile_with_warnings(pytester: Pytester, request: FixtureRequest) -> str: test_name = request.function.__name__ module_name = test_name.lstrip("test_") + "_module" test_file = pytester.makepyfile( - """ + f""" import {module_name} def test_func(): assert {module_name}.foo() == 1 - """.format( - module_name=module_name - ), + """, **{ module_name: """ import warnings @@ -437,7 +436,7 @@ class TestDeprecationWarningsByDefault: def create_file(self, pytester: Pytester, mark="") -> None: pytester.makepyfile( - """ + f""" import pytest, warnings warnings.warn(DeprecationWarning("collection")) @@ -445,9 +444,7 @@ class TestDeprecationWarningsByDefault: {mark} def test_foo(): warnings.warn(PendingDeprecationWarning("test run")) - """.format( - mark=mark - ) + """ ) @pytest.mark.parametrize("customize_filters", [True, False]) diff --git a/tox.ini b/tox.ini index b5c28029f..0ac2ff2dd 100644 --- a/tox.ini +++ b/tox.ini @@ -186,27 +186,3 @@ usedevelop = True deps = pypandoc commands = python scripts/generate-gh-release-notes.py {posargs} - -[flake8] -max-line-length = 120 -extend-ignore = - ; whitespace before ':' - E203 - ; Missing Docstrings - D100,D101,D102,D103,D104,D105,D106,D107 - ; Whitespace Issues - D202,D203,D204,D205,D209,D213 - ; Quotes Issues - D302 - ; Docstring Content Issues - D400,D401,D401,D402,D405,D406,D407,D408,D409,D410,D411,D412,D413,D414,D415,D416,D417 - ; Unused imports - F401 - - -[isort] -; This config mimics what reorder-python-imports does. -force_single_line = 1 -known_localfolder = pytest,_pytest -known_third_party = test_source,test_excinfo -force_alphabetical_sort_within_sections = 1