Skip to content

Add type annotations for callbacks #2042

@NMertsch

Description

@NMertsch

What is the problem or limitation you are having?

toga.Button's on_press parameter is annotated as callable|None, but it calls this callable like f(widget), so the callable has to accept a parameter:

import toga
from toga.style import Pack
from toga.style.pack import COLUMN


class HelloWorld(toga.App):
    def startup(self) -> None:
        main_box = toga.Box(style=Pack(direction=COLUMN))

        button = toga.Button("Say Hello!", on_press=self.say_hello)
        main_box.add(button)

        self.main_window = toga.MainWindow(title=self.formal_name)
        self.main_window.content = main_box
        self.main_window.show()

    def say_hello(self) -> None:
        self.main_window.info_dialog("Hello!", "Hello!")


def main() -> None:
    return HelloWorld()

Clicking the button leads to an error, because say_hello has an incompatible signature.

Since on_press is not annotated as e.g. Callable[[Widget], None], static type checkers like mypy or PyCharm don't catch this error.

Describe the solution you'd like

I'd like on_pressed to be annotated as Callable[[Widget], Any], Callable[[Button], None], or whatever fits best and prevents such errors (I'm not deeply familiar with toga semantics).

Describe alternatives you've considered

One could also inspect the callable and only pass the widget as parameter if the signature expects a parameter.

This would also prevent runtime issues with callbacks not requiring a reference to the calling widget, which I think is the most common source for signature mismatches here.

However, a callback like show_number_dialog(x: int) -> None would still not be flagged by static type checking.

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew features, or improvements to existing features.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions