I'm learning how to create GUIs with TKinter in Python. I'm using VSCode as my editor and I have this piece of code:
[...]
# create a button and bind an action to the button press event
button:tk.Button = tk.Button(master=gridFrame, text="Button")
button.bind("<ButtonPress-1>", self.play)
[...]
def play(self, event: tk.Event)->None:
"""Hanlde a player's move"""
clickedBtn: tk.Button = event.widget
Pylance complains on the type hint for event being of type Event
: Expected type arguments for generic class "Event". I've tried with this other hint:
def play(self, event: tk.Event[tk.Button])->None:
Which silences the warning, but when I try to run the code with this, I get a TypeError exception:
def play(self, event: Event[tk.Button])->None:
~~~~~^^^
TypeError: type 'Event' is not subscriptable
Is there a way to hint this or should I settle for Any
or silence the warning and admit defeat?
Thanks!
STerliakov already provided you with a sufficient solution in the comments.
Since type-hints existed in python they are are always evaluated as well (*). It is possible that there are stubs (.pyi) files defining generic types that are not generic at runtime. This is the situation you encounter: the type-checker expects a substriction, however at runtime it is not valid.
Solution 1
Immediate evaluation has many problems: NameError
s, circular-imports, efficiency, ...; to circumvent this PEP 563 allowed stringified annotations:
def play(self, event: 'tk.Event[tk.Button]') -> None: ...
Solution 2
In parallel from __future__ import annotations
was introduced that treats all annotations in the file as strings from a runtime perspective stored. They are stored in the __annotations__
attribute.
from __future__ import annotations
def play(self, event: tk.Event[tk.Button]) -> None: ...
print(play.__annotations__)
{'event': 'tk.Event[tk.Button]', 'return': 'None'}
In Python 3.14
The long deferred PEP 649 – Deferred Evaluation Of Annotations Using Descriptors is implemented. Annotations are not evaluated immediately. Runtime invalid annotations are valid. They only raise an error when accessed, depending on the method and provided globals. For example, accessing __annotations__
for the first time (but not before) will try to evaluate the annotation, i.e. only then will tk.Event
be subcripted.
If you do not want access to the actual annotations this behavior can be ignored. For a type-checker it is the same as Solution 1 and 2.
Footnote:
(*) the only exceptions are annotations inside a functions body, which have zero influence on the runtime.