1 # -*- coding: utf-8 -*-
5 from threading
import Lock
, Thread
6 from freezegun
import freeze_time
9 def unittest_run_loop(f
):
11 def wrapper(*args
, **kwargs
):
12 coro
= asyncio
.coroutine(f
)
13 future
= coro(*args
, **kwargs
)
14 loop
= asyncio
.get_event_loop()
15 loop
.run_until_complete(future
)
19 def CheckFactory(cached_property_decorator
, threadsafe
=False):
21 Create dynamically a Check class whose add_cached method is decorated by
22 the cached_property_decorator.
28 self
.control_total
= 0
32 async def add_control(self
):
33 self
.control_total
+= 1
34 return self
.control_total
36 @cached_property_decorator
37 async def add_cached(self
):
40 # Need to guard this since += isn't atomic.
42 self
.cached_total
+= 1
44 self
.cached_total
+= 1
46 return self
.cached_total
48 def run_threads(self
, num_threads
):
50 for _
in range(num_threads
):
52 def call_add_cached():
53 loop
= asyncio
.new_event_loop()
54 asyncio
.set_event_loop(loop
)
55 loop
.run_until_complete(self
.add_cached
)
57 thread
= Thread(target
=call_add_cached
)
59 threads
.append(thread
)
60 for thread
in threads
:
65 class TestCachedProperty(unittest
.TestCase
):
66 """Tests for cached_property"""
68 cached_property_factory
= cached_property
.cached_property
70 async def assert_control(self
, check
, expected
):
72 Assert that both `add_control` and 'control_total` equal `expected`
74 self
.assertEqual(await check
.add_control(), expected
)
75 self
.assertEqual(check
.control_total
, expected
)
77 async def assert_cached(self
, check
, expected
):
79 Assert that both `add_cached` and 'cached_total` equal `expected`
81 print("assert_cached", check
.add_cached
)
82 self
.assertEqual(await check
.add_cached
, expected
)
83 self
.assertEqual(check
.cached_total
, expected
)
86 async def test_cached_property(self
):
87 Check
= CheckFactory(self
.cached_property_factory
)
90 # The control shows that we can continue to add 1
91 await self
.assert_control(check
, 1)
92 await self
.assert_control(check
, 2)
94 # The cached version demonstrates how nothing is added after the first
95 await self
.assert_cached(check
, 1)
96 await self
.assert_cached(check
, 1)
98 # The cache does not expire
99 with
freeze_time("9999-01-01"):
100 await self
.assert_cached(check
, 1)
102 # Typically descriptors return themselves if accessed though the class
103 # rather than through an instance.
104 self
.assertTrue(isinstance(Check
.add_cached
, self
.cached_property_factory
))
107 async def test_reset_cached_property(self
):
108 Check
= CheckFactory(self
.cached_property_factory
)
111 # Run standard cache assertion
112 await self
.assert_cached(check
, 1)
113 await self
.assert_cached(check
, 1)
118 # Value is cached again after the next access
119 await self
.assert_cached(check
, 2)
120 await self
.assert_cached(check
, 2)
123 async def test_none_cached_property(self
):
128 self
.cached_total
= None
130 @self.cached_property_factory
131 async def add_cached(self
):
132 return self
.cached_total
134 await self
.assert_cached(Check(), None)