pybiolib 1.2.576__py3-none-any.whl → 1.2.582__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,39 @@
1
+ from biolib import utils
2
+ from biolib._internal.types import Optional
3
+ from biolib.api.client import ApiClient, ApiClientInitDict
4
+ from biolib.app import BioLibApp
5
+
6
+
7
+ class Session:
8
+ def __init__(self, _init_dict: ApiClientInitDict) -> None:
9
+ self._api = ApiClient(_init_dict=_init_dict)
10
+
11
+ @staticmethod
12
+ def get_session(refresh_token: str, base_url: Optional[str] = None) -> 'Session':
13
+ return Session(
14
+ _init_dict=ApiClientInitDict(
15
+ refresh_token=refresh_token,
16
+ base_url=base_url or utils.load_base_url_from_env(),
17
+ )
18
+ )
19
+
20
+ def load(self, uri: str) -> BioLibApp:
21
+ r"""Load a BioLib application by its URI or website URL.
22
+
23
+ Args:
24
+ uri (str): The URI or website URL of the application to load. Can be either:
25
+ - App URI (e.g., 'biolib/myapp:1.0.0')
26
+ - Website URL (e.g., 'https://biolib.com/biolib/myapp/')
27
+
28
+ Returns:
29
+ BioLibApp: The loaded application object
30
+
31
+ Example::
32
+
33
+ >>> # Load by URI
34
+ >>> app = biolib.load('biolib/myapp:1.0.0')
35
+ >>> # Load by website URL
36
+ >>> app = biolib.load('https://biolib.com/biolib/myapp/')
37
+ >>> result = app.cli('--help')
38
+ """
39
+ return BioLibApp(uri=uri, _api_client=self._api)
biolib/api/client.py CHANGED
@@ -1,10 +1,17 @@
1
+ import base64
2
+ import binascii
3
+ import json
4
+ from datetime import datetime, timezone
5
+ from json.decoder import JSONDecodeError
1
6
  from urllib.parse import urlencode, urljoin
2
7
 
3
8
  import importlib_metadata
4
9
 
5
10
  from biolib._internal.http_client import HttpClient, HttpResponse
11
+ from biolib._internal.types.typing import Any, Dict, Optional, TypedDict, Union, cast
6
12
  from biolib.biolib_api_client import BiolibApiClient as DeprecatedApiClient
7
- from biolib.typing_utils import Dict, Optional, Union, cast
13
+ from biolib.biolib_errors import BioLibError
14
+ from biolib.biolib_logging import logger
8
15
 
9
16
  OptionalHeaders = Union[
10
17
  Optional[Dict[str, str]],
@@ -20,9 +27,23 @@ def _get_biolib_package_version() -> str:
20
27
  return '0.0.0'
21
28
 
22
29
 
30
+ class ApiClientInitDict(TypedDict):
31
+ refresh_token: str
32
+ base_url: str
33
+
34
+
35
+ class JwtDecodeError(Exception):
36
+ pass
37
+
38
+
23
39
  class ApiClient(HttpClient):
24
40
  _biolib_package_version: str = _get_biolib_package_version()
25
41
 
42
+ def __init__(self, _init_dict: Optional[ApiClientInitDict] = None) -> None:
43
+ self._access_token: Optional[str] = None
44
+ self._refresh_token: Optional[str] = _init_dict['refresh_token'] if _init_dict else None
45
+ self._base_url: Optional[str] = _init_dict['base_url'] if _init_dict else None
46
+
26
47
  def get(
27
48
  self,
28
49
  path: str,
@@ -83,36 +104,104 @@ class ApiClient(HttpClient):
83
104
  url=self._get_absolute_url(path=path, query_params=None),
84
105
  )
85
106
 
86
- @staticmethod
87
- def _get_headers(opt_headers: OptionalHeaders, authenticate: bool) -> Dict[str, str]:
107
+ def _get_headers(self, opt_headers: OptionalHeaders, authenticate: bool) -> Dict[str, str]:
88
108
  # Only keep header keys with a value
89
109
  headers: Dict[str, str] = {key: value for key, value in (opt_headers or {}).items() if value}
90
110
 
91
111
  if authenticate:
92
- deprecated_api_client = DeprecatedApiClient.get()
93
- if deprecated_api_client.is_signed_in:
94
- deprecated_api_client.refresh_access_token()
95
-
96
- if deprecated_api_client.resource_deploy_key:
97
- headers['Authorization'] = f'Token {deprecated_api_client.resource_deploy_key}'
112
+ if self._refresh_token:
113
+ headers['Authorization'] = f'Bearer {self._get_access_token()}'
98
114
  else:
99
- # Adding access_token outside is_signed_in check as job_worker.py currently sets access_token
100
- # without setting refresh_token
101
- access_token = deprecated_api_client.access_token
102
- if access_token:
103
- headers['Authorization'] = f'Bearer {access_token}'
115
+ # TODO: Remove this block when deprecated api client is removed
116
+ deprecated_api_client = DeprecatedApiClient.get()
117
+ if deprecated_api_client.is_signed_in:
118
+ deprecated_api_client.refresh_access_token()
119
+
120
+ if deprecated_api_client.resource_deploy_key:
121
+ headers['Authorization'] = f'Token {deprecated_api_client.resource_deploy_key}'
122
+ else:
123
+ # Adding access_token outside is_signed_in check as job_worker.py currently sets access_token
124
+ # without setting refresh_token
125
+ access_token = deprecated_api_client.access_token
126
+ if access_token:
127
+ headers['Authorization'] = f'Bearer {access_token}'
104
128
 
105
129
  headers['client-type'] = 'biolib-python'
106
130
  headers['client-version'] = ApiClient._biolib_package_version
107
131
 
108
132
  return headers
109
133
 
110
- @staticmethod
111
- def _get_absolute_url(path: str, query_params: Optional[Dict[str, Union[str, int]]]) -> str:
134
+ def _get_absolute_url(self, path: str, query_params: Optional[Dict[str, Union[str, int]]]) -> str:
112
135
  deprecated_api_client = DeprecatedApiClient.get()
113
- base_api_url = urljoin(deprecated_api_client.base_url, '/api/')
136
+ base_url = self._base_url or deprecated_api_client.base_url
137
+ base_api_url = urljoin(base_url, '/api/')
114
138
  url = urljoin(base_api_url, path.strip('/') + '/')
115
139
  if query_params:
116
140
  url = url + '?' + urlencode(query_params)
117
141
 
118
142
  return url
143
+
144
+ def _get_access_token(self) -> str:
145
+ if self._access_token:
146
+ decoded_token = self._decode_jwt_without_checking_signature(self._access_token)
147
+ if datetime.now(tz=timezone.utc).timestamp() < decoded_token['payload']['exp'] - 60: # 60 second buffer
148
+ # Token has not expired yet
149
+ return self._access_token
150
+
151
+ # TODO: Implement nicer error handling
152
+ try:
153
+ response = HttpClient.request(
154
+ method='POST',
155
+ url=f'{self._base_url}/api/user/token/refresh/',
156
+ data={'refresh': self._refresh_token},
157
+ )
158
+ except Exception as exception:
159
+ logger.error('Sign in with refresh token failed')
160
+ raise exception
161
+
162
+ try:
163
+ response_dict = response.json()
164
+ except JSONDecodeError as error:
165
+ logger.error('Could not decode response from server as JSON:')
166
+ raise BioLibError(response.text) from error
167
+
168
+ self._access_token = cast(str, response_dict['access'])
169
+ return self._access_token
170
+
171
+ @staticmethod
172
+ def _decode_jwt_without_checking_signature(jwt: str) -> Dict[str, Any]:
173
+ jwt_bytes = jwt.encode('utf-8')
174
+
175
+ try:
176
+ signing_input, _ = jwt_bytes.rsplit(b'.', 1)
177
+ header_segment, payload_segment = signing_input.split(b'.', 1)
178
+ except ValueError as error:
179
+ raise JwtDecodeError('Not enough segments') from error
180
+
181
+ try:
182
+ header_data = base64.urlsafe_b64decode(header_segment)
183
+ except (TypeError, binascii.Error) as error:
184
+ raise JwtDecodeError('Invalid header padding') from error
185
+
186
+ try:
187
+ header = json.loads(header_data)
188
+ except ValueError as error:
189
+ raise JwtDecodeError(f'Invalid header string: {error}') from error
190
+
191
+ if not isinstance(header, dict):
192
+ raise JwtDecodeError('Invalid header string: must be a json object')
193
+
194
+ try:
195
+ payload_data = base64.urlsafe_b64decode(payload_segment)
196
+ except (TypeError, binascii.Error) as error:
197
+ raise JwtDecodeError('Invalid payload padding') from error
198
+
199
+ try:
200
+ payload = json.loads(payload_data)
201
+ except ValueError as error:
202
+ raise JwtDecodeError(f'Invalid payload string: {error}') from error
203
+
204
+ if not isinstance(header, dict):
205
+ raise JwtDecodeError('Invalid payload string: must be a json object')
206
+
207
+ return dict(header=header, payload=payload)
biolib/app/app.py CHANGED
@@ -6,6 +6,7 @@ import string
6
6
  from pathlib import Path
7
7
 
8
8
  from biolib import utils
9
+ from biolib.api.client import ApiClient
9
10
  from biolib.biolib_api_client import JobState
10
11
  from biolib.biolib_api_client.app_types import App, AppVersion
11
12
  from biolib.biolib_api_client.biolib_app_api import BiolibAppApi
@@ -16,13 +17,15 @@ from biolib.biolib_logging import logger
16
17
  from biolib.compute_node.job_worker.job_worker import JobWorker
17
18
  from biolib.experiments.experiment import Experiment
18
19
  from biolib.jobs import Job
19
- from biolib.typing_utils import Optional, Dict
20
+ from biolib.typing_utils import Dict, Optional
20
21
  from biolib.utils.app_uri import parse_app_uri
21
22
 
22
23
 
23
24
  class BioLibApp:
24
- def __init__(self, uri: str):
25
- app_response = BiolibAppApi.get_by_uri(uri)
25
+ def __init__(self, uri: str, _api_client: Optional[ApiClient] = None):
26
+ self._api_client: Optional[ApiClient] = _api_client
27
+
28
+ app_response = BiolibAppApi.get_by_uri(uri=uri, api_client=self._api_client)
26
29
  self._app: App = app_response['app']
27
30
  self._app_uri = app_response['app_uri']
28
31
  self._app_version: AppVersion = app_response['app_version']
@@ -98,6 +101,7 @@ class BioLibApp:
98
101
  timeout=timeout,
99
102
  requested_machine_count=machine_count,
100
103
  temporary_client_secrets=temporary_client_secrets,
104
+ api_client=self._api_client,
101
105
  )
102
106
  logger.info(f'View the result in your browser at: {utils.BIOLIB_BASE_URL}/results/{job.id}/')
103
107
  if blocking:
@@ -8,6 +8,7 @@ import urllib.parse
8
8
  import biolib.api
9
9
  from biolib import biolib_errors
10
10
  from biolib._internal.http_client import HttpError
11
+ from biolib.api.client import ApiClient
11
12
  from biolib.biolib_api_client import AppGetResponse
12
13
  from biolib.biolib_logging import logger
13
14
  from biolib.typing_utils import Optional
@@ -84,10 +85,11 @@ def _get_app_uri_from_str(input_str: str) -> str:
84
85
 
85
86
  class BiolibAppApi:
86
87
  @staticmethod
87
- def get_by_uri(uri: str) -> AppGetResponse:
88
+ def get_by_uri(uri: str, api_client: Optional[ApiClient] = None) -> AppGetResponse:
88
89
  uri = _get_app_uri_from_str(uri)
90
+ api = api_client or biolib.api.client
89
91
  try:
90
- response = biolib.api.client.get(path='/app/', params={'uri': uri})
92
+ response = api.get(path='/app/', params={'uri': uri})
91
93
  app_response: AppGetResponse = response.json()
92
94
  return app_response
93
95
 
@@ -5,6 +5,7 @@ import biolib.api
5
5
 
6
6
  from biolib import utils
