Skip to content

Commit 53141f3

Browse files
committed
Fix #1985: Keep a reference to ClientSession in response object
1 parent 22c8937 commit 53141f3

6 files changed

+65
-62
lines changed

CHANGES.rst

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ Changes
3636

3737
- Fix BadStatusLine caused by extra `CRLF` after `POST` data #1792
3838

39+
- Keep a reference to ClientSession in response object #1985
40+
3941

4042
2.1.0 (2017-05-26)
4143
------------------

aiohttp/client.py

+2-8
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,8 @@ def _request(self, method, url, *,
222222
compress=compress, chunked=chunked,
223223
expect100=expect100, loop=self._loop,
224224
response_class=self._response_class,
225-
proxy=proxy, proxy_auth=proxy_auth, timer=timer)
225+
proxy=proxy, proxy_auth=proxy_auth, timer=timer,
226+
session=self)
226227

227228
# connection timeout
228229
try:
@@ -688,13 +689,6 @@ def __await__(self):
688689
self._session.close()
689690
raise
690691

691-
def __del__(self):
692-
# in case of "resp = aiohttp.request(...)"
693-
# _SessionRequestContextManager get destroyed before resp get processed
694-
# and connection has to stay alive during this time
695-
# ClientSession.detach just cleans up connector attribute
696-
self._session.detach()
697-
698692

699693
def request(method, url, *,
700694
params=None,

aiohttp/client_reqrep.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,14 @@ def __init__(self, method, url, *,
6666
chunked=None, expect100=False,
6767
loop=None, response_class=None,
6868
proxy=None, proxy_auth=None, proxy_from_env=False,
69-
timer=None):
69+
timer=None, session=None):
7070

7171
if loop is None:
7272
loop = asyncio.get_event_loop()
7373

7474
assert isinstance(url, URL), url
7575
assert isinstance(proxy, (URL, type(None))), proxy
76-
76+
self._session = session
7777
if params:
7878
q = MultiDict(url.query)
7979
url2 = url.with_query(params)
@@ -409,7 +409,7 @@ def send(self, conn):
409409
request_info=self.request_info
410410
)
411411

412-
self.response._post_init(self.loop)
412+
self.response._post_init(self.loop, self._session)
413413
return self.response
414414

415415
@asyncio.coroutine
@@ -446,6 +446,7 @@ class ClientResponse(HeadersMixin):
446446
# post-init stage allows to not change ctor signature
447447
_loop = None
448448
_closed = True # to allow __del__ for non-initialized properly response
449+
_session = None
449450

450451
def __init__(self, method, url, *,
451452
writer=None, continue100=None, timer=None,
@@ -487,8 +488,9 @@ def _headers(self):
487488
def request_info(self):
488489
return self._request_info
489490

490-
def _post_init(self, loop):
491+
def _post_init(self, loop, session):
491492
self._loop = loop
493+
self._session = session # store a reference to session #1985
492494
if loop.get_debug():
493495
self._source_traceback = traceback.extract_stack(sys._getframe(1))
494496

tests/test_client_proto.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def test_client_protocol_readuntil_eof(loop):
7272
proto.data_received(b'HTTP/1.1 200 Ok\r\n\r\n')
7373

7474
response = ClientResponse('get', URL('http://def-cl-resp.org'))
75-
response._post_init(loop)
75+
response._post_init(loop, mock.Mock())
7676
yield from response.start(conn, read_until_eof=True)
7777

7878
assert not response.content.is_eof()

tests/test_client_request.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1104,7 +1104,7 @@ def send(self, conn):
11041104
self.url,
11051105
writer=self._writer,
11061106
continue100=self._continue)
1107-
resp._post_init(self.loop)
1107+
resp._post_init(self.loop, mock.Mock())
11081108
self.response = resp
11091109
nonlocal called
11101110
called = True

tests/test_client_response.py

+53-48
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,18 @@
1313
from aiohttp.client_reqrep import ClientResponse, RequestInfo
1414

1515

16+
@pytest.fixture
17+
def session():
18+
return mock.Mock()
19+
20+
1621
@asyncio.coroutine
17-
def test_http_processing_error():
22+
def test_http_processing_error(session):
1823
loop = mock.Mock()
1924
request_info = mock.Mock()
2025
response = ClientResponse(
2126
'get', URL('http://del-cl-resp.org'), request_info=request_info)
22-
response._post_init(loop)
27+
response._post_init(loop, session)
2328
loop.get_debug = mock.Mock()
2429
loop.get_debug.return_value = True
2530

@@ -34,10 +39,10 @@ def test_http_processing_error():
3439
assert info.value.request_info is request_info
3540

3641

37-
def test_del():
42+
def test_del(session):
3843
loop = mock.Mock()
3944
response = ClientResponse('get', URL('http://del-cl-resp.org'))
40-
response._post_init(loop)
45+
response._post_init(loop, session)
4146
loop.get_debug = mock.Mock()
4247
loop.get_debug.return_value = True
4348

@@ -53,9 +58,9 @@ def test_del():
5358
connection.release.assert_called_with()
5459

