[PEAK] PROPOSAL: Remove ZODB/Zope X3 dependencies/"compatibility"

Phillip J. Eby pje at telecommunity.com
Wed Jun 2 17:20:10 EDT 2004


At 09:05 PM 6/2/04 +0200, ueck at net-labs.de wrote:
>Quoting "Phillip J. Eby" <pje at telecommunity.com>:
> > Most implementations of IHTTPHandler will check to see if the environ
> > indicates a need to traverse further, and if so, call an appropriate
> > ITraversable's traverse method to obtain the new object, then adapt the 
> new
> > object to IHTTPHandler and delegate the handle_http call to it.
>
>one could then, within some component close the the traversal-root
>also do the work for parsing headers and request-type to decide
>wether it is a Browser/WebDAV/XMLRPC/SOAP-Request and
>adapt to more specific Interfaces (e.g. IXMLRPCHandler) if needed.
>but this component is only plugged into the publishing if this feature
>is needed(same for ++XXX++/@@-URL handlers).

You could do that, of course, but IMO it makes more sense to let endpoints 
handle these matters.  That is, the actual method or template or whatever 
that's getting executed can implement these protocols if and as they wish.

I've been thinking since yesterday that there should be an API call as follows:

     web.lookup(environ, key, default=NOT_GIVEN)

where 'key' is adaptable to config.IConfigKey.  This API would attempt to 
compute the value of 'key' for 'environ', or return its cached value.  To 
compute a value, it would look up the key 'peak.web.context', look up 
'config.FactoryFor(key)' in that context, and then invoke the found factory 
on 'environ' and 'key'.  That is, something like:

     def lookup(environ,key,default=NOT_GIVEN):
         if key in environ:
             return environ[key]

         context = environ['peak.web.context']
         factory = 
config.lookup(context,config.FactoryFor(key),default=NOT_FOUND)

         if factory is NOT_FOUND:
             if default is NOT_GIVEN:
                 raise XXX
             return default

         result = environ[key] = factory(environ,key,default)
         return result

Now, by registering factories for property names in an .ini, one can create 
configuration namespaces within the environment for whatever 
application-defined values might be needed.



> > ITraversable, however, will not be the one to make changes to the
> > HTTP-specific environ values such as PATH_INFO and SCRIPT_NAME.  Only
> > IHTTPHandler will make those changes.  However, I'm thinking that
> > ITraversables will still be required to update something in the environ to
> > reflect the path travelled.
>
>and an uptodate list of remaining items to traverse to.

No.  ITraversable is only responsible for traversing the name it has been 
given.  IHTTPHandler is responsible for adjusting PATH_INFO and SCRIPT_NAME 
as traversal progresses.

The reason for this division is that ITraversables can be used in non-HTTP 
contexts, such as traversal to resources from skins, and data path 
traversal in DOMlets.  These kinds of traversal should not have any effect 
on HTTP variables in the environment, or else anything that needs to know 
what URL was actually in effect would get confused.

So, although I still think that there will be something updated in the 
environment by ITraversables, I'm quite clear that it will not be any of 
the HTTP variables.


> > There are still some bits to be worked out with regard to how absolute and
> > relative URL's get calculated, but the rest is mostly just nailing down
> > division of responsibilities, defining names for certain values in
> > 'environ', and establishing API calls to get at most of the things that 
> one
> > now gets at via attributes of the context or the interaction.
>
>the parrallelism of Traversed-Path vs. Component-Tree will still exist ?

Depends on what you mean by that.  There was never any such parallelism 
required, although it's certainly convenient for lots of things.

For applications laid out using XML, yes, they will essentially be 
hierarchical in that way.  Resource directories too.  But, there is no 
tight binding occuring here, and the return value of a traversal operation 
need not have *any* relationship to the ITraversable that returned it.


> > At that point, peak.web will be something of a microkernel, with only two
> > very simple interfaces for someone to implement, and lots of imperative 
> API
> > calls that can be used to do useful things.  To make it easy-to-use,
> > however, we'll then define controller classes (replacing the previous
> > Decorator concept) and an XML vocabulary for defining/declaring them.  And
> > as time goes by we'll likely end up writing lots of HTTP utility functions
> > to do things like extract cookies, set cookies, etc. given an
> > 'environ'.  But, since these functions won't be bound into "request" or
> > "response" types, any component will be free to use alternative ways of
> > accomplishing those tasks, should they need to do so.
>
>i think i like this :)
>
>how can we get ahead ?

