reindex_object in __setattr__ method not working. (was: Losing acquisition wrapping between method calls)
Hi there, I am using __setattr__ in my class to decide if the class needs to be reindexed or not. The problem is, the moment you enter the __setattr__ method, self is not acquisition wrapped anymore, which means that reindex_object does not work since it cannot acquire the Catalog. This can be illustrated with the following small test class: class AQTest(CatalogAware, SimpleItem): '''Test class''' meta_type = "Health Window Test" def __setattr__(self, attrName, attrValue): if attrName.startswith('abc'): self.reindex_object() #This doesn't work, since self is no longer acquisition wrapped here, eg. self.aq_parent raises an error self.__dict__[attrName] = attrValue def test(self): '''Test method''' self.abcname = 'John' Any suggestions on how to fix this? Etienne
On Thu, 05 Feb 2004 09:59:39 +0200 Etienne Labuschagne <elabuschagne@gmsonline.co.za> wrote:
Hi there,
I am using __setattr__ in my class to decide if the class needs to be reindexed or not. The problem is, the moment you enter the __setattr__ method, self is not acquisition wrapped anymore, which means that reindex_object does not work since it cannot acquire the Catalog.
This can be illustrated with the following small test class:
class AQTest(CatalogAware, SimpleItem): '''Test class''' meta_type = "Health Window Test"
def __setattr__(self, attrName, attrValue): if attrName.startswith('abc'): self.reindex_object() #This doesn't work, since self is no longer acquisition wrapped here, eg. self.aq_parent raises an error self.__dict__[attrName] = attrValue
def test(self): '''Test method''' self.abcname = 'John'
Any suggestions on how to fix this?
I think you have found that __setattr__ is too low-level for this to work. Many operations may do setattr without your knowing it also (like ZODB), so indexing there although attractive for automation purposes is not going to work in practice. The canonical, if less automated, way it is done is to have mutator methods change attributes and call reindex object at the end. If you want to, you could just have a single mutator method which lets you set any attributes. In fact this would be more efficent then reindexing on every attribute change. Plus you can protect it with permissions so it can be called from untrusted code:: def set(self, **kw): """Set attrbiutes and reindex""" for name, value in kw.items(): setattr(self, name, value) self.reindex_object() OTOH, this method doesn't give much guidance as to which attributes to set. In general it is considered bad practice to manipulate attributes of objects directly and informally. You could improve this without loosing generality however:: def set(self, **kw): """Set attrbiutes and reindex""" for name, value in kw.items(): getattr(self, name) # Assert the attribute exists setattr(self, name, value) self.reindex_object() Ultimately, this is probably best though:: def edit(self, foo=None, bar=None, baz=None): if foo is not None: self._foo = foo ... self.reindex_object() That makes it clear what is editable from the signature and breaks if you typo a name. hth, -Casey
At 12:34 PM 5/2/2004 -0500, Casey Duncan wrote:
On Thu, 05 Feb 2004 09:59:39 +0200 Etienne Labuschagne <elabuschagne@gmsonline.co.za> wrote:
Hi there,
I am using __setattr__ in my class to decide if the class needs to be reindexed or not. The problem is, the moment you enter the __setattr__ method, self is not acquisition wrapped anymore, which means that reindex_object does not work since it cannot acquire the Catalog.
This can be illustrated with the following small test class:
class AQTest(CatalogAware, SimpleItem): '''Test class''' meta_type = "Health Window Test"
def __setattr__(self, attrName, attrValue): if attrName.startswith('abc'): self.reindex_object() #This doesn't work, since self is no longer acquisition wrapped here, eg. self.aq_parent raises an error self.__dict__[attrName] = attrValue
def test(self): '''Test method''' self.abcname = 'John'
Any suggestions on how to fix this?
I think you have found that __setattr__ is too low-level for this to work. Many operations may do setattr without your knowing it also (like ZODB), so indexing there although attractive for automation purposes is not going to work in practice.
The canonical, if less automated, way it is done is to have mutator methods change attributes and call reindex object at the end. If you want to, you could just have a single mutator method which lets you set any attributes. In fact this would be more efficent then reindexing on every attribute change. Plus you can protect it with permissions so it can be called from untrusted code::
def set(self, **kw): """Set attrbiutes and reindex""" for name, value in kw.items(): setattr(self, name, value) self.reindex_object()
OTOH, this method doesn't give much guidance as to which attributes to set. In general it is considered bad practice to manipulate attributes of objects directly and informally. You could improve this without loosing generality however::
def set(self, **kw): """Set attrbiutes and reindex""" for name, value in kw.items(): getattr(self, name) # Assert the attribute exists setattr(self, name, value) self.reindex_object()
Ultimately, this is probably best though::
def edit(self, foo=None, bar=None, baz=None): if foo is not None: self._foo = foo ... self.reindex_object()
That makes it clear what is editable from the signature and breaks if you typo a name.
hth,
-Casey
Thanks for clarifying the __setattr__ behaviour (also pointed out in another thread to me by Dieter Maurer), Usually I always make use of getters and setters (which would usually be the mutators) and never make use of direct attribute access (the example above was very brief and gave the impression of direct attribute access). After being spoilt by new-style properties, I tried to implement properties using getters and setters as new style classes is not yet an option in ZODB 3.2. My scheme worked perfectly and gives on the syntaxical ease of use that direct attribute access gives one, but with the correctness of getters and setters. Unfortunately I ran into this one small issue that is seemingly unfixable. This now leaves me with two choices - drop the "properties" or drop the automatic indexing. Due to the nature of my current product's architecture, the latter will be dropped and I will trigger reindexing "manually". I am currently trying to get ZODB 3.3a to work with Zope 2.7 so that I have access to new-style classes. This will give me the best of both worlds and I'll have to drop nothing. Thanks Etienne
participants (2)
-
Casey Duncan -
Etienne Labuschagne