From 0843982fb7fe2f926ebf24a15ac4b24a9a156c26 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Piotr=20Ma=C5=9Blanka?= <piotr.maslanka@henrietta.com.pl>
Date: Mon, 17 Oct 2022 17:55:34 +0200
Subject: [PATCH] added length

---
 CHANGELOG.md                         |  2 ++
 docs/coding/sequences.rst            |  5 +++++
 satella/__init__.py                  |  2 +-
 satella/coding/__init__.py           |  4 ++--
 satella/coding/misc.py               | 13 +++++++++++++
 tests/test_coding/test_misc.py       |  7 ++++++-
 tests/test_coding/test_predicates.py |  2 +-
 7 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index af5fb318..59656364 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1 +1,3 @@
 # v2.22.1
+
+* added length
\ No newline at end of file
diff --git a/docs/coding/sequences.rst b/docs/coding/sequences.rst
index b374ca6f..777cd9ad 100644
--- a/docs/coding/sequences.rst
+++ b/docs/coding/sequences.rst
@@ -11,6 +11,11 @@ Rolling averages
 Standard routines
 =================
 
+length
+------
+
+.. autofunction:: satella.coding.length
+
 IteratorListAdapter
 -------------------
 
diff --git a/satella/__init__.py b/satella/__init__.py
index 15ce3dd0..dc7612af 100644
--- a/satella/__init__.py
+++ b/satella/__init__.py
@@ -1 +1 @@
-__version__ = '2.22.1a1'
+__version__ = '2.22.1'
diff --git a/satella/coding/__init__.py b/satella/coding/__init__.py
index 612957d3..72369906 100644
--- a/satella/coding/__init__.py
+++ b/satella/coding/__init__.py
@@ -16,7 +16,7 @@ from .metaclasses import metaclass_maker, wrap_with, dont_wrap, wrap_property, D
 from .misc import update_if_not_none, update_key_if_none, update_attr_if_none, queue_iterator, \
     update_key_if_not_none, source_to_function, update_key_if_true, \
     get_arguments, call_with_arguments, chain_callables, Closeable, contains, \
-    enum_value
+    enum_value, length
 from .environment import Context
 from .overloading import overload, class_or_instancemethod
 from .recast_exceptions import rethrow_as, silence_excs, catch_exception, log_exceptions, \
@@ -25,7 +25,7 @@ from .expect_exception import expect_exception
 from .deep_compare import assert_equal, InequalityReason, Inequal
 
 __all__ = [
-    'EmptyContextManager', 'Context',
+    'EmptyContextManager', 'Context', 'length',
     'assert_equal', 'InequalityReason', 'Inequal',
     'Closeable', 'contains', 'enum_value', 'reraise_as',
     'expect_exception',
diff --git a/satella/coding/misc.py b/satella/coding/misc.py
index 5bb0092d..da3a42d7 100644
--- a/satella/coding/misc.py
+++ b/satella/coding/misc.py
@@ -22,6 +22,19 @@ def enum_value(value):
     return value
 
 
+def length(lenable) -> int:
+    """
+    Return length of an item. If it is a generator, exhaust it and return it's length.
+    """
+    try:
+        return len(lenable)
+    except TypeError:
+        i = 0
+        for _ in lenable:
+            i += 1
+        return i
+
+
 def contains(needle, haystack) -> bool:
     """
     A syntactic sugar for the following:
diff --git a/tests/test_coding/test_misc.py b/tests/test_coding/test_misc.py
index 5826fbca..05c89d34 100644
--- a/tests/test_coding/test_misc.py
+++ b/tests/test_coding/test_misc.py
@@ -5,7 +5,7 @@ import unittest
 
 from satella.coding import update_key_if_not_none, overload, class_or_instancemethod, \
     update_key_if_true, get_arguments, call_with_arguments, chain_callables, Closeable, \
-    contains, enum_value, for_argument
+    contains, enum_value, for_argument, length
 from satella.coding.structures import HashableMixin, ComparableEnum
 from satella.coding.transforms import jsonify, intify
 
@@ -99,6 +99,11 @@ class TestCase(unittest.TestCase):
         self.assertRaises(ValueError, lambda: intify(object()))
         self.assertEqual(intify([1, 2, 3]), 3)
 
+    def test_length(self):
+        y = [1, 2, 3]
+        x = (z for z in y)
+        self.assertEqual(length(x, 3))
+
     def test_execute_with_locals(self):
         def fun(a, b, *args, c=None, **kwargs):
             if len(kwargs):
diff --git a/tests/test_coding/test_predicates.py b/tests/test_coding/test_predicates.py
index 9538a8a2..4f3a7d05 100644
--- a/tests/test_coding/test_predicates.py
+++ b/tests/test_coding/test_predicates.py
@@ -124,7 +124,7 @@ class TestPredicates(unittest.TestCase):
         self.assertFalse(p([2, 2]))
 
     def test_len(self):
-        p = x.length() == 2
+        p = x.len_able() == 2
         self.assertTrue(p([1, 2]))
         self.assertFalse(p([]))
 
-- 
GitLab