toil 9.1.2__py3-none-any.whl → 9.2.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 (155) hide show
  1. toil/__init__.py +5 -9
  2. toil/batchSystems/abstractBatchSystem.py +23 -22
  3. toil/batchSystems/abstractGridEngineBatchSystem.py +17 -12
  4. toil/batchSystems/awsBatch.py +8 -8
  5. toil/batchSystems/cleanup_support.py +4 -4
  6. toil/batchSystems/contained_executor.py +3 -3
  7. toil/batchSystems/gridengine.py +3 -4
  8. toil/batchSystems/htcondor.py +5 -5
  9. toil/batchSystems/kubernetes.py +65 -63
  10. toil/batchSystems/local_support.py +2 -3
  11. toil/batchSystems/lsf.py +6 -7
  12. toil/batchSystems/mesos/batchSystem.py +11 -7
  13. toil/batchSystems/mesos/test/__init__.py +1 -2
  14. toil/batchSystems/options.py +9 -10
  15. toil/batchSystems/registry.py +3 -7
  16. toil/batchSystems/singleMachine.py +8 -11
  17. toil/batchSystems/slurm.py +49 -38
  18. toil/batchSystems/torque.py +3 -4
  19. toil/bus.py +36 -34
  20. toil/common.py +129 -89
  21. toil/cwl/cwltoil.py +857 -729
  22. toil/cwl/utils.py +44 -35
  23. toil/fileStores/__init__.py +3 -1
  24. toil/fileStores/abstractFileStore.py +28 -30
  25. toil/fileStores/cachingFileStore.py +8 -8
  26. toil/fileStores/nonCachingFileStore.py +10 -21
  27. toil/job.py +159 -158
  28. toil/jobStores/abstractJobStore.py +68 -69
  29. toil/jobStores/aws/jobStore.py +249 -213
  30. toil/jobStores/aws/utils.py +13 -24
  31. toil/jobStores/fileJobStore.py +28 -22
  32. toil/jobStores/googleJobStore.py +21 -17
  33. toil/jobStores/utils.py +3 -7
  34. toil/leader.py +14 -14
  35. toil/lib/accelerators.py +6 -4
  36. toil/lib/aws/__init__.py +9 -10
  37. toil/lib/aws/ami.py +33 -19
  38. toil/lib/aws/iam.py +6 -6
  39. toil/lib/aws/s3.py +259 -157
  40. toil/lib/aws/session.py +76 -76
  41. toil/lib/aws/utils.py +51 -43
  42. toil/lib/checksum.py +19 -15
  43. toil/lib/compatibility.py +3 -2
  44. toil/lib/conversions.py +45 -18
  45. toil/lib/directory.py +29 -26
  46. toil/lib/docker.py +93 -99
  47. toil/lib/dockstore.py +77 -50
  48. toil/lib/ec2.py +39 -38
  49. toil/lib/ec2nodes.py +11 -4
  50. toil/lib/exceptions.py +8 -5
  51. toil/lib/ftp_utils.py +9 -14
  52. toil/lib/generatedEC2Lists.py +161 -20
  53. toil/lib/history.py +141 -97
  54. toil/lib/history_submission.py +163 -72
  55. toil/lib/io.py +27 -17
  56. toil/lib/memoize.py +2 -1
  57. toil/lib/misc.py +15 -11
  58. toil/lib/pipes.py +40 -25
  59. toil/lib/plugins.py +12 -8
  60. toil/lib/resources.py +1 -0
  61. toil/lib/retry.py +32 -38
  62. toil/lib/threading.py +12 -12
  63. toil/lib/throttle.py +1 -2
  64. toil/lib/trs.py +113 -51
  65. toil/lib/url.py +14 -23
  66. toil/lib/web.py +7 -2
  67. toil/options/common.py +18 -15
  68. toil/options/cwl.py +2 -2
  69. toil/options/runner.py +9 -5
  70. toil/options/wdl.py +1 -3
  71. toil/provisioners/__init__.py +9 -9
  72. toil/provisioners/abstractProvisioner.py +22 -20
  73. toil/provisioners/aws/__init__.py +20 -14
  74. toil/provisioners/aws/awsProvisioner.py +10 -8
  75. toil/provisioners/clusterScaler.py +19 -18
  76. toil/provisioners/gceProvisioner.py +2 -3
  77. toil/provisioners/node.py +11 -13
  78. toil/realtimeLogger.py +4 -4
  79. toil/resource.py +5 -5
  80. toil/server/app.py +2 -2
  81. toil/server/cli/wes_cwl_runner.py +11 -11
  82. toil/server/utils.py +18 -21
  83. toil/server/wes/abstract_backend.py +9 -8
  84. toil/server/wes/amazon_wes_utils.py +3 -3
  85. toil/server/wes/tasks.py +3 -5
  86. toil/server/wes/toil_backend.py +17 -21
  87. toil/server/wsgi_app.py +3 -3
  88. toil/serviceManager.py +3 -4
  89. toil/statsAndLogging.py +12 -13
  90. toil/test/__init__.py +33 -24
  91. toil/test/batchSystems/batchSystemTest.py +12 -11
  92. toil/test/batchSystems/batch_system_plugin_test.py +3 -5
  93. toil/test/batchSystems/test_slurm.py +38 -24
  94. toil/test/cwl/conftest.py +5 -6
  95. toil/test/cwl/cwlTest.py +194 -78
  96. toil/test/cwl/download_file_uri.json +6 -0
  97. toil/test/cwl/download_file_uri_no_hostname.json +6 -0
  98. toil/test/docs/scripts/tutorial_staging.py +1 -0
  99. toil/test/jobStores/jobStoreTest.py +9 -7
  100. toil/test/lib/aws/test_iam.py +1 -3
  101. toil/test/lib/aws/test_s3.py +1 -1
  102. toil/test/lib/dockerTest.py +9 -9
  103. toil/test/lib/test_ec2.py +12 -11
  104. toil/test/lib/test_history.py +4 -4
  105. toil/test/lib/test_trs.py +16 -14
  106. toil/test/lib/test_url.py +7 -6
  107. toil/test/lib/url_plugin_test.py +12 -18
  108. toil/test/provisioners/aws/awsProvisionerTest.py +10 -8
  109. toil/test/provisioners/clusterScalerTest.py +2 -5
  110. toil/test/provisioners/clusterTest.py +1 -3
  111. toil/test/server/serverTest.py +13 -4
  112. toil/test/sort/restart_sort.py +2 -6
  113. toil/test/sort/sort.py +3 -8
  114. toil/test/src/deferredFunctionTest.py +7 -7
  115. toil/test/src/environmentTest.py +1 -2
  116. toil/test/src/fileStoreTest.py +5 -5
  117. toil/test/src/importExportFileTest.py +5 -6
  118. toil/test/src/jobServiceTest.py +22 -14
  119. toil/test/src/jobTest.py +121 -25
  120. toil/test/src/miscTests.py +5 -7
  121. toil/test/src/promisedRequirementTest.py +8 -7
  122. toil/test/src/regularLogTest.py +2 -3
  123. toil/test/src/resourceTest.py +5 -8
  124. toil/test/src/restartDAGTest.py +5 -6
  125. toil/test/src/resumabilityTest.py +2 -2
  126. toil/test/src/retainTempDirTest.py +3 -3
  127. toil/test/src/systemTest.py +3 -3
  128. toil/test/src/threadingTest.py +1 -1
  129. toil/test/src/workerTest.py +1 -2
  130. toil/test/utils/toilDebugTest.py +6 -4
  131. toil/test/utils/toilKillTest.py +1 -1
  132. toil/test/utils/utilsTest.py +15 -14
  133. toil/test/wdl/wdltoil_test.py +247 -124
  134. toil/test/wdl/wdltoil_test_kubernetes.py +2 -2
  135. toil/toilState.py +2 -3
  136. toil/utils/toilDebugFile.py +3 -8
  137. toil/utils/toilDebugJob.py +1 -2
  138. toil/utils/toilLaunchCluster.py +1 -2
  139. toil/utils/toilSshCluster.py +2 -0
  140. toil/utils/toilStats.py +19 -24
  141. toil/utils/toilStatus.py +11 -14
  142. toil/version.py +10 -10
  143. toil/wdl/wdltoil.py +313 -209
  144. toil/worker.py +18 -12
  145. {toil-9.1.2.dist-info → toil-9.2.0.dist-info}/METADATA +11 -14
  146. {toil-9.1.2.dist-info → toil-9.2.0.dist-info}/RECORD +150 -153
  147. {toil-9.1.2.dist-info → toil-9.2.0.dist-info}/WHEEL +1 -1
  148. toil/test/cwl/staging_cat.cwl +0 -27
  149. toil/test/cwl/staging_make_file.cwl +0 -25
  150. toil/test/cwl/staging_workflow.cwl +0 -43
  151. toil/test/cwl/zero_default.cwl +0 -61
  152. toil/test/utils/ABCWorkflowDebug/ABC.txt +0 -1
  153. {toil-9.1.2.dist-info → toil-9.2.0.dist-info}/entry_points.txt +0 -0
  154. {toil-9.1.2.dist-info → toil-9.2.0.dist-info}/licenses/LICENSE +0 -0
  155. {toil-9.1.2.dist-info → toil-9.2.0.dist-info}/top_level.txt +0 -0
