[Zope] Acquisition question

Brian Lloyd Brian@digicool.com
Thu, 11 Feb 1999 22:32:14 -0500


> Hi,
> 
> Can anybody explain why my dynamically created attributes 
> can't find their acquired attributes up the hierarchy?
> 
> I have the following code:
>
> class AcquTest(Acquisition.Implicit):
>     "the implicitluy aquired one"
>     __roles__ = None
>     def __init__(self,color=None):
>         if color:
>             self.color = color
>     def __getitem__(self,attr):
>         if self.__class__.__dict__.has_key(attr):
>             return self.__class__.__dict__[attr]
>         if self.__dict__.has_key(attr):
>             return self.__dict__[attr]
>         try: 
>             return self.aq_acquire(attr)
>         except: 
>             self.__dict__[attr] = AcquTest(attr)
>             return self.__dict__[attr]
>     local_html = HTML("<html>Local <!--#var color--></html>")
>     index_html = HTML("""
>        <!--#var page_header-->
>        <!--#var color--><br>
>        <!--#var page_footer-->""")
> 
> aq = AcquTest('orange')
> aq.page_header = HTML('<html><font color="<!--#var color-->"
> size=+3><b>')
> aq.page_footer = '</font></html>'
> aq.name = 'AQ root object'
> 
> aq.a = AcquTest()
> aq.a.b = AcquTest('blue')
> aq.a.color= 'green'
> ---8<------8<------8<------8<------8<------8<------8<---
> 
> and when I access it through web as /aq , /aq/a or /aq/a/b
> it works as expected (prints orange, green and blue in 
> respective coluor)
> 
> but when i want to access a new attribute (retrieved from 
> a database by attr in my real application), then it can't 
> acquire the other attributes up the hierarchy.
> 
> ie /aq/black does _not_ print black, instead it produces a traceback:
> ...
> KeyError: page_header
> 
> So obviously acquisition is not working for me here ;(

This is because you have effectively defeated acquistion inside
your __getitem__ method. One of the best things about acquisition
is that it is so black box that you almost never have to think
about it - which makes it easy to forget that if you override
__getitem__ you may need to manually maintain the acquisition
chain.

Acquiring objects are actually "wrappers" which wrap an object with
its parent in a given context. This wrapping process is usually done
for you by the Acquisition machinery which is hooked to the c equivalent
of __getattr__. Anytime you get an instance attribute in a way that
bypasses
that getattr hook, you need to wrap the object yourself to maintain the
acquisition chain, using the __of__ method).

Here is a modified __getitem__ that should work:


     def __getitem__(self,attr):
         # Anytime we get an attr from an __dict__ directly
         # rather than from a getattr, we need to wrap it
         # before returning it
         if self.__class__.__dict__.has_key(attr):
             return self.__class__.__dict__[attr].__of__(self)
         if self.__dict__.has_key(attr):
             return self.__dict__[attr].__of__(self)
         try:
             # If we are able to acquire the attr directly via 
             # aq_acquire, we _dont_ need to wrap it - the 
             # aq_acquire method will have taken care of that
             # for us...
             return self.aq_acquire(attr)
         except: 
             self.__dict__[attr] = AcquTest(attr)
             return self.__dict__[attr].__of__(self)

Hope this helps!


Brian Lloyd        brian@digicool.com
Software Engineer  540.371.6909              
Digital Creations  http://www.digicool.com