Remove extra processing from __signature__ attribute?

See gh-116110: remove extra processing for the __signature__ attribute by skirpichev · Pull Request #116234 · python/cpython · GitHub.

Extra processing was added in [Enum] `__text_signature__` of `EnumType.__call__` and derivatives · Issue #100039 · python/cpython · GitHub by @stoneleaf. But it seems, that same effect could be reached without this. As current docs says “The exact semantics are an implementation detail and are subject to unannounced changes.” - thus, I suggest reverting back old version (just type check, see pr).

CC @Gouvernathor

3 Likes

If this removes __signature__ attr str support, it would be useful to have _signature_fromstr made public.

For what? If to describe signatures in extension modules, than the semi-private attribute __signature__ is not better than the semi-private attribute __text_signature__, which is used in the CPython to do this. We need a better public mechanism here, see Type Signatures for extension modules (PEP draft) for some recent discussion.

Ah ok, makes sense. I think this is a good idea. No need for __signature__ to support str if __text_signature__ does that. Makes things more explicit and simple.

1 Like

I like the alternative approach. Thanks, @skirpichev!

Merged in 3.14.

3 Likes

I wish I had seen this sooner, there’s the utility of only importing inspect in decorators that transform functions that’s being removed by not allowing it to be set to a callable in 3.14+

def taskcache[**P, R](
    ttl: float | None = None,
    *,
    cache_transform: CacheTransformer | None = None,
) -> Deco[P, R]:
    
    def wrapper(coro: TaskCoroFunc[P, R]) -> TaskFunc[P, R]:
        @wraps(coro, assigned=_WRAP_ASSIGN)
        def wrapped(*args: P.args, **kwargs: P.kwargs) -> asyncio.Task[R]:
            ...

        def deferred_sig():
            # important part here, import only paid for if introspected.
            import inspect  
            sig = inspect.signature(coro)
            if inspect.iscoroutinefunction(coro):
                new_ret_ann = (
                    asyncio.Task
                    if sig.return_annotation is inspect.Signature.empty
                    else asyncio.Task[sig.return_annotation]
                )

                return sig.replace(return_annotation=new_ret_ann)
            return sig
        wrapped.__signature__ = deferred_sig  
        return wrapped
    return wrapper

I can probably work around this in an ugly way by overriding __subclass_check__ on a custom class in 3.14+ and continuing to rely on an undocumented internal behavior, but that’s not ideal