Skip to content

Commit

Permalink
bpo-36542: Allow to overwrite the signature for Python functions. (py…
Browse files Browse the repository at this point in the history
  • Loading branch information
serhiy-storchaka authored May 6, 2019
1 parent 96aeaec commit d53cf99
Show file tree
Hide file tree
Showing 17 changed files with 40 additions and 3 deletions.
1 change: 1 addition & 0 deletions Lib/bdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down
1 change: 1 addition & 0 deletions Lib/cProfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 2 additions & 0 deletions Lib/collections/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions Lib/concurrent/futures/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
1 change: 1 addition & 0 deletions Lib/concurrent/futures/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
1 change: 1 addition & 0 deletions Lib/concurrent/futures/thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
2 changes: 2 additions & 0 deletions Lib/contextlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down Expand Up @@ -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."""
Expand Down
1 change: 1 addition & 0 deletions Lib/curses/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,4 @@ def wrapper(*args, **kwds):
echo()
nocbreak()
endwin()
wrapper.__text_signature__ = '(func, /, *args, **kwds)'
1 change: 1 addition & 0 deletions Lib/functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
9 changes: 7 additions & 2 deletions Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions Lib/multiprocessing/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
'''
Expand Down Expand Up @@ -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."
Expand Down
1 change: 1 addition & 0 deletions Lib/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ def runcall(*args, **kw):
return func(*args, **kw)
finally:
sys.setprofile(None)
runcall.__text_signature__ = '($self, func, /, *args, **kw)'


#******************************************************************
Expand Down
11 changes: 11 additions & 0 deletions Lib/test/test_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
1 change: 1 addition & 0 deletions Lib/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion Lib/unittest/case.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down Expand Up @@ -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)."""
Expand All @@ -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."
Expand Down
1 change: 1 addition & 0 deletions Lib/weakref.py
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The signature of Python functions can now be overridden by specifying the
``__text_signature__`` attribute.

0 comments on commit d53cf99

Please sign in to comment.