Repository: zarr-developers/zarr-python

Issue:
Regression in v2.18.0 when setting an item in an object array
### Zarr version

v2.18.0

### Numcodecs version

0.12.1

### Python Version

3.12

### Operating System

Windows

### Installation

via `poetry install`

### Description

There seems to be a regression in v2.18.0 (compared to v2.17.2) concerning setting items in an object array.

### Steps to reproduce

Setup:

```py
import zarr
from numcodecs import MsgPack
import numpy as np

root = zarr.group()
arr = root.create_dataset(
    name="my_dataset",
    shape=0,
    dtype=object,
    object_codec=MsgPack(),
)
new_items = [
    ["A", 1],
    ["B", 2, "hello"],
]
arr_add = np.empty(len(new_items), dtype=object)
arr_add[:] = new_items
arr.append(arr_add)
arr[:] # --> array([list(['A', 1]), list(['B', 2, 'hello'])], dtype=object)
```
Problem:

```py
arr[0] = ["C", 3] # raises AttributeError: 'list' object has no attribute 'astype' (from Array._process_for_setitem)
```

Using Zarr v2.17.2, no such exception is raised, and the first item is successfully modified.

### Additional output

_No response_

Discussion:
jhamman: @zarr-developers/python-core-devs - I'm planning to merge this and make a 2.18.1 release later today.

Patch:
diff --git a/docs/release.rst b/docs/release.rst
index e2f9f3de..a81be463 100644
--- a/docs/release.rst
+++ b/docs/release.rst
@@ -31,6 +31,8 @@ Docs
 
 Maintenance
 ~~~~~~~~~~~
+* Fix a regression when getting or setting a single value from arrays with size-1 chunks.
+  By :user:`Deepak Cherian <dcherian>` :issue:`1874`
 
 Deprecations
 ~~~~~~~~~~~~
diff --git a/zarr/core.py b/zarr/core.py
index 6aa86b64..b1ccd203 100644
--- a/zarr/core.py
+++ b/zarr/core.py
@@ -2030,7 +2030,9 @@ class Array:
             and not self._filters
             and self._dtype != object
         ):
-            dest = out[out_selection]
+            # For 0D arrays out_selection = () and out[out_selection] is a scalar
+            # Avoid that
+            dest = out[out_selection] if out_selection else out
             # Assume that array-like objects that doesn't have a
             # `writeable` flag is writable.
             dest_is_writable = getattr(dest, "writeable", True)
diff --git a/zarr/indexing.py b/zarr/indexing.py
index 2f2402fe..35c1e813 100644
--- a/zarr/indexing.py
+++ b/zarr/indexing.py
@@ -52,6 +52,8 @@ def is_scalar(value, dtype):
         return True
     if isinstance(value, tuple) and dtype.names and len(value) == len(dtype.names):
         return True
+    if dtype.kind == "O" and not isinstance(value, np.ndarray):
+        return True
     return False
 
 


Test patch:
diff --git a/zarr/tests/test_core.py b/zarr/tests/test_core.py
index 730f7243..01a78ecd 100644
--- a/zarr/tests/test_core.py
+++ b/zarr/tests/test_core.py
@@ -3157,3 +3157,52 @@ def test_issue_1279(tmpdir):
 
     written_data = ds_reopened[:]
     assert_array_equal(data, written_data)
+
+
+def test_scalar_indexing():
+    store = zarr.KVStore({})
+
+    store["a"] = zarr.create((3,), chunks=(1,), store=store)
+    store["a"][:] = [1, 2, 3]
+
+    assert store["a"][1] == np.array(2.0)
+    assert store["a"][(1,)] == np.array(2.0)
+
+    store["a"][slice(1)] = [-1]
+    assert store["a"][0] == np.array(-1)
+
+    store["a"][0] = -2
+    assert store["a"][0] == np.array(-2)
+
+    store["a"][slice(1)] = (-3,)
+    assert store["a"][0] == np.array(-3)
+
+
+def test_object_array_indexing():
+    # regression test for #1874
+    from numcodecs import MsgPack
+
+    root = zarr.group()
+    arr = root.create_dataset(
+        name="my_dataset",
+        shape=0,
+        dtype=object,
+        object_codec=MsgPack(),
+    )
+    new_items = [
+        ["A", 1],
+        ["B", 2, "hello"],
+    ]
+    arr_add = np.empty(len(new_items), dtype=object)
+    arr_add[:] = new_items
+    arr.append(arr_add)
+
+    # heterogeneous elements
+    elem = ["C", 3]
+    arr[0] = elem
+    assert arr[0] == elem
+
+    # homogeneous elements
+    elem = [1, 3]
+    arr[1] = elem
+    assert arr[1] == elem