Repository: andir/isc-dhcp-filter

Issue:
Add `.count()` method as shortcut for len(list(leases))
Sometime the only intresting part about the lease db is how many are actually in a given state. Currently you've to write `len(list(leases.active))` to get the count of active leases. `leases.active.count()` and also implementing `__len__` would probably be handy.

Discussion:

Patch:
diff --git a/isc_dhcp_filter/__init__.py b/isc_dhcp_filter/__init__.py
index d769b6d..70524c3 100644
--- a/isc_dhcp_filter/__init__.py
+++ b/isc_dhcp_filter/__init__.py
@@ -1,6 +1,6 @@
+from isc_dhcp_leases import IscDhcpLeases
 from isc_dhcp_leases import Lease
 from isc_dhcp_leases import Lease6
-from isc_dhcp_leases import IscDhcpLeases
 
 
 def parse(*files):
@@ -119,6 +119,13 @@ class Leases:
 
         return Leases(g)
 
+    def count(self):
+        """
+        Returns the count of leases in the current set of leases
+        :return: int count of leases
+        """
+        return len(self)
+
     def __iter__(self):
         """
         Returns an iterator for the current set of leases
@@ -128,3 +135,16 @@ class Leases:
             yield from iter(self._leases)
         elif self._iter:
             yield from self._iter()
+
+    def __len__(self):
+        """
+        Implements __len__
+        If we are dealing with a generator we will expand it into `_leases`
+        :return:
+        """
+        if type(self._leases) is list:
+            return len(self._leases)
+        else:
+            l = list(iter(self))
+            self._leases = l
+            return len(l)


Test patch:
diff --git a/tests/__init__.py b/tests/__init__.py
index e924baf..426d90b 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1,11 +1,11 @@
 import os
 from unittest import TestCase
 
+from freezegun import freeze_time
 from isc_dhcp_leases import Lease
 from isc_dhcp_leases.iscdhcpleases import BaseLease
 
 from isc_dhcp_filter import parse, Leases
-from freezegun import freeze_time
 
 
 class LeaseLoaderMixin:
@@ -26,6 +26,15 @@ class BaseLeaseTester:
         self.assertEqual(len(active_valid), len(valid_active))
         self.assertEqual(len(active_valid), len(list(self.leases.current)))
 
+    def test_list_twice(self):
+        a = list(self.leases)
+        b = list(self.leases)
+
+        self.assertEqual(a, b)
+
+    def test_len(self):
+        self.assertEqual(len(self.leases), self.leases.count())
+
     def test_v4_filter(self):
         for lease in self.leases.v4:
             self.assertIsInstance(lease, Lease)
@@ -65,7 +74,7 @@ class BaseLeaseTester:
     def test_filter_combine(self):
         combined = Leases(self.leases.v4, self.leases.v6)
         l = len(list(combined))
-        self.assertEqual(l, len(list(self.leases)))
+        self.assertEqual(l, len(self.leases))
 
 
 class TestDhcpd6(LeaseLoaderMixin, BaseLeaseTester, TestCase):
@@ -74,18 +83,20 @@ class TestDhcpd6(LeaseLoaderMixin, BaseLeaseTester, TestCase):
     def test_dhcpv6_active(self):
         leases = self.leases
 
-        self.assertEqual(len(list(leases)), 4)
-        self.assertEqual(len(list(leases.active)), 4)
+        self.assertEqual(len(leases), 4)
+        self.assertEqual(len(leases.active), 4)
+        self.assertEqual(len(leases.active), 4)
 
     @freeze_time("2015-07-6 8:15:0")
     def test_dhcpv6_active_valid(self):
         leases = self.leases
 
-        active_valid = list(leases.active.valid)
-        valid_active = list(leases.valid.active)
+        active_valid = leases.active.valid
+        valid_active = leases.valid.active
 
         self.assertEqual(len(active_valid), len(valid_active))
-        self.assertEqual(len(active_valid), len(list(leases.current)))
+        self.assertEqual(len(active_valid), len(leases.current))
+        self.assertEqual(sorted(active_valid, key=id), sorted(valid_active, key=id))
 
     def test_dhcpv6_invalid(self):
         leases = self.leases
@@ -115,6 +126,11 @@ class TestDebian7(LeaseLoaderMixin, BaseLeaseTester, TestCase):
         leases2 = list(self.leases.where_eq('vendor-class-identifier', 'Some Vendor Identifier'))
         self.assertEqual(leases1, leases2)
 
+
 class TestEmptyLease(BaseLeaseTester, TestCase):
     def setUp(self):
-        self.leases = Leases()
\ No newline at end of file
+        self.leases = Leases()
+
+    def test_lease_count_zero(self):
+        self.assertEqual(self.leases.count(), 0)
+        self.assertEqual(len(self.leases), 0)