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

Reply via email to