Skip to content

Commit 3c838f1

Browse files
authored
Merge pull request #3 from OpenHCSDev/codex/typed-viewer-display-contract
Unify typed viewer display projection
2 parents eb72df4 + f5f7b13 commit 3c838f1

10 files changed

Lines changed: 127 additions & 171 deletions

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "polystore"
7-
version = "0.1.24"
7+
version = "0.1.25"
88
description = "Framework-agnostic multi-backend storage abstraction for ML and scientific computing"
99
readme = "README.md"
1010
requires-python = ">=3.11"

src/polystore/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Polystore package exports.
33
"""
44

5-
__version__ = "0.1.24"
5+
__version__ = "0.1.25"
66

77
from .atomic import (
88
FileLockError,

src/polystore/fiji_stream.py

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,25 @@
1414
import logging
1515
from enum import Enum
1616

17+
from zmqruntime.viewer_protocol import (
18+
ViewerBatchItemWireField,
19+
ViewerBatchWireField,
20+
ViewerWireMapping,
21+
ViewerWireValue,
22+
)
23+
1724
from .constants import Backend
18-
from .streaming_constants import StreamingDataType
25+
from .roi_converters import FijiROIConverter
1926
from .streaming import (
2027
FilePath,
2128
RoiStreamPayload,
22-
StreamingBuiltBatch,
2329
StreamingBackend,
30+
StreamingBuiltBatch,
2431
StreamingComponentNamesRequest,
2532
StreamingItemPreparationRequest,
26-
ViewerDisplayPayloadExtra,
2733
)
2834
from .streaming.viewer_transport import ViewerStreamItemPayload, ViewerStreamRequest
29-
from .roi_converters import FijiROIConverter
30-
from zmqruntime.viewer_protocol import (
31-
ViewerBatchItemWireField,
32-
ViewerBatchWireField,
33-
ViewerWireMapping,
34-
ViewerWireValue,
35-
)
35+
from .streaming_constants import StreamingDataType
3636

3737
logger = logging.getLogger(__name__)
3838

@@ -44,23 +44,6 @@ class FijiDisplayWireField(str, Enum):
4444
AUTO_CONTRAST = "auto_contrast"
4545

4646

47-
class FijiDisplayPayload:
48-
"""Display payload projection for Fiji stream messages."""
49-
50-
@staticmethod
51-
def auto_contrast_value(display_config) -> bool:
52-
return display_config.auto_contrast
53-
54-
@classmethod
55-
def from_display_config(cls, display_config) -> dict[str, ViewerWireValue]:
56-
return {
57-
FijiDisplayWireField.LUT.value: display_config.get_lut_name(),
58-
FijiDisplayWireField.AUTO_CONTRAST.value: cls.auto_contrast_value(
59-
display_config
60-
),
61-
}
62-
63-
6447
class FijiMessageMetadata:
6548
"""Typed access to optional Fiji message metadata."""
6649

@@ -86,14 +69,6 @@ class FijiStreamingBackend(StreamingBackend):
8669
VIEWER_TYPE = 'fiji'
8770
SHM_PREFIX = 'fiji_'
8871

89-
def display_payload_extra(
90-
self,
91-
stream_request: ViewerStreamRequest,
92-
) -> ViewerDisplayPayloadExtra:
93-
return ViewerDisplayPayloadExtra.from_mapping(
94-
FijiDisplayPayload.from_display_config(stream_request.display_config)
95-
)
96-
9772
def message_extra(
9873
self,
9974
stream_request: ViewerStreamRequest,

src/polystore/napari_stream.py

Lines changed: 7 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,20 @@
1515
import logging
1616
from enum import Enum
1717

18+
from zmqruntime.viewer_protocol import (
19+
ViewerBatchItemWireField,
20+
ViewerWireValue,
21+
)
22+
1823
from .constants import Backend
24+
from .roi_converters import NapariROIConverter
1925
from .streaming import (
2026
FilePath,
2127
RoiStreamPayload,
2228
StreamingBackend,
2329
StreamingItemPreparationRequest,
24-
ViewerDisplayPayloadExtra,
25-
)
26-
from .streaming.viewer_transport import ViewerStreamItemPayload, ViewerStreamRequest
27-
from .roi_converters import NapariROIConverter
28-
from zmqruntime.viewer_protocol import (
29-
ViewerBatchItemWireField,
30-
ViewerWireMapping,
31-
ViewerWireValue,
3230
)
31+
from .streaming.viewer_transport import ViewerStreamItemPayload
3332

3433
logger = logging.getLogger(__name__)
3534

@@ -41,41 +40,13 @@ class NapariDisplayWireField(str, Enum):
4140
VARIABLE_SIZE_HANDLING = "variable_size_handling"
4241

4342

44-
class NapariDisplayPayload:
45-
"""Display payload projection for Napari stream messages."""
46-
47-
@staticmethod
48-
def variable_size_handling_value(display_config):
49-
variable_size_handling = display_config.variable_size_handling
50-
if variable_size_handling is None:
51-
return None
52-
return variable_size_handling.value
53-
54-
@classmethod
55-
def from_display_config(cls, display_config) -> dict[str, ViewerWireValue]:
56-
return {
57-
NapariDisplayWireField.COLORMAP.value: display_config.get_colormap_name(),
58-
NapariDisplayWireField.VARIABLE_SIZE_HANDLING.value: (
59-
cls.variable_size_handling_value(display_config)
60-
),
61-
}
62-
63-
6443
class NapariStreamingBackend(StreamingBackend):
6544
"""Napari streaming backend with automatic registration."""
6645
_backend_type = Backend.NAPARI_STREAM.value
6746

6847
VIEWER_TYPE = 'napari'
6948
SHM_PREFIX = 'napari_'
7049

71-
def display_payload_extra(
72-
self,
73-
stream_request: ViewerStreamRequest,
74-
) -> ViewerDisplayPayloadExtra:
75-
return ViewerDisplayPayloadExtra.from_mapping(
76-
NapariDisplayPayload.from_display_config(stream_request.display_config)
77-
)
78-
7950
def _prepare_shapes_data(
8051
self,
8152
data: RoiStreamPayload,

src/polystore/streaming/_streaming_backend.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,38 +17,39 @@
1717
from pathlib import Path
1818
from types import MappingProxyType
1919
from typing import TypeAlias
20+
2021
import numpy as np
2122
import zmq
2223
from arraybridge import convert_memory, detect_memory_type
2324
from arraybridge.types import MemoryType as ArrayBridgeMemoryType
25+
from zmqruntime.ack_listener import GlobalAckListener
26+
from zmqruntime.config import ZMQConfig
27+
from zmqruntime.viewer_protocol import (
28+
ViewerBatchItemWireField,
29+
ViewerBatchMessagePayload,
30+
ViewerComponentMetadataPayload,
31+
ViewerDisplayConfigWireField,
32+
ViewerTransportEndpoint,
33+
ViewerWireMapping,
34+
ViewerWirePayload,
35+
ViewerWireValue,
36+
)
2437

2538
from ..base import DataSink
2639
from ..formats import PIXEL_PAYLOAD_EXTENSIONS
27-
from ..streaming_constants import StreamingDataType
2840
from ..roi import ROI, ROI_ZIP_EXTENSION
2941
from ..roi_converters import ROIShapeNapariPayloadConverter
42+
from ..streaming_constants import StreamingDataType
3043
from ..zmq_config import POLYSTORE_ZMQ_CONFIG
3144
from .viewer_transport import (
3245
ViewerMicroscopeHandlerABC,
46+
ViewerStreamBackendKwargs,
3347
ViewerStreamBatchItemInput,
3448
ViewerStreamBatchItemSource,
35-
ViewerStreamBackendKwargs,
3649
ViewerStreamItemPayload,
3750
ViewerStreamRequest,
3851
ViewerTransportDefaults,
3952
)
40-
from zmqruntime.ack_listener import GlobalAckListener
41-
from zmqruntime.config import ZMQConfig
42-
from zmqruntime.viewer_protocol import (
43-
ViewerBatchItemWireField,
44-
ViewerBatchMessagePayload,
45-
ViewerComponentMetadataPayload,
46-
ViewerDisplayConfigWireField,
47-
ViewerTransportEndpoint,
48-
ViewerWirePayload,
49-
ViewerWireMapping,
50-
ViewerWireValue,
51-
)
5253

5354
logger = logging.getLogger(__name__)
5455

@@ -86,9 +87,6 @@ def to_wire_mapping(self) -> dict[str, ViewerWireValue]:
8687
)
8788

8889

89-
EMPTY_DISPLAY_PAYLOAD_EXTRA = ViewerDisplayPayloadExtra()
90-
91-
9290
@dataclass(frozen=True)
9391
class StreamingComponentDomainValue:
9492
"""Viewer component value normalized for a batch-level domain."""
@@ -692,7 +690,9 @@ def display_payload_extra(
692690
self,
693691
stream_request: ViewerStreamRequest,
694692
) -> ViewerDisplayPayloadExtra:
695-
return EMPTY_DISPLAY_PAYLOAD_EXTRA
693+
return ViewerDisplayPayloadExtra.from_mapping(
694+
stream_request.display_config.display_payload_extra()
695+
)
696696

697697
def message_extra(
698698
self,

src/polystore/streaming/receivers/fiji/fiji_batch_processor.py

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import logging
2-
from typing import Any, Dict, List, Optional
2+
from typing import Any
33

44
from polystore.streaming.receivers.core import DebouncedBatchEngine
55

@@ -9,59 +9,52 @@
99
class FijiBatchProcessor:
1010
"""
1111
Batch processor for Fiji viewer with configurable batching strategies.
12-
13-
Accumulates items and builds hyperstacks based on batch_size configuration:
14-
- None: Wait for all items in operation, then build hyperstack once
15-
- N: Rebuild hyperstack every N items incrementally
16-
12+
1713
Uses debouncing to collect items arriving in rapid succession.
1814
"""
19-
15+
2016
def __init__(
2117
self,
2218
fiji_server,
23-
batch_size: Optional[int] = None,
2419
debounce_delay_ms: int = 500,
2520
max_debounce_wait_ms: int = 2000,
2621
):
2722
"""
2823
Initialize batch processor.
29-
24+
3025
Args:
3126
fiji_server: Reference to FijiViewerServer for display operations
32-
batch_size: Number of items to batch before displaying
33-
None = wait for all (default), N = display every N items
3427
debounce_delay_ms: Wait time after last item before processing (ms)
3528
max_debounce_wait_ms: Maximum total wait time before forcing display (ms)
3629
"""
3730
self.fiji_server = fiji_server
38-
self.batch_size = batch_size
3931
self.debounce_delay_ms = debounce_delay_ms
4032
self.max_debounce_wait_ms = max_debounce_wait_ms
41-
33+
4234
self._engine = DebouncedBatchEngine(
4335
process_fn=self._process_batch,
4436
debounce_delay_ms=debounce_delay_ms,
4537
max_debounce_wait_ms=max_debounce_wait_ms,
4638
)
47-
39+
4840
logger.info(
49-
f"FijiBatchProcessor: Created with batch_size={batch_size}, "
50-
f"debounce={debounce_delay_ms}ms, max_wait={max_debounce_wait_ms}ms"
41+
"FijiBatchProcessor: Created with debounce=%sms, max_wait=%sms",
42+
debounce_delay_ms,
43+
max_debounce_wait_ms,
5144
)
52-
45+
5346
def add_items(
5447
self,
5548
window_key: str,
56-
items: List[Dict[str, Any]],
57-
display_config: Dict[str, Any],
49+
items: list[dict[str, Any]],
50+
display_config: dict[str, Any],
5851
images_dir: str,
59-
component_names_metadata: Dict[str, Any],
60-
component_value_domain: Dict[str, Any],
52+
component_names_metadata: dict[str, Any],
53+
component_value_domain: dict[str, Any],
6154
):
6255
"""
6356
Add items to the batch for processing.
64-
57+
6558
Args:
6659
window_key: Unique identifier for the Fiji window
6760
items: List of items to add (images)
@@ -88,7 +81,7 @@ def flush(self) -> None:
8881
"""Force immediate processing of the pending batch."""
8982
self._engine.flush()
9083

