File results
============

The file results adapters provide adapters from Python file objects
to and from temporary file objects to zope.publisher.http.IResult. They also have
the property that they can handle security proxied files and unproxy
them in the result. Finally, if the request has a wsgi.file_wrapper
environment variable, then that is used to wrap the file in the
result.

Lets look at an example with a regular file object:

    >>> from zope import component
    >>> import zope.app.wsgi.fileresult
    >>> component.provideAdapter(zope.app.wsgi.fileresult.FileResult)
    >>> component.provideAdapter(zope.app.wsgi.fileresult.TemporaryFileResult)

    >>> import tempfile
    >>> dir = tempfile.mkdtemp()
    >>> import os
    >>> f = open(os.path.join(dir, 'f'), 'w+b')
    >>> f.write('One\nTwo\nThree\nHa ha! I love to count!\n')
    >>> from zope.security.checker import ProxyFactory
    >>> from zope.publisher.http import IResult
    >>> from zope.publisher.browser import TestRequest
    >>> request = TestRequest()
    >>> result = component.getMultiAdapter((ProxyFactory(f), request), IResult)
    >>> for line in result.body:
    ...     print line
    One
    Two
    Three
    Ha ha! I love to count!

    >>> result.headers
    (('Content-Length', '38'),)
    

We'll see something similar with a temporary file:

    >>> t = tempfile.TemporaryFile()
    >>> t.write('Three\nTwo\nOne\nHa ha! I love to count down!\n')
    >>> result = component.getMultiAdapter((ProxyFactory(t), request), IResult)
    >>> for line in result.body:
    ...     print line
    Three
    Two
    One
    Ha ha! I love to count down!

    >>> result.headers
    (('Content-Length', '43'),)

        
If we provide a custom file wrapper:

    >>> class Wrapper:
    ...     def __init__(self, file):
    ...         self.file = file
 
    >>> request = TestRequest(environ={'wsgi.file_wrapper': Wrapper})
    >>> result = component.getMultiAdapter((ProxyFactory(f), request), IResult)
    >>> result.body.__class__ is Wrapper
    True
    >>> result.body.file is f
    True

    >>> result.headers
    (('Content-Length', '38'),)

    >>> result = component.getMultiAdapter((ProxyFactory(t), request), IResult)
    >>> result.body.__class__ is Wrapper
    True
    >>> result.body.file is t
    True

    >>> result.headers
    (('Content-Length', '43'),)

Normally, the file given to FileResult must be seekable and the entire
file is used.  The adapters figure out the file size to determine a
content length and seek to the beginning of the file.

You can suppress this behavior by setting the content length yourself:

    >>> request = TestRequest()
    >>> request.response.setHeader('content-length', '10')
    >>> f.seek(7)
    >>> result = component.getMultiAdapter((ProxyFactory(t), request), IResult)
    >>> print f.tell()
    7

    >>> result.headers
    ()

Note, that you should really only use file returns for large results.
Files use file descriptors which can be somewhat scarece resources on
some systems.  Only use them when you needs them.
