pybiolib 1.2.923__py3-none-any.whl → 1.2.939__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.
- biolib/_internal/file_utils.py +45 -0
- biolib/app/app.py +49 -31
- biolib/cli/run.py +1 -1
- biolib/jobs/job.py +9 -2
- biolib/jobs/job_result.py +3 -0
- {pybiolib-1.2.923.dist-info → pybiolib-1.2.939.dist-info}/METADATA +1 -1
- {pybiolib-1.2.923.dist-info → pybiolib-1.2.939.dist-info}/RECORD +10 -10
- {pybiolib-1.2.923.dist-info → pybiolib-1.2.939.dist-info}/LICENSE +0 -0
- {pybiolib-1.2.923.dist-info → pybiolib-1.2.939.dist-info}/WHEEL +0 -0
- {pybiolib-1.2.923.dist-info → pybiolib-1.2.939.dist-info}/entry_points.txt +0 -0
biolib/_internal/file_utils.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import hashlib
|
1
2
|
import io
|
2
3
|
import os
|
3
4
|
import zipfile as zf
|
@@ -75,3 +76,47 @@ def get_iterable_zip_stream(files: List[str], chunk_size: int) -> Iterator[bytes
|
|
75
76
|
if len(chunk) == 0:
|
76
77
|
break
|
77
78
|
yield chunk
|
79
|
+
|
80
|
+
|
81
|
+
def path_to_renamed_path(path_str: str, prefix_with_slash: bool = True) -> str:
|
82
|
+
"""
|
83
|
+
Normalize file paths consistently:
|
84
|
+
- If path contains '..' (relative path going up), convert to absolute path
|
85
|
+
- If relative path not containing '..', keep as is, but prepend / if prefix_with_slash=True
|
86
|
+
- If absolute path that is subpath of current directory, convert to relative path
|
87
|
+
- If absolute path not subpath of current directory, hash the folder path and keep filename
|
88
|
+
"""
|
89
|
+
path = Path(path_str)
|
90
|
+
current_dir = Path.cwd()
|
91
|
+
|
92
|
+
if '..' in path.parts:
|
93
|
+
resolved_path = path.resolve()
|
94
|
+
try:
|
95
|
+
relative_path = resolved_path.relative_to(current_dir)
|
96
|
+
result = str(relative_path)
|
97
|
+
except ValueError:
|
98
|
+
folder_path = str(resolved_path.parent)
|
99
|
+
filename = resolved_path.name
|
100
|
+
folder_hash = hashlib.md5(folder_path.encode()).hexdigest()[:6]
|
101
|
+
result = f'/{folder_hash}/{filename}'
|
102
|
+
elif path.is_absolute():
|
103
|
+
try:
|
104
|
+
resolved_path = path.resolve()
|
105
|
+
relative_path = resolved_path.relative_to(current_dir)
|
106
|
+
result = str(relative_path)
|
107
|
+
except ValueError:
|
108
|
+
folder_path = str(path.parent)
|
109
|
+
filename = path.name
|
110
|
+
folder_hash = hashlib.md5(folder_path.encode()).hexdigest()[:6]
|
111
|
+
result = f'/{folder_hash}/{filename}'
|
112
|
+
else:
|
113
|
+
result = path_str
|
114
|
+
|
115
|
+
if prefix_with_slash:
|
116
|
+
if not result.startswith('/'):
|
117
|
+
return '/' + result
|
118
|
+
return result
|
119
|
+
else:
|
120
|
+
if result.startswith('/'):
|
121
|
+
return result[1:]
|
122
|
+
return result
|
biolib/app/app.py
CHANGED
@@ -20,6 +20,7 @@ from biolib.jobs.job import Result
|
|
20
20
|
from biolib.typing_utils import Dict, Optional
|
21
21
|
from biolib.utils.app_uri import parse_app_uri
|
22
22
|
from biolib._runtime.runtime import Runtime
|
23
|
+
from biolib._internal.file_utils import path_to_renamed_path
|
23
24
|
|
24
25
|
|
25
26
|
class BioLibApp:
|
@@ -168,16 +169,60 @@ Example: "app.cli('--help')"
|
|
168
169
|
files = []
|
169
170
|
|
170
171
|
files_dict = {}
|
172
|
+
if isinstance(files, list):
|
173
|
+
for file_path in files:
|
174
|
+
path = Path(file_path)
|
175
|
+
if path.is_dir():
|
176
|
+
for filename in path.rglob('*'):
|
177
|
+
if filename.is_dir():
|
178
|
+
continue
|
179
|
+
with open(filename, 'rb') as f:
|
180
|
+
relative_path = filename.relative_to(Path.cwd())
|
181
|
+
files_dict[path_to_renamed_path(str(relative_path))] = f.read()
|
182
|
+
else:
|
183
|
+
with open(path, 'rb') as f:
|
184
|
+
files_dict[path_to_renamed_path(str(path))] = f.read()
|
185
|
+
elif isinstance(files, dict):
|
186
|
+
files_dict = {}
|
187
|
+
for key, value in files.items():
|
188
|
+
if not key.startswith('/'):
|
189
|
+
key = '/' + key
|
190
|
+
files_dict[key] = value
|
191
|
+
else:
|
192
|
+
raise Exception('The given files input must be list or dict or None')
|
193
|
+
|
171
194
|
for idx, arg in enumerate(args):
|
172
195
|
if isinstance(arg, str):
|
173
196
|
if os.path.isfile(arg) or os.path.isdir(arg):
|
174
|
-
|
175
|
-
|
197
|
+
if os.path.isfile(arg):
|
198
|
+
with open(arg, 'rb') as f:
|
199
|
+
files_dict[path_to_renamed_path(arg)] = f.read()
|
200
|
+
elif os.path.isdir(arg):
|
201
|
+
path = Path(arg)
|
202
|
+
for filename in path.rglob('*'):
|
203
|
+
if filename.is_dir():
|
204
|
+
continue
|
205
|
+
with open(filename, 'rb') as f:
|
206
|
+
files_dict[path_to_renamed_path(str(filename))] = f.read()
|
207
|
+
args[idx] = path_to_renamed_path(arg, prefix_with_slash=False)
|
176
208
|
|
177
209
|
# support --myarg=file.txt
|
178
210
|
elif os.path.isfile(arg.split('=')[-1]) or os.path.isdir(arg.split('=')[-1]):
|
179
|
-
|
180
|
-
|
211
|
+
file_path = arg.split('=')[-1]
|
212
|
+
if os.path.isfile(file_path):
|
213
|
+
with open(file_path, 'rb') as f:
|
214
|
+
files_dict[path_to_renamed_path(file_path)] = f.read()
|
215
|
+
elif os.path.isdir(file_path):
|
216
|
+
path = Path(file_path)
|
217
|
+
for filename in path.rglob('*'):
|
218
|
+
if filename.is_dir():
|
219
|
+
continue
|
220
|
+
with open(filename, 'rb') as f:
|
221
|
+
files_dict[path_to_renamed_path(str(filename))] = f.read()
|
222
|
+
args[idx] = (
|
223
|
+
arg.split('=')[0] + '=' +
|
224
|
+
path_to_renamed_path(file_path, prefix_with_slash=False)
|
225
|
+
)
|
181
226
|
else:
|
182
227
|
pass # a normal string arg was given
|
183
228
|
else:
|
@@ -191,33 +236,6 @@ Example: "app.cli('--help')"
|
|
191
236
|
files_dict[f'/{tmp_filename}'] = file_data
|
192
237
|
args[idx] = tmp_filename
|
193
238
|
|
194
|
-
if isinstance(files, list):
|
195
|
-
for file in files:
|
196
|
-
path = Path(file).absolute()
|
197
|
-
|
198
|
-
# Recursively add data from files if dir
|
199
|
-
if path.is_dir():
|
200
|
-
for filename in path.rglob('*'):
|
201
|
-
if filename.is_dir():
|
202
|
-
continue
|
203
|
-
file = open(filename, 'rb')
|
204
|
-
relative_path = '/' + path.name + '/' + '/'.join(filename.relative_to(path).parts)
|
205
|
-
files_dict[relative_path] = file.read()
|
206
|
-
file.close()
|
207
|
-
|
208
|
-
# Add file data
|
209
|
-
else:
|
210
|
-
file = open(path, 'rb')
|
211
|
-
path_short = '/' + path.name
|
212
|
-
|
213
|
-
files_dict[path_short] = file.read()
|
214
|
-
file.close()
|
215
|
-
|
216
|
-
elif isinstance(files, dict):
|
217
|
-
files_dict.update(files)
|
218
|
-
else:
|
219
|
-
raise Exception('The given files input must be list or dict or None')
|
220
|
-
|
221
239
|
module_input_serialized: bytes = ModuleInput().serialize(
|
222
240
|
stdin=stdin,
|
223
241
|
arguments=args,
|
biolib/cli/run.py
CHANGED
@@ -48,7 +48,7 @@ def _run(local: bool, non_blocking: bool, uri: str, args: Tuple[str]) -> None:
|
|
48
48
|
)
|
49
49
|
|
50
50
|
if blocking:
|
51
|
-
job.save_files('biolib_results')
|
51
|
+
job.save_files('biolib_results', overwrite=True)
|
52
52
|
|
53
53
|
# Write stdout and stderr if it has not been streamed (Markdown is not streamed)
|
54
54
|
if app.version.get('stdout_render_type') == 'markdown' or not sys.stdout.isatty():
|
biolib/jobs/job.py
CHANGED
@@ -226,7 +226,7 @@ class Result:
|
|
226
226
|
|
227
227
|
return self._cached_input_arguments
|
228
228
|
|
229
|
-
def save_input_files(self, output_dir: str) -> None:
|
229
|
+
def save_input_files(self, output_dir: str, overwrite: bool = False) -> None:
|
230
230
|
logger.info('Downloading input files...')
|
231
231
|
module_input = self._get_module_input()
|
232
232
|
|
@@ -236,7 +236,12 @@ class Result:
|
|
236
236
|
# Remove leading slash of file_path
|
237
237
|
destination_file_path = Path(output_dir) / Path(path.lstrip('/'))
|
238
238
|
if destination_file_path.exists():
|
239
|
-
|
239
|
+
if not overwrite:
|
240
|
+
raise BioLibError(f'File {destination_file_path} already exists. Set overwrite=True to overwrite.')
|
241
|
+
else:
|
242
|
+
destination_file_path.rename(
|
243
|
+
f'{destination_file_path}.biolib-renamed.{time.strftime("%Y%m%d%H%M%S")}'
|
244
|
+
)
|
240
245
|
|
241
246
|
dir_path = destination_file_path.parent
|
242
247
|
if dir_path:
|
@@ -252,11 +257,13 @@ class Result:
|
|
252
257
|
output_dir: str,
|
253
258
|
path_filter: Optional[PathFilter] = None,
|
254
259
|
skip_file_if_exists: Optional[bool] = None,
|
260
|
+
overwrite: bool = False,
|
255
261
|
) -> None:
|
256
262
|
self.result.save_files(
|
257
263
|
output_dir=output_dir,
|
258
264
|
path_filter=path_filter,
|
259
265
|
skip_file_if_exists=skip_file_if_exists,
|
266
|
+
overwrite=overwrite,
|
260
267
|
)
|
261
268
|
|
262
269
|
def get_status(self) -> str:
|
biolib/jobs/job_result.py
CHANGED
@@ -39,6 +39,7 @@ class JobResult:
|
|
39
39
|
output_dir: str,
|
40
40
|
path_filter: Optional[PathFilter] = None,
|
41
41
|
skip_file_if_exists: Optional[bool] = None,
|
42
|
+
overwrite: bool = False,
|
42
43
|
) -> None:
|
43
44
|
module_output = self._get_module_output()
|
44
45
|
output_files = module_output.get_files()
|
@@ -68,6 +69,8 @@ class JobResult:
|
|
68
69
|
if skip_file_if_exists:
|
69
70
|
print(f'Skipping {destination_file_path} as a file with that name already exists locally.')
|
70
71
|
continue
|
72
|
+
elif not overwrite:
|
73
|
+
raise BioLibError(f'File {destination_file_path} already exists. Set overwrite=True to overwrite.')
|
71
74
|
else:
|
72
75
|
destination_file_path.rename(
|
73
76
|
f'{destination_file_path}.biolib-renamed.{time.strftime("%Y%m%d%H%M%S")}'
|
@@ -7,7 +7,7 @@ biolib/_internal/data_record/data_record.py,sha256=SD3-tKQY2RZv9ZSVNUhd2ISDYV64F
|
|
7
7
|
biolib/_internal/data_record/push_data.py,sha256=-L3a_7zZzDCXabBu3O4lWPMAMeBbeRPTrBlEM-_5SCI,2693
|
8
8
|
biolib/_internal/data_record/remote_storage_endpoint.py,sha256=eCptuZ4DMAPnaNCVDvpWXwXGI6Jac9U1N5dqU8Cj95Q,1732
|
9
9
|
biolib/_internal/errors.py,sha256=AokI6Dvaa22B__LTfSZUVdDsm0ye7lgBFv_aD9O_kbs,163
|
10
|
-
biolib/_internal/file_utils.py,sha256=
|
10
|
+
biolib/_internal/file_utils.py,sha256=u_fD4aAWpLtvPlvcnz_iP4cRsFBjfnE6MWJUFPbTQHQ,4276
|
11
11
|
biolib/_internal/fuse_mount/__init__.py,sha256=B_tM6RM2dBw-vbpoHJC4X3tOAaN1H2RDvqYJOw3xFwg,55
|
12
12
|
biolib/_internal/fuse_mount/experiment_fuse_mount.py,sha256=08aUdEq_bvqLBft_gSLjOClKDy5sBnMts1RfJf7AP_U,7012
|
13
13
|
biolib/_internal/http_client.py,sha256=9-3HG6LnLwJfrwjCeFj5XigHVH8Dzz6Dpr58IjQJHDo,6221
|
@@ -56,7 +56,7 @@ biolib/_session/session.py,sha256=US1Y1jfFIAm86-Lq3C7nCXpZXUJXXBVBkND9djMNYxI,16
|
|
56
56
|
biolib/api/__init__.py,sha256=mQ4u8FijqyLzjYMezMUUbbBGNB3iFmkNdjXnWPZ7Jlw,138
|
57
57
|
biolib/api/client.py,sha256=2GpKE7QrPgyPdgJgrV7XnZByIJf1n26UCy3aoaHBs1M,7881
|
58
58
|
biolib/app/__init__.py,sha256=cdPtcfb_U-bxb9iSL4fCEq2rpD9OjkyY4W-Zw60B0LI,37
|
59
|
-
biolib/app/app.py,sha256=
|
59
|
+
biolib/app/app.py,sha256=E4PuwMWiWujQQzHQcq7MfVEZV-q6uKAbwBLr0ZPY5Dk,11825
|
60
60
|
biolib/app/search_apps.py,sha256=K4a41f5XIWth2BWI7OffASgIsD0ko8elCax8YL2igaY,1470
|
61
61
|
biolib/biolib_api_client/__init__.py,sha256=E5EMa19wJoblwSdQPYrxc_BtIeRsAuO0L_jQweWw-Yk,182
|
62
62
|
biolib/biolib_api_client/api_client.py,sha256=IONzXeFCHl4wuct6fqOC_7NiTv_zFy6ys0hsAtvLzTA,7578
|
@@ -91,7 +91,7 @@ biolib/cli/download_container.py,sha256=HIZVHOPmslGE5M2Dsp9r2cCkAEJx__vcsDz5Wt5L
|
|
91
91
|
biolib/cli/init.py,sha256=OMYIltLJfF8q9VAvzDwNG0-1VixDkFGEN1ic2VhrW7c,4583
|
92
92
|
biolib/cli/lfs.py,sha256=z2qHUwink85mv9yDgifbVKkVwuyknGhMDTfly_gLKJM,4151
|
93
93
|
biolib/cli/push.py,sha256=J8BswMYVeTacZBHbm4K4a2XbS_I8kvfgRZLoby2wi3I,1695
|
94
|
-
biolib/cli/run.py,sha256=
|
94
|
+
biolib/cli/run.py,sha256=RAAXbIx8Bi-4fNkEoz2ODJ0fEtyS7VxD3dkc2fVZwjY,2150
|
95
95
|
biolib/cli/runtime.py,sha256=Xv-nrma5xX8NidWcvbUKcUvuN5TCarZa4A8mPVmF-z0,361
|
96
96
|
biolib/cli/sdk.py,sha256=XisJwTft4QDB_99eGDlz-WBqqRwIqkVg7HyvxWtOG8w,471
|
97
97
|
biolib/cli/start.py,sha256=rg8VVY8rboFhf1iQo3zE3WA5oh_R1VWWfYJEO1gMReY,1737
|
@@ -129,8 +129,8 @@ biolib/compute_node/webserver/worker_thread.py,sha256=7uD9yQPhePYvP2HCJ27EeZ_h6p
|
|
129
129
|
biolib/experiments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
130
130
|
biolib/experiments/experiment.py,sha256=gY1-d7FgVrcQO3YzOJ0mHS6j7z7rl5XzXEuk7dFi1gU,10460
|
131
131
|
biolib/jobs/__init__.py,sha256=aIb2H2DHjQbM2Bs-dysFijhwFcL58Blp0Co0gimED3w,32
|
132
|
-
biolib/jobs/job.py,sha256=
|
133
|
-
biolib/jobs/job_result.py,sha256=
|
132
|
+
biolib/jobs/job.py,sha256=B9v-dq9li0v9gDfmyyZOnCk-kN0vYPYxoybHWQsCkPg,27403
|
133
|
+
biolib/jobs/job_result.py,sha256=L__GA5dNSDFHoQpAG2xb-tNqp_p4fQl53bAmlcvAt6M,6074
|
134
134
|
biolib/jobs/types.py,sha256=ezvaoTANsWazK6PmfpYcqezdfjP7MNBEBfqIZGoZhz8,997
|
135
135
|
biolib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
136
136
|
biolib/runtime/__init__.py,sha256=MlRepA11n2H-3plB5rzWyyHK2JmP6PiaP3i6x3vt0mg,506
|
@@ -145,8 +145,8 @@ biolib/utils/cache_state.py,sha256=u256F37QSRIVwqKlbnCyzAX4EMI-kl6Dwu6qwj-Qmag,3
|
|
145
145
|
biolib/utils/multipart_uploader.py,sha256=XvGP1I8tQuKhAH-QugPRoEsCi9qvbRk-DVBs5PNwwJo,8452
|
146
146
|
biolib/utils/seq_util.py,sha256=Ozk0blGtPur_D9MwShD02r_mphyQmgZkx-lOHOwnlIM,6730
|
147
147
|
biolib/utils/zip/remote_zip.py,sha256=0wErYlxir5921agfFeV1xVjf29l9VNgGQvNlWOlj2Yc,23232
|
148
|
-
pybiolib-1.2.
|
149
|
-
pybiolib-1.2.
|
150
|
-
pybiolib-1.2.
|
151
|
-
pybiolib-1.2.
|
152
|
-
pybiolib-1.2.
|
148
|
+
pybiolib-1.2.939.dist-info/LICENSE,sha256=F2h7gf8i0agDIeWoBPXDMYScvQOz02pAWkKhTGOHaaw,1067
|
149
|
+
pybiolib-1.2.939.dist-info/METADATA,sha256=kPWSZXEhVmT-z_Wq2qmHvnhfksdAhH4eu8vOwxah3lU,1570
|
150
|
+
pybiolib-1.2.939.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
151
|
+
pybiolib-1.2.939.dist-info/entry_points.txt,sha256=p6DyaP_2kctxegTX23WBznnrDi4mz6gx04O5uKtRDXg,42
|
152
|
+
pybiolib-1.2.939.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|