toil 8.0.0__py3-none-any.whl → 8.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.
- toil/__init__.py +4 -39
- toil/batchSystems/abstractBatchSystem.py +1 -1
- toil/batchSystems/abstractGridEngineBatchSystem.py +1 -1
- toil/batchSystems/awsBatch.py +1 -1
- toil/batchSystems/cleanup_support.py +1 -1
- toil/batchSystems/kubernetes.py +53 -7
- toil/batchSystems/local_support.py +1 -1
- toil/batchSystems/mesos/batchSystem.py +13 -8
- toil/batchSystems/mesos/test/__init__.py +3 -2
- toil/batchSystems/options.py +1 -0
- toil/batchSystems/singleMachine.py +1 -1
- toil/batchSystems/slurm.py +229 -84
- toil/bus.py +5 -3
- toil/common.py +198 -54
- toil/cwl/cwltoil.py +32 -11
- toil/job.py +110 -86
- toil/jobStores/abstractJobStore.py +24 -3
- toil/jobStores/aws/jobStore.py +46 -10
- toil/jobStores/fileJobStore.py +25 -1
- toil/jobStores/googleJobStore.py +104 -30
- toil/leader.py +9 -0
- toil/lib/accelerators.py +3 -1
- toil/lib/aws/session.py +14 -3
- toil/lib/aws/utils.py +92 -35
- toil/lib/aws/utils.py.orig +504 -0
- toil/lib/bioio.py +1 -1
- toil/lib/docker.py +252 -91
- toil/lib/dockstore.py +387 -0
- toil/lib/ec2nodes.py +3 -2
- toil/lib/exceptions.py +5 -3
- toil/lib/history.py +1345 -0
- toil/lib/history_submission.py +695 -0
- toil/lib/io.py +56 -23
- toil/lib/misc.py +25 -1
- toil/lib/resources.py +2 -1
- toil/lib/retry.py +10 -10
- toil/lib/threading.py +11 -10
- toil/lib/{integration.py → trs.py} +95 -46
- toil/lib/web.py +38 -0
- toil/options/common.py +25 -2
- toil/options/cwl.py +10 -0
- toil/options/wdl.py +11 -0
- toil/provisioners/gceProvisioner.py +4 -4
- toil/server/api_spec/LICENSE +201 -0
- toil/server/api_spec/README.rst +5 -0
- toil/server/cli/wes_cwl_runner.py +5 -4
- toil/server/utils.py +2 -3
- toil/statsAndLogging.py +35 -1
- toil/test/__init__.py +275 -115
- toil/test/batchSystems/batchSystemTest.py +227 -205
- toil/test/batchSystems/test_slurm.py +199 -2
- toil/test/cactus/pestis.tar.gz +0 -0
- toil/test/conftest.py +7 -0
- toil/test/cwl/2.fasta +11 -0
- toil/test/cwl/2.fastq +12 -0
- toil/test/cwl/conftest.py +39 -0
- toil/test/cwl/cwlTest.py +1015 -780
- toil/test/cwl/directory/directory/file.txt +15 -0
- toil/test/cwl/download_directory_file.json +4 -0
- toil/test/cwl/download_directory_s3.json +4 -0
- toil/test/cwl/download_file.json +6 -0
- toil/test/cwl/download_http.json +6 -0
- toil/test/cwl/download_https.json +6 -0
- toil/test/cwl/download_s3.json +6 -0
- toil/test/cwl/download_subdirectory_file.json +5 -0
- toil/test/cwl/download_subdirectory_s3.json +5 -0
- toil/test/cwl/empty.json +1 -0
- toil/test/cwl/mock_mpi/fake_mpi.yml +8 -0
- toil/test/cwl/mock_mpi/fake_mpi_run.py +42 -0
- toil/test/cwl/optional-file-exists.json +6 -0
- toil/test/cwl/optional-file-missing.json +6 -0
- toil/test/cwl/optional-file.cwl +18 -0
- toil/test/cwl/preemptible_expression.json +1 -0
- toil/test/cwl/revsort-job-missing.json +6 -0
- toil/test/cwl/revsort-job.json +6 -0
- toil/test/cwl/s3_secondary_file.json +16 -0
- toil/test/cwl/seqtk_seq_job.json +6 -0
- toil/test/cwl/stream.json +6 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.dat +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f0 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f1 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f1i +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f2 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f2_TSM0 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f3 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f3_TSM0 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f4 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f4_TSM0 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.f5 +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.info +0 -0
- toil/test/cwl/test_filename_conflict_resolution.ms/table.lock +0 -0
- toil/test/cwl/whale.txt +16 -0
- toil/test/docs/scripts/example_alwaysfail.py +38 -0
- toil/test/docs/scripts/example_alwaysfail_with_files.wdl +33 -0
- toil/test/docs/scripts/example_cachingbenchmark.py +117 -0
- toil/test/docs/scripts/stagingExampleFiles/in.txt +1 -0
- toil/test/docs/scripts/stagingExampleFiles/out.txt +2 -0
- toil/test/docs/scripts/tutorial_arguments.py +23 -0
- toil/test/docs/scripts/tutorial_debugging.patch +12 -0
- toil/test/docs/scripts/tutorial_debugging_hangs.wdl +126 -0
- toil/test/docs/scripts/tutorial_debugging_works.wdl +129 -0
- toil/test/docs/scripts/tutorial_docker.py +20 -0
- toil/test/docs/scripts/tutorial_dynamic.py +24 -0
- toil/test/docs/scripts/tutorial_encapsulation.py +28 -0
- toil/test/docs/scripts/tutorial_encapsulation2.py +29 -0
- toil/test/docs/scripts/tutorial_helloworld.py +15 -0
- toil/test/docs/scripts/tutorial_invokeworkflow.py +27 -0
- toil/test/docs/scripts/tutorial_invokeworkflow2.py +30 -0
- toil/test/docs/scripts/tutorial_jobfunctions.py +22 -0
- toil/test/docs/scripts/tutorial_managing.py +29 -0
- toil/test/docs/scripts/tutorial_managing2.py +56 -0
- toil/test/docs/scripts/tutorial_multiplejobs.py +25 -0
- toil/test/docs/scripts/tutorial_multiplejobs2.py +21 -0
- toil/test/docs/scripts/tutorial_multiplejobs3.py +22 -0
- toil/test/docs/scripts/tutorial_promises.py +25 -0
- toil/test/docs/scripts/tutorial_promises2.py +30 -0
- toil/test/docs/scripts/tutorial_quickstart.py +22 -0
- toil/test/docs/scripts/tutorial_requirements.py +44 -0
- toil/test/docs/scripts/tutorial_services.py +45 -0
- toil/test/docs/scripts/tutorial_staging.py +45 -0
- toil/test/docs/scripts/tutorial_stats.py +64 -0
- toil/test/lib/aws/test_iam.py +3 -1
- toil/test/lib/dockerTest.py +205 -122
- toil/test/lib/test_history.py +236 -0
- toil/test/lib/test_trs.py +161 -0
- toil/test/provisioners/aws/awsProvisionerTest.py +12 -9
- toil/test/provisioners/clusterTest.py +4 -4
- toil/test/provisioners/gceProvisionerTest.py +16 -14
- toil/test/sort/sort.py +4 -1
- toil/test/src/busTest.py +17 -17
- toil/test/src/deferredFunctionTest.py +145 -132
- toil/test/src/importExportFileTest.py +71 -63
- toil/test/src/jobEncapsulationTest.py +27 -28
- toil/test/src/jobServiceTest.py +149 -133
- toil/test/src/jobTest.py +219 -211
- toil/test/src/miscTests.py +66 -60
- toil/test/src/promisedRequirementTest.py +163 -169
- toil/test/src/regularLogTest.py +24 -24
- toil/test/src/resourceTest.py +82 -76
- toil/test/src/restartDAGTest.py +51 -47
- toil/test/src/resumabilityTest.py +24 -19
- toil/test/src/retainTempDirTest.py +60 -57
- toil/test/src/systemTest.py +17 -13
- toil/test/src/threadingTest.py +29 -32
- toil/test/utils/ABCWorkflowDebug/B_file.txt +1 -0
- toil/test/utils/ABCWorkflowDebug/debugWorkflow.py +204 -0
- toil/test/utils/ABCWorkflowDebug/mkFile.py +16 -0
- toil/test/utils/ABCWorkflowDebug/sleep.cwl +12 -0
- toil/test/utils/ABCWorkflowDebug/sleep.yaml +1 -0
- toil/test/utils/toilDebugTest.py +117 -102
- toil/test/utils/toilKillTest.py +54 -53
- toil/test/utils/utilsTest.py +303 -229
- toil/test/wdl/lint_error.wdl +9 -0
- toil/test/wdl/md5sum/empty_file.json +1 -0
- toil/test/wdl/md5sum/md5sum-gs.json +1 -0
- toil/test/wdl/md5sum/md5sum.1.0.wdl +32 -0
- toil/test/wdl/md5sum/md5sum.input +1 -0
- toil/test/wdl/md5sum/md5sum.json +1 -0
- toil/test/wdl/md5sum/md5sum.wdl +25 -0
- toil/test/wdl/miniwdl_self_test/inputs-namespaced.json +1 -0
- toil/test/wdl/miniwdl_self_test/inputs.json +1 -0
- toil/test/wdl/miniwdl_self_test/self_test.wdl +40 -0
- toil/test/wdl/standard_library/as_map.json +16 -0
- toil/test/wdl/standard_library/as_map_as_input.wdl +23 -0
- toil/test/wdl/standard_library/as_pairs.json +7 -0
- toil/test/wdl/standard_library/as_pairs_as_input.wdl +23 -0
- toil/test/wdl/standard_library/ceil.json +3 -0
- toil/test/wdl/standard_library/ceil_as_command.wdl +16 -0
- toil/test/wdl/standard_library/ceil_as_input.wdl +16 -0
- toil/test/wdl/standard_library/collect_by_key.json +1 -0
- toil/test/wdl/standard_library/collect_by_key_as_input.wdl +23 -0
- toil/test/wdl/standard_library/cross.json +11 -0
- toil/test/wdl/standard_library/cross_as_input.wdl +19 -0
- toil/test/wdl/standard_library/flatten.json +7 -0
- toil/test/wdl/standard_library/flatten_as_input.wdl +18 -0
- toil/test/wdl/standard_library/floor.json +3 -0
- toil/test/wdl/standard_library/floor_as_command.wdl +16 -0
- toil/test/wdl/standard_library/floor_as_input.wdl +16 -0
- toil/test/wdl/standard_library/keys.json +8 -0
- toil/test/wdl/standard_library/keys_as_input.wdl +24 -0
- toil/test/wdl/standard_library/length.json +7 -0
- toil/test/wdl/standard_library/length_as_input.wdl +16 -0
- toil/test/wdl/standard_library/length_as_input_with_map.json +7 -0
- toil/test/wdl/standard_library/length_as_input_with_map.wdl +17 -0
- toil/test/wdl/standard_library/length_invalid.json +3 -0
- toil/test/wdl/standard_library/range.json +3 -0
- toil/test/wdl/standard_library/range_0.json +3 -0
- toil/test/wdl/standard_library/range_as_input.wdl +17 -0
- toil/test/wdl/standard_library/range_invalid.json +3 -0
- toil/test/wdl/standard_library/read_boolean.json +3 -0
- toil/test/wdl/standard_library/read_boolean_as_command.wdl +17 -0
- toil/test/wdl/standard_library/read_float.json +3 -0
- toil/test/wdl/standard_library/read_float_as_command.wdl +17 -0
- toil/test/wdl/standard_library/read_int.json +3 -0
- toil/test/wdl/standard_library/read_int_as_command.wdl +17 -0
- toil/test/wdl/standard_library/read_json.json +3 -0
- toil/test/wdl/standard_library/read_json_as_output.wdl +31 -0
- toil/test/wdl/standard_library/read_lines.json +3 -0
- toil/test/wdl/standard_library/read_lines_as_output.wdl +31 -0
- toil/test/wdl/standard_library/read_map.json +3 -0
- toil/test/wdl/standard_library/read_map_as_output.wdl +31 -0
- toil/test/wdl/standard_library/read_string.json +3 -0
- toil/test/wdl/standard_library/read_string_as_command.wdl +17 -0
- toil/test/wdl/standard_library/read_tsv.json +3 -0
- toil/test/wdl/standard_library/read_tsv_as_output.wdl +31 -0
- toil/test/wdl/standard_library/round.json +3 -0
- toil/test/wdl/standard_library/round_as_command.wdl +16 -0
- toil/test/wdl/standard_library/round_as_input.wdl +16 -0
- toil/test/wdl/standard_library/size.json +3 -0
- toil/test/wdl/standard_library/size_as_command.wdl +17 -0
- toil/test/wdl/standard_library/size_as_output.wdl +36 -0
- toil/test/wdl/standard_library/stderr.json +3 -0
- toil/test/wdl/standard_library/stderr_as_output.wdl +30 -0
- toil/test/wdl/standard_library/stdout.json +3 -0
- toil/test/wdl/standard_library/stdout_as_output.wdl +30 -0
- toil/test/wdl/standard_library/sub.json +3 -0
- toil/test/wdl/standard_library/sub_as_input.wdl +17 -0
- toil/test/wdl/standard_library/sub_as_input_with_file.wdl +17 -0
- toil/test/wdl/standard_library/transpose.json +6 -0
- toil/test/wdl/standard_library/transpose_as_input.wdl +18 -0
- toil/test/wdl/standard_library/write_json.json +6 -0
- toil/test/wdl/standard_library/write_json_as_command.wdl +17 -0
- toil/test/wdl/standard_library/write_lines.json +7 -0
- toil/test/wdl/standard_library/write_lines_as_command.wdl +17 -0
- toil/test/wdl/standard_library/write_map.json +6 -0
- toil/test/wdl/standard_library/write_map_as_command.wdl +17 -0
- toil/test/wdl/standard_library/write_tsv.json +6 -0
- toil/test/wdl/standard_library/write_tsv_as_command.wdl +17 -0
- toil/test/wdl/standard_library/zip.json +12 -0
- toil/test/wdl/standard_library/zip_as_input.wdl +19 -0
- toil/test/wdl/test.csv +3 -0
- toil/test/wdl/test.tsv +3 -0
- toil/test/wdl/testfiles/croo.wdl +38 -0
- toil/test/wdl/testfiles/drop_files.wdl +62 -0
- toil/test/wdl/testfiles/drop_files_subworkflow.wdl +13 -0
- toil/test/wdl/testfiles/empty.txt +0 -0
- toil/test/wdl/testfiles/not_enough_outputs.wdl +33 -0
- toil/test/wdl/testfiles/random.wdl +66 -0
- toil/test/wdl/testfiles/string_file_coercion.json +1 -0
- toil/test/wdl/testfiles/string_file_coercion.wdl +35 -0
- toil/test/wdl/testfiles/test.json +4 -0
- toil/test/wdl/testfiles/test_boolean.txt +1 -0
- toil/test/wdl/testfiles/test_float.txt +1 -0
- toil/test/wdl/testfiles/test_int.txt +1 -0
- toil/test/wdl/testfiles/test_lines.txt +5 -0
- toil/test/wdl/testfiles/test_map.txt +2 -0
- toil/test/wdl/testfiles/test_string.txt +1 -0
- toil/test/wdl/testfiles/url_to_file.wdl +13 -0
- toil/test/wdl/testfiles/url_to_optional_file.wdl +13 -0
- toil/test/wdl/testfiles/vocab.json +1 -0
- toil/test/wdl/testfiles/vocab.wdl +66 -0
- toil/test/wdl/testfiles/wait.wdl +34 -0
- toil/test/wdl/wdl_specification/type_pair.json +23 -0
- toil/test/wdl/wdl_specification/type_pair_basic.wdl +36 -0
- toil/test/wdl/wdl_specification/type_pair_with_files.wdl +36 -0
- toil/test/wdl/wdl_specification/v1_spec.json +1 -0
- toil/test/wdl/wdl_specification/v1_spec_declaration.wdl +39 -0
- toil/test/wdl/wdltoil_test.py +681 -408
- toil/test/wdl/wdltoil_test_kubernetes.py +2 -2
- toil/version.py +10 -10
- toil/wdl/wdltoil.py +350 -123
- toil/worker.py +113 -33
- {toil-8.0.0.dist-info → toil-8.2.0.dist-info}/METADATA +13 -7
- toil-8.2.0.dist-info/RECORD +439 -0
- {toil-8.0.0.dist-info → toil-8.2.0.dist-info}/WHEEL +1 -1
- toil/test/lib/test_integration.py +0 -104
- toil-8.0.0.dist-info/RECORD +0 -253
- {toil-8.0.0.dist-info → toil-8.2.0.dist-info}/entry_points.txt +0 -0
- {toil-8.0.0.dist-info → toil-8.2.0.dist-info/licenses}/LICENSE +0 -0
- {toil-8.0.0.dist-info → toil-8.2.0.dist-info}/top_level.txt +0 -0
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
"""
|
|
16
|
-
Contains functions for integrating Toil with
|
|
17
|
-
|
|
16
|
+
Contains functions for integrating Toil with GA4GH Tool Registry Service
|
|
17
|
+
servers, for fetching workflows.
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
20
|
import hashlib
|
|
@@ -24,35 +24,29 @@ import shutil
|
|
|
24
24
|
import sys
|
|
25
25
|
import tempfile
|
|
26
26
|
import zipfile
|
|
27
|
-
from typing import Any,
|
|
27
|
+
from typing import Any, Literal, Optional, Union, TypedDict, cast
|
|
28
28
|
|
|
29
29
|
from urllib.parse import urlparse, unquote, quote
|
|
30
30
|
import requests
|
|
31
31
|
|
|
32
32
|
from toil.lib.retry import retry
|
|
33
33
|
from toil.lib.io import file_digest, robust_rmtree
|
|
34
|
-
from toil.
|
|
34
|
+
from toil.lib.web import web_session
|
|
35
35
|
|
|
36
36
|
logger = logging.getLogger(__name__)
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
# doing cookies, and to send a sensible user agent.
|
|
40
|
-
# We expect the Toil and Python version to not be personally identifiable even
|
|
41
|
-
# in theory (someone might make a new Toil version first, buit there's no way
|
|
42
|
-
# to know for sure that nobody else did the same thing).
|
|
43
|
-
session = requests.Session()
|
|
44
|
-
session.headers.update({"User-Agent": f"Toil {baseVersion} on Python {'.'.join([str(v) for v in sys.version_info])}"})
|
|
38
|
+
TRS_ROOT = "https://dockstore.org" if "TOIL_TRS_ROOT" not in os.environ else os.environ["TOIL_TRS_ROOT"]
|
|
45
39
|
|
|
46
|
-
def
|
|
40
|
+
def is_trs_workflow(workflow: str) -> bool:
|
|
47
41
|
"""
|
|
48
|
-
Returns True if a workflow string smells
|
|
42
|
+
Returns True if a workflow string smells like TRS.
|
|
49
43
|
|
|
50
44
|
Detects Dockstore page URLs and strings that could be Dockstore TRS IDs.
|
|
51
45
|
"""
|
|
52
46
|
|
|
53
|
-
return workflow.startswith("
|
|
47
|
+
return workflow.startswith(f"{TRS_ROOT}/workflows/") or workflow.startswith(f"{TRS_ROOT}/my-workflows/") or workflow.startswith("#workflow/")
|
|
54
48
|
|
|
55
|
-
def
|
|
49
|
+
def extract_trs_spec(workflow: str) -> str:
|
|
56
50
|
"""
|
|
57
51
|
Parse a Dockstore workflow URL or TSR ID to a string that is definitely a TRS ID.
|
|
58
52
|
"""
|
|
@@ -63,13 +57,16 @@ def find_trs_spec(workflow: str) -> str:
|
|
|
63
57
|
logger.debug("Workflow %s is a TRS specifier already", workflow)
|
|
64
58
|
trs_spec = workflow
|
|
65
59
|
else:
|
|
66
|
-
# We need to get the right TRS ID from the
|
|
60
|
+
# We need to get the right TRS ID from the Dockstore URL
|
|
67
61
|
parsed = urlparse(workflow)
|
|
68
|
-
# TODO: We assume the
|
|
62
|
+
# TODO: We assume the Dockstore page URL structure and the TRS IDs are basically the same.
|
|
69
63
|
page_path = unquote(parsed.path)
|
|
70
|
-
if
|
|
64
|
+
if page_path.startswith("/workflows/"):
|
|
65
|
+
trs_spec = "#workflow/" + page_path[len("/workflows/"):]
|
|
66
|
+
elif page_path.startswith("/my-workflows/"):
|
|
67
|
+
trs_spec = "#workflow/" + page_path[len("/my-workflows/"):]
|
|
68
|
+
else:
|
|
71
69
|
raise RuntimeError("Cannot parse Dockstore URL " + workflow)
|
|
72
|
-
trs_spec = "#workflow/" + page_path[len("/workflows/"):]
|
|
73
70
|
logger.debug("Translated %s to TRS: %s", workflow, trs_spec)
|
|
74
71
|
|
|
75
72
|
return trs_spec
|
|
@@ -88,27 +85,36 @@ def parse_trs_spec(trs_spec: str) -> tuple[str, Optional[str]]:
|
|
|
88
85
|
trs_version = None
|
|
89
86
|
return trs_workflow_id, trs_version
|
|
90
87
|
|
|
88
|
+
def compose_trs_spec(trs_workflow_id: str, trs_version: str) -> str:
|
|
89
|
+
"""
|
|
90
|
+
Compose a TRS ID from a workflow ID and version ID.
|
|
91
|
+
"""
|
|
92
|
+
return f"{trs_workflow_id}:{trs_version}"
|
|
93
|
+
|
|
91
94
|
@retry(errors=[requests.exceptions.ConnectionError])
|
|
92
|
-
def
|
|
95
|
+
def find_workflow(workflow: str, supported_languages: Optional[set[str]] = None) -> tuple[str, str, str]:
|
|
93
96
|
"""
|
|
94
|
-
Given a Dockstore URL or TRS identifier, get the root WDL or CWL URL for the workflow.
|
|
97
|
+
Given a Dockstore URL or TRS identifier, get the root WDL or CWL URL for the workflow, along with the TRS workflow ID and version.
|
|
95
98
|
|
|
96
99
|
Accepts inputs like:
|
|
97
100
|
|
|
98
101
|
- https://dockstore.org/workflows/github.com/dockstore-testing/md5sum-checker:master?tab=info
|
|
99
102
|
- #workflow/github.com/dockstore-testing/md5sum-checker
|
|
100
103
|
|
|
101
|
-
Assumes the input is actually one of the supported formats. See
|
|
104
|
+
Assumes the input is actually one of the supported formats. See is_trs_workflow().
|
|
102
105
|
|
|
103
106
|
TODO: Needs to handle multi-workflow files if Dockstore can.
|
|
104
107
|
|
|
108
|
+
:raises FileNotFoundError: if the workflow or version doesn't exist.
|
|
109
|
+
:raises ValueError: if the version is not specified but cannot be
|
|
110
|
+
automatically determined.
|
|
105
111
|
"""
|
|
106
112
|
|
|
107
113
|
if supported_languages is not None and len(supported_languages) == 0:
|
|
108
114
|
raise ValueError("Set of supported languages must be nonempty if provided.")
|
|
109
115
|
|
|
110
116
|
# Get the TRS id[:version] string from what might be a Dockstore URL
|
|
111
|
-
trs_spec =
|
|
117
|
+
trs_spec = extract_trs_spec(workflow)
|
|
112
118
|
# Parse out workflow and possible version
|
|
113
119
|
trs_workflow_id, trs_version = parse_trs_spec(trs_spec)
|
|
114
120
|
|
|
@@ -116,8 +122,14 @@ def get_workflow_root_from_dockstore(workflow: str, supported_languages: Optiona
|
|
|
116
122
|
|
|
117
123
|
# Fetch the main TRS document.
|
|
118
124
|
# See e.g. https://dockstore.org/api/ga4gh/trs/v2/tools/%23workflow%2Fgithub.com%2Fdockstore-testing%2Fmd5sum-checker
|
|
119
|
-
trs_workflow_url = f"
|
|
120
|
-
|
|
125
|
+
trs_workflow_url = f"{TRS_ROOT}/api/ga4gh/trs/v2/tools/{quote(trs_workflow_id, safe='')}"
|
|
126
|
+
logger.debug("Get versions: %s", trs_workflow_url)
|
|
127
|
+
trs_workflow_response = web_session.get(trs_workflow_url)
|
|
128
|
+
if trs_workflow_response.status_code in (400, 404):
|
|
129
|
+
# If the workflow ID isn't in Dockstore's accepted format (and also thus doesn't exist), we can get a 400
|
|
130
|
+
raise FileNotFoundError(f"Workflow {trs_workflow_id} does not exist.")
|
|
131
|
+
trs_workflow_response.raise_for_status()
|
|
132
|
+
trs_workflow_document = trs_workflow_response.json()
|
|
121
133
|
|
|
122
134
|
# Make a map from version to version info. We will need the
|
|
123
135
|
# "descriptor_type" array to find eligible languages, and the "url" field
|
|
@@ -146,12 +158,10 @@ def get_workflow_root_from_dockstore(workflow: str, supported_languages: Optiona
|
|
|
146
158
|
continue
|
|
147
159
|
eligible_workflow_versions.add(version_name)
|
|
148
160
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
logger.debug("Defaulting to workflow version %s", default_version)
|
|
154
|
-
break
|
|
161
|
+
# TODO: Dockstore has a concept of a "default version", but doesn't expose
|
|
162
|
+
# it over TRS. To avoid defaulting to something that *isn't* the Dockstore
|
|
163
|
+
# default version, we refuse to choose a version when there are multiple
|
|
164
|
+
# possibilities.
|
|
155
165
|
|
|
156
166
|
if trs_version is None and len(eligible_workflow_versions) == 1:
|
|
157
167
|
# If there's just one version use that.
|
|
@@ -161,26 +171,31 @@ def get_workflow_root_from_dockstore(workflow: str, supported_languages: Optiona
|
|
|
161
171
|
|
|
162
172
|
# If we don't like what we found we compose a useful error message.
|
|
163
173
|
problems: list[str] = []
|
|
174
|
+
problem_type: type[Exception] = RuntimeError
|
|
164
175
|
if trs_version is None:
|
|
165
176
|
problems.append(f"Workflow {workflow} does not specify a version")
|
|
177
|
+
problem_type = ValueError
|
|
166
178
|
elif trs_version not in workflow_versions:
|
|
167
179
|
problems.append(f"Workflow version {trs_version} from {workflow} does not exist")
|
|
180
|
+
problem_type = FileNotFoundError
|
|
168
181
|
elif trs_version not in eligible_workflow_versions:
|
|
169
182
|
message = f"Workflow version {trs_version} from {workflow} is not available"
|
|
170
183
|
if supported_languages is not None:
|
|
171
184
|
message += f" in any of: {', '.join(supported_languages)}"
|
|
172
185
|
problems.append(message)
|
|
186
|
+
problem_type = FileNotFoundError
|
|
173
187
|
if len(problems) > 0:
|
|
174
188
|
if len(eligible_workflow_versions) == 0:
|
|
175
189
|
message = "No versions of the workflow are available"
|
|
176
190
|
if supported_languages is not None:
|
|
177
191
|
message += f" in any of: {', '.join(supported_languages)}"
|
|
178
192
|
problems.append(message)
|
|
193
|
+
problem_type = FileNotFoundError
|
|
179
194
|
elif trs_version is None:
|
|
180
195
|
problems.append(f"Add ':' and the name of a workflow version ({', '.join(eligible_workflow_versions)}) after '{trs_workflow_id}'")
|
|
181
196
|
else:
|
|
182
197
|
problems.append(f"Replace '{trs_version}' with one of ({', '.join(eligible_workflow_versions)})")
|
|
183
|
-
raise
|
|
198
|
+
raise problem_type("; ".join(problems))
|
|
184
199
|
|
|
185
200
|
# Tell MyPy we now have a version, or we would have raised
|
|
186
201
|
assert trs_version is not None
|
|
@@ -192,12 +207,35 @@ def get_workflow_root_from_dockstore(workflow: str, supported_languages: Optiona
|
|
|
192
207
|
language = candidate_language
|
|
193
208
|
|
|
194
209
|
logger.debug("Going to use %s version %s in %s", trs_workflow_id, trs_version, language)
|
|
195
|
-
|
|
210
|
+
|
|
211
|
+
return trs_workflow_id, trs_version, language
|
|
212
|
+
|
|
213
|
+
@retry(errors=[requests.exceptions.ConnectionError])
|
|
214
|
+
def fetch_workflow(trs_workflow_id: str, trs_version: str, language: str) -> str:
|
|
215
|
+
"""
|
|
216
|
+
Returns a URL or local path to a workflow's primary descriptor file.
|
|
217
|
+
|
|
218
|
+
The file will be in context with its required files so it can actually run.
|
|
219
|
+
|
|
220
|
+
:raises FileNotFoundError: if the workflow or version doesn't exist.
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
# TODO: We should probably use HATEOAS and pull this from the worflow
|
|
224
|
+
# document we probably already fetched but aren't passing.
|
|
225
|
+
trs_version_url = f"{TRS_ROOT}/api/ga4gh/trs/v2/tools/{quote(trs_workflow_id, safe='')}/versions/{quote(trs_version, safe='')}"
|
|
196
226
|
|
|
197
227
|
# Fetch the list of all the files
|
|
198
228
|
trs_files_url = f"{trs_version_url}/{language}/files"
|
|
199
229
|
logger.debug("Workflow files URL: %s", trs_files_url)
|
|
200
|
-
|
|
230
|
+
trs_files_response = web_session.get(trs_files_url)
|
|
231
|
+
if trs_files_response.status_code in (204, 400, 404):
|
|
232
|
+
# We can get a 204 No Content response if the version doesn't exist.
|
|
233
|
+
# That's successful, so we need to handle it specifically. See
|
|
234
|
+
# <https://github.com/dockstore/dockstore/issues/6048>
|
|
235
|
+
# We can also get a 400 if the workflow ID is not in Dockstore's expected format (3 slash-separated segments).
|
|
236
|
+
raise FileNotFoundError(f"Workflow {trs_workflow_id} version {trs_version} in language {language} does not exist.")
|
|
237
|
+
trs_files_response.raise_for_status()
|
|
238
|
+
trs_files_document = trs_files_response.json()
|
|
201
239
|
|
|
202
240
|
# Find the information we need to ID the primary descriptor file
|
|
203
241
|
primary_descriptor_path: Optional[str] = None
|
|
@@ -210,7 +248,7 @@ def get_workflow_root_from_dockstore(workflow: str, supported_languages: Optiona
|
|
|
210
248
|
primary_descriptor_hash = file_info["checksum"]["checksum"]
|
|
211
249
|
break
|
|
212
250
|
if primary_descriptor_path is None or primary_descriptor_hash is None or primary_descriptor_hash_algorithm is None:
|
|
213
|
-
raise RuntimeError("Could not find a primary descriptor file for
|
|
251
|
+
raise RuntimeError(f"Could not find a primary descriptor file for workflow {trs_workflow_id} version {trs_version} in language {language}")
|
|
214
252
|
primary_descriptor_basename = os.path.basename(primary_descriptor_path)
|
|
215
253
|
|
|
216
254
|
# Work out how to compute the hash we are looking for. See
|
|
@@ -263,7 +301,7 @@ def get_workflow_root_from_dockstore(workflow: str, supported_languages: Optiona
|
|
|
263
301
|
}
|
|
264
302
|
# If we don't set stream=True, we can't actually read anything from the
|
|
265
303
|
# raw stream, since Requests will have done it already.
|
|
266
|
-
with
|
|
304
|
+
with web_session.get(trs_zip_file_url, headers=headers, stream=True) as response:
|
|
267
305
|
response_content_length = response.headers.get("Content-Length")
|
|
268
306
|
logger.debug("Server reports content length: %s", response_content_length)
|
|
269
307
|
shutil.copyfileobj(response.raw, zip_file)
|
|
@@ -308,27 +346,38 @@ def get_workflow_root_from_dockstore(workflow: str, supported_languages: Optiona
|
|
|
308
346
|
logger.debug("Rejected %s because its %s hash %s is not %s", file_path, python_hash_name, file_hash, primary_descriptor_hash)
|
|
309
347
|
if found_path is None:
|
|
310
348
|
# We couldn't find the promised primary descriptor
|
|
311
|
-
raise RuntimeError(f"Could not find a {primary_descriptor_basename} with {primary_descriptor_hash_algorithm} hash {primary_descriptor_hash}")
|
|
349
|
+
raise RuntimeError(f"Could not find a {primary_descriptor_basename} with {primary_descriptor_hash_algorithm} hash {primary_descriptor_hash} for workflow {trs_workflow_id} version {trs_version} in language {language}")
|
|
312
350
|
|
|
313
351
|
return found_path
|
|
314
352
|
|
|
315
|
-
def resolve_workflow(workflow: str, supported_languages: Optional[set[str]] = None) -> str:
|
|
353
|
+
def resolve_workflow(workflow: str, supported_languages: Optional[set[str]] = None) -> tuple[str, Optional[str]]:
|
|
316
354
|
"""
|
|
317
355
|
Find the real workflow URL or filename from a command line argument.
|
|
318
356
|
|
|
319
357
|
Transform a workflow URL or path that might actually be a Dockstore page
|
|
320
|
-
URL or TRS specifier to an actual URL or path to a workflow document
|
|
358
|
+
URL or TRS specifier to an actual URL or path to a workflow document, and
|
|
359
|
+
optional TRS specifier.
|
|
360
|
+
|
|
361
|
+
Accepts inputs like
|
|
362
|
+
|
|
363
|
+
- https://dockstore.org/workflows/github.com/dockstore-testing/md5sum-checker:master?tab=info
|
|
364
|
+
- #workflow/github.com/dockstore-testing/md5sum-checker
|
|
365
|
+
- ./local.cwl
|
|
366
|
+
- https://example.com/~myuser/workflow/main.cwl
|
|
367
|
+
|
|
368
|
+
:raises FileNotFoundError: if the workflow or version should be in Dockstore but doesn't seem to exist.
|
|
321
369
|
"""
|
|
322
370
|
|
|
323
|
-
if
|
|
324
|
-
# Ask
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
371
|
+
if is_trs_workflow(workflow):
|
|
372
|
+
# Ask TRS host where to find TRS-looking things
|
|
373
|
+
trs_workflow_id, trs_version, language = find_workflow(workflow, supported_languages)
|
|
374
|
+
resolved = fetch_workflow(trs_workflow_id, trs_version, language)
|
|
375
|
+
logger.info("Resolved TRS workflow %s to %s", workflow, resolved)
|
|
376
|
+
return resolved, compose_trs_spec(trs_workflow_id, trs_version)
|
|
328
377
|
else:
|
|
329
378
|
# Pass other things through.
|
|
330
|
-
|
|
331
|
-
|
|
379
|
+
# TODO: Find out if they have TRS names.
|
|
380
|
+
return workflow, None
|
|
332
381
|
|
|
333
382
|
|
|
334
383
|
|
toil/lib/web.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Copyright (C) 2024 Regents of the University of California
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Contains functions for making web requests with Toil.
|
|
17
|
+
|
|
18
|
+
All web requests should go through this module, to make sure they use the right
|
|
19
|
+
user agent.
|
|
20
|
+
|
|
21
|
+
>>> from toil.lib.web import web_session
|
|
22
|
+
>>> web_session.get("https://example.com")
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
import logging
|
|
27
|
+
import requests
|
|
28
|
+
import sys
|
|
29
|
+
|
|
30
|
+
from toil.version import baseVersion
|
|
31
|
+
|
|
32
|
+
# We manage a Requests session at the module level in case we're supposed to be
|
|
33
|
+
# doing cookies, and to send a sensible user agent.
|
|
34
|
+
# We expect the Toil and Python version to not be personally identifiable even
|
|
35
|
+
# in theory (someone might make a new Toil version first, but there's no way
|
|
36
|
+
# to know for sure that nobody else did the same thing).
|
|
37
|
+
web_session = requests.Session()
|
|
38
|
+
web_session.headers.update({"User-Agent": f"Toil {baseVersion} on Python {'.'.join([str(v) for v in sys.version_info])}"})
|
toil/options/common.py
CHANGED
|
@@ -362,12 +362,13 @@ def add_base_toil_options(
|
|
|
362
362
|
action="store_true",
|
|
363
363
|
help="Do not capture standard output and error from batch system jobs.",
|
|
364
364
|
)
|
|
365
|
+
# TODO: Should this be deprecated since we always save stats now for history tracking?
|
|
365
366
|
core_options.add_argument(
|
|
366
367
|
"--stats",
|
|
367
368
|
dest="stats",
|
|
368
369
|
default=False,
|
|
369
370
|
action="store_true",
|
|
370
|
-
help="
|
|
371
|
+
help="Keep statistics about the toil workflow to be used by 'toil stats'.",
|
|
371
372
|
)
|
|
372
373
|
clean_choices = ["always", "onError", "never", "onSuccess"]
|
|
373
374
|
core_options.add_argument(
|
|
@@ -466,7 +467,8 @@ def add_base_toil_options(
|
|
|
466
467
|
)
|
|
467
468
|
|
|
468
469
|
caching = file_store_options.add_mutually_exclusive_group()
|
|
469
|
-
caching_help = "Enable or disable caching for your workflow, specifying this overrides default from
|
|
470
|
+
caching_help = ("Enable or disable worker level file caching for your workflow, specifying this overrides default from batch system. "
|
|
471
|
+
"Does not affect CWL or WDL task caching.")
|
|
470
472
|
caching.add_argument(
|
|
471
473
|
"--caching",
|
|
472
474
|
dest="caching",
|
|
@@ -858,6 +860,14 @@ def add_base_toil_options(
|
|
|
858
860
|
help=f"Number of times to retry a failing job before giving up and "
|
|
859
861
|
f"labeling job failed. default={1}",
|
|
860
862
|
)
|
|
863
|
+
job_options.add_argument(
|
|
864
|
+
"--stopOnFirstFailure",
|
|
865
|
+
dest="stop_on_first_failure",
|
|
866
|
+
type=strtobool,
|
|
867
|
+
default=False,
|
|
868
|
+
metavar="BOOL",
|
|
869
|
+
help="Stop the workflow at the first complete job failure.",
|
|
870
|
+
)
|
|
861
871
|
job_options.add_argument(
|
|
862
872
|
"--enableUnlimitedPreemptibleRetries",
|
|
863
873
|
"--enableUnlimitedPreemptableRetries",
|
|
@@ -1102,6 +1112,19 @@ def add_base_toil_options(
|
|
|
1102
1112
|
default=False,
|
|
1103
1113
|
help="Disables the progress bar shown when standard error is a terminal.",
|
|
1104
1114
|
)
|
|
1115
|
+
misc_options.add_argument(
|
|
1116
|
+
"--publishWorkflowMetrics",
|
|
1117
|
+
dest="publish_workflow_metrics",
|
|
1118
|
+
choices=["all", "current", "no"],
|
|
1119
|
+
default=None,
|
|
1120
|
+
help="Whether to publish workflow metrics reports (including unique workflow "
|
|
1121
|
+
"and task run IDs, job names, and version and Toil feature use information) to "
|
|
1122
|
+
"Dockstore when a workflow completes. Selecting \"current\" will publish metrics "
|
|
1123
|
+
"for the current workflow. Selecting \"all\" will also publish prior workflow "
|
|
1124
|
+
"runs from the Toil history database, even if they themselves were run with \"no\". "
|
|
1125
|
+
"Note that once published, workflow metrics CANNOT be deleted or un-published; they "
|
|
1126
|
+
"will stay published forever!"
|
|
1127
|
+
)
|
|
1105
1128
|
|
|
1106
1129
|
# Debug options
|
|
1107
1130
|
debug_options = parser.add_argument_group(
|
toil/options/cwl.py
CHANGED
|
@@ -419,3 +419,13 @@ def add_cwl_options(parser: ArgumentParser, suppress: bool = True) -> None:
|
|
|
419
419
|
type=str,
|
|
420
420
|
help=suppress_help or "Specify a cloud bucket endpoint for output files.",
|
|
421
421
|
)
|
|
422
|
+
parser.add_argument(
|
|
423
|
+
"--cachedir",
|
|
424
|
+
type=str,
|
|
425
|
+
help=suppress_help
|
|
426
|
+
or "Directory to cache intermediate workflow outputs to avoid "
|
|
427
|
+
"recomputing steps. Can be very helpful in the development and "
|
|
428
|
+
"troubleshooting of CWL documents. This automatically bypasses the file store."
|
|
429
|
+
" Not to be confused with --caching.",
|
|
430
|
+
dest="cachedir"
|
|
431
|
+
)
|
toil/options/wdl.py
CHANGED
|
@@ -86,3 +86,14 @@ def add_wdl_options(parser: ArgumentParser, suppress: bool = True) -> None:
|
|
|
86
86
|
default=None,
|
|
87
87
|
help=suppress_help or "Keep and return all call outputs as workflow outputs"
|
|
88
88
|
)
|
|
89
|
+
|
|
90
|
+
strict_arguments = ["--wdlStrict"] + (
|
|
91
|
+
["--strict"] if not suppress else []
|
|
92
|
+
)
|
|
93
|
+
parser.add_argument(
|
|
94
|
+
*strict_arguments,
|
|
95
|
+
dest="strict",
|
|
96
|
+
type=strtobool,
|
|
97
|
+
default=False,
|
|
98
|
+
help=suppress_help or "Exit runner if workflow has any lint warnings"
|
|
99
|
+
)
|
|
@@ -19,7 +19,6 @@ import time
|
|
|
19
19
|
import uuid
|
|
20
20
|
from typing import Optional
|
|
21
21
|
|
|
22
|
-
import requests
|
|
23
22
|
from libcloud.compute.drivers.gce import GCEFailedNode
|
|
24
23
|
from libcloud.compute.providers import get_driver
|
|
25
24
|
from libcloud.compute.types import Provider
|
|
@@ -27,6 +26,7 @@ from libcloud.compute.types import Provider
|
|
|
27
26
|
from toil.jobStores.googleJobStore import GoogleJobStore
|
|
28
27
|
from toil.lib.compatibility import compat_bytes_recursive
|
|
29
28
|
from toil.lib.conversions import human2bytes
|
|
29
|
+
from toil.lib.web import web_session
|
|
30
30
|
from toil.provisioners import NoSuchClusterException
|
|
31
31
|
from toil.provisioners.abstractProvisioner import AbstractProvisioner, Shape
|
|
32
32
|
from toil.provisioners.node import Node
|
|
@@ -83,11 +83,11 @@ class GCEProvisioner(AbstractProvisioner):
|
|
|
83
83
|
"""
|
|
84
84
|
metadata_server = "http://metadata/computeMetadata/v1/instance/"
|
|
85
85
|
metadata_flavor = {"Metadata-Flavor": "Google"}
|
|
86
|
-
zone =
|
|
86
|
+
zone = web_session.get(metadata_server + "zone", headers=metadata_flavor).text
|
|
87
87
|
self._zone = zone.split("/")[-1]
|
|
88
88
|
|
|
89
89
|
project_metadata_server = "http://metadata/computeMetadata/v1/project/"
|
|
90
|
-
self._projectId =
|
|
90
|
+
self._projectId = web_session.get(
|
|
91
91
|
project_metadata_server + "project-id", headers=metadata_flavor
|
|
92
92
|
).text
|
|
93
93
|
|
|
@@ -95,7 +95,7 @@ class GCEProvisioner(AbstractProvisioner):
|
|
|
95
95
|
self._googleJson = ""
|
|
96
96
|
self._clientEmail = ""
|
|
97
97
|
|
|
98
|
-
self._tags =
|
|
98
|
+
self._tags = web_session.get(
|
|
99
99
|
metadata_server + "description", headers=metadata_flavor
|
|
100
100
|
).text
|
|
101
101
|
tags = json.loads(self._tags)
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "{}"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright 2019 Global Alliance for Genomics and Health
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|