Writing a mutable object problem with __setattr__

Hi, I am trying to create a mutable object for a persistent object database, and I've ran into a problem. The semantics are: Object gets created as a 'Swizzle'. Swizzle is a stub that intercepts any attempt to access object properties. When code tries to access any of swizzle's properties, the swizzle should automatically mutate into the stored object, by changing its class and properties. With the __set/get/del/attr__ traps this seemed feasible in Python. But it turns out to be trickier than I've thought, my naive implementation is not working. What happens is: swizzle.__getattr__() gets called calls swizzle.load() swizzle.__class__ = stored_class swizzle.__dict__ = stored_data calls getattr(self, attr_name) calls swizzle.__getattr__() gets called and I enter an infinite loop and blow the stack Since swizzle's __class__ and __dict__ have changed, shouldn't getattr use the new class to get attributes? Why this is puzzling is that if I call swizzle.load() directly (bypassing __getattr__ trap), and then try referencing swizzle's fields, everything works. The object mutates successfully, and __getattr__ never gets called again. Any hints on what might be going wrong? Hopefully others have struggled with the same problem before me. Details of Python's class implementation and method dispatch would also be interesting. I've read Guido's Unifying Types essay, but I think I lack Python history required for its full understanding. I've tried many different approaches: - Swizzle inheriting from object (so that I can call super methods directly). This one would not let me assign into the __class__ - Swizzle calling different combinations of self.__setattr/setattr/setattribute Here is my current code. Thanks in advance, Aleks class Swizzle(object): """ A swizzle is a placeholder for an object that has not been loaded. It mutates into the loaded object upon access TODO: locking """ def __init__(self, oid): self.__setattr__('oid', oid) def __getattr__(self, name): print "swizzle getattr called" # oid is the only attribute that does not cause a load if (name == 'oid'): return self.__dict__[name] self.load() return getattr(self,name) def __setattr__(self, name, value): """ setattr passes class & dict because these are called when we are mutating the object """ print "swizzle setattr called" if (name == '__class__' or name == '__dict__' or name == 'oid'): self.__dict__[name] = value return self.load() return setattr(self,name, value) def __delattr__(self, name): print "swizzle delattr called" self.load() return delattr(self, name) def load(self): """ TODO LOCK""" print "Trying to load ", self.oid RepositoryManager.manager.load(self) def RepositoryManager.load(anItem) anItem.__dict__ = getProperties(anItem.oid) anItem.__class__ = getClass(anItem.oid)

I am trying to create a mutable object for a persistent object database, and I've ran into a problem. The semantics are:
Object gets created as a 'Swizzle'. Swizzle is a stub that intercepts any attempt to access object properties. When code tries to access any of swizzle's properties, the swizzle should automatically mutate into the stored object, by changing its class and properties.
This is a common pattern; Zope (really ZODB) uses this and calls the incomplete objects "ghosts". Zope doesn't change the __class__ though.
With the __set/get/del/attr__ traps this seemed feasible in Python. But it turns out to be trickier than I've thought, my naive implementation is not working.
What happens is: swizzle.__getattr__() gets called calls swizzle.load() swizzle.__class__ = stored_class swizzle.__dict__ = stored_data calls getattr(self, attr_name) calls swizzle.__getattr__() gets called and I enter an infinite loop and blow the stack
Since swizzle's __class__ and __dict__ have changed, shouldn't getattr use the new class to get attributes?
That's what I woul'd have thought too. Maybe the problem is that you're really requesting a non-existent attribute? Do you know which attribute name is being requested? That's the first information you need.
Why this is puzzling is that if I call swizzle.load() directly (bypassing __getattr__ trap), and then try referencing swizzle's fields, everything works. The object mutates successfully, and __getattr__ never gets called again.
Any hints on what might be going wrong? Hopefully others have struggled with the same problem before me.
Details of Python's class implementation and method dispatch would also be interesting. I've read Guido's Unifying Types essay, but I think I lack Python history required for its full understanding.
I've tried many different approaches: - Swizzle inheriting from object (so that I can call super methods directly). This one would not let me assign into the __class__
In Python 2.2.2 you can set __class__, as long as __class__ has a compatible instance lay-out (at the C implementation level). With new-style objects, you can use __getattribute__ instead of __getattr__ for more control (and also more opportunities to blow the stack :-).
- Swizzle calling different combinations of self.__setattr/setattr/setattribute
I don't understand -- these names don't exist.
Here is my current code.
Thanks in advance,
Aleks
class Swizzle(object): """ A swizzle is a placeholder for an object that has not been loaded. It mutates into the loaded object upon access TODO: locking """ def __init__(self, oid): self.__setattr__('oid', oid)
def __getattr__(self, name): print "swizzle getattr called" # oid is the only attribute that does not cause a load if (name == 'oid'): return self.__dict__[name] self.load() return getattr(self,name)
def __setattr__(self, name, value): """ setattr passes class & dict because these are called when we are mutating the object """ print "swizzle setattr called" if (name == '__class__' or name == '__dict__' or name == 'oid'): self.__dict__[name] = value return
Ah, here's the catch. You can't implement assignment to __dict__ or __class__ by doing "self.__dict__[name] = value". Use new-style classes and you'll be much happier: you can invoke the superclass __setattr__ to do the magic.
self.load() return setattr(self,name, value)
def __delattr__(self, name): print "swizzle delattr called" self.load() return delattr(self, name)
def load(self): """ TODO LOCK""" print "Trying to load ", self.oid RepositoryManager.manager.load(self)
def RepositoryManager.load(anItem) anItem.__dict__ = getProperties(anItem.oid) anItem.__class__ = getClass(anItem.oid)
--Guido van Rossum (home page: http://www.python.org/~guido/)

Guido van Rossum wrote:
Aleks Totic wrote:
I am trying to create a mutable object for a persistent object database, and I've ran into a problem. The semantics are:
Object gets created as a 'Swizzle'. Swizzle is a stub that intercepts any attempt to access object properties. When code tries to access any of swizzle's properties, the swizzle should automatically mutate into the stored object, by changing its class and properties.
.....................................
With the __set/get/del/attr__ traps this seemed feasible in Python. But it turns out to be trickier than I've thought, my naive implementation is not working.
What happens is: swizzle.__getattr__() gets called calls swizzle.load() swizzle.__class__ = stored_class swizzle.__dict__ = stored_data calls getattr(self, attr_name) calls swizzle.__getattr__() gets called and I enter an infinite loop and blow the stack
I've tried many different approaches: - Swizzle inheriting from object (so that I can call super methods directly). This one would not let me assign into the __class__
In Python 2.2.2 you can set __class__, as long as __class__ has a compatible instance lay-out (at the C implementation level).
This is the part where I am confused. How do I know if something is an old or new style object? Do new-style objects derive from built-in types (object/dict..)
With new-style objects, you can use __getattribute__ instead of __getattr__ for more control (and also more opportunities to blow the stack :-).
.................................
def __setattr__(self, name, value): """ setattr passes class & dict because these are called when we are mutating the object """ print "swizzle setattr called" if (name == '__class__' or name == '__dict__' or name == 'oid'): self.__dict__[name] = value return
Ah, here's the catch. You can't implement assignment to __dict__ or __class__ by doing "self.__dict__[name] = value".
Use new-style classes and you'll be much happier: you can invoke the superclass __setattr__ to do the magic.
I've tried this, but then I was unable to create any of the old-style classes. Is there any solution that would let me mutate an object into both? I suspect not. Then the question is: - is there a runtime/compiling python flag to force all objects into new-style layout? This will be the future, I think I've read somewhere. - how can I tell the difference between old and new programatically? Aleks

In Python 2.2.2 you can set __class__, as long as __class__ has a compatible instance lay-out (at the C implementation level).
This is the part where I am confused. How do I know if something is an old or new style object? Do new-style objects derive from built-in types (object/dict..)
Yes. Please re-read http://www.python.org/2.2.2/descrintro.html
Use new-style classes and you'll be much happier: you can invoke the superclass __setattr__ to do the magic.
I've tried this, but then I was unable to create any of the old-style classes. Is there any solution that would let me mutate an object into both? I suspect not. Then the question is:
No, you can never switch an object from classic to new-style.
- is there a runtime/compiling python flag to force all objects into new-style layout? This will be the future, I think I've read somewhere.
Almost. You can add __metaclass__=type to the top of each module, and then classes without a base class will become new-style. Classes derived from other classes will still be whatever that other class is; and there's no way to force the whole standard library to use new-style classes (amongst many reasons, exceptions must be classic classes).
- how can I tell the difference between old and new programatically?
type(C) == types.Classtype. Or check if the class has a __class__ attribute (classic classes don't). --Guido van Rossum (home page: http://www.python.org/~guido/)

On Tue, 2003-02-25 at 12:31, Guido van Rossum wrote:
I've tried this, but then I was unable to create any of the old-style classes. Is there any solution that would let me mutate an object into both? I suspect not. Then the question is:
No, you can never switch an object from classic to new-style.
One possibility, which isn't quite the same thing, is to derive a new-style class from the classic class. You can write this with a class statement like so: import cgi class FormContent(cgi.FormContent, object): pass or you can generate one manually: type.__new__(type, "FormContent", (cgi.FormContent, object), {}) In either case, you will have a new-style class that inherits all of its behavior from the classic class. Jeremy
participants (4)
-
Aleks Totic
-
Aleksandar Totic
-
Guido van Rossum
-
Jeremy Hylton