-
-
Notifications
You must be signed in to change notification settings - Fork 10.9k
TYP: regression between 2.1.3 and 2.2.0 (mypy only) #27957
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
Comments
We're seeing these failures in http://github.com/google/jax/ as well. |
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
Much simpler reproducer: import numpy as np
x = np.zeros(1)
x = x / 1 results in:
If I try: import numpy as np
x = np.zeros(1, dtype=float)
x = x / 1 I get:
Trying: import numpy as np
x = np.zeros(1, dtype=np.float32)
x = x / 1 gives:
Similarly, this: import numpy as np
x = np.zeros(1, dtype=np.float64)
x = x / 1 gives:
I have no idea how to annotate this in a way that will work. |
That's unrelated to this issue. Both the left- and the right-hand-side have Since numpy 2.2.0, from typing import Any
import numpy as np
f: np.floating[Any]
f8: np.float64
f = f8 # accepted
f8 = f # rejected on >=2.2.0, accepted on <2.2.0 So
Pyright gives a more helpful error message in this case:
So the result of To make this example work without typing errors, this is what you could do import numpy as np
import numpy.typing as npt
rng = np.random.default_rng(27446968)
n_states = 5
P = rng.random(size=(n_states, n_states))
p: npt.NDArray[np.floating]
p = rng.random(n_states)
p = P.T @ p |
This comment was marked as outdated.
This comment was marked as outdated.
This is yet another issue, and it has to do with shape-typing instead. While technically this isn't a bug, I'll admit that it's very annoying. The But currently, the division operator doesn't take shape-typing into account, so Anyway, this is how you can fix it to work with both mypy and pyright: import numpy as np
import numpy.typing as npt
x: npt.NDArray[np.floating]
x = np.zeros(1)
x = x / 1 You're other examples, @adamjstewart, can also be fixed in the same way. Feel free to open a separate issue about these "suboptimally annotated arithmetic operator types". |
Could you be more specific? |
I temporarily pinned NumPy 2.1 yesterday to fix the CI errors, but jax-ml/jax#25381 shows the errors under NumPy 2.2. In particular, this one prompted me to go searching for this issue:
I now see that this is probably the intended behavior: it's no longer safe to assume that an array is an array; every array annotation must now be specialized on its shape (as I said in the other thread, I suspect this will break a lot of valid code). |
To be quite honest, we've been talking about giving up on mypy & static type checking in JAX, because the signal-to-noise ratio of type failures is far too low. This change in NumPy might be the final nail in the coffin for that. |
It will by definition only break code that type-checkers (mypy in this case) deems invalid. So in a way, these errors have always been there, but numpy 2.2.0 made it so they now come to light. As my earlier answer showed, such cases are often easy to work around. And I agree that the mypy error messages are cryptic and difficult to read. And instead of giving u 8000 p on python typing altogether, perhaps you could switching from mypy to pyright @jakevdp. At the very least, you'd get better error messages, but it also helps a lot if you don't have to deal with all of those mypy bugs anymore (see https://github.com/erictraut/mypy_issues). |
I understand that the change is considered technically more accurate, but from the SP lecture notes' perspective (where I got my example), we'd like to write code that is simple and correct, but without using type annotations. This change makes that quite a bit harder to do. |
Is this a sign that the numerical computing community in Python is giving up on mypy as a standard type checker? Your responses here certainly could be read that way. Is that your intent? |
That's not necessarily the motivation behind these changes. There has been huge demand for shape-typing support (see e.g. #16544), and these changes are the steps we need to take towards realizing that goal. |
We should work toward implementing features that users are asking for. But if the cost of that is that simple & correct un-annotated code is now incorrectly flagged as a type error, I would contend that it's the implementation that is wrong, not the user code. |
There's no hidden agenda or something. It's just that I know first-hand how frustrating it can be to run into mypy bugs time and time again, especially in cases when mypy is the sole reason that I'm not able to implement a good idea. There are also many examples where mypy is preventing bugs from being fixed in typeshed stubs for the python standard library. So what I'm trying to say is that I'd hate to see people stop typing their code because of their frustrations with mypy. I'm convinced that typing could prevent many bugs, so getting rid of it could have serious consequences for libraries as popular as jax. So that's why I'm trying to show that there are alternatives that could make everyone's life a bit easier 🤷🏻 |
That's not quite right, because when you assign a 1-d array to a 2-d one in your example, you're doing something that's type-unsafe, i.e. something that can lead to bugs. In this case, the only way to prevent this, is using shape-typing. |
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
Hello! I help maintain mypy and several other projects in the Python typing ecosystem. Static type checking in Python is far from perfect, which is why I've volunteered a lot of my time to these projects. There's a lot going on in this issue. As far as I can tell, the concrete issues here are:
mypy sees several orders of magnitude more usage than basedpyright. So it seems less than ideal for users of both mypy and numpy to run into issues. Maybe folks could post concrete issues in python/mypy#18343 ? Like many open source projects, mypy is entirely driven by volunteer effort, but at least this way folks can track what needs to be done, what configuration needs to be used, or identify cases where the meaning of type annotations is underspecified. Regarding charris' comment about expertise in typing. If you post at https://github.com/python/typing/discussions or https://discuss.python.org/c/typing/32 you're likely to attract more eyeballs from folks who can bend typing to their will. Another thing that could be good is type checking downstream projects that depend on numpy in numpy's CI. I set up downstream regression checks for mypy, pyright, typeshed, basedpyright, etc and it's proven quite useful at avoiding regrettable regressions. Also real quick — I'd also like to contend with KotlinIsland's description of themselves as "closely involved" with mypy. Their primary involvement is maintaining a mypy fork, with very few substantive upstream changes. Projects should of course stand on their merits — I just want to provide some context for the claims to authority in that comment. |
In #27767 I intentionally use
That's unrelated to this issue, and enabling
The results from a recent survey (https://discuss.python.org/t/python-typing-survey-results/71821) with 1677 participants showed that ~60% used mypy, and ~34% used pyright.
See #28054 |
I count 21 merged PR's, 6 open PR's, 151 open issues, and 117 closed issues. Describing that as "very few substantive upstream changes" is not only disrespectful to @KotlinIsland, but to everyone that has invested a similar amount of their time into trying to improve mypy. It should also be noted that the majority of @KotlinIsland's involvement with the mypy codebase is through the work he's done for |
Wait, what is happening. If anything here is disrespectful to mypy contributors, it's statements like:
My intention with briefly mentioning that KotlinIsland's primary involvement with mypy was their fork and linking to their upstream PRs was to make it clear that statements like ^ aren't the opinion of people who actually maintain upstream mypy. (You're of course welcome to your opinion — like I said above, projects should stand on their merits. Just worth clarifying the source of that opinion's involvement with upstream, so people don't think it's the equivalent of say, rgommers saying that numpy is "beyond fixing". I wouldn't have said anything if they led with "As someone who forked mypy / As a long time user of mypy / As someone who has contributed several CI improvements to mypy" instead) Someone shared this link with me, so I came here to see if there was something I could do to help. For worse or for better, mypy has 7x monthly downloads of pyright and 230x basedpyright on PyPI, so it would be good for the community if things just worked. Feel free to post in python/mypy#18343 if there's something I could help with in my volunteer time. Unsubscribing from this issue. |
I found this issue when running into the following problem and chatted with hauntsaninja about it: import numpy
from numpy.typing import NDArray
def f() -> NDArray[numpy.float64]:
return numpy.zeros(2, dtype=numpy.float64) + numpy.float64(1.0) # error: Incompatible return value type (got "ndarray[tuple[int, ...], dtype[floating[Any]]]", expected "ndarray[tuple[int, ...], dtype[float64]]") [return-value] First, a general remark. Statements like this
are not only wrong but insulting. Wrong, because Mypy has supported property setters for ages. It only does not support different getter and setter types (I guess this is what jorenham refers to). Insulting (and also wrong), because it suggests that people who contributed to Mypy are trying to mislead the Python community. In the case of different setter types, there was a change in the perception of the desired behaviour. This change is now tricky to implement because Mypy's property support is not consistently programmed with its descriptor support for historical reasons. I do not want to go into more details, but I want to clarify there is no active deception but only limited time. And usually, one (or at least me) finds fixing problems in their free time more attractive than documenting them. But now to the actual topic. In my (and, I think, also hauntsaninja's) perception, this issue is about incomplete transitions in shape typing ( The only other hint pointing to another concrete topic I could find is this one:
But, again, I have the feeling you might have encountered a bug but are overgeneralising. Mypy supports from typing import Unpack
x: tuple[int, int, Unpack[tuple[int, ...]]]
x = (1,) # error: Incompatible types in assignment (expression has type "tuple[int]", variable has type "tuple[int, int, *tuple[int, ...]]") [assignment]
x = (1, 2, 3, 4)
reveal_type(x) # note: Revealed type is "tuple[builtins.int, builtins.int, builtins.int, builtins.int]" I work with Numpy and Mypy (and sometimes try to improve the latter a little), so I want them to work well together. I hope for a constructive discussion. |
I can reproduce this on both pyright and mypy, and this indeed used to be accepted on
When I say that mypy doesn't support something, I mean that within the context of typing, because that's what we're talking about here, and that's what mypy is meant to check. class A:
@property
def thing(self) -> str:
return "thing"
@thing.setter
def thing(self, thing: int, /) -> None:
assert isinstance(thing, int)
a = A()
a.thing = "unsafe" # mypy allows this https://mypy-play.net/?mypy=latest&python=3.12&gist=c74a929091e366135306081ea0965098 Mypy explicitly allows this type-unsafe behaviour, but rejects e.g.
I never mentioned individuals or a group of contributors. I'm purely talking about the software itself. So I don't A3E2 see why you think that I suggesting this. I thought it would go without saying, but I applaud anyone who contributes to open source, and actively try to motivate people to do so. Mypy is of course no exception to it.
No. I've explained this several times now. This issue isn't about shape-typing. And shape-typing isn't yet supported in numpy, so you shouldn't expect it to work.
This issue isn't about inheritance. It's about how
So that's why
I just checked this again with the latest mypy, and it seems to work now. So it appears my knowledge was outdated, and it's supported now 👌🏻. |
Yes, gladly,
from __future__ import annotations
def get(self: C) -> int:
return 1
class C:
g = property(get)
y = C().g + "x" There was a general attempt to improve the situation for all type checker maintainers and Python users (by turning As hauntsaninja mentioned, Static type checking in Python is far from perfect and things are complicated, so oversimplified "by the way accusations" are usually not helpful. But coming back to the main point of this issue (which you seem to have the authority to decide). From the perspective of all numpy users: What would be Mypy's preferred behaviour? I still guess they would want x = True
x = 1 # maybe okay
y: bool = True
y = 1 # maybe not okay What do you think? |
Yes, I agree that this would be the best way. IMO special casing should only be used in, well, special cases. But
I completely agree that static type checking is very difficult, especially in a gradual type system like the one Python uses. And it's no secret that mypy has a lot more issues than pyright has. See https://github.com/erictraut/mypy_issues for example, for a thorough analysis of this. So what So the way I see it, is that I'm not saying something new, and not accusing anyone of anything. Admittedly, I have my frustrations in dealing with mypy for five years or so, and we've seen here that I'm not the only one. But by no means did I intend to insult anyone; my frustrations are with the tool itself, and I having nothing but respect for those that try to make it better.
I agree that this would be the preferred behaviour in this case. The The Coincidentally, pyright already supports this: It accepts
|
That's not really the way things work here. I'm just "the typing guy", and most of what I do in open source is related to static typing in Python. And just like any other NumPy maintainer, I try to do what's best for numpy and its users. In the end, we all decide together. |
with a: bool = True
a = 1 # error
# but
b: bool = True
b = int(b) # no error where as pyright will never allow any redefinitions: a: bool = True # error: declaration is obscured
a: int = 1 pyright will allow a variable without an annotation to have assignments that can change the type, as it's not considered: a = 1
a = "" # no error the minuta of the issues that mypy has with this can be demonstrated: a: str = "a"
print(a) # use `a` to allow mypy redefinition
a: int
reveal_type(a) # mypy: int, runtime: "a" also, mypy doesn't support reassignments across branching logic: a = True
if bool():
a = 1 # mypy: error expected bool
else:
a = "a" # mypy: error expected bool
reveal_type(a) # mypy: bool, pyright: Literal[1, "a"] additionally, I never intended to mislead anyone into thinking I was a mypy collaborator/maintainer. I didn't state as much but I can understand that it could be interpreted that way. I have spent many years developing (based)mypy every day, so I would consider my experience quite a bit more than "has contributed several ci improvements" additionally-additionally, when I said "mypy is beyond saving", I wasn't trying to personally insult anyone, what I meant was that the issues in mypy are so deep in the implementation that it would require rewriting it to resolve them I have great respect for open source, Python, mypy and all of its collaborators and contributors. I detest the vitriolic, spiteful and downright nasty not niceness of certain maintainers that exist in this world, so I'm sorry if anything I said was taken personally |
Thanks for the clarifying words. I am very happy we left the stumblers behind us and seem to have started a closer collaboration. Numpy and Mypy working well together would be fantastic, and I look forward to Numpy providing shape-typing support for arrays. It's great that you are working on this!
I screened through the above comments again and have the impression that users are most bothered that Mypy reports not-so-relevant (from the user perspective) array inconsistencies for unannotated assignments. We internally discussed ways to make Mypy less strict for such cases, even when different scopes are involved. I am (cautiously) optimistic we can make some progress on this soon. Regarding the given example, I don't see how it relates to this issue. Could you please give an example? (Personally, I like narrowing but don't like and never use |
To reiterate what @jakevdp said, and what I agree with: "un-annotated user code that executes correctly should not error when type-checked". I care about that case in the context of teaching, and the Scientific Python lecture notes. |
@stefanv was avoiding EDIT: I suppose this PR answers the question: scipy-lectures/scientific-python-lectures#813 (mostly works, but some other issues remaining). |
Anything I can help with? |
Concerning mypy vs pyright I have had similar experience to @jorenham while adding type annotations in SymPy. There are many situations where I can add what seem like the right annotations and everything works with pyright but then I run mypy and it doesn't work and I need to go change the code or the annotations in awkward ways to satisfy mypy. I think it is worth remembering though that there are different consumers of the type checkers and that these different type checkers are typically used by different people. Downstream of NumPy there are lots of libraries that will for the most part use mypy if they do use a type checker. In this role mypy is a command line development tool that is used somewhat like a linter so the kind of people using it are also the kind of people who might run ruff or flake8 etc on their libraries in CI. On the other hand there are many "end users" who use NumPy directly but probably do not run any type checker explicitly and certainly would not use mypy. However pyright is a popular editor plugin and is e.g. part of the default Python plugin for vscode. The kind of person who is doing a typical end user calculation with NumPy probably does not add type hints to their code but will often benefit from things like autocompletion provided by pyright based on the type annotations/stubs that are provided by NumPy. I think that the typical person reading @stefanv's lecture notes does not run mypy. At most they have pyright running in their editor. If thinking about the "end user" experience it is more important to focus on pyright being able to infer the types correctly for users of NumPy's main public API. Importantly you want those users to have no type hints in their own code but for pyright to be able to infer the types of all the numpy objects. Then users can do things like hover their mouse over For downstream libraries that use type checkers it will be problematic as NumPy adds annotations because it will be a long process during which the annotations are incomplete and continuously changing. Whenever new annotations or stubs are added it will "break" some downstream library's type checking. In some cases that might be a genuine regression and it would be reasonable for NumPy to change/revert the annotations. Otherwise though downstream libraries will just need to adapt and fix their own type annotations. There isn't any way to do the "gradual typing" thing without going through a long process of churn so either NumPy gives up on typing altogether or this downstream library typing churn has to be accepted. There will be many downstream libraries who are using mypy in combination with the NumPy stubs regardless of whether NumPy "blesses" any other type checker. If, like in the OP issue, someone has problems that are specific to mypy then I think it is perfectly reasonable for NumPy to point out that the problem is specific to mypy and that mypy has configuration options and also that they can use a different checker if they want. The problematic behaviour that mypy is showing here is just that mypy disallows rebinding the type like this:
Personally I don't want to use a checker that behaves like that and I much prefer pyright's handling of this case. If someone else is happy with this style of type checking then they can feel free to use mypy but as NumPy (and other libraries) add more annotations it is going to break their mypy CI jobs. This is unavoidable since fewer things will be inferred as
Or they can configure mypy or they can use a different checker. If NumPy wants to use a type checker on its own internal code then it can choose whichever one it wants. If pyright seems best then just use pyright. It doesn't matter what anyone else thinks or what checker they use. I haven't used basedpyright but having a lot of experience of mypy and pyright I would recommend pyright over mypy. |
…ying class (such as np.float32) looking at microsoft/pyright#9051, they declined to fix it themselves, and suggested instead that the used must add a # pyright: ignore or # type: ignore directive to suppress this error. Numpy is working to resolve them: numpy/numpy#28076 and has already done so with npfloat64 (which I can verify in our errors) -- see numpy/numpy#27957 .
Describe the issue:
The following code seems correct, and passes type checking under 2.1.3 but not 2.2.0:
I suppose mypy is flagging that the float width can increase, but since float64 is used by random, that won't happen in this scenario.
Reproduce the code example:
Error message:
Python and NumPy Versions:
Python 3.13, mypy 1.13, numpy 2.2.0
Runtime Environment:
No response
Context for the issue:
No response
The text was updated successfully, but these errors were encountered: