yellowdog-python-examples 7.8.2__tar.gz → 7.8.3__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 (77) hide show
  1. {yellowdog-python-examples-7.8.2/yellowdog_python_examples.egg-info → yellowdog-python-examples-7.8.3}/PKG-INFO +2 -2
  2. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/requirements.txt +1 -1
  3. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/tests/test_demos.py +22 -0
  4. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/tests/test_entrypoints.py +6 -0
  5. yellowdog-python-examples-7.8.3/yd_commands/__init__.py +1 -0
  6. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/args.py +12 -11
  7. yellowdog-python-examples-7.8.3/yd_commands/boost.py +38 -0
  8. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/cloudwizard_aws.py +79 -69
  9. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/cloudwizard_azure.py +7 -5
  10. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/cloudwizard_common.py +8 -6
  11. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/cloudwizard_gcp.py +9 -7
  12. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/interactive.py +1 -5
  13. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/list.py +7 -5
  14. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/load_config.py +9 -8
  15. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/printing.py +163 -131
  16. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/start_hold_common.py +26 -20
  17. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/variables.py +6 -4
  18. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3/yellowdog_python_examples.egg-info}/PKG-INFO +2 -2
  19. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yellowdog_python_examples.egg-info/requires.txt +1 -1
  20. yellowdog-python-examples-7.8.2/yd_commands/__init__.py +0 -1
  21. yellowdog-python-examples-7.8.2/yd_commands/boost.py +0 -33
  22. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/LICENSE +0 -0
  23. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/PYPI_README.md +0 -0
  24. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/README.md +0 -0
  25. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/pyproject.toml +0 -0
  26. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/setup.cfg +0 -0
  27. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/setup.py +0 -0
  28. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/tests/test_create_remove.py +0 -0
  29. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/tests/test_dryruns.py +0 -0
  30. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/tests/test_gui.py +0 -0
  31. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/tests/test_list.py +0 -0
  32. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/tests/test_objects.py +0 -0
  33. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/tests/test_variable_processing.py +0 -0
  34. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/abort.py +0 -0
  35. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/admin.py +0 -0
  36. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/cancel.py +0 -0
  37. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/check_imports.py +0 -0
  38. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/cloudwizard.py +0 -0
  39. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/cloudwizard_aws_types.py +0 -0
  40. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/compact_json.py +0 -0
  41. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/config_types.py +0 -0
  42. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/create.py +0 -0
  43. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/csv_data.py +0 -0
  44. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/delete.py +0 -0
  45. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/download.py +0 -0
  46. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/follow.py +0 -0
  47. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/follow_utils.py +0 -0
  48. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/format_json.py +0 -0
  49. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/hold.py +0 -0
  50. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/id_utils.py +0 -0
  51. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/instantiate.py +0 -0
  52. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/items.py +0 -0
  53. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/jsonnet2json.py +0 -0
  54. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/load_resources.py +0 -0
  55. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/object_utilities.py +0 -0
  56. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/property_names.py +0 -0
  57. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/provision.py +0 -0
  58. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/provision_utils.py +0 -0
  59. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/remove.py +0 -0
  60. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/resize.py +0 -0
  61. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/settings.py +0 -0
  62. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/shutdown.py +0 -0
  63. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/start.py +0 -0
  64. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/submit.py +0 -0
  65. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/submit_utils.py +0 -0
  66. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/terminate.py +0 -0
  67. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/type_check.py +0 -0
  68. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/upload.py +0 -0
  69. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/upload_utils.py +0 -0
  70. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/utils.py +0 -0
  71. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/validate_properties.py +0 -0
  72. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/version.py +0 -0
  73. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yd_commands/wrapper.py +0 -0
  74. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yellowdog_python_examples.egg-info/SOURCES.txt +0 -0
  75. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yellowdog_python_examples.egg-info/dependency_links.txt +0 -0
  76. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yellowdog_python_examples.egg-info/entry_points.txt +0 -0
  77. {yellowdog-python-examples-7.8.2 → yellowdog-python-examples-7.8.3}/yellowdog_python_examples.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: yellowdog-python-examples
3
- Version: 7.8.2
3
+ Version: 7.8.3
4
4
  Summary: Example Python commands using the YellowDog Python SDK
5
5
  Home-page: https://github.com/yellowdog/python-examples
6
6
  Author: YellowDog Limited