@@ -12,33 +12,33 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  import builtins
15
- from collections.abc import Callable, Generator
16
15
  import logging
17
16
  import os
18
- from pathlib import Path
19
17
  import re
20
18
  import subprocess
21
19
  import sys
22
20
  import time
23
21
  import uuid
24
- from typing import Optional, Any, cast
22
+ from collections.abc import Callable, Generator
23
+ from pathlib import Path
24
+ from typing import Any, cast
25
+
25
26
  import pytest
26
27
 
27
28
  import toil
28
29
  from toil import resolveEntryPoint
29
30
  from toil.common import Config, Toil
31
+ from toil.fileStores.abstractFileStore import AbstractFileStore
30
32
  from toil.job import Job
31
33
  from toil.lib.bioio import system
32
- from toil.fileStores.abstractFileStore import AbstractFileStore
33
- from toil.test import (
34
- get_data,
35
- pneeds_aws_ec2 as needs_aws_ec2,
36
- pneeds_cwl as needs_cwl,
37
- pneeds_docker as needs_docker,
38
- pintegrative as integrative,
39
- pneeds_rsync3 as needs_rsync3,
40
- pslow as slow,
41
- )
34
+ from toil.test import get_data
35
+ from toil.test import pintegrative as integrative
36
+ from toil.test import pneeds_aws_ec2 as needs_aws_ec2
37
+ from toil.test import pneeds_cwl as needs_cwl
38
+ from toil.test import pneeds_docker as needs_docker
39
+ from toil.test import pneeds_rsync3 as needs_rsync3
40
+ from toil.test import pslow as slow
41
+ import toil.test.sort.sort
42
42
  from toil.test.sort.sort import makeFileToSort
43
43
  from toil.utils.toilStats import get_stats, process_data
44
44
  from toil.utils.toilStatus import ToilStatus
@@ -353,7 +353,7 @@ class TestUtils:
353
353
  jobstore: Path,
