Skip to content

Commit d41fa7b

Browse files
committed
[build-script] Introduced cached method/function decorator
cache_util.cached: cached function/method result associated with arguments. cache_util.reifry: replace attribute with the result of the method.
1 parent efca93e commit d41fa7b

File tree

2 files changed

+157
-0
lines changed

2 files changed

+157
-0
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# swift_build_support/cache_util.py -----------------------------*- python -*-
2+
#
3+
# This source file is part of the Swift.org open source project
4+
#
5+
# Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
6+
# Licensed under Apache License v2.0 with Runtime Library Exception
7+
#
8+
# See http://swift.org/LICENSE.txt for license information
9+
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
#
11+
# ----------------------------------------------------------------------------
12+
"""
13+
Cache related utilities
14+
"""
15+
# ----------------------------------------------------------------------------
16+
17+
from functools import update_wrapper
18+
19+
__all__ = [
20+
'cached',
21+
'reify'
22+
]
23+
24+
25+
def cached(func):
26+
"""Decorator that caches result of method or function.
27+
28+
Note: Support method or function.
29+
"""
30+
cache = {}
31+
32+
def wrapper(*args, **kwargs):
33+
key = tuple(args) + tuple(kwargs.items())
34+
if key not in cache:
35+
result = func(*args, **kwargs)
36+
cache[key] = result
37+
return result
38+
else:
39+
return cache[key]
40+
41+
return update_wrapper(wrapper, func)
42+
43+
44+
def reify(func):
45+
"""Decorator that replaces the wrapped method with the result after the
46+
first call.
47+
48+
Note: Support method that takes no arguments.
49+
"""
50+
class wrapper(object):
51+
def __get__(self, obj, objtype=None):
52+
if obj is None:
53+
return self
54+
result = func(obj)
55+
setattr(obj, func.__name__, result)
56+
return result
57+
58+
return update_wrapper(wrapper(), func)
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# tests/test_cache_util.py --------------------------------------*- python -*-
2+
#
3+
# This source file is part of the Swift.org open source project
4+
#
5+
# Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
6+
# Licensed under Apache License v2.0 with Runtime Library Exception
7+
#
8+
# See http://swift.org/LICENSE.txt for license information
9+
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
#
11+
# ----------------------------------------------------------------------------
12+
13+
import unittest
14+
15+
from swift_build_support import cache_util
16+
17+
18+
my_func_called = 0
19+
my_kfunc_called = 0
20+
21+
22+
@cache_util.cached
23+
def my_func(arg1, arg2):
24+
global my_func_called
25+
my_func_called += 1
26+
return "my_func_result(%s, %s)" % (arg1, arg2)
27+
28+
29+
@cache_util.cached
30+
def my_kfunc(arg1, arg2):
31+
global my_kfunc_called
32+
my_kfunc_called += 1
33+
return "my_kfunc_result(%s, %s)" % (arg1, arg2)
34+
35+
36+
class MyClass(object):
37+
def __init__(self, prop=None):
38+
self.my_method_called = 0
39+
self.my_prop_called = 0
40+
self.prop_value = prop
41+
42+
@cache_util.cached
43+
def my_method(self, arg1, arg2):
44+
self.my_method_called += 1
45+
return "my_meth_result(%s, %s)" % (arg1, arg2)
46+
47+
@cache_util.reify
48+
def my_prop(self):
49+
self.my_prop_called += 1
50+
return "==%s==" % (self.prop_value)
51+
52+
53+
class CacheUtilTestCase(unittest.TestCase):
54+
def test_cached_func(self):
55+
self.assertEqual(my_func("foo", 42), "my_func_result(foo, 42)")
56+
self.assertEqual(my_func_called, 1)
57+
self.assertEqual(my_func("foo", 42), "my_func_result(foo, 42)")
58+
self.assertEqual(my_func_called, 1)
59+
self.assertEqual(my_func("bar", 42), "my_func_result(bar, 42)")
60+
self.assertEqual(my_func_called, 2)
61+
self.assertEqual(my_func("foo", 42), "my_func_result(foo, 42)")
62+
self.assertEqual(my_func_called, 2)
63+
64+
def test_cached_kwfunc(self):
65+
self.assertEqual(my_kfunc("foo", arg2=42), "my_kfunc_result(foo, 42)")
66+
self.assertEqual(my_kfunc_called, 1)
67+
self.assertEqual(my_kfunc("foo", arg2=42), "my_kfunc_result(foo, 42)")
68+
self.assertEqual(my_kfunc_called, 1)
69+
self.assertEqual(my_kfunc("bar", arg2=42), "my_kfunc_result(bar, 42)")
70+
self.assertEqual(my_kfunc_called, 2)
71+
self.assertEqual(my_kfunc("foo", arg2=42), "my_kfunc_result(foo, 42)")
72+
self.assertEqual(my_kfunc_called, 2)
73+
74+
def test_cached_method(self):
75+
obj1 = MyClass()
76+
self.assertEqual(obj1.my_method("foo", 42), "my_meth_result(foo, 42)")
77+
self.assertEqual(obj1.my_method_called, 1)
78+
self.assertEqual(obj1.my_method("foo", 42), "my_meth_result(foo, 42)")
79+
self.assertEqual(obj1.my_method_called, 1)
80+
self.assertEqual(obj1.my_method("bar", 12), "my_meth_result(bar, 12)")
81+
self.assertEqual(obj1.my_method_called, 2)
82+
83+
# Test for instance independency.
84+
obj2 = MyClass()
85+
self.assertEqual(obj2.my_method("foo", 42), "my_meth_result(foo, 42)")
86+
self.assertEqual(obj2.my_method_called, 1)
87+
self.assertEqual(obj1.my_method_called, 2)
88+
89+
def test_reify(self):
90+
obj1 = MyClass(prop='foo')
91+
self.assertEqual(obj1.my_prop, '==foo==')
92+
self.assertEqual(obj1.my_prop_called, 1)
93+
self.assertEqual(obj1.my_prop, '==foo==')
94+
self.assertEqual(obj1.my_prop_called, 1)
95+
96+
# Test for instance independency.
97+
obj2 = MyClass(prop='bar')
98+
self.assertEqual(obj2.my_prop, '==bar==')
99+
self.assertEqual(obj1.my_prop, '==foo==')

0 commit comments

Comments
 (0)