Repository: bokeh/bokeh

Issue:
Auto-conversion to ColumnDataSource
Many times, we want to pass a data structure to a plotting function as a data source. To do so, an explicit conversion is required. Often, the converted object does not serve any purpose, except for the conversion itself.  The modification to handle various supported data structures, such as pandas DataFrame and dicts, is pretty trivial, as you can see [here][1]. Is this a good idea to perform auto-conversion? What are the potential hazards of the way I handled this?


[1]: https://github.com/bgbg/bokeh/commit/aa459a7a89b715c3c8fea9a8cc8a7a5fad5cae9a


Discussion:
Thoughts, @bokeh/dev ?

I'm fairly neural but, if done I'd prefer using something like:
```
from bokeh.util.dependencies import import_optional
pd = import_optional('pandas')

if pd and isinstance(pd.DataFrame, source)
   # do conversion and update kwargs
```
The version you provided, @bryevdv , indeed looks cleaner. However, it will not handle other cases that `ColumnDataSource.__init__` can handle, such as a dict for example. Not only that, my proposal is future-safe. If and when `ColumnDataSource` is able to read additional sources, this particular code will not need any changes. Plus, `ColumnDataSource` already performs all the needed tests and assertions.
If `ColumnDataSource` contains all necessary logic, lets not duplicate it in the plotting API.
Fair enough, I'd have go go look to see what exactly happens on failure in CDS. Given the comments above I might then instead suggest to catch whatever CDS throws in the plotting API, and wrap that in a more contextual exception `"Could not convert data_source to CDS, reason: ..."`
Looks like it raises `ValueError` if not passed a `dict` or `DataFrame`:

https://github.com/bokeh/bokeh/blob/master/bokeh/models/sources.py#L89

@bgbg If you wanted to update your code in a PR that catches `ValueError`, and raises a more contextualized error message, it would gladly be accepted. 

Resolving patch:
diff --git a/bokeh/plotting/figure.py b/bokeh/plotting/figure.py
--- a/bokeh/plotting/figure.py
+++ b/bokeh/plotting/figure.py
@@ -587,7 +587,8 @@ def scatter(self, *args, **kwargs):
             marker (str, optional): a valid marker_type, defaults to "circle"
             color (color value, optional): shorthand to set both fill and line color
             source (:class:`~bokeh.models.sources.ColumnDataSource`) : a user-supplied data source.
-                If none is supplied, one is created for the user automatically.
+                An attempt will be made to convert the object to :class:`~bokeh.models.sources.ColumnDataSource`
+                if needed. If none is supplied, one is created for the user automatically.
             **kwargs: :ref:`userguide_styling_line_properties` and :ref:`userguide_styling_fill_properties`
 
         Examples:
diff --git a/bokeh/plotting/helpers.py b/bokeh/plotting/helpers.py
--- a/bokeh/plotting/helpers.py
+++ b/bokeh/plotting/helpers.py
@@ -7,7 +7,8 @@
 import warnings
 
 import numpy as np
-from six import string_types
+import sys
+from six import string_types, reraise
 
 from ..models import (
     BoxSelectTool, BoxZoomTool, CategoricalAxis,
@@ -513,6 +514,19 @@ def func(self, **kwargs):
         is_user_source = kwargs.get('source', None) is not None
         renderer_kws = _pop_renderer_args(kwargs)
         source = renderer_kws['data_source']
+        if not isinstance(source, ColumnDataSource):
+            try:
+                # try converting the soruce to ColumnDataSource
+                source = ColumnDataSource(source)
+            except ValueError as err:
+                msg = "Failed to auto-convert {curr_type} to ColumnDataSource.\n Original error: {err}".format(
+                    curr_type=str(type(source)),
+                    err=err.message
+                )
+                reraise(ValueError, ValueError(msg), sys.exc_info()[2])
+
+            # update reddered_kws so that others can use the new source
+            renderer_kws['data_source'] = source
 
         # handle the main glyph, need to process literals
         glyph_ca = _pop_colors_and_alpha(glyphclass, kwargs)


Test patch:
diff --git a/bokeh/plotting/tests/test_figure.py b/bokeh/plotting/tests/test_figure.py
--- a/bokeh/plotting/tests/test_figure.py
+++ b/bokeh/plotting/tests/test_figure.py
@@ -1,6 +1,7 @@
 from __future__ import absolute_import
 import unittest
 import pytest
+import pandas as pd
 
 from bokeh.core.properties import value
 from bokeh.models import (
@@ -161,6 +162,16 @@ def test_plot_fill_props(self):
         self.assertEqual(p.background_fill_color, 'green')
         self.assertEqual(p.border_fill_color, 'yellow')
 
+    def test_columnsource_auto_conversion_from_dict(self):
+        p = plt.figure()
+        dct = {'x': [1, 2, 3], 'y': [2, 3, 4]}
+        p.circle(x='x', y='y', source=dct)
+
+    def test_columnsource_auto_conversion_from_pandas(self):
+        p = plt.figure()
+        df = pd.DataFrame({'x': [1, 2, 3], 'y': [2, 3, 4]})
+        p.circle(x='x', y='y', source=df)
+
 
 class TestMarkers(unittest.TestCase):