55that work with arbitrary numbers of components (not hardcoded to 3).
66"""
77
8- from typing import TypeVar , Generic , Dict , Any , Protocol , runtime_checkable
8+ from abc import ABC , abstractmethod
9+ from typing import TypeVar , Generic , Dict , Any , List
910from dataclasses import dataclass
1011
1112T = TypeVar ('T' )
@@ -29,9 +30,10 @@ class TypedData(Generic[T]):
2930 source : str
3031
3132
32- class ComponentAccessor (Protocol ):
33- """Protocol for component metadata access (arbitrary number of components)."""
33+ class ComponentAccessor (ABC ):
34+ """ABC for component metadata access (arbitrary number of components)."""
3435
36+ @abstractmethod
3537 def get_by_mode (self , mode : str ) -> list :
3638 """
3739 Get all component names that have this mode (stack/slice/window).
@@ -46,42 +48,68 @@ def get_by_mode(self, mode: str) -> list:
4648 If config has {'channel': 'stack', 'z_index': 'slice', 'well': 'window'}
4749 Then get_by_mode('stack') returns ['channel']
4850 """
49- ...
51+ raise NotImplementedError
5052
53+ @abstractmethod
5154 def get_value (self , item : Dict [str , Any ], component_name : str ) -> Any :
5255 """
5356 Get component value for an item.
5457
5558 Returns:
5659 Value or default (0) if component not in metadata.
5760 """
58- ...
61+ raise NotImplementedError
5962
63+ @abstractmethod
6064 def collect_values (self , component_names : list ) -> list [tuple ]:
6165 """
6266 Collect unique values for given components across all items.
6367
6468 Returns:
6569 Sorted list of tuples for consistent indexing.
6670 """
67- ...
71+ raise NotImplementedError
6872
6973
70- class HandlerContext (Protocol ):
71- """Protocol for handler context with generic component access."""
74+ class HandlerContext (ABC ):
75+ """ABC for handler context with generic component access."""
7276
73- server : Any
74- window_key : str
75- data : 'TypedData[Any]'
76- display_config : Dict [str , Any ]
77- components : ComponentAccessor
78- images_dir : str | None
77+ @property
78+ @abstractmethod
79+ def server (self ) -> Any :
80+ raise NotImplementedError
81+
82+ @property
83+ @abstractmethod
84+ def window_key (self ) -> str :
85+ raise NotImplementedError
86+
87+ @property
88+ @abstractmethod
89+ def data (self ) -> "TypedData[Any]" :
90+ raise NotImplementedError
91+
92+ @property
93+ @abstractmethod
94+ def display_config (self ) -> Dict [str , Any ]:
95+ raise NotImplementedError
96+
97+ @property
98+ @abstractmethod
99+ def components (self ) -> ComponentAccessor :
100+ raise NotImplementedError
101+
102+ @property
103+ @abstractmethod
104+ def images_dir (self ) -> str | None :
105+ raise NotImplementedError
79106
80107
81- class ItemHandler (Protocol ):
82- """Type-safe protocol for item handlers with automatic discovery."""
108+ class ItemHandler (ABC ):
109+ """Type-safe ABC for item handlers with automatic discovery."""
83110
84111 @staticmethod
112+ @abstractmethod
85113 def can_handle (data_type : str ) -> bool :
86114 """
87115 Check if this handler can process the given data type.
@@ -92,17 +120,18 @@ def can_handle(data_type: str) -> bool:
92120 Returns:
93121 True if this handler can process this type.
94122 """
95- ...
123+ raise NotImplementedError
96124
97125 @staticmethod
126+ @abstractmethod
98127 def handle (context : HandlerContext ) -> None :
99128 """
100129 Process items using type-safe context object.
101130
102131 Args:
103132 context: HandlerContext with typed data and component accessor.
104133 """
105- ...
134+ raise NotImplementedError
106135
107136
108137@dataclass (frozen = True )
@@ -171,7 +200,7 @@ def collect_values(self, component_names: list) -> list[tuple]:
171200
172201
173202@dataclass (frozen = True )
174- class SimpleHandlerContext ( HandlerContext ) :
203+ class SimpleHandlerContext :
175204 """Concrete implementation of HandlerContext protocol."""
176205
177206 server : Any
0 commit comments