From 697d4d64366fb12f82d2eb4d3f20e8eb8817cd20 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 6 May 2026 05:56:15 -0700 Subject: [PATCH 1/2] getfullargspec-annos --- Doc/library/inspect.rst | 13 ++++++++++++- Lib/inspect.py | 11 +++++++---- Lib/test/test_inspect/test_inspect.py | 18 ++++++++++++++++-- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 4825ac11ae2ee3..d8f9f98589c584 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -1189,7 +1189,7 @@ Classes and functions times. -.. function:: getfullargspec(func) +.. function:: getfullargspec(func, *, annotation_format=Format.VALUE) Get the names and default values of a Python function's parameters. A :term:`named tuple` is returned: @@ -1219,6 +1219,14 @@ 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. + A member of the + :class:`annotationlib.Format` enum can be passed to the + *annotation_format* parameter to control the format of the returned + annotations. For example, use + ``annotation_format=annotationlib.Format.STRING`` to return annotations in string + format. Note that with the default ``VALUE`` format, creation of some argspecs + may raise an exception. + .. versionchanged:: 3.4 This function is now based on :func:`signature`, but still ignores ``__wrapped__`` attributes and includes the already bound first @@ -1236,6 +1244,9 @@ Classes and functions order of keyword-only parameters as of version 3.7, although in practice this order had always been preserved in Python 3. + .. versionchanged:: next + The *annotation_format* parameter was added. + .. function:: getargvalues(frame) diff --git a/Lib/inspect.py b/Lib/inspect.py index b1bbdd4c365e3d..a96b3dc954ef0c 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1254,11 +1254,10 @@ def getargs(co): FullArgSpec = namedtuple('FullArgSpec', 'args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations') -def getfullargspec(func): +def getfullargspec(func, *, annotation_format=Format.VALUE): """Get the names and default values of a callable object's parameters. - A tuple of seven things is returned: - (args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations). + A FullArgSpec namedtuple is returned, which has the following attributes: 'args' is a list of the parameter names. 'varargs' and 'varkw' are the names of the * and ** parameters or None. 'defaults' is an n-tuple of the default values of the last n parameters. @@ -1266,6 +1265,9 @@ def getfullargspec(func): 'kwonlydefaults' is a dictionary mapping names from kwonlyargs to defaults. 'annotations' is a dictionary mapping parameter names to annotations. + The *annotation_format* parameter controls the format of the annotations. + See the annotationlib documentation for details. + Notable differences from inspect.signature(): - the "self" parameter is always reported, even for bound methods - wrapper chains defined by __wrapped__ *not* unwrapped automatically @@ -1291,7 +1293,8 @@ def getfullargspec(func): follow_wrapper_chains=False, skip_bound_arg=False, sigcls=Signature, - eval_str=False) + eval_str=False, + annotation_format=annotation_format) except Exception as ex: # Most of the times 'signature' will raise ValueError. # But, it can also raise AttributeError, and, maybe something diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index efe9d27e3407ff..9028d42c617fb4 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -1355,9 +1355,10 @@ def assertFullArgSpecEquals(self, routine, args_e, varargs_e=None, varkw_e=None, defaults_e=None, posonlyargs_e=[], kwonlyargs_e=[], kwonlydefaults_e=None, - ann_e={}): + ann_e={}, + annotation_format=Format.VALUE): args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = \ - inspect.getfullargspec(routine) + inspect.getfullargspec(routine, annotation_format=annotation_format) self.assertEqual(args, args_e) self.assertEqual(varargs, varargs_e) self.assertEqual(varkw, varkw_e) @@ -1390,6 +1391,19 @@ def test_getfullargspec(self): kwonlyargs_e=['e', 'f'], kwonlydefaults_e={'e': 4, 'f': 5}) + def get_getfullargspec_with_undefined_names_in_annotations(self): + def my_func(a: undefined_name): + pass + + with self.assertRaises(NameError): + inspect.getfullargspec(my_func) + + self.assertFullArgSpecEquals(my_func, ['a'], ann_e={'a': 'undefined_name'}, + annotation_format=Format.STRING) + + arg_spec = inspect.getfullargspec(my_func, annotation_format=Format.FORWARDREF) + self.assertIsInstance(arg_spec.annotations['a'], ForwardRef) + def test_argspec_api_ignores_wrapped(self): # Issue 20684: low level introspection API must ignore __wrapped__ @functools.wraps(mod.spam) From 6bf42e6934604c82364d58132a09c681515a2b2a Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 6 May 2026 05:57:03 -0700 Subject: [PATCH 2/2] blurb --- .../next/Library/2026-05-06-05-56-59.gh-issue-141560.wlSQaW.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2026-05-06-05-56-59.gh-issue-141560.wlSQaW.rst diff --git a/Misc/NEWS.d/next/Library/2026-05-06-05-56-59.gh-issue-141560.wlSQaW.rst b/Misc/NEWS.d/next/Library/2026-05-06-05-56-59.gh-issue-141560.wlSQaW.rst new file mode 100644 index 00000000000000..7e5432be8b6bde --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-06-05-56-59.gh-issue-141560.wlSQaW.rst @@ -0,0 +1 @@ +Add an *annotation_format* parameter to :func:`inspect.getfullargspec`.