diff options
author | Ask Solem <ask@celeryproject.org> | 2014-02-20 19:25:03 +0000 |
---|---|---|
committer | Ask Solem <ask@celeryproject.org> | 2014-05-03 22:27:17 +0100 |
commit | 19af91d48e08d29cf7dde87f4a0b25a0dd674701 (patch) | |
tree | 8aa12573ab92820881517cd62548fad87b975b4c | |
parent | b5df9fbe27a3350d4e0bb317a2254f0baa8d8bd7 (diff) | |
download | kombu-19af91d48e08d29cf7dde87f4a0b25a0dd674701.tar.gz |
100% coverage for kombu.async.http.base
-rw-r--r-- | kombu/async/http/base.py | 81 | ||||
-rw-r--r-- | kombu/tests/async/test_http.py | 119 |
2 files changed, 163 insertions, 37 deletions
diff --git a/kombu/async/http/base.py b/kombu/async/http/base.py index 4b1c56cc..219f2763 100644 --- a/kombu/async/http/base.py +++ b/kombu/async/http/base.py @@ -83,12 +83,11 @@ class Request(object): """ - url = headers = body = user_agent = network_interface = \ - on_stream = on_timeout = on_ready = on_header = on_prepare = \ + body = user_agent = network_interface = \ + on_stream = on_timeout = on_header = on_prepare = \ proxy_host = proxy_port = proxy_username = proxy_password = \ ca_certs = client_key = client_cert = None - method = 'GET' connect_timeout = 30.0 request_timeout = 30.0 follow_redirects = True @@ -96,18 +95,20 @@ class Request(object): use_gzip = True validate_cert = True - if PYPY: - __slots__ = ('__weakref__', ) + if not PYPY: # pragma: no cover + __slots__ = ('url', 'method', 'on_ready', 'headers', + '__weakref__', '__dict__') - def __init__(self, url, method='GET', on_ready=None, **kwargs): + def __init__(self, url, method='GET', on_ready=None, headers=None, **kwargs): self.url = url self.method = method or self.method self.on_ready = on_ready or promise() if kwargs: for k, v in items(kwargs): setattr(self, k, v) - if not isinstance(self.headers, Headers): - self.headers = Headers(self.headers or {}) + if not isinstance(headers, Headers): + headers = Headers(headers or {}) + self.headers = headers def then(self, callback, errback=None): self.on_ready.then(callback, errback) @@ -123,31 +124,40 @@ class Response(object): :keyword effective_url: See :attr:`effective_url`. :keyword status: See :attr:`status`. - """ + .. attribute:: request + + :class:`Request` object used to get this response. + + .. attribute:: code + + HTTP response code (e.g. 200, 404, or 500). + + .. attribute:: headers + + HTTP headers for this response (:class:`Headers`). - # :class:`Request` object used to get this response. - request = None + .. attribute:: buffer - #: HTTP response code (e.g. 200, 404, or 500). - code = None + Socket read buffer. - #: HTTP headers for this response (:class:`Headers`). - headers = None + .. attribute:: effective_url - #: Socket read buffer. - buffer = None + The destination url for this request after following redirects. - #: The destination url for this request after following redirects. - effective_url = None + .. attribute:: error - #: Error instance if the request resulted in a HTTP error code. + Error instance if the request resulted in a HTTP error code. - #: Human equivalent of :attr:`code`, e.g. ``OK``, `Not found`, or - #: 'Internal Server Error'. - status = None + .. attribute:: status - if PYPY: - __slots__ = ('__weakref__', ) + Human equivalent of :attr:`code`, e.g. ``OK``, `Not found`, or + 'Internal Server Error'. + + """ + + if not PYPY: # pragma: no cover + __slots__ = ('request', 'code', 'headers', 'buffer', 'effective_url', + 'error', 'status', '_body', '__weakref__') def __init__(self, request, code, headers=None, buffer=None, effective_url=None, error=None, status=None): @@ -156,6 +166,7 @@ class Response(object): self.headers = headers if headers is not None else Headers() self.buffer = buffer self.effective_url = effective_url or request.url + self._body = None self.status = status or responses.get(self.code, 'Unknown') self.error = error @@ -168,7 +179,7 @@ class Response(object): if self.error: raise self.error - @cached_property + @property def body(self): """The full contents of the response body. @@ -176,8 +187,10 @@ class Response(object): and subsequent accesses will be cached. """ - if self.buffer is not None: - return self.buffer.getvalue() + if self._body is None: + if self.buffer is not None: + self._body = self.buffer.getvalue() + return self._body @coro @@ -190,7 +203,8 @@ def header_parser(keyt=normalize_header): headers.complete = True continue elif line[0].isspace(): - headers[headers._prev_key] = ' '.join(['', line.lstrip()]) + pkey = headers._prev_key + headers[pkey] = ' '.join([headers.get(pkey) or '', line.lstrip()]) else: key, value = line.split(':', 1) key = headers._prev_key = keyt(key) @@ -211,6 +225,9 @@ class BaseClient(object): request = self.Request(request, **kwargs) self.add_request(request) + def add_request(self, request): + raise NotImplementedError('must implement add_request') + def close(self): pass @@ -219,3 +236,9 @@ class BaseClient(object): self._header_parser.send((bytes_to_str(line), headers)) except StopIteration: self._header_parser = header_parser() + + def __enter__(self): + return self + + def __exit__(self, *exc_info): + self.close() diff --git a/kombu/tests/async/test_http.py b/kombu/tests/async/test_http.py index 785f9e82..837aaf7c 100644 --- a/kombu/tests/async/test_http.py +++ b/kombu/tests/async/test_http.py @@ -1,12 +1,23 @@ from __future__ import absolute_import +from io import BytesIO + from amqp import promise from kombu.async import Hub from kombu.async import http +from kombu.async.http.base import BaseClient, normalize_header, header_parser +from kombu.exceptions import HttpError from kombu.tests.case import Case, Mock +class test_Headers(Case): + + def test_normalize(self): + self.assertEqual(normalize_header('accept-encoding'), + 'Accept-Encoding') + + class test_Request(Case): def test_init(self): @@ -32,37 +43,129 @@ class test_Request(Case): callback.assert_called_with(1) +class test_Response(Case): + + def test_init(self): + req = http.Request('http://foo') + r = http.Response(req, 200) + + self.assertEqual(r.status, 'OK') + self.assertEqual(r.effective_url, 'http://foo') + r.raise_for_error() + + def test_raise_for_error(self): + req = http.Request('http://foo') + r = http.Response(req, 404) + self.assertEqual(r.status, 'Not Found') + self.assertTrue(r.error) + + with self.assertRaises(HttpError): + r.raise_for_error() + + def test_get_body(self): + req = http.Request('http://foo') + req.buffer = BytesIO() + req.buffer.write(b'hello') + + rn = http.Response(req, 200, buffer=None) + self.assertIsNone(rn.body) + + r = http.Response(req, 200, buffer=req.buffer) + self.assertIsNone(r._body) + self.assertEqual(r.body, b'hello') + self.assertEqual(r._body, b'hello') + self.assertEqual(r.body, b'hello') + + +class test_BaseClient(Case): + + def test_init(self): + c = BaseClient(Mock(name='hub')) + self.assertTrue(c.hub) + self.assertTrue(c._header_parser) + + def test_perform(self): + c = BaseClient(Mock(name='hub')) + c.add_request = Mock(name='add_request') + + c.perform('http://foo') + self.assertTrue(c.add_request.called) + self.assertIsInstance(c.add_request.call_args[0][0], http.Request) + + req = http.Request('http://bar') + c.perform(req) + c.add_request.assert_called_with(req) + + def test_add_request(self): + c = BaseClient(Mock(name='hub')) + with self.assertRaises(NotImplementedError): + c.add_request(Mock(name='request')) + + def test_header_parser(self): + c = BaseClient(Mock(name='hub')) + parser = c._header_parser + headers = http.Headers() + + c.on_header(headers, 'HTTP/1.1') + c.on_header(headers, 'x-foo-bar: 123') + c.on_header(headers, 'People: George Costanza') + self.assertEqual(headers._prev_key, 'People') + c.on_header(headers, ' Jerry Seinfeld') + c.on_header(headers, ' Elaine Benes') + c.on_header(headers, ' Cosmo Kramer') + self.assertFalse(headers.complete) + c.on_header(headers, '') + self.assertTrue(headers.complete) + + with self.assertRaises(KeyError): + parser.throw(KeyError('foo')) + c.on_header(headers, '') + + self.assertEqual(headers['X-Foo-Bar'], '123') + self.assertEqual( + headers['People'], + 'George Costanza Jerry Seinfeld Elaine Benes Cosmo Kramer', + ) + + def test_close(self): + BaseClient(Mock(name='hub')).close() + + def test_as_context(self): + c = BaseClient(Mock(name='hub')) + c.close = Mock(name='close') + with c: + pass + c.close.assert_called_with() + + class test_Client(Case): - def test_get_request(self): + def xxx_get_request(self): hub = Hub() callback = Mock(name='callback') def on_ready(response): print('{0.effective_url} -> {0.code}'.format(response)) - on_ready = promise(on_ready) - on_ready.then(callback) requests = [ http.Request( 'http://localhost:8000/README.rst', - on_ready=on_ready, + on_ready=promise(on_ready, callback=callback), ), http.Request( 'http://localhost:8000/AUTHORS', - on_ready=on_ready, + on_ready=promise(on_ready, callback=callback), ), http.Request( 'http://localhost:8000/pavement.py', - on_ready=on_ready, + on_ready=promise(on_ready, callback=callback), ), http.Request( 'http://localhost:8000/setup.py', - on_ready=on_ready, + on_ready=promise(on_ready, callback=callback), ), ] client = http.Client(hub) for request in requests: - request.then(promise(callback)) client.perform(request) print('START PERFORM') |