dtlpy 1.85.25__py3-none-any.whl → 1.87.18__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 (49) hide show
  1. dtlpy/__init__.py +3 -3
  2. dtlpy/__version__.py +1 -1
  3. dtlpy/entities/__init__.py +1 -1
  4. dtlpy/entities/annotation.py +10 -12
  5. dtlpy/entities/annotation_collection.py +11 -9
  6. dtlpy/entities/annotation_definitions/__init__.py +2 -1
  7. dtlpy/entities/annotation_definitions/ref_image.py +86 -0
  8. dtlpy/entities/command.py +1 -1
  9. dtlpy/entities/dataset.py +4 -8
  10. dtlpy/entities/feature_set.py +0 -3
  11. dtlpy/entities/filters.py +12 -2
  12. dtlpy/entities/item.py +0 -1
  13. dtlpy/entities/model.py +51 -2
  14. dtlpy/entities/node.py +14 -5
  15. dtlpy/entities/ontology.py +2 -2
  16. dtlpy/entities/package_function.py +3 -0
  17. dtlpy/entities/pipeline.py +11 -2
  18. dtlpy/entities/recipe.py +1 -1
  19. dtlpy/entities/service.py +33 -16
  20. dtlpy/entities/task.py +18 -1
  21. dtlpy/entities/trigger.py +7 -1
  22. dtlpy/ml/base_model_adapter.py +56 -11
  23. dtlpy/ml/train_utils.py +0 -1
  24. dtlpy/new_instance.py +5 -3
  25. dtlpy/repositories/artifacts.py +9 -15
  26. dtlpy/repositories/codebases.py +2 -14
  27. dtlpy/repositories/commands.py +6 -7
  28. dtlpy/repositories/datasets.py +73 -43
  29. dtlpy/repositories/downloader.py +1 -1
  30. dtlpy/repositories/feature_sets.py +1 -6
  31. dtlpy/repositories/models.py +69 -26
  32. dtlpy/repositories/packages.py +5 -4
  33. dtlpy/repositories/pipelines.py +5 -4
  34. dtlpy/repositories/services.py +32 -5
  35. dtlpy/repositories/tasks.py +8 -3
  36. dtlpy/repositories/uploader.py +1 -1
  37. dtlpy/services/api_client.py +2 -1
  38. dtlpy/utilities/dataset_generators/dataset_generator.py +2 -2
  39. dtlpy/utilities/reports/figures.py +215 -48
  40. {dtlpy-1.85.25.dist-info → dtlpy-1.87.18.dist-info}/METADATA +1 -2
  41. {dtlpy-1.85.25.dist-info → dtlpy-1.87.18.dist-info}/RECORD +49 -48
  42. tests/features/environment.py +49 -2
  43. {dtlpy-1.85.25.data → dtlpy-1.87.18.data}/scripts/dlp +0 -0
  44. {dtlpy-1.85.25.data → dtlpy-1.87.18.data}/scripts/dlp.bat +0 -0
  45. {dtlpy-1.85.25.data → dtlpy-1.87.18.data}/scripts/dlp.py +0 -0
  46. {dtlpy-1.85.25.dist-info → dtlpy-1.87.18.dist-info}/LICENSE +0 -0
  47. {dtlpy-1.85.25.dist-info → dtlpy-1.87.18.dist-info}/WHEEL +0 -0
  48. {dtlpy-1.85.25.dist-info → dtlpy-1.87.18.dist-info}/entry_points.txt +0 -0
  49. {dtlpy-1.85.25.dist-info → dtlpy-1.87.18.dist-info}/top_level.txt +0 -0
dtlpy/entities/service.py CHANGED
@@ -80,8 +80,6 @@ class InstanceCatalog(str, Enum):
80
80
  - regular pod with medium size
81
81
  * - REGULAR_L
82
82
  - regular pod with large size
83
- * - REGULAR_XL
84
- - regular pod with extra large size
85
83
  * - HIGHMEM_XS
86
84
  - highmem pod with extra small size
87
85
  * - HIGHMEM_S
@@ -90,25 +88,27 @@ class InstanceCatalog(str, Enum):
90
88
  - highmem pod with medium size
91
89
  * - HIGHMEM_L
92
90
  - highmem pod with large size
93
- * - HIGHMEM_XL
94
- - highmem pod with extra large size
95
91
  * - GPU_K80_S
