Skip to content

Commit

Permalink
pythongh-108901: Add skip_bound_arg to Signature.from_callable()
Browse files Browse the repository at this point in the history
…and `signature()`
  • Loading branch information
sobolevn committed Mar 10, 2024
1 parent 5b2f21f commit 290e1ad
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 4 deletions.
11 changes: 9 additions & 2 deletions Doc/library/inspect.rst
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ and its return annotation. To retrieve a :class:`!Signature` object,
use the :func:`!signature`
function.

.. function:: signature(callable, *, follow_wrapped=True, globals=None, locals=None, eval_str=False)
.. function:: signature(callable, *, follow_wrapped=True, skip_bound_arg=True, globals=None, locals=None, eval_str=False)

Return a :class:`Signature` object for the given *callable*:

Expand Down Expand Up @@ -693,6 +693,10 @@ function.
.. versionchanged:: 3.10
The *globals*, *locals*, and *eval_str* parameters were added.

.. versionchanged:: 3.13
The *skip_bound_arg* parameter was added.
Pass ``False`` to keep the ``self`` parameter in a signature.

.. note::

Some callables may not be introspectable in certain implementations of
Expand Down Expand Up @@ -796,7 +800,7 @@ function.

.. versionadded:: 3.13

.. classmethod:: Signature.from_callable(obj, *, follow_wrapped=True, globals=None, locals=None, eval_str=False)
.. classmethod:: Signature.from_callable(obj, *, follow_wrapped=True, skip_bound_arg=True, globals=None, locals=None, eval_str=False)

Return a :class:`Signature` (or its subclass) object for a given callable
*obj*.
Expand All @@ -817,6 +821,9 @@ function.
.. versionchanged:: 3.10
The *globals*, *locals*, and *eval_str* parameters were added.

.. versionchanged:: 3.13
The *skip_bound_arg* parameter was added.


.. class:: Parameter(name, kind, *, default=Parameter.empty, annotation=Parameter.empty)

Expand Down
8 changes: 8 additions & 0 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,14 @@ and only logged in :ref:`Python Development Mode <devmode>` or on :ref:`Python
built on debug mode <debug-build>`.
(Contributed by Victor Stinner in :gh:`62948`.)

inspect
-------

* Add *skip_bound_arg* parameter to :func:`inspect.Signature.from_callable`
and :func:`inspect.signature`, pass ``False``
to keep the ``self`` parameter in a signature.
(Contributed by Nikita Sobolev in :gh:`108901`.)

ipaddress
---------

Expand Down
8 changes: 6 additions & 2 deletions Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -3087,10 +3087,12 @@ def __init__(self, parameters=None, *, return_annotation=_empty,

@classmethod
def from_callable(cls, obj, *,
follow_wrapped=True, globals=None, locals=None, eval_str=False):
follow_wrapped=True, skip_bound_arg=True,
globals=None, locals=None, eval_str=False):
"""Constructs Signature for the given callable object."""
return _signature_from_callable(obj, sigcls=cls,
follow_wrapper_chains=follow_wrapped,
skip_bound_arg=skip_bound_arg,
globals=globals, locals=locals, eval_str=eval_str)

@property
Expand Down Expand Up @@ -3357,9 +3359,11 @@ def format(self, *, max_width=None):
return rendered


def signature(obj, *, follow_wrapped=True, globals=None, locals=None, eval_str=False):
def signature(obj, *, follow_wrapped=True, skip_bound_arg=True,
globals=None, locals=None, eval_str=False):
"""Get a signature object for the passed callable."""
return Signature.from_callable(obj, follow_wrapped=follow_wrapped,
skip_bound_arg=skip_bound_arg,
globals=globals, locals=locals, eval_str=eval_str)


Expand Down
59 changes: 59 additions & 0 deletions Lib/test/test_inspect/test_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -4622,6 +4622,65 @@ class D2(D1):

self.assertEqual(inspect.signature(D2), inspect.signature(D1))

def test_signature_skip_bound_arg_method(self):
def decorator(func):
@functools.wraps(func) # set `__wrapper__` attribute
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper

class My:
def method(self, arg: int) -> None: ...
@decorator
def decorated(self, arg: int) -> None: ...
@classmethod
def cl(cls, arg2: str) -> None: ...
@staticmethod
def st(arg1: str) -> bool: ...

for follow_wrapped in (True, False):
sigs = {
My.method: '(self, arg: int) -> None',
My().method: '(self, arg: int) -> None',
My.decorated: (
'(self, arg: int) -> None'
if follow_wrapped else
'(*args, **kwargs) -> None'
),
My().decorated: (
'(self, arg: int) -> None'
if follow_wrapped else
'(*args, **kwargs) -> None'
),
My.cl: '(cls, arg2: str) -> None',
My().cl: '(cls, arg2: str) -> None',
My.st: '(arg1: str) -> bool',
My().st: '(arg1: str) -> bool',
}

for func in (inspect.signature, inspect.Signature.from_callable):
for fixture, text_sig in sigs.items():
with self.subTest(
fixture=fixture,
func=func,
follow_wrapped=follow_wrapped,
):
sig = func(
fixture,
follow_wrapped=follow_wrapped,
skip_bound_arg=False,
)
self.assertEqual(str(sig), text_sig)

def test_signature_skip_bound_arg_function(self):
def compare(self: object, other: object) -> bool: ...

for func in (inspect.signature, inspect.Signature.from_callable):
with self.subTest(func=func):
sig = func(compare, skip_bound_arg=False)
self.assertEqual(str(sig),
'(self: object, other: object) -> bool')


class TestParameterObject(unittest.TestCase):
def test_signature_parameter_kinds(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Add *skip_bound_arg* keyword-only parameter to
:func:`inspect.Signature.from_callable` and :func:`inspect.signature`. Pass
``skip_bound_arg=False`` to keep the ``self`` parameter in a signature.

0 comments on commit 290e1ad

Please sign in to comment.