Repository: bokeh/bokeh

Issue:
plotting.save still relies on global state
Reference: https://groups.google.com/a/continuum.io/forum/#!topic/bokeh/pyMpqY2p4e0

The problem is this line:

https://github.com/bokeh/bokeh/blob/master/bokeh/plotting.py#L590

where `save` unconditionally uses `_default_file['title']`. Save should add `title` as an optional parameter. It can either default to `None` and then check global state (just as is done at the top of `save` with `filename` etc.) or we could provide a generic default title like "Bokeh Plot".  

@shughes-uk you asked for some small tasks, here you go :)



Discussion:

Resolving patch:
diff --git a/bokeh/plotting.py b/bokeh/plotting.py
--- a/bokeh/plotting.py
+++ b/bokeh/plotting.py
@@ -540,7 +540,7 @@ def show(obj=None, browser=None, new="tab", url=None):
         controller.open("file://" + os.path.abspath(filename), new=new_param)
 
 
-def save(filename=None, resources=None, obj=None):
+def save(filename=None, resources=None, obj=None, title=None):
     """ Updates the file with the data for the current document.
 
     If a filename is supplied, or output_file(...) has been called, this will
@@ -550,11 +550,13 @@ def save(filename=None, resources=None, obj=None):
         filename (str, optional) : filename to save document under (default: None)
             if `filename` is None, the current output_file(...) filename is used if present
         resources (Resources, optional) : BokehJS resource config to use
-            if `resources` is None, the current default resource config is used
+            if `resources` is None, the current default resource config is used, failing that resources.INLINE is used
 
         obj (Document or Widget/Plot object, optional)
             if provided, then this is the object to save instead of curdoc()
             and its curplot()
+        title (str, optional) : title of the bokeh plot (default: None)
+        	if 'title' is None, the current default title config is used, failing that 'Bokeh Plot' is used
 
     Returns:
         None
@@ -566,13 +568,22 @@ def save(filename=None, resources=None, obj=None):
     if resources is None and _default_file:
         resources = _default_file['resources']
 
+    if title is None and _default_file:
+        title = _default_file['title']
+
     if not filename:
         warnings.warn("save() called but no filename was supplied and output_file(...) was never called, nothing saved")
         return
 
     if not resources:
-        warnings.warn("save() called but no resources was supplied and output_file(...) was never called, nothing saved")
-        return
+        warnings.warn("save() called but no resources was supplied and output_file(...) was never called, defaulting to resources.INLINE")
+        from .resources import INLINE
+        resources = INLINE
+
+
+    if not title:
+        warnings.warn("save() called but no title was supplied and output_file(...) was never called, using default title 'Bokeh Plot'")
+        title = "Bokeh Plot"
 
     if obj is None:
         if not curplot():
@@ -587,7 +598,7 @@ def save(filename=None, resources=None, obj=None):
     else:
         raise RuntimeError("Unable to save object of type '%s'" % type(obj))
 
-    html = file_html(doc, resources, _default_file['title'])
+    html = file_html(doc, resources, title)
     with io.open(filename, "w", encoding="utf-8") as f:
         f.write(decode_utf8(html))
 


Test patch: