Repository: modin-project/modin

Issue:
Error when using unhashable key in '__setitem__'
### Describe the problem
<!-- Describe the problem clearly here. -->
I get `TypeError: unhashable type: 'slice'` when trying to set value(s) to `Series` using slice.

### Source code / logs
<!-- Include any logs or source code that would be helpful to diagnose the problem. If including tracebacks, please include the full traceback. Large logs and files should be attached. Try to provide a reproducible test case that is the bare minimum necessary to generate the problem. -->

```python
import modin.pandas as pd
import pandas

ps = pandas.Series([1,2,3])
ms = pd.Series([1,2,3])

ps[1:3] = 0
ps
0    1
1    0
2    0
dtype: int64

ms[1:3] = 0
TypeError: unhashable type: 'slice'
```



Discussion:
It doesn't work for all cases where `key` is not a label-like value.
<details><summary>Example</summary>

```python
import modin.pandas as pd

ms = pd.Series([1,2,3])
ms[1] = 0 # Works fine
ms[1:3] = 0 # TypeError
ms[[True, False, True]] = 0 # TypeError
ms[[0, 1]] = 0 # TypeError
```

</details>

Current `Series.__setitem__` implementation:
https://github.com/modin-project/modin/blob/f14a3c187fd2349348e74c9e5095884371ef8645/modin/pandas/series.py#L287-L292
Problems:
1. We check that `key` is in `self.keys` dict assuming that `key` is always hashable, however it doesn't work for lists and slices.
2. Right after this check it calls query compilers `setitem` method, which also works only for label-like values, so adding a check that `key is hashable` to the first if-statement will not resolve the problem

Resolving patch:
diff --git a/modin/pandas/series.py b/modin/pandas/series.py
--- a/modin/pandas/series.py
+++ b/modin/pandas/series.py
@@ -285,11 +285,16 @@ def __round__(self, decimals=0):
         )
 
     def __setitem__(self, key, value):
-        if key not in self.keys():
-            raise KeyError(key)
-        self._create_or_update_from_compiler(
-            self._query_compiler.setitem(1, key, value), inplace=True
-        )
+        if isinstance(key, slice) and (
+            isinstance(key.start, int) or isinstance(key.stop, int)
+        ):
+            # There could be two type of slices:
+            #   - Location based slice (1:5)
+            #   - Labels based slice ("a":"e")
+            # For location based slice we're going to `iloc`, since `loc` can't manage it.
+            self.iloc[key] = value
+        else:
+            self.loc[key] = value
 
     def __sub__(self, right):
         return self.sub(right)


Test patch:
diff --git a/modin/pandas/test/test_series.py b/modin/pandas/test/test_series.py
--- a/modin/pandas/test/test_series.py
+++ b/modin/pandas/test/test_series.py
@@ -180,13 +180,13 @@ def inter_df_math_helper_one_side(modin_series, pandas_series, op):
         pass
 
 
-def create_test_series(vals, sort=False):
+def create_test_series(vals, sort=False, **kwargs):
     if isinstance(vals, dict):
-        modin_series = pd.Series(vals[next(iter(vals.keys()))])
-        pandas_series = pandas.Series(vals[next(iter(vals.keys()))])
+        modin_series = pd.Series(vals[next(iter(vals.keys()))], **kwargs)
+        pandas_series = pandas.Series(vals[next(iter(vals.keys()))], **kwargs)
     else:
-        modin_series = pd.Series(vals)
-        pandas_series = pandas.Series(vals)
+        modin_series = pd.Series(vals, **kwargs)
+        pandas_series = pandas.Series(vals, **kwargs)
     if sort:
         modin_series = modin_series.sort_values().reset_index(drop=True)
         pandas_series = pandas_series.sort_values().reset_index(drop=True)
@@ -526,6 +526,22 @@ def test___setitem__(data):
         df_equals(modin_series, pandas_series)
 
 
+@pytest.mark.parametrize(
+    "key",
+    [
+        pytest.param(slice(1, 3), id="numeric_slice"),
+        pytest.param(slice("a", "c"), id="index_based_slice"),
+        pytest.param(["a", "c", "e"], id="list_of_labels"),
+        pytest.param([True, False, True, False, True], id="boolean_mask"),
+    ],
+)
+def test___setitem___non_hashable(key):
+    md_sr, pd_sr = create_test_series([1, 2, 3, 4, 5], index=["a", "b", "c", "d", "e"])
+    md_sr[key] = 10
+    pd_sr[key] = 10
+    df_equals(md_sr, pd_sr)
+
+
 @pytest.mark.parametrize("data", test_data_values, ids=test_data_keys)
 def test___sizeof__(data):
     modin_series, pandas_series = create_test_series(data)