[pypy-commit] pypy py3.5: Issue #2639
arigo
pypy.commits at gmail.com
Wed Aug 23 11:31:58 EDT 2017
Author: Armin Rigo <arigo at tunes.org>
Branch: py3.5
Changeset: r92224:8fabef083b67
Date: 2017-08-23 17:31 +0200
http://bitbucket.org/pypy/pypy/changeset/8fabef083b67/
Log: Issue #2639
Assigning '__class__' between ModuleType and subclasses
diff --git a/pypy/interpreter/module.py b/pypy/interpreter/module.py
--- a/pypy/interpreter/module.py
+++ b/pypy/interpreter/module.py
@@ -10,9 +10,10 @@
class Module(W_Root):
"""A module."""
- _immutable_fields_ = ["w_dict?"]
+ _immutable_fields_ = ["w_dict?", "w_userclass?"]
_frozen = False
+ w_userclass = None
def __init__(self, space, w_name, w_dict=None):
self.space = space
@@ -148,6 +149,26 @@
self)
return space.call_function(space.w_list, w_dict)
+ # These three methods are needed to implement '__class__' assignment
+ # between a module and a subclass of module. They give every module
+ # the ability to have its '__class__' set, manually. Note that if
+ # you instantiate a subclass of ModuleType in the first place, then
+ # you get an RPython instance of a subclass of Module created in the
+ # normal way by typedef.py. That instance has got its own
+ # getclass(), getslotvalue(), etc. but provided it has no __slots__,
+ # it is compatible with ModuleType for '__class__' assignment.
+
+ def getclass(self, space):
+ if self.w_userclass is None:
+ return W_Root.getclass(self, space)
+ return self.w_userclass
+
+ def setclass(self, space, w_cls):
+ self.w_userclass = w_cls
+
+ def user_setup(self, space, w_subtype):
+ self.w_userclass = w_subtype
+
def init_extra_module_attrs(space, w_mod):
w_dict = w_mod.getdict(space)
diff --git a/pypy/interpreter/test/test_module.py b/pypy/interpreter/test/test_module.py
--- a/pypy/interpreter/test/test_module.py
+++ b/pypy/interpreter/test/test_module.py
@@ -220,3 +220,45 @@
import sys
m = type(sys).__new__(type(sys))
assert not m.__dict__
+
+ def test_class_assignment_for_module(self):
+ import sys
+ modtype = type(sys)
+ class X(modtype):
+ _foobar_ = 42
+
+ m = X("yytest_moduleyy")
+ assert type(m) is m.__class__ is X
+ assert m._foobar_ == 42
+ m.__class__ = modtype
+ assert type(m) is m.__class__ is modtype
+ assert not hasattr(m, '_foobar_')
+
+ m = modtype("xxtest_modulexx")
+ assert type(m) is m.__class__ is modtype
+ m.__class__ = X
+ assert m._foobar_ == 42
+ assert type(m) is m.__class__ is X
+
+ sys.__class__ = modtype
+ assert type(sys) is sys.__class__ is modtype
+ sys.__class__ = X
+ assert sys._foobar_ == 42
+ sys.__class__ = modtype
+
+ class XX(modtype):
+ __slots__ = ['a', 'b']
+
+ x = XX("zztest_modulezz")
+ assert x.__class__ is XX
+ raises(AttributeError, "x.a")
+ x.a = 42
+ assert x.a == 42
+ x.a = 43
+ assert x.a == 43
+ assert 'a' not in x.__dict__
+ del x.a
+ raises(AttributeError, "x.a")
+ raises(AttributeError, "del x.a")
+ raises(TypeError, "x.__class__ = X")
+ raises(TypeError, "sys.__class__ = XX")
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
@@ -141,13 +141,17 @@
def descr_set___class__(space, w_obj, w_newcls):
from pypy.objspace.std.typeobject import W_TypeObject
+ from pypy.interpreter.module import Module
+ #
if not isinstance(w_newcls, W_TypeObject):
raise oefmt(space.w_TypeError,
"__class__ must be set to a class, not '%T' "
"object", w_newcls)
- if not w_newcls.is_heaptype():
+ if not (w_newcls.is_heaptype() or
+ w_newcls is space.gettypeobject(Module.typedef)):
raise oefmt(space.w_TypeError,
- "__class__ assignment: only for heap types")
+ "__class__ assignment only supported for heap types "
+ "or ModuleType subclasses")
w_oldcls = space.type(w_obj)
assert isinstance(w_oldcls, W_TypeObject)
if (w_oldcls.get_full_instance_layout() ==
More information about the pypy-commit
mailing list