Skip to content

bpo-36492: Deprecate passing some arguments as keyword arguments. #12637

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

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
23 changes: 23 additions & 0 deletions Doc/whatsnew/3.8.rst
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,29 @@ Deprecated
version they will be errors.
(Contributed by Serhiy Storchaka in :issue:`36048`.)

* Deprecated passing the following arguments as keyword arguments:

- *func* in :func:`functools.partialmethod`, :func:`weakref.finalize`,
:meth:`profile.Profile.runcall`, :meth:`cProfile.Profile.runcall`,
:meth:`bdb.Bdb.runcall`, :meth:`trace.Trace.runfunc` and
:func:`curses.wrapper`.
- *function* in :func:`unittest.addModuleCleanup` and
:meth:`unittest.TestCase.addCleanup`.
- *fn* in the :meth:`~concurrent.futures.Executor.submit` method of
:class:`concurrent.futures.ThreadPoolExecutor` and
:class:`concurrent.futures.ProcessPoolExecutor`.
- *callback* in :meth:`contextlib.ExitStack.callback`,
:meth:`contextlib.AsyncExitStack.callback` and
:meth:`contextlib.AsyncExitStack.push_async_callback`.
- *c* and *typeid* in the :meth:`~multiprocessing.managers.Server.create`
method of :class:`multiprocessing.managers.Server` and
:class:`multiprocessing.managers.SharedMemoryServer`.
- *obj* in :func:`weakref.finalize`.

In future releases of Python they will be :ref:`positional-only
<positional-only_parameter>`.
(Contributed by Serhiy Storchaka in :issue:`36492`.)


API and Feature Removals
========================
Expand Down
17 changes: 16 additions & 1 deletion Lib/bdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -618,11 +618,26 @@ def runctx(self, cmd, globals, locals):

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

def runcall(self, func, *args, **kwds):
def runcall(*args, **kwds):
"""Debug a single function call.

Return the result of the function call.
"""
if len(args) >= 2:
self, func, *args = args
elif not args:
raise TypeError("descriptor 'runcall' of 'Bdb' object "
"needs an argument")
elif 'func' in kwds:
func = kwds.pop('func')
self, *args = args
import warnings
warnings.warn("Passing 'func' as keyword argument is deprecated",
DeprecationWarning, stacklevel=2)
else:
raise TypeError('runcall expected at least 1 positional argument, '
'got %d' % (len(args)-1))

self.reset()
sys.settrace(self.trace_dispatch)
res = None
Expand Down
17 changes: 16 additions & 1 deletion Lib/cProfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,22 @@ def runctx(self, cmd, globals, locals):
return self

# This method is more useful to profile a single function call.
def runcall(self, func, *args, **kw):
def runcall(*args, **kw):
if len(args) >= 2:
self, func, *args = args
elif not args:
raise TypeError("descriptor 'runcall' of 'Profile' object "
"needs an argument")
elif 'func' in kw:
func = kw.pop('func')
self, *args = args
import warnings
warnings.warn("Passing 'func' as keyword argument is deprecated",
DeprecationWarning, stacklevel=2)
else:
raise TypeError('runcall expected at least 1 positional argument, '
'got %d' % (len(args)-1))

self.enable()
try:
return func(*args, **kw)
Expand Down
15 changes: 14 additions & 1 deletion Lib/concurrent/futures/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ def set_exception(self, exception):
class Executor(object):
"""This is an abstract base class for concrete asynchronous executors."""

def submit(self, fn, *args, **kwargs):
def submit(*args, **kwargs):
"""Submits a callable to be executed with the given arguments.

Schedules the callable to be executed as fn(*args, **kwargs) and returns
Expand All @@ -553,6 +553,19 @@ def submit(self, fn, *args, **kwargs):
Returns:
A Future representing the given call.
"""
if len(args) >= 2:
pass
elif not args:
raise TypeError("descriptor 'submit' of 'Executor' object "
"needs an argument")
elif 'fn' in kwargs:
import warnings
warnings.warn("Passing 'fn' as keyword argument is deprecated",
DeprecationWarning, stacklevel=2)
else:
raise TypeError('submit expected at least 1 positional argument, '
'got %d' % (len(args)-1))

raise NotImplementedError()

def map(self, fn, *iterables, timeout=None, chunksize=1):
Expand Down
17 changes: 16 additions & 1 deletion Lib/concurrent/futures/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,22 @@ def _adjust_process_count(self):
p.start()
self._processes[p.pid] = p

