> 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/)
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4