Skip to content

Commit 42a139e

Browse files
bpo-36492: Deprecate passing some arguments as keyword arguments. (GH-12637)
Deprecated passing the following arguments as keyword arguments: - "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 the create() method of multiprocessing.managers.Server and multiprocessing.managers.SharedMemoryServer. - "obj" in weakref.finalize(). Also allowed to pass arbitrary keyword arguments (even "self" and "func") if the above arguments are passed as positional argument.
1 parent 5f2c508 commit 42a139e

22 files changed

+457
-21
lines changed

Doc/whatsnew/3.8.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,29 @@ Deprecated
594594
version they will be errors.
595595
(Contributed by Serhiy Storchaka in :issue:`36048`.)
596596

597+
* Deprecated passing the following arguments as keyword arguments:
598+
599+
- *func* in :func:`functools.partialmethod`, :func:`weakref.finalize`,
600+
:meth:`profile.Profile.runcall`, :meth:`cProfile.Profile.runcall`,
601+
:meth:`bdb.Bdb.runcall`, :meth:`trace.Trace.runfunc` and
602+
:func:`curses.wrapper`.
603+
- *function* in :func:`unittest.addModuleCleanup` and
604+
:meth:`unittest.TestCase.addCleanup`.
605+
- *fn* in the :meth:`~concurrent.futures.Executor.submit` method of
606+
:class:`concurrent.futures.ThreadPoolExecutor` and
607+
:class:`concurrent.futures.ProcessPoolExecutor`.
608+
- *callback* in :meth:`contextlib.ExitStack.callback`,
609+
:meth:`contextlib.AsyncExitStack.callback` and
610+
:meth:`contextlib.AsyncExitStack.push_async_callback`.
611+
- *c* and *typeid* in the :meth:`~multiprocessing.managers.Server.create`
612+
method of :class:`multiprocessing.managers.Server` and
613+
:class:`multiprocessing.managers.SharedMemoryServer`.
614+
- *obj* in :func:`weakref.finalize`.
615+
616+
In future releases of Python they will be :ref:`positional-only
617+
<positional-only_parameter>`.
618+
(Contributed by Serhiy Storchaka in :issue:`36492`.)
619+
597620

598621
API and Feature Removals
599622
========================

Lib/bdb.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,11 +618,26 @@ def runctx(self, cmd, globals, locals):
618618

619619
# This method is more useful to debug a single function call.
620620

621-
def runcall(self, func, *args, **kwds):
621+
def runcall(*args, **kwds):
622622
"""Debug a single function call.
623623
624624
Return the result of the function call.
625625
"""
626+
if len(args) >= 2:
627+
self, func, *args = args
628+
elif not args:
629+
raise TypeError("descriptor 'runcall' of 'Bdb' object "
630+
"needs an argument")
631+
elif 'func' in kwds:
632+
func = kwds.pop('func')
633+
self, *args = args
634+
import warnings
635+
warnings.warn("Passing 'func' as keyword argument is deprecated",
636+
DeprecationWarning, stacklevel=2)
637+
else:
638+
raise TypeError('runcall expected at least 1 positional argument, '
639+
'got %d' % (len(args)-1))
640+
626641
self.reset()
627642
sys.settrace(self.trace_dispatch)
628643
res = None

Lib/cProfile.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,22 @@ 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+
import warnings
116+
warnings.warn("Passing 'func' as keyword argument is deprecated",
117+
DeprecationWarning, stacklevel=2)
118+
else:
119+
raise TypeError('runcall expected at least 1 positional argument, '
120+
'got %d' % (len(args)-1))
121+
107122
self.enable()
108123
try:
109124
return func(*args, **kw)

Lib/concurrent/futures/_base.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ def set_exception(self, exception):
544544
class Executor(object):
545545
"""This is an abstract base class for concrete asynchronous executors."""
546546

547-
def submit(self, fn, *args, **kwargs):
547+
def submit(*args, **kwargs):
548548
"""Submits a callable to be executed with the given arguments.
549549
550550
Schedules the callable to be executed as fn(*args, **kwargs) and returns
@@ -553,6 +553,19 @@ def submit(self, fn, *args, **kwargs):
553553
Returns:
554554
A Future representing the given call.
555555
"""
556+
if len(args) >= 2:
557+
pass
558+
elif not args:
559+
raise TypeError("descriptor 'submit' of 'Executor' object "
560+
"needs an argument")
561+
elif 'fn' in kwargs:
562+
import warnings
563+
warnings.warn("Passing 'fn' as keyword argument is deprecated",
564+
DeprecationWarning, stacklevel=2)
565+
else:
566+
raise TypeError('submit expected at least 1 positional argument, '
567+
'got %d' % (len(args)-1))
568+
556569
raise NotImplementedError()
557570

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

Lib/concurrent/futures/process.py

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

