pybiolib 1.2.1180__py3-none-any.whl → 1.2.1228__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,4 +1,4 @@
1
- import copy
1
+ import json
2
2
  import os
3
3
  import re
4
4
  import sys
@@ -239,15 +239,24 @@ def push_application(
239
239
  app_data_path: Optional[Path] = None
240
240
  try:
241
241
  with open(config_yml_path) as config_yml_file:
242
- config = copy.deepcopy(yaml.safe_load(config_yml_file.read()))
243
-
244
- app_data = config.get('app_data') or config.get('assets')
245
- if app_data:
246
- if config.get('app_data') and config.get('assets'):
242
+ try:
243
+ config = json.loads(json.dumps(yaml.safe_load(config_yml_file.read())))
244
+ except (TypeError, ValueError) as e:
247
245
  raise BioLibError(
248
- 'In .biolib/config.yml you cannot specify both "app_data" and "assets" fields. Please use only one.'
249
- )
246
+ f'The .biolib/config.yml file contains data types that are not supported '
247
+ f'(must be JSON-serializable). Please ensure only standard JSON types '
248
+ f'(str, int, float, bool, list, dict, null) are used. Original error: {e}'
249
+ ) from e
250
+
251
+ if 'assets' in config and 'app_data' not in config:
252
+ config['app_data'] = config.pop('assets')
253
+ elif 'assets' in config and 'app_data' in config:
254
+ raise BioLibError(
255
+ 'In .biolib/config.yml you cannot specify both "app_data" and "assets" fields. Please use only one.'
256
+ )
250
257
 
258
+ app_data = config.get('app_data')
259
+ if app_data:
251
260
  field_name = 'app_data' if 'app_data' in config else 'assets'
252
261
  if not isinstance(app_data, str):
