pybiolib 1.2.876__py3-none-any.whl → 1.2.883__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.
@@ -0,0 +1,5 @@
1
+ from biolib.biolib_errors import BioLibError
2
+
3
+
4
+ class AuthenticationError(BioLibError):
5
+ """Raised when authentication is required but user is not signed in."""
@@ -1,5 +1,7 @@
1
1
  import os
2
2
  import re
3
+ import sys
4
+ import time
3
5
  from pathlib import Path
4
6
 
5
7
  import rich.progress
@@ -10,6 +12,7 @@ from biolib._internal.data_record.push_data import (
10
12
  push_data_path,
11
13
  validate_data_path_and_get_files_and_size_of_directory,
12
14
  )
15
+ from biolib._internal.errors import AuthenticationError
13
16
  from biolib._internal.file_utils import get_files_and_size_of_directory, get_iterable_zip_stream
14
17
  from biolib._internal.types.push import PushResponseDict
15
18
  from biolib.biolib_api_client import BiolibApiClient
@@ -36,6 +39,13 @@ class DockerStatusUpdate(TypedDict, total=False):
36
39
 
37
40
 
38
41
  def process_docker_status_updates(status_updates: Iterable[DockerStatusUpdate], action: str) -> None:
42
+ if sys.stdout.isatty():
43
+ _process_docker_status_updates_with_progress_bar(status_updates, action)
44
+ else:
45
+ _process_docker_status_updates_with_logging(status_updates, action)
46
+
47
+
48
+ def _process_docker_status_updates_with_progress_bar(status_updates: Iterable[DockerStatusUpdate], action: str) -> None:
39
49
  with rich.progress.Progress() as progress:
40
50
  layer_id_to_task_id = {}
41
51
  overall_task_id = progress.add_task(description=f'[bold blue]{action} Docker image', total=None)
