[Web-SIG] Comments/stylistic ideas regarding WSGI
Phillip J. Eby
pje at telecommunity.com
Mon Aug 23 07:52:35 CEST 2004
At 12:44 AM 8/23/04 -0400, angryhicKclown at netscape.net wrote:
>Now that I understand what WSGI is intended to be used for, I like it a
>lot. However, I do have a few suggestions.
>
>Although it means more typing, I think the API is too cryptic as-is.
So, now that you understand the API, you think it's too cryptic. :)
All kidding aside, I've made some attempts to make the spec more readable
with respect to the various callables, as you'll see in my next draft posting.
>I think that applications should be callable, but should have a single
>parameter: gateway. The gateway parameter contains attributes and methods
>such as environ, start_response(), and write(). This way, it's clear to
>the end-user both in documentation (removing many instances of "callable"
>and confusion with __init__) and also is very much more natural to many
>programmers.
I agree that it's more natural, but I disagree that "naturalness" is an
important goal for the WSGI spec. The reason is that most of WSGI's
initial audience will be implementing exactly *one* server/gateway and/or
application, in order to add support for it to their server or application
framework. They will thus have "spec in hand" when implementing. It's
more important that they be able to easily implement the spec.
The second audience for WSGI will be people creating "middleware"
components, and they will appreciate the bare-bones nature of WSGI even
more, because they will not need to implement a "gateway" class in order to
intercept inputs, outputs, or variables. Many fairly sophisticated pieces
of middleware will be written as a single function (maybe with one or two
nested functions).
Best of all, these functions will be *very* explicit as to what they are
modifying, because they will not contain code that's needed to emulate
functions they aren't replacing. Using multi-functional objects like your
"gateway" proposal means that middleware components have to implement the
full gateway interface.
>Finally, I think the most important reason this change should be
>implemented is because it allows the interface to be easily upgraded
>without breaking compatibility with older versions.
Actually, the current interface includes *numerous* routes for extension,
including additional 'wsgi.' keys, and keyword arguments to callables.
> Perhaps (just an example), in the future, there will be a need for a
> flush() method, in addition to the write() method. In the current
> version, start_response() would return a tuple of write() and flush(),
> which would break current compatibility. The only other way I see of
> doing this using the current spec would be passing a default parameter of
> the version of the API used, which is ugly.
It would be simple to add a 'wsgi.flush' key to the environ to supply this
functionality, were it needed. (Of course, flush() isn't actually needed,
because WSGI requires write buffers to always be emptied ASAP.)
>In my opinion, my proposal looks a bit clearer.
I agree with you, but as I said, it's not a primary goal. WSGI will rarely
be used directly by an application developer; it's much more likely that
you will use some other Python Web API layered atop WSGI. In other words,
the intended audience is developers of servers, frameworks, and
middleware. And most framework and server authors will only code to the
spec once, probably with the spec in hand so they can check their
compliance. I think it's better for them to have an absolutely unequivocal
spec, that's simple to implement and easy to verify the correctness
of. For example, did you use a dictionary? That's a trivial yes-or-no
thing to check, compared to, e.g., "did I implement a sufficiently
dictionary-like object?"
>My other idea (which follows the previous proposal) is to scrap
>start_response() entirely, and instead set gateway.status and
>gateway.headers attributes. The simple app would now look like:
>
> def simple_app(gateway):
> gateway.status = '200 OK'
> gateway.headers = [('Content-type','text/plain')] # perhaps
> gateway.set_header('Content-type','text/plain')?
> gateway.write('Hello world!\n')
To properly evaluate your proposal, it's inappropriate to use the
application-side code as a basis for comparison. Compare the *server-side*
code, and the code needed to implement various forms of middleware. You
will find that the relatively small gain on the application-side code is
*rapidly* counterbalanced by the expanding complexity of servers and
middleware. For example, to implement a middleware component that applies
an XSLT stylesheet, you'll need to create a class that implements all the
WSGI methods, and delegates the ones it doesn't need to the previous
gateway object. It will also need properties so it can observe the setting
of status and headers, and delegate those as well, while tracking what it
needs.
By comparison, the functional architecture of WSGI allows a middleware
component to simply pass through to the next component whatever it doesn't
need to change. For example, a middleware component for applying an XSLT
stylesheet would only need to define 'start_response' and 'write'
replacements, where the 'start_response' simply munged the headers for
content type and length, and the 'write' would pump data into the
stylesheet mechanism, and call the old write function with any output.
These changes are clearly connected to the functionality: there is no
overhead being added just so the next component downstream gets a more
"object-oriented" interface.
(I'm wondering if I should add any of this to the spec, but it already has
a paragraph in the Rationale section saying the API is intentionally
no-frills, and another one in the Q&A saying "Why is this interface so
low-level?". I'm not sure how much more I can add without it seeming
overdefensive, although I'm sure I'll get ten times as many more "why don't
you use an object" protests once this hits c.l.py. Oh well.)
More information about the Web-SIG
mailing list