def _configure_and_stream( self, config: dict[str, Any] | None) -> AsyncGenerator[DataEvent, None]: if config is None: return stream.empty() try: # Run all config steps in order (concat) and one at a time (task_limit=1). Drop the output. There is # nothing to compare them to (filter => false), then read all sensors of the bricklet and process them in # parallel (flatten). config_stream = stream.chain( stream.iterate(config["on_connect"]) | pipe.starmap(lambda func, timeout: stream.just(func()) | pipe .timeout(timeout)) | pipe.concat(task_limit=1) | pipe.filter(lambda result: False), stream.iterate(config["config"].items()) | pipe.starmap(self._parse_callback_configuration) | pipe.starmap(self._set_callback_configuration) | pipe.flatten() | pipe.map(lambda args: self._read_sensor(config["uuid"], *args)) | pipe.flatten(), ) return config_stream except NotConnectedError: # Do not log it raise except Exception: self._logger.exception("This should not happen.") raise
async def test_starmap(assert_run, event_loop): with event_loop.assert_cleanup(): xs = stream.range(5) ys = stream.range(5) zs = xs | pipe.zip(ys) | pipe.starmap(lambda x, y: x+y) expected = [x*2 for x in range(5)] await assert_run(zs, expected) with event_loop.assert_cleanup(): xs = stream.range(1, 4) ys = stream.range(1, 4) zs = xs | pipe.zip(ys) | pipe.starmap(asyncio.sleep) await assert_run(zs, [1, 2, 3]) assert event_loop.steps == [1, 2, 3]
def _stream_data(self, transport): config_stream = (stream.chain( stream.call( call_safely, f"{self.__database_topic}/get_config", f"{self.__database_topic}/status_update", transport.uuid, ), stream.iterate( event_bus.subscribe(f"nodes/by_uuid/{transport.uuid}/add")), ) | pipe.map(lambda config: self._create_device( transport, config)) | pipe.starmap(lambda config, device: stream.empty( ) if device is None else device.stream_data(config)) | pipe.switch() | context.pipe( transport, on_enter=lambda: logging.getLogger(__name__).info( "Connected to %s at %s (%s).", transport.name, transport.uri, transport.label), on_exit=lambda: logging.getLogger(__name__).info( "Disconnected from %s at %s (%s).", transport. name, transport.uri, transport.label), )) return config_stream
def _read_device(config: dict[str, Any]) -> AsyncGenerator[Any, None]: on_read: partial timeout: float on_read, timeout = config["on_read"] if inspect.isasyncgenfunction(on_read.func): return stream.iterate(on_read()) | pipe.timeout(timeout) return (stream.repeat(config["on_read"], interval=config["interval"]) | pipe.starmap(lambda func, timeout: stream.just(func()) | pipe .timeout(timeout)) | pipe.concat(task_limit=1))
async def test_starmap(assert_run, event_loop): with event_loop.assert_cleanup(): xs = stream.range(5) ys = stream.range(5) zs = xs | pipe.zip(ys) | pipe.starmap(lambda x, y: x+y) expected = [x*2 for x in range(5)] await assert_run(zs, expected) with event_loop.assert_cleanup(): xs = stream.range(1, 4) ys = stream.range(1, 4) zs = xs | pipe.zip(ys) | pipe.starmap(asyncio.sleep) await assert_run(zs, [1, 2, 3]) assert event_loop.steps == [1, 1, 1] with event_loop.assert_cleanup(): xs = stream.range(1, 4) ys = stream.range(1, 4) zs = xs | pipe.zip(ys) | pipe.starmap(asyncio.sleep, task_limit=1) await assert_run(zs, [1, 2, 3]) assert event_loop.steps == [1, 2, 3]
def _stream_transport(transport: TinkerforgeTransport): sensor_stream = stream.chain( stream.call(transport.enumerate) | pipe.filter(lambda x: False), stream.iterate(transport.read_enumeration()) | pipe.action(lambda enumeration: event_bus.publish(f"nodes/tinkerforge/{enumeration[1].uid}/remove", None)) | pipe.filter(lambda enumeration: enumeration[0] is not EnumerationType.DISCONNECTED) | pipe.starmap(lambda enumeration_type, sensor: TinkerforgeSensor(sensor)) | pipe.map(lambda sensor: sensor.stream_data()) | pipe.flatten(), ) | context.pipe( transport, on_enter=lambda: logging.getLogger(__name__).info( "Connected to Tinkerforge host at %s (%s).", transport.uri, transport.label ), on_exit=lambda: logging.getLogger(__name__).info( "Disconnected from Tinkerforge host at %s (%s).", transport.uri, transport.label ), ) return sensor_stream
def _configure_and_stream( self, config: dict[str, Any]) -> AsyncGenerator[DataEvent, None]: if config is None: return stream.empty() # Run all config steps in order (concat) and one at a time (task_limit=1). Drop the output. There is nothing to # compare them to (filter => false), then read the device. config_stream = stream.chain( stream.iterate(config["on_connect"]) | pipe.starmap(lambda func, timeout: stream.just(func()) | pipe. timeout(timeout)) | pipe.concat(task_limit=1) | pipe.filter(lambda result: False), self._read_device(config) | pipe.map(lambda item: DataEvent(sender=config["uuid"], topic=config["topic"], value=item, sid=0, unit=config["unit"])) | finally_action.pipe( stream.call(self._clean_up, config["on_disconnect"])), ) | catch.pipe(TypeError, on_exc=self.on_error) return config_stream