# Documented features and implementation details around \_\_signature\_\_

**URL:** <https://discuss.python.org/t/documented-features-and-implementation-details-around-signature/47282>\
**Category:** Documentation\
**Created:** [March 1, 2024, 10:13am UTC](https://discuss.python.org/t/documented-features-and-implementation-details-around-signature/47282 "2024-03-01T10:13:17Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Gouvernathor](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gouvernathor/32/7957_2.png) [@Gouvernathor](https://discuss.python.org/u/Gouvernathor)\
**Post date:** [March 1, 2024, 10:13am UTC](https://discuss.python.org/t/documented-features-and-implementation-details-around-signature/47282/1 "2024-03-01T10:13:17Z")

</div>

A bit of a recap.

In PEP362, support for inspecting callable signatures was added. It described the ` __signature__ ` attribute as an override to the `inspect.signature` function : any object found in ` __signature__ ` and not None would be returned by inspect.signature.  
But in fact, since 2014 apparently, a type guard allowing only instances of Signature to pass, or else raise an exception. (None is treated as if the attribute isn’t present.)  
None of this ended up in the documentation.

More than a year ago, [the behavior changed](https://github.com/python/cpython/issues/100039) (in a backwards-compatible way) by gaining undocumented support for strings and callables, as a special shoehorn for Enum. That’s something I’m trying to roll back, but that’s not the point here.

Not knowing that, in october 23, I authored [a merge request](https://github.com/python/cpython/pull/106311) to document the ` __signature__ ` as it was specified in the PEP. It was accepted and merged, but it was inaccurate as it did not account for the type guard or for the later support of strings and callables.

As a response to this discrepancy, the documentation [was patched](https://github.com/python/cpython/pull/116086) to write it all off as an implementation detail, advising people to see the exact semantics in the code, and demoting the ability to pass a Signature object as ` __signature__ ` from a feature to an implementation detail.

I think it should be re-documented as a feature in the following way :

- Signature objects in ` __signature__ ` are returned as-is by the inspect.signature function
- None is ignored and the behavior is as if ` __signature__ ` was not set
- Behavior for any other value is an implementation detail.

That way, uses of ` __signature__ ` as a cache or as a way to hide undocumented parameters remains guaranteed, while allowing the strings and callables support to be decided and to evolve separately.

Pull request [#116124](https://github.com/python/cpython/pull/116124) is my proposition.

@erlendaasland @skirpichev who most intervened about this lately.

---

<div class="post-metadata">

**Author:** ![skirpichev](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/skirpichev/32/10996_2.png) [@skirpichev](https://discuss.python.org/u/skirpichev)\
**Post date:** [March 1, 2024, 11:51am UTC](https://discuss.python.org/t/documented-features-and-implementation-details-around-signature/47282/2 "2024-03-01T11:51:20Z")

</div>

> [@Gouvernathor](#):
>
> More than a year ago, [the behavior changed](https://github.com/python/cpython/issues/100039) (in a backwards-compatible way) by gaining undocumented support for strings and callables, as a special shoehorn for Enum.

JFR, that special logic could be reverted back, keeping added features for the Enum. Patch: [gh-115937: remove extra processing for the \_\_signature\_\_ attribute by skirpichev · Pull Request #115984 · python/cpython · GitHub](https://github.com/python/cpython/pull/115984)

Also, years after the [PEP 362](https://peps.python.org/pep-0362/), the type check was added to solve [inspect.signature doesn't always return a signature · Issue #66000 · python/cpython · GitHub](https://github.com/python/cpython/issues/66000). Perhaps, a managed attribute that check type in the setter could be better as a solution: then the exception will be raised in a right time and in a less surprising place.

Is there reasons why we would like to keep more complex handling for this special attribute?

Given above, the documentation could be rephrased as: “If the object has a ` __signature__ ` attribute and if it is not `None`, this function returns it.” Just as it was designed in the PEP, IIUIC.

> [@Gouvernathor](#):
>
> uses of ` __signature__ ` as a cache or as a way to hide undocumented parameters remains guaranteed

Also, while in practice, probably, this will be tedious and inconvenient (see [Signatures for extension modules (PEP draft)](https://discuss.python.org/t/signatures-for-extension-modules-pep-draft/43914), for some alternative) — this attribute actually the only way how people could support introspection for extension modules.

But it worth to note, that the PEP 362 has limitations, the inspect module can’t yet represent functions with multiple signatures (see e.g. [Signatures, a call to action](https://discuss.python.org/t/signatures-a-call-to-action/23580)). Maybe this is an argument for we shouldn’t expose ` __signature__ ` as public? Wouldn’t be the ` __signatures__ ` as “list of Signature objects or None” - better?

Sorry, if this a bit off-topic and changes in logic shouldn’t be discussed here. @erlendaasland suggested me to start jet another thread on d.p.o, but I think it will be overkill:)

---

<div class="post-metadata">

**Author:** ![Gouvernathor](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gouvernathor/32/7957_2.png) [@Gouvernathor](https://discuss.python.org/u/Gouvernathor)\
**Post date:** [March 1, 2024, 1:38pm UTC](https://discuss.python.org/t/documented-features-and-implementation-details-around-signature/47282/3 "2024-03-01T13:38:23Z")

</div>

I too think discussion on the merits of potential behavior changes to ` __signature__ ` should be kept off-topic. As you mentioned, it is being discussed elsewhere, with an associated PEP draft, and I wish here to offer a stopgap change that will document the current behavior in the best possible way, and be compatible with some (though not all) possible future changes.

> [@skirpichev](#):
>
> But it worth to note, that the PEP 362 has limitations, the inspect module can’t yet represent functions with multiple signatures (see e.g. [Signatures, a call to action](https://discuss.python.org/t/signatures-a-call-to-action/23580)). Maybe this is an argument for we shouldn’t expose ` __signature__ ` as public? Wouldn’t be the ` __signatures__ ` as “list of Signature objects or None” - better?

First, I’d like to point out that the inspect module doesn’t fully and natively support C-defined functions (there are ways around it, notably ` __text_signature__ `).  
Second, maybe apart from these C-defined functions which I don’t know enough about, so in normal Python, “functions with multiple signatures” don’t exist. There are functions where one parameter changes its meaning based upon other parameters, and you may want to _document_ these functions using several signatures (the best example is `range`), but as an _introspection_ question, multiple signatures simply do not exist. It may be (I don’t know) that the “call to action” thread is leaning on introducing multiple signatures, but that’s not the current state of the language.  
So, the documentation shouldn’t account for speculative changes, in my opinion.  
In addition, even if a list of signature were to be supported for a single callable, with the ` __signatures__ ` you mentioned, the function to return that would be `inspect.signatures`, not `inspect.signature`. So that seems like a moot point to me.

> [@skirpichev](#):
>
> Given above, the documentation could be rephrased as: “If the object has a ` __signature__ ` attribute and if it is not `None`, this function returns it.” Just as it was designed in the PEP, IIUIC.

For now, the type check is in place in the function, so that sentence (which I wrote, lol) is inaccurate. If it ever were to change in the way you wish, the phrasing I’m proposing now in #116124 would _remain true_ : if you put a Signature object, it will be returned by the function, if you put None it will be ignored, if you (somehow) put something else that’s not documented.

---

<div class="post-metadata">

**Author:** ![pitrou](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pitrou/32/28_2.png) [@pitrou](https://discuss.python.org/u/pitrou)\
**Post date:** [March 1, 2024, 10:51pm UTC](https://discuss.python.org/t/documented-features-and-implementation-details-around-signature/47282/4 "2024-03-01T22:51:47Z")

</div>

> [@Gouvernathor](#):
>
> There are functions where one parameter changes its meaning based upon other parameters, and you may want to _document_ these functions using several signatures (the best example is `range`), but as an _introspection_ question, multiple signatures simply do not exist.

This is making a distinction that is not very useful IMHO. Users of introspection are interested in the signature(s) that a callable actually supports (i.e. can be successfully called with), not in _how_ the function was physically defined.

I.e. if a function was physically defined as taking `(*args, **kwargs)` but accepts a logical signature of `(x, y, *, some_option=None)` (for example because it’s doing argument parsing by hand), I as an introspection user am interested in the latter information, not the former.

That said, I agree that multiple signatures are a distraction from other more pressing concerns, such as documenting actual ` __signature__ ` and ` __text_signature__ ` semantics, and making it easier for C extensions to expose correct signatures.

---

<div class="post-metadata">

**Author:** ![Gouvernathor](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gouvernathor/32/7957_2.png) [@Gouvernathor](https://discuss.python.org/u/Gouvernathor)\
**Post date:** [March 4, 2024, 11:55am UTC](https://discuss.python.org/t/documented-features-and-implementation-details-around-signature/47282/5 "2024-03-04T11:55:18Z")

</div>

> [@pitrou](#):
>
> Users of introspection are interested in the signature(s) that a callable actually supports (i.e. can be successfully called with), not in _how_ the function was physically defined.
> 
> I.e. if a function was physically defined as taking `(*args, **kwargs)` but accepts a logical signature of `(x, y, *, some_option=None)` (for example because it’s doing argument parsing by hand), I as an introspection user am interested in the latter information, not the former.

(We’re drifting slightly off-topic but this is interesting.)  
Yes, I agree. That’s why I support the use of ` __signature__ ` to white-lie about a function’s signature, potentially to hide certain parameters and so on.

My point is slightly different : there’s a lot of different ways to document signatures in a human-readable way : one is documenting optional parameters with `=` and the default value, one uses square brackets to denote optional parameters without specifying their value, and another would use a list of signatures.

All three are perfectly valid to use in documentation, provided some measure of consistency, but the fact that only the first one has a syntax support in Python language is the distinction which, in my opinion, sets it apart from the others.

---

<div class="post-metadata">

**Author:** ![Gouvernathor](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gouvernathor/32/7957_2.png) [@Gouvernathor](https://discuss.python.org/u/Gouvernathor)\
**Post date:** [March 26, 2024, 11:47am UTC](https://discuss.python.org/t/documented-features-and-implementation-details-around-signature/47282/6 "2024-03-26T11:47:23Z")

</div>

> [@pitrou](#):
>
> more pressing concerns, such as documenting actual ` __signature__ `

So, can we move forward with this ? Is there comments, feedback, objections to the way I propose features and implementation details should be split, or to the phrasing I’m using in the pull request ?

---

<div class="post-metadata">

**Author:** ![skirpichev](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/skirpichev/32/10996_2.png) [@skirpichev](https://discuss.python.org/u/skirpichev)\
**Post date:** [March 29, 2024, 1:01pm UTC](https://discuss.python.org/t/documented-features-and-implementation-details-around-signature/47282/7 "2024-03-29T13:01:14Z")

</div>

> [@Gouvernathor](#):
>
> Is there comments, feedback, objections to the way I propose features and implementation details should be split, or to the phrasing I’m using in the pull request ?

Hmm, maybe you should also describe an impact of ` __signature__ ` attribute on _follow\_wrapped_ parameter of the `inspect.signature()`. This is mentioned somehow in the `inspect.unwrap()` docstring, but I doubt it’s the right place…

---

<div class="post-metadata">

**Author:** ![Gouvernathor](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gouvernathor/32/7957_2.png) [@Gouvernathor](https://discuss.python.org/u/Gouvernathor)\
**Post date:** [March 29, 2024, 8:10pm UTC](https://discuss.python.org/t/documented-features-and-implementation-details-around-signature/47282/8 "2024-03-29T20:10:30Z")

</div>

That’s true. However, the behavior regarding that is weird :  
The unwrapping process will stop at the first callable having a ` __signature__ ` attribute. That is, if that attribute has _any_ value, **including** None, when a None value is otherwise ignored…  
In other words, that None value has the effect of stopping the unwrapping recursive process, but if the None value is ignored when interpreting a callable’s signature, then it should be ignored all the way.

This started being only a doc issue, but I think this tiny detail should be changed with it… if only to make the behavior consistent. It only requires changing the following :

> <https://github.com/python/cpython/blob/01bd74eadbc4ff839d39762fae6366f50c1e116e/Lib/inspect.py#L2541>

to using `(lambda f: (getattr(f, " __signature__", None) is not None)` and the following lines unchanged.

---

<div class="post-metadata">

**Author:** ![skirpichev](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/skirpichev/32/10996_2.png) [@skirpichev](https://discuss.python.org/u/skirpichev)\
**Post date:** [March 30, 2024, 3:38am UTC](https://discuss.python.org/t/documented-features-and-implementation-details-around-signature/47282/9 "2024-03-30T03:38:35Z")

</div>

I don’t think it’s “weird”, rather this piece of code assumed, that if this attribute is set — it’s value is a correct `Signature` object. This constraints could be enforced by an appropriate setter for this magic attribute. Slightly simpler logic for ` __signature__ ` processing, that has on a pros also: you will get a `TypeError` exception from the setter, right at the time you break things, not from the `inspect.signature()`.

---

<div class="post-metadata">

**Author:** ![Gouvernathor](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gouvernathor/32/7957_2.png) [@Gouvernathor](https://discuss.python.org/u/Gouvernathor)\
**Post date:** [March 30, 2024, 10:33am UTC](https://discuss.python.org/t/documented-features-and-implementation-details-around-signature/47282/10 "2024-03-30T10:33:01Z")

</div>

That would be backwards-incompatible, and also magic.  
It breaks the feature of having `clbl. __signature__ = None` to make sure that the true signature of the callable is computed - since `del clbl. __signature__ ` will fail when the attribute wasn’t already set.  
By being also magic it feels bigger than such a small change requires.

---

<div class="post-metadata">

**Author:** ![ego-thales](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ego-thales/32/25719_2.png) [@ego-thales](https://discuss.python.org/u/ego-thales)\
**Post date:** [April 18, 2025, 9:47am UTC](https://discuss.python.org/t/documented-features-and-implementation-details-around-signature/47282/11 "2025-04-18T09:47:23Z")

</div>

Hi there,

A bit late to the punch, but I’m a good old +2 in favor of properly documenting ` __signature__ ` and not leaving it as an “implementation detail” that could change. For reference, here’s the [current implementation](https://github.com/python/cpython/blob/f3d877a27abca355f9d05decf3e2ce0874983288/Lib/inspect.py#L2444-L2454) and what the current doc today says:

```python
try:
    sig = obj. __signature__
except AttributeError:
    pass
else:
    if sig is not None:
        if not isinstance(sig, Signature):
            raise TypeError(
                'unexpected object {!r} in __signature__'
                'attribute'.format(sig))
        return sig

```

> **CPython implementation detail:** If the passed object has a ` __signature__ ` attribute, we may use it to create the signature. The exact semantics are an implementation detail and are subject to unannounced changes. Consult the source code for current semantics.

In order or priority, I think that:

1. it would be wise to at least **guarantee users** that if `obj. __signature__ ` returns a `Signature` object, then it is returned as is by `inspect.signature`;
2. it should be specified that `None` value is ignored;
3. I guess there’s still some debate over the handling of non`None` and non`Signature` values.
