Repository: scipy/scipy
File: scipy/interpolate/_rgi.py (Python)

Code before:
    # this class is based on code originally programmed by Johannes Buchner,
    # see https://github.com/JohannesBuchner/regulargrid

    _SPLINE_DEGREE_MAP = {"slinear": 1, "cubic": 3, "quintic": 5, 'pchip': 3,
                          "slinear_legacy": 1, "cubic_legacy": 3, "quintic_legacy": 5,}
    _SPLINE_METHODS_recursive = {"slinear_legacy", "cubic_legacy",
                                "quintic_legacy", "pchip"}
    _SPLINE_METHODS_ndbspl = {"slinear", "cubic", "quintic"}
    _SPLINE_METHODS = list(_SPLINE_DEGREE_MAP.keys())
    _ALL_METHODS = ["linear", "nearest"] + _SPLINE_METHODS

    # generic type compatibility with scipy-stubs
    __class_getitem__ = classmethod(GenericAlias)

    def __init__(self, points, values, method="linear", bounds_error=True,
                 fill_value=np.nan, *, solver=None, solver_args=None):
        if method not in self._ALL_METHODS:
            raise ValueError(f"Method '{method}' is not defined")
        elif method in self._SPLINE_METHODS:
            self._validate_grid_dimensions(points, method)   # NB: uses np.atleast_1d

        try:
            xp = array_namespace(*points, values)
        except Exception:
            # "duck-type" values
            xp = np_compat

        self._asarray = xp.asarray
        self.method = method
        self._spline = None
        self.bounds_error = bounds_error
        self._grid, self._descending_dimensions = _check_points(points)
        self._values = self._check_values(values)
        self._check_dimensionality(self._grid, self._values)
        self.fill_value = self._check_fill_value(self._values, fill_value)
        if self._descending_dimensions:
            self._values = np.flip(values, axis=self._descending_dimensions)
        if self.method == "pchip" and np.iscomplexobj(self._values):
            msg = ("`PchipInterpolator` only works with real values. If you are trying "
                   "to use the real components of the passed array, use `np.real` on "
                   "the array before passing to `RegularGridInterpolator`.")
            raise ValueError(msg)
        if method in self._SPLINE_METHODS_ndbspl:
            if solver_args is None:
                solver_args = {}
            self._spline = self._construct_spline(method, solver, **solver_args)
        else:
            if solver is not None or solver_args:
                raise ValueError(
                    f"{method =} does not accept the 'solver' argument. Got "
                    f" {solver = } and with arguments {solver_args}."

Review comment (suggestion):
I'm a little concerned about catching the bare `Exception` here and falling back to `np_compat`. The intention is that `values` only needs to quack like a duck, but if someone accidentally mixes array types in `points`, they may be surprised to find everything seems to work, but the NumPy backend is being used.

Might it be fine to instead do

```python
xp = array_namespace(*points)
```
?

All tests pass locally for me still with this change.


Code after:
    # this class is based on code originally programmed by Johannes Buchner,
    # see https://github.com/JohannesBuchner/regulargrid

    _SPLINE_DEGREE_MAP = {"slinear": 1, "cubic": 3, "quintic": 5, 'pchip': 3,
                          "slinear_legacy": 1, "cubic_legacy": 3, "quintic_legacy": 5,}
    _SPLINE_METHODS_recursive = {"slinear_legacy", "cubic_legacy",
                                "quintic_legacy", "pchip"}
    _SPLINE_METHODS_ndbspl = {"slinear", "cubic", "quintic"}
    _SPLINE_METHODS = list(_SPLINE_DEGREE_MAP.keys())
    _ALL_METHODS = ["linear", "nearest"] + _SPLINE_METHODS

    # generic type compatibility with scipy-stubs
    __class_getitem__ = classmethod(GenericAlias)

    def __init__(self, points, values, method="linear", bounds_error=True,
                 fill_value=np.nan, *, solver=None, solver_args=None):
        if method not in self._ALL_METHODS:
            raise ValueError(f"Method '{method}' is not defined")
        elif method in self._SPLINE_METHODS:
            self._validate_grid_dimensions(points, method)   # NB: uses np.atleast_1d

        try:
            xp = array_namespace(*points, values)
        except Exception as e:
            # either "duck-type" values or a user error?
            xp = array_namespace(*points) # still forbid mixed namespaces in `points`
            try:
                xp_v = array_namespace(values)
            except Exception:
                # "duck-type" values indeed, continue with `xp` as the namespace
                pass
            else:
                # both `points` and `values` are array API objects, check consistency
                if xp_v != xp:
                    raise e

        self._asarray = xp.asarray
        self.method = method
        self._spline = None
        self.bounds_error = bounds_error
        self._grid, self._descending_dimensions = _check_points(points)
        self._values = self._check_values(values)
        self._check_dimensionality(self._grid, self._values)
        self.fill_value = self._check_fill_value(self._values, fill_value)
        if self._descending_dimensions:
            self._values = np.flip(values, axis=self._descending_dimensions)
        if self.method == "pchip" and np.iscomplexobj(self._values):
            msg = ("`PchipInterpolator` only works with real values. If you are trying "
                   "to use the real components of the passed array, use `np.real` on "
                   "the array before passing to `RegularGridInterpolator`.")
            raise ValueError(msg)