-
-
Notifications
You must be signed in to change notification settings - Fork 34.2k
Closed as not planned
Labels
type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Description
Bug report
Bug description:
i have created a script that illustrates the bug.
in short, when storing lambda functions in a dict, in some cases all dict values have the same lambda.
this only occurs if the (local)-scoped vars are reused to create the lambda.
using uv, i have tested thist with:
- cpython-3.8.20
- cpython-3.9.25
- cpython-3.10.19
- cpython-3.11.14
- cpython-3.12.12
- cpython-3.13.11+freethreaded
- cpython-3.13.11
- cpython-3.14.2+freethreaded
- cpython-3.14.2
- cpython-3.14.3
- cpython-3.15.0a5+freethreaded
- cpython-3.15.0a5
- graalpy-3.11.0
- graalpy-3.12.0
- pypy-3.8.16
- pypy-3.9.19
- pypy-3.10.16
- pypy-3.11.13
from itertools import product
class Bug:
funs = {}
@classmethod
def add(cls, tag, fun):
cls.funs[tag] = fun
def use(self, tag):
return self.funs[tag]()
def a():
return "a"
def b():
return "b"
def test(assign, use_lambda, with_func):
print(f"{assign=}, {use_lambda=} {with_func=}")
ab = (("a", a), ("b", b))
bug = Bug()
def add_fun(bug, t, f):
if not use_lambda:
bug.add(t, f)
else:
bug.add(t, lambda: f())
if assign == "no_var":
# don't use vars, name the tag and function explicitly
if with_func:
add_fun(bug, "a", a)
add_fun(bug, "b", b)
else:
if not use_lambda:
bug.add("a", a)
bug.add("b", b)
else:
bug.add("a", lambda: a())
bug.add("b", lambda: b())
if assign == "loop_var":
# get the tag and function from the `ab` tuple in the loop vars t and f
if with_func:
for t, f in ab:
add_fun(bug, t, f)
else:
for t, f in ab:
if not use_lambda:
bug.add(t, f)
else:
bug.add(t, lambda: f())
if assign == "reuse_var":
# get the tag and function from the `ab` tuple in the local vars t and f
t, f = ab[0]
if with_func:
add_fun(bug, t, f)
else:
if not use_lambda:
bug.add(t, f)
else:
bug.add(t, lambda: f())
t, f = ab[1]
if with_func:
add_fun(bug, t, f)
else:
if not use_lambda:
bug.add(t, f)
else:
bug.add(t, lambda: f())
if assign == "new_var":
# get the tag and function from the `ab` tuple in different local vars (t0, t1) and (f0, f1)
t0, f0 = ab[0]
if with_func:
add_fun(bug, t0, f0)
else:
if not use_lambda:
bug.add(t0, f0)
else:
bug.add(t0, lambda: f0())
t1, f1 = ab[1]
if with_func:
add_fun(bug, t1, f1)
else:
if not use_lambda:
bug.add(t1, f1)
else:
bug.add(t1, lambda: f1())
a_ok = bug.use("a") == "a"
b_ok = bug.use("b") == "b"
print(
"a", a_ok, end="\n" if a_ok else f" # BUG: Expect 'a', got {bug.use('a')!r}\n"
)
print(
"b", b_ok, end="\n" if b_ok else f" # BUG: Expect 'b', got {bug.use('b')!r}\n"
)
print()
if __name__ == "__main__":
for assign, use_lambda, with_func in product(
("no_var", "loop_var", "reuse_var", "new_var"), (False, True), (False, True)
):
test(assign, use_lambda, with_func)CPython versions tested on:
3.14
Operating systems tested on:
Linux
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error