[pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
This commit is contained in:
pre-commit-ci[bot] 2024-04-13 00:00:18 +00:00
parent 72ad6dc953
commit f4cd1ba0d6
813 changed files with 66015 additions and 58839 deletions

View file

@ -1,5 +1,7 @@
# mypy: allow-untyped-defs
"""Utilities for assertion debugging."""
from __future__ import annotations
import collections.abc
import os
import pprint
@ -15,8 +17,8 @@ from typing import Protocol
from typing import Sequence
from unicodedata import normalize
from _pytest import outcomes
import _pytest._code
from _pytest import outcomes
from _pytest._io.pprint import PrettyPrinter
from _pytest._io.saferepr import saferepr
from _pytest._io.saferepr import saferepr_unlimited
@ -27,18 +29,18 @@ from _pytest.config import Config
# interpretation code and assertion rewriter to detect this plugin was
# loaded and in turn call the hooks defined here as part of the
# DebugInterpreter.
_reprcompare: Optional[Callable[[str, object, object], Optional[str]]] = None
_reprcompare: Callable[[str, object, object], str | None] | None = None
# Works similarly as _reprcompare attribute. Is populated with the hook call
# when pytest_runtest_setup is called.
_assertion_pass: Optional[Callable[[int, str, str], None]] = None
_assertion_pass: Callable[[int, str, str], None] | None = None
# Config object which is assigned during pytest_runtest_protocol.
_config: Optional[Config] = None
_config: Config | None = None
class _HighlightFunc(Protocol):
def __call__(self, source: str, lexer: Literal["diff", "python"] = "python") -> str:
def __call__(self, source: str, lexer: Literal['diff', 'python'] = 'python') -> str:
"""Apply highlighting to the given source."""
@ -54,27 +56,27 @@ def format_explanation(explanation: str) -> str:
"""
lines = _split_explanation(explanation)
result = _format_lines(lines)
return "\n".join(result)
return '\n'.join(result)
def _split_explanation(explanation: str) -> List[str]:
def _split_explanation(explanation: str) -> list[str]:
r"""Return a list of individual lines in the explanation.
This will return a list of lines split on '\n{', '\n}' and '\n~'.
Any other newlines will be escaped and appear in the line as the
literal '\n' characters.
"""
raw_lines = (explanation or "").split("\n")
raw_lines = (explanation or '').split('\n')
lines = [raw_lines[0]]
for values in raw_lines[1:]:
if values and values[0] in ["{", "}", "~", ">"]:
if values and values[0] in ['{', '}', '~', '>']:
lines.append(values)
else:
lines[-1] += "\\n" + values
lines[-1] += '\\n' + values
return lines
def _format_lines(lines: Sequence[str]) -> List[str]:
def _format_lines(lines: Sequence[str]) -> list[str]:
"""Format the individual lines.
This will replace the '{', '}' and '~' characters of our mini formatting
@ -87,24 +89,24 @@ def _format_lines(lines: Sequence[str]) -> List[str]:
stack = [0]
stackcnt = [0]
for line in lines[1:]:
if line.startswith("{"):
if line.startswith('{'):
if stackcnt[-1]:
s = "and "
s = 'and '
else:
s = "where "
s = 'where '
stack.append(len(result))
stackcnt[-1] += 1
stackcnt.append(0)
result.append(" +" + " " * (len(stack) - 1) + s + line[1:])
elif line.startswith("}"):
result.append(' +' + ' ' * (len(stack) - 1) + s + line[1:])
elif line.startswith('}'):
stack.pop()
stackcnt.pop()
result[stack[-1]] += line[1:]
else:
assert line[0] in ["~", ">"]
assert line[0] in ['~', '>']
stack[-1] += 1
indent = len(stack) if line.startswith("~") else len(stack) - 1
result.append(" " * indent + line[1:])
indent = len(stack) if line.startswith('~') else len(stack) - 1
result.append(' ' * indent + line[1:])
assert len(stack) == 1
return result
@ -126,15 +128,15 @@ def isset(x: Any) -> bool:
def isnamedtuple(obj: Any) -> bool:
return isinstance(obj, tuple) and getattr(obj, "_fields", None) is not None
return isinstance(obj, tuple) and getattr(obj, '_fields', None) is not None
def isdatacls(obj: Any) -> bool:
return getattr(obj, "__dataclass_fields__", None) is not None
return getattr(obj, '__dataclass_fields__', None) is not None
def isattrs(obj: Any) -> bool:
return getattr(obj, "__attrs_attrs__", None) is not None
return getattr(obj, '__attrs_attrs__', None) is not None
def isiterable(obj: Any) -> bool:
@ -156,28 +158,28 @@ def has_default_eq(
for dataclasses the default co_filename is <string>, for attrs class, the __eq__ should contain "attrs eq generated"
"""
# inspired from https://github.com/willmcgugan/rich/blob/07d51ffc1aee6f16bd2e5a25b4e82850fb9ed778/rich/pretty.py#L68
if hasattr(obj.__eq__, "__code__") and hasattr(obj.__eq__.__code__, "co_filename"):
if hasattr(obj.__eq__, '__code__') and hasattr(obj.__eq__.__code__, 'co_filename'):
code_filename = obj.__eq__.__code__.co_filename
if isattrs(obj):
return "attrs generated eq" in code_filename
return 'attrs generated eq' in code_filename
return code_filename == "<string>" # data class
return code_filename == '<string>' # data class
return True
def assertrepr_compare(
config, op: str, left: Any, right: Any, use_ascii: bool = False
) -> Optional[List[str]]:
config, op: str, left: Any, right: Any, use_ascii: bool = False,
) -> list[str] | None:
"""Return specialised explanations for some operators/operands."""
verbose = config.get_verbosity(Config.VERBOSITY_ASSERTIONS)
# Strings which normalize equal are often hard to distinguish when printed; use ascii() to make this easier.
# See issue #3246.
use_ascii = (
isinstance(left, str)
and isinstance(right, str)
and normalize("NFD", left) == normalize("NFD", right)
isinstance(left, str) and
isinstance(right, str) and
normalize('NFD', left) == normalize('NFD', right)
)
if verbose > 1:
@ -193,29 +195,29 @@ def assertrepr_compare(
left_repr = saferepr(left, maxsize=maxsize, use_ascii=use_ascii)
right_repr = saferepr(right, maxsize=maxsize, use_ascii=use_ascii)
summary = f"{left_repr} {op} {right_repr}"
summary = f'{left_repr} {op} {right_repr}'
highlighter = config.get_terminal_writer()._highlight
explanation = None
try:
if op == "==":
if op == '==':
explanation = _compare_eq_any(left, right, highlighter, verbose)
elif op == "not in":
elif op == 'not in':
if istext(left) and istext(right):
explanation = _notin_text(left, right, verbose)
elif op == "!=":
elif op == '!=':
if isset(left) and isset(right):
explanation = ["Both sets are equal"]
elif op == ">=":
explanation = ['Both sets are equal']
elif op == '>=':
if isset(left) and isset(right):
explanation = _compare_gte_set(left, right, highlighter, verbose)
elif op == "<=":
elif op == '<=':
if isset(left) and isset(right):
explanation = _compare_lte_set(left, right, highlighter, verbose)
elif op == ">":
elif op == '>':
if isset(left) and isset(right):
explanation = _compare_gt_set(left, right, highlighter, verbose)
elif op == "<":
elif op == '<':
if isset(left) and isset(right):
explanation = _compare_lt_set(left, right, highlighter, verbose)
@ -223,23 +225,23 @@ def assertrepr_compare(
raise
except Exception:
explanation = [
"(pytest_assertion plugin: representation of details failed: {}.".format(
_pytest._code.ExceptionInfo.from_current()._getreprcrash()
'(pytest_assertion plugin: representation of details failed: {}.'.format(
_pytest._code.ExceptionInfo.from_current()._getreprcrash(),
),
" Probably an object has a faulty __repr__.)",
' Probably an object has a faulty __repr__.)',
]
if not explanation:
return None
if explanation[0] != "":
explanation = ["", *explanation]
if explanation[0] != '':
explanation = ['', *explanation]
return [summary, *explanation]
def _compare_eq_any(
left: Any, right: Any, highlighter: _HighlightFunc, verbose: int = 0
) -> List[str]:
left: Any, right: Any, highlighter: _HighlightFunc, verbose: int = 0,
) -> list[str]:
explanation = []
if istext(left) and istext(right):
explanation = _diff_text(left, right, verbose)
@ -274,7 +276,7 @@ def _compare_eq_any(
return explanation
def _diff_text(left: str, right: str, verbose: int = 0) -> List[str]:
def _diff_text(left: str, right: str, verbose: int = 0) -> list[str]:
"""Return the explanation for the diff between text.
Unless --verbose is used this will skip leading and trailing
@ -282,7 +284,7 @@ def _diff_text(left: str, right: str, verbose: int = 0) -> List[str]:
"""
from difflib import ndiff
explanation: List[str] = []
explanation: list[str] = []
if verbose < 1:
i = 0 # just in case left or right has zero length
@ -292,7 +294,7 @@ def _diff_text(left: str, right: str, verbose: int = 0) -> List[str]:
if i > 42:
i -= 10 # Provide some context
explanation = [
"Skipping %s identical leading characters in diff, use -v to show" % i
'Skipping %s identical leading characters in diff, use -v to show' % i,
]
left = left[i:]
right = right[i:]
@ -303,8 +305,8 @@ def _diff_text(left: str, right: str, verbose: int = 0) -> List[str]:
if i > 42:
i -= 10 # Provide some context
explanation += [
f"Skipping {i} identical trailing "
"characters in diff, use -v to show"
f'Skipping {i} identical trailing '
'characters in diff, use -v to show',
]
left = left[:-i]
right = right[:-i]
@ -312,11 +314,11 @@ def _diff_text(left: str, right: str, verbose: int = 0) -> List[str]:
if left.isspace() or right.isspace():
left = repr(str(left))
right = repr(str(right))
explanation += ["Strings contain only whitespace, escaping them using repr()"]
explanation += ['Strings contain only whitespace, escaping them using repr()']
# "right" is the expected base against which we compare "left",
# see https://github.com/pytest-dev/pytest/issues/3333
explanation += [
line.strip("\n")
line.strip('\n')
for line in ndiff(right.splitlines(keepends), left.splitlines(keepends))
]
return explanation
@ -327,26 +329,26 @@ def _compare_eq_iterable(
right: Iterable[Any],
highligher: _HighlightFunc,
verbose: int = 0,
) -> List[str]:
) -> list[str]:
if verbose <= 0 and not running_on_ci():
return ["Use -v to get more diff"]
return ['Use -v to get more diff']
# dynamic import to speedup pytest
import difflib
left_formatting = PrettyPrinter().pformat(left).splitlines()
right_formatting = PrettyPrinter().pformat(right).splitlines()
explanation = ["", "Full diff:"]
explanation = ['', 'Full diff:']
# "right" is the expected base against which we compare "left",
# see https://github.com/pytest-dev/pytest/issues/3333
explanation.extend(
highligher(
"\n".join(
'\n'.join(
line.rstrip()
for line in difflib.ndiff(right_formatting, left_formatting)
),
lexer="diff",
).splitlines()
lexer='diff',
).splitlines(),
)
return explanation
@ -356,9 +358,9 @@ def _compare_eq_sequence(
right: Sequence[Any],
highlighter: _HighlightFunc,
verbose: int = 0,
) -> List[str]:
) -> list[str]:
comparing_bytes = isinstance(left, bytes) and isinstance(right, bytes)
explanation: List[str] = []
explanation: list[str] = []
len_left = len(left)
len_right = len(right)
for i in range(min(len_left, len_right)):
@ -372,15 +374,15 @@ def _compare_eq_sequence(
# 102
# >>> s[0:1]
# b'f'
left_value = left[i : i + 1]
right_value = right[i : i + 1]
left_value = left[i: i + 1]
right_value = right[i: i + 1]
else:
left_value = left[i]
right_value = right[i]
explanation.append(
f"At index {i} diff:"
f" {highlighter(repr(left_value))} != {highlighter(repr(right_value))}"
f'At index {i} diff:'
f' {highlighter(repr(left_value))} != {highlighter(repr(right_value))}',
)
break
@ -393,21 +395,21 @@ def _compare_eq_sequence(
len_diff = len_left - len_right
if len_diff:
if len_diff > 0:
dir_with_more = "Left"
dir_with_more = 'Left'
extra = saferepr(left[len_right])
else:
len_diff = 0 - len_diff
dir_with_more = "Right"
dir_with_more = 'Right'
extra = saferepr(right[len_left])
if len_diff == 1:
explanation += [
f"{dir_with_more} contains one more item: {highlighter(extra)}"
f'{dir_with_more} contains one more item: {highlighter(extra)}',
]
else:
explanation += [
"%s contains %d more items, first extra item: %s"
% (dir_with_more, len_diff, highlighter(extra))
'%s contains %d more items, first extra item: %s'
% (dir_with_more, len_diff, highlighter(extra)),
]
return explanation
@ -417,10 +419,10 @@ def _compare_eq_set(
right: AbstractSet[Any],
highlighter: _HighlightFunc,
verbose: int = 0,
) -> List[str]:
) -> list[str]:
explanation = []
explanation.extend(_set_one_sided_diff("left", left, right, highlighter))
explanation.extend(_set_one_sided_diff("right", right, left, highlighter))
explanation.extend(_set_one_sided_diff('left', left, right, highlighter))
explanation.extend(_set_one_sided_diff('right', right, left, highlighter))
return explanation
@ -429,10 +431,10 @@ def _compare_gt_set(
right: AbstractSet[Any],
highlighter: _HighlightFunc,
verbose: int = 0,
) -> List[str]:
) -> list[str]:
explanation = _compare_gte_set(left, right, highlighter)
if not explanation:
return ["Both sets are equal"]
return ['Both sets are equal']
return explanation
@ -441,10 +443,10 @@ def _compare_lt_set(
right: AbstractSet[Any],
highlighter: _HighlightFunc,
verbose: int = 0,
) -> List[str]:
) -> list[str]:
explanation = _compare_lte_set(left, right, highlighter)
if not explanation:
return ["Both sets are equal"]
return ['Both sets are equal']
return explanation
@ -453,8 +455,8 @@ def _compare_gte_set(
right: AbstractSet[Any],
highlighter: _HighlightFunc,
verbose: int = 0,
) -> List[str]:
return _set_one_sided_diff("right", right, left, highlighter)
) -> list[str]:
return _set_one_sided_diff('right', right, left, highlighter)
def _compare_lte_set(
@ -462,8 +464,8 @@ def _compare_lte_set(
right: AbstractSet[Any],
highlighter: _HighlightFunc,
verbose: int = 0,
) -> List[str]:
return _set_one_sided_diff("left", left, right, highlighter)
) -> list[str]:
return _set_one_sided_diff('left', left, right, highlighter)
def _set_one_sided_diff(
@ -471,11 +473,11 @@ def _set_one_sided_diff(
set1: AbstractSet[Any],
set2: AbstractSet[Any],
highlighter: _HighlightFunc,
) -> List[str]:
) -> list[str]:
explanation = []
diff = set1 - set2
if diff:
explanation.append(f"Extra items in the {posn} set:")
explanation.append(f'Extra items in the {posn} set:')
for item in diff:
explanation.append(highlighter(saferepr(item)))
return explanation
@ -486,52 +488,52 @@ def _compare_eq_dict(
right: Mapping[Any, Any],
highlighter: _HighlightFunc,
verbose: int = 0,
) -> List[str]:
explanation: List[str] = []
) -> list[str]:
explanation: list[str] = []
set_left = set(left)
set_right = set(right)
common = set_left.intersection(set_right)
same = {k: left[k] for k in common if left[k] == right[k]}
if same and verbose < 2:
explanation += ["Omitting %s identical items, use -vv to show" % len(same)]
explanation += ['Omitting %s identical items, use -vv to show' % len(same)]
elif same:
explanation += ["Common items:"]
explanation += ['Common items:']
explanation += highlighter(pprint.pformat(same)).splitlines()
diff = {k for k in common if left[k] != right[k]}
if diff:
explanation += ["Differing items:"]
explanation += ['Differing items:']
for k in diff:
explanation += [
highlighter(saferepr({k: left[k]}))
+ " != "
+ highlighter(saferepr({k: right[k]}))
highlighter(saferepr({k: left[k]})) +
' != ' +
highlighter(saferepr({k: right[k]})),
]
extra_left = set_left - set_right
len_extra_left = len(extra_left)
if len_extra_left:
explanation.append(
"Left contains %d more item%s:"
% (len_extra_left, "" if len_extra_left == 1 else "s")
'Left contains %d more item%s:'
% (len_extra_left, '' if len_extra_left == 1 else 's'),
)
explanation.extend(
highlighter(pprint.pformat({k: left[k] for k in extra_left})).splitlines()
highlighter(pprint.pformat({k: left[k] for k in extra_left})).splitlines(),
)
extra_right = set_right - set_left
len_extra_right = len(extra_right)
if len_extra_right:
explanation.append(
"Right contains %d more item%s:"
% (len_extra_right, "" if len_extra_right == 1 else "s")
'Right contains %d more item%s:'
% (len_extra_right, '' if len_extra_right == 1 else 's'),
)
explanation.extend(
highlighter(pprint.pformat({k: right[k] for k in extra_right})).splitlines()
highlighter(pprint.pformat({k: right[k] for k in extra_right})).splitlines(),
)
return explanation
def _compare_eq_cls(
left: Any, right: Any, highlighter: _HighlightFunc, verbose: int
) -> List[str]:
left: Any, right: Any, highlighter: _HighlightFunc, verbose: int,
) -> list[str]:
if not has_default_eq(left):
return []
if isdatacls(left):
@ -541,13 +543,13 @@ def _compare_eq_cls(
fields_to_check = [info.name for info in all_fields if info.compare]
elif isattrs(left):
all_fields = left.__attrs_attrs__
fields_to_check = [field.name for field in all_fields if getattr(field, "eq")]
fields_to_check = [field.name for field in all_fields if getattr(field, 'eq')]
elif isnamedtuple(left):
fields_to_check = left._fields
else:
assert False
indent = " "
indent = ' '
same = []
diff = []
for field in fields_to_check:
@ -558,46 +560,46 @@ def _compare_eq_cls(
explanation = []
if same or diff:
explanation += [""]
explanation += ['']
if same and verbose < 2:
explanation.append("Omitting %s identical items, use -vv to show" % len(same))
explanation.append('Omitting %s identical items, use -vv to show' % len(same))
elif same:
explanation += ["Matching attributes:"]
explanation += ['Matching attributes:']
explanation += highlighter(pprint.pformat(same)).splitlines()
if diff:
explanation += ["Differing attributes:"]
explanation += ['Differing attributes:']
explanation += highlighter(pprint.pformat(diff)).splitlines()
for field in diff:
field_left = getattr(left, field)
field_right = getattr(right, field)
explanation += [
"",
f"Drill down into differing attribute {field}:",
f"{indent}{field}: {highlighter(repr(field_left))} != {highlighter(repr(field_right))}",
'',
f'Drill down into differing attribute {field}:',
f'{indent}{field}: {highlighter(repr(field_left))} != {highlighter(repr(field_right))}',
]
explanation += [
indent + line
for line in _compare_eq_any(
field_left, field_right, highlighter, verbose
field_left, field_right, highlighter, verbose,
)
]
return explanation
def _notin_text(term: str, text: str, verbose: int = 0) -> List[str]:
def _notin_text(term: str, text: str, verbose: int = 0) -> list[str]:
index = text.find(term)
head = text[:index]
tail = text[index + len(term) :]
tail = text[index + len(term):]
correct_text = head + tail
diff = _diff_text(text, correct_text, verbose)
newdiff = ["%s is contained here:" % saferepr(term, maxsize=42)]
newdiff = ['%s is contained here:' % saferepr(term, maxsize=42)]
for line in diff:
if line.startswith("Skipping"):
if line.startswith('Skipping'):
continue
if line.startswith("- "):
if line.startswith('- '):
continue
if line.startswith("+ "):
newdiff.append(" " + line[2:])
if line.startswith('+ '):
newdiff.append(' ' + line[2:])
else:
newdiff.append(line)
return newdiff
@ -605,5 +607,5 @@ def _notin_text(term: str, text: str, verbose: int = 0) -> List[str]:
def running_on_ci() -> bool:
"""Check if we're currently running on a CI system."""
env_vars = ["CI", "BUILD_NUMBER"]
env_vars = ['CI', 'BUILD_NUMBER']
return any(var in os.environ for var in env_vars)