[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