dtlpy 1.113.10__py3-none-any.whl → 1.114.13__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 (243) hide show
  1. dtlpy/__init__.py +488 -488
  2. dtlpy/__version__.py +1 -1
  3. dtlpy/assets/__init__.py +26 -26
  4. dtlpy/assets/__pycache__/__init__.cpython-38.pyc +0 -0
  5. dtlpy/assets/code_server/config.yaml +2 -2
  6. dtlpy/assets/code_server/installation.sh +24 -24
  7. dtlpy/assets/code_server/launch.json +13 -13
  8. dtlpy/assets/code_server/settings.json +2 -2
  9. dtlpy/assets/main.py +53 -53
  10. dtlpy/assets/main_partial.py +18 -18
  11. dtlpy/assets/mock.json +11 -11
  12. dtlpy/assets/model_adapter.py +83 -83
  13. dtlpy/assets/package.json +61 -61
  14. dtlpy/assets/package_catalog.json +29 -29
  15. dtlpy/assets/package_gitignore +307 -307
  16. dtlpy/assets/service_runners/__init__.py +33 -33
  17. dtlpy/assets/service_runners/converter.py +96 -96
  18. dtlpy/assets/service_runners/multi_method.py +49 -49
  19. dtlpy/assets/service_runners/multi_method_annotation.py +54 -54
  20. dtlpy/assets/service_runners/multi_method_dataset.py +55 -55
  21. dtlpy/assets/service_runners/multi_method_item.py +52 -52
  22. dtlpy/assets/service_runners/multi_method_json.py +52 -52
  23. dtlpy/assets/service_runners/single_method.py +37 -37
  24. dtlpy/assets/service_runners/single_method_annotation.py +43 -43
  25. dtlpy/assets/service_runners/single_method_dataset.py +43 -43
  26. dtlpy/assets/service_runners/single_method_item.py +41 -41
  27. dtlpy/assets/service_runners/single_method_json.py +42 -42
  28. dtlpy/assets/service_runners/single_method_multi_input.py +45 -45
  29. dtlpy/assets/voc_annotation_template.xml +23 -23
  30. dtlpy/caches/base_cache.py +32 -32
  31. dtlpy/caches/cache.py +473 -473
  32. dtlpy/caches/dl_cache.py +201 -201
  33. dtlpy/caches/filesystem_cache.py +89 -89
  34. dtlpy/caches/redis_cache.py +84 -84
  35. dtlpy/dlp/__init__.py +20 -20
  36. dtlpy/dlp/cli_utilities.py +367 -367
  37. dtlpy/dlp/command_executor.py +764 -764
  38. dtlpy/dlp/dlp +1 -1
  39. dtlpy/dlp/dlp.bat +1 -1
  40. dtlpy/dlp/dlp.py +128 -128
  41. dtlpy/dlp/parser.py +651 -651
  42. dtlpy/entities/__init__.py +83 -83
  43. dtlpy/entities/analytic.py +311 -311
  44. dtlpy/entities/annotation.py +1879 -1879
  45. dtlpy/entities/annotation_collection.py +699 -699
  46. dtlpy/entities/annotation_definitions/__init__.py +20 -20
  47. dtlpy/entities/annotation_definitions/base_annotation_definition.py +100 -100
  48. dtlpy/entities/annotation_definitions/box.py +195 -195
  49. dtlpy/entities/annotation_definitions/classification.py +67 -67
  50. dtlpy/entities/annotation_definitions/comparison.py +72 -72
  51. dtlpy/entities/annotation_definitions/cube.py +204 -204
  52. dtlpy/entities/annotation_definitions/cube_3d.py +149 -149
  53. dtlpy/entities/annotation_definitions/description.py +32 -32
  54. dtlpy/entities/annotation_definitions/ellipse.py +124 -124
  55. dtlpy/entities/annotation_definitions/free_text.py +62 -62
  56. dtlpy/entities/annotation_definitions/gis.py +69 -69
  57. dtlpy/entities/annotation_definitions/note.py +139 -139
  58. dtlpy/entities/annotation_definitions/point.py +117 -117
  59. dtlpy/entities/annotation_definitions/polygon.py +182 -182
  60. dtlpy/entities/annotation_definitions/polyline.py +111 -111
  61. dtlpy/entities/annotation_definitions/pose.py +92 -92
  62. dtlpy/entities/annotation_definitions/ref_image.py +86 -86
  63. dtlpy/entities/annotation_definitions/segmentation.py +240 -240
  64. dtlpy/entities/annotation_definitions/subtitle.py +34 -34
  65. dtlpy/entities/annotation_definitions/text.py +85 -85
  66. dtlpy/entities/annotation_definitions/undefined_annotation.py +74 -74
  67. dtlpy/entities/app.py +220 -220
  68. dtlpy/entities/app_module.py +107 -107
  69. dtlpy/entities/artifact.py +174 -174
  70. dtlpy/entities/assignment.py +399 -399
  71. dtlpy/entities/base_entity.py +214 -214
  72. dtlpy/entities/bot.py +113 -113
  73. dtlpy/entities/codebase.py +296 -296
  74. dtlpy/entities/collection.py +38 -38
  75. dtlpy/entities/command.py +169 -169
  76. dtlpy/entities/compute.py +442 -442
  77. dtlpy/entities/dataset.py +1285 -1285
  78. dtlpy/entities/directory_tree.py +44 -44
  79. dtlpy/entities/dpk.py +470 -470
  80. dtlpy/entities/driver.py +222 -222
  81. dtlpy/entities/execution.py +397 -397
  82. dtlpy/entities/feature.py +124 -124
  83. dtlpy/entities/feature_set.py +145 -145
  84. dtlpy/entities/filters.py +641 -641
  85. dtlpy/entities/gis_item.py +107 -107
  86. dtlpy/entities/integration.py +184 -184
  87. dtlpy/entities/item.py +953 -953
  88. dtlpy/entities/label.py +123 -123
  89. dtlpy/entities/links.py +85 -85
  90. dtlpy/entities/message.py +175 -175
  91. dtlpy/entities/model.py +694 -691
  92. dtlpy/entities/node.py +1005 -1005
  93. dtlpy/entities/ontology.py +803 -803
  94. dtlpy/entities/organization.py +287 -287
  95. dtlpy/entities/package.py +657 -657
  96. dtlpy/entities/package_defaults.py +5 -5
  97. dtlpy/entities/package_function.py +185 -185
  98. dtlpy/entities/package_module.py +113 -113
  99. dtlpy/entities/package_slot.py +118 -118
  100. dtlpy/entities/paged_entities.py +290 -267
  101. dtlpy/entities/pipeline.py +593 -593
  102. dtlpy/entities/pipeline_execution.py +279 -279
  103. dtlpy/entities/project.py +394 -394
  104. dtlpy/entities/prompt_item.py +499 -499
  105. dtlpy/entities/recipe.py +301 -301
  106. dtlpy/entities/reflect_dict.py +102 -102
  107. dtlpy/entities/resource_execution.py +138 -138
  108. dtlpy/entities/service.py +958 -958
  109. dtlpy/entities/service_driver.py +117 -117
  110. dtlpy/entities/setting.py +294 -294
  111. dtlpy/entities/task.py +491 -491
  112. dtlpy/entities/time_series.py +143 -143
  113. dtlpy/entities/trigger.py +426 -426
  114. dtlpy/entities/user.py +118 -118
  115. dtlpy/entities/webhook.py +124 -124
  116. dtlpy/examples/__init__.py +19 -19
  117. dtlpy/examples/add_labels.py +135 -135
  118. dtlpy/examples/add_metadata_to_item.py +21 -21
  119. dtlpy/examples/annotate_items_using_model.py +65 -65
  120. dtlpy/examples/annotate_video_using_model_and_tracker.py +75 -75
  121. dtlpy/examples/annotations_convert_to_voc.py +9 -9
  122. dtlpy/examples/annotations_convert_to_yolo.py +9 -9
  123. dtlpy/examples/convert_annotation_types.py +51 -51
  124. dtlpy/examples/converter.py +143 -143
  125. dtlpy/examples/copy_annotations.py +22 -22
  126. dtlpy/examples/copy_folder.py +31 -31
  127. dtlpy/examples/create_annotations.py +51 -51
  128. dtlpy/examples/create_video_annotations.py +83 -83
  129. dtlpy/examples/delete_annotations.py +26 -26
  130. dtlpy/examples/filters.py +113 -113
  131. dtlpy/examples/move_item.py +23 -23
  132. dtlpy/examples/play_video_annotation.py +13 -13
  133. dtlpy/examples/show_item_and_mask.py +53 -53
  134. dtlpy/examples/triggers.py +49 -49
  135. dtlpy/examples/upload_batch_of_items.py +20 -20
  136. dtlpy/examples/upload_items_and_custom_format_annotations.py +55 -55
  137. dtlpy/examples/upload_items_with_modalities.py +43 -43
  138. dtlpy/examples/upload_segmentation_annotations_from_mask_image.py +44 -44
  139. dtlpy/examples/upload_yolo_format_annotations.py +70 -70
  140. dtlpy/exceptions.py +125 -125
  141. dtlpy/miscellaneous/__init__.py +20 -20
  142. dtlpy/miscellaneous/dict_differ.py +95 -95
  143. dtlpy/miscellaneous/git_utils.py +217 -217
  144. dtlpy/miscellaneous/json_utils.py +14 -14
  145. dtlpy/miscellaneous/list_print.py +105 -105
  146. dtlpy/miscellaneous/zipping.py +130 -130
  147. dtlpy/ml/__init__.py +20 -20
  148. dtlpy/ml/base_feature_extractor_adapter.py +27 -27
  149. dtlpy/ml/base_model_adapter.py +945 -940
  150. dtlpy/ml/metrics.py +461 -461
  151. dtlpy/ml/predictions_utils.py +274 -274
  152. dtlpy/ml/summary_writer.py +57 -57
  153. dtlpy/ml/train_utils.py +60 -60
  154. dtlpy/new_instance.py +252 -252
  155. dtlpy/repositories/__init__.py +56 -56
  156. dtlpy/repositories/analytics.py +85 -85
  157. dtlpy/repositories/annotations.py +916 -916
  158. dtlpy/repositories/apps.py +383 -383
  159. dtlpy/repositories/artifacts.py +452 -452
  160. dtlpy/repositories/assignments.py +599 -599
  161. dtlpy/repositories/bots.py +213 -213
  162. dtlpy/repositories/codebases.py +559 -559
  163. dtlpy/repositories/collections.py +332 -348
  164. dtlpy/repositories/commands.py +158 -158
  165. dtlpy/repositories/compositions.py +61 -61
  166. dtlpy/repositories/computes.py +434 -406
  167. dtlpy/repositories/datasets.py +1291 -1291
  168. dtlpy/repositories/downloader.py +895 -895
  169. dtlpy/repositories/dpks.py +433 -433
  170. dtlpy/repositories/drivers.py +266 -266
  171. dtlpy/repositories/executions.py +817 -817
  172. dtlpy/repositories/feature_sets.py +226 -226
  173. dtlpy/repositories/features.py +238 -238
  174. dtlpy/repositories/integrations.py +484 -484
  175. dtlpy/repositories/items.py +909 -915
  176. dtlpy/repositories/messages.py +94 -94
  177. dtlpy/repositories/models.py +877 -867
  178. dtlpy/repositories/nodes.py +80 -80
  179. dtlpy/repositories/ontologies.py +511 -511
  180. dtlpy/repositories/organizations.py +525 -525
  181. dtlpy/repositories/packages.py +1941 -1941
  182. dtlpy/repositories/pipeline_executions.py +448 -448
  183. dtlpy/repositories/pipelines.py +642 -642
  184. dtlpy/repositories/projects.py +539 -539
  185. dtlpy/repositories/recipes.py +399 -399
  186. dtlpy/repositories/resource_executions.py +137 -137
  187. dtlpy/repositories/schema.py +120 -120
  188. dtlpy/repositories/service_drivers.py +213 -213
  189. dtlpy/repositories/services.py +1704 -1704
  190. dtlpy/repositories/settings.py +339 -339
  191. dtlpy/repositories/tasks.py +1124 -1124
  192. dtlpy/repositories/times_series.py +278 -278
  193. dtlpy/repositories/triggers.py +536 -536
  194. dtlpy/repositories/upload_element.py +257 -257
  195. dtlpy/repositories/uploader.py +651 -651
  196. dtlpy/repositories/webhooks.py +249 -249
  197. dtlpy/services/__init__.py +22 -22
  198. dtlpy/services/aihttp_retry.py +131 -131
  199. dtlpy/services/api_client.py +1782 -1782
  200. dtlpy/services/api_reference.py +40 -40
  201. dtlpy/services/async_utils.py +133 -133
  202. dtlpy/services/calls_counter.py +44 -44
  203. dtlpy/services/check_sdk.py +68 -68
  204. dtlpy/services/cookie.py +115 -115
  205. dtlpy/services/create_logger.py +156 -156
  206. dtlpy/services/events.py +84 -84
  207. dtlpy/services/logins.py +235 -235
  208. dtlpy/services/reporter.py +256 -256
  209. dtlpy/services/service_defaults.py +91 -91
  210. dtlpy/utilities/__init__.py +20 -20
  211. dtlpy/utilities/annotations/__init__.py +16 -16
  212. dtlpy/utilities/annotations/annotation_converters.py +269 -269
  213. dtlpy/utilities/base_package_runner.py +264 -264
  214. dtlpy/utilities/converter.py +1650 -1650
  215. dtlpy/utilities/dataset_generators/__init__.py +1 -1
  216. dtlpy/utilities/dataset_generators/dataset_generator.py +670 -670
  217. dtlpy/utilities/dataset_generators/dataset_generator_tensorflow.py +23 -23
  218. dtlpy/utilities/dataset_generators/dataset_generator_torch.py +21 -21
  219. dtlpy/utilities/local_development/__init__.py +1 -1
  220. dtlpy/utilities/local_development/local_session.py +179 -179
  221. dtlpy/utilities/reports/__init__.py +2 -2
  222. dtlpy/utilities/reports/figures.py +343 -343
  223. dtlpy/utilities/reports/report.py +71 -71
  224. dtlpy/utilities/videos/__init__.py +17 -17
  225. dtlpy/utilities/videos/video_player.py +598 -598
  226. dtlpy/utilities/videos/videos.py +470 -470
  227. {dtlpy-1.113.10.data → dtlpy-1.114.13.data}/scripts/dlp +1 -1
  228. dtlpy-1.114.13.data/scripts/dlp.bat +2 -0
  229. {dtlpy-1.113.10.data → dtlpy-1.114.13.data}/scripts/dlp.py +128 -128
  230. {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/LICENSE +200 -200
  231. {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/METADATA +172 -172
  232. dtlpy-1.114.13.dist-info/RECORD +240 -0
  233. {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/WHEEL +1 -1
  234. tests/features/environment.py +551 -550
  235. dtlpy-1.113.10.data/scripts/dlp.bat +0 -2
  236. dtlpy-1.113.10.dist-info/RECORD +0 -244
  237. tests/assets/__init__.py +0 -0
  238. tests/assets/models_flow/__init__.py +0 -0
  239. tests/assets/models_flow/failedmain.py +0 -52
  240. tests/assets/models_flow/main.py +0 -62
  241. tests/assets/models_flow/main_model.py +0 -54
  242. {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/entry_points.txt +0 -0
  243. {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/top_level.txt +0 -0
@@ -1,559 +1,559 @@
1
- import hashlib
2
- import logging
3
- import os
4
- import io
5
- import random
6
- from typing import List
7
-
8
- from .. import entities, PlatformException, exceptions, repositories, miscellaneous
9
- from ..services.api_client import ApiClient
10
- from ..services.api_client import client as client_api
11
-
12
- logger = logging.getLogger(name='dtlpy')
13
-
14
-
15
- class Codebases:
16
- """
17
- Codebase Repository
18
-
19
- The Codebases class allows the user to manage codebases and their properties.
20
- The codebase is the code the user uploads for the user's packages to run.
21
- Read more about `codebase <https://developers.dataloop.ai/tutorials/faas/introduction/chapter/>`_ in our FaaS (function as a service).
22
- """
23
-
24
- def __init__(self,
25
- client_api: ApiClient,
26
- project: entities.Project = None,
27
- dataset: entities.Dataset = None,
28
- project_id: str = None):
29
- self._client_api = client_api
30
- self._project = project
31
- self._project_id = project_id
32
- self._dataset = dataset
33
- self._items_repository = None
34
- self.git_utils = miscellaneous.GitUtils()
35
-
36
- @property
37
- def items_repository(self) -> repositories.Items:
38
- if self._items_repository is None:
39
- if self._dataset is not None or self._project is not None:
40
- self._items_repository = self.dataset.items
41
- else:
42
- self._items_repository = repositories.Items(client_api=client_api)
43
- assert isinstance(self._items_repository, repositories.Items)
44
- return self._items_repository
45
-
46
- @property
47
- def project(self) -> entities.Project:
48
- if self._project is None:
49
- # project is None - try dataset
50
- if self._dataset is None:
51
- # dataset is None - try from project id
52
- if self._project_id is None:
53
- raise PlatformException(error='400',
54
- message='Cannot perform this action without a project')
55
- else:
56
- self._project = repositories.Projects(client_api=self._client_api).get(project_id=self._project_id)
57
- else:
58
- # dataset is not None - take project from there
59
- self._project = self.dataset.project
60
- assert isinstance(self._project, entities.Project)
61
- return self._project
62
-
63
- @property
64
- def dataset(self) -> entities.Dataset:
65
- if self._dataset is None:
66
- # get dataset from project
67
- try:
68
- self._dataset = self.project.datasets._get_binaries_dataset()
69
- except exceptions.NotFound:
70
- raise ValueError('Missing "Binaries" dataset in the project. Please contact support for help')
71
- assert isinstance(self._dataset, entities.Dataset)
72
- return self._dataset
73
-
74
- @dataset.setter
75
- def dataset(self, dataset: entities.Dataset):
76
- if not isinstance(dataset, entities.Dataset):
77
- raise ValueError('Must input a valid Dataset entity')
78
- self._dataset = dataset
79
-
80
- @staticmethod
81
- def __file_hash(filepath):
82
- m = hashlib.md5()
83
- with open(filepath, 'rb') as f:
84
- for chunk in iter(lambda: f.read(4096), b''):
85
- m.update(chunk)
86
- return m.hexdigest()
87
-
88
- def list_versions(self, codebase_name: str) -> entities.PagedEntities:
89
- """
90
- List all codebase versions.
91
-
92
- **Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a package.
93
-
94
- **Example**:
95
-
96
- .. code-block:: python
97
-
98
- package.codebases.list_versions(codebase_name='codebase_name')
99
-
100
- :param str codebase_name: code base name
101
- :return: list of versions
102
- :rtype: list
103
- """
104
- filters = entities.Filters()
105
- filters.add(field='filename', values='/codebases/{}/*'.format(codebase_name))
106
- versions = self.items_repository.list(filters=filters)
107
- return versions
108
-
109
- def list(self) -> entities.PagedEntities:
110
- """
111
- List all codebases.
112
-
113
- **Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a package.
114
-
115
- **Example**:
116
-
117
- .. code-block:: python
118
-
119
- package.codebases.list()
120
-
121
- :return: Paged entity
122
- :rtype: dtlpy.entities.paged_entities.PagedEntities
123
- """
124
- filters = entities.Filters()
125
- filters.add(field='filename', values='/codebases/*')
126
- filters.add(field='type', values='dir')
127
- codebases = self.items_repository.list(filters=filters)
128
- return codebases
129
-
130
- def get(self,
131
- codebase_name: str = None,
132
- codebase_id: str = None,
133
- version: str = None):
134
- """
135
- Get a Codebase object to use in your code.
136
-
137
- **Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a package.
138
-
139
- **Example**:
140
-
141
- .. code-block:: python
142
-
143
- package.codebases.get(codebase_name='codebase_name')
144
-
145
- :param str codebase_name: optional - search by name
146
- :param str codebase_id: optional - search by id
147
- :param str version: codebase version. default is latest. options: "all", "latest" or ver number - "10"
148
- :return: Codebase object
149
- """
150
- if codebase_id is not None:
151
- matched_version = self.items_repository.get(item_id=codebase_id)
152
- # verify input codebase name is same as the given id
153
- if codebase_name is not None and matched_version.name != codebase_name:
154
- logger.warning(
155
- "Mismatch found in codebases.get: codebase_name is different then codebase.name: "
156
- "{!r} != {!r}".format(
157
- codebase_name,
158
- matched_version.name))
159
- codebase = entities.Codebase(type='item',
160
- client_api=self._client_api,
161
- itemId=matched_version.id,
162
- item=matched_version)
163
- return codebase
164
-
165
- if codebase_name is None:
166
- raise PlatformException(error='400', message='Either "codebase_name" or "codebase_id" is needed')
167
- if version is None:
168
- version = 'latest'
169
-
170
- if version not in ['all', 'latest']:
171
- try:
172
- matched_version = self.items_repository.get(
173
- filepath='/codebases/{}/{}.zip'.format(codebase_name, version))
174
- except Exception:
175
- raise PlatformException(error='404',
176
- message='No matching version was found. version: {}'.format(version))
177
- codebase = entities.Codebase(type='item',
178
- client_api=self._client_api,
179
- itemId=matched_version.id,
180
- item=matched_version)
181
- return codebase
182
-
183
- # get all or latest
184
- versions_pages = self.list_versions(codebase_name=codebase_name)
185
- if versions_pages.items_count == 0:
186
- raise PlatformException(error='404', message='No codebase was found. name: {}'.format(codebase_name))
187
- else:
188
- if version == 'all':
189
- codebase = [entities.Codebase(type='item',
190
- client_api=self._client_api,
191
- itemId=mv.id,
192
- item=mv) for mv in versions_pages.all()]
193
- elif version == 'latest':
194
- max_ver = -1
195
- matched_version = None
196
- for page in versions_pages:
197
- for ver in page:
198
- if ver.type == 'dir':
199
- continue
200
- # extract version from filepath
201
- ver_int = int(os.path.splitext(ver.name)[0])
202
- if ver_int > max_ver:
203
- max_ver = ver_int
204
- matched_version = ver
205
- if matched_version is None:
206
- raise PlatformException(error='404',
207
- message='No codebase was found. name: {}'.format(codebase_name))
208
- else:
209
- codebase = entities.Codebase(type='item',
210
- client_api=self._client_api,
211
- itemId=matched_version.id,
212
- item=matched_version)
213
- else:
214
- raise PlatformException(error='404', message='Unknown version string: {}'.format(version))
215
-
216
- return codebase
217
-
218
- @staticmethod
219
- def get_current_version(all_versions_pages, zip_md):
220
- """
221
- This method returns the current version of the codebase and other versions found.
222
-
223
- **Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a package.
224
-
225
- :param codebase all_versions_pages: codebase object
226
- :param zip_md: zipped file of codebase
227
- :return: current version and all versions found of codebase
228
- :rtype: int, int
229
-
230
- **Example**:
231
-
232
- .. code-block:: python
233
-
234
- package.codebases.get_current_version(all_versions_pages='codebase_entity', zip_md='path')
235
- """
236
- latest_version = 0
237
- same_version_found = None
238
- # go over all existing versions
239
- for v_item in all_versions_pages:
240
- # get latest version
241
- if int(os.path.splitext(v_item.item.name)[0]) > latest_version:
242
- latest_version = int(os.path.splitext(v_item.item.name)[0])
243
- # check md5 to find same codebase
244
- if 'md5' in v_item.item.metadata['system'] and v_item.item.metadata['system']['md5'] == zip_md:
245
- same_version_found = v_item
246
- break
247
- return latest_version + 1, same_version_found
248
-
249
- def pack(self,
250
- directory: str,
251
- name: str = None,
252
- extension: str = 'zip',
253
- description: str = '',
254
- ignore_directories: List[str] = None,
255
- ignore_max_file_size: bool = False):
256
- """
257
- Zip a local code directory and post to codebases.
258
-
259
- **Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a package.
260
-
261
- :param str directory: local directory to pack
262
- :param str name: codebase name
263
- :param str extension: the extension of the file
264
- :param str description: codebase description
265
- :param list[str] ignore_directories: directories to ignore.
266
- :return: Codebase object
267
- :rtype: dtlpy.entities.codebase.Codebase
268
-
269
- **Example**:
270
-
271
- .. code-block:: python
272
-
273
- package.codebases.pack(directory='path_dir', name='codebase_name')
274
- """
275
- # create/get .dataloop dir
276
- cwd = os.getcwd()
277
- dl_dir = os.path.join(cwd, '.dataloop')
278
- if not os.path.isdir(dl_dir):
279
- os.mkdir(dl_dir)
280
-
281
- # get codebase name
282
- if name is None:
283
- name = os.path.basename(directory)
284
-
285
- # create/get dist folder
286
- zip_filename = os.path.join(dl_dir, '{}_{}.{}'.format(name, str(random.randrange(0, 1000)), extension))
287
-
288
- try:
289
- if not os.path.isdir(directory):
290
- raise PlatformException(error='400', message='Not a directory: {}'.format(directory))
291
- directory = os.path.abspath(directory)
292
-
293
- # create zipfile
294
- miscellaneous.Zipping.zip_directory(zip_filename=zip_filename,
295
- directory=directory,
296
- ignore_directories=ignore_directories,
297
- ignore_max_file_size=ignore_max_file_size)
298
- zip_md = self.__file_hash(zip_filename)
299
-
300
- # get latest version
301
- same_version_found = None
302
- try:
303
- all_versions_pages = self.get(codebase_name=name, version='all')
304
- except exceptions.NotFound:
305
- all_versions_pages = None
306
- if all_versions_pages is None:
307
- # no codebase with that name - create new version
308
- current_version = 0
309
- else:
310
- current_version, same_version_found = self.get_current_version(all_versions_pages=all_versions_pages,
311
- zip_md=zip_md)
312
-
313
- if same_version_found is not None:
314
- # same md5 hash file found in version - return the matched version
315
- codebase = same_version_found
316
- else:
317
- # no matched version was found - create a new version
318
- # read from zipped file
319
- with open(zip_filename, 'rb') as f:
320
- buffer = io.BytesIO(f.read())
321
- buffer.name = '{}.{}'.format(str(current_version), extension)
322
-
323
- # upload item
324
- item = self.items_repository.upload(local_path=buffer,
325
- remote_path='/codebases/{}'.format(name))
326
- if isinstance(item, list) and len(item) == 0:
327
- raise PlatformException(error='400', message='Failed upload codebase, check log file for details')
328
-
329
- # add source code to metadata
330
- if 'system' not in item.metadata:
331
- item.metadata['system'] = dict()
332
- item.metadata['system']['description'] = description
333
- item.metadata['system']['md5'] = zip_md
334
-
335
- # add git info to metadata
336
- if miscellaneous.GitUtils.is_git_repo(path=directory):
337
- # create 'git' field in metadata
338
- if 'git' not in item.metadata:
339
- item.metadata['git'] = dict()
340
-
341
- # add to metadata
342
- item.metadata['git']['status'] = miscellaneous.GitUtils.git_status(path=directory)
343
- item.metadata['git']['log'] = miscellaneous.GitUtils.git_log(path=directory)
344
- item.metadata['git']['url'] = miscellaneous.GitUtils.git_url(path=directory)
345
-
346
- # update item
347
- item = self.items_repository.update(item=item, system_metadata=True)
348
- codebase = entities.Codebase(type='item',
349
- client_api=self._client_api,
350
- itemId=item.id,
351
- item=item)
352
- except Exception:
353
- logger.error('Error when packing:')
354
- raise
355
- finally:
356
- # cleanup
357
- if zip_filename is not None:
358
- if os.path.isfile(zip_filename):
359
- os.remove(zip_filename)
360
- return codebase
361
-
362
- def _unpack_single(self,
363
- codebase,
364
- download_path: str,
365
- local_path: str):
366
- """
367
- :param dtlpy.entities.codebase.Codebase codebase: codebase object
368
- :param str download_path:
369
- :param str local_path:
370
- """
371
- # downloading with specific filename
372
- if isinstance(codebase, entities.ItemCodebase):
373
- artifact_filepath = self.items_repository.download(items=codebase.item_id,
374
- save_locally=True,
375
- local_path=os.path.join(download_path,
376
- codebase.item.name),
377
- to_items_folder=False)
378
- if not os.path.isfile(artifact_filepath):
379
- raise PlatformException(error='404',
380
- message='error downloading codebase. see above for more information')
381
- miscellaneous.Zipping.unzip_directory(zip_filename=artifact_filepath,
382
- to_directory=local_path)
383
- os.remove(artifact_filepath)
384
- logger.info('Source code was unpacked to: {}'.format(artifact_filepath))
385
- elif isinstance(codebase, entities.Item):
386
- artifact_filepath = codebase.download(save_locally=True,
387
- local_path=os.path.join(download_path,
388
- codebase.name),
389
- to_items_folder=False)
390
- if not os.path.isfile(artifact_filepath):
391
- raise PlatformException(error='404',
392
- message='error downloading codebase. see above for more information')
393
- miscellaneous.Zipping.unzip_directory(zip_filename=artifact_filepath,
394
- to_directory=local_path)
395
- os.remove(artifact_filepath)
396
- logger.info('Source code was unpacked to: {}'.format(artifact_filepath))
397
- elif isinstance(codebase, entities.GitCodebase):
398
- if codebase.is_git_repo(local_path) or \
399
- codebase.is_git_repo(os.path.join(local_path, codebase.git_repo_name)):
400
- artifact_filepath = self.pull_git(codebase=codebase, local_path=local_path)
401
- else: # Clone the repo if not exist
402
- artifact_filepath = self.clone_git(codebase=codebase, local_path=local_path)
403
- else:
404
- raise ValueError('Not implemented: "_unpack_single" for codebase type: {!r}'.format(codebase.type))
405
- return artifact_filepath
406
-
407
- def _get_single(self, codebase: entities.GitCodebase, name: str):
408
- value = None
409
- integration_id = codebase.credentials.get(name, {}).get('id', None)
410
- key = codebase.credentials.get(name, {}).get('key', None)
411
- if integration_id is not None:
412
- try:
413
- key = self.project.integrations.get(integrations_id=integration_id).name
414
- except Exception:
415
- pass
416
- value = os.environ.get(key, None)
417
-
418
- return value
419
-
420
- def _get_credential(self, codebase: entities.GitCodebase):
421
- username = password = None
422
-
423
- if codebase.credentials:
424
- try:
425
- username = self._get_single(codebase=codebase, name='username')
426
- password = self._get_single(codebase=codebase, name='password')
427
- except Exception:
428
- logger.exception('Failed to get credentials from codebase')
429
-
430
- return username, password
431
-
432
- def clone_git(self,
433
- codebase: entities.Codebase,
434
- local_path: str):
435
- """
436
- Clone code base
437
-
438
- **Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a package.
439
-
440
- :param dtlpy.entities.codebase.Codebase codebase: codebase object
441
- :param str local_path: local path
442
- :return: path where the clone will be
443
- :rtype: str
444
-
445
- **Example**:
446
-
447
- .. code-block:: python
448
-
449
- package.codebases.clone_git(codebase='codebase_entity', local_path='local_path')
450
- """
451
- if not isinstance(codebase, entities.GitCodebase):
452
- raise RuntimeError('only support Git Codebase')
453
- username, password = self._get_credential(codebase=codebase)
454
- response = self.git_utils.git_clone(path=local_path,
455
- git_url=codebase.git_url,
456
- tag=codebase.git_tag,
457
- username=username,
458
- password=password)
459
- if response:
460
- logger.info('Source code was cloned from {}(Git) to: {}'.format(codebase.git_url, local_path))
461
- else:
462
- raise RuntimeError('Failed cloning. See above for full log. codebase: {}'.format(codebase))
463
- return local_path
464
-
465
- def pull_git(self, codebase, local_path):
466
- """
467
- Pull (download) a codebase.
468
-
469
- **Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a package.
470
-
471
- :param dtlpy.entities.codebase.Codebase codebase: codebase object
472
- :param str local_path: local path
473
- :return: path where the Pull will be
474
- :rtype: str
475
-
476
- **Example**:
477
-
478
- .. code-block:: python
479
-
480
- package.codebases.pull_git(codebase='codebase_entity', local_path='local_path')
481
- """
482
- pull_cmd = 'git pull'
483
- if not codebase.is_git_repo(local_path):
484
- local_path = os.path.join(local_path, codebase.git_repo_name)
485
- response = self.git_utils.git_command(path=local_path, cmd=pull_cmd)
486
- if response:
487
- logger.info('pull successful {}(Git) to: {}'.format(codebase.git_url, os.path.dirname(local_path)))
488
- else:
489
- logger.critical("Could not pull")
490
-
491
- # we can test if this is not the same repo if needed...
492
- # FIXME need to change the order - checkout new branch and pull
493
- response_2 = self.git_utils.git_command(path=local_path, cmd='git checkout {}'.format(codebase.git_tag))
494
- return local_path
495
-
496
- def unpack(self,
497
- codebase: entities.Codebase = None,
498
- codebase_name: str = None,
499
- codebase_id: str = None,
500
- local_path: str = None,
501
- version: str = None):
502
- """
503
- Unpack codebase locally. Download source code and unzip.
504
-
505
- **Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a package.
506
-
507
- :param dtlpy.entities.codebase.Codebase codebase: `dl.Codebase` object
508
- :param str codebase_name: search by name
509
- :param str codebase_id: search by id
510
- :param str local_path: local path to save codebase
511
- :param str version: codebase version to unpack. default - latest
512
- :return: String (dirpath)
513
- :rtype: str
514
-
515
- **Example**:
516
-
517
- .. code-block:: python
518
-
519
- package.codebases.unpack(codebase='codebase_entity', local_path='local_path')
520
- """
521
- # get the codebase / multiple codebase
522
- if codebase is None:
523
- codebase = self.get(codebase_name=codebase_name,
524
- codebase_id=codebase_id,
525
- version=version)
526
- elif codebase_name is not None or codebase_id is not None:
527
- logger.warning("Using given codebase. Does not preforming search with name {!r} / id {!r}".
528
- format(codebase_name, codebase_id))
529
- download_path = local_path
530
- if isinstance(codebase, entities.PagedEntities):
531
- for page in codebase:
532
- for item in page:
533
- local_path = os.path.join(download_path, 'v.' + item.name.split('.')[0])
534
- self._unpack_single(codebase=item,
535
- download_path=download_path,
536
- local_path=local_path)
537
- return os.path.dirname(local_path)
538
- elif isinstance(codebase, list):
539
- for item in codebase:
540
- local_path = os.path.join(download_path, 'v.' + item.item.name.split('.')[0])
541
- self._unpack_single(codebase=item,
542
- download_path=download_path,
543
- local_path=local_path)
544
- return os.path.dirname(local_path)
545
- elif isinstance(codebase, (entities.ItemCodebase, entities.Item, entities.GitCodebase)):
546
- artifact_filepath = self._unpack_single(codebase=codebase,
547
- download_path=download_path,
548
- local_path=local_path)
549
- if isinstance(codebase, (entities.ItemCodebase, entities.Item)):
550
- dir_path = os.path.dirname(artifact_filepath) # use the directory of the artifact
551
- else:
552
- dir_path = artifact_filepath
553
- logger.info('Source code was unpacked to: {}'.format(dir_path))
554
- else:
555
- raise PlatformException(
556
- error='404',
557
- message='Codebase was not found! name:{name}, id:{id}'.format(name=codebase_name,
558
- id=codebase_id))
559
- return dir_path
1
+ import hashlib
2
+ import logging
3
+ import os
4
+ import io
5
+ import random
6
+ from typing import List
7
+
8
+ from .. import entities, PlatformException, exceptions, repositories, miscellaneous
9
+ from ..services.api_client import ApiClient
10
+ from ..services.api_client import client as client_api
11
+
12
+ logger = logging.getLogger(name='dtlpy')
13
+
14
+
15
+ class Codebases:
16
+ """
17
+ Codebase Repository
18
+
19
+ The Codebases class allows the user to manage codebases and their properties.
20
+ The codebase is the code the user uploads for the user's packages to run.
21
+ Read more about `codebase <https://developers.dataloop.ai/tutorials/faas/introduction/chapter/>`_ in our FaaS (function as a service).
22
+ """
23
+
24
+ def __init__(self,
25
+ client_api: ApiClient,
26
+ project: entities.Project = None,
27
+ dataset: entities.Dataset = None,
28
+ project_id: str = None):
29
+ self._client_api = client_api
30
+ self._project = project
31
+ self._project_id = project_id
32
+ self._dataset = dataset
33
+ self._items_repository = None
34
+ self.git_utils = miscellaneous.GitUtils()
35
+
36
+ @property
37
+ def items_repository(self) -> repositories.Items:
38
+ if self._items_repository is None:
39
+ if self._dataset is not None or self._project is not None:
40
+ self._items_repository = self.dataset.items
41
+ else:
42
+ self._items_repository = repositories.Items(client_api=client_api)
43
+ assert isinstance(self._items_repository, repositories.Items)
44
+ return self._items_repository
45
+
46
+ @property
47
+ def project(self) -> entities.Project:
48
+ if self._project is None:
49
+ # project is None - try dataset
50
+ if self._dataset is None:
51
+ # dataset is None - try from project id
52
+ if self._project_id is None:
53
+ raise PlatformException(error='400',
54
+ message='Cannot perform this action without a project')
55
+ else:
56
+ self._project = repositories.Projects(client_api=self._client_api).get(project_id=self._project_id)
57
+ else:
58
+ # dataset is not None - take project from there
59
+ self._project = self.dataset.project
60
+ assert isinstance(self._project, entities.Project)
61
+ return self._project
62
+
63
+ @property
64
+ def dataset(self) -> entities.Dataset:
65
+ if self._dataset is None:
66
+ # get dataset from project
67
+ try:
68
+ self._dataset = self.project.datasets._get_binaries_dataset()
69
+ except exceptions.NotFound:
70
+ raise ValueError('Missing "Binaries" dataset in the project. Please contact support for help')
71
+ assert isinstance(self._dataset, entities.Dataset)
72
+ return self._dataset
73
+
74
+ @dataset.setter
75
+ def dataset(self, dataset: entities.Dataset):
76
+ if not isinstance(dataset, entities.Dataset):
77
+ raise ValueError('Must input a valid Dataset entity')
78
+ self._dataset = dataset
79
+
80
+ @staticmethod
81
+ def __file_hash(filepath):
82
+ m = hashlib.md5()
83
+ with open(filepath, 'rb') as f:
84
+ for chunk in iter(lambda: f.read(4096), b''):
85
+ m.update(chunk)
86
+ return m.hexdigest()
87
+
88
+ def list_versions(self, codebase_name: str) -> entities.PagedEntities:
89
+ """
90
+ List all codebase versions.
91
+
92
+ **Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a package.
93
+
94
+ **Example**:
95
+
96
+ .. code-block:: python
97
+
98
+ package.codebases.list_versions(codebase_name='codebase_name')
99
+
100
+ :param str codebase_name: code base name
101
+ :return: list of versions
102
+ :rtype: list
103
+ """
104
+ filters = entities.Filters()
105
+ filters.add(field='filename', values='/codebases/{}/*'.format(codebase_name))
106
+ versions = self.items_repository.list(filters=filters)
107
+ return versions
108
+
109
+ def list(self) -> entities.PagedEntities:
110
+ """
111
+ List all codebases.
112
+
113
+ **Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a package.
114
+
115
+ **Example**:
116
+
117
+ .. code-block:: python
118
+
119
+ package.codebases.list()
120
+
121
+ :return: Paged entity
122
+ :rtype: dtlpy.entities.paged_entities.PagedEntities
123
+ """
124
+ filters = entities.Filters()
125
+ filters.add(field='filename', values='/codebases/*')
126
+ filters.add(field='type', values='dir')
127
+ codebases = self.items_repository.list(filters=filters)
128
+ return codebases
129
+
130
+ def get(self,
131
+ codebase_name: str = None,
132
+ codebase_id: str = None,
133
+ version: str = None):
134
+ """
135
+ Get a Codebase object to use in your code.
136
+
137
+ **Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a package.
138
+
139
+ **Example**:
140
+
141
+ .. code-block:: python
142
+
143
+ package.codebases.get(codebase_name='codebase_name')
144
+
145
+ :param str codebase_name: optional - search by name
146
+ :param str codebase_id: optional - search by id
147
+ :param str version: codebase version. default is latest. options: "all", "latest" or ver number - "10"
148
+ :return: Codebase object
149
+ """
150
+ if codebase_id is not None:
151
+ matched_version = self.items_repository.get(item_id=codebase_id)
152
+ # verify input codebase name is same as the given id
153
+ if codebase_name is not None and matched_version.name != codebase_name:
154
+ logger.warning(
155
+ "Mismatch found in codebases.get: codebase_name is different then codebase.name: "
156
+ "{!r} != {!r}".format(
157
+ codebase_name,
158
+ matched_version.name))
159
+ codebase = entities.Codebase(type='item',
160
+ client_api=self._client_api,
161
+ itemId=matched_version.id,
162
+ item=matched_version)
163
+ return codebase
164
+
165
+ if codebase_name is None:
166
+ raise PlatformException(error='400', message='Either "codebase_name" or "codebase_id" is needed')
167
+ if version is None:
168
+ version = 'latest'
169
+
170
+ if version not in ['all', 'latest']:
171
+ try:
172
+ matched_version = self.items_repository.get(
173
+ filepath='/codebases/{}/{}.zip'.format(codebase_name, version))
174
+ except Exception:
175
+ raise PlatformException(error='404',
176
+ message='No matching version was found. version: {}'.format(version))
177
+ codebase = entities.Codebase(type='item',
178
+ client_api=self._client_api,
179
+ itemId=matched_version.id,
180
+ item=matched_version)
181
+ return codebase
182
+
183
+ # get all or latest
184
+ versions_pages = self.list_versions(codebase_name=codebase_name)
185
+ if versions_pages.items_count == 0:
186
+ raise PlatformException(error='404', message='No codebase was found. name: {}'.format(codebase_name))
187
+ else:
188
+ if version == 'all':
189
+ codebase = [entities.Codebase(type='item',
190
+ client_api=self._client_api,
191
+ itemId=mv.id,
192
+ item=mv) for mv in versions_pages.all()]
193
+ elif version == 'latest':
194
+ max_ver = -1
195
+ matched_version = None
196
+ for page in versions_pages:
197
+ for ver in page:
198
+ if ver.type == 'dir':
199
+ continue
200
+ # extract version from filepath
201
+ ver_int = int(os.path.splitext(ver.name)[0])
202
+ if ver_int > max_ver:
203
+ max_ver = ver_int
204
+ matched_version = ver
205
+ if matched_version is None:
206
+ raise PlatformException(error='404',
207
+ message='No codebase was found. name: {}'.format(codebase_name))
208
+ else:
209
+ codebase = entities.Codebase(type='item',
210
+ client_api=self._client_api,
211
+ itemId=matched_version.id,
212
+ item=matched_version)
213
+ else:
214
+ raise PlatformException(error='404', message='Unknown version string: {}'.format(version))
215
+
216
+ return codebase
217
+
218
+ @staticmethod
219
+ def get_current_version(all_versions_pages, zip_md):
220
+ """
221
+ This method returns the current version of the codebase and other versions found.
222
+
223
+ **Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a package.
224
+
225
+ :param codebase all_versions_pages: codebase object
226
+ :param zip_md: zipped file of codebase
227
+ :return: current version and all versions found of codebase
228
+ :rtype: int, int
229
+
230
+ **Example**:
231
+
232
+ .. code-block:: python
233
+
234
+ package.codebases.get_current_version(all_versions_pages='codebase_entity', zip_md='path')
235
+ """
236
+ latest_version = 0
237
+ same_version_found = None
238
+ # go over all existing versions
239
+ for v_item in all_versions_pages:
240
+ # get latest version
241
+ if int(os.path.splitext(v_item.item.name)[0]) > latest_version:
242
+ latest_version = int(os.path.splitext(v_item.item.name)[0])
243
+ # check md5 to find same codebase
244
+ if 'md5' in v_item.item.metadata['system'] and v_item.item.metadata['system']['md5'] == zip_md:
245
+ same_version_found = v_item
246
+ break
247
+ return latest_version + 1, same_version_found
248
+
249
+ def pack(self,
250
+ directory: str,
251
+ name: str = None,
252
+ extension: str = 'zip',
253
+ description: str = '',
254
+ ignore_directories: List[str] = None,
255
+ ignore_max_file_size: bool = False):
256
+ """
257
+ Zip a local code directory and post to codebases.
258
+
259
+ **Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a package.
260
+
261
+ :param str directory: local directory to pack
262
+ :param str name: codebase name
263
+ :param str extension: the extension of the file
264
+ :param str description: codebase description
265
+ :param list[str] ignore_directories: directories to ignore.
266
+ :return: Codebase object
267
+ :rtype: dtlpy.entities.codebase.Codebase
268
+
269
+ **Example**:
270
+
271
+ .. code-block:: python
272
+
273
+ package.codebases.pack(directory='path_dir', name='codebase_name')
274
+ """
275
+ # create/get .dataloop dir
276
+ cwd = os.getcwd()
277
+ dl_dir = os.path.join(cwd, '.dataloop')
278
+ if not os.path.isdir(dl_dir):
279
+ os.mkdir(dl_dir)
280
+
281
+ # get codebase name
282
+ if name is None:
283
+ name = os.path.basename(directory)
284
+
285
+ # create/get dist folder
286
+ zip_filename = os.path.join(dl_dir, '{}_{}.{}'.format(name, str(random.randrange(0, 1000)), extension))
287
+
288
+ try:
289
+ if not os.path.isdir(directory):
290
+ raise PlatformException(error='400', message='Not a directory: {}'.format(directory))
291
+ directory = os.path.abspath(directory)
292
+
293
+ # create zipfile
294
+ miscellaneous.Zipping.zip_directory(zip_filename=zip_filename,
295
+ directory=directory,
296
+ ignore_directories=ignore_directories,
297
+ ignore_max_file_size=ignore_max_file_size)
298
+ zip_md = self.__file_hash(zip_filename)
299
+
300
+ # get latest version
301
+ same_version_found = None
302
+ try:
303
+ all_versions_pages = self.get(codebase_name=name, version='all')
304
+ except exceptions.NotFound:
305
+ all_versions_pages = None
306
+ if all_versions_pages is None:
307
+ # no codebase with that name - create new version
308
+ current_version = 0
309
+ else:
310
+ current_version, same_version_found = self.get_current_version(all_versions_pages=all_versions_pages,
311
+ zip_md=zip_md)
312
+
313
+ if same_version_found is not None:
314
+ # same md5 hash file found in version - return the matched version
315
+ codebase = same_version_found
316
+ else:
317
+ # no matched version was found - create a new version
318
+ # read from zipped file
319
+ with open(zip_filename, 'rb') as f:
320
+ buffer = io.BytesIO(f.read())
321
+ buffer.name = '{}.{}'.format(str(current_version), extension)
322
+
323
+ # upload item
324
+ item = self.items_repository.upload(local_path=buffer,
325
+ remote_path='/codebases/{}'.format(name))
326
+ if isinstance(item, list) and len(item) == 0:
327
+ raise PlatformException(error='400', message='Failed upload codebase, check log file for details')
328
+
329
+ # add source code to metadata
330
+ if 'system' not in item.metadata:
331
+ item.metadata['system'] = dict()
332
+ item.metadata['system']['description'] = description
333
+ item.metadata['system']['md5'] = zip_md
334
+
335
+ # add git info to metadata
336
+ if miscellaneous.GitUtils.is_git_repo(path=directory):
337
+ # create 'git' field in metadata
338
+ if 'git' not in item.metadata:
339
+ item.metadata['git'] = dict()
340
+
341
+ # add to metadata
342
+ item.metadata['git']['status'] = miscellaneous.GitUtils.git_status(path=directory)
343
+ item.metadata['git']['log'] = miscellaneous.GitUtils.git_log(path=directory)
344
+ item.metadata['git']['url'] = miscellaneous.GitUtils.git_url(path=directory)
345
+
346
+ # update item
347
+ item = self.items_repository.update(item=item, system_metadata=True)
348
+ codebase = entities.Codebase(type='item',
349
+ client_api=self._client_api,
350
+ itemId=item.id,
351
+ item=item)
352
+ except Exception:
353
+ logger.error('Error when packing:')
354
+ raise
355
+ finally:
356
+ # cleanup
357
+ if zip_filename is not None:
358
+ if os.path.isfile(zip_filename):
359
+ os.remove(zip_filename)
360
+ return codebase
361
+
362
+ def _unpack_single(self,
363
+ codebase,
364
+ download_path: str,
365
+ local_path: str):
366
+ """
367
+ :param dtlpy.entities.codebase.Codebase codebase: codebase object
368
+ :param str download_path:
369
+ :param str local_path:
370
+ """
371
+ # downloading with specific filename
372
+ if isinstance(codebase, entities.ItemCodebase):
373
+ artifact_filepath = self.items_repository.download(items=codebase.item_id,
374
+ save_locally=True,
375
+ local_path=os.path.join(download_path,
376
+ codebase.item.name),
377
+ to_items_folder=False)
378
+ if not os.path.isfile(artifact_filepath):
379
+ raise PlatformException(error='404',
380
+ message='error downloading codebase. see above for more information')
381
+ miscellaneous.Zipping.unzip_directory(zip_filename=artifact_filepath,
382
+ to_directory=local_path)
383
+ os.remove(artifact_filepath)
384
+ logger.info('Source code was unpacked to: {}'.format(artifact_filepath))
385
+ elif isinstance(codebase, entities.Item):
386
+ artifact_filepath = codebase.download(save_locally=True,
387
+ local_path=os.path.join(download_path,
388
+ codebase.name),
389
+ to_items_folder=False)
390
+ if not os.path.isfile(artifact_filepath):
391
+ raise PlatformException(error='404',
392
+ message='error downloading codebase. see above for more information')
393
+ miscellaneous.Zipping.unzip_directory(zip_filename=artifact_filepath,
394
+ to_directory=local_path)
395
+ os.remove(artifact_filepath)
396
+ logger.info('Source code was unpacked to: {}'.format(artifact_filepath))
397
+ elif isinstance(codebase, entities.GitCodebase):
398
+ if codebase.is_git_repo(local_path) or \
399
+ codebase.is_git_repo(os.path.join(local_path, codebase.git_repo_name)):
400
+ artifact_filepath = self.pull_git(codebase=codebase, local_path=local_path)
401
+ else: # Clone the repo if not exist
402
+ artifact_filepath = self.clone_git(codebase=codebase, local_path=local_path)
403
+ else:
404
+ raise ValueError('Not implemented: "_unpack_single" for codebase type: {!r}'.format(codebase.type))
405
+ return artifact_filepath
406
+
407
+ def _get_single(self, codebase: entities.GitCodebase, name: str):
408
+ value = None
409
+ integration_id = codebase.credentials.get(name, {}).get('id', None)
410
+ key = codebase.credentials.get(name, {}).get('key', None)
411
+ if integration_id is not None:
412
+ try:
413
+ key = self.project.integrations.get(integrations_id=integration_id).name
414
+ except Exception:
415
+ pass
416
+ value = os.environ.get(key, None)
417
+
418
+ return value
419
+
420
+ def _get_credential(self, codebase: entities.GitCodebase):
421
+ username = password = None
422
+
423
+ if codebase.credentials:
424
+ try:
425
+ username = self._get_single(codebase=codebase, name='username')
426
+ password = self._get_single(codebase=codebase, name='password')
427
+ except Exception:
428
+ logger.exception('Failed to get credentials from codebase')
429
+
430
+ return username, password
431
+
432
+ def clone_git(self,
433
+ codebase: entities.Codebase,
434
+ local_path: str):
435
+ """
436
+ Clone code base
437
+
438
+ **Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a package.
439
+
440
+ :param dtlpy.entities.codebase.Codebase codebase: codebase object
441
+ :param str local_path: local path
442
+ :return: path where the clone will be
443
+ :rtype: str
444
+
445
+ **Example**:
446
+
447
+ .. code-block:: python
448
+
449
+ package.codebases.clone_git(codebase='codebase_entity', local_path='local_path')
450
+ """
451
+ if not isinstance(codebase, entities.GitCodebase):
452
+ raise RuntimeError('only support Git Codebase')
453
+ username, password = self._get_credential(codebase=codebase)
454
+ response = self.git_utils.git_clone(path=local_path,
455
+ git_url=codebase.git_url,
456
+ tag=codebase.git_tag,
457
+ username=username,
458
+ password=password)
459
+ if response:
460
+ logger.info('Source code was cloned from {}(Git) to: {}'.format(codebase.git_url, local_path))
461
+ else:
462
+ raise RuntimeError('Failed cloning. See above for full log. codebase: {}'.format(codebase))
463
+ return local_path
464
+
465
+ def pull_git(self, codebase, local_path):
466
+ """
467
+ Pull (download) a codebase.
468
+
469
+ **Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a package.
470
+
471
+ :param dtlpy.entities.codebase.Codebase codebase: codebase object
472
+ :param str local_path: local path
473
+ :return: path where the Pull will be
474
+ :rtype: str
475
+
476
+ **Example**:
477
+
478
+ .. code-block:: python
479
+
480
+ package.codebases.pull_git(codebase='codebase_entity', local_path='local_path')
481
+ """
482
+ pull_cmd = 'git pull'
483
+ if not codebase.is_git_repo(local_path):
484
+ local_path = os.path.join(local_path, codebase.git_repo_name)
485
+ response = self.git_utils.git_command(path=local_path, cmd=pull_cmd)
486
+ if response:
487
+ logger.info('pull successful {}(Git) to: {}'.format(codebase.git_url, os.path.dirname(local_path)))
488
+ else:
489
+ logger.critical("Could not pull")
490
+
491
+ # we can test if this is not the same repo if needed...
492
+ # FIXME need to change the order - checkout new branch and pull
493
+ response_2 = self.git_utils.git_command(path=local_path, cmd='git checkout {}'.format(codebase.git_tag))
494
+ return local_path
495
+
496
+ def unpack(self,
497
+ codebase: entities.Codebase = None,
498
+ codebase_name: str = None,
499
+ codebase_id: str = None,
500
+ local_path: str = None,
501
+ version: str = None):
502
+ """
503
+ Unpack codebase locally. Download source code and unzip.
504
+
505
+ **Prerequisites**: You must be in the role of an *owner* or *developer*. You must have a package.
506
+
507
+ :param dtlpy.entities.codebase.Codebase codebase: `dl.Codebase` object
508
+ :param str codebase_name: search by name
509
+ :param str codebase_id: search by id
510
+ :param str local_path: local path to save codebase
511
+ :param str version: codebase version to unpack. default - latest
512
+ :return: String (dirpath)
513
+ :rtype: str
514
+
515
+ **Example**:
516
+
517
+ .. code-block:: python
518
+
519
+ package.codebases.unpack(codebase='codebase_entity', local_path='local_path')
520
+ """
521
+ # get the codebase / multiple codebase
522
+ if codebase is None:
523
+ codebase = self.get(codebase_name=codebase_name,
524
+ codebase_id=codebase_id,
525
+ version=version)
526
+ elif codebase_name is not None or codebase_id is not None:
527
+ logger.warning("Using given codebase. Does not preforming search with name {!r} / id {!r}".
528
+ format(codebase_name, codebase_id))
529
+ download_path = local_path
530
+ if isinstance(codebase, entities.PagedEntities):
531
+ for page in codebase:
532
+ for item in page:
533
+ local_path = os.path.join(download_path, 'v.' + item.name.split('.')[0])
534
+ self._unpack_single(codebase=item,
535
+ download_path=download_path,
536
+ local_path=local_path)
537
+ return os.path.dirname(local_path)
538
+ elif isinstance(codebase, list):
539
+ for item in codebase:
540
+ local_path = os.path.join(download_path, 'v.' + item.item.name.split('.')[0])
541
+ self._unpack_single(codebase=item,
542
+ download_path=download_path,
543
+ local_path=local_path)
544
+ return os.path.dirname(local_path)
545
+ elif isinstance(codebase, (entities.ItemCodebase, entities.Item, entities.GitCodebase)):
546
+ artifact_filepath = self._unpack_single(codebase=codebase,
547
+ download_path=download_path,
548
+ local_path=local_path)
549
+ if isinstance(codebase, (entities.ItemCodebase, entities.Item)):
550
+ dir_path = os.path.dirname(artifact_filepath) # use the directory of the artifact
551
+ else:
552
+ dir_path = artifact_filepath
553
+ logger.info('Source code was unpacked to: {}'.format(dir_path))
554
+ else:
555
+ raise PlatformException(
556
+ error='404',
557
+ message='Codebase was not found! name:{name}, id:{id}'.format(name=codebase_name,
558
+ id=codebase_id))
559
+ return dir_path