5560

56-
def test_close(loop):
61+
def test_close(loop, session):
5762
response = ClientResponse('get', URL('http://def-cl-resp.org'))
58-
response._post_init(loop)
63+
response._post_init(loop, session)
5964
response._closed = False
6065
response._connection = mock.Mock()
6166
response.close()
@@ -64,25 +69,25 @@ def test_close(loop):
6469
response.close()
6570

6671

67-
def test_wait_for_100_1(loop):
72+
def test_wait_for_100_1(loop, session):
6873
response = ClientResponse(
6974
'get', URL('http://python.org'), continue100=object())
70-
response._post_init(loop)
75+
response._post_init(loop, session)
7176
assert response._continue is not None
7277
response.close()
7378

7479

75-
def test_wait_for_100_2(loop):
80+
def test_wait_for_100_2(loop, session):
7681
response = ClientResponse(
7782
'get', URL('http://python.org'))
78-
response._post_init(loop)
83+
response._post_init(loop, session)
7984
assert response._continue is None
8085
response.close()
8186

8287

83-
def test_repr(loop):
88+
def test_repr(loop, session):
8489
response = ClientResponse('get', URL('http://def-cl-resp.org'))
85-
response._post_init(loop)
90+
response._post_init(loop, session)
8691
response.status = 200
8792
response.reason = 'Ok'
8893
assert '<ClientResponse(http://def-cl-resp.org) [200 Ok]>'\
@@ -109,9 +114,9 @@ def test_url_obj_deprecated():
109114

110115

111116
@asyncio.coroutine
112-
def test_read_and_release_connection(loop):
117+
def test_read_and_release_connection(loop, session):
113118
response = ClientResponse('get', URL('http://def-cl-resp.org'))
114-
response._post_init(loop)
119+
response._post_init(loop, session)
115120

116121
def side_effect(*args, **kwargs):
117122
fut = helpers.create_future(loop)
@@ -126,9 +131,9 @@ def side_effect(*args, **kwargs):
126131

127132

128133
@asyncio.coroutine
129-
def test_read_and_release_connection_with_error(loop):
134+
def test_read_and_release_connection_with_error(loop, session):
130135
response = ClientResponse('get', URL('http://def-cl-resp.org'))
131-
response._post_init(loop)
136+
response._post_init(loop, session)
132137
content = response.content = mock.Mock()
133138
content.read.return_value = helpers.create_future(loop)
134139
content.read.return_value.set_exception(ValueError)
@@ -139,9 +144,9 @@ def test_read_and_release_connection_with_error(loop):
139144

140145

141146
@asyncio.coroutine
142-
def test_release(loop):
147+
def test_release(loop, session):
143148
response = ClientResponse('get', URL('http://def-cl-resp.org'))
144-
response._post_init(loop)
149+
response._post_init(loop, session)
145150
fut = helpers.create_future(loop)
146151
fut.set_result(b'')
147152
content = response.content = mock.Mock()
@@ -152,13 +157,13 @@ def test_release(loop):
152157

153158

154159
@asyncio.coroutine
155-
def test_release_on_del(loop):
160+
def test_release_on_del(loop, session):
156161
connection = mock.Mock()
157162
connection.protocol.upgraded = False
158163

159164
def run(conn):
160165
response = ClientResponse('get', URL('http://def-cl-resp.org'))
161-
response._post_init(loop)
166+
response._post_init(loop, session)
162167
response._closed = False
163168
response._connection = conn
164169

@@ -168,9 +173,9 @@ def run(conn):
168173

169174

170175
@asyncio.coroutine
171-
def test_response_eof(loop):
176+
def test_response_eof(loop, session):
172177
response = ClientResponse('get', URL('http://def-cl-resp.org'))
173-
response._post_init(loop)
178+
response._post_init(loop, session)
174179
response._closed = False
175180
conn = response._connection = mock.Mock()
176181
conn.protocol.upgraded = False
@@ -181,9 +186,9 @@ def test_response_eof(loop):
181186

182187

183188
@asyncio.coroutine
184-
def test_response_eof_upgraded(loop):
189+
def test_response_eof_upgraded(loop, session):
185190
response = ClientResponse('get', URL('http://def-cl-resp.org'))
186-
response._post_init(loop)
191+
response._post_init(loop, session)
187192

188193
conn = response._connection = mock.Mock()
189194
conn.protocol.upgraded = True
@@ -194,9 +199,9 @@ def test_response_eof_upgraded(loop):
194199

195200

196201
@asyncio.coroutine
197-
def test_response_eof_after_connection_detach(loop):
202+
def test_response_eof_after_connection_detach(loop, session):
198203
response = ClientResponse('get', URL('http://def-cl-resp.org'))
199-
response._post_init(loop)
204+
response._post_init(loop, session)
200205
response._closed = False
201206
conn = response._connection = mock.Mock()
202207
conn.protocol = None
@@ -207,9 +212,9 @@ def test_response_eof_after_connection_detach(loop):
207212

208213

