Skip to content

Commit 312595c

Browse files
committed
hide the __class__ closure from the class body (#12370)
1 parent fe361df commit 312595c

File tree

7 files changed

+221
-152
lines changed

7 files changed

+221
-152
lines changed

Include/symtable.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ typedef struct _symtable_entry {
5353
unsigned ste_varkeywords : 1; /* true if block has varkeywords */
5454
unsigned ste_returns_value : 1; /* true if namespace uses return with
5555
an argument */
56+
unsigned ste_needs_class_closure : 1; /* for class scopes, true if a
57+
closure over __class__
58+
should be created */
5659
int ste_lineno; /* first line of block */
5760
int ste_col_offset; /* offset of first line of block */
5861
int ste_opt_lineno; /* lineno of last exec or import * */

Lib/importlib/_bootstrap.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,12 +390,13 @@ def _call_with_frames_removed(f, *args, **kwds):
390390
# keyword-only defaults)
391391
# Python 3.4a1 3260 (add LOAD_CLASSDEREF; allow locals of class to override
392392
# free vars)
393+
# Python 3.4a1 3270 (various tweaks to the __class_ closure)
393394
#
394395
# MAGIC must change whenever the bytecode emitted by the compiler may no
395396
# longer be understood by older implementations of the eval loop (usually
396397
# due to the addition of new opcodes).
397398

398-
_MAGIC_BYTES = (3260).to_bytes(2, 'little') + b'\r\n'
399+
_MAGIC_BYTES = (3270).to_bytes(2, 'little') + b'\r\n'
399400
_RAW_MAGIC_NUMBER = int.from_bytes(_MAGIC_BYTES, 'little')
400401

401402
_PYCACHE = '__pycache__'

Lib/test/test_super.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,7 @@ def nested():
8181

8282
self.assertEqual(E().f(), 'AE')
8383

84-
@unittest.expectedFailure
85-
def test___class___set(self):
84+
def test_various___class___pathologies(self):
8685
# See issue #12370
8786
class X(A):
8887
def f(self):
@@ -91,6 +90,31 @@ def f(self):
9190
x = X()
9291
self.assertEqual(x.f(), 'A')
9392
self.assertEqual(x.__class__, 413)
93+
class X:
94+
x = __class__
95+
def f():
96+
__class__
97+
self.assertIs(X.x, type(self))
98+
with self.assertRaises(NameError) as e:
99+
exec("""class X:
100+
__class__
101+
def f():
102+
__class__""", globals(), {})
103+
self.assertIs(type(e.exception), NameError) # Not UnboundLocalError
104+
class X:
105+
global __class__
106+
__class__ = 42
107+
def f():
108+
__class__
109+
self.assertEqual(globals()["__class__"], 42)
110+
del globals()["__class__"]
111+
self.assertNotIn("__class__", X.__dict__)
112+
class X:
113+
nonlocal __class__
114+
__class__ = 42
115+
def f():
116+
__class__
117+
self.assertEqual(__class__, 42)
94118

95119
def test___class___instancemethod(self):
96120
# See issue #14857

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 #12370: Prevent class bodies from interfering with the __class__
14+
closure.
15+
1316
- Issue #17237: Fix crash in the ASCII decoder on m68k.
1417

1518
- Issue #17927: Frame objects kept arguments alive if they had been

Python/compile.c

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,37 @@ compiler_enter_scope(struct compiler *c, identifier name,
535535
compiler_unit_free(u);
536536
return 0;
537537
}
538+
if (u->u_ste->ste_needs_class_closure) {
539+
/* Cook up a implicit __class__ cell. */
540+
_Py_IDENTIFIER(__class__);
541+
PyObject *tuple, *name, *zero;
542+
int res;
543+
assert(u->u_scope_type == COMPILER_SCOPE_CLASS);
544+
assert(PyDict_Size(u->u_cellvars) == 0);
545+
name = _PyUnicode_FromId(&PyId___class__);
546+
if (!name) {
547+
compiler_unit_free(u);
548+
return 0;
549+
}
550+
tuple = PyTuple_Pack(2, name, Py_TYPE(name));
551+
if (!tuple) {
552+
compiler_unit_free(u);
553+
return 0;
554+
}
555+
zero = PyLong_FromLong(0);
556+
if (!zero) {
557+
Py_DECREF(tuple);
558+
compiler_unit_free(u);
559+
return 0;
560+
}
561+
res = PyDict_SetItem(u->u_cellvars, tuple, zero);
562+
Py_DECREF(tuple);
563+
Py_DECREF(zero);
564+
if (res < 0) {
565+
compiler_unit_free(u);
566+
return 0;
567+
}
568+
}
538569

539570
u->u_freevars = dictbytype(u->u_ste->ste_symbols, FREE, DEF_FREE_CLASS,
540571
PyDict_Size(u->u_cellvars));
@@ -1331,6 +1362,9 @@ compiler_mod(struct compiler *c, mod_ty mod)
13311362
static int
13321363
get_ref_type(struct compiler *c, PyObject *name)
13331364
{
1365+
if (c->u->u_scope_type == COMPILER_SCOPE_CLASS &&
1366+
!PyUnicode_CompareWithASCIIString(name, "__class__"))
1367+
return CELL;
13341368
int scope = PyST_GetScope(c->u->u_ste, name);
13351369
if (scope == 0) {
13361370
char buf[350];
@@ -1704,24 +1738,24 @@ compiler_class(struct compiler *c, stmt_ty s)
17041738
compiler_exit_scope(c);
17051739
return 0;
17061740
}
1707-
/* return the (empty) __class__ cell */
1708-
str = PyUnicode_InternFromString("__class__");
1709-
if (str == NULL) {
1710-
compiler_exit_scope(c);
1711-
return 0;
1712-
}
1713-
i = compiler_lookup_arg(c->u->u_cellvars, str);
1714-
Py_DECREF(str);
1715-
if (i == -1) {
1716-
/* This happens when nobody references the cell */
1717-
PyErr_Clear();
1718-
/* Return None */
1719-
ADDOP_O(c, LOAD_CONST, Py_None, consts);
1720-
}
1721-
else {
1741+
if (c->u->u_ste->ste_needs_class_closure) {
1742+
/* return the (empty) __class__ cell */
1743+
str = PyUnicode_InternFromString("__class__");
1744+
if (str == NULL) {
1745+
compiler_exit_scope(c);
1746+
return 0;
1747+
}
1748+
i = compiler_lookup_arg(c->u->u_cellvars, str);
1749+
Py_DECREF(str);
1750+
assert(i == 0);
17221751
/* Return the cell where to store __class__ */
17231752
ADDOP_I(c, LOAD_CLOSURE, i);
17241753
}
1754+
else {
1755+
assert(PyDict_Size(c->u->u_cellvars) == 0);
1756+
/* This happens when nobody references the cell. Return None. */
1757+
ADDOP_O(c, LOAD_CONST, Py_None, consts);
1758+
}
17251759
ADDOP_IN_SCOPE(c, RETURN_VALUE);
17261760
/* create the code object */
17271761
co = assemble(c, 1);

0 commit comments

Comments
 (0)