We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add option to extend sys.path with app directory
(I thought there was already an issue for this, but I could not find it)

After some investigation I do not think it's possible (or even possibly desirable) to have things like 

```
from .foo import bar
```

work from the app directory. AFIAK Jupyter notebooks do not try to create a package, they just add the notebook directory to `sys.path` and then 

```
from foo import bar
```

is possible. I propose to do the same:

```
bokeh serve --modify-sys-path=yes
```

UPDATE: 

After a little more research, I believe the most proper thing to do is to prepend `''` into `sys.path` and set the CWD to the app location before `exec`, then undo those things. That seems to be exactly consistent with standard python behavior as described here: https://docs.python.org/2/library/sys.html#sys.path


--- 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/application/handlers/code_runner.py`
Content:
```
1 from __future__ import absolute_import, print_function
2 
3 from os.path import abspath
4 from types import ModuleType
5 import os
6 import sys
7 import traceback
8 
9 from bokeh.util.serialization import make_id
10 
11 class _CodeRunner(object):
12     """ Compile and run a Python source code."""
13 
14     def __init__(self, source, path):
15         self._failed = False
16         self._error = None
17         self._error_detail = None
18 
19         import ast
20         self._code = None
21 
22         try:
23             nodes = ast.parse(source, path)
24             self._code = compile(nodes, filename=path, mode='exec')
25         except SyntaxError as e:
26             self._failed = True
27             self._error = ("Invalid syntax in \"%s\" on line %d:\n%s" % (os.path.basename(e.filename), e.lineno, e.text))
28             import traceback
29             self._error_detail = traceback.format_exc()
30 
31         self._path = path
32         self._source = source
33 
34     @property
35     def source(self):
36         return self._source
37 
38 
39     @property
40     def path(self):
41         return self._path
42 
43     @property
44     def failed(self):
45         """True if the handler failed to modify the doc"""
46         return self._failed
47 
48     @property
49     def error(self):
50         """Error message if the handler failed"""
51         return self._error
52 
53     @property
54     def error_detail(self):
55         """Traceback or other details if the handler failed"""
56         return self._error_detail
57 
58     def new_module(self):
59         """Make a fresh module to run in."""
60         if self.failed:
61             return None
62 
63         module_name = 'bk_script_' + make_id().replace('-', '')
64         module = ModuleType(module_name)
65         module.__dict__['__file__'] = abspath(self._path)
66 
67         return module
68 
69     def run(self, module, post_check):
70         try:
71             exec(self._code, module.__dict__)
72             post_check()
73         except Exception as e:
74             self._failed = True
75             self._error_detail = traceback.format_exc()
76 
77             exc_type, exc_value, exc_traceback = sys.exc_info()
78             filename, line_number, func, txt = traceback.extract_tb(exc_traceback)[-1]
79 
80             self._error = "%s\nFile \"%s\", line %d, in %s:\n%s" % (str(e), os.path.basename(filename), line_number, func, txt)
81
```

Path: `examples/app/crossfilter/main.py`
Content:
```
1 import math
2 import numpy as np
3 import pandas as pd
4 
5 from functools import partial
6 
7 from bokeh import palettes
8 from bokeh.io import curdoc
9 from bokeh.models import HBox, Select
10 from bokeh.plotting import Figure
11 from bokeh.sampledata.autompg import autompg
12 
13 # this is to be able to import modules from the app directory
14 import sys
15 from os.path import dirname
16 sys.path.append(dirname(__file__))
17 
18 from models import StyleableBox, StatsBox
19 from models.helpers import load_component
20 
21 class AppModel(object):
22 
23     def __init__(self, df):
24         self.df = df
25         self.columns = []
26         self.color_field = None
27         self.size_field = None
28         self.palette_name = 'Spectral5'
29         self.palettes = [v for v in vars(palettes) if '_' not in v and 'brewer' not in v]
30         self.background_fill = '#2F2F2F'
31         self.default_color = "#31AADE"
32         self.default_size = 9
33         self.scatter_sizes = list(range(6, 22, 3))
34         self.stats_box_style = load_component('stats_box.css')
35         self.columns = self.describe_df()
36         self.x_field = self.continuous_columns[0]['name']
37         self.y_field = self.continuous_columns[1]['name']
38 
39     def describe_df(self):
40         descriptors = []
41         for col in self.df.columns:
42             desc = self.df[col].describe()
43             unique = len(self.df[col].unique())
44             if self.df[col].dtype == object:
45                 descriptors.append({
46                     'type': "DiscreteColumn", 'name': col,
47                     'count': desc['count'], 'unique': unique,
48                     'top': desc['top'], 'freq': desc['freq'],
49                 })
50             elif self.df[col].dtype == np.datetime64:
51                 descriptors.append({
52                     'type': "TimeColumn", 'name': col,
53                     'unique': unique, 'count': desc['count'],
54                     'unique': desc['unique'], 'first': desc['first'],
55                     'last': desc['last'],
56                 })
57             else:
58                 descriptors.append({
59                     'type': "ContinuousColumn", 'name': col,
60                     'count': desc['count'], 'unique': unique,
61                     'mean': "%.2f" % desc['mean'], 'std': "%.2f" % desc['std'],
62                     'min': "%.2f" % desc['min'], 'max': "%.2f" % desc['max'],
63                 })
64         return descriptors
65 
66     @property
67     def continuous_columns(self):
68         return [x for x in self.columns if x['type'] != 'DiscreteColumn']
69 
70     @property
71     def continuous_column_names(self):
72         return [x.get('name') for x in self.columns if x['type'] != 'DiscreteColumn']
73 
74     @property
75     def discrete_column_names(self):
76         return [x.get('name') for x in self.columns if x['type'] == 'DiscreteColumn']
77 
78     @property
79     def quantileable_column_names(self):
80         return [x.get('name') for x in self.columns if x['type'] != 'DiscreteColumn' and x['unique'] > 20]
81 
82     def get_axes_values(self):
83         xs = self.df[self.x_field].tolist()
84         ys = self.df[self.y_field].tolist()
85 
86         if self.color_field:
87             scatter_colors = list(reversed(getattr(palettes, self.palette_name)))
88             bins = len(scatter_colors)
89             groups = pd.qcut(self.df[self.color_field].tolist(), bins)
90             color = [scatter_colors[l] for l in groups.codes]
91         else:
92             color = self.default_color
93 
94         if self.size_field:
95             bins = len(self.scatter_sizes)
96             groups = pd.qcut(self.df[self.size_field].tolist(), bins)
97             size = [self.scatter_sizes[l] for l in groups.codes]
98         else:
99             size = self.default_size
100 
101         return xs, ys, color, size
102 
103 def bind_on_change(attr, old, new, model_field):
104     global plot_view
105     setattr(model, model_field, None) if new == 'None' else setattr(model, model_field, new)
106     plot_view.children = [create_figure()]
107 
108 def create_figure():
109     xs, ys, colors, sizes = model.get_axes_values()
110     fig_args = dict(tools='pan', plot_height=600, plot_width=800)
111 
112     if model.x_field in model.discrete_column_names and model.y_field in model.discrete_column_names:
113         figure = Figure(x_range=xs, y_range=ys, **fig_args)
114         figure.axis.major_label_orientation = math.pi / 4
115     elif model.x_field in model.discrete_column_names:
116         figure = Figure(x_range=xs, **fig_args)
117         figure.xaxis.major_label_orientation = math.pi / 4
118     elif model.y_field in model.discrete_column_names:
119         figure = Figure(y_range=ys, **fig_args)
120         figure.yaxis.major_label_orientation = math.pi / 4
121     else:
122         figure = Figure(**fig_args)
123 
124     figure.circle(x=xs, y=ys, color=colors, size=sizes, line_color="white", alpha=0.8)
125     figure.toolbar_location = None
126     figure.xaxis.axis_label = model.x_field
127     figure.yaxis.axis_label = model.y_field
128     figure.background_fill_color = model.background_fill
129     figure.border_fill_color = model.background_fill
130     figure.axis.axis_line_color = "white"
131     figure.axis.axis_label_text_color = "white"
132     figure.axis.major_label_text_color = "white"
133     figure.axis.major_tick_line_color = "white"
134     figure.axis.minor_tick_line_color = "white"
135     figure.axis.minor_tick_line_color = "white"
136     figure.grid.grid_line_dash = [6, 4]
137     figure.grid.grid_line_alpha = .3
138     return figure
139 
140 model = AppModel(autompg)
141 
142 controls_view = HBox(width=800)
143 
144 x_select = Select.create(name='X-Axis', value=model.x_field, options=model.df.columns)
145 x_select.on_change('value', partial(bind_on_change, model_field='x_field'))
146 
147 y_select = Select.create(name='Y-Axis', value=model.y_field, options=model.df.columns)
148 y_select.on_change('value', partial(bind_on_change, model_field='y_field'))
149 
150 color_select = Select.create(name='Color', value=model.color_field, options=['None'] + model.quantileable_column_names)
151 color_select.on_change('value', partial(bind_on_change, model_field='color_field'))
152 
153 palette_select = Select.create(name='Palette', options=sorted(model.palettes))
154 palette_select.on_change('value', partial(bind_on_change, model_field='palette_name'))
155 
156 size_select = Select.create(name='Size', value=model.size_field, options=['None'] + model.quantileable_column_names)
157 size_select.on_change('value', partial(bind_on_change, model_field='size_field'))
158 
159 controls_view.children = [x_select, y_select, color_select, palette_select, size_select]
160 
161 plot_view = HBox(width=900)
162 plot_view.children = [create_figure()]
163 
164 side_container = StyleableBox()
165 side_container.children = [StatsBox(display_items=c, styles=model.stats_box_style) for c in model.continuous_columns]
166 side_container.css_properties = dict(
167  position='absolute', overflow='scroll', top='1em', left='1em', bottom='1em'
168 )
169 
170 main_container = StyleableBox()
171 main_container.children = [controls_view, plot_view]
172 main_container.css_properties = dict(
173  position='absolute', top='1em', right='1em', left='12.5em', bottom='1em'
174 )
175 
176 doc = curdoc().add_root(HBox(children=[side_container, main_container]))
177
```

Path: `examples/app/crossfilter/models/models.py`
Content:
```
1 from bokeh.properties import Dict, String, Any
2 from bokeh.models.layouts import BaseBox
3 from bokeh.core import validation
4 from bokeh.core.validation.warnings import EMPTY_LAYOUT
5 
6 import sys
7 from os.path import dirname
8 sys.path.append(dirname(__file__))
9 
10 from helpers import load_component
11 
12 class StyleableBox(BaseBox):
13     '''
14     styleable box provides element level css_properties as a dictionary
15     '''
16     __implementation__ = load_component('./styleable_box.coffee')
17     css_properties = Dict(String, Any, default=None)
18     orientation = String(default='vertical')
19 
20 class StatsBox(BaseBox):
21     __implementation__ = load_component('./stats_box.coffee')
22     styles = String(default=None)
23     display_items = Dict(String, Any, default=None)
24     
25     @validation.warning(EMPTY_LAYOUT)
26     def _check_empty_layout(self):
27         pass
28
```
--- 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/application/handlers/code_runner.py b/bokeh/application/handlers/code_runner.py
--- a/bokeh/application/handlers/code_runner.py
+++ b/bokeh/application/handlers/code_runner.py
@@ -1,6 +1,6 @@
 from __future__ import absolute_import, print_function
 
-from os.path import abspath
+from os.path import abspath, dirname
 from types import ModuleType
 import os
 import sys
@@ -68,8 +68,17 @@
 
     def run(self, module, post_check):
         try:
+            # Simulate the sys.path behaviour decribed here:
+            #
+            # https://docs.python.org/2/library/sys.html#sys.path
+            _cwd = os.getcwd()
+            _sys_path = list(sys.path)
+            os.chdir(dirname(self._path))
+            sys.path.insert(0, '')
+
             exec(self._code, module.__dict__)
             post_check()
+
         except Exception as e:
             self._failed = True
             self._error_detail = traceback.format_exc()
@@ -78,3 +87,8 @@
             filename, line_number, func, txt = traceback.extract_tb(exc_traceback)[-1]
 
             self._error = "%s\nFile \"%s\", line %d, in %s:\n%s" % (str(e), os.path.basename(filename), line_number, func, txt)
+
+        finally:
+            # undo sys.path, CWD fixups
+            os.chdir(_cwd)
+            sys.path = _sys_path
diff --git a/examples/app/crossfilter/main.py b/examples/app/crossfilter/main.py
--- a/examples/app/crossfilter/main.py
+++ b/examples/app/crossfilter/main.py
@@ -10,11 +10,6 @@
 from bokeh.plotting import Figure
 from bokeh.sampledata.autompg import autompg
 
-# this is to be able to import modules from the app directory
-import sys
-from os.path import dirname
-sys.path.append(dirname(__file__))
-
 from models import StyleableBox, StatsBox
 from models.helpers import load_component
 
diff --git a/examples/app/crossfilter/models/models.py b/examples/app/crossfilter/models/models.py
--- a/examples/app/crossfilter/models/models.py
+++ b/examples/app/crossfilter/models/models.py
@@ -3,11 +3,7 @@
 from bokeh.core import validation
 from bokeh.core.validation.warnings import EMPTY_LAYOUT
 
-import sys
-from os.path import dirname
-sys.path.append(dirname(__file__))
-
-from helpers import load_component
+from .helpers import load_component
 
 class StyleableBox(BaseBox):
     '''
@@ -21,7 +17,7 @@
     __implementation__ = load_component('./stats_box.coffee')
     styles = String(default=None)
     display_items = Dict(String, Any, default=None)
-    
+
     @validation.warning(EMPTY_LAYOUT)
     def _check_empty_layout(self):
         pass

{"golden_diff": "diff --git a/bokeh/application/handlers/code_runner.py b/bokeh/application/handlers/code_runner.py\n--- a/bokeh/application/handlers/code_runner.py\n+++ b/bokeh/application/handlers/code_runner.py\n@@ -1,6 +1,6 @@\n from __future__ import absolute_import, print_function\n \n-from os.path import abspath\n+from os.path import abspath, dirname\n from types import ModuleType\n import os\n import sys\n@@ -68,8 +68,17 @@\n \n     def run(self, module, post_check):\n         try:\n+            # Simulate the sys.path behaviour decribed here:\n+            #\n+            # https://docs.python.org/2/library/sys.html#sys.path\n+            _cwd = os.getcwd()\n+            _sys_path = list(sys.path)\n+            os.chdir(dirname(self._path))\n+            sys.path.insert(0, '')\n+\n             exec(self._code, module.__dict__)\n             post_check()\n+\n         except Exception as e:\n             self._failed = True\n             self._error_detail = traceback.format_exc()\n@@ -78,3 +87,8 @@\n             filename, line_number, func, txt = traceback.extract_tb(exc_traceback)[-1]\n \n             self._error = \"%s\\nFile \\\"%s\\\", line %d, in %s:\\n%s\" % (str(e), os.path.basename(filename), line_number, func, txt)\n+\n+        finally:\n+            # undo sys.path, CWD fixups\n+            os.chdir(_cwd)\n+            sys.path = _sys_path\ndiff --git a/examples/app/crossfilter/main.py b/examples/app/crossfilter/main.py\n--- a/examples/app/crossfilter/main.py\n+++ b/examples/app/crossfilter/main.py\n@@ -10,11 +10,6 @@\n from bokeh.plotting import Figure\n from bokeh.sampledata.autompg import autompg\n \n-# this is to be able to import modules from the app directory\n-import sys\n-from os.path import dirname\n-sys.path.append(dirname(__file__))\n-\n from models import StyleableBox, StatsBox\n from models.helpers import load_component\n \ndiff --git a/examples/app/crossfilter/models/models.py b/examples/app/crossfilter/models/models.py\n--- a/examples/app/crossfilter/models/models.py\n+++ b/examples/app/crossfilter/models/models.py\n@@ -3,11 +3,7 @@\n from bokeh.core import validation\n from bokeh.core.validation.warnings import EMPTY_LAYOUT\n \n-import sys\n-from os.path import dirname\n-sys.path.append(dirname(__file__))\n-\n-from helpers import load_component\n+from .helpers import load_component\n \n class StyleableBox(BaseBox):\n     '''\n@@ -21,7 +17,7 @@\n     __implementation__ = load_component('./stats_box.coffee')\n     styles = String(default=None)\n     display_items = Dict(String, Any, default=None)\n-    \n+\n     @validation.warning(EMPTY_LAYOUT)\n     def _check_empty_layout(self):\n         pass\n", "issue": "Add option to extend sys.path with app directory\n(I thought there was already an issue for this, but I could not find it)\n\nAfter some investigation I do not think it's possible (or even possibly desirable) to have things like \n\n```\nfrom .foo import bar\n```\n\nwork from the app directory. AFIAK Jupyter notebooks do not try to create a package, they just add the notebook directory to `sys.path` and then \n\n```\nfrom foo import bar\n```\n\nis possible. I propose to do the same:\n\n```\nbokeh serve --modify-sys-path=yes\n```\n\nUPDATE: \n\nAfter a little more research, I believe the most proper thing to do is to prepend `''` into `sys.path` and set the CWD to the app location before `exec`, then undo those things. That seems to be exactly consistent with standard python behavior as described here: https://docs.python.org/2/library/sys.html#sys.path\n\n", "before_files": [{"content": "from __future__ import absolute_import, print_function\n\nfrom os.path import abspath\nfrom types import ModuleType\nimport os\nimport sys\nimport traceback\n\nfrom bokeh.util.serialization import make_id\n\nclass _CodeRunner(object):\n    \"\"\" Compile and run a Python source code.\"\"\"\n\n    def __init__(self, source, path):\n        self._failed = False\n        self._error = None\n        self._error_detail = None\n\n        import ast\n        self._code = None\n\n        try:\n            nodes = ast.parse(source, path)\n            self._code = compile(nodes, filename=path, mode='exec')\n        except SyntaxError as e:\n            self._failed = True\n            self._error = (\"Invalid syntax in \\\"%s\\\" on line %d:\\n%s\" % (os.path.basename(e.filename), e.lineno, e.text))\n            import traceback\n            self._error_detail = traceback.format_exc()\n\n        self._path = path\n        self._source = source\n\n    @property\n    def source(self):\n        return self._source\n\n\n    @property\n    def path(self):\n        return self._path\n\n    @property\n    def failed(self):\n        \"\"\"True if the handler failed to modify the doc\"\"\"\n        return self._failed\n\n    @property\n    def error(self):\n        \"\"\"Error message if the handler failed\"\"\"\n        return self._error\n\n    @property\n    def error_detail(self):\n        \"\"\"Traceback or other details if the handler failed\"\"\"\n        return self._error_detail\n\n    def new_module(self):\n        \"\"\"Make a fresh module to run in.\"\"\"\n        if self.failed:\n            return None\n\n        module_name = 'bk_script_' + make_id().replace('-', '')\n        module = ModuleType(module_name)\n        module.__dict__['__file__'] = abspath(self._path)\n\n        return module\n\n    def run(self, module, post_check):\n        try:\n            exec(self._code, module.__dict__)\n            post_check()\n        except Exception as e:\n            self._failed = True\n            self._error_detail = traceback.format_exc()\n\n            exc_type, exc_value, exc_traceback = sys.exc_info()\n            filename, line_number, func, txt = traceback.extract_tb(exc_traceback)[-1]\n\n            self._error = \"%s\\nFile \\\"%s\\\", line %d, in %s:\\n%s\" % (str(e), os.path.basename(filename), line_number, func, txt)\n", "path": "bokeh/application/handlers/code_runner.py"}, {"content": "import math\nimport numpy as np\nimport pandas as pd\n\nfrom functools import partial\n\nfrom bokeh import palettes\nfrom bokeh.io import curdoc\nfrom bokeh.models import HBox, Select\nfrom bokeh.plotting import Figure\nfrom bokeh.sampledata.autompg import autompg\n\n# this is to be able to import modules from the app directory\nimport sys\nfrom os.path import dirname\nsys.path.append(dirname(__file__))\n\nfrom models import StyleableBox, StatsBox\nfrom models.helpers import load_component\n\nclass AppModel(object):\n\n    def __init__(self, df):\n        self.df = df\n        self.columns = []\n        self.color_field = None\n        self.size_field = None\n        self.palette_name = 'Spectral5'\n        self.palettes = [v for v in vars(palettes) if '_' not in v and 'brewer' not in v]\n        self.background_fill = '#2F2F2F'\n        self.default_color = \"#31AADE\"\n        self.default_size = 9\n        self.scatter_sizes = list(range(6, 22, 3))\n        self.stats_box_style = load_component('stats_box.css')\n        self.columns = self.describe_df()\n        self.x_field = self.continuous_columns[0]['name']\n        self.y_field = self.continuous_columns[1]['name']\n\n    def describe_df(self):\n        descriptors = []\n        for col in self.df.columns:\n            desc = self.df[col].describe()\n            unique = len(self.df[col].unique())\n            if self.df[col].dtype == object:\n                descriptors.append({\n                    'type': \"DiscreteColumn\", 'name': col,\n                    'count': desc['count'], 'unique': unique,\n                    'top': desc['top'], 'freq': desc['freq'],\n                })\n            elif self.df[col].dtype == np.datetime64:\n                descriptors.append({\n                    'type': \"TimeColumn\", 'name': col,\n                    'unique': unique, 'count': desc['count'],\n                    'unique': desc['unique'], 'first': desc['first'],\n                    'last': desc['last'],\n                })\n            else:\n                descriptors.append({\n                    'type': \"ContinuousColumn\", 'name': col,\n                    'count': desc['count'], 'unique': unique,\n                    'mean': \"%.2f\" % desc['mean'], 'std': \"%.2f\" % desc['std'],\n                    'min': \"%.2f\" % desc['min'], 'max': \"%.2f\" % desc['max'],\n                })\n        return descriptors\n\n    @property\n    def continuous_columns(self):\n        return [x for x in self.columns if x['type'] != 'DiscreteColumn']\n\n    @property\n    def continuous_column_names(self):\n        return [x.get('name') for x in self.columns if x['type'] != 'DiscreteColumn']\n\n    @property\n    def discrete_column_names(self):\n        return [x.get('name') for x in self.columns if x['type'] == 'DiscreteColumn']\n\n    @property\n    def quantileable_column_names(self):\n        return [x.get('name') for x in self.columns if x['type'] != 'DiscreteColumn' and x['unique'] > 20]\n\n    def get_axes_values(self):\n        xs = self.df[self.x_field].tolist()\n        ys = self.df[self.y_field].tolist()\n\n        if self.color_field:\n            scatter_colors = list(reversed(getattr(palettes, self.palette_name)))\n            bins = len(scatter_colors)\n            groups = pd.qcut(self.df[self.color_field].tolist(), bins)\n            color = [scatter_colors[l] for l in groups.codes]\n        else:\n            color = self.default_color\n\n        if self.size_field:\n            bins = len(self.scatter_sizes)\n            groups = pd.qcut(self.df[self.size_field].tolist(), bins)\n            size = [self.scatter_sizes[l] for l in groups.codes]\n        else:\n            size = self.default_size\n\n        return xs, ys, color, size\n\ndef bind_on_change(attr, old, new, model_field):\n    global plot_view\n    setattr(model, model_field, None) if new == 'None' else setattr(model, model_field, new)\n    plot_view.children = [create_figure()]\n\ndef create_figure():\n    xs, ys, colors, sizes = model.get_axes_values()\n    fig_args = dict(tools='pan', plot_height=600, plot_width=800)\n\n    if model.x_field in model.discrete_column_names and model.y_field in model.discrete_column_names:\n        figure = Figure(x_range=xs, y_range=ys, **fig_args)\n        figure.axis.major_label_orientation = math.pi / 4\n    elif model.x_field in model.discrete_column_names:\n        figure = Figure(x_range=xs, **fig_args)\n        figure.xaxis.major_label_orientation = math.pi / 4\n    elif model.y_field in model.discrete_column_names:\n        figure = Figure(y_range=ys, **fig_args)\n        figure.yaxis.major_label_orientation = math.pi / 4\n    else:\n        figure = Figure(**fig_args)\n\n    figure.circle(x=xs, y=ys, color=colors, size=sizes, line_color=\"white\", alpha=0.8)\n    figure.toolbar_location = None\n    figure.xaxis.axis_label = model.x_field\n    figure.yaxis.axis_label = model.y_field\n    figure.background_fill_color = model.background_fill\n    figure.border_fill_color = model.background_fill\n    figure.axis.axis_line_color = \"white\"\n    figure.axis.axis_label_text_color = \"white\"\n    figure.axis.major_label_text_color = \"white\"\n    figure.axis.major_tick_line_color = \"white\"\n    figure.axis.minor_tick_line_color = \"white\"\n    figure.axis.minor_tick_line_color = \"white\"\n    figure.grid.grid_line_dash = [6, 4]\n    figure.grid.grid_line_alpha = .3\n    return figure\n\nmodel = AppModel(autompg)\n\ncontrols_view = HBox(width=800)\n\nx_select = Select.create(name='X-Axis', value=model.x_field, options=model.df.columns)\nx_select.on_change('value', partial(bind_on_change, model_field='x_field'))\n\ny_select = Select.create(name='Y-Axis', value=model.y_field, options=model.df.columns)\ny_select.on_change('value', partial(bind_on_change, model_field='y_field'))\n\ncolor_select = Select.create(name='Color', value=model.color_field, options=['None'] + model.quantileable_column_names)\ncolor_select.on_change('value', partial(bind_on_change, model_field='color_field'))\n\npalette_select = Select.create(name='Palette', options=sorted(model.palettes))\npalette_select.on_change('value', partial(bind_on_change, model_field='palette_name'))\n\nsize_select = Select.create(name='Size', value=model.size_field, options=['None'] + model.quantileable_column_names)\nsize_select.on_change('value', partial(bind_on_change, model_field='size_field'))\n\ncontrols_view.children = [x_select, y_select, color_select, palette_select, size_select]\n\nplot_view = HBox(width=900)\nplot_view.children = [create_figure()]\n\nside_container = StyleableBox()\nside_container.children = [StatsBox(display_items=c, styles=model.stats_box_style) for c in model.continuous_columns]\nside_container.css_properties = dict(\n position='absolute', overflow='scroll', top='1em', left='1em', bottom='1em'\n)\n\nmain_container = StyleableBox()\nmain_container.children = [controls_view, plot_view]\nmain_container.css_properties = dict(\n position='absolute', top='1em', right='1em', left='12.5em', bottom='1em'\n)\n\ndoc = curdoc().add_root(HBox(children=[side_container, main_container]))\n", "path": "examples/app/crossfilter/main.py"}, {"content": "from bokeh.properties import Dict, String, Any\nfrom bokeh.models.layouts import BaseBox\nfrom bokeh.core import validation\nfrom bokeh.core.validation.warnings import EMPTY_LAYOUT\n\nimport sys\nfrom os.path import dirname\nsys.path.append(dirname(__file__))\n\nfrom helpers import load_component\n\nclass StyleableBox(BaseBox):\n    '''\n    styleable box provides element level css_properties as a dictionary\n    '''\n    __implementation__ = load_component('./styleable_box.coffee')\n    css_properties = Dict(String, Any, default=None)\n    orientation = String(default='vertical')\n\nclass StatsBox(BaseBox):\n    __implementation__ = load_component('./stats_box.coffee')\n    styles = String(default=None)\n    display_items = Dict(String, Any, default=None)\n    \n    @validation.warning(EMPTY_LAYOUT)\n    def _check_empty_layout(self):\n        pass\n", "path": "examples/app/crossfilter/models/models.py"}], "after_files": [{"content": "from __future__ import absolute_import, print_function\n\nfrom os.path import abspath, dirname\nfrom types import ModuleType\nimport os\nimport sys\nimport traceback\n\nfrom bokeh.util.serialization import make_id\n\nclass _CodeRunner(object):\n    \"\"\" Compile and run a Python source code.\"\"\"\n\n    def __init__(self, source, path):\n        self._failed = False\n        self._error = None\n        self._error_detail = None\n\n        import ast\n        self._code = None\n\n        try:\n            nodes = ast.parse(source, path)\n            self._code = compile(nodes, filename=path, mode='exec')\n        except SyntaxError as e:\n            self._failed = True\n            self._error = (\"Invalid syntax in \\\"%s\\\" on line %d:\\n%s\" % (os.path.basename(e.filename), e.lineno, e.text))\n            import traceback\n            self._error_detail = traceback.format_exc()\n\n        self._path = path\n        self._source = source\n\n    @property\n    def source(self):\n        return self._source\n\n\n    @property\n    def path(self):\n        return self._path\n\n    @property\n    def failed(self):\n        \"\"\"True if the handler failed to modify the doc\"\"\"\n        return self._failed\n\n    @property\n    def error(self):\n        \"\"\"Error message if the handler failed\"\"\"\n        return self._error\n\n    @property\n    def error_detail(self):\n        \"\"\"Traceback or other details if the handler failed\"\"\"\n        return self._error_detail\n\n    def new_module(self):\n        \"\"\"Make a fresh module to run in.\"\"\"\n        if self.failed:\n            return None\n\n        module_name = 'bk_script_' + make_id().replace('-', '')\n        module = ModuleType(module_name)\n        module.__dict__['__file__'] = abspath(self._path)\n\n        return module\n\n    def run(self, module, post_check):\n        try:\n            # Simulate the sys.path behaviour decribed here:\n            #\n            # https://docs.python.org/2/library/sys.html#sys.path\n            _cwd = os.getcwd()\n            _sys_path = list(sys.path)\n            os.chdir(dirname(self._path))\n            sys.path.insert(0, '')\n\n            exec(self._code, module.__dict__)\n            post_check()\n\n        except Exception as e:\n            self._failed = True\n            self._error_detail = traceback.format_exc()\n\n            exc_type, exc_value, exc_traceback = sys.exc_info()\n            filename, line_number, func, txt = traceback.extract_tb(exc_traceback)[-1]\n\n            self._error = \"%s\\nFile \\\"%s\\\", line %d, in %s:\\n%s\" % (str(e), os.path.basename(filename), line_number, func, txt)\n\n        finally:\n            # undo sys.path, CWD fixups\n            os.chdir(_cwd)\n            sys.path = _sys_path\n", "path": "bokeh/application/handlers/code_runner.py"}, {"content": "import math\nimport numpy as np\nimport pandas as pd\n\nfrom functools import partial\n\nfrom bokeh import palettes\nfrom bokeh.io import curdoc\nfrom bokeh.models import HBox, Select\nfrom bokeh.plotting import Figure\nfrom bokeh.sampledata.autompg import autompg\n\nfrom models import StyleableBox, StatsBox\nfrom models.helpers import load_component\n\nclass AppModel(object):\n\n    def __init__(self, df):\n        self.df = df\n        self.columns = []\n        self.color_field = None\n        self.size_field = None\n        self.palette_name = 'Spectral5'\n        self.palettes = [v for v in vars(palettes) if '_' not in v and 'brewer' not in v]\n        self.background_fill = '#2F2F2F'\n        self.default_color = \"#31AADE\"\n        self.default_size = 9\n        self.scatter_sizes = list(range(6, 22, 3))\n        self.stats_box_style = load_component('stats_box.css')\n        self.columns = self.describe_df()\n        self.x_field = self.continuous_columns[0]['name']\n        self.y_field = self.continuous_columns[1]['name']\n\n    def describe_df(self):\n        descriptors = []\n        for col in self.df.columns:\n            desc = self.df[col].describe()\n            unique = len(self.df[col].unique())\n            if self.df[col].dtype == object:\n                descriptors.append({\n                    'type': \"DiscreteColumn\", 'name': col,\n                    'count': desc['count'], 'unique': unique,\n                    'top': desc['top'], 'freq': desc['freq'],\n                })\n            elif self.df[col].dtype == np.datetime64:\n                descriptors.append({\n                    'type': \"TimeColumn\", 'name': col,\n                    'unique': unique, 'count': desc['count'],\n                    'unique': desc['unique'], 'first': desc['first'],\n                    'last': desc['last'],\n                })\n            else:\n                descriptors.append({\n                    'type': \"ContinuousColumn\", 'name': col,\n                    'count': desc['count'], 'unique': unique,\n                    'mean': \"%.2f\" % desc['mean'], 'std': \"%.2f\" % desc['std'],\n                    'min': \"%.2f\" % desc['min'], 'max': \"%.2f\" % desc['max'],\n                })\n        return descriptors\n\n    @property\n    def continuous_columns(self):\n        return [x for x in self.columns if x['type'] != 'DiscreteColumn']\n\n    @property\n    def continuous_column_names(self):\n        return [x.get('name') for x in self.columns if x['type'] != 'DiscreteColumn']\n\n    @property\n    def discrete_column_names(self):\n        return [x.get('name') for x in self.columns if x['type'] == 'DiscreteColumn']\n\n    @property\n    def quantileable_column_names(self):\n        return [x.get('name') for x in self.columns if x['type'] != 'DiscreteColumn' and x['unique'] > 20]\n\n    def get_axes_values(self):\n        xs = self.df[self.x_field].tolist()\n        ys = self.df[self.y_field].tolist()\n\n        if self.color_field:\n            scatter_colors = list(reversed(getattr(palettes, self.palette_name)))\n            bins = len(scatter_colors)\n            groups = pd.qcut(self.df[self.color_field].tolist(), bins)\n            color = [scatter_colors[l] for l in groups.codes]\n        else:\n            color = self.default_color\n\n        if self.size_field:\n            bins = len(self.scatter_sizes)\n            groups = pd.qcut(self.df[self.size_field].tolist(), bins)\n            size = [self.scatter_sizes[l] for l in groups.codes]\n        else:\n            size = self.default_size\n\n        return xs, ys, color, size\n\ndef bind_on_change(attr, old, new, model_field):\n    global plot_view\n    setattr(model, model_field, None) if new == 'None' else setattr(model, model_field, new)\n    plot_view.children = [create_figure()]\n\ndef create_figure():\n    xs, ys, colors, sizes = model.get_axes_values()\n    fig_args = dict(tools='pan', plot_height=600, plot_width=800)\n\n    if model.x_field in model.discrete_column_names and model.y_field in model.discrete_column_names:\n        figure = Figure(x_range=xs, y_range=ys, **fig_args)\n        figure.axis.major_label_orientation = math.pi / 4\n    elif model.x_field in model.discrete_column_names:\n        figure = Figure(x_range=xs, **fig_args)\n        figure.xaxis.major_label_orientation = math.pi / 4\n    elif model.y_field in model.discrete_column_names:\n        figure = Figure(y_range=ys, **fig_args)\n        figure.yaxis.major_label_orientation = math.pi / 4\n    else:\n        figure = Figure(**fig_args)\n\n    figure.circle(x=xs, y=ys, color=colors, size=sizes, line_color=\"white\", alpha=0.8)\n    figure.toolbar_location = None\n    figure.xaxis.axis_label = model.x_field\n    figure.yaxis.axis_label = model.y_field\n    figure.background_fill_color = model.background_fill\n    figure.border_fill_color = model.background_fill\n    figure.axis.axis_line_color = \"white\"\n    figure.axis.axis_label_text_color = \"white\"\n    figure.axis.major_label_text_color = \"white\"\n    figure.axis.major_tick_line_color = \"white\"\n    figure.axis.minor_tick_line_color = \"white\"\n    figure.axis.minor_tick_line_color = \"white\"\n    figure.grid.grid_line_dash = [6, 4]\n    figure.grid.grid_line_alpha = .3\n    return figure\n\nmodel = AppModel(autompg)\n\ncontrols_view = HBox(width=800)\n\nx_select = Select.create(name='X-Axis', value=model.x_field, options=model.df.columns)\nx_select.on_change('value', partial(bind_on_change, model_field='x_field'))\n\ny_select = Select.create(name='Y-Axis', value=model.y_field, options=model.df.columns)\ny_select.on_change('value', partial(bind_on_change, model_field='y_field'))\n\ncolor_select = Select.create(name='Color', value=model.color_field, options=['None'] + model.quantileable_column_names)\ncolor_select.on_change('value', partial(bind_on_change, model_field='color_field'))\n\npalette_select = Select.create(name='Palette', options=sorted(model.palettes))\npalette_select.on_change('value', partial(bind_on_change, model_field='palette_name'))\n\nsize_select = Select.create(name='Size', value=model.size_field, options=['None'] + model.quantileable_column_names)\nsize_select.on_change('value', partial(bind_on_change, model_field='size_field'))\n\ncontrols_view.children = [x_select, y_select, color_select, palette_select, size_select]\n\nplot_view = HBox(width=900)\nplot_view.children = [create_figure()]\n\nside_container = StyleableBox()\nside_container.children = [StatsBox(display_items=c, styles=model.stats_box_style) for c in model.continuous_columns]\nside_container.css_properties = dict(\n position='absolute', overflow='scroll', top='1em', left='1em', bottom='1em'\n)\n\nmain_container = StyleableBox()\nmain_container.children = [controls_view, plot_view]\nmain_container.css_properties = dict(\n position='absolute', top='1em', right='1em', left='12.5em', bottom='1em'\n)\n\ndoc = curdoc().add_root(HBox(children=[side_container, main_container]))\n", "path": "examples/app/crossfilter/main.py"}, {"content": "from bokeh.properties import Dict, String, Any\nfrom bokeh.models.layouts import BaseBox\nfrom bokeh.core import validation\nfrom bokeh.core.validation.warnings import EMPTY_LAYOUT\n\nfrom .helpers import load_component\n\nclass StyleableBox(BaseBox):\n    '''\n    styleable box provides element level css_properties as a dictionary\n    '''\n    __implementation__ = load_component('./styleable_box.coffee')\n    css_properties = Dict(String, Any, default=None)\n    orientation = String(default='vertical')\n\nclass StatsBox(BaseBox):\n    __implementation__ = load_component('./stats_box.coffee')\n    styles = String(default=None)\n    display_items = Dict(String, Any, default=None)\n\n    @validation.warning(EMPTY_LAYOUT)\n    def _check_empty_layout(self):\n        pass\n", "path": "examples/app/crossfilter/models/models.py"}]}