cloudos-cli 2.38.3__tar.gz → 2.40.0__tar.gz

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 (37) hide show
  1. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/PKG-INFO +3 -3
  2. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/README.md +2 -2
  3. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/__main__.py +38 -40
  4. cloudos_cli-2.40.0/cloudos_cli/_version.py +1 -0
  5. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/datasets/datasets.py +89 -1
  6. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli.egg-info/PKG-INFO +3 -3
  7. cloudos_cli-2.38.3/cloudos_cli/_version.py +0 -1
  8. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/LICENSE +0 -0
  9. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/__init__.py +0 -0
  10. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/clos.py +0 -0
  11. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/configure/__init__.py +0 -0
  12. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/configure/configure.py +0 -0
  13. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/datasets/__init__.py +0 -0
  14. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/import_wf/__init__.py +0 -0
  15. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/import_wf/import_wf.py +0 -0
  16. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/jobs/__init__.py +0 -0
  17. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/jobs/job.py +0 -0
  18. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/link/__init__.py +0 -0
  19. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/link/link.py +0 -0
  20. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/queue/__init__.py +0 -0
  21. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/queue/queue.py +0 -0
  22. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/utils/__init__.py +0 -0
  23. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/utils/array_job.py +0 -0
  24. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/utils/cloud.py +0 -0
  25. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/utils/details.py +0 -0
  26. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/utils/errors.py +0 -0
  27. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/utils/requests.py +0 -0
  28. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli/utils/resources.py +0 -0
  29. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli.egg-info/SOURCES.txt +0 -0
  30. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli.egg-info/dependency_links.txt +0 -0
  31. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli.egg-info/entry_points.txt +0 -0
  32. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli.egg-info/requires.txt +0 -0
  33. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/cloudos_cli.egg-info/top_level.txt +0 -0
  34. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/setup.cfg +0 -0
  35. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/setup.py +0 -0
  36. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/tests/__init__.py +0 -0
  37. {cloudos_cli-2.38.3 → cloudos_cli-2.40.0}/tests/functions_for_pytest.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudos_cli
3
- Version: 2.38.3
3
+ Version: 2.40.0
4
4
  Summary: Python package for interacting with CloudOS
5
5
  Home-page: https://github.com/lifebit-ai/cloudos-cli
6
6
  Author: David Piñeyro
@@ -1006,8 +1006,8 @@ If you require more information on the files and folder listed, you can use the
1006
1006
  - Owner
1007
1007
  - Size in human readable format
1008
1008
  - Last updated
1009
- - Filepath (the file or folder name)
1010
- - S3 Path
1009
+ - File Name (the file or folder name)
1010
+ - Storage Path
1011
1011
 
1012
1012
  ##### Moving files
1013
1013
 
@@ -971,8 +971,8 @@ If you require more information on the files and folder listed, you can use the
971
971
  - Owner
972
972
  - Size in human readable format
973
973
  - Last updated
974
- - Filepath (the file or folder name)
975
- - S3 Path
974
+ - File Name (the file or folder name)
975
+ - Storage Path
976
976
 
977
977
  ##### Moving files
978
978
 
@@ -2653,7 +2653,7 @@ def run_bash_array_job(ctx,
2653
2653
  @click.option('--details',
2654
2654
  help=('When selected, it prints the details of the listed files. ' +
2655
2655
  'Details contains "Type", "Owner", "Size", "Last Updated", ' +
2656
- '"Filepath", "S3 Path".'),
2656
+ '"File Name", "Storage Path".'),
2657
2657
  is_flag=True)
2658
2658
  @click.pass_context
2659
2659
  def list_files(ctx,
@@ -2668,10 +2668,9 @@ def list_files(ctx,
2668
2668
  details):
2669
2669
  """List contents of a path within a CloudOS workspace dataset."""
2670
2670
 
2671
- # fallback to ctx default if profile not specified
2672
2671
  profile = profile or ctx.default_map['datasets']['list'].get('profile')
2673
-
2674
2672
  config_manager = ConfigurationProfile()
2673
+
2675
2674
  required_dict = {
2676
2675
  'apikey': True,
2677
2676
  'workspace_id': True,
@@ -2679,28 +2678,25 @@ def list_files(ctx,
2679
2678
  'project_name': False
2680
2679
  }
2681
2680
 
2682
- user_options = (
2683
- config_manager.load_profile_and_validate_data(
2684
- ctx,
2685
- INIT_PROFILE,
2686
- CLOUDOS_URL,
2687
- profile=profile,
2688
- required_dict=required_dict,
2689
- apikey=apikey,
2690
- cloudos_url=cloudos_url,
2691
- workspace_id=workspace_id,
2692
- workflow_name=None,
2693
- repository_platform=None,
2694
- execution_platform=None,
2695
- project_name=project_name
2696
- )
2681
+ user_options = config_manager.load_profile_and_validate_data(
2682
+ ctx,
2683
+ INIT_PROFILE,
2684
+ CLOUDOS_URL,
2685
+ profile=profile,
2686
+ required_dict=required_dict,
2687
+ apikey=apikey,
2688
+ cloudos_url=cloudos_url,
2689
+ workspace_id=workspace_id,
2690
+ workflow_name=None,
2691
+ repository_platform=None,
2692
+ execution_platform=None,
2693
+ project_name=project_name
2697
2694
  )
2698
- # Setup only the required parameters
2695
+
2699
2696
  apikey = user_options['apikey']
2700
2697
  cloudos_url = user_options['cloudos_url']
2701
2698
  workspace_id = user_options['workspace_id']
2702
2699
  project_name = user_options['project_name']
2703
-
2704
2700
  verify_ssl = ssl_selector(disable_ssl_verification, ssl_cert)
2705
2701
 
2706
2702
  datasets = Datasets(
@@ -2719,32 +2715,31 @@ def list_files(ctx,
2719
2715
  contents = result.get("files", []) + result.get("folders", [])
2720
2716
 
2721
2717
  if details:
2722
- console = Console(width=None) # Avoid terminal width truncation
2723
-
2718
+ console = Console(width=None)
2724
2719
  table = Table(show_header=True, header_style="bold white")
2725
2720
  table.add_column("Type", style="cyan", no_wrap=True)
2726
2721
  table.add_column("Owner", style="white")
2727
2722
  table.add_column("Size", style="magenta")
2728
2723
  table.add_column("Last Updated", style="green")
2729
- table.add_column("Filepath", style="bold", overflow="fold")
2730
- table.add_column("S3 Path", style="dim", no_wrap=False, overflow="fold", ratio=2)
2724
+ table.add_column("File Name", style="bold", overflow="fold")
2725
+ table.add_column("Storage Path", style="dim", no_wrap=False, overflow="fold", ratio=2)
2731
2726
 
2732
2727
  for item in contents:
2733
2728
  is_folder = "folderType" in item or item.get("isDir", False)
2734
2729
  type_ = "folder" if is_folder else "file"
2735
2730
 
2736
- user = item.get("user")
2731
+ user = item.get("user", {})
2737
2732
  if isinstance(user, dict):
2738
2733
  name = user.get("name", "").strip()
2739
2734
  surname = user.get("surname", "").strip()
2740
- if name and surname:
2741
- owner = f"{name} {surname}"
2742
- elif name:
2743
- owner = name
2744
- elif surname:
2745
- owner = surname
2746
- else:
2747
- owner = "-"
2735
+ else:
2736
+ name = surname = ""
2737
+ if name and surname:
2738
+ owner = f"{name} {surname}"
2739
+ elif name:
2740
+ owner = name
2741
+ elif surname:
2742
+ owner = surname
2748
2743
  else:
2749
2744
  owner = "-"
2750
2745
 
@@ -2754,14 +2749,17 @@ def list_files(ctx,
2754
2749
  updated = item.get("updatedAt") or item.get("lastModified", "-")
2755
2750
  filepath = item.get("name", "-")
2756
2751
 
2757
- if is_folder:
2758
- s3_bucket = item.get("s3BucketName")
2759
- s3_key = item.get("s3Prefix")
2760
- s3_path = f"s3://{s3_bucket}/{s3_key}" if s3_bucket and s3_key else "-"
2752
+ if item.get("fileType") == "S3File" or item.get("folderType") == "S3Folder":
2753
+ bucket = item.get("s3BucketName")
2754
+ key = item.get("s3ObjectKey") or item.get("s3Prefix")
2755
+ s3_path = f"s3://{bucket}/{key}" if bucket and key else "-"
2756
+ elif item.get("fileType") == "AzureBlobFile" or item.get("folderType") == "AzureBlobFolder":
2757
+ account = item.get("blobStorageAccountName")
2758
+ container = item.get("blobContainerName")
2759
+ key = item.get("blobName") if item.get("fileType") == "AzureBlobFile" else item.get("blobPrefix")
2760
+ s3_path = f"az://{account}.blob.core.windows.net/{container}/{key}" if account and container and key else "-"
2761
2761
  else:
2762
- s3_bucket = item.get("s3BucketName")
2763
- s3_key = item.get("s3ObjectKey") or item.get("s3Prefix")
2764
- s3_path = f"s3://{s3_bucket}/{s3_key}" if s3_bucket and s3_key else "-"
2762
+ s3_path = "-"
2765
2763
 
2766
2764
  style = Style(color="blue", underline=True) if is_folder else None
2767
2765
  table.add_row(type_, owner, size, updated, filepath, s3_path, style=style)
@@ -0,0 +1 @@
1
+ __version__ = '2.40.0'
@@ -5,6 +5,7 @@ This is the main class for file explorer (datasets).
5
5
  from dataclasses import dataclass
6
6
  from typing import Union
7
7
  from cloudos_cli.clos import Cloudos
8
+ from cloudos_cli.utils.errors import BadRequestException
8
9
  from cloudos_cli.utils.requests import retry_requests_get, retry_requests_put, retry_requests_post, retry_requests_delete
9
10
  import json
10
11
 
@@ -127,6 +128,8 @@ class Datasets(Cloudos):
127
128
  f' and an importsFile \'{importsfile}\' was not found')
128
129
  else:
129
130
  raise ValueError(f'[ERROR] No {name} element in {resource} was found')
131
+
132
+
130
133
  def list_project_content(self):
131
134
  """
132
135
  Fetch the information of the directories present in the projects.
@@ -150,6 +153,8 @@ class Datasets(Cloudos):
150
153
  self.project_id,
151
154
  self.workspace_id),
152
155
  headers=headers, verify=self.verify)
156
+ if r.status_code >= 400:
157
+ raise BadRequestException(r)
153
158
  raw = r.json()
154
159
  datasets = raw.get("datasets", [])
155
160
  # Normalize response
@@ -195,6 +200,8 @@ class Datasets(Cloudos):
195
200
  folder_id,
196
201
  self.workspace_id),
197
202
  headers=headers, verify=self.verify)
203
+ if r.status_code >= 400:
204
+ raise BadRequestException(r)
198
205
  return r.json()
199
206
 
200
207
  def list_s3_folder_content(self, s3_bucket_name, s3_relative_path):
@@ -224,6 +231,8 @@ class Datasets(Cloudos):
224
231
  s3_relative_path,
225
232
  self.workspace_id),
226
233
  headers=headers, verify=self.verify)
234
+ if r.status_code >= 400:
235
+ raise BadRequestException(r)
227
236
  raw = r.json()
228
237
 
229
238
  # Normalize response
@@ -265,8 +274,63 @@ class Datasets(Cloudos):
265
274
  folder_id,
266
275
  self.workspace_id),
267
276
  headers=headers, verify=self.verify)
277
+ if r.status_code >= 400:
278
+ raise BadRequestException(r)
268
279
  return r.json()
269
280
 
281
+ def list_azure_container_content(self, container_name: str, storage_account_name: str, path: str):
282
+ """
283
+ List contents of an Azure Blob container path.
284
+ """
285
+ headers = {
286
+ "Content-type": "application/json",
287
+ "apikey": self.apikey
288
+ }
289
+
290
+ url = f"{self.cloudos_url}/api/v1/data-access/azure/container-contents"
291
+ url += f"?containerName={container_name}&storageAccountName={storage_account_name}"
292
+ url += f"&path={path}&teamId={self.workspace_id}"
293
+
294
+ r = retry_requests_get(url, headers=headers, verify=self.verify)
295
+ if r.status_code >= 400:
296
+ raise BadRequestException(r)
297
+ raw = r.json()
298
+
299
+ # Normalize response to match existing expectations
300
+ normalized = {"folders": [], "files": []}
301
+ for item in raw.get("contents", []):
302
+ is_dir = item.get("isDir", False)
303
+
304
+ # Set a name field based on the last part of the blob path
305
+ path_str = item.get("path", "")
306
+ name = item.get("name") or path_str.rstrip("/").split("/")[-1]
307
+
308
+ # inject expected structure
309
+ if is_dir:
310
+ normalized["folders"].append({
311
+ "_id": item.get("_id"),
312
+ "name": name,
313
+ "folderType": "AzureBlobFolder",
314
+ "blobPrefix": path_str,
315
+ "blobContainerName": container_name,
316
+ "blobStorageAccountName": storage_account_name,
317
+ "kind": "Folder"
318
+ })
319
+ else:
320
+ normalized["files"].append({
321
+ "_id": item.get("_id"),
322
+ "name": name,
323
+ "fileType": "AzureBlobFile",
324
+ "blobName": path_str,
325
+ "blobContainerName": container_name,
326
+ "blobStorageAccountName": storage_account_name,
327
+ "sizeInBytes": item.get("size", 0),
328
+ "updatedAt": item.get("lastModified"),
329
+ "kind": "File"
330
+ })
331
+
332
+ return normalized
333
+
270
334
  def list_folder_content(self, path=None):
271
335
  """
272
336
  Wrapper to list contents of a CloudOS folder.
@@ -324,6 +388,21 @@ class Datasets(Cloudos):
324
388
  path_depth += 1
325
389
  break
326
390
 
391
+ elif folder_type == "AzureBlobFolder":
392
+ container_name = job_folder['blobContainerName']
393
+ storage_account_name = job_folder['blobStorageAccountName']
394
+ blob_prefix = job_folder['blobPrefix']
395
+ # trailing slash is mandatory for azure, otherwise it will not list the content of thefolde, just the folder
396
+ if not blob_prefix.endswith('/'):
397
+ blob_prefix += '/'
398
+
399
+ if path_depth == len(parts) - 1:
400
+ return self.list_azure_container_content(container_name, storage_account_name, blob_prefix)
401
+ else:
402
+ sub_path = '/'.join(parts[0:path_depth+1])
403
+ folder_content = self.list_folder_content(sub_path)
404
+ path_depth += 1
405
+ break
327
406
  else:
328
407
  raise ValueError(f"Unsupported folder type '{folder_type}' for path '{path}'")
329
408
 
@@ -366,6 +445,8 @@ class Datasets(Cloudos):
366
445
  }
367
446
  }
368
447
  response = retry_requests_put(url, headers=headers, data=json.dumps(payload), verify=self.verify)
448
+ if response.status_code >= 400:
449
+ raise BadRequestException(response)
369
450
  return response
370
451
 
371
452
  def rename_item(self, item_id: str, new_name: str, kind: str):
@@ -403,6 +484,8 @@ class Datasets(Cloudos):
403
484
  }
404
485
 
405
486
  response = retry_requests_put(url, headers=headers, data=json.dumps(payload), verify=self.verify)
487
+ if response.status_code >= 400:
488
+ raise BadRequestException(response)
406
489
  return response
407
490
 
408
491
  def copy_item(self, item, destination_id, destination_kind):
@@ -446,7 +529,8 @@ class Datasets(Cloudos):
446
529
  else:
447
530
  raise ValueError(f"Unknown item type for copy: {item.get('name')}")
448
531
  response = retry_requests_post(url, headers=headers, json=payload)
449
-
532
+ if response.status_code >= 400:
533
+ raise BadRequestException(response)
450
534
  return response
451
535
 
452
536
  def create_virtual_folder(self, name: str, parent_id: str, parent_kind: str):
@@ -487,6 +571,8 @@ class Datasets(Cloudos):
487
571
  }
488
572
 
489
573
  response = retry_requests_post(url, headers=headers, json=payload, verify=self.verify)
574
+ if response.status_code >= 400:
575
+ raise BadRequestException(response)
490
576
  return response
491
577
 
492
578
  def delete_item(self, item_id: str, kind: str):
@@ -517,4 +603,6 @@ class Datasets(Cloudos):
517
603
  }
518
604
 
519
605
  response = retry_requests_delete(url, headers=headers, verify=self.verify)
606
+ if response.status_code >= 400:
607
+ raise BadRequestException(response)
520
608
  return response
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudos_cli
3
- Version: 2.38.3
3
+ Version: 2.40.0
4
4
  Summary: Python package for interacting with CloudOS
5
5
  Home-page: https://github.com/lifebit-ai/cloudos-cli
6
6
  Author: David Piñeyro
@@ -1006,8 +1006,8 @@ If you require more information on the files and folder listed, you can use the
1006
1006
  - Owner
1007
1007
  - Size in human readable format
1008
1008
  - Last updated
1009
- - Filepath (the file or folder name)
1010
- - S3 Path
1009
+ - File Name (the file or folder name)
1010
+ - Storage Path
1011
1011
 
1012
1012
  ##### Moving files
1013
1013
 
@@ -1 +0,0 @@
1
- __version__ = '2.38.3'
File without changes
File without changes
File without changes