Re: [Web-SIG] WSGI Lite

6 views
Skip to first unread message

PJ Eby

unread,
Mar 19, 2013, 1:47:03 PM3/19/13
to Simon Yarde, web-sig
On Mon, Mar 18, 2013 at 1:08 PM, Simon Yarde <simon...@me.com> wrote:
> If I understand correctly, one of the goals of WSGI Lite is to keep the environ out of middleware and app code to prevent modification.

No. You are *allowed* to modify the environment, that's part of the
WSGI spec. What you *can't* do is trust that nobody *else* will
modify it. Which is why you can't use the environment to communicate
with middleware, only objects passed along in the environment.

For example, if middleware does env['foobar']=[], it's okay for a
called app to modify that list, as long as the middleware saves a
reference to it and doesn't try to pull it out of the environ later,
e.g.:

def middleware(...):
my_list = []
env['foobar'] = my_list
app(env)
if my_list: ....blah

But you must NEVER do this:

def middleware(...):
app(env)
if env['foobar']: ....blah

Even if you were the one who put 'foobar' into the env.

WSGI Lite's argument binding protocol addresses a different problem,
which is that after you call app(env), it's too late for you to get
anything you need out of the original environment, because app() was
within its rights to clear env or change its contents in any way. (It
also makes it easier to create application-specific or
framework-specific calling conventions, like if you want your
controller functions to be called with a user and a cart as
parameters, as long as you can define how to get a user and a cart
from a WSGI environment.)


> Could this particular goal be achieved by the middleware creating references to the environ values it depends upon prior to calling the app?

Yes, you can use that to communicate up the middleware chain, as I
showed above. But the problem WSGI Lite bindings solve is not really
related to that.


> Regarding my own scenario..
>
> I have been working on my own small framework, partly as a learning exercise and partly out of frustrations with existing frameworks.
>
> I return 3-tuple responses as per WSGI Lite, and my middleware generates minimal error responses rather than raise exceptions, e.g. ('404 Not Found', ['Content-Length': '0'], []).
>
> I use status-handler decorators to customise these basic responses; these operate at the level of the individual apps or across a dispatch app to provide global response customising.
>
> I use a 'final' flag in the environ to indicate to any outer status-handlers that the response is intended as definitive, i.e. environ[my_framework.status_handler.final] = True, and should not be altered again.

Don't do that. You need to put a callback in the environ, or a
mutable object that you keep a reference to. The environ dictionary
itself is strictly passed down to child handlers, and it's perfectly
valid for a piece of middleware to clear the environ entirely before
returning to its caller. So you can't use raw values in the environ
to communicate up the chain, only down.

Of course, even if you use another method to communicate this "final"
flag, that doesn't necessarily mean that your "final" flag makes any
sense in a WSGI context. It might actually be that you need to use a
custom header like 'X-MyFramework-Final', that your boundary
middleware strips. That is probably actually the right way to do it
in WSGI, because there's no guarantee a "final" response returned from
one app is actually going to be the same response as one returned from
another piece of middleware wrapping that. So really, since this is
information about the response, you should put it in a response
header.

(Perhaps in a future version of WSGI Lite, I could extend the response
protocol to support stripping out some special response headers at the
protocol boundary between WSGI 1 and WSGI Lite.)


> Under WSGI Lite, would I still be able to add flags to the environ? Or is there some other way I should be signalling to outer middleware?

Callbacks and mutable objects are the only way authorized by the WSGI
spec to communicate from an app to an outer server or middleware
(aside from response headers, bodies, or special response iterators),
and WSGI Lite doesn't change this. It just makes it easier to work
with values obtained from the environment.
_______________________________________________
Web-SIG mailing list
Web...@python.org
Web SIG: http://www.python.org/sigs/web-sig
Unsubscribe: http://mail.python.org/mailman/options/web-sig/python-web-sig-garchive-9074%40googlegroups.com

PJ Eby

unread,
Mar 22, 2013, 3:02:00 PM3/22/13
to Simon Yarde, web-sig
[Please follow-up to web-sig, rather than emailing me privately. Thanks.]

On Fri, Mar 22, 2013 at 6:52 AM, Simon Yarde <simon...@me.com> wrote:
> If I have two layers of middleware, can I trust the intermediate layer has
> not altered or wiped out any dotted-name keys in the environ prior to
> calling the layer below?

No; middleware isn't even required to pass the same environ object to
a nested app. But most middleware will, and won't delete things in
it.


> I discovered your article on WSGI Lite as I was having similar ideas about
> binding for the purposes you describe; auth, session, cart etc. and keeping
> the app code minimal. I was aiming to find a way to allow objects
> initialised with an environ to interrupt execution at initialisation time
> and return a response. I felt something like this was needed to avoid doing
> this at the top of every app:
>
> def app(input=Parse(XML, JSON),
> auth=Auth):
> if not input:
> return NotAcceptableResponse
> if not auth:
> return UnAuthorisedResponse
>
> I was playing around with a factory-method approach that would either return
> an instance or a 3-tuple response; the binding process would identify the
> result as an instance and add it to the calling args, or interrupt the call
> and return it.

Raising an exception to be caught would probably be preferable. It
might be that the Lite protocol should add a way to convert an
exception to a response, e.g. by looking for a
__wsgi_response__ attribute on it. That way, you could raise anything
you wanted as a default response, and the error would convert to a
response at the WSGI 1/Lite boundary.

This would mostly be suitable for app-specific errors, but of course
you could put error-handling middleware anywhere in the stack below
the boundary, or formatting middleware above the boundary.

So, thus far, possible extensions to the Lite protocol would be:

* Exception to response conversion
* A standard for stripping custom HTTP headers

I don't have any idea as to when I might get around to these, but if
somebody wants to create a model patch or two, that'd be cool. ;-)

The protocol of course is looking less and less "lite" with these
additions, but I suppose if one looks at "lite" as actually being a
collection of "microprotocols" it's not bad at all. Everything in
Lite is essentially orthogonal right now (and would continue to be
with these additions), so it's nothing like the obscenity of
complexity and legacies that is WSGI's core. ;-)
Reply all
Reply to author
Forward
0 new messages