Skip to content

Commit 1d9d1e7

Browse files
tetronmr-c
andauthored
outdir can be any valid URI ; add test for this (#1552)
* Fix to work with URI outdir, don't double-quote. * Test that external outdir URI is handled correctly Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <[email protected]> * exempt stdfsaccess from compilation instead of refactoring it * sort inputs * remove unused imports * Add module level docstring Co-authored-by: Michael R. Crusoe <[email protected]>
1 parent 30800a2 commit 1d9d1e7

File tree

10 files changed

+68
-21
lines changed

10 files changed

+68
-21
lines changed

cwltool/command_line_tool.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -200,11 +200,17 @@ def revmap_file(
200200
outside the container. Recognizes files in the pathmapper or remaps
201201
internal output directories to the external directory.
202202
"""
203-
outdir_uri, outdir_path = file_uri(str(outdir)), outdir
204203

205204
# builder.outdir is the inner (container/compute node) output directory
206205
# outdir is the outer (host/storage system) output directory
207206

207+
if outdir.startswith("/"):
208+
# local file path, turn it into a file:// URI
209+
outdir = file_uri(outdir)
210+
211+
# note: outer outdir should already be a URI and should not be URI
212+
# quoted any further.
213+
208214
if "location" in f and "path" not in f:
209215
location = cast(str, f["location"])
210216
if location.startswith("file://"):
@@ -234,9 +240,9 @@ def revmap_file(
234240
):
235241
f["location"] = revmap_f[1]
236242
elif (
237-
uripath == outdir_uri
238-
or uripath.startswith(outdir_uri + os.sep)
239-
or uripath.startswith(outdir_uri + "/")
243+
uripath == outdir
244+
or uripath.startswith(outdir + os.sep)
245+
or uripath.startswith(outdir + "/")
240246
):
241247
f["location"] = uripath
242248
elif (
@@ -245,9 +251,9 @@ def revmap_file(
245251
or path.startswith(builder.outdir + "/")
246252
):
247253
joined_path = builder.fs_access.join(
248-
outdir_path, path[len(builder.outdir) + 1 :]
254+
outdir, urllib.parse.quote(path[len(builder.outdir) + 1 :])
249255
)
250-
f["location"] = file_uri(joined_path)
256+
f["location"] = joined_path
251257
else:
252258
raise WorkflowException(
253259
"Output file path %s must be within designated output directory (%s) or an input "

cwltool/errors.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,3 @@ class UnsupportedRequirement(WorkflowException):
88

99
class ArgumentException(Exception):
1010
"""Mismatched command line arguments provided."""
11-
12-
pass

cwltool/executors.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from .provenance_profile import ProvenanceProfile
3434
from .task_queue import TaskQueue
3535
from .update import ORIGINAL_CWLVERSION
36-
from .utils import CWLObjectType, CWLOutputType, JobsType
36+
from .utils import CWLObjectType, JobsType
3737
from .workflow import Workflow
3838
from .workflow_job import WorkflowJob, WorkflowJobStep
3939

cwltool/job.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import time
1414
import uuid
1515
from abc import ABCMeta, abstractmethod
16-
from io import IOBase
1716
from threading import Timer
1817
from typing import (
1918
IO,
@@ -451,15 +450,13 @@ def _required_env(self) -> Dict[str, str]:
451450
Note that with containers, the paths will (likely) be those from
452451
inside.
453452
"""
454-
pass
455453

456454
def _preserve_environment_on_containers_warning(
457455
self, varname: Optional[Iterable[str]] = None
458456
) -> None:
459457
"""When running in a container, issue a warning."""
460458
# By default, don't do anything; ContainerCommandLineJob below
461459
# will issue a warning.
462-
pass
463460

464461
def prepare_environment(
465462
self, runtimeContext: RuntimeContext, envVarReq: Mapping[str, str]

cwltool/load_tool.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
"""Loads a CWL document."""
22

3+
import copy
34
import hashlib
45
import logging
56
import os
67
import re
78
import urllib
89
import uuid
9-
import copy
1010
from typing import (
1111
Any,
1212
Dict,

cwltool/provenance_profile.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
from .stdfsaccess import StdFsAccess
4848
from .utils import (
4949
CWLObjectType,
50-
CWLOutputType,
5150
JobsType,
5251
get_listing,
5352
posix_path,

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
"cwltool/secrets.py",
7474
"cwltool/singularity.py",
7575
"cwltool/software_requirements.py",
76-
"cwltool/stdfsaccess.py",
76+
# "cwltool/stdfsaccess.py", # StdFsAccess needs to be subclassable
7777
"cwltool/subgraph.py",
7878
"cwltool/update.py",
7979
"cwltool/utils.py",

tests/test_environment.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,13 @@ class CheckHolder(ABC):
6363
@abstractmethod
6464
def checks(tmp_prefix: str) -> EnvChecks:
6565
"""Return a mapping from environment variable names to how to check for correctness."""
66-
pass
6766

6867
# Any flags to pass to cwltool to force use of the correct container
6968
flags: List[str]
7069

7170
# Does the env tool (maybe in our container) accept a `-0` flag?
7271
env_accepts_null: bool
7372

74-
pass
75-
7673

7774
class NoContainer(CheckHolder):
7875
"""No containers at all, just run in the host."""

tests/test_load_tool.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1+
"""Tests for cwltool.load_tool."""
2+
import logging
13
from pathlib import Path
24

35
import pytest
4-
import logging
56

67
from cwltool.context import LoadingContext, RuntimeContext
78
from cwltool.errors import WorkflowException
89
from cwltool.load_tool import load_tool
10+
from cwltool.loghandler import _logger, configure_logging
911
from cwltool.process import use_custom_schema, use_standard_schema
1012
from cwltool.update import INTERNAL_VERSION
1113
from cwltool.utils import CWLObjectType
12-
from cwltool.loghandler import _logger, configure_logging
1314

1415
from .util import get_data
1516

tests/test_path_checks.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import urllib.parse
2+
from io import BytesIO
23
from pathlib import Path
3-
from typing import cast
4+
from typing import IO, Any, List, cast
45

56
import pytest
67
from ruamel.yaml.comments import CommentedMap
@@ -9,6 +10,7 @@
910
from cwltool.command_line_tool import CommandLineTool
1011
from cwltool.context import LoadingContext, RuntimeContext
1112
from cwltool.main import main
13+
from cwltool.stdfsaccess import StdFsAccess
1214
from cwltool.update import INTERNAL_VERSION
1315
from cwltool.utils import CWLObjectType
1416

@@ -105,6 +107,26 @@ def test_unicode_in_output_files(tmp_path: Path, filename: str) -> None:
105107
assert main(params) == 0
106108

107109

110+
class TestFsAccess(StdFsAccess):
111+
"""Stub fs access object that doesn't rely on the filesystem."""
112+
113+
def glob(self, pattern: str) -> List[str]:
114+
"""glob."""
115+
return [pattern]
116+
117+
def open(self, fn: str, mode: str) -> IO[Any]:
118+
"""open."""
119+
return BytesIO(b"aoeu")
120+
121+
def isfile(self, fn: str) -> bool:
122+
"""isfile."""
123+
return True
124+
125+
def size(self, fn: str) -> int:
126+
"""size."""
127+
return 4
128+
129+
108130
def test_clt_returns_specialchar_names(tmp_path: Path) -> None:
109131
"""Confirm that special characters in filenames do not cause problems."""
110132
loading_context = LoadingContext(
@@ -167,3 +189,30 @@ def test_clt_returns_specialchar_names(tmp_path: Path) -> None:
167189
assert result["basename"] == special
168190
assert result["nameroot"] == special
169191
assert str(result["location"]).endswith(urllib.parse.quote(special))
192+
193+
# Now test when outdir is a URI, make sure it doesn't get
194+
# incorrectly quoted as a file.
195+
builder = clt._init_job({}, RuntimeContext())
196+
builder.pathmapper = clt.make_path_mapper(
197+
builder.files, builder.stagedir, RuntimeContext(), True
198+
)
199+
builder.outdir = "/var/spool/cwl"
200+
fs_access = TestFsAccess("")
201+
202+
result = cast(
203+
CWLObjectType,
204+
clt.collect_output(
205+
output_schema,
206+
builder,
207+
"keep:ae755cd1b3cff63152ff4200f4dea7e9+52",
208+
fs_access,
209+
),
210+
)
211+
212+
assert result["class"] == "File"
213+
assert result["basename"] == special
214+
assert result["nameroot"] == special
215+
assert (
216+
result["location"]
217+
== "keep:ae755cd1b3cff63152ff4200f4dea7e9+52/%3A%3F%23%5B%5D%40%21%24%26%27%28%29%2A%2B%2C%3B%3D"
218+
)

0 commit comments

Comments
 (0)