Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3b0431d

Browse files
committed
check local class namespace before reaching for cells (closes #17853)
1 parent f256f5f commit 3b0431d

10 files changed

Lines changed: 187 additions & 122 deletions

File tree

Doc/library/dis.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,13 @@ the more significant byte last.
722722
Pushes a reference to the object the cell contains on the stack.
723723

724724

725+
.. opcode:: LOAD_CLASSDEREF (i)
726+
727+
Much like :opcode:`LOAD_DEREF` but first checks the locals dictionary before
728+
consulting the cell. This is used for loading free variables in class
729+
bodies.
730+
731+
725732
.. opcode:: STORE_DEREF (i)
726733

727734
Stores TOS into the cell contained in slot *i* of the cell and free variable

Include/opcode.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ extern "C" {
140140
#define SET_ADD 146
141141
#define MAP_ADD 147
142142

143+
#define LOAD_CLASSDEREF 148
143144

144145
/* EXCEPT_HANDLER is a special, implicit block type which is created when
145146
entering an except handler. It is not an opcode but we define it here

Lib/importlib/_bootstrap.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,12 +388,14 @@ def _call_with_frames_removed(f, *args, **kwds):
388388
# Python 3.3a4 3230 (revert changes to implicit __class__ closure)
389389
# Python 3.4a1 3250 (evaluate positional default arguments before
390390
# keyword-only defaults)
391+
# Python 3.4a1 3260 (add LOAD_CLASSDEREF; allow locals of class to override
392+
# free vars)
391393
#
392394
# MAGIC must change whenever the bytecode emitted by the compiler may no
393395
# longer be understood by older implementations of the eval loop (usually
394396
# due to the addition of new opcodes).
395397

396-
_MAGIC_BYTES = (3250).to_bytes(2, 'little') + b'\r\n'
398+
_MAGIC_BYTES = (3260).to_bytes(2, 'little') + b'\r\n'
397399
_RAW_MAGIC_NUMBER = int.from_bytes(_MAGIC_BYTES, 'little')
398400

399401
_PYCACHE = '__pycache__'

Lib/opcode.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ def jabs_op(name, op):
179179
def_op('SET_ADD', 146)
180180
def_op('MAP_ADD', 147)
181181

182+
def_op('LOAD_CLASSDEREF', 148)
183+
hasfree.append(148)
184+
182185
def_op('EXTENDED_ARG', 144)
183186
EXTENDED_ARG = 144
184187

Lib/test/test_scope.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,19 @@ def b():
714714
global a
715715

716716

717+
def testClassNamespaceOverridesClosure(self):
718+
# See #17853.
719+
x = 42
720+
class X:
721+
locals()["x"] = 43
722+
y = x
723+
self.assertEqual(X.y, 43)
724+
class X:
725+
locals()["x"] = 43
726+
del x
727+
self.assertFalse(hasattr(X, "x"))
728+
self.assertEqual(x, 42)
729+
717730

718731
def test_main():
719732
run_unittest(ScopeTests)

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ What's New in Python 3.4.0 Alpha 1?
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #17853: Ensure locals of a class that shadow free variables always win
14+
over the closures.
15+
1316
- Issue #17863: In the interactive console, don't loop forever if the encoding
1417
can't be fetched from stdin.
1518

Python/ceval.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2260,6 +2260,39 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
22602260
DISPATCH();
22612261
}
22622262

2263+
TARGET(LOAD_CLASSDEREF) {
2264+
PyObject *name, *value, *locals = f->f_locals;
2265+
int idx;
2266+
assert(locals);
2267+
assert(oparg >= PyTuple_GET_SIZE(co->co_cellvars));
2268+
idx = oparg - PyTuple_GET_SIZE(co->co_cellvars);
2269+
assert(idx >= 0 && idx < PyTuple_GET_SIZE(co->co_freevars));
2270+
name = PyTuple_GET_ITEM(co->co_freevars, idx);
2271+
if (PyDict_CheckExact(locals)) {
2272+
value = PyDict_GetItem(locals, name);
2273+
Py_XINCREF(value);
2274+
}
2275+
else {
2276+
value = PyObject_GetItem(locals, name);
2277+
if (value == NULL && PyErr_Occurred()) {
2278+
if (!PyErr_ExceptionMatches(PyExc_KeyError))
2279+
goto error;
2280+
PyErr_Clear();
2281+
}
2282+
}
2283+
if (!value) {
2284+
PyObject *cell = freevars[oparg];
2285+
value = PyCell_GET(cell);
2286+
if (value == NULL) {
2287+
format_exc_unbound(co, oparg);
2288+
goto error;
2289+
}
2290+
Py_INCREF(value);
2291+
}
2292+
PUSH(value);
2293+
DISPATCH();
2294+
}
2295+
22632296
TARGET(LOAD_DEREF) {
22642297
PyObject *cell = freevars[oparg];
22652298
PyObject *value = PyCell_GET(cell);

Python/compile.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,7 @@ opcode_stack_effect(int opcode, int oparg)
970970
case LOAD_CLOSURE:
971971
return 1;
972972
case LOAD_DEREF:
973+
case LOAD_CLASSDEREF:
973974
return 1;
974975
case STORE_DEREF:
975976
return -1;
@@ -2677,7 +2678,9 @@ compiler_nameop(struct compiler *c, identifier name, expr_context_ty ctx)
26772678
switch (optype) {
26782679
case OP_DEREF:
26792680
switch (ctx) {
2680-
case Load: op = LOAD_DEREF; break;
2681+
case Load:
2682+
op = (c->u->u_ste->ste_type == ClassBlock) ? LOAD_CLASSDEREF : LOAD_DEREF;
2683+
break;
26812684
case Store: op = STORE_DEREF; break;
26822685
case AugLoad:
26832686
case AugStore:

0 commit comments

Comments
 (0)