253
262
  raise BioLibError(
@@ -1,5 +1,6 @@
1
1
  import time
2
2
  from collections import OrderedDict
3
+ from pathlib import Path
3
4
 
4
5
  from biolib import api
5
6
  from biolib._internal.types.experiment import DeprecatedExperimentDict, ExperimentDict
@@ -8,6 +9,7 @@ from biolib._internal.utils import open_browser_window_from_notebook
8
9
  from biolib.biolib_api_client import BiolibApiClient
9
10
  from biolib.biolib_errors import BioLibError
10
11
  from biolib.jobs.job import Job
12
+ from biolib.jobs.job_result import PathFilter
11
13
  from biolib.jobs.types import JobsPaginatedResponse
12
14
  from biolib.tables import BioLibTable
13
15
  from biolib.typing_utils import Dict, List, Optional, Union
@@ -223,6 +225,80 @@ class Experiment:
223
225
  """
224
226
  return self.get_jobs(status=status)
225
227
 
228
+ def save_completed_results(
229
+ self,
230
+ output_dir: Optional[str] = None,
231
+ path_filter: Optional[PathFilter] = None,
232
+ skip_file_if_exists: bool = False,
233
+ overwrite: bool = False,
234
+ ) -> None:
235
+ r"""Save all completed results in this experiment to local folders.
236
+
237
+ Creates a folder structure with the experiment name as the root directory,
238
+ containing a subfolder for each completed result. Only results with
239
+ 'completed' status will be saved.
240
+
241
+ Args:
242
+ output_dir (str, optional): Base directory where the experiment folder
243
+ will be created. If None, uses the current working directory.
244
+ path_filter (PathFilter, optional): Filter to select which files in the results to save.
245
+ Can be a glob pattern string or a callable function.
246
+ skip_file_if_exists (bool, optional): Whether to skip files that already exist
247
+ locally instead of raising an error. Defaults to False.
248
+ overwrite (bool, optional): Whether to overwrite existing files.
249
+ Defaults to False.
250
+
251
+ Example::
252
+
253
+ >>> # Save all completed results to current directory
254
+ >>> experiment.save_completed_results()
255
+ >>> # This creates: ./experiment_name/result_1/, ./experiment_name/result_2/, etc.
256
+
257
+ >>> # Save to specific directory
258
+ >>> experiment.save_completed_results(output_dir="/path/to/save")
259
+ >>> # This creates: /path/to/save/experiment_name/result_1/, etc.
260
+ """
261
+ base_dir = Path(output_dir) if output_dir else Path.cwd()
262
+
263
+ if base_dir == Path('/'):
264
+ raise BioLibError("Cannot save experiment results to root directory '/'")
265
+
266
+ experiment_folder = base_dir / self.name
267
+ experiment_folder.mkdir(parents=True, exist_ok=True)
268
+
269
+ completed_results: List[Job] = []
270
+ failed_results = False
271
+ print('Getting experiment status...')
272
+ for result in self.get_results():
273
+ if result.get_status() == 'completed':
274
+ completed_results.append(result)
275
+ elif result.get_status() != 'in_progress':
276
+ failed_results = True
277
+
278
+ if failed_results:
279
+ print(
280
+ 'WARNING: Found failed or cancelled results in the experiment. '
281
+ 'Please verify you have all your results, and consider removing the failed ones.'
282
+ )
283
+ if not completed_results:
284
+ print(f"No completed results found in experiment '{self.name}'")
285
+ return
286
+
287
+ print(f"Saving {len(completed_results)} completed results from experiment '{self.name}' to {experiment_folder}")
288
+
289
+ for result in completed_results:
290
+ result_name = result.get_name()
291
+ result_folder = experiment_folder / result_name
292
+
293
+ result_folder.mkdir(parents=True, exist_ok=True)
294
+
295
+ result.save_files(
296
+ output_dir=str(result_folder),
297
+ path_filter=path_filter,
298
+ skip_file_if_exists=skip_file_if_exists,
299
+ overwrite=overwrite,
300
+ )
301
+
226
302
  def rename(self, destination: str) -> None:
227
303
  api.client.patch(f'/resources/{self.uuid}/', data={'uri': destination})
228
304
  self._refetch()
biolib/jobs/job.py CHANGED
@@ -256,7 +256,7 @@ class Result:
256
256
  self,
257
257
  output_dir: str,
258
258
  path_filter: Optional[PathFilter] = None,
259
- skip_file_if_exists: Optional[bool] = None,
259
+ skip_file_if_exists: bool = False,
260
260
  overwrite: bool = False,
261
261
  ) -> None:
262
262
  self.result.save_files(
biolib/jobs/job_result.py CHANGED
@@ -38,7 +38,7 @@ class JobResult:
38
38
  self,
39
39
  output_dir: str,
40
40
  path_filter: Optional[PathFilter] = None,
41
- skip_file_if_exists: Optional[bool] = None,
41
+ skip_file_if_exists: bool = False,
42
42
  overwrite: bool = False,
43
43
  ) -> None:
44
44
  module_output = self._get_module_output()
@@ -1,8 +1,9 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: pybiolib
3
- Version: 1.2.1180
3
+ Version: 1.2.1228
4
4
  Summary: BioLib Python Client
5
5
  License: MIT
6
+ License-File: LICENSE
6
7
  Keywords: biolib
7
8
  Author: biolib
8
9
  Author-email: hello@biolib.com
@@ -17,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.10
17
18
  Classifier: Programming Language :: Python :: 3.11
18
19
  Classifier: Programming Language :: Python :: 3.12
19
20
  Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
20
22
  Provides-Extra: compute-node
21
23
  Requires-Dist: appdirs (>=1.4.3)
22
24
  Requires-Dist: click (>=8.0.0)
@@ -16,7 +16,7 @@ biolib/_internal/lfs/__init__.py,sha256=gSWo_xg61UniYgD7yNYxeT4I9uaXBCBSi3_nmZjn
16
16
  biolib/_internal/lfs/cache.py,sha256=pQS2np21rdJ6I3DpoOutnzPHpLOZgUIS8TMltUJk_k4,2226
17
17
  biolib/_internal/libs/__init__.py,sha256=Jdf4tNPqe_oIIf6zYml6TiqhL_02Vyqwge6IELrAFhw,98
18
18
  biolib/_internal/libs/fusepy/__init__.py,sha256=AWDzNFS-XV_5yKb0Qx7kggIhPzq1nj_BZS5y2Nso08k,41944
19
- biolib/_internal/push_application.py,sha256=rP835wYU6PpF8w149G7uNiKs-OauHbGbjQryGucmIt8,18684
19
+ biolib/_internal/push_application.py,sha256=Ljv7cFeaWRlMdHwn93lhooD227_ZvG8oIRTWHdDdB2M,19177
20
20
  biolib/_internal/runtime.py,sha256=BiHl4klUHr36MCpqKaUso4idHeBZfPAahLYRQrabFqA,486
21
21
  biolib/_internal/string_utils.py,sha256=N7J7oGu6_yA_z0pOiKqxEh__lRdiDLh6kigeDkQEZ5g,265
22
22
  biolib/_internal/templates/__init__.py,sha256=NVbhLUMC8HITzkLvP88Qu7FHaL-SvQord-DX3gh1Ykk,24
@@ -139,10 +139,10 @@ biolib/compute_node/webserver/webserver_types.py,sha256=2t8EaFKESnves3BA_NBdnS2y
139
139
  biolib/compute_node/webserver/webserver_utils.py,sha256=XWvwYPbWNR3qS0FYbLLp-MDDfVk0QdaAmg3xPrT0H2s,4234
140
140
  biolib/compute_node/webserver/worker_thread.py,sha256=7uD9yQPhePYvP2HCJ27EeZ_h6psfIWFgqm1RHZxzobs,12483
141
141
  biolib/experiments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
142
- biolib/experiments/experiment.py,sha256=gY1-d7FgVrcQO3YzOJ0mHS6j7z7rl5XzXEuk7dFi1gU,10460
142
+ biolib/experiments/experiment.py,sha256=4g1xMYmfp5yzSOdwjf3pUUULF9QjqBJb4uQ25FHrFrk,13688
143
143
  biolib/jobs/__init__.py,sha256=aIb2H2DHjQbM2Bs-dysFijhwFcL58Blp0Co0gimED3w,32
144
- biolib/jobs/job.py,sha256=B9v-dq9li0v9gDfmyyZOnCk-kN0vYPYxoybHWQsCkPg,27403
145
- biolib/jobs/job_result.py,sha256=L__GA5dNSDFHoQpAG2xb-tNqp_p4fQl53bAmlcvAt6M,6074
144
+ biolib/jobs/job.py,sha256=AsWSnhDHNMz5_7pSsAfUYZx-34zahTDXLzhstSvLFiI,27394
145
+ biolib/jobs/job_result.py,sha256=3uE_oj567-yGLr4UEFAf9CsM5lcEcjK8XE8v-CJKz8Q,6065
146
146
  biolib/jobs/types.py,sha256=ezvaoTANsWazK6PmfpYcqezdfjP7MNBEBfqIZGoZhz8,997
147
147
  biolib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
148
148
  biolib/runtime/__init__.py,sha256=MlRepA11n2H-3plB5rzWyyHK2JmP6PiaP3i6x3vt0mg,506
@@ -157,8 +157,8 @@ biolib/utils/cache_state.py,sha256=u256F37QSRIVwqKlbnCyzAX4EMI-kl6Dwu6qwj-Qmag,3
157
157
  biolib/utils/multipart_uploader.py,sha256=XvGP1I8tQuKhAH-QugPRoEsCi9qvbRk-DVBs5PNwwJo,8452
158
158
  biolib/utils/seq_util.py,sha256=rImaghQGuIqTVWks6b9P2yKuN34uePUYPUFW_Wyoa4A,6737
159
159
  biolib/utils/zip/remote_zip.py,sha256=0wErYlxir5921agfFeV1xVjf29l9VNgGQvNlWOlj2Yc,23232
160
- pybiolib-1.2.1180.dist-info/LICENSE,sha256=F2h7gf8i0agDIeWoBPXDMYScvQOz02pAWkKhTGOHaaw,1067
161
- pybiolib-1.2.1180.dist-info/METADATA,sha256=uvyCdpVr94yTPMbWsufxJaRXVosq8oC_uF1pg4VyLTI,1571
162
- pybiolib-1.2.1180.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
163
- pybiolib-1.2.1180.dist-info/entry_points.txt,sha256=p6DyaP_2kctxegTX23WBznnrDi4mz6gx04O5uKtRDXg,42
164
- pybiolib-1.2.1180.dist-info/RECORD,,
160
+ pybiolib-1.2.1228.dist-info/METADATA,sha256=jD07Dls__RR6S-_Svx_bR-Ip4rVpbPtpLo2_lpIoycU,1644
161
+ pybiolib-1.2.1228.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
162
+ pybiolib-1.2.1228.dist-info/entry_points.txt,sha256=p6DyaP_2kctxegTX23WBznnrDi4mz6gx04O5uKtRDXg,42
163
+ pybiolib-1.2.1228.dist-info/licenses/LICENSE,sha256=F2h7gf8i0agDIeWoBPXDMYScvQOz02pAWkKhTGOHaaw,1067
164
+ pybiolib-1.2.1228.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.3
2
+ Generator: poetry-core 2.2.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any