96
- - GPU pod with small size
92
+ - GPU NVIDIA K80 pod with small size
97
93
  * - GPU_K80_M
98
- - GPU pod with medium size
94
+ - GPU NVIDIA K80 pod with medium size
95
+ * - GPU_T4_S
96
+ - GPU NVIDIA T4 pod with regular memory
97
+ * - GPU_T4_M
98
+ - GPU NVIDIA T4 pod with highmem
99
99
  """
100
100
  REGULAR_XS = "regular-xs"
101
101
  REGULAR_S = "regular-s"
102
102
  REGULAR_M = "regular-m"
103
103
  REGULAR_L = "regular-l"
104
- REGULAR_XL = "regular-xl"
105
104
  HIGHMEM_XS = "highmem-xs"
106
105
  HIGHMEM_S = "highmem-s"
107
106
  HIGHMEM_M = "highmem-m"
108
107
  HIGHMEM_L = "highmem-l"
109
- HIGHMEM_XL = "highmem-xl"
110
108
  GPU_K80_S = "gpu-k80-s"
111
109
  GPU_K80_M = "gpu-k80-m"
110
+ GPU_T4_S = "gpu-t4"
111
+ GPU_T4_M = "gpu-t4-m"
112
112
 
113
113
 
114
114
  class RuntimeType(str, Enum):
@@ -150,7 +150,7 @@ class KubernetesRuntime(ServiceRuntime):
150
150
  self.concurrency = kwargs.get('concurrency', concurrency)
151
151
  self.runner_image = kwargs.get('runnerImage', runner_image)
152
152
  self._proxy_image = kwargs.get('proxyImage', None)
153
- self.single_agent = kwargs.get('singleAgent', False)
153
+ self.single_agent = kwargs.get('singleAgent', None)
154
154
  self.preemptible = kwargs.get('preemptible', None)
155
155
 
156
156
  self.autoscaler = kwargs.get('autoscaler', autoscaler)
@@ -166,10 +166,12 @@ class KubernetesRuntime(ServiceRuntime):
166
166
  'podType': self.pod_type,
167
167
  'numReplicas': self.num_replicas,
168
168
  'concurrency': self.concurrency,
169
- 'singleAgent': self.single_agent,
170
169
  'autoscaler': None if self.autoscaler is None else self.autoscaler.to_json()
171
170
  }
172
171
 
172
+ if self.single_agent is not None:
173
+ _json['singleAgent'] = self.single_agent
174
+
173
175
  if self.runner_image is not None:
174
176
  _json['runnerImage'] = self.runner_image
175
177
 
@@ -221,6 +223,7 @@ class Service(entities.BaseEntity):
221
223
  max_attempts = attr.ib()
222
224
  mode = attr.ib(repr=False)
223
225
  metadata = attr.ib()
226
+ archive = attr.ib(repr=False)
224
227
 
225
228
  # SDK
226
229
  _package = attr.ib(repr=False)
@@ -229,6 +232,7 @@ class Service(entities.BaseEntity):
229
232
  # repositories
230
233
  _project = attr.ib(default=None, repr=False)
231
234
  _repositories = attr.ib(repr=False)
235
+ updated_by = attr.ib(default=None)
232
236
 
233
237
  @property
234
238
  def createdAt(self):
@@ -263,7 +267,7 @@ class Service(entities.BaseEntity):
263
267
  return status, service
264
268
 
265
269
  @classmethod
266
- def from_json(cls, _json: dict, client_api: ApiClient=None, package=None, project=None, is_fetched=True):
270
+ def from_json(cls, _json: dict, client_api: ApiClient = None, package=None, project=None, is_fetched=True):
267
271
  """
268
272
  Build a service entity object from a json
269
273
 
@@ -323,7 +327,9 @@ class Service(entities.BaseEntity):
323
327
  secrets=_json.get("secrets", None),
324
328
  type=_json.get("type", None),
325
329
  mode=_json.get('mode', dict()),
326
- metadata=_json.get('metadata', None)
330
+ metadata=_json.get('metadata', None),
331
+ archive=_json.get('archive', None),
332
+ updated_by=_json.get('updatedBy', None)
327
333
  )
328
334
  inst.is_fetched = is_fetched
329
335
  return inst
