Skip to content

Accept array-like objects in param.Array via __array__ protocol#1114

Open
ghostiee-11 wants to merge 2 commits intoholoviz:mainfrom
ghostiee-11:fix/array-accept-array-like
Open

Accept array-like objects in param.Array via __array__ protocol#1114
ghostiee-11 wants to merge 2 commits intoholoviz:mainfrom
ghostiee-11:fix/array-accept-array-like

Conversation

@ghostiee-11
Copy link

Summary

I was building a Panel dashboard that uses param.Array to store label arrays from a pandas DataFrame. With pandas 2.x using pyarrow as the default string backend, df["label"].values returns an ArrowStringArray instead of a numpy array. This causes param.Array to reject it with a ValueError, even though the object fully supports the numpy array protocol.

import param, pandas as pd class State(param.Parameterized): labels = param.Array() s = State() df = pd.DataFrame({"label": ["cat", "dog", "cat"]}) s.labels = df["label"].values # ValueError: Array parameter 'State.labels' value must be an instance of ndarray, not <ArrowStringArray>

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_ in Array to also accept objects implementing __array__ or __array_interface__, while still rejecting plain lists, strings, and other non-array types.

After

s.labels = df["label"].values # works now (ArrowStringArray) s.labels = pd.Categorical(["a", "b", "a"]) # works s.labels = [1, 2, 3] # still rejected (no __array__) s.labels = "not an array" # still rejected

Changes

  • param/parameters.py: Added _is_array_like() static method and _validate_class_() override in Array to accept objects with __array__ or __array_interface__ protocol
  • tests/testnumpy.py: Added 5 new tests covering __array__, __array_interface__, plain list rejection, string rejection, and pandas ExtensionArray (Categorical + ArrowStringArray)

Test plan

  • All 1449 existing param tests pass (0 failures)
  • 5 new tests covering acceptance and rejection cases
  • Verified serialize() / tolist() works with ArrowStringArray
  • Verified np.asarray() round-trip works on stored values
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.
Copilot AI review requested due to automatic review settings March 13, 2026 05:33
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.Array to 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.

Comment on lines +2255 to +2257
def _is_array_like(val):
"""Return True if *val* supports the numpy array protocol."""
return hasattr(val, '__array__') or hasattr(val, '__array_interface__')
Comment on lines +2259 to +2264
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)
Comment on lines +2235 to +2237
Accepts numpy ``ndarray`` objects as well as array-like objects that
implement the ``__array__`` protocol (e.g. pandas ``ExtensionArray``
subclasses such as ``ArrowStringArray``).
Comment on lines +157 to +158
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants