halib 0.2.1__py3-none-any.whl → 0.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. halib/__init__.py +3 -3
  2. halib/common/__init__.py +0 -0
  3. halib/common/common.py +178 -0
  4. halib/common/rich_color.py +285 -0
  5. halib/filetype/csvfile.py +3 -9
  6. halib/filetype/ipynb.py +3 -5
  7. halib/filetype/jsonfile.py +0 -3
  8. halib/filetype/textfile.py +0 -1
  9. halib/filetype/videofile.py +91 -2
  10. halib/filetype/yamlfile.py +3 -3
  11. halib/online/projectmake.py +7 -6
  12. halib/online/tele_noti.py +165 -0
  13. halib/research/core/__init__.py +0 -0
  14. halib/research/core/base_config.py +144 -0
  15. halib/research/core/base_exp.py +157 -0
  16. halib/research/core/param_gen.py +108 -0
  17. halib/research/core/wandb_op.py +117 -0
  18. halib/research/data/__init__.py +0 -0
  19. halib/research/data/dataclass_util.py +41 -0
  20. halib/research/data/dataset.py +208 -0
  21. halib/research/data/torchloader.py +165 -0
  22. halib/research/perf/__init__.py +0 -0
  23. halib/research/perf/flop_calc.py +190 -0
  24. halib/research/perf/gpu_mon.py +58 -0
  25. halib/research/perf/perfcalc.py +363 -0
  26. halib/research/perf/perfmetrics.py +137 -0
  27. halib/research/perf/perftb.py +778 -0
  28. halib/research/perf/profiler.py +301 -0
  29. halib/research/viz/__init__.py +0 -0
  30. halib/research/viz/plot.py +754 -0
  31. halib/system/filesys.py +60 -20
  32. halib/system/path.py +73 -0
  33. halib/utils/dict.py +9 -0
  34. halib/utils/list.py +12 -0
  35. {halib-0.2.1.dist-info → halib-0.2.2.dist-info}/METADATA +4 -1
  36. {halib-0.2.1.dist-info → halib-0.2.2.dist-info}/RECORD +39 -14
  37. {halib-0.2.1.dist-info → halib-0.2.2.dist-info}/WHEEL +0 -0
  38. {halib-0.2.1.dist-info → halib-0.2.2.dist-info}/licenses/LICENSE.txt +0 -0
  39. {halib-0.2.1.dist-info → halib-0.2.2.dist-info}/top_level.txt +0 -0
halib/system/filesys.py CHANGED
@@ -2,13 +2,16 @@ import glob
2
2
  import os
3
3
  import shutil
4
4
  from distutils.dir_util import copy_tree
5
+ from concurrent.futures import ThreadPoolExecutor
5
6
 
7
+ COMMON_IMG_EXT = ["jpg", "jpeg", "png", "bmp", "tiff", "gif"]
8
+ COMMON_VIDEO_EXT = ["mp4", "avi", "mov", "mkv", "flv", "wmv"]
6
9
 
7
10
  def is_exist(path):
8
11
  return os.path.exists(path)
9
12
 
10
13
 
11
- def is_directory(path):
14
+ def is_dir(path):
12
15
  return os.path.isdir(path)
13
16
 
14
17
 
@@ -17,7 +20,7 @@ def get_current_dir():
17
20
 
18
21
 
19
22
  def change_current_dir(new_dir):
20
- if is_directory(new_dir):
23
+ if is_dir(new_dir):
21
24
  os.chdir(new_dir)
22
25
 
23
26
 
@@ -64,27 +67,64 @@ def list_files(directory):
64
67
  )
65
68
  return files
66
69
 
67
-
68
- def filter_files_by_extension(directory, ext, recursive=True):
69
- if is_directory(directory):
70
- result_files = []
70
+ def filter_files_by_extension(directory, ext=None, recursive=True, num_workers=0):
71
+ """
72
+ Filters files using glob and multithreading.
73
+ If ext is None, returns ALL files.
74
+
75
+ Args:
76
+ directory (str): Path to search.
77
+ ext (str, list, or None): Extension(s) to find. If None, return all files.
78
+ recursive (bool): Whether to search subdirectories.
79
+ num_workers (int): Number of threads for checking file existence.
80
+ """
81
+ assert os.path.exists(directory) and os.path.isdir(
82
+ directory
83
+ ), "Directory does not exist"
84
+
85
+ # 1. Normalize extensions to a tuple (only if ext is provided)
86
+ extensions = None
87
+ if ext is not None:
71
88
  if isinstance(ext, list):
72
- ext_list = ext
89
+ extensions = tuple(ext)
73
90
  else:
74
- ext_list = [ext]
75
- if not recursive:
76
- filter_pattern = f"{directory}/*"
91
+ extensions = (ext,)
92
+
93
+ # 2. Define pattern
94
+ pattern = (
95
+ os.path.join(directory, "**", "*")
96
+ if recursive
97
+ else os.path.join(directory, "*")
98
+ )
99
+
100
+ # 3. Helper function for the thread workers
101
+ def validate_file(path):
102
+ if os.path.isfile(path):
103
+ return path
104
+ return None
105
+
106
+ result_files = []
107
+ if num_workers <= 0:
108
+ num_workers = os.cpu_count() or 4
109
+ # 4. Initialize ThreadPool with user-defined workers
110
+ with ThreadPoolExecutor(max_workers=num_workers) as executor:
111
+ # Step A: Get the iterator (lazy evaluation)
112
+ all_paths = glob.iglob(pattern, recursive=recursive)
113
+
114
+ # Step B: Apply Filter
115
+ if extensions is None:
116
+ # If ext is None, we skip the endswith check and pass everything
117
+ candidate_paths = all_paths
77
118
  else:
78
- filter_pattern = f"{directory}/**/*"
79
-
80
- for ext_item in ext_list:
81
- ext_filter = f"{filter_pattern}.{ext_item}"
82
- files = glob.glob(filter_pattern, recursive=True)
83
- files = [f for f in files if is_file(f) and f.endswith(ext_item)]
84
- result_files.extend(files)
85
- return result_files
86
- else:
87
- raise OSError("Directory not exists")
119
+ # Filter by extension string FIRST (Fast CPU op)
120
+ candidate_paths = (p for p in all_paths if p.endswith(extensions))
121
+
122
+ # Step C: Parallelize the disk check (Slow I/O op)
123
+ for result in executor.map(validate_file, candidate_paths):
124
+ if result:
125
+ result_files.append(result)
126
+
127
+ return result_files
88
128
 
89
129
 
90
130
  def is_file(path):
