NGPIris 5.4.1__tar.gz → 5.4.2__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 (54) hide show
  1. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris/cli/__init__.py +45 -7
  2. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris/hcp/hcp.py +47 -7
  3. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris.egg-info/PKG-INFO +1 -1
  4. {ngpiris-5.4.1 → ngpiris-5.4.2}/PKG-INFO +1 -1
  5. {ngpiris-5.4.1 → ngpiris-5.4.2}/pyproject.toml +1 -1
  6. {ngpiris-5.4.1 → ngpiris-5.4.2}/tests/test_hcp.py +12 -5
  7. {ngpiris-5.4.1 → ngpiris-5.4.2}/LICENSE +0 -0
  8. {ngpiris-5.4.1 → ngpiris-5.4.2}/MANIFEST.in +0 -0
  9. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris/__init__.py +0 -0
  10. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris/hci/__init__.py +0 -0
  11. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris/hci/exceptions.py +0 -0
  12. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris/hci/hci.py +0 -0
  13. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris/hci/helpers.py +0 -0
  14. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris/hcp/__init__.py +0 -0
  15. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris/hcp/exceptions.py +0 -0
  16. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris/hcp/helpers.py +0 -0
  17. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris/hcp/statistics.py +0 -0
  18. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris/parse_credentials/__init__.py +0 -0
  19. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris/parse_credentials/parse_credentials.py +0 -0
  20. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris/utils/__init__.py +0 -0
  21. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris/utils/utils.py +0 -0
  22. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris.egg-info/SOURCES.txt +0 -0
  23. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris.egg-info/dependency_links.txt +0 -0
  24. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris.egg-info/entry_points.txt +0 -0
  25. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris.egg-info/requires.txt +0 -0
  26. {ngpiris-5.4.1 → ngpiris-5.4.2}/NGPIris.egg-info/top_level.txt +0 -0
  27. {ngpiris-5.4.1 → ngpiris-5.4.2}/README.md +0 -0
  28. {ngpiris-5.4.1 → ngpiris-5.4.2}/docs/IRIS_5_changelog.md +0 -0
  29. {ngpiris-5.4.1 → ngpiris-5.4.2}/docs/Makefile +0 -0
  30. {ngpiris-5.4.1 → ngpiris-5.4.2}/docs/NGPIris.cli.rst +0 -0
  31. {ngpiris-5.4.1 → ngpiris-5.4.2}/docs/NGPIris.hci.rst +0 -0
  32. {ngpiris-5.4.1 → ngpiris-5.4.2}/docs/NGPIris.hcp.rst +0 -0
  33. {ngpiris-5.4.1 → ngpiris-5.4.2}/docs/NGPIris.parse_credentials.rst +0 -0
  34. {ngpiris-5.4.1 → ngpiris-5.4.2}/docs/NGPIris.rst +0 -0
  35. {ngpiris-5.4.1 → ngpiris-5.4.2}/docs/NGPIris.utils.rst +0 -0
  36. {ngpiris-5.4.1 → ngpiris-5.4.2}/docs/Tutorial.md +0 -0
  37. {ngpiris-5.4.1 → ngpiris-5.4.2}/docs/conf.py +0 -0
  38. {ngpiris-5.4.1 → ngpiris-5.4.2}/docs/index.rst +0 -0
  39. {ngpiris-5.4.1 → ngpiris-5.4.2}/docs/make.bat +0 -0
  40. {ngpiris-5.4.1 → ngpiris-5.4.2}/docs/modules.rst +0 -0
  41. {ngpiris-5.4.1 → ngpiris-5.4.2}/setup.cfg +0 -0
  42. {ngpiris-5.4.1 → ngpiris-5.4.2}/tests/data/80MB_test_file +0 -0
  43. {ngpiris-5.4.1 → ngpiris-5.4.2}/tests/data/a folder of data/80MB_test_file_0 +0 -0
  44. {ngpiris-5.4.1 → ngpiris-5.4.2}/tests/data/a folder of data/80MB_test_file_1 +0 -0
  45. {ngpiris-5.4.1 → ngpiris-5.4.2}/tests/data/complete_creds_0.json +0 -0
  46. {ngpiris-5.4.1 → ngpiris-5.4.2}/tests/data/complete_creds_1.json +0 -0
  47. {ngpiris-5.4.1 → ngpiris-5.4.2}/tests/data/complete_creds_2.json +0 -0
  48. {ngpiris-5.4.1 → ngpiris-5.4.2}/tests/data/incomplete_creds_0.json +0 -0
  49. {ngpiris-5.4.1 → ngpiris-5.4.2}/tests/data/incomplete_creds_1.json +0 -0
  50. {ngpiris-5.4.1 → ngpiris-5.4.2}/tests/data/incomplete_creds_2.json +0 -0
  51. {ngpiris-5.4.1 → ngpiris-5.4.2}/tests/data/incomplete_creds_3.json +0 -0
  52. {ngpiris-5.4.1 → ngpiris-5.4.2}/tests/data/incomplete_creds_4.json +0 -0
  53. {ngpiris-5.4.1 → ngpiris-5.4.2}/tests/test_hci.py +0 -0
  54. {ngpiris-5.4.1 → ngpiris-5.4.2}/tests/test_parse_credentials.py +0 -0
@@ -6,6 +6,7 @@ from pathlib import Path
6
6
  from boto3 import set_stream_logger
7
7
  from typing import Any, Generator
8
8
  from bitmath import Byte, TiB
9
+ import sys
9
10
 
10
11
  from NGPIris.hcp import HCPHandler
11
12
 
@@ -41,19 +42,32 @@ def add_trailing_slash(path : str) -> str:
41
42
  help = "Get the debug log for running a command",
42
43
  is_flag = True
43
44
  )
45
+ @click.option(
46
+ "-tc",
47
+ "--transfer_config",
48
+ help = "Use a custom transfer config for uploads or downloads",
49
+ )
44
50
  @click.version_option(package_name = "NGPIris")
45
51
  @click.pass_context
46
- def cli(context : Context, credentials : str, debug : bool):
52
+ def cli(context : Context, credentials : str, debug : bool, transfer_config : str):
47
53
  """
48
54
  NGP Intelligence and Repository Interface Software, IRIS.
49
55
 
50
56
  CREDENTIALS refers to the path to the JSON credentials file.
51
57
  """
58
+
59
+ if transfer_config:
60
+ context.ensure_object(dict)
61
+ context.obj["hcph"] = HCPHandler(credentials, custom_config_path = transfer_config)
62
+ else:
63
+ context.ensure_object(dict)
64
+ context.obj["hcph"] = HCPHandler(credentials)
65
+
52
66
  if debug:
53
67
  set_stream_logger(name="")
54
-
55
- context.ensure_object(dict)
56
- context.obj["hcph"] = HCPHandler(credentials)
68
+ click.echo(
69
+ context.obj["hcph"].transfer_config.__dict__
70
+ )
57
71
 
58
72
  @cli.command()
59
73
  @click.argument("bucket")
@@ -65,8 +79,25 @@ def cli(context : Context, credentials : str, debug : bool):
65
79
  help = "Simulate the command execution without making actual changes. Useful for testing and verification",
66
80
  is_flag = True
67
81
  )
82
+ @click.option(
83
+ "-um",
84
+ "--upload_mode",
85
+ help = "Choose an upload method. Default upload mode is STANDARD which uses a basic multipart upload. Use another mode than STANDARD if that mode misbehaves",
86
+ type = click.Choice(
87
+ ["STANDARD", "SIMPLE", "EQUAL_PARTS"],
88
+ case_sensitive = False
89
+ ),
90
+ default = "STANDARD"
91
+ )
92
+ @click.option(
93
+ "-ep",
94
+ "--equal_parts",
95
+ help = "Supplementary option when using the EQUAL_PARTS upload mode. Splits each file into a given number of parts. Must be a positive integer",
96
+ type = int,
97
+ default = 5
98
+ )
68
99
  @click.pass_context
69
- def upload(context : Context, bucket : str, source : str, destination : str, dry_run : bool):
100
+ def upload(context : Context, bucket : str, source : str, destination : str, dry_run : bool, upload_mode : str, equal_parts : int):
70
101
  """
71
102
  Upload files to an HCP bucket/namespace.
72
103
 
@@ -76,6 +107,13 @@ def upload(context : Context, bucket : str, source : str, destination : str, dry
76
107
 
77
108
  DESTINATION is the destination path on the HCP.
78
109
  """
110
+
111
+ if equal_parts <= 0:
112
+ click.echo("Error: --equal_parts value must be a positive integer", err=True)
113
+ sys.exit(1)
114
+
115
+ upload_mode_choice = HCPHandler.UploadMode(upload_mode.lower())
116
+
79
117
  hcph : HCPHandler = get_HCPHandler(context)
80
118
  hcph.mount_bucket(bucket)
81
119
  destination = add_trailing_slash(destination)
@@ -84,14 +122,14 @@ def upload(context : Context, bucket : str, source : str, destination : str, dry
84
122
  if dry_run:
85
123
  click.echo("This command would have uploaded the folder \"" + source + "\" to \"" + destination + "\"")
86
124
  else:
87
- hcph.upload_folder(source, destination)
125
+ hcph.upload_folder(source, destination, upload_mode = upload_mode_choice, equal_parts = equal_parts)
88
126
  else:
89
127
  file_name = Path(source).name
90
128
  destination += file_name
91
129
  if dry_run:
92
130
  click.echo("This command would have uploaded the file \"" + source + "\" to \"" + destination + "\"")
93
131
  else:
94
- hcph.upload_file(source, destination)
132
+ hcph.upload_file(source, destination, upload_mode = upload_mode_choice, equal_parts = equal_parts)
95
133
 
96
134
  @cli.command()
97
135
  @click.argument("bucket")
@@ -42,6 +42,7 @@ from urllib3 import disable_warnings
42
42
  from tqdm import tqdm
43
43
  from bitmath import TiB, Byte
44
44
 
45
+ from enum import Enum
45
46
  from typing import Generator
46
47
 
47
48
  _KB = 1024
@@ -468,9 +469,14 @@ class HCPHandler:
468
469
  self.download_file(object["Key"], p.as_posix(), show_progress_bar = show_progress_bar)
469
470
  else:
470
471
  raise NotADirectory(local_folder_path + " is not a directory")
472
+
473
+ class UploadMode(Enum):
474
+ STANDARD = "standard"
475
+ SIMPLE = "simple"
476
+ EQUAL_PARTS = "equal_parts"
471
477
 
472
478
  @check_mounted
473
- def upload_file(self, local_file_path : str, key : str = "", show_progress_bar : bool = True) -> None:
479
+ def upload_file(self, local_file_path : str, key : str = "", show_progress_bar : bool = True, upload_mode : UploadMode = UploadMode.STANDARD, equal_parts : int = 5) -> None:
474
480
  """
475
481
  Upload one file to the mounted bucket
476
482
 
@@ -483,6 +489,16 @@ class HCPHandler:
483
489
  :param show_progress_bar: Boolean choice of displaying a progress bar. Defaults to True
484
490
  :type show_progress_bar: bool, optional
485
491
 
492
+ :param upload_mode:
493
+ The upload mode of the transfer is any of the following:
494
+ HCPHandler.UploadMode.STANDARD,
495
+ HCPHandler.UploadMode.SIMPLE,
496
+ HCPHandler.UploadMode.EQUAL_PARTS\n
497
+ :type upload_mode: UploadMode, optional
498
+
499
+ :param equal_parts: The number of equal parts that each file should be divided into when using the HCPHandler.UploadMode.EQUAL_PARTS mode. Default is 5
500
+ :type equal_parts: int, optional
501
+
486
502
  :raises RuntimeError: If the \"\\\" is used in the file path
487
503
 
488
504
  :raises ObjectAlreadyExist: If the object already exist on the mounted bucket
@@ -499,8 +515,17 @@ class HCPHandler:
499
515
  if self.object_exists(key):
500
516
  raise ObjectAlreadyExist("The object \"" + key + "\" already exist in the mounted bucket")
501
517
  else:
518
+ file_size : int = stat(local_file_path).st_size
519
+
520
+ match upload_mode:
521
+ case HCPHandler.UploadMode.STANDARD:
522
+ config = self.transfer_config
523
+ case HCPHandler.UploadMode.SIMPLE:
524
+ config = TransferConfig(multipart_chunksize = file_size)
525
+ case HCPHandler.UploadMode.EQUAL_PARTS:
526
+ config = TransferConfig(multipart_chunksize = round(file_size / equal_parts))
527
+
502
528
  if show_progress_bar:
503
- file_size : int = stat(local_file_path).st_size
504
529
  with tqdm(
505
530
  total = file_size,
506
531
  unit = "B",
@@ -511,7 +536,7 @@ class HCPHandler:
511
536
  Filename = local_file_path,
512
537
  Bucket = self.bucket_name,
513
538
  Key = key,
514
- Config = self.transfer_config,
539
+ Config = config,
515
540
  Callback = lambda bytes_transferred : pbar.update(bytes_transferred)
516
541
  )
517
542
  else:
@@ -519,12 +544,11 @@ class HCPHandler:
519
544
  Filename = local_file_path,
520
545
  Bucket = self.bucket_name,
521
546
  Key = key,
522
- Config = self.transfer_config,
547
+ Config = config,
523
548
  )
524
549
 
525
-
526
550
  @check_mounted
527
- def upload_folder(self, local_folder_path : str, key : str = "", show_progress_bar : bool = True) -> None:
551
+ def upload_folder(self, local_folder_path : str, key : str = "", show_progress_bar : bool = True, upload_mode : UploadMode = UploadMode.STANDARD, equal_parts : int = 5) -> None:
528
552
  """
529
553
  Upload the contents of a folder to the mounted bucket
530
554
 
@@ -536,6 +560,16 @@ class HCPHandler:
536
560
 
537
561
  :param show_progress_bar: Boolean choice of displaying a progress bar. Defaults to True
538
562
  :type show_progress_bar: bool, optional
563
+
564
+ :param upload_mode:
565
+ The upload mode of the transfer is any of the following:
566
+ HCPHandler.UploadMode.STANDARD,
567
+ HCPHandler.UploadMode.SIMPLE,
568
+ HCPHandler.UploadMode.EQUAL_PARTS\n
569
+ :type upload_mode: UploadMode, optional
570
+
571
+ :param equal_parts: The number of equal parts that each file should be divided into when using the HCPHandler.UploadMode.EQUAL_PARTS mode. Default is 5
572
+ :type equal_parts: int, optional
539
573
  """
540
574
  raise_path_error(local_folder_path)
541
575
 
@@ -544,7 +578,13 @@ class HCPHandler:
544
578
  filenames = listdir(local_folder_path)
545
579
 
546
580
  for filename in filenames:
547
- self.upload_file(local_folder_path + filename, key + filename, show_progress_bar = show_progress_bar)
581
+ self.upload_file(
582
+ local_folder_path + filename,
583
+ key + filename,
584
+ show_progress_bar = show_progress_bar,
585
+ upload_mode = upload_mode,
586
+ equal_parts = equal_parts
587
+ )
548
588
 
549
589
  @check_mounted
550
590
  def delete_objects(self, keys : list[str], verbose : bool = True) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: NGPIris
3
- Version: 5.4.1
3
+ Version: 5.4.2
4
4
  Author-email: Erik Brink <erik.brink@gu.se>, Isak Sylvin <isak.sylvin@gu.se>
5
5
  Description-Content-Type: text/markdown
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: NGPIris
3
- Version: 5.4.1
3
+ Version: 5.4.2
4
4
  Author-email: Erik Brink <erik.brink@gu.se>, Isak Sylvin <isak.sylvin@gu.se>
5
5
  Description-Content-Type: text/markdown
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "NGPIris"
3
- version = "5.4.1"
3
+ version = "5.4.2"
4
4
  readme = "README.md"
5
5
  dependencies = [
6
6
  "requests >= 2.31.0",
@@ -1,12 +1,10 @@
1
1
 
2
-
3
2
  from pathlib import Path
4
3
  from filecmp import cmp
5
4
  from typing import Any, Callable
6
-
7
5
  from conftest import CustomConfig
8
-
9
6
  from NGPIris.hcp import HCPHandler
7
+ from icecream import ic
10
8
 
11
9
  # --------------------------- Helper fucntions ---------------------------------
12
10
 
@@ -70,6 +68,15 @@ def test_upload_file(custom_config : CustomConfig) -> None:
70
68
  show_progress_bar = False
71
69
  )
72
70
 
71
+ # Test every upload mode
72
+ for mode in HCPHandler.UploadMode:
73
+ ic(mode, custom_config.test_file_path + "_" + str(mode).replace(".", "_"))
74
+ custom_config.hcp_h.upload_file(
75
+ custom_config.test_file_path,
76
+ custom_config.test_file_path + "_" + str(mode).replace(".", "_"),
77
+ upload_mode = mode
78
+ )
79
+
73
80
  def test_upload_file_without_mounting(custom_config : CustomConfig) -> None:
74
81
  _hcp_h = custom_config.hcp_h
75
82
  _without_mounting(_hcp_h, HCPHandler.upload_file)
@@ -119,8 +126,6 @@ def test_get_file_in_sub_directory(custom_config : CustomConfig) -> None:
119
126
  assert custom_config.hcp_h.object_exists(custom_config.test_file_path)
120
127
  assert custom_config.hcp_h.get_object(custom_config.test_file_path)
121
128
 
122
- from icecream import ic
123
-
124
129
  def test_download_file(custom_config : CustomConfig) -> None:
125
130
  test_mount_bucket(custom_config)
126
131
  Path(custom_config.result_path).mkdir()
@@ -215,6 +220,8 @@ def test_delete_file(custom_config : CustomConfig) -> None:
215
220
  test_mount_bucket(custom_config)
216
221
  custom_config.hcp_h.delete_object(custom_config.test_file_path)
217
222
  custom_config.hcp_h.delete_object(custom_config.test_file_path + "_no_progress_bar")
223
+ for mode in HCPHandler.UploadMode:
224
+ custom_config.hcp_h.delete_object(custom_config.test_file_path + "_" + str(mode).replace(".", "_"))
218
225
  custom_config.hcp_h.delete_object("a_sub_directory/a_file")
219
226
  custom_config.hcp_h.delete_object("a_sub_directory")
220
227
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes