We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Bokeh 0.12.6 incompatible with Python 2.7.9?
Hi there! I have posted this issue with the [dask.distributed project](https://github.com/dask/distributed/issues/1193#issuecomment-309802212) in which context it appeared, and I was asked to file the issue here, since it seems to be a Bokeh problem.

I have a virtual environment with the following contents:
```
> pip freeze
backports-abc==0.5
bkcharts==0.2
bokeh==0.12.6           <--------------
boto3==1.4.4
botocore==1.5.71
certifi==2017.4.17
chardet==3.0.4
click==6.7
cloudpickle==0.3.1
dask==0.15.0            <--------------
distributed==1.17.1     <--------------
docutils==0.13.1
futures==3.1.1
graphviz==0.7.1
HeapDict==1.0.0
idna==2.5
Jinja2==2.9.6
jmespath==0.9.3
locket==0.2.0
MarkupSafe==1.0
msgpack-python==0.4.8
numpy==1.13.0
pandas==0.20.2
partd==0.3.8
psutil==5.2.2
python-dateutil==2.6.0
pytz==2017.2
PyYAML==3.12
requests==2.18.1
s3fs==0.1.1
s3transfer==0.1.10
singledispatch==3.4.0.3
six==1.10.0
sortedcontainers==1.5.7
tblib==1.3.2
toolz==0.8.2
tornado==4.5.1
urllib3==1.21.1
zict==0.1.2
```
When I try to start the dask scheduler, I get the following output:
```
> dask-scheduler
distributed.scheduler - INFO - -----------------------------------------------
distributed.scheduler - INFO - Could not launch service: ('bokeh', 8787)
Traceback (most recent call last):
  File "/home/vagrant/dask_venv/local/lib/python2.7/site-packages/distributed/scheduler.py", line 404, in start_services
    service = v(self, io_loop=self.loop)
  File "/home/vagrant/dask_venv/local/lib/python2.7/site-packages/distributed/bokeh/scheduler.py", line 995, in __init__
    scheduler)))
  File "/home/vagrant/dask_venv/local/lib/python2.7/site-packages/bokeh/application/handlers/function.py", line 11, in __init__
    _check_callback(func, ('doc',))
  File "/home/vagrant/dask_venv/local/lib/python2.7/site-packages/bokeh/util/callback_manager.py", line 12, in _check_callback
    sig = signature(callback)
  File "/home/vagrant/dask_venv/local/lib/python2.7/site-packages/bokeh/util/future.py", line 85, in signature
    for name in func.keywords.keys():
AttributeError: 'NoneType' object has no attribute 'keys'
distributed.scheduler - INFO -   Scheduler at:      tcp://10.0.2.15:8786
distributed.scheduler - INFO -        http at:              0.0.0.0:9786
distributed.scheduler - INFO - Local Directory:      /tmp/scheduler-zmXtOf
distributed.scheduler - INFO - -----------------------------------------------
^Cdistributed.scheduler - INFO - End scheduler at 'tcp://:8786'
```
I can fix this problem by downgrading Bokeh to 0.12.5:
```
> pip install -U bokeh==0.12.5
...
Installing collected packages: bokeh
  Found existing installation: bokeh 0.12.6
    Uninstalling bokeh-0.12.6:
      Successfully uninstalled bokeh-0.12.6
  Running setup.py install for bokeh ... done
Successfully installed bokeh-0.12.5

> dask-scheduler
distributed.scheduler - INFO - -----------------------------------------------
distributed.scheduler - INFO -   Scheduler at:      tcp://10.0.2.15:8786
distributed.scheduler - INFO -       bokeh at:              0.0.0.0:8787
distributed.scheduler - INFO -        http at:              0.0.0.0:9786
distributed.scheduler - INFO - Local Directory:      /tmp/scheduler-U0qy1k
distributed.scheduler - INFO - -----------------------------------------------
^Cdistributed.scheduler - INFO - End scheduler at 'tcp://:8786'
```

I was able to reproduce the issue on my Debian 8 machine with Python 2.7.9. The error does _not_ occur on my Mac with Python 2.7.11. @pitrou could not reproduce the problem with Python 2.7.12.

Bokeh 0.12.6 incompatible with Python 2.7.9?
Hi there! I have posted this issue with the [dask.distributed project](https://github.com/dask/distributed/issues/1193#issuecomment-309802212) in which context it appeared, and I was asked to file the issue here, since it seems to be a Bokeh problem.

I have a virtual environment with the following contents:
```
> pip freeze
backports-abc==0.5
bkcharts==0.2
bokeh==0.12.6           <--------------
boto3==1.4.4
botocore==1.5.71
certifi==2017.4.17
chardet==3.0.4
click==6.7
cloudpickle==0.3.1
dask==0.15.0            <--------------
distributed==1.17.1     <--------------
docutils==0.13.1
futures==3.1.1
graphviz==0.7.1
HeapDict==1.0.0
idna==2.5
Jinja2==2.9.6
jmespath==0.9.3
locket==0.2.0
MarkupSafe==1.0
msgpack-python==0.4.8
numpy==1.13.0
pandas==0.20.2
partd==0.3.8
psutil==5.2.2
python-dateutil==2.6.0
pytz==2017.2
PyYAML==3.12
requests==2.18.1
s3fs==0.1.1
s3transfer==0.1.10
singledispatch==3.4.0.3
six==1.10.0
sortedcontainers==1.5.7
tblib==1.3.2
toolz==0.8.2
tornado==4.5.1
urllib3==1.21.1
zict==0.1.2
```
When I try to start the dask scheduler, I get the following output:
```
> dask-scheduler
distributed.scheduler - INFO - -----------------------------------------------
distributed.scheduler - INFO - Could not launch service: ('bokeh', 8787)
Traceback (most recent call last):
  File "/home/vagrant/dask_venv/local/lib/python2.7/site-packages/distributed/scheduler.py", line 404, in start_services
    service = v(self, io_loop=self.loop)
  File "/home/vagrant/dask_venv/local/lib/python2.7/site-packages/distributed/bokeh/scheduler.py", line 995, in __init__
    scheduler)))
  File "/home/vagrant/dask_venv/local/lib/python2.7/site-packages/bokeh/application/handlers/function.py", line 11, in __init__
    _check_callback(func, ('doc',))
  File "/home/vagrant/dask_venv/local/lib/python2.7/site-packages/bokeh/util/callback_manager.py", line 12, in _check_callback
    sig = signature(callback)
  File "/home/vagrant/dask_venv/local/lib/python2.7/site-packages/bokeh/util/future.py", line 85, in signature
    for name in func.keywords.keys():
AttributeError: 'NoneType' object has no attribute 'keys'
distributed.scheduler - INFO -   Scheduler at:      tcp://10.0.2.15:8786
distributed.scheduler - INFO -        http at:              0.0.0.0:9786
distributed.scheduler - INFO - Local Directory:      /tmp/scheduler-zmXtOf
distributed.scheduler - INFO - -----------------------------------------------
^Cdistributed.scheduler - INFO - End scheduler at 'tcp://:8786'
```
I can fix this problem by downgrading Bokeh to 0.12.5:
```
> pip install -U bokeh==0.12.5
...
Installing collected packages: bokeh
  Found existing installation: bokeh 0.12.6
    Uninstalling bokeh-0.12.6:
      Successfully uninstalled bokeh-0.12.6
  Running setup.py install for bokeh ... done
Successfully installed bokeh-0.12.5

> dask-scheduler
distributed.scheduler - INFO - -----------------------------------------------
distributed.scheduler - INFO -   Scheduler at:      tcp://10.0.2.15:8786
distributed.scheduler - INFO -       bokeh at:              0.0.0.0:8787
distributed.scheduler - INFO -        http at:              0.0.0.0:9786
distributed.scheduler - INFO - Local Directory:      /tmp/scheduler-U0qy1k
distributed.scheduler - INFO - -----------------------------------------------
^Cdistributed.scheduler - INFO - End scheduler at 'tcp://:8786'
```

I was able to reproduce the issue on my Debian 8 machine with Python 2.7.9. The error does _not_ occur on my Mac with Python 2.7.11. @pitrou could not reproduce the problem with Python 2.7.12.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `bokeh/util/future.py`
Content:
```
1 ''' Utilities for Py2/Py3 interop.
2 
3 '''
4 
5 import sys
6 
7 def with_metaclass(meta, *bases):
8     """ Add metaclasses in both Python 2 and Python 3.
9 
10     Function from jinja2/_compat.py. License: BSD.
11 
12     Use it like this::
13 
14         class BaseForm(object):
15             pass
16 
17         class FormType(type):
18             pass
19 
20         class Form(with_metaclass(FormType, BaseForm)):
21             pass
22 
23     This requires a bit of explanation: the basic idea is to make a
24     dummy metaclass for one level of class instantiation that replaces
25     itself with the actual metaclass.  Because of internal type checks
26     we also need to make sure that we downgrade the custom metaclass
27     for one level to something closer to type (that's why __call__ and
28     __init__ comes back from type etc.).
29 
30     This has the advantage over six.with_metaclass of not introducing
31     dummy classes into the final MRO.
32     """
33     class metaclass(meta):
34         __call__ = type.__call__
35         __init__ = type.__init__
36         def __new__(cls, name, this_bases, d):
37             if this_bases is None:
38                 return type.__new__(cls, name, (), d)
39             return meta(name, bases, d)
40     return metaclass('temporary_class', None, {})
41 
42 
43 # There is a problem with using @wraps decorator in combination with functools.partial.
44 # This issue is not present in Python 3.
45 # This redefinition will be triggered only if issue affects user,
46 # otherwise regular definition of @wraps will be used.
47 #
48 # this code snippet was originally posted in following stack overflow discussion:
49 # http://stackoverflow.com/a/28752007
50 
51 from functools import wraps, partial, WRAPPER_ASSIGNMENTS
52 
53 try:
54     wraps(partial(wraps))(wraps)
55 except AttributeError:
56     @wraps(wraps)
57     def wraps(obj, attr_names=WRAPPER_ASSIGNMENTS, wraps=wraps):
58         return wraps(obj, assigned=(name for name in attr_names if hasattr(obj, name)))
59 
60 del partial, WRAPPER_ASSIGNMENTS
61 
62 
63 # inspect.getargspec and inspect.formatargspec were deprecated in Python 3.5
64 # in favor of the newer inspect.signature introspection
65 
66 if sys.version_info[:2] < (3, 4):
67 
68     def signature(func):
69         # The modifications in this function are to make results more in line
70         # with Python 3, i.e. self is not included in bound methods, supplied
71         # parameters are not reported in partial, etc. This simplifies the
72         # downstream code considerably.
73         from inspect import getargspec, isfunction, ismethod
74         from functools import partial
75 
76         if isfunction(func) or ismethod(func):
77             sig = getargspec(func)
78             if ismethod(func):
79                 sig.args.remove('self')
80             return sig
81 
82         elif isinstance(func, partial):
83             sig = getargspec(func.func)
84             if 'self' in sig.args: sig.args.remove('self')
85             for name in func.keywords.keys():
86                 sig.args.remove(name)
87             for val in func.args:
88                 del sig.args[0]
89             return sig
90 
91         else:
92             sig = getargspec(func.__call__)
93             sig.args.remove('self')
94             return sig
95 
96     def format_signature(sig):
97         from inspect import formatargspec
98         return formatargspec(*sig)
99 
100     def get_param_info(sig):
101         return (sig.args, sig.defaults or [])
102 
103 else:
104     from inspect import signature; signature
105 
106     def format_signature(sig):
107         return str(sig)
108 
109     def get_param_info(sig):
110         defaults = []
111         for param in sig.parameters.values():
112             if param.default is not param.empty:
113                 defaults.append(param.default)
114         return list(sig.parameters), defaults
115
```
--- 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/bokeh/util/future.py b/bokeh/util/future.py
--- a/bokeh/util/future.py
+++ b/bokeh/util/future.py
@@ -82,8 +82,9 @@
         elif isinstance(func, partial):
             sig = getargspec(func.func)
             if 'self' in sig.args: sig.args.remove('self')
-            for name in func.keywords.keys():
-                sig.args.remove(name)
+            if func.keywords is not None:
+                for name in func.keywords.keys():
+                    sig.args.remove(name)
             for val in func.args:
                 del sig.args[0]
             return sig

{"golden_diff": "diff --git a/bokeh/util/future.py b/bokeh/util/future.py\n--- a/bokeh/util/future.py\n+++ b/bokeh/util/future.py\n@@ -82,8 +82,9 @@\n         elif isinstance(func, partial):\n             sig = getargspec(func.func)\n             if 'self' in sig.args: sig.args.remove('self')\n-            for name in func.keywords.keys():\n-                sig.args.remove(name)\n+            if func.keywords is not None:\n+                for name in func.keywords.keys():\n+                    sig.args.remove(name)\n             for val in func.args:\n                 del sig.args[0]\n             return sig\n", "issue": "Bokeh 0.12.6 incompatible with Python 2.7.9?\nHi there! I have posted this issue with the [dask.distributed project](https://github.com/dask/distributed/issues/1193#issuecomment-309802212) in which context it appeared, and I was asked to file the issue here, since it seems to be a Bokeh problem.\r\n\r\nI have a virtual environment with the following contents:\r\n```\r\n> pip freeze\r\nbackports-abc==0.5\r\nbkcharts==0.2\r\nbokeh==0.12.6           <--------------\r\nboto3==1.4.4\r\nbotocore==1.5.71\r\ncertifi==2017.4.17\r\nchardet==3.0.4\r\nclick==6.7\r\ncloudpickle==0.3.1\r\ndask==0.15.0            <--------------\r\ndistributed==1.17.1     <--------------\r\ndocutils==0.13.1\r\nfutures==3.1.1\r\ngraphviz==0.7.1\r\nHeapDict==1.0.0\r\nidna==2.5\r\nJinja2==2.9.6\r\njmespath==0.9.3\r\nlocket==0.2.0\r\nMarkupSafe==1.0\r\nmsgpack-python==0.4.8\r\nnumpy==1.13.0\r\npandas==0.20.2\r\npartd==0.3.8\r\npsutil==5.2.2\r\npython-dateutil==2.6.0\r\npytz==2017.2\r\nPyYAML==3.12\r\nrequests==2.18.1\r\ns3fs==0.1.1\r\ns3transfer==0.1.10\r\nsingledispatch==3.4.0.3\r\nsix==1.10.0\r\nsortedcontainers==1.5.7\r\ntblib==1.3.2\r\ntoolz==0.8.2\r\ntornado==4.5.1\r\nurllib3==1.21.1\r\nzict==0.1.2\r\n```\r\nWhen I try to start the dask scheduler, I get the following output:\r\n```\r\n> dask-scheduler\r\ndistributed.scheduler - INFO - -----------------------------------------------\r\ndistributed.scheduler - INFO - Could not launch service: ('bokeh', 8787)\r\nTraceback (most recent call last):\r\n  File \"/home/vagrant/dask_venv/local/lib/python2.7/site-packages/distributed/scheduler.py\", line 404, in start_services\r\n    service = v(self, io_loop=self.loop)\r\n  File \"/home/vagrant/dask_venv/local/lib/python2.7/site-packages/distributed/bokeh/scheduler.py\", line 995, in __init__\r\n    scheduler)))\r\n  File \"/home/vagrant/dask_venv/local/lib/python2.7/site-packages/bokeh/application/handlers/function.py\", line 11, in __init__\r\n    _check_callback(func, ('doc',))\r\n  File \"/home/vagrant/dask_venv/local/lib/python2.7/site-packages/bokeh/util/callback_manager.py\", line 12, in _check_callback\r\n    sig = signature(callback)\r\n  File \"/home/vagrant/dask_venv/local/lib/python2.7/site-packages/bokeh/util/future.py\", line 85, in signature\r\n    for name in func.keywords.keys():\r\nAttributeError: 'NoneType' object has no attribute 'keys'\r\ndistributed.scheduler - INFO -   Scheduler at:      tcp://10.0.2.15:8786\r\ndistributed.scheduler - INFO -        http at:              0.0.0.0:9786\r\ndistributed.scheduler - INFO - Local Directory:      /tmp/scheduler-zmXtOf\r\ndistributed.scheduler - INFO - -----------------------------------------------\r\n^Cdistributed.scheduler - INFO - End scheduler at 'tcp://:8786'\r\n```\r\nI can fix this problem by downgrading Bokeh to 0.12.5:\r\n```\r\n> pip install -U bokeh==0.12.5\r\n...\r\nInstalling collected packages: bokeh\r\n  Found existing installation: bokeh 0.12.6\r\n    Uninstalling bokeh-0.12.6:\r\n      Successfully uninstalled bokeh-0.12.6\r\n  Running setup.py install for bokeh ... done\r\nSuccessfully installed bokeh-0.12.5\r\n\r\n> dask-scheduler\r\ndistributed.scheduler - INFO - -----------------------------------------------\r\ndistributed.scheduler - INFO -   Scheduler at:      tcp://10.0.2.15:8786\r\ndistributed.scheduler - INFO -       bokeh at:              0.0.0.0:8787\r\ndistributed.scheduler - INFO -        http at:              0.0.0.0:9786\r\ndistributed.scheduler - INFO - Local Directory:      /tmp/scheduler-U0qy1k\r\ndistributed.scheduler - INFO - -----------------------------------------------\r\n^Cdistributed.scheduler - INFO - End scheduler at 'tcp://:8786'\r\n```\r\n\r\nI was able to reproduce the issue on my Debian 8 machine with Python 2.7.9. The error does _not_ occur on my Mac with Python 2.7.11. @pitrou could not reproduce the problem with Python 2.7.12.\r\n\nBokeh 0.12.6 incompatible with Python 2.7.9?\nHi there! I have posted this issue with the [dask.distributed project](https://github.com/dask/distributed/issues/1193#issuecomment-309802212) in which context it appeared, and I was asked to file the issue here, since it seems to be a Bokeh problem.\r\n\r\nI have a virtual environment with the following contents:\r\n```\r\n> pip freeze\r\nbackports-abc==0.5\r\nbkcharts==0.2\r\nbokeh==0.12.6           <--------------\r\nboto3==1.4.4\r\nbotocore==1.5.71\r\ncertifi==2017.4.17\r\nchardet==3.0.4\r\nclick==6.7\r\ncloudpickle==0.3.1\r\ndask==0.15.0            <--------------\r\ndistributed==1.17.1     <--------------\r\ndocutils==0.13.1\r\nfutures==3.1.1\r\ngraphviz==0.7.1\r\nHeapDict==1.0.0\r\nidna==2.5\r\nJinja2==2.9.6\r\njmespath==0.9.3\r\nlocket==0.2.0\r\nMarkupSafe==1.0\r\nmsgpack-python==0.4.8\r\nnumpy==1.13.0\r\npandas==0.20.2\r\npartd==0.3.8\r\npsutil==5.2.2\r\npython-dateutil==2.6.0\r\npytz==2017.2\r\nPyYAML==3.12\r\nrequests==2.18.1\r\ns3fs==0.1.1\r\ns3transfer==0.1.10\r\nsingledispatch==3.4.0.3\r\nsix==1.10.0\r\nsortedcontainers==1.5.7\r\ntblib==1.3.2\r\ntoolz==0.8.2\r\ntornado==4.5.1\r\nurllib3==1.21.1\r\nzict==0.1.2\r\n```\r\nWhen I try to start the dask scheduler, I get the following output:\r\n```\r\n> dask-scheduler\r\ndistributed.scheduler - INFO - -----------------------------------------------\r\ndistributed.scheduler - INFO - Could not launch service: ('bokeh', 8787)\r\nTraceback (most recent call last):\r\n  File \"/home/vagrant/dask_venv/local/lib/python2.7/site-packages/distributed/scheduler.py\", line 404, in start_services\r\n    service = v(self, io_loop=self.loop)\r\n  File \"/home/vagrant/dask_venv/local/lib/python2.7/site-packages/distributed/bokeh/scheduler.py\", line 995, in __init__\r\n    scheduler)))\r\n  File \"/home/vagrant/dask_venv/local/lib/python2.7/site-packages/bokeh/application/handlers/function.py\", line 11, in __init__\r\n    _check_callback(func, ('doc',))\r\n  File \"/home/vagrant/dask_venv/local/lib/python2.7/site-packages/bokeh/util/callback_manager.py\", line 12, in _check_callback\r\n    sig = signature(callback)\r\n  File \"/home/vagrant/dask_venv/local/lib/python2.7/site-packages/bokeh/util/future.py\", line 85, in signature\r\n    for name in func.keywords.keys():\r\nAttributeError: 'NoneType' object has no attribute 'keys'\r\ndistributed.scheduler - INFO -   Scheduler at:      tcp://10.0.2.15:8786\r\ndistributed.scheduler - INFO -        http at:              0.0.0.0:9786\r\ndistributed.scheduler - INFO - Local Directory:      /tmp/scheduler-zmXtOf\r\ndistributed.scheduler - INFO - -----------------------------------------------\r\n^Cdistributed.scheduler - INFO - End scheduler at 'tcp://:8786'\r\n```\r\nI can fix this problem by downgrading Bokeh to 0.12.5:\r\n```\r\n> pip install -U bokeh==0.12.5\r\n...\r\nInstalling collected packages: bokeh\r\n  Found existing installation: bokeh 0.12.6\r\n    Uninstalling bokeh-0.12.6:\r\n      Successfully uninstalled bokeh-0.12.6\r\n  Running setup.py install for bokeh ... done\r\nSuccessfully installed bokeh-0.12.5\r\n\r\n> dask-scheduler\r\ndistributed.scheduler - INFO - -----------------------------------------------\r\ndistributed.scheduler - INFO -   Scheduler at:      tcp://10.0.2.15:8786\r\ndistributed.scheduler - INFO -       bokeh at:              0.0.0.0:8787\r\ndistributed.scheduler - INFO -        http at:              0.0.0.0:9786\r\ndistributed.scheduler - INFO - Local Directory:      /tmp/scheduler-U0qy1k\r\ndistributed.scheduler - INFO - -----------------------------------------------\r\n^Cdistributed.scheduler - INFO - End scheduler at 'tcp://:8786'\r\n```\r\n\r\nI was able to reproduce the issue on my Debian 8 machine with Python 2.7.9. The error does _not_ occur on my Mac with Python 2.7.11. @pitrou could not reproduce the problem with Python 2.7.12.\r\n\n", "before_files": [{"content": "''' Utilities for Py2/Py3 interop.\n\n'''\n\nimport sys\n\ndef with_metaclass(meta, *bases):\n    \"\"\" Add metaclasses in both Python 2 and Python 3.\n\n    Function from jinja2/_compat.py. License: BSD.\n\n    Use it like this::\n\n        class BaseForm(object):\n            pass\n\n        class FormType(type):\n            pass\n\n        class Form(with_metaclass(FormType, BaseForm)):\n            pass\n\n    This requires a bit of explanation: the basic idea is to make a\n    dummy metaclass for one level of class instantiation that replaces\n    itself with the actual metaclass.  Because of internal type checks\n    we also need to make sure that we downgrade the custom metaclass\n    for one level to something closer to type (that's why __call__ and\n    __init__ comes back from type etc.).\n\n    This has the advantage over six.with_metaclass of not introducing\n    dummy classes into the final MRO.\n    \"\"\"\n    class metaclass(meta):\n        __call__ = type.__call__\n        __init__ = type.__init__\n        def __new__(cls, name, this_bases, d):\n            if this_bases is None:\n                return type.__new__(cls, name, (), d)\n            return meta(name, bases, d)\n    return metaclass('temporary_class', None, {})\n\n\n# There is a problem with using @wraps decorator in combination with functools.partial.\n# This issue is not present in Python 3.\n# This redefinition will be triggered only if issue affects user,\n# otherwise regular definition of @wraps will be used.\n#\n# this code snippet was originally posted in following stack overflow discussion:\n# http://stackoverflow.com/a/28752007\n\nfrom functools import wraps, partial, WRAPPER_ASSIGNMENTS\n\ntry:\n    wraps(partial(wraps))(wraps)\nexcept AttributeError:\n    @wraps(wraps)\n    def wraps(obj, attr_names=WRAPPER_ASSIGNMENTS, wraps=wraps):\n        return wraps(obj, assigned=(name for name in attr_names if hasattr(obj, name)))\n\ndel partial, WRAPPER_ASSIGNMENTS\n\n\n# inspect.getargspec and inspect.formatargspec were deprecated in Python 3.5\n# in favor of the newer inspect.signature introspection\n\nif sys.version_info[:2] < (3, 4):\n\n    def signature(func):\n        # The modifications in this function are to make results more in line\n        # with Python 3, i.e. self is not included in bound methods, supplied\n        # parameters are not reported in partial, etc. This simplifies the\n        # downstream code considerably.\n        from inspect import getargspec, isfunction, ismethod\n        from functools import partial\n\n        if isfunction(func) or ismethod(func):\n            sig = getargspec(func)\n            if ismethod(func):\n                sig.args.remove('self')\n            return sig\n\n        elif isinstance(func, partial):\n            sig = getargspec(func.func)\n            if 'self' in sig.args: sig.args.remove('self')\n            for name in func.keywords.keys():\n                sig.args.remove(name)\n            for val in func.args:\n                del sig.args[0]\n            return sig\n\n        else:\n            sig = getargspec(func.__call__)\n            sig.args.remove('self')\n            return sig\n\n    def format_signature(sig):\n        from inspect import formatargspec\n        return formatargspec(*sig)\n\n    def get_param_info(sig):\n        return (sig.args, sig.defaults or [])\n\nelse:\n    from inspect import signature; signature\n\n    def format_signature(sig):\n        return str(sig)\n\n    def get_param_info(sig):\n        defaults = []\n        for param in sig.parameters.values():\n            if param.default is not param.empty:\n                defaults.append(param.default)\n        return list(sig.parameters), defaults\n", "path": "bokeh/util/future.py"}], "after_files": [{"content": "''' Utilities for Py2/Py3 interop.\n\n'''\n\nimport sys\n\ndef with_metaclass(meta, *bases):\n    \"\"\" Add metaclasses in both Python 2 and Python 3.\n\n    Function from jinja2/_compat.py. License: BSD.\n\n    Use it like this::\n\n        class BaseForm(object):\n            pass\n\n        class FormType(type):\n            pass\n\n        class Form(with_metaclass(FormType, BaseForm)):\n            pass\n\n    This requires a bit of explanation: the basic idea is to make a\n    dummy metaclass for one level of class instantiation that replaces\n    itself with the actual metaclass.  Because of internal type checks\n    we also need to make sure that we downgrade the custom metaclass\n    for one level to something closer to type (that's why __call__ and\n    __init__ comes back from type etc.).\n\n    This has the advantage over six.with_metaclass of not introducing\n    dummy classes into the final MRO.\n    \"\"\"\n    class metaclass(meta):\n        __call__ = type.__call__\n        __init__ = type.__init__\n        def __new__(cls, name, this_bases, d):\n            if this_bases is None:\n                return type.__new__(cls, name, (), d)\n            return meta(name, bases, d)\n    return metaclass('temporary_class', None, {})\n\n\n# There is a problem with using @wraps decorator in combination with functools.partial.\n# This issue is not present in Python 3.\n# This redefinition will be triggered only if issue affects user,\n# otherwise regular definition of @wraps will be used.\n#\n# this code snippet was originally posted in following stack overflow discussion:\n# http://stackoverflow.com/a/28752007\n\nfrom functools import wraps, partial, WRAPPER_ASSIGNMENTS\n\ntry:\n    wraps(partial(wraps))(wraps)\nexcept AttributeError:\n    @wraps(wraps)\n    def wraps(obj, attr_names=WRAPPER_ASSIGNMENTS, wraps=wraps):\n        return wraps(obj, assigned=(name for name in attr_names if hasattr(obj, name)))\n\ndel partial, WRAPPER_ASSIGNMENTS\n\n\n# inspect.getargspec and inspect.formatargspec were deprecated in Python 3.5\n# in favor of the newer inspect.signature introspection\n\nif sys.version_info[:2] < (3, 4):\n\n    def signature(func):\n        # The modifications in this function are to make results more in line\n        # with Python 3, i.e. self is not included in bound methods, supplied\n        # parameters are not reported in partial, etc. This simplifies the\n        # downstream code considerably.\n        from inspect import getargspec, isfunction, ismethod\n        from functools import partial\n\n        if isfunction(func) or ismethod(func):\n            sig = getargspec(func)\n            if ismethod(func):\n                sig.args.remove('self')\n            return sig\n\n        elif isinstance(func, partial):\n            sig = getargspec(func.func)\n            if 'self' in sig.args: sig.args.remove('self')\n            if func.keywords is not None:\n                for name in func.keywords.keys():\n                    sig.args.remove(name)\n            for val in func.args:\n                del sig.args[0]\n            return sig\n\n        else:\n            sig = getargspec(func.__call__)\n            sig.args.remove('self')\n            return sig\n\n    def format_signature(sig):\n        from inspect import formatargspec\n        return formatargspec(*sig)\n\n    def get_param_info(sig):\n        return (sig.args, sig.defaults or [])\n\nelse:\n    from inspect import signature; signature\n\n    def format_signature(sig):\n        return str(sig)\n\n    def get_param_info(sig):\n        defaults = []\n        for param in sig.parameters.values():\n            if param.default is not param.empty:\n                defaults.append(param.default)\n        return list(sig.parameters), defaults\n", "path": "bokeh/util/future.py"}]}