We need to more precisely specify which interfaces do what.  I've decided 
also that IHTTPHandler is a base class of IHTTPApplication, where the 
latter is the interface that applications are adapted to by a container 
(like the web runner, supervisor, FastCGI control, etc.).  This then makes 
it easy to put a transaction/error handling wrapper around the root 
component of a web app, without forcing the serialization format to specify 
that an object is "top level".

What I meant to say is that if you design an application's site layout with 
the XML format, it should be composable.  You should be able to "include" 
that XML file into a larger application, and just have it work.  So, there 
shouldn't need to be anything in the configuration that says, "I'm the root 
of the site".

Of course, we could handle this by having a flag in the environment that 
indicates whether the transaction/error wrapping is being handled by a 
higher level component, but then this means that every 'handle_http()' 
method would have to check this.  So, better to wrap the top level in an 
adapter.  Anyway, this bit of policy (one hit = one transaction, plus a 
mechanism for viewing/handling exceptions) should be the only thing hardwired.

It's possible to question even this, though.  Should individual components 
within a site perhaps be specifically marked for transaction handling or 
exception handling?  What happens if a subcomponent traps certain errors, 
and then the parent component commits the transaction anyway?  But I don't 
see much way around this, since even the very beginnings of traversal will 
likely want to have authentication, which probably means using a DM, which 
therefore means a transaction is needed.  So, I guess it does make sense to 
stick with the implicit transaction model.

Anyway, IHTTPApplication will be the same as IHTTPHandler, except that it's 
responsible for preparing the environment by:

* setting the initial value for 'peak.web.context'
* beginning the transaction
* trapping exceptions from subsequent handlers
* committing or aborting the transaction, if it is still in progress

However, to make this more flexible, it's possible that it could be given a 
plugin architecture, so as to allow registering plugins to do additional 
pre or post processing functions, and I suppose that these functions 
themselves could be plugin-ized.

Luckily, all of these policy issues do not interfere with mechanism.  The 
mechanism is simply that you need an IHTTPApplication, and you can always 
write your own.

Anyway, the main bits that are still really open-ended in my mind have to 
do with computing traversed and absolute URLs.  A traversable should be 
able to give you its URL, given an environment.  (Which means that 
ITraversable also needs a getURL() method.)

In the current system, TraversalContext can compute both a "traversed URL" 
and an "absoluteURL" by keeping track of the previous TraversalContext.  I 
suppose I could do the same with 'environ', i.e. having each 'environ' have 
a pointer to the previous environ traversed from.  And the IHTTPApplication 
could set the initial environment's base "absolute" URL.

So, attempting to map from old -> new:

     TraversalContext -> functions operating on 'environ'
     Traversal        -> IHTTPApplication initializes starting 'environ'
     *Traversable,Decorator,MultiTraverser -> roughly the same, but maybe 
fewer methods
     CallableAsWebPage -> needs complete reimplementation to handle mapply 
and argument parsing
     Interaction, InteractionPolicy -> more funcs on 'environ'
     Resource classes -> some refactoring
     Request/response classes -> functions operating on 'environ'
     etc...

All in all, this is mainly a matter of rather tedious rewriting, while 
adding tests.  One of the annoying things about the Zope dependency of 
peak.web is that it was hard to write good unit tests that didn't depend on 
Zope, too.  And, it was hard to define sufficiently small units, because so 
many things were emergent properties of a larger system.  It should be a 
lot easier to write tests for small operations against 'environ' dictionaries.

The rewriting will take a while, though.  When I've replaced all the 
existing code, I will not yet have replaced the functionality, because we 
will be losing Zope-supplied functions like cookie parsing, query string 
and form post parsing, cookie setting on responses, character set 
negotiation, and all of that sort of thing.  Those will have to be written 
as property factories and added to the configuration, but that also makes 
them good candidates for contributed patches.  And so, as soon as a 
'web.lookup()' function exists, that would be a good time when volunteers 
such as yourself could start writing factories to replace functionality we 
currently get from Zope X3.

Contributions should include unit tests that can be integrated into the 
peak.web test suite.

Oh, and by the way, I don't want to go with the 'foo:int' style of 
parameter passing as the default means of handling input parameters.  That 
doesn't mean it shouldn't be optionally available to people (via a 
different property namespace), I just mean that I don't consider it a high 
priority, and I don't think it should be the default mechanism for getting 
at query/cookie/form variables.  It was originally designed for interacting 
with oblivious Python code that wasn't designed for web use.  But that's 
what controllers (formerly decorators) are for, and we have other things 
like function attributes and adaptation that can be used to determine what 
kind of data a function is looking for.

Anyway...  did that answer your question?  :)




More information about the PEAK mailing list