arrayview 0.34.0__tar.gz → 0.35.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.
Files changed (106) hide show
  1. {arrayview-0.34.0 → arrayview-0.35.1}/PKG-INFO +1 -1
  2. {arrayview-0.34.0 → arrayview-0.35.1}/pyproject.toml +1 -1
  3. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_launcher.py +48 -36
  4. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_platform.py +8 -5
  5. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_viewer.html +47 -12
  6. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_vscode_extension.py +1 -1
  7. arrayview-0.35.1/src/arrayview/arrayview-opener.vsix +0 -0
  8. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_launch_plan.py +1 -1
  9. arrayview-0.34.0/src/arrayview/arrayview-opener.vsix +0 -0
  10. {arrayview-0.34.0 → arrayview-0.35.1}/.gitignore +0 -0
  11. {arrayview-0.34.0 → arrayview-0.35.1}/LICENSE +0 -0
  12. {arrayview-0.34.0 → arrayview-0.35.1}/README.md +0 -0
  13. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/ARCHITECTURE.md +0 -0
  14. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/__init__.py +0 -0
  15. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/__main__.py +0 -0
  16. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_analysis.py +0 -0
  17. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_app.py +0 -0
  18. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_codex_open.py +0 -0
  19. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_config.py +0 -0
  20. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_dicom.py +0 -0
  21. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_diff.py +0 -0
  22. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_icon.png +0 -0
  23. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_imaging.py +0 -0
  24. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_instance_registry.py +0 -0
  25. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_io.py +0 -0
  26. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_launch_plan.py +0 -0
  27. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_launch_trace.py +0 -0
  28. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_lifecycle.py +0 -0
  29. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_overlays.py +0 -0
  30. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_render.py +0 -0
  31. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_routes_analysis.py +0 -0
  32. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_routes_drop.py +0 -0
  33. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_routes_export.py +0 -0
  34. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_routes_loading.py +0 -0
  35. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_routes_persistence.py +0 -0
  36. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_routes_preferences.py +0 -0
  37. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_routes_preload.py +0 -0
  38. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_routes_query.py +0 -0
  39. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_routes_rendering.py +0 -0
  40. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_routes_segmentation.py +0 -0
  41. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_routes_state.py +0 -0
  42. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_routes_vectorfield.py +0 -0
  43. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_routes_websocket.py +0 -0
  44. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_segmentation.py +0 -0
  45. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_server.py +0 -0
  46. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_session.py +0 -0
  47. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_session_spec.py +0 -0
  48. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_shell.html +0 -0
  49. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_synthetic_mri.py +0 -0
  50. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_torch.py +0 -0
  51. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_vectorfield.py +0 -0
  52. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_vscode.py +0 -0
  53. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_vscode_browser.py +0 -0
  54. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/_vscode_signal.py +0 -0
  55. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/gsap.min.js +0 -0
  56. {arrayview-0.34.0 → arrayview-0.35.1}/src/arrayview/todo.md +0 -0
  57. {arrayview-0.34.0 → arrayview-0.35.1}/tests/capture_patient_loading.py +0 -0
  58. {arrayview-0.34.0 → arrayview-0.35.1}/tests/capture_v_animation.py +0 -0
  59. {arrayview-0.34.0 → arrayview-0.35.1}/tests/conftest.py +0 -0
  60. {arrayview-0.34.0 → arrayview-0.35.1}/tests/lifecycle_matrix.py +0 -0
  61. {arrayview-0.34.0 → arrayview-0.35.1}/tests/make_vectorfield_test_arrays.py +0 -0
  62. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_api.py +0 -0
  63. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_backend_shared.py +0 -0
  64. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_browser.py +0 -0
  65. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_cli.py +0 -0
  66. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_codex_open.py +0 -0
  67. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_colorbar_hover_highlight.py +0 -0
  68. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_command_reachability.py +0 -0
  69. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_config.py +0 -0
  70. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_cross_mode_parametrized.py +0 -0
  71. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_dicom.py +0 -0
  72. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_display_opener_results.py +0 -0
  73. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_doctor_commands.py +0 -0
  74. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_drop_import.py +0 -0
  75. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_instance_registry.py +0 -0
  76. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_interactions.py +0 -0
  77. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_invocation_contract_matrix.py +0 -0
  78. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_io.py +0 -0
  79. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_large_arrays.py +0 -0
  80. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_launch_blackbox.py +0 -0
  81. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_launch_diagnostics.py +0 -0
  82. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_launch_execution_ownership.py +0 -0
  83. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_launch_trace.py +0 -0
  84. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_lifecycle_contract.py +0 -0
  85. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_loading_server.py +0 -0
  86. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_mode_consistency.py +0 -0
  87. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_mode_matrix.py +0 -0
  88. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_mode_roundtrip.py +0 -0
  89. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_nifti_meta.py +0 -0
  90. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_nifti_series.py +0 -0
  91. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_preferences_ui.py +0 -0
  92. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_public_invocation_regressions.py +0 -0
  93. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_rgb_pixel_art.py +0 -0
  94. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_safe_server_stop.py +0 -0
  95. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_server_identity_status.py +0 -0
  96. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_session_spec.py +0 -0
  97. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_spawn_ownership.py +0 -0
  98. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_torch.py +0 -0
  99. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_view_component_integration.py +0 -0
  100. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_view_component_unit.py +0 -0
  101. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_view_handle_lifecycle.py +0 -0
  102. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_view_launch_execution.py +0 -0
  103. {arrayview-0.34.0 → arrayview-0.35.1}/tests/test_vscode_ack_protocol.py +0 -0
  104. {arrayview-0.34.0 → arrayview-0.35.1}/tests/ui_audit.py +0 -0
  105. {arrayview-0.34.0 → arrayview-0.35.1}/tests/v_anim_frames/.gitkeep +0 -0
  106. {arrayview-0.34.0 → arrayview-0.35.1}/tests/visual_smoke.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arrayview
3
- Version: 0.34.0
3
+ Version: 0.35.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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "arrayview"
7
- version = "0.34.0"
7
+ version = "0.35.1"
8
8
  description = "Fast multi-dimensional array viewer"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.12"
@@ -5031,6 +5031,11 @@ def arrayview():
5031
5031
  action="store_true",
5032
5032
  help="Enable verbose output (internal status messages)",
5033
5033
  )
5034
+ parser.add_argument(
5035
+ "--trace",
5036
+ action="store_true",
5037
+ help="Show full tracebacks instead of user-friendly errors",
5038
+ )
5034
5039
  parser.add_argument(
5035
5040
  "--diagnose",
5036
5041
  action="store_true",
@@ -5695,24 +5700,55 @@ def arrayview():
5695
5700
  "[ArrayView] --window native is not supported on remote tunnel; using vscode instead."
5696
5701
  )
5697
5702
 
5698
- if execution_registration is Registration.HTTP_LOAD:
5699
- _handle_cli_existing_server(
5703
+ try:
5704
+ if execution_registration is Registration.HTTP_LOAD:
5705
+ _handle_cli_existing_server(
5706
+ port=args.port,
5707
+ base_file=base_file,
5708
+ name=name,
5709
+ compare_files=compare_files,
5710
+ overlay_files=[] if args.stack_mode else file_overlay_paths,
5711
+ overlay_names=[] if args.stack_mode else file_overlay_names,
5712
+ rgb=args.rgb,
5713
+ vectorfield=args.vectorfield,
5714
+ vfield_components_dim=vfield_components_dim,
5715
+ use_native_shell=use_native_shell,
5716
+ dims_override=dims_override,
5717
+ watch=getattr(args, "watch", False),
5718
+ window_mode=window_mode,
5719
+ floating=args.floating,
5720
+ is_remote=is_remote,
5721
+ launch_context=launch_context,
5722
+ dir_patterns=dir_patterns,
5723
+ dir_overlay_specs=dir_overlay_specs,
5724
+ dir_case_regex=args.case_regex,
5725
+ dir_exclude_cases=sorted(excluded_cases),
5726
+ collection_load=args.load,
5727
+ collection_stack=args.stack_policy or "auto",
5728
+ )
5729
+ return
5730
+
5731
+ demo_name = getattr(args, "_demo_name", None)
5732
+ demo_cleanup = getattr(args, "_demo_cleanup", False)
5733
+ _handle_cli_spawned_daemon(
5700
5734
  port=args.port,
5701
5735
  base_file=base_file,
5702
5736
  name=name,
5703
5737
  compare_files=compare_files,
5704
5738
  overlay_files=[] if args.stack_mode else file_overlay_paths,
5705
5739
  overlay_names=[] if args.stack_mode else file_overlay_names,
5706
- rgb=args.rgb,
5707
- vectorfield=args.vectorfield,
5708
- vfield_components_dim=vfield_components_dim,
5709
- use_native_shell=use_native_shell,
5710
5740
  dims_override=dims_override,
5741
+ use_native_shell=use_native_shell,
5711
5742
  watch=getattr(args, "watch", False),
5712
5743
  window_mode=window_mode,
5713
5744
  floating=args.floating,
5714
5745
  is_remote=is_remote,
5715
5746
  launch_context=launch_context,
5747
+ vectorfield=args.vectorfield,
5748
+ vfield_components_dim=vfield_components_dim,
5749
+ rgb=args.rgb,
5750
+ demo_name=demo_name,
5751
+ demo_cleanup=demo_cleanup,
5716
5752
  dir_patterns=dir_patterns,
5717
5753
  dir_overlay_specs=dir_overlay_specs,
5718
5754
  dir_case_regex=args.case_regex,
@@ -5720,33 +5756,9 @@ def arrayview():
5720
5756
  collection_load=args.load,
5721
5757
  collection_stack=args.stack_policy or "auto",
5722
5758
  )
5723
- return
5724
-
5725
- demo_name = getattr(args, "_demo_name", None)
5726
- demo_cleanup = getattr(args, "_demo_cleanup", False)
5727
- _handle_cli_spawned_daemon(
5728
- port=args.port,
5729
- base_file=base_file,
5730
- name=name,
5731
- compare_files=compare_files,
5732
- overlay_files=[] if args.stack_mode else file_overlay_paths,
5733
- overlay_names=[] if args.stack_mode else file_overlay_names,
5734
- dims_override=dims_override,
5735
- use_native_shell=use_native_shell,
5736
- watch=getattr(args, "watch", False),
5737
- window_mode=window_mode,
5738
- floating=args.floating,
5739
- is_remote=is_remote,
5740
- launch_context=launch_context,
5741
- vectorfield=args.vectorfield,
5742
- vfield_components_dim=vfield_components_dim,
5743
- rgb=args.rgb,
5744
- demo_name=demo_name,
5745
- demo_cleanup=demo_cleanup,
5746
- dir_patterns=dir_patterns,
5747
- dir_overlay_specs=dir_overlay_specs,
5748
- dir_case_regex=args.case_regex,
5749
- dir_exclude_cases=sorted(excluded_cases),
5750
- collection_load=args.load,
5751
- collection_stack=args.stack_policy or "auto",
5752
- )
5759
+ except Exception as e:
5760
+ if args.trace or os.environ.get("ARRAYVIEW_TRACE"):
5761
+ raise
5762
+ msg = str(e).removeprefix("[ArrayView] ").rstrip()
5763
+ print(f"\033[1;31m[ArrayView] {msg}\033[0m", file=sys.stderr)
5764
+ sys.exit(1)
@@ -479,13 +479,16 @@ def _exact_vscode_window_registration(
479
479
  return None
480
480
  return window_id, registration
481
481
 
482
+ registration = None
482
483
  if ipc:
483
484
  window_id = hashlib.sha256(ipc.encode()).hexdigest()[:16]
484
- if registration := _read(window_id):
485
- return registration
486
- elif window_id := os.environ.get("ARRAYVIEW_WINDOW_ID"):
487
- if registration := _read(window_id):
488
- return registration
485
+ if reg := _read(window_id):
486
+ return reg
487
+ registration = reg
488
+ if registration is None:
489
+ window_id = os.environ.get("ARRAYVIEW_WINDOW_ID")
490
+ if window_id and (reg := _read(window_id)):
491
+ return reg
489
492
 
490
493
  exact_cli = _current_vscode_remote_cli()
491
494
  if exact_cli is None:
@@ -24285,7 +24285,7 @@
24285
24285
  title: 'Color & Range',
24286
24286
  entries: [
24287
24287
  { key: 'c', action: 'Open colormap picker / cycle', hint: 'opens the swatch island · repeated taps cycle browse selection · Enter accepts · Esc cancels' },
24288
- { key: 'd', action: 'Open histogram / range menu', hint: 'repeat d to cycle presets · arrows select · Enter commits and closes · click dims to cycle scope' },
24288
+ { key: 'd', action: 'Open histogram / range menu', hint: 'repeat d to cycle presets · j/k to select · arrows/Enter to close · click dims to cycle scope' },
24289
24289
  { key: 'Histogram open', action: 'Dim participation underlines', hint: 'Click a dim to cycle in scope → recompute → frozen. Hover a dim for the explanation.' },
24290
24290
  { key: 'L', action: 'Toggle log scale', hint: 'when values span several orders of magnitude' },
24291
24291
  { key: 'Shift+4', action: 'Toggle automatic display crop', hint: 'nonzero bounds plus a 12 mm margin · view-only, per collection item' },
@@ -35402,17 +35402,47 @@
35402
35402
 
35403
35403
  function _collapseHistogramImmediately() {
35404
35404
  if (_histAutoDismissTimer) { clearTimeout(_histAutoDismissTimer); _histAutoDismissTimer = null; }
35405
- if (!primaryCb || !primaryCb._expanded) return;
35405
+ _histAutoDismissTimer = null;
35406
35406
  lebesgueMode = false;
35407
- primaryCb._expanded = false;
35408
- primaryCb._manualExpand = false;
35409
- renderInfo();
35410
35407
  _clearLebesgueOverlay();
35411
- const tz = document.getElementById('slim-cb-tri-zone');
35412
- if (tz) tz.classList.remove('expanded');
35413
- primaryCb.startAnim(false);
35414
- if (multiViewActive || compareMvActive) drawMvColorbar();
35415
- else drawSlimColorbar(cbMarkerFrac);
35408
+ renderInfo();
35409
+
35410
+ if (primaryCb && primaryCb._expanded) {
35411
+ primaryCb._expanded = false;
35412
+ primaryCb._manualExpand = false;
35413
+ const tz = document.getElementById('slim-cb-tri-zone');
35414
+ if (tz) tz.classList.remove('expanded');
35415
+ primaryCb.startAnim(false);
35416
+ if (multiViewActive || compareMvActive) drawMvColorbar();
35417
+ else drawSlimColorbar(cbMarkerFrac);
35418
+ }
35419
+
35420
+ if (typeof qmriViews !== 'undefined' && qmriViews) {
35421
+ for (const view of qmriViews) {
35422
+ if (view._colorBar && view._colorBar._expanded) {
35423
+ view._colorBar._expanded = false;
35424
+ view._colorBar._manualExpand = false;
35425
+ if (view._colorBar.triZone) view._colorBar.triZone.classList.remove('expanded');
35426
+ view._colorBar.startAnim(false);
35427
+ view._colorBar.draw();
35428
+ }
35429
+ if (view._mosaicColorBar && view._mosaicColorBar._expanded) {
35430
+ view._mosaicColorBar._expanded = false;
35431
+ view._mosaicColorBar._manualExpand = false;
35432
+ if (view._mosaicColorBar.triZone) view._mosaicColorBar.triZone.classList.remove('expanded');
35433
+ view._mosaicColorBar.startAnim(false);
35434
+ view._mosaicColorBar.draw();
35435
+ }
35436
+ }
35437
+ }
35438
+
35439
+ if (_diffCenterCb && _diffCenterCb._expanded) {
35440
+ _diffCenterCb._expanded = false;
35441
+ _diffCenterCb._manualExpand = false;
35442
+ if (_diffCenterCb.triZone) _diffCenterCb.triZone.classList.remove('expanded');
35443
+ _diffCenterCb.startAnim(false);
35444
+ _diffCenterCb.draw();
35445
+ }
35416
35446
  }
35417
35447
 
35418
35448
  /** Cycle the state of dim `d` and re-apply the histogram + preset.
@@ -35475,13 +35505,18 @@
35475
35505
  _dmenuCyclePresetFromKey();
35476
35506
  return true;
35477
35507
  }
35478
- if (e.key === 'ArrowDown' || e.key === 'j') {
35508
+ if (e.key === 'ArrowLeft' || e.key === 'ArrowRight' ||
35509
+ e.key === 'ArrowUp' || e.key === 'ArrowDown') {
35510
+ _histPickerClose();
35511
+ return false;
35512
+ }
35513
+ if (e.key === 'j') {
35479
35514
  e.preventDefault();
35480
35515
  e.stopPropagation();
35481
35516
  _dmenuMoveSelection(1);
35482
35517
  return true;
35483
35518
  }
35484
- if (e.key === 'ArrowUp' || e.key === 'k') {
35519
+ if (e.key === 'k') {
35485
35520
  e.preventDefault();
35486
35521
  e.stopPropagation();
35487
35522
  _dmenuMoveSelection(-1);
@@ -54,7 +54,7 @@ _VSCODE_EXT_INSTALLED = False # cached so we only check once per process
54
54
  _VSCODE_EXT_FRESH_INSTALL = False # True if we just installed it this session
55
55
  _VSCODE_EXT_RELOAD_REQUIRED = False # installed files are newer than the live host
56
56
  _VSCODE_EXT_INSTALL_FAILED = False # automatic install could not complete safely
57
- _VSCODE_EXT_VERSION = "0.14.73" # current bundled extension version
57
+ _VSCODE_EXT_VERSION = "0.14.75" # current bundled extension version
58
58
  _VSCODE_CONFIGURED_PORTS: set[int] = set()
59
59
 
60
60
  def _bundled_vscode_vsix_version(vsix_path: str) -> str | None:
@@ -673,7 +673,7 @@ def test_existing_server_is_reused_through_http():
673
673
 
674
674
  @pytest.mark.parametrize(
675
675
  "server_uid",
676
- [None, os.geteuid() + 1],
676
+ [None, os.geteuid() + 1 if hasattr(os, "geteuid") else -1],
677
677
  ids=["missing-uid", "different-uid"],
678
678
  )
679
679
  def test_unowned_existing_server_uses_new_port(server_uid):
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes