Repository: common-workflow-language/cwltool

Issue:
Workflow that passes validation fails to pack
## Expected Behavior
If a workflow that passes validation (`cwltool --validate`) there should be no errors when packing.

## Actual Behavior
In this specific case, I tested the workflow https://raw.githubusercontent.com/Athanaseus/2gc-pipeline/master/selfcal_simulation.cwl .

It passes the validation (`cwltool --validate https://raw.githubusercontent.com/Athanaseus/2gc-pipeline/master/selfcal_simulation.cwl`) but fails when running:
```
cwltool --pack --debug \
https://raw.githubusercontent.com/Athanaseus/2gc-pipeline/master/selfcal_simulation.cwl > /dev/null
```

Also see CompEpigen/CWLab#38.
(@Athanaseus please have a look.)
## Workflow Code
Please find it at https://raw.githubusercontent.com/Athanaseus/2gc-pipeline/master/selfcal_simulation.cwl.

## Full Traceback
The error thrown was:
```
INFO /usr/local/bin/cwltool 1.0.20180809224403
ERROR I'm sorry, I couldn't load this CWL file.
The error was:
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/cwltool/main.py", line 916, in main
    print_pack(loadingContext.loader, processobj, uri, metadata)
  File "/usr/local/lib/python3.6/dist-packages/cwltool/main.py", line 563, in print_pack
    packed = pack(document_loader, processobj, uri, metadata)
  File "/usr/local/lib/python3.6/dist-packages/cwltool/pack.py", line 222, in pack
    import_embed(packed, set())
  File "/usr/local/lib/python3.6/dist-packages/cwltool/pack.py", line 118, in import_embed
    import_embed(d[k], seen)
  File "/usr/local/lib/python3.6/dist-packages/cwltool/pack.py", line 104, in import_embed
    import_embed(v, seen)
  File "/usr/local/lib/python3.6/dist-packages/cwltool/pack.py", line 118, in import_embed
    import_embed(d[k], seen)
  File "/usr/local/lib/python3.6/dist-packages/cwltool/pack.py", line 108, in import_embed
    if d[n] in seen:
TypeError: unhashable type: 'CommentedMap'
```

## Your Environment
* cwltool version: 1.0.20180809224403



Discussion:
Can you try with a new `cwltool` Version 1.0.20180809224403 is almost a year and a half old :-)

Resolving patch:
diff --git a/cwltool/pack.py b/cwltool/pack.py
--- a/cwltool/pack.py
+++ b/cwltool/pack.py
@@ -105,14 +105,15 @@ def import_embed(d, seen):
     elif isinstance(d, MutableMapping):
         for n in ("id", "name"):
             if n in d:
-                if d[n] in seen:
-                    this = d[n]
-                    d.clear()
-                    d["$import"] = this
-                else:
-                    this = d[n]
-                    seen.add(this)
-                    break
+                if isinstance(d[n], str):
+                    if d[n] in seen:
+                        this = d[n]
+                        d.clear()
+                        d["$import"] = this
+                    else:
+                        this = d[n]
+                        seen.add(this)
+                        break
 
         for k in sorted(d.keys()):
             import_embed(d[k], seen)


Test patch:
diff --git a/tests/test_pack.py b/tests/test_pack.py
--- a/tests/test_pack.py
+++ b/tests/test_pack.py
@@ -46,6 +46,33 @@ def test_pack():
     assert packed == expect_packed
 
 
+def test_pack_input_named_name():
+    loadingContext, workflowobj, uri = fetch_document(get_data("tests/wf/trick_revsort.cwl"))
+    loadingContext.do_update = False
+    loadingContext, uri = resolve_and_validate_document(
+        loadingContext, workflowobj, uri
+    )
+    processobj = loadingContext.loader.resolve_ref(uri)[0]
+
+    with open(get_data("tests/wf/expect_trick_packed.cwl")) as packed_file:
+        expect_packed = yaml.round_trip_load(packed_file)
+
+    packed = cwltool.pack.pack(
+        loadingContext.loader, processobj, uri, loadingContext.metadata
+    )
+    adjustFileObjs(
+        packed, partial(make_relative, os.path.abspath(get_data("tests/wf")))
+    )
+    adjustDirObjs(packed, partial(make_relative, os.path.abspath(get_data("tests/wf"))))
+
+    assert "$schemas" in packed
+    assert len(packed["$schemas"]) == len(expect_packed["$schemas"])
+    del packed["$schemas"]
+    del expect_packed["$schemas"]
+
+    assert packed == expect_packed
+
+
 def test_pack_single_tool():
     loadingContext, workflowobj, uri = fetch_document(
         get_data("tests/wf/formattest.cwl")
diff --git a/tests/wf/expect_trick_packed.cwl b/tests/wf/expect_trick_packed.cwl
new file mode 100644
--- /dev/null
+++ b/tests/wf/expect_trick_packed.cwl
@@ -0,0 +1,142 @@
+{
+    "$graph": [
+        {
+            "class": "CommandLineTool",
+            "doc": "Reverse each line using the `rev` command",
+            "inputs": [
+                {
+                    "type": "File",
+                    "inputBinding": {},
+                    "id": "#revtool.cwl/revtool_input"
+                }
+            ],
+            "outputs": [
+                {
+                    "type": "File",
+                    "outputBinding": {
+                        "glob": "output.txt"
+                    },
+                    "id": "#revtool.cwl/revtool_output"
+                }
+            ],
+            "baseCommand": "rev",
+            "stdout": "output.txt",
+            "id": "#revtool.cwl",
+            "$namespaces": {
+                "iana": "https://www.iana.org/assignments/media-types/"
+            }
+        },
+        {
+            "class": "CommandLineTool",
+            "doc": "Sort lines using the `sort` command",
+            "inputs": [
+                {
+                    "id": "#sorttool.cwl/reverse",
+                    "type": "boolean",
+                    "inputBinding": {
+                        "position": 1,
+                        "prefix": "--reverse"
+                    }
+                },
+                {
+                    "id": "#sorttool.cwl/sorted_input",
+                    "type": "File",
+                    "inputBinding": {
+                        "position": 2
+                    }
+                }
+            ],
+            "outputs": [
+                {
+                    "id": "#sorttool.cwl/sorted_output",
+                    "type": "File",
+                    "outputBinding": {
+                        "glob": "output.txt"
+                    }
+                }
+            ],
+            "baseCommand": "sort",
+            "stdout": "output.txt",
+            "id": "#sorttool.cwl"
+        },
+        {
+            "class": "Workflow",
+            "doc": "Reverse the lines in a document, then sort those lines.",
+            "hints": [
+                {
+                    "class": "DockerRequirement",
+                    "dockerPull": "debian:8"
+                }
+            ],
+            "inputs": [
+                {
+                    "type": "string",
+                    "doc": "Here to test for a bug in --pack",
+                    "id": "#main/name"
+                },
+                {
+                    "type": "boolean",
+                    "default": true,
+                    "doc": "If true, reverse (decending) sort",
+                    "id": "#main/reverse_sort"
+                },
+                {
+                    "type": "File",
+                    "doc": "The input file to be processed.",
+                    "format": "https://www.iana.org/assignments/media-types/text/plain",
+                    "default": {
+                        "class": "File",
+                        "location": "hello.txt"
+                    },
+                    "id": "#main/workflow_input"
+                }
+            ],
+            "outputs": [
+                {
+                    "type": "File",
+                    "outputSource": "#main/sorted/sorted_output",
+                    "doc": "The output with the lines reversed and sorted.",
+                    "id": "#main/sorted_output"
+                }
+            ],
+            "steps": [
+                {
+                    "in": [
+                        {
+                            "source": "#main/workflow_input",
+                            "id": "#main/rev/revtool_input"
+                        }
+                    ],
+                    "out": [
+                        "#main/rev/revtool_output"
+                    ],
+                    "run": "#revtool.cwl",
+                    "id": "#main/rev"
+                },
+                {
+                    "in": [
+                        {
+                            "source": "#main/reverse_sort",
+                            "id": "#main/sorted/reverse"
+                        },
+                        {
+                            "source": "#main/rev/revtool_output",
+                            "id": "#main/sorted/sorted_input"
+                        }
+                    ],
+                    "out": [
+                        "#main/sorted/sorted_output"
+                    ],
+                    "run": "#sorttool.cwl",
+                    "id": "#main/sorted"
+                }
+            ],
+            "id": "#main"
+        }
+    ],
+    "cwlVersion": "v1.0",
+    "$schemas": [
+        "empty2.ttl",
+        "empty.ttl"
+    ]
+}
\ No newline at end of file
diff --git a/tests/wf/trick_revsort.cwl b/tests/wf/trick_revsort.cwl
new file mode 100755
--- /dev/null
+++ b/tests/wf/trick_revsort.cwl
@@ -0,0 +1,79 @@
+#!/usr/bin/env cwl-runner
+#
+# This is a two-step workflow which uses "revtool" and "sorttool" defined above.
+#
+class: Workflow
+doc: "Reverse the lines in a document, then sort those lines."
+cwlVersion: v1.0
+
+# Requirements & hints specify prerequisites and extensions to the workflow.
+# In this example, DockerRequirement specifies a default Docker container
+# in which the command line tools will execute.
+hints:
+  - class: DockerRequirement
+    dockerPull: debian:8
+
+
+# The inputs array defines the structure of the input object that describes
+# the inputs to the workflow.
+#
+# The "reverse_sort" input parameter demonstrates the "default" field.  If the
+# field "reverse_sort" is not provided in the input object, the default value will
+# be used.
+inputs:
+  workflow_input:
+    type: File
+    doc: "The input file to be processed."
+    format: iana:text/plain
+    default:
+      class: File
+      location: hello.txt
+  reverse_sort:
+    type: boolean
+    default: true
+    doc: "If true, reverse (decending) sort"
+  name:
+    type: string
+    doc: "Here to test for a bug in --pack"
+
+# The "outputs" array defines the structure of the output object that describes
+# the outputs of the workflow.
+#
+# Each output field must be connected to the output of one of the workflow
+# steps using the "outputSource" field.  Here, the parameter "sorted_output" of the
+# workflow comes from the "sorted_output" output of the "sorted" step.
+outputs:
+  sorted_output:
+    type: File
+    outputSource: sorted/sorted_output
+    doc: "The output with the lines reversed and sorted."
+
+# The "steps" array lists the executable steps that make up the workflow.
+# The tool to execute each step is listed in the "run" field.
+#
+# In the first step, the "in" field of the step connects the upstream
+# parameter "workflow_input" of the workflow to the input parameter of the tool
+# "revtool_input"
+#
+# In the second step, the "in" field of the step connects the output
+# parameter "revtool_output" from the first step to the input parameter of the
+# tool "sorted_input".
+steps:
+  rev:
+    in:
+      revtool_input: workflow_input
+    out: [revtool_output]
+    run: revtool.cwl
+
+  sorted:
+    in:
+      sorted_input: rev/revtool_output
+      reverse: reverse_sort
+    out: [sorted_output]
+    run: sorttool.cwl
+
+$namespaces:
+  iana: https://www.iana.org/assignments/media-types/
+
+$schemas:
+ - empty2.ttl