Author: Carl Friedrich Bolz-Tereick <[email protected]>
Branch: py3.6
Changeset: r94528:92cfcd232b4f
Date: 2018-05-12 16:26 +0200
http://bitbucket.org/pypy/pypy/changeset/92cfcd232b4f/
Log: implement the new way the __class__ closure is filled (via
type.__new__ and the new __classcell__ element in the class body
dict)
diff --git a/pypy/interpreter/astcompiler/codegen.py
b/pypy/interpreter/astcompiler/codegen.py
--- a/pypy/interpreter/astcompiler/codegen.py
+++ b/pypy/interpreter/astcompiler/codegen.py
@@ -1826,6 +1826,8 @@
if scope == symtable.SCOPE_CELL_CLASS:
# Return the cell where to store __class__
self.emit_op_arg(ops.LOAD_CLOSURE, self.cell_vars["__class__"])
+ self.emit_op(ops.DUP_TOP)
+ self.name_op("__classcell__", ast.Store)
else:
# This happens when nobody references the cell
self.load_const(self.space.w_None)
diff --git a/pypy/interpreter/test/test_compiler.py
b/pypy/interpreter/test/test_compiler.py
--- a/pypy/interpreter/test/test_compiler.py
+++ b/pypy/interpreter/test/test_compiler.py
@@ -1109,6 +1109,74 @@
assert f() == (4, 3, 2, 1), repr(f())
"""
+ def test_classcell(self):
+ """
+ test_class = None
+ class Meta(type):
+ def __new__(cls, name, bases, namespace):
+ nonlocal test_class
+ self = super().__new__(cls, name, bases, namespace)
+ test_class = self.f()
+ return self
+ class A(metaclass=Meta):
+ @staticmethod
+ def f():
+ return __class__
+ assert test_class is A
+ """
+
+ def test_classcell_missing(self):
+ """
+ # Some metaclasses may not pass the original namespace to type.__new__
+ # We test that case here by forcibly deleting __classcell__
+ class Meta(type):
+ def __new__(cls, name, bases, namespace):
+ namespace.pop('__classcell__', None)
+ return super().__new__(cls, name, bases, namespace)
+
+ class WithClassRef(metaclass=Meta):
+ def f(self):
+ return __class__
+
+ # Check __class__ still gets set despite the warning
+ assert WithClassRef().f() is WithClassRef
+ """
+
+ def test_classcell_overwrite(self):
+ """
+ # Overwriting __classcell__ with nonsense is explicitly prohibited
+ class Meta(type):
+ def __new__(cls, name, bases, namespace, cell):
+ namespace['__classcell__'] = cell
+ return super().__new__(cls, name, bases, namespace)
+
+ raises(TypeError, '''if 1:
+ class A(metaclass=Meta, cell=object()):
+ pass
+ ''')
+ """
+
+ def test_classcell_wrong_cell(self):
+ """
+ # Pointing the cell reference at the wrong class is prohibited
+ class Meta(type):
+ def __new__(cls, name, bases, namespace):
+ cls = super().__new__(cls, name, bases, namespace)
+ B = type("B", (), namespace)
+ return cls
+
+ # works, no __class__
+ class A(metaclass=Meta):
+ pass
+
+ raises(TypeError, '''if 1:
+ class A(metaclass=Meta):
+ def f(self):
+ return __class__
+ ''')
+
+ """
+
class AppTestOptimizer(object):
def setup_class(cls):
@@ -1438,3 +1506,5 @@
assert eval(code) == u'\xc2\u20ac'
code = b'u"""\\\n# -*- coding: ascii -*-\n\xc2\xa4"""\n'
assert eval(code) == u'# -*- coding: ascii -*-\n\xa4'
+
+
diff --git a/pypy/module/__builtin__/compiling.py
b/pypy/module/__builtin__/compiling.py
--- a/pypy/module/__builtin__/compiling.py
+++ b/pypy/module/__builtin__/compiling.py
@@ -93,6 +93,8 @@
frame.exec_(w_prog, w_globals, w_locals)
def build_class(space, w_func, w_name, __args__):
+ from pypy.objspace.std.typeobject import _calculate_metaclass, W_TypeObject
+ from pypy.interpreter.nestedscope import Cell
if not isinstance(w_func, Function):
raise oefmt(space.w_TypeError, "__build_class__: func must be a
function")
bases_w, kwds_w = __args__.unpack()
@@ -109,7 +111,6 @@
if isclass:
# w_meta is really a class, so check for a more derived
# metaclass, or possible metaclass conflicts
- from pypy.objspace.std.typeobject import _calculate_metaclass
w_meta = _calculate_metaclass(space, w_meta, bases_w)
try:
@@ -135,6 +136,22 @@
keywords=keywords,
keywords_w=kwds_w.values())
w_class = space.call_args(w_meta, args)
- if isinstance(w_cell, Cell):
- w_cell.set(w_class)
+ if isinstance(w_cell, Cell) and isinstance(w_class, W_TypeObject):
+ if w_cell.empty():
+ # will become an error in Python 3.7
+ space.warn(space.newtext(
+ "__class__ not set defining %s as %s ."
+ "Was __classcell__ propagated to type.__new__?" % (
+ space.text_w(w_name),
+ space.text_w(space.str(w_class))
+ )),
+ space.w_DeprecationWarning)
+ w_cell.set(w_class)
+ else:
+ w_class_from_cell = w_cell.get()
+ if not space.is_w(w_class, w_class_from_cell):
+ raise oefmt(
+ space.w_TypeError,
+ "__class__ set to %s defining %s as %s",
+ w_class_from_cell, w_name, w_class)
return w_class
diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py
--- a/pypy/objspace/std/typeobject.py
+++ b/pypy/objspace/std/typeobject.py
@@ -805,12 +805,28 @@
w_type = space.allocate_instance(W_TypeObject, w_typetype)
W_TypeObject.__init__(w_type, space, name, bases_w or [space.w_object],
dict_w, is_heaptype=True)
+
+ # store the w_type in __classcell__
+ w_classcell = dict_w.get("__classcell__")
+ if w_classcell:
+ _store_type_in_classcell(space, w_type, w_classcell, dict_w)
+
w_type.ready()
_set_names(space, w_type)
_init_subclass(space, w_type, __args__)
return w_type
+def _store_type_in_classcell(space, w_type, w_classcell, dict_w):
+ from pypy.interpreter.nestedscope import Cell
+ if isinstance(w_classcell, Cell):
+ w_classcell.set(w_type)
+ else:
+ raise oefmt(space.w_TypeError,
+ "__classcell__ must be a nonlocal cell, not %T",
+ w_classcell)
+ del dict_w['__classcell__']
+
def _calculate_metaclass(space, w_metaclass, bases_w):
"""Determine the most derived metatype"""
w_winner = w_metaclass
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit