Skip to content

Commit a37f356

Browse files
[3.7] bpo-36492: Fix passing special keyword arguments to some functions. (GH-12637) (GH-12645)
The following arguments can be passed as keyword arguments for passing to other function if the corresponding required argument is passed as positional: - "func" in functools.partialmethod(), weakref.finalize(), profile.Profile.runcall(), cProfile.Profile.runcall(), bdb.Bdb.runcall(), trace.Trace.runfunc() and curses.wrapper(). - "function" in unittest.addModuleCleanup() and unittest.TestCase.addCleanup(). - "fn" in the submit() method of concurrent.futures.ThreadPoolExecutor and concurrent.futures.ProcessPoolExecutor. - "callback" in contextlib.ExitStack.callback(), contextlib.AsyncExitStack.callback() and contextlib.AsyncExitStack.push_async_callback(). - "c" and "typeid" in multiprocessing.managers.Server.create(). - "obj" in weakref.finalize(). (cherry picked from commit 42a139e)
1 parent 5e23395 commit a37f356

File tree

20 files changed

+282
-17
lines changed

20 files changed

+282
-17
lines changed

Lib/bdb.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,11 +616,23 @@ def runctx(self, cmd, globals, locals):
616616

617617
# This method is more useful to debug a single function call.
618618

619-
def runcall(self, func, *args, **kwds):
619+
def runcall(*args, **kwds):
620620
"""Debug a single function call.
621621
622622
Return the result of the function call.
623623
"""
624+
if len(args) >= 2:
625+
self, func, *args = args
626+
elif not args:
627+
raise TypeError("descriptor 'runcall' of 'Bdb' object "
628+
"needs an argument")
629+
elif 'func' in kwds:
630+
func = kwds.pop('func')
631+
self, *args = args
632+
else:
633+
raise TypeError('runcall expected at least 1 positional argument, '
634+
'got %d' % (len(args)-1))
635+
624636
self.reset()
625637
sys.settrace(self.trace_dispatch)
626638
res = None

Lib/cProfile.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,19 @@ def runctx(self, cmd, globals, locals):
103103
return self
104104

105105
# This method is more useful to profile a single function call.
106-
def runcall(self, func, *args, **kw):
106+
def runcall(*args, **kw):
107+
if len(args) >= 2:
108+
self, func, *args = args
109+
elif not args:
110+
raise TypeError("descriptor 'runcall' of 'Profile' object "
111+
"needs an argument")
112+
elif 'func' in kw:
113+
func = kw.pop('func')
114+
self, *args = args
115+
else:
116+
raise TypeError('runcall expected at least 1 positional argument, '
117+
'got %d' % (len(args)-1))
118+
107119
self.enable()
108120
try:
109121
return func(*args, **kw)

Lib/concurrent/futures/_base.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ def set_exception(self, exception):
536536
class Executor(object):
537537
"""This is an abstract base class for concrete asynchronous executors."""
538538

539-
def submit(self, fn, *args, **kwargs):
539+
def submit(*args, **kwargs):
540540
"""Submits a callable to be executed with the given arguments.
541541
542542
Schedules the callable to be executed as fn(*args, **kwargs) and returns
@@ -545,6 +545,15 @@ def submit(self, fn, *args, **kwargs):
545545
Returns:
546546
A Future representing the given call.
547547
"""
548+
if len(args) >= 2:
549+
pass
550+
elif not args:
551+
raise TypeError("descriptor 'submit' of 'Executor' object "
552+
"needs an argument")
553+
elif 'fn' not in kwargs:
554+
raise TypeError('submit expected at least 1 positional argument, '
555+
'got %d' % (len(args)-1))
556+
548557
raise NotImplementedError()
549558

550559
def map(self, fn, *iterables, timeout=None, chunksize=1):

Lib/concurrent/futures/process.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,19 @@ def _adjust_process_count(self):
593593
p.start()
594594
self._processes[p.pid] = p
595595

596-
def submit(self, fn, *args, **kwargs):
596+
def submit(*args, **kwargs):
597+
if len(args) >= 2:
598+
self, fn, *args = args
599+
elif not args:
600+
raise TypeError("descriptor 'submit' of 'ProcessPoolExecutor' object "
601+
"needs an argument")
602+
elif 'fn' in kwargs:
603+
fn = kwargs.pop('fn')
604+
self, *args = args
605+
else:
606+
raise TypeError('submit expected at least 1 positional argument, '
607+
'got %d' % (len(args)-1))
608+
597609
with self._shutdown_lock:
598610
if self._broken:
599611
raise BrokenProcessPool(self._broken)

Lib/concurrent/futures/thread.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,19 @@ def __init__(self, max_workers=None, thread_name_prefix='',
142142
self._initializer = initializer
143143
self._initargs = initargs
144144

145-
def submit(self, fn, *args, **kwargs):
145+
def submit(*args, **kwargs):
146+
if len(args) >= 2:
147+
self, fn, *args = args
148+
elif not args:
149+
raise TypeError("descriptor 'submit' of 'ThreadPoolExecutor' object "
150+
"needs an argument")
151+
elif 'fn' in kwargs:
152+
fn = kwargs.pop('fn')
153+
self, *args = args
154+
else:
155+
raise TypeError('submit expected at least 1 positional argument, '
156+
'got %d' % (len(args)-1))
157+
146158
with self._shutdown_lock:
147159
if self._broken:
148160
raise BrokenThreadPool(self._broken)

Lib/contextlib.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,8 @@ def _exit_wrapper(exc_type, exc, tb):
378378
return _exit_wrapper
379379

380380
@staticmethod
381-
def _create_cb_wrapper(callback, *args, **kwds):
381+
def _create_cb_wrapper(*args, **kwds):
382+
callback, *args = args
382383
def _exit_wrapper(exc_type, exc, tb):
383384
callback(*args, **kwds)
384385
return _exit_wrapper
@@ -427,11 +428,23 @@ def enter_context(self, cm):
427428
self._push_cm_exit(cm, _exit)
428429
return result
429430

430-
def callback(self, callback, *args, **kwds):
431+
def callback(*args, **kwds):
431432
"""Registers an arbitrary callback and arguments.
432433
433434
Cannot suppress exceptions.
434435
"""
436+
if len(args) >= 2:
437+
self, callback, *args = args
438+
elif not args:
439+
raise TypeError("descriptor 'callback' of '_BaseExitStack' object "
440+
"needs an argument")
441+
elif 'callback' in kwds:
442+
callback = kwds.pop('callback')
443+
self, *args = args
444+
else:
445+
raise TypeError('callback expected at least 1 positional argument, '
446+
'got %d' % (len(args)-1))
447+
435448
_exit_wrapper = self._create_cb_wrapper(callback, *args, **kwds)
436449

437450
# We changed the signature, so using @wraps is not appropriate, but
@@ -540,7 +553,8 @@ async def _exit_wrapper(exc_type, exc, tb):
540553
return _exit_wrapper
541554

542555
@staticmethod
543-
def _create_async_cb_wrapper(callback, *args, **kwds):
556+
def _create_async_cb_wrapper(*args, **kwds):
557+
callback, *args = args
544558
async def _exit_wrapper(exc_type, exc, tb):
545559
await callback(*args, **kwds)
546560
return _exit_wrapper
@@ -575,11 +589,23 @@ def push_async_exit(self, exit):
575589
self._push_async_cm_exit(exit, exit_method)
576590
return exit # Allow use as a decorator
577591

578-
def push_async_callback(self, callback, *args, **kwds):
592+
def push_async_callback(*args, **kwds):
579593
"""Registers an arbitrary coroutine function and arguments.
580594
581595
Cannot suppress exceptions.
582596
"""
597+
if len(args) >= 2:
598+
self, callback, *args = args
599+
elif not args:
600+
raise TypeError("descriptor 'push_async_callback' of "
601+
"'AsyncExitStack' object needs an argument")
602+
elif 'callback' in kwds:
603+
callback = kwds.pop('callback')
604+
self, *args = args
605+
else:
606+
raise TypeError('push_async_callback expected at least 1 '
607+
'positional argument, got %d' % (len(args)-1))
608+
583609
_exit_wrapper = self._create_async_cb_wrapper(callback, *args, **kwds)
584610

585611
# We changed the signature, so using @wraps is not appropriate, but

Lib/curses/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,22 @@ def start_color():
6060
# raises an exception, wrapper() will restore the terminal to a sane state so
6161
# you can read the resulting traceback.
6262

63-
def wrapper(func, *args, **kwds):
63+
def wrapper(*args, **kwds):
6464
"""Wrapper function that initializes curses and calls another function,
6565
restoring normal keyboard/screen behavior on error.
6666
The callable object 'func' is then passed the main window 'stdscr'
6767
as its first argument, followed by any other arguments passed to
6868
wrapper().
6969
"""
7070

71+
if args:
72+
func, *args = args
73+
elif 'func' in kwds:
74+
func = kwds.pop('func')
75+
else:
76+
raise TypeError('wrapper expected at least 1 positional argument, '
77+
'got %d' % len(args))
78+
7179
try:
7280
# Initialize curses
7381
stdscr = initscr()

Lib/functools.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,20 @@ class partialmethod(object):
323323
callables as instance methods.
324324
"""
325325

326-
def __init__(self, func, *args, **keywords):
326+
def __init__(*args, **keywords):
327+
if len(args) >= 2:
328+
self, func, *args = args
329+
elif not args:
330+
raise TypeError("descriptor '__init__' of partialmethod "
331+
"needs an argument")
332+
elif 'func' in keywords:
333+
func = keywords.pop('func')
334+
self, *args = args
335+
else:
336+
raise TypeError("type 'partialmethod' takes at least one argument, "
337+
"got %d" % (len(args)-1))
338+
args = tuple(args)
339+
327340
if not callable(func) and not hasattr(func, "__get__"):
328341
raise TypeError("{!r} is not callable or a descriptor"
329342
.format(func))

Lib/multiprocessing/managers.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,10 +351,30 @@ def shutdown(self, c):
351351
finally:
352352
self.stop_event.set()
353353

354-
def create(self, c, typeid, *args, **kwds):
354+
def create(*args, **kwds):
355355
'''
356356
Create a new shared object and return its id
357357
'''
358+
if len(args) >= 3:
359+
self, c, typeid, *args = args
360+
elif not args:
361+
raise TypeError("descriptor 'create' of 'Server' object "
362+
"needs an argument")
363+
else:
364+
if 'typeid' not in kwds:
365+
raise TypeError('create expected at least 2 positional '
366+
'arguments, got %d' % (len(args)-1))
367+
typeid = kwds.pop('typeid')
368+
if len(args) >= 2:
369+
self, c, *args = args
370+
else:
371+
if 'c' not in kwds:
372+
raise TypeError('create expected at least 2 positional '
373+
'arguments, got %d' % (len(args)-1))
374+
c = kwds.pop('c')
375+
self, *args = args
376+
args = tuple(args)
377+
358378
with self.mutex:
359379
callable, exposed, method_to_typeid, proxytype = \
360380
self.registry[typeid]
@@ -576,10 +596,13 @@ def _run_server(cls, registry, address, authkey, serializer, writer,
576596
util.info('manager serving at %r', server.address)
577597
server.serve_forever()
578598

579-
def _create(self, typeid, *args, **kwds):
599+
def _create(*args, **kwds):
580600
'''
581601
Create a new shared object; return the token and exposed tuple
582602
'''
603+
self, typeid, *args = args
604+
args = tuple(args)
605+
583606
assert self._state.value == State.STARTED, 'server not yet started'
584607
conn = self._Client(self._address, authkey=self._authkey)
585608
try:

Lib/profile.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,19 @@ def runctx(self, cmd, globals, locals):
425425
return self
426426

427427
# This method is more useful to profile a single function call.
428-
def runcall(self, func, *args, **kw):
428+
def runcall(*args, **kw):
429+
if len(args) >= 2:
430+
self, func, *args = args
431+
elif not args:
432+
raise TypeError("descriptor 'runcall' of 'Profile' object "
433+
"needs an argument")
434+
elif 'func' in kw:
435+
func = kw.pop('func')
436+
self, *args = args
437+
else:
438+
raise TypeError('runcall expected at least 1 positional argument, '
439+
'got %d' % (len(args)-1))
440+
429441
self.set_cmd(repr(func))
430442
sys.setprofile(self.dispatcher)
431443
try:

0 commit comments

Comments
 (0)