From f8ee9a2120250f3224627590c927643242927c82 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Piotr=20Ma=C5=9Blanka?= <piotr.maslanka@henrietta.com.pl>
Date: Fri, 9 Apr 2021 18:58:06 +0200
Subject: [PATCH] add equality and membership tests to Optional

---
 CHANGELOG.md                        |  2 ++
 satella/__init__.py                 |  2 +-
 satella/coding/optionals.py         | 20 ++++++++++++++++++++
 tests/test_coding/test_optionals.py | 11 +++++++++++
 4 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f9f57d74..218f83ad 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1 +1,3 @@
 # v2.15.6
+
+* added equality checking and membership test to Optional
diff --git a/satella/__init__.py b/satella/__init__.py
index a493016b..bfc8e94c 100644
--- a/satella/__init__.py
+++ b/satella/__init__.py
@@ -1 +1 @@
-__version__ = '2.15.6a1'
+__version__ = '2.15.6'
diff --git a/satella/coding/optionals.py b/satella/coding/optionals.py
index 893a16f9..596fe984 100644
--- a/satella/coding/optionals.py
+++ b/satella/coding/optionals.py
@@ -54,12 +54,32 @@ class Optional(Proxy):
     * getattr
     * getitem/setitem/delitem
     * testing for truth
+    * comparison (with nonexistent elements always comparing false)
+    * membership test (with nonexistent elements always returning false)
+
+    .. warning:: Returned objects via getattr and getitem are NOT wrapped in an
+        Optional. You need to do it by hand or just file an issue. I'll add that when I
+        need it.
+
+    :param obj: object to wrap
     """
     __slots__ = ()
 
     def __init__(self, obj):
         super().__init__(obj)
 
+    def __contains__(self, item) -> bool:
+        me = getattr(self, '_Proxy__obj')
+        if me is None:
+            return False
+        return item in me
+
+    def __eq__(self, other) -> bool:
+        me = getattr(self, '_Proxy__obj')
+        if me is None:
+            return False
+        return me == other
+
     def __getattr__(self, item):
         if getattr(self, '_Proxy__obj') is None:
             return EMPTY_OPTIONAL
diff --git a/tests/test_coding/test_optionals.py b/tests/test_coding/test_optionals.py
index fc7e7be8..34e75ce9 100644
--- a/tests/test_coding/test_optionals.py
+++ b/tests/test_coding/test_optionals.py
@@ -15,6 +15,17 @@ class TestOptionals(unittest.TestCase):
         self.assertFalse(c)
         self.assertTrue(b)
 
+    def test_optional_eq(self):
+        class Opt:
+            a = 5
+            b = [2]
+
+        a = Opt()
+        self.assertEqual(Optional(a).a, 5)
+        self.assertIn(2, Optional(a).b)
+        self.assertNotEqual(Optional(None).a, 5)
+        self.assertNotIn(2, Optional(None).b)
+
     def test_optional(self):
         self.assertIsNone(call_if_nnone(None))
         b = call_if_nnone(lambda y: y, 5)
-- 
GitLab