toil 7.0.0__py3-none-any.whl → 8.0.0__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 (190) hide show
  1. toil/__init__.py +121 -83
  2. toil/batchSystems/__init__.py +1 -0
  3. toil/batchSystems/abstractBatchSystem.py +137 -77
  4. toil/batchSystems/abstractGridEngineBatchSystem.py +211 -101
  5. toil/batchSystems/awsBatch.py +237 -128
  6. toil/batchSystems/cleanup_support.py +22 -16
  7. toil/batchSystems/contained_executor.py +30 -26
  8. toil/batchSystems/gridengine.py +85 -49
  9. toil/batchSystems/htcondor.py +164 -87
  10. toil/batchSystems/kubernetes.py +622 -386
  11. toil/batchSystems/local_support.py +17 -12
  12. toil/batchSystems/lsf.py +132 -79
  13. toil/batchSystems/lsfHelper.py +13 -11
  14. toil/batchSystems/mesos/__init__.py +41 -29
  15. toil/batchSystems/mesos/batchSystem.py +288 -149
  16. toil/batchSystems/mesos/executor.py +77 -49
  17. toil/batchSystems/mesos/test/__init__.py +31 -23
  18. toil/batchSystems/options.py +38 -29
  19. toil/batchSystems/registry.py +53 -19
  20. toil/batchSystems/singleMachine.py +293 -123
  21. toil/batchSystems/slurm.py +489 -137
  22. toil/batchSystems/torque.py +46 -32
  23. toil/bus.py +141 -73
  24. toil/common.py +630 -359
  25. toil/cwl/__init__.py +1 -1
  26. toil/cwl/cwltoil.py +1114 -532
  27. toil/cwl/utils.py +17 -22
  28. toil/deferred.py +62 -41
  29. toil/exceptions.py +5 -3
  30. toil/fileStores/__init__.py +5 -5
  31. toil/fileStores/abstractFileStore.py +88 -57
  32. toil/fileStores/cachingFileStore.py +711 -247
  33. toil/fileStores/nonCachingFileStore.py +113 -75
  34. toil/job.py +988 -315
  35. toil/jobStores/abstractJobStore.py +387 -243
  36. toil/jobStores/aws/jobStore.py +727 -403
  37. toil/jobStores/aws/utils.py +161 -109
  38. toil/jobStores/conftest.py +1 -0
  39. toil/jobStores/fileJobStore.py +289 -151
  40. toil/jobStores/googleJobStore.py +137 -70
  41. toil/jobStores/utils.py +36 -15
  42. toil/leader.py +614 -269
  43. toil/lib/accelerators.py +115 -18
  44. toil/lib/aws/__init__.py +55 -28
  45. toil/lib/aws/ami.py +122 -87
  46. toil/lib/aws/iam.py +284 -108
  47. toil/lib/aws/s3.py +31 -0
  48. toil/lib/aws/session.py +193 -58
  49. toil/lib/aws/utils.py +238 -218
  50. toil/lib/bioio.py +13 -5
  51. toil/lib/compatibility.py +11 -6
  52. toil/lib/conversions.py +83 -49
  53. toil/lib/docker.py +131 -103
  54. toil/lib/ec2.py +322 -209
  55. toil/lib/ec2nodes.py +174 -106
  56. toil/lib/encryption/_dummy.py +5 -3
  57. toil/lib/encryption/_nacl.py +10 -6
  58. toil/lib/encryption/conftest.py +1 -0
  59. toil/lib/exceptions.py +26 -7
  60. toil/lib/expando.py +4 -2
  61. toil/lib/ftp_utils.py +217 -0
  62. toil/lib/generatedEC2Lists.py +127 -19
  63. toil/lib/humanize.py +6 -2
  64. toil/lib/integration.py +341 -0
  65. toil/lib/io.py +99 -11
  66. toil/lib/iterables.py +4 -2
  67. toil/lib/memoize.py +12 -8
  68. toil/lib/misc.py +65 -18
  69. toil/lib/objects.py +2 -2
  70. toil/lib/resources.py +19 -7
  71. toil/lib/retry.py +115 -77
  72. toil/lib/threading.py +282 -80
  73. toil/lib/throttle.py +15 -14
  74. toil/options/common.py +834 -401
  75. toil/options/cwl.py +175 -90
  76. toil/options/runner.py +50 -0
  77. toil/options/wdl.py +70 -19
  78. toil/provisioners/__init__.py +111 -46
  79. toil/provisioners/abstractProvisioner.py +322 -157
  80. toil/provisioners/aws/__init__.py +62 -30
  81. toil/provisioners/aws/awsProvisioner.py +980 -627
  82. toil/provisioners/clusterScaler.py +541 -279
  83. toil/provisioners/gceProvisioner.py +282 -179
  84. toil/provisioners/node.py +147 -79
  85. toil/realtimeLogger.py +34 -22
  86. toil/resource.py +137 -75
  87. toil/server/app.py +127 -61
  88. toil/server/celery_app.py +3 -1
  89. toil/server/cli/wes_cwl_runner.py +82 -53
  90. toil/server/utils.py +54 -28
  91. toil/server/wes/abstract_backend.py +64 -26
  92. toil/server/wes/amazon_wes_utils.py +21 -15
  93. toil/server/wes/tasks.py +121 -63
  94. toil/server/wes/toil_backend.py +142 -107
  95. toil/server/wsgi_app.py +4 -3
  96. toil/serviceManager.py +58 -22
  97. toil/statsAndLogging.py +148 -64
  98. toil/test/__init__.py +263 -179
  99. toil/test/batchSystems/batchSystemTest.py +438 -195
  100. toil/test/batchSystems/batch_system_plugin_test.py +18 -7
  101. toil/test/batchSystems/test_gridengine.py +173 -0
  102. toil/test/batchSystems/test_lsf_helper.py +67 -58
  103. toil/test/batchSystems/test_slurm.py +93 -47
  104. toil/test/cactus/test_cactus_integration.py +20 -22
  105. toil/test/cwl/cwlTest.py +271 -71
  106. toil/test/cwl/measure_default_memory.cwl +12 -0
  107. toil/test/cwl/not_run_required_input.cwl +29 -0
  108. toil/test/cwl/scatter_duplicate_outputs.cwl +40 -0
  109. toil/test/docs/scriptsTest.py +60 -34
  110. toil/test/jobStores/jobStoreTest.py +412 -235
  111. toil/test/lib/aws/test_iam.py +116 -48
  112. toil/test/lib/aws/test_s3.py +16 -9
  113. toil/test/lib/aws/test_utils.py +5 -6
  114. toil/test/lib/dockerTest.py +118 -141
  115. toil/test/lib/test_conversions.py +113 -115
  116. toil/test/lib/test_ec2.py +57 -49
  117. toil/test/lib/test_integration.py +104 -0
  118. toil/test/lib/test_misc.py +12 -5
  119. toil/test/mesos/MesosDataStructuresTest.py +23 -10
  120. toil/test/mesos/helloWorld.py +7 -6
  121. toil/test/mesos/stress.py +25 -20
  122. toil/test/options/options.py +7 -2
  123. toil/test/provisioners/aws/awsProvisionerTest.py +293 -140
  124. toil/test/provisioners/clusterScalerTest.py +440 -250
  125. toil/test/provisioners/clusterTest.py +81 -42
  126. toil/test/provisioners/gceProvisionerTest.py +174 -100
  127. toil/test/provisioners/provisionerTest.py +25 -13
  128. toil/test/provisioners/restartScript.py +5 -4
  129. toil/test/server/serverTest.py +188 -141
  130. toil/test/sort/restart_sort.py +137 -68
  131. toil/test/sort/sort.py +134 -66
  132. toil/test/sort/sortTest.py +91 -49
  133. toil/test/src/autoDeploymentTest.py +140 -100
  134. toil/test/src/busTest.py +20 -18
  135. toil/test/src/checkpointTest.py +8 -2
  136. toil/test/src/deferredFunctionTest.py +49 -35
  137. toil/test/src/dockerCheckTest.py +33 -26
  138. toil/test/src/environmentTest.py +20 -10
  139. toil/test/src/fileStoreTest.py +538 -271
  140. toil/test/src/helloWorldTest.py +7 -4
  141. toil/test/src/importExportFileTest.py +61 -31
  142. toil/test/src/jobDescriptionTest.py +32 -17
  143. toil/test/src/jobEncapsulationTest.py +2 -0
  144. toil/test/src/jobFileStoreTest.py +74 -50
  145. toil/test/src/jobServiceTest.py +187 -73
  146. toil/test/src/jobTest.py +120 -70
  147. toil/test/src/miscTests.py +19 -18
  148. toil/test/src/promisedRequirementTest.py +82 -36
  149. toil/test/src/promisesTest.py +7 -6
  150. toil/test/src/realtimeLoggerTest.py +6 -6
  151. toil/test/src/regularLogTest.py +71 -37
  152. toil/test/src/resourceTest.py +80 -49
  153. toil/test/src/restartDAGTest.py +36 -22
  154. toil/test/src/resumabilityTest.py +9 -2
  155. toil/test/src/retainTempDirTest.py +45 -14
  156. toil/test/src/systemTest.py +12 -8
  157. toil/test/src/threadingTest.py +44 -25
  158. toil/test/src/toilContextManagerTest.py +10 -7
  159. toil/test/src/userDefinedJobArgTypeTest.py +8 -5
  160. toil/test/src/workerTest.py +33 -16
  161. toil/test/utils/toilDebugTest.py +70 -58
  162. toil/test/utils/toilKillTest.py +4 -5
  163. toil/test/utils/utilsTest.py +239 -102
  164. toil/test/wdl/wdltoil_test.py +789 -148
  165. toil/test/wdl/wdltoil_test_kubernetes.py +37 -23
  166. toil/toilState.py +52 -26
  167. toil/utils/toilConfig.py +13 -4
  168. toil/utils/toilDebugFile.py +44 -27
  169. toil/utils/toilDebugJob.py +85 -25
  170. toil/utils/toilDestroyCluster.py +11 -6
  171. toil/utils/toilKill.py +8 -3
  172. toil/utils/toilLaunchCluster.py +251 -145
  173. toil/utils/toilMain.py +37 -16
  174. toil/utils/toilRsyncCluster.py +27 -14
  175. toil/utils/toilSshCluster.py +45 -22
  176. toil/utils/toilStats.py +75 -36
  177. toil/utils/toilStatus.py +226 -119
  178. toil/utils/toilUpdateEC2Instances.py +3 -1
  179. toil/version.py +11 -11
  180. toil/wdl/utils.py +5 -5
  181. toil/wdl/wdltoil.py +3513 -1052
  182. toil/worker.py +269 -128
  183. toil-8.0.0.dist-info/METADATA +173 -0
  184. toil-8.0.0.dist-info/RECORD +253 -0
  185. {toil-7.0.0.dist-info → toil-8.0.0.dist-info}/WHEEL +1 -1
  186. toil-7.0.0.dist-info/METADATA +0 -158
  187. toil-7.0.0.dist-info/RECORD +0 -244
  188. {toil-7.0.0.dist-info → toil-8.0.0.dist-info}/LICENSE +0 -0
  189. {toil-7.0.0.dist-info → toil-8.0.0.dist-info}/entry_points.txt +0 -0
  190. {toil-7.0.0.dist-info → toil-8.0.0.dist-info}/top_level.txt +0 -0
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
  import json
15
15
  import logging
16
+ from uuid import uuid4
16
17
 
17
18
  import boto3
18
19
  from moto import mock_aws
@@ -28,23 +29,45 @@ class IAMTest(ToilTest):
28
29
  """Check that given permissions and associated functions perform correctly"""
29
30
 
30
31
  def test_permissions_iam(self):
31
- granted_perms = {'*': {'Action': ['ec2:*', 'iam:*', 's3:*', 'sdb:*'], 'NotAction': []}}
32
- assert iam.policy_permissions_allow(granted_perms, iam.CLUSTER_LAUNCHING_PERMISSIONS) is True
33
- granted_perms = {'*': {'Action': [], 'NotAction': ['s3:*']}}
34
- assert iam.policy_permissions_allow(granted_perms, iam.CLUSTER_LAUNCHING_PERMISSIONS) is True
32
+ granted_perms = {
33
+ "*": {"Action": ["ec2:*", "iam:*", "s3:*", "sdb:*"], "NotAction": []}
34
+ }
35
+ assert (
36
+ iam.policy_permissions_allow(
37
+ granted_perms, iam.CLUSTER_LAUNCHING_PERMISSIONS
38
+ )
39
+ is True
40
+ )
41
+ granted_perms = {"*": {"Action": [], "NotAction": ["s3:*"]}}
42
+ assert (
43
+ iam.policy_permissions_allow(
44
+ granted_perms, iam.CLUSTER_LAUNCHING_PERMISSIONS
45
+ )
46
+ is True
47
+ )
35
48
 
36
49
  def test_negative_permissions_iam(self):
37
- granted_perms = {'*': {'Action': ['ec2:*', 's3:*', 'sdb:*'], 'NotAction': []}}
38
- assert iam.policy_permissions_allow(granted_perms, iam.CLUSTER_LAUNCHING_PERMISSIONS) is False
39
- granted_perms = {'*': {'Action': [], 'NotAction': ['iam:*', 'ec2:*']}}
40
- assert iam.policy_permissions_allow(granted_perms, iam.CLUSTER_LAUNCHING_PERMISSIONS) is False
50
+ granted_perms = {"*": {"Action": ["ec2:*", "s3:*", "sdb:*"], "NotAction": []}}
51
+ assert (
52
+ iam.policy_permissions_allow(
53
+ granted_perms, iam.CLUSTER_LAUNCHING_PERMISSIONS
54
+ )
55
+ is False
56
+ )
57
+ granted_perms = {"*": {"Action": [], "NotAction": ["iam:*", "ec2:*"]}}
58
+ assert (
59
+ iam.policy_permissions_allow(
60
+ granted_perms, iam.CLUSTER_LAUNCHING_PERMISSIONS
61
+ )
62
+ is False
63
+ )
41
64
 
42
65
  def test_wildcard_handling(self):
43
- assert iam.permission_matches_any("iam:CreateRole", ['iam:Create**']) is True
44
- assert iam.permission_matches_any("iam:GetUser", ['iam:???????']) is True
45
- assert iam.permission_matches_any("iam:ListRoleTags", ['iam:*?*Tags']) is True
66
+ assert iam.permission_matches_any("iam:CreateRole", ["iam:Create**"]) is True
67
+ assert iam.permission_matches_any("iam:GetUser", ["iam:???????"]) is True
68
+ assert iam.permission_matches_any("iam:ListRoleTags", ["iam:*?*Tags"]) is True
46
69
  assert iam.permission_matches_any("iam:*", ["*"]) is True
47
- assert iam.permission_matches_any("ec2:*", ['iam:*']) is False
70
+ assert iam.permission_matches_any("ec2:*", ["iam:*"]) is False
48
71
 
49
72
  @mock_aws
50
73
  def test_get_policy_permissions(self):
@@ -55,68 +78,77 @@ class IAMTest(ToilTest):
55
78
  mock_iam.create_user(UserName=user_name)
56
79
 
57
80
  group_name = "default_group"
58
- mock_iam.create_group(
59
- GroupName=group_name
60
- )
81
+ mock_iam.create_group(GroupName=group_name)
61
82
 
62
- mock_iam.add_user_to_group(
63
- GroupName=group_name,
64
- UserName=user_name
65
- )
83
+ mock_iam.add_user_to_group(GroupName=group_name, UserName=user_name)
66
84
 
67
85
  policy_response = mock_iam.create_policy(
68
86
  PolicyName="test_iam_createrole",
69
- PolicyDocument=json.dumps({
70
- "Version": "2012-10-17", # represents version language
71
- "Statement": [
72
- {"Effect": "Allow", "Action": "iam:CreateRole", "Resource": "*"}
73
- ]
74
- })
87
+ PolicyDocument=json.dumps(
88
+ {
89
+ "Version": "2012-10-17", # represents version language
90
+ "Statement": [
91
+ {"Effect": "Allow", "Action": "iam:CreateRole", "Resource": "*"}
92
+ ],
93
+ }
94
+ ),
75
95
  )
76
96
  # attached user policy
77
97
  mock_iam.attach_user_policy(
78
- UserName=user_name,
79
- PolicyArn=policy_response["Policy"]["Arn"]
98
+ UserName=user_name, PolicyArn=policy_response["Policy"]["Arn"]
80
99
  )
81
100
 
82
101
  # inline user policy
83
102
  mock_iam.put_user_policy(
84
103
  UserName=user_name,
85
104
  PolicyName="test_iam_createinstanceprofile",
86
- PolicyDocument=json.dumps({
87
- "Version": "2012-10-17", # represents version language
88
- "Statement": [
89
- {"Effect": "Allow", "Action": "iam:CreateInstanceProfile", "Resource": "*"}
90
- ]
91
- })
105
+ PolicyDocument=json.dumps(
106
+ {
107
+ "Version": "2012-10-17", # represents version language
108
+ "Statement": [
109
+ {
110
+ "Effect": "Allow",
111
+ "Action": "iam:CreateInstanceProfile",
112
+ "Resource": "*",
113
+ }
114
+ ],
115
+ }
116
+ ),
92
117
  )
93
118
 
94
119
  # group policies
95
120
  policy_response = mock_iam.create_policy(
96
121
  PolicyName="test_iam_taginstanceprofile",
97
- PolicyDocument=json.dumps({
98
- "Version": "2012-10-17", # represents version language
99
- "Statement": [
100
- {"Effect": "Allow", "Action": "iam:TagInstanceProfile", "Resource": "*"}
101
- ]
102
- })
122
+ PolicyDocument=json.dumps(
123
+ {
124
+ "Version": "2012-10-17", # represents version language
125
+ "Statement": [
126
+ {
127
+ "Effect": "Allow",
128
+ "Action": "iam:TagInstanceProfile",
129
+ "Resource": "*",
130
+ }
131
+ ],
132
+ }
133
+ ),
103
134
  )
104
135
  # attached group policy
105
136
  mock_iam.attach_group_policy(
106
- GroupName=group_name,
107
- PolicyArn=policy_response["Policy"]["Arn"]
137
+ GroupName=group_name, PolicyArn=policy_response["Policy"]["Arn"]
108
138
  )
109
139
 
110
140
  # inline group policy
111
141
  mock_iam.put_group_policy(
112
142
  GroupName=group_name,
113
143
  PolicyName="test_iam_deleterole",
114
- PolicyDocument=json.dumps({
115
- "Version": "2012-10-17", # represents version language
116
- "Statement": [
117
- {"Effect": "Allow", "Action": "iam:DeleteRole", "Resource": "*"}
118
- ]
119
- })
144
+ PolicyDocument=json.dumps(
145
+ {
146
+ "Version": "2012-10-17", # represents version language
147
+ "Statement": [
148
+ {"Effect": "Allow", "Action": "iam:DeleteRole", "Resource": "*"}
149
+ ],
150
+ }
151
+ ),
120
152
  )
121
153
 
122
154
  actions_collection = iam.get_policy_permissions("us-west-2")
@@ -124,6 +156,42 @@ class IAMTest(ToilTest):
124
156
  actions_set = set(actions_collection["*"]["Action"])
125
157
  notactions_set = set(actions_collection["*"]["NotAction"])
126
158
 
127
- expected_actions = {"iam:CreateRole", "iam:CreateInstanceProfile", "iam:TagInstanceProfile", "iam:DeleteRole"}
159
+ expected_actions = {
160
+ "iam:CreateRole",
161
+ "iam:CreateInstanceProfile",
162
+ "iam:TagInstanceProfile",
163
+ "iam:DeleteRole",
164
+ }
128
165
  assert actions_set == expected_actions
129
166
  assert notactions_set == set()
167
+
168
+ def test_create_delete_iam_role(self):
169
+ region = "us-west-2"
170
+ role_name = f'test{str(uuid4()).replace("-", "")}'
171
+ with self.subTest("Create role w/policies."):
172
+ ec2_role_policy_document = json.dumps(
173
+ {
174
+ "Version": "2012-10-17",
175
+ "Statement": [
176
+ {
177
+ "Effect": "Allow",
178
+ "Principal": {"Service": ["ec2.amazonaws.com"]},
179
+ "Action": ["sts:AssumeRole"],
180
+ }
181
+ ],
182
+ }
183
+ )
184
+ policy = dict(
185
+ s3_deny=dict(
186
+ Version="2012-10-17",
187
+ Statement=[dict(Effect="Deny", Resource="*", Action="s3:*")],
188
+ )
189
+ )
190
+ iam.create_iam_role(
191
+ role_name=role_name,
192
+ assume_role_policy_document=ec2_role_policy_document,
193
+ policies=policy,
194
+ region=region,
195
+ )
196
+ with self.subTest("Delete role w/policies."):
197
+ iam.delete_iam_role(role_name=role_name, region=region)
@@ -14,7 +14,7 @@
14
14
  import logging
15
15
  import os
16
16
  import uuid
17
- from typing import Optional
17
+ 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
@@ -29,11 +29,12 @@ logging.basicConfig(level=logging.DEBUG)
29
29
  class S3Test(ToilTest):
30
30
  """Confirm the workarounds for us-east-1."""
31
31
 
32
- from mypy_boto3_s3 import S3ServiceResource
33
- from mypy_boto3_s3.service_resource import Bucket
32
+ if TYPE_CHECKING:
33
+ from mypy_boto3_s3 import S3ServiceResource
34
+ from mypy_boto3_s3.service_resource import Bucket
34
35
 
35
- s3_resource: Optional[S3ServiceResource]
36
- bucket: Optional[Bucket]
36
+ s3_resource: Optional["S3ServiceResource"]
37
+ bucket: Optional["Bucket"]
37
38
 
38
39
  @classmethod
39
40
  def setUpClass(cls) -> None:
@@ -57,16 +58,22 @@ class S3Test(ToilTest):
57
58
  self.assertEqual(get_bucket_region(bucket_name), "us-east-1")
58
59
 
59
60
  # Make sure all the bucket location getting strategies work on a bucket we created
60
- self.assertEqual(get_bucket_region(bucket_name, only_strategies = {1}), "us-east-1")
61
- self.assertEqual(get_bucket_region(bucket_name, only_strategies = {2}), "us-east-1")
62
- self.assertEqual(get_bucket_region(bucket_name, only_strategies = {3}), "us-east-1")
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
+ )
63
70
 
64
71
  def test_get_bucket_location_public_bucket(self) -> None:
65
72
  """
66
73
  Test getting buket location for a bucket we don't own.
67
74
  """
68
75
 
69
- bucket_name = 'spacenet-dataset'
76
+ bucket_name = "spacenet-dataset"
70
77
  # This bucket happens to live in us-east-1
71
78
  self.assertEqual(get_bucket_region(bucket_name), "us-east-1")
72
79
 
@@ -21,23 +21,25 @@ from toil.test import ToilTest
21
21
  logger = logging.getLogger(__name__)
22
22
  logging.basicConfig(level=logging.DEBUG)
23
23
 
24
+
24
25
  class TagGenerationTest(ToilTest):
25
26
  """
26
27
  Test for tag generation from environment variables
27
28
  """
29
+
28
30
  def test_build_tag(self):
29
31
  environment = dict()
30
32
  environment["TOIL_OWNER_TAG"] = "😀"
31
33
  environment["TOIL_AWS_TAGS"] = None
32
34
  tag_dict = build_tag_dict_from_env(environment)
33
- assert(tag_dict == {'Owner': '😀'})
35
+ assert tag_dict == {"Owner": "😀"}
34
36
 
35
37
  def test_empty_aws_tags(self):
36
38
  environment = dict()
37
39
  environment["TOIL_OWNER_TAG"] = None
38
40
  environment["TOIL_AWS_TAGS"] = "{}"
39
41
  tag_dict = build_tag_dict_from_env(environment)
40
- assert (tag_dict == dict())
42
+ assert tag_dict == dict()
41
43
 
42
44
  def test_incorrect_json_object(self):
43
45
  with pytest.raises(SystemExit):
@@ -58,7 +60,4 @@ class TagGenerationTest(ToilTest):
58
60
  environment["TOIL_OWNER_TAG"] = "😀"
59
61
  environment["TOIL_AWS_TAGS"] = '{"1": "2", " ":")"}'
60
62
  tag_dict = build_tag_dict_from_env(environment)
61
- assert(tag_dict == {'Owner': '😀', '1': '2', ' ': ')'})
62
-
63
-
64
-
63
+ assert tag_dict == {"Owner": "😀", "1": "2", " ": ")"}