I didn't expect object traversal to be so... dangerous. If I'm still here to talk about it :^) , that's only because of this thread (bless those mailing list archives!): http://mail.mems-exchange.org/pipermail/quixote-users/2002-January/000222.html I will expose the various stages of my tribulations by means of a silly, contrived example, with working (or not) code. Let's say we have two object, Folder and Directory, handling similar file system data. Both objects will have a "view" method, listing the contents of the self object. The Folder object will also have a "show" method, showing the contents of a File object whose index we will give in the request path. *** First try. Let's go straight to an object, and then to a method. Here is the code: _q_exports = ["Folder", "Directory"] class Folder: _q_exports = ["view"] def __init__(self, request): pass def view(self, request): return "Listing of Folder contents here" Here is the URL: http://localhost/QuixoTest/Folder/view Here is the response: error: Server Error: exceptions.TypeError, unbound method view() must be called with Folder instance as first argument (got HTTPRequest instance instead): file: /opt/python/lib/python2.2/site-packages/quixote/publish.py line: 572 Well, yes, of course, but how do I do that? :^( *** Second try. This time we'll use _q_getname to get an object back into the traversal. Here is the code: from quixote.errors import TraversalError _q_exports = [] def _q_getname(request, name): print "first getname,", name if name == "Folder": return Folder(request, name) elif name == "Directory": return Directory(request, name) else: raise TraversalError, \ "Unknown path element: %s" % name class Folder: _q_exports = ["view"] def __init__(self, request, name): print "Folder init,", name def view(self, request): print "Folder view" return "Listing of Folder contents here" class Directory: _q_exports = ["view"] def __init__(self, request, name): print "Directory init,", name def view(self, request): print "Directory view" return "Listing of Directory contents here" Here are the URLs: http://localhost/QuixoTest/Folder/view http://localhost/QuixoTest/Directory/view They both work. Anyway, such a usage of _q_getname looks like a kludge to me. *** Third try. Now we'll put that Integer demo to use. Here is the code of the new Folder class (the global _q_exports and _q_getname, and the Directory class, are the same as before): class Folder: _q_exports = ["view", "show"] def __init__(self, request, name): print "Folder init,", name try: self.idx = int(name) except ValueError: self.idx = 0 pass def _q_getname(self, request, name): print "Folder getname,", name return Folder(request, name) def view(self, request): print "Folder view" return "Listing of Folder contents here" def show(self, request): print "Folder show,", self.idx if self.idx: return "Contents of File #%s here" % self.idx else: return "No File specified" Here is the URL: http://localhost/QuixoTest/Folder/1/show This works, too. Alas, the debug log shows this: first getname, Folder Folder init, Folder Folder getname, 1 Folder init, 1 Folder show, 1 An unnecessary Folder object has been created, and its init code has tried to convert the class name to an integer. It would be nice if this could be avoided. *** Fourth try. To avoid it, we move the computation of the index to the _q_getname function, and make it return the self object. Here is the code (only the __init__ and the _q_getname methods, the rest is the same): class Folder: # just part of it def __init__(self, request, name): print "Folder init,", name pass def _q_getname(self, request, name): print "Folder getname,", name try: self.idx = int(name) except ValueError: self.idx = 0 return self(request, name) The URL is the same: http://localhost/QuixoTest/Folder/1/show Here is the response: error: Server Error: exceptions.AttributeError, Folder instance has no __call__ method: file: /home/nl/Araknos/aKab/aKab/Web/__init__.py line: 35 Well, well. Python tried to call the self object. *** Fifth try. We want to return the self object without calling it, so let's take away those parameters, unnecessary anyway. We will have to touch the global _q_getname too, since it creates the Folder object. Here is the whole code, again: from quixote.errors import TraversalError _q_exports = [] def _q_getname(request, name): print "\nfirst getname,", name if name == "Folder": return Folder() elif name == "Directory": return Directory() else: raise TraversalError, \ "Unknown path element: %s" % name class Folder: _q_exports = ["view", "show"] def __init__(self): print "Folder init" def _q_getname(self, request, name): print "Folder getname,", name try: self.idx = int(name) except ValueError: self.idx = 0 return self def view(self, request): print "Folder view" return "Listing of Folder contents here" def show(self, request): print "Folder show,", self.idx if self.idx: return "Contents of File #%s here" % self.idx else: return "No File specified" class Directory: _q_exports = ["view"] def __init__(self): print "Directory init" def view(self, request): print "Directory view" return "Listing of Directory contents here" Same URL: http://localhost/QuixoTest/Folder/1/show And here is the debug log: first getname, Folder Folder init Folder getname, 1 Folder show, 1 That's fine. It wasn't easy, but worth it. I post all this to soothe that nagging feeling that there's something else so obvious that I can't see it. :^) In any case, I feel that something about all this should go into the docs, adequately amended of my mistakes and misunderstandings. -- "We should forget about small efficiencies, about 97% of the time. Premature optimization is the root of all evil." Donald Knuth Nicola Larosa - nico@tekNico.net