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