@@ -352,9 +358,13 @@ class Service(entities.BaseEntity):
352
358
  @property
353
359
  def package(self):
354
360
  if self._package is None:
355
- self._package = repositories.Packages(client_api=self._client_api).get(package_id=self.package_id,
356
- fetch=None)
357
- assert isinstance(self._package, entities.Package)
361
+ try:
362
+ self._package = repositories.Packages(client_api=self._client_api).get(package_id=self.package_id,
363
+ fetch=None)
364
+ assert isinstance(self._package, entities.Package)
365
+ except:
366
+ self._package = repositories.Dpks(client_api=self._client_api).get(dpk_id=self.package_id)
367
+ assert isinstance(self._package, entities.Dpk)
358
368
  return self._package
359
369
 
360
370
  @property
@@ -443,7 +453,9 @@ class Service(entities.BaseEntity):
443
453
  attr.fields(Service).secrets,
444
454
  attr.fields(Service)._type,
445
455
  attr.fields(Service).mode,
446
- attr.fields(Service).metadata
456
+ attr.fields(Service).metadata,
457
+ attr.fields(Service).archive,
458
+ attr.fields(Service).updated_by,
447
459
  )
448
460
  )
449
461
 
@@ -463,6 +475,9 @@ class Service(entities.BaseEntity):
463
475
  _json['createdAt'] = self.created_at
464
476
  _json['updatedAt'] = self.updated_at
465
477
 
478
+ if self.updated_by is not None:
479
+ _json['updatedBy'] = self.updated_by
480
+
466
481
  if self.max_attempts is not None:
467
482
  _json['maxAttempts'] = self.max_attempts
468
483
 
@@ -487,6 +502,8 @@ class Service(entities.BaseEntity):
487
502
  if self.metadata:
488
503
  _json['metadata'] = self.metadata
489
504
 
505
+ if self.archive:
506
+ _json['archive'] = self.archive
490
507
  return _json
491
508
 
492
509
  def update(self, force=False):
dtlpy/entities/task.py CHANGED
@@ -92,6 +92,7 @@ class Task:
92
92
  available_actions = attr.ib()
93
93
  total_items = attr.ib()
94
94
  priority = attr.ib()
95
+ _description = attr.ib()
95
96
 
96
97
  # sdk
97
98
  _client_api = attr.ib(repr=False)
@@ -102,6 +103,18 @@ class Task:
102
103
  _tasks = attr.ib(default=None, repr=False)
103
104
  _settings = attr.ib(default=None, repr=False)
104
105
 
106
+ @property
107
+ def description(self):
108
+ return self._description
109
+
110
+ @description.setter
111
+ def description(self, description):
112
+ if not isinstance(description, str):
113
+ raise ValueError('description should be a string')
114
+ if self._description is None:
115
+ self._description = {}
116
+ self._description['content'] = description
117
+
105
118
  @staticmethod
106
119
  def _protected_from_json(_json, client_api, project, dataset):
107
120
  """
@@ -176,7 +189,8 @@ class Task:
176
189
  created_at=_json.get('createdAt', None),
177
190
  available_actions=actions,
178
191
  total_items=_json.get('totalItems', None),
179
- priority=_json.get('priority', None)
192
+ priority=_json.get('priority', None),
193
+ description=_json.get('description', None)
180
194
  )
181
195
 
182
196
  def to_json(self):
@@ -208,6 +222,7 @@ class Task:
208
222
  attr.fields(Task).created_at,
209
223
  attr.fields(Task).total_items,
210
224
  attr.fields(Task)._settings,
225
+ attr.fields(Task)._description
211
226
  )
212
227
  )
213
228
  _json['projectId'] = self.project_id
@@ -217,6 +232,7 @@ class Task:
217
232
  _json['dueDate'] = self.due_date
218
233
  _json['totalItems'] = self.total_items
219
234
  _json['forReview'] = self.for_review
235
+ _json['description'] = self.description
220
236
 
221
237
  if self.available_actions is not None:
222
238
  _json['availableActions'] = [action.to_json() for action in self.available_actions]
@@ -472,3 +488,4 @@ class Task:
472
488
  :rtype: bool
473
489
  """
474
490
  return self.tasks.set_status(status=status, operation=operation, item_ids=item_ids, task_id=self.id)
491
+
dtlpy/entities/trigger.py CHANGED
@@ -84,6 +84,8 @@ class BaseTrigger(entities.BaseEntity):
84
84
  _op_type = attr.ib(default='service')
85
85
  _repositories = attr.ib(repr=False)
86
86
 
87
+ updated_by = attr.ib(default=None)
88
+
87
89
  @staticmethod
88
90
  def _get_operation(operation):
89
91
  op_type = operation.get('type', None)
@@ -243,6 +245,7 @@ class BaseTrigger(entities.BaseEntity):
243
245
  attr.fields(BaseTrigger).created_at,
244
246
  attr.fields(BaseTrigger).updated_at,
245
247
  attr.fields(BaseTrigger).operation,
248
+ attr.fields(BaseTrigger).updated_by,
246
249
  ))
247
250
 
248
251
  # rename
@@ -251,6 +254,8 @@ class BaseTrigger(entities.BaseEntity):
251
254
  _json['updatedAt'] = self.updated_at
252
255
  if self.is_global is not None:
253
256
  _json['global'] = self.is_global
257
+ if self.updated_by is not None:
258
+ _json['updatedBy'] = self.updated_by
254
259
  return _json
255
260
 
256
261
  def delete(self):
@@ -342,7 +347,8 @@ class Trigger(BaseTrigger):
342
347
  op_type=operation.get('type', None),
343
348
  spec=spec,
344
349
  pipeline_id=pipeline_id,
345
- operation=operation
350
+ operation=operation,
351
+ updated_by=_json.get('updatedBy', None),
346
352
  )
347
353
 
348
354
 
@@ -1,10 +1,10 @@
1
- import copy
2
1
  import tempfile
3
2
  import datetime
4
3
  import logging
5
4
  import shutil
6
5
  import base64
7
6
  import tqdm
7
+ import sys
8
8
  import io
9
9
  import os
10
10
  from PIL import Image
@@ -12,7 +12,7 @@ from functools import partial
12
12
  import numpy as np
13
13
  from concurrent.futures import ThreadPoolExecutor
14
14
  import attr
15
- from .. import entities, utilities, repositories
15
+ from .. import entities, utilities, repositories, exceptions
16
16
  from ..services import service_defaults
17
17
  from ..services.api_client import ApiClient
18
18
 
@@ -36,6 +36,8 @@ class BaseModelAdapter(utilities.BaseServiceRunner):
36
36
  'image': self._item_to_image}
37
37
  if model_entity is not None:
38
38
  self.load_from_model(model_entity=model_entity)
39
+ logger.warning(
40
+ "in case of a mismatch between 'model.name' and 'model_info.name' in the model adapter, model_info.name will be updated to align with 'model.name'.")
39
41
 
40
42
  ##################
41
43
  # Configurations #
@@ -307,8 +309,6 @@ class BaseModelAdapter(utilities.BaseServiceRunner):
307
309
  if cleanup:
308
310
  shutil.rmtree(path=local_path, ignore_errors=True)
309
311
  self.logger.info("Clean-up. deleting {}".format(local_path))
310
- self.model_entity.status = 'trained'
311
- self.model_entity = self.model_entity.update()
312
312
 
313
313
  # ===============
314
314
  # SERVICE METHODS
@@ -337,10 +337,15 @@ class BaseModelAdapter(utilities.BaseServiceRunner):
337
337
  pool = ThreadPoolExecutor(max_workers=16)
338
338
 
339
339
  annotations = list()
340
- for i_batch in tqdm.tqdm(range(0, len(items), batch_size), desc='predicting', unit='bt', leave=None):
340
+ for i_batch in tqdm.tqdm(range(0, len(items), batch_size), desc='predicting', unit='bt', leave=None, file=sys.stdout):
341
341
  batch_items = items[i_batch: i_batch + batch_size]
342
342
  batch = list(pool.map(self.prepare_item_func, batch_items))
343
343
  batch_collections = self.predict(batch, **kwargs)
344
+ _futures = list(pool.map(partial(self._update_predictions_metadata),
345
+ batch_items,
346
+ batch_collections))
347
+ # Loop over the futures to make sure they are all done to avoid race conditions
348
+ _ = [_f for _f in _futures]
344
349
  if upload_annotations is True:
345
350
  self.logger.debug(
346
351
  "Uploading items' annotation for model {!r}.".format(self.model_entity.name))
