vitessce 3.5.4__py3-none-any.whl → 3.5.5__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.
vitessce/config.py CHANGED
@@ -478,7 +478,7 @@ class VitessceConfigView:
478
478
  A class to represent a view (i.e. visualization component) in the Vitessce view config layout.
479
479
  """
480
480
 
481
- def __init__(self, component, coordination_scopes, x, y, w, h):
481
+ def __init__(self, component, coordination_scopes, x, y, w, h, uid=None):
482
482
  """
483
483
  Not meant to be instantiated directly, but instead created and returned by the ``VitessceConfig.add_view()`` method.
484
484
 
@@ -498,6 +498,8 @@ class VitessceConfigView:
498
498
  "w": w,
499
499
  "h": h
500
500
  }
501
+ if uid is not None:
502
+ self.view["uid"] = uid
501
503
 
502
504
  def _to_py_params(self):
503
505
  params_dict = {
@@ -505,7 +507,7 @@ class VitessceConfigView:
505
507
  "x": self.view["x"],
506
508
  "y": self.view["y"],
507
509
  "w": self.view["w"],
508
- "h": self.view["h"]
510
+ "h": self.view["h"],
509
511
  }
510
512
  # Only include coordination_scopes if there are coordination scopes other than
511
513
  # the coorindation scope for the 'dataset' coordination type.
@@ -516,6 +518,8 @@ class VitessceConfigView:
516
518
  }
517
519
  if len(non_dataset_coordination_scopes) > 0:
518
520
  params_dict["coordination_scopes"] = non_dataset_coordination_scopes
521
+ if "uid" in self.view:
522
+ params_dict["uid"] = self.view["uid"]
519
523
  return params_dict
520
524
 
521
525
  def get_coordination_scope(self, c_type):
@@ -1018,7 +1022,7 @@ class VitessceConfig:
1018
1022
  """
1019
1023
  return self.config["datasets"]
1020
1024
 
1021
- def add_view(self, view_type, dataset=None, dataset_uid=None, x=0, y=0, w=1, h=1, mapping=None, coordination_scopes=None, props=None):
1025
+ def add_view(self, view_type, dataset=None, dataset_uid=None, x=0, y=0, w=1, h=1, mapping=None, coordination_scopes=None, props=None, uid=None):
1022
1026
  """
1023
1027
  Add a view to the config.
1024
1028
 
@@ -1086,7 +1090,7 @@ class VitessceConfig:
1086
1090
  if coordination_scopes is not None:
1087
1091
  internal_coordination_scopes.update(coordination_scopes)
1088
1092
  vcv = VitessceConfigView(
1089
- component_str, internal_coordination_scopes, x, y, w, h)
1093
+ component_str, internal_coordination_scopes, x, y, w, h, uid=uid)
1090
1094
 
1091
1095
  # Use the mapping parameter if component is scatterplot and the mapping is not None
1092
1096
  if mapping is not None:
@@ -1743,7 +1747,9 @@ class VitessceConfig:
1743
1747
  raise ValueError(
1744
1748
  "Multiple datasets are present, so every view must have an explicit dataset coordination scope.")
1745
1749
  new_view = VitessceConfigView(
1746
- c['component'], c_coord_scopes, c['x'], c['y'], c['w'], c['h'])
1750
+ c['component'], c_coord_scopes, c['x'], c['y'], c['w'], c['h'],
1751
+ uid=(c["uid"] if "uid" in c else None)
1752
+ )
1747
1753
  if 'props' in c.keys():
1748
1754
  new_view.set_props(**c['props'])
1749
1755
  vc.config['layout'].append(new_view)
vitessce/widget.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import importlib.util
2
2
  from urllib.parse import quote_plus
3
3
  import json
4
+ import sys
4
5
 
5
6
  # Widget dependencies
6
7
  import anywidget
