dtlpy 1.118.12__py3-none-any.whl → 1.118.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.
dtlpy/__version__.py CHANGED
@@ -1 +1 @@
1
- version = '1.118.12'
1
+ version = '1.118.13'
@@ -6,7 +6,7 @@ import os
6
6
  import sys
7
7
  import jwt
8
8
 
9
- from .. import exceptions, entities, repositories, utilities, assets
9
+ from .. import exceptions, entities, repositories, utilities, assets, miscellaneous
10
10
 
11
11
  logger = logging.getLogger(name='dtlpy')
12
12
 
@@ -76,8 +76,16 @@ class CommandExecutor:
76
76
  url = 'dtlpy'
77
77
  if args.url is None:
78
78
  try:
79
- payload = jwt.decode(self.dl.client_api.token, algorithms=['HS256'],
80
- verify=False, options={'verify_signature': False})
79
+ # oxsec-disable jwt-signature-disabled - Client-side SDK: signature verification disabled intentionally to check admin role; server validates on API calls
80
+ payload = jwt.decode(
81
+ self.dl.client_api.token,
82
+ options={
83
+ "verify_signature": False,
84
+ "verify_exp": False,
85
+ "verify_aud": False,
86
+ "verify_iss": False,
87
+ }
88
+ )
81
89
  if 'admin' in payload['https://dataloop.ai/authorization']['roles']:
82
90
  url = "https://storage.googleapis.com/dtlpy/dev/dtlpy-latest-py3-none-any.whl"
83
91
  except Exception:
@@ -235,6 +243,13 @@ class CommandExecutor:
235
243
  project = self.dl.projects.get(project_name=args.project_name)
236
244
  dataset = project.datasets.get(dataset_name=args.dataset_name)
237
245
 
246
+ # Validate local_path and local_annotations_path to prevent path traversal
247
+ miscellaneous.PathUtils.validate_paths(
248
+ [args.local_path, args.local_annotations_path],
249
+ base_path=os.getcwd(),
250
+ must_exist=True
251
+ )
252
+
238
253
  dataset.items.upload(local_path=args.local_path,
239
254
  remote_path=args.remote_path,
240
255
  file_types=args.file_types,
@@ -277,6 +292,13 @@ class CommandExecutor:
277
292
  remote_path.pop(remote_path.index(item))
278
293
  filters.add(field="dir", values=remote_path, operator=entities.FiltersOperations.IN, method='or')
279
294
 
295
+ # Validate local_path to prevent path traversal
296
+ miscellaneous.PathUtils.validate_directory_path(
297
+ args.local_path,
298
+ base_path=os.getcwd(),
299
+ must_exist=False
300
+ )
301
+
280
302
  if not args.without_binaries:
281
303
  dataset.items.download(filters=filters,
282
304
  local_path=args.local_path,
@@ -325,6 +347,9 @@ class CommandExecutor:
325
347
  args.split_seconds = int(args.split_seconds)
326
348
  if isinstance(args.split_times, str):
327
349
  args.split_times = [int(sec) for sec in args.split_times.split(",")]
350
+ # Validate filepath to prevent path traversal
351
+ miscellaneous.PathUtils.validate_file_path(args.filename)
352
+
328
353
  self.dl.utilities.videos.Videos.split_and_upload(
329
354
  project_name=args.project_name,
330
355
  dataset_name=args.dataset_name,
@@ -407,6 +432,8 @@ class CommandExecutor:
407
432
  def deploy(self, args):
408
433
  project = self.dl.projects.get(project_name=args.project_name)
409
434
  json_filepath = args.json_file
435
+ # Validate file path to prevent path traversal
436
+ miscellaneous.PathUtils.validate_file_path(json_filepath)
410
437
  deployed_services, package = self.dl.packages.deploy_from_file(project=project, json_filepath=json_filepath)
411
438
  logger.info("Successfully deployed {} from file: {}\nServices: {}".format(len(deployed_services),
412
439
  json_filepath,
@@ -464,6 +491,13 @@ class CommandExecutor:
464
491
  elif args.packages == "push":
465
492
  packages = self.utils.get_packages_repo(args=args)
466
493
 
494
+ # Validate src_path to prevent path traversal
495
+ miscellaneous.PathUtils.validate_directory_path(
496
+ args.src_path,
497
+ base_path=os.getcwd(),
498
+ must_exist=True
499
+ )
500
+
467
501
  package = packages.push(src_path=args.src_path,
468
502
  package_name=args.package_name,
469
503
  checkout=args.checkout)
@@ -568,7 +602,13 @@ class CommandExecutor:
568
602
  answers = inquirer.prompt(questions)
569
603
  #####
570
604
  # create a dir for that panel
571
- os.makedirs(answers.get('name'), exist_ok=True)
605
+ # Validate panel name to prevent path traversal
606
+ panel_name = answers.get('name')
607
+ # Validate panel name to prevent path traversal
608
+ miscellaneous.PathUtils.validate_directory_name(panel_name)
609
+ # Create directory in current working directory
610
+ panel_dir = os.path.join(os.getcwd(), panel_name)
611
+ os.makedirs(panel_dir, exist_ok=True)
572
612
  # dump to dataloop.json
573
613
  app_filename = assets.paths.APP_JSON_FILENAME
574
614
  if not os.path.isfile(app_filename):
@@ -630,12 +670,22 @@ class CommandExecutor:
630
670
  directory = args.dir
631
671
  if directory == '..':
632
672
  directory = os.path.split(os.getcwd())[0]
673
+ # Validate path to prevent path traversal
674
+ miscellaneous.PathUtils.validate_directory_path(
675
+ directory,
676
+ base_path=os.getcwd(),
677
+ must_exist=True
678
+ )
633
679
  os.chdir(directory)
634
680
  print(os.getcwd())
635
681
 
636
682
  @staticmethod
637
683
  def mkdir(args):
638
- os.mkdir(args.name)
684
+ # Validate directory name to prevent path traversal
685
+ miscellaneous.PathUtils.validate_directory_name(args.name)
686
+ # Create directory in current working directory
687
+ dir_path = os.path.join(os.getcwd(), args.name)
688
+ os.mkdir(dir_path)
639
689
 
640
690
  # noinspection PyUnusedLocal
641
691
  @staticmethod
@@ -18,3 +18,4 @@ from .git_utils import GitUtils
18
18
  from .zipping import Zipping
19
19
  from .list_print import List
20
20
  from .json_utils import JsonUtils
21
+ from .path_utils import PathUtils
@@ -0,0 +1,264 @@
1
+ import os
2
+ import tempfile
3
+ from pathlib import Path
4
+ from .. import exceptions
5
+ from ..services import service_defaults
6
+
7
+
8
+ class PathUtils:
9
+ """
10
+ Utility class for path validation and sanitization to prevent path traversal attacks.
11
+ """
12
+ allowed_roots = [tempfile.gettempdir(), service_defaults.DATALOOP_PATH]
13
+
14
+ @staticmethod
15
+ def _contains_traversal(path: str) -> bool:
16
+ """
17
+ Check if path contains path traversal sequences.
18
+
19
+ :param str path: Path to check
20
+ :return: True if path contains traversal sequences
21
+ :rtype: bool
22
+ """
23
+ if not path:
24
+ return False
25
+
26
+ # Normalize the path to handle different separators
27
+ normalized = os.path.normpath(path)
28
+
29
+ # Check for parent directory references
30
+ parts = Path(normalized).parts
31
+ if '..' in parts:
32
+ return True
33
+
34
+ # Check for encoded traversal sequences (evasion attempts)
35
+ if '%2e%2e' in path.lower() or '..%2f' in path.lower() or '..%5c' in path.lower():
36
+ return True
37
+
38
+ return False
39
+
40
+ @staticmethod
41
+ def _is_within_base(resolved_path: str, base_path: str) -> bool:
42
+ """
43
+ Check if resolved_path is within base_path.
44
+
45
+ :param str resolved_path: Absolute resolved path
46
+ :param str base_path: Base directory path
47
+ :return: True if resolved_path is within base_path
48
+ :rtype: bool
49
+ """
50
+ try:
51
+ resolved = os.path.abspath(os.path.normpath(resolved_path))
52
+ base = os.path.abspath(os.path.normpath(base_path))
53
+
54
+ # Get common path
55
+ common = os.path.commonpath([resolved, base])
56
+ return common == base
57
+ except (ValueError, OSError):
58
+ # On Windows, if paths are on different drives, commonpath raises ValueError
59
+ return False
60
+
61
+ @staticmethod
62
+ def _is_allowed_path(resolved_path: str, base_path: str) -> bool:
63
+ """
64
+ Check if resolved_path is within base_path or any allowed_root.
65
+
66
+ :param str resolved_path: Absolute resolved path
67
+ :param str base_path: Base directory path
68
+ :return: True if resolved_path is within base_path or any allowed_root
69
+ :rtype: bool
70
+ """
71
+ for allowed_root in [base_path] + PathUtils.allowed_roots:
72
+ if PathUtils._is_within_base(resolved_path, allowed_root):
73
+ return True
74
+ return False
75
+
76
+ @staticmethod
77
+ def validate_directory_name(name: str) -> str:
78
+ """
79
+ Validate a directory name to ensure it doesn't contain path traversal sequences.
80
+
81
+ :param str name: Directory name to validate
82
+ :return: Validated directory name
83
+ :rtype: str
84
+ :raises PlatformException: If name contains invalid characters or traversal sequences
85
+ """
86
+ if not name:
87
+ raise exceptions.PlatformException(
88
+ error='400',
89
+ message='Directory name cannot be empty'
90
+ )
91
+
92
+ # Check for path separators
93
+ if os.sep in name or (os.altsep and os.altsep in name):
94
+ raise exceptions.PlatformException(
95
+ error='400',
96
+ message='Directory name cannot contain path separators'
97
+ )
98
+
99
+ # Check for traversal sequences
100
+ if PathUtils._contains_traversal(name):
101
+ raise exceptions.PlatformException(
102
+ error='400',
103
+ message='Directory name cannot contain path traversal sequences'
104
+ )
105
+
106
+ return name
107
+
108
+ @staticmethod
109
+ def _validate_single_path(path, base_path: str, must_exist: bool):
110
+ """
111
+ Internal method to validate a single path string.
112
+
113
+ :param path: Path to validate (str or Path object)
114
+ :param str base_path: Base directory to restrict path to
115
+ :param bool must_exist: If True, path must exist
116
+ :raises PlatformException: If path is invalid or contains traversal sequences
117
+ """
118
+ # Convert Path objects to strings
119
+ if isinstance(path, Path):
120
+ path = str(path)
121
+ if isinstance(base_path, Path):
122
+ base_path = str(base_path)
123
+
124
+ # Skip validation if not a string
125
+ if not isinstance(path, str):
126
+ return
127
+
128
+ # Skip validation for URLs and external paths
129
+ if path.startswith(('http://', 'https://', 'external://')):
130
+ return
131
+
132
+ # Empty string check
133
+ if not path:
134
+ raise exceptions.PlatformException(
135
+ error='400',
136
+ message='Path cannot be empty'
137
+ )
138
+
139
+ # Check for traversal sequences in the original path
140
+ if PathUtils._contains_traversal(path):
141
+ raise exceptions.PlatformException(
142
+ error='400',
143
+ message='Path contains invalid traversal sequences'
144
+ )
145
+
146
+ # Resolve path (absolute paths allowed if within base_path)
147
+ if os.path.isabs(path):
148
+ resolved = os.path.abspath(os.path.normpath(path))
149
+ else:
150
+ resolved = os.path.abspath(os.path.normpath(os.path.join(base_path, path)))
151
+
152
+ # Reject if path is outside base_path or allowed_roots
153
+ if not PathUtils._is_allowed_path(resolved, base_path):
154
+ raise exceptions.PlatformException(
155
+ error='400',
156
+ message='Path resolves outside allowed directory'
157
+ )
158
+
159
+ # Check if path must exist
160
+ if must_exist and not os.path.exists(resolved):
161
+ raise exceptions.PlatformException(
162
+ error='404',
163
+ message='Path does not exist: {}'.format(path)
164
+ )
165
+
166
+ @staticmethod
167
+ def validate_paths(paths, base_path = None, must_exist: bool = False):
168
+ """
169
+ Validate file or directory paths against path traversal attacks.
170
+ Accepts a list of paths and validates each one.
171
+ Skips validation if path is None or not a string.
172
+ Skips validation for URLs (http://, https://) and external paths (external://).
173
+
174
+ :param paths: Path(s) to validate - can be str, Path, list of str/Path, or None
175
+ :param base_path: Optional base directory to restrict path to (str or Path). If None, uses current working directory
176
+ :param bool must_exist: If True, path must exist
177
+ :raises PlatformException: If any path is invalid or contains traversal sequences
178
+ """
179
+ # Handle None - skip validation
180
+ if paths is None:
181
+ return
182
+
183
+ # Convert base_path Path object to string
184
+ if isinstance(base_path, Path):
185
+ base_path = str(base_path)
186
+
187
+ # Resolve base_path
188
+ if base_path is None:
189
+ base_path = os.getcwd()
190
+
191
+ # Handle list of paths
192
+ if isinstance(paths, list):
193
+ for path in paths:
194
+ PathUtils._validate_single_path(path, base_path, must_exist)
195
+ else:
196
+ # Single path
197
+ PathUtils._validate_single_path(paths, base_path, must_exist)
198
+
199
+ @staticmethod
200
+ def validate_file_path(file_path, base_path = None, must_exist: bool = True):
201
+ """
202
+ Validate a file path against path traversal attacks.
203
+
204
+ :param file_path: File path to validate (str or Path object)
205
+ :param base_path: Optional base directory to restrict path to (str or Path). If None, uses current working directory
206
+ :param bool must_exist: If True, file must exist (default: True)
207
+ :raises PlatformException: If path is invalid, contains traversal sequences, or is not a file
208
+ """
209
+ # Convert Path objects to strings
210
+ if isinstance(file_path, Path):
211
+ file_path = str(file_path)
212
+ if isinstance(base_path, Path):
213
+ base_path = str(base_path)
214
+
215
+ PathUtils.validate_paths(file_path, base_path=base_path, must_exist=must_exist)
216
+
217
+ if must_exist and isinstance(file_path, str) and not file_path.startswith(('http://', 'https://', 'external://')):
218
+ # Resolve path to check if it's a file
219
+ if base_path is None:
220
+ base_path = os.getcwd()
221
+ if os.path.isabs(file_path):
222
+ resolved = os.path.abspath(os.path.normpath(file_path))
223
+ else:
224
+ resolved = os.path.abspath(os.path.normpath(os.path.join(base_path, file_path)))
225
+
226
+ if not os.path.isfile(resolved):
227
+ raise exceptions.PlatformException(
228
+ error='400',
229
+ message='Path is not a file: {}'.format(file_path)
230
+ )
231
+
232
+ @staticmethod
233
+ def validate_directory_path(dir_path, base_path = None, must_exist: bool = True):
234
+ """
235
+ Validate a directory path against path traversal attacks.
236
+
237
+ :param dir_path: Directory path to validate (str or Path object)
238
+ :param base_path: Optional base directory to restrict path to (str or Path). If None, uses current working directory
239
+ :param bool must_exist: If True, directory must exist (default: True)
240
+ :raises PlatformException: If path is invalid, contains traversal sequences, or is not a directory
241
+ """
242
+ # Convert Path objects to strings
243
+ if isinstance(dir_path, Path):
244
+ dir_path = str(dir_path)
245
+ if isinstance(base_path, Path):
246
+ base_path = str(base_path)
247
+
248
+ PathUtils.validate_paths(dir_path, base_path=base_path, must_exist=must_exist)
249
+
250
+ if must_exist and isinstance(dir_path, str) and not dir_path.startswith(('http://', 'https://', 'external://')):
251
+ # Resolve path to check if it's a directory
252
+ if base_path is None:
253
+ base_path = os.getcwd()
254
+ if os.path.isabs(dir_path):
255
+ resolved = os.path.abspath(os.path.normpath(dir_path))
256
+ else:
257
+ resolved = os.path.abspath(os.path.normpath(os.path.join(base_path, dir_path)))
258
+
259
+ if not os.path.isdir(resolved):
260
+ raise exceptions.PlatformException(
261
+ error='400',
262
+ message='Path is not a directory: {}'.format(dir_path)
263
+ )
264
+
@@ -2,7 +2,6 @@ from copy import deepcopy
2
2
  import traceback
3
3
  import logging
4
4
  import json
5
- import jwt
6
5
  import os
7
6
  from PIL import Image
8
7
  from io import BytesIO
@@ -336,9 +335,7 @@ class Annotations:
336
335
 
337
336
  def _delete_single_annotation(self, w_annotation_id):
338
337
  try:
339
- creator = jwt.decode(self._client_api.token, algorithms=['HS256'],
340
- verify=False, options={'verify_signature': False})['email']
341
- payload = {'username': creator}
338
+ payload = {'username': self._client_api.info()['user_email']}
342
339
  success, response = self._client_api.gen_request(req_type='delete',
343
340
  path='/annotations/{}'.format(w_annotation_id),
344
341
  json_req=payload)
@@ -513,6 +513,13 @@ class Packages:
513
513
  if codebase is None:
514
514
  src_path = os.getcwd()
515
515
  logger.warning('No src_path is given, getting package information from cwd: {}'.format(src_path))
516
+ else:
517
+ # Validate src_path to prevent path traversal
518
+ miscellaneous.PathUtils.validate_directory_path(
519
+ src_path,
520
+ base_path=os.getcwd(),
521
+ must_exist=True
522
+ )
516
523
 
517
524
  # get package json
518
525
  package_from_json = dict()
@@ -941,6 +948,8 @@ class Packages:
941
948
 
942
949
  project.packages.deploy_from_file(project='project_entity', json_filepath='json_filepath')
943
950
  """
951
+ # Validate file path to prevent path traversal
952
+ miscellaneous.PathUtils.validate_file_path(json_filepath)
944
953
  with open(json_filepath, 'r') as f:
945
954
  data = json.load(f)
946
955
 
@@ -1,6 +1,5 @@
1
1
  import logging
2
2
  from urllib.parse import quote
3
- import jwt
4
3
 
5
4
  from .. import entities, miscellaneous, exceptions, _api_reference
6
5
  from ..services.api_client import ApiClient
@@ -159,8 +158,7 @@ class Projects:
159
158
  assert isinstance(title, str)
160
159
  assert isinstance(content, str)
161
160
  if self._client_api.token is not None:
162
- sender = jwt.decode(self._client_api.token, algorithms=['HS256'],
163
- verify=False, options={'verify_signature': False})['email']
161
+ sender = self._client_api.info()['user_email']
164
162
  else:
165
163
  raise exceptions.PlatformException('600', 'Token expired please log in')
166
164
 
@@ -856,8 +856,16 @@ class ApiClient:
856
856
  """
857
857
  user_email = 'null'
858
858
  if self.token is not None:
859
- payload = jwt.decode(self.token, algorithms=['HS256'],
860
- verify=False, options={'verify_signature': False})
859
+ # oxsec-disable jwt-signature-disabled - Client-side SDK: signature verification disabled intentionally to extract user email; server validates on API calls
860
+ payload = jwt.decode(
861
+ self.token,
862
+ options={
863
+ "verify_signature": False,
864
+ "verify_exp": False,
865
+ "verify_aud": False,
866
+ "verify_iss": False,
867
+ }
868
+ )
861
869
  user_email = payload['email']
862
870
  information = {'environment': self.environment,
863
871
  'user_email': user_email}
@@ -1353,8 +1361,16 @@ class ApiClient:
1353
1361
  if self.token is None or self.token == '':
1354
1362
  expired = True
1355
1363
  else:
1356
- payload = jwt.decode(self.token, algorithms=['HS256'],
1357
- options={'verify_signature': False}, verify=False)
1364
+ # oxsec-disable jwt-signature-disabled - Client-side SDK: signature verification disabled intentionally to check token expiration; server validates on API calls
1365
+ payload = jwt.decode(
1366
+ self.token,
1367
+ options={
1368
+ "verify_signature": False,
1369
+ "verify_exp": False,
1370
+ "verify_aud": False,
1371
+ "verify_iss": False,
1372
+ }
1373
+ )
1358
1374
  d = datetime.datetime.now(datetime.timezone.utc)
1359
1375
  epoch = datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
1360
1376
  now = (d - epoch).total_seconds()
@@ -2,7 +2,6 @@ import time
2
2
  import logging
3
3
  import threading
4
4
  import traceback
5
- import jwt
6
5
 
7
6
  logger = logging.getLogger(name='dtlpy')
8
7
 
@@ -21,9 +20,7 @@ def check_in_thread(version, client_api):
21
20
 
22
21
  # try read token for email
23
22
  try:
24
- payload = jwt.decode(client_api.token, algorithms=['HS256'],
25
- verify=False, options={'verify_signature': False})
26
- user_email = payload['email']
23
+ user_email = client_api.info()['user_email']
27
24
  except Exception:
28
25
  user_email = 'na'
29
26
  logger.debug('SDK info: user: {}, version: {}'.format(user_email, version))
dtlpy/services/logins.py CHANGED
@@ -32,15 +32,12 @@ def login_secret(api_client, email, password, client_id, client_secret=None, for
32
32
  # TODO add deprecation warning to client_id
33
33
  # check if already logged in with SAME email
34
34
  if api_client.token is not None or api_client.token == '':
35
- try:
36
- payload = jwt.decode(api_client.token, algorithms=['HS256'],
37
- options={'verify_signature': False}, verify=False)
38
- if 'email' in payload and \
39
- payload['email'] == email and \
40
- not api_client.token_expired() and \
41
- not force:
42
- return True
43
- except jwt.exceptions.DecodeError:
35
+ logged_email = api_client.info()['user_email']
36
+ if logged_email == email and \
37
+ not api_client.token_expired() and \
38
+ not force:
39
+ return True
40
+ else:
44
41
  logger.debug('{}'.format('Cant decode token. Force login is used'))
45
42
 
46
43
  logger.info('[Start] Login Secret')
@@ -71,13 +68,12 @@ def login_secret(api_client, email, password, client_id, client_secret=None, for
71
68
  api_client.refresh_token = response_dict['refresh_token']
72
69
 
73
70
  # set new client id for refresh
74
- payload = jwt.decode(api_client.token, algorithms=['HS256'],
75
- options={'verify_signature': False}, verify=False)
76
- if 'email' in payload:
77
- logger.info('[Done] Login Secret. User: {}'.format(payload['email']))
71
+ logged_email = api_client.info()['user_email']
72
+
73
+ if not logged_email == 'null':
74
+ logger.info(f'[Done] Login Secret. User: {logged_email}')
78
75
  else:
79
- logger.info('[Done] Login Secret. User: {}'.format(email))
80
- logger.info(payload)
76
+ logger.info(f'[Done] Login Secret. User: {email}')
81
77
  return True
82
78
 
83
79
 
@@ -206,8 +202,16 @@ def login(api_client, auth0_url=None, audience=None, client_id=None, login_domai
206
202
  success, tokens = server.process_request()
207
203
 
208
204
  if success:
209
- decoded_jwt = jwt.decode(tokens['id'], verify=False,
210
- options={'verify_signature': False})
205
+ # oxsec-disable jwt-signature-disabled - Client-side SDK: signature verification disabled intentionally to extract claims for display; server validates on API calls
206
+ decoded_jwt = jwt.decode(
207
+ tokens['id'],
208
+ options={
209
+ "verify_signature": False,
210
+ "verify_exp": False,
211
+ "verify_aud": False,
212
+ "verify_iss": False,
213
+ }
214
+ )
211
215
 
212
216
  if 'email' in decoded_jwt:
213
217
  logger.info('Logged in: {}'.format(decoded_jwt['email']))
@@ -7,6 +7,7 @@ import os
7
7
  import logging
8
8
  import dtlpy as dl
9
9
  import shutil
10
+ from dtlpy import miscellaneous
10
11
 
11
12
  logger = logging.getLogger(name='dtlpy')
12
13
 
@@ -344,6 +345,9 @@ class Videos:
344
345
  raise
345
346
  # https://www.ffmpeg.org/ffmpeg-formats.html#Examples-9
346
347
 
348
+ # Validate filepath to prevent path traversal
349
+ miscellaneous.PathUtils.validate_file_path(filepath)
350
+
347
351
  if not os.path.isfile(filepath):
348
352
  raise IOError('File doesnt exists: {}'.format(filepath))
349
353
  logger.info('Extracting video information...')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dtlpy
3
- Version: 1.118.12
3
+ Version: 1.118.13
4
4
  Summary: SDK and CLI for Dataloop platform
5
5
  Home-page: https://github.com/dataloop-ai/dtlpy
6
6
  Author: Dataloop Team
@@ -1,5 +1,5 @@
1
1
  dtlpy/__init__.py,sha256=p9qDLEQ01cTj0Xx8FkxJPHSmP7LMHMv8b5-ZWW1FPrU,20625
2
- dtlpy/__version__.py,sha256=zzDq0O015CukNUyAYZIC27rY6zu8_tSZPOwxCHkmfnw,21
2
+ dtlpy/__version__.py,sha256=rKb0jeJZoFfB0Nvl7WNtP0jvSvA4lP7Psb-TendlWlQ,21
3
3
  dtlpy/exceptions.py,sha256=EQCKs3pwhwZhgMByQN3D3LpWpdxwcKPEEt-bIaDwURM,2871
4
4
  dtlpy/new_instance.py,sha256=XegQav2hzPrPAUzuRFvUIGSPidoK-rbb02Q43NPsIpo,9982
5
5
  dtlpy/assets/__init__.py,sha256=D_hAa6NM8Zoy32sF_9b7m0b7I-BQEyBFg8-9Tg2WOeo,976
@@ -38,7 +38,7 @@ dtlpy/caches/filesystem_cache.py,sha256=OrBqyEucSVp7g33c6R1BR3ICbkgQnwYWEDhQ7OxH
38
38
  dtlpy/caches/redis_cache.py,sha256=bgJgxgAXFR_TxPDvlLS4TKumFds-ihNf668JbPYUfpc,2331
39
39
  dtlpy/dlp/__init__.py,sha256=QG_BxSqeic0foFBmzIkpZEF4EvxOZamknj2f5Cb6T6Q,868
40
40
  dtlpy/dlp/cli_utilities.py,sha256=gZA9XIN5GG-xWJ6S1i6T17CDDCypDoev6CY-WHlogYg,16055
41
- dtlpy/dlp/command_executor.py,sha256=JKtRKTwrKfkXHa1VuFhPw15FuwexBPq_9ANAu2pSyXs,32113
41
+ dtlpy/dlp/command_executor.py,sha256=oQPOzE6eVUvm54IsdVYcWgVOByWjOqa2nQQY8EAd8Vc,34320
42
42
  dtlpy/dlp/dlp,sha256=-F0vSCWuSOOtgERAtsPMPyMmzitjhB7Yeftg_PDlDjw,10
43
43
  dtlpy/dlp/dlp.bat,sha256=QOvx8Dlx5dUbCTMpwbhOcAIXL1IWmgVRSboQqDhIn3A,37
44
44
  dtlpy/dlp/dlp.py,sha256=Zv9yoXwNAx4gkED-JiayN-ZkX2dPn4FB0SDx9qc7muo,4404
@@ -141,11 +141,12 @@ dtlpy/examples/upload_items_and_custom_format_annotations.py,sha256=KgGeIs2Q6MoT
141
141
  dtlpy/examples/upload_items_with_modalities.py,sha256=PJyzPIvRSWi_nh7JlOR9YZKFrMuvYffDx5mz9TiVIvU,1848
142
142
  dtlpy/examples/upload_segmentation_annotations_from_mask_image.py,sha256=JQGc8wQ3zTRRlVcRLs223UwCYCAfChKlvU0QOPEqezI,1388
143
143
  dtlpy/examples/upload_yolo_format_annotations.py,sha256=PDLhC5pBGrC68Pix-7I7SgdaCweYNZPgJxg0h4ssWyc,2610
144
- dtlpy/miscellaneous/__init__.py,sha256=twbvfsKdiNHNR-vUuy8nUlY3vuUVaSnm-wO83yQdeFY,829
144
+ dtlpy/miscellaneous/__init__.py,sha256=_-NhTwcGH2FxFsWMf-vfmYYT6Ouuw08rN1fALJg2Zvw,863
145
145
  dtlpy/miscellaneous/dict_differ.py,sha256=POJbKR0YyWPf5gFADFpIaNFj9gt2aVBTNof7GJNxTCw,3489
146
146
  dtlpy/miscellaneous/git_utils.py,sha256=CT_CCDsqDqu_bY3cLcOSU6k3Zr6w40t8GJULLUtAJ_U,7971
147
147
  dtlpy/miscellaneous/json_utils.py,sha256=0P4YTlL6o_L7AUrvAeqkqA46MZZK_hDdTrdnmI59y6g,428
148
148
  dtlpy/miscellaneous/list_print.py,sha256=fBGTMXFUwDG8DD4W6HyR8BTGtbTckLf4W09quNRJm5M,4828
149
+ dtlpy/miscellaneous/path_utils.py,sha256=TiYHuwfWUBbz6E99QP-MQivkBC70aMBdYRd3RTJ0QgQ,10426
149
150
  dtlpy/miscellaneous/zipping.py,sha256=JplTc8UDFvO8WaD5vKuumVLN0lU_-GtHoE0doWKtmKg,5383
150
151
  dtlpy/ml/__init__.py,sha256=vPkyXpc9kcWWZ_PxyPEOsjKBJdEbowLkZr8FZIb_OBM,799
151
152
  dtlpy/ml/base_feature_extractor_adapter.py,sha256=iiEGYAx0Rdn4K46H_FlKrAv3ebTXHSxNVAmio0BxhaI,1178
@@ -156,7 +157,7 @@ dtlpy/ml/summary_writer.py,sha256=dehDi8zmGC1sAGyy_3cpSWGXoGQSiQd7bL_Thoo8yIs,27
156
157
  dtlpy/ml/train_utils.py,sha256=t607DfyGBRrUQZ9jPmPe4V9Udzfk0hPWuw4OvKZKAeo,2440
157
158
  dtlpy/repositories/__init__.py,sha256=D2YI3ZLlSx0OlgVr8y_E9rsj-IxCDOj0MB6QTlv2NSA,2061
158
159
  dtlpy/repositories/analytics.py,sha256=dQPCYTPAIuyfVI_ppR49W7_GBj0033feIm9Gd7LW1V0,2966
159
- dtlpy/repositories/annotations.py,sha256=idTKzanNt-ncB0eIKE5p6WclrVGNjceI2Y7dAzDFtzY,43595
160
+ dtlpy/repositories/annotations.py,sha256=ygN6JRa9jAod9kXDwksaScsTqzWCzegYsmF_i2be5xA,43442
160
161
  dtlpy/repositories/apps.py,sha256=miCYJNqte8TVFkBezE8yzueMsz593jNO9sSUfZRVV7M,15969
161
162
  dtlpy/repositories/artifacts.py,sha256=Ke2ustTNw-1eQ0onLsWY7gL2aChjXPAX5p1uQ_EzMbo,19081
162
163
  dtlpy/repositories/assignments.py,sha256=1VwJZ7ctQe1iaDDDpeYDgoj2G-TCgzolVLUEqUocd2w,25506
@@ -180,10 +181,10 @@ dtlpy/repositories/models.py,sha256=K8mqmYM_kj3z727VDcdcnQC_XDw18bf48ujEV-deStU,
180
181
  dtlpy/repositories/nodes.py,sha256=xXJm_YA0vDUn0dVvaGeq6ORM0vI3YXvfjuylvGRtkxo,3061
181
182
  dtlpy/repositories/ontologies.py,sha256=unnMhD2isR9DVE5S8Fg6fSDf1ZZ5Xemxxufx4LEUT3w,19577
182
183
  dtlpy/repositories/organizations.py,sha256=6ijUDFbsogfRul1g_vUB5AZOb41MRmV5NhNU7WLHt3A,22825
183
- dtlpy/repositories/packages.py,sha256=QhkXMZkpseCt0pDropJuqoHJL0RMa5plk8AN0V3w6Nk,86807
184
+ dtlpy/repositories/packages.py,sha256=XA1cu7zW9sHfMlayjUD8OeAknsEpHzEcN-uebOIFmxc,87172
184
185
  dtlpy/repositories/pipeline_executions.py,sha256=sEC03bu5DsHc554z3xDbMCP529rhfADcktXgWkswEwI,17281
185
186
  dtlpy/repositories/pipelines.py,sha256=5qosyxLFgNbcmL7uoTr9klAj1VM-7mWvsOvngbUU1Qk,24320
186
- dtlpy/repositories/projects.py,sha256=TKLCuL7Inlv4GwgcQcuXkPQtgacfrXYjsTQng8nPC7Y,21623
187
+ dtlpy/repositories/projects.py,sha256=_e5z4byytBu-nEYqsRO0HF0IbLgyyUaLLkx0EBND6Ls,21501
187
188
  dtlpy/repositories/recipes.py,sha256=q1FMk4kBPzBS-QIbkxeSsMcAJmYuS7gpYL8t3XIBWII,17117
188
189
  dtlpy/repositories/resource_executions.py,sha256=PyzsbdJxz6jf17Gx13GZmqdu6tZo3TTVv-DypnJ_sY0,5374
189
190
  dtlpy/repositories/schema.py,sha256=kTKDrbwm7BfQnBAK81LpAl9ChNFdyUweSLNazlJJhjk,3953
@@ -198,15 +199,15 @@ dtlpy/repositories/uploader.py,sha256=I9mP-Ikj0zUOdMTf-7FN_huHWXYeWc8gzVRpfUPAXj
198
199
  dtlpy/repositories/webhooks.py,sha256=IIpxOJ-7KeQp1TY9aJZz-FuycSjAoYx0TDk8z86KAK8,9033
199
200
  dtlpy/services/__init__.py,sha256=VfVJy2otIrDra6i7Sepjyez2ujiE6171ChQZp-YgxsM,904
200
201
  dtlpy/services/aihttp_retry.py,sha256=tgntZsAY0dW9v08rkjX1T5BLNDdDd8svtgn7nH8DSGU,5022
201
- dtlpy/services/api_client.py,sha256=MbpTsGVgFonv04pWCfYkOP0shM3LcfCOrucVEXvNn4M,71907
202
+ dtlpy/services/api_client.py,sha256=XS5tYyKvwW5RYlR1RD06zGk9-_MPf2h7yOoog2EDhxQ,72559
202
203
  dtlpy/services/api_reference.py,sha256=cW-B3eoi9Xs3AwI87_Kr6GV_E6HPoC73aETFaGz3A-0,1515
203
204
  dtlpy/services/async_utils.py,sha256=kaYHTPw0Lg8PeJJq8whPyzrBYkzD7offs5hsKRZXJm8,3960
204
205
  dtlpy/services/calls_counter.py,sha256=gr0io5rIsO5-7Cgc8neA1vK8kUtYhgFPmDQ2jXtiZZs,1036
205
- dtlpy/services/check_sdk.py,sha256=H4KL5xrmNGfR9fUSxTVUeBm_3YFGjkwUZpFnqhFBJyI,2617
206
+ dtlpy/services/check_sdk.py,sha256=CaOzAtI7ETtgX3x6hS5NWx77liqqRwIKLJ7B42EXIt4,2464
206
207
  dtlpy/services/cookie.py,sha256=sSZR1QV4ienCcZ8lEK_Y4nZYBgAxO3kHrcBXFKGcmwQ,3694
207
208
  dtlpy/services/create_logger.py,sha256=2dC39CCmc17H4LYRpY0bRIT4S50UTGNOBPYIqJnrsIU,6350
208
209
  dtlpy/services/events.py,sha256=mpcu8RusLPrBcJEbWR61uFb4FiU_dQv3xoa7uM-rTcY,3686
209
- dtlpy/services/logins.py,sha256=YMMi_C_A97ZNtIlREE30hpBRhULAZJtORiVL6OL0oPQ,8766
210
+ dtlpy/services/logins.py,sha256=_fUJ5UlEDWLX09TtcsYy9G1Qca5p6X24xu4XvE-N9Sk,8745
210
211
  dtlpy/services/reporter.py,sha256=qQ9Tgws-8cSpvKbLhJ8j8vgVSgxju2qAgnDL5sCk0Dg,9191
211
212
  dtlpy/services/service_defaults.py,sha256=a7KoqkVmn2TXmM9gN9JRaVVtcG2b8JGIieVnaZeEaao,3860
212
213
  dtlpy/utilities/__init__.py,sha256=ncQD1O5lZ7L9n9rNRBivyqNVFDZyQcmqn-X-wyQhhIs,898
@@ -225,13 +226,13 @@ dtlpy/utilities/reports/figures.py,sha256=o3TOrwo1ytY14H-NV-TyEgVYCFDSm6HYAXVXRd
225
226
  dtlpy/utilities/reports/report.py,sha256=3nEsNnIWmdPEsd21nN8vMMgaZVcPKn9iawKTTeOQg2A,2639
226
227
  dtlpy/utilities/videos/__init__.py,sha256=SV3w51vfPuGBxaMeNemx6qEMHw_C4lLpWNGXMvdsKSY,734
227
228
  dtlpy/utilities/videos/video_player.py,sha256=LCxg0EZ_DeuwcT7U_r7MRC6Q19s0xdFb7x5Gk39PRms,24072
228
- dtlpy/utilities/videos/videos.py,sha256=Dj916B4TQRIhI7HZVevl3foFrCsPp0eeWwvGbgX3-_A,21875
229
- dtlpy-1.118.12.data/scripts/dlp,sha256=-F0vSCWuSOOtgERAtsPMPyMmzitjhB7Yeftg_PDlDjw,10
230
- dtlpy-1.118.12.data/scripts/dlp.bat,sha256=QOvx8Dlx5dUbCTMpwbhOcAIXL1IWmgVRSboQqDhIn3A,37
231
- dtlpy-1.118.12.data/scripts/dlp.py,sha256=ZpfJvYE1_OTSorEYBphqTOutnHSb5TqOXh0y_mUCTJs,4393
232
- dtlpy-1.118.12.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
233
- dtlpy-1.118.12.dist-info/METADATA,sha256=-R4pN-l-KYSHK8DgWK-kWCA5eLuISDOrpK-7nIUmd9I,5908
234
- dtlpy-1.118.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
235
- dtlpy-1.118.12.dist-info/entry_points.txt,sha256=C4PyKthCs_no88HU39eioO68oei64STYXC2ooGZTc4Y,43
236
- dtlpy-1.118.12.dist-info/top_level.txt,sha256=MSr60TGZYlwXCKxlLoZCfILRZ6pU_3L-20d2SZvygyA,6
237
- dtlpy-1.118.12.dist-info/RECORD,,
229
+ dtlpy/utilities/videos/videos.py,sha256=4xbbrHDeJhyR8VXW5ojLEp1GGGwgMoRIRyGrXEX5wiA,22031
230
+ dtlpy-1.118.13.data/scripts/dlp,sha256=-F0vSCWuSOOtgERAtsPMPyMmzitjhB7Yeftg_PDlDjw,10
231
+ dtlpy-1.118.13.data/scripts/dlp.bat,sha256=QOvx8Dlx5dUbCTMpwbhOcAIXL1IWmgVRSboQqDhIn3A,37
232
+ dtlpy-1.118.13.data/scripts/dlp.py,sha256=ZpfJvYE1_OTSorEYBphqTOutnHSb5TqOXh0y_mUCTJs,4393
233
+ dtlpy-1.118.13.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
234
+ dtlpy-1.118.13.dist-info/METADATA,sha256=ozuEK7ANlXc6pEc4wDX84knLXQO7rv1BJ-AAhC_bdgw,5908
235
+ dtlpy-1.118.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
236
+ dtlpy-1.118.13.dist-info/entry_points.txt,sha256=C4PyKthCs_no88HU39eioO68oei64STYXC2ooGZTc4Y,43
237
+ dtlpy-1.118.13.dist-info/top_level.txt,sha256=MSr60TGZYlwXCKxlLoZCfILRZ6pU_3L-20d2SZvygyA,6
238
+ dtlpy-1.118.13.dist-info/RECORD,,
File without changes