From 0b43bcf5287d9494e3332b391350fcd32fe93f2c Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Sun, 27 May 2012 18:17:07 +1000 Subject: Close #14857: fix regression in references to PEP 3135 implicit __class__ closure variable. Reopens issue #12370, but also updates unittest.mock to workaround that issue --- Lib/test/test_super.py | 24 ++++++++++++++++++++++++ Lib/unittest/mock.py | 9 ++++++--- Misc/NEWS | 3 +++ Objects/typeobject.c | 2 +- Python/compile.c | 2 +- Python/import.c | 3 ++- Python/symtable.c | 19 ++++++++----------- 7 files changed, 45 insertions(+), 17 deletions(-) diff --git a/Lib/test/test_super.py b/Lib/test/test_super.py index 298cae0..32eb1c0 100644 --- a/Lib/test/test_super.py +++ b/Lib/test/test_super.py @@ -81,6 +81,7 @@ class TestSuper(unittest.TestCase): self.assertEqual(E().f(), 'AE') + @unittest.expectedFailure def test___class___set(self): # See issue #12370 class X(A): @@ -91,6 +92,29 @@ class TestSuper(unittest.TestCase): self.assertEqual(x.f(), 'A') self.assertEqual(x.__class__, 413) + def test___class___instancemethod(self): + # See issue #14857 + class X: + def f(self): + return __class__ + self.assertIs(X().f(), X) + + def test___class___classmethod(self): + # See issue #14857 + class X: + @classmethod + def f(cls): + return __class__ + self.assertIs(X.f(), X) + + def test___class___staticmethod(self): + # See issue #14857 + class X: + @staticmethod + def f(): + return __class__ + self.assertIs(X.f(), X) + def test_main(): support.run_unittest(TestSuper) diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index a94acd6..36be0fd 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -39,6 +39,9 @@ if 'java' in sys.platform: FILTER_DIR = True +# Workaround for issue #12370 +# Without this, the __class__ properties wouldn't be set correctly +_safe_super = super def _is_instance_mock(obj): # can't use isinstance on Mock objects because they override __class__ @@ -397,7 +400,7 @@ class NonCallableMock(Base): if kwargs: self.configure_mock(**kwargs) - super(NonCallableMock, self).__init__( + _safe_super(NonCallableMock, self).__init__( spec, wraps, name, spec_set, parent, _spec_state ) @@ -820,7 +823,7 @@ class CallableMixin(Base): _spec_state=None, _new_name='', _new_parent=None, **kwargs): self.__dict__['_mock_return_value'] = return_value - super(CallableMixin, self).__init__( + _safe_super(CallableMixin, self).__init__( spec, wraps, name, spec_set, parent, _spec_state, _new_name, _new_parent, **kwargs ) @@ -1690,7 +1693,7 @@ def _set_return_value(mock, method, name): class MagicMixin(object): def __init__(self, *args, **kw): - super(MagicMixin, self).__init__(*args, **kw) + _safe_super(MagicMixin, self).__init__(*args, **kw) self._mock_set_magics() diff --git a/Misc/NEWS b/Misc/NEWS index 5727201..512783a 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ What's New in Python 3.3.0 Alpha 4? Core and Builtins ----------------- +- Issue #14857: fix regression in references to PEP 3135 implicit __class__ + closure variable (Reopens issue #12370) + - Issue #14712 (PEP 405): Virtual environments. Implemented by Vinay Sajip. - Issue #14660 (PEP 420): Namespace packages. Implemented by Eric Smith. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index cb4b99b..872ed99 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -6436,7 +6436,7 @@ super_init(PyObject *self, PyObject *args, PyObject *kwds) PyObject *name = PyTuple_GET_ITEM(co->co_freevars, i); assert(PyUnicode_Check(name)); if (!PyUnicode_CompareWithASCIIString(name, - "@__class__")) { + "__class__")) { Py_ssize_t index = co->co_nlocals + PyTuple_GET_SIZE(co->co_cellvars) + i; PyObject *cell = f->f_localsplus[index]; diff --git a/Python/compile.c b/Python/compile.c index 10e9ad2..2a1b754 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1676,7 +1676,7 @@ compiler_class(struct compiler *c, stmt_ty s) return 0; } /* return the (empty) __class__ cell */ - str = PyUnicode_InternFromString("@__class__"); + str = PyUnicode_InternFromString("__class__"); if (str == NULL) { compiler_exit_scope(c); return 0; diff --git a/Python/import.c b/Python/import.c index ab6ff86..98d9dc5 100644 --- a/Python/import.c +++ b/Python/import.c @@ -106,6 +106,7 @@ typedef unsigned short mode_t; Python 3.3a0 3200 (__qualname__ added) 3210 (added size modulo 2**32 to the pyc header) Python 3.3a1 3220 (changed PEP 380 implementation) + Python 3.3a4 3230 (revert changes to implicit __class__ closure) */ /* MAGIC must change whenever the bytecode emitted by the compiler may no @@ -118,7 +119,7 @@ typedef unsigned short mode_t; #define STRIFY(name) QUOTE(name) #define MAJOR STRIFY(PY_MAJOR_VERSION) #define MINOR STRIFY(PY_MINOR_VERSION) -#define MAGIC (3220 | ((long)'\r'<<16) | ((long)'\n'<<24)) +#define MAGIC (3230 | ((long)'\r'<<16) | ((long)'\n'<<24)) #define TAG "cpython-" MAJOR MINOR; #define CACHEDIR "__pycache__" /* Current magic word and string tag as globals. */ diff --git a/Python/symtable.c b/Python/symtable.c index b622835..a0b786b 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -221,17 +221,10 @@ symtable_new(void) struct symtable * PySymtable_Build(mod_ty mod, const char *filename, PyFutureFeatures *future) { - struct symtable *st; + struct symtable *st = symtable_new(); asdl_seq *seq; int i; - if (__class__ == NULL) { - __class__ = PyUnicode_InternFromString("@__class__"); - if (__class__ == NULL) - return NULL; - } - - st = symtable_new(); if (st == NULL) return st; st->st_filename = filename; @@ -747,6 +740,8 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free, } else { /* Special-case __class__ */ + if (!GET_IDENTIFIER(__class__)) + goto error; assert(PySet_Contains(local, __class__) == 1); if (PySet_Add(newbound, __class__) < 0) goto error; @@ -784,7 +779,7 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free, NULL)) goto error; else if (ste->ste_type == ClassBlock && !analyze_cells(scopes, newfree, - "@__class__")) + "__class__")) goto error; /* Records the results of the analysis in the symbol table entry */ if (!update_symbols(ste->ste_symbols, scopes, bound, newfree, @@ -1111,7 +1106,8 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s) if (!symtable_enter_block(st, s->v.ClassDef.name, ClassBlock, (void *)s, s->lineno, s->col_offset)) return 0; - if (!symtable_add_def(st, __class__, DEF_LOCAL) || + if (!GET_IDENTIFIER(__class__) || + !symtable_add_def(st, __class__, DEF_LOCAL) || !GET_IDENTIFIER(__locals__) || !symtable_add_def(st, __locals__, DEF_PARAM)) { symtable_exit_block(st, s); @@ -1376,7 +1372,8 @@ symtable_visit_expr(struct symtable *st, expr_ty e) if (e->v.Name.ctx == Load && st->st_cur->ste_type == FunctionBlock && !PyUnicode_CompareWithASCIIString(e->v.Name.id, "super")) { - if (!symtable_add_def(st, __class__, USE)) + if (!GET_IDENTIFIER(__class__) || + !symtable_add_def(st, __class__, USE)) return 0; } break; -- cgit v0.12