7
7
  from biolib._internal.http_client import HttpError
8
+ from biolib.api.client import ApiClient
8
9
  from biolib.biolib_api_client import CloudJob, JobState
9
10
  from biolib.biolib_errors import JobResultPermissionError, JobResultError, JobResultNotFound, StorageDownloadFailed
10
11
  from biolib.biolib_logging import logger
@@ -35,19 +36,19 @@ def _get_user_info() -> Optional[str]:
35
36
 
36
37
 
37
38
  class BiolibJobApi:
38
-
39
39
  @staticmethod
40
40
  def create(
41
- app_version_id,
42
- app_resource_name_prefix=None,
43
- override_command=False,
44
- caller_job=None,
45
- machine='',
46
- experiment_uuid: Optional[str] = None,
47
- timeout: Optional[int] = None,
48
- notify: bool = False,
49
- requested_machine_count: Optional[int] = None,
50
- temporary_client_secrets: Optional[Dict[str, str]] = None,
41
+ app_version_id,
42
+ app_resource_name_prefix=None,
43
+ override_command=False,
44
+ caller_job=None,
45
+ machine='',
46
+ experiment_uuid: Optional[str] = None,
47
+ timeout: Optional[int] = None,
48
+ notify: bool = False,
49
+ requested_machine_count: Optional[int] = None,
50
+ temporary_client_secrets: Optional[Dict[str, str]] = None,
51
+ api_client: Optional[ApiClient] = None,
51
52
  ):
52
53
  data = {
53
54
  'app_version_id': app_version_id,
@@ -58,22 +59,16 @@ class BiolibJobApi:
58
59
  }
59
60
 
60
61
  if app_resource_name_prefix:
61
- data.update({
62
- 'app_resource_name_prefix': app_resource_name_prefix
63
- })
62
+ data.update({'app_resource_name_prefix': app_resource_name_prefix})
64
63
 
65
64
  if override_command:
66
- data.update({
67
- 'arguments_override_command': override_command
68
- })
65
+ data.update({'arguments_override_command': override_command})
69
66
 
70
67
  if caller_job:
71
68
  data['caller_job'] = caller_job
72
69
 
73
70
  if machine:
74
- data.update({
75
- 'requested_machine': machine
76
- })
71
+ data.update({'requested_machine': machine})
77
72
 
78
73
  if requested_machine_count:
79
74
  data.update({'requested_machine_count': requested_machine_count})
@@ -87,7 +82,8 @@ class BiolibJobApi:
87
82
  if temporary_client_secrets:
88
83
  data['temporary_client_secrets'] = temporary_client_secrets
89
84
 
90
- response = biolib.api.client.post(path='/jobs/', data=data)
85
+ api = api_client or biolib.api.client
86
+ response = api.post(path='/jobs/', data=data)
91
87
 
92
88
  return response.json()
93
89
 
@@ -99,20 +95,25 @@ class BiolibJobApi:
99
95
  logger.error(f'Failed to update job "{job_uuid}" to state "{state.value}" due to {error}')
100
96
 
101
97
  @staticmethod
102
- def create_cloud_job(job_id: str, result_name_prefix: Optional[str]) -> CloudJob:
98
+ def create_cloud_job(
99
+ job_id: str,
100
+ result_name_prefix: Optional[str],
101
+ api_client: Optional[ApiClient] = None,
102
+ ) -> CloudJob:
103
103
  data = {'job_id': job_id}
104
104
  if result_name_prefix:
105
105
  data['result_name_prefix'] = result_name_prefix
106
106
 
107
- response = biolib.api.client.post(path='/jobs/cloud/', data=data)
107
+ api = api_client or biolib.api.client
108
+ response = api.post(path='/jobs/cloud/', data=data)
108
109
  cloud_job: CloudJob = response.json()
109
110
  return cloud_job
110
111
 
111
112
  @staticmethod
112
113
  def get_job_storage_download_url(
113
- job_uuid: str,
114
- job_auth_token: str,
115
- storage_type: Literal['input', 'results'],
114
+ job_uuid: str,
115
+ job_auth_token: str,
116
+ storage_type: Literal['input', 'results'],
116
117
  ) -> str:
