[pypy-svn] r76837 - in pypy/branch/no-_immutable_/pypy/rpython: . ootypesystem test

arigo at codespeak.net arigo at codespeak.net
Thu Sep 2 16:11:24 CEST 2010


Author: arigo
Date: Thu Sep  2 16:11:22 2010
New Revision: 76837

Modified:
   pypy/branch/no-_immutable_/pypy/rpython/ootypesystem/ootype.py
   pypy/branch/no-_immutable_/pypy/rpython/ootypesystem/rclass.py
   pypy/branch/no-_immutable_/pypy/rpython/rclass.py
   pypy/branch/no-_immutable_/pypy/rpython/test/test_rclass.py
Log:
Write tests about various combinations of _immutable_,
_immutable_fields_, and subclassing.  Implement that.


Modified: pypy/branch/no-_immutable_/pypy/rpython/ootypesystem/ootype.py
==============================================================================
--- pypy/branch/no-_immutable_/pypy/rpython/ootypesystem/ootype.py	(original)
+++ pypy/branch/no-_immutable_/pypy/rpython/ootypesystem/ootype.py	Thu Sep  2 16:11:22 2010
@@ -267,6 +267,14 @@
             return self._fields_with_default[:]
         return self._superclass._get_fields_with_default() + self._fields_with_default
 
+    def _immutable_field(self, field):
+        if 'immutable_fields' in self._hints:
+            try:
+                s = self._hints['immutable_fields'].fields[field]
+                return s or True
+            except KeyError:
+                pass
+        return self._hints.get('immutable', False)
 
 
 class SpecializableType(OOType):

Modified: pypy/branch/no-_immutable_/pypy/rpython/ootypesystem/rclass.py
==============================================================================
--- pypy/branch/no-_immutable_/pypy/rpython/ootypesystem/rclass.py	(original)
+++ pypy/branch/no-_immutable_/pypy/rpython/ootypesystem/rclass.py	Thu Sep  2 16:11:22 2010
@@ -194,6 +194,7 @@
             self.lowleveltype._hints.update(hints)
 
         if self.classdef is None:
+            self.fields = {}
             self.allfields = {}
             self.allmethods = {}
             self.allclassattributes = {}
@@ -210,6 +211,7 @@
             allclassattributes = {}
 
         fields = {}
+        nonmangledfields = []
         fielddefaults = {}
 
         if llfields:
@@ -224,6 +226,7 @@
                 allfields[mangled] = repr
                 oot = repr.lowleveltype
                 fields[mangled] = oot
+                nonmangledfields.append(name)
                 try:
                     value = self.classdef.classdesc.read_attribute(name)
                     fielddefaults[mangled] = repr.convert_desc_or_const(value)
@@ -294,6 +297,7 @@
                     if not attrdef.s_value.is_constant():
                         classattributes[mangled] = attrdef.s_value, value
 
+        self.fields = nonmangledfields
         self.allfields = allfields
         self.allmethods = allmethods
         self.allclassattributes = allclassattributes

Modified: pypy/branch/no-_immutable_/pypy/rpython/rclass.py
==============================================================================
--- pypy/branch/no-_immutable_/pypy/rpython/rclass.py	(original)
+++ pypy/branch/no-_immutable_/pypy/rpython/rclass.py	Thu Sep  2 16:11:22 2010
@@ -19,8 +19,8 @@
         return True
 
 class ImmutableConflictError(Exception):
-    """Raised when an attribute 'x' is found on some parent class,
-    but defined in a subclass to be in _immutable_fields_."""
+    """Raised when the _immutable_ or _immutable_fields_ hints are
+    not consistent across a class hierarchy."""
 
 
 def getclassrepr(rtyper, classdef):
@@ -157,10 +157,9 @@
         pass
 
     def _check_for_immutable_hints(self, hints):
-        if '_immutable_' in self.classdef.classdesc.classdict:
-            raise TyperError(
-                "%r: the _immutable_ hint is not supported any more.\n"
-                "Use _immutable_fields_ instead." % (self,))
+        if self.classdef.classdesc.lookup('_immutable_') is not None:
+            hints = hints.copy()
+            hints['immutable'] = True
         self.immutable_field_list = []  # unless overwritten below
         if self.classdef.classdesc.lookup('_immutable_fields_') is not None:
             hints = hints.copy()
@@ -187,39 +186,29 @@
         return 'InstanceR %s' % (clsname,)
 
     def _setup_repr_final(self):
+        self._setup_immutable_field_list()
+        self._check_for_immutable_conflicts()
+
+    def _setup_immutable_field_list(self):
         hints = self.object_type._hints
         if "immutable_fields" in hints:
             accessor = hints["immutable_fields"]
-            immutable_fields = {}
-            rbase = self
-            while rbase.classdef is not None:
-                immutable_fields.update(
-                    dict.fromkeys(rbase.immutable_field_list, rbase))
-                rbase = rbase.rbase
-            self._parse_field_list(immutable_fields, accessor)
+            if not hasattr(accessor, 'fields'):
+                immutable_fields = []
+                rbase = self
+                while rbase.classdef is not None:
+                    immutable_fields += rbase.immutable_field_list
+                    rbase = rbase.rbase
+                self._parse_field_list(immutable_fields, accessor)
 
     def _parse_field_list(self, fields, accessor):
         with_suffix = {}
-        for name, rbase in fields.items():
+        for name in fields:
             if name.endswith('[*]'):
                 name = name[:-3]
                 suffix = '[*]'
             else:
                 suffix = ''
