Author: Carl Friedrich Bolz-Tereick <[email protected]>
Branch: py3.6
Changeset: r94462:9e10c6bdc41e
Date: 2018-05-03 10:02 +0200
http://bitbucket.org/pypy/pypy/changeset/9e10c6bdc41e/
Log: introduces the __init_subclass__ hook (PEP 487)
diff --git a/pypy/objspace/std/objectobject.py
b/pypy/objspace/std/objectobject.py
--- a/pypy/objspace/std/objectobject.py
+++ b/pypy/objspace/std/objectobject.py
@@ -122,6 +122,8 @@
def descr___subclasshook__(space, __args__):
return space.w_NotImplemented
+def descr___init_subclass__(space, w_cls):
+ return space.w_None
def descr__init__(space, w_obj, __args__):
if _excess_args(__args__):
@@ -284,12 +286,14 @@
__doc__ = "The most base type",
__new__ = interp2app(descr__new__),
__subclasshook__ = interp2app(descr___subclasshook__, as_classmethod=True),
+ __init_subclass__ = interp2app(descr___init_subclass__,
as_classmethod=True),
# these are actually implemented in pypy.objspace.descroperation
__getattribute__ = interp2app(Object.descr__getattribute__.im_func),
__setattr__ = interp2app(Object.descr__setattr__.im_func),
__delattr__ = interp2app(Object.descr__delattr__.im_func),
+
__init__ = interp2app(descr__init__),
__class__ = GetSetProperty(descr_get___class__, descr_set___class__),
__repr__ = interp2app(descr__repr__),
diff --git a/pypy/objspace/std/test/test_obj.py
b/pypy/objspace/std/test/test_obj.py
--- a/pypy/objspace/std/test/test_obj.py
+++ b/pypy/objspace/std/test/test_obj.py
@@ -359,6 +359,11 @@
assert o.__ge__(o2) is NotImplemented
assert o.__gt__(o2) is NotImplemented
+ def test_init_subclass(self):
+ object().__init_subclass__() # does not crash
+ object.__init_subclass__() # does not crash
+ raises(TypeError, object.__init_subclass__, 1)
+
def test_isinstance_shortcut():
from pypy.objspace.std import objspace
space = objspace.StdObjSpace()
diff --git a/pypy/objspace/std/test/test_typeobject.py
b/pypy/objspace/std/test/test_typeobject.py
--- a/pypy/objspace/std/test/test_typeobject.py
+++ b/pypy/objspace/std/test/test_typeobject.py
@@ -1556,3 +1556,43 @@
assert X.a.owner is X
assert X.a.name == "a"
+ def test_type_init_accepts_kwargs(self):
+ type.__init__(type, "a", (object, ), {}, a=1)
+
+ def test_init_subclass_classmethod(self):
+ assert isinstance(object.__dict__['__init_subclass__'], classmethod)
+ class A(object):
+ subclasses = []
+
+ def __init_subclass__(cls):
+ cls.subclass.append(cls)
+ assert isinstance(A.__dict__['__init_subclass__'], classmethod)
+
+ def test_init_subclass(self):
+ class PluginBase(object):
+ subclasses = []
+
+ def __init_subclass__(cls):
+ cls.subclasses.append(cls)
+
+ class B(PluginBase):
+ pass
+
+ class C(PluginBase):
+ pass
+
+ assert PluginBase.subclasses == [B, C]
+
+
+ class X(object):
+ subclasses = []
+
+ def __init_subclass__(cls, **kwargs):
+ cls.kwargs = kwargs
+
+ exec("""if 1:
+ class Y(X, a=1, b=2):
+ pass
+
+ assert Y.kwargs == dict(a=1, b=2)
+ """)
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
@@ -749,20 +749,26 @@
return space.newbool(not space.is_w(self, w_other))
-def descr__new__(space, w_typetype, w_name, w_bases=None, w_dict=None):
+def descr__new__(space, w_typetype, __args__):
"""This is used to create user-defined classes only."""
+ if len(__args__.arguments_w) not in (1, 3):
+ raise oefmt(space.w_TypeError,
+ "type.__new__() takes 1 or 3 arguments")
+
+ w_name = __args__.arguments_w[0]
+
w_typetype = _precheck_for_new(space, w_typetype)
# special case for type(x)
if (space.is_w(space.type(w_typetype), space.w_type) and
- w_bases is None and w_dict is None):
+ len(__args__.arguments_w) == 1):
return space.type(w_name)
- return _create_new_type(space, w_typetype, w_name, w_bases, w_dict)
+ w_bases = __args__.arguments_w[1]
+ w_dict = __args__.arguments_w[2]
+ return _create_new_type(space, w_typetype, w_name, w_bases, w_dict,
__args__)
def _check_new_args(space, w_name, w_bases, w_dict):
- if w_bases is None or w_dict is None:
- raise oefmt(space.w_TypeError, "type() takes 1 or 3 arguments")
if not space.isinstance_w(w_name, space.w_text):
raise oefmt(space.w_TypeError,
"type() argument 1 must be string, not %T", w_name)
@@ -774,7 +780,7 @@
"type() argument 3 must be dict, not %T", w_dict)
-def _create_new_type(space, w_typetype, w_name, w_bases, w_dict):
+def _create_new_type(space, w_typetype, w_name, w_bases, w_dict, __args__):
# this is in its own function because we want the special case 'type(x)'
# above to be seen by the jit.
_check_new_args(space, w_name, w_bases, w_dict)
@@ -802,6 +808,7 @@
w_type.ready()
_set_names(space, w_type)
+ _init_subclass(space, w_type, __args__)
return w_type
def _calculate_metaclass(space, w_metaclass, bases_w):
@@ -831,11 +838,15 @@
# XXX what happens when the call raises, gets turned into a
RuntimeError?
space.get_and_call_function(w_meth, w_value, w_type,
space.newtext(key))
+def _init_subclass(space, w_type, __args__):
+ # bit of a mess, but I didn't feel like implementing the super logic
+ w_super = space.getattr(space.builtin, space.newtext("super"))
+ w_func = space.getattr(space.call_function(w_super, w_type, w_type),
+ space.newtext("__init_subclass__"))
+ args = __args__.replace_arguments([])
+ space.call_args(w_func, args)
def descr__init__(space, w_type, __args__):
- if __args__.keywords:
- raise oefmt(space.w_TypeError,
- "type.__init__() takes no keyword arguments")
if len(__args__.arguments_w) not in (1, 3):
raise oefmt(space.w_TypeError,
"type.__init__() takes 1 or 3 arguments")
@@ -1311,6 +1322,7 @@
w_self.mro_w = [] # temporarily
w_self.hasmro = False
compute_mro(w_self)
+ ensure_classmethod_init_subclass(w_self)
def ensure_static_new(w_self):
# special-case __new__, as in CPython:
@@ -1320,6 +1332,12 @@
if isinstance(w_new, Function):
w_self.dict_w['__new__'] = StaticMethod(w_new)
+def ensure_classmethod_init_subclass(w_self):
+ if '__init_subclass__' in w_self.dict_w:
+ w_init_subclass = w_self.dict_w['__init_subclass__']
+ if isinstance(w_init_subclass, Function):
+ w_self.dict_w['__init_subclass__'] = ClassMethod(w_init_subclass)
+
def ensure_module_attr(w_self):
# initialize __module__ in the dict (user-defined types only)
if '__module__' not in w_self.dict_w:
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit