[Grok-dev] late night revelation regarding grok.View

Brandon Craig Rhodes brandon at rhodesmill.org
Sun Sep 23 01:51:22 EDT 2007


I have found a much simpler solution to the problem with grok.View,
zope.traversing.browser.absoluteurl.AbsoluteURL, and _getContextName.
It will create a new version of Grok that works both with the old, and
with the upcoming, versions of zope.traversing.

The solution, for those without the time to read further, is to add
the following line to grok.View.__init__():

        self.__name__ = self.__view_name__

Some background:

When Zope needs to know where a given object lives, it asks it for two
attributes: its __parent__, which is the container in which the object
resides, and its __name__.

But when grok.View was written, its writer chose *not* to set the
class's __name__ to whatever the user wanted the view named in URLs
(like "index.html" or "photos"), because these days __name__ is
special to Python - it is supposed to hold the name of the class for
use in error messages and such.  So there were two problems:

 - Each grok.View's name had to be stored somewhere safe.

 - When AbsoluteURL came along to ask for a View's __name__, the value
   from the other storage had to be what got returned.

So far, grok.View has been storing its name in the non-reserved class
attribute __view_name__, and then registering its own AbsoluteURL
multi-adapter subclass that looks in __view_name__ instead of in
__name__.

Earlier this week I improved AbsoluteURL in a way that breaks the hook
that Grok relied on, so I suggested that we replace the AbsoluteURL
adapter that grok.View has been using with a small and sleek ILocation
adapter that, when asked for __name__, returns context.__view_name__.
I spent some time on this solution tonight, and it turns out to be
almost impossible, because grok.View actually inherits from classes
that proclaim that it natively offers ILocation!  Therefore I was
having to disable this claim with:

    interface.implementsOnly(interfaces.IGrokView, IBrowserPage, IBrowserView)

as the first line of grok.View (which means "I implement everything
but ILocation"), which is brittle and ugly!

So I rubbed my chin and thought to myself: why on earth don't all Zope
objects have this problem?  If it's so important for grok.View to
avoid setting __name__ so that Python doesn't get mad, then why don't
I see all other Zope objects doing similar maneuvers?

And then the answer hit me: because most Zope objects are instances!
There is no problem with an instance setting its own __name__, only
with classes doing so; and that's the problem - each zope.View that
the user declares is a new class.

Which immediately suggested the solution, which, as I stated above, is
to add the following line to the __init__() method of grok.View:

        self.__name__ = self.__view_name__

Do you see the magic?  With this addition, every time a grok.View
instance is created, its __name__ - which we can set with impunity,
since Python is not sensitive about the __name__'s of instances - gets
set to the secret value squirreled away in __view_name__!  And then,
rather than having to go behind Zope's back to make the View operate,
we find that it Just Works because we're now actually letting each
View instance fulfill the contract declared by its interfaces.

I'll commit this change tomorrow if none of the experts think I'm
crazy.

-- 
Brandon Craig Rhodes   brandon at rhodesmill.org   http://rhodesmill.org/brandon


More information about the Grok-dev mailing list