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

This commit is contained in:
Serhiy Storchaka 2019-05-06 22:40:27 +03:00 committed by GitHub
parent 96aeaec647
commit d53cf99dca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 40 additions and 3 deletions

View file

@ -649,6 +649,7 @@ def runcall(*args, **kwds):
self.quitting = True
sys.settrace(None)
return res
runcall.__text_signature__ = '($self, func, /, *args, **kwds)'
def set_trace():

View file

@ -124,6 +124,7 @@ def runcall(*args, **kw):
return func(*args, **kw)
finally:
self.disable()
runcall.__text_signature__ = '($self, func, /, *args, **kw)'
def __enter__(self):
self.enable()

View file

@ -1018,6 +1018,8 @@ def __init__(*args, **kwargs):
self.update(dict)
if kwargs:
self.update(kwargs)
__init__.__text_signature__ = '($self, dict=None, /, **kwargs)'
def __len__(self): return len(self.data)
def __getitem__(self, key):
if key in self.data:

View file

@ -567,6 +567,7 @@ def submit(*args, **kwargs):
'got %d' % (len(args)-1))
raise NotImplementedError()
submit.__text_signature__ = '($self, fn, /, *args, **kwargs)'
def map(self, fn, *iterables, timeout=None, chunksize=1):
"""Returns an iterator equivalent to map(fn, iter).

View file

@ -630,6 +630,7 @@ def submit(*args, **kwargs):
self._start_queue_management_thread()
return f
submit.__text_signature__ = _base.Executor.submit.__text_signature__
submit.__doc__ = _base.Executor.submit.__doc__
def map(self, fn, *iterables, timeout=None, chunksize=1):

View file

@ -174,6 +174,7 @@ def submit(*args, **kwargs):
self._work_queue.put(w)
self._adjust_thread_count()
return f
submit.__text_signature__ = _base.Executor.submit.__text_signature__
submit.__doc__ = _base.Executor.submit.__doc__
def _adjust_thread_count(self):

View file

@ -454,6 +454,7 @@ def callback(*args, **kwds):
_exit_wrapper.__wrapped__ = callback
self._push_exit_callback(_exit_wrapper)
return callback # Allow use as a decorator
callback.__text_signature__ = '($self, callback, /, *args, **kwds)'
def _push_cm_exit(self, cm, cm_exit):
"""Helper to correctly register callbacks to __exit__ methods."""
@ -615,6 +616,7 @@ def push_async_callback(*args, **kwds):
_exit_wrapper.__wrapped__ = callback
self._push_exit_callback(_exit_wrapper, False)
return callback # Allow use as a decorator
push_async_callback.__text_signature__ = '($self, callback, /, *args, **kwds)'
async def aclose(self):
"""Immediately unwind the context stack."""

View file

@ -110,3 +110,4 @@ def wrapper(*args, **kwds):
echo()
nocbreak()
endwin()
wrapper.__text_signature__ = '(func, /, *args, **kwds)'

View file

@ -388,6 +388,7 @@ def __init__(*args, **keywords):
self.func = func
self.args = args
self.keywords = keywords
__init__.__text_signature__ = '($self, func, /, *args, **keywords)'
def __repr__(self):
args = ", ".join(map(repr, self.args))

View file

@ -2121,7 +2121,7 @@ def _signature_from_builtin(cls, func, skip_bound_arg=True):
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."""
is_duck_function = False
@ -2133,6 +2133,10 @@ def _signature_from_function(cls, func):
# of pure function:
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 information.
@ -2301,7 +2305,8 @@ def _signature_from_callable(obj, *,
if isfunction(obj) or _signature_is_functionlike(obj):
# If it's a pure Python function, or an object that is duck type
# 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):
return _signature_from_builtin(sigcls, obj,

View file

@ -419,6 +419,7 @@ def create(*args, **kwds):
self.incref(c, ident)
return ident, tuple(exposed)
create.__text_signature__ = '($self, c, typeid, /, *args, **kwds)'
def get_methods(self, c, token):
'''
@ -1309,6 +1310,7 @@ def create(*args, **kwargs):
if hasattr(self.registry[typeid][-1], "_shared_memory_proxy"):
kwargs['shared_memory_context'] = self.shared_memory_context
return Server.create(*args, **kwargs)
create.__text_signature__ = '($self, c, typeid, /, *args, **kwargs)'
def shutdown(self, c):
"Call unlink() on all tracked shared memory, terminate the Server."

View file

@ -447,6 +447,7 @@ def runcall(*args, **kw):
return func(*args, **kw)
finally:
sys.setprofile(None)
runcall.__text_signature__ = '($self, func, /, *args, **kw)'
#******************************************************************

View file

@ -3782,6 +3782,17 @@ def test_builtins_have_signatures(self):
with self.subTest(builtin=name):
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:
def __init__(self, n):

View file

@ -476,6 +476,7 @@ def runfunc(*args, **kw):
if not self.donothing:
sys.settrace(None)
return result
runfunc.__text_signature__ = '($self, func, /, *args, **kw)'
def file_module_function_of(self, frame):
code = frame.f_code

View file

@ -102,6 +102,7 @@ def addModuleCleanup(*args, **kwargs):
args = tuple(args)
_module_cleanups.append((function, args, kwargs))
addModuleCleanup.__text_signature__ = '(function, /, *args, **kwargs)'
def doModuleCleanups():
@ -498,8 +499,8 @@ def addCleanup(*args, **kwargs):
args = tuple(args)
self._cleanups.append((function, args, kwargs))
addCleanup.__text_signature__ = '($self, function, /, *args, **kwargs)'
@classmethod
def addClassCleanup(*args, **kwargs):
"""Same as addCleanup, except the cleanup items are called even if
setUpClass fails (unlike tearDownClass)."""
@ -514,6 +515,8 @@ def addClassCleanup(*args, **kwargs):
args = tuple(args)
cls._class_cleanups.append((function, args, kwargs))
addClassCleanup.__text_signature__ = '($cls, function, /, *args, **kwargs)'
addClassCleanup = classmethod(addClassCleanup)
def setUp(self):
"Hook method for setting up the test fixture before exercising it."

View file

@ -569,6 +569,7 @@ def __init__(*args, **kwargs):
info.index = next(self._index_iter)
self._registry[self] = info
finalize._dirty = True
__init__.__text_signature__ = '($self, obj, func, /, *args, **kwargs)'
def __call__(self, _=None):
"""If alive then mark as dead and return func(*args, **kwargs);

View file

@ -0,0 +1,2 @@
The signature of Python functions can now be overridden by specifying the
``__text_signature__`` attribute.