arrayview 0.43.0__tar.gz → 0.43.1__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.
- {arrayview-0.43.0 → arrayview-0.43.1}/PKG-INFO +2 -2
- {arrayview-0.43.0 → arrayview-0.43.1}/pyproject.toml +1 -1
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_diagnostics.py +2 -1
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_routes_loading.py +23 -22
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_routes_rendering.py +16 -1
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_routes_vectorfield.py +20 -19
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_session.py +10 -1
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_source_safety.py +8 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_viewer.html +383 -55
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_vscode_extension.py +1 -1
- arrayview-0.43.1/src/arrayview/arrayview-opener.vsix +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/lifecycle_matrix.py +1 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_api.py +3 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_launch_diagnostics.py +4 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_mode_matrix.py +75 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_source_safety.py +5 -0
- arrayview-0.43.0/src/arrayview/arrayview-opener.vsix +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/.gitignore +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/LICENSE +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/README.md +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/ARCHITECTURE.md +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/__init__.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/__main__.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_analysis.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_app.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_config.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_dicom.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_diff.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_extra_ports.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_icon.png +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_imaging.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_instance_registry.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_io.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_launch_plan.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_launch_trace.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_launcher.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_lifecycle.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_overlays.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_platform.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_render.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_routes_analysis.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_routes_drop.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_routes_export.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_routes_persistence.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_routes_preferences.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_routes_preload.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_routes_query.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_routes_segmentation.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_routes_state.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_routes_websocket.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_segmentation.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_server.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_session_spec.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_shell.html +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_synthetic_mri.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_torch.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_tutorial.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_vectorfield.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_vscode.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_vscode_browser.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_vscode_signal.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/_watch.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/gsap.min.js +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/src/arrayview/todo.md +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/capture_patient_loading.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/capture_v_animation.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/conftest.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/make_vectorfield_test_arrays.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_1d_arrays.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_backend_shared.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_browser.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_cli.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_colorbar_hover_highlight.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_config.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_dicom.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_display_opener_results.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_doctor_commands.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_drop_import.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_instance_registry.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_invocation_contract_matrix.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_io.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_large_arrays.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_launch_blackbox.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_launch_execution_ownership.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_launch_plan.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_launch_trace.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_lifecycle_contract.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_loading_server.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_mode_consistency.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_mode_roundtrip.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_nifti_meta.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_nifti_series.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_preferences_ui.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_public_invocation_regressions.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_render_failure_reporting.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_render_progress.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_rgb_pixel_art.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_safe_server_stop.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_server_identity_status.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_session_spec.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_spawn_ownership.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_torch.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_tutorial.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_tutorial_browser.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_value_formatting.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_view_handle_lifecycle.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_view_launch_execution.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_vscode_ack_protocol.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/test_window_level_preview.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/ui_audit.py +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/v_anim_frames/.gitkeep +0 -0
- {arrayview-0.43.0 → arrayview-0.43.1}/tests/visual_smoke.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: arrayview
|
|
3
|
-
Version: 0.43.
|
|
3
|
+
Version: 0.43.1
|
|
4
4
|
Summary: Fast multi-dimensional array viewer
|
|
5
5
|
Project-URL: Home, https://github.com/oscarvanderheide/arrayview
|
|
6
6
|
Project-URL: Source, https://github.com/oscarvanderheide/arrayview
|
|
@@ -96,7 +96,8 @@ _DIAGNOSES: list[tuple[str, str, str, str]] = [
|
|
|
96
96
|
(r"did not start the viewer script", ACTION,
|
|
97
97
|
"VS Code opened an integrated-browser tab, but the tab did not navigate "
|
|
98
98
|
"to the ArrayView page.",
|
|
99
|
-
|
|
99
|
+
_RELOAD + " Retrying without reloading sends the next launch to the "
|
|
100
|
+
"same stuck browser."),
|
|
100
101
|
(r"did not render a frame|did not become ready",
|
|
101
102
|
ACTION,
|
|
102
103
|
"The viewer opened but showed no frame before the deadline — usually a "
|
|
@@ -137,32 +137,33 @@ def register_loading_routes(app, *, notify_shells, setup_rgb) -> None:
|
|
|
137
137
|
staging_dir = validated_staging_directory(abs_path, claimed_staging)
|
|
138
138
|
if claimed_staging and staging_dir is None:
|
|
139
139
|
raise HTTPException(status_code=400, detail="invalid source staging directory")
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
"error": (
|
|
144
|
-
f"Direct server access to network mount {unsafe_mount.mountpoint!r} "
|
|
145
|
-
"is refused. Launch the file through the arrayview CLI so it can "
|
|
146
|
-
"be staged safely."
|
|
147
|
-
)
|
|
148
|
-
}
|
|
149
|
-
nested_source_mount = network_mount_below(abs_path)
|
|
150
|
-
if nested_source_mount is not None:
|
|
151
|
-
return {
|
|
152
|
-
"error": (
|
|
153
|
-
f"Source discovery would enter network mount "
|
|
154
|
-
f"{nested_source_mount.mountpoint!r}; copy the collection locally first."
|
|
155
|
-
)
|
|
156
|
-
}
|
|
157
|
-
if source_may_require_sibling_discovery(abs_path):
|
|
158
|
-
nested_sibling_mount = network_mount_below(os.path.dirname(abs_path))
|
|
159
|
-
if nested_sibling_mount is not None:
|
|
140
|
+
if os.environ.get("ARRAYVIEW_SKIP_SOURCE_STAGING", "1") == "0":
|
|
141
|
+
unsafe_mount = direct_network_mount(abs_path)
|
|
142
|
+
if unsafe_mount is not None:
|
|
160
143
|
return {
|
|
161
144
|
"error": (
|
|
162
|
-
f"
|
|
163
|
-
|
|
145
|
+
f"Direct server access to network mount {unsafe_mount.mountpoint!r} "
|
|
146
|
+
"is refused. Launch the file through the arrayview CLI so it can "
|
|
147
|
+
"be staged safely."
|
|
164
148
|
)
|
|
165
149
|
}
|
|
150
|
+
nested_source_mount = network_mount_below(abs_path)
|
|
151
|
+
if nested_source_mount is not None:
|
|
152
|
+
return {
|
|
153
|
+
"error": (
|
|
154
|
+
f"Source discovery would enter network mount "
|
|
155
|
+
f"{nested_source_mount.mountpoint!r}; copy the collection locally first."
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
if source_may_require_sibling_discovery(abs_path):
|
|
159
|
+
nested_sibling_mount = network_mount_below(os.path.dirname(abs_path))
|
|
160
|
+
if nested_sibling_mount is not None:
|
|
161
|
+
return {
|
|
162
|
+
"error": (
|
|
163
|
+
f"Related-file discovery would enter network mount "
|
|
164
|
+
f"{nested_sibling_mount.mountpoint!r}; copy the series locally first."
|
|
165
|
+
)
|
|
166
|
+
}
|
|
166
167
|
for pattern in dir_patterns or []:
|
|
167
168
|
pattern_path = lexical_abspath(str(pattern))
|
|
168
169
|
scan_root = scan_root_before_magic(pattern_path)
|
|
@@ -23,7 +23,7 @@ from arrayview._render import (
|
|
|
23
23
|
render_rgb_rgba,
|
|
24
24
|
render_rgba,
|
|
25
25
|
)
|
|
26
|
-
from arrayview._session import HEAVY_OP_LIMIT_BYTES, SESSIONS
|
|
26
|
+
from arrayview._session import HEAVY_OP_LIMIT_BYTES, SESSIONS, _schedule_prefetch
|
|
27
27
|
from arrayview._synthetic_mri import (
|
|
28
28
|
_qmri_adjust_vmin_vmax,
|
|
29
29
|
qmri_display_slice,
|
|
@@ -276,6 +276,21 @@ def register_rendering_routes(app, *, get_session_or_404) -> None:
|
|
|
276
276
|
mosaic_cols,
|
|
277
277
|
)
|
|
278
278
|
render_ms = (time.perf_counter() - render_t0) * 1000.0
|
|
279
|
+
# This endpoint is what compare mode polls per scroll step (the live
|
|
280
|
+
# WS path has its own prefetch call). Without this, a second array
|
|
281
|
+
# open puts every pane on the request-then-wait path with nothing
|
|
282
|
+
# warmed ahead, which is a much bigger hit than the extra data alone.
|
|
283
|
+
# No per-request direction field here (stateless HTTP), so infer it
|
|
284
|
+
# from the index this session last rendered, mirroring the WS guard.
|
|
285
|
+
if slice_dim >= 0 and dim_z < 0:
|
|
286
|
+
prev_idx = getattr(session, "_http_prefetch_prev_idx", None)
|
|
287
|
+
direction = 1
|
|
288
|
+
if prev_idx is not None and len(prev_idx) == len(idx_tuple):
|
|
289
|
+
delta = idx_tuple[slice_dim] - prev_idx[slice_dim]
|
|
290
|
+
if delta:
|
|
291
|
+
direction = 1 if delta > 0 else -1
|
|
292
|
+
session._http_prefetch_prev_idx = idx_tuple
|
|
293
|
+
_schedule_prefetch(session, dim_x, dim_y, list(idx_tuple), slice_dim, direction)
|
|
279
294
|
encode_t0 = time.perf_counter()
|
|
280
295
|
img = _pil_image().fromarray(rgba[:, :, :3], mode="RGB")
|
|
281
296
|
buf = io.BytesIO()
|
|
@@ -79,26 +79,27 @@ def register_vectorfield_routes(app) -> None:
|
|
|
79
79
|
staging_dir = validated_staging_directory(filepath, claimed_staging)
|
|
80
80
|
if claimed_staging and staging_dir is None:
|
|
81
81
|
raise HTTPException(status_code=400, detail="invalid source staging directory")
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if nested_mount is not None:
|
|
96
|
-
return {
|
|
97
|
-
"error": (
|
|
98
|
-
f"Vector-field discovery would enter network mount "
|
|
99
|
-
f"{nested_mount.mountpoint!r}; copy it locally first."
|
|
82
|
+
if os.environ.get("ARRAYVIEW_SKIP_SOURCE_STAGING", "1") == "0":
|
|
83
|
+
unsafe_mount = direct_network_mount(filepath)
|
|
84
|
+
if unsafe_mount is not None:
|
|
85
|
+
return {
|
|
86
|
+
"error": (
|
|
87
|
+
f"Direct server access to network mount {unsafe_mount.mountpoint!r} "
|
|
88
|
+
"is refused; launch through the arrayview CLI."
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
nested_mount = network_mount_below(filepath)
|
|
92
|
+
if source_may_require_sibling_discovery(filepath):
|
|
93
|
+
nested_mount = nested_mount or network_mount_below(
|
|
94
|
+
os.path.dirname(filepath)
|
|
100
95
|
)
|
|
101
|
-
|
|
96
|
+
if nested_mount is not None:
|
|
97
|
+
return {
|
|
98
|
+
"error": (
|
|
99
|
+
f"Vector-field discovery would enter network mount "
|
|
100
|
+
f"{nested_mount.mountpoint!r}; copy it locally first."
|
|
101
|
+
)
|
|
102
|
+
}
|
|
102
103
|
components_dim = body.get("components_dim")
|
|
103
104
|
session = SESSIONS.get(sid)
|
|
104
105
|
if not session:
|
|
@@ -141,14 +141,22 @@ _OVERLAY_PREFETCH_POOL = None
|
|
|
141
141
|
_PREFETCH_LOCK = threading.Lock()
|
|
142
142
|
|
|
143
143
|
|
|
144
|
+
_PREFETCH_WORKERS: int = max(2, min(4, os.cpu_count() or 2))
|
|
145
|
+
|
|
146
|
+
|
|
144
147
|
def _get_prefetch_pool():
|
|
145
148
|
global _PREFETCH_POOL
|
|
146
149
|
with _PREFETCH_LOCK:
|
|
147
150
|
if _PREFETCH_POOL is None or _PREFETCH_POOL._shutdown:
|
|
148
151
|
import concurrent.futures
|
|
149
152
|
|
|
153
|
+
# One pool is shared by every open session (see _schedule_prefetch).
|
|
154
|
+
# A single worker meant a second open array serialized behind the
|
|
155
|
+
# first array's lookahead, so scrolling any pane lagged. Size like
|
|
156
|
+
# _RENDER_WORKERS so a handful of concurrently open arrays each get
|
|
157
|
+
# a lane.
|
|
150
158
|
_PREFETCH_POOL = concurrent.futures.ThreadPoolExecutor(
|
|
151
|
-
max_workers=
|
|
159
|
+
max_workers=_PREFETCH_WORKERS, thread_name_prefix="arrayview-prefetch"
|
|
152
160
|
)
|
|
153
161
|
return _PREFETCH_POOL
|
|
154
162
|
|
|
@@ -674,6 +682,7 @@ __all__ = [
|
|
|
674
682
|
"_render",
|
|
675
683
|
# Prefetch
|
|
676
684
|
"_PREFETCH_POOL",
|
|
685
|
+
"_PREFETCH_WORKERS",
|
|
677
686
|
"_OVERLAY_PREFETCH_POOL",
|
|
678
687
|
"_PREFETCH_LOCK",
|
|
679
688
|
"_get_prefetch_pool",
|
|
@@ -713,6 +713,14 @@ def prepare_source(path: str, *, timeout: float | None = None) -> PreparedSource
|
|
|
713
713
|
"""Preflight and, for a network file, make a bounded local snapshot."""
|
|
714
714
|
absolute = lexical_abspath(path)
|
|
715
715
|
mount = mount_for_path(absolute)
|
|
716
|
+
if os.environ.get("ARRAYVIEW_SKIP_SOURCE_STAGING", "1") != "0":
|
|
717
|
+
# Network-source staging is opt-in, not the default: use the source
|
|
718
|
+
# path directly, exactly like a local file. This is the pre-staging
|
|
719
|
+
# behavior (fast open, but a dropped network mount while the file is
|
|
720
|
+
# memory-mapped can wedge the process in a way not even `kill -9`
|
|
721
|
+
# recovers from — see docs/loading.md). Set
|
|
722
|
+
# ARRAYVIEW_SKIP_SOURCE_STAGING=0 to restore the safety copy.
|
|
723
|
+
return PreparedSource(absolute, absolute, None, mount)
|
|
716
724
|
if mount is None or mount.filesystem not in _NETWORK_FILESYSTEMS:
|
|
717
725
|
unsafe_discovery = discovery_network_mount(absolute)
|
|
718
726
|
if unsafe_discovery is not None:
|