597-
def submit(self, fn, *args, **kwargs):
597+
def submit(*args, **kwargs):
598+
if len(args) >= 2:
599+
self, fn, *args = args
600+
elif not args:
601+
raise TypeError("descriptor 'submit' of 'ProcessPoolExecutor' object "
602+
"needs an argument")
603+
elif 'fn' in kwargs:
604+
fn = kwargs.pop('fn')
605+
self, *args = args
606+
import warnings
607+
warnings.warn("Passing 'fn' as keyword argument is deprecated",
608+
DeprecationWarning, stacklevel=2)
609+
else:
610+
raise TypeError('submit expected at least 1 positional argument, '
611+
'got %d' % (len(args)-1))
612+
598613
with self._shutdown_lock:
599614
if self._broken:
600615
raise BrokenProcessPool(self._broken)

Lib/concurrent/futures/thread.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,22 @@ 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+
import warnings
155+
warnings.warn("Passing 'fn' as keyword argument is deprecated",
156+
DeprecationWarning, stacklevel=2)
157+
else:
158+
raise TypeError('submit expected at least 1 positional argument, '
159+
'got %d' % (len(args)-1))
160+
146161
with self._shutdown_lock:
147162
if self._broken:
148163
raise BrokenThreadPool(self._broken)

Lib/contextlib.py

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,8 @@ def _create_exit_wrapper(cm, cm_exit):
377377
return MethodType(cm_exit, cm)
378378

379379
@staticmethod
380-
def _create_cb_wrapper(callback, *args, **kwds):
380+
def _create_cb_wrapper(*args, **kwds):
381+
callback, *args = args
381382
def _exit_wrapper(exc_type, exc, tb):
382383
callback(*args, **kwds)
383384
return _exit_wrapper
@@ -426,11 +427,26 @@ def enter_context(self, cm):
426427
self._push_cm_exit(cm, _exit)
427428
return result
428429

429-
def callback(self, callback, *args, **kwds):
430+
def callback(*args, **kwds):
430431
"""Registers an arbitrary callback and arguments.
431432
432433
Cannot suppress exceptions.
433434
"""
435+
if len(args) >= 2:
436+
self, callback, *args = args
437+
elif not args:
438+
raise TypeError("descriptor 'callback' of '_BaseExitStack' object "
439+
"needs an argument")
440+
elif 'callback' in kwds:
441+
callback = kwds.pop('callback')
442+
self, *args = args
443+
import warnings
444+
warnings.warn("Passing 'callback' as keyword argument is deprecated",
445+
DeprecationWarning, stacklevel=2)
446+
else:
447+
raise TypeError('callback expected at least 1 positional argument, '
448+
'got %d' % (len(args)-1))
449+
434450
_exit_wrapper = self._create_cb_wrapper(callback, *args, **kwds)
435451

436452
# We changed the signature, so using @wraps is not appropriate, but
@@ -536,7 +552,8 @@ def _create_async_exit_wrapper(cm, cm_exit):
536552
return MethodType(cm_exit, cm)
537553

538554
@staticmethod
539-
def _create_async_cb_wrapper(callback, *args, **kwds):
555+
def _create_async_cb_wrapper(*args, **kwds):
556+
callback, *args = args
540557
async def _exit_wrapper(exc_type, exc, tb):
541558
await callback(*args, **kwds)
542559
return _exit_wrapper
@@ -571,11 +588,26 @@ def push_async_exit(self, exit):
571588
self._push_async_cm_exit(exit, exit_method)
572589
return exit # Allow use as a decorator
573590

574-
def push_async_callback(self, callback, *args, **kwds):
591+
def push_async_callback(*args, **kwds):
575592
"""Registers an arbitrary coroutine function and arguments.
576593
577594
Cannot suppress exceptions.
578595
"""
596+
if len(args) >= 2:
597+
self, callback, *args = args
598+
elif not args:
599+
raise TypeError("descriptor 'push_async_callback' of "
600+
"'AsyncExitStack' object needs an argument")
601+
elif 'callback' in kwds:
602+
callback = kwds.pop('callback')
603+
self, *args = args
604+
import warnings
605+
warnings.warn("Passing 'callback' as keyword argument is deprecated",
606+
DeprecationWarning, stacklevel=2)
607+
else:
608+
raise TypeError('push_async_callback expected at least 1 '
609+
'positional argument, got %d' % (len(args)-1))
610+
579611
_exit_wrapper = self._create_async_cb_wrapper(callback, *args, **kwds)
580612

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

Lib/curses/__init__.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,25 @@ 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+
import warnings
76+
warnings.warn("Passing 'func' as keyword argument is deprecated",
77+
DeprecationWarning, stacklevel=2)
78+
else:
79+
raise TypeError('wrapper expected at least 1 positional argument, '
80+
'got %d' % len(args))
81+
7182
try:
7283
# Initialize curses
7384
stdscr = initscr()

