Commit d53cf99d authored by Serhiy Storchaka's avatar Serhiy Storchaka Committed by GitHub

bpo-36542: Allow to overwrite the signature for Python functions. (GH-12705)

parent 96aeaec6
...@@ -649,6 +649,7 @@ class Bdb: ...@@ -649,6 +649,7 @@ class Bdb:
self.quitting = True self.quitting = True
sys.settrace(None) sys.settrace(None)
return res return res
runcall.__text_signature__ = '($self, func, /, *args, **kwds)'
def set_trace(): def set_trace():
......
...@@ -124,6 +124,7 @@ class Profile(_lsprof.Profiler): ...@@ -124,6 +124,7 @@ class Profile(_lsprof.Profiler):
return func(*args, **kw) return func(*args, **kw)
finally: finally:
self.disable() self.disable()
runcall.__text_signature__ = '($self, func, /, *args, **kw)'
def __enter__(self): def __enter__(self):
self.enable() self.enable()
......
...@@ -1018,6 +1018,8 @@ class UserDict(_collections_abc.MutableMapping): ...@@ -1018,6 +1018,8 @@ class UserDict(_collections_abc.MutableMapping):
self.update(dict) self.update(dict)
if kwargs: if kwargs:
self.update(kwargs) self.update(kwargs)
__init__.__text_signature__ = '($self, dict=None, /, **kwargs)'
def __len__(self): return len(self.data) def __len__(self): return len(self.data)
def __getitem__(self, key): def __getitem__(self, key):
if key in self.data: if key in self.data:
......
...@@ -567,6 +567,7 @@ class Executor(object): ...@@ -567,6 +567,7 @@ class Executor(object):
'got %d' % (len(args)-1)) 'got %d' % (len(args)-1))
raise NotImplementedError() raise NotImplementedError()
submit.__text_signature__ = '($self, fn, /, *args, **kwargs)'
def map(self, fn, *iterables, timeout=None, chunksize=1): def map(self, fn, *iterables, timeout=None, chunksize=1):
"""Returns an iterator equivalent to map(fn, iter). """Returns an iterator equivalent to map(fn, iter).
......
...@@ -630,6 +630,7 @@ class ProcessPoolExecutor(_base.Executor): ...@@ -630,6 +630,7 @@ class ProcessPoolExecutor(_base.Executor):
self._start_queue_management_thread() self._start_queue_management_thread()
return f return f
submit.__text_signature__ = _base.Executor.submit.__text_signature__
submit.__doc__ = _base.Executor.submit.__doc__ submit.__doc__ = _base.Executor.submit.__doc__
def map(self, fn, *iterables, timeout=None, chunksize=1): def map(self, fn, *iterables, timeout=None, chunksize=1):
......
...@@ -174,6 +174,7 @@ class ThreadPoolExecutor(_base.Executor): ...@@ -174,6 +174,7 @@ class ThreadPoolExecutor(_base.Executor):
self._work_queue.put(w) self._work_queue.put(w)
self._adjust_thread_count() self._adjust_thread_count()
return f return f
submit.__text_signature__ = _base.Executor.submit.__text_signature__
submit.__doc__ = _base.Executor.submit.__doc__ submit.__doc__ = _base.Executor.submit.__doc__
def _adjust_thread_count(self): def _adjust_thread_count(self):
......
...@@ -454,6 +454,7 @@ class _BaseExitStack: ...@@ -454,6 +454,7 @@ class _BaseExitStack:
_exit_wrapper.__wrapped__ = callback _exit_wrapper.__wrapped__ = callback
self._push_exit_callback(_exit_wrapper) self._push_exit_callback(_exit_wrapper)
return callback # Allow use as a decorator return callback # Allow use as a decorator
callback.__text_signature__ = '($self, callback, /, *args, **kwds)'
def _push_cm_exit(self, cm, cm_exit): def _push_cm_exit(self, cm, cm_exit):
"""Helper to correctly register callbacks to __exit__ methods.""" """Helper to correctly register callbacks to __exit__ methods."""
...@@ -615,6 +616,7 @@ class AsyncExitStack(_BaseExitStack, AbstractAsyncContextManager): ...@@ -615,6 +616,7 @@ class AsyncExitStack(_BaseExitStack, AbstractAsyncContextManager):
_exit_wrapper.__wrapped__ = callback _exit_wrapper.__wrapped__ = callback
self._push_exit_callback(_exit_wrapper, False) self._push_exit_callback(_exit_wrapper, False)
return callback # Allow use as a decorator return callback # Allow use as a decorator
push_async_callback.__text_signature__ = '($self, callback, /, *args, **kwds)'
async def aclose(self): async def aclose(self):
"""Immediately unwind the context stack.""" """Immediately unwind the context stack."""
......
...@@ -110,3 +110,4 @@ def wrapper(*args, **kwds): ...@@ -110,3 +110,4 @@ def wrapper(*args, **kwds):
echo() echo()
nocbreak() nocbreak()
endwin() endwin()
wrapper.__text_signature__ = '(func, /, *args, **kwds)'
...@@ -388,6 +388,7 @@ class partialmethod(object): ...@@ -388,6 +388,7 @@ class partialmethod(object):
self.func = func self.func = func
self.args = args self.args = args
self.keywords = keywords self.keywords = keywords
__init__.__text_signature__ = '($self, func, /, *args, **keywords)'
def __repr__(self): def __repr__(self):
args = ", ".join(map(repr, self.args)) args = ", ".join(map(repr, self.args))
......
...@@ -2121,7 +2121,7 @@ def _signature_from_builtin(cls, func, skip_bound_arg=True): ...@@ -2121,7 +2121,7 @@ def _signature_from_builtin(cls, func, skip_bound_arg=True):
return _signature_fromstr(cls, func, s, skip_bound_arg) return _signature_fromstr(cls, func, s, skip_bound_arg)
def _signature_from_function(cls, func): def _signature_from_function(cls, func, skip_bound_arg=True):
"""Private helper: constructs Signature for the given python function.""" """Private helper: constructs Signature for the given python function."""
is_duck_function = False is_duck_function = False
...@@ -2133,6 +2133,10 @@ def _signature_from_function(cls, func): ...@@ -2133,6 +2133,10 @@ def _signature_from_function(cls, func):
# of pure function: # of pure function:
raise TypeError('{!r} is not a Python function'.format(func)) raise TypeError('{!r} is not a Python function'.format(func))
s = getattr(func, "__text_signature__", None)
if s:
return _signature_fromstr(cls, func, s, skip_bound_arg)
Parameter = cls._parameter_cls Parameter = cls._parameter_cls
# Parameter information. # Parameter information.
...@@ -2301,7 +2305,8 @@ def _signature_from_callable(obj, *, ...@@ -2301,7 +2305,8 @@ def _signature_from_callable(obj, *,
if isfunction(obj) or _signature_is_functionlike(obj): if isfunction(obj) or _signature_is_functionlike(obj):
# If it's a pure Python function, or an object that is duck type # If it's a pure Python function, or an object that is duck type
# of a Python function (Cython functions, for instance), then: # of a Python function (Cython functions, for instance), then:
return _signature_from_function(sigcls, obj) return _signature_from_function(sigcls, obj,
skip_bound_arg=skip_bound_arg)
if _signature_is_builtin(obj): if _signature_is_builtin(obj):
return _signature_from_builtin(sigcls, obj, return _signature_from_builtin(sigcls, obj,
......
...@@ -419,6 +419,7 @@ class Server(object): ...@@ -419,6 +419,7 @@ class Server(object):
self.incref(c, ident) self.incref(c, ident)
return ident, tuple(exposed) return ident, tuple(exposed)
create.__text_signature__ = '($self, c, typeid, /, *args, **kwds)'
def get_methods(self, c, token): def get_methods(self, c, token):
''' '''
...@@ -1309,6 +1310,7 @@ if HAS_SHMEM: ...@@ -1309,6 +1310,7 @@ if HAS_SHMEM:
if hasattr(self.registry[typeid][-1], "_shared_memory_proxy"): if hasattr(self.registry[typeid][-1], "_shared_memory_proxy"):
kwargs['shared_memory_context'] = self.shared_memory_context kwargs['shared_memory_context'] = self.shared_memory_context
return Server.create(*args, **kwargs) return Server.create(*args, **kwargs)
create.__text_signature__ = '($self, c, typeid, /, *args, **kwargs)'
def shutdown(self, c): def shutdown(self, c):
"Call unlink() on all tracked shared memory, terminate the Server." "Call unlink() on all tracked shared memory, terminate the Server."
......
...@@ -447,6 +447,7 @@ class Profile: ...@@ -447,6 +447,7 @@ class Profile:
return func(*args, **kw) return func(*args, **kw)
finally: finally:
sys.setprofile(None) sys.setprofile(None)
runcall.__text_signature__ = '($self, func, /, *args, **kw)'
#****************************************************************** #******************************************************************
......
...@@ -3782,6 +3782,17 @@ class TestSignatureDefinitions(unittest.TestCase): ...@@ -3782,6 +3782,17 @@ class TestSignatureDefinitions(unittest.TestCase):
with self.subTest(builtin=name): with self.subTest(builtin=name):
self.assertIsNone(obj.__text_signature__) self.assertIsNone(obj.__text_signature__)
def test_python_function_override_signature(self):
def func(*args, **kwargs):
pass
func.__text_signature__ = '($self, a, b=1, *args, c, d=2, **kwargs)'
sig = inspect.signature(func)
self.assertIsNotNone(sig)
self.assertEqual(str(sig), '(self, /, a, b=1, *args, c, d=2, **kwargs)')
func.__text_signature__ = '($self, a, b=1, /, *args, c, d=2, **kwargs)'
sig = inspect.signature(func)
self.assertEqual(str(sig), '(self, a, b=1, /, *args, c, d=2, **kwargs)')
class NTimesUnwrappable: class NTimesUnwrappable:
def __init__(self, n): def __init__(self, n):
......
...@@ -476,6 +476,7 @@ class Trace: ...@@ -476,6 +476,7 @@ class Trace:
if not self.donothing: if not self.donothing:
sys.settrace(None) sys.settrace(None)
return result return result
runfunc.__text_signature__ = '($self, func, /, *args, **kw)'
def file_module_function_of(self, frame): def file_module_function_of(self, frame):
code = frame.f_code code = frame.f_code
......
...@@ -102,6 +102,7 @@ def addModuleCleanup(*args, **kwargs): ...@@ -102,6 +102,7 @@ def addModuleCleanup(*args, **kwargs):
args = tuple(args) args = tuple(args)
_module_cleanups.append((function, args, kwargs)) _module_cleanups.append((function, args, kwargs))
addModuleCleanup.__text_signature__ = '(function, /, *args, **kwargs)'
def doModuleCleanups(): def doModuleCleanups():
...@@ -498,8 +499,8 @@ class TestCase(object): ...@@ -498,8 +499,8 @@ class TestCase(object):
args = tuple(args) args = tuple(args)
self._cleanups.append((function, args, kwargs)) self._cleanups.append((function, args, kwargs))
addCleanup.__text_signature__ = '($self, function, /, *args, **kwargs)'
@classmethod
def addClassCleanup(*args, **kwargs): def addClassCleanup(*args, **kwargs):
"""Same as addCleanup, except the cleanup items are called even if """Same as addCleanup, except the cleanup items are called even if
setUpClass fails (unlike tearDownClass).""" setUpClass fails (unlike tearDownClass)."""
...@@ -514,6 +515,8 @@ class TestCase(object): ...@@ -514,6 +515,8 @@ class TestCase(object):
args = tuple(args) args = tuple(args)
cls._class_cleanups.append((function, args, kwargs)) cls._class_cleanups.append((function, args, kwargs))
addClassCleanup.__text_signature__ = '($cls, function, /, *args, **kwargs)'
addClassCleanup = classmethod(addClassCleanup)
def setUp(self): def setUp(self):
"Hook method for setting up the test fixture before exercising it." "Hook method for setting up the test fixture before exercising it."
......
...@@ -569,6 +569,7 @@ class finalize: ...@@ -569,6 +569,7 @@ class finalize:
info.index = next(self._index_iter) info.index = next(self._index_iter)
self._registry[self] = info self._registry[self] = info
finalize._dirty = True finalize._dirty = True
__init__.__text_signature__ = '($self, obj, func, /, *args, **kwargs)'
def __call__(self, _=None): def __call__(self, _=None):
"""If alive then mark as dead and return func(*args, **kwargs); """If alive then mark as dead and return func(*args, **kwargs);
......
The signature of Python functions can now be overridden by specifying the
``__text_signature__`` attribute.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment