-
Notifications
You must be signed in to change notification settings - Fork 115
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
Support for numpy.typing.NDArray
#195
Comments
I'm favorable to getting support for this in, but I don't have much time to work on typeguard these days. |
maybe @BvB93 would be interested? 👉 👈 |
I'm not familiar with the So if you'd want to validate the dtype at runtime based on import numpy as np
import numpy.typing as npt
from typing import Type
array_annotation = npt.NDArray[np.float64]
dtype: Type[np.dtype] = array_annotation.__args__[-1]
scalar_type: Type[np.float64] = dtype.__args__[0]
# Compare `scalar_type` to the passed arrays `dtype.type` attribute
array = np.random.rand(10)
assert issubclass(array.dtype.type, scalar_type) |
Thanks a lot @BvB93. Very quickly, let's say you write a function that receives types. How would one check if a type is a def check_type(argname, value, expected_type, memo):
...
# @BvB93 Unsure The next line ?
if npt and isinstance(expected_type, npt._generic_alias._GenericAlias) :
expected_dtype = expected_type.__args__[-1]
expected_scalar_type = expected_dtype.__args__[0]
if not isinstance(value, np.ndarray):
raise TypeError(
'type of {} must be {}; got {} instead'.
format(argname, qualified_name(expected_type), qualified_name(value)))
if expected_scalar_type != Any not issubclass(value.dtype.type, expected_scalar_type):
raise TypeError('NumPy dtype of {} ({}) is not compatible with the {} dtype.'.format(
argname, value.dtype, expected_scalar_type
))
... @agronholm From my understanding, something like this would be ok as an addition to In the imports: try:
import numpy.typing as not
import numpy as np
except ImportError:
npt = None
np = None Testing for whether |
(edited the previous comment with an import strategy) |
adding the code right before the |
Directly accessing private API is a very bad idea, especially since this will break for python >= 3.9: - if npt and isinstance(expected_type, npt._generic_alias._GenericAlias) :
+ if npt:
+ GenericAlias = type(npt.NDArray) # You could cache this somewhere as it's constant
+ if isintance(expected_type, GenericAlias) and expected_type.__origin__ is np.ndarray: Furthermore, there is also the possibility of |
I'm well aware that it's a bad idea, that's why I was asking how |
thanks, this is great. |
Obligitory necrobump: beartype fully supports The only active PEP-compliant runtime type checkers are beartype, pydantic, and typical – the latter two of which have yet to support Lastly, I'll note for the sake of posterity that beartype fully supports Come back to us with |
The trouble is that typeguard is no longer at the top of my (long) list of projects to be maintained. After I'm done updating my currently focus (APScheduler), I will move on to wheel, and if nobody has picked up the slack before that, I will turn my attention to typeguard again. How much time that will take is anybody's guess, but you should know that typeguard has not been completely abandoned. |
I think this warrants a plugin system. I'm tentatively scheduling this for v3.0. |
On the topic of plugin systems: torchtyping offers a system of rich type annotations for PyTorch tensors, e.g. All of which is to say: a plugin system sounds very interesting. |
I've opened #215 to discuss this. |
In case you are not already following the linked discussion, there is now a plugin system in the |
I have a PoC checker for ndarray. Let me know if this is useful. Is it necessary to check all elements or just the first? from __future__ import annotations
from typing import Any
import numpy as np
from numpy.typing import NDArray
from typeguard import (
TypeCheckError,
TypeCheckMemo,
TypeCheckerCallable,
check_type_internal,
config,
typechecked
)
def check_ndarray(value: Any, origin_type: Any, args: tuple[Any, ...], memo: TypeCheckMemo):
if not isinstance(value, np.ndarray):
raise TypeCheckError("is not a numpy.ndarray")
if args:
expected_type = args[1].__args__[0]
for i, v in enumerate(value):
try:
check_type_internal(v, expected_type, memo)
except TypeCheckError as exc:
exc.append_path_element(f"item {i}")
raise
def numpy_checker_lookup(origin_type: Any, args: tuple, extras: tuple) -> TypeCheckerCallable | None:
if origin_type is np.ndarray:
return check_ndarray
return None
config.checker_lookup_functions.append(numpy_checker_lookup)
@typechecked
def foo(a_float_array: NDArray[np.float64]):
pass
array = np.zeros(5)
foo(array) |
Btw -- heads up that jaxtyping now exists, which (thanks to a new and smarter design):
I can see that typeguard might want to support the I know typeguard is something of a back-burner project, so I'm mentioning all of this mostly to emphasise that I don't think any special demands need to be placed on typeguard in order to support array typing. :) |
It's on the way. Due to the recent changes, |
Typeguard 3.0.0 is out. While it's not part of Typeguard itself, there is a mechanism with which to implement checks like this with extensions. |
NumPy has introduced a new typing library: https://numpy.org/devdocs/reference/typing.html.
One part of it is that you can mention the
dtype
of elements in the type:Right now,
typeguard
doesn't check that thedtype
is respected, and doesn't emit notifications when passed andarray
that has the wrongdtype
.I understand the eventual argument against making specific fixes. However, NumPy is one of if not the single most used third-party Python library, with incredibly wide applications, including in the domain of all types of research, like physics and medicine (and AI). Allowing better type safety in all of these great settings would maybe be more important than not making a specific change. Idk, you decide.
Best.
The text was updated successfully, but these errors were encountered: