dtlpy 1.115.44__py3-none-any.whl → 1.116.6__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.
- dtlpy/__init__.py +491 -491
- dtlpy/__version__.py +1 -1
- dtlpy/assets/__init__.py +26 -26
- dtlpy/assets/code_server/config.yaml +2 -2
- dtlpy/assets/code_server/installation.sh +24 -24
- dtlpy/assets/code_server/launch.json +13 -13
- dtlpy/assets/code_server/settings.json +2 -2
- dtlpy/assets/main.py +53 -53
- dtlpy/assets/main_partial.py +18 -18
- dtlpy/assets/mock.json +11 -11
- dtlpy/assets/model_adapter.py +83 -83
- dtlpy/assets/package.json +61 -61
- dtlpy/assets/package_catalog.json +29 -29
- dtlpy/assets/package_gitignore +307 -307
- dtlpy/assets/service_runners/__init__.py +33 -33
- dtlpy/assets/service_runners/converter.py +96 -96
- dtlpy/assets/service_runners/multi_method.py +49 -49
- dtlpy/assets/service_runners/multi_method_annotation.py +54 -54
- dtlpy/assets/service_runners/multi_method_dataset.py +55 -55
- dtlpy/assets/service_runners/multi_method_item.py +52 -52
- dtlpy/assets/service_runners/multi_method_json.py +52 -52
- dtlpy/assets/service_runners/single_method.py +37 -37
- dtlpy/assets/service_runners/single_method_annotation.py +43 -43
- dtlpy/assets/service_runners/single_method_dataset.py +43 -43
- dtlpy/assets/service_runners/single_method_item.py +41 -41
- dtlpy/assets/service_runners/single_method_json.py +42 -42
- dtlpy/assets/service_runners/single_method_multi_input.py +45 -45
- dtlpy/assets/voc_annotation_template.xml +23 -23
- dtlpy/caches/base_cache.py +32 -32
- dtlpy/caches/cache.py +473 -473
- dtlpy/caches/dl_cache.py +201 -201
- dtlpy/caches/filesystem_cache.py +89 -89
- dtlpy/caches/redis_cache.py +84 -84
- dtlpy/dlp/__init__.py +20 -20
- dtlpy/dlp/cli_utilities.py +367 -367
- dtlpy/dlp/command_executor.py +764 -764
- dtlpy/dlp/dlp +1 -1
- dtlpy/dlp/dlp.bat +1 -1
- dtlpy/dlp/dlp.py +128 -128
- dtlpy/dlp/parser.py +651 -651
- dtlpy/entities/__init__.py +83 -83
- dtlpy/entities/analytic.py +347 -347
- dtlpy/entities/annotation.py +1879 -1879
- dtlpy/entities/annotation_collection.py +699 -699
- dtlpy/entities/annotation_definitions/__init__.py +20 -20
- dtlpy/entities/annotation_definitions/base_annotation_definition.py +100 -100
- dtlpy/entities/annotation_definitions/box.py +195 -195
- dtlpy/entities/annotation_definitions/classification.py +67 -67
- dtlpy/entities/annotation_definitions/comparison.py +72 -72
- dtlpy/entities/annotation_definitions/cube.py +204 -204
- dtlpy/entities/annotation_definitions/cube_3d.py +149 -149
- dtlpy/entities/annotation_definitions/description.py +32 -32
- dtlpy/entities/annotation_definitions/ellipse.py +124 -124
- dtlpy/entities/annotation_definitions/free_text.py +62 -62
- dtlpy/entities/annotation_definitions/gis.py +69 -69
- dtlpy/entities/annotation_definitions/note.py +139 -139
- dtlpy/entities/annotation_definitions/point.py +117 -117
- dtlpy/entities/annotation_definitions/polygon.py +182 -182
- dtlpy/entities/annotation_definitions/polyline.py +111 -111
- dtlpy/entities/annotation_definitions/pose.py +92 -92
- dtlpy/entities/annotation_definitions/ref_image.py +86 -86
- dtlpy/entities/annotation_definitions/segmentation.py +240 -240
- dtlpy/entities/annotation_definitions/subtitle.py +34 -34
- dtlpy/entities/annotation_definitions/text.py +85 -85
- dtlpy/entities/annotation_definitions/undefined_annotation.py +74 -74
- dtlpy/entities/app.py +220 -220
- dtlpy/entities/app_module.py +107 -107
- dtlpy/entities/artifact.py +174 -174
- dtlpy/entities/assignment.py +399 -399
- dtlpy/entities/base_entity.py +214 -214
- dtlpy/entities/bot.py +113 -113
- dtlpy/entities/codebase.py +292 -292
- dtlpy/entities/collection.py +38 -38
- dtlpy/entities/command.py +169 -169
- dtlpy/entities/compute.py +449 -449
- dtlpy/entities/dataset.py +1299 -1299
- dtlpy/entities/directory_tree.py +44 -44
- dtlpy/entities/dpk.py +470 -470
- dtlpy/entities/driver.py +235 -235
- dtlpy/entities/execution.py +397 -397
- dtlpy/entities/feature.py +124 -124
- dtlpy/entities/feature_set.py +145 -145
- dtlpy/entities/filters.py +798 -798
- dtlpy/entities/gis_item.py +107 -107
- dtlpy/entities/integration.py +184 -184
- dtlpy/entities/item.py +959 -959
- dtlpy/entities/label.py +123 -123
- dtlpy/entities/links.py +85 -85
- dtlpy/entities/message.py +175 -175
- dtlpy/entities/model.py +684 -684
- dtlpy/entities/node.py +1005 -1005
- dtlpy/entities/ontology.py +810 -803
- dtlpy/entities/organization.py +287 -287
- dtlpy/entities/package.py +657 -657
- dtlpy/entities/package_defaults.py +5 -5
- dtlpy/entities/package_function.py +185 -185
- dtlpy/entities/package_module.py +113 -113
- dtlpy/entities/package_slot.py +118 -118
- dtlpy/entities/paged_entities.py +299 -299
- dtlpy/entities/pipeline.py +624 -624
- dtlpy/entities/pipeline_execution.py +279 -279
- dtlpy/entities/project.py +394 -394
- dtlpy/entities/prompt_item.py +505 -505
- dtlpy/entities/recipe.py +301 -301
- dtlpy/entities/reflect_dict.py +102 -102
- dtlpy/entities/resource_execution.py +138 -138
- dtlpy/entities/service.py +963 -963
- dtlpy/entities/service_driver.py +117 -117
- dtlpy/entities/setting.py +294 -294
- dtlpy/entities/task.py +495 -495
- dtlpy/entities/time_series.py +143 -143
- dtlpy/entities/trigger.py +426 -426
- dtlpy/entities/user.py +118 -118
- dtlpy/entities/webhook.py +124 -124
- dtlpy/examples/__init__.py +19 -19
- dtlpy/examples/add_labels.py +135 -135
- dtlpy/examples/add_metadata_to_item.py +21 -21
- dtlpy/examples/annotate_items_using_model.py +65 -65
- dtlpy/examples/annotate_video_using_model_and_tracker.py +75 -75
- dtlpy/examples/annotations_convert_to_voc.py +9 -9
- dtlpy/examples/annotations_convert_to_yolo.py +9 -9
- dtlpy/examples/convert_annotation_types.py +51 -51
- dtlpy/examples/converter.py +143 -143
- dtlpy/examples/copy_annotations.py +22 -22
- dtlpy/examples/copy_folder.py +31 -31
- dtlpy/examples/create_annotations.py +51 -51
- dtlpy/examples/create_video_annotations.py +83 -83
- dtlpy/examples/delete_annotations.py +26 -26
- dtlpy/examples/filters.py +113 -113
- dtlpy/examples/move_item.py +23 -23
- dtlpy/examples/play_video_annotation.py +13 -13
- dtlpy/examples/show_item_and_mask.py +53 -53
- dtlpy/examples/triggers.py +49 -49
- dtlpy/examples/upload_batch_of_items.py +20 -20
- dtlpy/examples/upload_items_and_custom_format_annotations.py +55 -55
- dtlpy/examples/upload_items_with_modalities.py +43 -43
- dtlpy/examples/upload_segmentation_annotations_from_mask_image.py +44 -44
- dtlpy/examples/upload_yolo_format_annotations.py +70 -70
- dtlpy/exceptions.py +125 -125
- dtlpy/miscellaneous/__init__.py +20 -20
- dtlpy/miscellaneous/dict_differ.py +95 -95
- dtlpy/miscellaneous/git_utils.py +217 -217
- dtlpy/miscellaneous/json_utils.py +14 -14
- dtlpy/miscellaneous/list_print.py +105 -105
- dtlpy/miscellaneous/zipping.py +130 -130
- dtlpy/ml/__init__.py +20 -20
- dtlpy/ml/base_feature_extractor_adapter.py +27 -27
- dtlpy/ml/base_model_adapter.py +1257 -1230
- dtlpy/ml/metrics.py +461 -461
- dtlpy/ml/predictions_utils.py +274 -274
- dtlpy/ml/summary_writer.py +57 -57
- dtlpy/ml/train_utils.py +60 -60
- dtlpy/new_instance.py +252 -252
- dtlpy/repositories/__init__.py +56 -56
- dtlpy/repositories/analytics.py +85 -85
- dtlpy/repositories/annotations.py +916 -916
- dtlpy/repositories/apps.py +383 -383
- dtlpy/repositories/artifacts.py +452 -452
- dtlpy/repositories/assignments.py +599 -599
- dtlpy/repositories/bots.py +213 -213
- dtlpy/repositories/codebases.py +559 -559
- dtlpy/repositories/collections.py +332 -332
- dtlpy/repositories/commands.py +152 -152
- dtlpy/repositories/compositions.py +61 -61
- dtlpy/repositories/computes.py +439 -439
- dtlpy/repositories/datasets.py +1504 -1504
- dtlpy/repositories/downloader.py +976 -923
- dtlpy/repositories/dpks.py +433 -433
- dtlpy/repositories/drivers.py +482 -482
- dtlpy/repositories/executions.py +815 -815
- dtlpy/repositories/feature_sets.py +226 -226
- dtlpy/repositories/features.py +255 -255
- dtlpy/repositories/integrations.py +484 -484
- dtlpy/repositories/items.py +912 -912
- dtlpy/repositories/messages.py +94 -94
- dtlpy/repositories/models.py +1000 -1000
- dtlpy/repositories/nodes.py +80 -80
- dtlpy/repositories/ontologies.py +511 -511
- dtlpy/repositories/organizations.py +525 -525
- dtlpy/repositories/packages.py +1941 -1941
- dtlpy/repositories/pipeline_executions.py +451 -451
- dtlpy/repositories/pipelines.py +640 -640
- dtlpy/repositories/projects.py +539 -539
- dtlpy/repositories/recipes.py +419 -399
- dtlpy/repositories/resource_executions.py +137 -137
- dtlpy/repositories/schema.py +120 -120
- dtlpy/repositories/service_drivers.py +213 -213
- dtlpy/repositories/services.py +1704 -1704
- dtlpy/repositories/settings.py +339 -339
- dtlpy/repositories/tasks.py +1477 -1477
- dtlpy/repositories/times_series.py +278 -278
- dtlpy/repositories/triggers.py +536 -536
- dtlpy/repositories/upload_element.py +257 -257
- dtlpy/repositories/uploader.py +661 -661
- dtlpy/repositories/webhooks.py +249 -249
- dtlpy/services/__init__.py +22 -22
- dtlpy/services/aihttp_retry.py +131 -131
- dtlpy/services/api_client.py +1785 -1785
- dtlpy/services/api_reference.py +40 -40
- dtlpy/services/async_utils.py +133 -133
- dtlpy/services/calls_counter.py +44 -44
- dtlpy/services/check_sdk.py +68 -68
- dtlpy/services/cookie.py +115 -115
- dtlpy/services/create_logger.py +156 -156
- dtlpy/services/events.py +84 -84
- dtlpy/services/logins.py +235 -235
- dtlpy/services/reporter.py +256 -256
- dtlpy/services/service_defaults.py +91 -91
- dtlpy/utilities/__init__.py +20 -20
- dtlpy/utilities/annotations/__init__.py +16 -16
- dtlpy/utilities/annotations/annotation_converters.py +269 -269
- dtlpy/utilities/base_package_runner.py +285 -264
- dtlpy/utilities/converter.py +1650 -1650
- dtlpy/utilities/dataset_generators/__init__.py +1 -1
- dtlpy/utilities/dataset_generators/dataset_generator.py +670 -670
- dtlpy/utilities/dataset_generators/dataset_generator_tensorflow.py +23 -23
- dtlpy/utilities/dataset_generators/dataset_generator_torch.py +21 -21
- dtlpy/utilities/local_development/__init__.py +1 -1
- dtlpy/utilities/local_development/local_session.py +179 -179
- dtlpy/utilities/reports/__init__.py +2 -2
- dtlpy/utilities/reports/figures.py +343 -343
- dtlpy/utilities/reports/report.py +71 -71
- dtlpy/utilities/videos/__init__.py +17 -17
- dtlpy/utilities/videos/video_player.py +598 -598
- dtlpy/utilities/videos/videos.py +470 -470
- {dtlpy-1.115.44.data → dtlpy-1.116.6.data}/scripts/dlp +1 -1
- dtlpy-1.116.6.data/scripts/dlp.bat +2 -0
- {dtlpy-1.115.44.data → dtlpy-1.116.6.data}/scripts/dlp.py +128 -128
- {dtlpy-1.115.44.dist-info → dtlpy-1.116.6.dist-info}/METADATA +186 -186
- dtlpy-1.116.6.dist-info/RECORD +239 -0
- {dtlpy-1.115.44.dist-info → dtlpy-1.116.6.dist-info}/WHEEL +1 -1
- {dtlpy-1.115.44.dist-info → dtlpy-1.116.6.dist-info}/licenses/LICENSE +200 -200
- tests/features/environment.py +551 -551
- dtlpy/assets/__pycache__/__init__.cpython-310.pyc +0 -0
- dtlpy-1.115.44.data/scripts/dlp.bat +0 -2
- dtlpy-1.115.44.dist-info/RECORD +0 -240
- {dtlpy-1.115.44.dist-info → dtlpy-1.116.6.dist-info}/entry_points.txt +0 -0
- {dtlpy-1.115.44.dist-info → dtlpy-1.116.6.dist-info}/top_level.txt +0 -0
dtlpy/repositories/executions.py
CHANGED
|
@@ -1,815 +1,815 @@
|
|
|
1
|
-
import threading
|
|
2
|
-
import logging
|
|
3
|
-
import time
|
|
4
|
-
from copy import deepcopy
|
|
5
|
-
|
|
6
|
-
import numpy as np
|
|
7
|
-
|
|
8
|
-
from .. import exceptions, entities, repositories, miscellaneous, _api_reference
|
|
9
|
-
from ..services.api_client import ApiClient
|
|
10
|
-
|
|
11
|
-
logger = logging.getLogger(name='dtlpy')
|
|
12
|
-
MAX_SLEEP_TIME = 30
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class Executions:
|
|
16
|
-
"""
|
|
17
|
-
Service Executions Repository
|
|
18
|
-
|
|
19
|
-
The Executions class allows the users to manage executions (executions of services) and their properties.
|
|
20
|
-
See our documentation for more information about `executions <https://developers.dataloop.ai/tutorials/faas/execution_control/chapter/>`_.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
def __init__(self,
|
|
24
|
-
client_api: ApiClient,
|
|
25
|
-
service: entities.Service = None,
|
|
26
|
-
project: entities.Project = None):
|
|
27
|
-
self._client_api = client_api
|
|
28
|
-
self._service = service
|
|
29
|
-
self._project = project
|
|
30
|
-
|
|
31
|
-
############
|
|
32
|
-
# entities #
|
|
33
|
-
############
|
|
34
|
-
@property
|
|
35
|
-
def service(self) -> entities.Service:
|
|
36
|
-
if self._service is None:
|
|
37
|
-
raise exceptions.PlatformException(
|
|
38
|
-
error='2001',
|
|
39
|
-
message='Missing "service". need to set a Service entity or use service.executions repository')
|
|
40
|
-
assert isinstance(self._service, entities.Service)
|
|
41
|
-
return self._service
|
|
42
|
-
|
|
43
|
-
@service.setter
|
|
44
|
-
def service(self, service: entities.Service):
|
|
45
|
-
if not isinstance(service, entities.Service):
|
|
46
|
-
raise ValueError('Must input a valid Service entity')
|
|
47
|
-
self._service = service
|
|
48
|
-
|
|
49
|
-
@property
|
|
50
|
-
def project(self) -> entities.Project:
|
|
51
|
-
if self._project is None:
|
|
52
|
-
if self._service is not None:
|
|
53
|
-
self._project = self._service._project
|
|
54
|
-
if self._project is None:
|
|
55
|
-
raise exceptions.PlatformException(
|
|
56
|
-
error='2001',
|
|
57
|
-
message='Missing "project". need to set a Project entity or use Project.executions repository')
|
|
58
|
-
assert isinstance(self._project, entities.Project)
|
|
59
|
-
return self._project
|
|
60
|
-
|
|
61
|
-
@project.setter
|
|
62
|
-
def project(self, project: entities.Project):
|
|
63
|
-
if not isinstance(project, entities.Project):
|
|
64
|
-
raise ValueError('Must input a valid Project entity')
|
|
65
|
-
self._project = project
|
|
66
|
-
|
|
67
|
-
def __get_from_entity(self, name, value):
|
|
68
|
-
project_id = None
|
|
69
|
-
try:
|
|
70
|
-
from_dataset = False
|
|
71
|
-
entity_obj = None
|
|
72
|
-
for input_type in entities.FunctionIO.INPUT_TYPES:
|
|
73
|
-
input_type = input_type.value
|
|
74
|
-
entity = input_type.lower()
|
|
75
|
-
param = '{}_id'.format(entity)
|
|
76
|
-
if isinstance(value, dict):
|
|
77
|
-
if param in value:
|
|
78
|
-
repo = getattr(repositories, '{}s'.format(input_type))(client_api=self._client_api)
|
|
79
|
-
entity_obj = repo.get(**{param: value[param]})
|
|
80
|
-
if param in ['annotation_id', 'item_id']:
|
|
81
|
-
from_dataset = True
|
|
82
|
-
if param == 'item_id':
|
|
83
|
-
entity_obj = entity_obj.dataset
|
|
84
|
-
else:
|
|
85
|
-
entity_obj = repositories.Datasets(client_api=self._client_api).get(
|
|
86
|
-
dataset_id=entity_obj.dataset_id)
|
|
87
|
-
elif param in ['dataset_id']:
|
|
88
|
-
from_dataset = True
|
|
89
|
-
break
|
|
90
|
-
elif isinstance(value, str):
|
|
91
|
-
if entity == name:
|
|
92
|
-
repo = getattr(repositories, '{}s'.format(input_type))(client_api=self._client_api)
|
|
93
|
-
entity_obj = repo.get(**{param: value})
|
|
94
|
-
if name in ['annotation', 'item']:
|
|
95
|
-
from_dataset = True
|
|
96
|
-
if param == 'item_id':
|
|
97
|
-
entity_obj = entity_obj.dataset
|
|
98
|
-
else:
|
|
99
|
-
entity_obj = repositories.Datasets(client_api=self._client_api).get(
|
|
100
|
-
dataset_id=entity_obj.dataset_id)
|
|
101
|
-
elif name in ['dataset']:
|
|
102
|
-
from_dataset = True
|
|
103
|
-
break
|
|
104
|
-
|
|
105
|
-
if entity_obj is not None:
|
|
106
|
-
if isinstance(entity_obj, entities.Project):
|
|
107
|
-
project_id = entity_obj.id
|
|
108
|
-
elif from_dataset:
|
|
109
|
-
project_id = entity_obj.projects[0]
|
|
110
|
-
else:
|
|
111
|
-
project_id = entity_obj.project_id
|
|
112
|
-
except Exception:
|
|
113
|
-
project_id = None
|
|
114
|
-
|
|
115
|
-
return project_id
|
|
116
|
-
|
|
117
|
-
def __get_project_id(self, project_id=None, payload=None):
|
|
118
|
-
if project_id is None:
|
|
119
|
-
inputs = payload.get('input', dict())
|
|
120
|
-
try:
|
|
121
|
-
for key, val in inputs.items():
|
|
122
|
-
project_id = self.__get_from_entity(name=key, value=val)
|
|
123
|
-
if project_id is not None:
|
|
124
|
-
break
|
|
125
|
-
except Exception:
|
|
126
|
-
project_id = None
|
|
127
|
-
|
|
128
|
-
if project_id is None:
|
|
129
|
-
# if still None - get from current repository
|
|
130
|
-
if self._project is not None:
|
|
131
|
-
project_id = self._project.id
|
|
132
|
-
else:
|
|
133
|
-
raise exceptions.PlatformException('400', 'Please provide project_id')
|
|
134
|
-
|
|
135
|
-
return project_id
|
|
136
|
-
|
|
137
|
-
###########
|
|
138
|
-
# methods #
|
|
139
|
-
###########
|
|
140
|
-
@_api_reference.add(path='/executions/{serviceId}', method='post')
|
|
141
|
-
def create(self,
|
|
142
|
-
# executions info
|
|
143
|
-
service_id: str = None,
|
|
144
|
-
execution_input: list = None,
|
|
145
|
-
function_name: str = None,
|
|
146
|
-
# inputs info
|
|
147
|
-
resource: entities.PackageInputType = None,
|
|
148
|
-
item_id: str = None,
|
|
149
|
-
dataset_id: str = None,
|
|
150
|
-
annotation_id: str = None,
|
|
151
|
-
project_id: str = None,
|
|
152
|
-
# execution config
|
|
153
|
-
sync: bool = False,
|
|
154
|
-
stream_logs: bool = False,
|
|
155
|
-
return_output: bool = False,
|
|
156
|
-
# misc
|
|
157
|
-
return_curl_only: bool = False,
|
|
158
|
-
timeout: int = None) -> entities.Execution:
|
|
159
|
-
"""
|
|
160
|
-
Execute a function on an existing service
|
|
161
|
-
|
|
162
|
-
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
163
|
-
|
|
164
|
-
:param str service_id: service id to execute on
|
|
165
|
-
:param List[FunctionIO] or dict execution_input: input dictionary or list of FunctionIO entities
|
|
166
|
-
:param str function_name: function name to run
|
|
167
|
-
:param str resource: input type.
|
|
168
|
-
:param str item_id: optional - item id as input to function
|
|
169
|
-
:param str dataset_id: optional - dataset id as input to function
|
|
170
|
-
:param str annotation_id: optional - annotation id as input to function
|
|
171
|
-
:param str project_id: resource's project
|
|
172
|
-
:param bool sync: if true, wait for function to end
|
|
173
|
-
:param bool stream_logs: prints logs of the new execution. only works with sync=True
|
|
174
|
-
:param bool return_output: if True and sync is True - will return the output directly
|
|
175
|
-
:param bool return_curl_only: return the cURL of the creation WITHOUT actually do it
|
|
176
|
-
:param int timeout: int, seconds to wait until TimeoutError is raised. if <=0 - wait until done -
|
|
177
|
-
by default wait take the service timeout
|
|
178
|
-
:return: execution object
|
|
179
|
-
:rtype: dtlpy.entities.execution.Execution
|
|
180
|
-
|
|
181
|
-
**Example**:
|
|
182
|
-
|
|
183
|
-
.. code-block:: python
|
|
184
|
-
|
|
185
|
-
service.executions.create(function_name='function_name', item_id='item_id', project_id='project_id')
|
|
186
|
-
"""
|
|
187
|
-
if service_id is None:
|
|
188
|
-
if self._service is None:
|
|
189
|
-
raise exceptions.PlatformException('400', 'Please provide service id')
|
|
190
|
-
service_id = self._service.id
|
|
191
|
-
|
|
192
|
-
if resource is None:
|
|
193
|
-
if annotation_id is not None:
|
|
194
|
-
resource = entities.PackageInputType.ANNOTATION
|
|
195
|
-
elif item_id is not None:
|
|
196
|
-
resource = entities.PackageInputType.ITEM
|
|
197
|
-
elif dataset_id is not None:
|
|
198
|
-
resource = entities.PackageInputType.DATASET
|
|
199
|
-
|
|
200
|
-
# payload
|
|
201
|
-
payload = dict()
|
|
202
|
-
if execution_input is None:
|
|
203
|
-
if resource is not None:
|
|
204
|
-
inputs = {resource.lower(): {
|
|
205
|
-
'dataset_id': dataset_id}
|
|
206
|
-
}
|
|
207
|
-
if item_id is not None:
|
|
208
|
-
inputs[resource.lower()]['item_id'] = item_id
|
|
209
|
-
if annotation_id is not None:
|
|
210
|
-
inputs[resource.lower()]['annotation_id'] = annotation_id
|
|
211
|
-
payload['input'] = inputs
|
|
212
|
-
else:
|
|
213
|
-
if isinstance(execution_input, dict):
|
|
214
|
-
payload['input'] = execution_input
|
|
215
|
-
else:
|
|
216
|
-
if not isinstance(execution_input, list):
|
|
217
|
-
execution_input = [execution_input]
|
|
218
|
-
if len(execution_input) > 0 and isinstance(execution_input[0], entities.FunctionIO):
|
|
219
|
-
payload['input'] = dict()
|
|
220
|
-
for single_input in execution_input:
|
|
221
|
-
payload['input'].update(single_input.to_json(resource='execution'))
|
|
222
|
-
else:
|
|
223
|
-
raise exceptions.PlatformException('400', 'Unknown input type')
|
|
224
|
-
|
|
225
|
-
payload['projectId'] = self.__get_project_id(project_id=project_id, payload=payload)
|
|
226
|
-
|
|
227
|
-
if function_name is not None:
|
|
228
|
-
payload['functionName'] = function_name
|
|
229
|
-
else:
|
|
230
|
-
payload['functionName'] = entities.package_defaults.DEFAULT_PACKAGE_FUNCTION_NAME
|
|
231
|
-
|
|
232
|
-
# request url
|
|
233
|
-
url_path = '/executions/{service_id}'.format(service_id=service_id)
|
|
234
|
-
|
|
235
|
-
if return_curl_only:
|
|
236
|
-
curl = self._client_api.export_curl_request(req_type='post',
|
|
237
|
-
path=url_path,
|
|
238
|
-
json_req=payload)
|
|
239
|
-
logger.warning(msg='Execution was NOT created. Exporting cURL only.')
|
|
240
|
-
return curl
|
|
241
|
-
success, response = self._client_api.gen_request(req_type='post',
|
|
242
|
-
path=url_path,
|
|
243
|
-
json_req=payload)
|
|
244
|
-
# exception handling
|
|
245
|
-
if not success:
|
|
246
|
-
raise exceptions.PlatformException(response)
|
|
247
|
-
|
|
248
|
-
# return entity
|
|
249
|
-
execution = entities.Execution.from_json(_json=response.json(),
|
|
250
|
-
client_api=self._client_api,
|
|
251
|
-
project=self._project,
|
|
252
|
-
service=self._service)
|
|
253
|
-
|
|
254
|
-
if sync and not return_output and not stream_logs:
|
|
255
|
-
execution = self.wait(execution_id=execution.id, timeout=timeout)
|
|
256
|
-
|
|
257
|
-
if sync and (stream_logs or return_output):
|
|
258
|
-
thread = None
|
|
259
|
-
if stream_logs:
|
|
260
|
-
thread = threading.Thread(target=self.logs,
|
|
261
|
-
kwargs={'execution_id': execution.id,
|
|
262
|
-
'follow': True,
|
|
263
|
-
'until_completed': True})
|
|
264
|
-
thread.setDaemon(True)
|
|
265
|
-
thread.start()
|
|
266
|
-
execution = self.get(execution_id=execution.id,
|
|
267
|
-
sync=True)
|
|
268
|
-
# stream logs
|
|
269
|
-
if stream_logs and thread is not None:
|
|
270
|
-
thread.join()
|
|
271
|
-
if sync and return_output:
|
|
272
|
-
return execution.output
|
|
273
|
-
return execution
|
|
274
|
-
|
|
275
|
-
@_api_reference.add(path='/executions/{serviceId}', method='post')
|
|
276
|
-
def create_batch(self,
|
|
277
|
-
service_id: str,
|
|
278
|
-
filters,
|
|
279
|
-
function_name: str = None,
|
|
280
|
-
execution_inputs: list = None,
|
|
281
|
-
wait=True
|
|
282
|
-
):
|
|
283
|
-
"""
|
|
284
|
-
Execute a function on an existing service
|
|
285
|
-
|
|
286
|
-
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
287
|
-
|
|
288
|
-
:param str service_id: service id to execute on
|
|
289
|
-
:param filters: Filters entity for a filtering before execute
|
|
290
|
-
:param str function_name: function name to run
|
|
291
|
-
:param List[FunctionIO] or dict execution_inputs: input dictionary or list of FunctionIO entities, that represent the extra inputs of the function
|
|
292
|
-
:param bool wait: wait until create task finish
|
|
293
|
-
:return: execution object
|
|
294
|
-
:rtype: dtlpy.entities.execution.Execution
|
|
295
|
-
|
|
296
|
-
**Example**:
|
|
297
|
-
|
|
298
|
-
.. code-block:: python
|
|
299
|
-
|
|
300
|
-
command = service.executions.create_batch(
|
|
301
|
-
execution_inputs=dl.FunctionIO(type=dl.PackageInputType.STRING, value='test', name='string'),
|
|
302
|
-
filters=dl.Filters(field='dir', values='/test'),
|
|
303
|
-
function_name='run')
|
|
304
|
-
"""
|
|
305
|
-
if service_id is None:
|
|
306
|
-
if self._service is None:
|
|
307
|
-
raise exceptions.PlatformException('400', 'Please provide service id')
|
|
308
|
-
service_id = self._service.id
|
|
309
|
-
|
|
310
|
-
if filters is None:
|
|
311
|
-
raise exceptions.PlatformException('400', 'Please provide filter')
|
|
312
|
-
|
|
313
|
-
if execution_inputs is None:
|
|
314
|
-
execution_inputs = dict()
|
|
315
|
-
|
|
316
|
-
if isinstance(execution_inputs, dict):
|
|
317
|
-
extra_inputs = execution_inputs
|
|
318
|
-
else:
|
|
319
|
-
if not isinstance(execution_inputs, list):
|
|
320
|
-
execution_inputs = [execution_inputs]
|
|
321
|
-
if len(execution_inputs) > 0 and isinstance(execution_inputs[0], entities.FunctionIO):
|
|
322
|
-
extra_inputs = dict()
|
|
323
|
-
for single_input in execution_inputs:
|
|
324
|
-
extra_inputs.update(single_input.to_json(resource='execution'))
|
|
325
|
-
else:
|
|
326
|
-
raise exceptions.PlatformException('400', 'Unknown input type')
|
|
327
|
-
|
|
328
|
-
# payload
|
|
329
|
-
payload = dict()
|
|
330
|
-
payload['batch'] = dict()
|
|
331
|
-
payload['batch']['query'] = filters.prepare()
|
|
332
|
-
payload['batch']['args'] = extra_inputs
|
|
333
|
-
|
|
334
|
-
if function_name is not None:
|
|
335
|
-
payload['functionName'] = function_name
|
|
336
|
-
else:
|
|
337
|
-
payload['functionName'] = entities.package_defaults.DEFAULT_PACKAGE_FUNCTION_NAME
|
|
338
|
-
|
|
339
|
-
# request url
|
|
340
|
-
url_path = '/executions/{service_id}'.format(service_id=service_id)
|
|
341
|
-
|
|
342
|
-
success, response = self._client_api.gen_request(req_type='post',
|
|
343
|
-
path=url_path,
|
|
344
|
-
json_req=payload)
|
|
345
|
-
# exception handling
|
|
346
|
-
if not success:
|
|
347
|
-
raise exceptions.PlatformException(response)
|
|
348
|
-
|
|
349
|
-
response_json = response.json()
|
|
350
|
-
command = entities.Command.from_json(_json=response_json,
|
|
351
|
-
client_api=self._client_api)
|
|
352
|
-
if wait:
|
|
353
|
-
command = command.wait(timeout=0)
|
|
354
|
-
return command
|
|
355
|
-
|
|
356
|
-
@_api_reference.add(path='/executions/rerun', method='post')
|
|
357
|
-
def rerun_batch(self,
|
|
358
|
-
filters,
|
|
359
|
-
service_id: str = None,
|
|
360
|
-
wait=True
|
|
361
|
-
):
|
|
362
|
-
"""
|
|
363
|
-
rerun a executions on an existing service
|
|
364
|
-
|
|
365
|
-
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a Filter.
|
|
366
|
-
|
|
367
|
-
:param filters: Filters entity for a filtering before rerun
|
|
368
|
-
:param str service_id: service id to rerun on
|
|
369
|
-
:param bool wait: wait until create task finish
|
|
370
|
-
:return: rerun command
|
|
371
|
-
:rtype: dtlpy.entities.command.Command
|
|
372
|
-
|
|
373
|
-
**Example**:
|
|
374
|
-
|
|
375
|
-
.. code-block:: python
|
|
376
|
-
|
|
377
|
-
command = service.executions.rerun_batch(
|
|
378
|
-
filters=dl.Filters(field='id', values=['executionId'], operator=dl.FiltersOperations.IN, resource=dl.FiltersResource.EXECUTION))
|
|
379
|
-
"""
|
|
380
|
-
url_path = '/executions/rerun'
|
|
381
|
-
|
|
382
|
-
if filters is None:
|
|
383
|
-
raise exceptions.PlatformException('400', 'Please provide filter')
|
|
384
|
-
|
|
385
|
-
if filters.resource != entities.FiltersResource.EXECUTION:
|
|
386
|
-
raise exceptions.PlatformException(
|
|
387
|
-
error='400',
|
|
388
|
-
message='Filters resource must to be FiltersResource.EXECUTION. Got: {!r}'.format(filters.resource))
|
|
389
|
-
|
|
390
|
-
if service_id is not None and not filters.has_field('serviceId'):
|
|
391
|
-
filters = deepcopy(filters)
|
|
392
|
-
filters.add(field='serviceId', values=service_id, method=entities.FiltersMethod.AND)
|
|
393
|
-
|
|
394
|
-
success, response = self._client_api.gen_request(req_type='post',
|
|
395
|
-
path=url_path,
|
|
396
|
-
json_req={'query': filters.prepare()['filter']})
|
|
397
|
-
# exception handling
|
|
398
|
-
if not success:
|
|
399
|
-
raise exceptions.PlatformException(response)
|
|
400
|
-
|
|
401
|
-
response_json = response.json()
|
|
402
|
-
command = entities.Command.from_json(_json=response_json,
|
|
403
|
-
client_api=self._client_api)
|
|
404
|
-
if wait:
|
|
405
|
-
command = command.wait(timeout=0)
|
|
406
|
-
return command
|
|
407
|
-
|
|
408
|
-
def _list(self, filters: entities.Filters):
|
|
409
|
-
"""
|
|
410
|
-
List service executions
|
|
411
|
-
|
|
412
|
-
:param dtlpy.entities.filters.Filters filters: dl.Filters entity to filters items
|
|
413
|
-
:return:
|
|
414
|
-
"""
|
|
415
|
-
url = '/query/faas'
|
|
416
|
-
|
|
417
|
-
# request
|
|
418
|
-
success, response = self._client_api.gen_request(req_type='POST',
|
|
419
|
-
path=url,
|
|
420
|
-
json_req=filters.prepare())
|
|
421
|
-
if not success:
|
|
422
|
-
raise exceptions.PlatformException(response)
|
|
423
|
-
|
|
424
|
-
return response.json()
|
|
425
|
-
|
|
426
|
-
@_api_reference.add(path='/query/faas', method='post')
|
|
427
|
-
def list(self, filters: entities.Filters = None) -> entities.PagedEntities:
|
|
428
|
-
"""
|
|
429
|
-
List service executions
|
|
430
|
-
|
|
431
|
-
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
432
|
-
|
|
433
|
-
:param dtlpy.entities.filters.Filters filters: dl.Filters entity to filters items
|
|
434
|
-
:return: Paged entity
|
|
435
|
-
:rtype: dtlpy.entities.paged_entities.PagedEntities
|
|
436
|
-
|
|
437
|
-
**Example**:
|
|
438
|
-
|
|
439
|
-
.. code-block:: python
|
|
440
|
-
|
|
441
|
-
service.executions.list()
|
|
442
|
-
"""
|
|
443
|
-
# default filtersf
|
|
444
|
-
if filters is None:
|
|
445
|
-
filters = entities.Filters(resource=entities.FiltersResource.EXECUTION)
|
|
446
|
-
# assert type filters
|
|
447
|
-
elif not isinstance(filters, entities.Filters):
|
|
448
|
-
raise exceptions.PlatformException(
|
|
449
|
-
error='400',
|
|
450
|
-
message='Unknown filters type: {!r}'.format(type(filters)))
|
|
451
|
-
if filters.resource != entities.FiltersResource.EXECUTION:
|
|
452
|
-
raise exceptions.PlatformException(
|
|
453
|
-
error='400',
|
|
454
|
-
message='Filters resource must to be FiltersResource.EXECUTION. Got: {!r}'.format(filters.resource))
|
|
455
|
-
if self._project is not None:
|
|
456
|
-
filters.add(field='projectId', values=self._project.id)
|
|
457
|
-
if self._service is not None:
|
|
458
|
-
filters.add(field='serviceId', values=self._service.id)
|
|
459
|
-
|
|
460
|
-
paged = entities.PagedEntities(items_repository=self,
|
|
461
|
-
filters=filters,
|
|
462
|
-
page_offset=filters.page,
|
|
463
|
-
page_size=filters.page_size,
|
|
464
|
-
client_api=self._client_api)
|
|
465
|
-
paged.get_page()
|
|
466
|
-
return paged
|
|
467
|
-
|
|
468
|
-
def _build_entities_from_response(self, response_items) -> miscellaneous.List[entities.Execution]:
|
|
469
|
-
pool = self._client_api.thread_pools(pool_name='entity.create')
|
|
470
|
-
jobs = [None for _ in range(len(response_items))]
|
|
471
|
-
# return execution list
|
|
472
|
-
for i_item, item in enumerate(response_items):
|
|
473
|
-
jobs[i_item] = pool.submit(entities.Execution._protected_from_json,
|
|
474
|
-
**{'client_api': self._client_api,
|
|
475
|
-
'_json': item,
|
|
476
|
-
'project': self._project,
|
|
477
|
-
'service': self._service})
|
|
478
|
-
|
|
479
|
-
# get results
|
|
480
|
-
results = [j.result() for j in jobs]
|
|
481
|
-
# log errors
|
|
482
|
-
_ = [logger.warning(r[1]) for r in results if r[0] is False]
|
|
483
|
-
# return good jobs
|
|
484
|
-
return miscellaneous.List([r[1] for r in results if r[0] is True])
|
|
485
|
-
|
|
486
|
-
@_api_reference.add(path='/executions/{id}', method='get')
|
|
487
|
-
def get(self,
|
|
488
|
-
execution_id: str = None,
|
|
489
|
-
sync: bool = False
|
|
490
|
-
) -> entities.Execution:
|
|
491
|
-
"""
|
|
492
|
-
Get Service execution object
|
|
493
|
-
|
|
494
|
-
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
495
|
-
|
|
496
|
-
:param str execution_id: execution id
|
|
497
|
-
:param bool sync: if true, wait for the execution to finish
|
|
498
|
-
:return: Service execution object
|
|
499
|
-
:rtype: dtlpy.entities.execution.Execution
|
|
500
|
-
|
|
501
|
-
**Example**:
|
|
502
|
-
|
|
503
|
-
.. code-block:: python
|
|
504
|
-
|
|
505
|
-
service.executions.get(execution_id='execution_id')
|
|
506
|
-
"""
|
|
507
|
-
url_path = "/executions/{}".format(execution_id)
|
|
508
|
-
if sync:
|
|
509
|
-
return self.wait(execution_id=execution_id)
|
|
510
|
-
|
|
511
|
-
success, response = self._client_api.gen_request(req_type="get",
|
|
512
|
-
path=url_path)
|
|
513
|
-
|
|
514
|
-
# exception handling
|
|
515
|
-
if not success:
|
|
516
|
-
raise exceptions.PlatformException(response)
|
|
517
|
-
|
|
518
|
-
# return entity
|
|
519
|
-
return entities.Execution.from_json(client_api=self._client_api,
|
|
520
|
-
_json=response.json(),
|
|
521
|
-
project=self._project,
|
|
522
|
-
service=self._service)
|
|
523
|
-
|
|
524
|
-
def logs(self,
|
|
525
|
-
execution_id: str,
|
|
526
|
-
follow: bool = True,
|
|
527
|
-
until_completed: bool = True):
|
|
528
|
-
"""
|
|
529
|
-
executions logs
|
|
530
|
-
|
|
531
|
-
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
532
|
-
|
|
533
|
-
:param str execution_id: execution id
|
|
534
|
-
:param bool follow: if true, keep stream future logs
|
|
535
|
-
:param bool until_completed: if true, wait until completed
|
|
536
|
-
:return: executions logs
|
|
537
|
-
|
|
538
|
-
**Example**:
|
|
539
|
-
|
|
540
|
-
.. code-block:: python
|
|
541
|
-
|
|
542
|
-
service.executions.logs(execution_id='execution_id')
|
|
543
|
-
"""
|
|
544
|
-
return self.service.log(execution_id=execution_id,
|
|
545
|
-
follow=follow,
|
|
546
|
-
until_completed=until_completed,
|
|
547
|
-
view=True)
|
|
548
|
-
|
|
549
|
-
def increment(self, execution: entities.Execution):
|
|
550
|
-
"""
|
|
551
|
-
Increment the number of attempts that an execution is allowed to attempt to run a service that is not responding.
|
|
552
|
-
|
|
553
|
-
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
554
|
-
|
|
555
|
-
:param dtlpy.entities.execution.Execution execution:
|
|
556
|
-
:return: int
|
|
557
|
-
:rtype: int
|
|
558
|
-
|
|
559
|
-
**Example**:
|
|
560
|
-
|
|
561
|
-
.. code-block:: python
|
|
562
|
-
|
|
563
|
-
service.executions.increment(execution='execution_entity')
|
|
564
|
-
"""
|
|
565
|
-
# request
|
|
566
|
-
success, response = self._client_api.gen_request(
|
|
567
|
-
req_type='post',
|
|
568
|
-
path='/executions/{}/attempts'.format(execution.id)
|
|
569
|
-
)
|
|
570
|
-
|
|
571
|
-
# exception handling
|
|
572
|
-
if not success:
|
|
573
|
-
raise exceptions.PlatformException(response)
|
|
574
|
-
|
|
575
|
-
# exception handling
|
|
576
|
-
if not success:
|
|
577
|
-
raise exceptions.PlatformException(response)
|
|
578
|
-
else:
|
|
579
|
-
return response.json()
|
|
580
|
-
|
|
581
|
-
@_api_reference.add(path='/executions/{executionId}/rerun', method='post')
|
|
582
|
-
def rerun(self, execution: entities.Execution, sync: bool = False):
|
|
583
|
-
"""
|
|
584
|
-
Rerun execution
|
|
585
|
-
|
|
586
|
-
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
587
|
-
|
|
588
|
-
:param dtlpy.entities.execution.Execution execution:
|
|
589
|
-
:param bool sync: wait for the execution to finish
|
|
590
|
-
:return: Execution object
|
|
591
|
-
:rtype: dtlpy.entities.execution.Execution
|
|
592
|
-
|
|
593
|
-
**Example**:
|
|
594
|
-
|
|
595
|
-
.. code-block:: python
|
|
596
|
-
|
|
597
|
-
service.executions.rerun(execution='execution_entity')
|
|
598
|
-
"""
|
|
599
|
-
|
|
600
|
-
url_path = "/executions/{}/rerun".format(execution.id)
|
|
601
|
-
# request
|
|
602
|
-
success, response = self._client_api.gen_request(req_type='post',
|
|
603
|
-
path=url_path)
|
|
604
|
-
|
|
605
|
-
# exception handling
|
|
606
|
-
if not success:
|
|
607
|
-
raise exceptions.PlatformException(response)
|
|
608
|
-
else:
|
|
609
|
-
execution = entities.Execution.from_json(
|
|
610
|
-
client_api=self._client_api,
|
|
611
|
-
_json=response.json(),
|
|
612
|
-
project=self._project,
|
|
613
|
-
service=self._service
|
|
614
|
-
)
|
|
615
|
-
if sync:
|
|
616
|
-
execution = self.wait(execution_id=execution.id)
|
|
617
|
-
return execution
|
|
618
|
-
|
|
619
|
-
def wait(self,
|
|
620
|
-
execution_id: str = None,
|
|
621
|
-
execution: entities.Execution = None,
|
|
622
|
-
timeout: int = None,
|
|
623
|
-
backoff_factor=1):
|
|
624
|
-
"""
|
|
625
|
-
Get Service execution object.
|
|
626
|
-
|
|
627
|
-
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
628
|
-
|
|
629
|
-
:param str execution_id: execution id
|
|
630
|
-
:param str execution: dl.Execution, optional. must input one of execution or execution_id
|
|
631
|
-
:param int timeout: seconds to wait until TimeoutError is raised. if <=0 - wait until done - by default wait take the service timeout
|
|
632
|
-
:param float backoff_factor: A backoff factor to apply between attempts after the second try
|
|
633
|
-
:return: Service execution object
|
|
634
|
-
:rtype: dtlpy.entities.execution.Execution
|
|
635
|
-
|
|
636
|
-
**Example**:
|
|
637
|
-
|
|
638
|
-
.. code-block:: python
|
|
639
|
-
|
|
640
|
-
service.executions.wait(execution_id='execution_id')
|
|
641
|
-
"""
|
|
642
|
-
if execution is None:
|
|
643
|
-
if execution_id is None:
|
|
644
|
-
raise ValueError('Must input at least one: [execution, execution_id]')
|
|
645
|
-
else:
|
|
646
|
-
execution = self.get(execution_id=execution_id)
|
|
647
|
-
elapsed = 0
|
|
648
|
-
start = int(time.time())
|
|
649
|
-
if timeout is None or timeout <= 0:
|
|
650
|
-
timeout = np.inf
|
|
651
|
-
|
|
652
|
-
num_tries = 1
|
|
653
|
-
while elapsed < timeout:
|
|
654
|
-
execution = self.get(execution_id=execution.id)
|
|
655
|
-
if not execution.in_progress():
|
|
656
|
-
break
|
|
657
|
-
elapsed = time.time() - start
|
|
658
|
-
if elapsed >= timeout:
|
|
659
|
-
raise TimeoutError(
|
|
660
|
-
f"execution wait() got timeout. id: {execution.id!r}, status: {execution.latest_status}")
|
|
661
|
-
sleep_time = np.min([timeout - elapsed, backoff_factor * (2 ** num_tries), MAX_SLEEP_TIME])
|
|
662
|
-
num_tries += 1
|
|
663
|
-
logger.debug(f"Execution {execution.id} is running for {elapsed:.2f}[s]. Sleeping for {sleep_time:.2f}[s]")
|
|
664
|
-
time.sleep(sleep_time)
|
|
665
|
-
|
|
666
|
-
return execution
|
|
667
|
-
|
|
668
|
-
@_api_reference.add(path='/executions/{id}/terminate', method='post')
|
|
669
|
-
def terminate(self, execution: entities.Execution):
|
|
670
|
-
"""
|
|
671
|
-
Terminate Execution
|
|
672
|
-
|
|
673
|
-
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
674
|
-
|
|
675
|
-
:param dtlpy.entities.execution.Execution execution:
|
|
676
|
-
:return: execution object
|
|
677
|
-
:rtype: dtlpy.entities.execution.Execution
|
|
678
|
-
|
|
679
|
-
**Example**:
|
|
680
|
-
|
|
681
|
-
.. code-block:: python
|
|
682
|
-
|
|
683
|
-
service.executions.terminate(execution='execution_entity')
|
|
684
|
-
"""
|
|
685
|
-
# request
|
|
686
|
-
success, response = self._client_api.gen_request(req_type='post',
|
|
687
|
-
path='/executions/{}/terminate'.format(execution.id))
|
|
688
|
-
|
|
689
|
-
# exception handling
|
|
690
|
-
if not success:
|
|
691
|
-
raise exceptions.PlatformException(response)
|
|
692
|
-
else:
|
|
693
|
-
return entities.Execution.from_json(_json=response.json(),
|
|
694
|
-
service=self._service,
|
|
695
|
-
project=self._project,
|
|
696
|
-
client_api=self._client_api)
|
|
697
|
-
|
|
698
|
-
@_api_reference.add(path='/executions/{id}', method='patch')
|
|
699
|
-
def update(self, execution: entities.Execution) -> entities.Execution:
|
|
700
|
-
"""
|
|
701
|
-
Update execution changes to platform
|
|
702
|
-
|
|
703
|
-
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
704
|
-
|
|
705
|
-
:param dtlpy.entities.execution.Execution execution: execution entity
|
|
706
|
-
:return: Service execution object
|
|
707
|
-
:rtype: dtlpy.entities.execution.Execution
|
|
708
|
-
|
|
709
|
-
**Example**:
|
|
710
|
-
|
|
711
|
-
.. code-block:: python
|
|
712
|
-
|
|
713
|
-
service.executions.update(execution='execution_entity')
|
|
714
|
-
"""
|
|
715
|
-
# payload
|
|
716
|
-
payload = execution.to_json()
|
|
717
|
-
|
|
718
|
-
# request
|
|
719
|
-
success, response = self._client_api.gen_request(req_type='patch',
|
|
720
|
-
path='/executions/{}'.format(execution.id),
|
|
721
|
-
json_req=payload)
|
|
722
|
-
|
|
723
|
-
# exception handling
|
|
724
|
-
if not success:
|
|
725
|
-
raise exceptions.PlatformException(response)
|
|
726
|
-
|
|
727
|
-
# return entity
|
|
728
|
-
if self._project is not None:
|
|
729
|
-
project = self._project
|
|
730
|
-
else:
|
|
731
|
-
project = execution._project
|
|
732
|
-
|
|
733
|
-
# return
|
|
734
|
-
if self._service is not None:
|
|
735
|
-
service = self._service
|
|
736
|
-
else:
|
|
737
|
-
service = execution._service
|
|
738
|
-
|
|
739
|
-
return entities.Execution.from_json(_json=response.json(),
|
|
740
|
-
service=service,
|
|
741
|
-
project=self._project,
|
|
742
|
-
client_api=self._client_api)
|
|
743
|
-
|
|
744
|
-
def progress_update(
|
|
745
|
-
self,
|
|
746
|
-
execution_id: str,
|
|
747
|
-
status: entities.ExecutionStatus = None,
|
|
748
|
-
percent_complete: int = None,
|
|
749
|
-
message: str = None,
|
|
750
|
-
output: str = None,
|
|
751
|
-
service_version: str = None
|
|
752
|
-
):
|
|
753
|
-
"""
|
|
754
|
-
Update Execution Progress.
|
|
755
|
-
|
|
756
|
-
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
757
|
-
|
|
758
|
-
:param str execution_id: execution id
|
|
759
|
-
:param str status: ExecutionStatus
|
|
760
|
-
:param int percent_complete: percent work done
|
|
761
|
-
:param str message: message
|
|
762
|
-
:param str output: the output of the execution
|
|
763
|
-
:param str service_version: service version
|
|
764
|
-
:return: Service execution object
|
|
765
|
-
:rtype: dtlpy.entities.execution.Execution
|
|
766
|
-
|
|
767
|
-
**Example**:
|
|
768
|
-
|
|
769
|
-
.. code-block:: python
|
|
770
|
-
|
|
771
|
-
service.executions.progress_update(execution_id='execution_id', status='complete', percent_complete=100)
|
|
772
|
-
"""
|
|
773
|
-
# create payload
|
|
774
|
-
payload = dict()
|
|
775
|
-
|
|
776
|
-
if status is not None:
|
|
777
|
-
payload['status'] = status
|
|
778
|
-
else:
|
|
779
|
-
if percent_complete is not None and isinstance(percent_complete, int):
|
|
780
|
-
if percent_complete < 100:
|
|
781
|
-
payload['status'] = 'inProgress'
|
|
782
|
-
else:
|
|
783
|
-
payload['status'] = 'completed'
|
|
784
|
-
elif output is not None:
|
|
785
|
-
payload['status'] = 'completed'
|
|
786
|
-
else:
|
|
787
|
-
payload['status'] = 'inProgress'
|
|
788
|
-
|
|
789
|
-
if percent_complete is not None:
|
|
790
|
-
payload['percentComplete'] = percent_complete
|
|
791
|
-
|
|
792
|
-
if message is not None:
|
|
793
|
-
payload['message'] = message
|
|
794
|
-
|
|
795
|
-
if output is not None:
|
|
796
|
-
payload['output'] = output
|
|
797
|
-
|
|
798
|
-
if service_version is not None:
|
|
799
|
-
payload['serviceVersion'] = service_version
|
|
800
|
-
|
|
801
|
-
# request
|
|
802
|
-
success, response = self._client_api.gen_request(
|
|
803
|
-
req_type="post",
|
|
804
|
-
path="/executions/{}/progress".format(execution_id),
|
|
805
|
-
json_req=payload
|
|
806
|
-
)
|
|
807
|
-
|
|
808
|
-
# exception handling
|
|
809
|
-
if success:
|
|
810
|
-
return entities.Execution.from_json(_json=response.json(),
|
|
811
|
-
client_api=self._client_api,
|
|
812
|
-
project=self._project,
|
|
813
|
-
service=self._service)
|
|
814
|
-
else:
|
|
815
|
-
raise exceptions.PlatformException(response)
|
|
1
|
+
import threading
|
|
2
|
+
import logging
|
|
3
|
+
import time
|
|
4
|
+
from copy import deepcopy
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from .. import exceptions, entities, repositories, miscellaneous, _api_reference
|
|
9
|
+
from ..services.api_client import ApiClient
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(name='dtlpy')
|
|
12
|
+
MAX_SLEEP_TIME = 30
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Executions:
|
|
16
|
+
"""
|
|
17
|
+
Service Executions Repository
|
|
18
|
+
|
|
19
|
+
The Executions class allows the users to manage executions (executions of services) and their properties.
|
|
20
|
+
See our documentation for more information about `executions <https://developers.dataloop.ai/tutorials/faas/execution_control/chapter/>`_.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self,
|
|
24
|
+
client_api: ApiClient,
|
|
25
|
+
service: entities.Service = None,
|
|
26
|
+
project: entities.Project = None):
|
|
27
|
+
self._client_api = client_api
|
|
28
|
+
self._service = service
|
|
29
|
+
self._project = project
|
|
30
|
+
|
|
31
|
+
############
|
|
32
|
+
# entities #
|
|
33
|
+
############
|
|
34
|
+
@property
|
|
35
|
+
def service(self) -> entities.Service:
|
|
36
|
+
if self._service is None:
|
|
37
|
+
raise exceptions.PlatformException(
|
|
38
|
+
error='2001',
|
|
39
|
+
message='Missing "service". need to set a Service entity or use service.executions repository')
|
|
40
|
+
assert isinstance(self._service, entities.Service)
|
|
41
|
+
return self._service
|
|
42
|
+
|
|
43
|
+
@service.setter
|
|
44
|
+
def service(self, service: entities.Service):
|
|
45
|
+
if not isinstance(service, entities.Service):
|
|
46
|
+
raise ValueError('Must input a valid Service entity')
|
|
47
|
+
self._service = service
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def project(self) -> entities.Project:
|
|
51
|
+
if self._project is None:
|
|
52
|
+
if self._service is not None:
|
|
53
|
+
self._project = self._service._project
|
|
54
|
+
if self._project is None:
|
|
55
|
+
raise exceptions.PlatformException(
|
|
56
|
+
error='2001',
|
|
57
|
+
message='Missing "project". need to set a Project entity or use Project.executions repository')
|
|
58
|
+
assert isinstance(self._project, entities.Project)
|
|
59
|
+
return self._project
|
|
60
|
+
|
|
61
|
+
@project.setter
|
|
62
|
+
def project(self, project: entities.Project):
|
|
63
|
+
if not isinstance(project, entities.Project):
|
|
64
|
+
raise ValueError('Must input a valid Project entity')
|
|
65
|
+
self._project = project
|
|
66
|
+
|
|
67
|
+
def __get_from_entity(self, name, value):
|
|
68
|
+
project_id = None
|
|
69
|
+
try:
|
|
70
|
+
from_dataset = False
|
|
71
|
+
entity_obj = None
|
|
72
|
+
for input_type in entities.FunctionIO.INPUT_TYPES:
|
|
73
|
+
input_type = input_type.value
|
|
74
|
+
entity = input_type.lower()
|
|
75
|
+
param = '{}_id'.format(entity)
|
|
76
|
+
if isinstance(value, dict):
|
|
77
|
+
if param in value:
|
|
78
|
+
repo = getattr(repositories, '{}s'.format(input_type))(client_api=self._client_api)
|
|
79
|
+
entity_obj = repo.get(**{param: value[param]})
|
|
80
|
+
if param in ['annotation_id', 'item_id']:
|
|
81
|
+
from_dataset = True
|
|
82
|
+
if param == 'item_id':
|
|
83
|
+
entity_obj = entity_obj.dataset
|
|
84
|
+
else:
|
|
85
|
+
entity_obj = repositories.Datasets(client_api=self._client_api).get(
|
|
86
|
+
dataset_id=entity_obj.dataset_id)
|
|
87
|
+
elif param in ['dataset_id']:
|
|
88
|
+
from_dataset = True
|
|
89
|
+
break
|
|
90
|
+
elif isinstance(value, str):
|
|
91
|
+
if entity == name:
|
|
92
|
+
repo = getattr(repositories, '{}s'.format(input_type))(client_api=self._client_api)
|
|
93
|
+
entity_obj = repo.get(**{param: value})
|
|
94
|
+
if name in ['annotation', 'item']:
|
|
95
|
+
from_dataset = True
|
|
96
|
+
if param == 'item_id':
|
|
97
|
+
entity_obj = entity_obj.dataset
|
|
98
|
+
else:
|
|
99
|
+
entity_obj = repositories.Datasets(client_api=self._client_api).get(
|
|
100
|
+
dataset_id=entity_obj.dataset_id)
|
|
101
|
+
elif name in ['dataset']:
|
|
102
|
+
from_dataset = True
|
|
103
|
+
break
|
|
104
|
+
|
|
105
|
+
if entity_obj is not None:
|
|
106
|
+
if isinstance(entity_obj, entities.Project):
|
|
107
|
+
project_id = entity_obj.id
|
|
108
|
+
elif from_dataset:
|
|
109
|
+
project_id = entity_obj.projects[0]
|
|
110
|
+
else:
|
|
111
|
+
project_id = entity_obj.project_id
|
|
112
|
+
except Exception:
|
|
113
|
+
project_id = None
|
|
114
|
+
|
|
115
|
+
return project_id
|
|
116
|
+
|
|
117
|
+
def __get_project_id(self, project_id=None, payload=None):
|
|
118
|
+
if project_id is None:
|
|
119
|
+
inputs = payload.get('input', dict())
|
|
120
|
+
try:
|
|
121
|
+
for key, val in inputs.items():
|
|
122
|
+
project_id = self.__get_from_entity(name=key, value=val)
|
|
123
|
+
if project_id is not None:
|
|
124
|
+
break
|
|
125
|
+
except Exception:
|
|
126
|
+
project_id = None
|
|
127
|
+
|
|
128
|
+
if project_id is None:
|
|
129
|
+
# if still None - get from current repository
|
|
130
|
+
if self._project is not None:
|
|
131
|
+
project_id = self._project.id
|
|
132
|
+
else:
|
|
133
|
+
raise exceptions.PlatformException('400', 'Please provide project_id')
|
|
134
|
+
|
|
135
|
+
return project_id
|
|
136
|
+
|
|
137
|
+
###########
|
|
138
|
+
# methods #
|
|
139
|
+
###########
|
|
140
|
+
@_api_reference.add(path='/executions/{serviceId}', method='post')
|
|
141
|
+
def create(self,
|
|
142
|
+
# executions info
|
|
143
|
+
service_id: str = None,
|
|
144
|
+
execution_input: list = None,
|
|
145
|
+
function_name: str = None,
|
|
146
|
+
# inputs info
|
|
147
|
+
resource: entities.PackageInputType = None,
|
|
148
|
+
item_id: str = None,
|
|
149
|
+
dataset_id: str = None,
|
|
150
|
+
annotation_id: str = None,
|
|
151
|
+
project_id: str = None,
|
|
152
|
+
# execution config
|
|
153
|
+
sync: bool = False,
|
|
154
|
+
stream_logs: bool = False,
|
|
155
|
+
return_output: bool = False,
|
|
156
|
+
# misc
|
|
157
|
+
return_curl_only: bool = False,
|
|
158
|
+
timeout: int = None) -> entities.Execution:
|
|
159
|
+
"""
|
|
160
|
+
Execute a function on an existing service
|
|
161
|
+
|
|
162
|
+
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
163
|
+
|
|
164
|
+
:param str service_id: service id to execute on
|
|
165
|
+
:param List[FunctionIO] or dict execution_input: input dictionary or list of FunctionIO entities
|
|
166
|
+
:param str function_name: function name to run
|
|
167
|
+
:param str resource: input type.
|
|
168
|
+
:param str item_id: optional - item id as input to function
|
|
169
|
+
:param str dataset_id: optional - dataset id as input to function
|
|
170
|
+
:param str annotation_id: optional - annotation id as input to function
|
|
171
|
+
:param str project_id: resource's project
|
|
172
|
+
:param bool sync: if true, wait for function to end
|
|
173
|
+
:param bool stream_logs: prints logs of the new execution. only works with sync=True
|
|
174
|
+
:param bool return_output: if True and sync is True - will return the output directly
|
|
175
|
+
:param bool return_curl_only: return the cURL of the creation WITHOUT actually do it
|
|
176
|
+
:param int timeout: int, seconds to wait until TimeoutError is raised. if <=0 - wait until done -
|
|
177
|
+
by default wait take the service timeout
|
|
178
|
+
:return: execution object
|
|
179
|
+
:rtype: dtlpy.entities.execution.Execution
|
|
180
|
+
|
|
181
|
+
**Example**:
|
|
182
|
+
|
|
183
|
+
.. code-block:: python
|
|
184
|
+
|
|
185
|
+
service.executions.create(function_name='function_name', item_id='item_id', project_id='project_id')
|
|
186
|
+
"""
|
|
187
|
+
if service_id is None:
|
|
188
|
+
if self._service is None:
|
|
189
|
+
raise exceptions.PlatformException('400', 'Please provide service id')
|
|
190
|
+
service_id = self._service.id
|
|
191
|
+
|
|
192
|
+
if resource is None:
|
|
193
|
+
if annotation_id is not None:
|
|
194
|
+
resource = entities.PackageInputType.ANNOTATION
|
|
195
|
+
elif item_id is not None:
|
|
196
|
+
resource = entities.PackageInputType.ITEM
|
|
197
|
+
elif dataset_id is not None:
|
|
198
|
+
resource = entities.PackageInputType.DATASET
|
|
199
|
+
|
|
200
|
+
# payload
|
|
201
|
+
payload = dict()
|
|
202
|
+
if execution_input is None:
|
|
203
|
+
if resource is not None:
|
|
204
|
+
inputs = {resource.lower(): {
|
|
205
|
+
'dataset_id': dataset_id}
|
|
206
|
+
}
|
|
207
|
+
if item_id is not None:
|
|
208
|
+
inputs[resource.lower()]['item_id'] = item_id
|
|
209
|
+
if annotation_id is not None:
|
|
210
|
+
inputs[resource.lower()]['annotation_id'] = annotation_id
|
|
211
|
+
payload['input'] = inputs
|
|
212
|
+
else:
|
|
213
|
+
if isinstance(execution_input, dict):
|
|
214
|
+
payload['input'] = execution_input
|
|
215
|
+
else:
|
|
216
|
+
if not isinstance(execution_input, list):
|
|
217
|
+
execution_input = [execution_input]
|
|
218
|
+
if len(execution_input) > 0 and isinstance(execution_input[0], entities.FunctionIO):
|
|
219
|
+
payload['input'] = dict()
|
|
220
|
+
for single_input in execution_input:
|
|
221
|
+
payload['input'].update(single_input.to_json(resource='execution'))
|
|
222
|
+
else:
|
|
223
|
+
raise exceptions.PlatformException('400', 'Unknown input type')
|
|
224
|
+
|
|
225
|
+
payload['projectId'] = self.__get_project_id(project_id=project_id, payload=payload)
|
|
226
|
+
|
|
227
|
+
if function_name is not None:
|
|
228
|
+
payload['functionName'] = function_name
|
|
229
|
+
else:
|
|
230
|
+
payload['functionName'] = entities.package_defaults.DEFAULT_PACKAGE_FUNCTION_NAME
|
|
231
|
+
|
|
232
|
+
# request url
|
|
233
|
+
url_path = '/executions/{service_id}'.format(service_id=service_id)
|
|
234
|
+
|
|
235
|
+
if return_curl_only:
|
|
236
|
+
curl = self._client_api.export_curl_request(req_type='post',
|
|
237
|
+
path=url_path,
|
|
238
|
+
json_req=payload)
|
|
239
|
+
logger.warning(msg='Execution was NOT created. Exporting cURL only.')
|
|
240
|
+
return curl
|
|
241
|
+
success, response = self._client_api.gen_request(req_type='post',
|
|
242
|
+
path=url_path,
|
|
243
|
+
json_req=payload)
|
|
244
|
+
# exception handling
|
|
245
|
+
if not success:
|
|
246
|
+
raise exceptions.PlatformException(response)
|
|
247
|
+
|
|
248
|
+
# return entity
|
|
249
|
+
execution = entities.Execution.from_json(_json=response.json(),
|
|
250
|
+
client_api=self._client_api,
|
|
251
|
+
project=self._project,
|
|
252
|
+
service=self._service)
|
|
253
|
+
|
|
254
|
+
if sync and not return_output and not stream_logs:
|
|
255
|
+
execution = self.wait(execution_id=execution.id, timeout=timeout)
|
|
256
|
+
|
|
257
|
+
if sync and (stream_logs or return_output):
|
|
258
|
+
thread = None
|
|
259
|
+
if stream_logs:
|
|
260
|
+
thread = threading.Thread(target=self.logs,
|
|
261
|
+
kwargs={'execution_id': execution.id,
|
|
262
|
+
'follow': True,
|
|
263
|
+
'until_completed': True})
|
|
264
|
+
thread.setDaemon(True)
|
|
265
|
+
thread.start()
|
|
266
|
+
execution = self.get(execution_id=execution.id,
|
|
267
|
+
sync=True)
|
|
268
|
+
# stream logs
|
|
269
|
+
if stream_logs and thread is not None:
|
|
270
|
+
thread.join()
|
|
271
|
+
if sync and return_output:
|
|
272
|
+
return execution.output
|
|
273
|
+
return execution
|
|
274
|
+
|
|
275
|
+
@_api_reference.add(path='/executions/{serviceId}', method='post')
|
|
276
|
+
def create_batch(self,
|
|
277
|
+
service_id: str,
|
|
278
|
+
filters,
|
|
279
|
+
function_name: str = None,
|
|
280
|
+
execution_inputs: list = None,
|
|
281
|
+
wait=True
|
|
282
|
+
):
|
|
283
|
+
"""
|
|
284
|
+
Execute a function on an existing service
|
|
285
|
+
|
|
286
|
+
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
287
|
+
|
|
288
|
+
:param str service_id: service id to execute on
|
|
289
|
+
:param filters: Filters entity for a filtering before execute
|
|
290
|
+
:param str function_name: function name to run
|
|
291
|
+
:param List[FunctionIO] or dict execution_inputs: input dictionary or list of FunctionIO entities, that represent the extra inputs of the function
|
|
292
|
+
:param bool wait: wait until create task finish
|
|
293
|
+
:return: execution object
|
|
294
|
+
:rtype: dtlpy.entities.execution.Execution
|
|
295
|
+
|
|
296
|
+
**Example**:
|
|
297
|
+
|
|
298
|
+
.. code-block:: python
|
|
299
|
+
|
|
300
|
+
command = service.executions.create_batch(
|
|
301
|
+
execution_inputs=dl.FunctionIO(type=dl.PackageInputType.STRING, value='test', name='string'),
|
|
302
|
+
filters=dl.Filters(field='dir', values='/test'),
|
|
303
|
+
function_name='run')
|
|
304
|
+
"""
|
|
305
|
+
if service_id is None:
|
|
306
|
+
if self._service is None:
|
|
307
|
+
raise exceptions.PlatformException('400', 'Please provide service id')
|
|
308
|
+
service_id = self._service.id
|
|
309
|
+
|
|
310
|
+
if filters is None:
|
|
311
|
+
raise exceptions.PlatformException('400', 'Please provide filter')
|
|
312
|
+
|
|
313
|
+
if execution_inputs is None:
|
|
314
|
+
execution_inputs = dict()
|
|
315
|
+
|
|
316
|
+
if isinstance(execution_inputs, dict):
|
|
317
|
+
extra_inputs = execution_inputs
|
|
318
|
+
else:
|
|
319
|
+
if not isinstance(execution_inputs, list):
|
|
320
|
+
execution_inputs = [execution_inputs]
|
|
321
|
+
if len(execution_inputs) > 0 and isinstance(execution_inputs[0], entities.FunctionIO):
|
|
322
|
+
extra_inputs = dict()
|
|
323
|
+
for single_input in execution_inputs:
|
|
324
|
+
extra_inputs.update(single_input.to_json(resource='execution'))
|
|
325
|
+
else:
|
|
326
|
+
raise exceptions.PlatformException('400', 'Unknown input type')
|
|
327
|
+
|
|
328
|
+
# payload
|
|
329
|
+
payload = dict()
|
|
330
|
+
payload['batch'] = dict()
|
|
331
|
+
payload['batch']['query'] = filters.prepare()
|
|
332
|
+
payload['batch']['args'] = extra_inputs
|
|
333
|
+
|
|
334
|
+
if function_name is not None:
|
|
335
|
+
payload['functionName'] = function_name
|
|
336
|
+
else:
|
|
337
|
+
payload['functionName'] = entities.package_defaults.DEFAULT_PACKAGE_FUNCTION_NAME
|
|
338
|
+
|
|
339
|
+
# request url
|
|
340
|
+
url_path = '/executions/{service_id}'.format(service_id=service_id)
|
|
341
|
+
|
|
342
|
+
success, response = self._client_api.gen_request(req_type='post',
|
|
343
|
+
path=url_path,
|
|
344
|
+
json_req=payload)
|
|
345
|
+
# exception handling
|
|
346
|
+
if not success:
|
|
347
|
+
raise exceptions.PlatformException(response)
|
|
348
|
+
|
|
349
|
+
response_json = response.json()
|
|
350
|
+
command = entities.Command.from_json(_json=response_json,
|
|
351
|
+
client_api=self._client_api)
|
|
352
|
+
if wait:
|
|
353
|
+
command = command.wait(timeout=0)
|
|
354
|
+
return command
|
|
355
|
+
|
|
356
|
+
@_api_reference.add(path='/executions/rerun', method='post')
|
|
357
|
+
def rerun_batch(self,
|
|
358
|
+
filters,
|
|
359
|
+
service_id: str = None,
|
|
360
|
+
wait=True
|
|
361
|
+
):
|
|
362
|
+
"""
|
|
363
|
+
rerun a executions on an existing service
|
|
364
|
+
|
|
365
|
+
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a Filter.
|
|
366
|
+
|
|
367
|
+
:param filters: Filters entity for a filtering before rerun
|
|
368
|
+
:param str service_id: service id to rerun on
|
|
369
|
+
:param bool wait: wait until create task finish
|
|
370
|
+
:return: rerun command
|
|
371
|
+
:rtype: dtlpy.entities.command.Command
|
|
372
|
+
|
|
373
|
+
**Example**:
|
|
374
|
+
|
|
375
|
+
.. code-block:: python
|
|
376
|
+
|
|
377
|
+
command = service.executions.rerun_batch(
|
|
378
|
+
filters=dl.Filters(field='id', values=['executionId'], operator=dl.FiltersOperations.IN, resource=dl.FiltersResource.EXECUTION))
|
|
379
|
+
"""
|
|
380
|
+
url_path = '/executions/rerun'
|
|
381
|
+
|
|
382
|
+
if filters is None:
|
|
383
|
+
raise exceptions.PlatformException('400', 'Please provide filter')
|
|
384
|
+
|
|
385
|
+
if filters.resource != entities.FiltersResource.EXECUTION:
|
|
386
|
+
raise exceptions.PlatformException(
|
|
387
|
+
error='400',
|
|
388
|
+
message='Filters resource must to be FiltersResource.EXECUTION. Got: {!r}'.format(filters.resource))
|
|
389
|
+
|
|
390
|
+
if service_id is not None and not filters.has_field('serviceId'):
|
|
391
|
+
filters = deepcopy(filters)
|
|
392
|
+
filters.add(field='serviceId', values=service_id, method=entities.FiltersMethod.AND)
|
|
393
|
+
|
|
394
|
+
success, response = self._client_api.gen_request(req_type='post',
|
|
395
|
+
path=url_path,
|
|
396
|
+
json_req={'query': filters.prepare()['filter']})
|
|
397
|
+
# exception handling
|
|
398
|
+
if not success:
|
|
399
|
+
raise exceptions.PlatformException(response)
|
|
400
|
+
|
|
401
|
+
response_json = response.json()
|
|
402
|
+
command = entities.Command.from_json(_json=response_json,
|
|
403
|
+
client_api=self._client_api)
|
|
404
|
+
if wait:
|
|
405
|
+
command = command.wait(timeout=0)
|
|
406
|
+
return command
|
|
407
|
+
|
|
408
|
+
def _list(self, filters: entities.Filters):
|
|
409
|
+
"""
|
|
410
|
+
List service executions
|
|
411
|
+
|
|
412
|
+
:param dtlpy.entities.filters.Filters filters: dl.Filters entity to filters items
|
|
413
|
+
:return:
|
|
414
|
+
"""
|
|
415
|
+
url = '/query/faas'
|
|
416
|
+
|
|
417
|
+
# request
|
|
418
|
+
success, response = self._client_api.gen_request(req_type='POST',
|
|
419
|
+
path=url,
|
|
420
|
+
json_req=filters.prepare())
|
|
421
|
+
if not success:
|
|
422
|
+
raise exceptions.PlatformException(response)
|
|
423
|
+
|
|
424
|
+
return response.json()
|
|
425
|
+
|
|
426
|
+
@_api_reference.add(path='/query/faas', method='post')
|
|
427
|
+
def list(self, filters: entities.Filters = None) -> entities.PagedEntities:
|
|
428
|
+
"""
|
|
429
|
+
List service executions
|
|
430
|
+
|
|
431
|
+
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
432
|
+
|
|
433
|
+
:param dtlpy.entities.filters.Filters filters: dl.Filters entity to filters items
|
|
434
|
+
:return: Paged entity
|
|
435
|
+
:rtype: dtlpy.entities.paged_entities.PagedEntities
|
|
436
|
+
|
|
437
|
+
**Example**:
|
|
438
|
+
|
|
439
|
+
.. code-block:: python
|
|
440
|
+
|
|
441
|
+
service.executions.list()
|
|
442
|
+
"""
|
|
443
|
+
# default filtersf
|
|
444
|
+
if filters is None:
|
|
445
|
+
filters = entities.Filters(resource=entities.FiltersResource.EXECUTION)
|
|
446
|
+
# assert type filters
|
|
447
|
+
elif not isinstance(filters, entities.Filters):
|
|
448
|
+
raise exceptions.PlatformException(
|
|
449
|
+
error='400',
|
|
450
|
+
message='Unknown filters type: {!r}'.format(type(filters)))
|
|
451
|
+
if filters.resource != entities.FiltersResource.EXECUTION:
|
|
452
|
+
raise exceptions.PlatformException(
|
|
453
|
+
error='400',
|
|
454
|
+
message='Filters resource must to be FiltersResource.EXECUTION. Got: {!r}'.format(filters.resource))
|
|
455
|
+
if self._project is not None:
|
|
456
|
+
filters.add(field='projectId', values=self._project.id)
|
|
457
|
+
if self._service is not None:
|
|
458
|
+
filters.add(field='serviceId', values=self._service.id)
|
|
459
|
+
|
|
460
|
+
paged = entities.PagedEntities(items_repository=self,
|
|
461
|
+
filters=filters,
|
|
462
|
+
page_offset=filters.page,
|
|
463
|
+
page_size=filters.page_size,
|
|
464
|
+
client_api=self._client_api)
|
|
465
|
+
paged.get_page()
|
|
466
|
+
return paged
|
|
467
|
+
|
|
468
|
+
def _build_entities_from_response(self, response_items) -> miscellaneous.List[entities.Execution]:
|
|
469
|
+
pool = self._client_api.thread_pools(pool_name='entity.create')
|
|
470
|
+
jobs = [None for _ in range(len(response_items))]
|
|
471
|
+
# return execution list
|
|
472
|
+
for i_item, item in enumerate(response_items):
|
|
473
|
+
jobs[i_item] = pool.submit(entities.Execution._protected_from_json,
|
|
474
|
+
**{'client_api': self._client_api,
|
|
475
|
+
'_json': item,
|
|
476
|
+
'project': self._project,
|
|
477
|
+
'service': self._service})
|
|
478
|
+
|
|
479
|
+
# get results
|
|
480
|
+
results = [j.result() for j in jobs]
|
|
481
|
+
# log errors
|
|
482
|
+
_ = [logger.warning(r[1]) for r in results if r[0] is False]
|
|
483
|
+
# return good jobs
|
|
484
|
+
return miscellaneous.List([r[1] for r in results if r[0] is True])
|
|
485
|
+
|
|
486
|
+
@_api_reference.add(path='/executions/{id}', method='get')
|
|
487
|
+
def get(self,
|
|
488
|
+
execution_id: str = None,
|
|
489
|
+
sync: bool = False
|
|
490
|
+
) -> entities.Execution:
|
|
491
|
+
"""
|
|
492
|
+
Get Service execution object
|
|
493
|
+
|
|
494
|
+
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
495
|
+
|
|
496
|
+
:param str execution_id: execution id
|
|
497
|
+
:param bool sync: if true, wait for the execution to finish
|
|
498
|
+
:return: Service execution object
|
|
499
|
+
:rtype: dtlpy.entities.execution.Execution
|
|
500
|
+
|
|
501
|
+
**Example**:
|
|
502
|
+
|
|
503
|
+
.. code-block:: python
|
|
504
|
+
|
|
505
|
+
service.executions.get(execution_id='execution_id')
|
|
506
|
+
"""
|
|
507
|
+
url_path = "/executions/{}".format(execution_id)
|
|
508
|
+
if sync:
|
|
509
|
+
return self.wait(execution_id=execution_id)
|
|
510
|
+
|
|
511
|
+
success, response = self._client_api.gen_request(req_type="get",
|
|
512
|
+
path=url_path)
|
|
513
|
+
|
|
514
|
+
# exception handling
|
|
515
|
+
if not success:
|
|
516
|
+
raise exceptions.PlatformException(response)
|
|
517
|
+
|
|
518
|
+
# return entity
|
|
519
|
+
return entities.Execution.from_json(client_api=self._client_api,
|
|
520
|
+
_json=response.json(),
|
|
521
|
+
project=self._project,
|
|
522
|
+
service=self._service)
|
|
523
|
+
|
|
524
|
+
def logs(self,
|
|
525
|
+
execution_id: str,
|
|
526
|
+
follow: bool = True,
|
|
527
|
+
until_completed: bool = True):
|
|
528
|
+
"""
|
|
529
|
+
executions logs
|
|
530
|
+
|
|
531
|
+
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
532
|
+
|
|
533
|
+
:param str execution_id: execution id
|
|
534
|
+
:param bool follow: if true, keep stream future logs
|
|
535
|
+
:param bool until_completed: if true, wait until completed
|
|
536
|
+
:return: executions logs
|
|
537
|
+
|
|
538
|
+
**Example**:
|
|
539
|
+
|
|
540
|
+
.. code-block:: python
|
|
541
|
+
|
|
542
|
+
service.executions.logs(execution_id='execution_id')
|
|
543
|
+
"""
|
|
544
|
+
return self.service.log(execution_id=execution_id,
|
|
545
|
+
follow=follow,
|
|
546
|
+
until_completed=until_completed,
|
|
547
|
+
view=True)
|
|
548
|
+
|
|
549
|
+
def increment(self, execution: entities.Execution):
|
|
550
|
+
"""
|
|
551
|
+
Increment the number of attempts that an execution is allowed to attempt to run a service that is not responding.
|
|
552
|
+
|
|
553
|
+
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
554
|
+
|
|
555
|
+
:param dtlpy.entities.execution.Execution execution:
|
|
556
|
+
:return: int
|
|
557
|
+
:rtype: int
|
|
558
|
+
|
|
559
|
+
**Example**:
|
|
560
|
+
|
|
561
|
+
.. code-block:: python
|
|
562
|
+
|
|
563
|
+
service.executions.increment(execution='execution_entity')
|
|
564
|
+
"""
|
|
565
|
+
# request
|
|
566
|
+
success, response = self._client_api.gen_request(
|
|
567
|
+
req_type='post',
|
|
568
|
+
path='/executions/{}/attempts'.format(execution.id)
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
# exception handling
|
|
572
|
+
if not success:
|
|
573
|
+
raise exceptions.PlatformException(response)
|
|
574
|
+
|
|
575
|
+
# exception handling
|
|
576
|
+
if not success:
|
|
577
|
+
raise exceptions.PlatformException(response)
|
|
578
|
+
else:
|
|
579
|
+
return response.json()
|
|
580
|
+
|
|
581
|
+
@_api_reference.add(path='/executions/{executionId}/rerun', method='post')
|
|
582
|
+
def rerun(self, execution: entities.Execution, sync: bool = False):
|
|
583
|
+
"""
|
|
584
|
+
Rerun execution
|
|
585
|
+
|
|
586
|
+
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
587
|
+
|
|
588
|
+
:param dtlpy.entities.execution.Execution execution:
|
|
589
|
+
:param bool sync: wait for the execution to finish
|
|
590
|
+
:return: Execution object
|
|
591
|
+
:rtype: dtlpy.entities.execution.Execution
|
|
592
|
+
|
|
593
|
+
**Example**:
|
|
594
|
+
|
|
595
|
+
.. code-block:: python
|
|
596
|
+
|
|
597
|
+
service.executions.rerun(execution='execution_entity')
|
|
598
|
+
"""
|
|
599
|
+
|
|
600
|
+
url_path = "/executions/{}/rerun".format(execution.id)
|
|
601
|
+
# request
|
|
602
|
+
success, response = self._client_api.gen_request(req_type='post',
|
|
603
|
+
path=url_path)
|
|
604
|
+
|
|
605
|
+
# exception handling
|
|
606
|
+
if not success:
|
|
607
|
+
raise exceptions.PlatformException(response)
|
|
608
|
+
else:
|
|
609
|
+
execution = entities.Execution.from_json(
|
|
610
|
+
client_api=self._client_api,
|
|
611
|
+
_json=response.json(),
|
|
612
|
+
project=self._project,
|
|
613
|
+
service=self._service
|
|
614
|
+
)
|
|
615
|
+
if sync:
|
|
616
|
+
execution = self.wait(execution_id=execution.id)
|
|
617
|
+
return execution
|
|
618
|
+
|
|
619
|
+
def wait(self,
|
|
620
|
+
execution_id: str = None,
|
|
621
|
+
execution: entities.Execution = None,
|
|
622
|
+
timeout: int = None,
|
|
623
|
+
backoff_factor=1):
|
|
624
|
+
"""
|
|
625
|
+
Get Service execution object.
|
|
626
|
+
|
|
627
|
+
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
628
|
+
|
|
629
|
+
:param str execution_id: execution id
|
|
630
|
+
:param str execution: dl.Execution, optional. must input one of execution or execution_id
|
|
631
|
+
:param int timeout: seconds to wait until TimeoutError is raised. if <=0 - wait until done - by default wait take the service timeout
|
|
632
|
+
:param float backoff_factor: A backoff factor to apply between attempts after the second try
|
|
633
|
+
:return: Service execution object
|
|
634
|
+
:rtype: dtlpy.entities.execution.Execution
|
|
635
|
+
|
|
636
|
+
**Example**:
|
|
637
|
+
|
|
638
|
+
.. code-block:: python
|
|
639
|
+
|
|
640
|
+
service.executions.wait(execution_id='execution_id')
|
|
641
|
+
"""
|
|
642
|
+
if execution is None:
|
|
643
|
+
if execution_id is None:
|
|
644
|
+
raise ValueError('Must input at least one: [execution, execution_id]')
|
|
645
|
+
else:
|
|
646
|
+
execution = self.get(execution_id=execution_id)
|
|
647
|
+
elapsed = 0
|
|
648
|
+
start = int(time.time())
|
|
649
|
+
if timeout is None or timeout <= 0:
|
|
650
|
+
timeout = np.inf
|
|
651
|
+
|
|
652
|
+
num_tries = 1
|
|
653
|
+
while elapsed < timeout:
|
|
654
|
+
execution = self.get(execution_id=execution.id)
|
|
655
|
+
if not execution.in_progress():
|
|
656
|
+
break
|
|
657
|
+
elapsed = time.time() - start
|
|
658
|
+
if elapsed >= timeout:
|
|
659
|
+
raise TimeoutError(
|
|
660
|
+
f"execution wait() got timeout. id: {execution.id!r}, status: {execution.latest_status}")
|
|
661
|
+
sleep_time = np.min([timeout - elapsed, backoff_factor * (2 ** num_tries), MAX_SLEEP_TIME])
|
|
662
|
+
num_tries += 1
|
|
663
|
+
logger.debug(f"Execution {execution.id} is running for {elapsed:.2f}[s]. Sleeping for {sleep_time:.2f}[s]")
|
|
664
|
+
time.sleep(sleep_time)
|
|
665
|
+
|
|
666
|
+
return execution
|
|
667
|
+
|
|
668
|
+
@_api_reference.add(path='/executions/{id}/terminate', method='post')
|
|
669
|
+
def terminate(self, execution: entities.Execution):
|
|
670
|
+
"""
|
|
671
|
+
Terminate Execution
|
|
672
|
+
|
|
673
|
+
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
674
|
+
|
|
675
|
+
:param dtlpy.entities.execution.Execution execution:
|
|
676
|
+
:return: execution object
|
|
677
|
+
:rtype: dtlpy.entities.execution.Execution
|
|
678
|
+
|
|
679
|
+
**Example**:
|
|
680
|
+
|
|
681
|
+
.. code-block:: python
|
|
682
|
+
|
|
683
|
+
service.executions.terminate(execution='execution_entity')
|
|
684
|
+
"""
|
|
685
|
+
# request
|
|
686
|
+
success, response = self._client_api.gen_request(req_type='post',
|
|
687
|
+
path='/executions/{}/terminate'.format(execution.id))
|
|
688
|
+
|
|
689
|
+
# exception handling
|
|
690
|
+
if not success:
|
|
691
|
+
raise exceptions.PlatformException(response)
|
|
692
|
+
else:
|
|
693
|
+
return entities.Execution.from_json(_json=response.json(),
|
|
694
|
+
service=self._service,
|
|
695
|
+
project=self._project,
|
|
696
|
+
client_api=self._client_api)
|
|
697
|
+
|
|
698
|
+
@_api_reference.add(path='/executions/{id}', method='patch')
|
|
699
|
+
def update(self, execution: entities.Execution) -> entities.Execution:
|
|
700
|
+
"""
|
|
701
|
+
Update execution changes to platform
|
|
702
|
+
|
|
703
|
+
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
704
|
+
|
|
705
|
+
:param dtlpy.entities.execution.Execution execution: execution entity
|
|
706
|
+
:return: Service execution object
|
|
707
|
+
:rtype: dtlpy.entities.execution.Execution
|
|
708
|
+
|
|
709
|
+
**Example**:
|
|
710
|
+
|
|
711
|
+
.. code-block:: python
|
|
712
|
+
|
|
713
|
+
service.executions.update(execution='execution_entity')
|
|
714
|
+
"""
|
|
715
|
+
# payload
|
|
716
|
+
payload = execution.to_json()
|
|
717
|
+
|
|
718
|
+
# request
|
|
719
|
+
success, response = self._client_api.gen_request(req_type='patch',
|
|
720
|
+
path='/executions/{}'.format(execution.id),
|
|
721
|
+
json_req=payload)
|
|
722
|
+
|
|
723
|
+
# exception handling
|
|
724
|
+
if not success:
|
|
725
|
+
raise exceptions.PlatformException(response)
|
|
726
|
+
|
|
727
|
+
# return entity
|
|
728
|
+
if self._project is not None:
|
|
729
|
+
project = self._project
|
|
730
|
+
else:
|
|
731
|
+
project = execution._project
|
|
732
|
+
|
|
733
|
+
# return
|
|
734
|
+
if self._service is not None:
|
|
735
|
+
service = self._service
|
|
736
|
+
else:
|
|
737
|
+
service = execution._service
|
|
738
|
+
|
|
739
|
+
return entities.Execution.from_json(_json=response.json(),
|
|
740
|
+
service=service,
|
|
741
|
+
project=self._project,
|
|
742
|
+
client_api=self._client_api)
|
|
743
|
+
|
|
744
|
+
def progress_update(
|
|
745
|
+
self,
|
|
746
|
+
execution_id: str,
|
|
747
|
+
status: entities.ExecutionStatus = None,
|
|
748
|
+
percent_complete: int = None,
|
|
749
|
+
message: str = None,
|
|
750
|
+
output: str = None,
|
|
751
|
+
service_version: str = None
|
|
752
|
+
):
|
|
753
|
+
"""
|
|
754
|
+
Update Execution Progress.
|
|
755
|
+
|
|
756
|
+
**Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a service.
|
|
757
|
+
|
|
758
|
+
:param str execution_id: execution id
|
|
759
|
+
:param str status: ExecutionStatus
|
|
760
|
+
:param int percent_complete: percent work done
|
|
761
|
+
:param str message: message
|
|
762
|
+
:param str output: the output of the execution
|
|
763
|
+
:param str service_version: service version
|
|
764
|
+
:return: Service execution object
|
|
765
|
+
:rtype: dtlpy.entities.execution.Execution
|
|
766
|
+
|
|
767
|
+
**Example**:
|
|
768
|
+
|
|
769
|
+
.. code-block:: python
|
|
770
|
+
|
|
771
|
+
service.executions.progress_update(execution_id='execution_id', status='complete', percent_complete=100)
|
|
772
|
+
"""
|
|
773
|
+
# create payload
|
|
774
|
+
payload = dict()
|
|
775
|
+
|
|
776
|
+
if status is not None:
|
|
777
|
+
payload['status'] = status
|
|
778
|
+
else:
|
|
779
|
+
if percent_complete is not None and isinstance(percent_complete, int):
|
|
780
|
+
if percent_complete < 100:
|
|
781
|
+
payload['status'] = 'inProgress'
|
|
782
|
+
else:
|
|
783
|
+
payload['status'] = 'completed'
|
|
784
|
+
elif output is not None:
|
|
785
|
+
payload['status'] = 'completed'
|
|
786
|
+
else:
|
|
787
|
+
payload['status'] = 'inProgress'
|
|
788
|
+
|
|
789
|
+
if percent_complete is not None:
|
|
790
|
+
payload['percentComplete'] = percent_complete
|
|
791
|
+
|
|
792
|
+
if message is not None:
|
|
793
|
+
payload['message'] = message
|
|
794
|
+
|
|
795
|
+
if output is not None:
|
|
796
|
+
payload['output'] = output
|
|
797
|
+
|
|
798
|
+
if service_version is not None:
|
|
799
|
+
payload['serviceVersion'] = service_version
|
|
800
|
+
|
|
801
|
+
# request
|
|
802
|
+
success, response = self._client_api.gen_request(
|
|
803
|
+
req_type="post",
|
|
804
|
+
path="/executions/{}/progress".format(execution_id),
|
|
805
|
+
json_req=payload
|
|
806
|
+
)
|
|
807
|
+
|
|
808
|
+
# exception handling
|
|
809
|
+
if success:
|
|
810
|
+
return entities.Execution.from_json(_json=response.json(),
|
|
811
|
+
client_api=self._client_api,
|
|
812
|
+
project=self._project,
|
|
813
|
+
service=self._service)
|
|
814
|
+
else:
|
|
815
|
+
raise exceptions.PlatformException(response)
|