@@ -408,12 +413,11 @@ class BaseModelAdapter(utilities.BaseServiceRunner):
408
413
  cleanup=False,
409
414
  progress: utilities.Progress = None,
410
415
  context: utilities.Context = None):
411
- # FROM PARENT
412
416
  """
413
- Train on existing model.
414
- data will be taken from dl.Model.datasetId
415
- configuration is as defined in dl.Model.configuration
416
- upload the output the model's bucket (model.bucket)
417
+ Train on existing model.
418
+ data will be taken from dl.Model.datasetId
419
+ configuration is as defined in dl.Model.configuration
420
+ upload the output the model's bucket (model.bucket)
417
421
  """
418
422
  if isinstance(model, dict):
419
423
  model = repositories.Models(client_api=self._client_api).get(model_id=model['id'])
@@ -462,7 +466,8 @@ class BaseModelAdapter(utilities.BaseServiceRunner):
462
466
  progress=99)
463
467
 
464
468
  self.save_to_model(local_path=output_path, replace=True)
465
-
469
+ model.status = 'trained'
470
+ model.update()
466
471
  ###########
467
472
  # cleanup #
468
473
  ###########
@@ -597,6 +602,46 @@ class BaseModelAdapter(utilities.BaseServiceRunner):
597
602
  image = np.asarray(Image.open(io.BytesIO(binary)))
598
603
  return image
599
604
 
605
+ def _update_predictions_metadata(self, item: entities.Item, predictions: entities.AnnotationCollection):
606
+ """
607
+ add model_name and model_id to the metadata of the annotations.
608
+ add model_info to the metadata of the system metadata of the annotation.
609
+ Add item id to all the annotations in the AnnotationCollection
610
+
611
+ :param item: Entity.Item
612
+ :param predictions: item's AnnotationCollection
613
+ :return:
614
+ """
615
+ for prediction in predictions:
616
+ if prediction.type == entities.AnnotationType.SEGMENTATION:
617
+ try:
618
+ color = self.model_entity.dataset._get_ontology().color_map.get(prediction.label)
619
+ except (exceptions.BadRequest, exceptions.NotFound):
620
+ color = None
621
+ logger.warning("Can't get annotation color from item's dataset, using model's dataset.")
622
+ if color is None:
623
+ try:
624
+ color = self.model_entity.dataset._get_ontology().color_map.get(prediction.label)
625
+ except (exceptions.BadRequest, exceptions.NotFound):
626
+ logger.warning("Can't get annotation color from model's dataset, using default.")
627
+ color = prediction.color
628
+ prediction.color = color
629
+
630
+ prediction.item_id = item.id
631
+ if 'user' in prediction.metadata and 'model' in prediction.metadata['user']:
632
+ prediction.metadata['user']['model']['model_id'] = self.model_entity.id
633
+ prediction.metadata['user']['model']['name'] = self.model_entity.name
634
+ if 'system' not in prediction.metadata:
635
+ prediction.metadata['system'] = dict()
636
+ if 'model' not in prediction.metadata['system']:
637
+ prediction.metadata['system']['model'] = dict()
638
+ confidence = prediction.metadata.get('user', dict()).get('model', dict()).get('confidence', None)
639
+ prediction.metadata['system']['model'] = {
640
+ 'model_id': self.model_entity.id,
641
+ 'name': self.model_entity.name,
642
+ 'confidence': confidence
643
+ }
644
+
600
645
  ##############################
601
646
  # Callback Factory functions #
602
647
  ##############################
dtlpy/ml/train_utils.py CHANGED
@@ -58,4 +58,3 @@ def prepare_dataset(dataset: entities.Dataset,
58
58
  cloned_dataset.metadata['system']['clone_info'].update({'filters': json.dumps(filters.prepare())})
59
59
  cloned_dataset.update(system_metadata=True)
60
60
  return cloned_dataset
61
- # cloned_dataset.set_readonly(True)
dtlpy/new_instance.py CHANGED
@@ -166,14 +166,16 @@ class Dtlpy:
166
166
  REGULAR_S = 'regular-s'
167
167
  REGULAR_M = 'regular-m'
168
168
  REGULAR_L = 'regular-l'
169
- REGULAR_XL = 'regular-xl'
170
169
  HIGHMEM_MICRO = 'highmem-micro'
171
170
  HIGHMEM_XS = 'highmem-xs'
172
171
  HIGHMEM_S = 'highmem-s'
173
172
  HIGHMEM_M = 'highmem-m'
174
173
  HIGHMEM_L = 'highmem-l'
175
- HIGHMEM_XL = 'highmem-xl'
176
- GPU_K80_S = 'gpu-k80-s'
174
+ GPU_K80_S = "gpu-k80-s"
175
+ GPU_K80_M = "gpu-k80-m"
176
+ GPU_T4_S = "gpu-t4-s"
177
+ GPU_T4_M = "gpu-t4-m"
178
+
177
179
 
178
180
  class LoggingLevel:
179
181
  DEBUG = 'debug'
@@ -22,7 +22,7 @@ class Artifacts:
22
22
  project_id: str = None,
23
23
  model: entities.Model = None,
24
24
  package: entities.Package = None,
25
- dataset_name='Binaries'):
25
+ dataset_name=None):
26
26
  self._client_api = client_api
27
27
  self._project = project
28
28
  self._dataset = dataset
@@ -40,21 +40,15 @@ class Artifacts:
40
40
  if self._dataset is None:
41
41
  # get dataset from project
42
42
  try:
43
- self._dataset = self.project.datasets.get(dataset_name=self.dataset_name)
43
+ if self.dataset_name is None:
44
+ self.dataset_name = 'Binaries'
45
+ self._dataset = self.project.datasets._get_binaries_dataset()
46
+ else:
47
+ self._dataset = self.project.datasets.get(dataset_name=self.dataset_name)
44
48
  except exceptions.NotFound:
45
- self._dataset = None
46
- if self._dataset is None:
47
- logger.debug(
48
- 'Dataset for artifacts was not found. Creating... dataset name: {ds!r}. project_id={id}'.format(
49
- ds=self.dataset_name, id=self.project.id))
50
- self._dataset = self.project.datasets.create(dataset_name=self.dataset_name)
51
- # add system to metadata
52
- if 'metadata' not in self._dataset.to_json():
53
- self._dataset.metadata = dict()
54
- if 'system' not in self._dataset.metadata:
55
- self._dataset.metadata['system'] = dict()
56
- self._dataset.metadata['system']['scope'] = 'system'
57
- self.project.datasets.update(dataset=self._dataset, system_metadata=True)
49
+ raise ValueError(
50
+ f'Missing "{self.dataset_name}" dataset in the project. Please contact support for help')
51
+
58
52
  return self._dataset
59
53
 
60
54
  @property
@@ -65,21 +65,9 @@ class Codebases:
65
65
  if self._dataset is None:
66
66
  # get dataset from project
67
67
  try:
68
- self._dataset = self.project.datasets.get(dataset_name='Binaries')
68
+ self._dataset = self.project.datasets._get_binaries_dataset()
69
69
  except exceptions.NotFound:
70
- self._dataset = None
71
- if self._dataset is None:
72
- logger.debug(
73
- 'Dataset for codebases was not found. Creating... dataset name: "Binaries". project_id={}'.format(
74
- self.project.id))
75
- self._dataset = self.project.datasets.create(dataset_name='Binaries')
76
- # add system to metadata
77
- if 'metadata' not in self._dataset.to_json():
78
- self._dataset.metadata = dict()
79
- if 'system' not in self._dataset.metadata:
80
- self._dataset.metadata['system'] = dict()
81
- self._dataset.metadata['system']['scope'] = 'system'
82
- self.project.datasets.update(dataset=self._dataset, system_metadata=True)
70
+ raise ValueError('Missing "Binaries" dataset in the project. Please contact support for help')
83
71
  assert isinstance(self._dataset, entities.Dataset)
84
72
  return self._dataset
85
73
 
@@ -1,5 +1,4 @@
1
1
  import numpy as np
2
- import warnings
3
2
  import logging
4
3
  import time
5
4
  import tqdm
@@ -10,7 +9,7 @@ from ..services.api_client import ApiClient
10
9
 
11
10
  logger = logging.getLogger(name='dtlpy')
12
11
 
13
- MAX_SLEEP_TIME = 8
12
+ MAX_SLEEP_TIME = 30
14
13
 
15
14
 
16
15
  class Commands:
@@ -71,15 +70,15 @@ class Commands:
71
70
  return entities.Command.from_json(client_api=self._client_api,
72
71
  _json=response.json())
73
72
 
74
- def wait(self, command_id, timeout=0, step=None, url=None, backoff_factor=0.1):
73
+ def wait(self, command_id, timeout=0, step=None, url=None, backoff_factor=1):
75
74
  """
76
75
  Wait for command to finish
77
76
 
78
77
  backoff_factor: A backoff factor to apply between attempts after the second try
79
78
  {backoff factor} * (2 ** ({number of total retries} - 1))
80
- seconds. If the backoff_factor is 0.1, then :func:`.sleep` will sleep
81
- for [0.0s, 0.2s, 0.4s, ...] between retries. It will never be longer
82
- than 8 sec
79
+ seconds. If the backoff_factor is 1, then :func:`.sleep` will sleep
80
+ for [0s, 2s, 4s, ...] between retries. It will never be longer
81
+ than 30 sec
83
82
 
84
83
  :param str command_id: Command id to wait to
85
84
  :param int timeout: int, seconds to wait until TimeoutError is raised. if 0 - wait until done
@@ -103,7 +102,7 @@ class Commands:
103
102
  if not command.in_progress():
104
103
  break
105
104
  elapsed = time.time() - start
106
- sleep_time = np.min([timeout - elapsed, backoff_factor * (2 ** (num_tries - 1)), MAX_SLEEP_TIME])
105
+ sleep_time = np.min([timeout - elapsed, backoff_factor * (2 ** num_tries), MAX_SLEEP_TIME])
107
106
  num_tries += 1
108
107
  logger.debug("Command {!r} is running for {:.2f}[s] and now Going to sleep {:.2f}[s]".format(command.id,
109
108
  elapsed,
@@ -96,6 +96,21 @@ class Datasets:
96
96
  filters.add(field='dir', values=folder_path + '*')
97
97
  return filters
98
98
 
99
+ def _get_binaries_dataset(self):
100
+ filters = entities.Filters(resource=entities.FiltersResource.DATASET)
101
+ filters.add(field='name', values='Binaries')
102
+ filters.system_space = True
103
+ datasets = self.list(filters=filters)
104
+ if len(datasets) == 0:
105
+ # empty list
106
+ raise exceptions.PlatformException('404', 'Dataset not found. Name: "Binaries"')
107
+ # dataset = None
108
+ elif len(datasets) > 1:
109
+ raise exceptions.PlatformException('400', 'More than one dataset with same name.')
110
+ else:
111
+ dataset = datasets[0]
112
+ return dataset
113
+
99
114
  @property
100
115
  def platform_url(self):
101
116
  return self._client_api._get_resource_url("projects/{}/datasets".format(self.project.id))
@@ -165,58 +180,78 @@ class Datasets:
165
180
  self._client_api.state_io.put('dataset', dataset.to_json())
166
181
  logger.info('Checked out to dataset {}'.format(dataset.name))
167
182
 
168
- @_api_reference.add(path='/datasets', method='get')
169
- def list(self, name=None, creator=None) -> miscellaneous.List[entities.Dataset]:
183
+ @_api_reference.add(path='/datasets/query', method='post')
184
+ def list(self, name=None, creator=None, filters: entities.Filters = None) -> miscellaneous.List[entities.Dataset]:
170
185
  """
171
186
  List all datasets.
172
187
 
173
188
  **Prerequisites**: You must be an *owner* or *developer* to use this method.
174
189
 
175
190
  :param str name: list by name
176
- :param str creator: list by creator
191
+ :param str creator: list by
192
+ :param dtlpy.entities.filters.Filters filters: Filters entity containing filters parameters
177
193
  :return: List of datasets
178
194
  :rtype: list
179
195
 
180
196
  **Example**:
181
197
 
182
198
  .. code-block:: python
