Repository: isi-vista/immutablecollections

Issue:
Slicing is broken for the single-element implementation of ImmutableSet

Comments:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 60cbc1b..cd7abcb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,12 @@
+Immutablecollections 0.8.0 (2019-07-25)
+=======================================
+
+New Features
+------------
+
+- Add workarounds for mypy problems with attrs type converters and generics.  This is to workaround python/mypy#5738 and  python-attrs/attrs#519 . (`#59 <https://github.com/isi-vista/immutablecollections/pull/59>`_)
+
+
 Immutablecollections 0.7.0 (2019-05-21)
 =======================================
 
diff --git a/immutablecollections/_immutableset.py b/immutablecollections/_immutableset.py
index 5e8f907..51c2db8 100644
--- a/immutablecollections/_immutableset.py
+++ b/immutablecollections/_immutableset.py
@@ -614,10 +614,19 @@ class _SingletonImmutableSet(ImmutableSet[T]):
         if item == 0 or item == -1:
             return self._single_value
         elif isinstance(item, slice):
-            raise NotImplementedError(
-                "Slicing of singleton immutable sets not yet implemented, see "
-                "https://github.com/isi-vista/immutablecollections/issues/23."
-            )
+            if item.step is None or item.step > 0:
+                if (
+                    (item.start is None and item.stop is None)
+                    or (item.start is None and (item.stop is None or item.stop >= 1))
+                    or (item.start <= 0 and (item.stop is None or item.stop >= 1))
+                ):
+                    return self
+                else:
+                    return _EMPTY
+            elif item.step < 0:
+                return self.__getitem__(slice(item.stop, item.start, -item.step))
+            else:
+                raise ValueError("Can't slice with step size of zero.")
         else:
             raise IndexError(f"Index {item} out-of-bounds for size 1 ImmutableSet")
 
diff --git a/immutablecollections/converter_utils.py b/immutablecollections/converter_utils.py
new file mode 100644
index 0000000..3757a0f
--- /dev/null
+++ b/immutablecollections/converter_utils.py
@@ -0,0 +1,63 @@
+from typing import Any, Iterable, Mapping, Optional, Tuple, Union
+
+from immutablecollections import (
+    ImmutableDict,
+    ImmutableListMultiDict,
+    ImmutableSet,
+    ImmutableSetMultiDict,
+    immutabledict,
+    immutablelistmultidict,
+    immutableset,
+    immutablesetmultidict,
+)
+
+
+def _to_tuple(val: Iterable[Any]) -> Tuple[Any, ...]:
+    """Needed until https://github.com/python/mypy/issues/5738
+    and https://github.com/python-attrs/attrs/issues/519 are fixed.
+    """
+    return tuple(val)
+
+
+def _to_immutableset(val: Optional[Iterable[Any]]) -> ImmutableSet[Any]:
+    """Needed until https://github.com/python/mypy/issues/5738
+        and https://github.com/python-attrs/attrs/issues/519 are fixed.
+    """
+    return immutableset(val)
+
+
+def _to_immutabledict(
+    val: Optional[
+        Union[Iterable[Tuple[Any, Any]], Mapping[Any, Any], ImmutableDict[Any, Any]]
+    ]
+) -> ImmutableDict[Any, Any]:
+    """Needed until https://github.com/python/mypy/issues/5738
+        and https://github.com/python-attrs/attrs/issues/519 are fixed.
+    """
+    return immutabledict(val)
+
+
+def _to_immutablesetmultidict(
+    val: Optional[
+        Union[
+            Iterable[Tuple[Any, Any]], Mapping[Any, Any], ImmutableSetMultiDict[Any, Any]
+        ]
+    ]
+) -> ImmutableSetMultiDict[Any, Any]:
+    """Needed until https://github.com/python/mypy/issues/5738
+        and https://github.com/python-attrs/attrs/issues/519 are fixed.
+    """
+    return immutablesetmultidict(val)
+
+
+def _to_immutablelistmultidict(
+    val: Optional[
+        Union[
+            Iterable[Tuple[Any, Any]], Mapping[Any, Any], ImmutableListMultiDict[Any, Any]
+        ]
+    ]
+) -> ImmutableListMultiDict[Any, Any]:
+    """Needed until https://github.com/python/mypy/issues/5738
+        and https://github.com/python-attrs/attrs/issues/519 are fixed.
+    """
+    return immutablelistmultidict(val)
diff --git a/immutablecollections/version.py b/immutablecollections/version.py
index bc6f82a..f1dc7b5 100644
--- a/immutablecollections/version.py
+++ b/immutablecollections/version.py
@@ -1,1 +1,1 @@
-version = "0.8.0"  # pylint:disable=invalid-name
+version = "0.9.0"  # pylint:disable=invalid-name


Test patch:
diff --git a/tests/test_immutableset.py b/tests/test_immutableset.py
index 591a8e9..d9fc910 100644
--- a/tests/test_immutableset.py
+++ b/tests/test_immutableset.py
@@ -222,6 +222,32 @@ class TestImmutableSet(TestCase):
         with self.assertRaises(IndexError):
             s[-25]
 
+    def test_singleton_slice(self):
+        s = immutableset([1])
+
+        self.assertEqual(s, s[0:1])
+        self.assertEqual(s, s[-1:1])
+        self.assertEqual(s, s[0:])
+        self.assertEqual(s, s[:1])
+        self.assertEqual(s, s[:50])
+        self.assertEqual(s, s[-2:])
+
+        self.assertEqual(immutableset(), s[1:5])
+        self.assertEqual(immutableset(), s[51:56])
+        self.assertEqual(immutableset(), s[-5:-1])
+        self.assertEqual(immutableset(), s[-1:0])
+        self.assertEqual(immutableset(), s[1:])
+
+        self.assertEqual(immutableset(), s[5:1])
+
+        self.assertEqual(s, s[0:1:1])
+        self.assertEqual(s, s[1:-50:-1])
+        self.assertEqual(s, s[0:1:5])
+        self.assertEqual(s, s[1:-1:-60])
+
+        with self.assertRaises(ValueError):
+            s[0:1:0]
+
     def test_slice(self):
         self.assertEqual(2, immutableset([1, 2, 3])[1])
         self.assertEqual((2, 3), immutableset([1, 2, 3])[1:])