[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