bpo-36751: Deprecate getfullargspec and report positional-only args as regular args (GH-13016)
* bpo-36751: Deprecate getfullargspec and report positional-only args as regular args * Use inspect.signature in testhelpers
This commit is contained in:
parent
81c5a90595
commit
d5d2b45469
|
@ -948,6 +948,11 @@ Classes and functions
|
|||
APIs. This function is retained primarily for use in code that needs to
|
||||
maintain compatibility with the Python 2 ``inspect`` module API.
|
||||
|
||||
.. deprecated:: 3.8
|
||||
Use :func:`signature` and
|
||||
:ref:`Signature Object <inspect-signature-object>`, which provide a
|
||||
better introspecting API for callables.
|
||||
|
||||
.. versionchanged:: 3.4
|
||||
This function is now based on :func:`signature`, but still ignores
|
||||
``__wrapped__`` attributes and includes the already bound first
|
||||
|
|
|
@ -726,6 +726,10 @@ Deprecated
|
|||
<positional-only_parameter>`.
|
||||
(Contributed by Serhiy Storchaka in :issue:`36492`.)
|
||||
|
||||
* The function :func:`~inspect.getfullargspec` in the :mod:`inspect`
|
||||
module is deprecated in favor of the :func:`inspect.signature`
|
||||
API. (Contributed by Pablo Galindo in :issue:`36751`.)
|
||||
|
||||
|
||||
API and Feature Removals
|
||||
========================
|
||||
|
|
|
@ -1081,16 +1081,15 @@ def getargspec(func):
|
|||
warnings.warn("inspect.getargspec() is deprecated since Python 3.0, "
|
||||
"use inspect.signature() or inspect.getfullargspec()",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
args, varargs, varkw, defaults, posonlyargs, kwonlyargs, \
|
||||
kwonlydefaults, ann = getfullargspec(func)
|
||||
if posonlyargs or kwonlyargs or ann:
|
||||
raise ValueError("Function has positional-only, keyword-only parameters"
|
||||
" or annotations, use getfullargspec() API which can"
|
||||
" support them")
|
||||
args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = \
|
||||
getfullargspec(func)
|
||||
if kwonlyargs or ann:
|
||||
raise ValueError("Function has keyword-only parameters or annotations"
|
||||
", use inspect.signature() API which can support them")
|
||||
return ArgSpec(args, varargs, varkw, defaults)
|
||||
|
||||
FullArgSpec = namedtuple('FullArgSpec',
|
||||
'args, varargs, varkw, defaults, posonlyargs, kwonlyargs, kwonlydefaults, annotations')
|
||||
'args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations')
|
||||
|
||||
def getfullargspec(func):
|
||||
"""Get the names and default values of a callable object's parameters.
|
||||
|
@ -1104,11 +1103,16 @@ def getfullargspec(func):
|
|||
'kwonlydefaults' is a dictionary mapping names from kwonlyargs to defaults.
|
||||
'annotations' is a dictionary mapping parameter names to annotations.
|
||||
|
||||
.. deprecated:: 3.8
|
||||
Use inspect.signature() instead of inspect.getfullargspec().
|
||||
|
||||
Notable differences from inspect.signature():
|
||||
- the "self" parameter is always reported, even for bound methods
|
||||
- wrapper chains defined by __wrapped__ *not* unwrapped automatically
|
||||
"""
|
||||
|
||||
warnings.warn("Use inspect.signature() instead of inspect.getfullargspec()",
|
||||
DeprecationWarning)
|
||||
try:
|
||||
# Re: `skip_bound_arg=False`
|
||||
#
|
||||
|
@ -1182,8 +1186,8 @@ def getfullargspec(func):
|
|||
# compatibility with 'func.__defaults__'
|
||||
defaults = None
|
||||
|
||||
return FullArgSpec(args, varargs, varkw, defaults,
|
||||
posonlyargs, kwonlyargs, kwdefaults, annotations)
|
||||
return FullArgSpec(posonlyargs + args, varargs, varkw, defaults,
|
||||
kwonlyargs, kwdefaults, annotations)
|
||||
|
||||
|
||||
ArgInfo = namedtuple('ArgInfo', 'args varargs keywords locals')
|
||||
|
@ -1214,8 +1218,7 @@ def formatannotationrelativeto(object):
|
|||
return _formatannotation
|
||||
|
||||
def formatargspec(args, varargs=None, varkw=None, defaults=None,
|
||||
posonlyargs=(), kwonlyargs=(), kwonlydefaults={},
|
||||
annotations={},
|
||||
kwonlyargs=(), kwonlydefaults={}, annotations={},
|
||||
formatarg=str,
|
||||
formatvarargs=lambda name: '*' + name,
|
||||
formatvarkw=lambda name: '**' + name,
|
||||
|
@ -1248,17 +1251,12 @@ def formatargspec(args, varargs=None, varkw=None, defaults=None,
|
|||
return result
|
||||
specs = []
|
||||
if defaults:
|
||||
firstdefault = len(posonlyargs) + len(args) - len(defaults)
|
||||
posonly_left = len(posonlyargs)
|
||||
for i, arg in enumerate([*posonlyargs, *args]):
|
||||
firstdefault = len(args) - len(defaults)
|
||||
for i, arg in enumerate(args):
|
||||
spec = formatargandannotation(arg)
|
||||
if defaults and i >= firstdefault:
|
||||
spec = spec + formatvalue(defaults[i - firstdefault])
|
||||
specs.append(spec)
|
||||
posonly_left -= 1
|
||||
if posonlyargs and posonly_left == 0:
|
||||
specs.append('/')
|
||||
|
||||
if varargs is not None:
|
||||
specs.append(formatvarargs(formatargandannotation(varargs)))
|
||||
else:
|
||||
|
@ -1346,8 +1344,7 @@ def getcallargs(*func_and_positional, **named):
|
|||
func = func_and_positional[0]
|
||||
positional = func_and_positional[1:]
|
||||
spec = getfullargspec(func)
|
||||
(args, varargs, varkw, defaults, posonlyargs,
|
||||
kwonlyargs, kwonlydefaults, ann) = spec
|
||||
args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = spec
|
||||
f_name = func.__name__
|
||||
arg2value = {}
|
||||
|
||||
|
@ -1356,16 +1353,12 @@ def getcallargs(*func_and_positional, **named):
|
|||
# implicit 'self' (or 'cls' for classmethods) argument
|
||||
positional = (func.__self__,) + positional
|
||||
num_pos = len(positional)
|
||||
num_posonlyargs = len(posonlyargs)
|
||||
num_args = len(args)
|
||||
num_defaults = len(defaults) if defaults else 0
|
||||
|
||||
n = min(num_pos, num_posonlyargs)
|
||||
for i in range(num_posonlyargs):
|
||||
arg2value[posonlyargs[i]] = positional[i]
|
||||
n = min(num_pos, num_args)
|
||||
for i in range(n):
|
||||
arg2value[args[i]] = positional[num_posonlyargs+i]
|
||||
arg2value[args[i]] = positional[i]
|
||||
if varargs:
|
||||
arg2value[varargs] = tuple(positional[n:])
|
||||
possible_kwargs = set(args + kwonlyargs)
|
||||
|
|
|
@ -766,28 +766,29 @@ class TestClassesAndFunctions(unittest.TestCase):
|
|||
posonlyargs_e=[], kwonlyargs_e=[],
|
||||
kwonlydefaults_e=None,
|
||||
ann_e={}, formatted=None):
|
||||
args, varargs, varkw, defaults, posonlyargs, kwonlyargs, kwonlydefaults, ann = \
|
||||
inspect.getfullargspec(routine)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = \
|
||||
inspect.getfullargspec(routine)
|
||||
self.assertEqual(args, args_e)
|
||||
self.assertEqual(varargs, varargs_e)
|
||||
self.assertEqual(varkw, varkw_e)
|
||||
self.assertEqual(defaults, defaults_e)
|
||||
self.assertEqual(posonlyargs, posonlyargs_e)
|
||||
self.assertEqual(kwonlyargs, kwonlyargs_e)
|
||||
self.assertEqual(kwonlydefaults, kwonlydefaults_e)
|
||||
self.assertEqual(ann, ann_e)
|
||||
if formatted is not None:
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.assertEqual(inspect.formatargspec(args, varargs, varkw, defaults,
|
||||
posonlyargs, kwonlyargs,
|
||||
kwonlydefaults, ann),
|
||||
kwonlyargs, kwonlydefaults, ann),
|
||||
formatted)
|
||||
|
||||
def test_getargspec(self):
|
||||
self.assertArgSpecEquals(mod.eggs, ['x', 'y'], formatted='(x, y)')
|
||||
|
||||
self.assertRaises(ValueError, self.assertArgSpecEquals,
|
||||
mod.spam, [])
|
||||
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, [])
|
||||
|
@ -811,25 +812,22 @@ class TestClassesAndFunctions(unittest.TestCase):
|
|||
kwonlyargs_e=['arg'],
|
||||
formatted='(*, arg)')
|
||||
|
||||
self.assertFullArgSpecEquals(mod2.all_markers, ['c', 'd'],
|
||||
posonlyargs_e=['a', 'b'],
|
||||
self.assertFullArgSpecEquals(mod2.all_markers, ['a', 'b', 'c', 'd'],
|
||||
kwonlyargs_e=['e', 'f'],
|
||||
formatted='(a, b, /, c, d, *, e, f)')
|
||||
formatted='(a, b, c, d, *, e, f)')
|
||||
|
||||
self.assertFullArgSpecEquals(mod2.all_markers_with_args_and_kwargs,
|
||||
['c', 'd'],
|
||||
posonlyargs_e=['a', 'b'],
|
||||
['a', 'b', 'c', 'd'],
|
||||
varargs_e='args',
|
||||
varkw_e='kwargs',
|
||||
kwonlyargs_e=['e', 'f'],
|
||||
formatted='(a, b, /, c, d, *args, e, f, **kwargs)')
|
||||
formatted='(a, b, c, d, *args, e, f, **kwargs)')
|
||||
|
||||
self.assertFullArgSpecEquals(mod2.all_markers_with_defaults, ['c', 'd'],
|
||||
self.assertFullArgSpecEquals(mod2.all_markers_with_defaults, ['a', 'b', 'c', 'd'],
|
||||
defaults_e=(1,2,3),
|
||||
posonlyargs_e=['a', 'b'],
|
||||
kwonlyargs_e=['e', 'f'],
|
||||
kwonlydefaults_e={'e': 4, 'f': 5},
|
||||
formatted='(a, b=1, /, c=2, d=3, *, e=4, f=5)')
|
||||
formatted='(a, b=1, c=2, d=3, *, e=4, f=5)')
|
||||
|
||||
def test_argspec_api_ignores_wrapped(self):
|
||||
# Issue 20684: low level introspection API must ignore __wrapped__
|
||||
|
@ -877,25 +875,27 @@ class TestClassesAndFunctions(unittest.TestCase):
|
|||
spam_param = inspect.Parameter('spam', inspect.Parameter.POSITIONAL_ONLY)
|
||||
test.__signature__ = inspect.Signature(parameters=(spam_param,))
|
||||
|
||||
self.assertFullArgSpecEquals(test, [], posonlyargs_e=['spam'], formatted='(spam, /)')
|
||||
self.assertFullArgSpecEquals(test, ['spam'], formatted='(spam)')
|
||||
|
||||
def test_getfullargspec_signature_annos(self):
|
||||
def test(a:'spam') -> 'ham': pass
|
||||
spec = inspect.getfullargspec(test)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
spec = inspect.getfullargspec(test)
|
||||
self.assertEqual(test.__annotations__, spec.annotations)
|
||||
|
||||
def test(): pass
|
||||
spec = inspect.getfullargspec(test)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
spec = inspect.getfullargspec(test)
|
||||
self.assertEqual(test.__annotations__, spec.annotations)
|
||||
|
||||
@unittest.skipIf(MISSING_C_DOCSTRINGS,
|
||||
"Signature information for builtins requires docstrings")
|
||||
def test_getfullargspec_builtin_methods(self):
|
||||
self.assertFullArgSpecEquals(_pickle.Pickler.dump, [],
|
||||
posonlyargs_e=['self', 'obj'], formatted='(self, obj, /)')
|
||||
self.assertFullArgSpecEquals(_pickle.Pickler.dump, ['self', 'obj'],
|
||||
formatted='(self, obj)')
|
||||
|
||||
self.assertFullArgSpecEquals(_pickle.Pickler(io.BytesIO()).dump, [],
|
||||
posonlyargs_e=['self', 'obj'], formatted='(self, obj, /)')
|
||||
self.assertFullArgSpecEquals(_pickle.Pickler(io.BytesIO()).dump, ['self', 'obj'],
|
||||
formatted='(self, obj)')
|
||||
|
||||
self.assertFullArgSpecEquals(
|
||||
os.stat,
|
||||
|
@ -910,7 +910,8 @@ class TestClassesAndFunctions(unittest.TestCase):
|
|||
def test_getfullargspec_builtin_func(self):
|
||||
import _testcapi
|
||||
builtin = _testcapi.docstring_with_signature_with_defaults
|
||||
spec = inspect.getfullargspec(builtin)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
spec = inspect.getfullargspec(builtin)
|
||||
self.assertEqual(spec.defaults[0], 'avocado')
|
||||
|
||||
@cpython_only
|
||||
|
@ -919,17 +920,20 @@ class TestClassesAndFunctions(unittest.TestCase):
|
|||
def test_getfullargspec_builtin_func_no_signature(self):
|
||||
import _testcapi
|
||||
builtin = _testcapi.docstring_no_signature
|
||||
with self.assertRaises(TypeError):
|
||||
inspect.getfullargspec(builtin)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
with self.assertRaises(TypeError):
|
||||
inspect.getfullargspec(builtin)
|
||||
|
||||
def test_getfullargspec_definition_order_preserved_on_kwonly(self):
|
||||
for fn in signatures_with_lexicographic_keyword_only_parameters():
|
||||
signature = inspect.getfullargspec(fn)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
signature = inspect.getfullargspec(fn)
|
||||
l = list(signature.kwonlyargs)
|
||||
sorted_l = sorted(l)
|
||||
self.assertTrue(l)
|
||||
self.assertEqual(l, sorted_l)
|
||||
signature = inspect.getfullargspec(unsorted_keyword_only_parameters_fn)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
signature = inspect.getfullargspec(unsorted_keyword_only_parameters_fn)
|
||||
l = list(signature.kwonlyargs)
|
||||
self.assertEqual(l, unsorted_keyword_only_parameters)
|
||||
|
||||
|
@ -1386,8 +1390,9 @@ class TestGetcallargsFunctions(unittest.TestCase):
|
|||
def assertEqualCallArgs(self, func, call_params_string, locs=None):
|
||||
locs = dict(locs or {}, func=func)
|
||||
r1 = eval('func(%s)' % call_params_string, None, locs)
|
||||
r2 = eval('inspect.getcallargs(func, %s)' % call_params_string, None,
|
||||
locs)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
r2 = eval('inspect.getcallargs(func, %s)' % call_params_string, None,
|
||||
locs)
|
||||
self.assertEqual(r1, r2)
|
||||
|
||||
def assertEqualException(self, func, call_param_string, locs=None):
|
||||
|
@ -1399,8 +1404,9 @@ class TestGetcallargsFunctions(unittest.TestCase):
|
|||
else:
|
||||
self.fail('Exception not raised')
|
||||
try:
|
||||
eval('inspect.getcallargs(func, %s)' % call_param_string, None,
|
||||
locs)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
eval('inspect.getcallargs(func, %s)' % call_param_string, None,
|
||||
locs)
|
||||
except Exception as e:
|
||||
ex2 = e
|
||||
else:
|
||||
|
@ -1558,14 +1564,16 @@ class TestGetcallargsFunctions(unittest.TestCase):
|
|||
def f5(*, a): pass
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
'missing 1 required keyword-only'):
|
||||
inspect.getcallargs(f5)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
inspect.getcallargs(f5)
|
||||
|
||||
|
||||
# issue20817:
|
||||
def f6(a, b, c):
|
||||
pass
|
||||
with self.assertRaisesRegex(TypeError, "'a', 'b' and 'c'"):
|
||||
inspect.getcallargs(f6)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
inspect.getcallargs(f6)
|
||||
|
||||
# bpo-33197
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
|
|
|
@ -920,7 +920,7 @@ class SpecSignatureTest(unittest.TestCase):
|
|||
mock(1, 2)
|
||||
mock(x=1, y=2)
|
||||
|
||||
self.assertEqual(inspect.getfullargspec(mock), inspect.getfullargspec(myfunc))
|
||||
self.assertEqual(inspect.signature(mock), inspect.signature(myfunc))
|
||||
self.assertEqual(mock.mock_calls, [call(1, 2), call(x=1, y=2)])
|
||||
self.assertRaises(TypeError, mock, 1)
|
||||
|
||||
|
@ -934,7 +934,7 @@ class SpecSignatureTest(unittest.TestCase):
|
|||
mock(1, 2, c=3)
|
||||
mock(1, c=3)
|
||||
|
||||
self.assertEqual(inspect.getfullargspec(mock), inspect.getfullargspec(foo))
|
||||
self.assertEqual(inspect.signature(mock), inspect.signature(foo))
|
||||
self.assertEqual(mock.mock_calls, [call(1, 2, c=3), call(1, c=3)])
|
||||
self.assertRaises(TypeError, mock, 1)
|
||||
self.assertRaises(TypeError, mock, 1, 2, 3, c=4)
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
The :func:`~inspect.getfullargspec` function in the :mod:`inspect` module is
|
||||
deprecated in favor of the :func:`inspect.signature` API. Contributed by
|
||||
Pablo Galindo.
|
Loading…
Reference in New Issue