node_to_json 0.1.1__tar.gz → 0.2.0__tar.gz
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.
- {node_to_json-0.1.1 → node_to_json-0.2.0}/PKG-INFO +1 -1
- {node_to_json-0.1.1 → node_to_json-0.2.0}/pyproject.toml +1 -1
- {node_to_json-0.1.1 → node_to_json-0.2.0}/pyproject.toml.orig +1 -1
- node_to_json-0.2.0/src/node_to_json/__init__.py +237 -0
- {node_to_json-0.1.1 → node_to_json-0.2.0}/src/node_to_json/asset_funcs.py +9 -0
- node_to_json-0.2.0/src/node_to_json/file_util.py +344 -0
- node_to_json-0.2.0/src/node_to_json/func_util.py +623 -0
- {node_to_json-0.1.1 → node_to_json-0.2.0}/src/node_to_json/node_getters.py +183 -33
- {node_to_json-0.1.1 → node_to_json-0.2.0}/src/node_to_json/node_registry.py +6 -7
- {node_to_json-0.1.1 → node_to_json-0.2.0}/src/node_to_json/node_setters.py +183 -59
- {node_to_json-0.1.1 → node_to_json-0.2.0}/src/node_to_json/setter_funcs.py +136 -89
- node_to_json-0.1.1/src/node_to_json/__init__.py +0 -53
- node_to_json-0.1.1/src/node_to_json/func_util.py +0 -90
- {node_to_json-0.1.1 → node_to_json-0.2.0}/README.md +0 -0
- {node_to_json-0.1.1 → node_to_json-0.2.0}/src/node_to_json/json_io.py +0 -0
- {node_to_json-0.1.1 → node_to_json-0.2.0}/src/node_to_json/py.typed +0 -0
- {node_to_json-0.1.1 → node_to_json-0.2.0}/src/node_to_json/type_util.py +0 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Blender 5.2
|
|
3
|
+
Serialize Node Groups to JSON files.
|
|
4
|
+
Load Node Groups from JSON files.
|
|
5
|
+
Author: Demingo Hill (Noizirom) (C)
|
|
6
|
+
"""
|
|
7
|
+
from .asset_funcs import (
|
|
8
|
+
get_asset_nodes,
|
|
9
|
+
asset_node_group,
|
|
10
|
+
nodes_dir,
|
|
11
|
+
get_asset_name_dict,
|
|
12
|
+
)
|
|
13
|
+
from .func_util import (
|
|
14
|
+
nested_dict,
|
|
15
|
+
grouped_by_dict,
|
|
16
|
+
convert_attr,
|
|
17
|
+
convert_default_value,
|
|
18
|
+
get_node_group_data_type,
|
|
19
|
+
get_node_groups_by_type,
|
|
20
|
+
get_reversed_node_groups_by_type,
|
|
21
|
+
get_dict_keys,
|
|
22
|
+
get_dict_values,
|
|
23
|
+
get_basename_keys,
|
|
24
|
+
get_basenames,
|
|
25
|
+
match_keys_basenames,
|
|
26
|
+
immutable_dict,
|
|
27
|
+
hash_dict,
|
|
28
|
+
hash_list,
|
|
29
|
+
is_string_blank,
|
|
30
|
+
string_has_space,
|
|
31
|
+
string_startswith_space,
|
|
32
|
+
string_has_escape_character,
|
|
33
|
+
string_startswith,
|
|
34
|
+
string_endswith,
|
|
35
|
+
string_char_count,
|
|
36
|
+
concat_gen,
|
|
37
|
+
gen_len,
|
|
38
|
+
get_max_series_num,
|
|
39
|
+
get_match_series_highest,
|
|
40
|
+
get_all_related_node_groups,
|
|
41
|
+
get_node_group_names_,
|
|
42
|
+
node_group_name_dict,
|
|
43
|
+
get_all_related_node_groups_wo_assests,
|
|
44
|
+
get_node_group_names_wo_assests,
|
|
45
|
+
node_group_name_dict_wo_assests,
|
|
46
|
+
delete_full_node_tree,
|
|
47
|
+
delete_geo_node_modifier,
|
|
48
|
+
get_modifier_stack_geo_nodes,
|
|
49
|
+
get_items,
|
|
50
|
+
get_attrs,
|
|
51
|
+
split_list_of_dicts,
|
|
52
|
+
string_point_check,
|
|
53
|
+
CallAfterFunc,
|
|
54
|
+
CallBeforeFunc,
|
|
55
|
+
benchmark,
|
|
56
|
+
get_profile,
|
|
57
|
+
)
|
|
58
|
+
from .json_io import dict_to_json, json_to_dict
|
|
59
|
+
from .node_getters import (
|
|
60
|
+
serialize_node_group,
|
|
61
|
+
serialize_mat_group,
|
|
62
|
+
serialize_material,
|
|
63
|
+
serialize_comp_group,
|
|
64
|
+
serialize_compositor,
|
|
65
|
+
get_node_build_data,
|
|
66
|
+
get_node_group_interface,
|
|
67
|
+
get_node_group_names,
|
|
68
|
+
get_node_presets,
|
|
69
|
+
get_group_nodes_presets,
|
|
70
|
+
get_special_node_presets,
|
|
71
|
+
get_geometry_node_input_presets,
|
|
72
|
+
get_geometry_node_presets,
|
|
73
|
+
get_node_group_input_presets,
|
|
74
|
+
get_node_group_presets,
|
|
75
|
+
special_nodes,
|
|
76
|
+
)
|
|
77
|
+
from .node_registry import (
|
|
78
|
+
bpy_types_funcs,
|
|
79
|
+
register_node_setter,
|
|
80
|
+
ng_getter_funcs,
|
|
81
|
+
register_ng_getter,
|
|
82
|
+
ng_setter_funcs,
|
|
83
|
+
register_ng_setter,
|
|
84
|
+
)
|
|
85
|
+
from .node_setters import (
|
|
86
|
+
create_node_group_from_data,
|
|
87
|
+
add_node_group,
|
|
88
|
+
create_shader_group_from_data,
|
|
89
|
+
create_material,
|
|
90
|
+
add_material_to_ob,
|
|
91
|
+
create_comp_group,
|
|
92
|
+
add_comp_group,
|
|
93
|
+
create_texture_group_from_data,
|
|
94
|
+
process_node,
|
|
95
|
+
get_name_dict,
|
|
96
|
+
get_group_dict,
|
|
97
|
+
set_special_node_presets,
|
|
98
|
+
set_geometry_node_input_presets,
|
|
99
|
+
set_geometry_node_presets,
|
|
100
|
+
set_node_group_input_presets,
|
|
101
|
+
set_node_group_presets,
|
|
102
|
+
)
|
|
103
|
+
from .file_util import (
|
|
104
|
+
is_file_accessible,
|
|
105
|
+
read_file,
|
|
106
|
+
write_file,
|
|
107
|
+
get_dir_files,
|
|
108
|
+
get_dir_file_stems,
|
|
109
|
+
copy_file_by_chunks,
|
|
110
|
+
read_file_by_chunks,
|
|
111
|
+
write_bytesio,
|
|
112
|
+
write_stringio,
|
|
113
|
+
inject_detect,
|
|
114
|
+
is_file_suspicious,
|
|
115
|
+
get_from_zip,
|
|
116
|
+
get_zip_file_list,
|
|
117
|
+
zip_from_folder,
|
|
118
|
+
zip_append,
|
|
119
|
+
read_from_zip,
|
|
120
|
+
create_filler_zip,
|
|
121
|
+
zip_data_file,
|
|
122
|
+
zip_data_files,
|
|
123
|
+
append_file_to_zip,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
__all__ = [
|
|
127
|
+
"get_asset_nodes",
|
|
128
|
+
"asset_node_group",
|
|
129
|
+
"nodes_dir",
|
|
130
|
+
"get_asset_name_dict",
|
|
131
|
+
"nested_dict",
|
|
132
|
+
"grouped_by_dict",
|
|
133
|
+
"convert_attr",
|
|
134
|
+
"convert_default_value",
|
|
135
|
+
"get_node_group_data_type",
|
|
136
|
+
"get_node_groups_by_type",
|
|
137
|
+
"get_reversed_node_groups_by_type",
|
|
138
|
+
"dict_to_json",
|
|
139
|
+
"json_to_dict",
|
|
140
|
+
"serialize_node_group",
|
|
141
|
+
"serialize_mat_group",
|
|
142
|
+
"serialize_material",
|
|
143
|
+
"serialize_comp_group",
|
|
144
|
+
"serialize_compositor",
|
|
145
|
+
"get_node_build_data",
|
|
146
|
+
"get_node_group_interface",
|
|
147
|
+
"get_node_group_names",
|
|
148
|
+
"bpy_types_funcs",
|
|
149
|
+
"register_node_setter",
|
|
150
|
+
"ng_getter_funcs",
|
|
151
|
+
"register_ng_getter",
|
|
152
|
+
"ng_setter_funcs",
|
|
153
|
+
"register_ng_setter",
|
|
154
|
+
"create_node_group_from_data",
|
|
155
|
+
"add_node_group",
|
|
156
|
+
"create_shader_group_from_data",
|
|
157
|
+
"create_material",
|
|
158
|
+
"add_material_to_ob",
|
|
159
|
+
"create_comp_group",
|
|
160
|
+
"add_comp_group",
|
|
161
|
+
"create_texture_group_from_data",
|
|
162
|
+
"process_node",
|
|
163
|
+
"get_name_dict",
|
|
164
|
+
"get_group_dict",
|
|
165
|
+
"set_special_node_presets",
|
|
166
|
+
"set_geometry_node_input_presets",
|
|
167
|
+
"set_geometry_node_presets",
|
|
168
|
+
"set_node_group_input_presets",
|
|
169
|
+
"set_node_group_presets",
|
|
170
|
+
"get_node_presets",
|
|
171
|
+
"get_group_nodes_presets",
|
|
172
|
+
"get_special_node_presets",
|
|
173
|
+
"get_geometry_node_input_presets",
|
|
174
|
+
"get_geometry_node_presets",
|
|
175
|
+
"get_node_group_input_presets",
|
|
176
|
+
"get_node_group_presets",
|
|
177
|
+
"special_nodes",
|
|
178
|
+
"get_dict_keys",
|
|
179
|
+
"get_dict_values",
|
|
180
|
+
"get_basename_keys",
|
|
181
|
+
"get_basenames",
|
|
182
|
+
"match_keys_basenames",
|
|
183
|
+
"immutable_dict",
|
|
184
|
+
"hash_dict",
|
|
185
|
+
"hash_list",
|
|
186
|
+
"is_string_blank",
|
|
187
|
+
"string_has_space",
|
|
188
|
+
"string_startswith_space",
|
|
189
|
+
"string_has_escape_character",
|
|
190
|
+
"string_startswith",
|
|
191
|
+
"string_endswith",
|
|
192
|
+
"string_char_count",
|
|
193
|
+
"concat_gen",
|
|
194
|
+
"gen_len",
|
|
195
|
+
"get_max_series_num",
|
|
196
|
+
"get_match_series_highest",
|
|
197
|
+
"get_all_related_node_groups",
|
|
198
|
+
"get_node_group_names_",
|
|
199
|
+
"node_group_name_dict",
|
|
200
|
+
"get_all_related_node_groups_wo_assests",
|
|
201
|
+
"get_node_group_names_wo_assests",
|
|
202
|
+
"node_group_name_dict_wo_assests",
|
|
203
|
+
"delete_full_node_tree",
|
|
204
|
+
"delete_geo_node_modifier",
|
|
205
|
+
"get_modifier_stack_geo_nodes",
|
|
206
|
+
"is_file_accessible",
|
|
207
|
+
"read_file",
|
|
208
|
+
"write_file",
|
|
209
|
+
"get_dir_files",
|
|
210
|
+
"get_dir_file_stems",
|
|
211
|
+
"copy_file_by_chunks",
|
|
212
|
+
"read_file_by_chunks",
|
|
213
|
+
"write_bytesio",
|
|
214
|
+
"write_stringio",
|
|
215
|
+
"inject_detect",
|
|
216
|
+
"is_file_suspicious",
|
|
217
|
+
"get_from_zip",
|
|
218
|
+
"get_zip_file_list",
|
|
219
|
+
"zip_from_folder",
|
|
220
|
+
"zip_append",
|
|
221
|
+
"read_from_zip",
|
|
222
|
+
"create_filler_zip",
|
|
223
|
+
"zip_data_file",
|
|
224
|
+
"zip_data_files",
|
|
225
|
+
"append_file_to_zip",
|
|
226
|
+
"get_items",
|
|
227
|
+
"get_attrs",
|
|
228
|
+
"split_list_of_dicts",
|
|
229
|
+
"string_point_check",
|
|
230
|
+
"CallAfterFunc",
|
|
231
|
+
"CallBeforeFunc",
|
|
232
|
+
"benchmark",
|
|
233
|
+
"get_profile",
|
|
234
|
+
]
|
|
235
|
+
|
|
236
|
+
def __dir__():
|
|
237
|
+
return __all__
|
|
@@ -21,10 +21,19 @@ def get_asset_nodes_helper(file: str) -> Generator[str, None, None]:
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def get_asset_nodes() -> Generator[str, None, None]:
|
|
24
|
+
"""Retrieve the name of all factory asset node groups.
|
|
25
|
+
"""
|
|
24
26
|
for file in nodes_dir.rglob('*blend'):
|
|
25
27
|
yield from get_asset_nodes_helper(file)
|
|
26
28
|
|
|
27
29
|
|
|
30
|
+
def get_asset_name_dict() -> Generator[tuple[str, str], None, None]:
|
|
31
|
+
"""Retrieve the name and file path of all factory asset node groups.
|
|
32
|
+
"""
|
|
33
|
+
for file in nodes_dir.rglob('*blend'):
|
|
34
|
+
for name in get_asset_nodes_helper(file):
|
|
35
|
+
yield name, file
|
|
36
|
+
|
|
28
37
|
|
|
29
38
|
def get_asset_file(name: str) -> str | None:
|
|
30
39
|
for file in nodes_dir.rglob('*blend'):
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from re import findall
|
|
3
|
+
from numpy import where, char
|
|
4
|
+
from zipfile import ZipFile, is_zipfile, ZIP_LZMA
|
|
5
|
+
from typing import Any, Generator, Callable
|
|
6
|
+
from io import BytesIO, StringIO
|
|
7
|
+
from os import access, F_OK, R_OK, W_OK
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SuspectFileError(Exception):
|
|
11
|
+
def __init__(self, message):
|
|
12
|
+
self.message = message
|
|
13
|
+
super().__init__(self.message)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _is_file_accessible(file_path: str) -> list[bool]:
|
|
17
|
+
return [access(file_path, F_OK), access(file_path, R_OK), access(file_path, W_OK)]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _is_file_accessible_message(data: list[bool]) -> list[str]:
|
|
21
|
+
msg = "File Path {} {}. "
|
|
22
|
+
if not all(data):
|
|
23
|
+
return [msg.format('is not', 'accessible')]
|
|
24
|
+
exists_ = (msg.format('does', 'exist') if data[0] else msg.format('does not', 'exist'))
|
|
25
|
+
readable_ = (msg.format('is', 'readable') if data[1] else msg.format('is not', 'readable'))
|
|
26
|
+
writeable_ = (msg.format('is', 'writeable') if data[2] else msg.format('is not', 'writeable'))
|
|
27
|
+
return [exists_, readable_, writeable_]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def is_file_accessible(file_path: str) -> dict[str, list[bool] | str]:
|
|
31
|
+
"""Retrieve data dict of a file's existance, readability, and writeability.
|
|
32
|
+
|
|
33
|
+
:param file_path: File path to run check on.
|
|
34
|
+
:type file_path: str
|
|
35
|
+
:return: Dictionary of a files accesibility and a message stating the results
|
|
36
|
+
:rtype: dict[str, list[bool] | str]"""
|
|
37
|
+
acc = _is_file_accessible(file_path)
|
|
38
|
+
return {'access': acc, 'message': "".join(_ for _ in _is_file_accessible_message(acc))}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def read_file(file: str) -> Any:
|
|
42
|
+
"""Read data from a file.
|
|
43
|
+
|
|
44
|
+
:param file: Path of a file to read.
|
|
45
|
+
:type file: str
|
|
46
|
+
:return: Data read from the file.
|
|
47
|
+
:rtype: Any"""
|
|
48
|
+
with open(file, 'r') as f:
|
|
49
|
+
data = f.read()
|
|
50
|
+
return data
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def write_file(file: str, data: Any) -> None:
|
|
54
|
+
"""Write data to a file.
|
|
55
|
+
|
|
56
|
+
:param file: Path of a file to write to.
|
|
57
|
+
:type file: str
|
|
58
|
+
:param data: Data to write to file.
|
|
59
|
+
:type data: Any"""
|
|
60
|
+
with open(file, 'w') as f:
|
|
61
|
+
f.write(data)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_dir_files(dir_path: str, filter: str='*py') -> Generator[Path, None, None]:
|
|
65
|
+
"""Retrieve a generator object of the files in a directory the match the filter.
|
|
66
|
+
|
|
67
|
+
:param dir_path: Path of the directory to retrieve files from.
|
|
68
|
+
:type dir_path: str
|
|
69
|
+
:param filter: Filter to use for file retrieval.
|
|
70
|
+
:type filter: str
|
|
71
|
+
:return: Generator of the files in the directory that match the filter.
|
|
72
|
+
:rtype: Generator[Path, None, None]"""
|
|
73
|
+
return Path(dir_path).glob(filter)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def get_dir_file_stems(dir_path: str, filter: str='*py', ignore_files: list[str] | None=None) -> Generator[str, None, None]:
|
|
77
|
+
"""Retrieve a generator object of the file name stripped of the prefix path.
|
|
78
|
+
|
|
79
|
+
:param dir_path: Path of the directory to retrieve files from.
|
|
80
|
+
:type dir_path: str
|
|
81
|
+
:param filter: Filter to use for file retrieval.
|
|
82
|
+
:type filter: str
|
|
83
|
+
:param ignore_files: File names to ignore.
|
|
84
|
+
:type ignore_files: list[str] | None
|
|
85
|
+
:return: Description
|
|
86
|
+
:rtype: Generator[str, None, None]"""
|
|
87
|
+
if not ignore_files:
|
|
88
|
+
ignore_files = ['__init__']
|
|
89
|
+
return (file.stem for file in get_dir_files(dir_path, filter=filter) if file.stem not in ignore_files)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def copy_file_by_chunks(src_file: str, target_file: str) -> None:
|
|
93
|
+
"""Copy a file in byte chunks.
|
|
94
|
+
|
|
95
|
+
:param src_file: Source file path to copy data from.
|
|
96
|
+
:type src_file: str
|
|
97
|
+
:param target_file: Target file path to copy data to.
|
|
98
|
+
:type target_file: str"""
|
|
99
|
+
with open(src_file, 'rb') as src:
|
|
100
|
+
with open(target_file, 'wb') as tgt:
|
|
101
|
+
while True:
|
|
102
|
+
chunk = src.read(1024 * 1024)
|
|
103
|
+
if not chunk:
|
|
104
|
+
break
|
|
105
|
+
tgt.write(chunk)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def read_file_by_chunks(src_file: str) -> Generator[Any, None, None]:
|
|
109
|
+
"""Read a file in byte chunks.
|
|
110
|
+
|
|
111
|
+
:param src_file: Source file path to read data from.
|
|
112
|
+
:type src_file: str"""
|
|
113
|
+
with open(src_file, 'rb') as src:
|
|
114
|
+
while True:
|
|
115
|
+
chunk = src.read(1024 * 1024)
|
|
116
|
+
if not chunk:
|
|
117
|
+
break
|
|
118
|
+
yield chunk
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def write_bytesio(data: bytes) -> BytesIO:
|
|
122
|
+
"""Convert bytes data into a BytesIo object.
|
|
123
|
+
|
|
124
|
+
:param data: Bytes data.
|
|
125
|
+
:type data: bytes
|
|
126
|
+
:return: Bytes data converted to a BytesIO object.
|
|
127
|
+
:rtype: BytesIO"""
|
|
128
|
+
buffer = BytesIO()
|
|
129
|
+
buffer.write(data)
|
|
130
|
+
buffer.seek(0)
|
|
131
|
+
return buffer
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def write_stringio(data: str) -> StringIO:
|
|
135
|
+
"""Convert string data into a StringIO object.
|
|
136
|
+
|
|
137
|
+
:param data: String data.
|
|
138
|
+
:type data: str
|
|
139
|
+
:return: String data converted to a StringIO object.
|
|
140
|
+
:rtype: StringIO"""
|
|
141
|
+
buffer = StringIO()
|
|
142
|
+
buffer.write(data)
|
|
143
|
+
buffer.seek(0)
|
|
144
|
+
return buffer
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def inject_detect(data: str) -> bool:
|
|
148
|
+
"""Check a text string for suspicious characters and function calls.
|
|
149
|
+
|
|
150
|
+
:param data: Large text string usually read from a file.
|
|
151
|
+
:type data: str
|
|
152
|
+
:return: Result of string check.
|
|
153
|
+
:rtype: bool"""
|
|
154
|
+
s = findall(r'\b(?:os|sys|subprocess|asyncio|pathlib|marshal|ast|cmd|ord|chr)\b', data)
|
|
155
|
+
f = findall(r' eval\(| var\(| exec\(| ord\(| chr\(| -c ', data)
|
|
156
|
+
i = findall(r' os\.| sys\.| subprocess\.| asyncio\.| pathlib\.| marshal\.| ast\.| cmd\.', data)
|
|
157
|
+
check_ = [len(s) > 0, len(f) > 0, len(i) > 0]
|
|
158
|
+
if any(check_):
|
|
159
|
+
print(f"[SUSPICIOUS]: {[_ for idx, _ in enumerate([s, f, i]) if check_[idx]]}")
|
|
160
|
+
return True
|
|
161
|
+
return False
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def is_file_suspicious(file: str) -> bool | None:
|
|
165
|
+
"""Check a file for suspicious characters and function calls.
|
|
166
|
+
|
|
167
|
+
:param file: Path of file to check.
|
|
168
|
+
:type file: str
|
|
169
|
+
:return: Return False if checks return False else raise SuspectFileError.
|
|
170
|
+
:rtype: bool | None"""
|
|
171
|
+
data = read_file(file)
|
|
172
|
+
check_ = inject_detect(data)
|
|
173
|
+
if check_:
|
|
174
|
+
raise SuspectFileError(f"Suspect File!!! {Path(file.name)}")
|
|
175
|
+
return check_
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def get_from_zip(zip_file: str, file_name: str, is_gen: bool, func: Callable, *args: Any, **kwargs: Any) -> Generator[Any, None, None] | Any | None:
|
|
179
|
+
"""Retrieve data from a zip archive.
|
|
180
|
+
|
|
181
|
+
:param zip_file: Path of a compressed zip archive.
|
|
182
|
+
:type zip_file: str
|
|
183
|
+
:param file_name: Name of file in zip archive.
|
|
184
|
+
:type file_name: str
|
|
185
|
+
:param is_gen: Return a generator object if True else the result of a function func.
|
|
186
|
+
:type is_gen: bool
|
|
187
|
+
:param func: Function to run on file data.
|
|
188
|
+
:type func: Callable
|
|
189
|
+
:param args: Arguments for function func.
|
|
190
|
+
:type args: Any
|
|
191
|
+
:param kwargs: Keyword arguments for function func.
|
|
192
|
+
:type kwargs: Any
|
|
193
|
+
:return: Generator object if is_gen is True else the result of function func or None if the operations fail.
|
|
194
|
+
:rtype: Generator[Any, None, None] | Any"""
|
|
195
|
+
try:
|
|
196
|
+
with ZipFile(zip_file, 'r') as zf:
|
|
197
|
+
with zf.open(file_name) as hf:
|
|
198
|
+
if is_gen:
|
|
199
|
+
return (_ for _ in list(func(hf, *args, **kwargs)))
|
|
200
|
+
return func(hf, *args, **kwargs)
|
|
201
|
+
except:
|
|
202
|
+
pass
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def get_zip_file_list(zip_file: str) -> Generator[str, None, None]:
|
|
206
|
+
"""Retrieve the name of the files contained in a zip archive.
|
|
207
|
+
|
|
208
|
+
:param zip_file: Path of a compressed zip archive.
|
|
209
|
+
:type zip_file: str
|
|
210
|
+
:return: A generator object of the file names
|
|
211
|
+
:rtype: Generator[str, None, None]"""
|
|
212
|
+
with ZipFile(zip_file, 'r') as zf:
|
|
213
|
+
name_list = (f for f in zf.namelist())
|
|
214
|
+
return name_list
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def zip_from_folder(src_dir_path: str, dest_dir_path: str) -> None:
|
|
218
|
+
"""Copy files from folder into a zip archive.
|
|
219
|
+
|
|
220
|
+
:param src_dir_path: Source path of files to move to zip archive.
|
|
221
|
+
:type src_dir_path: str
|
|
222
|
+
:param dest_dir_path: Path of the zip archive.
|
|
223
|
+
:type dest_dir_path: str"""
|
|
224
|
+
name = Path(src_dir_path).stem
|
|
225
|
+
zip_name = f"{name}.zip"
|
|
226
|
+
zip_file = Path(dest_dir_path).joinpath(zip_name)
|
|
227
|
+
files_list = Path(src_dir_path).rglob("*")
|
|
228
|
+
with ZipFile(file=zip_file, mode='w', compression=ZIP_LZMA, compresslevel=9) as zf:
|
|
229
|
+
for file in files_list:
|
|
230
|
+
zf.write(filename=file, arcname=file.name)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def zip_append(zip_file: str, file: str, arcname: str | None=None) -> None:
|
|
234
|
+
"""Append a file to a zip archive.
|
|
235
|
+
|
|
236
|
+
:param zip_file: Path of a compressed zip archive.
|
|
237
|
+
:type zip_file: str
|
|
238
|
+
:param file: File to append to zip archive.
|
|
239
|
+
:type file: str
|
|
240
|
+
:param arcname: Name to assign to appended file. If None, the file name is used instead.
|
|
241
|
+
:type arcname: str | None"""
|
|
242
|
+
with ZipFile(zip_file, 'a') as zf:
|
|
243
|
+
name = (Path(file).name if isinstance(arcname, type(None)) else arcname)
|
|
244
|
+
zf.write(file, arcname=name)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def read_from_zip(zip_file: str, file_name: str) -> Any:
|
|
248
|
+
"""Read from a file in a zip archive.
|
|
249
|
+
|
|
250
|
+
:param zip_file: Path of a compressed zip archive.
|
|
251
|
+
:type zip_file: str
|
|
252
|
+
:param file_name: Name of file from zip archive to read.
|
|
253
|
+
:type file_name: str
|
|
254
|
+
:return: Data read from the file.
|
|
255
|
+
:rtype: Any"""
|
|
256
|
+
with ZipFile(zip_file, 'r') as zf:
|
|
257
|
+
with zf.open(zf.namelist()[where(char.find(zf.namelist(), file_name) > -1)[0][0]]) as f:
|
|
258
|
+
data = f.read()
|
|
259
|
+
return data
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def create_filler_zip(zip_file: str, file_name: str='USER.txt') -> None:
|
|
263
|
+
"""Create a filler file for a zip archive. Useful for creating an empty zip archive since zip archives can not be empty.
|
|
264
|
+
|
|
265
|
+
:param zip_file: Path of a compressed zip archive.
|
|
266
|
+
:type zip_file: str
|
|
267
|
+
:param file_name: Name for the filler file
|
|
268
|
+
:type file_name: str"""
|
|
269
|
+
with ZipFile(zip_file, 'w', compression=ZIP_LZMA, compresslevel=9, allowZip64=True) as zf:
|
|
270
|
+
zf.writestr(file_name, "")
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def write_zip_file_data(file: str, zip_file: str, arcname: str | None=None) -> None:
|
|
274
|
+
"""Copy file data to a zip archive.
|
|
275
|
+
|
|
276
|
+
:param file: Path of file to copy to zip archive.
|
|
277
|
+
:type file: str
|
|
278
|
+
:param zip_file: Path of a compressed zip archive.
|
|
279
|
+
:type zip_file: str
|
|
280
|
+
:param arcname: Name to save file in zip archive. If None use the file name instead.
|
|
281
|
+
:type arcname: str | None"""
|
|
282
|
+
file = Path(file)
|
|
283
|
+
temp_file = Path(f"temp{file.suffix}")
|
|
284
|
+
temp_file.touch(exist_ok=True)
|
|
285
|
+
copy_file_by_chunks(file, temp_file)
|
|
286
|
+
if not arcname:
|
|
287
|
+
arcname = file.name
|
|
288
|
+
zip_file.write(filename=temp_file, arcname=arcname)
|
|
289
|
+
temp_file.unlink()
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def zip_data_file(file: str, zip_file: str, arcname: str | None=None) -> None:
|
|
293
|
+
"""Write file data to zip archive.
|
|
294
|
+
|
|
295
|
+
:param file: Path of file to copy to zip archive.
|
|
296
|
+
:type file: str
|
|
297
|
+
:param zip_file: Path of a compressed zip archive.
|
|
298
|
+
:type zip_file: str
|
|
299
|
+
:param arcname: Name to save file in zip archive. If None use the file name instead.
|
|
300
|
+
:type arcname: str | None"""
|
|
301
|
+
with ZipFile(file=zip_file, mode='w', compression=ZIP_LZMA, compresslevel=9) as zf:
|
|
302
|
+
write_zip_file_data(file, zf, arcname=arcname)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def zip_data_files(src_dir_path: str, dest_dir_path: str, filter: str="*py") -> None:
|
|
306
|
+
"""Write file data from directory to zip archive.
|
|
307
|
+
|
|
308
|
+
:param src_dir_path: Source directory to copy files from.
|
|
309
|
+
:type src_dir_path: str
|
|
310
|
+
:param dest_dir_path: Directory to write zip archive to.
|
|
311
|
+
:type dest_dir_path: str
|
|
312
|
+
:param filter: Filter for files in source directory.
|
|
313
|
+
:type filter: str"""
|
|
314
|
+
name = Path(src_dir_path).stem
|
|
315
|
+
zip_name = f"{name}.zip"
|
|
316
|
+
zip_file = Path(dest_dir_path).joinpath(zip_name)
|
|
317
|
+
files_list = Path(src_dir_path).rglob(filter)
|
|
318
|
+
with ZipFile(file=zip_file, mode='w', compression=ZIP_LZMA, compresslevel=9) as zf:
|
|
319
|
+
for file in files_list:
|
|
320
|
+
write_zip_file_data(file, zf)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def append_file_to_zip(src_file: str, zip_file: str, arcname: str | None=None) -> None:
|
|
324
|
+
"""Append a file to a zip archive.
|
|
325
|
+
|
|
326
|
+
:param src_file: Path of file to copy to zip archive.
|
|
327
|
+
:type src_file: str
|
|
328
|
+
:param zip_file: Path of a compressed zip archive.
|
|
329
|
+
:type zip_file: str
|
|
330
|
+
:param arcname: Name to save file in zip archive. If None use the file name instead.
|
|
331
|
+
:type arcname: str | None"""
|
|
332
|
+
if not is_zipfile(zip_file):
|
|
333
|
+
raise ValueError("Destination is not a zip archive!")
|
|
334
|
+
src_file = Path(src_file)
|
|
335
|
+
file_names = set(Path(file).name for file in get_zip_file_list(zip_file))
|
|
336
|
+
if arcname:
|
|
337
|
+
if arcname in file_names:
|
|
338
|
+
raise FileExistsError("File already exists!")
|
|
339
|
+
else:
|
|
340
|
+
if str(src_file.stem) in file_names:
|
|
341
|
+
raise FileExistsError("File already exists!")
|
|
342
|
+
with ZipFile(file=zip_file, mode='a', compression=ZIP_LZMA, compresslevel=9) as zf:
|
|
343
|
+
write_zip_file_data(src_file, zf, arcname=arcname)
|
|
344
|
+
|