Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
43 changes: 42 additions & 1 deletion asyncpg/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import struct
import time
import urllib.parse
import warnings

from . import cursor
from . import exceptions
Expand Down Expand Up @@ -216,7 +217,12 @@ async def execute(self, query: str, *args, timeout: float=None) -> str:
_, status, _ = await self._execute(query, args, 0, timeout, True)
return status.decode()

async def executemany(self, command: str, args, timeout: float=None):
async def executemany(self, command: str, args,
_timeout: float=None, **kw):
# The real signature of this method is:
#
# executemany(self, command: str, args, *, timeout: float=None)
#
"""Execute an SQL *command* for each sequence of arguments in *args*.

Example:
Expand All @@ -234,6 +240,23 @@ async def executemany(self, command: str, args, timeout: float=None):

.. versionadded:: 0.7.0
"""
if 'timeout' in kw:
timeout = kw.pop('timeout')
else:
timeout = _timeout
if timeout is not None:
warnings.warn(
"Passing 'timeout' as a positional argument to "
"executemany() is deprecated and will be removed in "
"asyncpg 0.11.0. Pass it as a keyword argument instead: "
"`executemany(..., timeout=...)`.",
DeprecationWarning, stacklevel=2)
if kw:
first_kwarg = next(iter(kw))
raise TypeError(
'executemany() got an unexpected keyword argument {!r}'.format(
first_kwarg))

return await self._executemany(command, args, timeout)

async def _get_statement(self, query, timeout, *, named: bool=False):
Expand Down Expand Up @@ -948,3 +971,21 @@ def _detect_server_capabilities(server_version, connection_settings):
sql_reset=sql_reset,
sql_close_all=sql_close_all
)


def _patch_executemany_signature():
# Patch Connection.executemany() signature to remove '**kw' parameter
# and change '_timeout' keyword arg to 'timeout' keyword-only arg.
# TODO Remove in 0.11.0.
import inspect
sig = inspect.signature(Connection.executemany)
params = sig.parameters.copy()
params.pop('kw')
timeout = params.pop('_timeout')
timeout = timeout.replace(name='timeout', kind=timeout.KEYWORD_ONLY)
params['timeout'] = timeout
Connection.executemany.__signature__ = sig.replace(
parameters=params.values())


_patch_executemany_signature()
28 changes: 28 additions & 0 deletions tests/test_execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import asyncio
import asyncpg
import inspect
import warnings

from asyncpg import _testbase as tb

Expand Down Expand Up @@ -152,3 +154,29 @@ async def test_execute_many_2(self):
''', good_data)
finally:
await self.con.execute('DROP TABLE exmany')

async def test_execute_many_3_kwonly_timeout(self):
with self.assertWarnsRegex(DeprecationWarning,
"Passing 'timeout' as a positional"):
await self.con.executemany(
'''SELECT $1::int''',
[(1,), (2,)],
1)

with warnings.catch_warnings():
warnings.simplefilter("error")

# Test that passing timeout as a kwarg doesn't trigger a warning.
await self.con.executemany(
'''SELECT $1::int''',
[(1,), (2,)],
timeout=1)

# Test that not passing timeout is fine too.
await self.con.executemany(
'''SELECT $1::int''',
[(1,), (2,)])

self.assertEqual(
str(inspect.signature(self.con.executemany)),
'(command:str, args, *, timeout:float=None)')