mypy icon indicating copy to clipboard operation
mypy copied to clipboard

AnyStr in "(*param: Union[AnyStr, Any]) -> AnyStr" doesn't work

Open gamecss opened this issue 1 year ago • 10 comments

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

gamecss avatar Jun 04 '23 01:06 gamecss

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.

sobolevn avatar Jun 04 '23 11:06 sobolevn

I don't think mypy is technically wrong here. If we solve the typevar to str, both None and b"" will match Any.

JelleZijlstra avatar Jun 04 '23 13:06 JelleZijlstra

I don't think mypy is technically wrong here. If we solve the typevar to str, both None and b"" will match Any.

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.

gamecss avatar Jun 05 '23 12:06 gamecss

I apologize for repeatedly closing and reopening issues. It's too easy to hit the Close with comment button.

gamecss avatar Jun 05 '23 12:06 gamecss

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

ikonst avatar Jun 05 '23 13:06 ikonst

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.

gamecss avatar Jun 05 '23 14:06 gamecss

Whoops 🤦

ikonst avatar Jun 05 '23 15:06 ikonst

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 avatar Jun 05 '23 15:06 ikonst

@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.

erictraut avatar Jun 05 '23 16:06 erictraut

So, what should I do now?

gamecss avatar Jun 17 '23 04:06 gamecss

So, what should I do now?

Ok.... I think I should close this issue now.

gamecss avatar Aug 03 '23 02:08 gamecss