py36+: pyupgrade: py36+

This commit is contained in:
Anthony Sottile 2020-10-02 13:16:22 -07:00
parent b1bcb9fba8
commit 66bd44c13a
66 changed files with 264 additions and 346 deletions

View File

@ -5,12 +5,12 @@ repos:
- id: black - id: black
args: [--safe, --quiet] args: [--safe, --quiet]
- repo: https://github.com/asottile/blacken-docs - repo: https://github.com/asottile/blacken-docs
rev: v1.7.0 rev: v1.8.0
hooks: hooks:
- id: blacken-docs - id: blacken-docs
additional_dependencies: [black==19.10b0] additional_dependencies: [black==19.10b0]
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.1.0 rev: v3.2.0
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
- id: end-of-file-fixer - id: end-of-file-fixer
@ -21,7 +21,7 @@ repos:
exclude: _pytest/(debugging|hookspec).py exclude: _pytest/(debugging|hookspec).py
language_version: python3 language_version: python3
- repo: https://gitlab.com/pycqa/flake8 - repo: https://gitlab.com/pycqa/flake8
rev: 3.8.2 rev: 3.8.3
hooks: hooks:
- id: flake8 - id: flake8
language_version: python3 language_version: python3
@ -29,23 +29,23 @@ repos:
- flake8-typing-imports==1.9.0 - flake8-typing-imports==1.9.0
- flake8-docstrings==1.5.0 - flake8-docstrings==1.5.0
- repo: https://github.com/asottile/reorder_python_imports - repo: https://github.com/asottile/reorder_python_imports
rev: v2.3.0 rev: v2.3.5
hooks: hooks:
- id: reorder-python-imports - id: reorder-python-imports
args: ['--application-directories=.:src', --py3-plus] args: ['--application-directories=.:src', --py36-plus]
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v2.4.4 rev: v2.7.2
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py3-plus] args: [--py36-plus]
- repo: https://github.com/asottile/setup-cfg-fmt - repo: https://github.com/asottile/setup-cfg-fmt
rev: v1.9.0 rev: v1.11.0
hooks: hooks:
- id: setup-cfg-fmt - id: setup-cfg-fmt
# TODO: when upgrading setup-cfg-fmt this can be removed # TODO: when upgrading setup-cfg-fmt this can be removed
args: [--max-py-version=3.9] args: [--max-py-version=3.9]
- repo: https://github.com/pre-commit/mirrors-mypy - 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: hooks:
- id: mypy - id: mypy
files: ^(src/|testing/) files: ^(src/|testing/)

View File

@ -176,7 +176,7 @@ class TestRaises:
def test_reinterpret_fails_with_print_for_the_fun_of_it(self): def test_reinterpret_fails_with_print_for_the_fun_of_it(self):
items = [1, 2, 3] items = [1, 2, 3]
print("items is {!r}".format(items)) print(f"items is {items!r}")
a, b = items.pop() a, b = items.pop()
def test_some_error(self): def test_some_error(self):

View File

@ -11,4 +11,4 @@ def pytest_runtest_setup(item):
return return
mod = item.getparent(pytest.Module).obj mod = item.getparent(pytest.Module).obj
if hasattr(mod, "hello"): if hasattr(mod, "hello"):
print("mod.hello {!r}".format(mod.hello)) print(f"mod.hello {mod.hello!r}")

View File

@ -26,7 +26,7 @@ class Python:
def __init__(self, version, picklefile): def __init__(self, version, picklefile):
self.pythonpath = shutil.which(version) self.pythonpath = shutil.which(version)
if not self.pythonpath: if not self.pythonpath:
pytest.skip("{!r} not found".format(version)) pytest.skip(f"{version!r} not found")
self.picklefile = picklefile self.picklefile = picklefile
def dumps(self, obj): def dumps(self, obj):
@ -69,4 +69,4 @@ class Python:
@pytest.mark.parametrize("obj", [42, {}, {1: 3}]) @pytest.mark.parametrize("obj", [42, {}, {1: 3}])
def test_basic_objects(python1, python2, obj): def test_basic_objects(python1, python2, obj):
python1.dumps(obj) python1.dumps(obj)
python2.load_and_is_true("obj == {}".format(obj)) python2.load_and_is_true(f"obj == {obj}")

View File

@ -40,7 +40,7 @@ class YamlItem(pytest.Item):
) )
def reportinfo(self): def reportinfo(self):
return self.fspath, 0, "usecase: {}".format(self.name) return self.fspath, 0, f"usecase: {self.name}"
class YamlException(Exception): class YamlException(Exception):

View File

@ -17,9 +17,7 @@ def announce(version):
stdout = stdout.decode("utf-8") stdout = stdout.decode("utf-8")
last_version = stdout.strip() last_version = stdout.strip()
stdout = check_output( stdout = check_output(["git", "log", f"{last_version}..HEAD", "--format=%aN"])
["git", "log", "{}..HEAD".format(last_version), "--format=%aN"]
)
stdout = stdout.decode("utf-8") stdout = stdout.decode("utf-8")
contributors = set(stdout.splitlines()) contributors = set(stdout.splitlines())
@ -31,14 +29,10 @@ def announce(version):
Path(__file__).parent.joinpath(template_name).read_text(encoding="UTF-8") Path(__file__).parent.joinpath(template_name).read_text(encoding="UTF-8")
) )
contributors_text = ( contributors_text = "\n".join(f"* {name}" for name in sorted(contributors)) + "\n"
"\n".join("* {}".format(name) for name in sorted(contributors)) + "\n"
)
text = template_text.format(version=version, contributors=contributors_text) text = template_text.format(version=version, contributors=contributors_text)
target = Path(__file__).parent.joinpath( target = Path(__file__).parent.joinpath(f"../doc/en/announce/release-{version}.rst")
"../doc/en/announce/release-{}.rst".format(version)
)
target.write_text(text, encoding="UTF-8") target.write_text(text, encoding="UTF-8")
print(f"{Fore.CYAN}[generate.announce] {Fore.RESET}Generated {target.name}") 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() lines = index_path.read_text(encoding="UTF-8").splitlines()
indent = " " indent = " "
for index, line in enumerate(lines): for index, line in enumerate(lines):
if line.startswith("{}release-".format(indent)): if line.startswith(f"{indent}release-"):
new_line = indent + target.stem new_line = indent + target.stem
if line != new_line: if line != new_line:
lines.insert(index, new_line) lines.insert(index, new_line)
@ -96,7 +90,7 @@ def pre_release(version, *, skip_check_links):
if not skip_check_links: if not skip_check_links:
check_links() check_links()
msg = "Prepare release version {}".format(version) msg = f"Prepare release version {version}"
check_call(["git", "commit", "-a", "-m", msg]) check_call(["git", "commit", "-a", "-m", msg])
print() print()

View File

@ -58,7 +58,7 @@ class Code:
if not hasattr(rawcode, "co_filename"): if not hasattr(rawcode, "co_filename"):
rawcode = getrawcode(rawcode) rawcode = getrawcode(rawcode)
if not isinstance(rawcode, CodeType): 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.filename = rawcode.co_filename
self.firstlineno = rawcode.co_firstlineno - 1 self.firstlineno = rawcode.co_firstlineno - 1
self.name = rawcode.co_name self.name = rawcode.co_name
@ -747,7 +747,7 @@ class FormattedExcinfo:
else: else:
str_repr = safeformat(value) str_repr = safeformat(value)
# if len(str_repr) < 70 or not isinstance(value, (list, tuple, dict)): # 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: # else:
# self._line("%-10s =\\" % (name,)) # self._line("%-10s =\\" % (name,))
# # XXX # # XXX
@ -1056,7 +1056,7 @@ class ReprEntry(TerminalRepr):
# separate indents and source lines that are not failures: we want to # separate indents and source lines that are not failures: we want to
# highlight the code but not the indentation, which may contain markers # highlight the code but not the indentation, which may contain markers
# such as "> assert 0" # such as "> assert 0"
fail_marker = "{} ".format(FormattedExcinfo.fail_marker) fail_marker = f"{FormattedExcinfo.fail_marker} "
indent_size = len(fail_marker) indent_size = len(fail_marker)
indents = [] # type: List[str] indents = [] # type: List[str]
source_lines = [] # type: List[str] source_lines = [] # type: List[str]
@ -1122,7 +1122,7 @@ class ReprFileLocation(TerminalRepr):
if i != -1: if i != -1:
msg = msg[:i] msg = msg[:i]
tw.write(self.path, bold=True, red=True) tw.write(self.path, bold=True, red=True)
tw.line(":{}: {}".format(self.lineno, msg)) tw.line(f":{self.lineno}: {msg}")
@attr.s(eq=False) @attr.s(eq=False)
@ -1142,7 +1142,7 @@ class ReprFuncArgs(TerminalRepr):
if self.args: if self.args:
linesofar = "" linesofar = ""
for name, value in self.args: for name, value in self.args:
ns = "{} = {}".format(name, value) ns = f"{name} = {value}"
if len(ns) + len(linesofar) + 2 > tw.fullwidth: if len(ns) + len(linesofar) + 2 > tw.fullwidth:
if linesofar: if linesofar:
tw.line(linesofar) tw.line(linesofar)

View File

@ -97,7 +97,7 @@ class TerminalWriter:
def markup(self, text: str, **markup: bool) -> str: def markup(self, text: str, **markup: bool) -> str:
for name in markup: for name in markup:
if name not in self._esctable: if name not in self._esctable:
raise ValueError("unknown markup: {!r}".format(name)) raise ValueError(f"unknown markup: {name!r}")
if self.hasmarkup: if self.hasmarkup:
esc = [self._esctable[name] for name, on in markup.items() if on] esc = [self._esctable[name] for name, on in markup.items() if on]
if esc: if esc:
@ -128,7 +128,7 @@ class TerminalWriter:
# N <= (fullwidth - len(title) - 2) // (2*len(sepchar)) # N <= (fullwidth - len(title) - 2) // (2*len(sepchar))
N = max((fullwidth - len(title) - 2) // (2 * len(sepchar)), 1) N = max((fullwidth - len(title) - 2) // (2 * len(sepchar)), 1)
fill = sepchar * N fill = sepchar * N
line = "{} {} {}".format(fill, title, fill) line = f"{fill} {title} {fill}"
else: else:
# we want len(sepchar)*N <= fullwidth # we want len(sepchar)*N <= fullwidth
# i.e. N <= fullwidth // len(sepchar) # i.e. N <= fullwidth // len(sepchar)

View File

@ -48,7 +48,7 @@ assertstate_key = StoreKey["AssertionState"]()
# pytest caches rewritten pycs in pycache dirs # 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_EXT = ".py" + (__debug__ and "c" or "o")
PYC_TAIL = "." + PYTEST_TAG + PYC_EXT PYC_TAIL = "." + PYTEST_TAG + PYC_EXT
@ -149,7 +149,7 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader)
ok = try_makedirs(cache_dir) ok = try_makedirs(cache_dir)
if not ok: if not ok:
write = False 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 cache_name = fn.name[:-3] + PYC_TAIL
pyc = cache_dir / cache_name 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... # to check for a cached pyc. This may not be optimal...
co = _read_pyc(fn, pyc, state.trace) co = _read_pyc(fn, pyc, state.trace)
if co is None: if co is None:
state.trace("rewriting {!r}".format(fn)) state.trace(f"rewriting {fn!r}")
source_stat, co = _rewrite_test(fn, self.config) source_stat, co = _rewrite_test(fn, self.config)
if write: if write:
self._writing_pyc = True self._writing_pyc = True
@ -166,7 +166,7 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader)
finally: finally:
self._writing_pyc = False self._writing_pyc = False
else: else:
state.trace("found cached rewritten pyc for {}".format(fn)) state.trace(f"found cached rewritten pyc for {fn}")
exec(co, module.__dict__) exec(co, module.__dict__)
def _early_rewrite_bailout(self, name: str, state: "AssertionState") -> bool: 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): if self._is_marked_for_rewrite(name, state):
return False return False
state.trace("early skip of rewriting module: {}".format(name)) state.trace(f"early skip of rewriting module: {name}")
return True return True
def _should_rewrite(self, name: str, fn: str, state: "AssertionState") -> bool: def _should_rewrite(self, name: str, fn: str, state: "AssertionState") -> bool:
# always rewrite conftest files # always rewrite conftest files
if os.path.basename(fn) == "conftest.py": 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 return True
if self.session is not None: if self.session is not None:
if self.session.isinitpath(py.path.local(fn)): if self.session.isinitpath(py.path.local(fn)):
state.trace( state.trace(f"matched test file (was specified on cmdline): {fn!r}")
"matched test file (was specified on cmdline): {!r}".format(fn)
)
return True return True
# modules not passed explicitly on the command line are only # 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) fn_path = PurePath(fn)
for pat in self.fnpats: for pat in self.fnpats:
if fnmatch_ex(pat, fn_path): 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 True
return self._is_marked_for_rewrite(name, state) return self._is_marked_for_rewrite(name, state)
@ -237,9 +235,7 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader)
except KeyError: except KeyError:
for marked in self._must_rewrite: for marked in self._must_rewrite:
if name == marked or name.startswith(marked + "."): if name == marked or name.startswith(marked + "."):
state.trace( state.trace(f"matched marked file {name!r} (from {marked!r})")
"matched marked file {!r} (from {!r})".format(name, marked)
)
self._marked_for_rewrite_cache[name] = True self._marked_for_rewrite_cache[name] = True
return True return True
@ -308,7 +304,7 @@ if sys.platform == "win32":
with atomic_write(os.fspath(pyc), mode="wb", overwrite=True) as fp: with atomic_write(os.fspath(pyc), mode="wb", overwrite=True) as fp:
_write_pyc_fp(fp, source_stat, co) _write_pyc_fp(fp, source_stat, co)
except OSError as e: 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 # we ignore any failure to write the cache file
# there are many reasons, permission-denied, pycache dir being a # there are many reasons, permission-denied, pycache dir being a
# file etc. # file etc.
@ -324,20 +320,18 @@ else:
source_stat: os.stat_result, source_stat: os.stat_result,
pyc: Path, pyc: Path,
) -> bool: ) -> bool:
proc_pyc = "{}.{}".format(pyc, os.getpid()) proc_pyc = f"{pyc}.{os.getpid()}"
try: try:
fp = open(proc_pyc, "wb") fp = open(proc_pyc, "wb")
except OSError as e: except OSError as e:
state.trace( state.trace(f"error writing pyc file at {proc_pyc}: errno={e.errno}")
"error writing pyc file at {}: errno={}".format(proc_pyc, e.errno)
)
return False return False
try: try:
_write_pyc_fp(fp, source_stat, co) _write_pyc_fp(fp, source_stat, co)
os.rename(proc_pyc, os.fspath(pyc)) os.rename(proc_pyc, os.fspath(pyc))
except OSError as e: 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 # we ignore any failure to write the cache file
# there are many reasons, permission-denied, pycache dir being a # there are many reasons, permission-denied, pycache dir being a
# file etc. # file etc.
@ -377,7 +371,7 @@ def _read_pyc(
size = stat_result.st_size size = stat_result.st_size
data = fp.read(12) data = fp.read(12)
except OSError as e: except OSError as e:
trace("_read_pyc({}): OSError {}".format(source, e)) trace(f"_read_pyc({source}): OSError {e}")
return None return None
# Check for invalid or out of date pyc file. # Check for invalid or out of date pyc file.
if ( if (
@ -390,7 +384,7 @@ def _read_pyc(
try: try:
co = marshal.load(fp) co = marshal.load(fp)
except Exception as e: except Exception as e:
trace("_read_pyc({}): marshal.load error {}".format(source, e)) trace(f"_read_pyc({source}): marshal.load error {e}")
return None return None
if not isinstance(co, types.CodeType): if not isinstance(co, types.CodeType):
trace("_read_pyc(%s): not a code object" % source) trace("_read_pyc(%s): not a code object" % source)
@ -982,7 +976,7 @@ class AssertionRewriter(ast.NodeVisitor):
symbol = BINOP_MAP[binop.op.__class__] symbol = BINOP_MAP[binop.op.__class__]
left_expr, left_expl = self.visit(binop.left) left_expr, left_expl = self.visit(binop.left)
right_expr, right_expl = self.visit(binop.right) 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)) res = self.assign(ast.BinOp(left_expr, binop.op, right_expr))
return res, explanation return res, explanation
@ -1007,7 +1001,7 @@ class AssertionRewriter(ast.NodeVisitor):
new_call = ast.Call(new_func, new_args, new_kwargs) new_call = ast.Call(new_func, new_args, new_kwargs)
res = self.assign(new_call) res = self.assign(new_call)
res_expl = self.explanation_param(self.display(res)) 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 return res, outer_expl
def visit_Starred(self, starred: ast.Starred) -> Tuple[ast.Starred, str]: def visit_Starred(self, starred: ast.Starred) -> Tuple[ast.Starred, str]:
@ -1030,7 +1024,7 @@ class AssertionRewriter(ast.NodeVisitor):
self.push_format_context() self.push_format_context()
left_res, left_expl = self.visit(comp.left) left_res, left_expl = self.visit(comp.left)
if isinstance(comp.left, (ast.Compare, ast.BoolOp)): 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))] res_variables = [self.variable() for i in range(len(comp.ops))]
load_names = [ast.Name(v, ast.Load()) for v in res_variables] load_names = [ast.Name(v, ast.Load()) for v in res_variables]
store_names = [ast.Name(v, ast.Store()) 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: for i, op, next_operand in it:
next_res, next_expl = self.visit(next_operand) next_res, next_expl = self.visit(next_operand)
if isinstance(next_operand, (ast.Compare, ast.BoolOp)): if isinstance(next_operand, (ast.Compare, ast.BoolOp)):
next_expl = "({})".format(next_expl) next_expl = f"({next_expl})"
results.append(next_res) results.append(next_res)
sym = BINOP_MAP[op.__class__] sym = BINOP_MAP[op.__class__]
syms.append(ast.Str(sym)) syms.append(ast.Str(sym))
expl = "{} {} {}".format(left_expl, sym, next_expl) expl = f"{left_expl} {sym} {next_expl}"
expls.append(ast.Str(expl)) expls.append(ast.Str(expl))
res_expr = ast.Compare(left_res, [op], [next_res]) res_expr = ast.Compare(left_res, [op], [next_res])
self.statements.append(ast.Assign([store_names[i]], res_expr)) self.statements.append(ast.Assign([store_names[i]], res_expr))

View File

@ -70,10 +70,10 @@ def _truncate_explanation(
truncated_line_count += 1 # Account for the part-truncated final line truncated_line_count += 1 # Account for the part-truncated final line
msg = "...Full output truncated" msg = "...Full output truncated"
if truncated_line_count == 1: if truncated_line_count == 1:
msg += " ({} line hidden)".format(truncated_line_count) msg += f" ({truncated_line_count} line hidden)"
else: else:
msg += " ({} lines hidden)".format(truncated_line_count) msg += f" ({truncated_line_count} lines hidden)"
msg += ", {}".format(USAGE_MSG) msg += f", {USAGE_MSG}"
truncated_explanation.extend(["", str(msg)]) truncated_explanation.extend(["", str(msg)])
return truncated_explanation return truncated_explanation

View File

@ -142,7 +142,7 @@ def assertrepr_compare(config, op: str, left: Any, right: Any) -> Optional[List[
left_repr = saferepr(left, maxsize=maxsize) left_repr = saferepr(left, maxsize=maxsize)
right_repr = saferepr(right, maxsize=maxsize) right_repr = saferepr(right, maxsize=maxsize)
summary = "{} {} {}".format(left_repr, op, right_repr) summary = f"{left_repr} {op} {right_repr}"
explanation = None explanation = None
try: try:
@ -316,9 +316,7 @@ def _compare_eq_sequence(
left_value = left[i] left_value = left[i]
right_value = right[i] right_value = right[i]
explanation += [ explanation += [f"At index {i} diff: {left_value!r} != {right_value!r}"]
"At index {} diff: {!r} != {!r}".format(i, left_value, right_value)
]
break break
if comparing_bytes: if comparing_bytes:
@ -338,9 +336,7 @@ def _compare_eq_sequence(
extra = saferepr(right[len_left]) extra = saferepr(right[len_left])
if len_diff == 1: if len_diff == 1:
explanation += [ explanation += [f"{dir_with_more} contains one more item: {extra}"]
"{} contains one more item: {}".format(dir_with_more, extra)
]
else: else:
explanation += [ explanation += [
"%s contains %d more items, first extra item: %s" "%s contains %d more items, first extra item: %s"

View File

@ -500,7 +500,7 @@ def pytest_report_header(config: Config) -> Optional[str]:
displaypath = cachedir.relative_to(config.rootpath) displaypath = cachedir.relative_to(config.rootpath)
except ValueError: except ValueError:
displaypath = cachedir displaypath = cachedir
return "cachedir: {}".format(displaypath) return f"cachedir: {displaypath}"
return None return None
@ -542,5 +542,5 @@ def cacheshow(config: Config, session: Session) -> int:
# print("%s/" % p.relto(basedir)) # print("%s/" % p.relto(basedir))
if p.is_file(): if p.is_file():
key = str(p.relative_to(basedir)) 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 return 0

View File

@ -544,7 +544,7 @@ class CaptureResult(Generic[AnyStr]):
return tuple(self) < tuple(other) return tuple(self) < tuple(other)
def __repr__(self) -> str: 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]): class MultiCapture(Generic[AnyStr]):
@ -638,7 +638,7 @@ def _get_multicapture(method: "_CaptureMethod") -> MultiCapture[str]:
return MultiCapture( return MultiCapture(
in_=None, out=SysCapture(1, tee=True), err=SysCapture(2, tee=True) 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 # CaptureManager and CaptureFixture

View File

@ -143,8 +143,7 @@ def getfuncargnames(
parameters = signature(function).parameters parameters = signature(function).parameters
except (ValueError, TypeError) as e: except (ValueError, TypeError) as e:
fail( fail(
"Could not determine arguments of {!r}: {}".format(function, e), f"Could not determine arguments of {function!r}: {e}", pytrace=False,
pytrace=False,
) )
arg_names = tuple( arg_names = tuple(
@ -197,7 +196,7 @@ def get_default_arg_names(function: Callable[..., Any]) -> Tuple[str, ...]:
_non_printable_ascii_translate_table = { _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( _non_printable_ascii_translate_table.update(
{ord("\t"): "\\t", ord("\r"): "\\r", ord("\n"): "\\n"} {ord("\t"): "\\t", ord("\r"): "\\r", ord("\n"): "\\n"}

View File

@ -144,9 +144,7 @@ def main(
except ConftestImportFailure as e: except ConftestImportFailure as e:
exc_info = ExceptionInfo(e.excinfo) exc_info = ExceptionInfo(e.excinfo)
tw = TerminalWriter(sys.stderr) tw = TerminalWriter(sys.stderr)
tw.line( tw.line(f"ImportError while loading conftest '{e.path}'.", red=True)
"ImportError while loading conftest '{e.path}'.".format(e=e), red=True
)
exc_info.traceback = exc_info.traceback.filter( exc_info.traceback = exc_info.traceback.filter(
filter_traceback_for_conftest_import_failure filter_traceback_for_conftest_import_failure
) )
@ -173,7 +171,7 @@ def main(
except UsageError as e: except UsageError as e:
tw = TerminalWriter(sys.stderr) tw = TerminalWriter(sys.stderr)
for msg in e.args: 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 return ExitCode.USAGE_ERROR
@ -206,7 +204,7 @@ def filename_arg(path: str, optname: str) -> str:
:optname: Name of the option. :optname: Name of the option.
""" """
if os.path.isdir(path): 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 return path
@ -217,7 +215,7 @@ def directory_arg(path: str, optname: str) -> str:
:optname: Name of the option. :optname: Name of the option.
""" """
if not os.path.isdir(path): 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 return path
@ -583,7 +581,7 @@ class PytestPluginManager(PluginManager):
if path and path.relto(dirpath) or path == dirpath: if path and path.relto(dirpath) or path == dirpath:
assert mod not in mods assert mod not in mods
mods.append(mod) mods.append(mod)
self.trace("loading conftestmodule {!r}".format(mod)) self.trace(f"loading conftestmodule {mod!r}")
self.consider_conftest(mod) self.consider_conftest(mod)
return mod return mod
@ -889,7 +887,7 @@ class Config:
_a = FILE_OR_DIR _a = FILE_OR_DIR
self._parser = Parser( self._parser = Parser(
usage="%(prog)s [options] [{}] [{}] [...]".format(_a, _a), usage=f"%(prog)s [options] [{_a}] [{_a}] [...]",
processopt=self._processopt, processopt=self._processopt,
) )
self.pluginmanager = pluginmanager self.pluginmanager = pluginmanager
@ -1191,9 +1189,7 @@ class Config:
# we don't want to prevent --help/--version to work # we don't want to prevent --help/--version to work
# so just let is pass and print a warning at the end # so just let is pass and print a warning at the end
self.issue_config_time_warning( self.issue_config_time_warning(
PytestConfigWarning( PytestConfigWarning(f"could not load initial conftests: {e.path}"),
"could not load initial conftests: {}".format(e.path)
),
stacklevel=2, stacklevel=2,
) )
else: else:
@ -1227,7 +1223,7 @@ class Config:
def _validate_config_options(self) -> None: def _validate_config_options(self) -> None:
for key in sorted(self._get_unknown_ini_keys()): 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: def _validate_plugins(self) -> None:
required_plugins = sorted(self.getini("required_plugins")) required_plugins = sorted(self.getini("required_plugins"))
@ -1362,7 +1358,7 @@ class Config:
try: try:
description, type, default = self._parser._inidict[name] description, type, default = self._parser._inidict[name]
except KeyError as e: 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) override_value = self._get_override_ini_value(name)
if override_value is None: if override_value is None:
try: try:
@ -1467,8 +1463,8 @@ class Config:
if skip: if skip:
import pytest import pytest
pytest.skip("no {!r} option found".format(name)) pytest.skip(f"no {name!r} option found")
raise ValueError("no option named {!r}".format(name)) from e raise ValueError(f"no option named {name!r}") from e
def getvalue(self, name: str, path=None): def getvalue(self, name: str, path=None):
"""Deprecated, use getoption() instead.""" """Deprecated, use getoption() instead."""
@ -1501,7 +1497,7 @@ class Config:
def _warn_about_skipped_plugins(self) -> None: def _warn_about_skipped_plugins(self) -> None:
for module_name, msg in self.pluginmanager.skipped_plugins: for module_name, msg in self.pluginmanager.skipped_plugins:
self.issue_config_time_warning( self.issue_config_time_warning(
PytestConfigWarning("skipped plugin {!r}: {}".format(module_name, msg)), PytestConfigWarning(f"skipped plugin {module_name!r}: {msg}"),
stacklevel=2, stacklevel=2,
) )
@ -1554,7 +1550,7 @@ def _strtobool(val: str) -> bool:
elif val in ("n", "no", "f", "false", "off", "0"): elif val in ("n", "no", "f", "false", "off", "0"):
return False return False
else: else:
raise ValueError("invalid truth value {!r}".format(val)) raise ValueError(f"invalid truth value {val!r}")
@lru_cache(maxsize=50) @lru_cache(maxsize=50)
@ -1568,7 +1564,7 @@ def parse_warning_filter(
""" """
parts = arg.split(":") parts = arg.split(":")
if len(parts) > 5: 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: while len(parts) < 5:
parts.append("") parts.append("")
action_, message, category_, module, lineno_ = [s.strip() for s in parts] action_, message, category_, module, lineno_ = [s.strip() for s in parts]
@ -1584,7 +1580,7 @@ def parse_warning_filter(
if lineno < 0: if lineno < 0:
raise ValueError raise ValueError
except (ValueError, OverflowError) as e: 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: else:
lineno = 0 lineno = 0
return action, message, category, module, lineno return action, message, category, module, lineno

View File

@ -188,7 +188,7 @@ class ArgumentError(Exception):
def __str__(self) -> str: def __str__(self) -> str:
if self.option_id: if self.option_id:
return "option {}: {}".format(self.option_id, self.msg) return f"option {self.option_id}: {self.msg}"
else: else:
return self.msg return self.msg
@ -389,11 +389,11 @@ class MyOptionParser(argparse.ArgumentParser):
def error(self, message: str) -> "NoReturn": def error(self, message: str) -> "NoReturn":
"""Transform argparse error message into UsageError.""" """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"): if hasattr(self._parser, "_config_source_hint"):
# Type ignored because the attribute is set dynamically. # 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) raise UsageError(self.format_usage() + msg)
@ -410,7 +410,7 @@ class MyOptionParser(argparse.ArgumentParser):
if arg and arg[0] == "-": if arg and arg[0] == "-":
lines = ["unrecognized arguments: %s" % (" ".join(unrecognized))] lines = ["unrecognized arguments: %s" % (" ".join(unrecognized))]
for k, v in sorted(self.extra_info.items()): for k, v in sorted(self.extra_info.items()):
lines.append(" {}: {}".format(k, v)) lines.append(f" {k}: {v}")
self.error("\n".join(lines)) self.error("\n".join(lines))
getattr(parsed, FILE_OR_DIR).extend(unrecognized) getattr(parsed, FILE_OR_DIR).extend(unrecognized)
return parsed return parsed

View File

@ -35,7 +35,7 @@ def _validate_usepdb_cls(value: str) -> Tuple[str, str]:
modname, classname = value.split(":") modname, classname = value.split(":")
except ValueError as e: except ValueError as e:
raise argparse.ArgumentTypeError( 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 ) from e
return (modname, classname) return (modname, classname)
@ -136,7 +136,7 @@ class pytestPDB:
except Exception as exc: except Exception as exc:
value = ":".join((modname, classname)) value = ":".join((modname, classname))
raise UsageError( raise UsageError(
"--pdbcls: could not import {!r}: {}".format(value, exc) f"--pdbcls: could not import {value!r}: {exc}"
) from exc ) from exc
else: else:
import pdb import pdb
@ -257,7 +257,7 @@ class pytestPDB:
else: else:
capturing = cls._is_capturing(capman) capturing = cls._is_capturing(capman)
if capturing == "global": if capturing == "global":
tw.sep(">", "PDB {} (IO-capturing turned off)".format(method)) tw.sep(">", f"PDB {method} (IO-capturing turned off)")
elif capturing: elif capturing:
tw.sep( tw.sep(
">", ">",
@ -265,7 +265,7 @@ class pytestPDB:
% (method, capturing), % (method, capturing),
) )
else: else:
tw.sep(">", "PDB {}".format(method)) tw.sep(">", f"PDB {method}")
_pdb = cls._import_pdb_cls(capman)(**kwargs) _pdb = cls._import_pdb_cls(capman)(**kwargs)

View File

@ -349,7 +349,7 @@ class DoctestItem(pytest.Item):
] ]
indent = ">>>" indent = ">>>"
for line in example.source.splitlines(): for line in example.source.splitlines():
lines.append("??? {} {}".format(indent, line)) lines.append(f"??? {indent} {line}")
indent = "..." indent = "..."
if isinstance(failure, doctest.DocTestFailure): if isinstance(failure, doctest.DocTestFailure):
lines += checker.output_difference( lines += checker.output_difference(

View File

@ -469,7 +469,7 @@ class FixtureRequest:
"""Test function object if the request has a per-function scope.""" """Test function object if the request has a per-function scope."""
if self.scope != "function": if self.scope != "function":
raise AttributeError( raise AttributeError(
"function not available in {}-scoped context".format(self.scope) f"function not available in {self.scope}-scoped context"
) )
return self._pyfuncitem.obj return self._pyfuncitem.obj
@ -477,9 +477,7 @@ class FixtureRequest:
def cls(self): def cls(self):
"""Class (can be None) where the test function was collected.""" """Class (can be None) where the test function was collected."""
if self.scope not in ("class", "function"): if self.scope not in ("class", "function"):
raise AttributeError( raise AttributeError(f"cls not available in {self.scope}-scoped context")
"cls not available in {}-scoped context".format(self.scope)
)
clscol = self._pyfuncitem.getparent(_pytest.python.Class) clscol = self._pyfuncitem.getparent(_pytest.python.Class)
if clscol: if clscol:
return clscol.obj return clscol.obj
@ -498,18 +496,14 @@ class FixtureRequest:
def module(self): def module(self):
"""Python module object where the test function was collected.""" """Python module object where the test function was collected."""
if self.scope not in ("function", "class", "module"): if self.scope not in ("function", "class", "module"):
raise AttributeError( raise AttributeError(f"module not available in {self.scope}-scoped context")
"module not available in {}-scoped context".format(self.scope)
)
return self._pyfuncitem.getparent(_pytest.python.Module).obj return self._pyfuncitem.getparent(_pytest.python.Module).obj
@property @property
def fspath(self) -> py.path.local: def fspath(self) -> py.path.local:
"""The file system path of the test module which collected this test.""" """The file system path of the test module which collected this test."""
if self.scope not in ("function", "class", "module", "package"): if self.scope not in ("function", "class", "module", "package"):
raise AttributeError( raise AttributeError(f"module not available in {self.scope}-scoped context")
"module not available in {}-scoped context".format(self.scope)
)
# TODO: Remove ignore once _pyfuncitem is properly typed. # TODO: Remove ignore once _pyfuncitem is properly typed.
return self._pyfuncitem.fspath # type: ignore return self._pyfuncitem.fspath # type: ignore
@ -757,7 +751,7 @@ class SubRequest(FixtureRequest):
self._fixturemanager = request._fixturemanager self._fixturemanager = request._fixturemanager
def __repr__(self) -> str: def __repr__(self) -> str:
return "<SubRequest {!r} for {!r}>".format(self.fixturename, self._pyfuncitem) return f"<SubRequest {self.fixturename!r} for {self._pyfuncitem!r}>"
def addfinalizer(self, finalizer: Callable[[], object]) -> None: def addfinalizer(self, finalizer: Callable[[], object]) -> None:
self._fixturedef.addfinalizer(finalizer) self._fixturedef.addfinalizer(finalizer)
@ -792,7 +786,7 @@ def scope2index(scope: str, descr: str, where: Optional[str] = None) -> int:
except ValueError: except ValueError:
fail( fail(
"{} {}got an unexpected scope value '{}'".format( "{} {}got an unexpected scope value '{}'".format(
descr, "from {} ".format(where) if where else "", scope descr, f"from {where} " if where else "", scope
), ),
pytrace=False, pytrace=False,
) )
@ -848,7 +842,7 @@ class FixtureLookupError(LookupError):
self.argname self.argname
) )
else: 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 available fixtures: {}".format(", ".join(sorted(available)))
msg += "\n use 'pytest --fixtures [testpath]' for help on them." msg += "\n use 'pytest --fixtures [testpath]' for help on them."
@ -882,8 +876,7 @@ class FixtureLookupErrorRepr(TerminalRepr):
) )
for line in lines[1:]: for line in lines[1:]:
tw.line( tw.line(
"{} {}".format(FormattedExcinfo.flow_marker, line.strip()), f"{FormattedExcinfo.flow_marker} {line.strip()}", red=True,
red=True,
) )
tw.line() tw.line()
tw.line("%s:%d" % (self.filename, self.firstlineno + 1)) tw.line("%s:%d" % (self.filename, self.firstlineno + 1))
@ -907,9 +900,7 @@ def call_fixture_func(
try: try:
fixture_result = next(generator) fixture_result = next(generator)
except StopIteration: except StopIteration:
raise ValueError( raise ValueError(f"{request.fixturename} did not yield a value") from None
"{} did not yield a value".format(request.fixturename)
) from None
finalizer = functools.partial(_teardown_yield_fixture, fixturefunc, generator) finalizer = functools.partial(_teardown_yield_fixture, fixturefunc, generator)
request.addfinalizer(finalizer) request.addfinalizer(finalizer)
else: else:
@ -987,7 +978,7 @@ class FixtureDef(Generic[_FixtureValue]):
self.scopenum = scope2index( self.scopenum = scope2index(
# TODO: Check if the `or` here is really necessary. # TODO: Check if the `or` here is really necessary.
scope_ or "function", # type: ignore[unreachable] scope_ or "function", # type: ignore[unreachable]
descr="Fixture '{}'".format(func.__name__), descr=f"Fixture '{func.__name__}'",
where=baseid, where=baseid,
) )
self.scope = scope_ self.scope = scope_

View File

@ -137,7 +137,7 @@ def showversion(config: Config) -> None:
for line in plugininfo: for line in plugininfo:
sys.stderr.write(line + "\n") sys.stderr.write(line + "\n")
else: 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]]: def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]:
@ -172,8 +172,8 @@ def showhelp(config: Config) -> None:
if type is None: if type is None:
type = "string" type = "string"
if help is None: if help is None:
raise TypeError("help argument cannot be None for {}".format(name)) raise TypeError(f"help argument cannot be None for {name}")
spec = "{} ({}):".format(name, type) spec = f"{name} ({type}):"
tw.write(" %s" % spec) tw.write(" %s" % spec)
spec_len = len(spec) spec_len = len(spec)
if spec_len > (indent_len - 3): 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"), ("PYTEST_DEBUG", "set to enable debug tracing of pytest's internals"),
] ]
for name, help in vars: for name, help in vars:
tw.line(" {:<24} {}".format(name, help)) tw.line(f" {name:<24} {help}")
tw.line() tw.line()
tw.line() tw.line()
@ -235,7 +235,7 @@ def getpluginversioninfo(config: Config) -> List[str]:
lines.append("setuptools registered plugins:") lines.append("setuptools registered plugins:")
for plugin, dist in plugininfo: for plugin, dist in plugininfo:
loc = getattr(plugin, "__file__", repr(plugin)) 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) lines.append(" " + content)
return lines return lines
@ -243,9 +243,7 @@ def getpluginversioninfo(config: Config) -> List[str]:
def pytest_report_header(config: Config) -> List[str]: def pytest_report_header(config: Config) -> List[str]:
lines = [] lines = []
if config.option.debug or config.option.traceconfig: if config.option.debug or config.option.traceconfig:
lines.append( lines.append(f"using: pytest-{pytest.__version__} pylib-{py.__version__}")
"using: pytest-{} pylib-{}".format(pytest.__version__, py.__version__)
)
verinfo = getpluginversioninfo(config) verinfo = getpluginversioninfo(config)
if verinfo: if verinfo:
@ -259,5 +257,5 @@ def pytest_report_header(config: Config) -> List[str]:
r = plugin.__file__ r = plugin.__file__
else: else:
r = repr(plugin) r = repr(plugin)
lines.append(" {:<20}: {}".format(name, r)) lines.append(f" {name:<20}: {r}")
return lines return lines

View File

@ -228,9 +228,9 @@ class _NodeReporter:
reason = str(report.longrepr) reason = str(report.longrepr)
if report.when == "teardown": if report.when == "teardown":
msg = 'failed on teardown with "{}"'.format(reason) msg = f'failed on teardown with "{reason}"'
else: else:
msg = 'failed on setup with "{}"'.format(reason) msg = f'failed on setup with "{reason}"'
self._add_simple("error", msg, str(report.longrepr)) self._add_simple("error", msg, str(report.longrepr))
def append_skipped(self, report: TestReport) -> None: def append_skipped(self, report: TestReport) -> None:
@ -246,7 +246,7 @@ class _NodeReporter:
filename, lineno, skipreason = report.longrepr filename, lineno, skipreason = report.longrepr
if skipreason.startswith("Skipped: "): if skipreason.startswith("Skipped: "):
skipreason = skipreason[9:] skipreason = skipreason[9:]
details = "{}:{}: {}".format(filename, lineno, skipreason) details = f"{filename}:{lineno}: {skipreason}"
skipped = ET.Element("skipped", type="pytest.skip", message=skipreason) skipped = ET.Element("skipped", type="pytest.skip", message=skipreason)
skipped.text = bin_xml_escape(details) skipped.text = bin_xml_escape(details)
@ -683,7 +683,7 @@ class LogXML:
logfile.close() logfile.close()
def pytest_terminal_summary(self, terminalreporter: TerminalReporter) -> None: 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: def add_global_property(self, name: str, value: object) -> None:
__tracebackhide__ = True __tracebackhide__ = True

View File

@ -267,9 +267,7 @@ def wrap_session(
if excinfo.value.returncode is not None: if excinfo.value.returncode is not None:
exitstatus = excinfo.value.returncode exitstatus = excinfo.value.returncode
if initstate < 2: if initstate < 2:
sys.stderr.write( sys.stderr.write(f"{excinfo.typename}: {excinfo.value.msg}\n")
"{}: {}\n".format(excinfo.typename, excinfo.value.msg)
)
config.hook.pytest_keyboard_interrupt(excinfo=excinfo) config.hook.pytest_keyboard_interrupt(excinfo=excinfo)
session.exitstatus = exitstatus session.exitstatus = exitstatus
except BaseException: except BaseException:
@ -615,8 +613,8 @@ class Session(nodes.FSCollector):
if self._notfound: if self._notfound:
errors = [] errors = []
for arg, cols in self._notfound: for arg, cols in self._notfound:
line = "(no name {!r} in any of {!r})".format(arg, cols) line = f"(no name {arg!r} in any of {cols!r})"
errors.append("not found: {}\n{}".format(arg, line)) errors.append(f"not found: {arg}\n{line}")
raise UsageError(*errors) raise UsageError(*errors)
if not genitems: if not genitems:
items = rep.result items = rep.result

View File

@ -201,7 +201,7 @@ def deselect_by_keyword(items: "List[Item]", config: Config) -> None:
expression = Expression.compile(keywordexpr) expression = Expression.compile(keywordexpr)
except ParseError as e: except ParseError as e:
raise UsageError( raise UsageError(
"Wrong expression passed to '-k': {}: {}".format(keywordexpr, e) f"Wrong expression passed to '-k': {keywordexpr}: {e}"
) from None ) from None
remaining = [] remaining = []
@ -245,9 +245,7 @@ def deselect_by_mark(items: "List[Item]", config: Config) -> None:
try: try:
expression = Expression.compile(matchexpr) expression = Expression.compile(matchexpr)
except ParseError as e: except ParseError as e:
raise UsageError( raise UsageError(f"Wrong expression passed to '-m': {matchexpr}: {e}") from None
"Wrong expression passed to '-m': {}: {}".format(matchexpr, e)
) from None
remaining = [] remaining = []
deselected = [] deselected = []

View File

@ -66,7 +66,7 @@ class ParseError(Exception):
self.message = message self.message = message
def __str__(self) -> str: def __str__(self) -> str:
return "at column {}: {}".format(self.column, self.message) return f"at column {self.column}: {self.message}"
class Scanner: class Scanner:

View File

@ -310,7 +310,7 @@ class MarkDecorator:
return self.name # for backward-compat (2.4.1 had this attr) return self.name # for backward-compat (2.4.1 had this attr)
def __repr__(self) -> str: def __repr__(self) -> str:
return "<MarkDecorator {!r}>".format(self.mark) return f"<MarkDecorator {self.mark!r}>"
def with_args(self, *args: object, **kwargs: object) -> "MarkDecorator": def with_args(self, *args: object, **kwargs: object) -> "MarkDecorator":
"""Return a MarkDecorator with extra arguments added. """Return a MarkDecorator with extra arguments added.
@ -364,7 +364,7 @@ def normalize_mark_list(mark_list: Iterable[Union[Mark, MarkDecorator]]) -> List
] # unpack MarkDecorator ] # unpack MarkDecorator
for mark in extracted: for mark in extracted:
if not isinstance(mark, Mark): 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)] return [x for x in extracted if isinstance(x, Mark)]
@ -498,14 +498,14 @@ class MarkGenerator:
if name not in self._markers: if name not in self._markers:
if self._config.option.strict_markers: if self._config.option.strict_markers:
fail( fail(
"{!r} not found in `markers` configuration option".format(name), f"{name!r} not found in `markers` configuration option",
pytrace=False, pytrace=False,
) )
# Raise a specific error for common misspellings of "parametrize". # Raise a specific error for common misspellings of "parametrize".
if name in ["parameterize", "parametrise", "parameterise"]: if name in ["parameterize", "parametrise", "parameterise"]:
__tracebackhide__ = True __tracebackhide__ = True
fail("Unknown '{}' mark, did you mean 'parametrize'?".format(name)) fail(f"Unknown '{name}' mark, did you mean 'parametrize'?")
warnings.warn( warnings.warn(
"Unknown pytest.mark.%s - is this a typo? You can register " "Unknown pytest.mark.%s - is this a typo? You can register "
@ -556,4 +556,4 @@ class NodeKeywords(MutableMapping[str, Any]):
return len(self._seen()) return len(self._seen())
def __repr__(self) -> str: def __repr__(self) -> str:
return "<NodeKeywords for node {}>".format(self.node) return f"<NodeKeywords for node {self.node}>"

View File

@ -74,7 +74,7 @@ def resolve(name: str) -> object:
if expected == used: if expected == used:
raise raise
else: 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) found = annotated_getattr(found, part, used)
return found 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]: 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] if not isinstance(import_path, str) or "." not in import_path: # type: ignore[unreachable]
raise TypeError( raise TypeError(f"must be absolute import path string, not {import_path!r}")
"must be absolute import path string, not {!r}".format(import_path)
)
module, attr = import_path.rsplit(".", 1) module, attr = import_path.rsplit(".", 1)
target = resolve(module) target = resolve(module)
if raising: if raising:
@ -202,7 +200,7 @@ class MonkeyPatch:
oldval = getattr(target, name, notset) oldval = getattr(target, name, notset)
if raising and oldval is 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 # avoid class descriptors like staticmethod/classmethod
if inspect.isclass(target): if inspect.isclass(target):

View File

@ -40,7 +40,7 @@ class OutcomeException(BaseException):
def __repr__(self) -> str: def __repr__(self) -> str:
if self.msg: if self.msg:
return self.msg return self.msg
return "<{} instance>".format(self.__class__.__name__) return f"<{self.__class__.__name__} instance>"
__str__ = __repr__ __str__ = __repr__
@ -208,7 +208,7 @@ def importorskip(
__import__(modname) __import__(modname)
except ImportError as exc: except ImportError as exc:
if reason is None: 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 raise Skipped(reason, allow_module_level=True) from None
mod = sys.modules[modname] mod = sys.modules[modname]
if minversion is None: if minversion is None:

View File

@ -107,4 +107,4 @@ def pytest_terminal_summary(terminalreporter: TerminalReporter) -> None:
s = file.getvalue() s = file.getvalue()
assert len(s) assert len(s)
pastebinurl = create_new_paste(s) pastebinurl = create_new_paste(s)
terminalreporter.write_line("{} --> {}".format(msg, pastebinurl)) terminalreporter.write_line(f"{msg} --> {pastebinurl}")

View File

@ -63,9 +63,7 @@ def on_rm_rf_error(func, path: str, exc, *, start_path: Path) -> bool:
if not isinstance(excvalue, PermissionError): if not isinstance(excvalue, PermissionError):
warnings.warn( warnings.warn(
PytestWarning( PytestWarning(f"(rm_rf) error removing {path}\n{exctype}: {excvalue}")
"(rm_rf) error removing {}\n{}: {}".format(path, exctype, excvalue)
)
) )
return False return False
@ -200,7 +198,7 @@ def make_numbered_dir(root: Path, prefix: str) -> Path:
# try up to 10 times to create the folder # try up to 10 times to create the folder
max_existing = max(map(parse_num, find_suffixes(root, prefix)), default=-1) max_existing = max(map(parse_num, find_suffixes(root, prefix)), default=-1)
new_number = max_existing + 1 new_number = max_existing + 1
new_path = root.joinpath("{}{}".format(prefix, new_number)) new_path = root.joinpath(f"{prefix}{new_number}")
try: try:
new_path.mkdir() new_path.mkdir()
except Exception: except Exception:
@ -221,7 +219,7 @@ def create_cleanup_lock(p: Path) -> Path:
try: try:
fd = os.open(str(lock_path), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644) fd = os.open(str(lock_path), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644)
except FileExistsError as e: 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: else:
pid = os.getpid() pid = os.getpid()
spid = str(pid).encode() spid = str(pid).encode()
@ -258,7 +256,7 @@ def maybe_delete_a_numbered_dir(path: Path) -> None:
lock_path = create_cleanup_lock(path) lock_path = create_cleanup_lock(path)
parent = path.parent parent = path.parent
garbage = parent.joinpath("garbage-{}".format(uuid.uuid4())) garbage = parent.joinpath(f"garbage-{uuid.uuid4()}")
path.rename(garbage) path.rename(garbage)
rm_rf(garbage) rm_rf(garbage)
except OSError: except OSError:
@ -401,7 +399,7 @@ def fnmatch_ex(pattern: str, path) -> bool:
else: else:
name = str(path) name = str(path)
if path.is_absolute() and not os.path.isabs(pattern): 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) return fnmatch.fnmatch(name, pattern)
@ -415,7 +413,7 @@ def symlink_or_skip(src, dst, **kwargs):
try: try:
os.symlink(str(src), str(dst), **kwargs) os.symlink(str(src), str(dst), **kwargs)
except OSError as e: except OSError as e:
skip("symlinks not supported: {}".format(e)) skip(f"symlinks not supported: {e}")
class ImportMode(Enum): class ImportMode(Enum):

View File

@ -197,7 +197,7 @@ class ParsedCall:
def __repr__(self) -> str: def __repr__(self) -> str:
d = self.__dict__.copy() d = self.__dict__.copy()
del d["_name"] del d["_name"]
return "<ParsedCall {!r}(**{!r})>".format(self._name, d) return f"<ParsedCall {self._name!r}(**{d!r})>"
if TYPE_CHECKING: if TYPE_CHECKING:
# The class has undetermined attributes, this tells mypy about it. # The class has undetermined attributes, this tells mypy about it.
@ -252,7 +252,7 @@ class HookRecorder:
break break
print("NONAMEMATCH", name, "with", call) print("NONAMEMATCH", name, "with", call)
else: 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: def popcall(self, name: str) -> ParsedCall:
__tracebackhide__ = True __tracebackhide__ = True
@ -260,7 +260,7 @@ class HookRecorder:
if call._name == name: if call._name == name:
del self.calls[i] del self.calls[i]
return call 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]) lines.extend([" %s" % x for x in self.calls])
pytest.fail("\n".join(lines)) pytest.fail("\n".join(lines))
@ -388,7 +388,7 @@ class HookRecorder:
elif rep.skipped: elif rep.skipped:
skipped.append(rep) skipped.append(rep)
else: else:
assert rep.failed, "Unexpected outcome: {!r}".format(rep) assert rep.failed, f"Unexpected outcome: {rep!r}"
failed.append(rep) failed.append(rep)
return passed, skipped, failed return passed, skipped, failed
@ -658,7 +658,7 @@ class Testdir:
mp.setenv("PY_COLORS", "0") mp.setenv("PY_COLORS", "0")
def __repr__(self) -> str: def __repr__(self) -> str:
return "<Testdir {!r}>".format(self.tmpdir) return f"<Testdir {self.tmpdir!r}>"
def __str__(self) -> str: def __str__(self) -> str:
return str(self.tmpdir) return str(self.tmpdir)
@ -874,7 +874,7 @@ class Testdir:
return result return result
else: else:
raise LookupError( 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 Session = Session
@ -1087,7 +1087,7 @@ class Testdir:
return self.runpytest_inprocess(*args, **kwargs) return self.runpytest_inprocess(*args, **kwargs)
elif self._method == "subprocess": elif self._method == "subprocess":
return self.runpytest_subprocess(*args, **kwargs) 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): def _ensure_basetemp(self, args):
args = list(args) args = list(args)
@ -1329,7 +1329,7 @@ class Testdir:
for line in lines: for line in lines:
print(line, file=fp) print(line, file=fp)
except UnicodeEncodeError: 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, ...]: def _getpytestargs(self) -> Tuple[str, ...]:
return sys.executable, "-mpytest" return sys.executable, "-mpytest"
@ -1386,7 +1386,7 @@ class Testdir:
""" """
basetemp = self.tmpdir.mkdir("temp-pexpect") basetemp = self.tmpdir.mkdir("temp-pexpect")
invoke = " ".join(map(str, self._getpytestargs())) 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) return self.spawn(cmd, expect_timeout=expect_timeout)
def spawn(self, cmd: str, expect_timeout: float = 10.0) -> "pexpect.spawn": def spawn(self, cmd: str, expect_timeout: float = 10.0) -> "pexpect.spawn":
@ -1573,7 +1573,7 @@ class LineMatcher:
break break
else: else:
if consecutive and started: if consecutive and started:
msg = "no consecutive match: {!r}".format(line) msg = f"no consecutive match: {line!r}"
self._log(msg) self._log(msg)
self._log( self._log(
"{:>{width}}".format("with:", width=wnick), repr(nextline) "{:>{width}}".format("with:", width=wnick), repr(nextline)
@ -1587,7 +1587,7 @@ class LineMatcher:
self._log("{:>{width}}".format("and:", width=wnick), repr(nextline)) self._log("{:>{width}}".format("and:", width=wnick), repr(nextline))
extralines.append(nextline) extralines.append(nextline)
else: else:
msg = "remains unmatched: {!r}".format(line) msg = f"remains unmatched: {line!r}"
self._log(msg) self._log(msg)
self._fail(msg) self._fail(msg)
self._log_output = [] self._log_output = []
@ -1622,7 +1622,7 @@ class LineMatcher:
wnick = len(match_nickname) + 1 wnick = len(match_nickname) + 1
for line in self.lines: for line in self.lines:
if match_func(line, pat): if match_func(line, pat):
msg = "{}: {!r}".format(match_nickname, pat) msg = f"{match_nickname}: {pat!r}"
self._log(msg) self._log(msg)
self._log("{:>{width}}".format("with:", width=wnick), repr(line)) self._log("{:>{width}}".format("with:", width=wnick), repr(line))
self._fail(msg) self._fail(msg)

View File

@ -483,7 +483,7 @@ class PyCollector(PyobjMixin, nodes.Collector):
fixtureinfo.prune_dependency_tree() fixtureinfo.prune_dependency_tree()
for callspec in metafunc._calls: for callspec in metafunc._calls:
subname = "{}[{}]".format(name, callspec.id) subname = f"{name}[{callspec.id}]"
yield Function.from_parent( yield Function.from_parent(
self, self,
name=subname, name=subname,
@ -888,7 +888,7 @@ class CallSpec2:
def _checkargnotcontained(self, arg: str) -> None: def _checkargnotcontained(self, arg: str) -> None:
if arg in self.params or arg in self.funcargs: 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: def getparam(self, name: str) -> object:
try: try:
@ -918,7 +918,7 @@ class CallSpec2:
elif valtype_for_arg == "funcargs": elif valtype_for_arg == "funcargs":
self.funcargs[arg] = val self.funcargs[arg] = val
else: # pragma: no cover 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.indices[arg] = param_index
self._arg2scopenum[arg] = scopenum self._arg2scopenum[arg] = scopenum
self._idlist.append(id) self._idlist.append(id)
@ -1068,7 +1068,7 @@ class Metafunc:
object.__setattr__(_param_mark._param_ids_from, "_param_ids_generated", ids) object.__setattr__(_param_mark._param_ids_from, "_param_ids_generated", ids)
scopenum = scope2index( 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 # Create the new calls: if we are parametrize() multiple times (by applying the decorator
@ -1224,7 +1224,7 @@ class Metafunc:
else: else:
name = "fixture" if indirect else "argument" name = "fixture" if indirect else "argument"
fail( fail(
"In {}: function uses no {} '{}'".format(func_name, name, arg), f"In {func_name}: function uses no {name} '{arg}'",
pytrace=False, pytrace=False,
) )
@ -1291,7 +1291,7 @@ def _idval(
if generated_id is not None: if generated_id is not None:
val = generated_id val = generated_id
except Exception as e: 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 = "error raised while trying to determine id of parameter '{}' at position {}"
msg = prefix + msg.format(argname, idx) msg = prefix + msg.format(argname, idx)
raise ValueError(msg) from e raise ValueError(msg) from e
@ -1400,7 +1400,7 @@ def _show_fixtures_per_test(config: Config, session: Session) -> None:
return return
if verbose > 0: if verbose > 0:
bestrel = get_best_relpath(fixture_def.func) bestrel = get_best_relpath(fixture_def.func)
funcargspec = "{} -- {}".format(argname, bestrel) funcargspec = f"{argname} -- {bestrel}"
else: else:
funcargspec = argname funcargspec = argname
tw.line(funcargspec, green=True) 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. # This test item does not use any fixtures.
return return
tw.line() tw.line()
tw.sep("-", "fixtures used by {}".format(item.name)) tw.sep("-", f"fixtures used by {item.name}")
# TODO: Fix this type ignore. # TODO: Fix this type ignore.
tw.sep("-", "({})".format(get_best_relpath(item.function))) # type: ignore[attr-defined] tw.sep("-", "({})".format(get_best_relpath(item.function))) # type: ignore[attr-defined]
# dict key not used in loop but needed for sorting. # 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 currentmodule != module:
if not module.startswith("_pytest."): if not module.startswith("_pytest."):
tw.line() tw.line()
tw.sep("-", "fixtures defined from {}".format(module)) tw.sep("-", f"fixtures defined from {module}")
currentmodule = module currentmodule = module
if verbose <= 0 and argname[0] == "_": if verbose <= 0 and argname[0] == "_":
continue continue
@ -1491,7 +1491,7 @@ def _showfixtures_main(config: Config, session: Session) -> None:
if doc: if doc:
write_docstring(tw, doc) write_docstring(tw, doc)
else: else:
tw.line(" {}: no docstring available".format(loc), red=True) tw.line(f" {loc}: no docstring available", red=True)
tw.line() tw.line()

View File

@ -25,7 +25,7 @@ from _pytest.outcomes import fail
def _non_numeric_type_error(value, at: Optional[str]) -> TypeError: 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( return TypeError(
"cannot make approximate comparisons to non-numeric values: {!r} {}".format( "cannot make approximate comparisons to non-numeric values: {!r} {}".format(
value, at_str value, at_str
@ -98,7 +98,7 @@ class ApproxNumpy(ApproxBase):
def __repr__(self) -> str: def __repr__(self) -> str:
list_scalars = _recursive_list_map(self._approx_scalar, self.expected.tolist()) 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: def __eq__(self, actual) -> bool:
import numpy as np import numpy as np
@ -109,9 +109,7 @@ class ApproxNumpy(ApproxBase):
try: try:
actual = np.asarray(actual) actual = np.asarray(actual)
except Exception as e: except Exception as e:
raise TypeError( raise TypeError(f"cannot compare '{actual}' to numpy.ndarray") from e
"cannot compare '{}' to numpy.ndarray".format(actual)
) from e
if not np.isscalar(actual) and actual.shape != self.expected.shape: if not np.isscalar(actual) and actual.shape != self.expected.shape:
return False return False
@ -219,7 +217,7 @@ class ApproxScalar(ApproxBase):
# If a sensible tolerance can't be calculated, self.tolerance will # If a sensible tolerance can't be calculated, self.tolerance will
# raise a ValueError. In this case, display '???'. # raise a ValueError. In this case, display '???'.
try: try:
vetted_tolerance = "{:.1e}".format(self.tolerance) vetted_tolerance = f"{self.tolerance:.1e}"
if ( if (
isinstance(self.expected, Complex) isinstance(self.expected, Complex)
and self.expected.imag and self.expected.imag
@ -229,7 +227,7 @@ class ApproxScalar(ApproxBase):
except ValueError: except ValueError:
vetted_tolerance = "???" vetted_tolerance = "???"
return "{} ± {}".format(self.expected, vetted_tolerance) return f"{self.expected} ± {vetted_tolerance}"
def __eq__(self, actual) -> bool: def __eq__(self, actual) -> bool:
"""Return whether the given value is equal to the expected value """Return whether the given value is equal to the expected value
@ -291,7 +289,7 @@ class ApproxScalar(ApproxBase):
if absolute_tolerance < 0: if absolute_tolerance < 0:
raise ValueError( 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): if math.isnan(absolute_tolerance):
raise ValueError("absolute tolerance can't be NaN.") raise ValueError("absolute tolerance can't be NaN.")
@ -313,7 +311,7 @@ class ApproxScalar(ApproxBase):
if relative_tolerance < 0: if relative_tolerance < 0:
raise ValueError( 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): if math.isnan(relative_tolerance):
raise ValueError("relative tolerance can't be NaN.") 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__ not_a = exc.__name__ if isinstance(exc, type) else type(exc).__name__
raise TypeError(msg.format(not_a)) raise TypeError(msg.format(not_a))
message = "DID NOT RAISE {}".format(expected_exception) message = f"DID NOT RAISE {expected_exception}"
if not args: if not args:
match = kwargs.pop("match", None) # type: Optional[Union[str, Pattern[str]]] match = kwargs.pop("match", None) # type: Optional[Union[str, Pattern[str]]]

View File

@ -321,7 +321,7 @@ class TestReport(BaseReport):
excinfo, style=item.config.getoption("tbstyle", "auto") excinfo, style=item.config.getoption("tbstyle", "auto")
) )
for rwhen, key, content in item._report_sections: for rwhen, key, content in item._report_sections:
sections.append(("Captured {} {}".format(key, rwhen), content)) sections.append((f"Captured {key} {rwhen}", content))
return cls( return cls(
item.nodeid, item.nodeid,
item.location, item.location,

View File

@ -93,7 +93,7 @@ def pytest_terminal_summary(terminalreporter: "TerminalReporter") -> None:
% (len(dlist) - i, durations_min) % (len(dlist) - i, durations_min)
) )
break 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: def pytest_sessionstart(session: "Session") -> None:
@ -186,7 +186,7 @@ def _update_current_test_var(
""" """
var_name = "PYTEST_CURRENT_TEST" var_name = "PYTEST_CURRENT_TEST"
if when: if when:
value = "{} ({})".format(item.nodeid, when) value = f"{item.nodeid} ({when})"
# don't allow null bytes on environment variables (see #2644, #2957) # don't allow null bytes on environment variables (see #2644, #2957)
value = value.replace("\x00", "(null)") value = value.replace("\x00", "(null)")
os.environ[var_name] = value os.environ[var_name] = value
@ -248,7 +248,7 @@ def call_runtest_hook(
elif when == "teardown": elif when == "teardown":
ihook = item.ihook.pytest_runtest_teardown ihook = item.ihook.pytest_runtest_teardown
else: else:
assert False, "Unhandled runtest hook case: {}".format(when) assert False, f"Unhandled runtest hook case: {when}"
reraise = (Exit,) # type: Tuple[Type[BaseException], ...] reraise = (Exit,) # type: Tuple[Type[BaseException], ...]
if not item.config.getoption("usepdb", False): if not item.config.getoption("usepdb", False):
reraise += (KeyboardInterrupt,) reraise += (KeyboardInterrupt,)
@ -290,7 +290,7 @@ class CallInfo(Generic[TResult]):
@property @property
def result(self) -> TResult: def result(self) -> TResult:
if self.excinfo is not None: 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 # The cast is safe because an exception wasn't raised, hence
# _result has the expected function return type (which may be # _result has the expected function return type (which may be
# None, that's why a cast and not an assert). # None, that's why a cast and not an assert).
@ -330,8 +330,8 @@ class CallInfo(Generic[TResult]):
def __repr__(self) -> str: def __repr__(self) -> str:
if self.excinfo is None: if self.excinfo is None:
return "<CallInfo when={!r} result: {!r}>".format(self.when, self._result) return f"<CallInfo when={self.when!r} result: {self._result!r}>"
return "<CallInfo when={!r} excinfo={!r}>".format(self.when, self.excinfo) return f"<CallInfo when={self.when!r} excinfo={self.excinfo!r}>"
def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> TestReport: def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> TestReport:

View File

@ -101,7 +101,7 @@ def evaluate_condition(item: Item, mark: Mark, condition: object) -> Tuple[bool,
if hasattr(item, "obj"): if hasattr(item, "obj"):
globals_.update(item.obj.__globals__) # type: ignore[attr-defined] globals_.update(item.obj.__globals__) # type: ignore[attr-defined]
try: try:
filename = "<{} condition>".format(mark.name) filename = f"<{mark.name} condition>"
condition_code = compile(condition, filename, "eval") condition_code = compile(condition, filename, "eval")
result = eval(condition_code, globals_) result = eval(condition_code, globals_)
except SyntaxError as exc: 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": if unexpectedsuccess_key in item._store and rep.when == "call":
reason = item._store[unexpectedsuccess_key] reason = item._store[unexpectedsuccess_key]
if reason: if reason:
rep.longrepr = "Unexpected success: {}".format(reason) rep.longrepr = f"Unexpected success: {reason}"
else: else:
rep.longrepr = "Unexpected success" rep.longrepr = "Unexpected success"
rep.outcome = "failed" rep.outcome = "failed"

View File

@ -304,7 +304,7 @@ class WarningReport:
relpath = bestrelpath( relpath = bestrelpath(
config.invocation_params.dir, absolutepath(filename) config.invocation_params.dir, absolutepath(filename)
) )
return "{}:{}".format(relpath, linenum) return f"{relpath}:{linenum}"
else: else:
return str(self.fslocation) return str(self.fslocation)
return None return None
@ -487,7 +487,7 @@ class TerminalReporter:
def pytest_plugin_registered(self, plugin: _PluggyPlugin) -> None: def pytest_plugin_registered(self, plugin: _PluggyPlugin) -> None:
if self.config.option.traceconfig: if self.config.option.traceconfig:
msg = "PLUGIN registered: {}".format(plugin) msg = f"PLUGIN registered: {plugin}"
# XXX This event may happen during setup/teardown time # XXX This event may happen during setup/teardown time
# which unfortunately captures our output here # which unfortunately captures our output here
# which garbles our output if we use self.write_line. # which garbles our output if we use self.write_line.
@ -593,9 +593,9 @@ class TerminalReporter:
if collected: if collected:
progress = self._progress_nodeids_reported progress = self._progress_nodeids_reported
counter_format = "{{:{}d}}".format(len(str(collected))) 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_string.format(len(progress), collected)
return " [ {} / {} ]".format(collected, collected) return f" [ {collected} / {collected} ]"
else: else:
if collected: if collected:
return " [{:3d}%]".format( return " [{:3d}%]".format(
@ -682,7 +682,7 @@ class TerminalReporter:
self.write_sep("=", "test session starts", bold=True) self.write_sep("=", "test session starts", bold=True)
verinfo = platform.python_version() verinfo = platform.python_version()
if not self.no_header: 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) pypy_version_info = getattr(sys, "pypy_version_info", None)
if pypy_version_info: if pypy_version_info:
verinfo = ".".join(map(str, pypy_version_info[:3])) verinfo = ".".join(map(str, pypy_version_info[:3]))
@ -778,7 +778,7 @@ class TerminalReporter:
if col.name == "()": # Skip Instances. if col.name == "()": # Skip Instances.
continue continue
indent = (len(stack) - 1) * " " indent = (len(stack) - 1) * " "
self._tw.line("{}{}".format(indent, col)) self._tw.line(f"{indent}{col}")
if self.config.option.verbose >= 1: if self.config.option.verbose >= 1:
obj = getattr(col, "obj", None) obj = getattr(col, "obj", None)
doc = inspect.getdoc(obj) if obj else None doc = inspect.getdoc(obj) if obj else None
@ -1018,7 +1018,7 @@ class TerminalReporter:
if rep.when == "collect": if rep.when == "collect":
msg = "ERROR collecting " + msg msg = "ERROR collecting " + msg
else: 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.write_sep("_", msg, red=True, bold=True)
self._outrep_summary(rep) self._outrep_summary(rep)
@ -1091,7 +1091,7 @@ class TerminalReporter:
for rep in xfailed: for rep in xfailed:
verbose_word = rep._get_verbose_word(self.config) verbose_word = rep._get_verbose_word(self.config)
pos = _get_pos(self.config, rep) pos = _get_pos(self.config, rep)
lines.append("{} {}".format(verbose_word, pos)) lines.append(f"{verbose_word} {pos}")
reason = rep.wasxfail reason = rep.wasxfail
if reason: if reason:
lines.append(" " + str(reason)) lines.append(" " + str(reason))
@ -1102,7 +1102,7 @@ class TerminalReporter:
verbose_word = rep._get_verbose_word(self.config) verbose_word = rep._get_verbose_word(self.config)
pos = _get_pos(self.config, rep) pos = _get_pos(self.config, rep)
reason = rep.wasxfail reason = rep.wasxfail
lines.append("{} {} {}".format(verbose_word, pos, reason)) lines.append(f"{verbose_word} {pos} {reason}")
def show_skipped(lines: List[str]) -> None: def show_skipped(lines: List[str]) -> None:
skipped = self.stats.get("skipped", []) # type: List[CollectReport] 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) verbose_word = rep._get_verbose_word(config)
pos = _get_pos(config, rep) pos = _get_pos(config, rep)
line = "{} {}".format(verbose_word, pos) line = f"{verbose_word} {pos}"
len_line = wcswidth(line) len_line = wcswidth(line)
ellipsis, len_ellipsis = "...", 3 ellipsis, len_ellipsis = "...", 3
if len_line > termwidth - len_ellipsis: if len_line > termwidth - len_ellipsis:
@ -1302,7 +1302,7 @@ def _plugin_nameversions(plugininfo) -> List[str]:
def format_session_duration(seconds: float) -> str: def format_session_duration(seconds: float) -> str:
"""Format the given seconds in a human readable manner to show in the final summary.""" """Format the given seconds in a human readable manner to show in the final summary."""
if seconds < 60: if seconds < 60:
return "{:.2f}s".format(seconds) return f"{seconds:.2f}s"
else: else:
dt = datetime.timedelta(seconds=int(seconds)) dt = datetime.timedelta(seconds=int(seconds))
return "{:.2f}s ({})".format(seconds, dt) return f"{seconds:.2f}s ({dt})"

View File

@ -50,9 +50,7 @@ class TempPathFactory:
def _ensure_relative_to_basetemp(self, basename: str) -> str: def _ensure_relative_to_basetemp(self, basename: str) -> str:
basename = os.path.normpath(basename) basename = os.path.normpath(basename)
if (self.getbasetemp() / basename).resolve().parent != self.getbasetemp(): if (self.getbasetemp() / basename).resolve().parent != self.getbasetemp():
raise ValueError( raise ValueError(f"{basename} is not a normalized and relative path")
"{} is not a normalized and relative path".format(basename)
)
return basename return basename
def mktemp(self, basename: str, numbered: bool = True) -> Path: def mktemp(self, basename: str, numbered: bool = True) -> Path:
@ -94,7 +92,7 @@ class TempPathFactory:
user = get_user() or "unknown" user = get_user() or "unknown"
# use a sub-directory in the temproot to speed-up # use a sub-directory in the temproot to speed-up
# make_numbered_dir() call # make_numbered_dir() call
rootdir = temproot.joinpath("pytest-of-{}".format(user)) rootdir = temproot.joinpath(f"pytest-of-{user}")
rootdir.mkdir(exist_ok=True) rootdir.mkdir(exist_ok=True)
basetemp = make_numbered_dir_with_cleanup( basetemp = make_numbered_dir_with_cleanup(
prefix="pytest-", root=rootdir, keep=3, lock_timeout=LOCK_TIMEOUT prefix="pytest-", root=rootdir, keep=3, lock_timeout=LOCK_TIMEOUT

View File

@ -156,7 +156,7 @@ class TestGeneralUsage:
assert x 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"]) result.stdout.fnmatch_lines(["> assert x", "E assert 0"])
assert result.ret == 1 assert result.ret == 1
@ -185,7 +185,7 @@ class TestGeneralUsage:
assert result.ret == ExitCode.USAGE_ERROR assert result.ret == ExitCode.USAGE_ERROR
result.stderr.fnmatch_lines( result.stderr.fnmatch_lines(
[ [
"ERROR: not found: {}".format(p2), f"ERROR: not found: {p2}",
"(no name {!r} in any of [[][]])".format(str(p2)), "(no name {!r} in any of [[][]])".format(str(p2)),
"", "",
] ]
@ -212,7 +212,7 @@ class TestGeneralUsage:
result = testdir.runpytest() result = testdir.runpytest()
assert result.stdout.lines == [] assert result.stdout.lines == []
assert result.stderr.lines == [ assert result.stderr.lines == [
"ImportError while loading conftest '{}'.".format(conftest), f"ImportError while loading conftest '{conftest}'.",
"conftest.py:3: in <module>", "conftest.py:3: in <module>",
" foo()", " foo()",
"conftest.py:2: in foo", "conftest.py:2: in foo",
@ -503,7 +503,7 @@ class TestInvocationVariants:
def test_pydoc(self, testdir): def test_pydoc(self, testdir):
for name in ("py.test", "pytest"): 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 assert result.ret == 0
s = result.stdout.str() s = result.stdout.str()
assert "MarkGenerator" in s assert "MarkGenerator" in s
@ -671,8 +671,8 @@ class TestInvocationVariants:
) )
lib = ns.mkdir(dirname) lib = ns.mkdir(dirname)
lib.ensure("__init__.py") lib.ensure("__init__.py")
lib.join("test_{}.py".format(dirname)).write( lib.join(f"test_{dirname}.py").write(
"def test_{}(): pass\ndef test_other():pass".format(dirname) f"def test_{dirname}(): pass\ndef test_other():pass"
) )
# The structure of the test directory is now: # The structure of the test directory is now:
@ -891,7 +891,7 @@ class TestDurations:
if ("test_%s" % x) in line and y in line: if ("test_%s" % x) in line and y in line:
break break
else: else:
raise AssertionError("not found {} {}".format(x, y)) raise AssertionError(f"not found {x} {y}")
def test_calls_showall_verbose(self, testdir, mock_timing): def test_calls_showall_verbose(self, testdir, mock_timing):
testdir.makepyfile(self.source) testdir.makepyfile(self.source)
@ -904,7 +904,7 @@ class TestDurations:
if ("test_%s" % x) in line and y in line: if ("test_%s" % x) in line and y in line:
break break
else: else:
raise AssertionError("not found {} {}".format(x, y)) raise AssertionError(f"not found {x} {y}")
def test_with_deselected(self, testdir, mock_timing): def test_with_deselected(self, testdir, mock_timing):
testdir.makepyfile(self.source) testdir.makepyfile(self.source)

View File

@ -206,8 +206,8 @@ class TestTraceback_f_g_h:
excinfo = pytest.raises(ValueError, h) excinfo = pytest.raises(ValueError, h)
traceback = excinfo.traceback traceback = excinfo.traceback
ntraceback = traceback.filter() ntraceback = traceback.filter()
print("old: {!r}".format(traceback)) print(f"old: {traceback!r}")
print("new: {!r}".format(ntraceback)) print(f"new: {ntraceback!r}")
if matching: if matching:
assert len(ntraceback) == len(traceback) - 2 assert len(ntraceback) == len(traceback) - 2
@ -265,7 +265,7 @@ class TestTraceback_f_g_h:
decorator = pytest.importorskip("decorator").decorator decorator = pytest.importorskip("decorator").decorator
def log(f, *k, **kw): def log(f, *k, **kw):
print("{} {}".format(k, kw)) print(f"{k} {kw}")
f(*k, **kw) f(*k, **kw)
log = decorator(log) log = decorator(log)
@ -426,13 +426,13 @@ def test_match_raises_error(testdir):
assert result.ret != 0 assert result.ret != 0
exc_msg = "Regex pattern '[[]123[]]+' does not match 'division by zero'." 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.stdout.no_fnmatch_line("*__tracebackhide__ = True*")
result = testdir.runpytest("--fulltrace") result = testdir.runpytest("--fulltrace")
assert result.ret != 0 assert result.ret != 0
result.stdout.fnmatch_lines( 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():", "def entry():",
"> f(0)", "> f(0)",
"", "",
"{}:5: ".format(mod.__file__), f"{mod.__file__}:5: ",
"_ _ *", "_ _ *",
"", "",
" def f(x):", " def f(x):",
"> raise ValueError(x)", "> raise ValueError(x)",
"E ValueError: 0", "E ValueError: 0",
"", "",
"{}:3: ValueError".format(mod.__file__), f"{mod.__file__}:3: ValueError",
] ]
) )
assert raised == 3 assert raised == 3

View File

@ -262,10 +262,10 @@ def test_log_cli_default_level_multiple_tests(testdir, request):
result = testdir.runpytest() result = testdir.runpytest()
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(
[ [
"{}::test_log_1 ".format(filename), f"{filename}::test_log_1 ",
"*WARNING*log message from test_log_1*", "*WARNING*log message from test_log_1*",
"PASSED *50%*", "PASSED *50%*",
"{}::test_log_2 ".format(filename), f"{filename}::test_log_2 ",
"*WARNING*log message from test_log_2*", "*WARNING*log message from test_log_2*",
"PASSED *100%*", "PASSED *100%*",
"=* 2 passed in *=", "=* 2 passed in *=",
@ -318,7 +318,7 @@ def test_log_cli_default_level_sections(testdir, request):
result = testdir.runpytest() result = testdir.runpytest()
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(
[ [
"{}::test_log_1 ".format(filename), f"{filename}::test_log_1 ",
"*-- live log start --*", "*-- live log start --*",
"*WARNING* >>>>> START >>>>>*", "*WARNING* >>>>> START >>>>>*",
"*-- live log setup --*", "*-- 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*", "*WARNING*log message from teardown of test_log_1*",
"*-- live log finish --*", "*-- live log finish --*",
"*WARNING* <<<<< END <<<<<<<*", "*WARNING* <<<<< END <<<<<<<*",
"{}::test_log_2 ".format(filename), f"{filename}::test_log_2 ",
"*-- live log start --*", "*-- live log start --*",
"*WARNING* >>>>> START >>>>>*", "*WARNING* >>>>> START >>>>>*",
"*-- live log setup --*", "*-- live log setup --*",
@ -394,7 +394,7 @@ def test_live_logs_unknown_sections(testdir, request):
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(
[ [
"*WARNING*Unknown Section*", "*WARNING*Unknown Section*",
"{}::test_log_1 ".format(filename), f"{filename}::test_log_1 ",
"*WARNING* >>>>> START >>>>>*", "*WARNING* >>>>> START >>>>>*",
"*-- live log setup --*", "*-- live log setup --*",
"*WARNING*log message from setup of test_log_1*", "*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 = testdir.runpytest()
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(
[ [
"{}::test_log_1 ".format(filename), f"{filename}::test_log_1 ",
"*-- live log start --*", "*-- live log start --*",
"*WARNING* >>>>> START >>>>>*", "*WARNING* >>>>> START >>>>>*",
"*-- live log setup --*", "*-- live log setup --*",
@ -638,7 +638,7 @@ def test_log_file_cli(testdir):
log_file = testdir.tmpdir.join("pytest.log").strpath log_file = testdir.tmpdir.join("pytest.log").strpath
result = testdir.runpytest( 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 # 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 log_file = testdir.tmpdir.join("pytest.log").strpath
result = testdir.runpytest( result = testdir.runpytest("-s", f"--log-file={log_file}", "--log-file-level=INFO")
"-s", "--log-file={}".format(log_file), "--log-file-level=INFO"
)
# fnmatch_lines does an assertion internally # fnmatch_lines does an assertion internally
result.stdout.fnmatch_lines(["test_log_file_cli_level.py PASSED"]) result.stdout.fnmatch_lines(["test_log_file_cli_level.py PASSED"])

View File

@ -471,7 +471,7 @@ class TestApprox:
expected = "4.0e-06" expected = "4.0e-06"
result = testdir.runpytest() result = testdir.runpytest()
result.stdout.fnmatch_lines( 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( @pytest.mark.parametrize(
@ -483,8 +483,7 @@ class TestApprox:
) )
def test_expected_value_type_error(self, x, name): def test_expected_value_type_error(self, x, name):
with pytest.raises( with pytest.raises(
TypeError, TypeError, match=fr"pytest.approx\(\) does not support nested {name}:",
match=r"pytest.approx\(\) does not support nested {}:".format(name),
): ):
approx(x) approx(x)

View File

@ -1993,7 +1993,7 @@ class TestAutouseManagement:
pass pass
""" """
) )
confcut = "--confcutdir={}".format(testdir.tmpdir) confcut = f"--confcutdir={testdir.tmpdir}"
reprec = testdir.inline_run("-v", "-s", confcut) reprec = testdir.inline_run("-v", "-s", confcut)
reprec.assertoutcome(passed=8) reprec.assertoutcome(passed=8)
config = reprec.getcalls("pytest_unconfigure")[0].config config = reprec.getcalls("pytest_unconfigure")[0].config
@ -3796,7 +3796,7 @@ class TestParameterizedSubRequest:
" test_foos.py::test_foo", " test_foos.py::test_foo",
"", "",
"Requested fixture 'fix_with_param' defined in:", "Requested fixture 'fix_with_param' defined in:",
"{}:4".format(fixfile), f"{fixfile}:4",
"Requested here:", "Requested here:",
"test_foos.py:4", "test_foos.py:4",
"*1 failed*", "*1 failed*",
@ -3813,9 +3813,9 @@ class TestParameterizedSubRequest:
" test_foos.py::test_foo", " test_foos.py::test_foo",
"", "",
"Requested fixture 'fix_with_param' defined in:", "Requested fixture 'fix_with_param' defined in:",
"{}:4".format(fixfile), f"{fixfile}:4",
"Requested here:", "Requested here:",
"{}:4".format(testfile), f"{testfile}:4",
"*1 failed*", "*1 failed*",
] ]
) )

View File

@ -211,7 +211,7 @@ class TestRaises:
pytest.raises(TypeError, int, match="invalid") pytest.raises(TypeError, int, match="invalid")
def tfunc(match): 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="asdf").match("match=asdf")
pytest.raises(ValueError, tfunc, match="").match("match=") pytest.raises(ValueError, tfunc, match="").match("match=")

View File

@ -11,7 +11,7 @@ def equal_with_bash(prefix, ffc, fc, out=None):
res_bash = set(fc(prefix)) res_bash = set(fc(prefix))
retval = set(res) == res_bash retval = set(res) == res_bash
if out: 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: if not retval:
out.write(" python - bash: %s\n" % (set(res) - res_bash)) out.write(" python - bash: %s\n" % (set(res) - res_bash))
out.write(" bash - python: %s\n" % (res_bash - set(res))) out.write(" bash - python: %s\n" % (res_bash - set(res)))
@ -45,26 +45,16 @@ class FilesCompleter:
completion = [] completion = []
if self.allowednames: if self.allowednames:
if self.directories: if self.directories:
files = _wrapcall( files = _wrapcall(["bash", "-c", f"compgen -A directory -- '{prefix}'"])
["bash", "-c", "compgen -A directory -- '{p}'".format(p=prefix)]
)
completion += [f + "/" for f in files] completion += [f + "/" for f in files]
for x in self.allowednames: for x in self.allowednames:
completion += _wrapcall( completion += _wrapcall(
[ ["bash", "-c", f"compgen -A file -X '!*.{x}' -- '{prefix}'"]
"bash",
"-c",
"compgen -A file -X '!*.{0}' -- '{p}'".format(x, p=prefix),
]
) )
else: else:
completion += _wrapcall( completion += _wrapcall(["bash", "-c", f"compgen -A file -- '{prefix}'"])
["bash", "-c", "compgen -A file -- '{p}'".format(p=prefix)]
)
anticomp = _wrapcall( anticomp = _wrapcall(["bash", "-c", f"compgen -A directory -- '{prefix}'"])
["bash", "-c", "compgen -A directory -- '{p}'".format(p=prefix)]
)
completion = list(set(completion) - set(anticomp)) completion = list(set(completion) - set(anticomp))

View File

@ -1494,7 +1494,7 @@ def test_assert_tuple_warning(testdir):
""" """
) )
result = testdir.runpytest() 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 # tuples with size != 2 should not trigger the warning
testdir.makepyfile( testdir.makepyfile(

View File

@ -198,14 +198,14 @@ class TestAssertionRewrite:
lines = msg.splitlines() lines = msg.splitlines()
if verbose > 1: if verbose > 1:
assert lines == [ assert lines == [
"assert {!r} == 42".format(X), f"assert {X!r} == 42",
" +{!r}".format(X), f" +{X!r}",
" -42", " -42",
] ]
elif verbose > 0: elif verbose > 0:
assert lines == [ assert lines == [
"assert <class 'test_...e.<locals>.X'> == 42", "assert <class 'test_...e.<locals>.X'> == 42",
" +{!r}".format(X), f" +{X!r}",
" -42", " -42",
] ]
else: else:
@ -652,7 +652,7 @@ class TestAssertionRewrite:
assert getmsg(f1) == "assert 42" assert getmsg(f1) == "assert 42"
def my_reprcompare2(op, left, right) -> str: def my_reprcompare2(op, left, right) -> str:
return "{} {} {}".format(left, op, right) return f"{left} {op} {right}"
monkeypatch.setattr(util, "_reprcompare", my_reprcompare2) monkeypatch.setattr(util, "_reprcompare", my_reprcompare2)
@ -834,9 +834,7 @@ def test_rewritten():
) )
result = testdir.runpytest_subprocess() result = testdir.runpytest_subprocess()
assert result.ret == 0 assert result.ret == 0
found_names = glob.glob( found_names = glob.glob(f"__pycache__/*-pytest-{pytest.__version__}.pyc")
"__pycache__/*-pytest-{}.pyc".format(pytest.__version__)
)
assert found_names, "pyc with expected tag not found in names: {}".format( assert found_names, "pyc with expected tag not found in names: {}".format(
glob.glob("__pycache__/*.pyc") glob.glob("__pycache__/*.pyc")
) )

View File

@ -189,9 +189,7 @@ def test_cache_reportheader_external_abspath(testdir, tmpdir_factory):
) )
) )
result = testdir.runpytest("-v") result = testdir.runpytest("-v")
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines([f"cachedir: {external_cache}"])
["cachedir: {abscache}".format(abscache=external_cache)]
)
def test_cache_show(testdir): def test_cache_show(testdir):

View File

@ -937,7 +937,7 @@ def lsof_check():
out = subprocess.check_output(("lsof", "-p", str(pid))).decode() out = subprocess.check_output(("lsof", "-p", str(pid))).decode()
except (OSError, subprocess.CalledProcessError, UnicodeDecodeError) as exc: except (OSError, subprocess.CalledProcessError, UnicodeDecodeError) as exc:
# about UnicodeDecodeError, see note on pytester # 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 yield
out2 = subprocess.check_output(("lsof", "-p", str(pid))).decode() out2 = subprocess.check_output(("lsof", "-p", str(pid))).decode()
len1 = len([x for x in out.split("\n") if "REG" in x]) len1 = len([x for x in out.split("\n") if "REG" in x])

View File

@ -35,7 +35,7 @@ def test_real_func_loop_limit():
self.left = 1000 self.left = 1000
def __repr__(self): def __repr__(self):
return "<Evil left={left}>".format(left=self.left) return f"<Evil left={self.left}>"
def __getattr__(self, attr): def __getattr__(self, attr):
if not self.left: if not self.left:

View File

@ -1652,7 +1652,7 @@ def test_help_and_version_after_argument_error(testdir):
assert result.ret == ExitCode.USAGE_ERROR assert result.ret == ExitCode.USAGE_ERROR
result = testdir.runpytest("--version") 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 assert result.ret == ExitCode.USAGE_ERROR
@ -1797,12 +1797,7 @@ class TestPytestPluginsVariable:
res = testdir.runpytest() res = testdir.runpytest()
assert res.ret == 2 assert res.ret == 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"
res.stdout.fnmatch_lines( res.stdout.fnmatch_lines([f"*{msg}*", f"*subdirectory{os.sep}conftest.py*"])
[
"*{msg}*".format(msg=msg),
"*subdirectory{sep}conftest.py*".format(sep=os.sep),
]
)
@pytest.mark.parametrize("use_pyargs", [True, False]) @pytest.mark.parametrize("use_pyargs", [True, False])
def test_pytest_plugins_in_non_top_level_conftest_unsupported_pyargs( def test_pytest_plugins_in_non_top_level_conftest_unsupported_pyargs(
@ -1830,7 +1825,7 @@ class TestPytestPluginsVariable:
if use_pyargs: if use_pyargs:
assert msg not in res.stdout.str() assert msg not in res.stdout.str()
else: 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( def test_pytest_plugins_in_non_top_level_conftest_unsupported_no_top_level_conftest(
self, testdir self, testdir
@ -1854,12 +1849,7 @@ class TestPytestPluginsVariable:
res = testdir.runpytest_subprocess() res = testdir.runpytest_subprocess()
assert res.ret == 2 assert res.ret == 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"
res.stdout.fnmatch_lines( res.stdout.fnmatch_lines([f"*{msg}*", f"*subdirectory{os.sep}conftest.py*"])
[
"*{msg}*".format(msg=msg),
"*subdirectory{sep}conftest.py*".format(sep=os.sep),
]
)
def test_pytest_plugins_in_non_top_level_conftest_unsupported_no_false_positives( def test_pytest_plugins_in_non_top_level_conftest_unsupported_no_false_positives(
self, testdir self, testdir
@ -1887,7 +1877,7 @@ def test_conftest_import_error_repr(tmpdir):
path = tmpdir.join("foo/conftest.py") path = tmpdir.join("foo/conftest.py")
with pytest.raises( with pytest.raises(
ConftestImportFailure, ConftestImportFailure,
match=re.escape("RuntimeError: some error (from {})".format(path)), match=re.escape(f"RuntimeError: some error (from {path})"),
): ):
try: try:
raise RuntimeError("some error") raise RuntimeError("some error")

View File

@ -251,9 +251,7 @@ class TestPDB:
assert False assert False
""" """
) )
child = testdir.spawn_pytest( child = testdir.spawn_pytest(f"--show-capture={showcapture} --pdb {p1}")
"--show-capture={} --pdb {}".format(showcapture, p1)
)
if showcapture in ("all", "log"): if showcapture in ("all", "log"):
child.expect("captured log") child.expect("captured log")
child.expect("get rekt") child.expect("get rekt")
@ -706,7 +704,7 @@ class TestPDB:
set_trace() 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 ===") child.expect("=== SET_TRACE ===")
before = child.before.decode("utf8") before = child.before.decode("utf8")
if not capture_arg: if not capture_arg:
@ -744,7 +742,7 @@ class TestPDB:
x = 5 x = 5
""" """
) )
child = testdir.spawn("{} {}".format(sys.executable, p1)) child = testdir.spawn(f"{sys.executable} {p1}")
child.expect("x = 5") child.expect("x = 5")
child.expect("Pdb") child.expect("Pdb")
child.sendeof() child.sendeof()
@ -1085,12 +1083,12 @@ class TestTraceOption:
child.expect_exact(func) child.expect_exact(func)
child.expect_exact("Pdb") child.expect_exact("Pdb")
child.sendline("args") child.sendline("args")
child.expect_exact("{} = 1\r\n".format(argname)) child.expect_exact(f"{argname} = 1\r\n")
child.expect_exact("Pdb") child.expect_exact("Pdb")
child.sendline("c") child.sendline("c")
child.expect_exact("Pdb") child.expect_exact("Pdb")
child.sendline("args") child.sendline("args")
child.expect_exact("{} = 2\r\n".format(argname)) child.expect_exact(f"{argname} = 2\r\n")
child.expect_exact("Pdb") child.expect_exact("Pdb")
child.sendline("c") child.sendline("c")
child.expect_exact("> PDB continue (IO-capturing resumed) >") child.expect_exact("> PDB continue (IO-capturing resumed) >")

View File

@ -1494,7 +1494,7 @@ def test_is_setup_py_not_named_setup_py(tmpdir):
@pytest.mark.parametrize("mod", ("setuptools", "distutils.core")) @pytest.mark.parametrize("mod", ("setuptools", "distutils.core"))
def test_is_setup_py_is_a_setup_py(tmpdir, mod): def test_is_setup_py_is_a_setup_py(tmpdir, mod):
setup_py = tmpdir.join("setup.py") 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) assert _is_setup_py(setup_py)

View File

@ -124,7 +124,7 @@ def test_already_initialized(faulthandler_timeout, testdir):
"-mpytest", "-mpytest",
testdir.tmpdir, testdir.tmpdir,
"-o", "-o",
"faulthandler_timeout={}".format(faulthandler_timeout), f"faulthandler_timeout={faulthandler_timeout}",
) )
# ensure warning is emitted if faulthandler_timeout is configured # ensure warning is emitted if faulthandler_timeout is configured
warning_line = "*faulthandler.py*faulthandler module enabled before*" warning_line = "*faulthandler.py*faulthandler module enabled before*"

View File

@ -6,9 +6,7 @@ def test_version_verbose(testdir, pytestconfig):
testdir.monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD") testdir.monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD")
result = testdir.runpytest("--version", "--version") result = testdir.runpytest("--version", "--version")
assert result.ret == 0 assert result.ret == 0
result.stderr.fnmatch_lines( result.stderr.fnmatch_lines([f"*pytest*{pytest.__version__}*imported from*"])
["*pytest*{}*imported from*".format(pytest.__version__)]
)
if pytestconfig.pluginmanager.list_plugin_distinfo(): if pytestconfig.pluginmanager.list_plugin_distinfo():
result.stderr.fnmatch_lines(["*setuptools registered plugins:", "*at*"]) result.stderr.fnmatch_lines(["*setuptools registered plugins:", "*at*"])
@ -18,7 +16,7 @@ def test_version_less_verbose(testdir, pytestconfig):
result = testdir.runpytest("--version") result = testdir.runpytest("--version")
assert result.ret == 0 assert result.ret == 0
# p = py.path.local(py.__file__).dirpath() # 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): def test_help(testdir):

View File

@ -245,9 +245,7 @@ class TestPython:
pass pass
""" """
) )
result, dom = run_and_parse( result, dom = run_and_parse("-o", f"junit_duration_report={duration_report}")
"-o", "junit_duration_report={}".format(duration_report)
)
node = dom.find_first_by_tag("testsuite") node = dom.find_first_by_tag("testsuite")
tnode = node.find_first_by_tag("testcase") tnode = node.find_first_by_tag("testcase")
val = float(tnode["time"]) val = float(tnode["time"])

View File

@ -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 # i.e.: Expect drive on windows because we just have drive:filename, whereas
# we expect a relative path on Linux. # we expect a relative path on Linux.
expect = ( expect = f"*{subst_p}*" if sys.platform == "win32" else "*sub2/test_foo.py*"
"*{}*".format(subst_p) if sys.platform == "win32" else "*sub2/test_foo.py*"
)
result.stdout.fnmatch_lines([expect]) result.stdout.fnmatch_lines([expect])

View File

@ -48,20 +48,20 @@ def test_wrap_session_notify_exception(ret_exc, testdir):
if exc == SystemExit: if exc == SystemExit:
assert result.stdout.lines[-3:] == [ 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> raise SystemExit("boom")',
"INTERNALERROR> SystemExit: boom", "INTERNALERROR> SystemExit: boom",
] ]
else: else:
assert result.stdout.lines[-3:] == [ 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> raise ValueError("boom")',
"INTERNALERROR> ValueError: boom", "INTERNALERROR> ValueError: boom",
] ]
if returncode is False: if returncode is False:
assert result.stderr.lines == ["mainloop: caught unexpected SystemExit!"] assert result.stderr.lines == ["mainloop: caught unexpected SystemExit!"]
else: 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)) @pytest.mark.parametrize("returncode", (None, 42))

View File

@ -1094,7 +1094,7 @@ def test_marker_expr_eval_failure_handling(testdir, expr):
pass 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 = testdir.runpytest(foo, "-m", expr)
result.stderr.fnmatch_lines([expected]) result.stderr.fnmatch_lines([expected])
assert result.ret == ExitCode.USAGE_ERROR assert result.ret == ExitCode.USAGE_ERROR

View File

@ -27,6 +27,6 @@ def test_no_warnings(module: str) -> None:
subprocess.check_call(( subprocess.check_call((
sys.executable, sys.executable,
"-W", "error", "-W", "error",
"-c", "__import__({!r})".format(module), "-c", f"__import__({module!r})",
)) ))
# fmt: on # fmt: on

View File

@ -580,20 +580,20 @@ def test_linematcher_no_matching(function) -> None:
obtained = str(e.value).splitlines() obtained = str(e.value).splitlines()
if function == "no_fnmatch_line": if function == "no_fnmatch_line":
assert obtained == [ assert obtained == [
"nomatch: '{}'".format(good_pattern), f"nomatch: '{good_pattern}'",
" and: 'cachedir: .pytest_cache'", " and: 'cachedir: .pytest_cache'",
" and: 'collecting ... collected 1 item'", " and: 'collecting ... collected 1 item'",
" and: ''", " and: ''",
"fnmatch: '{}'".format(good_pattern), f"fnmatch: '{good_pattern}'",
" with: 'show_fixtures_per_test.py OK'", " with: 'show_fixtures_per_test.py OK'",
] ]
else: else:
assert obtained == [ assert obtained == [
" nomatch: '{}'".format(good_pattern), f" nomatch: '{good_pattern}'",
" and: 'cachedir: .pytest_cache'", " and: 'cachedir: .pytest_cache'",
" and: 'collecting ... collected 1 item'", " and: 'collecting ... collected 1 item'",
" and: ''", " and: ''",
"re.match: '{}'".format(good_pattern), f"re.match: '{good_pattern}'",
" with: 'show_fixtures_per_test.py OK'", " with: 'show_fixtures_per_test.py OK'",
] ]

View File

@ -472,7 +472,7 @@ def test_callinfo() -> None:
ci2 = runner.CallInfo.from_call(lambda: 0 / 0, "collect") ci2 = runner.CallInfo.from_call(lambda: 0 / 0, "collect")
assert ci2.when == "collect" assert ci2.when == "collect"
assert not hasattr(ci2, "result") assert not hasattr(ci2, "result")
assert repr(ci2) == "<CallInfo when='collect' excinfo={!r}>".format(ci2.excinfo) assert repr(ci2) == f"<CallInfo when='collect' excinfo={ci2.excinfo!r}>"
assert str(ci2) == repr(ci2) assert str(ci2) == repr(ci2)
assert ci2.excinfo assert ci2.excinfo
@ -481,7 +481,7 @@ def test_callinfo() -> None:
assert 0, "assert_msg" assert 0, "assert_msg"
ci3 = runner.CallInfo.from_call(raise_assertion, "call") ci3 = runner.CallInfo.from_call(raise_assertion, "call")
assert repr(ci3) == "<CallInfo when='call' excinfo={!r}>".format(ci3.excinfo) assert repr(ci3) == f"<CallInfo when='call' excinfo={ci3.excinfo!r}>"
assert "\n" not in repr(ci3) assert "\n" not in repr(ci3)

View File

@ -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( result.stdout.fnmatch_lines(
[ [
"*rootdir: {}/root".format(testdir.tmpdir), f"*rootdir: {testdir.tmpdir}/root",
"root/test_rootdir_option_arg.py *", "root/test_rootdir_option_arg.py *",
"*1 passed*", "*1 passed*",
] ]

View File

@ -758,10 +758,7 @@ class TestSkipif:
) )
result = testdir.runpytest("-s", "-rsxX") result = testdir.runpytest("-s", "-rsxX")
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(
[ [f"*{msg1}*test_foo.py*second_condition*", f"*1 {msg2}*"]
"*{msg1}*test_foo.py*second_condition*".format(msg1=msg1),
"*1 {msg2}*".format(msg2=msg2),
]
) )
assert result.ret == 0 assert result.ret == 0

View File

@ -1725,9 +1725,9 @@ def test_summary_stats(
tr._main_color = None tr._main_color = None
print("Based on stats: %s" % stats_arg) 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() (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 line == exp_line
assert color == exp_color assert color == exp_color
@ -1773,7 +1773,7 @@ class TestClassicOutputStyle:
[ [
"test_one.py .", "test_one.py .",
"test_two.py F", "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*", "*2 failed, 3 passed in*",
] ]
) )
@ -1784,9 +1784,9 @@ class TestClassicOutputStyle:
[ [
"test_one.py::test_one PASSED", "test_one.py::test_one PASSED",
"test_two.py::test_two FAILED", "test_two.py::test_two FAILED",
"sub{}test_three.py::test_three_1 PASSED".format(os.sep), f"sub{os.sep}test_three.py::test_three_1 PASSED",
"sub{}test_three.py::test_three_2 FAILED".format(os.sep), f"sub{os.sep}test_three.py::test_three_2 FAILED",
"sub{}test_three.py::test_three_3 PASSED".format(os.sep), f"sub{os.sep}test_three.py::test_three_3 PASSED",
"*2 failed, 3 passed in*", "*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 actual = _get_line_with_reprcrash_message(config, rep(), width) # type: ignore
assert actual == expected assert actual == expected
if actual != "{} {}".format(mocked_verbose_word, mocked_pos): if actual != f"{mocked_verbose_word} {mocked_pos}":
assert len(actual) <= width assert len(actual) <= width
assert wcswidth(actual) <= width assert wcswidth(actual) <= width

View File

@ -1086,9 +1086,9 @@ def test_error_message_with_parametrized_fixtures(testdir):
) )
def test_setup_inheritance_skipping(testdir, test_name, expected_outcome): def test_setup_inheritance_skipping(testdir, test_name, expected_outcome):
"""Issue #4700""" """Issue #4700"""
testdir.copy_example("unittest/{}".format(test_name)) testdir.copy_example(f"unittest/{test_name}")
result = testdir.runpytest() result = testdir.runpytest()
result.stdout.fnmatch_lines(["* {} in *".format(expected_outcome)]) result.stdout.fnmatch_lines([f"* {expected_outcome} in *"])
def test_BdbQuit(testdir): def test_BdbQuit(testdir):

View File

@ -712,7 +712,7 @@ class TestStackLevel:
file, _, func = location file, _, func = location
assert "could not load initial conftests" in str(warning.message) 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" assert func == "_preparse"
@pytest.mark.filterwarnings("default") @pytest.mark.filterwarnings("default")
@ -748,7 +748,7 @@ class TestStackLevel:
file, _, func = location file, _, func = location
assert "skipped plugin 'some_plugin': thing" in str(warning.message) 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" assert func == "_warn_about_skipped_plugins"
def test_issue4445_issue5928_mark_generator(self, testdir): def test_issue4445_issue5928_mark_generator(self, testdir):