You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Not sure if this has been reported before, but the following line is causing stubtest to crash with numpy>=2.2.0 in case you have an empty numpy array as argument default (which is what least scipy==1.14.1 does):
Because since NumPy 2.2.0, ndarray.__bool__ will raise if the array is empty. And the reason why __bool__ gets called, is because ndarray.__ne__ returns another array with np.bool dtype of the same shape:
>>> import numpy as np
>>> np.__version__
'2.2.0'
>>> a = np.array([])
>>> a
array([], dtype=float64)
>>> a ==object() # numpy "vectorizes" the `==` over each element
array([], dtype=bool)
>>> bool(a ==object())
Traceback (most recent call last):
File "<python-input-24>", line 1, in <module>
bool(a == object())
~~~~^^^^^^^^^^^^^^^
ValueError: The truth value of an empty array is ambiguous. Use `array.size > 0` to check that an array is not empty.
So when stubtest encounters a def f(a=np.array([])): ... (on the .py side), stubtest will crash:
$ stubtest --mypy-config-file=pyproject.toml --allowlist=.mypyignore --ignore-unused-allowlist scipy
Traceback (most recent call last):
File "/home/runner/work/scipy-stubs/scipy-stubs/.venv/bin/stubtest", line [8](https://github.com/jorenham/scipy-stubs/actions/runs/12246812991/job/34163454128?pr=288#step:7:9), in<module>sys.exit(main())
~~~~^^
File "/home/runner/work/scipy-stubs/scipy-stubs/.venv/lib/python3.13/site-packages/mypy/stubtest.py", line 20[9](https://github.com/jorenham/scipy-stubs/actions/runs/12246812991/job/34163454128?pr=288#step:7:10)8, in main
return test_stubs(parse_options(sys.argv[1:]))
File "/home/runner/work/scipy-stubs/scipy-stubs/.venv/lib/python3.13/site-packages/mypy/stubtest.py", line 1971, in test_stubs
forerrorin test_module(module):
~~~~~~~~~~~^^^^^^^^
File "/home/runner/work/scipy-stubs/scipy-stubs/.venv/lib/python3.13/site-packages/mypy/stubtest.py", line 247, in test_module
yield from verify(stub, runtime, [module_name])
File "/home/runner/work/scipy-stubs/scipy-stubs/.venv/lib/python3.13/site-packages/mypy/stubtest.py", line 423, in verify_mypyfile
yield from verify(stub_entry, runtime_entry, object_path + [entry])
File "/home/runner/work/scipy-stubs/scipy-stubs/.venv/lib/python3.13/site-packages/mypy/stubtest.py", line 578, in verify_typeinfo
yield from verify(stub_to_verify, runtime_attr, object_path + [entry])
File "/home/runner/work/scipy-stubs/scipy-stubs/.venv/lib/python3.13/site-packages/mypy/stubtest.py", line [10](https://github.com/jorenham/scipy-stubs/actions/runs/12246812991/job/34163454128?pr=288#step:7:11)64, in verify_funcitem
formessagein _verify_signature(stub_sig, runtime_sig, function_name=stub.name):
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/runner/work/scipy-stubs/scipy-stubs/.venv/lib/python3.[13](https://github.com/jorenham/scipy-stubs/actions/runs/12246812991/job/34163454128?pr=288#step:7:14)/site-packages/mypy/stubtest.py", line 902, in _verify_signature
yield from _verify_arg_default_value(stub_arg, runtime_arg)
File "/home/runner/work/scipy-stubs/scipy-stubs/.venv/lib/python3.13/site-packages/mypy/stubtest.py", line 664, in _verify_arg_default_value
if runtime_arg.default != inspect.Parameter.empty:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: The truth value of an empty array is ambiguous. Use `array.size > 0` to check that an array is not empty.
Error: Process completed with exit code 1.
Luckily, fixing this is just a matter of replacing != with is not when comparing against inspect.Parameter.empty (which happens in at least 2 two other places in mypy/stubtest.py as well, btw).
I could scrape together a PR if you'd like, but that could take some time (of which I don't have a lot the coming week) as I don't have a local dev env for mypy setup at the moment. And I can imagine that's more mypy-savvy could probably fix this in a coffee break on their nokia 3310 (assuming they don't get distracted and end up playing snake 2, of course). Anyway, I wouldn't mind either way, so let me know 🤷🏻.
Not sure if this has been reported before, but the following line is causing stubtest to crash with numpy>=2.2.0 in case you have an empty numpy array as argument default (which is what least scipy==1.14.1 does):
But a false positive (according to the typing spec) is reported:
Incompatible return type for "__new__" (returns "ndarray[tuple[int], dtype[signedinteger[_8Bit]]]", but must return a subtype of "int8")
But even so, the second overload isn't completely ignored, because the following isn't reported as an error:
reveal_type(int8(42)) # int8 (correct)reveal_type(int8([42])) # int8 (wrong; should be `numpy.ndarray`)
So it's as if the return type of second __new__ overload is ignored, and Self is used instead 🤔
I believe that #15182 is at least in part responsible for this behavior.
Oh and you might already know this, but this is also blocking a typeshed fix for (at least) builtins.reversed annotations (python/typeshed#11646). So the amount of people that will be happy once this is resolved isn't limited to the numpy community :)
@jorenham: We now in fact consider including an (optional) NumPy plugin in Mypy. Personally, I am longing for fewer unexpected Any return values and better shape typing (possibly including simple math). See this issue for a (very rapidly prototyped) prototype.
I know you prefer some degree of synchronicity among type checkers. But are you nevertheless interested in sharing your opinion on which plugin features could help a significant number of NumPy and Mypy users?
The cumsum example in the referenced issue is something that could (and should) be fixed in the numpy stubs. It should be too difficult, since it's a shape-preserving operation. So if you truly wish to help a significant number of NumPy users, then I'd say contributing stubs improvements to this class of functions would be the way to go.
However, there some cases where it's currently impossible to statically determine the output shape type, but where a mypy plugin would be able to. An rather extreme example of this is numpy.einsum, where the output shape is determined through a string using a particular mini-syntax.
Then there's also a third category of functions where even a mypy plugin wouldn't be able to determine the shape-type, for example numpy.load.
So to summarize; a mypy plugin for shape-typing would be helpful for functions whose output shape-type can statically be determined yet cannot be expressed using Python's type system. I'm not sure which ones exactly these are, but there are probably only a handful of them.
I know you thought about this much more thoroughly, but I am still optimistic that shape typing (using Literal) would be very helpful and may not be impossible (though maybe too much effort).
As I see it, the main points in your cited comments are...
Upcasting problems (Literal[1] to int)
Opposed to basedpyright, Mypy keeps the Literal type in the given example:
fromtypingimportLiteraldefunwrap[N: int](shape: tuple[N]) ->N:
returnshape[0]
s: tuple[Literal[1]] = (1,)
reveal_type(unwrap(s)) # note: Revealed type is "Literal[1]"
Missing integer arithmetic logic in type checkers
That would be one task of the pugin. We could teach it that np.reshape(np.ndarray[tuple[Literal[12]], Any], (4, -1)) -> np.ndarray[tuple[Literal[4], Literal[3]], Any]. (Admittedly, lots of work.)
False positives (or, more correctly, unclear positives)
If a function only works for ndarray[tuple[Literal[2], Literal[2]], Any], I would be happy if Mypy would complain if I insert ndarray[tuple[int, int], Any]
The cumsum example in the referenced issue is something that could (and should) be fixed in the numpy stubs. It should be too difficult, since it's a shape-preserving operation. So if you truly wish to help a significant number of NumPy users, then I'd say contributing stubs improvements to this class of functions would be the way to go.
I picked this example absolutely randomly. But yes, making the stubs perfect where possible is preferable, of course. For me, it is hard to predict which parts of the NumPy stubs can be (or will be) improved substantially.
Thanks for your recommendations again. I'll let them sink in for a while.
As someone who used to be an active NumPy user for ~10 years (a little known fact about me: I have PhD in theoretical physics) here are my 2 cents:
There are two different levels of "precision" when typing arrays: just the number of dimensions, and actual array shape. In my experience it would be great to have the second. There were countless times when after waiting 5 minutes I discovered that I have an off-by-one error because of np.diff(), or some matrices won't multiply because I forgot .T.
I don't really buy the argument that (exact) Literal types for array shapes will cause a lot of harm because we don't have a "gradual" equivalent of int. First, if plugin would only (or mostly) infer precise types for arrays (not for functions that accept them), this will not be a problem unless an array appears in invariant position, which is rarely the case. Second, in the worst case we can always add mypy_extensions.AnyInt.
I don't really buy the argument that (exact) Literal types for array shapes will cause a lot of harm because we don't have a "gradual" equivalent of int. First, if plugin would only (or mostly) infer precise types for arrays (not for functions that accept them), this will not be a problem unless an array appears in invariant position, which is rarely the case. Second, in the worst case we can always add mypy_extensions.AnyInt.
If such an AnyInt would exist in Python, and if there'd be some way to statically infer that e.g. np.empty(12).reshape((3,-1)) has shape (3, 4), that np.swapaxes([[1, 2, 3], [4, 5, 6]], -1, -2) has shape (3, 2), etc., then I'd be all for it.
But don't get me wrong; AFAIK it perfectly fine if someone creates a mypy plugin to help with shape-typing in NumPy. It's just not something that we're going to build ourselves or require our users to use; that's all.
There's #11347 amd #14070 that can lead to unexpected np.ndarray inferred dtypes in certain situations. We often use the "Never trick" to work around this (because pyright also has a similar issue), but there are still some places where it can still happen.
There are also quite a lot of # type: ignore[override] in the numpy stubs that I believe to be false positives. And although I haven't investigated this in detail, I wouldn't be surprised if at least some of those are related to #12379 and #20557 .
I also ran into #9464 a couple of times. But Eric Traut once told me that fixing this could be very complex, because it would require solving constraint sets using something like a unification algorithm. But if mypy already has the machinery for that in place, perhaps it's not as difficult as it would be for pyright 🤷
I guess I could work around this by adding a bunch of overloads with density: bool for each True/False pair, but seeing as histogram already has 35 overloads now, I'm sure you understand why I'd rather not :P.
Activity
hauntsaninja commentedon Dec 28, 2024
PR adding mypy_primer to numpy's CI: numpy/numpy#28073
hauntsaninja commentedon Dec 28, 2024
PR fixing a crash when using numpy with weird import following settings: #18351
hauntsaninja commentedon Dec 28, 2024
PR improving diagnostics for some case someone ran into using numpy: #18352
jorenham commentedon Dec 28, 2024
Not sure if this has been reported before, but the following line is causing stubtest to crash with
numpy>=2.2.0in case you have an empty numpy array as argument default (which is what leastscipy==1.14.1does):mypy/mypy/stubtest.py
Line 673 in 60da03a
Because since NumPy 2.2.0,
ndarray.__bool__will raise if the array is empty. And the reason why__bool__gets called, is becausendarray.__ne__returns another array withnp.booldtype of the same shape:So when stubtest encounters a
def f(a=np.array([])): ...(on the.pyside),stubtestwill crash:Luckily, fixing this is just a matter of replacing
!=withis notwhen comparing againstinspect.Parameter.empty(which happens in at least 2 two other places inmypy/stubtest.pyas well, btw).I could scrape together a PR if you'd like, but that could take some time (of which I don't have a lot the coming week) as I don't have a local dev env for mypy setup at the moment. And I can imagine that's more mypy-savvy could probably fix this in a coffee break on their nokia 3310 (assuming they don't get distracted and end up playing snake 2, of course). Anyway, I wouldn't mind either way, so let me know 🤷🏻.
hamdanal commentedon Dec 28, 2024
Thanks for the report, fix is here #18353
stubtest: Fix crash with numpy array default values (#18353)
jorenham commentedon Dec 29, 2024
jorenham commentedon Jan 5, 2025
It's not possible to annotate the following behavior:
And it leads to incorrectly inferred types
Pyright correctly reports
Type of "np.object_(Spam())" is "Spam".The other numpy "scalar" constructors either return an instance of themselves, or an
ndarray, dependening on the input. For example:The bare minimum stub looks like
But a false positive (according to the typing spec) is reported:
But even so, the second overload isn't completely ignored, because the following isn't reported as an error:
So it's as if the return type of second
__new__overload is ignored, andSelfis used instead 🤔I believe that #15182 is at least in part responsible for this behavior.
Oh and you might already know this, but this is also blocking a typeshed fix for (at least)
builtins.reversedannotations (python/typeshed#11646). So the amount of people that will be happy once this is resolved isn't limited to the numpy community :)jorenham commentedon Jan 21, 2025
#18496
matteobrv commentedon Jan 29, 2025
#18566
Anyfor numpy 2.2.2. expressions that worked with 2.2.1 #18496numpy.float64instead ofAnyfornumpy.ndarray.__iter__in numpy 2.2.2 #1856623 remaining items
jorenham commentedon Apr 1, 2026
I noticed that you use
Literalintegers in the shape type in your example. However, as I explained in https://discuss.python.org/t/pep-827-type-manipulation/106353/88 and numpy/numtype#578, I would recommend against doing that.The
cumsumexample in the referenced issue is something that could (and should) be fixed in the numpy stubs. It should be too difficult, since it's a shape-preserving operation. So if you truly wish to help a significant number of NumPy users, then I'd say contributing stubs improvements to this class of functions would be the way to go.However, there some cases where it's currently impossible to statically determine the output shape type, but where a mypy plugin would be able to. An rather extreme example of this is
numpy.einsum, where the output shape is determined through a string using a particular mini-syntax.Then there's also a third category of functions where even a mypy plugin wouldn't be able to determine the shape-type, for example
numpy.load.So to summarize; a mypy plugin for shape-typing would be helpful for functions whose output shape-type can statically be determined yet cannot be expressed using Python's type system. I'm not sure which ones exactly these are, but there are probably only a handful of them.
tyralla commentedon Apr 1, 2026
Thanks for your insights, @jorenham!
I know you thought about this much more thoroughly, but I am still optimistic that shape typing (using
Literal) would be very helpful and may not be impossible (though maybe too much effort).As I see it, the main points in your cited comments are...
Upcasting problems (
Literal[1]toint)Opposed to basedpyright, Mypy keeps the
Literaltype in the given example:Missing integer arithmetic logic in type checkers
That would be one task of the pugin. We could teach it that
np.reshape(np.ndarray[tuple[Literal[12]], Any], (4, -1)) -> np.ndarray[tuple[Literal[4], Literal[3]], Any]. (Admittedly, lots of work.)False positives (or, more correctly, unclear positives)
If a function only works for
ndarray[tuple[Literal[2], Literal[2]], Any], I would be happy if Mypy would complain if I insertndarray[tuple[int, int], Any]I picked this example absolutely randomly. But yes, making the stubs perfect where possible is preferable, of course. For me, it is hard to predict which parts of the NumPy stubs can be (or will be) improved substantially.
Thanks for your recommendations again. I'll let them sink in for a while.
ilevkivskyi commentedon Apr 6, 2026
As someone who used to be an active NumPy user for ~10 years (a little known fact about me: I have PhD in theoretical physics) here are my 2 cents:
np.diff(), or some matrices won't multiply because I forgot.T.Literaltypes for array shapes will cause a lot of harm because we don't have a "gradual" equivalent ofint. First, if plugin would only (or mostly) infer precise types for arrays (not for functions that accept them), this will not be a problem unless an array appears in invariant position, which is rarely the case. Second, in the worst case we can always addmypy_extensions.AnyInt.jorenham commentedon Apr 6, 2026
If such an
AnyIntwould exist in Python, and if there'd be some way to statically infer that e.g.np.empty(12).reshape((3,-1))has shape(3, 4), thatnp.swapaxes([[1, 2, 3], [4, 5, 6]], -1, -2)has shape(3, 2), etc., then I'd be all for it.But don't get me wrong; AFAIK it perfectly fine if someone creates a mypy plugin to help with shape-typing in NumPy. It's just not something that we're going to build ourselves or require our users to use; that's all.
ilevkivskyi commentedon May 21, 2026
@jorenham @MarcoGorelli Could you please add more links to issues affecting Python numeric stack here?
jorenham commentedon May 21, 2026
There's #11347 amd #14070 that can lead to unexpected
np.ndarrayinferred dtypes in certain situations. We often use the "Never trick" to work around this (because pyright also has a similar issue), but there are still some places where it can still happen.There are also quite a lot of
# type: ignore[override]in the numpy stubs that I believe to be false positives. And although I haven't investigated this in detail, I wouldn't be surprised if at least some of those are related to #12379 and #20557 .I also ran into #9464 a couple of times. But Eric Traut once told me that fixing this could be very complex, because it would require solving constraint sets using something like a unification algorithm. But if mypy already has the machinery for that in place, perhaps it's not as difficult as it would be for pyright 🤷
jorenham commentedon Aug 9, 2026
self-annotated methods #11347tuple[*Ts]subtype with extra type parameter not gradually assignable to itself #19908AssertionErrorincheck_multi_assignment_from_tuple#21824jorenham commentedon Oct 7, 2026
I ran into #14764 a couple of times today when auditing the NumPy stubs for bugs. For example:
reports
and
np.histogramhas exhaustive pairs ofdensity: Literal[True]anddensity: Literal[False]overloads: https://github.com/numpy/numpy/blob/f5cac4c74a4e0793df8defa73c824a75f60a8c7d/numpy/lib/_histograms_impl.pyi#L91-L399I guess I could work around this by adding a bunch of overloads with
density: boolfor eachTrue/Falsepair, but seeing ashistogramalready has 35 overloads now, I'm sure you understand why I'd rather not :P.Maybe you're already aware of this (in which case I apologize for mansplaining :P), but the overload spec argument type expansion section states that
which doesn't seem to be happening here.
ilevkivskyi commentedon Oct 7, 2026
@jorenham Yeah, that issue was already on my radar, there is already a WIP implementation btw. Hopefully a fix for it will be in v2.5.