[Zope] Wrapping objects: __getattr__ weirdness

Paul Winkler pw_lists@slinkp.com
Wed, 9 Oct 2002 17:33:04 -0700


I'm trying to write a pretty transparent wrapper product in 
python, which will have one property, source_path,
which contains a (string) path to another object.
I can resolve this path to the object, no problem;
but I cant' see how to delegate attribute requests
to it. my  __getattr__ fails with  
(snip - complete traceback later):
  File "/zope/Zope_software_home/lib/python/OFS/Traversable.py", line 95, in unrestrictedTraverse
    self=self.getPhysicalRoot()
TypeError: object of type 'string' is not callable


Which is weird, because I can do getPhysicalRoot() on both
the wrapper and the wrapped object and it ain't a string....

Before I go further I'll explain the motivating scenario.  We 
have four zope sites (on one server) which have a fair amount 
of content overlap. This content is not in a consistent location 
across sites (e.g. we might have www.site1/blah/bleh/foo  
and www.site2/blarf/foo).  We have a non-technically-minded, 
overworked content manager and an ever-expanding mountain of content. 
Currently, her job is nearly impossible and i'm charged with creating 
the solution.  

So I thought I would:
1) build a single cmf site for content management
(cmf also provides other things we need, so it seems to be a good
fit).

2) customize workflow so that one stage of publishing consists
of specifying where you want the content to appear, and then
wrapper objects are created which will render the content in context
*and* provide access to the wrapped object's properties.
e.g. you'd get a form in which you can type in some variant of
"site1/blah/bleh/foo" and "www.site2/blarf/foo", and we'd
automagically create wrappers at those locations which point
back to this content.

3) customize the management skins so that 
deleting or moving the source content also updates the
wrappers. (sounds like zope 3 will make this easier, but
who knows when that will be.)


Alternative solutions to the above scenario would be welcome,
but in the meantime, here's what I'm trying now for the
wrapper product:

    # ... management methods snipped, they're working fine

    def get_source_object(self):
        """ return the (aq-wrapped) object referred to by source_path. """
        obj = self.unrestrictedTraverse(self.source_path, default=None)
        return obj.__of__(self)

    # everything else comes from the source object.
    def __getattr__(self, name):
        obj = self.get_source_object()
        attr =  getattr(obj, name)
        # do I need to aq-wrap attr if obj is aq-wrapped? not sure.
        return attr.__of__(self)   
        # return attr doesn't work either


Now I go into zope and create one of these at /paul_stuff/,
called "whatever", and point it to '/index_html'.  That
seems to work. Now let's have a look, at the prompt...

>>> wrapper = app.paul_stuff.whatever
>>> wrapper
<ContentProxy instance at 8c9d418>
>>> wrapper.source_path
'/index_html'
>>> obj = wrapper.get_source_object()
>>> obj
<DTMLMethod instance at 8a95ca0>
>>> obj.getPhysicalPath()
('', 'index_html')


So far so good...  but try to access something in obj via wrapper,
and it's no good.

>>> obj.raw
'<dtml-var standard_html_header>\n<dtml-var zope_quick_start>\n<dtml-var standard_html_footer>'
>>> wrapper.raw
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "/zope/InstanceHome/Products/ContentProxy/ContentProxy.py", line 68, in __getattr__
    obj = self.get_source_object() 
  File "/zope/InstanceHome/Products/ContentProxy/ContentProxy.py", line 63, in get_source_object
    obj = self.unrestrictedTraverse(self.source_path, default=None) 
  File "/zope/Zope_software_home/lib/python/OFS/Traversable.py", line 95, in unrestrictedTraverse
    self=self.getPhysicalRoot()
TypeError: object of type 'string' is not callable


This is baffling. Where am I calling a string?? 
I've been searching for similar issues on google
and turning up nothing that helps...
this message seems to indicate that overriding __getattr__ should be doable:
http://zope.nipltd.com/public/lists/dev-archive.nsf/b1b1e2c766c8f6c7802568c1006328f1/f9c0f2754a54368580256bcf0029524b?OpenDocument

--

Paul Winkler
"Welcome to Muppet Labs, where the future is made - today!"