From 995eef036270b2ffd1e1a41c26166f7beeed967e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Ma=C5=9Blanka?= <piotr.maslanka@henrietta.com.pl> Date: Tue, 5 Mar 2024 08:49:42 +0100 Subject: [PATCH] added __len__ to Optional --- CHANGELOG.md | 2 +- satella/__init__.py | 2 +- satella/coding/optionals.py | 41 ++++++++++------------------- tests/test_coding/test_optionals.py | 30 +++++++++++++++++++++ 4 files changed, 46 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4201c01..1d8fe903 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,4 +4,4 @@ * satella.coding.Context are considered experimental * added a way to register an object to be cleaned up via MemoryPressureManager * fixed get_own_cpu_usage() to work on Windows - +* added __len__ to Optional diff --git a/satella/__init__.py b/satella/__init__.py index d7226f3a..1eb0d8fc 100644 --- a/satella/__init__.py +++ b/satella/__init__.py @@ -1 +1 @@ -__version__ = '2.24.2a4' +__version__ = '2.24.2a5' diff --git a/satella/coding/optionals.py b/satella/coding/optionals.py index 00be5883..92f17433 100644 --- a/satella/coding/optionals.py +++ b/satella/coding/optionals.py @@ -15,10 +15,7 @@ def iterate_if_nnone(iterable: tp.Optional[tp.Iterable]) -> tp.Iterable: :param iterable: iterable to iterate over :return: an empty generator if iterable is none, else an iterator over iterable """ - if iterable is not None: - return iter(iterable) - else: - return iter(tuple()) + return iter(iterable) if iterable is not None else iter(tuple()) def call_if_nnone(clbl: tp.Optional[tp.Callable[..., V]], *args, **kwargs) -> tp.Optional[V]: @@ -77,41 +74,34 @@ class Optional(Proxy[T]): me = getattr(self, '_Proxy__obj') if me is None: return False - return me == other + return other == me def __getattr__(self, item): - if getattr(self, '_Proxy__obj') is None: - return EMPTY_OPTIONAL - else: - return super().__getattr__(item) + return EMPTY_OPTIONAL if getattr(self, '_Proxy__obj') is None else super().__getattr__(item) def __call__(self, *args, **kwargs): - if getattr(self, '_Proxy__obj') is None: - return EMPTY_OPTIONAL - else: - return super().__call__(*args, **kwargs) + return EMPTY_OPTIONAL if getattr(self, '_Proxy__obj') is None else super().__call__(*args, **kwargs) def __bool__(self): - if getattr(self, '_Proxy__obj') is None: - return False - else: - return super().__bool__() + return False if getattr(self, '_Proxy__obj') is None else super().__bool__() def __getitem__(self, item): - if getattr(self, '_Proxy__obj') is None: - return EMPTY_OPTIONAL - else: - return super().__getitem__(item) + return EMPTY_OPTIONAL if getattr(self, '_Proxy__obj') is None else super().__getattr__(item) def __setitem__(self, key, value) -> None: if getattr(self, '_Proxy__obj') is None: return - return super().__setitem__(key, value) + super().__setitem__(key, value) def __delitem__(self, key) -> None: if getattr(self, '_Proxy__obj') is None: return - return super().__delitem__(key) + super().__delitem__(key) + + def __len__(self) -> int: + obj = getattr(self, '_Proxy__obj') + return 0 if obj is None else len(obj) + EMPTY_OPTIONAL = Optional(None) @@ -125,7 +115,4 @@ def extract_optional(v: tp.Union[T, Optional[T]]) -> T: :param v: value to extract the value from :return: resulting value """ - if isinstance(v, Optional): - return getattr(v, '_Proxy__obj') - else: - return v + return getattr(v, '_Proxy__obj') if isinstance(v, Optional) else v diff --git a/tests/test_coding/test_optionals.py b/tests/test_coding/test_optionals.py index 80a164bb..d94def9c 100644 --- a/tests/test_coding/test_optionals.py +++ b/tests/test_coding/test_optionals.py @@ -20,6 +20,35 @@ class TestOptionals(unittest.TestCase): self.assertFalse(c) self.assertTrue(b) + def test_optional_lambda(self): + Optional(None()) + Optional(lambda: 5)() + + def test_object(self): + class Obj: + def __init__(self): + self.a = 5 + + obj = Obj() + opt = Optional(obj) + Optional(None).a + self.assertEqual(opt.a, 5) + opt.a = 6 + self.assertEqual(obj.a, 6) + self.assertEqual(opt.a, 6) + del opt.a + self.assertRaises(AttributeError, obj.a) + self.assertRaises(AttributeError, opt.a) + + def test_list(self): + a = [1, 2, 3] + opt = Optional(a) + self.assertEqual(opt[0], 1) + del opt[0] + self.assertEqual(len(opt), 2) + opt[0] = 4 + self.assertEqual(opt[0], 4) + def test_optional_eq(self): class Opt: a = 5 @@ -30,6 +59,7 @@ class TestOptionals(unittest.TestCase): self.assertIn(2, Optional(a).b) self.assertNotEqual(Optional(None).a, 5) self.assertNotIn(2, Optional(None).b) + self.assertEqual(Optional(5), Optional(5)) def test_optional(self): self.assertIsNone(call_if_nnone(None)) -- GitLab