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 <enr...@enricozini.org>
signature.asc
Description: Digital signature