[pypy-svn] r32088 - in pypy/dist/pypy: annotation annotation/test interpreter objspace/std rpython rpython/lltypesystem/test rpython/test translator/c/test

arigo at codespeak.net arigo at codespeak.net
Fri Sep 8 19:52:04 CEST 2006


Author: arigo
Date: Fri Sep  8 19:51:59 2006
New Revision: 32088

Modified:
   pypy/dist/pypy/annotation/classdef.py
   pypy/dist/pypy/annotation/description.py
   pypy/dist/pypy/annotation/test/test_annrpython.py
   pypy/dist/pypy/annotation/unaryop.py
   pypy/dist/pypy/interpreter/baseobjspace.py
   pypy/dist/pypy/objspace/std/model.py
   pypy/dist/pypy/rpython/lltypesystem/test/test_rtagged.py
   pypy/dist/pypy/rpython/objectmodel.py
   pypy/dist/pypy/rpython/test/test_objectmodel.py
   pypy/dist/pypy/translator/c/test/test_rtagged.py
Log:
Add support for optional __slots__ in the annotator.  If an attribute
appears on a class, but is not in the __slots__ of that class, then
there is probably a problem: the annotator moved the attribute up to a
common base class when you were not expecting it.

(Just to make sure this is clear, our usage of __slots__ is not about
what attributes *should* appear, and certainly not about giving type
hints.  The annotator doesn't mind if a program doesn't seem to be using
all the attributes specified in the __slots__.  Such attributes won't
appear in compiled programs anyway.  And you couldn't tell the
difference anyway :-)

(And don't go adding __slots__ everywhere please!  It's for abstract
base classes kind of usage.)



Modified: pypy/dist/pypy/annotation/classdef.py
==============================================================================
--- pypy/dist/pypy/annotation/classdef.py	(original)
+++ pypy/dist/pypy/annotation/classdef.py	Fri Sep  8 19:51:59 2006
@@ -72,13 +72,14 @@
         self.bookkeeper = bookkeeper
         self.s_value = s_ImpossibleValue
         self.readonly = True
+        self.slot_allowed = True
         self.read_locations = {}
 
     def add_constant_source(self, classdef, source):
         s_value = source.s_get_value(classdef, self.name)
         if source.instance_level:
             # a prebuilt instance source forces readonly=False, see above
-            self.readonly = False
+            self.modified(classdef)
         s_new_value = unionof(self.s_value, s_value)       
         if isdegenerated(s_new_value):            
             self.bookkeeper.ondegenerated("source %r attr %s" % (source, self.name),
@@ -90,18 +91,16 @@
         # Same as 'self.s_value' for historical reasons.
         return self.s_value
 
-    def merge(self, other, classdef=None):
+    def merge(self, other, classdef='?'):
         assert self.name == other.name
         s_new_value = unionof(self.s_value, other.s_value)
         if isdegenerated(s_new_value):
-            if classdef is None:
-                what = "? attr %s" % self.name
-            else:
-                what = "%r attr %s" % (classdef, self.name)
+            what = "%s attr %s" % (classdef, self.name)
             self.bookkeeper.ondegenerated(what, s_new_value)
 
-        self.s_value = s_new_value        
-        self.readonly = self.readonly and other.readonly
+        self.s_value = s_new_value
+        if not other.readonly:
+            self.modified(classdef)
         self.read_locations.update(other.read_locations)
 
     def mutated(self, homedef): # reflow from attr read positions
@@ -119,6 +118,17 @@
                         self.bookkeeper.warning("demoting method %s to base class %s" % 
                                                 (self.name, homedef))
 
+        # check for attributes forbidden by slots
+        if homedef.classdesc.allslots is not None:
+            if self.name not in homedef.classdesc.allslots:
+                self.slot_allowed = False
+                if not self.readonly:
+                    raise NoSuchSlotError(homedef, self.name)
+
+    def modified(self, classdef='?'):
+        self.readonly = False
+        if not self.slot_allowed:
+            raise NoSuchSlotError(classdef, self.name)
 
 
 class ClassDef:
@@ -401,6 +411,9 @@
             self.obj.__dict__[name])
         return s_value
 