-            #
-            # check that the field is not higher up the class hierarchy
-            # than where it was originally defined in _immutable_fields_
-            if rbase.rbase.classdef is not None:
-                try:
-                    rbase.rbase._get_field(name)
-                except KeyError:
-                    pass
-                else:
-                    raise ImmutableConflictError(
-                        "the field %r is declared in _immutable_fields_ in "
-                        "class %r, but actually ends up in parent class"
-                        % (name, rbase))
-            #
             try:
                 mangled_name, r = self._get_field(name)
             except KeyError:
@@ -228,6 +217,36 @@
         accessor.initialize(self.object_type, with_suffix)
         return with_suffix
 
+    def _check_for_immutable_conflicts(self):
+        # check for conflicts, i.e. a field that is defined normally as
+        # mutable in some parent class but that is now declared immutable
+        from pypy.rpython.lltypesystem.lltype import Void
+        is_self_immutable = "immutable" in self.object_type._hints
+        base = self
+        while base.classdef is not None:
+            base = base.rbase
+            for fieldname in base.fields:
+                try:
+                    mangled, r = base._get_field(fieldname)
+                except KeyError:
+                    continue
+                if r.lowleveltype == Void:
+                    continue
+                base._setup_immutable_field_list()
+                if base.object_type._immutable_field(mangled):
+                    continue
+                # 'fieldname' is a mutable, non-Void field in the parent
+                if is_self_immutable:
+                    raise ImmutableConflictError(
+                        "class %r has _immutable_=True, but parent class %r "
+                        "defines (at least) the mutable field %r" % (
+                        self, base, fieldname))
+                if fieldname in self.immutable_field_list:
+                    raise ImmutableConflictError(
+                        "field %r is defined mutable in class %r, but "
+                        "listed in _immutable_fields_ in subclass %r" % (
+                        fieldname, base, self))
+
     def new_instance(self, llops, classcallhop=None):
         raise NotImplementedError
 

Modified: pypy/branch/no-_immutable_/pypy/rpython/test/test_rclass.py
==============================================================================
--- pypy/branch/no-_immutable_/pypy/rpython/test/test_rclass.py	(original)
+++ pypy/branch/no-_immutable_/pypy/rpython/test/test_rclass.py	Thu Sep  2 16:11:22 2010
@@ -709,7 +709,7 @@
 
     def test_immutable(self):
         class I(object):
-            _immutable_fields_ = ["v"]
+            _immutable_ = True
             
             def __init__(self, v):
                 self.v = v
@@ -796,7 +796,7 @@
         assert accessor.fields == {"inst_y" : ""} or \
                accessor.fields == {"oy" : ""} # for ootype
 
-    def test_immutable_forbidden_inheritance(self):
+    def test_immutable_forbidden_inheritance_1(self):
         from pypy.rpython.rclass import ImmutableConflictError
         class A(object):
             pass
@@ -805,9 +805,84 @@
         def f():
             A().v = 123
             B()             # crash: class B says 'v' is immutable,
-                            # but it is defined on parent class I
+                            # but it is defined on parent class A
         py.test.raises(ImmutableConflictError, self.gengraph, f, [])
 
+    def test_immutable_forbidden_inheritance_2(self):
+        from pypy.rpython.rclass import ImmutableConflictError
+        class A(object):
+            pass
+        class B(A):
+            _immutable_ = True
+        def f():
+            A().v = 123
+            B()             # crash: class B has _immutable_ = True
+                            # but class A defines 'v' to be mutable
+        py.test.raises(ImmutableConflictError, self.gengraph, f, [])
+
+    def test_immutable_ok_inheritance_2(self):
+        from pypy.jit.metainterp.typesystem import deref
+        class A(object):
+            _immutable_fields_ = ['v']
+        class B(A):
+            _immutable_ = True
+        def f():
+            A().v = 123
+            B().w = 456
+            return B()
+        t, typer, graph = self.gengraph(f, [])
+        B_TYPE = deref(graph.getreturnvar().concretetype)
+        assert B_TYPE._hints["immutable"]
+        try:
+            A_TYPE = B_TYPE.super
+        except AttributeError:
+            A_TYPE = B_TYPE._superclass  # for ootype
+        accessor = A_TYPE._hints["immutable_fields"]
+        assert accessor.fields == {"inst_v" : ""} or \
+               accessor.fields == {"ov" : ""} # for ootype
+
+    def test_immutable_subclass_1(self):
+        from pypy.jit.metainterp.typesystem import deref
+        class A(object):
+            _immutable_ = True
+        class B(A):
+            pass
+        def f():
+            B().v = 123
+            return B()
+        t, typer, graph = self.gengraph(f, [])
+        B_TYPE = deref(graph.getreturnvar().concretetype)
+        assert B_TYPE._hints["immutable"]    # inherited from A
+
+    def test_immutable_subclass_2(self):
+        from pypy.jit.metainterp.typesystem import deref
+        class A(object):
+            pass
+        class B(A):
+            _immutable_ = True
+        def f():
+            B().v = 123
+            return B()
+        t, typer, graph = self.gengraph(f, [])
+        B_TYPE = deref(graph.getreturnvar().concretetype)
+        assert B_TYPE._hints["immutable"]
+
+    def test_immutable_subclass_void(self):
+        from pypy.jit.metainterp.typesystem import deref
+        class A(object):
+            pass
+        class B(A):
+            _immutable_ = True
+        def myfunc():
+            pass
+        def f():
+            A().f = myfunc    # it's ok to add Void attributes to A
+            B().v = 123       # even though only B is declared _immutable_
+            return B()
+        t, typer, graph = self.gengraph(f, [])
+        B_TYPE = deref(graph.getreturnvar().concretetype)
+        assert B_TYPE._hints["immutable"]
+
 
 class TestLLtype(BaseTestRclass, LLRtypeMixin):
 



More information about the Pypy-commit mailing list