Skip to content

Decorator infers Never when using protocols #18877

Closed as not planned
Closed as not planned
@brandonchinn178

Description

@brandonchinn178

I'm trying to add types to a decorator that can be used on methods in classes containing a database_store property. The decorator logs the start/end time to the instance's database_store.

For simplicity, I'll just use a logging.Logger here. (mypy playground)

import logging
import time
from typing import *

class Loggable(Protocol):
    logger: logging.Logger

P = ParamSpec("P")
T = TypeVar("T", covariant=True)
class TimedFunc(Protocol, Generic[P, T]):
    def __call__(_self, self: Loggable, *args: P.args, **kwargs: P.kwargs) -> T:
        ...

def timer(func: TimedFunc[P, T]) -> TimedFunc[P, T]:
    def wrapper(self: Loggable, *args: P.args, **kwargs: P.kwargs) -> T:
        self.logger.info(f"{func} - Start {time.time()}")
        resp = func(self, *args, **kwargs)
        self.logger.info(f"{func} - End {time.time()}")
        return resp
    return wrapper

class Foo:
    def __init__(self) -> None:
        self.logger = logging.getLogger()

    @timer
    def bar(self, value: str) -> str:
        return value

This results in an error

main.py:26: error: Argument 1 to "timer" has incompatible type "Callable[[Foo, str], str]"; expected "TimedFunc[Never, Never]"  [arg-type]
main.py:26: note: "TimedFunc[Never, Never].__call__" has type "Callable[[Arg(Loggable, 'self'), VarArg(Never), KwArg(Never)], Never]"

It works if I hardcode self: Foo instead of self: Loggable.

  1. Why are the type variables being inferred as Never?
  2. Is this intended to work, or am I doing something wrong?

Other things I tried

  • I tried using Concatenate[Loggable, P], but with --strict, this forces self to be a positional argument, and I don't want to update all the methods to do def foo(self, /, ...).

Possibly related

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions