Skip to content

Commit b950cb8

Browse files
torquehynek
andauthored
compat: move check for closure cellvar update wrapper function (#1092)
* compat: move check for closure cellvar update wrapper function This sanity checking code ended up getting decoupled from the old implementation in a18b395 when a new, much simpler method was added. This check can fail spuriously in certain environments where it doesn't even need to be run because the newer approach is used. This is noticeable when using python scripts compiled by nuitka. Since next-gen attrs uses slots on everything by default, the use of this fixup function is now pervasive, as well. * changelog: note fix * Semantic newlines --------- Co-authored-by: Hynek Schlawack <[email protected]>
1 parent bf52236 commit b950cb8

File tree

2 files changed

+20
-19
lines changed

2 files changed

+20
-19
lines changed

changelog.d/1092.change.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix slots class cellvar updating closure in CPython 3.8+ even when `__code__` introspection is unavailable.

src/attr/_compat.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -82,32 +82,32 @@ def set_closure_cell(cell, value):
8282

8383
# Otherwise gotta do it the hard way.
8484

85-
# Create a function that will set its first cellvar to `value`.
86-
def set_first_cellvar_to(value):
87-
x = value
88-
return
89-
90-
# This function will be eliminated as dead code, but
91-
# not before its reference to `x` forces `x` to be
92-
# represented as a closure cell rather than a local.
93-
def force_x_to_be_a_cell(): # pragma: no cover
94-
return x
95-
9685
try:
97-
# Extract the code object and make sure our assumptions about
98-
# the closure behavior are correct.
99-
co = set_first_cellvar_to.__code__
100-
if co.co_cellvars != ("x",) or co.co_freevars != ():
101-
raise AssertionError # pragma: no cover
102-
103-
# Convert this code object to a code object that sets the
104-
# function's first _freevar_ (not cellvar) to the argument.
10586
if sys.version_info >= (3, 8):
10687

10788
def set_closure_cell(cell, value):
10889
cell.cell_contents = value
10990

11091
else:
92+
# Create a function that will set its first cellvar to `value`.
93+
def set_first_cellvar_to(value):
94+
x = value
95+
return
96+
97+
# This function will be eliminated as dead code, but
98+
# not before its reference to `x` forces `x` to be
99+
# represented as a closure cell rather than a local.
100+
def force_x_to_be_a_cell(): # pragma: no cover
101+
return x
102+
103+
# Extract the code object and make sure our assumptions about
104+
# the closure behavior are correct.
105+
co = set_first_cellvar_to.__code__
106+
if co.co_cellvars != ("x",) or co.co_freevars != ():
107+
raise AssertionError # pragma: no cover
108+
109+
# Convert this code object to a code object that sets the
110+
# function's first _freevar_ (not cellvar) to the argument.
111111
args = [co.co_argcount]
112112
args.append(co.co_kwonlyargcount)
113113
args.extend(

0 commit comments

Comments
 (0)