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.
@@ -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
- files.append(arg)
175
- args[idx] = Path(arg).name
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
- files.append(arg.split('=')[-1])
180
- args[idx] = arg.split('=')[0] + '=' + Path(arg.split('=')[-1]).name
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
- destination_file_path.rename(f'{destination_file_path}.biolib-renamed.{time.strftime("%Y%m%d%H%M%S")}')
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")}'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pybiolib
3
- Version: 1.2.923
3
+ Version: 1.2.939
4
4
  Summary: BioLib Python Client
5
5
  License: MIT
6
6
  Keywords: biolib
@@ -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=4jT6j7bB21c0JNn5BfnyWQib_zt0CVtJ_TiOFOStRcE,2604
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=7AD0S1ih_drWbWXFKixGXyOKwQurt3ecND4m0Vb-Aik,10554
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=MCo0ZqW2pHBxOoCI3i5gAx5D0auW9fmxHqkAF4TRhms,2134
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=INQuNpEpe8rQPWBWeDFRjcAd-i4U6hF63FAsJ0BkJ_Y,27086
133
- biolib/jobs/job_result.py,sha256=rALHiKYNaC9lHi_JJqBob1RubzNLwG9Z386kwRJjd2M,5885
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.923.dist-info/LICENSE,sha256=F2h7gf8i0agDIeWoBPXDMYScvQOz02pAWkKhTGOHaaw,1067
149
- pybiolib-1.2.923.dist-info/METADATA,sha256=_6O5_T5lbKG9bEy2PBkthT5GWj6KyGyCGJMd337P_eI,1570
150
- pybiolib-1.2.923.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
151
- pybiolib-1.2.923.dist-info/entry_points.txt,sha256=p6DyaP_2kctxegTX23WBznnrDi4mz6gx04O5uKtRDXg,42
152
- pybiolib-1.2.923.dist-info/RECORD,,
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,,