vitessce 3.5.4__py3-none-any.whl → 3.5.6__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,45 @@ 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
+ const jupyterLabConfigEl = document.getElementById('jupyter-config-data');
179
+
180
+ let baseUrl;
181
+ if (isInWorkspaces) {
182
+ const pathSegments = pathname.split('/');
183
+ const passthroughIndex = pathSegments.indexOf('passthrough');
184
+ if (passthroughIndex !== -1) {
185
+ baseUrl = pathSegments.slice(0, passthroughIndex + 3).join('/');
186
+ baseUrl += '/';
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
+ ...f,
201
+ url: `${origin}${baseUrl}${f.url}`,
202
+ })),
203
+ })),
204
+ };
195
205
  }
196
206
 
197
207
  async function render(view) {
@@ -204,6 +214,9 @@ async function render(view) {
204
214
  const storeUrls = view.model.get('store_urls');
205
215
  const invokeTimeout = view.model.get('invoke_timeout');
206
216
 
217
+ const pageMode = view.model.get('page_mode');
218
+ const pageEsm = view.model.get('page_esm');
219
+
207
220
  const pkgName = (jsDevMode ? "@vitessce/dev" : "vitessce");
208
221
 
209
222
  importMap.imports["vitessce"] = (customJsUrl.length > 0
@@ -217,8 +230,10 @@ async function render(view) {
217
230
  PluginViewType,
218
231
  PluginCoordinationType,
219
232
  PluginJointFileType,
233
+ PluginAsyncFunction,
220
234
  z,
221
235
  useCoordination,
236
+ usePageModeView,
222
237
  useGridItemSize,
223
238
  // TODO: names and function signatures are subject to change for the following functions
224
239
  // Reference: https://github.com/keller-mark/use-coordination/issues/37#issuecomment-1946226827
@@ -234,6 +249,7 @@ async function render(view) {
234
249
  let pluginCoordinationTypes = [];
235
250
  let pluginFileTypes = [];
236
251
  let pluginJointFileTypes = [];
252
+ let pluginAsyncFunctions = [];
237
253
 
238
254
  let pending = [];
239
255
  let batchId = 0;
@@ -318,12 +334,13 @@ async function render(view) {
318
334
  const pluginModule = (await import(pluginEsmUrl)).default;
319
335
  URL.revokeObjectURL(pluginEsmUrl);
320
336
 
321
- const pluginsObj = await pluginModule.createPlugins({
337
+ const pluginDeps = {
322
338
  React,
323
339
  PluginFileType,
324
340
  PluginViewType,
325
341
  PluginCoordinationType,
326
342
  PluginJointFileType,
343
+ PluginAsyncFunction,
327
344
  z,
328
345
  invokeCommand: invokePluginCommand,
329
346
  useCoordination,
@@ -334,7 +351,8 @@ async function render(view) {
334
351
  useComplexCoordinationSecondary,
335
352
  useCoordinationScopes,
336
353
  useCoordinationScopesBy,
337
- });
354
+ };
355
+ const pluginsObj = await pluginModule.createPlugins(pluginDeps);
338
356
  if(Array.isArray(pluginsObj.pluginViewTypes)) {
339
357
  pluginViewTypes = [...pluginViewTypes, ...pluginsObj.pluginViewTypes];
340
358
  }
@@ -347,7 +365,29 @@ async function render(view) {
347
365
  if(Array.isArray(pluginsObj.pluginJointFileTypes)) {
348
366
  pluginJointFileTypes = [...pluginJointFileTypes, ...pluginsObj.pluginJointFileTypes];
349
367
  }
368
+ if(Array.isArray(pluginsObj.pluginAsyncFunctions)) {
369
+ pluginAsyncFunctions = [...pluginAsyncFunctions, ...pluginsObj.pluginAsyncFunctions];
370
+ }
371
+ } catch(e) {
372
+ console.error("Error loading plugin ESM or executing createPlugins function.");
373
+ console.error(e);
374
+ }
375
+ }
376
+
377
+ let PageComponent;
378
+ if(pageMode && pageEsm.length > 0) {
379
+ try {
380
+ const pageEsmUrl = URL.createObjectURL(new Blob([pageEsm], { type: "text/javascript" }));
381
+ const pageModule = (await import(pageEsmUrl)).default;
382
+ URL.revokeObjectURL(pageEsmUrl);
383
+
384
+ const pageDeps = {
385
+ React,
386
+ usePageModeView,
387
+ };
388
+ PageComponent = await pageModule.createPage(pageDeps);
350
389
  } catch(e) {
390
+ console.error("Error loading page ESM or executing createPage function.")
351
391
  console.error(e);
352
392
  }
353
393
  }
@@ -415,14 +455,17 @@ async function render(view) {
415
455
 
416
456
  const vitessceProps = {
417
457
  height, theme, config, onConfigChange, validateConfig,
418
- pluginViewTypes, pluginCoordinationTypes, pluginFileTypes, pluginJointFileTypes,
419
- remountOnUidChange, stores,
458
+ pluginViewTypes, pluginCoordinationTypes,
459
+ pluginFileTypes,pluginJointFileTypes, pluginAsyncFunctions,
460
+ remountOnUidChange, stores, pageMode,
420
461
  };
421
462
 
422
463
  return e('div', { ref: divRef, style: { height: height + 'px' } },
423
464
  e(React.Suspense, { fallback: e('div', {}, 'Loading...') },
424
465
  e(React.StrictMode, {},
425
- e(Vitessce, vitessceProps)
466
+ e(Vitessce, vitessceProps,
467
+ (pageMode ? e(PageComponent, {}) : null)
468
+ ),
426
469
  ),
427
470
  ),
428
471
  );
@@ -456,6 +499,7 @@ function createPlugins(utilsForPlugins) {
456
499
  PluginViewType,
457
500
  PluginCoordinationType,
458
501
  PluginJointFileType,
502
+ PluginAsyncFunction,
459
503
  z,
460
504
  useCoordination,
461
505
  invokeCommand,
@@ -465,11 +509,33 @@ function createPlugins(utilsForPlugins) {
465
509
  pluginFileTypes: undefined,
466
510
  pluginCoordinationTypes: undefined,
467
511
  pluginJointFileTypes: undefined,
512
+ pluginAsyncFunctions: undefined,
468
513
  };
469
514
  }
470
515
  export default { createPlugins };
471
516
  """
472
517
 
518
+ DEFAULT_PAGE_ESM = """
519
+ function createPage(utilsForPage) {
520
+ const {
521
+ React,
522
+ usePageModeView,
523
+ } = utilsForPage;
524
+
525
+ function PageComponent(props) {
526
+ const BiomarkerSelect = usePageModeView('biomarker-select');
527
+
528
+ return (
529
+ <div>
530
+ <BiomarkerSelect />
531
+ </div>
532
+ );
533
+ }
534
+ return PageComponent;
535
+ }
536
+ export default { createPage };
537
+ """
538
+
473
539
 
474
540
  class VitesscePlugin:
475
541
  """
@@ -514,11 +580,13 @@ class VitessceWidget(anywidget.AnyWidget):
514
580
  custom_js_url = Unicode('').tag(sync=True)
515
581
  plugin_esm = List(trait=Unicode(''), default_value=[]).tag(sync=True)
516
582
  remount_on_uid_change = Bool(True).tag(sync=True)
583
+ page_mode = Bool(False).tag(sync=True)
584
+ page_esm = Unicode('').tag(sync=True)
517
585
  invoke_timeout = Int(300000).tag(sync=True)
518
586
 
519
587
  store_urls = List(trait=Unicode(''), default_value=[]).tag(sync=True)
520
588
 
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):
589
+ 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
590
  """
523
591
  Construct a new Vitessce widget.
524
592
 
@@ -531,10 +599,12 @@ class VitessceWidget(anywidget.AnyWidget):
531
599
  :param str js_package_version: The version of the NPM package ('vitessce' if not js_dev_mode else '@vitessce/dev').
532
600
  :param bool js_dev_mode: Should @vitessce/dev be used (typically for debugging purposes)? By default, False.
533
601
  :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.
602
+ :param list[VitesscePlugin] plugins: A list of subclasses of VitesscePlugin. Optional.
535
603
  :param bool remount_on_uid_change: Passed to the remountOnUidChange prop of the <Vitessce/> React component. By default, True.
536
604
  :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.
605
+ :param int invoke_timeout: The timeout in milliseconds for invoking Python functions from JavaScript. By default, 300000.
606
+ :param bool page_mode: Whether to render the <Vitessce/> component in grid-mode or page-mode. By default, False.
607
+ :param str page_esm: The ES module string for the page component creation function. Optional.
538
608
 
539
609
  .. code-block:: python
540
610
  :emphasize-lines: 4
@@ -566,7 +636,9 @@ class VitessceWidget(anywidget.AnyWidget):
566
636
  super(VitessceWidget, self).__init__(
567
637
  config=config_dict, height=height, theme=theme, proxy=proxy,
568
638
  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,
639
+ plugin_esm=plugin_esm, remount_on_uid_change=remount_on_uid_change,
640
+ page_mode=page_mode, page_esm=('' if page_esm is None else page_esm),
641
+ invoke_timeout=invoke_timeout,
570
642
  uid=uid_str, store_urls=list(self._stores.keys())
571
643
  )
572
644
 
@@ -648,7 +720,7 @@ class VitessceWidget(anywidget.AnyWidget):
648
720
  # Launch Vitessce using plain HTML representation (no ipywidgets)
649
721
 
650
722
 
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):
723
+ 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
724
  from IPython.display import display, HTML
653
725
  uid_str = "vitessce" + get_uid_str(uid)
654
726
 
@@ -658,12 +730,17 @@ def ipython_display(config, height=600, theme='auto', base_url=None, host_name=N
658
730
  routes = config.get_routes()
659
731
  serve_routes(config, routes, use_port)
660
732
 
733
+ plugins = plugins or []
734
+ plugin_esm = [p.plugin_esm for p in plugins]
735
+
661
736
  model_vals = {
662
737
  "uid": uid_str,
663
738
  "js_package_version": js_package_version,
664
739
  "js_dev_mode": js_dev_mode,
665
740
  "custom_js_url": custom_js_url,
666
741
  "plugin_esm": plugin_esm,
742
+ "page_mode": page_mode,
743
+ "page_esm": ('' if page_esm is None else page_esm),
667
744
  "remount_on_uid_change": remount_on_uid_change,
668
745
  "invoke_timeout": 30000,
669
746
  "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.6
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=XngfJKODnUwvNJ8qM1nC10fvdZPzpj6MsG2duPaCnj0,31490
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.6.dist-info/METADATA,sha256=GYRIA202oFik97MbgKsYZDgpXqo9CbxExdvrx3CRycs,9556
21
+ vitessce-3.5.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
22
+ vitessce-3.5.6.dist-info/licenses/LICENSE,sha256=sNNpI0PQ57AW8_XnTAjU5Yw8YBA_DRNkVHrHYpCIhRU,1067
23
+ vitessce-3.5.6.dist-info/RECORD,,