1#-----------------------------------------------------------------------------
2# Copyright (c) 2012 - 2021, Anaconda, Inc., and Bokeh Contributors.
3# All rights reserved.
4#
5# The full license is in the file LICENSE.txt, distributed with this software.
6#-----------------------------------------------------------------------------
7'''
8
9'''
10
11#-----------------------------------------------------------------------------
12# Boilerplate
13#-----------------------------------------------------------------------------
14import logging # isort:skip
15log = logging.getLogger(__name__)
16
17#-----------------------------------------------------------------------------
18# Imports
19#-----------------------------------------------------------------------------
20
21# Standard library imports
22from os.path import abspath
23from warnings import warn
24
25# Bokeh imports
26from ..settings import settings
27from .state import curstate
28from .util import default_filename
29
30#-----------------------------------------------------------------------------
31# Globals and constants
32#-----------------------------------------------------------------------------
33
34DEFAULT_TITLE = "Bokeh Plot"
35
36__all__ = (
37    'save',
38)
39
40#-----------------------------------------------------------------------------
41# General API
42#-----------------------------------------------------------------------------
43
44def save(obj, filename=None, resources=None, title=None, template=None, state=None, **kwargs):
45    ''' Save an HTML file with the data for the current document.
46
47    Will fall back to the default output state (or an explicitly provided
48    :class:`State` object) for ``filename``, ``resources``, or ``title`` if they
49    are not provided. If the filename is not given and not provided via output state,
50    it is derived from the script name (e.g. ``/foo/myplot.py`` will create
51    ``/foo/myplot.html``)
52
53    Args:
54        obj (LayoutDOM object) : a Layout (Row/Column), Plot or Widget object to display
55
56        filename (str, optional) : filename to save document under (default: None)
57            If None, use the default state configuration.
58
59        resources (Resources, optional) : A Resources config to use (default: None)
60            If None, use the default state configuration, if there is one.
61            otherwise use ``resources.INLINE``.
62
63        title (str, optional) : a title for the HTML document (default: None)
64            If None, use the default state title value, if there is one.
65            Otherwise, use "Bokeh Plot"
66
67        template (Template, optional) : HTML document template (default: FILE)
68            A Jinja2 Template, see bokeh.core.templates.FILE for the required template
69            parameters
70
71        state (State, optional) :
72            A :class:`State` object. If None, then the current default
73            implicit state is used. (default: None).
74
75    Returns:
76        str: the filename where the HTML file is saved.
77
78    '''
79
80    if state is None:
81        state = curstate()
82
83    theme = state.document.theme
84
85    filename, resources, title = _get_save_args(state, filename, resources, title)
86    _save_helper(obj, filename, resources, title, template, theme)
87    return abspath(filename)
88
89#-----------------------------------------------------------------------------
90# Dev API
91#-----------------------------------------------------------------------------
92
93#-----------------------------------------------------------------------------
94# Private API
95#-----------------------------------------------------------------------------
96
97def _get_save_args(state, filename, resources, title):
98    '''
99
100    '''
101    filename, is_default_filename = _get_save_filename(state, filename)
102
103    resources = _get_save_resources(state, resources, is_default_filename)
104
105    title = _get_save_title(state, title, is_default_filename)
106
107    return filename, resources, title
108
109def _get_save_filename(state, filename):
110    if filename is not None:
111        return filename, False
112
113    if state.file and not settings.ignore_filename():
114        return state.file['filename'], False
115
116    return default_filename("html"), True
117
118def _get_save_resources(state, resources, suppress_warning):
119    if resources is not None:
120        return resources
121
122    if state.file:
123        return state.file['resources']
124
125    if not suppress_warning:
126        warn("save() called but no resources were supplied and output_file(...) was never called, defaulting to resources.CDN")
127
128    from ..resources import Resources
129    return Resources(mode=settings.resources())
130
131def _get_save_title(state, title, suppress_warning):
132    if title is not None:
133        return title
134
135    if state.file:
136        return state.file['title']
137
138    if not suppress_warning:
139        warn("save() called but no title was supplied and output_file(...) was never called, using default title 'Bokeh Plot'")
140
141    return DEFAULT_TITLE
142
143def _save_helper(obj, filename, resources, title, template, theme=None):
144    '''
145
146    '''
147    from ..embed import file_html
148    html = file_html(obj, resources, title=title, template=template, theme=theme)
149
150    with open(filename, mode="w", encoding="utf-8") as f:
151        f.write(html)
152
153#-----------------------------------------------------------------------------
154# Code
155#-----------------------------------------------------------------------------
156