From e0df986fa160819b9d0906a5371ac4cfc3258d0b Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sat, 9 Mar 2024 12:56:09 +0300 Subject: [PATCH 1/7] gh-108901: Add `inspect.Signature.from_frame` --- Doc/library/inspect.rst | 7 ++ Lib/inspect.py | 52 ++++++++++- Lib/test/test_inspect/test_inspect.py | 90 +++++++++++++++++++ ...-03-09-12-55-44.gh-issue-108901.s9VClL.rst | 3 + 4 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-03-09-12-55-44.gh-issue-108901.s9VClL.rst diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index ed8d705da3b0b5..0bfd18475e6461 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -817,6 +817,13 @@ function. .. versionchanged:: 3.10 The *globals*, *locals*, and *eval_str* parameters were added. + .. classmethod:: Signature.from_frame(frame) + + Return a :class:`Signature` (or its subclass) object for a given + :ref:`frame object `. + + .. versionadded:: 3.13 + .. class:: Parameter(name, kind, *, default=Parameter.empty, annotation=Parameter.empty) diff --git a/Lib/inspect.py b/Lib/inspect.py index 8a2b2c96e993b5..6b067f35f4a5ed 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -2419,16 +2419,29 @@ def _signature_from_function(cls, func, skip_bound_arg=True, Parameter = cls._parameter_cls # Parameter information. - func_code = func.__code__ + annotations = get_annotations(func, globals=globals, locals=locals, eval_str=eval_str) + return _signature_from_code( + func.__code__, + annotations=annotations, + defaults=func.__defaults__, + kwdefaults=func.__kwdefaults__, + cls=cls, + is_duck_function=is_duck_function, + ) + + +def _signature_from_code( + func_code, + *, + annotations, defaults, kwdefaults, + cls, is_duck_function, +): pos_count = func_code.co_argcount arg_names = func_code.co_varnames posonly_count = func_code.co_posonlyargcount positional = arg_names[:pos_count] keyword_only_count = func_code.co_kwonlyargcount keyword_only = arg_names[pos_count:pos_count + keyword_only_count] - annotations = get_annotations(func, globals=globals, locals=locals, eval_str=eval_str) - defaults = func.__defaults__ - kwdefaults = func.__kwdefaults__ if defaults: pos_default_count = len(defaults) @@ -3093,6 +3106,37 @@ def from_callable(cls, obj, *, follow_wrapper_chains=follow_wrapped, globals=globals, locals=locals, eval_str=eval_str) + @classmethod + def from_frame(cls, frame): + """Constructs Signature from a given frame object.""" + if not isframe(frame): + raise TypeError(f'Frame object expected, got: {type(frame)}') + + func_code = frame.f_code + pos_count = func_code.co_argcount + arg_names = func_code.co_varnames + keyword_only_count = func_code.co_kwonlyargcount + + defaults = [] + kwdefaults = {} + if frame.f_locals: + for name in arg_names[:pos_count]: + if name in frame.f_locals: + defaults.append(frame.f_locals[name]) + + for name in arg_names[pos_count : pos_count + keyword_only_count]: + if name in frame.f_locals: + kwdefaults.update({name: frame.f_locals[name]}) + + return _signature_from_code( + func_code, + annotations={}, + defaults=defaults, + kwdefaults=kwdefaults, + cls=cls, + is_duck_function=False, + ) + @property def parameters(self): return self._parameters diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 52cf68b93b85fa..05c94df769f3db 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -4623,6 +4623,96 @@ class D2(D1): self.assertEqual(inspect.signature(D2), inspect.signature(D1)) +class TestSignatureFromFrame(unittest.TestCase): + def test_signature_from_frame(self): + ns = {} + def inner(a, /, b, *e, c: int = 3, d, **f) -> None: + ns['fr'] = inspect.currentframe() + + inner(1, 2, d=4) + self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), + '(a=1, /, b=2, *e, c=3, d=4, **f)') + + def test_signature_with_pos_only_defaults(self): + ns = {} + def inner(a=1, /, b=2, *e, c: int = 3, d, **f) -> None: + ns['fr'] = inspect.currentframe() + + inner(d=4) + self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), + '(a=1, /, b=2, *e, c=3, d=4, **f)') + + def test_signature_from_frame_no_locals(self): + ns = {} + def inner(): + ns['fr'] = inspect.currentframe() + + inner() + self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), + '()') + + def test_signature_from_frame_no_pos(self): + ns = {} + def inner(*, a, b=2, **c): + ns['fr'] = inspect.currentframe() + + inner(a=1) + self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), + '(*, a=1, b=2, **c)') + + def test_signature_from_frame_no_kw(self): + ns = {} + def inner(a, /, b, *c): + ns['fr'] = inspect.currentframe() + + inner(1, 2) + self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), + '(a=1, /, b=2, *c)') + + def test_signature_from_frame_with_nonlocal(self): + fr = None + def inner(a, /, b, *c): + nonlocal fr + fr = inspect.currentframe() + + inner(1, 2) + self.assertEqual(str(inspect.Signature.from_frame(fr)), + '(a=1, /, b=2, *c)') + + def test_signature_from_method_frame(self): + ns = {} + class _A: + def inner(self, a, *, b): + ns['fr'] = inspect.currentframe() + def __repr__(self): + return '_A' + + _A().inner(1, b=2) + self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), + '(self=_A, a=1, *, b=2)') + + def test_signature_from_frame_defaults_change(self): + ns = {} + def inner(a=1, /, c=5, *, b=2): + a = 3 + ns['fr'] = inspect.currentframe() + b = 4 + + inner() + self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), + '(a=3, /, c=5, *, b=4)') + + def test_signature_from_frame_mod(self): + self.assertEqual(str(inspect.Signature.from_frame(mod.fr)), + '(x=11, y=14)') + self.assertEqual(str(inspect.Signature.from_frame(mod.fr.f_back)), + '(a=7, /, b=8, c=9, d=3, e=4, f=5, *g, **h)') + + def test_signature_from_not_frame(self): + with self.assertRaisesRegex(TypeError, 'Frame object expected'): + inspect.Signature.from_frame(lambda: ...) + + class TestParameterObject(unittest.TestCase): def test_signature_parameter_kinds(self): P = inspect.Parameter diff --git a/Misc/NEWS.d/next/Library/2024-03-09-12-55-44.gh-issue-108901.s9VClL.rst b/Misc/NEWS.d/next/Library/2024-03-09-12-55-44.gh-issue-108901.s9VClL.rst new file mode 100644 index 00000000000000..b95511723d3fd6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-03-09-12-55-44.gh-issue-108901.s9VClL.rst @@ -0,0 +1,3 @@ +Add :meth:`inspect.Signature.from_frame` to get a singature from a frame +object, it will be used as a new alternative for older +:func:`inspect.getargvalues` and :func:`inspect.formatargvalues`. From b434fbc759569d8f24f330eef2d6e0bf7ed45dd0 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sat, 9 Mar 2024 13:14:00 +0300 Subject: [PATCH 2/7] Improve docs --- Doc/library/inspect.rst | 6 ++++++ Doc/whatsnew/3.13.rst | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 0bfd18475e6461..30739130468a4b 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -822,6 +822,12 @@ function. Return a :class:`Signature` (or its subclass) object for a given :ref:`frame object `. + Notice that it is impossible to get signatures + with annotations from frames. + Because annotations are stored in functions. + Also note that default parameter values might + be modified inside the frame. + .. versionadded:: 3.13 diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 51939909000960..ebaf418ee875bb 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -361,6 +361,12 @@ and only logged in :ref:`Python Development Mode ` or on :ref:`Python built on debug mode `. (Contributed by Victor Stinner in :gh:`62948`.) +inspect +------- + +* Add :meth:`inspect.Signature.from_frame` to get signatures from frame objects + and to replace the old :func:`inspect.getargvalues` API. + ipaddress --------- From bbe3229db2176077f8ac964249e78edd93ff48b9 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sat, 9 Mar 2024 15:15:39 +0300 Subject: [PATCH 3/7] Improve docs --- Lib/inspect.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Lib/inspect.py b/Lib/inspect.py index 6b067f35f4a5ed..ebd71bb4073ab9 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -3108,7 +3108,15 @@ def from_callable(cls, obj, *, @classmethod def from_frame(cls, frame): - """Constructs Signature from a given frame object.""" + """ + Constructs Signature from a given frame object. + + Notice that it is impossible to get signatures + with annotations from frames. + Because annotations are stored in functions. + Also note that default parameter values might + be modified inside the frame. + """ if not isframe(frame): raise TypeError(f'Frame object expected, got: {type(frame)}') From 72905617dd93b0c6b35ff44284ce0e3230cbe724 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 10 Mar 2024 09:39:43 +0300 Subject: [PATCH 4/7] Address review --- Doc/library/inspect.rst | 9 +++++---- Lib/inspect.py | 16 ++++++++++----- Lib/test/test_inspect/test_inspect.py | 20 +++++++++---------- ...-03-09-12-55-44.gh-issue-108901.s9VClL.rst | 5 ++--- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 30739130468a4b..35e6a1cdfa4d1f 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -823,10 +823,11 @@ function. :ref:`frame object `. Notice that it is impossible to get signatures - with annotations from frames. - Because annotations are stored in functions. - Also note that default parameter values might - be modified inside the frame. + with annotations from frames, + because annotations are stored + in function inside :attr:`~function.__annotations__` attribute. + Also note that default value are populated from frame's variables, + not real function's default values. .. versionadded:: 3.13 diff --git a/Lib/inspect.py b/Lib/inspect.py index ebd71bb4073ab9..ec7151eab3cc4f 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -2419,7 +2419,12 @@ def _signature_from_function(cls, func, skip_bound_arg=True, Parameter = cls._parameter_cls # Parameter information. - annotations = get_annotations(func, globals=globals, locals=locals, eval_str=eval_str) + annotations = get_annotations( + func, + globals=globals, + locals=locals, + eval_str=eval_str, + ) return _signature_from_code( func.__code__, annotations=annotations, @@ -3112,10 +3117,11 @@ def from_frame(cls, frame): Constructs Signature from a given frame object. Notice that it is impossible to get signatures - with annotations from frames. - Because annotations are stored in functions. - Also note that default parameter values might - be modified inside the frame. + with annotations from frames, + because annotations are stored + in function inside ``__annotations__`` attribute. + Also note that default value are populated from frame's variables, + not real function's default values. """ if not isframe(frame): raise TypeError(f'Frame object expected, got: {type(frame)}') diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 05c94df769f3db..e44ca64aa421a2 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -4624,7 +4624,7 @@ class D2(D1): class TestSignatureFromFrame(unittest.TestCase): - def test_signature_from_frame(self): + def test_from_frame(self): ns = {} def inner(a, /, b, *e, c: int = 3, d, **f) -> None: ns['fr'] = inspect.currentframe() @@ -4633,7 +4633,7 @@ def inner(a, /, b, *e, c: int = 3, d, **f) -> None: self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), '(a=1, /, b=2, *e, c=3, d=4, **f)') - def test_signature_with_pos_only_defaults(self): + def test_from_frame_with_pos_only_defaults(self): ns = {} def inner(a=1, /, b=2, *e, c: int = 3, d, **f) -> None: ns['fr'] = inspect.currentframe() @@ -4642,7 +4642,7 @@ def inner(a=1, /, b=2, *e, c: int = 3, d, **f) -> None: self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), '(a=1, /, b=2, *e, c=3, d=4, **f)') - def test_signature_from_frame_no_locals(self): + def test_from_frame_no_locals(self): ns = {} def inner(): ns['fr'] = inspect.currentframe() @@ -4651,7 +4651,7 @@ def inner(): self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), '()') - def test_signature_from_frame_no_pos(self): + def test_from_frame_no_pos(self): ns = {} def inner(*, a, b=2, **c): ns['fr'] = inspect.currentframe() @@ -4660,7 +4660,7 @@ def inner(*, a, b=2, **c): self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), '(*, a=1, b=2, **c)') - def test_signature_from_frame_no_kw(self): + def test_from_frame_no_kw(self): ns = {} def inner(a, /, b, *c): ns['fr'] = inspect.currentframe() @@ -4669,7 +4669,7 @@ def inner(a, /, b, *c): self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), '(a=1, /, b=2, *c)') - def test_signature_from_frame_with_nonlocal(self): + def test_from_frame_with_nonlocal(self): fr = None def inner(a, /, b, *c): nonlocal fr @@ -4679,7 +4679,7 @@ def inner(a, /, b, *c): self.assertEqual(str(inspect.Signature.from_frame(fr)), '(a=1, /, b=2, *c)') - def test_signature_from_method_frame(self): + def test_from_method_frame(self): ns = {} class _A: def inner(self, a, *, b): @@ -4691,7 +4691,7 @@ def __repr__(self): self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), '(self=_A, a=1, *, b=2)') - def test_signature_from_frame_defaults_change(self): + def test_from_frame_defaults_change(self): ns = {} def inner(a=1, /, c=5, *, b=2): a = 3 @@ -4702,13 +4702,13 @@ def inner(a=1, /, c=5, *, b=2): self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), '(a=3, /, c=5, *, b=4)') - def test_signature_from_frame_mod(self): + def test_from_frame_mod(self): self.assertEqual(str(inspect.Signature.from_frame(mod.fr)), '(x=11, y=14)') self.assertEqual(str(inspect.Signature.from_frame(mod.fr.f_back)), '(a=7, /, b=8, c=9, d=3, e=4, f=5, *g, **h)') - def test_signature_from_not_frame(self): + def test_from_not_frame(self): with self.assertRaisesRegex(TypeError, 'Frame object expected'): inspect.Signature.from_frame(lambda: ...) diff --git a/Misc/NEWS.d/next/Library/2024-03-09-12-55-44.gh-issue-108901.s9VClL.rst b/Misc/NEWS.d/next/Library/2024-03-09-12-55-44.gh-issue-108901.s9VClL.rst index b95511723d3fd6..543a7e7452e3fe 100644 --- a/Misc/NEWS.d/next/Library/2024-03-09-12-55-44.gh-issue-108901.s9VClL.rst +++ b/Misc/NEWS.d/next/Library/2024-03-09-12-55-44.gh-issue-108901.s9VClL.rst @@ -1,3 +1,2 @@ -Add :meth:`inspect.Signature.from_frame` to get a singature from a frame -object, it will be used as a new alternative for older -:func:`inspect.getargvalues` and :func:`inspect.formatargvalues`. +Add :meth:`inspect.Signature.from_frame` to get a singature +from a frame object. From 76ae2ef13bf96f34064030ce63a567611c874d32 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 10 Mar 2024 10:46:18 +0300 Subject: [PATCH 5/7] Typos! --- Doc/library/inspect.rst | 2 +- Lib/inspect.py | 2 +- .../next/Library/2024-03-09-12-55-44.gh-issue-108901.s9VClL.rst | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 35e6a1cdfa4d1f..bc2aaa0edb4378 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -826,7 +826,7 @@ function. with annotations from frames, because annotations are stored in function inside :attr:`~function.__annotations__` attribute. - Also note that default value are populated from frame's variables, + Also note that default values are populated from frame's variables, not real function's default values. .. versionadded:: 3.13 diff --git a/Lib/inspect.py b/Lib/inspect.py index ec7151eab3cc4f..32178fa34451b0 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -3120,7 +3120,7 @@ def from_frame(cls, frame): with annotations from frames, because annotations are stored in function inside ``__annotations__`` attribute. - Also note that default value are populated from frame's variables, + Also note that default values are populated from frame's variables, not real function's default values. """ if not isframe(frame): diff --git a/Misc/NEWS.d/next/Library/2024-03-09-12-55-44.gh-issue-108901.s9VClL.rst b/Misc/NEWS.d/next/Library/2024-03-09-12-55-44.gh-issue-108901.s9VClL.rst index 543a7e7452e3fe..14f38d74083511 100644 --- a/Misc/NEWS.d/next/Library/2024-03-09-12-55-44.gh-issue-108901.s9VClL.rst +++ b/Misc/NEWS.d/next/Library/2024-03-09-12-55-44.gh-issue-108901.s9VClL.rst @@ -1,2 +1,2 @@ -Add :meth:`inspect.Signature.from_frame` to get a singature +Add :meth:`inspect.Signature.from_frame` to get a signature from a frame object. From bfe30fe8fa42ded0dc16a44053bd7b0cf97d2ea1 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 10 Mar 2024 11:01:30 +0300 Subject: [PATCH 6/7] Add tests for cleared frame --- Lib/test/test_inspect/test_inspect.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index e44ca64aa421a2..31dcbea8391ba7 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -4679,6 +4679,16 @@ def inner(a, /, b, *c): self.assertEqual(str(inspect.Signature.from_frame(fr)), '(a=1, /, b=2, *c)') + def test_clear_frame(self): + ns = {} + def inner(a=1, /, c=5, *, b=2): + ns['fr'] = inspect.currentframe() + + inner() + ns['fr'].clear() + self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), + '(a, /, c, *, b)') + def test_from_method_frame(self): ns = {} class _A: From 26b52910d15e6824a32975e3ce70ad00a47152cf Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 11 Mar 2024 10:05:35 +0300 Subject: [PATCH 7/7] Do not show defaults for parameters --- Doc/library/inspect.rst | 7 +++---- Doc/whatsnew/3.13.rst | 3 +-- Lib/inspect.py | 29 ++++++--------------------- Lib/test/test_inspect/test_inspect.py | 20 +++++++++--------- 4 files changed, 19 insertions(+), 40 deletions(-) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index bc2aaa0edb4378..3c85faa1bc6057 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -823,11 +823,10 @@ function. :ref:`frame object `. Notice that it is impossible to get signatures - with annotations from frames, + with defaults or annotations from frames, because annotations are stored - in function inside :attr:`~function.__annotations__` attribute. - Also note that default values are populated from frame's variables, - not real function's default values. + in a function inside ``__defaults__``, ``__kwdefaults__``, + and ``__annotations__`` attributes. .. versionadded:: 3.13 diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index ebaf418ee875bb..9574ed2e1864b8 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -364,8 +364,7 @@ built on debug mode `. inspect ------- -* Add :meth:`inspect.Signature.from_frame` to get signatures from frame objects - and to replace the old :func:`inspect.getargvalues` API. +* Add :meth:`inspect.Signature.from_frame` to get signatures from frame objects. ipaddress --------- diff --git a/Lib/inspect.py b/Lib/inspect.py index 32178fa34451b0..865fd401dc158e 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -3117,36 +3117,19 @@ def from_frame(cls, frame): Constructs Signature from a given frame object. Notice that it is impossible to get signatures - with annotations from frames, + with defaults or annotations from frames, because annotations are stored - in function inside ``__annotations__`` attribute. - Also note that default values are populated from frame's variables, - not real function's default values. + in a function inside ``__defaults__``, ``__kwdefaults__``, + and ``__annotations__`` attributes. """ if not isframe(frame): raise TypeError(f'Frame object expected, got: {type(frame)}') - func_code = frame.f_code - pos_count = func_code.co_argcount - arg_names = func_code.co_varnames - keyword_only_count = func_code.co_kwonlyargcount - - defaults = [] - kwdefaults = {} - if frame.f_locals: - for name in arg_names[:pos_count]: - if name in frame.f_locals: - defaults.append(frame.f_locals[name]) - - for name in arg_names[pos_count : pos_count + keyword_only_count]: - if name in frame.f_locals: - kwdefaults.update({name: frame.f_locals[name]}) - return _signature_from_code( - func_code, + frame.f_code, annotations={}, - defaults=defaults, - kwdefaults=kwdefaults, + defaults=(), + kwdefaults={}, cls=cls, is_duck_function=False, ) diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 31dcbea8391ba7..3af5731939e5bb 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -4631,7 +4631,7 @@ def inner(a, /, b, *e, c: int = 3, d, **f) -> None: inner(1, 2, d=4) self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), - '(a=1, /, b=2, *e, c=3, d=4, **f)') + '(a, /, b, *e, c, d, **f)') def test_from_frame_with_pos_only_defaults(self): ns = {} @@ -4640,7 +4640,7 @@ def inner(a=1, /, b=2, *e, c: int = 3, d, **f) -> None: inner(d=4) self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), - '(a=1, /, b=2, *e, c=3, d=4, **f)') + '(a, /, b, *e, c, d, **f)') def test_from_frame_no_locals(self): ns = {} @@ -4658,7 +4658,7 @@ def inner(*, a, b=2, **c): inner(a=1) self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), - '(*, a=1, b=2, **c)') + '(*, a, b, **c)') def test_from_frame_no_kw(self): ns = {} @@ -4667,7 +4667,7 @@ def inner(a, /, b, *c): inner(1, 2) self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), - '(a=1, /, b=2, *c)') + '(a, /, b, *c)') def test_from_frame_with_nonlocal(self): fr = None @@ -4677,7 +4677,7 @@ def inner(a, /, b, *c): inner(1, 2) self.assertEqual(str(inspect.Signature.from_frame(fr)), - '(a=1, /, b=2, *c)') + '(a, /, b, *c)') def test_clear_frame(self): ns = {} @@ -4694,12 +4694,10 @@ def test_from_method_frame(self): class _A: def inner(self, a, *, b): ns['fr'] = inspect.currentframe() - def __repr__(self): - return '_A' _A().inner(1, b=2) self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), - '(self=_A, a=1, *, b=2)') + '(self, a, *, b)') def test_from_frame_defaults_change(self): ns = {} @@ -4710,13 +4708,13 @@ def inner(a=1, /, c=5, *, b=2): inner() self.assertEqual(str(inspect.Signature.from_frame(ns['fr'])), - '(a=3, /, c=5, *, b=4)') + '(a, /, c, *, b)') def test_from_frame_mod(self): self.assertEqual(str(inspect.Signature.from_frame(mod.fr)), - '(x=11, y=14)') + '(x, y)') self.assertEqual(str(inspect.Signature.from_frame(mod.fr.f_back)), - '(a=7, /, b=8, c=9, d=3, e=4, f=5, *g, **h)') + '(a, /, b, c, d, e, f, *g, **h)') def test_from_not_frame(self): with self.assertRaisesRegex(TypeError, 'Frame object expected'):