183
-
184
- datasets = project.datasets.list(name='name')
199
+ filters = dl.Filters(resource='datasets')
200
+ filters.add(field='readonly', values=False)
201
+ datasets = project.datasets.list(filters=filters)
185
202
  """
186
- url = '/datasets'
203
+ if filters is None:
204
+ filters = entities.Filters(resource=entities.FiltersResource.DATASET)
205
+ # assert type filters
206
+ elif not isinstance(filters, entities.Filters):
207
+ raise exceptions.PlatformException(error='400',
208
+ message='Unknown filters type: {!r}'.format(type(filters)))
209
+ if filters.resource != entities.FiltersResource.DATASET:
210
+ raise exceptions.PlatformException(
211
+ error='400',
212
+ message='Filters resource must to be FiltersResource.DATASET. Got: {!r}'.format(filters.resource))
187
213
 
188
- query_params = {
189
- 'name': name,
190
- 'creator': creator
191
- }
214
+ url = '/datasets/query'
192
215
 
216
+ if name is not None:
217
+ filters.add(field='name', values=name)
218
+ if creator is not None:
219
+ filters.add(field='creator', values=creator)
193
220
  if self._project is not None:
194
- query_params['projects'] = self.project.id
195
-
196
- url += '?{}'.format(urlencode({key: val for key, val in query_params.items() if val is not None}, doseq=True))
197
-
198
- success, response = self._client_api.gen_request(req_type='get',
199
- path=url)
200
- if success:
201
- pool = self._client_api.thread_pools('entity.create')
202
- datasets_json = response.json()
203
- jobs = [None for _ in range(len(datasets_json))]
204
- # return triggers list
205
- for i_dataset, dataset in enumerate(datasets_json):
206
- jobs[i_dataset] = pool.submit(entities.Dataset._protected_from_json,
207
- **{'client_api': self._client_api,
208
- '_json': dataset,
209
- 'datasets': self,
210
- 'project': self.project})
211
-
212
- # get all results
213
- results = [j.result() for j in jobs]
214
- # log errors
215
- _ = [logger.warning(r[1]) for r in results if r[0] is False]
216
- # return good jobs
217
- datasets = miscellaneous.List([r[1] for r in results if r[0] is True])
218
- else:
219
- raise exceptions.PlatformException(response)
221
+ filters.context = {"projects": [self._project.id]}
222
+ filters.page_size = 1000
223
+ filters.page = 0
224
+ datasets = list()
225
+ while True:
226
+ success, response = self._client_api.gen_request(req_type='POST',
227
+ json_req=filters.prepare(),
228
+ path=url,
229
+ headers={'user_query': filters._user_query})
230
+ if success:
231
+ pool = self._client_api.thread_pools('entity.create')
232
+ datasets_json = response.json()['items']
233
+ jobs = [None for _ in range(len(datasets_json))]
234
+ # return triggers list
235
+ for i_dataset, dataset in enumerate(datasets_json):
236
+ jobs[i_dataset] = pool.submit(entities.Dataset._protected_from_json,
237
+ **{'client_api': self._client_api,
238
+ '_json': dataset,
239
+ 'datasets': self,
240
+ 'project': self.project})
241
+
242
+ # get all results
243
+ results = [j.result() for j in jobs]
244
+ # log errors
245
+ _ = [logger.warning(r[1]) for r in results if r[0] is False]
246
+ # return good jobs
247
+ datasets.extend([r[1] for r in results if r[0] is True])
248
+ if response.json()['hasNextPage'] is True:
249
+ filters.page += 1
250
+ else:
251
+ break
252
+ else:
253
+ raise exceptions.PlatformException(response)
254
+ datasets = miscellaneous.List(datasets)
220
255
  return datasets
221
256
 
222
257
  @_api_reference.add(path='/datasets/{id}', method='get')
@@ -953,10 +988,5 @@ class Datasets:
953
988
 
954
989
  project.datasets.set_readonly(dataset='dataset_entity', state=True)
955
990
  """
956
- if dataset.readonly != state:
957
- patch = {'readonly': state}
958
- self.update(dataset=dataset,
959
- patch=patch)
960
- dataset._readonly = state
961
- else:
962
- logger.warning('Dataset is already "readonly={}". Nothing was done'.format(state))
991
+ import warnings
992
+ warnings.warn("`readonly` flag on dataset is deprecated, doing nothing.", DeprecationWarning)
@@ -816,7 +816,7 @@ class Downloader:
816
816
  total=3,
817
817
  read=3,
818
818
  connect=3,
819
- backoff_factor=0.3,
819
+ backoff_factor=1,
820
820
  )
821
821
  adapter = HTTPAdapter(max_retries=retry)
822
822
  s.mount('http://', adapter)