1 # -*- coding: utf-8 -*-
8 from threading
import Lock
, Thread
9 from freezegun
import freeze_time
10 import cached_property
13 def unittest_run_loop(f
):
14 def wrapper(*args
, **kwargs
):
15 coro
= asyncio
.coroutine(f
)
16 future
= coro(*args
, **kwargs
)
17 loop
= asyncio
.get_event_loop()
18 loop
.run_until_complete(future
)
22 def CheckFactory(cached_property_decorator
, threadsafe
=False):
24 Create dynamically a Check class whose add_cached method is decorated by
25 the cached_property_decorator.
31 self
.control_total
= 0
35 async def add_control(self
):
36 self
.control_total
+= 1
37 return self
.control_total
39 @cached_property_decorator
40 async def add_cached(self
):
43 # Need to guard this since += isn't atomic.
45 self
.cached_total
+= 1
47 self
.cached_total
+= 1
49 return self
.cached_total
51 def run_threads(self
, num_threads
):
53 for _
in range(num_threads
):
54 def call_add_cached():
55 loop
= asyncio
.new_event_loop()
56 asyncio
.set_event_loop(loop
)
57 loop
.run_until_complete(self
.add_cached
)
58 thread
= Thread(target
=call_add_cached
)
60 threads
.append(thread
)
61 for thread
in threads
:
67 class TestCachedProperty(unittest
.TestCase
):
68 """Tests for cached_property"""
70 cached_property_factory
= cached_property
.cached_property
72 async def assert_control(self
, check
, expected
):
74 Assert that both `add_control` and 'control_total` equal `expected`
76 self
.assertEqual(await check
.add_control(), expected
)
77 self
.assertEqual(check
.control_total
, expected
)
79 async def assert_cached(self
, check
, expected
):
81 Assert that both `add_cached` and 'cached_total` equal `expected`
83 print('assert_cached', check
.add_cached
)
84 self
.assertEqual(await check
.add_cached
, expected
)
85 self
.assertEqual(check
.cached_total
, expected
)
88 async def test_cached_property(self
):
89 Check
= CheckFactory(self
.cached_property_factory
)
92 # The control shows that we can continue to add 1
93 await self
.assert_control(check
, 1)
94 await self
.assert_control(check
, 2)
96 # The cached version demonstrates how nothing is added after the first
97 await self
.assert_cached(check
, 1)
98 await self
.assert_cached(check
, 1)
100 # The cache does not expire
101 with
freeze_time("9999-01-01"):
102 await self
.assert_cached(check
, 1)
104 # Typically descriptors return themselves if accessed though the class
105 # rather than through an instance.
106 self
.assertTrue(isinstance(Check
.add_cached
,
107 self
.cached_property_factory
))
110 async def test_reset_cached_property(self
):
111 Check
= CheckFactory(self
.cached_property_factory
)
114 # Run standard cache assertion
115 await self
.assert_cached(check
, 1)
116 await self
.assert_cached(check
, 1)
121 # Value is cached again after the next access
122 await self
.assert_cached(check
, 2)
123 await self
.assert_cached(check
, 2)
126 async def test_none_cached_property(self
):
130 self
.cached_total
= None
132 @self.cached_property_factory
133 async def add_cached(self
):
134 return self
.cached_total
136 await self
.assert_cached(Check(), None)
140 pass # Running older version of Python that doesn't support asyncio