halib/system/path.py ADDED
@@ -0,0 +1,73 @@
1
+ from ..common.common import *
2
+ from ..filetype import csvfile
3
+ import pandas as pd
4
+ import platform
5
+
6
+
7
+ PC_NAME_TO_ABBR = {
8
+ "DESKTOP-JQD9K01": "MainPC",
9
+ "DESKTOP-5IRHU87": "MSI_Laptop",
10
+ "DESKTOP-96HQCNO": "4090_SV",
11
+ "DESKTOP-Q2IKLC0": "4GPU_SV",
12
+ "DESKTOP-QNS3DNF": "1GPU_SV",
13
+ }
14
+
15
+ DEFAULT_ABBR_WORKING_DISK = {
16
+ "MainPC": "E:",
17
+ "MSI_Laptop": "D:",
18
+ "4090_SV": "E:",
19
+ "4GPU_SV": "D:",
20
+ }
21
+
22
+ def list_PCs(show=True):
23
+ df = pd.DataFrame(
24
+ list(PC_NAME_TO_ABBR.items()), columns=["PC Name", "Abbreviation"]
25
+ )
26
+ if show:
27
+ csvfile.fn_display_df(df)
28
+ return df
29
+
30
+
31
+ def get_PC_name():
32
+ return platform.node()
33
+
34
+
35
+ def get_PC_abbr_name():
36
+ pc_name = get_PC_name()
37
+ return PC_NAME_TO_ABBR.get(pc_name, "Unknown")
38
+
39
+
40
+ # ! This funcction search for full paths in the obj and normalize them according to the current platform and working disk
41
+ # ! E.g: "E:/zdataset/DFire", but working_disk: "D:", current_platform: "windows" => "D:/zdataset/DFire"
42
+ # ! E.g: "E:/zdataset/DFire", but working_disk: "D:", current_platform: "linux" => "/mnt/d/zdataset/DFire"
43
+ def normalize_paths(obj, working_disk, current_platform):
44
+ if isinstance(obj, dict):
45
+ for key, value in obj.items():
46
+ obj[key] = normalize_paths(value, working_disk, current_platform)
47
+ return obj
48
+ elif isinstance(obj, list):
49
+ for i, item in enumerate(obj):
50
+ obj[i] = normalize_paths(item, working_disk, current_platform)
51
+ return obj
52
+ elif isinstance(obj, str):
53
+ # Normalize backslashes to forward slashes for consistency
54
+ obj = obj.replace("\\", "/")
55
+ # Regex for Windows-style path: e.g., "E:/zdataset/DFire"
56
+ win_match = re.match(r"^([A-Z]):/(.*)$", obj)
57
+ # Regex for Linux-style path: e.g., "/mnt/e/zdataset/DFire"
58
+ lin_match = re.match(r"^/mnt/([a-z])/(.*)$", obj)
59
+ if win_match or lin_match:
60
+ rest = win_match.group(2) if win_match else lin_match.group(2)
61
+ if current_platform == "windows":
62
+ # working_disk is like "D:", so "D:/" + rest
63
+ new_path = working_disk + "/" + rest
64
+ elif current_platform == "linux":
65
+ # Extract drive letter from working_disk (e.g., "D:" -> "d")
66
+ drive_letter = working_disk[0].lower()
67
+ new_path = "/mnt/" + drive_letter + "/" + rest
68
+ else:
69
+ # Unknown platform, return original
70
+ return obj
71
+ return new_path
72
+ # For non-strings or non-path strings, return as is
73
+ return obj
halib/utils/dict.py ADDED
@@ -0,0 +1,9 @@
1
+ def flatten_dict(d, parent_key="", sep="."):
2
+ items = {}
3
+ for k, v in d.items():
4
+ key = f"{parent_key}{sep}{k}" if parent_key else k
5
+ if isinstance(v, dict):
6
+ items.update(flatten_dict(v, key, sep=sep))
7
+ else:
8
+ items[key] = v
9
+ return items
halib/utils/list.py ADDED
@@ -0,0 +1,12 @@
1
+ def subtract(list_a, list_b):
2
+ return [item for item in list_a if item not in list_b]
3
+
4
+
5
+ def union(list_a, list_b, no_duplicate=False):
6
+ if no_duplicate:
7
+ return list(set(list_a) | set(list_b))
8
+ else:
9
+ return list_a + list_b
10
+
11
+ def intersection(list_a, list_b):
12
+ return list(set(list_a) & set(list_b))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: halib
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Small library for common tasks
5
5
  Author: Hoang Van Ha
6
6
  Author-email: hoangvanhauit@gmail.com
@@ -53,6 +53,9 @@ Dynamic: summary
53
53
 
54
54
  # Helper package for coding and automation
55
55
 
56
+ **Version 0.2.2**
57
+ + reorganize packages with most changes in `research` package
58
+
56
59
  **Version 0.2.01**
57
60
  + `research/base_exp`: add `eval_exp` method to evaluate experiment (e.g., model evaluation on test set) after experiment running is done.
58
61
 
@@ -1,4 +1,4 @@
1
- halib/__init__.py,sha256=dHuxmYnZjeFT2VXuZU5yKX7c81XqcXX5tsKFtcaxkqI,1657
1
+ halib/__init__.py,sha256=ZPa8Dvj-NfbwlwzO0PiqnKtLvu7uKGNb3tRpw7S4ICM,1657
2
2
  halib/common.py,sha256=9hn-IXOlGZODoBHy8U2A0aLgmPEnTeQjbzAVGwXAjwo,4242
3
3
  halib/csvfile.py,sha256=Eoeni0NIbNG3mB5ESWAvNwhJxOjmCaPd1qqYRHImbvk,1567
4
4
  halib/cuda.py,sha256=1bvtBY8QvTWdLaxalzK9wqXPl0Ft3AfhcrebupxGzEA,1010
@@ -16,18 +16,22 @@ halib/tele_noti.py,sha256=xL8f889VFR65uVJTj1NNT3qSi3lJ7s_iDjcObwZ0-V0,5926
16
16
  halib/textfile.py,sha256=EhVFrit-nRBJx18e6rtIqcE1cSbgsLnMXe_kdhi1EPI,399
17
17
  halib/torchloader.py,sha256=-q9YE-AoHZE1xQX2dgNxdqtucEXYs4sQ22WXdl6EGfI,6500
18
18
  halib/videofile.py,sha256=NTLTZ-j6YD47duw2LN2p-lDQDglYFP1LpEU_0gzHLdI,4737
19
+ halib/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ halib/common/common.py,sha256=olkeXdFdojOkySP5aurzxKlehngRwBHdNBw5JfE4_fQ,5038
21
+ halib/common/rich_color.py,sha256=tyK5fl3Dtv1tKsfFzt_5Rco4Fj72QliA-w5aGXaVuqQ,6392
19
22
  halib/filetype/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- halib/filetype/csvfile.py,sha256=4Klf8YNzY1MaCD3o5Wp5GG3KMfQIBOEVzHV_7DO5XBo,6604
21
- halib/filetype/ipynb.py,sha256=hd1IxQmSdNyCT1WwteSmy6e_xFVpdEPC1jM0drr6ag4,1663
22
- halib/filetype/jsonfile.py,sha256=9LBdM7LV9QgJA1bzJRkq69qpWOP22HDXPGirqXTgSCw,480
23
- halib/filetype/textfile.py,sha256=QtuI5PdLxu4hAqSeafr3S8vCXwtvgipWV4Nkl7AzDYM,399
24
- halib/filetype/videofile.py,sha256=4nfVAYYtoT76y8P4WYyxNna4Iv1o2iV6xaMcUzNPC4s,4736
25
- halib/filetype/yamlfile.py,sha256=59P9cdqTx655XXeQtkmAJoR_UhhVN4L8Tro-kd8Ri5g,2741
23
+ halib/filetype/csvfile.py,sha256=dwWM1LglQr1SaSWq2KDl93fYzQn1638JGo_l1GbBhbk,6460
24
+ halib/filetype/ipynb.py,sha256=pd2LgmPa7ZbF0YlQJbeQZEsl6jHQUSoyVtkCT7WhU5Q,1657
25
+ halib/filetype/jsonfile.py,sha256=2HcBqXYjLNvqFok3PHOgH59vlhDCZLZpt7ezvgx1TFM,474
26
+ halib/filetype/textfile.py,sha256=3koEFyVme1SEHdud7TnjArHndoiqfMGfMdYY3NIFegM,397
27
+ halib/filetype/videofile.py,sha256=wDyZp7Dh0ZuNgQUvt8gLTpy3Flx1jDr-QsO4-jzriGE,8104
28
+ halib/filetype/yamlfile.py,sha256=ZV8-kL8MWVYC-HGjo6nv0gkJ4joauo43uqez5dqTOdk,2739
26
29
  halib/online/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
30
  halib/online/gdrive.py,sha256=RmF4y6UPxektkKIctmfT-pKWZsBM9FVUeld6zZmJkp0,7787
28
31
  halib/online/gdrive_mkdir.py,sha256=wSJkQMJCDuS1gxQ2lHQHq_IrJ4xR_SEoPSo9n_2WNFU,1474
29
32
  halib/online/gdrive_test.py,sha256=hMWzz4RqZwETHp4GG4WwVNFfYvFQhp2Boz5t-DqwMo0,1342
30
- halib/online/projectmake.py,sha256=Zrs96WgXvO4nIrwxnCOletL4aTBge-EoF0r7hpKO1w8,4034
33
+ halib/online/projectmake.py,sha256=-WhJNmM6RpAfnWcQeinDBFjRKig165l7uqBk-XQwcbA,4036
34
+ halib/online/tele_noti.py,sha256=ErR5ys8gHmdjjDfSfVJxBIbPxgH7QD0oNRQgZweGi4s,5941
31
35
  halib/research/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
36
  halib/research/base_config.py,sha256=AqZHZ0NNQ3WmUOfRzs36lf3o0FrehSdVLbdmgNpbV7A,2833
33
37
  halib/research/base_exp.py,sha256=mC2bcG3M-dIvAkLw3d4O4Nu0HyvfrmvHvHX4iW8qurY,5749
@@ -44,21 +48,42 @@ halib/research/plot.py,sha256=GBCXP1QnzRlNqjAl9UvGvW3I9II61DBStJNQThrLy38,28578
44
48
  halib/research/profiler.py,sha256=GRAewTo0jGkOputjmRwtYVfJYBze_ivsOnrW9exWkPQ,11772
45
49
  halib/research/torchloader.py,sha256=yqUjcSiME6H5W210363HyRUrOi3ISpUFAFkTr1w4DCw,6503
46
50
  halib/research/wandb_op.py,sha256=YzLEqME5kIRxi3VvjFkW83wnFrsn92oYeqYuNwtYRkY,4188
51
+ halib/research/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
+ halib/research/core/base_config.py,sha256=FXYFFGKry-HwNrjlPlTeTB-ROhM1Bow5Xxems2fdW5k,4204
53
+ halib/research/core/base_exp.py,sha256=6CpuTP43NA7gE5JUN4V_-UAKHrlzLPJNI8piCBh3p1s,5734
54
+ halib/research/core/param_gen.py,sha256=I9JHrDCaep4CjvApDoX0QzFuw38zMC2PsDFueuA7pjM,4271
55
+ halib/research/core/wandb_op.py,sha256=powL2QyLBqF-6PUGAOqd60s1npHLLKJxPns3S4hKeNo,4160
56
+ halib/research/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
+ halib/research/data/dataclass_util.py,sha256=OPZzqmug0be4JEq0hJ68pKjnyl0PRYQMVJGhKw1kvyk,1382
58
+ halib/research/data/dataset.py,sha256=hm7CDTmudo_KV21RF0TQknHsw44PKhlMoBs9i8WIq6k,6743
59
+ halib/research/data/torchloader.py,sha256=oWUplXlGd1IB6CqdRd-mGe-DfMjjZxz9hQ7SWONb-0s,6519
60
+ halib/research/perf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
61
+ halib/research/perf/flop_calc.py,sha256=Kb3Gwqc7QtGALZzfyYXBA_9SioReJpTJdUX84kqj-Aw,6907
62
+ halib/research/perf/gpu_mon.py,sha256=vD41_ZnmPLKguuq9X44SB_vwd9JrblO4BDzHLXZhhFY,2233
63
+ halib/research/perf/perfcalc.py,sha256=FSWDStz9f94dObyAHYHRtq4fuo0dIw7l9JH_x5Wd7cQ,16225
64
+ halib/research/perf/perfmetrics.py,sha256=qRiNiCKGUSTLY7gPMVMuVHGAAyeosfGWup2eM4490aw,5485
65
+ halib/research/perf/perftb.py,sha256=IWElg3OB5dmhfxnY8pMZvkL2y_EnvLmEx3gJlpUR1Fs,31066
66
+ halib/research/perf/profiler.py,sha256=5ZjES8kAqEsSV1mC3Yr_1ivFLwQDc_yv4HY7dKt_AS0,11782
67
+ halib/research/viz/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
+ halib/research/viz/plot.py,sha256=51FhD1mH4nthTrY3K4mrO5pxI5AzvHXpZy5_ToqkYHs,28580
47
69
  halib/sys/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
