diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6068a2d32..26289b72f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,12 +5,12 @@ repos: - id: black args: [--safe, --quiet] - repo: https://github.com/asottile/blacken-docs - rev: v1.7.0 + rev: v1.8.0 hooks: - id: blacken-docs additional_dependencies: [black==19.10b0] - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.1.0 + rev: v3.2.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -21,7 +21,7 @@ repos: exclude: _pytest/(debugging|hookspec).py language_version: python3 - repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.2 + rev: 3.8.3 hooks: - id: flake8 language_version: python3 @@ -29,23 +29,23 @@ repos: - flake8-typing-imports==1.9.0 - flake8-docstrings==1.5.0 - repo: https://github.com/asottile/reorder_python_imports - rev: v2.3.0 + rev: v2.3.5 hooks: - id: reorder-python-imports - args: ['--application-directories=.:src', --py3-plus] + args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.4.4 + rev: v2.7.2 hooks: - id: pyupgrade - args: [--py3-plus] + args: [--py36-plus] - repo: https://github.com/asottile/setup-cfg-fmt - rev: v1.9.0 + rev: v1.11.0 hooks: - id: setup-cfg-fmt # TODO: when upgrading setup-cfg-fmt this can be removed args: [--max-py-version=3.9] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.780 # NOTE: keep this in sync with setup.cfg. + rev: v0.782 # NOTE: keep this in sync with setup.cfg. hooks: - id: mypy files: ^(src/|testing/) diff --git a/doc/en/example/assertion/failure_demo.py b/doc/en/example/assertion/failure_demo.py index a172a007e..abb9bce50 100644 --- a/doc/en/example/assertion/failure_demo.py +++ b/doc/en/example/assertion/failure_demo.py @@ -176,7 +176,7 @@ class TestRaises: def test_reinterpret_fails_with_print_for_the_fun_of_it(self): items = [1, 2, 3] - print("items is {!r}".format(items)) + print(f"items is {items!r}") a, b = items.pop() def test_some_error(self): diff --git a/doc/en/example/assertion/global_testmodule_config/conftest.py b/doc/en/example/assertion/global_testmodule_config/conftest.py index fd467f09e..7cdf18cdb 100644 --- a/doc/en/example/assertion/global_testmodule_config/conftest.py +++ b/doc/en/example/assertion/global_testmodule_config/conftest.py @@ -11,4 +11,4 @@ def pytest_runtest_setup(item): return mod = item.getparent(pytest.Module).obj if hasattr(mod, "hello"): - print("mod.hello {!r}".format(mod.hello)) + print(f"mod.hello {mod.hello!r}") diff --git a/doc/en/example/multipython.py b/doc/en/example/multipython.py index 9db6879ed..21bddcd03 100644 --- a/doc/en/example/multipython.py +++ b/doc/en/example/multipython.py @@ -26,7 +26,7 @@ class Python: def __init__(self, version, picklefile): self.pythonpath = shutil.which(version) if not self.pythonpath: - pytest.skip("{!r} not found".format(version)) + pytest.skip(f"{version!r} not found") self.picklefile = picklefile def dumps(self, obj): @@ -69,4 +69,4 @@ class Python: @pytest.mark.parametrize("obj", [42, {}, {1: 3}]) def test_basic_objects(python1, python2, obj): python1.dumps(obj) - python2.load_and_is_true("obj == {}".format(obj)) + python2.load_and_is_true(f"obj == {obj}") diff --git a/doc/en/example/nonpython/conftest.py b/doc/en/example/nonpython/conftest.py index 6e5a57092..bdcc8b762 100644 --- a/doc/en/example/nonpython/conftest.py +++ b/doc/en/example/nonpython/conftest.py @@ -40,7 +40,7 @@ class YamlItem(pytest.Item): ) def reportinfo(self): - return self.fspath, 0, "usecase: {}".format(self.name) + return self.fspath, 0, f"usecase: {self.name}" class YamlException(Exception): diff --git a/scripts/release.py b/scripts/release.py index 5e3158ab5..798e42e1f 100644 --- a/scripts/release.py +++ b/scripts/release.py @@ -17,9 +17,7 @@ def announce(version): stdout = stdout.decode("utf-8") last_version = stdout.strip() - stdout = check_output( - ["git", "log", "{}..HEAD".format(last_version), "--format=%aN"] - ) + stdout = check_output(["git", "log", f"{last_version}..HEAD", "--format=%aN"]) stdout = stdout.decode("utf-8") contributors = set(stdout.splitlines()) @@ -31,14 +29,10 @@ def announce(version): Path(__file__).parent.joinpath(template_name).read_text(encoding="UTF-8") ) - contributors_text = ( - "\n".join("* {}".format(name) for name in sorted(contributors)) + "\n" - ) + contributors_text = "\n".join(f"* {name}" for name in sorted(contributors)) + "\n" text = template_text.format(version=version, contributors=contributors_text) - target = Path(__file__).parent.joinpath( - "../doc/en/announce/release-{}.rst".format(version) - ) + target = Path(__file__).parent.joinpath(f"../doc/en/announce/release-{version}.rst") target.write_text(text, encoding="UTF-8") print(f"{Fore.CYAN}[generate.announce] {Fore.RESET}Generated {target.name}") @@ -47,7 +41,7 @@ def announce(version): lines = index_path.read_text(encoding="UTF-8").splitlines() indent = " " for index, line in enumerate(lines): - if line.startswith("{}release-".format(indent)): + if line.startswith(f"{indent}release-"): new_line = indent + target.stem if line != new_line: lines.insert(index, new_line) @@ -96,7 +90,7 @@ def pre_release(version, *, skip_check_links): if not skip_check_links: check_links() - msg = "Prepare release version {}".format(version) + msg = f"Prepare release version {version}" check_call(["git", "commit", "-a", "-m", msg]) print() diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index d6140b8dc..7054ef407 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -58,7 +58,7 @@ class Code: if not hasattr(rawcode, "co_filename"): rawcode = getrawcode(rawcode) if not isinstance(rawcode, CodeType): - raise TypeError("not a code object: {!r}".format(rawcode)) + raise TypeError(f"not a code object: {rawcode!r}") self.filename = rawcode.co_filename self.firstlineno = rawcode.co_firstlineno - 1 self.name = rawcode.co_name @@ -747,7 +747,7 @@ class FormattedExcinfo: else: str_repr = safeformat(value) # if len(str_repr) < 70 or not isinstance(value, (list, tuple, dict)): - lines.append("{:<10} = {}".format(name, str_repr)) + lines.append(f"{name:<10} = {str_repr}") # else: # self._line("%-10s =\\" % (name,)) # # XXX @@ -1056,7 +1056,7 @@ class ReprEntry(TerminalRepr): # separate indents and source lines that are not failures: we want to # highlight the code but not the indentation, which may contain markers # such as "> assert 0" - fail_marker = "{} ".format(FormattedExcinfo.fail_marker) + fail_marker = f"{FormattedExcinfo.fail_marker} " indent_size = len(fail_marker) indents = [] # type: List[str] source_lines = [] # type: List[str] @@ -1122,7 +1122,7 @@ class ReprFileLocation(TerminalRepr): if i != -1: msg = msg[:i] tw.write(self.path, bold=True, red=True) - tw.line(":{}: {}".format(self.lineno, msg)) + tw.line(f":{self.lineno}: {msg}") @attr.s(eq=False) @@ -1142,7 +1142,7 @@ class ReprFuncArgs(TerminalRepr): if self.args: linesofar = "" for name, value in self.args: - ns = "{} = {}".format(name, value) + ns = f"{name} = {value}" if len(ns) + len(linesofar) + 2 > tw.fullwidth: if linesofar: tw.line(linesofar) diff --git a/src/_pytest/_io/terminalwriter.py b/src/_pytest/_io/terminalwriter.py index 3dc25c6fe..9077d4193 100644 --- a/src/_pytest/_io/terminalwriter.py +++ b/src/_pytest/_io/terminalwriter.py @@ -97,7 +97,7 @@ class TerminalWriter: def markup(self, text: str, **markup: bool) -> str: for name in markup: if name not in self._esctable: - raise ValueError("unknown markup: {!r}".format(name)) + raise ValueError(f"unknown markup: {name!r}") if self.hasmarkup: esc = [self._esctable[name] for name, on in markup.items() if on] if esc: @@ -128,7 +128,7 @@ class TerminalWriter: # N <= (fullwidth - len(title) - 2) // (2*len(sepchar)) N = max((fullwidth - len(title) - 2) // (2 * len(sepchar)), 1) fill = sepchar * N - line = "{} {} {}".format(fill, title, fill) + line = f"{fill} {title} {fill}" else: # we want len(sepchar)*N <= fullwidth # i.e. N <= fullwidth // len(sepchar) diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 1b504115c..e23d89569 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -48,7 +48,7 @@ assertstate_key = StoreKey["AssertionState"]() # pytest caches rewritten pycs in pycache dirs -PYTEST_TAG = "{}-pytest-{}".format(sys.implementation.cache_tag, version) +PYTEST_TAG = f"{sys.implementation.cache_tag}-pytest-{version}" PYC_EXT = ".py" + (__debug__ and "c" or "o") PYC_TAIL = "." + PYTEST_TAG + PYC_EXT @@ -149,7 +149,7 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader) ok = try_makedirs(cache_dir) if not ok: write = False - state.trace("read only directory: {}".format(cache_dir)) + state.trace(f"read only directory: {cache_dir}") cache_name = fn.name[:-3] + PYC_TAIL pyc = cache_dir / cache_name @@ -157,7 +157,7 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader) # to check for a cached pyc. This may not be optimal... co = _read_pyc(fn, pyc, state.trace) if co is None: - state.trace("rewriting {!r}".format(fn)) + state.trace(f"rewriting {fn!r}") source_stat, co = _rewrite_test(fn, self.config) if write: self._writing_pyc = True @@ -166,7 +166,7 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader) finally: self._writing_pyc = False else: - state.trace("found cached rewritten pyc for {}".format(fn)) + state.trace(f"found cached rewritten pyc for {fn}") exec(co, module.__dict__) def _early_rewrite_bailout(self, name: str, state: "AssertionState") -> bool: @@ -205,20 +205,18 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader) if self._is_marked_for_rewrite(name, state): return False - state.trace("early skip of rewriting module: {}".format(name)) + state.trace(f"early skip of rewriting module: {name}") return True def _should_rewrite(self, name: str, fn: str, state: "AssertionState") -> bool: # always rewrite conftest files if os.path.basename(fn) == "conftest.py": - state.trace("rewriting conftest file: {!r}".format(fn)) + state.trace(f"rewriting conftest file: {fn!r}") return True if self.session is not None: if self.session.isinitpath(py.path.local(fn)): - state.trace( - "matched test file (was specified on cmdline): {!r}".format(fn) - ) + state.trace(f"matched test file (was specified on cmdline): {fn!r}") return True # modules not passed explicitly on the command line are only @@ -226,7 +224,7 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader) fn_path = PurePath(fn) for pat in self.fnpats: if fnmatch_ex(pat, fn_path): - state.trace("matched test file {!r}".format(fn)) + state.trace(f"matched test file {fn!r}") return True return self._is_marked_for_rewrite(name, state) @@ -237,9 +235,7 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader) except KeyError: for marked in self._must_rewrite: if name == marked or name.startswith(marked + "."): - state.trace( - "matched marked file {!r} (from {!r})".format(name, marked) - ) + state.trace(f"matched marked file {name!r} (from {marked!r})") self._marked_for_rewrite_cache[name] = True return True @@ -308,7 +304,7 @@ if sys.platform == "win32": with atomic_write(os.fspath(pyc), mode="wb", overwrite=True) as fp: _write_pyc_fp(fp, source_stat, co) except OSError as e: - state.trace("error writing pyc file at {}: {}".format(pyc, e)) + state.trace(f"error writing pyc file at {pyc}: {e}") # we ignore any failure to write the cache file # there are many reasons, permission-denied, pycache dir being a # file etc. @@ -324,20 +320,18 @@ else: source_stat: os.stat_result, pyc: Path, ) -> bool: - proc_pyc = "{}.{}".format(pyc, os.getpid()) + proc_pyc = f"{pyc}.{os.getpid()}" try: fp = open(proc_pyc, "wb") except OSError as e: - state.trace( - "error writing pyc file at {}: errno={}".format(proc_pyc, e.errno) - ) + state.trace(f"error writing pyc file at {proc_pyc}: errno={e.errno}") return False try: _write_pyc_fp(fp, source_stat, co) os.rename(proc_pyc, os.fspath(pyc)) except OSError as e: - state.trace("error writing pyc file at {}: {}".format(pyc, e)) + state.trace(f"error writing pyc file at {pyc}: {e}") # we ignore any failure to write the cache file # there are many reasons, permission-denied, pycache dir being a # file etc. @@ -377,7 +371,7 @@ def _read_pyc( size = stat_result.st_size data = fp.read(12) except OSError as e: - trace("_read_pyc({}): OSError {}".format(source, e)) + trace(f"_read_pyc({source}): OSError {e}") return None # Check for invalid or out of date pyc file. if ( @@ -390,7 +384,7 @@ def _read_pyc( try: co = marshal.load(fp) except Exception as e: - trace("_read_pyc({}): marshal.load error {}".format(source, e)) + trace(f"_read_pyc({source}): marshal.load error {e}") return None if not isinstance(co, types.CodeType): trace("_read_pyc(%s): not a code object" % source) @@ -982,7 +976,7 @@ class AssertionRewriter(ast.NodeVisitor): symbol = BINOP_MAP[binop.op.__class__] left_expr, left_expl = self.visit(binop.left) right_expr, right_expl = self.visit(binop.right) - explanation = "({} {} {})".format(left_expl, symbol, right_expl) + explanation = f"({left_expl} {symbol} {right_expl})" res = self.assign(ast.BinOp(left_expr, binop.op, right_expr)) return res, explanation @@ -1007,7 +1001,7 @@ class AssertionRewriter(ast.NodeVisitor): new_call = ast.Call(new_func, new_args, new_kwargs) res = self.assign(new_call) res_expl = self.explanation_param(self.display(res)) - outer_expl = "{}\n{{{} = {}\n}}".format(res_expl, res_expl, expl) + outer_expl = f"{res_expl}\n{{{res_expl} = {expl}\n}}" return res, outer_expl def visit_Starred(self, starred: ast.Starred) -> Tuple[ast.Starred, str]: @@ -1030,7 +1024,7 @@ class AssertionRewriter(ast.NodeVisitor): self.push_format_context() left_res, left_expl = self.visit(comp.left) if isinstance(comp.left, (ast.Compare, ast.BoolOp)): - left_expl = "({})".format(left_expl) + left_expl = f"({left_expl})" res_variables = [self.variable() for i in range(len(comp.ops))] load_names = [ast.Name(v, ast.Load()) for v in res_variables] store_names = [ast.Name(v, ast.Store()) for v in res_variables] @@ -1041,11 +1035,11 @@ class AssertionRewriter(ast.NodeVisitor): for i, op, next_operand in it: next_res, next_expl = self.visit(next_operand) if isinstance(next_operand, (ast.Compare, ast.BoolOp)): - next_expl = "({})".format(next_expl) + next_expl = f"({next_expl})" results.append(next_res) sym = BINOP_MAP[op.__class__] syms.append(ast.Str(sym)) - expl = "{} {} {}".format(left_expl, sym, next_expl) + expl = f"{left_expl} {sym} {next_expl}" expls.append(ast.Str(expl)) res_expr = ast.Compare(left_res, [op], [next_res]) self.statements.append(ast.Assign([store_names[i]], res_expr)) diff --git a/src/_pytest/assertion/truncate.py b/src/_pytest/assertion/truncate.py index c572cc744..5ba9ddca7 100644 --- a/src/_pytest/assertion/truncate.py +++ b/src/_pytest/assertion/truncate.py @@ -70,10 +70,10 @@ def _truncate_explanation( truncated_line_count += 1 # Account for the part-truncated final line msg = "...Full output truncated" if truncated_line_count == 1: - msg += " ({} line hidden)".format(truncated_line_count) + msg += f" ({truncated_line_count} line hidden)" else: - msg += " ({} lines hidden)".format(truncated_line_count) - msg += ", {}".format(USAGE_MSG) + msg += f" ({truncated_line_count} lines hidden)" + msg += f", {USAGE_MSG}" truncated_explanation.extend(["", str(msg)]) return truncated_explanation diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index 1d0e903cd..28bd13d4d 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -142,7 +142,7 @@ def assertrepr_compare(config, op: str, left: Any, right: Any) -> Optional[List[ left_repr = saferepr(left, maxsize=maxsize) right_repr = saferepr(right, maxsize=maxsize) - summary = "{} {} {}".format(left_repr, op, right_repr) + summary = f"{left_repr} {op} {right_repr}" explanation = None try: @@ -316,9 +316,7 @@ def _compare_eq_sequence( left_value = left[i] right_value = right[i] - explanation += [ - "At index {} diff: {!r} != {!r}".format(i, left_value, right_value) - ] + explanation += [f"At index {i} diff: {left_value!r} != {right_value!r}"] break if comparing_bytes: @@ -338,9 +336,7 @@ def _compare_eq_sequence( extra = saferepr(right[len_left]) if len_diff == 1: - explanation += [ - "{} contains one more item: {}".format(dir_with_more, extra) - ] + explanation += [f"{dir_with_more} contains one more item: {extra}"] else: explanation += [ "%s contains %d more items, first extra item: %s" diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 5a1070b77..23feb7fbe 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -500,7 +500,7 @@ def pytest_report_header(config: Config) -> Optional[str]: displaypath = cachedir.relative_to(config.rootpath) except ValueError: displaypath = cachedir - return "cachedir: {}".format(displaypath) + return f"cachedir: {displaypath}" return None @@ -542,5 +542,5 @@ def cacheshow(config: Config, session: Session) -> int: # print("%s/" % p.relto(basedir)) if p.is_file(): key = str(p.relative_to(basedir)) - tw.line("{} is a file of length {:d}".format(key, p.stat().st_size)) + tw.line(f"{key} is a file of length {p.stat().st_size:d}") return 0 diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index 9ba062444..bf3c98941 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -544,7 +544,7 @@ class CaptureResult(Generic[AnyStr]): return tuple(self) < tuple(other) def __repr__(self) -> str: - return "CaptureResult(out={!r}, err={!r})".format(self.out, self.err) + return f"CaptureResult(out={self.out!r}, err={self.err!r})" class MultiCapture(Generic[AnyStr]): @@ -638,7 +638,7 @@ def _get_multicapture(method: "_CaptureMethod") -> MultiCapture[str]: return MultiCapture( in_=None, out=SysCapture(1, tee=True), err=SysCapture(2, tee=True) ) - raise ValueError("unknown capturing method: {!r}".format(method)) + raise ValueError(f"unknown capturing method: {method!r}") # CaptureManager and CaptureFixture diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index a7e270859..69ff2e007 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -143,8 +143,7 @@ def getfuncargnames( parameters = signature(function).parameters except (ValueError, TypeError) as e: fail( - "Could not determine arguments of {!r}: {}".format(function, e), - pytrace=False, + f"Could not determine arguments of {function!r}: {e}", pytrace=False, ) arg_names = tuple( @@ -197,7 +196,7 @@ def get_default_arg_names(function: Callable[..., Any]) -> Tuple[str, ...]: _non_printable_ascii_translate_table = { - i: "\\x{:02x}".format(i) for i in range(128) if i not in range(32, 127) + i: f"\\x{i:02x}" for i in range(128) if i not in range(32, 127) } _non_printable_ascii_translate_table.update( {ord("\t"): "\\t", ord("\r"): "\\r", ord("\n"): "\\n"} diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 9e7cdbd00..08d37650c 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -144,9 +144,7 @@ def main( except ConftestImportFailure as e: exc_info = ExceptionInfo(e.excinfo) tw = TerminalWriter(sys.stderr) - tw.line( - "ImportError while loading conftest '{e.path}'.".format(e=e), red=True - ) + tw.line(f"ImportError while loading conftest '{e.path}'.", red=True) exc_info.traceback = exc_info.traceback.filter( filter_traceback_for_conftest_import_failure ) @@ -173,7 +171,7 @@ def main( except UsageError as e: tw = TerminalWriter(sys.stderr) for msg in e.args: - tw.line("ERROR: {}\n".format(msg), red=True) + tw.line(f"ERROR: {msg}\n", red=True) return ExitCode.USAGE_ERROR @@ -206,7 +204,7 @@ def filename_arg(path: str, optname: str) -> str: :optname: Name of the option. """ if os.path.isdir(path): - raise UsageError("{} must be a filename, given: {}".format(optname, path)) + raise UsageError(f"{optname} must be a filename, given: {path}") return path @@ -217,7 +215,7 @@ def directory_arg(path: str, optname: str) -> str: :optname: Name of the option. """ if not os.path.isdir(path): - raise UsageError("{} must be a directory, given: {}".format(optname, path)) + raise UsageError(f"{optname} must be a directory, given: {path}") return path @@ -583,7 +581,7 @@ class PytestPluginManager(PluginManager): if path and path.relto(dirpath) or path == dirpath: assert mod not in mods mods.append(mod) - self.trace("loading conftestmodule {!r}".format(mod)) + self.trace(f"loading conftestmodule {mod!r}") self.consider_conftest(mod) return mod @@ -889,7 +887,7 @@ class Config: _a = FILE_OR_DIR self._parser = Parser( - usage="%(prog)s [options] [{}] [{}] [...]".format(_a, _a), + usage=f"%(prog)s [options] [{_a}] [{_a}] [...]", processopt=self._processopt, ) self.pluginmanager = pluginmanager @@ -1191,9 +1189,7 @@ class Config: # we don't want to prevent --help/--version to work # so just let is pass and print a warning at the end self.issue_config_time_warning( - PytestConfigWarning( - "could not load initial conftests: {}".format(e.path) - ), + PytestConfigWarning(f"could not load initial conftests: {e.path}"), stacklevel=2, ) else: @@ -1227,7 +1223,7 @@ class Config: def _validate_config_options(self) -> None: for key in sorted(self._get_unknown_ini_keys()): - self._warn_or_fail_if_strict("Unknown config option: {}\n".format(key)) + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") def _validate_plugins(self) -> None: required_plugins = sorted(self.getini("required_plugins")) @@ -1362,7 +1358,7 @@ class Config: try: description, type, default = self._parser._inidict[name] except KeyError as e: - raise ValueError("unknown configuration value: {!r}".format(name)) from e + raise ValueError(f"unknown configuration value: {name!r}") from e override_value = self._get_override_ini_value(name) if override_value is None: try: @@ -1467,8 +1463,8 @@ class Config: if skip: import pytest - pytest.skip("no {!r} option found".format(name)) - raise ValueError("no option named {!r}".format(name)) from e + pytest.skip(f"no {name!r} option found") + raise ValueError(f"no option named {name!r}") from e def getvalue(self, name: str, path=None): """Deprecated, use getoption() instead.""" @@ -1501,7 +1497,7 @@ class Config: def _warn_about_skipped_plugins(self) -> None: for module_name, msg in self.pluginmanager.skipped_plugins: self.issue_config_time_warning( - PytestConfigWarning("skipped plugin {!r}: {}".format(module_name, msg)), + PytestConfigWarning(f"skipped plugin {module_name!r}: {msg}"), stacklevel=2, ) @@ -1554,7 +1550,7 @@ def _strtobool(val: str) -> bool: elif val in ("n", "no", "f", "false", "off", "0"): return False else: - raise ValueError("invalid truth value {!r}".format(val)) + raise ValueError(f"invalid truth value {val!r}") @lru_cache(maxsize=50) @@ -1568,7 +1564,7 @@ def parse_warning_filter( """ parts = arg.split(":") if len(parts) > 5: - raise warnings._OptionError("too many fields (max 5): {!r}".format(arg)) + raise warnings._OptionError(f"too many fields (max 5): {arg!r}") while len(parts) < 5: parts.append("") action_, message, category_, module, lineno_ = [s.strip() for s in parts] @@ -1584,7 +1580,7 @@ def parse_warning_filter( if lineno < 0: raise ValueError except (ValueError, OverflowError) as e: - raise warnings._OptionError("invalid lineno {!r}".format(lineno_)) from e + raise warnings._OptionError(f"invalid lineno {lineno_!r}") from e else: lineno = 0 return action, message, category, module, lineno diff --git a/src/_pytest/config/argparsing.py b/src/_pytest/config/argparsing.py index 16777587e..3ee54a552 100644 --- a/src/_pytest/config/argparsing.py +++ b/src/_pytest/config/argparsing.py @@ -188,7 +188,7 @@ class ArgumentError(Exception): def __str__(self) -> str: if self.option_id: - return "option {}: {}".format(self.option_id, self.msg) + return f"option {self.option_id}: {self.msg}" else: return self.msg @@ -389,11 +389,11 @@ class MyOptionParser(argparse.ArgumentParser): def error(self, message: str) -> "NoReturn": """Transform argparse error message into UsageError.""" - msg = "{}: error: {}".format(self.prog, message) + msg = f"{self.prog}: error: {message}" if hasattr(self._parser, "_config_source_hint"): # Type ignored because the attribute is set dynamically. - msg = "{} ({})".format(msg, self._parser._config_source_hint) # type: ignore + msg = f"{msg} ({self._parser._config_source_hint})" # type: ignore raise UsageError(self.format_usage() + msg) @@ -410,7 +410,7 @@ class MyOptionParser(argparse.ArgumentParser): if arg and arg[0] == "-": lines = ["unrecognized arguments: %s" % (" ".join(unrecognized))] for k, v in sorted(self.extra_info.items()): - lines.append(" {}: {}".format(k, v)) + lines.append(f" {k}: {v}") self.error("\n".join(lines)) getattr(parsed, FILE_OR_DIR).extend(unrecognized) return parsed diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index 2b3faf8dc..80004f468 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -35,7 +35,7 @@ def _validate_usepdb_cls(value: str) -> Tuple[str, str]: modname, classname = value.split(":") except ValueError as e: raise argparse.ArgumentTypeError( - "{!r} is not in the format 'modname:classname'".format(value) + f"{value!r} is not in the format 'modname:classname'" ) from e return (modname, classname) @@ -136,7 +136,7 @@ class pytestPDB: except Exception as exc: value = ":".join((modname, classname)) raise UsageError( - "--pdbcls: could not import {!r}: {}".format(value, exc) + f"--pdbcls: could not import {value!r}: {exc}" ) from exc else: import pdb @@ -257,7 +257,7 @@ class pytestPDB: else: capturing = cls._is_capturing(capman) if capturing == "global": - tw.sep(">", "PDB {} (IO-capturing turned off)".format(method)) + tw.sep(">", f"PDB {method} (IO-capturing turned off)") elif capturing: tw.sep( ">", @@ -265,7 +265,7 @@ class pytestPDB: % (method, capturing), ) else: - tw.sep(">", "PDB {}".format(method)) + tw.sep(">", f"PDB {method}") _pdb = cls._import_pdb_cls(capman)(**kwargs) diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index bdbbc5197..194e5e598 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -349,7 +349,7 @@ class DoctestItem(pytest.Item): ] indent = ">>>" for line in example.source.splitlines(): - lines.append("??? {} {}".format(indent, line)) + lines.append(f"??? {indent} {line}") indent = "..." if isinstance(failure, doctest.DocTestFailure): lines += checker.output_difference( diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 8aa03d121..00dfb2559 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -480,7 +480,7 @@ class FixtureRequest: """Test function object if the request has a per-function scope.""" if self.scope != "function": raise AttributeError( - "function not available in {}-scoped context".format(self.scope) + f"function not available in {self.scope}-scoped context" ) return self._pyfuncitem.obj @@ -488,9 +488,7 @@ class FixtureRequest: def cls(self): """Class (can be None) where the test function was collected.""" if self.scope not in ("class", "function"): - raise AttributeError( - "cls not available in {}-scoped context".format(self.scope) - ) + raise AttributeError(f"cls not available in {self.scope}-scoped context") clscol = self._pyfuncitem.getparent(_pytest.python.Class) if clscol: return clscol.obj @@ -509,18 +507,14 @@ class FixtureRequest: def module(self): """Python module object where the test function was collected.""" if self.scope not in ("function", "class", "module"): - raise AttributeError( - "module not available in {}-scoped context".format(self.scope) - ) + raise AttributeError(f"module not available in {self.scope}-scoped context") return self._pyfuncitem.getparent(_pytest.python.Module).obj @property def fspath(self) -> py.path.local: """The file system path of the test module which collected this test.""" if self.scope not in ("function", "class", "module", "package"): - raise AttributeError( - "module not available in {}-scoped context".format(self.scope) - ) + raise AttributeError(f"module not available in {self.scope}-scoped context") # TODO: Remove ignore once _pyfuncitem is properly typed. return self._pyfuncitem.fspath # type: ignore @@ -770,7 +764,7 @@ class SubRequest(FixtureRequest): self._fixturemanager = request._fixturemanager def __repr__(self) -> str: - return "".format(self.fixturename, self._pyfuncitem) + return f"" def addfinalizer(self, finalizer: Callable[[], object]) -> None: self._fixturedef.addfinalizer(finalizer) @@ -805,7 +799,7 @@ def scope2index(scope: str, descr: str, where: Optional[str] = None) -> int: except ValueError: fail( "{} {}got an unexpected scope value '{}'".format( - descr, "from {} ".format(where) if where else "", scope + descr, f"from {where} " if where else "", scope ), pytrace=False, ) @@ -861,7 +855,7 @@ class FixtureLookupError(LookupError): self.argname ) else: - msg = "fixture '{}' not found".format(self.argname) + msg = f"fixture '{self.argname}' not found" msg += "\n available fixtures: {}".format(", ".join(sorted(available))) msg += "\n use 'pytest --fixtures [testpath]' for help on them." @@ -895,8 +889,7 @@ class FixtureLookupErrorRepr(TerminalRepr): ) for line in lines[1:]: tw.line( - "{} {}".format(FormattedExcinfo.flow_marker, line.strip()), - red=True, + f"{FormattedExcinfo.flow_marker} {line.strip()}", red=True, ) tw.line() tw.line("%s:%d" % (self.filename, self.firstlineno + 1)) @@ -920,9 +913,7 @@ def call_fixture_func( try: fixture_result = next(generator) except StopIteration: - raise ValueError( - "{} did not yield a value".format(request.fixturename) - ) from None + raise ValueError(f"{request.fixturename} did not yield a value") from None finalizer = functools.partial(_teardown_yield_fixture, fixturefunc, generator) request.addfinalizer(finalizer) else: @@ -1000,7 +991,7 @@ class FixtureDef(Generic[_FixtureValue]): self.scopenum = scope2index( # TODO: Check if the `or` here is really necessary. scope_ or "function", # type: ignore[unreachable] - descr="Fixture '{}'".format(func.__name__), + descr=f"Fixture '{func.__name__}'", where=baseid, ) self.scope = scope_ diff --git a/src/_pytest/helpconfig.py b/src/_pytest/helpconfig.py index 348a65ede..9c3a1804d 100644 --- a/src/_pytest/helpconfig.py +++ b/src/_pytest/helpconfig.py @@ -137,7 +137,7 @@ def showversion(config: Config) -> None: for line in plugininfo: sys.stderr.write(line + "\n") else: - sys.stderr.write("pytest {}\n".format(pytest.__version__)) + sys.stderr.write(f"pytest {pytest.__version__}\n") def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: @@ -172,8 +172,8 @@ def showhelp(config: Config) -> None: if type is None: type = "string" if help is None: - raise TypeError("help argument cannot be None for {}".format(name)) - spec = "{} ({}):".format(name, type) + raise TypeError(f"help argument cannot be None for {name}") + spec = f"{name} ({type}):" tw.write(" %s" % spec) spec_len = len(spec) if spec_len > (indent_len - 3): @@ -208,7 +208,7 @@ def showhelp(config: Config) -> None: ("PYTEST_DEBUG", "set to enable debug tracing of pytest's internals"), ] for name, help in vars: - tw.line(" {:<24} {}".format(name, help)) + tw.line(f" {name:<24} {help}") tw.line() tw.line() @@ -235,7 +235,7 @@ def getpluginversioninfo(config: Config) -> List[str]: lines.append("setuptools registered plugins:") for plugin, dist in plugininfo: loc = getattr(plugin, "__file__", repr(plugin)) - content = "{}-{} at {}".format(dist.project_name, dist.version, loc) + content = f"{dist.project_name}-{dist.version} at {loc}" lines.append(" " + content) return lines @@ -243,9 +243,7 @@ def getpluginversioninfo(config: Config) -> List[str]: def pytest_report_header(config: Config) -> List[str]: lines = [] if config.option.debug or config.option.traceconfig: - lines.append( - "using: pytest-{} pylib-{}".format(pytest.__version__, py.__version__) - ) + lines.append(f"using: pytest-{pytest.__version__} pylib-{py.__version__}") verinfo = getpluginversioninfo(config) if verinfo: @@ -259,5 +257,5 @@ def pytest_report_header(config: Config) -> List[str]: r = plugin.__file__ else: r = repr(plugin) - lines.append(" {:<20}: {}".format(name, r)) + lines.append(f" {name:<20}: {r}") return lines diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index 877b9be78..621d63176 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -228,9 +228,9 @@ class _NodeReporter: reason = str(report.longrepr) if report.when == "teardown": - msg = 'failed on teardown with "{}"'.format(reason) + msg = f'failed on teardown with "{reason}"' else: - msg = 'failed on setup with "{}"'.format(reason) + msg = f'failed on setup with "{reason}"' self._add_simple("error", msg, str(report.longrepr)) def append_skipped(self, report: TestReport) -> None: @@ -246,7 +246,7 @@ class _NodeReporter: filename, lineno, skipreason = report.longrepr if skipreason.startswith("Skipped: "): skipreason = skipreason[9:] - details = "{}:{}: {}".format(filename, lineno, skipreason) + details = f"{filename}:{lineno}: {skipreason}" skipped = ET.Element("skipped", type="pytest.skip", message=skipreason) skipped.text = bin_xml_escape(details) @@ -683,7 +683,7 @@ class LogXML: logfile.close() def pytest_terminal_summary(self, terminalreporter: TerminalReporter) -> None: - terminalreporter.write_sep("-", "generated xml file: {}".format(self.logfile)) + terminalreporter.write_sep("-", f"generated xml file: {self.logfile}") def add_global_property(self, name: str, value: object) -> None: __tracebackhide__ = True diff --git a/src/_pytest/main.py b/src/_pytest/main.py index bb11df4ea..bb08bb15c 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -267,9 +267,7 @@ def wrap_session( if excinfo.value.returncode is not None: exitstatus = excinfo.value.returncode if initstate < 2: - sys.stderr.write( - "{}: {}\n".format(excinfo.typename, excinfo.value.msg) - ) + sys.stderr.write(f"{excinfo.typename}: {excinfo.value.msg}\n") config.hook.pytest_keyboard_interrupt(excinfo=excinfo) session.exitstatus = exitstatus except BaseException: @@ -615,8 +613,8 @@ class Session(nodes.FSCollector): if self._notfound: errors = [] for arg, cols in self._notfound: - line = "(no name {!r} in any of {!r})".format(arg, cols) - errors.append("not found: {}\n{}".format(arg, line)) + line = f"(no name {arg!r} in any of {cols!r})" + errors.append(f"not found: {arg}\n{line}") raise UsageError(*errors) if not genitems: items = rep.result diff --git a/src/_pytest/mark/__init__.py b/src/_pytest/mark/__init__.py index cc6e80e80..329a11c4a 100644 --- a/src/_pytest/mark/__init__.py +++ b/src/_pytest/mark/__init__.py @@ -201,7 +201,7 @@ def deselect_by_keyword(items: "List[Item]", config: Config) -> None: expression = Expression.compile(keywordexpr) except ParseError as e: raise UsageError( - "Wrong expression passed to '-k': {}: {}".format(keywordexpr, e) + f"Wrong expression passed to '-k': {keywordexpr}: {e}" ) from None remaining = [] @@ -245,9 +245,7 @@ def deselect_by_mark(items: "List[Item]", config: Config) -> None: try: expression = Expression.compile(matchexpr) except ParseError as e: - raise UsageError( - "Wrong expression passed to '-m': {}: {}".format(matchexpr, e) - ) from None + raise UsageError(f"Wrong expression passed to '-m': {matchexpr}: {e}") from None remaining = [] deselected = [] diff --git a/src/_pytest/mark/expression.py b/src/_pytest/mark/expression.py index 9f4592221..b3acef5d0 100644 --- a/src/_pytest/mark/expression.py +++ b/src/_pytest/mark/expression.py @@ -66,7 +66,7 @@ class ParseError(Exception): self.message = message def __str__(self) -> str: - return "at column {}: {}".format(self.column, self.message) + return f"at column {self.column}: {self.message}" class Scanner: diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 0af996fd0..b2ab2e35b 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -310,7 +310,7 @@ class MarkDecorator: return self.name # for backward-compat (2.4.1 had this attr) def __repr__(self) -> str: - return "".format(self.mark) + return f"" def with_args(self, *args: object, **kwargs: object) -> "MarkDecorator": """Return a MarkDecorator with extra arguments added. @@ -364,7 +364,7 @@ def normalize_mark_list(mark_list: Iterable[Union[Mark, MarkDecorator]]) -> List ] # unpack MarkDecorator for mark in extracted: if not isinstance(mark, Mark): - raise TypeError("got {!r} instead of Mark".format(mark)) + raise TypeError(f"got {mark!r} instead of Mark") return [x for x in extracted if isinstance(x, Mark)] @@ -498,14 +498,14 @@ class MarkGenerator: if name not in self._markers: if self._config.option.strict_markers: fail( - "{!r} not found in `markers` configuration option".format(name), + f"{name!r} not found in `markers` configuration option", pytrace=False, ) # Raise a specific error for common misspellings of "parametrize". if name in ["parameterize", "parametrise", "parameterise"]: __tracebackhide__ = True - fail("Unknown '{}' mark, did you mean 'parametrize'?".format(name)) + fail(f"Unknown '{name}' mark, did you mean 'parametrize'?") warnings.warn( "Unknown pytest.mark.%s - is this a typo? You can register " @@ -556,4 +556,4 @@ class NodeKeywords(MutableMapping[str, Any]): return len(self._seen()) def __repr__(self) -> str: - return "".format(self.node) + return f"" diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index bca12cd7a..d75032e65 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -74,7 +74,7 @@ def resolve(name: str) -> object: if expected == used: raise else: - raise ImportError("import error in {}: {}".format(used, ex)) from ex + raise ImportError(f"import error in {used}: {ex}") from ex found = annotated_getattr(found, part, used) return found @@ -93,9 +93,7 @@ def annotated_getattr(obj: object, name: str, ann: str) -> object: def derive_importpath(import_path: str, raising: bool) -> Tuple[str, object]: if not isinstance(import_path, str) or "." not in import_path: # type: ignore[unreachable] - raise TypeError( - "must be absolute import path string, not {!r}".format(import_path) - ) + raise TypeError(f"must be absolute import path string, not {import_path!r}") module, attr = import_path.rsplit(".", 1) target = resolve(module) if raising: @@ -202,7 +200,7 @@ class MonkeyPatch: oldval = getattr(target, name, notset) if raising and oldval is notset: - raise AttributeError("{!r} has no attribute {!r}".format(target, name)) + raise AttributeError(f"{target!r} has no attribute {name!r}") # avoid class descriptors like staticmethod/classmethod if inspect.isclass(target): diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index cc70e72d4..8130a4413 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -40,7 +40,7 @@ class OutcomeException(BaseException): def __repr__(self) -> str: if self.msg: return self.msg - return "<{} instance>".format(self.__class__.__name__) + return f"<{self.__class__.__name__} instance>" __str__ = __repr__ @@ -208,7 +208,7 @@ def importorskip( __import__(modname) except ImportError as exc: if reason is None: - reason = "could not import {!r}: {}".format(modname, exc) + reason = f"could not import {modname!r}: {exc}" raise Skipped(reason, allow_module_level=True) from None mod = sys.modules[modname] if minversion is None: diff --git a/src/_pytest/pastebin.py b/src/_pytest/pastebin.py index 0546d2377..c206900db 100644 --- a/src/_pytest/pastebin.py +++ b/src/_pytest/pastebin.py @@ -107,4 +107,4 @@ def pytest_terminal_summary(terminalreporter: TerminalReporter) -> None: s = file.getvalue() assert len(s) pastebinurl = create_new_paste(s) - terminalreporter.write_line("{} --> {}".format(msg, pastebinurl)) + terminalreporter.write_line(f"{msg} --> {pastebinurl}") diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index 75663ee62..0bc5bff2b 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -63,9 +63,7 @@ def on_rm_rf_error(func, path: str, exc, *, start_path: Path) -> bool: if not isinstance(excvalue, PermissionError): warnings.warn( - PytestWarning( - "(rm_rf) error removing {}\n{}: {}".format(path, exctype, excvalue) - ) + PytestWarning(f"(rm_rf) error removing {path}\n{exctype}: {excvalue}") ) return False @@ -200,7 +198,7 @@ def make_numbered_dir(root: Path, prefix: str) -> Path: # try up to 10 times to create the folder max_existing = max(map(parse_num, find_suffixes(root, prefix)), default=-1) new_number = max_existing + 1 - new_path = root.joinpath("{}{}".format(prefix, new_number)) + new_path = root.joinpath(f"{prefix}{new_number}") try: new_path.mkdir() except Exception: @@ -221,7 +219,7 @@ def create_cleanup_lock(p: Path) -> Path: try: fd = os.open(str(lock_path), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644) except FileExistsError as e: - raise OSError("cannot create lockfile in {path}".format(path=p)) from e + raise OSError(f"cannot create lockfile in {p}") from e else: pid = os.getpid() spid = str(pid).encode() @@ -258,7 +256,7 @@ def maybe_delete_a_numbered_dir(path: Path) -> None: lock_path = create_cleanup_lock(path) parent = path.parent - garbage = parent.joinpath("garbage-{}".format(uuid.uuid4())) + garbage = parent.joinpath(f"garbage-{uuid.uuid4()}") path.rename(garbage) rm_rf(garbage) except OSError: @@ -401,7 +399,7 @@ def fnmatch_ex(pattern: str, path) -> bool: else: name = str(path) if path.is_absolute() and not os.path.isabs(pattern): - pattern = "*{}{}".format(os.sep, pattern) + pattern = f"*{os.sep}{pattern}" return fnmatch.fnmatch(name, pattern) @@ -415,7 +413,7 @@ def symlink_or_skip(src, dst, **kwargs): try: os.symlink(str(src), str(dst), **kwargs) except OSError as e: - skip("symlinks not supported: {}".format(e)) + skip(f"symlinks not supported: {e}") class ImportMode(Enum): diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 834069a09..0a4fffed7 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -197,7 +197,7 @@ class ParsedCall: def __repr__(self) -> str: d = self.__dict__.copy() del d["_name"] - return "".format(self._name, d) + return f"" if TYPE_CHECKING: # The class has undetermined attributes, this tells mypy about it. @@ -252,7 +252,7 @@ class HookRecorder: break print("NONAMEMATCH", name, "with", call) else: - pytest.fail("could not find {!r} check {!r}".format(name, check)) + pytest.fail(f"could not find {name!r} check {check!r}") def popcall(self, name: str) -> ParsedCall: __tracebackhide__ = True @@ -260,7 +260,7 @@ class HookRecorder: if call._name == name: del self.calls[i] return call - lines = ["could not find call {!r}, in:".format(name)] + lines = [f"could not find call {name!r}, in:"] lines.extend([" %s" % x for x in self.calls]) pytest.fail("\n".join(lines)) @@ -388,7 +388,7 @@ class HookRecorder: elif rep.skipped: skipped.append(rep) else: - assert rep.failed, "Unexpected outcome: {!r}".format(rep) + assert rep.failed, f"Unexpected outcome: {rep!r}" failed.append(rep) return passed, skipped, failed @@ -658,7 +658,7 @@ class Testdir: mp.setenv("PY_COLORS", "0") def __repr__(self) -> str: - return "".format(self.tmpdir) + return f"" def __str__(self) -> str: return str(self.tmpdir) @@ -874,7 +874,7 @@ class Testdir: return result else: raise LookupError( - 'example "{}" is not found as a file or directory'.format(example_path) + f'example "{example_path}" is not found as a file or directory' ) Session = Session @@ -1087,7 +1087,7 @@ class Testdir: return self.runpytest_inprocess(*args, **kwargs) elif self._method == "subprocess": return self.runpytest_subprocess(*args, **kwargs) - raise RuntimeError("Unrecognized runpytest option: {}".format(self._method)) + raise RuntimeError(f"Unrecognized runpytest option: {self._method}") def _ensure_basetemp(self, args): args = list(args) @@ -1329,7 +1329,7 @@ class Testdir: for line in lines: print(line, file=fp) except UnicodeEncodeError: - print("couldn't print to {} because of encoding".format(fp)) + print(f"couldn't print to {fp} because of encoding") def _getpytestargs(self) -> Tuple[str, ...]: return sys.executable, "-mpytest" @@ -1386,7 +1386,7 @@ class Testdir: """ basetemp = self.tmpdir.mkdir("temp-pexpect") invoke = " ".join(map(str, self._getpytestargs())) - cmd = "{} --basetemp={} {}".format(invoke, basetemp, string) + cmd = f"{invoke} --basetemp={basetemp} {string}" return self.spawn(cmd, expect_timeout=expect_timeout) def spawn(self, cmd: str, expect_timeout: float = 10.0) -> "pexpect.spawn": @@ -1573,7 +1573,7 @@ class LineMatcher: break else: if consecutive and started: - msg = "no consecutive match: {!r}".format(line) + msg = f"no consecutive match: {line!r}" self._log(msg) self._log( "{:>{width}}".format("with:", width=wnick), repr(nextline) @@ -1587,7 +1587,7 @@ class LineMatcher: self._log("{:>{width}}".format("and:", width=wnick), repr(nextline)) extralines.append(nextline) else: - msg = "remains unmatched: {!r}".format(line) + msg = f"remains unmatched: {line!r}" self._log(msg) self._fail(msg) self._log_output = [] @@ -1622,7 +1622,7 @@ class LineMatcher: wnick = len(match_nickname) + 1 for line in self.lines: if match_func(line, pat): - msg = "{}: {!r}".format(match_nickname, pat) + msg = f"{match_nickname}: {pat!r}" self._log(msg) self._log("{:>{width}}".format("with:", width=wnick), repr(line)) self._fail(msg) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 1acf2c0e5..d07615a5a 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -483,7 +483,7 @@ class PyCollector(PyobjMixin, nodes.Collector): fixtureinfo.prune_dependency_tree() for callspec in metafunc._calls: - subname = "{}[{}]".format(name, callspec.id) + subname = f"{name}[{callspec.id}]" yield Function.from_parent( self, name=subname, @@ -888,7 +888,7 @@ class CallSpec2: def _checkargnotcontained(self, arg: str) -> None: if arg in self.params or arg in self.funcargs: - raise ValueError("duplicate {!r}".format(arg)) + raise ValueError(f"duplicate {arg!r}") def getparam(self, name: str) -> object: try: @@ -918,7 +918,7 @@ class CallSpec2: elif valtype_for_arg == "funcargs": self.funcargs[arg] = val else: # pragma: no cover - assert False, "Unhandled valtype for arg: {}".format(valtype_for_arg) + assert False, f"Unhandled valtype for arg: {valtype_for_arg}" self.indices[arg] = param_index self._arg2scopenum[arg] = scopenum self._idlist.append(id) @@ -1068,7 +1068,7 @@ class Metafunc: object.__setattr__(_param_mark._param_ids_from, "_param_ids_generated", ids) scopenum = scope2index( - scope, descr="parametrize() call in {}".format(self.function.__name__) + scope, descr=f"parametrize() call in {self.function.__name__}" ) # Create the new calls: if we are parametrize() multiple times (by applying the decorator @@ -1224,7 +1224,7 @@ class Metafunc: else: name = "fixture" if indirect else "argument" fail( - "In {}: function uses no {} '{}'".format(func_name, name, arg), + f"In {func_name}: function uses no {name} '{arg}'", pytrace=False, ) @@ -1291,7 +1291,7 @@ def _idval( if generated_id is not None: val = generated_id except Exception as e: - prefix = "{}: ".format(nodeid) if nodeid is not None else "" + prefix = f"{nodeid}: " if nodeid is not None else "" msg = "error raised while trying to determine id of parameter '{}' at position {}" msg = prefix + msg.format(argname, idx) raise ValueError(msg) from e @@ -1400,7 +1400,7 @@ def _show_fixtures_per_test(config: Config, session: Session) -> None: return if verbose > 0: bestrel = get_best_relpath(fixture_def.func) - funcargspec = "{} -- {}".format(argname, bestrel) + funcargspec = f"{argname} -- {bestrel}" else: funcargspec = argname tw.line(funcargspec, green=True) @@ -1417,7 +1417,7 @@ def _show_fixtures_per_test(config: Config, session: Session) -> None: # This test item does not use any fixtures. return tw.line() - tw.sep("-", "fixtures used by {}".format(item.name)) + tw.sep("-", f"fixtures used by {item.name}") # TODO: Fix this type ignore. tw.sep("-", "({})".format(get_best_relpath(item.function))) # type: ignore[attr-defined] # dict key not used in loop but needed for sorting. @@ -1476,7 +1476,7 @@ def _showfixtures_main(config: Config, session: Session) -> None: if currentmodule != module: if not module.startswith("_pytest."): tw.line() - tw.sep("-", "fixtures defined from {}".format(module)) + tw.sep("-", f"fixtures defined from {module}") currentmodule = module if verbose <= 0 and argname[0] == "_": continue @@ -1491,7 +1491,7 @@ def _showfixtures_main(config: Config, session: Session) -> None: if doc: write_docstring(tw, doc) else: - tw.line(" {}: no docstring available".format(loc), red=True) + tw.line(f" {loc}: no docstring available", red=True) tw.line() diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 71ba6d815..fbfe90ed9 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -25,7 +25,7 @@ from _pytest.outcomes import fail def _non_numeric_type_error(value, at: Optional[str]) -> TypeError: - at_str = " at {}".format(at) if at else "" + at_str = f" at {at}" if at else "" return TypeError( "cannot make approximate comparisons to non-numeric values: {!r} {}".format( value, at_str @@ -98,7 +98,7 @@ class ApproxNumpy(ApproxBase): def __repr__(self) -> str: list_scalars = _recursive_list_map(self._approx_scalar, self.expected.tolist()) - return "approx({!r})".format(list_scalars) + return f"approx({list_scalars!r})" def __eq__(self, actual) -> bool: import numpy as np @@ -109,9 +109,7 @@ class ApproxNumpy(ApproxBase): try: actual = np.asarray(actual) except Exception as e: - raise TypeError( - "cannot compare '{}' to numpy.ndarray".format(actual) - ) from e + raise TypeError(f"cannot compare '{actual}' to numpy.ndarray") from e if not np.isscalar(actual) and actual.shape != self.expected.shape: return False @@ -219,7 +217,7 @@ class ApproxScalar(ApproxBase): # If a sensible tolerance can't be calculated, self.tolerance will # raise a ValueError. In this case, display '???'. try: - vetted_tolerance = "{:.1e}".format(self.tolerance) + vetted_tolerance = f"{self.tolerance:.1e}" if ( isinstance(self.expected, Complex) and self.expected.imag @@ -229,7 +227,7 @@ class ApproxScalar(ApproxBase): except ValueError: vetted_tolerance = "???" - return "{} ± {}".format(self.expected, vetted_tolerance) + return f"{self.expected} ± {vetted_tolerance}" def __eq__(self, actual) -> bool: """Return whether the given value is equal to the expected value @@ -291,7 +289,7 @@ class ApproxScalar(ApproxBase): if absolute_tolerance < 0: raise ValueError( - "absolute tolerance can't be negative: {}".format(absolute_tolerance) + f"absolute tolerance can't be negative: {absolute_tolerance}" ) if math.isnan(absolute_tolerance): raise ValueError("absolute tolerance can't be NaN.") @@ -313,7 +311,7 @@ class ApproxScalar(ApproxBase): if relative_tolerance < 0: raise ValueError( - "relative tolerance can't be negative: {}".format(absolute_tolerance) + f"relative tolerance can't be negative: {absolute_tolerance}" ) if math.isnan(relative_tolerance): raise ValueError("relative tolerance can't be NaN.") @@ -698,7 +696,7 @@ def raises( not_a = exc.__name__ if isinstance(exc, type) else type(exc).__name__ raise TypeError(msg.format(not_a)) - message = "DID NOT RAISE {}".format(expected_exception) + message = f"DID NOT RAISE {expected_exception}" if not args: match = kwargs.pop("match", None) # type: Optional[Union[str, Pattern[str]]] diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index c60137d38..8e7f0f9bb 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -321,7 +321,7 @@ class TestReport(BaseReport): excinfo, style=item.config.getoption("tbstyle", "auto") ) for rwhen, key, content in item._report_sections: - sections.append(("Captured {} {}".format(key, rwhen), content)) + sections.append((f"Captured {key} {rwhen}", content)) return cls( item.nodeid, item.location, diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index e16fb4ab4..e617c2326 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -93,7 +93,7 @@ def pytest_terminal_summary(terminalreporter: "TerminalReporter") -> None: % (len(dlist) - i, durations_min) ) break - tr.write_line("{:02.2f}s {:<8} {}".format(rep.duration, rep.when, rep.nodeid)) + tr.write_line(f"{rep.duration:02.2f}s {rep.when:<8} {rep.nodeid}") def pytest_sessionstart(session: "Session") -> None: @@ -186,7 +186,7 @@ def _update_current_test_var( """ var_name = "PYTEST_CURRENT_TEST" if when: - value = "{} ({})".format(item.nodeid, when) + value = f"{item.nodeid} ({when})" # don't allow null bytes on environment variables (see #2644, #2957) value = value.replace("\x00", "(null)") os.environ[var_name] = value @@ -248,7 +248,7 @@ def call_runtest_hook( elif when == "teardown": ihook = item.ihook.pytest_runtest_teardown else: - assert False, "Unhandled runtest hook case: {}".format(when) + assert False, f"Unhandled runtest hook case: {when}" reraise = (Exit,) # type: Tuple[Type[BaseException], ...] if not item.config.getoption("usepdb", False): reraise += (KeyboardInterrupt,) @@ -290,7 +290,7 @@ class CallInfo(Generic[TResult]): @property def result(self) -> TResult: if self.excinfo is not None: - raise AttributeError("{!r} has no valid result".format(self)) + raise AttributeError(f"{self!r} has no valid result") # The cast is safe because an exception wasn't raised, hence # _result has the expected function return type (which may be # None, that's why a cast and not an assert). @@ -330,8 +330,8 @@ class CallInfo(Generic[TResult]): def __repr__(self) -> str: if self.excinfo is None: - return "".format(self.when, self._result) - return "".format(self.when, self.excinfo) + return f"" + return f"" def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> TestReport: diff --git a/src/_pytest/skipping.py b/src/_pytest/skipping.py index cc505fdd7..afc3610eb 100644 --- a/src/_pytest/skipping.py +++ b/src/_pytest/skipping.py @@ -101,7 +101,7 @@ def evaluate_condition(item: Item, mark: Mark, condition: object) -> Tuple[bool, if hasattr(item, "obj"): globals_.update(item.obj.__globals__) # type: ignore[attr-defined] try: - filename = "<{} condition>".format(mark.name) + filename = f"<{mark.name} condition>" condition_code = compile(condition, filename, "eval") result = eval(condition_code, globals_) except SyntaxError as exc: @@ -264,7 +264,7 @@ def pytest_runtest_makereport(item: Item, call: CallInfo[None]): if unexpectedsuccess_key in item._store and rep.when == "call": reason = item._store[unexpectedsuccess_key] if reason: - rep.longrepr = "Unexpected success: {}".format(reason) + rep.longrepr = f"Unexpected success: {reason}" else: rep.longrepr = "Unexpected success" rep.outcome = "failed" diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 5b7a09c64..f84797af2 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -304,7 +304,7 @@ class WarningReport: relpath = bestrelpath( config.invocation_params.dir, absolutepath(filename) ) - return "{}:{}".format(relpath, linenum) + return f"{relpath}:{linenum}" else: return str(self.fslocation) return None @@ -487,7 +487,7 @@ class TerminalReporter: def pytest_plugin_registered(self, plugin: _PluggyPlugin) -> None: if self.config.option.traceconfig: - msg = "PLUGIN registered: {}".format(plugin) + msg = f"PLUGIN registered: {plugin}" # XXX This event may happen during setup/teardown time # which unfortunately captures our output here # which garbles our output if we use self.write_line. @@ -593,9 +593,9 @@ class TerminalReporter: if collected: progress = self._progress_nodeids_reported counter_format = "{{:{}d}}".format(len(str(collected))) - format_string = " [{}/{{}}]".format(counter_format) + format_string = f" [{counter_format}/{{}}]" return format_string.format(len(progress), collected) - return " [ {} / {} ]".format(collected, collected) + return f" [ {collected} / {collected} ]" else: if collected: return " [{:3d}%]".format( @@ -682,7 +682,7 @@ class TerminalReporter: self.write_sep("=", "test session starts", bold=True) verinfo = platform.python_version() if not self.no_header: - msg = "platform {} -- Python {}".format(sys.platform, verinfo) + msg = f"platform {sys.platform} -- Python {verinfo}" pypy_version_info = getattr(sys, "pypy_version_info", None) if pypy_version_info: verinfo = ".".join(map(str, pypy_version_info[:3])) @@ -778,7 +778,7 @@ class TerminalReporter: if col.name == "()": # Skip Instances. continue indent = (len(stack) - 1) * " " - self._tw.line("{}{}".format(indent, col)) + self._tw.line(f"{indent}{col}") if self.config.option.verbose >= 1: obj = getattr(col, "obj", None) doc = inspect.getdoc(obj) if obj else None @@ -1018,7 +1018,7 @@ class TerminalReporter: if rep.when == "collect": msg = "ERROR collecting " + msg else: - msg = "ERROR at {} of {}".format(rep.when, msg) + msg = f"ERROR at {rep.when} of {msg}" self.write_sep("_", msg, red=True, bold=True) self._outrep_summary(rep) @@ -1091,7 +1091,7 @@ class TerminalReporter: for rep in xfailed: verbose_word = rep._get_verbose_word(self.config) pos = _get_pos(self.config, rep) - lines.append("{} {}".format(verbose_word, pos)) + lines.append(f"{verbose_word} {pos}") reason = rep.wasxfail if reason: lines.append(" " + str(reason)) @@ -1102,7 +1102,7 @@ class TerminalReporter: verbose_word = rep._get_verbose_word(self.config) pos = _get_pos(self.config, rep) reason = rep.wasxfail - lines.append("{} {} {}".format(verbose_word, pos, reason)) + lines.append(f"{verbose_word} {pos} {reason}") def show_skipped(lines: List[str]) -> None: skipped = self.stats.get("skipped", []) # type: List[CollectReport] @@ -1201,7 +1201,7 @@ def _get_line_with_reprcrash_message( verbose_word = rep._get_verbose_word(config) pos = _get_pos(config, rep) - line = "{} {}".format(verbose_word, pos) + line = f"{verbose_word} {pos}" len_line = wcswidth(line) ellipsis, len_ellipsis = "...", 3 if len_line > termwidth - len_ellipsis: @@ -1302,7 +1302,7 @@ def _plugin_nameversions(plugininfo) -> List[str]: def format_session_duration(seconds: float) -> str: """Format the given seconds in a human readable manner to show in the final summary.""" if seconds < 60: - return "{:.2f}s".format(seconds) + return f"{seconds:.2f}s" else: dt = datetime.timedelta(seconds=int(seconds)) - return "{:.2f}s ({})".format(seconds, dt) + return f"{seconds:.2f}s ({dt})" diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index 02a613483..b889be888 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -50,9 +50,7 @@ class TempPathFactory: def _ensure_relative_to_basetemp(self, basename: str) -> str: basename = os.path.normpath(basename) if (self.getbasetemp() / basename).resolve().parent != self.getbasetemp(): - raise ValueError( - "{} is not a normalized and relative path".format(basename) - ) + raise ValueError(f"{basename} is not a normalized and relative path") return basename def mktemp(self, basename: str, numbered: bool = True) -> Path: @@ -94,7 +92,7 @@ class TempPathFactory: user = get_user() or "unknown" # use a sub-directory in the temproot to speed-up # make_numbered_dir() call - rootdir = temproot.joinpath("pytest-of-{}".format(user)) + rootdir = temproot.joinpath(f"pytest-of-{user}") rootdir.mkdir(exist_ok=True) basetemp = make_numbered_dir_with_cleanup( prefix="pytest-", root=rootdir, keep=3, lock_timeout=LOCK_TIMEOUT diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index ff5112d55..f4b7d6135 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -156,7 +156,7 @@ class TestGeneralUsage: assert x """ ) - result = testdir.runpytest(p, "--import-mode={}".format(import_mode)) + result = testdir.runpytest(p, f"--import-mode={import_mode}") result.stdout.fnmatch_lines(["> assert x", "E assert 0"]) assert result.ret == 1 @@ -185,7 +185,7 @@ class TestGeneralUsage: assert result.ret == ExitCode.USAGE_ERROR result.stderr.fnmatch_lines( [ - "ERROR: not found: {}".format(p2), + f"ERROR: not found: {p2}", "(no name {!r} in any of [[][]])".format(str(p2)), "", ] @@ -212,7 +212,7 @@ class TestGeneralUsage: result = testdir.runpytest() assert result.stdout.lines == [] assert result.stderr.lines == [ - "ImportError while loading conftest '{}'.".format(conftest), + f"ImportError while loading conftest '{conftest}'.", "conftest.py:3: in ", " foo()", "conftest.py:2: in foo", @@ -503,7 +503,7 @@ class TestInvocationVariants: def test_pydoc(self, testdir): for name in ("py.test", "pytest"): - result = testdir.runpython_c("import {};help({})".format(name, name)) + result = testdir.runpython_c(f"import {name};help({name})") assert result.ret == 0 s = result.stdout.str() assert "MarkGenerator" in s @@ -671,8 +671,8 @@ class TestInvocationVariants: ) lib = ns.mkdir(dirname) lib.ensure("__init__.py") - lib.join("test_{}.py".format(dirname)).write( - "def test_{}(): pass\ndef test_other():pass".format(dirname) + lib.join(f"test_{dirname}.py").write( + f"def test_{dirname}(): pass\ndef test_other():pass" ) # The structure of the test directory is now: @@ -891,7 +891,7 @@ class TestDurations: if ("test_%s" % x) in line and y in line: break else: - raise AssertionError("not found {} {}".format(x, y)) + raise AssertionError(f"not found {x} {y}") def test_calls_showall_verbose(self, testdir, mock_timing): testdir.makepyfile(self.source) @@ -904,7 +904,7 @@ class TestDurations: if ("test_%s" % x) in line and y in line: break else: - raise AssertionError("not found {} {}".format(x, y)) + raise AssertionError(f"not found {x} {y}") def test_with_deselected(self, testdir, mock_timing): testdir.makepyfile(self.source) diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index ce07d4dbc..d1ed43300 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -206,8 +206,8 @@ class TestTraceback_f_g_h: excinfo = pytest.raises(ValueError, h) traceback = excinfo.traceback ntraceback = traceback.filter() - print("old: {!r}".format(traceback)) - print("new: {!r}".format(ntraceback)) + print(f"old: {traceback!r}") + print(f"new: {ntraceback!r}") if matching: assert len(ntraceback) == len(traceback) - 2 @@ -265,7 +265,7 @@ class TestTraceback_f_g_h: decorator = pytest.importorskip("decorator").decorator def log(f, *k, **kw): - print("{} {}".format(k, kw)) + print(f"{k} {kw}") f(*k, **kw) log = decorator(log) @@ -426,13 +426,13 @@ def test_match_raises_error(testdir): assert result.ret != 0 exc_msg = "Regex pattern '[[]123[]]+' does not match 'division by zero'." - result.stdout.fnmatch_lines(["E * AssertionError: {}".format(exc_msg)]) + result.stdout.fnmatch_lines([f"E * AssertionError: {exc_msg}"]) result.stdout.no_fnmatch_line("*__tracebackhide__ = True*") result = testdir.runpytest("--fulltrace") assert result.ret != 0 result.stdout.fnmatch_lines( - ["*__tracebackhide__ = True*", "E * AssertionError: {}".format(exc_msg)] + ["*__tracebackhide__ = True*", f"E * AssertionError: {exc_msg}"] ) @@ -834,14 +834,14 @@ raise ValueError() "def entry():", "> f(0)", "", - "{}:5: ".format(mod.__file__), + f"{mod.__file__}:5: ", "_ _ *", "", " def f(x):", "> raise ValueError(x)", "E ValueError: 0", "", - "{}:3: ValueError".format(mod.__file__), + f"{mod.__file__}:3: ValueError", ] ) assert raised == 3 diff --git a/testing/logging/test_reporting.py b/testing/logging/test_reporting.py index 7590b5762..7545d016d 100644 --- a/testing/logging/test_reporting.py +++ b/testing/logging/test_reporting.py @@ -262,10 +262,10 @@ def test_log_cli_default_level_multiple_tests(testdir, request): result = testdir.runpytest() result.stdout.fnmatch_lines( [ - "{}::test_log_1 ".format(filename), + f"{filename}::test_log_1 ", "*WARNING*log message from test_log_1*", "PASSED *50%*", - "{}::test_log_2 ".format(filename), + f"{filename}::test_log_2 ", "*WARNING*log message from test_log_2*", "PASSED *100%*", "=* 2 passed in *=", @@ -318,7 +318,7 @@ def test_log_cli_default_level_sections(testdir, request): result = testdir.runpytest() result.stdout.fnmatch_lines( [ - "{}::test_log_1 ".format(filename), + f"{filename}::test_log_1 ", "*-- live log start --*", "*WARNING* >>>>> START >>>>>*", "*-- live log setup --*", @@ -330,7 +330,7 @@ def test_log_cli_default_level_sections(testdir, request): "*WARNING*log message from teardown of test_log_1*", "*-- live log finish --*", "*WARNING* <<<<< END <<<<<<<*", - "{}::test_log_2 ".format(filename), + f"{filename}::test_log_2 ", "*-- live log start --*", "*WARNING* >>>>> START >>>>>*", "*-- live log setup --*", @@ -394,7 +394,7 @@ def test_live_logs_unknown_sections(testdir, request): result.stdout.fnmatch_lines( [ "*WARNING*Unknown Section*", - "{}::test_log_1 ".format(filename), + f"{filename}::test_log_1 ", "*WARNING* >>>>> START >>>>>*", "*-- live log setup --*", "*WARNING*log message from setup of test_log_1*", @@ -453,7 +453,7 @@ def test_sections_single_new_line_after_test_outcome(testdir, request): result = testdir.runpytest() result.stdout.fnmatch_lines( [ - "{}::test_log_1 ".format(filename), + f"{filename}::test_log_1 ", "*-- live log start --*", "*WARNING* >>>>> START >>>>>*", "*-- live log setup --*", @@ -638,7 +638,7 @@ def test_log_file_cli(testdir): log_file = testdir.tmpdir.join("pytest.log").strpath result = testdir.runpytest( - "-s", "--log-file={}".format(log_file), "--log-file-level=WARNING" + "-s", f"--log-file={log_file}", "--log-file-level=WARNING" ) # fnmatch_lines does an assertion internally @@ -670,9 +670,7 @@ def test_log_file_cli_level(testdir): log_file = testdir.tmpdir.join("pytest.log").strpath - result = testdir.runpytest( - "-s", "--log-file={}".format(log_file), "--log-file-level=INFO" - ) + result = testdir.runpytest("-s", f"--log-file={log_file}", "--log-file-level=INFO") # fnmatch_lines does an assertion internally result.stdout.fnmatch_lines(["test_log_file_cli_level.py PASSED"]) diff --git a/testing/python/approx.py b/testing/python/approx.py index 5f12da376..b37c9f757 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -471,7 +471,7 @@ class TestApprox: expected = "4.0e-06" result = testdir.runpytest() result.stdout.fnmatch_lines( - ["*At index 0 diff: 3 != 4 ± {}".format(expected), "=* 1 failed in *="] + [f"*At index 0 diff: 3 != 4 ± {expected}", "=* 1 failed in *="] ) @pytest.mark.parametrize( @@ -483,8 +483,7 @@ class TestApprox: ) def test_expected_value_type_error(self, x, name): with pytest.raises( - TypeError, - match=r"pytest.approx\(\) does not support nested {}:".format(name), + TypeError, match=fr"pytest.approx\(\) does not support nested {name}:", ): approx(x) diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 07e1c6f73..c8b200fcc 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -1993,7 +1993,7 @@ class TestAutouseManagement: pass """ ) - confcut = "--confcutdir={}".format(testdir.tmpdir) + confcut = f"--confcutdir={testdir.tmpdir}" reprec = testdir.inline_run("-v", "-s", confcut) reprec.assertoutcome(passed=8) config = reprec.getcalls("pytest_unconfigure")[0].config @@ -3796,7 +3796,7 @@ class TestParameterizedSubRequest: " test_foos.py::test_foo", "", "Requested fixture 'fix_with_param' defined in:", - "{}:4".format(fixfile), + f"{fixfile}:4", "Requested here:", "test_foos.py:4", "*1 failed*", @@ -3813,9 +3813,9 @@ class TestParameterizedSubRequest: " test_foos.py::test_foo", "", "Requested fixture 'fix_with_param' defined in:", - "{}:4".format(fixfile), + f"{fixfile}:4", "Requested here:", - "{}:4".format(testfile), + f"{testfile}:4", "*1 failed*", ] ) diff --git a/testing/python/raises.py b/testing/python/raises.py index 26931a378..c3580afad 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -211,7 +211,7 @@ class TestRaises: pytest.raises(TypeError, int, match="invalid") def tfunc(match): - raise ValueError("match={}".format(match)) + raise ValueError(f"match={match}") pytest.raises(ValueError, tfunc, match="asdf").match("match=asdf") pytest.raises(ValueError, tfunc, match="").match("match=") diff --git a/testing/test_argcomplete.py b/testing/test_argcomplete.py index 9cab242c0..a3224be51 100644 --- a/testing/test_argcomplete.py +++ b/testing/test_argcomplete.py @@ -11,7 +11,7 @@ def equal_with_bash(prefix, ffc, fc, out=None): res_bash = set(fc(prefix)) retval = set(res) == res_bash if out: - out.write("equal_with_bash({}) {} {}\n".format(prefix, retval, res)) + out.write(f"equal_with_bash({prefix}) {retval} {res}\n") if not retval: out.write(" python - bash: %s\n" % (set(res) - res_bash)) out.write(" bash - python: %s\n" % (res_bash - set(res))) @@ -45,26 +45,16 @@ class FilesCompleter: completion = [] if self.allowednames: if self.directories: - files = _wrapcall( - ["bash", "-c", "compgen -A directory -- '{p}'".format(p=prefix)] - ) + files = _wrapcall(["bash", "-c", f"compgen -A directory -- '{prefix}'"]) completion += [f + "/" for f in files] for x in self.allowednames: completion += _wrapcall( - [ - "bash", - "-c", - "compgen -A file -X '!*.{0}' -- '{p}'".format(x, p=prefix), - ] + ["bash", "-c", f"compgen -A file -X '!*.{x}' -- '{prefix}'"] ) else: - completion += _wrapcall( - ["bash", "-c", "compgen -A file -- '{p}'".format(p=prefix)] - ) + completion += _wrapcall(["bash", "-c", f"compgen -A file -- '{prefix}'"]) - anticomp = _wrapcall( - ["bash", "-c", "compgen -A directory -- '{p}'".format(p=prefix)] - ) + anticomp = _wrapcall(["bash", "-c", f"compgen -A directory -- '{prefix}'"]) completion = list(set(completion) - set(anticomp)) diff --git a/testing/test_assertion.py b/testing/test_assertion.py index d6dc9fc98..f91bc3cb4 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -1494,7 +1494,7 @@ def test_assert_tuple_warning(testdir): """ ) result = testdir.runpytest() - result.stdout.fnmatch_lines(["*test_assert_tuple_warning.py:2:*{}*".format(msg)]) + result.stdout.fnmatch_lines([f"*test_assert_tuple_warning.py:2:*{msg}*"]) # tuples with size != 2 should not trigger the warning testdir.makepyfile( diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 98dea5d88..7dcaf10ea 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -198,14 +198,14 @@ class TestAssertionRewrite: lines = msg.splitlines() if verbose > 1: assert lines == [ - "assert {!r} == 42".format(X), - " +{!r}".format(X), + f"assert {X!r} == 42", + f" +{X!r}", " -42", ] elif verbose > 0: assert lines == [ "assert .X'> == 42", - " +{!r}".format(X), + f" +{X!r}", " -42", ] else: @@ -652,7 +652,7 @@ class TestAssertionRewrite: assert getmsg(f1) == "assert 42" def my_reprcompare2(op, left, right) -> str: - return "{} {} {}".format(left, op, right) + return f"{left} {op} {right}" monkeypatch.setattr(util, "_reprcompare", my_reprcompare2) @@ -834,9 +834,7 @@ def test_rewritten(): ) result = testdir.runpytest_subprocess() assert result.ret == 0 - found_names = glob.glob( - "__pycache__/*-pytest-{}.pyc".format(pytest.__version__) - ) + found_names = glob.glob(f"__pycache__/*-pytest-{pytest.__version__}.pyc") assert found_names, "pyc with expected tag not found in names: {}".format( glob.glob("__pycache__/*.pyc") ) diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index a911257ce..6203ebec7 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -189,9 +189,7 @@ def test_cache_reportheader_external_abspath(testdir, tmpdir_factory): ) ) result = testdir.runpytest("-v") - result.stdout.fnmatch_lines( - ["cachedir: {abscache}".format(abscache=external_cache)] - ) + result.stdout.fnmatch_lines([f"cachedir: {external_cache}"]) def test_cache_show(testdir): diff --git a/testing/test_capture.py b/testing/test_capture.py index 317a59227..7aeb2d8ac 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -937,7 +937,7 @@ def lsof_check(): out = subprocess.check_output(("lsof", "-p", str(pid))).decode() except (OSError, subprocess.CalledProcessError, UnicodeDecodeError) as exc: # about UnicodeDecodeError, see note on pytester - pytest.skip("could not run 'lsof' ({!r})".format(exc)) + pytest.skip(f"could not run 'lsof' ({exc!r})") yield out2 = subprocess.check_output(("lsof", "-p", str(pid))).decode() len1 = len([x for x in out.split("\n") if "REG" in x]) diff --git a/testing/test_compat.py b/testing/test_compat.py index 5ddde3250..752e2d0e1 100644 --- a/testing/test_compat.py +++ b/testing/test_compat.py @@ -35,7 +35,7 @@ def test_real_func_loop_limit(): self.left = 1000 def __repr__(self): - return "".format(left=self.left) + return f"" def __getattr__(self, attr): if not self.left: diff --git a/testing/test_config.py b/testing/test_config.py index 7a0c135ef..39f88d945 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1652,7 +1652,7 @@ def test_help_and_version_after_argument_error(testdir): assert result.ret == ExitCode.USAGE_ERROR result = testdir.runpytest("--version") - result.stderr.fnmatch_lines(["pytest {}".format(pytest.__version__)]) + result.stderr.fnmatch_lines([f"pytest {pytest.__version__}"]) assert result.ret == ExitCode.USAGE_ERROR @@ -1797,12 +1797,7 @@ class TestPytestPluginsVariable: res = testdir.runpytest() assert res.ret == 2 msg = "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported" - res.stdout.fnmatch_lines( - [ - "*{msg}*".format(msg=msg), - "*subdirectory{sep}conftest.py*".format(sep=os.sep), - ] - ) + res.stdout.fnmatch_lines([f"*{msg}*", f"*subdirectory{os.sep}conftest.py*"]) @pytest.mark.parametrize("use_pyargs", [True, False]) def test_pytest_plugins_in_non_top_level_conftest_unsupported_pyargs( @@ -1830,7 +1825,7 @@ class TestPytestPluginsVariable: if use_pyargs: assert msg not in res.stdout.str() else: - res.stdout.fnmatch_lines(["*{msg}*".format(msg=msg)]) + res.stdout.fnmatch_lines([f"*{msg}*"]) def test_pytest_plugins_in_non_top_level_conftest_unsupported_no_top_level_conftest( self, testdir @@ -1854,12 +1849,7 @@ class TestPytestPluginsVariable: res = testdir.runpytest_subprocess() assert res.ret == 2 msg = "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported" - res.stdout.fnmatch_lines( - [ - "*{msg}*".format(msg=msg), - "*subdirectory{sep}conftest.py*".format(sep=os.sep), - ] - ) + res.stdout.fnmatch_lines([f"*{msg}*", f"*subdirectory{os.sep}conftest.py*"]) def test_pytest_plugins_in_non_top_level_conftest_unsupported_no_false_positives( self, testdir @@ -1887,7 +1877,7 @@ def test_conftest_import_error_repr(tmpdir): path = tmpdir.join("foo/conftest.py") with pytest.raises( ConftestImportFailure, - match=re.escape("RuntimeError: some error (from {})".format(path)), + match=re.escape(f"RuntimeError: some error (from {path})"), ): try: raise RuntimeError("some error") diff --git a/testing/test_debugging.py b/testing/test_debugging.py index 948b621f7..f22d5d724 100644 --- a/testing/test_debugging.py +++ b/testing/test_debugging.py @@ -251,9 +251,7 @@ class TestPDB: assert False """ ) - child = testdir.spawn_pytest( - "--show-capture={} --pdb {}".format(showcapture, p1) - ) + child = testdir.spawn_pytest(f"--show-capture={showcapture} --pdb {p1}") if showcapture in ("all", "log"): child.expect("captured log") child.expect("get rekt") @@ -706,7 +704,7 @@ class TestPDB: set_trace() """ ) - child = testdir.spawn_pytest("--tb=short {} {}".format(p1, capture_arg)) + child = testdir.spawn_pytest(f"--tb=short {p1} {capture_arg}") child.expect("=== SET_TRACE ===") before = child.before.decode("utf8") if not capture_arg: @@ -744,7 +742,7 @@ class TestPDB: x = 5 """ ) - child = testdir.spawn("{} {}".format(sys.executable, p1)) + child = testdir.spawn(f"{sys.executable} {p1}") child.expect("x = 5") child.expect("Pdb") child.sendeof() @@ -1085,12 +1083,12 @@ class TestTraceOption: child.expect_exact(func) child.expect_exact("Pdb") child.sendline("args") - child.expect_exact("{} = 1\r\n".format(argname)) + child.expect_exact(f"{argname} = 1\r\n") child.expect_exact("Pdb") child.sendline("c") child.expect_exact("Pdb") child.sendline("args") - child.expect_exact("{} = 2\r\n".format(argname)) + child.expect_exact(f"{argname} = 2\r\n") child.expect_exact("Pdb") child.sendline("c") child.expect_exact("> PDB continue (IO-capturing resumed) >") diff --git a/testing/test_doctest.py b/testing/test_doctest.py index 13b859797..0e8bba980 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -1494,7 +1494,7 @@ def test_is_setup_py_not_named_setup_py(tmpdir): @pytest.mark.parametrize("mod", ("setuptools", "distutils.core")) def test_is_setup_py_is_a_setup_py(tmpdir, mod): setup_py = tmpdir.join("setup.py") - setup_py.write('from {} import setup; setup(name="foo")'.format(mod)) + setup_py.write(f'from {mod} import setup; setup(name="foo")') assert _is_setup_py(setup_py) diff --git a/testing/test_faulthandler.py b/testing/test_faulthandler.py index 87a195bf8..4d0b32ede 100644 --- a/testing/test_faulthandler.py +++ b/testing/test_faulthandler.py @@ -124,7 +124,7 @@ def test_already_initialized(faulthandler_timeout, testdir): "-mpytest", testdir.tmpdir, "-o", - "faulthandler_timeout={}".format(faulthandler_timeout), + f"faulthandler_timeout={faulthandler_timeout}", ) # ensure warning is emitted if faulthandler_timeout is configured warning_line = "*faulthandler.py*faulthandler module enabled before*" diff --git a/testing/test_helpconfig.py b/testing/test_helpconfig.py index 6116242ec..6f6d53337 100644 --- a/testing/test_helpconfig.py +++ b/testing/test_helpconfig.py @@ -6,9 +6,7 @@ def test_version_verbose(testdir, pytestconfig): testdir.monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD") result = testdir.runpytest("--version", "--version") assert result.ret == 0 - result.stderr.fnmatch_lines( - ["*pytest*{}*imported from*".format(pytest.__version__)] - ) + result.stderr.fnmatch_lines([f"*pytest*{pytest.__version__}*imported from*"]) if pytestconfig.pluginmanager.list_plugin_distinfo(): result.stderr.fnmatch_lines(["*setuptools registered plugins:", "*at*"]) @@ -18,7 +16,7 @@ def test_version_less_verbose(testdir, pytestconfig): result = testdir.runpytest("--version") assert result.ret == 0 # p = py.path.local(py.__file__).dirpath() - result.stderr.fnmatch_lines(["pytest {}".format(pytest.__version__)]) + result.stderr.fnmatch_lines([f"pytest {pytest.__version__}"]) def test_help(testdir): diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 23ef3f347..ef4ff6a77 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -245,9 +245,7 @@ class TestPython: pass """ ) - result, dom = run_and_parse( - "-o", "junit_duration_report={}".format(duration_report) - ) + result, dom = run_and_parse("-o", f"junit_duration_report={duration_report}") node = dom.find_first_by_tag("testsuite") tnode = node.find_first_by_tag("testcase") val = float(tnode["time"]) diff --git a/testing/test_link_resolve.py b/testing/test_link_resolve.py index f43f7ded5..7eaf41247 100644 --- a/testing/test_link_resolve.py +++ b/testing/test_link_resolve.py @@ -77,7 +77,5 @@ def test_link_resolve(testdir: pytester.Testdir) -> None: # i.e.: Expect drive on windows because we just have drive:filename, whereas # we expect a relative path on Linux. - expect = ( - "*{}*".format(subst_p) if sys.platform == "win32" else "*sub2/test_foo.py*" - ) + expect = f"*{subst_p}*" if sys.platform == "win32" else "*sub2/test_foo.py*" result.stdout.fnmatch_lines([expect]) diff --git a/testing/test_main.py b/testing/test_main.py index 8ec7b8111..3e94668e8 100644 --- a/testing/test_main.py +++ b/testing/test_main.py @@ -48,20 +48,20 @@ def test_wrap_session_notify_exception(ret_exc, testdir): if exc == SystemExit: assert result.stdout.lines[-3:] == [ - 'INTERNALERROR> File "{}", line 4, in pytest_sessionstart'.format(c1), + f'INTERNALERROR> File "{c1}", line 4, in pytest_sessionstart', 'INTERNALERROR> raise SystemExit("boom")', "INTERNALERROR> SystemExit: boom", ] else: assert result.stdout.lines[-3:] == [ - 'INTERNALERROR> File "{}", line 4, in pytest_sessionstart'.format(c1), + f'INTERNALERROR> File "{c1}", line 4, in pytest_sessionstart', 'INTERNALERROR> raise ValueError("boom")', "INTERNALERROR> ValueError: boom", ] if returncode is False: assert result.stderr.lines == ["mainloop: caught unexpected SystemExit!"] else: - assert result.stderr.lines == ["Exit: exiting after {}...".format(exc.__name__)] + assert result.stderr.lines == [f"Exit: exiting after {exc.__name__}..."] @pytest.mark.parametrize("returncode", (None, 42)) diff --git a/testing/test_mark.py b/testing/test_mark.py index 5d5e0cf42..b3240b1b1 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -1094,7 +1094,7 @@ def test_marker_expr_eval_failure_handling(testdir, expr): pass """ ) - expected = "ERROR: Wrong expression passed to '-m': {}: *".format(expr) + expected = f"ERROR: Wrong expression passed to '-m': {expr}: *" result = testdir.runpytest(foo, "-m", expr) result.stderr.fnmatch_lines([expected]) assert result.ret == ExitCode.USAGE_ERROR diff --git a/testing/test_meta.py b/testing/test_meta.py index 97b2e1a1a..25a46a35f 100644 --- a/testing/test_meta.py +++ b/testing/test_meta.py @@ -27,6 +27,6 @@ def test_no_warnings(module: str) -> None: subprocess.check_call(( sys.executable, "-W", "error", - "-c", "__import__({!r})".format(module), + "-c", f"__import__({module!r})", )) # fmt: on diff --git a/testing/test_pytester.py b/testing/test_pytester.py index 46fab0ce8..d27000f3b 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -580,20 +580,20 @@ def test_linematcher_no_matching(function) -> None: obtained = str(e.value).splitlines() if function == "no_fnmatch_line": assert obtained == [ - "nomatch: '{}'".format(good_pattern), + f"nomatch: '{good_pattern}'", " and: 'cachedir: .pytest_cache'", " and: 'collecting ... collected 1 item'", " and: ''", - "fnmatch: '{}'".format(good_pattern), + f"fnmatch: '{good_pattern}'", " with: 'show_fixtures_per_test.py OK'", ] else: assert obtained == [ - " nomatch: '{}'".format(good_pattern), + f" nomatch: '{good_pattern}'", " and: 'cachedir: .pytest_cache'", " and: 'collecting ... collected 1 item'", " and: ''", - "re.match: '{}'".format(good_pattern), + f"re.match: '{good_pattern}'", " with: 'show_fixtures_per_test.py OK'", ] diff --git a/testing/test_runner.py b/testing/test_runner.py index d3b7729f7..0101f6823 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -472,7 +472,7 @@ def test_callinfo() -> None: ci2 = runner.CallInfo.from_call(lambda: 0 / 0, "collect") assert ci2.when == "collect" assert not hasattr(ci2, "result") - assert repr(ci2) == "".format(ci2.excinfo) + assert repr(ci2) == f"" assert str(ci2) == repr(ci2) assert ci2.excinfo @@ -481,7 +481,7 @@ def test_callinfo() -> None: assert 0, "assert_msg" ci3 = runner.CallInfo.from_call(raise_assertion, "call") - assert repr(ci3) == "".format(ci3.excinfo) + assert repr(ci3) == f"" assert "\n" not in repr(ci3) diff --git a/testing/test_session.py b/testing/test_session.py index 1800771da..446d764c3 100644 --- a/testing/test_session.py +++ b/testing/test_session.py @@ -348,10 +348,10 @@ def test_rootdir_option_arg(testdir, monkeypatch, path): """ ) - result = testdir.runpytest("--rootdir={}".format(path)) + result = testdir.runpytest(f"--rootdir={path}") result.stdout.fnmatch_lines( [ - "*rootdir: {}/root".format(testdir.tmpdir), + f"*rootdir: {testdir.tmpdir}/root", "root/test_rootdir_option_arg.py *", "*1 passed*", ] diff --git a/testing/test_skipping.py b/testing/test_skipping.py index b32d2267d..bf014e343 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -758,10 +758,7 @@ class TestSkipif: ) result = testdir.runpytest("-s", "-rsxX") result.stdout.fnmatch_lines( - [ - "*{msg1}*test_foo.py*second_condition*".format(msg1=msg1), - "*1 {msg2}*".format(msg2=msg2), - ] + [f"*{msg1}*test_foo.py*second_condition*", f"*1 {msg2}*"] ) assert result.ret == 0 diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 1ff308fa6..490f2df09 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -1725,9 +1725,9 @@ def test_summary_stats( tr._main_color = None print("Based on stats: %s" % stats_arg) - print('Expect summary: "{}"; with color "{}"'.format(exp_line, exp_color)) + print(f'Expect summary: "{exp_line}"; with color "{exp_color}"') (line, color) = tr.build_summary_stats_line() - print('Actually got: "{}"; with color "{}"'.format(line, color)) + print(f'Actually got: "{line}"; with color "{color}"') assert line == exp_line assert color == exp_color @@ -1773,7 +1773,7 @@ class TestClassicOutputStyle: [ "test_one.py .", "test_two.py F", - "sub{}test_three.py .F.".format(os.sep), + f"sub{os.sep}test_three.py .F.", "*2 failed, 3 passed in*", ] ) @@ -1784,9 +1784,9 @@ class TestClassicOutputStyle: [ "test_one.py::test_one PASSED", "test_two.py::test_two FAILED", - "sub{}test_three.py::test_three_1 PASSED".format(os.sep), - "sub{}test_three.py::test_three_2 FAILED".format(os.sep), - "sub{}test_three.py::test_three_3 PASSED".format(os.sep), + f"sub{os.sep}test_three.py::test_three_1 PASSED", + f"sub{os.sep}test_three.py::test_three_2 FAILED", + f"sub{os.sep}test_three.py::test_three_3 PASSED", "*2 failed, 3 passed in*", ] ) @@ -2146,7 +2146,7 @@ def test_line_with_reprcrash(monkeypatch): actual = _get_line_with_reprcrash_message(config, rep(), width) # type: ignore assert actual == expected - if actual != "{} {}".format(mocked_verbose_word, mocked_pos): + if actual != f"{mocked_verbose_word} {mocked_pos}": assert len(actual) <= width assert wcswidth(actual) <= width diff --git a/testing/test_unittest.py b/testing/test_unittest.py index f3f4d4e06..1aa885264 100644 --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -1086,9 +1086,9 @@ def test_error_message_with_parametrized_fixtures(testdir): ) def test_setup_inheritance_skipping(testdir, test_name, expected_outcome): """Issue #4700""" - testdir.copy_example("unittest/{}".format(test_name)) + testdir.copy_example(f"unittest/{test_name}") result = testdir.runpytest() - result.stdout.fnmatch_lines(["* {} in *".format(expected_outcome)]) + result.stdout.fnmatch_lines([f"* {expected_outcome} in *"]) def test_BdbQuit(testdir): diff --git a/testing/test_warnings.py b/testing/test_warnings.py index b7a231094..3af253ecd 100644 --- a/testing/test_warnings.py +++ b/testing/test_warnings.py @@ -712,7 +712,7 @@ class TestStackLevel: file, _, func = location assert "could not load initial conftests" in str(warning.message) - assert "config{sep}__init__.py".format(sep=os.sep) in file + assert f"config{os.sep}__init__.py" in file assert func == "_preparse" @pytest.mark.filterwarnings("default") @@ -748,7 +748,7 @@ class TestStackLevel: file, _, func = location assert "skipped plugin 'some_plugin': thing" in str(warning.message) - assert "config{sep}__init__.py".format(sep=os.sep) in file + assert f"config{os.sep}__init__.py" in file assert func == "_warn_about_skipped_plugins" def test_issue4445_issue5928_mark_generator(self, testdir):