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 2022-10-27 13:54:33
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-flake8-bugbear (Old)
 and      /work/SRC/openSUSE:Factory/.python-flake8-bugbear.new.2275 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-flake8-bugbear"

Thu Oct 27 13:54:33 2022 rev:9 rq:1031467 version:22.10.27

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-flake8-bugbear/python-flake8-bugbear.changes  
    2022-10-26 12:32:05.768351483 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-flake8-bugbear.new.2275/python-flake8-bugbear.changes
    2022-10-27 13:55:09.512869342 +0200
@@ -1,0 +2,8 @@
+Thu Oct 27 07:26:17 UTC 2022 - Martin Li??ka <mli...@suse.cz>
+
+- Update to 22.10.27:
+  * B027: Ignore @overload decorator (#306)
+  * B023: Also fix map (#305)
+  * B023: Avoid false alarms with filter, reduce, key= and return. Added tests 
for functools (#303)
+
+-------------------------------------------------------------------

Old:
----
  flake8-bugbear-22.10.25.tar.gz

New:
----
  flake8-bugbear-22.10.27.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-flake8-bugbear.spec ++++++
--- /var/tmp/diff_new_pack.NaroCG/_old  2022-10-27 13:55:09.960871626 +0200
+++ /var/tmp/diff_new_pack.NaroCG/_new  2022-10-27 13:55:09.964871647 +0200
@@ -19,7 +19,7 @@
 %define skip_python2 1
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-flake8-bugbear
-Version:        22.10.25
+Version:        22.10.27
 Release:        0
 Summary:        A plugin for flake8 finding likely bugs and design problems in 
your program
 License:        MIT

++++++ flake8-bugbear-22.10.25.tar.gz -> flake8-bugbear-22.10.27.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/flake8-bugbear-22.10.25/PKG-INFO 
new/flake8-bugbear-22.10.27/PKG-INFO
--- old/flake8-bugbear-22.10.25/PKG-INFO        2022-10-25 01:25:14.457198000 
+0200
+++ new/flake8-bugbear-22.10.27/PKG-INFO        2022-10-27 00:37:28.730224100 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: flake8-bugbear
-Version: 22.10.25
+Version: 22.10.27
 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
@@ -326,6 +326,13 @@
 Change Log
 ----------
 
+22.10.27
+~~~~~~~~~
+
+* B027: Ignore @overload decorator (#306)
+* B023: Also fix map (#305)
+* B023: Avoid false alarms with filter, reduce, key= and return. Added tests 
for functools (#303)
+
 22.10.25
 ~~~~~~~~~
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/flake8-bugbear-22.10.25/README.rst 
new/flake8-bugbear-22.10.27/README.rst
--- old/flake8-bugbear-22.10.25/README.rst      2022-10-25 01:25:06.000000000 
+0200
+++ new/flake8-bugbear-22.10.27/README.rst      2022-10-27 00:37:19.000000000 
+0200
@@ -297,6 +297,13 @@
 Change Log
 ----------
 
+22.10.27
+~~~~~~~~~
+
+* B027: Ignore @overload decorator (#306)
+* B023: Also fix map (#305)
+* B023: Avoid false alarms with filter, reduce, key= and return. Added tests 
for functools (#303)
+
 22.10.25
 ~~~~~~~~~
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/flake8-bugbear-22.10.25/bugbear.py 
new/flake8-bugbear-22.10.27/bugbear.py
--- old/flake8-bugbear-22.10.25/bugbear.py      2022-10-25 01:25:06.000000000 
+0200
+++ new/flake8-bugbear-22.10.27/bugbear.py      2022-10-27 00:37:19.000000000 
+0200
@@ -12,7 +12,7 @@
 import attr
 import pycodestyle
 
-__version__ = "22.10.25"
+__version__ = "22.10.27"
 
 LOG = logging.getLogger("flake8.bugbear")
 CONTEXTFUL_NODES = (
@@ -280,7 +280,7 @@
             names = [_to_name_str(e) for e in node.type.elts]
             as_ = " as " + node.name if node.name is not None else ""
             if len(names) == 0:
-                vs = ("`except (){}:`".format(as_),)
+                vs = (f"`except (){as_}:`",)
                 self.errors.append(B001(node.lineno, node.col_offset, vars=vs))
             elif len(names) == 1:
                 self.errors.append(B013(node.lineno, node.col_offset, 
vars=names))
@@ -568,7 +568,7 @@
                 n = targets.names[name][0]
                 self.errors.append(B020(n.lineno, n.col_offset, vars=(name,)))
 
-    def check_for_b023(self, loop_node):
+    def check_for_b023(self, loop_node):  # noqa: C901
         """Check that functions (including lambdas) do not use loop variables.
 
         https://docs.python-guide.org/writing/gotchas/#late-binding-closures 
from
@@ -584,9 +584,41 @@
         # implement this "backwards": first we find all the candidate variable
         # uses, and then if there are any we check for assignment of those 
names
         # inside the loop body.
+        safe_functions = []
         suspicious_variables = []
         for node in ast.walk(loop_node):
-            if isinstance(node, FUNCTION_NODES):
+            # check if function is immediately consumed to avoid false alarm
+            if isinstance(node, ast.Call):
+                # check for filter&reduce
+                if (
+                    isinstance(node.func, ast.Name)
+                    and node.func.id in ("filter", "reduce", "map")
+                ) or (
+                    isinstance(node.func, ast.Attribute)
+                    and node.func.attr == "reduce"
+                    and isinstance(node.func.value, ast.Name)
+                    and node.func.value.id == "functools"
+                ):
+                    for arg in node.args:
+                        if isinstance(arg, FUNCTION_NODES):
+                            safe_functions.append(arg)
+
+                # check for key=
+                for keyword in node.keywords:
+                    if keyword.arg == "key" and isinstance(
+                        keyword.value, FUNCTION_NODES
+                    ):
+                        safe_functions.append(keyword.value)
+
+            # mark `return lambda: x` as safe
+            # does not (currently) check inner lambdas in a returned expression
+            # e.g. `return (lambda: x, )
+            if isinstance(node, ast.Return):
+                if isinstance(node.value, FUNCTION_NODES):
+                    safe_functions.append(node.value)
+
+            # find unsafe functions
+            if isinstance(node, FUNCTION_NODES) and node not in safe_functions:
                 argnames = {
                     arg.arg for arg in ast.walk(node.args) if isinstance(arg, 
ast.arg)
                 }
@@ -594,16 +626,19 @@
                     body_nodes = ast.walk(node.body)
                 else:
                     body_nodes = itertools.chain.from_iterable(map(ast.walk, 
node.body))
+                errors = []
                 for name in body_nodes:
-                    if (
-                        isinstance(name, ast.Name)
-                        and name.id not in argnames
-                        and isinstance(name.ctx, ast.Load)
-                    ):
-                        err = B023(name.lineno, name.col_offset, 
vars=(name.id,))
-                        if err not in self._b023_seen:
-                            self._b023_seen.add(err)  # dedupe across nested 
loops
-                            suspicious_variables.append(err)
+                    if isinstance(name, ast.Name) and name.id not in argnames:
+                        if isinstance(name.ctx, ast.Load):
+                            errors.append(
+                                B023(name.lineno, name.col_offset, 
vars=(name.id,))
+                            )
+                        elif isinstance(name.ctx, ast.Store):
+                            argnames.add(name.id)
+                for err in errors:
+                    if err.vars[0] not in argnames and err not in 
self._b023_seen:
+                        self._b023_seen.add(err)  # dedupe across nested loops
+                        suspicious_variables.append(err)
 
         if suspicious_variables:
             reassigned_in_loop = set(self._get_assigned_names(loop_node))
@@ -634,6 +669,14 @@
                 isinstance(expr, ast.Attribute) and expr.attr[:8] == "abstract"
             )
 
+        def is_overload(expr):
+            return (isinstance(expr, ast.Name) and expr.id == "overload") or (
+                isinstance(expr, ast.Attribute)
+                and isinstance(expr.value, ast.Name)
+                and expr.value.id == "typing"
+                and expr.attr == "overload"
+            )
+
         def empty_body(body) -> bool:
             def is_str_or_ellipsis(node):
                 # ast.Ellipsis and ast.Str used in python<3.8
@@ -677,7 +720,11 @@
 
             has_abstract_method |= has_abstract_decorator
 
-            if not has_abstract_decorator and empty_body(stmt.body):
+            if (
+                not has_abstract_decorator
+                and empty_body(stmt.body)
+                and not any(map(is_overload, stmt.decorator_list))
+            ):
                 self.errors.append(
                     B027(stmt.lineno, stmt.col_offset, vars=(stmt.name,))
                 )
@@ -912,7 +959,7 @@
                     uniques.add(name)
                 seen.extend(uniques)
         # sort to have a deterministic output
-        duplicates = sorted(set(x for x in seen if seen.count(x) > 1))
+        duplicates = sorted({x for x in seen if seen.count(x) > 1})
         for duplicate in duplicates:
             self.errors.append(B025(node.lineno, node.col_offset, 
vars=(duplicate,)))
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/flake8-bugbear-22.10.25/flake8_bugbear.egg-info/PKG-INFO 
new/flake8-bugbear-22.10.27/flake8_bugbear.egg-info/PKG-INFO
--- old/flake8-bugbear-22.10.25/flake8_bugbear.egg-info/PKG-INFO        
2022-10-25 01:25:14.000000000 +0200
+++ new/flake8-bugbear-22.10.27/flake8_bugbear.egg-info/PKG-INFO        
2022-10-27 00:37:28.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: flake8-bugbear
-Version: 22.10.25
+Version: 22.10.27
 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
@@ -326,6 +326,13 @@
 Change Log
 ----------
 
+22.10.27
+~~~~~~~~~
+
+* B027: Ignore @overload decorator (#306)
+* B023: Also fix map (#305)
+* B023: Avoid false alarms with filter, reduce, key= and return. Added tests 
for functools (#303)
+
 22.10.25
 ~~~~~~~~~
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/flake8-bugbear-22.10.25/tests/b023.py 
new/flake8-bugbear-22.10.27/tests/b023.py
--- old/flake8-bugbear-22.10.25/tests/b023.py   2022-10-25 01:25:06.000000000 
+0200
+++ new/flake8-bugbear-22.10.27/tests/b023.py   2022-10-27 00:37:19.000000000 
+0200
@@ -2,10 +2,10 @@
 Should emit:
 B023 - on lines 12, 13, 16, 28, 29, 30, 31, 40, 42, 50, 51, 52, 53, 61, 68.
 """
+from functools import reduce
 
 functions = []
 z = 0
-
 for x in range(3):
     y = x + 1
     # Subject to late-binding problems
@@ -25,10 +25,10 @@
 
 
 def check_inside_functions_too():
-    ls = [lambda: x for x in range(2)]
-    st = {lambda: x for x in range(2)}
-    gn = (lambda: x for x in range(2))
-    dt = {x: lambda: x for x in range(2)}
+    ls = [lambda: x for x in range(2)]  # error
+    st = {lambda: x for x in range(2)}  # error
+    gn = (lambda: x for x in range(2))  # error
+    dt = {x: lambda: x for x in range(2)}  # error
 
 
 async def pointless_async_iterable():
@@ -37,9 +37,9 @@
 
 async def container_for_problems():
     async for x in pointless_async_iterable():
-        functions.append(lambda: x)
+        functions.append(lambda: x)  # error
 
-    [lambda: x async for x in pointless_async_iterable()]
+    [lambda: x async for x in pointless_async_iterable()]  # error
 
 
 a = 10
@@ -47,10 +47,10 @@
 while True:
     a = a_ = a - 1
     b += 1
-    functions.append(lambda: a)
-    functions.append(lambda: a_)
-    functions.append(lambda: b)
-    functions.append(lambda: c)  # not a name error because of late binding!
+    functions.append(lambda: a)  # error
+    functions.append(lambda: a_)  # error
+    functions.append(lambda: b)  # error
+    functions.append(lambda: c)  # error, but not a name error due to late 
binding
     c: bool = a > 3
     if not c:
         break
@@ -58,7 +58,7 @@
 # Nested loops should not duplicate reports
 for j in range(2):
     for k in range(3):
-        lambda: j * k
+        lambda: j * k  # error
 
 
 for j, k, l in [(1, 2, 3)]:
@@ -76,3 +76,95 @@
 
     def explicit_capture(captured=var):
         return captured
+
+
+# `query` is defined in the function, so also defining it in the loop should 
be OK.
+for name in ["a", "b"]:
+    query = name
+
+    def myfunc(x):
+        query = x
+        query_post = x
+        _ = query
+        _ = query_post
+
+    query_post = name  # in case iteration order matters
+
+
+# Bug here because two dict comprehensions reference `name`, one of which is 
inside
+# the lambda.  This should be totally fine, of course.
+_ = {
+    k: v
+    for k, v in reduce(
+        lambda data, event: merge_mappings(
+            [data, {name: f(caches, data, event) for name, f in xx}]
+        ),
+        events,
+        {name: getattr(group, name) for name in yy},
+    ).items()
+    if k in backfill_fields
+}
+
+
+# OK to define lambdas if they're immediately consumed, typically as the `key=`
+# argument or in a consumed `filter()` (even if a comprehension is better 
style)
+for x in range(2):
+    # It's not a complete get-out-of-linting-free construct - these should 
fail:
+    min([None, lambda: x], key=repr)
+    sorted([None, lambda: x], key=repr)
+    any(filter(bool, [None, lambda: x]))
+    list(filter(bool, [None, lambda: x]))
+    all(reduce(bool, [None, lambda: x]))
+
+    # But all these ones should be OK:
+    min(range(3), key=lambda y: x * y)
+    max(range(3), key=lambda y: x * y)
+    sorted(range(3), key=lambda y: x * y)
+
+    any(map(lambda y: x < y, range(3)))
+    all(map(lambda y: x < y, range(3)))
+    set(map(lambda y: x < y, range(3)))
+    list(map(lambda y: x < y, range(3)))
+    tuple(map(lambda y: x < y, range(3)))
+    sorted(map(lambda y: x < y, range(3)))
+    frozenset(map(lambda y: x < y, range(3)))
+
+    any(filter(lambda y: x < y, range(3)))
+    all(filter(lambda y: x < y, range(3)))
+    set(filter(lambda y: x < y, range(3)))
+    list(filter(lambda y: x < y, range(3)))
+    tuple(filter(lambda y: x < y, range(3)))
+    sorted(filter(lambda y: x < y, range(3)))
+    frozenset(filter(lambda y: x < y, range(3)))
+
+    any(reduce(lambda y: x | y, range(3)))
+    all(reduce(lambda y: x | y, range(3)))
+    set(reduce(lambda y: x | y, range(3)))
+    list(reduce(lambda y: x | y, range(3)))
+    tuple(reduce(lambda y: x | y, range(3)))
+    sorted(reduce(lambda y: x | y, range(3)))
+    frozenset(reduce(lambda y: x | y, range(3)))
+
+    import functools
+
+    any(functools.reduce(lambda y: x | y, range(3)))
+    all(functools.reduce(lambda y: x | y, range(3)))
+    set(functools.reduce(lambda y: x | y, range(3)))
+    list(functools.reduce(lambda y: x | y, range(3)))
+    tuple(functools.reduce(lambda y: x | y, range(3)))
+    sorted(functools.reduce(lambda y: x | y, range(3)))
+    frozenset(functools.reduce(lambda y: x | y, range(3)))
+
+# OK because the lambda which references a loop variable is defined in a 
`return`
+# statement, and after we return the loop variable can't be redefined.
+# In principle we could do something fancy with `break`, but it's not worth it.
+def iter_f(names):
+    for name in names:
+        if exists(name):
+            return lambda: name if exists(name) else None
+
+        if foo(name):
+            return [lambda: name]  # known false alarm
+
+        if False:
+            return [lambda: i for i in range(3)]  # error
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/flake8-bugbear-22.10.25/tests/b027.py 
new/flake8-bugbear-22.10.27/tests/b027.py
--- old/flake8-bugbear-22.10.25/tests/b027.py   2022-10-25 01:25:06.000000000 
+0200
+++ new/flake8-bugbear-22.10.27/tests/b027.py   2022-10-27 00:37:19.000000000 
+0200
@@ -5,7 +5,7 @@
 
 """
 Should emit:
-B025 - on lines 13, 16, 19, 23, 31
+B027 - on lines 13, 16, 19, 23, 31
 """
 
 
@@ -57,3 +57,22 @@
 
     def empty_2(self):  # safe
         pass
+
+
+# ignore @overload, fixes issue #304
+import typing
+from typing import Union, overload
+
+
+class AstractClass(ABC):
+    @overload
+    def empty_1(self, foo: str):
+        ...
+
+    @typing.overload
+    def empty_1(self, foo: int):
+        ...
+
+    @abstractmethod
+    def empty_1(self, foo: Union[str, int]):
+        ...
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/flake8-bugbear-22.10.25/tests/test_bugbear.py 
new/flake8-bugbear-22.10.27/tests/test_bugbear.py
--- old/flake8-bugbear-22.10.25/tests/test_bugbear.py   2022-10-25 
01:25:06.000000000 +0200
+++ new/flake8-bugbear-22.10.27/tests/test_bugbear.py   2022-10-27 
00:37:19.000000000 +0200
@@ -351,6 +351,13 @@
             B023(61, 16, vars=("j",)),
             B023(61, 20, vars=("k",)),
             B023(68, 9, vars=("l",)),
+            B023(113, 23, vars=("x",)),
+            B023(114, 26, vars=("x",)),
+            B023(115, 36, vars=("x",)),
+            B023(116, 37, vars=("x",)),
+            B023(117, 36, vars=("x",)),
+            B023(167, 28, vars=("name",)),  # known false alarm
+            B023(170, 28, vars=("i",)),
         )
         self.assertEqual(errors, expected)
 

Reply via email to