209214
@asyncio.coroutine
210-
def test_text(loop):
215+
def test_text(loop, session):
211216
response = ClientResponse('get', URL('http://def-cl-resp.org'))
212-
response._post_init(loop)
217+
response._post_init(loop, session)
213218

214219
def side_effect(*args, **kwargs):
215220
fut = helpers.create_future(loop)
@@ -227,9 +232,9 @@ def side_effect(*args, **kwargs):
227232

228233

229234
@asyncio.coroutine
230-
def test_text_bad_encoding(loop):
235+
def test_text_bad_encoding(loop, session):
231236
response = ClientResponse('get', URL('http://def-cl-resp.org'))
232-
response._post_init(loop)
237+
response._post_init(loop, session)
233238

234239
def side_effect(*args, **kwargs):
235240
fut = helpers.create_future(loop)
@@ -250,9 +255,9 @@ def side_effect(*args, **kwargs):
250255

251256

252257
@asyncio.coroutine
253-
def test_text_custom_encoding(loop):
258+
def test_text_custom_encoding(loop, session):
254259
response = ClientResponse('get', URL('http://def-cl-resp.org'))
255-
response._post_init(loop)
260+
response._post_init(loop, session)
256261

257262
def side_effect(*args, **kwargs):
258263
fut = helpers.create_future(loop)
@@ -272,9 +277,9 @@ def side_effect(*args, **kwargs):
272277

273278

274279
@asyncio.coroutine
275-
def test_text_detect_encoding(loop):
280+
def test_text_detect_encoding(loop, session):
276281
response = ClientResponse('get', URL('http://def-cl-resp.org'))
277-
response._post_init(loop)
282+
response._post_init(loop, session)
278283

279284
def side_effect(*args, **kwargs):
280285
fut = helpers.create_future(loop)
@@ -292,9 +297,9 @@ def side_effect(*args, **kwargs):
292297

293298

294299
@asyncio.coroutine
295-
def test_text_after_read(loop):
300+
def test_text_after_read(loop, session):
296301
response = ClientResponse('get', URL('http://def-cl-resp.org'))
297-
response._post_init(loop)
302+
response._post_init(loop, session)
298303

299304
def side_effect(*args, **kwargs):
300305
fut = helpers.create_future(loop)
@@ -312,9 +317,9 @@ def side_effect(*args, **kwargs):
312317

313318

314319
@asyncio.coroutine
315-
def test_json(loop):
320+
def test_json(loop, session):
316321
response = ClientResponse('get', URL('http://def-cl-resp.org'))
317-
response._post_init(loop)
322+
response._post_init(loop, session)
318323

319324
def side_effect(*args, **kwargs):
320325
fut = helpers.create_future(loop)
@@ -332,9 +337,9 @@ def side_effect(*args, **kwargs):
332337

333338

334339
@asyncio.coroutine
335-
def test_json_custom_loader(loop):
340+
def test_json_custom_loader(loop, session):
336341
response = ClientResponse('get', URL('http://def-cl-resp.org'))
337-
response._post_init(loop)
342+
response._post_init(loop, session)
338343
response.headers = {
339344
'Content-Type': 'application/json;charset=cp1251'}
340345
response._content = b'data'
@@ -347,9 +352,9 @@ def custom(content):
347352

348353

349354
@asyncio.coroutine
350-
def test_json_no_content(loop):
355+
def test_json_no_content(loop, session):
351356
response = ClientResponse('get', URL('http://def-cl-resp.org'))
352-
response._post_init(loop)
357+
response._post_init(loop, session)
353358
response.headers = {
354359
'Content-Type': 'data/octet-stream'}
355360
response._content = b''
@@ -364,9 +369,9 @@ def test_json_no_content(loop):
364369

365370

366371
@asyncio.coroutine
367-
def test_json_override_encoding(loop):
372+
def test_json_override_encoding(loop, session):
368373
response = ClientResponse('get', URL('http://def-cl-resp.org'))
369-
response._post_init(loop)
374+
response._post_init(loop, session)
370375

371376
def side_effect(*args, **kwargs):
372377
fut = helpers.create_future(loop)
@@ -386,19 +391,19 @@ def side_effect(*args, **kwargs):
386391

387392

388393
@pytest.mark.xfail
389-
def test_override_flow_control(loop):
394+
def test_override_flow_control(loop, session):
390395
class MyResponse(ClientResponse):
391396
flow_control_class = aiohttp.StreamReader
392397
response = MyResponse('get', URL('http://my-cl-resp.org'))
393-
response._post_init(loop)
398+
response._post_init(loop, session)
394399
response._connection = mock.Mock()
395400
assert isinstance(response.content, aiohttp.StreamReader)
396401
response.close()
397402

398403

399-
def test_get_encoding_unknown(loop):
404+
def test_get_encoding_unknown(loop, session):
400405
response = ClientResponse('get', URL('http://def-cl-resp.org'))
401-
response._post_init(loop)
406+
response._post_init(loop, session)
402407

403408
response.headers = {'Content-Type': 'application/json'}
404409
with mock.patch('aiohttp.client_reqrep.chardet') as m_chardet:

0 commit comments

Comments
 (0)