Lib/functools.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,23 @@ class partialmethod(object):
354354
callables as instance methods.
355355
"""
356356

357-
def __init__(self, func, *args, **keywords):
357+
def __init__(*args, **keywords):
358+
if len(args) >= 2:
359+
self, func, *args = args
360+
elif not args:
361+
raise TypeError("descriptor '__init__' of partialmethod "
362+
"needs an argument")
363+
elif 'func' in keywords:
364+
func = keywords.pop('func')
365+
self, *args = args
366+
import warnings
367+
warnings.warn("Passing 'func' as keyword argument is deprecated",
368+
DeprecationWarning, stacklevel=2)
369+
else:
370+
raise TypeError("type 'partialmethod' takes at least one argument, "
371+
"got %d" % (len(args)-1))
372+
args = tuple(args)
373+
358374
if not callable(func) and not hasattr(func, "__get__"):
359375
raise TypeError("{!r} is not callable or a descriptor"
360376
.format(func))

Lib/multiprocessing/managers.py

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -358,10 +358,36 @@ def shutdown(self, c):
358358
finally:
359359
self.stop_event.set()
360360

361-
def create(self, c, typeid, *args, **kwds):
361+
def create(*args, **kwds):
362362
'''
363363
Create a new shared object and return its id
364364
'''
365+
if len(args) >= 3:
366+
self, c, typeid, *args = args
367+
elif not args:
368+
raise TypeError("descriptor 'create' of 'Server' object "
369+
"needs an argument")
370+
else:
371+
if 'typeid' not in kwds:
372+
raise TypeError('create expected at least 2 positional '
373+
'arguments, got %d' % (len(args)-1))
374+
typeid = kwds.pop('typeid')
375+
if len(args) >= 2:
376+
self, c, *args = args
377+
import warnings
378+
warnings.warn("Passing 'typeid' as keyword argument is deprecated",
379+
DeprecationWarning, stacklevel=2)
380+
else:
381+
if 'c' not in kwds:
382+
raise TypeError('create expected at least 2 positional '
383+
'arguments, got %d' % (len(args)-1))
384+
c = kwds.pop('c')
385+
self, *args = args
386+
import warnings
387+
warnings.warn("Passing 'c' as keyword argument is deprecated",
388+
DeprecationWarning, stacklevel=2)
389+
args = tuple(args)
390+
365391
with self.mutex:
366392
callable, exposed, method_to_typeid, proxytype = \
367393
self.registry[typeid]
@@ -583,10 +609,13 @@ def _run_server(cls, registry, address, authkey, serializer, writer,
583609
util.info('manager serving at %r', server.address)
584610
server.serve_forever()
585611

586-
def _create(self, typeid, *args, **kwds):
612+
def _create(*args, **kwds):
587613
'''
588614
Create a new shared object; return the token and exposed tuple
589615
'''
616+
self, typeid, *args = args
617+
args = tuple(args)
618+
590619
assert self._state.value == State.STARTED, 'server not yet started'
591620
conn = self._Client(self._address, authkey=self._authkey)
592621
try:
@@ -1261,15 +1290,25 @@ def __init__(self, *args, **kwargs):
12611290
_SharedMemoryTracker(f"shmm_{self.address}_{getpid()}")
12621291
util.debug(f"SharedMemoryServer started by pid {getpid()}")
12631292

1264-
def create(self, c, typeid, *args, **kwargs):
1293+
def create(*args, **kwargs):
12651294
"""Create a new distributed-shared object (not backed by a shared
12661295
memory block) and return its id to be used in a Proxy Object."""
12671296
# Unless set up as a shared proxy, don't make shared_memory_context
12681297
# a standard part of kwargs. This makes things easier for supplying
12691298
# simple functions.
1299+
if len(args) >= 3:
1300+
typeod = args[2]
1301+
elif 'typeid' in kwargs:
1302+
typeid = kwargs['typeid']
1303+
elif not args:
1304+
raise TypeError("descriptor 'create' of 'SharedMemoryServer' "
1305+
"object needs an argument")
1306+
else:
1307+
raise TypeError('create expected at least 2 positional '
1308+
'arguments, got %d' % (len(args)-1))
12701309
if hasattr(self.registry[typeid][-1], "_shared_memory_proxy"):
12711310
kwargs['shared_memory_context'] = self.shared_memory_context
1272-
return Server.create(self, c, typeid, *args, **kwargs)
1311+
return Server.create(*args, **kwargs)
12731312

12741313
def shutdown(self, c):
12751314
"Call unlink() on all tracked shared memory, terminate the Server."

Lib/profile.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,22 @@ 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+
import warnings
438+
warnings.warn("Passing 'func' as keyword argument is deprecated",
439+
DeprecationWarning, stacklevel=2)
440+
else:
441+
raise TypeError('runcall expected at least 1 positional argument, '
442+
'got %d' % (len(args)-1))
443+
429444
self.set_cmd(repr(func))
430445
sys.setprofile(self.dispatcher)
431446
try:

0 commit comments

Comments
 (0)