Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: PyCQA/flake8-bugbear
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 23.3.23
Choose a base ref
...
head repository: PyCQA/flake8-bugbear
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 23.5.9
Choose a head ref
  • 6 commits
  • 7 files changed
  • 5 contributors

Commits on Mar 23, 2023

  1. Verified

    This commit was signed with the committer’s verified signature.
    targos Michaël Zasso
    Copy the full SHA
    d4c77fe View commit details

Commits on Apr 4, 2023

  1. [pre-commit.ci] pre-commit autoupdate (#379)

    updates:
    - [github.com/psf/black: 23.1.0 → 23.3.0](psf/black@23.1.0...23.3.0)
    
    Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
    pre-commit-ci[bot] authored Apr 4, 2023

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    MylesBorins Myles Borins
    Copy the full SHA
    8c0e7eb View commit details

Commits on May 9, 2023

  1. Add B908: Rule to check that assertRaises-like contexts has no more t…

    …han 1 top-level statements (#382)
    
    * Add B908: Rule to check that assertRaises-like contexts has no more than 1 top-level statement
    
    * Update README with B908
    
    * Catch "with raises" and "with warns"
    
    * Update README
    
    * Fix typing
    
    * Add "assertWarnsRegexp" to B908_unittest_methods
    
    * Update README.rst
    
    Co-authored-by: Cooper Lees <me@cooperlees.com>
    
    * Remove assertWarnsRegexp
    
    * Remove old comment
    
    ---------
    
    Co-authored-by: Alexey Nikitin <alexeynikitin@swatmobility.com>
    Co-authored-by: Cooper Lees <me@cooperlees.com>
    3 people authored May 9, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    BethGriggs Bethany Griggs
    Copy the full SHA
    d752c44 View commit details
  2. (3.8) Remove outdated sys.version_info checks (#375)

    Co-authored-by: Cooper Lees <me@cooperlees.com>
    FozzieHi and cooperlees authored May 9, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    BethGriggs Bethany Griggs
    Copy the full SHA
    3f16173 View commit details
  3. Add B033: Duplicate items in sets (#373)

    * Add B033: Duplicate items in sets
    
    * Simplify by checking lengths
    
    ---------
    
    Co-authored-by: Cooper Lees <me@cooperlees.com>
    FozzieHi and cooperlees authored May 9, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    BethGriggs Bethany Griggs
    Copy the full SHA
    eb300be View commit details
  4. Verified

    This commit was signed with the committer’s verified signature.
    BethGriggs Bethany Griggs
    Copy the full SHA
    535c109 View commit details
Showing with 205 additions and 28 deletions.
  1. +1 −1 .pre-commit-config.yaml
  2. +14 −1 README.rst
  3. +71 −9 bugbear.py
  4. +6 −4 tests/b028.py
  5. +17 −0 tests/b033.py
  6. +57 −0 tests/b908.py
  7. +39 −13 tests/test_bugbear.py
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ repos:
- id: isort

- repo: https://github.com/psf/black
rev: 23.1.0
rev: 23.3.0
hooks:
- id: black
args:
15 changes: 14 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
@@ -173,7 +173,7 @@ limitations make it difficult.

**B027**: Empty method in abstract base class, but has no abstract decorator. Consider adding @abstractmethod.

**B028**: No explicit stacklevel keyword argument found. The warn method from the warnings module uses a
**B028**: No explicit stacklevel argument found. The warn method from the warnings module uses a
stacklevel of 1 by default. This will only show a stack trace for the line on which the warn method is called.
It is therefore recommended to use a stacklevel of 2 or greater to provide more information to the user.

@@ -186,6 +186,8 @@ second usage. Save the result to a list if the result is needed multiple times.

**B032**: Possible unintentional type annotation (using ``:``). Did you mean to assign (using ``=``)?

**B033**: Sets should not contain duplicate items. Duplicate items will be replaced with a single item at runtime.

Opinionated warnings
~~~~~~~~~~~~~~~~~~~~

@@ -228,6 +230,8 @@ This is meant to be enabled by developers writing visitors using the ``ast`` mod

**B907**: Consider replacing ``f"'{foo}'"`` with ``f"{foo!r}"`` which is both easier to read and will escape quotes inside ``foo`` if that would appear. The check tries to filter out any format specs that are invalid together with ``!r``. If you're using other conversion flags then e.g. ``f"'{foo!a}'"`` can be replaced with ``f"{ascii(foo)!r}"``. Not currently implemented for python<3.8 or ``str.format()`` calls.

**B908**: Contexts with exceptions assertions like ``with self.assertRaises`` or ``with pytest.raises`` should not have multiple top-level statements. Each statement should be in its own context. That way, the test ensures that the exception is raised only in the exact statement where you expect it.

**B950**: Line too long. This is a pragmatic equivalent of
``pycodestyle``'s ``E501``: it considers "max-line-length" but only triggers
when the value has been exceeded by **more than 10%**. ``noqa`` and ``type: ignore`` comments are ignored. You will no
@@ -325,12 +329,21 @@ MIT
Change Log
----------

23.5.9
~~~~~~

* Add B033: Detect duplicate items in sets
* Add B908: Detect assertRauses like contexts only has top level statements that could throw
* Add B028: Allow stacklevel to be explicitly assigned as a positional argument
* Remove more < 3.8 checks / assertions

23.3.23
~~~~~~~~~~

* flake8-bugbear is now >= 3.8.1 project like flake8>=6.0.0
* This has allowed some more modern AST usage cleanup and less CI running etc.
* B030: Fix crash on certain unusual except handlers (e.g. ``except a[0].b:``)
* Add B033: Check for duplicate items in sets.

23.3.12
~~~~~~~~
80 changes: 71 additions & 9 deletions bugbear.py
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@
import attr
import pycodestyle

__version__ = "23.3.23"
__version__ = "23.5.9"

LOG = logging.getLogger("flake8.bugbear")
CONTEXTFUL_NODES = (
@@ -28,6 +28,14 @@
ast.GeneratorExp,
)
FUNCTION_NODES = (ast.AsyncFunctionDef, ast.FunctionDef, ast.Lambda)
B908_pytest_functions = {"raises", "warns"}
B908_unittest_methods = {
"assertRaises",
"assertRaisesRegex",
"assertRaisesRegexp",
"assertWarns",
"assertWarnsRegex",
}

Context = namedtuple("Context", ["node", "stack"])

@@ -504,6 +512,7 @@ def visit_Raise(self, node):
def visit_With(self, node):
self.check_for_b017(node)
self.check_for_b022(node)
self.check_for_b908(node)
self.generic_visit(node)

def visit_JoinedStr(self, node):
@@ -518,6 +527,10 @@ def visit_Import(self, node):
self.check_for_b005(node)
self.generic_visit(node)

def visit_Set(self, node):
self.check_for_b033(node)
self.generic_visit(node)

def check_for_b005(self, node):
if isinstance(node, ast.Import):
for name in node.names:
@@ -1104,6 +1117,39 @@ def check_for_b022(self, node):
):
self.errors.append(B022(node.lineno, node.col_offset))

@staticmethod
def _is_assertRaises_like(node: ast.withitem) -> bool:
if not (
isinstance(node, ast.withitem)
and isinstance(node.context_expr, ast.Call)
and isinstance(node.context_expr.func, (ast.Attribute, ast.Name))
):
return False
if isinstance(node.context_expr.func, ast.Name):
# "with raises"
return node.context_expr.func.id in B908_pytest_functions
elif isinstance(node.context_expr.func, ast.Attribute) and isinstance(
node.context_expr.func.value, ast.Name
):
return (
# "with pytest.raises"
node.context_expr.func.value.id == "pytest"
and node.context_expr.func.attr in B908_pytest_functions
) or (
# "with self.assertRaises"
node.context_expr.func.value.id == "self"
and node.context_expr.func.attr in B908_unittest_methods
)
else:
return False

def check_for_b908(self, node: ast.With):
if len(node.body) < 2:
return
for node_item in node.items:
if self._is_assertRaises_like(node_item):
self.errors.append(B908(node.lineno, node.col_offset))

def check_for_b025(self, node):
seen = []
for handler in node.handlers:
@@ -1174,11 +1220,6 @@ def check_for_b906(self, node: ast.FunctionDef):
self.errors.append(B906(node.lineno, node.col_offset))

def check_for_b907(self, node: ast.JoinedStr): # noqa: C901
# AST structure of strings in f-strings in 3.7 is different enough this
# implementation doesn't work
if sys.version_info <= (3, 7):
return # pragma: no cover

def myunparse(node: ast.AST) -> str: # pragma: no cover
if sys.version_info >= (3, 9):
return ast.unparse(node)
@@ -1290,6 +1331,7 @@ def check_for_b028(self, node):
and isinstance(node.func.value, ast.Name)
and node.func.value.id == "warnings"
and not any(kw.arg == "stacklevel" for kw in node.keywords)
and len(node.args) < 3
):
self.errors.append(B028(node.lineno, node.col_offset))

@@ -1308,6 +1350,14 @@ def check_for_b032(self, node):
):
self.errors.append(B032(node.lineno, node.col_offset))

def check_for_b033(self, node):
constants = [
item.value
for item in filter(lambda x: isinstance(x, ast.Constant), node.elts)
]
if len(constants) != len(set(constants)):
self.errors.append(B033(node.lineno, node.col_offset))


def compose_call_path(node):
if isinstance(node, ast.Attribute):
@@ -1674,7 +1724,7 @@ def visit_Lambda(self, node):
)
B028 = Error(
message=(
"B028 No explicit stacklevel keyword argument found. The warn method from the"
"B028 No explicit stacklevel argument found. The warn method from the"
" warnings module uses a stacklevel of 1 by default. This will only show a"
" stack trace for the line on which the warn method is called."
" It is therefore recommended to use a stacklevel of 2 or"
@@ -1705,6 +1755,13 @@ def visit_Lambda(self, node):
)
)

