After #905 is merged, the following NumPy array protocol attributes/methods will be explicitly handed by Pint Quantities:
__array_priority__
__array_ufunc__
__array_function__
All others (any starting with __array_) are implicitly handled by
- Warning with a
UnitStrippedWarning
- If magnitude is an
ndarray, return the corresponding attribute on the magnitude
- If the magnitude is not an
ndarray, return the corresponding attribute on the magnitude converted to an ndarray.
Before #905, step 3 above was extremely problematic, since the conversion was done in place (see #399, #481, #509). While now it's a bit better because it should no longer mutate the Quantity, the conversion to ndarray may not respect the behavior of arbitrary duck arrays (see #845 / #878), and may return misleading values (such as may be the case with __array_struct__ and __array_interface__ as brought up in #905).
Being uncomfortable with this implicit handing, and seeing the issues it has caused, I would suggest that Pint Quantities instead only (explicitly) respond to:
and raise AttributeError for all other __array_*__ attributes. This would bring Pint into alignment with the recommendations set by NumPy for custom array containers (see https://docs.scipy.org/doc/numpy-1.17.0/user/basics.dispatch.html) and, to the best I can tell, the example set by Dask and Sparse (and to a certain extent CuPy).
However, while it seems to be the "more correct" implementation, given that this would be removing a fallback, it would be a major breaking change with potentially unforeseen consequences. And so, I wanted to reach out for feedback before I try putting together a follow-up PR to #905 to address these issues and work towards resolving #845 / #878.
If this is an acceptable change, would it make sense to include a deprecation cycle, by leaving in the __array_*__ fallback with a DeprecationWarning for Pint v0.10, and then changing to AttributeError in v0.11? Or is there a consensus to leave the current behavior in place and try working around the issues that arise?
For reference, for the three duck array packages pointed to in NEP 30 (Dask, CuPy, and Sparse), here is a brief summary of their behavior with these attributes from what I could scour from their source code (no guarantee on correctness though, as I haven't worked with any of their internals):
Dask and Sparse
- Explicitly defines
__array__
__array_priority__
__array_ufunc__
__array_function__
- Does not respond to others
CuPy
After #905 is merged, the following NumPy array protocol attributes/methods will be explicitly handed by Pint Quantities:
__array_priority____array_ufunc____array_function__All others (any starting with
__array_) are implicitly handled byUnitStrippedWarningndarray, return the corresponding attribute on the magnitudendarray, return the corresponding attribute on the magnitude converted to anndarray.Before #905, step 3 above was extremely problematic, since the conversion was done in place (see #399, #481, #509). While now it's a bit better because it should no longer mutate the Quantity, the conversion to ndarray may not respect the behavior of arbitrary duck arrays (see #845 / #878), and may return misleading values (such as may be the case with
__array_struct__and__array_interface__as brought up in #905).Being uncomfortable with this implicit handing, and seeing the issues it has caused, I would suggest that Pint Quantities instead only (explicitly) respond to:
__array__(by warning withUnitStrippedWarningand then returningnp.asarraycalled on the magnitude)__array_priority__(see suggestions in NEP-18 Compatibility #905 (comment))__array_ufunc__(as done in NEP-18 Compatibility #905)__array_function__(as done in NEP-18 Compatibility #905)and raise
AttributeErrorfor all other__array_*__attributes. This would bring Pint into alignment with the recommendations set by NumPy for custom array containers (see https://docs.scipy.org/doc/numpy-1.17.0/user/basics.dispatch.html) and, to the best I can tell, the example set by Dask and Sparse (and to a certain extent CuPy).However, while it seems to be the "more correct" implementation, given that this would be removing a fallback, it would be a major breaking change with potentially unforeseen consequences. And so, I wanted to reach out for feedback before I try putting together a follow-up PR to #905 to address these issues and work towards resolving #845 / #878.
If this is an acceptable change, would it make sense to include a deprecation cycle, by leaving in the
__array_*__fallback with aDeprecationWarningfor Pint v0.10, and then changing toAttributeErrorin v0.11? Or is there a consensus to leave the current behavior in place and try working around the issues that arise?For reference, for the three duck array packages pointed to in NEP 30 (Dask, CuPy, and Sparse), here is a brief summary of their behavior with these attributes from what I could scour from their source code (no guarantee on correctness though, as I haven't worked with any of their internals):
Dask and Sparse
__array____array_priority____array_ufunc____array_function__CuPy
__array____array_priority____array_ufunc____array_function____array_wrap__,__array_interface__listed as TODO in commentsndarray(see https://github.com/cupy/cupy/blob/1928c3b22c972647ecf2242ddced62015407593f/cupyx/fallback_mode/fallback.py)