Skip to content

Commit 6999e5a

Browse files
committed
Merge pull request #250 from mind1master/master
Memorising of web.Request's read, text, json
2 parents 45157ec + 5fe2460 commit 6999e5a

File tree

4 files changed

+48
-18
lines changed

4 files changed

+48
-18
lines changed

aiohttp/web.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import json
1010
import re
1111
import os
12+
import warnings
1213

1314
from urllib.parse import urlsplit, parse_qsl, urlencode, unquote
1415
from types import MappingProxyType
@@ -172,6 +173,8 @@ def __init__(self, app, message, payload, transport, reader, writer, *,
172173
self._payload = payload
173174
self._cookies = None
174175

176+
self._read_bytes = None
177+
175178
@property
176179
def method(self):
177180
"""Read only property for getting HTTP method.
@@ -281,6 +284,12 @@ def cookies(self):
281284

282285
@property
283286
def payload(self):
287+
"""Return raw payload stream."""
288+
warnings.warn('use Request.content instead', DeprecationWarning)
289+
return self._payload
290+
291+
@property
292+
def content(self):
284293
"""Return raw payload stream."""
285294
return self._payload
286295

@@ -300,13 +309,15 @@ def read(self):
300309
301310
Returns bytes object with full request content.
302311
"""
303-
body = bytearray()
304-
while True:
305-
chunk = yield from self._payload.readany()
306-
body.extend(chunk)
307-
if chunk is EOF_MARKER:
308-
break
309-
return bytes(body)
312+
if self._read_bytes is None:
313+
body = bytearray()
314+
while True:
315+
chunk = yield from self._payload.readany()
316+
body.extend(chunk)
317+
if chunk is EOF_MARKER:
318+
break
319+
self._read_bytes = bytes(body)
320+
return self._read_bytes
310321

311322
@asyncio.coroutine
312323
def text(self):

docs/web_reference.rst

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,26 @@ first positional parameter.
131131

132132
Read-only :class:`~aiohttp.multidict.MultiDictProxy` lazy property.
133133

134+
.. attribute:: content
135+
136+
A :class:`~aiohttp.streams.FlowControlStreamReader` instance,
137+
input stream for reading request's *BODY*.
138+
139+
Read-only property.
140+
141+
.. versionadded:: 0.15
142+
134143
.. attribute:: payload
135144

136145
A :class:`~aiohttp.streams.FlowControlStreamReader` instance,
137146
input stream for reading request's *BODY*.
138147

139148
Read-only property.
140149

150+
.. deprecated:: 0.15
151+
152+
Use :attr:`~Request.content` instead.
153+
141154
.. attribute:: content_type
142155

143156
Read-only property with *content* part of *Content-Type* header.
@@ -173,10 +186,10 @@ first positional parameter.
173186

174187
The method is a :ref:`coroutine <coroutine>`.
175188

176-
.. warning::
189+
.. note::
177190

178-
The method doesn't store read data internally, subsequent
179-
:meth:`~Request.read` call will return empty bytes ``b''``.
191+
The method **does** store read data internally, subsequent
192+
:meth:`~Request.read` call will return the same value.
180193

181194
.. method:: text()
182195

@@ -187,10 +200,10 @@ first positional parameter.
187200

188201
The method is a :ref:`coroutine <coroutine>`.
189202

190-
.. warning::
203+
.. note::
191204

192-
The method doesn't store read data internally, subsequent
193-
:meth:`~Request.text` call will return empty string ``''``.
205+
The method **does** store read data internally, subsequent
206+
:meth:`~Request.text` call will return the same value.
194207

195208
.. method:: json(*, loader=json.loads)
196209

@@ -208,10 +221,10 @@ first positional parameter.
208221
and returns :class:`dict` with parsed
209222
JSON (:func:`json.loads` by default).
210223

211-
.. warning::
224+
.. note::
212225

213-
The method doesn't store read data internally, subsequent
214-
:meth:`~Request.json` call will raise an exception.
226+
The method **does** store read data internally, subsequent
227+
:meth:`~Request.json` call will return the same value.
215228

216229
.. method:: post()
217230

@@ -226,7 +239,7 @@ first positional parameter.
226239
*application/x-www-form-urlencoded* or *multipart/form-data*
227240
returns empty multidict.
228241

229-
.. warning::
242+
.. note::
230243

231244
The method **does** store read data internally, subsequent
232245
:meth:`~Request.post` call will return the same value.

tests/test_web_functional.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ def test_post_text(self):
8080
def handler(request):
8181
data = yield from request.text()
8282
self.assertEqual('русский', data)
83+
data2 = yield from request.text()
84+
self.assertEqual(data, data2)
8385
return web.Response(text=data)
8486

8587
@asyncio.coroutine
@@ -101,6 +103,8 @@ def test_post_json(self):
101103
def handler(request):
102104
data = yield from request.json()
103105
self.assertEqual(dct, data)
106+
data2 = yield from request.json()
107+
self.assertEqual(data, data2)
104108
resp = web.Response()
105109
resp.content_type = 'application/json'
106110
resp.body = json.dumps(data).encode('utf8')

tests/test_web_request.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ def test_ctor(self):
4545
# second call should return the same object
4646
self.assertIs(get, req.GET)
4747

48-
self.assertIs(self.payload, req.payload)
48+
with self.assertWarns(DeprecationWarning):
49+
self.assertIs(self.payload, req.payload)
50+
self.assertIs(self.payload, req.content)
4951
self.assertIs(self.transport, req.transport)
5052
self.assertTrue(req.keep_alive)
5153

0 commit comments

Comments
 (0)