As I said on IRC I think this is pretty cool stuff IMHO.
Your solution does probably make a lot more sense than using a tool, a
tool makes use of CP3 hooks, but hooks are really application/framework
specifics while middleware are working at the HTTP level
(beginning/ending of a request process) that's why I actually think
your solution is more coherent and feels right than wrapping the
middleware concept inside a CP tool since they *really* are two
different things (both needed and very useful) and wrapping a
middleware to look like a tool doesn't make that much sense.
With CP3 hooks you have fine grained (framework level) control over the
request/response process while with WSGI middleware you can apply
request preprocessors and response postprocessors where it makes sense
to do so, you can also reuse a wide range of available middleware or
make your own and share them to let other applications/framework use
them, but you can't make everything with a WSGI middleware
(encoding/decoding comes to mind as a first example) that's why you
need CP tools, you should use them and they need to stay because they
are essential.
>From PEP333:
"In addition to ease of implementation for existing and future
frameworks and servers, it should also be easy to create request
preprocessors, response postprocessors, and other WSGI-based
"middleware" components that look like an application to their
containing server, while acting as a server for their contained
applications."
So your solution does make a middleware look like an application to the
CP wsgi server and not like a tool, it's just right IMHO. ;-)
What I would really like to be able to do with CP3 is the the ability
to easily configure a middleware and the middleware pipeline using
simple configuration directives just like you can do with any CP
component, example:
cp_app = cherrypy.Application(Root(), conf=config)
cherrypy.tree.mount(cp_app, '/evalexception', wrap=False)
where config is something like this:
# configure some middlewares to be used inside our pipeline
middleware.evalexception.on = True
middleware.auth_digest.on = True
middleware.auth_digest.realm = ...
middleware.auth_digest.authfunc = ...
# configure the pipeline to apply
middleware_pipeline = [middleware.evalexception,
middleware.auth_digest]
This configuration task could be easily made by leveraging the same
interface used by Paste filter factory [1] just to standardize that
thing so that any middleware writer can provide such a function (for
example flup does this IIRC), to use them in CP you will just need to
register them in the same way you register a tool.
I don't think this would be very difficult but I think it would rock, I
*really* hope this discussion doesn't start another long thread
regarding CP and WSGI, personally I don't think CP should change that
much (from a user perspective) to accommodate an easy WSGI use, giving
the ability to configure a middleware and the middleware pipeline and
given that you can already mount multiple isolated apps (wsgi or CP)
and you can access the wsgi environ form the request object you
shouldn't need any other esoteric thing and CP3 will still remain our
beloved CP. ;-)
Ciao
Michele
[1] http://pythonpaste.org/deploy/#paste-filter-factory
Christian Wyglendowski wrote:
> Hi folks,
>
> I have created a new branch in the CP SVN repo to experiment with some WSGI
> ideas. I wanted to get some feedback from all of you on whether or not
> these changes are desirable/beneficial/etc.
>
> The branch is at http://svn.cherrypy.org/branches/cp3-wsgi-remix/. You can
> browse the code via Trac at
> http://www.cherrypy.org/browser/branches/cp3-wsgi-remix.
>
> Michele mentioned on IRC yesterday that it would be great to be able to hook
> middleware into the CP request process via CP3 tools to wrap various points
> in the tree with the functionality from the middleware. While it might be
> possible to do that, I decided to investigate a different path that would
> yield similar results.
>
> cherrypy.tree is now a dispatcher for WSGI applications, via a new dispatch
> method. The dispatching functionality from _cpwsgiserver has been removed.
> cherrypy.tree.mount now accepts 3 general types of "apps":
>
> 1) A standard cherrypy.Application (formerly
> _cptree.Application) - left as is.
> 2) A "root" object - wrapped in an Application instance.
> 3) A WSGI callable - optionally wrapped in a _cpwsgi.HostedWSGI
> instance.
>
> Assuming simple "hello, world" style Root class and test_app WSGI callable,
> the following is possible:
>
> cherrypy.tree.mount(Root())
> cherrypy.tree.mount(test_app, '/hosted/app0')
>
> config = {'/': {'throw_errors':True}}
>
> cp_app = cherrypy.Application(Root(), conf=config)
> cherrypy.tree.mount(EvalException(cp_app), '/evalexception', wrap=False)
>
> cherrypy.server.quickstart()
> cherrypy.engine.start()
>
> That hosts a standard CP3 Application at "/", a generic WSGI callable at
> "/hosted/app0" (then wrapped in a HostedWSGI class to allow for tools) and a
> CP3 Application wrapped in EvalException middleware at "/evalexception". I
> never liked the hoops that had to be jumped through in 2.2 to wrap a CP WSGI
> app in middleware, so the EvalException example above gets me excited :-)
>
> The Application class was moved from _cptree to _cpwsgi and now has a
> __call__ method that implements the functionality that was previously in
> _wsgi_callable.
>
> The test/test_wsgiapps.py file tests some of the behavior in the example
> above. Have a look if you want to see a more complete example.
>
> I'm sure I have overlooked some stuff, and there is probably some cruft and
> needed reorganization/refactoring, but it passes the test suite. I only had
> to modify test/helper.py to adjust the app mounting behavior.
>
> Let me know what you think.
>
> Christian
> http://www.dowski.com
>
> ------=_Part_161603_10520877.1155651181262
> Content-Type: text/html; charset=ISO-8859-1
> X-Google-AttachSize: 3299
>
> Hi folks,<br><br>I have created a new branch in the CP SVN repo to experiment with some WSGI ideas. I wanted to get some feedback from all of you on whether or not these changes are desirable/beneficial/etc.<br><br>The branch is at
> <a href="http://svn.cherrypy.org/branches/cp3-wsgi-remix/">http://svn.cherrypy.org/branches/cp3-wsgi-remix/</a>. You can browse the code via Trac at <a href="http://www.cherrypy.org/browser/branches/cp3-wsgi-remix">http://www.cherrypy.org/browser/branches/cp3-wsgi-remix
> </a>.<br><br>Michele mentioned on IRC yesterday that it would be great to be able to hook middleware into the CP request process via CP3 tools to wrap various points in the tree with the functionality from the middleware. While it might be possible to do that, I decided to investigate a different path that would yield similar results.
> <br><br>cherrypy.tree is now a dispatcher for WSGI applications, via a new dispatch method. The dispatching functionality from _cpwsgiserver has been removed. cherrypy.tree.mount now accepts 3 general types of "apps":
> <br><br> 1) A standard cherrypy.Application (formerly _cptree.Application) - left as is.<br> 2) A "root" object - wrapped in an Application instance.<br> 3) A WSGI callable - optionally wrapped in a _cpwsgi.HostedWSGI instance.
> <br><br>Assuming simple "hello, world" style Root class and test_app WSGI callable, the following is possible:<br><br><div style="margin-left: 40px;"><span>cherrypy.tree.mount(Root())</span><br><span>cherrypy.tree.mount
> (test_app, '/hosted/app0')</span><br><span></span><br><span>config = {'/': {'throw_errors':True}}</span><br><span></span><br><span>cp_app = cherrypy.Application(Root(), conf=config)</span><br><span>cherrypy.tree.mount(EvalException(cp_app), '/evalexception', wrap=False)
> </span><br><span></span><br><span>cherrypy.server.quickstart()</span><br><span>cherrypy.engine.start()<br></span></div><br>That hosts a standard CP3 Application at "/", a generic WSGI callable at "/hosted/app0" (then wrapped in a HostedWSGI class to allow for tools) and a CP3 Application wrapped in EvalException middleware at "/evalexception". I never liked the hoops that had to be jumped through in
> 2.2 to wrap a CP WSGI app in middleware, so the EvalException example above gets me excited :-)<span></span><br><span></span><span></span><br>The Application class was moved from _cptree to _cpwsgi and now has a __call__ method that implements the functionality that was previously in _wsgi_callable.
> <br><br>The test/test_wsgiapps.py file tests some of the behavior in the
> example above. Have a look if you want to see a more complete example.<br><span></span><br>I'm sure I have overlooked some stuff, and there is probably some cruft and needed reorganization/refactoring, but it passes the test suite. I only had to modify test/helper.py to adjust the app mounting behavior.
> <br><br>Let me know what you think.<br><br>Christian<br><a href="http://www.dowski.com">http://www.dowski.com</a><br>
>
> ------=_Part_161603_10520877.1155651181262--