91-
def _process_batch(self, items: List[Dict[str, Any]], context: Dict[str, Any]) -> None:
84+
def _process_batch(self, items: list[dict[str, Any]], context: dict[str, Any]) -> None:
9285
"""Process callback used by shared debounced batch engine."""
9386
display_config = context["display_config"]
9487
images_dir = context["images_dir"]

src/polystore/streaming/receivers/napari/napari_batch_processor.py

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -42,31 +42,15 @@ class NapariBatchProcessor:
4242
adapts batch payloads into the server display operation.
4343
"""
4444

45-
def __init__(
46-
self,
47-
napari_server,
48-
batch_size: int | None = None,
49-
debounce_delay_ms: int = 1000,
50-
max_debounce_wait_ms: int = 5000,
51-
):
45+
def __init__(self, napari_server):
5246
"""
5347
Initialize batch processor.
5448
5549
Args:
5650
napari_server: Reference to NapariViewerServer for display operations
57-
batch_size: Reserved for compatibility with viewer configuration
58-
debounce_delay_ms: Qt-thread debounce delay owned by the caller
59-
max_debounce_wait_ms: Reserved for compatibility with viewer configuration
6051
"""
6152
self.napari_server = napari_server
62-
self.batch_size = batch_size
63-
self.debounce_delay_ms = debounce_delay_ms
64-
self.max_debounce_wait_ms = max_debounce_wait_ms
65-
66-
logger.info(
67-
f"NapariBatchProcessor: Created with batch_size={batch_size}, "
68-
f"debounce={debounce_delay_ms}ms, max_wait={max_debounce_wait_ms}ms"
69-
)
53+
logger.info("NapariBatchProcessor: Created")
7054

7155
def add_items(
7256
self,

src/polystore/streaming/viewer_transport.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ class ViewerDisplayConfigABC(ABC):
4343
def component_modes(self) -> Mapping[DisplayComponentToken, DisplayModeToken]:
4444
"""Return mode assignments by display component."""
4545

46+
@abstractmethod
47+
def display_payload_extra(self) -> ViewerWireMapping:
48+
"""Project backend-specific display fields onto the viewer wire payload."""
49+
4650

4751
class ViewerFilenameParserABC(ABC):
4852
"""Filename parser surface needed by viewer streaming metadata."""

0 commit comments

Comments
 (0)