Repository: DataBiosphere/toil

Issue:
Option parse error with --disableCaching
When you pass `--disableCaching` as last option on the command line, you get a `RuntimeError` in `parseBool()`.
```
$ toil-cwl-runner --disableCaching cwltool cwljob
Traceback (most recent call last):
  File "/home/rapthor-mloose/code/toil/venv/bin/toil-cwl-runner", line 11, in <module>
    load_entry_point('toil', 'console_scripts', 'toil-cwl-runner')()
  File "/home/rapthor-mloose/code/toil/src/toil/cwl/cwltoil.py", line 3098, in main
    options = parser.parse_args([workdir] + args)
  File "/usr/lib64/python3.6/argparse.py", line 1734, in parse_args
    args, argv = self.parse_known_args(args, namespace)
  File "/usr/lib64/python3.6/argparse.py", line 1766, in parse_known_args
    namespace, args = self._parse_known_args(args, namespace)
  File "/usr/lib64/python3.6/argparse.py", line 1972, in _parse_known_args
    start_index = consume_optional(start_index)
  File "/usr/lib64/python3.6/argparse.py", line 1912, in consume_optional
    take_action(action, args, option_string)
  File "/usr/lib64/python3.6/argparse.py", line 1824, in take_action
    argument_values = self._get_values(action, argument_strings)
  File "/usr/lib64/python3.6/argparse.py", line 2265, in _get_values
    value = self._get_value(action, arg_string)
  File "/usr/lib64/python3.6/argparse.py", line 2294, in _get_value
    result = type_func(arg_string)
  File "/home/rapthor-mloose/code/toil/src/toil/common.py", line 659, in parseBool
    raise RuntimeError("Could not interpret \"%s\" as a boolean value" % val)
RuntimeError: Could not interpret "cwltool" as a boolean value
```
This can be circumvented by adding `=yes` to the option, but that should not be necessary.

┆Issue is synchronized with this [Jira Task](https://ucsc-cgl.atlassian.net/browse/TOIL-1048)
┆Issue Number: TOIL-1048



Discussion:
Previously we've accepted `--disableCaching false` and `--disableCaching true`; I don't think we have a way in `argparse` to backtrack out of taking an argument to `--disableCaching` if it doesn't look boolean-y and make it a positional argument instead.

We could make `--disableCaching` a plain flag that can't accept arguments, but that would break some currently working command lines.

We could also provide a new non-argument-supporting flag to disable caching.

Or we could hackily preprocess the arguments before parsing them properly to shove a `--` or a value in after this argument if the thing after it doesn't look bool-y.

We have several of these `nargs='?'` optional arguments, like `--writeLogs` and `--defaultPreemptable`, which will consume the first positional argument after them if they are the last flag before the positional arguments. None of the other `--disableThing` options are like this, though.

The best thing to do might be to break compatibility here, because they way we currently do it is weird. I think we ended up this way because we started out intending caching to be on by default, but then it was broken for ages, so we changed the default to off and let you say `--disableCaching false` to turn it back on. Looks like that turned out to be a bad idea.

Resolving patch:
diff --git a/src/toil/common.py b/src/toil/common.py
--- a/src/toil/common.py
+++ b/src/toil/common.py
@@ -25,7 +25,7 @@
 
 from urllib.parse import urlparse
 from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser, _ArgumentGroup
-from typing import Any, Callable, List, Optional, Tuple, TypeVar, Union
+from typing import Any, Callable, Dict, List, Optional, Tuple, TypeVar, Union
 
 import requests
 
@@ -476,7 +476,7 @@ def addOptions(parser: ArgumentParser, config: Config = Config()):
     move_exports.add_argument("--moveExports", dest="moveExports", action='store_true', help=move_exports_help)
     move_exports.add_argument("--noMoveExports", dest="moveExports", action='store_false', help=move_exports_help)
     move_exports.set_defaults(moveExports=False)
-    file_store_options.add_argument('--disableCaching', dest='disableCaching', type='bool', nargs='?', const=True,
+    file_store_options.add_argument('--disableCaching', dest='disableCaching', action='store_true',
                                     default=False,
                                     help='Disables caching in the file store. This flag must be set to use '
                                          'a batch system that does not support cleanup, such as Parasol.')


Test patch:
diff --git a/src/toil/test/cwl/cwlTest.py b/src/toil/test/cwl/cwlTest.py
--- a/src/toil/test/cwl/cwlTest.py
+++ b/src/toil/test/cwl/cwlTest.py
@@ -101,9 +101,11 @@ def run_conformance_tests(workDir: str, yml: str, caching: bool = False, batchSy
             # Otherwise dump all output to our output stream
             cmd.append('--verbose')
 
-        args_passed_directly_to_toil = [f'--disableCaching={not caching}',
-                                        '--clean=always',
-                                        '--logDebug'] + extra_args
+        args_passed_directly_to_toil = ['--clean=always', '--logDebug']
+        if not caching:
+            # Turn off caching for the run
+            args_passed_directly_to_toil.append('--disableCaching')
+        args_passed_directly_to_toil += extra_args
 
         if 'SINGULARITY_DOCKER_HUB_MIRROR' in os.environ:
             args_passed_directly_to_toil.append('--setEnv=SINGULARITY_DOCKER_HUB_MIRROR')
@@ -629,7 +631,7 @@ def test_usage_message(self):
         cwl_job_json = 'test/cwl/revsort-job.json'
         jobstore = 'delete-test-toil'
         random_option_1 = '--logInfo'
-        random_option_2 = '--disableCaching=false'
+        random_option_2 = '--disableChaining'
         cmd_wrong_ordering_1 = [toil, cwl, cwl_job_json, jobstore, random_option_1, random_option_2]
         cmd_wrong_ordering_2 = [toil, cwl, jobstore, random_option_1, random_option_2, cwl_job_json]
         cmd_wrong_ordering_3 = [toil, jobstore, random_option_1, random_option_2, cwl, cwl_job_json]
diff --git a/src/toil/test/docs/scripts/example_cachingbenchmark.py b/src/toil/test/docs/scripts/example_cachingbenchmark.py
--- a/src/toil/test/docs/scripts/example_cachingbenchmark.py
+++ b/src/toil/test/docs/scripts/example_cachingbenchmark.py
@@ -6,13 +6,13 @@
 
 Invoke like:
 
-    python examples/example_cachingbenchmark.py ./jobstore --realTimeLogging --logInfo --disableCaching False
+    python examples/example_cachingbenchmark.py ./jobstore --realTimeLogging --logInfo
 
-    python examples/example_cachingbenchmark.py ./jobstore --realTimeLogging --logInfo --disableCaching True
+    python examples/example_cachingbenchmark.py ./jobstore --realTimeLogging --logInfo --disableCaching
 
-    python examples/example_cachingbenchmark.py aws:us-west-2:cachingjobstore --realTimeLogging --logInfo --disableCaching False
+    python examples/example_cachingbenchmark.py aws:us-west-2:cachingjobstore --realTimeLogging --logInfo
 
-    python examples/example_cachingbenchmark.py aws:us-west-2:cachingjobstore --realTimeLogging --logInfo --disableCaching True
+    python examples/example_cachingbenchmark.py aws:us-west-2:cachingjobstore --realTimeLogging --logInfo --disableCaching
 
 """