dtlpy 1.115.44__py3-none-any.whl → 1.117.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.
Files changed (238) hide show
  1. dtlpy/__init__.py +491 -491
  2. dtlpy/__version__.py +1 -1
  3. dtlpy/assets/__init__.py +26 -26
  4. dtlpy/assets/code_server/config.yaml +2 -2
  5. dtlpy/assets/code_server/installation.sh +24 -24
  6. dtlpy/assets/code_server/launch.json +13 -13
  7. dtlpy/assets/code_server/settings.json +2 -2
  8. dtlpy/assets/main.py +53 -53
  9. dtlpy/assets/main_partial.py +18 -18
  10. dtlpy/assets/mock.json +11 -11
  11. dtlpy/assets/model_adapter.py +83 -83
  12. dtlpy/assets/package.json +61 -61
  13. dtlpy/assets/package_catalog.json +29 -29
  14. dtlpy/assets/package_gitignore +307 -307
  15. dtlpy/assets/service_runners/__init__.py +33 -33
  16. dtlpy/assets/service_runners/converter.py +96 -96
  17. dtlpy/assets/service_runners/multi_method.py +49 -49
  18. dtlpy/assets/service_runners/multi_method_annotation.py +54 -54
  19. dtlpy/assets/service_runners/multi_method_dataset.py +55 -55
  20. dtlpy/assets/service_runners/multi_method_item.py +52 -52
  21. dtlpy/assets/service_runners/multi_method_json.py +52 -52
  22. dtlpy/assets/service_runners/single_method.py +37 -37
  23. dtlpy/assets/service_runners/single_method_annotation.py +43 -43
  24. dtlpy/assets/service_runners/single_method_dataset.py +43 -43
  25. dtlpy/assets/service_runners/single_method_item.py +41 -41
  26. dtlpy/assets/service_runners/single_method_json.py +42 -42
  27. dtlpy/assets/service_runners/single_method_multi_input.py +45 -45
  28. dtlpy/assets/voc_annotation_template.xml +23 -23
  29. dtlpy/caches/base_cache.py +32 -32
  30. dtlpy/caches/cache.py +473 -473
  31. dtlpy/caches/dl_cache.py +201 -201
  32. dtlpy/caches/filesystem_cache.py +89 -89
  33. dtlpy/caches/redis_cache.py +84 -84
  34. dtlpy/dlp/__init__.py +20 -20
  35. dtlpy/dlp/cli_utilities.py +367 -367
  36. dtlpy/dlp/command_executor.py +764 -764
  37. dtlpy/dlp/dlp +1 -1
  38. dtlpy/dlp/dlp.bat +1 -1
  39. dtlpy/dlp/dlp.py +128 -128
  40. dtlpy/dlp/parser.py +651 -651
  41. dtlpy/entities/__init__.py +83 -83
  42. dtlpy/entities/analytic.py +347 -347
  43. dtlpy/entities/annotation.py +1879 -1879
  44. dtlpy/entities/annotation_collection.py +699 -699
  45. dtlpy/entities/annotation_definitions/__init__.py +20 -20
  46. dtlpy/entities/annotation_definitions/base_annotation_definition.py +100 -100
  47. dtlpy/entities/annotation_definitions/box.py +195 -195
  48. dtlpy/entities/annotation_definitions/classification.py +67 -67
  49. dtlpy/entities/annotation_definitions/comparison.py +72 -72
  50. dtlpy/entities/annotation_definitions/cube.py +204 -204
  51. dtlpy/entities/annotation_definitions/cube_3d.py +149 -149
  52. dtlpy/entities/annotation_definitions/description.py +32 -32
  53. dtlpy/entities/annotation_definitions/ellipse.py +124 -124
  54. dtlpy/entities/annotation_definitions/free_text.py +62 -62
  55. dtlpy/entities/annotation_definitions/gis.py +69 -69
  56. dtlpy/entities/annotation_definitions/note.py +139 -139
  57. dtlpy/entities/annotation_definitions/point.py +117 -117
  58. dtlpy/entities/annotation_definitions/polygon.py +182 -182
  59. dtlpy/entities/annotation_definitions/polyline.py +111 -111
  60. dtlpy/entities/annotation_definitions/pose.py +92 -92
  61. dtlpy/entities/annotation_definitions/ref_image.py +86 -86
  62. dtlpy/entities/annotation_definitions/segmentation.py +240 -240
  63. dtlpy/entities/annotation_definitions/subtitle.py +34 -34
  64. dtlpy/entities/annotation_definitions/text.py +85 -85
  65. dtlpy/entities/annotation_definitions/undefined_annotation.py +74 -74
  66. dtlpy/entities/app.py +220 -220
  67. dtlpy/entities/app_module.py +107 -107
  68. dtlpy/entities/artifact.py +174 -174
  69. dtlpy/entities/assignment.py +399 -399
  70. dtlpy/entities/base_entity.py +214 -214
  71. dtlpy/entities/bot.py +113 -113
  72. dtlpy/entities/codebase.py +292 -292
  73. dtlpy/entities/collection.py +38 -38
  74. dtlpy/entities/command.py +169 -169
  75. dtlpy/entities/compute.py +449 -449
  76. dtlpy/entities/dataset.py +1299 -1299
  77. dtlpy/entities/directory_tree.py +44 -44
  78. dtlpy/entities/dpk.py +470 -470
  79. dtlpy/entities/driver.py +235 -235
  80. dtlpy/entities/execution.py +397 -397
  81. dtlpy/entities/feature.py +124 -124
  82. dtlpy/entities/feature_set.py +152 -145
  83. dtlpy/entities/filters.py +798 -798
  84. dtlpy/entities/gis_item.py +107 -107
  85. dtlpy/entities/integration.py +184 -184
  86. dtlpy/entities/item.py +975 -959
  87. dtlpy/entities/label.py +123 -123
  88. dtlpy/entities/links.py +85 -85
  89. dtlpy/entities/message.py +175 -175
  90. dtlpy/entities/model.py +684 -684
  91. dtlpy/entities/node.py +1005 -1005
  92. dtlpy/entities/ontology.py +810 -803
  93. dtlpy/entities/organization.py +287 -287
  94. dtlpy/entities/package.py +657 -657
  95. dtlpy/entities/package_defaults.py +5 -5
  96. dtlpy/entities/package_function.py +185 -185
  97. dtlpy/entities/package_module.py +113 -113
  98. dtlpy/entities/package_slot.py +118 -118
  99. dtlpy/entities/paged_entities.py +299 -299
  100. dtlpy/entities/pipeline.py +624 -624
  101. dtlpy/entities/pipeline_execution.py +279 -279
  102. dtlpy/entities/project.py +394 -394
  103. dtlpy/entities/prompt_item.py +505 -505
  104. dtlpy/entities/recipe.py +301 -301
  105. dtlpy/entities/reflect_dict.py +102 -102
  106. dtlpy/entities/resource_execution.py +138 -138
  107. dtlpy/entities/service.py +974 -963
  108. dtlpy/entities/service_driver.py +117 -117
  109. dtlpy/entities/setting.py +294 -294
  110. dtlpy/entities/task.py +495 -495
  111. dtlpy/entities/time_series.py +143 -143
  112. dtlpy/entities/trigger.py +426 -426
  113. dtlpy/entities/user.py +118 -118
  114. dtlpy/entities/webhook.py +124 -124
  115. dtlpy/examples/__init__.py +19 -19
  116. dtlpy/examples/add_labels.py +135 -135
  117. dtlpy/examples/add_metadata_to_item.py +21 -21
  118. dtlpy/examples/annotate_items_using_model.py +65 -65
  119. dtlpy/examples/annotate_video_using_model_and_tracker.py +75 -75
  120. dtlpy/examples/annotations_convert_to_voc.py +9 -9
  121. dtlpy/examples/annotations_convert_to_yolo.py +9 -9
  122. dtlpy/examples/convert_annotation_types.py +51 -51
  123. dtlpy/examples/converter.py +143 -143
  124. dtlpy/examples/copy_annotations.py +22 -22
  125. dtlpy/examples/copy_folder.py +31 -31
  126. dtlpy/examples/create_annotations.py +51 -51
  127. dtlpy/examples/create_video_annotations.py +83 -83
  128. dtlpy/examples/delete_annotations.py +26 -26
  129. dtlpy/examples/filters.py +113 -113
  130. dtlpy/examples/move_item.py +23 -23
  131. dtlpy/examples/play_video_annotation.py +13 -13
  132. dtlpy/examples/show_item_and_mask.py +53 -53
  133. dtlpy/examples/triggers.py +49 -49
  134. dtlpy/examples/upload_batch_of_items.py +20 -20
  135. dtlpy/examples/upload_items_and_custom_format_annotations.py +55 -55
  136. dtlpy/examples/upload_items_with_modalities.py +43 -43
  137. dtlpy/examples/upload_segmentation_annotations_from_mask_image.py +44 -44
  138. dtlpy/examples/upload_yolo_format_annotations.py +70 -70
  139. dtlpy/exceptions.py +125 -125
  140. dtlpy/miscellaneous/__init__.py +20 -20
  141. dtlpy/miscellaneous/dict_differ.py +95 -95
  142. dtlpy/miscellaneous/git_utils.py +217 -217
  143. dtlpy/miscellaneous/json_utils.py +14 -14
  144. dtlpy/miscellaneous/list_print.py +105 -105
  145. dtlpy/miscellaneous/zipping.py +130 -130
  146. dtlpy/ml/__init__.py +20 -20
  147. dtlpy/ml/base_feature_extractor_adapter.py +27 -27
  148. dtlpy/ml/base_model_adapter.py +1287 -1230
  149. dtlpy/ml/metrics.py +461 -461
  150. dtlpy/ml/predictions_utils.py +274 -274
  151. dtlpy/ml/summary_writer.py +57 -57
  152. dtlpy/ml/train_utils.py +60 -60
  153. dtlpy/new_instance.py +252 -252
  154. dtlpy/repositories/__init__.py +56 -56
  155. dtlpy/repositories/analytics.py +85 -85
  156. dtlpy/repositories/annotations.py +916 -916
  157. dtlpy/repositories/apps.py +383 -383
  158. dtlpy/repositories/artifacts.py +452 -452
  159. dtlpy/repositories/assignments.py +599 -599
  160. dtlpy/repositories/bots.py +213 -213
  161. dtlpy/repositories/codebases.py +559 -559
  162. dtlpy/repositories/collections.py +332 -332
  163. dtlpy/repositories/commands.py +152 -152
  164. dtlpy/repositories/compositions.py +61 -61
  165. dtlpy/repositories/computes.py +439 -439
  166. dtlpy/repositories/datasets.py +1585 -1504
  167. dtlpy/repositories/downloader.py +1157 -923
  168. dtlpy/repositories/dpks.py +433 -433
  169. dtlpy/repositories/drivers.py +482 -482
  170. dtlpy/repositories/executions.py +815 -815
  171. dtlpy/repositories/feature_sets.py +256 -226
  172. dtlpy/repositories/features.py +255 -255
  173. dtlpy/repositories/integrations.py +484 -484
  174. dtlpy/repositories/items.py +912 -912
  175. dtlpy/repositories/messages.py +94 -94
  176. dtlpy/repositories/models.py +1000 -1000
  177. dtlpy/repositories/nodes.py +80 -80
  178. dtlpy/repositories/ontologies.py +511 -511
  179. dtlpy/repositories/organizations.py +525 -525
  180. dtlpy/repositories/packages.py +1941 -1941
  181. dtlpy/repositories/pipeline_executions.py +451 -451
  182. dtlpy/repositories/pipelines.py +640 -640
  183. dtlpy/repositories/projects.py +539 -539
  184. dtlpy/repositories/recipes.py +429 -399
  185. dtlpy/repositories/resource_executions.py +137 -137
  186. dtlpy/repositories/schema.py +120 -120
  187. dtlpy/repositories/service_drivers.py +213 -213
  188. dtlpy/repositories/services.py +1704 -1704
  189. dtlpy/repositories/settings.py +339 -339
  190. dtlpy/repositories/tasks.py +1477 -1477
  191. dtlpy/repositories/times_series.py +278 -278
  192. dtlpy/repositories/triggers.py +536 -536
  193. dtlpy/repositories/upload_element.py +257 -257
  194. dtlpy/repositories/uploader.py +661 -661
  195. dtlpy/repositories/webhooks.py +249 -249
  196. dtlpy/services/__init__.py +22 -22
  197. dtlpy/services/aihttp_retry.py +131 -131
  198. dtlpy/services/api_client.py +1786 -1785
  199. dtlpy/services/api_reference.py +40 -40
  200. dtlpy/services/async_utils.py +133 -133
  201. dtlpy/services/calls_counter.py +44 -44
  202. dtlpy/services/check_sdk.py +68 -68
  203. dtlpy/services/cookie.py +115 -115
  204. dtlpy/services/create_logger.py +156 -156
  205. dtlpy/services/events.py +84 -84
  206. dtlpy/services/logins.py +235 -235
  207. dtlpy/services/reporter.py +256 -256
  208. dtlpy/services/service_defaults.py +91 -91
  209. dtlpy/utilities/__init__.py +20 -20
  210. dtlpy/utilities/annotations/__init__.py +16 -16
  211. dtlpy/utilities/annotations/annotation_converters.py +269 -269
  212. dtlpy/utilities/base_package_runner.py +285 -264
  213. dtlpy/utilities/converter.py +1650 -1650
  214. dtlpy/utilities/dataset_generators/__init__.py +1 -1
  215. dtlpy/utilities/dataset_generators/dataset_generator.py +670 -670
  216. dtlpy/utilities/dataset_generators/dataset_generator_tensorflow.py +23 -23
  217. dtlpy/utilities/dataset_generators/dataset_generator_torch.py +21 -21
  218. dtlpy/utilities/local_development/__init__.py +1 -1
  219. dtlpy/utilities/local_development/local_session.py +179 -179
  220. dtlpy/utilities/reports/__init__.py +2 -2
  221. dtlpy/utilities/reports/figures.py +343 -343
  222. dtlpy/utilities/reports/report.py +71 -71
  223. dtlpy/utilities/videos/__init__.py +17 -17
  224. dtlpy/utilities/videos/video_player.py +598 -598
  225. dtlpy/utilities/videos/videos.py +470 -470
  226. {dtlpy-1.115.44.data → dtlpy-1.117.6.data}/scripts/dlp +1 -1
  227. dtlpy-1.117.6.data/scripts/dlp.bat +2 -0
  228. {dtlpy-1.115.44.data → dtlpy-1.117.6.data}/scripts/dlp.py +128 -128
  229. {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/METADATA +186 -186
  230. dtlpy-1.117.6.dist-info/RECORD +239 -0
  231. {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/WHEEL +1 -1
  232. {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/licenses/LICENSE +200 -200
  233. tests/features/environment.py +551 -551
  234. dtlpy/assets/__pycache__/__init__.cpython-310.pyc +0 -0
  235. dtlpy-1.115.44.data/scripts/dlp.bat +0 -2
  236. dtlpy-1.115.44.dist-info/RECORD +0 -240
  237. {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/entry_points.txt +0 -0
  238. {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/top_level.txt +0 -0
dtlpy/entities/package.py CHANGED
@@ -1,657 +1,657 @@
1
- from collections import namedtuple
2
- from typing import Union
3
- from enum import Enum
4
- import traceback
5
- import logging
6
- import inspect
7
- import typing
8
- import json
9
- import os
10
-
11
- from .package_module import PackageModule
12
- from .package_slot import PackageSlot
13
- from .. import repositories, entities, exceptions
14
- from ..services.api_client import ApiClient
15
-
16
- logger = logging.getLogger(name='dtlpy')
17
-
18
-
19
- class RequirementOperator(str, Enum):
20
- EQUAL = '==',
21
- GREATER_THAN = '>',
22
- LESS_THAN = '<',
23
- EQUAL_OR_LESS_THAN = '<=',
24
- EQUAL_OR_GREATER_THAN = '>='
25
-
26
- @staticmethod
27
- def keys():
28
- return [key.value for key in list(RequirementOperator)]
29
-
30
-
31
- class PackageRequirement:
32
-
33
- def __init__(self, name: str, version: str = None, operator: str = None):
34
- self.name = name
35
- self.version = version
36
-
37
- valid_operators = RequirementOperator.keys()
38
- if operator is not None and operator not in valid_operators:
39
- raise Exception('Illegal operator: {}. Please select from: {}'.format(operator, valid_operators))
40
-
41
- self.operator = operator
42
-
43
- def to_json(self):
44
- _json = {'name': self.name}
45
- if self.version is not None:
46
- _json['version'] = self.version
47
- if self.operator is not None:
48
- _json['operator'] = self.operator
49
- return _json
50
-
51
- @classmethod
52
- def from_json(cls, _json: dict):
53
- return cls(**_json)
54
-
55
-
56
- class Package(entities.DlEntity):
57
- """
58
- Package object
59
- """
60
- # platform
61
- id: str = entities.DlProperty(location=['id'], _type=str)
62
- url: str = entities.DlProperty(location=['url'], _type=str)
63
- name: str = entities.DlProperty(location=['name'], _type=str)
64
- version: str = entities.DlProperty(location=['version'], _type=str)
65
- created_at: str = entities.DlProperty(location=['createdAt'], _type=str)
66
- updated_at: str = entities.DlProperty(location=['updatedAt'], _type=str)
67
- project_id: str = entities.DlProperty(location=['projectId'], _type=str)
68
- creator: str = entities.DlProperty(location=['creator'], _type=str)
69
- type: str = entities.DlProperty(location=['type'], _type=str)
70
- metadata: dict = entities.DlProperty(location=['metadata'], _type=dict)
71
- ui_hooks: list = entities.DlProperty(location=['uiHooks'], _type=str)
72
- service_config: dict = entities.DlProperty(location=['serviceConfig'], _type=str)
73
- is_global: bool = entities.DlProperty(location=['global'], _type=str)
74
-
75
- codebase: typing.Any = entities.DlProperty(location=['codebase'], _kls='Codebase')
76
- modules: typing.List[PackageModule] = entities.DlProperty(location=['modules'], _kls='PackageModule')
77
- slots: typing.Union[typing.List[PackageSlot], None] = entities.DlProperty(location=['slots'],
78
- _kls='PackageSlot')
79
- requirements: typing.Union[typing.List[PackageRequirement], None] = entities.DlProperty(location=['requirements'],
80
- _kls='PackageRequirement')
81
-
82
- # sdk
83
- _client_api: ApiClient
84
- _revisions = None
85
- __repositories = None
86
- _project = None
87
-
88
- def __repr__(self):
89
- # TODO need to move to DlEntity
90
- return "Package(id={id}, name={name}, creator={creator}, project_id={project_id}, type={type}, version={version})".format(
91
- id=self.id,
92
- name=self.name,
93
- version=self.version,
94
- type=self.type,
95
- project_id=self.project_id,
96
- creator=self.creator)
97
-
98
- @property
99
- def createdAt(self):
100
- return self.created_at
101
-
102
- @property
103
- def updatedAt(self):
104
- return self.updated_at
105
-
106
- @property
107
- def revisions(self):
108
- if self._revisions is None:
109
- self._revisions = self.packages.revisions(package=self)
110
- return self._revisions
111
-
112
- @property
113
- def platform_url(self):
114
- return self._client_api._get_resource_url("projects/{}/packages/{}/main".format(self.project.id, self.id))
115
-
116
- @property
117
- def codebase_id(self):
118
- if self.codebase is not None and self.codebase.type == entities.PackageCodebaseType.ITEM:
119
- return self.codebase.item_id
120
- return None
121
-
122
- @codebase_id.setter
123
- def codebase_id(self, item_id: str):
124
- self.codebase = entities.ItemCodebase(item_id=item_id)
125
-
126
- @staticmethod
127
- def _protected_from_json(_json, client_api, project=None, is_fetched=True):
128
- """
129
- Same as from_json but with try-except to catch if error
130
-
131
- :param _json: platform json
132
- :param client_api: ApiClient entity
133
- :return:
134
- """
135
- try:
136
- package = Package.from_json(_json=_json,
137
- client_api=client_api,
138
- project=project,
139
- is_fetched=is_fetched)
140
- status = True
141
- except Exception:
142
- package = traceback.format_exc()
143
- status = False
144
- return status, package
145
-
146
- @classmethod
147
- def from_json(cls, _json, client_api, project=None, is_fetched=True):
148
- """
149
- Turn platform representation of package into a package entity
150
-
151
- :param dict _json: platform representation of package
152
- :param dl.ApiClient client_api: ApiClient entity
153
- :param dtlpy.entities.project.Project project: project entity
154
- :param is_fetched: is Entity fetched from Platform
155
- :return: Package entity
156
- :rtype: dtlpy.entities.package.Package
157
- """
158
- if project is not None:
159
- if project.id != _json.get('projectId', None):
160
- logger.warning('Package has been fetched from a project that is not belong to it')
161
- project = None
162
- # Entity
163
- inst = cls(_dict=_json)
164
- # Platform
165
- inst._project = project
166
- inst._client_api = client_api
167
- inst.is_fetched = is_fetched
168
-
169
- return inst
170
-
171
- def to_json(self):
172
- """
173
- Turn Package entity into a platform representation of Package
174
-
175
- :return: platform json of package
176
- :rtype: dict
177
- """
178
- _json = self._dict.copy()
179
- return _json
180
-
181
- ############
182
- # entities #
183
- ############
184
- @property
185
- def project(self):
186
- if self._project is None:
187
- self._project = self.projects.get(project_id=self.project_id, fetch=None)
188
- assert isinstance(self._project, entities.Project)
189
- return self._project
190
-
191
- @project.setter
192
- def project(self, project):
193
- assert isinstance(self._project, entities.Project), "Unknwon 'project' type: {}".format(type(project))
194
- self._project = project
195
-
196
- ################
197
- # repositories #
198
- ################
199
- @property
200
- def _repositories(self):
201
- if self.__repositories is None:
202
- reps = namedtuple('repositories',
203
- field_names=['executions', 'services', 'projects', 'packages', 'artifacts', 'codebases',
204
- 'models'])
205
-
206
- self.__repositories = reps(
207
- executions=repositories.Executions(client_api=self._client_api,
208
- project=self._project),
209
- services=repositories.Services(client_api=self._client_api,
210
- package=self,
211
- project=self._project,
212
- project_id=self.project_id),
213
- projects=repositories.Projects(client_api=self._client_api),
214
- packages=repositories.Packages(client_api=self._client_api,
215
- project=self._project),
216
- artifacts=repositories.Artifacts(client_api=self._client_api,
217
- project=self._project,
218
- project_id=self.project_id,
219
- package=self),
220
- codebases=repositories.Codebases(client_api=self._client_api, project=self._project,
221
- project_id=self.project_id),
222
- models=repositories.Models(client_api=self._client_api,
223
- project=self._project,
224
- package=self,
225
- project_id=self.project_id)
226
- )
227
- return self.__repositories
228
-
229
- @property
230
- def executions(self):
231
- assert isinstance(self._repositories.executions, repositories.Executions)
232
- return self._repositories.executions
233
-
234
- @property
235
- def services(self):
236
- assert isinstance(self._repositories.services, repositories.Services)
237
- return self._repositories.services
238
-
239
- @property
240
- def projects(self):
241
- assert isinstance(self._repositories.projects, repositories.Projects)
242
- return self._repositories.projects
243
-
244
- @property
245
- def packages(self):
246
- assert isinstance(self._repositories.packages, repositories.Packages)
247
- return self._repositories.packages
248
-
249
- @property
250
- def codebases(self):
251
- assert isinstance(self._repositories.codebases, repositories.Codebases)
252
- return self._repositories.codebases
253
-
254
- @property
255
- def artifacts(self):
256
- assert isinstance(self._repositories.artifacts, repositories.Artifacts)
257
- return self._repositories.artifacts
258
-
259
- @property
260
- def models(self):
261
- assert isinstance(self._repositories.models, repositories.Models)
262
- return self._repositories.models
263
-
264
- ##############
265
- # properties #
266
- ##############
267
- @property
268
- def git_status(self):
269
- status = 'Git status unavailable'
270
- try:
271
- if self.codebase.type == entities.PackageCodebaseType.ITEM:
272
- if 'git' in self.codebase.item.metadata:
273
- status = self.codebase.item.metadata['git'].get('status', status)
274
- except Exception:
275
- logging.debug('Error getting codebase')
276
- return status
277
-
278
- @property
279
- def git_log(self):
280
- log = 'Git log unavailable'
281
- try:
282
- if self.codebase.type == entities.PackageCodebaseType.ITEM:
283
- if 'git' in self.codebase.item.metadata:
284
- log = self.codebase.item.metadata['git'].get('log', log)
285
- except Exception:
286
- logging.debug('Error getting codebase')
287
- return log
288
-
289
- ###########
290
- # methods #
291
- ###########
292
- def update(self):
293
- """
294
- Update Package changes to platform
295
-
296
- :return: Package entity
297
- """
298
- return self.packages.update(package=self)
299
-
300
- def deploy(self,
301
- service_name=None,
302
- revision=None,
303
- init_input=None,
304
- runtime=None,
305
- sdk_version=None,
306
- agent_versions=None,
307
- verify=True,
308
- bot=None,
309
- pod_type=None,
310
- module_name=None,
311
- run_execution_as_process=None,
312
- execution_timeout=None,
313
- drain_time=None,
314
- on_reset=None,
315
- max_attempts=None,
316
- force=False,
317
- secrets: list = None,
318
- **kwargs):
319
- """
320
- Deploy package
321
-
322
- :param str service_name: service name
323
- :param str revision: package revision - default=latest
324
- :param init_input: config to run at startup
325
- :param dict runtime: runtime resources
326
- :param str sdk_version: - optional - string - sdk version
327
- :param dict agent_versions: - dictionary - - optional -versions of sdk, agent runner and agent proxy
328
- :param str bot: bot email
329
- :param str pod_type: pod type dl.InstanceCatalog
330
- :param bool verify: verify the inputs
331
- :param str module_name: module name
332
- :param bool run_execution_as_process: run execution as process
333
- :param int execution_timeout: execution timeout
334
- :param int drain_time: drain time
335
- :param str on_reset: on reset
336
- :param int max_attempts: Maximum execution retries in-case of a service reset
337
- :param bool force: optional - terminate old replicas immediately
338
- :param list secrets: list of the integrations ids
339
- :return: Service object
340
- :rtype: dtlpy.entities.service.Service
341
-
342
- **Example**:
343
-
344
- .. code-block:: python
345
- service: dl.Service = package.deploy(service_name=package_name,
346
- execution_timeout=3 * 60 * 60,
347
- module_name=module.name,
348
- runtime=dl.KubernetesRuntime(
349
- concurrency=10,
350
- pod_type=dl.InstanceCatalog.REGULAR_S,
351
- autoscaler=dl.KubernetesRabbitmqAutoscaler(
352
- min_replicas=1,
353
- max_replicas=20,
354
- queue_length=20
355
- )
356
- )
357
- )
358
-
359
- """
360
- return self.project.packages.deploy(package=self,
361
- service_name=service_name,
362
- project_id=self.project_id,
363
- revision=revision,
364
- init_input=init_input,
365
- runtime=runtime,
366
- sdk_version=sdk_version,
367
- agent_versions=agent_versions,
368
- pod_type=pod_type,
369
- bot=bot,
370
- verify=verify,
371
- module_name=module_name,
372
- run_execution_as_process=run_execution_as_process,
373
- execution_timeout=execution_timeout,
374
- drain_time=drain_time,
375
- on_reset=on_reset,
376
- max_attempts=max_attempts,
377
- force=force,
378
- jwt_forward=kwargs.get('jwt_forward', None),
379
- is_global=kwargs.get('is_global', None),
380
- secrets=secrets)
381
-
382
- def checkout(self):
383
- """
384
- Checkout as package
385
-
386
- :return:
387
- """
388
- return self.packages.checkout(package=self)
389
-
390
- def delete(self) -> bool:
391
- """
392
- Delete Package object
393
-
394
- :return: True
395
- """
396
- return self.packages.delete(package=self)
397
-
398
- def push(self,
399
- codebase: Union[entities.GitCodebase, entities.ItemCodebase] = None,
400
- src_path: str = None,
401
- package_name: str = None,
402
- modules: list = None,
403
- checkout: bool = False,
404
- revision_increment: str = None,
405
- service_update: bool = False,
406
- service_config: dict = None,
407
- package_type='faas'
408
- ):
409
- """
410
- Push local package
411
-
412
- :param dtlpy.entities.codebase.Codebase codebase: PackageCode object - defines how to store the package code
413
- :param bool checkout: save package to local checkout
414
- :param str src_path: location of pacjage codebase folder to zip
415
- :param str package_name: name of package
416
- :param list modules: list of PackageModule
417
- :param str revision_increment: optional - str - version bumping method - major/minor/patch - default = None
418
- :param bool service_update: optional - bool - update the service
419
- :param dict service_config : Service object as dict. Contains the spec of the default service to create.
420
- :param str package_type: default is "faas", one of "app", "ml"
421
- :return: package entity
422
- :rtype: dtlpy.entities.package.Package
423
-
424
- **Example**:
425
-
426
- .. code-block:: python
427
-
428
- package = packages.push(package_name='package_name',
429
- modules=[module],
430
- version='1.0.0',
431
- src_path=os.getcwd())
432
- """
433
- return self.project.packages.push(
434
- package_name=package_name if package_name is not None else self.name,
435
- modules=modules if modules is not None else self.modules,
436
- revision_increment=revision_increment,
437
- codebase=codebase,
438
- src_path=src_path,
439
- checkout=checkout,
440
- service_update=service_update,
441
- service_config=service_config,
442
- package_type=package_type
443
- )
444
-
445
- def pull(self, version=None, local_path=None) -> str:
446
- """
447
- Pull local package
448
-
449
- :param str version: version
450
- :param str local_path: local path
451
-
452
- **Example**:
453
-
454
- .. code-block:: python
455
-
456
- path = package.pull(local_path='local_path')
457
- """
458
- return self.packages.pull(package=self,
459
- version=version,
460
- local_path=local_path)
461
-
462
- def build(self, module_name=None, init_inputs=None, local_path=None, from_local=None):
463
- """
464
- Instantiate a module from the package code. Returns a loaded instance of the runner class
465
-
466
- :param module_name: Name of the module to build the runner class
467
- :param str init_inputs: dictionary of the class init variables (if exists). will be used to init the module class
468
- :param str local_path: local path of the package (if from_local=False - codebase will be downloaded)
469
- :param bool from_local: bool. if true - codebase will not be downloaded (only use local files)
470
- :return: dl.BaseServiceRunner
471
- """
472
- return self.packages.build(package=self,
473
- module_name=module_name,
474
- local_path=local_path,
475
- init_inputs=init_inputs,
476
- from_local=from_local)
477
-
478
- def open_in_web(self):
479
- """
480
- Open the package in web platform
481
-
482
- """
483
- url = self._client_api._get_resource_url(
484
- f"projects/{self.project.id}/faas?byCreator=false&byProject=true&byDataloop=false&tab=library&name={self.name}")
485
- self._client_api._open_in_web(url=url)
486
-
487
- def test(self,
488
- cwd=None,
489
- concurrency=None,
490
- module_name=entities.package_defaults.DEFAULT_PACKAGE_MODULE_NAME,
491
- function_name=entities.package_defaults.DEFAULT_PACKAGE_FUNCTION_NAME,
492
- class_name=entities.package_defaults.DEFAULT_PACKAGE_CLASS_NAME,
493
- entry_point=entities.package_defaults.DEFAULT_PACKAGE_ENTRY_POINT
494
- ):
495
- """
496
- Test local package in local environment.
497
-
498
- :param str cwd: path to the file
499
- :param int concurrency: the concurrency of the test
500
- :param str module_name: module name
501
- :param str function_name: function name
502
- :param str class_name: class name
503
- :param str entry_point: the file to run like main.py
504
- :return: list created by the function that tested the output
505
- :rtype: list
506
-
507
- **Example**:
508
-
509
- .. code-block:: python
510
-
511
- package.test(cwd='path_to_package',
512
- function_name='run')
513
- """
514
- return self.project.packages.test_local_package(
515
- cwd=cwd,
516
- concurrency=concurrency,
517
- package=self,
518
- module_name=module_name,
519
- function_name=function_name,
520
- class_name=class_name,
521
- entry_point=entry_point
522
- )
523
-
524
- @staticmethod
525
- def _mockify_input(input_type):
526
- _json = dict()
527
- if input_type == 'Dataset':
528
- _json.update({'dataset_id': 'id'})
529
- if input_type == 'Item':
530
- _json.update({'item_id': 'id', 'dataset_id': 'id'})
531
- if input_type == 'Annotation':
532
- _json.update({'annotation_id': 'id', 'item_id': 'id', 'dataset_id': 'id'})
533
- return _json
534
-
535
- def mockify(self, local_path=None, module_name=None, function_name=None):
536
- if local_path is None:
537
- local_path = os.getcwd()
538
-
539
- if module_name is None:
540
- if self.modules:
541
- module_name = self.modules[0].name
542
- else:
543
- raise exceptions.PlatformException('400', 'Package has no modules')
544
-
545
- modules = [module for module in self.modules if module.name == module_name]
546
- if not modules:
547
- raise exceptions.PlatformException('404', 'Module not found: {}'.format(module_name))
548
- module = modules[0]
549
-
550
- if function_name is None:
551
- funcs = [func for func in module.functions]
552
- if funcs:
553
- func = funcs[0]
554
- else:
555
- raise exceptions.PlatformException('400', 'Module: {} has no functions'.format(module_name))
556
- else:
557
- funcs = [func for func in module.functions if func.name == function_name]
558
- if not funcs:
559
- raise exceptions.PlatformException('404', 'Function not found: {}'.format(function_name))
560
- func = funcs[0]
561
-
562
- mock = dict()
563
- for module in self.modules:
564
- mock['module_name'] = module.name
565
- mock['function_name'] = func.name
566
- mock['init_params'] = {inpt.name: self._mockify_input(input_type=inpt.type) for inpt in module.init_inputs}
567
- mock['inputs'] = [{'name': inpt.name, 'value': self._mockify_input(input_type=inpt.type)} for inpt in
568
- func.inputs]
569
-
570
- with open(os.path.join(local_path, 'mock.json'), 'w') as f:
571
- json.dump(mock, f)
572
-
573
- @staticmethod
574
- def get_ml_metadata(cls=None,
575
- available_methods=None,
576
- output_type=entities.AnnotationType.CLASSIFICATION,
577
- input_type='image',
578
- default_configuration: dict = None):
579
- """
580
- Create ML metadata for the package
581
- :param cls: ModelAdapter class, to get the list of available_methods
582
- :param available_methods: available user function on the adapter. ['load', 'save', 'predict', 'train']
583
- :param output_type: annotation type the model create, e.g. dl.AnnotationType.CLASSIFICATION
584
- :param input_type: input file type the model gets, one of ['image', 'video', 'txt']
585
- :param default_configuration: default service configuration for the deployed services
586
- :return:
587
- """
588
- user_implemented_methods = ['load', 'save', 'predict', 'train']
589
- if available_methods is None:
590
- # default
591
- available_methods = user_implemented_methods
592
-
593
- if cls is not None:
594
- # TODO dont check if function is on the adapter - check if the functions is implemented (not raise NotImplemented)
595
- available_methods = [
596
- {name: 'BaseModelAdapter' not in getattr(cls, name).__qualname__}
597
- for name in user_implemented_methods
598
- ]
599
- if default_configuration is None:
600
- default_configuration = dict()
601
- metadata = {
602
- 'system': {'ml': {'defaultConfiguration': default_configuration,
603
- 'outputType': output_type,
604
- 'inputType': input_type,
605
- 'supportedMethods': available_methods
606
- }}}
607
- return metadata
608
-
609
- class decorators:
610
- @staticmethod
611
- def module(name='default-module', description='', init_inputs=None):
612
- def wrapper(cls: typing.Callable):
613
- # package_module_dict = package_module.to_json()
614
- package_module_dict = {"name": name,
615
- "description": description,
616
- "functions": list(),
617
- "className": cls.__name__}
618
- if init_inputs is not None:
619
- package_module_dict.update(initInputs=Package.decorators.parse_io(io_list=init_inputs))
620
- for member_name, member in inspect.getmembers(cls, predicate=inspect.isfunction):
621
- spec = getattr(member, '__dtlpy__', None)
622
- if spec is not None:
623
- package_module_dict["functions"].append(spec)
624
- cls.__dtlpy__ = package_module_dict
625
- return cls
626
-
627
- return wrapper
628
-
629
- @staticmethod
630
- def function(display_name=None, inputs=None, outputs=None):
631
- def wrapper(func: typing.Callable):
632
- if display_name is None:
633
- d_name = func.__name__
634
- else:
635
- d_name = display_name
636
- func.__dtlpy__ = {"name": func.__name__,
637
- "displayName": d_name,
638
- "input": Package.decorators.parse_io(io_list=inputs),
639
- "output": Package.decorators.parse_io(io_list=outputs)}
640
- return func
641
-
642
- return wrapper
643
-
644
- @staticmethod
645
- def parse_io(io_list: dict):
646
- output = list()
647
- if io_list is not None:
648
- for io_name, io_type in io_list.items():
649
- if isinstance(io_type, Enum):
650
- io_type = io_type.name
651
- if isinstance(io_type, type):
652
- io_type = io_type.__name__
653
- else:
654
- io_type = str(io_type)
655
- output.append({"name": io_name,
656
- "type": str(io_type)})
657
- return output
1
+ from collections import namedtuple
2
+ from typing import Union
3
+ from enum import Enum
4
+ import traceback
5
+ import logging
6
+ import inspect
7
+ import typing
8
+ import json
9
+ import os
10
+
11
+ from .package_module import PackageModule
12
+ from .package_slot import PackageSlot
13
+ from .. import repositories, entities, exceptions
14
+ from ..services.api_client import ApiClient
15
+
16
+ logger = logging.getLogger(name='dtlpy')
17
+
18
+
19
+ class RequirementOperator(str, Enum):
20
+ EQUAL = '==',
21
+ GREATER_THAN = '>',
22
+ LESS_THAN = '<',
23
+ EQUAL_OR_LESS_THAN = '<=',
24
+ EQUAL_OR_GREATER_THAN = '>='
25
+
26
+ @staticmethod
27
+ def keys():
28
+ return [key.value for key in list(RequirementOperator)]
29
+
30
+
31
+ class PackageRequirement:
32
+
33
+ def __init__(self, name: str, version: str = None, operator: str = None):
34
+ self.name = name
35
+ self.version = version
36
+
37
+ valid_operators = RequirementOperator.keys()
38
+ if operator is not None and operator not in valid_operators:
39
+ raise Exception('Illegal operator: {}. Please select from: {}'.format(operator, valid_operators))
40
+
41
+ self.operator = operator
42
+
43
+ def to_json(self):
44
+ _json = {'name': self.name}
45
+ if self.version is not None:
46
+ _json['version'] = self.version
47
+ if self.operator is not None:
48
+ _json['operator'] = self.operator
49
+ return _json
50
+
51
+ @classmethod
52
+ def from_json(cls, _json: dict):
53
+ return cls(**_json)
54
+
55
+
56
+ class Package(entities.DlEntity):
57
+ """
58
+ Package object
59
+ """
60
+ # platform
61
+ id: str = entities.DlProperty(location=['id'], _type=str)
62
+ url: str = entities.DlProperty(location=['url'], _type=str)
63
+ name: str = entities.DlProperty(location=['name'], _type=str)
64
+ version: str = entities.DlProperty(location=['version'], _type=str)
65
+ created_at: str = entities.DlProperty(location=['createdAt'], _type=str)
66
+ updated_at: str = entities.DlProperty(location=['updatedAt'], _type=str)
67
+ project_id: str = entities.DlProperty(location=['projectId'], _type=str)
68
+ creator: str = entities.DlProperty(location=['creator'], _type=str)
69
+ type: str = entities.DlProperty(location=['type'], _type=str)
70
+ metadata: dict = entities.DlProperty(location=['metadata'], _type=dict)
71
+ ui_hooks: list = entities.DlProperty(location=['uiHooks'], _type=str)
72
+ service_config: dict = entities.DlProperty(location=['serviceConfig'], _type=str)
73
+ is_global: bool = entities.DlProperty(location=['global'], _type=str)
74
+
75
+ codebase: typing.Any = entities.DlProperty(location=['codebase'], _kls='Codebase')
76
+ modules: typing.List[PackageModule] = entities.DlProperty(location=['modules'], _kls='PackageModule')
77
+ slots: typing.Union[typing.List[PackageSlot], None] = entities.DlProperty(location=['slots'],
78
+ _kls='PackageSlot')
79
+ requirements: typing.Union[typing.List[PackageRequirement], None] = entities.DlProperty(location=['requirements'],
80
+ _kls='PackageRequirement')
81
+
82
+ # sdk
83
+ _client_api: ApiClient
84
+ _revisions = None
85
+ __repositories = None
86
+ _project = None
87
+
88
+ def __repr__(self):
89
+ # TODO need to move to DlEntity
90
+ return "Package(id={id}, name={name}, creator={creator}, project_id={project_id}, type={type}, version={version})".format(
91
+ id=self.id,
92
+ name=self.name,
93
+ version=self.version,
94
+ type=self.type,
95
+ project_id=self.project_id,
96
+ creator=self.creator)
97
+
98
+ @property
99
+ def createdAt(self):
100
+ return self.created_at
101
+
102
+ @property
103
+ def updatedAt(self):
104
+ return self.updated_at
105
+
106
+ @property
107
+ def revisions(self):
108
+ if self._revisions is None:
109
+ self._revisions = self.packages.revisions(package=self)
110
+ return self._revisions
111
+
112
+ @property
113
+ def platform_url(self):
114
+ return self._client_api._get_resource_url("projects/{}/packages/{}/main".format(self.project.id, self.id))
115
+
116
+ @property
117
+ def codebase_id(self):
118
+ if self.codebase is not None and self.codebase.type == entities.PackageCodebaseType.ITEM:
119
+ return self.codebase.item_id
120
+ return None
121
+
122
+ @codebase_id.setter
123
+ def codebase_id(self, item_id: str):
124
+ self.codebase = entities.ItemCodebase(item_id=item_id)
125
+
126
+ @staticmethod
127
+ def _protected_from_json(_json, client_api, project=None, is_fetched=True):
128
+ """
129
+ Same as from_json but with try-except to catch if error
130
+
131
+ :param _json: platform json
132
+ :param client_api: ApiClient entity
133
+ :return:
134
+ """
135
+ try:
136
+ package = Package.from_json(_json=_json,
137
+ client_api=client_api,
138
+ project=project,
139
+ is_fetched=is_fetched)
140
+ status = True
141
+ except Exception:
142
+ package = traceback.format_exc()
143
+ status = False
144
+ return status, package
145
+
146
+ @classmethod
147
+ def from_json(cls, _json, client_api, project=None, is_fetched=True):
148
+ """
149
+ Turn platform representation of package into a package entity
150
+
151
+ :param dict _json: platform representation of package
152
+ :param dl.ApiClient client_api: ApiClient entity
153
+ :param dtlpy.entities.project.Project project: project entity
154
+ :param is_fetched: is Entity fetched from Platform
155
+ :return: Package entity
156
+ :rtype: dtlpy.entities.package.Package
157
+ """
158
+ if project is not None:
159
+ if project.id != _json.get('projectId', None):
160
+ logger.warning('Package has been fetched from a project that is not belong to it')
161
+ project = None
162
+ # Entity
163
+ inst = cls(_dict=_json)
164
+ # Platform
165
+ inst._project = project
166
+ inst._client_api = client_api
167
+ inst.is_fetched = is_fetched
168
+
169
+ return inst
170
+
171
+ def to_json(self):
172
+ """
173
+ Turn Package entity into a platform representation of Package
174
+
175
+ :return: platform json of package
176
+ :rtype: dict
177
+ """
178
+ _json = self._dict.copy()
179
+ return _json
180
+
181
+ ############
182
+ # entities #
183
+ ############
184
+ @property
185
+ def project(self):
186
+ if self._project is None:
187
+ self._project = self.projects.get(project_id=self.project_id, fetch=None)
188
+ assert isinstance(self._project, entities.Project)
189
+ return self._project
190
+
191
+ @project.setter
192
+ def project(self, project):
193
+ assert isinstance(self._project, entities.Project), "Unknwon 'project' type: {}".format(type(project))
194
+ self._project = project
195
+
196
+ ################
197
+ # repositories #
198
+ ################
199
+ @property
200
+ def _repositories(self):
201
+ if self.__repositories is None:
202
+ reps = namedtuple('repositories',
203
+ field_names=['executions', 'services', 'projects', 'packages', 'artifacts', 'codebases',
204
+ 'models'])
205
+
206
+ self.__repositories = reps(
207
+ executions=repositories.Executions(client_api=self._client_api,
208
+ project=self._project),
209
+ services=repositories.Services(client_api=self._client_api,
210
+ package=self,
211
+ project=self._project,
212
+ project_id=self.project_id),
213
+ projects=repositories.Projects(client_api=self._client_api),
214
+ packages=repositories.Packages(client_api=self._client_api,
215
+ project=self._project),
216
+ artifacts=repositories.Artifacts(client_api=self._client_api,
217
+ project=self._project,
218
+ project_id=self.project_id,
219
+ package=self),
220
+ codebases=repositories.Codebases(client_api=self._client_api, project=self._project,
221
+ project_id=self.project_id),
222
+ models=repositories.Models(client_api=self._client_api,
223
+ project=self._project,
224
+ package=self,
225
+ project_id=self.project_id)
226
+ )
227
+ return self.__repositories
228
+
229
+ @property
230
+ def executions(self):
231
+ assert isinstance(self._repositories.executions, repositories.Executions)
232
+ return self._repositories.executions
233
+
234
+ @property
235
+ def services(self):
236
+ assert isinstance(self._repositories.services, repositories.Services)
237
+ return self._repositories.services
238
+
239
+ @property
240
+ def projects(self):
241
+ assert isinstance(self._repositories.projects, repositories.Projects)
242
+ return self._repositories.projects
243
+
244
+ @property
245
+ def packages(self):
246
+ assert isinstance(self._repositories.packages, repositories.Packages)
247
+ return self._repositories.packages
248
+
249
+ @property
250
+ def codebases(self):
251
+ assert isinstance(self._repositories.codebases, repositories.Codebases)
252
+ return self._repositories.codebases
253
+
254
+ @property
255
+ def artifacts(self):
256
+ assert isinstance(self._repositories.artifacts, repositories.Artifacts)
257
+ return self._repositories.artifacts
258
+
259
+ @property
260
+ def models(self):
261
+ assert isinstance(self._repositories.models, repositories.Models)
262
+ return self._repositories.models
263
+
264
+ ##############
265
+ # properties #
266
+ ##############
267
+ @property
268
+ def git_status(self):
269
+ status = 'Git status unavailable'
270
+ try:
271
+ if self.codebase.type == entities.PackageCodebaseType.ITEM:
272
+ if 'git' in self.codebase.item.metadata:
273
+ status = self.codebase.item.metadata['git'].get('status', status)
274
+ except Exception:
275
+ logging.debug('Error getting codebase')
276
+ return status
277
+
278
+ @property
279
+ def git_log(self):
280
+ log = 'Git log unavailable'
281
+ try:
282
+ if self.codebase.type == entities.PackageCodebaseType.ITEM:
283
+ if 'git' in self.codebase.item.metadata:
284
+ log = self.codebase.item.metadata['git'].get('log', log)
285
+ except Exception:
286
+ logging.debug('Error getting codebase')
287
+ return log
288
+
289
+ ###########
290
+ # methods #
291
+ ###########
292
+ def update(self):
293
+ """
294
+ Update Package changes to platform
295
+
296
+ :return: Package entity
297
+ """
298
+ return self.packages.update(package=self)
299
+
300
+ def deploy(self,
301
+ service_name=None,
302
+ revision=None,
303
+ init_input=None,
304
+ runtime=None,
305
+ sdk_version=None,
306
+ agent_versions=None,
307
+ verify=True,
308
+ bot=None,
309
+ pod_type=None,
310
+ module_name=None,
311
+ run_execution_as_process=None,
312
+ execution_timeout=None,
313
+ drain_time=None,
314
+ on_reset=None,
315
+ max_attempts=None,
316
+ force=False,
317
+ secrets: list = None,
318
+ **kwargs):
319
+ """
320
+ Deploy package
321
+
322
+ :param str service_name: service name
323
+ :param str revision: package revision - default=latest
324
+ :param init_input: config to run at startup
325
+ :param dict runtime: runtime resources
326
+ :param str sdk_version: - optional - string - sdk version
327
+ :param dict agent_versions: - dictionary - - optional -versions of sdk, agent runner and agent proxy
328
+ :param str bot: bot email
329
+ :param str pod_type: pod type dl.InstanceCatalog
330
+ :param bool verify: verify the inputs
331
+ :param str module_name: module name
332
+ :param bool run_execution_as_process: run execution as process
333
+ :param int execution_timeout: execution timeout
334
+ :param int drain_time: drain time
335
+ :param str on_reset: on reset
336
+ :param int max_attempts: Maximum execution retries in-case of a service reset
337
+ :param bool force: optional - terminate old replicas immediately
338
+ :param list secrets: list of the integrations ids
339
+ :return: Service object
340
+ :rtype: dtlpy.entities.service.Service
341
+
342
+ **Example**:
343
+
344
+ .. code-block:: python
345
+ service: dl.Service = package.deploy(service_name=package_name,
346
+ execution_timeout=3 * 60 * 60,
347
+ module_name=module.name,
348
+ runtime=dl.KubernetesRuntime(
349
+ concurrency=10,
350
+ pod_type=dl.InstanceCatalog.REGULAR_S,
351
+ autoscaler=dl.KubernetesRabbitmqAutoscaler(
352
+ min_replicas=1,
353
+ max_replicas=20,
354
+ queue_length=20
355
+ )
356
+ )
357
+ )
358
+
359
+ """
360
+ return self.project.packages.deploy(package=self,
361
+ service_name=service_name,
362
+ project_id=self.project_id,
363
+ revision=revision,
364
+ init_input=init_input,
365
+ runtime=runtime,
366
+ sdk_version=sdk_version,
367
+ agent_versions=agent_versions,
368
+ pod_type=pod_type,
369
+ bot=bot,
370
+ verify=verify,
371
+ module_name=module_name,
372
+ run_execution_as_process=run_execution_as_process,
373
+ execution_timeout=execution_timeout,
374
+ drain_time=drain_time,
375
+ on_reset=on_reset,
376
+ max_attempts=max_attempts,
377
+ force=force,
378
+ jwt_forward=kwargs.get('jwt_forward', None),
379
+ is_global=kwargs.get('is_global', None),
380
+ secrets=secrets)
381
+
382
+ def checkout(self):
383
+ """
384
+ Checkout as package
385
+
386
+ :return:
387
+ """
388
+ return self.packages.checkout(package=self)
389
+
390
+ def delete(self) -> bool:
391
+ """
392
+ Delete Package object
393
+
394
+ :return: True
395
+ """
396
+ return self.packages.delete(package=self)
397
+
398
+ def push(self,
399
+ codebase: Union[entities.GitCodebase, entities.ItemCodebase] = None,
400
+ src_path: str = None,
401
+ package_name: str = None,
402
+ modules: list = None,
403
+ checkout: bool = False,
404
+ revision_increment: str = None,
405
+ service_update: bool = False,
406
+ service_config: dict = None,
407
+ package_type='faas'
408
+ ):
409
+ """
410
+ Push local package
411
+
412
+ :param dtlpy.entities.codebase.Codebase codebase: PackageCode object - defines how to store the package code
413
+ :param bool checkout: save package to local checkout
414
+ :param str src_path: location of pacjage codebase folder to zip
415
+ :param str package_name: name of package
416
+ :param list modules: list of PackageModule
417
+ :param str revision_increment: optional - str - version bumping method - major/minor/patch - default = None
418
+ :param bool service_update: optional - bool - update the service
419
+ :param dict service_config : Service object as dict. Contains the spec of the default service to create.
420
+ :param str package_type: default is "faas", one of "app", "ml"
421
+ :return: package entity
422
+ :rtype: dtlpy.entities.package.Package
423
+
424
+ **Example**:
425
+
426
+ .. code-block:: python
427
+
428
+ package = packages.push(package_name='package_name',
429
+ modules=[module],
430
+ version='1.0.0',
431
+ src_path=os.getcwd())
432
+ """
433
+ return self.project.packages.push(
434
+ package_name=package_name if package_name is not None else self.name,
435
+ modules=modules if modules is not None else self.modules,
436
+ revision_increment=revision_increment,
437
+ codebase=codebase,
438
+ src_path=src_path,
439
+ checkout=checkout,
440
+ service_update=service_update,
441
+ service_config=service_config,
442
+ package_type=package_type
443
+ )
444
+
445
+ def pull(self, version=None, local_path=None) -> str:
446
+ """
447
+ Pull local package
448
+
449
+ :param str version: version
450
+ :param str local_path: local path
451
+
452
+ **Example**:
453
+
454
+ .. code-block:: python
455
+
456
+ path = package.pull(local_path='local_path')
457
+ """
458
+ return self.packages.pull(package=self,
459
+ version=version,
460
+ local_path=local_path)
461
+
462
+ def build(self, module_name=None, init_inputs=None, local_path=None, from_local=None):
463
+ """
464
+ Instantiate a module from the package code. Returns a loaded instance of the runner class
465
+
466
+ :param module_name: Name of the module to build the runner class
467
+ :param str init_inputs: dictionary of the class init variables (if exists). will be used to init the module class
468
+ :param str local_path: local path of the package (if from_local=False - codebase will be downloaded)
469
+ :param bool from_local: bool. if true - codebase will not be downloaded (only use local files)
470
+ :return: dl.BaseServiceRunner
471
+ """
472
+ return self.packages.build(package=self,
473
+ module_name=module_name,
474
+ local_path=local_path,
475
+ init_inputs=init_inputs,
476
+ from_local=from_local)
477
+
478
+ def open_in_web(self):
479
+ """
480
+ Open the package in web platform
481
+
482
+ """
483
+ url = self._client_api._get_resource_url(
484
+ f"projects/{self.project.id}/faas?byCreator=false&byProject=true&byDataloop=false&tab=library&name={self.name}")
485
+ self._client_api._open_in_web(url=url)
486
+
487
+ def test(self,
488
+ cwd=None,
489
+ concurrency=None,
490
+ module_name=entities.package_defaults.DEFAULT_PACKAGE_MODULE_NAME,
491
+ function_name=entities.package_defaults.DEFAULT_PACKAGE_FUNCTION_NAME,
492
+ class_name=entities.package_defaults.DEFAULT_PACKAGE_CLASS_NAME,
493
+ entry_point=entities.package_defaults.DEFAULT_PACKAGE_ENTRY_POINT
494
+ ):
495
+ """
496
+ Test local package in local environment.
497
+
498
+ :param str cwd: path to the file
499
+ :param int concurrency: the concurrency of the test
500
+ :param str module_name: module name
501
+ :param str function_name: function name
502
+ :param str class_name: class name
503
+ :param str entry_point: the file to run like main.py
504
+ :return: list created by the function that tested the output
505
+ :rtype: list
506
+
507
+ **Example**:
508
+
509
+ .. code-block:: python
510
+
511
+ package.test(cwd='path_to_package',
512
+ function_name='run')
513
+ """
514
+ return self.project.packages.test_local_package(
515
+ cwd=cwd,
516
+ concurrency=concurrency,
517
+ package=self,
518
+ module_name=module_name,
519
+ function_name=function_name,
520
+ class_name=class_name,
521
+ entry_point=entry_point
522
+ )
523
+
524
+ @staticmethod
525
+ def _mockify_input(input_type):
526
+ _json = dict()
527
+ if input_type == 'Dataset':
528
+ _json.update({'dataset_id': 'id'})
529
+ if input_type == 'Item':
530
+ _json.update({'item_id': 'id', 'dataset_id': 'id'})
531
+ if input_type == 'Annotation':
532
+ _json.update({'annotation_id': 'id', 'item_id': 'id', 'dataset_id': 'id'})
533
+ return _json
534
+
535
+ def mockify(self, local_path=None, module_name=None, function_name=None):
536
+ if local_path is None:
537
+ local_path = os.getcwd()
538
+
539
+ if module_name is None:
540
+ if self.modules:
541
+ module_name = self.modules[0].name
542
+ else:
543
+ raise exceptions.PlatformException('400', 'Package has no modules')
544
+
545
+ modules = [module for module in self.modules if module.name == module_name]
546
+ if not modules:
547
+ raise exceptions.PlatformException('404', 'Module not found: {}'.format(module_name))
548
+ module = modules[0]
549
+
550
+ if function_name is None:
551
+ funcs = [func for func in module.functions]
552
+ if funcs:
553
+ func = funcs[0]
554
+ else:
555
+ raise exceptions.PlatformException('400', 'Module: {} has no functions'.format(module_name))
556
+ else:
557
+ funcs = [func for func in module.functions if func.name == function_name]
558
+ if not funcs:
559
+ raise exceptions.PlatformException('404', 'Function not found: {}'.format(function_name))
560
+ func = funcs[0]
561
+
562
+ mock = dict()
563
+ for module in self.modules:
564
+ mock['module_name'] = module.name
565
+ mock['function_name'] = func.name
566
+ mock['init_params'] = {inpt.name: self._mockify_input(input_type=inpt.type) for inpt in module.init_inputs}
567
+ mock['inputs'] = [{'name': inpt.name, 'value': self._mockify_input(input_type=inpt.type)} for inpt in
568
+ func.inputs]
569
+
570
+ with open(os.path.join(local_path, 'mock.json'), 'w') as f:
571
+ json.dump(mock, f)
572
+
573
+ @staticmethod
574
+ def get_ml_metadata(cls=None,
575
+ available_methods=None,
576
+ output_type=entities.AnnotationType.CLASSIFICATION,
577
+ input_type='image',
578
+ default_configuration: dict = None):
579
+ """
580
+ Create ML metadata for the package
581
+ :param cls: ModelAdapter class, to get the list of available_methods
582
+ :param available_methods: available user function on the adapter. ['load', 'save', 'predict', 'train']
583
+ :param output_type: annotation type the model create, e.g. dl.AnnotationType.CLASSIFICATION
584
+ :param input_type: input file type the model gets, one of ['image', 'video', 'txt']
585
+ :param default_configuration: default service configuration for the deployed services
586
+ :return:
587
+ """
588
+ user_implemented_methods = ['load', 'save', 'predict', 'train']
589
+ if available_methods is None:
590
+ # default
591
+ available_methods = user_implemented_methods
592
+
593
+ if cls is not None:
594
+ # TODO dont check if function is on the adapter - check if the functions is implemented (not raise NotImplemented)
595
+ available_methods = [
596
+ {name: 'BaseModelAdapter' not in getattr(cls, name).__qualname__}
597
+ for name in user_implemented_methods
598
+ ]
599
+ if default_configuration is None:
600
+ default_configuration = dict()
601
+ metadata = {
602
+ 'system': {'ml': {'defaultConfiguration': default_configuration,
603
+ 'outputType': output_type,
604
+ 'inputType': input_type,
605
+ 'supportedMethods': available_methods
606
+ }}}
607
+ return metadata
608
+
609
+ class decorators:
610
+ @staticmethod
611
+ def module(name='default-module', description='', init_inputs=None):
612
+ def wrapper(cls: typing.Callable):
613
+ # package_module_dict = package_module.to_json()
614
+ package_module_dict = {"name": name,
615
+ "description": description,
616
+ "functions": list(),
617
+ "className": cls.__name__}
618
+ if init_inputs is not None:
619
+ package_module_dict.update(initInputs=Package.decorators.parse_io(io_list=init_inputs))
620
+ for member_name, member in inspect.getmembers(cls, predicate=inspect.isfunction):
621
+ spec = getattr(member, '__dtlpy__', None)
622
+ if spec is not None:
623
+ package_module_dict["functions"].append(spec)
624
+ cls.__dtlpy__ = package_module_dict
625
+ return cls
626
+
627
+ return wrapper
628
+
629
+ @staticmethod
630
+ def function(display_name=None, inputs=None, outputs=None):
631
+ def wrapper(func: typing.Callable):
632
+ if display_name is None:
633
+ d_name = func.__name__
634
+ else:
635
+ d_name = display_name
636
+ func.__dtlpy__ = {"name": func.__name__,
637
+ "displayName": d_name,
638
+ "input": Package.decorators.parse_io(io_list=inputs),
639
+ "output": Package.decorators.parse_io(io_list=outputs)}
640
+ return func
641
+
642
+ return wrapper
643
+
644
+ @staticmethod
645
+ def parse_io(io_list: dict):
646
+ output = list()
647
+ if io_list is not None:
648
+ for io_name, io_type in io_list.items():
649
+ if isinstance(io_type, Enum):
650
+ io_type = io_type.name
651
+ if isinstance(io_type, type):
652
+ io_type = io_type.__name__
653
+ else:
654
+ io_type = str(io_type)
655
+ output.append({"name": io_name,
656
+ "type": str(io_type)})
657
+ return output