/usr/share/pyshared/zope/publisher/httpresults.txt is in python-zope.publisher 3.12.6-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | Creating HTTP Results
=====================
This document describes the state of creating HTTP results for Zope
3.4. This is different than it was in the past.
Traditionally in Zope, HTTP results are created by simply returning
strings. Strings are inspected to deduce their content type, which is
usually HTML. Applications can override this by setting a response
headers (calling request.response.setHeader).
In Zope 2, applications could also call response.write. This allows
both:
- Effecient handling of large output
- HTTP chucked output for streaming
Before release 3.1, Zope 3 has a response write method that did
neither of these things. Developers coming from Zope 2 might use the
write method, expecting it to have the same bahavior as it does in
Zope 2. At least until we can satisfy those expectations, we have
disabled the response write method for now. Maybe we'll reinstate it
in the future.
There is currently no support for streaming (at least while holding on
to a database connection and transaction), but there is now support for
returning large amounts of data.
Returning large amounts of data without storing the data in memory
------------------------------------------------------------------
To return a large result, you should write the result to a temporary
file (tempfile.TemporaryFile) and return the temporary file.
Alternatively, if the data you want to return is already in a
(non-temporary) file, just open and return that file. The publisher
(actually an adapter used by the publisher) will handle a returned
file very efficiently.
The publisher will compute the response content length from the file
automatically. It is up to applications to set the content type.
It will also take care of positioning the file to it's beginning,
so applications don't need to do this beforehand.
This is actually accomplished via zope.app.wsgi.fileresult.FileResult,
and happens if and only if that, or something like it, is registered as
an adapter. The FileResult, however, does what needs to happen thanks
to a special hook associated with the IResult interface, used by the
http module in this package.
IResult
-------
The interface for IResult, found in zope/publisher/interfaces/http.py,
describes the interface thoroughly. The IHTTPResponse.setHeader method
that uses it also documents how it is used. Reading the IResult
interface and the IHTTPResponse.setHeader description (in the same
interface file) is highly recommended.
In addition to supporting sending large amoounts of data, IResult
supports postprocessing of output. setResult tries to adapt everything
to IResult. Postprocessing might include XSLT transforms, adding an
O-wrap around the content, adding JavaScript and CSS header lines on the
basis of elements added to a page, or pipelining somehow to do all of it
sequentially. May the best approach win! This merely makes the
different options possible.
To close, we'll build a quick example so you can see it working.
>>> import zope.interface
>>> import zope.component
>>> from zope.publisher.browser import TestRequest
>>> from zope.publisher.interfaces.http import IResult, IHTTPRequest
>>> import cgi
>>> @zope.interface.implementer(IResult)
... @zope.component.adapter(unicode, IHTTPRequest)
... def do_something_silly_to_unicode_results(val, request):
... request.response.setHeader('X-Silly', 'Yes')
... return (u'<html>\n<head>\n<title>raw</title>\n</head>\n<body>\n' +
... cgi.escape(val) + '\n</body>\n</html>')
...
>>> zope.component.provideAdapter(do_something_silly_to_unicode_results)
That's returning a unicode string, which is special cased to (1) make an
iterable that is chunked, (2) encode, and (3) set content-length.
>>> request = TestRequest()
>>> request.response.setHeader('content-type', 'text/html')
>>> request.response.setResult(u'<h1>Foo!</h1>')
>>> request.response.getHeader('x-silly')
'Yes'
>>> request.response.getHeader('content-type')
'text/html;charset=utf-8'
>>> res = tuple(request.response.consumeBodyIter())
>>> res
('<html>\n<head>\n<title>raw</title>\n</head>\n<body>\n<h1>Foo!</h1>\n</body>\n</html>',)
>>> len(res[0]) == int(request.response.getHeader('content-length'))
True
You can also do everything yourself by returning any non-basestring iterable
(for instance, a list or tuple).
>>> @zope.interface.implementer(IResult)
... @zope.component.adapter(int, IHTTPRequest)
... def do_something_silly_to_int_results(val, request):
... return ['This', ' is an int: %i' % (val,),]
...
>>> zope.component.provideAdapter(do_something_silly_to_int_results)
>>> request = TestRequest()
>>> request.response.setHeader('content-type', 'text/plain')
>>> request.response.setResult(42)
>>> request.response.getHeader('content-type')
'text/plain'
>>> res = tuple(request.response.consumeBodyIter())
>>> res
('This', ' is an int: 42')
>>> request.response.getHeader('content-length') is None
True
Again, READ THE INTERFACES. One important bit is that you can't hold on to
a database connection in one of these iterables.
You can bypass the adaptation by calling `setResult` with an object that
provides IResult. The ``DirectResult`` class in the http module is the
simplest way to do this, but any other IResult should work.
>>> from zope.publisher.http import DirectResult
>>> @zope.interface.implementer(IResult)
... @zope.component.adapter(DirectResult, IHTTPRequest)
... def dont_touch_this(val, request):
... raise ValueError('boo! hiss!') # we don't get here.
...
>>> request = TestRequest()
>>> request.response.setResult(DirectResult(('hi',)))
>>> tuple(request.response.consumeBodyIter())
('hi',)
|