Skip to content

Commit

Permalink
Remove Python Compatibility Warnings (#182)
Browse files Browse the repository at this point in the history
- The world has move to Python 3
  - If people need these checks they can pin to 21.4.3

Tests: Ensure all remaining tests still pass

Fixes #177
  • Loading branch information
cooperlees authored Sep 3, 2021
1 parent ceffb5e commit ab0868d
Show file tree
Hide file tree
Showing 6 changed files with 2 additions and 262 deletions.
39 changes: 0 additions & 39 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -131,45 +131,6 @@ Either assert for a more specific exception (builtin or custom), use
data available in ``ex``.


Python 3 compatibility warnings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

These have higher risk of false positives but discover regressions that
are dangerous to slip through when test coverage is not great. Let me
know if a popular library is triggering any of the following warnings
for valid code.

**B301**: Python 3 does not include ``.iter*`` methods on dictionaries.
The default behavior is to return iterables. Simply remove the ``iter``
prefix from the method. For Python 2 compatibility, also prefer the
Python 3 equivalent if you expect that the size of the dict to be small
and bounded. The performance regression on Python 2 will be negligible
and the code is going to be the clearest. Alternatively, use
``six.iter*`` or ``future.utils.iter*``.

**B302**: Python 3 does not include ``.view*`` methods on dictionaries.
The default behavior is to return viewables. Simply remove the ``view``
prefix from the method. For Python 2 compatibility, also prefer the
Python 3 equivalent if you expect that the size of the dict to be small
and bounded. The performance regression on Python 2 will be negligible
and the code is going to be the clearest. Alternatively, use
``six.view*`` or ``future.utils.view*``.

**B303**: The ``__metaclass__`` attribute on a class definition does
nothing on Python 3. Use ``class MyClass(BaseClass, metaclass=...)``.
For Python 2 compatibility, use ``six.add_metaclass``.

**B304**: ``sys.maxint`` is not a thing on Python 3. Use
``sys.maxsize``.

**B305**: ``.next()`` is not a thing on Python 3. Use the ``next()``
builtin. For Python 2 compatibility, use ``six.next()``.

**B306**: ``BaseException.message`` has been deprecated as of Python 2.6
and is removed in Python 3. Use ``str(e)`` to access the user-readable
message. Use ``e.args`` to access arguments passed to the exception.


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

Expand Down
82 changes: 2 additions & 80 deletions bugbear.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,14 +226,7 @@ def visit_UAdd(self, node):

def visit_Call(self, node):
if isinstance(node.func, ast.Attribute):
for bug in (B301, B302, B305):
if node.func.attr in bug.methods:
call_path = ".".join(self.compose_call_path(node.func.value))
if call_path not in bug.valid_paths:
self.errors.append(bug(node.lineno, node.col_offset))
break
else:
self.check_for_b005(node)
self.check_for_b005(node)
else:
with suppress(AttributeError, IndexError):
if (
Expand All @@ -258,25 +251,8 @@ def visit_Call(self, node):

self.generic_visit(node)

def visit_Attribute(self, node):
call_path = list(self.compose_call_path(node))
if ".".join(call_path) == "sys.maxint":
self.errors.append(B304(node.lineno, node.col_offset))
elif len(call_path) == 2 and call_path[1] == "message":
name = call_path[0]
for elem in reversed(self.node_stack[:-1]):
if isinstance(elem, ast.ExceptHandler) and elem.name == name:
self.errors.append(B306(node.lineno, node.col_offset))
break

def visit_Assign(self, node):
if isinstance(self.node_stack[-2], ast.ClassDef):
# note: by hasattr below we're ignoring starred arguments, slices
# and tuples for simplicity.
assign_targets = {t.id for t in node.targets if hasattr(t, "id")}
if "__metaclass__" in assign_targets:
self.errors.append(B303(node.lineno, node.col_offset))
elif len(node.targets) == 1:
if len(node.targets) == 1:
t = node.targets[0]
if isinstance(t, ast.Attribute) and isinstance(t.value, ast.Name):
if (t.value.id, t.attr) == ("os", "environ"):
Expand Down Expand Up @@ -771,60 +747,6 @@ def visit(self, node):
)
)

# Those could be false positives but it's more dangerous to let them slip
# through if they're not.
B301 = Error(
message=(
"B301 Python 3 does not include `.iter*` methods on dictionaries. "
"Remove the `iter` prefix from the method name. For Python 2 "
"compatibility, prefer the Python 3 equivalent unless you expect "
"the size of the container to be large or unbounded. Then use "
"`six.iter*` or `future.utils.iter*`."
)
)
B301.methods = {"iterkeys", "itervalues", "iteritems", "iterlists"}
B301.valid_paths = {"six", "future.utils", "builtins"}

B302 = Error(
message=(
"B302 Python 3 does not include `.view*` methods on dictionaries. "
"Remove the `view` prefix from the method name. For Python 2 "
"compatibility, prefer the Python 3 equivalent unless you expect "
"the size of the container to be large or unbounded. Then use "
"`six.view*` or `future.utils.view*`."
)
)
B302.methods = {"viewkeys", "viewvalues", "viewitems", "viewlists"}
B302.valid_paths = {"six", "future.utils", "builtins"}

B303 = Error(
message=(
"B303 `__metaclass__` does nothing on Python 3. Use "
"`class MyClass(BaseClass, metaclass=...)`. For Python 2 "
"compatibility, use `six.add_metaclass`."
)
)

B304 = Error(message="B304 `sys.maxint` is not a thing on Python 3. Use `sys.maxsize`.")

B305 = Error(
message=(
"B305 `.next()` is not a thing on Python 3. Use the `next()` "
"builtin. For Python 2 compatibility, use `six.next()`."
)
)
B305.methods = {"next"}
B305.valid_paths = {"six", "future.utils", "builtins"}

B306 = Error(
message=(
"B306 `BaseException.message` has been deprecated as of Python "
"2.6 and is removed in Python 3. Use `str(e)` to access the "
"user-readable message. Use `e.args` to access arguments passed "
"to the exception."
)
)

# Warnings disabled by default.
B901 = Error(
message=(
Expand Down
48 changes: 0 additions & 48 deletions tests/b301_b302_b305.py

This file was deleted.

46 changes: 0 additions & 46 deletions tests/b303_b304.py

This file was deleted.

11 changes: 0 additions & 11 deletions tests/b306.py

This file was deleted.

38 changes: 0 additions & 38 deletions tests/test_bugbear.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,6 @@
B015,
B016,
B017,
B301,
B302,
B303,
B304,
B305,
B306,
B901,
B902,
B903,
Expand Down Expand Up @@ -213,38 +207,6 @@ def test_b017(self):
expected = self.errors(B017(22, 8))
self.assertEqual(errors, expected)

def test_b301_b302_b305(self):
filename = Path(__file__).absolute().parent / "b301_b302_b305.py"
bbc = BugBearChecker(filename=str(filename))
errors = list(bbc.run())
self.assertEqual(
errors,
self.errors(
B301(39, 4),
B301(40, 4),
B301(41, 4),
B301(42, 4),
B302(43, 4),
B302(44, 4),
B302(45, 4),
B302(46, 4),
B305(47, 4),
B305(48, 4),
),
)

def test_b303_b304(self):
filename = Path(__file__).absolute().parent / "b303_b304.py"
bbc = BugBearChecker(filename=str(filename))
errors = list(bbc.run())
self.assertEqual(errors, self.errors(B303(25, 4), B304(46, 4)))

def test_b306(self):
filename = Path(__file__).absolute().parent / "b306.py"
bbc = BugBearChecker(filename=str(filename))
errors = list(bbc.run())
self.assertEqual(errors, self.errors(B306(9, 10)))

def test_b901(self):
filename = Path(__file__).absolute().parent / "b901.py"
bbc = BugBearChecker(filename=str(filename))
Expand Down

0 comments on commit ab0868d

Please sign in to comment.