117
118
  try:
118
119
  response = biolib.api.client.get(
@@ -154,19 +155,21 @@ class BiolibJobApi:
154
155
 
155
156
  @staticmethod
156
157
  def create_job_with_data(
157
- app_version_uuid: str,
158
- app_resource_name_prefix: Optional[str],
159
- module_input_serialized: bytes,
160
- arguments_override_command: bool,
161
- experiment_uuid: Optional[str],
162
- requested_machine: Optional[str],
163
- result_name_prefix: Optional[str],
164
- caller_job_uuid: Optional[str] = None,
165
- requested_timeout_seconds: Optional[int] = None,
166
- notify: bool = False,
167
- requested_machine_count: Optional[int] = None,
158
+ app_version_uuid: str,
159
+ app_resource_name_prefix: Optional[str],
160
+ module_input_serialized: bytes,
161
+ arguments_override_command: bool,
162
+ experiment_uuid: Optional[str],
163
+ requested_machine: Optional[str],
164
+ result_name_prefix: Optional[str],
165
+ caller_job_uuid: Optional[str] = None,
166
+ requested_timeout_seconds: Optional[int] = None,
167
+ notify: bool = False,
168
+ requested_machine_count: Optional[int] = None,
169
+ api_client: Optional[ApiClient] = None,
168
170
  ) -> Dict:
169
- job_dict: Dict = biolib.api.client.post(
171
+ api = api_client or biolib.api.client
172
+ job_dict: Dict = api.post(
170
173
  path='/jobs/create_job_with_data/',
171
174
  data=module_input_serialized,
172
175
  headers={
@@ -184,6 +187,6 @@ class BiolibJobApi:
184
187
  'result-name-prefix': result_name_prefix,
185
188
  'requested-timeout-seconds': str(requested_timeout_seconds) if requested_timeout_seconds else None,
186
189
  'notify': 'true' if notify else 'false',
187
- }
190
+ },
188
191
  ).json()
189
192
  return job_dict
biolib/jobs/job.py CHANGED
@@ -6,9 +6,11 @@ from datetime import datetime, timedelta
6
6
  from pathlib import Path
7
7
  from urllib.parse import urlparse
8
8
 
9
- from biolib import api, utils
9
+ import biolib.api.client
10
+ from biolib import utils
10
11
  from biolib._internal.http_client import HttpClient
11
12
  from biolib._internal.utils import open_browser_window_from_notebook
13
+ from biolib.api.client import ApiClient
12
14
  from biolib.biolib_api_client import BiolibApiClient, CreatedJobDict
13
15
  from biolib.biolib_api_client.biolib_app_api import BiolibAppApi
14
16
  from biolib.biolib_api_client.biolib_job_api import BiolibJobApi
@@ -39,7 +41,9 @@ class Job:
39
41
  }
40
42
  )
41
43
 
42
- def __init__(self, job_dict: JobDict):
44
+ def __init__(self, job_dict: JobDict, _api_client: Optional[ApiClient] = None):
45
+ self._api_client: Optional[ApiClient] = _api_client
46
+
43
47
  self._uuid: str = job_dict['uuid']
44
48
  self._auth_token: str = job_dict['auth_token']
45
49
 
@@ -208,7 +212,7 @@ class Job:
208
212
 
209
213
  def cancel(self) -> None:
210
214
  try:
