[PEAK] sitemaps: <content> in non-root <location>

Phillip J. Eby pje at telecommunity.com
Fri Jan 7 16:26:57 EST 2005


At 07:25 PM 1/7/05 +0100, Radek Kanovsky wrote:
>On Fri, Jan 07, 2005 at 01:12:25PM -0500, Phillip J. Eby wrote:
>
> > >So sitemaps.addHelper should be fixed
> > >too:
> > >
> > >    def addHelper(handler,helper):
> > >        def helped_handler(ctx, ob, namespace, name, qname,
> > >default=NOT_GIVEN):
> > >            ob = helper(ob)
> > >            return handler(
> > >                ctx.clone(current=ob, viewHandler=ctx.viewHandler),
> > >                ob,namespace,name,qname,default
> > >            )
> > >        return helped_handler
> >
> > Right; I see what you mean.  I'm wondering if maybe I should just add
> > 'viewHandler' to the list of attributes that are automatically 
> cloned.  But
> > I'm not sure if there are maybe clonings that change 'current', but where
> > the viewHandler should *not* be kept.  I'll have to take a look at that.
>
>I have roughly checked all clone() calls in peak.web and there is
>probably another clone() call in templates.Expects class that need fix.
>Maybe, some global fix would be better.

Yeah, I'm now thinking that something more like your original clone_from 
proposal would actually be better, although I think it should be eager 
rather than lazy; i.e. it should be done at cloning time rather than 
waiting until viewHandler is used, because I don't want to keep around the 
reference to the cloned context.

I've just checked in a more global version of the change.  I don't want to 
dig too much into this for now, because I expect view handling to be 
refactored at some point soon to use generic functions anyway.

In particular, I'm thinking that the traversal context constructor might 
call some sort of "traversedTo(ctx)" generic function that will dispatch on 
ctx.current to see if anything special needs to be done (like setting the 
view handler or changing the policy object to implement different security 
rules in context).  (But, I am not yet sure if this is a good idea, 
performance-wise.)

Actually, I'm also thinking that 'handle_http' will become a generic 
function that will be hookable with before/after/around methods to do 
things like require a login, start a session, etc.  This will get rid of 
the need for 'IWebTraversable.beforeHTTP', and indeed the other 
IWebTraversable methods (getURL and traverseTo) are probably also better 
served as generic functions, essentially leaving no reason to even have the 
IWebTraversable or IHTTPHandler interfaces, just some useful classes that 
define useful methods for those generic functions.  IWebException might go 
away as well, since 'handleException()' could easily also be a generic 
function.

But making those interfaces into generic functions won't have significant 
performance impact, since they are called only a few times (at most once 
per URL level) per request.  It's traversal contexts that might be created 
hundreds or even thousands of times to generate a page.  OTOH, probably 
contexts spend most of their time right now in _setup, __init__, and most 
especially all the functions called by traverseName, so these are 
more  ripe for future optimization anyway.  So, one single-dispatch generic 
call in __init__ probably won't hurt that much, especially if the 
IViewService adaptation can be replaced by it.  I.e., there'll still be 
only one adaptation happening over the context's lifetime to deal with 
location-specific stuff like possible changes of view service, interaction 
policy, skin service, etc.

Hm.  Actually, if traversal itself is a generic function, then all of those 
issues might be embeddable as various methods, such that we can kill 
multiple birds with one stone.  IOW, currently there are lots of functions 
in peak.web with the INamespaceHandler signature, and many of these might 
be able to be methods of a generic function, rather than being looked up 
and dispatched through the other means currently in use.  For example:

@traverseTo.when("ns=='view'")
def traverseView(ctx, ob, ns, name, qname, default=NOT_GIVEN):

     handler = ctx.viewHandler(name,ob)
     if handler is not None:
         return handler(ctx, ob, ns, name, qname, default)

     if default is NOT_GIVEN:
         raise errors.NotFound(ctx,qname,ob)
     return default

Although, more to the point, if there's a single "traverseTo" generic 
function, you would just encode views as when "ns=='view' and 
name=='someView' and isinstance(ob,SomeType)", and you wouldn't need the 
above code at all.  (Indeed, you could possibly even include an "and 
ctx.allows(ob,name)" or other security test as well, allowing different 
views to be selected based on access rights.)  For that matter, you could 
include tests based on the policy instance and URL path in order to have 
location-specific views, without having to mess with changing viewHandler 
around.

The main downside to this approach is that if you have a global generic 
function, then it will end up with references to all these things.  It 
would be better to be able to have the interaction policy have its own 
generic function instance specific to the application.  But, the default 
conditions have to be loaded somehow, and any other "global" rules that get 
imported later should still become active.  This seems to me to mean that 
generic functions (and Dispatchers) need some kind of ability to allow 
others to register and get notified when methods are added to them.  That 
way, you could then clone predicate-dispatch generic functions in the same 
way as you can currently clone single-dispatch generic functions.  The hard 
part is eliminating ambiguity between the "inherited" and "overridden" 
rules between the base function and the derived (cloned) function.  I guess 
if there is an expression/test for what GF a method is added to, you can 
include this in the signature, and therefore make it part of specificity 
testing, such that a method implied in a derived GF is thereby "more specific".

OTOH, none of that is needed for prototyping a GF-based peak.web, and even 
for using it in all but the most demanding situations.  So we could test 
out the ideas using a single global GF and see if we like it, before adding 
GF cloning to the dispatch package.

Anyway, ISTM that a lot of stuff in peak.web could be boiled down to five 
generic functions (not counting the two from peak.security):

* handle_http(ctx)  (aka ctx.renderHTTP())

* traverseTo(ctx, ob, ns, name, qname, default=NOT_GIVEN)  (which would be 
invoked by ctx.traverseName() after doing parseName())

* onTraverse(ctx)  (allow objects to do special things like change policy, 
user, skin, environ contents, etc. upon traversal to them)

* handleException(ctx, exc_info, retry_allowed=True)

* getURL(ctx,ob) (allowing us to get rid of kludgy 'peak.web.url' view)

And this would eliminate at least five interfaces, and perhaps at least as 
many adapters and other classes, while expanding flexibility to do things 
like notice that the request is XML-RPC and marshal that differently than a 
regular POST, etc.

Of course, there are probably some other generic functions that would be 
useful, like a 'getUser' that would do what the old LoginManager would do, 
i.e. extract credentials from a request, then look up users and attempt to 
authenticate them.  And maybe something like 'getMenu' to obtain registered 
menu items for an object, and so on, and so on.

Also, I think the sitemap language needs to grow tags to allow defining 
methods for some of the functions above.  It already has a way to specify a 
kind of method for 'getURL' for content items, but a more general facility 
might also be helpful.  Similarly, a way to specify onTraverse/handle_http 
hooks would also be important.

Finally, there would be plenty of predicate functions needed as well, to 
encapsulate ideas like "there are names left to traverse" or "this is an 
XML-RPC request", in order to make it easier to write rules that depend on 
these things.

But all this is still just in early stages of thought, and I'm not sure 
if/how much all this will actually simplify peak.web, as opposed to 
extending capability or flexibility.  Or more precisely, I'm not sure it's 
going to reduce the code size or complexity.  I do think it will reduce the 
complexity of *understanding* the framework, if we replace lots of nouns 
(interfaces) with a handful of situational verbs (generic functions).  Even 
more specifically, it's easier to see which generic functions I should hook 
into to accomplish something, than to see which interfaces my object should 
implement, or whether I need a Decorator or MultiTraverser or some such thing.




More information about the PEAK mailing list