publishing a double-underscore method?
Hi all, I'm working on a product where I want to publish the results of a custom __getattr__ method. I realize overriding __getattr__ isn't generally recommended, but it fits my specific requirements for this product. The problem I'm having is that even very trivial returns from __getattr__ kick out the "empty or missing docstring" error. A trivial example that doesn't work: ---------- security.declarePublic('__getattr__') def __getattr__(self, name): """ I have a docstring, not that it seems to matter """ if name == 'test': return 'this is a test' else: raise AttributeError, name ---------- Here's what I actually want to do: ---------- def __getattr__(self, name): if name in self.some_mapping.keys(): return self.some_method(name).__of__(self) else: raise AttributeError, name ---------- Playing with this a bit, it appears that anything I attempt to return from __getattr__ is checked for publishability **after** the return value has been computed. Since double-underscore methods are not typically published, I'm not surprised that I'm having trouble. It seems there should be some directive that explicitly permits method publication in such cases. Is there such a thing? Thanks in advance. I'm using Zope 2.6.1 on Gentoo, if it matters. Dylan
On Tue, May 06, 2003 at 03:36:02PM -0700, Dylan Reinhardt wrote:
Hi all,
I'm working on a product where I want to publish the results of a custom __getattr__ method. I realize overriding __getattr__ isn't generally recommended, but it fits my specific requirements for this product.
The problem I'm having is that even very trivial returns from __getattr__ kick out the "empty or missing docstring" error.
A trivial example that doesn't work:
---------- security.declarePublic('__getattr__')
def __getattr__(self, name): """ I have a docstring, not that it seems to matter """
right, you're not visiting http://mysite/foo/__getattr__ so the publisher doesn't care whether __getattr__ has a docstring.
if name == 'test': return 'this is a test'
so you're visiting http://mysite/foo/test? i guess the problem is that 'test' and/or 'this is a test' do not have docstrings. i'm not sure which is the problem. try using some type other than strings, something with a docstring? anyway, all that said, i think you might consider using __bobo_traverse__ instead of __getattr__. That's what it's for. -- Paul Winkler home: http://www.slinkp.com "Muppet Labs, where the future is made - today!"
On Tue, 2003-05-06 at 11:45, Paul Winkler wrote:
if name == 'test': return 'this is a test'
so you're visiting http://mysite/foo/test?
Yes.
i guess the problem is that 'test' and/or 'this is a test' do not have docstrings. i'm not sure which is the problem.
I'm not sure I see how either *could* have docstrings. Am I missing something?
try using some type other than strings, something with a docstring?
I also did: __getattr__(self, name): if name == 'test': return self.my_test() *and* tried __getattr__(self, name): if name == 'test': return self.my_test().__of__(self) for: def my_test(self): """ got a docstring here too """ return 'simple test completed' Each of the above uses of my_test returned the same docstring error.
anyway, all that said, i think you might consider using __bobo_traverse__ instead of __getattr__. That's what it's for.
Sounds like I've got some Googling to do. Thanks for the push in the right direction. Dylan
I'm glad to see you got it working using __bobo_traverse__ but i wanted to follow up on this, in case others following this thread are still confused. On Tue, May 06, 2003 at 04:21:49PM -0700, Dylan Reinhardt wrote:
On Tue, 2003-05-06 at 11:45, Paul Winkler wrote:
if name == 'test': return 'this is a test'
so you're visiting http://mysite/foo/test?
Yes.
i guess the problem is that 'test' and/or 'this is a test' do not have docstrings. i'm not sure which is the problem.
I'm not sure I see how either *could* have docstrings. Am I missing something?
Yes - when you visit http://my_zope/foo/test, assuming there's an object with the id "foo" in the zope root, ZPublisher tries to get "test" from foo by doing getattr(foo, "test") In your case, you've defined __getattr__ in such a way that it succeeds even though there is no attribute foo.test. All well and good so far; the result is a string, "this is a test". Here's where the problem is. Once the path has been resolved to an object, ZPublisher DOES NOT send this object directly to the browser. Instead, in lib/python/ZPublisher/BaseRequest.py, in BaseRequest.traverse() (a really huge monster method), it does several things to try to "publish" the object (i.e. send something back to the browser). Summarized: 0) check to see if the object has a __doc__ attribute. if not, stop raise an error - only objects with docstrings may be published. In your case, this is as far as we got since strings don't have docstrings. 1) check to see if the object defines __browser_default__ (which allows you to use something other than index_html). I don't know the details of __browser_default__ so we'll ignore that for now. :) 2) otherwise, try to get an index_html attribute from the object, call it, and send the result to the browser. 3) otherwise, try to call the object itself (i.e. its __call__ method) and send the result to the browser.
I also did:
__getattr__(self, name): if name == 'test': return self.my_test() *and* tried
__getattr__(self, name): if name == 'test': return self.my_test().__of__(self)
for:
def my_test(self): """ got a docstring here too """ return 'simple test completed'
Each of the above uses of my_test returned the same docstring error.
heh... that's because you're still giving zpublisher a string to publish. If you gave it self.my_test instead of self.my_test(), you'd be OK. BUT then you get into why it's tricky to overload __getattr__... __getattr__ is already used heavily by zope for acquisition AND security, and as soon as I tried doing funky things with __getattr__ i got all kinds of odd errors with very confusing tracebacks. This is the reason we have __bobo_traverse__. -- Paul Winkler home: http://www.slinkp.com "Muppet Labs, where the future is made - today!"
On Wednesday 07 May 2003 03:57 am, Paul Winkler wrote:
BUT then you get into why it's tricky to overload __getattr__... __getattr__ is already used heavily by zope for acquisition AND security, and as soon as I tried doing funky things with __getattr__ i got all kinds of odd errors with very confusing tracebacks. This is the reason we have __bobo_traverse__.
I have a __bobo_traverse__ problem myself, which is baffling me: * I have a Zope Object "Home" which has an overloaded __bobo_traverse__. Otherwise it inherits from "Folder". * If the name requested is contained in "Home" (or aquirable?), it returns that. Otherwise, it does a database lookup using a ZSQL method, and tries to return the wrapped result: return UserPB(username, self.DB.getUser(username=username)[0]).__of__(self) Now if I visit the URL: "/Home/myuser" I see the result of calling UserPB.index_html() of the new object "myuser". To get that far, I had to have UserPB inherit from Acquisition.Explicit and Traversable, and I had to define methods "getId()" and "index_html()". So far, so good. But if I try to access the object "myuser" and call one of its methods: Home.restrictedTraverse('myuser').profile( ... args omitted ...) then the security check in Traversable.traverse() fails -- I get "You are not authorized to access myuser in this context" Why could that happen? I've used ClassSecurityInfo() in "Home" and also setDefaultAccess("allow") (I might want to change that later, once I understand what's going on). I've tried the VerboseSecurity product on this, but it doesn't make any difference in this case -- same messages (and yes I am certain I'm using ZOPE_SECRITY_POLICY=PYTHON, I've seen the logs with and without that, and without it, VerboseSecurity logs an error on startup. TIA for any ideas, Terry -- Terry Hancock ( hancock at anansispaceworks.com ) Anansi Spaceworks http://www.anansispaceworks.com
Thanks for the pointer, Paul... works like a charm! For anyone else following this thread, the solution is: --------- class Foo(various_mixins): def __bobo_traverse__(self, request, key): if key == 'test': return self.testing def testing(self): """ a test method """ return 'testing, 1... 2... 3' ---------- This will return the results of foo.testing() for the request: server/foo/test Note that the absence of parens on line 4 is not a typo... __bobo_traverse__ returns a callable object, not a result string. Thanks again, Dylan
participants (3)
-
Dylan Reinhardt -
Paul Winkler -
Terry Hancock