1 # -*- coding: utf-8 -*-
5 from threading
import Lock
, Thread
6 from freezegun
import freeze_time
10 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
)
20 def CheckFactory(cached_property_decorator
, threadsafe
=False):
22 Create dynamically a Check class whose add_cached method is decorated by
23 the cached_property_decorator.
28 self
.control_total
= 0
32 async def add_control(self
): # noqa
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
:
66 class TestCachedProperty(unittest
.TestCase
):
67 """Tests for cached_property"""
69 cached_property_factory
= cached_property
.cached_property
71 async def assert_control(self
, check
, expected
):
73 Assert that both `add_control` and 'control_total` equal `expected`
75 self
.assertEqual(await check
.add_control(), expected
)
76 self
.assertEqual(check
.control_total
, expected
)
78 async def assert_cached(self
, check
, expected
):
80 Assert that both `add_cached` and 'cached_total` equal `expected`
82 print("assert_cached", check
.add_cached
)
83 self
.assertEqual(await check
.add_cached
, expected
)
84 self
.assertEqual(check
.cached_total
, expected
)
87 async def test_cached_property(self
):
88 Check
= CheckFactory(self
.cached_property_factory
)
91 # The control shows that we can continue to add 1
92 await self
.assert_control(check
, 1)
93 await self
.assert_control(check
, 2)
95 # The cached version demonstrates how nothing is added after the first
96 await self
.assert_cached(check
, 1)
97 await self
.assert_cached(check
, 1)
99 # The cache does not expire
100 with
freeze_time("9999-01-01"):
101 await self
.assert_cached(check
, 1)
103 # Typically descriptors return themselves if accessed though the class
104 # rather than through an instance.
105 self
.assertTrue(isinstance(Check
.add_cached
, self
.cached_property_factory
))
108 async def test_reset_cached_property(self
):
109 Check
= CheckFactory(self
.cached_property_factory
)
112 # Run standard cache assertion
113 await self
.assert_cached(check
, 1)
114 await self
.assert_cached(check
, 1)
119 # Value is cached again after the next access
120 await self
.assert_cached(check
, 2)
121 await self
.assert_cached(check
, 2)
124 async def test_none_cached_property(self
):
127 self
.cached_total
= None
129 @self.cached_property_factory
130 async def add_cached(self
):
131 return self
.cached_total
133 await self
.assert_cached(Check(), None)