Repository: common-workflow-language/cwltool

Issue:
'CommentedMap' object has no attribute 'decode' when parsing CWL with invalid input source
## Expected Behavior
When the `source` of a step input in a workflow is invalid, `cwltool` should report that error by printing a normal hierarchical validation error.

## Actual Behavior
When `cwltool` encounters this source error, it tries to throw an exception, but in doing so calls the `shortname` method on the invalid ID, which causes the exception `'CommentedMap' object has no attribute 'decode'` that is hard to decipher.

## Workflow Code
Unfortunately I have found it difficult to create a minimal example of this error. However, if you clone this repository: https://github.com/TMiguelT/CWL-Bug, you should be able to replicate it. If you run (from the repository root), `cwltool --validate --debug workflows/cpipe.broken.cwl`, the bug should occur. If instead you run `cwltool --validate --debug workflows/cpipe.cwl`, which does not have this invalid input source, this bug will not occur. Not that the difference between these two files is on line 222: https://github.com/TMiguelT/CWL-Bug/blob/master/workflows/cpipe.broken.cwl#L222.

## Full Traceback
```pytb
/vlsci/VR0320/milton/CWL-Bug/venv/bin/cwltool 1.0.20170928192020
Resolved 'workflows/cpipe.broken.cwl' to 'file:///vlsci/VR0320/milton/cwl_bug/cpipe-out-rabix/workflows/cpipe.broken.cwl'
I'm sorry, I couldn't load this CWL file.  The error was:
Traceback (most recent call last):
  File "/vlsci/VR0320/milton/CWL-Bug/venv/lib/python3.5/site-packages/cwltool/main.py", line 837, in main
    makeTool, make_tool_kwds)
  File "/vlsci/VR0320/milton/CWL-Bug/venv/lib/python3.5/site-packages/cwltool/load_tool.py", line 263, in make_tool
    tool = makeTool(processobj, **kwargs)
  File "/vlsci/VR0320/milton/CWL-Bug/venv/lib/python3.5/site-packages/cwltool/workflow.py", line 40, in defaultMakeTool
    return Workflow(toolpath_object, **kwargs)
  File "/vlsci/VR0320/milton/CWL-Bug/venv/lib/python3.5/site-packages/cwltool/workflow.py", line 522, in __init__
    self.steps.append(WorkflowStep(step, n, **kwargs))
  File "/vlsci/VR0320/milton/CWL-Bug/venv/lib/python3.5/site-packages/cwltool/workflow.py", line 716, in __init__
    + "\n" + SourceLine(self.embedded_tool.tool, "outputs").makeError(
  File "/vlsci/VR0320/milton/CWL-Bug/venv/lib/python3.5/site-packages/cwltool/process.py", line 170, in shortname
    d = urllib.parse.urlparse(inputid)
  File "/usr/local/python/3.5.2-intel/lib/python3.5/urllib/parse.py", line 294, in urlparse
    url, scheme, _coerce_result = _coerce_args(url, scheme)
  File "/usr/local/python/3.5.2-intel/lib/python3.5/urllib/parse.py", line 114, in _coerce_args
    return _decode_args(args) + (_encode_result,)
  File "/usr/local/python/3.5.2-intel/lib/python3.5/urllib/parse.py", line 98, in _decode_args
    return tuple(x.decode(encoding, errors) if x else '' for x in args)
  File "/usr/local/python/3.5.2-intel/lib/python3.5/urllib/parse.py", line 98, in <genexpr>
    return tuple(x.decode(encoding, errors) if x else '' for x in args)
AttributeError: 'CommentedMap' object has no attribute 'decode'
```

## Your Environment
* cwltool version: 1.0.20170928192020
* Python version: Python 3.5.2
* Red Hat Enterprise Linux Server release 6.9 (Santiago) (also replicated on Ubuntu 16.04)


Discussion:
@TMiguelT Thank you for your report, I agree that we need a better response here
Hello, @mr-c! Any updates here? We have just met this error!

Resolving patch:
diff --git a/cwltool/workflow.py b/cwltool/workflow.py
--- a/cwltool/workflow.py
+++ b/cwltool/workflow.py
@@ -672,15 +672,19 @@ def __init__(self,
                         param["type"] = "Any"
                         param["not_connected"] = True
                     else:
+                        if isinstance(step_entry, Mapping):
+                            step_entry_name = step_entry['id']
+                        else:
+                            step_entry_name = step_entry
                         validation_errors.append(
                             SourceLine(self.tool["out"], index).makeError(
                                 "Workflow step output '%s' does not correspond to"
-                                % shortname(step_entry))
+                                % shortname(step_entry_name))
                             + "\n" + SourceLine(self.embedded_tool.tool, "outputs").makeError(
                                 "  tool output (expected '%s')" % (
                                     "', '".join(
                                         [shortname(tool_entry["id"]) for tool_entry in
-                                         self.embedded_tool.tool[toolfield]]))))
+                                         self.embedded_tool.tool['outputs']]))))
                 param["id"] = inputid
                 param.lc.line = toolpath_object[stepfield].lc.data[index][0]
                 param.lc.col = toolpath_object[stepfield].lc.data[index][1]


Test patch:
diff --git a/tests/checker_wf/broken-wf3.cwl b/tests/checker_wf/broken-wf3.cwl
new file mode 100755
--- /dev/null
+++ b/tests/checker_wf/broken-wf3.cwl
@@ -0,0 +1,45 @@
+#!/usr/bin/env cwl-runner
+class: Workflow
+cwlVersion: v1.0
+requirements:
+  ScatterFeatureRequirement: {}
+  MultipleInputFeatureRequirement: {}
+  StepInputExpressionRequirement: {}
+inputs:
+  letters0:
+    type: [string, int]
+    default: "a0"
+  letters1:
+    type: string[]
+    default: ["a1", "b1"]
+  letters2:
+    type: [string, int]
+    default: "a2"
+  letters3:
+    type: string[]
+    default: ["a3", "b3"]
+  letters4:
+    type: string
+    default: "a4"
+  letters5:
+    type: string[]
+    default: ["a5", "b5", "c5"]
+
+outputs:
+  all:
+    type: File
+    outputSource: cat/txt
+
+steps:
+  - id: embedded
+    run: functional-wf.cwl
+    in: []
+    out:
+     - id: All
+  - id: cat
+    run: cat.cwl
+    in:
+      - id: cat_in
+        source:
+         - embedded/All
+    out: [txt]
diff --git a/tests/test_examples.py b/tests/test_examples.py
--- a/tests/test_examples.py
+++ b/tests/test_examples.py
@@ -659,6 +659,9 @@ def test_static_checker():
     with pytest.raises(schema_salad.validate.ValidationException):
         factory.make(get_data("tests/checker_wf/broken-wf2.cwl"))
 
+    with pytest.raises(schema_salad.validate.ValidationException):
+        factory.make(get_data("tests/checker_wf/broken-wf3.cwl"))
+
 
 def test_var_spool_cwl_checker1():
     """Confirm that references to /var/spool/cwl are caught."""