Skip to content

Commit

Permalink
Merge pull request #1733 from penguinolog/ClientResponse
Browse files Browse the repository at this point in the history
Client response
  • Loading branch information
fafhrd91 authored Mar 21, 2017
2 parents a70c8e3 + 9114e3f commit 8662357
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Changes

- Content disposition with semicolon in filename #917

- Added `request_info` to response object and `ClientResponseError`.

2.0.1 (2017-03-21)
------------------
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Alexander Travov
Alexandru Mihai
Alexey Firsov
Alexey Popravka
Alexey Stepanov
Alex Key
Alex Khomchenko
Alex Lisovoy
Expand Down
4 changes: 3 additions & 1 deletion aiohttp/client_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ class ClientResponseError(ClientError):
message = ''
headers = None

def __init__(self, *, code=None, message='', headers=None):
def __init__(self, *, code=None, message='', headers=None,
request_info=None):
if code is not None:
self.code = code
self.message = message
self.headers = headers
self.request_info = request_info

super().__init__("%s, message='%s'" % (self.code, message))

Expand Down
19 changes: 16 additions & 3 deletions aiohttp/client_reqrep.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import asyncio
import collections
import io
import json
import sys
Expand Down Expand Up @@ -27,6 +28,9 @@
__all__ = ('ClientRequest', 'ClientResponse')


RequestInfo = collections.namedtuple('RequestInfo', ('url', 'headers'))


class ClientRequest:

GET_METHODS = {hdrs.METH_GET, hdrs.METH_HEAD, hdrs.METH_OPTIONS}
Expand Down Expand Up @@ -385,7 +389,9 @@ def send(self, conn):

self.response = self.response_class(
self.method, self.original_url,
writer=self._writer, continue100=self._continue, timer=self._timer)
writer=self._writer, continue100=self._continue, timer=self._timer,
request_info=RequestInfo(self.url, self.headers)
)

self.response._post_init(self.loop)
return self.response
Expand Down Expand Up @@ -426,7 +432,8 @@ class ClientResponse(HeadersMixin):
_closed = True # to allow __del__ for non-initialized properly response

def __init__(self, method, url, *,
writer=None, continue100=None, timer=None):
writer=None, continue100=None, timer=None,
request_info=None):
assert isinstance(url, URL)

self.method = method
Expand All @@ -439,6 +446,7 @@ def __init__(self, method, url, *,
self._continue = continue100
self._closed = True
self._history = ()
self._request_info = request_info
self._timer = timer if timer is not None else TimerNoop()

@property
Expand All @@ -459,6 +467,10 @@ def host(self):
def _headers(self):
return self.headers

@property
def request_info(self):
return self._request_info

def _post_init(self, loop):
self._loop = loop
if loop.get_debug():
Expand Down Expand Up @@ -609,7 +621,8 @@ def raise_for_status(self):
raise ClientResponseError(
code=self.status,
message=self.reason,
headers=self.headers)
headers=self.headers,
request_info=self.request_info)

def _cleanup_writer(self):
if self._writer is not None and not self._writer.done():
Expand Down
7 changes: 7 additions & 0 deletions docs/client.rst
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,13 @@ It is not possible to use :meth:`~ClientResponse.read`,
:meth:`~ClientResponse.json` and :meth:`~ClientResponse.text` after
explicit reading from :attr:`~ClientResponse.content`.

RequestInfo
-----------

`ClientResponse` object contains :attr:`~ClientResponse.request_info` property,
which contains request fields: `url` and `headers`.
On `raise_for_status` structure is copied to `ClientResponseError` instance.


Custom Headers
--------------
Expand Down
5 changes: 5 additions & 0 deletions docs/client_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,11 @@ Response object
:return: *BODY* as *JSON* data parsed by *loads* parameter or
``None`` if *BODY* is empty or contains white-spaces only.

.. attribute:: request_info

A namedtuple with request URL and headers from :class:`ClientRequest`
object.


ClientWebSocketResponse
-----------------------
Expand Down
42 changes: 41 additions & 1 deletion tests/test_client_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import aiohttp
from aiohttp import helpers
from aiohttp.client_reqrep import ClientResponse
from aiohttp.client_reqrep import ClientResponse, RequestInfo


def test_del():
Expand Down Expand Up @@ -425,3 +425,43 @@ def test_charset_no_charset():
response.headers = {'Content-Type': 'application/json'}

assert response.charset is None


def test_response_request_info():
url = 'http://def-cl-resp.org'
headers = {'Content-Type': 'application/json;charset=cp1251'}
response = ClientResponse(
'get', URL(url),
request_info=RequestInfo(
url,
headers
)
)
assert url == response.request_info.url
assert headers == response.request_info.headers


def test_response_request_info_empty():
url = 'http://def-cl-resp.org'
response = ClientResponse(
'get', URL(url),
)
assert response.request_info is None


def test_request_info_in_exception():
url = 'http://def-cl-resp.org'
headers = {'Content-Type': 'application/json;charset=cp1251'}
response = ClientResponse(
'get',
URL(url),
request_info=RequestInfo(
url,
headers
)
)
response.status = 409
response.reason = 'CONFLICT'
with pytest.raises(aiohttp.ClientResponseError) as cm:
response.raise_for_status()
assert cm.value.request_info == response.request_info

0 comments on commit 8662357

Please sign in to comment.