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.12
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.3.23
Choose a head ref
  • 6 commits
  • 8 files changed
  • 3 contributors

Commits on Mar 14, 2023

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    b4404df View commit details
  2. Copy the full SHA
    a110333 View commit details

Commits on Mar 16, 2023

  1. (3.8) Minor tweaks (#368)

    FozzieHi authored Mar 16, 2023
    Copy the full SHA
    fc4dde8 View commit details

Commits on Mar 17, 2023

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    d28ff32 View commit details

Commits on Mar 23, 2023

  1. Copy the full SHA
    03a0f40 View commit details
  2. Copy the full SHA
    fce31d4 View commit details
Showing with 62 additions and 35 deletions.
  1. +1 −1 .github/workflows/ci.yml
  2. +2 −2 DEVELOPMENT.md
  3. +7 −0 README.rst
  4. +40 −22 bugbear.py
  5. +2 −3 pyproject.toml
  6. +6 −0 tests/b030.py
  7. +2 −4 tests/test_bugbear.py
  8. +2 −3 tox.ini
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
python-version: ["3.8", "3.9", "3.10", "3.11"]

os: [ubuntu-latest]

4 changes: 2 additions & 2 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ We use GitHub. To get started I'd suggest visiting https://guides.github.com/

Please make sure you system has the following:

- Python 3.7.0 or greater
- Python 3.8.0 or greater
- git cli client

Also ensure you can authenticate with GitHub via SSH Keys or HTTPS.
@@ -55,7 +55,7 @@ You can also use [tox](https://tox.wiki/en/latest/index.html) to test with multi
```console
/path/to/venv/bin/tox
```
will by default run all tests on python versions 3.7 through 3.11. If you only want to test a specific version you can specify the environment with `-e`
will by default run all tests on python versions 3.8 through 3.11. If you only want to test a specific version you can specify the environment with `-e`

```console
/path/to/venv/bin/tox -e py38
7 changes: 7 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -325,6 +325,13 @@ MIT
Change Log
----------

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:``)

23.3.12
~~~~~~~~

62 changes: 40 additions & 22 deletions bugbear.py
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@
import attr
import pycodestyle

__version__ = "23.3.12"
__version__ = "23.3.23"

LOG = logging.getLogger("flake8.bugbear")
CONTEXTFUL_NODES = (
@@ -135,7 +135,7 @@ def add_options(optmanager):
help="Skip B008 test for additional immutable calls.",
)

@lru_cache() # noqa: B019
@lru_cache # noqa: B019
def should_warn(self, code):
"""Returns `True` if Bugbear should emit a particular warning.
@@ -189,7 +189,7 @@ def _is_identifier(arg):
# Return True if arg is a valid identifier, per
# https://docs.python.org/2/reference/lexical_analysis.html#identifiers

if not isinstance(arg, ast.Str):
if not isinstance(arg, ast.Constant) or not isinstance(arg.value, str):
return False

return re.match(r"^[A-Za-z_][A-Za-z0-9_]*$", arg.s) is not None
@@ -250,15 +250,18 @@ def _check_redundant_excepthandlers(names, node):
def _to_name_str(node):
# Turn Name and Attribute nodes to strings, e.g "ValueError" or
# "pkg.mod.error", handling any depth of attribute accesses.
# Return None for unrecognized nodes.
if isinstance(node, ast.Name):
return node.id
if isinstance(node, ast.Call):
return _to_name_str(node.func)
assert isinstance(node, ast.Attribute), f"Unexpected node type: {type(node)}"
try:
return _to_name_str(node.value) + "." + node.attr
except AttributeError:
return _to_name_str(node.value)
elif isinstance(node, ast.Attribute):
inner = _to_name_str(node.value)
if inner is None:
return None
return f"{inner}.{node.attr}"
else:
return None


def names_from_assignments(assign_target):
@@ -345,19 +348,22 @@ def visit_ExceptHandler(self, node):
self.generic_visit(node)
return
handlers = _flatten_excepthandler(node.type)
good_handlers = []
names = []
bad_handlers = []
ignored_handlers = []
for handler in handlers:
if isinstance(handler, (ast.Name, ast.Attribute)):
good_handlers.append(handler)
name = _to_name_str(handler)
if name is None:
ignored_handlers.append(handler)
else:
names.append(name)
elif isinstance(handler, (ast.Call, ast.Starred)):
ignored_handlers.append(handler)
else:
bad_handlers.append(handler)
if bad_handlers:
self.errors.append(B030(node.lineno, node.col_offset))
names = [_to_name_str(e) for e in good_handlers]
if len(names) == 0 and not bad_handlers and not ignored_handlers:
self.errors.append(B029(node.lineno, node.col_offset))
elif (
@@ -526,7 +532,11 @@ def check_for_b005(self, node):
):
return # method is being run on an imported module

if len(node.args) != 1 or not isinstance(node.args[0], ast.Str):
if (
len(node.args) != 1
or not isinstance(node.args[0], ast.Constant)
or not isinstance(node.args[0].value, str)
):
return # used arguments don't match the builtin strip

call_path = ".".join(compose_call_path(node.func.value))
@@ -560,7 +570,7 @@ def check_for_b007(self, node):
self.errors.append(B007(n.lineno, n.col_offset, vars=(name,)))

def check_for_b011(self, node):
if isinstance(node.test, ast.NameConstant) and node.test.value is False:
if isinstance(node.test, ast.Constant) and node.test.value is False:
self.errors.append(B011(node.lineno, node.col_offset))

def check_for_b012(self, node):
@@ -585,7 +595,13 @@ def check_for_b015(self, node):
self.errors.append(B015(node.lineno, node.col_offset))

def check_for_b016(self, node):
if isinstance(node.exc, (ast.NameConstant, ast.Num, ast.Str, ast.JoinedStr)):
if isinstance(node.exc, ast.JoinedStr) or (
isinstance(node.exc, ast.Constant)
and (
isinstance(node.exc.value, (int, float, complex, str, bool))
or node.exc.value is None
)
):
self.errors.append(B016(node.lineno, node.col_offset))

def check_for_b017(self, node):
@@ -768,10 +784,8 @@ def is_overload(expr):

def empty_body(body) -> bool:
def is_str_or_ellipsis(node):
# ast.Ellipsis and ast.Str used in python<3.8
return isinstance(node, (ast.Ellipsis, ast.Str)) or (
isinstance(node, ast.Constant)
and (node.value is Ellipsis or isinstance(node.value, str))
return isinstance(node, ast.Constant) and (
node.value is Ellipsis or isinstance(node.value, str)
)

# Function body consist solely of `pass`, `...`, and/or (doc)string literals
@@ -1020,7 +1034,8 @@ def check_for_b903(self, node):
if (
body
and isinstance(body[0], ast.Expr)
and isinstance(body[0].value, ast.Str)
and isinstance(body[0].value, ast.Constant)
and isinstance(body[0].value.value, str)
):
# Ignore the docstring
body = body[1:]
@@ -1052,13 +1067,16 @@ def check_for_b018(self, node):
if isinstance(
subnode.value,
(
ast.Num,
ast.Bytes,
ast.NameConstant,
ast.List,
ast.Set,
ast.Dict,
),
) or (
isinstance(subnode.value, ast.Constant)
and (
isinstance(subnode.value.value, (int, float, complex, bytes, bool))
or subnode.value.value is None
)
):
self.errors.append(B018(subnode.lineno, subnode.col_offset))

5 changes: 2 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -26,7 +26,6 @@ classifiers = [
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
@@ -35,8 +34,8 @@ classifiers = [
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Software Development :: Quality Assurance",
]
requires-python = ">=3.7"
dependencies = ["flake8 >= 3.0.0", "attrs>=19.2.0"]
requires-python = ">=3.8.1"
dependencies = ["flake8>=6.0.0", "attrs>=19.2.0"]
dynamic = ["version"]

[project.urls]
6 changes: 6 additions & 0 deletions tests/b030.py
Original file line number Diff line number Diff line change
@@ -32,3 +32,9 @@ def what_to_catch():
pass
except what_to_catch(): # ok
pass


try:
pass
except a.b[1].c: # ok
pass
6 changes: 2 additions & 4 deletions tests/test_bugbear.py
Original file line number Diff line number Diff line change
@@ -747,8 +747,7 @@ def test_selfclean_bugbear(self):
filename = Path(__file__).absolute().parent.parent / "bugbear.py"
proc = subprocess.run(
["flake8", str(filename)],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
capture_output=True,
timeout=60,
)
self.assertEqual(proc.returncode, 0, proc.stdout.decode("utf8"))
@@ -759,8 +758,7 @@ def test_selfclean_test_bugbear(self):
filename = Path(__file__).absolute()
proc = subprocess.run(
["flake8", str(filename)],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
capture_output=True,
timeout=60,
)
self.assertEqual(proc.returncode, 0, proc.stdout.decode("utf8"))
5 changes: 2 additions & 3 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
# The test environment and commands
[tox]
# default environments to run without `-e`
envlist = py37, py38, py39, py310, py311
envlist = py38, py39, py310, py311

[gh-actions]
python =
3.7: py37
3.8: py38
3.9: py39
3.10: py310
3.11: py311

[testenv:{py37, py38, py39, py310, py311}]
[testenv:{py38, py39, py310, py311}]
description = Run coverage
deps =
coverage