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
toil/lib/bioio.py
CHANGED
|
@@ -27,15 +27,19 @@ def system(command):
|
|
|
27
27
|
will be passed to subprocess.check_call.
|
|
28
28
|
:type command: str | sequence[string]
|
|
29
29
|
"""
|
|
30
|
-
logger.warning(
|
|
31
|
-
|
|
30
|
+
logger.warning(
|
|
31
|
+
'Deprecated toil method that will be moved/replaced in a future release."'
|
|
32
|
+
)
|
|
33
|
+
logger.debug(f"Running: {command}")
|
|
32
34
|
subprocess.check_call(command, shell=isinstance(command, str), bufsize=-1)
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
# Used by cactus; now a wrapper and not used in Toil.
|
|
36
38
|
# TODO: Remove from cactus and then remove from Toil.
|
|
37
39
|
def getLogLevelString(logger=None):
|
|
38
|
-
root_logger.warning(
|
|
40
|
+
root_logger.warning(
|
|
41
|
+
'Deprecated toil method. Please call "logging.getLevelName" directly.'
|
|
42
|
+
)
|
|
39
43
|
if logger is None:
|
|
40
44
|
logger = root_logger
|
|
41
45
|
return logging.getLevelName(logger.getEffectiveLevel())
|
|
@@ -44,12 +48,16 @@ def getLogLevelString(logger=None):
|
|
|
44
48
|
# Used by cactus; now a wrapper and not used in Toil.
|
|
45
49
|
# TODO: Remove from cactus and then remove from Toil.
|
|
46
50
|
def setLoggingFromOptions(options):
|
|
47
|
-
logger.warning(
|
|
51
|
+
logger.warning(
|
|
52
|
+
'Deprecated toil method. Please use "toil.statsAndLogging.set_logging_from_options()" instead."'
|
|
53
|
+
)
|
|
48
54
|
set_logging_from_options(options)
|
|
49
55
|
|
|
50
56
|
|
|
51
57
|
# Used by cactus; now a wrapper and not used in Toil.
|
|
52
58
|
# TODO: Remove from cactus and then remove from Toil.
|
|
53
59
|
def getTempFile(suffix="", rootDir=None):
|
|
54
|
-
logger.warning(
|
|
60
|
+
logger.warning(
|
|
61
|
+
'Deprecated toil method. Please use "toil.test.get_temp_file()" instead."'
|
|
62
|
+
)
|
|
55
63
|
return get_temp_file(suffix, rootDir)
|
toil/lib/compatibility.py
CHANGED
|
@@ -7,15 +7,20 @@ def deprecated(new_function_name: str) -> Callable[..., Any]:
|
|
|
7
7
|
def decorate(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
8
8
|
@functools.wraps(func)
|
|
9
9
|
def call(*args: Any, **kwargs: Any) -> Any:
|
|
10
|
-
warnings.warn(
|
|
11
|
-
|
|
10
|
+
warnings.warn(
|
|
11
|
+
f'WARNING: "{func.__name__}()" is deprecated. Please use "{new_function_name}()" instead.',
|
|
12
|
+
DeprecationWarning,
|
|
13
|
+
)
|
|
12
14
|
return func(*args, **kwargs)
|
|
15
|
+
|
|
13
16
|
return call
|
|
17
|
+
|
|
14
18
|
return decorate
|
|
15
19
|
|
|
16
20
|
|
|
17
21
|
def compat_bytes(s: Union[bytes, str]) -> str:
|
|
18
|
-
return s.decode(
|
|
22
|
+
return s.decode("utf-8") if isinstance(s, bytes) else s
|
|
23
|
+
|
|
19
24
|
|
|
20
25
|
# MyPy can't yet support the recursive type we would need to say "we go through
|
|
21
26
|
# any structure of dicts, tuples, lists, and sets and convert all bytes types
|
|
@@ -28,13 +33,13 @@ def compat_bytes_recursive(data: Any) -> Any:
|
|
|
28
33
|
"""
|
|
29
34
|
if isinstance(data, dict):
|
|
30
35
|
# Keyed collection
|
|
31
|
-
return type(data)(
|
|
36
|
+
return type(data)(compat_bytes_recursive(i) for i in data.items())
|
|
32
37
|
elif isinstance(data, (tuple, list, set)):
|
|
33
38
|
# Flat collection
|
|
34
|
-
return type(data)(
|
|
39
|
+
return type(data)(compat_bytes_recursive(i) for i in data)
|
|
35
40
|
elif isinstance(data, bytes):
|
|
36
41
|
# Leaf bytes
|
|
37
|
-
return data.decode(
|
|
42
|
+
return data.decode("utf-8")
|
|
38
43
|
else:
|
|
39
44
|
# Leaf non-bytes
|
|
40
45
|
return data
|
toil/lib/conversions.py
CHANGED
|
@@ -4,68 +4,99 @@ Also contains general conversion functions
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import math
|
|
7
|
-
from typing import
|
|
7
|
+
from typing import Optional, SupportsInt, Union
|
|
8
8
|
|
|
9
9
|
# See https://en.wikipedia.org/wiki/Binary_prefix
|
|
10
|
-
BINARY_PREFIXES = [
|
|
11
|
-
|
|
10
|
+
BINARY_PREFIXES = [
|
|
11
|
+
"ki",
|
|
12
|
+
"mi",
|
|
13
|
+
"gi",
|
|
14
|
+
"ti",
|
|
15
|
+
"pi",
|
|
16
|
+
"ei",
|
|
17
|
+
"kib",
|
|
18
|
+
"mib",
|
|
19
|
+
"gib",
|
|
20
|
+
"tib",
|
|
21
|
+
"pib",
|
|
22
|
+
"eib",
|
|
23
|
+
]
|
|
24
|
+
DECIMAL_PREFIXES = [
|
|
25
|
+
"b",
|
|
26
|
+
"k",
|
|
27
|
+
"m",
|
|
28
|
+
"g",
|
|
29
|
+
"t",
|
|
30
|
+
"p",
|
|
31
|
+
"e",
|
|
32
|
+
"kb",
|
|
33
|
+
"mb",
|
|
34
|
+
"gb",
|
|
35
|
+
"tb",
|
|
36
|
+
"pb",
|
|
37
|
+
"eb",
|
|
38
|
+
]
|
|
12
39
|
VALID_PREFIXES = BINARY_PREFIXES + DECIMAL_PREFIXES
|
|
13
40
|
|
|
14
41
|
|
|
15
|
-
def bytes_in_unit(unit: str =
|
|
42
|
+
def bytes_in_unit(unit: str = "B") -> int:
|
|
16
43
|
num_bytes = 1
|
|
17
|
-
if unit.lower() in [
|
|
44
|
+
if unit.lower() in ["ki", "kib"]:
|
|
18
45
|
num_bytes = 1 << 10
|
|
19
|
-
if unit.lower() in [
|
|
46
|
+
if unit.lower() in ["mi", "mib"]:
|
|
20
47
|
num_bytes = 1 << 20
|
|
21
|
-
if unit.lower() in [
|
|
48
|
+
if unit.lower() in ["gi", "gib"]:
|
|
22
49
|
num_bytes = 1 << 30
|
|
23
|
-
if unit.lower() in [
|
|
50
|
+
if unit.lower() in ["ti", "tib"]:
|
|
24
51
|
num_bytes = 1 << 40
|
|
25
|
-
if unit.lower() in [
|
|
52
|
+
if unit.lower() in ["pi", "pib"]:
|
|
26
53
|
num_bytes = 1 << 50
|
|
27
|
-
if unit.lower() in [
|
|
54
|
+
if unit.lower() in ["ei", "eib"]:
|
|
28
55
|
num_bytes = 1 << 60
|
|
29
56
|
|
|
30
|
-
if unit.lower() in [
|
|
57
|
+
if unit.lower() in ["k", "kb"]:
|
|
31
58
|
num_bytes = 1000
|
|
32
|
-
if unit.lower() in [
|
|
33
|
-
num_bytes = 1000
|
|
34
|
-
if unit.lower() in [
|
|
35
|
-
num_bytes = 1000
|
|
36
|
-
if unit.lower() in [
|
|
37
|
-
num_bytes = 1000
|
|
38
|
-
if unit.lower() in [
|
|
39
|
-
num_bytes = 1000
|
|
40
|
-
if unit.lower() in [
|
|
41
|
-
num_bytes = 1000
|
|
59
|
+
if unit.lower() in ["m", "mb"]:
|
|
60
|
+
num_bytes = 1000**2
|
|
61
|
+
if unit.lower() in ["g", "gb"]:
|
|
62
|
+
num_bytes = 1000**3
|
|
63
|
+
if unit.lower() in ["t", "tb"]:
|
|
64
|
+
num_bytes = 1000**4
|
|
65
|
+
if unit.lower() in ["p", "pb"]:
|
|
66
|
+
num_bytes = 1000**5
|
|
67
|
+
if unit.lower() in ["e", "eb"]:
|
|
68
|
+
num_bytes = 1000**6
|
|
42
69
|
return num_bytes
|
|
43
70
|
|
|
44
71
|
|
|
45
|
-
def convert_units(num: float,
|
|
46
|
-
src_unit: str,
|
|
47
|
-
dst_unit: str = 'B') -> float:
|
|
72
|
+
def convert_units(num: float, src_unit: str, dst_unit: str = "B") -> float:
|
|
48
73
|
"""Returns a float representing the converted input in dst_units."""
|
|
49
74
|
if not src_unit.lower() in VALID_PREFIXES:
|
|
50
|
-
raise RuntimeError(
|
|
75
|
+
raise RuntimeError(
|
|
76
|
+
f"{src_unit} not a valid unit, valid units are {VALID_PREFIXES}."
|
|
77
|
+
)
|
|
51
78
|
if not dst_unit.lower() in VALID_PREFIXES:
|
|
52
|
-
raise RuntimeError(
|
|
79
|
+
raise RuntimeError(
|
|
80
|
+
f"{dst_unit} not a valid unit, valid units are {VALID_PREFIXES}."
|
|
81
|
+
)
|
|
53
82
|
return (num * bytes_in_unit(src_unit)) / bytes_in_unit(dst_unit)
|
|
54
83
|
|
|
55
84
|
|
|
56
|
-
def parse_memory_string(string: str) ->
|
|
85
|
+
def parse_memory_string(string: str) -> tuple[float, str]:
|
|
57
86
|
"""
|
|
58
87
|
Given a string representation of some memory (i.e. '1024 Mib'), return the
|
|
59
88
|
number and unit.
|
|
60
89
|
"""
|
|
61
90
|
for i, character in enumerate(string):
|
|
62
91
|
# find the first character of the unit
|
|
63
|
-
if character not in
|
|
92
|
+
if character not in "0123456789.-_ ":
|
|
64
93
|
units = string[i:].strip()
|
|
65
94
|
if not units.lower() in VALID_PREFIXES:
|
|
66
|
-
raise RuntimeError(
|
|
95
|
+
raise RuntimeError(
|
|
96
|
+
f"{units} not a valid unit, valid units are {VALID_PREFIXES}."
|
|
97
|
+
)
|
|
67
98
|
return float(string[:i]), units
|
|
68
|
-
return float(string),
|
|
99
|
+
return float(string), "b"
|
|
69
100
|
|
|
70
101
|
|
|
71
102
|
def human2bytes(string: str) -> int:
|
|
@@ -75,7 +106,7 @@ def human2bytes(string: str) -> int:
|
|
|
75
106
|
"""
|
|
76
107
|
value, unit = parse_memory_string(string)
|
|
77
108
|
|
|
78
|
-
return int(convert_units(value, src_unit=unit, dst_unit=
|
|
109
|
+
return int(convert_units(value, src_unit=unit, dst_unit="b"))
|
|
79
110
|
|
|
80
111
|
|
|
81
112
|
def bytes2human(n: SupportsInt) -> str:
|
|
@@ -84,47 +115,73 @@ def bytes2human(n: SupportsInt) -> str:
|
|
|
84
115
|
if n < 0:
|
|
85
116
|
raise ValueError("n < 0")
|
|
86
117
|
elif n < 1:
|
|
87
|
-
return
|
|
118
|
+
return "0 b"
|
|
88
119
|
|
|
89
120
|
power_level = math.floor(math.log(n, 1024))
|
|
90
|
-
units = (
|
|
121
|
+
units = ("b", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei")
|
|
91
122
|
|
|
92
123
|
unit = units[power_level if power_level < len(units) else -1]
|
|
93
124
|
value = convert_units(n, "b", unit)
|
|
94
|
-
return f
|
|
95
|
-
|
|
125
|
+
return f"{value:.1f} {unit}"
|
|
126
|
+
|
|
127
|
+
|
|
96
128
|
def b_to_mib(n: Union[int, float]) -> float:
|
|
97
129
|
"""
|
|
98
130
|
Convert a number from bytes to mibibytes.
|
|
99
131
|
"""
|
|
100
|
-
return convert_units(n,
|
|
132
|
+
return convert_units(n, "b", "mib")
|
|
101
133
|
|
|
102
134
|
|
|
103
135
|
def mib_to_b(n: Union[int, float]) -> float:
|
|
104
136
|
"""
|
|
105
137
|
Convert a number from mibibytes to bytes.
|
|
106
138
|
"""
|
|
107
|
-
return convert_units(n,
|
|
139
|
+
return convert_units(n, "mib", "b")
|
|
140
|
+
|
|
108
141
|
|
|
109
|
-
#General Conversions
|
|
142
|
+
# General Conversions
|
|
110
143
|
|
|
111
|
-
|
|
144
|
+
|
|
145
|
+
def hms_duration_to_seconds(hms: str) -> float:
|
|
112
146
|
"""
|
|
113
|
-
Parses a given time string in hours:minutes:seconds,
|
|
147
|
+
Parses a given time string in hours:minutes:seconds,
|
|
114
148
|
returns an equivalent total seconds value
|
|
115
149
|
"""
|
|
116
|
-
vals_to_convert = hms.split(
|
|
150
|
+
vals_to_convert = hms.split(":")
|
|
117
151
|
seconds = 0.0
|
|
118
|
-
|
|
152
|
+
|
|
119
153
|
for val in vals_to_convert:
|
|
120
|
-
if
|
|
154
|
+
if float(val) < 0:
|
|
121
155
|
raise ValueError("Invalid Time, negative value")
|
|
122
156
|
|
|
123
|
-
if
|
|
157
|
+
if len(vals_to_convert) != 3:
|
|
124
158
|
raise ValueError("Invalid amount of fields, function takes input in 'hh:mm:ss'")
|
|
125
159
|
|
|
126
|
-
seconds += float(vals_to_convert[0]) * 60 * 60
|
|
127
|
-
seconds += float(vals_to_convert[1]) * 60
|
|
128
|
-
seconds += float(vals_to_convert[2])
|
|
160
|
+
seconds += float(vals_to_convert[0]) * 60 * 60
|
|
161
|
+
seconds += float(vals_to_convert[1]) * 60
|
|
162
|
+
seconds += float(vals_to_convert[2])
|
|
129
163
|
|
|
130
164
|
return seconds
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def strtobool(val: str) -> bool:
|
|
168
|
+
"""
|
|
169
|
+
Make a human-readable string into a bool.
|
|
170
|
+
|
|
171
|
+
Convert a string along the lines of "y", "1", "ON", "TrUe", or
|
|
172
|
+
"Yes" to True, and the corresponding false-ish values to False.
|
|
173
|
+
"""
|
|
174
|
+
# We only track prefixes, so "y" covers "y", "yes",
|
|
175
|
+
# and "yeah no" and makes them all True.
|
|
176
|
+
TABLE = {True: ["1", "on", "y", "t"], False: ["0", "off", "n", "f"]}
|
|
177
|
+
lowered = val.lower()
|
|
178
|
+
for result, prefixes in TABLE.items():
|
|
179
|
+
for prefix in prefixes:
|
|
180
|
+
if lowered.startswith(prefix):
|
|
181
|
+
return result
|
|
182
|
+
raise ValueError(f'Cannot convert "{val}" to a bool')
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def opt_strtobool(b: Optional[str]) -> Optional[bool]:
|
|
186
|
+
"""Convert an optional string representation of bool to None or bool"""
|
|
187
|
+
return b if b is None else strtobool(b)
|