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 CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "1.3.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 1727520805 --verbose 0'
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 os.path.isfile(file_path), f'file {file_path} not found'
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
- np.warnings.filterwarnings('ignore', category=np.VisibleDeprecationWarning)
8
+ warnings.filterwarnings('ignore', category=np.VisibleDeprecationWarning)
9
9
 
10
10
 
11
11
  class Kevin_Notation_Writer:
@@ -1,3 +1,4 @@
1
1
  from .write import write
2
2
  from .read import read
3
3
  from .enum_variable import Strictness_Level
4
+ from .saved_node_name_builder import Saved_Node_Name_Builder
@@ -27,7 +27,10 @@ class NDL(Backend_Base):
27
27
  """
28
28
  是否可以写
29
29
  """
30
- return True
30
+ if "name" in kwargs:
31
+ return not os.path.exists(os.path.join(self.paras["folder"], f'{kwargs["name"]}'))
32
+ else:
33
+ return True
31
34
 
32
35
  def readable(self, name, **kwargs):
33
36
  """
@@ -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
- temp_dir = None
21
- if os.path.isfile(input_path) and input_path.endswith(".tar"):
22
- while True:
23
- temp_dir = os.path.join(os.path.dirname(input_path), f'temp{time.time()}')
24
- if not os.path.isdir(temp_dir):
25
- os.makedirs(temp_dir)
26
- break
27
- for_os.unpack(source=input_path, target=temp_dir)
28
- input_path = os.path.join(temp_dir, os.listdir(temp_dir)[0])
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 os.path.isfile(os.path.join(input_path, "record.json")):
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 temp_dir is not None:
61
- for_os.remove(path=temp_dir, ignore_errors=True)
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, **kwargs):
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/目录: 其中包含一系列 <name>.<suffix> 文件或者 <name> 文件夹,其中包含复杂节点值/结构的序列化结果
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
- os.makedirs(output_dir, exist_ok=True)
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 = dict()
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=True)
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(output_dir, "nodes"))
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, processed_s=processed_s, name=setting["match_cond"][6:],
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], **paras)
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(output_dir, "var.json"), b_use_suggested_converter=True)
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
- file_path=os.path.join(output_dir, "record.json"), b_use_suggested_converter=True)
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=output_dir)
180
- for_os.remove(path=output_dir, ignore_errors=True)
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=name, var=value)
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=idx, var=value)
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=idx, var=value)
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__), "temp3"), traversal_mode="bfs",
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)
@@ -1,4 +1,5 @@
1
1
  from .remove import remove
2
+ from .copy import copy
2
3
  from .pack import pack
3
4
  from .unpack import unpack
4
5
  from .walk import walk, Path_Ignorer, Ignore_Scope
@@ -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
- assert False, f'inputs <nested_dict_list> has different structure\nthe nodes that differ are:\n{e}'
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
- assert False, \
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
- 本函数会首先将输入的 args 中的所有变量转换为 np.array;
49
- 然后使用 issubclass() 判断转换后得到的变量属于以下哪几种基本类型:
50
- - 当所有变量都属于 np.number 数值(包含int、float等)或者 np.bool_ 布尔值时,
51
- 将对变量两两求差,当差值小于给定的容许误差 tolerance 时,视为一致。
52
- - 当所有变量都属于 np.flexible 可变长度类型(包含string等)或者 np.object 时,
53
- 将使用==进行比较,当返回值都为 True 时,视为一致。
54
- - 当变量的基本类型不一致(比如同时有np.number和np.flexible)时,
55
- 直接判断为不一致。
56
- numpy 中基本类型之间的继承关系参见: https://numpy.org.cn/reference/arrays/scalars.html
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
- 当设置为 False 时,不同形状的变量可能因为 numpy broadcast 机制而在比较前自动 reshape 为相同维度,进而可能通过比较。
69
+ 注意:仅在原始 args 中含有 np.array 或者 tensor 的情况会采取 broadcast,亦即此时该参数才会起效。
70
+ 当设置为 False 时,不同形状的变量可能因为 broadcast 机制而在比较前自动 reshape 为相同维度,进而通过比较。
62
71
  """
63
- np.warnings.filterwarnings('ignore', category=np.VisibleDeprecationWarning)
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
- args = [v.detach().cpu() if torch.is_tensor(v) else v for v in args]
70
- args = [np.asarray(v) for v in args]
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
- for v in args[1:]:
101
+ # 比较形状
102
+ if b_has_raw_array:
73
103
  if require_same_shape:
74
- assert args[0].shape == v.shape, \
75
- f"{args[0]}, {v}, different shape: {args[0].shape}, {v.shape}"
76
- if issubclass(args[0].dtype.type, (np.number, np.bool_,)):
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
- if args[0].size > 0:
80
- assert np.max(np.abs(args[0] - v.astype(dtype=float))) < tolerance, \
81
- f"{args[0]}, {v}, deviation: {np.max(np.abs(args[0] - v))}"
82
- elif issubclass(args[0].dtype.type, (np.flexible, object,)):
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
- for i, j in zip(args[0].reshape(-1), v.reshape(-1)):
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
- else:
94
- raise ValueError
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
- check_consistency(True, True)
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=SLC9_d60jm2WkdRlGpjY4ED_icVGwHgWLxaZOqS0zbk,410
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=S-rlY-spIAJVyB-zPQOmKUDdtNAgedSmXHjkY_TNGUE,1808
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=dyzkufi_SM-uU7cyI3yLFd8U4GnyRR5pyS88op6mhRY,16331
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=xLDfzSZIDNBssHArUEK84gF6WZFR64qJy0iOpM8LbP8,92
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=8196AZW3y1eZNguw5fDi5gQmOtpJ0MdqhRNHZ5EWYHI,2577
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=sQtryi7NbVWynOMWpKKkqxoTf9dxSGE4yC4ReIAxT8U,18145
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=QMF4DFAnt1sp35atds6t44nfCYuIOeGgW1-SPfJq6KM,1652
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=KTXAUaounOQ8oWSX-XzJMIFoopgaodBAm9RmQGMBgP8,234
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=Q6GzEPWggV4EovJdrertlL2l__W7k0zUEpuQIQDcA7A,128
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=5zwjHc0M3A-RTYcgZpIugm6-L-m2pDSG496dCkg8Q4Q,5038
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.3.dist-info/METADATA,sha256=LkQQ3jJCLlHGvHeAnCCGC_uiNz6qoWLFNSt_K00N0qs,2228
306
- kevin_toolbox_dev-1.3.3.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
307
- kevin_toolbox_dev-1.3.3.dist-info/top_level.txt,sha256=S5TeRGF-PwlhsaUEPTI-f2vWrpLmh3axpyI6v-Fi75o,14
308
- kevin_toolbox_dev-1.3.3.dist-info/RECORD,,
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
-