Skip to content

Commit 424760d

Browse files
committed
Make 'timeout' arg of Connection.executemany a keyword-only arg.
Backwards compatibility will be preserved until 0.11.
1 parent a2a6736 commit 424760d

File tree

2 files changed

+70
-1
lines changed

2 files changed

+70
-1
lines changed

asyncpg/connection.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import struct
1414
import time
1515
import urllib.parse
16+
import warnings
1617

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

219-
async def executemany(self, command: str, args, timeout: float=None):
220+
async def executemany(self, command: str, args,
221+
_timeout: float=None, **kw):
222+
# The real signature of this method is:
223+
#
224+
# executemany(self, command: str, args, *, timeout: float=None)
225+
#
220226
"""Execute an SQL *command* for each sequence of arguments in *args*.
221227
222228
Example:
@@ -234,6 +240,23 @@ async def executemany(self, command: str, args, timeout: float=None):
234240
235241
.. versionadded:: 0.7.0
236242
"""
243+
if 'timeout' in kw:
244+
timeout = kw.pop('timeout')
245+
else:
246+
timeout = _timeout
247+
if timeout is not None:
248+
warnings.warn(
249+
"Passing 'timeout' as a positional argument to "
250+
"executemany() is deprecated and will be removed in "
251+
"asyncpg 0.11.0. Pass it as a keyword argument instead: "
252+
"`executemany(..., timeout=...)`.",
253+
DeprecationWarning, stacklevel=2)
254+
if kw:
255+
first_kwarg = next(iter(kw))
256+
raise TypeError(
257+
'executemany() got an unexpected keyword argument {!r}'.format(
258+
first_kwarg))
259+
237260
return await self._executemany(command, args, timeout)
238261

239262
async def _get_statement(self, query, timeout, *, named: bool=False):
@@ -948,3 +971,21 @@ def _detect_server_capabilities(server_version, connection_settings):
948971
sql_reset=sql_reset,
949972
sql_close_all=sql_close_all
950973
)
974+
975+
976+
def _patch_executemany_signature():
977+
# Patch Connection.executemany() signature to remove '**kw' parameter
978+
# and change '_timeout' keyword arg to 'timeout' keyword-only arg.
979+
# TODO Remove in 0.11.0.
980+
import inspect
981+
sig = inspect.signature(Connection.executemany)
982+
params = sig.parameters.copy()
983+
params.pop('kw')
984+
timeout = params.pop('_timeout')
985+
timeout = timeout.replace(name='timeout', kind=timeout.KEYWORD_ONLY)
986+
params['timeout'] = timeout
987+
Connection.executemany.__signature__ = sig.replace(
988+
parameters=params.values())
989+
990+
991+
_patch_executemany_signature()

tests/test_execute.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
import asyncio
99
import asyncpg
10+
import inspect
11+
import warnings
1012

1113
from asyncpg import _testbase as tb
1214

@@ -152,3 +154,29 @@ async def test_execute_many_2(self):
152154
''', good_data)
153155
finally:
154156
await self.con.execute('DROP TABLE exmany')
157+
158+
async def test_execute_many_3_kwonly_timeout(self):
159+
with self.assertWarnsRegex(DeprecationWarning,
160+
"Passing 'timeout' as a positional"):
161+
await self.con.executemany(
162+
'''SELECT $1::int''',
163+
[(1,), (2,)],
164+
1)
165+
166+
with warnings.catch_warnings():
167+
warnings.simplefilter("error")
168+
169+
# Test that passing timeout as a kwarg doesn't trigger a warning.
170+
await self.con.executemany(
171+
'''SELECT $1::int''',
172+
[(1,), (2,)],
173+
timeout=1)
174+
175+
# Test that not passing timeout is fine too.
176+
await self.con.executemany(
177+
'''SELECT $1::int''',
178+
[(1,), (2,)])
179+
180+
self.assertEqual(
181+
str(inspect.signature(self.con.executemany)),
182+
'(command:str, args, *, timeout:float=None)')

0 commit comments

Comments
 (0)