@@ -104,7 +105,8 @@ def get_base_url_and_port(port, next_port, proxy=False, base_url=None, host_name
104
105
 
105
106
  if base_url is None:
106
107
  if proxy:
107
- if importlib.util.find_spec('jupyter_server_proxy') is None:
108
+ is_in_workspaces = sys.executable.startswith('/hive')
109
+ if importlib.util.find_spec('jupyter_server_proxy') is None and not is_in_workspaces:
108
110
  raise ValueError(
109
111
  "To use the widget through a proxy, jupyter-server-proxy must be installed.")
110
112
  if host_name is None:
@@ -161,37 +163,52 @@ const React = await importWithMap("react", importMap);
161
163
  const { createRoot } = await importWithMap("react-dom/client", importMap);
162
164
 
163
165
  const e = React.createElement;
164
-
166
+ const WORKSPACES_URL_KEYWORD = 'https://workspaces-pt'
165
167
  const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
166
-
167
168
  // The jupyter server may be running through a proxy,
168
169
  // which means that the client needs to prepend the part of the URL before /proxy/8000 such as
169
170
  // https://hub.gke2.mybinder.org/user/vitessce-vitessce-python-swi31vcv/proxy/8000/A/0/cells
171
+ // For workspaces: https://workspaces-pt.hubmapconsortium.org/passthrough/HOSTNAME/PORT/ADDITIONAL_PATH_INFO?QUERY_PARAMS=HELLO_WORLD
170
172
  function prependBaseUrl(config, proxy, hasHostName) {
171
- if(!proxy || hasHostName) {
172
- return config;
173
- }
174
- const { origin } = new URL(window.location.href);
175
- let baseUrl;
176
- const jupyterLabConfigEl = document.getElementById('jupyter-config-data');
177
-
178
- if (jupyterLabConfigEl) {
179
- // This is jupyter lab
180
- baseUrl = JSON.parse(jupyterLabConfigEl.textContent || '').baseUrl;
181
- } else {
182
- // This is jupyter notebook
183
- baseUrl = document.getElementsByTagName('body')[0].getAttribute('data-base-url');
184
- }
185
- return {
186
- ...config,
187
- datasets: config.datasets.map(d => ({
188
- ...d,
189
- files: d.files.map(f => ({
190
- ...f,
191
- url: `${origin}${baseUrl}${f.url}`,
192
- })),
193
- })),
194
- };
173
+ if (!proxy || hasHostName) {
174
+ return config;
175
+ }
176
+ const { origin, pathname } = new URL(window.location.href);
177
+ const isInWorkspaces = origin.startsWith(WORKSPACES_URL_KEYWORD);
178
+
179
+ const jupyterLabConfigEl = document.getElementById('jupyter-config-data');
180
+
181
+ let baseUrl;
182
+ if (isInWorkspaces) {
183
+ const pathSegments = pathname.split('/');
184
+ const passthroughIndex = pathSegments.indexOf('passthrough');
185
+ if (passthroughIndex !== -1) {
186
+ baseUrl = pathSegments.slice(0, passthroughIndex + 2).join('/');
187
+ }
188
+ } else if (jupyterLabConfigEl) {
189
+ // This is jupyter lab
190
+ baseUrl = JSON.parse(jupyterLabConfigEl.textContent || '').baseUrl;
191
+ } else {
192
+ // This is jupyter notebook
193
+ baseUrl = document.getElementsByTagName('body')[0].getAttribute('data-base-url');
194
+ }
195
+ return {
196
+ ...config,
197
+ datasets: config.datasets.map(d => ({
198
+ ...d,
199
+ files: d.files.map(f => {
200
+ if (isInWorkspaces && f.url.startsWith('proxy')) {
201
+ // When the user is in workspaces, we do not use jupyter_server_proxy.
202
+ // Instead, the workspaces infrastructure has its own "passthrough" functionality.
203
+ f.url = f.url.replace('proxy', '');
204
+ }
205
+ return {
206
+ ...f,
207
+ url: `${origin}${baseUrl}${f.url}`,
208
+ };
209
+ }),
210
+ })),
211
+ };
195
212
  }
196
213
 
197
214
  async function render(view) {
@@ -204,6 +221,9 @@ async function render(view) {
204
221
  const storeUrls = view.model.get('store_urls');
205
222
  const invokeTimeout = view.model.get('invoke_timeout');
206
223
 
224
+ const pageMode = view.model.get('page_mode');
225
+ const pageEsm = view.model.get('page_esm');
226
+
207
227
  const pkgName = (jsDevMode ? "@vitessce/dev" : "vitessce");
208
228
 
209
229
  importMap.imports["vitessce"] = (customJsUrl.length > 0
@@ -217,8 +237,10 @@ async function render(view) {
217
237
  PluginViewType,
218
238
  PluginCoordinationType,
219
239
  PluginJointFileType,
240
+ PluginAsyncFunction,
220
241
  z,
221
242
  useCoordination,
243
+ usePageModeView,
222
244
  useGridItemSize,
223
245
  // TODO: names and function signatures are subject to change for the following functions
224
246
  // Reference: https://github.com/keller-mark/use-coordination/issues/37#issuecomment-1946226827
@@ -234,6 +256,7 @@ async function render(view) {
234
256
  let pluginCoordinationTypes = [];
235
257
  let pluginFileTypes = [];
236
258
  let pluginJointFileTypes = [];
259
+ let pluginAsyncFunctions = [];
237
260
 
238
261
  let pending = [];
239
262
  let batchId = 0;
@@ -318,12 +341,13 @@ async function render(view) {
318
341
  const pluginModule = (await import(pluginEsmUrl)).default;
319
342
  URL.revokeObjectURL(pluginEsmUrl);
320
343
 
321
- const pluginsObj = await pluginModule.createPlugins({
344
+ const pluginDeps = {
322
345
  React,
323
346
  PluginFileType,
324
347
  PluginViewType,
325
348
  PluginCoordinationType,
326
349
  PluginJointFileType,
350
+ PluginAsyncFunction,
327
351
  z,
328
352
  invokeCommand: invokePluginCommand,
329
353
  useCoordination,
@@ -334,7 +358,8 @@ async function render(view) {
334
358
  useComplexCoordinationSecondary,
335
359
  useCoordinationScopes,
336
360
  useCoordinationScopesBy,
337
- });
361
+ };
362
+ const pluginsObj = await pluginModule.createPlugins(pluginDeps);
338
363
  if(Array.isArray(pluginsObj.pluginViewTypes)) {
339
364
  pluginViewTypes = [...pluginViewTypes, ...pluginsObj.pluginViewTypes];
340
365
  }
@@ -347,7 +372,29 @@ async function render(view) {
347
372
  if(Array.isArray(pluginsObj.pluginJointFileTypes)) {
348
373
  pluginJointFileTypes = [...pluginJointFileTypes, ...pluginsObj.pluginJointFileTypes];
349
374
  }
375
+ if(Array.isArray(pluginsObj.pluginAsyncFunctions)) {
376
+ pluginAsyncFunctions = [...pluginAsyncFunctions, ...pluginsObj.pluginAsyncFunctions];
377
+ }
350
378
  } catch(e) {
379
+ console.error("Error loading plugin ESM or executing createPlugins function.");
380
+ console.error(e);
381
+ }
382
+ }
383
+
384
+ let PageComponent;
385
+ if(pageMode && pageEsm.length > 0) {
386
+ try {
387
+ const pageEsmUrl = URL.createObjectURL(new Blob([pageEsm], { type: "text/javascript" }));
388
+ const pageModule = (await import(pageEsmUrl)).default;
389
+ URL.revokeObjectURL(pageEsmUrl);
390
+
391
+ const pageDeps = {
392
+ React,
393
+ usePageModeView,
394
+ };
395
+ PageComponent = await pageModule.createPage(pageDeps);
396
+ } catch(e) {
397
+ console.error("Error loading page ESM or executing createPage function.")
351
398
  console.error(e);
352
399
  }
353
400
  }
@@ -415,14 +462,17 @@ async function render(view) {
415
462
 
416
463
  const vitessceProps = {
417
464
  height, theme, config, onConfigChange, validateConfig,
418
- pluginViewTypes, pluginCoordinationTypes, pluginFileTypes, pluginJointFileTypes,
419
- remountOnUidChange, stores,
465
+ pluginViewTypes, pluginCoordinationTypes,
466
+ pluginFileTypes,pluginJointFileTypes, pluginAsyncFunctions,
467
+ remountOnUidChange, stores, pageMode,
420
468
  };
421
469
 
422
470
  return e('div', { ref: divRef, style: { height: height + 'px' } },
423
471
  e(React.Suspense, { fallback: e('div', {}, 'Loading...') },
424
472
  e(React.StrictMode, {},
425
- e(Vitessce, vitessceProps)
473
+ e(Vitessce, vitessceProps,
474
+ (pageMode ? e(PageComponent, {}) : null)
475
+ ),
426
476
  ),
427
477
  ),
428
478
  );
@@ -456,6 +506,7 @@ function createPlugins(utilsForPlugins) {
456
506
  PluginViewType,
457
507
  PluginCoordinationType,
458
508
  PluginJointFileType,
509
+ PluginAsyncFunction,
459
510
  z,
460
511
  useCoordination,
461
512
  invokeCommand,
@@ -465,11 +516,33 @@ function createPlugins(utilsForPlugins) {
465
516
  pluginFileTypes: undefined,
466
517
  pluginCoordinationTypes: undefined,
467
518
  pluginJointFileTypes: undefined,
519
+ pluginAsyncFunctions: undefined,
468
520
  };
469
521
  }
470
522
  export default { createPlugins };
471
523
  """
472
524
 
525
+ DEFAULT_PAGE_ESM = """
526
+ function createPage(utilsForPage) {
527
+ const {
528
+ React,
529
+ usePageModeView,
530
+ } = utilsForPage;
531
+
532
+ function PageComponent(props) {
533
+ const BiomarkerSelect = usePageModeView('biomarker-select');
534
+
535
+ return (
536
+ <div>
537
+ <BiomarkerSelect />
538
+ </div>
539
+ );
540
+ }
541
+ return PageComponent;
542
+ }
543
+ export default { createPage };
544
+ """
545
+
473
546
 
474
547
  class VitesscePlugin:
475
548
  """
@@ -514,11 +587,13 @@ class VitessceWidget(anywidget.AnyWidget):
514
587
  custom_js_url = Unicode('').tag(sync=True)
515
588
  plugin_esm = List(trait=Unicode(''), default_value=[]).tag(sync=True)
516
589
  remount_on_uid_change = Bool(True).tag(sync=True)
590
+ page_mode = Bool(False).tag(sync=True)
591
+ page_esm = Unicode('').tag(sync=True)
517
592
  invoke_timeout = Int(300000).tag(sync=True)
518
593
 
519
594
  store_urls = List(trait=Unicode(''), default_value=[]).tag(sync=True)
520
595
 
521
- def __init__(self, config, height=600, theme='auto', uid=None, port=None, proxy=False, js_package_version='3.5.11', js_dev_mode=False, custom_js_url='', plugins=None, remount_on_uid_change=True, prefer_local=True, invoke_timeout=300000):
596
+ def __init__(self, config, height=600, theme='auto', uid=None, port=None, proxy=False, js_package_version='3.5.11', js_dev_mode=False, custom_js_url='', plugins=None, remount_on_uid_change=True, prefer_local=True, invoke_timeout=300000, page_mode=False, page_esm=None):
522
597
  """
523
598
  Construct a new Vitessce widget.
524
599
 
@@ -531,10 +606,12 @@ class VitessceWidget(anywidget.AnyWidget):
531
606
  :param str js_package_version: The version of the NPM package ('vitessce' if not js_dev_mode else '@vitessce/dev').
532
607
  :param bool js_dev_mode: Should @vitessce/dev be used (typically for debugging purposes)? By default, False.
533
608
  :param str custom_js_url: A URL to a JavaScript file to use (instead of 'vitessce' or '@vitessce/dev' NPM package).
534
- :param list[WidgetPlugin] plugins: A list of subclasses of VitesscePlugin. Optional.
609
+ :param list[VitesscePlugin] plugins: A list of subclasses of VitesscePlugin. Optional.
535
610
  :param bool remount_on_uid_change: Passed to the remountOnUidChange prop of the <Vitessce/> React component. By default, True.
536
611
  :param bool prefer_local: Should local data be preferred (only applies to `*_artifact` data objects)? By default, True.
537
- :param int invoke_timeout: The timeout in milliseconds for invoking Python functions from JavaScript. By default, 30000.
612
+ :param int invoke_timeout: The timeout in milliseconds for invoking Python functions from JavaScript. By default, 300000.
613
+ :param bool page_mode: Whether to render the <Vitessce/> component in grid-mode or page-mode. By default, False.
614
+ :param str page_esm: The ES module string for the page component creation function. Optional.
538
615
 
539
616
  .. code-block:: python
540
617
  :emphasize-lines: 4
@@ -566,7 +643,9 @@ class VitessceWidget(anywidget.AnyWidget):
566
643
  super(VitessceWidget, self).__init__(
567
644
  config=config_dict, height=height, theme=theme, proxy=proxy,
568
645
  js_package_version=js_package_version, js_dev_mode=js_dev_mode, custom_js_url=custom_js_url,
569
- plugin_esm=plugin_esm, remount_on_uid_change=remount_on_uid_change, invoke_timeout=invoke_timeout,
646
+ plugin_esm=plugin_esm, remount_on_uid_change=remount_on_uid_change,
647
+ page_mode=page_mode, page_esm=('' if page_esm is None else page_esm),
648
+ invoke_timeout=invoke_timeout,
570
649
  uid=uid_str, store_urls=list(self._stores.keys())
571
650
  )
572
651
 
@@ -648,7 +727,7 @@ class VitessceWidget(anywidget.AnyWidget):
648
727
  # Launch Vitessce using plain HTML representation (no ipywidgets)
649
728
 
650
729
 
651
- def ipython_display(config, height=600, theme='auto', base_url=None, host_name=None, uid=None, port=None, proxy=False, js_package_version='3.5.11', js_dev_mode=False, custom_js_url='', plugin_esm=DEFAULT_PLUGIN_ESM, remount_on_uid_change=True):
730
+ def ipython_display(config, height=600, theme='auto', base_url=None, host_name=None, uid=None, port=None, proxy=False, js_package_version='3.5.11', js_dev_mode=False, custom_js_url='', plugins=None, remount_on_uid_change=True, page_mode=False, page_esm=None):
652
731
  from IPython.display import display, HTML
653
732
  uid_str = "vitessce" + get_uid_str(uid)
654
733
 
@@ -658,12 +737,17 @@ def ipython_display(config, height=600, theme='auto', base_url=None, host_name=N
658
737
  routes = config.get_routes()
659
738
  serve_routes(config, routes, use_port)
660
739
 
740
+ plugins = plugins or []
741
+ plugin_esm = [p.plugin_esm for p in plugins]
742
+
661
743
  model_vals = {
662
744
  "uid": uid_str,
663
745
  "js_package_version": js_package_version,
664
746
  "js_dev_mode": js_dev_mode,
665
747
  "custom_js_url": custom_js_url,
666
748
  "plugin_esm": plugin_esm,
749
+ "page_mode": page_mode,
750
+ "page_esm": ('' if page_esm is None else page_esm),
667
751
  "remount_on_uid_change": remount_on_uid_change,
668
752
  "invoke_timeout": 30000,
669
753
  "proxy": proxy,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vitessce
3
- Version: 3.5.4
3
+ Version: 3.5.5
4
4
  Summary: Jupyter widget facilitating interactive visualization of spatial single-cell data with Vitessce
5
5
  Project-URL: repository, https://github.com/vitessce/vitessce-python
6
6
  Author-email: Mark Keller <mark_keller@hms.harvard.edu>
@@ -1,5 +1,5 @@
1
1
  vitessce/__init__.py,sha256=03pAJROyDfo6EErBx8Oqb9Emqol1QKQ_9zKhkwuQ36Q,1781
2
- vitessce/config.py,sha256=OhFbbyx3bSLysZwx2DI_O9fPPck4nPyErxMVTrFeHTs,80985
2
+ vitessce/config.py,sha256=vZX4k-kS3zspYVT08PRuLSBeB-tU4GJgbN93z1Ed1iY,81228
3
3
  vitessce/config_converter.py,sha256=IRPnGPGaETvJbYZNUv2pe54SHHHsDY9VWo3JRjSI5FM,14681
4
4
  vitessce/constants.py,sha256=NTir8PzJAGX6a5Q3Ike0qy2n75WyFAezMP05PZ4mgWA,15233
5
5
  vitessce/export.py,sha256=L7j5sVC0nBSqGocFWQyyHImSiAF4IjXhmqV1QtpuNc4,3874
@@ -7,7 +7,7 @@ vitessce/file_def_utils.py,sha256=RM9Hj1uwPY-Y37JLOHiKA_acGY40p8C9WsDk8iVjDz8,66
7
7
  vitessce/repr.py,sha256=qMmefmZ3E-3sRVxeI5q1DTZnfuwbXKiA85eyqk5MCT4,2287
8
8
  vitessce/routes.py,sha256=U8T-L-3QCD_tAbPF8LsUlSMhPWNbyzbLNUnxP9Z9s9o,2140
9
9
  vitessce/utils.py,sha256=obzjj65qsagu60_yuhGc-0jmHO-BW0Y-bDs0FgrBqLY,981
10
- vitessce/widget.py,sha256=pLG0IuAnGdbqIVj_JkSY2TCPJH0qQlnvKG7YRmOLrr8,28336
10
+ vitessce/widget.py,sha256=1NEtcdWNi7zvxaaVApPdcmVWLFdnSAepkP9mhM-MNdg,31848
11
11
  vitessce/wrappers.py,sha256=kVqXFcpbuIb8MtZQSB4F7k32JM8bwCgMBrOkcAAhmvc,74676
12
12
  vitessce/data_utils/__init__.py,sha256=3mWi1lMjoj4_dNbhMOvyE-HEJu0qpMzcmkhfz_5T6n8,361
13
13
  vitessce/data_utils/anndata.py,sha256=iLa5-bRezHgBzL_XCHO7w0pc0RQ4urzZbDsqJbBYeCk,10668
@@ -17,7 +17,7 @@ vitessce/data_utils/ome.py,sha256=aK-iGNgjUmUMsWgdZhW78VrtMBKMW_jIQDYnsupq6BE,54
17
17
  vitessce/widget_plugins/__init__.py,sha256=lto2GXnc7KwjIoT-jvzyRYLj0XTJG3uxoX45Hc9EcWA,82
18
18
  vitessce/widget_plugins/demo_plugin.py,sha256=14S7nOxdlKSxIHw9DUcNCN83NE_U1EMPy2D4k0FDues,1797
19
19
  vitessce/widget_plugins/spatial_query.py,sha256=CYxvmMT1Je_jguikPROQxlegkPgIIzemKGbZSJfZMyI,12314
20
- vitessce-3.5.4.dist-info/METADATA,sha256=OatY_JEy-365BTQu0HM-Fb6hF6Nhkkx4sclpRBQ1kPM,9556
21
- vitessce-3.5.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
22
- vitessce-3.5.4.dist-info/licenses/LICENSE,sha256=sNNpI0PQ57AW8_XnTAjU5Yw8YBA_DRNkVHrHYpCIhRU,1067
23
- vitessce-3.5.4.dist-info/RECORD,,
20
+ vitessce-3.5.5.dist-info/METADATA,sha256=TxKrmFE7fIgpxR_ipuLnjB3oiw52VsXPqk_oLJnmRu8,9556
21
+ vitessce-3.5.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
22
+ vitessce-3.5.5.dist-info/licenses/LICENSE,sha256=sNNpI0PQ57AW8_XnTAjU5Yw8YBA_DRNkVHrHYpCIhRU,1067
23
+ vitessce-3.5.5.dist-info/RECORD,,