From 5524ae8a4bf4ae79fe493c5327ca7efa13b5c9c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Piotr=20Ma=C5=9Blanka?= <piotr.maslanka@henrietta.com.pl>
Date: Sat, 28 May 2022 16:36:30 +0200
Subject: [PATCH] 2.19.0

---
 CHANGELOG.md                          |  3 ++-
 docs/coding/structures.rst            |  7 +++++++
 satella/__init__.py                   |  2 +-
 satella/coding/structures/__init__.py |  3 ++-
 satella/coding/structures/zip_dict.py | 30 +++++++++++++++++++++++++++
 tests/test_coding/test_structures.py  | 15 +++++++++++++-
 6 files changed, 56 insertions(+), 4 deletions(-)
 create mode 100644 satella/coding/structures/zip_dict.py

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 082feff7..c87dc97a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,4 +5,5 @@
 * fixed a bug in DictionaryEQAble
 * fixed a bug in ListDeleter
 * minor breaking change: changed semantics of ListDeleter
-* added `CPManager`
\ No newline at end of file
+* added `CPManager`
+* added `SetZip`
\ No newline at end of file
diff --git a/docs/coding/structures.rst b/docs/coding/structures.rst
index 611a2d7c..0cf7a60e 100644
--- a/docs/coding/structures.rst
+++ b/docs/coding/structures.rst
@@ -17,6 +17,13 @@ You can also use the following singleton.
     :members:
 
 
+SetZip
+------
+
+.. autoclass:: satella.coding.structures.SetZip
+    :members:
+
+
 PushIterable
 ------------
 
diff --git a/satella/__init__.py b/satella/__init__.py
index 1e2c5e41..6d2db50c 100644
--- a/satella/__init__.py
+++ b/satella/__init__.py
@@ -1 +1 @@
-__version__ = '2.19.0rc3'
+__version__ = '2.19.0'
diff --git a/satella/coding/structures/__init__.py b/satella/coding/structures/__init__.py
index 09743246..9677096d 100644
--- a/satella/coding/structures/__init__.py
+++ b/satella/coding/structures/__init__.py
@@ -18,11 +18,12 @@ from .typednamedtuple import typednamedtuple
 from .lru import LRU
 from .syncable_droppable import DBStorage, SyncableDroppable
 from .tuples import Vector
+from .zip_dict import SetZip
 from .push_iterable import PushIterable
 
 __all__ = [
     'PushIterable',
-    'Vector',
+    'Vector', 'SetZip',
     'DBStorage', 'SyncableDroppable',
     'LRU',
     'LRUCacheDict',
diff --git a/satella/coding/structures/zip_dict.py b/satella/coding/structures/zip_dict.py
new file mode 100644
index 00000000..0459607c
--- /dev/null
+++ b/satella/coding/structures/zip_dict.py
@@ -0,0 +1,30 @@
+class SetZip:
+    """
+    An object which zips a bunch of sets together.
+
+    Ie. checks for inclusion by checking each set.
+
+    Also supports len and iteration protocol.
+
+    You can also add extra sets:
+
+    >>> c = SetZip()
+    >>> c += set([1, 2, 3])
+
+    Provided arguments must implement contains, length and iter.
+    """
+    def __iadd__(self, other: set):
+        self.args.append(other)
+
+    def __init__(self, *args: set):
+        self.args = list(args)
+
+    def __contains__(self, item) -> bool:
+        return any(item in arg for arg in self.args)
+
+    def __len__(self) -> int:
+        return sum(len(arg) for arg in self.args)
+
+    def __iter__(self):
+        for arg in self.args:
+            yield from arg
diff --git a/tests/test_coding/test_structures.py b/tests/test_coding/test_structures.py
index 720fb5e8..9acf9550 100644
--- a/tests/test_coding/test_structures.py
+++ b/tests/test_coding/test_structures.py
@@ -15,11 +15,24 @@ from satella.coding.structures import TimeBasedHeap, Heap, typednamedtuple, \
     CacheDict, StrEqHashableMixin, ComparableIntEnum, HashableIntEnum, ComparableAndHashableBy, \
     ComparableAndHashableByInt, SparseMatrix, ExclusiveWritebackCache, Subqueue, \
     CountingDict, ComparableEnum, LRU, LRUCacheDict, Vector, DefaultDict, PushIterable, \
-    ComparableAndHashableByStr, NotEqualToAnything, NOT_EQUAL_TO_ANYTHING, DictionaryEQAble
+    ComparableAndHashableByStr, NotEqualToAnything, NOT_EQUAL_TO_ANYTHING, DictionaryEQAble, SetZip
 
 
 class TestStructures(unittest.TestCase):
 
+    def test_zip(self):
+        a = set([1,2, 3])
+        b = set([3,4,5])
+        c = SetZip(a, b)
+        self.assertIn(4, c)
+        self.assertNotIn(9, c)
+        self.assertEqual(len(c), 6)
+        self.assertEqual(set(c), set([1,2,3,4,5]))
+
+        c += set([0])
+        self.assertEqual(len(c), 7)
+        self.assertIn(0, c)
+
     def test_dictionary_eqable(self):
         class Dupa(DictionaryEQAble):
             def __init__(self, a):
-- 
GitLab