]> granicus.if.org Git - python/commitdiff
bpo-36542: Allow to overwrite the signature for Python functions. (GH-12705)
authorSerhiy Storchaka <storchaka@gmail.com>
Mon, 6 May 2019 19:40:27 +0000 (22:40 +0300)
committerGitHub <noreply@github.com>
Mon, 6 May 2019 19:40:27 +0000 (22:40 +0300)
17 files changed:
Lib/bdb.py
Lib/cProfile.py
Lib/collections/__init__.py
Lib/concurrent/futures/_base.py
Lib/concurrent/futures/process.py
Lib/concurrent/futures/thread.py
Lib/contextlib.py
Lib/curses/__init__.py
Lib/functools.py
Lib/inspect.py
Lib/multiprocessing/managers.py
Lib/profile.py
Lib/test/test_inspect.py
Lib/trace.py
Lib/unittest/case.py
Lib/weakref.py
Misc/NEWS.d/next/Library/2019-04-06-12-36-09.bpo-36542.Q0qyYV.rst [new file with mode: 0644]

index 54aa98437450a25613a6f8013d9492abc26cbb37..69174364c46aef2422ce926e24afa7c4ce141c27 100644 (file)
@@ -649,6 +649,7 @@ class Bdb:
             self.quitting = True
             sys.settrace(None)
         return res
+    runcall.__text_signature__ = '($self, func, /, *args, **kwds)'
 
 
 def set_trace():
index 2e449cc576cebd349f4bf2c7a07f4ae7321619c7..369d02e22e24aa6a582c765ec36b4aac0892001b 100755 (executable)
@@ -124,6 +124,7 @@ class Profile(_lsprof.Profiler):
             return func(*args, **kw)
         finally:
             self.disable()
+    runcall.__text_signature__ = '($self, func, /, *args, **kw)'
 
     def __enter__(self):
         self.enable()
index 9657c1cf83bc58f9a225721f700c54af060f233f..e6cafb320fabf7249be1547643dac0a5495a389d 100644 (file)
@@ -1018,6 +1018,8 @@ class UserDict(_collections_abc.MutableMapping):
             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:
index ea16eef841c51818382430124ebf57f82d9d6e1e..8f155f0ea82bdcfbc56a6368c96fd8977a7f9626 100644 (file)
@@ -567,6 +567,7 @@ class Executor(object):
                             '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).
index e6ce278b5d44c6c2e7df73d5a018710f7ca6fd33..21bf4a447f084c494d4378643bb3162e86964c64 100644 (file)
@@ -630,6 +630,7 @@ class ProcessPoolExecutor(_base.Executor):
 
             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):
index 0a61e3a9ac1bd1b8232eb8bb893b97a4327bc46c..2af31a106dd9147de1c84a8ec7e1f600cfb31fd9 100644 (file)
@@ -174,6 +174,7 @@ class ThreadPoolExecutor(_base.Executor):
             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):
index ae498a2b6ef5e27941f88d6f96142e340d11c230..de989a001c6dfbc0f3885ca55a212568b22c15ee 100644 (file)
@@ -454,6 +454,7 @@ class _BaseExitStack:
         _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 @@ class AsyncExitStack(_BaseExitStack, AbstractAsyncContextManager):
         _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."""
index 44a198428820f75d87b42fdb591a750e283969fe..24ff3ca93a8933507230ab1e46b15ae1eb82a16a 100644 (file)
@@ -110,3 +110,4 @@ def wrapper(*args, **kwds):
             echo()
             nocbreak()
             endwin()
+wrapper.__text_signature__ = '(func, /, *args, **kwds)'
index 1f1874db9b4cce510ca088bde86a5ae95bef0fce..28d9f6f75fdb8b9fc51492eeee6bd09512d1621f 100644 (file)
@@ -388,6 +388,7 @@ class partialmethod(object):
             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))
index c460309bb5a1b65778169f53c815029d0f5da607..6c3027987b300147598e71f47f43d3d410f781cf 100644 (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,
index 80c3ddb9154a4da88b74dd998882f70d2808da7e..22abd47fb1f23f1fd6594bffc021434feee5d44d 100644 (file)
@@ -419,6 +419,7 @@ class Server(object):
 
         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 @@ if HAS_SHMEM:
             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."
index 9a865d3f6f6ed7abee7f8a94a349de649f89ef1f..1346297c04a5931d133fe305b48ca7dfde89b51d 100755 (executable)
@@ -447,6 +447,7 @@ class Profile:
             return func(*args, **kw)
         finally:
             sys.setprofile(None)
+    runcall.__text_signature__ = '($self, func, /, *args, **kw)'
 
 
     #******************************************************************
index 3c825b00e5e4a793be3b32dd74c54c9d30290272..c54cdb23c242ffcaa323292902138ab2af09f35e 100644 (file)
@@ -3782,6 +3782,17 @@ class TestSignatureDefinitions(unittest.TestCase):
             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):
index fd40fbae8505c0186b8297724520ed762255ff0b..63008a134a8aec183429a4c4362cf5ef6e8d91ee 100755 (executable)
@@ -476,6 +476,7 @@ class Trace:
             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
index 8ff2546fc207cceef2b70b804e46e5b31fa47ca8..8e01c3dc7bbd12ddff7f247853e3c993997fe604 100644 (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 @@ class TestCase(object):
         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 @@ class TestCase(object):
         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."
index 285c70792e0b8fda54b81e1962f72a788cf7c419..1eeb7b0a0b44469bf24c7fd5ee089b83cc748370 100644 (file)
@@ -569,6 +569,7 @@ class finalize:
         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);
diff --git a/Misc/NEWS.d/next/Library/2019-04-06-12-36-09.bpo-36542.Q0qyYV.rst b/Misc/NEWS.d/next/Library/2019-04-06-12-36-09.bpo-36542.Q0qyYV.rst
new file mode 100644 (file)
index 0000000..8374776
--- /dev/null
@@ -0,0 +1,2 @@
+The signature of Python functions can now be overridden by specifying the
+``__text_signature__`` attribute.