toil 6.1.0a1__py3-none-any.whl → 8.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- toil/__init__.py +122 -315
- toil/batchSystems/__init__.py +1 -0
- toil/batchSystems/abstractBatchSystem.py +173 -89
- toil/batchSystems/abstractGridEngineBatchSystem.py +272 -148
- toil/batchSystems/awsBatch.py +244 -135
- toil/batchSystems/cleanup_support.py +26 -16
- toil/batchSystems/contained_executor.py +31 -28
- toil/batchSystems/gridengine.py +86 -50
- toil/batchSystems/htcondor.py +166 -89
- toil/batchSystems/kubernetes.py +632 -382
- toil/batchSystems/local_support.py +20 -15
- toil/batchSystems/lsf.py +134 -81
- toil/batchSystems/lsfHelper.py +13 -11
- toil/batchSystems/mesos/__init__.py +41 -29
- toil/batchSystems/mesos/batchSystem.py +290 -151
- toil/batchSystems/mesos/executor.py +79 -50
- toil/batchSystems/mesos/test/__init__.py +31 -23
- toil/batchSystems/options.py +46 -28
- toil/batchSystems/registry.py +53 -19
- toil/batchSystems/singleMachine.py +296 -125
- toil/batchSystems/slurm.py +603 -138
- toil/batchSystems/torque.py +47 -33
- toil/bus.py +186 -76
- toil/common.py +664 -368
- toil/cwl/__init__.py +1 -1
- toil/cwl/cwltoil.py +1136 -483
- toil/cwl/utils.py +17 -22
- toil/deferred.py +63 -42
- toil/exceptions.py +5 -3
- toil/fileStores/__init__.py +5 -5
- toil/fileStores/abstractFileStore.py +140 -60
- toil/fileStores/cachingFileStore.py +717 -269
- toil/fileStores/nonCachingFileStore.py +116 -87
- toil/job.py +1225 -368
- toil/jobStores/abstractJobStore.py +416 -266
- toil/jobStores/aws/jobStore.py +863 -477
- toil/jobStores/aws/utils.py +201 -120
- toil/jobStores/conftest.py +3 -2
- toil/jobStores/fileJobStore.py +292 -154
- toil/jobStores/googleJobStore.py +140 -74
- toil/jobStores/utils.py +36 -15
- toil/leader.py +668 -272
- toil/lib/accelerators.py +115 -18
- toil/lib/aws/__init__.py +74 -31
- toil/lib/aws/ami.py +122 -87
- toil/lib/aws/iam.py +284 -108
- toil/lib/aws/s3.py +31 -0
- toil/lib/aws/session.py +214 -39
- toil/lib/aws/utils.py +287 -231
- toil/lib/bioio.py +13 -5
- toil/lib/compatibility.py +11 -6
- toil/lib/conversions.py +104 -47
- toil/lib/docker.py +131 -103
- toil/lib/ec2.py +361 -199
- toil/lib/ec2nodes.py +174 -106
- toil/lib/encryption/_dummy.py +5 -3
- toil/lib/encryption/_nacl.py +10 -6
- toil/lib/encryption/conftest.py +1 -0
- toil/lib/exceptions.py +26 -7
- toil/lib/expando.py +5 -3
- toil/lib/ftp_utils.py +217 -0
- toil/lib/generatedEC2Lists.py +127 -19
- toil/lib/humanize.py +6 -2
- toil/lib/integration.py +341 -0
- toil/lib/io.py +141 -15
- toil/lib/iterables.py +4 -2
- toil/lib/memoize.py +12 -8
- toil/lib/misc.py +66 -21
- toil/lib/objects.py +2 -2
- toil/lib/resources.py +68 -15
- toil/lib/retry.py +126 -81
- toil/lib/threading.py +299 -82
- toil/lib/throttle.py +16 -15
- toil/options/common.py +843 -409
- toil/options/cwl.py +175 -90
- toil/options/runner.py +50 -0
- toil/options/wdl.py +73 -17
- toil/provisioners/__init__.py +117 -46
- toil/provisioners/abstractProvisioner.py +332 -157
- toil/provisioners/aws/__init__.py +70 -33
- toil/provisioners/aws/awsProvisioner.py +1145 -715
- toil/provisioners/clusterScaler.py +541 -279
- toil/provisioners/gceProvisioner.py +282 -179
- toil/provisioners/node.py +155 -79
- toil/realtimeLogger.py +34 -22
- toil/resource.py +137 -75
- toil/server/app.py +128 -62
- toil/server/celery_app.py +3 -1
- toil/server/cli/wes_cwl_runner.py +82 -53
- toil/server/utils.py +54 -28
- toil/server/wes/abstract_backend.py +64 -26
- toil/server/wes/amazon_wes_utils.py +21 -15
- toil/server/wes/tasks.py +121 -63
- toil/server/wes/toil_backend.py +142 -107
- toil/server/wsgi_app.py +4 -3
- toil/serviceManager.py +58 -22
- toil/statsAndLogging.py +224 -70
- toil/test/__init__.py +282 -183
- toil/test/batchSystems/batchSystemTest.py +460 -210
- toil/test/batchSystems/batch_system_plugin_test.py +90 -0
- toil/test/batchSystems/test_gridengine.py +173 -0
- toil/test/batchSystems/test_lsf_helper.py +67 -58
- toil/test/batchSystems/test_slurm.py +110 -49
- toil/test/cactus/__init__.py +0 -0
- toil/test/cactus/test_cactus_integration.py +56 -0
- toil/test/cwl/cwlTest.py +496 -287
- toil/test/cwl/measure_default_memory.cwl +12 -0
- toil/test/cwl/not_run_required_input.cwl +29 -0
- toil/test/cwl/scatter_duplicate_outputs.cwl +40 -0
- toil/test/cwl/seqtk_seq.cwl +1 -1
- toil/test/docs/scriptsTest.py +69 -46
- toil/test/jobStores/jobStoreTest.py +427 -264
- toil/test/lib/aws/test_iam.py +118 -50
- toil/test/lib/aws/test_s3.py +16 -9
- toil/test/lib/aws/test_utils.py +5 -6
- toil/test/lib/dockerTest.py +118 -141
- toil/test/lib/test_conversions.py +113 -115
- toil/test/lib/test_ec2.py +58 -50
- toil/test/lib/test_integration.py +104 -0
- toil/test/lib/test_misc.py +12 -5
- toil/test/mesos/MesosDataStructuresTest.py +23 -10
- toil/test/mesos/helloWorld.py +7 -6
- toil/test/mesos/stress.py +25 -20
- toil/test/options/__init__.py +13 -0
- toil/test/options/options.py +42 -0
- toil/test/provisioners/aws/awsProvisionerTest.py +320 -150
- toil/test/provisioners/clusterScalerTest.py +440 -250
- toil/test/provisioners/clusterTest.py +166 -44
- toil/test/provisioners/gceProvisionerTest.py +174 -100
- toil/test/provisioners/provisionerTest.py +25 -13
- toil/test/provisioners/restartScript.py +5 -4
- toil/test/server/serverTest.py +188 -141
- toil/test/sort/restart_sort.py +137 -68
- toil/test/sort/sort.py +134 -66
- toil/test/sort/sortTest.py +91 -49
- toil/test/src/autoDeploymentTest.py +141 -101
- toil/test/src/busTest.py +20 -18
- toil/test/src/checkpointTest.py +8 -2
- toil/test/src/deferredFunctionTest.py +49 -35
- toil/test/src/dockerCheckTest.py +32 -24
- toil/test/src/environmentTest.py +135 -0
- toil/test/src/fileStoreTest.py +539 -272
- toil/test/src/helloWorldTest.py +7 -4
- toil/test/src/importExportFileTest.py +61 -31
- toil/test/src/jobDescriptionTest.py +46 -21
- toil/test/src/jobEncapsulationTest.py +2 -0
- toil/test/src/jobFileStoreTest.py +74 -50
- toil/test/src/jobServiceTest.py +187 -73
- toil/test/src/jobTest.py +121 -71
- toil/test/src/miscTests.py +19 -18
- toil/test/src/promisedRequirementTest.py +82 -36
- toil/test/src/promisesTest.py +7 -6
- toil/test/src/realtimeLoggerTest.py +10 -6
- toil/test/src/regularLogTest.py +71 -37
- toil/test/src/resourceTest.py +80 -49
- toil/test/src/restartDAGTest.py +36 -22
- toil/test/src/resumabilityTest.py +9 -2
- toil/test/src/retainTempDirTest.py +45 -14
- toil/test/src/systemTest.py +12 -8
- toil/test/src/threadingTest.py +44 -25
- toil/test/src/toilContextManagerTest.py +10 -7
- toil/test/src/userDefinedJobArgTypeTest.py +8 -5
- toil/test/src/workerTest.py +73 -23
- toil/test/utils/toilDebugTest.py +103 -33
- toil/test/utils/toilKillTest.py +4 -5
- toil/test/utils/utilsTest.py +245 -106
- toil/test/wdl/wdltoil_test.py +818 -149
- toil/test/wdl/wdltoil_test_kubernetes.py +91 -0
- toil/toilState.py +120 -35
- toil/utils/toilConfig.py +13 -4
- toil/utils/toilDebugFile.py +44 -27
- toil/utils/toilDebugJob.py +214 -27
- toil/utils/toilDestroyCluster.py +11 -6
- toil/utils/toilKill.py +8 -3
- toil/utils/toilLaunchCluster.py +256 -140
- toil/utils/toilMain.py +37 -16
- toil/utils/toilRsyncCluster.py +32 -14
- toil/utils/toilSshCluster.py +49 -22
- toil/utils/toilStats.py +356 -273
- toil/utils/toilStatus.py +292 -139
- toil/utils/toilUpdateEC2Instances.py +3 -1
- toil/version.py +12 -12
- toil/wdl/utils.py +5 -5
- toil/wdl/wdltoil.py +3913 -1033
- toil/worker.py +367 -184
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/LICENSE +25 -0
- toil-8.0.0.dist-info/METADATA +173 -0
- toil-8.0.0.dist-info/RECORD +253 -0
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/WHEEL +1 -1
- toil-6.1.0a1.dist-info/METADATA +0 -125
- toil-6.1.0a1.dist-info/RECORD +0 -237
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/entry_points.txt +0 -0
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# This workflow fills in a required int from an optional int, but only when the
|
|
2
|
+
# int is really present. But it also uses the value to compute the conditional
|
|
3
|
+
# task's resource requirements, so Toil can't just schedule the task and then
|
|
4
|
+
# check the condition.
|
|
5
|
+
# See <https://github.com/DataBiosphere/toil/issues/4930#issue-2297563321>
|
|
6
|
+
cwlVersion: v1.2
|
|
7
|
+
class: Workflow
|
|
8
|
+
requirements:
|
|
9
|
+
InlineJavascriptRequirement: {}
|
|
10
|
+
inputs:
|
|
11
|
+
optional_input: int?
|
|
12
|
+
steps:
|
|
13
|
+
the_step:
|
|
14
|
+
in:
|
|
15
|
+
required_input:
|
|
16
|
+
source: optional_input
|
|
17
|
+
when: $(inputs.required_input != null)
|
|
18
|
+
run:
|
|
19
|
+
cwlVersion: v1.2
|
|
20
|
+
class: CommandLineTool
|
|
21
|
+
inputs:
|
|
22
|
+
required_input: int
|
|
23
|
+
requirements:
|
|
24
|
+
ResourceRequirement:
|
|
25
|
+
coresMax: $(inputs.required_input)
|
|
26
|
+
baseCommand: "nproc"
|
|
27
|
+
outputs: []
|
|
28
|
+
out: []
|
|
29
|
+
outputs: []
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
cwlVersion: v1.2
|
|
2
|
+
class: Workflow
|
|
3
|
+
id: scatter-duplicate-outputs
|
|
4
|
+
|
|
5
|
+
inputs:
|
|
6
|
+
- id: toScatterOver
|
|
7
|
+
type: string[]
|
|
8
|
+
default: ['this','is','an','array','of','more','than','two','things']
|
|
9
|
+
|
|
10
|
+
steps:
|
|
11
|
+
echo:
|
|
12
|
+
run:
|
|
13
|
+
id: scatter
|
|
14
|
+
class: CommandLineTool
|
|
15
|
+
inputs:
|
|
16
|
+
s: string
|
|
17
|
+
baseCommand: [echo]
|
|
18
|
+
arguments: [ $(inputs.s)]
|
|
19
|
+
stdout: scatter.out
|
|
20
|
+
stderr: scatter.err
|
|
21
|
+
outputs:
|
|
22
|
+
out: stdout
|
|
23
|
+
err: stderr
|
|
24
|
+
in:
|
|
25
|
+
- id: s
|
|
26
|
+
source: toScatterOver
|
|
27
|
+
scatter:
|
|
28
|
+
- s
|
|
29
|
+
out:
|
|
30
|
+
- id: out
|
|
31
|
+
- id: err
|
|
32
|
+
|
|
33
|
+
outputs:
|
|
34
|
+
- id: echo_out
|
|
35
|
+
type: File[]
|
|
36
|
+
outputSource: ["echo/out"]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
requirements:
|
|
40
|
+
- class: ScatterFeatureRequirement
|
toil/test/cwl/seqtk_seq.cwl
CHANGED
toil/test/docs/scriptsTest.py
CHANGED
|
@@ -5,15 +5,18 @@ import subprocess
|
|
|
5
5
|
import sys
|
|
6
6
|
import unittest
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
pkg_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) # noqa
|
|
9
11
|
sys.path.insert(0, pkg_root) # noqa
|
|
10
12
|
|
|
11
|
-
from toil.test import ToilTest,
|
|
13
|
+
from toil.test import ToilTest, needs_docker
|
|
12
14
|
from toil.version import python
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
class ToilDocumentationTest(ToilTest):
|
|
16
18
|
"""Tests for scripts in the toil tutorials."""
|
|
19
|
+
|
|
17
20
|
@classmethod
|
|
18
21
|
def setUpClass(cls):
|
|
19
22
|
super(ToilTest, cls).setUpClass()
|
|
@@ -21,14 +24,8 @@ class ToilDocumentationTest(ToilTest):
|
|
|
21
24
|
|
|
22
25
|
def tearDown(self) -> None:
|
|
23
26
|
super(ToilTest, self).tearDown()
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
for output in output_files:
|
|
27
|
-
output_file = os.path.join(self.directory, 'scripts/cwlExampleFiles', output)
|
|
28
|
-
if os.path.exists(output_file):
|
|
29
|
-
os.remove(output_file)
|
|
30
|
-
|
|
31
|
-
jobstores = ['/mnt/ephemeral/workspace/toil-pull-requests/toilWorkflowRun']
|
|
27
|
+
|
|
28
|
+
jobstores = ["/mnt/ephemeral/workspace/toil-pull-requests/toilWorkflowRun"]
|
|
32
29
|
for jobstore in jobstores:
|
|
33
30
|
if os.path.exists(jobstore):
|
|
34
31
|
shutil.rmtree(jobstore)
|
|
@@ -36,19 +33,24 @@ class ToilDocumentationTest(ToilTest):
|
|
|
36
33
|
unittest.TestCase.tearDown(self)
|
|
37
34
|
|
|
38
35
|
"""Just check the exit code"""
|
|
39
|
-
|
|
36
|
+
|
|
37
|
+
def checkExitCode(self, script, extra_args: list[str] = []):
|
|
40
38
|
program = os.path.join(self.directory, "scripts", script)
|
|
41
|
-
process = subprocess.Popen(
|
|
42
|
-
|
|
39
|
+
process = subprocess.Popen(
|
|
40
|
+
[python, program, "file:my-jobstore", "--clean=always"] + extra_args,
|
|
41
|
+
stdout=subprocess.PIPE,
|
|
42
|
+
stderr=subprocess.PIPE,
|
|
43
|
+
)
|
|
43
44
|
stdout, stderr = process.communicate()
|
|
44
45
|
if isinstance(stdout, bytes):
|
|
45
|
-
stdout = stdout.decode(
|
|
46
|
-
stderr = stderr.decode(
|
|
46
|
+
stdout = stdout.decode("utf-8")
|
|
47
|
+
stderr = stderr.decode("utf-8")
|
|
47
48
|
if not process.returncode == 0:
|
|
48
49
|
raise RuntimeError(stderr)
|
|
49
|
-
return stdout +
|
|
50
|
+
return stdout + " " + stderr
|
|
50
51
|
|
|
51
52
|
"""Check the exit code and the output"""
|
|
53
|
+
|
|
52
54
|
def checkExpectedOut(self, script, expectedOutput):
|
|
53
55
|
outerr = self.checkExitCode(script)
|
|
54
56
|
|
|
@@ -57,6 +59,7 @@ class ToilDocumentationTest(ToilTest):
|
|
|
57
59
|
self.assertGreater(index, -1, f"Expected:\n{expectedOutput}\nOutput:\n{outerr}")
|
|
58
60
|
|
|
59
61
|
"""Check the exit code and look for a pattern"""
|
|
62
|
+
|
|
60
63
|
def checkExpectedPattern(self, script, expectedPattern):
|
|
61
64
|
outerr = self.checkExitCode(script)
|
|
62
65
|
|
|
@@ -65,13 +68,11 @@ class ToilDocumentationTest(ToilTest):
|
|
|
65
68
|
n = re.search(pattern, outerr)
|
|
66
69
|
self.assertNotEqual(n, None, f"Pattern:\n{expectedPattern}\nOutput:\n{outerr}")
|
|
67
70
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
self.checkExitCode("
|
|
71
|
-
|
|
72
|
-
def testDiscoverfiles(self):
|
|
73
|
-
self.checkExitCode("tutorial_discoverfiles.py")
|
|
71
|
+
def testStats(self):
|
|
72
|
+
# This script asks for 4 cores but we might need to run the tests in only 3.
|
|
73
|
+
self.checkExitCode("tutorial_stats.py", ["--scale=0.5"])
|
|
74
74
|
|
|
75
|
+
@pytest.mark.timeout(1200)
|
|
75
76
|
def testDynamic(self):
|
|
76
77
|
self.checkExitCode("tutorial_dynamic.py")
|
|
77
78
|
|
|
@@ -82,16 +83,24 @@ class ToilDocumentationTest(ToilTest):
|
|
|
82
83
|
self.checkExitCode("tutorial_encapsulation2.py")
|
|
83
84
|
|
|
84
85
|
def testHelloworld(self):
|
|
85
|
-
self.checkExpectedOut(
|
|
86
|
+
self.checkExpectedOut(
|
|
87
|
+
"tutorial_helloworld.py", "Hello, world!, here's a message: You did it!\n"
|
|
88
|
+
)
|
|
86
89
|
|
|
87
90
|
def testInvokeworkflow(self):
|
|
88
|
-
self.checkExpectedOut(
|
|
91
|
+
self.checkExpectedOut(
|
|
92
|
+
"tutorial_invokeworkflow.py", "Hello, world!, here's a message: Woot\n"
|
|
93
|
+
)
|
|
89
94
|
|
|
90
95
|
def testInvokeworkflow2(self):
|
|
91
|
-
self.checkExpectedOut(
|
|
96
|
+
self.checkExpectedOut(
|
|
97
|
+
"tutorial_invokeworkflow2.py", "Hello, world!, I have a message: Woot!\n"
|
|
98
|
+
)
|
|
92
99
|
|
|
93
100
|
def testJobFunctions(self):
|
|
94
|
-
self.checkExpectedOut(
|
|
101
|
+
self.checkExpectedOut(
|
|
102
|
+
"tutorial_jobfunctions.py", "Hello world, I have a message: Woot!\n"
|
|
103
|
+
)
|
|
95
104
|
|
|
96
105
|
def testManaging(self):
|
|
97
106
|
self.checkExitCode("tutorial_managing.py")
|
|
@@ -100,38 +109,51 @@ class ToilDocumentationTest(ToilTest):
|
|
|
100
109
|
self.checkExitCode("tutorial_managing2.py")
|
|
101
110
|
|
|
102
111
|
def testMultiplejobs(self):
|
|
103
|
-
self.checkExpectedPattern(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
112
|
+
self.checkExpectedPattern(
|
|
113
|
+
"tutorial_multiplejobs.py",
|
|
114
|
+
"Hello world, I have a message: first.*Hello world, I have a message: "
|
|
115
|
+
"second or third.*Hello world, I have a message: second or third.*Hello world,"
|
|
116
|
+
" I have a message: last",
|
|
117
|
+
)
|
|
107
118
|
|
|
108
119
|
def testMultiplejobs2(self):
|
|
109
|
-
self.checkExpectedPattern(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
120
|
+
self.checkExpectedPattern(
|
|
121
|
+
"tutorial_multiplejobs2.py",
|
|
122
|
+
"Hello world, I have a message: first.*Hello world, I have a message: "
|
|
123
|
+
"second or third.*Hello world, I have a message: second or third.*Hello world,"
|
|
124
|
+
" I have a message: last",
|
|
125
|
+
)
|
|
113
126
|
|
|
114
127
|
def testMultiplejobs3(self):
|
|
115
|
-
self.checkExpectedPattern(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
128
|
+
self.checkExpectedPattern(
|
|
129
|
+
"tutorial_multiplejobs3.py",
|
|
130
|
+
"Hello world, I have a message: first.*Hello world, I have a message: "
|
|
131
|
+
"second or third.*Hello world, I have a message: second or third.*Hello world,"
|
|
132
|
+
" I have a message: last",
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
@pytest.mark.timeout(1200)
|
|
120
136
|
def testPromises2(self):
|
|
121
|
-
self.checkExpectedOut(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
137
|
+
self.checkExpectedOut(
|
|
138
|
+
"tutorial_promises2.py",
|
|
139
|
+
"['00000', '00001', '00010', '00011', '00100', '00101', '00110', '00111',"
|
|
140
|
+
" '01000', '01001', '01010', '01011', '01100', '01101', '01110', '01111',"
|
|
141
|
+
" '10000', '10001', '10010', '10011', '10100', '10101', '10110', '10111',"
|
|
142
|
+
" '11000', '11001', '11010', '11011', '11100', '11101', '11110', '11111']",
|
|
143
|
+
)
|
|
126
144
|
|
|
127
145
|
def testQuickstart(self):
|
|
128
|
-
self.checkExpectedOut(
|
|
146
|
+
self.checkExpectedOut(
|
|
147
|
+
"tutorial_quickstart.py", "Hello, world!, here's a message: Woot\n"
|
|
148
|
+
)
|
|
129
149
|
|
|
130
150
|
def testRequirements(self):
|
|
131
151
|
self.checkExitCode("tutorial_requirements.py")
|
|
132
152
|
|
|
133
153
|
def testArguments(self):
|
|
134
|
-
self.checkExpectedOut(
|
|
154
|
+
self.checkExpectedOut(
|
|
155
|
+
"tutorial_arguments.py", "Hello, world!, here's a message: Woot"
|
|
156
|
+
)
|
|
135
157
|
|
|
136
158
|
@needs_docker
|
|
137
159
|
def testDocker(self):
|
|
@@ -146,5 +168,6 @@ class ToilDocumentationTest(ToilTest):
|
|
|
146
168
|
def testStaging(self):
|
|
147
169
|
self.checkExitCode("tutorial_staging.py")
|
|
148
170
|
|
|
171
|
+
|
|
149
172
|
if __name__ == "__main__":
|
|
150
173
|
unittest.main()
|