Fixed middleware example not handling 'close' correctly. Fixed an

erroneous statement re: calling 'write()' with empty strings.  Moved
"Multiple Invocations" note into overview, and moved "callable"
definition to overview's preface.
This commit is contained in:
Phillip J. Eby 2004-09-17 16:32:17 +00:00
parent 3f6ca60005
commit 3fd244d830
1 changed files with 58 additions and 34 deletions

View File

@ -132,6 +132,15 @@ application to their containing server, and as a server to a
contained application, and can be used to provide extended APIs,
content transformation, navigation, and other useful functions.
Throughout this specification, we will use the term "a callable" to
mean "a function, method, class, or an instance with a ``__call__``
method". It is up to the server, gateway, or application implementing
the callable to choose the appropriate implementation technique for
their needs. Conversely, a server, gateway, or application that is
invoking a callable **must not** have any dependency on what kind of
callable was provided to it. Callables are only to be called, not
introspected upon.
The Application/Framework Side
------------------------------
@ -140,7 +149,9 @@ The application object is simply a callable object that accepts
two arguments. The term "object" should not be misconstrued as
requiring an actual object instance: a function, method, class,
or instance with a ``__call__`` method are all acceptable for
use as an application object.
use as an application object. Application objects must be able
to be invoked more than once, as virtually all servers/gateways
(other than CGI) will make such repeated requests.
(Note: although we refer to it as an "application" object, this
should not be construed to mean that application developers will use
@ -186,15 +197,6 @@ other is a class::
self.start(status, headers)
yield "Hello world!\n"
Throughout this specification, we will use the term "a callable" to
mean "a function, method, class, or an instance with a ``__call__``
method". It is up to the server, gateway, or application implementing
the callable to choose the appropriate implementation technique for
their needs. Conversely, a server, gateway, or application that is
invoking a callable must *not* have any dependency on what kind of
callable was provided to it. Callables are only to be called, not
introspected upon.
The Server/Gateway Side
-----------------------
@ -311,30 +313,54 @@ Here is a (tongue-in-cheek) example of a middleware component that
converts ``text/plain`` responses to pig latin, using Joe Strout's
``piglatin.py``. (Note: a "real" middleware component would
probably use a more robust way of checking the content type, and
should also check for a content encoding.)
should also check for a content encoding. Also, this simple
example ignores the possibility that a word might be split across
a block boundary.)
::
from piglatin import piglatin
class LatinIter:
"""Transform iterated output to piglatin, if it's okay to do so
Note that the "okayness" can change until the application yields
its first non-empty string, so 'transform_ok' has to be a mutable
truth value."""
def __init__(self,result,transform_ok):
if hasattr(result,'close'):
self.close = result.close
self._next = iter(result).next
self.transform_ok = transform_ok
def __iter__(self):
return self
def next(self):
if self.transform_ok:
return piglatin(self._next())
else:
return self._next()
class Latinator:
# by default, don't transform output
transform = str
transform = False
def __init__(self, application):
self.application = application
def __call__(environ, start_response):
def write_latin(data):
self.write(self.transform(data))
transform_ok = []
def start_latin(status,headers,exc_info=None):
for name,value in headers:
if name.lower()=='content-type' and value=='text/plain':
self.transform = piglatin
transform_ok.append(True)
# Strip content-length if present, else it'll be wrong
headers = [(name,value)
for name,value in headers
@ -342,11 +368,17 @@ should also check for a content encoding.)
]
break
self.write = start_response(status,headers,exc_info)
return write_latin
write = start_response(status,headers,exc_info)
if transform_ok:
def write_latin(data):
write(piglatin(data))
return write_latin
else:
return write
return LatinIter(self.application(environ,start_latin),transform_ok)
for data in self.application(environ,start_latin):
yield self.transform(data)
# Run foo_app under a Latinator's control, using the example CGI gateway
from foo_app import foo_app
@ -672,12 +704,11 @@ using them, and raise an error if they are supplied to
headers, please see the `Other HTTP Features`_ section below.)
The ``start_response`` callable **must not** actually transmit the
HTTP headers. It must store them until the first ``write`` call that
is passed a non-empty string, or until after the first iteration of
the application return value that yields a non-empty string. This is
to ensure that buffered and asynchronous applications can replace
their originally intended output with error output, up until the last
possible moment.
HTTP headers. It must store them until the first ``write`` call, or
until after the first iteration of the application return value that
yields a non-empty string. This is to ensure that buffered and
asynchronous applications can replace their originally intended output
with error output, up until the last possible moment.
The ``exc_info`` argument, if supplied, must be a Python
``sys.exc_info()`` tuple. This argument should be supplied by the
@ -919,13 +950,6 @@ only the lower 8 bits may be used, for any value referred to in
this specification as a "string".
Multiple Invocations
--------------------
Application objects must be able to be invoked more than once, since
virtually all servers/gateways will make such requests.
Error Handling
--------------