354
354
  status: str,
355
355
  status_fn: Callable[[str], str],
356
- process: Optional[subprocess.Popen[Any]] = None,
356
+ process: subprocess.Popen[Any] | None = None,
357
357
  seconds: int = 20,
358
358
  ) -> None:
359
359
  time_elapsed = 0.0
@@ -542,6 +542,7 @@ class TestUtils:
542
542
 
543
543
  def fake_print(*args: Any, **kwargs: Any) -> None:
544
544
  print_args.extend(args)
545
+
545
546
  # Run a workflow that will always fail
546
547
  with get_data("test/cwl/alwaysfails.cwl") as cwl_file:
547
548
  cmd = [
@@ -2,75 +2,105 @@ import json
2
2
  import logging
3
3
  import os
4
4
  import re
5
- import shutil
6
5
  import string
7
6
  import subprocess
8
7
  import unittest
9
8
  from collections.abc import Generator
10
9
  from pathlib import Path
11
- from typing import Any, Optional, Union, cast
10
+ from typing import Any, cast
12
11
  from unittest.mock import patch
12
+ from urllib.parse import quote
13
13
  from uuid import uuid4
14
14
 
15
15
  import pytest
16
- from pytest_httpserver import HTTPServer
17
-
18
16
  import WDL.Error
19
17
  import WDL.Expr
18
+ from pytest_httpserver import HTTPServer
20
19
 
21
20
  from toil.fileStores import FileID
22
21
  from toil.test import (
23
- ToilTest,
24
22
  get_data,
25
23
  needs_docker,
26
24
  needs_docker_cuda,
27
25
  needs_google_storage,
28
26
  needs_online,
29
27
  needs_singularity_or_docker,
30
- needs_wdl,
31
28
  slow,
32
29
  )
33
30
  from toil.version import exactPython
34
- from toil.wdl.wdltoil import (
35
- WDLSectionJob,
36
- WDLWorkflowGraph,
37
- parse_disks,
38
- )
31
+ from toil.wdl.wdltoil import WDLSectionJob, WDLWorkflowGraph, parse_disks
39
32
 
40
33
  logger = logging.getLogger(__name__)
41
34
 
42
35
 
43
36
  WDL_CONFORMANCE_TEST_REPO = "https://github.com/DataBiosphere/wdl-conformance-tests.git"
44
- WDL_CONFORMANCE_TEST_COMMIT = "46b5f85ee38ec60d0b8b9c35928b5104a2af83d5"
37
+ WDL_CONFORMANCE_TEST_COMMIT = "12d6d8a54a11803fb529aeca18ee01cba01f1d3e"
45
38
  # These tests are known to require things not implemented by
46
39
  # Toil and will not be run in CI.
47
40
  WDL_CONFORMANCE_TESTS_UNSUPPORTED_BY_TOIL = [
48
- 16, # Basic object test (deprecated and removed in 1.1); MiniWDL and toil-wdl-runner do not support Objects, so this will fail if ran by them
49
- 21, # Parser: expression placeholders in strings in conditional expressions in 1.0, Cromwell style; Fails with MiniWDL and toil-wdl-runner
50
- 64, # Legacy test for as_map_as_input; It looks like MiniWDL does not have the function as_map()
51
- 77, # Test that array cannot coerce to a string. WDL 1.1 does not allow compound types to coerce into a string. This should return a TypeError.
41
+ "object", # Basic object test (deprecated and removed in 1.1); MiniWDL and toil-wdl-runner do not support Objects, so this will fail if ran by them
42
+ "string_placeholders_conditionals_1_0", # Parser: expression placeholders in strings in conditional expressions in 1.0, Cromwell style; Fails with MiniWDL and toil-wdl-runner
43
+ "as_map", # Legacy test for as_map_as_input; It looks like MiniWDL does not have the function as_map()
44
+ "array_coerce", # Test that array cannot coerce to a string. WDL 1.1 does not allow compound types to coerce into a string. This should return a TypeError.
45
+ "sibling_directories", # TODO: This has started failing in CI despite passing locally on Mac and Linux. Come up with a more consistent test!
52
46
  ]
53
- WDL_UNIT_TESTS_UNSUPPORTED_BY_TOIL = [
54
- 14, # test_object, Objects are not supported
55
- 19, # map_to_struct, miniwdl cannot coerce map to struct, https://github.com/chanzuckerberg/miniwdl/issues/712
56
- 52, # relative_and_absolute, needs root to run
57
- 58, # test_gpu, needs gpu to run, else warning
58
- 59, # will be fixed in #5001
59
- 66, # This needs way too many resources (and actually doesn't work?), see https://github.com/DataBiosphere/wdl-conformance-tests/blob/2d617b703a33791f75f30a9db43c3740a499cd89/README_UNIT.md?plain=1#L8
60
- 67, # same as above
61
- 68, # Bug, see #https://github.com/DataBiosphere/toil/issues/4993
62
- 69, # Same as 68
63
- 87, # MiniWDL does not handle metacharacters properly when running regex, https://github.com/chanzuckerberg/miniwdl/issues/709
64
- 97, # miniwdl bug, see https://github.com/chanzuckerberg/miniwdl/issues/701
65
- 105, # miniwdl (and toil) bug, unserializable json is serialized, see https://github.com/chanzuckerberg/miniwdl/issues/702
66
- 107, # object not supported
67
- 108, # object not supported
68
- 109, # object not supported
69
- 110, # object not supported
70
- 120, # miniwdl bug, see https://github.com/chanzuckerberg/miniwdl/issues/699
71
- 131, # miniwdl bug, evalerror, see https://github.com/chanzuckerberg/miniwdl/issues/700
72
- 134, # same as 131
73
- 144, # miniwdl and toil bug
47
+
48
+ # These tests (in the same order as in SPEC.md) are known to fail
49
+ WDL_11_UNIT_TESTS_UNSUPPORTED_BY_TOIL = [
50
+ "test_object", # Objects are not supported
51
+ "map_to_struct", # miniwdl cannot coerce map to struct, https://github.com/chanzuckerberg/miniwdl/issues/712
52
+ "relative_and_absolute_task", # needs root to run
53
+ "test_gpu_task", # needs gpu to run, else warning
54
+ "hisat2_task", # This needs way too many resources (and actually doesn't work?), see https://github.com/DataBiosphere/wdl-conformance-tests/blob/2d617b703a33791f75f30a9db43c3740a499cd89/README_UNIT.md?plain=1#L8
55
+ "gatk_haplotype_caller_task", # same as above
56
+ "input_ref_call", # Inputs refering into workflow body not yet implemented: see https://github.com/DataBiosphere/toil/issues/4993
57
+ "call_imported", # Same as input_ref_call since it imports it
58
+ "call_imported_task", # Same as input_ref_call since it imports it
59
+ "test_sub", # MiniWDL does not handle metacharacters properly when running regex, https://github.com/chanzuckerberg/miniwdl/issues/709
60
+ "read_bool_task", # miniwdl bug, see https://github.com/chanzuckerberg/miniwdl/issues/701
61
+ "write_json_fail", # miniwdl (and toil) bug, unserializable json is serialized, see https://github.com/chanzuckerberg/miniwdl/issues/702
62
+ "read_object_task", # object not supported
63
+ "read_objects_task", # object not supported
64
+ "write_object_task", # object not supported
65
+ "write_objects_task", # object not supported
66
+ "test_transpose", # miniwdl bug, see https://github.com/chanzuckerberg/miniwdl/issues/699
67
+ "test_as_map_fail", # miniwdl bug, evalerror, see https://github.com/chanzuckerberg/miniwdl/issues/700
68
+ "test_collect_by_key", # same as test_as_map_
69
+ "serde_pair", # miniwdl and toil bug
70
+ ]
71
+
72
+ WDL_12_UNIT_TESTS_UNSUPPORTED_BY_TOIL = WDL_11_UNIT_TESTS_UNSUPPORTED_BY_TOIL + [
73
+ "relative_paths_context", # Toil can't yet resolve File coercion at task scope relative to task file.
74
+ "file_directory_equality", # String to Directory coercion not yet implemented.
75
+ "single_return_code_task", # MiniWDL 1.13.1 only knows returnCodes and not return_codes.
76
+ "all_return_codes_task", # MiniWDL 1.13.1 only knows returnCodes and not return_codes.
77
+ "test_runtime_info_task", # MiniWDL 1.13.1 can't yet expose the task global.
78
+ "placeholder_none", # 'outputs' section expected 1 results (['placeholder_none.s']), got 0 instead ([]) with exit code 1
79
+ "person_struct_task", # Doesn't work as written in the spec; see https://github.com/openwdl/wdl/issues/739
80
+ "import_structs", # Feature not yet implemented?
81
+ "environment_variable_should_echo", # Ln 14 Col 45: Unexpected token STRING1_FRAGMENT
82
+ "outputs_task", # 'outputs' section expected 2 results (['outputs.threshold', 'outputs.two_csvs']), got 3 instead (['outputs.two_csvs', 'outputs.csvs', 'outputs.threshold']) with exit code 0
83
+ "glob_task", # 'outputs' section expected 1 results (['glob.last_file_contents']), got 2 instead (['glob.last_file_contents', 'glob.outfiles']) with exit code 0
84
+ "test_hints_task", # Test is written as if the file has 3 lines, but it really has 2. See https://github.com/openwdl/wdl/issues/741
85
+ "input_hint_task", # Missing outputs in test definition: https://github.com/openwdl/wdl/issues/740
86
+ "test_allow_nested_inputs", # Ln 27 Col 3: Unexpected token HINTS
87
+ "multi_nested_inputs", # Ln 8 Col 9: Unexpected token STRING1_FRAGMENT
88
+ "allow_nested", # Ln 32 Col 9: Unexpected token STRING1_FRAGMENT
89
+ "test_find_task", # Ln 9 Col 22: No such function: find
90
+ "test_matches_task", # Ln 7 Col 29: No such function: matches
91
+ "change_extension_task", # 'outputs' section expected 2 results (['change_extension.data', 'change_extension.index']), got 3 instead (['change_extension.index', 'change_extension.data', 'change_extension.data_file']) with exit code 0
92
+ "join_paths_task", # Ln 14 Col 15: No such function: join_paths
93
+ "gen_files_task", # 'outputs' section expected 1 results (['gen_files.glob_len']), got 2 instead (['gen_files.glob_len', 'gen_files.files']) with exit code 0
94
+ "file_sizes_task", # WDL.Error.StaticTypeMismatch: Expected File? instead of Map[String,Pair[Int,File?]]
95
+ "read_tsv_task", # Ln 21 Col 5: Unknown type Object
96
+ "write_tsv_task", # Ln 28 Col 16: write_tsv expects 1 argument(s)
97
+ "test_contains", # Ln 25 Col 22: No such function: contains
98
+ "chunk_array", # Ln 8 Col 17: No such function: chunk
99
+ "test_select_first", # Ln 14 Col 17: select_first expects 1 argument(s)
100
+ "test_keys", # Ln 32 Col 36: Expected Map[Any,Any] instead of Name
101
+ "test_contains_key", # Ln 18 Col 20: No such function: contains_key
102
+ "test_values", # Ln 28 Col 20: No such function: values
103
+ "test_length", # length() isn't implemented for maps and strings yet
74
104
  ]
75
105
 
76
106
 
@@ -152,8 +182,78 @@ class TestWDLConformance:
152
182
  "-v",
153
183
  "1.1",
154
184
  "--progress",
155
- "--exclude-numbers",
156
- ",".join([str(t) for t in WDL_UNIT_TESTS_UNSUPPORTED_BY_TOIL]),
185
+ "--exclude-ids",
186
+ ",".join(WDL_11_UNIT_TESTS_UNSUPPORTED_BY_TOIL),
187
+ ]
188
+ p2 = subprocess.run(commands2, capture_output=True)
189
+ self.check(p2)
190
+
191
+ @slow
192
+ def test_unit_tests_v12(self, wdl_conformance_test_repo: Path) -> None:
193
+ # TODO: Using a branch lets Toil commits that formerly passed start to
194
+ # fail CI when the branch moves.
195
+ os.chdir(wdl_conformance_test_repo)
196
+ repo_url = "https://github.com/adamnovak/wdl.git"
197
+ repo_branch = "wdl-1.2-fix-json"
198
+ commands1 = [
199
+ exactPython,
200
+ "setup_unit_tests.py",
201
+ "-v",
202
+ "1.2",
203
+ "--extra-patch-data",
204
+ "unit_tests_patch_data.yaml",
205
+ "--repo",
206
+ repo_url,
207
+ "--branch",
208
+ repo_branch,
209
+ "--force-pull",
210
+ ]
211
+ p1 = subprocess.run(commands1, capture_output=True)
212
+ self.check(p1)
213
+ commands2 = [
214
+ exactPython,
215
+ "run_unit.py",
216
+ "-r",
217
+ "toil-wdl-runner",
218
+ "-v",
219
+ "1.2",
220
+ "--progress",
221
+ "--exclude-ids",
222
+ ",".join(WDL_12_UNIT_TESTS_UNSUPPORTED_BY_TOIL),
223
+ ]
224
+ p2 = subprocess.run(commands2, capture_output=True)
225
+ self.check(p2)
226
+
227
+ @slow
228
+ def test_single_unit_test(self, wdl_conformance_test_repo: Path) -> None:
229
+ os.chdir(wdl_conformance_test_repo)
230
+ repo_url = "https://github.com/openwdl/wdl.git"
231
+ repo_branch = "wdl-1.1"
232
+ commands1 = [
233
+ exactPython,
234
+ "setup_unit_tests.py",
235
+ "-v",
236
+ "1.1",
237
+ "--extra-patch-data",
238
+ "unit_tests_patch_data.yaml",
239
+ "--repo",
240
+ repo_url,
241
+ "--branch",
242
+ repo_branch,
243
+ "--force-pull",
244
+ ]
245
+ p1 = subprocess.run(commands1, capture_output=True)
246
+ self.check(p1)
247
+ commands2 = [
248
+ exactPython,
249
+ "run_unit.py",
250
+ "-r",
251
+ "toil-wdl-runner",
252
+ "-v",
253
+ "1.1",
254
+ "--progress",
255
+ "--id",
256
+ ",".join("glob_task"),
157
257
  ]
158
258
  p2 = subprocess.run(commands2, capture_output=True)
159
259
  self.check(p2)
@@ -173,10 +273,8 @@ class TestWDLConformance:
173
273
  "1.0",
174
274
  ]
175
275
  if WDL_CONFORMANCE_TESTS_UNSUPPORTED_BY_TOIL:
176
- commands.append("--exclude-numbers")
177
- commands.append(
178
- ",".join([str(t) for t in WDL_CONFORMANCE_TESTS_UNSUPPORTED_BY_TOIL])
179
- )
276
+ commands.append("--exclude-ids")
277
+ commands.append(",".join(WDL_CONFORMANCE_TESTS_UNSUPPORTED_BY_TOIL))
180
278
  p = subprocess.run(commands, capture_output=True)
181
279
 
182
280
  self.check(p)
@@ -196,10 +294,8 @@ class TestWDLConformance:
196
294
  "1.1",
197
295
  ]
198
296
  if WDL_CONFORMANCE_TESTS_UNSUPPORTED_BY_TOIL:
199
- commands.append("--exclude-numbers")
200
- commands.append(
201
- ",".join([str(t) for t in WDL_CONFORMANCE_TESTS_UNSUPPORTED_BY_TOIL])
202
- )
297
+ commands.append("--exclude-ids")
298
+ commands.append(",".join(WDL_CONFORMANCE_TESTS_UNSUPPORTED_BY_TOIL))
203
299
  p = subprocess.run(commands, capture_output=True)
204
300
 
205
301
  self.check(p)
@@ -207,7 +303,9 @@ class TestWDLConformance:
207
303
  # estimated running time: 10 minutes (once all the appropriate tests get
208
304
  # marked as "development")
209
305
  @slow
210
- def test_conformance_tests_development(self, wdl_conformance_test_repo: Path) -> None:
306
+ def test_conformance_tests_development(
307
+ self, wdl_conformance_test_repo: Path
308
+ ) -> None:
211
309
  os.chdir(wdl_conformance_test_repo)
212
310
  commands = [
213
311
  exactPython,
@@ -220,10 +318,8 @@ class TestWDLConformance:
220
318
  "development",
221
319
  ]
222
320
  if WDL_CONFORMANCE_TESTS_UNSUPPORTED_BY_TOIL:
223
- commands.append("--exclude-numbers")
224
- commands.append(
225
- ",".join([str(t) for t in WDL_CONFORMANCE_TESTS_UNSUPPORTED_BY_TOIL])
226
- )
321
+ commands.append("--exclude-ids")
322
+ commands.append(",".join(WDL_CONFORMANCE_TESTS_UNSUPPORTED_BY_TOIL))
227
323
  p = subprocess.run(commands, capture_output=True)
228
324
 
229
325
  self.check(p)
@@ -284,6 +380,51 @@ class TestWDL:
284
380
  assert os.path.exists(result["ga4ghMd5.value"])
285
381
  assert os.path.basename(result["ga4ghMd5.value"]) == "md5sum.txt"
286
382
 
383
+ @needs_singularity_or_docker
384
+ def test_file_uri_no_hostname(self, tmp_path: Path, subtests: pytest.Subtests) -> None:
385
+ """Test if Toil handles file URIs without even empty hostnames"""
386
+
387
+ # We need to test file:/absolute/path/to/the/file in conjunction with
388
+ # worker imports, which didn't work in
389
+ # https://github.com/DataBiosphere/toil/issues/5392
390
+ with get_data("test/wdl/md5sum/md5sum.1.0.wdl") as wdl:
391
+ with get_data("test/wdl/md5sum/md5sum.input") as input_file:
392
+ # We need to wrap the absolute path to the input file in a JSON as a URI.
393
+ file_uri = f"file:{quote(os.path.abspath(input_file))}"
394
+
395
+ # Then put that in inline input JSON
396
+ input_json = json.dumps({"ga4ghMd5.inputFile": file_uri})
397
+
398
+ for worker_import in (False, True):
399
+ with subtests.test(msg=f"Worker import: {worker_import}"):
400
+
401
+ result_json = subprocess.check_output(
402
+ self.base_command
403
+ + [
404
+ str(wdl),
405
+ input_json,
406
+ "-o",
407
+ str(tmp_path),
408
+ "--logDebug",
409
+ "--retryCount=0",
410
+ ]
411
+ + (
412
+ [
413
+ "--runImportsOnWorkers",
414
+ ]
415
+ if worker_import
416
+ else []
417
+ )
418
+ )
419
+ result = json.loads(result_json)
420
+
421
+ assert "ga4ghMd5.value" in result
422
+ assert isinstance(result["ga4ghMd5.value"], str)
423
+ assert os.path.exists(result["ga4ghMd5.value"])
424
+ assert (
425
+ os.path.basename(result["ga4ghMd5.value"]) == "md5sum.txt"
426
+ )
427
+
287
428
  @needs_online
288
429
  def test_url_to_file(self, tmp_path: Path) -> None:
289
430
  """
@@ -314,7 +455,7 @@ class TestWDL:
314
455
  "-o",
315
456
  str(tmp_path),
316
457
  "--logInfo",
317
- "--retryCount=0"
458
+ "--retryCount=0",
318
459
  ]
319
460
  )
320
461
  result = json.loads(result_json)
@@ -329,13 +470,7 @@ class TestWDL:
329
470
  with get_data("test/wdl/testfiles/gather.wdl") as wdl:
330
471
  result_json = subprocess.check_output(
331
472
  self.base_command
332
- + [
333
- str(wdl),
334
- "-o",
335
- str(tmp_path),
336
- "--logInfo",
337
- "--retryCount=0"
338
- ]
473
+ + [str(wdl), "-o", str(tmp_path), "--logInfo", "--retryCount=0"]
339
474
  )
340
475
  result = json.loads(result_json)
341
476
 
@@ -374,42 +509,35 @@ class TestWDL:
374
509
  out_dir = tmp_path / "out"
375
510
  file_path = tmp_path / "file"
376
511
  jobstore_path = tmp_path / "tree"
377
- command = (
378
- self.base_command
379
- + [
380
- str(wdl),
381
- "-o",
382
- str(out_dir),
383
- "-i",
384
- json.dumps({"read_file.input_string": str(file_path)}),
385
- "--jobStore",
386
- str(jobstore_path),
387
- "--retryCount=0"
388
- ]
389
- )
512
+ command = self.base_command + [
513
+ str(wdl),
514
+ "-o",
515
+ str(out_dir),
516
+ "-i",
517
+ json.dumps({"read_file.input_string": str(file_path)}),
518
+ "--jobStore",
519
+ str(jobstore_path),
520
+ "--retryCount=0",
521
+ ]
390
522
  with pytest.raises(subprocess.CalledProcessError):
391
523
  # The first time we run it, it should fail because it's trying
392
524
  # to work on a nonexistent file from a string path.
393
- result_json = subprocess.check_output(
394
- command + ["--logCritical"]
395
- )
525
+ result_json = subprocess.check_output(command + ["--logCritical"])
396
526
 
397
527
  # Then create the file
398
528
  with open(file_path, "w") as f:
399
529
  f.write("This is a line\n")
400
530
  f.write("This is a different line")
401
-
531
+
402
532
  # Now it should work
403
- result_json = subprocess.check_output(
404
- command + ["--restart"]
405
- )
533
+ result_json = subprocess.check_output(command + ["--restart"])
406
534
  result = json.loads(result_json)
407
535
 
408
536
  assert "read_file.lines" in result
409
537
  assert isinstance(result["read_file.lines"], list)
410
538
  assert result["read_file.lines"] == [
411
539
  "This is a line",
412
- "This is a different line"
540
+ "This is a different line",
413
541
  ]
414
542
 
415
543
  # Since we were catching
@@ -721,15 +849,14 @@ class TestWDL:
721
849
  Return the parsed output.
722
850
  """
723
851
  logger.info("Test optional file with HTTP code %s", code)
724
- httpserver.expect_request(
725
- "/" + str(code)
726
- ).respond_with_data(
727
- "Some data",
728
- status=code,
729
- content_type="text/plain"
852
+ httpserver.expect_request("/" + str(code)).respond_with_data(
853
+ "Some data", status=code, content_type="text/plain"
730
854
  )
731
855
  base_url = httpserver.url_for("/")
732
- json_value = '{"url_to_optional_file.http_code": %d, "url_to_optional_file.base_url": "%s"}' % (code, base_url)
856
+ json_value = (
857
+ '{"url_to_optional_file.http_code": %d, "url_to_optional_file.base_url": "%s"}'
858
+ % (code, base_url)
859
+ )
733
860
  result_json = subprocess.check_output(
734
861
  self.base_command
735
862
  + [
@@ -761,6 +888,7 @@ class TestWDL:
761
888
  with pytest.raises(subprocess.CalledProcessError):
762
889
  run_for_code(code)
763
890
 
891
+ @needs_singularity_or_docker
764
892
  def test_missing_output_directory(self, tmp_path: Path) -> None:
765
893
  """
766
894
  Test if Toil can run a WDL workflow into a new directory.
@@ -781,7 +909,7 @@ class TestWDL:
781
909
 
782
910
  @needs_singularity_or_docker
783
911
  def test_miniwdl_self_test(
784
- self, tmp_path: Path, extra_args: Optional[list[str]] = None
912
+ self, tmp_path: Path, extra_args: list[str] | None = None
785
913
  ) -> None:
786
914
  """Test if the MiniWDL self test runs and produces the expected output."""
787
915
  with get_data("test/wdl/miniwdl_self_test/self_test.wdl") as wdl_file:
@@ -860,7 +988,7 @@ class TestWDL:
860
988
  @pytest.mark.integrative
861
989
  @needs_singularity_or_docker
862
990
  def test_dockstore_trs(
863
- self, tmp_path: Path, extra_args: Optional[list[str]] = None
991
+ self, tmp_path: Path, extra_args: list[str] | None = None
864
992
  ) -> None:
865
993
  wdl_file = "#workflow/github.com/dockstore/bcc2020-training/HelloWorld:master"
866
994
  # Needs an input but doesn't provide a good one.
@@ -894,7 +1022,7 @@ class TestWDL:
894
1022
  @pytest.mark.integrative
895
1023
  @needs_singularity_or_docker
896
1024
  def test_dockstore_metrics_publication(
897
- self, tmp_path: Path, extra_args: Optional[list[str]] = None
1025
+ self, tmp_path: Path, extra_args: list[str] | None = None
898
1026
  ) -> None:
899
1027
  wdl_file = "#workflow/github.com/dockstore/bcc2020-training/HelloWorld:master"
900
1028
  # Needs an input but doesn't provide a good one.
@@ -908,10 +1036,12 @@ class TestWDL:
908
1036
  # Set credentials we got permission to publish from the Dockstore team,
909
1037
  # and work on the staging Dockstore.
910
1038
  env["TOIL_TRS_ROOT"] = "https://staging.dockstore.org"
911
- env["TOIL_DOCKSTORE_TOKEN"] = "99cf5578ebe94b194d7864630a86258fa3d6cedcc17d757b5dd49e64ee3b68c3"
1039
+ env["TOIL_DOCKSTORE_TOKEN"] = (
1040
+ "99cf5578ebe94b194d7864630a86258fa3d6cedcc17d757b5dd49e64ee3b68c3"
1041
+ )
912
1042
  # Enable history for when <https://github.com/DataBiosphere/toil/pull/5258> merges
913
1043
  env["TOIL_HISTORY"] = "True"
914
-
1044
+
915
1045
  try:
916
1046
  output_log = subprocess.check_output(
917
1047
  self.base_command
@@ -930,10 +1060,15 @@ class TestWDL:
930
1060
  env=env,
931
1061
  ).decode("utf-8", errors="replace")
932
1062
  except subprocess.CalledProcessError as e:
933
- logger.error("Test run of Toil failed: %s", e.stdout.decode("utf-8", errors="replace"))
1063
+ logger.error(
1064
+ "Test run of Toil failed: %s",
1065
+ e.stdout.decode("utf-8", errors="replace"),
1066
+ )
934
1067
  raise
935
1068
 
936
- assert "Workflow metrics were accepted by Dockstore." in output_log, f"No acceptance message in log: {output_log}"
1069
+ assert (
1070
+ "Workflow metrics were accepted by Dockstore." in output_log
1071
+ ), f"No acceptance message in log: {output_log}"
937
1072
 
938
1073
  @slow
939
1074
  @needs_docker_cuda
@@ -943,27 +1078,10 @@ class TestWDL:
943
1078
 
944
1079
  json_dir = tmp_path / "json"
945
1080
  json_dir.mkdir()
946
- base_uri = "https://raw.githubusercontent.com/vgteam/vg_wdl/65dd739aae765f5c4dedd14f2e42d5a263f9267a"
1081
+ base_uri = "https://raw.githubusercontent.com/vgteam/vg_wdl/fc6654db25e3e2c2bb85cc6dc5e3bb81dfe7a236"
947
1082
 
948
1083
  wdl_file = f"{base_uri}/workflows/giraffe_and_deepvariant.wdl"
949
- json_file = json_dir / "inputs.json"
950
- with json_file.open("w") as fp:
951
- # Write some inputs. We need to override the example inputs to use a GPU container, but that means we need absolute input URLs.
952
- json.dump(
953
- {
954
- "GiraffeDeepVariant.INPUT_READ_FILE_1": f"{base_uri}/tests/small_sim_graph/reads_1.fastq.gz",
955
- "GiraffeDeepVariant.INPUT_READ_FILE_2": f"{base_uri}/tests/small_sim_graph/reads_2.fastq.gz",
956
- "GiraffeDeepVariant.XG_FILE": f"{base_uri}/tests/small_sim_graph/graph.xg",
957
- "GiraffeDeepVariant.SAMPLE_NAME": "s0",
958
- "GiraffeDeepVariant.GBWT_FILE": f"{base_uri}/tests/small_sim_graph/graph.gbwt",
959
- "GiraffeDeepVariant.GGBWT_FILE": f"{base_uri}/tests/small_sim_graph/graph.gg",
960
- "GiraffeDeepVariant.MIN_FILE": f"{base_uri}/tests/small_sim_graph/graph.min",
961
- "GiraffeDeepVariant.DIST_FILE": f"{base_uri}/tests/small_sim_graph/graph.dist",
962
- "GiraffeDeepVariant.OUTPUT_GAF": True,
963
- "GiraffeDeepVariant.runDeepVariantCallVariants.in_dv_gpu_container": "google/deepvariant:1.3.0-gpu",
964
- },
965
- fp,
966
- )
1084
+ json_file = f"{base_uri}/params/giraffe_and_deepvariant.json"
967
1085
 
968
1086
  result_json = subprocess.check_output(
969
1087
  self.base_command
@@ -1000,7 +1118,7 @@ class TestWDL:
1000
1118
  # TODO: Reduce memory requests with custom/smaller inputs.
1001
1119
  # TODO: Skip if node lacks enough memory.
1002
1120
 
1003
- base_uri = "https://raw.githubusercontent.com/vgteam/vg_wdl/65dd739aae765f5c4dedd14f2e42d5a263f9267a"
1121
+ base_uri = "https://raw.githubusercontent.com/vgteam/vg_wdl/fc6654db25e3e2c2bb85cc6dc5e3bb81dfe7a236"
1004
1122
  wdl_file = f"{base_uri}/workflows/giraffe.wdl"
1005
1123
  json_file = f"{base_uri}/params/giraffe.json"
1006
1124
 
@@ -1055,19 +1173,24 @@ class TestWDL:
1055
1173
  """Test that Toil's lint check works"""
1056
1174
  with get_data("test/wdl/lint_error.wdl") as wdl:
1057
1175
  out = subprocess.check_output(
1058
- self.base_command + [str(wdl), "-o", str(tmp_path), "--logInfo"], stderr=subprocess.STDOUT)
1176
+ self.base_command + [str(wdl), "-o", str(tmp_path), "--logInfo"],
1177
+ stderr=subprocess.STDOUT,
1178
+ )
1059
1179
 
1060
- assert b'UnnecessaryQuantifier' in out
1180
+ assert b"UnnecessaryQuantifier" in out
1061
1181
 
1062
1182
  p = subprocess.Popen(
1063
- self.base_command + [wdl, "--strict=True", "--logCritical"], stderr=subprocess.PIPE)
1183
+ self.base_command + [wdl, "--strict=True", "--logCritical"],
1184
+ stderr=subprocess.PIPE,
1185
+ )
1064
1186
  # Not actually a test assert; we need this to teach MyPy that we
1065
1187
  # get an stderr when we pass stderr=subprocess.PIPE.
1066
1188
  assert p.stderr is not None
1067
1189
  stderr = p.stderr.read()
1068
1190
  p.wait()
1069
1191
  assert p.returncode == 2
1070
- assert b'Workflow did not pass linting in strict mode' in stderr
1192
+ assert b"Workflow did not pass linting in strict mode" in stderr
1193
+
1071
1194
 
1072
1195
  class TestWDLToilBench(unittest.TestCase):
1073
1196
  """Tests for Toil's MiniWDL-based implementation that don't run workflows."""
@@ -1178,14 +1301,16 @@ class TestWDLToilBench(unittest.TestCase):
1178
1301
  assert "decl2" in result[0]
1179
1302
  assert "successor" in result[1]
1180
1303
 
1181
- def make_string_expr(self, to_parse: str, expr_type: type[WDL.Expr.String] = WDL.Expr.String) -> WDL.Expr.String:
1304
+ def make_string_expr(
1305
+ self, to_parse: str, expr_type: type[WDL.Expr.String] = WDL.Expr.String
1306
+ ) -> WDL.Expr.String:
1182
1307
  """
1183
1308
  Parse pseudo-WDL for testing whitespace removal.
1184
1309
  """
1185
1310
 
1186
1311
  pos = WDL.Error.SourcePosition("nowhere", "nowhere", 0, 0, 0, 0)
1187
1312
 
1188
- parts: list[Union[str, WDL.Expr.Placeholder]] = re.split("(~{[^}]*})", to_parse)
1313
+ parts: list[str | WDL.Expr.Placeholder] = re.split("(~{[^}]*})", to_parse)
1189
1314
  for i in range(1, len(parts), 2):
1190
1315
  parts[i] = WDL.Expr.Placeholder(pos, {}, WDL.Expr.Null(pos))
1191
1316
 
@@ -1196,9 +1321,7 @@ class TestWDLToilBench(unittest.TestCase):
1196
1321
  Test to make sure that we pick sensible but non-colliding directories to put files in.
1197
1322
  """
1198
1323
 
1199
- from toil.wdl.wdltoil import (
1200
- choose_human_readable_directory,
1201
- )
1324
+ from toil.wdl.wdltoil import choose_human_readable_directory
1202
1325
 
1203
1326
  # The first time we should get a path with the task name
1204
1327
  first_chosen = choose_human_readable_directory(