We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN 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


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `cwltool/pack.py`
Content:
```
1 """Reformat a CWL document and all its references to be a single stream."""
2 
3 import copy
4 import urllib
5 from typing import (
6     Any,
7     Callable,
8     Dict,
9     List,
10     MutableMapping,
11     MutableSequence,
12     Optional,
13     Set,
14     Union,
15     cast,
16 )
17 
18 from ruamel.yaml.comments import CommentedMap, CommentedSeq
19 from schema_salad.ref_resolver import Loader, SubLoader
20 from schema_salad.sourceline import cmap
21 
22 from .process import shortname, uniquename
23 
24 
25 def flatten_deps(d, files):  # type: (Any, Set[str]) -> None
26     if isinstance(d, MutableSequence):
27         for s in d:
28             flatten_deps(s, files)
29     elif isinstance(d, MutableMapping):
30         if d["class"] == "File":
31             files.add(d["location"])
32         if "secondaryFiles" in d:
33             flatten_deps(d["secondaryFiles"], files)
34         if "listing" in d:
35             flatten_deps(d["listing"], files)
36 
37 
38 LoadRefType = Callable[
39     [Optional[str], str], Union[Dict[str, Any], List[Dict[str, Any]], str, None]
40 ]
41 
42 
43 def find_run(
44     d,  # type: Any
45     loadref,  # type: LoadRefType
46     runs,  # type: Set[str]
47 ):  # type: (...) -> None
48     if isinstance(d, MutableSequence):
49         for s in d:
50             find_run(s, loadref, runs)
51     elif isinstance(d, MutableMapping):
52         if "run" in d and isinstance(d["run"], str):
53             if d["run"] not in runs:
54                 runs.add(d["run"])
55                 find_run(loadref(None, d["run"]), loadref, runs)
56         for s in d.values():
57             find_run(s, loadref, runs)
58 
59 
60 def find_ids(d, ids):  # type: (Any, Set[str]) -> None
61     if isinstance(d, MutableSequence):
62         for s in d:
63             find_ids(s, ids)
64     elif isinstance(d, MutableMapping):
65         for i in ("id", "name"):
66             if i in d and isinstance(d[i], str):
67                 ids.add(d[i])
68         for s in d.values():
69             find_ids(s, ids)
70 
71 
72 def replace_refs(d, rewrite, stem, newstem):
73     # type: (Any, Dict[str, str], str, str) -> None
74     if isinstance(d, MutableSequence):
75         for s, v in enumerate(d):
76             if isinstance(v, str):
77                 if v in rewrite:
78                     d[s] = rewrite[v]
79                 elif v.startswith(stem):
80                     d[s] = newstem + v[len(stem) :]
81                     rewrite[v] = d[s]
82             else:
83                 replace_refs(v, rewrite, stem, newstem)
84     elif isinstance(d, MutableMapping):
85         for s, v in d.items():
86             if isinstance(v, str):
87                 if v in rewrite:
88                     d[s] = rewrite[v]
89                 elif v.startswith(stem):
90                     id_ = v[len(stem) :]
91                     # prevent appending newstems if tool is already packed
92                     if id_.startswith(newstem.strip("#")):
93                         d[s] = "#" + id_
94                     else:
95                         d[s] = newstem + id_
96                     rewrite[v] = d[s]
97             replace_refs(v, rewrite, stem, newstem)
98 
99 
100 def import_embed(d, seen):
101     # type: (Any, Set[str]) -> None
102     if isinstance(d, MutableSequence):
103         for v in d:
104             import_embed(v, seen)
105     elif isinstance(d, MutableMapping):
106         for n in ("id", "name"):
107             if n in d:
108                 if d[n] in seen:
109                     this = d[n]
110                     d.clear()
111                     d["$import"] = this
112                 else:
113                     this = d[n]
114                     seen.add(this)
115                     break
116 
117         for k in sorted(d.keys()):
118             import_embed(d[k], seen)
119 
120 
121 def pack(
122     document_loader: Loader,
123     processobj,  # type: Union[Dict[str, Any], List[Dict[str, Any]]]
124     uri,  # type: str
125     metadata,  # type: Dict[str, str]
126     rewrite_out=None,  # type: Optional[Dict[str, str]]
127 ):  # type: (...) -> Dict[str, Any]
128 
129     document_loader = SubLoader(document_loader)
130     document_loader.idx = {}
131     if isinstance(processobj, MutableMapping):
132         document_loader.idx[processobj["id"]] = CommentedMap(processobj.items())
133     elif isinstance(processobj, MutableSequence):
134         _, frag = urllib.parse.urldefrag(uri)
135         for po in processobj:
136             if not frag:
137                 if po["id"].endswith("#main"):
138                     uri = po["id"]
139             document_loader.idx[po["id"]] = CommentedMap(po.items())
140         document_loader.idx[metadata["id"]] = CommentedMap(metadata.items())
141 
142     def loadref(base, uri):
143         # type: (Optional[str], str) -> Union[Dict[str, Any], List[Dict[str, Any]], str, None]
144         return document_loader.resolve_ref(uri, base_url=base)[0]
145 
146     ids = set()  # type: Set[str]
147     find_ids(processobj, ids)
148 
149     runs = {uri}
150     find_run(processobj, loadref, runs)
151 
152     for f in runs:
153         find_ids(document_loader.resolve_ref(f)[0], ids)
154 
155     names = set()  # type: Set[str]
156     if rewrite_out is None:
157         rewrite = {}  # type: Dict[str, str]
158     else:
159         rewrite = rewrite_out
160 
161     mainpath, _ = urllib.parse.urldefrag(uri)
162 
163     def rewrite_id(r, mainuri):
164         # type: (str, str) -> None
165         if r == mainuri:
166             rewrite[r] = "#main"
167         elif r.startswith(mainuri) and r[len(mainuri)] in ("#", "/"):
168             if r[len(mainuri) :].startswith("#main/"):
169                 rewrite[r] = "#" + uniquename(r[len(mainuri) + 1 :], names)
170             else:
171                 rewrite[r] = "#" + uniquename("main/" + r[len(mainuri) + 1 :], names)
172         else:
173             path, frag = urllib.parse.urldefrag(r)
174             if path == mainpath:
175                 rewrite[r] = "#" + uniquename(frag, names)
176             else:
177                 if path not in rewrite:
178                     rewrite[path] = "#" + uniquename(shortname(path), names)
179 
180     sortedids = sorted(ids)
181 
182     for r in sortedids:
183         rewrite_id(r, uri)
184 
185     packed = CommentedMap(
186         (("$graph", CommentedSeq()), ("cwlVersion", metadata["cwlVersion"]))
187     )
188     namespaces = metadata.get("$namespaces", None)
189 
190     schemas = set()  # type: Set[str]
191     if "$schemas" in metadata:
192         for each_schema in metadata["$schemas"]:
193             schemas.add(each_schema)
194     for r in sorted(runs):
195         dcr, metadata = document_loader.resolve_ref(r)
196         if isinstance(dcr, CommentedSeq):
197             dcr = dcr[0]
198             dcr = cast(CommentedMap, dcr)
199         if not isinstance(dcr, MutableMapping):
200             continue
201         metadata = cast(Dict[str, Any], metadata)
202         if "$schemas" in metadata:
203             for s in metadata["$schemas"]:
204                 schemas.add(s)
205         if dcr.get("class") not in ("Workflow", "CommandLineTool", "ExpressionTool"):
206             continue
207         dc = cast(Dict[str, Any], copy.deepcopy(dcr))
208         v = rewrite[r]
209         dc["id"] = v
210         for n in ("name", "cwlVersion", "$namespaces", "$schemas"):
211             if n in dc:
212                 del dc[n]
213         packed["$graph"].append(dc)
214 
215     if schemas:
216         packed["$schemas"] = list(schemas)
217 
218     for r in list(rewrite.keys()):
219         v = rewrite[r]
220         replace_refs(packed, rewrite, r + "/" if "#" in r else r + "#", v + "/")
221 
222     import_embed(packed, set())
223 
224     if len(packed["$graph"]) == 1:
225         # duplicate 'cwlVersion' and $schemas inside $graph when there is only
226         # a single item because we will print the contents inside '$graph'
227         # rather than whole dict
228         packed["$graph"][0]["cwlVersion"] = packed["cwlVersion"]
229         if schemas:
230             packed["$graph"][0]["$schemas"] = list(schemas)
231     # always include $namespaces in the #main
232     if namespaces:
233         packed["$graph"][0]["$namespaces"] = namespaces
234 
235     return packed
236
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/cwltool/pack.py b/cwltool/pack.py
--- a/cwltool/pack.py
+++ b/cwltool/pack.py
@@ -105,14 +105,15 @@
     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)

{"golden_diff": "diff --git a/cwltool/pack.py b/cwltool/pack.py\n--- a/cwltool/pack.py\n+++ b/cwltool/pack.py\n@@ -105,14 +105,15 @@\n     elif isinstance(d, MutableMapping):\n         for n in (\"id\", \"name\"):\n             if n in d:\n-                if d[n] in seen:\n-                    this = d[n]\n-                    d.clear()\n-                    d[\"$import\"] = this\n-                else:\n-                    this = d[n]\n-                    seen.add(this)\n-                    break\n+                if isinstance(d[n], str):\n+                    if d[n] in seen:\n+                        this = d[n]\n+                        d.clear()\n+                        d[\"$import\"] = this\n+                    else:\n+                        this = d[n]\n+                        seen.add(this)\n+                        break\n \n         for k in sorted(d.keys()):\n             import_embed(d[k], seen)\n", "issue": "Workflow that passes validation fails to pack\n## Expected Behavior\r\nIf a workflow that passes validation (`cwltool --validate`) there should be no errors when packing.\r\n\r\n## Actual Behavior\r\nIn this specific case, I tested the workflow https://raw.githubusercontent.com/Athanaseus/2gc-pipeline/master/selfcal_simulation.cwl .\r\n\r\nIt passes the validation (`cwltool --validate https://raw.githubusercontent.com/Athanaseus/2gc-pipeline/master/selfcal_simulation.cwl`) but fails when running:\r\n```\r\ncwltool --pack --debug \\\r\nhttps://raw.githubusercontent.com/Athanaseus/2gc-pipeline/master/selfcal_simulation.cwl > /dev/null\r\n```\r\n\r\nAlso see CompEpigen/CWLab#38.\r\n(@Athanaseus please have a look.)\r\n## Workflow Code\r\nPlease find it at https://raw.githubusercontent.com/Athanaseus/2gc-pipeline/master/selfcal_simulation.cwl.\r\n\r\n## Full Traceback\r\nThe error thrown was:\r\n```\r\nINFO /usr/local/bin/cwltool 1.0.20180809224403\r\nERROR I'm sorry, I couldn't load this CWL file.\r\nThe error was:\r\nTraceback (most recent call last):\r\n  File \"/usr/local/lib/python3.6/dist-packages/cwltool/main.py\", line 916, in main\r\n    print_pack(loadingContext.loader, processobj, uri, metadata)\r\n  File \"/usr/local/lib/python3.6/dist-packages/cwltool/main.py\", line 563, in print_pack\r\n    packed = pack(document_loader, processobj, uri, metadata)\r\n  File \"/usr/local/lib/python3.6/dist-packages/cwltool/pack.py\", line 222, in pack\r\n    import_embed(packed, set())\r\n  File \"/usr/local/lib/python3.6/dist-packages/cwltool/pack.py\", line 118, in import_embed\r\n    import_embed(d[k], seen)\r\n  File \"/usr/local/lib/python3.6/dist-packages/cwltool/pack.py\", line 104, in import_embed\r\n    import_embed(v, seen)\r\n  File \"/usr/local/lib/python3.6/dist-packages/cwltool/pack.py\", line 118, in import_embed\r\n    import_embed(d[k], seen)\r\n  File \"/usr/local/lib/python3.6/dist-packages/cwltool/pack.py\", line 108, in import_embed\r\n    if d[n] in seen:\r\nTypeError: unhashable type: 'CommentedMap'\r\n```\r\n\r\n## Your Environment\r\n* cwltool version: 1.0.20180809224403\r\n\n", "before_files": [{"content": "\"\"\"Reformat a CWL document and all its references to be a single stream.\"\"\"\n\nimport copy\nimport urllib\nfrom typing import (\n    Any,\n    Callable,\n    Dict,\n    List,\n    MutableMapping,\n    MutableSequence,\n    Optional,\n    Set,\n    Union,\n    cast,\n)\n\nfrom ruamel.yaml.comments import CommentedMap, CommentedSeq\nfrom schema_salad.ref_resolver import Loader, SubLoader\nfrom schema_salad.sourceline import cmap\n\nfrom .process import shortname, uniquename\n\n\ndef flatten_deps(d, files):  # type: (Any, Set[str]) -> None\n    if isinstance(d, MutableSequence):\n        for s in d:\n            flatten_deps(s, files)\n    elif isinstance(d, MutableMapping):\n        if d[\"class\"] == \"File\":\n            files.add(d[\"location\"])\n        if \"secondaryFiles\" in d:\n            flatten_deps(d[\"secondaryFiles\"], files)\n        if \"listing\" in d:\n            flatten_deps(d[\"listing\"], files)\n\n\nLoadRefType = Callable[\n    [Optional[str], str], Union[Dict[str, Any], List[Dict[str, Any]], str, None]\n]\n\n\ndef find_run(\n    d,  # type: Any\n    loadref,  # type: LoadRefType\n    runs,  # type: Set[str]\n):  # type: (...) -> None\n    if isinstance(d, MutableSequence):\n        for s in d:\n            find_run(s, loadref, runs)\n    elif isinstance(d, MutableMapping):\n        if \"run\" in d and isinstance(d[\"run\"], str):\n            if d[\"run\"] not in runs:\n                runs.add(d[\"run\"])\n                find_run(loadref(None, d[\"run\"]), loadref, runs)\n        for s in d.values():\n            find_run(s, loadref, runs)\n\n\ndef find_ids(d, ids):  # type: (Any, Set[str]) -> None\n    if isinstance(d, MutableSequence):\n        for s in d:\n            find_ids(s, ids)\n    elif isinstance(d, MutableMapping):\n        for i in (\"id\", \"name\"):\n            if i in d and isinstance(d[i], str):\n                ids.add(d[i])\n        for s in d.values():\n            find_ids(s, ids)\n\n\ndef replace_refs(d, rewrite, stem, newstem):\n    # type: (Any, Dict[str, str], str, str) -> None\n    if isinstance(d, MutableSequence):\n        for s, v in enumerate(d):\n            if isinstance(v, str):\n                if v in rewrite:\n                    d[s] = rewrite[v]\n                elif v.startswith(stem):\n                    d[s] = newstem + v[len(stem) :]\n                    rewrite[v] = d[s]\n            else:\n                replace_refs(v, rewrite, stem, newstem)\n    elif isinstance(d, MutableMapping):\n        for s, v in d.items():\n            if isinstance(v, str):\n                if v in rewrite:\n                    d[s] = rewrite[v]\n                elif v.startswith(stem):\n                    id_ = v[len(stem) :]\n                    # prevent appending newstems if tool is already packed\n                    if id_.startswith(newstem.strip(\"#\")):\n                        d[s] = \"#\" + id_\n                    else:\n                        d[s] = newstem + id_\n                    rewrite[v] = d[s]\n            replace_refs(v, rewrite, stem, newstem)\n\n\ndef import_embed(d, seen):\n    # type: (Any, Set[str]) -> None\n    if isinstance(d, MutableSequence):\n        for v in d:\n            import_embed(v, seen)\n    elif isinstance(d, MutableMapping):\n        for n in (\"id\", \"name\"):\n            if n in d:\n                if d[n] in seen:\n                    this = d[n]\n                    d.clear()\n                    d[\"$import\"] = this\n                else:\n                    this = d[n]\n                    seen.add(this)\n                    break\n\n        for k in sorted(d.keys()):\n            import_embed(d[k], seen)\n\n\ndef pack(\n    document_loader: Loader,\n    processobj,  # type: Union[Dict[str, Any], List[Dict[str, Any]]]\n    uri,  # type: str\n    metadata,  # type: Dict[str, str]\n    rewrite_out=None,  # type: Optional[Dict[str, str]]\n):  # type: (...) -> Dict[str, Any]\n\n    document_loader = SubLoader(document_loader)\n    document_loader.idx = {}\n    if isinstance(processobj, MutableMapping):\n        document_loader.idx[processobj[\"id\"]] = CommentedMap(processobj.items())\n    elif isinstance(processobj, MutableSequence):\n        _, frag = urllib.parse.urldefrag(uri)\n        for po in processobj:\n            if not frag:\n                if po[\"id\"].endswith(\"#main\"):\n                    uri = po[\"id\"]\n            document_loader.idx[po[\"id\"]] = CommentedMap(po.items())\n        document_loader.idx[metadata[\"id\"]] = CommentedMap(metadata.items())\n\n    def loadref(base, uri):\n        # type: (Optional[str], str) -> Union[Dict[str, Any], List[Dict[str, Any]], str, None]\n        return document_loader.resolve_ref(uri, base_url=base)[0]\n\n    ids = set()  # type: Set[str]\n    find_ids(processobj, ids)\n\n    runs = {uri}\n    find_run(processobj, loadref, runs)\n\n    for f in runs:\n        find_ids(document_loader.resolve_ref(f)[0], ids)\n\n    names = set()  # type: Set[str]\n    if rewrite_out is None:\n        rewrite = {}  # type: Dict[str, str]\n    else:\n        rewrite = rewrite_out\n\n    mainpath, _ = urllib.parse.urldefrag(uri)\n\n    def rewrite_id(r, mainuri):\n        # type: (str, str) -> None\n        if r == mainuri:\n            rewrite[r] = \"#main\"\n        elif r.startswith(mainuri) and r[len(mainuri)] in (\"#\", \"/\"):\n            if r[len(mainuri) :].startswith(\"#main/\"):\n                rewrite[r] = \"#\" + uniquename(r[len(mainuri) + 1 :], names)\n            else:\n                rewrite[r] = \"#\" + uniquename(\"main/\" + r[len(mainuri) + 1 :], names)\n        else:\n            path, frag = urllib.parse.urldefrag(r)\n            if path == mainpath:\n                rewrite[r] = \"#\" + uniquename(frag, names)\n            else:\n                if path not in rewrite:\n                    rewrite[path] = \"#\" + uniquename(shortname(path), names)\n\n    sortedids = sorted(ids)\n\n    for r in sortedids:\n        rewrite_id(r, uri)\n\n    packed = CommentedMap(\n        ((\"$graph\", CommentedSeq()), (\"cwlVersion\", metadata[\"cwlVersion\"]))\n    )\n    namespaces = metadata.get(\"$namespaces\", None)\n\n    schemas = set()  # type: Set[str]\n    if \"$schemas\" in metadata:\n        for each_schema in metadata[\"$schemas\"]:\n            schemas.add(each_schema)\n    for r in sorted(runs):\n        dcr, metadata = document_loader.resolve_ref(r)\n        if isinstance(dcr, CommentedSeq):\n            dcr = dcr[0]\n            dcr = cast(CommentedMap, dcr)\n        if not isinstance(dcr, MutableMapping):\n            continue\n        metadata = cast(Dict[str, Any], metadata)\n        if \"$schemas\" in metadata:\n            for s in metadata[\"$schemas\"]:\n                schemas.add(s)\n        if dcr.get(\"class\") not in (\"Workflow\", \"CommandLineTool\", \"ExpressionTool\"):\n            continue\n        dc = cast(Dict[str, Any], copy.deepcopy(dcr))\n        v = rewrite[r]\n        dc[\"id\"] = v\n        for n in (\"name\", \"cwlVersion\", \"$namespaces\", \"$schemas\"):\n            if n in dc:\n                del dc[n]\n        packed[\"$graph\"].append(dc)\n\n    if schemas:\n        packed[\"$schemas\"] = list(schemas)\n\n    for r in list(rewrite.keys()):\n        v = rewrite[r]\n        replace_refs(packed, rewrite, r + \"/\" if \"#\" in r else r + \"#\", v + \"/\")\n\n    import_embed(packed, set())\n\n    if len(packed[\"$graph\"]) == 1:\n        # duplicate 'cwlVersion' and $schemas inside $graph when there is only\n        # a single item because we will print the contents inside '$graph'\n        # rather than whole dict\n        packed[\"$graph\"][0][\"cwlVersion\"] = packed[\"cwlVersion\"]\n        if schemas:\n            packed[\"$graph\"][0][\"$schemas\"] = list(schemas)\n    # always include $namespaces in the #main\n    if namespaces:\n        packed[\"$graph\"][0][\"$namespaces\"] = namespaces\n\n    return packed\n", "path": "cwltool/pack.py"}], "after_files": [{"content": "\"\"\"Reformat a CWL document and all its references to be a single stream.\"\"\"\n\nimport copy\nimport urllib\nfrom typing import (\n    Any,\n    Callable,\n    Dict,\n    List,\n    MutableMapping,\n    MutableSequence,\n    Optional,\n    Set,\n    Union,\n    cast,\n)\n\nfrom ruamel.yaml.comments import CommentedMap, CommentedSeq\nfrom schema_salad.ref_resolver import Loader, SubLoader\nfrom schema_salad.sourceline import cmap\n\nfrom .process import shortname, uniquename\n\n\ndef flatten_deps(d, files):  # type: (Any, Set[str]) -> None\n    if isinstance(d, MutableSequence):\n        for s in d:\n            flatten_deps(s, files)\n    elif isinstance(d, MutableMapping):\n        if d[\"class\"] == \"File\":\n            files.add(d[\"location\"])\n        if \"secondaryFiles\" in d:\n            flatten_deps(d[\"secondaryFiles\"], files)\n        if \"listing\" in d:\n            flatten_deps(d[\"listing\"], files)\n\n\nLoadRefType = Callable[\n    [Optional[str], str], Union[Dict[str, Any], List[Dict[str, Any]], str, None]\n]\n\n\ndef find_run(\n    d,  # type: Any\n    loadref,  # type: LoadRefType\n    runs,  # type: Set[str]\n):  # type: (...) -> None\n    if isinstance(d, MutableSequence):\n        for s in d:\n            find_run(s, loadref, runs)\n    elif isinstance(d, MutableMapping):\n        if \"run\" in d and isinstance(d[\"run\"], str):\n            if d[\"run\"] not in runs:\n                runs.add(d[\"run\"])\n                find_run(loadref(None, d[\"run\"]), loadref, runs)\n        for s in d.values():\n            find_run(s, loadref, runs)\n\n\ndef find_ids(d, ids):  # type: (Any, Set[str]) -> None\n    if isinstance(d, MutableSequence):\n        for s in d:\n            find_ids(s, ids)\n    elif isinstance(d, MutableMapping):\n        for i in (\"id\", \"name\"):\n            if i in d and isinstance(d[i], str):\n                ids.add(d[i])\n        for s in d.values():\n            find_ids(s, ids)\n\n\ndef replace_refs(d, rewrite, stem, newstem):\n    # type: (Any, Dict[str, str], str, str) -> None\n    if isinstance(d, MutableSequence):\n        for s, v in enumerate(d):\n            if isinstance(v, str):\n                if v in rewrite:\n                    d[s] = rewrite[v]\n                elif v.startswith(stem):\n                    d[s] = newstem + v[len(stem) :]\n                    rewrite[v] = d[s]\n            else:\n                replace_refs(v, rewrite, stem, newstem)\n    elif isinstance(d, MutableMapping):\n        for s, v in d.items():\n            if isinstance(v, str):\n                if v in rewrite:\n                    d[s] = rewrite[v]\n                elif v.startswith(stem):\n                    id_ = v[len(stem) :]\n                    # prevent appending newstems if tool is already packed\n                    if id_.startswith(newstem.strip(\"#\")):\n                        d[s] = \"#\" + id_\n                    else:\n                        d[s] = newstem + id_\n                    rewrite[v] = d[s]\n            replace_refs(v, rewrite, stem, newstem)\n\n\ndef import_embed(d, seen):\n    # type: (Any, Set[str]) -> None\n    if isinstance(d, MutableSequence):\n        for v in d:\n            import_embed(v, seen)\n    elif isinstance(d, MutableMapping):\n        for n in (\"id\", \"name\"):\n            if n in d:\n                if isinstance(d[n], str):\n                    if d[n] in seen:\n                        this = d[n]\n                        d.clear()\n                        d[\"$import\"] = this\n                    else:\n                        this = d[n]\n                        seen.add(this)\n                        break\n\n        for k in sorted(d.keys()):\n            import_embed(d[k], seen)\n\n\ndef pack(\n    document_loader: Loader,\n    processobj,  # type: Union[Dict[str, Any], List[Dict[str, Any]]]\n    uri,  # type: str\n    metadata,  # type: Dict[str, str]\n    rewrite_out=None,  # type: Optional[Dict[str, str]]\n):  # type: (...) -> Dict[str, Any]\n\n    document_loader = SubLoader(document_loader)\n    document_loader.idx = {}\n    if isinstance(processobj, MutableMapping):\n        document_loader.idx[processobj[\"id\"]] = CommentedMap(processobj.items())\n    elif isinstance(processobj, MutableSequence):\n        _, frag = urllib.parse.urldefrag(uri)\n        for po in processobj:\n            if not frag:\n                if po[\"id\"].endswith(\"#main\"):\n                    uri = po[\"id\"]\n            document_loader.idx[po[\"id\"]] = CommentedMap(po.items())\n        document_loader.idx[metadata[\"id\"]] = CommentedMap(metadata.items())\n\n    def loadref(base, uri):\n        # type: (Optional[str], str) -> Union[Dict[str, Any], List[Dict[str, Any]], str, None]\n        return document_loader.resolve_ref(uri, base_url=base)[0]\n\n    ids = set()  # type: Set[str]\n    find_ids(processobj, ids)\n\n    runs = {uri}\n    find_run(processobj, loadref, runs)\n\n    for f in runs:\n        find_ids(document_loader.resolve_ref(f)[0], ids)\n\n    names = set()  # type: Set[str]\n    if rewrite_out is None:\n        rewrite = {}  # type: Dict[str, str]\n    else:\n        rewrite = rewrite_out\n\n    mainpath, _ = urllib.parse.urldefrag(uri)\n\n    def rewrite_id(r, mainuri):\n        # type: (str, str) -> None\n        if r == mainuri:\n            rewrite[r] = \"#main\"\n        elif r.startswith(mainuri) and r[len(mainuri)] in (\"#\", \"/\"):\n            if r[len(mainuri) :].startswith(\"#main/\"):\n                rewrite[r] = \"#\" + uniquename(r[len(mainuri) + 1 :], names)\n            else:\n                rewrite[r] = \"#\" + uniquename(\"main/\" + r[len(mainuri) + 1 :], names)\n        else:\n            path, frag = urllib.parse.urldefrag(r)\n            if path == mainpath:\n                rewrite[r] = \"#\" + uniquename(frag, names)\n            else:\n                if path not in rewrite:\n                    rewrite[path] = \"#\" + uniquename(shortname(path), names)\n\n    sortedids = sorted(ids)\n\n    for r in sortedids:\n        rewrite_id(r, uri)\n\n    packed = CommentedMap(\n        ((\"$graph\", CommentedSeq()), (\"cwlVersion\", metadata[\"cwlVersion\"]))\n    )\n    namespaces = metadata.get(\"$namespaces\", None)\n\n    schemas = set()  # type: Set[str]\n    if \"$schemas\" in metadata:\n        for each_schema in metadata[\"$schemas\"]:\n            schemas.add(each_schema)\n    for r in sorted(runs):\n        dcr, metadata = document_loader.resolve_ref(r)\n        if isinstance(dcr, CommentedSeq):\n            dcr = dcr[0]\n            dcr = cast(CommentedMap, dcr)\n        if not isinstance(dcr, MutableMapping):\n            continue\n        metadata = cast(Dict[str, Any], metadata)\n        if \"$schemas\" in metadata:\n            for s in metadata[\"$schemas\"]:\n                schemas.add(s)\n        if dcr.get(\"class\") not in (\"Workflow\", \"CommandLineTool\", \"ExpressionTool\"):\n            continue\n        dc = cast(Dict[str, Any], copy.deepcopy(dcr))\n        v = rewrite[r]\n        dc[\"id\"] = v\n        for n in (\"name\", \"cwlVersion\", \"$namespaces\", \"$schemas\"):\n            if n in dc:\n                del dc[n]\n        packed[\"$graph\"].append(dc)\n\n    if schemas:\n        packed[\"$schemas\"] = list(schemas)\n\n    for r in list(rewrite.keys()):\n        v = rewrite[r]\n        replace_refs(packed, rewrite, r + \"/\" if \"#\" in r else r + \"#\", v + \"/\")\n\n    import_embed(packed, set())\n\n    if len(packed[\"$graph\"]) == 1:\n        # duplicate 'cwlVersion' and $schemas inside $graph when there is only\n        # a single item because we will print the contents inside '$graph'\n        # rather than whole dict\n        packed[\"$graph\"][0][\"cwlVersion\"] = packed[\"cwlVersion\"]\n        if schemas:\n            packed[\"$graph\"][0][\"$schemas\"] = list(schemas)\n    # always include $namespaces in the #main\n    if namespaces:\n        packed[\"$graph\"][0][\"$namespaces\"] = namespaces\n\n    return packed\n", "path": "cwltool/pack.py"}]}