We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Use key names rather than function names in dask.visualize
When creating task graph images we currently inspect the task graph to find functions and use those function names in the graph.  Now that atop produces `subgraph_callables` all the time, this is less informative than it used to be.  We could either dig deeper here, or we could use the keynames instead.  This is what is done in most of the other diagnostics.  It might be a good time to switch over?

cc @jcrist 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `dask/dot.py`
Content:
```
1 from __future__ import absolute_import, division, print_function
2 
3 import re
4 import os
5 from functools import partial
6 
7 from .compatibility import apply
8 from .core import istask, get_dependencies, ishashable
9 from .utils import funcname, import_required
10 
11 
12 graphviz = import_required("graphviz", "Drawing dask graphs requires the "
13                                        "`graphviz` python library and the "
14                                        "`graphviz` system library to be "
15                                        "installed.")
16 
17 
18 def task_label(task):
19     """Label for a task on a dot graph.
20 
21     Examples
22     --------
23     >>> from operator import add
24     >>> task_label((add, 1, 2))
25     'add'
26     >>> task_label((add, (add, 1, 2), 3))
27     'add(...)'
28     """
29     func = task[0]
30     if func is apply:
31         func = task[1]
32     if hasattr(func, 'funcs'):
33         if len(func.funcs) > 1:
34             return '{0}(...)'.format(funcname(func.funcs[0]))
35         else:
36             head = funcname(func.funcs[0])
37     else:
38         head = funcname(func)
39     if any(has_sub_tasks(i) for i in task[1:]):
40         return '{0}(...)'.format(head)
41     else:
42         return head
43 
44 
45 def has_sub_tasks(task):
46     """Returns True if the task has sub tasks"""
47     if istask(task):
48         return True
49     elif isinstance(task, list):
50         return any(has_sub_tasks(i) for i in task)
51     else:
52         return False
53 
54 
55 def name(x):
56     try:
57         return str(hash(x))
58     except TypeError:
59         return str(hash(str(x)))
60 
61 
62 _HASHPAT = re.compile('([0-9a-z]{32})')
63 _UUIDPAT = re.compile('([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})')
64 
65 
66 def label(x, cache=None):
67     """
68 
69     >>> label('x')
70     'x'
71 
72     >>> label(('x', 1))
73     "('x', 1)"
74 
75     >>> from hashlib import md5
76     >>> x = 'x-%s-hello' % md5(b'1234').hexdigest()
77     >>> x
78     'x-81dc9bdb52d04dc20036dbd8313ed055-hello'
79 
80     >>> label(x)
81     'x-#-hello'
82 
83     >>> from uuid import uuid1
84     >>> x = 'x-%s-hello' % uuid1()
85     >>> x  # doctest: +SKIP
86     'x-4c1a3d7e-0b45-11e6-8334-54ee75105593-hello'
87 
88     >>> label(x)
89     'x-#-hello'
90     """
91     s = str(x)
92     for pattern in (_HASHPAT, _UUIDPAT):
93         m = re.search(pattern, s)
94         if m is not None:
95             for h in m.groups():
96                 if cache is not None:
97                     n = cache.get(h, len(cache))
98                     label = '#{0}'.format(n)
99                     # cache will be overwritten destructively
100                     cache[h] = n
101                 else:
102                     label = '#'
103                 s = s.replace(h, label)
104     return s
105 
106 
107 def to_graphviz(dsk, data_attributes=None, function_attributes=None,
108                 rankdir='BT', graph_attr={}, node_attr=None, edge_attr=None, **kwargs):
109     if data_attributes is None:
110         data_attributes = {}
111     if function_attributes is None:
112         function_attributes = {}
113 
114     graph_attr = graph_attr or {}
115     graph_attr['rankdir'] = rankdir
116     graph_attr.update(kwargs)
117     g = graphviz.Digraph(graph_attr=graph_attr,
118                          node_attr=node_attr,
119                          edge_attr=edge_attr)
120 
121     seen = set()
122     cache = {}
123 
124     for k, v in dsk.items():
125         k_name = name(k)
126         if k_name not in seen:
127             seen.add(k_name)
128             attrs = data_attributes.get(k, {})
129             attrs.setdefault('label', label(k, cache=cache))
130             attrs.setdefault('shape', 'box')
131             g.node(k_name, **attrs)
132 
133         if istask(v):
134             func_name = name((k, 'function'))
135             if func_name not in seen:
136                 seen.add(func_name)
137                 attrs = function_attributes.get(k, {})
138                 attrs.setdefault('label', task_label(v))
139                 attrs.setdefault('shape', 'circle')
140                 g.node(func_name, **attrs)
141             g.edge(func_name, k_name)
142 
143             for dep in get_dependencies(dsk, k):
144                 dep_name = name(dep)
145                 if dep_name not in seen:
146                     seen.add(dep_name)
147                     attrs = data_attributes.get(dep, {})
148                     attrs.setdefault('label', label(dep, cache=cache))
149                     attrs.setdefault('shape', 'box')
150                     g.node(dep_name, **attrs)
151                 g.edge(dep_name, func_name)
152         elif ishashable(v) and v in dsk:
153             g.edge(name(v), k_name)
154     return g
155 
156 
157 IPYTHON_IMAGE_FORMATS = frozenset(['jpeg', 'png'])
158 IPYTHON_NO_DISPLAY_FORMATS = frozenset(['dot', 'pdf'])
159 
160 
161 def _get_display_cls(format):
162     """
163     Get the appropriate IPython display class for `format`.
164 
165     Returns `IPython.display.SVG` if format=='svg', otherwise
166     `IPython.display.Image`.
167 
168     If IPython is not importable, return dummy function that swallows its
169     arguments and returns None.
170     """
171     dummy = lambda *args, **kwargs: None
172     try:
173         import IPython.display as display
174     except ImportError:
175         # Can't return a display object if no IPython.
176         return dummy
177 
178     if format in IPYTHON_NO_DISPLAY_FORMATS:
179         # IPython can't display this format natively, so just return None.
180         return dummy
181     elif format in IPYTHON_IMAGE_FORMATS:
182         # Partially apply `format` so that `Image` and `SVG` supply a uniform
183         # interface to the caller.
184         return partial(display.Image, format=format)
185     elif format == 'svg':
186         return display.SVG
187     else:
188         raise ValueError("Unknown format '%s' passed to `dot_graph`" % format)
189 
190 
191 def dot_graph(dsk, filename='mydask', format=None, **kwargs):
192     """
193     Render a task graph using dot.
194 
195     If `filename` is not None, write a file to disk with that name in the
196     format specified by `format`.  `filename` should not include an extension.
197 
198     Parameters
199     ----------
200     dsk : dict
201         The graph to display.
202     filename : str or None, optional
203         The name (without an extension) of the file to write to disk.  If
204         `filename` is None, no file will be written, and we communicate with
205         dot using only pipes.  Default is 'mydask'.
206     format : {'png', 'pdf', 'dot', 'svg', 'jpeg', 'jpg'}, optional
207         Format in which to write output file.  Default is 'png'.
208     **kwargs
209         Additional keyword arguments to forward to `to_graphviz`.
210 
211     Returns
212     -------
213     result : None or IPython.display.Image or IPython.display.SVG  (See below.)
214 
215     Notes
216     -----
217     If IPython is installed, we return an IPython.display object in the
218     requested format.  If IPython is not installed, we just return None.
219 
220     We always return None if format is 'pdf' or 'dot', because IPython can't
221     display these formats natively. Passing these formats with filename=None
222     will not produce any useful output.
223 
224     See Also
225     --------
226     dask.dot.to_graphviz
227     """
228     g = to_graphviz(dsk, **kwargs)
229 
230     fmts = ['.png', '.pdf', '.dot', '.svg', '.jpeg', '.jpg']
231     if format is None and any(filename.lower().endswith(fmt) for fmt in fmts):
232         filename, format = os.path.splitext(filename)
233         format = format[1:].lower()
234 
235     if format is None:
236         format = 'png'
237 
238     data = g.pipe(format=format)
239     if not data:
240         raise RuntimeError("Graphviz failed to properly produce an image. "
241                            "This probably means your installation of graphviz "
242                            "is missing png support. See: "
243                            "https://github.com/ContinuumIO/anaconda-issues/"
244                            "issues/485 for more information.")
245 
246     display_cls = _get_display_cls(format)
247 
248     if not filename:
249         return display_cls(data=data)
250 
251     full_filename = '.'.join([filename, format])
252     with open(full_filename, 'wb') as f:
253         f.write(data)
254 
255     return display_cls(filename=full_filename)
256
```
--- 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/dask/dot.py b/dask/dot.py
--- a/dask/dot.py
+++ b/dask/dot.py
@@ -6,7 +6,7 @@
 
 from .compatibility import apply
 from .core import istask, get_dependencies, ishashable
-from .utils import funcname, import_required
+from .utils import funcname, import_required, key_split
 
 
 graphviz = import_required("graphviz", "Drawing dask graphs requires the "
@@ -104,6 +104,25 @@
     return s
 
 
+def box_label(key):
+    """ Label boxes in graph by chunk index
+
+    >>> box_label(('x', 1, 2, 3))
+    '(1, 2, 3)'
+    >>> box_label(('x', 123))
+    '123'
+    >>> box_label('x')
+    ''
+    """
+    if isinstance(key, tuple):
+        key = key[1:]
+        if len(key) == 1:
+            [key] = key
+        return str(key)
+    else:
+        return ""
+
+
 def to_graphviz(dsk, data_attributes=None, function_attributes=None,
                 rankdir='BT', graph_attr={}, node_attr=None, edge_attr=None, **kwargs):
     if data_attributes is None:
@@ -119,14 +138,13 @@
                          edge_attr=edge_attr)
 
     seen = set()
-    cache = {}
 
     for k, v in dsk.items():
         k_name = name(k)
         if k_name not in seen:
             seen.add(k_name)
             attrs = data_attributes.get(k, {})
-            attrs.setdefault('label', label(k, cache=cache))
+            attrs.setdefault('label', box_label(k))
             attrs.setdefault('shape', 'box')
             g.node(k_name, **attrs)
 
@@ -135,7 +153,7 @@
             if func_name not in seen:
                 seen.add(func_name)
                 attrs = function_attributes.get(k, {})
-                attrs.setdefault('label', task_label(v))
+                attrs.setdefault('label', key_split(k))
                 attrs.setdefault('shape', 'circle')
                 g.node(func_name, **attrs)
             g.edge(func_name, k_name)
@@ -145,7 +163,7 @@
                 if dep_name not in seen:
                     seen.add(dep_name)
                     attrs = data_attributes.get(dep, {})
-                    attrs.setdefault('label', label(dep, cache=cache))
+                    attrs.setdefault('label', box_label(dep))
                     attrs.setdefault('shape', 'box')
                     g.node(dep_name, **attrs)
                 g.edge(dep_name, func_name)

{"golden_diff": "diff --git a/dask/dot.py b/dask/dot.py\n--- a/dask/dot.py\n+++ b/dask/dot.py\n@@ -6,7 +6,7 @@\n \n from .compatibility import apply\n from .core import istask, get_dependencies, ishashable\n-from .utils import funcname, import_required\n+from .utils import funcname, import_required, key_split\n \n \n graphviz = import_required(\"graphviz\", \"Drawing dask graphs requires the \"\n@@ -104,6 +104,25 @@\n     return s\n \n \n+def box_label(key):\n+    \"\"\" Label boxes in graph by chunk index\n+\n+    >>> box_label(('x', 1, 2, 3))\n+    '(1, 2, 3)'\n+    >>> box_label(('x', 123))\n+    '123'\n+    >>> box_label('x')\n+    ''\n+    \"\"\"\n+    if isinstance(key, tuple):\n+        key = key[1:]\n+        if len(key) == 1:\n+            [key] = key\n+        return str(key)\n+    else:\n+        return \"\"\n+\n+\n def to_graphviz(dsk, data_attributes=None, function_attributes=None,\n                 rankdir='BT', graph_attr={}, node_attr=None, edge_attr=None, **kwargs):\n     if data_attributes is None:\n@@ -119,14 +138,13 @@\n                          edge_attr=edge_attr)\n \n     seen = set()\n-    cache = {}\n \n     for k, v in dsk.items():\n         k_name = name(k)\n         if k_name not in seen:\n             seen.add(k_name)\n             attrs = data_attributes.get(k, {})\n-            attrs.setdefault('label', label(k, cache=cache))\n+            attrs.setdefault('label', box_label(k))\n             attrs.setdefault('shape', 'box')\n             g.node(k_name, **attrs)\n \n@@ -135,7 +153,7 @@\n             if func_name not in seen:\n                 seen.add(func_name)\n                 attrs = function_attributes.get(k, {})\n-                attrs.setdefault('label', task_label(v))\n+                attrs.setdefault('label', key_split(k))\n                 attrs.setdefault('shape', 'circle')\n                 g.node(func_name, **attrs)\n             g.edge(func_name, k_name)\n@@ -145,7 +163,7 @@\n                 if dep_name not in seen:\n                     seen.add(dep_name)\n                     attrs = data_attributes.get(dep, {})\n-                    attrs.setdefault('label', label(dep, cache=cache))\n+                    attrs.setdefault('label', box_label(dep))\n                     attrs.setdefault('shape', 'box')\n                     g.node(dep_name, **attrs)\n                 g.edge(dep_name, func_name)\n", "issue": "Use key names rather than function names in dask.visualize\nWhen creating task graph images we currently inspect the task graph to find functions and use those function names in the graph.  Now that atop produces `subgraph_callables` all the time, this is less informative than it used to be.  We could either dig deeper here, or we could use the keynames instead.  This is what is done in most of the other diagnostics.  It might be a good time to switch over?\r\n\r\ncc @jcrist \n", "before_files": [{"content": "from __future__ import absolute_import, division, print_function\n\nimport re\nimport os\nfrom functools import partial\n\nfrom .compatibility import apply\nfrom .core import istask, get_dependencies, ishashable\nfrom .utils import funcname, import_required\n\n\ngraphviz = import_required(\"graphviz\", \"Drawing dask graphs requires the \"\n                                       \"`graphviz` python library and the \"\n                                       \"`graphviz` system library to be \"\n                                       \"installed.\")\n\n\ndef task_label(task):\n    \"\"\"Label for a task on a dot graph.\n\n    Examples\n    --------\n    >>> from operator import add\n    >>> task_label((add, 1, 2))\n    'add'\n    >>> task_label((add, (add, 1, 2), 3))\n    'add(...)'\n    \"\"\"\n    func = task[0]\n    if func is apply:\n        func = task[1]\n    if hasattr(func, 'funcs'):\n        if len(func.funcs) > 1:\n            return '{0}(...)'.format(funcname(func.funcs[0]))\n        else:\n            head = funcname(func.funcs[0])\n    else:\n        head = funcname(func)\n    if any(has_sub_tasks(i) for i in task[1:]):\n        return '{0}(...)'.format(head)\n    else:\n        return head\n\n\ndef has_sub_tasks(task):\n    \"\"\"Returns True if the task has sub tasks\"\"\"\n    if istask(task):\n        return True\n    elif isinstance(task, list):\n        return any(has_sub_tasks(i) for i in task)\n    else:\n        return False\n\n\ndef name(x):\n    try:\n        return str(hash(x))\n    except TypeError:\n        return str(hash(str(x)))\n\n\n_HASHPAT = re.compile('([0-9a-z]{32})')\n_UUIDPAT = re.compile('([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})')\n\n\ndef label(x, cache=None):\n    \"\"\"\n\n    >>> label('x')\n    'x'\n\n    >>> label(('x', 1))\n    \"('x', 1)\"\n\n    >>> from hashlib import md5\n    >>> x = 'x-%s-hello' % md5(b'1234').hexdigest()\n    >>> x\n    'x-81dc9bdb52d04dc20036dbd8313ed055-hello'\n\n    >>> label(x)\n    'x-#-hello'\n\n    >>> from uuid import uuid1\n    >>> x = 'x-%s-hello' % uuid1()\n    >>> x  # doctest: +SKIP\n    'x-4c1a3d7e-0b45-11e6-8334-54ee75105593-hello'\n\n    >>> label(x)\n    'x-#-hello'\n    \"\"\"\n    s = str(x)\n    for pattern in (_HASHPAT, _UUIDPAT):\n        m = re.search(pattern, s)\n        if m is not None:\n            for h in m.groups():\n                if cache is not None:\n                    n = cache.get(h, len(cache))\n                    label = '#{0}'.format(n)\n                    # cache will be overwritten destructively\n                    cache[h] = n\n                else:\n                    label = '#'\n                s = s.replace(h, label)\n    return s\n\n\ndef to_graphviz(dsk, data_attributes=None, function_attributes=None,\n                rankdir='BT', graph_attr={}, node_attr=None, edge_attr=None, **kwargs):\n    if data_attributes is None:\n        data_attributes = {}\n    if function_attributes is None:\n        function_attributes = {}\n\n    graph_attr = graph_attr or {}\n    graph_attr['rankdir'] = rankdir\n    graph_attr.update(kwargs)\n    g = graphviz.Digraph(graph_attr=graph_attr,\n                         node_attr=node_attr,\n                         edge_attr=edge_attr)\n\n    seen = set()\n    cache = {}\n\n    for k, v in dsk.items():\n        k_name = name(k)\n        if k_name not in seen:\n            seen.add(k_name)\n            attrs = data_attributes.get(k, {})\n            attrs.setdefault('label', label(k, cache=cache))\n            attrs.setdefault('shape', 'box')\n            g.node(k_name, **attrs)\n\n        if istask(v):\n            func_name = name((k, 'function'))\n            if func_name not in seen:\n                seen.add(func_name)\n                attrs = function_attributes.get(k, {})\n                attrs.setdefault('label', task_label(v))\n                attrs.setdefault('shape', 'circle')\n                g.node(func_name, **attrs)\n            g.edge(func_name, k_name)\n\n            for dep in get_dependencies(dsk, k):\n                dep_name = name(dep)\n                if dep_name not in seen:\n                    seen.add(dep_name)\n                    attrs = data_attributes.get(dep, {})\n                    attrs.setdefault('label', label(dep, cache=cache))\n                    attrs.setdefault('shape', 'box')\n                    g.node(dep_name, **attrs)\n                g.edge(dep_name, func_name)\n        elif ishashable(v) and v in dsk:\n            g.edge(name(v), k_name)\n    return g\n\n\nIPYTHON_IMAGE_FORMATS = frozenset(['jpeg', 'png'])\nIPYTHON_NO_DISPLAY_FORMATS = frozenset(['dot', 'pdf'])\n\n\ndef _get_display_cls(format):\n    \"\"\"\n    Get the appropriate IPython display class for `format`.\n\n    Returns `IPython.display.SVG` if format=='svg', otherwise\n    `IPython.display.Image`.\n\n    If IPython is not importable, return dummy function that swallows its\n    arguments and returns None.\n    \"\"\"\n    dummy = lambda *args, **kwargs: None\n    try:\n        import IPython.display as display\n    except ImportError:\n        # Can't return a display object if no IPython.\n        return dummy\n\n    if format in IPYTHON_NO_DISPLAY_FORMATS:\n        # IPython can't display this format natively, so just return None.\n        return dummy\n    elif format in IPYTHON_IMAGE_FORMATS:\n        # Partially apply `format` so that `Image` and `SVG` supply a uniform\n        # interface to the caller.\n        return partial(display.Image, format=format)\n    elif format == 'svg':\n        return display.SVG\n    else:\n        raise ValueError(\"Unknown format '%s' passed to `dot_graph`\" % format)\n\n\ndef dot_graph(dsk, filename='mydask', format=None, **kwargs):\n    \"\"\"\n    Render a task graph using dot.\n\n    If `filename` is not None, write a file to disk with that name in the\n    format specified by `format`.  `filename` should not include an extension.\n\n    Parameters\n    ----------\n    dsk : dict\n        The graph to display.\n    filename : str or None, optional\n        The name (without an extension) of the file to write to disk.  If\n        `filename` is None, no file will be written, and we communicate with\n        dot using only pipes.  Default is 'mydask'.\n    format : {'png', 'pdf', 'dot', 'svg', 'jpeg', 'jpg'}, optional\n        Format in which to write output file.  Default is 'png'.\n    **kwargs\n        Additional keyword arguments to forward to `to_graphviz`.\n\n    Returns\n    -------\n    result : None or IPython.display.Image or IPython.display.SVG  (See below.)\n\n    Notes\n    -----\n    If IPython is installed, we return an IPython.display object in the\n    requested format.  If IPython is not installed, we just return None.\n\n    We always return None if format is 'pdf' or 'dot', because IPython can't\n    display these formats natively. Passing these formats with filename=None\n    will not produce any useful output.\n\n    See Also\n    --------\n    dask.dot.to_graphviz\n    \"\"\"\n    g = to_graphviz(dsk, **kwargs)\n\n    fmts = ['.png', '.pdf', '.dot', '.svg', '.jpeg', '.jpg']\n    if format is None and any(filename.lower().endswith(fmt) for fmt in fmts):\n        filename, format = os.path.splitext(filename)\n        format = format[1:].lower()\n\n    if format is None:\n        format = 'png'\n\n    data = g.pipe(format=format)\n    if not data:\n        raise RuntimeError(\"Graphviz failed to properly produce an image. \"\n                           \"This probably means your installation of graphviz \"\n                           \"is missing png support. See: \"\n                           \"https://github.com/ContinuumIO/anaconda-issues/\"\n                           \"issues/485 for more information.\")\n\n    display_cls = _get_display_cls(format)\n\n    if not filename:\n        return display_cls(data=data)\n\n    full_filename = '.'.join([filename, format])\n    with open(full_filename, 'wb') as f:\n        f.write(data)\n\n    return display_cls(filename=full_filename)\n", "path": "dask/dot.py"}], "after_files": [{"content": "from __future__ import absolute_import, division, print_function\n\nimport re\nimport os\nfrom functools import partial\n\nfrom .compatibility import apply\nfrom .core import istask, get_dependencies, ishashable\nfrom .utils import funcname, import_required, key_split\n\n\ngraphviz = import_required(\"graphviz\", \"Drawing dask graphs requires the \"\n                                       \"`graphviz` python library and the \"\n                                       \"`graphviz` system library to be \"\n                                       \"installed.\")\n\n\ndef task_label(task):\n    \"\"\"Label for a task on a dot graph.\n\n    Examples\n    --------\n    >>> from operator import add\n    >>> task_label((add, 1, 2))\n    'add'\n    >>> task_label((add, (add, 1, 2), 3))\n    'add(...)'\n    \"\"\"\n    func = task[0]\n    if func is apply:\n        func = task[1]\n    if hasattr(func, 'funcs'):\n        if len(func.funcs) > 1:\n            return '{0}(...)'.format(funcname(func.funcs[0]))\n        else:\n            head = funcname(func.funcs[0])\n    else:\n        head = funcname(func)\n    if any(has_sub_tasks(i) for i in task[1:]):\n        return '{0}(...)'.format(head)\n    else:\n        return head\n\n\ndef has_sub_tasks(task):\n    \"\"\"Returns True if the task has sub tasks\"\"\"\n    if istask(task):\n        return True\n    elif isinstance(task, list):\n        return any(has_sub_tasks(i) for i in task)\n    else:\n        return False\n\n\ndef name(x):\n    try:\n        return str(hash(x))\n    except TypeError:\n        return str(hash(str(x)))\n\n\n_HASHPAT = re.compile('([0-9a-z]{32})')\n_UUIDPAT = re.compile('([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})')\n\n\ndef label(x, cache=None):\n    \"\"\"\n\n    >>> label('x')\n    'x'\n\n    >>> label(('x', 1))\n    \"('x', 1)\"\n\n    >>> from hashlib import md5\n    >>> x = 'x-%s-hello' % md5(b'1234').hexdigest()\n    >>> x\n    'x-81dc9bdb52d04dc20036dbd8313ed055-hello'\n\n    >>> label(x)\n    'x-#-hello'\n\n    >>> from uuid import uuid1\n    >>> x = 'x-%s-hello' % uuid1()\n    >>> x  # doctest: +SKIP\n    'x-4c1a3d7e-0b45-11e6-8334-54ee75105593-hello'\n\n    >>> label(x)\n    'x-#-hello'\n    \"\"\"\n    s = str(x)\n    for pattern in (_HASHPAT, _UUIDPAT):\n        m = re.search(pattern, s)\n        if m is not None:\n            for h in m.groups():\n                if cache is not None:\n                    n = cache.get(h, len(cache))\n                    label = '#{0}'.format(n)\n                    # cache will be overwritten destructively\n                    cache[h] = n\n                else:\n                    label = '#'\n                s = s.replace(h, label)\n    return s\n\n\ndef box_label(key):\n    \"\"\" Label boxes in graph by chunk index\n\n    >>> box_label(('x', 1, 2, 3))\n    '(1, 2, 3)'\n    >>> box_label(('x', 123))\n    '123'\n    >>> box_label('x')\n    ''\n    \"\"\"\n    if isinstance(key, tuple):\n        key = key[1:]\n        if len(key) == 1:\n            [key] = key\n        return str(key)\n    else:\n        return \"\"\n\n\ndef to_graphviz(dsk, data_attributes=None, function_attributes=None,\n                rankdir='BT', graph_attr={}, node_attr=None, edge_attr=None, **kwargs):\n    if data_attributes is None:\n        data_attributes = {}\n    if function_attributes is None:\n        function_attributes = {}\n\n    graph_attr = graph_attr or {}\n    graph_attr['rankdir'] = rankdir\n    graph_attr.update(kwargs)\n    g = graphviz.Digraph(graph_attr=graph_attr,\n                         node_attr=node_attr,\n                         edge_attr=edge_attr)\n\n    seen = set()\n\n    for k, v in dsk.items():\n        k_name = name(k)\n        if k_name not in seen:\n            seen.add(k_name)\n            attrs = data_attributes.get(k, {})\n            attrs.setdefault('label', box_label(k))\n            attrs.setdefault('shape', 'box')\n            g.node(k_name, **attrs)\n\n        if istask(v):\n            func_name = name((k, 'function'))\n            if func_name not in seen:\n                seen.add(func_name)\n                attrs = function_attributes.get(k, {})\n                attrs.setdefault('label', key_split(k))\n                attrs.setdefault('shape', 'circle')\n                g.node(func_name, **attrs)\n            g.edge(func_name, k_name)\n\n            for dep in get_dependencies(dsk, k):\n                dep_name = name(dep)\n                if dep_name not in seen:\n                    seen.add(dep_name)\n                    attrs = data_attributes.get(dep, {})\n                    attrs.setdefault('label', box_label(dep))\n                    attrs.setdefault('shape', 'box')\n                    g.node(dep_name, **attrs)\n                g.edge(dep_name, func_name)\n        elif ishashable(v) and v in dsk:\n            g.edge(name(v), k_name)\n    return g\n\n\nIPYTHON_IMAGE_FORMATS = frozenset(['jpeg', 'png'])\nIPYTHON_NO_DISPLAY_FORMATS = frozenset(['dot', 'pdf'])\n\n\ndef _get_display_cls(format):\n    \"\"\"\n    Get the appropriate IPython display class for `format`.\n\n    Returns `IPython.display.SVG` if format=='svg', otherwise\n    `IPython.display.Image`.\n\n    If IPython is not importable, return dummy function that swallows its\n    arguments and returns None.\n    \"\"\"\n    dummy = lambda *args, **kwargs: None\n    try:\n        import IPython.display as display\n    except ImportError:\n        # Can't return a display object if no IPython.\n        return dummy\n\n    if format in IPYTHON_NO_DISPLAY_FORMATS:\n        # IPython can't display this format natively, so just return None.\n        return dummy\n    elif format in IPYTHON_IMAGE_FORMATS:\n        # Partially apply `format` so that `Image` and `SVG` supply a uniform\n        # interface to the caller.\n        return partial(display.Image, format=format)\n    elif format == 'svg':\n        return display.SVG\n    else:\n        raise ValueError(\"Unknown format '%s' passed to `dot_graph`\" % format)\n\n\ndef dot_graph(dsk, filename='mydask', format=None, **kwargs):\n    \"\"\"\n    Render a task graph using dot.\n\n    If `filename` is not None, write a file to disk with that name in the\n    format specified by `format`.  `filename` should not include an extension.\n\n    Parameters\n    ----------\n    dsk : dict\n        The graph to display.\n    filename : str or None, optional\n        The name (without an extension) of the file to write to disk.  If\n        `filename` is None, no file will be written, and we communicate with\n        dot using only pipes.  Default is 'mydask'.\n    format : {'png', 'pdf', 'dot', 'svg', 'jpeg', 'jpg'}, optional\n        Format in which to write output file.  Default is 'png'.\n    **kwargs\n        Additional keyword arguments to forward to `to_graphviz`.\n\n    Returns\n    -------\n    result : None or IPython.display.Image or IPython.display.SVG  (See below.)\n\n    Notes\n    -----\n    If IPython is installed, we return an IPython.display object in the\n    requested format.  If IPython is not installed, we just return None.\n\n    We always return None if format is 'pdf' or 'dot', because IPython can't\n    display these formats natively. Passing these formats with filename=None\n    will not produce any useful output.\n\n    See Also\n    --------\n    dask.dot.to_graphviz\n    \"\"\"\n    g = to_graphviz(dsk, **kwargs)\n\n    fmts = ['.png', '.pdf', '.dot', '.svg', '.jpeg', '.jpg']\n    if format is None and any(filename.lower().endswith(fmt) for fmt in fmts):\n        filename, format = os.path.splitext(filename)\n        format = format[1:].lower()\n\n    if format is None:\n        format = 'png'\n\n    data = g.pipe(format=format)\n    if not data:\n        raise RuntimeError(\"Graphviz failed to properly produce an image. \"\n                           \"This probably means your installation of graphviz \"\n                           \"is missing png support. See: \"\n                           \"https://github.com/ContinuumIO/anaconda-issues/\"\n                           \"issues/485 for more information.\")\n\n    display_cls = _get_display_cls(format)\n\n    if not filename:\n        return display_cls(data=data)\n\n    full_filename = '.'.join([filename, format])\n    with open(full_filename, 'wb') as f:\n        f.write(data)\n\n    return display_cls(filename=full_filename)\n", "path": "dask/dot.py"}]}