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

Overload fallback for literal booleans allows forbidden literal signatures #13893

Closed
eyaler opened this issue Oct 14, 2022 · 2 comments
Closed
Labels
bug mypy got something wrong

Comments

@eyaler
Copy link

eyaler commented Oct 14, 2022

I am not sure if this is a mypy issue or is a limitation of PEP 586. I want to use boolean literals to forbid a specific combination of function parameter types and boolean flag values. In my example data is AnyStr, but if data_must_be_bytes=True it must be bytes. (In my real use case data_must_be_bytes is actually data_is_image; and I want to allow either bytes for images, or AnyStr for non images)

As indicated in PEP 586 and mypy docs, I need to add a fallback to allow dealing with the original bool type. However, this fallback will also allow the forbidden combination where data is str and data_must_be_bytes=True

Assuming that the the necessity of the fallback is out of this discussion's scope, I guess I either need a way explicitly state in the fallback that data_must_be_bytes is bool except Literal[True], or have mypy not do the fallback from Literal[True] to bool when a narrower overload is given. Without a way to overcome this issue, there seems to be no benefit in using overloading and literals here, over simpler type annotations. It would also be useful to have the docs call out such cases where the added complexity is redundant.

from typing import overload, Literal, AnyStr


@overload
def f(data: bytes, data_must_be_bytes: Literal[True]) -> int: ...


# redundant due to following fallback:
#@overload
#def f(data: AnyStr, data_must_be_bytes: Literal[False]) -> int: ...


# necessary fallback for non literals:
@overload
def f(data: AnyStr, data_must_be_bytes: bool) -> int: ...


def f(data, data_must_be_bytes):
    return 0

f(data='string', data_must_be_bytes=False)
f(data=b'bytes', data_must_be_bytes=False)
f(data='string', data_must_be_bytes=True) # this passes but i want it to fail
f(data=b'bytes', data_must_be_bytes=True)
f(data='string', data_must_be_bytes=bool(0))
f(data=b'bytes', data_must_be_bytes=bool(0))
f(data='string', data_must_be_bytes=bool(1))
f(data=b'bytes', data_must_be_bytes=bool(1))

mypy-play

@eyaler eyaler added the bug mypy got something wrong label Oct 14, 2022
@JelleZijlstra
Copy link
Member

You may be interested in the TypingError proposal: python/typing#1043. It would provide a way to tell the type checker that a particular parameter combination should produce an error.

Also, you probably should use str | bytes, not AnyStr. AnyStr is a type variable, and type variables are generally not meaningful when used only in one place in a signature.

@erictraut
Copy link

I think mypy is doing the right thing here. Recommend closing.

@JelleZijlstra JelleZijlstra closed this as not planned Won't fix, can't repro, duplicate, stale Aug 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

3 participants