Repository: pandas-dev/pandas

Issue:
BUG/TST: PeriodArray.__setitem__ with slice and list-like value fails
```python

In [1]: import pandas as pd; import numpy as np

In [2]: arr = pd.core.arrays.period_array([2000, 2001], 'D')

In [3]: arr[:1] = ['2001']
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-907919e36fd9> in <module>()
----> 1 arr[:1] = ['2001']

~/sandbox/pandas/pandas/core/arrays/period.py in __setitem__(self, key, value)
    358         # work, since the freq can't be inferred.
    359         if is_list_like(value):
--> 360             if len(key) != len(value) and not com.is_bool_indexer(key):
    361                 msg = ("shape mismatch: value array of length '{}' does not "
    362                        "match indexing result of length '{}'.")

TypeError: object of type 'slice' has no len()

```

---

We should be checking that the if `key` is a slice

```python
            is_slice = isinstance(key, slice)
            if (not is_slice
                    and len(key) != len(value)
                    and not com.is_bool_indexer(key)):
                msg = ("shape mismatch: value array of length '{}' does not "
                       "match indexing result of length '{}'.")
                raise ValueError(msg.format(len(key), len(value)))
            if not is_slice and len(key) == 0:
                return
```

Then, if they slice length doesn't match with value, NumPy will complain at the end when we do `self._data[key] = value`.

I've fixed this on my all-in-one branch. To close this issue, we should also add a base extension tests to BaseSetitemTests like


```python
def test_setitem_slice_mismatch_length_raises(self, data):
    arr = data[:5]
    with pytest.raises(ValueError):
        arr[:1] = arr[:2]
```

and a successful one like


```python
def test_setitem_slice_array(self, data):
    arr = data[:5].copy()
    arr[:5] = data[-5:]
    self.assert_extension_array_equal(arr, data[-5:])
```

(those are untested)


Discussion:

Resolving patch:
diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py
--- a/pandas/core/arrays/period.py
+++ b/pandas/core/arrays/period.py
@@ -347,7 +347,7 @@ def __repr__(self):
 
     def __setitem__(
             self,
-            key,   # type: Union[int, Sequence[int], Sequence[bool]]
+            key,   # type: Union[int, Sequence[int], Sequence[bool], slice]
             value  # type: Union[NaTType, Period, Sequence[Period]]
     ):
         # type: (...) -> None
@@ -357,11 +357,14 @@ def __setitem__(
         # ndarray[datetime64ns]. I think ndarray[int] / ndarray[str] won't
         # work, since the freq can't be inferred.
         if is_list_like(value):
-            if len(key) != len(value) and not com.is_bool_indexer(key):
+            is_slice = isinstance(key, slice)
+            if (not is_slice
+                    and len(key) != len(value)
+                    and not com.is_bool_indexer(key)):
                 msg = ("shape mismatch: value array of length '{}' does not "
                        "match indexing result of length '{}'.")
                 raise ValueError(msg.format(len(key), len(value)))
-            if len(key) == 0:
+            if not is_slice and len(key) == 0:
                 return
 
             value = period_array(value)


Test patch:
diff --git a/pandas/tests/extension/base/setitem.py b/pandas/tests/extension/base/setitem.py
--- a/pandas/tests/extension/base/setitem.py
+++ b/pandas/tests/extension/base/setitem.py
@@ -173,3 +173,13 @@ def test_setitem_tuple_index(self, data):
         expected = pd.Series(data.take([1, 1]), index=s.index)
         s[(0, 1)] = data[1]
         self.assert_series_equal(s, expected)
+
+    def test_setitem_slice_mismatch_length_raises(self, data):
+        arr = data[:5]
+        with pytest.raises(ValueError):
+            arr[:1] = arr[:2]
+
+    def test_setitem_slice_array(self, data):
+        arr = data[:5].copy()
+        arr[:5] = data[-5:]
+        self.assert_extension_array_equal(arr, data[-5:])