toil 9.0.0__py3-none-any.whl → 9.1.1__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 (71) hide show
  1. toil/batchSystems/abstractBatchSystem.py +13 -5
  2. toil/batchSystems/abstractGridEngineBatchSystem.py +17 -5
  3. toil/batchSystems/kubernetes.py +13 -2
  4. toil/batchSystems/mesos/batchSystem.py +33 -2
  5. toil/batchSystems/slurm.py +191 -16
  6. toil/cwl/cwltoil.py +17 -82
  7. toil/fileStores/__init__.py +1 -1
  8. toil/fileStores/abstractFileStore.py +5 -2
  9. toil/fileStores/cachingFileStore.py +1 -1
  10. toil/job.py +30 -14
  11. toil/jobStores/abstractJobStore.py +24 -19
  12. toil/jobStores/aws/jobStore.py +862 -1963
  13. toil/jobStores/aws/utils.py +24 -270
  14. toil/jobStores/googleJobStore.py +25 -9
  15. toil/jobStores/utils.py +0 -327
  16. toil/leader.py +27 -22
  17. toil/lib/aws/config.py +22 -0
  18. toil/lib/aws/s3.py +477 -9
  19. toil/lib/aws/utils.py +22 -33
  20. toil/lib/checksum.py +88 -0
  21. toil/lib/conversions.py +33 -31
  22. toil/lib/directory.py +217 -0
  23. toil/lib/ec2.py +97 -29
  24. toil/lib/exceptions.py +2 -1
  25. toil/lib/expando.py +2 -2
  26. toil/lib/generatedEC2Lists.py +73 -16
  27. toil/lib/io.py +33 -2
  28. toil/lib/memoize.py +21 -7
  29. toil/lib/pipes.py +385 -0
  30. toil/lib/retry.py +1 -1
  31. toil/lib/threading.py +1 -1
  32. toil/lib/web.py +4 -5
  33. toil/provisioners/__init__.py +5 -2
  34. toil/provisioners/aws/__init__.py +43 -36
  35. toil/provisioners/aws/awsProvisioner.py +22 -13
  36. toil/provisioners/node.py +60 -12
  37. toil/resource.py +3 -13
  38. toil/test/__init__.py +14 -16
  39. toil/test/batchSystems/test_slurm.py +103 -14
  40. toil/test/cwl/staging_cat.cwl +27 -0
  41. toil/test/cwl/staging_make_file.cwl +25 -0
  42. toil/test/cwl/staging_workflow.cwl +43 -0
  43. toil/test/cwl/zero_default.cwl +61 -0
  44. toil/test/docs/scripts/tutorial_staging.py +17 -8
  45. toil/test/jobStores/jobStoreTest.py +23 -133
  46. toil/test/lib/aws/test_iam.py +7 -7
  47. toil/test/lib/aws/test_s3.py +30 -33
  48. toil/test/lib/aws/test_utils.py +9 -9
  49. toil/test/provisioners/aws/awsProvisionerTest.py +59 -6
  50. toil/test/src/autoDeploymentTest.py +2 -3
  51. toil/test/src/fileStoreTest.py +89 -87
  52. toil/test/utils/ABCWorkflowDebug/ABC.txt +1 -0
  53. toil/test/utils/ABCWorkflowDebug/debugWorkflow.py +4 -4
  54. toil/test/utils/toilKillTest.py +35 -28
  55. toil/test/wdl/md5sum/md5sum.json +1 -1
  56. toil/test/wdl/testfiles/gather.wdl +52 -0
  57. toil/test/wdl/wdltoil_test.py +120 -38
  58. toil/test/wdl/wdltoil_test_kubernetes.py +9 -0
  59. toil/utils/toilDebugFile.py +6 -3
  60. toil/utils/toilStats.py +17 -2
  61. toil/version.py +6 -6
  62. toil/wdl/wdltoil.py +1038 -549
  63. toil/worker.py +5 -2
  64. {toil-9.0.0.dist-info → toil-9.1.1.dist-info}/METADATA +12 -12
  65. {toil-9.0.0.dist-info → toil-9.1.1.dist-info}/RECORD +69 -61
  66. toil/lib/iterables.py +0 -112
  67. toil/test/docs/scripts/stagingExampleFiles/in.txt +0 -1
  68. {toil-9.0.0.dist-info → toil-9.1.1.dist-info}/WHEEL +0 -0
  69. {toil-9.0.0.dist-info → toil-9.1.1.dist-info}/entry_points.txt +0 -0
  70. {toil-9.0.0.dist-info → toil-9.1.1.dist-info}/licenses/LICENSE +0 -0
  71. {toil-9.0.0.dist-info → toil-9.1.1.dist-info}/top_level.txt +0 -0
@@ -28,9 +28,9 @@ logging.basicConfig(level=logging.DEBUG)
28
28
  class IAMTest(ToilTest):
29
29
  """Check that given permissions and associated functions perform correctly"""
30
30
 
31
- def test_permissions_iam(self):
31
+ def test_permissions_iam(self) -> None:
32
32
  granted_perms = {
33
- "*": {"Action": ["ec2:*", "iam:*", "s3:*", "sdb:*"], "NotAction": []}
33
+ "*": {"Action": ["ec2:*", "iam:*", "s3:*"], "NotAction": []}
34
34
  }
35
35
  assert (
36
36
  iam.policy_permissions_allow(
@@ -46,8 +46,8 @@ class IAMTest(ToilTest):
46
46
  is True
47
47
  )
48
48
 
49
- def test_negative_permissions_iam(self):
50
- granted_perms = {"*": {"Action": ["ec2:*", "s3:*", "sdb:*"], "NotAction": []}}
49
+ def test_negative_permissions_iam(self) -> None:
50
+ granted_perms = {"*": {"Action": ["ec2:*", "s3:*"], "NotAction": []}}
51
51
  assert (
52
52
  iam.policy_permissions_allow(
53
53
  granted_perms, iam.CLUSTER_LAUNCHING_PERMISSIONS
@@ -62,7 +62,7 @@ class IAMTest(ToilTest):
62
62
  is False
63
63
  )
64
64
 
65
- def test_wildcard_handling(self):
65
+ def test_wildcard_handling(self) -> None:
66
66
  assert iam.permission_matches_any("iam:CreateRole", ["iam:Create**"]) is True
67
67
  assert iam.permission_matches_any("iam:GetUser", ["iam:???????"]) is True
68
68
  assert iam.permission_matches_any("iam:ListRoleTags", ["iam:*?*Tags"]) is True
@@ -71,7 +71,7 @@ class IAMTest(ToilTest):
71
71
 
72
72
  @mock_aws
73
73
  @needs_aws_s3 # mock is incomplete, this avoid 'botocore.exceptions.NoCredentialsError: Unable to locate credentials'
74
- def test_get_policy_permissions(self):
74
+ def test_get_policy_permissions(self) -> None:
75
75
  mock_iam = boto3.client("iam")
76
76
 
77
77
  # username that moto pretends we have from client.get_user()
@@ -167,7 +167,7 @@ class IAMTest(ToilTest):
167
167
  assert notactions_set == set()
168
168
 
169
169
  @needs_aws_s3
170
- def test_create_delete_iam_role(self):
170
+ def test_create_delete_iam_role(self) -> None:
171
171
  region = "us-west-2"
172
172
  role_name = f'test{str(uuid4()).replace("-", "")}'
173
173
  with self.subTest("Create role w/policies."):
@@ -18,67 +18,64 @@ from typing import TYPE_CHECKING, Optional
18
18
 
19
19
  from toil.jobStores.aws.jobStore import AWSJobStore
20
20
  from toil.lib.aws.session import establish_boto3_session
21
- from toil.lib.aws.utils import create_s3_bucket, get_bucket_region
21
+ from toil.lib.aws.utils import create_s3_bucket, delete_s3_bucket, get_bucket_region
22
22
  from toil.test import ToilTest, needs_aws_s3
23
23
 
24
24
  logger = logging.getLogger(__name__)
25
25
  logging.basicConfig(level=logging.DEBUG)
26
26
 
27
+ if TYPE_CHECKING:
28
+ from mypy_boto3_s3 import S3ServiceResource
29
+ from mypy_boto3_s3.service_resource import Bucket
27
30
 
28
31
  @needs_aws_s3
29
32
  class S3Test(ToilTest):
30
33
  """Confirm the workarounds for us-east-1."""
31
34
 
32
- if TYPE_CHECKING:
33
- from mypy_boto3_s3 import S3ServiceResource
34
- from mypy_boto3_s3.service_resource import Bucket
35
-
36
35
  s3_resource: Optional["S3ServiceResource"]
37
- bucket: Optional["Bucket"]
38
36
 
39
37
  @classmethod
40
38
  def setUpClass(cls) -> None:
41
39
  super().setUpClass()
42
40
  session = establish_boto3_session(region_name="us-east-1")
43
41
  cls.s3_resource = session.resource("s3", region_name="us-east-1")
44
- cls.bucket = None
45
42
 
46
43
  def test_create_bucket(self) -> None:
47
44
  """Test bucket creation for us-east-1."""
48
45
  bucket_name = f"toil-s3test-{uuid.uuid4()}"
49
46
  assert self.s3_resource
50
- S3Test.bucket = create_s3_bucket(self.s3_resource, bucket_name, "us-east-1")
51
- S3Test.bucket.wait_until_exists()
52
- owner_tag = os.environ.get("TOIL_OWNER_TAG")
53
- if owner_tag:
54
- bucket_tagging = self.s3_resource.BucketTagging(bucket_name)
55
- bucket_tagging.put(
56
- Tagging={"TagSet": [{"Key": "Owner", "Value": owner_tag}]}
57
- )
58
- self.assertEqual(get_bucket_region(bucket_name), "us-east-1")
47
+ bucket: Optional["Bucket"] = None
48
+ try:
49
+ bucket = create_s3_bucket(self.s3_resource, bucket_name, "us-east-1")
50
+ bucket.wait_until_exists()
51
+ owner_tag = os.environ.get("TOIL_OWNER_TAG")
52
+ if owner_tag:
53
+ bucket_tagging = self.s3_resource.BucketTagging(bucket_name)
54
+ bucket_tagging.put(
55
+ Tagging={"TagSet": [{"Key": "Owner", "Value": owner_tag}]}
56
+ )
57
+ self.assertEqual(get_bucket_region(bucket_name), "us-east-1")
59
58
 
60
- # Make sure all the bucket location getting strategies work on a bucket we created
61
- self.assertEqual(
62
- get_bucket_region(bucket_name, only_strategies={1}), "us-east-1"
63
- )
64
- self.assertEqual(
65
- get_bucket_region(bucket_name, only_strategies={2}), "us-east-1"
66
- )
67
- self.assertEqual(
68
- get_bucket_region(bucket_name, only_strategies={3}), "us-east-1"
69
- )
59
+ # Make sure all the bucket location getting strategies work on a bucket we created
60
+ self.assertEqual(
61
+ get_bucket_region(bucket_name, only_strategies={1}), "us-east-1"
62
+ )
63
+ self.assertEqual(
64
+ get_bucket_region(bucket_name, only_strategies={2}), "us-east-1"
65
+ )
66
+ self.assertEqual(
67
+ get_bucket_region(bucket_name, only_strategies={3}), "us-east-1"
68
+ )
69
+ finally:
70
+ # Clean up the bucket if we managed to make it
71
+ if bucket is not None:
72
+ delete_s3_bucket(self.s3_resource, bucket_name)
70
73
 
71
74
  def test_get_bucket_location_public_bucket(self) -> None:
72
75
  """
73
- Test getting buket location for a bucket we don't own.
76
+ Test getting bucket location for a bucket we don't own.
74
77
  """
75
78
 
76
79
  bucket_name = "spacenet-dataset"
77
80
  # This bucket happens to live in us-east-1
78
81
  self.assertEqual(get_bucket_region(bucket_name), "us-east-1")
79
-
80
- @classmethod
81
- def tearDownClass(cls) -> None:
82
- if cls.bucket:
83
- AWSJobStore._delete_bucket(cls.bucket)
84
- super().tearDownClass()
@@ -27,35 +27,35 @@ class TagGenerationTest(ToilTest):
27
27
  Test for tag generation from environment variables
28
28
  """
29
29
 
30
- def test_build_tag(self):
30
+ def test_build_tag(self) -> None:
31
31
  environment = dict()
32
32
  environment["TOIL_OWNER_TAG"] = "😀"
33
- environment["TOIL_AWS_TAGS"] = None
33
+ environment["TOIL_AWS_TAGS"] = ""
34
34
  tag_dict = build_tag_dict_from_env(environment)
35
35
  assert tag_dict == {"Owner": "😀"}
36
36
 
37
- def test_empty_aws_tags(self):
37
+ def test_empty_aws_tags(self) -> None:
38
38
  environment = dict()
39
- environment["TOIL_OWNER_TAG"] = None
39
+ environment["TOIL_OWNER_TAG"] = ""
40
40
  environment["TOIL_AWS_TAGS"] = "{}"
41
41
  tag_dict = build_tag_dict_from_env(environment)
42
42
  assert tag_dict == dict()
43
43
 
44
- def test_incorrect_json_object(self):
44
+ def test_incorrect_json_object(self) -> None:
45
45
  with pytest.raises(SystemExit):
46
46
  environment = dict()
47
- environment["TOIL_OWNER_TAG"] = None
47
+ environment["TOIL_OWNER_TAG"] = ""
48
48
  environment["TOIL_AWS_TAGS"] = "231"
49
49
  tag_dict = build_tag_dict_from_env(environment)
50
50
 
51
- def test_incorrect_json_emoji(self):
51
+ def test_incorrect_json_emoji(self) -> None:
52
52
  with pytest.raises(SystemExit):
53
53
  environment = dict()
54
- environment["TOIL_OWNER_TAG"] = None
54
+ environment["TOIL_OWNER_TAG"] = ""
55
55
  environment["TOIL_AWS_TAGS"] = "😀"
56
56
  tag_dict = build_tag_dict_from_env(environment)
57
57
 
58
- def test_build_tag_with_tags(self):
58
+ def test_build_tag_with_tags(self) -> None:
59
59
  environment = dict()
60
60
  environment["TOIL_OWNER_TAG"] = "😀"
61
61
  environment["TOIL_AWS_TAGS"] = '{"1": "2", " ":")"}'
@@ -11,6 +11,7 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+ import datetime
14
15
  import logging
15
16
  import os
16
17
  import subprocess
@@ -24,13 +25,22 @@ from uuid import uuid4
24
25
 
25
26
  import pytest
26
27
 
28
+ import toil.lib.aws.session
29
+
30
+ from toil.lib.aws import zone_to_region
27
31
  from toil.provisioners import cluster_factory
28
32
  from toil.provisioners.aws.awsProvisioner import AWSProvisioner
33
+ from toil.provisioners.aws import (
34
+ _get_spot_history,
35
+ get_aws_zone_from_spot_market,
36
+ get_best_aws_zone,
37
+ )
29
38
  from toil.test import (
30
39
  ToilTest,
31
40
  get_data,
32
41
  integrative,
33
42
  needs_aws_ec2,
43
+ needs_aws_s3,
34
44
  needs_fetchable_appliance,
35
45
  needs_mesos,
36
46
  slow,
@@ -53,10 +63,35 @@ if TYPE_CHECKING:
53
63
 
54
64
  log = logging.getLogger(__name__)
55
65
 
66
+ @pytest.fixture
67
+ def aws_zone():
68
+ """
69
+ Supply an appropriate AWS zone to work in to tests that need one.
70
+ """
71
+ zone = get_best_aws_zone()
72
+ assert (
73
+ zone is not None
74
+ ), "Could not determine AWS availability zone to test in; is TOIL_AWS_ZONE set?"
75
+ return zone
76
+
77
+ @pytest.fixture
78
+ def aws_region(aws_zone):
79
+ """
80
+ Supply an appropriate AWS region to work in to tests that need one.
81
+ """
82
+ return zone_to_region(aws_zone)
83
+
84
+ @pytest.fixture
85
+ def ec2_client(aws_region):
86
+ """
87
+ Supply an AWS EC2 client tests that need one.
88
+ """
89
+ return toil.lib.aws.session.client("ec2", aws_region)
90
+
56
91
 
57
- class AWSProvisionerBenchTest(ToilTest):
92
+ class TestAWSProvisionerBenchTest:
58
93
  """
59
- Tests for the AWS provisioner that don't actually provision anything.
94
+ Tests for the AWS provisioner that don't actually provision instances.
60
95
  """
61
96
 
62
97
  # Needs to talk to EC2 for image discovery
@@ -71,7 +106,8 @@ class AWSProvisionerBenchTest(ToilTest):
71
106
  assert ami.startswith("ami-")
72
107
 
73
108
  @needs_aws_ec2
74
- def test_read_write_global_files(self):
109
+ @needs_aws_s3
110
+ def test_read_write_global_files(self, aws_zone):
75
111
  """
76
112
  Make sure the `_write_file_to_cloud()` and `_read_file_from_cloud()`
77
113
  functions of the AWS provisioner work as intended.
@@ -79,7 +115,7 @@ class AWSProvisionerBenchTest(ToilTest):
79
115
  provisioner = AWSProvisioner(
80
116
  f"aws-provisioner-test-{uuid4()}",
81
117
  "mesos",
82
- "us-west-2a",
118
+ aws_zone,
83
119
  50,
84
120
  None,
85
121
  None,
@@ -90,13 +126,30 @@ class AWSProvisionerBenchTest(ToilTest):
90
126
 
91
127
  try:
92
128
  url = provisioner._write_file_to_cloud(key, contents=contents)
93
- self.assertTrue(url.startswith("s3://"))
129
+ assert url.startswith("s3://")
94
130
 
95
- self.assertEqual(contents, provisioner._read_file_from_cloud(key))
131
+ assert provisioner._read_file_from_cloud(key) == contents
96
132
  finally:
97
133
  # the cluster was never launched, but we need to clean up the s3 bucket
98
134
  provisioner.destroyCluster()
99
135
 
136
+ @needs_aws_ec2
137
+ def test_get_spot_history(self, ec2_client) -> None:
138
+ """
139
+ Make sure that we can download spot price history from AWS.
140
+ """
141
+ history = _get_spot_history(ec2_client, "t3.large")
142
+ # We should have 7 days of history, newest first.
143
+
144
+ @needs_aws_ec2
145
+ def test_get_aws_zone_from_spot_market(self, ec2_client) -> None:
146
+ """
147
+ Make sure that we can process spot price history to pick a zone.
148
+ """
149
+ zone_options = ["us-west-2a", "af-south-1c"]
150
+ zone_choice = get_aws_zone_from_spot_market(0.01, "t3.large", ec2_client, zone_options)
151
+ assert zone_choice in zone_options
152
+
100
153
 
101
154
  @needs_aws_ec2
102
155
  @needs_fetchable_appliance
@@ -4,7 +4,6 @@ import time
4
4
  from contextlib import contextmanager
5
5
 
6
6
  from toil.exceptions import FailedJobsException
7
- from toil.lib.iterables import concat
8
7
  from toil.test import ApplianceTestSupport, needs_local_appliance, needs_mesos, slow
9
8
  from toil.version import exactPython
10
9
 
@@ -85,7 +84,7 @@ class AutoDeploymentTest(ApplianceTestSupport):
85
84
  "--defaultMemory=10M",
86
85
  "/data/jobstore",
87
86
  ]
88
- command = concat(pythonArgs, toilArgs)
87
+ command = pythonArgs + toilArgs
89
88
  self.assertRaises(
90
89
  subprocess.CalledProcessError, leader.runOnAppliance, *command
91
90
  )
@@ -96,7 +95,7 @@ class AutoDeploymentTest(ApplianceTestSupport):
96
95
  path=self.sitePackages, packagePath="foo.bar", script=userScript
97
96
  )
98
97
  # ... and restart Toil.
99
- command = concat(pythonArgs, "--restart", toilArgs)
98
+ command = pythonArgs + ["--restart"] + toilArgs
100
99
  leader.runOnAppliance(*command)
101
100
 
102
101
  def testSplitRootPackages(self):