diff --git a/WDL/CLI.py b/WDL/CLI.py index d1625dd3..40f755b1 100644 --- a/WDL/CLI.py +++ b/WDL/CLI.py @@ -1000,6 +1000,20 @@ def runner_input_value(s_value, ty, file_found, root): elif not (file_found and file_found(fn)): # maybe URI raise Error.InputError("File not found: " + fn) return Value.File(fn) + if isinstance(ty, Type.Directory): + dn = os.path.expanduser(s_value) + if os.path.isdir(dn): + dn = os.path.abspath(dn) + if not path_really_within(dn, root): + raise Error.InputError( + f"all input paths must be located within the configured `file_io.root' directory `{root}' " + f"unlike `{dn}'" + ) + # TODO: courtesy check for symlinks that have absolute paths or relatively point + # outside the directory + else: # TODO: relax for URIs + raise Error.InputError("Directory not found: " + dn) + return Value.Directory(dn) if isinstance(ty, Type.Boolean): if s_value == "true": return Value.Boolean(True) @@ -1241,7 +1255,7 @@ def localize( doc = load(wdlfile, path or [], check_quant=check_quant, read_source=read_source) def file_found(fn): - return runtime.download.able(cfg, fn) or os.path.isfile(fn) + return runtime.download.able(cfg, fn) or os.path.exists(fn) try: target, input_env, input_json = runner_input( diff --git a/WDL/Value.py b/WDL/Value.py index cb2b2c39..92d1e6cc 100644 --- a/WDL/Value.py +++ b/WDL/Value.py @@ -186,11 +186,7 @@ class Directory(String): def coerce(self, desired_type: Optional[Type.Base] = None) -> Base: "" - if self.value is None: - if isinstance(desired_type, Type.Directory) and desired_type.optional: - return Null(self.expr) - else: - raise FileNotFoundError() + # TODO: similar coercion logic for Directory? outputs when we support those return super().coerce(desired_type) diff --git a/tests/runner.t b/tests/runner.t index 44b474df..55728f11 100644 --- a/tests/runner.t +++ b/tests/runner.t @@ -11,7 +11,7 @@ source tests/bash-tap/bash-tap-bootstrap export PYTHONPATH="$SOURCE_DIR:$PYTHONPATH" miniwdl="python3 -m WDL" -plan tests 53 +plan tests 55 $miniwdl run_self_test is "$?" "0" "run_self_test" @@ -246,6 +246,41 @@ $miniwdl run --copy-input-files mv_input_file.wdl file=quick is "$?" "0" "copy input files" is "$(basename `jq -r '.["mv_input_file.xxx"]' _LAST/outputs.json`)" "xxx" "updated _LAST" +cat << 'EOF' > dir_io.wdl +version development +workflow w { + input { + Directory d + } + call t { + input: + d = d + } + output { + Int dsz = round(size(t.files)) + } +} +task t { + input { + Directory d + } + command <<< + mkdir outdir + find ~{d} -type f | xargs -i{} cp {} outdir/ + >>> + output { + Array[File] files = glob("outdir/*") + } +} +EOF + +mkdir -p indir/subdir +echo alice > indir/alice.txt +echo bob > indir/subdir/bob.txt +miniwdl run dir_io.wdl d=indir +is "$?" "0" "directory input" +is `jq -r '.["w.dsz"]' _LAST/outputs.json` "10" "use of directory input" + cat << 'EOF' > uri_inputs.json {"my_workflow.files": ["https://google.com/robots.txt", "https://raw.githubusercontent.com/chanzuckerberg/miniwdl/main/tests/alyssa_ben.txt"]} EOF diff --git a/tests/test_7runner.py b/tests/test_7runner.py index c8cb5b2d..b974c5d5 100644 --- a/tests/test_7runner.py +++ b/tests/test_7runner.py @@ -57,6 +57,12 @@ def _run(self, wdl:str, inputs = None, expected_exception: Exception = None, cfg return WDL.values_to_json(outputs) class TestDirectoryIO(RunnerTestCase): + def test_coercion(self): + assert WDL.Type.Directory().coerces(WDL.Type.String()) + d = WDL.Value.String("foo").coerce(WDL.Type.Directory()) + assert isinstance(d, WDL.Value.Directory) + assert d.value == "foo" + def test_basic_directory(self): wdl = R""" version development