Skip to content

Overloads do not take type: ignore into account #12454

Closed

Description

@A5rocks

Bug Report

When matching overloads, a type: ignore is not passed into the underlying type check.

To Reproduce
Have this code:

from typing import Awaitable, Generic, TypeVar, overload

_R_co = TypeVar("_R_co", covariant=True)
_R = TypeVar("_R")

class Job(Generic[_R_co]):
    ...

@overload
def run_job(job: Job[Awaitable[_R]], a: int) -> _R | None: ...
@overload
def run_job(job: Job[Awaitable[_R] | _R], a: int) -> _R | None: ...

def run_job(job: Job[Awaitable[_R] | _R], a: int) -> _R | None: ...

job: Job[Awaitable[None] | None]

run_job(
    job,
    "",   # type: ignore[arg-type]
)

# "" is a stand-in for an invalid type.
# Passing an int would make the error go away.

Expected Behavior

Type checks fine.

Actual Behavior

repro.py:18: error: No overload variant of "run_job" matches argument types "Job[Optional[Awaitable[None]]]", "str"
repro.py:18: note: Possible overload variants:
repro.py:18: note:     def [_R] run_job(job: Job[Awaitable[_R]], a: int) -> Optional[_R]
repro.py:18: note:     def [_R] run_job(job: Job[Union[Awaitable[_R], _R]], a: int) -> Optional[_R]

Your Environment

  • Mypy version used: 81994f1 (based off master)
  • Mypy command-line flags: none
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: 3.10.2
  • Operating system and version: Windows 11

Originally reported by @cdce8p in #11847.

Activity

  1. erictraut commented on Mar 26, 2022

    @erictraut

    A # type: ignore comment simply suppresses errors on that line. It is not intended to "propagate" in any way.

  2. A5rocks commented on Mar 26, 2022

    @A5rocks
    CollaboratorAuthor

    Ah sorry, I was being a bit unclear with my terminology: not sure how to say it exactly but that probably shouldn't fail (function -> overload shouldn't be a breaking change, imo).

    Mainly, I'm referring to this that mypy uses to check overloads are ok not taking type ignores into account:

    mypy/mypy/checkexpr.py

    Lines 1763 to 1771 in 14de8c6

    ret_type, infer_type = self.check_call(
    callee=typ,
    args=args,
    arg_kinds=arg_kinds,
    arg_names=arg_names,
    context=context,
    arg_messages=overload_messages,
    callable_name=callable_name,
    object_type=object_type)

    If it should fail, my bad!

  3. DevilXD commented on Mar 26, 2022

    @DevilXD

    The error comes from line 18, which starts with run_job(. What about using a # type: ignore on that line instead? Like so:

    run_job(  # type: ignore
        job,
        "",
    )
  4. cdce8p commented on Mar 26, 2022

    @cdce8p
    Collaborator

    The error comes from line 18, which starts with run_job(. What about using a # type: ignore on that line instead? Like so:

    run_job(  # type: ignore
        job,
        "",
    )

    That doesn't solve the issue

    error: Argument 1 to "run_job" has incompatible type "Job[[int], Optional[Awaitable[None]]]"; expected "Job[[int], Awaitable[None]]"
    error: Argument 2 to "run_job" has incompatible type "str"; expected "int"
    

    The first error is still wrong and now mypy doesn't ignore the second one anymore.

    The only option other option would be this

        run_job(job, "")  # type: ignore[arg-type]

    That would simply hide both errors instead of resolving the underlying issue. In the particular case it wouldn't be possible due to formatting constraints.

    --
    The tests are done with #11847 since mypy doesn't yet support generic ParamSpec.

  5. cdce8p commented on Mar 27, 2022

    @cdce8p
    Collaborator

    Thought about this some more. I'm not sure how mypy, or any other type checker, is supposed to choose between overloads if one argument type is incorrect. Overloads simply depend on the arg-types. An even simpler example

    @overload
    def func(var: int) -> int: ...
    @overload
    def func(var: float) -> str: ...
    def func(var: float) -> int | str: ...
    
    reveal_type(func(
        "Hello"  # type: ignore[arg-type]
    ))

    What should mypy infer here? Currently it's Any, probably the best we can do. Pyright infers Unknown which is also an option.

    I think we can close this issue.

  6. JelleZijlstra commented on Mar 27, 2022

    @JelleZijlstra
    Member

    Maybe I'm missing something, but I also don't think there's a bug here. mypy passes if you use this instead:

    run_job(  # type: ignore[call-overload]
        job,
        "",
    )

    As @erictraut said, there is no notion of "passing" a type ignore into a call; the comment simply suppresses type errors on that line. In this case, we generate the error on the first line of the call, so that's where you have to put the ignore.

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

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions