toil 8.2.0__py3-none-any.whl → 9.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 (99) hide show
  1. toil/batchSystems/abstractBatchSystem.py +13 -5
  2. toil/batchSystems/abstractGridEngineBatchSystem.py +17 -5
  3. toil/batchSystems/kubernetes.py +13 -2
  4. toil/batchSystems/mesos/batchSystem.py +33 -2
  5. toil/batchSystems/registry.py +15 -118
  6. toil/batchSystems/slurm.py +191 -16
  7. toil/common.py +20 -1
  8. toil/cwl/cwltoil.py +97 -119
  9. toil/cwl/utils.py +103 -3
  10. toil/fileStores/__init__.py +1 -1
  11. toil/fileStores/abstractFileStore.py +5 -2
  12. toil/fileStores/cachingFileStore.py +1 -1
  13. toil/job.py +30 -14
  14. toil/jobStores/abstractJobStore.py +35 -255
  15. toil/jobStores/aws/jobStore.py +864 -1964
  16. toil/jobStores/aws/utils.py +24 -270
  17. toil/jobStores/fileJobStore.py +2 -1
  18. toil/jobStores/googleJobStore.py +32 -13
  19. toil/jobStores/utils.py +0 -327
  20. toil/leader.py +27 -22
  21. toil/lib/accelerators.py +1 -1
  22. toil/lib/aws/config.py +22 -0
  23. toil/lib/aws/s3.py +477 -9
  24. toil/lib/aws/utils.py +22 -33
  25. toil/lib/checksum.py +88 -0
  26. toil/lib/conversions.py +33 -31
  27. toil/lib/directory.py +217 -0
  28. toil/lib/ec2.py +97 -29
  29. toil/lib/exceptions.py +2 -1
  30. toil/lib/expando.py +2 -2
  31. toil/lib/generatedEC2Lists.py +138 -19
  32. toil/lib/io.py +33 -2
  33. toil/lib/memoize.py +21 -7
  34. toil/lib/misc.py +1 -1
  35. toil/lib/pipes.py +385 -0
  36. toil/lib/plugins.py +106 -0
  37. toil/lib/retry.py +1 -1
  38. toil/lib/threading.py +1 -1
  39. toil/lib/url.py +320 -0
  40. toil/lib/web.py +4 -5
  41. toil/options/cwl.py +13 -1
  42. toil/options/runner.py +17 -10
  43. toil/options/wdl.py +12 -1
  44. toil/provisioners/__init__.py +5 -2
  45. toil/provisioners/aws/__init__.py +43 -36
  46. toil/provisioners/aws/awsProvisioner.py +47 -15
  47. toil/provisioners/node.py +60 -12
  48. toil/resource.py +3 -13
  49. toil/server/app.py +12 -6
  50. toil/server/cli/wes_cwl_runner.py +2 -2
  51. toil/server/wes/abstract_backend.py +21 -43
  52. toil/server/wes/toil_backend.py +2 -2
  53. toil/test/__init__.py +16 -18
  54. toil/test/batchSystems/batchSystemTest.py +2 -9
  55. toil/test/batchSystems/batch_system_plugin_test.py +7 -0
  56. toil/test/batchSystems/test_slurm.py +103 -14
  57. toil/test/cwl/cwlTest.py +181 -8
  58. toil/test/cwl/staging_cat.cwl +27 -0
  59. toil/test/cwl/staging_make_file.cwl +25 -0
  60. toil/test/cwl/staging_workflow.cwl +43 -0
  61. toil/test/cwl/zero_default.cwl +61 -0
  62. toil/test/docs/scripts/tutorial_staging.py +17 -8
  63. toil/test/docs/scriptsTest.py +2 -1
  64. toil/test/jobStores/jobStoreTest.py +23 -133
  65. toil/test/lib/aws/test_iam.py +7 -7
  66. toil/test/lib/aws/test_s3.py +30 -33
  67. toil/test/lib/aws/test_utils.py +9 -9
  68. toil/test/lib/test_url.py +69 -0
  69. toil/test/lib/url_plugin_test.py +105 -0
  70. toil/test/provisioners/aws/awsProvisionerTest.py +60 -7
  71. toil/test/provisioners/clusterTest.py +15 -2
  72. toil/test/provisioners/gceProvisionerTest.py +1 -1
  73. toil/test/server/serverTest.py +78 -36
  74. toil/test/src/autoDeploymentTest.py +2 -3
  75. toil/test/src/fileStoreTest.py +89 -87
  76. toil/test/utils/ABCWorkflowDebug/ABC.txt +1 -0
  77. toil/test/utils/ABCWorkflowDebug/debugWorkflow.py +4 -4
  78. toil/test/utils/toilKillTest.py +35 -28
  79. toil/test/wdl/md5sum/md5sum-gs.json +1 -1
  80. toil/test/wdl/md5sum/md5sum.json +1 -1
  81. toil/test/wdl/testfiles/read_file.wdl +18 -0
  82. toil/test/wdl/testfiles/url_to_optional_file.wdl +2 -1
  83. toil/test/wdl/wdltoil_test.py +171 -162
  84. toil/test/wdl/wdltoil_test_kubernetes.py +9 -0
  85. toil/utils/toilDebugFile.py +6 -3
  86. toil/utils/toilSshCluster.py +23 -0
  87. toil/utils/toilStats.py +17 -2
  88. toil/utils/toilUpdateEC2Instances.py +1 -0
  89. toil/version.py +10 -10
  90. toil/wdl/wdltoil.py +1179 -825
  91. toil/worker.py +16 -8
  92. {toil-8.2.0.dist-info → toil-9.1.0.dist-info}/METADATA +32 -32
  93. {toil-8.2.0.dist-info → toil-9.1.0.dist-info}/RECORD +97 -85
  94. {toil-8.2.0.dist-info → toil-9.1.0.dist-info}/WHEEL +1 -1
  95. toil/lib/iterables.py +0 -112
  96. toil/test/docs/scripts/stagingExampleFiles/in.txt +0 -1
  97. {toil-8.2.0.dist-info → toil-9.1.0.dist-info}/entry_points.txt +0 -0
  98. {toil-8.2.0.dist-info → toil-9.1.0.dist-info}/licenses/LICENSE +0 -0
  99. {toil-8.2.0.dist-info → toil-9.1.0.dist-info}/top_level.txt +0 -0
@@ -11,6 +11,7 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+ import datetime
14
15
  import logging
15
16
  import os
16
17
  import subprocess
@@ -24,13 +25,22 @@ from uuid import uuid4
24
25
 
25
26
  import pytest
26
27
 
28
+ import toil.lib.aws.session
29
+
30
+ from toil.lib.aws import zone_to_region
27
31
  from toil.provisioners import cluster_factory
28
32
  from toil.provisioners.aws.awsProvisioner import AWSProvisioner
33
+ from toil.provisioners.aws import (
34
+ _get_spot_history,
35
+ get_aws_zone_from_spot_market,
36
+ get_best_aws_zone,
37
+ )
29
38
  from toil.test import (
30
39
  ToilTest,
31
40
  get_data,
32
41
  integrative,
33
42
  needs_aws_ec2,
43
+ needs_aws_s3,
34
44
  needs_fetchable_appliance,
35
45
  needs_mesos,
36
46
  slow,
@@ -53,10 +63,35 @@ if TYPE_CHECKING:
53
63
 
54
64
  log = logging.getLogger(__name__)
55
65
 
66
+ @pytest.fixture
67
+ def aws_zone():
68
+ """
69
+ Supply an appropriate AWS zone to work in to tests that need one.
70
+ """
71
+ zone = get_best_aws_zone()
72
+ assert (
73
+ zone is not None
74
+ ), "Could not determine AWS availability zone to test in; is TOIL_AWS_ZONE set?"
75
+ return zone
76
+
77
+ @pytest.fixture
78
+ def aws_region(aws_zone):
79
+ """
80
+ Supply an appropriate AWS region to work in to tests that need one.
81
+ """
82
+ return zone_to_region(aws_zone)
83
+
84
+ @pytest.fixture
85
+ def ec2_client(aws_region):
86
+ """
87
+ Supply an AWS EC2 client tests that need one.
88
+ """
89
+ return toil.lib.aws.session.client("ec2", aws_region)
90
+
56
91
 
57
- class AWSProvisionerBenchTest(ToilTest):
92
+ class TestAWSProvisionerBenchTest:
58
93
  """
59
- Tests for the AWS provisioner that don't actually provision anything.
94
+ Tests for the AWS provisioner that don't actually provision instances.
60
95
  """
61
96
 
62
97
  # Needs to talk to EC2 for image discovery
@@ -71,7 +106,8 @@ class AWSProvisionerBenchTest(ToilTest):
71
106
  assert ami.startswith("ami-")
72
107
 
73
108
  @needs_aws_ec2
74
- def test_read_write_global_files(self):
109
+ @needs_aws_s3
110
+ def test_read_write_global_files(self, aws_zone):
75
111
  """
76
112
  Make sure the `_write_file_to_cloud()` and `_read_file_from_cloud()`
77
113
  functions of the AWS provisioner work as intended.
@@ -79,7 +115,7 @@ class AWSProvisionerBenchTest(ToilTest):
79
115
  provisioner = AWSProvisioner(
80
116
  f"aws-provisioner-test-{uuid4()}",
81
117
  "mesos",
82
- "us-west-2a",
118
+ aws_zone,
83
119
  50,
84
120
  None,
85
121
  None,
@@ -90,13 +126,30 @@ class AWSProvisionerBenchTest(ToilTest):
90
126
 
91
127
  try:
92
128
  url = provisioner._write_file_to_cloud(key, contents=contents)
93
- self.assertTrue(url.startswith("s3://"))
129
+ assert url.startswith("s3://")
94
130
 
95
- self.assertEqual(contents, provisioner._read_file_from_cloud(key))
131
+ assert provisioner._read_file_from_cloud(key) == contents
96
132
  finally:
97
133
  # the cluster was never launched, but we need to clean up the s3 bucket
98
134
  provisioner.destroyCluster()
99
135
 
136
+ @needs_aws_ec2
137
+ def test_get_spot_history(self, ec2_client) -> None:
138
+ """
139
+ Make sure that we can download spot price history from AWS.
140
+ """
141
+ history = _get_spot_history(ec2_client, "t3.large")
142
+ # We should have 7 days of history, newest first.
143
+
144
+ @needs_aws_ec2
145
+ def test_get_aws_zone_from_spot_market(self, ec2_client) -> None:
146
+ """
147
+ Make sure that we can process spot price history to pick a zone.
148
+ """
149
+ zone_options = ["us-west-2a", "af-south-1c"]
150
+ zone_choice = get_aws_zone_from_spot_market(0.01, "t3.large", ec2_client, zone_options)
151
+ assert zone_choice in zone_options
152
+
100
153
 
101
154
  @needs_aws_ec2
102
155
  @needs_fetchable_appliance
@@ -526,7 +579,7 @@ class AWSAutoscaleTestMultipleNodeTypes(AbstractAWSAutoscaleTest):
526
579
  runCommand = [
527
580
  self.python(),
528
581
  self.script(),
529
- "--fileToSort=/home/s3am/bin/asadmin",
582
+ "--fileToSort=/etc/passwd",
530
583
  "--sortMemory=0.6G",
531
584
  "--mergeMemory=3.0G",
532
585
  ]
@@ -31,6 +31,8 @@ from toil.test import (
31
31
  slow,
32
32
  )
33
33
 
34
+ from toil.test.cwl.cwlTest import TestCWLv12Conformance
35
+
34
36
  log = logging.getLogger(__name__)
35
37
 
36
38
 
@@ -237,6 +239,17 @@ class CWLOnARMTest(AbstractClusterTest):
237
239
 
238
240
  @needs_env_var("CI_COMMIT_SHA", "a git commit sha")
239
241
  def test_cwl_on_arm(self) -> None:
242
+ # Import the test we want to run remotely, so we know right away if it exists.
243
+ test_class = TestCWLv12Conformance
244
+ test_method = test_class.test_run_conformance
245
+
246
+ # Work out how to describe it as a pytest test spec.
247
+ # __qualname__ gives classname.methodname
248
+ test_name = test_method.__qualname__.replace(".", "::")
249
+ # The module path is the file path under src, with dots.
250
+ test_path = test_class.__module__.replace(".", "/")
251
+ test_spec = f"src/{test_path}.py::{test_name}"
252
+
240
253
  # Make a cluster
241
254
  self.launchCluster()
242
255
  # get the leader so we know the IP address - we don't need to wait since create cluster
@@ -276,12 +289,12 @@ class CWLOnARMTest(AbstractClusterTest):
276
289
  ]
277
290
  )
278
291
 
279
- # Runs the TestCWLv12 on an ARM instance
292
+ # Runs the test on an ARM instance
280
293
  self.sshUtil(
281
294
  [
282
295
  "bash",
283
296
  "-c",
284
- f". .{self.venvDir}/bin/activate && cd {self.cwl_test_dir}/toil && pytest --log-cli-level DEBUG -r s src/toil/test/cwl/cwlTest.py::TestCWLv12::test_run_conformance",
297
+ f". .{self.venvDir}/bin/activate && cd {self.cwl_test_dir}/toil && pytest --log-cli-level DEBUG -r s {test_spec}",
285
298
  ]
286
299
  )
287
300
 
@@ -341,7 +341,7 @@ class GCEAutoscaleTestMultipleNodeTypes(AbstractGCEAutoscaleTest):
341
341
  runCommand = [
342
342
  "/home/venv/bin/python",
343
343
  "/home/sort.py",
344
- "--fileToSort=/home/s3am/bin/asadmin",
344
+ "--fileToSort=/etc/passwd",
345
345
  "--sortMemory=0.6G",
346
346
  "--mergeMemory=3.0G",
347
347
  ]
@@ -33,7 +33,7 @@ except ImportError:
33
33
  # extra wasn't installed. We'll then skip them all.
34
34
  pass
35
35
 
36
- from toil.test import ToilTest, needs_aws_s3, needs_celery_broker, needs_server
36
+ from toil.test import ToilTest, needs_aws_s3, needs_celery_broker, needs_cwl, needs_server, integrative
37
37
 
38
38
  logger = logging.getLogger(__name__)
39
39
  logging.basicConfig(level=logging.INFO)
@@ -185,6 +185,7 @@ class FileStateStoreURLTest(hidden.AbstractStateStoreTest):
185
185
 
186
186
 
187
187
  @needs_aws_s3
188
+ @integrative
188
189
  class BucketUsingTest(ToilTest):
189
190
  """
190
191
  Base class for tests that need a bucket.
@@ -268,6 +269,35 @@ class AWSStateStoreTest(hidden.AbstractStateStoreTest, BucketUsingTest):
268
269
  self.assertEqual(obj.content_length, len("testvalue"))
269
270
 
270
271
 
272
+ # Problem: httpx (which the Connexion test client uses the API of)
273
+ # automatically decides whether to send posts in
274
+ # application/x-www-form-urlencoded or multipart/form-data format
275
+ # based on whether they are uploading any files. But the GA4GH WES
276
+ # API run workflow endpoint only accepts multipart/form-data. It takes
277
+ # workflow_attachment as an array of binary strings officially, but this is
278
+ # actually how Swagger takes multiple-file upload fields. See
279
+ # <https://swagger.io/docs/specification/v2_0/file-upload/#multiple-upload>.
280
+ # (httpx doesn't seem to support actually sending multiple files to one field
281
+ # either, but if we send just one file it ends up as the only value in
282
+ # Swagger's array.)
283
+ #
284
+ # The apparently-official workaround for forcing multipart encoding is to
285
+ # construct an empty dict-saped data structure that is truthy, and pass that as
286
+ # your file list. See
287
+ # <https://github.com/encode/httpx/discussions/2399#discussioncomment-3814186>
288
+ class TrueDict(dict):
289
+ """
290
+ Dict that is truthy even when empty.
291
+
292
+ Used as a workaround to set httpx post request encoding as recommended in
293
+ <https://github.com/encode/httpx/discussions/2399#discussioncomment-3814186>.
294
+ """
295
+ def __bool__(self) -> bool:
296
+ """
297
+ Always say the object is truthy.
298
+ """
299
+ return True
300
+
271
301
  @needs_server
272
302
  class AbstractToilWESServerTest(ToilTest):
273
303
  """
@@ -296,11 +326,12 @@ class AbstractToilWESServerTest(ToilTest):
296
326
  )
297
327
 
298
328
  # Make the FlaskApp
299
- server_app = create_app(args)
329
+ self.app = create_app(args)
300
330
 
301
- # Fish out the actual Flask
302
- self.app: Flask = server_app.app
303
- self.app.testing = True
331
+ # Neither <https://flask.palletsprojects.com/en/stable/testing/> nor
332
+ # <https://connexion.readthedocs.io/en/latest/testing.html#testing>
333
+ # suggests setting a testing flag on the Connexion app or its internal
334
+ # Flask app, so we don't.
304
335
 
305
336
  self.example_cwl = textwrap.dedent(
306
337
  """
@@ -345,7 +376,6 @@ class AbstractToilWESServerTest(ToilTest):
345
376
  """
346
377
  rv = client.get(f"/ga4gh/wes/v1/runs/{run_id}")
347
378
  self.assertEqual(rv.status_code, 200)
348
- self.assertTrue(rv.is_json)
349
379
  return rv
350
380
 
351
381
  def _check_successful_log(self, client: "FlaskClient", run_id: str) -> None:
@@ -354,15 +384,16 @@ class AbstractToilWESServerTest(ToilTest):
354
384
  The workflow should succeed, it should have some tasks, and they should have all succeeded.
355
385
  """
356
386
  rv = self._fetch_run_log(client, run_id)
387
+ rv_json = rv.json()
357
388
  logger.debug("Log info: %s", rv.json)
358
- run_log = rv.json.get("run_log")
389
+ run_log = rv_json.get("run_log")
359
390
  self.assertEqual(type(run_log), dict)
360
391
  if "exit_code" in run_log:
361
392
  # The workflow succeeded if it has an exit code
362
393
  self.assertEqual(run_log["exit_code"], 0)
363
394
  # The workflow is complete
364
- self.assertEqual(rv.json.get("state"), "COMPLETE")
365
- task_logs = rv.json.get("task_logs")
395
+ self.assertEqual(rv_json.get("state"), "COMPLETE")
396
+ task_logs = rv_json.get("task_logs")
366
397
  # There are tasks reported
367
398
  self.assertEqual(type(task_logs), list)
368
399
  self.assertGreater(len(task_logs), 0)
@@ -375,7 +406,7 @@ class AbstractToilWESServerTest(ToilTest):
375
406
  """Report the log for the given workflow run."""
376
407
  rv = self._fetch_run_log(client, run_id)
377
408
  logger.debug(f"Report log response: {rv.json}")
378
- run_log = rv.json.get("run_log")
409
+ run_log = rv.json().get("run_log")
379
410
  self.assertEqual(type(run_log), dict)
380
411
  self.assertEqual(type(run_log.get("stdout")), str)
381
412
  self.assertEqual(type(run_log.get("stderr")), str)
@@ -395,12 +426,13 @@ class AbstractToilWESServerTest(ToilTest):
395
426
  logger.info("Fetch %s", url)
396
427
  rv = client.get(url)
397
428
  self.assertEqual(rv.status_code, 200)
398
- logger.info("Got %s:\n%s", url, rv.data.decode("utf-8"))
429
+ logger.info("Got %s:\n%s", url, rv.content.decode("utf-8"))
399
430
 
400
431
  def _start_slow_workflow(self, client: "FlaskClient") -> str:
401
432
  """
402
433
  Start a slow workflow and return its ID.
403
434
  """
435
+
404
436
  rv = client.post(
405
437
  "/ga4gh/wes/v1/runs",
406
438
  data={
@@ -408,15 +440,18 @@ class AbstractToilWESServerTest(ToilTest):
408
440
  "workflow_type": "CWL",
409
441
  "workflow_type_version": "v1.0",
410
442
  "workflow_params": json.dumps({"delay": "5"}),
411
- "workflow_attachment": [
412
- (BytesIO(self.slow_cwl.encode()), "slow.cwl"),
413
- ],
443
+ },
444
+ files={
445
+ "workflow_attachment": (
446
+ "slow.cwl",
447
+ BytesIO(self.slow_cwl.encode()),
448
+ "application/octet-stream",
449
+ ),
414
450
  },
415
451
  )
416
452
  # workflow is submitted successfully
417
453
  self.assertEqual(rv.status_code, 200)
418
- self.assertTrue(rv.is_json)
419
- run_id = rv.json.get("run_id")
454
+ run_id = rv.json().get("run_id")
420
455
  self.assertIsNotNone(run_id)
421
456
 
422
457
  return run_id
@@ -428,11 +463,11 @@ class AbstractToilWESServerTest(ToilTest):
428
463
 
429
464
  rv = client.get(f"/ga4gh/wes/v1/runs/{run_id}/status")
430
465
  self.assertEqual(rv.status_code, 200)
431
- self.assertTrue(rv.is_json)
432
- self.assertIn("run_id", rv.json)
433
- self.assertEqual(rv.json.get("run_id"), run_id)
434
- self.assertIn("state", rv.json)
435
- state = rv.json.get("state")
466
+ rv_json = rv.json()
467
+ self.assertIn("run_id", rv_json)
468
+ self.assertEqual(rv_json.get("run_id"), run_id)
469
+ self.assertIn("state", rv_json)
470
+ state = rv_json.get("state")
436
471
  self.assertIn(
437
472
  state,
438
473
  [
@@ -492,7 +527,10 @@ class ToilWESServerBenchTest(AbstractToilWESServerTest):
492
527
  """Test the homepage endpoint."""
493
528
  with self.app.test_client() as client:
494
529
  rv = client.get("/")
495
- self.assertEqual(rv.status_code, 302)
530
+ # The client will follow the redirect and populate the url on the response
531
+ self.assertEqual(rv.url.path, "/ga4gh/wes/v1/service-info")
532
+ # We see the final 200 OK status code
533
+ self.assertEqual(rv.status_code, 200)
496
534
 
497
535
  def test_health(self) -> None:
498
536
  """Test the health check endpoint."""
@@ -505,7 +543,7 @@ class ToilWESServerBenchTest(AbstractToilWESServerTest):
505
543
  with self.app.test_client() as client:
506
544
  rv = client.get("/ga4gh/wes/v1/service-info")
507
545
  self.assertEqual(rv.status_code, 200)
508
- service_info = json.loads(rv.data)
546
+ service_info = rv.json()
509
547
 
510
548
  self.assertIn("version", service_info)
511
549
  self.assertIn("workflow_type_versions", service_info)
@@ -519,7 +557,7 @@ class ToilWESServerBenchTest(AbstractToilWESServerTest):
519
557
  self.assertIn("system_state_counts", service_info)
520
558
  self.assertIn("tags", service_info)
521
559
 
522
-
560
+ @needs_cwl
523
561
  class ToilWESServerWorkflowTest(AbstractToilWESServerTest):
524
562
  """
525
563
  Tests of the WES server running workflows.
@@ -548,11 +586,10 @@ class ToilWESServerWorkflowTest(AbstractToilWESServerTest):
548
586
  {"message": "Hello, world!"} if include_message else {}
549
587
  )
550
588
  with self.app.test_client() as client:
551
- rv = client.post("/ga4gh/wes/v1/runs", data=post_data)
589
+ rv = client.post("/ga4gh/wes/v1/runs", data=post_data, files=TrueDict())
552
590
  # workflow is submitted successfully
553
591
  self.assertEqual(rv.status_code, 200)
554
- self.assertTrue(rv.is_json)
555
- run_id = rv.json.get("run_id")
592
+ run_id = rv.json().get("run_id")
556
593
  self.assertIsNotNone(run_id)
557
594
 
558
595
  # Check status
@@ -571,11 +608,11 @@ class ToilWESServerWorkflowTest(AbstractToilWESServerTest):
571
608
  "workflow_type_version": "v1.0",
572
609
  "workflow_params": "{}",
573
610
  },
611
+ files=TrueDict(),
574
612
  )
575
613
  self.assertEqual(rv.status_code, 400)
576
- self.assertTrue(rv.is_json)
577
614
  self.assertEqual(
578
- rv.json.get("msg"),
615
+ rv.json().get("msg"),
579
616
  "Relative 'workflow_url' but missing 'workflow_attachment'",
580
617
  )
581
618
 
@@ -589,20 +626,24 @@ class ToilWESServerWorkflowTest(AbstractToilWESServerTest):
589
626
  "workflow_type": "CWL",
590
627
  "workflow_type_version": "v1.0",
591
628
  "workflow_params": json.dumps({"message": "Hello, world!"}),
592
- "workflow_attachment": [
593
- (BytesIO(self.example_cwl.encode()), "example.cwl"),
594
- ],
629
+ },
630
+ files={
631
+ "workflow_attachment": (
632
+ "example.cwl",
633
+ BytesIO(self.example_cwl.encode()),
634
+ "application/octet-stream",
635
+ ),
595
636
  },
596
637
  )
597
638
  # workflow is submitted successfully
598
639
  self.assertEqual(rv.status_code, 200)
599
- self.assertTrue(rv.is_json)
600
- run_id = rv.json.get("run_id")
640
+ run_id = rv.json().get("run_id")
601
641
  self.assertIsNotNone(run_id)
602
642
 
603
643
  # Check status
604
644
  self._wait_for_success(client, run_id)
605
645
 
646
+ @integrative
606
647
  def test_run_workflow_https_url(self) -> None:
607
648
  """Test run example CWL workflow from the Internet."""
608
649
  with self.app.test_client() as client:
@@ -614,11 +655,11 @@ class ToilWESServerWorkflowTest(AbstractToilWESServerTest):
614
655
  "workflow_type_version": "v1.2",
615
656
  "workflow_params": json.dumps({"message": "Hello, world!"}),
616
657
  },
658
+ files=TrueDict(),
617
659
  )
618
660
  # workflow is submitted successfully
619
661
  self.assertEqual(rv.status_code, 200)
620
- self.assertTrue(rv.is_json)
621
- run_id = rv.json.get("run_id")
662
+ run_id = rv.json().get("run_id")
622
663
  self.assertIsNotNone(run_id)
623
664
 
624
665
  # Check status
@@ -730,6 +771,7 @@ class ToilWESServerWorkflowTest(AbstractToilWESServerTest):
730
771
 
731
772
 
732
773
  @needs_celery_broker
774
+ @integrative
733
775
  class ToilWESServerCeleryWorkflowTest(ToilWESServerWorkflowTest):
734
776
  """
735
777
  End-to-end workflow-running tests against Celery.
@@ -4,7 +4,6 @@ import time
4
4
  from contextlib import contextmanager
5
5
 
6
6
  from toil.exceptions import FailedJobsException
7
- from toil.lib.iterables import concat
8
7
  from toil.test import ApplianceTestSupport, needs_local_appliance, needs_mesos, slow
9
8
  from toil.version import exactPython
10
9
 
@@ -85,7 +84,7 @@ class AutoDeploymentTest(ApplianceTestSupport):
85
84
  "--defaultMemory=10M",
86
85
  "/data/jobstore",
87
86
  ]
88
- command = concat(pythonArgs, toilArgs)
87
+ command = pythonArgs + toilArgs
89
88
  self.assertRaises(
90
89
  subprocess.CalledProcessError, leader.runOnAppliance, *command
91
90
  )
@@ -96,7 +95,7 @@ class AutoDeploymentTest(ApplianceTestSupport):
96
95
  path=self.sitePackages, packagePath="foo.bar", script=userScript
97
96
  )
98
97
  # ... and restart Toil.
99
- command = concat(pythonArgs, "--restart", toilArgs)
98
+ command = pythonArgs + ["--restart"] + toilArgs
100
99
  leader.runOnAppliance(*command)
101
100
 
102
101
  def testSplitRootPackages(self):