Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type checks missed for Callable[[str], str] #245

Closed
jas-ho opened this issue Mar 25, 2022 · 5 comments
Closed

Type checks missed for Callable[[str], str] #245

jas-ho opened this issue Mar 25, 2022 · 5 comments

Comments

@jas-ho
Copy link

jas-ho commented Mar 25, 2022

check_type applied to callables seems to only care about the number of arguments and not its type.

To Reproduce

from typing import Callable

import typeguard


def function(a: str) -> str:
    return a


if __name__ == "__main__":
    typeguard.check_type("function", function, Callable[[str], str])  # passes expectedly

    # next three lines should all raise TypeError since either input or return type (or both) do not match
    typeguard.check_type("function", function, Callable[[int], str])  # passes unexpectedly
    typeguard.check_type("function", function, Callable[[str], int])  # passes unexpectedly
    typeguard.check_type("function", function, Callable[[int], int])  # passes unexpectedly

Expected behavior
The example above should fail at typeguard.check_type("function", function, Callable[[int], str]).

Additional context
Version: typeguard==2.13.3

What already works are checks that the number of arguments matches the expectation:

    # next line raises TypeError as expected:
    # "callable passed as function has too many arguments in its declaration; expected 0 but 1 argument(s) declared"
    typeguard.check_type("function", function, Callable[[], str])  # this raises TypeError as expected
    # next line raises TypeError as expected:
    # "callable passed as function has too few arguments in its declaration; expected 2 but 1 argument(s) declared"
    typeguard.check_type("function", function, Callable[[str, str], str])
@agronholm
Copy link
Owner

Such type checks would have to compare annotations against annotations which is better covered by static type checkers. It's huge effort to implement such checks in typeguard, and I'm not entirely sure it's possible. This is a duplicate of #12 anyway.

@jas-ho
Copy link
Author

jas-ho commented Mar 25, 2022

Hi @agronholm , thanks a lot for your quick reply and for the work on typeguard!

@jas-ho
Copy link
Author

jas-ho commented Mar 25, 2022

I totally understand your arguments for why this is not an important issue for typeguard. But let me maybe add some context (which might be interesting for other typeguard users as well).

better covered by static type checkers

I agree with you in case the functions are available outside of runtime.

In the case, I was looking at that was not the case since the functions were dynamically instantiated based on some config. Of course, there might still be other ways of guaranteeing that the instantiated function objects end up respecting the desired interface. typeguard was just the first thing I found which looked like it might do the trick.

I guess a naive but workable alternative could be to just try to call the functions with desired inputs and assert on the output types.

@agronholm
Copy link
Owner

I totally understand your arguments for why this is not an important issue for typeguard

It would be, but the extraordinary effort required to implement checks of annotations vs annotations was too much.

I agree with you in case the functions are available outside of runtime.

The vast majority of functions are visible to static type checkers.

In the case, I was looking at that was not the case since the functions were dynamically instantiated based on some config

What does "instantiating" mean for functions?

I guess a naive but workable alternative could be to just try to call the functions with desired inputs and assert on the output types.

Do you mean wrapping the callables with something like @typechecked, but which would check against the Callable[...]?

@jas-ho
Copy link
Author

jas-ho commented Apr 1, 2022

What does "instantiating" mean for functions?

sorry for confusing language. what I really mean is sth like (*)

def get_function(function_name: str) -> Callable[[X], Y]:
    """Returns the function named `function_name` from the library of functions."""
    ...

What I want is a check that what get_function gives me is indeed of type Callable[[X], Y]

Do you mean wrapping the callables with something like @TypeChecked, but which would check against the Callable[...]?

given the context explained above what I meant was something as naive as

candidate = get_function("my_func")
try:
    y = candidate(x) # where x is some test object of class X
    typeguard.check_type("y", y, Y)
except TypeError:
    raise TypeError("function `candidate` is not of expected type Callable[[X], Y]")

does that explain better what I had in mind?

(*) note that, in reality, get_function would have additional parameters to configure the function (or callable class instance) to be returned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants