Open wadetregaskis-linkedin opened 3 years ago
I can't repro the code provided passing type checking on any (mypy, python) version:
~/tmp λ cat test65.py
stringy = "foo"
f"""\
{stringy.nope()}
""" # type: ignore
~/tmp λ python3.7 -m mypy --version
mypy 0.782
~/tmp λ python3.7 -m mypy test65.py
test65.py:4: error: "str" has no attribute "nope"
Found 1 error in 1 file (checked 1 source file)
~/tmp λ python3.6 -m mypy --version
mypy 0.782
~/tmp λ python3.6 -m mypy test65.py
test65.py:4: error: "str" has no attribute "nope"
Found 1 error in 1 file (checked 1 source file)
~/tmp λ python3.7 -m pip install mypy==0.770
[...]
~/tmp λ python3.7 -m mypy --version
mypy 0.770
~/tmp λ python3.7 -m mypy test65.py
test65.py:4: error: "str" has no attribute "nope"
Found 1 error in 1 file (checked 1 source file)
Two workarounds: you can alias the expression before your f-string or use cast(Any, stringy).nope()
.
If there is an issue here, it's probably going to be a wontfix, because mypy gets the location of type-ignores from stdlib's AST in 3.8.
So you're saying multiline format strings are always going behave this way now? That kinda says that they're just fundamentally incompatible with mypy… that seems… suboptimal, to say the least. :(
First and foremost, I'm saying I can't repro your issue (that the behaviour of mypy changes here when using different Python versions).
Sure, and I don't know why that is. But what you're saying is that for you this happens with all versions of Python. That's even worse!
I did indeed ultimately have to pull out virtually all the embedded expressions, into separate variables, so I could # type: ignore
each one. It added an entire extra screen of boilerplate code, on top of being very tedious to do. Sure, not the worst thing in the world, but it's particularly surprising in a language like Python that otherwise encourages readability & brevity.
You're right, I probably shouldn't have been so hasty to close. I took a look at the code and there seems to be opportunity to lie about lineno's of JoinedStr values in fastparse.
That said, if you find that there is a repro that passes on some (mypy, python_version) combination, that would be great and increases the chance this issue has a happy ending.
I tried figuring out what's special about my local setup, but wasn't able to - it's a bunch of LinkedIn-specific & -internal stuff, with plenty of complexity, so it's neither all that surprising that the behaviour is different, nor trivial to figure out why.
And important follow-up though re. workarounds: I didn't know no_type_check
existed, until you (@hauntsaninja) mentioned it on #9483. Just to reiterate what I also said there: that's a much better workaround than the others I knew of. It's not quite as nice as what I requested here, but it's close - I just have to wrap the code in question in a dummy, nested function in order to be able to apply the decorator. Silly enough that it requires a comment documenting it, but quite functional otherwise.
I do see a slight difference between 3.7-, and 3.8+. Up to 3.7, the error points at the t
of stringy.nope()
. But for 3.8 and later, it points to the t
. Maybe that's a clue to something?
(The major difference is that when mypy is run using 3.7, the parser is typed_ast, while with 3.8+ it's the stdlib ast module.)
Just FYI, this is still broken with Python 3.9.x (w/ mypy 0.790 & 0.800, at least).
This is still broken with Python 3.11.2 (w/ mypy 1.0.1).
Once PEP 701 (https://peps.python.org/pep-0701) is accepted you should be able to put the comment inside the interpolation.
Just ran into the same issue, thanks @gvanrossum for pointing out the PEP!
Looks like Python 3.12 does indeed allow comments inside of an f-string and mypy picks them up. So, @wadetregaskis-linkedin’s example above now works:
stringy = "foo"
f"""{
stringy.nope() # type: ignore
}"""
Note, however, the placement of the curly braces!
(ignore that this is a true positive - my real code is much more complicated and is actually valid, the above is just a contrived example to show the core problem)
In Python 3.7 with mypy 0.782, it reports the error on the closing
"""
(if you remove the# type: ignore
), so it picks up the ignore directive and all is well. In Python 3.8, it reports the error on the previous line. There's no way to add# type: ignore
on that line, and adding it anywhere else has no effect.In other cases it tends to report the error as being on the line with the opening
"""
… I'm not sure why in this particular, contrived example it behaves differently. But either way, it's a major problem, since there's no practical workaround for this [in my much larger, real-world code]. It's blocking the upgrade to Python 3.8 for one of my major projects.(the output from mypy w/ Python 3.8)