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''' Encapsulate implicit state that is useful for Bokeh plotting APIs.
8
9.. note::
10    While ``State`` objects can also be manipulated explicitly, they are
11    automatically configured when the functions :func:`~bokeh.io.output_file`,
12    etc. from :ref:`bokeh.io` are used, so this is not necessary under
13    typical usage.
14
15Generating output for Bokeh plots requires coordinating several things:
16
17:class:`~bokeh.document.Document`
18    Groups together Bokeh models that may be shared between plots (e.g.,
19    range or data source objects) into one common structure.
20
21:class:`~bokeh.resources.Resources`
22    Control how JavaScript and CSS for the client library BokehJS are
23    included and used in the generated output.
24
25It is possible to handle the configuration of these things manually, and some
26examples of doing this can be found in ``examples/models`` directory. When
27developing sophisticated applications, it may be necessary or desirable to work
28at this level. However, for general use this would quickly become burdensome.
29This module provides a ``State`` class that encapsulates these objects and
30ensures their proper configuration in many common usage scenarios.
31
32'''
33
34#-----------------------------------------------------------------------------
35# Boilerplate
36#-----------------------------------------------------------------------------
37import logging # isort:skip
38log = logging.getLogger(__name__)
39
40#-----------------------------------------------------------------------------
41# Imports
42#-----------------------------------------------------------------------------
43
44# Standard library imports
45import os
46
47# Bokeh imports
48from ..document import Document
49from ..resources import Resources
50
51#-----------------------------------------------------------------------------
52# Globals and constants
53#-----------------------------------------------------------------------------
54
55__all__ = (
56    'curstate',
57    'State',
58)
59
60#-----------------------------------------------------------------------------
61# General API
62#-----------------------------------------------------------------------------
63
64class State:
65    ''' Manage state related to controlling Bokeh output.
66
67    '''
68    def __init__(self):
69        self.last_comms_handle = None
70        self.uuid_to_server = {} # Mapping from uuid to server instance
71        self.reset()
72
73    # Properties --------------------------------------------------------------
74
75    @property
76    def document(self):
77        ''' A default :class:`~bokeh.document.Document` to use for all
78        output operations.
79
80        '''
81        return self._document
82
83    @document.setter
84    def document(self, doc):
85        self._document = doc
86
87    @property
88    def file(self):
89        ''' A dict with the default configuration for file output (READ ONLY)
90
91        The dictionary value has the following form:
92
93        .. code-block:: python
94
95            {
96                'filename'  : # filename to use when saving
97                'resources' : # resources configuration
98                'title'     : # a title for the HTML document
99            }
100
101        '''
102        return self._file
103
104    @property
105    def notebook(self):
106        ''' Whether to generate notebook output on show operations. (READ ONLY)
107
108        '''
109        return self._notebook
110
111    @property
112    def notebook_type(self):
113        ''' Notebook type
114
115        '''
116        return self._notebook_type
117
118    @notebook_type.setter
119    def notebook_type(self, notebook_type):
120        ''' Notebook type, acceptable values are 'jupyter' as well as any names
121        defined by external notebook hooks that have been installed.
122
123        '''
124        if notebook_type is None or not isinstance(notebook_type, str):
125            raise ValueError("Notebook type must be a string")
126        self._notebook_type = notebook_type.lower()
127
128    # Public methods ----------------------------------------------------------
129
130    def output_file(self, filename, title="Bokeh Plot", mode=None, root_dir=None):
131        ''' Configure output to a standalone HTML file.
132
133        Calling ``output_file`` not clear the effects of any other calls to
134        ``output_notebook``, etc. It adds an additional output destination
135        (publishing to HTML files). Any other active output modes continue
136        to be active.
137
138        Args:
139            filename (str) : a filename for saving the HTML document
140
141            title (str, optional) : a title for the HTML document
142
143            mode (str, optional) : how to include BokehJS (default: ``'cdn'``)
144
145                One of: ``'inline'``, ``'cdn'``, ``'relative(-dev)'`` or
146                ``'absolute(-dev)'``. See :class:`~bokeh.resources.Resources`
147                for more details.
148
149            root_dir (str, optional) : root dir to use for absolute resources
150                (default: None)
151
152                This value is ignored for other resource types, e.g. ``INLINE`` or ``CDN``.
153
154        .. warning::
155            The specified output file will be overwritten on every save, e.g.,
156            every time ``show()`` or ``save()`` is called.
157
158        '''
159        self._file = {
160            'filename'  : filename,
161            'resources' : Resources(mode=mode, root_dir=root_dir),
162            'title'     : title
163        }
164
165        if os.path.isfile(filename):
166            log.info("Session output file '%s' already exists, will be overwritten." % filename)
167
168    def output_notebook(self, notebook_type='jupyter'):
169        ''' Generate output in notebook cells.
170
171        Calling ``output_notebook`` not clear the effects of any other calls
172        to ``output_file``, etc. It adds an additional output destination
173        (publishing to notebook output cells). Any other active output modes
174        continue to be active.
175
176        Returns:
177            None
178
179        '''
180        self._notebook = True
181        self.notebook_type = notebook_type
182
183    def reset(self):
184        ''' Deactivate all currently active output modes and set ``curdoc()``
185        to a fresh empty ``Document``.
186
187        Subsequent calls to ``show()`` will not render until a new output mode
188        is activated.
189
190        Returns:
191            None
192
193        '''
194        self._reset_with_doc(Document())
195
196    # Private methods ---------------------------------------------------------
197
198    def _reset_keeping_doc(self):
199        ''' Reset output modes but DO NOT replace the default Document
200
201        '''
202        self._file = None
203        self._notebook = False
204        self._notebook_type = None
205
206    def _reset_with_doc(self, doc):
207        ''' Reset output modes but DO replace the default Document
208
209        '''
210        self._document = doc
211        self._reset_keeping_doc()
212
213def curstate():
214    ''' Return the current State object
215
216    Returns:
217      State : the current default State object
218
219    '''
220    global _STATE
221    if _STATE is None:
222        _STATE = State()
223    return _STATE
224
225#-----------------------------------------------------------------------------
226# Dev API
227#-----------------------------------------------------------------------------
228
229#-----------------------------------------------------------------------------
230# Private API
231#-----------------------------------------------------------------------------
232
233_STATE = None
234
235#-----------------------------------------------------------------------------
236# Code
237#-----------------------------------------------------------------------------
238