Skip to content

Commit 56d5120

Browse files
tetronmr-c
andauthored
Update schema-salad dependency for bug fix (#1463)
* Update schema-salad dependency for bug fix Specifically common-workflow-language/schema_salad#408 * Use vocabulary in schema salad validation * Fixes internal Avro type change from File to org.w3id.cwl.cwl.File Co-authored-by: Michael R. Crusoe <[email protected]> Co-authored-by: Michael R. Crusoe <[email protected]>
1 parent 8ec1eb7 commit 56d5120

File tree

10 files changed

+75
-38
lines changed

10 files changed

+75
-38
lines changed

cwltool/builder.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@
4646
from .pathmapper import PathMapper
4747
from .provenance_profile import ProvenanceProfile # pylint: disable=unused-import
4848

49+
INPUT_OBJ_VOCAB: Dict[str, str] = {
50+
"Any": "https://w3id.org/cwl/salad#Any",
51+
"File": "https://w3id.org/cwl/cwl#File",
52+
"Directory": "https://w3id.org/cwl/cwl#Directory",
53+
}
54+
4955

5056
def content_limit_respected_read_bytes(f): # type: (IO[bytes]) -> bytes
5157
contents = f.read(CONTENT_LIMIT + 1)
@@ -272,7 +278,7 @@ def bind_input(
272278
avsc = self.names.get_name(cast(str, t["name"]), None)
273279
if not avsc:
274280
avsc = make_avsc_object(convert_to_dict(t), self.names)
275-
if validate(avsc, datum):
281+
if validate(avsc, datum, vocab=INPUT_OBJ_VOCAB):
276282
schema = copy.deepcopy(schema)
277283
schema["type"] = t
278284
if not value_from_expression:
@@ -376,7 +382,7 @@ def _capture_files(f: CWLObjectType) -> CWLObjectType:
376382
self.files.append(f)
377383
return f
378384

379-
if schema["type"] == "File":
385+
if schema["type"] == "org.w3id.cwl.cwl.File":
380386
datum = cast(CWLObjectType, datum)
381387
self.files.append(datum)
382388

@@ -516,7 +522,7 @@ def addsf(
516522
_capture_files,
517523
)
518524

519-
if schema["type"] == "Directory":
525+
if schema["type"] == "org.w3id.cwl.cwl.Directory":
520526
datum = cast(CWLObjectType, datum)
521527
ll = schema.get("loadListing") or self.loadListing
522528
if ll and ll != "no_listing":

cwltool/command_line_tool.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,12 @@
4040
from schema_salad.validate import validate_ex
4141
from typing_extensions import TYPE_CHECKING, Type
4242

43-
from .builder import Builder, content_limit_respected_read_bytes, substitute
43+
from .builder import (
44+
Builder,
45+
content_limit_respected_read_bytes,
46+
substitute,
47+
INPUT_OBJ_VOCAB,
48+
)
4449
from .context import LoadingContext, RuntimeContext, getdefault
4550
from .docker import DockerCommandLineJob
4651
from .errors import UnsupportedRequirement, WorkflowException
@@ -1110,7 +1115,11 @@ def collect_output_ports(
11101115
Schema, self.names.get_name("outputs_record_schema", None)
11111116
)
11121117
validate_ex(
1113-
expected_schema, ret, strict=False, logger=_logger_validation_warnings
1118+
expected_schema,
1119+
ret,
1120+
strict=False,
1121+
logger=_logger_validation_warnings,
1122+
vocab=INPUT_OBJ_VOCAB,
11141123
)
11151124
if ret is not None and builder.mutation_manager is not None:
11161125
adjustFileObjs(ret, builder.mutation_manager.set_generation)

cwltool/expression.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def next_seg(
134134
m = segment_re.match(remaining_string)
135135
if not m:
136136
return current_value
137-
next_segment_str = m.group(0)
137+
next_segment_str = m.group(1)
138138

139139
key = None # type: Optional[Union[str, int]]
140140
if next_segment_str[0] == ".":
@@ -146,7 +146,7 @@ def next_seg(
146146
if (
147147
isinstance(current_value, MutableSequence)
148148
and key == "length"
149-
and not remaining_string[m.end(0) :]
149+
and not remaining_string[m.end(1) :]
150150
):
151151
return len(current_value)
152152
if not isinstance(current_value, MutableMapping):
@@ -175,7 +175,7 @@ def next_seg(
175175
try:
176176
return next_seg(
177177
parsed_string + remaining_string,
178-
remaining_string[m.end(0) :],
178+
remaining_string[m.end(1) :],
179179
cast(CWLOutputType, current_value[cast(str, key)]),
180180
)
181181
except KeyError:
@@ -184,7 +184,7 @@ def next_seg(
184184
try:
185185
return next_seg(
186186
parsed_string + remaining_string,
187-
remaining_string[m.end(0) :],
187+
remaining_string[m.end(1) :],
188188
current_value[key],
189189
)
190190
except KeyError:

cwltool/process.py

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@
4444
from schema_salad.ref_resolver import Loader, file_uri, uri_file_path
4545
from schema_salad.schema import load_schema, make_avro_schema, make_valid_avro
4646
from schema_salad.sourceline import SourceLine, strip_dup_lineno
47-
from schema_salad.utils import convert_to_dict
48-
from schema_salad.validate import validate_ex
47+
from schema_salad.utils import ContextType, convert_to_dict
48+
from schema_salad.validate import validate_ex, avro_type_name
4949
from typing_extensions import TYPE_CHECKING
5050

5151
from . import expression
52-
from .builder import Builder, HasReqsHints
52+
from .builder import Builder, HasReqsHints, INPUT_OBJ_VOCAB
5353
from .context import LoadingContext, RuntimeContext, getdefault
5454
from .errors import UnsupportedRequirement, WorkflowException
5555
from .loghandler import _logger
@@ -457,30 +457,34 @@ def fill_in_defaults(
457457

458458

459459
def avroize_type(
460-
field_type: Union[
461-
CWLObjectType, MutableSequence[CWLOutputType], CWLOutputType, None
462-
],
460+
field_type: Union[CWLObjectType, MutableSequence[Any], CWLOutputType, None],
463461
name_prefix: str = "",
464-
) -> None:
462+
) -> Union[CWLObjectType, MutableSequence[Any], CWLOutputType, None]:
465463
"""Add missing information to a type so that CWL types are valid."""
466464
if isinstance(field_type, MutableSequence):
467-
for field in field_type:
468-
avroize_type(field, name_prefix)
465+
for i, field in enumerate(field_type):
466+
field_type[i] = avroize_type(field, name_prefix)
469467
elif isinstance(field_type, MutableMapping):
470468
if field_type["type"] in ("enum", "record"):
471469
if "name" not in field_type:
472470
field_type["name"] = name_prefix + str(uuid.uuid4())
473471
if field_type["type"] == "record":
474-
avroize_type(
472+
field_type["fields"] = avroize_type(
475473
cast(MutableSequence[CWLOutputType], field_type["fields"]), name_prefix
476474
)
477-
if field_type["type"] == "array":
478-
avroize_type(
475+
elif field_type["type"] == "array":
476+
field_type["items"] = avroize_type(
479477
cast(MutableSequence[CWLOutputType], field_type["items"]), name_prefix
480478
)
481-
if isinstance(field_type["type"], MutableSequence):
482-
for ctype in field_type["type"]:
483-
avroize_type(cast(CWLOutputType, ctype), name_prefix)
479+
else:
480+
field_type["type"] = avroize_type(
481+
cast(CWLOutputType, field_type["type"]), name_prefix
482+
)
483+
elif field_type == "File":
484+
return "org.w3id.cwl.cwl.File"
485+
elif field_type == "Directory":
486+
return "org.w3id.cwl.cwl.Directory"
487+
return field_type
484488

485489

486490
def get_overrides(
@@ -635,6 +639,7 @@ def __init__(
635639
sdtypes,
636640
{cast(str, t["name"]): cast(Dict[str, Any], t) for t in sdtypes},
637641
set(),
642+
vocab=INPUT_OBJ_VOCAB,
638643
)
639644
for i in av:
640645
self.schemaDefs[i["name"]] = i # type: ignore
@@ -669,7 +674,8 @@ def __init__(
669674
c["type"] = nullable
670675
else:
671676
c["type"] = c["type"]
672-
avroize_type(c["type"], c["name"])
677+
678+
c["type"] = avroize_type(c["type"], c["name"])
673679
if key == "inputs":
674680
cast(
675681
List[CWLObjectType], self.inputs_record_schema["fields"]
@@ -709,9 +715,13 @@ def __init__(
709715
)
710716
raise
711717
if self.doc_schema is not None:
718+
classname = toolpath_object["class"]
719+
avroname = classname
720+
if self.doc_loader and classname in self.doc_loader.vocab:
721+
avroname = avro_type_name(self.doc_loader.vocab[classname])
712722
validate_js_expressions(
713723
toolpath_object,
714-
self.doc_schema.names[toolpath_object["class"]],
724+
self.doc_schema.names[avroname],
715725
validate_js_options,
716726
)
717727

@@ -778,7 +788,13 @@ def _init_job(
778788
raise WorkflowException(
779789
"Missing input record schema: " "{}".format(self.names)
780790
)
781-
validate_ex(schema, job, strict=False, logger=_logger_validation_warnings)
791+
validate_ex(
792+
schema,
793+
job,
794+
strict=False,
795+
logger=_logger_validation_warnings,
796+
vocab=INPUT_OBJ_VOCAB,
797+
)
782798

783799
if load_listing and load_listing != "no_listing":
784800
get_listing(fs_access, job, recursive=(load_listing == "deep_listing"))
@@ -1015,13 +1031,16 @@ def evalResources(
10151031
def validate_hints(
10161032
self, avsc_names: Names, hints: List[CWLObjectType], strict: bool
10171033
) -> None:
1034+
if self.doc_loader is None:
1035+
return
10181036
for i, r in enumerate(hints):
10191037
sl = SourceLine(hints, i, ValidationException)
10201038
with sl:
1021-
if (
1022-
avsc_names.get_name(cast(str, r["class"]), None) is not None
1023-
and self.doc_loader is not None
1024-
):
1039+
classname = cast(str, r["class"])
1040+
avroname = classname
1041+
if classname in self.doc_loader.vocab:
1042+
avroname = avro_type_name(self.doc_loader.vocab[classname])
1043+
if avsc_names.get_name(avroname, None) is not None:
10251044
plain_hint = {
10261045
key: r[key]
10271046
for key in r
@@ -1030,10 +1049,11 @@ def validate_hints(
10301049
validate_ex(
10311050
cast(
10321051
Schema,
1033-
avsc_names.get_name(cast(str, plain_hint["class"]), None),
1052+
avsc_names.get_name(avroname, None),
10341053
),
10351054
plain_hint,
10361055
strict=strict,
1056+
vocab=self.doc_loader.vocab,
10371057
)
10381058
elif r["class"] in ("NetworkAccess", "LoadListingRequirement"):
10391059
pass

cwltool/validate_js.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def is_expression(tool, schema):
3939
# type: (Any, Optional[Schema]) -> bool
4040
return (
4141
isinstance(schema, EnumSchema)
42-
and schema.name == "Expression"
42+
and schema.name == "org.w3id.cwl.cwl.Expression"
4343
and isinstance(tool, str)
4444
)
4545

@@ -76,6 +76,7 @@ def get_expressions(
7676
tool,
7777
raise_ex=False,
7878
logger=_logger_validation_warnings,
79+
vocab={},
7980
):
8081
valid_schema = possible_schema
8182

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ requests>=2.4.3
22
ruamel.yaml>=0.15,<0.17.11
33
rdflib>=4.2.2,<5.1
44
shellescape>=3.4.1,<3.9
5-
schema-salad>=7.1.20210518142926,<8
5+
schema-salad>=8.1.20210627200047,<9
66
prov==1.5.1
77
bagit==1.8.1
88
mypy-extensions

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@
113113
"shellescape >= 3.4.1, < 3.9",
114114
# 7.1.20210518142926 or later required due to
115115
# https://github.com/common-workflow-language/schema_salad/issues/385
116-
"schema-salad >= 7.1.20210518142926, < 8",
116+
"schema-salad >= 8.1.20210627200047, < 9",
117117
"mypy-extensions",
118118
"psutil >= 5.6.6",
119119
"prov == 1.5.1",

tests/test_procgenerator.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def test_missing_enable_ext(monkeypatch: pytest.MonkeyPatch) -> None:
2020
assert (
2121
main(
2222
[
23+
"--debug",
2324
"--enable-ext",
2425
"--enable-dev",
2526
get_data("tests/wf/generator/zing.cwl"),

tests/test_validate_js.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def test_get_expressions() -> None:
2828
test_cwl_yaml = yaml.load(TEST_CWL)
2929
schema = process.get_schema("v1.0")[1]
3030
assert isinstance(schema, Names)
31-
clt_schema = schema.names["CommandLineTool"]
31+
clt_schema = schema.names["org.w3id.cwl.cwl.CommandLineTool"]
3232

3333
exprs = validate_js.get_expressions(test_cwl_yaml, clt_schema)
3434

@@ -40,7 +40,7 @@ def test_validate_js_expressions(mocker: Any) -> None:
4040
test_cwl_yaml = yaml.load(TEST_CWL)
4141
schema = process.get_schema("v1.0")[1]
4242
assert isinstance(schema, Names)
43-
clt_schema = schema.names["CommandLineTool"]
43+
clt_schema = schema.names["org.w3id.cwl.cwl.CommandLineTool"]
4444

4545
mocker.patch("cwltool.validate_js._logger")
4646
# mocker.patch("cwltool.validate_js.print_js_hint_messages")

tests/wf/generator/pytoolgen.cwl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
cwlVersion: v1.0
33
$namespaces:
44
cwltool: "http://commonwl.org/cwltool#"
5-
class: ProcessGenerator
5+
class: cwltool:ProcessGenerator
66
inputs:
77
script: string
88
dir: Directory

0 commit comments

Comments
 (0)