-
Notifications
You must be signed in to change notification settings - Fork 158
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
add support for "async with cursor_context()" #265
base: master
Are you sure you want to change the base?
Changes from 17 commits
f83c890
c850e2b
0ffd312
157c580
e0d69bd
0660956
8af3e6d
fd42c6c
4949621
bebe1b3
857abfd
e8a78b2
bfbd60e
623c368
4770379
452e10b
5da64ed
3897640
375c6bf
dabca4e
cc05d18
c8b506d
1e964ff
1ca9b5f
2321a82
0a57593
a5cec12
a30fa02
bdc39c3
945b8fa
dd94ae4
351bcd8
60e9573
256f7b8
02ca4e1
cbd93c5
4e52944
f613762
3e4d0ba
55b4b79
1133c6a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
import asyncio | ||
import sys | ||
|
||
import warnings | ||
|
||
PY_35 = sys.version_info >= (3, 5) | ||
PY_352 = sys.version_info >= (3, 5, 2) | ||
|
@@ -125,7 +125,7 @@ class _PoolAcquireContextManager(_ContextManager): | |
__slots__ = ('_coro', '_conn', '_pool') | ||
|
||
def __init__(self, coro, pool): | ||
self._coro = coro | ||
super().__init__(coro) | ||
self._conn = None | ||
self._pool = pool | ||
|
||
|
@@ -163,6 +163,10 @@ def __init__(self, pool, conn): | |
self._pool = pool | ||
self._conn = conn | ||
|
||
@property | ||
def conn(self): | ||
return self._conn | ||
|
||
def __enter__(self): | ||
assert self._conn | ||
return self._conn | ||
|
@@ -193,37 +197,97 @@ def __aexit__(self, exc_type, exc_val, exc_tb): | |
class _PoolCursorContextManager: | ||
"""Context manager. | ||
|
||
This enables the following idiom for acquiring and releasing a | ||
This enables the following idioms for acquiring and releasing a | ||
cursor around a block: | ||
|
||
with (yield from pool.cursor()) as cur: | ||
yield from cur.execute("SELECT 1") | ||
|
||
async with pool.cursor() as cur: | ||
yield from cur.execute("SELECT 1") | ||
|
||
while failing loudly when accidentally using: | ||
|
||
with pool: | ||
<block> | ||
""" | ||
|
||
__slots__ = ('_pool', '_conn', '_cur') | ||
__slots__ = ('_pool', '_cursor_kwargs', '_cur') | ||
|
||
def __init__(self, pool, conn, cur): | ||
def __init__(self, pool, cursor_kwargs=None): | ||
self._pool = pool | ||
self._conn = conn | ||
self._cur = cur | ||
self._cursor_kwargs = cursor_kwargs | ||
self._cur = None | ||
|
||
def __enter__(self): | ||
return self._cur | ||
|
||
def __exit__(self, *args): | ||
try: | ||
self._cur.close() | ||
self._pool.release(self._conn) | ||
self._pool.__exit__(*args) | ||
finally: | ||
self._pool = None | ||
self._conn = None | ||
self._cur = None | ||
|
||
@asyncio.coroutine | ||
def _init_pool(self, with_aenter): | ||
assert not self._cur | ||
|
||
if with_aenter: | ||
conn = None | ||
else: | ||
conn = yield from self._pool.acquire() | ||
|
||
# self._pool now morphs into a _PoolConnectionContextManager | ||
self._pool = _PoolConnectionContextManager(self._pool, conn) | ||
|
||
@asyncio.coroutine | ||
def _yield_await_init(self): | ||
yield from self._init_pool(False) | ||
self._cur = yield from self._pool.conn.cursor(**self._cursor_kwargs) | ||
return self | ||
|
||
def __iter__(self): | ||
# This will get hit if you use "yield from pool.cursor()" | ||
if PY_35: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't think we need this warning. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is just so they to start using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess in the future because right now it doesn't actually wait for the connection to be closed. |
||
warnings.warn("This usage is deprecated, use 'async with` syntax", | ||
DeprecationWarning) | ||
return self._yield_await_init() | ||
|
||
def __await__(self): | ||
# This will get hit directly if you "await pool.cursor()" | ||
# this is using a trick similar to the one here: | ||
# https://magicstack.github.io/asyncpg/current/_modules/asyncpg/pool.html | ||
# however since `self._init()` is an "asyncio.coroutine" we can't use | ||
# just return self._init().__await__() as that returns a generator | ||
# witout an "__await__" attribute, and we can't return a coroutine from | ||
# here | ||
if PY_35: | ||
warnings.warn("This usage is deprecated, use 'async with` syntax", | ||
DeprecationWarning) | ||
value = yield from self._yield_await_init() | ||
return value | ||
|
||
if PY_35: | ||
@asyncio.coroutine | ||
def __aenter__(self): | ||
yield from self._init_pool(True) | ||
|
||
# this will create the connection | ||
yield from self._pool.__aenter__() | ||
self._cur = yield from self._pool.conn.cursor( | ||
**self._cursor_kwargs) | ||
return self._cur | ||
|
||
@asyncio.coroutine | ||
def __aexit__(self, exc_type, exc_val, exc_tb): | ||
try: | ||
yield from self._cur.__aexit__(exc_type, exc_val, exc_tb) | ||
self._cur = None | ||
finally: | ||
yield from self._pool.__aexit__(exc_type, exc_val, exc_tb) | ||
self._pool = None | ||
|
||
|
||
if not PY_35: | ||
try: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,9 @@ | ||
import asyncio | ||
import pytest | ||
import aiopg | ||
import aiopg.sa | ||
from aiopg.sa import SAConnection | ||
|
||
|
||
@asyncio.coroutine | ||
async def test_cursor_await(make_connection): | ||
conn = await make_connection() | ||
|
||
|
@@ -16,7 +14,6 @@ async def test_cursor_await(make_connection): | |
cursor.close() | ||
|
||
|
||
@asyncio.coroutine | ||
async def test_connect_context_manager(loop, pg_params): | ||
async with aiopg.connect(loop=loop, **pg_params) as conn: | ||
cursor = await conn.cursor() | ||
|
@@ -27,7 +24,26 @@ async def test_connect_context_manager(loop, pg_params): | |
assert conn.closed | ||
|
||
|
||
@asyncio.coroutine | ||
async def test_pool_cursor_context_manager(loop, pg_params): | ||
async with aiopg.create_pool(loop=loop, **pg_params) as pool: | ||
async with pool.cursor() as cursor: | ||
await cursor.execute('SELECT 42') | ||
resp = await cursor.fetchone() | ||
assert resp == (42, ) | ||
assert cursor.closed | ||
assert pool.closed | ||
|
||
|
||
async def test_pool_cursor_await_context_manager(loop, pg_params): | ||
async with aiopg.create_pool(loop=loop, **pg_params) as pool: | ||
with (await pool.cursor()) as cursor: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe this is tested in |
||
await cursor.execute('SELECT 42') | ||
resp = await cursor.fetchone() | ||
assert resp == (42, ) | ||
assert cursor.closed | ||
assert pool.closed | ||
|
||
|
||
async def test_connection_context_manager(make_connection): | ||
conn = await make_connection() | ||
assert not conn.closed | ||
|
@@ -40,7 +56,6 @@ async def test_connection_context_manager(make_connection): | |
assert conn.closed | ||
|
||
|
||
@asyncio.coroutine | ||
async def test_cursor_create_with_context_manager(make_connection): | ||
conn = await make_connection() | ||
|
||
|
@@ -53,7 +68,6 @@ async def test_cursor_create_with_context_manager(make_connection): | |
assert cursor.closed | ||
|
||
|
||
@asyncio.coroutine | ||
async def test_cursor_with_context_manager(make_connection): | ||
conn = await make_connection() | ||
cursor = await conn.cursor() | ||
|
@@ -66,7 +80,6 @@ async def test_cursor_with_context_manager(make_connection): | |
assert cursor.closed | ||
|
||
|
||
@asyncio.coroutine | ||
async def test_cursor_lightweight(make_connection): | ||
conn = await make_connection() | ||
cursor = await conn.cursor() | ||
|
@@ -78,7 +91,6 @@ async def test_cursor_lightweight(make_connection): | |
assert cursor.closed | ||
|
||
|
||
@asyncio.coroutine | ||
async def test_pool_context_manager(pg_params, loop): | ||
pool = await aiopg.create_pool(loop=loop, **pg_params) | ||
|
||
|
@@ -93,7 +105,6 @@ async def test_pool_context_manager(pg_params, loop): | |
assert pool.closed | ||
|
||
|
||
@asyncio.coroutine | ||
async def test_create_pool_context_manager(pg_params, loop): | ||
async with aiopg.create_pool(loop=loop, **pg_params) as pool: | ||
async with pool.acquire() as conn: | ||
|
@@ -107,7 +118,6 @@ async def test_create_pool_context_manager(pg_params, loop): | |
assert pool.closed | ||
|
||
|
||
@asyncio.coroutine | ||
async def test_cursor_aiter(make_connection): | ||
result = [] | ||
conn = await make_connection() | ||
|
@@ -122,7 +132,6 @@ async def test_cursor_aiter(make_connection): | |
assert conn.closed | ||
|
||
|
||
@asyncio.coroutine | ||
async def test_engine_context_manager(pg_params, loop): | ||
engine = await aiopg.sa.create_engine(loop=loop, **pg_params) | ||
async with engine: | ||
|
@@ -132,15 +141,13 @@ async def test_engine_context_manager(pg_params, loop): | |
assert engine.closed | ||
|
||
|
||
@asyncio.coroutine | ||
async def test_create_engine_context_manager(pg_params, loop): | ||
async with aiopg.sa.create_engine(loop=loop, **pg_params) as engine: | ||
async with engine.acquire() as conn: | ||
assert isinstance(conn, SAConnection) | ||
assert engine.closed | ||
|
||
|
||
@asyncio.coroutine | ||
async def test_result_proxy_aiter(pg_params, loop): | ||
sql = 'SELECT generate_series(1, 5);' | ||
result = [] | ||
|
@@ -154,7 +161,6 @@ async def test_result_proxy_aiter(pg_params, loop): | |
assert conn.closed | ||
|
||
|
||
@asyncio.coroutine | ||
async def test_transaction_context_manager(pg_params, loop): | ||
sql = 'SELECT generate_series(1, 5);' | ||
result = [] | ||
|
@@ -181,7 +187,6 @@ async def test_transaction_context_manager(pg_params, loop): | |
assert conn.closed | ||
|
||
|
||
@asyncio.coroutine | ||
async def test_transaction_context_manager_error(pg_params, loop): | ||
async with aiopg.sa.create_engine(loop=loop, **pg_params) as engine: | ||
async with engine.acquire() as conn: | ||
|
@@ -194,7 +199,6 @@ async def test_transaction_context_manager_error(pg_params, loop): | |
assert conn.closed | ||
|
||
|
||
@asyncio.coroutine | ||
async def test_transaction_context_manager_commit_once(pg_params, loop): | ||
async with aiopg.sa.create_engine(loop=loop, **pg_params) as engine: | ||
async with engine.acquire() as conn: | ||
|
@@ -214,7 +218,6 @@ async def test_transaction_context_manager_commit_once(pg_params, loop): | |
assert conn.closed | ||
|
||
|
||
@asyncio.coroutine | ||
async def test_transaction_context_manager_nested_commit(pg_params, loop): | ||
sql = 'SELECT generate_series(1, 5);' | ||
result = [] | ||
|
@@ -244,7 +247,6 @@ async def test_transaction_context_manager_nested_commit(pg_params, loop): | |
assert conn.closed | ||
|
||
|
||
@asyncio.coroutine | ||
async def test_sa_connection_execute(pg_params, loop): | ||
sql = 'SELECT generate_series(1, 5);' | ||
result = [] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you need this property?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
used here https://github.com/aio-libs/aiopg/pull/265/files#diff-222246bbd6dde09cb363ee2c57a67887R246