kevin-toolbox-dev 1.3.3__py3-none-any.whl → 1.3.5__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/data_flow/file/json_/read_json.py +11 -5
- kevin_toolbox/data_flow/file/kevin_notation/kevin_notation_writer.py +1 -1
- kevin_toolbox/nested_dict_list/serializer/__init__.py +1 -0
- kevin_toolbox/nested_dict_list/serializer/backends/_ndl.py +4 -1
- kevin_toolbox/nested_dict_list/serializer/read.py +27 -16
- kevin_toolbox/nested_dict_list/serializer/saved_node_name_builder.py +31 -0
- kevin_toolbox/nested_dict_list/serializer/write.py +66 -32
- kevin_toolbox/nested_dict_list/value_parser/__init__.py +1 -0
- kevin_toolbox/nested_dict_list/value_parser/replace_identical_with_reference.py +127 -0
- kevin_toolbox/patches/for_os/__init__.py +1 -0
- kevin_toolbox/patches/for_os/copy.py +33 -0
- kevin_toolbox/patches/for_test/check_consistency.py +104 -33
- kevin_toolbox_dev-1.3.5.dist-info/METADATA +74 -0
- {kevin_toolbox_dev-1.3.3.dist-info → kevin_toolbox_dev-1.3.5.dist-info}/RECORD +17 -14
- kevin_toolbox_dev-1.3.3.dist-info/METADATA +0 -75
- {kevin_toolbox_dev-1.3.3.dist-info → kevin_toolbox_dev-1.3.5.dist-info}/WHEEL +0 -0
- {kevin_toolbox_dev-1.3.3.dist-info → kevin_toolbox_dev-1.3.5.dist-info}/top_level.txt +0 -0
kevin_toolbox/__init__.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
__version__ = "1.3.
|
1
|
+
__version__ = "1.3.5"
|
2
2
|
|
3
3
|
|
4
4
|
import os
|
@@ -12,5 +12,5 @@ os.system(
|
|
12
12
|
os.system(
|
13
13
|
f'python {os.path.split(__file__)[0]}/env_info/check_validity_and_uninstall.py '
|
14
14
|
f'--package_name kevin-toolbox-dev '
|
15
|
-
f'--expiration_timestamp
|
15
|
+
f'--expiration_timestamp 1731518184 --verbose 0'
|
16
16
|
)
|
@@ -1,15 +1,17 @@
|
|
1
1
|
import os
|
2
2
|
import json
|
3
|
+
from io import BytesIO, StringIO
|
3
4
|
from kevin_toolbox.data_flow.file.json_.converter import integrate, unescape_tuple_and_set, unescape_non_str_dict_key
|
4
5
|
from kevin_toolbox.nested_dict_list import traverse
|
5
6
|
|
6
7
|
|
7
|
-
def read_json(file_path, converters=None, b_use_suggested_converter=False):
|
8
|
+
def read_json(file_path=None, file_obj=None, converters=None, b_use_suggested_converter=False):
|
8
9
|
"""
|
9
10
|
读取 json file
|
10
11
|
|
11
12
|
参数:
|
12
13
|
file_path
|
14
|
+
file_obj
|
13
15
|
converters: <list of converters> 对读取内容中每个节点的处理方式
|
14
16
|
转换器 converter 应该是一个形如 def(x): ... ; return x 的函数,具体可以参考
|
15
17
|
json_.converter 中已实现的转换器
|
@@ -19,13 +21,17 @@ def read_json(file_path, converters=None, b_use_suggested_converter=False):
|
|
19
21
|
默认为 False。
|
20
22
|
注意:当 converters 非 None,此参数失效,以 converters 中的具体设置为准
|
21
23
|
"""
|
22
|
-
assert
|
24
|
+
assert file_path is not None or file_obj is not None
|
25
|
+
if file_path is not None:
|
26
|
+
assert os.path.isfile(file_path), f'file {file_path} not found'
|
27
|
+
file_obj = open(file_path, 'r')
|
28
|
+
elif isinstance(file_obj, (BytesIO,)):
|
29
|
+
file_obj = StringIO(file_obj.read().decode('utf-8'))
|
30
|
+
content = json.load(file_obj)
|
31
|
+
|
23
32
|
if converters is None and b_use_suggested_converter:
|
24
33
|
converters = [unescape_tuple_and_set, unescape_non_str_dict_key]
|
25
34
|
|
26
|
-
with open(file_path, 'r') as f:
|
27
|
-
content = json.load(f)
|
28
|
-
|
29
35
|
if converters is not None:
|
30
36
|
converter = integrate(converters)
|
31
37
|
content = traverse(var=[content],
|
@@ -5,7 +5,7 @@ import warnings
|
|
5
5
|
from kevin_toolbox.data_flow.file.kevin_notation.converter import Converter, CONVERTER_FOR_WRITER
|
6
6
|
from kevin_toolbox.data_flow.file import kevin_notation
|
7
7
|
|
8
|
-
|
8
|
+
warnings.filterwarnings('ignore', category=np.VisibleDeprecationWarning)
|
9
9
|
|
10
10
|
|
11
11
|
class Kevin_Notation_Writer:
|
@@ -3,6 +3,7 @@ import time
|
|
3
3
|
from kevin_toolbox.patches import for_os
|
4
4
|
from kevin_toolbox.data_flow.file import json_
|
5
5
|
import kevin_toolbox.nested_dict_list as ndl
|
6
|
+
import tempfile
|
6
7
|
|
7
8
|
|
8
9
|
def read(input_path, **kwargs):
|
@@ -16,26 +17,35 @@ def read(input_path, **kwargs):
|
|
16
17
|
|
17
18
|
assert os.path.exists(input_path)
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
20
|
+
with tempfile.TemporaryDirectory(dir=os.path.dirname(input_path)) as temp_dir:
|
21
|
+
if os.path.isfile(input_path) and input_path.endswith(".tar"): # 解压
|
22
|
+
for_os.unpack(source=input_path, target=temp_dir)
|
23
|
+
input_path = os.path.join(temp_dir, os.listdir(temp_dir)[0])
|
24
|
+
var = _read_unpacked_ndl(input_path, **kwargs)
|
25
|
+
|
26
|
+
return var
|
27
|
+
|
28
|
+
|
29
|
+
def _read_unpacked_ndl(input_path, **kwargs):
|
30
|
+
"""
|
31
|
+
读取 input_path 中保存的嵌套字典列表
|
32
|
+
"""
|
33
|
+
from kevin_toolbox.nested_dict_list.serializer.variable import SERIALIZER_BACKEND
|
34
|
+
|
35
|
+
assert os.path.exists(input_path)
|
29
36
|
|
30
37
|
# 读取 var
|
31
38
|
var = json_.read(file_path=os.path.join(input_path, "var.json"), b_use_suggested_converter=True)
|
32
39
|
|
40
|
+
# 读取 record
|
41
|
+
record_s = dict()
|
42
|
+
if os.path.isfile(os.path.join(input_path, "record.json")):
|
43
|
+
record_s = json_.read(file_path=os.path.join(input_path, "record.json"), b_use_suggested_converter=True)
|
44
|
+
|
33
45
|
# 读取被处理的节点
|
34
46
|
processed_nodes = []
|
35
|
-
if
|
36
|
-
for name, value in ndl.get_nodes(
|
37
|
-
var=json_.read(file_path=os.path.join(input_path, "record.json"),
|
38
|
-
b_use_suggested_converter=True)["processed"], level=-1, b_strict=True):
|
47
|
+
if record_s:
|
48
|
+
for name, value in ndl.get_nodes(var=record_s["processed"], level=-1, b_strict=True):
|
39
49
|
if value:
|
40
50
|
processed_nodes.append(name)
|
41
51
|
else:
|
@@ -57,8 +67,9 @@ def read(input_path, **kwargs):
|
|
57
67
|
ndl.set_value(var=var, name=name, value=bk.read(**value))
|
58
68
|
|
59
69
|
#
|
60
|
-
if
|
61
|
-
|
70
|
+
if record_s.get("b_keep_identical_relations", False):
|
71
|
+
from kevin_toolbox.nested_dict_list import value_parser
|
72
|
+
var = value_parser.replace_identical_with_reference(var=var, flag="same", b_reverse=True)
|
62
73
|
|
63
74
|
return var
|
64
75
|
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Saved_Node_Name_Builder:
|
2
|
+
"""
|
3
|
+
生成保存节点内容时的文件夹/文件名称
|
4
|
+
"""
|
5
|
+
|
6
|
+
def __init__(self, format_):
|
7
|
+
try:
|
8
|
+
temp = format_.format(**{k: k + "_" * 3 for k in {"raw_name", "id", "hash_name", "count"}})
|
9
|
+
assert len(temp) > len(format_)
|
10
|
+
except:
|
11
|
+
raise ValueError(f'invalid saved_node_name_format {format_}')
|
12
|
+
|
13
|
+
self.format_ = format_
|
14
|
+
self.count = 0
|
15
|
+
|
16
|
+
def __call__(self, name, value):
|
17
|
+
from kevin_toolbox.nested_dict_list import get_hash
|
18
|
+
|
19
|
+
res = self.format_.format(
|
20
|
+
**{"raw_name": name, "id": id(value), "hash_name": get_hash(name, length=12), "count": self.count})
|
21
|
+
self.count += 1
|
22
|
+
return res
|
23
|
+
|
24
|
+
|
25
|
+
if __name__ == '__main__':
|
26
|
+
bd = Saved_Node_Name_Builder(format_="{raw_name}_{count}_{hash_name}_{id}")
|
27
|
+
print(bd(":a@0", 1))
|
28
|
+
print(bd(":b:c", []))
|
29
|
+
|
30
|
+
# bd = Saved_Node_Name_Builder(format_="")
|
31
|
+
# bd = Saved_Node_Name_Builder(format_="{raw_name2}_{count}")
|
@@ -1,22 +1,25 @@
|
|
1
1
|
import os
|
2
2
|
import time
|
3
3
|
import warnings
|
4
|
+
import tempfile
|
4
5
|
import kevin_toolbox
|
5
6
|
from kevin_toolbox.data_flow.file import json_
|
6
7
|
from kevin_toolbox.patches import for_os
|
7
8
|
import kevin_toolbox.nested_dict_list as ndl
|
8
9
|
from kevin_toolbox.nested_dict_list.traverse import Traversal_Mode
|
9
10
|
from .enum_variable import Strictness_Level
|
11
|
+
from .saved_node_name_builder import Saved_Node_Name_Builder
|
10
12
|
|
11
13
|
|
12
14
|
def write(var, output_dir, settings=None, traversal_mode=Traversal_Mode.BFS, b_pack_into_tar=True,
|
13
|
-
strictness_level=Strictness_Level.COMPATIBLE,
|
15
|
+
strictness_level=Strictness_Level.COMPATIBLE, saved_node_name_format='{count}_{hash_name}',
|
16
|
+
b_keep_identical_relations=False, **kwargs):
|
14
17
|
"""
|
15
18
|
将输入的嵌套字典列表 var 的结构和节点值保存到文件中
|
16
19
|
遍历 var,匹配并使用 settings 中设置的保存方式来对各部分结构/节点进行序列化
|
17
20
|
将会生成一个文件夹或者 .tar 文件,其中包含:
|
18
21
|
- var.json: 用于保存结构、简单节点值、复杂节点值/结构的序列化方式
|
19
|
-
- nodes
|
22
|
+
- nodes/: 该目录中包含一系列 <name>.<suffix> 文件或者 <name> 文件夹,其中包含复杂节点值/结构的序列化结果
|
20
23
|
- record.json: 其中记录了:
|
21
24
|
{
|
22
25
|
"processed": ... # 对哪些节点/部分进行了处理
|
@@ -82,25 +85,56 @@ def write(var, output_dir, settings=None, traversal_mode=Traversal_Mode.BFS, b_p
|
|
82
85
|
- "low" / Strictness_Level.IGNORE_FAILURE 匹配不完整,或者某些节点尝试过所有匹配到
|
83
86
|
的 backend 之后仍然无法写入
|
84
87
|
默认是 "normal"
|
88
|
+
saved_node_name_format: <str> nodes/目录下节点文件/文件夹的命名方式。
|
89
|
+
基本结构为: '{<part_0>}...{<part_1>}...'
|
90
|
+
其中 {} 内将根据 part 指定的类型进行自动填充。目前支持以下几种选项:
|
91
|
+
- "raw_name" 该节点对应位置的 name。
|
92
|
+
- "id" 该节点在当前内存中的 id。
|
93
|
+
- "hash_name" 该节点位置 name 的 hash 值。
|
94
|
+
- "count" 累加计算,表示是保存的第几个节点。
|
95
|
+
!!注意:
|
96
|
+
"raw_name" 该选项在 v1.3.3 前被使用,但是由于其可能含有 : 和 / 等特殊符号,当以其作为文件夹名时,
|
97
|
+
可能会引发错误。因此对于 windows 用户,禁止使用该选项,对于 mac 和 linux 用户,同样也不建议使用该选项。
|
98
|
+
"id" 虽然具有唯一性,但是其值对于每次运行是随机的。
|
99
|
+
"hash_name" 有极低的可能会发生 hash 碰撞。
|
100
|
+
综合而言:
|
101
|
+
建议使用 "hash_name" 和 "count" 的组合。
|
102
|
+
默认值为:
|
103
|
+
'{count}_{hash_name}'
|
104
|
+
b_keep_identical_relations: <boolean> 是否保留不同节点之间的 id 相等关系。
|
105
|
+
具体而言,就是使用 value_parser.replace_identical_with_reference() 函数将具有相同 id 的多个节点,
|
106
|
+
替换为单个节点和其多个引用的形式。
|
107
|
+
对于 ndl 中存在大量具有相同 id 的重复节点的情况,使用该操作可以额外达到压缩的效果。
|
108
|
+
默认为 False
|
85
109
|
"""
|
86
110
|
from kevin_toolbox.nested_dict_list.serializer.variable import SERIALIZER_BACKEND
|
87
111
|
|
88
|
-
#
|
112
|
+
# 检查参数
|
89
113
|
traversal_mode = Traversal_Mode(traversal_mode)
|
90
114
|
strictness_level = Strictness_Level(strictness_level)
|
91
|
-
|
115
|
+
#
|
116
|
+
assert not os.path.exists(output_dir + ".tar" if b_pack_into_tar else output_dir), f'target already exists'
|
117
|
+
os.makedirs(os.path.dirname(output_dir), exist_ok=True)
|
118
|
+
temp_dir = tempfile.TemporaryDirectory(dir=os.path.dirname(output_dir))
|
119
|
+
temp_output_dir = os.path.join(temp_dir.name, os.path.basename(output_dir))
|
120
|
+
os.makedirs(temp_output_dir, exist_ok=True)
|
121
|
+
#
|
92
122
|
var = ndl.copy_(var=var, b_deepcopy=False)
|
123
|
+
if b_keep_identical_relations:
|
124
|
+
from kevin_toolbox.nested_dict_list import value_parser
|
125
|
+
var = value_parser.replace_identical_with_reference(var=var, flag="same", b_reverse=False)
|
93
126
|
if settings is None:
|
94
127
|
settings = [{"match_cond": "<level>-1", "backend": (":skip:simple", ":numpy:npy", ":torch:tensor", ":pickle")}]
|
128
|
+
snn_builder = Saved_Node_Name_Builder(format_=saved_node_name_format)
|
95
129
|
|
96
130
|
# 构建 processed_s
|
97
131
|
# 为了避免重复处理节点/结构,首先构建与 var 具有相似结构的 processed_s 来记录处理处理进度。
|
98
132
|
# 对于 processed_s,其节点值为 True 时表示该节点已经被处理,当节点值为 False 或者 list/dict 类型时表示该节点或者节点下面的结构中仍然
|
99
133
|
# 存在未处理的部分。
|
100
134
|
# 对于中间节点,只有其下所有叶节点都未处理时才会被匹配。
|
101
|
-
processed_s =
|
135
|
+
processed_s = ndl.copy_(var=var, b_deepcopy=False, b_keep_internal_references=False)
|
102
136
|
for n, _ in ndl.get_nodes(var=var, level=-1, b_strict=True):
|
103
|
-
ndl.set_value(var=processed_s, name=n, value=False, b_force=
|
137
|
+
ndl.set_value(var=processed_s, name=n, value=False, b_force=False)
|
104
138
|
# processed_s_bak 用于记录 var 的原始结构
|
105
139
|
processed_s_bak = ndl.copy_(var=processed_s, b_deepcopy=True)
|
106
140
|
if "_hook_for_debug" in kwargs:
|
@@ -117,7 +151,7 @@ def write(var, output_dir, settings=None, traversal_mode=Traversal_Mode.BFS, b_p
|
|
117
151
|
backend_name_ls = setting["backend"] if isinstance(setting["backend"], (list, tuple)) else [setting["backend"]]
|
118
152
|
for i in backend_name_ls:
|
119
153
|
if i not in backend_s:
|
120
|
-
backend_s[i] = SERIALIZER_BACKEND.get(name=i)(folder=os.path.join(
|
154
|
+
backend_s[i] = SERIALIZER_BACKEND.get(name=i)(folder=os.path.join(temp_output_dir, "nodes"))
|
121
155
|
#
|
122
156
|
t_mode = Traversal_Mode(setting.get("traversal_mode", traversal_mode))
|
123
157
|
# _process and paras
|
@@ -126,27 +160,21 @@ def write(var, output_dir, settings=None, traversal_mode=Traversal_Mode.BFS, b_p
|
|
126
160
|
_process = _process_from_top_to_down
|
127
161
|
else:
|
128
162
|
_process = _process_from_down_to_top
|
129
|
-
paras = dict(
|
130
|
-
var=var, processed_s=processed_s, match_cond=setting["match_cond"],
|
131
|
-
traversal_mode=t_mode, strictness_level=strictness_level
|
132
|
-
)
|
163
|
+
paras = dict(var=var, match_cond=setting["match_cond"], traversal_mode=t_mode)
|
133
164
|
elif setting["match_cond"].startswith("<level>"):
|
134
165
|
_process = _process_for_level
|
135
|
-
paras = dict(
|
136
|
-
var=var, processed_s=processed_s, processed_s_bak=processed_s_bak,
|
137
|
-
level=int(setting["match_cond"][7:]), strictness_level=strictness_level
|
138
|
-
)
|
166
|
+
paras = dict(var=var, processed_s_bak=processed_s_bak, level=int(setting["match_cond"][7:]))
|
139
167
|
elif setting["match_cond"].startswith("<node>"):
|
140
168
|
_process = _process_for_name
|
141
|
-
paras = dict(var=var,
|
142
|
-
strictness_level=strictness_level)
|
169
|
+
paras = dict(var=var, name=setting["match_cond"][6:])
|
143
170
|
else:
|
144
171
|
raise ValueError(f'invalid match_cond: {setting["match_cond"]}')
|
145
172
|
# 执行
|
146
173
|
for i in backend_name_ls:
|
147
174
|
# print(processed_s)
|
148
175
|
# print(f'backend: {i}')
|
149
|
-
_process(backend=backend_s[i],
|
176
|
+
_process(backend=backend_s[i], strictness_level=strictness_level, processed_s=processed_s,
|
177
|
+
snn_builder=snn_builder, **paras)
|
150
178
|
if "_hook_for_debug" in kwargs:
|
151
179
|
kwargs["_hook_for_debug"]["processed"].append([i, ndl.copy_(var=processed_s, b_deepcopy=True)])
|
152
180
|
|
@@ -168,16 +196,19 @@ def write(var, output_dir, settings=None, traversal_mode=Traversal_Mode.BFS, b_p
|
|
168
196
|
f'please check settings to make sure all nodes have been covered and can be deal with backend'
|
169
197
|
|
170
198
|
# 保存 var 的结构
|
171
|
-
json_.write(content=var, file_path=os.path.join(
|
199
|
+
json_.write(content=var, file_path=os.path.join(temp_output_dir, "var.json"), b_use_suggested_converter=True)
|
172
200
|
# 保存处理结果(非必要)
|
173
201
|
json_.write(content=dict(processed=processed_s, raw_structure=processed_s_bak, timestamp=time.time(),
|
174
|
-
kt_version=kevin_toolbox.__version__
|
175
|
-
|
202
|
+
kt_version=kevin_toolbox.__version__,
|
203
|
+
b_keep_identical_relations=b_keep_identical_relations),
|
204
|
+
file_path=os.path.join(temp_output_dir, "record.json"), b_use_suggested_converter=True)
|
176
205
|
|
177
206
|
# 打包成 .tar 文件
|
178
207
|
if b_pack_into_tar:
|
179
|
-
for_os.pack(source=
|
180
|
-
|
208
|
+
for_os.pack(source=temp_output_dir)
|
209
|
+
os.rename(temp_output_dir + ".tar", output_dir + ".tar")
|
210
|
+
else:
|
211
|
+
os.rename(temp_output_dir, output_dir)
|
181
212
|
|
182
213
|
|
183
214
|
def _judge_processed_or_not(processed_s, name):
|
@@ -196,13 +227,13 @@ def _judge_processed_or_not(processed_s, name):
|
|
196
227
|
return b_processed
|
197
228
|
|
198
229
|
|
199
|
-
def _process_for_level(var, processed_s, processed_s_bak, level, backend, strictness_level):
|
230
|
+
def _process_for_level(var, processed_s, processed_s_bak, level, backend, strictness_level, snn_builder):
|
200
231
|
for name, _ in ndl.get_nodes(var=processed_s_bak, level=level, b_strict=True):
|
201
232
|
_process_for_name(var=var, processed_s=processed_s, name=name, backend=backend,
|
202
|
-
strictness_level=strictness_level)
|
233
|
+
strictness_level=strictness_level, snn_builder=snn_builder)
|
203
234
|
|
204
235
|
|
205
|
-
def _process_for_name(var, processed_s, name, backend, strictness_level):
|
236
|
+
def _process_for_name(var, processed_s, name, backend, strictness_level, snn_builder):
|
206
237
|
if _judge_processed_or_not(processed_s=processed_s, name=name) is True:
|
207
238
|
# has been processed
|
208
239
|
return
|
@@ -212,8 +243,9 @@ def _process_for_name(var, processed_s, name, backend, strictness_level):
|
|
212
243
|
return
|
213
244
|
|
214
245
|
# write by backend
|
246
|
+
snn_name = snn_builder(name=name, value=value)
|
215
247
|
try:
|
216
|
-
res = backend.write(name=
|
248
|
+
res = backend.write(name=snn_name, var=value)
|
217
249
|
except:
|
218
250
|
assert strictness_level in (Strictness_Level.IGNORE_FAILURE, Strictness_Level.COMPATIBLE), \
|
219
251
|
f'An error occurred when node {name} was saved using the first matched backend {backend}'
|
@@ -222,7 +254,7 @@ def _process_for_name(var, processed_s, name, backend, strictness_level):
|
|
222
254
|
ndl.set_value(var=var, name=name, value=res, b_force=False)
|
223
255
|
|
224
256
|
|
225
|
-
def _process_from_top_to_down(var, processed_s, match_cond, backend, traversal_mode, strictness_level):
|
257
|
+
def _process_from_top_to_down(var, processed_s, match_cond, backend, traversal_mode, strictness_level, snn_builder):
|
226
258
|
def match_cond_(parent_type, idx, value):
|
227
259
|
nonlocal match_cond, processed_s
|
228
260
|
|
@@ -237,8 +269,9 @@ def _process_from_top_to_down(var, processed_s, match_cond, backend, traversal_m
|
|
237
269
|
nonlocal processed_s, backend, strictness_level
|
238
270
|
|
239
271
|
# write by backend
|
272
|
+
snn_name = snn_builder(name=idx, value=value)
|
240
273
|
try:
|
241
|
-
res = backend.write(name=
|
274
|
+
res = backend.write(name=snn_name, var=value)
|
242
275
|
except:
|
243
276
|
assert strictness_level in (Strictness_Level.IGNORE_FAILURE, Strictness_Level.COMPATIBLE), \
|
244
277
|
f'An error occurred when node {name} was saved using the first matched backend {backend}'
|
@@ -250,7 +283,7 @@ def _process_from_top_to_down(var, processed_s, match_cond, backend, traversal_m
|
|
250
283
|
b_use_name_as_idx=True, traversal_mode=traversal_mode, b_traverse_matched_element=False)
|
251
284
|
|
252
285
|
|
253
|
-
def _process_from_down_to_top(var, processed_s, match_cond, backend, traversal_mode, strictness_level):
|
286
|
+
def _process_from_down_to_top(var, processed_s, match_cond, backend, traversal_mode, strictness_level, snn_builder):
|
254
287
|
processed_s_raw, processed_s = processed_s, ndl.copy_(var=processed_s, b_deepcopy=True)
|
255
288
|
|
256
289
|
def match_cond_(parent_type, idx, value):
|
@@ -268,8 +301,9 @@ def _process_from_down_to_top(var, processed_s, match_cond, backend, traversal_m
|
|
268
301
|
nonlocal processed_s, backend, processed_s_raw, strictness_level
|
269
302
|
|
270
303
|
# write by backend
|
304
|
+
snn_name = snn_builder(name=idx, value=value)
|
271
305
|
try:
|
272
|
-
res = backend.write(name=
|
306
|
+
res = backend.write(name=snn_name, var=value)
|
273
307
|
except:
|
274
308
|
assert strictness_level in (Strictness_Level.IGNORE_FAILURE, Strictness_Level.COMPATIBLE), \
|
275
309
|
f'An error occurred when node {name} was saved using the first matched backend {backend}'
|
@@ -305,7 +339,7 @@ if __name__ == '__main__':
|
|
305
339
|
{"match_cond": lambda _, __, value: not isinstance(value, (list, dict)),
|
306
340
|
"backend": (":skip:simple",)},
|
307
341
|
]
|
308
|
-
write(var=var_, output_dir=os.path.join(os.path.dirname(__file__), "
|
342
|
+
write(var=var_, output_dir=os.path.join(os.path.dirname(__file__), "temp"), traversal_mode="bfs",
|
309
343
|
b_pack_into_tar=True, settings=settings_, _hook_for_debug=_hook_for_debug)
|
310
344
|
|
311
345
|
for bk_name, p in _hook_for_debug["processed"]:
|
@@ -2,3 +2,4 @@ from .cal_relation_between_references import cal_relation_between_references
|
|
2
2
|
from .eval_references import eval_references
|
3
3
|
from .parse_references import parse_references
|
4
4
|
from .parse_and_eval_references import parse_and_eval_references
|
5
|
+
from .replace_identical_with_reference import replace_identical_with_reference
|
@@ -0,0 +1,127 @@
|
|
1
|
+
from collections import defaultdict
|
2
|
+
from kevin_toolbox.nested_dict_list import get_nodes, get_value, set_value
|
3
|
+
from kevin_toolbox.nested_dict_list import value_parser
|
4
|
+
|
5
|
+
|
6
|
+
def replace_identical_with_reference(var, flag="same", match_cond=None, b_reverse=False):
|
7
|
+
"""
|
8
|
+
将具有相同 id 的多个节点,替换为单个节点和其多个引用的形式
|
9
|
+
一般用于去除冗余部分,压缩 ndl 的结构
|
10
|
+
|
11
|
+
参数:
|
12
|
+
var:
|
13
|
+
flag: <str> 引用标记头,表示该节点应该替换为指定节点的内容
|
14
|
+
默认为 "same"
|
15
|
+
注意,在正向过程中,如果遇到本身就是以该 flag 开头的字符串,会自动在前添加多一个 flag 以示区分,
|
16
|
+
然后在逆向过程中,遇到两个 flag 标记开头的字符串将删除一个,然后跳过不处理。
|
17
|
+
match_cond: <func> 仅对匹配上(返回True视为匹配上)的节点进行处理
|
18
|
+
函数类型为 def(name, value)
|
19
|
+
其中:
|
20
|
+
name 该节点在结构体中的位置
|
21
|
+
value 节点的值
|
22
|
+
默认不对 int、float、bool、str、None 等类型进行处理
|
23
|
+
b_reverse: <boolean> 是否进行逆向操作
|
24
|
+
"""
|
25
|
+
if match_cond is None:
|
26
|
+
match_cond = lambda name, value: not isinstance(value, (int, float, bool, str, type(None)))
|
27
|
+
assert callable(match_cond)
|
28
|
+
|
29
|
+
if b_reverse:
|
30
|
+
return _reverse(var, flag)
|
31
|
+
else:
|
32
|
+
return _forward(var, flag, match_cond)
|
33
|
+
|
34
|
+
|
35
|
+
def _forward(var, flag, match_cond):
|
36
|
+
id_to_height_s = defaultdict(set)
|
37
|
+
id_to_name_s = defaultdict(set)
|
38
|
+
height = 1
|
39
|
+
while True:
|
40
|
+
node_ls = get_nodes(var=var, level=-height, b_strict=True)
|
41
|
+
if not node_ls:
|
42
|
+
break
|
43
|
+
for name, value in node_ls:
|
44
|
+
if not match_cond(name, value):
|
45
|
+
continue
|
46
|
+
id_to_name_s[id(value)].add(name)
|
47
|
+
id_to_height_s[id(value)].add(height)
|
48
|
+
height += 1
|
49
|
+
|
50
|
+
#
|
51
|
+
for k, v in list(id_to_height_s.items()):
|
52
|
+
# 仅保留有多个节点对应的 id
|
53
|
+
if len(id_to_name_s[k]) <= 1:
|
54
|
+
id_to_height_s.pop(k)
|
55
|
+
id_to_name_s.pop(k)
|
56
|
+
continue
|
57
|
+
# 具有相同 id 的节点所处的高度应该相同
|
58
|
+
assert len(v) == 1, f'nodes {id_to_name_s[k]} have different heights: {v}'
|
59
|
+
# 按高度排序
|
60
|
+
id_vs_height = sorted([(k, v.pop()) for k, v in id_to_height_s.items()], key=lambda x: x[1], reverse=True)
|
61
|
+
|
62
|
+
# 从高到低,依次将具有相同 id 的节点替换为 单个节点和多个引用 的形式
|
63
|
+
temp = []
|
64
|
+
processed_name_set = set()
|
65
|
+
for k, _ in id_vs_height:
|
66
|
+
# 找出父节点仍然未被处理的节点(亦即仍然能够访问到的节点)
|
67
|
+
unprocessed_name_set = {n for n in id_to_name_s[k] if id(get_value(var=var, name=n, default=temp)) == k}
|
68
|
+
if len(unprocessed_name_set) <= 1:
|
69
|
+
continue
|
70
|
+
# 任选其一进行保留,其余改为引用
|
71
|
+
keep_name = unprocessed_name_set.pop()
|
72
|
+
for name in unprocessed_name_set:
|
73
|
+
try:
|
74
|
+
var = set_value(var=var, name=name, value=f'<{flag}>{{{keep_name}}}', b_force=False)
|
75
|
+
except:
|
76
|
+
breakpoint()
|
77
|
+
processed_name_set.update(unprocessed_name_set)
|
78
|
+
|
79
|
+
# 将叶节点中,未被处理过,且是 str,且以 flag 开头的字符串,添加多一个 flag,以示区分
|
80
|
+
for name, value in get_nodes(var=var, level=-1, b_strict=True):
|
81
|
+
if name not in processed_name_set and isinstance(value, str) and value.startswith(f'<{flag}>'):
|
82
|
+
var = set_value(var=var, name=name, value=f'<{flag}>' + value, b_force=False)
|
83
|
+
|
84
|
+
return var
|
85
|
+
|
86
|
+
|
87
|
+
class _My_Str:
|
88
|
+
def __init__(self, s):
|
89
|
+
self.s = s
|
90
|
+
|
91
|
+
|
92
|
+
def _reverse(var, flag):
|
93
|
+
# 找出叶节点中,带有2个以上 flag 标记的字符串,删除其中一个标记,并使用 _My_Str 包裹,以便与普通引用节点区分
|
94
|
+
for name, value in get_nodes(var=var, level=-1, b_strict=True):
|
95
|
+
if isinstance(value, str) and value.startswith(f'<{flag}><{flag}>'):
|
96
|
+
var = set_value(var=var, name=name, value=_My_Str(value[len(flag) + 2:]), b_force=False)
|
97
|
+
# 解释引用
|
98
|
+
var, _ = value_parser.parse_and_eval_references(var=var, flag=flag)
|
99
|
+
# 解除 _My_Str 包裹
|
100
|
+
for name, value in get_nodes(var=var, level=-1, b_strict=True):
|
101
|
+
if isinstance(value, _My_Str):
|
102
|
+
var = set_value(var=var, name=name, value=value.s, b_force=False)
|
103
|
+
return var
|
104
|
+
|
105
|
+
|
106
|
+
if __name__ == '__main__':
|
107
|
+
import numpy as np
|
108
|
+
from kevin_toolbox.nested_dict_list import copy_
|
109
|
+
|
110
|
+
a = np.array([1, 2, 3])
|
111
|
+
b = np.ones((2, 3))
|
112
|
+
c = [a, b]
|
113
|
+
d = {"a": a, "b": b}
|
114
|
+
e = {"c1": c, "c2": c}
|
115
|
+
x = [e, a, d, c, "<same>{@1}", "<same><same>{@1}"]
|
116
|
+
|
117
|
+
print(x)
|
118
|
+
|
119
|
+
y = replace_identical_with_reference(var=copy_(x, b_deepcopy=True), flag="same")
|
120
|
+
print(y)
|
121
|
+
|
122
|
+
x1 = replace_identical_with_reference(var=y, flag="same", b_reverse=True)
|
123
|
+
print(x1)
|
124
|
+
|
125
|
+
from kevin_toolbox.patches.for_test import check_consistency
|
126
|
+
|
127
|
+
check_consistency(x, x1)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
import os
|
2
|
+
import shutil
|
3
|
+
from kevin_toolbox.patches.for_os import remove
|
4
|
+
|
5
|
+
|
6
|
+
def copy(src, dst, follow_symlinks=True, remove_dst_if_exists=False):
|
7
|
+
"""
|
8
|
+
复制文件/文件夹/软连接
|
9
|
+
|
10
|
+
参数:
|
11
|
+
follow_symlinks: <boolean> 是否跟随符号链接
|
12
|
+
对于 src 是软连接或者 src 指向的目录下具有软连接的情况,
|
13
|
+
当设置为 True 时,会复制所有链接指向的实际文件或目录内容。
|
14
|
+
当设置为 False 时,则仅仅软连接本身,而不是指向的内容
|
15
|
+
默认为 True
|
16
|
+
remove_dst_if_exists: <boolean> 当目标存在时,是否尝试进行移除
|
17
|
+
"""
|
18
|
+
assert os.path.exists(src), f'failed to copy, src not exists: {src}'
|
19
|
+
if remove_dst_if_exists:
|
20
|
+
remove(path=dst, ignore_errors=True)
|
21
|
+
assert not os.path.exists(dst), f'failed to copy, dst exists: {dst}'
|
22
|
+
|
23
|
+
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
24
|
+
if os.path.isdir(src):
|
25
|
+
if not follow_symlinks and os.path.islink(src):
|
26
|
+
# 如果是符号链接,并且我们跟随符号链接,则复制链接本身
|
27
|
+
os.symlink(os.readlink(src), dst)
|
28
|
+
else:
|
29
|
+
# 否则,递归复制目录
|
30
|
+
shutil.copytree(src, dst, symlinks=not follow_symlinks)
|
31
|
+
else:
|
32
|
+
# 复制文件
|
33
|
+
shutil.copy2(src, dst, follow_symlinks=follow_symlinks)
|
@@ -7,7 +7,8 @@ import kevin_toolbox.nested_dict_list as ndl
|
|
7
7
|
def check_consistency(*args, tolerance=1e-7, require_same_shape=True):
|
8
8
|
"""
|
9
9
|
检查 args 中多个变量之间是否一致
|
10
|
-
变量支持python的所有内置类型,以及复杂的 nested_dict_list 结构, array 等
|
10
|
+
变量支持 python 的所有内置类型,以及复杂的 nested_dict_list 结构, array 等
|
11
|
+
对于 array,不区分 numpy 的 array,torch 的 tensor,还是 tuple of number,只要其中的值相等,即视为相同。
|
11
12
|
|
12
13
|
参数:
|
13
14
|
tolerance: <float> 判断 <np.number/np.bool_> 之间是否一致时,的容许误差。
|
@@ -28,13 +29,13 @@ def check_consistency(*args, tolerance=1e-7, require_same_shape=True):
|
|
28
29
|
try:
|
29
30
|
_check_item(*names_ls, tolerance=tolerance, require_same_shape=True)
|
30
31
|
except AssertionError as e:
|
31
|
-
|
32
|
+
raise AssertionError(f'inputs <nested_dict_list> has different structure\nthe nodes that differ are:\n{e}')
|
32
33
|
for its in zip(names_ls[0], *values_ls):
|
33
34
|
try:
|
34
35
|
_check_item(*its[1:], tolerance=tolerance, require_same_shape=require_same_shape)
|
35
36
|
except AssertionError as e:
|
36
|
-
|
37
|
-
f'value of nodes {its[0]} in inputs <nested_dict_list> are inconsistent\nthe difference is:\n{e}'
|
37
|
+
raise AssertionError(
|
38
|
+
f'value of nodes {its[0]} in inputs <nested_dict_list> are inconsistent\nthe difference is:\n{e}')
|
38
39
|
# 简单结构
|
39
40
|
else:
|
40
41
|
_check_item(*args, tolerance=tolerance, require_same_shape=require_same_shape)
|
@@ -45,44 +46,111 @@ def _check_item(*args, tolerance, require_same_shape):
|
|
45
46
|
检查 args 中多个 array 之间是否一致
|
46
47
|
|
47
48
|
工作流程:
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
49
|
+
1. 对于 args 都是 tuple/list 且每个 tuple/list 的长度都一致的情况,将会拆分为对应各个元素递归进行比较。
|
50
|
+
2. 将 args 中的 tuple 和 tensor 分别转换为 list 和 numpy。
|
51
|
+
3. 检查 args 中是否有 np.array 或者 tensor,若有则根据 require_same_shape 判断其形状是否一致。
|
52
|
+
4. 先将输入的 args 中的所有变量转换为 np.array;
|
53
|
+
然后使用 issubclass() 判断转换后得到的变量属于以下哪几种基本类型:
|
54
|
+
- 当所有变量都属于 np.number 数值(包含int、float等)或者 np.bool_ 布尔值时,
|
55
|
+
将对变量两两求差,当差值小于给定的容许误差 tolerance 时,视为一致。
|
56
|
+
注意:在比较过程中,若变量中存在 np.nan 值,将会首先比较有 np.nan 值的位置是否相等,然后再比较非 np.nan 值部分。
|
57
|
+
亦即在相同位置上都具有 np.nan 视为相同。
|
58
|
+
比如 [np.nan, np.nan, 3] 和 [np.nan, np.nan, 3] 会视为相等,
|
59
|
+
[np.nan, np.nan] 和 np.nan 在 require_same_shape=False 时会被视为相等。
|
60
|
+
- 当所有变量都属于 np.flexible 可变长度类型(包含string等)或者 np.object 时,
|
61
|
+
将使用==进行比较,当返回值都为 True 时,视为一致。
|
62
|
+
- 当变量的基本类型不一致(比如同时有np.number和np.flexible)时,
|
63
|
+
直接判断为不一致。
|
64
|
+
numpy 中基本类型之间的继承关系参见: https://numpy.org.cn/reference/arrays/scalars.html
|
57
65
|
|
58
66
|
参数:
|
59
67
|
tolerance: <float> 判断 <np.number/np.bool_> 之间是否一致时,的容许误差。
|
60
68
|
require_same_shape: <boolean> 是否强制要求变量的形状一致。
|
61
|
-
|
69
|
+
注意:仅在原始 args 中含有 np.array 或者 tensor 的情况会采取 broadcast,亦即此时该参数才会起效。
|
70
|
+
当设置为 False 时,不同形状的变量可能因为 broadcast 机制而在比较前自动 reshape 为相同维度,进而通过比较。
|
62
71
|
"""
|
63
|
-
|
72
|
+
warnings.filterwarnings('ignore', category=np.VisibleDeprecationWarning)
|
64
73
|
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
65
74
|
|
66
75
|
assert len(args) >= 2
|
67
|
-
assert isinstance(tolerance, (int, float,))
|
76
|
+
assert isinstance(tolerance, (int, float,)) and tolerance >= 0
|
77
|
+
raw_args = args
|
78
|
+
|
79
|
+
# 当 args 都是 tuple/list 且每个 tuple 的长度都一致时,将会拆分为对应各个元素进行比较
|
80
|
+
if all([isinstance(i, (tuple, list)) for i in args]) and all([len(i) == len(args[0]) for i in args[1:]]):
|
81
|
+
for i, it_ls in enumerate(zip(*args)):
|
82
|
+
try:
|
83
|
+
_check_item(*it_ls, tolerance=tolerance, require_same_shape=require_same_shape)
|
84
|
+
except AssertionError as e:
|
85
|
+
raise AssertionError(f'elements {i} are inconsistent\nthe difference is:\n{e}')
|
86
|
+
return
|
87
|
+
|
88
|
+
#
|
89
|
+
args = ndl.traverse(var=list(args), match_cond=lambda _, __, v: isinstance(v, (tuple,)), action_mode="replace",
|
90
|
+
converter=lambda _, v: list(v), b_traverse_matched_element=True)
|
91
|
+
args = ndl.traverse(var=list(args), match_cond=lambda _, __, v: torch.is_tensor(v), action_mode="replace",
|
92
|
+
converter=lambda _, v: v.detach().cpu().numpy(), b_traverse_matched_element=False)
|
93
|
+
b_has_raw_array = any([isinstance(i, np.ndarray) for i in args])
|
68
94
|
|
69
|
-
|
70
|
-
|
95
|
+
try:
|
96
|
+
args = [np.asarray(v) for v in args] # if b_has_raw_array else [np.array(v, dtype=object) for v in args]
|
97
|
+
except Exception as e:
|
98
|
+
raise RuntimeError(f'{raw_args} cannot be converted to np.array, \n'
|
99
|
+
f'because {e}')
|
71
100
|
|
72
|
-
|
101
|
+
# 比较形状
|
102
|
+
if b_has_raw_array:
|
73
103
|
if require_same_shape:
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
104
|
+
# 要求形状一致
|
105
|
+
for v in args[1:]:
|
106
|
+
assert args[0].shape == v.shape, \
|
107
|
+
f"{args[0]}, {v}, different shape: {args[0].shape}, {v.shape}"
|
108
|
+
else:
|
109
|
+
# 否则要求至少能够进行 broadcast
|
110
|
+
for v in args[1:]:
|
111
|
+
try:
|
112
|
+
np.broadcast_arrays(args[0], v)
|
113
|
+
except:
|
114
|
+
raise AssertionError(f'{args[0]}, {v}, failed to broadcast')
|
115
|
+
# 如果都是空的 array,直接视为相等
|
116
|
+
if all([i.size == 0 for i in args]):
|
117
|
+
return
|
118
|
+
b_allow_broadcast = b_has_raw_array and not require_same_shape
|
119
|
+
|
120
|
+
# 比较值
|
121
|
+
if issubclass(args[0].dtype.type, (np.number, np.bool_,)):
|
122
|
+
# 数字类型
|
123
|
+
for v in args[1:]:
|
78
124
|
assert issubclass(v.dtype.type, (np.number, np.bool_,))
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
#
|
125
|
+
v_0, v_1 = args[0].astype(dtype=float), v.astype(dtype=float)
|
126
|
+
v_0, v_1 = np.broadcast_arrays(v_0, v_1) if b_allow_broadcast else (v_0, v_1)
|
127
|
+
assert v_0.shape == v_1.shape, \
|
128
|
+
f'{v_0}, {v_1}, different shape: {v_0.shape}, {v_1.shape}'
|
129
|
+
#
|
130
|
+
if v_0.size > 0:
|
131
|
+
try:
|
132
|
+
if np.any(np.isnan(v)):
|
133
|
+
assert np.all(np.isnan(v_0) == np.isnan(v_1))
|
134
|
+
v_0 = np.nan_to_num(v_0, nan=1e10)
|
135
|
+
v_1 = np.nan_to_num(v_1, nan=1e10)
|
136
|
+
assert np.max(np.abs(v_0 - v_1)) < tolerance
|
137
|
+
except AssertionError:
|
138
|
+
raise AssertionError(f"{args[0]}, {v}, deviation: {np.max(np.abs(args[0] - v))}")
|
139
|
+
elif issubclass(args[0].dtype.type, (np.flexible, object,)):
|
140
|
+
# 可变长度类型
|
141
|
+
for v in args[1:]:
|
84
142
|
assert issubclass(v.dtype.type, (np.flexible, object,))
|
85
|
-
|
143
|
+
v_0, v_1 = np.broadcast_arrays(args[0], v) if b_allow_broadcast else (args[0], v)
|
144
|
+
assert v_0.shape == v_1.shape, \
|
145
|
+
f'{v_0}, {v_1}, different shape: {v_0.shape}, {v_1.shape}\n' + (
|
146
|
+
'' if require_same_shape else
|
147
|
+
f'\tMore details: \n'
|
148
|
+
f'\t\tAlthough require_same_shape=False has been setted, broadcast failed because the variable at \n'
|
149
|
+
f'\t\tthis position does not contain elements of type np.array and tensor.')
|
150
|
+
#
|
151
|
+
for i, j in zip(v_0.reshape(-1), v_1.reshape(-1)):
|
152
|
+
if i is j:
|
153
|
+
continue
|
86
154
|
temp = i == j
|
87
155
|
if isinstance(temp, (bool,)):
|
88
156
|
assert temp, \
|
@@ -90,14 +158,17 @@ def _check_item(*args, tolerance, require_same_shape):
|
|
90
158
|
else:
|
91
159
|
assert temp.all(), \
|
92
160
|
f"{args[0]}, {v}, diff: {temp}"
|
93
|
-
|
94
|
-
|
161
|
+
else:
|
162
|
+
raise ValueError
|
95
163
|
|
96
164
|
|
97
165
|
if __name__ == '__main__':
|
98
166
|
a = np.array([[1, 2, 3]])
|
99
167
|
b = np.array([[1, 2, 3]])
|
100
168
|
c = {'d': 3, 'c': 4}
|
101
|
-
check_consistency([c, a], [c, b])
|
102
169
|
|
103
|
-
|
170
|
+
# var = ((1, 2), (4))
|
171
|
+
#
|
172
|
+
# var = ndl.traverse(var=[var], match_cond=lambda _, __, v: isinstance(v, (tuple,)), action_mode="replace",
|
173
|
+
# converter=lambda _, v: list(v), b_traverse_matched_element=True)[0]
|
174
|
+
# print(var)
|
@@ -0,0 +1,74 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: kevin-toolbox-dev
|
3
|
+
Version: 1.3.5
|
4
|
+
Summary: 一个常用的工具代码包集合
|
5
|
+
Home-page: https://github.com/cantbeblank96/kevin_toolbox
|
6
|
+
Download-URL: https://github.com/username/your-package/archive/refs/tags/v1.0.0.tar.gz
|
7
|
+
Author: kevin hsu
|
8
|
+
Author-email: xukaiming1996@163.com
|
9
|
+
License: MIT
|
10
|
+
Keywords: mathematics,pytorch,numpy,machine-learning,algorithm
|
11
|
+
Platform: UNKNOWN
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
13
|
+
Classifier: Programming Language :: Python
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
15
|
+
Requires-Python: >=3.6
|
16
|
+
Description-Content-Type: text/markdown
|
17
|
+
Requires-Dist: torch (>=1.2.0)
|
18
|
+
Requires-Dist: numpy (>=1.19.0)
|
19
|
+
Provides-Extra: plot
|
20
|
+
Requires-Dist: matplotlib (>=3.0) ; extra == 'plot'
|
21
|
+
Provides-Extra: rest
|
22
|
+
Requires-Dist: pytest (>=6.2.5) ; extra == 'rest'
|
23
|
+
Requires-Dist: line-profiler (>=3.5) ; extra == 'rest'
|
24
|
+
|
25
|
+
# kevin_toolbox
|
26
|
+
|
27
|
+
一个通用的工具代码包集合
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
环境要求
|
32
|
+
|
33
|
+
```shell
|
34
|
+
numpy>=1.19
|
35
|
+
pytorch>=1.2
|
36
|
+
```
|
37
|
+
|
38
|
+
安装方法:
|
39
|
+
|
40
|
+
```shell
|
41
|
+
pip install kevin-toolbox --no-dependencies
|
42
|
+
```
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
[项目地址 Repo](https://github.com/cantbeblank96/kevin_toolbox)
|
47
|
+
|
48
|
+
[使用指南 User_Guide](./notes/User_Guide.md)
|
49
|
+
|
50
|
+
[免责声明 Disclaimer](./notes/Disclaimer.md)
|
51
|
+
|
52
|
+
[版本更新记录](./notes/Release_Record.md):
|
53
|
+
|
54
|
+
- v 1.3.5 (2024-05-18)【bug fix】【new feature】
|
55
|
+
- patches
|
56
|
+
- for_os
|
57
|
+
- 【new feature】add copy(),无论是文件/目录/软连接都可以使用该函数进行复制。
|
58
|
+
- 同时支持follow_symlinks参数用于决定是否跟随符号链接复制其指向的内容,支持remove_dst_if_exists用于决定当目标存在时是否尝试进行移除。
|
59
|
+
- for_test
|
60
|
+
- 【bug fix】fix bug in check_consistency(),解决了以下问题:
|
61
|
+
- 对于含有 np.nan 值的array错误地一律都判断为不相等,修改后将相同位置的 np.nan 值视为相等。
|
62
|
+
- require_same_shape=False 时无法正常为不对齐的 可变长度类型 报错。
|
63
|
+
- 对于包含 requires_grad=True 的 tensor 的复杂 tuple 异常报错。
|
64
|
+
- 添加了对应的测试用例。
|
65
|
+
- nested_dict_list
|
66
|
+
- 【bug fix】modify temporary file management in write() and read(),加强对写入和读取时创建的临时文件的管理,保证异常退出时能够自动清理临时文件夹。
|
67
|
+
- data_flow.file
|
68
|
+
- json_
|
69
|
+
- 【new feature】modify read(),新增了 file_obj 参数,支持直接从 BytesIO 和 StringIO 中读取json文件。
|
70
|
+
- 添加了对应的测试用例。
|
71
|
+
- 在更高版本的numpy中已经没有 np.warnings 了,因此将所有 np.warnings 替换为 warnings。
|
72
|
+
|
73
|
+
|
74
|
+
|
@@ -1,4 +1,4 @@
|
|
1
|
-
kevin_toolbox/__init__.py,sha256=
|
1
|
+
kevin_toolbox/__init__.py,sha256=JzwJXkcT4vV0qixg5n7pd24pB0WTgH5FVJcfB3f6ZCQ,410
|
2
2
|
kevin_toolbox/computer_science/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
kevin_toolbox/computer_science/algorithm/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
4
4
|
kevin_toolbox/computer_science/algorithm/cache_manager/__init__.py,sha256=p2hddkZ1HfYF9-m2Hx-o9IotwQHd4QwDCePy2ADpTDA,41
|
@@ -71,7 +71,7 @@ kevin_toolbox/data_flow/core/reader/unified_reader.py,sha256=l6JxPoDUOdx2ZIPX2WL
|
|
71
71
|
kevin_toolbox/data_flow/core/reader/unified_reader_base.py,sha256=4gIADdV8UKpt2yD8dZjQsXFcF75nJ83ooIae3D7bw2s,11783
|
72
72
|
kevin_toolbox/data_flow/file/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
73
73
|
kevin_toolbox/data_flow/file/json_/__init__.py,sha256=VAt8COS2tO3PJRuhSc43i35fEOlArFM_YahdTmEBaHE,85
|
74
|
-
kevin_toolbox/data_flow/file/json_/read_json.py,sha256=
|
74
|
+
kevin_toolbox/data_flow/file/json_/read_json.py,sha256=BhAUCcagwPsSMaMeCJyyxDW3h9SGf1Dfvb0nXi6B_T8,2084
|
75
75
|
kevin_toolbox/data_flow/file/json_/write_json.py,sha256=mWaxePr_QzfyeCb0hAy4xTKOGX7q0eFjep0jDqOqIgw,2379
|
76
76
|
kevin_toolbox/data_flow/file/json_/converter/__init__.py,sha256=oQMgAgzELLq_f4LIIfz5E6l_E7g4lFsXqfmnJ3tPZTY,401
|
77
77
|
kevin_toolbox/data_flow/file/json_/converter/convert_dict_key_to_number.py,sha256=SuSZj_HCqKZutHAJ5AttABnGBRZplPGQhMxJBt2Wlgc,559
|
@@ -84,7 +84,7 @@ kevin_toolbox/data_flow/file/json_/converter/unescape_tuple_and_set.py,sha256=3v
|
|
84
84
|
kevin_toolbox/data_flow/file/kevin_notation/__init__.py,sha256=g9UUF9nJrOMc0ndCwVROQLsux4Or2yVMJMdhK5P9nRc,259
|
85
85
|
kevin_toolbox/data_flow/file/kevin_notation/converter.py,sha256=5k_Yxw-fBKEkxFG0bnl1fRsz06MlUS-4f3gZ--bmDs8,3621
|
86
86
|
kevin_toolbox/data_flow/file/kevin_notation/kevin_notation_reader.py,sha256=iN6nV8mMbifTbECNmjc-G2pzpxivhks5kCXGVdvS9fQ,8297
|
87
|
-
kevin_toolbox/data_flow/file/kevin_notation/kevin_notation_writer.py,sha256=
|
87
|
+
kevin_toolbox/data_flow/file/kevin_notation/kevin_notation_writer.py,sha256=Py8kyyi9RgxCSpuGebNkaxc9g4S8TdtiNJNDrJyP2UA,16328
|
88
88
|
kevin_toolbox/data_flow/file/kevin_notation/read.py,sha256=w0RE0WwTmWycEozJVshAiE0gMBxproBRwBEMLS6tB6c,544
|
89
89
|
kevin_toolbox/data_flow/file/kevin_notation/write.py,sha256=gPabz_h2mtXqCTyBVzip_QSb6L4tsrNSqibFzuqsIv8,556
|
90
90
|
kevin_toolbox/data_flow/file/kevin_notation/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -227,14 +227,15 @@ kevin_toolbox/nested_dict_list/name_handler/__init__.py,sha256=P_pWq78oN6NdvWg2h
|
|
227
227
|
kevin_toolbox/nested_dict_list/name_handler/build_name.py,sha256=VPWyjE8i8l-4Zm4tkD06Ie4J2NCsmI32ecOxZQqqmok,989
|
228
228
|
kevin_toolbox/nested_dict_list/name_handler/escape_node.py,sha256=niT9MxmsyrSZYhKXlWzdoKXVYhWRCR-kmQBkZopznpA,1163
|
229
229
|
kevin_toolbox/nested_dict_list/name_handler/parse_name.py,sha256=vUlAXPocpVSxtb3EnRi7U5K40Tz9plFG-_sbwLfYiy4,2280
|
230
|
-
kevin_toolbox/nested_dict_list/serializer/__init__.py,sha256=
|
230
|
+
kevin_toolbox/nested_dict_list/serializer/__init__.py,sha256=79dd9l-mNz0bycFKjNm7YsfWPR-JsVx9NoG_Ofqy-HQ,153
|
231
231
|
kevin_toolbox/nested_dict_list/serializer/enum_variable.py,sha256=RWPydtXI4adOJYGo_k5CWHSL0Odzj_bsahb24p1ranY,847
|
232
|
-
kevin_toolbox/nested_dict_list/serializer/read.py,sha256=
|
232
|
+
kevin_toolbox/nested_dict_list/serializer/read.py,sha256=Ms86R8jRvuvbIWiyL8WUYXvU9APRXe72w9q_YcOKUWM,2944
|
233
|
+
kevin_toolbox/nested_dict_list/serializer/saved_node_name_builder.py,sha256=qsD-rmDmVaKZP4owN3Wm3QY2Ksi71XlYETqw4VmIsSU,1011
|
233
234
|
kevin_toolbox/nested_dict_list/serializer/variable.py,sha256=ZywG6obipRBCGY1cY42gdvsuWk8GLZXr6eCYcW7ZJ9c,392
|
234
|
-
kevin_toolbox/nested_dict_list/serializer/write.py,sha256=
|
235
|
+
kevin_toolbox/nested_dict_list/serializer/write.py,sha256=VMwD579NBl0QZeHh9P4WuFzA3CdtuRaqLLu5S5NGt5E,21468
|
235
236
|
kevin_toolbox/nested_dict_list/serializer/backends/__init__.py,sha256=8g7y-L3cmctxao616dVkGiot00FJzKNmNl_69V2bSmE,39
|
236
237
|
kevin_toolbox/nested_dict_list/serializer/backends/_json_.py,sha256=oJXIc28yjxsD9ZJuw120pVHTVsTzCdaXEhVUSQeydq4,2145
|
237
|
-
kevin_toolbox/nested_dict_list/serializer/backends/_ndl.py,sha256=
|
238
|
+
kevin_toolbox/nested_dict_list/serializer/backends/_ndl.py,sha256=3YkAq_Bqzehnw0kGxqxwtF6uUz0EV37tLI-1ROHjixY,1794
|
238
239
|
kevin_toolbox/nested_dict_list/serializer/backends/_numpy_bin.py,sha256=xiPFmPUTjy0X0R1E0N8mrByENhNb69QalHnbYQXFvTo,1470
|
239
240
|
kevin_toolbox/nested_dict_list/serializer/backends/_numpy_npy.py,sha256=CF6R7ie68zA0TqAXBdxUgHKVDYtEPHfVXR9rMFBbsdw,1384
|
240
241
|
kevin_toolbox/nested_dict_list/serializer/backends/_pickle_.py,sha256=yCe4BXwSU55fbBX-HbTWI5ReT9Hhd7knu5zqvgUw5t4,1282
|
@@ -243,11 +244,12 @@ kevin_toolbox/nested_dict_list/serializer/backends/_skip_simple.py,sha256=dS9kKh
|
|
243
244
|
kevin_toolbox/nested_dict_list/serializer/backends/_torch_all.py,sha256=aw9M1Hep65lkvb8_QU3VkecE6q3l4UqXansh4lnMv7s,1282
|
244
245
|
kevin_toolbox/nested_dict_list/serializer/backends/_torch_tensor.py,sha256=Wiimzc0XGxFRYJiipzVnUd27scaJZZTOYp5OxYo3cKg,1353
|
245
246
|
kevin_toolbox/nested_dict_list/serializer/backends/backend_base.py,sha256=SZpLRhdSIykHZ_Ds3HX96lKLfXCyKzMQ_lx139XXBtc,1741
|
246
|
-
kevin_toolbox/nested_dict_list/value_parser/__init__.py,sha256=
|
247
|
+
kevin_toolbox/nested_dict_list/value_parser/__init__.py,sha256=MgbpkiXzVuA_i3VZ4VDNrQZfzpUZN3uaHrgOt_wq-4E,313
|
247
248
|
kevin_toolbox/nested_dict_list/value_parser/cal_relation_between_references.py,sha256=m3P7q5-pbbWqtjOjJUT-499q4mCyjtFEtFpGgovShSg,3119
|
248
249
|
kevin_toolbox/nested_dict_list/value_parser/eval_references.py,sha256=YQyOm3awKVRusXxSNObjJ2yPf0oE4gleobOn_RN_nzU,2301
|
249
250
|
kevin_toolbox/nested_dict_list/value_parser/parse_and_eval_references.py,sha256=RQEDFFNAhQQcX9H8curwja-pI2gKZlVx4M2qeneBOhA,2370
|
250
251
|
kevin_toolbox/nested_dict_list/value_parser/parse_references.py,sha256=G470xNzrRpYlS5To8R5yV0M6nX4iE5LLMp_eV49bh3Y,2116
|
252
|
+
kevin_toolbox/nested_dict_list/value_parser/replace_identical_with_reference.py,sha256=4nd5q3qTi4sTfOqHF0-HyLMDS9a5x8wgwETfpDI8jh8,5419
|
251
253
|
kevin_toolbox/patches/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
252
254
|
kevin_toolbox/patches/for_logging/__init__.py,sha256=xymF6mjwY4Cin7CoEwanFY5ZVk8oY0pDLqjZAGa7_Rg,39
|
253
255
|
kevin_toolbox/patches/for_logging/build_logger.py,sha256=0UoRMKaERd8wlHGTiNR3dbLQRAtXcb0QZuGkX2oFgGo,3071
|
@@ -280,13 +282,14 @@ kevin_toolbox/patches/for_optuna/serialize/for_study/load.py,sha256=DwnDheJx5IJp
|
|
280
282
|
kevin_toolbox/patches/for_optuna/serialize/for_trial/__init__.py,sha256=YSROCv89O84gzzRP2NfVl0Xhd4_RsdoqU3Nnr-vhWhk,46
|
281
283
|
kevin_toolbox/patches/for_optuna/serialize/for_trial/dump.py,sha256=FT-Z1rzCNUYz5St1Yc0bfDWAV10WaV41rlbWIS72Jp0,982
|
282
284
|
kevin_toolbox/patches/for_optuna/serialize/for_trial/load.py,sha256=2fpeeHPKA9bT7CjQ6DVRXOarF6IAA6_f2pXbB1rXcvE,796
|
283
|
-
kevin_toolbox/patches/for_os/__init__.py,sha256=
|
285
|
+
kevin_toolbox/patches/for_os/__init__.py,sha256=jx4UCTxEW2QhLfgfPhxcgUrftvs_2420IAQJso82DeQ,151
|
286
|
+
kevin_toolbox/patches/for_os/copy.py,sha256=PWFLu15DpIA4JZxatvphHANNn2H3nC93qTbLLxDl5NU,1509
|
284
287
|
kevin_toolbox/patches/for_os/pack.py,sha256=A6u4g3dfwXPtlU4gBcThNrktz6dO4DVi2wmQXytqfDI,656
|
285
288
|
kevin_toolbox/patches/for_os/remove.py,sha256=PmwqzVJbyfdqwXn_T1F9d4Oar8CwQ2YFaqcZQkfnrnI,750
|
286
289
|
kevin_toolbox/patches/for_os/unpack.py,sha256=d_fO7nPmExy1VIg7ADIMayCzjBBeFxvJLhIsulIRlzI,1047
|
287
290
|
kevin_toolbox/patches/for_os/walk.py,sha256=LrtEeRUDwzZgu_zGZ-kPsFJd4D-8R8ECHW6WNdEsDSw,8376
|
288
291
|
kevin_toolbox/patches/for_test/__init__.py,sha256=sFr2VZD1zk8Vtjq2_F8uE4xNovJF6yDY8j1YND5XAw0,49
|
289
|
-
kevin_toolbox/patches/for_test/check_consistency.py,sha256=
|
292
|
+
kevin_toolbox/patches/for_test/check_consistency.py,sha256=cerf4NywkvWYMvuJUjimfRRVU7D9vL30jTAX0NxxRoM,9422
|
290
293
|
kevin_toolbox/patches/for_torch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
291
294
|
kevin_toolbox/patches/for_torch/compatible/__init__.py,sha256=7l4FPTILBR8EmStJNHIsbwfFkZpe4xkJZtrlyC8riL8,75
|
292
295
|
kevin_toolbox/patches/for_torch/compatible/concat.py,sha256=vFJn9B437gxDOa5Ze_gXxZewiClqoiIkWeAktDEcJK4,393
|
@@ -302,7 +305,7 @@ kevin_toolbox/patches/for_torch/math/get_y_at_x.py,sha256=bfoVcasZ_tMdhR_1Me0Jli
|
|
302
305
|
kevin_toolbox/patches/for_torch/math/my_around.py,sha256=ptpU3ids50gwf663EpHbw7raj9tNrDGBFZ5t_uMNH14,1378
|
303
306
|
kevin_toolbox/patches/for_torch/nn/__init__.py,sha256=aJs3RMqRzQmd8KKDmQW9FxwCqS5yfPqEdg-m0PwlQro,39
|
304
307
|
kevin_toolbox/patches/for_torch/nn/lambda_layer.py,sha256=KUuLiX_Dr4bvRmpAaCW5QTDWDcnMPRnw0jg4NNXTFhM,223
|
305
|
-
kevin_toolbox_dev-1.3.
|
306
|
-
kevin_toolbox_dev-1.3.
|
307
|
-
kevin_toolbox_dev-1.3.
|
308
|
-
kevin_toolbox_dev-1.3.
|
308
|
+
kevin_toolbox_dev-1.3.5.dist-info/METADATA,sha256=ftmjWCBMUjdd756VCDmVAXgSXT1hIBWpZpvzngyAWCQ,2636
|
309
|
+
kevin_toolbox_dev-1.3.5.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
310
|
+
kevin_toolbox_dev-1.3.5.dist-info/top_level.txt,sha256=S5TeRGF-PwlhsaUEPTI-f2vWrpLmh3axpyI6v-Fi75o,14
|
311
|
+
kevin_toolbox_dev-1.3.5.dist-info/RECORD,,
|
@@ -1,75 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: kevin-toolbox-dev
|
3
|
-
Version: 1.3.3
|
4
|
-
Summary: 一个常用的工具代码包集合
|
5
|
-
Home-page: https://github.com/cantbeblank96/kevin_toolbox
|
6
|
-
Download-URL: https://github.com/username/your-package/archive/refs/tags/v1.0.0.tar.gz
|
7
|
-
Author: kevin hsu
|
8
|
-
Author-email: xukaiming1996@163.com
|
9
|
-
License: MIT
|
10
|
-
Keywords: mathematics,pytorch,numpy,machine-learning,algorithm
|
11
|
-
Platform: UNKNOWN
|
12
|
-
Classifier: License :: OSI Approved :: MIT License
|
13
|
-
Classifier: Programming Language :: Python
|
14
|
-
Classifier: Programming Language :: Python :: 3
|
15
|
-
Requires-Python: >=3.6
|
16
|
-
Description-Content-Type: text/markdown
|
17
|
-
Requires-Dist: torch (>=1.2.0)
|
18
|
-
Requires-Dist: numpy (>=1.19.0)
|
19
|
-
Provides-Extra: plot
|
20
|
-
Requires-Dist: matplotlib (>=3.0) ; extra == 'plot'
|
21
|
-
Provides-Extra: rest
|
22
|
-
Requires-Dist: pytest (>=6.2.5) ; extra == 'rest'
|
23
|
-
Requires-Dist: line-profiler (>=3.5) ; extra == 'rest'
|
24
|
-
|
25
|
-
# kevin_toolbox
|
26
|
-
|
27
|
-
一个通用的工具代码包集合
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
环境要求
|
32
|
-
|
33
|
-
```shell
|
34
|
-
numpy>=1.19
|
35
|
-
pytorch>=1.2
|
36
|
-
```
|
37
|
-
|
38
|
-
安装方法:
|
39
|
-
|
40
|
-
```shell
|
41
|
-
pip install kevin-toolbox --no-dependencies
|
42
|
-
```
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
[项目地址 Repo](https://github.com/cantbeblank96/kevin_toolbox)
|
47
|
-
|
48
|
-
[使用指南 User_Guide](./notes/User_Guide.md)
|
49
|
-
|
50
|
-
[免责声明 Disclaimer](./notes/Disclaimer.md)
|
51
|
-
|
52
|
-
[版本更新记录](./notes/Release_Record.md):
|
53
|
-
|
54
|
-
- v 1.3.3 (2024-04-01)【bug fix】【new feature】
|
55
|
-
- math.utils
|
56
|
-
- 【bug fix】更正拼写错误,将原有的 spilt_integer_most_evenly() 中的 "spilt" 改正为 "split",新的函数名为 split_integer_most_evenly
|
57
|
-
|
58
|
-
- patches.for_numpy.random
|
59
|
-
- 【bug fix】modify get_rng(),改进以避免遇到 rng 中不存在的函数时报错。
|
60
|
-
|
61
|
-
- data_flow.file
|
62
|
-
- json_
|
63
|
-
- 【new feature】将 escape_tuple() 改成 escape_tuple_and_set(),新增对 set 进行处理。
|
64
|
-
- 【new feature】将 unescape_tuple() 改成 unescape_tuple_and_set()。
|
65
|
-
|
66
|
-
- core.reader
|
67
|
-
- 【new feature】为 File_Iterative_Reader 新增了 file_obj 参数,支持直接输入文件对象。
|
68
|
-
|
69
|
-
- kevin_notation
|
70
|
-
- 【new feature】根据 File_Iterative_Reader 的修改,为 Kevin_Notation_Reader 和 read() 对应增加 file_obj 参数,以支持直接输入文件对象。
|
71
|
-
- 补充了对应的测试用例。
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
File without changes
|
File without changes
|