def submit(self, fn, *args, **kwargs):
def submit(*args, **kwargs):
if len(args) >= 2:
self, fn, *args = args
elif not args:
raise TypeError("descriptor 'submit' of 'ProcessPoolExecutor' object "
"needs an argument")
elif 'fn' in kwargs:
fn = kwargs.pop('fn')
self, *args = args
import warnings
warnings.warn("Passing 'fn' as keyword argument is deprecated",
DeprecationWarning, stacklevel=2)
else:
raise TypeError('submit expected at least 1 positional argument, '
'got %d' % (len(args)-1))

with self._shutdown_lock:
if self._broken:
raise BrokenProcessPool(self._broken)
Expand Down
17 changes: 16 additions & 1 deletion Lib/concurrent/futures/thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,22 @@ def __init__(self, max_workers=None, thread_name_prefix='',
self._initializer = initializer
self._initargs = initargs

def submit(self, fn, *args, **kwargs):
def submit(*args, **kwargs):
if len(args) >= 2:
self, fn, *args = args
elif not args:
raise TypeError("descriptor 'submit' of 'ThreadPoolExecutor' object "
"needs an argument")
elif 'fn' in kwargs:
fn = kwargs.pop('fn')
self, *args = args
import warnings
warnings.warn("Passing 'fn' as keyword argument is deprecated",
DeprecationWarning, stacklevel=2)
else:
raise TypeError('submit expected at least 1 positional argument, '
'got %d' % (len(args)-1))

with self._shutdown_lock:
if self._broken:
raise BrokenThreadPool(self._broken)
Expand Down
40 changes: 36 additions & 4 deletions Lib/contextlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,8 @@ def _create_exit_wrapper(cm, cm_exit):
return MethodType(cm_exit, cm)

@staticmethod
def _create_cb_wrapper(callback, *args, **kwds):
def _create_cb_wrapper(*args, **kwds):
callback, *args = args
def _exit_wrapper(exc_type, exc, tb):
callback(*args, **kwds)
return _exit_wrapper
Expand Down Expand Up @@ -426,11 +427,26 @@ def enter_context(self, cm):
self._push_cm_exit(cm, _exit)
return result

def callback(self, callback, *args, **kwds):
def callback(*args, **kwds):
"""Registers an arbitrary callback and arguments.

Cannot suppress exceptions.
"""
if len(args) >= 2:
self, callback, *args = args
elif not args:
raise TypeError("descriptor 'callback' of '_BaseExitStack' object "
"needs an argument")
elif 'callback' in kwds:
callback = kwds.pop('callback')
self, *args = args
import warnings
warnings.warn("Passing 'callback' as keyword argument is deprecated",
DeprecationWarning, stacklevel=2)
else:
raise TypeError('callback expected at least 1 positional argument, '
'got %d' % (len(args)-1))

_exit_wrapper = self._create_cb_wrapper(callback, *args, **kwds)

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

@staticmethod
def _create_async_cb_wrapper(callback, *args, **kwds):
def _create_async_cb_wrapper(*args, **kwds):
callback, *args = args
async def _exit_wrapper(exc_type, exc, tb):
await callback(*args, **kwds)
return _exit_wrapper
Expand Down Expand Up @@ -571,11 +588,26 @@ def push_async_exit(self, exit):
self._push_async_cm_exit(exit, exit_method)
return exit # Allow use as a decorator

def push_async_callback(self, callback, *args, **kwds):
def push_async_callback(*args, **kwds):
"""Registers an arbitrary coroutine function and arguments.

Cannot suppress exceptions.
"""
if len(args) >= 2:
self, callback, *args = args
elif not args:
raise TypeError("descriptor 'push_async_callback' of "
"'AsyncExitStack' object needs an argument")
elif 'callback' in kwds:
callback = kwds.pop('callback')
self, *args = args
import warnings
warnings.warn("Passing 'callback' as keyword argument is deprecated",
DeprecationWarning, stacklevel=2)
else:
raise TypeError('push_async_callback expected at least 1 '
'positional argument, got %d' % (len(args)-1))

_exit_wrapper = self._create_async_cb_wrapper(callback, *args, **kwds)

# We changed the signature, so using @wraps is not appropriate, but
Expand Down
13 changes: 12 additions & 1 deletion Lib/curses/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,25 @@ def start_color():
# raises an exception, wrapper() will restore the terminal to a sane state so
# you can read the resulting traceback.

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

if args:
func, *args = args
elif 'func' in kwds:
func = kwds.pop('func')
import warnings
warnings.warn("Passing 'func' as keyword argument is deprecated",
DeprecationWarning, stacklevel=2)
else:
raise TypeError('wrapper expected at least 1 positional argument, '
'got %d' % len(args))

try:
# Initialize curses
stdscr = initscr()
Expand Down
18 changes: 17 additions & 1 deletion Lib/functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,23 @@ class partialmethod(object):
callables as instance methods.
"""

def __init__(self, func, *args, **keywords):
def __init__(*args, **keywords):
if len(args) >= 2:
self, func, *args = args
elif not args:
raise TypeError("descriptor '__init__' of partialmethod "
"needs an argument")
elif 'func' in keywords:
func = keywords.pop('func')
self, *args = args
import warnings
warnings.warn("Passing 'func' as keyword argument is deprecated",
DeprecationWarning, stacklevel=2)
else:
raise TypeError("type 'partialmethod' takes at least one argument, "
"got %d" % (len(args)-1))
args = tuple(args)

if not callable(func) and not hasattr(func, "__get__"):
raise TypeError("{!r} is not callable or a descriptor"
.format(func))
Expand Down
47 changes: 43 additions & 4 deletions Lib/multiprocessing/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,10 +358,36 @@ def shutdown(self, c):
finally:
self.stop_event.set()

def create(self, c, typeid, *args, **kwds):
def create(*args, **kwds):
'''
Create a new shared object and return its id
'''
if len(args) >= 3:
self, c, typeid, *args = args
elif not args:
raise TypeError("descriptor 'create' of 'Server' object "
"needs an argument")
else:
if 'typeid' not in kwds:
raise TypeError('create expected at least 2 positional '
'arguments, got %d' % (len(args)-1))
typeid = kwds.pop('typeid')
if len(args) >= 2:
self, c, *args = args
import warnings
warnings.warn("Passing 'typeid' as keyword argument is deprecated",
DeprecationWarning, stacklevel=2)
else:
if 'c' not in kwds:
raise TypeError('create expected at least 2 positional '
'arguments, got %d' % (len(args)-1))
c = kwds.pop('c')
self, *args = args
import warnings
warnings.warn("Passing 'c' as keyword argument is deprecated",
DeprecationWarning, stacklevel=2)
args = tuple(args)

with self.mutex:
callable, exposed, method_to_typeid, proxytype = \
self.registry[typeid]
Expand Down Expand Up @@ -583,10 +609,13 @@ def _run_server(cls, registry, address, authkey, serializer, writer,
util.info('manager serving at %r', server.address)
server.serve_forever()

def _create(self, typeid, *args, **kwds):
def _create(*args, **kwds):
'''
Create a new shared object; return the token and exposed tuple
'''
self, typeid, *args = args
args = tuple(args)

assert self._state.value == State.STARTED, 'server not yet started'
conn = self._Client(self._address, authkey=self._authkey)
try:
Expand Down Expand Up @@ -1261,15 +1290,25 @@ def __init__(self, *args, **kwargs):
_SharedMemoryTracker(f"shmm_{self.address}_{getpid()}")
util.debug(f"SharedMemoryServer started by pid {getpid()}")

def create(self, c, typeid, *args, **kwargs):
def create(*args, **kwargs):
"""Create a new distributed-shared object (not backed by a shared
memory block) and return its id to be used in a Proxy Object."""
# Unless set up as a shared proxy, don't make shared_memory_context
# a standard part of kwargs. This makes things easier for supplying
# simple functions.
if len(args) >= 3:
typeod = args[2]
elif 'typeid' in kwargs:
typeid = kwargs['typeid']
elif not args:
raise TypeError("descriptor 'create' of 'SharedMemoryServer' "
"object needs an argument")
else:
raise TypeError('create expected at least 2 positional '
'arguments, got %d' % (len(args)-1))
if hasattr(self.registry[typeid][-1], "_shared_memory_proxy"):
kwargs['shared_memory_context'] = self.shared_memory_context
return Server.create(self, c, typeid, *args, **kwargs)
return Server.create(*args, **kwargs)

def shutdown(self, c):
"Call unlink() on all tracked shared memory, terminate the Server."
Expand Down
17 changes: 16 additions & 1 deletion Lib/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,22 @@ def runctx(self, cmd, globals, locals):
return self

# This method is more useful to profile a single function call.
def runcall(self, func, *args, **kw):
def runcall(*args, **kw):
if len(args) >= 2:
self, func, *args = args
elif not args:
raise TypeError("descriptor 'runcall' of 'Profile' object "
"needs an argument")
elif 'func' in kw:
func = kw.pop('func')
self, *args = args
import warnings
warnings.warn("Passing 'func' as keyword argument is deprecated",
DeprecationWarning, stacklevel=2)
else:
raise TypeError('runcall expected at least 1 positional argument, '
'got %d' % (len(args)-1))

self.set_cmd(repr(func))
sys.setprofile(self.dispatcher)
try:
Expand Down
Loading