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.
- cubevis/__init__.py +12 -23
- cubevis/__js__/bokeh-3.6/cubevisjs.min.js +64 -0
- cubevis/__js__/{cubevisjs.min.js → bokeh-3.7/cubevisjs.min.js} +3 -3
- cubevis/__js__/bokeh-3.8/cubevisjs.min.js +64 -0
- cubevis/__js__/casalib.min.js +1 -1
- cubevis/__version__.py +1 -1
- cubevis/bokeh/__init__.py +16 -0
- cubevis/bokeh/annotations/_ev_poly_annotation.py +2 -1
- cubevis/bokeh/format/_wcs_ticks.py +6 -2
- cubevis/bokeh/models/__init__.py +1 -0
- cubevis/bokeh/models/_showable.py +352 -0
- cubevis/bokeh/models/_tip.py +2 -1
- cubevis/bokeh/models/_tip_button.py +2 -3
- cubevis/bokeh/sources/_data_pipe.py +6 -2
- cubevis/bokeh/sources/_image_data_source.py +6 -2
- cubevis/bokeh/sources/_image_pipe.py +4 -1
- cubevis/bokeh/sources/_spectra_data_source.py +6 -3
- cubevis/bokeh/sources/_updatable_data_source.py +6 -2
- cubevis/bokeh/state/__init__.py +4 -3
- cubevis/bokeh/state/_current.py +34 -0
- cubevis/bokeh/state/_initialize.py +282 -116
- cubevis/bokeh/state/_javascript.py +95 -21
- cubevis/bokeh/tools/_cbreset_tool.py +2 -1
- cubevis/bokeh/tools/_drag_tool.py +2 -1
- cubevis/bokeh/utils/__init__.py +0 -1
- cubevis/exe/_setting.py +1 -0
- cubevis/private/apps/__init__.py +4 -2
- cubevis/private/apps/_interactiveclean.mustache +6 -2
- cubevis/private/apps/_interactiveclean.py +6 -2
- cubevis/private/apps/_interactivecleannotebook.mustache +112 -0
- cubevis/private/apps/_interactivecleannotebook.py +1874 -0
- cubevis/private/casatasks/iclean.py +4 -0
- cubevis/toolbox/_app_context.py +5 -9
- cubevis/toolbox/_cube.py +6 -2
- cubevis/toolbox/_interactive_clean_ui.mustache +20 -31
- cubevis/toolbox/_interactive_clean_ui.py +20 -31
- cubevis/utils/__init__.py +183 -41
- cubevis/utils/_git.py +36 -0
- cubevis/utils/_jupyter.py +12 -0
- {cubevis-1.0.0.dist-info → cubevis-1.0.4.dist-info}/METADATA +3 -3
- {cubevis-1.0.0.dist-info → cubevis-1.0.4.dist-info}/RECORD +43 -39
- cubevis/__js__/bokeh-3.6.1.min.js +0 -728
- cubevis/__js__/bokeh-tables-3.6.1.min.js +0 -119
- cubevis/__js__/bokeh-widgets-3.6.1.min.js +0 -141
- {cubevis-1.0.0.dist-info → cubevis-1.0.4.dist-info}/WHEEL +0 -0
- {cubevis-1.0.0.dist-info → cubevis-1.0.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
########################################################################
|
|
2
2
|
#
|
|
3
|
-
# Copyright (C) 2021,2022,2023
|
|
3
|
+
# Copyright (C) 2021,2022,2023,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
|
|
@@ -30,135 +30,301 @@ generated HTML that is used to display the Bokeh plots that ``casagui``'s
|
|
|
30
30
|
applications produce'''
|
|
31
31
|
import os
|
|
32
32
|
import re
|
|
33
|
+
import logging
|
|
33
34
|
from os import path
|
|
35
|
+
from enum import Enum
|
|
34
36
|
from os.path import dirname, join, basename, abspath
|
|
35
37
|
from urllib.parse import urlparse
|
|
36
38
|
from bokeh import resources
|
|
39
|
+
from bokeh.settings import settings
|
|
37
40
|
from ...utils import path_to_url, static_vars, have_network
|
|
38
41
|
|
|
42
|
+
logger = logging.getLogger(__name__)
|
|
39
43
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
_CUBEVIS_LIBS = {}
|
|
45
|
+
# Selector for which source to prefer for JavaScript files. Bokeh's Python
|
|
46
|
+
# package comes with static versions of the Bokeh JavaScript libraries.
|
|
47
|
+
# cubevis libraries are available both as static versions contained within
|
|
48
|
+
# the Python package and from an NRAO website.
|
|
49
|
+
#
|
|
50
|
+
# None = auto-detect, 'local' = prefer local, 'network' = prefer network
|
|
51
|
+
class JsLoading(Enum):
|
|
52
|
+
AUTO = 'auto'
|
|
53
|
+
LOCAL = 'local'
|
|
54
|
+
NETWORK = 'network'
|
|
55
|
+
|
|
56
|
+
_JS_STRATEGY = JsLoading.AUTO
|
|
57
|
+
|
|
58
|
+
# Package-level registry
|
|
59
|
+
_JUPYTER_STATE = {
|
|
60
|
+
'models_registered': set()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
def get_cubevis_libs( ):
|
|
64
|
+
"""Get the package-level default cubevis paths"""
|
|
65
|
+
return _CUBEVIS_LIBS
|
|
66
|
+
|
|
67
|
+
def set_cubevis_lib(**kw):
|
|
68
|
+
"""Set custom paths for cubevis JavaScript libraries"""
|
|
69
|
+
libs = get_cubevis_libs()
|
|
70
|
+
for lib in ['bokeh', 'bokeh_widgets', 'bokeh_tables', 'casalib', 'cubevisjs']:
|
|
71
|
+
if lib in kw:
|
|
72
|
+
libs[lib] = kw[lib]
|
|
73
|
+
return libs
|
|
74
|
+
|
|
75
|
+
def get_js_loading( ):
|
|
76
|
+
"""Get JavaScript loading strategy.
|
|
77
|
+
"""
|
|
78
|
+
return _JS_STRATEGY
|
|
79
|
+
|
|
80
|
+
def set_js_loading(strategy):
|
|
81
|
+
"""Set JavaScript loading strategy.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
strategy (JsLoading or str):
|
|
85
|
+
- 'auto': Smart selection based on environment
|
|
86
|
+
- 'local': Always prefer local static files when available
|
|
87
|
+
- 'network': Always prefer network CDN when available
|
|
88
|
+
"""
|
|
89
|
+
global _JS_STRATEGY
|
|
90
|
+
if isinstance(strategy, str):
|
|
91
|
+
_JS_STRATEGY = JsLoading(strategy)
|
|
92
|
+
elif isinstance(strategy, JsLoading):
|
|
93
|
+
_JS_STRATEGY = strategy
|
|
94
|
+
else:
|
|
95
|
+
raise TypeError("'strategy' must be a valid JsLoading string or enum member")
|
|
96
|
+
|
|
97
|
+
def get_js_loading_selection( ):
|
|
98
|
+
# Check for user override
|
|
99
|
+
global _JS_STRATEGY
|
|
100
|
+
|
|
101
|
+
prefer_network = False
|
|
102
|
+
prefer_local = False
|
|
103
|
+
|
|
104
|
+
in_jupyter = is_jupyter()
|
|
105
|
+
use_network = have_network()
|
|
106
|
+
|
|
107
|
+
if _JS_STRATEGY is JsLoading.LOCAL:
|
|
108
|
+
prefer_network = False
|
|
109
|
+
prefer_local = True
|
|
110
|
+
logger.debug("Strategy: User override -> prefer local")
|
|
111
|
+
elif _JS_STRATEGY is JsLoading.NETWORK:
|
|
112
|
+
prefer_network = True
|
|
113
|
+
prefer_local = False
|
|
114
|
+
logger.debug("Strategy: User override -> prefer network")
|
|
115
|
+
else:
|
|
116
|
+
# Auto-detect optimal strategy
|
|
117
|
+
if in_jupyter and use_network:
|
|
118
|
+
# Jupyter notebooks work best with CDN versions
|
|
119
|
+
prefer_network = True
|
|
120
|
+
prefer_local = False
|
|
121
|
+
logger.debug("Strategy: Jupyter + network -> prefer CDN")
|
|
122
|
+
elif not use_network:
|
|
123
|
+
# No choice - must use local
|
|
124
|
+
prefer_network = False
|
|
125
|
+
prefer_local = True
|
|
126
|
+
logger.debug("Strategy: No network -> local only")
|
|
127
|
+
else:
|
|
128
|
+
# Standalone application with network -> prefer local for speed/reliability
|
|
129
|
+
prefer_network = False
|
|
130
|
+
prefer_local = True
|
|
131
|
+
logger.debug("Strategy: Standalone + network -> prefer local static")
|
|
132
|
+
|
|
133
|
+
return prefer_local, prefer_network
|
|
134
|
+
|
|
135
|
+
def is_jupyter():
|
|
136
|
+
"""Check if running in Jupyter environment"""
|
|
137
|
+
try:
|
|
138
|
+
from IPython import get_ipython
|
|
139
|
+
return get_ipython() is not None
|
|
140
|
+
except ImportError:
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
def resolve_js_library_paths():
|
|
144
|
+
"""Resolve paths to all required JavaScript libraries based on environment and availability.
|
|
145
|
+
|
|
146
|
+
Strategy:
|
|
147
|
+
- Jupyter notebooks: Prefer network CDN (required for proper functionality)
|
|
148
|
+
- Standalone apps with network: Prefer local static (faster, more reliable)
|
|
149
|
+
- No network: Use local static (only option)
|
|
150
|
+
- Corporate/restricted networks: Prefer local static (firewall bypass)
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
dict: Dictionary with resolved paths for 'casalib', 'bokeh', 'bokeh_widgets',
|
|
154
|
+
'bokeh_tables', and 'cubevisjs'
|
|
155
|
+
"""
|
|
156
|
+
### These functions also select remote/local based on Jupyter or
|
|
157
|
+
### no network because these URLs also are included in the
|
|
158
|
+
### __javascript__ variables for Bokeh model derived member variables.
|
|
159
|
+
from . import casalib_url as get_casalib_url
|
|
160
|
+
from . import cubevisjs_url as get_cubevisjs_url
|
|
161
|
+
|
|
162
|
+
# Start with user-defined overrides
|
|
163
|
+
result = get_cubevis_libs().copy()
|
|
164
|
+
|
|
165
|
+
# Handle {JSLIB}/ substitution for user paths
|
|
166
|
+
for key, value in result.items():
|
|
167
|
+
if value and value.startswith("{JSLIB}/"):
|
|
168
|
+
result[key] = path_to_url(value[8:])
|
|
169
|
+
|
|
170
|
+
prefer_local, prefer_network = get_js_loading_selection( )
|
|
171
|
+
|
|
172
|
+
# Get available sources
|
|
173
|
+
bokeh_static_js = {}
|
|
174
|
+
bokeh_static_available = False
|
|
175
|
+
|
|
176
|
+
try:
|
|
177
|
+
bokeh_static_path = settings.bokehjs_path()
|
|
178
|
+
js_dir = join(bokeh_static_path, 'js')
|
|
179
|
+
logger.debug( f"Bokeh static pat: {js_dir}" )
|
|
180
|
+
|
|
181
|
+
if path.exists(js_dir):
|
|
182
|
+
for filename in os.listdir(js_dir):
|
|
183
|
+
if filename.endswith('.js'):
|
|
184
|
+
file_path = f"file://{abspath(join(js_dir, filename))}"
|
|
185
|
+
|
|
186
|
+
## js_dir seems to contain both minified and non minified versions of
|
|
187
|
+
## the bokeh librarys. If we ever want to allow for non-minified
|
|
188
|
+
## versions, e.g. for debugging, we'll need to look here
|
|
189
|
+
if re.match(r'.*bokeh(?:-\d+\.\d+\.\d+)?\.min\.js$', filename):
|
|
190
|
+
bokeh_static_js['bokeh'] = file_path
|
|
191
|
+
elif re.match(r'.*bokeh-widgets(?:-\d+\.\d+\.\d+)?\.min\.js$', filename):
|
|
192
|
+
bokeh_static_js['bokeh_widgets'] = file_path
|
|
193
|
+
elif re.match(r'.*bokeh-tables(?:-\d+\.\d+\.\d+)?\.min\.js$', filename):
|
|
194
|
+
bokeh_static_js['bokeh_tables'] = file_path
|
|
195
|
+
|
|
196
|
+
bokeh_static_available = len(bokeh_static_js) > 0
|
|
197
|
+
|
|
198
|
+
except Exception as e:
|
|
199
|
+
logger.warning(f"Could not access Bokeh static files: {e}")
|
|
200
|
+
|
|
201
|
+
# Network connection available
|
|
202
|
+
use_network = have_network()
|
|
203
|
+
|
|
204
|
+
# Fill in missing Bokeh libraries using the selected strategy
|
|
205
|
+
for lib in ['bokeh', 'bokeh_widgets', 'bokeh_tables']:
|
|
206
|
+
if lib not in result or result[lib] is None:
|
|
207
|
+
if prefer_local and bokeh_static_available and lib in bokeh_static_js:
|
|
208
|
+
result[lib] = bokeh_static_js[lib]
|
|
209
|
+
logger.debug(f"Using local static for {lib}")
|
|
210
|
+
elif prefer_network and use_network:
|
|
211
|
+
# Let Bokeh handle CDN URLs - return None to use defaults
|
|
212
|
+
result[lib] = None
|
|
213
|
+
logger.debug(f"Using CDN for {lib}")
|
|
214
|
+
elif bokeh_static_available and lib in bokeh_static_js:
|
|
215
|
+
# Fallback to local if network preference fails
|
|
216
|
+
result[lib] = bokeh_static_js[lib]
|
|
217
|
+
logger.debug(f"Fallback to local static for {lib}")
|
|
218
|
+
else:
|
|
219
|
+
# Last resort - try local cache
|
|
220
|
+
try:
|
|
221
|
+
cached_path = path_to_url(f"{lib}.min.js")
|
|
222
|
+
if cached_path != f"{lib}.min.js": # Found in cache
|
|
223
|
+
result[lib] = cached_path
|
|
224
|
+
logger.debug(f"Using local cache for {lib}")
|
|
225
|
+
else:
|
|
226
|
+
result[lib] = None
|
|
227
|
+
logger.debug(f"No source found for {lib}")
|
|
228
|
+
except:
|
|
229
|
+
result[lib] = None
|
|
230
|
+
|
|
231
|
+
# Fill in cubevis-specific libraries
|
|
232
|
+
if 'casalib' not in result or result['casalib'] is None:
|
|
233
|
+
result['casalib'] = get_casalib_url( )
|
|
234
|
+
|
|
235
|
+
if 'cubevisjs' not in result or result['cubevisjs'] is None:
|
|
236
|
+
result['cubevisjs'] = get_cubevisjs_url( )
|
|
237
|
+
|
|
238
|
+
logger.debug(f"Resolved JS library paths: {result}")
|
|
239
|
+
logger.debug(f"Environment: jupyter={is_jupyter( )}, network={use_network}, strategy={'network' if prefer_network else 'local'}")
|
|
240
|
+
|
|
241
|
+
return result
|
|
242
|
+
|
|
243
|
+
@static_vars(initialized=False)
|
|
244
|
+
def order_bokeh_js():
|
|
43
245
|
"""Initialize `bokeh` for use with the ``cubevisjs`` extensions.
|
|
44
246
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
can be copied into the ``__js__`` directory to replace the existing
|
|
52
|
-
libraries. To debug within the bokehjs library, a URL or a path
|
|
53
|
-
can be supplied by the ``bokehjs_subst`` parameter and it will be
|
|
54
|
-
substituted for the standard Bokeh library.
|
|
55
|
-
|
|
56
|
-
Parameters
|
|
57
|
-
----------
|
|
58
|
-
bokehjs_subst: None or str or list of str
|
|
59
|
-
Bokeh dependent javascript library which is loaded after all
|
|
60
|
-
other Bokeh libraries have been loaded. This path could be a
|
|
61
|
-
local path, a URL or None. None is the default in which case
|
|
62
|
-
it loads the published library for the current version of
|
|
63
|
-
``cubevisjs`` and ``bokeh``
|
|
64
|
-
|
|
65
|
-
Example
|
|
66
|
-
-------
|
|
67
|
-
from cubevis.bokeh.state import initialize_bokeh
|
|
68
|
-
initialize_bokeh( bokehjs_subst="/tmp/bokeh-3.2.2.js" )
|
|
247
|
+
This function injects the cubevis JavaScript libraries into Bokeh's resource
|
|
248
|
+
loading system in the proper order: casalib → bokeh core → bokeh widgets →
|
|
249
|
+
bokeh tables → cubevisjs.
|
|
250
|
+
|
|
251
|
+
The function automatically determines the best source for JavaScript libraries
|
|
252
|
+
based on the current environment (Jupyter vs standalone, network availability, etc.).
|
|
69
253
|
"""
|
|
70
254
|
|
|
71
|
-
if
|
|
72
|
-
### only initialize once...
|
|
255
|
+
if order_bokeh_js.initialized:
|
|
73
256
|
return
|
|
74
257
|
|
|
75
|
-
|
|
258
|
+
# Get all resolved library paths
|
|
259
|
+
js_paths = resolve_js_library_paths()
|
|
260
|
+
|
|
261
|
+
logger.debug(f"order_bokeh_js() initializing with paths: {js_paths}")
|
|
262
|
+
order_bokeh_js.initialized = True
|
|
263
|
+
|
|
264
|
+
# Store original Bokeh js_files function
|
|
76
265
|
resources.Resources._old_js_files = resources.Resources.js_files
|
|
77
|
-
def js_files( self ):
|
|
78
|
-
########################################################################
|
|
79
|
-
### Function that will replace the member function within Bokeh that ###
|
|
80
|
-
### returns the URLs for Bokeh initialization... ###
|
|
81
|
-
########################################################################
|
|
82
|
-
def expand_paths( replacement ):
|
|
83
|
-
### turn all paths into URLs
|
|
84
|
-
### turn a single string into a list with one string
|
|
85
|
-
if replacement is None:
|
|
86
|
-
return [ ]
|
|
87
|
-
if type(replacement) == str:
|
|
88
|
-
if path.isfile(replacement):
|
|
89
|
-
return [ f'''file://{abspath(replacement)}''' ]
|
|
90
|
-
elif replacement.startswith('http'):
|
|
91
|
-
return [ replacement ]
|
|
92
|
-
else:
|
|
93
|
-
raise RuntimeError( f'''debugging bokehjs substitution ('{replacement}') does not exist''' )
|
|
94
|
-
if type(replacement) == list:
|
|
95
|
-
result = [ ]
|
|
96
|
-
for u in replacement:
|
|
97
|
-
if path.isfile(u):
|
|
98
|
-
result.append( f'''file://{abspath(u)}''' )
|
|
99
|
-
elif u.startswith('http'):
|
|
100
|
-
result.append( u )
|
|
101
|
-
else:
|
|
102
|
-
raise RuntimeError( f'''debugging bokehjs substitution ('{u}') does not exist''' )
|
|
103
|
-
return result
|
|
104
|
-
return [ ]
|
|
105
|
-
def cubevisjs_predicate( s ):
|
|
106
|
-
### detect cubevisjs library URL
|
|
107
|
-
return basename(s).startswith('cubevisjs')
|
|
108
|
-
def replace_bokehjs( urls, replacement ):
|
|
109
|
-
### substitute replacement list for the bokehjs library URL
|
|
110
|
-
result = [ ]
|
|
111
|
-
for url in urls:
|
|
112
|
-
if re.match( r'.*/bokeh-\d+\.\d+\.\d+(?:\.min)?\.js$', url ):
|
|
113
|
-
result += replacement
|
|
114
|
-
else:
|
|
115
|
-
result.append(url)
|
|
116
|
-
return result
|
|
117
|
-
|
|
118
|
-
def include_all_bokehjs( urls ):
|
|
119
|
-
result = urls.copy( )
|
|
120
|
-
bokeh_path=None
|
|
121
|
-
has_bokeh_widgets=False
|
|
122
|
-
has_bokeh_tables=False
|
|
123
|
-
for url in urls:
|
|
124
|
-
if re.match( r'.*/bokeh-\d+\.\d+\.\d+(?:\.min)?\.js$', url ):
|
|
125
|
-
bokeh_path = url
|
|
126
|
-
if re.match( r'.*/bokeh-widgets-\d+\.\d+\.\d+(?:\.min)?\.js$', url ):
|
|
127
|
-
has_bokeh_widgets = True
|
|
128
|
-
if re.match( r'.*/bokeh-tables-\d+\.\d+\.\d+(?:\.min)?\.js$', url ):
|
|
129
|
-
has_bokeh_tables = True
|
|
130
|
-
|
|
131
|
-
if bokeh_path:
|
|
132
|
-
if not has_bokeh_widgets:
|
|
133
|
-
result.append( bokeh_path.replace('/bokeh-','/bokeh-widgets-') )
|
|
134
|
-
if not has_bokeh_tables:
|
|
135
|
-
result.append( bokeh_path.replace('/bokeh-','/bokeh-tables-') )
|
|
136
|
-
|
|
137
|
-
return result
|
|
138
|
-
|
|
139
|
-
user_bokehjs_replacement = expand_paths(bokehjs_subst)
|
|
140
|
-
sys_urls = include_all_bokehjs( resources.Resources._old_js_files.fget(self) )
|
|
141
|
-
if initialize_bokeh.do_local_subst:
|
|
142
|
-
local_urls = [ ]
|
|
143
|
-
for url in sys_urls:
|
|
144
|
-
pieces = urlparse(url)
|
|
145
|
-
if pieces.scheme != 'file':
|
|
146
|
-
lib = basename(pieces.path)
|
|
147
|
-
url = path_to_url(lib)
|
|
148
|
-
if lib != url:
|
|
149
|
-
local_urls.append(url)
|
|
150
|
-
else:
|
|
151
|
-
raise RuntimeError( '''Cannot confirm internet access and could not find local cached versions of Bokeh's libraries''' )
|
|
152
|
-
else:
|
|
153
|
-
local_urls.append(url)
|
|
154
|
-
sys_urls = local_urls
|
|
155
|
-
|
|
156
|
-
if len(user_bokehjs_replacement) == 0:
|
|
157
|
-
### move cubevisjs library after Bokeh libraries
|
|
158
|
-
return [x for x in sys_urls if not cubevisjs_predicate(x)] + [x for x in sys_urls if cubevisjs_predicate(x)]
|
|
159
|
-
else:
|
|
160
|
-
### replace bokehjs library with bokehjs_subst and move cubevisjs library after Bokeh libraries
|
|
161
|
-
return replace_bokehjs( [x for x in sys_urls if not cubevisjs_predicate(x)], user_bokehjs_replacement ) + [x for x in sys_urls if cubevisjs_predicate(x)]
|
|
162
266
|
|
|
267
|
+
def js_files(self):
|
|
268
|
+
"""Replacement function that returns JavaScript files in the correct order"""
|
|
269
|
+
|
|
270
|
+
# Get original Bokeh URLs as fallback
|
|
271
|
+
original_urls = resources.Resources._old_js_files.fget(self)
|
|
272
|
+
logger.debug( f"Original Bokeh JavaScript: {original_urls}" )
|
|
273
|
+
|
|
274
|
+
def get_url_or_fallback(lib_key, fallback_pattern=None):
|
|
275
|
+
"""Get resolved URL or find fallback from original Bokeh URLs"""
|
|
276
|
+
resolved_path = js_paths.get(lib_key)
|
|
277
|
+
|
|
278
|
+
if resolved_path:
|
|
279
|
+
return resolved_path
|
|
280
|
+
|
|
281
|
+
# Look for fallback in original URLs
|
|
282
|
+
if fallback_pattern:
|
|
283
|
+
for url in original_urls:
|
|
284
|
+
if re.match(fallback_pattern, url):
|
|
285
|
+
return url
|
|
286
|
+
|
|
287
|
+
return None
|
|
288
|
+
|
|
289
|
+
# Build the ordered list
|
|
290
|
+
ordered_js = []
|
|
291
|
+
|
|
292
|
+
# 1. casalib (always first)
|
|
293
|
+
casalib_url = get_url_or_fallback('casalib')
|
|
294
|
+
if casalib_url:
|
|
295
|
+
ordered_js.append(casalib_url)
|
|
296
|
+
|
|
297
|
+
# 2. bokeh core
|
|
298
|
+
bokeh_url = get_url_or_fallback('bokeh', r'.*/bokeh-\d+\.\d+\.\d+(?:\.min)?\.js$')
|
|
299
|
+
if bokeh_url:
|
|
300
|
+
ordered_js.append(bokeh_url)
|
|
301
|
+
|
|
302
|
+
# 3. bokeh widgets
|
|
303
|
+
widgets_url = get_url_or_fallback('bokeh_widgets', r'.*/bokeh-widgets-\d+\.\d+\.\d+(?:\.min)?\.js$')
|
|
304
|
+
if widgets_url:
|
|
305
|
+
ordered_js.append(widgets_url)
|
|
306
|
+
|
|
307
|
+
# 4. bokeh tables
|
|
308
|
+
tables_url = get_url_or_fallback('bokeh_tables', r'.*/bokeh-tables-\d+\.\d+\.\d+(?:\.min)?\.js$')
|
|
309
|
+
if tables_url:
|
|
310
|
+
ordered_js.append(tables_url)
|
|
311
|
+
|
|
312
|
+
# 5. cubevisjs (always last)
|
|
313
|
+
cubevisjs_url = get_url_or_fallback('cubevisjs')
|
|
314
|
+
if cubevisjs_url:
|
|
315
|
+
ordered_js.append(cubevisjs_url)
|
|
316
|
+
|
|
317
|
+
logger.debug(f"Final JS load order: {ordered_js}")
|
|
318
|
+
return ordered_js
|
|
319
|
+
|
|
320
|
+
# Replace Bokeh's js_files property
|
|
163
321
|
resources.Resources.js_files = property(js_files)
|
|
164
322
|
return
|
|
323
|
+
|
|
324
|
+
def get_jupyter_state( ):
|
|
325
|
+
"""Get the package-level Jupyter state"""
|
|
326
|
+
return _JUPYTER_STATE
|
|
327
|
+
|
|
328
|
+
def register_model(model_class):
|
|
329
|
+
"""Register a model class that needs dependencies"""
|
|
330
|
+
_JUPYTER_STATE['models_registered'].add(model_class.__name__)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
########################################################################
|
|
2
2
|
#
|
|
3
|
-
# Copyright (C) 2023
|
|
3
|
+
# Copyright (C) 2023,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,26 +28,100 @@
|
|
|
28
28
|
'''This contains functions which return the URLs to the ``cubevis``
|
|
29
29
|
JavaScript libraries. The ``casalib`` library has Bokeh independent
|
|
30
30
|
functions while the `cubevisjs` library has the Bokeh extensions'''
|
|
31
|
-
from
|
|
32
|
-
from
|
|
33
|
-
from
|
|
31
|
+
from os import path, environ
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
from packaging import version
|
|
34
|
+
from bokeh import __version__ as bokeh_version
|
|
35
|
+
from . import get_js_loading_selection
|
|
36
|
+
from ...utils import max_git_version as _max_git_version
|
|
34
37
|
|
|
35
|
-
|
|
38
|
+
_local_library_path = None
|
|
39
|
+
_bokeh_major_minor = None
|
|
40
|
+
|
|
41
|
+
_CUBEVIS_RELEASE_VERSION = None
|
|
42
|
+
_CUBEVIS_GITHUB_VERSION = None
|
|
43
|
+
_BOKEH_MAJOR_MINOR = None
|
|
44
|
+
_CUBEVIS_JS_TAG = None
|
|
45
|
+
|
|
46
|
+
def bokeh_major_minor( ):
|
|
47
|
+
global _BOKEH_MAJOR_MINOR
|
|
48
|
+
if _BOKEH_MAJOR_MINOR is None:
|
|
49
|
+
from bokeh import __version__ as bokeh_version
|
|
50
|
+
v = version.parse(bokeh_version)
|
|
51
|
+
_BOKEH_MAJOR_MINOR = f"{v.major}.{v.minor}"
|
|
52
|
+
return _BOKEH_MAJOR_MINOR
|
|
53
|
+
|
|
54
|
+
def github_js_tag( ):
|
|
55
|
+
global _CUBEVIS_JS_TAG
|
|
56
|
+
if _CUBEVIS_JS_TAG is None:
|
|
57
|
+
release = cubevis_release_version( )
|
|
58
|
+
if release is not None:
|
|
59
|
+
_CUBEVIS_JS_TAG = f"v{release}"
|
|
60
|
+
else:
|
|
61
|
+
if 'CUBEVIS_JS_TAG' in environ:
|
|
62
|
+
_CUBEVIS_JS_TAG = environ['CUBEVIS_JS_TAG']
|
|
63
|
+
else:
|
|
64
|
+
_CUBEVIS_JS_TAG = f"v{cubevis_github_version( )}"
|
|
65
|
+
return _CUBEVIS_JS_TAG
|
|
66
|
+
|
|
67
|
+
def cubevis_github_version( ):
|
|
68
|
+
global _CUBEVIS_GITHUB_VERSION
|
|
69
|
+
if _CUBEVIS_GITHUB_VERSION is None:
|
|
70
|
+
_CUBEVIS_GITHUB_VERSION = _max_git_version( )
|
|
71
|
+
return _CUBEVIS_GITHUB_VERSION
|
|
72
|
+
|
|
73
|
+
def cubevis_release_version( ):
|
|
74
|
+
global _CUBEVIS_RELEASE_VERSION
|
|
75
|
+
if _CUBEVIS_RELEASE_VERSION is None:
|
|
76
|
+
try:
|
|
77
|
+
###
|
|
78
|
+
### __version__.py is generated as part of the build, but if the source tree
|
|
79
|
+
### for cubevis is used directly for development, no __version__.py will be
|
|
80
|
+
### available...
|
|
81
|
+
###
|
|
82
|
+
from ...__version__ import __version__ as package_version
|
|
83
|
+
_CUBEVIS_RELEASE_VERSION = package_version
|
|
84
|
+
except ModuleNotFoundError: pass
|
|
85
|
+
return _CUBEVIS_RELEASE_VERSION
|
|
86
|
+
|
|
87
|
+
def casalib_path( ):
|
|
88
|
+
global _local_library_path
|
|
89
|
+
if _local_library_path is None:
|
|
90
|
+
_local_library_path = path.join( path.dirname(path.dirname(path.dirname(__file__))), '__js__' )
|
|
91
|
+
casalib_path = path.join( _local_library_path, 'casalib.min.js' )
|
|
92
|
+
if not path.isfile( casalib_path ):
|
|
93
|
+
raise RuntimeError( f''''casalib' JavaScript library not found at '{casalib_path}\'''' )
|
|
94
|
+
return casalib_path
|
|
95
|
+
|
|
96
|
+
def cubevisjs_path( ):
|
|
97
|
+
global _local_library_path
|
|
98
|
+
global _bokeh_major_minor
|
|
99
|
+
if _local_library_path is None:
|
|
100
|
+
_local_library_path = path.join( path.dirname(path.dirname(path.dirname(__file__))), '__js__' )
|
|
101
|
+
if _bokeh_major_minor is None:
|
|
102
|
+
version = bokeh_version.split('.')
|
|
103
|
+
_bokeh_major_minor = f"{version[0]}.{version[1]}"
|
|
104
|
+
cubevisjs_path = path.join( _local_library_path, f"bokeh-{_bokeh_major_minor}", 'cubevisjs.min.js' )
|
|
105
|
+
if not path.isfile(cubevisjs_path):
|
|
106
|
+
raise RuntimeError( f''''cubevisjs' JavaScript library not found at '{cubevisjs_path}\'''' )
|
|
107
|
+
return cubevisjs_path
|
|
108
|
+
|
|
109
|
+
### These functions MUST also select remote/local based on Jupyter or
|
|
110
|
+
### no network because these URLs also are included in the
|
|
111
|
+
### __javascript__ variables for Bokeh model derived member variables.
|
|
112
|
+
### These paths are then included in the Bokeh list of JavaScript
|
|
113
|
+
### libraries loaded when the GUI is loaded. However, the ordering is
|
|
114
|
+
### not correct because cubevisjs.min.js must appear AFTER all of the
|
|
115
|
+
### Bokeh files.
|
|
36
116
|
def casalib_url( ):
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
initialize_bokeh( )
|
|
43
|
-
return f'''file://{casalib_url.library_path}'''
|
|
44
|
-
|
|
45
|
-
@static_vars( library_path=None )
|
|
117
|
+
prefer_local, prefer_network = get_js_loading_selection( )
|
|
118
|
+
if prefer_network:
|
|
119
|
+
return f"https://cdn.jsdelivr.net/gh/casangi/cubevis@{github_js_tag( )}/cubevis/__js__/casalib.min.js"
|
|
120
|
+
else:
|
|
121
|
+
return f"file://{casalib_path( )}"
|
|
46
122
|
def cubevisjs_url( ):
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
initialize_bokeh( )
|
|
53
|
-
return f'''file://{cubevisjs_url.library_path}'''
|
|
123
|
+
prefer_local, prefer_network = get_js_loading_selection( )
|
|
124
|
+
if prefer_network:
|
|
125
|
+
return f"https://cdn.jsdelivr.net/gh/casangi/cubevis@{github_js_tag( )}/cubevis/__js__/bokeh-{bokeh_major_minor( )}/cubevisjs.min.js"
|
|
126
|
+
else:
|
|
127
|
+
return f"file://{cubevisjs_path( )}"
|
|
@@ -32,8 +32,9 @@
|
|
|
32
32
|
from bokeh.models import ResetTool, Tool
|
|
33
33
|
from bokeh.core.properties import Instance, Nullable
|
|
34
34
|
from bokeh.models.callbacks import Callback
|
|
35
|
+
from .. import BokehInit
|
|
35
36
|
|
|
36
|
-
class CBResetTool(ResetTool):
|
|
37
|
+
class CBResetTool(ResetTool,BokehInit):
|
|
37
38
|
'''Tool that emits press and pressup events to signal start and end of drag'''
|
|
38
39
|
|
|
39
40
|
precallback = Nullable(Instance(Callback), help="""
|
|
@@ -36,8 +36,9 @@ from bokeh.core.properties import Instance, Nullable
|
|
|
36
36
|
from bokeh.models.callbacks import Callback
|
|
37
37
|
from os.path import join,dirname
|
|
38
38
|
from pathlib import Path
|
|
39
|
+
from .. import BokehInit
|
|
39
40
|
|
|
40
|
-
class DragTool(Drag):
|
|
41
|
+
class DragTool(Drag,BokehInit):
|
|
41
42
|
'''Tool that emits press and pressup events to signal start and end of drag'''
|
|
42
43
|
|
|
43
44
|
start = Nullable(Instance(Callback), help="""
|
cubevis/bokeh/utils/__init__.py
CHANGED
cubevis/exe/_setting.py
CHANGED
cubevis/private/apps/__init__.py
CHANGED
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
'''End user applications supplied by ``cubevis``.'''
|
|
30
30
|
import sys
|
|
31
31
|
from cubevis.utils import copydoc, ImportProtectedModule
|
|
32
|
-
from cubevis.bokeh.state import initialize_session
|
|
33
|
-
initialize_session()
|
|
32
|
+
#from cubevis.bokeh.state import initialize_session
|
|
33
|
+
#initialize_session()
|
|
34
34
|
|
|
35
35
|
###
|
|
36
36
|
### This import roundabout was introduced when astroviper and casa6 apps were in the same
|
|
@@ -42,7 +42,9 @@ sys.modules[__name__].__class__ = ImportProtectedModule( __name__, { 'plotants':
|
|
|
42
42
|
'CreateMask': '._createmask',
|
|
43
43
|
'CreateRegion': '._createregion',
|
|
44
44
|
'InteractiveClean': '._interactiveclean',
|
|
45
|
+
'InteractiveCleanNotebook': '._interactivecleannotebook',
|
|
45
46
|
'iclean': '..casatasks.iclean',
|
|
47
|
+
'iclean_notebook': '..casatasks.iclean_notebook',
|
|
46
48
|
'createmask': '..casatasks.createmask',
|
|
47
49
|
'createregion': '..casatasks.createregion',
|
|
48
50
|
} )
|
|
@@ -32,7 +32,9 @@ of tclean'''
|
|
|
32
32
|
from pprint import pprint
|
|
33
33
|
|
|
34
34
|
import sys
|
|
35
|
+
from uuid import uuid4
|
|
35
36
|
from os.path import exists
|
|
37
|
+
from bokeh.plotting import show
|
|
36
38
|
from casatasks.private.imagerhelpers.input_parameters import ImagerParameters
|
|
37
39
|
|
|
38
40
|
from cubevis.utils import find_pkg, load_pkg
|
|
@@ -84,6 +86,8 @@ class InteractiveClean:
|
|
|
84
86
|
cell='12.0arcsec', specmode='cube',
|
|
85
87
|
interpolation='nearest', ... )( ) )
|
|
86
88
|
'''
|
|
89
|
+
self._id = uuid4( )
|
|
87
90
|
context = exe.Context( exe.Mode.SYNC )
|
|
88
|
-
exec_task = self._ui(
|
|
89
|
-
|
|
91
|
+
bokeh_ui, exec_task = self._ui( context, self._id )
|
|
92
|
+
show(bokeh_ui)
|
|
93
|
+
return context.execute( exec_task, self._id )
|
|
@@ -31,7 +31,9 @@ of tclean'''
|
|
|
31
31
|
from pprint import pprint
|
|
32
32
|
|
|
33
33
|
import sys
|
|
34
|
+
from uuid import uuid4
|
|
34
35
|
from os.path import exists
|
|
36
|
+
from bokeh.plotting import show
|
|
35
37
|
from casatasks.private.imagerhelpers.input_parameters import ImagerParameters
|
|
36
38
|
|
|
37
39
|
from cubevis.utils import find_pkg, load_pkg
|
|
@@ -1846,6 +1848,8 @@ class InteractiveClean:
|
|
|
1846
1848
|
cell='12.0arcsec', specmode='cube',
|
|
1847
1849
|
interpolation='nearest', ... )( ) )
|
|
1848
1850
|
'''
|
|
1851
|
+
self._id = uuid4( )
|
|
1849
1852
|
context = exe.Context( exe.Mode.SYNC )
|
|
1850
|
-
exec_task = self._ui(
|
|
1851
|
-
|
|
1853
|
+
bokeh_ui, exec_task = self._ui( context, self._id )
|
|
1854
|
+
show(bokeh_ui)
|
|
1855
|
+
return context.execute( exec_task, self._id )
|