From 37dc2b28834c95d24746c2958e9ea31d3e8d1968 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Mon, 11 Jan 2016 15:15:01 -0500 Subject: [PATCH] Issue #25486: Resurrect inspect.getargspec in 3.6. Backout a565aad5d6e1. The decision is that we shouldn't remove popular APIs (however long they are depreacted) from Python 3, while 2.7 is still around and supported. --- Doc/library/inspect.rst | 18 ++++++++++++++++++ Doc/whatsnew/3.6.rst | 3 --- Lib/inspect.py | 25 +++++++++++++++++++++++++ Lib/test/test_inspect.py | 40 +++++++++++++++++++++++++++++++++++----- Misc/NEWS | 3 +-- 5 files changed, 79 insertions(+), 10 deletions(-) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 8045d85a08e..8e8d7253a6e 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -796,6 +796,24 @@ Classes and functions classes using multiple inheritance and their descendants will appear multiple times. + +.. function:: getargspec(func) + + Get the names and default values of a Python function's arguments. A + :term:`named tuple` ``ArgSpec(args, varargs, keywords, defaults)`` is + returned. *args* is a list of the argument names. *varargs* and *keywords* + are the names of the ``*`` and ``**`` arguments or ``None``. *defaults* is a + tuple of default argument values or ``None`` if there are no default + arguments; if this tuple has *n* elements, they correspond to the last + *n* elements listed in *args*. + + .. deprecated:: 3.0 + Use :func:`signature` and + :ref:`Signature Object `, which provide a + better introspecting API for callables. This function will be removed + in Python 3.6. + + .. function:: getfullargspec(func) Get the names and default values of a Python function's arguments. A diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst index bf5161d1a50..f7dbbeecbc4 100644 --- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -218,9 +218,6 @@ Removed API and Feature Removals ------------------------ -* ``inspect.getargspec()`` was removed (was deprecated since CPython 3.0). - :func:`inspect.getfullargspec` is an almost drop in replacement. - * ``inspect.getmoduleinfo()`` was removed (was deprecated since CPython 3.3). :func:`inspect.getmodulename` should be used for obtaining the module name for a given path. diff --git a/Lib/inspect.py b/Lib/inspect.py index 7615e5277f7..74f3b3c3bbb 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1004,6 +1004,31 @@ def _getfullargs(co): varkw = co.co_varnames[nargs] return args, varargs, kwonlyargs, varkw + +ArgSpec = namedtuple('ArgSpec', 'args varargs keywords defaults') + +def getargspec(func): + """Get the names and default values of a function's arguments. + + A tuple of four things is returned: (args, varargs, keywords, defaults). + 'args' is a list of the argument names, including keyword-only argument names. + 'varargs' and 'keywords' are the names of the * and ** arguments or None. + 'defaults' is an n-tuple of the default values of the last n arguments. + + Use the getfullargspec() API for Python 3 code, as annotations + and keyword arguments are supported. getargspec() will raise ValueError + if the func has either annotations or keyword arguments. + """ + warnings.warn("inspect.getargspec() is deprecated, " + "use inspect.signature() instead", DeprecationWarning, + stacklevel=2) + args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = \ + getfullargspec(func) + if kwonlyargs or ann: + raise ValueError("Function has keyword-only arguments or annotations" + ", use getfullargspec() API which can support them") + return ArgSpec(args, varargs, varkw, defaults) + FullArgSpec = namedtuple('FullArgSpec', 'args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations') diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index a88e7fdbd8c..283c92268cb 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -628,6 +628,18 @@ class D(B, C): pass got = inspect.getmro(D) self.assertEqual(expected, got) + def assertArgSpecEquals(self, routine, args_e, varargs_e=None, + varkw_e=None, defaults_e=None, formatted=None): + with self.assertWarns(DeprecationWarning): + args, varargs, varkw, defaults = inspect.getargspec(routine) + self.assertEqual(args, args_e) + self.assertEqual(varargs, varargs_e) + self.assertEqual(varkw, varkw_e) + self.assertEqual(defaults, defaults_e) + if formatted is not None: + self.assertEqual(inspect.formatargspec(args, varargs, varkw, defaults), + formatted) + def assertFullArgSpecEquals(self, routine, args_e, varargs_e=None, varkw_e=None, defaults_e=None, kwonlyargs_e=[], kwonlydefaults_e=None, @@ -646,6 +658,23 @@ def assertFullArgSpecEquals(self, routine, args_e, varargs_e=None, kwonlyargs, kwonlydefaults, ann), formatted) + def test_getargspec(self): + self.assertArgSpecEquals(mod.eggs, ['x', 'y'], formatted='(x, y)') + + self.assertArgSpecEquals(mod.spam, + ['a', 'b', 'c', 'd', 'e', 'f'], + 'g', 'h', (3, 4, 5), + '(a, b, c, d=3, e=4, f=5, *g, **h)') + + self.assertRaises(ValueError, self.assertArgSpecEquals, + mod2.keyworded, []) + + self.assertRaises(ValueError, self.assertArgSpecEquals, + mod2.annotated, []) + self.assertRaises(ValueError, self.assertArgSpecEquals, + mod2.keyword_only_arg, []) + + def test_getfullargspec(self): self.assertFullArgSpecEquals(mod2.keyworded, [], varargs_e='arg1', kwonlyargs_e=['arg2'], @@ -659,19 +688,20 @@ def test_getfullargspec(self): kwonlyargs_e=['arg'], formatted='(*, arg)') - def test_fullargspec_api_ignores_wrapped(self): + def test_argspec_api_ignores_wrapped(self): # Issue 20684: low level introspection API must ignore __wrapped__ @functools.wraps(mod.spam) def ham(x, y): pass # Basic check + self.assertArgSpecEquals(ham, ['x', 'y'], formatted='(x, y)') self.assertFullArgSpecEquals(ham, ['x', 'y'], formatted='(x, y)') self.assertFullArgSpecEquals(functools.partial(ham), ['x', 'y'], formatted='(x, y)') # Other variants def check_method(f): - self.assertFullArgSpecEquals(f, ['self', 'x', 'y'], - formatted='(self, x, y)') + self.assertArgSpecEquals(f, ['self', 'x', 'y'], + formatted='(self, x, y)') class C: @functools.wraps(mod.spam) def ham(self, x, y): @@ -749,11 +779,11 @@ def test_getfullagrspec_builtin_func_no_signature(self): with self.assertRaises(TypeError): inspect.getfullargspec(builtin) - def test_getfullargspec_method(self): + def test_getargspec_method(self): class A(object): def m(self): pass - self.assertFullArgSpecEquals(A.m, ['self']) + self.assertArgSpecEquals(A.m, ['self']) def test_classify_newstyle(self): class A(object): diff --git a/Misc/NEWS b/Misc/NEWS index 593ed2475eb..089388ed080 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -429,8 +429,7 @@ Library - Issue #23661: unittest.mock side_effects can now be exceptions again. This was a regression vs Python 3.4. Patch from Ignacio Rossi -- Issue #13248: Remove deprecated inspect.getargspec and inspect.getmoduleinfo - functions. +- Issue #13248: Remove deprecated inspect.getmoduleinfo function. - Issue #25578: Fix (another) memory leak in SSLSocket.getpeercer().