Repository: pandas-dev/pandas
Pull request: BUG/TST: PeriodArray.__setitem__ with slice and list-like value
- [ ] closes #23978 
- [ ] tests added / passed
- [ ] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [x] whatsnew entry


Problem statement:
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)

Hints from issue discussion:
Hello, has your all-in-one branch been merged into master? i fetched all, and looks like this change is not in master yet? maybe i did something wrong... 
No, it's not quite ready for a PR yet.

Commit message: remove redundant blank line

Patch to review:
diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py
index 53629dca4d391..dd79c9bddac15 100644
--- 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)
diff --git a/pandas/tests/extension/test_period.py b/pandas/tests/extension/test_period.py
index 2e629ccb2981e..589919b9f5d1f 100644
--- a/pandas/tests/extension/test_period.py
+++ b/pandas/tests/extension/test_period.py
@@ -8,6 +8,7 @@
 import pandas as pd
 from pandas.core.arrays import PeriodArray
 from pandas.tests.extension import base
+import pandas.util.testing as tm
 
 
 @pytest.fixture
@@ -147,7 +148,16 @@ class TestReshaping(BasePeriodTests, base.BaseReshapingTests):
 
 
 class TestSetitem(BasePeriodTests, base.BaseSetitemTests):
-    pass
+
+    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:]
+        tm.assert_extension_array_equal(arr, data[-5:])
 
 
 class TestGroupby(BasePeriodTests, base.BaseGroupbyTests):


Code review comments:
diff_hunk: @@ -147,7 +148,16 @@ class TestReshaping(BasePeriodTests, base.BaseReshapingTests):
 
 
 class TestSetitem(BasePeriodTests, base.BaseSetitemTests):
-    pass
+
+    def test_setitem_slice_mismatch_length_raises(self, data):
path: pandas/tests/extension/test_period.py
text: @user1:
Ahh, just noticed. This should be on the base class BaseSetitemTests. Then all the subclasses will be tested, including period. 

@user1:
Same for the other test. 

@author:
ahh, i see, yeah, makes sense!! thx!