1- """Partially predifined behaviour for HPK mock.
1+ """Partially predefined behavior for HPK mock.
22
33This class provides basic Hpk mock functionality by taking over some usually
44desired tasks. With that in place, the user may inherit from this class
55in order to further specify behavior, without having to start from scratch.
6- Even if some of the predefined behaviour is not desired, the implementation
6+ Even if some of the predefined behavior is not desired, the implementation
77can give some reference on how an individual mock server can be implemented.
88
99
10- Already predefined behaviour :
10+ Already predefined behavior :
1111
1212 * Simulating state for get/set:
1313 A dictionary is used to store the state of the mock server.
2222 The subscriptions are stored and on every change, the new value is passed
2323 into the queues.
2424 * Adding chronological timestamps to responses:
25- The server answers need timestamps to the responsis in any case.
25+ The server answers need timestamps to the responses in any case.
2626 By using the monotonic clock, the timestamps are added automatically.
2727
2828"""
@@ -95,7 +95,7 @@ def __init__(
9595 self ._common_prefix = None
9696
9797 def get_timestamp (self ) -> int :
98- """Create a realisitc timestamp.
98+ """Create a realistic timestamp.
9999
100100 Call this function to obtain a timestamp for some response.
101101 As a internal clock is used, subsequent calls will return
@@ -107,15 +107,15 @@ def get_timestamp(self) -> int:
107107 return time .monotonic_ns ()
108108
109109 def _sanitize_path (self , path : LabOneNodePath ) -> LabOneNodePath :
110- """Sanatize the path.
110+ """Sanitize the path.
111111
112112 Removes trailing slashes and replaces empty path with root path.
113113
114114 Args:
115- path: Path to sanatize .
115+ path: Path to sanitize .
116116
117117 Returns:
118- Sanatized path.
118+ Sanitized path.
119119 """
120120 if self ._common_prefix and not path .startswith ("/" ):
121121 return f"{ self ._common_prefix } /{ path } "
@@ -127,19 +127,19 @@ async def list_nodes_info(
127127 * ,
128128 flags : ListNodesInfoFlags | int = ListNodesInfoFlags .ALL , # noqa: ARG002
129129 ) -> dict [LabOneNodePath , NodeInfoType ]:
130- """Predefined behaviour for list_nodes_info.
130+ """Predefined behavior for list_nodes_info.
131131
132132 Uses knowledge of the tree structure to answer.
133133
134134 Warning:
135135 Flags will be ignored in this implementation. (TODO)
136- For now, the behaviour is equivalent to
136+ For now, the behavior is equivalent to
137137 ListNodesFlags.RECURSIVE | ListNodesFlags.ABSOLUTE
138138
139139 Args:
140140 path: Path to narrow down which nodes should be listed. Omitting
141141 the path will list all nodes by default.
142- flags: Flags to control the behaviour of the list_nodes_info method.
142+ flags: Flags to control the behavior of the list_nodes_info method.
143143
144144 Returns:
145145 Dictionary of paths to node info.
@@ -154,19 +154,19 @@ async def list_nodes(
154154 * ,
155155 flags : ListNodesFlags | int = ListNodesFlags .ABSOLUTE , # noqa: ARG002
156156 ) -> list [LabOneNodePath ]:
157- """Predefined behaviour for list_nodes.
157+ """Predefined behavior for list_nodes.
158158
159159 Uses knowledge of the tree structure to answer.
160160
161161 Warning:
162162 Flags will be ignored in this implementation. (TODO)
163- For now, the behaviour is equivalent to
163+ For now, the behavior is equivalent to
164164 ListNodesFlags.RECURSIVE | ListNodesFlags.ABSOLUTE
165165
166166 Args:
167167 path: Path to narrow down which nodes should be listed. Omitting
168168 the path will list all nodes by default.
169- flags: Flags to control the behaviour of the list_nodes method.
169+ flags: Flags to control the behavior of the list_nodes method.
170170
171171 Returns:
172172 List of paths.
@@ -183,7 +183,7 @@ async def list_nodes(
183183 ]
184184
185185 async def get (self , path : LabOneNodePath ) -> AnnotatedValue :
186- """Predefined behaviour for get.
186+ """Predefined behavior for get.
187187
188188 Look up the path in the internal dictionary.
189189
@@ -212,20 +212,44 @@ async def get_with_expression(
212212 | ListNodesFlags .EXCLUDE_STREAMING
213213 | ListNodesFlags .GET_ONLY ,
214214 ) -> list [AnnotatedValue ]:
215- """Predefined behaviour for get_with_expression.
215+ """Predefined behavior for get_with_expression.
216216
217217 Find all nodes associated with the path expression
218218 and call get for each of them.
219219
220220 Args:
221221 path_expression: Path expression to get.
222- flags: Flags to control the behaviour of the get_with_expression method.
222+ flags: Flags to control the behavior of the get_with_expression method.
223223
224224 Returns:
225225 List of values, corresponding to nodes of the path expression.
226226 """
227227 return [await self .get (p ) for p in await self .list_nodes (path = path_expression )]
228228
229+ async def _update_subscriptions (self , value : AnnotatedValue ) -> None :
230+ """Update all subscriptions with the new value.
231+
232+ Args:
233+ value: New value.
234+ """
235+ if self .memory [value .path ].streaming_handles :
236+ # sending updated value to subscriptions
237+ result = await asyncio .gather (
238+ * [
239+ handle .send_value (value )
240+ for handle in self .memory [value .path ].streaming_handles
241+ ],
242+ )
243+ # Remove all disconnected subscriptions
244+ self .memory [value .path ].streaming_handles = [
245+ handle
246+ for handle , success in zip (
247+ self .memory [value .path ].streaming_handles ,
248+ result ,
249+ )
250+ if success
251+ ]
252+
229253 @t .overload
230254 async def set (self , value : AnnotatedValue ) -> AnnotatedValue : ...
231255
@@ -241,7 +265,7 @@ async def set(
241265 value : AnnotatedValue | Value ,
242266 path : str = "" ,
243267 ) -> AnnotatedValue :
244- """Predefined behaviour for set.
268+ """Predefined behavior for set.
245269
246270 Updates the internal dictionary. A set command is considered
247271 as an update and will be distributed to all registered subscription handlers.
@@ -271,14 +295,7 @@ async def set(
271295 path = path ,
272296 timestamp = self .get_timestamp (),
273297 )
274- if self .memory [path ].streaming_handles :
275- # sending updated value to subscriptions
276- await asyncio .gather (
277- * [
278- handle .send_value (response )
279- for handle in self .memory [path ].streaming_handles
280- ],
281- )
298+ await self ._update_subscriptions (value = response )
282299 return response
283300
284301 @t .overload
@@ -299,7 +316,7 @@ async def set_with_expression(
299316 value : AnnotatedValue | Value ,
300317 path : LabOneNodePath | None = None ,
301318 ) -> list [AnnotatedValue ]:
302- """Predefined behaviour for set_with_expression.
319+ """Predefined behavior for set_with_expression.
303320
304321 Finds all nodes associated with the path expression
305322 and call set for each of them.
@@ -323,7 +340,7 @@ async def set_with_expression(
323340 return result
324341
325342 async def subscribe (self , subscription : Subscription ) -> None :
326- """Predefined behaviour for subscribe.
343+ """Predefined behavior for subscribe.
327344
328345 Stores the subscription. Whenever an update event happens
329346 they are distributed to all registered handles,
0 commit comments