Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/pyramid/response.py : 42%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import mimetypes
2from os.path import getmtime, getsize
4import venusian
6from webob import Response as _Response
7from zope.interface import implementer
8from pyramid.interfaces import IResponse, IResponseFactory
11def init_mimetypes(mimetypes):
12 # this is a function so it can be unittested
13 if hasattr(mimetypes, 'init'):
14 mimetypes.init()
15 return True
16 return False
19# See http://bugs.python.org/issue5853 which is a recursion bug
20# that seems to effect Python 2.6, Python 2.6.1, and 2.6.2 (a fix
21# has been applied on the Python 2 trunk).
22init_mimetypes(mimetypes)
24_BLOCK_SIZE = 4096 * 64 # 256K
27@implementer(IResponse)
28class Response(_Response):
29 pass
32class FileResponse(Response):
33 """
34 A Response object that can be used to serve a static file from disk
35 simply.
37 ``path`` is a file path on disk.
39 ``request`` must be a Pyramid :term:`request` object. Note
40 that a request *must* be passed if the response is meant to attempt to
41 use the ``wsgi.file_wrapper`` feature of the web server that you're using
42 to serve your Pyramid application.
44 ``cache_max_age`` is the number of seconds that should be used
45 to HTTP cache this response.
47 ``content_type`` is the content_type of the response.
49 ``content_encoding`` is the content_encoding of the response.
50 It's generally safe to leave this set to ``None`` if you're serving a
51 binary file. This argument will be ignored if you also leave
52 ``content-type`` as ``None``.
53 """
55 def __init__(
56 self,
57 path,
58 request=None,
59 cache_max_age=None,
60 content_type=None,
61 content_encoding=None,
62 ):
63 if content_type is None:
64 content_type, content_encoding = _guess_type(path)
65 super(FileResponse, self).__init__(
66 conditional_response=True,
67 content_type=content_type,
68 content_encoding=content_encoding,
69 )
70 self.last_modified = getmtime(path)
71 content_length = getsize(path)
72 f = open(path, 'rb')
73 app_iter = None
74 if request is not None:
75 environ = request.environ
76 if 'wsgi.file_wrapper' in environ:
77 app_iter = environ['wsgi.file_wrapper'](f, _BLOCK_SIZE)
78 if app_iter is None:
79 app_iter = FileIter(f, _BLOCK_SIZE)
80 self.app_iter = app_iter
81 # assignment of content_length must come after assignment of app_iter
82 self.content_length = content_length
83 if cache_max_age is not None:
84 self.cache_expires = cache_max_age
87class FileIter(object):
88 """ A fixed-block-size iterator for use as a WSGI app_iter.
90 ``file`` is a Python file pointer (or at least an object with a ``read``
91 method that takes a size hint).
93 ``block_size`` is an optional block size for iteration.
94 """
96 def __init__(self, file, block_size=_BLOCK_SIZE):
97 self.file = file
98 self.block_size = block_size
100 def __iter__(self):
101 return self
103 def next(self):
104 val = self.file.read(self.block_size)
105 if not val:
106 raise StopIteration
107 return val
109 __next__ = next # py3
111 def close(self):
112 self.file.close()
115class response_adapter(object):
116 """ Decorator activated via a :term:`scan` which treats the function
117 being decorated as a :term:`response adapter` for the set of types or
118 interfaces passed as ``*types_or_ifaces`` to the decorator constructor.
120 For example, if you scan the following response adapter:
122 .. code-block:: python
124 from pyramid.response import Response
125 from pyramid.response import response_adapter
127 @response_adapter(int)
128 def myadapter(i):
129 return Response(status=i)
131 You can then return an integer from your view callables, and it will be
132 converted into a response with the integer as the status code.
134 More than one type or interface can be passed as a constructor argument.
135 The decorated response adapter will be called for each type or interface.
137 .. code-block:: python
139 import json
141 from pyramid.response import Response
142 from pyramid.response import response_adapter
144 @response_adapter(dict, list)
145 def myadapter(ob):
146 return Response(json.dumps(ob))
148 This method will have no effect until a :term:`scan` is performed
149 agains the package or module which contains it, ala:
151 .. code-block:: python
153 from pyramid.config import Configurator
154 config = Configurator()
155 config.scan('somepackage_containing_adapters')
157 Two additional keyword arguments which will be passed to the
158 :term:`venusian` ``attach`` function are ``_depth`` and ``_category``.
160 ``_depth`` is provided for people who wish to reuse this class from another
161 decorator. The default value is ``0`` and should be specified relative to
162 the ``response_adapter`` invocation. It will be passed in to the
163 :term:`venusian` ``attach`` function as the depth of the callstack when
164 Venusian checks if the decorator is being used in a class or module
165 context. It's not often used, but it can be useful in this circumstance.
167 ``_category`` sets the decorator category name. It can be useful in
168 combination with the ``category`` argument of ``scan`` to control which
169 views should be processed.
171 See the :py:func:`venusian.attach` function in Venusian for more
172 information about the ``_depth`` and ``_category`` arguments.
174 .. versionchanged:: 1.9.1
175 Added the ``_depth`` and ``_category`` arguments.
177 """
179 venusian = venusian # for unit testing
181 def __init__(self, *types_or_ifaces, **kwargs):
182 self.types_or_ifaces = types_or_ifaces
183 self.depth = kwargs.pop('_depth', 0)
184 self.category = kwargs.pop('_category', 'pyramid')
185 self.kwargs = kwargs
187 def register(self, scanner, name, wrapped):
188 config = scanner.config
189 for type_or_iface in self.types_or_ifaces:
190 config.add_response_adapter(wrapped, type_or_iface, **self.kwargs)
192 def __call__(self, wrapped):
193 self.venusian.attach(
194 wrapped,
195 self.register,
196 category=self.category,
197 depth=self.depth + 1,
198 )
199 return wrapped
202def _get_response_factory(registry):
203 """ Obtain a :class: `pyramid.response.Response` using the
204 `pyramid.interfaces.IResponseFactory`.
205 """
206 response_factory = registry.queryUtility(
207 IResponseFactory, default=lambda r: Response()
208 )
210 return response_factory
213def _guess_type(path):
214 content_type, content_encoding = mimetypes.guess_type(path, strict=False)
215 if content_type is None:
216 content_type = 'application/octet-stream'
217 # str-ifying content_type is a workaround for a bug in Python 2.7.7
218 # on Windows where mimetypes.guess_type returns unicode for the
219 # content_type.
220 content_type = str(content_type)
221 return content_type, content_encoding