70
  halib/sys/cmd.py,sha256=b2x7JPcNnFjLGheIESVYvqAb-w2UwBM1PAwYxMZ5YjA,228
49
71
  halib/sys/filesys.py,sha256=ERpnELLDKJoTIIKf-AajgkY62nID4qmqmX5TkE95APU,2931
50
72
  halib/system/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
73
  halib/system/cmd.py,sha256=b2x7JPcNnFjLGheIESVYvqAb-w2UwBM1PAwYxMZ5YjA,228
52
- halib/system/filesys.py,sha256=ERpnELLDKJoTIIKf-AajgkY62nID4qmqmX5TkE95APU,2931
74
+ halib/system/filesys.py,sha256=102J2fkQhmH1_-HQVy2FQ4NOU8LTjMWV3hToT_APtq8,4401
75
+ halib/system/path.py,sha256=E1inJqRWqUYknP8Cdx0T5nUV-rLG9WYy2DiQmBxaQJc,2642
53
76
  halib/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
77
  halib/utils/dataclass_util.py,sha256=rj2IMLlUzbm2OlF5_B2dRTk9njZOaF7tTjYkOsq8uLY,1416
78
+ halib/utils/dict.py,sha256=wYE6Iw-_CnCWdMg9tpJ2Y2-e2ESkW9FxmdBkZkbUh80,299
55
79
  halib/utils/dict_op.py,sha256=wYE6Iw-_CnCWdMg9tpJ2Y2-e2ESkW9FxmdBkZkbUh80,299
56
80
  halib/utils/gpu_mon.py,sha256=vD41_ZnmPLKguuq9X44SB_vwd9JrblO4BDzHLXZhhFY,2233
81
+ halib/utils/list.py,sha256=BM-8sRhYyqF7bh4p7TQtV7P_gnFruUCA6DTUOombaZg,337
57
82
  halib/utils/listop.py,sha256=Vpa8_2fI0wySpB2-8sfTBkyi_A4FhoFVVvFiuvW8N64,339
58
83
  halib/utils/tele_noti.py,sha256=-4WXZelCA4W9BroapkRyIdUu9cUVrcJJhegnMs_WpGU,5928
59
84
  halib/utils/video.py,sha256=zLoj5EHk4SmP9OnoHjO8mLbzPdtq6gQPzTQisOEDdO8,3261
60
- halib-0.2.1.dist-info/licenses/LICENSE.txt,sha256=qZssdna4aETiR8znYsShUjidu-U4jUT9Q-EWNlZ9yBQ,1100
61
- halib-0.2.1.dist-info/METADATA,sha256=VZ-msDI3P56gyQR12cQm1Ea6HZ7A6c1tXAHtuSzadJU,6548
62
- halib-0.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
63
- halib-0.2.1.dist-info/top_level.txt,sha256=7AD6PLaQTreE0Fn44mdZsoHBe_Zdd7GUmjsWPyQ7I-k,6
64
- halib-0.2.1.dist-info/RECORD,,
85
+ halib-0.2.2.dist-info/licenses/LICENSE.txt,sha256=qZssdna4aETiR8znYsShUjidu-U4jUT9Q-EWNlZ9yBQ,1100
86
+ halib-0.2.2.dist-info/METADATA,sha256=d0kciUOqcsw8mMago2IYt6gH4X3MHExycZt4RqEX61M,6632
87
+ halib-0.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
88
+ halib-0.2.2.dist-info/top_level.txt,sha256=7AD6PLaQTreE0Fn44mdZsoHBe_Zdd7GUmjsWPyQ7I-k,6
89
+ halib-0.2.2.dist-info/RECORD,,
File without changes