[Python-checkins] cpython: hide the __class__ closure from the class body (#12370)
benjamin.peterson
python-checkins at python.org
Wed May 15 22:27:40 CEST 2013
http://hg.python.org/cpython/rev/3d858f1eef54
changeset: 83783:3d858f1eef54
user: Benjamin Peterson <benjamin at python.org>
date: Wed May 15 15:26:42 2013 -0500
summary:
hide the __class__ closure from the class body (#12370)
files:
Include/symtable.h | 3 +
Lib/importlib/_bootstrap.py | 3 +-
Lib/test/test_super.py | 28 ++-
Misc/NEWS | 3 +
Python/compile.c | 64 ++++-
Python/importlib.h | 238 ++++++++++++------------
Python/symtable.c | 34 +-
7 files changed, 221 insertions(+), 152 deletions(-)
diff --git a/Include/symtable.h b/Include/symtable.h
--- a/Include/symtable.h
+++ b/Include/symtable.h
@@ -53,6 +53,9 @@
unsigned ste_varkeywords : 1; /* true if block has varkeywords */
unsigned ste_returns_value : 1; /* true if namespace uses return with
an argument */
+ unsigned ste_needs_class_closure : 1; /* for class scopes, true if a
+ closure over __class__
+ should be created */
int ste_lineno; /* first line of block */
int ste_col_offset; /* offset of first line of block */
int ste_opt_lineno; /* lineno of last exec or import * */
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py
--- a/Lib/importlib/_bootstrap.py
+++ b/Lib/importlib/_bootstrap.py
@@ -390,12 +390,13 @@
# keyword-only defaults)
# Python 3.4a1 3260 (add LOAD_CLASSDEREF; allow locals of class to override
# free vars)
+# Python 3.4a1 3270 (various tweaks to the __class_ closure)
#
# MAGIC must change whenever the bytecode emitted by the compiler may no
# longer be understood by older implementations of the eval loop (usually
# due to the addition of new opcodes).
-_MAGIC_BYTES = (3260).to_bytes(2, 'little') + b'\r\n'
+_MAGIC_BYTES = (3270).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(_MAGIC_BYTES, 'little')
_PYCACHE = '__pycache__'
diff --git a/Lib/test/test_super.py b/Lib/test/test_super.py
--- a/Lib/test/test_super.py
+++ b/Lib/test/test_super.py
@@ -81,8 +81,7 @@
self.assertEqual(E().f(), 'AE')
- @unittest.expectedFailure
- def test___class___set(self):
+ def test_various___class___pathologies(self):
# See issue #12370
class X(A):
def f(self):
@@ -91,6 +90,31 @@
x = X()
self.assertEqual(x.f(), 'A')
self.assertEqual(x.__class__, 413)
+ class X:
+ x = __class__
+ def f():
+ __class__
+ self.assertIs(X.x, type(self))
+ with self.assertRaises(NameError) as e:
+ exec("""class X:
+ __class__
+ def f():
+ __class__""", globals(), {})
+ self.assertIs(type(e.exception), NameError) # Not UnboundLocalError
+ class X:
+ global __class__
+ __class__ = 42
+ def f():
+ __class__
+ self.assertEqual(globals()["__class__"], 42)
+ del globals()["__class__"]
+ self.assertNotIn("__class__", X.__dict__)
+ class X:
+ nonlocal __class__
+ __class__ = 42
+ def f():
+ __class__
+ self.assertEqual(__class__, 42)
def test___class___instancemethod(self):
# See issue #14857
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@
Core and Builtins
-----------------
+- Issue #12370: Prevent class bodies from interfering with the __class__
+ closure.
+
- Issue #17237: Fix crash in the ASCII decoder on m68k.
- Issue #17927: Frame objects kept arguments alive if they had been
diff --git a/Python/compile.c b/Python/compile.c
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -535,6 +535,37 @@
compiler_unit_free(u);
return 0;
}
+ if (u->u_ste->ste_needs_class_closure) {
+ /* Cook up a implicit __class__ cell. */
+ _Py_IDENTIFIER(__class__);
+ PyObject *tuple, *name, *zero;
+ int res;
+ assert(u->u_scope_type == COMPILER_SCOPE_CLASS);
+ assert(PyDict_Size(u->u_cellvars) == 0);
+ name = _PyUnicode_FromId(&PyId___class__);
+ if (!name) {
+ compiler_unit_free(u);
+ return 0;
+ }
+ tuple = PyTuple_Pack(2, name, Py_TYPE(name));
+ if (!tuple) {
+ compiler_unit_free(u);
+ return 0;
+ }
+ zero = PyLong_FromLong(0);
+ if (!zero) {
+ Py_DECREF(tuple);
+ compiler_unit_free(u);
+ return 0;
+ }
+ res = PyDict_SetItem(u->u_cellvars, tuple, zero);
+ Py_DECREF(tuple);
+ Py_DECREF(zero);
+ if (res < 0) {
+ compiler_unit_free(u);
+ return 0;
+ }
+ }
u->u_freevars = dictbytype(u->u_ste->ste_symbols, FREE, DEF_FREE_CLASS,
PyDict_Size(u->u_cellvars));
@@ -1331,6 +1362,9 @@
static int
get_ref_type(struct compiler *c, PyObject *name)
{
+ if (c->u->u_scope_type == COMPILER_SCOPE_CLASS &&
+ !PyUnicode_CompareWithASCIIString(name, "__class__"))
+ return CELL;
int scope = PyST_GetScope(c->u->u_ste, name);
if (scope == 0) {
char buf[350];
@@ -1704,23 +1738,23 @@
compiler_exit_scope(c);
return 0;
}
- /* return the (empty) __class__ cell */
- str = PyUnicode_InternFromString("__class__");
- if (str == NULL) {
- compiler_exit_scope(c);
- return 0;
- }
- i = compiler_lookup_arg(c->u->u_cellvars, str);
- Py_DECREF(str);
- if (i == -1) {
- /* This happens when nobody references the cell */
- PyErr_Clear();
- /* Return None */
- ADDOP_O(c, LOAD_CONST, Py_None, consts);
+ if (c->u->u_ste->ste_needs_class_closure) {
+ /* return the (empty) __class__ cell */
+ str = PyUnicode_InternFromString("__class__");
+ if (str == NULL) {
+ compiler_exit_scope(c);
+ return 0;
+ }
+ i = compiler_lookup_arg(c->u->u_cellvars, str);
+ Py_DECREF(str);
+ assert(i == 0);
+ /* Return the cell where to store __class__ */
+ ADDOP_I(c, LOAD_CLOSURE, i);
}
else {
- /* Return the cell where to store __class__ */
- ADDOP_I(c, LOAD_CLOSURE, i);
+ assert(PyDict_Size(c->u->u_cellvars) == 0);
+ /* This happens when nobody references the cell. Return None. */
+ ADDOP_O(c, LOAD_CONST, Py_None, consts);
}
ADDOP_IN_SCOPE(c, RETURN_VALUE);
/* create the code object */
diff --git a/Python/importlib.h b/Python/importlib.h
--- a/Python/importlib.h
+++ b/Python/importlib.h
[stripped]
diff --git a/Python/symtable.c b/Python/symtable.c
--- a/Python/symtable.c
+++ b/Python/symtable.c
@@ -77,6 +77,7 @@
ste->ste_child_free = 0;
ste->ste_generator = 0;
ste->ste_returns_value = 0;
+ ste->ste_needs_class_closure = 0;
if (PyDict_SetItem(st->st_blocks, ste->ste_id, (PyObject *)ste) < 0)
goto fail;
@@ -514,13 +515,10 @@
Note that the current block's free variables are included in free.
That's safe because no name can be free and local in the same scope.
-
- The 'restricted' argument may be set to a string to restrict the analysis
- to the one variable whose name equals that string (e.g. "__class__").
*/
static int
-analyze_cells(PyObject *scopes, PyObject *free, const char *restricted)
+analyze_cells(PyObject *scopes, PyObject *free)
{
PyObject *name, *v, *v_cell;
int success = 0;
@@ -537,9 +535,6 @@
continue;
if (!PySet_Contains(free, name))
continue;
- if (restricted != NULL &&
- PyUnicode_CompareWithASCIIString(name, restricted))
- continue;
/* Replace LOCAL with CELL for this name, and remove
from free. It is safe to replace the value of name
in the dict, because it will not cause a resize.
@@ -555,6 +550,20 @@
return success;
}
+static int
+drop_class_free(PySTEntryObject *ste, PyObject *free)
+{
+ int res;
+ if (!GET_IDENTIFIER(__class__))
+ return 0;
+ res = PySet_Discard(free, __class__);
+ if (res < 0)
+ return 0;
+ if (res)
+ ste->ste_needs_class_closure = 1;
+ return 1;
+}
+
/* Check for illegal statements in unoptimized namespaces */
static int
check_unoptimized(const PySTEntryObject* ste) {
@@ -785,7 +794,6 @@
/* Special-case __class__ */
if (!GET_IDENTIFIER(__class__))
goto error;
- assert(PySet_Contains(local, __class__) == 1);
if (PySet_Add(newbound, __class__) < 0)
goto error;
}
@@ -818,11 +826,9 @@
Py_DECREF(temp);
/* Check if any local variables must be converted to cell variables */
- if (ste->ste_type == FunctionBlock && !analyze_cells(scopes, newfree,
- NULL))
+ if (ste->ste_type == FunctionBlock && !analyze_cells(scopes, newfree))
goto error;
- else if (ste->ste_type == ClassBlock && !analyze_cells(scopes, newfree,
- "__class__"))
+ else if (ste->ste_type == ClassBlock && !drop_class_free(ste, newfree))
goto error;
/* Records the results of the analysis in the symbol table entry */
if (!update_symbols(ste->ste_symbols, scopes, bound, newfree,
@@ -1179,9 +1185,7 @@
if (!symtable_enter_block(st, s->v.ClassDef.name, ClassBlock,
(void *)s, s->lineno, s->col_offset))
VISIT_QUIT(st, 0);
- if (!GET_IDENTIFIER(__class__) ||
- !symtable_add_def(st, __class__, DEF_LOCAL) ||
- !GET_IDENTIFIER(__locals__) ||
+ if (!GET_IDENTIFIER(__locals__) ||
!symtable_add_def(st, __locals__, DEF_PARAM)) {
symtable_exit_block(st, s);
VISIT_QUIT(st, 0);
--
Repository URL: http://hg.python.org/cpython
More information about the Python-checkins
mailing list