Merge pull request #11914 - Activate flake8-bugbear and flake8-pyi
This commit is contained in:
commit
0d1f4c63fa
|
@ -128,15 +128,24 @@ ignore = "W009"
|
|||
src = ["src"]
|
||||
line-length = 88
|
||||
select = [
|
||||
"B", # bugbear
|
||||
"D", # pydocstyle
|
||||
"E", # pycodestyle
|
||||
"F", # pyflakes
|
||||
"I", # isort
|
||||
"PYI", # flake8-pyi
|
||||
"UP", # pyupgrade
|
||||
"RUF", # ruff
|
||||
"W", # pycodestyle
|
||||
]
|
||||
ignore = [
|
||||
# bugbear ignore
|
||||
"B004", # Using `hasattr(x, "__call__")` to test if x is callable is unreliable.
|
||||
"B007", # Loop control variable `i` not used within loop body
|
||||
"B009", # Do not call `getattr` with a constant attribute value
|
||||
"B010", # [*] Do not call `setattr` with a constant attribute value.
|
||||
"B011", # Do not `assert False` (`python -O` removes these calls)
|
||||
"B028", # No explicit `stacklevel` keyword argument found
|
||||
# pycodestyle ignore
|
||||
# pytest can do weird low-level things, and we usually know
|
||||
# what we're doing when we use type(..) is ...
|
||||
|
@ -180,4 +189,6 @@ known-local-folder = ["pytest", "_pytest"]
|
|||
lines-after-imports = 2
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"src/_pytest/_py/**/*.py" = ["B", "PYI"]
|
||||
"src/_pytest/_version.py" = ["I001"]
|
||||
"testing/python/approx.py" = ["B015"]
|
||||
|
|
|
@ -79,7 +79,7 @@ def prepare_release_pr(
|
|||
)
|
||||
except InvalidFeatureRelease as e:
|
||||
print(f"{Fore.RED}{e}")
|
||||
raise SystemExit(1)
|
||||
raise SystemExit(1) from None
|
||||
|
||||
print(f"Version: {Fore.CYAN}{version}")
|
||||
|
||||
|
|
|
@ -208,7 +208,7 @@ def main() -> None:
|
|||
f.write(f"This list contains {len(plugins)} plugins.\n\n")
|
||||
f.write(".. only:: not latex\n\n")
|
||||
|
||||
wcwidth # reference library that must exist for tabulate to work
|
||||
_ = wcwidth # reference library that must exist for tabulate to work
|
||||
plugin_table = tabulate.tabulate(plugins, headers="keys", tablefmt="rst")
|
||||
f.write(indent(plugin_table, " "))
|
||||
f.write("\n\n")
|
||||
|
|
|
@ -232,17 +232,17 @@ class TerminalWriter:
|
|||
# which may lead to the previous color being propagated to the
|
||||
# start of the expression, so reset first.
|
||||
return "\x1b[0m" + highlighted
|
||||
except pygments.util.ClassNotFound:
|
||||
except pygments.util.ClassNotFound as e:
|
||||
raise UsageError(
|
||||
"PYTEST_THEME environment variable had an invalid value: '{}'. "
|
||||
"Only valid pygment styles are allowed.".format(
|
||||
os.getenv("PYTEST_THEME")
|
||||
)
|
||||
)
|
||||
except pygments.util.OptionError:
|
||||
) from e
|
||||
except pygments.util.OptionError as e:
|
||||
raise UsageError(
|
||||
"PYTEST_THEME_MODE environment variable had an invalid value: '{}'. "
|
||||
"The only allowed values are 'dark' and 'light'.".format(
|
||||
os.getenv("PYTEST_THEME_MODE")
|
||||
)
|
||||
)
|
||||
) from e
|
||||
|
|
|
@ -598,7 +598,8 @@ if sys.version_info >= (3, 11) or TYPE_CHECKING:
|
|||
else:
|
||||
|
||||
class CaptureResult(
|
||||
collections.namedtuple("CaptureResult", ["out", "err"]), Generic[AnyStr]
|
||||
collections.namedtuple("CaptureResult", ["out", "err"]), # noqa: PYI024
|
||||
Generic[AnyStr],
|
||||
):
|
||||
"""The result of :method:`caplog.readouterr() <pytest.CaptureFixture.readouterr>`."""
|
||||
|
||||
|
|
|
@ -15,11 +15,6 @@ from typing import Any
|
|||
from typing import Callable
|
||||
from typing import Final
|
||||
from typing import NoReturn
|
||||
from typing import TypeVar
|
||||
|
||||
|
||||
_T = TypeVar("_T")
|
||||
_S = TypeVar("_S")
|
||||
|
||||
|
||||
# fmt: off
|
||||
|
|
|
@ -1848,13 +1848,13 @@ def parse_warning_filter(
|
|||
try:
|
||||
action: "warnings._ActionKind" = warnings._getaction(action_) # type: ignore[attr-defined]
|
||||
except warnings._OptionError as e:
|
||||
raise UsageError(error_template.format(error=str(e)))
|
||||
raise UsageError(error_template.format(error=str(e))) from None
|
||||
try:
|
||||
category: Type[Warning] = _resolve_warning_category(category_)
|
||||
except Exception:
|
||||
exc_info = ExceptionInfo.from_current()
|
||||
exception_text = exc_info.getrepr(style="native")
|
||||
raise UsageError(error_template.format(error=exception_text))
|
||||
raise UsageError(error_template.format(error=exception_text)) from None
|
||||
if message and escape:
|
||||
message = re.escape(message)
|
||||
if module and escape:
|
||||
|
@ -1867,7 +1867,7 @@ def parse_warning_filter(
|
|||
except ValueError as e:
|
||||
raise UsageError(
|
||||
error_template.format(error=f"invalid lineno {lineno_!r}: {e}")
|
||||
)
|
||||
) from None
|
||||
else:
|
||||
lineno = 0
|
||||
return action, message, category, module, lineno
|
||||
|
|
|
@ -209,8 +209,8 @@ class TestCaseFunction(Function):
|
|||
)
|
||||
# Invoke the attributes to trigger storing the traceback
|
||||
# trial causes some issue there.
|
||||
excinfo.value
|
||||
excinfo.traceback
|
||||
_ = excinfo.value
|
||||
_ = excinfo.traceback
|
||||
except TypeError:
|
||||
try:
|
||||
try:
|
||||
|
@ -361,14 +361,21 @@ def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> None:
|
|||
|
||||
|
||||
# Twisted trial support.
|
||||
classImplements_has_run = False
|
||||
|
||||
|
||||
@hookimpl(wrapper=True)
|
||||
def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]:
|
||||
if isinstance(item, TestCaseFunction) and "twisted.trial.unittest" in sys.modules:
|
||||
ut: Any = sys.modules["twisted.python.failure"]
|
||||
global classImplements_has_run
|
||||
Failure__init__ = ut.Failure.__init__
|
||||
check_testcase_implements_trial_reporter()
|
||||
if not classImplements_has_run:
|
||||
from twisted.trial.itrial import IReporter
|
||||
from zope.interface import classImplements
|
||||
|
||||
classImplements(TestCaseFunction, IReporter)
|
||||
classImplements_has_run = True
|
||||
|
||||
def excstore(
|
||||
self, exc_value=None, exc_type=None, exc_tb=None, captureVars=None
|
||||
|
@ -396,16 +403,6 @@ def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]:
|
|||
return res
|
||||
|
||||
|
||||
def check_testcase_implements_trial_reporter(done: List[int] = []) -> None:
|
||||
if done:
|
||||
return
|
||||
from twisted.trial.itrial import IReporter
|
||||
from zope.interface import classImplements
|
||||
|
||||
classImplements(TestCaseFunction, IReporter)
|
||||
done.append(1)
|
||||
|
||||
|
||||
def _is_skipped(obj) -> bool:
|
||||
"""Return True if the given object has been marked with @unittest.skip."""
|
||||
return bool(getattr(obj, "__unittest_skip__", False))
|
||||
|
|
|
@ -1241,9 +1241,9 @@ class TestWINLocalPath:
|
|||
|
||||
def test_owner_group_not_implemented(self, path1):
|
||||
with pytest.raises(NotImplementedError):
|
||||
path1.stat().owner
|
||||
_ = path1.stat().owner
|
||||
with pytest.raises(NotImplementedError):
|
||||
path1.stat().group
|
||||
_ = path1.stat().group
|
||||
|
||||
def test_chmod_simple_int(self, path1):
|
||||
mode = path1.stat().mode
|
||||
|
|
|
@ -387,7 +387,7 @@ def test_excinfo_no_python_sourcecode(tmp_path: Path) -> None:
|
|||
excinfo = pytest.raises(ValueError, template.render, h=h)
|
||||
for item in excinfo.traceback:
|
||||
print(item) # XXX: for some reason jinja.Template.render is printed in full
|
||||
item.source # shouldn't fail
|
||||
_ = item.source # shouldn't fail
|
||||
if isinstance(item.path, Path) and item.path.name == "test.txt":
|
||||
assert str(item.source) == "{{ h()}}:"
|
||||
|
||||
|
@ -418,7 +418,7 @@ def test_codepath_Queue_example() -> None:
|
|||
|
||||
def test_match_succeeds():
|
||||
with pytest.raises(ZeroDivisionError) as excinfo:
|
||||
0 // 0
|
||||
_ = 0 // 0
|
||||
excinfo.match(r".*zero.*")
|
||||
|
||||
|
||||
|
@ -584,7 +584,7 @@ class TestFormattedExcinfo:
|
|||
try:
|
||||
|
||||
def f():
|
||||
1 / 0
|
||||
_ = 1 / 0
|
||||
|
||||
f()
|
||||
|
||||
|
@ -601,7 +601,7 @@ class TestFormattedExcinfo:
|
|||
print(line)
|
||||
assert lines == [
|
||||
" def f():",
|
||||
"> 1 / 0",
|
||||
"> _ = 1 / 0",
|
||||
"E ZeroDivisionError: division by zero",
|
||||
]
|
||||
|
||||
|
@ -638,7 +638,7 @@ raise ValueError()
|
|||
pr = FormattedExcinfo()
|
||||
|
||||
try:
|
||||
1 / 0
|
||||
_ = 1 / 0
|
||||
except ZeroDivisionError:
|
||||
excinfo = ExceptionInfo.from_current()
|
||||
|
||||
|
@ -1582,7 +1582,7 @@ def test_no_recursion_index_on_recursion_error():
|
|||
return getattr(self, "_" + attr)
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
RecursionDepthError().trigger
|
||||
_ = RecursionDepthError().trigger
|
||||
assert "maximum recursion" in str(excinfo.getrepr())
|
||||
|
||||
|
||||
|
|
|
@ -280,7 +280,7 @@ class TestRaises:
|
|||
|
||||
def test_raises_context_manager_with_kwargs(self):
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
with pytest.raises(Exception, foo="bar"): # type: ignore[call-overload]
|
||||
with pytest.raises(OSError, foo="bar"): # type: ignore[call-overload]
|
||||
pass
|
||||
assert "Unexpected keyword arguments" in str(excinfo.value)
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
# mypy: allow-untyped-defs
|
||||
import collections
|
||||
import sys
|
||||
import textwrap
|
||||
from typing import Any
|
||||
from typing import List
|
||||
from typing import MutableSequence
|
||||
from typing import NamedTuple
|
||||
from typing import Optional
|
||||
|
||||
import attr
|
||||
|
@ -1179,7 +1179,9 @@ class TestAssert_reprcompare_attrsclass:
|
|||
|
||||
class TestAssert_reprcompare_namedtuple:
|
||||
def test_namedtuple(self) -> None:
|
||||
NT = collections.namedtuple("NT", ["a", "b"])
|
||||
class NT(NamedTuple):
|
||||
a: Any
|
||||
b: Any
|
||||
|
||||
left = NT(1, "b")
|
||||
right = NT(1, "c")
|
||||
|
@ -1200,8 +1202,13 @@ class TestAssert_reprcompare_namedtuple:
|
|||
]
|
||||
|
||||
def test_comparing_two_different_namedtuple(self) -> None:
|
||||
NT1 = collections.namedtuple("NT1", ["a", "b"])
|
||||
NT2 = collections.namedtuple("NT2", ["a", "b"])
|
||||
class NT1(NamedTuple):
|
||||
a: Any
|
||||
b: Any
|
||||
|
||||
class NT2(NamedTuple):
|
||||
a: Any
|
||||
b: Any
|
||||
|
||||
left = NT1(1, "b")
|
||||
right = NT2(2, "b")
|
||||
|
|
|
@ -169,17 +169,17 @@ class ErrorsHelper:
|
|||
|
||||
def test_helper_failures() -> None:
|
||||
helper = ErrorsHelper()
|
||||
with pytest.raises(Exception):
|
||||
helper.raise_exception
|
||||
with pytest.raises(Exception): # noqa: B017
|
||||
_ = helper.raise_exception
|
||||
with pytest.raises(OutcomeException):
|
||||
helper.raise_fail_outcome
|
||||
_ = helper.raise_fail_outcome
|
||||
|
||||
|
||||
def test_safe_getattr() -> None:
|
||||
helper = ErrorsHelper()
|
||||
assert safe_getattr(helper, "raise_exception", "default") == "default"
|
||||
assert safe_getattr(helper, "raise_fail_outcome", "default") == "default"
|
||||
with pytest.raises(BaseException):
|
||||
with pytest.raises(BaseException): # noqa: B017
|
||||
assert safe_getattr(helper, "raise_baseexception", "default")
|
||||
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ class TestFixtureRequestSessionScoped:
|
|||
AttributeError,
|
||||
match="path not available in session-scoped context",
|
||||
):
|
||||
session_request.fspath
|
||||
_ = session_request.fspath
|
||||
|
||||
|
||||
@pytest.mark.parametrize("config_type", ["ini", "pyproject"])
|
||||
|
|
|
@ -42,7 +42,7 @@ class TestMark:
|
|||
def test_pytest_mark_name_starts_with_underscore(self) -> None:
|
||||
mark = MarkGenerator(_ispytest=True)
|
||||
with pytest.raises(AttributeError):
|
||||
mark._some_name
|
||||
_ = mark._some_name
|
||||
|
||||
|
||||
def test_marked_class_run_twice(pytester: Pytester) -> None:
|
||||
|
|
|
@ -228,7 +228,7 @@ class TestDeprecatedCall:
|
|||
for warning in other_warnings:
|
||||
|
||||
def f():
|
||||
warnings.warn(warning("hi"))
|
||||
warnings.warn(warning("hi")) # noqa: B023
|
||||
|
||||
with pytest.warns(warning):
|
||||
with pytest.raises(pytest.fail.Exception):
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# mypy: allow-untyped-defs
|
||||
"""Terminal reporting of the full testing process."""
|
||||
import collections
|
||||
from io import StringIO
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
@ -10,6 +9,7 @@ from types import SimpleNamespace
|
|||
from typing import cast
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import NamedTuple
|
||||
from typing import Tuple
|
||||
|
||||
import pluggy
|
||||
|
@ -34,7 +34,9 @@ from _pytest.terminal import TerminalReporter
|
|||
import pytest
|
||||
|
||||
|
||||
DistInfo = collections.namedtuple("DistInfo", ["project_name", "version"])
|
||||
class DistInfo(NamedTuple):
|
||||
project_name: str
|
||||
version: int
|
||||
|
||||
|
||||
TRANS_FNMATCH = str.maketrans({"[": "[[]", "]": "[]]"})
|
||||
|
|
|
@ -18,8 +18,7 @@ WARNINGS_SUMMARY_HEADER = "warnings summary"
|
|||
def pyfile_with_warnings(pytester: Pytester, request: FixtureRequest) -> str:
|
||||
"""Create a test file which calls a function in a module which generates warnings."""
|
||||
pytester.syspathinsert()
|
||||
test_name = request.function.__name__
|
||||
module_name = test_name.lstrip("test_") + "_module"
|
||||
module_name = request.function.__name__[len("test_") :] + "_module"
|
||||
test_file = pytester.makepyfile(
|
||||
f"""
|
||||
import {module_name}
|
||||
|
|
Loading…
Reference in New Issue