cubevis 1.0.0__py3-none-any.whl → 1.0.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. cubevis/__init__.py +12 -23
  2. cubevis/__js__/bokeh-3.6/cubevisjs.min.js +64 -0
  3. cubevis/__js__/{cubevisjs.min.js → bokeh-3.7/cubevisjs.min.js} +3 -3
  4. cubevis/__js__/bokeh-3.8/cubevisjs.min.js +64 -0
  5. cubevis/__js__/casalib.min.js +1 -1
  6. cubevis/__version__.py +1 -1
  7. cubevis/bokeh/__init__.py +16 -0
  8. cubevis/bokeh/annotations/_ev_poly_annotation.py +2 -1
  9. cubevis/bokeh/format/_wcs_ticks.py +6 -2
  10. cubevis/bokeh/models/__init__.py +1 -0
  11. cubevis/bokeh/models/_showable.py +352 -0
  12. cubevis/bokeh/models/_tip.py +2 -1
  13. cubevis/bokeh/models/_tip_button.py +2 -3
  14. cubevis/bokeh/sources/_data_pipe.py +6 -2
  15. cubevis/bokeh/sources/_image_data_source.py +6 -2
  16. cubevis/bokeh/sources/_image_pipe.py +4 -1
  17. cubevis/bokeh/sources/_spectra_data_source.py +6 -3
  18. cubevis/bokeh/sources/_updatable_data_source.py +6 -2
  19. cubevis/bokeh/state/__init__.py +4 -3
  20. cubevis/bokeh/state/_current.py +34 -0
  21. cubevis/bokeh/state/_initialize.py +282 -116
  22. cubevis/bokeh/state/_javascript.py +95 -21
  23. cubevis/bokeh/tools/_cbreset_tool.py +2 -1
  24. cubevis/bokeh/tools/_drag_tool.py +2 -1
  25. cubevis/bokeh/utils/__init__.py +0 -1
  26. cubevis/exe/_setting.py +1 -0
  27. cubevis/private/apps/__init__.py +4 -2
  28. cubevis/private/apps/_interactiveclean.mustache +6 -2
  29. cubevis/private/apps/_interactiveclean.py +6 -2
  30. cubevis/private/apps/_interactivecleannotebook.mustache +112 -0
  31. cubevis/private/apps/_interactivecleannotebook.py +1874 -0
  32. cubevis/private/casatasks/iclean.py +4 -0
  33. cubevis/toolbox/_app_context.py +5 -9
  34. cubevis/toolbox/_cube.py +6 -2
  35. cubevis/toolbox/_interactive_clean_ui.mustache +20 -31
  36. cubevis/toolbox/_interactive_clean_ui.py +20 -31
  37. cubevis/utils/__init__.py +183 -41
  38. cubevis/utils/_git.py +36 -0
  39. cubevis/utils/_jupyter.py +12 -0
  40. {cubevis-1.0.0.dist-info → cubevis-1.0.4.dist-info}/METADATA +3 -3
  41. {cubevis-1.0.0.dist-info → cubevis-1.0.4.dist-info}/RECORD +43 -39
  42. cubevis/__js__/bokeh-3.6.1.min.js +0 -728
  43. cubevis/__js__/bokeh-tables-3.6.1.min.js +0 -119
  44. cubevis/__js__/bokeh-widgets-3.6.1.min.js +0 -141
  45. {cubevis-1.0.0.dist-info → cubevis-1.0.4.dist-info}/WHEEL +0 -0
  46. {cubevis-1.0.0.dist-info → cubevis-1.0.4.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,352 @@
1
+ import logging
2
+ from bokeh.models.layouts import LayoutDOM
3
+ from bokeh.models.ui import UIElement
4
+ from bokeh.core.properties import Instance
5
+ from bokeh.io import curdoc
6
+ from .. import BokehInit
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ class Showable(LayoutDOM,BokehInit):
11
+ """Wrap a UIElement to make any Bokeh UI component showable with show()
12
+
13
+ This class works by acting as a simple container that delegates to its UI element.
14
+ For Jupyter notebook display, use show(showable) - automatic display via _repr_mimebundle_
15
+ is not reliably supported by Bokeh's architecture.
16
+ """
17
+
18
+ @property
19
+ def document(self):
20
+ """Get the document this model is attached to."""
21
+ return getattr(self, '_document', None)
22
+
23
+ @document.setter
24
+ def document(self, doc):
25
+ """
26
+ Intercept when Bokeh tries to attach us to a document.
27
+ This is called by bokeh.plotting.show() when it adds us to a document.
28
+ """
29
+ from bokeh.io.state import curstate
30
+ import traceback
31
+
32
+ # Allow None (detaching from document)
33
+ if doc is None:
34
+ self._document = None
35
+ return
36
+
37
+ state = curstate()
38
+
39
+ # Check calling context
40
+ stack = traceback.extract_stack()
41
+
42
+ # Detect if called from bokeh.plotting.show or bokeh.io.show
43
+ called_from_bokeh_show = any(
44
+ ('bokeh/io/' in frame.filename or 'bokeh\\io\\' in frame.filename or
45
+ 'bokeh/plotting/' in frame.filename or 'bokeh\\plotting\\' in frame.filename)
46
+ for frame in stack[:-2] # Exclude the last 2 frames (this setter and __setattr__)
47
+ )
48
+
49
+ # Check if called from our own methods
50
+ called_from_our_methods = any(
51
+ 'Showable' in str(frame.line) or frame.filename.endswith('showable.py')
52
+ for frame in stack[-5:-2] # Check recent frames
53
+ )
54
+
55
+ if state.file and called_from_bokeh_show and not called_from_our_methods:
56
+ raise RuntimeError(
57
+ f"\n{'='*70}\n"
58
+ f"❌ Cannot use bokeh.plotting.show() with {self.__class__.__name__}\n\n"
59
+ f"Please use one of these methods instead:\n"
60
+ f" • my_showable.show() # Custom show method\n"
61
+ f" • my_showable # Automatic display (evaluate in cell)\n\n"
62
+ f"Reason: bokeh.plotting.show() doesn't properly handle the custom\n"
63
+ f"sizing and backend requirements of Showable objects.\n"
64
+ f"{'='*70}\n"
65
+ )
66
+
67
+ # Validate environment
68
+ if not state.notebook:
69
+ raise RuntimeError(
70
+ f"{self.__class__.__name__} can only be displayed in Jupyter notebooks.\n"
71
+ f"Please run:\n"
72
+ f" from bokeh.io import output_notebook\n"
73
+ f" output_notebook()"
74
+ )
75
+
76
+ # Apply notebook sizing before attaching to document
77
+ if self._notebook_sizing == 'fixed':
78
+ self.sizing_mode = None
79
+ self.width = self._notebook_width
80
+ self.height = self._notebook_height
81
+
82
+ # Now set the document
83
+ self._document = doc
84
+
85
+ # Start backend if needed
86
+ if not hasattr(self, '_backend_started') or not self._backend_started:
87
+ self._start_backend()
88
+ self._backend_started = True
89
+
90
+ def __init__( self, ui_element=None, backend_func=None,
91
+ result_retrieval=None,
92
+ notebook_width=1200, notebook_height=800,
93
+ notebook_sizing='fixed', **kwargs):
94
+ logger.debug(f"\tShowable::__init__(ui_element={type(ui_element).__name__ if ui_element else None}, {kwargs}): {id(self)}")
95
+
96
+ # Set default sizing if not provided
97
+ sizing_params = {'sizing_mode', 'width', 'height'}
98
+ provided_sizing_params = set(kwargs.keys()) & sizing_params
99
+ if not provided_sizing_params:
100
+ kwargs['sizing_mode'] = 'stretch_both'
101
+
102
+ # CRITICAL FIX: Don't call _ensure_in_document during __init__
103
+ # Let Bokeh handle document management through the normal flow
104
+ super().__init__(**kwargs)
105
+
106
+ # Set the UI element
107
+ if ui_element is not None:
108
+ self.ui = ui_element
109
+
110
+ # Set the function to be called upon display
111
+ if backend_func is not None:
112
+ self._backend_startup_callback = backend_func
113
+ # function to be called to fetch the Showable GUI
114
+ # result (if one is/will be available)...
115
+ self._result_retrieval = result_retrieval
116
+
117
+ self._notebook_width = notebook_width
118
+ self._notebook_height = notebook_height
119
+ self._notebook_sizing = notebook_sizing # 'fixed' or 'stretch'
120
+ self._notebook_rendering = None
121
+
122
+ ui = Instance(UIElement, help="""
123
+ A UI element, which can be plots, layouts, widgets, or any other UIElement.
124
+ """)
125
+
126
+ # FIXED: Remove the children property override
127
+ # Let LayoutDOM handle its own children management
128
+ # The TypeScript side will handle the UI element rendering
129
+
130
+ def _sphinx_height_hint(self):
131
+ """Delegate height hint to the wrapped UI element"""
132
+ logger.debug(f"\tShowable::_sphinx_height_hint(): {id(self)}")
133
+ if self.ui and hasattr(self.ui, '_sphinx_height_hint'):
134
+ return self.ui._sphinx_height_hint()
135
+ return None
136
+
137
+ def _ensure_in_document(self):
138
+ """Ensure this Showable is in the current document"""
139
+ from bokeh.io import curdoc
140
+ current_doc = curdoc()
141
+
142
+ # FIXED: More careful document management
143
+ # Only add to document if we're not already in the right one
144
+ if self.document is None:
145
+ current_doc.add_root(self)
146
+ logger.debug(f"\tShowable::_ensure_in_document(): Added {id(self)} to document {id(current_doc)}")
147
+ elif self.document is not current_doc:
148
+ # Remove from old document first
149
+ if self in self.document.roots:
150
+ self.document.remove_root(self)
151
+ current_doc.add_root(self)
152
+ logger.debug(f"\tShowable::_ensure_in_document(): Moved {id(self)} to document {id(current_doc)}")
153
+
154
+ # HOOK: Backend startup when added to document
155
+ # This catches both direct show() calls and Bokeh's show() function
156
+ if not hasattr(self, '_backend_started'):
157
+ self._start_backend( )
158
+ self._backend_started = True
159
+
160
+ def get_future(self):
161
+ if self._result_retrieval is None:
162
+ raise RuntimeError( f"{self.name if self.name else 'this showable'} does not return a result" )
163
+ else:
164
+ return self._result_retrieval( )
165
+
166
+ def get_result(self):
167
+ if self._result_retrieval is None:
168
+ raise RuntimeError( f"{self.name if self.name else 'this showable'} does not return a result" )
169
+ else:
170
+ return self._result_retrieval( ).result( )
171
+
172
+ def _start_backend(self):
173
+ """Hook to start backend services when showing"""
174
+ # Override this in subclasses or set a callback
175
+ if hasattr(self, '_backend_startup_count'):
176
+ ### backend has already been started
177
+ ### must figure out what is the proper way to handle this case
178
+ logger.debug(f"\tShowable::_start_backend(): backend already started for {id(self)} [{self._backend_startup_count}]")
179
+ self._backend_startup_count += 1
180
+ return
181
+
182
+ if hasattr(self, '_backend_startup_callback'):
183
+ try:
184
+ self._backend_startup_callback()
185
+ logger.debug(f"\tShowable::_start_backend(): Executed startup callback for {id(self)}")
186
+ self._backend_startup_count = 1
187
+ except Exception as e:
188
+ logger.error(f"\tShowable::_start_backend(): Error in startup callback: {e}")
189
+
190
+ # Example: Start asyncio backend
191
+ # if hasattr(self, '_backend_manager'):
192
+ # self._backend_manager.start()
193
+
194
+ logger.debug(f"\tShowable::_start_backend(): Backend startup hook called for {id(self)}")
195
+
196
+ def set_backend_startup_callback(self, callback):
197
+ """Set a callback to be called when show() is invoked"""
198
+ if not callable(callback):
199
+ raise ValueError("Backend startup callback must be callable")
200
+ self._backend_startup_callback = callback
201
+ logger.debug(f"\tShowable::set_backend_startup_callback(): Set callback for {id(self)}")
202
+
203
+ def _stop_backend(self):
204
+ """Hook to stop backend services - override in subclasses"""
205
+ if hasattr(self, '_backend_cleanup_callback'):
206
+ try:
207
+ self._backend_cleanup_callback()
208
+ logger.debug(f"\tShowable::_stop_backend(): Executed cleanup callback for {id(self)}")
209
+ except Exception as e:
210
+ logger.error(f"\tShowable::_stop_backend(): Error in cleanup callback: {e}")
211
+
212
+ logger.debug(f"\tShowable::_stop_backend(): Backend cleanup hook called for {id(self)}")
213
+
214
+ def set_backend_cleanup_callback(self, callback):
215
+ """Set a callback to be called when cleaning up backend"""
216
+ if not callable(callback):
217
+ raise ValueError("Backend cleanup callback must be callable")
218
+ self._backend_cleanup_callback = callback
219
+ logger.debug(f"\tShowable::set_backend_cleanup_callback(): Set callback for {id(self)}")
220
+
221
+ def __del__(self):
222
+ """Cleanup when Showable is destroyed"""
223
+ if hasattr(self, '_backend_startup_callback') and self._backend_startup_callback:
224
+ self._stop_backend()
225
+
226
+ def _get_notebook_html(self, start_backend=True):
227
+ """
228
+ Common logic for generating HTML in notebook environments.
229
+ Returns the HTML string to display, or None if not in a notebook.
230
+ """
231
+ from bokeh.embed import components
232
+ from bokeh.io.state import curstate
233
+
234
+ state = curstate()
235
+
236
+ if not state.notebook:
237
+ return None
238
+
239
+ if self.ui is None:
240
+ return '<div style="color: red; padding: 10px; border: 1px solid red;">Showable object with no UI set</div>'
241
+
242
+ if self._notebook_rendering:
243
+ # Return a lightweight reference instead of re-rendering the full GUI
244
+ return f'''
245
+ <div style="padding: 10px; background: #f0f8f0; border-left: 4px solid #4CAF50; margin: 5px 0;">
246
+ <strong>↑ iclean GUI active above</strong>
247
+ <small style="color: #666; display: block; margin-top: 5px;">
248
+ Showable ID: {self.id[-8:]} | Backend: Running
249
+ </small>
250
+ </div>
251
+ '''
252
+
253
+ # Apply notebook sizing for Jupyter context
254
+ if self._notebook_sizing == 'fixed':
255
+ self.sizing_mode = None
256
+ self.width = self._notebook_width
257
+ self.height = self._notebook_height
258
+
259
+ script, div = components(self)
260
+ if start_backend:
261
+ self._start_backend()
262
+
263
+ self._notebook_rendering = f'{script}\n{div}'
264
+ return self._notebook_rendering
265
+
266
+ def _repr_html_(self, start_backend=True):
267
+ """
268
+ HTML representation for Jupyter display.
269
+
270
+ Note: Bokeh doesn't reliably support automatic display via _repr_mimebundle_.
271
+ This provides a helpful message directing users to use show().
272
+ """
273
+ html = self._get_notebook_html(start_backend)
274
+
275
+ if html is not None:
276
+ return html
277
+
278
+ # Not in notebook environment
279
+ return f"<!-- error: non-notebook environment{' in ' + self.name if self.name else ''} -->" + '''
280
+ <div style="padding: 15px; border: 2px solid #4CAF50; border-radius: 5px; background: #f9fff9; margin: 10px 0;">
281
+ <strong>📊 Showable Widget Ready</strong><br>
282
+ <em>Notebook display is not enabled, run:</em>
283
+ <p><pre>
284
+ from bokeh.io import output_notebook
285
+ output_notebook()</pre>
286
+ <p><em>and try again.</em>
287
+ <hr>
288
+ <small>Contains: {}</small>
289
+ </div>
290
+ '''.format(type(self.ui).__name__ if self.ui else "None")
291
+
292
+ def _repr_mimebundle_(self, include=None, exclude=None):
293
+ """
294
+ MIME bundle representation for Jupyter display.
295
+
296
+ This is called by Bokeh's show() function and IPython's display system.
297
+ By implementing this, we ensure consistent display whether the object
298
+ is displayed via:
299
+ - Automatic display (just evaluating the object)
300
+ - IPython display(showable)
301
+ - Bokeh show(showable)
302
+ - showable.show()
303
+ """
304
+ from bokeh.io.state import curstate
305
+
306
+ state = curstate()
307
+
308
+ if state.notebook:
309
+ html = self._get_notebook_html(start_backend=True)
310
+ if html:
311
+ return {
312
+ 'text/html': html
313
+ }
314
+
315
+ # Fall back to default Bokeh behavior for non-notebook environments
316
+ # Return None to let Bokeh handle it
317
+ return None
318
+
319
+ def show(self, start_backend=True):
320
+ """Explicitly show this Showable using inline display in Jupyter"""
321
+ from bokeh.io.state import curstate
322
+
323
+ self._ensure_in_document()
324
+
325
+ state = curstate()
326
+
327
+ if state.notebook:
328
+ # In Jupyter, display directly using IPython.display
329
+ from IPython.display import display, HTML
330
+
331
+ html = self._get_notebook_html(start_backend)
332
+ if html:
333
+ display(HTML(html))
334
+ return
335
+
336
+ # Fall back to standard Bokeh show for non-notebook environments
337
+ from bokeh.io import show as bokeh_show
338
+ if start_backend:
339
+ self._start_backend()
340
+ bokeh_show(self)
341
+
342
+ def __str__(self):
343
+ """String conversion"""
344
+ name = f", name='{self.name}'" if self.name else ""
345
+ return f"{self.__class__.__name__}(id='{self.id}'{name} ...)"
346
+
347
+ def __repr__(self):
348
+ """String representation from repr(...)"""
349
+ ui_type = type(self.ui).__name__ if self.ui else "None"
350
+ doc_info = f"doc='{id(self.document)}'" if self.document else "doc=None"
351
+ backend_info = f"backend='{'started' if getattr(self, '_backend_startup_count', 0) else 'not-started'}'"
352
+ return f"{self.__class__.__name__}(id='{self.id}', name='{self.name}', ui='{ui_type}', {doc_info}, {backend_info})"
@@ -1,8 +1,9 @@
1
1
  from bokeh.models import Tooltip
2
2
  from bokeh.models.layouts import LayoutDOM, UIElement
3
3
  from bokeh.core.properties import Instance, Required, Float, Int, Either
4
+ from .. import BokehInit
4
5
 
5
- class Tip(LayoutDOM):
6
+ class Tip(LayoutDOM,BokehInit):
6
7
  '''Display a tooltip for the child element
7
8
  '''
8
9
 
@@ -6,10 +6,9 @@ from bokeh.models.widgets.buttons import AbstractButton
6
6
  from bokeh.models.ui.icons import BuiltinIcon
7
7
  from bokeh.core.properties import Instance, Required, Override, Nullable, Float, Int, Either
8
8
  from bokeh.models import Tooltip
9
+ from .. import BokehInit
9
10
 
10
-
11
-
12
- class TipButton(AbstractButton):
11
+ class TipButton(AbstractButton,BokehInit):
13
12
  """ A button with a help symbol that displays additional text when hovered
14
13
  over or clicked.
15
14
  """
@@ -44,8 +44,9 @@ from bokeh.models.callbacks import Callback
44
44
 
45
45
  from ...utils import serialize, deserialize
46
46
  from ..state import casalib_url, cubevisjs_url
47
+ from .. import BokehInit
47
48
 
48
- class DataPipe(DataSource):
49
+ class DataPipe(DataSource,BokehInit):
49
50
  """This class allows for communication between Python and the JavaScript implementation
50
51
  running in a browser. It allows Python code to send a message to JavaScript and register
51
52
  a callback which will receive the result. JavaScript code can do the same to request
@@ -75,7 +76,10 @@ class DataPipe(DataSource):
75
76
  _active_sessions = {} # session_id -> {'websocket': ws, 'timestamp': time, 'datapipe': instance}
76
77
  _session_lock = threading.Lock()
77
78
 
78
- __javascript__ = [ casalib_url( ), cubevisjs_url( ) ]
79
+ ###################################################################
80
+ ### filled from cubevis.bokeh.state._initialize._order_bokeh_js ###
81
+ ###################################################################
82
+ #__javascript__ = [ casalib_url( ), cubevisjs_url( ) ]
79
83
 
80
84
  def __init__( self, *args, abort=None, **kwargs ):
81
85
  super( ).__init__( *args, **kwargs )
@@ -34,8 +34,9 @@ from bokeh.core.properties import Instance, Tuple, Int, Nullable
34
34
  from bokeh.models.callbacks import Callback
35
35
  from ._image_pipe import ImagePipe
36
36
  from ..state import casalib_url, cubevisjs_url
37
+ from .. import BokehInit
37
38
 
38
- class ImageDataSource(ColumnDataSource):
39
+ class ImageDataSource(ColumnDataSource,BokehInit):
39
40
  """Implementation of a ``ColumnDataSource`` customized for planes from
40
41
  `CASA`/`CNGI` image cubes. This is designed to use an `ImagePipe` to
41
42
  update the image channel/plane displayed in a browser, app or notebook
@@ -59,7 +60,10 @@ class ImageDataSource(ColumnDataSource):
59
60
  num_chans = Tuple( Int, Int, help="[ num-stokes-planes, num-channels ]" )
60
61
  cur_chan = Tuple( Int, Int, help="[ num-stokes-planes, num-channels ]" )
61
62
 
62
- __javascript__ = [ casalib_url( ), cubevisjs_url( ) ]
63
+ ###################################################################
64
+ ### filled from cubevis.bokeh.state._initialize._order_bokeh_js ###
65
+ ###################################################################
66
+ #__javascript__ = [ casalib_url( ), cubevisjs_url( ) ]
63
67
 
64
68
  def __init__( self, *args, **kwargs ):
65
69
  super( ).__init__( *args, **kwargs )
@@ -85,7 +85,10 @@ class ImagePipe(DataPipe):
85
85
  data source for (raw) image channel histogram of intensities used with a "figure.quad(...)"
86
86
  ''')
87
87
 
88
- __javascript__ = [ casalib_url( ), cubevisjs_url( ) ]
88
+ ###################################################################
89
+ ### filled from cubevis.bokeh.state._initialize._order_bokeh_js ###
90
+ ###################################################################
91
+ #__javascript__ = [ casalib_url( ), cubevisjs_url( ) ]
89
92
 
90
93
  def __open_image( self, image ):
91
94
  if self.__img is not None:
@@ -32,9 +32,9 @@ from bokeh.util.compiler import TypeScript
32
32
  from bokeh.core.properties import Instance
33
33
  from . import ImagePipe
34
34
  from ..state import casalib_url, cubevisjs_url
35
+ from .. import BokehInit
35
36
 
36
-
37
- class SpectraDataSource(ColumnDataSource):
37
+ class SpectraDataSource(ColumnDataSource,BokehInit):
38
38
  """Implementation of a ``ColumnDataSource`` customized for spectral lines
39
39
  from `CASA`/`CNGI` image cubes. This is designed to use an `ImagePipe` to
40
40
  update the spectral line plot displayed in a browser, app or notebook with
@@ -48,7 +48,10 @@ class SpectraDataSource(ColumnDataSource):
48
48
 
49
49
  image_source = Instance(ImagePipe)
50
50
 
51
- __javascript__ = [ casalib_url( ), cubevisjs_url( ) ]
51
+ ###################################################################
52
+ ### filled from cubevis.bokeh.state._initialize._order_bokeh_js ###
53
+ ###################################################################
54
+ #__javascript__ = [ casalib_url( ), cubevisjs_url( ) ]
52
55
 
53
56
  def __init__( self, *args, **kwargs ):
54
57
  super( ).__init__( *args, **kwargs )
@@ -38,8 +38,9 @@ import websockets
38
38
  from ._data_pipe import DataPipe
39
39
  from ..state import casalib_url, cubevisjs_url
40
40
  from ...utils import find_ws_address
41
+ from .. import BokehInit
41
42
 
42
- class UpdatableDataSource(ColumnDataSource):
43
+ class UpdatableDataSource(ColumnDataSource,BokehInit):
43
44
  """Implementation of a `ColumnDataSource` customized to allow updates from Python via
44
45
  WebSockets after the Bokeh GUI has been displayed. This class is derived from `ColumnDataSource`
45
46
  and provides an `update` function that can be used to update the plotted data. While this
@@ -87,7 +88,10 @@ class UpdatableDataSource(ColumnDataSource):
87
88
  Internal id used for communcations.
88
89
  """ )
89
90
 
90
- __javascript__ = [ casalib_url( ), cubevisjs_url( ) ]
91
+ ###################################################################
92
+ ### filled from cubevis.bokeh.state._initialize._order_bokeh_js ###
93
+ ###################################################################
94
+ #__javascript__ = [ casalib_url( ), cubevisjs_url( ) ]
91
95
 
92
96
  __created_pipe = False
93
97
  __id = str(uuid4( ))
@@ -1,6 +1,6 @@
1
1
  ########################################################################
2
2
  #
3
- # Copyright (C) 2022
3
+ # Copyright (C) 2022,2025
4
4
  # Associated Universities, Inc. Washington DC, USA.
5
5
  #
6
6
  # This script is free software; you can redistribute it and/or modify it
@@ -28,7 +28,8 @@
28
28
  '''Bokeh state management functions (both within the Bokeh distribution
29
29
  and with the Bokeh extensions found in ``cubevis.bokeh``.'''
30
30
 
31
- from ._initialize import initialize_bokeh
31
+ from ._initialize import order_bokeh_js, register_model, set_cubevis_lib, get_js_loading, set_js_loading, get_js_loading_selection, JsLoading
32
32
  from ._session import setup_session as initialize_session
33
33
  from ._palette import available_palettes, find_palette, default_palette
34
- from ._javascript import casalib_url, cubevisjs_url
34
+ from ._javascript import casalib_path, casalib_url, cubevisjs_path, cubevisjs_url
35
+ from ._current import CurrentBokehState as current_bokeh_state
@@ -0,0 +1,34 @@
1
+ from enum import Enum
2
+ from typing import FrozenSet
3
+ from bokeh import __version__ as _bokeh_version
4
+ from bokeh.io.state import curstate
5
+
6
+ class BokehMode(Enum):
7
+ NOTEBOOK = "jupyter_notebook"
8
+ FILE = "file_output"
9
+ BROWSER = "browser_display"
10
+ SERVER = "bokeh_server"
11
+
12
+ class CurrentBokehState:
13
+
14
+ @staticmethod
15
+ def version( ) -> str:
16
+ return _bokeh_version
17
+
18
+ @staticmethod
19
+ def mode( ) -> FrozenSet[str]:
20
+ state = curstate( )
21
+
22
+ notebook = getattr(state, '_notebook', False)
23
+ file = getattr(state, '_file', None) is not None
24
+ server = getattr(state, '_server_enabled', False)
25
+ browser = file or not (notebook or server)
26
+
27
+ return frozenset(
28
+ mode for mode, condition in [
29
+ (BokehMode.NOTEBOOK, notebook),
30
+ (BokehMode.FILE, file),
31
+ (BokehMode.SERVER, server),
32
+ (BokehMode.BROWSER, browser),
33
+ ] if condition
34
+ )