On Sat, Jun 19, 2010 at 12:11:39AM +0200, Marcel Hellkamp wrote:
> > So, much more simple and readable, and with only one environ lookup:
> >
> > # Cast Files into iterables
> > if hasattr(out, 'read'):
> > wrapper = request.environ.get('wsgi.file_wrapper', None)
> > if wrapper: out = wrapper(out)
> > return out
>
> The 'wsgi.file_wrapper' is optional and we must return an iterable. Some
> file-like objects may not be iterable. A working fallback won't hurt :)
I see, but...
> --- a/bottle.py
> +++ b/bottle.py
> @@ -508,8 +508,8 @@ class Bottle(object):
>
> # File-like objects. Wrap or transfer in chunks that fit into memory.
> if hasattr(out, 'read'):
> - out = request.environ.get('wsgi.file_wrapper',
> - lambda x, y: iter(lambda: x.read(y), tob('')))(out,
> 1024*64)
> + return request.environ.get('wsgi.file_wrapper',
> + lambda x: iter(lambda: x.read(1024*64), tob('')))(out)
>
> # Handle Iterables. We peek into them to detect their inner type.
> try:
...in this case we still lose the .close() method in case
wsgi.file_wrapper is not present in the environment (which, if I
observed correctly, is the case at least with paste and wsgiref).
I, for one, rely on that close() behaviour a lot, since I'm streaming
the output of a command line tool and at the end of streaming I need to
wait() for the tool to end or kill it if close() is called before it has
finished (such as if the client closes connection prematurely).
The only way I can see to fix it is to create a proper wrapper which
also proxies the close() method, something like this (uglyish,
untested):
# Cast Files into iterables
if hasattr(out, 'read'):
wrapper = request.environ.get('wsgi.file_wrapper', None)
if wrapper:
out = wrapper(out)
elif not hasattr(out, "__iter__"):
class FilelikeToIterable(object):
def __init__(self, fd)
self.__orig = fd
def read(self, *args):
return self.__orig.read(*args)
def __iter__(self):
return self
def __next__(self):
res = self.__orig.read(1024*64)
if not res: raise StopIteration()
return res
def close(self):
if hasattr(self.__orig, "close"):
self.__orig.close()
out = FilelikeToIterable(out)
return out
Alternatively, one could document that if a file-like object is
returned, it must be file-like enough to be iterable. I'd assume there
are not many non-iterable file-like objects in use out there, although
I know I can't know the bottle userbase as well as you do.
Ciao,
Enrico
--
GPG key: 4096R/E7AD5568 2009-05-08 Enrico Zini <[email protected]>
signature.asc
Description: Digital signature

