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.
Files changed (56) hide show
  1. kevin_toolbox/__init__.py +2 -2
  2. kevin_toolbox/computer_science/algorithm/cache_manager/__init__.py +1 -0
  3. kevin_toolbox/computer_science/algorithm/cache_manager/cache/__init__.py +2 -0
  4. kevin_toolbox/computer_science/algorithm/cache_manager/cache/cache_base.py +89 -0
  5. kevin_toolbox/computer_science/algorithm/cache_manager/cache/memo_cache.py +36 -0
  6. kevin_toolbox/computer_science/algorithm/cache_manager/cache_manager.py +218 -0
  7. kevin_toolbox/computer_science/algorithm/cache_manager/strategy/__init__.py +5 -0
  8. kevin_toolbox/computer_science/algorithm/cache_manager/strategy/fifo_strategy.py +21 -0
  9. kevin_toolbox/computer_science/algorithm/cache_manager/strategy/lfu_strategy.py +80 -0
  10. kevin_toolbox/computer_science/algorithm/cache_manager/strategy/lru_strategy.py +43 -0
  11. kevin_toolbox/computer_science/algorithm/cache_manager/strategy/lst_strategy.py +26 -0
  12. kevin_toolbox/computer_science/algorithm/cache_manager/strategy/strategy_base.py +45 -0
  13. kevin_toolbox/computer_science/algorithm/cache_manager/test/__init__.py +0 -0
  14. kevin_toolbox/computer_science/algorithm/cache_manager/test/test_cache_builder.py +37 -0
  15. kevin_toolbox/computer_science/algorithm/cache_manager/test/test_cache_manager.py +197 -0
  16. kevin_toolbox/computer_science/algorithm/cache_manager/test/test_cache_strategy.py +129 -0
  17. kevin_toolbox/computer_science/algorithm/cache_manager/variable.py +28 -0
  18. kevin_toolbox/computer_science/algorithm/registration/registry.py +38 -16
  19. kevin_toolbox/data_flow/core/cache/__init__.py +1 -1
  20. kevin_toolbox/data_flow/core/cache/cache_manager_for_iterator.py +36 -168
  21. kevin_toolbox/data_flow/core/cache/test/__init__.py +0 -0
  22. kevin_toolbox/data_flow/core/cache/test/test_cache_manager_for_iterator.py +34 -0
  23. kevin_toolbox/data_flow/core/reader/file_iterative_reader.py +44 -9
  24. kevin_toolbox/data_flow/core/reader/unified_reader.py +2 -2
  25. kevin_toolbox/data_flow/core/reader/unified_reader_base.py +4 -5
  26. kevin_toolbox/data_flow/file/json_/converter/__init__.py +2 -2
  27. kevin_toolbox/data_flow/file/json_/converter/escape_tuple_and_set.py +23 -0
  28. kevin_toolbox/data_flow/file/json_/converter/{unescape_tuple.py → unescape_tuple_and_set.py} +7 -5
  29. kevin_toolbox/data_flow/file/json_/read_json.py +3 -3
  30. kevin_toolbox/data_flow/file/json_/write_json.py +3 -3
  31. kevin_toolbox/data_flow/file/kevin_notation/kevin_notation_reader.py +6 -5
  32. kevin_toolbox/data_flow/file/kevin_notation/read.py +4 -2
  33. kevin_toolbox/data_flow/file/kevin_notation/test/test_kevin_notation.py +15 -3
  34. kevin_toolbox/data_flow/file/markdown/generate_table.py +2 -2
  35. kevin_toolbox/math/utils/__init__.py +1 -1
  36. kevin_toolbox/math/utils/{spilt_integer_most_evenly.py → split_integer_most_evenly.py} +2 -2
  37. kevin_toolbox/nested_dict_list/get_nodes.py +9 -4
  38. kevin_toolbox/nested_dict_list/name_handler/build_name.py +1 -1
  39. kevin_toolbox/nested_dict_list/name_handler/parse_name.py +1 -1
  40. kevin_toolbox/nested_dict_list/set_default.py +44 -28
  41. kevin_toolbox/patches/for_matplotlib/__init__.py +1 -0
  42. kevin_toolbox/patches/for_matplotlib/generate_color_list.py +33 -0
  43. kevin_toolbox/patches/for_numpy/linalg/__init__.py +1 -0
  44. kevin_toolbox/patches/for_numpy/linalg/entropy.py +26 -0
  45. kevin_toolbox/patches/for_numpy/random/__init__.py +3 -0
  46. kevin_toolbox/patches/for_numpy/random/get_rng.py +64 -0
  47. kevin_toolbox/patches/for_numpy/random/truncated_multivariate_normal.py +129 -0
  48. kevin_toolbox/patches/for_numpy/random/truncated_normal.py +89 -0
  49. kevin_toolbox/patches/for_numpy/random/variable.py +10 -0
  50. kevin_toolbox/patches/for_optuna/serialize/for_study/dump.py +10 -2
  51. kevin_toolbox_dev-1.3.3.dist-info/METADATA +75 -0
  52. {kevin_toolbox_dev-1.3.1.dist-info → kevin_toolbox_dev-1.3.3.dist-info}/RECORD +54 -29
  53. kevin_toolbox/data_flow/file/json_/converter/escape_tuple.py +0 -20
  54. kevin_toolbox_dev-1.3.1.dist-info/METADATA +0 -91
  55. {kevin_toolbox_dev-1.3.1.dist-info → kevin_toolbox_dev-1.3.3.dist-info}/WHEEL +0 -0
  56. {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
- if len(self._item_to_add) > 0:
184
- for i in self._item_to_add:
185
- self.add(**i)
186
- self._item_to_add.clear()
187
- if len(self._path_to_collect) > 0:
188
- for i in self._path_to_collect:
189
- self.collect_from_paths(**i)
190
- self._path_to_collect.clear()
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
- caller_frame = inspect.stack()[1]
252
- assert os.path.basename(caller_frame.filename) != "__init__.py", \
253
- f'calling Registry.collect_from_paths() in __init__.py is forbidden, file: {caller_frame.filename}.\n' \
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
- # 若不是 package,判断是否满足 Path_Ignorer 中的 dirs 对应的规则
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
- # 若该模块是 package,判断该模块的文件路径是否满足 Path_Ignorer 中的 files 对应的规则
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, Strategies
1
+ from .cache_manager_for_iterator import Cache_Manager_for_Iterator