plexora 0.0.8__tar.gz → 0.0.9__tar.gz
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.
- {plexora-0.0.8/plexora.egg-info → plexora-0.0.9}/PKG-INFO +1 -1
- {plexora-0.0.8 → plexora-0.0.9}/plexora/connect.py +146 -129
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/node/app.py +18 -1
- {plexora-0.0.8 → plexora-0.0.9/plexora.egg-info}/PKG-INFO +1 -1
- {plexora-0.0.8 → plexora-0.0.9}/pyproject.toml +1 -1
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_connect.py +68 -50
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_node_warm_and_cache.py +28 -10
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_remote_connect.py +10 -6
- {plexora-0.0.8 → plexora-0.0.9}/LICENSE +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/MANIFEST.in +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/README.md +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/__init__.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/__main__.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/_resources.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/_url.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/api/__init__.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/api/dataset.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/api/http.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/api/plugin.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/api/store.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/askpass.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/cli.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/dist/354_bundle.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/dist/354_bundle.js.LICENSE.txt +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/dist/418_bundle.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/dist/418_bundle.js.LICENSE.txt +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/dist/693b935cb1f907814be9c6199a68c217.svg +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/dist/713c134ff47cd328a5b711526f151082.svg +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/dist/770_bundle.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/dist/ff743f408972e0e96ca9153ed5ae4c83.svg +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/dist/vendor_bundle.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/dist/vendor_bundle.js.LICENSE.txt +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/LICENSE.txt +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/canvas-overlay-hd.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/changelog.txt +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/.gitattributes +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/LICENSE.txt +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/Toolbar.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/fullpage.psd +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/home.psd +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/next.psd +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/previous.psd +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateleft.psd +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateright.psd +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/zoom.psd +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/README.md +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_grouphover.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_hover.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_pressed.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_rest.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_grouphover.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_hover.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_pressed.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_rest.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_grouphover.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_hover.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_pressed.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_rest.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_grouphover.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_hover.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_pressed.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_rest.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_grouphover.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_hover.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_pressed.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_rest.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_grouphover.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_hover.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_pressed.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_rest.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_grouphover.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_hover.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_pressed.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_rest.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_grouphover.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_hover.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_pressed.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_rest.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-scalebar.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/css/import.css +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/css/main.css +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/css/openProject.css +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/css/quickView.css +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/css/settings.css +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/css/tokens.css +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/css/viewer.css +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/img/apple-touch-icon.png +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/img/favicon.ico +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/img/logo.ai +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/img/logo.svg +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/img/logo_with_text.ai +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/img/logo_with_text.svg +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/main.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/pluginRegistry.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/appRouter.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/appStatus.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/browsePicker.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/connectionModal.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/dataLayer.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/dataLocation.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/datasetContext.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/fileLocation.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/glRenderer.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/importFormValidation.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/keyboardShortcuts.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/logTerminal.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/numericData.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/pageBoot.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/passVariablesToFrontend.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/pathPicker.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/placePicker.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/remoteGlobe.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/remoteState.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/resourceRouting.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/resourceStatus.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/sessionExpiry.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/services/simpleEventHandler.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/vendor.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/channelList.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/channelNamesUpload.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/colorSwatchPicker.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/columnClassifier.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/coordinateField.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/dataSourceField.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/imageViewer.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/miniMap.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/navbarControls.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/openProjectPage.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/popoverPortal.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/projectEdit.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/quickViewLanding.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/rainbow.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/requirementsModal.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/rgbImageViewer.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/searchableSelect.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/segmentationProgress.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/segmentationWait.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/settingsPage.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/toolLoader.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/viewerControls.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/viewerManager.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/views/viewerSidebar.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/js/workers/tileDecoder.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/shaders/frag.glsl +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/src/shaders/vert.glsl +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/templates/_fragment.html +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/templates/base.html +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/templates/index.html +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/templates/open_project.html +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/templates/project_columns.html +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/templates/project_edit.html +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/templates/settings.html +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/client/templates/upload.html +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/datasource.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/jupyter.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/nodes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/notebook_env.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/paths.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/__init__.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/cell_explorer/__init__.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/cell_explorer/server/__init__.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/cell_explorer/server/routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/cell_explorer/server/state.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/cell_explorer/server/values.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/cell_explorer/server/variables.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/cell_explorer/static/cellExplorerApi.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/cell_explorer/static/cellExplorerColors.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/cell_explorer/static/cellExplorerContinuous.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/cell_explorer/static/cellExplorerFigureBridge.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/cell_explorer/static/cellExplorerLegend.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/cell_explorer/static/cellExplorerRoiBridge.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/cell_explorer/static/cellExplorerSidebarController.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/cell_explorer/static/cellExplorerState.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/cell_explorer/static/cell_explorer.css +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/cell_explorer/templates/cell_explorer/panel.html +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/__init__.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/server/__init__.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/server/compose.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/server/export.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/server/export_jobs.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/server/operations.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/server/pixels.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/server/provenance.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/server/render.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/server/repository.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/server/routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/server/schema.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/server/shapegeom.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/server/sources.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/server/strokegeom.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/server/textmetrics.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureActions.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureBuilderApi.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureCanvas.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureCaptureBoxes.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureCaptureDock.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureCaptureTool.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureChoiceField.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureColorField.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureConfirm.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureContextBar.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureContextMenu.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureDocumentState.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureExportUi.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureImagePanel.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureLibrary.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureLineDefs.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureLinePanel.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figurePanelCompositor.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figurePointEditor.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureQuickEdit.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureRichText.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureSceneSnapshot.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureSchema.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureShapeDefs.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureShapeDrawing.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureShapeGeometry.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureShapePanel.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureSidebarController.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureStrokeGeometry.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureTextEditor.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureTextPanel.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureViewOptions.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figureWorkspace.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/static/figure_builder.css +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/templates/figure_builder/library.html +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/templates/figure_builder/workspace.html +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/figure_builder/templates/figure_builder/workspace_body.html +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/gating/__init__.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/gating/server/__init__.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/gating/server/anndata_gates.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/gating/server/database.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/gating/server/model.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/gating/server/routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/gating/server/tableops.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/gating/static/csvGatingList.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/gating/static/gating.css +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/gating/static/gatingApi.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/gating/static/gatingSidebarController.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/gating/templates/gating/legacy.html +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/gating/templates/gating/panel.html +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/__init__.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/server/__init__.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/server/adapters.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/server/geojson.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/server/geometry.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/server/mapping.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/server/operations.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/server/repository.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/server/routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/server/schema.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/server/tableops.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/static/roi.css +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/static/roiApi.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/static/roiFigureBridge.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/static/roiGeometry.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/static/roiRenderer.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/static/roiSidebarController.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/static/roiState.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/static/roiTools.js +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/plugins/roi/templates/roi/panel.html +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/proxy.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/models/adapters/__init__.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/models/adapters/anndata_adapter.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/models/adapters/base.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/models/adapters/classify.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/models/adapters/csv_adapter.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/models/adapters/inspection.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/models/adapters/spatialdata_adapter.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/models/centroid_tiles.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/models/data_migration.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/models/data_model.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/models/database_model.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/models/nodes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/models/project.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/models/recipes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/models/remote_sessions.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/models/remotes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/models/secret_store.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/node/api.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/node/resources.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/plugins.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/providers/__init__.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/providers/base.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/providers/http.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/providers/local.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/providers/node.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/providers/operations.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/providers/wire.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/routes/browse_routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/routes/data_routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/routes/import_routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/routes/page_routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/routes/project_routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/routes/quick_view_routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/routes/settings_routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/routes/system_routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/routes/tool_routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/routes/transfer_routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/utils/addHEColumns.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/utils/channel_file.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/utils/dir_listing.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/utils/fast_png.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/utils/file_transfer.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/utils/fullConversion.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/utils/native_dialog.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/utils/pre_normalization.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/utils/segmentation_pyramid.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/utils/smallestenclosingcircle.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server/utils/tiffsurgeon.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/server_cli.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora/test.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora.egg-info/SOURCES.txt +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora.egg-info/dependency_links.txt +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora.egg-info/entry_points.txt +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora.egg-info/requires.txt +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/plexora.egg-info/top_level.txt +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/setup.cfg +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_anndata_adapter.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_api_dataset.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_api_store.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_app_router.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_app_shell.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_auth_token.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_browse_routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_cell_color_layer.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_cell_mode_control.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_centroid_tiles.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_channel_names_modal.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_channel_names_upload.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_channel_overview.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_channel_rename_state.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_classify_columns.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_cli.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_column_classifier_css.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_config_store.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_connection_modal.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_csv_adapter.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_data_location.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_database_model.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_datalayer_requests.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_derive_anndata_channel_names.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_detect_data_type.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_file_location.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_file_transfer_node.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_hd_slider_domain.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_icon_names.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_jupyter_viewer.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_label_outlines.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_label_tile_lifecycle.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_marker_split.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_mask_without_table.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_metadata_columns.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_mini_map.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_nav_shortcuts.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_node_direct_routing.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_node_disconnect.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_node_dynamic.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_node_image.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_node_reconnect.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_node_routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_node_table.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_notebook_env.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_notebook_mode.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_optional_segmentation.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_page_assets.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_page_routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_path_picker.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_paths.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_plugin_boundary.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_plugin_css_boundary.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_plugins.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_popover_portal.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_project_edit_routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_project_record.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_quick_view_routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_recipes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_reconnect.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_refresh_dataset.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_register_anndata_datasource.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_register_image_datasource.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_remote_globe.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_remote_health.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_remote_state.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_rename_channels.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_requirements_routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_resource_status_banner.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_segmentation_mapping.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_segmentation_mode_routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_segmentation_pyramid.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_segmentation_wait.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_session_expiry.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_session_walltime.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_settings_page.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_settings_remotes_page.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_shared_roots.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_spatialdata_adapter.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_tile_url.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_tool_assets.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_tool_cards.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_tool_coexistence.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_tool_switching.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_transfer_routes.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_unreachable_node.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_url_helpers.py +0 -0
- {plexora-0.0.8 → plexora-0.0.9}/tests/test_view_menu.py +0 -0
|
@@ -222,7 +222,12 @@ def normalize_remote_command(remote_command):
|
|
|
222
222
|
#: has Plexora is asking for the current one, and a plain install would report
|
|
223
223
|
#: "already satisfied" and leave last year's copy in place -- which is the
|
|
224
224
|
#: exact failure the option exists to end.
|
|
225
|
-
|
|
225
|
+
#: `--progress-bar off` because the install rides the launch's ssh, which asks
|
|
226
|
+
#: for a pty (`-t`) -- and pip with a pty redraws its download bar with
|
|
227
|
+
#: carriage returns, which a line-based log renders as one line growing to
|
|
228
|
+
#: kilobytes. The per-package Collecting/Installing lines still come through,
|
|
229
|
+
#: and they are the progress worth showing.
|
|
230
|
+
PIP_ARGUMENTS = ("install", "--progress-bar", "off", "--upgrade", "plexora")
|
|
226
231
|
|
|
227
232
|
#: What conda does to a subprocess's output unless told not to: buffers it and
|
|
228
233
|
#: prints the lot when the child exits. Correct for a script whose output is a
|
|
@@ -346,23 +351,35 @@ def parse_installed_version(lines):
|
|
|
346
351
|
return None
|
|
347
352
|
|
|
348
353
|
|
|
349
|
-
|
|
350
|
-
|
|
354
|
+
#: Printed by the remote shell between the install and the launch. The two
|
|
355
|
+
#: run in ONE ssh -- `pip … && echo … && srun …` -- so the only way to know
|
|
356
|
+
#: pip finished (rather than printed its last routine line) is to say so
|
|
357
|
+
#: ourselves. `&&` is also the failure story: a pip that exits non-zero
|
|
358
|
+
#: short-circuits the chain, the marker never appears, the ssh ends, and
|
|
359
|
+
#: nothing was launched from the half-upgraded environment.
|
|
360
|
+
INSTALL_DONE_MARK = "PLEXORA_INSTALL_DONE"
|
|
351
361
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
362
|
+
|
|
363
|
+
def parse_install_done(line):
|
|
364
|
+
"""Matcher for the marker line. True on the marker, None otherwise."""
|
|
365
|
+
return True if line.strip() == INSTALL_DONE_MARK else None
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def install_prefixed(install_line, launch_line):
|
|
369
|
+
"""One remote command: install, say so, then launch.
|
|
370
|
+
|
|
371
|
+
One command because it is one login. The install used to be its own ssh,
|
|
372
|
+
which was one extra authentication -- and at a site that confirms every
|
|
373
|
+
login on somebody's phone, "connect" buzzed it twice. Chaining also puts
|
|
374
|
+
the install literally immediately before the run, in the same shell, so
|
|
375
|
+
nothing can start from the copy the upgrade was replacing.
|
|
376
|
+
|
|
377
|
+
Under a scheduler the chain still runs the install on the LOGIN node --
|
|
378
|
+
it precedes the `srun` in the same command -- which is where it belongs:
|
|
379
|
+
the environment is on a shared filesystem, and the allocation should not
|
|
380
|
+
be spent watching pip download wheels.
|
|
360
381
|
"""
|
|
361
|
-
|
|
362
|
-
if jump:
|
|
363
|
-
argv += ["-J", jump]
|
|
364
|
-
argv += [target, install_line]
|
|
365
|
-
return argv
|
|
382
|
+
return f"{install_line} && echo {INSTALL_DONE_MARK} && {launch_line}"
|
|
366
383
|
|
|
367
384
|
|
|
368
385
|
def remote_command_line(remote_command, port, *, bind_node=False, datasource=None,
|
|
@@ -823,24 +840,6 @@ class _Watched:
|
|
|
823
840
|
def tail(self, count=6):
|
|
824
841
|
return self.lines[-count:]
|
|
825
842
|
|
|
826
|
-
def wait_for_exit(self, deadline):
|
|
827
|
-
"""Block until this process ends. Its exit code, or None if it did not.
|
|
828
|
-
|
|
829
|
-
The one watcher here that is waited ON rather than merely watched: an
|
|
830
|
-
install has to have finished before the launch that depends on it
|
|
831
|
-
starts. `drain` before returning, for the same reason every other
|
|
832
|
-
reader of `lines` calls it -- the pump thread is still working through
|
|
833
|
-
what the shell wrote on its way out, and that is where the reason for
|
|
834
|
-
a non-zero exit lives.
|
|
835
|
-
"""
|
|
836
|
-
while _now() < deadline:
|
|
837
|
-
code = self.process.poll()
|
|
838
|
-
if code is not None:
|
|
839
|
-
self.drain()
|
|
840
|
-
return code
|
|
841
|
-
_sleep(0.2)
|
|
842
|
-
return self.process.poll()
|
|
843
|
-
|
|
844
843
|
def stop(self):
|
|
845
844
|
if self.process.poll() is not None:
|
|
846
845
|
return
|
|
@@ -1087,14 +1086,13 @@ def _missing_command_hint(remote_command, watched):
|
|
|
1087
1086
|
|
|
1088
1087
|
|
|
1089
1088
|
class _BareInstall:
|
|
1090
|
-
"""
|
|
1089
|
+
"""What `_begin_install`/`_await_install` read, for a caller with no session.
|
|
1091
1090
|
|
|
1092
1091
|
`connect_node` drives one ssh by hand -- it predates `NodeSession` and is
|
|
1093
|
-
what `plexora connect
|
|
1092
|
+
what `plexora node connect` still runs -- so there is no object there to
|
|
1094
1093
|
read `target` and `remote_command` off. This is that object and nothing
|
|
1095
|
-
more
|
|
1096
|
-
|
|
1097
|
-
echoed lines and no progress list to update.
|
|
1094
|
+
more; its `_phase` is a no-op because a terminal has the echoed lines and
|
|
1095
|
+
no progress list to update.
|
|
1098
1096
|
"""
|
|
1099
1097
|
|
|
1100
1098
|
def __init__(self, target, remote_command, jump, ssh_opts, echo):
|
|
@@ -1103,7 +1101,6 @@ class _BareInstall:
|
|
|
1103
1101
|
self.jump = jump
|
|
1104
1102
|
self.ssh_opts = ssh_opts
|
|
1105
1103
|
self.echo = echo
|
|
1106
|
-
self.watchers = []
|
|
1107
1104
|
self.install_log = []
|
|
1108
1105
|
self.installed_version = None
|
|
1109
1106
|
|
|
@@ -1118,63 +1115,53 @@ class _BareInstall:
|
|
|
1118
1115
|
return watched
|
|
1119
1116
|
|
|
1120
1117
|
|
|
1121
|
-
def
|
|
1122
|
-
"""
|
|
1123
|
-
|
|
1124
|
-
Shared by both session shapes because it is the same act for both: one
|
|
1125
|
-
ssh, run to completion, whose output is the connection's own log. It runs
|
|
1126
|
-
BEFORE the launch and not beside it, which is the whole point of the
|
|
1127
|
-
option -- a viewer started from the copy that was already there, while an
|
|
1128
|
-
upgrade downloads alongside it, is the version the user asked to stop
|
|
1129
|
-
running.
|
|
1130
|
-
|
|
1131
|
-
Under a scheduler it still runs on the target -- the login node -- rather
|
|
1132
|
-
than inside the job. The environment being written to is on a filesystem
|
|
1133
|
-
both machines share, so the compute node gets the same result; and doing
|
|
1134
|
-
it here means the install is not spending an allocation, and not standing
|
|
1135
|
-
between somebody and the announce that says their job finally started.
|
|
1136
|
-
|
|
1137
|
-
The watcher is taken back out of `session.watchers` when it exits. That
|
|
1138
|
-
list is what `alive` is computed from -- `all(w.alive for w in ...)` --
|
|
1139
|
-
so an exited process left in it would report the whole connection dead the
|
|
1140
|
-
moment the install succeeded. Its output has already been echoed and is
|
|
1141
|
-
kept here, which is the part anybody wants afterwards.
|
|
1142
|
-
"""
|
|
1118
|
+
def _begin_install(session):
|
|
1119
|
+
"""Announce the install and hand back the pip line the launch is chained to."""
|
|
1143
1120
|
line = install_command_line(session.remote_command)
|
|
1144
1121
|
environment = environment_label(session.remote_command)
|
|
1145
1122
|
session._phase("installing")
|
|
1146
1123
|
session.echo(f" Installing Plexora on {session.target}"
|
|
1147
1124
|
+ (f" in {environment}" if environment else "")
|
|
1148
|
-
+ "; this can take a few minutes.")
|
|
1149
|
-
|
|
1150
|
-
install_ssh_argv(session.target, line, jump=session.jump,
|
|
1151
|
-
ssh_opts=session.ssh_opts),
|
|
1152
|
-
"install",
|
|
1153
|
-
)
|
|
1154
|
-
try:
|
|
1155
|
-
code = watched.wait_for_exit(_now() + INSTALL_TIMEOUT)
|
|
1156
|
-
finally:
|
|
1157
|
-
watched.stop()
|
|
1158
|
-
if watched in session.watchers:
|
|
1159
|
-
session.watchers.remove(watched)
|
|
1160
|
-
if watched in _ACTIVE:
|
|
1161
|
-
_ACTIVE.remove(watched)
|
|
1162
|
-
session.install_log = list(watched.lines)
|
|
1125
|
+
+ " before launching it; this can take a few minutes.")
|
|
1126
|
+
return line
|
|
1163
1127
|
|
|
1164
|
-
if code is None:
|
|
1165
|
-
raise ConnectError(
|
|
1166
|
-
f"Installing Plexora on {session.target} did not finish within "
|
|
1167
|
-
f"{INSTALL_TIMEOUT:g}s.\n"
|
|
1168
|
-
+ "\n".join(f" {text}" for text in watched.tail())
|
|
1169
|
-
+ "\n\nThe environment may be half-upgraded. Run it by hand over "
|
|
1170
|
-
"there to see where it stopped:\n"
|
|
1171
|
-
f" {line}\n"
|
|
1172
|
-
"Or turn “Install or update Plexora” off and connect to the "
|
|
1173
|
-
"copy that is already installed."
|
|
1174
|
-
)
|
|
1175
|
-
if code != 0:
|
|
1176
|
-
raise ConnectError(_install_failure(session, line, watched, code))
|
|
1177
1128
|
|
|
1129
|
+
def _await_install(session, watched, line):
|
|
1130
|
+
"""Block until the chained install prints its marker, or explain why not.
|
|
1131
|
+
|
|
1132
|
+
`watched` is the connection's own primary ssh -- install and launch are
|
|
1133
|
+
one process now -- so unlike the old separate-ssh install there is no
|
|
1134
|
+
watcher to clean up afterwards: a marker means the same process is now
|
|
1135
|
+
busy launching, and a death before the marker means the connection failed
|
|
1136
|
+
and the watcher's exit is the correct thing for `alive` to report.
|
|
1137
|
+
"""
|
|
1138
|
+
event = watched.events["installed"]
|
|
1139
|
+
deadline = _now() + INSTALL_TIMEOUT
|
|
1140
|
+
# `found`, not the event alone: the pump sets every event when the stream
|
|
1141
|
+
# ends, precisely so waiters do not hang on an announce that is never
|
|
1142
|
+
# coming -- so a set event proves the process finished talking, and only
|
|
1143
|
+
# `found` proves the marker was in what it said.
|
|
1144
|
+
while not watched.found.get("installed"):
|
|
1145
|
+
event.wait(0.2)
|
|
1146
|
+
if watched.found.get("installed"):
|
|
1147
|
+
break
|
|
1148
|
+
if not watched.alive:
|
|
1149
|
+
watched.drain()
|
|
1150
|
+
session.install_log = list(watched.lines)
|
|
1151
|
+
code = watched.process.poll()
|
|
1152
|
+
raise ConnectError(_install_failure(session, line, watched, code))
|
|
1153
|
+
if _now() > deadline:
|
|
1154
|
+
raise ConnectError(
|
|
1155
|
+
f"Installing Plexora on {session.target} did not finish within "
|
|
1156
|
+
f"{INSTALL_TIMEOUT:g}s.\n"
|
|
1157
|
+
+ "\n".join(f" {text}" for text in watched.tail())
|
|
1158
|
+
+ "\n\nThe environment may be half-upgraded. Run it by hand "
|
|
1159
|
+
"over there to see where it stopped:\n"
|
|
1160
|
+
f" {line}\n"
|
|
1161
|
+
"Or turn “Install or update Plexora” off and connect to the "
|
|
1162
|
+
"copy that is already installed."
|
|
1163
|
+
)
|
|
1164
|
+
session.install_log = list(watched.lines)
|
|
1178
1165
|
version = parse_installed_version(watched.lines)
|
|
1179
1166
|
session.installed_version = version
|
|
1180
1167
|
session.echo(f" Plexora {version} is installed on {session.target}."
|
|
@@ -1311,9 +1298,9 @@ class Session:
|
|
|
1311
1298
|
#: one -- the viewer still opens.
|
|
1312
1299
|
self.data_nodes = []
|
|
1313
1300
|
self.node_errors = []
|
|
1314
|
-
#: What the install said, and which version it left behind.
|
|
1315
|
-
#: the
|
|
1316
|
-
#:
|
|
1301
|
+
#: What the install said, and which version it left behind. A copy of
|
|
1302
|
+
#: the primary's lines up to the marker -- the install rides the same
|
|
1303
|
+
#: ssh as the launch (see `install_prefixed`).
|
|
1317
1304
|
self.install_log = []
|
|
1318
1305
|
self.installed_version = None
|
|
1319
1306
|
|
|
@@ -1340,12 +1327,11 @@ class Session:
|
|
|
1340
1327
|
def log(self, count=40):
|
|
1341
1328
|
"""Every ssh's recent output, labelled, oldest first.
|
|
1342
1329
|
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
failed connection is most likely to be after.
|
|
1330
|
+
Install output needs no special handling any more: the install rides
|
|
1331
|
+
the primary ssh itself, so its lines are in that watcher's tail like
|
|
1332
|
+
everything else the connection printed.
|
|
1347
1333
|
"""
|
|
1348
|
-
lines = [
|
|
1334
|
+
lines = []
|
|
1349
1335
|
for watched in self.watchers:
|
|
1350
1336
|
lines += [f"[{watched.label}] {line}" for line in watched.tail(count)]
|
|
1351
1337
|
return lines
|
|
@@ -1463,32 +1449,45 @@ class Session:
|
|
|
1463
1449
|
matchers["node"] = parse_node_announce
|
|
1464
1450
|
|
|
1465
1451
|
try:
|
|
1466
|
-
#
|
|
1467
|
-
#
|
|
1468
|
-
#
|
|
1469
|
-
|
|
1470
|
-
|
|
1452
|
+
# The install rides the SAME ssh as the launch, chained ahead of
|
|
1453
|
+
# it -- one login instead of two, and at a Duo site one buzz of
|
|
1454
|
+
# the phone instead of two. `&&` is what keeps the promise the
|
|
1455
|
+
# separate-ssh version made: a failed install launches nothing.
|
|
1456
|
+
install_line = _begin_install(self) if self.install else None
|
|
1457
|
+
if install_line:
|
|
1458
|
+
matchers["installed"] = parse_install_done
|
|
1471
1459
|
|
|
1472
1460
|
local_node = None
|
|
1473
1461
|
if self.local_node:
|
|
1474
1462
|
local_node = self._start_local_node(local_node_serve_port)
|
|
1475
1463
|
|
|
1476
1464
|
if self.srun is None:
|
|
1465
|
+
line = (install_prefixed(install_line, launch)
|
|
1466
|
+
if install_line else launch)
|
|
1477
1467
|
self.primary = self._spawn(
|
|
1478
1468
|
direct_ssh_argv(self.target, self.local_port,
|
|
1479
|
-
self.remote_port,
|
|
1469
|
+
self.remote_port, line, jump=self.jump,
|
|
1480
1470
|
ssh_opts=self.ssh_opts,
|
|
1481
1471
|
forwards=forwards, reverse=reverse),
|
|
1482
1472
|
"ssh", matchers,
|
|
1483
1473
|
)
|
|
1474
|
+
if install_line:
|
|
1475
|
+
_await_install(self, self.primary, install_line)
|
|
1476
|
+
# The install spent none of the connection's own budget.
|
|
1477
|
+
deadline = _now() + self.timeout
|
|
1484
1478
|
self._phase("starting")
|
|
1485
1479
|
else:
|
|
1480
|
+
remote = srun_command_line(self.srun, launch)
|
|
1481
|
+
if install_line:
|
|
1482
|
+
remote = install_prefixed(install_line, remote)
|
|
1486
1483
|
self.primary = self._spawn(
|
|
1487
|
-
job_ssh_argv(self.target,
|
|
1488
|
-
srun_command_line(self.srun, launch),
|
|
1484
|
+
job_ssh_argv(self.target, remote,
|
|
1489
1485
|
jump=self.jump, ssh_opts=self.ssh_opts),
|
|
1490
1486
|
"job", matchers,
|
|
1491
1487
|
)
|
|
1488
|
+
if install_line:
|
|
1489
|
+
_await_install(self, self.primary, install_line)
|
|
1490
|
+
deadline = _now() + self.timeout
|
|
1492
1491
|
self._phase("waiting_for_job")
|
|
1493
1492
|
self.node, self.node_port = _wait_for_announce(
|
|
1494
1493
|
self.primary, deadline, echo=self.echo)
|
|
@@ -1830,7 +1829,7 @@ class NodeSession:
|
|
|
1830
1829
|
return bool(self.watchers) and all(w.alive for w in self.watchers)
|
|
1831
1830
|
|
|
1832
1831
|
def log(self, count=40):
|
|
1833
|
-
lines = [
|
|
1832
|
+
lines = []
|
|
1834
1833
|
for watched in self.watchers:
|
|
1835
1834
|
lines += [f"[{watched.label}] {line}" for line in watched.tail(count)]
|
|
1836
1835
|
return lines
|
|
@@ -1871,30 +1870,44 @@ class NodeSession:
|
|
|
1871
1870
|
host=bind)
|
|
1872
1871
|
matchers = {"node": parse_node_announce}
|
|
1873
1872
|
|
|
1874
|
-
#
|
|
1875
|
-
#
|
|
1876
|
-
#
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1873
|
+
# The install is chained ahead of the launch in the SAME ssh -- one
|
|
1874
|
+
# login, one Duo prompt -- and it has its own budget: `self.timeout`
|
|
1875
|
+
# is how long a node may take to answer, and an install that ate half
|
|
1876
|
+
# of it would leave a node that was starting normally being called
|
|
1877
|
+
# dead. The deadline is therefore taken after the marker.
|
|
1878
|
+
install_line = _begin_install(self) if self.install else None
|
|
1879
|
+
if install_line:
|
|
1880
|
+
matchers["installed"] = parse_install_done
|
|
1880
1881
|
|
|
1881
1882
|
if self.srun is None:
|
|
1882
|
-
|
|
1883
|
+
if not install_line:
|
|
1884
|
+
self._phase("tunneling")
|
|
1885
|
+
line = (install_prefixed(install_line, launch)
|
|
1886
|
+
if install_line else launch)
|
|
1883
1887
|
self.primary = self._spawn(
|
|
1884
1888
|
direct_ssh_argv(self.target, self.local_port, self.remote_port,
|
|
1885
|
-
|
|
1889
|
+
line, jump=self.jump, ssh_opts=self.ssh_opts),
|
|
1886
1890
|
"node", matchers)
|
|
1891
|
+
if install_line:
|
|
1892
|
+
_await_install(self, self.primary, install_line)
|
|
1893
|
+
self._phase("tunneling")
|
|
1887
1894
|
else:
|
|
1888
1895
|
# No forward on this one: at the moment it opens, the job has not
|
|
1889
1896
|
# been allocated and there is no host to point a forward at. The
|
|
1890
1897
|
# announce is what names it, and the tunnel follows.
|
|
1891
|
-
self.
|
|
1892
|
-
|
|
1893
|
-
|
|
1898
|
+
remote = srun_command_line(self.srun, launch)
|
|
1899
|
+
if install_line:
|
|
1900
|
+
remote = install_prefixed(install_line, remote)
|
|
1894
1901
|
self.primary = self._spawn(
|
|
1895
|
-
job_ssh_argv(self.target,
|
|
1902
|
+
job_ssh_argv(self.target, remote,
|
|
1896
1903
|
jump=self.jump, ssh_opts=self.ssh_opts),
|
|
1897
1904
|
"job", matchers)
|
|
1905
|
+
if install_line:
|
|
1906
|
+
_await_install(self, self.primary, install_line)
|
|
1907
|
+
self._phase("waiting_for_job")
|
|
1908
|
+
self.echo(" asking the scheduler for a node to serve the data "
|
|
1909
|
+
"from; this can wait in the queue.")
|
|
1910
|
+
deadline = _now() + self.timeout
|
|
1898
1911
|
|
|
1899
1912
|
announced = _wait_for_node(self.primary, deadline, echo=self.echo)
|
|
1900
1913
|
if not announced:
|
|
@@ -2133,21 +2146,25 @@ def connect_node(target, serve, *, name=None,
|
|
|
2133
2146
|
local, remote = pick_ports(local_port, remote_port)
|
|
2134
2147
|
name = name or f"{split_target(target)[1]}-node"
|
|
2135
2148
|
|
|
2136
|
-
if install:
|
|
2137
|
-
# A stand-in with the four attributes `_install_plexora` reads. This
|
|
2138
|
-
# function drives its ssh by hand rather than through a session object,
|
|
2139
|
-
# and giving it one just to install would be a bigger change than the
|
|
2140
|
-
# shim it would replace.
|
|
2141
|
-
_install_plexora(_BareInstall(target, remote_command, jump, ssh_opts,
|
|
2142
|
-
echo))
|
|
2143
|
-
|
|
2144
2149
|
launch = node_command_line(remote_command, remote, serve,
|
|
2145
2150
|
allow_origin=allow_origin, plugins=plugins)
|
|
2151
|
+
matchers = {"node": parse_node_announce}
|
|
2152
|
+
shim = install_line = None
|
|
2153
|
+
if install:
|
|
2154
|
+
# A stand-in with the attributes `_begin_install`/`_await_install`
|
|
2155
|
+
# read. This function drives its ssh by hand rather than through a
|
|
2156
|
+
# session object, and giving it one just to install would be a bigger
|
|
2157
|
+
# change than the shim it replaces.
|
|
2158
|
+
shim = _BareInstall(target, remote_command, jump, ssh_opts, echo)
|
|
2159
|
+
install_line = _begin_install(shim)
|
|
2160
|
+
launch = install_prefixed(install_line, launch)
|
|
2161
|
+
matchers["installed"] = parse_install_done
|
|
2146
2162
|
argv = direct_ssh_argv(target, local, remote, launch, jump=jump,
|
|
2147
2163
|
ssh_opts=ssh_opts)
|
|
2148
2164
|
echo(f"$ {' '.join(argv)}")
|
|
2149
|
-
watched = _Watched(argv, "node", echo=echo,
|
|
2150
|
-
|
|
2165
|
+
watched = _Watched(argv, "node", echo=echo, matchers=matchers)
|
|
2166
|
+
if install_line:
|
|
2167
|
+
_await_install(shim, watched, install_line)
|
|
2151
2168
|
_ACTIVE.append(watched)
|
|
2152
2169
|
deadline = _now() + timeout
|
|
2153
2170
|
|
|
@@ -361,6 +361,19 @@ def warm_resources(registry, log=print):
|
|
|
361
361
|
across a node restart -- so fitting them here would be twenty seconds of
|
|
362
362
|
work for an answer nobody is going to ask this node for.
|
|
363
363
|
|
|
364
|
+
**And deliberately not the window SCANS.** An earlier version computed
|
|
365
|
+
every channel's quantization window here, on the reasoning that a first
|
|
366
|
+
zoom should not pay for it. Each window is a full-resolution read of a
|
|
367
|
+
whole channel plane, and a node restored from its manifest can be serving
|
|
368
|
+
several whole slides -- so on a cluster filesystem that reasoning turned
|
|
369
|
+
into the node reading a hundred gigabytes, by itself, starting seconds
|
|
370
|
+
after it announced, and answering nothing -- not even /health -- while it
|
|
371
|
+
did. Watched live, twice, on two clusters: the panel said "Not answering"
|
|
372
|
+
about a machine that was busy doing work nobody had asked for yet. Windows
|
|
373
|
+
are seeded from the persistent store when the last job already paid for
|
|
374
|
+
them -- a JSON read -- and are otherwise computed on demand, one channel
|
|
375
|
+
at a time, when a request actually needs that channel.
|
|
376
|
+
|
|
364
377
|
Sequential, and on one background thread. The reads contend for the same
|
|
365
378
|
file, so racing them wins nothing, and the readers-writer lock means a real
|
|
366
379
|
request can interleave rather than queue behind the whole warm.
|
|
@@ -380,8 +393,12 @@ def warm_resources(registry, log=print):
|
|
|
380
393
|
continue
|
|
381
394
|
overview = resource.opened_overview
|
|
382
395
|
for index in range(len(overview) if overview is not None else 0):
|
|
396
|
+
stored = node_api._stored_window(resource, index)
|
|
397
|
+
if stored is None:
|
|
398
|
+
continue
|
|
383
399
|
with node_api._reading(resource):
|
|
384
|
-
node_api.
|
|
400
|
+
node_api._cached(resource, ("qwindow", index),
|
|
401
|
+
lambda window=stored: window)
|
|
385
402
|
except Exception as exc: # pragma: no cover - unreadable at warm time
|
|
386
403
|
# Never fatal. A resource that cannot be warmed is one that
|
|
387
404
|
# will report its own failure when something asks for it, and
|