Repository: bokeh/bokeh

Issue:
If main.py is run by bokeh serve, warn about running with directory name instead
Lots of reports of people running, e.g.
```
bokeh serve --show crossfilter/main.py
```
Which prevents all the features of "directory style" apps from being enabled. 


Discussion:
How are you going to distinguish if user intended `main.py` to be part of a "directory style" app or a standalone script? I presume only by name, so maybe instead of warning about this lets make

```
bokeh serve --show crossfilter
```

and

```
bokeh serve --show crossfilter/main.py
```

eqivalent. Unless I miss something here obviously.

I'm not sure it's simple to do that reliably, unfortunately. In which case I'd rather have a mostly likely very rare false positive warning, that informatively says "looks like you are running a directory-style app, if so, do this..." than deal with the "bug reports" we have now. 

Here is the current code:

```
    if os.path.isdir(path):
        handler = DirectoryHandler(filename=path, argv=argv)
    else:
        if path.endswith(".ipynb"):
            handler = NotebookHandler(filename=path, argv=argv)
        elif path.endswith(".py"):
            handler = ScriptHandler(filename=path, argv=argv)
        else:
            raise ValueError("Expected a '.py' script or '.ipynb' notebook, got: '%s'" % path)
```

Are you proposing something like:

```
    if os.path.isdir(path):
        handler = DirectoryHandler(filename=path, argv=argv)
    else:
        if path.endswith("main.py"):
            handler = DirectoryHandler(filename=dirname(path), argv=argv)
        elif path.endswith(".ipynb"):
            handler = NotebookHandler(filename=path, argv=argv)
        elif path.endswith(".py"):
            handler = ScriptHandler(filename=path, argv=argv)
        else:
            raise ValueError("Expected a '.py' script or '.ipynb' notebook, got: '%s'" % path)
```

?

I think that could break fairly badly for a `main.py` that _wasn't_ part of a directory app (and how could we possibly tell the difference for sure) and I think it would have to be more annoyingly complicated to successfully handle just `bokeh serve main.py` (i.e. when dirname is empty). I'd rather just warn on any path that ends with `main.py`, and give instructions how to run directory apps correctly. 


Resolving patch:
diff --git a/bokeh/command/util.py b/bokeh/command/util.py
--- a/bokeh/command/util.py
+++ b/bokeh/command/util.py
@@ -5,6 +5,7 @@
 
 import os
 import sys
+import warnings
 
 from bokeh.application import Application
 from bokeh.application.handlers import ScriptHandler, DirectoryHandler, NotebookHandler
@@ -19,6 +20,16 @@ def die(message):
     print(message, file=sys.stderr)
     sys.exit(1)
 
+DIRSTYLE_MAIN_WARNING = """
+It looks like you might be running the main.py of a directory app directly.
+If this is the case, to enable the features of directory style apps, you must
+call "bokeh serve" on the directory instead. For example:
+
+    bokeh serve my_app_dir/
+
+If this is not the case, renaming main.py will supress this warning.
+"""
+
 def build_single_handler_application(path, argv=None):
     ''' Return a Bokeh application built using a single handler for a file
     or directory.
@@ -44,6 +55,8 @@ def build_single_handler_application(path, argv=None):
         if path.endswith(".ipynb"):
             handler = NotebookHandler(filename=path, argv=argv)
         elif path.endswith(".py"):
+            if path.endswith("main.py"):
+                warnings.warn(DIRSTYLE_MAIN_WARNING)
             handler = ScriptHandler(filename=path, argv=argv)
         else:
             raise ValueError("Expected a '.py' script or '.ipynb' notebook, got: '%s'" % path)


Test patch:
diff --git a/bokeh/command/tests/test_util.py b/bokeh/command/tests/test_util.py
--- a/bokeh/command/tests/test_util.py
+++ b/bokeh/command/tests/test_util.py
@@ -1,5 +1,6 @@
 import tempfile
 
+from mock import patch
 import pytest
 
 import bokeh.command.util as util
@@ -15,3 +16,20 @@ def test_build_single_handler_application_unknown_file():
     with pytest.raises(ValueError):
         f = tempfile.NamedTemporaryFile(suffix=".bad")
         util.build_single_handler_application(f.name)
+
+DIRSTYLE_MAIN_WARNING_COPY = """
+It looks like you might be running the main.py of a directory app directly.
+If this is the case, to enable the features of directory style apps, you must
+call "bokeh serve" on the directory instead. For example:
+
+    bokeh serve my_app_dir/
+
+If this is not the case, renaming main.py will supress this warning.
+"""
+
+@patch('warnings.warn')
+def test_build_single_handler_application_main_py(mock_warn):
+    f = tempfile.NamedTemporaryFile(suffix="main.py")
+    util.build_single_handler_application(f.name)
+    assert mock_warn.called
+    assert mock_warn.call_args[0] == (DIRSTYLE_MAIN_WARNING_COPY,)