mypy
mypy copied to clipboard
AnyStr in "(*param: Union[AnyStr, Any]) -> AnyStr" doesn't work
Bug Report (*_: Union[AnyStr]) -> AnyStr doesn't work.
(A clear and concise description of what the bug is.)
To Reproduce (mypy-play link)
from typing import AnyStr, Union, Any
from typing_extensions import reveal_type
def abc(*param: Union[AnyStr, Any]) -> AnyStr:
...
reveal_type(abc(None, b"wjsj"))
Expected Behavior reveal_type(abc(None, b"")) # Revealed type is "builtins.bytes"
Actual Behavior reveal_type(abc(None, b"")) # Revealed type is "builtins.str"
Your Environment
- Mypy version used: 1.3.0
- Mypy command-line flags: Nope
- Mypy configuration options from
mypy.ini
(and other config files): Nope - Python version used: Python 3.10.6
The use-case is not quite clear to me. What do you expect to happen for:
-
abc(1, 1)
? -
abc('a', b'b')
?
I agree that the revealed type does not seem ideal, but the input is not correct as well.
I don't think mypy is technically wrong here. If we solve the typevar to str, both None
and b""
will match Any
.
I don't think mypy is technically wrong here. If we solve the typevar to str, both
None
andb""
will matchAny
.
But pyright works right. It guessed the result of abc(anything, b"") is bytes. 2) Any? Sometimes I'm going to use some untyped libraries. Then, mypy will think untyped_library.some_variable is Any anyway. So,
abc(anything, "") # str
abc(anything, b"") # bytes
but if I wrote abc(anything, b"")
, mypy said it's str.
I apologize for repeatedly closing and reopening issues. It's too easy to hit the Close with comment button.
It appears to be dependent on the order of the TypeVar constraints: https://mypy-play.net/?mypy=latest&gist=6fbea6ae3ff6123c5b05b901dffec4b5
We could either:
- evaluate all matches and somehow rank them?
- fail if multiple constraints match, unless type context disambiguates?
IMO it's unclear how to rank, and any ranking we come up with will be somewhat arbitrary and not minimize astonishment. Failing ambiguous cases will help minimize astonishment.
~For some reason I couldn't get pyright to work:~ Pyright: https://pyright-playground.decorator-factory.su/?gzip=H4sIAAfxfWQC_32OQQrCMBBF9znF7JpILTbLgkIv4Cq4EZGIk1CwSZlGIeDhDWkFi-LsPn9m3jPkewhx6JyFrh88BeCtiyWoOOBBk2BM1bB9R16ouihhDFTCJQYcU39FA4avdANp8wnpWsB6l0LDIA1huJMDfdycGCN8oL6dExC54XvvMD_jQoifXWbklim50JDF3OYHs4adNOSnhpw0qqpa4u0fvP3CvwCsxtBoKAEAAA%3D%3D
Something wrong with the code. Change reveal_type(f(None, str))
or reveal_type(f(None, bytes))
to reveal_type(f(None, str()))
or reveal_type(f(None, bytes()))
.
Modified code works.
Whoops 🤦
Now that I think about it, the behavior is telling about pyright's design: when there are multiple matches, they decide Unknown (having Unknown is a major difference between pyright and mypy) rather than picking the first match.
@erictraut Does this difference have to do with what's described in Constraint Solver: Ambiguous Solution Scoring?
@ikonst, yes, the behavior you're seeing here in pyright is the result of the "ambiguous solution scoring" heuristics in its constraint solver.
A union that includes an Any
is unusual and creates an ambiguity here. It would be reasonable for a type checker to assume that all values passed to *param
are compatible with Any
, so the AnyStr
type variable should go unsolved. It appears that's what mypy is doing here. I think that behavior is defensible, although perhaps not what the user expects.
However, this code is hitting a known bug in mypy where it incorrectly handles unsolved constrained type variables. Rather than evaluating an unsolved constrained TypeVar as Any
, it instead evaluates it as the first constraint type (in this case, str
). That's clearly wrong and will result in both false negatives and false positives.
So, what should I do now?
So, what should I do now?
Ok.... I think I should close this issue now.