typing_extensions icon indicating copy to clipboard operation
typing_extensions copied to clipboard

TypeError: Parameter list cannot be empty for TypeVarTuple

Open EmmanuelMess opened this issue 2 years ago • 2 comments

There is a problem with typing_extensions that doesn't happen with typing. Running the program with python3.11 works, but with python3.10 with typing_extensions throws:

Traceback (most recent call last):
  File ".../main.py", line 48, in <module>
    main()
  File ".../main.py", line 40, in main
    signal: Signal[()] = Signal[()]()
  File "/usr/lib/python3.10/typing.py", line 312, in inner
    return func(*args, **kwds)
  File "/usr/lib/python3.10/typing.py", line 1328, in __class_getitem__
    raise TypeError(
TypeError: Parameter list to Signal[...] cannot be empty

from __future__ import annotations

from typing_extensions import Callable, TypeVarTuple, Generic, Unpack, List, TypeVar

VarArgs = TypeVarTuple('VarArgs')


class Signal(Generic[Unpack[VarArgs]]):

    def __init__(self):
        self.functions: List[Callable[..., None]] = []
        """
        Simple mechanism that allows abstracted invocation of callbacks. Multiple callbacks can be attached to a signal
        so that they are all called when the signal is emitted.
        """

    def connect(self, function: Callable[..., None]):
        """
        Add a callback to this Signal
        :param function: callback to call when emited
        """
        self.functions.append(function)

    def emit(self, *args: Unpack[VarArgs]):
        """
        Call all callbacks with the arguments passed
        :param args: arguments for the signal, must be the same type as type parameter
        """
        for function in self.functions:
            if args:
                function(*args)
            else:
                function()


def main():
    def signalEmptyCall():
        print("Hello!")

    signal: Signal[()] = Signal[()]()

    signal.connect(signalEmptyCall)

    signal.emit()


if __name__ == '__main__':
    main()

EmmanuelMess avatar Jan 18 '24 23:01 EmmanuelMess

I leave a quick comment here.

The problem is that in 3.10 typing.Generic(and earlier?) there is a check:

        if not params and cls is not Tuple:
             raise TypeError(f"Parameter list to {cls}[...] cannot be empty")

As params is an empty tuple the TypeError is raised. We either need to monkeypatch typing or create a custom version of Generic - as there are multiple is Generic tests in typing I am not sure how easy it is to create a stable alternative version.

Afaik, the only workaround that can be used before 3.11 is to use the equivalent form of ()Unpack[Tuple[()]]:

signal: Signal[Unpack[Tuple[()]]]

Daraan avatar Dec 15 '24 11:12 Daraan

We could make a typing_extensions.Generic that is different from typing.Generic and fixes this issue.

JelleZijlstra avatar Dec 16 '24 03:12 JelleZijlstra