kevin-toolbox-dev 1.3.1__py3-none-any.whl → 1.3.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kevin_toolbox/__init__.py +2 -2
- kevin_toolbox/computer_science/algorithm/cache_manager/__init__.py +1 -0
- kevin_toolbox/computer_science/algorithm/cache_manager/cache/__init__.py +2 -0
- kevin_toolbox/computer_science/algorithm/cache_manager/cache/cache_base.py +89 -0
- kevin_toolbox/computer_science/algorithm/cache_manager/cache/memo_cache.py +36 -0
- kevin_toolbox/computer_science/algorithm/cache_manager/cache_manager.py +218 -0
- kevin_toolbox/computer_science/algorithm/cache_manager/strategy/__init__.py +5 -0
- kevin_toolbox/computer_science/algorithm/cache_manager/strategy/fifo_strategy.py +21 -0
- kevin_toolbox/computer_science/algorithm/cache_manager/strategy/lfu_strategy.py +80 -0
- kevin_toolbox/computer_science/algorithm/cache_manager/strategy/lru_strategy.py +43 -0
- kevin_toolbox/computer_science/algorithm/cache_manager/strategy/lst_strategy.py +26 -0
- kevin_toolbox/computer_science/algorithm/cache_manager/strategy/strategy_base.py +45 -0
- kevin_toolbox/computer_science/algorithm/cache_manager/test/__init__.py +0 -0
- kevin_toolbox/computer_science/algorithm/cache_manager/test/test_cache_builder.py +37 -0
- kevin_toolbox/computer_science/algorithm/cache_manager/test/test_cache_manager.py +197 -0
- kevin_toolbox/computer_science/algorithm/cache_manager/test/test_cache_strategy.py +129 -0
- kevin_toolbox/computer_science/algorithm/cache_manager/variable.py +28 -0
- kevin_toolbox/computer_science/algorithm/registration/registry.py +38 -16
- kevin_toolbox/data_flow/core/cache/__init__.py +1 -1
- kevin_toolbox/data_flow/core/cache/cache_manager_for_iterator.py +36 -168
- kevin_toolbox/data_flow/core/cache/test/__init__.py +0 -0
- kevin_toolbox/data_flow/core/cache/test/test_cache_manager_for_iterator.py +34 -0
- kevin_toolbox/data_flow/core/reader/file_iterative_reader.py +44 -9
- kevin_toolbox/data_flow/core/reader/unified_reader.py +2 -2
- kevin_toolbox/data_flow/core/reader/unified_reader_base.py +4 -5
- kevin_toolbox/data_flow/file/json_/converter/__init__.py +2 -2
- kevin_toolbox/data_flow/file/json_/converter/escape_tuple_and_set.py +23 -0
- kevin_toolbox/data_flow/file/json_/converter/{unescape_tuple.py → unescape_tuple_and_set.py} +7 -5
- kevin_toolbox/data_flow/file/json_/read_json.py +3 -3
- kevin_toolbox/data_flow/file/json_/write_json.py +3 -3
- kevin_toolbox/data_flow/file/kevin_notation/kevin_notation_reader.py +6 -5
- kevin_toolbox/data_flow/file/kevin_notation/read.py +4 -2
- kevin_toolbox/data_flow/file/kevin_notation/test/test_kevin_notation.py +15 -3
- kevin_toolbox/data_flow/file/markdown/generate_table.py +2 -2
- kevin_toolbox/math/utils/__init__.py +1 -1
- kevin_toolbox/math/utils/{spilt_integer_most_evenly.py → split_integer_most_evenly.py} +2 -2
- kevin_toolbox/nested_dict_list/get_nodes.py +9 -4
- kevin_toolbox/nested_dict_list/name_handler/build_name.py +1 -1
- kevin_toolbox/nested_dict_list/name_handler/parse_name.py +1 -1
- kevin_toolbox/nested_dict_list/set_default.py +44 -28
- kevin_toolbox/patches/for_matplotlib/__init__.py +1 -0
- kevin_toolbox/patches/for_matplotlib/generate_color_list.py +33 -0
- kevin_toolbox/patches/for_numpy/linalg/__init__.py +1 -0
- kevin_toolbox/patches/for_numpy/linalg/entropy.py +26 -0
- kevin_toolbox/patches/for_numpy/random/__init__.py +3 -0
- kevin_toolbox/patches/for_numpy/random/get_rng.py +64 -0
- kevin_toolbox/patches/for_numpy/random/truncated_multivariate_normal.py +129 -0
- kevin_toolbox/patches/for_numpy/random/truncated_normal.py +89 -0
- kevin_toolbox/patches/for_numpy/random/variable.py +10 -0
- kevin_toolbox/patches/for_optuna/serialize/for_study/dump.py +10 -2
- kevin_toolbox_dev-1.3.3.dist-info/METADATA +75 -0
- {kevin_toolbox_dev-1.3.1.dist-info → kevin_toolbox_dev-1.3.3.dist-info}/RECORD +54 -29
- kevin_toolbox/data_flow/file/json_/converter/escape_tuple.py +0 -20
- kevin_toolbox_dev-1.3.1.dist-info/METADATA +0 -91
- {kevin_toolbox_dev-1.3.1.dist-info → kevin_toolbox_dev-1.3.3.dist-info}/WHEEL +0 -0
- {kevin_toolbox_dev-1.3.1.dist-info → kevin_toolbox_dev-1.3.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,197 @@
|
|
1
|
+
import pytest
|
2
|
+
import time
|
3
|
+
import random
|
4
|
+
from kevin_toolbox.patches.for_test import check_consistency
|
5
|
+
from kevin_toolbox.computer_science.algorithm.cache_manager import Cache_Manager
|
6
|
+
|
7
|
+
|
8
|
+
def test_cache_manager_with_lru_strategy():
|
9
|
+
print("test Cache_Manager with LRU_Strategy")
|
10
|
+
|
11
|
+
strategy = ":by_last_time:LRU"
|
12
|
+
for cache in [":in_memory:Memo", ]:
|
13
|
+
cache_manager = Cache_Manager(upper_bound=3, refactor_size=2, strategy=strategy, cache=cache)
|
14
|
+
|
15
|
+
# 添加数据 a、b、c
|
16
|
+
# 优先级依次为 c、b、a
|
17
|
+
cache_manager.add(key="a", value=1)
|
18
|
+
cache_manager.add(key="b", value=2)
|
19
|
+
cache_manager.add(key="c", value=3)
|
20
|
+
#
|
21
|
+
for k, v in {"a": 1, "b": 2, "c": 3}.items():
|
22
|
+
assert cache_manager.has(key=k)
|
23
|
+
check_consistency(cache_manager.cache.read(key=k), v) # 不影响 metadata
|
24
|
+
|
25
|
+
# 重新添加 a,并访问 a
|
26
|
+
# 优先级变为 a、c、b
|
27
|
+
cache_manager.add(key="a", value=3)
|
28
|
+
check_consistency(cache_manager.get(key="a"), 3)
|
29
|
+
#
|
30
|
+
for k, v in {"a": 3, "b": 2, "c": 3}.items():
|
31
|
+
assert cache_manager.has(key=k)
|
32
|
+
check_consistency(cache_manager.cache.read(key=k), v) # 不影响 metadata
|
33
|
+
for k in ["d", "e"]:
|
34
|
+
assert not cache_manager.has(key=k)
|
35
|
+
with pytest.raises(KeyError):
|
36
|
+
cache_manager.get(key=k)
|
37
|
+
|
38
|
+
# 添加数据 d
|
39
|
+
# 优先级变为 d、a、c、b,超过upper_bound,触发重整到refactor_size大小,变为 d、a
|
40
|
+
cache_manager.add(key="d", value=4)
|
41
|
+
#
|
42
|
+
for k, v in {"a": 3, "d": 4}.items():
|
43
|
+
assert cache_manager.has(key=k)
|
44
|
+
check_consistency(cache_manager.cache.read(key=k), v) # 不影响 metadata
|
45
|
+
for k in ["b", "c"]:
|
46
|
+
assert not cache_manager.has(key=k)
|
47
|
+
with pytest.raises(KeyError):
|
48
|
+
cache_manager.get(key=k)
|
49
|
+
|
50
|
+
# 继续添加 e、f、g
|
51
|
+
# 重整后优先级依次为 g、f、e
|
52
|
+
cache_manager.add(key="e", value=5)
|
53
|
+
cache_manager.add(key="f", value=6)
|
54
|
+
cache_manager.add(key="g", value=7)
|
55
|
+
#
|
56
|
+
for k, v in {"e": 5, "f": 6, "g": 7}.items():
|
57
|
+
assert cache_manager.has(key=k)
|
58
|
+
for k in ["a", "d"]:
|
59
|
+
assert not cache_manager.has(key=k)
|
60
|
+
with pytest.raises(KeyError):
|
61
|
+
cache_manager.get(key=k)
|
62
|
+
|
63
|
+
|
64
|
+
def test_cache_manager_with_fifo_strategy():
|
65
|
+
print("test Cache_Manager with FIFO_Strategy")
|
66
|
+
|
67
|
+
strategy = ":by_initial_time:FIFO"
|
68
|
+
for cache in [":in_memory:Memo", ]:
|
69
|
+
cache_manager = Cache_Manager(upper_bound=3, refactor_size=2, strategy=strategy, cache=cache)
|
70
|
+
|
71
|
+
# 添加数据 a、b、c
|
72
|
+
# 优先级依次为 c、b、a
|
73
|
+
cache_manager.add(key="a", value=1)
|
74
|
+
cache_manager.add(key="b", value=2)
|
75
|
+
cache_manager.add(key="c", value=3)
|
76
|
+
#
|
77
|
+
for k, v in {"a": 1, "b": 2, "c": 3}.items():
|
78
|
+
check_consistency(cache_manager.get(key=k), v)
|
79
|
+
|
80
|
+
# 无论后面怎么读取,优先级都不变
|
81
|
+
for k, v in {"b": 2, "c": 3, "a": 1}.items():
|
82
|
+
for _ in range(random.randint(3, 6)):
|
83
|
+
cache_manager.get(key=k)
|
84
|
+
cache_manager.add(key=k, value=v)
|
85
|
+
|
86
|
+
# 添加数据 d
|
87
|
+
# 优先级变为 d、c、b、a,超过upper_bound,触发重整到refactor_size大小,变为 d、c
|
88
|
+
cache_manager.add(key="d", value=4)
|
89
|
+
#
|
90
|
+
for k, v in {"c": 3, "d": 4}.items():
|
91
|
+
assert cache_manager.has(key=k)
|
92
|
+
check_consistency(cache_manager.cache.read(key=k), v) # 不影响 metadata
|
93
|
+
for k in ["b", "a"]:
|
94
|
+
assert not cache_manager.has(key=k)
|
95
|
+
with pytest.raises(KeyError):
|
96
|
+
cache_manager.get(key=k)
|
97
|
+
|
98
|
+
# 继续添加 e、f、g
|
99
|
+
# 重整后优先级依次为 g、f、e
|
100
|
+
cache_manager.add(key="e", value=5)
|
101
|
+
cache_manager.add(key="f", value=6)
|
102
|
+
cache_manager.add(key="g", value=7)
|
103
|
+
#
|
104
|
+
for k, v in {"e": 5, "f": 6, "g": 7}.items():
|
105
|
+
assert cache_manager.has(key=k)
|
106
|
+
for k in ["a", "d"]:
|
107
|
+
assert not cache_manager.has(key=k)
|
108
|
+
with pytest.raises(KeyError):
|
109
|
+
cache_manager.get(key=k)
|
110
|
+
|
111
|
+
|
112
|
+
def test_cache_manager_with_lfu_strategy():
|
113
|
+
print("test Cache_Manager with LFU_Strategy")
|
114
|
+
|
115
|
+
strategy = ":by_counts:LFU"
|
116
|
+
for cache in [":in_memory:Memo", ]:
|
117
|
+
cache_manager = Cache_Manager(upper_bound=3, refactor_size=2, strategy=strategy, cache=cache)
|
118
|
+
|
119
|
+
# 添加数据 a、b、c,然后各访问 2、3、4
|
120
|
+
# 优先级依次为 c、b、a
|
121
|
+
cache_manager.add(key="a", value=1)
|
122
|
+
cache_manager.add(key="b", value=2)
|
123
|
+
cache_manager.add(key="c", value=3)
|
124
|
+
#
|
125
|
+
for k, v in {"a": 1, "b": 2, "c": 3}.items():
|
126
|
+
for _ in range(v + 1):
|
127
|
+
check_consistency(cache_manager.get(key=k), v)
|
128
|
+
check_consistency(cache_manager.get(key="d", default_factory=lambda: "fuck"), "fuck")
|
129
|
+
|
130
|
+
# 重新添加 c
|
131
|
+
# 优先级变为 b、a、c
|
132
|
+
cache_manager.add(key="c", value=4, b_allow_overwrite=True)
|
133
|
+
check_consistency(cache_manager.get(key="c"), 4)
|
134
|
+
#
|
135
|
+
for k, v in {"a": 1, "b": 2, "c": 4}.items():
|
136
|
+
assert cache_manager.has(key=k)
|
137
|
+
check_consistency(cache_manager.cache.read(key=k), v) # 不影响 metadata
|
138
|
+
for k in ["d", "e"]:
|
139
|
+
assert not cache_manager.has(key=k)
|
140
|
+
with pytest.raises(KeyError):
|
141
|
+
cache_manager.get(key=k)
|
142
|
+
|
143
|
+
# 添加数据 d
|
144
|
+
# 优先级变为 b、a、c、d,超过upper_bound,触发重整到refactor_size大小,变为 b、a
|
145
|
+
cache_manager.get(key="d", default=4, b_add_if_not_found=True)
|
146
|
+
#
|
147
|
+
for k, v in {"a": 1, "b": 2}.items():
|
148
|
+
assert cache_manager.has(key=k)
|
149
|
+
check_consistency(cache_manager.cache.read(key=k), v) # 不影响 metadata
|
150
|
+
for k in ["c", "d"]:
|
151
|
+
assert not cache_manager.has(key=k)
|
152
|
+
with pytest.raises(KeyError):
|
153
|
+
cache_manager.get(key=k)
|
154
|
+
|
155
|
+
# 继续添加 e、f、g,访问一次 e
|
156
|
+
# 重整后优先级依次为 b、a、g
|
157
|
+
cache_manager.get(key="e", default=5, b_add_if_not_found=True)
|
158
|
+
cache_manager.add(key="f", value=6)
|
159
|
+
cache_manager.add(key="g", value=7)
|
160
|
+
#
|
161
|
+
for k, v in {"g": 7, "a": 1, "b": 2}.items():
|
162
|
+
assert cache_manager.has(key=k)
|
163
|
+
for k in ["f", "e"]:
|
164
|
+
assert not cache_manager.has(key=k)
|
165
|
+
with pytest.raises(KeyError):
|
166
|
+
cache_manager.get(key=k)
|
167
|
+
|
168
|
+
|
169
|
+
def test_cache_manager_with_lst_strategy():
|
170
|
+
print("test Cache_Manager with LST_Strategy")
|
171
|
+
|
172
|
+
strategy = ":by_survival_time:LST"
|
173
|
+
for cache in [":in_memory:Memo", ]:
|
174
|
+
cache_manager = Cache_Manager(upper_bound=3, refactor_size=2, strategy=strategy, cache=cache)
|
175
|
+
|
176
|
+
# 添加数据 a、b、c,然后读取2次 b,读取1次 c,读取3次 a
|
177
|
+
# 优先级依次为 a、c、b
|
178
|
+
cache_manager.add(key="a", value=1)
|
179
|
+
cache_manager.add(key="b", value=2)
|
180
|
+
cache_manager.add(key="c", value=3)
|
181
|
+
#
|
182
|
+
for k, v in {"b": 2, "c": 1, "a": 3}.items():
|
183
|
+
for _ in range(v):
|
184
|
+
time.sleep(0.1)
|
185
|
+
cache_manager.get(key=k)
|
186
|
+
|
187
|
+
# 添加数据 d
|
188
|
+
# 优先级变为 a、c、b、d,超过upper_bound,触发重整到refactor_size大小,变为 a、c
|
189
|
+
cache_manager.get(key="d", default=4, b_add_if_not_found=True)
|
190
|
+
#
|
191
|
+
for k, v in {"a": 1, "c": 3}.items():
|
192
|
+
assert cache_manager.has(key=k)
|
193
|
+
check_consistency(cache_manager.cache.read(key=k), v) # 不影响 metadata
|
194
|
+
for k in ["b", "d"]:
|
195
|
+
assert not cache_manager.has(key=k)
|
196
|
+
with pytest.raises(KeyError):
|
197
|
+
cache_manager.get(key=k)
|
@@ -0,0 +1,129 @@
|
|
1
|
+
import pytest
|
2
|
+
import random
|
3
|
+
import time
|
4
|
+
from kevin_toolbox.patches.for_test import check_consistency
|
5
|
+
from kevin_toolbox.computer_science.algorithm.cache_manager.strategy import Strategy_Base
|
6
|
+
from kevin_toolbox.computer_science.algorithm.cache_manager.variable import CACHE_STRATEGY_REGISTRY
|
7
|
+
|
8
|
+
|
9
|
+
def test_lru_strategy():
|
10
|
+
print("test LRU_Strategy")
|
11
|
+
|
12
|
+
strategy = CACHE_STRATEGY_REGISTRY.get(name=":by_last_time:LRU")() # type: Strategy_Base
|
13
|
+
|
14
|
+
# 写入 1、2、3、4
|
15
|
+
# 按照 last_time 排序,优先级为 4、3、2、1
|
16
|
+
for i in range(1, 5):
|
17
|
+
strategy.notified_by_write_of_cache(key=i, value=i, metadata=None)
|
18
|
+
#
|
19
|
+
expected_orders = [4, 3, 2, 1]
|
20
|
+
for i in range(0, 5):
|
21
|
+
check_consistency(strategy.suggest(refactor_size=i), list(reversed(expected_orders[i:])))
|
22
|
+
# 移除并重写 3
|
23
|
+
# 优先级变为 3、4、2、1
|
24
|
+
strategy.notified_by_remove_of_cache(key=3, metadata=None)
|
25
|
+
strategy.notified_by_write_of_cache(key=3, value=3, metadata=None)
|
26
|
+
#
|
27
|
+
expected_orders = [3, 4, 2, 1]
|
28
|
+
for i in range(0, 5):
|
29
|
+
check_consistency(strategy.suggest(refactor_size=i), list(reversed(expected_orders[i:])))
|
30
|
+
# 读取 1
|
31
|
+
# 优先级变为 1、3、4、2
|
32
|
+
strategy.notified_by_read_of_cache(key=1, value=1, metadata=None)
|
33
|
+
#
|
34
|
+
expected_orders = [1, 3, 4, 2]
|
35
|
+
for i in range(0, 5):
|
36
|
+
check_consistency(strategy.suggest(refactor_size=i), list(reversed(expected_orders[i:])))
|
37
|
+
|
38
|
+
|
39
|
+
def test_fifo_strategy():
|
40
|
+
print("test FIFO_Strategy")
|
41
|
+
|
42
|
+
strategy = CACHE_STRATEGY_REGISTRY.get(name=":by_initial_time:FIFO")() # type: Strategy_Base
|
43
|
+
|
44
|
+
# 写入 1、2、3、4
|
45
|
+
# 按照 initial_time 排序,优先级为 4、3、2、1
|
46
|
+
for i in range(1, 5):
|
47
|
+
strategy.notified_by_write_of_cache(key=i, value=i, metadata=None)
|
48
|
+
#
|
49
|
+
expected_orders = [4, 3, 2, 1]
|
50
|
+
for i in range(0, 5):
|
51
|
+
check_consistency(strategy.suggest(refactor_size=i), list(reversed(expected_orders[i:])))
|
52
|
+
|
53
|
+
# 无论后面怎么读取,优先级都不变
|
54
|
+
for i in range(1, 5):
|
55
|
+
for _ in range(random.randint(3, 6)):
|
56
|
+
strategy.notified_by_read_of_cache(key=i, value=i, metadata=None)
|
57
|
+
#
|
58
|
+
for i in range(0, 5):
|
59
|
+
check_consistency(strategy.suggest(refactor_size=i), list(reversed(expected_orders[i:])))
|
60
|
+
|
61
|
+
# 重写 1,删除 4
|
62
|
+
# 优先级变为 1、3、2
|
63
|
+
strategy.notified_by_remove_of_cache(key=1, metadata=None)
|
64
|
+
strategy.notified_by_write_of_cache(key=1, value=3, metadata=None)
|
65
|
+
strategy.notified_by_remove_of_cache(key=4, metadata=None)
|
66
|
+
#
|
67
|
+
expected_orders = [1, 3, 2]
|
68
|
+
for i in range(0, 5):
|
69
|
+
check_consistency(strategy.suggest(refactor_size=i), list(reversed(expected_orders[i:])))
|
70
|
+
|
71
|
+
|
72
|
+
def test_lfu_strategy():
|
73
|
+
print("test LFU_Strategy")
|
74
|
+
|
75
|
+
strategy = CACHE_STRATEGY_REGISTRY.get(name=":by_counts:LFU")() # type: Strategy_Base
|
76
|
+
|
77
|
+
# 写入 1、2、3、4
|
78
|
+
# 按照 counts 排序,优先级为 4==3==2==1
|
79
|
+
for i in range(1, 5):
|
80
|
+
strategy.notified_by_write_of_cache(key=i, value=i, metadata=None)
|
81
|
+
# 读取2次 1,读取1次 3,读取3次 2
|
82
|
+
# counts变为 4:0 3:1 2:3 1:2
|
83
|
+
# 优先级变为 2、1、3、4
|
84
|
+
for key, counts in {1: 2, 3: 1, 2: 3}.items():
|
85
|
+
for i in range(counts):
|
86
|
+
strategy.notified_by_read_of_cache(key=key, value=key, metadata=None)
|
87
|
+
#
|
88
|
+
expected_orders = [2, 1, 3, 4]
|
89
|
+
for i in range(0, 5):
|
90
|
+
check_consistency(strategy.suggest(refactor_size=i), list(reversed(expected_orders[i:])))
|
91
|
+
# 移除并重写 3,读取一次 4
|
92
|
+
# 优先级变为 2、1、4、3
|
93
|
+
strategy.notified_by_remove_of_cache(key=3, metadata=None)
|
94
|
+
strategy.notified_by_write_of_cache(key=3, value=3, metadata=None)
|
95
|
+
strategy.notified_by_read_of_cache(key=4, value=4, metadata=None)
|
96
|
+
#
|
97
|
+
expected_orders = [2, 1, 4, 3]
|
98
|
+
for i in range(0, 5):
|
99
|
+
check_consistency(strategy.suggest(refactor_size=i), list(reversed(expected_orders[i:])))
|
100
|
+
|
101
|
+
|
102
|
+
def test_lst_strategy():
|
103
|
+
print("test LST_Strategy")
|
104
|
+
|
105
|
+
strategy = CACHE_STRATEGY_REGISTRY.get(name=":by_survival_time:LST")() # type: Strategy_Base
|
106
|
+
|
107
|
+
# 写入 1、2、3、4
|
108
|
+
# 按照 survival_time 排序,优先级为 4==3==2==1
|
109
|
+
for i in range(1, 5):
|
110
|
+
strategy.notified_by_write_of_cache(key=i, value=i, metadata={"survival_time": 0})
|
111
|
+
init_time = time.time()
|
112
|
+
# 依次 读取2次 1,读取1次 3,读取3次 2
|
113
|
+
# 优先级变为 2、3、1、4
|
114
|
+
for key, counts in {1: 2, 3: 1, 2: 3}.items():
|
115
|
+
for i in range(counts):
|
116
|
+
strategy.notified_by_read_of_cache(key=key, value=key, metadata={"survival_time": time.time() - init_time})
|
117
|
+
#
|
118
|
+
expected_orders = [2, 3, 1, 4]
|
119
|
+
for i in range(0, 5):
|
120
|
+
check_consistency(strategy.suggest(refactor_size=i), list(reversed(expected_orders[i:])))
|
121
|
+
# 移除并重写 3,读取一次 4
|
122
|
+
# 优先级变为 4、2、1、3
|
123
|
+
strategy.notified_by_remove_of_cache(key=3, metadata=None)
|
124
|
+
strategy.notified_by_write_of_cache(key=3, value=3, metadata={"survival_time": 0})
|
125
|
+
strategy.notified_by_read_of_cache(key=4, value=4, metadata={"survival_time": time.time() - init_time})
|
126
|
+
#
|
127
|
+
expected_orders = [4, 2, 1, 3]
|
128
|
+
for i in range(0, 5):
|
129
|
+
check_consistency(strategy.suggest(refactor_size=i), list(reversed(expected_orders[i:])))
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import os
|
2
|
+
from kevin_toolbox.computer_science.algorithm.registration import Registry
|
3
|
+
|
4
|
+
ignore_s = [
|
5
|
+
{
|
6
|
+
"func": lambda _, __, path: os.path.basename(path) in ["temp", "test", "__pycache__",
|
7
|
+
"_old_version"],
|
8
|
+
"scope": ["root", "dirs"]
|
9
|
+
},
|
10
|
+
]
|
11
|
+
|
12
|
+
# 包含缓存管理or更新策略
|
13
|
+
CACHE_STRATEGY_REGISTRY = Registry(uid="CACHE_STRATEGY_REGISTRY")
|
14
|
+
|
15
|
+
CACHE_STRATEGY_REGISTRY.collect_from_paths(
|
16
|
+
path_ls=[os.path.join(os.path.dirname(__file__), "strategy"), ],
|
17
|
+
ignore_s=ignore_s,
|
18
|
+
b_execute_now=False
|
19
|
+
)
|
20
|
+
|
21
|
+
# 包含缓存构建器
|
22
|
+
CACHE_BUILDER_REGISTRY = Registry(uid="CACHE_BUILDER_REGISTRY")
|
23
|
+
|
24
|
+
CACHE_BUILDER_REGISTRY.collect_from_paths(
|
25
|
+
path_ls=[os.path.join(os.path.dirname(__file__), "cache"), ],
|
26
|
+
ignore_s=ignore_s,
|
27
|
+
b_execute_now=False
|
28
|
+
)
|
@@ -180,14 +180,16 @@ class Registry:
|
|
180
180
|
找不到时,若无默认值则报错,否则将返回默认值
|
181
181
|
"""
|
182
182
|
# 加载待注册成员
|
183
|
-
|
184
|
-
|
185
|
-
self.
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
self.
|
190
|
-
|
183
|
+
while self._item_to_add or self._path_to_collect:
|
184
|
+
if len(self._item_to_add) > 0:
|
185
|
+
for i in self._item_to_add:
|
186
|
+
self.add(**i)
|
187
|
+
self._item_to_add.clear()
|
188
|
+
if len(self._path_to_collect) > 0:
|
189
|
+
for i in self._path_to_collect:
|
190
|
+
i.setdefault("caller_file", inspect.stack()[1].filename)
|
191
|
+
self.collect_from_paths(**i)
|
192
|
+
self._path_to_collect.clear()
|
191
193
|
|
192
194
|
return ndl.get_value(var=self.database, name=name, b_pop=b_pop, **kwargs)
|
193
195
|
|
@@ -220,7 +222,7 @@ class Registry:
|
|
220
222
|
|
221
223
|
# -------------------- 通过路径添加 --------------------- #
|
222
224
|
|
223
|
-
def collect_from_paths(self, path_ls=None, ignore_s=None, b_execute_now=False):
|
225
|
+
def collect_from_paths(self, path_ls=None, ignore_s=None, b_execute_now=False, **kwargs):
|
224
226
|
"""
|
225
227
|
遍历 path_ls 下的所有模块,并自动导入其中主要被注册的部分
|
226
228
|
比如被 register() 装饰器包裹或者通过 add() 添加的部分
|
@@ -243,14 +245,15 @@ class Registry:
|
|
243
245
|
1. 在当前脚本中显式导入该实例前,调用了其他脚本执行了该实例的 collect_from_paths() 函数,且设置 b_execute_now=True,
|
244
246
|
此时若导入的成员中有类,且该类继承自某个父类,且在初始化时使用了 super(xx,self).__init__ 继承初始化函数,将出现
|
245
247
|
TypeError: super(type, obj): obj must be an instance or subtype of type 的错误
|
246
|
-
2. 在模块的 __init__.py 文件中使用 collect_from_paths()
|
248
|
+
2. 在模块的 __init__.py 文件中使用 collect_from_paths() 或间接通过 get() 调用 collect_from_paths()
|
249
|
+
3. collect_from_paths() 函数中的搜索路径中包含了调用该函数的文件位置。
|
247
250
|
为了避免情况 1,应该尽量避免设置 b_execute_now=True。
|
248
251
|
或者省略 super(xx,self).__init__ 中的参数改为 super().__init__
|
249
252
|
"""
|
250
253
|
# 检查调用位置
|
251
|
-
|
252
|
-
assert os.path.basename(
|
253
|
-
f'calling Registry.collect_from_paths() in __init__.py is forbidden, file: {
|
254
|
+
caller_file = kwargs.get("caller_file", inspect.stack()[1].filename)
|
255
|
+
assert os.path.basename(caller_file) != "__init__.py", \
|
256
|
+
f'calling Registry.collect_from_paths() in __init__.py is forbidden, file: {caller_file}.\n' \
|
254
257
|
f'you can call it in other files, and then import the result of the call in __init__.py'
|
255
258
|
|
256
259
|
# 根据 ignore_s 构建 Path_Ignorer
|
@@ -259,7 +262,7 @@ class Registry:
|
|
259
262
|
#
|
260
263
|
if not b_execute_now:
|
261
264
|
self._path_to_collect.append(
|
262
|
-
dict(path_ls=path_ls, ignore_s=path_ignorer, b_execute_now=True))
|
265
|
+
dict(path_ls=path_ls, ignore_s=path_ignorer, b_execute_now=True, caller_file=caller_file))
|
263
266
|
return
|
264
267
|
|
265
268
|
#
|
@@ -280,16 +283,35 @@ class Registry:
|
|
280
283
|
# (快速判断)判断该模块所在目录是否在 path_set 中
|
281
284
|
if loader.path not in path_set:
|
282
285
|
continue
|
286
|
+
# is_pkg:
|
287
|
+
# - 为 True 时表示当前遍历到的模块是一个包(即一个包含其他模块或子包的目录)
|
288
|
+
# - 为 False 时表示当前模块是一个普通的 Python 模块(文件),不包含其他模块或子包。
|
283
289
|
if is_pkg:
|
284
|
-
#
|
290
|
+
# 若是目录形式的 package,判断是否满足 Path_Ignorer 中的 dirs 对应的规则
|
285
291
|
path = os.path.dirname(loader.find_module(module_name).path)
|
286
292
|
if path_ignorer(Ignore_Scope.DIRS, True, os.path.islink(path), path):
|
287
293
|
continue
|
288
294
|
else:
|
289
|
-
# 若该模块是
|
295
|
+
# 若该模块是 module,判断该模块的文件路径是否满足 Path_Ignorer 中的 files 对应的规则
|
290
296
|
path = loader.find_module(module_name).path
|
291
297
|
if path_ignorer(Ignore_Scope.FILES, False, os.path.islink(path), path):
|
292
298
|
continue
|
299
|
+
# 若该模块与调用的文件相同,则报错。
|
300
|
+
if path == caller_file:
|
301
|
+
# collect_from_paths() 函数中的搜索路径不应该包含调用该函数文件。
|
302
|
+
# 因为这样将会导致该函数被自己无限递归调用。
|
303
|
+
# 要避免这样的错误,你可以选择:
|
304
|
+
# 1. 将该函数的调用位置放置在待搜索路径外;
|
305
|
+
# 2. 使用 ignore_s 参数来避免加载该函数的调用位置。
|
306
|
+
raise RuntimeError(
|
307
|
+
f'Registry.collect_from_paths(): \n'
|
308
|
+
f'\tThe search path in a function should not include the file location from which it is called. \n'
|
309
|
+
f'\tBecause this will cause the function to be called infinitely recursively by itself. \n'
|
310
|
+
f'To avoid such errors, you can choose: \n'
|
311
|
+
f'\t1. Place the calling location of this function ({path}) outside the path to be searched; \n'
|
312
|
+
f'\t2. Use the ignore_s parameter to avoid searching the calling location of the function, '
|
313
|
+
f'such as {{"func": lambda _, __, path: path == "{path}", "scope": ["files",]}}'
|
314
|
+
)
|
293
315
|
# 加载模块
|
294
316
|
module = loader.find_module(module_name).load_module(module_name)
|
295
317
|
# 选择遍历过程中第一次找到的 Registry 实例
|
@@ -1 +1 @@
|
|
1
|
-
from .cache_manager_for_iterator import Cache_Manager_for_Iterator
|
1
|
+
from .cache_manager_for_iterator import Cache_Manager_for_Iterator
|