Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pep492 #536

Merged
merged 24 commits into from
Oct 1, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ install:
- python setup.py develop

script:
- flake8 aiohttp examples tests
- flake8 aiohttp
- if python -c "import sys; sys.exit(sys.version_info >= (3,5)"; then
flake8 examples tests;
fi
- coverage run --source=aiohttp setup.py test
- python setup.py check -rm

Expand Down
235 changes: 160 additions & 75 deletions aiohttp/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
'delete', 'post', 'put', 'patch')

PY_341 = sys.version_info >= (3, 4, 1)
PY_35 = sys.version_info >= (3, 5)


class ClientSession:
Expand Down Expand Up @@ -89,7 +90,6 @@ def __del__(self, _warnings=warnings):
context['source_traceback'] = self._source_traceback
self._loop.call_exception_handler(context)

@asyncio.coroutine
def request(self, method, url, *,
params=None,
data=None,
Expand All @@ -106,6 +106,41 @@ def request(self, method, url, *,
expect100=False,
read_until_eof=True):
"""Perform HTTP request."""
return _RequestContextManager(
self._request(
method,
url,
params=params,
data=data,
headers=headers,
skip_auto_headers=skip_auto_headers,
files=files,
auth=auth,
allow_redirects=allow_redirects,
max_redirects=max_redirects,
encoding=encoding,
version=version,
compress=compress,
chunked=chunked,
expect100=expect100,
read_until_eof=read_until_eof))

@asyncio.coroutine
def _request(self, method, url, *,
params=None,
data=None,
headers=None,
skip_auto_headers=None,
files=None,
auth=None,
allow_redirects=True,
max_redirects=10,
encoding='utf-8',
version=aiohttp.HttpVersion11,
compress=None,
chunked=None,
expect100=False,
read_until_eof=True):

if self.closed:
raise RuntimeError('Session is closed')
Expand Down Expand Up @@ -289,60 +324,53 @@ def _prepare_headers(self, headers):
added_names.add(key)
return result

@asyncio.coroutine
def get(self, url, *, allow_redirects=True, **kwargs):
"""Perform HTTP GET request."""
resp = yield from self.request(hdrs.METH_GET, url,
allow_redirects=allow_redirects,
**kwargs)
return resp
return _RequestContextManager(
self._request(hdrs.METH_GET, url,
allow_redirects=allow_redirects,
**kwargs))

@asyncio.coroutine
def options(self, url, *, allow_redirects=True, **kwargs):
"""Perform HTTP OPTIONS request."""
resp = yield from self.request(hdrs.METH_OPTIONS, url,
allow_redirects=allow_redirects,
**kwargs)
return resp
return _RequestContextManager(
self._request(hdrs.METH_OPTIONS, url,
allow_redirects=allow_redirects,
**kwargs))

@asyncio.coroutine
def head(self, url, *, allow_redirects=False, **kwargs):
"""Perform HTTP HEAD request."""
resp = yield from self.request(hdrs.METH_HEAD, url,
allow_redirects=allow_redirects,
**kwargs)
return resp
return _RequestContextManager(
self._request(hdrs.METH_HEAD, url,
allow_redirects=allow_redirects,
**kwargs))

@asyncio.coroutine
def post(self, url, *, data=None, **kwargs):
"""Perform HTTP POST request."""
resp = yield from self.request(hdrs.METH_POST, url,
data=data,
**kwargs)
return resp
return _RequestContextManager(
self._request(hdrs.METH_POST, url,
data=data,
**kwargs))

@asyncio.coroutine
def put(self, url, *, data=None, **kwargs):
"""Perform HTTP PUT request."""
resp = yield from self.request(hdrs.METH_PUT, url,
data=data,
**kwargs)
return resp
return _RequestContextManager(
self._request(hdrs.METH_PUT, url,
data=data,
**kwargs))

@asyncio.coroutine
def patch(self, url, *, data=None, **kwargs):
"""Perform HTTP PATCH request."""
resp = yield from self.request(hdrs.METH_PATCH, url,
data=data,
**kwargs)
return resp
return _RequestContextManager(
self._request(hdrs.METH_PATCH, url,
data=data,
**kwargs))

@asyncio.coroutine
def delete(self, url, **kwargs):
"""Perform HTTP DELETE request."""
resp = yield from self.request(hdrs.METH_DELETE, url,
**kwargs)
return resp
return _RequestContextManager(
self._request(hdrs.METH_DELETE, url,
**kwargs))

def close(self):
"""Close underlying connector.
Expand Down Expand Up @@ -384,12 +412,84 @@ def __enter__(self):
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()

if PY_35:
from collections.abc import Coroutine
base = Coroutine
else:
base = object


class _RequestContextManager(base):

__slots__ = ('_coro', '_resp')

def __init__(self, coro):
self._coro = coro
self._resp = None

def send(self, value):
return self._coro.send(value)

def throw(self, typ, val=None, tb=None):
if val is None:
return self._coro.throw(typ)
elif tb is None:
return self._coro.throw(typ, val)
else:
return self._coro.throw(typ, val, tb)

def close(self):
return self._coro.close()

@asyncio.coroutine
def __iter__(self):
resp = yield from self._coro
return resp

if PY_35:
def __await__(self):
resp = yield from self._coro
return resp

@asyncio.coroutine
def __aenter__(self):
self._resp = yield from self._coro
return self._resp

@asyncio.coroutine
def __aexit__(self, exc_type, exc, tb):
if exc_type is not None:
self._resp.close()
else:
yield from self._resp.release()


if not PY_35:
try:
from asyncio import coroutines
coroutines._COROUTINE_TYPES += (_RequestContextManager,)
except:
pass


class _DetachedRequestContextManager(_RequestContextManager):

__slots__ = _RequestContextManager.__slots__ + ('_session', )

def __init__(self, coro, session):
super().__init__(coro)
self._session = session

if PY_341:
def __del__(self):
self._session.detach()


@asyncio.coroutine
def request(method, url, *,
params=None,
data=None,
headers=None,
skip_auto_headers=None,
cookies=None,
files=None,
auth=None,
Expand Down Expand Up @@ -460,63 +560,48 @@ def request(method, url, *,
cookies=cookies,
connector=connector,
**kwargs)
try:
resp = yield from session.request(method, url,
params=params,
data=data,
headers=headers,
files=files,
auth=auth,
allow_redirects=allow_redirects,
max_redirects=max_redirects,
encoding=encoding,
version=version,
compress=compress,
chunked=chunked,
expect100=expect100,
read_until_eof=read_until_eof)
return resp
finally:
session.detach()
return _DetachedRequestContextManager(
session._request(method, url,
params=params,
data=data,
headers=headers,
skip_auto_headers=skip_auto_headers,
files=files,
auth=auth,
allow_redirects=allow_redirects,
max_redirects=max_redirects,
encoding=encoding,
version=version,
compress=compress,
chunked=chunked,
expect100=expect100,
read_until_eof=read_until_eof),
session=session)


@asyncio.coroutine
def get(url, **kwargs):
ret = yield from request(hdrs.METH_GET, url, **kwargs)
return ret
return request(hdrs.METH_GET, url, **kwargs)


@asyncio.coroutine
def options(url, **kwargs):
ret = yield from request(hdrs.METH_OPTIONS, url, **kwargs)
return ret
return request(hdrs.METH_OPTIONS, url, **kwargs)


@asyncio.coroutine
def head(url, **kwargs):
ret = yield from request(hdrs.METH_HEAD, url, **kwargs)
return ret
return request(hdrs.METH_HEAD, url, **kwargs)


@asyncio.coroutine
def post(url, **kwargs):
ret = yield from request(hdrs.METH_POST, url, **kwargs)
return ret
return request(hdrs.METH_POST, url, **kwargs)


@asyncio.coroutine
def put(url, **kwargs):
ret = yield from request(hdrs.METH_PUT, url, **kwargs)
return ret
return request(hdrs.METH_PUT, url, **kwargs)


@asyncio.coroutine
def patch(url, **kwargs):
ret = yield from request(hdrs.METH_PATCH, url, **kwargs)
return ret
return request(hdrs.METH_PATCH, url, **kwargs)


@asyncio.coroutine
def delete(url, **kwargs):
ret = yield from request(hdrs.METH_DELETE, url, **kwargs)
return ret
return request(hdrs.METH_DELETE, url, **kwargs)
13 changes: 13 additions & 0 deletions aiohttp/client_reqrep.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from .protocol import HttpMessage

PY_341 = sys.version_info >= (3, 4, 1)
PY_35 = sys.version_info >= (3, 5)

HTTP_PORT = 80
HTTPS_PORT = 443
Expand Down Expand Up @@ -749,3 +750,15 @@ def json(self, *, encoding=None, loads=json.loads):
encoding = self._get_encoding()

return loads(self._content.decode(encoding))

if PY_35:
@asyncio.coroutine
def __aenter__(self):
return self

@asyncio.coroutine
def __aexit__(self, exc_type, exc_val, exc_tb):
if exc_type is None:
yield from self.release()
else:
self.close()
13 changes: 13 additions & 0 deletions docs/client_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,19 @@ Response object
User never creates the instance of ClientResponse class but gets it
from API calls.

:class:`ClientResponse` supports async context manager protocol, e.g.::

resp = await client_session.get(url)
async with resp:
assert resp.status == 200

After exiting from ``async with`` block response object will be
*released* (see :meth:`release` coroutine).

.. versionadded:: 0.18

Support for ``async with``.

.. attribute:: version

Response's version, :class:`HttpVersion` instance.
Expand Down
Loading