@@ -97,6 +107,74 @@ def process_docker_status_updates(status_updates: Iterable[DockerStatusUpdate],
97
107
  print(update)
98
108
 
99
109
 
110
+ def _process_docker_status_updates_with_logging(status_updates: Iterable[DockerStatusUpdate], action: str) -> None:
111
+ layer_progress = {}
112
+ layer_status = {}
113
+ last_log_time = time.time()
114
+
115
+ logger.info(f'{action} Docker image...')
116
+
117
+ for update in status_updates:
118
+ current_time = time.time()
119
+
120
+ if 'progressDetail' in update and 'id' in update:
121
+ layer_id = update['id']
122
+ progress_detail = update['progressDetail']
123
+
124
+ if progress_detail and 'current' in progress_detail and 'total' in progress_detail:
125
+ current = progress_detail['current']
126
+ total = progress_detail['total']
127
+ percentage = (current / total * 100) if total > 0 else 0
128
+ layer_progress[layer_id] = percentage
129
+ layer_status[layer_id] = f'{action.lower()}'
130
+ elif update.get('status') == 'Layer already exists':
131
+ layer_progress[layer_id] = 100
132
+ layer_status[layer_id] = 'already exists'
133
+
134
+ elif 'status' in update and 'id' in update:
135
+ layer_id = update['id']
136
+ status = update['status']
137
+ layer_status[layer_id] = status.lower()
138
+
139
+ if status in ['Pushed', 'Uploaded'] or status == 'Layer already exists':
140
+ layer_progress[layer_id] = 100
141
+
142
+ elif 'status' in update and update['status']:
143
+ status = update['status']
144
+ if status not in ['Preparing', 'Pushing', 'Pushed', 'Waiting', 'Layer already exists']:
145
+ logger.info(f'{action} Docker image - {status}')
146
+
147
+ if current_time - last_log_time >= 10.0:
148
+ _log_progress_summary(action, layer_progress, layer_status)
149
+ last_log_time = current_time
150
+
151
+ _log_progress_summary(action, layer_progress, layer_status)
152
+ logger.info(f'{action} Docker image completed')
153
+
154
+
155
+ def _log_progress_summary(action: str, layer_progress: dict, layer_status: dict) -> None:
156
+ if not layer_progress and not layer_status:
157
+ return
158
+
159
+ completed_layers = sum(1 for progress in layer_progress.values() if progress >= 100)
160
+ total_layers = len(layer_progress) if layer_progress else len(layer_status)
161
+
162
+ if total_layers > 0:
163
+ overall_percentage = completed_layers / total_layers * 100
164
+ logger.info(
165
+ f'{action} progress: {completed_layers}/{total_layers} layers completed ({overall_percentage:.1f}%)'
166
+ )
167
+
168
+ active_layers = [
169
+ layer_id
170
+ for layer_id, status in layer_status.items()
171
+ if status in ['preparing', 'waiting', 'pushing', 'uploading'] and layer_progress.get(layer_id, 0) < 100
172
+ ]
173
+
174
+ if active_layers:
175
+ logger.info(f'Active layers: {", ".join(active_layers[:5])}{"..." if len(active_layers) > 5 else ""}')
176
+
177
+
100
178
  def set_app_version_as_active(
101
179
  app_version_uuid: str,
102
180
  ):
@@ -127,13 +205,21 @@ def push_application(
127
205
 
128
206
  api_client = BiolibApiClient.get()
129
207
  if not api_client.is_signed_in:
130
- # TODO: Create an exception class for expected errors like this that does not print stacktrace
131
-
132
- raise Exception(
133
- 'You must be authenticated to push an application.\n'
134
- 'Please set the environment variable "BIOLIB_TOKEN=[your_deploy_token]"\n'
135
- f'You can get a deploy key at: {api_client.base_url}/{app_uri_to_fetch}/settings/keys/'
136
- ) from None
208
+ github_ref = os.getenv('GITHUB_REF')
209
+ if github_ref and not api_client.resource_deploy_key:
210
+ raise AuthenticationError(
211
+ 'You must be authenticated to push an application.\n'
212
+ 'Please set the environment variable "BIOLIB_TOKEN=[your_deploy_token]"\n'
213
+ f'You can get a deploy key at: {api_client.base_url}/{app_uri_to_fetch}/settings/keys/\n'
214
+ 'Then add it to your GitHub repository at: '
215
+ 'Settings -> Secrets and variables -> Actions -> Repository secrets'
216
+ )
217
+ else:
218
+ raise AuthenticationError(
219
+ 'You must be authenticated to push an application.\n'
220
+ 'Please set the environment variable "BIOLIB_TOKEN=[your_deploy_token]"\n'
221
+ f'You can get a deploy key at: {api_client.base_url}/{app_uri_to_fetch}/settings/keys/'
222
+ )
137
223
 
138
224
  # prepare zip file
139
225
  config_yml_path = app_path_absolute.joinpath('.biolib/config.yml')
biolib/cli/push.py CHANGED
@@ -1,8 +1,10 @@
1
1
  import logging
2
+ import sys
2
3
  from typing import Optional
3
4
 
4
5
  import click
5
6
 
7
+ from biolib._internal.errors import AuthenticationError
6
8
  from biolib._internal.push_application import push_application
7
9
  from biolib.biolib_logging import logger, logger_no_user_data
8
10
 
@@ -34,11 +36,15 @@ def push(uri, path: str, copy_images_from_version: Optional[str], dev: bool, pre
34
36
  elif pre_release:
35
37
  set_as_active = False
36
38
  set_as_published = True
37
- push_application(
38
- app_path=path,
39
- app_uri=uri,
40
- app_version_to_copy_images_from=copy_images_from_version,
41
- set_as_active=set_as_active,
42
- set_as_published=set_as_published,
43
- dry_run=dry_run,
44
- )
39
+ try:
40
+ push_application(
41
+ app_path=path,
42
+ app_uri=uri,
43
+ app_version_to_copy_images_from=copy_images_from_version,
44
+ set_as_active=set_as_active,
45
+ set_as_published=set_as_published,
46
+ dry_run=dry_run,
47
+ )
48
+ except AuthenticationError as error:
49
+ print(error.message, file=sys.stderr)
50
+ exit(1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pybiolib
3
- Version: 1.2.876
3
+ Version: 1.2.883
4
4
  Summary: BioLib Python Client
5
5
  License: MIT
6
6
  Keywords: biolib
@@ -6,6 +6,7 @@ biolib/_internal/data_record/__init__.py,sha256=fGdME6JGRU_2VxpJbYpGXYndjN-feUkm
6
6
  biolib/_internal/data_record/data_record.py,sha256=SD3-tKQY2RZv9ZSVNUhd2ISDYV64Fk1Sc642qyf_Vis,4618
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
+ biolib/_internal/errors.py,sha256=AokI6Dvaa22B__LTfSZUVdDsm0ye7lgBFv_aD9O_kbs,163
9
10
  biolib/_internal/file_utils.py,sha256=4jT6j7bB21c0JNn5BfnyWQib_zt0CVtJ_TiOFOStRcE,2604
10
11
  biolib/_internal/fuse_mount/__init__.py,sha256=B_tM6RM2dBw-vbpoHJC4X3tOAaN1H2RDvqYJOw3xFwg,55
11
12
  biolib/_internal/fuse_mount/experiment_fuse_mount.py,sha256=08aUdEq_bvqLBft_gSLjOClKDy5sBnMts1RfJf7AP_U,7012
@@ -21,7 +22,7 @@ biolib/_internal/llm_instructions/.github/instructions/style-react-ts.instructio
21
22
  biolib/_internal/llm_instructions/.github/prompts/biolib_app_inputs.prompt.md,sha256=SMzXZImdID0yKjhE1fG54VrHdD8IVuwRxRKsMF-KjWs,697
22
23
  biolib/_internal/llm_instructions/.github/prompts/biolib_run_apps.prompt.md,sha256=-t1bmGr3zEFa6lKHaLcI98yq4Sg1TCMW8xbelNSHaOA,696
23
24
  biolib/_internal/llm_instructions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
- biolib/_internal/push_application.py,sha256=snnd3aCPJeByEQTCajDi7Op5TH9-BXjJS9QCS8e6cjY,14347
25
+ biolib/_internal/push_application.py,sha256=OwabiLPK8P_py3GavIzhYqcQL7hZ6XPKDrhSJUSxeRs,18023
25
26
  biolib/_internal/runtime.py,sha256=BiHl4klUHr36MCpqKaUso4idHeBZfPAahLYRQrabFqA,486
26
27
  biolib/_internal/templates/__init__.py,sha256=NVbhLUMC8HITzkLvP88Qu7FHaL-SvQord-DX3gh1Ykk,24
27
28
  biolib/_internal/templates/init_template/.biolib/config.yml,sha256=y4ndTgbFvUE1UiGcIOqogT2Wm8jahGffeyU5rlCEltQ,427
@@ -85,7 +86,7 @@ biolib/cli/data_record.py,sha256=t8DfJK2EZ_SNZ9drDA_N5Jqy8DNwf9f5SlFrIaOvtv0,350
85
86
  biolib/cli/download_container.py,sha256=HIZVHOPmslGE5M2Dsp9r2cCkAEJx__vcsDz5Wt5LRos,483
86
87
  biolib/cli/init.py,sha256=_dhnKdlRMy2oSNRRsXdjFKVzayy2aiRQkeF948VCCkg,4438
87
88
  biolib/cli/lfs.py,sha256=z2qHUwink85mv9yDgifbVKkVwuyknGhMDTfly_gLKJM,4151
88
- biolib/cli/push.py,sha256=pSFEUQkQ69M__eR1nIT9ejW4V4_MtX3lb8ydEc1uKiM,1484
89
+ biolib/cli/push.py,sha256=J8BswMYVeTacZBHbm4K4a2XbS_I8kvfgRZLoby2wi3I,1695
89
90
  biolib/cli/run.py,sha256=MCo0ZqW2pHBxOoCI3i5gAx5D0auW9fmxHqkAF4TRhms,2134
90
91
  biolib/cli/runtime.py,sha256=Xv-nrma5xX8NidWcvbUKcUvuN5TCarZa4A8mPVmF-z0,361
91
92
  biolib/cli/sdk.py,sha256=XisJwTft4QDB_99eGDlz-WBqqRwIqkVg7HyvxWtOG8w,471
@@ -140,8 +141,8 @@ biolib/utils/cache_state.py,sha256=u256F37QSRIVwqKlbnCyzAX4EMI-kl6Dwu6qwj-Qmag,3
140
141
  biolib/utils/multipart_uploader.py,sha256=XvGP1I8tQuKhAH-QugPRoEsCi9qvbRk-DVBs5PNwwJo,8452
141
142
  biolib/utils/seq_util.py,sha256=Ozk0blGtPur_D9MwShD02r_mphyQmgZkx-lOHOwnlIM,6730
142
143
  biolib/utils/zip/remote_zip.py,sha256=0wErYlxir5921agfFeV1xVjf29l9VNgGQvNlWOlj2Yc,23232
143
- pybiolib-1.2.876.dist-info/LICENSE,sha256=F2h7gf8i0agDIeWoBPXDMYScvQOz02pAWkKhTGOHaaw,1067
144
- pybiolib-1.2.876.dist-info/METADATA,sha256=Vcom4VROgeB12DAZP7lZnWrX7hZ0wipD3drOcAn8RGs,1570
145
- pybiolib-1.2.876.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
146
- pybiolib-1.2.876.dist-info/entry_points.txt,sha256=p6DyaP_2kctxegTX23WBznnrDi4mz6gx04O5uKtRDXg,42
147
- pybiolib-1.2.876.dist-info/RECORD,,
144
+ pybiolib-1.2.883.dist-info/LICENSE,sha256=F2h7gf8i0agDIeWoBPXDMYScvQOz02pAWkKhTGOHaaw,1067
145
+ pybiolib-1.2.883.dist-info/METADATA,sha256=SpFwDiscPum6qx9WgKbEzFGSBnMntarbRXNVGXSW0I8,1570
146
+ pybiolib-1.2.883.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
147
+ pybiolib-1.2.883.dist-info/entry_points.txt,sha256=p6DyaP_2kctxegTX23WBznnrDi4mz6gx04O5uKtRDXg,42
148
+ pybiolib-1.2.883.dist-info/RECORD,,