Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-flake8-bugbear for openSUSE:Factory checked in at 2023-07-12 17:27:24 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-flake8-bugbear (Old) and /work/SRC/openSUSE:Factory/.python-flake8-bugbear.new.8922 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-flake8-bugbear" Wed Jul 12 17:27:24 2023 rev:15 rq:1098207 version:23.7.10 Changes: -------- --- /work/SRC/openSUSE:Factory/python-flake8-bugbear/python-flake8-bugbear.changes 2023-06-11 19:58:36.872366122 +0200 +++ /work/SRC/openSUSE:Factory/.python-flake8-bugbear.new.8922/python-flake8-bugbear.changes 2023-07-12 17:27:50.466706226 +0200 @@ -1,0 +2,10 @@ +Tue Jul 11 14:10:46 UTC 2023 - Dirk Müller <dmuel...@suse.com> + +- update to 23.7.10: + * Add B034: re.sub/subn/split must pass flags/count/maxsplit as + keyword arguments. + * Fix a crash and several test failures on Python 3.12, all + relating to the B907 * check. + * Declare support for Python 3.12. + +------------------------------------------------------------------- Old: ---- flake8-bugbear-23.6.5.tar.gz New: ---- flake8-bugbear-23.7.10.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-flake8-bugbear.spec ++++++ --- /var/tmp/diff_new_pack.znxZnQ/_old 2023-07-12 17:27:51.086710518 +0200 +++ /var/tmp/diff_new_pack.znxZnQ/_new 2023-07-12 17:27:51.090710546 +0200 @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-flake8-bugbear -Version: 23.6.5 +Version: 23.7.10 Release: 0 Summary: A plugin for flake8 finding likely bugs and design problems in your program License: MIT ++++++ flake8-bugbear-23.6.5.tar.gz -> flake8-bugbear-23.7.10.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.6.5/PKG-INFO new/flake8-bugbear-23.7.10/PKG-INFO --- old/flake8-bugbear-23.6.5/PKG-INFO 2023-06-05 18:20:50.367162500 +0200 +++ new/flake8-bugbear-23.7.10/PKG-INFO 2023-07-10 18:29:34.876180400 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: flake8-bugbear -Version: 23.6.5 +Version: 23.7.10 Summary: A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle. Author-email: Åukasz Langa <luk...@langa.pl> License: MIT @@ -19,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Quality Assurance @@ -217,6 +218,8 @@ **B033**: Sets should not contain duplicate items. Duplicate items will be replaced with a single item at runtime. +**B034**: Calls to `re.sub`, `re.subn` or `re.split` should pass `flags` or `count`/`maxsplit` as keyword arguments. It is commonly assumed that `flags` is the third positional parameter, forgetting about `count`/`maxsplit`, since many other `re` module functions are of the form `f(pattern, string, flags)`. + Opinionated warnings ~~~~~~~~~~~~~~~~~~~~ @@ -249,8 +252,11 @@ for details. **B905**: ``zip()`` without an explicit `strict=` parameter set. ``strict=True`` causes the resulting iterator -to raise a ``ValueError`` if the arguments are exhausted at differing lengths. The ``strict=`` argument -was added in Python 3.10, so don't enable this flag for code that should work on <3.10. +to raise a ``ValueError`` if the arguments are exhausted at differing lengths. + +Exclusions are `itertools.count <https://docs.python.org/3/library/itertools.html#itertools.count>`_, `itertools.cycle <https://docs.python.org/3/library/itertools.html#itertools.cycle>`_ and `itertools.repeat <https://docs.python.org/3/library/itertools.html#itertools.repeat>`_ (with times=None) since they are infinite iterators. + +The ``strict=`` argument was added in Python 3.10, so don't enable this flag for code that should work on <3.10. For more information: https://peps.python.org/pep-0618/ **B906**: ``visit_`` function with no further call to a ``visit`` function. This is often an error, and will stop the visitor from recursing into the subnodes of a visited node. Consider adding a call ``self.generic_visit(node)`` at the end of the function. @@ -358,6 +364,14 @@ Change Log ---------- +23.7.10 +~~~~~~~~~~ + +* Add B034: re.sub/subn/split must pass flags/count/maxsplit as keyword arguments. +* Fix a crash and several test failures on Python 3.12, all relating to the B907 + check. +* Declare support for Python 3.12. + 23.6.5 ~~~~~~ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.6.5/README.rst new/flake8-bugbear-23.7.10/README.rst --- old/flake8-bugbear-23.6.5/README.rst 2023-06-05 18:20:41.000000000 +0200 +++ new/flake8-bugbear-23.7.10/README.rst 2023-07-10 18:29:25.000000000 +0200 @@ -188,6 +188,8 @@ **B033**: Sets should not contain duplicate items. Duplicate items will be replaced with a single item at runtime. +**B034**: Calls to `re.sub`, `re.subn` or `re.split` should pass `flags` or `count`/`maxsplit` as keyword arguments. It is commonly assumed that `flags` is the third positional parameter, forgetting about `count`/`maxsplit`, since many other `re` module functions are of the form `f(pattern, string, flags)`. + Opinionated warnings ~~~~~~~~~~~~~~~~~~~~ @@ -220,8 +222,11 @@ for details. **B905**: ``zip()`` without an explicit `strict=` parameter set. ``strict=True`` causes the resulting iterator -to raise a ``ValueError`` if the arguments are exhausted at differing lengths. The ``strict=`` argument -was added in Python 3.10, so don't enable this flag for code that should work on <3.10. +to raise a ``ValueError`` if the arguments are exhausted at differing lengths. + +Exclusions are `itertools.count <https://docs.python.org/3/library/itertools.html#itertools.count>`_, `itertools.cycle <https://docs.python.org/3/library/itertools.html#itertools.cycle>`_ and `itertools.repeat <https://docs.python.org/3/library/itertools.html#itertools.repeat>`_ (with times=None) since they are infinite iterators. + +The ``strict=`` argument was added in Python 3.10, so don't enable this flag for code that should work on <3.10. For more information: https://peps.python.org/pep-0618/ **B906**: ``visit_`` function with no further call to a ``visit`` function. This is often an error, and will stop the visitor from recursing into the subnodes of a visited node. Consider adding a call ``self.generic_visit(node)`` at the end of the function. @@ -329,6 +334,14 @@ Change Log ---------- +23.7.10 +~~~~~~~~~~ + +* Add B034: re.sub/subn/split must pass flags/count/maxsplit as keyword arguments. +* Fix a crash and several test failures on Python 3.12, all relating to the B907 + check. +* Declare support for Python 3.12. + 23.6.5 ~~~~~~ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.6.5/bugbear.py new/flake8-bugbear-23.7.10/bugbear.py --- old/flake8-bugbear-23.6.5/bugbear.py 2023-06-05 18:20:41.000000000 +0200 +++ new/flake8-bugbear-23.7.10/bugbear.py 2023-07-10 18:29:25.000000000 +0200 @@ -5,6 +5,7 @@ import math import re import sys +import warnings from collections import namedtuple from contextlib import suppress from functools import lru_cache, partial @@ -13,7 +14,7 @@ import attr import pycodestyle -__version__ = "23.6.5" +__version__ = "23.7.10" LOG = logging.getLogger("flake8.bugbear") CONTEXTFUL_NODES = ( @@ -200,7 +201,7 @@ 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 + return re.match(r"^[A-Za-z_][A-Za-z0-9_]*$", arg.value) is not None def _flatten_excepthandler(node): @@ -401,14 +402,14 @@ with suppress(AttributeError, IndexError): if ( node.func.id in ("getattr", "hasattr") - and node.args[1].s == "__call__" + and node.args[1].value == "__call__" ): self.errors.append(B004(node.lineno, node.col_offset)) if ( node.func.id == "getattr" and len(node.args) == 2 and _is_identifier(node.args[1]) - and not iskeyword(node.args[1].s) + and not iskeyword(node.args[1].value) ): self.errors.append(B009(node.lineno, node.col_offset)) elif ( @@ -416,14 +417,15 @@ and node.func.id == "setattr" and len(node.args) == 3 and _is_identifier(node.args[1]) - and not iskeyword(node.args[1].s) + and not iskeyword(node.args[1].value) ): self.errors.append(B010(node.lineno, node.col_offset)) self.check_for_b026(node) - self.check_for_b905(node) self.check_for_b028(node) + self.check_for_b034(node) + self.check_for_b905(node) self.generic_visit(node) def visit_Module(self, node): @@ -556,11 +558,11 @@ if call_path in B005.valid_paths: return # path is exempt - s = node.args[0].s - if len(s) == 1: + value = node.args[0].value + if len(value) == 1: return # stripping just one character - if len(s) == len(set(s)): + if len(value) == len(set(value)): return # no characters appear more than once self.errors.append(B005(node.lineno, node.col_offset)) @@ -1168,12 +1170,46 @@ for duplicate in duplicates: self.errors.append(B025(node.lineno, node.col_offset, vars=(duplicate,))) - def check_for_b905(self, node): - if ( - isinstance(node.func, ast.Name) - and node.func.id == "zip" - and not any(kw.arg == "strict" for kw in node.keywords) + @staticmethod + def _is_infinite_iterator(node: ast.expr) -> bool: + if not ( + isinstance(node, ast.Call) + and isinstance(node.func, ast.Attribute) + and isinstance(node.func.value, ast.Name) + and node.func.value.id == "itertools" ): + return False + if node.func.attr in {"cycle", "count"}: + return True + elif node.func.attr == "repeat": + if len(node.args) == 1 and len(node.keywords) == 0: + # itertools.repeat(iterable) + return True + if ( + len(node.args) == 2 + and isinstance(node.args[1], ast.Constant) + and node.args[1].value is None + ): + # itertools.repeat(iterable, None) + return True + for kw in node.keywords: + # itertools.repeat(iterable, times=None) + if ( + kw.arg == "times" + and isinstance(kw.value, ast.Constant) + and kw.value.value is None + ): + return True + + return False + + def check_for_b905(self, node): + if not (isinstance(node.func, ast.Name) and node.func.id == "zip"): + return + for arg in node.args: + if self._is_infinite_iterator(arg): + return + if not any(kw.arg == "strict" for kw in node.keywords): self.errors.append(B905(node.lineno, node.col_offset)) def check_for_b906(self, node: ast.FunctionDef): @@ -1182,7 +1218,12 @@ # extract what's visited class_name = node.name[len("visit_") :] - class_type = getattr(ast, class_name, None) + + # silence any DeprecationWarnings + # that might come from accessing a deprecated AST node + with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=DeprecationWarning) + class_type = getattr(ast, class_name, None) if ( # not a valid ast subclass @@ -1239,36 +1280,34 @@ current_mark = None variable = None for value in node.values: - # check for quote mark after pre-marked variable - if ( - current_mark is not None - and variable is not None - and isinstance(value, ast.Constant) - and isinstance(value.value, str) - and value.value[0] == current_mark - ): - self.errors.append( - B907( - variable.lineno, - variable.col_offset, - vars=(myunparse(variable.value),), - ) - ) - current_mark = variable = None - # don't continue with length>1, so we can detect a new pre-mark - # in the same string as a post-mark, e.g. `"{foo}" "{bar}"` - if len(value.value) == 1: + if isinstance(value, ast.Constant) and isinstance(value.value, str): + if not value.value: continue - # detect pre-mark - if ( - isinstance(value, ast.Constant) - and isinstance(value.value, str) - and value.value[-1] in quote_marks - ): - current_mark = value.value[-1] - variable = None - continue + # check for quote mark after pre-marked variable + if ( + current_mark is not None + and variable is not None + and value.value[0] == current_mark + ): + self.errors.append( + B907( + variable.lineno, + variable.col_offset, + vars=(myunparse(variable.value),), + ) + ) + current_mark = variable = None + # don't continue with length>1, so we can detect a new pre-mark + # in the same string as a post-mark, e.g. `"{foo}" "{bar}"` + if len(value.value) == 1: + continue + + # detect pre-mark + if value.value[-1] in quote_marks: + current_mark = value.value[-1] + variable = None + continue # detect variable, if there's a pre-mark if ( @@ -1362,6 +1401,27 @@ else: seen.add(elt.value) + def check_for_b034(self, node: ast.Call): + if not isinstance(node.func, ast.Attribute): + return + if not isinstance(node.func.value, ast.Name) or node.func.value.id != "re": + return + + def check(num_args, param_name): + if len(node.args) > num_args: + self.errors.append( + B034( + node.args[num_args].lineno, + node.args[num_args].col_offset, + vars=(node.func.attr, param_name), + ) + ) + + if node.func.attr in ("sub", "subn"): + check(3, "count") + elif node.func.attr == "split": + check(2, "maxsplit") + def compose_call_path(node): if isinstance(node, ast.Attribute): @@ -1766,6 +1826,13 @@ ) ) +B034 = Error( + message=( + "B034 {} should pass `{}` and `flags` as keyword arguments to avoid confusion" + " due to unintuitive argument positions." + ) +) + # Warnings disabled by default. B901 = Error( message=( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.6.5/flake8_bugbear.egg-info/PKG-INFO new/flake8-bugbear-23.7.10/flake8_bugbear.egg-info/PKG-INFO --- old/flake8-bugbear-23.6.5/flake8_bugbear.egg-info/PKG-INFO 2023-06-05 18:20:50.000000000 +0200 +++ new/flake8-bugbear-23.7.10/flake8_bugbear.egg-info/PKG-INFO 2023-07-10 18:29:34.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: flake8-bugbear -Version: 23.6.5 +Version: 23.7.10 Summary: A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle. Author-email: Åukasz Langa <luk...@langa.pl> License: MIT @@ -19,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Quality Assurance @@ -217,6 +218,8 @@ **B033**: Sets should not contain duplicate items. Duplicate items will be replaced with a single item at runtime. +**B034**: Calls to `re.sub`, `re.subn` or `re.split` should pass `flags` or `count`/`maxsplit` as keyword arguments. It is commonly assumed that `flags` is the third positional parameter, forgetting about `count`/`maxsplit`, since many other `re` module functions are of the form `f(pattern, string, flags)`. + Opinionated warnings ~~~~~~~~~~~~~~~~~~~~ @@ -249,8 +252,11 @@ for details. **B905**: ``zip()`` without an explicit `strict=` parameter set. ``strict=True`` causes the resulting iterator -to raise a ``ValueError`` if the arguments are exhausted at differing lengths. The ``strict=`` argument -was added in Python 3.10, so don't enable this flag for code that should work on <3.10. +to raise a ``ValueError`` if the arguments are exhausted at differing lengths. + +Exclusions are `itertools.count <https://docs.python.org/3/library/itertools.html#itertools.count>`_, `itertools.cycle <https://docs.python.org/3/library/itertools.html#itertools.cycle>`_ and `itertools.repeat <https://docs.python.org/3/library/itertools.html#itertools.repeat>`_ (with times=None) since they are infinite iterators. + +The ``strict=`` argument was added in Python 3.10, so don't enable this flag for code that should work on <3.10. For more information: https://peps.python.org/pep-0618/ **B906**: ``visit_`` function with no further call to a ``visit`` function. This is often an error, and will stop the visitor from recursing into the subnodes of a visited node. Consider adding a call ``self.generic_visit(node)`` at the end of the function. @@ -358,6 +364,14 @@ Change Log ---------- +23.7.10 +~~~~~~~~~~ + +* Add B034: re.sub/subn/split must pass flags/count/maxsplit as keyword arguments. +* Fix a crash and several test failures on Python 3.12, all relating to the B907 + check. +* Declare support for Python 3.12. + 23.6.5 ~~~~~~ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.6.5/flake8_bugbear.egg-info/SOURCES.txt new/flake8-bugbear-23.7.10/flake8_bugbear.egg-info/SOURCES.txt --- old/flake8-bugbear-23.6.5/flake8_bugbear.egg-info/SOURCES.txt 2023-06-05 18:20:50.000000000 +0200 +++ new/flake8-bugbear-23.7.10/flake8_bugbear.egg-info/SOURCES.txt 2023-07-10 18:29:34.000000000 +0200 @@ -48,6 +48,7 @@ tests/b031.py tests/b032.py tests/b033.py +tests/b034.py tests/b901.py tests/b902.py tests/b902_py38.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.6.5/pyproject.toml new/flake8-bugbear-23.7.10/pyproject.toml --- old/flake8-bugbear-23.6.5/pyproject.toml 2023-06-05 18:20:41.000000000 +0200 +++ new/flake8-bugbear-23.7.10/pyproject.toml 2023-07-10 18:29:25.000000000 +0200 @@ -30,6 +30,7 @@ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3 :: Only", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Quality Assurance", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.6.5/tests/b034.py new/flake8-bugbear-23.7.10/tests/b034.py --- old/flake8-bugbear-23.6.5/tests/b034.py 1970-01-01 01:00:00.000000000 +0100 +++ new/flake8-bugbear-23.7.10/tests/b034.py 2023-07-10 18:29:25.000000000 +0200 @@ -0,0 +1,30 @@ +import re +from re import sub + +# error +re.sub("a", "b", "aaa", re.IGNORECASE) +re.sub("a", "b", "aaa", 5) +re.sub("a", "b", "aaa", 5, re.IGNORECASE) +re.subn("a", "b", "aaa", re.IGNORECASE) +re.subn("a", "b", "aaa", 5) +re.subn("a", "b", "aaa", 5, re.IGNORECASE) +re.split(" ", "a a a a", re.I) +re.split(" ", "a a a a", 2) +re.split(" ", "a a a a", 2, re.I) + +# okay +re.sub("a", "b", "aaa") +re.sub("a", "b", "aaa", flags=re.IGNORECASE) +re.sub("a", "b", "aaa", count=5) +re.sub("a", "b", "aaa", count=5, flags=re.IGNORECASE) +re.subn("a", "b", "aaa") +re.subn("a", "b", "aaa", flags=re.IGNORECASE) +re.subn("a", "b", "aaa", count=5) +re.subn("a", "b", "aaa", count=5, flags=re.IGNORECASE) +re.split(" ", "a a a a", flags=re.I) +re.split(" ", "a a a a", maxsplit=2) +re.split(" ", "a a a a", maxsplit=2, flags=re.I) + + +# not covered +sub("a", "b", "aaa", re.IGNORECASE) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.6.5/tests/b905_py310.py new/flake8-bugbear-23.7.10/tests/b905_py310.py --- old/flake8-bugbear-23.6.5/tests/b905_py310.py 2023-06-05 18:20:41.000000000 +0200 +++ new/flake8-bugbear-23.7.10/tests/b905_py310.py 2023-07-10 18:29:25.000000000 +0200 @@ -8,3 +8,15 @@ zip(range(3), strict=True) zip("a", "b", strict=False) zip("a", "b", "c", strict=True) + +# infinite iterators from itertools module should not raise errors +import itertools + +zip([1, 2, 3], itertools.cycle("ABCDEF")) +zip([1, 2, 3], itertools.count()) +zip([1, 2, 3], itertools.repeat(1)) +zip([1, 2, 3], itertools.repeat(1, None)) +zip([1, 2, 3], itertools.repeat(1, times=None)) + +zip([1, 2, 3], itertools.repeat(1, 1)) +zip([1, 2, 3], itertools.repeat(1, times=4)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.6.5/tests/b907.py new/flake8-bugbear-23.7.10/tests/b907.py --- old/flake8-bugbear-23.6.5/tests/b907.py 2023-06-05 18:20:41.000000000 +0200 +++ new/flake8-bugbear-23.7.10/tests/b907.py 2023-07-10 18:29:25.000000000 +0200 @@ -17,12 +17,12 @@ f'a "{foo()}" b' # fmt: off -k = (f'"' # error emitted on this line since all values are assigned the same lineno +k = (f'"' # Error emitted here on <py312 (all values assigned the same lineno) f'{var}' f'"' f'"') -k = (f'"' # error emitted on this line +k = (f'"' # error emitted on this line on <py312 f'{var}' '"' f'"') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.6.5/tests/test_bugbear.py new/flake8-bugbear-23.7.10/tests/test_bugbear.py --- old/flake8-bugbear-23.6.5/tests/test_bugbear.py 2023-06-05 18:20:41.000000000 +0200 +++ new/flake8-bugbear-23.7.10/tests/test_bugbear.py 2023-07-10 18:29:25.000000000 +0200 @@ -8,9 +8,6 @@ from argparse import Namespace from pathlib import Path -from hypothesis import HealthCheck, given, settings -from hypothesmith import from_grammar - from bugbear import ( B001, B002, @@ -45,6 +42,7 @@ B031, B032, B033, + B034, B901, B902, B903, @@ -505,6 +503,23 @@ ) self.assertEqual(errors, expected) + def test_b034(self): + filename = Path(__file__).absolute().parent / "b034.py" + bbc = BugBearChecker(filename=str(filename)) + errors = list(bbc.run()) + expected = self.errors( + B034(5, 24, vars=("sub", "count")), + B034(6, 24, vars=("sub", "count")), + B034(7, 24, vars=("sub", "count")), + B034(8, 25, vars=("subn", "count")), + B034(9, 25, vars=("subn", "count")), + B034(10, 25, vars=("subn", "count")), + B034(11, 25, vars=("split", "maxsplit")), + B034(12, 25, vars=("split", "maxsplit")), + B034(13, 25, vars=("split", "maxsplit")), + ) + self.assertEqual(errors, expected) + def test_b908(self): filename = Path(__file__).absolute().parent / "b908.py" bbc = BugBearChecker(filename=str(filename)) @@ -524,43 +539,51 @@ filename = Path(__file__).absolute().parent / "b907.py" bbc = BugBearChecker(filename=str(filename)) errors = list(bbc.run()) - expected = self.errors( - B907(8, 0, vars=("var",)), - B907(9, 0, vars=("var",)), - B907(10, 0, vars=("var",)), - B907(12, 0, vars=("var",)), - B907(13, 0, vars=("var",)), - B907(14, 0, vars=("var",)), - B907(16, 0, vars=("'hello'",)), - B907(17, 0, vars=("foo()",)), - B907(20, 5, vars=("var",)), - B907(25, 5, vars=("var",)), - B907(31, 0, vars=("var",)), - B907(32, 0, vars=("var",)), - B907(33, 0, vars=("var",)), - B907(33, 0, vars=("var2",)), - B907(34, 0, vars=("var",)), - B907(34, 0, vars=("var2",)), - B907(35, 0, vars=("var",)), - B907(35, 0, vars=("var2",)), - B907(38, 0, vars=("var2",)), - B907(41, 0, vars=("var",)), - B907(42, 0, vars=("var.__str__",)), - B907(43, 0, vars=("var.__str__.__repr__",)), - B907(44, 0, vars=("3 + 5" if sys.version_info >= (3, 9) else "BinOp",)), - B907(45, 0, vars=("foo()",)), - B907(46, 0, vars=("None",)), - B907(47, 0, vars=("..." if sys.version_info >= (3, 9) else "Ellipsis",)), - B907(48, 0, vars=("True",)), - B907(51, 0, vars=("var",)), - B907(52, 0, vars=("var",)), - B907(53, 0, vars=("var",)), - B907(54, 0, vars=("var",)), - B907(57, 0, vars=("var",)), - B907(60, 0, vars=("var",)), - B907(64, 0, vars=("var",)), - B907(66, 0, vars=("var",)), - B907(68, 0, vars=("var",)), + py39 = sys.version_info >= (3, 9) + py312 = sys.version_info >= (3, 12) + + def on_py312(number): + """F-string nodes have column numbers set to 0 on <py312""" + return number if py312 else 0 + + expected = self.errors( + B907(8, on_py312(9), vars=("var",)), + B907(9, on_py312(3), vars=("var",)), + B907(10, on_py312(9), vars=("var",)), + B907(12, on_py312(9), vars=("var",)), + B907(13, on_py312(3), vars=("var",)), + B907(14, on_py312(9), vars=("var",)), + B907(16, on_py312(5), vars=("'hello'",)), + B907(17, on_py312(5), vars=("foo()",)), + # Multiline f-strings have lineno changes as well as colno changes on py312+ + B907(21 if py312 else 20, 7 if py312 else 5, vars=("var",)), + B907(26 if py312 else 25, 7 if py312 else 5, vars=("var",)), + B907(31, on_py312(12), vars=("var",)), + B907(32, on_py312(3), vars=("var",)), + B907(33, on_py312(3), vars=("var",)), + B907(33, on_py312(29), vars=("var2",)), + B907(34, on_py312(3), vars=("var",)), + B907(34, on_py312(15), vars=("var2",)), + B907(35, on_py312(3), vars=("var",)), + B907(35, on_py312(10), vars=("var2",)), + B907(38, on_py312(13), vars=("var2",)), + B907(41, on_py312(3), vars=("var",)), + B907(42, on_py312(3), vars=("var.__str__",)), + B907(43, on_py312(3), vars=("var.__str__.__repr__",)), + B907(44, on_py312(3), vars=("3 + 5" if py39 else "BinOp",)), + B907(45, on_py312(3), vars=("foo()",)), + B907(46, on_py312(3), vars=("None",)), + B907(47, on_py312(3), vars=("..." if py39 else "Ellipsis",)), + B907(48, on_py312(3), vars=("True",)), + B907(51, on_py312(3), vars=("var",)), + B907(52, on_py312(3), vars=("var",)), + B907(53, on_py312(3), vars=("var",)), + B907(54, on_py312(3), vars=("var",)), + B907(57, on_py312(3), vars=("var",)), + B907(60, on_py312(3), vars=("var",)), + B907(64, on_py312(5), vars=("var",)), + B907(66, on_py312(3), vars=("var",)), + B907(68, on_py312(3), vars=("var",)), ) self.assertEqual(errors, expected) @@ -695,6 +718,8 @@ B905(4, 15), B905(5, 4), B905(6, 0), + B905(21, 0), + B905(22, 0), ] self.assertEqual(errors, self.errors(*expected)) @@ -795,13 +820,18 @@ class TestFuzz(unittest.TestCase): - @settings(suppress_health_check=[HealthCheck.too_slow]) - @given(from_grammar().map(ast.parse)) - def test_does_not_crash_on_any_valid_code(self, syntax_tree): - # Given any syntatically-valid source code, flake8-bugbear should - # not crash. This tests doesn't check that we do the *right* thing, - # just that we don't crash on valid-if-poorly-styled code! - BugBearVisitor(filename="<string>", lines=[]).visit(syntax_tree) + # TODO: enable this test on py312 once hypothesmith supports py312 + if sys.version_info < (3, 12): + from hypothesis import HealthCheck, given, settings + from hypothesmith import from_grammar + + @settings(suppress_health_check=[HealthCheck.too_slow]) + @given(from_grammar().map(ast.parse)) + def test_does_not_crash_on_any_valid_code(self, syntax_tree): + # Given any syntatically-valid source code, flake8-bugbear should + # not crash. This tests doesn't check that we do the *right* thing, + # just that we don't crash on valid-if-poorly-styled code! + BugBearVisitor(filename="<string>", lines=[]).visit(syntax_tree) def test_does_not_crash_on_site_code(self): # Because the generator isn't perfect, we'll also test on all the code diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.6.5/tox.ini new/flake8-bugbear-23.7.10/tox.ini --- old/flake8-bugbear-23.6.5/tox.ini 2023-06-05 18:20:41.000000000 +0200 +++ new/flake8-bugbear-23.7.10/tox.ini 2023-07-10 18:29:25.000000000 +0200 @@ -1,7 +1,7 @@ # The test environment and commands [tox] # default environments to run without `-e` -envlist = py38, py39, py310, py311 +envlist = py38, py39, py310, py311, py312 [gh-actions] python = @@ -9,8 +9,9 @@ 3.9: py39 3.10: py310 3.11: py311 + 3.12: py312 -[testenv:{py38, py39, py310, py311}] +[testenv] description = Run coverage deps = coverage @@ -19,3 +20,8 @@ commands = coverage run tests/test_bugbear.py {posargs} coverage report -m + +[testenv:py312] +deps = + # the other dependencies aren't yet installable on py312+ + coverage