211
- api.client.patch(
215
+ biolib.api.client.patch(
212
216
  path=f'/jobs/{self._uuid}/',
213
217
  headers={'Job-Auth-Token': self._auth_token} if self._auth_token else None,
214
218
  data={'state': 'cancelled'},
@@ -219,7 +223,7 @@ class Job:
219
223
 
220
224
  def rename(self, name: str) -> None:
221
225
  try:
222
- api.client.patch(
226
+ biolib.api.client.patch(
223
227
  path=f'/jobs/{self._uuid}/main_result/',
224
228
  headers={'Job-Auth-Token': self._auth_token} if self._auth_token else None,
225
229
  data={'result_name_prefix': name},
@@ -325,20 +329,21 @@ class Job:
325
329
  params['state'] = status
326
330
 
327
331
  api_path = '/jobs/'
328
- response = api.client.get(api_path, params=params).json()
332
+ response = biolib.api.client.get(api_path, params=params).json()
329
333
  jobs = [job_dict for job_dict in response['results']]
330
334
 
331
335
  for page_number in range(2, response['page_count'] + 1):
332
336
  if len(jobs) >= count:
333
337
  break
334
- page_response = api.client.get(path=api_path, params=dict(**params, page=page_number)).json()
338
+ page_response = biolib.api.client.get(path=api_path, params=dict(**params, page=page_number)).json()
335
339
  jobs.extend([job_dict for job_dict in page_response['results']])
336
340
 
337
341
  return jobs[:count]
338
342
 
339
343
  @staticmethod
340
- def _get_job_dict(uuid: str, auth_token: Optional[str] = None) -> JobDict:
341
- job_dict: JobDict = api.client.get(
344
+ def _get_job_dict(uuid: str, auth_token: Optional[str] = None, api_client: Optional[ApiClient] = None) -> JobDict:
345
+ api = api_client or biolib.api.client
346
+ job_dict: JobDict = api.get(
342
347
  path=f'/jobs/{uuid}/',
343
348
  headers={'Job-Auth-Token': auth_token} if auth_token else None,
344
349
  ).json()
@@ -506,6 +511,7 @@ class Job:
506
511
  notify: bool = False,
507
512
  requested_machine_count: Optional[int] = None,
508
513
  temporary_client_secrets: Optional[Dict[str, str]] = None,
514
+ api_client: Optional[ApiClient] = None,
509
515
  ) -> 'Job':
510
516
  if len(module_input_serialized) < 500_000 and temporary_client_secrets is None:
511
517
  _job_dict = BiolibJobApi.create_job_with_data(
@@ -519,6 +525,7 @@ class Job:
519
525
  requested_timeout_seconds=timeout,
520
526
  result_name_prefix=result_prefix,
521
527
  requested_machine_count=requested_machine_count,
528
+ api_client=api_client,
522
529
  )
523
530
  return Job(cast(JobDict, _job_dict))
524
531
 
@@ -532,8 +539,9 @@ class Job:
532
539
  timeout=timeout,
533
540
  requested_machine_count=requested_machine_count,
534
541
  temporary_client_secrets=temporary_client_secrets,
542
+ api_client=api_client,
535
543
  )
536
544
  JobStorage.upload_module_input(job=job_dict, module_input_serialized=module_input_serialized)
537
545
  cloud_job = BiolibJobApi.create_cloud_job(job_id=job_dict['public_id'], result_name_prefix=result_prefix)
538
546
  logger.debug(f"Cloud: Job created with id {cloud_job['public_id']}")
539
- return Job(cast(JobDict, job_dict))
547
+ return Job(cast(JobDict, job_dict), _api_client=api_client)
biolib/sdk/__init__.py CHANGED
@@ -5,12 +5,17 @@ from biolib._data_record.data_record import DataRecord as _DataRecord
5
5
  from biolib._internal.push_application import push_application as _push_application
6
6
  from biolib._internal.push_application import set_app_version_as_active as _set_app_version_as_active
7
7
  from biolib._runtime.runtime import Runtime as _Runtime
8
+ from biolib._session.session import Session as _Session
8
9
  from biolib.app import BioLibApp as _BioLibApp
9
10
 
10
11
  # Classes to expose as public API
11
12
  Runtime = _Runtime
12
13
 
13
14
 
15
+ def get_session(refresh_token: str, base_url: Optional[str] = None) -> _Session:
16
+ return _Session.get_session(refresh_token=refresh_token, base_url=base_url)
17
+
18
+
14
19
  def push_app_version(uri: str, path: str) -> _BioLibApp:
15
20
  push_data = _push_application(
16
21
  app_uri=uri,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pybiolib
3
- Version: 1.2.576
3
+ Version: 1.2.582
4
4
  Summary: BioLib Python Client
5
5
  License: MIT
6
6
  Keywords: biolib
@@ -26,17 +26,18 @@ biolib/_internal/types/typing.py,sha256=qrsk8hHcGEbDpU1QQFzHAKnhQxkMe7uJ6pxHeAnf
26
26
  biolib/_internal/utils/__init__.py,sha256=p5vsIFyu-zYqBgdSMfwW9NC_jk7rXvvCbV4Bzd3As7c,630
27
27
  biolib/_internal/utils/multinode.py,sha256=-J3PEAK3NaOwCn--5T7vWHkA3yu5w9QhmuhkQcH-2wY,8229
28
28
  biolib/_runtime/runtime.py,sha256=bZQ0m39R9jOBVAtlyvzDnOobKueOAQUCwMUZjDQnO7E,4439
29
+ biolib/_session/session.py,sha256=K-mbWKP6zMEdKha4Key6XEqRvduS_Ol4LgAVjf37tO4,1341
29
30
  biolib/api/__init__.py,sha256=mQ4u8FijqyLzjYMezMUUbbBGNB3iFmkNdjXnWPZ7Jlw,138
30
- biolib/api/client.py,sha256=EN3lDbbgWxw3aPDe8W5XIU6WkMfOHW7Dmsz-R6QqxAw,4176
31
+ biolib/api/client.py,sha256=pILuZhmWCXGuzrp9rPwtwRizvIGGbIlT2o4wV-vwL24,7668
31
32
  biolib/app/__init__.py,sha256=cdPtcfb_U-bxb9iSL4fCEq2rpD9OjkyY4W-Zw60B0LI,37
32
- biolib/app/app.py,sha256=MHYwKgjFDoJFmnY6aX5Qi0IvZwHaTfRDUMwl6yVBq4s,9724
33
+ biolib/app/app.py,sha256=HxW3T1iuDh-DMhVDB0NbgQjRXqgE7dPw8CAdhrd5lbw,9940
33
34
  biolib/app/search_apps.py,sha256=K4a41f5XIWth2BWI7OffASgIsD0ko8elCax8YL2igaY,1470
34
35
  biolib/biolib_api_client/__init__.py,sha256=E5EMa19wJoblwSdQPYrxc_BtIeRsAuO0L_jQweWw-Yk,182
35
36
  biolib/biolib_api_client/api_client.py,sha256=ohvbWbpresxLHFGPkvXACfmiTYsBk8RBx5XsBbLYg_M,7546
36
37
  biolib/biolib_api_client/app_types.py,sha256=1sXz9XnLRKNALMglNdTbew7AL6OkcUan0MPdj4xQLis,2456
37
38
  biolib/biolib_api_client/auth.py,sha256=kjm0ZHnH3I8so3su2sZbBxNHYp-ZUdrZ5lwQ0K36RSw,949
38
- biolib/biolib_api_client/biolib_app_api.py,sha256=RUXrTxk7XKzm4wxv7NBXttuFjT60DI9vNOvkS-cjKNc,5065
39
- biolib/biolib_api_client/biolib_job_api.py,sha256=49SBozudLYlOByuQMABiHZkwK5Jt5_orjv8Vw0rJpks,7306
39
+ biolib/biolib_api_client/biolib_app_api.py,sha256=DdE_kRfDgVa876Cdl_D1P0lBoaghFomEyy-ZP8K7SoY,5177
40
+ biolib/biolib_api_client/biolib_job_api.py,sha256=3Hzv5Os4d377pKrCzg3i7c5xiy9FiqeVssub690GLdA,7423
40
41
  biolib/biolib_api_client/common_types.py,sha256=RH-1KNHqUF-EkTpfPOSTt5Mq1GPdfju_cqXDesscO1I,123
41
42
  biolib/biolib_api_client/job_types.py,sha256=yBdBwjharbQJuXCi2xKMi0t_r6XxnbWnkchHekTpCJY,1351
42
43
  biolib/biolib_api_client/lfs_types.py,sha256=joZWP6-sa5_Ug_6xIp5fHAgEo_bqLE3rbleQocZtDcg,339
@@ -101,12 +102,12 @@ biolib/compute_node/webserver/worker_thread.py,sha256=7uD9yQPhePYvP2HCJ27EeZ_h6p
101
102
  biolib/experiments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
102
103
  biolib/experiments/experiment.py,sha256=pBtnOHz0kKoFxlIGf08o8ZCEOze-CljwfOsTdhvCTCk,8646
103
104
  biolib/jobs/__init__.py,sha256=aIb2H2DHjQbM2Bs-dysFijhwFcL58Blp0Co0gimED3w,32
104
- biolib/jobs/job.py,sha256=vmALUOA9APqz0qTjmlisT6ChZHUwOaTlX-1VLhWJSrA,23176
105
+ biolib/jobs/job.py,sha256=v6q7QbQWKeNCl5Sog912aduboq7PU1l4vhOVhlZewEI,23591
105
106
  biolib/jobs/job_result.py,sha256=rALHiKYNaC9lHi_JJqBob1RubzNLwG9Z386kwRJjd2M,5885
106
107
  biolib/jobs/types.py,sha256=ezvaoTANsWazK6PmfpYcqezdfjP7MNBEBfqIZGoZhz8,997
107
108
  biolib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
108
109
  biolib/runtime/__init__.py,sha256=MlRepA11n2H-3plB5rzWyyHK2JmP6PiaP3i6x3vt0mg,506
109
- biolib/sdk/__init__.py,sha256=8uyOAKt8-Xo11jAWXCg14NkE1u0FkOBC07L3V7wGsCg,1960
110
+ biolib/sdk/__init__.py,sha256=fvSPXe4JC4uq75-y7LGrn3C7VFrQ-1I6Yp6SF5oG2DY,2179
110
111
  biolib/tables.py,sha256=MmruV-nJLc3HbLVJBAiDuDCgS2-4oaUkpoCLLUNYbxQ,1173
111
112
  biolib/templates/__init__.py,sha256=Yx62sSyDCDesRQDQgmbDsLpfgEh93fWE8r9u4g2azXk,36
112
113
  biolib/templates/example_app.py,sha256=EB3E3RT4SeO_ii5nVQqJpi5KDGNE_huF1ub-e5ZFveE,715
@@ -119,8 +120,8 @@ biolib/utils/cache_state.py,sha256=u256F37QSRIVwqKlbnCyzAX4EMI-kl6Dwu6qwj-Qmag,3
119
120
  biolib/utils/multipart_uploader.py,sha256=XvGP1I8tQuKhAH-QugPRoEsCi9qvbRk-DVBs5PNwwJo,8452
120
121
  biolib/utils/seq_util.py,sha256=Ozk0blGtPur_D9MwShD02r_mphyQmgZkx-lOHOwnlIM,6730
121
122
  biolib/utils/zip/remote_zip.py,sha256=0wErYlxir5921agfFeV1xVjf29l9VNgGQvNlWOlj2Yc,23232
122
- pybiolib-1.2.576.dist-info/LICENSE,sha256=F2h7gf8i0agDIeWoBPXDMYScvQOz02pAWkKhTGOHaaw,1067
123
- pybiolib-1.2.576.dist-info/METADATA,sha256=A9yROH2MsXxeQp7bK3C9I3wmtqkUX9SewszNGWiz8ws,1570
124
- pybiolib-1.2.576.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
125
- pybiolib-1.2.576.dist-info/entry_points.txt,sha256=p6DyaP_2kctxegTX23WBznnrDi4mz6gx04O5uKtRDXg,42
126
- pybiolib-1.2.576.dist-info/RECORD,,
123
+ pybiolib-1.2.582.dist-info/LICENSE,sha256=F2h7gf8i0agDIeWoBPXDMYScvQOz02pAWkKhTGOHaaw,1067
124
+ pybiolib-1.2.582.dist-info/METADATA,sha256=EXVefv4YrS8ZoOBerWf0yDWbEHE_LIGzj5QLbzeZ2SY,1570
125
+ pybiolib-1.2.582.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
126
+ pybiolib-1.2.582.dist-info/entry_points.txt,sha256=p6DyaP_2kctxegTX23WBznnrDi4mz6gx04O5uKtRDXg,42
127
+ pybiolib-1.2.582.dist-info/RECORD,,