toil 6.0.0__py3-none-any.whl → 6.1.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 (51) hide show
  1. toil/batchSystems/abstractBatchSystem.py +19 -4
  2. toil/batchSystems/abstractGridEngineBatchSystem.py +22 -22
  3. toil/batchSystems/cleanup_support.py +7 -3
  4. toil/batchSystems/lsf.py +7 -7
  5. toil/batchSystems/slurm.py +85 -14
  6. toil/bus.py +38 -0
  7. toil/common.py +20 -18
  8. toil/cwl/cwltoil.py +81 -63
  9. toil/exceptions.py +1 -1
  10. toil/fileStores/abstractFileStore.py +53 -4
  11. toil/fileStores/cachingFileStore.py +4 -20
  12. toil/fileStores/nonCachingFileStore.py +5 -14
  13. toil/job.py +46 -30
  14. toil/jobStores/abstractJobStore.py +21 -23
  15. toil/jobStores/aws/utils.py +5 -4
  16. toil/jobStores/fileJobStore.py +1 -1
  17. toil/leader.py +17 -14
  18. toil/lib/conversions.py +19 -0
  19. toil/lib/generatedEC2Lists.py +8 -8
  20. toil/lib/io.py +28 -2
  21. toil/lib/resources.py +8 -1
  22. toil/lib/threading.py +27 -12
  23. toil/options/common.py +5 -7
  24. toil/options/wdl.py +5 -0
  25. toil/provisioners/abstractProvisioner.py +8 -0
  26. toil/statsAndLogging.py +36 -8
  27. toil/test/batchSystems/test_slurm.py +21 -6
  28. toil/test/cactus/__init__.py +0 -0
  29. toil/test/cactus/test_cactus_integration.py +58 -0
  30. toil/test/cwl/cwlTest.py +243 -151
  31. toil/test/docs/scriptsTest.py +2 -2
  32. toil/test/jobStores/jobStoreTest.py +7 -5
  33. toil/test/lib/test_ec2.py +1 -1
  34. toil/test/options/__init__.py +13 -0
  35. toil/test/options/options.py +37 -0
  36. toil/test/provisioners/clusterTest.py +9 -8
  37. toil/test/utils/toilDebugTest.py +1 -1
  38. toil/test/utils/utilsTest.py +3 -3
  39. toil/test/wdl/wdltoil_test.py +91 -16
  40. toil/utils/toilDebugFile.py +1 -1
  41. toil/utils/toilStats.py +309 -266
  42. toil/utils/toilStatus.py +1 -1
  43. toil/version.py +9 -9
  44. toil/wdl/wdltoil.py +341 -189
  45. toil/worker.py +31 -16
  46. {toil-6.0.0.dist-info → toil-6.1.0.dist-info}/METADATA +6 -7
  47. {toil-6.0.0.dist-info → toil-6.1.0.dist-info}/RECORD +51 -47
  48. {toil-6.0.0.dist-info → toil-6.1.0.dist-info}/LICENSE +0 -0
  49. {toil-6.0.0.dist-info → toil-6.1.0.dist-info}/WHEEL +0 -0
  50. {toil-6.0.0.dist-info → toil-6.1.0.dist-info}/entry_points.txt +0 -0
  51. {toil-6.0.0.dist-info → toil-6.1.0.dist-info}/top_level.txt +0 -0
toil/test/cwl/cwlTest.py CHANGED
@@ -26,10 +26,20 @@ import zipfile
26
26
  from functools import partial
27
27
  from io import StringIO
28
28
  from pathlib import Path
29
- from typing import Dict, List, MutableMapping, Optional
29
+ from typing import (TYPE_CHECKING,
30
+ Any,
31
+ Callable,
32
+ Dict,
33
+ List,
34
+ MutableMapping,
35
+ Optional,
36
+ cast)
30
37
  from unittest.mock import Mock, call
31
38
  from urllib.request import urlretrieve
32
39
 
40
+ if TYPE_CHECKING:
41
+ from cwltool.utils import CWLObjectType
42
+
33
43
  import pytest
34
44
 
35
45
  pkg_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) # noqa
@@ -37,7 +47,8 @@ sys.path.insert(0, pkg_root) # noqa
37
47
 
38
48
  from schema_salad.exceptions import ValidationException
39
49
 
40
- from toil.cwl.utils import (download_structure,
50
+ from toil.cwl.utils import (DirectoryStructure,
51
+ download_structure,
41
52
  visit_cwl_class_and_reduce,
42
53
  visit_top_cwl_class)
43
54
  from toil.exceptions import FailedJobsException
@@ -74,14 +85,14 @@ def run_conformance_tests(
74
85
  yml: str,
75
86
  runner: Optional[str] = None,
76
87
  caching: bool = False,
77
- batchSystem: str = None,
78
- selected_tests: str = None,
79
- selected_tags: str = None,
80
- skipped_tests: str = None,
88
+ batchSystem: Optional[str] = None,
89
+ selected_tests: Optional[str] = None,
90
+ selected_tags: Optional[str] = None,
91
+ skipped_tests: Optional[str] = None,
81
92
  extra_args: Optional[List[str]] = None,
82
93
  must_support_all_features: bool = False,
83
94
  junit_file: Optional[str] = None,
84
- ):
95
+ ) -> None:
85
96
  """
86
97
  Run the CWL conformance tests.
87
98
 
@@ -199,6 +210,8 @@ def run_conformance_tests(
199
210
  raise e
200
211
 
201
212
 
213
+ TesterFuncType = Callable[[str, str, "CWLObjectType"], None]
214
+
202
215
  @needs_cwl
203
216
  class CWLWorkflowTest(ToilTest):
204
217
  """
@@ -207,19 +220,19 @@ class CWLWorkflowTest(ToilTest):
207
220
  inputs.
208
221
  """
209
222
 
210
- def setUp(self):
223
+ def setUp(self) -> None:
211
224
  """Runs anew before each test to create farm fresh temp dirs."""
212
225
  self.outDir = f"/tmp/toil-cwl-test-{str(uuid.uuid4())}"
213
226
  os.makedirs(self.outDir)
214
227
  self.rootDir = self._projectRootPath()
215
228
 
216
- def tearDown(self):
229
+ def tearDown(self) -> None:
217
230
  """Clean up outputs."""
218
231
  if os.path.exists(self.outDir):
219
232
  shutil.rmtree(self.outDir)
220
233
  unittest.TestCase.tearDown(self)
221
234
 
222
- def test_cwl_cmdline_input(self):
235
+ def test_cwl_cmdline_input(self) -> None:
223
236
  """
224
237
  Test that running a CWL workflow with inputs specified on the command line passes.
225
238
  """
@@ -230,7 +243,15 @@ class CWLWorkflowTest(ToilTest):
230
243
  # If the workflow runs, it must have had options
231
244
  cwltoil.main(args, stdout=st)
232
245
 
233
- def _tester(self, cwlfile, jobfile, expect, main_args=[], out_name="output", output_here=False):
246
+ def _tester(
247
+ self,
248
+ cwlfile: str,
249
+ jobfile: str,
250
+ expect: "CWLObjectType",
251
+ main_args: List[str] = [],
252
+ out_name: str = "output",
253
+ output_here: bool = False,
254
+ ) -> None:
234
255
  from toil.cwl import cwltoil
235
256
 
236
257
  st = StringIO()
@@ -264,7 +285,9 @@ class CWLWorkflowTest(ToilTest):
264
285
  self.assertTrue(os.path.exists(v["path"]))
265
286
  self.assertFalse(os.stat(v["path"]).st_mode & stat.S_IXUSR)
266
287
 
267
- def _debug_worker_tester(self, cwlfile, jobfile, expect):
288
+ def _debug_worker_tester(
289
+ self, cwlfile: str, jobfile: str, expect: "CWLObjectType"
290
+ ) -> None:
268
291
  from toil.cwl import cwltoil
269
292
 
270
293
  st = StringIO()
@@ -284,21 +307,21 @@ class CWLWorkflowTest(ToilTest):
284
307
  out["output"].pop("nameroot", None)
285
308
  self.assertEqual(out, expect)
286
309
 
287
- def revsort(self, cwl_filename, tester_fn):
310
+ def revsort(self, cwl_filename: str, tester_fn: TesterFuncType) -> None:
288
311
  tester_fn(
289
312
  "src/toil/test/cwl/" + cwl_filename,
290
313
  "src/toil/test/cwl/revsort-job.json",
291
314
  self._expected_revsort_output(self.outDir),
292
315
  )
293
316
 
294
- def revsort_no_checksum(self, cwl_filename, tester_fn):
317
+ def revsort_no_checksum(self, cwl_filename: str, tester_fn: TesterFuncType) -> None:
295
318
  tester_fn(
296
319
  "src/toil/test/cwl/" + cwl_filename,
297
320
  "src/toil/test/cwl/revsort-job.json",
298
321
  self._expected_revsort_nochecksum_output(self.outDir),
299
322
  )
300
323
 
301
- def download(self, inputs, tester_fn):
324
+ def download(self, inputs: str, tester_fn: TesterFuncType) -> None:
302
325
  input_location = os.path.join("src/toil/test/cwl", inputs)
303
326
  tester_fn(
304
327
  "src/toil/test/cwl/download.cwl",
@@ -306,7 +329,7 @@ class CWLWorkflowTest(ToilTest):
306
329
  self._expected_download_output(self.outDir),
307
330
  )
308
331
 
309
- def load_contents(self, inputs, tester_fn):
332
+ def load_contents(self, inputs: str, tester_fn: TesterFuncType) -> None:
310
333
  input_location = os.path.join("src/toil/test/cwl", inputs)
311
334
  tester_fn(
312
335
  "src/toil/test/cwl/load_contents.cwl",
@@ -314,7 +337,7 @@ class CWLWorkflowTest(ToilTest):
314
337
  self._expected_load_contents_output(self.outDir),
315
338
  )
316
339
 
317
- def download_directory(self, inputs, tester_fn):
340
+ def download_directory(self, inputs: str, tester_fn: TesterFuncType) -> None:
318
341
  input_location = os.path.join("src/toil/test/cwl", inputs)
319
342
  tester_fn(
320
343
  "src/toil/test/cwl/download_directory.cwl",
@@ -322,7 +345,7 @@ class CWLWorkflowTest(ToilTest):
322
345
  self._expected_download_output(self.outDir),
323
346
  )
324
347
 
325
- def download_subdirectory(self, inputs, tester_fn):
348
+ def download_subdirectory(self, inputs: str, tester_fn: TesterFuncType) -> None:
326
349
  input_location = os.path.join("src/toil/test/cwl", inputs)
327
350
  tester_fn(
328
351
  "src/toil/test/cwl/download_subdirectory.cwl",
@@ -330,7 +353,7 @@ class CWLWorkflowTest(ToilTest):
330
353
  self._expected_download_output(self.outDir),
331
354
  )
332
355
 
333
- def test_mpi(self):
356
+ def test_mpi(self) -> None:
334
357
  from toil.cwl import cwltoil
335
358
 
336
359
  stdout = StringIO()
@@ -355,7 +378,7 @@ class CWLWorkflowTest(ToilTest):
355
378
  self.assertTrue(isinstance(two_pids[1], int))
356
379
 
357
380
  @needs_aws_s3
358
- def test_s3_as_secondary_file(self):
381
+ def test_s3_as_secondary_file(self) -> None:
359
382
  from toil.cwl import cwltoil
360
383
 
361
384
  stdout = StringIO()
@@ -374,21 +397,21 @@ class CWLWorkflowTest(ToilTest):
374
397
  with open(out["output"]["location"][len("file://") :]) as f:
375
398
  self.assertEqual(f.read().strip(), "When is s4 coming out?")
376
399
 
377
- def test_run_revsort(self):
400
+ def test_run_revsort(self) -> None:
378
401
  self.revsort("revsort.cwl", self._tester)
379
402
 
380
- def test_run_revsort_nochecksum(self):
403
+ def test_run_revsort_nochecksum(self) -> None:
381
404
  self.revsort_no_checksum(
382
405
  "revsort.cwl", partial(self._tester, main_args=["--no-compute-checksum"])
383
406
  )
384
407
 
385
- def test_run_revsort2(self):
408
+ def test_run_revsort2(self) -> None:
386
409
  self.revsort("revsort2.cwl", self._tester)
387
410
 
388
- def test_run_revsort_debug_worker(self):
411
+ def test_run_revsort_debug_worker(self) -> None:
389
412
  self.revsort("revsort.cwl", self._debug_worker_tester)
390
413
 
391
- def test_run_colon_output(self):
414
+ def test_run_colon_output(self) -> None:
392
415
  self._tester(
393
416
  "src/toil/test/cwl/colon_test_output.cwl",
394
417
  "src/toil/test/cwl/colon_test_output_job.yaml",
@@ -396,7 +419,7 @@ class CWLWorkflowTest(ToilTest):
396
419
  out_name="result",
397
420
  )
398
421
 
399
- def test_glob_dir_bypass_file_store(self):
422
+ def test_glob_dir_bypass_file_store(self) -> None:
400
423
  self.maxDiff = 1000
401
424
  try:
402
425
  # We need to output to the current directory to make sure that
@@ -416,58 +439,58 @@ class CWLWorkflowTest(ToilTest):
416
439
  pass
417
440
 
418
441
  @needs_aws_s3
419
- def test_download_s3(self):
442
+ def test_download_s3(self) -> None:
420
443
  self.download("download_s3.json", self._tester)
421
444
 
422
- def test_download_http(self):
445
+ def test_download_http(self) -> None:
423
446
  self.download("download_http.json", self._tester)
424
447
 
425
- def test_download_https(self):
448
+ def test_download_https(self) -> None:
426
449
  self.download("download_https.json", self._tester)
427
450
 
428
- def test_download_https_reference(self):
451
+ def test_download_https_reference(self) -> None:
429
452
  self.download("download_https.json", partial(self._tester, main_args=["--reference-inputs"]))
430
453
 
431
- def test_download_file(self):
454
+ def test_download_file(self) -> None:
432
455
  self.download("download_file.json", self._tester)
433
456
 
434
457
  @needs_aws_s3
435
- def test_download_directory_s3(self):
458
+ def test_download_directory_s3(self) -> None:
436
459
  self.download_directory("download_directory_s3.json", self._tester)
437
460
 
438
461
  @needs_aws_s3
439
- def test_download_directory_s3_reference(self):
462
+ def test_download_directory_s3_reference(self) -> None:
440
463
  self.download_directory("download_directory_s3.json", partial(self._tester, main_args=["--reference-inputs"]))
441
464
 
442
- def test_download_directory_file(self):
465
+ def test_download_directory_file(self) -> None:
443
466
  self.download_directory("download_directory_file.json", self._tester)
444
467
 
445
468
  @needs_aws_s3
446
- def test_download_subdirectory_s3(self):
469
+ def test_download_subdirectory_s3(self) -> None:
447
470
  self.download_subdirectory("download_subdirectory_s3.json", self._tester)
448
471
 
449
- def test_download_subdirectory_file(self):
472
+ def test_download_subdirectory_file(self) -> None:
450
473
  self.download_subdirectory("download_subdirectory_file.json", self._tester)
451
474
 
452
475
  # We also want to make sure we can run a bare tool with loadContents on the inputs, which requires accessing the input data early in the leader.
453
476
 
454
477
  @needs_aws_s3
455
- def test_load_contents_s3(self):
478
+ def test_load_contents_s3(self) -> None:
456
479
  self.load_contents("download_s3.json", self._tester)
457
480
 
458
- def test_load_contents_http(self):
481
+ def test_load_contents_http(self) -> None:
459
482
  self.load_contents("download_http.json", self._tester)
460
483
 
461
- def test_load_contents_https(self):
484
+ def test_load_contents_https(self) -> None:
462
485
  self.load_contents("download_https.json", self._tester)
463
486
 
464
- def test_load_contents_file(self):
487
+ def test_load_contents_file(self) -> None:
465
488
  self.load_contents("download_file.json", self._tester)
466
489
 
467
490
  @slow
468
491
  @pytest.mark.integrative
469
- @unittest.skip
470
- def test_bioconda(self):
492
+ @unittest.skip("Fails too often due to remote service")
493
+ def test_bioconda(self) -> None:
471
494
  self._tester(
472
495
  "src/toil/test/cwl/seqtk_seq.cwl",
473
496
  "src/toil/test/cwl/seqtk_seq_job.json",
@@ -476,10 +499,20 @@ class CWLWorkflowTest(ToilTest):
476
499
  out_name="output1",
477
500
  )
478
501
 
502
+ @needs_docker
503
+ def test_default_args(self) -> None:
504
+ self._tester(
505
+ "src/toil/test/cwl/seqtk_seq.cwl",
506
+ "src/toil/test/cwl/seqtk_seq_job.json",
507
+ self._expected_seqtk_output(self.outDir),
508
+ main_args=["--default-container", "quay.io/biocontainers/seqtk:r93--0"],
509
+ out_name="output1",
510
+ )
511
+
479
512
  @needs_docker
480
513
  @pytest.mark.integrative
481
- @unittest.skip
482
- def test_biocontainers(self):
514
+ @unittest.skip("Fails too often due to remote service")
515
+ def test_biocontainers(self) -> None:
483
516
  self._tester(
484
517
  "src/toil/test/cwl/seqtk_seq.cwl",
485
518
  "src/toil/test/cwl/seqtk_seq_job.json",
@@ -491,7 +524,7 @@ class CWLWorkflowTest(ToilTest):
491
524
  @needs_docker
492
525
  @needs_docker_cuda
493
526
  @needs_local_cuda
494
- def test_cuda(self):
527
+ def test_cuda(self) -> None:
495
528
  self._tester(
496
529
  "src/toil/test/cwl/nvidia_smi.cwl",
497
530
  "src/toil/test/cwl/empty.json",
@@ -500,7 +533,7 @@ class CWLWorkflowTest(ToilTest):
500
533
  )
501
534
 
502
535
  @slow
503
- def test_restart(self):
536
+ def test_restart(self) -> None:
504
537
  """
505
538
  Enable restarts with toil-cwl-runner -- run failing test, re-run correct test.
506
539
  Only implemented for single machine.
@@ -529,7 +562,7 @@ class CWLWorkflowTest(ToilTest):
529
562
  ][-1]
530
563
  os.symlink(os.path.join(cal_path, "date"), f'{os.path.join(outDir, "rev")}')
531
564
 
532
- def path_with_bogus_rev():
565
+ def path_with_bogus_rev() -> str:
533
566
  # append to the front of the PATH so that we check there first
534
567
  return f"{outDir}:" + os.environ["PATH"]
535
568
 
@@ -552,7 +585,7 @@ class CWLWorkflowTest(ToilTest):
552
585
  pass
553
586
 
554
587
  @needs_aws_s3
555
- def test_streamable(self, extra_args: List[str] = None):
588
+ def test_streamable(self, extra_args: Optional[List[str]] = None) -> None:
556
589
  """
557
590
  Test that a file with 'streamable'=True is a named pipe.
558
591
  This is a CWL1.2 feature.
@@ -585,13 +618,13 @@ class CWLWorkflowTest(ToilTest):
585
618
  self.assertEqual(f.read().strip(), "When is s4 coming out?")
586
619
 
587
620
  @needs_aws_s3
588
- def test_streamable_reference(self):
621
+ def test_streamable_reference(self) -> None:
589
622
  """
590
623
  Test that a streamable file is a stream even when passed around by URI.
591
624
  """
592
625
  self.test_streamable(extra_args=["--reference-inputs"])
593
626
 
594
- def test_preemptible(self):
627
+ def test_preemptible(self) -> None:
595
628
  """
596
629
  Tests that the http://arvados.org/cwl#UsePreemptible extension is supported.
597
630
  """
@@ -615,7 +648,7 @@ class CWLWorkflowTest(ToilTest):
615
648
  with open(out[out_name]["location"][len("file://") :]) as f:
616
649
  self.assertEqual(f.read().strip(), "hello")
617
650
 
618
- def test_preemptible_expression(self):
651
+ def test_preemptible_expression(self) -> None:
619
652
  """
620
653
  Tests that the http://arvados.org/cwl#UsePreemptible extension is validated.
621
654
  """
@@ -639,7 +672,7 @@ class CWLWorkflowTest(ToilTest):
639
672
 
640
673
 
641
674
  @staticmethod
642
- def _expected_seqtk_output(outDir):
675
+ def _expected_seqtk_output(outDir: str) -> "CWLObjectType":
643
676
  path = os.path.join(outDir, "out")
644
677
  loc = "file://" + path
645
678
  return {
@@ -654,7 +687,7 @@ class CWLWorkflowTest(ToilTest):
654
687
  }
655
688
 
656
689
  @staticmethod
657
- def _expected_revsort_output(outDir):
690
+ def _expected_revsort_output(outDir: str) -> "CWLObjectType":
658
691
  path = os.path.join(outDir, "output.txt")
659
692
  loc = "file://" + path
660
693
  return {
@@ -669,7 +702,7 @@ class CWLWorkflowTest(ToilTest):
669
702
  }
670
703
 
671
704
  @staticmethod
672
- def _expected_revsort_nochecksum_output(outDir):
705
+ def _expected_revsort_nochecksum_output(outDir: str) -> "CWLObjectType":
673
706
  path = os.path.join(outDir, "output.txt")
674
707
  loc = "file://" + path
675
708
  return {
@@ -683,7 +716,7 @@ class CWLWorkflowTest(ToilTest):
683
716
  }
684
717
 
685
718
  @staticmethod
686
- def _expected_download_output(outDir):
719
+ def _expected_download_output(outDir: str) -> "CWLObjectType":
687
720
  path = os.path.join(outDir, "output.txt")
688
721
  loc = "file://" + path
689
722
  return {
@@ -698,7 +731,7 @@ class CWLWorkflowTest(ToilTest):
698
731
  }
699
732
 
700
733
  @staticmethod
701
- def _expected_glob_dir_output(out_dir):
734
+ def _expected_glob_dir_output(out_dir: str) -> "CWLObjectType":
702
735
  dir_path = os.path.join(out_dir, "shouldmake")
703
736
  dir_loc = "file://" + dir_path
704
737
  file_path = os.path.join(dir_path, "test.txt")
@@ -727,7 +760,7 @@ class CWLWorkflowTest(ToilTest):
727
760
  }
728
761
 
729
762
  @classmethod
730
- def _expected_load_contents_output(cls, out_dir):
763
+ def _expected_load_contents_output(cls, out_dir: str) -> "CWLObjectType":
731
764
  """
732
765
  Generate the putput we expect from load_contents.cwl, when sending
733
766
  output files to the given directory.
@@ -737,7 +770,7 @@ class CWLWorkflowTest(ToilTest):
737
770
  return expected
738
771
 
739
772
  @staticmethod
740
- def _expected_colon_output(outDir):
773
+ def _expected_colon_output(outDir: str) -> "CWLObjectType":
741
774
  path = os.path.join(outDir, "A:Gln2Cys_result")
742
775
  loc = "file://" + os.path.join(outDir, "A%3AGln2Cys_result")
743
776
  return {
@@ -761,7 +794,7 @@ class CWLWorkflowTest(ToilTest):
761
794
  }
762
795
  }
763
796
 
764
- def _expected_streaming_output(self, outDir):
797
+ def _expected_streaming_output(self, outDir: str) -> "CWLObjectType":
765
798
  path = os.path.join(outDir, "output.txt")
766
799
  loc = "file://" + path
767
800
  return {
@@ -783,7 +816,7 @@ class CWLv10Test(ToilTest):
783
816
  Run the CWL 1.0 conformance tests in various environments.
784
817
  """
785
818
 
786
- def setUp(self):
819
+ def setUp(self) -> None:
787
820
  """Runs anew before each test to create farm fresh temp dirs."""
788
821
  self.outDir = f"/tmp/toil-cwl-test-{str(uuid.uuid4())}"
789
822
  os.makedirs(self.outDir)
@@ -804,7 +837,7 @@ class CWLv10Test(ToilTest):
804
837
  shutil.move("common-workflow-language-%s" % testhash, self.cwlSpec)
805
838
  os.remove("spec.zip")
806
839
 
807
- def tearDown(self):
840
+ def tearDown(self) -> None:
808
841
  """Clean up outputs."""
809
842
  if os.path.exists(self.outDir):
810
843
  shutil.rmtree(self.outDir)
@@ -812,99 +845,106 @@ class CWLv10Test(ToilTest):
812
845
 
813
846
  @slow
814
847
  @pytest.mark.timeout(CONFORMANCE_TEST_TIMEOUT)
815
- def test_run_conformance_with_caching(self):
848
+ def test_run_conformance_with_caching(self) -> None:
816
849
  self.test_run_conformance(caching=True)
817
850
 
818
851
  @slow
819
852
  @pytest.mark.timeout(CONFORMANCE_TEST_TIMEOUT)
820
853
  def test_run_conformance(
821
- self, batchSystem=None, caching=False, selected_tests=None
822
- ):
854
+ self,
855
+ batchSystem: Optional[str] = None,
856
+ caching: bool = False,
857
+ selected_tests: Optional[str] = None,
858
+ skipped_tests: Optional[str] = None,
859
+ extra_args: Optional[List[str]] = None,
860
+ ) -> None:
823
861
  run_conformance_tests(
824
862
  workDir=self.workDir,
825
863
  yml="conformance_test_v1.0.yaml",
826
864
  caching=caching,
827
865
  batchSystem=batchSystem,
828
866
  selected_tests=selected_tests,
867
+ skipped_tests=skipped_tests,
868
+ extra_args=extra_args,
829
869
  )
830
870
 
831
871
  @slow
832
872
  @needs_lsf
833
- @unittest.skip
834
- def test_lsf_cwl_conformance(self, **kwargs):
835
- return self.test_run_conformance(batchSystem="lsf", **kwargs)
873
+ @unittest.skip("Not run")
874
+ def test_lsf_cwl_conformance(self, caching: bool = False) -> None:
875
+ self.test_run_conformance(batchSystem="lsf", caching=caching)
836
876
 
837
877
  @slow
838
878
  @needs_slurm
839
- @unittest.skip
840
- def test_slurm_cwl_conformance(self, **kwargs):
841
- return self.test_run_conformance(batchSystem="slurm", **kwargs)
879
+ @unittest.skip("Not run")
880
+ def test_slurm_cwl_conformance(self, caching: bool = False) -> None:
881
+ self.test_run_conformance(batchSystem="slurm", caching=caching)
842
882
 
843
883
  @slow
844
884
  @needs_torque
845
- @unittest.skip
846
- def test_torque_cwl_conformance(self, **kwargs):
847
- return self.test_run_conformance(batchSystem="torque", **kwargs)
885
+ @unittest.skip("Not run")
886
+ def test_torque_cwl_conformance(self, caching: bool = False) -> None:
887
+ self.test_run_conformance(batchSystem="torque", caching=caching)
848
888
 
849
889
  @slow
850
890
  @needs_gridengine
851
- @unittest.skip
852
- def test_gridengine_cwl_conformance(self, **kwargs):
853
- return self.test_run_conformance(batchSystem="grid_engine", **kwargs)
891
+ @unittest.skip("Not run")
892
+ def test_gridengine_cwl_conformance(self, caching: bool = False) -> None:
893
+ self.test_run_conformance(batchSystem="grid_engine", caching=caching)
854
894
 
855
895
  @slow
856
896
  @needs_mesos
857
- @unittest.skip
858
- def test_mesos_cwl_conformance(self, **kwargs):
859
- return self.test_run_conformance(batchSystem="mesos", **kwargs)
897
+ @unittest.skip("Not run")
898
+ def test_mesos_cwl_conformance(self, caching: bool = False) -> None:
899
+ self.test_run_conformance(batchSystem="mesos", caching=caching)
860
900
 
861
901
  @slow
862
902
  @needs_kubernetes
863
- def test_kubernetes_cwl_conformance(self, **kwargs):
864
- return self.test_run_conformance(
903
+ def test_kubernetes_cwl_conformance(self, caching: bool = False) -> None:
904
+ self.test_run_conformance(
905
+ caching=caching,
865
906
  batchSystem="kubernetes",
866
907
  extra_args=["--retryCount=3"],
867
908
  # This test doesn't work with
868
909
  # Singularity; see
869
910
  # https://github.com/common-workflow-language/cwltool/blob/7094ede917c2d5b16d11f9231fe0c05260b51be6/conformance-test.sh#L99-L117
870
911
  skipped_tests="docker_entrypoint",
871
- **kwargs,
872
912
  )
873
913
 
874
914
  @slow
875
915
  @needs_lsf
876
- @unittest.skip
877
- def test_lsf_cwl_conformance_with_caching(self):
878
- return self.test_lsf_cwl_conformance(caching=True)
916
+ @unittest.skip("Not run")
917
+ def test_lsf_cwl_conformance_with_caching(self) -> None:
918
+ self.test_lsf_cwl_conformance(caching=True)
879
919
 
880
920
  @slow
881
921
  @needs_slurm
882
- @unittest.skip
883
- def test_slurm_cwl_conformance_with_caching(self):
884
- return self.test_slurm_cwl_conformance(caching=True)
922
+ @unittest.skip("Not run")
923
+ def test_slurm_cwl_conformance_with_caching(self) -> None:
924
+ self.test_slurm_cwl_conformance(caching=True)
885
925
 
886
926
  @slow
887
927
  @needs_torque
888
- @unittest.skip
889
- def test_torque_cwl_conformance_with_caching(self):
890
- return self.test_torque_cwl_conformance(caching=True)
928
+ @unittest.skip("Not run")
929
+ def test_torque_cwl_conformance_with_caching(self) -> None:
930
+ self.test_torque_cwl_conformance(caching=True)
891
931
 
892
932
  @slow
893
933
  @needs_gridengine
894
- @unittest.skip
895
- def test_gridengine_cwl_conformance_with_caching(self):
896
- return self.test_gridengine_cwl_conformance(caching=True)
934
+ @unittest.skip("Not run")
935
+ def test_gridengine_cwl_conformance_with_caching(self) -> None:
936
+ self.test_gridengine_cwl_conformance(caching=True)
897
937
 
898
938
  @slow
899
939
  @needs_mesos
900
- @unittest.skip
901
- def test_mesos_cwl_conformance_with_caching(self):
902
- return self.test_mesos_cwl_conformance(caching=True)
940
+ @unittest.skip("Not run")
941
+ def test_mesos_cwl_conformance_with_caching(self) -> None:
942
+ self.test_mesos_cwl_conformance(caching=True)
903
943
 
904
944
  @slow
905
945
  @needs_kubernetes
906
- def test_kubernetes_cwl_conformance_with_caching(self):
907
- return self.test_kubernetes_cwl_conformance(caching=True)
946
+ def test_kubernetes_cwl_conformance_with_caching(self) -> None:
947
+ self.test_kubernetes_cwl_conformance(caching=True)
908
948
 
909
949
 
910
950
  @needs_cwl
@@ -914,8 +954,12 @@ class CWLv11Test(ToilTest):
914
954
  Run the CWL 1.1 conformance tests in various environments.
915
955
  """
916
956
 
957
+ rootDir: str
958
+ cwlSpec: str
959
+ test_yaml: str
960
+
917
961
  @classmethod
918
- def setUpClass(cls):
962
+ def setUpClass(cls) -> None:
919
963
  """Runs anew before each test."""
920
964
  cls.rootDir = cls._projectRootPath()
921
965
  cls.cwlSpec = os.path.join(cls.rootDir, "src/toil/test/cwl/spec_v11")
@@ -929,37 +973,50 @@ class CWLv11Test(ToilTest):
929
973
  )
930
974
  p.communicate()
931
975
 
932
- def tearDown(self):
976
+ def tearDown(self) -> None:
933
977
  """Clean up outputs."""
934
978
  unittest.TestCase.tearDown(self)
935
979
 
936
980
  @slow
937
981
  @pytest.mark.timeout(CONFORMANCE_TEST_TIMEOUT)
938
- def test_run_conformance(self, **kwargs):
939
- run_conformance_tests(workDir=self.cwlSpec, yml=self.test_yaml, **kwargs)
982
+ def test_run_conformance(
983
+ self,
984
+ caching: bool = False,
985
+ batchSystem: Optional[str] = None,
986
+ skipped_tests: Optional[str] = None,
987
+ extra_args: Optional[List[str]] = None,
988
+ ) -> None:
989
+ run_conformance_tests(
990
+ workDir=self.cwlSpec,
991
+ yml=self.test_yaml,
992
+ caching=caching,
993
+ batchSystem=batchSystem,
994
+ skipped_tests=skipped_tests,
995
+ extra_args=extra_args,
996
+ )
940
997
 
941
998
  @slow
942
999
  @pytest.mark.timeout(CONFORMANCE_TEST_TIMEOUT)
943
- def test_run_conformance_with_caching(self):
1000
+ def test_run_conformance_with_caching(self) -> None:
944
1001
  self.test_run_conformance(caching=True)
945
1002
 
946
1003
  @slow
947
1004
  @needs_kubernetes
948
- def test_kubernetes_cwl_conformance(self, **kwargs):
949
- return self.test_run_conformance(
1005
+ def test_kubernetes_cwl_conformance(self, caching: bool = False) -> None:
1006
+ self.test_run_conformance(
950
1007
  batchSystem="kubernetes",
951
1008
  extra_args=["--retryCount=3"],
952
1009
  # These tests don't work with
953
1010
  # Singularity; see
954
1011
  # https://github.com/common-workflow-language/cwltool/blob/7094ede917c2d5b16d11f9231fe0c05260b51be6/conformance-test.sh#L99-L117
955
1012
  skipped_tests="docker_entrypoint,stdin_shorcut",
956
- **kwargs,
1013
+ caching=caching,
957
1014
  )
958
1015
 
959
1016
  @slow
960
1017
  @needs_kubernetes
961
- def test_kubernetes_cwl_conformance_with_caching(self):
962
- return self.test_kubernetes_cwl_conformance(caching=True)
1018
+ def test_kubernetes_cwl_conformance_with_caching(self) -> None:
1019
+ self.test_kubernetes_cwl_conformance(caching=True)
963
1020
 
964
1021
 
965
1022
  @needs_cwl
@@ -969,8 +1026,12 @@ class CWLv12Test(ToilTest):
969
1026
  Run the CWL 1.2 conformance tests in various environments.
970
1027
  """
971
1028
 
1029
+ rootDir: str
1030
+ cwlSpec: str
1031
+ test_yaml: str
1032
+
972
1033
  @classmethod
973
- def setUpClass(cls):
1034
+ def setUpClass(cls) -> None:
974
1035
  """Runs anew before each test."""
975
1036
  cls.rootDir = cls._projectRootPath()
976
1037
  cls.cwlSpec = os.path.join(cls.rootDir, "src/toil/test/cwl/spec_v12")
@@ -984,22 +1045,41 @@ class CWLv12Test(ToilTest):
984
1045
  )
985
1046
  p.communicate()
986
1047
 
987
- def tearDown(self):
1048
+ def tearDown(self) -> None:
988
1049
  """Clean up outputs."""
989
1050
  unittest.TestCase.tearDown(self)
990
1051
 
991
1052
  @slow
992
1053
  @pytest.mark.timeout(CONFORMANCE_TEST_TIMEOUT)
993
- def test_run_conformance(self, **kwargs):
994
- if "junit_file" not in kwargs:
995
- kwargs["junit_file"] = os.path.join(
996
- self.rootDir, "conformance-1.2.junit.xml"
997
- )
998
- run_conformance_tests(workDir=self.cwlSpec, yml=self.test_yaml, **kwargs)
1054
+ def test_run_conformance(
1055
+ self,
1056
+ runner: Optional[str] = None,
1057
+ caching: bool = False,
1058
+ batchSystem: Optional[str] = None,
1059
+ selected_tests: Optional[str] = None,
1060
+ skipped_tests: Optional[str] = None,
1061
+ extra_args: Optional[List[str]] = None,
1062
+ must_support_all_features: bool = False,
1063
+ junit_file: Optional[str] = None,
1064
+ ) -> None:
1065
+ if junit_file is None:
1066
+ junit_file = os.path.join(self.rootDir, "conformance-1.2.junit.xml")
1067
+ run_conformance_tests(
1068
+ workDir=self.cwlSpec,
1069
+ yml=self.test_yaml,
1070
+ runner=runner,
1071
+ caching=caching,
1072
+ batchSystem=batchSystem,
1073
+ selected_tests=selected_tests,
1074
+ skipped_tests=skipped_tests,
1075
+ extra_args=extra_args,
1076
+ must_support_all_features=must_support_all_features,
1077
+ junit_file=junit_file,
1078
+ )
999
1079
 
1000
1080
  @slow
1001
1081
  @pytest.mark.timeout(CONFORMANCE_TEST_TIMEOUT)
1002
- def test_run_conformance_with_caching(self):
1082
+ def test_run_conformance_with_caching(self) -> None:
1003
1083
  self.test_run_conformance(
1004
1084
  caching=True,
1005
1085
  junit_file = os.path.join(
@@ -1009,7 +1089,7 @@ class CWLv12Test(ToilTest):
1009
1089
 
1010
1090
  @slow
1011
1091
  @pytest.mark.timeout(CONFORMANCE_TEST_TIMEOUT)
1012
- def test_run_conformance_with_in_place_update(self):
1092
+ def test_run_conformance_with_in_place_update(self) -> None:
1013
1093
  """
1014
1094
  Make sure that with --bypass-file-store we properly support in place
1015
1095
  update on a single node, and that this doesn't break any other
@@ -1024,12 +1104,15 @@ class CWLv12Test(ToilTest):
1024
1104
 
1025
1105
  @slow
1026
1106
  @needs_kubernetes
1027
- def test_kubernetes_cwl_conformance(self, **kwargs):
1028
- if "junit_file" not in kwargs:
1029
- kwargs["junit_file"] = os.path.join(
1107
+ def test_kubernetes_cwl_conformance(
1108
+ self, caching: bool = False, junit_file: Optional[str] = None
1109
+ ) -> None:
1110
+ if junit_file is None:
1111
+ junit_file = os.path.join(
1030
1112
  self.rootDir, "kubernetes-conformance-1.2.junit.xml"
1031
1113
  )
1032
- return self.test_run_conformance(
1114
+ self.test_run_conformance(
1115
+ caching=caching,
1033
1116
  batchSystem="kubernetes",
1034
1117
  extra_args=["--retryCount=3"],
1035
1118
  # This test doesn't work with
@@ -1038,13 +1121,13 @@ class CWLv12Test(ToilTest):
1038
1121
  # and
1039
1122
  # https://github.com/common-workflow-language/cwltool/issues/1441#issuecomment-826747975
1040
1123
  skipped_tests="docker_entrypoint",
1041
- **kwargs,
1124
+ junit_file=junit_file,
1042
1125
  )
1043
1126
 
1044
1127
  @slow
1045
1128
  @needs_kubernetes
1046
- def test_kubernetes_cwl_conformance_with_caching(self):
1047
- return self.test_kubernetes_cwl_conformance(
1129
+ def test_kubernetes_cwl_conformance_with_caching(self) -> None:
1130
+ self.test_kubernetes_cwl_conformance(
1048
1131
  caching=True,
1049
1132
  junit_file=os.path.join(
1050
1133
  self.rootDir, "kubernetes-caching-conformance-1.2.junit.xml"
@@ -1053,7 +1136,7 @@ class CWLv12Test(ToilTest):
1053
1136
 
1054
1137
  @slow
1055
1138
  @needs_wes_server
1056
- def test_wes_server_cwl_conformance(self):
1139
+ def test_wes_server_cwl_conformance(self) -> None:
1057
1140
  """
1058
1141
  Run the CWL conformance tests via WES. TOIL_WES_ENDPOINT must be
1059
1142
  specified. If the WES server requires authentication, set TOIL_WES_USER
@@ -1078,7 +1161,7 @@ class CWLv12Test(ToilTest):
1078
1161
  # 1. `cwltool --print-deps` doesn't seem to include secondary files from the default
1079
1162
  # e.g.: https://github.com/common-workflow-language/cwl-v1.2/blob/1.2.1_proposed/tests/mixed-versions/wf-v10.cwl#L4-L10
1080
1163
 
1081
- return self.test_run_conformance(
1164
+ self.test_run_conformance(
1082
1165
  runner="toil-wes-cwl-runner",
1083
1166
  selected_tests="1-309,313-337",
1084
1167
  extra_args=extra_args,
@@ -1093,7 +1176,7 @@ class CWLOnARMTest(AbstractClusterTest):
1093
1176
  Run the CWL 1.2 conformance tests on ARM specifically.
1094
1177
  """
1095
1178
 
1096
- def __init__(self, methodName):
1179
+ def __init__(self, methodName: str) -> None:
1097
1180
  super().__init__(methodName=methodName)
1098
1181
  self.clusterName = "cwl-test-" + str(uuid.uuid4())
1099
1182
  self.leaderNodeType = "t4g.2xlarge"
@@ -1101,12 +1184,12 @@ class CWLOnARMTest(AbstractClusterTest):
1101
1184
  # We need to be running in a directory which Flatcar and the Toil Appliance both have
1102
1185
  self.cwl_test_dir = "/tmp/toil/cwlTests"
1103
1186
 
1104
- def setUp(self):
1187
+ def setUp(self) -> None:
1105
1188
  super().setUp()
1106
1189
  self.jobStore = f"aws:{self.awsRegion()}:cluster-{uuid.uuid4()}"
1107
1190
 
1108
1191
  @needs_env_var("CI_COMMIT_SHA", "a git commit sha")
1109
- def test_cwl_on_arm(self):
1192
+ def test_cwl_on_arm(self) -> None:
1110
1193
  # Make a cluster
1111
1194
  self.launchCluster()
1112
1195
  # get the leader so we know the IP address - we don't need to wait since create cluster
@@ -1167,7 +1250,7 @@ class CWLOnARMTest(AbstractClusterTest):
1167
1250
 
1168
1251
  @needs_cwl
1169
1252
  @pytest.mark.cwl_small_log_dir
1170
- def test_workflow_echo_string_scatter_stderr_log_dir(tmp_path: Path):
1253
+ def test_workflow_echo_string_scatter_stderr_log_dir(tmp_path: Path) -> None:
1171
1254
  log_dir = tmp_path / "cwl-logs"
1172
1255
  job_store = "test_workflow_echo_string_scatter_stderr_log_dir"
1173
1256
  toil = "toil-cwl-runner"
@@ -1274,7 +1357,7 @@ def test_log_dir_echo_stderr(tmp_path: Path) -> None:
1274
1357
 
1275
1358
  @needs_cwl
1276
1359
  @pytest.mark.cwl_small_log_dir
1277
- def test_filename_conflict_resolution(tmp_path: Path):
1360
+ def test_filename_conflict_resolution(tmp_path: Path) -> None:
1278
1361
  out_dir = tmp_path / "cwl-out-dir"
1279
1362
  toil = "toil-cwl-runner"
1280
1363
  options = [
@@ -1297,7 +1380,7 @@ def test_filename_conflict_resolution(tmp_path: Path):
1297
1380
  @needs_cwl
1298
1381
  @needs_docker
1299
1382
  @pytest.mark.cwl_small_log_dir
1300
- def test_filename_conflict_detection(tmp_path: Path):
1383
+ def test_filename_conflict_detection(tmp_path: Path) -> None:
1301
1384
  """
1302
1385
  Make sure we don't just stage files over each other when using a container.
1303
1386
  """
@@ -1319,7 +1402,7 @@ def test_filename_conflict_detection(tmp_path: Path):
1319
1402
  @needs_cwl
1320
1403
  @needs_docker
1321
1404
  @pytest.mark.cwl_small_log_dir
1322
- def test_filename_conflict_detection_at_root(tmp_path: Path):
1405
+ def test_filename_conflict_detection_at_root(tmp_path: Path) -> None:
1323
1406
  """
1324
1407
  Make sure we don't just stage files over each other.
1325
1408
 
@@ -1343,7 +1426,7 @@ def test_filename_conflict_detection_at_root(tmp_path: Path):
1343
1426
 
1344
1427
  @needs_cwl
1345
1428
  @pytest.mark.cwl_small
1346
- def test_pick_value_with_one_null_value(caplog):
1429
+ def test_pick_value_with_one_null_value(caplog: pytest.LogCaptureFixture) -> None:
1347
1430
  """
1348
1431
  Make sure toil-cwl-runner does not false log a warning when pickValue is
1349
1432
  used but outputSource only contains one null value. See: #3991.
@@ -1362,7 +1445,7 @@ def test_pick_value_with_one_null_value(caplog):
1362
1445
 
1363
1446
  @needs_cwl
1364
1447
  @pytest.mark.cwl_small
1365
- def test_workflow_echo_string():
1448
+ def test_workflow_echo_string() -> None:
1366
1449
  toil = "toil-cwl-runner"
1367
1450
  jobstore = f"--jobStore=file:explicit-local-jobstore-{uuid.uuid4()}"
1368
1451
  option_1 = "--strict-memory-limit"
@@ -1372,14 +1455,18 @@ def test_workflow_echo_string():
1372
1455
  cmd = [toil, jobstore, option_1, option_2, option_3, cwl]
1373
1456
  p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1374
1457
  stdout, stderr = p.communicate()
1375
- assert stdout.decode("utf-8").strip() == "{}", f"Got wrong output: {stdout}\nWith error: {stderr}"
1376
- assert b"Finished toil run successfully" in stderr
1458
+ stdout2 = stdout.decode("utf-8")
1459
+ stderr2 = stderr.decode("utf-8")
1460
+ assert (
1461
+ stdout2.strip() == "{}"
1462
+ ), f"Got wrong output: {stdout2}\nWith error: {stderr2}"
1463
+ assert "Finished toil run successfully" in stderr2
1377
1464
  assert p.returncode == 0
1378
1465
 
1379
1466
 
1380
1467
  @needs_cwl
1381
1468
  @pytest.mark.cwl_small
1382
- def test_workflow_echo_string_scatter_capture_stdout():
1469
+ def test_workflow_echo_string_scatter_capture_stdout() -> None:
1383
1470
  toil = "toil-cwl-runner"
1384
1471
  jobstore = f"--jobStore=file:explicit-local-jobstore-{uuid.uuid4()}"
1385
1472
  option_1 = "--strict-memory-limit"
@@ -1412,7 +1499,7 @@ def test_workflow_echo_string_scatter_capture_stdout():
1412
1499
 
1413
1500
  @needs_cwl
1414
1501
  @pytest.mark.cwl_small
1415
- def test_visit_top_cwl_class():
1502
+ def test_visit_top_cwl_class() -> None:
1416
1503
  structure = {
1417
1504
  "class": "Directory",
1418
1505
  "listing": [
@@ -1438,7 +1525,7 @@ def test_visit_top_cwl_class():
1438
1525
 
1439
1526
  counter = 0
1440
1527
 
1441
- def increment(thing: Dict) -> None:
1528
+ def increment(thing: "CWLObjectType") -> None:
1442
1529
  """
1443
1530
  Make sure we are at something CWL object like, and count it.
1444
1531
  """
@@ -1463,7 +1550,7 @@ def test_visit_top_cwl_class():
1463
1550
 
1464
1551
  @needs_cwl
1465
1552
  @pytest.mark.cwl_small
1466
- def test_visit_cwl_class_and_reduce():
1553
+ def test_visit_cwl_class_and_reduce() -> None:
1467
1554
  structure = {
1468
1555
  "class": "Directory",
1469
1556
  "listing": [
@@ -1489,7 +1576,7 @@ def test_visit_cwl_class_and_reduce():
1489
1576
 
1490
1577
  down_count = 0
1491
1578
 
1492
- def op_down(thing: MutableMapping) -> int:
1579
+ def op_down(thing: "CWLObjectType") -> int:
1493
1580
  """
1494
1581
  Grab the ID of the thing we are at, and count what we visit going
1495
1582
  down.
@@ -1501,7 +1588,7 @@ def test_visit_cwl_class_and_reduce():
1501
1588
  up_count = 0
1502
1589
  up_child_count = 0
1503
1590
 
1504
- def op_up(thing: MutableMapping, down_value: int, child_results: List[str]) -> str:
1591
+ def op_up(thing: "CWLObjectType", down_value: int, child_results: List[str]) -> str:
1505
1592
  """
1506
1593
  Check the down return value and the up return values, and count
1507
1594
  what we visit going up and what child relationships we have.
@@ -1524,7 +1611,7 @@ def test_visit_cwl_class_and_reduce():
1524
1611
 
1525
1612
  @needs_cwl
1526
1613
  @pytest.mark.cwl_small
1527
- def test_download_structure(tmp_path) -> None:
1614
+ def test_download_structure(tmp_path: Path) -> None:
1528
1615
  """
1529
1616
  Make sure that download_structure makes the right calls to what it thinks is the file store.
1530
1617
  """
@@ -1534,7 +1621,7 @@ def test_download_structure(tmp_path) -> None:
1534
1621
  fid2 = FileID("adifferentfile", 1000, True)
1535
1622
 
1536
1623
  # And what directory structure it would be in
1537
- structure = {
1624
+ structure: DirectoryStructure = {
1538
1625
  "dir1": {
1539
1626
  "dir2": {
1540
1627
  "f1": "toilfile:" + fid1.pack(),
@@ -1555,9 +1642,9 @@ def test_download_structure(tmp_path) -> None:
1555
1642
  # These will be populated.
1556
1643
  # TODO: This cache seems unused. Remove it?
1557
1644
  # This maps filesystem path to CWL URI
1558
- index = {}
1645
+ index: Dict[str, str] = {}
1559
1646
  # This maps CWL URI to filesystem path
1560
- existing = {}
1647
+ existing: Dict[str, str] = {}
1561
1648
 
1562
1649
  # Do the download
1563
1650
  download_structure(file_store, index, existing, structure, to_dir)
@@ -1573,11 +1660,16 @@ def test_download_structure(tmp_path) -> None:
1573
1660
  assert os.path.join(to_dir, "dir1/dir2/f1again") in index
1574
1661
  assert os.path.join(to_dir, "anotherfile") in index
1575
1662
  assert (
1576
- index[os.path.join(to_dir, "dir1/dir2/f1")] == structure["dir1"]["dir2"]["f1"]
1663
+ index[os.path.join(to_dir, "dir1/dir2/f1")]
1664
+ == cast(
1665
+ DirectoryStructure, cast(DirectoryStructure, structure["dir1"])["dir2"]
1666
+ )["f1"]
1577
1667
  )
1578
1668
  assert (
1579
1669
  index[os.path.join(to_dir, "dir1/dir2/f1again")]
1580
- == structure["dir1"]["dir2"]["f1again"]
1670
+ == cast(
1671
+ DirectoryStructure, cast(DirectoryStructure, structure["dir1"])["dir2"]
1672
+ )["f1again"]
1581
1673
  )
1582
1674
  assert index[os.path.join(to_dir, "anotherfile")] == structure["anotherfile"]
1583
1675