B033 = Error(
message=(
"B033 Sets should not contain duplicate items. Duplicate items will be replaced"
" with a single item at runtime."
)
)

# Warnings disabled by default.
B901 = Error(
message=(
@@ -1758,7 +1815,12 @@ def visit_Lambda(self, node):
" flag."
)
)

B908 = Error(
message=(
"B908 assertRaises-type context should not contains more than one top-level"
" statement."
)
)
B950 = Error(message="B950 line too long ({} > {} characters)")

disabled_by_default = ["B901", "B902", "B903", "B904", "B905", "B906", "B950"]
disabled_by_default = ["B901", "B902", "B903", "B904", "B905", "B906", "B908", "B950"]
10 changes: 6 additions & 4 deletions tests/b028.py
Original file line number Diff line number Diff line change
@@ -5,7 +5,9 @@
B028 - on lines 8 and 9
"""

warnings.warn(DeprecationWarning("test"))
warnings.warn(DeprecationWarning("test"), source=None)
warnings.warn(DeprecationWarning("test"), source=None, stacklevel=2)
warnings.warn(DeprecationWarning("test"), stacklevel=1)
warnings.warn("test", DeprecationWarning)
warnings.warn("test", DeprecationWarning, source=None)
warnings.warn("test", DeprecationWarning, source=None, stacklevel=2)
warnings.warn("test", DeprecationWarning, stacklevel=1)
warnings.warn("test", DeprecationWarning, 1)
warnings.warn("test", category=DeprecationWarning, stacklevel=1)
17 changes: 17 additions & 0 deletions tests/b033.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""
Should emit:
B033 - on lines 6-12
"""

test = {1, 2, 3, 3, 5}
test = {"a", "b", "c", "c", "e"}
test = {True, False, True}
test = {None, True, None}
test = {3, 3.0}
test = {1, True}
test = {0, False}

test = {1, 2, 3, 3.5, 5}
test = {"a", "b", "c", "d", "e"}
test = {True, False}
test = {None}
57 changes: 57 additions & 0 deletions tests/b908.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import unittest
import warnings

import pytest
from pytest import raises, warns

with pytest.raises(TypeError):
a = 1 + "x"
b = "x" + 1
print(a, b)


class SomeTestCase(unittest.TestCase):
def test_func_raises(self):
with self.assertRaises(TypeError):
a = 1 + "x"
b = "x" + 1
print(a, b)

def test_func_raises_regex(self):
with self.assertRaisesRegex(TypeError):
a = 1 + "x"
b = "x" + 1
print(a, b)

def test_func_raises_regexp(self):
with self.assertRaisesRegexp(TypeError):
a = 1 + "x"
b = "x" + 1
print(a, b)

def test_raises_correct(self):
with self.assertRaises(TypeError):
print("1" + 1)


with raises(Exception):
"1" + 1
"2" + 2

with pytest.warns(Warning):
print("print before warning")
warnings.warn("some warning", stacklevel=1)

with warns(Warning):
print("print before warning")
warnings.warn("some warning", stacklevel=1)

# should not raise an error
with pytest.raises(TypeError):
print("1" + 1)

with pytest.warns(Warning):
warnings.warn("some warning", stacklevel=1)

with raises(Exception):
raise Exception("some exception")
Loading