@@ -19,7 +19,7 @@ Requires-Dist: yellowdog-sdk>=8.0.0
19
19
  Requires-Dist: toml
20
20
  Requires-Dist: tabulate>=0.9.0
21
21
  Requires-Dist: PyPAC>=0.16.4
22
- Requires-Dist: rich>=13.5.2
22
+ Requires-Dist: rich>=13.7.1
23
23
  Requires-Dist: requests
24
24
  Requires-Dist: boto3
25
25
  Requires-Dist: google-cloud-compute
@@ -2,7 +2,7 @@ yellowdog-sdk >= 8.0.0
2
2
  toml
3
3
  tabulate >= 0.9.0
4
4
  PyPAC >= 0.16.4
5
- rich >= 13.5.2
5
+ rich >= 13.7.1
6
6
  requests
7
7
  boto3
8
8
  google-cloud-compute
@@ -8,6 +8,7 @@ from cli_test_helpers import shell
8
8
 
9
9
  DEMO_DIR = "../python-examples-demos"
10
10
  CMD_SEQ = "yd-provision && yd-submit -f && yd-terminate -y && yd-delete -y"
11
+ NEXTFLOW = "/Users/pwt/nextflow/nextflow"
11
12
 
12
13
 
13
14
  @pytest.mark.demos
@@ -51,3 +52,24 @@ class TestDemos:
51
52
  def test_cmd_exe(self):
52
53
  result = shell(f"cd {DEMO_DIR}/cmd.exe && {CMD_SEQ}")
53
54
  assert result.exit_code == 0
55
+
56
+ def test_nextflow_image_montage(self):
57
+ result = shell(
58
+ f"cd {DEMO_DIR}/nextflow/image-montage && {NEXTFLOW} main.nf "
59
+ "&& cd .. && ./cleanup.sh"
60
+ )
61
+ assert result.exit_code == 0
62
+
63
+ def test_nextflow_salmon_rna(self):
64
+ result = shell(
65
+ f"cd {DEMO_DIR}/nextflow/salmon-rna && {NEXTFLOW} main.nf "
66
+ "&& cd .. && ./cleanup.sh"
67
+ )
68
+ assert result.exit_code == 0
69
+
70
+ def test_cmd_modelled_on_premise(self):
71
+ result = shell(
72
+ f"cd {DEMO_DIR}/modelled-on-premise && yd-instantiate "
73
+ "&& sleep 120 && yd-terminate -y"
74
+ )
75
+ assert result.exit_code == 0
@@ -42,3 +42,9 @@ def test_entrypoints():
42
42
  assert result.exit_code == 0
43
43
  result = shell("yd-cloudwizard --help")
44
44
  assert result.exit_code == 0
45
+ result = shell("yd-start --help")
46
+ assert result.exit_code == 0
47
+ result = shell("yd-hold --help")
48
+ assert result.exit_code == 0
49
+ result = shell("yd-boost --help")
50
+ assert result.exit_code == 0
@@ -0,0 +1 @@
1
+ __version__ = "7.8.3"
@@ -131,8 +131,6 @@ class CLIParser:
131
131
  "remove",
132
132
  "cloudwizard",
133
133
  "follow",
134
- "hold",
135
- "start",
136
134
  ]
137
135
  ):
138
136
  parser.add_argument(
@@ -338,6 +336,8 @@ class CLIParser:
338
336
  "resize",
339
337
  "cloudwizard",
340
338
  "boost",
339
+ "hold",
340
+ "start",
341
341
  ]
342
342
  ):
343
343
  parser.add_argument(
@@ -345,7 +345,7 @@ class CLIParser:
345
345
  "-y",
346
346
  action="store_true",
347
347
  required=False,
348
- help="perform destructive actions without requiring user confirmation",
348
+ help="perform modifying actions without requiring user confirmation",
349
349
  )
350
350
 
351
351
  if any(module in sys.argv[0] for module in ["delete", "download"]):
@@ -639,18 +639,19 @@ class CLIParser:
639
639
  )
640
640
 
641
641
  if "boost" in sys.argv[0]:
642
- parser.add_argument(
643
- "allowance",
644
- metavar="<allowance-ID>",
645
- type=str,
646
- help=("the YellowDog ID of the allowance to boost"),
647
- )
648
642
  parser.add_argument(
649
643
  "boost_hours",
650
644
  metavar="<boost hours>",
651
645
  type=int,
652
646
  help="the number of hours to boost the allowance by",
653
647
  )
648
+ parser.add_argument(
649
+ "allowances",
650
+ metavar="<allowance-ID> [<allowance-ID>]",
651
+ nargs="+",
652
+ type=str,
653
+ help="the YellowDog ID(s) of the allowance(s) to boost",
654
+ )
654
655
 
655
656
  if "shutdown" in sys.argv[0]:
656
657
  parser.add_argument(
@@ -1401,8 +1402,8 @@ class CLIParser:
1401
1402
 
1402
1403
  @property
1403
1404
  @allow_missing_attribute
1404
- def allowance(self) -> str:
1405
- return self.args.allowance
1405
+ def allowance_list(self) -> List[str]:
1406
+ return self.args.allowances
1406
1407
 
1407
1408
  @property
1408
1409
  @allow_missing_attribute
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ A script to boost allowances.
5
+ """
6
+
7
+ from yd_commands.interactive import confirmed
8
+ from yd_commands.printing import print_error, print_log
9
+ from yd_commands.wrapper import ARGS_PARSER, CLIENT, main_wrapper
10
+
11
+
12
+ @main_wrapper
13
+ def main():
14
+
15
+ count = 0
16
+ for allowance in ARGS_PARSER.allowance_list:
17
+ if not confirmed(
18
+ f"Boost Allowance {allowance} by {ARGS_PARSER.boost_hours} hours?"
19
+ ):
20
+ continue
21
+ try:
22
+ CLIENT.allowances_client.boost_allowance_by_id(
23
+ allowance, ARGS_PARSER.boost_hours
24
+ )
25
+ print_log(
26
+ f"Boosted Allowance {allowance} by {ARGS_PARSER.boost_hours} hours"
27
+ )
28
+ count += 1
29
+ except Exception as e:
30
+ print_error(f"Unable to boost Allowance {allowance}: {e}")
31
+
32
+ if count > 1:
33
+ print_log(f"Boosted {count} allowances by {ARGS_PARSER.boost_hours} hours")
34
+
35
+
36
+ # Standalone entry point
37
+ if __name__ == "__main__":
38
+ main()
@@ -79,42 +79,44 @@ AWS_YD_IMAGE_REGIONS = [
79
79
 
80
80
  YELLOWDOG_POLICY = {
81
81
  "Version": "2012-10-17",
82
- "Statement": [{
83
- "Sid": "VisualEditor0",
84
- "Effect": "Allow",
85
- "Action": [
86
- "EC2:CancelSpotInstanceRequests",
87
- "EC2:CreateFleet",
88
- "EC2:CreateLaunchTemplate",
89
- "EC2:CreatePlacementGroup",
90
- "EC2:CreateTags",
91
- "EC2:DeleteFleets",
92
- "EC2:DeleteLaunchTemplate",
93
- "EC2:DeletePlacementGroup",
94
- "EC2:DescribeFleets",
95
- "EC2:DescribeInstanceTypes",
96
- "EC2:DescribeInstances",
97
- "EC2:DescribeLaunchTemplates",
98
- "EC2:DescribePlacementGroups",
99
- "EC2:DescribeSpotInstanceRequests",
100
- "EC2:ModifyFleet",
101
- "EC2:RebootInstances",
102
- "EC2:RequestSpotInstances",
103
- "EC2:RunInstances",
104
- "EC2:StartInstances",
105
- "EC2:StopInstances",
106
- "EC2:TerminateInstances",
107
- "S3:AbortMultipartUpload",
108
- "S3:CreateBucket",
109
- "S3:DeleteBucket",
110
- "S3:DeleteObject",
111
- "S3:GetObject",
112
- "S3:ListBucketMultipartUploads",
113
- "S3:ListMultipartUploadParts",
114
- "S3:PutObject",
115
- ],
116
- "Resource": "*",
117
- }],
82
+ "Statement": [
83
+ {
84
+ "Sid": "VisualEditor0",
85
+ "Effect": "Allow",
86
+ "Action": [
87
+ "EC2:CancelSpotInstanceRequests",
88
+ "EC2:CreateFleet",
89
+ "EC2:CreateLaunchTemplate",
90
+ "EC2:CreatePlacementGroup",
91
+ "EC2:CreateTags",
92
+ "EC2:DeleteFleets",
93
+ "EC2:DeleteLaunchTemplate",
94
+ "EC2:DeletePlacementGroup",
95
+ "EC2:DescribeFleets",
96
+ "EC2:DescribeInstanceTypes",
97
+ "EC2:DescribeInstances",
98
+ "EC2:DescribeLaunchTemplates",
99
+ "EC2:DescribePlacementGroups",
100
+ "EC2:DescribeSpotInstanceRequests",
101
+ "EC2:ModifyFleet",
102
+ "EC2:RebootInstances",
103
+ "EC2:RequestSpotInstances",
104
+ "EC2:RunInstances",
105
+ "EC2:StartInstances",
106
+ "EC2:StopInstances",
107
+ "EC2:TerminateInstances",
108
+ "S3:AbortMultipartUpload",
109
+ "S3:CreateBucket",
110
+ "S3:DeleteBucket",
111
+ "S3:DeleteObject",
112
+ "S3:GetObject",
113
+ "S3:ListBucketMultipartUploads",
114
+ "S3:ListMultipartUploadParts",
115
+ "S3:PutObject",
116
+ ],
117
+ "Resource": "*",
118
+ }
119
+ ],
118
120
  }
119
121
 
120
122
 
@@ -182,12 +184,14 @@ class AWSConfig(CommonCloudConfig):
182
184
  A list of regions can be supplied as an argument.
183
185
  The 'operation' argument must be 'add-ssh' or 'remove-ssh'.
184
186
  """
185
- ssh_ipv4_ingress_rule = [{
186
- "IpProtocol": "tcp",
187
- "FromPort": 22,
188
- "ToPort": 22,
189
- "IpRanges": [{"CidrIp": f"0.0.0.0/0"}],
190
- }]
187
+ ssh_ipv4_ingress_rule = [
188
+ {
189
+ "IpProtocol": "tcp",
190
+ "FromPort": 22,
191
+ "ToPort": 22,
192
+ "IpRanges": [{"CidrIp": f"0.0.0.0/0"}],
193
+ }
194
+ ]
191
195
  for region in (
192
196
  AWS_YD_IMAGE_REGIONS if selected_region is None else [selected_region]
193
197
  ):
@@ -359,11 +363,13 @@ class AWSConfig(CommonCloudConfig):
359
363
  "Creating YellowDog Namespace Configuration"
360
364
  f" 'S3:{self._get_s3_bucket_name()}' -> '{YD_NAMESPACE}'"
361
365
  )
362
- create_resources([
363
- self._generate_yd_namespace_configuration(
364
- namespace=YD_NAMESPACE, s3_bucket_name=self._get_s3_bucket_name()
365
- )
366
- ])
366
+ create_resources(
367
+ [
368
+ self._generate_yd_namespace_configuration(
369
+ namespace=YD_NAMESPACE, s3_bucket_name=self._get_s3_bucket_name()
370
+ )
371
+ ]
372
+ )
367
373
 
368
374
  # Sequence the Compute Requirement Templates before the Compute Source
369
375
  # Templates for subsequent removals.
@@ -390,11 +396,13 @@ class AWSConfig(CommonCloudConfig):
390
396
  self._remove_keyring(keyring_name=YD_KEYRING_NAME)
391
397
 
392
398
  # Remove the Namespace Configuration
393
- remove_resources([
394
- self._generate_yd_namespace_configuration(
395
- YD_NAMESPACE, self._get_s3_bucket_name()
396
- )
397
- ])
399
+ remove_resources(
400
+ [
401
+ self._generate_yd_namespace_configuration(
402
+ YD_NAMESPACE, self._get_s3_bucket_name()
403
+ )
404
+ ]
405
+ )
398
406
 
399
407
  def _gather_aws_network_information(self):
400
408
  """
@@ -961,23 +969,25 @@ class AWSConfig(CommonCloudConfig):
961
969
  """
962
970
  assert self._aws_user is not None
963
971
  s3_bucket_name = self._get_s3_bucket_name()
964
- return json.dumps({
965
- "Version": "2012-10-17",
966
- "Statement": [
967
- {
968
- "Effect": "Allow",
969
- "Principal": {"AWS": self._aws_user.arn},
970
- "Action": "s3:*",
971
- "Resource": f"arn:aws:s3:::{s3_bucket_name}/*",
972
- },
973
- {
974
- "Effect": "Allow",
975
- "Principal": {"AWS": self._aws_user.arn},
976
- "Action": "s3:ListBucket",
977
- "Resource": f"arn:aws:s3:::{s3_bucket_name}",
978
- },
979
- ],
980
- })
972
+ return json.dumps(
973
+ {
974
+ "Version": "2012-10-17",
975
+ "Statement": [
976
+ {
977
+ "Effect": "Allow",
978
+ "Principal": {"AWS": self._aws_user.arn},
979
+ "Action": "s3:*",
980
+ "Resource": f"arn:aws:s3:::{s3_bucket_name}/*",
981
+ },
982
+ {
983
+ "Effect": "Allow",
984
+ "Principal": {"AWS": self._aws_user.arn},
985
+ "Action": "s3:ListBucket",
986
+ "Resource": f"arn:aws:s3:::{s3_bucket_name}",
987
+ },
988
+ ],
989
+ }
990
+ )
981
991
 
982
992
  def _get_s3_bucket_name(self) -> str:
983
993
  """
@@ -491,11 +491,13 @@ class AzureConfig(CommonCloudConfig):
491
491
 
492
492
  # Create namespace configuration (Keyring/Credential creation must come first)
493
493
  print_log(f"Creating YellowDog Namespace Configuration '{YD_NAMESPACE}'")
494
- create_resources([
495
- self._generate_yd_namespace_configuration(
496
- namespace=YD_NAMESPACE, storage_blob_name=STORAGE_BLOB_NAME
497
- )
498
- ])
494
+ create_resources(
495
+ [
496
+ self._generate_yd_namespace_configuration(
497
+ namespace=YD_NAMESPACE, storage_blob_name=STORAGE_BLOB_NAME
498
+ )
499
+ ]
500
+ )
499
501
 
500
502
  # Save the list of resources
501
503
  # Sequence the Compute Requirement Templates before the Compute Source
@@ -186,12 +186,14 @@ class CommonCloudConfig(ABC):
186
186
  "type": "co.yellowdog.platform.model.StringAttributeConstraint",
187
187
  },
188
188
  ],
189
- "preferences": [{
190
- "attribute": "yd.cost",
191
- "rankOrder": "PREFER_LOWER",
192
- "type": "co.yellowdog.platform.model.NumericAttributePreference",
193
- "weight": 1,
194
- }],
189
+ "preferences": [
190
+ {
191
+ "attribute": "yd.cost",
192
+ "rankOrder": "PREFER_LOWER",
193
+ "type": "co.yellowdog.platform.model.NumericAttributePreference",
194
+ "weight": 1,
195
+ }
196
+ ],
195
197
  "maximumSourceCount": 5,
196
198
  "minimumSourceCount": 1,
197
199
  "strategyType": f"co.yellowdog.platform.model.{strategy}ProvisionStrategy",
@@ -158,13 +158,15 @@ class GCPConfig(CommonCloudConfig):
158
158
  client=self._client, name_prefix=YD_RESOURCE_PREFIX
159
159
  )
160
160
  self._remove_keyring(YD_KEYRING_NAME)
161
- remove_resources([
162
- self._generate_namespace_configuration(
163
- YD_NAMESPACE,
164
- self._generate_bucket_name(),
165
- credential_name=f"{YD_KEYRING_NAME}/{YD_CREDENTIAL_NAME}",
166
- )
167
- ])
161
+ remove_resources(
162
+ [
163
+ self._generate_namespace_configuration(
164
+ YD_NAMESPACE,
165
+ self._generate_bucket_name(),
166
+ credential_name=f"{YD_KEYRING_NAME}/{YD_CREDENTIAL_NAME}",
167
+ )
168
+ ]
169
+ )
168
170
 
169
171
  def _gather_regions(self):
170
172
  """
@@ -1,5 +1,5 @@
1
1
  """
2
- User interaction processing
2
+ User interaction processing utilities.
3
3
  """
4
4
 
5
5
  from os import getenv
@@ -76,8 +76,6 @@ def select(
76
76
  else (f"Please select items (e.g.: 1,2,4-7 / *){cancel_string}:")
77
77
  )
78
78
  selector_string = CONSOLE.input(print_string(input_string) + " ")
79
- if selector_string == "": # A quirk of Rich?
80
- print()
81
79
  if selector_string.strip() == "*":
82
80
  selector_string = f"1-{len(objects)}"
83
81
  selector_list = selector_string.split(",")
@@ -156,8 +154,6 @@ def confirmed(msg: str) -> bool:
156
154
  # Seek user confirmation
157
155
  while True:
158
156
  response = CONSOLE.input(print_string(f"{msg} (y/N):") + " ")
159
- if response == "": # Seems to be a quirk of Rich
160
- print()
161
157
  if response.lower() in ["y", "yes"]:
162
158
  print_log("Action confirmed by user")
163
159
  return True
@@ -519,10 +519,12 @@ def list_namespaces():
519
519
  # Assemble and print the table
520
520
  headings = [field.capitalize() for field in all_fields]
521
521
  headings.insert(0, "#")
522
- rows = sorted([
523
- [index + 1] + [namespace.get(field, "") for field in all_fields]
524
- for index, namespace in enumerate(namespace_list)
525
- ])
522
+ rows = sorted(
523
+ [
524
+ [index + 1] + [namespace.get(field, "") for field in all_fields]
525
+ for index, namespace in enumerate(namespace_list)
526
+ ]
527
+ )
526
528
  print()
527
529
  CONSOLE_TABLE.print(
528
530
  indent(tabulate(rows, headings, tablefmt="simple_outline")),
@@ -544,7 +546,7 @@ def list_allowances():
544
546
  )
545
547
  allowances: List[Allowance] = search_client.list_all()
546
548
  if len(allowances) == 0:
547
- print_log("No allowances to display")
549
+ print_log("No Allowances to display")
548
550
  return
549
551
 
550
552
  if not ARGS_PARSER.details:
@@ -4,7 +4,7 @@ Common utility functions, mostly related to loading configuration data.
4
4
 
5
5
  import os
6
6
  from os import getenv
7
- from os.path import dirname, relpath
7
+ from os.path import dirname, join, relpath
8
8
  from sys import exit
9
9
  from typing import Dict, Optional
10
10
 
@@ -41,18 +41,18 @@ from yd_commands.variables import (
41
41
  )
42
42
 
43
43
  # CLI > YD_CONF > 'config.toml'
44
- config_file = relpath(
44
+ CONFIG_FILE = relpath(
45
45
  getenv("YD_CONF", "config.toml")
46
46
  if ARGS_PARSER.config_file is None
47
47
  else ARGS_PARSER.config_file
48
48
  )
49
49
 
50
50
  try:
51
- CONFIG_FILE_DIR = dirname(config_file)
52
- print_log(f"Loading configuration data from: '{config_file}'")
53
- CONFIG_TOML: Dict = load_toml_file_with_variable_substitutions(config_file)
51
+ CONFIG_FILE_DIR = dirname(CONFIG_FILE)
52
+ print_log(f"Loading configuration data from: '{CONFIG_FILE}'")
53
+ CONFIG_TOML: Dict = load_toml_file_with_variable_substitutions(CONFIG_FILE)
54
54
  try:
55
- validate_properties(CONFIG_TOML, f"'{config_file}'")
55
+ validate_properties(CONFIG_TOML, f"'{CONFIG_FILE}'")
56
56
  except Exception as e:
57
57
  print_error(e)
58
58
  exit(1)
@@ -71,7 +71,7 @@ except FileNotFoundError as e:
71
71
 
72
72
  except (PermissionError, TomlDecodeError) as e:
73
73
  print_error(
74
- f"Unable to load configuration data from '{config_file}': {e}",
74
+ f"Unable to load configuration data from '{CONFIG_FILE}': {e}",
75
75
  )
76
76
  exit(1)
77
77
 
@@ -87,7 +87,7 @@ def load_config_common() -> ConfigCommon:
87
87
  try:
88
88
  common_section = CONFIG_TOML.get(COMMON_SECTION, {})
89
89
 
90
- # Check for IMPORT directive (common section in a separate file)
90
+ # Check for IMPORT directive ('common' section in a separate file)
91
91
  common_section_import_file = common_section.get(IMPORT_COMMON, None)
92
92
  if common_section_import_file is not None:
93
93
  common_section = import_toml(common_section_import_file)
@@ -160,6 +160,7 @@ def load_config_common() -> ConfigCommon:
160
160
 
161
161
 
162
162
  def import_toml(filename: str) -> Dict:
163
+ filename = relpath(join(CONFIG_FILE_DIR, process_variable_substitutions(filename)))
163
164
  print_log(f"Loading imported common configuration data from: '{filename}'")
164
165
  try:
165
166
  common_config: Dict = load_toml_file_with_variable_substitutions(filename)