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

User-defined TypeGuard for Callable is coroutine function has errors #13710

Closed
EldarSehayekZenity opened this issue Sep 23, 2022 · 6 comments
Closed
Labels
bug mypy got something wrong topic-paramspec PEP 612, ParamSpec, Concatenate topic-typeguard TypeGuard / PEP 647

Comments

@EldarSehayekZenity
Copy link

EldarSehayekZenity commented Sep 23, 2022

Bug Report

Defining a TypeGuard for callable to check if coroutine doesn't work when passed a generic callable with Concatenate.

To Reproduce

  1. Write this code
_PArgs = ParamSpec("_PArgs")

def iscoroutinefunc(func: Callable[_PArgs, Any]) -> TypeGuard[Callable[_PArgs, Coroutine[Any, Any, Any]]]:
    return inspect.iscoroutinefunction(func)
  1. Call the type guard like this:
_PRouteParams = ParamSpec("_PRouteParams")
async def my_coro(x: int, y: int, z: int) -> str:
        await asyncio.sleep(1)
        return str(x + y + z)

    async def execute_if_coro(
        my_func_or_coro: Callable[Concatenate[int, _PRouteParams], Any], *args: _PRouteParams.args, **kwargs: _PRouteParams.kwargs
    ) -> None:
        if iscoroutinefunc(my_func_or_coro):
            await my_func_or_coro(3, *args, **kwargs)
        else:
            my_func_or_coro(3, *args, **kwargs)

    await execute_if_coro(my_coro, 5, z=6)
  1. Run mypy on above code with "strict=True" setting

Expected Behavior

I expected mypy to infer in execute_if_coro that my_func_or_coro can receive an a positional integer before the additional *args and **kwargs and that the result will be awaitable using await keyword.

Actual Behavior

Mypy reports 2 errors

  • Argument 1 to "iscoroutinefunc" has incompatible type "Callable[[int, **_PRouteParams], Any]"; expected "Callable[[int, **_PRouteParams], Any]
  • Argument 1 has incompatible type "int"; expected "[int, **_PRouteParams.args]"

Your Environment

  • Mypy version used: 0.971
  • Mypy command-line flags: N/A
  • Mypy configuration options from mypy.ini (and other config files):

follow_imports = "normal"
ignore_errors = false
implicit_reexport = false
warn_redundant_casts = true
warn_unused_ignores = true
disallow_any_generics = true
disallow_untyped_defs = true
check_untyped_defs = true
allow_redefinition = false
local_partial_types = true
strict_optional = true
strict_equality = true
warn_unused_configs = true
warn_unreachable = true
warn_no_return = true
no_implicit_optional = true
strict = true

  • Python version used: 3.10.4
  • Operating system and version: Windows 11
@EldarSehayekZenity EldarSehayekZenity added the bug mypy got something wrong label Sep 23, 2022
@JelleZijlstra JelleZijlstra added topic-typeguard TypeGuard / PEP 647 topic-paramspec PEP 612, ParamSpec, Concatenate labels Sep 23, 2022
@A5rocks
Copy link
Contributor

A5rocks commented Feb 22, 2023

Just as a note, I've used this code to try to test this:

from typing import ParamSpec, Callable, TypeGuard, Any, Coroutine, Concatenate
import inspect
import asyncio

_PArgs = ParamSpec("_PArgs")

def iscoroutinefunc(func: Callable[_PArgs, Any]) -> TypeGuard[Callable[_PArgs, Coroutine[Any, Any, Any]]]:
    return inspect.iscoroutinefunction(func)

_PRouteParams = ParamSpec("_PRouteParams")
async def main() -> None:
    async def my_coro(x: int, y: int, z: int) -> str:
        await asyncio.sleep(1)
        return str(x + y + z)

    async def execute_if_coro(
        my_func_or_coro: Callable[Concatenate[int, _PRouteParams], Any], *args: _PRouteParams.args, **kwargs: _PRouteParams.kwargs
    ) -> None:
        if iscoroutinefunc(my_func_or_coro):
            await my_func_or_coro(3, *args, **kwargs)
        else:
            my_func_or_coro(3, *args, **kwargs)

    await execute_if_coro(my_coro, 5, z=6)

(Just posting so I do not need to change the code next time I check. Needed to add the imports and the async main + indentation.)

@EldarSehayekZenity
Copy link
Author

@A5rocks Thank you very much for your PR I see all checks are approved will it be merged and released soon?

@A5rocks
Copy link
Contributor

A5rocks commented Apr 10, 2023

Hi :)

I haven't put in time recently to the PR but I have 2 main things I need to do before it can really be looked at for real:

  1. Add a regression test for all fixed issues.
  2. Go through mypy-primer's output and either fix things or say why the new change is correct.

I hope to have that done in a few days!

@A5rocks
Copy link
Contributor

A5rocks commented May 15, 2023

FYI this is fixed as of Mypy 1.2! Turns out some other changed fixed this, oops.

@hauntsaninja
Copy link
Collaborator

hauntsaninja commented May 15, 2023

mypy_primer --bisect -p test.py --debug --old v1.0.0 bisects the change in behaviour to your #14677 :-)

@JelleZijlstra
Copy link
Member

Should we add a new test case for this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-paramspec PEP 612, ParamSpec, Concatenate topic-typeguard TypeGuard / PEP 647
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants