Accept array-like objects in param.Array via __array__ protocol#1114
Accept array-like objects in param.Array via __array__ protocol#1114ghostiee-11 wants to merge 2 commits intoholoviz:mainfrom
Conversation
param.Array currently only accepts numpy ndarray instances. With pandas 2.x defaulting to pyarrow-backed string columns, df["col"].values returns an ArrowStringArray which param.Array rejects with a ValueError, even though the object fully supports the numpy array protocol via __array__. This overrides _validate_class_ in Array to also accept objects that implement __array__ or __array_interface__, while still rejecting plain lists, strings, and other non-array types.
There was a problem hiding this comment.
Pull request overview
This PR updates param.Array validation to accept non-ndarray “array-like” objects (e.g. pandas ExtensionArray / pyarrow-backed arrays) via NumPy array protocols, and adds tests to cover the new acceptance/rejection behavior.
Changes:
- Extend
param.Arrayto treat values with__array__/__array_interface__as valid. - Add unit tests ensuring array-like acceptance and list/string rejection.
- Add pandas-focused coverage for ExtensionArray types (Categorical + pyarrow string array when available).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
param/parameters.py |
Loosens Array validation to accept array-protocol objects; expands docstring. |
tests/testnumpy.py |
Adds tests for __array__, __array_interface__, rejection cases, and pandas ExtensionArray acceptance. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def _is_array_like(val): | ||
| """Return True if *val* supports the numpy array protocol.""" | ||
| return hasattr(val, '__array__') or hasattr(val, '__array_interface__') |
| def _validate_class_(self, val, class_, is_instance): | ||
| # Accept array-like objects (e.g. pandas ExtensionArray, | ||
| # ArrowStringArray) that support the numpy array protocol. | ||
| if is_instance and not isinstance(val, class_) and self._is_array_like(val): | ||
| return | ||
| super()._validate_class_(val, class_, is_instance) |
| Accepts numpy ``ndarray`` objects as well as array-like objects that | ||
| implement the ``__array__`` protocol (e.g. pandas ``ExtensionArray`` | ||
| subclasses such as ``ArrowStringArray``). |
| except (ImportError, TypeError): | ||
| pass # pyarrow not installed, skip this sub-test |
- Use getattr with try/except instead of hasattr to handle objects where property access raises non-AttributeError - Verify __array__ is callable, __array_interface__ is not None - serialize: fall back to numpy.asarray(value).tolist() when value lacks .tolist() method - Docstring: mention both __array__ and __array_interface__ protocols - Tests: broaden pyarrow exception catch, add serialize assertions for both the fallback path and pandas ExtensionArray
|
Thanks for this. I'm not yet 100% whether we should support it on |
|
I agree that we shouldn't loosen the strictness of I do think this PR is still quite a way from being in a mergeable state, even with a name change. |
|
I do like |
Summary
I was building a Panel dashboard that uses
param.Arrayto store label arrays from a pandas DataFrame. With pandas 2.x using pyarrow as the default string backend,df["label"].valuesreturns anArrowStringArrayinstead of a numpy array. This causesparam.Arrayto reject it with aValueError, even though the object fully supports the numpy array protocol.I had to work around this everywhere with
np.array(df[col].tolist())which felt unnecessary since the object already implements__array__.Fix
I overrode
_validate_class_inArrayto also accept objects implementing__array__or__array_interface__, while still rejecting plain lists, strings, and other non-array types.After
Changes
param/parameters.py: Added_is_array_like()static method and_validate_class_()override inArrayto accept objects with__array__or__array_interface__protocoltests/testnumpy.py: Added 5 new tests covering__array__,__array_interface__, plain list rejection, string rejection, and pandas ExtensionArray (Categorical + ArrowStringArray)Test plan
serialize()/tolist()works with ArrowStringArraynp.asarray()round-trip works on stored values