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-09-20 13:26:01 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-flake8-bugbear (Old) and /work/SRC/openSUSE:Factory/.python-flake8-bugbear.new.16627 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-flake8-bugbear" Wed Sep 20 13:26:01 2023 rev:16 rq:1111899 version:23.9.16 Changes: -------- --- /work/SRC/openSUSE:Factory/python-flake8-bugbear/python-flake8-bugbear.changes 2023-07-12 17:27:50.466706226 +0200 +++ /work/SRC/openSUSE:Factory/.python-flake8-bugbear.new.16627/python-flake8-bugbear.changes 2023-09-20 13:27:53.355850283 +0200 @@ -1,0 +2,8 @@ +Mon Sep 18 06:54:37 UTC 2023 - Daniel Garcia <daniel.gar...@suse.com> + +- update to 23.9.16: + * add --classmethod-decorators (#405) + * fix name collision for node_stack on python 3.12 (#406) + * Use pypa/build to build the package (#404) + +------------------------------------------------------------------- Old: ---- flake8-bugbear-23.7.10.tar.gz New: ---- flake8-bugbear-23.9.16.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-flake8-bugbear.spec ++++++ --- /var/tmp/diff_new_pack.58MDOo/_old 2023-09-20 13:27:54.447889406 +0200 +++ /var/tmp/diff_new_pack.58MDOo/_new 2023-09-20 13:27:54.451889549 +0200 @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-flake8-bugbear -Version: 23.7.10 +Version: 23.9.16 Release: 0 Summary: A plugin for flake8 finding likely bugs and design problems in your program License: MIT ++++++ flake8-bugbear-23.7.10.tar.gz -> flake8-bugbear-23.9.16.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.7.10/PKG-INFO new/flake8-bugbear-23.9.16/PKG-INFO --- old/flake8-bugbear-23.7.10/PKG-INFO 2023-07-10 18:29:34.876180400 +0200 +++ new/flake8-bugbear-23.9.16/PKG-INFO 2023-09-16 22:26:20.477575000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: flake8-bugbear -Version: 23.7.10 +Version: 23.9.16 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 @@ -25,8 +25,16 @@ Classifier: Topic :: Software Development :: Quality Assurance Requires-Python: >=3.8.1 Description-Content-Type: text/x-rst -Provides-Extra: dev License-File: LICENSE +Requires-Dist: flake8>=6.0.0 +Requires-Dist: attrs>=19.2.0 +Provides-Extra: dev +Requires-Dist: tox; extra == "dev" +Requires-Dist: coverage; extra == "dev" +Requires-Dist: hypothesis; extra == "dev" +Requires-Dist: hypothesmith>=0.2; extra == "dev" +Requires-Dist: pre-commit; extra == "dev" +Requires-Dist: pytest; extra == "dev" ============== flake8-bugbear @@ -327,13 +335,16 @@ Configuration ------------- -The plugin currently has one setting: +The plugin currently has the following settings: ``extend-immutable-calls``: Specify a list of additional immutable calls. This could be useful, when using other libraries that provide more immutable calls, beside those already handled by ``flake8-bugbear``. Calls to these method will no longer raise a ``B008`` warning. +``classmethod-decorators``: Specify a list of decorators to additionally mark a method as a ``classmethod`` as used by B902. Default values are ``classmethod, validator, root_validator``, and when an ``@obj.name`` decorator is specified it will match against either ``name`` or ``obj.name``. +This functions similarly to how `pep8-naming <https://github.com/PyCQA/pep8-naming>` handles it, but with different defaults, and they don't support specifying attributes such that a decorator will never match against a specified value ``obj.name`` even if decorated with ``@obj.name``. + For example:: [flake8] @@ -341,6 +352,7 @@ max-complexity = 12 ... extend-immutable-calls = pathlib.Path, Path + classmethod-decorators = myclassmethod, mylibrary.otherclassmethod Tests / Lints --------------- @@ -364,8 +376,15 @@ Change Log ---------- +23.9.16 +~~~~~~~ + +* add --classmethod-decorators (#405) +* fix name collision for node_stack on python 3.12 (#406) +* Use pypa/build to build the package (#404) + 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.7.10/README.rst new/flake8-bugbear-23.9.16/README.rst --- old/flake8-bugbear-23.7.10/README.rst 2023-07-10 18:29:25.000000000 +0200 +++ new/flake8-bugbear-23.9.16/README.rst 2023-09-16 22:26:06.000000000 +0200 @@ -297,13 +297,16 @@ Configuration ------------- -The plugin currently has one setting: +The plugin currently has the following settings: ``extend-immutable-calls``: Specify a list of additional immutable calls. This could be useful, when using other libraries that provide more immutable calls, beside those already handled by ``flake8-bugbear``. Calls to these method will no longer raise a ``B008`` warning. +``classmethod-decorators``: Specify a list of decorators to additionally mark a method as a ``classmethod`` as used by B902. Default values are ``classmethod, validator, root_validator``, and when an ``@obj.name`` decorator is specified it will match against either ``name`` or ``obj.name``. +This functions similarly to how `pep8-naming <https://github.com/PyCQA/pep8-naming>` handles it, but with different defaults, and they don't support specifying attributes such that a decorator will never match against a specified value ``obj.name`` even if decorated with ``@obj.name``. + For example:: [flake8] @@ -311,6 +314,7 @@ max-complexity = 12 ... extend-immutable-calls = pathlib.Path, Path + classmethod-decorators = myclassmethod, mylibrary.otherclassmethod Tests / Lints --------------- @@ -334,8 +338,15 @@ Change Log ---------- +23.9.16 +~~~~~~~ + +* add --classmethod-decorators (#405) +* fix name collision for node_stack on python 3.12 (#406) +* Use pypa/build to build the package (#404) + 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.7.10/bugbear.py new/flake8-bugbear-23.9.16/bugbear.py --- old/flake8-bugbear-23.7.10/bugbear.py 2023-07-10 18:29:25.000000000 +0200 +++ new/flake8-bugbear-23.9.16/bugbear.py 2023-09-16 22:26:06.000000000 +0200 @@ -10,11 +10,12 @@ from contextlib import suppress from functools import lru_cache, partial from keyword import iskeyword +from typing import Dict, List, Set, Union import attr import pycodestyle -__version__ = "23.7.10" +__version__ = "23.9.16" LOG = logging.getLogger("flake8.bugbear") CONTEXTFUL_NODES = ( @@ -38,6 +39,8 @@ "assertWarnsRegex", } +B902_default_decorators = {"classmethod", "validator", "root_validator"} + Context = namedtuple("Context", ["node", "stack"]) @@ -62,10 +65,15 @@ else: b008_extend_immutable_calls = set() + b902_classmethod_decorators: set[str] = B902_default_decorators + if self.options and hasattr(self.options, "classmethod_decorators"): + b902_classmethod_decorators = set(self.options.classmethod_decorators) + visitor = self.visitor( filename=self.filename, lines=self.lines, b008_extend_immutable_calls=b008_extend_immutable_calls, + b902_classmethod_decorators=b902_classmethod_decorators, ) visitor.visit(self.tree) for e in itertools.chain(visitor.errors, self.gen_line_based_checks()): @@ -143,6 +151,19 @@ default=[], help="Skip B008 test for additional immutable calls.", ) + # you cannot register the same option in two different plugins, so we + # only register --classmethod-decorators if pep8-naming is not installed + if "pep8ext_naming" not in sys.modules.keys(): + optmanager.add_option( + "--classmethod-decorators", + comma_separated_list=True, + parse_from_config=True, + default=B902_default_decorators, + help=( + "List of method decorators that should be treated as classmethods" + " by B902" + ), + ) @lru_cache # noqa: B019 def should_warn(self, code): @@ -184,10 +205,8 @@ return True LOG.info( - ( - "Optional warning %s not present in selected warnings: %r. Not " - "firing it at all." - ), + "Optional warning %s not present in selected warnings: %r. Not " + "firing it at all.", code, self.options.select, ) @@ -310,7 +329,7 @@ filename = attr.ib() lines = attr.ib() b008_extend_immutable_calls = attr.ib(default=attr.Factory(set)) - node_stack = attr.ib(default=attr.Factory(list)) + b902_classmethod_decorators = attr.ib(default=attr.Factory(set)) node_window = attr.ib(default=attr.Factory(list)) errors = attr.ib(default=attr.Factory(list)) futures = attr.ib(default=attr.Factory(set)) @@ -974,37 +993,51 @@ self.errors.append(B901(return_node.lineno, return_node.col_offset)) break - def check_for_b902(self, node): + # taken from pep8-naming + @classmethod + def find_decorator_name(cls, d): + if isinstance(d, ast.Name): + return d.id + elif isinstance(d, ast.Attribute): + return d.attr + elif isinstance(d, ast.Call): + return cls.find_decorator_name(d.func) + + def check_for_b902( # noqa: C901 (too complex) + self, node: Union[ast.FunctionDef, ast.AsyncFunctionDef] + ) -> None: + def is_classmethod(decorators: Set[str]) -> bool: + return ( + any(name in decorators for name in self.b902_classmethod_decorators) + or node.name in B902.implicit_classmethods + ) + if len(self.contexts) < 2 or not isinstance( self.contexts[-2].node, ast.ClassDef ): return cls = self.contexts[-2].node - decorators = NameFinder() - decorators.visit(node.decorator_list) - if "staticmethod" in decorators.names: + decorators: set[str] = { + self.find_decorator_name(d) for d in node.decorator_list + } + + if "staticmethod" in decorators: # TODO: maybe warn if the first argument is surprisingly `self` or # `cls`? return bases = {b.id for b in cls.bases if isinstance(b, ast.Name)} if "type" in bases: - if ( - "classmethod" in decorators.names - or node.name in B902.implicit_classmethods - ): + if is_classmethod(decorators): expected_first_args = B902.metacls kind = "metaclass class" else: expected_first_args = B902.cls kind = "metaclass instance" else: - if ( - "classmethod" in decorators.names - or node.name in B902.implicit_classmethods - ): + if is_classmethod(decorators): expected_first_args = B902.cls kind = "class" else: @@ -1441,9 +1474,11 @@ key is name string, value is the node (useful for location purposes). """ - names = attr.ib(default=attr.Factory(dict)) + names: Dict[str, List[ast.Name]] = attr.ib(default=attr.Factory(dict)) - def visit_Name(self, node): # noqa: B906 # names don't contain other names + def visit_Name( # noqa: B906 # names don't contain other names + self, node: ast.Name + ): self.names.setdefault(node.id, []).append(node) def visit(self, node): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.7.10/flake8_bugbear.egg-info/PKG-INFO new/flake8-bugbear-23.9.16/flake8_bugbear.egg-info/PKG-INFO --- old/flake8-bugbear-23.7.10/flake8_bugbear.egg-info/PKG-INFO 2023-07-10 18:29:34.000000000 +0200 +++ new/flake8-bugbear-23.9.16/flake8_bugbear.egg-info/PKG-INFO 2023-09-16 22:26:20.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: flake8-bugbear -Version: 23.7.10 +Version: 23.9.16 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 @@ -25,8 +25,16 @@ Classifier: Topic :: Software Development :: Quality Assurance Requires-Python: >=3.8.1 Description-Content-Type: text/x-rst -Provides-Extra: dev License-File: LICENSE +Requires-Dist: flake8>=6.0.0 +Requires-Dist: attrs>=19.2.0 +Provides-Extra: dev +Requires-Dist: tox; extra == "dev" +Requires-Dist: coverage; extra == "dev" +Requires-Dist: hypothesis; extra == "dev" +Requires-Dist: hypothesmith>=0.2; extra == "dev" +Requires-Dist: pre-commit; extra == "dev" +Requires-Dist: pytest; extra == "dev" ============== flake8-bugbear @@ -327,13 +335,16 @@ Configuration ------------- -The plugin currently has one setting: +The plugin currently has the following settings: ``extend-immutable-calls``: Specify a list of additional immutable calls. This could be useful, when using other libraries that provide more immutable calls, beside those already handled by ``flake8-bugbear``. Calls to these method will no longer raise a ``B008`` warning. +``classmethod-decorators``: Specify a list of decorators to additionally mark a method as a ``classmethod`` as used by B902. Default values are ``classmethod, validator, root_validator``, and when an ``@obj.name`` decorator is specified it will match against either ``name`` or ``obj.name``. +This functions similarly to how `pep8-naming <https://github.com/PyCQA/pep8-naming>` handles it, but with different defaults, and they don't support specifying attributes such that a decorator will never match against a specified value ``obj.name`` even if decorated with ``@obj.name``. + For example:: [flake8] @@ -341,6 +352,7 @@ max-complexity = 12 ... extend-immutable-calls = pathlib.Path, Path + classmethod-decorators = myclassmethod, mylibrary.otherclassmethod Tests / Lints --------------- @@ -364,8 +376,15 @@ Change Log ---------- +23.9.16 +~~~~~~~ + +* add --classmethod-decorators (#405) +* fix name collision for node_stack on python 3.12 (#406) +* Use pypa/build to build the package (#404) + 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.7.10/flake8_bugbear.egg-info/SOURCES.txt new/flake8-bugbear-23.9.16/flake8_bugbear.egg-info/SOURCES.txt --- old/flake8-bugbear-23.7.10/flake8_bugbear.egg-info/SOURCES.txt 2023-07-10 18:29:34.000000000 +0200 +++ new/flake8-bugbear-23.9.16/flake8_bugbear.egg-info/SOURCES.txt 2023-09-16 22:26:20.000000000 +0200 @@ -51,6 +51,7 @@ tests/b034.py tests/b901.py tests/b902.py +tests/b902_extended.py tests/b902_py38.py tests/b903.py tests/b904.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.7.10/tests/b006_b008.py new/flake8-bugbear-23.9.16/tests/b006_b008.py --- old/flake8-bugbear-23.7.10/tests/b006_b008.py 2023-07-10 18:29:25.000000000 +0200 +++ new/flake8-bugbear-23.9.16/tests/b006_b008.py 2023-09-16 22:26:06.000000000 +0200 @@ -12,8 +12,7 @@ # B006 # Allow immutable literals/calls/comprehensions -def this_is_okay(value=(1, 2, 3)): - ... +def this_is_okay(value=(1, 2, 3)): ... async def and_this_also(value=tuple()): @@ -50,35 +49,28 @@ pass -def kwonlyargs_immutable(*, value=()): - ... +def kwonlyargs_immutable(*, value=()): ... # Flag mutable literals/comprehensions -def this_is_wrong(value=[1, 2, 3]): - ... +def this_is_wrong(value=[1, 2, 3]): ... -def this_is_also_wrong(value={}): - ... +def this_is_also_wrong(value={}): ... -def and_this(value=set()): - ... +def and_this(value=set()): ... -def this_too(value=collections.OrderedDict()): - ... +def this_too(value=collections.OrderedDict()): ... -async def async_this_too(value=collections.defaultdict()): - ... +async def async_this_too(value=collections.defaultdict()): ... -def dont_forget_me(value=collections.deque()): - ... +def dont_forget_me(value=collections.deque()): ... # N.B. we're also flagging the function call in the comprehension @@ -94,8 +86,7 @@ pass -def kwonlyargs_mutable(*, value=[]): - ... +def kwonlyargs_mutable(*, value=[]): ... # Recommended approach for mutable defaults @@ -106,16 +97,14 @@ # B008 # Flag function calls as default args (including if they are part of a sub-expression) -def in_fact_all_calls_are_wrong(value=time.time()): - ... +def in_fact_all_calls_are_wrong(value=time.time()): ... def f(when=dt.datetime.now() + dt.timedelta(days=7)): pass -def can_even_catch_lambdas(a=(lambda x: x)()): - ... +def can_even_catch_lambdas(a=(lambda x: x)()): ... # Recommended approach for function calls as default args diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.7.10/tests/b008_extended.py new/flake8-bugbear-23.9.16/tests/b008_extended.py --- old/flake8-bugbear-23.7.10/tests/b008_extended.py 2023-07-10 18:29:25.000000000 +0200 +++ new/flake8-bugbear-23.9.16/tests/b008_extended.py 2023-09-16 22:26:06.000000000 +0200 @@ -4,13 +4,10 @@ from fastapi import Query -def this_is_okay_extended(db=fastapi.Depends(get_db)): - ... +def this_is_okay_extended(db=fastapi.Depends(get_db)): ... -def this_is_okay_extended_second(data: List[str] = fastapi.Query(None)): - ... +def this_is_okay_extended_second(data: List[str] = fastapi.Query(None)): ... -def this_is_not_okay_relative_import_not_listed(data: List[str] = Query(None)): - ... +def this_is_not_okay_relative_import_not_listed(data: List[str] = Query(None)): ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.7.10/tests/b019.py new/flake8-bugbear-23.9.16/tests/b019.py --- old/flake8-bugbear-23.7.10/tests/b019.py 2023-07-10 18:29:25.000000000 +0200 +++ new/flake8-bugbear-23.9.16/tests/b019.py 2023-09-16 22:26:06.000000000 +0200 @@ -6,98 +6,77 @@ from functools import cache, cached_property, lru_cache -def some_other_cache(): - ... +def some_other_cache(): ... class Foo: def __init__(self, x): self.x = x - def compute_method(self, y): - ... + def compute_method(self, y): ... @some_other_cache - def user_cached_method(self, y): - ... + def user_cached_method(self, y): ... @classmethod @functools.cache - def cached_classmethod(cls, y): - ... + def cached_classmethod(cls, y): ... @classmethod @cache - def other_cached_classmethod(cls, y): - ... + def other_cached_classmethod(cls, y): ... @classmethod @functools.lru_cache - def lru_cached_classmethod(cls, y): - ... + def lru_cached_classmethod(cls, y): ... @classmethod @lru_cache - def other_lru_cached_classmethod(cls, y): - ... + def other_lru_cached_classmethod(cls, y): ... @staticmethod @functools.cache - def cached_staticmethod(y): - ... + def cached_staticmethod(y): ... @staticmethod @cache - def other_cached_staticmethod(y): - ... + def other_cached_staticmethod(y): ... @staticmethod @functools.lru_cache - def lru_cached_staticmethod(y): - ... + def lru_cached_staticmethod(y): ... @staticmethod @lru_cache - def other_lru_cached_staticmethod(y): - ... + def other_lru_cached_staticmethod(y): ... @functools.cached_property - def some_cached_property(self): - ... + def some_cached_property(self): ... @cached_property - def some_other_cached_property(self): - ... + def some_other_cached_property(self): ... # Remaining methods should emit B019 @functools.cache - def cached_method(self, y): - ... + def cached_method(self, y): ... @cache - def another_cached_method(self, y): - ... + def another_cached_method(self, y): ... @functools.cache() - def called_cached_method(self, y): - ... + def called_cached_method(self, y): ... @cache() - def another_called_cached_method(self, y): - ... + def another_called_cached_method(self, y): ... @functools.lru_cache - def lru_cached_method(self, y): - ... + def lru_cached_method(self, y): ... @lru_cache - def another_lru_cached_method(self, y): - ... + def another_lru_cached_method(self, y): ... @functools.lru_cache() - def called_lru_cached_method(self, y): - ... + def called_lru_cached_method(self, y): ... @lru_cache() - def another_called_lru_cached_method(self, y): - ... + def another_called_lru_cached_method(self, y): ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.7.10/tests/b027.py new/flake8-bugbear-23.9.16/tests/b027.py --- old/flake8-bugbear-23.7.10/tests/b027.py 2023-07-10 18:29:25.000000000 +0200 +++ new/flake8-bugbear-23.9.16/tests/b027.py 2023-09-16 22:26:06.000000000 +0200 @@ -32,16 +32,14 @@ ... @abstractmethod - def abstract_1(self): - ... + def abstract_1(self): ... @abstractmethod def abstract_2(self): pass @abc.abstractmethod - def abstract_3(self): - ... + def abstract_3(self): ... def body_1(self): print("foo") @@ -69,21 +67,16 @@ class AstractClass(ABC): @overload - def empty_1(self, foo: str): - ... + def empty_1(self, foo: str): ... @typing.overload - def empty_1(self, foo: int): - ... + def empty_1(self, foo: int): ... @t.overload - def empty_1(self, foo: list): - ... + def empty_1(self, foo: list): ... @anything.overload - def empty_1(self, foo: float): - ... + def empty_1(self, foo: float): ... @abstractmethod - def empty_1(self, foo: Union[str, int, list, float]): - ... + def empty_1(self, foo: Union[str, int, list, float]): ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.7.10/tests/b902.py new/flake8-bugbear-23.9.16/tests/b902.py --- old/flake8-bugbear-23.7.10/tests/b902.py 2023-07-10 18:29:25.000000000 +0200 +++ new/flake8-bugbear-23.9.16/tests/b902.py 2023-09-16 22:26:06.000000000 +0200 @@ -1,63 +1,47 @@ -def not_a_method(arg1): - ... +def not_a_method(arg1): ... class NoWarnings: def __init__(self): - def not_a_method_either(arg1): - ... + def not_a_method_either(arg1): ... - def __new__(cls, *args, **kwargs): - ... + def __new__(cls, *args, **kwargs): ... - def method(self, arg1, *, yeah): - ... + def method(self, arg1, *, yeah): ... - async def async_method(self, arg1, *, yeah): - ... + async def async_method(self, arg1, *, yeah): ... @classmethod - def someclassmethod(cls, arg1, with_default=None): - ... + def someclassmethod(cls, arg1, with_default=None): ... @staticmethod - def not_a_problem(arg1): - ... + def not_a_problem(arg1): ... class Warnings: - def __init__(i_am_special): - ... + def __init__(i_am_special): ... - def almost_a_class_method(cls, arg1): - ... + def almost_a_class_method(cls, arg1): ... - def almost_a_static_method(): - ... + def almost_a_static_method(): ... @classmethod - def wat(self, i_like_confusing_people): - ... + def wat(self, i_like_confusing_people): ... def i_am_strange(*args, **kwargs): self = args[0] - def defaults_anyone(self=None): - ... + def defaults_anyone(self=None): ... - def invalid_kwargs_only(**kwargs): - ... + def invalid_kwargs_only(**kwargs): ... - def invalid_keyword_only(*, self): - ... + def invalid_keyword_only(*, self): ... - async def async_invalid_keyword_only(*, self): - ... + async def async_invalid_keyword_only(*, self): ... class Meta(type): - def __init__(cls, name, bases, d): - ... + def __init__(cls, name, bases, d): ... @classmethod def __prepare__(metacls, name, bases): @@ -65,16 +49,14 @@ class OtherMeta(type): - def __init__(self, name, bases, d): - ... + def __init__(self, name, bases, d): ... @classmethod def __prepare__(cls, name, bases): return {} @classmethod - def first_arg_mcs_allowed(mcs, value): - ... + def first_arg_mcs_allowed(mcs, value): ... def type_factory(): @@ -82,21 +64,16 @@ class CrazyBases(Warnings, type_factory(), metaclass=type): - def __init__(self): - ... + def __init__(self): ... class RuntimeError("This is not a base"): - def __init__(self): - ... + def __init__(self): ... class ImplicitClassMethods: - def __new__(cls, *args, **kwargs): - ... + def __new__(cls, *args, **kwargs): ... - def __init_subclass__(cls, *args, **kwargs): - ... + def __init_subclass__(cls, *args, **kwargs): ... - def __class_getitem__(cls, key): - ... + def __class_getitem__(cls, key): ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.7.10/tests/b902_extended.py new/flake8-bugbear-23.9.16/tests/b902_extended.py --- old/flake8-bugbear-23.7.10/tests/b902_extended.py 1970-01-01 01:00:00.000000000 +0100 +++ new/flake8-bugbear-23.9.16/tests/b902_extended.py 2023-09-16 22:26:06.000000000 +0200 @@ -0,0 +1,104 @@ +# parameters: --classmethod-decorators=["mylibrary.classmethod", "validator"] +class Errors: + # correctly registered as classmethod + @validator + def foo_validator(self) -> None: ... + + @other.validator + def foo_other_validator(self) -> None: ... + + @foo.bar.validator + def foo_foo_bar_validator(self) -> None: ... + + @validator.blah + def foo_validator_blah(cls) -> None: ... + + # specifying attribute in options is not valid + @mylibrary.makeclassmethod + def foo2(cls) -> None: ... + + # specified attribute in options + @makeclassmethod + def foo6(cls) -> None: ... + + # classmethod is default, but if not specified it's ignored + @classmethod + def foo3(cls) -> None: ... + + # random unknown decorator + @aoeuaoeu + def foo5(cls) -> None: ... + + +class NoErrors: + @validator + def foo1(cls) -> None: ... + + @other.validator + def foo4(cls) -> None: ... + + @mylibrary.makeclassmethod + def foo2(self) -> None: ... + + @classmethod + def foo3(self) -> None: ... + + @aoeuaoeu + def foo5(self) -> None: ... + + @makeclassmethod + def foo6(self) -> None: ... + + +# Above tests, duplicated to check that the separate logic for metaclasses also works + + +class ErrorsMeta(type): + # correctly registered as classmethod + @validator + def foo_validator(cls) -> None: ... + + @other.validator + def foo_other_validator(cls) -> None: ... + + @foo.bar.validator + def foo_foo_bar_validator(cls) -> None: ... + + @validator.blah + def foo_validator_blah(metacls) -> None: ... + + # specifying attribute in options is not valid + @mylibrary.makeclassmethod + def foo2(metacls) -> None: ... + + # specified attribute in options + @makeclassmethod + def foo6(metacls) -> None: ... + + # classmethod is default, but if not specified it's ignored + @classmethod + def foo3(metacls) -> None: ... + + # random unknown decorator + @aoeuaoeu + def foo5(metacls) -> None: ... + + +class NoErrorsMeta(type): + @validator + def foo1(metacls) -> None: ... + + @other.validator + def foo4(metacls) -> None: ... + + @mylibrary.makeclassmethod + def foo2(cls) -> None: ... + + @classmethod + def foo3(cls) -> None: ... + + @aoeuaoeu + def foo5(cls) -> None: ... + + @makeclassmethod + def foo6(cls) -> None: ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.7.10/tests/b902_py38.py new/flake8-bugbear-23.9.16/tests/b902_py38.py --- old/flake8-bugbear-23.7.10/tests/b902_py38.py 2023-07-10 18:29:25.000000000 +0200 +++ new/flake8-bugbear-23.9.16/tests/b902_py38.py 2023-09-16 22:26:06.000000000 +0200 @@ -1,63 +1,47 @@ -def not_a_method(arg1, /): - ... +def not_a_method(arg1, /): ... class NoWarnings: def __init__(self, /): - def not_a_method_either(arg1, /): - ... + def not_a_method_either(arg1, /): ... - def __new__(cls, /, *args, **kwargs): - ... + def __new__(cls, /, *args, **kwargs): ... - def method(self, arg1, /, *, yeah): - ... + def method(self, arg1, /, *, yeah): ... - async def async_method(self, arg1, /, *, yeah): - ... + async def async_method(self, arg1, /, *, yeah): ... @classmethod - def someclassmethod(cls, arg1, with_default=None, /): - ... + def someclassmethod(cls, arg1, with_default=None, /): ... @staticmethod - def not_a_problem(arg1, /): - ... + def not_a_problem(arg1, /): ... class Warnings: - def __init__(i_am_special, /): - ... + def __init__(i_am_special, /): ... - def almost_a_class_method(cls, arg1, /): - ... + def almost_a_class_method(cls, arg1, /): ... - def almost_a_static_method(): - ... + def almost_a_static_method(): ... @classmethod - def wat(self, i_like_confusing_people, /): - ... + def wat(self, i_like_confusing_people, /): ... def i_am_strange(*args, **kwargs): self = args[0] - def defaults_anyone(self=None, /): - ... + def defaults_anyone(self=None, /): ... - def invalid_kwargs_only(**kwargs): - ... + def invalid_kwargs_only(**kwargs): ... - def invalid_keyword_only(*, self): - ... + def invalid_keyword_only(*, self): ... - async def async_invalid_keyword_only(*, self): - ... + async def async_invalid_keyword_only(*, self): ... class Meta(type): - def __init__(cls, name, bases, d, /): - ... + def __init__(cls, name, bases, d, /): ... @classmethod def __prepare__(metacls, name, bases, /): @@ -65,16 +49,14 @@ class OtherMeta(type): - def __init__(self, name, bases, d, /): - ... + def __init__(self, name, bases, d, /): ... @classmethod def __prepare__(cls, name, bases, /): return {} @classmethod - def first_arg_mcs_allowed(mcs, value, /): - ... + def first_arg_mcs_allowed(mcs, value, /): ... def type_factory(): @@ -82,21 +64,16 @@ class CrazyBases(Warnings, type_factory(), metaclass=type): - def __init__(self): - ... + def __init__(self): ... class RuntimeError("This is not a base"): - def __init__(self): - ... + def __init__(self): ... class ImplicitClassMethods: - def __new__(cls, /, *args, **kwargs): - ... + def __new__(cls, /, *args, **kwargs): ... - def __init_subclass__(cls, /, *args, **kwargs): - ... + def __init_subclass__(cls, /, *args, **kwargs): ... - def __class_getitem__(cls, key, /): - ... + def __class_getitem__(cls, key, /): ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.7.10/tests/b903.py new/flake8-bugbear-23.9.16/tests/b903.py --- old/flake8-bugbear-23.7.10/tests/b903.py 2023-07-10 18:29:25.000000000 +0200 +++ new/flake8-bugbear-23.9.16/tests/b903.py 2023-09-16 22:26:06.000000000 +0200 @@ -3,8 +3,7 @@ self.foo = foo self.bar = bar - def other_function(self): - ... + def other_function(self): ... class NoWarningsClassAttributes: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.7.10/tests/b906.py new/flake8-bugbear-23.9.16/tests/b906.py --- old/flake8-bugbear-23.7.10/tests/b906.py 2023-07-10 18:29:25.000000000 +0200 +++ new/flake8-bugbear-23.9.16/tests/b906.py 2023-09-16 22:26:06.000000000 +0200 @@ -6,8 +6,7 @@ # error -def visit_For(): - ... +def visit_For(): ... # has call to visit function @@ -27,74 +26,58 @@ # not a valid AST class, no error -def visit_foo(): - ... +def visit_foo(): ... # Break has no subfields to visit, so no error -def visit_Break(): - ... +def visit_Break(): ... # explicitly check `visit` and `generic_visit` # doesn't start with _visit, safe -def visit(): - ... +def visit(): ... # doesn't start with _visit, safe -def generic_visit(): - ... +def generic_visit(): ... # check no crash on short name -def a(): - ... +def a(): ... -def visit_(): - ... +def visit_(): ... # Check exceptions for ast types that only contain ADSL builtin types # i.e. don't contain any ast.AST subnodes and therefore don't need a generic_visit -def visit_alias(): - ... +def visit_alias(): ... -def visit_Constant(): - ... +def visit_Constant(): ... -def visit_Global(): - ... +def visit_Global(): ... -def visit_MatchSingleton(): - ... +def visit_MatchSingleton(): ... -def visit_MatchStar(): - ... +def visit_MatchStar(): ... -def visit_Nonlocal(): - ... +def visit_Nonlocal(): ... -def visit_TypeIgnore(): - ... +def visit_TypeIgnore(): ... # These nodes are deprecated, but some codebases may still use them # for backwards-compatibility with Python 3.7 -def visit_Bytes(): - ... +def visit_Bytes(): ... -def visit_Num(): - ... +def visit_Num(): ... -def visit_Str(): - ... +def visit_Str(): ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.7.10/tests/test_bugbear.py new/flake8-bugbear-23.9.16/tests/test_bugbear.py --- old/flake8-bugbear-23.7.10/tests/test_bugbear.py 2023-07-10 18:29:25.000000000 +0200 +++ new/flake8-bugbear-23.9.16/tests/test_bugbear.py 2023-09-16 22:26:06.000000000 +0200 @@ -114,31 +114,31 @@ self.assertEqual( errors, self.errors( - B006(60, 24), - B006(64, 29), - B006(68, 19), - B006(72, 19), - B006(76, 31), - B006(80, 25), - B006(85, 45), - B008(85, 60), - B006(89, 45), - B008(89, 63), - B006(93, 44), - B008(93, 59), - B006(97, 32), - B008(109, 38), - B008(113, 11), - B008(113, 31), - B008(117, 29), - B008(160, 29), - B008(164, 44), - B006(170, 19), - B008(170, 20), - B008(170, 30), - B008(176, 21), - B008(181, 18), - B008(181, 36), + B006(58, 24), + B006(61, 29), + B006(64, 19), + B006(67, 19), + B006(70, 31), + B006(73, 25), + B006(77, 45), + B008(77, 60), + B006(81, 45), + B008(81, 63), + B006(85, 44), + B008(85, 59), + B006(89, 32), + B008(100, 38), + B008(103, 11), + B008(103, 31), + B008(107, 29), + B008(149, 29), + B008(153, 44), + B006(159, 19), + B008(159, 20), + B008(159, 30), + B008(165, 21), + B008(170, 18), + B008(170, 36), ), ) @@ -168,7 +168,7 @@ self.assertEqual( errors, - self.errors(B008(15, 66)), + self.errors(B008(13, 66)), ) def test_b009_b010(self): @@ -296,14 +296,14 @@ self.assertEqual( errors, self.errors( - B019(73, 5), - B019(77, 5), + B019(60, 5), + B019(63, 5), + B019(66, 5), + B019(69, 5), + B019(72, 5), + B019(75, 5), + B019(78, 5), B019(81, 5), - B019(85, 5), - B019(89, 5), - B019(93, 5), - B019(97, 5), - B019(101, 5), ), ) @@ -654,16 +654,49 @@ self.assertEqual( errors, self.errors( - B902(29, 17, vars=("'i_am_special'", "instance", "self")), - B902(32, 30, vars=("'cls'", "instance", "self")), - B902(35, 4, vars=("(none)", "instance", "self")), - B902(39, 12, vars=("'self'", "class", "cls")), - B902(42, 22, vars=("*args", "instance", "self")), - B902(48, 30, vars=("**kwargs", "instance", "self")), - B902(51, 32, vars=("*, self", "instance", "self")), - B902(54, 44, vars=("*, self", "instance", "self")), - B902(68, 17, vars=("'self'", "metaclass instance", "cls")), - B902(72, 20, vars=("'cls'", "metaclass class", "metacls")), + B902(22, 17, vars=("'i_am_special'", "instance", "self")), + B902(24, 30, vars=("'cls'", "instance", "self")), + B902(26, 4, vars=("(none)", "instance", "self")), + B902(29, 12, vars=("'self'", "class", "cls")), + B902(31, 22, vars=("*args", "instance", "self")), + B902(36, 30, vars=("**kwargs", "instance", "self")), + B902(38, 32, vars=("*, self", "instance", "self")), + B902(40, 44, vars=("*, self", "instance", "self")), + B902(52, 17, vars=("'self'", "metaclass instance", "cls")), + B902(55, 20, vars=("'cls'", "metaclass class", "metacls")), + ), + ) + + def test_b902_extended(self): + filename = Path(__file__).absolute().parent / "b902_extended.py" + + mock_options = Namespace( + classmethod_decorators=["mylibrary.makeclassmethod", "validator"], + select=["B902"], + ) + bbc = BugBearChecker(filename=str(filename), options=mock_options) + errors = list(bbc.run()) + + self.assertEqual( + errors, + self.errors( + B902(5, 22, vars=("'self'", "class", "cls")), + B902(8, 28, vars=("'self'", "class", "cls")), + B902(11, 30, vars=("'self'", "class", "cls")), + B902(14, 27, vars=("'cls'", "instance", "self")), + B902(18, 13, vars=("'cls'", "instance", "self")), + B902(22, 13, vars=("'cls'", "instance", "self")), + B902(26, 13, vars=("'cls'", "instance", "self")), + B902(30, 13, vars=("'cls'", "instance", "self")), + # metaclass + B902(59, 22, vars=("'cls'", "metaclass class", "metacls")), + B902(62, 28, vars=("'cls'", "metaclass class", "metacls")), + B902(65, 30, vars=("'cls'", "metaclass class", "metacls")), + B902(68, 27, vars=("'metacls'", "metaclass instance", "cls")), + B902(72, 13, vars=("'metacls'", "metaclass instance", "cls")), + B902(76, 13, vars=("'metacls'", "metaclass instance", "cls")), + B902(80, 13, vars=("'metacls'", "metaclass instance", "cls")), + B902(84, 13, vars=("'metacls'", "metaclass instance", "cls")), ), ) @@ -674,16 +707,16 @@ self.assertEqual( errors, self.errors( - B902(29, 17, vars=("'i_am_special'", "instance", "self")), - B902(32, 30, vars=("'cls'", "instance", "self")), - B902(35, 4, vars=("(none)", "instance", "self")), - B902(39, 12, vars=("'self'", "class", "cls")), - B902(42, 22, vars=("*args", "instance", "self")), - B902(48, 30, vars=("**kwargs", "instance", "self")), - B902(51, 32, vars=("*, self", "instance", "self")), - B902(54, 44, vars=("*, self", "instance", "self")), - B902(68, 17, vars=("'self'", "metaclass instance", "cls")), - B902(72, 20, vars=("'cls'", "metaclass class", "metacls")), + B902(22, 17, vars=("'i_am_special'", "instance", "self")), + B902(24, 30, vars=("'cls'", "instance", "self")), + B902(26, 4, vars=("(none)", "instance", "self")), + B902(29, 12, vars=("'self'", "class", "cls")), + B902(31, 22, vars=("*args", "instance", "self")), + B902(36, 30, vars=("**kwargs", "instance", "self")), + B902(38, 32, vars=("*, self", "instance", "self")), + B902(40, 44, vars=("*, self", "instance", "self")), + B902(52, 17, vars=("'self'", "metaclass instance", "cls")), + B902(55, 20, vars=("'cls'", "metaclass class", "metacls")), ), ) @@ -691,7 +724,7 @@ filename = Path(__file__).absolute().parent / "b903.py" bbc = BugBearChecker(filename=str(filename)) errors = list(bbc.run()) - self.assertEqual(errors, self.errors(B903(32, 0), B903(38, 0))) + self.assertEqual(errors, self.errors(B903(31, 0), B903(37, 0))) def test_b904(self): filename = Path(__file__).absolute().parent / "b904.py" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flake8-bugbear-23.7.10/tox.ini new/flake8-bugbear-23.9.16/tox.ini --- old/flake8-bugbear-23.7.10/tox.ini 2023-07-10 18:29:25.000000000 +0200 +++ new/flake8-bugbear-23.9.16/tox.ini 2023-09-16 22:26:06.000000000 +0200 @@ -1,14 +1,14 @@ # The test environment and commands [tox] # default environments to run without `-e` -envlist = py38, py39, py310, py311, py312 +envlist = py38, py39, py310, py311, py312, pep8_naming [gh-actions] python = 3.8: py38 3.9: py39 3.10: py310 - 3.11: py311 + 3.11: py311,pep8_naming 3.12: py312 [testenv] @@ -21,6 +21,16 @@ coverage run tests/test_bugbear.py {posargs} coverage report -m +[testenv:pep8_naming] +deps = + coverage + hypothesis + hypothesmith + pep8-naming +commands = + coverage run tests/test_bugbear.py -k b902 {posargs} + coverage report -m + [testenv:py312] deps = # the other dependencies aren't yet installable on py312+