Repository: bokeh/bokeh

Issue:
bokeh serve error with directory format and --num-proc != 1
I am using the [directory format](http://bokeh.pydata.org/en/latest/docs/user_guide/server.html#directory-format) with a bokeh server that has a structure like:

```
fst_app
   |
   +---main.py
snd_app
   |
   +---main.py

```

If I set --num-procs to any integer other than 1, then I get the error message:

`ERROR:'DirectoryHandler' object has no attribute 'safe_to_fork'`

The command is:

`bokeh serve --num-procs 0 first_app snd_app`

The error occurs even with only one app specified, but I can explicitly pass --num-procs 1 without issue. I can also run the [single module format](http://bokeh.pydata.org/en/latest/docs/user_guide/server.html#single-module-format) with any number of processes.



Discussion:

Resolving patch:
diff --git a/bokeh/application/handlers/directory.py b/bokeh/application/handlers/directory.py
--- a/bokeh/application/handlers/directory.py
+++ b/bokeh/application/handlers/directory.py
@@ -90,6 +90,10 @@ def failed(self):
     def error(self):
         return self._main_handler.error or self._lifecycle_handler.error
 
+    @property
+    def safe_to_fork(self):
+        return self._main_handler.safe_to_fork
+
     @property
     def error_detail(self):
         return self._main_handler.error_detail or self._lifecycle_handler.error_detail
diff --git a/bokeh/application/handlers/function.py b/bokeh/application/handlers/function.py
--- a/bokeh/application/handlers/function.py
+++ b/bokeh/application/handlers/function.py
@@ -10,6 +10,12 @@ def __init__(self, func):
         super(FunctionHandler, self).__init__()
         _check_callback(func, ('doc',))
         self._func = func
+        self._safe_to_fork = True
 
     def modify_document(self, doc):
         self._func(doc)
+        self._safe_to_fork = False
+
+    @property
+    def safe_to_fork(self):
+        return self._safe_to_fork


Test patch:
diff --git a/bokeh/application/handlers/tests/test_code.py b/bokeh/application/handlers/tests/test_code.py
--- a/bokeh/application/handlers/tests/test_code.py
+++ b/bokeh/application/handlers/tests/test_code.py
@@ -88,3 +88,12 @@ def test_script_argv(self):
 
         assert handler.error is not None
         assert "argv: ['test_filename', 10, 20, 30]" in handler.error
+
+    def test_safe_to_fork(self):
+        doc = Document()
+        handler = CodeHandler(source="# This script does nothing", filename="/test_filename")
+        assert handler.safe_to_fork
+        handler.modify_document(doc)
+        if handler.failed:
+            raise RuntimeError(handler.error)
+        assert not handler.safe_to_fork
diff --git a/bokeh/application/handlers/tests/test_directory.py b/bokeh/application/handlers/tests/test_directory.py
--- a/bokeh/application/handlers/tests/test_directory.py
+++ b/bokeh/application/handlers/tests/test_directory.py
@@ -212,3 +212,19 @@ def load(filename):
         assert not doc.roots
 
         assert doc.template is FILE
+
+    def test_safe_to_fork(self):
+        doc = Document()
+        result = {}
+        def load(filename):
+            handler = DirectoryHandler(filename=filename)
+            assert handler.safe_to_fork
+            result['handler'] = handler
+            handler.modify_document(doc)
+            if handler.failed:
+                raise RuntimeError(handler.error)
+            assert not handler.safe_to_fork
+
+        with_directory_contents({
+            'main.py' : "# This script does nothing",
+        }, load)
diff --git a/bokeh/application/handlers/tests/test_function.py b/bokeh/application/handlers/tests/test_function.py
--- a/bokeh/application/handlers/tests/test_function.py
+++ b/bokeh/application/handlers/tests/test_function.py
@@ -37,3 +37,14 @@ def add_roots(doc):
         if handler.failed:
             raise RuntimeError(handler.error)
         assert len(doc.roots) == 2
+
+    def test_safe_to_fork(self):
+        def noop(doc):
+            pass
+        handler = FunctionHandler(noop)
+        doc = Document()
+        assert handler.safe_to_fork
+        handler.modify_document(doc)
+        if handler.failed:
+            raise RuntimeError(handler.error)
+        assert not handler.safe_to_fork