+class NoSuchSlotError(Exception):
+    "Raised when an attribute is found on a class where __slots__ forbits it."
+
 # ____________________________________________________________
 
 FORCE_ATTRIBUTES_INTO_CLASSES = {

Modified: pypy/dist/pypy/annotation/description.py
==============================================================================
--- pypy/dist/pypy/annotation/description.py	(original)
+++ pypy/dist/pypy/annotation/description.py	Fri Sep  8 19:51:59 2006
@@ -344,6 +344,7 @@
 class ClassDesc(Desc):
     knowntype = type
     instance_level = False
+    allslots = None   # or a set
 
     def __init__(self, bookkeeper, pyobj=None,
                  name=None, basedesc=None, classdict=None,
@@ -393,6 +394,18 @@
             if base is not object:
                 self.basedesc = bookkeeper.getdesc(base)
 
+            if '__slots__' in cls.__dict__:
+                slots = cls.__dict__['__slots__']
+                if isinstance(slots, str):
+                    slots = (slots,)
+                slots = dict.fromkeys(slots)
+                if self.basedesc is not None:
+                    if self.basedesc.allslots is None:
+                        raise Exception("%r has slots, but not its base class"
+                                        % (pyobj,))
+                    slots.update(self.basedesc.allslots)
+                self.allslots = slots
+
     def add_source_attribute(self, name, value, mixin=False):
         if isinstance(value, types.FunctionType):
             # for debugging
@@ -451,7 +464,7 @@
                 if cls in FORCE_ATTRIBUTES_INTO_CLASSES:
                     for name, s_value in FORCE_ATTRIBUTES_INTO_CLASSES[cls].items():
                         classdef.generalize_attr(name, s_value)
-                        classdef.find_attribute(name).readonly = False
+                        classdef.find_attribute(name).modified(classdef)
 
             # register all class attributes as coming from this ClassDesc
             # (as opposed to prebuilt instances)

Modified: pypy/dist/pypy/annotation/test/test_annrpython.py
==============================================================================
--- pypy/dist/pypy/annotation/test/test_annrpython.py	(original)
+++ pypy/dist/pypy/annotation/test/test_annrpython.py	Fri Sep  8 19:51:59 2006
@@ -2118,9 +2118,9 @@
 
     def test_unboxed_value(self):
         class A(object):
-            pass
+            __slots__ = ()
         class C(A, objectmodel.UnboxedValue):
-            __slots__ = 'smallint'
+            __slots__ = unboxedattrname = 'smallint'
         def f(n):
             return C(n).smallint
 
@@ -2334,6 +2334,33 @@
         assert not s.nonneg
         py.test.raises(Exception, a.build_types, fun, [s_nonneg, int])
 
+    def test_slots_check(self):
+        class Base(object):
+            __slots__ = 'x'
+        class A(Base):
+            __slots__ = 'y'
+            def m(self):
+                return 65
+        class C(Base):
+            __slots__ = 'z'
+            def m(self):
+                return 67
+        for attrname, works in [('x', True),
+                                ('y', False),
+                                ('z', False),
+                                ('t', False)]:
+            def fun(n):
+                if n: o = A()
+                else: o = C()
+                setattr(o, attrname, 12)
+                return o.m()
+            a = self.RPythonAnnotator()
+            if works:
+                a.build_types(fun, [int])
+            else:
+                from pypy.annotation.classdef import NoSuchSlotError
+                py.test.raises(NoSuchSlotError, a.build_types, fun, [int])
+
 def g(n):
     return [0,1,2,n]
 

Modified: pypy/dist/pypy/annotation/unaryop.py
==============================================================================
--- pypy/dist/pypy/annotation/unaryop.py	(original)
+++ pypy/dist/pypy/annotation/unaryop.py	Fri Sep  8 19:51:59 2006
@@ -523,7 +523,7 @@
             # find the (possibly parent) class where this attr is defined
             clsdef = ins.classdef.locate_attribute(attr)
             attrdef = clsdef.attrs[attr]
-            attrdef.readonly = False
+            attrdef.modified(clsdef)
 
             # if the attrdef is new, this must fail
             if attrdef.getvalue().contains(s_value):

Modified: pypy/dist/pypy/interpreter/baseobjspace.py
==============================================================================
--- pypy/dist/pypy/interpreter/baseobjspace.py	(original)
+++ pypy/dist/pypy/interpreter/baseobjspace.py	Fri Sep  8 19:51:59 2006
@@ -13,6 +13,8 @@
 class W_Root(object):
     """This is the abstract root class of all wrapped objects that live
     in a 'normal' object space like StdObjSpace."""
+    __slots__ = ()
+
     def getdict(self):
         return None
 

Modified: pypy/dist/pypy/objspace/std/model.py
==============================================================================
--- pypy/dist/pypy/objspace/std/model.py	(original)
+++ pypy/dist/pypy/objspace/std/model.py	Fri Sep  8 19:51:59 2006
@@ -212,6 +212,7 @@
     "Parent base class for wrapped objects provided by the StdObjSpace."
     # Note that not all wrapped objects in the interpreter inherit from
     # W_Object.  (They inherit from W_Root.)
+    __slots__ = ()
 
     def __repr__(self):
         s = '%s(%s)' % (

Modified: pypy/dist/pypy/rpython/lltypesystem/test/test_rtagged.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/test/test_rtagged.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/test/test_rtagged.py	Fri Sep  8 19:51:59 2006
@@ -9,6 +9,7 @@
 
 
 class A(object):
+    __slots__ = ()
     def meth(self, x):
         raise NotImplementedError
 

Modified: pypy/dist/pypy/rpython/objectmodel.py
==============================================================================
--- pypy/dist/pypy/rpython/objectmodel.py	(original)
+++ pypy/dist/pypy/rpython/objectmodel.py	Fri Sep  8 19:51:59 2006
@@ -211,10 +211,10 @@
         # this funtion is annotated but not included in the translated program
         int_as_pointer = value * 2 + 1   # XXX for now
         if -sys.maxint-1 <= int_as_pointer <= sys.maxint:
-            if isinstance(self.__slots__, str):
-                setattr(self, self.__slots__, value)
+            if isinstance(self.__class__.__slots__, str):
+                setattr(self, self.__class__.__slots__, value)
             else:
-                setattr(self, self.__slots__[0], value)
+                setattr(self, self.__class__.__slots__[0], value)
         else:
             raise OverflowError("UnboxedValue: argument out of range")
 
@@ -222,10 +222,10 @@
         return '<unboxed %d>' % (self.getvalue(),)
 
     def getvalue(self):   # helper, equivalent to reading the custom field
-        if isinstance(self.__slots__, str):
-            return getattr(self, self.__slots__)
+        if isinstance(self.__class__.__slots__, str):
+            return getattr(self, self.__class__.__slots__)
         else:
-            return getattr(self, self.__slots__[0])
+            return getattr(self, self.__class__.__slots__[0])
 
 # ____________________________________________________________
 

Modified: pypy/dist/pypy/rpython/test/test_objectmodel.py
==============================================================================
--- pypy/dist/pypy/rpython/test/test_objectmodel.py	(original)
+++ pypy/dist/pypy/rpython/test/test_objectmodel.py	Fri Sep  8 19:51:59 2006
@@ -118,7 +118,7 @@
 
 def test_unboxed_value():
     class Base(object):
-        pass
+        __slots__ = ()
     class C(Base, UnboxedValue):
         __slots__ = 'smallint'
 

Modified: pypy/dist/pypy/translator/c/test/test_rtagged.py
==============================================================================
--- pypy/dist/pypy/translator/c/test/test_rtagged.py	(original)
+++ pypy/dist/pypy/translator/c/test/test_rtagged.py	Fri Sep  8 19:51:59 2006
@@ -3,6 +3,7 @@
 
 
 class A(object):
+    __slots__ = ()
     def meth(self, x):
         raise NotImplementedError
 



More information about the Pypy-commit mailing list