From fd5b6ea710ce5ac7cb9d905a577baa701ef2b370 Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Mon, 31 Jul 2023 18:39:22 +0200 Subject: [PATCH 01/27] use service changes --- Dockerfile.service | 3 - brewblox_devcon_spark/__main__.py | 91 +-- brewblox_devcon_spark/api/blocks_api.py | 19 +- brewblox_devcon_spark/api/error_response.py | 2 +- brewblox_devcon_spark/api/mqtt_api.py | 2 +- brewblox_devcon_spark/api/system_api.py | 6 +- brewblox_devcon_spark/backup_storage.py | 6 +- brewblox_devcon_spark/broadcaster.py | 47 +- brewblox_devcon_spark/commander.py | 2 +- .../connection/connection_handler.py | 14 +- .../connection/mock_connection.py | 4 +- .../connection/mqtt_connection.py | 4 +- .../connection/stream_connection.py | 14 +- brewblox_devcon_spark/controller.py | 2 +- brewblox_devcon_spark/datastore.py | 4 +- brewblox_devcon_spark/global_store.py | 4 +- brewblox_devcon_spark/models.py | 25 +- brewblox_devcon_spark/service_status.py | 6 +- brewblox_devcon_spark/service_store.py | 2 +- brewblox_devcon_spark/synchronization.py | 4 +- brewblox_devcon_spark/time_sync.py | 2 +- poetry.lock | 677 ++++++++++-------- pyproject.toml | 3 +- test/conftest.py | 129 ++-- test/test_backup_storage.py | 16 +- test/test_block_store.py | 8 +- test/test_broadcaster.py | 86 +-- test/test_codec.py | 3 +- test/test_commander.py | 10 +- test/test_connection_handler.py | 47 +- test/test_controller.py | 3 +- test/test_datastore.py | 9 +- test/test_global_store.py | 7 +- test/test_integration.py | 108 +-- test/test_main.py | 43 +- test/test_mqtt_api.py | 24 +- test/test_mqtt_connection.py | 132 +++- test/test_service_status.py | 5 +- test/test_service_store.py | 5 +- test/test_sim_api.py | 4 +- test/test_stream_connection.py | 2 +- test/test_synchronization.py | 9 +- test/test_time_sync.py | 10 +- 43 files changed, 846 insertions(+), 757 deletions(-) diff --git a/Dockerfile.service b/Dockerfile.service index 16af5a4c..3362d341 100644 --- a/Dockerfile.service +++ b/Dockerfile.service @@ -22,9 +22,6 @@ FROM python:3.9-slim-bullseye EXPOSE 5000 WORKDIR /app -ARG service_info=UNKNOWN -ENV SERVICE_INFO=${service_info} - RUN set -ex \ && apt-get update \ && apt-get install -y --no-install-recommends \ diff --git a/brewblox_devcon_spark/__main__.py b/brewblox_devcon_spark/__main__.py index 3d8c7096..2c94bf2c 100644 --- a/brewblox_devcon_spark/__main__.py +++ b/brewblox_devcon_spark/__main__.py @@ -2,7 +2,7 @@ Example of how to import and use the brewblox service """ -import logging +import json from configparser import ConfigParser from os import getenv @@ -18,11 +18,12 @@ from brewblox_devcon_spark.models import (DiscoveryType, ServiceConfig, ServiceFirmwareIni) +FIRMWARE_INI = 'firmware/firmware.ini' LOGGER = brewblox_logger(__name__) -def create_parser(default_name='spark'): - parser = service.create_parser(default_name=default_name) +def create_parser(): + parser = service.create_parser('spark') # Device options group = parser.add_argument_group('Device communication') @@ -114,18 +115,18 @@ def create_parser(default_name='spark'): return parser -def parse_ini(app) -> ServiceFirmwareIni: # pragma: no cover +def parse_ini(app) -> ServiceFirmwareIni: parser = ConfigParser() - parser.read('firmware/firmware.ini') + parser.read(FIRMWARE_INI) config = ServiceFirmwareIni(parser['FIRMWARE'].items()) LOGGER.info(f'firmware.ini: {config}') return config def main(): - app = service.create_app(parser=create_parser()) - logging.captureWarnings(True) - config: ServiceConfig = app['config'] + parser = create_parser() + config = service.create_config(parser, model=ServiceConfig) + app = service.create_app(config) app['ini'] = parse_ini(app) if getenv('ENABLE_DEBUGGER', False): # pragma: no cover @@ -133,39 +134,47 @@ def main(): debugpy.listen(('0.0.0.0', 5678)) LOGGER.info('Debugger is enabled and listening on 5678') - if config['simulation'] or config['mock']: - config['device_id'] = config['device_id'] or '123456789012345678901234' - - scheduler.setup(app) - mqtt.setup(app) - http.setup(app) - - global_store.setup(app) - service_store.setup(app) - block_store.setup(app) - - service_status.setup(app) - codec.setup(app) - connection.setup(app) - commander.setup(app) - synchronization.setup(app) - controller.setup(app) - - backup_storage.setup(app) - broadcaster.setup(app) - time_sync.setup(app) - - error_response.setup(app) - blocks_api.setup(app) - system_api.setup(app) - settings_api.setup(app) - mqtt_api.setup(app) - sim_api.setup(app) - backup_api.setup(app) - debug_api.setup(app) - - service.furnish(app) - service.run(app) + if config.device_id is None and (config.simulation or config.mock): + config.device_id = '123456789012345678901234' + + async def setup(): + scheduler.setup(app) + http.setup(app) + mqtt.setup(app, + client_will=mqtt.Will( + topic=f'{config.state_topic}/{config.name}', + payload=json.dumps({ + 'key': config.name, + 'type': 'Spark.state', + 'data': None, + }) + )) + + global_store.setup(app) + service_store.setup(app) + block_store.setup(app) + + service_status.setup(app) + codec.setup(app) + connection.setup(app) + commander.setup(app) + synchronization.setup(app) + controller.setup(app) + + backup_storage.setup(app) + broadcaster.setup(app) + time_sync.setup(app) + + error_response.setup(app) + blocks_api.setup(app) + system_api.setup(app) + settings_api.setup(app) + mqtt_api.setup(app) + sim_api.setup(app) + backup_api.setup(app) + debug_api.setup(app) + + service.run_app(app, setup()) if __name__ == '__main__': diff --git a/brewblox_devcon_spark/api/blocks_api.py b/brewblox_devcon_spark/api/blocks_api.py index 5094bd03..5e1f5044 100644 --- a/brewblox_devcon_spark/api/blocks_api.py +++ b/brewblox_devcon_spark/api/blocks_api.py @@ -13,7 +13,7 @@ from brewblox_devcon_spark import controller from brewblox_devcon_spark.models import (Block, BlockIdentity, BlockIdentityList, BlockList, - BlockNameChange) + BlockNameChange, ServiceConfig) LOGGER = brewblox_logger(__name__) routes = web.RouteTableDef() @@ -27,25 +27,26 @@ class BlocksView(PydanticView): def __init__(self, request: web.Request) -> None: super().__init__(request) self.app = request.app - self.controller = controller.fget(request.app) + self.config: ServiceConfig = self.app['config'] + self.controller = controller.fget(self.app) async def publish(self, changed: list[Block] = None, deleted: list[BlockIdentity] = None): changed = [v.dict() for v in changed] if changed else [] deleted = [v.id for v in deleted] if deleted else [] - name = self.app['config']['name'] - topic = self.app['config']['state_topic'] + f'/{name}/patch' + name = self.config.name await mqtt.publish(self.app, - topic, - err=False, - message=json.dumps({ + topic=f'{self.config.state_topic}/{name}/patch', + payload=json.dumps({ 'key': name, 'type': 'Spark.patch', 'ttl': '1d', 'data': { 'changed': changed, 'deleted': deleted, - } - })) + }, + }), + err=False, + ) @routes.view('/blocks/create') diff --git a/brewblox_devcon_spark/api/error_response.py b/brewblox_devcon_spark/api/error_response.py index 8c86a78e..5ff70f30 100644 --- a/brewblox_devcon_spark/api/error_response.py +++ b/brewblox_devcon_spark/api/error_response.py @@ -28,7 +28,7 @@ async def controller_error_middleware(request: web.Request, handler) -> web.Resp except Exception as ex: app = request.app message = strex(ex) - debug = app['config']['debug'] + debug = app['config'].debug LOGGER.error(f'[{request.url}] => {message}', exc_info=debug) response = {'error': message} diff --git a/brewblox_devcon_spark/api/mqtt_api.py b/brewblox_devcon_spark/api/mqtt_api.py index 88ef2cec..2dfe7803 100644 --- a/brewblox_devcon_spark/api/mqtt_api.py +++ b/brewblox_devcon_spark/api/mqtt_api.py @@ -19,7 +19,7 @@ class MqttApi(features.ServiceFeature): def __init__(self, app: web.Application): super().__init__(app) - self.name = app['config']['name'] + self.name = app['config'].name self.controller = controller.fget(app) self.listeners = { '/create': self._create, diff --git a/brewblox_devcon_spark/api/system_api.py b/brewblox_devcon_spark/api/system_api.py index fe08cebc..379e7320 100644 --- a/brewblox_devcon_spark/api/system_api.py +++ b/brewblox_devcon_spark/api/system_api.py @@ -135,9 +135,9 @@ class FlashView(PydanticView): # pragma: no cover def __init__(self, request: web.Request) -> None: super().__init__(request) self.app = request.app - self.name: str = self.app['config']['name'] - self.simulation: bool = self.app['config']['simulation'] - self.topic: str = self.app['config']['state_topic'] + f'/{self.name}/update' + self.name: str = self.app['config'].name + self.simulation: bool = self.app['config'].simulation + self.topic: str = self.app['config'].state_topic + f'/{self.name}/update' self.version: str = self.app['ini']['firmware_version'][:8] self.date: str = self.app['ini']['firmware_date'] diff --git a/brewblox_devcon_spark/backup_storage.py b/brewblox_devcon_spark/backup_storage.py index 01d26add..7965e856 100644 --- a/brewblox_devcon_spark/backup_storage.py +++ b/brewblox_devcon_spark/backup_storage.py @@ -24,10 +24,10 @@ def __init__(self, app: web.Application): super().__init__(app) config: ServiceConfig = app['config'] - self.name = config['name'] + self.name = config.name self.dir = BASE_BACKUP_DIR / self.name - self.interval_s = config['backup_interval'] - self.retry_interval_s = config['backup_retry_interval'] + self.interval_s = config.backup_interval + self.retry_interval_s = config.backup_retry_interval if self.retry_interval_s <= 0: self.retry_interval_s = self.interval_s diff --git a/brewblox_devcon_spark/broadcaster.py b/brewblox_devcon_spark/broadcaster.py index f04cbff0..693a76e2 100644 --- a/brewblox_devcon_spark/broadcaster.py +++ b/brewblox_devcon_spark/broadcaster.py @@ -23,22 +23,11 @@ def __init__(self, app: web.Application): super().__init__(app) config: ServiceConfig = app['config'] - self.name = config['name'] - self.interval = config['broadcast_interval'] - self.isolated = self.interval <= 0 or config['isolated'] - self.state_topic = config['state_topic'] + f'/{self.name}' - self.history_topic = config['history_topic'] + f'/{self.name}' - - self._will_message = json.dumps({ - 'key': self.name, - 'type': 'Spark.state', - 'data': None, - }) - - # A will is published if the client connection is broken - mqtt.set_client_will(app, - self.state_topic, - self._will_message) + self.name = config.name + self.interval = config.broadcast_interval + self.isolated = self.interval <= 0 or config.isolated + self.state_topic = f'{config.state_topic}/{config.name}' + self.history_topic = f'{config.history_topic}/{config.name}' async def prepare(self): if self.isolated: @@ -46,12 +35,6 @@ async def prepare(self): async def before_shutdown(self, app: web.Application): await self.end() - # This is an orderly shutdown - MQTT will won't be published - await mqtt.publish(self.app, - self.state_topic, - err=False, - retain=True, - message=self._will_message) async def run(self): try: @@ -70,20 +53,19 @@ async def run(self): } await mqtt.publish(self.app, - self.history_topic, - err=False, - message=json.dumps({ + topic=self.history_topic, + payload=json.dumps({ 'key': self.name, 'data': history_data, - })) + }), + err=False, + ) finally: # State event is always published await mqtt.publish(self.app, - self.state_topic, - err=False, - retain=True, - message=json.dumps({ + topic=self.state_topic, + payload=json.dumps({ 'key': self.name, 'type': 'Spark.state', 'data': { @@ -92,7 +74,10 @@ async def run(self): 'relations': calculate_relations(blocks), 'claims': calculate_claims(blocks), }, - })) + }), + retain=True, + err=False, + ) except Exception as ex: LOGGER.debug(f'{self} exception: {strex(ex)}') diff --git a/brewblox_devcon_spark/commander.py b/brewblox_devcon_spark/commander.py index 83079a7f..20f27a3e 100644 --- a/brewblox_devcon_spark/commander.py +++ b/brewblox_devcon_spark/commander.py @@ -41,7 +41,7 @@ def __init__(self, app: web.Application): config: ServiceConfig = app['config'] self._msgid = 0 - self._timeout = config['command_timeout'] + self._timeout = config.command_timeout self._active_messages: dict[int, asyncio.Future[IntermediateResponse]] = {} self._codec = codec.fget(app) self._conn = connection.fget(app) diff --git a/brewblox_devcon_spark/connection/connection_handler.py b/brewblox_devcon_spark/connection/connection_handler.py index c0f29904..6ba9f3fa 100644 --- a/brewblox_devcon_spark/connection/connection_handler.py +++ b/brewblox_devcon_spark/connection/connection_handler.py @@ -64,7 +64,7 @@ async def on_response(self, msg: str): async def discover(self) -> ConnectionImplBase: config: ServiceConfig = self.app['config'] - discovery_type = config['discovery'] + discovery_type = config.discovery LOGGER.info(f'Discovering devices... ({discovery_type})') try: @@ -93,11 +93,11 @@ async def discover(self) -> ConnectionImplBase: async def connect(self) -> ConnectionImplBase: config: ServiceConfig = self.app['config'] - mock = config['mock'] - simulation = config['simulation'] - device_serial = config['device_serial'] - device_host = config['device_host'] - device_port = config['device_port'] + mock = config.mock + simulation = config.simulation + device_serial = config.device_serial + device_host = config.device_host + device_port = config.device_port if mock: return await connect_mock(self.app, self) @@ -141,7 +141,7 @@ async def run(self): # USB devices that were plugged in after container start are not visible # If we are potentially connecting to a USB device, we need to restart - if config['device_serial'] or config['discovery'] in [DiscoveryType.all, DiscoveryType.usb]: + if config.device_serial or config.discovery in [DiscoveryType.all, DiscoveryType.usb]: raise web.GracefulExit() else: self._retry_count = 0 diff --git a/brewblox_devcon_spark/connection/mock_connection.py b/brewblox_devcon_spark/connection/mock_connection.py index e618f91f..9e998cdc 100644 --- a/brewblox_devcon_spark/connection/mock_connection.py +++ b/brewblox_devcon_spark/connection/mock_connection.py @@ -149,7 +149,7 @@ async def welcome(self): 'mock', ResetReason.NONE.value, ResetData.NOT_SPECIFIED.value, - self.app['config']['device_id'], + self.app['config'].device_id, ] await self.on_event(','.join(welcome)) @@ -279,7 +279,7 @@ async def close(self): async def connect_mock(app: web.Application, callbacks: ConnectionCallbacks) -> ConnectionImplBase: - device_id = app['config']['device_id'] + device_id = app['config'].device_id conn = MockConnection(app, device_id, callbacks) await conn.connect() return conn diff --git a/brewblox_devcon_spark/connection/mqtt_connection.py b/brewblox_devcon_spark/connection/mqtt_connection.py index f70cc374..c8c63d28 100644 --- a/brewblox_devcon_spark/connection/mqtt_connection.py +++ b/brewblox_devcon_spark/connection/mqtt_connection.py @@ -78,7 +78,7 @@ async def close(self): class MqttDeviceTracker(features.ServiceFeature): def __init__(self, app: web.Application): super().__init__(app) - self._isolated = app['config']['isolated'] + self._isolated = app['config'].isolated self._handshake_topic = HANDSHAKE_TOPIC + '+' self._devices: dict[str, asyncio.Event] = {} @@ -127,5 +127,5 @@ def fget(app: web.Application) -> MqttDeviceTracker: async def discover_mqtt(app: web.Application, callbacks: ConnectionCallbacks, ) -> Optional[MqttConnection]: - device_id = app['config']['device_id'] + device_id = app['config'].device_id return await fget(app).discover(callbacks, device_id) diff --git a/brewblox_devcon_spark/connection/stream_connection.py b/brewblox_devcon_spark/connection/stream_connection.py index 63af1005..bc34fd78 100644 --- a/brewblox_devcon_spark/connection/stream_connection.py +++ b/brewblox_devcon_spark/connection/stream_connection.py @@ -154,9 +154,9 @@ async def connect_simulation(app: web.Application, callbacks: ConnectionCallbacks, ) -> ConnectionImplBase: # pragma: no cover config: ServiceConfig = app['config'] - device_id = config['device_id'] - port = config['device_port'] - display_ws_port = config['display_ws_port'] + device_id = config.device_id + port = config.device_port + display_ws_port = config.display_ws_port arch = platform.machine() binary = SIM_BINARIES.get(arch) @@ -182,8 +182,8 @@ async def connect_usb(app: web.Application, port: Optional[int] = None, ) -> ConnectionImplBase: # pragma: no cover config: ServiceConfig = app['config'] - device_serial = device_serial or config['device_serial'] - port = port or config['device_port'] + device_serial = device_serial or config.device_serial + port = port or config.device_port proc = await asyncio.create_subprocess_exec('/usr/bin/socat', f'tcp-listen:{port},reuseaddr,fork', f'file:{device_serial},raw,echo=0,b{USB_BAUD_RATE}') @@ -194,7 +194,7 @@ async def connect_usb(app: web.Application, async def discover_mdns(app: web.Application, callbacks: ConnectionCallbacks, ) -> Optional[ConnectionImplBase]: - device_id = app['config']['device_id'] + device_id = app['config'].device_id try: resp = await mdns.discover_one(device_id, BREWBLOX_DNS_TYPE, @@ -208,7 +208,7 @@ async def discover_usb(app: web.Application, callbacks: ConnectionCallbacks, ) -> Optional[ConnectionImplBase]: # pragma: no cover config: ServiceConfig = app['config'] - device_id = config['device_id'] + device_id = config.device_id for usb_port in list_ports.grep(SPARK_DEVICE_REGEX): if device_id is None or device_id.lower() == usb_port.serial_number.lower(): LOGGER.info(f'Discovered {[v for v in usb_port]}') diff --git a/brewblox_devcon_spark/controller.py b/brewblox_devcon_spark/controller.py index ebd71d89..e5f113ff 100644 --- a/brewblox_devcon_spark/controller.py +++ b/brewblox_devcon_spark/controller.py @@ -69,7 +69,7 @@ class SparkController(features.ServiceFeature): def __init__(self, app: web.Application): super().__init__(app) - self._name = app['config']['name'] + self._name = app['config'].name self._cmder = commander.fget(app) self._store = block_store.fget(app) self._discovery_lock: asyncio.Lock = None diff --git a/brewblox_devcon_spark/datastore.py b/brewblox_devcon_spark/datastore.py index 74a6fb96..7e77d793 100644 --- a/brewblox_devcon_spark/datastore.py +++ b/brewblox_devcon_spark/datastore.py @@ -29,7 +29,7 @@ async def wrapper(self, *args, **kwargs): async def check_remote(app: web.Application): - if app['config']['isolated']: + if app['config'].isolated: return num_attempts = 0 while True: @@ -48,7 +48,7 @@ class FlushedStore(repeater.RepeaterFeature): def __init__(self, app: web.Application): super().__init__(app) - self._isolated = app['config']['isolated'] + self._isolated = app['config'].isolated self._changed_event: asyncio.Event = None @property diff --git a/brewblox_devcon_spark/global_store.py b/brewblox_devcon_spark/global_store.py index 70908972..eba589b3 100644 --- a/brewblox_devcon_spark/global_store.py +++ b/brewblox_devcon_spark/global_store.py @@ -31,8 +31,8 @@ class GlobalConfigStore(features.ServiceFeature): def __init__(self, app: web.Application): super().__init__(app) config: ServiceConfig = app['config'] - self._isolated = config['isolated'] - self._datastore_topic = config['datastore_topic'] + self._isolated = config.isolated + self._datastore_topic = config.datastore_topic self._global_topic = f'{self._datastore_topic}/{const.GLOBAL_NAMESPACE}' self.units = default_units() diff --git a/brewblox_devcon_spark/models.py b/brewblox_devcon_spark/models.py index cff74be2..36dc2a15 100644 --- a/brewblox_devcon_spark/models.py +++ b/brewblox_devcon_spark/models.py @@ -2,6 +2,7 @@ from dataclasses import dataclass from typing import Any, Literal, Optional, TypedDict, Union +from brewblox_service.models import BaseServiceConfig from pydantic import BaseModel, Field, validator @@ -14,14 +15,6 @@ class ServiceFirmwareIni(TypedDict): system_version: str -MqttProtocolType_ = Literal[ - 'mqtt', - 'mqtts', - 'ws', - 'wss', -] - - class DiscoveryType(enum.Enum): all = 1 usb = 2 @@ -36,21 +29,7 @@ def __str__(self): return self.name -class ServiceConfig(TypedDict): - # brewblox_service - name: str - host: str - port: int - debug: bool - mqtt_protocol: MqttProtocolType_ - mqtt_host: str - mqtt_port: int - mqtt_path: str - history_topic: str - state_topic: str - - # brewblox_devcon_spark - +class ServiceConfig(BaseServiceConfig): # Device options simulation: bool mock: bool diff --git a/brewblox_devcon_spark/service_status.py b/brewblox_devcon_spark/service_status.py index fef5d4f6..96d752da 100644 --- a/brewblox_devcon_spark/service_status.py +++ b/brewblox_devcon_spark/service_status.py @@ -28,7 +28,7 @@ def __init__(self, app: web.Application): config: ServiceConfig = app['config'] service_desc = ServiceDescription( - name=config['name'], + name=config.name, firmware=FirmwareDescription( firmware_version=ini['firmware_version'], proto_version=ini['proto_version'], @@ -36,7 +36,7 @@ def __init__(self, app: web.Application): proto_date=ini['proto_date'], ), device=DeviceDescription( - device_id=config['device_id'] or '', + device_id=config.device_id or '', ), ) @@ -102,7 +102,7 @@ def set_acknowledged(self, controller: ControllerDescription): wildcard_id = not service.device.device_id compatible_firmware = service.firmware.proto_version == controller.firmware.proto_version \ - or bool(config['skip_version_check']) + or bool(config.skip_version_check) matching_firmware = service.firmware.firmware_version == controller.firmware.firmware_version compatible_identity = service.device.device_id == controller.device.device_id \ or wildcard_id diff --git a/brewblox_devcon_spark/service_store.py b/brewblox_devcon_spark/service_store.py index 8519be1d..068bc02d 100644 --- a/brewblox_devcon_spark/service_store.py +++ b/brewblox_devcon_spark/service_store.py @@ -38,7 +38,7 @@ def __init__(self, app: web.Application): super().__init__(app) self._config: dict = {} self._ready_event: asyncio.Event = None - self.key: str = SERVICE_STORE_KEY.format(name=self.app['config']['name']) + self.key: str = SERVICE_STORE_KEY.format(name=self.app['config'].name) def __str__(self): return f'<{type(self).__name__}>' diff --git a/brewblox_devcon_spark/synchronization.py b/brewblox_devcon_spark/synchronization.py index 1a358e40..e86c6012 100644 --- a/brewblox_devcon_spark/synchronization.py +++ b/brewblox_devcon_spark/synchronization.py @@ -119,8 +119,8 @@ def device_name(self) -> str: # Simulation services are identified by service name. # This prevents data conflicts when a simulation service # is reconfigured to start interacting with a controller. - if self.app['config']['simulation']: - return 'simulator__' + self.app['config']['name'] + if self.app['config'].simulation: + return 'simulator__' + self.app['config'].name return service_status.desc(self.app).controller.device.device_id diff --git a/brewblox_devcon_spark/time_sync.py b/brewblox_devcon_spark/time_sync.py index f7a4d35f..dc00ec29 100644 --- a/brewblox_devcon_spark/time_sync.py +++ b/brewblox_devcon_spark/time_sync.py @@ -22,7 +22,7 @@ def __init__(self, app: web.Application): super().__init__(app) config: ServiceConfig = app['config'] - self.interval_s = config['time_sync_interval'] + self.interval_s = config.time_sync_interval self.enabled = self.interval_s > 0 async def prepare(self): diff --git a/poetry.lock b/poetry.lock index 8a50abec..00434c4b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,9 +1,10 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry and should not be changed by hand. [[package]] name = "aiofiles" version = "0.8.0" description = "File support for asyncio." +category = "main" optional = false python-versions = ">=3.6,<4.0" files = [ @@ -15,6 +16,7 @@ files = [ name = "aiohttp" version = "3.8.5" description = "Async http client/server framework (asyncio)" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -123,6 +125,7 @@ speedups = ["Brotli", "aiodns", "cchardet"] name = "aiohttp-pydantic" version = "1.12.2" description = "Aiohttp View using pydantic to validate request body and query sting regarding method annotations." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -140,21 +143,25 @@ test = ["pytest (==6.1.2)", "pytest-aiohttp (==0.3.0)", "pytest-cov (==2.10.1)", [[package]] name = "aiomqtt" -version = "0.1.3" -description = "An AsyncIO asynchronous wrapper around paho-mqtt." +version = "1.0.0" +description = "The idiomatic asyncio MQTT client, wrapped around paho-mqtt" +category = "main" optional = false -python-versions = "*" +python-versions = ">=3.7,<4.0" files = [ - {file = "aiomqtt-0.1.3.tar.gz", hash = "sha256:8c09676cbc1e682ea7b282fcd9bcae1b9630855dacc5180dbcacbee069b9576d"}, + {file = "aiomqtt-1.0.0-py3-none-any.whl", hash = "sha256:dd6fe629c10ac24a3d2abc2bcc09afe4f66bc06b6457b63cf764cc81f3b980fe"}, + {file = "aiomqtt-1.0.0.tar.gz", hash = "sha256:a96c4af50f54ded0c07d4dfc14aa3e212265cbebf659e2ab64950cf14ba8dca2"}, ] [package.dependencies] -paho-mqtt = ">=1.3.0" +paho-mqtt = ">=1.6.0,<2.0.0" +typing-extensions = {version = ">=4.4.0,<5.0.0", markers = "python_version < \"3.10\""} [[package]] name = "aiosignal" version = "1.3.1" description = "aiosignal: a list of registered asynchronous callbacks" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -169,6 +176,7 @@ frozenlist = ">=1.1.0" name = "aiozeroconf" version = "0.1.8" description = "Pure Python Multicast DNS Service Discovery Library for asyncio (Bonjour/Avahi compatible)" +category = "main" optional = false python-versions = "*" files = [ @@ -182,6 +190,7 @@ netifaces = "*" name = "aresponses" version = "2.1.6" description = "Asyncio response mocking. Similar to the responses library used for 'requests'" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -190,13 +199,14 @@ files = [ ] [package.dependencies] -aiohttp = ">=3.1.0,<4.dev0" +aiohttp = ">=3.1.0,<4.0.0" pytest-asyncio = "*" [[package]] name = "async-timeout" version = "4.0.2" description = "Timeout context manager for asyncio programs" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -208,6 +218,7 @@ files = [ name = "attrs" version = "23.1.0" description = "Classes Without Boilerplate" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -226,6 +237,7 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte name = "autopep8" version = "2.0.2" description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -241,6 +253,7 @@ tomli = {version = "*", markers = "python_version < \"3.11\""} name = "bitstring" version = "4.0.2" description = "Simple construction, analysis and modification of binary data." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -250,23 +263,31 @@ files = [ [[package]] name = "brewblox-service" -version = "2.1.1" +version = "3.0.2" description = "Scaffolding for Brewblox backend services" +category = "main" optional = false python-versions = ">=3.9,<4" -files = [ - {file = "brewblox_service-2.1.1.tar.gz", hash = "sha256:8fd3d6fdf6328443279b32d5b6f1f8c368843b8efcb533a468271e7756129207"}, -] +files = [] +develop = false [package.dependencies] -aiohttp = ">=3.7" -aiohttp-pydantic = ">=1.12" -aiomqtt = "0.1.3" +aiohttp = "==3.*" +aiohttp-pydantic = "==1.*" +aiomqtt = "==1.*" +pydantic = "==1.*" + +[package.source] +type = "git" +url = "https://github.com/brewblox/brewblox-service.git" +reference = "develop" +resolved_reference = "318788407a50ba5ee355efd9bfe33ed545c6cd9f" [[package]] name = "cffi" version = "1.15.1" description = "Foreign Function Interface for Python calling C code." +category = "main" optional = false python-versions = "*" files = [ @@ -341,92 +362,94 @@ pycparser = "*" [[package]] name = "charset-normalizer" -version = "3.1.0" +version = "3.2.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, - {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, + {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, + {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, ] [[package]] name = "ciso8601" version = "2.3.0" description = "Fast ISO8601 date time parser for Python written in C" +category = "main" optional = false python-versions = "*" files = [ @@ -481,6 +504,7 @@ files = [ name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -492,6 +516,7 @@ files = [ name = "coverage" version = "7.2.7" description = "Code coverage measurement for Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -567,6 +592,7 @@ toml = ["tomli"] name = "cryptography" version = "40.0.1" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -608,6 +634,7 @@ tox = ["tox"] name = "debugpy" version = "1.6.7" description = "An implementation of the Debug Adapter Protocol for Python" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -635,6 +662,7 @@ files = [ name = "ecdsa" version = "0.18.0" description = "ECDSA cryptographic signature library (pure python)" +category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -653,6 +681,7 @@ gmpy2 = ["gmpy2"] name = "esptool" version = "4.6.2" description = "A serial utility to communicate & flash code to Espressif chips." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -675,6 +704,7 @@ hsm = ["python-pkcs11"] name = "exceptiongroup" version = "1.1.2" description = "Backport of PEP 654 (exception groups)" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -687,24 +717,26 @@ test = ["pytest (>=6)"] [[package]] name = "flake8" -version = "6.0.0" +version = "6.1.0" description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" optional = false python-versions = ">=3.8.1" files = [ - {file = "flake8-6.0.0-py2.py3-none-any.whl", hash = "sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7"}, - {file = "flake8-6.0.0.tar.gz", hash = "sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181"}, + {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, + {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, ] [package.dependencies] mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.10.0,<2.11.0" -pyflakes = ">=3.0.0,<3.1.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.1.0,<3.2.0" [[package]] name = "flake8-quotes" version = "3.3.2" description = "Flake8 lint for quotes." +category = "dev" optional = false python-versions = "*" files = [ @@ -716,148 +748,138 @@ flake8 = "*" [[package]] name = "frozenlist" -version = "1.3.3" +version = "1.4.0" description = "A list-like structure which implements collections.abc.MutableSequence" +category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "frozenlist-1.3.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff8bf625fe85e119553b5383ba0fb6aa3d0ec2ae980295aaefa552374926b3f4"}, - {file = "frozenlist-1.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dfbac4c2dfcc082fcf8d942d1e49b6aa0766c19d3358bd86e2000bf0fa4a9cf0"}, - {file = "frozenlist-1.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b1c63e8d377d039ac769cd0926558bb7068a1f7abb0f003e3717ee003ad85530"}, - {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fdfc24dcfce5b48109867c13b4cb15e4660e7bd7661741a391f821f23dfdca7"}, - {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c926450857408e42f0bbc295e84395722ce74bae69a3b2aa2a65fe22cb14b99"}, - {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1841e200fdafc3d51f974d9d377c079a0694a8f06de2e67b48150328d66d5483"}, - {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f470c92737afa7d4c3aacc001e335062d582053d4dbe73cda126f2d7031068dd"}, - {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:783263a4eaad7c49983fe4b2e7b53fa9770c136c270d2d4bbb6d2192bf4d9caf"}, - {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:924620eef691990dfb56dc4709f280f40baee568c794b5c1885800c3ecc69816"}, - {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae4dc05c465a08a866b7a1baf360747078b362e6a6dbeb0c57f234db0ef88ae0"}, - {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:bed331fe18f58d844d39ceb398b77d6ac0b010d571cba8267c2e7165806b00ce"}, - {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:02c9ac843e3390826a265e331105efeab489ffaf4dd86384595ee8ce6d35ae7f"}, - {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9545a33965d0d377b0bc823dcabf26980e77f1b6a7caa368a365a9497fb09420"}, - {file = "frozenlist-1.3.3-cp310-cp310-win32.whl", hash = "sha256:d5cd3ab21acbdb414bb6c31958d7b06b85eeb40f66463c264a9b343a4e238642"}, - {file = "frozenlist-1.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:b756072364347cb6aa5b60f9bc18e94b2f79632de3b0190253ad770c5df17db1"}, - {file = "frozenlist-1.3.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b4395e2f8d83fbe0c627b2b696acce67868793d7d9750e90e39592b3626691b7"}, - {file = "frozenlist-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14143ae966a6229350021384870458e4777d1eae4c28d1a7aa47f24d030e6678"}, - {file = "frozenlist-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5d8860749e813a6f65bad8285a0520607c9500caa23fea6ee407e63debcdbef6"}, - {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23d16d9f477bb55b6154654e0e74557040575d9d19fe78a161bd33d7d76808e8"}, - {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb82dbba47a8318e75f679690190c10a5e1f447fbf9df41cbc4c3afd726d88cb"}, - {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9309869032abb23d196cb4e4db574232abe8b8be1339026f489eeb34a4acfd91"}, - {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a97b4fe50b5890d36300820abd305694cb865ddb7885049587a5678215782a6b"}, - {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c188512b43542b1e91cadc3c6c915a82a5eb95929134faf7fd109f14f9892ce4"}, - {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:303e04d422e9b911a09ad499b0368dc551e8c3cd15293c99160c7f1f07b59a48"}, - {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0771aed7f596c7d73444c847a1c16288937ef988dc04fb9f7be4b2aa91db609d"}, - {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:66080ec69883597e4d026f2f71a231a1ee9887835902dbe6b6467d5a89216cf6"}, - {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:41fe21dc74ad3a779c3d73a2786bdf622ea81234bdd4faf90b8b03cad0c2c0b4"}, - {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f20380df709d91525e4bee04746ba612a4df0972c1b8f8e1e8af997e678c7b81"}, - {file = "frozenlist-1.3.3-cp311-cp311-win32.whl", hash = "sha256:f30f1928162e189091cf4d9da2eac617bfe78ef907a761614ff577ef4edfb3c8"}, - {file = "frozenlist-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:a6394d7dadd3cfe3f4b3b186e54d5d8504d44f2d58dcc89d693698e8b7132b32"}, - {file = "frozenlist-1.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8df3de3a9ab8325f94f646609a66cbeeede263910c5c0de0101079ad541af332"}, - {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0693c609e9742c66ba4870bcee1ad5ff35462d5ffec18710b4ac89337ff16e27"}, - {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd4210baef299717db0a600d7a3cac81d46ef0e007f88c9335db79f8979c0d3d"}, - {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:394c9c242113bfb4b9aa36e2b80a05ffa163a30691c7b5a29eba82e937895d5e"}, - {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6327eb8e419f7d9c38f333cde41b9ae348bec26d840927332f17e887a8dcb70d"}, - {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e24900aa13212e75e5b366cb9065e78bbf3893d4baab6052d1aca10d46d944c"}, - {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3843f84a6c465a36559161e6c59dce2f2ac10943040c2fd021cfb70d58c4ad56"}, - {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:84610c1502b2461255b4c9b7d5e9c48052601a8957cd0aea6ec7a7a1e1fb9420"}, - {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:c21b9aa40e08e4f63a2f92ff3748e6b6c84d717d033c7b3438dd3123ee18f70e"}, - {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:efce6ae830831ab6a22b9b4091d411698145cb9b8fc869e1397ccf4b4b6455cb"}, - {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:40de71985e9042ca00b7953c4f41eabc3dc514a2d1ff534027f091bc74416401"}, - {file = "frozenlist-1.3.3-cp37-cp37m-win32.whl", hash = "sha256:180c00c66bde6146a860cbb81b54ee0df350d2daf13ca85b275123bbf85de18a"}, - {file = "frozenlist-1.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9bbbcedd75acdfecf2159663b87f1bb5cfc80e7cd99f7ddd9d66eb98b14a8411"}, - {file = "frozenlist-1.3.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:034a5c08d36649591be1cbb10e09da9f531034acfe29275fc5454a3b101ce41a"}, - {file = "frozenlist-1.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba64dc2b3b7b158c6660d49cdb1d872d1d0bf4e42043ad8d5006099479a194e5"}, - {file = "frozenlist-1.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:47df36a9fe24054b950bbc2db630d508cca3aa27ed0566c0baf661225e52c18e"}, - {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:008a054b75d77c995ea26629ab3a0c0d7281341f2fa7e1e85fa6153ae29ae99c"}, - {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:841ea19b43d438a80b4de62ac6ab21cfe6827bb8a9dc62b896acc88eaf9cecba"}, - {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e235688f42b36be2b6b06fc37ac2126a73b75fb8d6bc66dd632aa35286238703"}, - {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca713d4af15bae6e5d79b15c10c8522859a9a89d3b361a50b817c98c2fb402a2"}, - {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ac5995f2b408017b0be26d4a1d7c61bce106ff3d9e3324374d66b5964325448"}, - {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4ae8135b11652b08a8baf07631d3ebfe65a4c87909dbef5fa0cdde440444ee4"}, - {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4ea42116ceb6bb16dbb7d526e242cb6747b08b7710d9782aa3d6732bd8d27649"}, - {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:810860bb4bdce7557bc0febb84bbd88198b9dbc2022d8eebe5b3590b2ad6c842"}, - {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ee78feb9d293c323b59a6f2dd441b63339a30edf35abcb51187d2fc26e696d13"}, - {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0af2e7c87d35b38732e810befb9d797a99279cbb85374d42ea61c1e9d23094b3"}, - {file = "frozenlist-1.3.3-cp38-cp38-win32.whl", hash = "sha256:899c5e1928eec13fd6f6d8dc51be23f0d09c5281e40d9cf4273d188d9feeaf9b"}, - {file = "frozenlist-1.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:7f44e24fa70f6fbc74aeec3e971f60a14dde85da364aa87f15d1be94ae75aeef"}, - {file = "frozenlist-1.3.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2b07ae0c1edaa0a36339ec6cce700f51b14a3fc6545fdd32930d2c83917332cf"}, - {file = "frozenlist-1.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ebb86518203e12e96af765ee89034a1dbb0c3c65052d1b0c19bbbd6af8a145e1"}, - {file = "frozenlist-1.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5cf820485f1b4c91e0417ea0afd41ce5cf5965011b3c22c400f6d144296ccbc0"}, - {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c11e43016b9024240212d2a65043b70ed8dfd3b52678a1271972702d990ac6d"}, - {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8fa3c6e3305aa1146b59a09b32b2e04074945ffcfb2f0931836d103a2c38f936"}, - {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:352bd4c8c72d508778cf05ab491f6ef36149f4d0cb3c56b1b4302852255d05d5"}, - {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65a5e4d3aa679610ac6e3569e865425b23b372277f89b5ef06cf2cdaf1ebf22b"}, - {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e2c1185858d7e10ff045c496bbf90ae752c28b365fef2c09cf0fa309291669"}, - {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f163d2fd041c630fed01bc48d28c3ed4a3b003c00acd396900e11ee5316b56bb"}, - {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:05cdb16d09a0832eedf770cb7bd1fe57d8cf4eaf5aced29c4e41e3f20b30a784"}, - {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:8bae29d60768bfa8fb92244b74502b18fae55a80eac13c88eb0b496d4268fd2d"}, - {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:eedab4c310c0299961ac285591acd53dc6723a1ebd90a57207c71f6e0c2153ab"}, - {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3bbdf44855ed8f0fbcd102ef05ec3012d6a4fd7c7562403f76ce6a52aeffb2b1"}, - {file = "frozenlist-1.3.3-cp39-cp39-win32.whl", hash = "sha256:efa568b885bca461f7c7b9e032655c0c143d305bf01c30caf6db2854a4532b38"}, - {file = "frozenlist-1.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:cfe33efc9cb900a4c46f91a5ceba26d6df370ffddd9ca386eb1d4f0ad97b9ea9"}, - {file = "frozenlist-1.3.3.tar.gz", hash = "sha256:58bcc55721e8a90b88332d6cd441261ebb22342e238296bb330968952fbb3a6a"}, + {file = "frozenlist-1.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:764226ceef3125e53ea2cb275000e309c0aa5464d43bd72abd661e27fffc26ab"}, + {file = "frozenlist-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d6484756b12f40003c6128bfcc3fa9f0d49a687e171186c2d85ec82e3758c559"}, + {file = "frozenlist-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9ac08e601308e41eb533f232dbf6b7e4cea762f9f84f6357136eed926c15d12c"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d081f13b095d74b67d550de04df1c756831f3b83dc9881c38985834387487f1b"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71932b597f9895f011f47f17d6428252fc728ba2ae6024e13c3398a087c2cdea"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:981b9ab5a0a3178ff413bca62526bb784249421c24ad7381e39d67981be2c326"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e41f3de4df3e80de75845d3e743b3f1c4c8613c3997a912dbf0229fc61a8b963"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6918d49b1f90821e93069682c06ffde41829c346c66b721e65a5c62b4bab0300"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e5c8764c7829343d919cc2dfc587a8db01c4f70a4ebbc49abde5d4b158b007b"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8d0edd6b1c7fb94922bf569c9b092ee187a83f03fb1a63076e7774b60f9481a8"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e29cda763f752553fa14c68fb2195150bfab22b352572cb36c43c47bedba70eb"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:0c7c1b47859ee2cac3846fde1c1dc0f15da6cec5a0e5c72d101e0f83dcb67ff9"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:901289d524fdd571be1c7be054f48b1f88ce8dddcbdf1ec698b27d4b8b9e5d62"}, + {file = "frozenlist-1.4.0-cp310-cp310-win32.whl", hash = "sha256:1a0848b52815006ea6596c395f87449f693dc419061cc21e970f139d466dc0a0"}, + {file = "frozenlist-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:b206646d176a007466358aa21d85cd8600a415c67c9bd15403336c331a10d956"}, + {file = "frozenlist-1.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:de343e75f40e972bae1ef6090267f8260c1446a1695e77096db6cfa25e759a95"}, + {file = "frozenlist-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad2a9eb6d9839ae241701d0918f54c51365a51407fd80f6b8289e2dfca977cc3"}, + {file = "frozenlist-1.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bd7bd3b3830247580de99c99ea2a01416dfc3c34471ca1298bccabf86d0ff4dc"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdf1847068c362f16b353163391210269e4f0569a3c166bc6a9f74ccbfc7e839"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38461d02d66de17455072c9ba981d35f1d2a73024bee7790ac2f9e361ef1cd0c"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5a32087d720c608f42caed0ef36d2b3ea61a9d09ee59a5142d6070da9041b8f"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd65632acaf0d47608190a71bfe46b209719bf2beb59507db08ccdbe712f969b"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261b9f5d17cac914531331ff1b1d452125bf5daa05faf73b71d935485b0c510b"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b89ac9768b82205936771f8d2eb3ce88503b1556324c9f903e7156669f521472"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:008eb8b31b3ea6896da16c38c1b136cb9fec9e249e77f6211d479db79a4eaf01"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e74b0506fa5aa5598ac6a975a12aa8928cbb58e1f5ac8360792ef15de1aa848f"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:490132667476f6781b4c9458298b0c1cddf237488abd228b0b3650e5ecba7467"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:76d4711f6f6d08551a7e9ef28c722f4a50dd0fc204c56b4bcd95c6cc05ce6fbb"}, + {file = "frozenlist-1.4.0-cp311-cp311-win32.whl", hash = "sha256:a02eb8ab2b8f200179b5f62b59757685ae9987996ae549ccf30f983f40602431"}, + {file = "frozenlist-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:515e1abc578dd3b275d6a5114030b1330ba044ffba03f94091842852f806f1c1"}, + {file = "frozenlist-1.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f0ed05f5079c708fe74bf9027e95125334b6978bf07fd5ab923e9e55e5fbb9d3"}, + {file = "frozenlist-1.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ca265542ca427bf97aed183c1676e2a9c66942e822b14dc6e5f42e038f92a503"}, + {file = "frozenlist-1.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:491e014f5c43656da08958808588cc6c016847b4360e327a62cb308c791bd2d9"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17ae5cd0f333f94f2e03aaf140bb762c64783935cc764ff9c82dff626089bebf"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e78fb68cf9c1a6aa4a9a12e960a5c9dfbdb89b3695197aa7064705662515de2"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5655a942f5f5d2c9ed93d72148226d75369b4f6952680211972a33e59b1dfdc"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c11b0746f5d946fecf750428a95f3e9ebe792c1ee3b1e96eeba145dc631a9672"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e66d2a64d44d50d2543405fb183a21f76b3b5fd16f130f5c99187c3fb4e64919"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:88f7bc0fcca81f985f78dd0fa68d2c75abf8272b1f5c323ea4a01a4d7a614efc"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5833593c25ac59ede40ed4de6d67eb42928cca97f26feea219f21d0ed0959b79"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:fec520865f42e5c7f050c2a79038897b1c7d1595e907a9e08e3353293ffc948e"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:b826d97e4276750beca7c8f0f1a4938892697a6bcd8ec8217b3312dad6982781"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ceb6ec0a10c65540421e20ebd29083c50e6d1143278746a4ef6bcf6153171eb8"}, + {file = "frozenlist-1.4.0-cp38-cp38-win32.whl", hash = "sha256:2b8bcf994563466db019fab287ff390fffbfdb4f905fc77bc1c1d604b1c689cc"}, + {file = "frozenlist-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:a6c8097e01886188e5be3e6b14e94ab365f384736aa1fca6a0b9e35bd4a30bc7"}, + {file = "frozenlist-1.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6c38721585f285203e4b4132a352eb3daa19121a035f3182e08e437cface44bf"}, + {file = "frozenlist-1.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a0c6da9aee33ff0b1a451e867da0c1f47408112b3391dd43133838339e410963"}, + {file = "frozenlist-1.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:93ea75c050c5bb3d98016b4ba2497851eadf0ac154d88a67d7a6816206f6fa7f"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f61e2dc5ad442c52b4887f1fdc112f97caeff4d9e6ebe78879364ac59f1663e1"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa384489fefeb62321b238e64c07ef48398fe80f9e1e6afeff22e140e0850eef"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10ff5faaa22786315ef57097a279b833ecab1a0bfb07d604c9cbb1c4cdc2ed87"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:007df07a6e3eb3e33e9a1fe6a9db7af152bbd8a185f9aaa6ece10a3529e3e1c6"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f4f399d28478d1f604c2ff9119907af9726aed73680e5ed1ca634d377abb087"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c5374b80521d3d3f2ec5572e05adc94601985cc526fb276d0c8574a6d749f1b3"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ce31ae3e19f3c902de379cf1323d90c649425b86de7bbdf82871b8a2a0615f3d"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7211ef110a9194b6042449431e08c4d80c0481e5891e58d429df5899690511c2"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:556de4430ce324c836789fa4560ca62d1591d2538b8ceb0b4f68fb7b2384a27a"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7645a8e814a3ee34a89c4a372011dcd817964ce8cb273c8ed6119d706e9613e3"}, + {file = "frozenlist-1.4.0-cp39-cp39-win32.whl", hash = "sha256:19488c57c12d4e8095a922f328df3f179c820c212940a498623ed39160bc3c2f"}, + {file = "frozenlist-1.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:6221d84d463fb110bdd7619b69cb43878a11d51cbb9394ae3105d082d5199167"}, + {file = "frozenlist-1.4.0.tar.gz", hash = "sha256:09163bdf0b2907454042edb19f887c6d33806adc71fbd54afc14908bfdc22251"}, ] [[package]] name = "grpcio" -version = "1.56.0" +version = "1.56.2" description = "HTTP/2-based RPC framework" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "grpcio-1.56.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:fb34ace11419f1ae321c36ccaa18d81cd3f20728cd191250be42949d6845bb2d"}, - {file = "grpcio-1.56.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:008767c0aed4899e657b50f2e0beacbabccab51359eba547f860e7c55f2be6ba"}, - {file = "grpcio-1.56.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:17f47aeb9be0da5337f9ff33ebb8795899021e6c0741ee68bd69774a7804ca86"}, - {file = "grpcio-1.56.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43c50d810cc26349b093bf2cfe86756ab3e9aba3e7e681d360930c1268e1399a"}, - {file = "grpcio-1.56.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:187b8f71bad7d41eea15e0c9812aaa2b87adfb343895fffb704fb040ca731863"}, - {file = "grpcio-1.56.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:881575f240eb5db72ddca4dc5602898c29bc082e0d94599bf20588fb7d1ee6a0"}, - {file = "grpcio-1.56.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c243b158dd7585021d16c50498c4b2ec0a64a6119967440c5ff2d8c89e72330e"}, - {file = "grpcio-1.56.0-cp310-cp310-win32.whl", hash = "sha256:8b3b2c7b5feef90bc9a5fa1c7f97637e55ec3e76460c6d16c3013952ee479cd9"}, - {file = "grpcio-1.56.0-cp310-cp310-win_amd64.whl", hash = "sha256:03a80451530fd3b8b155e0c4480434f6be669daf7ecba56f73ef98f94222ee01"}, - {file = "grpcio-1.56.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:64bd3abcf9fb4a9fa4ede8d0d34686314a7075f62a1502217b227991d9ca4245"}, - {file = "grpcio-1.56.0-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:fdc3a895791af4addbb826808d4c9c35917c59bb5c430d729f44224e51c92d61"}, - {file = "grpcio-1.56.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:4f84a6fd4482e5fe73b297d4874b62a535bc75dc6aec8e9fe0dc88106cd40397"}, - {file = "grpcio-1.56.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14e70b4dda3183abea94c72d41d5930c333b21f8561c1904a372d80370592ef3"}, - {file = "grpcio-1.56.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b5ce42a5ebe3e04796246ba50357f1813c44a6efe17a37f8dc7a5c470377312"}, - {file = "grpcio-1.56.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8219f17baf069fe8e42bd8ca0b312b875595e43a70cabf397be4fda488e2f27d"}, - {file = "grpcio-1.56.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:defdd14b518e6e468466f799aaa69db0355bca8d3a5ea75fb912d28ba6f8af31"}, - {file = "grpcio-1.56.0-cp311-cp311-win32.whl", hash = "sha256:50f4daa698835accbbcc60e61e0bc29636c0156ddcafb3891c987e533a0031ba"}, - {file = "grpcio-1.56.0-cp311-cp311-win_amd64.whl", hash = "sha256:59c4e606993a47146fbeaf304b9e78c447f5b9ee5641cae013028c4cca784617"}, - {file = "grpcio-1.56.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:b1f4b6f25a87d80b28dd6d02e87d63fe1577fe6d04a60a17454e3f8077a38279"}, - {file = "grpcio-1.56.0-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:c2148170e01d464d41011a878088444c13413264418b557f0bdcd1bf1b674a0e"}, - {file = "grpcio-1.56.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:0409de787ebbf08c9d2bca2bcc7762c1efe72eada164af78b50567a8dfc7253c"}, - {file = "grpcio-1.56.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66f0369d27f4c105cd21059d635860bb2ea81bd593061c45fb64875103f40e4a"}, - {file = "grpcio-1.56.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38fdf5bd0a1c754ce6bf9311a3c2c7ebe56e88b8763593316b69e0e9a56af1de"}, - {file = "grpcio-1.56.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:79d4c5911d12a7aa671e5eb40cbb50a830396525014d2d6f254ea2ba180ce637"}, - {file = "grpcio-1.56.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5d2fc471668a7222e213f86ef76933b18cdda6a51ea1322034478df8c6519959"}, - {file = "grpcio-1.56.0-cp37-cp37m-win_amd64.whl", hash = "sha256:991224fd485e088d3cb5e34366053691a4848a6b7112b8f5625a411305c26691"}, - {file = "grpcio-1.56.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:c6f36621aabecbaff3e70c4d1d924c76c8e6a7ffec60c331893640a4af0a8037"}, - {file = "grpcio-1.56.0-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:1eadd6de258901929223f422ffed7f8b310c0323324caf59227f9899ea1b1674"}, - {file = "grpcio-1.56.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:72836b5a1d4f508ffbcfe35033d027859cc737972f9dddbe33fb75d687421e2e"}, - {file = "grpcio-1.56.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f92a99ab0c7772fb6859bf2e4f44ad30088d18f7c67b83205297bfb229e0d2cf"}, - {file = "grpcio-1.56.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa08affbf672d051cd3da62303901aeb7042a2c188c03b2c2a2d346fc5e81c14"}, - {file = "grpcio-1.56.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2db108b4c8e29c145e95b0226973a66d73ae3e3e7fae00329294af4e27f1c42"}, - {file = "grpcio-1.56.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8674fdbd28266d8efbcddacf4ec3643f76fe6376f73283fd63a8374c14b0ef7c"}, - {file = "grpcio-1.56.0-cp38-cp38-win32.whl", hash = "sha256:bd55f743e654fb050c665968d7ec2c33f03578a4bbb163cfce38024775ff54cc"}, - {file = "grpcio-1.56.0-cp38-cp38-win_amd64.whl", hash = "sha256:c63bc5ac6c7e646c296fed9139097ae0f0e63f36f0864d7ce431cce61fe0118a"}, - {file = "grpcio-1.56.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:c0bc9dda550785d23f4f025be614b7faa8d0293e10811f0f8536cf50435b7a30"}, - {file = "grpcio-1.56.0-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:d596408bab632ec7b947761e83ce6b3e7632e26b76d64c239ba66b554b7ee286"}, - {file = "grpcio-1.56.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:76b6e6e1ee9bda32e6e933efd61c512e9a9f377d7c580977f090d1a9c78cca44"}, - {file = "grpcio-1.56.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7beb84ebd0a3f732625124b73969d12b7350c5d9d64ddf81ae739bbc63d5b1ed"}, - {file = "grpcio-1.56.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83ec714bbbe9b9502177c842417fde39f7a267031e01fa3cd83f1ca49688f537"}, - {file = "grpcio-1.56.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4feee75565d1b5ab09cb3a5da672b84ca7f6dd80ee07a50f5537207a9af543a4"}, - {file = "grpcio-1.56.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b4638a796778329cc8e142e4f57c705adb286b3ba64e00b0fa91eeb919611be8"}, - {file = "grpcio-1.56.0-cp39-cp39-win32.whl", hash = "sha256:437af5a7673bca89c4bc0a993382200592d104dd7bf55eddcd141cef91f40bab"}, - {file = "grpcio-1.56.0-cp39-cp39-win_amd64.whl", hash = "sha256:4241a1c2c76e748023c834995cd916570e7180ee478969c2d79a60ce007bc837"}, - {file = "grpcio-1.56.0.tar.gz", hash = "sha256:4c08ee21b3d10315b8dc26f6c13917b20ed574cdbed2d2d80c53d5508fdcc0f2"}, + {file = "grpcio-1.56.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:bf0b9959e673505ee5869950642428046edb91f99942607c2ecf635f8a4b31c9"}, + {file = "grpcio-1.56.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:5144feb20fe76e73e60c7d73ec3bf54f320247d1ebe737d10672480371878b48"}, + {file = "grpcio-1.56.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:a72797549935c9e0b9bc1def1768c8b5a709538fa6ab0678e671aec47ebfd55e"}, + {file = "grpcio-1.56.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3f3237a57e42f79f1e560726576aedb3a7ef931f4e3accb84ebf6acc485d316"}, + {file = "grpcio-1.56.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:900bc0096c2ca2d53f2e5cebf98293a7c32f532c4aeb926345e9747452233950"}, + {file = "grpcio-1.56.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:97e0efaebbfd222bcaac2f1735c010c1d3b167112d9d237daebbeedaaccf3d1d"}, + {file = "grpcio-1.56.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c0c85c5cbe8b30a32fa6d802588d55ffabf720e985abe9590c7c886919d875d4"}, + {file = "grpcio-1.56.2-cp310-cp310-win32.whl", hash = "sha256:06e84ad9ae7668a109e970c7411e7992751a116494cba7c4fb877656527f9a57"}, + {file = "grpcio-1.56.2-cp310-cp310-win_amd64.whl", hash = "sha256:10954662f77dc36c9a1fb5cc4a537f746580d6b5734803be1e587252682cda8d"}, + {file = "grpcio-1.56.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:c435f5ce1705de48e08fcbcfaf8aee660d199c90536e3e06f2016af7d6a938dd"}, + {file = "grpcio-1.56.2-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:6108e5933eb8c22cd3646e72d5b54772c29f57482fd4c41a0640aab99eb5071d"}, + {file = "grpcio-1.56.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:8391cea5ce72f4a12368afd17799474015d5d3dc00c936a907eb7c7eaaea98a5"}, + {file = "grpcio-1.56.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:750de923b456ca8c0f1354d6befca45d1f3b3a789e76efc16741bd4132752d95"}, + {file = "grpcio-1.56.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fda2783c12f553cdca11c08e5af6eecbd717280dc8fbe28a110897af1c15a88c"}, + {file = "grpcio-1.56.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9e04d4e4cfafa7c5264e535b5d28e786f0571bea609c3f0aaab13e891e933e9c"}, + {file = "grpcio-1.56.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:89a49cc5ad08a38b6141af17e00d1dd482dc927c7605bc77af457b5a0fca807c"}, + {file = "grpcio-1.56.2-cp311-cp311-win32.whl", hash = "sha256:6a007a541dff984264981fbafeb052bfe361db63578948d857907df9488d8774"}, + {file = "grpcio-1.56.2-cp311-cp311-win_amd64.whl", hash = "sha256:af4063ef2b11b96d949dccbc5a987272f38d55c23c4c01841ea65a517906397f"}, + {file = "grpcio-1.56.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:a6ff459dac39541e6a2763a4439c4ca6bc9ecb4acc05a99b79246751f9894756"}, + {file = "grpcio-1.56.2-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:f20fd21f7538f8107451156dd1fe203300b79a9ddceba1ee0ac8132521a008ed"}, + {file = "grpcio-1.56.2-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:d1fbad1f9077372b6587ec589c1fc120b417b6c8ad72d3e3cc86bbbd0a3cee93"}, + {file = "grpcio-1.56.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ee26e9dfb3996aff7c870f09dc7ad44a5f6732b8bdb5a5f9905737ac6fd4ef1"}, + {file = "grpcio-1.56.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4c60abd950d6de3e4f1ddbc318075654d275c29c846ab6a043d6ed2c52e4c8c"}, + {file = "grpcio-1.56.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1c31e52a04e62c8577a7bf772b3e7bed4df9c9e0dd90f92b6ffa07c16cab63c9"}, + {file = "grpcio-1.56.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:345356b307cce5d14355e8e055b4ca5f99bc857c33a3dc1ddbc544fca9cd0475"}, + {file = "grpcio-1.56.2-cp37-cp37m-win_amd64.whl", hash = "sha256:42e63904ee37ae46aa23de50dac8b145b3596f43598fa33fe1098ab2cbda6ff5"}, + {file = "grpcio-1.56.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:7c5ede2e2558f088c49a1ddda19080e4c23fb5d171de80a726b61b567e3766ed"}, + {file = "grpcio-1.56.2-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:33971197c47965cc1d97d78d842163c283e998223b151bab0499b951fd2c0b12"}, + {file = "grpcio-1.56.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d39f5d4af48c138cb146763eda14eb7d8b3ccbbec9fe86fb724cd16e0e914c64"}, + {file = "grpcio-1.56.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ded637176addc1d3eef35331c39acc598bac550d213f0a1bedabfceaa2244c87"}, + {file = "grpcio-1.56.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c90da4b124647547a68cf2f197174ada30c7bb9523cb976665dfd26a9963d328"}, + {file = "grpcio-1.56.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3ccb621749a81dc7755243665a70ce45536ec413ef5818e013fe8dfbf5aa497b"}, + {file = "grpcio-1.56.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4eb37dd8dd1aa40d601212afa27ca5be255ba792e2e0b24d67b8af5e012cdb7d"}, + {file = "grpcio-1.56.2-cp38-cp38-win32.whl", hash = "sha256:ddb4a6061933bd9332b74eac0da25f17f32afa7145a33a0f9711ad74f924b1b8"}, + {file = "grpcio-1.56.2-cp38-cp38-win_amd64.whl", hash = "sha256:8940d6de7068af018dfa9a959a3510e9b7b543f4c405e88463a1cbaa3b2b379a"}, + {file = "grpcio-1.56.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:51173e8fa6d9a2d85c14426bdee5f5c4a0654fd5fddcc21fe9d09ab0f6eb8b35"}, + {file = "grpcio-1.56.2-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:373b48f210f43327a41e397391715cd11cfce9ded2fe76a5068f9bacf91cc226"}, + {file = "grpcio-1.56.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:42a3bbb2bc07aef72a7d97e71aabecaf3e4eb616d39e5211e2cfe3689de860ca"}, + {file = "grpcio-1.56.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5344be476ac37eb9c9ad09c22f4ea193c1316bf074f1daf85bddb1b31fda5116"}, + {file = "grpcio-1.56.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3fa3ab0fb200a2c66493828ed06ccd1a94b12eddbfb985e7fd3e5723ff156c6"}, + {file = "grpcio-1.56.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b975b85d1d5efc36cf8b237c5f3849b64d1ba33d6282f5e991f28751317504a1"}, + {file = "grpcio-1.56.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cbdf2c498e077282cd427cfd88bdce4668019791deef0be8155385ab2ba7837f"}, + {file = "grpcio-1.56.2-cp39-cp39-win32.whl", hash = "sha256:139f66656a762572ae718fa0d1f2dce47c05e9fbf7a16acd704c354405b97df9"}, + {file = "grpcio-1.56.2-cp39-cp39-win_amd64.whl", hash = "sha256:830215173ad45d670140ff99aac3b461f9be9a6b11bee1a17265aaaa746a641a"}, + {file = "grpcio-1.56.2.tar.gz", hash = "sha256:0ff789ae7d8ddd76d2ac02e7d13bfef6fc4928ac01e1dcaa182be51b6bcc0aaa"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.56.0)"] +protobuf = ["grpcio-tools (>=1.56.2)"] [[package]] name = "grpcio-tools" version = "1.48.2" description = "Protobuf code generator for gRPC" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -918,6 +940,7 @@ setuptools = "*" name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -929,6 +952,7 @@ files = [ name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -938,19 +962,21 @@ files = [ [[package]] name = "invoke" -version = "2.1.3" +version = "2.2.0" description = "Pythonic task execution" +category = "dev" optional = false python-versions = ">=3.6" files = [ - {file = "invoke-2.1.3-py3-none-any.whl", hash = "sha256:51e86a08d964160e01c44eccd22f50b25842bd96a9c63c11177032594cb86740"}, - {file = "invoke-2.1.3.tar.gz", hash = "sha256:a3b15d52d50bbabd851b8a39582c772180b614000fa1612b4d92484d54d38c6b"}, + {file = "invoke-2.2.0-py3-none-any.whl", hash = "sha256:6ea924cc53d4f78e3d98bc436b08069a03077e6f85ad1ddaa8a116d7dad15820"}, + {file = "invoke-2.2.0.tar.gz", hash = "sha256:ee6cbb101af1a859c7fe84f2a264c059020b0cb7fe3535f9424300ab568f6bd5"}, ] [[package]] name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -968,6 +994,7 @@ i18n = ["Babel (>=2.7)"] name = "markupsafe" version = "2.1.3" description = "Safely add untrusted strings to HTML/XML markup." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1027,6 +1054,7 @@ files = [ name = "mccabe" version = "0.7.0" description = "McCabe checker, plugin for flake8" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1038,6 +1066,7 @@ files = [ name = "multidict" version = "6.0.4" description = "multidict implementation" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1121,6 +1150,7 @@ files = [ name = "netifaces" version = "0.11.0" description = "Portable network interface information." +category = "main" optional = false python-versions = "*" files = [ @@ -1160,6 +1190,7 @@ files = [ name = "packaging" version = "23.1" description = "Core utilities for Python packages" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1171,6 +1202,7 @@ files = [ name = "paho-mqtt" version = "1.6.1" description = "MQTT version 5.0/3.1.1 client class" +category = "main" optional = false python-versions = "*" files = [ @@ -1184,6 +1216,7 @@ proxy = ["PySocks"] name = "pint" version = "0.19.2" description = "Physical quantities module" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1199,6 +1232,7 @@ uncertainties = ["uncertainties (>=3.1.6)"] name = "pluggy" version = "1.2.0" description = "plugin and hook calling mechanisms for python" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1214,6 +1248,7 @@ testing = ["pytest", "pytest-benchmark"] name = "protobuf" version = "3.20.3" description = "Protocol Buffers" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1243,19 +1278,21 @@ files = [ [[package]] name = "pycodestyle" -version = "2.10.0" +version = "2.11.0" description = "Python style guide checker" +category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "pycodestyle-2.10.0-py2.py3-none-any.whl", hash = "sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610"}, - {file = "pycodestyle-2.10.0.tar.gz", hash = "sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053"}, + {file = "pycodestyle-2.11.0-py2.py3-none-any.whl", hash = "sha256:5d1013ba8dc7895b548be5afb05740ca82454fd899971563d2ef625d090326f8"}, + {file = "pycodestyle-2.11.0.tar.gz", hash = "sha256:259bcc17857d8a8b3b4a2327324b79e5f020a13c16074670f9c8c8f872ea76d0"}, ] [[package]] name = "pycparser" version = "2.21" description = "C parser in Python" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1265,47 +1302,48 @@ files = [ [[package]] name = "pydantic" -version = "1.10.10" +version = "1.10.12" description = "Data validation and settings management using python type hints" +category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-1.10.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:adad1ee4ab9888f12dac2529276704e719efcf472e38df7813f5284db699b4ec"}, - {file = "pydantic-1.10.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a7db03339893feef2092ff7b1afc9497beed15ebd4af84c3042a74abce02d48"}, - {file = "pydantic-1.10.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67b3714b97ff84b2689654851c2426389bcabfac9080617bcf4306c69db606f6"}, - {file = "pydantic-1.10.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edfdf0a5abc5c9bf2052ebaec20e67abd52e92d257e4f2d30e02c354ed3e6030"}, - {file = "pydantic-1.10.10-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20a3b30fd255eeeb63caa9483502ba96b7795ce5bf895c6a179b3d909d9f53a6"}, - {file = "pydantic-1.10.10-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db4c7f7e60ca6f7d6c1785070f3e5771fcb9b2d88546e334d2f2c3934d949028"}, - {file = "pydantic-1.10.10-cp310-cp310-win_amd64.whl", hash = "sha256:a2d5be50ac4a0976817144c7d653e34df2f9436d15555189f5b6f61161d64183"}, - {file = "pydantic-1.10.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:566a04ba755e8f701b074ffb134ddb4d429f75d5dced3fbd829a527aafe74c71"}, - {file = "pydantic-1.10.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f79db3652ed743309f116ba863dae0c974a41b688242482638b892246b7db21d"}, - {file = "pydantic-1.10.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c62376890b819bebe3c717a9ac841a532988372b7e600e76f75c9f7c128219d5"}, - {file = "pydantic-1.10.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4870f13a4fafd5bc3e93cff3169222534fad867918b188e83ee0496452978437"}, - {file = "pydantic-1.10.10-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:990027e77cda6072a566e433b6962ca3b96b4f3ae8bd54748e9d62a58284d9d7"}, - {file = "pydantic-1.10.10-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8c40964596809eb616d94f9c7944511f620a1103d63d5510440ed2908fc410af"}, - {file = "pydantic-1.10.10-cp311-cp311-win_amd64.whl", hash = "sha256:ea9eebc2ebcba3717e77cdeee3f6203ffc0e78db5f7482c68b1293e8cc156e5e"}, - {file = "pydantic-1.10.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:762aa598f79b4cac2f275d13336b2dd8662febee2a9c450a49a2ab3bec4b385f"}, - {file = "pydantic-1.10.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dab5219659f95e357d98d70577b361383057fb4414cfdb587014a5f5c595f7b"}, - {file = "pydantic-1.10.10-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3d4ee957a727ccb5a36f1b0a6dbd9fad5dedd2a41eada99a8df55c12896e18d"}, - {file = "pydantic-1.10.10-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b69f9138dec566962ec65623c9d57bee44412d2fc71065a5f3ebb3820bdeee96"}, - {file = "pydantic-1.10.10-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7aa75d1bd9cc275cf9782f50f60cddaf74cbaae19b6ada2a28e737edac420312"}, - {file = "pydantic-1.10.10-cp37-cp37m-win_amd64.whl", hash = "sha256:9f62a727f5c590c78c2d12fda302d1895141b767c6488fe623098f8792255fe5"}, - {file = "pydantic-1.10.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:aac218feb4af73db8417ca7518fb3bade4534fcca6e3fb00f84966811dd94450"}, - {file = "pydantic-1.10.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:88546dc10a40b5b52cae87d64666787aeb2878f9a9b37825aedc2f362e7ae1da"}, - {file = "pydantic-1.10.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c41bbaae89e32fc582448e71974de738c055aef5ab474fb25692981a08df808a"}, - {file = "pydantic-1.10.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b71bd504d1573b0b722ae536e8ffb796bedeef978979d076bf206e77dcc55a5"}, - {file = "pydantic-1.10.10-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e088e3865a2270ecbc369924cd7d9fbc565667d9158e7f304e4097ebb9cf98dd"}, - {file = "pydantic-1.10.10-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3403a090db45d4027d2344859d86eb797484dfda0706cf87af79ace6a35274ef"}, - {file = "pydantic-1.10.10-cp38-cp38-win_amd64.whl", hash = "sha256:e0014e29637125f4997c174dd6167407162d7af0da73414a9340461ea8573252"}, - {file = "pydantic-1.10.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9965e49c6905840e526e5429b09e4c154355b6ecc0a2f05492eda2928190311d"}, - {file = "pydantic-1.10.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:748d10ab6089c5d196e1c8be9de48274f71457b01e59736f7a09c9dc34f51887"}, - {file = "pydantic-1.10.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86936c383f7c38fd26d35107eb669c85d8f46dfceae873264d9bab46fe1c7dde"}, - {file = "pydantic-1.10.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a26841be620309a9697f5b1ffc47dce74909e350c5315ccdac7a853484d468a"}, - {file = "pydantic-1.10.10-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:409b810f387610cc7405ab2fa6f62bdf7ea485311845a242ebc0bd0496e7e5ac"}, - {file = "pydantic-1.10.10-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ce937a2a2c020bcad1c9fde02892392a1123de6dda906ddba62bfe8f3e5989a2"}, - {file = "pydantic-1.10.10-cp39-cp39-win_amd64.whl", hash = "sha256:37ebddef68370e6f26243acc94de56d291e01227a67b2ace26ea3543cf53dd5f"}, - {file = "pydantic-1.10.10-py3-none-any.whl", hash = "sha256:a5939ec826f7faec434e2d406ff5e4eaf1716eb1f247d68cd3d0b3612f7b4c8a"}, - {file = "pydantic-1.10.10.tar.gz", hash = "sha256:3b8d5bd97886f9eb59260594207c9f57dce14a6f869c6ceea90188715d29921a"}, + {file = "pydantic-1.10.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a1fcb59f2f355ec350073af41d927bf83a63b50e640f4dbaa01053a28b7a7718"}, + {file = "pydantic-1.10.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b7ccf02d7eb340b216ec33e53a3a629856afe1c6e0ef91d84a4e6f2fb2ca70fe"}, + {file = "pydantic-1.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fb2aa3ab3728d950bcc885a2e9eff6c8fc40bc0b7bb434e555c215491bcf48b"}, + {file = "pydantic-1.10.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:771735dc43cf8383959dc9b90aa281f0b6092321ca98677c5fb6125a6f56d58d"}, + {file = "pydantic-1.10.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ca48477862372ac3770969b9d75f1bf66131d386dba79506c46d75e6b48c1e09"}, + {file = "pydantic-1.10.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a5e7add47a5b5a40c49b3036d464e3c7802f8ae0d1e66035ea16aa5b7a3923ed"}, + {file = "pydantic-1.10.12-cp310-cp310-win_amd64.whl", hash = "sha256:e4129b528c6baa99a429f97ce733fff478ec955513630e61b49804b6cf9b224a"}, + {file = "pydantic-1.10.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b0d191db0f92dfcb1dec210ca244fdae5cbe918c6050b342d619c09d31eea0cc"}, + {file = "pydantic-1.10.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:795e34e6cc065f8f498c89b894a3c6da294a936ee71e644e4bd44de048af1405"}, + {file = "pydantic-1.10.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69328e15cfda2c392da4e713443c7dbffa1505bc9d566e71e55abe14c97ddc62"}, + {file = "pydantic-1.10.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2031de0967c279df0d8a1c72b4ffc411ecd06bac607a212892757db7462fc494"}, + {file = "pydantic-1.10.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ba5b2e6fe6ca2b7e013398bc7d7b170e21cce322d266ffcd57cca313e54fb246"}, + {file = "pydantic-1.10.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2a7bac939fa326db1ab741c9d7f44c565a1d1e80908b3797f7f81a4f86bc8d33"}, + {file = "pydantic-1.10.12-cp311-cp311-win_amd64.whl", hash = "sha256:87afda5539d5140cb8ba9e8b8c8865cb5b1463924d38490d73d3ccfd80896b3f"}, + {file = "pydantic-1.10.12-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:549a8e3d81df0a85226963611950b12d2d334f214436a19537b2efed61b7639a"}, + {file = "pydantic-1.10.12-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:598da88dfa127b666852bef6d0d796573a8cf5009ffd62104094a4fe39599565"}, + {file = "pydantic-1.10.12-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba5c4a8552bff16c61882db58544116d021d0b31ee7c66958d14cf386a5b5350"}, + {file = "pydantic-1.10.12-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c79e6a11a07da7374f46970410b41d5e266f7f38f6a17a9c4823db80dadf4303"}, + {file = "pydantic-1.10.12-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab26038b8375581dc832a63c948f261ae0aa21f1d34c1293469f135fa92972a5"}, + {file = "pydantic-1.10.12-cp37-cp37m-win_amd64.whl", hash = "sha256:e0a16d274b588767602b7646fa05af2782576a6cf1022f4ba74cbb4db66f6ca8"}, + {file = "pydantic-1.10.12-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6a9dfa722316f4acf4460afdf5d41d5246a80e249c7ff475c43a3a1e9d75cf62"}, + {file = "pydantic-1.10.12-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a73f489aebd0c2121ed974054cb2759af8a9f747de120acd2c3394cf84176ccb"}, + {file = "pydantic-1.10.12-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b30bcb8cbfccfcf02acb8f1a261143fab622831d9c0989707e0e659f77a18e0"}, + {file = "pydantic-1.10.12-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fcfb5296d7877af406ba1547dfde9943b1256d8928732267e2653c26938cd9c"}, + {file = "pydantic-1.10.12-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2f9a6fab5f82ada41d56b0602606a5506aab165ca54e52bc4545028382ef1c5d"}, + {file = "pydantic-1.10.12-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dea7adcc33d5d105896401a1f37d56b47d443a2b2605ff8a969a0ed5543f7e33"}, + {file = "pydantic-1.10.12-cp38-cp38-win_amd64.whl", hash = "sha256:1eb2085c13bce1612da8537b2d90f549c8cbb05c67e8f22854e201bde5d98a47"}, + {file = "pydantic-1.10.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ef6c96b2baa2100ec91a4b428f80d8f28a3c9e53568219b6c298c1125572ebc6"}, + {file = "pydantic-1.10.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6c076be61cd0177a8433c0adcb03475baf4ee91edf5a4e550161ad57fc90f523"}, + {file = "pydantic-1.10.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d5a58feb9a39f481eda4d5ca220aa8b9d4f21a41274760b9bc66bfd72595b86"}, + {file = "pydantic-1.10.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5f805d2d5d0a41633651a73fa4ecdd0b3d7a49de4ec3fadf062fe16501ddbf1"}, + {file = "pydantic-1.10.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1289c180abd4bd4555bb927c42ee42abc3aee02b0fb2d1223fb7c6e5bef87dbe"}, + {file = "pydantic-1.10.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5d1197e462e0364906cbc19681605cb7c036f2475c899b6f296104ad42b9f5fb"}, + {file = "pydantic-1.10.12-cp39-cp39-win_amd64.whl", hash = "sha256:fdbdd1d630195689f325c9ef1a12900524dceb503b00a987663ff4f58669b93d"}, + {file = "pydantic-1.10.12-py3-none-any.whl", hash = "sha256:b749a43aa51e32839c9d71dc67eb1e4221bb04af1033a32e3923d46f9effa942"}, + {file = "pydantic-1.10.12.tar.gz", hash = "sha256:0fe8a415cea8f340e7a9af9c54fc71a649b43e8ca3cc732986116b3cb135d303"}, ] [package.dependencies] @@ -1317,19 +1355,21 @@ email = ["email-validator (>=1.0.3)"] [[package]] name = "pyflakes" -version = "3.0.1" +version = "3.1.0" description = "passive checker of Python programs" +category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "pyflakes-3.0.1-py2.py3-none-any.whl", hash = "sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf"}, - {file = "pyflakes-3.0.1.tar.gz", hash = "sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd"}, + {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, + {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, ] [[package]] name = "pyserial" version = "3.5" description = "Python Serial Port Extension" +category = "main" optional = false python-versions = "*" files = [ @@ -1344,6 +1384,7 @@ cp2110 = ["hidapi"] name = "pyserial-asyncio" version = "0.6" description = "Python Serial Port Extension - Asynchronous I/O support" +category = "main" optional = false python-versions = "*" files = [ @@ -1358,6 +1399,7 @@ pyserial = "*" name = "pytest" version = "7.4.0" description = "pytest: simple powerful testing with Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1380,6 +1422,7 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "pytest-aiohttp" version = "1.0.4" description = "Pytest plugin for aiohttp support" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1397,13 +1440,14 @@ testing = ["coverage (==6.2)", "mypy (==0.931)"] [[package]] name = "pytest-asyncio" -version = "0.21.0" +version = "0.21.1" description = "Pytest support for asyncio" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-asyncio-0.21.0.tar.gz", hash = "sha256:2b38a496aef56f56b0e87557ec313e11e1ab9276fc3863f6a7be0f1d0e415e1b"}, - {file = "pytest_asyncio-0.21.0-py3-none-any.whl", hash = "sha256:f2b3366b7cd501a4056858bd39349d5af19742aed2d81660b7998b6341c7eb9c"}, + {file = "pytest-asyncio-0.21.1.tar.gz", hash = "sha256:40a7eae6dded22c7b604986855ea48400ab15b069ae38116e8c01238e9eeb64d"}, + {file = "pytest_asyncio-0.21.1-py3-none-any.whl", hash = "sha256:8666c1c8ac02631d7c51ba282e0c69a8a452b211ffedf2599099845da5c5c37b"}, ] [package.dependencies] @@ -1417,6 +1461,7 @@ testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy name = "pytest-cov" version = "4.1.0" description = "Pytest plugin for measuring coverage." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1435,6 +1480,7 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale name = "pytest-mock" version = "3.11.1" description = "Thin-wrapper around the mock package for easier use with pytest" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1452,6 +1498,7 @@ dev = ["pre-commit", "pytest-asyncio", "tox"] name = "pytimeparse" version = "1.1.8" description = "Time expression parser" +category = "main" optional = false python-versions = "*" files = [ @@ -1461,57 +1508,59 @@ files = [ [[package]] name = "pyyaml" -version = "6.0" +version = "6.0.1" description = "YAML parser and emitter for Python" +category = "main" optional = false python-versions = ">=3.6" files = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, - {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, - {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] [[package]] name = "reedsolo" version = "1.7.0" description = "Pure-Python Reed Solomon encoder/decoder" +category = "main" optional = false python-versions = "*" files = [ @@ -1523,6 +1572,7 @@ files = [ name = "setuptools" version = "68.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1539,6 +1589,7 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs ( name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1550,6 +1601,7 @@ files = [ name = "swagger-ui-bundle" version = "0.0.9" description = "swagger_ui_bundle - swagger-ui files in a pip package" +category = "main" optional = false python-versions = "*" files = [ @@ -1564,6 +1616,7 @@ Jinja2 = ">=2.0" name = "tomli" version = "2.0.1" description = "A lil' TOML parser" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1575,6 +1628,7 @@ files = [ name = "typing-extensions" version = "4.7.1" description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1586,6 +1640,7 @@ files = [ name = "yarl" version = "1.9.2" description = "Yet another URL library" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1672,4 +1727,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.9,<4" -content-hash = "96b324ed69f2a0a9c98abfc0a384c2db063be084d722675b8ded85d3733ac992" +content-hash = "476140ad032a9710b768c19d14f7b5129707a7662d569589b332af729fe48863" diff --git a/pyproject.toml b/pyproject.toml index dc037808..2b71a6ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ reportMissingImports = "information" [tool.poetry.dependencies] python = ">=3.9,<4" -brewblox-service = "^2.1.1" +brewblox-service = {git = "https://github.com/brewblox/brewblox-service.git", rev = "develop"} pyserial-asyncio = "^0.6" protobuf = "<4.0dev" aiofiles = "^0.8.0" @@ -26,7 +26,6 @@ esptool = "^4.0" pytimeparse = "^1.1.8" ciso8601 = "^2.2.0" cryptography = "40.0.1" -pydantic = "==1.*" [tool.poetry.group.dev.dependencies] pytest = "*" diff --git a/test/conftest.py b/test/conftest.py index 609dcab6..55ea3c21 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -6,10 +6,11 @@ import logging import pytest -from brewblox_service import brewblox_logger, features, service +from aiohttp import test_utils +from brewblox_service import brewblox_logger, features, service, testing -from brewblox_devcon_spark.__main__ import create_parser -from brewblox_devcon_spark.models import DiscoveryType, ServiceConfig +from brewblox_devcon_spark.models import (DiscoveryType, ServiceConfig, + ServiceFirmwareIni) LOGGER = brewblox_logger(__name__) @@ -24,33 +25,36 @@ def log_enabled(): @pytest.fixture def app_config() -> ServiceConfig: - return { + return ServiceConfig( # From brewblox_service - 'name': 'test_app', - 'host': 'localhost', - 'port': 1234, - 'debug': True, - 'mqtt_protocol': 'mqtt', - 'mqtt_host': 'eventbus', - 'mqtt_port': 1883, - 'mqtt_path': '/eventbus', - 'history_topic': '/brewcast/history', - 'state_topic': '/brewcast/state', + name='test_app', + host='localhost', + port=1234, + debug=True, + mqtt_protocol='mqtt', + mqtt_host='eventbus', + mqtt_port=1883, + mqtt_path='/eventbus', + history_topic='brewcast/history', + state_topic='brewcast/state', # From brewblox_devcon_spark - 'device_serial': '/dev/TESTEH', - 'device_id': '1234', - 'display_ws_port': 7377, - 'discovery': DiscoveryType.all, - 'simulation': False, - 'mock': True, - 'command_timeout': 10, - 'broadcast_interval': 5, - 'isolated': True, - 'backup_interval': 3600, - 'backup_retry_interval': 300, - 'time_sync_interval': 900, - } + device_serial='/dev/TESTEH', + device_id='1234', + device_port=8332, + display_ws_port=7377, + discovery=DiscoveryType.all, + simulation=False, + mock=True, + command_timeout=10, + broadcast_interval=5, + isolated=True, + backup_interval=3600, + backup_retry_interval=300, + time_sync_interval=900, + skip_version_check=False, + datastore_topic='brewcast/datastore', + ) @pytest.fixture @@ -58,23 +62,23 @@ def sys_args(app_config) -> list: return [str(v) for v in [ 'app_name', '--debug', - '--name', app_config['name'], - '--host', app_config['host'], - '--port', app_config['port'], - '--device-serial', app_config['device_serial'], - '--device-id', app_config['device_id'], - '--discovery', app_config['discovery'], - '--command-timeout', app_config['command_timeout'], - '--broadcast-interval', app_config['broadcast_interval'], - '--backup-interval', app_config['backup_interval'], - '--backup-retry-interval', app_config['backup_retry_interval'], + '--name', app_config.name, + '--host', app_config.host, + '--port', app_config.port, + '--device-serial', app_config.device_serial, + '--device-id', app_config.device_id, + '--discovery', app_config.discovery, + '--command-timeout', app_config.command_timeout, + '--broadcast-interval', app_config.broadcast_interval, + '--backup-interval', app_config.backup_interval, + '--backup-retry-interval', app_config.backup_retry_interval, '--isolated', '--mock', ]] @pytest.fixture -def app_ini() -> dict: +def app_ini() -> ServiceFirmwareIni: return { 'proto_version': '3f2243a', 'proto_date': '2019-06-06', @@ -85,15 +89,19 @@ def app_ini() -> dict: @pytest.fixture -def app(sys_args, app_ini): - parser = create_parser('default') - app = service.create_app(parser=parser, raw_args=sys_args[1:]) +def app(app_config, app_ini): + app = service.create_app(app_config) app['ini'] = app_ini return app @pytest.fixture -async def client(app, aiohttp_client, aiohttp_server): +async def setup(app): + pass + + +@pytest.fixture +async def client(app, setup, aiohttp_client, aiohttp_server): """Allows patching the app or aiohttp_client before yielding it. Any tests wishing to add custom behavior to app can override the fixture @@ -103,36 +111,19 @@ async def client(app, aiohttp_client, aiohttp_server): LOGGER.debug(f'Feature "{name}" = {impl}') LOGGER.debug(app.on_startup) - return await aiohttp_client(await aiohttp_server(app)) + test_server: test_utils.TestServer = await aiohttp_server(app) + test_client: test_utils.TestClient = await aiohttp_client(test_server) + return test_client @pytest.fixture(scope='session') -def find_free_port(): - """ - Returns a factory that finds the next free port that is available on the OS - This is a bit of a hack, it does this by creating a new socket, and calling - bind with the 0 port. The operating system will assign a brand new port, - which we can find out using getsockname(). Once we have the new port - information we close the socket thereby returning it to the free pool. - This means it is technically possible for this function to return the same - port twice (for example if run in very quick succession), however operating - systems return a random port number in the default range (1024 - 65535), - and it is highly unlikely for two processes to get the same port number. - In other words, it is possible to flake, but incredibly unlikely. - """ - - def _find_free_port(): - import socket - - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - s.bind(('0.0.0.0', 0)) - portnum = s.getsockname()[1] - s.close() - - return portnum - - return _find_free_port +def broker(): + with testing.docker_container( + name='mqtt-test-container', + ports={'mqtt': 1883, 'ws': 15675}, + args=['ghcr.io/brewblox/mosquitto:develop'], + ) as ports: + yield ports @pytest.fixture diff --git a/test/test_backup_storage.py b/test/test_backup_storage.py index 7336d3a0..f835c49e 100644 --- a/test/test_backup_storage.py +++ b/test/test_backup_storage.py @@ -11,7 +11,7 @@ commander, connection, controller, global_store, service_status, service_store, synchronization) -from brewblox_devcon_spark.models import Backup, BackupIdentity +from brewblox_devcon_spark.models import Backup, BackupIdentity, ServiceConfig TESTED = backup_storage.__name__ @@ -22,9 +22,11 @@ def m_backup_dir(mocker, tmp_path): @pytest.fixture -def app(app): - app['config']['backup_interval'] = 0.01 - app['config']['backup_retry_interval'] = 0.01 +async def setup(app): + config: ServiceConfig = app['config'] + config.backup_interval = 0.01 + config.backup_retry_interval = 0.01 + service_status.setup(app) scheduler.setup(app) codec.setup(app) @@ -38,7 +40,6 @@ def app(app): backup_storage.setup(app) backup_storage.fget(app)._autostart = False - return app @pytest.fixture @@ -47,8 +48,9 @@ async def synchronized(app, client): async def test_inactive(app, client, synchronized): - app['config']['backup_interval'] = -2 - app['config']['backup_retry_interval'] = -1 + config: ServiceConfig = app['config'] + config.backup_interval = -2 + config.backup_retry_interval = -1 storage = backup_storage.BackupStorage(app) with pytest.raises(repeater.RepeaterCancelled): diff --git a/test/test_block_store.py b/test/test_block_store.py index 0168196b..cc370a49 100644 --- a/test/test_block_store.py +++ b/test/test_block_store.py @@ -11,6 +11,7 @@ from brewblox_service import http, scheduler from brewblox_devcon_spark import block_store, const, datastore +from brewblox_devcon_spark.models import ServiceConfig TESTED = block_store.__name__ DATASTORE = datastore.__name__ @@ -48,14 +49,15 @@ async def handler(request): @pytest.fixture -def app(app, mocker): +async def setup(app, mocker): mocker.patch(DATASTORE + '.FLUSH_DELAY_S', 0.01) mocker.patch(DATASTORE + '.RETRY_INTERVAL_S', 0.01) - app['config']['isolated'] = False + + config: ServiceConfig = app['config'] + config.isolated = False http.setup(app) scheduler.setup(app) block_store.setup(app) - return app @pytest.fixture diff --git a/test/test_broadcaster.py b/test/test_broadcaster.py index 08dc50fd..ce48c655 100644 --- a/test/test_broadcaster.py +++ b/test/test_broadcaster.py @@ -2,17 +2,18 @@ Tests brewblox_devcon_spark.broadcaster """ -from unittest.mock import ANY, call +from unittest.mock import ANY, Mock, call import pytest -from brewblox_service import repeater, scheduler +from brewblox_service import mqtt, repeater, scheduler +from pytest_mock import MockerFixture from brewblox_devcon_spark import (block_store, broadcaster, codec, commander, connection, controller, exceptions, global_store, service_status, service_store, synchronization) from brewblox_devcon_spark.connection import mock_connection -from brewblox_devcon_spark.models import ErrorCode +from brewblox_devcon_spark.models import ErrorCode, ServiceConfig TESTED = broadcaster.__name__ @@ -28,19 +29,17 @@ def m_relations(mocker): @pytest.fixture -def m_publish(mocker): - mocker.patch(TESTED + '.mqtt.handler', autospec=True) - m = mocker.patch(TESTED + '.mqtt.publish', autospec=True) - return m - +async def setup(app, broker): + config: ServiceConfig = app['config'] + config.broadcast_interval = 0.01 + config.mqtt_host = 'localhost' + config.mqtt_port = broker['mqtt'] + config.history_topic = 'testcast/history' + config.state_topic = 'testcast/state' -@pytest.fixture -def app(app): - app['config']['broadcast_interval'] = 0.01 - app['config']['history_topic'] = 'testcast/history' - app['config']['state_topic'] = 'testcast/state' service_status.setup(app) scheduler.setup(app) + mqtt.setup(app) codec.setup(app) block_store.setup(app) global_store.setup(app) @@ -49,7 +48,6 @@ def app(app): commander.setup(app) synchronization.setup(app) controller.setup(app) - return app @pytest.fixture @@ -57,27 +55,32 @@ async def synchronized(app, client): await service_status.wait_synchronized(app) -async def test_disabled(app, m_publish, client, synchronized): - app['config']['broadcast_interval'] = 0 - app['config']['isolated'] = False +@pytest.fixture +def m_publish(app, mocker: MockerFixture): + m = mocker.spy(mqtt, 'publish') + return m + + +async def test_disabled(app, client, synchronized): + app['config'].broadcast_interval = 0 + app['config'].isolated = False b = broadcaster.Broadcaster(app) with pytest.raises(repeater.RepeaterCancelled): await b.prepare() -async def test_broadcast_unsync(app, m_publish, client, synchronized, mocker): +async def test_broadcast_unsync(app, client, synchronized, m_publish: Mock): service_status.fget(app).synchronized_ev.clear() - app['config']['isolated'] = False + app['config'].isolated = False b = broadcaster.Broadcaster(app) await b.prepare() await b.run() assert m_publish.call_count == 1 -async def test_broadcast(app, m_publish, client, synchronized): - app['config']['isolated'] = False - # status = service_status.desc(app) +async def test_broadcast(app, client, synchronized, m_publish: Mock): + app['config'].isolated = False b = broadcaster.Broadcaster(app) await b.prepare() @@ -85,40 +88,23 @@ async def test_broadcast(app, m_publish, client, synchronized): m_publish.assert_has_calls([ call(app, - 'testcast/history/test_app', + topic='testcast/history/test_app', + payload=ANY, err=False, - message=ANY), + ), call(app, - 'testcast/state/test_app', - err=False, + topic='testcast/state/test_app', + payload=ANY, retain=True, - message=ANY), + err=False, + ), ]) - # { - # 'key': 'test_app', - # 'data': ANY, - # } - - # { - # 'key': 'test_app', - # 'type': 'Spark.state', - # 'data': { - # 'status': status.dict(), - # 'blocks': ANY, - # 'relations': {}, - # 'claims': {}, - # }, - # } - - print(m_publish.call_args_list) - await b.before_shutdown(app) - assert m_publish.call_count == 3 -async def test_error(app, m_publish, client, synchronized): - app['config']['isolated'] = False +async def test_error(app, client, synchronized, m_publish: Mock): + app['config'].isolated = False b = broadcaster.Broadcaster(app) await b.prepare() @@ -133,9 +119,9 @@ async def test_error(app, m_publish, client, synchronized): assert m_publish.call_count == 3 -async def test_api_broadcaster(app, m_publish, client): +async def test_api_broadcaster(app, client, m_publish: Mock): await service_status.wait_synchronized(app) - app['config']['isolated'] = False + app['config'].isolated = False b = broadcaster.Broadcaster(app) await b.prepare() await b.run() diff --git a/test/test_codec.py b/test/test_codec.py index a82630aa..2de5fc50 100644 --- a/test/test_codec.py +++ b/test/test_codec.py @@ -16,13 +16,12 @@ @pytest.fixture -def app(app): +async def setup(app): service_status.setup(app) scheduler.setup(app) codec.setup(app) service_store.setup(app) connection.setup(app) - return app @pytest.fixture diff --git a/test/test_commander.py b/test/test_commander.py index 782b085b..81e16fef 100644 --- a/test/test_commander.py +++ b/test/test_commander.py @@ -10,21 +10,23 @@ from brewblox_devcon_spark import (codec, commander, connection, service_status, service_store) -from brewblox_devcon_spark.models import ErrorCode, IntermediateResponse +from brewblox_devcon_spark.models import (ErrorCode, IntermediateResponse, + ServiceConfig) TESTED = commander.__name__ @pytest.fixture -def app(app): - app['config']['command_timeout'] = 1 +async def setup(app): + config: ServiceConfig = app['config'] + config.command_timeout = 1 + service_status.setup(app) scheduler.setup(app) service_store.setup(app) codec.setup(app) connection.setup(app) commander.setup(app) - return app async def test_acknowledge(app, client): diff --git a/test/test_connection_handler.py b/test/test_connection_handler.py index 41200c85..253375e4 100644 --- a/test/test_connection_handler.py +++ b/test/test_connection_handler.py @@ -38,12 +38,11 @@ def welcome_message(): @pytest.fixture -def app(app): +async def setup(app): service_status.setup(app) scheduler.setup(app) service_store.setup(app) unit_conversion.setup(app) - return app async def test_calc_backoff(): @@ -75,7 +74,7 @@ def reset(): handler = connection_handler.ConnectionHandler(app) # Discovery order is USB -> mDNS -> MQTT - config['discovery'] = DiscoveryType.all + config.discovery = DiscoveryType.all await handler.discover() m_discover_usb.assert_awaited_once_with(app, handler) @@ -85,7 +84,7 @@ def reset(): reset() # Only discover mDNS - config['discovery'] = DiscoveryType.mdns + config.discovery = DiscoveryType.mdns m_discover_mdns.return_value = 'tcp_result' assert await handler.discover() == 'tcp_result' @@ -95,7 +94,7 @@ def reset(): reset() # Only discover MQTT - config['discovery'] = DiscoveryType.mqtt + config.discovery = DiscoveryType.mqtt m_discover_mqtt.return_value = 'mqtt_result' assert await handler.discover() == 'mqtt_result' @@ -105,7 +104,7 @@ def reset(): reset() # Retry if discovery fails the first time - config['discovery'] = DiscoveryType.mdns + config.discovery = DiscoveryType.mdns m_discover_mdns.side_effect = [None, None, 'tcp_result'] assert await handler.discover() == 'tcp_result' @@ -118,7 +117,7 @@ def reset(): # Throw a timeout error after a while mocker.patch(TESTED + '.DISCOVERY_TIMEOUT_S', 0.01) - config['discovery'] = DiscoveryType.all + config.discovery = DiscoveryType.all m_discover_usb.return_value = None m_discover_mdns.return_value = None m_discover_mqtt.return_value = None @@ -153,12 +152,12 @@ def reset(): # Lowest prio: discovery # Discovery order is serial -> TCP -> MQTT - config['mock'] = False - config['simulation'] = False - config['device_serial'] = None - config['device_host'] = None - config['discovery'] = DiscoveryType.all - config['device_id'] = '01ab23ce' + config.mock = False + config.simulation = False + config.device_serial = None + config.device_host = None + config.discovery = DiscoveryType.all + config.device_id = '01ab23ce' await handler.connect() @@ -169,8 +168,8 @@ def reset(): reset() # If host is set, TCP takes precedence over discovery - config['device_host'] = 'hostface' - config['device_port'] = 1234 + config.device_host = 'hostface' + config.device_port = 1234 await handler.connect() @@ -181,7 +180,7 @@ def reset(): reset() # If serial is set, it takes precedence over TCP - config['device_serial'] = 'serialface' + config.device_serial = 'serialface' await handler.connect() @@ -192,7 +191,7 @@ def reset(): reset() # If simulation is set, it takes precedence over serial - config['simulation'] = True + config.simulation = True await handler.connect() @@ -203,7 +202,7 @@ def reset(): reset() # If mock is set, it takes precedence over simulation - config['mock'] = True + config.mock = True await handler.connect() @@ -282,11 +281,11 @@ async def test_handler_discovery_error(app, client, mocker): m_discover_usb.return_value = None config: ServiceConfig = app['config'] - config['mock'] = False - config['simulation'] = False - config['device_serial'] = None - config['device_host'] = None - config['discovery'] = DiscoveryType.usb + config.mock = False + config.simulation = False + config.device_serial = None + config.device_host = None + config.discovery = DiscoveryType.usb service_status.set_enabled(app, True) @@ -300,7 +299,7 @@ async def test_handler_discovery_error(app, client, mocker): # No reboot is required when discovery does not involve USB m_discover_mqtt = mocker.patch(TESTED + '.discover_mqtt', autospec=True) m_discover_mqtt.return_value = None - config['discovery'] = DiscoveryType.mqtt + config.discovery = DiscoveryType.mqtt # No error, only a silent exit await handler.run() diff --git a/test/test_controller.py b/test/test_controller.py index e08097ce..79970a15 100644 --- a/test/test_controller.py +++ b/test/test_controller.py @@ -19,7 +19,7 @@ @pytest.fixture -def app(app): +async def setup(app): service_status.setup(app) scheduler.setup(app) codec.setup(app) @@ -30,7 +30,6 @@ def app(app): service_store.setup(app) synchronization.setup(app) controller.setup(app) - return app @pytest.fixture diff --git a/test/test_datastore.py b/test/test_datastore.py index 864e8c20..46f16e22 100644 --- a/test/test_datastore.py +++ b/test/test_datastore.py @@ -10,6 +10,7 @@ from brewblox_service import http, scheduler from brewblox_devcon_spark import datastore +from brewblox_devcon_spark.models import ServiceConfig TESTED = datastore.__name__ @@ -24,13 +25,15 @@ async def handler(request): @pytest.fixture -def app(app, mocker): +async def setup(app, mocker): mocker.patch(TESTED + '.FLUSH_DELAY_S', 0.01) mocker.patch(TESTED + '.RETRY_INTERVAL_S', 0.01) - app['config']['isolated'] = False + + config: ServiceConfig = app['config'] + config.isolated = False + http.setup(app) scheduler.setup(app) - return app async def test_check_remote(app, client, aresponses: ResponsesMockServer): diff --git a/test/test_global_store.py b/test/test_global_store.py index 3cc2b98f..5027ad36 100644 --- a/test/test_global_store.py +++ b/test/test_global_store.py @@ -13,6 +13,7 @@ from brewblox_devcon_spark import const, global_store from brewblox_devcon_spark.global_store import GlobalConfigStore +from brewblox_devcon_spark.models import ServiceConfig TESTED = global_store.__name__ @@ -40,12 +41,12 @@ def m_mqtt(mocker): @pytest.fixture -def app(app): - app['config']['isolated'] = False +async def setup(app): + config: ServiceConfig = app['config'] + config.isolated = False http.setup(app) scheduler.setup(app) global_store.setup(app) - return app @pytest.fixture diff --git a/test/test_integration.py b/test/test_integration.py index c1fa42d2..35a09878 100644 --- a/test/test_integration.py +++ b/test/test_integration.py @@ -7,8 +7,10 @@ from unittest.mock import ANY, AsyncMock import pytest -from brewblox_service import scheduler +from aiohttp.test_utils import TestClient +from brewblox_service import mqtt, scheduler from brewblox_service.testing import find_free_port, response +from pytest_mock import MockerFixture from brewblox_devcon_spark import (backup_storage, block_store, codec, commander, connection, const, controller, @@ -53,18 +55,18 @@ def block_args(): @pytest.fixture(autouse=True) -def m_publish(mocker): - m = mocker.patch(blocks_api.__name__ + '.mqtt.publish', autospec=True) +def m_publish(app, mocker: MockerFixture): + m = mocker.spy(mqtt, 'publish') return m @pytest.fixture(autouse=True) -def m_backup_dir(mocker, tmp_path): +def m_backup_dir(mocker: MockerFixture, tmp_path): mocker.patch(backup_storage.__name__ + '.BASE_BACKUP_DIR', tmp_path / 'backup') @pytest.fixture(autouse=True) -def m_simulator_dir(mocker, tmp_path): +def m_simulator_dir(mocker: MockerFixture, tmp_path): mocker.patch(stream_connection.__name__ + '.SIMULATION_CWD', tmp_path / 'simulator') @@ -77,19 +79,24 @@ def repeated_blocks(ids, args): @pytest.fixture -async def app(app): +async def setup(app, broker): """App + controller routes""" - config: ServiceConfig = app['config'] app['ini'] = parse_ini(app) - config['mock'] = False - config['simulation'] = True - config['isolated'] = True - config['device_id'] = '123456789012345678901234' - config['device_port'] = find_free_port() - config['display_ws_port'] = 0 # let firmware find its own free port + + config: ServiceConfig = app['config'] + config.mock = False + config.simulation = True + config.isolated = True + config.device_id = '123456789012345678901234' + config.device_port = find_free_port() + config.display_ws_port = 0 # let firmware find its own free port + config.mqtt_host = 'localhost' + config.mqtt_port = broker['mqtt'] + config.state_topic = 'test_integration/state' service_status.setup(app) scheduler.setup(app) + mqtt.setup(app) codec.setup(app) connection.setup(app) commander.setup(app) @@ -107,22 +114,25 @@ async def app(app): settings_api.setup(app) debug_api.setup(app) - return app - @pytest.fixture async def production_app(app): - app['config']['debug'] = False + app['config'].debug = False return app @pytest.fixture(autouse=True) -async def synchronized(app, client): +async def synchronized(app, client: TestClient): # Prevents test hangups if the connection fails - await asyncio.wait_for(service_status.wait_synchronized(app), timeout=5) + await asyncio.wait_for( + asyncio.gather( + service_status.wait_synchronized(app), + mqtt.fget(app).ready.wait(), + ), + timeout=5) -async def test_create(app, client, block_args): +async def test_create(app, client: TestClient, block_args): # Create object retd = await response(client.post('/blocks/create', json=block_args), 201) assert retd['id'] == block_args['id'] @@ -135,7 +145,7 @@ async def test_create(app, client, block_args): assert retd['id'] == 'other_obj' -async def test_invalid_input(app, client, block_args): +async def test_invalid_input(app, client: TestClient, block_args): # 400 if input fails schema check del block_args['type'] retv = await response(client.post('/blocks/create', json=block_args), 400) @@ -154,7 +164,7 @@ async def test_invalid_input(app, client, block_args): assert 'traceback' in retv -async def test_invalid_input_prod(production_app, client, block_args): +async def test_invalid_input_prod(production_app, client: TestClient, block_args): # 400 if input fails schema check del block_args['type'] retv = await response(client.post('/blocks/create', json=block_args), 400) @@ -174,7 +184,7 @@ async def test_invalid_input_prod(production_app, client, block_args): assert 'traceback' not in retv -async def test_create_performance(app, client, block_args): +async def test_create_performance(app, client: TestClient, block_args): num_items = 50 ids = [f'id{num}' for num in range(num_items)] blocks = repeated_blocks(ids, block_args) @@ -186,7 +196,7 @@ async def test_create_performance(app, client, block_args): assert set(ids).issubset(ret_ids(retd)) -async def test_batch_create_read_delete(app, client, block_args): +async def test_batch_create_read_delete(app, client: TestClient, block_args): num_items = 50 ids = [f'id{num}' for num in range(num_items)] blocks = repeated_blocks(ids, block_args) @@ -208,7 +218,7 @@ async def test_batch_create_read_delete(app, client, block_args): assert set(ids).isdisjoint(ret_ids(retv)) -async def test_read(app, client, block_args): +async def test_read(app, client: TestClient, block_args): await response(client.post('/blocks/read', json={'id': 'testobj'}), 400) # Object does not exist await response(client.post('/blocks/create', json=block_args), 201) @@ -216,12 +226,12 @@ async def test_read(app, client, block_args): assert retd['id'] == 'testobj' -async def test_read_performance(app, client, block_args): +async def test_read_performance(app, client: TestClient, block_args): await response(client.post('/blocks/create', json=block_args), 201) await asyncio.gather(*(response(client.post('/blocks/read', json={'id': 'testobj'})) for _ in range(100))) -async def test_read_logged(app, client, block_args): +async def test_read_logged(app, client: TestClient, block_args): await response(client.post('/blocks/create', json=block_args), 201) retd = await response(client.post('/blocks/read/logged', json={'id': 'testobj'})) @@ -229,31 +239,31 @@ async def test_read_logged(app, client, block_args): assert 'address' not in retd['data'] # address is not a logged field -async def test_write(app, client, block_args, m_publish): +async def test_write(app, client: TestClient, block_args, m_publish): await response(client.post('/blocks/create', json=block_args), 201) assert await response(client.post('/blocks/write', json=block_args)) assert m_publish.call_count == 2 -async def test_batch_write(app, client, block_args, m_publish): +async def test_batch_write(app, client: TestClient, block_args, m_publish): await response(client.post('/blocks/create', json=block_args), 201) assert await response(client.post('/blocks/batch/write', json=[block_args, block_args, block_args])) assert m_publish.call_count == 2 -async def test_patch(app, client, block_args, m_publish): +async def test_patch(app, client: TestClient, block_args, m_publish): await response(client.post('/blocks/create', json=block_args), 201) assert await response(client.post('/blocks/patch', json=block_args)) assert m_publish.call_count == 2 -async def test_batch_patch(app, client, block_args, m_publish): +async def test_batch_patch(app, client: TestClient, block_args, m_publish): await response(client.post('/blocks/create', json=block_args), 201) assert await response(client.post('/blocks/batch/patch', json=[block_args, block_args, block_args])) assert m_publish.call_count == 2 -async def test_delete(app, client, block_args): +async def test_delete(app, client: TestClient, block_args): await response(client.post('/blocks/create', json=block_args), 201) retd = await response(client.post('/blocks/delete', json={'id': 'testobj'})) @@ -262,7 +272,7 @@ async def test_delete(app, client, block_args): await response(client.post('/blocks/read', json={'id': 'testobj'}), 400) -async def test_nid_crud(app, client, block_args): +async def test_nid_crud(app, client: TestClient, block_args): created = await response(client.post('/blocks/create', json=block_args), 201) nid = created['nid'] @@ -274,7 +284,7 @@ async def test_nid_crud(app, client, block_args): await response(client.post('/blocks/read', json={'nid': nid}), 500) -async def test_stored_blocks(app, client, block_args): +async def test_stored_blocks(app, client: TestClient, block_args): retd = await response(client.post('/blocks/all/read/stored')) base_num = len(retd) @@ -288,7 +298,7 @@ async def test_stored_blocks(app, client, block_args): await response(client.post('/blocks/read/stored', json={'id': 'flappy'}), 400) -async def test_delete_all(app, client, block_args): +async def test_delete_all(app, client: TestClient, block_args): n_sys_obj = len(await response(client.post('/blocks/all/read'))) for i in range(5): @@ -300,7 +310,7 @@ async def test_delete_all(app, client, block_args): assert len(await response(client.post('/blocks/all/read'))) == n_sys_obj -async def test_cleanup(app, client, block_args): +async def test_cleanup(app, client: TestClient, block_args): store = block_store.fget(app) await response(client.post('/blocks/create', json=block_args), 201) store['unused', 456] = {} @@ -309,7 +319,7 @@ async def test_cleanup(app, client, block_args): assert not [v for v in retv if v['id'] == 'testobj'] -async def test_rename(app, client, block_args): +async def test_rename(app, client: TestClient, block_args): await response(client.post('/blocks/create', json=block_args), 201) existing = block_args['id'] desired = 'newname' @@ -322,7 +332,7 @@ async def test_rename(app, client, block_args): await response(client.post('/blocks/read', json={'id': desired})) -async def test_sequence(app, client): +async def test_sequence(app, client: TestClient): setpoint_block = { 'id': 'setpoint', 'type': 'SetpointSensorPair', @@ -354,12 +364,12 @@ async def test_sequence(app, client): ] -async def test_ping(app, client): +async def test_ping(app, client: TestClient): await response(client.get('/system/ping')) await response(client.post('/system/ping')) -async def test_settings_api(app, client, block_args): +async def test_settings_api(app, client: TestClient, block_args): await response(client.post('/blocks/create', json=block_args), 201) retd = await response(client.get('/settings/autoconnecting')) @@ -371,12 +381,12 @@ async def test_settings_api(app, client, block_args): assert retd == {'enabled': False} -async def test_discover(app, client): +async def test_discover(app, client: TestClient): resp = await response(client.post('/blocks/discover')) assert resp == [] -async def test_validate(app, client, block_args): +async def test_validate(app, client: TestClient, block_args): validate_args = { 'type': block_args['type'], 'data': block_args['data'], @@ -403,7 +413,7 @@ async def test_validate(app, client, block_args): await response(client.post('/blocks/validate', json=invalid_link_obj), 400) -async def test_backup_save(app, client, block_args): +async def test_backup_save(app, client: TestClient, block_args): retd = await response(client.post('/blocks/backup/save')) base_num = len(retd['blocks']) @@ -412,7 +422,7 @@ async def test_backup_save(app, client, block_args): assert len(retd['blocks']) == 1 + base_num -async def test_backup_load(app, client, spark_blocks): +async def test_backup_load(app, client: TestClient, spark_blocks): # reverse the set, to ensure some blocks are written with invalid references data = { 'store': [{'keys': [block['id'], block['nid']], 'data': dict()} for block in spark_blocks], @@ -457,7 +467,7 @@ async def test_backup_load(app, client, spark_blocks): assert 'derpface' not in resp_ids -async def test_backup_stored(app, client, block_args): +async def test_backup_stored(app, client: TestClient, block_args): portable = await response(client.post('/blocks/backup/save')) saved_stored = await response(client.post('/blocks/backup/stored/save', json={ 'name': 'stored', @@ -495,7 +505,7 @@ async def test_backup_stored(app, client, block_args): assert block_args['id'] not in ids -async def test_read_all_logged(app, client): +async def test_read_all_logged(app, client: TestClient): args = { 'id': 'pwm', 'type': 'ActuatorPwm', @@ -526,7 +536,7 @@ async def test_read_all_logged(app, client): assert 'period' not in obj_data -async def test_system_status(app, client): +async def test_system_status(app, client: TestClient): await service_status.wait_synchronized(app) resp = await response(client.get('/system/status')) @@ -569,7 +579,7 @@ async def test_system_status(app, client): assert resp['controller'] is None -async def test_system_resets(app, client, mocker): +async def test_system_resets(app, client: TestClient, mocker): sys_api = system_api.__name__ mocker.patch(sys_api + '.shutdown_soon', AsyncMock()) @@ -581,7 +591,7 @@ async def test_system_resets(app, client, mocker): await response(client.post('/system/factory_reset')) -async def test_debug_encode_request(app, client): +async def test_debug_encode_request(app, client: TestClient): payload = DecodedPayload( blockId=123, blockType='TempSensorOneWire', @@ -619,7 +629,7 @@ async def test_debug_encode_request(app, client): assert payload.content['offset']['value'] == 20 -async def test_debug_encode_response(app, client): +async def test_debug_encode_response(app, client: TestClient): payload = DecodedPayload( blockId=123, blockType='TempSensorOneWire', diff --git a/test/test_main.py b/test/test_main.py index e70abf10..9235dfc4 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -3,33 +3,45 @@ """ import pytest -from brewblox_service import http, mqtt, scheduler +from brewblox_service import http, mqtt, scheduler, service from brewblox_devcon_spark import __main__ as main from brewblox_devcon_spark import (backup_storage, block_store, broadcaster, codec, commander, connection, controller, global_store, service_status, service_store, synchronization, time_sync) +from brewblox_devcon_spark.models import ServiceConfig TESTED = main.__name__ @pytest.fixture(autouse=True) -def mocked_parse(mocker, app_ini): - m = mocker.patch(TESTED + '.parse_ini') - m.return_value = app_ini - return m +def m_firmware_ini(mocker): + mocker.patch(TESTED + '.FIRMWARE_INI', 'firmware.ini') -def test_main(mocker, app): - mocker.patch(TESTED + '.service.run') - mocker.patch(TESTED + '.service.create_app').return_value = app +@pytest.fixture +def m_service_funcs(app, mocker, event_loop): + def dummy_run(app, setup): + event_loop.run_until_complete(setup) + mocker.patch(TESTED + '.service.create_config', autospec=True).return_value = app['config'] + mocker.patch(TESTED + '.service.create_app', autospec=True).return_value = app + mocker.patch(TESTED + '.service.run_app', dummy_run) + + +def test_parse(app, sys_args): + parser = main.create_parser() + config = service.create_config(parser, model=ServiceConfig, raw_args=sys_args[1:]) + assert isinstance(config, ServiceConfig) + + +def test_main(app, m_service_funcs): main.main() assert None not in [ scheduler.fget(app), - mqtt.handler(app), + mqtt.fget(app), http.fget(app), global_store.fget(app), @@ -50,12 +62,11 @@ def test_main(mocker, app): @pytest.mark.parametrize('auto_id', [True, False]) -def test_simulation(auto_id, mocker, app): - app['config']['device_id'] = None - app['config']['mock'] = auto_id - app['config']['simulation'] = auto_id - mocker.patch(TESTED + '.service.run') - mocker.patch(TESTED + '.service.create_app').return_value = app +def test_simulation(auto_id, app, m_service_funcs): + config: ServiceConfig = app['config'] + config.device_id = None + config.mock = auto_id + config.simulation = auto_id main.main() - assert bool(app['config']['device_id']) is auto_id + assert bool(config.device_id) is auto_id diff --git a/test/test_mqtt_api.py b/test/test_mqtt_api.py index a4dd8efb..0c192c22 100644 --- a/test/test_mqtt_api.py +++ b/test/test_mqtt_api.py @@ -3,27 +3,27 @@ """ import pytest -from brewblox_service import scheduler +from brewblox_service import mqtt, scheduler from brewblox_devcon_spark import (block_store, codec, commander, connection, controller, exceptions, global_store, service_status, service_store, synchronization) from brewblox_devcon_spark.api import mqtt_api -from brewblox_devcon_spark.models import Block, BlockIdentity +from brewblox_devcon_spark.models import Block, BlockIdentity, ServiceConfig TESTED = mqtt_api.__name__ -@pytest.fixture(autouse=True) -def m_mqtt(mocker): - mocker.patch(TESTED + '.mqtt', autospec=True) - - @pytest.fixture -async def app(app, event_loop): - """App + controller routes""" +async def setup(app, broker): + config: ServiceConfig = app['config'] + config.mqtt_host = 'localhost' + config.mqtt_port = broker['mqtt'] + config.state_topic = 'test_mqtt/state' + scheduler.setup(app) + mqtt.setup(app) service_status.setup(app) block_store.setup(app) global_store.setup(app) @@ -36,13 +36,11 @@ async def app(app, event_loop): mqtt_api.setup(app) - return app - def block_args(app): return Block( id='testobj', - serviceId=app['config']['name'], + serviceId=app['config'].name, type='TempSensorOneWire', data={ 'value': 12345, @@ -84,7 +82,7 @@ async def test_delete(app, client): await api._create('topic', block_args(app).json()) await api._delete('topic', BlockIdentity( id='testobj', - serviceId=app['config']['name'], + serviceId=app['config'].name, ).json()) with pytest.raises(exceptions.UnknownId): diff --git a/test/test_mqtt_connection.py b/test/test_mqtt_connection.py index 582487aa..3aca8b86 100644 --- a/test/test_mqtt_connection.py +++ b/test/test_mqtt_connection.py @@ -2,42 +2,54 @@ Tests brewblox_devcon_spark.connection.mqtt_connection """ +import asyncio from unittest.mock import AsyncMock import pytest +from brewblox_service import mqtt, scheduler from brewblox_devcon_spark.connection import (connection_handler, mqtt_connection) +from brewblox_devcon_spark.models import ServiceConfig TESTED = mqtt_connection.__name__ @pytest.fixture -async def m_mqtt(mocker): - return { - k: mocker.patch(f'{TESTED}.mqtt.{k}', autospec=True) - for k in [ - 'listen', - 'subscribe', - 'unlisten', - 'unsubscribe', - 'publish' - ]} +async def setup(app, broker): + config: ServiceConfig = app['config'] + config.isolated = False + config.mqtt_host = 'localhost' + config.mqtt_port = broker['mqtt'] + + scheduler.setup(app) + mqtt.setup(app) + mqtt_connection.setup(app) -@pytest.fixture -async def app(app, m_mqtt): - app['config']['isolated'] = False - mqtt_connection.setup(app) - return app +@pytest.fixture(autouse=True) +async def synchronized(app, client): + await asyncio.wait_for(mqtt.fget(app).ready.wait(), timeout=5) -async def test_mqtt_discovery(app, client, m_mqtt, mocker): +async def test_mqtt_discovery(app, client, mocker): mocker.patch(TESTED + '.DISCOVERY_TIMEOUT_S', 0.001) - device_id = app['config']['device_id'] + recv = asyncio.Event() + config: ServiceConfig = app['config'] + device_id = config.device_id + + async def recv_cb(topic: str, payload: str): + recv.set() + + await mqtt.listen(app, 'brewcast/cbox/handshake/#', recv_cb) + + # Publish handshake message + await mqtt.publish(app, + topic=f'brewcast/cbox/handshake/{device_id}', + payload='handshake message') + await recv.wait() tracker = mqtt_connection.fget(app) - await tracker._handshake_cb(mqtt_connection.HANDSHAKE_TOPIC + device_id, 'handshake message') assert await tracker.discover(AsyncMock(), None) is None assert await tracker.discover(AsyncMock(), '09876') is None @@ -48,37 +60,89 @@ async def test_mqtt_discovery(app, client, m_mqtt, mocker): conn = await mqtt_connection.discover_mqtt(app, AsyncMock()) assert isinstance(conn, mqtt_connection.MqttConnection) - # LWT received: controller disconnected - await tracker._handshake_cb(mqtt_connection.HANDSHAKE_TOPIC + device_id, '') + # Publish LWT -> controller disconnected + recv.clear() + await mqtt.publish(app, + topic=f'brewcast/cbox/handshake/{device_id}', + payload='') + await recv.wait() + + # No longer discoverable assert await tracker.discover(AsyncMock(), device_id) is None -async def test_mqtt_impl(app, client, m_mqtt, mocker): +async def test_mqtt_impl(app, client, mocker): callbacks = AsyncMock(spec=connection_handler.ConnectionHandler(app)) - device_id = app['config']['device_id'] + config: ServiceConfig = app['config'] + device_id = config.device_id + recv_handshake = asyncio.Event() + recv_req = asyncio.Event() + recv_resp = asyncio.Event() + recv_log = asyncio.Event() + + async def on_handshake(topic: str, payload: str): + recv_handshake.set() + + async def on_request(topic: str, payload: str): + resp_topic = topic.replace('/req/', '/resp/') + await mqtt.publish(app, resp_topic, payload[::-1]) + recv_req.set() + + async def on_response(topic: str, payload: str): + recv_resp.set() + + async def on_log(topic: str, payload: str): + recv_log.set() + + await mqtt.subscribe(app, '#') + await mqtt.listen(app, 'brewcast/cbox/handshake/#', on_handshake) + await mqtt.listen(app, 'brewcast/cbox/req/#', on_request) + await mqtt.listen(app, 'brewcast/cbox/resp/#', on_response) + await mqtt.listen(app, 'brewcast/cbox/log/#', on_log) + + await mqtt.publish(app, + topic=f'brewcast/cbox/handshake/{device_id}', + payload='handshake message') + impl = mqtt_connection.MqttConnection(app, device_id, callbacks) await impl.connect() assert impl.connected.is_set() - assert m_mqtt['subscribe'].await_count == 1 + 3 # includes initial call by tracker - assert m_mqtt['listen'].await_count == 1 + 3 # includes initial call by tracker await impl.send_request('hello') - m_mqtt['publish'].assert_awaited_once_with(app, mqtt_connection.REQUEST_TOPIC + device_id, 'hello') + await asyncio.wait_for(recv_req.wait(), timeout=5) + await asyncio.wait_for(recv_resp.wait(), timeout=5) + callbacks.on_response.assert_awaited_once_with('olleh') - await impl._resp_cb('topic', 'resp') - callbacks.on_response.assert_awaited_once_with('resp') + await mqtt.publish(app, + topic=f'brewcast/cbox/log/{device_id}', + payload='log message') - await impl._log_cb('topic', 'event') - callbacks.on_event.assert_awaited_once_with('event') + await asyncio.wait_for(recv_log.wait(), timeout=5) + callbacks.on_event.assert_awaited_once_with('log message') # LWT is an empty message to handshake topic - await impl._handshake_cb('topic', 'republish') - assert not impl.disconnected.is_set() - await impl._handshake_cb('topic', '') + recv_handshake.clear() + await mqtt.publish(app, + topic=f'brewcast/cbox/handshake/{device_id}', + payload='') + await asyncio.wait_for(recv_handshake.wait(), timeout=5) assert impl.disconnected.is_set() # Can safely be called await impl.close() assert impl.disconnected.is_set() - assert m_mqtt['unsubscribe'].await_count == 3 - assert m_mqtt['unlisten'].await_count == 3 + + +async def test_isolated(app, client, mocker): + m_listen = mocker.spy(mqtt, 'listen') + m_unlisten = mocker.spy(mqtt, 'unlisten') + app['config'].isolated = True + + tracker = mqtt_connection.MqttDeviceTracker(app) + await tracker.startup(app) + assert await tracker.discover(AsyncMock(), '1234') is None + await tracker.before_shutdown(app) + await tracker.shutdown(app) + + assert m_listen.await_count == 0 + assert m_unlisten.await_count == 0 diff --git a/test/test_service_status.py b/test/test_service_status.py index 230dd7b2..88d2f2a2 100644 --- a/test/test_service_status.py +++ b/test/test_service_status.py @@ -28,15 +28,14 @@ def make_desc(app) -> ControllerDescription: proto_date=ini['proto_date'], ), device=DeviceDescription( - device_id=config['device_id'], + device_id=config.device_id, ), ) @pytest.fixture -def app(app): +async def setup(app): service_status.setup(app) - return app async def test_state_machine(app, client): diff --git a/test/test_service_store.py b/test/test_service_store.py index 9922f39a..8aa40bf5 100644 --- a/test/test_service_store.py +++ b/test/test_service_store.py @@ -41,14 +41,13 @@ async def handler(request): @pytest.fixture -def app(app, mocker): +async def setup(app, mocker): mocker.patch(DATASTORE + '.FLUSH_DELAY_S', 0.01) mocker.patch(DATASTORE + '.RETRY_INTERVAL_S', 0.01) - app['config']['isolated'] = False + app['config'].isolated = False http.setup(app) scheduler.setup(app) service_store.setup(app) - return app @pytest.fixture diff --git a/test/test_sim_api.py b/test/test_sim_api.py index 7f984e05..15098b2c 100644 --- a/test/test_sim_api.py +++ b/test/test_sim_api.py @@ -30,14 +30,12 @@ async def stream_handler(request: web.Request) -> web.Response: @pytest.fixture -async def app(app): +async def setup(app): """App + controller routes""" scheduler.setup(app) sim_api.setup(app) app.router.add_routes(routes) - return app - @pytest.fixture(autouse=True) async def m_wait_sync(mocker): diff --git a/test/test_stream_connection.py b/test/test_stream_connection.py index 97d4bc95..b4e0075c 100644 --- a/test/test_stream_connection.py +++ b/test/test_stream_connection.py @@ -92,7 +92,7 @@ async def test_tcp_connection_error(app, client, echo_server, test_port): async def test_discover_mdns(app, client, echo_server, mocker, test_port): m_mdns_discover = mocker.patch(TESTED + '.mdns.discover_one', autospec=True) - m_mdns_discover.return_value = ConnectInfo('localhost', test_port, app['config']['device_id']) + m_mdns_discover.return_value = ConnectInfo('localhost', test_port, app['config'].device_id) callbacks = DummyCallbacks() impl = await stream_connection.discover_mdns(app, callbacks) diff --git a/test/test_synchronization.py b/test/test_synchronization.py index 7470fddd..855b492d 100644 --- a/test/test_synchronization.py +++ b/test/test_synchronization.py @@ -43,8 +43,8 @@ async def disconnect(app): @pytest.fixture -async def app(app, event_loop): - app['config']['isolated'] = True +async def setup(app): + app['config'].isolated = True scheduler.setup(app) service_status.setup(app) codec.setup(app) @@ -53,7 +53,6 @@ async def app(app, event_loop): global_store.setup(app) service_store.setup(app) block_store.setup(app) - return app async def test_sync_status(app, client): @@ -95,9 +94,9 @@ async def test_timeout(app, client, mocker): async def test_device_name(app, client): s = await connect(app) - assert s.device_name == app['config']['device_id'] + assert s.device_name == app['config'].device_id - app['config']['simulation'] = True + app['config'].simulation = True assert s.device_name.startswith('simulator__') diff --git a/test/test_time_sync.py b/test/test_time_sync.py index 72ebda92..3b88875b 100644 --- a/test/test_time_sync.py +++ b/test/test_time_sync.py @@ -10,6 +10,7 @@ from brewblox_devcon_spark import (block_store, codec, commander, connection, controller, global_store, service_status, service_store, synchronization, time_sync) +from brewblox_devcon_spark.models import ServiceConfig TESTED = time_sync.__name__ @@ -26,8 +27,10 @@ def m_controller(mocker): @pytest.fixture -def app(app): - app['config']['time_sync_interval'] = 0.01 +async def setup(app): + config: ServiceConfig = app['config'] + config.time_sync_interval = 0.01 + service_status.setup(app) scheduler.setup(app) codec.setup(app) @@ -39,7 +42,6 @@ def app(app): synchronization.setup(app) controller.setup(app) time_sync.setup(app) - return app @pytest.fixture @@ -54,7 +56,7 @@ async def test_sync(app, client, synchronized, m_controller): async def test_disabled_sync(app, client, synchronized): - app['config']['time_sync_interval'] = 0 + app['config'].time_sync_interval = 0 sync = time_sync.TimeSync(app) with pytest.raises(repeater.RepeaterCancelled): From cc766dce6942bac997fce2b9fb9b925d01868781 Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Tue, 1 Aug 2023 13:08:42 +0200 Subject: [PATCH 02/27] bump to service 3.1 --- poetry.lock | 23 +++++++++-------------- pyproject.toml | 2 +- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/poetry.lock b/poetry.lock index 00434c4b..0c36f129 100644 --- a/poetry.lock +++ b/poetry.lock @@ -263,25 +263,20 @@ files = [ [[package]] name = "brewblox-service" -version = "3.0.2" +version = "3.1.0" description = "Scaffolding for Brewblox backend services" category = "main" optional = false python-versions = ">=3.9,<4" -files = [] -develop = false +files = [ + {file = "brewblox_service-3.1.0.tar.gz", hash = "sha256:cb483dd85e8776c00f312de3dd2c1e6da02b414cd4256ed870e32f585c2e5f36"}, +] [package.dependencies] -aiohttp = "==3.*" -aiohttp-pydantic = "==1.*" -aiomqtt = "==1.*" -pydantic = "==1.*" - -[package.source] -type = "git" -url = "https://github.com/brewblox/brewblox-service.git" -reference = "develop" -resolved_reference = "318788407a50ba5ee355efd9bfe33ed545c6cd9f" +aiohttp = ">=3.0.0,<4.0.0" +aiohttp-pydantic = ">=1.0.0,<2.0.0" +aiomqtt = ">=1.0.0,<2.0.0" +pydantic = ">=1.0.0,<2.0.0" [[package]] name = "cffi" @@ -1727,4 +1722,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.9,<4" -content-hash = "476140ad032a9710b768c19d14f7b5129707a7662d569589b332af729fe48863" +content-hash = "df2fb057d45f454ba21fb837b4788b0156a989284cca2705418334ec57b154ea" diff --git a/pyproject.toml b/pyproject.toml index 2b71a6ef..911961e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ reportMissingImports = "information" [tool.poetry.dependencies] python = ">=3.9,<4" -brewblox-service = {git = "https://github.com/brewblox/brewblox-service.git", rev = "develop"} +brewblox-service = "^3.1" pyserial-asyncio = "^0.6" protobuf = "<4.0dev" aiofiles = "^0.8.0" From 99ccb71167aa080aee6febec53428f30a07046b1 Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Tue, 1 Aug 2023 15:36:03 +0200 Subject: [PATCH 03/27] use brewblox service wheel --- poetry.lock | 8 +++++--- pyproject.toml | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 0c36f129..40da8767 100644 --- a/poetry.lock +++ b/poetry.lock @@ -263,19 +263,21 @@ files = [ [[package]] name = "brewblox-service" -version = "3.1.0" +version = "3.1.2" description = "Scaffolding for Brewblox backend services" category = "main" optional = false python-versions = ">=3.9,<4" files = [ - {file = "brewblox_service-3.1.0.tar.gz", hash = "sha256:cb483dd85e8776c00f312de3dd2c1e6da02b414cd4256ed870e32f585c2e5f36"}, + {file = "brewblox_service-3.1.2-py3-none-any.whl", hash = "sha256:4e72417824083d9745bb51df538a99a76aaf774d53623aaa570330df128dc6fe"}, + {file = "brewblox_service-3.1.2.tar.gz", hash = "sha256:29bd803531b6be1309a880e5d8ad4eeecdbe2a5ca7ade6334635420a544ef9db"}, ] [package.dependencies] aiohttp = ">=3.0.0,<4.0.0" aiohttp-pydantic = ">=1.0.0,<2.0.0" aiomqtt = ">=1.0.0,<2.0.0" +cryptography = "40.0.1" pydantic = ">=1.0.0,<2.0.0" [[package]] @@ -1722,4 +1724,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.9,<4" -content-hash = "df2fb057d45f454ba21fb837b4788b0156a989284cca2705418334ec57b154ea" +content-hash = "0cdbaeb0b9c0ed473fe16a657c1011778b94b5ec141c2ea895efb2a0d71d3ba1" diff --git a/pyproject.toml b/pyproject.toml index 911961e4..b25f1a28 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ reportMissingImports = "information" [tool.poetry.dependencies] python = ">=3.9,<4" -brewblox-service = "^3.1" +brewblox-service = "^3.1.2" pyserial-asyncio = "^0.6" protobuf = "<4.0dev" aiofiles = "^0.8.0" From fbe4e0387d3b373dc574c3b3dcaf5737d5238151 Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Mon, 11 Sep 2023 16:12:00 +0200 Subject: [PATCH 04/27] bump dependencies; use protobuf 4 --- .vscode/settings.json | 5 +- Dockerfile.service | 4 +- brewblox_devcon_spark/codec/lookup.py | 25 +- .../proto-compiled/ActuatorAnalogMock_pb2.py | 42 +- .../codec/proto-compiled/ActuatorLogic_pb2.py | 104 +-- .../proto-compiled/ActuatorOffset_pb2.py | 49 +- .../codec/proto-compiled/ActuatorPwm_pb2.py | 40 +- .../codec/proto-compiled/Balancer_pb2.py | 44 +- .../codec/proto-compiled/Claims_pb2.py | 17 +- .../codec/proto-compiled/Constraints_pb2.py | 168 ++--- .../codec/proto-compiled/DS2408_pb2.py | 55 +- .../codec/proto-compiled/DS2413_pb2.py | 40 +- .../proto-compiled/DigitalActuator_pb2.py | 36 +- .../codec/proto-compiled/DigitalInput_pb2.py | 39 +- .../proto-compiled/DisplaySettings_pb2.py | 50 +- .../codec/proto-compiled/EdgeCase_pb2.py | 80 +-- .../codec/proto-compiled/FastPwm_pb2.py | 40 +- .../codec/proto-compiled/IoArray_pb2.py | 75 +- .../codec/proto-compiled/MockPins_pb2.py | 44 +- .../codec/proto-compiled/MotorValve_pb2.py | 50 +- .../codec/proto-compiled/Mutex_pb2.py | 28 +- .../codec/proto-compiled/OneWireBus_pb2.py | 36 +- .../proto-compiled/OneWireGpioModule_pb2.py | 109 +-- .../codec/proto-compiled/Pid_pb2.py | 66 +- .../codec/proto-compiled/Screen_pb2.py | 85 +-- .../codec/proto-compiled/Sequence_pb2.py | 301 ++------ .../proto-compiled/SetpointProfile_pb2.py | 48 +- .../proto-compiled/SetpointSensorPair_pb2.py | 56 +- .../codec/proto-compiled/Spark2Pins_pb2.py | 49 +- .../codec/proto-compiled/Spark3Pins_pb2.py | 45 +- .../codec/proto-compiled/SysInfo_pb2.py | 75 +- .../proto-compiled/TempSensorCombi_pb2.py | 38 +- .../proto-compiled/TempSensorExternal_pb2.py | 32 +- .../proto-compiled/TempSensorMock_pb2.py | 44 +- .../proto-compiled/TempSensorOneWire_pb2.py | 30 +- .../codec/proto-compiled/TouchSettings_pb2.py | 30 +- .../codec/proto-compiled/WiFiSettings_pb2.py | 50 +- .../codec/proto-compiled/brewblox_pb2.py | 123 +--- .../codec/proto-compiled/command_pb2.py | 113 +-- .../codec/proto-compiled/nanopb_pb2.py | 73 +- brewblox_devcon_spark/codec/sequence.py | 41 +- poetry.lock | 659 ++++++++++-------- pyproject.toml | 17 +- 43 files changed, 1103 insertions(+), 2052 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 80ea0f6e..69224c90 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,8 +2,6 @@ "python.defaultInterpreterPath": ".venv/bin/python", "python.terminal.activateEnvInCurrentTerminal": true, "python.terminal.activateEnvironment": true, - "python.linting.flake8Enabled": true, - "python.linting.pylintEnabled": false, "python.testing.pytestArgs": [ "--no-cov", "." @@ -17,6 +15,5 @@ ], "[python]": { "editor.defaultFormatter": "ms-python.autopep8" - }, - "python.formatting.provider": "none" + } } \ No newline at end of file diff --git a/Dockerfile.service b/Dockerfile.service index 3362d341..3604e655 100644 --- a/Dockerfile.service +++ b/Dockerfile.service @@ -1,4 +1,4 @@ -FROM python:3.9-bullseye as base +FROM python:3.11-bookworm as base ENV PIP_EXTRA_INDEX_URL=https://www.piwheels.org/simple ENV PIP_FIND_LINKS=/wheeley @@ -18,7 +18,7 @@ RUN set -ex \ && bash -c "rm /app/firmware/${OTHER_SIM}" \ && rm /app/firmware/*.elf -FROM python:3.9-slim-bullseye +FROM python:3.11-slim-bookworm EXPOSE 5000 WORKDIR /app diff --git a/brewblox_devcon_spark/codec/lookup.py b/brewblox_devcon_spark/codec/lookup.py index b9097cb1..7e16df7e 100644 --- a/brewblox_devcon_spark/codec/lookup.py +++ b/brewblox_devcon_spark/codec/lookup.py @@ -6,12 +6,13 @@ from dataclasses import dataclass from typing import Generator, Optional, Type +from google.protobuf.descriptor import Descriptor, FileDescriptor +from google.protobuf.internal.enum_type_wrapper import EnumTypeWrapper from google.protobuf.message import Message -from google.protobuf.reflection import GeneratedProtocolMessageType from . import pb2 -BlockType = pb2.brewblox_pb2.BlockType +BlockType: EnumTypeWrapper = pb2.brewblox_pb2.BlockType @dataclass(frozen=True) @@ -38,22 +39,18 @@ def _interface_lookup_generator() -> Generator[InterfaceLookup, None, None]: def _object_lookup_generator() -> Generator[ObjectLookup, None, None]: - for pb in [getattr(pb2, k) for k in pb2.__all__]: - members = [getattr(pb, k) - for k in dir(pb) - if not k.startswith('_')] - messages = [el - for el in members - if isinstance(el, GeneratedProtocolMessageType)] - - for msg_cls in messages: - desc = msg_cls.DESCRIPTOR - opts = desc.GetOptions().Extensions[pb2.brewblox_pb2.msg] + for pb_module in [getattr(pb2, k) for k in pb2.__all__]: + file_desc: FileDescriptor = pb_module.DESCRIPTOR + messages: dict[str, Descriptor] = file_desc.message_types_by_name + + for msg_name, msg_desc in messages.items(): + msg_cls: Message = getattr(pb_module, msg_name) + opts = msg_desc.GetOptions().Extensions[pb2.brewblox_pb2.msg] if opts.objtype: yield ObjectLookup( type_str=BlockType.Name(opts.objtype), type_int=opts.objtype, - subtype_str=(desc.name if opts.subtype else None), + subtype_str=(msg_name if opts.subtype else None), subtype_int=opts.subtype, message_cls=msg_cls, ) diff --git a/brewblox_devcon_spark/codec/proto-compiled/ActuatorAnalogMock_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/ActuatorAnalogMock_pb2.py index aec43569..47799896 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/ActuatorAnalogMock_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/ActuatorAnalogMock_pb2.py @@ -4,9 +4,8 @@ """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -18,41 +17,34 @@ import Claims_pb2 as Claims__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x41\x63tuatorAnalogMock.proto\x12\x17\x62lox.ActuatorAnalogMock\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\x11\x43onstraints.proto\x1a\x0c\x43laims.proto\"\xca\x04\n\x05\x42lock\x12\x0f\n\x07\x65nabled\x18\r \x01(\x08\x12)\n\rstoredSetting\x18\x0b \x01(\x11\x42\x12\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12\x30\n\x0e\x64\x65siredSetting\x18\t \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12)\n\x07setting\x18\x01 \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12\'\n\x05value\x18\x02 \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12 \n\nminSetting\x18\x04 \x01(\x11\x42\x0c\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12 \n\nmaxSetting\x18\x05 \x01(\x11\x42\x0c\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12\x1e\n\x08minValue\x18\x06 \x01(\x11\x42\x0c\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12\x1e\n\x08maxValue\x18\x07 \x01(\x11\x42\x0c\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12\x44\n\rconstrainedBy\x18\x08 \x01(\x0b\x32-.blox.Constraints.DeprecatedAnalogConstraints\x12\x38\n\x0b\x63onstraints\x18\x0e \x01(\x0b\x32#.blox.Constraints.AnalogConstraints\x12%\n\tclaimedBy\x18\n \x01(\rB\x12\x8a\xb5\x18\x03\x18\xff\x01\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x10\x12-\n\x0bsettingMode\x18\x0c \x01(\x0e\x32\x18.blox.Claims.SettingMode:%\x8a\xb5\x18\x03\x18\xb1\x02\x8a\xb5\x18\x02H\x01\x8a\xb5\x18\x02H\x05\x8a\xb5\x18\x02H\x13\x8a\xb5\x18\x02H\x0f\x8a\xb5\x18\x02H\x10\x62\x06proto3') - - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'ActuatorAnalogMock_pb2' - # @@protoc_insertion_point(class_scope:blox.ActuatorAnalogMock.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x41\x63tuatorAnalogMock.proto\x12\x17\x62lox.ActuatorAnalogMock\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\x11\x43onstraints.proto\x1a\x0c\x43laims.proto\"\x93\x04\n\x05\x42lock\x12\x0f\n\x07\x65nabled\x18\r \x01(\x08\x12%\n\rstoredSetting\x18\x0b \x01(\x11\x42\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x10\x80 0\x01\x12(\n\x0e\x64\x65siredSetting\x18\t \x01(\x11\x42\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x10\x80 (\x01\x30\x01\x12!\n\x07setting\x18\x01 \x01(\x11\x42\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x10\x80 (\x01\x30\x01\x12\x1f\n\x05value\x18\x02 \x01(\x11\x42\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x10\x80 (\x01\x30\x01\x12 \n\nminSetting\x18\x04 \x01(\x11\x42\x0c\x92?\x02\x38 \x8a\xb5\x18\x03\x10\x80 \x12 \n\nmaxSetting\x18\x05 \x01(\x11\x42\x0c\x92?\x02\x38 \x8a\xb5\x18\x03\x10\x80 \x12\x1e\n\x08minValue\x18\x06 \x01(\x11\x42\x0c\x92?\x02\x38 \x8a\xb5\x18\x03\x10\x80 \x12\x1e\n\x08maxValue\x18\x07 \x01(\x11\x42\x0c\x92?\x02\x38 \x8a\xb5\x18\x03\x10\x80 \x12\x44\n\rconstrainedBy\x18\x08 \x01(\x0b\x32-.blox.Constraints.DeprecatedAnalogConstraints\x12\x38\n\x0b\x63onstraints\x18\x0e \x01(\x0b\x32#.blox.Constraints.AnalogConstraints\x12!\n\tclaimedBy\x18\n \x01(\rB\x0e\x92?\x02\x38\x10\x8a\xb5\x18\x05\x18\xff\x01(\x01\x12-\n\x0bsettingMode\x18\x0c \x01(\x0e\x32\x18.blox.Claims.SettingMode:\x0e\x8a\xb5\x18\n\x18\xb1\x02J\x05\x01\x05\x13\x0f\x10\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ActuatorAnalogMock_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _BLOCK.fields_by_name['storedSetting']._options = None - _BLOCK.fields_by_name['storedSetting']._serialized_options = b'\212\265\030\0020\001\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['storedSetting']._serialized_options = b'\222?\0028 \212\265\030\005\020\200 0\001' _BLOCK.fields_by_name['desiredSetting']._options = None - _BLOCK.fields_by_name['desiredSetting']._serialized_options = b'\212\265\030\0020\001\212\265\030\002(\001\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['desiredSetting']._serialized_options = b'\222?\0028 \212\265\030\007\020\200 (\0010\001' _BLOCK.fields_by_name['setting']._options = None - _BLOCK.fields_by_name['setting']._serialized_options = b'\212\265\030\0020\001\212\265\030\002(\001\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['setting']._serialized_options = b'\222?\0028 \212\265\030\007\020\200 (\0010\001' _BLOCK.fields_by_name['value']._options = None - _BLOCK.fields_by_name['value']._serialized_options = b'\212\265\030\0020\001\212\265\030\002(\001\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['value']._serialized_options = b'\222?\0028 \212\265\030\007\020\200 (\0010\001' _BLOCK.fields_by_name['minSetting']._options = None - _BLOCK.fields_by_name['minSetting']._serialized_options = b'\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['minSetting']._serialized_options = b'\222?\0028 \212\265\030\003\020\200 ' _BLOCK.fields_by_name['maxSetting']._options = None - _BLOCK.fields_by_name['maxSetting']._serialized_options = b'\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['maxSetting']._serialized_options = b'\222?\0028 \212\265\030\003\020\200 ' _BLOCK.fields_by_name['minValue']._options = None - _BLOCK.fields_by_name['minValue']._serialized_options = b'\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['minValue']._serialized_options = b'\222?\0028 \212\265\030\003\020\200 ' _BLOCK.fields_by_name['maxValue']._options = None - _BLOCK.fields_by_name['maxValue']._serialized_options = b'\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['maxValue']._serialized_options = b'\222?\0028 \212\265\030\003\020\200 ' _BLOCK.fields_by_name['claimedBy']._options = None - _BLOCK.fields_by_name['claimedBy']._serialized_options = b'\212\265\030\003\030\377\001\212\265\030\002(\001\222?\0028\020' + _BLOCK.fields_by_name['claimedBy']._serialized_options = b'\222?\0028\020\212\265\030\005\030\377\001(\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\261\002\212\265\030\002H\001\212\265\030\002H\005\212\265\030\002H\023\212\265\030\002H\017\212\265\030\002H\020' - _BLOCK._serialized_start=117 - _BLOCK._serialized_end=703 + _BLOCK._serialized_options = b'\212\265\030\n\030\261\002J\005\001\005\023\017\020' + _globals['_BLOCK']._serialized_start=117 + _globals['_BLOCK']._serialized_end=648 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/ActuatorLogic_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/ActuatorLogic_pb2.py index e42df303..1a38dc82 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/ActuatorLogic_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/ActuatorLogic_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: ActuatorLogic.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -18,80 +16,28 @@ import IoArray_pb2 as IoArray__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13\x41\x63tuatorLogic.proto\x12\x12\x62lox.ActuatorLogic\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\rIoArray.proto\"\xb7\x01\n\x0e\x44igitalCompare\x12/\n\x02op\x18\x01 \x01(\x0e\x32#.blox.ActuatorLogic.DigitalOperator\x12\x32\n\x06result\x18\x02 \x01(\x0e\x32\x1a.blox.ActuatorLogic.ResultB\x06\x8a\xb5\x18\x02(\x01\x12\x17\n\x02id\x18\x03 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x1b\x92?\x02\x38\x10\x12\'\n\x03rhs\x18\x04 \x01(\x0e\x32\x1a.blox.IoArray.DigitalState\"\xa7\x01\n\rAnalogCompare\x12.\n\x02op\x18\x01 \x01(\x0e\x32\".blox.ActuatorLogic.AnalogOperator\x12\x32\n\x06result\x18\x02 \x01(\x0e\x32\x1a.blox.ActuatorLogic.ResultB\x06\x8a\xb5\x18\x02(\x01\x12\x17\n\x02id\x18\x03 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x01\x92?\x02\x38\x10\x12\x19\n\x03rhs\x18\x04 \x01(\x11\x42\x0c\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \"\xd5\x02\n\x05\x42lock\x12\x1d\n\x08targetId\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x06\x92?\x02\x38\x10\x12\x0f\n\x07\x65nabled\x18\x03 \x01(\x08\x12\x38\n\x06result\x18\x04 \x01(\x0e\x32\x1a.blox.ActuatorLogic.ResultB\x0c\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02(\x01\x12\x19\n\nexpression\x18\x05 \x01(\tB\x05\x92?\x02p@\x12:\n\x07\x64igital\x18\x06 \x03(\x0b\x32\".blox.ActuatorLogic.DigitalCompareB\x05\x92?\x02\x10\x10\x12\x38\n\x06\x61nalog\x18\x07 \x03(\x0b\x32!.blox.ActuatorLogic.AnalogCompareB\x05\x92?\x02\x10\x10\x12\x1d\n\x08\x65rrorPos\x18\x08 \x01(\rB\x0b\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x08\x12#\n\x0e\x64rivenTargetId\x18Z \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03:\r\x8a\xb5\x18\x03\x18\xc2\x02\x8a\xb5\x18\x02H\x0f*\xcb\x03\n\x06Result\x12\x10\n\x0cRESULT_FALSE\x10\x00\x12\x0f\n\x0bRESULT_TRUE\x10\x01\x12\x10\n\x0cRESULT_EMPTY\x10\x02\x12\x1a\n\x16RESULT_EMPTY_SUBSTRING\x10\x03\x12\x1a\n\x16RESULT_BLOCK_NOT_FOUND\x10\x04\x12\x1d\n\x19RESULT_INVALID_DIGITAL_OP\x10\x05\x12\x1c\n\x18RESULT_INVALID_ANALOG_OP\x10\x06\x12$\n RESULT_UNDEFINED_DIGITAL_COMPARE\x10\x08\x12#\n\x1fRESULT_UNDEFINED_ANALOG_COMPARE\x10\x07\x12\"\n\x1eRESULT_UNEXPECTED_OPEN_BRACKET\x10\x0b\x12#\n\x1fRESULT_UNEXPECTED_CLOSE_BRACKET\x10\t\x12\x1f\n\x1bRESULT_UNEXPECTED_CHARACTER\x10\x0c\x12 \n\x1cRESULT_UNEXPECTED_COMPARISON\x10\r\x12\x1e\n\x1aRESULT_UNEXPECTED_OPERATOR\x10\x0e\x12 \n\x1cRESULT_MISSING_CLOSE_BRACKET\x10\n*a\n\x0f\x44igitalOperator\x12\x0f\n\x0bOP_VALUE_IS\x10\x00\x12\x13\n\x0fOP_VALUE_IS_NOT\x10\x01\x12\x11\n\rOP_DESIRED_IS\x10\n\x12\x15\n\x11OP_DESIRED_IS_NOT\x10\x0b*X\n\x0e\x41nalogOperator\x12\x0f\n\x0bOP_VALUE_LE\x10\x00\x12\x0f\n\x0bOP_VALUE_GE\x10\x01\x12\x11\n\rOP_SETTING_LE\x10\n\x12\x11\n\rOP_SETTING_GE\x10\x0b\x62\x06proto3') - -_RESULT = DESCRIPTOR.enum_types_by_name['Result'] -Result = enum_type_wrapper.EnumTypeWrapper(_RESULT) -_DIGITALOPERATOR = DESCRIPTOR.enum_types_by_name['DigitalOperator'] -DigitalOperator = enum_type_wrapper.EnumTypeWrapper(_DIGITALOPERATOR) -_ANALOGOPERATOR = DESCRIPTOR.enum_types_by_name['AnalogOperator'] -AnalogOperator = enum_type_wrapper.EnumTypeWrapper(_ANALOGOPERATOR) -RESULT_FALSE = 0 -RESULT_TRUE = 1 -RESULT_EMPTY = 2 -RESULT_EMPTY_SUBSTRING = 3 -RESULT_BLOCK_NOT_FOUND = 4 -RESULT_INVALID_DIGITAL_OP = 5 -RESULT_INVALID_ANALOG_OP = 6 -RESULT_UNDEFINED_DIGITAL_COMPARE = 8 -RESULT_UNDEFINED_ANALOG_COMPARE = 7 -RESULT_UNEXPECTED_OPEN_BRACKET = 11 -RESULT_UNEXPECTED_CLOSE_BRACKET = 9 -RESULT_UNEXPECTED_CHARACTER = 12 -RESULT_UNEXPECTED_COMPARISON = 13 -RESULT_UNEXPECTED_OPERATOR = 14 -RESULT_MISSING_CLOSE_BRACKET = 10 -OP_VALUE_IS = 0 -OP_VALUE_IS_NOT = 1 -OP_DESIRED_IS = 10 -OP_DESIRED_IS_NOT = 11 -OP_VALUE_LE = 0 -OP_VALUE_GE = 1 -OP_SETTING_LE = 10 -OP_SETTING_GE = 11 - - -_DIGITALCOMPARE = DESCRIPTOR.message_types_by_name['DigitalCompare'] -_ANALOGCOMPARE = DESCRIPTOR.message_types_by_name['AnalogCompare'] -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -DigitalCompare = _reflection.GeneratedProtocolMessageType('DigitalCompare', (_message.Message,), { - 'DESCRIPTOR' : _DIGITALCOMPARE, - '__module__' : 'ActuatorLogic_pb2' - # @@protoc_insertion_point(class_scope:blox.ActuatorLogic.DigitalCompare) - }) -_sym_db.RegisterMessage(DigitalCompare) - -AnalogCompare = _reflection.GeneratedProtocolMessageType('AnalogCompare', (_message.Message,), { - 'DESCRIPTOR' : _ANALOGCOMPARE, - '__module__' : 'ActuatorLogic_pb2' - # @@protoc_insertion_point(class_scope:blox.ActuatorLogic.AnalogCompare) - }) -_sym_db.RegisterMessage(AnalogCompare) - -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'ActuatorLogic_pb2' - # @@protoc_insertion_point(class_scope:blox.ActuatorLogic.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13\x41\x63tuatorLogic.proto\x12\x12\x62lox.ActuatorLogic\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\rIoArray.proto\"\xb7\x01\n\x0e\x44igitalCompare\x12/\n\x02op\x18\x01 \x01(\x0e\x32#.blox.ActuatorLogic.DigitalOperator\x12\x32\n\x06result\x18\x02 \x01(\x0e\x32\x1a.blox.ActuatorLogic.ResultB\x06\x8a\xb5\x18\x02(\x01\x12\x17\n\x02id\x18\x03 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x1b\x12\'\n\x03rhs\x18\x04 \x01(\x0e\x32\x1a.blox.IoArray.DigitalState\"\xa7\x01\n\rAnalogCompare\x12.\n\x02op\x18\x01 \x01(\x0e\x32\".blox.ActuatorLogic.AnalogOperator\x12\x32\n\x06result\x18\x02 \x01(\x0e\x32\x1a.blox.ActuatorLogic.ResultB\x06\x8a\xb5\x18\x02(\x01\x12\x17\n\x02id\x18\x03 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x01\x12\x19\n\x03rhs\x18\x04 \x01(\x11\x42\x0c\x92?\x02\x38 \x8a\xb5\x18\x03\x10\x80 \"\xce\x02\n\x05\x42lock\x12\x1d\n\x08targetId\x18\x01 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x06\x12\x0f\n\x07\x65nabled\x18\x03 \x01(\x08\x12\x34\n\x06result\x18\x04 \x01(\x0e\x32\x1a.blox.ActuatorLogic.ResultB\x08\x8a\xb5\x18\x04(\x01\x30\x01\x12\x19\n\nexpression\x18\x05 \x01(\tB\x05\x92?\x02p@\x12:\n\x07\x64igital\x18\x06 \x03(\x0b\x32\".blox.ActuatorLogic.DigitalCompareB\x05\x92?\x02\x10\x10\x12\x38\n\x06\x61nalog\x18\x07 \x03(\x0b\x32!.blox.ActuatorLogic.AnalogCompareB\x05\x92?\x02\x10\x10\x12\x1d\n\x08\x65rrorPos\x18\x08 \x01(\rB\x0b\x92?\x02\x38\x08\x8a\xb5\x18\x02(\x01\x12#\n\x0e\x64rivenTargetId\x18Z \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01:\n\x8a\xb5\x18\x06\x18\xc2\x02J\x01\x0f*\xcb\x03\n\x06Result\x12\x10\n\x0cRESULT_FALSE\x10\x00\x12\x0f\n\x0bRESULT_TRUE\x10\x01\x12\x10\n\x0cRESULT_EMPTY\x10\x02\x12\x1a\n\x16RESULT_EMPTY_SUBSTRING\x10\x03\x12\x1a\n\x16RESULT_BLOCK_NOT_FOUND\x10\x04\x12\x1d\n\x19RESULT_INVALID_DIGITAL_OP\x10\x05\x12\x1c\n\x18RESULT_INVALID_ANALOG_OP\x10\x06\x12$\n RESULT_UNDEFINED_DIGITAL_COMPARE\x10\x08\x12#\n\x1fRESULT_UNDEFINED_ANALOG_COMPARE\x10\x07\x12\"\n\x1eRESULT_UNEXPECTED_OPEN_BRACKET\x10\x0b\x12#\n\x1fRESULT_UNEXPECTED_CLOSE_BRACKET\x10\t\x12\x1f\n\x1bRESULT_UNEXPECTED_CHARACTER\x10\x0c\x12 \n\x1cRESULT_UNEXPECTED_COMPARISON\x10\r\x12\x1e\n\x1aRESULT_UNEXPECTED_OPERATOR\x10\x0e\x12 \n\x1cRESULT_MISSING_CLOSE_BRACKET\x10\n*a\n\x0f\x44igitalOperator\x12\x0f\n\x0bOP_VALUE_IS\x10\x00\x12\x13\n\x0fOP_VALUE_IS_NOT\x10\x01\x12\x11\n\rOP_DESIRED_IS\x10\n\x12\x15\n\x11OP_DESIRED_IS_NOT\x10\x0b*X\n\x0e\x41nalogOperator\x12\x0f\n\x0bOP_VALUE_LE\x10\x00\x12\x0f\n\x0bOP_VALUE_GE\x10\x01\x12\x11\n\rOP_SETTING_LE\x10\n\x12\x11\n\rOP_SETTING_GE\x10\x0b\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ActuatorLogic_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _DIGITALCOMPARE.fields_by_name['result']._options = None _DIGITALCOMPARE.fields_by_name['result']._serialized_options = b'\212\265\030\002(\001' _DIGITALCOMPARE.fields_by_name['id']._options = None - _DIGITALCOMPARE.fields_by_name['id']._serialized_options = b'\212\265\030\002\030\033\222?\0028\020' + _DIGITALCOMPARE.fields_by_name['id']._serialized_options = b'\222?\0028\020\212\265\030\002\030\033' _ANALOGCOMPARE.fields_by_name['result']._options = None _ANALOGCOMPARE.fields_by_name['result']._serialized_options = b'\212\265\030\002(\001' _ANALOGCOMPARE.fields_by_name['id']._options = None - _ANALOGCOMPARE.fields_by_name['id']._serialized_options = b'\212\265\030\002\030\001\222?\0028\020' + _ANALOGCOMPARE.fields_by_name['id']._serialized_options = b'\222?\0028\020\212\265\030\002\030\001' _ANALOGCOMPARE.fields_by_name['rhs']._options = None - _ANALOGCOMPARE.fields_by_name['rhs']._serialized_options = b'\212\265\030\003\020\200 \222?\0028 ' + _ANALOGCOMPARE.fields_by_name['rhs']._serialized_options = b'\222?\0028 \212\265\030\003\020\200 ' _BLOCK.fields_by_name['targetId']._options = None - _BLOCK.fields_by_name['targetId']._serialized_options = b'\212\265\030\002\030\006\222?\0028\020' + _BLOCK.fields_by_name['targetId']._serialized_options = b'\222?\0028\020\212\265\030\002\030\006' _BLOCK.fields_by_name['result']._options = None - _BLOCK.fields_by_name['result']._serialized_options = b'\212\265\030\0020\001\212\265\030\002(\001' + _BLOCK.fields_by_name['result']._serialized_options = b'\212\265\030\004(\0010\001' _BLOCK.fields_by_name['expression']._options = None _BLOCK.fields_by_name['expression']._serialized_options = b'\222?\002p@' _BLOCK.fields_by_name['digital']._options = None @@ -99,21 +45,21 @@ _BLOCK.fields_by_name['analog']._options = None _BLOCK.fields_by_name['analog']._serialized_options = b'\222?\002\020\020' _BLOCK.fields_by_name['errorPos']._options = None - _BLOCK.fields_by_name['errorPos']._serialized_options = b'\212\265\030\002(\001\222?\0028\010' + _BLOCK.fields_by_name['errorPos']._serialized_options = b'\222?\0028\010\212\265\030\002(\001' _BLOCK.fields_by_name['drivenTargetId']._options = None - _BLOCK.fields_by_name['drivenTargetId']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['drivenTargetId']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\302\002\212\265\030\002H\017' - _RESULT._serialized_start=789 - _RESULT._serialized_end=1248 - _DIGITALOPERATOR._serialized_start=1250 - _DIGITALOPERATOR._serialized_end=1347 - _ANALOGOPERATOR._serialized_start=1349 - _ANALOGOPERATOR._serialized_end=1437 - _DIGITALCOMPARE._serialized_start=89 - _DIGITALCOMPARE._serialized_end=272 - _ANALOGCOMPARE._serialized_start=275 - _ANALOGCOMPARE._serialized_end=442 - _BLOCK._serialized_start=445 - _BLOCK._serialized_end=786 + _BLOCK._serialized_options = b'\212\265\030\006\030\302\002J\001\017' + _globals['_RESULT']._serialized_start=782 + _globals['_RESULT']._serialized_end=1241 + _globals['_DIGITALOPERATOR']._serialized_start=1243 + _globals['_DIGITALOPERATOR']._serialized_end=1340 + _globals['_ANALOGOPERATOR']._serialized_start=1342 + _globals['_ANALOGOPERATOR']._serialized_end=1430 + _globals['_DIGITALCOMPARE']._serialized_start=89 + _globals['_DIGITALCOMPARE']._serialized_end=272 + _globals['_ANALOGCOMPARE']._serialized_start=275 + _globals['_ANALOGCOMPARE']._serialized_end=442 + _globals['_BLOCK']._serialized_start=445 + _globals['_BLOCK']._serialized_end=779 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/ActuatorOffset_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/ActuatorOffset_pb2.py index 3f0c6f27..418ff963 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/ActuatorOffset_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/ActuatorOffset_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: ActuatorOffset.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -19,45 +17,34 @@ import Claims_pb2 as Claims__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x41\x63tuatorOffset.proto\x12\x13\x62lox.ActuatorOffset\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\x11\x43onstraints.proto\x1a\x0c\x43laims.proto\"\x89\x05\n\x05\x42lock\x12\x0f\n\x07\x65nabled\x18\n \x01(\x08\x12\x1d\n\x08targetId\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x04\x92?\x02\x38\x10\x12 \n\x0breferenceId\x18\x03 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x04\x92?\x02\x38\x10\x12/\n\rstoredSetting\x18\r \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02\x08\x06\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12\x36\n\x0e\x64\x65siredSetting\x18\x0b \x01(\x11\x42\x1e\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x02\x08\x06\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12/\n\x07setting\x18\x06 \x01(\x11\x42\x1e\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x02\x08\x06\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12-\n\x05value\x18\x07 \x01(\x11\x42\x1e\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x03\x10\x80 \x8a\xb5\x18\x02\x08\x06\x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x12\x43\n\x17referenceSettingOrValue\x18\x04 \x01(\x0e\x32\".blox.ActuatorOffset.ReferenceKind\x12\x44\n\rconstrainedBy\x18\x08 \x01(\x0b\x32-.blox.Constraints.DeprecatedAnalogConstraints\x12\x38\n\x0b\x63onstraints\x18\x0f \x01(\x0b\x32#.blox.Constraints.AnalogConstraints\x12%\n\tclaimedBy\x18\x0c \x01(\rB\x12\x8a\xb5\x18\x03\x18\xff\x01\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x10\x12-\n\x0bsettingMode\x18\x0e \x01(\x0e\x32\x18.blox.Claims.SettingMode\x12#\n\x0e\x64rivenTargetId\x18Z \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03:%\x8a\xb5\x18\x03\x18\xb4\x02\x8a\xb5\x18\x02H\x01\x8a\xb5\x18\x02H\x05\x8a\xb5\x18\x02H\x13\x8a\xb5\x18\x02H\x0f\x8a\xb5\x18\x02H\x10*/\n\rReferenceKind\x12\x0f\n\x0bREF_SETTING\x10\x00\x12\r\n\tREF_VALUE\x10\x01\x62\x06proto3') - -_REFERENCEKIND = DESCRIPTOR.enum_types_by_name['ReferenceKind'] -ReferenceKind = enum_type_wrapper.EnumTypeWrapper(_REFERENCEKIND) -REF_SETTING = 0 -REF_VALUE = 1 - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'ActuatorOffset_pb2' - # @@protoc_insertion_point(class_scope:blox.ActuatorOffset.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x41\x63tuatorOffset.proto\x12\x13\x62lox.ActuatorOffset\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\x11\x43onstraints.proto\x1a\x0c\x43laims.proto\"\xc2\x04\n\x05\x42lock\x12\x0f\n\x07\x65nabled\x18\n \x01(\x08\x12\x1d\n\x08targetId\x18\x01 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x04\x12 \n\x0breferenceId\x18\x03 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x04\x12\'\n\rstoredSetting\x18\r \x01(\x11\x42\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x08\x06\x10\x80 0\x01\x12*\n\x0e\x64\x65siredSetting\x18\x0b \x01(\x11\x42\x12\x92?\x02\x38 \x8a\xb5\x18\t\x08\x06\x10\x80 (\x01\x30\x01\x12#\n\x07setting\x18\x06 \x01(\x11\x42\x12\x92?\x02\x38 \x8a\xb5\x18\t\x08\x06\x10\x80 (\x01\x30\x01\x12!\n\x05value\x18\x07 \x01(\x11\x42\x12\x92?\x02\x38 \x8a\xb5\x18\t\x08\x06\x10\x80 (\x01\x30\x01\x12\x43\n\x17referenceSettingOrValue\x18\x04 \x01(\x0e\x32\".blox.ActuatorOffset.ReferenceKind\x12\x44\n\rconstrainedBy\x18\x08 \x01(\x0b\x32-.blox.Constraints.DeprecatedAnalogConstraints\x12\x38\n\x0b\x63onstraints\x18\x0f \x01(\x0b\x32#.blox.Constraints.AnalogConstraints\x12!\n\tclaimedBy\x18\x0c \x01(\rB\x0e\x92?\x02\x38\x10\x8a\xb5\x18\x05\x18\xff\x01(\x01\x12-\n\x0bsettingMode\x18\x0e \x01(\x0e\x32\x18.blox.Claims.SettingMode\x12#\n\x0e\x64rivenTargetId\x18Z \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01:\x0e\x8a\xb5\x18\n\x18\xb4\x02J\x05\x01\x05\x13\x0f\x10*/\n\rReferenceKind\x12\x0f\n\x0bREF_SETTING\x10\x00\x12\r\n\tREF_VALUE\x10\x01\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ActuatorOffset_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _BLOCK.fields_by_name['targetId']._options = None - _BLOCK.fields_by_name['targetId']._serialized_options = b'\212\265\030\002\030\004\222?\0028\020' + _BLOCK.fields_by_name['targetId']._serialized_options = b'\222?\0028\020\212\265\030\002\030\004' _BLOCK.fields_by_name['referenceId']._options = None - _BLOCK.fields_by_name['referenceId']._serialized_options = b'\212\265\030\002\030\004\222?\0028\020' + _BLOCK.fields_by_name['referenceId']._serialized_options = b'\222?\0028\020\212\265\030\002\030\004' _BLOCK.fields_by_name['storedSetting']._options = None - _BLOCK.fields_by_name['storedSetting']._serialized_options = b'\212\265\030\0020\001\212\265\030\002\010\006\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['storedSetting']._serialized_options = b'\222?\0028 \212\265\030\007\010\006\020\200 0\001' _BLOCK.fields_by_name['desiredSetting']._options = None - _BLOCK.fields_by_name['desiredSetting']._serialized_options = b'\212\265\030\0020\001\212\265\030\002(\001\212\265\030\002\010\006\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['desiredSetting']._serialized_options = b'\222?\0028 \212\265\030\t\010\006\020\200 (\0010\001' _BLOCK.fields_by_name['setting']._options = None - _BLOCK.fields_by_name['setting']._serialized_options = b'\212\265\030\0020\001\212\265\030\002(\001\212\265\030\002\010\006\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['setting']._serialized_options = b'\222?\0028 \212\265\030\t\010\006\020\200 (\0010\001' _BLOCK.fields_by_name['value']._options = None - _BLOCK.fields_by_name['value']._serialized_options = b'\212\265\030\0020\001\212\265\030\003\020\200 \212\265\030\002\010\006\222?\0028 \212\265\030\002(\001' + _BLOCK.fields_by_name['value']._serialized_options = b'\222?\0028 \212\265\030\t\010\006\020\200 (\0010\001' _BLOCK.fields_by_name['claimedBy']._options = None - _BLOCK.fields_by_name['claimedBy']._serialized_options = b'\212\265\030\003\030\377\001\212\265\030\002(\001\222?\0028\020' + _BLOCK.fields_by_name['claimedBy']._serialized_options = b'\222?\0028\020\212\265\030\005\030\377\001(\001' _BLOCK.fields_by_name['drivenTargetId']._options = None - _BLOCK.fields_by_name['drivenTargetId']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['drivenTargetId']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\264\002\212\265\030\002H\001\212\265\030\002H\005\212\265\030\002H\023\212\265\030\002H\017\212\265\030\002H\020' - _REFERENCEKIND._serialized_start=760 - _REFERENCEKIND._serialized_end=807 - _BLOCK._serialized_start=109 - _BLOCK._serialized_end=758 + _BLOCK._serialized_options = b'\212\265\030\n\030\264\002J\005\001\005\023\017\020' + _globals['_REFERENCEKIND']._serialized_start=689 + _globals['_REFERENCEKIND']._serialized_end=736 + _globals['_BLOCK']._serialized_start=109 + _globals['_BLOCK']._serialized_end=687 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/ActuatorPwm_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/ActuatorPwm_pb2.py index 9eac289c..cf2e52bc 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/ActuatorPwm_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/ActuatorPwm_pb2.py @@ -4,9 +4,8 @@ """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -18,39 +17,32 @@ import Claims_pb2 as Claims__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11\x41\x63tuatorPwm.proto\x12\x10\x62lox.ActuatorPwm\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\x11\x43onstraints.proto\x1a\x0c\x43laims.proto\"\xad\x04\n\x05\x42lock\x12\x0f\n\x07\x65nabled\x18\x08 \x01(\x08\x12\x1f\n\nactuatorId\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x06\x92?\x02\x38\x10\x12)\n\rstoredSetting\x18\x0b \x01(\x11\x42\x12\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12\x30\n\x0e\x64\x65siredSetting\x18\t \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12)\n\x07setting\x18\x04 \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12\'\n\x05value\x18\x05 \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x12\x1d\n\x06period\x18\x03 \x01(\rB\r\x8a\xb5\x18\x02\x08\x03\x8a\xb5\x18\x03\x10\xe8\x07\x12\x44\n\rconstrainedBy\x18\x06 \x01(\x0b\x32-.blox.Constraints.DeprecatedAnalogConstraints\x12\x38\n\x0b\x63onstraints\x18\r \x01(\x0b\x32#.blox.Constraints.AnalogConstraints\x12%\n\tclaimedBy\x18\n \x01(\rB\x12\x8a\xb5\x18\x03\x18\xff\x01\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x10\x12-\n\x0bsettingMode\x18\x0c \x01(\x0e\x32\x18.blox.Claims.SettingMode\x12%\n\x10\x64rivenActuatorId\x18Z \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03:%\x8a\xb5\x18\x03\x18\xb3\x02\x8a\xb5\x18\x02H\x01\x8a\xb5\x18\x02H\x05\x8a\xb5\x18\x02H\x13\x8a\xb5\x18\x02H\x0f\x8a\xb5\x18\x02H\x10\x62\x06proto3') - - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'ActuatorPwm_pb2' - # @@protoc_insertion_point(class_scope:blox.ActuatorPwm.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11\x41\x63tuatorPwm.proto\x12\x10\x62lox.ActuatorPwm\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\x11\x43onstraints.proto\x1a\x0c\x43laims.proto\"\xf2\x03\n\x05\x42lock\x12\x0f\n\x07\x65nabled\x18\x08 \x01(\x08\x12\x1f\n\nactuatorId\x18\x01 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x06\x12%\n\rstoredSetting\x18\x0b \x01(\x11\x42\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x10\x80 0\x01\x12(\n\x0e\x64\x65siredSetting\x18\t \x01(\x11\x42\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x10\x80 (\x01\x30\x01\x12!\n\x07setting\x18\x04 \x01(\x11\x42\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x10\x80 (\x01\x30\x01\x12\x1f\n\x05value\x18\x05 \x01(\x11\x42\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x10\x80 (\x01\x30\x01\x12\x19\n\x06period\x18\x03 \x01(\rB\t\x8a\xb5\x18\x05\x08\x03\x10\xe8\x07\x12\x44\n\rconstrainedBy\x18\x06 \x01(\x0b\x32-.blox.Constraints.DeprecatedAnalogConstraints\x12\x38\n\x0b\x63onstraints\x18\r \x01(\x0b\x32#.blox.Constraints.AnalogConstraints\x12!\n\tclaimedBy\x18\n \x01(\rB\x0e\x92?\x02\x38\x10\x8a\xb5\x18\x05\x18\xff\x01(\x01\x12-\n\x0bsettingMode\x18\x0c \x01(\x0e\x32\x18.blox.Claims.SettingMode\x12%\n\x10\x64rivenActuatorId\x18Z \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01:\x0e\x8a\xb5\x18\n\x18\xb3\x02J\x05\x01\x05\x13\x0f\x10\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ActuatorPwm_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _BLOCK.fields_by_name['actuatorId']._options = None - _BLOCK.fields_by_name['actuatorId']._serialized_options = b'\212\265\030\002\030\006\222?\0028\020' + _BLOCK.fields_by_name['actuatorId']._serialized_options = b'\222?\0028\020\212\265\030\002\030\006' _BLOCK.fields_by_name['storedSetting']._options = None - _BLOCK.fields_by_name['storedSetting']._serialized_options = b'\212\265\030\0020\001\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['storedSetting']._serialized_options = b'\222?\0028 \212\265\030\005\020\200 0\001' _BLOCK.fields_by_name['desiredSetting']._options = None - _BLOCK.fields_by_name['desiredSetting']._serialized_options = b'\212\265\030\0020\001\212\265\030\002(\001\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['desiredSetting']._serialized_options = b'\222?\0028 \212\265\030\007\020\200 (\0010\001' _BLOCK.fields_by_name['setting']._options = None - _BLOCK.fields_by_name['setting']._serialized_options = b'\212\265\030\0020\001\212\265\030\002(\001\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['setting']._serialized_options = b'\222?\0028 \212\265\030\007\020\200 (\0010\001' _BLOCK.fields_by_name['value']._options = None - _BLOCK.fields_by_name['value']._serialized_options = b'\212\265\030\0020\001\212\265\030\003\020\200 \222?\0028 \212\265\030\002(\001' + _BLOCK.fields_by_name['value']._serialized_options = b'\222?\0028 \212\265\030\007\020\200 (\0010\001' _BLOCK.fields_by_name['period']._options = None - _BLOCK.fields_by_name['period']._serialized_options = b'\212\265\030\002\010\003\212\265\030\003\020\350\007' + _BLOCK.fields_by_name['period']._serialized_options = b'\212\265\030\005\010\003\020\350\007' _BLOCK.fields_by_name['claimedBy']._options = None - _BLOCK.fields_by_name['claimedBy']._serialized_options = b'\212\265\030\003\030\377\001\212\265\030\002(\001\222?\0028\020' + _BLOCK.fields_by_name['claimedBy']._serialized_options = b'\222?\0028\020\212\265\030\005\030\377\001(\001' _BLOCK.fields_by_name['drivenActuatorId']._options = None - _BLOCK.fields_by_name['drivenActuatorId']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['drivenActuatorId']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\263\002\212\265\030\002H\001\212\265\030\002H\005\212\265\030\002H\023\212\265\030\002H\017\212\265\030\002H\020' - _BLOCK._serialized_start=103 - _BLOCK._serialized_end=660 + _BLOCK._serialized_options = b'\212\265\030\n\030\263\002J\005\001\005\023\017\020' + _globals['_BLOCK']._serialized_start=103 + _globals['_BLOCK']._serialized_end=601 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/Balancer_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/Balancer_pb2.py index de604759..5fa94403 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/Balancer_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/Balancer_pb2.py @@ -4,9 +4,8 @@ """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -16,41 +15,26 @@ import nanopb_pb2 as nanopb__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0e\x42\x61lancer.proto\x12\rblox.Balancer\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"\x90\x01\n\x10\x42\x61lancedActuator\x12$\n\x02id\x18\x01 \x01(\rB\x18\x8a\xb5\x18\x03\x18\xff\x01\x8a\xb5\x18\x02\x30\x00\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x10\x12+\n\trequested\x18\x02 \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x00\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x12)\n\x07granted\x18\x03 \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x00\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x8a\xb5\x18\x02(\x01\"V\n\x05\x42lock\x12>\n\x07\x63lients\x18\x01 \x03(\x0b\x32\x1f.blox.Balancer.BalancedActuatorB\x0c\x8a\xb5\x18\x02\x30\x00\x8a\xb5\x18\x02(\x01:\r\x8a\xb5\x18\x03\x18\xb5\x02\x8a\xb5\x18\x02H\x07\x62\x06proto3') - - - -_BALANCEDACTUATOR = DESCRIPTOR.message_types_by_name['BalancedActuator'] -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -BalancedActuator = _reflection.GeneratedProtocolMessageType('BalancedActuator', (_message.Message,), { - 'DESCRIPTOR' : _BALANCEDACTUATOR, - '__module__' : 'Balancer_pb2' - # @@protoc_insertion_point(class_scope:blox.Balancer.BalancedActuator) - }) -_sym_db.RegisterMessage(BalancedActuator) - -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'Balancer_pb2' - # @@protoc_insertion_point(class_scope:blox.Balancer.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0e\x42\x61lancer.proto\x12\rblox.Balancer\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"r\n\x10\x42\x61lancedActuator\x12\x1a\n\x02id\x18\x01 \x01(\rB\x0e\x92?\x02\x38\x10\x8a\xb5\x18\x05\x18\xff\x01(\x01\x12!\n\trequested\x18\x02 \x01(\x11\x42\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x10\x80 (\x01\x12\x1f\n\x07granted\x18\x03 \x01(\x11\x42\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x10\x80 (\x01\"M\n\x05\x42lock\x12\x38\n\x07\x63lients\x18\x01 \x03(\x0b\x32\x1f.blox.Balancer.BalancedActuatorB\x06\x8a\xb5\x18\x02(\x01:\n\x8a\xb5\x18\x06\x18\xb5\x02J\x01\x07\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Balancer_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _BALANCEDACTUATOR.fields_by_name['id']._options = None - _BALANCEDACTUATOR.fields_by_name['id']._serialized_options = b'\212\265\030\003\030\377\001\212\265\030\0020\000\212\265\030\002(\001\222?\0028\020' + _BALANCEDACTUATOR.fields_by_name['id']._serialized_options = b'\222?\0028\020\212\265\030\005\030\377\001(\001' _BALANCEDACTUATOR.fields_by_name['requested']._options = None - _BALANCEDACTUATOR.fields_by_name['requested']._serialized_options = b'\212\265\030\0020\000\212\265\030\003\020\200 \222?\0028 \212\265\030\002(\001' + _BALANCEDACTUATOR.fields_by_name['requested']._serialized_options = b'\222?\0028 \212\265\030\005\020\200 (\001' _BALANCEDACTUATOR.fields_by_name['granted']._options = None - _BALANCEDACTUATOR.fields_by_name['granted']._serialized_options = b'\212\265\030\0020\000\212\265\030\003\020\200 \222?\0028 \212\265\030\002(\001' + _BALANCEDACTUATOR.fields_by_name['granted']._serialized_options = b'\222?\0028 \212\265\030\005\020\200 (\001' _BLOCK.fields_by_name['clients']._options = None - _BLOCK.fields_by_name['clients']._serialized_options = b'\212\265\030\0020\000\212\265\030\002(\001' + _BLOCK.fields_by_name['clients']._serialized_options = b'\212\265\030\002(\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\265\002\212\265\030\002H\007' - _BALANCEDACTUATOR._serialized_start=64 - _BALANCEDACTUATOR._serialized_end=208 - _BLOCK._serialized_start=210 - _BLOCK._serialized_end=296 + _BLOCK._serialized_options = b'\212\265\030\006\030\265\002J\001\007' + _globals['_BALANCEDACTUATOR']._serialized_start=63 + _globals['_BALANCEDACTUATOR']._serialized_end=177 + _globals['_BLOCK']._serialized_start=179 + _globals['_BLOCK']._serialized_end=256 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/Claims_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/Claims_pb2.py index 48540c54..48b6b72e 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/Claims_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/Claims_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: Claims.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -17,15 +15,12 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0c\x43laims.proto\x12\x0b\x62lox.Claims*&\n\x0bSettingMode\x12\n\n\x06STORED\x10\x00\x12\x0b\n\x07\x43LAIMED\x10\x01\x62\x06proto3') -_SETTINGMODE = DESCRIPTOR.enum_types_by_name['SettingMode'] -SettingMode = enum_type_wrapper.EnumTypeWrapper(_SETTINGMODE) -STORED = 0 -CLAIMED = 1 - - +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Claims_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None - _SETTINGMODE._serialized_start=29 - _SETTINGMODE._serialized_end=67 + _globals['_SETTINGMODE']._serialized_start=29 + _globals['_SETTINGMODE']._serialized_end=67 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/Constraints_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/Constraints_pb2.py index 42c8082e..cb9538da 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/Constraints_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/Constraints_pb2.py @@ -4,9 +4,8 @@ """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -16,165 +15,86 @@ import nanopb_pb2 as nanopb__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11\x43onstraints.proto\x12\x10\x62lox.Constraints\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"Y\n\x0fValueConstraint\x12\x1b\n\x05value\x18\x01 \x01(\x11\x42\x0c\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12\x0f\n\x07\x65nabled\x18\x32 \x01(\x08\x12\x18\n\x08limiting\x18\x33 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\"\x99\x01\n\x12\x42\x61lancedConstraint\x12\x1f\n\nbalancerId\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x07\x92?\x02\x38\x10\x12\x1e\n\x07granted\x18\x02 \x01(\rB\r\x8a\xb5\x18\x03\x10\x80 \x8a\xb5\x18\x02(\x01\x12\x0f\n\x07\x65nabled\x18\x32 \x01(\x08\x12\x18\n\x08limiting\x18\x33 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\x12\x17\n\x02id\x18Z \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03\"\x92\x01\n\x12\x44urationConstraint\x12$\n\x08\x64uration\x18\x01 \x01(\rB\x12\x8a\xb5\x18\x02\x08\x03\x8a\xb5\x18\x03\x10\xe8\x07\x92?\x02\x38 \x12\x0f\n\x07\x65nabled\x18\x32 \x01(\x08\x12\x18\n\x08limiting\x18\x33 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\x12+\n\tremaining\x18\x34 \x01(\rB\x18\x8a\xb5\x18\x02\x08\x03\x8a\xb5\x18\x03\x10\xe8\x07\x8a\xb5\x18\x02(\x01\x92?\x02\x38 \"\xf5\x01\n\x11MutexedConstraint\x12\x1c\n\x07mutexId\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x08\x92?\x02\x38\x10\x12)\n\rextraHoldTime\x18\x02 \x01(\rB\x12\x8a\xb5\x18\x02\x08\x03\x8a\xb5\x18\x03\x10\xe8\x07\x92?\x02\x38 \x12\x17\n\x07hasLock\x18\x04 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\x12\x0f\n\x07\x65nabled\x18\x32 \x01(\x08\x12\x18\n\x08limiting\x18\x33 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\x12+\n\tremaining\x18\x34 \x01(\rB\x18\x8a\xb5\x18\x02\x08\x03\x8a\xb5\x18\x03\x10\xe8\x07\x8a\xb5\x18\x02(\x01\x92?\x02\x38 \x12&\n\x11hasCustomHoldTime\x18Z \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03\"\xab\x01\n\x11\x41nalogConstraints\x12.\n\x03min\x18\x01 \x01(\x0b\x32!.blox.Constraints.ValueConstraint\x12.\n\x03max\x18\x02 \x01(\x0b\x32!.blox.Constraints.ValueConstraint\x12\x36\n\x08\x62\x61lanced\x18\x03 \x01(\x0b\x32$.blox.Constraints.BalancedConstraint\"\xa8\x02\n\x12\x44igitalConstraints\x12\x34\n\x06minOff\x18\x01 \x01(\x0b\x32$.blox.Constraints.DurationConstraint\x12\x33\n\x05minOn\x18\x02 \x01(\x0b\x32$.blox.Constraints.DurationConstraint\x12\x38\n\ndelayedOff\x18\x03 \x01(\x0b\x32$.blox.Constraints.DurationConstraint\x12\x37\n\tdelayedOn\x18\x04 \x01(\x0b\x32$.blox.Constraints.DurationConstraint\x12\x34\n\x07mutexed\x18\x05 \x01(\x0b\x32#.blox.Constraints.MutexedConstraint\"\xb8\x01\n\x1a\x44\x65precatedAnalogConstraint\x12\x1b\n\x03min\x18\x01 \x01(\x11\x42\x0c\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 H\x00\x12\x1b\n\x03max\x18\x02 \x01(\x11\x42\x0c\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 H\x00\x12\x38\n\x08\x62\x61lanced\x18\x03 \x01(\x0b\x32$.blox.Constraints.BalancedConstraintH\x00\x12\x18\n\x08limiting\x18\x64 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\x42\x0c\n\nconstraint\"g\n\x1b\x44\x65precatedAnalogConstraints\x12H\n\x0b\x63onstraints\x18\x01 \x03(\x0b\x32,.blox.Constraints.DeprecatedAnalogConstraintB\x05\x92?\x02\x10\x08\"\xeb\x02\n\x1b\x44\x65precatedDigitalConstraint\x12$\n\x06minOff\x18\x01 \x01(\rB\x12\x8a\xb5\x18\x02\x08\x03\x8a\xb5\x18\x03\x10\xe8\x07\x92?\x02\x38 H\x00\x12#\n\x05minOn\x18\x02 \x01(\rB\x12\x8a\xb5\x18\x02\x08\x03\x8a\xb5\x18\x03\x10\xe8\x07\x92?\x02\x38 H\x00\x12\x36\n\x07mutexed\x18\x04 \x01(\x0b\x32#.blox.Constraints.MutexedConstraintH\x00\x12(\n\ndelayedOff\x18\x05 \x01(\rB\x12\x8a\xb5\x18\x02\x08\x03\x8a\xb5\x18\x03\x10\xe8\x07\x92?\x02\x38 H\x00\x12\'\n\tdelayedOn\x18\x06 \x01(\rB\x12\x8a\xb5\x18\x02\x08\x03\x8a\xb5\x18\x03\x10\xe8\x07\x92?\x02\x38 H\x00\x12\x1c\n\x05mutex\x18\x03 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x08\x92?\x02\x38\x10H\x00\x12\x1d\n\x08limiting\x18\x64 \x01(\rB\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03\x12+\n\tremaining\x18\x65 \x01(\rB\x18\x8a\xb5\x18\x02\x08\x03\x8a\xb5\x18\x03\x10\xe8\x07\x8a\xb5\x18\x02(\x01\x92?\x02\x38 B\x0c\n\nconstraint\"i\n\x1c\x44\x65precatedDigitalConstraints\x12I\n\x0b\x63onstraints\x18\x01 \x03(\x0b\x32-.blox.Constraints.DeprecatedDigitalConstraintB\x05\x92?\x02\x10\x08\x62\x06proto3') - - - -_VALUECONSTRAINT = DESCRIPTOR.message_types_by_name['ValueConstraint'] -_BALANCEDCONSTRAINT = DESCRIPTOR.message_types_by_name['BalancedConstraint'] -_DURATIONCONSTRAINT = DESCRIPTOR.message_types_by_name['DurationConstraint'] -_MUTEXEDCONSTRAINT = DESCRIPTOR.message_types_by_name['MutexedConstraint'] -_ANALOGCONSTRAINTS = DESCRIPTOR.message_types_by_name['AnalogConstraints'] -_DIGITALCONSTRAINTS = DESCRIPTOR.message_types_by_name['DigitalConstraints'] -_DEPRECATEDANALOGCONSTRAINT = DESCRIPTOR.message_types_by_name['DeprecatedAnalogConstraint'] -_DEPRECATEDANALOGCONSTRAINTS = DESCRIPTOR.message_types_by_name['DeprecatedAnalogConstraints'] -_DEPRECATEDDIGITALCONSTRAINT = DESCRIPTOR.message_types_by_name['DeprecatedDigitalConstraint'] -_DEPRECATEDDIGITALCONSTRAINTS = DESCRIPTOR.message_types_by_name['DeprecatedDigitalConstraints'] -ValueConstraint = _reflection.GeneratedProtocolMessageType('ValueConstraint', (_message.Message,), { - 'DESCRIPTOR' : _VALUECONSTRAINT, - '__module__' : 'Constraints_pb2' - # @@protoc_insertion_point(class_scope:blox.Constraints.ValueConstraint) - }) -_sym_db.RegisterMessage(ValueConstraint) - -BalancedConstraint = _reflection.GeneratedProtocolMessageType('BalancedConstraint', (_message.Message,), { - 'DESCRIPTOR' : _BALANCEDCONSTRAINT, - '__module__' : 'Constraints_pb2' - # @@protoc_insertion_point(class_scope:blox.Constraints.BalancedConstraint) - }) -_sym_db.RegisterMessage(BalancedConstraint) - -DurationConstraint = _reflection.GeneratedProtocolMessageType('DurationConstraint', (_message.Message,), { - 'DESCRIPTOR' : _DURATIONCONSTRAINT, - '__module__' : 'Constraints_pb2' - # @@protoc_insertion_point(class_scope:blox.Constraints.DurationConstraint) - }) -_sym_db.RegisterMessage(DurationConstraint) - -MutexedConstraint = _reflection.GeneratedProtocolMessageType('MutexedConstraint', (_message.Message,), { - 'DESCRIPTOR' : _MUTEXEDCONSTRAINT, - '__module__' : 'Constraints_pb2' - # @@protoc_insertion_point(class_scope:blox.Constraints.MutexedConstraint) - }) -_sym_db.RegisterMessage(MutexedConstraint) - -AnalogConstraints = _reflection.GeneratedProtocolMessageType('AnalogConstraints', (_message.Message,), { - 'DESCRIPTOR' : _ANALOGCONSTRAINTS, - '__module__' : 'Constraints_pb2' - # @@protoc_insertion_point(class_scope:blox.Constraints.AnalogConstraints) - }) -_sym_db.RegisterMessage(AnalogConstraints) - -DigitalConstraints = _reflection.GeneratedProtocolMessageType('DigitalConstraints', (_message.Message,), { - 'DESCRIPTOR' : _DIGITALCONSTRAINTS, - '__module__' : 'Constraints_pb2' - # @@protoc_insertion_point(class_scope:blox.Constraints.DigitalConstraints) - }) -_sym_db.RegisterMessage(DigitalConstraints) - -DeprecatedAnalogConstraint = _reflection.GeneratedProtocolMessageType('DeprecatedAnalogConstraint', (_message.Message,), { - 'DESCRIPTOR' : _DEPRECATEDANALOGCONSTRAINT, - '__module__' : 'Constraints_pb2' - # @@protoc_insertion_point(class_scope:blox.Constraints.DeprecatedAnalogConstraint) - }) -_sym_db.RegisterMessage(DeprecatedAnalogConstraint) - -DeprecatedAnalogConstraints = _reflection.GeneratedProtocolMessageType('DeprecatedAnalogConstraints', (_message.Message,), { - 'DESCRIPTOR' : _DEPRECATEDANALOGCONSTRAINTS, - '__module__' : 'Constraints_pb2' - # @@protoc_insertion_point(class_scope:blox.Constraints.DeprecatedAnalogConstraints) - }) -_sym_db.RegisterMessage(DeprecatedAnalogConstraints) - -DeprecatedDigitalConstraint = _reflection.GeneratedProtocolMessageType('DeprecatedDigitalConstraint', (_message.Message,), { - 'DESCRIPTOR' : _DEPRECATEDDIGITALCONSTRAINT, - '__module__' : 'Constraints_pb2' - # @@protoc_insertion_point(class_scope:blox.Constraints.DeprecatedDigitalConstraint) - }) -_sym_db.RegisterMessage(DeprecatedDigitalConstraint) - -DeprecatedDigitalConstraints = _reflection.GeneratedProtocolMessageType('DeprecatedDigitalConstraints', (_message.Message,), { - 'DESCRIPTOR' : _DEPRECATEDDIGITALCONSTRAINTS, - '__module__' : 'Constraints_pb2' - # @@protoc_insertion_point(class_scope:blox.Constraints.DeprecatedDigitalConstraints) - }) -_sym_db.RegisterMessage(DeprecatedDigitalConstraints) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11\x43onstraints.proto\x12\x10\x62lox.Constraints\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"Y\n\x0fValueConstraint\x12\x1b\n\x05value\x18\x01 \x01(\x11\x42\x0c\x92?\x02\x38 \x8a\xb5\x18\x03\x10\x80 \x12\x0f\n\x07\x65nabled\x18\x32 \x01(\x08\x12\x18\n\x08limiting\x18\x33 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\"\x95\x01\n\x12\x42\x61lancedConstraint\x12\x1f\n\nbalancerId\x18\x01 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x07\x12\x1a\n\x07granted\x18\x02 \x01(\rB\t\x8a\xb5\x18\x05\x10\x80 (\x01\x12\x0f\n\x07\x65nabled\x18\x32 \x01(\x08\x12\x18\n\x08limiting\x18\x33 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\x12\x17\n\x02id\x18Z \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01\"\x86\x01\n\x12\x44urationConstraint\x12 \n\x08\x64uration\x18\x01 \x01(\rB\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x08\x03\x10\xe8\x07\x12\x0f\n\x07\x65nabled\x18\x32 \x01(\x08\x12\x18\n\x08limiting\x18\x33 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\x12#\n\tremaining\x18\x34 \x01(\rB\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x08\x03\x10\xe8\x07(\x01\"\xe9\x01\n\x11MutexedConstraint\x12\x1c\n\x07mutexId\x18\x01 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x08\x12%\n\rextraHoldTime\x18\x02 \x01(\rB\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x08\x03\x10\xe8\x07\x12\x17\n\x07hasLock\x18\x04 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\x12\x0f\n\x07\x65nabled\x18\x32 \x01(\x08\x12\x18\n\x08limiting\x18\x33 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\x12#\n\tremaining\x18\x34 \x01(\rB\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x08\x03\x10\xe8\x07(\x01\x12&\n\x11hasCustomHoldTime\x18Z \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01\"\xab\x01\n\x11\x41nalogConstraints\x12.\n\x03min\x18\x01 \x01(\x0b\x32!.blox.Constraints.ValueConstraint\x12.\n\x03max\x18\x02 \x01(\x0b\x32!.blox.Constraints.ValueConstraint\x12\x36\n\x08\x62\x61lanced\x18\x03 \x01(\x0b\x32$.blox.Constraints.BalancedConstraint\"\xa8\x02\n\x12\x44igitalConstraints\x12\x34\n\x06minOff\x18\x01 \x01(\x0b\x32$.blox.Constraints.DurationConstraint\x12\x33\n\x05minOn\x18\x02 \x01(\x0b\x32$.blox.Constraints.DurationConstraint\x12\x38\n\ndelayedOff\x18\x03 \x01(\x0b\x32$.blox.Constraints.DurationConstraint\x12\x37\n\tdelayedOn\x18\x04 \x01(\x0b\x32$.blox.Constraints.DurationConstraint\x12\x34\n\x07mutexed\x18\x05 \x01(\x0b\x32#.blox.Constraints.MutexedConstraint\"\xb8\x01\n\x1a\x44\x65precatedAnalogConstraint\x12\x1b\n\x03min\x18\x01 \x01(\x11\x42\x0c\x92?\x02\x38 \x8a\xb5\x18\x03\x10\x80 H\x00\x12\x1b\n\x03max\x18\x02 \x01(\x11\x42\x0c\x92?\x02\x38 \x8a\xb5\x18\x03\x10\x80 H\x00\x12\x38\n\x08\x62\x61lanced\x18\x03 \x01(\x0b\x32$.blox.Constraints.BalancedConstraintH\x00\x12\x18\n\x08limiting\x18\x64 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\x42\x0c\n\nconstraint\"g\n\x1b\x44\x65precatedAnalogConstraints\x12H\n\x0b\x63onstraints\x18\x01 \x03(\x0b\x32,.blox.Constraints.DeprecatedAnalogConstraintB\x05\x92?\x02\x10\x08\"\xd3\x02\n\x1b\x44\x65precatedDigitalConstraint\x12 \n\x06minOff\x18\x01 \x01(\rB\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x08\x03\x10\xe8\x07H\x00\x12\x1f\n\x05minOn\x18\x02 \x01(\rB\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x08\x03\x10\xe8\x07H\x00\x12\x36\n\x07mutexed\x18\x04 \x01(\x0b\x32#.blox.Constraints.MutexedConstraintH\x00\x12$\n\ndelayedOff\x18\x05 \x01(\rB\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x08\x03\x10\xe8\x07H\x00\x12#\n\tdelayedOn\x18\x06 \x01(\rB\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x08\x03\x10\xe8\x07H\x00\x12\x1c\n\x05mutex\x18\x03 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x08H\x00\x12\x1d\n\x08limiting\x18\x64 \x01(\rB\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01\x12#\n\tremaining\x18\x65 \x01(\rB\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x08\x03\x10\xe8\x07(\x01\x42\x0c\n\nconstraint\"i\n\x1c\x44\x65precatedDigitalConstraints\x12I\n\x0b\x63onstraints\x18\x01 \x03(\x0b\x32-.blox.Constraints.DeprecatedDigitalConstraintB\x05\x92?\x02\x10\x08\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Constraints_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _VALUECONSTRAINT.fields_by_name['value']._options = None - _VALUECONSTRAINT.fields_by_name['value']._serialized_options = b'\212\265\030\003\020\200 \222?\0028 ' + _VALUECONSTRAINT.fields_by_name['value']._serialized_options = b'\222?\0028 \212\265\030\003\020\200 ' _VALUECONSTRAINT.fields_by_name['limiting']._options = None _VALUECONSTRAINT.fields_by_name['limiting']._serialized_options = b'\212\265\030\002(\001' _BALANCEDCONSTRAINT.fields_by_name['balancerId']._options = None - _BALANCEDCONSTRAINT.fields_by_name['balancerId']._serialized_options = b'\212\265\030\002\030\007\222?\0028\020' + _BALANCEDCONSTRAINT.fields_by_name['balancerId']._serialized_options = b'\222?\0028\020\212\265\030\002\030\007' _BALANCEDCONSTRAINT.fields_by_name['granted']._options = None - _BALANCEDCONSTRAINT.fields_by_name['granted']._serialized_options = b'\212\265\030\003\020\200 \212\265\030\002(\001' + _BALANCEDCONSTRAINT.fields_by_name['granted']._serialized_options = b'\212\265\030\005\020\200 (\001' _BALANCEDCONSTRAINT.fields_by_name['limiting']._options = None _BALANCEDCONSTRAINT.fields_by_name['limiting']._serialized_options = b'\212\265\030\002(\001' _BALANCEDCONSTRAINT.fields_by_name['id']._options = None - _BALANCEDCONSTRAINT.fields_by_name['id']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BALANCEDCONSTRAINT.fields_by_name['id']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _DURATIONCONSTRAINT.fields_by_name['duration']._options = None - _DURATIONCONSTRAINT.fields_by_name['duration']._serialized_options = b'\212\265\030\002\010\003\212\265\030\003\020\350\007\222?\0028 ' + _DURATIONCONSTRAINT.fields_by_name['duration']._serialized_options = b'\222?\0028 \212\265\030\005\010\003\020\350\007' _DURATIONCONSTRAINT.fields_by_name['limiting']._options = None _DURATIONCONSTRAINT.fields_by_name['limiting']._serialized_options = b'\212\265\030\002(\001' _DURATIONCONSTRAINT.fields_by_name['remaining']._options = None - _DURATIONCONSTRAINT.fields_by_name['remaining']._serialized_options = b'\212\265\030\002\010\003\212\265\030\003\020\350\007\212\265\030\002(\001\222?\0028 ' + _DURATIONCONSTRAINT.fields_by_name['remaining']._serialized_options = b'\222?\0028 \212\265\030\007\010\003\020\350\007(\001' _MUTEXEDCONSTRAINT.fields_by_name['mutexId']._options = None - _MUTEXEDCONSTRAINT.fields_by_name['mutexId']._serialized_options = b'\212\265\030\002\030\010\222?\0028\020' + _MUTEXEDCONSTRAINT.fields_by_name['mutexId']._serialized_options = b'\222?\0028\020\212\265\030\002\030\010' _MUTEXEDCONSTRAINT.fields_by_name['extraHoldTime']._options = None - _MUTEXEDCONSTRAINT.fields_by_name['extraHoldTime']._serialized_options = b'\212\265\030\002\010\003\212\265\030\003\020\350\007\222?\0028 ' + _MUTEXEDCONSTRAINT.fields_by_name['extraHoldTime']._serialized_options = b'\222?\0028 \212\265\030\005\010\003\020\350\007' _MUTEXEDCONSTRAINT.fields_by_name['hasLock']._options = None _MUTEXEDCONSTRAINT.fields_by_name['hasLock']._serialized_options = b'\212\265\030\002(\001' _MUTEXEDCONSTRAINT.fields_by_name['limiting']._options = None _MUTEXEDCONSTRAINT.fields_by_name['limiting']._serialized_options = b'\212\265\030\002(\001' _MUTEXEDCONSTRAINT.fields_by_name['remaining']._options = None - _MUTEXEDCONSTRAINT.fields_by_name['remaining']._serialized_options = b'\212\265\030\002\010\003\212\265\030\003\020\350\007\212\265\030\002(\001\222?\0028 ' + _MUTEXEDCONSTRAINT.fields_by_name['remaining']._serialized_options = b'\222?\0028 \212\265\030\007\010\003\020\350\007(\001' _MUTEXEDCONSTRAINT.fields_by_name['hasCustomHoldTime']._options = None - _MUTEXEDCONSTRAINT.fields_by_name['hasCustomHoldTime']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _MUTEXEDCONSTRAINT.fields_by_name['hasCustomHoldTime']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _DEPRECATEDANALOGCONSTRAINT.fields_by_name['min']._options = None - _DEPRECATEDANALOGCONSTRAINT.fields_by_name['min']._serialized_options = b'\212\265\030\003\020\200 \222?\0028 ' + _DEPRECATEDANALOGCONSTRAINT.fields_by_name['min']._serialized_options = b'\222?\0028 \212\265\030\003\020\200 ' _DEPRECATEDANALOGCONSTRAINT.fields_by_name['max']._options = None - _DEPRECATEDANALOGCONSTRAINT.fields_by_name['max']._serialized_options = b'\212\265\030\003\020\200 \222?\0028 ' + _DEPRECATEDANALOGCONSTRAINT.fields_by_name['max']._serialized_options = b'\222?\0028 \212\265\030\003\020\200 ' _DEPRECATEDANALOGCONSTRAINT.fields_by_name['limiting']._options = None _DEPRECATEDANALOGCONSTRAINT.fields_by_name['limiting']._serialized_options = b'\212\265\030\002(\001' _DEPRECATEDANALOGCONSTRAINTS.fields_by_name['constraints']._options = None _DEPRECATEDANALOGCONSTRAINTS.fields_by_name['constraints']._serialized_options = b'\222?\002\020\010' _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['minOff']._options = None - _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['minOff']._serialized_options = b'\212\265\030\002\010\003\212\265\030\003\020\350\007\222?\0028 ' + _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['minOff']._serialized_options = b'\222?\0028 \212\265\030\005\010\003\020\350\007' _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['minOn']._options = None - _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['minOn']._serialized_options = b'\212\265\030\002\010\003\212\265\030\003\020\350\007\222?\0028 ' + _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['minOn']._serialized_options = b'\222?\0028 \212\265\030\005\010\003\020\350\007' _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['delayedOff']._options = None - _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['delayedOff']._serialized_options = b'\212\265\030\002\010\003\212\265\030\003\020\350\007\222?\0028 ' + _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['delayedOff']._serialized_options = b'\222?\0028 \212\265\030\005\010\003\020\350\007' _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['delayedOn']._options = None - _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['delayedOn']._serialized_options = b'\212\265\030\002\010\003\212\265\030\003\020\350\007\222?\0028 ' + _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['delayedOn']._serialized_options = b'\222?\0028 \212\265\030\005\010\003\020\350\007' _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['mutex']._options = None - _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['mutex']._serialized_options = b'\212\265\030\002\030\010\222?\0028\020' + _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['mutex']._serialized_options = b'\222?\0028\020\212\265\030\002\030\010' _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['limiting']._options = None - _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['limiting']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['limiting']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['remaining']._options = None - _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['remaining']._serialized_options = b'\212\265\030\002\010\003\212\265\030\003\020\350\007\212\265\030\002(\001\222?\0028 ' + _DEPRECATEDDIGITALCONSTRAINT.fields_by_name['remaining']._serialized_options = b'\222?\0028 \212\265\030\007\010\003\020\350\007(\001' _DEPRECATEDDIGITALCONSTRAINTS.fields_by_name['constraints']._options = None _DEPRECATEDDIGITALCONSTRAINTS.fields_by_name['constraints']._serialized_options = b'\222?\002\020\010' - _VALUECONSTRAINT._serialized_start=69 - _VALUECONSTRAINT._serialized_end=158 - _BALANCEDCONSTRAINT._serialized_start=161 - _BALANCEDCONSTRAINT._serialized_end=314 - _DURATIONCONSTRAINT._serialized_start=317 - _DURATIONCONSTRAINT._serialized_end=463 - _MUTEXEDCONSTRAINT._serialized_start=466 - _MUTEXEDCONSTRAINT._serialized_end=711 - _ANALOGCONSTRAINTS._serialized_start=714 - _ANALOGCONSTRAINTS._serialized_end=885 - _DIGITALCONSTRAINTS._serialized_start=888 - _DIGITALCONSTRAINTS._serialized_end=1184 - _DEPRECATEDANALOGCONSTRAINT._serialized_start=1187 - _DEPRECATEDANALOGCONSTRAINT._serialized_end=1371 - _DEPRECATEDANALOGCONSTRAINTS._serialized_start=1373 - _DEPRECATEDANALOGCONSTRAINTS._serialized_end=1476 - _DEPRECATEDDIGITALCONSTRAINT._serialized_start=1479 - _DEPRECATEDDIGITALCONSTRAINT._serialized_end=1842 - _DEPRECATEDDIGITALCONSTRAINTS._serialized_start=1844 - _DEPRECATEDDIGITALCONSTRAINTS._serialized_end=1949 + _globals['_VALUECONSTRAINT']._serialized_start=69 + _globals['_VALUECONSTRAINT']._serialized_end=158 + _globals['_BALANCEDCONSTRAINT']._serialized_start=161 + _globals['_BALANCEDCONSTRAINT']._serialized_end=310 + _globals['_DURATIONCONSTRAINT']._serialized_start=313 + _globals['_DURATIONCONSTRAINT']._serialized_end=447 + _globals['_MUTEXEDCONSTRAINT']._serialized_start=450 + _globals['_MUTEXEDCONSTRAINT']._serialized_end=683 + _globals['_ANALOGCONSTRAINTS']._serialized_start=686 + _globals['_ANALOGCONSTRAINTS']._serialized_end=857 + _globals['_DIGITALCONSTRAINTS']._serialized_start=860 + _globals['_DIGITALCONSTRAINTS']._serialized_end=1156 + _globals['_DEPRECATEDANALOGCONSTRAINT']._serialized_start=1159 + _globals['_DEPRECATEDANALOGCONSTRAINT']._serialized_end=1343 + _globals['_DEPRECATEDANALOGCONSTRAINTS']._serialized_start=1345 + _globals['_DEPRECATEDANALOGCONSTRAINTS']._serialized_end=1448 + _globals['_DEPRECATEDDIGITALCONSTRAINT']._serialized_start=1451 + _globals['_DEPRECATEDDIGITALCONSTRAINT']._serialized_end=1790 + _globals['_DEPRECATEDDIGITALCONSTRAINTS']._serialized_start=1792 + _globals['_DEPRECATEDDIGITALCONSTRAINTS']._serialized_end=1897 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/DS2408_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/DS2408_pb2.py index 052915f2..e7811fe2 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/DS2408_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/DS2408_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: DS2408.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -18,36 +16,11 @@ import IoArray_pb2 as IoArray__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0c\x44S2408.proto\x12\x0b\x62lox.DS2408\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\rIoArray.proto\"\x85\x02\n\x05\x42lock\x12\x17\n\x07\x61\x64\x64ress\x18\x01 \x01(\x06\x42\x06\x8a\xb5\x18\x02 \x01\x12\x19\n\tconnected\x18\x06 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\x12\x30\n\x0b\x63onnectMode\x18\t \x01(\x0e\x32\x1b.blox.DS2408.PinConnectMode\x12(\n\x0coneWireBusId\x18\n \x01(\rB\x12\x8a\xb5\x18\x03\x18\x82\x02\x92?\x02\x38\x10\x8a\xb5\x18\x02(\x01\x12\x36\n\x08\x63hannels\x18\x0b \x03(\x0b\x32\x17.blox.IoArray.IoChannelB\x0b\x92?\x02\x10\x08\x8a\xb5\x18\x02(\x01\x12\x19\n\x04pins\x18Z \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03:\x19\x8a\xb5\x18\x03\x18\xbd\x02\x8a\xb5\x18\x02H\n\x8a\xb5\x18\x02H\x0b\x8a\xb5\x18\x02H\t*\xfc\x01\n\tChannelId\x12\x14\n\x10\x44S2408_CHAN_NONE\x10\x00\x12\x11\n\rDS2408_CHAN_A\x10\x01\x12\x11\n\rDS2408_CHAN_B\x10\x02\x12\x11\n\rDS2408_CHAN_C\x10\x03\x12\x11\n\rDS2408_CHAN_D\x10\x04\x12\x11\n\rDS2408_CHAN_E\x10\x05\x12\x11\n\rDS2408_CHAN_F\x10\x06\x12\x11\n\rDS2408_CHAN_G\x10\x07\x12\x11\n\rDS2408_CHAN_H\x10\x08\x12\x15\n\x11\x44S2408_VALVE_NONE\x10\x00\x12\x12\n\x0e\x44S2408_VALVE_A\x10\x05\x12\x12\n\x0e\x44S2408_VALVE_B\x10\x01\x1a\x02\x10\x01*9\n\x0ePinConnectMode\x12\x11\n\rCONNECT_VALVE\x10\x00\x12\x14\n\x10\x43ONNECT_ACTUATOR\x10\x01\x62\x06proto3') - -_CHANNELID = DESCRIPTOR.enum_types_by_name['ChannelId'] -ChannelId = enum_type_wrapper.EnumTypeWrapper(_CHANNELID) -_PINCONNECTMODE = DESCRIPTOR.enum_types_by_name['PinConnectMode'] -PinConnectMode = enum_type_wrapper.EnumTypeWrapper(_PINCONNECTMODE) -DS2408_CHAN_NONE = 0 -DS2408_CHAN_A = 1 -DS2408_CHAN_B = 2 -DS2408_CHAN_C = 3 -DS2408_CHAN_D = 4 -DS2408_CHAN_E = 5 -DS2408_CHAN_F = 6 -DS2408_CHAN_G = 7 -DS2408_CHAN_H = 8 -DS2408_VALVE_NONE = 0 -DS2408_VALVE_A = 5 -DS2408_VALVE_B = 1 -CONNECT_VALVE = 0 -CONNECT_ACTUATOR = 1 - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'DS2408_pb2' - # @@protoc_insertion_point(class_scope:blox.DS2408.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0c\x44S2408.proto\x12\x0b\x62lox.DS2408\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\rIoArray.proto\"\xf4\x01\n\x05\x42lock\x12\x17\n\x07\x61\x64\x64ress\x18\x01 \x01(\x06\x42\x06\x8a\xb5\x18\x02 \x01\x12\x19\n\tconnected\x18\x06 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\x12\x30\n\x0b\x63onnectMode\x18\t \x01(\x0e\x32\x1b.blox.DS2408.PinConnectMode\x12$\n\x0coneWireBusId\x18\n \x01(\rB\x0e\x92?\x02\x38\x10\x8a\xb5\x18\x05\x18\x82\x02(\x01\x12\x36\n\x08\x63hannels\x18\x0b \x03(\x0b\x32\x17.blox.IoArray.IoChannelB\x0b\x92?\x02\x10\x08\x8a\xb5\x18\x02(\x01\x12\x19\n\x04pins\x18Z \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01:\x0c\x8a\xb5\x18\x08\x18\xbd\x02J\x03\n\x0b\t*\xfc\x01\n\tChannelId\x12\x14\n\x10\x44S2408_CHAN_NONE\x10\x00\x12\x11\n\rDS2408_CHAN_A\x10\x01\x12\x11\n\rDS2408_CHAN_B\x10\x02\x12\x11\n\rDS2408_CHAN_C\x10\x03\x12\x11\n\rDS2408_CHAN_D\x10\x04\x12\x11\n\rDS2408_CHAN_E\x10\x05\x12\x11\n\rDS2408_CHAN_F\x10\x06\x12\x11\n\rDS2408_CHAN_G\x10\x07\x12\x11\n\rDS2408_CHAN_H\x10\x08\x12\x15\n\x11\x44S2408_VALVE_NONE\x10\x00\x12\x12\n\x0e\x44S2408_VALVE_A\x10\x05\x12\x12\n\x0e\x44S2408_VALVE_B\x10\x01\x1a\x02\x10\x01*9\n\x0ePinConnectMode\x12\x11\n\rCONNECT_VALVE\x10\x00\x12\x14\n\x10\x43ONNECT_ACTUATOR\x10\x01\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'DS2408_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None @@ -58,17 +31,17 @@ _BLOCK.fields_by_name['connected']._options = None _BLOCK.fields_by_name['connected']._serialized_options = b'\212\265\030\002(\001' _BLOCK.fields_by_name['oneWireBusId']._options = None - _BLOCK.fields_by_name['oneWireBusId']._serialized_options = b'\212\265\030\003\030\202\002\222?\0028\020\212\265\030\002(\001' + _BLOCK.fields_by_name['oneWireBusId']._serialized_options = b'\222?\0028\020\212\265\030\005\030\202\002(\001' _BLOCK.fields_by_name['channels']._options = None _BLOCK.fields_by_name['channels']._serialized_options = b'\222?\002\020\010\212\265\030\002(\001' _BLOCK.fields_by_name['pins']._options = None - _BLOCK.fields_by_name['pins']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['pins']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\275\002\212\265\030\002H\n\212\265\030\002H\013\212\265\030\002H\t' - _CHANNELID._serialized_start=339 - _CHANNELID._serialized_end=591 - _PINCONNECTMODE._serialized_start=593 - _PINCONNECTMODE._serialized_end=650 - _BLOCK._serialized_start=75 - _BLOCK._serialized_end=336 + _BLOCK._serialized_options = b'\212\265\030\010\030\275\002J\003\n\013\t' + _globals['_CHANNELID']._serialized_start=322 + _globals['_CHANNELID']._serialized_end=574 + _globals['_PINCONNECTMODE']._serialized_start=576 + _globals['_PINCONNECTMODE']._serialized_end=633 + _globals['_BLOCK']._serialized_start=75 + _globals['_BLOCK']._serialized_end=319 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/DS2413_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/DS2413_pb2.py index 502d8e2c..c394737e 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/DS2413_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/DS2413_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: DS2413.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -18,23 +16,11 @@ import IoArray_pb2 as IoArray__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0c\x44S2413.proto\x12\x0b\x62lox.DS2413\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\rIoArray.proto\"\xd2\x01\n\x05\x42lock\x12\x17\n\x07\x61\x64\x64ress\x18\x01 \x01(\x06\x42\x06\x8a\xb5\x18\x02 \x01\x12\x19\n\tconnected\x18\x06 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\x12(\n\x0coneWireBusId\x18\x08 \x01(\rB\x12\x8a\xb5\x18\x03\x18\x82\x02\x92?\x02\x38\x10\x8a\xb5\x18\x02(\x01\x12;\n\x08\x63hannels\x18\t \x03(\x0b\x32\x17.blox.IoArray.IoChannelB\x10\x92?\x02\x10\x02\x92?\x02x\x01\x8a\xb5\x18\x02(\x01\x12\x19\n\x04pins\x18Z \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03:\x13\x8a\xb5\x18\x03\x18\xbb\x02\x8a\xb5\x18\x02H\n\x8a\xb5\x18\x02H\t*G\n\tChannelId\x12\x14\n\x10\x44S2413_CHAN_NONE\x10\x00\x12\x11\n\rDS2413_CHAN_A\x10\x01\x12\x11\n\rDS2413_CHAN_B\x10\x02\x62\x06proto3') - -_CHANNELID = DESCRIPTOR.enum_types_by_name['ChannelId'] -ChannelId = enum_type_wrapper.EnumTypeWrapper(_CHANNELID) -DS2413_CHAN_NONE = 0 -DS2413_CHAN_A = 1 -DS2413_CHAN_B = 2 - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'DS2413_pb2' - # @@protoc_insertion_point(class_scope:blox.DS2413.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0c\x44S2413.proto\x12\x0b\x62lox.DS2413\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\rIoArray.proto\"\xc3\x01\n\x05\x42lock\x12\x17\n\x07\x61\x64\x64ress\x18\x01 \x01(\x06\x42\x06\x8a\xb5\x18\x02 \x01\x12\x19\n\tconnected\x18\x06 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\x12$\n\x0coneWireBusId\x18\x08 \x01(\rB\x0e\x92?\x02\x38\x10\x8a\xb5\x18\x05\x18\x82\x02(\x01\x12\x38\n\x08\x63hannels\x18\t \x03(\x0b\x32\x17.blox.IoArray.IoChannelB\r\x92?\x04\x10\x02x\x01\x8a\xb5\x18\x02(\x01\x12\x19\n\x04pins\x18Z \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01:\x0b\x8a\xb5\x18\x07\x18\xbb\x02J\x02\n\t*G\n\tChannelId\x12\x14\n\x10\x44S2413_CHAN_NONE\x10\x00\x12\x11\n\rDS2413_CHAN_A\x10\x01\x12\x11\n\rDS2413_CHAN_B\x10\x02\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'DS2413_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None @@ -43,15 +29,15 @@ _BLOCK.fields_by_name['connected']._options = None _BLOCK.fields_by_name['connected']._serialized_options = b'\212\265\030\002(\001' _BLOCK.fields_by_name['oneWireBusId']._options = None - _BLOCK.fields_by_name['oneWireBusId']._serialized_options = b'\212\265\030\003\030\202\002\222?\0028\020\212\265\030\002(\001' + _BLOCK.fields_by_name['oneWireBusId']._serialized_options = b'\222?\0028\020\212\265\030\005\030\202\002(\001' _BLOCK.fields_by_name['channels']._options = None - _BLOCK.fields_by_name['channels']._serialized_options = b'\222?\002\020\002\222?\002x\001\212\265\030\002(\001' + _BLOCK.fields_by_name['channels']._serialized_options = b'\222?\004\020\002x\001\212\265\030\002(\001' _BLOCK.fields_by_name['pins']._options = None - _BLOCK.fields_by_name['pins']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['pins']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\273\002\212\265\030\002H\n\212\265\030\002H\t' - _CHANNELID._serialized_start=287 - _CHANNELID._serialized_end=358 - _BLOCK._serialized_start=75 - _BLOCK._serialized_end=285 + _BLOCK._serialized_options = b'\212\265\030\007\030\273\002J\002\n\t' + _globals['_CHANNELID']._serialized_start=272 + _globals['_CHANNELID']._serialized_end=343 + _globals['_BLOCK']._serialized_start=75 + _globals['_BLOCK']._serialized_end=270 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/DigitalActuator_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/DigitalActuator_pb2.py index 4ff94cd5..45889f00 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/DigitalActuator_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/DigitalActuator_pb2.py @@ -4,9 +4,8 @@ """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -19,39 +18,32 @@ import Claims_pb2 as Claims__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x44igitalActuator.proto\x12\x14\x62lox.DigitalActuator\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\x11\x43onstraints.proto\x1a\rIoArray.proto\x1a\x0c\x43laims.proto\"\xab\x05\n\x05\x42lock\x12\x1d\n\x08hwDevice\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\n\x92?\x02\x38\x10\x12\x16\n\x07\x63hannel\x18\x02 \x01(\rB\x05\x92?\x02\x38\x08\x12\x37\n\x0bstoredState\x18\x0b \x01(\x0e\x32\x1a.blox.IoArray.DigitalStateB\x06\x8a\xb5\x18\x02\x30\x01\x12>\n\x0c\x64\x65siredState\x18\x06 \x01(\x0e\x32\x1a.blox.IoArray.DigitalStateB\x0c\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02(\x01\x12\x37\n\x05state\x18\x03 \x01(\x0e\x32\x1a.blox.IoArray.DigitalStateB\x0c\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02(\x01\x12\x0e\n\x06invert\x18\x04 \x01(\x08\x12\x45\n\rconstrainedBy\x18\x05 \x01(\x0b\x32..blox.Constraints.DeprecatedDigitalConstraints\x12\x39\n\x0b\x63onstraints\x18\r \x01(\x0b\x32$.blox.Constraints.DigitalConstraints\x12H\n\x18transitionDurationPreset\x18\x07 \x01(\x0e\x32&.blox.IoArray.TransitionDurationPreset\x12\x30\n\x19transitionDurationSetting\x18\x08 \x01(\rB\r\x8a\xb5\x18\x02\x08\x03\x8a\xb5\x18\x03\x10\xe8\x07\x12\x34\n\x17transitionDurationValue\x18\t \x01(\rB\x13\x8a\xb5\x18\x02\x08\x03\x8a\xb5\x18\x03\x10\xe8\x07\x8a\xb5\x18\x02(\x01\x12%\n\tclaimedBy\x18\n \x01(\rB\x12\x8a\xb5\x18\x03\x18\xff\x01\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x10\x12-\n\x0bsettingMode\x18\x0c \x01(\x0e\x32\x18.blox.Claims.SettingMode:\x1f\x8a\xb5\x18\x03\x18\xbe\x02\x8a\xb5\x18\x02H\x06\x8a\xb5\x18\x02H\x15\x8a\xb5\x18\x02H\x10\x8a\xb5\x18\x02H\x11\x62\x06proto3') - - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'DigitalActuator_pb2' - # @@protoc_insertion_point(class_scope:blox.DigitalActuator.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x44igitalActuator.proto\x12\x14\x62lox.DigitalActuator\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\x11\x43onstraints.proto\x1a\rIoArray.proto\x1a\x0c\x43laims.proto\"\x81\x05\n\x05\x42lock\x12\x1d\n\x08hwDevice\x18\x01 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\n\x12\x16\n\x07\x63hannel\x18\x02 \x01(\rB\x05\x92?\x02\x38\x08\x12\x37\n\x0bstoredState\x18\x0b \x01(\x0e\x32\x1a.blox.IoArray.DigitalStateB\x06\x8a\xb5\x18\x02\x30\x01\x12:\n\x0c\x64\x65siredState\x18\x06 \x01(\x0e\x32\x1a.blox.IoArray.DigitalStateB\x08\x8a\xb5\x18\x04(\x01\x30\x01\x12\x33\n\x05state\x18\x03 \x01(\x0e\x32\x1a.blox.IoArray.DigitalStateB\x08\x8a\xb5\x18\x04(\x01\x30\x01\x12\x0e\n\x06invert\x18\x04 \x01(\x08\x12\x45\n\rconstrainedBy\x18\x05 \x01(\x0b\x32..blox.Constraints.DeprecatedDigitalConstraints\x12\x39\n\x0b\x63onstraints\x18\r \x01(\x0b\x32$.blox.Constraints.DigitalConstraints\x12H\n\x18transitionDurationPreset\x18\x07 \x01(\x0e\x32&.blox.IoArray.TransitionDurationPreset\x12,\n\x19transitionDurationSetting\x18\x08 \x01(\rB\t\x8a\xb5\x18\x05\x08\x03\x10\xe8\x07\x12,\n\x17transitionDurationValue\x18\t \x01(\rB\x0b\x8a\xb5\x18\x07\x08\x03\x10\xe8\x07(\x01\x12!\n\tclaimedBy\x18\n \x01(\rB\x0e\x92?\x02\x38\x10\x8a\xb5\x18\x05\x18\xff\x01(\x01\x12-\n\x0bsettingMode\x18\x0c \x01(\x0e\x32\x18.blox.Claims.SettingMode:\r\x8a\xb5\x18\t\x18\xbe\x02J\x04\x06\x15\x10\x11\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'DigitalActuator_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _BLOCK.fields_by_name['hwDevice']._options = None - _BLOCK.fields_by_name['hwDevice']._serialized_options = b'\212\265\030\002\030\n\222?\0028\020' + _BLOCK.fields_by_name['hwDevice']._serialized_options = b'\222?\0028\020\212\265\030\002\030\n' _BLOCK.fields_by_name['channel']._options = None _BLOCK.fields_by_name['channel']._serialized_options = b'\222?\0028\010' _BLOCK.fields_by_name['storedState']._options = None _BLOCK.fields_by_name['storedState']._serialized_options = b'\212\265\030\0020\001' _BLOCK.fields_by_name['desiredState']._options = None - _BLOCK.fields_by_name['desiredState']._serialized_options = b'\212\265\030\0020\001\212\265\030\002(\001' + _BLOCK.fields_by_name['desiredState']._serialized_options = b'\212\265\030\004(\0010\001' _BLOCK.fields_by_name['state']._options = None - _BLOCK.fields_by_name['state']._serialized_options = b'\212\265\030\0020\001\212\265\030\002(\001' + _BLOCK.fields_by_name['state']._serialized_options = b'\212\265\030\004(\0010\001' _BLOCK.fields_by_name['transitionDurationSetting']._options = None - _BLOCK.fields_by_name['transitionDurationSetting']._serialized_options = b'\212\265\030\002\010\003\212\265\030\003\020\350\007' + _BLOCK.fields_by_name['transitionDurationSetting']._serialized_options = b'\212\265\030\005\010\003\020\350\007' _BLOCK.fields_by_name['transitionDurationValue']._options = None - _BLOCK.fields_by_name['transitionDurationValue']._serialized_options = b'\212\265\030\002\010\003\212\265\030\003\020\350\007\212\265\030\002(\001' + _BLOCK.fields_by_name['transitionDurationValue']._serialized_options = b'\212\265\030\007\010\003\020\350\007(\001' _BLOCK.fields_by_name['claimedBy']._options = None - _BLOCK.fields_by_name['claimedBy']._serialized_options = b'\212\265\030\003\030\377\001\212\265\030\002(\001\222?\0028\020' + _BLOCK.fields_by_name['claimedBy']._serialized_options = b'\222?\0028\020\212\265\030\005\030\377\001(\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\276\002\212\265\030\002H\006\212\265\030\002H\025\212\265\030\002H\020\212\265\030\002H\021' - _BLOCK._serialized_start=126 - _BLOCK._serialized_end=809 + _BLOCK._serialized_options = b'\212\265\030\t\030\276\002J\004\006\025\020\021' + _globals['_BLOCK']._serialized_start=126 + _globals['_BLOCK']._serialized_end=767 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/DigitalInput_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/DigitalInput_pb2.py index fed6f361..edeb8726 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/DigitalInput_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/DigitalInput_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: DigitalInput.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -18,39 +16,28 @@ import IoArray_pb2 as IoArray__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x44igitalInput.proto\x12\x11\x62lox.DigitalInput\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\rIoArray.proto\"\xac\x02\n\x05\x42lock\x12\x1d\n\x08hwDevice\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\n\x92?\x02\x38\x10\x12\x16\n\x07\x63hannel\x18\x02 \x01(\rB\x05\x92?\x02\x38\x08\x12\x37\n\x05state\x18\x03 \x01(\x0e\x32\x1a.blox.IoArray.DigitalStateB\x0c\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02(\x01\x12\x0e\n\x06invert\x18\x04 \x01(\x08\x12\x33\n\x08\x62\x65havior\x18\x05 \x01(\x0e\x32!.blox.DigitalInput.ToggleBehavior\x12$\n\rminActiveTime\x18\x06 \x01(\rB\r\x8a\xb5\x18\x02\x08\x03\x8a\xb5\x18\x03\x10\xe8\x07\x12\x33\n\x07hwState\x18\x07 \x01(\x0e\x32\x1a.blox.IoArray.DigitalStateB\x06\x8a\xb5\x18\x02(\x01:\x13\x8a\xb5\x18\x03\x18\xca\x02\x8a\xb5\x18\x02H\x1b\x8a\xb5\x18\x02H\x11*-\n\x0eToggleBehavior\x12\n\n\x06\x44IRECT\x10\x00\x12\x0f\n\x0b\x41LTERNATING\x10\x01\x62\x06proto3') - -_TOGGLEBEHAVIOR = DESCRIPTOR.enum_types_by_name['ToggleBehavior'] -ToggleBehavior = enum_type_wrapper.EnumTypeWrapper(_TOGGLEBEHAVIOR) -DIRECT = 0 -ALTERNATING = 1 - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'DigitalInput_pb2' - # @@protoc_insertion_point(class_scope:blox.DigitalInput.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x44igitalInput.proto\x12\x11\x62lox.DigitalInput\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\rIoArray.proto\"\x9c\x02\n\x05\x42lock\x12\x1d\n\x08hwDevice\x18\x01 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\n\x12\x16\n\x07\x63hannel\x18\x02 \x01(\rB\x05\x92?\x02\x38\x08\x12\x33\n\x05state\x18\x03 \x01(\x0e\x32\x1a.blox.IoArray.DigitalStateB\x08\x8a\xb5\x18\x04(\x01\x30\x01\x12\x0e\n\x06invert\x18\x04 \x01(\x08\x12\x33\n\x08\x62\x65havior\x18\x05 \x01(\x0e\x32!.blox.DigitalInput.ToggleBehavior\x12 \n\rminActiveTime\x18\x06 \x01(\rB\t\x8a\xb5\x18\x05\x08\x03\x10\xe8\x07\x12\x33\n\x07hwState\x18\x07 \x01(\x0e\x32\x1a.blox.IoArray.DigitalStateB\x06\x8a\xb5\x18\x02(\x01:\x0b\x8a\xb5\x18\x07\x18\xca\x02J\x02\x1b\x11*-\n\x0eToggleBehavior\x12\n\n\x06\x44IRECT\x10\x00\x12\x0f\n\x0b\x41LTERNATING\x10\x01\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'DigitalInput_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _BLOCK.fields_by_name['hwDevice']._options = None - _BLOCK.fields_by_name['hwDevice']._serialized_options = b'\212\265\030\002\030\n\222?\0028\020' + _BLOCK.fields_by_name['hwDevice']._serialized_options = b'\222?\0028\020\212\265\030\002\030\n' _BLOCK.fields_by_name['channel']._options = None _BLOCK.fields_by_name['channel']._serialized_options = b'\222?\0028\010' _BLOCK.fields_by_name['state']._options = None - _BLOCK.fields_by_name['state']._serialized_options = b'\212\265\030\0020\001\212\265\030\002(\001' + _BLOCK.fields_by_name['state']._serialized_options = b'\212\265\030\004(\0010\001' _BLOCK.fields_by_name['minActiveTime']._options = None - _BLOCK.fields_by_name['minActiveTime']._serialized_options = b'\212\265\030\002\010\003\212\265\030\003\020\350\007' + _BLOCK.fields_by_name['minActiveTime']._serialized_options = b'\212\265\030\005\010\003\020\350\007' _BLOCK.fields_by_name['hwState']._options = None _BLOCK.fields_by_name['hwState']._serialized_options = b'\212\265\030\002(\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\312\002\212\265\030\002H\033\212\265\030\002H\021' - _TOGGLEBEHAVIOR._serialized_start=389 - _TOGGLEBEHAVIOR._serialized_end=434 - _BLOCK._serialized_start=87 - _BLOCK._serialized_end=387 + _BLOCK._serialized_options = b'\212\265\030\007\030\312\002J\002\033\021' + _globals['_TOGGLEBEHAVIOR']._serialized_start=373 + _globals['_TOGGLEBEHAVIOR']._serialized_end=418 + _globals['_BLOCK']._serialized_start=87 + _globals['_BLOCK']._serialized_end=371 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/DisplaySettings_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/DisplaySettings_pb2.py index d47ca288..a26e825f 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/DisplaySettings_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/DisplaySettings_pb2.py @@ -4,9 +4,8 @@ """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -16,57 +15,42 @@ import nanopb_pb2 as nanopb__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x44isplaySettings.proto\x12\x14\x62lox.DisplaySettings\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"\xf2\x01\n\x06Widget\x12\x12\n\x03pos\x18\x01 \x01(\rB\x05\x92?\x02\x38\x08\x12\x1f\n\x05\x63olor\x18\x02 \x01(\x0c\x42\x10\x92?\x02\x08\x03\x92?\x02x\x01\x8a\xb5\x18\x02\x38\x01\x12\x13\n\x04name\x18\x03 \x01(\tB\x05\x92?\x02\x08\x10\x12!\n\ntempSensor\x18\n \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x02\x92?\x02\x38\x10H\x00\x12)\n\x12setpointSensorPair\x18\x0b \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x04\x92?\x02\x38\x10H\x00\x12%\n\x0e\x61\x63tuatorAnalog\x18\x0c \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x05\x92?\x02\x38\x10H\x00\x12\x1b\n\x03pid\x18\x0e \x01(\rB\x0c\x8a\xb5\x18\x03\x18\xb0\x02\x92?\x02\x38\x10H\x00\x42\x0c\n\nWidgetType\"\xba\x01\n\x05\x42lock\x12\x34\n\x07widgets\x18\x01 \x03(\x0b\x32\x1c.blox.DisplaySettings.WidgetB\x05\x92?\x02\x10\x06\x12\x13\n\x04name\x18\x02 \x01(\tB\x05\x92?\x02\x08(\x12\x1f\n\nbrightness\x18Z \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03\x12\x1d\n\x08timeZone\x18[ \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03\x12\x1d\n\x08tempUnit\x18\\ \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03:\x07\x8a\xb5\x18\x03\x18\xba\x02\x62\x06proto3') - - - -_WIDGET = DESCRIPTOR.message_types_by_name['Widget'] -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Widget = _reflection.GeneratedProtocolMessageType('Widget', (_message.Message,), { - 'DESCRIPTOR' : _WIDGET, - '__module__' : 'DisplaySettings_pb2' - # @@protoc_insertion_point(class_scope:blox.DisplaySettings.Widget) - }) -_sym_db.RegisterMessage(Widget) - -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'DisplaySettings_pb2' - # @@protoc_insertion_point(class_scope:blox.DisplaySettings.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x44isplaySettings.proto\x12\x14\x62lox.DisplaySettings\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"\xef\x01\n\x06Widget\x12\x12\n\x03pos\x18\x01 \x01(\rB\x05\x92?\x02\x38\x08\x12\x1c\n\x05\x63olor\x18\x02 \x01(\x0c\x42\r\x92?\x04\x08\x03x\x01\x8a\xb5\x18\x02\x38\x01\x12\x13\n\x04name\x18\x03 \x01(\tB\x05\x92?\x02\x08\x10\x12!\n\ntempSensor\x18\n \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x02H\x00\x12)\n\x12setpointSensorPair\x18\x0b \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x04H\x00\x12%\n\x0e\x61\x63tuatorAnalog\x18\x0c \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x05H\x00\x12\x1b\n\x03pid\x18\x0e \x01(\rB\x0c\x92?\x02\x38\x10\x8a\xb5\x18\x03\x18\xb0\x02H\x00\x42\x0c\n\nWidgetType\"\xba\x01\n\x05\x42lock\x12\x34\n\x07widgets\x18\x01 \x03(\x0b\x32\x1c.blox.DisplaySettings.WidgetB\x05\x92?\x02\x10\x06\x12\x13\n\x04name\x18\x02 \x01(\tB\x05\x92?\x02\x08(\x12\x1f\n\nbrightness\x18Z \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01\x12\x1d\n\x08timeZone\x18[ \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01\x12\x1d\n\x08tempUnit\x18\\ \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01:\x07\x8a\xb5\x18\x03\x18\xba\x02\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'DisplaySettings_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _WIDGET.fields_by_name['pos']._options = None _WIDGET.fields_by_name['pos']._serialized_options = b'\222?\0028\010' _WIDGET.fields_by_name['color']._options = None - _WIDGET.fields_by_name['color']._serialized_options = b'\222?\002\010\003\222?\002x\001\212\265\030\0028\001' + _WIDGET.fields_by_name['color']._serialized_options = b'\222?\004\010\003x\001\212\265\030\0028\001' _WIDGET.fields_by_name['name']._options = None _WIDGET.fields_by_name['name']._serialized_options = b'\222?\002\010\020' _WIDGET.fields_by_name['tempSensor']._options = None - _WIDGET.fields_by_name['tempSensor']._serialized_options = b'\212\265\030\002\030\002\222?\0028\020' + _WIDGET.fields_by_name['tempSensor']._serialized_options = b'\222?\0028\020\212\265\030\002\030\002' _WIDGET.fields_by_name['setpointSensorPair']._options = None - _WIDGET.fields_by_name['setpointSensorPair']._serialized_options = b'\212\265\030\002\030\004\222?\0028\020' + _WIDGET.fields_by_name['setpointSensorPair']._serialized_options = b'\222?\0028\020\212\265\030\002\030\004' _WIDGET.fields_by_name['actuatorAnalog']._options = None - _WIDGET.fields_by_name['actuatorAnalog']._serialized_options = b'\212\265\030\002\030\005\222?\0028\020' + _WIDGET.fields_by_name['actuatorAnalog']._serialized_options = b'\222?\0028\020\212\265\030\002\030\005' _WIDGET.fields_by_name['pid']._options = None - _WIDGET.fields_by_name['pid']._serialized_options = b'\212\265\030\003\030\260\002\222?\0028\020' + _WIDGET.fields_by_name['pid']._serialized_options = b'\222?\0028\020\212\265\030\003\030\260\002' _BLOCK.fields_by_name['widgets']._options = None _BLOCK.fields_by_name['widgets']._serialized_options = b'\222?\002\020\006' _BLOCK.fields_by_name['name']._options = None _BLOCK.fields_by_name['name']._serialized_options = b'\222?\002\010(' _BLOCK.fields_by_name['brightness']._options = None - _BLOCK.fields_by_name['brightness']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['brightness']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK.fields_by_name['timeZone']._options = None - _BLOCK.fields_by_name['timeZone']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['timeZone']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK.fields_by_name['tempUnit']._options = None - _BLOCK.fields_by_name['tempUnit']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['tempUnit']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK._options = None _BLOCK._serialized_options = b'\212\265\030\003\030\272\002' - _WIDGET._serialized_start=78 - _WIDGET._serialized_end=320 - _BLOCK._serialized_start=323 - _BLOCK._serialized_end=509 + _globals['_WIDGET']._serialized_start=78 + _globals['_WIDGET']._serialized_end=317 + _globals['_BLOCK']._serialized_start=320 + _globals['_BLOCK']._serialized_end=506 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/EdgeCase_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/EdgeCase_pb2.py index 4c076db9..7bfcb63a 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/EdgeCase_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/EdgeCase_pb2.py @@ -4,9 +4,8 @@ """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -16,59 +15,20 @@ import nanopb_pb2 as nanopb__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0e\x45\x64geCase.proto\x12\rblox.EdgeCase\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"B\n\x08Settings\x12\x17\n\x07\x61\x64\x64ress\x18\x01 \x01(\x06\x42\x06\x8a\xb5\x18\x02 \x01\x12\x1d\n\x06offset\x18\x02 \x01(\x11\x42\r\x8a\xb5\x18\x02\x08\x06\x8a\xb5\x18\x03\x10\x80\x02\"@\n\x05State\x12\x1c\n\x05value\x18\x01 \x01(\x11\x42\r\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80\x02\x12\x19\n\tconnected\x18\x02 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\"(\n\nNestedLink\x12\x1a\n\nconnection\x18\x01 \x01(\rB\x06\x8a\xb5\x18\x02\x18\x02\"\xa6\x02\n\x05\x42lock\x12)\n\x08settings\x18\x01 \x01(\x0b\x32\x17.blox.EdgeCase.Settings\x12#\n\x05state\x18\x02 \x01(\x0b\x32\x14.blox.EdgeCase.State\x12\x14\n\x04link\x18\x03 \x01(\rB\x06\x8a\xb5\x18\x02\x18\x05\x12\x32\n\x0f\x61\x64\x64itionalLinks\x18\x04 \x03(\x0b\x32\x19.blox.EdgeCase.NestedLink\x12!\n\nlistValues\x18\x05 \x03(\x02\x42\r\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80\x02\x12\x1d\n\x06\x64\x65ltaV\x18\x06 \x01(\rB\r\x8a\xb5\x18\x02\x08\x07\x8a\xb5\x18\x03\x10\x80\x02\x12\x16\n\x06logged\x18\x07 \x01(\rB\x06\x8a\xb5\x18\x02\x30\x01\x12\x10\n\x08unLogged\x18\x08 \x01(\r\x12\x17\n\x02ip\x18\n \x01(\rB\x0b\x8a\xb5\x18\x02`\x01\x92?\x02\x38 \"#\n\x07SubCase\x12\x10\n\x08subvalue\x18\x01 \x01(\r:\x06\x8a\xb5\x18\x02X\x01\x62\x06proto3') - - - -_SETTINGS = DESCRIPTOR.message_types_by_name['Settings'] -_STATE = DESCRIPTOR.message_types_by_name['State'] -_NESTEDLINK = DESCRIPTOR.message_types_by_name['NestedLink'] -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -_SUBCASE = DESCRIPTOR.message_types_by_name['SubCase'] -Settings = _reflection.GeneratedProtocolMessageType('Settings', (_message.Message,), { - 'DESCRIPTOR' : _SETTINGS, - '__module__' : 'EdgeCase_pb2' - # @@protoc_insertion_point(class_scope:blox.EdgeCase.Settings) - }) -_sym_db.RegisterMessage(Settings) - -State = _reflection.GeneratedProtocolMessageType('State', (_message.Message,), { - 'DESCRIPTOR' : _STATE, - '__module__' : 'EdgeCase_pb2' - # @@protoc_insertion_point(class_scope:blox.EdgeCase.State) - }) -_sym_db.RegisterMessage(State) - -NestedLink = _reflection.GeneratedProtocolMessageType('NestedLink', (_message.Message,), { - 'DESCRIPTOR' : _NESTEDLINK, - '__module__' : 'EdgeCase_pb2' - # @@protoc_insertion_point(class_scope:blox.EdgeCase.NestedLink) - }) -_sym_db.RegisterMessage(NestedLink) - -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'EdgeCase_pb2' - # @@protoc_insertion_point(class_scope:blox.EdgeCase.Block) - }) -_sym_db.RegisterMessage(Block) - -SubCase = _reflection.GeneratedProtocolMessageType('SubCase', (_message.Message,), { - 'DESCRIPTOR' : _SUBCASE, - '__module__' : 'EdgeCase_pb2' - # @@protoc_insertion_point(class_scope:blox.EdgeCase.SubCase) - }) -_sym_db.RegisterMessage(SubCase) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0e\x45\x64geCase.proto\x12\rblox.EdgeCase\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\">\n\x08Settings\x12\x17\n\x07\x61\x64\x64ress\x18\x01 \x01(\x06\x42\x06\x8a\xb5\x18\x02 \x01\x12\x19\n\x06offset\x18\x02 \x01(\x11\x42\t\x8a\xb5\x18\x05\x08\x06\x10\x80\x02\"<\n\x05State\x12\x18\n\x05value\x18\x01 \x01(\x11\x42\t\x8a\xb5\x18\x05\x08\x01\x10\x80\x02\x12\x19\n\tconnected\x18\x02 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\"(\n\nNestedLink\x12\x1a\n\nconnection\x18\x01 \x01(\rB\x06\x8a\xb5\x18\x02\x18\x02\"\x9e\x02\n\x05\x42lock\x12)\n\x08settings\x18\x01 \x01(\x0b\x32\x17.blox.EdgeCase.Settings\x12#\n\x05state\x18\x02 \x01(\x0b\x32\x14.blox.EdgeCase.State\x12\x14\n\x04link\x18\x03 \x01(\rB\x06\x8a\xb5\x18\x02\x18\x05\x12\x32\n\x0f\x61\x64\x64itionalLinks\x18\x04 \x03(\x0b\x32\x19.blox.EdgeCase.NestedLink\x12\x1d\n\nlistValues\x18\x05 \x03(\x02\x42\t\x8a\xb5\x18\x05\x08\x01\x10\x80\x02\x12\x19\n\x06\x64\x65ltaV\x18\x06 \x01(\rB\t\x8a\xb5\x18\x05\x08\x07\x10\x80\x02\x12\x16\n\x06logged\x18\x07 \x01(\rB\x06\x8a\xb5\x18\x02\x30\x01\x12\x10\n\x08unLogged\x18\x08 \x01(\r\x12\x17\n\x02ip\x18\n \x01(\rB\x0b\x92?\x02\x38 \x8a\xb5\x18\x02`\x01\"#\n\x07SubCase\x12\x10\n\x08subvalue\x18\x01 \x01(\r:\x06\x8a\xb5\x18\x02X\x01\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'EdgeCase_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _SETTINGS.fields_by_name['address']._options = None _SETTINGS.fields_by_name['address']._serialized_options = b'\212\265\030\002 \001' _SETTINGS.fields_by_name['offset']._options = None - _SETTINGS.fields_by_name['offset']._serialized_options = b'\212\265\030\002\010\006\212\265\030\003\020\200\002' + _SETTINGS.fields_by_name['offset']._serialized_options = b'\212\265\030\005\010\006\020\200\002' _STATE.fields_by_name['value']._options = None - _STATE.fields_by_name['value']._serialized_options = b'\212\265\030\002\010\001\212\265\030\003\020\200\002' + _STATE.fields_by_name['value']._serialized_options = b'\212\265\030\005\010\001\020\200\002' _STATE.fields_by_name['connected']._options = None _STATE.fields_by_name['connected']._serialized_options = b'\212\265\030\002(\001' _NESTEDLINK.fields_by_name['connection']._options = None @@ -76,23 +36,23 @@ _BLOCK.fields_by_name['link']._options = None _BLOCK.fields_by_name['link']._serialized_options = b'\212\265\030\002\030\005' _BLOCK.fields_by_name['listValues']._options = None - _BLOCK.fields_by_name['listValues']._serialized_options = b'\212\265\030\002\010\001\212\265\030\003\020\200\002' + _BLOCK.fields_by_name['listValues']._serialized_options = b'\212\265\030\005\010\001\020\200\002' _BLOCK.fields_by_name['deltaV']._options = None - _BLOCK.fields_by_name['deltaV']._serialized_options = b'\212\265\030\002\010\007\212\265\030\003\020\200\002' + _BLOCK.fields_by_name['deltaV']._serialized_options = b'\212\265\030\005\010\007\020\200\002' _BLOCK.fields_by_name['logged']._options = None _BLOCK.fields_by_name['logged']._serialized_options = b'\212\265\030\0020\001' _BLOCK.fields_by_name['ip']._options = None - _BLOCK.fields_by_name['ip']._serialized_options = b'\212\265\030\002`\001\222?\0028 ' + _BLOCK.fields_by_name['ip']._serialized_options = b'\222?\0028 \212\265\030\002`\001' _SUBCASE._options = None _SUBCASE._serialized_options = b'\212\265\030\002X\001' - _SETTINGS._serialized_start=63 - _SETTINGS._serialized_end=129 - _STATE._serialized_start=131 - _STATE._serialized_end=195 - _NESTEDLINK._serialized_start=197 - _NESTEDLINK._serialized_end=237 - _BLOCK._serialized_start=240 - _BLOCK._serialized_end=534 - _SUBCASE._serialized_start=536 - _SUBCASE._serialized_end=571 + _globals['_SETTINGS']._serialized_start=63 + _globals['_SETTINGS']._serialized_end=125 + _globals['_STATE']._serialized_start=127 + _globals['_STATE']._serialized_end=187 + _globals['_NESTEDLINK']._serialized_start=189 + _globals['_NESTEDLINK']._serialized_end=229 + _globals['_BLOCK']._serialized_start=232 + _globals['_BLOCK']._serialized_end=518 + _globals['_SUBCASE']._serialized_start=520 + _globals['_SUBCASE']._serialized_end=555 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/FastPwm_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/FastPwm_pb2.py index 285a81c0..04c5186b 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/FastPwm_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/FastPwm_pb2.py @@ -4,9 +4,8 @@ """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -19,41 +18,34 @@ import Claims_pb2 as Claims__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rFastPwm.proto\x12\x0c\x62lox.FastPwm\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\x11\x43onstraints.proto\x1a\rIoArray.proto\x1a\x0c\x43laims.proto\"\xf4\x05\n\x05\x42lock\x12\x0f\n\x07\x65nabled\x18\x08 \x01(\x08\x12\x1d\n\x08hwDevice\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\n\x92?\x02\x38\x10\x12\x16\n\x07\x63hannel\x18\x02 \x01(\rB\x05\x92?\x02\x38\x08\x12)\n\rstoredSetting\x18\x0e \x01(\x11\x42\x12\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12\x30\n\x0e\x64\x65siredSetting\x18\x05 \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12)\n\x07setting\x18\x04 \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12\'\n\x05value\x18\x06 \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12\x0e\n\x06invert\x18\x0c \x01(\x08\x12-\n\tfrequency\x18\x03 \x01(\x0e\x32\x1a.blox.IoArray.PwmFrequency\x12\x44\n\rconstrainedBy\x18\x07 \x01(\x0b\x32-.blox.Constraints.DeprecatedAnalogConstraints\x12\x38\n\x0b\x63onstraints\x18\x10 \x01(\x0b\x32#.blox.Constraints.AnalogConstraints\x12H\n\x18transitionDurationPreset\x18\t \x01(\x0e\x32&.blox.IoArray.TransitionDurationPreset\x12\x30\n\x19transitionDurationSetting\x18\n \x01(\rB\r\x8a\xb5\x18\x02\x08\x03\x8a\xb5\x18\x03\x10\xe8\x07\x12\x34\n\x17transitionDurationValue\x18\x0b \x01(\rB\x13\x8a\xb5\x18\x02\x08\x03\x8a\xb5\x18\x03\x10\xe8\x07\x8a\xb5\x18\x02(\x01\x12%\n\tclaimedBy\x18\r \x01(\rB\x12\x8a\xb5\x18\x03\x18\xff\x01\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x10\x12-\n\x0bsettingMode\x18\x0f \x01(\x0e\x32\x18.blox.Claims.SettingMode:+\x8a\xb5\x18\x03\x18\xc9\x02\x8a\xb5\x18\x02H\x01\x8a\xb5\x18\x02H\x13\x8a\xb5\x18\x02H\x05\x8a\xb5\x18\x02H\x0f\x8a\xb5\x18\x02H\x10\x8a\xb5\x18\x02H\x11\x62\x06proto3') - - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'FastPwm_pb2' - # @@protoc_insertion_point(class_scope:blox.FastPwm.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rFastPwm.proto\x12\x0c\x62lox.FastPwm\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\x11\x43onstraints.proto\x1a\rIoArray.proto\x1a\x0c\x43laims.proto\"\xac\x05\n\x05\x42lock\x12\x0f\n\x07\x65nabled\x18\x08 \x01(\x08\x12\x1d\n\x08hwDevice\x18\x01 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\n\x12\x16\n\x07\x63hannel\x18\x02 \x01(\rB\x05\x92?\x02\x38\x08\x12%\n\rstoredSetting\x18\x0e \x01(\x11\x42\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x10\x80 0\x01\x12(\n\x0e\x64\x65siredSetting\x18\x05 \x01(\x11\x42\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x10\x80 (\x01\x30\x01\x12!\n\x07setting\x18\x04 \x01(\x11\x42\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x10\x80 (\x01\x30\x01\x12\x1f\n\x05value\x18\x06 \x01(\x11\x42\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x10\x80 (\x01\x30\x01\x12\x0e\n\x06invert\x18\x0c \x01(\x08\x12-\n\tfrequency\x18\x03 \x01(\x0e\x32\x1a.blox.IoArray.PwmFrequency\x12\x44\n\rconstrainedBy\x18\x07 \x01(\x0b\x32-.blox.Constraints.DeprecatedAnalogConstraints\x12\x38\n\x0b\x63onstraints\x18\x10 \x01(\x0b\x32#.blox.Constraints.AnalogConstraints\x12H\n\x18transitionDurationPreset\x18\t \x01(\x0e\x32&.blox.IoArray.TransitionDurationPreset\x12,\n\x19transitionDurationSetting\x18\n \x01(\rB\t\x8a\xb5\x18\x05\x08\x03\x10\xe8\x07\x12,\n\x17transitionDurationValue\x18\x0b \x01(\rB\x0b\x8a\xb5\x18\x07\x08\x03\x10\xe8\x07(\x01\x12!\n\tclaimedBy\x18\r \x01(\rB\x0e\x92?\x02\x38\x10\x8a\xb5\x18\x05\x18\xff\x01(\x01\x12-\n\x0bsettingMode\x18\x0f \x01(\x0e\x32\x18.blox.Claims.SettingMode:\x0f\x8a\xb5\x18\x0b\x18\xc9\x02J\x06\x01\x13\x05\x0f\x10\x11\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'FastPwm_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _BLOCK.fields_by_name['hwDevice']._options = None - _BLOCK.fields_by_name['hwDevice']._serialized_options = b'\212\265\030\002\030\n\222?\0028\020' + _BLOCK.fields_by_name['hwDevice']._serialized_options = b'\222?\0028\020\212\265\030\002\030\n' _BLOCK.fields_by_name['channel']._options = None _BLOCK.fields_by_name['channel']._serialized_options = b'\222?\0028\010' _BLOCK.fields_by_name['storedSetting']._options = None - _BLOCK.fields_by_name['storedSetting']._serialized_options = b'\212\265\030\0020\001\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['storedSetting']._serialized_options = b'\222?\0028 \212\265\030\005\020\200 0\001' _BLOCK.fields_by_name['desiredSetting']._options = None - _BLOCK.fields_by_name['desiredSetting']._serialized_options = b'\212\265\030\0020\001\212\265\030\002(\001\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['desiredSetting']._serialized_options = b'\222?\0028 \212\265\030\007\020\200 (\0010\001' _BLOCK.fields_by_name['setting']._options = None - _BLOCK.fields_by_name['setting']._serialized_options = b'\212\265\030\0020\001\212\265\030\002(\001\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['setting']._serialized_options = b'\222?\0028 \212\265\030\007\020\200 (\0010\001' _BLOCK.fields_by_name['value']._options = None - _BLOCK.fields_by_name['value']._serialized_options = b'\212\265\030\0020\001\212\265\030\002(\001\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['value']._serialized_options = b'\222?\0028 \212\265\030\007\020\200 (\0010\001' _BLOCK.fields_by_name['transitionDurationSetting']._options = None - _BLOCK.fields_by_name['transitionDurationSetting']._serialized_options = b'\212\265\030\002\010\003\212\265\030\003\020\350\007' + _BLOCK.fields_by_name['transitionDurationSetting']._serialized_options = b'\212\265\030\005\010\003\020\350\007' _BLOCK.fields_by_name['transitionDurationValue']._options = None - _BLOCK.fields_by_name['transitionDurationValue']._serialized_options = b'\212\265\030\002\010\003\212\265\030\003\020\350\007\212\265\030\002(\001' + _BLOCK.fields_by_name['transitionDurationValue']._serialized_options = b'\212\265\030\007\010\003\020\350\007(\001' _BLOCK.fields_by_name['claimedBy']._options = None - _BLOCK.fields_by_name['claimedBy']._serialized_options = b'\212\265\030\003\030\377\001\212\265\030\002(\001\222?\0028\020' + _BLOCK.fields_by_name['claimedBy']._serialized_options = b'\222?\0028\020\212\265\030\005\030\377\001(\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\311\002\212\265\030\002H\001\212\265\030\002H\023\212\265\030\002H\005\212\265\030\002H\017\212\265\030\002H\020\212\265\030\002H\021' - _BLOCK._serialized_start=110 - _BLOCK._serialized_end=866 + _BLOCK._serialized_options = b'\212\265\030\013\030\311\002J\006\001\023\005\017\020\021' + _globals['_BLOCK']._serialized_start=110 + _globals['_BLOCK']._serialized_end=794 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/IoArray_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/IoArray_pb2.py index 4747e591..39ae426e 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/IoArray_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/IoArray_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: IoArray.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -17,50 +15,11 @@ import brewblox_pb2 as brewblox__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rIoArray.proto\x12\x0c\x62lox.IoArray\x1a\x0cnanopb.proto\x1a\x0e\x62rewblox.proto\"n\n\tIoChannel\x12\x11\n\x02id\x18\x01 \x01(\rB\x05\x92?\x02\x38\x08\x12\'\n\x0c\x63\x61pabilities\x18\x02 \x01(\rB\x11\x8a\xb5\x18\x02P\x01\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x10\x12%\n\tclaimedBy\x18\x03 \x01(\rB\x12\x8a\xb5\x18\x03\x18\xff\x01\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x10*\x85\x01\n\x0c\x44igitalState\x12\x12\n\x0eSTATE_INACTIVE\x10\x00\x12\x10\n\x0cSTATE_ACTIVE\x10\x01\x12\x11\n\rSTATE_UNKNOWN\x10\x02\x12\x11\n\rSTATE_REVERSE\x10\x03\x12\x0c\n\x08Inactive\x10\x00\x12\n\n\x06\x41\x63tive\x10\x01\x12\x0b\n\x07Unknown\x10\x02\x1a\x02\x10\x01*^\n\x18TransitionDurationPreset\x12\n\n\x06ST_OFF\x10\x00\x12\x0b\n\x07ST_FAST\x10\x01\x12\r\n\tST_MEDIUM\x10\x02\x12\x0b\n\x07ST_SLOW\x10\x03\x12\r\n\tST_CUSTOM\x10\x04*\x85\x02\n\x13\x43hannelCapabilities\x12\x16\n\x12\x43HAN_SUPPORTS_NONE\x10\x00\x12 \n\x1c\x43HAN_SUPPORTS_DIGITAL_OUTPUT\x10\x01\x12\x1a\n\x16\x43HAN_SUPPORTS_PWM_80HZ\x10\x02\x12\x1b\n\x17\x43HAN_SUPPORTS_PWM_100HZ\x10\x04\x12\x1b\n\x17\x43HAN_SUPPORTS_PWM_200HZ\x10\x08\x12\x1c\n\x18\x43HAN_SUPPORTS_PWM_2000HZ\x10\x10\x12\x1f\n\x1b\x43HAN_SUPPORTS_BIDIRECTIONAL\x10 \x12\x1f\n\x1b\x43HAN_SUPPORTS_DIGITAL_INPUT\x10@*^\n\x0cPwmFrequency\x12\x11\n\rPWM_FREQ_80HZ\x10\x00\x12\x12\n\x0ePWM_FREQ_100HZ\x10\x01\x12\x12\n\x0ePWM_FREQ_200HZ\x10\x02\x12\x13\n\x0fPWM_FREQ_2000HZ\x10\x03\x62\x06proto3') - -_DIGITALSTATE = DESCRIPTOR.enum_types_by_name['DigitalState'] -DigitalState = enum_type_wrapper.EnumTypeWrapper(_DIGITALSTATE) -_TRANSITIONDURATIONPRESET = DESCRIPTOR.enum_types_by_name['TransitionDurationPreset'] -TransitionDurationPreset = enum_type_wrapper.EnumTypeWrapper(_TRANSITIONDURATIONPRESET) -_CHANNELCAPABILITIES = DESCRIPTOR.enum_types_by_name['ChannelCapabilities'] -ChannelCapabilities = enum_type_wrapper.EnumTypeWrapper(_CHANNELCAPABILITIES) -_PWMFREQUENCY = DESCRIPTOR.enum_types_by_name['PwmFrequency'] -PwmFrequency = enum_type_wrapper.EnumTypeWrapper(_PWMFREQUENCY) -STATE_INACTIVE = 0 -STATE_ACTIVE = 1 -STATE_UNKNOWN = 2 -STATE_REVERSE = 3 -Inactive = 0 -Active = 1 -Unknown = 2 -ST_OFF = 0 -ST_FAST = 1 -ST_MEDIUM = 2 -ST_SLOW = 3 -ST_CUSTOM = 4 -CHAN_SUPPORTS_NONE = 0 -CHAN_SUPPORTS_DIGITAL_OUTPUT = 1 -CHAN_SUPPORTS_PWM_80HZ = 2 -CHAN_SUPPORTS_PWM_100HZ = 4 -CHAN_SUPPORTS_PWM_200HZ = 8 -CHAN_SUPPORTS_PWM_2000HZ = 16 -CHAN_SUPPORTS_BIDIRECTIONAL = 32 -CHAN_SUPPORTS_DIGITAL_INPUT = 64 -PWM_FREQ_80HZ = 0 -PWM_FREQ_100HZ = 1 -PWM_FREQ_200HZ = 2 -PWM_FREQ_2000HZ = 3 - - -_IOCHANNEL = DESCRIPTOR.message_types_by_name['IoChannel'] -IoChannel = _reflection.GeneratedProtocolMessageType('IoChannel', (_message.Message,), { - 'DESCRIPTOR' : _IOCHANNEL, - '__module__' : 'IoArray_pb2' - # @@protoc_insertion_point(class_scope:blox.IoArray.IoChannel) - }) -_sym_db.RegisterMessage(IoChannel) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rIoArray.proto\x12\x0c\x62lox.IoArray\x1a\x0cnanopb.proto\x1a\x0e\x62rewblox.proto\"f\n\tIoChannel\x12\x11\n\x02id\x18\x01 \x01(\rB\x05\x92?\x02\x38\x08\x12#\n\x0c\x63\x61pabilities\x18\x02 \x01(\rB\r\x92?\x02\x38\x10\x8a\xb5\x18\x04(\x01P\x01\x12!\n\tclaimedBy\x18\x03 \x01(\rB\x0e\x92?\x02\x38\x10\x8a\xb5\x18\x05\x18\xff\x01(\x01*\x85\x01\n\x0c\x44igitalState\x12\x12\n\x0eSTATE_INACTIVE\x10\x00\x12\x10\n\x0cSTATE_ACTIVE\x10\x01\x12\x11\n\rSTATE_UNKNOWN\x10\x02\x12\x11\n\rSTATE_REVERSE\x10\x03\x12\x0c\n\x08Inactive\x10\x00\x12\n\n\x06\x41\x63tive\x10\x01\x12\x0b\n\x07Unknown\x10\x02\x1a\x02\x10\x01*^\n\x18TransitionDurationPreset\x12\n\n\x06ST_OFF\x10\x00\x12\x0b\n\x07ST_FAST\x10\x01\x12\r\n\tST_MEDIUM\x10\x02\x12\x0b\n\x07ST_SLOW\x10\x03\x12\r\n\tST_CUSTOM\x10\x04*\x85\x02\n\x13\x43hannelCapabilities\x12\x16\n\x12\x43HAN_SUPPORTS_NONE\x10\x00\x12 \n\x1c\x43HAN_SUPPORTS_DIGITAL_OUTPUT\x10\x01\x12\x1a\n\x16\x43HAN_SUPPORTS_PWM_80HZ\x10\x02\x12\x1b\n\x17\x43HAN_SUPPORTS_PWM_100HZ\x10\x04\x12\x1b\n\x17\x43HAN_SUPPORTS_PWM_200HZ\x10\x08\x12\x1c\n\x18\x43HAN_SUPPORTS_PWM_2000HZ\x10\x10\x12\x1f\n\x1b\x43HAN_SUPPORTS_BIDIRECTIONAL\x10 \x12\x1f\n\x1b\x43HAN_SUPPORTS_DIGITAL_INPUT\x10@*^\n\x0cPwmFrequency\x12\x11\n\rPWM_FREQ_80HZ\x10\x00\x12\x12\n\x0ePWM_FREQ_100HZ\x10\x01\x12\x12\n\x0ePWM_FREQ_200HZ\x10\x02\x12\x13\n\x0fPWM_FREQ_2000HZ\x10\x03\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'IoArray_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None @@ -69,17 +28,17 @@ _IOCHANNEL.fields_by_name['id']._options = None _IOCHANNEL.fields_by_name['id']._serialized_options = b'\222?\0028\010' _IOCHANNEL.fields_by_name['capabilities']._options = None - _IOCHANNEL.fields_by_name['capabilities']._serialized_options = b'\212\265\030\002P\001\212\265\030\002(\001\222?\0028\020' + _IOCHANNEL.fields_by_name['capabilities']._serialized_options = b'\222?\0028\020\212\265\030\004(\001P\001' _IOCHANNEL.fields_by_name['claimedBy']._options = None - _IOCHANNEL.fields_by_name['claimedBy']._serialized_options = b'\212\265\030\003\030\377\001\212\265\030\002(\001\222?\0028\020' - _DIGITALSTATE._serialized_start=174 - _DIGITALSTATE._serialized_end=307 - _TRANSITIONDURATIONPRESET._serialized_start=309 - _TRANSITIONDURATIONPRESET._serialized_end=403 - _CHANNELCAPABILITIES._serialized_start=406 - _CHANNELCAPABILITIES._serialized_end=667 - _PWMFREQUENCY._serialized_start=669 - _PWMFREQUENCY._serialized_end=763 - _IOCHANNEL._serialized_start=61 - _IOCHANNEL._serialized_end=171 + _IOCHANNEL.fields_by_name['claimedBy']._serialized_options = b'\222?\0028\020\212\265\030\005\030\377\001(\001' + _globals['_DIGITALSTATE']._serialized_start=166 + _globals['_DIGITALSTATE']._serialized_end=299 + _globals['_TRANSITIONDURATIONPRESET']._serialized_start=301 + _globals['_TRANSITIONDURATIONPRESET']._serialized_end=395 + _globals['_CHANNELCAPABILITIES']._serialized_start=398 + _globals['_CHANNELCAPABILITIES']._serialized_end=659 + _globals['_PWMFREQUENCY']._serialized_start=661 + _globals['_PWMFREQUENCY']._serialized_end=755 + _globals['_IOCHANNEL']._serialized_start=61 + _globals['_IOCHANNEL']._serialized_end=163 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/MockPins_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/MockPins_pb2.py index c75f8dce..d268df1b 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/MockPins_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/MockPins_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: MockPins.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -18,40 +16,22 @@ import IoArray_pb2 as IoArray__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0eMockPins.proto\x12\rblox.MockPins\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\rIoArray.proto\"n\n\x05\x42lock\x12;\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x17.blox.IoArray.IoChannelB\x10\x92?\x02\x10\x08\x92?\x02x\x01\x8a\xb5\x18\x02(\x01\x12\x19\n\x04pins\x18Z \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03:\r\x8a\xb5\x18\x03\x18\xc3\x02\x8a\xb5\x18\x02H\n*\xcb\x01\n\tChannelId\x12\x16\n\x12MOCKPINS_CHAN_NONE\x10\x00\x12\x13\n\x0fMOCKPINS_CHAN_A\x10\x01\x12\x13\n\x0fMOCKPINS_CHAN_B\x10\x02\x12\x13\n\x0fMOCKPINS_CHAN_C\x10\x03\x12\x13\n\x0fMOCKPINS_CHAN_D\x10\x04\x12\x13\n\x0fMOCKPINS_CHAN_E\x10\x05\x12\x13\n\x0fMOCKPINS_CHAN_F\x10\x06\x12\x13\n\x0fMOCKPINS_CHAN_G\x10\x07\x12\x13\n\x0fMOCKPINS_CHAN_H\x10\x08\x62\x06proto3') - -_CHANNELID = DESCRIPTOR.enum_types_by_name['ChannelId'] -ChannelId = enum_type_wrapper.EnumTypeWrapper(_CHANNELID) -MOCKPINS_CHAN_NONE = 0 -MOCKPINS_CHAN_A = 1 -MOCKPINS_CHAN_B = 2 -MOCKPINS_CHAN_C = 3 -MOCKPINS_CHAN_D = 4 -MOCKPINS_CHAN_E = 5 -MOCKPINS_CHAN_F = 6 -MOCKPINS_CHAN_G = 7 -MOCKPINS_CHAN_H = 8 - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'MockPins_pb2' - # @@protoc_insertion_point(class_scope:blox.MockPins.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0eMockPins.proto\x12\rblox.MockPins\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\rIoArray.proto\"h\n\x05\x42lock\x12\x38\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x17.blox.IoArray.IoChannelB\r\x92?\x04\x10\x08x\x01\x8a\xb5\x18\x02(\x01\x12\x19\n\x04pins\x18Z \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01:\n\x8a\xb5\x18\x06\x18\xc3\x02J\x01\n*\xcb\x01\n\tChannelId\x12\x16\n\x12MOCKPINS_CHAN_NONE\x10\x00\x12\x13\n\x0fMOCKPINS_CHAN_A\x10\x01\x12\x13\n\x0fMOCKPINS_CHAN_B\x10\x02\x12\x13\n\x0fMOCKPINS_CHAN_C\x10\x03\x12\x13\n\x0fMOCKPINS_CHAN_D\x10\x04\x12\x13\n\x0fMOCKPINS_CHAN_E\x10\x05\x12\x13\n\x0fMOCKPINS_CHAN_F\x10\x06\x12\x13\n\x0fMOCKPINS_CHAN_G\x10\x07\x12\x13\n\x0fMOCKPINS_CHAN_H\x10\x08\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'MockPins_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _BLOCK.fields_by_name['channels']._options = None - _BLOCK.fields_by_name['channels']._serialized_options = b'\222?\002\020\010\222?\002x\001\212\265\030\002(\001' + _BLOCK.fields_by_name['channels']._serialized_options = b'\222?\004\020\010x\001\212\265\030\002(\001' _BLOCK.fields_by_name['pins']._options = None - _BLOCK.fields_by_name['pins']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['pins']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\303\002\212\265\030\002H\n' - _CHANNELID._serialized_start=191 - _CHANNELID._serialized_end=394 - _BLOCK._serialized_start=78 - _BLOCK._serialized_end=188 + _BLOCK._serialized_options = b'\212\265\030\006\030\303\002J\001\n' + _globals['_CHANNELID']._serialized_start=185 + _globals['_CHANNELID']._serialized_end=388 + _globals['_BLOCK']._serialized_start=78 + _globals['_BLOCK']._serialized_end=182 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/MotorValve_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/MotorValve_pb2.py index 788b0753..2e548760 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/MotorValve_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/MotorValve_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: MotorValve.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -20,50 +18,34 @@ import Claims_pb2 as Claims__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10MotorValve.proto\x12\x0f\x62lox.MotorValve\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\x11\x43onstraints.proto\x1a\rIoArray.proto\x1a\x0c\x43laims.proto\"\xcb\x04\n\x05\x42lock\x12\x1d\n\x08hwDevice\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x0b\x92?\x02\x38\x10\x12\x16\n\x07\x63hannel\x18\x02 \x01(\rB\x05\x92?\x02\x38\x08\x12\x37\n\x0bstoredState\x18\t \x01(\x0e\x32\x1a.blox.IoArray.DigitalStateB\x06\x8a\xb5\x18\x02\x30\x01\x12>\n\x0c\x64\x65siredState\x18\x07 \x01(\x0e\x32\x1a.blox.IoArray.DigitalStateB\x0c\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02(\x01\x12\x37\n\x05state\x18\x03 \x01(\x0e\x32\x1a.blox.IoArray.DigitalStateB\x0c\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02(\x01\x12=\n\nvalveState\x18\x06 \x01(\x0e\x32\x1b.blox.MotorValve.ValveStateB\x0c\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02(\x01\x12\x45\n\rconstrainedBy\x18\x05 \x01(\x0b\x32..blox.Constraints.DeprecatedDigitalConstraints\x12\x39\n\x0b\x63onstraints\x18\x0b \x01(\x0b\x32$.blox.Constraints.DigitalConstraints\x12%\n\tclaimedBy\x18\x08 \x01(\rB\x12\x8a\xb5\x18\x03\x18\xff\x01\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x10\x12-\n\x0bsettingMode\x18\n \x01(\x0e\x32\x18.blox.Claims.SettingMode\x12!\n\x0cstartChannel\x18Z \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03:\x1f\x8a\xb5\x18\x03\x18\xc1\x02\x8a\xb5\x18\x02H\x06\x8a\xb5\x18\x02H\x15\x8a\xb5\x18\x02H\x10\x8a\xb5\x18\x02H\x11*\x96\x01\n\nValveState\x12\x11\n\rVALVE_UNKNOWN\x10\x00\x12\x0e\n\nVALVE_OPEN\x10\x01\x12\x10\n\x0cVALVE_CLOSED\x10\x02\x12\x11\n\rVALVE_OPENING\x10\x03\x12\x11\n\rVALVE_CLOSING\x10\x04\x12\x18\n\x14VALVE_HALF_OPEN_IDLE\x10\x05\x12\x13\n\x0fVALVE_INIT_IDLE\x10\x06\x62\x06proto3') - -_VALVESTATE = DESCRIPTOR.enum_types_by_name['ValveState'] -ValveState = enum_type_wrapper.EnumTypeWrapper(_VALVESTATE) -VALVE_UNKNOWN = 0 -VALVE_OPEN = 1 -VALVE_CLOSED = 2 -VALVE_OPENING = 3 -VALVE_CLOSING = 4 -VALVE_HALF_OPEN_IDLE = 5 -VALVE_INIT_IDLE = 6 - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'MotorValve_pb2' - # @@protoc_insertion_point(class_scope:blox.MotorValve.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10MotorValve.proto\x12\x0f\x62lox.MotorValve\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\x11\x43onstraints.proto\x1a\rIoArray.proto\x1a\x0c\x43laims.proto\"\xa9\x04\n\x05\x42lock\x12\x1d\n\x08hwDevice\x18\x01 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x0b\x12\x16\n\x07\x63hannel\x18\x02 \x01(\rB\x05\x92?\x02\x38\x08\x12\x37\n\x0bstoredState\x18\t \x01(\x0e\x32\x1a.blox.IoArray.DigitalStateB\x06\x8a\xb5\x18\x02\x30\x01\x12:\n\x0c\x64\x65siredState\x18\x07 \x01(\x0e\x32\x1a.blox.IoArray.DigitalStateB\x08\x8a\xb5\x18\x04(\x01\x30\x01\x12\x33\n\x05state\x18\x03 \x01(\x0e\x32\x1a.blox.IoArray.DigitalStateB\x08\x8a\xb5\x18\x04(\x01\x30\x01\x12\x39\n\nvalveState\x18\x06 \x01(\x0e\x32\x1b.blox.MotorValve.ValveStateB\x08\x8a\xb5\x18\x04(\x01\x30\x01\x12\x45\n\rconstrainedBy\x18\x05 \x01(\x0b\x32..blox.Constraints.DeprecatedDigitalConstraints\x12\x39\n\x0b\x63onstraints\x18\x0b \x01(\x0b\x32$.blox.Constraints.DigitalConstraints\x12!\n\tclaimedBy\x18\x08 \x01(\rB\x0e\x92?\x02\x38\x10\x8a\xb5\x18\x05\x18\xff\x01(\x01\x12-\n\x0bsettingMode\x18\n \x01(\x0e\x32\x18.blox.Claims.SettingMode\x12!\n\x0cstartChannel\x18Z \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01:\r\x8a\xb5\x18\t\x18\xc1\x02J\x04\x06\x15\x10\x11*\x96\x01\n\nValveState\x12\x11\n\rVALVE_UNKNOWN\x10\x00\x12\x0e\n\nVALVE_OPEN\x10\x01\x12\x10\n\x0cVALVE_CLOSED\x10\x02\x12\x11\n\rVALVE_OPENING\x10\x03\x12\x11\n\rVALVE_CLOSING\x10\x04\x12\x18\n\x14VALVE_HALF_OPEN_IDLE\x10\x05\x12\x13\n\x0fVALVE_INIT_IDLE\x10\x06\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'MotorValve_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _BLOCK.fields_by_name['hwDevice']._options = None - _BLOCK.fields_by_name['hwDevice']._serialized_options = b'\212\265\030\002\030\013\222?\0028\020' + _BLOCK.fields_by_name['hwDevice']._serialized_options = b'\222?\0028\020\212\265\030\002\030\013' _BLOCK.fields_by_name['channel']._options = None _BLOCK.fields_by_name['channel']._serialized_options = b'\222?\0028\010' _BLOCK.fields_by_name['storedState']._options = None _BLOCK.fields_by_name['storedState']._serialized_options = b'\212\265\030\0020\001' _BLOCK.fields_by_name['desiredState']._options = None - _BLOCK.fields_by_name['desiredState']._serialized_options = b'\212\265\030\0020\001\212\265\030\002(\001' + _BLOCK.fields_by_name['desiredState']._serialized_options = b'\212\265\030\004(\0010\001' _BLOCK.fields_by_name['state']._options = None - _BLOCK.fields_by_name['state']._serialized_options = b'\212\265\030\0020\001\212\265\030\002(\001' + _BLOCK.fields_by_name['state']._serialized_options = b'\212\265\030\004(\0010\001' _BLOCK.fields_by_name['valveState']._options = None - _BLOCK.fields_by_name['valveState']._serialized_options = b'\212\265\030\0020\001\212\265\030\002(\001' + _BLOCK.fields_by_name['valveState']._serialized_options = b'\212\265\030\004(\0010\001' _BLOCK.fields_by_name['claimedBy']._options = None - _BLOCK.fields_by_name['claimedBy']._serialized_options = b'\212\265\030\003\030\377\001\212\265\030\002(\001\222?\0028\020' + _BLOCK.fields_by_name['claimedBy']._serialized_options = b'\222?\0028\020\212\265\030\005\030\377\001(\001' _BLOCK.fields_by_name['startChannel']._options = None - _BLOCK.fields_by_name['startChannel']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['startChannel']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\301\002\212\265\030\002H\006\212\265\030\002H\025\212\265\030\002H\020\212\265\030\002H\021' - _VALVESTATE._serialized_start=706 - _VALVESTATE._serialized_end=856 - _BLOCK._serialized_start=116 - _BLOCK._serialized_end=703 + _BLOCK._serialized_options = b'\212\265\030\t\030\301\002J\004\006\025\020\021' + _globals['_VALVESTATE']._serialized_start=672 + _globals['_VALVESTATE']._serialized_end=822 + _globals['_BLOCK']._serialized_start=116 + _globals['_BLOCK']._serialized_end=669 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/Mutex_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/Mutex_pb2.py index e10e5e5e..a241807a 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/Mutex_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/Mutex_pb2.py @@ -4,9 +4,8 @@ """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -16,27 +15,20 @@ import nanopb_pb2 as nanopb__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0bMutex.proto\x12\nblox.Mutex\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"s\n\x05\x42lock\x12/\n\rwaitRemaining\x18\x02 \x01(\rB\x18\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x02\x08\x03\x8a\xb5\x18\x03\x10\xe8\x07\x92?\x02\x38 \x12*\n\x15\x64ifferentActuatorWait\x18Z \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03:\r\x8a\xb5\x18\x03\x18\xb6\x02\x8a\xb5\x18\x02H\x08\x62\x06proto3') - - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'Mutex_pb2' - # @@protoc_insertion_point(class_scope:blox.Mutex.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0bMutex.proto\x12\nblox.Mutex\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"h\n\x05\x42lock\x12\'\n\rwaitRemaining\x18\x02 \x01(\rB\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x08\x03\x10\xe8\x07(\x01\x12*\n\x15\x64ifferentActuatorWait\x18Z \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01:\n\x8a\xb5\x18\x06\x18\xb6\x02J\x01\x08\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Mutex_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _BLOCK.fields_by_name['waitRemaining']._options = None - _BLOCK.fields_by_name['waitRemaining']._serialized_options = b'\212\265\030\002(\001\212\265\030\002\010\003\212\265\030\003\020\350\007\222?\0028 ' + _BLOCK.fields_by_name['waitRemaining']._serialized_options = b'\222?\0028 \212\265\030\007\010\003\020\350\007(\001' _BLOCK.fields_by_name['differentActuatorWait']._options = None - _BLOCK.fields_by_name['differentActuatorWait']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['differentActuatorWait']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\266\002\212\265\030\002H\010' - _BLOCK._serialized_start=57 - _BLOCK._serialized_end=172 + _BLOCK._serialized_options = b'\212\265\030\006\030\266\002J\001\010' + _globals['_BLOCK']._serialized_start=57 + _globals['_BLOCK']._serialized_end=161 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/OneWireBus_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/OneWireBus_pb2.py index c5aa865b..fc7d48b1 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/OneWireBus_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/OneWireBus_pb2.py @@ -4,9 +4,8 @@ """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -16,26 +15,11 @@ import nanopb_pb2 as nanopb__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10OneWireBus.proto\x12\x0f\x62lox.OneWireBus\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"5\n\x07\x43ommand\x12\x15\n\x06opcode\x18\x01 \x01(\rB\x05\x92?\x02\x38\x08\x12\x13\n\x04\x64\x61ta\x18\x02 \x01(\rB\x05\x92?\x02\x38\x08\"Z\n\x05\x42lock\x12)\n\x07\x63ommand\x18\x01 \x01(\x0b\x32\x18.blox.OneWireBus.Command\x12\x1d\n\x07\x61\x64\x64ress\x18\x02 \x03(\x06\x42\x0c\x8a\xb5\x18\x02 \x01\x8a\xb5\x18\x02(\x01:\x07\x8a\xb5\x18\x03\x18\x82\x02\x62\x06proto3') - - - -_COMMAND = DESCRIPTOR.message_types_by_name['Command'] -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Command = _reflection.GeneratedProtocolMessageType('Command', (_message.Message,), { - 'DESCRIPTOR' : _COMMAND, - '__module__' : 'OneWireBus_pb2' - # @@protoc_insertion_point(class_scope:blox.OneWireBus.Command) - }) -_sym_db.RegisterMessage(Command) - -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'OneWireBus_pb2' - # @@protoc_insertion_point(class_scope:blox.OneWireBus.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10OneWireBus.proto\x12\x0f\x62lox.OneWireBus\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"5\n\x07\x43ommand\x12\x15\n\x06opcode\x18\x01 \x01(\rB\x05\x92?\x02\x38\x08\x12\x13\n\x04\x64\x61ta\x18\x02 \x01(\rB\x05\x92?\x02\x38\x08\"V\n\x05\x42lock\x12)\n\x07\x63ommand\x18\x01 \x01(\x0b\x32\x18.blox.OneWireBus.Command\x12\x19\n\x07\x61\x64\x64ress\x18\x02 \x03(\x06\x42\x08\x8a\xb5\x18\x04 \x01(\x01:\x07\x8a\xb5\x18\x03\x18\x82\x02\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'OneWireBus_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None @@ -44,11 +28,11 @@ _COMMAND.fields_by_name['data']._options = None _COMMAND.fields_by_name['data']._serialized_options = b'\222?\0028\010' _BLOCK.fields_by_name['address']._options = None - _BLOCK.fields_by_name['address']._serialized_options = b'\212\265\030\002 \001\212\265\030\002(\001' + _BLOCK.fields_by_name['address']._serialized_options = b'\212\265\030\004 \001(\001' _BLOCK._options = None _BLOCK._serialized_options = b'\212\265\030\003\030\202\002' - _COMMAND._serialized_start=67 - _COMMAND._serialized_end=120 - _BLOCK._serialized_start=122 - _BLOCK._serialized_end=212 + _globals['_COMMAND']._serialized_start=67 + _globals['_COMMAND']._serialized_end=120 + _globals['_BLOCK']._serialized_start=122 + _globals['_BLOCK']._serialized_end=208 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/OneWireGpioModule_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/OneWireGpioModule_pb2.py index fe386e66..4a68bdaa 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/OneWireGpioModule_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/OneWireGpioModule_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: OneWireGpioModule.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -17,75 +15,26 @@ import nanopb_pb2 as nanopb__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17OneWireGpioModule.proto\x12\x16\x62lox.OneWireGpioModule\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"\x9d\x02\n\x11GpioModuleChannel\x12\x11\n\x02id\x18\x01 \x01(\rB\x05\x92?\x02\x38\x08\x12:\n\ndeviceType\x18\x02 \x01(\x0e\x32&.blox.OneWireGpioModule.GpioDeviceType\x12\x1d\n\x08pinsMask\x18\x03 \x01(\rB\x0b\x8a\xb5\x18\x02P\x01\x92?\x02\x38\x08\x12\x14\n\x05width\x18\x04 \x01(\rB\x05\x92?\x02\x38\x08\x12\x13\n\x04name\x18\x05 \x01(\tB\x05\x92?\x02\x08 \x12\'\n\x0c\x63\x61pabilities\x18\x06 \x01(\rB\x11\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x02P\x01\x92?\x02\x38\x10\x12%\n\tclaimedBy\x18\x07 \x01(\rB\x12\x8a\xb5\x18\x03\x18\xff\x01\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x10\x12\x1f\n\nerrorFlags\x18\x08 \x01(\rB\x0b\x92?\x02\x38\x08\x8a\xb5\x18\x02P\x01\"\x86\x06\n\x05\x42lock\x12\x42\n\x08\x63hannels\x18\x01 \x03(\x0b\x32).blox.OneWireGpioModule.GpioModuleChannelB\x05\x92?\x02\x10\x08\x12\x1d\n\x0emodulePosition\x18\x02 \x01(\rB\x05\x92?\x02\x38\x08\x12!\n\x0cmoduleStatus\x18\x03 \x01(\rB\x0b\x92?\x02\x38\x08\x8a\xb5\x18\x02P\x01\x12(\n\rpullUpDesired\x18\x04 \x01(\rB\x11\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x08\x8a\xb5\x18\x02P\x01\x12\'\n\x0cpullUpStatus\x18\x05 \x01(\rB\x11\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x08\x8a\xb5\x18\x02P\x01\x12+\n\x10pullUpWhenActive\x18\x06 \x01(\rB\x11\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x08\x8a\xb5\x18\x02P\x01\x12-\n\x12pullUpWhenInactive\x18\x07 \x01(\rB\x11\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x08\x8a\xb5\x18\x02P\x01\x12*\n\x0fpullDownDesired\x18\x08 \x01(\rB\x11\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x08\x8a\xb5\x18\x02P\x01\x12)\n\x0epullDownStatus\x18\t \x01(\rB\x11\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x08\x8a\xb5\x18\x02P\x01\x12-\n\x12pullDownWhenActive\x18\n \x01(\rB\x11\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x08\x8a\xb5\x18\x02P\x01\x12/\n\x14pullDownWhenInactive\x18\x0b \x01(\rB\x11\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x08\x8a\xb5\x18\x02P\x01\x12&\n\x0boverCurrent\x18\x0c \x01(\rB\x11\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x08\x8a\xb5\x18\x02P\x01\x12#\n\x08openLoad\x18\r \x01(\rB\x11\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x08\x8a\xb5\x18\x02P\x01\x12\x18\n\x10useExternalPower\x18\x0e \x01(\x08\x12$\n\x0f\x66\x61ultsHistory5m\x18\x0f \x01(\rB\x0b\x92?\x02\x38\x08\x8a\xb5\x18\x02P\x01\x12%\n\x10\x66\x61ultsHistory60m\x18\x10 \x01(\rB\x0b\x92?\x02\x38\x08\x8a\xb5\x18\x02P\x01\x12&\n\x11moduleStatusClear\x18Z \x01(\rB\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03\x12 \n\x0b\x63learFaults\x18 \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03:\x13\x8a\xb5\x18\x03\x18\xc5\x02\x8a\xb5\x18\x02H\n\x8a\xb5\x18\x02H\x0c*\xad\x05\n\x0eGpioDeviceType\x12\x11\n\rGPIO_DEV_NONE\x10\x00\x12\x13\n\x0fGPIO_DEV_SSR_2P\x10\x01\x12\x13\n\x0fGPIO_DEV_SSR_1P\x10\x02\x12 \n\x1cGPIO_DEV_MECHANICAL_RELAY_2P\x10\x03\x12*\n&GPIO_DEV_MECHANICAL_RELAY_1P_HIGH_SIDE\x10\x04\x12)\n%GPIO_DEV_MECHANICAL_RELAY_1P_LOW_SIDE\x10\x05\x12\x14\n\x10GPIO_DEV_COIL_2P\x10\x06\x12\"\n\x1eGPIO_DEV_COIL_2P_BIDIRECTIONAL\x10\x07\x12\x1e\n\x1aGPIO_DEV_COIL_1P_HIGH_SIDE\x10\x08\x12\x1d\n\x19GPIO_DEV_COIL_1P_LOW_SIDE\x10\t\x12\x15\n\x11GPIO_DEV_MOTOR_2P\x10\n\x12#\n\x1fGPIO_DEV_MOTOR_2P_BIDIRECTIONAL\x10\x0b\x12\x1f\n\x1bGPIO_DEV_MOTOR_1P_HIGH_SIDE\x10\x0c\x12\x1e\n\x1aGPIO_DEV_MOTOR_1P_LOW_SIDE\x10\r\x12\"\n\x1eGPIO_DEV_DETECT_LOW_CURRENT_2P\x10\x0e\x12&\n\"GPIO_DEV_DETECT_LOW_CURRENT_1P_GND\x10\x0f\x12\x15\n\x11GPIO_DEV_POWER_1P\x10\x11\x12)\n%GPIO_DEV_DETECT_HIGH_CURRENT_1P_POWER\x10\x12\x12\x13\n\x0fGPIO_DEV_GND_1P\x10\x13\x12\'\n#GPIO_DEV_DETECT_HIGH_CURRENT_1P_GND\x10\x14\x12#\n\x1fGPIO_DEV_DETECT_HIGH_CURRENT_2P\x10\x15*\x8a\x02\n\x0eGpioErrorFlags\x12\x11\n\rGPIO_ERR_NONE\x10\x00\x12\x1b\n\x17GPIO_ERR_POWER_ON_RESET\x10\x01\x12\x18\n\x14GPIO_ERR_OVERVOLTAGE\x10\x02\x12\x19\n\x15GPIO_ERR_UNDERVOLTAGE\x10\x04\x12\x18\n\x14GPIO_ERR_OVERCURRENT\x10\x08\x12\x16\n\x12GPIO_ERR_OPEN_LOAD\x10\x10\x12$\n GPIO_ERR_OVERTEMPERATURE_WARNING\x10 \x12\"\n\x1eGPIO_ERR_OVERTEMPERATURE_ERROR\x10@\x12\x17\n\x12GPIO_ERR_SPI_ERROR\x10\x80\x01\x62\x06proto3') - -_GPIODEVICETYPE = DESCRIPTOR.enum_types_by_name['GpioDeviceType'] -GpioDeviceType = enum_type_wrapper.EnumTypeWrapper(_GPIODEVICETYPE) -_GPIOERRORFLAGS = DESCRIPTOR.enum_types_by_name['GpioErrorFlags'] -GpioErrorFlags = enum_type_wrapper.EnumTypeWrapper(_GPIOERRORFLAGS) -GPIO_DEV_NONE = 0 -GPIO_DEV_SSR_2P = 1 -GPIO_DEV_SSR_1P = 2 -GPIO_DEV_MECHANICAL_RELAY_2P = 3 -GPIO_DEV_MECHANICAL_RELAY_1P_HIGH_SIDE = 4 -GPIO_DEV_MECHANICAL_RELAY_1P_LOW_SIDE = 5 -GPIO_DEV_COIL_2P = 6 -GPIO_DEV_COIL_2P_BIDIRECTIONAL = 7 -GPIO_DEV_COIL_1P_HIGH_SIDE = 8 -GPIO_DEV_COIL_1P_LOW_SIDE = 9 -GPIO_DEV_MOTOR_2P = 10 -GPIO_DEV_MOTOR_2P_BIDIRECTIONAL = 11 -GPIO_DEV_MOTOR_1P_HIGH_SIDE = 12 -GPIO_DEV_MOTOR_1P_LOW_SIDE = 13 -GPIO_DEV_DETECT_LOW_CURRENT_2P = 14 -GPIO_DEV_DETECT_LOW_CURRENT_1P_GND = 15 -GPIO_DEV_POWER_1P = 17 -GPIO_DEV_DETECT_HIGH_CURRENT_1P_POWER = 18 -GPIO_DEV_GND_1P = 19 -GPIO_DEV_DETECT_HIGH_CURRENT_1P_GND = 20 -GPIO_DEV_DETECT_HIGH_CURRENT_2P = 21 -GPIO_ERR_NONE = 0 -GPIO_ERR_POWER_ON_RESET = 1 -GPIO_ERR_OVERVOLTAGE = 2 -GPIO_ERR_UNDERVOLTAGE = 4 -GPIO_ERR_OVERCURRENT = 8 -GPIO_ERR_OPEN_LOAD = 16 -GPIO_ERR_OVERTEMPERATURE_WARNING = 32 -GPIO_ERR_OVERTEMPERATURE_ERROR = 64 -GPIO_ERR_SPI_ERROR = 128 - - -_GPIOMODULECHANNEL = DESCRIPTOR.message_types_by_name['GpioModuleChannel'] -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -GpioModuleChannel = _reflection.GeneratedProtocolMessageType('GpioModuleChannel', (_message.Message,), { - 'DESCRIPTOR' : _GPIOMODULECHANNEL, - '__module__' : 'OneWireGpioModule_pb2' - # @@protoc_insertion_point(class_scope:blox.OneWireGpioModule.GpioModuleChannel) - }) -_sym_db.RegisterMessage(GpioModuleChannel) - -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'OneWireGpioModule_pb2' - # @@protoc_insertion_point(class_scope:blox.OneWireGpioModule.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17OneWireGpioModule.proto\x12\x16\x62lox.OneWireGpioModule\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"\x95\x02\n\x11GpioModuleChannel\x12\x11\n\x02id\x18\x01 \x01(\rB\x05\x92?\x02\x38\x08\x12:\n\ndeviceType\x18\x02 \x01(\x0e\x32&.blox.OneWireGpioModule.GpioDeviceType\x12\x1d\n\x08pinsMask\x18\x03 \x01(\rB\x0b\x92?\x02\x38\x08\x8a\xb5\x18\x02P\x01\x12\x14\n\x05width\x18\x04 \x01(\rB\x05\x92?\x02\x38\x08\x12\x13\n\x04name\x18\x05 \x01(\tB\x05\x92?\x02\x08 \x12#\n\x0c\x63\x61pabilities\x18\x06 \x01(\rB\r\x92?\x02\x38\x10\x8a\xb5\x18\x04(\x01P\x01\x12!\n\tclaimedBy\x18\x07 \x01(\rB\x0e\x92?\x02\x38\x10\x8a\xb5\x18\x05\x18\xff\x01(\x01\x12\x1f\n\nerrorFlags\x18\x08 \x01(\rB\x0b\x92?\x02\x38\x08\x8a\xb5\x18\x02P\x01\"\xd6\x05\n\x05\x42lock\x12\x42\n\x08\x63hannels\x18\x01 \x03(\x0b\x32).blox.OneWireGpioModule.GpioModuleChannelB\x05\x92?\x02\x10\x08\x12\x1d\n\x0emodulePosition\x18\x02 \x01(\rB\x05\x92?\x02\x38\x08\x12!\n\x0cmoduleStatus\x18\x03 \x01(\rB\x0b\x92?\x02\x38\x08\x8a\xb5\x18\x02P\x01\x12$\n\rpullUpDesired\x18\x04 \x01(\rB\r\x92?\x02\x38\x08\x8a\xb5\x18\x04(\x01P\x01\x12#\n\x0cpullUpStatus\x18\x05 \x01(\rB\r\x92?\x02\x38\x08\x8a\xb5\x18\x04(\x01P\x01\x12\'\n\x10pullUpWhenActive\x18\x06 \x01(\rB\r\x92?\x02\x38\x08\x8a\xb5\x18\x04(\x01P\x01\x12)\n\x12pullUpWhenInactive\x18\x07 \x01(\rB\r\x92?\x02\x38\x08\x8a\xb5\x18\x04(\x01P\x01\x12&\n\x0fpullDownDesired\x18\x08 \x01(\rB\r\x92?\x02\x38\x08\x8a\xb5\x18\x04(\x01P\x01\x12%\n\x0epullDownStatus\x18\t \x01(\rB\r\x92?\x02\x38\x08\x8a\xb5\x18\x04(\x01P\x01\x12)\n\x12pullDownWhenActive\x18\n \x01(\rB\r\x92?\x02\x38\x08\x8a\xb5\x18\x04(\x01P\x01\x12+\n\x14pullDownWhenInactive\x18\x0b \x01(\rB\r\x92?\x02\x38\x08\x8a\xb5\x18\x04(\x01P\x01\x12\"\n\x0boverCurrent\x18\x0c \x01(\rB\r\x92?\x02\x38\x08\x8a\xb5\x18\x04(\x01P\x01\x12\x1f\n\x08openLoad\x18\r \x01(\rB\r\x92?\x02\x38\x08\x8a\xb5\x18\x04(\x01P\x01\x12\x18\n\x10useExternalPower\x18\x0e \x01(\x08\x12$\n\x0f\x66\x61ultsHistory5m\x18\x0f \x01(\rB\x0b\x92?\x02\x38\x08\x8a\xb5\x18\x02P\x01\x12%\n\x10\x66\x61ultsHistory60m\x18\x10 \x01(\rB\x0b\x92?\x02\x38\x08\x8a\xb5\x18\x02P\x01\x12&\n\x11moduleStatusClear\x18Z \x01(\rB\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01\x12 \n\x0b\x63learFaults\x18 \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01:\x0b\x8a\xb5\x18\x07\x18\xc5\x02J\x02\n\x0c*\xad\x05\n\x0eGpioDeviceType\x12\x11\n\rGPIO_DEV_NONE\x10\x00\x12\x13\n\x0fGPIO_DEV_SSR_2P\x10\x01\x12\x13\n\x0fGPIO_DEV_SSR_1P\x10\x02\x12 \n\x1cGPIO_DEV_MECHANICAL_RELAY_2P\x10\x03\x12*\n&GPIO_DEV_MECHANICAL_RELAY_1P_HIGH_SIDE\x10\x04\x12)\n%GPIO_DEV_MECHANICAL_RELAY_1P_LOW_SIDE\x10\x05\x12\x14\n\x10GPIO_DEV_COIL_2P\x10\x06\x12\"\n\x1eGPIO_DEV_COIL_2P_BIDIRECTIONAL\x10\x07\x12\x1e\n\x1aGPIO_DEV_COIL_1P_HIGH_SIDE\x10\x08\x12\x1d\n\x19GPIO_DEV_COIL_1P_LOW_SIDE\x10\t\x12\x15\n\x11GPIO_DEV_MOTOR_2P\x10\n\x12#\n\x1fGPIO_DEV_MOTOR_2P_BIDIRECTIONAL\x10\x0b\x12\x1f\n\x1bGPIO_DEV_MOTOR_1P_HIGH_SIDE\x10\x0c\x12\x1e\n\x1aGPIO_DEV_MOTOR_1P_LOW_SIDE\x10\r\x12\"\n\x1eGPIO_DEV_DETECT_LOW_CURRENT_2P\x10\x0e\x12&\n\"GPIO_DEV_DETECT_LOW_CURRENT_1P_GND\x10\x0f\x12\x15\n\x11GPIO_DEV_POWER_1P\x10\x11\x12)\n%GPIO_DEV_DETECT_HIGH_CURRENT_1P_POWER\x10\x12\x12\x13\n\x0fGPIO_DEV_GND_1P\x10\x13\x12\'\n#GPIO_DEV_DETECT_HIGH_CURRENT_1P_GND\x10\x14\x12#\n\x1fGPIO_DEV_DETECT_HIGH_CURRENT_2P\x10\x15*\x8a\x02\n\x0eGpioErrorFlags\x12\x11\n\rGPIO_ERR_NONE\x10\x00\x12\x1b\n\x17GPIO_ERR_POWER_ON_RESET\x10\x01\x12\x18\n\x14GPIO_ERR_OVERVOLTAGE\x10\x02\x12\x19\n\x15GPIO_ERR_UNDERVOLTAGE\x10\x04\x12\x18\n\x14GPIO_ERR_OVERCURRENT\x10\x08\x12\x16\n\x12GPIO_ERR_OPEN_LOAD\x10\x10\x12$\n GPIO_ERR_OVERTEMPERATURE_WARNING\x10 \x12\"\n\x1eGPIO_ERR_OVERTEMPERATURE_ERROR\x10@\x12\x17\n\x12GPIO_ERR_SPI_ERROR\x10\x80\x01\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'OneWireGpioModule_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _GPIOMODULECHANNEL.fields_by_name['id']._options = None _GPIOMODULECHANNEL.fields_by_name['id']._serialized_options = b'\222?\0028\010' _GPIOMODULECHANNEL.fields_by_name['pinsMask']._options = None - _GPIOMODULECHANNEL.fields_by_name['pinsMask']._serialized_options = b'\212\265\030\002P\001\222?\0028\010' + _GPIOMODULECHANNEL.fields_by_name['pinsMask']._serialized_options = b'\222?\0028\010\212\265\030\002P\001' _GPIOMODULECHANNEL.fields_by_name['width']._options = None _GPIOMODULECHANNEL.fields_by_name['width']._serialized_options = b'\222?\0028\010' _GPIOMODULECHANNEL.fields_by_name['name']._options = None _GPIOMODULECHANNEL.fields_by_name['name']._serialized_options = b'\222?\002\010 ' _GPIOMODULECHANNEL.fields_by_name['capabilities']._options = None - _GPIOMODULECHANNEL.fields_by_name['capabilities']._serialized_options = b'\212\265\030\002(\001\212\265\030\002P\001\222?\0028\020' + _GPIOMODULECHANNEL.fields_by_name['capabilities']._serialized_options = b'\222?\0028\020\212\265\030\004(\001P\001' _GPIOMODULECHANNEL.fields_by_name['claimedBy']._options = None - _GPIOMODULECHANNEL.fields_by_name['claimedBy']._serialized_options = b'\212\265\030\003\030\377\001\212\265\030\002(\001\222?\0028\020' + _GPIOMODULECHANNEL.fields_by_name['claimedBy']._serialized_options = b'\222?\0028\020\212\265\030\005\030\377\001(\001' _GPIOMODULECHANNEL.fields_by_name['errorFlags']._options = None _GPIOMODULECHANNEL.fields_by_name['errorFlags']._serialized_options = b'\222?\0028\010\212\265\030\002P\001' _BLOCK.fields_by_name['channels']._options = None @@ -95,41 +44,41 @@ _BLOCK.fields_by_name['moduleStatus']._options = None _BLOCK.fields_by_name['moduleStatus']._serialized_options = b'\222?\0028\010\212\265\030\002P\001' _BLOCK.fields_by_name['pullUpDesired']._options = None - _BLOCK.fields_by_name['pullUpDesired']._serialized_options = b'\212\265\030\002(\001\222?\0028\010\212\265\030\002P\001' + _BLOCK.fields_by_name['pullUpDesired']._serialized_options = b'\222?\0028\010\212\265\030\004(\001P\001' _BLOCK.fields_by_name['pullUpStatus']._options = None - _BLOCK.fields_by_name['pullUpStatus']._serialized_options = b'\212\265\030\002(\001\222?\0028\010\212\265\030\002P\001' + _BLOCK.fields_by_name['pullUpStatus']._serialized_options = b'\222?\0028\010\212\265\030\004(\001P\001' _BLOCK.fields_by_name['pullUpWhenActive']._options = None - _BLOCK.fields_by_name['pullUpWhenActive']._serialized_options = b'\212\265\030\002(\001\222?\0028\010\212\265\030\002P\001' + _BLOCK.fields_by_name['pullUpWhenActive']._serialized_options = b'\222?\0028\010\212\265\030\004(\001P\001' _BLOCK.fields_by_name['pullUpWhenInactive']._options = None - _BLOCK.fields_by_name['pullUpWhenInactive']._serialized_options = b'\212\265\030\002(\001\222?\0028\010\212\265\030\002P\001' + _BLOCK.fields_by_name['pullUpWhenInactive']._serialized_options = b'\222?\0028\010\212\265\030\004(\001P\001' _BLOCK.fields_by_name['pullDownDesired']._options = None - _BLOCK.fields_by_name['pullDownDesired']._serialized_options = b'\212\265\030\002(\001\222?\0028\010\212\265\030\002P\001' + _BLOCK.fields_by_name['pullDownDesired']._serialized_options = b'\222?\0028\010\212\265\030\004(\001P\001' _BLOCK.fields_by_name['pullDownStatus']._options = None - _BLOCK.fields_by_name['pullDownStatus']._serialized_options = b'\212\265\030\002(\001\222?\0028\010\212\265\030\002P\001' + _BLOCK.fields_by_name['pullDownStatus']._serialized_options = b'\222?\0028\010\212\265\030\004(\001P\001' _BLOCK.fields_by_name['pullDownWhenActive']._options = None - _BLOCK.fields_by_name['pullDownWhenActive']._serialized_options = b'\212\265\030\002(\001\222?\0028\010\212\265\030\002P\001' + _BLOCK.fields_by_name['pullDownWhenActive']._serialized_options = b'\222?\0028\010\212\265\030\004(\001P\001' _BLOCK.fields_by_name['pullDownWhenInactive']._options = None - _BLOCK.fields_by_name['pullDownWhenInactive']._serialized_options = b'\212\265\030\002(\001\222?\0028\010\212\265\030\002P\001' + _BLOCK.fields_by_name['pullDownWhenInactive']._serialized_options = b'\222?\0028\010\212\265\030\004(\001P\001' _BLOCK.fields_by_name['overCurrent']._options = None - _BLOCK.fields_by_name['overCurrent']._serialized_options = b'\212\265\030\002(\001\222?\0028\010\212\265\030\002P\001' + _BLOCK.fields_by_name['overCurrent']._serialized_options = b'\222?\0028\010\212\265\030\004(\001P\001' _BLOCK.fields_by_name['openLoad']._options = None - _BLOCK.fields_by_name['openLoad']._serialized_options = b'\212\265\030\002(\001\222?\0028\010\212\265\030\002P\001' + _BLOCK.fields_by_name['openLoad']._serialized_options = b'\222?\0028\010\212\265\030\004(\001P\001' _BLOCK.fields_by_name['faultsHistory5m']._options = None _BLOCK.fields_by_name['faultsHistory5m']._serialized_options = b'\222?\0028\010\212\265\030\002P\001' _BLOCK.fields_by_name['faultsHistory60m']._options = None _BLOCK.fields_by_name['faultsHistory60m']._serialized_options = b'\222?\0028\010\212\265\030\002P\001' _BLOCK.fields_by_name['moduleStatusClear']._options = None - _BLOCK.fields_by_name['moduleStatusClear']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['moduleStatusClear']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK.fields_by_name['clearFaults']._options = None - _BLOCK.fields_by_name['clearFaults']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['clearFaults']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\305\002\212\265\030\002H\n\212\265\030\002H\014' - _GPIODEVICETYPE._serialized_start=1147 - _GPIODEVICETYPE._serialized_end=1832 - _GPIOERRORFLAGS._serialized_start=1835 - _GPIOERRORFLAGS._serialized_end=2101 - _GPIOMODULECHANNEL._serialized_start=82 - _GPIOMODULECHANNEL._serialized_end=367 - _BLOCK._serialized_start=370 - _BLOCK._serialized_end=1144 + _BLOCK._serialized_options = b'\212\265\030\007\030\305\002J\002\n\014' + _globals['_GPIODEVICETYPE']._serialized_start=1091 + _globals['_GPIODEVICETYPE']._serialized_end=1776 + _globals['_GPIOERRORFLAGS']._serialized_start=1779 + _globals['_GPIOERRORFLAGS']._serialized_end=2045 + _globals['_GPIOMODULECHANNEL']._serialized_start=82 + _globals['_GPIOMODULECHANNEL']._serialized_end=359 + _globals['_BLOCK']._serialized_start=362 + _globals['_BLOCK']._serialized_end=1088 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/Pid_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/Pid_pb2.py index 381b2aed..d3a0465a 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/Pid_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/Pid_pb2.py @@ -4,9 +4,8 @@ """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -17,69 +16,62 @@ import SetpointSensorPair_pb2 as SetpointSensorPair__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tPid.proto\x12\x08\x62lox.Pid\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\x18SetpointSensorPair.proto\"\xb0\x07\n\x05\x42lock\x12\x1c\n\x07inputId\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x04\x92?\x02\x38\x10\x12\x1d\n\x08outputId\x18\x02 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x05\x92?\x02\x38\x10\x12\x32\n\ninputValue\x18\x05 \x01(\x11\x42\x1e\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x12\x34\n\x0cinputSetting\x18\x06 \x01(\x11\x42\x1e\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x12-\n\x0boutputValue\x18\x07 \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x12/\n\routputSetting\x18\x08 \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x12\x17\n\x07\x65nabled\x18\x0b \x01(\x08\x42\x06\x8a\xb5\x18\x02\x30\x01\x12\x1c\n\x06\x61\x63tive\x18\x0c \x01(\x08\x42\x0c\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02(\x01\x12\x1e\n\x02kp\x18\r \x01(\x11\x42\x12\x8a\xb5\x18\x02\x08\x02\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12\x17\n\x02ti\x18\x0e \x01(\rB\x0b\x8a\xb5\x18\x02\x08\x03\x92?\x02\x38\x10\x12\x17\n\x02td\x18\x0f \x01(\rB\x0b\x8a\xb5\x18\x02\x08\x03\x92?\x02\x38\x10\x12#\n\x01p\x18\x10 \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x12#\n\x01i\x18\x11 \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x12#\n\x01\x64\x18\x12 \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x12-\n\x05\x65rror\x18\x13 \x01(\x11\x42\x1e\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02\x08\x06\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x12*\n\x08integral\x18\x14 \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x03\x10\x80\x02\x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x12-\n\nderivative\x18\x15 \x01(\x11\x42\x19\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x04\x10\x80\x80 \x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x12)\n\rintegralReset\x18\x17 \x01(\x11\x42\x12\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12+\n\x0f\x62oilPointAdjust\x18\x18 \x01(\x11\x42\x12\x8a\xb5\x18\x02\x08\x06\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12#\n\rboilMinOutput\x18\x19 \x01(\x11\x42\x0c\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12$\n\x0e\x62oilModeActive\x18\x1a \x01(\x08\x42\x0c\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02(\x01\x12G\n\x10\x64\x65rivativeFilter\x18\x1b \x01(\x0e\x32%.blox.SetpointSensorPair.FilterChoiceB\x06\x8a\xb5\x18\x02(\x01\x12#\n\x0e\x64rivenOutputId\x18Z \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03:\r\x8a\xb5\x18\x03\x18\xb0\x02\x8a\xb5\x18\x02H\x0f\x62\x06proto3') - - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'Pid_pb2' - # @@protoc_insertion_point(class_scope:blox.Pid.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tPid.proto\x12\x08\x62lox.Pid\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\x18SetpointSensorPair.proto\"\xbd\x06\n\x05\x42lock\x12\x1c\n\x07inputId\x18\x01 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x04\x12\x1d\n\x08outputId\x18\x02 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x05\x12&\n\ninputValue\x18\x05 \x01(\x11\x42\x12\x92?\x02\x38 \x8a\xb5\x18\t\x08\x01\x10\x80 (\x01\x30\x01\x12(\n\x0cinputSetting\x18\x06 \x01(\x11\x42\x12\x92?\x02\x38 \x8a\xb5\x18\t\x08\x01\x10\x80 (\x01\x30\x01\x12%\n\x0boutputValue\x18\x07 \x01(\x11\x42\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x10\x80 (\x01\x30\x01\x12\'\n\routputSetting\x18\x08 \x01(\x11\x42\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x10\x80 (\x01\x30\x01\x12\x17\n\x07\x65nabled\x18\x0b \x01(\x08\x42\x06\x8a\xb5\x18\x02\x30\x01\x12\x18\n\x06\x61\x63tive\x18\x0c \x01(\x08\x42\x08\x8a\xb5\x18\x04(\x01\x30\x01\x12\x1a\n\x02kp\x18\r \x01(\x11\x42\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x08\x02\x10\x80 \x12\x17\n\x02ti\x18\x0e \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x08\x03\x12\x17\n\x02td\x18\x0f \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x08\x03\x12\x1b\n\x01p\x18\x10 \x01(\x11\x42\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x10\x80 (\x01\x30\x01\x12\x1b\n\x01i\x18\x11 \x01(\x11\x42\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x10\x80 (\x01\x30\x01\x12\x1b\n\x01\x64\x18\x12 \x01(\x11\x42\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x10\x80 (\x01\x30\x01\x12!\n\x05\x65rror\x18\x13 \x01(\x11\x42\x12\x92?\x02\x38 \x8a\xb5\x18\t\x08\x06\x10\x80 (\x01\x30\x01\x12\"\n\x08integral\x18\x14 \x01(\x11\x42\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x10\x80\x02(\x01\x30\x01\x12%\n\nderivative\x18\x15 \x01(\x11\x42\x11\x92?\x02\x38 \x8a\xb5\x18\x08\x10\x80\x80 (\x01\x30\x01\x12%\n\rintegralReset\x18\x17 \x01(\x11\x42\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x10\x80 0\x01\x12\'\n\x0f\x62oilPointAdjust\x18\x18 \x01(\x11\x42\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x08\x06\x10\x80 \x12#\n\rboilMinOutput\x18\x19 \x01(\x11\x42\x0c\x92?\x02\x38 \x8a\xb5\x18\x03\x10\x80 \x12 \n\x0e\x62oilModeActive\x18\x1a \x01(\x08\x42\x08\x8a\xb5\x18\x04(\x01\x30\x01\x12G\n\x10\x64\x65rivativeFilter\x18\x1b \x01(\x0e\x32%.blox.SetpointSensorPair.FilterChoiceB\x06\x8a\xb5\x18\x02(\x01\x12#\n\x0e\x64rivenOutputId\x18Z \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01:\n\x8a\xb5\x18\x06\x18\xb0\x02J\x01\x0f\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Pid_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _BLOCK.fields_by_name['inputId']._options = None - _BLOCK.fields_by_name['inputId']._serialized_options = b'\212\265\030\002\030\004\222?\0028\020' + _BLOCK.fields_by_name['inputId']._serialized_options = b'\222?\0028\020\212\265\030\002\030\004' _BLOCK.fields_by_name['outputId']._options = None - _BLOCK.fields_by_name['outputId']._serialized_options = b'\212\265\030\002\030\005\222?\0028\020' + _BLOCK.fields_by_name['outputId']._serialized_options = b'\222?\0028\020\212\265\030\002\030\005' _BLOCK.fields_by_name['inputValue']._options = None - _BLOCK.fields_by_name['inputValue']._serialized_options = b'\212\265\030\0020\001\212\265\030\002\010\001\212\265\030\003\020\200 \222?\0028 \212\265\030\002(\001' + _BLOCK.fields_by_name['inputValue']._serialized_options = b'\222?\0028 \212\265\030\t\010\001\020\200 (\0010\001' _BLOCK.fields_by_name['inputSetting']._options = None - _BLOCK.fields_by_name['inputSetting']._serialized_options = b'\212\265\030\0020\001\212\265\030\002\010\001\212\265\030\003\020\200 \222?\0028 \212\265\030\002(\001' + _BLOCK.fields_by_name['inputSetting']._serialized_options = b'\222?\0028 \212\265\030\t\010\001\020\200 (\0010\001' _BLOCK.fields_by_name['outputValue']._options = None - _BLOCK.fields_by_name['outputValue']._serialized_options = b'\212\265\030\0020\001\212\265\030\003\020\200 \222?\0028 \212\265\030\002(\001' + _BLOCK.fields_by_name['outputValue']._serialized_options = b'\222?\0028 \212\265\030\007\020\200 (\0010\001' _BLOCK.fields_by_name['outputSetting']._options = None - _BLOCK.fields_by_name['outputSetting']._serialized_options = b'\212\265\030\0020\001\212\265\030\003\020\200 \222?\0028 \212\265\030\002(\001' + _BLOCK.fields_by_name['outputSetting']._serialized_options = b'\222?\0028 \212\265\030\007\020\200 (\0010\001' _BLOCK.fields_by_name['enabled']._options = None _BLOCK.fields_by_name['enabled']._serialized_options = b'\212\265\030\0020\001' _BLOCK.fields_by_name['active']._options = None - _BLOCK.fields_by_name['active']._serialized_options = b'\212\265\030\0020\001\212\265\030\002(\001' + _BLOCK.fields_by_name['active']._serialized_options = b'\212\265\030\004(\0010\001' _BLOCK.fields_by_name['kp']._options = None - _BLOCK.fields_by_name['kp']._serialized_options = b'\212\265\030\002\010\002\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['kp']._serialized_options = b'\222?\0028 \212\265\030\005\010\002\020\200 ' _BLOCK.fields_by_name['ti']._options = None - _BLOCK.fields_by_name['ti']._serialized_options = b'\212\265\030\002\010\003\222?\0028\020' + _BLOCK.fields_by_name['ti']._serialized_options = b'\222?\0028\020\212\265\030\002\010\003' _BLOCK.fields_by_name['td']._options = None - _BLOCK.fields_by_name['td']._serialized_options = b'\212\265\030\002\010\003\222?\0028\020' + _BLOCK.fields_by_name['td']._serialized_options = b'\222?\0028\020\212\265\030\002\010\003' _BLOCK.fields_by_name['p']._options = None - _BLOCK.fields_by_name['p']._serialized_options = b'\212\265\030\0020\001\212\265\030\003\020\200 \222?\0028 \212\265\030\002(\001' + _BLOCK.fields_by_name['p']._serialized_options = b'\222?\0028 \212\265\030\007\020\200 (\0010\001' _BLOCK.fields_by_name['i']._options = None - _BLOCK.fields_by_name['i']._serialized_options = b'\212\265\030\0020\001\212\265\030\003\020\200 \222?\0028 \212\265\030\002(\001' + _BLOCK.fields_by_name['i']._serialized_options = b'\222?\0028 \212\265\030\007\020\200 (\0010\001' _BLOCK.fields_by_name['d']._options = None - _BLOCK.fields_by_name['d']._serialized_options = b'\212\265\030\0020\001\212\265\030\003\020\200 \222?\0028 \212\265\030\002(\001' + _BLOCK.fields_by_name['d']._serialized_options = b'\222?\0028 \212\265\030\007\020\200 (\0010\001' _BLOCK.fields_by_name['error']._options = None - _BLOCK.fields_by_name['error']._serialized_options = b'\212\265\030\0020\001\212\265\030\002\010\006\212\265\030\003\020\200 \222?\0028 \212\265\030\002(\001' + _BLOCK.fields_by_name['error']._serialized_options = b'\222?\0028 \212\265\030\t\010\006\020\200 (\0010\001' _BLOCK.fields_by_name['integral']._options = None - _BLOCK.fields_by_name['integral']._serialized_options = b'\212\265\030\0020\001\212\265\030\003\020\200\002\222?\0028 \212\265\030\002(\001' + _BLOCK.fields_by_name['integral']._serialized_options = b'\222?\0028 \212\265\030\007\020\200\002(\0010\001' _BLOCK.fields_by_name['derivative']._options = None - _BLOCK.fields_by_name['derivative']._serialized_options = b'\212\265\030\0020\001\212\265\030\004\020\200\200 \222?\0028 \212\265\030\002(\001' + _BLOCK.fields_by_name['derivative']._serialized_options = b'\222?\0028 \212\265\030\010\020\200\200 (\0010\001' _BLOCK.fields_by_name['integralReset']._options = None - _BLOCK.fields_by_name['integralReset']._serialized_options = b'\212\265\030\0020\001\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['integralReset']._serialized_options = b'\222?\0028 \212\265\030\005\020\200 0\001' _BLOCK.fields_by_name['boilPointAdjust']._options = None - _BLOCK.fields_by_name['boilPointAdjust']._serialized_options = b'\212\265\030\002\010\006\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['boilPointAdjust']._serialized_options = b'\222?\0028 \212\265\030\005\010\006\020\200 ' _BLOCK.fields_by_name['boilMinOutput']._options = None - _BLOCK.fields_by_name['boilMinOutput']._serialized_options = b'\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['boilMinOutput']._serialized_options = b'\222?\0028 \212\265\030\003\020\200 ' _BLOCK.fields_by_name['boilModeActive']._options = None - _BLOCK.fields_by_name['boilModeActive']._serialized_options = b'\212\265\030\0020\001\212\265\030\002(\001' + _BLOCK.fields_by_name['boilModeActive']._serialized_options = b'\212\265\030\004(\0010\001' _BLOCK.fields_by_name['derivativeFilter']._options = None _BLOCK.fields_by_name['derivativeFilter']._serialized_options = b'\212\265\030\002(\001' _BLOCK.fields_by_name['drivenOutputId']._options = None - _BLOCK.fields_by_name['drivenOutputId']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['drivenOutputId']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\260\002\212\265\030\002H\017' - _BLOCK._serialized_start=80 - _BLOCK._serialized_end=1024 + _BLOCK._serialized_options = b'\212\265\030\006\030\260\002J\001\017' + _globals['_BLOCK']._serialized_start=80 + _globals['_BLOCK']._serialized_end=909 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/Screen_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/Screen_pb2.py index d346ee48..74fde433 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/Screen_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/Screen_pb2.py @@ -4,9 +4,8 @@ """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -17,57 +16,9 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cScreen.proto\x12\x06screen\x1a\x0cnanopb.proto\"\xa2\x01\n\nLayoutNode\x12\x15\n\x06parent\x18\x01 \x01(\rB\x05\x92?\x02\x38\x08\x12\x15\n\x06nodeId\x18\x02 \x01(\rB\x05\x92?\x02\x38\x08\x12%\n\x04type\x18\x03 \x01(\x0e\x32\x17.screen.LayoutNode.Type\x12\x15\n\x06weight\x18\x04 \x01(\rB\x05\x92?\x02\x38\x10\"(\n\x04Type\x12\x07\n\x03Row\x10\x00\x12\n\n\x06\x43olumn\x10\x01\x12\x0b\n\x07\x43ontent\x10\x02\"=\n\x05\x43olor\x12\x10\n\x01r\x18\x01 \x01(\rB\x05\x92?\x02\x38\x08\x12\x10\n\x01g\x18\x02 \x01(\rB\x05\x92?\x02\x38\x08\x12\x10\n\x01\x62\x18\x03 \x01(\rB\x05\x92?\x02\x38\x08\"^\n\x12NumericValueWidget\x12\x1c\n\x05\x63olor\x18\x01 \x01(\x0b\x32\r.screen.Color\x12\x14\n\x05value\x18\x02 \x01(\rB\x05\x92?\x02\x38\x08\x12\x14\n\x05label\x18\x03 \x01(\tB\x05\x92?\x02p(\"+\n\x0b\x43olorWidget\x12\x1c\n\x05\x63olor\x18\x01 \x01(\x0b\x32\r.screen.Color\"\x9b\x01\n\x0b\x43ontentNode\x12\x1b\n\x0clayoutNodeId\x18\x01 \x01(\rB\x05\x92?\x02\x38\x08\x12\x38\n\x12numericValueWidget\x18\x02 \x01(\x0b\x32\x1a.screen.NumericValueWidgetH\x00\x12*\n\x0b\x63olorWidget\x18\x03 \x01(\x0b\x32\x13.screen.ColorWidgetH\x00\x42\t\n\x07\x63ontent\"\\\n\x06\x43onfig\x12\'\n\x0blayoutNodes\x18\x01 \x03(\x0b\x32\x12.screen.LayoutNode\x12)\n\x0c\x63ontentNodes\x18\x02 \x03(\x0b\x32\x13.screen.ContentNodeb\x06proto3') - - -_LAYOUTNODE = DESCRIPTOR.message_types_by_name['LayoutNode'] -_COLOR = DESCRIPTOR.message_types_by_name['Color'] -_NUMERICVALUEWIDGET = DESCRIPTOR.message_types_by_name['NumericValueWidget'] -_COLORWIDGET = DESCRIPTOR.message_types_by_name['ColorWidget'] -_CONTENTNODE = DESCRIPTOR.message_types_by_name['ContentNode'] -_CONFIG = DESCRIPTOR.message_types_by_name['Config'] -_LAYOUTNODE_TYPE = _LAYOUTNODE.enum_types_by_name['Type'] -LayoutNode = _reflection.GeneratedProtocolMessageType('LayoutNode', (_message.Message,), { - 'DESCRIPTOR' : _LAYOUTNODE, - '__module__' : 'Screen_pb2' - # @@protoc_insertion_point(class_scope:screen.LayoutNode) - }) -_sym_db.RegisterMessage(LayoutNode) - -Color = _reflection.GeneratedProtocolMessageType('Color', (_message.Message,), { - 'DESCRIPTOR' : _COLOR, - '__module__' : 'Screen_pb2' - # @@protoc_insertion_point(class_scope:screen.Color) - }) -_sym_db.RegisterMessage(Color) - -NumericValueWidget = _reflection.GeneratedProtocolMessageType('NumericValueWidget', (_message.Message,), { - 'DESCRIPTOR' : _NUMERICVALUEWIDGET, - '__module__' : 'Screen_pb2' - # @@protoc_insertion_point(class_scope:screen.NumericValueWidget) - }) -_sym_db.RegisterMessage(NumericValueWidget) - -ColorWidget = _reflection.GeneratedProtocolMessageType('ColorWidget', (_message.Message,), { - 'DESCRIPTOR' : _COLORWIDGET, - '__module__' : 'Screen_pb2' - # @@protoc_insertion_point(class_scope:screen.ColorWidget) - }) -_sym_db.RegisterMessage(ColorWidget) - -ContentNode = _reflection.GeneratedProtocolMessageType('ContentNode', (_message.Message,), { - 'DESCRIPTOR' : _CONTENTNODE, - '__module__' : 'Screen_pb2' - # @@protoc_insertion_point(class_scope:screen.ContentNode) - }) -_sym_db.RegisterMessage(ContentNode) - -Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), { - 'DESCRIPTOR' : _CONFIG, - '__module__' : 'Screen_pb2' - # @@protoc_insertion_point(class_scope:screen.Config) - }) -_sym_db.RegisterMessage(Config) - +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Screen_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None @@ -89,18 +40,18 @@ _NUMERICVALUEWIDGET.fields_by_name['label']._serialized_options = b'\222?\002p(' _CONTENTNODE.fields_by_name['layoutNodeId']._options = None _CONTENTNODE.fields_by_name['layoutNodeId']._serialized_options = b'\222?\0028\010' - _LAYOUTNODE._serialized_start=39 - _LAYOUTNODE._serialized_end=201 - _LAYOUTNODE_TYPE._serialized_start=161 - _LAYOUTNODE_TYPE._serialized_end=201 - _COLOR._serialized_start=203 - _COLOR._serialized_end=264 - _NUMERICVALUEWIDGET._serialized_start=266 - _NUMERICVALUEWIDGET._serialized_end=360 - _COLORWIDGET._serialized_start=362 - _COLORWIDGET._serialized_end=405 - _CONTENTNODE._serialized_start=408 - _CONTENTNODE._serialized_end=563 - _CONFIG._serialized_start=565 - _CONFIG._serialized_end=657 + _globals['_LAYOUTNODE']._serialized_start=39 + _globals['_LAYOUTNODE']._serialized_end=201 + _globals['_LAYOUTNODE_TYPE']._serialized_start=161 + _globals['_LAYOUTNODE_TYPE']._serialized_end=201 + _globals['_COLOR']._serialized_start=203 + _globals['_COLOR']._serialized_end=264 + _globals['_NUMERICVALUEWIDGET']._serialized_start=266 + _globals['_NUMERICVALUEWIDGET']._serialized_end=360 + _globals['_COLORWIDGET']._serialized_start=362 + _globals['_COLORWIDGET']._serialized_end=405 + _globals['_CONTENTNODE']._serialized_start=408 + _globals['_CONTENTNODE']._serialized_end=563 + _globals['_CONFIG']._serialized_start=565 + _globals['_CONFIG']._serialized_end=657 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/Sequence_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/Sequence_pb2.py index f3532a33..52c95648 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/Sequence_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/Sequence_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: Sequence.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -18,213 +16,52 @@ import IoArray_pb2 as IoArray__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0eSequence.proto\x12\rblox.Sequence\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\rIoArray.proto\"\x17\n\x07\x43omment\x12\x0c\n\x04text\x18\x01 \x01(\t\"\t\n\x07Restart\",\n\rEnableDisable\x12\x1b\n\x06target\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x0f\x92?\x02\x38\x10\"\x06\n\x04Wait\"-\n\x0cWaitDuration\x12\x1d\n\x08\x64uration\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x08\x03\x92?\x02\x38 \"&\n\tWaitUntil\x12\x19\n\x04time\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02X\x01\x92?\x02\x38 \"y\n\x14WaitTemperatureRange\x12\x1b\n\x06target\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x02\x92?\x02\x38\x10\x12!\n\x05lower\x18\x02 \x01(\x11\x42\x12\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12!\n\x05upper\x18\x03 \x01(\x11\x42\x12\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \"Y\n\x17WaitTemperatureBoundary\x12\x1b\n\x06target\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x02\x92?\x02\x38\x10\x12!\n\x05value\x18\x02 \x01(\x11\x42\x12\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \"O\n\x0bSetSetpoint\x12\x1b\n\x06target\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x04\x92?\x02\x38\x10\x12#\n\x07setting\x18\x02 \x01(\x11\x42\x12\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \"R\n\x0cWaitSetpoint\x12\x1b\n\x06target\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x04\x92?\x02\x38\x10\x12%\n\tprecision\x18\x02 \x01(\x11\x42\x12\x8a\xb5\x18\x02\x08\x06\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \"V\n\nSetDigital\x12\x1b\n\x06target\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x06\x92?\x02\x38\x10\x12+\n\x07setting\x18\x02 \x01(\x0e\x32\x1a.blox.IoArray.DigitalState\"*\n\x0bWaitDigital\x12\x1b\n\x06target\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x06\x92?\x02\x38\x10\"Z\n\x10WaitDigitalState\x12\x1b\n\x06target\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x1b\x92?\x02\x38\x10\x12)\n\x05state\x18\x02 \x01(\x0e\x32\x1a.blox.IoArray.DigitalState\"D\n\x06SetPwm\x12\x1b\n\x06target\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x05\x92?\x02\x38\x10\x12\x1d\n\x07setting\x18\x02 \x01(\x11\x42\x0c\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \"-\n\rTargetProfile\x12\x1c\n\x06target\x18\x01 \x01(\rB\x0c\x8a\xb5\x18\x03\x18\xb7\x02\x92?\x02\x38\x10\".\n\x0eTargetSequence\x12\x1c\n\x06target\x18\x01 \x01(\rB\x0c\x8a\xb5\x18\x03\x18\xc6\x02\x92?\x02\x38\x10\"\xd0\n\n\x0bInstruction\x12)\n\x07RESTART\x18\x01 \x01(\x0b\x32\x16.blox.Sequence.RestartH\x00\x12.\n\x06\x45NABLE\x18\x02 \x01(\x0b\x32\x1c.blox.Sequence.EnableDisableH\x00\x12/\n\x07\x44ISABLE\x18\x03 \x01(\x0b\x32\x1c.blox.Sequence.EnableDisableH\x00\x12#\n\x04WAIT\x18\x14 \x01(\x0b\x32\x13.blox.Sequence.WaitH\x00\x12\x34\n\rWAIT_DURATION\x18\x04 \x01(\x0b\x32\x1b.blox.Sequence.WaitDurationH\x00\x12.\n\nWAIT_UNTIL\x18\x05 \x01(\x0b\x32\x18.blox.Sequence.WaitUntilH\x00\x12@\n\x11WAIT_TEMP_BETWEEN\x18\x06 \x01(\x0b\x32#.blox.Sequence.WaitTemperatureRangeH\x00\x12\x44\n\x15WAIT_TEMP_NOT_BETWEEN\x18\x07 \x01(\x0b\x32#.blox.Sequence.WaitTemperatureRangeH\x00\x12\x43\n\x14WAIT_TEMP_UNEXPECTED\x18\x08 \x01(\x0b\x32#.blox.Sequence.WaitTemperatureRangeH\x00\x12\x41\n\x0fWAIT_TEMP_ABOVE\x18\t \x01(\x0b\x32&.blox.Sequence.WaitTemperatureBoundaryH\x00\x12\x41\n\x0fWAIT_TEMP_BELOW\x18\n \x01(\x0b\x32&.blox.Sequence.WaitTemperatureBoundaryH\x00\x12\x32\n\x0cSET_SETPOINT\x18\x0b \x01(\x0b\x32\x1a.blox.Sequence.SetSetpointH\x00\x12\x34\n\rWAIT_SETPOINT\x18\x0c \x01(\x0b\x32\x1b.blox.Sequence.WaitSetpointH\x00\x12:\n\x13WAIT_SETPOINT_ABOVE\x18\x15 \x01(\x0b\x32\x1b.blox.Sequence.WaitSetpointH\x00\x12:\n\x13WAIT_SETPOINT_BELOW\x18\x16 \x01(\x0b\x32\x1b.blox.Sequence.WaitSetpointH\x00\x12\x30\n\x0bSET_DIGITAL\x18\r \x01(\x0b\x32\x19.blox.Sequence.SetDigitalH\x00\x12\x32\n\x0cWAIT_DIGITAL\x18\x0e \x01(\x0b\x32\x1a.blox.Sequence.WaitDigitalH\x00\x12>\n\x13WAIT_DIGITAL_EQUALS\x18\x17 \x01(\x0b\x32\x1f.blox.Sequence.WaitDigitalStateH\x00\x12(\n\x07SET_PWM\x18\x0f \x01(\x0b\x32\x15.blox.Sequence.SetPwmH\x00\x12\x35\n\rSTART_PROFILE\x18\x10 \x01(\x0b\x32\x1c.blox.Sequence.TargetProfileH\x00\x12\x34\n\x0cWAIT_PROFILE\x18\x11 \x01(\x0b\x32\x1c.blox.Sequence.TargetProfileH\x00\x12\x37\n\x0eSTART_SEQUENCE\x18\x12 \x01(\x0b\x32\x1d.blox.Sequence.TargetSequenceH\x00\x12\x36\n\rWAIT_SEQUENCE\x18\x13 \x01(\x0b\x32\x1d.blox.Sequence.TargetSequenceH\x00\x12*\n\x07\x43OMMENT\x18\xc8\x01 \x01(\x0b\x32\x16.blox.Sequence.CommentH\x00:\x06\x92?\x03\xb0\x01\x01\x42\x13\n\x11instruction_oneof\"\xb6\x03\n\x05\x42lock\x12\x17\n\x07\x65nabled\x18\x01 \x01(\x08\x42\x06\x8a\xb5\x18\x02\x30\x01\x12\x30\n\x0cinstructions\x18\x02 \x03(\x0b\x32\x1a.blox.Sequence.Instruction\x12\x15\n\roverrideState\x18\x03 \x01(\x08\x12&\n\x11\x61\x63tiveInstruction\x18\x04 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x30\x01\x12\x35\n\x06status\x18\x08 \x01(\x0e\x32\x1d.blox.Sequence.SequenceStatusB\x06\x8a\xb5\x18\x02(\x01\x12\x33\n\x05\x65rror\x18\t \x01(\x0e\x32\x1c.blox.Sequence.SequenceErrorB\x06\x8a\xb5\x18\x02(\x01\x12/\n\x07\x65lapsed\x18\n \x01(\rB\x1e\x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02\x08\x03\x8a\xb5\x18\x03\x10\xe8\x07\x12/\n\x1a\x61\x63tiveInstructionStartedAt\x18\x05 \x01(\rB\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03\x12\x1f\n\ndisabledAt\x18\x06 \x01(\rB\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03\x12%\n\x10\x64isabledDuration\x18\x07 \x01(\rB\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03:\r\x8a\xb5\x18\x03\x18\xc6\x02\x8a\xb5\x18\x02H\x0f*l\n\x0eSequenceStatus\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x0c\n\x08\x44ISABLED\x10\x01\x12\n\n\x06PAUSED\x10\x02\x12\x08\n\x04NEXT\x10\x03\x12\x08\n\x04WAIT\x10\x04\x12\x07\n\x03\x45ND\x10\x05\x12\x0b\n\x07RESTART\x10\x06\x12\t\n\x05\x45RROR\x10\x07*\x8c\x01\n\rSequenceError\x12\x08\n\x04NONE\x10\x00\x12\x14\n\x10INVALID_ARGUMENT\x10\x01\x12\x12\n\x0eINVALID_TARGET\x10\x02\x12\x13\n\x0fINACTIVE_TARGET\x10\x03\x12\x13\n\x0f\x44ISABLED_TARGET\x10\x04\x12\x1d\n\x19SYSTEM_TIME_NOT_AVAILABLE\x10\x05\x62\x06proto3') - -_SEQUENCESTATUS = DESCRIPTOR.enum_types_by_name['SequenceStatus'] -SequenceStatus = enum_type_wrapper.EnumTypeWrapper(_SEQUENCESTATUS) -_SEQUENCEERROR = DESCRIPTOR.enum_types_by_name['SequenceError'] -SequenceError = enum_type_wrapper.EnumTypeWrapper(_SEQUENCEERROR) -UNKNOWN = 0 -DISABLED = 1 -PAUSED = 2 -NEXT = 3 -WAIT = 4 -END = 5 -RESTART = 6 -ERROR = 7 -NONE = 0 -INVALID_ARGUMENT = 1 -INVALID_TARGET = 2 -INACTIVE_TARGET = 3 -DISABLED_TARGET = 4 -SYSTEM_TIME_NOT_AVAILABLE = 5 - - -_COMMENT = DESCRIPTOR.message_types_by_name['Comment'] -_RESTART = DESCRIPTOR.message_types_by_name['Restart'] -_ENABLEDISABLE = DESCRIPTOR.message_types_by_name['EnableDisable'] -_WAIT = DESCRIPTOR.message_types_by_name['Wait'] -_WAITDURATION = DESCRIPTOR.message_types_by_name['WaitDuration'] -_WAITUNTIL = DESCRIPTOR.message_types_by_name['WaitUntil'] -_WAITTEMPERATURERANGE = DESCRIPTOR.message_types_by_name['WaitTemperatureRange'] -_WAITTEMPERATUREBOUNDARY = DESCRIPTOR.message_types_by_name['WaitTemperatureBoundary'] -_SETSETPOINT = DESCRIPTOR.message_types_by_name['SetSetpoint'] -_WAITSETPOINT = DESCRIPTOR.message_types_by_name['WaitSetpoint'] -_SETDIGITAL = DESCRIPTOR.message_types_by_name['SetDigital'] -_WAITDIGITAL = DESCRIPTOR.message_types_by_name['WaitDigital'] -_WAITDIGITALSTATE = DESCRIPTOR.message_types_by_name['WaitDigitalState'] -_SETPWM = DESCRIPTOR.message_types_by_name['SetPwm'] -_TARGETPROFILE = DESCRIPTOR.message_types_by_name['TargetProfile'] -_TARGETSEQUENCE = DESCRIPTOR.message_types_by_name['TargetSequence'] -_INSTRUCTION = DESCRIPTOR.message_types_by_name['Instruction'] -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Comment = _reflection.GeneratedProtocolMessageType('Comment', (_message.Message,), { - 'DESCRIPTOR' : _COMMENT, - '__module__' : 'Sequence_pb2' - # @@protoc_insertion_point(class_scope:blox.Sequence.Comment) - }) -_sym_db.RegisterMessage(Comment) - -Restart = _reflection.GeneratedProtocolMessageType('Restart', (_message.Message,), { - 'DESCRIPTOR' : _RESTART, - '__module__' : 'Sequence_pb2' - # @@protoc_insertion_point(class_scope:blox.Sequence.Restart) - }) -_sym_db.RegisterMessage(Restart) - -EnableDisable = _reflection.GeneratedProtocolMessageType('EnableDisable', (_message.Message,), { - 'DESCRIPTOR' : _ENABLEDISABLE, - '__module__' : 'Sequence_pb2' - # @@protoc_insertion_point(class_scope:blox.Sequence.EnableDisable) - }) -_sym_db.RegisterMessage(EnableDisable) - -Wait = _reflection.GeneratedProtocolMessageType('Wait', (_message.Message,), { - 'DESCRIPTOR' : _WAIT, - '__module__' : 'Sequence_pb2' - # @@protoc_insertion_point(class_scope:blox.Sequence.Wait) - }) -_sym_db.RegisterMessage(Wait) - -WaitDuration = _reflection.GeneratedProtocolMessageType('WaitDuration', (_message.Message,), { - 'DESCRIPTOR' : _WAITDURATION, - '__module__' : 'Sequence_pb2' - # @@protoc_insertion_point(class_scope:blox.Sequence.WaitDuration) - }) -_sym_db.RegisterMessage(WaitDuration) - -WaitUntil = _reflection.GeneratedProtocolMessageType('WaitUntil', (_message.Message,), { - 'DESCRIPTOR' : _WAITUNTIL, - '__module__' : 'Sequence_pb2' - # @@protoc_insertion_point(class_scope:blox.Sequence.WaitUntil) - }) -_sym_db.RegisterMessage(WaitUntil) - -WaitTemperatureRange = _reflection.GeneratedProtocolMessageType('WaitTemperatureRange', (_message.Message,), { - 'DESCRIPTOR' : _WAITTEMPERATURERANGE, - '__module__' : 'Sequence_pb2' - # @@protoc_insertion_point(class_scope:blox.Sequence.WaitTemperatureRange) - }) -_sym_db.RegisterMessage(WaitTemperatureRange) - -WaitTemperatureBoundary = _reflection.GeneratedProtocolMessageType('WaitTemperatureBoundary', (_message.Message,), { - 'DESCRIPTOR' : _WAITTEMPERATUREBOUNDARY, - '__module__' : 'Sequence_pb2' - # @@protoc_insertion_point(class_scope:blox.Sequence.WaitTemperatureBoundary) - }) -_sym_db.RegisterMessage(WaitTemperatureBoundary) - -SetSetpoint = _reflection.GeneratedProtocolMessageType('SetSetpoint', (_message.Message,), { - 'DESCRIPTOR' : _SETSETPOINT, - '__module__' : 'Sequence_pb2' - # @@protoc_insertion_point(class_scope:blox.Sequence.SetSetpoint) - }) -_sym_db.RegisterMessage(SetSetpoint) - -WaitSetpoint = _reflection.GeneratedProtocolMessageType('WaitSetpoint', (_message.Message,), { - 'DESCRIPTOR' : _WAITSETPOINT, - '__module__' : 'Sequence_pb2' - # @@protoc_insertion_point(class_scope:blox.Sequence.WaitSetpoint) - }) -_sym_db.RegisterMessage(WaitSetpoint) - -SetDigital = _reflection.GeneratedProtocolMessageType('SetDigital', (_message.Message,), { - 'DESCRIPTOR' : _SETDIGITAL, - '__module__' : 'Sequence_pb2' - # @@protoc_insertion_point(class_scope:blox.Sequence.SetDigital) - }) -_sym_db.RegisterMessage(SetDigital) - -WaitDigital = _reflection.GeneratedProtocolMessageType('WaitDigital', (_message.Message,), { - 'DESCRIPTOR' : _WAITDIGITAL, - '__module__' : 'Sequence_pb2' - # @@protoc_insertion_point(class_scope:blox.Sequence.WaitDigital) - }) -_sym_db.RegisterMessage(WaitDigital) - -WaitDigitalState = _reflection.GeneratedProtocolMessageType('WaitDigitalState', (_message.Message,), { - 'DESCRIPTOR' : _WAITDIGITALSTATE, - '__module__' : 'Sequence_pb2' - # @@protoc_insertion_point(class_scope:blox.Sequence.WaitDigitalState) - }) -_sym_db.RegisterMessage(WaitDigitalState) - -SetPwm = _reflection.GeneratedProtocolMessageType('SetPwm', (_message.Message,), { - 'DESCRIPTOR' : _SETPWM, - '__module__' : 'Sequence_pb2' - # @@protoc_insertion_point(class_scope:blox.Sequence.SetPwm) - }) -_sym_db.RegisterMessage(SetPwm) - -TargetProfile = _reflection.GeneratedProtocolMessageType('TargetProfile', (_message.Message,), { - 'DESCRIPTOR' : _TARGETPROFILE, - '__module__' : 'Sequence_pb2' - # @@protoc_insertion_point(class_scope:blox.Sequence.TargetProfile) - }) -_sym_db.RegisterMessage(TargetProfile) - -TargetSequence = _reflection.GeneratedProtocolMessageType('TargetSequence', (_message.Message,), { - 'DESCRIPTOR' : _TARGETSEQUENCE, - '__module__' : 'Sequence_pb2' - # @@protoc_insertion_point(class_scope:blox.Sequence.TargetSequence) - }) -_sym_db.RegisterMessage(TargetSequence) - -Instruction = _reflection.GeneratedProtocolMessageType('Instruction', (_message.Message,), { - 'DESCRIPTOR' : _INSTRUCTION, - '__module__' : 'Sequence_pb2' - # @@protoc_insertion_point(class_scope:blox.Sequence.Instruction) - }) -_sym_db.RegisterMessage(Instruction) - -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'Sequence_pb2' - # @@protoc_insertion_point(class_scope:blox.Sequence.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0eSequence.proto\x12\rblox.Sequence\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\rIoArray.proto\"\x17\n\x07\x43omment\x12\x0c\n\x04text\x18\x01 \x01(\t\"\t\n\x07Restart\",\n\rEnableDisable\x12\x1b\n\x06target\x18\x01 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x0f\"\x06\n\x04Wait\"-\n\x0cWaitDuration\x12\x1d\n\x08\x64uration\x18\x01 \x01(\rB\x0b\x92?\x02\x38 \x8a\xb5\x18\x02\x08\x03\"&\n\tWaitUntil\x12\x19\n\x04time\x18\x01 \x01(\rB\x0b\x92?\x02\x38 \x8a\xb5\x18\x02X\x01\"q\n\x14WaitTemperatureRange\x12\x1b\n\x06target\x18\x01 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x02\x12\x1d\n\x05lower\x18\x02 \x01(\x11\x42\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x08\x01\x10\x80 \x12\x1d\n\x05upper\x18\x03 \x01(\x11\x42\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x08\x01\x10\x80 \"U\n\x17WaitTemperatureBoundary\x12\x1b\n\x06target\x18\x01 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x02\x12\x1d\n\x05value\x18\x02 \x01(\x11\x42\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x08\x01\x10\x80 \"K\n\x0bSetSetpoint\x12\x1b\n\x06target\x18\x01 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x04\x12\x1f\n\x07setting\x18\x02 \x01(\x11\x42\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x08\x01\x10\x80 \"N\n\x0cWaitSetpoint\x12\x1b\n\x06target\x18\x01 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x04\x12!\n\tprecision\x18\x02 \x01(\x11\x42\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x08\x06\x10\x80 \"V\n\nSetDigital\x12\x1b\n\x06target\x18\x01 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x06\x12+\n\x07setting\x18\x02 \x01(\x0e\x32\x1a.blox.IoArray.DigitalState\"*\n\x0bWaitDigital\x12\x1b\n\x06target\x18\x01 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x06\"Z\n\x10WaitDigitalState\x12\x1b\n\x06target\x18\x01 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x1b\x12)\n\x05state\x18\x02 \x01(\x0e\x32\x1a.blox.IoArray.DigitalState\"D\n\x06SetPwm\x12\x1b\n\x06target\x18\x01 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x05\x12\x1d\n\x07setting\x18\x02 \x01(\x11\x42\x0c\x92?\x02\x38 \x8a\xb5\x18\x03\x10\x80 \"-\n\rTargetProfile\x12\x1c\n\x06target\x18\x01 \x01(\rB\x0c\x92?\x02\x38\x10\x8a\xb5\x18\x03\x18\xb7\x02\".\n\x0eTargetSequence\x12\x1c\n\x06target\x18\x01 \x01(\rB\x0c\x92?\x02\x38\x10\x8a\xb5\x18\x03\x18\xc6\x02\"\xd0\n\n\x0bInstruction\x12)\n\x07RESTART\x18\x01 \x01(\x0b\x32\x16.blox.Sequence.RestartH\x00\x12.\n\x06\x45NABLE\x18\x02 \x01(\x0b\x32\x1c.blox.Sequence.EnableDisableH\x00\x12/\n\x07\x44ISABLE\x18\x03 \x01(\x0b\x32\x1c.blox.Sequence.EnableDisableH\x00\x12#\n\x04WAIT\x18\x14 \x01(\x0b\x32\x13.blox.Sequence.WaitH\x00\x12\x34\n\rWAIT_DURATION\x18\x04 \x01(\x0b\x32\x1b.blox.Sequence.WaitDurationH\x00\x12.\n\nWAIT_UNTIL\x18\x05 \x01(\x0b\x32\x18.blox.Sequence.WaitUntilH\x00\x12@\n\x11WAIT_TEMP_BETWEEN\x18\x06 \x01(\x0b\x32#.blox.Sequence.WaitTemperatureRangeH\x00\x12\x44\n\x15WAIT_TEMP_NOT_BETWEEN\x18\x07 \x01(\x0b\x32#.blox.Sequence.WaitTemperatureRangeH\x00\x12\x43\n\x14WAIT_TEMP_UNEXPECTED\x18\x08 \x01(\x0b\x32#.blox.Sequence.WaitTemperatureRangeH\x00\x12\x41\n\x0fWAIT_TEMP_ABOVE\x18\t \x01(\x0b\x32&.blox.Sequence.WaitTemperatureBoundaryH\x00\x12\x41\n\x0fWAIT_TEMP_BELOW\x18\n \x01(\x0b\x32&.blox.Sequence.WaitTemperatureBoundaryH\x00\x12\x32\n\x0cSET_SETPOINT\x18\x0b \x01(\x0b\x32\x1a.blox.Sequence.SetSetpointH\x00\x12\x34\n\rWAIT_SETPOINT\x18\x0c \x01(\x0b\x32\x1b.blox.Sequence.WaitSetpointH\x00\x12:\n\x13WAIT_SETPOINT_ABOVE\x18\x15 \x01(\x0b\x32\x1b.blox.Sequence.WaitSetpointH\x00\x12:\n\x13WAIT_SETPOINT_BELOW\x18\x16 \x01(\x0b\x32\x1b.blox.Sequence.WaitSetpointH\x00\x12\x30\n\x0bSET_DIGITAL\x18\r \x01(\x0b\x32\x19.blox.Sequence.SetDigitalH\x00\x12\x32\n\x0cWAIT_DIGITAL\x18\x0e \x01(\x0b\x32\x1a.blox.Sequence.WaitDigitalH\x00\x12>\n\x13WAIT_DIGITAL_EQUALS\x18\x17 \x01(\x0b\x32\x1f.blox.Sequence.WaitDigitalStateH\x00\x12(\n\x07SET_PWM\x18\x0f \x01(\x0b\x32\x15.blox.Sequence.SetPwmH\x00\x12\x35\n\rSTART_PROFILE\x18\x10 \x01(\x0b\x32\x1c.blox.Sequence.TargetProfileH\x00\x12\x34\n\x0cWAIT_PROFILE\x18\x11 \x01(\x0b\x32\x1c.blox.Sequence.TargetProfileH\x00\x12\x37\n\x0eSTART_SEQUENCE\x18\x12 \x01(\x0b\x32\x1d.blox.Sequence.TargetSequenceH\x00\x12\x36\n\rWAIT_SEQUENCE\x18\x13 \x01(\x0b\x32\x1d.blox.Sequence.TargetSequenceH\x00\x12*\n\x07\x43OMMENT\x18\xc8\x01 \x01(\x0b\x32\x16.blox.Sequence.CommentH\x00:\x06\x92?\x03\xb0\x01\x01\x42\x13\n\x11instruction_oneof\"\xa7\x03\n\x05\x42lock\x12\x17\n\x07\x65nabled\x18\x01 \x01(\x08\x42\x06\x8a\xb5\x18\x02\x30\x01\x12\x30\n\x0cinstructions\x18\x02 \x03(\x0b\x32\x1a.blox.Sequence.Instruction\x12\x15\n\roverrideState\x18\x03 \x01(\x08\x12&\n\x11\x61\x63tiveInstruction\x18\x04 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x30\x01\x12\x35\n\x06status\x18\x08 \x01(\x0e\x32\x1d.blox.Sequence.SequenceStatusB\x06\x8a\xb5\x18\x02(\x01\x12\x33\n\x05\x65rror\x18\t \x01(\x0e\x32\x1c.blox.Sequence.SequenceErrorB\x06\x8a\xb5\x18\x02(\x01\x12#\n\x07\x65lapsed\x18\n \x01(\rB\x12\x92?\x02\x38 \x8a\xb5\x18\t\x08\x03\x10\xe8\x07(\x01\x30\x01\x12/\n\x1a\x61\x63tiveInstructionStartedAt\x18\x05 \x01(\rB\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01\x12\x1f\n\ndisabledAt\x18\x06 \x01(\rB\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01\x12%\n\x10\x64isabledDuration\x18\x07 \x01(\rB\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01:\n\x8a\xb5\x18\x06\x18\xc6\x02J\x01\x0f*l\n\x0eSequenceStatus\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x0c\n\x08\x44ISABLED\x10\x01\x12\n\n\x06PAUSED\x10\x02\x12\x08\n\x04NEXT\x10\x03\x12\x08\n\x04WAIT\x10\x04\x12\x07\n\x03\x45ND\x10\x05\x12\x0b\n\x07RESTART\x10\x06\x12\t\n\x05\x45RROR\x10\x07*\x8c\x01\n\rSequenceError\x12\x08\n\x04NONE\x10\x00\x12\x14\n\x10INVALID_ARGUMENT\x10\x01\x12\x12\n\x0eINVALID_TARGET\x10\x02\x12\x13\n\x0fINACTIVE_TARGET\x10\x03\x12\x13\n\x0f\x44ISABLED_TARGET\x10\x04\x12\x1d\n\x19SYSTEM_TIME_NOT_AVAILABLE\x10\x05\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Sequence_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _ENABLEDISABLE.fields_by_name['target']._options = None - _ENABLEDISABLE.fields_by_name['target']._serialized_options = b'\212\265\030\002\030\017\222?\0028\020' + _ENABLEDISABLE.fields_by_name['target']._serialized_options = b'\222?\0028\020\212\265\030\002\030\017' _WAITDURATION.fields_by_name['duration']._options = None - _WAITDURATION.fields_by_name['duration']._serialized_options = b'\212\265\030\002\010\003\222?\0028 ' + _WAITDURATION.fields_by_name['duration']._serialized_options = b'\222?\0028 \212\265\030\002\010\003' _WAITUNTIL.fields_by_name['time']._options = None - _WAITUNTIL.fields_by_name['time']._serialized_options = b'\212\265\030\002X\001\222?\0028 ' + _WAITUNTIL.fields_by_name['time']._serialized_options = b'\222?\0028 \212\265\030\002X\001' _WAITTEMPERATURERANGE.fields_by_name['target']._options = None - _WAITTEMPERATURERANGE.fields_by_name['target']._serialized_options = b'\212\265\030\002\030\002\222?\0028\020' + _WAITTEMPERATURERANGE.fields_by_name['target']._serialized_options = b'\222?\0028\020\212\265\030\002\030\002' _WAITTEMPERATURERANGE.fields_by_name['lower']._options = None - _WAITTEMPERATURERANGE.fields_by_name['lower']._serialized_options = b'\212\265\030\002\010\001\212\265\030\003\020\200 \222?\0028 ' + _WAITTEMPERATURERANGE.fields_by_name['lower']._serialized_options = b'\222?\0028 \212\265\030\005\010\001\020\200 ' _WAITTEMPERATURERANGE.fields_by_name['upper']._options = None - _WAITTEMPERATURERANGE.fields_by_name['upper']._serialized_options = b'\212\265\030\002\010\001\212\265\030\003\020\200 \222?\0028 ' + _WAITTEMPERATURERANGE.fields_by_name['upper']._serialized_options = b'\222?\0028 \212\265\030\005\010\001\020\200 ' _WAITTEMPERATUREBOUNDARY.fields_by_name['target']._options = None - _WAITTEMPERATUREBOUNDARY.fields_by_name['target']._serialized_options = b'\212\265\030\002\030\002\222?\0028\020' + _WAITTEMPERATUREBOUNDARY.fields_by_name['target']._serialized_options = b'\222?\0028\020\212\265\030\002\030\002' _WAITTEMPERATUREBOUNDARY.fields_by_name['value']._options = None - _WAITTEMPERATUREBOUNDARY.fields_by_name['value']._serialized_options = b'\212\265\030\002\010\001\212\265\030\003\020\200 \222?\0028 ' + _WAITTEMPERATUREBOUNDARY.fields_by_name['value']._serialized_options = b'\222?\0028 \212\265\030\005\010\001\020\200 ' _SETSETPOINT.fields_by_name['target']._options = None - _SETSETPOINT.fields_by_name['target']._serialized_options = b'\212\265\030\002\030\004\222?\0028\020' + _SETSETPOINT.fields_by_name['target']._serialized_options = b'\222?\0028\020\212\265\030\002\030\004' _SETSETPOINT.fields_by_name['setting']._options = None - _SETSETPOINT.fields_by_name['setting']._serialized_options = b'\212\265\030\002\010\001\212\265\030\003\020\200 \222?\0028 ' + _SETSETPOINT.fields_by_name['setting']._serialized_options = b'\222?\0028 \212\265\030\005\010\001\020\200 ' _WAITSETPOINT.fields_by_name['target']._options = None - _WAITSETPOINT.fields_by_name['target']._serialized_options = b'\212\265\030\002\030\004\222?\0028\020' + _WAITSETPOINT.fields_by_name['target']._serialized_options = b'\222?\0028\020\212\265\030\002\030\004' _WAITSETPOINT.fields_by_name['precision']._options = None - _WAITSETPOINT.fields_by_name['precision']._serialized_options = b'\212\265\030\002\010\006\212\265\030\003\020\200 \222?\0028 ' + _WAITSETPOINT.fields_by_name['precision']._serialized_options = b'\222?\0028 \212\265\030\005\010\006\020\200 ' _SETDIGITAL.fields_by_name['target']._options = None - _SETDIGITAL.fields_by_name['target']._serialized_options = b'\212\265\030\002\030\006\222?\0028\020' + _SETDIGITAL.fields_by_name['target']._serialized_options = b'\222?\0028\020\212\265\030\002\030\006' _WAITDIGITAL.fields_by_name['target']._options = None - _WAITDIGITAL.fields_by_name['target']._serialized_options = b'\212\265\030\002\030\006\222?\0028\020' + _WAITDIGITAL.fields_by_name['target']._serialized_options = b'\222?\0028\020\212\265\030\002\030\006' _WAITDIGITALSTATE.fields_by_name['target']._options = None - _WAITDIGITALSTATE.fields_by_name['target']._serialized_options = b'\212\265\030\002\030\033\222?\0028\020' + _WAITDIGITALSTATE.fields_by_name['target']._serialized_options = b'\222?\0028\020\212\265\030\002\030\033' _SETPWM.fields_by_name['target']._options = None - _SETPWM.fields_by_name['target']._serialized_options = b'\212\265\030\002\030\005\222?\0028\020' + _SETPWM.fields_by_name['target']._serialized_options = b'\222?\0028\020\212\265\030\002\030\005' _SETPWM.fields_by_name['setting']._options = None - _SETPWM.fields_by_name['setting']._serialized_options = b'\212\265\030\003\020\200 \222?\0028 ' + _SETPWM.fields_by_name['setting']._serialized_options = b'\222?\0028 \212\265\030\003\020\200 ' _TARGETPROFILE.fields_by_name['target']._options = None - _TARGETPROFILE.fields_by_name['target']._serialized_options = b'\212\265\030\003\030\267\002\222?\0028\020' + _TARGETPROFILE.fields_by_name['target']._serialized_options = b'\222?\0028\020\212\265\030\003\030\267\002' _TARGETSEQUENCE.fields_by_name['target']._options = None - _TARGETSEQUENCE.fields_by_name['target']._serialized_options = b'\212\265\030\003\030\306\002\222?\0028\020' + _TARGETSEQUENCE.fields_by_name['target']._serialized_options = b'\222?\0028\020\212\265\030\003\030\306\002' _INSTRUCTION._options = None _INSTRUCTION._serialized_options = b'\222?\003\260\001\001' _BLOCK.fields_by_name['enabled']._options = None @@ -236,53 +73,53 @@ _BLOCK.fields_by_name['error']._options = None _BLOCK.fields_by_name['error']._serialized_options = b'\212\265\030\002(\001' _BLOCK.fields_by_name['elapsed']._options = None - _BLOCK.fields_by_name['elapsed']._serialized_options = b'\222?\0028 \212\265\030\002(\001\212\265\030\0020\001\212\265\030\002\010\003\212\265\030\003\020\350\007' + _BLOCK.fields_by_name['elapsed']._serialized_options = b'\222?\0028 \212\265\030\t\010\003\020\350\007(\0010\001' _BLOCK.fields_by_name['activeInstructionStartedAt']._options = None - _BLOCK.fields_by_name['activeInstructionStartedAt']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['activeInstructionStartedAt']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK.fields_by_name['disabledAt']._options = None - _BLOCK.fields_by_name['disabledAt']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['disabledAt']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK.fields_by_name['disabledDuration']._options = None - _BLOCK.fields_by_name['disabledDuration']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['disabledDuration']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\306\002\212\265\030\002H\017' - _SEQUENCESTATUS._serialized_start=2827 - _SEQUENCESTATUS._serialized_end=2935 - _SEQUENCEERROR._serialized_start=2938 - _SEQUENCEERROR._serialized_end=3078 - _COMMENT._serialized_start=78 - _COMMENT._serialized_end=101 - _RESTART._serialized_start=103 - _RESTART._serialized_end=112 - _ENABLEDISABLE._serialized_start=114 - _ENABLEDISABLE._serialized_end=158 - _WAIT._serialized_start=160 - _WAIT._serialized_end=166 - _WAITDURATION._serialized_start=168 - _WAITDURATION._serialized_end=213 - _WAITUNTIL._serialized_start=215 - _WAITUNTIL._serialized_end=253 - _WAITTEMPERATURERANGE._serialized_start=255 - _WAITTEMPERATURERANGE._serialized_end=376 - _WAITTEMPERATUREBOUNDARY._serialized_start=378 - _WAITTEMPERATUREBOUNDARY._serialized_end=467 - _SETSETPOINT._serialized_start=469 - _SETSETPOINT._serialized_end=548 - _WAITSETPOINT._serialized_start=550 - _WAITSETPOINT._serialized_end=632 - _SETDIGITAL._serialized_start=634 - _SETDIGITAL._serialized_end=720 - _WAITDIGITAL._serialized_start=722 - _WAITDIGITAL._serialized_end=764 - _WAITDIGITALSTATE._serialized_start=766 - _WAITDIGITALSTATE._serialized_end=856 - _SETPWM._serialized_start=858 - _SETPWM._serialized_end=926 - _TARGETPROFILE._serialized_start=928 - _TARGETPROFILE._serialized_end=973 - _TARGETSEQUENCE._serialized_start=975 - _TARGETSEQUENCE._serialized_end=1021 - _INSTRUCTION._serialized_start=1024 - _INSTRUCTION._serialized_end=2384 - _BLOCK._serialized_start=2387 - _BLOCK._serialized_end=2825 + _BLOCK._serialized_options = b'\212\265\030\006\030\306\002J\001\017' + _globals['_SEQUENCESTATUS']._serialized_start=2792 + _globals['_SEQUENCESTATUS']._serialized_end=2900 + _globals['_SEQUENCEERROR']._serialized_start=2903 + _globals['_SEQUENCEERROR']._serialized_end=3043 + _globals['_COMMENT']._serialized_start=78 + _globals['_COMMENT']._serialized_end=101 + _globals['_RESTART']._serialized_start=103 + _globals['_RESTART']._serialized_end=112 + _globals['_ENABLEDISABLE']._serialized_start=114 + _globals['_ENABLEDISABLE']._serialized_end=158 + _globals['_WAIT']._serialized_start=160 + _globals['_WAIT']._serialized_end=166 + _globals['_WAITDURATION']._serialized_start=168 + _globals['_WAITDURATION']._serialized_end=213 + _globals['_WAITUNTIL']._serialized_start=215 + _globals['_WAITUNTIL']._serialized_end=253 + _globals['_WAITTEMPERATURERANGE']._serialized_start=255 + _globals['_WAITTEMPERATURERANGE']._serialized_end=368 + _globals['_WAITTEMPERATUREBOUNDARY']._serialized_start=370 + _globals['_WAITTEMPERATUREBOUNDARY']._serialized_end=455 + _globals['_SETSETPOINT']._serialized_start=457 + _globals['_SETSETPOINT']._serialized_end=532 + _globals['_WAITSETPOINT']._serialized_start=534 + _globals['_WAITSETPOINT']._serialized_end=612 + _globals['_SETDIGITAL']._serialized_start=614 + _globals['_SETDIGITAL']._serialized_end=700 + _globals['_WAITDIGITAL']._serialized_start=702 + _globals['_WAITDIGITAL']._serialized_end=744 + _globals['_WAITDIGITALSTATE']._serialized_start=746 + _globals['_WAITDIGITALSTATE']._serialized_end=836 + _globals['_SETPWM']._serialized_start=838 + _globals['_SETPWM']._serialized_end=906 + _globals['_TARGETPROFILE']._serialized_start=908 + _globals['_TARGETPROFILE']._serialized_end=953 + _globals['_TARGETSEQUENCE']._serialized_start=955 + _globals['_TARGETSEQUENCE']._serialized_end=1001 + _globals['_INSTRUCTION']._serialized_start=1004 + _globals['_INSTRUCTION']._serialized_end=2364 + _globals['_BLOCK']._serialized_start=2367 + _globals['_BLOCK']._serialized_end=2790 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/SetpointProfile_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/SetpointProfile_pb2.py index d02b710b..d94e06e1 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/SetpointProfile_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/SetpointProfile_pb2.py @@ -4,9 +4,8 @@ """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -16,45 +15,30 @@ import nanopb_pb2 as nanopb__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15SetpointProfile.proto\x12\x14\x62lox.SetpointProfile\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"b\n\x05Point\x12\x19\n\x04time\x18\x01 \x01(\rB\x0b\x8a\xb5\x18\x02\x08\x03\x92?\x02\x38 \x12)\n\x0btemperature\x18\x02 \x01(\x05\x42\x12\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 H\x00\x42\x13\n\x11temperature_oneof\"\xe6\x01\n\x05\x42lock\x12+\n\x06points\x18\x01 \x03(\x0b\x32\x1b.blox.SetpointProfile.Point\x12\x0f\n\x07\x65nabled\x18\x03 \x01(\x08\x12\x1e\n\x08targetId\x18\x04 \x01(\rB\x0c\x8a\xb5\x18\x03\x18\xaf\x02\x92?\x02\x38\x10\x12/\n\x07setting\x18\x05 \x01(\x11\x42\x1e\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x12\x1a\n\x05start\x18\x06 \x01(\rB\x0b\x8a\xb5\x18\x02X\x01\x92?\x02\x38 \x12#\n\x0e\x64rivenTargetId\x18Z \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03:\r\x8a\xb5\x18\x03\x18\xb7\x02\x8a\xb5\x18\x02H\x0f\x62\x06proto3') - - - -_POINT = DESCRIPTOR.message_types_by_name['Point'] -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Point = _reflection.GeneratedProtocolMessageType('Point', (_message.Message,), { - 'DESCRIPTOR' : _POINT, - '__module__' : 'SetpointProfile_pb2' - # @@protoc_insertion_point(class_scope:blox.SetpointProfile.Point) - }) -_sym_db.RegisterMessage(Point) - -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'SetpointProfile_pb2' - # @@protoc_insertion_point(class_scope:blox.SetpointProfile.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15SetpointProfile.proto\x12\x14\x62lox.SetpointProfile\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"^\n\x05Point\x12\x19\n\x04time\x18\x01 \x01(\rB\x0b\x92?\x02\x38 \x8a\xb5\x18\x02\x08\x03\x12%\n\x0btemperature\x18\x02 \x01(\x05\x42\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x08\x01\x10\x80 H\x00\x42\x13\n\x11temperature_oneof\"\xd7\x01\n\x05\x42lock\x12+\n\x06points\x18\x01 \x03(\x0b\x32\x1b.blox.SetpointProfile.Point\x12\x0f\n\x07\x65nabled\x18\x03 \x01(\x08\x12\x1e\n\x08targetId\x18\x04 \x01(\rB\x0c\x92?\x02\x38\x10\x8a\xb5\x18\x03\x18\xaf\x02\x12#\n\x07setting\x18\x05 \x01(\x11\x42\x12\x92?\x02\x38 \x8a\xb5\x18\t\x08\x01\x10\x80 (\x01\x30\x01\x12\x1a\n\x05start\x18\x06 \x01(\rB\x0b\x92?\x02\x38 \x8a\xb5\x18\x02X\x01\x12#\n\x0e\x64rivenTargetId\x18Z \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01:\n\x8a\xb5\x18\x06\x18\xb7\x02J\x01\x0f\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'SetpointProfile_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _POINT.fields_by_name['time']._options = None - _POINT.fields_by_name['time']._serialized_options = b'\212\265\030\002\010\003\222?\0028 ' + _POINT.fields_by_name['time']._serialized_options = b'\222?\0028 \212\265\030\002\010\003' _POINT.fields_by_name['temperature']._options = None - _POINT.fields_by_name['temperature']._serialized_options = b'\212\265\030\002\010\001\212\265\030\003\020\200 \222?\0028 ' + _POINT.fields_by_name['temperature']._serialized_options = b'\222?\0028 \212\265\030\005\010\001\020\200 ' _BLOCK.fields_by_name['targetId']._options = None - _BLOCK.fields_by_name['targetId']._serialized_options = b'\212\265\030\003\030\257\002\222?\0028\020' + _BLOCK.fields_by_name['targetId']._serialized_options = b'\222?\0028\020\212\265\030\003\030\257\002' _BLOCK.fields_by_name['setting']._options = None - _BLOCK.fields_by_name['setting']._serialized_options = b'\212\265\030\0020\001\212\265\030\002\010\001\212\265\030\003\020\200 \222?\0028 \212\265\030\002(\001' + _BLOCK.fields_by_name['setting']._serialized_options = b'\222?\0028 \212\265\030\t\010\001\020\200 (\0010\001' _BLOCK.fields_by_name['start']._options = None - _BLOCK.fields_by_name['start']._serialized_options = b'\212\265\030\002X\001\222?\0028 ' + _BLOCK.fields_by_name['start']._serialized_options = b'\222?\0028 \212\265\030\002X\001' _BLOCK.fields_by_name['drivenTargetId']._options = None - _BLOCK.fields_by_name['drivenTargetId']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['drivenTargetId']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\267\002\212\265\030\002H\017' - _POINT._serialized_start=77 - _POINT._serialized_end=175 - _BLOCK._serialized_start=178 - _BLOCK._serialized_end=408 + _BLOCK._serialized_options = b'\212\265\030\006\030\267\002J\001\017' + _globals['_POINT']._serialized_start=77 + _globals['_POINT']._serialized_end=171 + _globals['_BLOCK']._serialized_start=174 + _globals['_BLOCK']._serialized_end=389 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/SetpointSensorPair_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/SetpointSensorPair_pb2.py index 3a7bb499..c5da9b54 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/SetpointSensorPair_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/SetpointSensorPair_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: SetpointSensorPair.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -18,52 +16,36 @@ import Claims_pb2 as Claims__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18SetpointSensorPair.proto\x12\x17\x62lox.SetpointSensorPair\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\x0c\x43laims.proto\"\xd4\x04\n\x05\x42lock\x12\x0f\n\x07\x65nabled\x18\x07 \x01(\x08\x12\x1d\n\x08sensorId\x18\x02 \x01(\rB\x0b\x8a\xb5\x18\x02\x18\x02\x92?\x02\x38\x10\x12/\n\rstoredSetting\x18\x08 \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12\x36\n\x0e\x64\x65siredSetting\x18\x0f \x01(\x11\x42\x1e\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x12/\n\x07setting\x18\x05 \x01(\x11\x42\x1e\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x12-\n\x05value\x18\x06 \x01(\x11\x42\x1e\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x12\x37\n\x0fvalueUnfiltered\x18\x0b \x01(\x11\x42\x1e\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x12\x35\n\x06\x66ilter\x18\t \x01(\x0e\x32%.blox.SetpointSensorPair.FilterChoice\x12+\n\x0f\x66ilterThreshold\x18\n \x01(\x11\x42\x12\x8a\xb5\x18\x02\x08\x06\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12\x13\n\x0bresetFilter\x18\x0c \x01(\x08\x12%\n\tclaimedBy\x18\r \x01(\rB\x12\x8a\xb5\x18\x03\x18\xff\x01\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x10\x12-\n\x0bsettingMode\x18\x0e \x01(\x0e\x32\x18.blox.Claims.SettingMode\x12#\n\x0esettingEnabled\x18Z \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03:%\x8a\xb5\x18\x03\x18\xaf\x02\x8a\xb5\x18\x02H\x01\x8a\xb5\x18\x02H\x14\x8a\xb5\x18\x02H\x04\x8a\xb5\x18\x02H\x0f\x8a\xb5\x18\x02H\x10*~\n\x0c\x46ilterChoice\x12\x0f\n\x0b\x46ILTER_NONE\x10\x00\x12\x0e\n\nFILTER_15s\x10\x01\x12\x0e\n\nFILTER_45s\x10\x02\x12\x0e\n\nFILTER_90s\x10\x03\x12\r\n\tFILTER_3m\x10\x04\x12\x0e\n\nFILTER_10m\x10\x05\x12\x0e\n\nFILTER_30m\x10\x06\x62\x06proto3') - -_FILTERCHOICE = DESCRIPTOR.enum_types_by_name['FilterChoice'] -FilterChoice = enum_type_wrapper.EnumTypeWrapper(_FILTERCHOICE) -FILTER_NONE = 0 -FILTER_15s = 1 -FILTER_45s = 2 -FILTER_90s = 3 -FILTER_3m = 4 -FILTER_10m = 5 -FILTER_30m = 6 - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'SetpointSensorPair_pb2' - # @@protoc_insertion_point(class_scope:blox.SetpointSensorPair.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18SetpointSensorPair.proto\x12\x17\x62lox.SetpointSensorPair\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\x0c\x43laims.proto\"\xfd\x03\n\x05\x42lock\x12\x0f\n\x07\x65nabled\x18\x07 \x01(\x08\x12\x1d\n\x08sensorId\x18\x02 \x01(\rB\x0b\x92?\x02\x38\x10\x8a\xb5\x18\x02\x18\x02\x12\'\n\rstoredSetting\x18\x08 \x01(\x11\x42\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x08\x01\x10\x80 0\x01\x12*\n\x0e\x64\x65siredSetting\x18\x0f \x01(\x11\x42\x12\x92?\x02\x38 \x8a\xb5\x18\t\x08\x01\x10\x80 (\x01\x30\x01\x12#\n\x07setting\x18\x05 \x01(\x11\x42\x12\x92?\x02\x38 \x8a\xb5\x18\t\x08\x01\x10\x80 (\x01\x30\x01\x12!\n\x05value\x18\x06 \x01(\x11\x42\x12\x92?\x02\x38 \x8a\xb5\x18\t\x08\x01\x10\x80 (\x01\x30\x01\x12+\n\x0fvalueUnfiltered\x18\x0b \x01(\x11\x42\x12\x92?\x02\x38 \x8a\xb5\x18\t\x08\x01\x10\x80 (\x01\x30\x01\x12\x35\n\x06\x66ilter\x18\t \x01(\x0e\x32%.blox.SetpointSensorPair.FilterChoice\x12\'\n\x0f\x66ilterThreshold\x18\n \x01(\x11\x42\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x08\x06\x10\x80 \x12\x13\n\x0bresetFilter\x18\x0c \x01(\x08\x12!\n\tclaimedBy\x18\r \x01(\rB\x0e\x92?\x02\x38\x10\x8a\xb5\x18\x05\x18\xff\x01(\x01\x12-\n\x0bsettingMode\x18\x0e \x01(\x0e\x32\x18.blox.Claims.SettingMode\x12#\n\x0esettingEnabled\x18Z \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01:\x0e\x8a\xb5\x18\n\x18\xaf\x02J\x05\x01\x14\x04\x0f\x10*~\n\x0c\x46ilterChoice\x12\x0f\n\x0b\x46ILTER_NONE\x10\x00\x12\x0e\n\nFILTER_15s\x10\x01\x12\x0e\n\nFILTER_45s\x10\x02\x12\x0e\n\nFILTER_90s\x10\x03\x12\r\n\tFILTER_3m\x10\x04\x12\x0e\n\nFILTER_10m\x10\x05\x12\x0e\n\nFILTER_30m\x10\x06\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'SetpointSensorPair_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _BLOCK.fields_by_name['sensorId']._options = None - _BLOCK.fields_by_name['sensorId']._serialized_options = b'\212\265\030\002\030\002\222?\0028\020' + _BLOCK.fields_by_name['sensorId']._serialized_options = b'\222?\0028\020\212\265\030\002\030\002' _BLOCK.fields_by_name['storedSetting']._options = None - _BLOCK.fields_by_name['storedSetting']._serialized_options = b'\212\265\030\0020\001\212\265\030\002\010\001\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['storedSetting']._serialized_options = b'\222?\0028 \212\265\030\007\010\001\020\200 0\001' _BLOCK.fields_by_name['desiredSetting']._options = None - _BLOCK.fields_by_name['desiredSetting']._serialized_options = b'\212\265\030\0020\001\212\265\030\002\010\001\212\265\030\003\020\200 \222?\0028 \212\265\030\002(\001' + _BLOCK.fields_by_name['desiredSetting']._serialized_options = b'\222?\0028 \212\265\030\t\010\001\020\200 (\0010\001' _BLOCK.fields_by_name['setting']._options = None - _BLOCK.fields_by_name['setting']._serialized_options = b'\212\265\030\0020\001\212\265\030\002\010\001\212\265\030\003\020\200 \222?\0028 \212\265\030\002(\001' + _BLOCK.fields_by_name['setting']._serialized_options = b'\222?\0028 \212\265\030\t\010\001\020\200 (\0010\001' _BLOCK.fields_by_name['value']._options = None - _BLOCK.fields_by_name['value']._serialized_options = b'\212\265\030\0020\001\212\265\030\002\010\001\212\265\030\003\020\200 \222?\0028 \212\265\030\002(\001' + _BLOCK.fields_by_name['value']._serialized_options = b'\222?\0028 \212\265\030\t\010\001\020\200 (\0010\001' _BLOCK.fields_by_name['valueUnfiltered']._options = None - _BLOCK.fields_by_name['valueUnfiltered']._serialized_options = b'\212\265\030\0020\001\212\265\030\002\010\001\212\265\030\003\020\200 \222?\0028 \212\265\030\002(\001' + _BLOCK.fields_by_name['valueUnfiltered']._serialized_options = b'\222?\0028 \212\265\030\t\010\001\020\200 (\0010\001' _BLOCK.fields_by_name['filterThreshold']._options = None - _BLOCK.fields_by_name['filterThreshold']._serialized_options = b'\212\265\030\002\010\006\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['filterThreshold']._serialized_options = b'\222?\0028 \212\265\030\005\010\006\020\200 ' _BLOCK.fields_by_name['claimedBy']._options = None - _BLOCK.fields_by_name['claimedBy']._serialized_options = b'\212\265\030\003\030\377\001\212\265\030\002(\001\222?\0028\020' + _BLOCK.fields_by_name['claimedBy']._serialized_options = b'\222?\0028\020\212\265\030\005\030\377\001(\001' _BLOCK.fields_by_name['settingEnabled']._options = None - _BLOCK.fields_by_name['settingEnabled']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['settingEnabled']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\257\002\212\265\030\002H\001\212\265\030\002H\024\212\265\030\002H\004\212\265\030\002H\017\212\265\030\002H\020' - _FILTERCHOICE._serialized_start=696 - _FILTERCHOICE._serialized_end=822 - _BLOCK._serialized_start=98 - _BLOCK._serialized_end=694 + _BLOCK._serialized_options = b'\212\265\030\n\030\257\002J\005\001\024\004\017\020' + _globals['_FILTERCHOICE']._serialized_start=609 + _globals['_FILTERCHOICE']._serialized_end=735 + _globals['_BLOCK']._serialized_start=98 + _globals['_BLOCK']._serialized_end=607 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/Spark2Pins_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/Spark2Pins_pb2.py index 644cb527..f98eaa05 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/Spark2Pins_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/Spark2Pins_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: Spark2Pins.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -18,45 +16,26 @@ import IoArray_pb2 as IoArray__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10Spark2Pins.proto\x12\x0f\x62lox.Spark2Pins\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\rIoArray.proto\"\xb7\x01\n\x05\x42lock\x12\x12\n\nsoundAlarm\x18\x05 \x01(\x08\x12\x33\n\x08hardware\x18\x08 \x01(\x0e\x32\x19.blox.Spark2Pins.HardwareB\x06\x8a\xb5\x18\x02(\x01\x12;\n\x08\x63hannels\x18\t \x03(\x0b\x32\x17.blox.IoArray.IoChannelB\x10\x92?\x02\x10\x04\x92?\x02x\x01\x8a\xb5\x18\x02(\x01\x12\x19\n\x04pins\x18Z \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03:\r\x8a\xb5\x18\x03\x18\xc0\x02\x8a\xb5\x18\x02H\n*\x85\x01\n\tChannelId\x12\x14\n\x10SPARK2_CHAN_NONE\x10\x00\x12\x17\n\x13SPARK2_CHAN_BOTTOM1\x10\x01\x12\x17\n\x13SPARK2_CHAN_BOTTOM2\x10\x02\x12\x17\n\x13SPARK2_CHAN_BOTTOM3\x10\x03\x12\x17\n\x13SPARK2_CHAN_BOTTOM0\x10\x04*8\n\x08Hardware\x12\x0e\n\nHW_UNKNOWN\x10\x00\x12\r\n\tHW_SPARK1\x10\x01\x12\r\n\tHW_SPARK2\x10\x02\x62\x06proto3') - -_CHANNELID = DESCRIPTOR.enum_types_by_name['ChannelId'] -ChannelId = enum_type_wrapper.EnumTypeWrapper(_CHANNELID) -_HARDWARE = DESCRIPTOR.enum_types_by_name['Hardware'] -Hardware = enum_type_wrapper.EnumTypeWrapper(_HARDWARE) -SPARK2_CHAN_NONE = 0 -SPARK2_CHAN_BOTTOM1 = 1 -SPARK2_CHAN_BOTTOM2 = 2 -SPARK2_CHAN_BOTTOM3 = 3 -SPARK2_CHAN_BOTTOM0 = 4 -HW_UNKNOWN = 0 -HW_SPARK1 = 1 -HW_SPARK2 = 2 - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'Spark2Pins_pb2' - # @@protoc_insertion_point(class_scope:blox.Spark2Pins.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10Spark2Pins.proto\x12\x0f\x62lox.Spark2Pins\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\rIoArray.proto\"\xb1\x01\n\x05\x42lock\x12\x12\n\nsoundAlarm\x18\x05 \x01(\x08\x12\x33\n\x08hardware\x18\x08 \x01(\x0e\x32\x19.blox.Spark2Pins.HardwareB\x06\x8a\xb5\x18\x02(\x01\x12\x38\n\x08\x63hannels\x18\t \x03(\x0b\x32\x17.blox.IoArray.IoChannelB\r\x92?\x04\x10\x04x\x01\x8a\xb5\x18\x02(\x01\x12\x19\n\x04pins\x18Z \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01:\n\x8a\xb5\x18\x06\x18\xc0\x02J\x01\n*\x85\x01\n\tChannelId\x12\x14\n\x10SPARK2_CHAN_NONE\x10\x00\x12\x17\n\x13SPARK2_CHAN_BOTTOM1\x10\x01\x12\x17\n\x13SPARK2_CHAN_BOTTOM2\x10\x02\x12\x17\n\x13SPARK2_CHAN_BOTTOM3\x10\x03\x12\x17\n\x13SPARK2_CHAN_BOTTOM0\x10\x04*8\n\x08Hardware\x12\x0e\n\nHW_UNKNOWN\x10\x00\x12\r\n\tHW_SPARK1\x10\x01\x12\r\n\tHW_SPARK2\x10\x02\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Spark2Pins_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _BLOCK.fields_by_name['hardware']._options = None _BLOCK.fields_by_name['hardware']._serialized_options = b'\212\265\030\002(\001' _BLOCK.fields_by_name['channels']._options = None - _BLOCK.fields_by_name['channels']._serialized_options = b'\222?\002\020\004\222?\002x\001\212\265\030\002(\001' + _BLOCK.fields_by_name['channels']._serialized_options = b'\222?\004\020\004x\001\212\265\030\002(\001' _BLOCK.fields_by_name['pins']._options = None - _BLOCK.fields_by_name['pins']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['pins']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\300\002\212\265\030\002H\n' - _CHANNELID._serialized_start=269 - _CHANNELID._serialized_end=402 - _HARDWARE._serialized_start=404 - _HARDWARE._serialized_end=460 - _BLOCK._serialized_start=83 - _BLOCK._serialized_end=266 + _BLOCK._serialized_options = b'\212\265\030\006\030\300\002J\001\n' + _globals['_CHANNELID']._serialized_start=263 + _globals['_CHANNELID']._serialized_end=396 + _globals['_HARDWARE']._serialized_start=398 + _globals['_HARDWARE']._serialized_end=454 + _globals['_BLOCK']._serialized_start=83 + _globals['_BLOCK']._serialized_end=260 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/Spark3Pins_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/Spark3Pins_pb2.py index f0835971..2f429b01 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/Spark3Pins_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/Spark3Pins_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: Spark3Pins.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -18,41 +16,26 @@ import IoArray_pb2 as IoArray__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10Spark3Pins.proto\x12\x0f\x62lox.Spark3Pins\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\rIoArray.proto\"\x84\x02\n\x05\x42lock\x12\x18\n\x10\x65nableIoSupply5V\x18\x02 \x01(\x08\x12\x19\n\x11\x65nableIoSupply12V\x18\x03 \x01(\x08\x12\x12\n\nsoundAlarm\x18\x05 \x01(\x08\x12$\n\x08voltage5\x18\x06 \x01(\rB\x12\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x10\x8a\xb5\x18\x03\x10\xe8\x07\x12%\n\tvoltage12\x18\x07 \x01(\rB\x12\x8a\xb5\x18\x02(\x01\x92?\x02\x38\x10\x8a\xb5\x18\x03\x10\xe8\x07\x12;\n\x08\x63hannels\x18\x08 \x03(\x0b\x32\x17.blox.IoArray.IoChannelB\x10\x92?\x02\x10\x05\x92?\x02x\x01\x8a\xb5\x18\x02(\x01\x12\x19\n\x04pins\x18Z \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03:\r\x8a\xb5\x18\x03\x18\xbf\x02\x8a\xb5\x18\x02H\n*\x92\x01\n\tChannelId\x12\x11\n\rSPARK3_NO_PIN\x10\x00\x12\x14\n\x10SPARK3_CHAN_TOP1\x10\x01\x12\x14\n\x10SPARK3_CHAN_TOP2\x10\x02\x12\x14\n\x10SPARK3_CHAN_TOP3\x10\x03\x12\x17\n\x13SPARK3_CHAN_BOTTOM1\x10\x04\x12\x17\n\x13SPARK3_CHAN_BOTTOM2\x10\x05\x62\x06proto3') - -_CHANNELID = DESCRIPTOR.enum_types_by_name['ChannelId'] -ChannelId = enum_type_wrapper.EnumTypeWrapper(_CHANNELID) -SPARK3_NO_PIN = 0 -SPARK3_CHAN_TOP1 = 1 -SPARK3_CHAN_TOP2 = 2 -SPARK3_CHAN_TOP3 = 3 -SPARK3_CHAN_BOTTOM1 = 4 -SPARK3_CHAN_BOTTOM2 = 5 - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'Spark3Pins_pb2' - # @@protoc_insertion_point(class_scope:blox.Spark3Pins.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10Spark3Pins.proto\x12\x0f\x62lox.Spark3Pins\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\x1a\rIoArray.proto\"\xf6\x01\n\x05\x42lock\x12\x18\n\x10\x65nableIoSupply5V\x18\x02 \x01(\x08\x12\x19\n\x11\x65nableIoSupply12V\x18\x03 \x01(\x08\x12\x12\n\nsoundAlarm\x18\x05 \x01(\x08\x12 \n\x08voltage5\x18\x06 \x01(\rB\x0e\x92?\x02\x38\x10\x8a\xb5\x18\x05\x10\xe8\x07(\x01\x12!\n\tvoltage12\x18\x07 \x01(\rB\x0e\x92?\x02\x38\x10\x8a\xb5\x18\x05\x10\xe8\x07(\x01\x12\x38\n\x08\x63hannels\x18\x08 \x03(\x0b\x32\x17.blox.IoArray.IoChannelB\r\x92?\x04\x10\x05x\x01\x8a\xb5\x18\x02(\x01\x12\x19\n\x04pins\x18Z \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01:\n\x8a\xb5\x18\x06\x18\xbf\x02J\x01\n*\x92\x01\n\tChannelId\x12\x11\n\rSPARK3_NO_PIN\x10\x00\x12\x14\n\x10SPARK3_CHAN_TOP1\x10\x01\x12\x14\n\x10SPARK3_CHAN_TOP2\x10\x02\x12\x14\n\x10SPARK3_CHAN_TOP3\x10\x03\x12\x17\n\x13SPARK3_CHAN_BOTTOM1\x10\x04\x12\x17\n\x13SPARK3_CHAN_BOTTOM2\x10\x05\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Spark3Pins_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _BLOCK.fields_by_name['voltage5']._options = None - _BLOCK.fields_by_name['voltage5']._serialized_options = b'\212\265\030\002(\001\222?\0028\020\212\265\030\003\020\350\007' + _BLOCK.fields_by_name['voltage5']._serialized_options = b'\222?\0028\020\212\265\030\005\020\350\007(\001' _BLOCK.fields_by_name['voltage12']._options = None - _BLOCK.fields_by_name['voltage12']._serialized_options = b'\212\265\030\002(\001\222?\0028\020\212\265\030\003\020\350\007' + _BLOCK.fields_by_name['voltage12']._serialized_options = b'\222?\0028\020\212\265\030\005\020\350\007(\001' _BLOCK.fields_by_name['channels']._options = None - _BLOCK.fields_by_name['channels']._serialized_options = b'\222?\002\020\005\222?\002x\001\212\265\030\002(\001' + _BLOCK.fields_by_name['channels']._serialized_options = b'\222?\004\020\005x\001\212\265\030\002(\001' _BLOCK.fields_by_name['pins']._options = None - _BLOCK.fields_by_name['pins']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['pins']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\277\002\212\265\030\002H\n' - _CHANNELID._serialized_start=346 - _CHANNELID._serialized_end=492 - _BLOCK._serialized_start=83 - _BLOCK._serialized_end=343 + _BLOCK._serialized_options = b'\212\265\030\006\030\277\002J\001\n' + _globals['_CHANNELID']._serialized_start=332 + _globals['_CHANNELID']._serialized_end=478 + _globals['_BLOCK']._serialized_start=83 + _globals['_BLOCK']._serialized_end=329 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/SysInfo_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/SysInfo_pb2.py index afbcbe47..202fd380 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/SysInfo_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/SysInfo_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: SysInfo.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -17,77 +15,58 @@ import nanopb_pb2 as nanopb__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rSysInfo.proto\x12\x0c\x62lox.SysInfo\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"\x8f\x06\n\x05\x42lock\x12#\n\x08\x64\x65viceId\x18\x01 \x01(\x0c\x42\x11\x8a\xb5\x18\x02(\x01\x92?\x02\x08\x0c\x8a\xb5\x18\x02\x38\x01\x12\x1c\n\x07version\x18\x02 \x01(\tB\x0b\x8a\xb5\x18\x02(\x01\x92?\x02\x08\x0c\x12\x30\n\x08platform\x18\x03 \x01(\x0e\x32\x16.blox.SysInfo.PlatformB\x06\x8a\xb5\x18\x02(\x01\x12$\n\x0fprotocolVersion\x18\x07 \x01(\tB\x0b\x8a\xb5\x18\x02(\x01\x92?\x02\x08\x0c\x12 \n\x0breleaseDate\x18\x08 \x01(\tB\x0b\x8a\xb5\x18\x02(\x01\x92?\x02\x08\x0c\x12!\n\x0cprotocolDate\x18\t \x01(\tB\x0b\x8a\xb5\x18\x02(\x01\x92?\x02\x08\x0c\x12\x1d\n\x02ip\x18\n \x01(\rB\x11\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x02`\x01\x92?\x02\x38 \x12.\n\x06uptime\x18\x0b \x01(\rB\x1e\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02\x08\x03\x8a\xb5\x18\x03\x10\xe8\x07\x92?\x02\x38 \x12\x32\n\x10updatesPerSecond\x18\x0c \x01(\rB\x18\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x03\x10\xe8\x07\x92?\x02\x38 \x12\x1f\n\nsystemTime\x18\r \x01(\rB\x0b\x8a\xb5\x18\x02X\x01\x92?\x02\x38 \x12\x17\n\x08timeZone\x18\x0e \x01(\tB\x05\x92?\x02\x08 \x12/\n\x08tempUnit\x18\x0f \x01(\x0e\x32\x1d.blox.SysInfo.TemperatureUnit\x12 \n\x11\x64isplayBrightness\x18\x10 \x01(\rB\x05\x92?\x02\x38\x08\x12*\n\x08voltage5\x18\x11 \x01(\rB\x18\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x02\x30\x01\x92?\x02\x38\x10\x8a\xb5\x18\x03\x10\xe8\x07\x12\x31\n\x0fvoltageExternal\x18\x12 \x01(\rB\x18\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x02\x30\x01\x92?\x02\x38\x10\x8a\xb5\x18\x03\x10\xe8\x07\x12 \n\nmemoryFree\x18\x13 \x01(\rB\x0c\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x02\x30\x01\x12*\n\x14memoryFreeContiguous\x18\x14 \x01(\rB\x0c\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x02\x30\x01\x12&\n\x10memoryFreeLowest\x18\x15 \x01(\rB\x0c\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x02\x30\x01\x12\x1c\n\x07\x63ommand\x18Z \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03\x12\x1a\n\x05trace\x18[ \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03:\x07\x8a\xb5\x18\x03\x18\x80\x02*}\n\x08Platform\x12\x14\n\x10PLATFORM_UNKNOWN\x10\x00\x12\x10\n\x0cPLATFORM_GCC\x10\x03\x12\x13\n\x0fPLATFORM_PHOTON\x10\x06\x12\x0f\n\x0bPLATFORM_P1\x10\x08\x12\x10\n\x0cPLATFORM_ESP\x10\x64\x12\x11\n\x0cPLATFORM_SIM\x10\xc8\x01*8\n\x0fTemperatureUnit\x12\x10\n\x0cTEMP_CELSIUS\x10\x00\x12\x13\n\x0fTEMP_FAHRENHEIT\x10\x01\x62\x06proto3') - -_PLATFORM = DESCRIPTOR.enum_types_by_name['Platform'] -Platform = enum_type_wrapper.EnumTypeWrapper(_PLATFORM) -_TEMPERATUREUNIT = DESCRIPTOR.enum_types_by_name['TemperatureUnit'] -TemperatureUnit = enum_type_wrapper.EnumTypeWrapper(_TEMPERATUREUNIT) -PLATFORM_UNKNOWN = 0 -PLATFORM_GCC = 3 -PLATFORM_PHOTON = 6 -PLATFORM_P1 = 8 -PLATFORM_ESP = 100 -PLATFORM_SIM = 200 -TEMP_CELSIUS = 0 -TEMP_FAHRENHEIT = 1 - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'SysInfo_pb2' - # @@protoc_insertion_point(class_scope:blox.SysInfo.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rSysInfo.proto\x12\x0c\x62lox.SysInfo\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"\xd7\x05\n\x05\x42lock\x12\x1f\n\x08\x64\x65viceId\x18\x01 \x01(\x0c\x42\r\x92?\x02\x08\x0c\x8a\xb5\x18\x04(\x01\x38\x01\x12\x1c\n\x07version\x18\x02 \x01(\tB\x0b\x92?\x02\x08\x0c\x8a\xb5\x18\x02(\x01\x12\x30\n\x08platform\x18\x03 \x01(\x0e\x32\x16.blox.SysInfo.PlatformB\x06\x8a\xb5\x18\x02(\x01\x12$\n\x0fprotocolVersion\x18\x07 \x01(\tB\x0b\x92?\x02\x08\x0c\x8a\xb5\x18\x02(\x01\x12 \n\x0breleaseDate\x18\x08 \x01(\tB\x0b\x92?\x02\x08\x0c\x8a\xb5\x18\x02(\x01\x12!\n\x0cprotocolDate\x18\t \x01(\tB\x0b\x92?\x02\x08\x0c\x8a\xb5\x18\x02(\x01\x12\x19\n\x02ip\x18\n \x01(\rB\r\x92?\x02\x38 \x8a\xb5\x18\x04(\x01`\x01\x12\"\n\x06uptime\x18\x0b \x01(\rB\x12\x92?\x02\x38 \x8a\xb5\x18\t\x08\x03\x10\xe8\x07(\x01\x30\x01\x12*\n\x10updatesPerSecond\x18\x0c \x01(\rB\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x10\xe8\x07(\x01\x30\x01\x12\x1f\n\nsystemTime\x18\r \x01(\rB\x0b\x92?\x02\x38 \x8a\xb5\x18\x02X\x01\x12\x17\n\x08timeZone\x18\x0e \x01(\tB\x05\x92?\x02\x08 \x12/\n\x08tempUnit\x18\x0f \x01(\x0e\x32\x1d.blox.SysInfo.TemperatureUnit\x12 \n\x11\x64isplayBrightness\x18\x10 \x01(\rB\x05\x92?\x02\x38\x08\x12\"\n\x08voltage5\x18\x11 \x01(\rB\x10\x92?\x02\x38\x10\x8a\xb5\x18\x07\x10\xe8\x07(\x01\x30\x01\x12)\n\x0fvoltageExternal\x18\x12 \x01(\rB\x10\x92?\x02\x38\x10\x8a\xb5\x18\x07\x10\xe8\x07(\x01\x30\x01\x12\x1c\n\nmemoryFree\x18\x13 \x01(\rB\x08\x8a\xb5\x18\x04(\x01\x30\x01\x12&\n\x14memoryFreeContiguous\x18\x14 \x01(\rB\x08\x8a\xb5\x18\x04(\x01\x30\x01\x12\"\n\x10memoryFreeLowest\x18\x15 \x01(\rB\x08\x8a\xb5\x18\x04(\x01\x30\x01\x12\x1c\n\x07\x63ommand\x18Z \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01\x12\x1a\n\x05trace\x18[ \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01:\x07\x8a\xb5\x18\x03\x18\x80\x02*}\n\x08Platform\x12\x14\n\x10PLATFORM_UNKNOWN\x10\x00\x12\x10\n\x0cPLATFORM_GCC\x10\x03\x12\x13\n\x0fPLATFORM_PHOTON\x10\x06\x12\x0f\n\x0bPLATFORM_P1\x10\x08\x12\x10\n\x0cPLATFORM_ESP\x10\x64\x12\x11\n\x0cPLATFORM_SIM\x10\xc8\x01*8\n\x0fTemperatureUnit\x12\x10\n\x0cTEMP_CELSIUS\x10\x00\x12\x13\n\x0fTEMP_FAHRENHEIT\x10\x01\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'SysInfo_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _BLOCK.fields_by_name['deviceId']._options = None - _BLOCK.fields_by_name['deviceId']._serialized_options = b'\212\265\030\002(\001\222?\002\010\014\212\265\030\0028\001' + _BLOCK.fields_by_name['deviceId']._serialized_options = b'\222?\002\010\014\212\265\030\004(\0018\001' _BLOCK.fields_by_name['version']._options = None - _BLOCK.fields_by_name['version']._serialized_options = b'\212\265\030\002(\001\222?\002\010\014' + _BLOCK.fields_by_name['version']._serialized_options = b'\222?\002\010\014\212\265\030\002(\001' _BLOCK.fields_by_name['platform']._options = None _BLOCK.fields_by_name['platform']._serialized_options = b'\212\265\030\002(\001' _BLOCK.fields_by_name['protocolVersion']._options = None - _BLOCK.fields_by_name['protocolVersion']._serialized_options = b'\212\265\030\002(\001\222?\002\010\014' + _BLOCK.fields_by_name['protocolVersion']._serialized_options = b'\222?\002\010\014\212\265\030\002(\001' _BLOCK.fields_by_name['releaseDate']._options = None - _BLOCK.fields_by_name['releaseDate']._serialized_options = b'\212\265\030\002(\001\222?\002\010\014' + _BLOCK.fields_by_name['releaseDate']._serialized_options = b'\222?\002\010\014\212\265\030\002(\001' _BLOCK.fields_by_name['protocolDate']._options = None - _BLOCK.fields_by_name['protocolDate']._serialized_options = b'\212\265\030\002(\001\222?\002\010\014' + _BLOCK.fields_by_name['protocolDate']._serialized_options = b'\222?\002\010\014\212\265\030\002(\001' _BLOCK.fields_by_name['ip']._options = None - _BLOCK.fields_by_name['ip']._serialized_options = b'\212\265\030\002(\001\212\265\030\002`\001\222?\0028 ' + _BLOCK.fields_by_name['ip']._serialized_options = b'\222?\0028 \212\265\030\004(\001`\001' _BLOCK.fields_by_name['uptime']._options = None - _BLOCK.fields_by_name['uptime']._serialized_options = b'\212\265\030\002(\001\212\265\030\0020\001\212\265\030\002\010\003\212\265\030\003\020\350\007\222?\0028 ' + _BLOCK.fields_by_name['uptime']._serialized_options = b'\222?\0028 \212\265\030\t\010\003\020\350\007(\0010\001' _BLOCK.fields_by_name['updatesPerSecond']._options = None - _BLOCK.fields_by_name['updatesPerSecond']._serialized_options = b'\212\265\030\002(\001\212\265\030\0020\001\212\265\030\003\020\350\007\222?\0028 ' + _BLOCK.fields_by_name['updatesPerSecond']._serialized_options = b'\222?\0028 \212\265\030\007\020\350\007(\0010\001' _BLOCK.fields_by_name['systemTime']._options = None - _BLOCK.fields_by_name['systemTime']._serialized_options = b'\212\265\030\002X\001\222?\0028 ' + _BLOCK.fields_by_name['systemTime']._serialized_options = b'\222?\0028 \212\265\030\002X\001' _BLOCK.fields_by_name['timeZone']._options = None _BLOCK.fields_by_name['timeZone']._serialized_options = b'\222?\002\010 ' _BLOCK.fields_by_name['displayBrightness']._options = None _BLOCK.fields_by_name['displayBrightness']._serialized_options = b'\222?\0028\010' _BLOCK.fields_by_name['voltage5']._options = None - _BLOCK.fields_by_name['voltage5']._serialized_options = b'\212\265\030\002(\001\212\265\030\0020\001\222?\0028\020\212\265\030\003\020\350\007' + _BLOCK.fields_by_name['voltage5']._serialized_options = b'\222?\0028\020\212\265\030\007\020\350\007(\0010\001' _BLOCK.fields_by_name['voltageExternal']._options = None - _BLOCK.fields_by_name['voltageExternal']._serialized_options = b'\212\265\030\002(\001\212\265\030\0020\001\222?\0028\020\212\265\030\003\020\350\007' + _BLOCK.fields_by_name['voltageExternal']._serialized_options = b'\222?\0028\020\212\265\030\007\020\350\007(\0010\001' _BLOCK.fields_by_name['memoryFree']._options = None - _BLOCK.fields_by_name['memoryFree']._serialized_options = b'\212\265\030\002(\001\212\265\030\0020\001' + _BLOCK.fields_by_name['memoryFree']._serialized_options = b'\212\265\030\004(\0010\001' _BLOCK.fields_by_name['memoryFreeContiguous']._options = None - _BLOCK.fields_by_name['memoryFreeContiguous']._serialized_options = b'\212\265\030\002(\001\212\265\030\0020\001' + _BLOCK.fields_by_name['memoryFreeContiguous']._serialized_options = b'\212\265\030\004(\0010\001' _BLOCK.fields_by_name['memoryFreeLowest']._options = None - _BLOCK.fields_by_name['memoryFreeLowest']._serialized_options = b'\212\265\030\002(\001\212\265\030\0020\001' + _BLOCK.fields_by_name['memoryFreeLowest']._serialized_options = b'\212\265\030\004(\0010\001' _BLOCK.fields_by_name['command']._options = None - _BLOCK.fields_by_name['command']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['command']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK.fields_by_name['trace']._options = None - _BLOCK.fields_by_name['trace']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['trace']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK._options = None _BLOCK._serialized_options = b'\212\265\030\003\030\200\002' - _PLATFORM._serialized_start=847 - _PLATFORM._serialized_end=972 - _TEMPERATUREUNIT._serialized_start=974 - _TEMPERATUREUNIT._serialized_end=1030 - _BLOCK._serialized_start=62 - _BLOCK._serialized_end=845 + _globals['_PLATFORM']._serialized_start=791 + _globals['_PLATFORM']._serialized_end=916 + _globals['_TEMPERATUREUNIT']._serialized_start=918 + _globals['_TEMPERATUREUNIT']._serialized_end=974 + _globals['_BLOCK']._serialized_start=62 + _globals['_BLOCK']._serialized_end=789 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/TempSensorCombi_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/TempSensorCombi_pb2.py index ec45917f..34051459 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/TempSensorCombi_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/TempSensorCombi_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: TempSensorCombi.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -17,34 +15,22 @@ import nanopb_pb2 as nanopb__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15TempSensorCombi.proto\x12\x14\x62lox.TempSensorCombi\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"\xa4\x01\n\x05\x42lock\x12-\n\x05value\x18\x01 \x01(\x11\x42\x1e\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80 \x8a\xb5\x18\x02(\x01\x92?\x02\x38 \x12:\n\x0b\x63ombineFunc\x18\x02 \x01(\x0e\x32%.blox.TempSensorCombi.SensorCombiFunc\x12!\n\x07sensors\x18\x03 \x03(\rB\x10\x8a\xb5\x18\x02\x18\x02\x92?\x02\x38\x10\x92?\x02\x10\x08:\r\x8a\xb5\x18\x03\x18\xc4\x02\x8a\xb5\x18\x02H\x02*b\n\x0fSensorCombiFunc\x12\x19\n\x15SENSOR_COMBI_FUNC_AVG\x10\x00\x12\x19\n\x15SENSOR_COMBI_FUNC_MIN\x10\x01\x12\x19\n\x15SENSOR_COMBI_FUNC_MAX\x10\x02\x62\x06proto3') - -_SENSORCOMBIFUNC = DESCRIPTOR.enum_types_by_name['SensorCombiFunc'] -SensorCombiFunc = enum_type_wrapper.EnumTypeWrapper(_SENSORCOMBIFUNC) -SENSOR_COMBI_FUNC_AVG = 0 -SENSOR_COMBI_FUNC_MIN = 1 -SENSOR_COMBI_FUNC_MAX = 2 - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'TempSensorCombi_pb2' - # @@protoc_insertion_point(class_scope:blox.TempSensorCombi.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15TempSensorCombi.proto\x12\x14\x62lox.TempSensorCombi\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"\x92\x01\n\x05\x42lock\x12!\n\x05value\x18\x01 \x01(\x11\x42\x12\x92?\x02\x38 \x8a\xb5\x18\t\x08\x01\x10\x80 (\x01\x30\x01\x12:\n\x0b\x63ombineFunc\x18\x02 \x01(\x0e\x32%.blox.TempSensorCombi.SensorCombiFunc\x12\x1e\n\x07sensors\x18\x03 \x03(\rB\r\x92?\x04\x10\x08\x38\x10\x8a\xb5\x18\x02\x18\x02:\n\x8a\xb5\x18\x06\x18\xc4\x02J\x01\x02*b\n\x0fSensorCombiFunc\x12\x19\n\x15SENSOR_COMBI_FUNC_AVG\x10\x00\x12\x19\n\x15SENSOR_COMBI_FUNC_MIN\x10\x01\x12\x19\n\x15SENSOR_COMBI_FUNC_MAX\x10\x02\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TempSensorCombi_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _BLOCK.fields_by_name['value']._options = None - _BLOCK.fields_by_name['value']._serialized_options = b'\212\265\030\0020\001\212\265\030\002\010\001\212\265\030\003\020\200 \212\265\030\002(\001\222?\0028 ' + _BLOCK.fields_by_name['value']._serialized_options = b'\222?\0028 \212\265\030\t\010\001\020\200 (\0010\001' _BLOCK.fields_by_name['sensors']._options = None - _BLOCK.fields_by_name['sensors']._serialized_options = b'\212\265\030\002\030\002\222?\0028\020\222?\002\020\010' + _BLOCK.fields_by_name['sensors']._serialized_options = b'\222?\004\020\0108\020\212\265\030\002\030\002' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\304\002\212\265\030\002H\002' - _SENSORCOMBIFUNC._serialized_start=244 - _SENSORCOMBIFUNC._serialized_end=342 - _BLOCK._serialized_start=78 - _BLOCK._serialized_end=242 + _BLOCK._serialized_options = b'\212\265\030\006\030\304\002J\001\002' + _globals['_SENSORCOMBIFUNC']._serialized_start=226 + _globals['_SENSORCOMBIFUNC']._serialized_end=324 + _globals['_BLOCK']._serialized_start=78 + _globals['_BLOCK']._serialized_end=224 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/TempSensorExternal_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/TempSensorExternal_pb2.py index f22f4080..57085988 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/TempSensorExternal_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/TempSensorExternal_pb2.py @@ -4,9 +4,8 @@ """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -16,31 +15,24 @@ import nanopb_pb2 as nanopb__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18TempSensorExternal.proto\x12\x17\x62lox.TempSensorExternal\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"\xd3\x01\n\x05\x42lock\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x1c\n\x07timeout\x18\x02 \x01(\rB\x0b\x8a\xb5\x18\x02\x08\x03\x92?\x02\x38 \x12)\n\x07setting\x18\x03 \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12,\n\x0blastUpdated\x18\x04 \x01(\rB\x17\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02X\x01\x8a\xb5\x18\x02(\x01\x92?\x02\x38 \x12-\n\x05value\x18\x05 \x01(\x11\x42\x1e\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80 \x8a\xb5\x18\x02(\x01\x92?\x02\x38 :\x13\x8a\xb5\x18\x03\x18\xc8\x02\x8a\xb5\x18\x02H\x02\x8a\xb5\x18\x02H\x0f\x62\x06proto3') - - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'TempSensorExternal_pb2' - # @@protoc_insertion_point(class_scope:blox.TempSensorExternal.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18TempSensorExternal.proto\x12\x17\x62lox.TempSensorExternal\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"\xaf\x01\n\x05\x42lock\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x1c\n\x07timeout\x18\x02 \x01(\rB\x0b\x92?\x02\x38 \x8a\xb5\x18\x02\x08\x03\x12!\n\x07setting\x18\x03 \x01(\x11\x42\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x08\x01\x10\x80 0\x01\x12$\n\x0blastUpdated\x18\x04 \x01(\rB\x0f\x92?\x02\x38 \x8a\xb5\x18\x06(\x01\x30\x01X\x01\x12!\n\x05value\x18\x05 \x01(\x11\x42\x12\x92?\x02\x38 \x8a\xb5\x18\t\x08\x01\x10\x80 (\x01\x30\x01:\x0b\x8a\xb5\x18\x07\x18\xc8\x02J\x02\x02\x0f\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TempSensorExternal_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _BLOCK.fields_by_name['timeout']._options = None - _BLOCK.fields_by_name['timeout']._serialized_options = b'\212\265\030\002\010\003\222?\0028 ' + _BLOCK.fields_by_name['timeout']._serialized_options = b'\222?\0028 \212\265\030\002\010\003' _BLOCK.fields_by_name['setting']._options = None - _BLOCK.fields_by_name['setting']._serialized_options = b'\212\265\030\0020\001\212\265\030\002\010\001\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['setting']._serialized_options = b'\222?\0028 \212\265\030\007\010\001\020\200 0\001' _BLOCK.fields_by_name['lastUpdated']._options = None - _BLOCK.fields_by_name['lastUpdated']._serialized_options = b'\212\265\030\0020\001\212\265\030\002X\001\212\265\030\002(\001\222?\0028 ' + _BLOCK.fields_by_name['lastUpdated']._serialized_options = b'\222?\0028 \212\265\030\006(\0010\001X\001' _BLOCK.fields_by_name['value']._options = None - _BLOCK.fields_by_name['value']._serialized_options = b'\212\265\030\0020\001\212\265\030\002\010\001\212\265\030\003\020\200 \212\265\030\002(\001\222?\0028 ' + _BLOCK.fields_by_name['value']._serialized_options = b'\222?\0028 \212\265\030\t\010\001\020\200 (\0010\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\310\002\212\265\030\002H\002\212\265\030\002H\017' - _BLOCK._serialized_start=84 - _BLOCK._serialized_end=295 + _BLOCK._serialized_options = b'\212\265\030\007\030\310\002J\002\002\017' + _globals['_BLOCK']._serialized_start=84 + _globals['_BLOCK']._serialized_end=259 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/TempSensorMock_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/TempSensorMock_pb2.py index 33081efc..6ad204f9 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/TempSensorMock_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/TempSensorMock_pb2.py @@ -4,9 +4,8 @@ """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -16,43 +15,28 @@ import nanopb_pb2 as nanopb__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14TempSensorMock.proto\x12\x13\x62lox.TempSensorMock\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"^\n\x0b\x46luctuation\x12+\n\tamplitude\x18\x01 \x01(\x11\x42\x18\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02\x08\x06\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12\"\n\x06period\x18\x02 \x01(\rB\x12\x8a\xb5\x18\x02\x08\x03\x8a\xb5\x18\x03\x10\xe8\x07\x92?\x02\x38 \"\xbd\x01\n\x05\x42lock\x12-\n\x05value\x18\x01 \x01(\x11\x42\x1e\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80 \x8a\xb5\x18\x02(\x01\x92?\x02\x38 \x12\x19\n\tconnected\x18\x03 \x01(\x08\x42\x06\x8a\xb5\x18\x02\x30\x01\x12#\n\x07setting\x18\x04 \x01(\x11\x42\x12\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12\x36\n\x0c\x66luctuations\x18\x05 \x03(\x0b\x32 .blox.TempSensorMock.Fluctuation:\r\x8a\xb5\x18\x03\x18\xad\x02\x8a\xb5\x18\x02H\x02\x62\x06proto3') - - - -_FLUCTUATION = DESCRIPTOR.message_types_by_name['Fluctuation'] -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Fluctuation = _reflection.GeneratedProtocolMessageType('Fluctuation', (_message.Message,), { - 'DESCRIPTOR' : _FLUCTUATION, - '__module__' : 'TempSensorMock_pb2' - # @@protoc_insertion_point(class_scope:blox.TempSensorMock.Fluctuation) - }) -_sym_db.RegisterMessage(Fluctuation) - -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'TempSensorMock_pb2' - # @@protoc_insertion_point(class_scope:blox.TempSensorMock.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14TempSensorMock.proto\x12\x13\x62lox.TempSensorMock\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"R\n\x0b\x46luctuation\x12#\n\tamplitude\x18\x01 \x01(\x11\x42\x10\x92?\x02\x38 \x8a\xb5\x18\x07\x08\x06\x10\x80 0\x01\x12\x1e\n\x06period\x18\x02 \x01(\rB\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x08\x03\x10\xe8\x07\"\xaa\x01\n\x05\x42lock\x12!\n\x05value\x18\x01 \x01(\x11\x42\x12\x92?\x02\x38 \x8a\xb5\x18\t\x08\x01\x10\x80 (\x01\x30\x01\x12\x19\n\tconnected\x18\x03 \x01(\x08\x42\x06\x8a\xb5\x18\x02\x30\x01\x12\x1f\n\x07setting\x18\x04 \x01(\x11\x42\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x08\x01\x10\x80 \x12\x36\n\x0c\x66luctuations\x18\x05 \x03(\x0b\x32 .blox.TempSensorMock.Fluctuation:\n\x8a\xb5\x18\x06\x18\xad\x02J\x01\x02\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TempSensorMock_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _FLUCTUATION.fields_by_name['amplitude']._options = None - _FLUCTUATION.fields_by_name['amplitude']._serialized_options = b'\212\265\030\0020\001\212\265\030\002\010\006\212\265\030\003\020\200 \222?\0028 ' + _FLUCTUATION.fields_by_name['amplitude']._serialized_options = b'\222?\0028 \212\265\030\007\010\006\020\200 0\001' _FLUCTUATION.fields_by_name['period']._options = None - _FLUCTUATION.fields_by_name['period']._serialized_options = b'\212\265\030\002\010\003\212\265\030\003\020\350\007\222?\0028 ' + _FLUCTUATION.fields_by_name['period']._serialized_options = b'\222?\0028 \212\265\030\005\010\003\020\350\007' _BLOCK.fields_by_name['value']._options = None - _BLOCK.fields_by_name['value']._serialized_options = b'\212\265\030\0020\001\212\265\030\002\010\001\212\265\030\003\020\200 \212\265\030\002(\001\222?\0028 ' + _BLOCK.fields_by_name['value']._serialized_options = b'\222?\0028 \212\265\030\t\010\001\020\200 (\0010\001' _BLOCK.fields_by_name['connected']._options = None _BLOCK.fields_by_name['connected']._serialized_options = b'\212\265\030\0020\001' _BLOCK.fields_by_name['setting']._options = None - _BLOCK.fields_by_name['setting']._serialized_options = b'\212\265\030\002\010\001\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['setting']._serialized_options = b'\222?\0028 \212\265\030\005\010\001\020\200 ' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\255\002\212\265\030\002H\002' - _FLUCTUATION._serialized_start=75 - _FLUCTUATION._serialized_end=169 - _BLOCK._serialized_start=172 - _BLOCK._serialized_end=361 + _BLOCK._serialized_options = b'\212\265\030\006\030\255\002J\001\002' + _globals['_FLUCTUATION']._serialized_start=75 + _globals['_FLUCTUATION']._serialized_end=157 + _globals['_BLOCK']._serialized_start=160 + _globals['_BLOCK']._serialized_end=330 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/TempSensorOneWire_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/TempSensorOneWire_pb2.py index a06b0e57..b509f507 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/TempSensorOneWire_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/TempSensorOneWire_pb2.py @@ -4,9 +4,8 @@ """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -16,31 +15,24 @@ import nanopb_pb2 as nanopb__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17TempSensorOneWire.proto\x12\x16\x62lox.TempSensorOneWire\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"\xb2\x01\n\x05\x42lock\x12-\n\x05value\x18\x01 \x01(\x11\x42\x1e\x8a\xb5\x18\x02\x30\x01\x8a\xb5\x18\x02\x08\x01\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x8a\xb5\x18\x02(\x01\x12\"\n\x06offset\x18\x03 \x01(\x11\x42\x12\x8a\xb5\x18\x02\x08\x06\x8a\xb5\x18\x03\x10\x80 \x92?\x02\x38 \x12\x17\n\x07\x61\x64\x64ress\x18\x04 \x01(\x06\x42\x06\x8a\xb5\x18\x02 \x01\x12(\n\x0coneWireBusId\x18\x05 \x01(\rB\x12\x8a\xb5\x18\x03\x18\x82\x02\x92?\x02\x38\x10\x8a\xb5\x18\x02(\x01:\x13\x8a\xb5\x18\x03\x18\xae\x02\x8a\xb5\x18\x02H\x02\x8a\xb5\x18\x02H\tb\x06proto3') - - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'TempSensorOneWire_pb2' - # @@protoc_insertion_point(class_scope:blox.TempSensorOneWire.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17TempSensorOneWire.proto\x12\x16\x62lox.TempSensorOneWire\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"\x96\x01\n\x05\x42lock\x12!\n\x05value\x18\x01 \x01(\x11\x42\x12\x92?\x02\x38 \x8a\xb5\x18\t\x08\x01\x10\x80 (\x01\x30\x01\x12\x1e\n\x06offset\x18\x03 \x01(\x11\x42\x0e\x92?\x02\x38 \x8a\xb5\x18\x05\x08\x06\x10\x80 \x12\x17\n\x07\x61\x64\x64ress\x18\x04 \x01(\x06\x42\x06\x8a\xb5\x18\x02 \x01\x12$\n\x0coneWireBusId\x18\x05 \x01(\rB\x0e\x92?\x02\x38\x10\x8a\xb5\x18\x05\x18\x82\x02(\x01:\x0b\x8a\xb5\x18\x07\x18\xae\x02J\x02\x02\tb\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TempSensorOneWire_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None _BLOCK.fields_by_name['value']._options = None - _BLOCK.fields_by_name['value']._serialized_options = b'\212\265\030\0020\001\212\265\030\002\010\001\212\265\030\003\020\200 \222?\0028 \212\265\030\002(\001' + _BLOCK.fields_by_name['value']._serialized_options = b'\222?\0028 \212\265\030\t\010\001\020\200 (\0010\001' _BLOCK.fields_by_name['offset']._options = None - _BLOCK.fields_by_name['offset']._serialized_options = b'\212\265\030\002\010\006\212\265\030\003\020\200 \222?\0028 ' + _BLOCK.fields_by_name['offset']._serialized_options = b'\222?\0028 \212\265\030\005\010\006\020\200 ' _BLOCK.fields_by_name['address']._options = None _BLOCK.fields_by_name['address']._serialized_options = b'\212\265\030\002 \001' _BLOCK.fields_by_name['oneWireBusId']._options = None - _BLOCK.fields_by_name['oneWireBusId']._serialized_options = b'\212\265\030\003\030\202\002\222?\0028\020\212\265\030\002(\001' + _BLOCK.fields_by_name['oneWireBusId']._serialized_options = b'\222?\0028\020\212\265\030\005\030\202\002(\001' _BLOCK._options = None - _BLOCK._serialized_options = b'\212\265\030\003\030\256\002\212\265\030\002H\002\212\265\030\002H\t' - _BLOCK._serialized_start=82 - _BLOCK._serialized_end=260 + _BLOCK._serialized_options = b'\212\265\030\007\030\256\002J\002\002\t' + _globals['_BLOCK']._serialized_start=82 + _globals['_BLOCK']._serialized_end=232 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/TouchSettings_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/TouchSettings_pb2.py index 07f7e2a8..f68dc1ac 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/TouchSettings_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/TouchSettings_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: TouchSettings.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -19,21 +17,9 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13TouchSettings.proto\x12\x12\x62lox.TouchSettings\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"\xb6\x01\n\x05\x42lock\x12\x32\n\ncalibrated\x18\x01 \x01(\x0e\x32\x1e.blox.TouchSettings.Calibrated\x12\x16\n\x07xOffset\x18\x02 \x01(\x05\x42\x05\x92?\x02\x38\x10\x12\x16\n\x07yOffset\x18\x03 \x01(\x05\x42\x05\x92?\x02\x38\x10\x12\x1f\n\x10xBitsPerPixelX16\x18\x04 \x01(\rB\x05\x92?\x02\x38\x10\x12\x1f\n\x10yBitsPerPixelX16\x18\x05 \x01(\rB\x05\x92?\x02\x38\x10:\x07\x8a\xb5\x18\x03\x18\xb9\x02*G\n\nCalibrated\x12\x11\n\rCALIBRATED_NO\x10\x00\x12\x12\n\x0e\x43\x41LIBRATED_YES\x10\x01\x12\x12\n\x0e\x43\x41LIBRATED_NEW\x10\x02\x62\x06proto3') -_CALIBRATED = DESCRIPTOR.enum_types_by_name['Calibrated'] -Calibrated = enum_type_wrapper.EnumTypeWrapper(_CALIBRATED) -CALIBRATED_NO = 0 -CALIBRATED_YES = 1 -CALIBRATED_NEW = 2 - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'TouchSettings_pb2' - # @@protoc_insertion_point(class_scope:blox.TouchSettings.Block) - }) -_sym_db.RegisterMessage(Block) - +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TouchSettings_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None @@ -47,8 +33,8 @@ _BLOCK.fields_by_name['yBitsPerPixelX16']._serialized_options = b'\222?\0028\020' _BLOCK._options = None _BLOCK._serialized_options = b'\212\265\030\003\030\271\002' - _CALIBRATED._serialized_start=258 - _CALIBRATED._serialized_end=329 - _BLOCK._serialized_start=74 - _BLOCK._serialized_end=256 + _globals['_CALIBRATED']._serialized_start=258 + _globals['_CALIBRATED']._serialized_end=329 + _globals['_BLOCK']._serialized_start=74 + _globals['_BLOCK']._serialized_end=256 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/WiFiSettings_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/WiFiSettings_pb2.py index d30cd06d..09d26fe0 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/WiFiSettings_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/WiFiSettings_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: WiFiSettings.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -17,33 +15,11 @@ import nanopb_pb2 as nanopb__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12WiFiSettings.proto\x12\x11\x62lox.WiFiSettings\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"\xd4\x01\n\x05\x42lock\x12\x13\n\x04ssid\x18\x01 \x01(\tB\x05\x92?\x02\x08!\x12\x17\n\x08password\x18\x02 \x01(\tB\x05\x92?\x02\x08@\x12-\n\x08security\x18\x03 \x01(\x0e\x32\x1b.blox.WiFiSettings.Security\x12)\n\x06\x63ipher\x18\x04 \x01(\x0e\x32\x19.blox.WiFiSettings.Cipher\x12!\n\x06signal\x18\x05 \x01(\x05\x42\x11\x8a\xb5\x18\x02(\x01\x8a\xb5\x18\x02\x30\x01\x92?\x02\x38\x08\x12\x17\n\x02ip\x18Z \x01(\x08\x42\x0b\x8a\xb5\x18\x02H\x01\x92?\x02\x18\x03:\x07\x8a\xb5\x18\x03\x18\xb8\x02*\xa7\x01\n\x08Security\x12\x12\n\x0eWLAN_SEC_UNSEC\x10\x00\x12\x10\n\x0cWLAN_SEC_WEP\x10\x01\x12\x10\n\x0cWLAN_SEC_WPA\x10\x02\x12\x11\n\rWLAN_SEC_WPA2\x10\x03\x12\x1b\n\x17WLAN_SEC_WPA_ENTERPRISE\x10\x04\x12\x1c\n\x18WLAN_SEC_WPA2_ENTERPRISE\x10\x05\x12\x15\n\x10WLAN_SEC_NOT_SET\x10\xff\x01*i\n\x06\x43ipher\x12\x17\n\x13WLAN_CIPHER_NOT_SET\x10\x00\x12\x13\n\x0fWLAN_CIPHER_AES\x10\x01\x12\x14\n\x10WLAN_CIPHER_TKIP\x10\x02\x12\x1b\n\x17WLAN_CIPHER_AES_OR_TKIP\x10\x03\x62\x06proto3') - -_SECURITY = DESCRIPTOR.enum_types_by_name['Security'] -Security = enum_type_wrapper.EnumTypeWrapper(_SECURITY) -_CIPHER = DESCRIPTOR.enum_types_by_name['Cipher'] -Cipher = enum_type_wrapper.EnumTypeWrapper(_CIPHER) -WLAN_SEC_UNSEC = 0 -WLAN_SEC_WEP = 1 -WLAN_SEC_WPA = 2 -WLAN_SEC_WPA2 = 3 -WLAN_SEC_WPA_ENTERPRISE = 4 -WLAN_SEC_WPA2_ENTERPRISE = 5 -WLAN_SEC_NOT_SET = 255 -WLAN_CIPHER_NOT_SET = 0 -WLAN_CIPHER_AES = 1 -WLAN_CIPHER_TKIP = 2 -WLAN_CIPHER_AES_OR_TKIP = 3 - - -_BLOCK = DESCRIPTOR.message_types_by_name['Block'] -Block = _reflection.GeneratedProtocolMessageType('Block', (_message.Message,), { - 'DESCRIPTOR' : _BLOCK, - '__module__' : 'WiFiSettings_pb2' - # @@protoc_insertion_point(class_scope:blox.WiFiSettings.Block) - }) -_sym_db.RegisterMessage(Block) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12WiFiSettings.proto\x12\x11\x62lox.WiFiSettings\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"\xd0\x01\n\x05\x42lock\x12\x13\n\x04ssid\x18\x01 \x01(\tB\x05\x92?\x02\x08!\x12\x17\n\x08password\x18\x02 \x01(\tB\x05\x92?\x02\x08@\x12-\n\x08security\x18\x03 \x01(\x0e\x32\x1b.blox.WiFiSettings.Security\x12)\n\x06\x63ipher\x18\x04 \x01(\x0e\x32\x19.blox.WiFiSettings.Cipher\x12\x1d\n\x06signal\x18\x05 \x01(\x05\x42\r\x92?\x02\x38\x08\x8a\xb5\x18\x04(\x01\x30\x01\x12\x17\n\x02ip\x18Z \x01(\x08\x42\x0b\x92?\x02\x18\x03\x8a\xb5\x18\x02H\x01:\x07\x8a\xb5\x18\x03\x18\xb8\x02*\xa7\x01\n\x08Security\x12\x12\n\x0eWLAN_SEC_UNSEC\x10\x00\x12\x10\n\x0cWLAN_SEC_WEP\x10\x01\x12\x10\n\x0cWLAN_SEC_WPA\x10\x02\x12\x11\n\rWLAN_SEC_WPA2\x10\x03\x12\x1b\n\x17WLAN_SEC_WPA_ENTERPRISE\x10\x04\x12\x1c\n\x18WLAN_SEC_WPA2_ENTERPRISE\x10\x05\x12\x15\n\x10WLAN_SEC_NOT_SET\x10\xff\x01*i\n\x06\x43ipher\x12\x17\n\x13WLAN_CIPHER_NOT_SET\x10\x00\x12\x13\n\x0fWLAN_CIPHER_AES\x10\x01\x12\x14\n\x10WLAN_CIPHER_TKIP\x10\x02\x12\x1b\n\x17WLAN_CIPHER_AES_OR_TKIP\x10\x03\x62\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'WiFiSettings_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None @@ -52,15 +28,15 @@ _BLOCK.fields_by_name['password']._options = None _BLOCK.fields_by_name['password']._serialized_options = b'\222?\002\010@' _BLOCK.fields_by_name['signal']._options = None - _BLOCK.fields_by_name['signal']._serialized_options = b'\212\265\030\002(\001\212\265\030\0020\001\222?\0028\010' + _BLOCK.fields_by_name['signal']._serialized_options = b'\222?\0028\010\212\265\030\004(\0010\001' _BLOCK.fields_by_name['ip']._options = None - _BLOCK.fields_by_name['ip']._serialized_options = b'\212\265\030\002H\001\222?\002\030\003' + _BLOCK.fields_by_name['ip']._serialized_options = b'\222?\002\030\003\212\265\030\002H\001' _BLOCK._options = None _BLOCK._serialized_options = b'\212\265\030\003\030\270\002' - _SECURITY._serialized_start=287 - _SECURITY._serialized_end=454 - _CIPHER._serialized_start=456 - _CIPHER._serialized_end=561 - _BLOCK._serialized_start=72 - _BLOCK._serialized_end=284 + _globals['_SECURITY']._serialized_start=283 + _globals['_SECURITY']._serialized_end=450 + _globals['_CIPHER']._serialized_start=452 + _globals['_CIPHER']._serialized_end=557 + _globals['_BLOCK']._serialized_start=72 + _globals['_BLOCK']._serialized_end=280 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/brewblox_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/brewblox_pb2.py index c97dc642..a6b3c7d3 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/brewblox_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/brewblox_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: brewblox.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -19,106 +17,9 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0e\x62rewblox.proto\x12\x08\x62rewblox\x1a google/protobuf/descriptor.proto\x1a\x0cnanopb.proto\"|\n\x0bMessageOpts\x12$\n\x07objtype\x18\x03 \x01(\x0e\x32\x13.brewblox.BlockType\x12(\n\x04impl\x18\t \x03(\x0e\x32\x13.brewblox.BlockTypeB\x05\x92?\x02\x10\x05\x12\x16\n\x07subtype\x18\x0b \x01(\rB\x05\x92?\x02\x38\x10:\x05\x92?\x02\x30\x01\"\xf4\x01\n\tFieldOpts\x12 \n\x04unit\x18\x01 \x01(\x0e\x32\x12.brewblox.UnitType\x12\r\n\x05scale\x18\x02 \x01(\r\x12$\n\x07objtype\x18\x03 \x01(\x0e\x32\x13.brewblox.BlockType\x12\r\n\x05hexed\x18\x04 \x01(\x08\x12\x10\n\x08readonly\x18\x05 \x01(\x08\x12\x0e\n\x06logged\x18\x06 \x01(\x08\x12\x0e\n\x06hexstr\x18\x07 \x01(\x08\x12\x0f\n\x07ignored\x18\t \x01(\x08\x12\x10\n\x08\x62itfield\x18\n \x01(\x08\x12\x10\n\x08\x64\x61tetime\x18\x0b \x01(\x08\x12\x13\n\x0bipv4address\x18\x0c \x01(\x08:\x05\x92?\x02\x30\x01*\x8c\x02\n\x08UnitType\x12\n\n\x06NotSet\x10\x00\x12\x0b\n\x07\x43\x65lsius\x10\x01\x12\x12\n\x0eInverseCelsius\x10\x02\x12\n\n\x06Second\x10\x03\x12\n\n\x06Minute\x10\x04\x12\x08\n\x04Hour\x10\x05\x12\x10\n\x0c\x44\x65ltaCelsius\x10\x06\x12\x19\n\x15\x44\x65ltaCelsiusPerSecond\x10\x07\x12\x19\n\x15\x44\x65ltaCelsiusPerMinute\x10\x08\x12\x17\n\x13\x44\x65ltaCelsiusPerHour\x10\t\x12\x1a\n\x16\x44\x65ltaCelsiusMultSecond\x10\n\x12\x1a\n\x16\x44\x65ltaCelsiusMultMinute\x10\x0b\x12\x18\n\x14\x44\x65ltaCelsiusMultHour\x10\x0c*\xb8\n\n\tBlockType\x12\x0b\n\x07Invalid\x10\x00\x12\x19\n\x15ProcessValueInterface\x10\x01\x12\x17\n\x13TempSensorInterface\x10\x02\x12\x1f\n\x1bSetpointSensorPairInterface\x10\x04\x12\x1b\n\x17\x41\x63tuatorAnalogInterface\x10\x05\x12\x1c\n\x18\x41\x63tuatorDigitalInterface\x10\x06\x12\x15\n\x11\x42\x61lancerInterface\x10\x07\x12\x12\n\x0eMutexInterface\x10\x08\x12\x1a\n\x16OneWireDeviceInterface\x10\t\x12\x14\n\x10IoArrayInterface\x10\n\x12\x13\n\x0f\x44S2408Interface\x10\x0b\x12\x17\n\x13OneWireBusInterface\x10\x0c\x12\x15\n\x11IoModuleInterface\x10\r\x12\x1f\n\x1bOneWireDeviceBlockInterface\x10\x0e\x12\x14\n\x10\x45nablerInterface\x10\x0f\x12\x16\n\x12\x43laimableInterface\x10\x10\x12\x15\n\x11IoDriverInterface\x10\x11\x12\x15\n\x11SetpointInterface\x10\x12\x12\x19\n\x15StoredAnalogInterface\x10\x13\x12\x1b\n\x17StoredSetpointInterface\x10\x14\x12\x1a\n\x16StoredDigitalInterface\x10\x15\x12\x1e\n\x1a\x43onstrainedAnalogInterface\x10\x16\x12 \n\x1c\x43onstrainedSetpointInterface\x10\x17\x12\x1f\n\x1b\x43onstrainedDigitalInterface\x10\x18\x12\x1c\n\x18ScanningFactoryInterface\x10\x19\x12\x1c\n\x18I2CDiscoverableInterface\x10\x1a\x12\x14\n\x10\x44igitalInterface\x10\x1b\x12\x08\n\x03\x41ny\x10\xff\x01\x12\x0c\n\x07SysInfo\x10\x80\x02\x12\n\n\x05Ticks\x10\x81\x02\x12\x0f\n\nOneWireBus\x10\x82\x02\x12\x0e\n\tBoardPins\x10\x83\x02\x12\x13\n\x0eTempSensorMock\x10\xad\x02\x12\x16\n\x11TempSensorOneWire\x10\xae\x02\x12\x17\n\x12SetpointSensorPair\x10\xaf\x02\x12\x08\n\x03Pid\x10\xb0\x02\x12\x17\n\x12\x41\x63tuatorAnalogMock\x10\xb1\x02\x12\x10\n\x0b\x41\x63tuatorPin\x10\xb2\x02\x12\x10\n\x0b\x41\x63tuatorPwm\x10\xb3\x02\x12\x13\n\x0e\x41\x63tuatorOffset\x10\xb4\x02\x12\r\n\x08\x42\x61lancer\x10\xb5\x02\x12\n\n\x05Mutex\x10\xb6\x02\x12\x14\n\x0fSetpointProfile\x10\xb7\x02\x12\x11\n\x0cWiFiSettings\x10\xb8\x02\x12\x12\n\rTouchSettings\x10\xb9\x02\x12\x14\n\x0f\x44isplaySettings\x10\xba\x02\x12\x0b\n\x06\x44S2413\x10\xbb\x02\x12\x14\n\x0f\x41\x63tuatorOneWire\x10\xbc\x02\x12\x0b\n\x06\x44S2408\x10\xbd\x02\x12\x14\n\x0f\x44igitalActuator\x10\xbe\x02\x12\x0f\n\nSpark3Pins\x10\xbf\x02\x12\x0f\n\nSpark2Pins\x10\xc0\x02\x12\x0f\n\nMotorValve\x10\xc1\x02\x12\x12\n\rActuatorLogic\x10\xc2\x02\x12\r\n\x08MockPins\x10\xc3\x02\x12\x14\n\x0fTempSensorCombi\x10\xc4\x02\x12\x16\n\x11OneWireGpioModule\x10\xc5\x02\x12\r\n\x08Sequence\x10\xc6\x02\x12\x17\n\x12TempSensorExternal\x10\xc8\x02\x12\x0c\n\x07\x46\x61stPwm\x10\xc9\x02\x12\x11\n\x0c\x44igitalInput\x10\xca\x02:J\n\x05\x66ield\x12\x1d.google.protobuf.FieldOptions\x18\xd1\x86\x03 \x01(\x0b\x32\x13.brewblox.FieldOptsB\x05\x92?\x02\x18\x03:L\n\x03msg\x12\x1f.google.protobuf.MessageOptions\x18\xd1\x86\x03 \x01(\x0b\x32\x15.brewblox.MessageOptsB\x05\x92?\x02\x18\x03\x62\x06proto3') -_UNITTYPE = DESCRIPTOR.enum_types_by_name['UnitType'] -UnitType = enum_type_wrapper.EnumTypeWrapper(_UNITTYPE) -_BLOCKTYPE = DESCRIPTOR.enum_types_by_name['BlockType'] -BlockType = enum_type_wrapper.EnumTypeWrapper(_BLOCKTYPE) -NotSet = 0 -Celsius = 1 -InverseCelsius = 2 -Second = 3 -Minute = 4 -Hour = 5 -DeltaCelsius = 6 -DeltaCelsiusPerSecond = 7 -DeltaCelsiusPerMinute = 8 -DeltaCelsiusPerHour = 9 -DeltaCelsiusMultSecond = 10 -DeltaCelsiusMultMinute = 11 -DeltaCelsiusMultHour = 12 -Invalid = 0 -ProcessValueInterface = 1 -TempSensorInterface = 2 -SetpointSensorPairInterface = 4 -ActuatorAnalogInterface = 5 -ActuatorDigitalInterface = 6 -BalancerInterface = 7 -MutexInterface = 8 -OneWireDeviceInterface = 9 -IoArrayInterface = 10 -DS2408Interface = 11 -OneWireBusInterface = 12 -IoModuleInterface = 13 -OneWireDeviceBlockInterface = 14 -EnablerInterface = 15 -ClaimableInterface = 16 -IoDriverInterface = 17 -SetpointInterface = 18 -StoredAnalogInterface = 19 -StoredSetpointInterface = 20 -StoredDigitalInterface = 21 -ConstrainedAnalogInterface = 22 -ConstrainedSetpointInterface = 23 -ConstrainedDigitalInterface = 24 -ScanningFactoryInterface = 25 -I2CDiscoverableInterface = 26 -DigitalInterface = 27 -Any = 255 -SysInfo = 256 -Ticks = 257 -OneWireBus = 258 -BoardPins = 259 -TempSensorMock = 301 -TempSensorOneWire = 302 -SetpointSensorPair = 303 -Pid = 304 -ActuatorAnalogMock = 305 -ActuatorPin = 306 -ActuatorPwm = 307 -ActuatorOffset = 308 -Balancer = 309 -Mutex = 310 -SetpointProfile = 311 -WiFiSettings = 312 -TouchSettings = 313 -DisplaySettings = 314 -DS2413 = 315 -ActuatorOneWire = 316 -DS2408 = 317 -DigitalActuator = 318 -Spark3Pins = 319 -Spark2Pins = 320 -MotorValve = 321 -ActuatorLogic = 322 -MockPins = 323 -TempSensorCombi = 324 -OneWireGpioModule = 325 -Sequence = 326 -TempSensorExternal = 328 -FastPwm = 329 -DigitalInput = 330 - -FIELD_FIELD_NUMBER = 50001 -field = DESCRIPTOR.extensions_by_name['field'] -MSG_FIELD_NUMBER = 50001 -msg = DESCRIPTOR.extensions_by_name['msg'] - -_MESSAGEOPTS = DESCRIPTOR.message_types_by_name['MessageOpts'] -_FIELDOPTS = DESCRIPTOR.message_types_by_name['FieldOpts'] -MessageOpts = _reflection.GeneratedProtocolMessageType('MessageOpts', (_message.Message,), { - 'DESCRIPTOR' : _MESSAGEOPTS, - '__module__' : 'brewblox_pb2' - # @@protoc_insertion_point(class_scope:brewblox.MessageOpts) - }) -_sym_db.RegisterMessage(MessageOpts) - -FieldOpts = _reflection.GeneratedProtocolMessageType('FieldOpts', (_message.Message,), { - 'DESCRIPTOR' : _FIELDOPTS, - '__module__' : 'brewblox_pb2' - # @@protoc_insertion_point(class_scope:brewblox.FieldOpts) - }) -_sym_db.RegisterMessage(FieldOpts) - +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'brewblox_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: google_dot_protobuf_dot_descriptor__pb2.FieldOptions.RegisterExtension(field) google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(msg) @@ -136,12 +37,12 @@ _MESSAGEOPTS._serialized_options = b'\222?\0020\001' _FIELDOPTS._options = None _FIELDOPTS._serialized_options = b'\222?\0020\001' - _UNITTYPE._serialized_start=450 - _UNITTYPE._serialized_end=718 - _BLOCKTYPE._serialized_start=721 - _BLOCKTYPE._serialized_end=2057 - _MESSAGEOPTS._serialized_start=76 - _MESSAGEOPTS._serialized_end=200 - _FIELDOPTS._serialized_start=203 - _FIELDOPTS._serialized_end=447 + _globals['_UNITTYPE']._serialized_start=450 + _globals['_UNITTYPE']._serialized_end=718 + _globals['_BLOCKTYPE']._serialized_start=721 + _globals['_BLOCKTYPE']._serialized_end=2057 + _globals['_MESSAGEOPTS']._serialized_start=76 + _globals['_MESSAGEOPTS']._serialized_end=200 + _globals['_FIELDOPTS']._serialized_start=203 + _globals['_FIELDOPTS']._serialized_end=447 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/command_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/command_pb2.py index c62d69e3..0ebf37dc 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/command_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/command_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: command.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -19,88 +17,9 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rcommand.proto\x12\x07\x63ommand\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\"\xac\x01\n\x07Payload\x12\x16\n\x07\x62lockId\x18\x01 \x01(\rB\x05\x92?\x02\x38\x10\x12&\n\tblockType\x18\x02 \x01(\x0e\x32\x13.brewblox.BlockType\x12\x16\n\x07subtype\x18\x03 \x01(\rB\x05\x92?\x02\x38\x10\x12\x0f\n\x07\x63ontent\x18\x04 \x01(\t\x12\x13\n\x04mask\x18\x05 \x03(\rB\x05\x92?\x02\x38\x10\x12#\n\x08maskMode\x18\x06 \x01(\x0e\x32\x11.command.MaskMode\"\\\n\x07Request\x12\r\n\x05msgId\x18\x01 \x01(\r\x12\x1f\n\x06opcode\x18\x02 \x01(\x0e\x32\x0f.command.Opcode\x12!\n\x07payload\x18\x03 \x01(\x0b\x32\x10.command.Payload\"_\n\x08Response\x12\r\n\x05msgId\x18\x01 \x01(\r\x12!\n\x05\x65rror\x18\x02 \x01(\x0e\x32\x12.command.ErrorCode\x12!\n\x07payload\x18\x03 \x03(\x0b\x32\x10.command.Payload*\xbc\x02\n\x06Opcode\x12\x08\n\x04NONE\x10\x00\x12\x0b\n\x07VERSION\x10\x01\x12\x0e\n\nBLOCK_READ\x10\n\x12\x12\n\x0e\x42LOCK_READ_ALL\x10\x0b\x12\x0f\n\x0b\x42LOCK_WRITE\x10\x0c\x12\x10\n\x0c\x42LOCK_CREATE\x10\r\x12\x10\n\x0c\x42LOCK_DELETE\x10\x0e\x12\x12\n\x0e\x42LOCK_DISCOVER\x10\x0f\x12\x15\n\x11\x42LOCK_STORED_READ\x10\x10\x12\x19\n\x15\x42LOCK_STORED_READ_ALL\x10\x11\x12\x10\n\x0cSTORAGE_READ\x10\x14\x12\x14\n\x10STORAGE_READ_ALL\x10\x15\x12\n\n\x06REBOOT\x10\x1e\x12\x10\n\x0c\x43LEAR_BLOCKS\x10\x1f\x12\x0e\n\nCLEAR_WIFI\x10 \x12\x11\n\rFACTORY_RESET\x10!\x12\x13\n\x0f\x46IRMWARE_UPDATE\x10(*\xed\x05\n\tErrorCode\x12\x06\n\x02OK\x10\x00\x12\x11\n\rUNKNOWN_ERROR\x10\x01\x12\x12\n\x0eINVALID_OPCODE\x10\x02\x12\x15\n\x11INSUFFICIENT_HEAP\x10\x04\x12\x18\n\x14INSUFFICIENT_STORAGE\x10\x05\x12\x11\n\rNETWORK_ERROR\x10\n\x12\x16\n\x12NETWORK_READ_ERROR\x10\x0b\x12\x1a\n\x16NETWORK_DECODING_ERROR\x10\x0c\x12\x17\n\x13NETWORK_WRITE_ERROR\x10\r\x12\x1a\n\x16NETWORK_ENCODING_ERROR\x10\x0e\x12\x11\n\rSTORAGE_ERROR\x10\x14\x12\x16\n\x12STORAGE_READ_ERROR\x10\x15\x12\x1a\n\x16STORAGE_DECODING_ERROR\x10\x16\x12\x15\n\x11STORAGE_CRC_ERROR\x10\x17\x12\x17\n\x13STORAGE_WRITE_ERROR\x10\x18\x12\x1a\n\x16STORAGE_ENCODING_ERROR\x10\x19\x12\x16\n\x12\x42LOCK_NOT_WRITABLE\x10\x1e\x12\x16\n\x12\x42LOCK_NOT_READABLE\x10\x1f\x12\x17\n\x13\x42LOCK_NOT_CREATABLE\x10 \x12\x17\n\x13\x42LOCK_NOT_DELETABLE\x10!\x12\x11\n\rINVALID_BLOCK\x10(\x12\x14\n\x10INVALID_BLOCK_ID\x10)\x12\x16\n\x12INVALID_BLOCK_TYPE\x10*\x12\x19\n\x15INVALID_BLOCK_SUBTYPE\x10+\x12\x19\n\x15INVALID_BLOCK_CONTENT\x10,\x12\x18\n\x14INVALID_STORED_BLOCK\x10\x32\x12\x1b\n\x17INVALID_STORED_BLOCK_ID\x10\x33\x12\x1d\n\x19INVALID_STORED_BLOCK_TYPE\x10\x34\x12 \n\x1cINVALID_STORED_BLOCK_SUBTYPE\x10\x35\x12 \n\x1cINVALID_STORED_BLOCK_CONTENT\x10\x36*5\n\x08MaskMode\x12\x0b\n\x07NO_MASK\x10\x00\x12\r\n\tINCLUSIVE\x10\x01\x12\r\n\tEXCLUSIVE\x10\x02\x62\x06proto3') -_OPCODE = DESCRIPTOR.enum_types_by_name['Opcode'] -Opcode = enum_type_wrapper.EnumTypeWrapper(_OPCODE) -_ERRORCODE = DESCRIPTOR.enum_types_by_name['ErrorCode'] -ErrorCode = enum_type_wrapper.EnumTypeWrapper(_ERRORCODE) -_MASKMODE = DESCRIPTOR.enum_types_by_name['MaskMode'] -MaskMode = enum_type_wrapper.EnumTypeWrapper(_MASKMODE) -NONE = 0 -VERSION = 1 -BLOCK_READ = 10 -BLOCK_READ_ALL = 11 -BLOCK_WRITE = 12 -BLOCK_CREATE = 13 -BLOCK_DELETE = 14 -BLOCK_DISCOVER = 15 -BLOCK_STORED_READ = 16 -BLOCK_STORED_READ_ALL = 17 -STORAGE_READ = 20 -STORAGE_READ_ALL = 21 -REBOOT = 30 -CLEAR_BLOCKS = 31 -CLEAR_WIFI = 32 -FACTORY_RESET = 33 -FIRMWARE_UPDATE = 40 -OK = 0 -UNKNOWN_ERROR = 1 -INVALID_OPCODE = 2 -INSUFFICIENT_HEAP = 4 -INSUFFICIENT_STORAGE = 5 -NETWORK_ERROR = 10 -NETWORK_READ_ERROR = 11 -NETWORK_DECODING_ERROR = 12 -NETWORK_WRITE_ERROR = 13 -NETWORK_ENCODING_ERROR = 14 -STORAGE_ERROR = 20 -STORAGE_READ_ERROR = 21 -STORAGE_DECODING_ERROR = 22 -STORAGE_CRC_ERROR = 23 -STORAGE_WRITE_ERROR = 24 -STORAGE_ENCODING_ERROR = 25 -BLOCK_NOT_WRITABLE = 30 -BLOCK_NOT_READABLE = 31 -BLOCK_NOT_CREATABLE = 32 -BLOCK_NOT_DELETABLE = 33 -INVALID_BLOCK = 40 -INVALID_BLOCK_ID = 41 -INVALID_BLOCK_TYPE = 42 -INVALID_BLOCK_SUBTYPE = 43 -INVALID_BLOCK_CONTENT = 44 -INVALID_STORED_BLOCK = 50 -INVALID_STORED_BLOCK_ID = 51 -INVALID_STORED_BLOCK_TYPE = 52 -INVALID_STORED_BLOCK_SUBTYPE = 53 -INVALID_STORED_BLOCK_CONTENT = 54 -NO_MASK = 0 -INCLUSIVE = 1 -EXCLUSIVE = 2 - - -_PAYLOAD = DESCRIPTOR.message_types_by_name['Payload'] -_REQUEST = DESCRIPTOR.message_types_by_name['Request'] -_RESPONSE = DESCRIPTOR.message_types_by_name['Response'] -Payload = _reflection.GeneratedProtocolMessageType('Payload', (_message.Message,), { - 'DESCRIPTOR' : _PAYLOAD, - '__module__' : 'command_pb2' - # @@protoc_insertion_point(class_scope:command.Payload) - }) -_sym_db.RegisterMessage(Payload) - -Request = _reflection.GeneratedProtocolMessageType('Request', (_message.Message,), { - 'DESCRIPTOR' : _REQUEST, - '__module__' : 'command_pb2' - # @@protoc_insertion_point(class_scope:command.Request) - }) -_sym_db.RegisterMessage(Request) - -Response = _reflection.GeneratedProtocolMessageType('Response', (_message.Message,), { - 'DESCRIPTOR' : _RESPONSE, - '__module__' : 'command_pb2' - # @@protoc_insertion_point(class_scope:command.Response) - }) -_sym_db.RegisterMessage(Response) - +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'command_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None @@ -110,16 +29,16 @@ _PAYLOAD.fields_by_name['subtype']._serialized_options = b'\222?\0028\020' _PAYLOAD.fields_by_name['mask']._options = None _PAYLOAD.fields_by_name['mask']._serialized_options = b'\222?\0028\020' - _OPCODE._serialized_start=423 - _OPCODE._serialized_end=739 - _ERRORCODE._serialized_start=742 - _ERRORCODE._serialized_end=1491 - _MASKMODE._serialized_start=1493 - _MASKMODE._serialized_end=1546 - _PAYLOAD._serialized_start=57 - _PAYLOAD._serialized_end=229 - _REQUEST._serialized_start=231 - _REQUEST._serialized_end=323 - _RESPONSE._serialized_start=325 - _RESPONSE._serialized_end=420 + _globals['_OPCODE']._serialized_start=423 + _globals['_OPCODE']._serialized_end=739 + _globals['_ERRORCODE']._serialized_start=742 + _globals['_ERRORCODE']._serialized_end=1491 + _globals['_MASKMODE']._serialized_start=1493 + _globals['_MASKMODE']._serialized_end=1546 + _globals['_PAYLOAD']._serialized_start=57 + _globals['_PAYLOAD']._serialized_end=229 + _globals['_REQUEST']._serialized_start=231 + _globals['_REQUEST']._serialized_end=323 + _globals['_RESPONSE']._serialized_start=325 + _globals['_RESPONSE']._serialized_end=420 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/nanopb_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/nanopb_pb2.py index 5db30586..17fc7dc5 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/nanopb_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/nanopb_pb2.py @@ -2,12 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: nanopb.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -18,52 +16,9 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cnanopb.proto\x1a google/protobuf/descriptor.proto\"\xa4\x07\n\rNanoPBOptions\x12\x10\n\x08max_size\x18\x01 \x01(\x05\x12\x12\n\nmax_length\x18\x0e \x01(\x05\x12\x11\n\tmax_count\x18\x02 \x01(\x05\x12&\n\x08int_size\x18\x07 \x01(\x0e\x32\x08.IntSize:\nIS_DEFAULT\x12$\n\x04type\x18\x03 \x01(\x0e\x32\n.FieldType:\nFT_DEFAULT\x12\x18\n\nlong_names\x18\x04 \x01(\x08:\x04true\x12\x1c\n\rpacked_struct\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x0bpacked_enum\x18\n \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0cskip_message\x18\x06 \x01(\x08:\x05\x66\x61lse\x12\x18\n\tno_unions\x18\x08 \x01(\x08:\x05\x66\x61lse\x12\r\n\x05msgid\x18\t \x01(\r\x12\x1e\n\x0f\x61nonymous_oneof\x18\x0b \x01(\x08:\x05\x66\x61lse\x12\x15\n\x06proto3\x18\x0c \x01(\x08:\x05\x66\x61lse\x12#\n\x14proto3_singular_msgs\x18\x15 \x01(\x08:\x05\x66\x61lse\x12\x1d\n\x0e\x65num_to_string\x18\r \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0c\x66ixed_length\x18\x0f \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x0b\x66ixed_count\x18\x10 \x01(\x08:\x05\x66\x61lse\x12\x1e\n\x0fsubmsg_callback\x18\x16 \x01(\x08:\x05\x66\x61lse\x12/\n\x0cmangle_names\x18\x11 \x01(\x0e\x32\x11.TypenameMangling:\x06M_NONE\x12(\n\x11\x63\x61llback_datatype\x18\x12 \x01(\t:\rpb_callback_t\x12\x34\n\x11\x63\x61llback_function\x18\x13 \x01(\t:\x19pb_default_field_callback\x12\x30\n\x0e\x64\x65scriptorsize\x18\x14 \x01(\x0e\x32\x0f.DescriptorSize:\x07\x44S_AUTO\x12\x1a\n\x0b\x64\x65\x66\x61ult_has\x18\x17 \x01(\x08:\x05\x66\x61lse\x12\x0f\n\x07include\x18\x18 \x03(\t\x12\x0f\n\x07\x65xclude\x18\x1a \x03(\t\x12\x0f\n\x07package\x18\x19 \x01(\t\x12\x41\n\rtype_override\x18\x1b \x01(\x0e\x32*.google.protobuf.FieldDescriptorProto.Type\x12\x19\n\x0bsort_by_tag\x18\x1c \x01(\x08:\x04true\x12.\n\rfallback_type\x18\x1d \x01(\x0e\x32\n.FieldType:\x0b\x46T_CALLBACK*i\n\tFieldType\x12\x0e\n\nFT_DEFAULT\x10\x00\x12\x0f\n\x0b\x46T_CALLBACK\x10\x01\x12\x0e\n\nFT_POINTER\x10\x04\x12\r\n\tFT_STATIC\x10\x02\x12\r\n\tFT_IGNORE\x10\x03\x12\r\n\tFT_INLINE\x10\x05*D\n\x07IntSize\x12\x0e\n\nIS_DEFAULT\x10\x00\x12\x08\n\x04IS_8\x10\x08\x12\t\n\x05IS_16\x10\x10\x12\t\n\x05IS_32\x10 \x12\t\n\x05IS_64\x10@*Z\n\x10TypenameMangling\x12\n\n\x06M_NONE\x10\x00\x12\x13\n\x0fM_STRIP_PACKAGE\x10\x01\x12\r\n\tM_FLATTEN\x10\x02\x12\x16\n\x12M_PACKAGE_INITIALS\x10\x03*E\n\x0e\x44\x65scriptorSize\x12\x0b\n\x07\x44S_AUTO\x10\x00\x12\x08\n\x04\x44S_1\x10\x01\x12\x08\n\x04\x44S_2\x10\x02\x12\x08\n\x04\x44S_4\x10\x04\x12\x08\n\x04\x44S_8\x10\x08:E\n\x0enanopb_fileopt\x12\x1c.google.protobuf.FileOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:G\n\rnanopb_msgopt\x12\x1f.google.protobuf.MessageOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:E\n\x0enanopb_enumopt\x12\x1c.google.protobuf.EnumOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:>\n\x06nanopb\x12\x1d.google.protobuf.FieldOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptionsB\x1a\n\x18\x66i.kapsi.koti.jpa.nanopb') -_FIELDTYPE = DESCRIPTOR.enum_types_by_name['FieldType'] -FieldType = enum_type_wrapper.EnumTypeWrapper(_FIELDTYPE) -_INTSIZE = DESCRIPTOR.enum_types_by_name['IntSize'] -IntSize = enum_type_wrapper.EnumTypeWrapper(_INTSIZE) -_TYPENAMEMANGLING = DESCRIPTOR.enum_types_by_name['TypenameMangling'] -TypenameMangling = enum_type_wrapper.EnumTypeWrapper(_TYPENAMEMANGLING) -_DESCRIPTORSIZE = DESCRIPTOR.enum_types_by_name['DescriptorSize'] -DescriptorSize = enum_type_wrapper.EnumTypeWrapper(_DESCRIPTORSIZE) -FT_DEFAULT = 0 -FT_CALLBACK = 1 -FT_POINTER = 4 -FT_STATIC = 2 -FT_IGNORE = 3 -FT_INLINE = 5 -IS_DEFAULT = 0 -IS_8 = 8 -IS_16 = 16 -IS_32 = 32 -IS_64 = 64 -M_NONE = 0 -M_STRIP_PACKAGE = 1 -M_FLATTEN = 2 -M_PACKAGE_INITIALS = 3 -DS_AUTO = 0 -DS_1 = 1 -DS_2 = 2 -DS_4 = 4 -DS_8 = 8 - -NANOPB_FILEOPT_FIELD_NUMBER = 1010 -nanopb_fileopt = DESCRIPTOR.extensions_by_name['nanopb_fileopt'] -NANOPB_MSGOPT_FIELD_NUMBER = 1010 -nanopb_msgopt = DESCRIPTOR.extensions_by_name['nanopb_msgopt'] -NANOPB_ENUMOPT_FIELD_NUMBER = 1010 -nanopb_enumopt = DESCRIPTOR.extensions_by_name['nanopb_enumopt'] -NANOPB_FIELD_NUMBER = 1010 -nanopb = DESCRIPTOR.extensions_by_name['nanopb'] - -_NANOPBOPTIONS = DESCRIPTOR.message_types_by_name['NanoPBOptions'] -NanoPBOptions = _reflection.GeneratedProtocolMessageType('NanoPBOptions', (_message.Message,), { - 'DESCRIPTOR' : _NANOPBOPTIONS, - '__module__' : 'nanopb_pb2' - # @@protoc_insertion_point(class_scope:NanoPBOptions) - }) -_sym_db.RegisterMessage(NanoPBOptions) - +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'nanopb_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: google_dot_protobuf_dot_descriptor__pb2.FileOptions.RegisterExtension(nanopb_fileopt) google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(nanopb_msgopt) @@ -72,14 +27,14 @@ DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\030fi.kapsi.koti.jpa.nanopb' - _FIELDTYPE._serialized_start=985 - _FIELDTYPE._serialized_end=1090 - _INTSIZE._serialized_start=1092 - _INTSIZE._serialized_end=1160 - _TYPENAMEMANGLING._serialized_start=1162 - _TYPENAMEMANGLING._serialized_end=1252 - _DESCRIPTORSIZE._serialized_start=1254 - _DESCRIPTORSIZE._serialized_end=1323 - _NANOPBOPTIONS._serialized_start=51 - _NANOPBOPTIONS._serialized_end=983 + _globals['_FIELDTYPE']._serialized_start=985 + _globals['_FIELDTYPE']._serialized_end=1090 + _globals['_INTSIZE']._serialized_start=1092 + _globals['_INTSIZE']._serialized_end=1160 + _globals['_TYPENAMEMANGLING']._serialized_start=1162 + _globals['_TYPENAMEMANGLING']._serialized_end=1252 + _globals['_DESCRIPTORSIZE']._serialized_start=1254 + _globals['_DESCRIPTORSIZE']._serialized_end=1323 + _globals['_NANOPBOPTIONS']._serialized_start=51 + _globals['_NANOPBOPTIONS']._serialized_end=983 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/sequence.py b/brewblox_devcon_spark/codec/sequence.py index 0ffe7aa2..96926d2f 100644 --- a/brewblox_devcon_spark/codec/sequence.py +++ b/brewblox_devcon_spark/codec/sequence.py @@ -8,10 +8,14 @@ from datetime import timedelta from typing import Any +from google.protobuf.descriptor import Descriptor, FieldDescriptor + from brewblox_devcon_spark.codec import bloxfield, time_utils from brewblox_devcon_spark.codec.pb2 import Sequence_pb2, brewblox_pb2 from brewblox_devcon_spark.models import Block +INSTRUCTION_MSG_DESC: Descriptor = Sequence_pb2.Instruction.DESCRIPTOR + def from_line(line: str, line_num: int) -> dict: """ @@ -29,13 +33,40 @@ def from_line(line: str, line_num: int) -> dict: args = '' try: - instruction_field = getattr(Sequence_pb2.Instruction, opcode) - arg_fields_by_name = instruction_field.DESCRIPTOR.message_type.fields_by_name - except AttributeError: + # The generic `Instruction` is a big oneof in proto, with all opcodes mapped to fields + # `fields_by_name[opcode]` yields the descriptor of a opcode field in the Instruction message + # + # Message Instruction { + # oneof instruction_oneof { + # ... + # WaitDuration WAIT_DURATION = 4; # <-- `opcode_field_desc` for opcode 'WAIT_DURATION' + # ... + # } + # } + # + opcode_field_desc: FieldDescriptor = INSTRUCTION_MSG_DESC.fields_by_name[opcode] + + # The descriptor for the specific instruction message + # + # message WaitDuration { # <-- `opcode_msg_desc` for opcode 'WAIT_DURATION' + # uint32 duration = 1 + # [ (brewblox.field).unit = Second, (nanopb).int_size = IS_32 ]; + # } + opcode_msg_desc: Descriptor = opcode_field_desc.message_type + + # The fields in the specific instruction message + # Here, the arguments for the specific instruction are declared + # + # message WaitDuration { + # uint32 duration = 1 # <-- `opcode_arg_field_descs['duration']` for opcode 'WAIT_DURATION' + # [ (brewblox.field).unit = Second, (nanopb).int_size = IS_32 ]; + # } + opcode_arg_field_descs: dict[str, FieldDescriptor] = opcode_msg_desc.fields_by_name + except KeyError: raise ValueError(f'line {line_num}: Invalid instruction name: `{opcode}`') def parse_arg_value(key: str, value: str) -> Any: - field_desc = arg_fields_by_name.get(key) + field_desc = opcode_arg_field_descs.get(key) if '=' in value: raise ValueError(f'line {line_num}: Missing argument separator: `{key}={value}`') @@ -108,7 +139,7 @@ def parse_arg_value(key: str, value: str) -> Any: for key, value in argdict.items() } - if missing := set(arg_fields_by_name.keys()) - set(parsed.keys()): + if missing := set(opcode_arg_field_descs.keys()) - set(parsed.keys()): raise ValueError(f'line {line_num}: Missing arguments: `{", ".join(missing)}`') return {opcode: parsed} diff --git a/poetry.lock b/poetry.lock index 40da8767..b21e8e00 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,14 +2,14 @@ [[package]] name = "aiofiles" -version = "0.8.0" +version = "23.2.1" description = "File support for asyncio." category = "main" optional = false -python-versions = ">=3.6,<4.0" +python-versions = ">=3.7" files = [ - {file = "aiofiles-0.8.0-py3-none-any.whl", hash = "sha256:7a973fc22b29e9962d0897805ace5856e6a566ab1f0c8e5c91ff6c866519c937"}, - {file = "aiofiles-0.8.0.tar.gz", hash = "sha256:8334f23235248a3b2e83b2c3a78a22674f39969b96397126cc93664d9a901e59"}, + {file = "aiofiles-23.2.1-py3-none-any.whl", hash = "sha256:19297512c647d4b27a2cf7c34caa7e405c0d60b5560618a29a9fe027b18b0107"}, + {file = "aiofiles-23.2.1.tar.gz", hash = "sha256:84ec2218d8419404abcb9f0c02df3f34c6e0a68ed41072acfb1cef5cbc29051a"}, ] [[package]] @@ -143,14 +143,14 @@ test = ["pytest (==6.1.2)", "pytest-aiohttp (==0.3.0)", "pytest-cov (==2.10.1)", [[package]] name = "aiomqtt" -version = "1.0.0" +version = "1.2.0" description = "The idiomatic asyncio MQTT client, wrapped around paho-mqtt" category = "main" optional = false -python-versions = ">=3.7,<4.0" +python-versions = ">=3.8,<4.0" files = [ - {file = "aiomqtt-1.0.0-py3-none-any.whl", hash = "sha256:dd6fe629c10ac24a3d2abc2bcc09afe4f66bc06b6457b63cf764cc81f3b980fe"}, - {file = "aiomqtt-1.0.0.tar.gz", hash = "sha256:a96c4af50f54ded0c07d4dfc14aa3e212265cbebf659e2ab64950cf14ba8dca2"}, + {file = "aiomqtt-1.2.0-py3-none-any.whl", hash = "sha256:756879c415c3c89a380bec2b283c165d9e7cc581d490cf29f7e85c28ff0494c5"}, + {file = "aiomqtt-1.2.0.tar.gz", hash = "sha256:aad3da59aa77e12e28144a2fdccd60c840d9c2709fc287894696935974270ca2"}, ] [package.dependencies] @@ -204,14 +204,14 @@ pytest-asyncio = "*" [[package]] name = "async-timeout" -version = "4.0.2" +version = "4.0.3" description = "Timeout context manager for asyncio programs" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"}, - {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, ] [[package]] @@ -235,49 +235,163 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte [[package]] name = "autopep8" -version = "2.0.2" +version = "2.0.4" description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" category = "dev" optional = false python-versions = ">=3.6" files = [ - {file = "autopep8-2.0.2-py2.py3-none-any.whl", hash = "sha256:86e9303b5e5c8160872b2f5ef611161b2893e9bfe8ccc7e2f76385947d57a2f1"}, - {file = "autopep8-2.0.2.tar.gz", hash = "sha256:f9849cdd62108cb739dbcdbfb7fdcc9a30d1b63c4cc3e1c1f893b5360941b61c"}, + {file = "autopep8-2.0.4-py2.py3-none-any.whl", hash = "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb"}, + {file = "autopep8-2.0.4.tar.gz", hash = "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c"}, ] [package.dependencies] pycodestyle = ">=2.10.0" tomli = {version = "*", markers = "python_version < \"3.11\""} +[[package]] +name = "bitarray" +version = "2.8.1" +description = "efficient arrays of booleans -- C extension" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "bitarray-2.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6be965028785413a6163dd55a639b898b22f67f9b6ed554081c23e94a602031e"}, + {file = "bitarray-2.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29e19cb80a69f6d1a64097bfbe1766c418e1a785d901b583ef0328ea10a30399"}, + {file = "bitarray-2.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0f6d705860f59721d7282496a4d29b5fd78690e1c1473503832c983e762b01b"}, + {file = "bitarray-2.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6df04efdba4e1bf9d93a1735e42005f8fcf812caf40c03934d9322412d563499"}, + {file = "bitarray-2.8.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:18530ed3ddd71e9ff95440afce531efc3df7a3e0657f1c201c2c3cb41dd65869"}, + {file = "bitarray-2.8.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4cd81ffd2d58ef68c22c825aff89f4a47bd721e2ada0a3a96793169f370ae21"}, + {file = "bitarray-2.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8367768ab797105eb97dfbd4577fcde281618de4d8d3b16ad62c477bb065f347"}, + {file = "bitarray-2.8.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:848af80518d0ed2aee782018588c7c88805f51b01271935df5b256c8d81c726e"}, + {file = "bitarray-2.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c54b0af16be45de534af9d77e8a180126cd059f72db8b6550f62dda233868942"}, + {file = "bitarray-2.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f30cdce22af3dc7c73e70af391bfd87c4574cc40c74d651919e20efc26e014b5"}, + {file = "bitarray-2.8.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:bc03bb358ae3917247d257207c79162e666d407ac473718d1b95316dac94162b"}, + {file = "bitarray-2.8.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:cf38871ed4cd89df9db7c70f729b948fa3e2848a07c69f78e4ddfbe4f23db63c"}, + {file = "bitarray-2.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4a637bcd199c1366c65b98f18884f0d0b87403f04676b21e4635831660d722a7"}, + {file = "bitarray-2.8.1-cp310-cp310-win32.whl", hash = "sha256:904719fb7304d4115228b63c178f0cc725ad3b73e285c4b328e45a99a8e3fad6"}, + {file = "bitarray-2.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:1e859c664500d57526fe07140889a3b58dca54ff3b16ac6dc6d534a65c933084"}, + {file = "bitarray-2.8.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2d3f28a80f2e6bb96e9360a4baf3fbacb696b5aba06a14c18a15488d4b6f398f"}, + {file = "bitarray-2.8.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4677477a406f2a9e064920463f69172b865e4d69117e1f2160064d3f5912b0bd"}, + {file = "bitarray-2.8.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9061c0a50216f24c97fb2325de84200e5ad5555f25c854ddcb3ceb6f12136055"}, + {file = "bitarray-2.8.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:843af12991161b358b6379a8dc5f6636798f3dacdae182d30995b6a2df3b263e"}, + {file = "bitarray-2.8.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9336300fd0acf07ede92e424930176dc4b43ef1b298489e93ba9a1695e8ea752"}, + {file = "bitarray-2.8.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0af01e1f61fe627f63648c0c6f52de8eac56710a2ef1dbce4851d867084cc7e"}, + {file = "bitarray-2.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ab81c74a1805fe74330859b38e70d7525cdd80953461b59c06660046afaffcf"}, + {file = "bitarray-2.8.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2015a9dd718393e814ff7b9e80c58190eb1cef7980f86a97a33e8440e158ce2"}, + {file = "bitarray-2.8.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b0493ab66c6b8e17e9fde74c646b39ee09c236cf28a787cb8cbd3a83c05bff7"}, + {file = "bitarray-2.8.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:81e83ed7e0b1c09c5a33b97712da89e7a21fd3e5598eff3975c39540f5619792"}, + {file = "bitarray-2.8.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:741c3a2c0997c8f8878edfc65a4a8f7aa72eede337c9bc0b7bd8a45cf6e70dbc"}, + {file = "bitarray-2.8.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:57aeab27120a8a50917845bb81b0976e33d4759f2156b01359e2b43d445f5127"}, + {file = "bitarray-2.8.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:17c32ba584e8fb9322419390e0e248769ed7d59de3ffa7432562a4c0ec4f1f82"}, + {file = "bitarray-2.8.1-cp311-cp311-win32.whl", hash = "sha256:b67733a240a96f09b7597af97ac4d60c59140cfcfd180f11a7221863b82f023a"}, + {file = "bitarray-2.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:7b29d4bf3d3da1847f2be9e30105bf51caaf5922e94dc827653e250ed33f4e8a"}, + {file = "bitarray-2.8.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:5f6175c1cf07dadad3213d60075704cf2e2f1232975cfd4ac8328c24a05e8f78"}, + {file = "bitarray-2.8.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cc066c7290151600b8872865708d2d00fb785c5db8a0df20d70d518e02f172b"}, + {file = "bitarray-2.8.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ce2ef9291a193a0e0cd5e23970bf3b682cc8b95220561d05b775b8d616d665f"}, + {file = "bitarray-2.8.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c5582dd7d906e6f9ec1704f99d56d812f7d395d28c02262bc8b50834d51250c3"}, + {file = "bitarray-2.8.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2aa2267eb6d2b88ef7d139e79a6daaa84cd54d241b9797478f10dcb95a9cd620"}, + {file = "bitarray-2.8.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a04d4851e83730f03c4a6aac568c7d8b42f78f0f9cc8231d6db66192b030ce1e"}, + {file = "bitarray-2.8.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:f7d2ec2174d503cbb092f8353527842633c530b4e03b9922411640ac9c018a19"}, + {file = "bitarray-2.8.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:b65a04b2e029b0694b52d60786732afd15b1ec6517de61a36afbb7808a2ffac1"}, + {file = "bitarray-2.8.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:55020d6fb9b72bd3606969f5431386c592ed3666133bd475af945aa0fa9e84ec"}, + {file = "bitarray-2.8.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:797de3465f5f6c6be9a412b4e99eb6e8cdb86b83b6756655c4d83a65d0b9a376"}, + {file = "bitarray-2.8.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:f9a66745682e175e143a180524a63e692acb2b8c86941073f6dd4ee906e69608"}, + {file = "bitarray-2.8.1-cp36-cp36m-win32.whl", hash = "sha256:443726af4bd60515e4e41ea36c5dbadb29a59bc799bcbf431011d1c6fd4363e3"}, + {file = "bitarray-2.8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:2b0f754a5791635b8239abdcc0258378111b8ee7a8eb3e2bbc24bcc48a0f0b08"}, + {file = "bitarray-2.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d175e16419a52d54c0ac44c93309ba76dc2cfd33ee9d20624f1a5eb86b8e162e"}, + {file = "bitarray-2.8.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3128234bde3629ab301a501950587e847d30031a9cbf04d95f35cbf44469a9e"}, + {file = "bitarray-2.8.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75104c3076676708c1ac2484ebf5c26464fb3850312de33a5b5bf61bfa7dbec5"}, + {file = "bitarray-2.8.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82bfb6ab9b1b5451a5483c9a2ae2a8f83799d7503b384b54f6ab56ea74abb305"}, + {file = "bitarray-2.8.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dc064a63445366f6b26eaf77230d326b9463e903ba59d6ff5efde0c5ec1ea0e"}, + {file = "bitarray-2.8.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cbe54685cf6b17b3e15faf6c4b76773bc1c484bc447020737d2550a9dde5f6e6"}, + {file = "bitarray-2.8.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9fed8aba8d1b09cf641b50f1e6dd079c31677106ea4b63ec29f4c49adfabd63f"}, + {file = "bitarray-2.8.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7c17dd8fb146c2c680bf1cb28b358f9e52a14076e44141c5442148863ee95d7d"}, + {file = "bitarray-2.8.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:c9efcee311d9ba0c619743060585af9a9b81496e97b945843d5e954c67722a75"}, + {file = "bitarray-2.8.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dc7acffee09822b334d1b46cd384e969804abdf18f892c82c05c2328066cd2ae"}, + {file = "bitarray-2.8.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ea71e0a50060f96ad0821e0ac785e91e44807f8b69555970979d81934961d5bd"}, + {file = "bitarray-2.8.1-cp37-cp37m-win32.whl", hash = "sha256:69ab51d551d50e4d6ca35abc95c9d04b33ad28418019bb5481ab09bdbc0df15c"}, + {file = "bitarray-2.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:3024ab4c4906c3681408ca17c35833237d18813ebb9f24ae9f9e3157a4a66939"}, + {file = "bitarray-2.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:46fdd27c8fa4186d8b290bf74a28cbd91b94127b1b6a35c265a002e394fa9324"}, + {file = "bitarray-2.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d32ccd2c0d906eae103ef84015f0545a395052b0b6eb0e02e9023ca0132557f6"}, + {file = "bitarray-2.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9186cf8135ca170cd907d8c4df408a87747570d192d89ec4ff23805611c702a0"}, + {file = "bitarray-2.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8d6e5ff385fea25caf26fd58b43f087deb763dcaddd18d3df2895235cf1b484"}, + {file = "bitarray-2.8.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d6a9c72354327c7aa9890ff87904cbe86830cb1fb58c39750a0afac8df5e051"}, + {file = "bitarray-2.8.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2f13b7d0694ce2024c82fc595e6ccc3918e7f069747c3de41b1ce72a9a1e346"}, + {file = "bitarray-2.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d38ceca90ed538706e3f111513073590f723f90659a7af0b992b29776a6e816"}, + {file = "bitarray-2.8.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b977c39e3734e73540a2e3a71501c2c6261c70c6ce59d427bb7c4ecf6331c7e"}, + {file = "bitarray-2.8.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:214c05a7642040f6174e29f3e099549d3c40ac44616405081bf230dcafb38767"}, + {file = "bitarray-2.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ad440c17ef2ff42e94286186b5bcf82bf87c4026f91822675239102ebe1f7035"}, + {file = "bitarray-2.8.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:28dee92edd0d21655e56e1870c22468d0dabe557df18aa69f6d06b1543614180"}, + {file = "bitarray-2.8.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:df9d8a9a46c46950f306394705512553c552b633f8bf3c11359c4204289f11e3"}, + {file = "bitarray-2.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1a0d27aad02d8abcb1d3b7d85f463877c4937e71adf9b6adb9367f2cdad91a52"}, + {file = "bitarray-2.8.1-cp38-cp38-win32.whl", hash = "sha256:6033303431a7c85a535b3f1b0ec28abc2ebc2167c263f244993b56ccb87cae6b"}, + {file = "bitarray-2.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:9b65d487451e0e287565c8436cf4da45260f958f911299f6122a20d7ec76525c"}, + {file = "bitarray-2.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9aad7b4670f090734b272c072c9db375c63bd503512be9a9393e657dcacfc7e2"}, + {file = "bitarray-2.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bf80804014e3736515b84044c2be0e70080616b4ceddd4e38d85f3167aeb8165"}, + {file = "bitarray-2.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7f7231ef349e8f4955d9b39561f4683a418a73443cfce797a4eddbee1ba9664"}, + {file = "bitarray-2.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67e8fb18df51e649adbc81359e1db0f202d72708fba61b06f5ac8db47c08d107"}, + {file = "bitarray-2.8.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d5df3d6358425c9dfb6bdbd4f576563ec4173d24693a9042d05aadcb23c0b98"}, + {file = "bitarray-2.8.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6ea51ba4204d086d5b76e84c31d2acbb355ed1b075ded54eb9b7070b0b95415d"}, + {file = "bitarray-2.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1414582b3b7516d2282433f0914dd9846389b051b2aea592ae7cc165806c24ac"}, + {file = "bitarray-2.8.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5934e3a623a1d485e1dcfc1990246e3c32c6fc6e7f0fd894750800d35fdb5794"}, + {file = "bitarray-2.8.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:aa08a9b03888c768b9b2383949a942804d50d8164683b39fe62f0bfbfd9b4204"}, + {file = "bitarray-2.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:00ff372dfaced7dd6cc2dffd052fafc118053cf81a442992b9a23367479d77d7"}, + {file = "bitarray-2.8.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:dd76bbf5a4b2ab84b8ffa229f5648e80038ba76bf8d7acc5de9dd06031b38117"}, + {file = "bitarray-2.8.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e88a706f92ad1e0e1e66f6811d10b6155d5f18f0de9356ee899a7966a4e41992"}, + {file = "bitarray-2.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b2560475c5a1ff96fcab01fae7cf6b9a6da590f02659556b7fccc7991e401884"}, + {file = "bitarray-2.8.1-cp39-cp39-win32.whl", hash = "sha256:74cd1725d08325b6669e6e9a5d09cec29e7c41f7d58e082286af5387414d046d"}, + {file = "bitarray-2.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:e48c45ea7944225bcee026c457a70eaea61db3659d9603f07fc8a643ab7e633b"}, + {file = "bitarray-2.8.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c2426dc7a0d92d8254def20ab7a231626397ce5b6fb3d4f44be74cc1370a60c3"}, + {file = "bitarray-2.8.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d34790a919f165b6f537935280ef5224957d9ce8ab11d339f5e6d0319a683ccc"}, + {file = "bitarray-2.8.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c26a923080bc211cab8f5a5e242e3657b32951fec8980db0616e9239aade482"}, + {file = "bitarray-2.8.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0de1bc5f971aba46de88a4eb0dbb5779e30bbd7514f4dcbff743c209e0c02667"}, + {file = "bitarray-2.8.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3bb5f2954dd897b0bac13b5449e5c977534595b688120c8af054657a08b01f46"}, + {file = "bitarray-2.8.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:62ac31059a3c510ef64ed93d930581b262fd4592e6d95ede79fca91e8d3d3ef6"}, + {file = "bitarray-2.8.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae32ac7217e83646b9f64d7090bf7b737afaa569665621f110a05d9738ca841a"}, + {file = "bitarray-2.8.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3994f7dc48d21af40c0d69fca57d8040b02953f4c7c3652c2341d8947e9cbedf"}, + {file = "bitarray-2.8.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c361201e1c3ee6d6b2266f8b7a645389880bccab1b29e22e7a6b7b6e7831ad5"}, + {file = "bitarray-2.8.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:861850d6a58e7b6a7096d0b0efed9c6d993a6ab8b9d01e781df1f4d80cc00efa"}, + {file = "bitarray-2.8.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ee772c20dcb56b03d666a4e4383d0b5b942b0ccc27815e42fe0737b34cba2082"}, + {file = "bitarray-2.8.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63fa75e87ad8c57d5722cc87902ca148ef8bbbba12b5c5b3c3730a1bc9ac2886"}, + {file = "bitarray-2.8.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b999fb66980f885961d197d97d7ff5a13b7ab524ccf45ccb4704f4b82ce02e3"}, + {file = "bitarray-2.8.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3243e4b8279ff2fe4c6e7869f0e6930c17799ee9f8d07317f68d44a66b46281e"}, + {file = "bitarray-2.8.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:542358b178b025dcc95e7fb83389e9954f701c41d312cbb66bdd763cbe5414b5"}, + {file = "bitarray-2.8.1.tar.gz", hash = "sha256:e68ceef35a88625d16169550768fcc8d3894913e363c24ecbf6b8c07eb02c8f3"}, +] + [[package]] name = "bitstring" -version = "4.0.2" +version = "4.1.2" description = "Simple construction, analysis and modification of binary data." category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "bitstring-4.0.2-py3-none-any.whl", hash = "sha256:f93893791e8a6ecb02adcc5c191aaa964a06a9e1449e326cfb4f49ff5198e588"}, - {file = "bitstring-4.0.2.tar.gz", hash = "sha256:a391db8828ac4485dd5ce72c80b27ebac3e7b989631359959e310cd9729723b2"}, + {file = "bitstring-4.1.2-py3-none-any.whl", hash = "sha256:bc0dbdb282099d5c6fbf995cd7261449ddeb7bbf042996e503471a35eb2efaa3"}, + {file = "bitstring-4.1.2.tar.gz", hash = "sha256:c22283d60fd3e1a8f386ccd4f1915d7fe13481d6349db39711421e24d4a9cccf"}, ] +[package.dependencies] +bitarray = ">=2.8.0,<3.0.0" + [[package]] name = "brewblox-service" -version = "3.1.2" +version = "3.3.1" description = "Scaffolding for Brewblox backend services" category = "main" optional = false python-versions = ">=3.9,<4" files = [ - {file = "brewblox_service-3.1.2-py3-none-any.whl", hash = "sha256:4e72417824083d9745bb51df538a99a76aaf774d53623aaa570330df128dc6fe"}, - {file = "brewblox_service-3.1.2.tar.gz", hash = "sha256:29bd803531b6be1309a880e5d8ad4eeecdbe2a5ca7ade6334635420a544ef9db"}, + {file = "brewblox_service-3.3.1-py3-none-any.whl", hash = "sha256:38db50f18850a49dbb9803d49719a8b8db484218b396b709e651d4e0fdf48e04"}, + {file = "brewblox_service-3.3.1.tar.gz", hash = "sha256:4a37dbe96955e64c9d86e7fc8623d70219e6cfe037bf9bafac8fae5e2e6b1712"}, ] [package.dependencies] aiohttp = ">=3.0.0,<4.0.0" aiohttp-pydantic = ">=1.0.0,<2.0.0" aiomqtt = ">=1.0.0,<2.0.0" -cryptography = "40.0.1" pydantic = ">=1.0.0,<2.0.0" [[package]] @@ -511,72 +625,64 @@ files = [ [[package]] name = "coverage" -version = "7.2.7" +version = "7.3.1" description = "Code coverage measurement for Python" category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"}, - {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"}, - {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"}, - {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"}, - {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"}, - {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"}, - {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"}, - {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"}, - {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"}, - {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"}, - {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"}, - {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"}, - {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"}, - {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"}, - {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"}, - {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"}, - {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"}, - {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"}, - {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"}, - {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"}, - {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"}, - {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"}, - {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"}, - {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"}, - {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"}, - {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"}, - {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"}, - {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"}, - {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"}, - {file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"}, - {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"}, - {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"}, - {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"}, - {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"}, - {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"}, - {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"}, - {file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"}, - {file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"}, - {file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"}, - {file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"}, - {file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"}, - {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"}, - {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"}, - {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"}, - {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"}, - {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"}, - {file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"}, - {file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"}, - {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"}, - {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"}, - {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"}, - {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"}, - {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"}, - {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"}, - {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"}, - {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"}, - {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"}, - {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"}, - {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"}, - {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"}, + {file = "coverage-7.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cd0f7429ecfd1ff597389907045ff209c8fdb5b013d38cfa7c60728cb484b6e3"}, + {file = "coverage-7.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:966f10df9b2b2115da87f50f6a248e313c72a668248be1b9060ce935c871f276"}, + {file = "coverage-7.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0575c37e207bb9b98b6cf72fdaaa18ac909fb3d153083400c2d48e2e6d28bd8e"}, + {file = "coverage-7.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:245c5a99254e83875c7fed8b8b2536f040997a9b76ac4c1da5bff398c06e860f"}, + {file = "coverage-7.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c96dd7798d83b960afc6c1feb9e5af537fc4908852ef025600374ff1a017392"}, + {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:de30c1aa80f30af0f6b2058a91505ea6e36d6535d437520067f525f7df123887"}, + {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:50dd1e2dd13dbbd856ffef69196781edff26c800a74f070d3b3e3389cab2600d"}, + {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9c0c19f70d30219113b18fe07e372b244fb2a773d4afde29d5a2f7930765136"}, + {file = "coverage-7.3.1-cp310-cp310-win32.whl", hash = "sha256:770f143980cc16eb601ccfd571846e89a5fe4c03b4193f2e485268f224ab602f"}, + {file = "coverage-7.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:cdd088c00c39a27cfa5329349cc763a48761fdc785879220d54eb785c8a38520"}, + {file = "coverage-7.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:74bb470399dc1989b535cb41f5ca7ab2af561e40def22d7e188e0a445e7639e3"}, + {file = "coverage-7.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:025ded371f1ca280c035d91b43252adbb04d2aea4c7105252d3cbc227f03b375"}, + {file = "coverage-7.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6191b3a6ad3e09b6cfd75b45c6aeeffe7e3b0ad46b268345d159b8df8d835f9"}, + {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7eb0b188f30e41ddd659a529e385470aa6782f3b412f860ce22b2491c89b8593"}, + {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c8f0df9dfd8ff745bccff75867d63ef336e57cc22b2908ee725cc552689ec8"}, + {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7eb3cd48d54b9bd0e73026dedce44773214064be93611deab0b6a43158c3d5a0"}, + {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ac3c5b7e75acac31e490b7851595212ed951889918d398b7afa12736c85e13ce"}, + {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b4ee7080878077af0afa7238df1b967f00dc10763f6e1b66f5cced4abebb0a3"}, + {file = "coverage-7.3.1-cp311-cp311-win32.whl", hash = "sha256:229c0dd2ccf956bf5aeede7e3131ca48b65beacde2029f0361b54bf93d36f45a"}, + {file = "coverage-7.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:c6f55d38818ca9596dc9019eae19a47410d5322408140d9a0076001a3dcb938c"}, + {file = "coverage-7.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5289490dd1c3bb86de4730a92261ae66ea8d44b79ed3cc26464f4c2cde581fbc"}, + {file = "coverage-7.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca833941ec701fda15414be400c3259479bfde7ae6d806b69e63b3dc423b1832"}, + {file = "coverage-7.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd694e19c031733e446c8024dedd12a00cda87e1c10bd7b8539a87963685e969"}, + {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aab8e9464c00da5cb9c536150b7fbcd8850d376d1151741dd0d16dfe1ba4fd26"}, + {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87d38444efffd5b056fcc026c1e8d862191881143c3aa80bb11fcf9dca9ae204"}, + {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8a07b692129b8a14ad7a37941a3029c291254feb7a4237f245cfae2de78de037"}, + {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2829c65c8faaf55b868ed7af3c7477b76b1c6ebeee99a28f59a2cb5907a45760"}, + {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1f111a7d85658ea52ffad7084088277135ec5f368457275fc57f11cebb15607f"}, + {file = "coverage-7.3.1-cp312-cp312-win32.whl", hash = "sha256:c397c70cd20f6df7d2a52283857af622d5f23300c4ca8e5bd8c7a543825baa5a"}, + {file = "coverage-7.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:5ae4c6da8b3d123500f9525b50bf0168023313963e0e2e814badf9000dd6ef92"}, + {file = "coverage-7.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ca70466ca3a17460e8fc9cea7123c8cbef5ada4be3140a1ef8f7b63f2f37108f"}, + {file = "coverage-7.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f2781fd3cabc28278dc982a352f50c81c09a1a500cc2086dc4249853ea96b981"}, + {file = "coverage-7.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6407424621f40205bbe6325686417e5e552f6b2dba3535dd1f90afc88a61d465"}, + {file = "coverage-7.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04312b036580ec505f2b77cbbdfb15137d5efdfade09156961f5277149f5e344"}, + {file = "coverage-7.3.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac9ad38204887349853d7c313f53a7b1c210ce138c73859e925bc4e5d8fc18e7"}, + {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:53669b79f3d599da95a0afbef039ac0fadbb236532feb042c534fbb81b1a4e40"}, + {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:614f1f98b84eb256e4f35e726bfe5ca82349f8dfa576faabf8a49ca09e630086"}, + {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f1a317fdf5c122ad642db8a97964733ab7c3cf6009e1a8ae8821089993f175ff"}, + {file = "coverage-7.3.1-cp38-cp38-win32.whl", hash = "sha256:defbbb51121189722420a208957e26e49809feafca6afeef325df66c39c4fdb3"}, + {file = "coverage-7.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:f4f456590eefb6e1b3c9ea6328c1e9fa0f1006e7481179d749b3376fc793478e"}, + {file = "coverage-7.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f12d8b11a54f32688b165fd1a788c408f927b0960984b899be7e4c190ae758f1"}, + {file = "coverage-7.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f09195dda68d94a53123883de75bb97b0e35f5f6f9f3aa5bf6e496da718f0cb6"}, + {file = "coverage-7.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6601a60318f9c3945be6ea0f2a80571f4299b6801716f8a6e4846892737ebe4"}, + {file = "coverage-7.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07d156269718670d00a3b06db2288b48527fc5f36859425ff7cec07c6b367745"}, + {file = "coverage-7.3.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:636a8ac0b044cfeccae76a36f3b18264edcc810a76a49884b96dd744613ec0b7"}, + {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5d991e13ad2ed3aced177f524e4d670f304c8233edad3210e02c465351f785a0"}, + {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:586649ada7cf139445da386ab6f8ef00e6172f11a939fc3b2b7e7c9082052fa0"}, + {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4aba512a15a3e1e4fdbfed2f5392ec221434a614cc68100ca99dcad7af29f3f8"}, + {file = "coverage-7.3.1-cp39-cp39-win32.whl", hash = "sha256:6bc6f3f4692d806831c136c5acad5ccedd0262aa44c087c46b7101c77e139140"}, + {file = "coverage-7.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:553d7094cb27db58ea91332e8b5681bac107e7242c23f7629ab1316ee73c4981"}, + {file = "coverage-7.3.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:220eb51f5fb38dfdb7e5d54284ca4d0cd70ddac047d750111a68ab1798945194"}, + {file = "coverage-7.3.1.tar.gz", hash = "sha256:6cb7fe1581deb67b782c153136541e20901aa312ceedaf1467dcb35255787952"}, ] [package.dependencies] @@ -587,31 +693,35 @@ toml = ["tomli"] [[package]] name = "cryptography" -version = "40.0.1" +version = "41.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "cryptography-40.0.1-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:918cb89086c7d98b1b86b9fdb70c712e5a9325ba6f7d7cfb509e784e0cfc6917"}, - {file = "cryptography-40.0.1-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:9618a87212cb5200500e304e43691111570e1f10ec3f35569fdfcd17e28fd797"}, - {file = "cryptography-40.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a4805a4ca729d65570a1b7cac84eac1e431085d40387b7d3bbaa47e39890b88"}, - {file = "cryptography-40.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63dac2d25c47f12a7b8aa60e528bfb3c51c5a6c5a9f7c86987909c6c79765554"}, - {file = "cryptography-40.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0a4e3406cfed6b1f6d6e87ed243363652b2586b2d917b0609ca4f97072994405"}, - {file = "cryptography-40.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1e0af458515d5e4028aad75f3bb3fe7a31e46ad920648cd59b64d3da842e4356"}, - {file = "cryptography-40.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:d8aa3609d337ad85e4eb9bb0f8bcf6e4409bfb86e706efa9a027912169e89122"}, - {file = "cryptography-40.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cf91e428c51ef692b82ce786583e214f58392399cf65c341bc7301d096fa3ba2"}, - {file = "cryptography-40.0.1-cp36-abi3-win32.whl", hash = "sha256:650883cc064297ef3676b1db1b7b1df6081794c4ada96fa457253c4cc40f97db"}, - {file = "cryptography-40.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:a805a7bce4a77d51696410005b3e85ae2839bad9aa38894afc0aa99d8e0c3160"}, - {file = "cryptography-40.0.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd033d74067d8928ef00a6b1327c8ea0452523967ca4463666eeba65ca350d4c"}, - {file = "cryptography-40.0.1-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d36bbeb99704aabefdca5aee4eba04455d7a27ceabd16f3b3ba9bdcc31da86c4"}, - {file = "cryptography-40.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:32057d3d0ab7d4453778367ca43e99ddb711770477c4f072a51b3ca69602780a"}, - {file = "cryptography-40.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:f5d7b79fa56bc29580faafc2ff736ce05ba31feaa9d4735048b0de7d9ceb2b94"}, - {file = "cryptography-40.0.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7c872413353c70e0263a9368c4993710070e70ab3e5318d85510cc91cce77e7c"}, - {file = "cryptography-40.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:28d63d75bf7ae4045b10de5413fb1d6338616e79015999ad9cf6fc538f772d41"}, - {file = "cryptography-40.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6f2bbd72f717ce33100e6467572abaedc61f1acb87b8d546001328d7f466b778"}, - {file = "cryptography-40.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cc3a621076d824d75ab1e1e530e66e7e8564e357dd723f2533225d40fe35c60c"}, - {file = "cryptography-40.0.1.tar.gz", hash = "sha256:2803f2f8b1e95f614419926c7e6f55d828afc614ca5ed61543877ae668cc3472"}, + {file = "cryptography-41.0.3-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:652627a055cb52a84f8c448185922241dd5217443ca194d5739b44612c5e6507"}, + {file = "cryptography-41.0.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:8f09daa483aedea50d249ef98ed500569841d6498aa9c9f4b0531b9964658922"}, + {file = "cryptography-41.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fd871184321100fb400d759ad0cddddf284c4b696568204d281c902fc7b0d81"}, + {file = "cryptography-41.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84537453d57f55a50a5b6835622ee405816999a7113267739a1b4581f83535bd"}, + {file = "cryptography-41.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3fb248989b6363906827284cd20cca63bb1a757e0a2864d4c1682a985e3dca47"}, + {file = "cryptography-41.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:42cb413e01a5d36da9929baa9d70ca90d90b969269e5a12d39c1e0d475010116"}, + {file = "cryptography-41.0.3-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:aeb57c421b34af8f9fe830e1955bf493a86a7996cc1338fe41b30047d16e962c"}, + {file = "cryptography-41.0.3-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6af1c6387c531cd364b72c28daa29232162010d952ceb7e5ca8e2827526aceae"}, + {file = "cryptography-41.0.3-cp37-abi3-win32.whl", hash = "sha256:0d09fb5356f975974dbcb595ad2d178305e5050656affb7890a1583f5e02a306"}, + {file = "cryptography-41.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:a983e441a00a9d57a4d7c91b3116a37ae602907a7618b882c8013b5762e80574"}, + {file = "cryptography-41.0.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5259cb659aa43005eb55a0e4ff2c825ca111a0da1814202c64d28a985d33b087"}, + {file = "cryptography-41.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:67e120e9a577c64fe1f611e53b30b3e69744e5910ff3b6e97e935aeb96005858"}, + {file = "cryptography-41.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:7efe8041897fe7a50863e51b77789b657a133c75c3b094e51b5e4b5cec7bf906"}, + {file = "cryptography-41.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ce785cf81a7bdade534297ef9e490ddff800d956625020ab2ec2780a556c313e"}, + {file = "cryptography-41.0.3-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:57a51b89f954f216a81c9d057bf1a24e2f36e764a1ca9a501a6964eb4a6800dd"}, + {file = "cryptography-41.0.3-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c2f0d35703d61002a2bbdcf15548ebb701cfdd83cdc12471d2bae80878a4207"}, + {file = "cryptography-41.0.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:23c2d778cf829f7d0ae180600b17e9fceea3c2ef8b31a99e3c694cbbf3a24b84"}, + {file = "cryptography-41.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:95dd7f261bb76948b52a5330ba5202b91a26fbac13ad0e9fc8a3ac04752058c7"}, + {file = "cryptography-41.0.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:41d7aa7cdfded09b3d73a47f429c298e80796c8e825ddfadc84c8a7f12df212d"}, + {file = "cryptography-41.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d0d651aa754ef58d75cec6edfbd21259d93810b73f6ec246436a21b7841908de"}, + {file = "cryptography-41.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ab8de0d091acbf778f74286f4989cf3d1528336af1b59f3e5d2ebca8b5fe49e1"}, + {file = "cryptography-41.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a74fbcdb2a0d46fe00504f571a2a540532f4c188e6ccf26f1f178480117b33c4"}, + {file = "cryptography-41.0.3.tar.gz", hash = "sha256:6d192741113ef5e30d89dcb5b956ef4e1578f304708701b8b73d38e3e1461f34"}, ] [package.dependencies] @@ -620,39 +730,43 @@ cffi = ">=1.12" [package.extras] docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] -pep8test = ["black", "check-manifest", "mypy", "ruff"] -sdist = ["setuptools-rust (>=0.11.4)"] +nox = ["nox"] +pep8test = ["black", "check-sdist", "mypy", "ruff"] +sdist = ["build"] ssh = ["bcrypt (>=3.1.5)"] -test = ["iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-shard (>=0.1.2)", "pytest-subtests", "pytest-xdist"] +test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] -tox = ["tox"] [[package]] name = "debugpy" -version = "1.6.7" +version = "1.7.0" description = "An implementation of the Debug Adapter Protocol for Python" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "debugpy-1.6.7-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b3e7ac809b991006ad7f857f016fa92014445085711ef111fdc3f74f66144096"}, - {file = "debugpy-1.6.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3876611d114a18aafef6383695dfc3f1217c98a9168c1aaf1a02b01ec7d8d1e"}, - {file = "debugpy-1.6.7-cp310-cp310-win32.whl", hash = "sha256:33edb4afa85c098c24cc361d72ba7c21bb92f501104514d4ffec1fb36e09c01a"}, - {file = "debugpy-1.6.7-cp310-cp310-win_amd64.whl", hash = "sha256:ed6d5413474e209ba50b1a75b2d9eecf64d41e6e4501977991cdc755dc83ab0f"}, - {file = "debugpy-1.6.7-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:38ed626353e7c63f4b11efad659be04c23de2b0d15efff77b60e4740ea685d07"}, - {file = "debugpy-1.6.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:279d64c408c60431c8ee832dfd9ace7c396984fd7341fa3116aee414e7dcd88d"}, - {file = "debugpy-1.6.7-cp37-cp37m-win32.whl", hash = "sha256:dbe04e7568aa69361a5b4c47b4493d5680bfa3a911d1e105fbea1b1f23f3eb45"}, - {file = "debugpy-1.6.7-cp37-cp37m-win_amd64.whl", hash = "sha256:f90a2d4ad9a035cee7331c06a4cf2245e38bd7c89554fe3b616d90ab8aab89cc"}, - {file = "debugpy-1.6.7-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:5224eabbbeddcf1943d4e2821876f3e5d7d383f27390b82da5d9558fd4eb30a9"}, - {file = "debugpy-1.6.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bae1123dff5bfe548ba1683eb972329ba6d646c3a80e6b4c06cd1b1dd0205e9b"}, - {file = "debugpy-1.6.7-cp38-cp38-win32.whl", hash = "sha256:9cd10cf338e0907fdcf9eac9087faa30f150ef5445af5a545d307055141dd7a4"}, - {file = "debugpy-1.6.7-cp38-cp38-win_amd64.whl", hash = "sha256:aaf6da50377ff4056c8ed470da24632b42e4087bc826845daad7af211e00faad"}, - {file = "debugpy-1.6.7-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:0679b7e1e3523bd7d7869447ec67b59728675aadfc038550a63a362b63029d2c"}, - {file = "debugpy-1.6.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de86029696e1b3b4d0d49076b9eba606c226e33ae312a57a46dca14ff370894d"}, - {file = "debugpy-1.6.7-cp39-cp39-win32.whl", hash = "sha256:d71b31117779d9a90b745720c0eab54ae1da76d5b38c8026c654f4a066b0130a"}, - {file = "debugpy-1.6.7-cp39-cp39-win_amd64.whl", hash = "sha256:c0ff93ae90a03b06d85b2c529eca51ab15457868a377c4cc40a23ab0e4e552a3"}, - {file = "debugpy-1.6.7-py2.py3-none-any.whl", hash = "sha256:53f7a456bc50706a0eaabecf2d3ce44c4d5010e46dfc65b6b81a518b42866267"}, - {file = "debugpy-1.6.7.zip", hash = "sha256:c4c2f0810fa25323abfdfa36cbbbb24e5c3b1a42cb762782de64439c575d67f2"}, + {file = "debugpy-1.7.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:17ad9a681aca1704c55b9a5edcb495fa8f599e4655c9872b7f9cf3dc25890d48"}, + {file = "debugpy-1.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1285920a3f9a75f5d1acf59ab1b9da9ae6eb9a05884cd7674f95170c9cafa4de"}, + {file = "debugpy-1.7.0-cp310-cp310-win32.whl", hash = "sha256:a6f43a681c5025db1f1c0568069d1d1bad306a02e7c36144912b26d9c90e4724"}, + {file = "debugpy-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:9e9571d831ad3c75b5fb6f3efcb71c471cf2a74ba84af6ac1c79ce00683bed4b"}, + {file = "debugpy-1.7.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:538765a41198aa88cc089295b39c7322dd598f9ef1d52eaae12145c63bf9430a"}, + {file = "debugpy-1.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7e8cf91f8f3f9b5fad844dd88427b85d398bda1e2a0cd65d5a21312fcbc0c6f"}, + {file = "debugpy-1.7.0-cp311-cp311-win32.whl", hash = "sha256:18a69f8e142a716310dd0af6d7db08992aed99e2606108732efde101e7c65e2a"}, + {file = "debugpy-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:7515a5ba5ee9bfe956685909c5f28734c1cecd4ee813523363acfe3ca824883a"}, + {file = "debugpy-1.7.0-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:bc8da67ade39d9e75608cdb8601d07e63a4e85966e0572c981f14e2cf42bcdef"}, + {file = "debugpy-1.7.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5036e918c6ba8fc4c4f1fd0207d81db634431a02f0dc2ba51b12fd793c8c9de"}, + {file = "debugpy-1.7.0-cp37-cp37m-win32.whl", hash = "sha256:d5be95b3946a4d7b388e45068c7b75036ac5a610f41014aee6cafcd5506423ad"}, + {file = "debugpy-1.7.0-cp37-cp37m-win_amd64.whl", hash = "sha256:0e90314a078d4e3f009520c8387aba8f74c3034645daa7a332a3d1bb81335756"}, + {file = "debugpy-1.7.0-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:1565fd904f9571c430adca597771255cff4f92171486fced6f765dcbdfc8ec8d"}, + {file = "debugpy-1.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6516f36a2e95b3be27f171f12b641e443863f4ad5255d0fdcea6ae0be29bb912"}, + {file = "debugpy-1.7.0-cp38-cp38-win32.whl", hash = "sha256:2b0e489613bc066051439df04c56777ec184b957d6810cb65f235083aef7a0dc"}, + {file = "debugpy-1.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:7bf0b4bbd841b2397b6a8de15da9227f1164f6d43ceee971c50194eaed930a9d"}, + {file = "debugpy-1.7.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:ad22e1095b9977af432465c1e09132ba176e18df3834b1efcab1a449346b350b"}, + {file = "debugpy-1.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f625e427f21423e5874139db529e18cb2966bdfcc1cb87a195538c5b34d163d1"}, + {file = "debugpy-1.7.0-cp39-cp39-win32.whl", hash = "sha256:18bca8429d6632e2d3435055416d2d88f0309cc39709f4f6355c8d412cc61f24"}, + {file = "debugpy-1.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:dc8a12ac8b97ef3d6973c6679a093138c7c9b03eb685f0e253269a195f651559"}, + {file = "debugpy-1.7.0-py2.py3-none-any.whl", hash = "sha256:f6de2e6f24f62969e0f0ef682d78c98161c4dca29e9fb05df4d2989005005502"}, + {file = "debugpy-1.7.0.zip", hash = "sha256:676911c710e85567b17172db934a71319ed9d995104610ce23fd74a07f66e6f6"}, ] [[package]] @@ -699,14 +813,14 @@ hsm = ["python-pkcs11"] [[package]] name = "exceptiongroup" -version = "1.1.2" +version = "1.1.3" description = "Backport of PEP 654 (exception groups)" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.1.2-py3-none-any.whl", hash = "sha256:e346e69d186172ca7cf029c8c1d16235aa0e04035e5750b4b95039e65204328f"}, - {file = "exceptiongroup-1.1.2.tar.gz", hash = "sha256:12c3e887d6485d16943a309616de20ae5582633e0a2eda17f4e10fd61c1e8af5"}, + {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, + {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, ] [package.extras] @@ -816,121 +930,120 @@ files = [ [[package]] name = "grpcio" -version = "1.56.2" +version = "1.58.0" description = "HTTP/2-based RPC framework" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "grpcio-1.56.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:bf0b9959e673505ee5869950642428046edb91f99942607c2ecf635f8a4b31c9"}, - {file = "grpcio-1.56.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:5144feb20fe76e73e60c7d73ec3bf54f320247d1ebe737d10672480371878b48"}, - {file = "grpcio-1.56.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:a72797549935c9e0b9bc1def1768c8b5a709538fa6ab0678e671aec47ebfd55e"}, - {file = "grpcio-1.56.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3f3237a57e42f79f1e560726576aedb3a7ef931f4e3accb84ebf6acc485d316"}, - {file = "grpcio-1.56.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:900bc0096c2ca2d53f2e5cebf98293a7c32f532c4aeb926345e9747452233950"}, - {file = "grpcio-1.56.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:97e0efaebbfd222bcaac2f1735c010c1d3b167112d9d237daebbeedaaccf3d1d"}, - {file = "grpcio-1.56.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c0c85c5cbe8b30a32fa6d802588d55ffabf720e985abe9590c7c886919d875d4"}, - {file = "grpcio-1.56.2-cp310-cp310-win32.whl", hash = "sha256:06e84ad9ae7668a109e970c7411e7992751a116494cba7c4fb877656527f9a57"}, - {file = "grpcio-1.56.2-cp310-cp310-win_amd64.whl", hash = "sha256:10954662f77dc36c9a1fb5cc4a537f746580d6b5734803be1e587252682cda8d"}, - {file = "grpcio-1.56.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:c435f5ce1705de48e08fcbcfaf8aee660d199c90536e3e06f2016af7d6a938dd"}, - {file = "grpcio-1.56.2-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:6108e5933eb8c22cd3646e72d5b54772c29f57482fd4c41a0640aab99eb5071d"}, - {file = "grpcio-1.56.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:8391cea5ce72f4a12368afd17799474015d5d3dc00c936a907eb7c7eaaea98a5"}, - {file = "grpcio-1.56.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:750de923b456ca8c0f1354d6befca45d1f3b3a789e76efc16741bd4132752d95"}, - {file = "grpcio-1.56.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fda2783c12f553cdca11c08e5af6eecbd717280dc8fbe28a110897af1c15a88c"}, - {file = "grpcio-1.56.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9e04d4e4cfafa7c5264e535b5d28e786f0571bea609c3f0aaab13e891e933e9c"}, - {file = "grpcio-1.56.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:89a49cc5ad08a38b6141af17e00d1dd482dc927c7605bc77af457b5a0fca807c"}, - {file = "grpcio-1.56.2-cp311-cp311-win32.whl", hash = "sha256:6a007a541dff984264981fbafeb052bfe361db63578948d857907df9488d8774"}, - {file = "grpcio-1.56.2-cp311-cp311-win_amd64.whl", hash = "sha256:af4063ef2b11b96d949dccbc5a987272f38d55c23c4c01841ea65a517906397f"}, - {file = "grpcio-1.56.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:a6ff459dac39541e6a2763a4439c4ca6bc9ecb4acc05a99b79246751f9894756"}, - {file = "grpcio-1.56.2-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:f20fd21f7538f8107451156dd1fe203300b79a9ddceba1ee0ac8132521a008ed"}, - {file = "grpcio-1.56.2-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:d1fbad1f9077372b6587ec589c1fc120b417b6c8ad72d3e3cc86bbbd0a3cee93"}, - {file = "grpcio-1.56.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ee26e9dfb3996aff7c870f09dc7ad44a5f6732b8bdb5a5f9905737ac6fd4ef1"}, - {file = "grpcio-1.56.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4c60abd950d6de3e4f1ddbc318075654d275c29c846ab6a043d6ed2c52e4c8c"}, - {file = "grpcio-1.56.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1c31e52a04e62c8577a7bf772b3e7bed4df9c9e0dd90f92b6ffa07c16cab63c9"}, - {file = "grpcio-1.56.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:345356b307cce5d14355e8e055b4ca5f99bc857c33a3dc1ddbc544fca9cd0475"}, - {file = "grpcio-1.56.2-cp37-cp37m-win_amd64.whl", hash = "sha256:42e63904ee37ae46aa23de50dac8b145b3596f43598fa33fe1098ab2cbda6ff5"}, - {file = "grpcio-1.56.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:7c5ede2e2558f088c49a1ddda19080e4c23fb5d171de80a726b61b567e3766ed"}, - {file = "grpcio-1.56.2-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:33971197c47965cc1d97d78d842163c283e998223b151bab0499b951fd2c0b12"}, - {file = "grpcio-1.56.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d39f5d4af48c138cb146763eda14eb7d8b3ccbbec9fe86fb724cd16e0e914c64"}, - {file = "grpcio-1.56.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ded637176addc1d3eef35331c39acc598bac550d213f0a1bedabfceaa2244c87"}, - {file = "grpcio-1.56.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c90da4b124647547a68cf2f197174ada30c7bb9523cb976665dfd26a9963d328"}, - {file = "grpcio-1.56.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3ccb621749a81dc7755243665a70ce45536ec413ef5818e013fe8dfbf5aa497b"}, - {file = "grpcio-1.56.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4eb37dd8dd1aa40d601212afa27ca5be255ba792e2e0b24d67b8af5e012cdb7d"}, - {file = "grpcio-1.56.2-cp38-cp38-win32.whl", hash = "sha256:ddb4a6061933bd9332b74eac0da25f17f32afa7145a33a0f9711ad74f924b1b8"}, - {file = "grpcio-1.56.2-cp38-cp38-win_amd64.whl", hash = "sha256:8940d6de7068af018dfa9a959a3510e9b7b543f4c405e88463a1cbaa3b2b379a"}, - {file = "grpcio-1.56.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:51173e8fa6d9a2d85c14426bdee5f5c4a0654fd5fddcc21fe9d09ab0f6eb8b35"}, - {file = "grpcio-1.56.2-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:373b48f210f43327a41e397391715cd11cfce9ded2fe76a5068f9bacf91cc226"}, - {file = "grpcio-1.56.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:42a3bbb2bc07aef72a7d97e71aabecaf3e4eb616d39e5211e2cfe3689de860ca"}, - {file = "grpcio-1.56.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5344be476ac37eb9c9ad09c22f4ea193c1316bf074f1daf85bddb1b31fda5116"}, - {file = "grpcio-1.56.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3fa3ab0fb200a2c66493828ed06ccd1a94b12eddbfb985e7fd3e5723ff156c6"}, - {file = "grpcio-1.56.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b975b85d1d5efc36cf8b237c5f3849b64d1ba33d6282f5e991f28751317504a1"}, - {file = "grpcio-1.56.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cbdf2c498e077282cd427cfd88bdce4668019791deef0be8155385ab2ba7837f"}, - {file = "grpcio-1.56.2-cp39-cp39-win32.whl", hash = "sha256:139f66656a762572ae718fa0d1f2dce47c05e9fbf7a16acd704c354405b97df9"}, - {file = "grpcio-1.56.2-cp39-cp39-win_amd64.whl", hash = "sha256:830215173ad45d670140ff99aac3b461f9be9a6b11bee1a17265aaaa746a641a"}, - {file = "grpcio-1.56.2.tar.gz", hash = "sha256:0ff789ae7d8ddd76d2ac02e7d13bfef6fc4928ac01e1dcaa182be51b6bcc0aaa"}, + {file = "grpcio-1.58.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3e6bebf1dfdbeb22afd95650e4f019219fef3ab86d3fca8ebade52e4bc39389a"}, + {file = "grpcio-1.58.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:cde11577d5b6fd73a00e6bfa3cf5f428f3f33c2d2878982369b5372bbc4acc60"}, + {file = "grpcio-1.58.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:a2d67ff99e70e86b2be46c1017ae40b4840d09467d5455b2708de6d4c127e143"}, + {file = "grpcio-1.58.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ed979b273a81de36fc9c6716d9fb09dd3443efa18dcc8652501df11da9583e9"}, + {file = "grpcio-1.58.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:458899d2ebd55d5ca2350fd3826dfd8fcb11fe0f79828ae75e2b1e6051d50a29"}, + {file = "grpcio-1.58.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bc7ffef430b80345729ff0a6825e9d96ac87efe39216e87ac58c6c4ef400de93"}, + {file = "grpcio-1.58.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5b23d75e5173faa3d1296a7bedffb25afd2fddb607ef292dfc651490c7b53c3d"}, + {file = "grpcio-1.58.0-cp310-cp310-win32.whl", hash = "sha256:fad9295fe02455d4f158ad72c90ef8b4bcaadfdb5efb5795f7ab0786ad67dd58"}, + {file = "grpcio-1.58.0-cp310-cp310-win_amd64.whl", hash = "sha256:bc325fed4d074367bebd465a20763586e5e1ed5b943e9d8bc7c162b1f44fd602"}, + {file = "grpcio-1.58.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:652978551af02373a5a313e07bfef368f406b5929cf2d50fa7e4027f913dbdb4"}, + {file = "grpcio-1.58.0-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:9f13a171281ebb4d7b1ba9f06574bce2455dcd3f2f6d1fbe0fd0d84615c74045"}, + {file = "grpcio-1.58.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:8774219e21b05f750eef8adc416e9431cf31b98f6ce9def288e4cea1548cbd22"}, + {file = "grpcio-1.58.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09206106848462763f7f273ca93d2d2d4d26cab475089e0de830bb76be04e9e8"}, + {file = "grpcio-1.58.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62831d5e251dd7561d9d9e83a0b8655084b2a1f8ea91e4bd6b3cedfefd32c9d2"}, + {file = "grpcio-1.58.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:212f38c6a156862098f6bdc9a79bf850760a751d259d8f8f249fc6d645105855"}, + {file = "grpcio-1.58.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4b12754af201bb993e6e2efd7812085ddaaef21d0a6f0ff128b97de1ef55aa4a"}, + {file = "grpcio-1.58.0-cp311-cp311-win32.whl", hash = "sha256:3886b4d56bd4afeac518dbc05933926198aa967a7d1d237a318e6fbc47141577"}, + {file = "grpcio-1.58.0-cp311-cp311-win_amd64.whl", hash = "sha256:002f228d197fea12797a14e152447044e14fb4fdb2eb5d6cfa496f29ddbf79ef"}, + {file = "grpcio-1.58.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:b5e8db0aff0a4819946215f156bd722b6f6c8320eb8419567ffc74850c9fd205"}, + {file = "grpcio-1.58.0-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:201e550b7e2ede113b63e718e7ece93cef5b0fbf3c45e8fe4541a5a4305acd15"}, + {file = "grpcio-1.58.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:d79b660681eb9bc66cc7cbf78d1b1b9e335ee56f6ea1755d34a31108b80bd3c8"}, + {file = "grpcio-1.58.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ef8d4a76d2c7d8065aba829f8d0bc0055495c998dce1964ca5b302d02514fb3"}, + {file = "grpcio-1.58.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6cba491c638c76d3dc6c191d9c75041ca5b8f5c6de4b8327ecdcab527f130bb4"}, + {file = "grpcio-1.58.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6801ff6652ecd2aae08ef994a3e49ff53de29e69e9cd0fd604a79ae4e545a95c"}, + {file = "grpcio-1.58.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:24edec346e69e672daf12b2c88e95c6f737f3792d08866101d8c5f34370c54fd"}, + {file = "grpcio-1.58.0-cp37-cp37m-win_amd64.whl", hash = "sha256:7e473a7abad9af48e3ab5f3b5d237d18208024d28ead65a459bd720401bd2f8f"}, + {file = "grpcio-1.58.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:4891bbb4bba58acd1d620759b3be11245bfe715eb67a4864c8937b855b7ed7fa"}, + {file = "grpcio-1.58.0-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:e9f995a8a421405958ff30599b4d0eec244f28edc760de82f0412c71c61763d2"}, + {file = "grpcio-1.58.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:2f85f87e2f087d9f632c085b37440a3169fda9cdde80cb84057c2fc292f8cbdf"}, + {file = "grpcio-1.58.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb6b92036ff312d5b4182fa72e8735d17aceca74d0d908a7f08e375456f03e07"}, + {file = "grpcio-1.58.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d81c2b2b24c32139dd2536972f1060678c6b9fbd106842a9fcdecf07b233eccd"}, + {file = "grpcio-1.58.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:fbcecb6aedd5c1891db1d70efbfbdc126c986645b5dd616a045c07d6bd2dfa86"}, + {file = "grpcio-1.58.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92ae871a902cf19833328bd6498ec007b265aabf2fda845ab5bd10abcaf4c8c6"}, + {file = "grpcio-1.58.0-cp38-cp38-win32.whl", hash = "sha256:dc72e04620d49d3007771c0e0348deb23ca341c0245d610605dddb4ac65a37cb"}, + {file = "grpcio-1.58.0-cp38-cp38-win_amd64.whl", hash = "sha256:1c1c5238c6072470c7f1614bf7c774ffde6b346a100521de9ce791d1e4453afe"}, + {file = "grpcio-1.58.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:fe643af248442221db027da43ed43e53b73e11f40c9043738de9a2b4b6ca7697"}, + {file = "grpcio-1.58.0-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:128eb1f8e70676d05b1b0c8e6600320fc222b3f8c985a92224248b1367122188"}, + {file = "grpcio-1.58.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:039003a5e0ae7d41c86c768ef8b3ee2c558aa0a23cf04bf3c23567f37befa092"}, + {file = "grpcio-1.58.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f061722cad3f9aabb3fbb27f3484ec9d4667b7328d1a7800c3c691a98f16bb0"}, + {file = "grpcio-1.58.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0af11938acf8cd4cf815c46156bcde36fa5850518120920d52620cc3ec1830"}, + {file = "grpcio-1.58.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d4cef77ad2fed42b1ba9143465856d7e737279854e444925d5ba45fc1f3ba727"}, + {file = "grpcio-1.58.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:24765a627eb4d9288ace32d5104161c3654128fe27f2808ecd6e9b0cfa7fc8b9"}, + {file = "grpcio-1.58.0-cp39-cp39-win32.whl", hash = "sha256:f0241f7eb0d2303a545136c59bc565a35c4fc3b924ccbd69cb482f4828d6f31c"}, + {file = "grpcio-1.58.0-cp39-cp39-win_amd64.whl", hash = "sha256:dcfba7befe3a55dab6fe1eb7fc9359dc0c7f7272b30a70ae0af5d5b063842f28"}, + {file = "grpcio-1.58.0.tar.gz", hash = "sha256:532410c51ccd851b706d1fbc00a87be0f5312bd6f8e5dbf89d4e99c7f79d7499"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.56.2)"] +protobuf = ["grpcio-tools (>=1.58.0)"] [[package]] name = "grpcio-tools" -version = "1.48.2" +version = "1.58.0" description = "Protobuf code generator for gRPC" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "grpcio-tools-1.48.2.tar.gz", hash = "sha256:8902a035708555cddbd61b5467cea127484362decc52de03f061a1a520fe90cd"}, - {file = "grpcio_tools-1.48.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:92acc3e10ba2b0dcb90a88ae9fe1cc0ffba6868545207e4ff20ca95284f8e3c9"}, - {file = "grpcio_tools-1.48.2-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e5bb396d63495667d4df42e506eed9d74fc9a51c99c173c04395fe7604c848f1"}, - {file = "grpcio_tools-1.48.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:84a84d601a238572d049d3108e04fe4c206536e81076d56e623bd525a1b38def"}, - {file = "grpcio_tools-1.48.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70564521e86a0de35ea9ac6daecff10cb46860aec469af65869974807ce8e98b"}, - {file = "grpcio_tools-1.48.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdbbe63f6190187de5946891941629912ac8196701ed2253fa91624a397822ec"}, - {file = "grpcio_tools-1.48.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae56f133b05b7e5d780ef7e032dd762adad7f3dc8f64adb43ff5bfabd659f435"}, - {file = "grpcio_tools-1.48.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f0feb4f2b777fa6377e977faa89c26359d4f31953de15e035505b92f41aa6906"}, - {file = "grpcio_tools-1.48.2-cp310-cp310-win32.whl", hash = "sha256:80f450272316ca0924545f488c8492649ca3aeb7044d4bf59c426dcdee527f7c"}, - {file = "grpcio_tools-1.48.2-cp310-cp310-win_amd64.whl", hash = "sha256:21ff50e321736eba22210bf9b94e05391a9ac345f26e7df16333dc75d63e74fb"}, - {file = "grpcio_tools-1.48.2-cp36-cp36m-linux_armv7l.whl", hash = "sha256:d598ccde6338b2cfbb3124f34c95f03394209013f9b1ed4a5360a736853b1c27"}, - {file = "grpcio_tools-1.48.2-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:a43d26714933f23de93ea0bf9c86c66a6ede709b8ca32e357f9e2181703e64ae"}, - {file = "grpcio_tools-1.48.2-cp36-cp36m-manylinux_2_17_aarch64.whl", hash = "sha256:55fdebc73fb580717656b1bafa4f8eca448726a7aa22726a6c0a7895d2f0f088"}, - {file = "grpcio_tools-1.48.2-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8588819b22d0de3aa1951e1991cc3e4b9aa105eecf6e3e24eb0a2fc8ab958b3e"}, - {file = "grpcio_tools-1.48.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9771d4d317dca029dfaca7ec9282d8afe731c18bc536ece37fd39b8a974cc331"}, - {file = "grpcio_tools-1.48.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d886a9e052a038642b3af5d18e6f2085d1656d9788e202dc23258cf3a751e7ca"}, - {file = "grpcio_tools-1.48.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d77e8b1613876e0d8fd17709509d4ceba13492816426bd156f7e88a4c47e7158"}, - {file = "grpcio_tools-1.48.2-cp36-cp36m-win32.whl", hash = "sha256:dcaaecdd5e847de5c1d533ea91522bf56c9e6b2dc98cdc0d45f0a1c26e846ea2"}, - {file = "grpcio_tools-1.48.2-cp36-cp36m-win_amd64.whl", hash = "sha256:0119aabd9ceedfdf41b56b9fdc8284dd85a7f589d087f2694d743f346a368556"}, - {file = "grpcio_tools-1.48.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:189be2a9b672300ca6845d94016bdacc052fdbe9d1ae9e85344425efae2ff8ef"}, - {file = "grpcio_tools-1.48.2-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:9443f5c30bac449237c3cf99da125f8d6e6c01e17972bc683ee73b75dea95573"}, - {file = "grpcio_tools-1.48.2-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:e0403e095b343431195db1305248b50019ad55d3dd310254431af87e14ef83a2"}, - {file = "grpcio_tools-1.48.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5410d6b601d1404835e34466bd8aee37213489b36ee1aad2276366e265ff29d4"}, - {file = "grpcio_tools-1.48.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51be91b7c7056ff9ee48b1eccd4a2840b0126230803a5e09dfc082a5b16a91c1"}, - {file = "grpcio_tools-1.48.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:516eedd5eb7af6326050bc2cfceb3a977b9cc1144f283c43cc4956905285c912"}, - {file = "grpcio_tools-1.48.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d18599ab572b2f15a8f3db49503272d1bb4fcabb4b4d1214ef03aca1816b20a0"}, - {file = "grpcio_tools-1.48.2-cp37-cp37m-win32.whl", hash = "sha256:d18ef2adc05a8ef9e58ac46357f6d4ce7e43e077c7eda0a4425773461f9d0e6e"}, - {file = "grpcio_tools-1.48.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d9753944e5a6b6b78b76ce9d2ae0fe3f748008c1849deb7fadcb64489d6553b"}, - {file = "grpcio_tools-1.48.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:3c8749dca04a8d302862ceeb1dfbdd071ee13b281395975f24405a347e5baa57"}, - {file = "grpcio_tools-1.48.2-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:7307dd2408b82ea545ae63502ec03036b025f449568556ea9a056e06129a7a4e"}, - {file = "grpcio_tools-1.48.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:072234859f6069dc43a6be8ad6b7d682f4ba1dc2e2db2ebf5c75f62eee0f6dfb"}, - {file = "grpcio_tools-1.48.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6cc298fbfe584de8876a85355efbcf796dfbcfac5948c9560f5df82e79336e2a"}, - {file = "grpcio_tools-1.48.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f75973a42c710999acd419968bc79f00327e03e855bbe82c6529e003e49af660"}, - {file = "grpcio_tools-1.48.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f766050e491d0b3203b6b85638015f543816a2eb7d089fc04e86e00f6de0e31d"}, - {file = "grpcio_tools-1.48.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8e0d74403484eb77e8df2566a64b8b0b484b5c87903678c381634dd72f252d5e"}, - {file = "grpcio_tools-1.48.2-cp38-cp38-win32.whl", hash = "sha256:cb75bac0cd43858cb759ef103fe68f8c540cb58b63dda127e710228fec3007b8"}, - {file = "grpcio_tools-1.48.2-cp38-cp38-win_amd64.whl", hash = "sha256:cabc8b0905cedbc3b2b7b2856334fa35cce3d4bc79ae241cacd8cca8940a5c85"}, - {file = "grpcio_tools-1.48.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:e712a6d00606ad19abdeae852a7e521d6f6d0dcea843708fecf3a38be16a851e"}, - {file = "grpcio_tools-1.48.2-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:e7e7668f89fd598c5469bb58e16bfd12b511d9947ccc75aec94da31f62bc3758"}, - {file = "grpcio_tools-1.48.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:a415fbec67d4ff7efe88794cbe00cf548d0f0a5484cceffe0a0c89d47694c491"}, - {file = "grpcio_tools-1.48.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d96e96ae7361aa51c9cd9c73b677b51f691f98df6086860fcc3c45852d96b0b0"}, - {file = "grpcio_tools-1.48.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e20d7885a40e68a2bda92908acbabcdf3c14dd386c3845de73ba139e9df1f132"}, - {file = "grpcio_tools-1.48.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8a5614251c46da07549e24f417cf989710250385e9d80deeafc53a0ee7df6325"}, - {file = "grpcio_tools-1.48.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ace0035766fe01a1b096aa050be9f0a9f98402317e7aeff8bfe55349be32a407"}, - {file = "grpcio_tools-1.48.2-cp39-cp39-win32.whl", hash = "sha256:4fa4300b1be59b046492ed3c5fdb59760bc6433f44c08f50de900f9552ec7461"}, - {file = "grpcio_tools-1.48.2-cp39-cp39-win_amd64.whl", hash = "sha256:0fb6c1c1e56eb26b224adc028a4204b6ad0f8b292efa28067dff273bbc8b27c4"}, + {file = "grpcio-tools-1.58.0.tar.gz", hash = "sha256:6f4d80ceb591e31ca4dceec747dbe56132e1392a0a9bb1c8fe001d1b5cac898a"}, + {file = "grpcio_tools-1.58.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:60c874908f3b40f32f1bb0221f7b3ab65ecb53a4d0a9f0a394f031f1b292c177"}, + {file = "grpcio_tools-1.58.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:1852e798f31e5437ca7b37abc910e028b34732fb19364862cedb87b1dab66fad"}, + {file = "grpcio_tools-1.58.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:149fb48f53cb691a6328f68bed8e4036c730f7106b7f98e92c2c0403f0b9e93c"}, + {file = "grpcio_tools-1.58.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba3d383e5ca93826038b70f326fce8e8d12dd9b2f64d363a3d612f7475f12dd2"}, + {file = "grpcio_tools-1.58.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6997511e9d2979f7a2389479682dbb06823f21a904e8fb0a5c6baaf1b4b4a863"}, + {file = "grpcio_tools-1.58.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8de0b701da479643f71fad71fe66885cddd89441ae16e2c724939b47742dc72e"}, + {file = "grpcio_tools-1.58.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:43cc23908b63fcaefe690b10f68a2d8652c994b5b36ab77d2271d9608c895320"}, + {file = "grpcio_tools-1.58.0-cp310-cp310-win32.whl", hash = "sha256:2c2221123d010dc6231799e63a37f2f4786bf614ef65b23009c387cd20d8b193"}, + {file = "grpcio_tools-1.58.0-cp310-cp310-win_amd64.whl", hash = "sha256:df2788736bdf58abe7b0e4d6b1ff806f7686c98c5ad900da312252e3322d91c4"}, + {file = "grpcio_tools-1.58.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:b6ea5578712cdb29b0ff60bfc6405bf0e8d681b9c71d106dd1cda54fe7fe4e55"}, + {file = "grpcio_tools-1.58.0-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c29880f491581c83181c0a84a4d11402af2b13166a5266f64e246adf1da7aa66"}, + {file = "grpcio_tools-1.58.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:32d51e933c3565414dd0835f930bb28a1cdeba435d9d2c87fa3cf8b1d284db3c"}, + {file = "grpcio_tools-1.58.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ad9d77f25514584b1ddc981d70c9e50dfcfc388aa5ba943eee67520c5267ed9"}, + {file = "grpcio_tools-1.58.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4882382631e6352819059278a5c878ce0b067008dd490911d16d5616e8a36d85"}, + {file = "grpcio_tools-1.58.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d84091a189d848d94645b7c48b61734c12ec03b0d46e5fc0049343a26989ac5c"}, + {file = "grpcio_tools-1.58.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:85ac28a9621e9b92a3fc416288c4ce45542db0b4c31b3e23031dd8e0a0ec5590"}, + {file = "grpcio_tools-1.58.0-cp311-cp311-win32.whl", hash = "sha256:7371d8ea80234b29affec145e25569523f549520ed7e53b2aa92bed412cdecfd"}, + {file = "grpcio_tools-1.58.0-cp311-cp311-win_amd64.whl", hash = "sha256:6997df6e7c5cf4d3ddc764240c1ff6a04b45d70ec28913b38fbc6396ef743e12"}, + {file = "grpcio_tools-1.58.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:ac65b8d6e3acaf88b815edf9af88ff844b6600ff3d2591c05ba4f655b45d5fb4"}, + {file = "grpcio_tools-1.58.0-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:88e8191d0dd789bebf42533808728f5ce75d2c51e2a72bdf20abe5b5e3fbec42"}, + {file = "grpcio_tools-1.58.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:a3dbece2a121761499a659b799979d4b738586d1065439053de553773eee11ca"}, + {file = "grpcio_tools-1.58.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1086fe240c4c879b9721952b47d46996deb283c2d9355a8dc24a804811aacf70"}, + {file = "grpcio_tools-1.58.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7ae3dca059d5b358dd03fb63277428fa7d771605d4074a019138dd38d70719a"}, + {file = "grpcio_tools-1.58.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3f8904ac7fc3da2e874f00b3a986e8b7e004f499344a8e7eb213c26dfb025041"}, + {file = "grpcio_tools-1.58.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:aadbd8393ae332e49731adb31e741f2e689989150569b7acc939f5ea43124e2d"}, + {file = "grpcio_tools-1.58.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1cb6e24194786687d4f23c64de1f0ce553af51de22746911bc37340f85f9783e"}, + {file = "grpcio_tools-1.58.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:6ec43909095c630df3e479e77469bdad367067431f4af602f6ccb978a3b78afd"}, + {file = "grpcio_tools-1.58.0-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:4be49ed320b0ebcbc21d19ef555fbf229c1c452105522b728e1171ee2052078e"}, + {file = "grpcio_tools-1.58.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:28eefebddec3d3adf19baca78f8b82a2287d358e1b1575ae018cdca8eacc6269"}, + {file = "grpcio_tools-1.58.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ef8c696e9d78676cc3f583a92bbbf2c84e94e350f7ad22f150a52559f4599d1"}, + {file = "grpcio_tools-1.58.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9aeb5949e46558d21c51fd3ec3eeecc59c94dbca76c67c0a80d3da6b7437930c"}, + {file = "grpcio_tools-1.58.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f7144aad9396d35fb1b80429600a970b559c2ad4d07020eeb180fe83cea2bee"}, + {file = "grpcio_tools-1.58.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4ee26e9253a721fff355737649678535f76cf5d642aa3ac0cd937832559b90af"}, + {file = "grpcio_tools-1.58.0-cp38-cp38-win32.whl", hash = "sha256:343f572312039059a8797d6e29a7fc62196e73131ab01755660a9d48202267c1"}, + {file = "grpcio_tools-1.58.0-cp38-cp38-win_amd64.whl", hash = "sha256:cd7acfbb43b7338a78cf4a67528d05530d574d92b7c829d185b78dfc451d158f"}, + {file = "grpcio_tools-1.58.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:46628247fbce86d18232eead24bd22ed0826c79f3fe2fc2fbdbde45971361049"}, + {file = "grpcio_tools-1.58.0-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:51587842a54e025a3d0d37afcf4ef2b7ac1def9a5d17448665cb424b53d6c287"}, + {file = "grpcio_tools-1.58.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:a062ae3072a2a39a3c057f4d68b57b021f1dd2956cd09aab39709f6af494e1de"}, + {file = "grpcio_tools-1.58.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eec3c93a08df11c80ef1c29a616bcbb0d83dbc6ea41b48306fcacc720416dfa7"}, + {file = "grpcio_tools-1.58.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b63f823ac991ff77104da614d2a2485a59d37d57830eb2e387a6e2a3edc7fa2b"}, + {file = "grpcio_tools-1.58.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:579c11a9f198847ed48dbc4f211c67fe96a73320b87c81f01b044b72e24a7d77"}, + {file = "grpcio_tools-1.58.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6ca2fc1dd8049d417a5034d944c9df05cee76f855b3e431627ab4292e7c01c47"}, + {file = "grpcio_tools-1.58.0-cp39-cp39-win32.whl", hash = "sha256:453023120114c35d3d9d6717ea0820e5d5c140f51f9d0b621de4397ff854471b"}, + {file = "grpcio_tools-1.58.0-cp39-cp39-win_amd64.whl", hash = "sha256:b6c896f1df99c35cf062d4803c15663ff00a33ff09add28baa6e475cf6b5e258"}, ] [package.dependencies] -grpcio = ">=1.48.2" -protobuf = ">=3.12.0,<4.0dev" +grpcio = ">=1.58.0" +protobuf = ">=4.21.6,<5.0dev" setuptools = "*" [[package]] @@ -1211,30 +1324,39 @@ proxy = ["PySocks"] [[package]] name = "pint" -version = "0.19.2" +version = "0.22" description = "Physical quantities module" category = "main" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "Pint-0.19.2.tar.gz", hash = "sha256:e1d4989ff510b378dad64f91711e7bdabe5ca78d75b06a18569ac454678c4baf"}, + {file = "Pint-0.22-py3-none-any.whl", hash = "sha256:6e2b3c5c2b4d9b516608bc860a417a39d66eb99c958f36540cf931d2c2e9f80f"}, + {file = "Pint-0.22.tar.gz", hash = "sha256:2d139f6abbcf3016cad7d3cec05707fe908ac4f99cf59aedfd6ee667b7a64433"}, ] +[package.dependencies] +typing-extensions = "*" + [package.extras] +babel = ["babel (<=2.8)"] +dask = ["dask"] +mip = ["mip (>=1.13)"] numpy = ["numpy (>=1.19.5)"] +pandas = ["pint-pandas (>=0.3)"] test = ["pytest", "pytest-cov", "pytest-mpl", "pytest-subtests"] uncertainties = ["uncertainties (>=3.1.6)"] +xarray = ["xarray"] [[package]] name = "pluggy" -version = "1.2.0" +version = "1.3.0" description = "plugin and hook calling mechanisms for python" category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, - {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, + {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, + {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, ] [package.extras] @@ -1243,34 +1365,25 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "protobuf" -version = "3.20.3" -description = "Protocol Buffers" +version = "4.24.3" +description = "" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "protobuf-3.20.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99"}, - {file = "protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e"}, - {file = "protobuf-3.20.3-cp310-cp310-win32.whl", hash = "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c"}, - {file = "protobuf-3.20.3-cp310-cp310-win_amd64.whl", hash = "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7"}, - {file = "protobuf-3.20.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469"}, - {file = "protobuf-3.20.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4"}, - {file = "protobuf-3.20.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4"}, - {file = "protobuf-3.20.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454"}, - {file = "protobuf-3.20.3-cp37-cp37m-win32.whl", hash = "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905"}, - {file = "protobuf-3.20.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c"}, - {file = "protobuf-3.20.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7"}, - {file = "protobuf-3.20.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee"}, - {file = "protobuf-3.20.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050"}, - {file = "protobuf-3.20.3-cp38-cp38-win32.whl", hash = "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86"}, - {file = "protobuf-3.20.3-cp38-cp38-win_amd64.whl", hash = "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9"}, - {file = "protobuf-3.20.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b"}, - {file = "protobuf-3.20.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b"}, - {file = "protobuf-3.20.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402"}, - {file = "protobuf-3.20.3-cp39-cp39-win32.whl", hash = "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480"}, - {file = "protobuf-3.20.3-cp39-cp39-win_amd64.whl", hash = "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7"}, - {file = "protobuf-3.20.3-py2.py3-none-any.whl", hash = "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db"}, - {file = "protobuf-3.20.3.tar.gz", hash = "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2"}, + {file = "protobuf-4.24.3-cp310-abi3-win32.whl", hash = "sha256:20651f11b6adc70c0f29efbe8f4a94a74caf61b6200472a9aea6e19898f9fcf4"}, + {file = "protobuf-4.24.3-cp310-abi3-win_amd64.whl", hash = "sha256:3d42e9e4796a811478c783ef63dc85b5a104b44aaaca85d4864d5b886e4b05e3"}, + {file = "protobuf-4.24.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:6e514e8af0045be2b56e56ae1bb14f43ce7ffa0f68b1c793670ccbe2c4fc7d2b"}, + {file = "protobuf-4.24.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:ba53c2f04798a326774f0e53b9c759eaef4f6a568ea7072ec6629851c8435959"}, + {file = "protobuf-4.24.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:f6ccbcf027761a2978c1406070c3788f6de4a4b2cc20800cc03d52df716ad675"}, + {file = "protobuf-4.24.3-cp37-cp37m-win32.whl", hash = "sha256:1b182c7181a2891e8f7f3a1b5242e4ec54d1f42582485a896e4de81aa17540c2"}, + {file = "protobuf-4.24.3-cp37-cp37m-win_amd64.whl", hash = "sha256:b0271a701e6782880d65a308ba42bc43874dabd1a0a0f41f72d2dac3b57f8e76"}, + {file = "protobuf-4.24.3-cp38-cp38-win32.whl", hash = "sha256:e29d79c913f17a60cf17c626f1041e5288e9885c8579832580209de8b75f2a52"}, + {file = "protobuf-4.24.3-cp38-cp38-win_amd64.whl", hash = "sha256:067f750169bc644da2e1ef18c785e85071b7c296f14ac53e0900e605da588719"}, + {file = "protobuf-4.24.3-cp39-cp39-win32.whl", hash = "sha256:2da777d34b4f4f7613cdf85c70eb9a90b1fbef9d36ae4a0ccfe014b0b07906f1"}, + {file = "protobuf-4.24.3-cp39-cp39-win_amd64.whl", hash = "sha256:f631bb982c5478e0c1c70eab383af74a84be66945ebf5dd6b06fc90079668d0b"}, + {file = "protobuf-4.24.3-py3-none-any.whl", hash = "sha256:f6f8dc65625dadaad0c8545319c2e2f0424fede988368893ca3844261342c11a"}, + {file = "protobuf-4.24.3.tar.gz", hash = "sha256:12e9ad2ec079b833176d2921be2cb24281fa591f0b119b208b788adc48c2561d"}, ] [[package]] @@ -1394,14 +1507,14 @@ pyserial = "*" [[package]] name = "pytest" -version = "7.4.0" +version = "7.4.2" description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, - {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, + {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, + {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, ] [package.dependencies] @@ -1417,14 +1530,14 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no [[package]] name = "pytest-aiohttp" -version = "1.0.4" +version = "1.0.5" description = "Pytest plugin for aiohttp support" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-aiohttp-1.0.4.tar.gz", hash = "sha256:39ff3a0d15484c01d1436cbedad575c6eafbf0f57cdf76fb94994c97b5b8c5a4"}, - {file = "pytest_aiohttp-1.0.4-py3-none-any.whl", hash = "sha256:1d2dc3a304c2be1fd496c0c2fb6b31ab60cd9fc33984f761f951f8ea1eb4ca95"}, + {file = "pytest-aiohttp-1.0.5.tar.gz", hash = "sha256:880262bc5951e934463b15e3af8bb298f11f7d4d3ebac970aab425aff10a780a"}, + {file = "pytest_aiohttp-1.0.5-py3-none-any.whl", hash = "sha256:63a5360fd2f34dda4ab8e6baee4c5f5be4cd186a403cabd498fced82ac9c561e"}, ] [package.dependencies] @@ -1567,20 +1680,20 @@ files = [ [[package]] name = "setuptools" -version = "68.0.0" +version = "68.2.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, - {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, + {file = "setuptools-68.2.0-py3-none-any.whl", hash = "sha256:af3d5949030c3f493f550876b2fd1dd5ec66689c4ee5d5344f009746f71fd5a8"}, + {file = "setuptools-68.2.0.tar.gz", hash = "sha256:00478ca80aeebeecb2f288d3206b0de568df5cd2b8fada1209843cc9a8d88a48"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" @@ -1724,4 +1837,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.9,<4" -content-hash = "0cdbaeb0b9c0ed473fe16a657c1011778b94b5ec141c2ea895efb2a0d71d3ba1" +content-hash = "db29577a6c352ad358fa1cc029f65a25b9f0ac528713ad4a6cf16bf92d3af911" diff --git a/pyproject.toml b/pyproject.toml index b25f1a28..85fd6877 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,17 +15,16 @@ reportMissingImports = "information" [tool.poetry.dependencies] python = ">=3.9,<4" -brewblox-service = "^3.1.2" +brewblox-service = "^3.3.1" pyserial-asyncio = "^0.6" -protobuf = "<4.0dev" -aiofiles = "^0.8.0" +protobuf = "^4.24.3" +aiofiles = "^23.2.1" aiozeroconf = "^0.1.8" debugpy = "^1.5.1" -Pint = "^0.19" +Pint = "^0.22" esptool = "^4.0" pytimeparse = "^1.1.8" ciso8601 = "^2.2.0" -cryptography = "40.0.1" [tool.poetry.group.dev.dependencies] pytest = "*" @@ -36,9 +35,9 @@ flake8 = "*" autopep8 = "*" aresponses = "*" flake8-quotes = "*" -invoke = "^2.1.3" -grpcio-tools = "<1.49" +invoke = "*" +grpcio-tools = "^1.58.0" [build-system] -requires = ["poetry>=0.12", "cryptography==40.0.1"] -build-backend = "poetry.masonry.api" +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" From db250dc94c52dcf582119622f8c863841c11f23c Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Wed, 13 Sep 2023 11:15:18 +0200 Subject: [PATCH 05/27] pull service with aiomqtt bump --- poetry.lock | 18 ++++++++++++++---- pyproject.toml | 2 +- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index b21e8e00..674810a3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -378,14 +378,14 @@ bitarray = ">=2.8.0,<3.0.0" [[package]] name = "brewblox-service" -version = "3.3.1" +version = "3.3.2" description = "Scaffolding for Brewblox backend services" category = "main" optional = false python-versions = ">=3.9,<4" files = [ - {file = "brewblox_service-3.3.1-py3-none-any.whl", hash = "sha256:38db50f18850a49dbb9803d49719a8b8db484218b396b709e651d4e0fdf48e04"}, - {file = "brewblox_service-3.3.1.tar.gz", hash = "sha256:4a37dbe96955e64c9d86e7fc8623d70219e6cfe037bf9bafac8fae5e2e6b1712"}, + {file = "brewblox_service-3.3.2-py3-none-any.whl", hash = "sha256:2b0bb621956a61e7846fa30d8005df8428416a0e3fe1ae2729f081d36bf7d8e3"}, + {file = "brewblox_service-3.3.2.tar.gz", hash = "sha256:0bf13ecf42729978cb9e4b2b3f5b4c5b2623bdc1aab16581fa6ca299ca6b4eac"}, ] [package.dependencies] @@ -1629,6 +1629,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -1636,8 +1637,15 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -1654,6 +1662,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -1661,6 +1670,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -1837,4 +1847,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.9,<4" -content-hash = "db29577a6c352ad358fa1cc029f65a25b9f0ac528713ad4a6cf16bf92d3af911" +content-hash = "77746098165efa0d9d239d42ce2a21681d750d6ed85cb1962b0604aa1ab5c149" diff --git a/pyproject.toml b/pyproject.toml index 85fd6877..3e142fe8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ reportMissingImports = "information" [tool.poetry.dependencies] python = ">=3.9,<4" -brewblox-service = "^3.3.1" +brewblox-service = "^3.3.2" pyserial-asyncio = "^0.6" protobuf = "^4.24.3" aiofiles = "^23.2.1" From b6a7387dcd445bf6108dc9fc8d266d7e1c8ec054 Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Wed, 13 Sep 2023 15:57:30 +0200 Subject: [PATCH 06/27] build with 3.11 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 31061240..72e5cc6b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: - uses: docker/setup-buildx-action@v2 - uses: actions/setup-python@v4 with: - python-version: "3.9" + python-version: "3.11" - name: (Service) Image metadata id: service_meta From d9abcec3cf73a138bf6df466252fd766be65893b Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Thu, 14 Sep 2023 13:07:29 +0200 Subject: [PATCH 07/27] pull firmware mqtt fix --- firmware.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware.ini b/firmware.ini index b495a38f..d0d9ef52 100644 --- a/firmware.ini +++ b/firmware.ini @@ -1,7 +1,7 @@ [FIRMWARE] -firmware_version=bd82dd18 -firmware_date=2023-07-29 -firmware_sha=bd82dd184bd94a85702bf16f2423245aaa7f603c +firmware_version=11cc4893 +firmware_date=2023-09-13 +firmware_sha=11cc489302ea3a95d70f8ef89ae73a6faacfc7a7 proto_version=67da1ac7 proto_date=2023-07-26 system_version=3.2.0 From 6dbc8e2133fe1c6164883b9d8728717987252ce5 Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Thu, 14 Sep 2023 18:22:20 +0200 Subject: [PATCH 08/27] Use heredoc syntax for dockerfile --- Dockerfile.service | 53 +++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/Dockerfile.service b/Dockerfile.service index 3604e655..c5705928 100644 --- a/Dockerfile.service +++ b/Dockerfile.service @@ -6,35 +6,48 @@ ENV PIP_FIND_LINKS=/wheeley COPY ./dist /app/dist COPY ./firmware /app/firmware -RUN set -ex \ - && mkdir /wheeley \ - && pip3 install --upgrade pip wheel setuptools \ - && pip3 wheel --wheel-dir=/wheeley -r /app/dist/requirements.txt \ - && pip3 wheel --wheel-dir=/wheeley /app/dist/*.tar.gz \ - && ARCH="$(dpkg --print-architecture)" \ - && if [ "${ARCH}" = "amd64" ]; then OTHER_SIM='brewblox-{arm32,arm64}.sim'; fi \ - && if [ "${ARCH}" = "armhf" ]; then OTHER_SIM='brewblox-{amd64,arm64}.sim'; fi \ - && if [ "${ARCH}" = "arm64" ]; then OTHER_SIM='brewblox-{amd64,arm32}.sim'; fi \ - && bash -c "rm /app/firmware/${OTHER_SIM}" \ - && rm /app/firmware/*.elf +RUN < Date: Fri, 15 Sep 2023 13:08:47 +0200 Subject: [PATCH 09/27] Run in venv --- Dockerfile.service | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/Dockerfile.service b/Dockerfile.service index c5705928..2bc9257d 100644 --- a/Dockerfile.service +++ b/Dockerfile.service @@ -2,17 +2,24 @@ FROM python:3.11-bookworm as base ENV PIP_EXTRA_INDEX_URL=https://www.piwheels.org/simple ENV PIP_FIND_LINKS=/wheeley +ENV VENV=/app/.venv +ENV PATH="$VENV/bin:$PATH" COPY ./dist /app/dist COPY ./firmware /app/firmware -RUN < Date: Wed, 11 Oct 2023 16:52:36 +0200 Subject: [PATCH 10/27] Use error objects for non interface types that can't be decoded --- brewblox_devcon_spark/codec/__init__.py | 4 ++-- brewblox_devcon_spark/codec/lookup.py | 20 +++++++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/brewblox_devcon_spark/codec/__init__.py b/brewblox_devcon_spark/codec/__init__.py index 88337ec5..12f8a6cb 100644 --- a/brewblox_devcon_spark/codec/__init__.py +++ b/brewblox_devcon_spark/codec/__init__.py @@ -15,7 +15,7 @@ IntermediateResponse) from . import pb2, time_utils, unit_conversion -from .lookup import INTERFACE_LOOKUPS, OBJECT_LOOKUPS +from .lookup import COMBINED_LOOKUPS, INTERFACE_LOOKUPS, OBJECT_LOOKUPS from .opts import (DateFormatOpt, DecodeOpts, FilterOpt, MetadataOpt, ProtoEnumOpt) from .processor import ProtobufProcessor @@ -128,7 +128,7 @@ def encode_payload(self, payload: DecodedPayload) -> EncodedPayload: # Interface-only payload if payload.content is None: - lookup = next((v for v in INTERFACE_LOOKUPS + lookup = next((v for v in COMBINED_LOOKUPS if v.type_str == payload.blockType)) return EncodedPayload( blockId=payload.blockId, diff --git a/brewblox_devcon_spark/codec/lookup.py b/brewblox_devcon_spark/codec/lookup.py index 7e16df7e..f9b51487 100644 --- a/brewblox_devcon_spark/codec/lookup.py +++ b/brewblox_devcon_spark/codec/lookup.py @@ -14,6 +14,10 @@ BlockType: EnumTypeWrapper = pb2.brewblox_pb2.BlockType +# Block type values below this are reserved for interfaces +# They will not be associated with actual messages +BLOCK_INTERFACE_TYPE_END = 255 + @dataclass(frozen=True) class InterfaceLookup: @@ -31,11 +35,12 @@ class ObjectLookup: def _interface_lookup_generator() -> Generator[InterfaceLookup, None, None]: - for blockType in BlockType.values(): - yield InterfaceLookup( - type_str=BlockType.Name(blockType), - type_int=blockType, - ) + for block_type in BlockType.values(): + if block_type <= BLOCK_INTERFACE_TYPE_END: + yield InterfaceLookup( + type_str=BlockType.Name(block_type), + type_int=block_type, + ) def _object_lookup_generator() -> Generator[ObjectLookup, None, None]: @@ -86,3 +91,8 @@ def _object_lookup_generator() -> Generator[ObjectLookup, None, None]: type_int=9001, ), ] + +COMBINED_LOOKUPS: list[InterfaceLookup] = [ + *OBJECT_LOOKUPS, + *INTERFACE_LOOKUPS, +] From b9009682308e9d5fd6702b3df091c3912035306b Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Thu, 12 Oct 2023 13:59:06 +0200 Subject: [PATCH 11/27] pull firmware with buffer fixes; libssl-3 dependency --- README.md | 10 ++-------- firmware.ini | 6 +++--- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 21296210..4d4dbc5c 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,6 @@ To set up the development environment, follow the instructions at Date: Tue, 31 Oct 2023 14:47:10 +0100 Subject: [PATCH 12/27] improved detection for container restarts during USB discovery --- .../connection/connection_handler.py | 50 ++++++++++++++---- test/test_connection_handler.py | 51 +++++++++++++++++-- 2 files changed, 86 insertions(+), 15 deletions(-) diff --git a/brewblox_devcon_spark/connection/connection_handler.py b/brewblox_devcon_spark/connection/connection_handler.py index 6ba9f3fa..7940f421 100644 --- a/brewblox_devcon_spark/connection/connection_handler.py +++ b/brewblox_devcon_spark/connection/connection_handler.py @@ -35,7 +35,7 @@ class ConnectionHandler(repeater.RepeaterFeature, ConnectionCallbacks): def __init__(self, app: web.Application): super().__init__(app) - self._retry_count: int = 0 + self._attempts: int = 0 self._impl: ConnectionImplBase = None def __str__(self): @@ -49,6 +49,37 @@ def connected(self) -> bool: return self._impl is not None \ and self._impl.connected.is_set() + @property + def usb_compatible(self) -> bool: + config: ServiceConfig = self.app['config'] + + # Simulations (internal or external) do not use USB + if config.mock or config.simulation: + return False + + # Hardcoded addresses take precedence over device discovery + if config.device_serial or config.device_host: + return config.device_serial is not None + + # USB is explicitly enabled + if config.discovery == DiscoveryType.usb: + return True + + # TCP is explicitly enabled + if config.discovery != DiscoveryType.all: + return False + + # Spark models can be identified by device ID + # Spark 2/3 use 12 bytes / 24 characters + # Spark 4 uses 6 bytes / 12 characters + # Spark simulations can have variable length IDs + # USB should only be disabled if we're sure it is not supported + if config.device_id and len(config.device_id) == 12: + return False + + # We're not sure + return True + async def on_event(self, msg: str): """ This function can be replaced by whoever wants to receive @@ -112,11 +143,10 @@ async def connect(self) -> ConnectionImplBase: async def run(self): """Implements RepeaterFeature.run""" - config: ServiceConfig = self.app['config'] delay = service_store.get_reconnect_delay(self.app) try: - if self._retry_count > MAX_RETRY_COUNT: + if self._attempts > MAX_RETRY_COUNT: raise ConnectionAbortedError('Retry attempts exhausted') await asyncio.sleep(delay) @@ -129,7 +159,7 @@ async def run(self): self._impl.kind, self._impl.address) - self._retry_count = 0 + self._attempts = 0 self._reconnect_interval = 0 await self._impl.disconnected.wait() @@ -141,17 +171,17 @@ async def run(self): # USB devices that were plugged in after container start are not visible # If we are potentially connecting to a USB device, we need to restart - if config.device_serial or config.discovery in [DiscoveryType.all, DiscoveryType.usb]: + if self.usb_compatible: raise web.GracefulExit() else: - self._retry_count = 0 + self._attempts = 0 except Exception as ex: - if self._retry_count: - LOGGER.debug(strex(ex)) - else: + self._attempts += 1 + if self._attempts == 1: LOGGER.error(strex(ex)) - self._retry_count += 1 + else: + LOGGER.debug(strex(ex)) finally: with suppress(Exception): diff --git a/test/test_connection_handler.py b/test/test_connection_handler.py index 253375e4..5ff3489a 100644 --- a/test/test_connection_handler.py +++ b/test/test_connection_handler.py @@ -54,6 +54,46 @@ async def test_calc_backoff(): assert connection_handler.calc_backoff(60) == connection_handler.MAX_RECONNECT_DELAY_S +async def test_usb_compatible(app, client, mocker): + config: ServiceConfig = app['config'] + handler = connection_handler.ConnectionHandler(app) + + # Set all relevant settings to default + def reset(): + config.discovery = DiscoveryType.all + config.device_serial = None + config.device_host = None + config.device_id = None + config.simulation = False + config.mock = False + + # The default is to enable USB + reset() + assert handler.usb_compatible + + reset() + config.mock = True + assert not handler.usb_compatible + + reset() + config.device_host = 'localhost' + assert not handler.usb_compatible + + reset() + config.discovery = DiscoveryType.lan + assert not handler.usb_compatible + + reset() + config.discovery = DiscoveryType.usb + assert handler.usb_compatible + + # Positive identification of Spark 4 ID + reset() + config.discovery = DiscoveryType.all + config.device_id = 'x'*12 + assert not handler.usb_compatible + + async def test_handler_discovery(app, client, mocker): mocker.patch(TESTED + '.DISCOVERY_INTERVAL_S', 0) @@ -256,6 +296,7 @@ async def test_handler_disconnect(app, client, mocker): async def test_handler_connect_error(app, client, mocker): mocker.patch(TESTED + '.web.GracefulExit', DummyExit) + mocker.patch(TESTED + '.calc_backoff').return_value = 0.0001 m_connect_mock: AsyncMock = mocker.patch(TESTED + '.connect_mock', autospec=True) m_connect_mock.side_effect = ConnectionRefusedError @@ -264,12 +305,12 @@ async def test_handler_connect_error(app, client, mocker): handler = connection_handler.ConnectionHandler(app) # Retry until attempts exhausted - with pytest.raises(DummyExit): - async with timeout(5): - while True: - await handler.run() + # This is a mock - it will not attempt to restart the service + for _ in range(connection_handler.MAX_RETRY_COUNT * 2): + await handler.run() - assert m_connect_mock.await_count == 1 + connection_handler.MAX_RETRY_COUNT + # It immediately threw a connection abort once the retry count was exceeded + assert m_connect_mock.await_count == connection_handler.MAX_RETRY_COUNT * 2 - 1 async def test_handler_discovery_error(app, client, mocker): From 11746616a2c31cfe8e925428d52a6661d6ccf85a Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Tue, 31 Oct 2023 14:59:11 +0100 Subject: [PATCH 13/27] more robust test server --- test/test_connection_handler.py | 1 - test/test_stream_connection.py | 24 +++++++++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/test/test_connection_handler.py b/test/test_connection_handler.py index 5ff3489a..3ee156bd 100644 --- a/test/test_connection_handler.py +++ b/test/test_connection_handler.py @@ -7,7 +7,6 @@ from unittest.mock import AsyncMock import pytest -from async_timeout import timeout from brewblox_service import scheduler from brewblox_devcon_spark import exceptions, service_status, service_store diff --git a/test/test_stream_connection.py b/test/test_stream_connection.py index b4e0075c..dddc0296 100644 --- a/test/test_stream_connection.py +++ b/test/test_stream_connection.py @@ -38,17 +38,19 @@ def test_port(): @pytest.fixture async def echo_server(test_port): async def echo_handler(reader: asyncio.StreamReader, writer: asyncio.StreamWriter): - while True: - data = await reader.read(100) - if not data: - break - if data == b'error\n': - writer.write_eof() - break - writer.write(data+b'') - await writer.drain() - writer.close() - await writer.wait_closed() + try: + while True: + data = await reader.read(100) + if not data: + break + if data == b'error\n': + writer.write_eof() + break + writer.write(data+b'') + await writer.drain() + finally: + writer.close() + await writer.wait_closed() server = await asyncio.start_server(echo_handler, 'localhost', test_port) task = asyncio.create_task(server.serve_forever()) yield server From bbed279dcb2e46dfa414aa38597919533168f488 Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Tue, 31 Oct 2023 15:16:47 +0100 Subject: [PATCH 14/27] firmware bump --- firmware.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware.ini b/firmware.ini index 0189ca56..2e497f07 100644 --- a/firmware.ini +++ b/firmware.ini @@ -1,7 +1,7 @@ [FIRMWARE] -firmware_version=616eb590 -firmware_date=2023-10-12 -firmware_sha=616eb590c3a7d606cf78aadd3d652ac0ce97c508 +firmware_version=1a38c071 +firmware_date=2023-10-30 +firmware_sha=1a38c071d1596b2f66322a7182b98e3608ab4f88 proto_version=67da1ac7 proto_date=2023-07-26 system_version=3.2.0 From 4ec599ae7c393454eadd1dec159f668cfcb4bbe7 Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Mon, 20 Nov 2023 15:48:30 +0100 Subject: [PATCH 15/27] add OpenApi docstrings to debug endpoints --- brewblox_devcon_spark/api/debug_api.py | 60 ++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/brewblox_devcon_spark/api/debug_api.py b/brewblox_devcon_spark/api/debug_api.py index 54d3c5bd..2ad9067a 100644 --- a/brewblox_devcon_spark/api/debug_api.py +++ b/brewblox_devcon_spark/api/debug_api.py @@ -29,6 +29,16 @@ def __init__(self, request: web.Request) -> None: @routes.view('/_debug/encode_request') class EncodeRequestView(CodecView): async def post(self, args: IntermediateRequest) -> r200[EncodedMessage]: + """ + Encode a Request object to a base64 string. + + Te object encoded to protobuf bytes. + The bytes are encoded as base64 to make them ASCII-safe. + + Included payloads must have been encoded using /_debug/encode_payload. + + tags: Debug + """ message = self.codec.encode_request(args) return web.json_response( EncodedMessage(message=message).dict() @@ -38,6 +48,16 @@ async def post(self, args: IntermediateRequest) -> r200[EncodedMessage]: @routes.view('/_debug/decode_request') class DecodeRequestView(CodecView): async def post(self, args: EncodedMessage) -> r200[IntermediateRequest]: + """ + Decode a Request object from a base64 string. + + The base64 string is converted to bytes. + The bytes are decoded using protobuf. + + Included payloads must be decoded using /_debug/decode_payload + + tags: Debug + """ request = self.codec.decode_request(args.message) return web.json_response( request.clean_dict() @@ -47,6 +67,16 @@ async def post(self, args: EncodedMessage) -> r200[IntermediateRequest]: @routes.view('/_debug/encode_response') class EncodeResponseView(CodecView): async def post(self, args: IntermediateResponse) -> r200[EncodedMessage]: + """ + Encode a Response object to a base64 string. + + Te object encoded to protobuf bytes. + The bytes are encoded as base64 to make them ASCII-safe. + + Included payloads must have been encoded using /_debug/encode_payload. + + tags: Debug + """ message = self.codec.encode_response(args) return web.json_response( EncodedMessage(message=message).dict() @@ -56,6 +86,16 @@ async def post(self, args: IntermediateResponse) -> r200[EncodedMessage]: @routes.view('/_debug/decode_response') class DecodeResponseView(CodecView): async def post(self, args: EncodedMessage) -> r200[IntermediateResponse]: + """ + Decode a Response object from a base64 string. + + The base64 string is converted to bytes. + The bytes are decoded using protobuf. + + Included payloads must be decoded using /_debug/decode_payload + + tags: Debug + """ response = self.codec.decode_response(args.message) return web.json_response( response.clean_dict() @@ -65,6 +105,16 @@ async def post(self, args: EncodedMessage) -> r200[IntermediateResponse]: @routes.view('/_debug/encode_payload') class EncodePayloadView(CodecView): async def post(self, args: DecodedPayload) -> r200[EncodedPayload]: + """ + Encode a Payload object. + + `content` is encoded to protobuf bytes, + and then encoded as base64 to make it ASCII-safe. + + This operation is symmetrical with /_debug/decode_payload. + + tags: Debug + """ payload = self.codec.encode_payload(args) return web.json_response( payload.clean_dict() @@ -74,6 +124,16 @@ async def post(self, args: DecodedPayload) -> r200[EncodedPayload]: @routes.view('/_debug/decode_payload') class DecodePayloadView(CodecView): async def post(self, args: EncodedPayload) -> r200[DecodedPayload]: + """ + Decode a Payload object. + + `content` is converted to bytes, + and then decoded using protobuf. + + This operation is symmetrical with /_debug/encode_payload + + tags: Debug + """ payload = self.codec.decode_payload(args) return web.json_response( payload.clean_dict() From ec28a3443757ca10451e946516eb2e6fb5313713 Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Wed, 22 Nov 2023 20:03:03 +0100 Subject: [PATCH 16/27] bump to fix errors in aiohttp update --- .../connection/connection_handler.py | 3 +- .../connection/stream_connection.py | 3 +- brewblox_devcon_spark/mdns.py | 5 +- brewblox_devcon_spark/synchronization.py | 3 +- poetry.lock | 1574 ++++++++--------- pyproject.toml | 2 +- 6 files changed, 720 insertions(+), 870 deletions(-) diff --git a/brewblox_devcon_spark/connection/connection_handler.py b/brewblox_devcon_spark/connection/connection_handler.py index 7940f421..bfa23dff 100644 --- a/brewblox_devcon_spark/connection/connection_handler.py +++ b/brewblox_devcon_spark/connection/connection_handler.py @@ -2,7 +2,6 @@ from contextlib import suppress from aiohttp import web -from async_timeout import timeout from brewblox_service import brewblox_logger, features, repeater, strex from brewblox_devcon_spark import exceptions, service_status, service_store @@ -99,7 +98,7 @@ async def discover(self) -> ConnectionImplBase: LOGGER.info(f'Discovering devices... ({discovery_type})') try: - async with timeout(DISCOVERY_TIMEOUT_S): + async with asyncio.timeout(DISCOVERY_TIMEOUT_S): while True: if discovery_type in [DiscoveryType.all, DiscoveryType.usb]: result = await discover_usb(self.app, self) diff --git a/brewblox_devcon_spark/connection/stream_connection.py b/brewblox_devcon_spark/connection/stream_connection.py index bc34fd78..cd1b7256 100644 --- a/brewblox_devcon_spark/connection/stream_connection.py +++ b/brewblox_devcon_spark/connection/stream_connection.py @@ -13,7 +13,6 @@ from typing import Optional from aiohttp import web -from async_timeout import timeout from brewblox_service import brewblox_logger, strex from serial.tools import list_ports @@ -130,7 +129,7 @@ async def connect_subprocess(app: web.Application, # We just started a subprocess # Give it some time to get started and respond to the port try: - async with timeout(SUBPROCESS_CONNECT_TIMEOUT_S): + async with asyncio.timeout(SUBPROCESS_CONNECT_TIMEOUT_S): while True: if proc.returncode is not None: raise ChildProcessError(f'Subprocess exited with return code {proc.returncode}') diff --git a/brewblox_devcon_spark/mdns.py b/brewblox_devcon_spark/mdns.py index 60e1eaf6..391e9eb4 100644 --- a/brewblox_devcon_spark/mdns.py +++ b/brewblox_devcon_spark/mdns.py @@ -10,7 +10,6 @@ from aiozeroconf import ServiceBrowser, ServiceStateChange, Zeroconf from aiozeroconf.aiozeroconf import ServiceInfo -from async_timeout import timeout from brewblox_service import brewblox_logger DEFAULT_TIMEOUT_S = 5 @@ -65,7 +64,7 @@ async def discover_all( timeout_v: float, ) -> Generator[ConnectInfo, None, None]: with suppress(asyncio.TimeoutError): - async with timeout(timeout_v): + async with asyncio.timeout(timeout_v): async for res in _discover(desired_id, dns_type): # pragma: no branch yield res @@ -75,6 +74,6 @@ async def discover_one( dns_type: str, timeout_v: Optional[float] = None, ) -> ConnectInfo: - async with timeout(timeout_v): + async with asyncio.timeout(timeout_v): async for res in _discover(desired_id, dns_type): # pragma: no branch return res diff --git a/brewblox_devcon_spark/synchronization.py b/brewblox_devcon_spark/synchronization.py index e86c6012..82d37104 100644 --- a/brewblox_devcon_spark/synchronization.py +++ b/brewblox_devcon_spark/synchronization.py @@ -46,7 +46,6 @@ from functools import wraps from aiohttp import web -from async_timeout import timeout from brewblox_service import brewblox_logger, features, repeater, strex from brewblox_devcon_spark import (block_store, codec, commander, const, @@ -154,7 +153,7 @@ async def _sync_handshake(self): # Simultaneously prompt a handshake, and wait for it to be received ack_task = asyncio.create_task(service_status.wait_acknowledged(self.app)) try: - async with timeout(HANDSHAKE_TIMEOUT_S): + async with asyncio.timeout(HANDSHAKE_TIMEOUT_S): while not ack_task.done(): await self._prompt_handshake() await asyncio.wait([ack_task], timeout=PING_INTERVAL_S) diff --git a/poetry.lock b/poetry.lock index 674810a3..8cf57c59 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,9 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "aiofiles" version = "23.2.1" description = "File support for asyncio." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -14,118 +13,103 @@ files = [ [[package]] name = "aiohttp" -version = "3.8.5" +version = "3.9.0" description = "Async http client/server framework (asyncio)" -category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "aiohttp-3.8.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a94159871304770da4dd371f4291b20cac04e8c94f11bdea1c3478e557fbe0d8"}, - {file = "aiohttp-3.8.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:13bf85afc99ce6f9ee3567b04501f18f9f8dbbb2ea11ed1a2e079670403a7c84"}, - {file = "aiohttp-3.8.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ce2ac5708501afc4847221a521f7e4b245abf5178cf5ddae9d5b3856ddb2f3a"}, - {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96943e5dcc37a6529d18766597c491798b7eb7a61d48878611298afc1fca946c"}, - {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ad5c3c4590bb3cc28b4382f031f3783f25ec223557124c68754a2231d989e2b"}, - {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c413c633d0512df4dc7fd2373ec06cc6a815b7b6d6c2f208ada7e9e93a5061d"}, - {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df72ac063b97837a80d80dec8d54c241af059cc9bb42c4de68bd5b61ceb37caa"}, - {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c48c5c0271149cfe467c0ff8eb941279fd6e3f65c9a388c984e0e6cf57538e14"}, - {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:368a42363c4d70ab52c2c6420a57f190ed3dfaca6a1b19afda8165ee16416a82"}, - {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7607ec3ce4993464368505888af5beb446845a014bc676d349efec0e05085905"}, - {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0d21c684808288a98914e5aaf2a7c6a3179d4df11d249799c32d1808e79503b5"}, - {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:312fcfbacc7880a8da0ae8b6abc6cc7d752e9caa0051a53d217a650b25e9a691"}, - {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ad093e823df03bb3fd37e7dec9d4670c34f9e24aeace76808fc20a507cace825"}, - {file = "aiohttp-3.8.5-cp310-cp310-win32.whl", hash = "sha256:33279701c04351a2914e1100b62b2a7fdb9a25995c4a104259f9a5ead7ed4802"}, - {file = "aiohttp-3.8.5-cp310-cp310-win_amd64.whl", hash = "sha256:6e4a280e4b975a2e7745573e3fc9c9ba0d1194a3738ce1cbaa80626cc9b4f4df"}, - {file = "aiohttp-3.8.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae871a964e1987a943d83d6709d20ec6103ca1eaf52f7e0d36ee1b5bebb8b9b9"}, - {file = "aiohttp-3.8.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:461908b2578955045efde733719d62f2b649c404189a09a632d245b445c9c975"}, - {file = "aiohttp-3.8.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72a860c215e26192379f57cae5ab12b168b75db8271f111019509a1196dfc780"}, - {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc14be025665dba6202b6a71cfcdb53210cc498e50068bc088076624471f8bb9"}, - {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8af740fc2711ad85f1a5c034a435782fbd5b5f8314c9a3ef071424a8158d7f6b"}, - {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:841cd8233cbd2111a0ef0a522ce016357c5e3aff8a8ce92bcfa14cef890d698f"}, - {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ed1c46fb119f1b59304b5ec89f834f07124cd23ae5b74288e364477641060ff"}, - {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84f8ae3e09a34f35c18fa57f015cc394bd1389bce02503fb30c394d04ee6b938"}, - {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62360cb771707cb70a6fd114b9871d20d7dd2163a0feafe43fd115cfe4fe845e"}, - {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:23fb25a9f0a1ca1f24c0a371523546366bb642397c94ab45ad3aedf2941cec6a"}, - {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0ba0d15164eae3d878260d4c4df859bbdc6466e9e6689c344a13334f988bb53"}, - {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5d20003b635fc6ae3f96d7260281dfaf1894fc3aa24d1888a9b2628e97c241e5"}, - {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0175d745d9e85c40dcc51c8f88c74bfbaef9e7afeeeb9d03c37977270303064c"}, - {file = "aiohttp-3.8.5-cp311-cp311-win32.whl", hash = "sha256:2e1b1e51b0774408f091d268648e3d57f7260c1682e7d3a63cb00d22d71bb945"}, - {file = "aiohttp-3.8.5-cp311-cp311-win_amd64.whl", hash = "sha256:043d2299f6dfdc92f0ac5e995dfc56668e1587cea7f9aa9d8a78a1b6554e5755"}, - {file = "aiohttp-3.8.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cae533195e8122584ec87531d6df000ad07737eaa3c81209e85c928854d2195c"}, - {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f21e83f355643c345177a5d1d8079f9f28b5133bcd154193b799d380331d5d3"}, - {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a75ef35f2df54ad55dbf4b73fe1da96f370e51b10c91f08b19603c64004acc"}, - {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e2e9839e14dd5308ee773c97115f1e0a1cb1d75cbeeee9f33824fa5144c7634"}, - {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44e65da1de4403d0576473e2344828ef9c4c6244d65cf4b75549bb46d40b8dd"}, - {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78d847e4cde6ecc19125ccbc9bfac4a7ab37c234dd88fbb3c5c524e8e14da543"}, - {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:c7a815258e5895d8900aec4454f38dca9aed71085f227537208057853f9d13f2"}, - {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:8b929b9bd7cd7c3939f8bcfffa92fae7480bd1aa425279d51a89327d600c704d"}, - {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:5db3a5b833764280ed7618393832e0853e40f3d3e9aa128ac0ba0f8278d08649"}, - {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:a0215ce6041d501f3155dc219712bc41252d0ab76474615b9700d63d4d9292af"}, - {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:fd1ed388ea7fbed22c4968dd64bab0198de60750a25fe8c0c9d4bef5abe13824"}, - {file = "aiohttp-3.8.5-cp36-cp36m-win32.whl", hash = "sha256:6e6783bcc45f397fdebc118d772103d751b54cddf5b60fbcc958382d7dd64f3e"}, - {file = "aiohttp-3.8.5-cp36-cp36m-win_amd64.whl", hash = "sha256:b5411d82cddd212644cf9360879eb5080f0d5f7d809d03262c50dad02f01421a"}, - {file = "aiohttp-3.8.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:01d4c0c874aa4ddfb8098e85d10b5e875a70adc63db91f1ae65a4b04d3344cda"}, - {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5980a746d547a6ba173fd5ee85ce9077e72d118758db05d229044b469d9029a"}, - {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a482e6da906d5e6e653be079b29bc173a48e381600161c9932d89dfae5942ef"}, - {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80bd372b8d0715c66c974cf57fe363621a02f359f1ec81cba97366948c7fc873"}, - {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1161b345c0a444ebcf46bf0a740ba5dcf50612fd3d0528883fdc0eff578006a"}, - {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd56db019015b6acfaaf92e1ac40eb8434847d9bf88b4be4efe5bfd260aee692"}, - {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:153c2549f6c004d2754cc60603d4668899c9895b8a89397444a9c4efa282aaf4"}, - {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4a01951fabc4ce26ab791da5f3f24dca6d9a6f24121746eb19756416ff2d881b"}, - {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bfb9162dcf01f615462b995a516ba03e769de0789de1cadc0f916265c257e5d8"}, - {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:7dde0009408969a43b04c16cbbe252c4f5ef4574ac226bc8815cd7342d2028b6"}, - {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4149d34c32f9638f38f544b3977a4c24052042affa895352d3636fa8bffd030a"}, - {file = "aiohttp-3.8.5-cp37-cp37m-win32.whl", hash = "sha256:68c5a82c8779bdfc6367c967a4a1b2aa52cd3595388bf5961a62158ee8a59e22"}, - {file = "aiohttp-3.8.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2cf57fb50be5f52bda004b8893e63b48530ed9f0d6c96c84620dc92fe3cd9b9d"}, - {file = "aiohttp-3.8.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:eca4bf3734c541dc4f374ad6010a68ff6c6748f00451707f39857f429ca36ced"}, - {file = "aiohttp-3.8.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1274477e4c71ce8cfe6c1ec2f806d57c015ebf84d83373676036e256bc55d690"}, - {file = "aiohttp-3.8.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28c543e54710d6158fc6f439296c7865b29e0b616629767e685a7185fab4a6b9"}, - {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:910bec0c49637d213f5d9877105d26e0c4a4de2f8b1b29405ff37e9fc0ad52b8"}, - {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5443910d662db951b2e58eb70b0fbe6b6e2ae613477129a5805d0b66c54b6cb7"}, - {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e460be6978fc24e3df83193dc0cc4de46c9909ed92dd47d349a452ef49325b7"}, - {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb1558def481d84f03b45888473fc5a1f35747b5f334ef4e7a571bc0dfcb11f8"}, - {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34dd0c107799dcbbf7d48b53be761a013c0adf5571bf50c4ecad5643fe9cfcd0"}, - {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aa1990247f02a54185dc0dff92a6904521172a22664c863a03ff64c42f9b5410"}, - {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0e584a10f204a617d71d359fe383406305a4b595b333721fa50b867b4a0a1548"}, - {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:a3cf433f127efa43fee6b90ea4c6edf6c4a17109d1d037d1a52abec84d8f2e42"}, - {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:c11f5b099adafb18e65c2c997d57108b5bbeaa9eeee64a84302c0978b1ec948b"}, - {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:84de26ddf621d7ac4c975dbea4c945860e08cccde492269db4e1538a6a6f3c35"}, - {file = "aiohttp-3.8.5-cp38-cp38-win32.whl", hash = "sha256:ab88bafedc57dd0aab55fa728ea10c1911f7e4d8b43e1d838a1739f33712921c"}, - {file = "aiohttp-3.8.5-cp38-cp38-win_amd64.whl", hash = "sha256:5798a9aad1879f626589f3df0f8b79b3608a92e9beab10e5fda02c8a2c60db2e"}, - {file = "aiohttp-3.8.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a6ce61195c6a19c785df04e71a4537e29eaa2c50fe745b732aa937c0c77169f3"}, - {file = "aiohttp-3.8.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:773dd01706d4db536335fcfae6ea2440a70ceb03dd3e7378f3e815b03c97ab51"}, - {file = "aiohttp-3.8.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f83a552443a526ea38d064588613aca983d0ee0038801bc93c0c916428310c28"}, - {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f7372f7341fcc16f57b2caded43e81ddd18df53320b6f9f042acad41f8e049a"}, - {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea353162f249c8097ea63c2169dd1aa55de1e8fecbe63412a9bc50816e87b761"}, - {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d47ae48db0b2dcf70bc8a3bc72b3de86e2a590fc299fdbbb15af320d2659de"}, - {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d827176898a2b0b09694fbd1088c7a31836d1a505c243811c87ae53a3f6273c1"}, - {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3562b06567c06439d8b447037bb655ef69786c590b1de86c7ab81efe1c9c15d8"}, - {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4e874cbf8caf8959d2adf572a78bba17cb0e9d7e51bb83d86a3697b686a0ab4d"}, - {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6809a00deaf3810e38c628e9a33271892f815b853605a936e2e9e5129762356c"}, - {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:33776e945d89b29251b33a7e7d006ce86447b2cfd66db5e5ded4e5cd0340585c"}, - {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:eaeed7abfb5d64c539e2db173f63631455f1196c37d9d8d873fc316470dfbacd"}, - {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e91d635961bec2d8f19dfeb41a539eb94bd073f075ca6dae6c8dc0ee89ad6f91"}, - {file = "aiohttp-3.8.5-cp39-cp39-win32.whl", hash = "sha256:00ad4b6f185ec67f3e6562e8a1d2b69660be43070bd0ef6fcec5211154c7df67"}, - {file = "aiohttp-3.8.5-cp39-cp39-win_amd64.whl", hash = "sha256:c0a9034379a37ae42dea7ac1e048352d96286626251862e448933c0f59cbd79c"}, - {file = "aiohttp-3.8.5.tar.gz", hash = "sha256:b9552ec52cc147dbf1944ac7ac98af7602e51ea2dcd076ed194ca3c0d1c7d0bc"}, + {file = "aiohttp-3.9.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6896b8416be9ada4d22cd359d7cb98955576ce863eadad5596b7cdfbf3e17c6c"}, + {file = "aiohttp-3.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1736d87dad8ef46a8ec9cddd349fa9f7bd3a064c47dd6469c0d6763d3d49a4fc"}, + {file = "aiohttp-3.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c9e5f4d7208cda1a2bb600e29069eecf857e6980d0ccc922ccf9d1372c16f4b"}, + {file = "aiohttp-3.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8488519aa05e636c5997719fe543c8daf19f538f4fa044f3ce94bee608817cff"}, + {file = "aiohttp-3.9.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ab16c254e2312efeb799bc3c06897f65a133b38b69682bf75d1f1ee1a9c43a9"}, + {file = "aiohttp-3.9.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7a94bde005a8f926d0fa38b88092a03dea4b4875a61fbcd9ac6f4351df1b57cd"}, + {file = "aiohttp-3.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b777c9286b6c6a94f50ddb3a6e730deec327e9e2256cb08b5530db0f7d40fd8"}, + {file = "aiohttp-3.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:571760ad7736b34d05597a1fd38cbc7d47f7b65deb722cb8e86fd827404d1f6b"}, + {file = "aiohttp-3.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:deac0a32aec29608eb25d730f4bc5a261a65b6c48ded1ed861d2a1852577c932"}, + {file = "aiohttp-3.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4ee1b4152bc3190cc40ddd6a14715e3004944263ea208229ab4c297712aa3075"}, + {file = "aiohttp-3.9.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:3607375053df58ed6f23903aa10cf3112b1240e8c799d243bbad0f7be0666986"}, + {file = "aiohttp-3.9.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:65b0a70a25456d329a5e1426702dde67be0fb7a4ead718005ba2ca582d023a94"}, + {file = "aiohttp-3.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5a2eb5311a37fe105aa35f62f75a078537e1a9e4e1d78c86ec9893a3c97d7a30"}, + {file = "aiohttp-3.9.0-cp310-cp310-win32.whl", hash = "sha256:2cbc14a13fb6b42d344e4f27746a4b03a2cb0c1c3c5b932b0d6ad8881aa390e3"}, + {file = "aiohttp-3.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ac9669990e2016d644ba8ae4758688534aabde8dbbc81f9af129c3f5f01ca9cd"}, + {file = "aiohttp-3.9.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f8e05f5163528962ce1d1806fce763ab893b1c5b7ace0a3538cd81a90622f844"}, + {file = "aiohttp-3.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4afa8f71dba3a5a2e1e1282a51cba7341ae76585345c43d8f0e624882b622218"}, + {file = "aiohttp-3.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f929f4c9b9a00f3e6cc0587abb95ab9c05681f8b14e0fe1daecfa83ea90f8318"}, + {file = "aiohttp-3.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28185e36a78d247c55e9fbea2332d16aefa14c5276a582ce7a896231c6b1c208"}, + {file = "aiohttp-3.9.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a486ddf57ab98b6d19ad36458b9f09e6022de0381674fe00228ca7b741aacb2f"}, + {file = "aiohttp-3.9.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70e851f596c00f40a2f00a46126c95c2e04e146015af05a9da3e4867cfc55911"}, + {file = "aiohttp-3.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5b7bf8fe4d39886adc34311a233a2e01bc10eb4e842220235ed1de57541a896"}, + {file = "aiohttp-3.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c67a51ea415192c2e53e4e048c78bab82d21955b4281d297f517707dc836bf3d"}, + {file = "aiohttp-3.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:694df243f394629bcae2d8ed94c589a181e8ba8604159e6e45e7b22e58291113"}, + {file = "aiohttp-3.9.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3dd8119752dd30dd7bca7d4bc2a92a59be6a003e4e5c2cf7e248b89751b8f4b7"}, + {file = "aiohttp-3.9.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:eb6dfd52063186ac97b4caa25764cdbcdb4b10d97f5c5f66b0fa95052e744eb7"}, + {file = "aiohttp-3.9.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:d97c3e286d0ac9af6223bc132dc4bad6540b37c8d6c0a15fe1e70fb34f9ec411"}, + {file = "aiohttp-3.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:816f4db40555026e4cdda604a1088577c1fb957d02f3f1292e0221353403f192"}, + {file = "aiohttp-3.9.0-cp311-cp311-win32.whl", hash = "sha256:3abf0551874fecf95f93b58f25ef4fc9a250669a2257753f38f8f592db85ddea"}, + {file = "aiohttp-3.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:e18d92c3e9e22553a73e33784fcb0ed484c9874e9a3e96c16a8d6a1e74a0217b"}, + {file = "aiohttp-3.9.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:99ae01fb13a618b9942376df77a1f50c20a281390dad3c56a6ec2942e266220d"}, + {file = "aiohttp-3.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:05857848da443c8c12110d99285d499b4e84d59918a21132e45c3f0804876994"}, + {file = "aiohttp-3.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:317719d7f824eba55857fe0729363af58e27c066c731bc62cd97bc9c3d9c7ea4"}, + {file = "aiohttp-3.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1e3b3c107ccb0e537f309f719994a55621acd2c8fdf6d5ce5152aed788fb940"}, + {file = "aiohttp-3.9.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45820ddbb276113ead8d4907a7802adb77548087ff5465d5c554f9aa3928ae7d"}, + {file = "aiohttp-3.9.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:05a183f1978802588711aed0dea31e697d760ce9055292db9dc1604daa9a8ded"}, + {file = "aiohttp-3.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a4cd44788ea0b5e6bb8fa704597af3a30be75503a7ed1098bc5b8ffdf6c982"}, + {file = "aiohttp-3.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:673343fbc0c1ac44d0d2640addc56e97a052504beacd7ade0dc5e76d3a4c16e8"}, + {file = "aiohttp-3.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7e8a3b79b6d186a9c99761fd4a5e8dd575a48d96021f220ac5b5fa856e5dd029"}, + {file = "aiohttp-3.9.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6777a390e41e78e7c45dab43a4a0196c55c3b8c30eebe017b152939372a83253"}, + {file = "aiohttp-3.9.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7ae5f99a32c53731c93ac3075abd3e1e5cfbe72fc3eaac4c27c9dd64ba3b19fe"}, + {file = "aiohttp-3.9.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:f1e4f254e9c35d8965d377e065c4a8a55d396fe87c8e7e8429bcfdeeb229bfb3"}, + {file = "aiohttp-3.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:11ca808f9a6b63485059f5f6e164ef7ec826483c1212a44f268b3653c91237d8"}, + {file = "aiohttp-3.9.0-cp312-cp312-win32.whl", hash = "sha256:de3cc86f4ea8b4c34a6e43a7306c40c1275e52bfa9748d869c6b7d54aa6dad80"}, + {file = "aiohttp-3.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:ca4fddf84ac7d8a7d0866664936f93318ff01ee33e32381a115b19fb5a4d1202"}, + {file = "aiohttp-3.9.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f09960b5bb1017d16c0f9e9f7fc42160a5a49fa1e87a175fd4a2b1a1833ea0af"}, + {file = "aiohttp-3.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8303531e2c17b1a494ffaeba48f2da655fe932c4e9a2626c8718403c83e5dd2b"}, + {file = "aiohttp-3.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4790e44f46a4aa07b64504089def5744d3b6780468c4ec3a1a36eb7f2cae9814"}, + {file = "aiohttp-3.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1d7edf74a36de0e5ca50787e83a77cf352f5504eb0ffa3f07000a911ba353fb"}, + {file = "aiohttp-3.9.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94697c7293199c2a2551e3e3e18438b4cba293e79c6bc2319f5fd652fccb7456"}, + {file = "aiohttp-3.9.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a1b66dbb8a7d5f50e9e2ea3804b01e766308331d0cac76eb30c563ac89c95985"}, + {file = "aiohttp-3.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9623cfd9e85b76b83ef88519d98326d4731f8d71869867e47a0b979ffec61c73"}, + {file = "aiohttp-3.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f32c86dc967ab8c719fd229ce71917caad13cc1e8356ee997bf02c5b368799bf"}, + {file = "aiohttp-3.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f50b4663c3e0262c3a361faf440761fbef60ccdde5fe8545689a4b3a3c149fb4"}, + {file = "aiohttp-3.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:dcf71c55ec853826cd70eadb2b6ac62ec577416442ca1e0a97ad875a1b3a0305"}, + {file = "aiohttp-3.9.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:42fe4fd9f0dfcc7be4248c162d8056f1d51a04c60e53366b0098d1267c4c9da8"}, + {file = "aiohttp-3.9.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76a86a9989ebf82ee61e06e2bab408aec4ea367dc6da35145c3352b60a112d11"}, + {file = "aiohttp-3.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f9e09a1c83521d770d170b3801eea19b89f41ccaa61d53026ed111cb6f088887"}, + {file = "aiohttp-3.9.0-cp38-cp38-win32.whl", hash = "sha256:a00ce44c21612d185c5275c5cba4bab8d7c1590f248638b667ed8a782fa8cd6f"}, + {file = "aiohttp-3.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:d5b9345ab92ebe6003ae11d8092ce822a0242146e6fa270889b9ba965457ca40"}, + {file = "aiohttp-3.9.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:98d21092bf2637c5fa724a428a69e8f5955f2182bff61f8036827cf6ce1157bf"}, + {file = "aiohttp-3.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:35a68cd63ca6aaef5707888f17a70c36efe62b099a4e853d33dc2e9872125be8"}, + {file = "aiohttp-3.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d7f6235c7475658acfc1769d968e07ab585c79f6ca438ddfecaa9a08006aee2"}, + {file = "aiohttp-3.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db04d1de548f7a62d1dd7e7cdf7c22893ee168e22701895067a28a8ed51b3735"}, + {file = "aiohttp-3.9.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:536b01513d67d10baf6f71c72decdf492fb7433c5f2f133e9a9087379d4b6f31"}, + {file = "aiohttp-3.9.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c8b0a6487e8109427ccf638580865b54e2e3db4a6e0e11c02639231b41fc0f"}, + {file = "aiohttp-3.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7276fe0017664414fdc3618fca411630405f1aaf0cc3be69def650eb50441787"}, + {file = "aiohttp-3.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23170247ef89ffa842a02bbfdc425028574d9e010611659abeb24d890bc53bb8"}, + {file = "aiohttp-3.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b1a2ea8252cacc7fd51df5a56d7a2bb1986ed39be9397b51a08015727dfb69bd"}, + {file = "aiohttp-3.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2d71abc15ff7047412ef26bf812dfc8d0d1020d664617f4913df2df469f26b76"}, + {file = "aiohttp-3.9.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:2d820162c8c2bdbe97d328cd4f417c955ca370027dce593345e437b2e9ffdc4d"}, + {file = "aiohttp-3.9.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:2779f5e7c70f7b421915fd47db332c81de365678180a9f3ab404088f87ba5ff9"}, + {file = "aiohttp-3.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:366bc870d7ac61726f32a489fbe3d1d8876e87506870be66b01aeb84389e967e"}, + {file = "aiohttp-3.9.0-cp39-cp39-win32.whl", hash = "sha256:1df43596b826022b14998f0460926ce261544fedefe0d2f653e1b20f49e96454"}, + {file = "aiohttp-3.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:9c196b30f1b1aa3363a69dd69079ae9bec96c2965c4707eaa6914ba099fb7d4f"}, + {file = "aiohttp-3.9.0.tar.gz", hash = "sha256:09f23292d29135025e19e8ff4f0a68df078fe4ee013bca0105b2e803989de92d"}, ] [package.dependencies] aiosignal = ">=1.1.2" -async-timeout = ">=4.0.0a3,<5.0" attrs = ">=17.3.0" -charset-normalizer = ">=2.0,<4.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" yarl = ">=1.0,<2.0" [package.extras] -speedups = ["Brotli", "aiodns", "cchardet"] +speedups = ["Brotli", "aiodns", "brotlicffi"] [[package]] name = "aiohttp-pydantic" version = "1.12.2" description = "Aiohttp View using pydantic to validate request body and query sting regarding method annotations." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -143,25 +127,22 @@ test = ["pytest (==6.1.2)", "pytest-aiohttp (==0.3.0)", "pytest-cov (==2.10.1)", [[package]] name = "aiomqtt" -version = "1.2.0" +version = "1.2.1" description = "The idiomatic asyncio MQTT client, wrapped around paho-mqtt" -category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "aiomqtt-1.2.0-py3-none-any.whl", hash = "sha256:756879c415c3c89a380bec2b283c165d9e7cc581d490cf29f7e85c28ff0494c5"}, - {file = "aiomqtt-1.2.0.tar.gz", hash = "sha256:aad3da59aa77e12e28144a2fdccd60c840d9c2709fc287894696935974270ca2"}, + {file = "aiomqtt-1.2.1-py3-none-any.whl", hash = "sha256:3925b40b2b95b1905753d53ef3a9162e903cfab35ebe9647ab4d52e45ffb727f"}, + {file = "aiomqtt-1.2.1.tar.gz", hash = "sha256:7582f4341f08ef7110dd9ab3a559454dc28ccda1eac502ff8f08a73b238ecede"}, ] [package.dependencies] paho-mqtt = ">=1.6.0,<2.0.0" -typing-extensions = {version = ">=4.4.0,<5.0.0", markers = "python_version < \"3.10\""} [[package]] name = "aiosignal" version = "1.3.1" description = "aiosignal: a list of registered asynchronous callbacks" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -176,7 +157,6 @@ frozenlist = ">=1.1.0" name = "aiozeroconf" version = "0.1.8" description = "Pure Python Multicast DNS Service Discovery Library for asyncio (Bonjour/Avahi compatible)" -category = "main" optional = false python-versions = "*" files = [ @@ -190,7 +170,6 @@ netifaces = "*" name = "aresponses" version = "2.1.6" description = "Asyncio response mocking. Similar to the responses library used for 'requests'" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -199,26 +178,13 @@ files = [ ] [package.dependencies] -aiohttp = ">=3.1.0,<4.0.0" +aiohttp = ">=3.1.0,<4.dev0" pytest-asyncio = "*" -[[package]] -name = "async-timeout" -version = "4.0.3" -description = "Timeout context manager for asyncio programs" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, - {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, -] - [[package]] name = "attrs" version = "23.1.0" description = "Classes Without Boilerplate" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -237,7 +203,6 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte name = "autopep8" version = "2.0.4" description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -247,130 +212,147 @@ files = [ [package.dependencies] pycodestyle = ">=2.10.0" -tomli = {version = "*", markers = "python_version < \"3.11\""} [[package]] name = "bitarray" -version = "2.8.1" +version = "2.8.3" description = "efficient arrays of booleans -- C extension" -category = "main" optional = false python-versions = "*" files = [ - {file = "bitarray-2.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6be965028785413a6163dd55a639b898b22f67f9b6ed554081c23e94a602031e"}, - {file = "bitarray-2.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29e19cb80a69f6d1a64097bfbe1766c418e1a785d901b583ef0328ea10a30399"}, - {file = "bitarray-2.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0f6d705860f59721d7282496a4d29b5fd78690e1c1473503832c983e762b01b"}, - {file = "bitarray-2.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6df04efdba4e1bf9d93a1735e42005f8fcf812caf40c03934d9322412d563499"}, - {file = "bitarray-2.8.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:18530ed3ddd71e9ff95440afce531efc3df7a3e0657f1c201c2c3cb41dd65869"}, - {file = "bitarray-2.8.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4cd81ffd2d58ef68c22c825aff89f4a47bd721e2ada0a3a96793169f370ae21"}, - {file = "bitarray-2.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8367768ab797105eb97dfbd4577fcde281618de4d8d3b16ad62c477bb065f347"}, - {file = "bitarray-2.8.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:848af80518d0ed2aee782018588c7c88805f51b01271935df5b256c8d81c726e"}, - {file = "bitarray-2.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c54b0af16be45de534af9d77e8a180126cd059f72db8b6550f62dda233868942"}, - {file = "bitarray-2.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f30cdce22af3dc7c73e70af391bfd87c4574cc40c74d651919e20efc26e014b5"}, - {file = "bitarray-2.8.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:bc03bb358ae3917247d257207c79162e666d407ac473718d1b95316dac94162b"}, - {file = "bitarray-2.8.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:cf38871ed4cd89df9db7c70f729b948fa3e2848a07c69f78e4ddfbe4f23db63c"}, - {file = "bitarray-2.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4a637bcd199c1366c65b98f18884f0d0b87403f04676b21e4635831660d722a7"}, - {file = "bitarray-2.8.1-cp310-cp310-win32.whl", hash = "sha256:904719fb7304d4115228b63c178f0cc725ad3b73e285c4b328e45a99a8e3fad6"}, - {file = "bitarray-2.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:1e859c664500d57526fe07140889a3b58dca54ff3b16ac6dc6d534a65c933084"}, - {file = "bitarray-2.8.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2d3f28a80f2e6bb96e9360a4baf3fbacb696b5aba06a14c18a15488d4b6f398f"}, - {file = "bitarray-2.8.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4677477a406f2a9e064920463f69172b865e4d69117e1f2160064d3f5912b0bd"}, - {file = "bitarray-2.8.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9061c0a50216f24c97fb2325de84200e5ad5555f25c854ddcb3ceb6f12136055"}, - {file = "bitarray-2.8.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:843af12991161b358b6379a8dc5f6636798f3dacdae182d30995b6a2df3b263e"}, - {file = "bitarray-2.8.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9336300fd0acf07ede92e424930176dc4b43ef1b298489e93ba9a1695e8ea752"}, - {file = "bitarray-2.8.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0af01e1f61fe627f63648c0c6f52de8eac56710a2ef1dbce4851d867084cc7e"}, - {file = "bitarray-2.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ab81c74a1805fe74330859b38e70d7525cdd80953461b59c06660046afaffcf"}, - {file = "bitarray-2.8.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2015a9dd718393e814ff7b9e80c58190eb1cef7980f86a97a33e8440e158ce2"}, - {file = "bitarray-2.8.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b0493ab66c6b8e17e9fde74c646b39ee09c236cf28a787cb8cbd3a83c05bff7"}, - {file = "bitarray-2.8.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:81e83ed7e0b1c09c5a33b97712da89e7a21fd3e5598eff3975c39540f5619792"}, - {file = "bitarray-2.8.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:741c3a2c0997c8f8878edfc65a4a8f7aa72eede337c9bc0b7bd8a45cf6e70dbc"}, - {file = "bitarray-2.8.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:57aeab27120a8a50917845bb81b0976e33d4759f2156b01359e2b43d445f5127"}, - {file = "bitarray-2.8.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:17c32ba584e8fb9322419390e0e248769ed7d59de3ffa7432562a4c0ec4f1f82"}, - {file = "bitarray-2.8.1-cp311-cp311-win32.whl", hash = "sha256:b67733a240a96f09b7597af97ac4d60c59140cfcfd180f11a7221863b82f023a"}, - {file = "bitarray-2.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:7b29d4bf3d3da1847f2be9e30105bf51caaf5922e94dc827653e250ed33f4e8a"}, - {file = "bitarray-2.8.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:5f6175c1cf07dadad3213d60075704cf2e2f1232975cfd4ac8328c24a05e8f78"}, - {file = "bitarray-2.8.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cc066c7290151600b8872865708d2d00fb785c5db8a0df20d70d518e02f172b"}, - {file = "bitarray-2.8.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ce2ef9291a193a0e0cd5e23970bf3b682cc8b95220561d05b775b8d616d665f"}, - {file = "bitarray-2.8.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c5582dd7d906e6f9ec1704f99d56d812f7d395d28c02262bc8b50834d51250c3"}, - {file = "bitarray-2.8.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2aa2267eb6d2b88ef7d139e79a6daaa84cd54d241b9797478f10dcb95a9cd620"}, - {file = "bitarray-2.8.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a04d4851e83730f03c4a6aac568c7d8b42f78f0f9cc8231d6db66192b030ce1e"}, - {file = "bitarray-2.8.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:f7d2ec2174d503cbb092f8353527842633c530b4e03b9922411640ac9c018a19"}, - {file = "bitarray-2.8.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:b65a04b2e029b0694b52d60786732afd15b1ec6517de61a36afbb7808a2ffac1"}, - {file = "bitarray-2.8.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:55020d6fb9b72bd3606969f5431386c592ed3666133bd475af945aa0fa9e84ec"}, - {file = "bitarray-2.8.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:797de3465f5f6c6be9a412b4e99eb6e8cdb86b83b6756655c4d83a65d0b9a376"}, - {file = "bitarray-2.8.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:f9a66745682e175e143a180524a63e692acb2b8c86941073f6dd4ee906e69608"}, - {file = "bitarray-2.8.1-cp36-cp36m-win32.whl", hash = "sha256:443726af4bd60515e4e41ea36c5dbadb29a59bc799bcbf431011d1c6fd4363e3"}, - {file = "bitarray-2.8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:2b0f754a5791635b8239abdcc0258378111b8ee7a8eb3e2bbc24bcc48a0f0b08"}, - {file = "bitarray-2.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d175e16419a52d54c0ac44c93309ba76dc2cfd33ee9d20624f1a5eb86b8e162e"}, - {file = "bitarray-2.8.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3128234bde3629ab301a501950587e847d30031a9cbf04d95f35cbf44469a9e"}, - {file = "bitarray-2.8.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75104c3076676708c1ac2484ebf5c26464fb3850312de33a5b5bf61bfa7dbec5"}, - {file = "bitarray-2.8.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82bfb6ab9b1b5451a5483c9a2ae2a8f83799d7503b384b54f6ab56ea74abb305"}, - {file = "bitarray-2.8.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dc064a63445366f6b26eaf77230d326b9463e903ba59d6ff5efde0c5ec1ea0e"}, - {file = "bitarray-2.8.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cbe54685cf6b17b3e15faf6c4b76773bc1c484bc447020737d2550a9dde5f6e6"}, - {file = "bitarray-2.8.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9fed8aba8d1b09cf641b50f1e6dd079c31677106ea4b63ec29f4c49adfabd63f"}, - {file = "bitarray-2.8.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7c17dd8fb146c2c680bf1cb28b358f9e52a14076e44141c5442148863ee95d7d"}, - {file = "bitarray-2.8.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:c9efcee311d9ba0c619743060585af9a9b81496e97b945843d5e954c67722a75"}, - {file = "bitarray-2.8.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dc7acffee09822b334d1b46cd384e969804abdf18f892c82c05c2328066cd2ae"}, - {file = "bitarray-2.8.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ea71e0a50060f96ad0821e0ac785e91e44807f8b69555970979d81934961d5bd"}, - {file = "bitarray-2.8.1-cp37-cp37m-win32.whl", hash = "sha256:69ab51d551d50e4d6ca35abc95c9d04b33ad28418019bb5481ab09bdbc0df15c"}, - {file = "bitarray-2.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:3024ab4c4906c3681408ca17c35833237d18813ebb9f24ae9f9e3157a4a66939"}, - {file = "bitarray-2.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:46fdd27c8fa4186d8b290bf74a28cbd91b94127b1b6a35c265a002e394fa9324"}, - {file = "bitarray-2.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d32ccd2c0d906eae103ef84015f0545a395052b0b6eb0e02e9023ca0132557f6"}, - {file = "bitarray-2.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9186cf8135ca170cd907d8c4df408a87747570d192d89ec4ff23805611c702a0"}, - {file = "bitarray-2.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8d6e5ff385fea25caf26fd58b43f087deb763dcaddd18d3df2895235cf1b484"}, - {file = "bitarray-2.8.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d6a9c72354327c7aa9890ff87904cbe86830cb1fb58c39750a0afac8df5e051"}, - {file = "bitarray-2.8.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2f13b7d0694ce2024c82fc595e6ccc3918e7f069747c3de41b1ce72a9a1e346"}, - {file = "bitarray-2.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d38ceca90ed538706e3f111513073590f723f90659a7af0b992b29776a6e816"}, - {file = "bitarray-2.8.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b977c39e3734e73540a2e3a71501c2c6261c70c6ce59d427bb7c4ecf6331c7e"}, - {file = "bitarray-2.8.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:214c05a7642040f6174e29f3e099549d3c40ac44616405081bf230dcafb38767"}, - {file = "bitarray-2.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ad440c17ef2ff42e94286186b5bcf82bf87c4026f91822675239102ebe1f7035"}, - {file = "bitarray-2.8.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:28dee92edd0d21655e56e1870c22468d0dabe557df18aa69f6d06b1543614180"}, - {file = "bitarray-2.8.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:df9d8a9a46c46950f306394705512553c552b633f8bf3c11359c4204289f11e3"}, - {file = "bitarray-2.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1a0d27aad02d8abcb1d3b7d85f463877c4937e71adf9b6adb9367f2cdad91a52"}, - {file = "bitarray-2.8.1-cp38-cp38-win32.whl", hash = "sha256:6033303431a7c85a535b3f1b0ec28abc2ebc2167c263f244993b56ccb87cae6b"}, - {file = "bitarray-2.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:9b65d487451e0e287565c8436cf4da45260f958f911299f6122a20d7ec76525c"}, - {file = "bitarray-2.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9aad7b4670f090734b272c072c9db375c63bd503512be9a9393e657dcacfc7e2"}, - {file = "bitarray-2.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bf80804014e3736515b84044c2be0e70080616b4ceddd4e38d85f3167aeb8165"}, - {file = "bitarray-2.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7f7231ef349e8f4955d9b39561f4683a418a73443cfce797a4eddbee1ba9664"}, - {file = "bitarray-2.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67e8fb18df51e649adbc81359e1db0f202d72708fba61b06f5ac8db47c08d107"}, - {file = "bitarray-2.8.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d5df3d6358425c9dfb6bdbd4f576563ec4173d24693a9042d05aadcb23c0b98"}, - {file = "bitarray-2.8.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6ea51ba4204d086d5b76e84c31d2acbb355ed1b075ded54eb9b7070b0b95415d"}, - {file = "bitarray-2.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1414582b3b7516d2282433f0914dd9846389b051b2aea592ae7cc165806c24ac"}, - {file = "bitarray-2.8.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5934e3a623a1d485e1dcfc1990246e3c32c6fc6e7f0fd894750800d35fdb5794"}, - {file = "bitarray-2.8.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:aa08a9b03888c768b9b2383949a942804d50d8164683b39fe62f0bfbfd9b4204"}, - {file = "bitarray-2.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:00ff372dfaced7dd6cc2dffd052fafc118053cf81a442992b9a23367479d77d7"}, - {file = "bitarray-2.8.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:dd76bbf5a4b2ab84b8ffa229f5648e80038ba76bf8d7acc5de9dd06031b38117"}, - {file = "bitarray-2.8.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e88a706f92ad1e0e1e66f6811d10b6155d5f18f0de9356ee899a7966a4e41992"}, - {file = "bitarray-2.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b2560475c5a1ff96fcab01fae7cf6b9a6da590f02659556b7fccc7991e401884"}, - {file = "bitarray-2.8.1-cp39-cp39-win32.whl", hash = "sha256:74cd1725d08325b6669e6e9a5d09cec29e7c41f7d58e082286af5387414d046d"}, - {file = "bitarray-2.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:e48c45ea7944225bcee026c457a70eaea61db3659d9603f07fc8a643ab7e633b"}, - {file = "bitarray-2.8.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c2426dc7a0d92d8254def20ab7a231626397ce5b6fb3d4f44be74cc1370a60c3"}, - {file = "bitarray-2.8.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d34790a919f165b6f537935280ef5224957d9ce8ab11d339f5e6d0319a683ccc"}, - {file = "bitarray-2.8.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c26a923080bc211cab8f5a5e242e3657b32951fec8980db0616e9239aade482"}, - {file = "bitarray-2.8.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0de1bc5f971aba46de88a4eb0dbb5779e30bbd7514f4dcbff743c209e0c02667"}, - {file = "bitarray-2.8.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3bb5f2954dd897b0bac13b5449e5c977534595b688120c8af054657a08b01f46"}, - {file = "bitarray-2.8.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:62ac31059a3c510ef64ed93d930581b262fd4592e6d95ede79fca91e8d3d3ef6"}, - {file = "bitarray-2.8.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae32ac7217e83646b9f64d7090bf7b737afaa569665621f110a05d9738ca841a"}, - {file = "bitarray-2.8.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3994f7dc48d21af40c0d69fca57d8040b02953f4c7c3652c2341d8947e9cbedf"}, - {file = "bitarray-2.8.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c361201e1c3ee6d6b2266f8b7a645389880bccab1b29e22e7a6b7b6e7831ad5"}, - {file = "bitarray-2.8.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:861850d6a58e7b6a7096d0b0efed9c6d993a6ab8b9d01e781df1f4d80cc00efa"}, - {file = "bitarray-2.8.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ee772c20dcb56b03d666a4e4383d0b5b942b0ccc27815e42fe0737b34cba2082"}, - {file = "bitarray-2.8.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63fa75e87ad8c57d5722cc87902ca148ef8bbbba12b5c5b3c3730a1bc9ac2886"}, - {file = "bitarray-2.8.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b999fb66980f885961d197d97d7ff5a13b7ab524ccf45ccb4704f4b82ce02e3"}, - {file = "bitarray-2.8.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3243e4b8279ff2fe4c6e7869f0e6930c17799ee9f8d07317f68d44a66b46281e"}, - {file = "bitarray-2.8.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:542358b178b025dcc95e7fb83389e9954f701c41d312cbb66bdd763cbe5414b5"}, - {file = "bitarray-2.8.1.tar.gz", hash = "sha256:e68ceef35a88625d16169550768fcc8d3894913e363c24ecbf6b8c07eb02c8f3"}, + {file = "bitarray-2.8.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:be7c6343a7f24293a988e5a27c1e2f44f028476e35192e73663c4acec5c4766e"}, + {file = "bitarray-2.8.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:38233e5793e107575be656908419d2bceab359c78c28affc386c7b88b8882b8f"}, + {file = "bitarray-2.8.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:acf24bc6aedd0a490af71591b99401867d4445d64db09a7bfe0bde3e8498cc8d"}, + {file = "bitarray-2.8.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04fcb292637012a1551e55c00796e31b5c66d1692ca25a5ac83d23779c23cd29"}, + {file = "bitarray-2.8.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:015908355354d42973ad41ba4eca697b4b55690b3ece6d9629118273e7a9e380"}, + {file = "bitarray-2.8.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48a89c2112420ebeb163a3c273c244d542cf9315c9ce5a875d305f91adcdac24"}, + {file = "bitarray-2.8.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb530a9fb7ed13a1a49bda81db2def4c73b7fef0fd1bb969b1d7605121869230"}, + {file = "bitarray-2.8.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c87146e9c2c196c012e97273f82215e2239b9bffcbb6c7802bbbedac87be2358"}, + {file = "bitarray-2.8.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:84a2628a5377971d73c95014e540a51327eb27ffdfbab81e43eac494eced3dc2"}, + {file = "bitarray-2.8.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6bcbe2ea34c88cf736f157cf3d713c1af112f0d7a9eec390d69a9e042b7d76d4"}, + {file = "bitarray-2.8.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:67ee9d71af3db621aa637f96520a8df8534fcc64e881360d3ed3a07f7e47ed1b"}, + {file = "bitarray-2.8.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ba3f27d82b45543a7d1488d151594915a6e67fb28bd4f21eb0901df2ba4ede86"}, + {file = "bitarray-2.8.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:095923f084d2271f28d7430798e698f6d0b304c58b072b4f2eb0bc132321323b"}, + {file = "bitarray-2.8.3-cp310-cp310-win32.whl", hash = "sha256:de91007504b475a93d8b0949db9dec86d39c0306de9914f7b9087daeb3d9fbaf"}, + {file = "bitarray-2.8.3-cp310-cp310-win_amd64.whl", hash = "sha256:09c140daa13d2515609d5a2dbfd289eada200e96222671194dc72eae89bc3c7b"}, + {file = "bitarray-2.8.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2bfd32ce49d23584333087262fb367b371c74cf531f6b0c16759d59f47c847d7"}, + {file = "bitarray-2.8.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:12035756896d71e82edf6a6fb46d3ca299eadbec25140c12505d4b32f561b0da"}, + {file = "bitarray-2.8.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:73fa449d9e551a063ff5c68b5d2cc0caaede5b59366d37457261ae3080f61fca"}, + {file = "bitarray-2.8.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18707458f6467072a9c3322835a299fa86df8fb3962f51afac2b50c6a4babf82"}, + {file = "bitarray-2.8.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f142476b3bb80f6887b5a3a08d69bbd526093aee5a00973c26458cc16dd5e47"}, + {file = "bitarray-2.8.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:47400fa421b8a3947f6676981f8d9b8581239831533dff374477ef2b86fda42f"}, + {file = "bitarray-2.8.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56f51107bb5406bfa4889064c01d5f9e7a545b3e2b53f159626c72c910fe8f07"}, + {file = "bitarray-2.8.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a3741359cbb1a9eb50188e8faa0ced96ca658eb85061786b7f686efa94c3604"}, + {file = "bitarray-2.8.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c65080bbba08ce07b136490b4df3d0907ec3dd76c3c5d47fda011002420f6d31"}, + {file = "bitarray-2.8.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:117a6f409dabc15320f3212d05d878cc33436c1e118e8746bf3775da2509bb7d"}, + {file = "bitarray-2.8.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:782ff781ae3c4956c15764aefc06ceb8c1c348794f09dfc8ebf62ff35166da1f"}, + {file = "bitarray-2.8.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:a7b839e5c038111fd2fbd09e83ca945da357d690e49cfa269c09aed239db9c2b"}, + {file = "bitarray-2.8.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ab7e9b1846cc62739d9d293a94f704949b588afb9ed72db00e26b7fcdb4661a3"}, + {file = "bitarray-2.8.3-cp311-cp311-win32.whl", hash = "sha256:20cc6573ac21627e0fde854d4e0450d4c97706213bac986c0d38d252452da155"}, + {file = "bitarray-2.8.3-cp311-cp311-win_amd64.whl", hash = "sha256:8011a63692e9e32cdc3fac3dfd0beceece926e8b53fb91750037fc386917f90b"}, + {file = "bitarray-2.8.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da61c6d7b6288d29db5be77048176f41f7320316997fced28b5415e1f939448e"}, + {file = "bitarray-2.8.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:60774f73151dbcabefb5acb6d97ac09a51c999f9a903ac6f8db3d8368d338969"}, + {file = "bitarray-2.8.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c815a7ca72a5eebcd85caaeb4d32b71af1c795e38b3dff5dcb5b6b1f3ba0b4f"}, + {file = "bitarray-2.8.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a102cd1fafee8919a069fed9ea40c1ffe4d6037fd5b0a7f47326c2f75f24f70f"}, + {file = "bitarray-2.8.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b2816afe82feeb7948e58ca0be31c254e23307953e56d3313f293f79279fbe7"}, + {file = "bitarray-2.8.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98fe712a82f65de536b65fa9af7601df4e8231f14e3b0b14ef22e16e30d2fbea"}, + {file = "bitarray-2.8.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8defbf10a731b44892001daa6903b2f2f7ad8c623a7b4d9ae6bd674592b1763e"}, + {file = "bitarray-2.8.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e98a7b510aaaf0d7368b7cb983d3106aecd28abdfa4b4593b80e7f4ab5af0a97"}, + {file = "bitarray-2.8.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a5e24317b0768789c52586a31284dec8ccafa2f6c128df2f2d79656142f1e794"}, + {file = "bitarray-2.8.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:c30dbbe2f49056d4bd97a94c07a7fc0118ecc85661fdbaada36dfa9b14dc5962"}, + {file = "bitarray-2.8.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:2adb2ba1e7196f62587f4011b213b3609a717f92698a398904192e201ec3e29e"}, + {file = "bitarray-2.8.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:3aa1bd71236e07f0e7ab859a130fc57645301fd1ffd64be9a9750bce51446acb"}, + {file = "bitarray-2.8.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:63e595ca8dab2b77104e618782764bc3b172a0e9c6f97734d5fdd299063feac0"}, + {file = "bitarray-2.8.3-cp312-cp312-win32.whl", hash = "sha256:0c3de6517df7bbac18632046e722ca9000a4aeb76da68e545437fee1e61e2bbc"}, + {file = "bitarray-2.8.3-cp312-cp312-win_amd64.whl", hash = "sha256:4a6a4e83ecab1fd1fc171c57334663b24c5d286b66421efac2428b7e105c5d62"}, + {file = "bitarray-2.8.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:993438edd54350133f7569a8691074a90aa2297def69ec0e7af34de3d175cd00"}, + {file = "bitarray-2.8.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06770f6f7d238c2e2d251e9f5346358653ea8f3dbbedc83d18598f6c044f16b4"}, + {file = "bitarray-2.8.3-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44e3944ebccbc38ebdb7bd3c37a9b6ff91d87db2dad4bf3910e2b01fbd36831b"}, + {file = "bitarray-2.8.3-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a86c308018b59b999cf3d5a16889d3a347b48a2d08f34fbb4e29d5dc05fa198a"}, + {file = "bitarray-2.8.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b92c17b15bd5536c3e067051c67531adc81fcb6c1a699a760600ccd03dfcfba"}, + {file = "bitarray-2.8.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3d80bc6722652c847e5f503c2ce94a641b016059ec45bde4e1f13454b33e904"}, + {file = "bitarray-2.8.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fbc7ac38de41052599f1e27edf4f33c02d5aea6810ee299825a81863a32e26a0"}, + {file = "bitarray-2.8.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:bbca4c4bc9854e3166474e471f3230989fd2baf32c915e363c32f91dc6ebb704"}, + {file = "bitarray-2.8.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:74efd69ac9d06ce9f43a1f513cee8a82c314f85aa0bd74664abe9e608fb59ffd"}, + {file = "bitarray-2.8.3-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:c3f7a6c6b78edd81fca0035fb7a156a79f25919e1b0598afb483c26513d562f1"}, + {file = "bitarray-2.8.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b0cefac8fedb3dbbf97542dc0c6fdd8bf09a210bf6fa5799083b7309fd97b1b2"}, + {file = "bitarray-2.8.3-cp36-cp36m-win32.whl", hash = "sha256:67e366efaea6e0b5971593a83d062cb7e4e09e03d29f8d5b825effdf5f516ad3"}, + {file = "bitarray-2.8.3-cp36-cp36m-win_amd64.whl", hash = "sha256:621d5658b890b99b3f8b1a678b0afed10e096d53baa767ecbcf428fce1f48415"}, + {file = "bitarray-2.8.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ac5451951ce1e0616385e77de49afc7bd90bdf9d0aa99c0fd7b0bd23400db890"}, + {file = "bitarray-2.8.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff6b6b47da38223803aa3e7aab356f84e0636ecdbd43fa4bd11dbc00a923d474"}, + {file = "bitarray-2.8.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:154082c814e4007bf15d8dfc576ebd4e79e9ed3626017cd53810961cee7e65d8"}, + {file = "bitarray-2.8.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9f4f29c0338e5862ebc3b88091d29ff28d44ab80381f238da08aabb054777c2"}, + {file = "bitarray-2.8.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b153b846a6ac4b6eca71bb5f84d3dba51f3cd159f4322f5d67b2c41cf15973ad"}, + {file = "bitarray-2.8.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a2c8e06c3463746181255e03f07535c136f5346fb9c4a90eec2da27695102533"}, + {file = "bitarray-2.8.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f16a2247c27f4db3f8d01665ee97d46eaf0240b7a9feae16c17e906a3bb9a794"}, + {file = "bitarray-2.8.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:57f1fc3a089d9907859e940c6a4db3f5358013c75bba3b15156d93a58bca868e"}, + {file = "bitarray-2.8.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:c42fcddc955d84164667d899e8d4bbb763f4bc029fe72642a65df7382c46fe94"}, + {file = "bitarray-2.8.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:e60254ac626790c8c95415b095c6831056ca57a5d31839564210530c3278f170"}, + {file = "bitarray-2.8.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a0bb2e5c0c9f964bf43a09a1cf37233ff96b3318c9a50b1b7c3d74a875b32072"}, + {file = "bitarray-2.8.3-cp37-cp37m-win32.whl", hash = "sha256:edddd6d885c7195ba7734936bc1efc8a37de18ec886a8be44a484980da87947e"}, + {file = "bitarray-2.8.3-cp37-cp37m-win_amd64.whl", hash = "sha256:44ee266b71cd6bd7c99f937b30ac3b7627cad04777f2c12894cd0f820cb79ada"}, + {file = "bitarray-2.8.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a836a988ada812776af9ea6e88edf1e2eaaf38ebd545bbbcd500b2db0ced3a4f"}, + {file = "bitarray-2.8.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:089a4658706ec63293c153ffb1472cea1bbefb39ccfb214f52f0c1f5d10bf28e"}, + {file = "bitarray-2.8.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f8c492d90b41c510d799cc37c27892b149be77e225df6446854ce0b164e243a3"}, + {file = "bitarray-2.8.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b661052a4762825790a728469f897c341558392342cb68a6c54708d4e5198254"}, + {file = "bitarray-2.8.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4fd5e8a2e1b898ebc91faf6e1938bde38a4d20ee8ea49835e9adadd9b87c97c"}, + {file = "bitarray-2.8.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d4f3e78a8c1c5bf625632488a4bdd78fe87c4603ea10443cb8f207c2a846efe"}, + {file = "bitarray-2.8.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5797552e849079ff963936a037087367f20b41d5a612b07a1ba032259a2b86c8"}, + {file = "bitarray-2.8.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:adfc210df3d85017f5d2ef82db94d46b585ecbbd7357a6ee1c3bc125cc2658e2"}, + {file = "bitarray-2.8.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:252bdf94c74192b10f7fdb42683adf1403892acdce39e3e3524e8b070793b1c7"}, + {file = "bitarray-2.8.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:879bb9f11bad60a5588f5efb4e60f42844e4787ce7d5bb0f8eb8b87a835e914f"}, + {file = "bitarray-2.8.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:7a6413b5f53d44e134276d5a3747b71d17cbc25177a50445458921424a760dcd"}, + {file = "bitarray-2.8.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3d0daf70de198dcde459451c534333c0f59ab847649be013c9b88d24f0e49767"}, + {file = "bitarray-2.8.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:09244fa4e39ca263820dd8eca83a0175a98fb8f9bd353b4285a9ef2928b7fb41"}, + {file = "bitarray-2.8.3-cp38-cp38-win32.whl", hash = "sha256:7ad527ff1d398a703eba71ac270625087691e62efab8d0e331c53affe0628030"}, + {file = "bitarray-2.8.3-cp38-cp38-win_amd64.whl", hash = "sha256:2fcaf220e53518762dae0701082cb70d620656eaaecf5512695a6afafa885ea6"}, + {file = "bitarray-2.8.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e19756480bff2703155060d1849d37138a1d2242287563de112fb5bdd3217d"}, + {file = "bitarray-2.8.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:123333df4b22f12f4fc13fa4821b8ca075df59161bd41f5f189ffc791aaac10b"}, + {file = "bitarray-2.8.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ff62c1c174ceae7ef0456702f9eff1f3d76590c075b9c984c459d734f73fc766"}, + {file = "bitarray-2.8.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7554518934364b30d8da085f7a759ee3838c9ae4265b48beb82072f942b2816e"}, + {file = "bitarray-2.8.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8f0306dbc6605dd7f9e2dada33a3916c0c28f37128464de7153df7d8cf7a959"}, + {file = "bitarray-2.8.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2aeae0f2dacf546256f8720a1e8233b6735a3bf76778be701a1736d26fe4ecec"}, + {file = "bitarray-2.8.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c02d24051d7070b8f3b52fa9c8984fd8eb035115545f7c4be44c9825e8b58c8"}, + {file = "bitarray-2.8.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82fe0a774204159383d1be993191d51500cb44adbd3e9287da801e4657c0d4b2"}, + {file = "bitarray-2.8.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:aa4513a7393055faef630dcfb4d10a339c47eeb943487c0e9063ba763b66cb73"}, + {file = "bitarray-2.8.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:36f9752b654e18f99130a2bf84f54b1e6b8fad4f5f768f4390eb9b769a64a59c"}, + {file = "bitarray-2.8.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:a4212b66f9ae2e28ca1aa0307167ebfcdb2ca263a56b786cc572699e8a717f91"}, + {file = "bitarray-2.8.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cadccf651900e3858e55dfd762d5de0786aec853f1fb26183905ddee233183b4"}, + {file = "bitarray-2.8.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f756d159099f154a21d73932f13c8ce27f45a1c892d9b19c66a1a2c50c18474"}, + {file = "bitarray-2.8.3-cp39-cp39-win32.whl", hash = "sha256:c2ffed55994f5c73d34371474946767f936b0b83237f800be0f27a3e783baadb"}, + {file = "bitarray-2.8.3-cp39-cp39-win_amd64.whl", hash = "sha256:f69cacb3d983200114e48ec0c894e28690926f166b71202f75e976d5cd588be9"}, + {file = "bitarray-2.8.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d6a8a1da9205de97eea14aaa731c657fa8decd2d6878ee3d2d4bf33291960216"}, + {file = "bitarray-2.8.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8562dd32b4d9810a0b9c04fe3d1ed8078f27d74e3738063162c677b253216666"}, + {file = "bitarray-2.8.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed974048a4ced6e7b5d1cfcb83c046e70bf31b8a28eacfee3afa62f8690dee69"}, + {file = "bitarray-2.8.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2448d8f5ce6d8a840a5dff1b41f5124445141530724af7ba82ec7967eabd290a"}, + {file = "bitarray-2.8.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:64d867953b530b3dde93663d4c4708b533216e9dca3f3b4489698261cd80fcef"}, + {file = "bitarray-2.8.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:72bba6b388ba7c48a882bd58c86972aab73a30c3fb5b3341f28eb5bdc17365f8"}, + {file = "bitarray-2.8.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f62ee2eae65b72e034a24ac2bacd78d48845193168b54407e93bccd3772b247f"}, + {file = "bitarray-2.8.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07ed46857ed73765f2316e08f2d5108b7e694b44f4293e30fb526f3123c829d4"}, + {file = "bitarray-2.8.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:136bd205384a3089bc22c02a365a152e61b1e8d06ec664185c90e3ab8967260c"}, + {file = "bitarray-2.8.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:42d2d0123b1e68b387f4b2fd288e1a8f0dfb991cf1d2fbc56d948c3f4a113d8d"}, + {file = "bitarray-2.8.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5f35d5ff7334610b42632b30c27332b30db3680dd0174f86e382c3e150dfea2c"}, + {file = "bitarray-2.8.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7618abbac8999cd942be278130b88ac6ed364ba3446222f1db0faf4de7a052cf"}, + {file = "bitarray-2.8.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50923d862e01a546f942272193612f386ec1f90cc4528b10561854902bd8aab0"}, + {file = "bitarray-2.8.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c99838782dbec7f0c5cba1a6d4faa8e2da2b522423aa36a7f383a2265ac0ae3f"}, + {file = "bitarray-2.8.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e76735a285e834fc9db560de11e086453128c1177950a15c3404fe16c7d76f5e"}, + {file = "bitarray-2.8.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ffa74d8601e26570f1d0e3042fda6eb26b64ba8d8dfe9b96d0bf90a6f0d81582"}, + {file = "bitarray-2.8.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6993e46c81702d0bb39aad83ceb228cec087bc321782fbd2c6ddff7c653dcc8"}, + {file = "bitarray-2.8.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d9ec6a214563d2edd46d1a553583782379a2cb1016e8cc6c524e011905433b1"}, + {file = "bitarray-2.8.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34ceedbeed9aefde10c273d44801971db8f7505f80933fbb936969ee2343b8a3"}, + {file = "bitarray-2.8.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cc178297951343c8d8cd8a391999abf0024ca319671418f98dea0d7e71354126"}, + {file = "bitarray-2.8.3.tar.gz", hash = "sha256:e15587b2bdf18d32eb3ba25f5f5a51bedd0dc06b3112a4c53dab5e7753bc6588"}, ] [[package]] name = "bitstring" -version = "4.1.2" +version = "4.1.3" description = "Simple construction, analysis and modification of binary data." -category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "bitstring-4.1.2-py3-none-any.whl", hash = "sha256:bc0dbdb282099d5c6fbf995cd7261449ddeb7bbf042996e503471a35eb2efaa3"}, - {file = "bitstring-4.1.2.tar.gz", hash = "sha256:c22283d60fd3e1a8f386ccd4f1915d7fe13481d6349db39711421e24d4a9cccf"}, + {file = "bitstring-4.1.3-py3-none-any.whl", hash = "sha256:5619b9d6a4939717962b0fecb0034ad30277606b8b0bcce3f7cd6ac2400f6427"}, + {file = "bitstring-4.1.3.tar.gz", hash = "sha256:1b47c84644a961ba8503db2bba8a5965ab53e81474becdf0a18383b5b5f3f795"}, ] [package.dependencies] @@ -380,7 +362,6 @@ bitarray = ">=2.8.0,<3.0.0" name = "brewblox-service" version = "3.3.2" description = "Scaffolding for Brewblox backend services" -category = "main" optional = false python-versions = ">=3.9,<4" files = [ @@ -389,233 +370,142 @@ files = [ ] [package.dependencies] -aiohttp = ">=3.0.0,<4.0.0" -aiohttp-pydantic = ">=1.0.0,<2.0.0" -aiomqtt = ">=1.0.0,<2.0.0" -pydantic = ">=1.0.0,<2.0.0" +aiohttp = "==3.*" +aiohttp-pydantic = "==1.*" +aiomqtt = "==1.*" +pydantic = "==1.*" [[package]] name = "cffi" -version = "1.15.1" +version = "1.16.0" description = "Foreign Function Interface for Python calling C code." -category = "main" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, ] [package.dependencies] pycparser = "*" -[[package]] -name = "charset-normalizer" -version = "3.2.0" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, - {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, -] - [[package]] name = "ciso8601" -version = "2.3.0" +version = "2.3.1" description = "Fast ISO8601 date time parser for Python written in C" -category = "main" optional = false python-versions = "*" files = [ - {file = "ciso8601-2.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8f884d6a0b7384f8b1c57f740196988dd1229242c1be7c30a75424725590e0b3"}, - {file = "ciso8601-2.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:58517dfe06c30ad65fb1b4e9de66ccb72752d79bc71d7b7d26cbc0d008b7265a"}, - {file = "ciso8601-2.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c66032757d314ad232904f91a54df4907bd9af41b0d0b4acc19bfde1ab52983b"}, - {file = "ciso8601-2.3.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6cae7a74d9485a2f191adc5aad2563756af89cc1f3190e7d89f401b2349eb2b"}, - {file = "ciso8601-2.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:47cc66899e5facdccc28f183b978ace9edbebdea6545c013ec1d369fdea3de61"}, - {file = "ciso8601-2.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b4596c9d92719af4f06082c59182ce9de3a73e2bda67304498d9ac78264dd5c"}, - {file = "ciso8601-2.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:a002a8dc91e63730f7ca8eae0cb1e2832ee057fedf65e5b9bf416aefb1dd8cab"}, - {file = "ciso8601-2.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:87a6f58bdda833cb8d78c6482a179fff663903a8f562755e119bf815b1014f2e"}, - {file = "ciso8601-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7667faf021314315a3c498e4c7c8cf57a7014af0960ddd5b671bcf03b2d0132b"}, - {file = "ciso8601-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa90488666ee44796932850fc419cd55863b320f77b1474991e60f321b5ac7d2"}, - {file = "ciso8601-2.3.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1aba1f59b6d27ec694128f9ba85e22c1f17e67ffc5b1b0a991628bb402e25e81"}, - {file = "ciso8601-2.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:896dd46c7f2129140fc36dbe9ccf78cec02143b941b5a608e652cd40e39f6064"}, - {file = "ciso8601-2.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2cf6dfa22f21f838b730f977bc7ad057c37646f683bf42a727b4e763f44d47dc"}, - {file = "ciso8601-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:a8c4aa6880fd698075d5478615d4668e70af6424d90b1686c560c1ec3459926a"}, - {file = "ciso8601-2.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b12d314415ba1e4e4bfcfa3db782335949ca1866a2b6fe22c47099fed9c82826"}, - {file = "ciso8601-2.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d115fc2501a316256dd0b961b0b384a12998c626ab1e91cd06164f7792e3908"}, - {file = "ciso8601-2.3.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5817bd895c0d083c161ea38459de8e2b90d798de09769aaba003fe53c1418aba"}, - {file = "ciso8601-2.3.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:7d68741fe53cd0134e8e94109ede36d7aeaa65a36682680d53b69f790291d80f"}, - {file = "ciso8601-2.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:74c4b0fe3fd0ce1a0da941f3f50af1a81970d7e4536cbae43f27e041b4ae4d3e"}, - {file = "ciso8601-2.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0136d49f2265bf3d06ffb7bc649a64ed316e921ba6cd05e0fecc477c80fe5097"}, - {file = "ciso8601-2.3.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2188dd4784d87e4008cc765c80e26a503450c57a98655321de777679c556b133"}, - {file = "ciso8601-2.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4e0fa37c6d58be990c10d537ed286a35c018b5f038039ad796cf2352bc26799e"}, - {file = "ciso8601-2.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:fa1085b47c15df627d6bea783a8f7c89a59268af85e204992a013df174b339aa"}, - {file = "ciso8601-2.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:352809f24dc0fa7e05b85046f8bd34165a20fa5ebb5b43e053668fa69d57e657"}, - {file = "ciso8601-2.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7e8e78f8c7d35e6b43ad7316f652e2d53bf4b8798725d481abff14657852a88c"}, - {file = "ciso8601-2.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4cc04399f79a62338d4f4c19560d2b30f2d257021df1b0e55bae9209d8844c0c"}, - {file = "ciso8601-2.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e4affe0e72debf18c98d2f9e41c24a8ec8421ea65fafba96919f20a8d0f9bf87"}, - {file = "ciso8601-2.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d7d0f84fb0276c031bf606da484e9dc52ebdf121695732609dc49b30e8cf7c"}, - {file = "ciso8601-2.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8b1a217967083ac295d9239f5ba5235c66697fdadc2d5399c7bac53353218201"}, - {file = "ciso8601-2.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2785f374388e48c21420e820295d36a8d0734542e4e7bd3899467dc4d56016da"}, - {file = "ciso8601-2.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:59e6ac990dc31b14a39344a6a0f651658829bc59666cfff13c8deca37e360d86"}, - {file = "ciso8601-2.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3b135cda50be4ed52e44e815794cb19b268baf75d6c2a2a34eb6c2851bbe9423"}, - {file = "ciso8601-2.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b247b4a854119d438d28e0efd0258a5bb710be59ffeba3d2bea5bdab82f90ef3"}, - {file = "ciso8601-2.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:243ffcbee824ed74b21bd1cede72050d36095df5fad8f1704730669d2b0db5be"}, - {file = "ciso8601-2.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d39aa3d7148fcd9db1007c258e47c9e0174f383d82f5504b80db834c6215b7e4"}, - {file = "ciso8601-2.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e838b694b009e2d9b3b680008fa4c56e52f83935a31ea86fe4203dfff0086f88"}, - {file = "ciso8601-2.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:aa58f55ed5c8b1e9962b56b2ecbfcca32f056edf8ecdce73b6623c55a2fd11e8"}, - {file = "ciso8601-2.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:161dc428d1735ed6dee6ce599c4275ef3fe280fe37308e3cc2efd4301781a7ff"}, - {file = "ciso8601-2.3.0-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:374275a329138b9b70c857c9ea460f65dc7f01ed2513f991e57090f39bf01de5"}, - {file = "ciso8601-2.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:58910c03b5464d6b766ac5d894c6089ee8279432b85181283571b0e2bf502df4"}, - {file = "ciso8601-2.3.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9f7608a276fa46d28255906c341752a87fe5353d8060932e0ec71745148a4d8"}, - {file = "ciso8601-2.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e20d14155f7b069f2aa2387a3f31de98f93bb94da63ad1b5aae78445b33f0529"}, - {file = "ciso8601-2.3.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3f781561401c8666accae823ed8f2a5d1fa50b3e65eb65c21a2bd0374e14f19"}, - {file = "ciso8601-2.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0f4a649e9693e5a46843b0ebd288de1e45b8852a2cff684e3a6b6f3fd56ec4e"}, - {file = "ciso8601-2.3.0.tar.gz", hash = "sha256:19e3fbd786d8bec3358eac94d8774d365b694b604fd1789244b87083f66c8900"}, + {file = "ciso8601-2.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:57db9a28e87f9e4fccba643fb70a9ba1515adc5e1325508eb2c10dd96620314c"}, + {file = "ciso8601-2.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c59646197ddbf84909b6c31d55f744cfeef51811e3910b61d0f58f2885823fd"}, + {file = "ciso8601-2.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6a25da209193134842cd573464a5323f46fcc3ed781b633f15a34793ba7e1064"}, + {file = "ciso8601-2.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ae83f4e60fc7e260a4188e4ec4ac1bdd40bdb382eeda92fc266c5aa2f0a1ee"}, + {file = "ciso8601-2.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2c1ef17d1ea52a39b2dce6535583631ae4bfb65c76f0ee8c99413a6861a46c9e"}, + {file = "ciso8601-2.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3771049ba29bd1077588c0a24be1d53f7493e7cc686b2caa92f7cae129636a0e"}, + {file = "ciso8601-2.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:55381365366dacb57207cec610d26c9a6c0d237cb65a0cf67a2baaa5299f2366"}, + {file = "ciso8601-2.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9f25647803c9a5aaaed130c53bbec7ea06a4f95ba5c7016f59e444b4ef7ac39e"}, + {file = "ciso8601-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:473288cd63efe6a2cf3f4b5f90394e53095358ccb13d6128f87a2da85d0f389b"}, + {file = "ciso8601-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:121d27c55f4455eaa27ba3bd602beca915df9a352f235e935636a4660321070e"}, + {file = "ciso8601-2.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef44cb4dc83f37019a356c7a72692cbe17072456f4879ca6bc0339f67eee5d00"}, + {file = "ciso8601-2.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:364702e338212b6c1a8643d9399ada21560cf132f363853473560625cb4207f1"}, + {file = "ciso8601-2.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8acb45545e6a654310c6ef788aacb2d73686646c414ceacdd9f5f78a83165af5"}, + {file = "ciso8601-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:99addd8b113f85fac549167073f317a318cd2b5841552598ceb97b97c5708a38"}, + {file = "ciso8601-2.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f39bb5936debf21c52e5d52b89f26857c303da80c43a72883946096a6ef5e561"}, + {file = "ciso8601-2.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:21cf83ca945bb26ecd95364ae2c9ed0276378e5fe35ce1b64d4c6d5b33038ea3"}, + {file = "ciso8601-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:013410263cba46748d2de29e9894341ae41223356cde7970478c32bd0984d10c"}, + {file = "ciso8601-2.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b26935687ef1837b56997d8c61f1d789e698be58b261410e629eda9c89812141"}, + {file = "ciso8601-2.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0d980a2a88030d4d8b2434623c250866a75b4979d289eba69bec445c51ace99f"}, + {file = "ciso8601-2.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:87721de54e008fb1c4c3978553b05a9c417aa25b76ddf5702d6f7e8d9b109288"}, + {file = "ciso8601-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:9f107a4c051e7c0416824279264d94f4ed3da0fbd82bd96ec3c3293426826de4"}, + {file = "ciso8601-2.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:02ecbd7c8336c4e1c6bb725b898e29414ee92bdc0be6c72fb07036836b1ac867"}, + {file = "ciso8601-2.3.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36525b1f380f4601533f4631c69911e44efb9cb50beab1da3248b0daa32bced4"}, + {file = "ciso8601-2.3.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:874d20c6339e9096baaadfd1b9610bb8d5b373a0f2858cc06de8142b98d2129c"}, + {file = "ciso8601-2.3.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:46a3663c2cf838f0149e1cdb8e4bdc95716e03cf2d5f803a6eb755d825896ebe"}, + {file = "ciso8601-2.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e8e76825f80ce313d75bbbef1d3b8bd9e0ce31dbc157d1981e9593922c9983e7"}, + {file = "ciso8601-2.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6850889813f3135e0aa18f0aaec64249dd81d36a1b9bce60bb45182930c86663"}, + {file = "ciso8601-2.3.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c690ac24ec3407f68cdfd5e032c6cb18126ef33d6c4b3db0669b9cbb8c96bd4"}, + {file = "ciso8601-2.3.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:024c52d5d0670f15ca3dc53eff7345b6eaee22fba929675f6a408f9d1e159d98"}, + {file = "ciso8601-2.3.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7ae2c3442d042de5330672d0d28486ed92f9d7c6dc010943aa618fd361d4638"}, + {file = "ciso8601-2.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:22128f0def36fa3c4cf0c482a216e8b8ad722def08bc11c07438eff82bdcd02a"}, + {file = "ciso8601-2.3.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:025859ec286a994aa3f2120c0f27d053b719cabc975398338374f2cc1f961125"}, + {file = "ciso8601-2.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2a64ff58904d4418d60fa9619014ae820ae21f7aef58da46df78a4c647f951ec"}, + {file = "ciso8601-2.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d1f85c0b7fa742bbfd18177137ccbaa3f867dd06157f91595075bb959a733048"}, + {file = "ciso8601-2.3.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ac59453664781dfddebee51f9a36e41819993823fdb09ddc0ce0e4bd3ff0c3"}, + {file = "ciso8601-2.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:eaecca7e0c3ef9e8f5e963e212b083684e849f9a9bb25834d3042363223a73cd"}, + {file = "ciso8601-2.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ad8f417c45eea973a694599b96f40d841215bfee352cb9963383e8d66b309981"}, + {file = "ciso8601-2.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:b869396e9756a7c0696d8eb69ce1d8980bea5e25c86e5996b10d78c900a4362c"}, + {file = "ciso8601-2.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7eb7b5ef8714d3d1fe9f3256b7a679ad783da899a0b7503a5ace78186735f840"}, + {file = "ciso8601-2.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:02828107880848ff497971ebc98e6dc851ad7af8ec14a58089e0e11f3111cad6"}, + {file = "ciso8601-2.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:566b4a8b2f9717e54ffcdd732a7c8051a91da30a60a4f1dafb62e303a1dbac69"}, + {file = "ciso8601-2.3.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58a749d63f28c2eda71416c9d6014113b0748abf5fd14c502b01bd515502fedf"}, + {file = "ciso8601-2.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:cb135de0e3b8feb7e74a4f7a234e8c8545957fe8d26316a1a549553f425c629d"}, + {file = "ciso8601-2.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:695583810836a42945084b33621b22b0309701c6916689f6a3588fa44c5bc413"}, + {file = "ciso8601-2.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:21204d98496cf5c0511dc21533be55c2a2d34b8c65603946a116812ffbae3b2d"}, + {file = "ciso8601-2.3.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c29ea2b03dee2dc0a5d3e4a0b7d7768c597781e9fa451fe1025600f7cb55a89"}, + {file = "ciso8601-2.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7533256af90724b8b7a707dcd1be4b67989447595c8e1e1c28399d4fd51dac50"}, + {file = "ciso8601-2.3.1-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4bc9d577c0d1e57532513fc2899f5231727e28981a426767f7fa13dacb18c06"}, + {file = "ciso8601-2.3.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:4e30501eed43eea7ef64f032c81cd1d8b2020035cbdcefad40db72e2f3bc97ff"}, + {file = "ciso8601-2.3.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:070f568de3bc269268296cb9265704dc5fcb9d4c12b1f1c67536624174df5d09"}, + {file = "ciso8601-2.3.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:9065053c034c80c0afd74c71a4906675d07078a05cfd1cb5ff70661378cdbe60"}, + {file = "ciso8601-2.3.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ac00d293cdb3d1a5c78e09b3d75c7b0292ab45d5b26853b436ff5087eba2165"}, + {file = "ciso8601-2.3.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:06941e2ee46701f083aeb21d13eb762d74d5ed6c46ff22119f27a42ed6edc8f9"}, + {file = "ciso8601-2.3.1.tar.gz", hash = "sha256:3212c7ffe5d8080270548b5f2692ffd2039683b6628a8d2ad456122cc5793c4c"}, ] [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -625,103 +515,98 @@ files = [ [[package]] name = "coverage" -version = "7.3.1" +version = "7.3.2" description = "Code coverage measurement for Python" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cd0f7429ecfd1ff597389907045ff209c8fdb5b013d38cfa7c60728cb484b6e3"}, - {file = "coverage-7.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:966f10df9b2b2115da87f50f6a248e313c72a668248be1b9060ce935c871f276"}, - {file = "coverage-7.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0575c37e207bb9b98b6cf72fdaaa18ac909fb3d153083400c2d48e2e6d28bd8e"}, - {file = "coverage-7.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:245c5a99254e83875c7fed8b8b2536f040997a9b76ac4c1da5bff398c06e860f"}, - {file = "coverage-7.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c96dd7798d83b960afc6c1feb9e5af537fc4908852ef025600374ff1a017392"}, - {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:de30c1aa80f30af0f6b2058a91505ea6e36d6535d437520067f525f7df123887"}, - {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:50dd1e2dd13dbbd856ffef69196781edff26c800a74f070d3b3e3389cab2600d"}, - {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9c0c19f70d30219113b18fe07e372b244fb2a773d4afde29d5a2f7930765136"}, - {file = "coverage-7.3.1-cp310-cp310-win32.whl", hash = "sha256:770f143980cc16eb601ccfd571846e89a5fe4c03b4193f2e485268f224ab602f"}, - {file = "coverage-7.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:cdd088c00c39a27cfa5329349cc763a48761fdc785879220d54eb785c8a38520"}, - {file = "coverage-7.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:74bb470399dc1989b535cb41f5ca7ab2af561e40def22d7e188e0a445e7639e3"}, - {file = "coverage-7.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:025ded371f1ca280c035d91b43252adbb04d2aea4c7105252d3cbc227f03b375"}, - {file = "coverage-7.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6191b3a6ad3e09b6cfd75b45c6aeeffe7e3b0ad46b268345d159b8df8d835f9"}, - {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7eb0b188f30e41ddd659a529e385470aa6782f3b412f860ce22b2491c89b8593"}, - {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c8f0df9dfd8ff745bccff75867d63ef336e57cc22b2908ee725cc552689ec8"}, - {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7eb3cd48d54b9bd0e73026dedce44773214064be93611deab0b6a43158c3d5a0"}, - {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ac3c5b7e75acac31e490b7851595212ed951889918d398b7afa12736c85e13ce"}, - {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b4ee7080878077af0afa7238df1b967f00dc10763f6e1b66f5cced4abebb0a3"}, - {file = "coverage-7.3.1-cp311-cp311-win32.whl", hash = "sha256:229c0dd2ccf956bf5aeede7e3131ca48b65beacde2029f0361b54bf93d36f45a"}, - {file = "coverage-7.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:c6f55d38818ca9596dc9019eae19a47410d5322408140d9a0076001a3dcb938c"}, - {file = "coverage-7.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5289490dd1c3bb86de4730a92261ae66ea8d44b79ed3cc26464f4c2cde581fbc"}, - {file = "coverage-7.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca833941ec701fda15414be400c3259479bfde7ae6d806b69e63b3dc423b1832"}, - {file = "coverage-7.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd694e19c031733e446c8024dedd12a00cda87e1c10bd7b8539a87963685e969"}, - {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aab8e9464c00da5cb9c536150b7fbcd8850d376d1151741dd0d16dfe1ba4fd26"}, - {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87d38444efffd5b056fcc026c1e8d862191881143c3aa80bb11fcf9dca9ae204"}, - {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8a07b692129b8a14ad7a37941a3029c291254feb7a4237f245cfae2de78de037"}, - {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2829c65c8faaf55b868ed7af3c7477b76b1c6ebeee99a28f59a2cb5907a45760"}, - {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1f111a7d85658ea52ffad7084088277135ec5f368457275fc57f11cebb15607f"}, - {file = "coverage-7.3.1-cp312-cp312-win32.whl", hash = "sha256:c397c70cd20f6df7d2a52283857af622d5f23300c4ca8e5bd8c7a543825baa5a"}, - {file = "coverage-7.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:5ae4c6da8b3d123500f9525b50bf0168023313963e0e2e814badf9000dd6ef92"}, - {file = "coverage-7.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ca70466ca3a17460e8fc9cea7123c8cbef5ada4be3140a1ef8f7b63f2f37108f"}, - {file = "coverage-7.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f2781fd3cabc28278dc982a352f50c81c09a1a500cc2086dc4249853ea96b981"}, - {file = "coverage-7.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6407424621f40205bbe6325686417e5e552f6b2dba3535dd1f90afc88a61d465"}, - {file = "coverage-7.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04312b036580ec505f2b77cbbdfb15137d5efdfade09156961f5277149f5e344"}, - {file = "coverage-7.3.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac9ad38204887349853d7c313f53a7b1c210ce138c73859e925bc4e5d8fc18e7"}, - {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:53669b79f3d599da95a0afbef039ac0fadbb236532feb042c534fbb81b1a4e40"}, - {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:614f1f98b84eb256e4f35e726bfe5ca82349f8dfa576faabf8a49ca09e630086"}, - {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f1a317fdf5c122ad642db8a97964733ab7c3cf6009e1a8ae8821089993f175ff"}, - {file = "coverage-7.3.1-cp38-cp38-win32.whl", hash = "sha256:defbbb51121189722420a208957e26e49809feafca6afeef325df66c39c4fdb3"}, - {file = "coverage-7.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:f4f456590eefb6e1b3c9ea6328c1e9fa0f1006e7481179d749b3376fc793478e"}, - {file = "coverage-7.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f12d8b11a54f32688b165fd1a788c408f927b0960984b899be7e4c190ae758f1"}, - {file = "coverage-7.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f09195dda68d94a53123883de75bb97b0e35f5f6f9f3aa5bf6e496da718f0cb6"}, - {file = "coverage-7.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6601a60318f9c3945be6ea0f2a80571f4299b6801716f8a6e4846892737ebe4"}, - {file = "coverage-7.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07d156269718670d00a3b06db2288b48527fc5f36859425ff7cec07c6b367745"}, - {file = "coverage-7.3.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:636a8ac0b044cfeccae76a36f3b18264edcc810a76a49884b96dd744613ec0b7"}, - {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5d991e13ad2ed3aced177f524e4d670f304c8233edad3210e02c465351f785a0"}, - {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:586649ada7cf139445da386ab6f8ef00e6172f11a939fc3b2b7e7c9082052fa0"}, - {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4aba512a15a3e1e4fdbfed2f5392ec221434a614cc68100ca99dcad7af29f3f8"}, - {file = "coverage-7.3.1-cp39-cp39-win32.whl", hash = "sha256:6bc6f3f4692d806831c136c5acad5ccedd0262aa44c087c46b7101c77e139140"}, - {file = "coverage-7.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:553d7094cb27db58ea91332e8b5681bac107e7242c23f7629ab1316ee73c4981"}, - {file = "coverage-7.3.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:220eb51f5fb38dfdb7e5d54284ca4d0cd70ddac047d750111a68ab1798945194"}, - {file = "coverage-7.3.1.tar.gz", hash = "sha256:6cb7fe1581deb67b782c153136541e20901aa312ceedaf1467dcb35255787952"}, + {file = "coverage-7.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf"}, + {file = "coverage-7.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda"}, + {file = "coverage-7.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a"}, + {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c"}, + {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f"}, + {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6"}, + {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148"}, + {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9"}, + {file = "coverage-7.3.2-cp310-cp310-win32.whl", hash = "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f"}, + {file = "coverage-7.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611"}, + {file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"}, + {file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"}, + {file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"}, + {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"}, + {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"}, + {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"}, + {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"}, + {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"}, + {file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"}, + {file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"}, + {file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"}, + {file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"}, + {file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"}, + {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"}, + {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"}, + {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"}, + {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"}, + {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"}, + {file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"}, + {file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"}, + {file = "coverage-7.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738"}, + {file = "coverage-7.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2"}, + {file = "coverage-7.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2"}, + {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c"}, + {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9"}, + {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82"}, + {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901"}, + {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76"}, + {file = "coverage-7.3.2-cp38-cp38-win32.whl", hash = "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92"}, + {file = "coverage-7.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a"}, + {file = "coverage-7.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce"}, + {file = "coverage-7.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9"}, + {file = "coverage-7.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f"}, + {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25"}, + {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9"}, + {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6"}, + {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc"}, + {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083"}, + {file = "coverage-7.3.2-cp39-cp39-win32.whl", hash = "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce"}, + {file = "coverage-7.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f"}, + {file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"}, + {file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"}, ] -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - [package.extras] toml = ["tomli"] [[package]] name = "cryptography" -version = "41.0.3" +version = "41.0.5" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-41.0.3-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:652627a055cb52a84f8c448185922241dd5217443ca194d5739b44612c5e6507"}, - {file = "cryptography-41.0.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:8f09daa483aedea50d249ef98ed500569841d6498aa9c9f4b0531b9964658922"}, - {file = "cryptography-41.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fd871184321100fb400d759ad0cddddf284c4b696568204d281c902fc7b0d81"}, - {file = "cryptography-41.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84537453d57f55a50a5b6835622ee405816999a7113267739a1b4581f83535bd"}, - {file = "cryptography-41.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3fb248989b6363906827284cd20cca63bb1a757e0a2864d4c1682a985e3dca47"}, - {file = "cryptography-41.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:42cb413e01a5d36da9929baa9d70ca90d90b969269e5a12d39c1e0d475010116"}, - {file = "cryptography-41.0.3-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:aeb57c421b34af8f9fe830e1955bf493a86a7996cc1338fe41b30047d16e962c"}, - {file = "cryptography-41.0.3-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6af1c6387c531cd364b72c28daa29232162010d952ceb7e5ca8e2827526aceae"}, - {file = "cryptography-41.0.3-cp37-abi3-win32.whl", hash = "sha256:0d09fb5356f975974dbcb595ad2d178305e5050656affb7890a1583f5e02a306"}, - {file = "cryptography-41.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:a983e441a00a9d57a4d7c91b3116a37ae602907a7618b882c8013b5762e80574"}, - {file = "cryptography-41.0.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5259cb659aa43005eb55a0e4ff2c825ca111a0da1814202c64d28a985d33b087"}, - {file = "cryptography-41.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:67e120e9a577c64fe1f611e53b30b3e69744e5910ff3b6e97e935aeb96005858"}, - {file = "cryptography-41.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:7efe8041897fe7a50863e51b77789b657a133c75c3b094e51b5e4b5cec7bf906"}, - {file = "cryptography-41.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ce785cf81a7bdade534297ef9e490ddff800d956625020ab2ec2780a556c313e"}, - {file = "cryptography-41.0.3-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:57a51b89f954f216a81c9d057bf1a24e2f36e764a1ca9a501a6964eb4a6800dd"}, - {file = "cryptography-41.0.3-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c2f0d35703d61002a2bbdcf15548ebb701cfdd83cdc12471d2bae80878a4207"}, - {file = "cryptography-41.0.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:23c2d778cf829f7d0ae180600b17e9fceea3c2ef8b31a99e3c694cbbf3a24b84"}, - {file = "cryptography-41.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:95dd7f261bb76948b52a5330ba5202b91a26fbac13ad0e9fc8a3ac04752058c7"}, - {file = "cryptography-41.0.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:41d7aa7cdfded09b3d73a47f429c298e80796c8e825ddfadc84c8a7f12df212d"}, - {file = "cryptography-41.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d0d651aa754ef58d75cec6edfbd21259d93810b73f6ec246436a21b7841908de"}, - {file = "cryptography-41.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ab8de0d091acbf778f74286f4989cf3d1528336af1b59f3e5d2ebca8b5fe49e1"}, - {file = "cryptography-41.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a74fbcdb2a0d46fe00504f571a2a540532f4c188e6ccf26f1f178480117b33c4"}, - {file = "cryptography-41.0.3.tar.gz", hash = "sha256:6d192741113ef5e30d89dcb5b956ef4e1578f304708701b8b73d38e3e1461f34"}, + {file = "cryptography-41.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:da6a0ff8f1016ccc7477e6339e1d50ce5f59b88905585f77193ebd5068f1e797"}, + {file = "cryptography-41.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b948e09fe5fb18517d99994184854ebd50b57248736fd4c720ad540560174ec5"}, + {file = "cryptography-41.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d38e6031e113b7421db1de0c1b1f7739564a88f1684c6b89234fbf6c11b75147"}, + {file = "cryptography-41.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e270c04f4d9b5671ebcc792b3ba5d4488bf7c42c3c241a3748e2599776f29696"}, + {file = "cryptography-41.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ec3b055ff8f1dce8e6ef28f626e0972981475173d7973d63f271b29c8a2897da"}, + {file = "cryptography-41.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:7d208c21e47940369accfc9e85f0de7693d9a5d843c2509b3846b2db170dfd20"}, + {file = "cryptography-41.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:8254962e6ba1f4d2090c44daf50a547cd5f0bf446dc658a8e5f8156cae0d8548"}, + {file = "cryptography-41.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a48e74dad1fb349f3dc1d449ed88e0017d792997a7ad2ec9587ed17405667e6d"}, + {file = "cryptography-41.0.5-cp37-abi3-win32.whl", hash = "sha256:d3977f0e276f6f5bf245c403156673db103283266601405376f075c849a0b936"}, + {file = "cryptography-41.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:73801ac9736741f220e20435f84ecec75ed70eda90f781a148f1bad546963d81"}, + {file = "cryptography-41.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3be3ca726e1572517d2bef99a818378bbcf7d7799d5372a46c79c29eb8d166c1"}, + {file = "cryptography-41.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e886098619d3815e0ad5790c973afeee2c0e6e04b4da90b88e6bd06e2a0b1b72"}, + {file = "cryptography-41.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:573eb7128cbca75f9157dcde974781209463ce56b5804983e11a1c462f0f4e88"}, + {file = "cryptography-41.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0c327cac00f082013c7c9fb6c46b7cc9fa3c288ca702c74773968173bda421bf"}, + {file = "cryptography-41.0.5-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:227ec057cd32a41c6651701abc0328135e472ed450f47c2766f23267b792a88e"}, + {file = "cryptography-41.0.5-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:22892cc830d8b2c89ea60148227631bb96a7da0c1b722f2aac8824b1b7c0b6b8"}, + {file = "cryptography-41.0.5-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:5a70187954ba7292c7876734183e810b728b4f3965fbe571421cb2434d279179"}, + {file = "cryptography-41.0.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:88417bff20162f635f24f849ab182b092697922088b477a7abd6664ddd82291d"}, + {file = "cryptography-41.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c707f7afd813478e2019ae32a7c49cd932dd60ab2d2a93e796f68236b7e1fbf1"}, + {file = "cryptography-41.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:580afc7b7216deeb87a098ef0674d6ee34ab55993140838b14c9b83312b37b86"}, + {file = "cryptography-41.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fba1e91467c65fe64a82c689dc6cf58151158993b13eb7a7f3f4b7f395636723"}, + {file = "cryptography-41.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0d2a6a598847c46e3e321a7aef8af1436f11c27f1254933746304ff014664d84"}, + {file = "cryptography-41.0.5.tar.gz", hash = "sha256:392cb88b597247177172e02da6b7a63deeff1937fa6fec3bbf902ebd75d97ec7"}, ] [package.dependencies] @@ -739,41 +624,35 @@ test-randomorder = ["pytest-randomly"] [[package]] name = "debugpy" -version = "1.7.0" +version = "1.8.0" description = "An implementation of the Debug Adapter Protocol for Python" -category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "debugpy-1.7.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:17ad9a681aca1704c55b9a5edcb495fa8f599e4655c9872b7f9cf3dc25890d48"}, - {file = "debugpy-1.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1285920a3f9a75f5d1acf59ab1b9da9ae6eb9a05884cd7674f95170c9cafa4de"}, - {file = "debugpy-1.7.0-cp310-cp310-win32.whl", hash = "sha256:a6f43a681c5025db1f1c0568069d1d1bad306a02e7c36144912b26d9c90e4724"}, - {file = "debugpy-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:9e9571d831ad3c75b5fb6f3efcb71c471cf2a74ba84af6ac1c79ce00683bed4b"}, - {file = "debugpy-1.7.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:538765a41198aa88cc089295b39c7322dd598f9ef1d52eaae12145c63bf9430a"}, - {file = "debugpy-1.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7e8cf91f8f3f9b5fad844dd88427b85d398bda1e2a0cd65d5a21312fcbc0c6f"}, - {file = "debugpy-1.7.0-cp311-cp311-win32.whl", hash = "sha256:18a69f8e142a716310dd0af6d7db08992aed99e2606108732efde101e7c65e2a"}, - {file = "debugpy-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:7515a5ba5ee9bfe956685909c5f28734c1cecd4ee813523363acfe3ca824883a"}, - {file = "debugpy-1.7.0-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:bc8da67ade39d9e75608cdb8601d07e63a4e85966e0572c981f14e2cf42bcdef"}, - {file = "debugpy-1.7.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5036e918c6ba8fc4c4f1fd0207d81db634431a02f0dc2ba51b12fd793c8c9de"}, - {file = "debugpy-1.7.0-cp37-cp37m-win32.whl", hash = "sha256:d5be95b3946a4d7b388e45068c7b75036ac5a610f41014aee6cafcd5506423ad"}, - {file = "debugpy-1.7.0-cp37-cp37m-win_amd64.whl", hash = "sha256:0e90314a078d4e3f009520c8387aba8f74c3034645daa7a332a3d1bb81335756"}, - {file = "debugpy-1.7.0-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:1565fd904f9571c430adca597771255cff4f92171486fced6f765dcbdfc8ec8d"}, - {file = "debugpy-1.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6516f36a2e95b3be27f171f12b641e443863f4ad5255d0fdcea6ae0be29bb912"}, - {file = "debugpy-1.7.0-cp38-cp38-win32.whl", hash = "sha256:2b0e489613bc066051439df04c56777ec184b957d6810cb65f235083aef7a0dc"}, - {file = "debugpy-1.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:7bf0b4bbd841b2397b6a8de15da9227f1164f6d43ceee971c50194eaed930a9d"}, - {file = "debugpy-1.7.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:ad22e1095b9977af432465c1e09132ba176e18df3834b1efcab1a449346b350b"}, - {file = "debugpy-1.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f625e427f21423e5874139db529e18cb2966bdfcc1cb87a195538c5b34d163d1"}, - {file = "debugpy-1.7.0-cp39-cp39-win32.whl", hash = "sha256:18bca8429d6632e2d3435055416d2d88f0309cc39709f4f6355c8d412cc61f24"}, - {file = "debugpy-1.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:dc8a12ac8b97ef3d6973c6679a093138c7c9b03eb685f0e253269a195f651559"}, - {file = "debugpy-1.7.0-py2.py3-none-any.whl", hash = "sha256:f6de2e6f24f62969e0f0ef682d78c98161c4dca29e9fb05df4d2989005005502"}, - {file = "debugpy-1.7.0.zip", hash = "sha256:676911c710e85567b17172db934a71319ed9d995104610ce23fd74a07f66e6f6"}, + {file = "debugpy-1.8.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:7fb95ca78f7ac43393cd0e0f2b6deda438ec7c5e47fa5d38553340897d2fbdfb"}, + {file = "debugpy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef9ab7df0b9a42ed9c878afd3eaaff471fce3fa73df96022e1f5c9f8f8c87ada"}, + {file = "debugpy-1.8.0-cp310-cp310-win32.whl", hash = "sha256:a8b7a2fd27cd9f3553ac112f356ad4ca93338feadd8910277aff71ab24d8775f"}, + {file = "debugpy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:5d9de202f5d42e62f932507ee8b21e30d49aae7e46d5b1dd5c908db1d7068637"}, + {file = "debugpy-1.8.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:ef54404365fae8d45cf450d0544ee40cefbcb9cb85ea7afe89a963c27028261e"}, + {file = "debugpy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60009b132c91951354f54363f8ebdf7457aeb150e84abba5ae251b8e9f29a8a6"}, + {file = "debugpy-1.8.0-cp311-cp311-win32.whl", hash = "sha256:8cd0197141eb9e8a4566794550cfdcdb8b3db0818bdf8c49a8e8f8053e56e38b"}, + {file = "debugpy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:a64093656c4c64dc6a438e11d59369875d200bd5abb8f9b26c1f5f723622e153"}, + {file = "debugpy-1.8.0-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:b05a6b503ed520ad58c8dc682749113d2fd9f41ffd45daec16e558ca884008cd"}, + {file = "debugpy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c6fb41c98ec51dd010d7ed650accfd07a87fe5e93eca9d5f584d0578f28f35f"}, + {file = "debugpy-1.8.0-cp38-cp38-win32.whl", hash = "sha256:46ab6780159eeabb43c1495d9c84cf85d62975e48b6ec21ee10c95767c0590aa"}, + {file = "debugpy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:bdc5ef99d14b9c0fcb35351b4fbfc06ac0ee576aeab6b2511702e5a648a2e595"}, + {file = "debugpy-1.8.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:61eab4a4c8b6125d41a34bad4e5fe3d2cc145caecd63c3fe953be4cc53e65bf8"}, + {file = "debugpy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:125b9a637e013f9faac0a3d6a82bd17c8b5d2c875fb6b7e2772c5aba6d082332"}, + {file = "debugpy-1.8.0-cp39-cp39-win32.whl", hash = "sha256:57161629133113c97b387382045649a2b985a348f0c9366e22217c87b68b73c6"}, + {file = "debugpy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:e3412f9faa9ade82aa64a50b602544efcba848c91384e9f93497a458767e6926"}, + {file = "debugpy-1.8.0-py2.py3-none-any.whl", hash = "sha256:9c9b0ac1ce2a42888199df1a1906e45e6f3c9555497643a85e0bf2406e3ffbc4"}, + {file = "debugpy-1.8.0.zip", hash = "sha256:12af2c55b419521e33d5fb21bd022df0b5eb267c3e178f1d374a63a2a6bdccd0"}, ] [[package]] name = "ecdsa" version = "0.18.0" description = "ECDSA cryptographic signature library (pure python)" -category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -792,7 +671,6 @@ gmpy2 = ["gmpy2"] name = "esptool" version = "4.6.2" description = "A serial utility to communicate & flash code to Espressif chips." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -811,26 +689,10 @@ reedsolo = ">=1.5.3,<1.8" dev = ["black", "coverage (>=6.0,<7.0)", "flake8 (>=3.2.0)", "flake8-gl-codeclimate", "flake8-import-order", "pre-commit", "pyelftools", "pytest", "pytest-rerunfailures"] hsm = ["python-pkcs11"] -[[package]] -name = "exceptiongroup" -version = "1.1.3" -description = "Backport of PEP 654 (exception groups)" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, - {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, -] - -[package.extras] -test = ["pytest (>=6)"] - [[package]] name = "flake8" version = "6.1.0" description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" optional = false python-versions = ">=3.8.1" files = [ @@ -847,7 +709,6 @@ pyflakes = ">=3.1.0,<3.2.0" name = "flake8-quotes" version = "3.3.2" description = "Flake8 lint for quotes." -category = "dev" optional = false python-versions = "*" files = [ @@ -861,7 +722,6 @@ flake8 = "*" name = "frozenlist" version = "1.4.0" description = "A list-like structure which implements collections.abc.MutableSequence" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -930,119 +790,135 @@ files = [ [[package]] name = "grpcio" -version = "1.58.0" +version = "1.59.3" description = "HTTP/2-based RPC framework" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "grpcio-1.58.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3e6bebf1dfdbeb22afd95650e4f019219fef3ab86d3fca8ebade52e4bc39389a"}, - {file = "grpcio-1.58.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:cde11577d5b6fd73a00e6bfa3cf5f428f3f33c2d2878982369b5372bbc4acc60"}, - {file = "grpcio-1.58.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:a2d67ff99e70e86b2be46c1017ae40b4840d09467d5455b2708de6d4c127e143"}, - {file = "grpcio-1.58.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ed979b273a81de36fc9c6716d9fb09dd3443efa18dcc8652501df11da9583e9"}, - {file = "grpcio-1.58.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:458899d2ebd55d5ca2350fd3826dfd8fcb11fe0f79828ae75e2b1e6051d50a29"}, - {file = "grpcio-1.58.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bc7ffef430b80345729ff0a6825e9d96ac87efe39216e87ac58c6c4ef400de93"}, - {file = "grpcio-1.58.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5b23d75e5173faa3d1296a7bedffb25afd2fddb607ef292dfc651490c7b53c3d"}, - {file = "grpcio-1.58.0-cp310-cp310-win32.whl", hash = "sha256:fad9295fe02455d4f158ad72c90ef8b4bcaadfdb5efb5795f7ab0786ad67dd58"}, - {file = "grpcio-1.58.0-cp310-cp310-win_amd64.whl", hash = "sha256:bc325fed4d074367bebd465a20763586e5e1ed5b943e9d8bc7c162b1f44fd602"}, - {file = "grpcio-1.58.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:652978551af02373a5a313e07bfef368f406b5929cf2d50fa7e4027f913dbdb4"}, - {file = "grpcio-1.58.0-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:9f13a171281ebb4d7b1ba9f06574bce2455dcd3f2f6d1fbe0fd0d84615c74045"}, - {file = "grpcio-1.58.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:8774219e21b05f750eef8adc416e9431cf31b98f6ce9def288e4cea1548cbd22"}, - {file = "grpcio-1.58.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09206106848462763f7f273ca93d2d2d4d26cab475089e0de830bb76be04e9e8"}, - {file = "grpcio-1.58.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62831d5e251dd7561d9d9e83a0b8655084b2a1f8ea91e4bd6b3cedfefd32c9d2"}, - {file = "grpcio-1.58.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:212f38c6a156862098f6bdc9a79bf850760a751d259d8f8f249fc6d645105855"}, - {file = "grpcio-1.58.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4b12754af201bb993e6e2efd7812085ddaaef21d0a6f0ff128b97de1ef55aa4a"}, - {file = "grpcio-1.58.0-cp311-cp311-win32.whl", hash = "sha256:3886b4d56bd4afeac518dbc05933926198aa967a7d1d237a318e6fbc47141577"}, - {file = "grpcio-1.58.0-cp311-cp311-win_amd64.whl", hash = "sha256:002f228d197fea12797a14e152447044e14fb4fdb2eb5d6cfa496f29ddbf79ef"}, - {file = "grpcio-1.58.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:b5e8db0aff0a4819946215f156bd722b6f6c8320eb8419567ffc74850c9fd205"}, - {file = "grpcio-1.58.0-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:201e550b7e2ede113b63e718e7ece93cef5b0fbf3c45e8fe4541a5a4305acd15"}, - {file = "grpcio-1.58.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:d79b660681eb9bc66cc7cbf78d1b1b9e335ee56f6ea1755d34a31108b80bd3c8"}, - {file = "grpcio-1.58.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ef8d4a76d2c7d8065aba829f8d0bc0055495c998dce1964ca5b302d02514fb3"}, - {file = "grpcio-1.58.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6cba491c638c76d3dc6c191d9c75041ca5b8f5c6de4b8327ecdcab527f130bb4"}, - {file = "grpcio-1.58.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6801ff6652ecd2aae08ef994a3e49ff53de29e69e9cd0fd604a79ae4e545a95c"}, - {file = "grpcio-1.58.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:24edec346e69e672daf12b2c88e95c6f737f3792d08866101d8c5f34370c54fd"}, - {file = "grpcio-1.58.0-cp37-cp37m-win_amd64.whl", hash = "sha256:7e473a7abad9af48e3ab5f3b5d237d18208024d28ead65a459bd720401bd2f8f"}, - {file = "grpcio-1.58.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:4891bbb4bba58acd1d620759b3be11245bfe715eb67a4864c8937b855b7ed7fa"}, - {file = "grpcio-1.58.0-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:e9f995a8a421405958ff30599b4d0eec244f28edc760de82f0412c71c61763d2"}, - {file = "grpcio-1.58.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:2f85f87e2f087d9f632c085b37440a3169fda9cdde80cb84057c2fc292f8cbdf"}, - {file = "grpcio-1.58.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb6b92036ff312d5b4182fa72e8735d17aceca74d0d908a7f08e375456f03e07"}, - {file = "grpcio-1.58.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d81c2b2b24c32139dd2536972f1060678c6b9fbd106842a9fcdecf07b233eccd"}, - {file = "grpcio-1.58.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:fbcecb6aedd5c1891db1d70efbfbdc126c986645b5dd616a045c07d6bd2dfa86"}, - {file = "grpcio-1.58.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92ae871a902cf19833328bd6498ec007b265aabf2fda845ab5bd10abcaf4c8c6"}, - {file = "grpcio-1.58.0-cp38-cp38-win32.whl", hash = "sha256:dc72e04620d49d3007771c0e0348deb23ca341c0245d610605dddb4ac65a37cb"}, - {file = "grpcio-1.58.0-cp38-cp38-win_amd64.whl", hash = "sha256:1c1c5238c6072470c7f1614bf7c774ffde6b346a100521de9ce791d1e4453afe"}, - {file = "grpcio-1.58.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:fe643af248442221db027da43ed43e53b73e11f40c9043738de9a2b4b6ca7697"}, - {file = "grpcio-1.58.0-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:128eb1f8e70676d05b1b0c8e6600320fc222b3f8c985a92224248b1367122188"}, - {file = "grpcio-1.58.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:039003a5e0ae7d41c86c768ef8b3ee2c558aa0a23cf04bf3c23567f37befa092"}, - {file = "grpcio-1.58.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f061722cad3f9aabb3fbb27f3484ec9d4667b7328d1a7800c3c691a98f16bb0"}, - {file = "grpcio-1.58.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0af11938acf8cd4cf815c46156bcde36fa5850518120920d52620cc3ec1830"}, - {file = "grpcio-1.58.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d4cef77ad2fed42b1ba9143465856d7e737279854e444925d5ba45fc1f3ba727"}, - {file = "grpcio-1.58.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:24765a627eb4d9288ace32d5104161c3654128fe27f2808ecd6e9b0cfa7fc8b9"}, - {file = "grpcio-1.58.0-cp39-cp39-win32.whl", hash = "sha256:f0241f7eb0d2303a545136c59bc565a35c4fc3b924ccbd69cb482f4828d6f31c"}, - {file = "grpcio-1.58.0-cp39-cp39-win_amd64.whl", hash = "sha256:dcfba7befe3a55dab6fe1eb7fc9359dc0c7f7272b30a70ae0af5d5b063842f28"}, - {file = "grpcio-1.58.0.tar.gz", hash = "sha256:532410c51ccd851b706d1fbc00a87be0f5312bd6f8e5dbf89d4e99c7f79d7499"}, + {file = "grpcio-1.59.3-cp310-cp310-linux_armv7l.whl", hash = "sha256:aca028a6c7806e5b61e5f9f4232432c52856f7fcb98e330b20b6bc95d657bdcc"}, + {file = "grpcio-1.59.3-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:19ad26a7967f7999c8960d2b9fe382dae74c55b0c508c613a6c2ba21cddf2354"}, + {file = "grpcio-1.59.3-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:72b71dad2a3d1650e69ad42a5c4edbc59ee017f08c32c95694172bc501def23c"}, + {file = "grpcio-1.59.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c0f0a11d82d0253656cc42e04b6a149521e02e755fe2e4edd21123de610fd1d4"}, + {file = "grpcio-1.59.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60cddafb70f9a2c81ba251b53b4007e07cca7389e704f86266e22c4bffd8bf1d"}, + {file = "grpcio-1.59.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6c75a1fa0e677c1d2b6d4196ad395a5c381dfb8385f07ed034ef667cdcdbcc25"}, + {file = "grpcio-1.59.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e1d8e01438d5964a11167eec1edb5f85ed8e475648f36c834ed5db4ffba24ac8"}, + {file = "grpcio-1.59.3-cp310-cp310-win32.whl", hash = "sha256:c4b0076f0bf29ee62335b055a9599f52000b7941f577daa001c7ef961a1fbeab"}, + {file = "grpcio-1.59.3-cp310-cp310-win_amd64.whl", hash = "sha256:b1f00a3e6e0c3dccccffb5579fc76ebfe4eb40405ba308505b41ef92f747746a"}, + {file = "grpcio-1.59.3-cp311-cp311-linux_armv7l.whl", hash = "sha256:3996aaa21231451161dc29df6a43fcaa8b332042b6150482c119a678d007dd86"}, + {file = "grpcio-1.59.3-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:cb4e9cbd9b7388fcb06412da9f188c7803742d06d6f626304eb838d1707ec7e3"}, + {file = "grpcio-1.59.3-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:8022ca303d6c694a0d7acfb2b472add920217618d3a99eb4b14edc7c6a7e8fcf"}, + {file = "grpcio-1.59.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b36683fad5664283755a7f4e2e804e243633634e93cd798a46247b8e54e3cb0d"}, + {file = "grpcio-1.59.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8239b853226e4824e769517e1b5232e7c4dda3815b200534500338960fcc6118"}, + {file = "grpcio-1.59.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0511af8653fbda489ff11d542a08505d56023e63cafbda60e6e00d4e0bae86ea"}, + {file = "grpcio-1.59.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e78dc982bda74cef2ddfce1c91d29b96864c4c680c634e279ed204d51e227473"}, + {file = "grpcio-1.59.3-cp311-cp311-win32.whl", hash = "sha256:6a5c3a96405966c023e139c3bcccb2c7c776a6f256ac6d70f8558c9041bdccc3"}, + {file = "grpcio-1.59.3-cp311-cp311-win_amd64.whl", hash = "sha256:ed26826ee423b11477297b187371cdf4fa1eca874eb1156422ef3c9a60590dd9"}, + {file = "grpcio-1.59.3-cp312-cp312-linux_armv7l.whl", hash = "sha256:45dddc5cb5227d30fa43652d8872dc87f086d81ab4b500be99413bad0ae198d7"}, + {file = "grpcio-1.59.3-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:1736496d74682e53dd0907fd515f2694d8e6a96c9a359b4080b2504bf2b2d91b"}, + {file = "grpcio-1.59.3-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:ddbd1a16138e52e66229047624de364f88a948a4d92ba20e4e25ad7d22eef025"}, + {file = "grpcio-1.59.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcfa56f8d031ffda902c258c84c4b88707f3a4be4827b4e3ab8ec7c24676320d"}, + {file = "grpcio-1.59.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2eb8f0c7c0c62f7a547ad7a91ba627a5aa32a5ae8d930783f7ee61680d7eb8d"}, + {file = "grpcio-1.59.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8d993399cc65e3a34f8fd48dd9ad7a376734564b822e0160dd18b3d00c1a33f9"}, + {file = "grpcio-1.59.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0bd141f4f41907eb90bda74d969c3cb21c1c62779419782a5b3f5e4b5835718"}, + {file = "grpcio-1.59.3-cp312-cp312-win32.whl", hash = "sha256:33b8fd65d4e97efa62baec6171ce51f9cf68f3a8ba9f866f4abc9d62b5c97b79"}, + {file = "grpcio-1.59.3-cp312-cp312-win_amd64.whl", hash = "sha256:0e735ed002f50d4f3cb9ecfe8ac82403f5d842d274c92d99db64cfc998515e07"}, + {file = "grpcio-1.59.3-cp37-cp37m-linux_armv7l.whl", hash = "sha256:ea40ce4404e7cca0724c91a7404da410f0144148fdd58402a5942971e3469b94"}, + {file = "grpcio-1.59.3-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83113bcc393477b6f7342b9f48e8a054330c895205517edc66789ceea0796b53"}, + {file = "grpcio-1.59.3-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:73afbac602b8f1212a50088193601f869b5073efa9855b3e51aaaec97848fc8a"}, + {file = "grpcio-1.59.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:575d61de1950b0b0699917b686b1ca108690702fcc2df127b8c9c9320f93e069"}, + {file = "grpcio-1.59.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cd76057b5c9a4d68814610ef9226925f94c1231bbe533fdf96f6181f7d2ff9e"}, + {file = "grpcio-1.59.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:95d6fd804c81efe4879e38bfd84d2b26e339a0a9b797e7615e884ef4686eb47b"}, + {file = "grpcio-1.59.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0d42048b8a3286ea4134faddf1f9a59cf98192b94aaa10d910a25613c5eb5bfb"}, + {file = "grpcio-1.59.3-cp37-cp37m-win_amd64.whl", hash = "sha256:4619fea15c64bcdd9d447cdbdde40e3d5f1da3a2e8ae84103d94a9c1df210d7e"}, + {file = "grpcio-1.59.3-cp38-cp38-linux_armv7l.whl", hash = "sha256:95b5506e70284ac03b2005dd9ffcb6708c9ae660669376f0192a710687a22556"}, + {file = "grpcio-1.59.3-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:9e17660947660ccfce56c7869032910c179a5328a77b73b37305cd1ee9301c2e"}, + {file = "grpcio-1.59.3-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:00912ce19914d038851be5cd380d94a03f9d195643c28e3ad03d355cc02ce7e8"}, + {file = "grpcio-1.59.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e58b3cadaa3c90f1efca26ba33e0d408b35b497307027d3d707e4bcd8de862a6"}, + {file = "grpcio-1.59.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d787ecadea865bdf78f6679f6f5bf4b984f18f659257ba612979df97a298b3c3"}, + {file = "grpcio-1.59.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0814942ba1bba269db4e760a34388640c601dece525c6a01f3b4ff030cc0db69"}, + {file = "grpcio-1.59.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fb111aa99d3180c361a35b5ae1e2c63750220c584a1344229abc139d5c891881"}, + {file = "grpcio-1.59.3-cp38-cp38-win32.whl", hash = "sha256:eb8ba504c726befe40a356ecbe63c6c3c64c9a439b3164f5a718ec53c9874da0"}, + {file = "grpcio-1.59.3-cp38-cp38-win_amd64.whl", hash = "sha256:cdbc6b32fadab9bebc6f49d3e7ec4c70983c71e965497adab7f87de218e84391"}, + {file = "grpcio-1.59.3-cp39-cp39-linux_armv7l.whl", hash = "sha256:c82ca1e4be24a98a253d6dbaa216542e4163f33f38163fc77964b0f0d255b552"}, + {file = "grpcio-1.59.3-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:36636babfda14f9e9687f28d5b66d349cf88c1301154dc71c6513de2b6c88c59"}, + {file = "grpcio-1.59.3-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:5f9b2e591da751ac7fdd316cc25afafb7a626dededa9b414f90faad7f3ccebdb"}, + {file = "grpcio-1.59.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a93a82876a4926bf451db82ceb725bd87f42292bacc94586045261f501a86994"}, + {file = "grpcio-1.59.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce31fa0bfdd1f2bb15b657c16105c8652186eab304eb512e6ae3b99b2fdd7d13"}, + {file = "grpcio-1.59.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:16da0e40573962dab6cba16bec31f25a4f468e6d05b658e589090fe103b03e3d"}, + {file = "grpcio-1.59.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1d1a17372fd425addd5812049fa7374008ffe689585f27f802d0935522cf4b7"}, + {file = "grpcio-1.59.3-cp39-cp39-win32.whl", hash = "sha256:52cc38a7241b5f7b4a91aaf9000fdd38e26bb00d5e8a71665ce40cfcee716281"}, + {file = "grpcio-1.59.3-cp39-cp39-win_amd64.whl", hash = "sha256:b491e5bbcad3020a96842040421e508780cade35baba30f402df9d321d1c423e"}, + {file = "grpcio-1.59.3.tar.gz", hash = "sha256:7800f99568a74a06ebdccd419dd1b6e639b477dcaf6da77ea702f8fb14ce5f80"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.58.0)"] +protobuf = ["grpcio-tools (>=1.59.3)"] [[package]] name = "grpcio-tools" -version = "1.58.0" +version = "1.59.3" description = "Protobuf code generator for gRPC" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "grpcio-tools-1.58.0.tar.gz", hash = "sha256:6f4d80ceb591e31ca4dceec747dbe56132e1392a0a9bb1c8fe001d1b5cac898a"}, - {file = "grpcio_tools-1.58.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:60c874908f3b40f32f1bb0221f7b3ab65ecb53a4d0a9f0a394f031f1b292c177"}, - {file = "grpcio_tools-1.58.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:1852e798f31e5437ca7b37abc910e028b34732fb19364862cedb87b1dab66fad"}, - {file = "grpcio_tools-1.58.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:149fb48f53cb691a6328f68bed8e4036c730f7106b7f98e92c2c0403f0b9e93c"}, - {file = "grpcio_tools-1.58.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba3d383e5ca93826038b70f326fce8e8d12dd9b2f64d363a3d612f7475f12dd2"}, - {file = "grpcio_tools-1.58.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6997511e9d2979f7a2389479682dbb06823f21a904e8fb0a5c6baaf1b4b4a863"}, - {file = "grpcio_tools-1.58.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8de0b701da479643f71fad71fe66885cddd89441ae16e2c724939b47742dc72e"}, - {file = "grpcio_tools-1.58.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:43cc23908b63fcaefe690b10f68a2d8652c994b5b36ab77d2271d9608c895320"}, - {file = "grpcio_tools-1.58.0-cp310-cp310-win32.whl", hash = "sha256:2c2221123d010dc6231799e63a37f2f4786bf614ef65b23009c387cd20d8b193"}, - {file = "grpcio_tools-1.58.0-cp310-cp310-win_amd64.whl", hash = "sha256:df2788736bdf58abe7b0e4d6b1ff806f7686c98c5ad900da312252e3322d91c4"}, - {file = "grpcio_tools-1.58.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:b6ea5578712cdb29b0ff60bfc6405bf0e8d681b9c71d106dd1cda54fe7fe4e55"}, - {file = "grpcio_tools-1.58.0-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c29880f491581c83181c0a84a4d11402af2b13166a5266f64e246adf1da7aa66"}, - {file = "grpcio_tools-1.58.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:32d51e933c3565414dd0835f930bb28a1cdeba435d9d2c87fa3cf8b1d284db3c"}, - {file = "grpcio_tools-1.58.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ad9d77f25514584b1ddc981d70c9e50dfcfc388aa5ba943eee67520c5267ed9"}, - {file = "grpcio_tools-1.58.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4882382631e6352819059278a5c878ce0b067008dd490911d16d5616e8a36d85"}, - {file = "grpcio_tools-1.58.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d84091a189d848d94645b7c48b61734c12ec03b0d46e5fc0049343a26989ac5c"}, - {file = "grpcio_tools-1.58.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:85ac28a9621e9b92a3fc416288c4ce45542db0b4c31b3e23031dd8e0a0ec5590"}, - {file = "grpcio_tools-1.58.0-cp311-cp311-win32.whl", hash = "sha256:7371d8ea80234b29affec145e25569523f549520ed7e53b2aa92bed412cdecfd"}, - {file = "grpcio_tools-1.58.0-cp311-cp311-win_amd64.whl", hash = "sha256:6997df6e7c5cf4d3ddc764240c1ff6a04b45d70ec28913b38fbc6396ef743e12"}, - {file = "grpcio_tools-1.58.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:ac65b8d6e3acaf88b815edf9af88ff844b6600ff3d2591c05ba4f655b45d5fb4"}, - {file = "grpcio_tools-1.58.0-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:88e8191d0dd789bebf42533808728f5ce75d2c51e2a72bdf20abe5b5e3fbec42"}, - {file = "grpcio_tools-1.58.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:a3dbece2a121761499a659b799979d4b738586d1065439053de553773eee11ca"}, - {file = "grpcio_tools-1.58.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1086fe240c4c879b9721952b47d46996deb283c2d9355a8dc24a804811aacf70"}, - {file = "grpcio_tools-1.58.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7ae3dca059d5b358dd03fb63277428fa7d771605d4074a019138dd38d70719a"}, - {file = "grpcio_tools-1.58.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3f8904ac7fc3da2e874f00b3a986e8b7e004f499344a8e7eb213c26dfb025041"}, - {file = "grpcio_tools-1.58.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:aadbd8393ae332e49731adb31e741f2e689989150569b7acc939f5ea43124e2d"}, - {file = "grpcio_tools-1.58.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1cb6e24194786687d4f23c64de1f0ce553af51de22746911bc37340f85f9783e"}, - {file = "grpcio_tools-1.58.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:6ec43909095c630df3e479e77469bdad367067431f4af602f6ccb978a3b78afd"}, - {file = "grpcio_tools-1.58.0-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:4be49ed320b0ebcbc21d19ef555fbf229c1c452105522b728e1171ee2052078e"}, - {file = "grpcio_tools-1.58.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:28eefebddec3d3adf19baca78f8b82a2287d358e1b1575ae018cdca8eacc6269"}, - {file = "grpcio_tools-1.58.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ef8c696e9d78676cc3f583a92bbbf2c84e94e350f7ad22f150a52559f4599d1"}, - {file = "grpcio_tools-1.58.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9aeb5949e46558d21c51fd3ec3eeecc59c94dbca76c67c0a80d3da6b7437930c"}, - {file = "grpcio_tools-1.58.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f7144aad9396d35fb1b80429600a970b559c2ad4d07020eeb180fe83cea2bee"}, - {file = "grpcio_tools-1.58.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4ee26e9253a721fff355737649678535f76cf5d642aa3ac0cd937832559b90af"}, - {file = "grpcio_tools-1.58.0-cp38-cp38-win32.whl", hash = "sha256:343f572312039059a8797d6e29a7fc62196e73131ab01755660a9d48202267c1"}, - {file = "grpcio_tools-1.58.0-cp38-cp38-win_amd64.whl", hash = "sha256:cd7acfbb43b7338a78cf4a67528d05530d574d92b7c829d185b78dfc451d158f"}, - {file = "grpcio_tools-1.58.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:46628247fbce86d18232eead24bd22ed0826c79f3fe2fc2fbdbde45971361049"}, - {file = "grpcio_tools-1.58.0-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:51587842a54e025a3d0d37afcf4ef2b7ac1def9a5d17448665cb424b53d6c287"}, - {file = "grpcio_tools-1.58.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:a062ae3072a2a39a3c057f4d68b57b021f1dd2956cd09aab39709f6af494e1de"}, - {file = "grpcio_tools-1.58.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eec3c93a08df11c80ef1c29a616bcbb0d83dbc6ea41b48306fcacc720416dfa7"}, - {file = "grpcio_tools-1.58.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b63f823ac991ff77104da614d2a2485a59d37d57830eb2e387a6e2a3edc7fa2b"}, - {file = "grpcio_tools-1.58.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:579c11a9f198847ed48dbc4f211c67fe96a73320b87c81f01b044b72e24a7d77"}, - {file = "grpcio_tools-1.58.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6ca2fc1dd8049d417a5034d944c9df05cee76f855b3e431627ab4292e7c01c47"}, - {file = "grpcio_tools-1.58.0-cp39-cp39-win32.whl", hash = "sha256:453023120114c35d3d9d6717ea0820e5d5c140f51f9d0b621de4397ff854471b"}, - {file = "grpcio_tools-1.58.0-cp39-cp39-win_amd64.whl", hash = "sha256:b6c896f1df99c35cf062d4803c15663ff00a33ff09add28baa6e475cf6b5e258"}, + {file = "grpcio-tools-1.59.3.tar.gz", hash = "sha256:cd160ac4281cd1ae77a2c880377a7728349340b4c91e24285037b57c18e9f651"}, + {file = "grpcio_tools-1.59.3-cp310-cp310-linux_armv7l.whl", hash = "sha256:17017fe74734c158e0f93817f1ff17aeda37d0f105ed6e63b12c26b66743a7a8"}, + {file = "grpcio_tools-1.59.3-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:ac1013e4f84ffd15c45ead6d19c9d188b76c14466a799aa9c338ce3b9ebf6dcc"}, + {file = "grpcio_tools-1.59.3-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:0a5d760619305eb51a8719ce9c081398f145a46bc7c86a6e2cebe0648a21f40c"}, + {file = "grpcio_tools-1.59.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de3d9649b7a3091ec785a67d5bf006584440f03896ee52259c6d9ff412d08afb"}, + {file = "grpcio_tools-1.59.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21868aa510317d3f39e5de40208ffb8ab1beb1cbcab333317939b59a9b5db055"}, + {file = "grpcio_tools-1.59.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0b116a888580317e421358f26bfaeec47d6f73079e8a47bad60d1f9f7b30f2a5"}, + {file = "grpcio_tools-1.59.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6bd4a72c27abda191e2360b2b720ada1880aba96a63604a6f9d7c37bb3bbf5c4"}, + {file = "grpcio_tools-1.59.3-cp310-cp310-win32.whl", hash = "sha256:d70cad744e92c7576c226a72998ae8dd59240c942f73798bbde40284eb9eb991"}, + {file = "grpcio_tools-1.59.3-cp310-cp310-win_amd64.whl", hash = "sha256:2b8a4aca0c11f2a8b3bfe103362984bdc427ab762428446ef2e12922fd48ee10"}, + {file = "grpcio_tools-1.59.3-cp311-cp311-linux_armv7l.whl", hash = "sha256:b4418b78408ff56ee70a0b14484c07f5e48c2e6f4fa7be390d486a686d0cd6e4"}, + {file = "grpcio_tools-1.59.3-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:58de83ced4f86458f45288a5f76d9765dc245a9ce4e783a194decccc7e0674ea"}, + {file = "grpcio_tools-1.59.3-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:76b0cdcbcb38722840d3eaff6439ddb4b8f0210c6718553d7b7c911834b10e60"}, + {file = "grpcio_tools-1.59.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0cacf59513b100bfb3d8de51ba43db6140aa9bcb7bba872badb48acb430c002"}, + {file = "grpcio_tools-1.59.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:019fdd986c80b13187574c291df5054f241bdbe87dbc86e4cee73ffa28328647"}, + {file = "grpcio_tools-1.59.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ff304b9d6c23d8e2ecc860bebac1ec6768a2d920985bcea9ce4a7aaeeea44f76"}, + {file = "grpcio_tools-1.59.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ca286affe613beaf2d5a6b8bd83203dcf488917194b416da48aa849047b5f081"}, + {file = "grpcio_tools-1.59.3-cp311-cp311-win32.whl", hash = "sha256:8f69141ff370729ceaad0286b8c6e15352c9bb39aa8f18c0500ce3d0238c2981"}, + {file = "grpcio_tools-1.59.3-cp311-cp311-win_amd64.whl", hash = "sha256:05ec4ffe16b6eab12813476e6d7465a0027bee33999d4776ae1d9c0664d0fc54"}, + {file = "grpcio_tools-1.59.3-cp312-cp312-linux_armv7l.whl", hash = "sha256:21d976419630f72a7cefebe7dcfc451b33d70c805a43ff5a60c43367813f0527"}, + {file = "grpcio_tools-1.59.3-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:396106f92ea6ab2157535e1a009bac99aa15680ca8addbc8e7c1a4d3f5b1fb2c"}, + {file = "grpcio_tools-1.59.3-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:4f064483e0046a4a193d6c67b26ea0f61737e8232ff61636a7fa0bc5244458be"}, + {file = "grpcio_tools-1.59.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6dc6da8e3780df25095c1952f45c334e1554f25b991ffe75dbf0408795d27a0"}, + {file = "grpcio_tools-1.59.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87111be05c1a159ce3fffbfad86ff69fd4bf1702cde127eb698d8e8c3a018ab6"}, + {file = "grpcio_tools-1.59.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:83453a13c2120238eb7fb993b03b370496e76071a7b45c816aa682d9226d29c1"}, + {file = "grpcio_tools-1.59.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4384b29d8e126bc6e24a5efd9d60a2a2015867c7109fa67ff2ed274b3f4a05c5"}, + {file = "grpcio_tools-1.59.3-cp312-cp312-win32.whl", hash = "sha256:ce1372c9acde9d74c7e54858598ac0c5203dd3ec24b9085f7a8b2f33cc156736"}, + {file = "grpcio_tools-1.59.3-cp312-cp312-win_amd64.whl", hash = "sha256:84179e3a7c9067e993700b3255f2adc10e9b16e8dd28525d1dd1a02b9ca603ee"}, + {file = "grpcio_tools-1.59.3-cp37-cp37m-linux_armv7l.whl", hash = "sha256:592208a9a02df75993cc4dba111d2b81f0e6a3f3837856be239e1aceb6651f31"}, + {file = "grpcio_tools-1.59.3-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:1abe30ce770ac4fed966d017771fa7f8ced6a279de7ce68023e2c07f07911e76"}, + {file = "grpcio_tools-1.59.3-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:4dce57668e2aa8c3bb0b2a0bb766a2654ee0f4d8d31e02a6001e98af18569285"}, + {file = "grpcio_tools-1.59.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acaefd3c362250a02cc93fc1b5440e3cb30ab8d7fd81594b2975ea19f123aae3"}, + {file = "grpcio_tools-1.59.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76542e1c11e3f2c22c19cff6b3233caab35924fad1f685ce63184d31b4050fa8"}, + {file = "grpcio_tools-1.59.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:64fd1efb23da054f61aca2346c5139f317b7f4c545f6dbda5ec246f281af8e86"}, + {file = "grpcio_tools-1.59.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ea6454acde508c9a62dab3f59e98b32e32b26aa60df20080982503bb7db51304"}, + {file = "grpcio_tools-1.59.3-cp37-cp37m-win_amd64.whl", hash = "sha256:a048d4bde526f3c6e364abea2c3a481f3bbebc4bfa7fdcfcc3e5ee4f8ab9c4c5"}, + {file = "grpcio_tools-1.59.3-cp38-cp38-linux_armv7l.whl", hash = "sha256:b4db59a62d31c98105af08b1bfb8878c239e4cf31088f2d9864756cdfec67746"}, + {file = "grpcio_tools-1.59.3-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:05ac0f6683759e5508081c09af26cb6cc949c2c54d75ff8b76344709f78dda53"}, + {file = "grpcio_tools-1.59.3-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:a031e1ced828a00f1eb59db5f5d4dd39d3bd6a7df8187f84830d4a32a1bbc686"}, + {file = "grpcio_tools-1.59.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f48b4409b306675b7673dad725c9cf3234bf05623bf8a193ad14af39d0368ba6"}, + {file = "grpcio_tools-1.59.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36524d97cc936767a69815b90be76a1420b3218a7724ce69cde6ece794e72a17"}, + {file = "grpcio_tools-1.59.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7cc7e8b893a6c37a8a28735fede1aa40275988a668d1e22c5f234938a77d811d"}, + {file = "grpcio_tools-1.59.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:917be645a71cf9592d2f5a3589c20c074a6539954017e8e2dca5e8ba13025625"}, + {file = "grpcio_tools-1.59.3-cp38-cp38-win32.whl", hash = "sha256:a1394b7a65d738ee0ce4eac1fc95158dd9c97b5c3f690d259e6ee0bf131697de"}, + {file = "grpcio_tools-1.59.3-cp38-cp38-win_amd64.whl", hash = "sha256:007745bd3c5a788dcb73eeb6cd773613a834bd2442e7d062dcafe46dbd4bb5f6"}, + {file = "grpcio_tools-1.59.3-cp39-cp39-linux_armv7l.whl", hash = "sha256:102b5f14a500dbb766f24a96884d9572a3ea7a56d69695461100fb71ec922ef6"}, + {file = "grpcio_tools-1.59.3-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:46c384a0e30a8422a3e2c5530b3cd69b652dd659549907e2eaac7ca4e0ab614d"}, + {file = "grpcio_tools-1.59.3-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:ee013da4f5a4ef50fdeca372470733bc402677a4dc0023ee94bf42478b5a620d"}, + {file = "grpcio_tools-1.59.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b7883ce3d532c09f29c016fdac107f9a3dc43f9e6b60faf8b91fcba21824269"}, + {file = "grpcio_tools-1.59.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2861e4814ebc147854c2246092c433931f4c15f3c8105ae8716b1e282313a5ae"}, + {file = "grpcio_tools-1.59.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d93590a6a82469f3e58e39692230d99c43a39b215cb581e072dcd52286471152"}, + {file = "grpcio_tools-1.59.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f8396183e6e0a16178773963dc21b58c0c532783476fda314198a9e42f57af7d"}, + {file = "grpcio_tools-1.59.3-cp39-cp39-win32.whl", hash = "sha256:6747b1d82d08e0f5e1a6438532343a1c5504147d1a199c5756e702e5f916de4c"}, + {file = "grpcio_tools-1.59.3-cp39-cp39-win_amd64.whl", hash = "sha256:3a560dcb176dd42c37af5d37299e318341a572547e32b942247daa834d2164c0"}, ] [package.dependencies] -grpcio = ">=1.58.0" +grpcio = ">=1.59.3" protobuf = ">=4.21.6,<5.0dev" setuptools = "*" @@ -1050,7 +926,6 @@ setuptools = "*" name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1062,7 +937,6 @@ files = [ name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1074,7 +948,6 @@ files = [ name = "invoke" version = "2.2.0" description = "Pythonic task execution" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1086,7 +959,6 @@ files = [ name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1104,7 +976,6 @@ i18n = ["Babel (>=2.7)"] name = "markupsafe" version = "2.1.3" description = "Safely add untrusted strings to HTML/XML markup." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1128,6 +999,16 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -1164,7 +1045,6 @@ files = [ name = "mccabe" version = "0.7.0" description = "McCabe checker, plugin for flake8" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1176,7 +1056,6 @@ files = [ name = "multidict" version = "6.0.4" description = "multidict implementation" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1260,7 +1139,6 @@ files = [ name = "netifaces" version = "0.11.0" description = "Portable network interface information." -category = "main" optional = false python-versions = "*" files = [ @@ -1298,21 +1176,19 @@ files = [ [[package]] name = "packaging" -version = "23.1" +version = "23.2" description = "Core utilities for Python packages" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] [[package]] name = "paho-mqtt" version = "1.6.1" description = "MQTT version 5.0/3.1.1 client class" -category = "main" optional = false python-versions = "*" files = [ @@ -1326,7 +1202,6 @@ proxy = ["PySocks"] name = "pint" version = "0.22" description = "Physical quantities module" -category = "main" optional = false python-versions = ">=3.9" files = [ @@ -1351,7 +1226,6 @@ xarray = ["xarray"] name = "pluggy" version = "1.3.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1365,44 +1239,39 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "protobuf" -version = "4.24.3" +version = "4.25.1" description = "" -category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "protobuf-4.24.3-cp310-abi3-win32.whl", hash = "sha256:20651f11b6adc70c0f29efbe8f4a94a74caf61b6200472a9aea6e19898f9fcf4"}, - {file = "protobuf-4.24.3-cp310-abi3-win_amd64.whl", hash = "sha256:3d42e9e4796a811478c783ef63dc85b5a104b44aaaca85d4864d5b886e4b05e3"}, - {file = "protobuf-4.24.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:6e514e8af0045be2b56e56ae1bb14f43ce7ffa0f68b1c793670ccbe2c4fc7d2b"}, - {file = "protobuf-4.24.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:ba53c2f04798a326774f0e53b9c759eaef4f6a568ea7072ec6629851c8435959"}, - {file = "protobuf-4.24.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:f6ccbcf027761a2978c1406070c3788f6de4a4b2cc20800cc03d52df716ad675"}, - {file = "protobuf-4.24.3-cp37-cp37m-win32.whl", hash = "sha256:1b182c7181a2891e8f7f3a1b5242e4ec54d1f42582485a896e4de81aa17540c2"}, - {file = "protobuf-4.24.3-cp37-cp37m-win_amd64.whl", hash = "sha256:b0271a701e6782880d65a308ba42bc43874dabd1a0a0f41f72d2dac3b57f8e76"}, - {file = "protobuf-4.24.3-cp38-cp38-win32.whl", hash = "sha256:e29d79c913f17a60cf17c626f1041e5288e9885c8579832580209de8b75f2a52"}, - {file = "protobuf-4.24.3-cp38-cp38-win_amd64.whl", hash = "sha256:067f750169bc644da2e1ef18c785e85071b7c296f14ac53e0900e605da588719"}, - {file = "protobuf-4.24.3-cp39-cp39-win32.whl", hash = "sha256:2da777d34b4f4f7613cdf85c70eb9a90b1fbef9d36ae4a0ccfe014b0b07906f1"}, - {file = "protobuf-4.24.3-cp39-cp39-win_amd64.whl", hash = "sha256:f631bb982c5478e0c1c70eab383af74a84be66945ebf5dd6b06fc90079668d0b"}, - {file = "protobuf-4.24.3-py3-none-any.whl", hash = "sha256:f6f8dc65625dadaad0c8545319c2e2f0424fede988368893ca3844261342c11a"}, - {file = "protobuf-4.24.3.tar.gz", hash = "sha256:12e9ad2ec079b833176d2921be2cb24281fa591f0b119b208b788adc48c2561d"}, + {file = "protobuf-4.25.1-cp310-abi3-win32.whl", hash = "sha256:193f50a6ab78a970c9b4f148e7c750cfde64f59815e86f686c22e26b4fe01ce7"}, + {file = "protobuf-4.25.1-cp310-abi3-win_amd64.whl", hash = "sha256:3497c1af9f2526962f09329fd61a36566305e6c72da2590ae0d7d1322818843b"}, + {file = "protobuf-4.25.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:0bf384e75b92c42830c0a679b0cd4d6e2b36ae0cf3dbb1e1dfdda48a244f4bcd"}, + {file = "protobuf-4.25.1-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:0f881b589ff449bf0b931a711926e9ddaad3b35089cc039ce1af50b21a4ae8cb"}, + {file = "protobuf-4.25.1-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:ca37bf6a6d0046272c152eea90d2e4ef34593aaa32e8873fc14c16440f22d4b7"}, + {file = "protobuf-4.25.1-cp38-cp38-win32.whl", hash = "sha256:abc0525ae2689a8000837729eef7883b9391cd6aa7950249dcf5a4ede230d5dd"}, + {file = "protobuf-4.25.1-cp38-cp38-win_amd64.whl", hash = "sha256:1484f9e692091450e7edf418c939e15bfc8fc68856e36ce399aed6889dae8bb0"}, + {file = "protobuf-4.25.1-cp39-cp39-win32.whl", hash = "sha256:8bdbeaddaac52d15c6dce38c71b03038ef7772b977847eb6d374fc86636fa510"}, + {file = "protobuf-4.25.1-cp39-cp39-win_amd64.whl", hash = "sha256:becc576b7e6b553d22cbdf418686ee4daa443d7217999125c045ad56322dda10"}, + {file = "protobuf-4.25.1-py3-none-any.whl", hash = "sha256:a19731d5e83ae4737bb2a089605e636077ac001d18781b3cf489b9546c7c80d6"}, + {file = "protobuf-4.25.1.tar.gz", hash = "sha256:57d65074b4f5baa4ab5da1605c02be90ac20c8b40fb137d6a8df9f416b0d0ce2"}, ] [[package]] name = "pycodestyle" -version = "2.11.0" +version = "2.11.1" description = "Python style guide checker" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "pycodestyle-2.11.0-py2.py3-none-any.whl", hash = "sha256:5d1013ba8dc7895b548be5afb05740ca82454fd899971563d2ef625d090326f8"}, - {file = "pycodestyle-2.11.0.tar.gz", hash = "sha256:259bcc17857d8a8b3b4a2327324b79e5f020a13c16074670f9c8c8f872ea76d0"}, + {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, + {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, ] [[package]] name = "pycparser" version = "2.21" description = "C parser in Python" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1412,48 +1281,47 @@ files = [ [[package]] name = "pydantic" -version = "1.10.12" +version = "1.10.13" description = "Data validation and settings management using python type hints" -category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-1.10.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a1fcb59f2f355ec350073af41d927bf83a63b50e640f4dbaa01053a28b7a7718"}, - {file = "pydantic-1.10.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b7ccf02d7eb340b216ec33e53a3a629856afe1c6e0ef91d84a4e6f2fb2ca70fe"}, - {file = "pydantic-1.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fb2aa3ab3728d950bcc885a2e9eff6c8fc40bc0b7bb434e555c215491bcf48b"}, - {file = "pydantic-1.10.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:771735dc43cf8383959dc9b90aa281f0b6092321ca98677c5fb6125a6f56d58d"}, - {file = "pydantic-1.10.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ca48477862372ac3770969b9d75f1bf66131d386dba79506c46d75e6b48c1e09"}, - {file = "pydantic-1.10.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a5e7add47a5b5a40c49b3036d464e3c7802f8ae0d1e66035ea16aa5b7a3923ed"}, - {file = "pydantic-1.10.12-cp310-cp310-win_amd64.whl", hash = "sha256:e4129b528c6baa99a429f97ce733fff478ec955513630e61b49804b6cf9b224a"}, - {file = "pydantic-1.10.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b0d191db0f92dfcb1dec210ca244fdae5cbe918c6050b342d619c09d31eea0cc"}, - {file = "pydantic-1.10.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:795e34e6cc065f8f498c89b894a3c6da294a936ee71e644e4bd44de048af1405"}, - {file = "pydantic-1.10.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69328e15cfda2c392da4e713443c7dbffa1505bc9d566e71e55abe14c97ddc62"}, - {file = "pydantic-1.10.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2031de0967c279df0d8a1c72b4ffc411ecd06bac607a212892757db7462fc494"}, - {file = "pydantic-1.10.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ba5b2e6fe6ca2b7e013398bc7d7b170e21cce322d266ffcd57cca313e54fb246"}, - {file = "pydantic-1.10.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2a7bac939fa326db1ab741c9d7f44c565a1d1e80908b3797f7f81a4f86bc8d33"}, - {file = "pydantic-1.10.12-cp311-cp311-win_amd64.whl", hash = "sha256:87afda5539d5140cb8ba9e8b8c8865cb5b1463924d38490d73d3ccfd80896b3f"}, - {file = "pydantic-1.10.12-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:549a8e3d81df0a85226963611950b12d2d334f214436a19537b2efed61b7639a"}, - {file = "pydantic-1.10.12-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:598da88dfa127b666852bef6d0d796573a8cf5009ffd62104094a4fe39599565"}, - {file = "pydantic-1.10.12-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba5c4a8552bff16c61882db58544116d021d0b31ee7c66958d14cf386a5b5350"}, - {file = "pydantic-1.10.12-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c79e6a11a07da7374f46970410b41d5e266f7f38f6a17a9c4823db80dadf4303"}, - {file = "pydantic-1.10.12-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab26038b8375581dc832a63c948f261ae0aa21f1d34c1293469f135fa92972a5"}, - {file = "pydantic-1.10.12-cp37-cp37m-win_amd64.whl", hash = "sha256:e0a16d274b588767602b7646fa05af2782576a6cf1022f4ba74cbb4db66f6ca8"}, - {file = "pydantic-1.10.12-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6a9dfa722316f4acf4460afdf5d41d5246a80e249c7ff475c43a3a1e9d75cf62"}, - {file = "pydantic-1.10.12-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a73f489aebd0c2121ed974054cb2759af8a9f747de120acd2c3394cf84176ccb"}, - {file = "pydantic-1.10.12-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b30bcb8cbfccfcf02acb8f1a261143fab622831d9c0989707e0e659f77a18e0"}, - {file = "pydantic-1.10.12-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fcfb5296d7877af406ba1547dfde9943b1256d8928732267e2653c26938cd9c"}, - {file = "pydantic-1.10.12-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2f9a6fab5f82ada41d56b0602606a5506aab165ca54e52bc4545028382ef1c5d"}, - {file = "pydantic-1.10.12-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dea7adcc33d5d105896401a1f37d56b47d443a2b2605ff8a969a0ed5543f7e33"}, - {file = "pydantic-1.10.12-cp38-cp38-win_amd64.whl", hash = "sha256:1eb2085c13bce1612da8537b2d90f549c8cbb05c67e8f22854e201bde5d98a47"}, - {file = "pydantic-1.10.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ef6c96b2baa2100ec91a4b428f80d8f28a3c9e53568219b6c298c1125572ebc6"}, - {file = "pydantic-1.10.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6c076be61cd0177a8433c0adcb03475baf4ee91edf5a4e550161ad57fc90f523"}, - {file = "pydantic-1.10.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d5a58feb9a39f481eda4d5ca220aa8b9d4f21a41274760b9bc66bfd72595b86"}, - {file = "pydantic-1.10.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5f805d2d5d0a41633651a73fa4ecdd0b3d7a49de4ec3fadf062fe16501ddbf1"}, - {file = "pydantic-1.10.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1289c180abd4bd4555bb927c42ee42abc3aee02b0fb2d1223fb7c6e5bef87dbe"}, - {file = "pydantic-1.10.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5d1197e462e0364906cbc19681605cb7c036f2475c899b6f296104ad42b9f5fb"}, - {file = "pydantic-1.10.12-cp39-cp39-win_amd64.whl", hash = "sha256:fdbdd1d630195689f325c9ef1a12900524dceb503b00a987663ff4f58669b93d"}, - {file = "pydantic-1.10.12-py3-none-any.whl", hash = "sha256:b749a43aa51e32839c9d71dc67eb1e4221bb04af1033a32e3923d46f9effa942"}, - {file = "pydantic-1.10.12.tar.gz", hash = "sha256:0fe8a415cea8f340e7a9af9c54fc71a649b43e8ca3cc732986116b3cb135d303"}, + {file = "pydantic-1.10.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:efff03cc7a4f29d9009d1c96ceb1e7a70a65cfe86e89d34e4a5f2ab1e5693737"}, + {file = "pydantic-1.10.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ecea2b9d80e5333303eeb77e180b90e95eea8f765d08c3d278cd56b00345d01"}, + {file = "pydantic-1.10.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1740068fd8e2ef6eb27a20e5651df000978edce6da6803c2bef0bc74540f9548"}, + {file = "pydantic-1.10.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84bafe2e60b5e78bc64a2941b4c071a4b7404c5c907f5f5a99b0139781e69ed8"}, + {file = "pydantic-1.10.13-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bc0898c12f8e9c97f6cd44c0ed70d55749eaf783716896960b4ecce2edfd2d69"}, + {file = "pydantic-1.10.13-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:654db58ae399fe6434e55325a2c3e959836bd17a6f6a0b6ca8107ea0571d2e17"}, + {file = "pydantic-1.10.13-cp310-cp310-win_amd64.whl", hash = "sha256:75ac15385a3534d887a99c713aa3da88a30fbd6204a5cd0dc4dab3d770b9bd2f"}, + {file = "pydantic-1.10.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c553f6a156deb868ba38a23cf0df886c63492e9257f60a79c0fd8e7173537653"}, + {file = "pydantic-1.10.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5e08865bc6464df8c7d61439ef4439829e3ab62ab1669cddea8dd00cd74b9ffe"}, + {file = "pydantic-1.10.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e31647d85a2013d926ce60b84f9dd5300d44535a9941fe825dc349ae1f760df9"}, + {file = "pydantic-1.10.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:210ce042e8f6f7c01168b2d84d4c9eb2b009fe7bf572c2266e235edf14bacd80"}, + {file = "pydantic-1.10.13-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8ae5dd6b721459bfa30805f4c25880e0dd78fc5b5879f9f7a692196ddcb5a580"}, + {file = "pydantic-1.10.13-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f8e81fc5fb17dae698f52bdd1c4f18b6ca674d7068242b2aff075f588301bbb0"}, + {file = "pydantic-1.10.13-cp311-cp311-win_amd64.whl", hash = "sha256:61d9dce220447fb74f45e73d7ff3b530e25db30192ad8d425166d43c5deb6df0"}, + {file = "pydantic-1.10.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4b03e42ec20286f052490423682016fd80fda830d8e4119f8ab13ec7464c0132"}, + {file = "pydantic-1.10.13-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f59ef915cac80275245824e9d771ee939133be38215555e9dc90c6cb148aaeb5"}, + {file = "pydantic-1.10.13-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a1f9f747851338933942db7af7b6ee8268568ef2ed86c4185c6ef4402e80ba8"}, + {file = "pydantic-1.10.13-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:97cce3ae7341f7620a0ba5ef6cf043975cd9d2b81f3aa5f4ea37928269bc1b87"}, + {file = "pydantic-1.10.13-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:854223752ba81e3abf663d685f105c64150873cc6f5d0c01d3e3220bcff7d36f"}, + {file = "pydantic-1.10.13-cp37-cp37m-win_amd64.whl", hash = "sha256:b97c1fac8c49be29486df85968682b0afa77e1b809aff74b83081cc115e52f33"}, + {file = "pydantic-1.10.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c958d053453a1c4b1c2062b05cd42d9d5c8eb67537b8d5a7e3c3032943ecd261"}, + {file = "pydantic-1.10.13-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4c5370a7edaac06daee3af1c8b1192e305bc102abcbf2a92374b5bc793818599"}, + {file = "pydantic-1.10.13-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d6f6e7305244bddb4414ba7094ce910560c907bdfa3501e9db1a7fd7eaea127"}, + {file = "pydantic-1.10.13-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3a3c792a58e1622667a2837512099eac62490cdfd63bd407993aaf200a4cf1f"}, + {file = "pydantic-1.10.13-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c636925f38b8db208e09d344c7aa4f29a86bb9947495dd6b6d376ad10334fb78"}, + {file = "pydantic-1.10.13-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:678bcf5591b63cc917100dc50ab6caebe597ac67e8c9ccb75e698f66038ea953"}, + {file = "pydantic-1.10.13-cp38-cp38-win_amd64.whl", hash = "sha256:6cf25c1a65c27923a17b3da28a0bdb99f62ee04230c931d83e888012851f4e7f"}, + {file = "pydantic-1.10.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8ef467901d7a41fa0ca6db9ae3ec0021e3f657ce2c208e98cd511f3161c762c6"}, + {file = "pydantic-1.10.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:968ac42970f57b8344ee08837b62f6ee6f53c33f603547a55571c954a4225691"}, + {file = "pydantic-1.10.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9849f031cf8a2f0a928fe885e5a04b08006d6d41876b8bbd2fc68a18f9f2e3fd"}, + {file = "pydantic-1.10.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56e3ff861c3b9c6857579de282ce8baabf443f42ffba355bf070770ed63e11e1"}, + {file = "pydantic-1.10.13-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f00790179497767aae6bcdc36355792c79e7bbb20b145ff449700eb076c5f96"}, + {file = "pydantic-1.10.13-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:75b297827b59bc229cac1a23a2f7a4ac0031068e5be0ce385be1462e7e17a35d"}, + {file = "pydantic-1.10.13-cp39-cp39-win_amd64.whl", hash = "sha256:e70ca129d2053fb8b728ee7d1af8e553a928d7e301a311094b8a0501adc8763d"}, + {file = "pydantic-1.10.13-py3-none-any.whl", hash = "sha256:b87326822e71bd5f313e7d3bfdc77ac3247035ac10b0c0618bd99dcf95b1e687"}, + {file = "pydantic-1.10.13.tar.gz", hash = "sha256:32c8b48dcd3b2ac4e78b0ba4af3a2c2eb6048cb75202f0ea7b34feb740efc340"}, ] [package.dependencies] @@ -1467,7 +1335,6 @@ email = ["email-validator (>=1.0.3)"] name = "pyflakes" version = "3.1.0" description = "passive checker of Python programs" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1479,7 +1346,6 @@ files = [ name = "pyserial" version = "3.5" description = "Python Serial Port Extension" -category = "main" optional = false python-versions = "*" files = [ @@ -1494,7 +1360,6 @@ cp2110 = ["hidapi"] name = "pyserial-asyncio" version = "0.6" description = "Python Serial Port Extension - Asynchronous I/O support" -category = "main" optional = false python-versions = "*" files = [ @@ -1507,23 +1372,20 @@ pyserial = "*" [[package]] name = "pytest" -version = "7.4.2" +version = "7.4.3" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, - {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, + {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, + {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] @@ -1532,7 +1394,6 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "pytest-aiohttp" version = "1.0.5" description = "Pytest plugin for aiohttp support" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1552,7 +1413,6 @@ testing = ["coverage (==6.2)", "mypy (==0.931)"] name = "pytest-asyncio" version = "0.21.1" description = "Pytest support for asyncio" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1571,7 +1431,6 @@ testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy name = "pytest-cov" version = "4.1.0" description = "Pytest plugin for measuring coverage." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1588,14 +1447,13 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale [[package]] name = "pytest-mock" -version = "3.11.1" +version = "3.12.0" description = "Thin-wrapper around the mock package for easier use with pytest" -category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest-mock-3.11.1.tar.gz", hash = "sha256:7f6b125602ac6d743e523ae0bfa71e1a697a2f5534064528c6ff84c2f7c2fc7f"}, - {file = "pytest_mock-3.11.1-py3-none-any.whl", hash = "sha256:21c279fff83d70763b05f8874cc9cfb3fcacd6d354247a976f9529d19f9acf39"}, + {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, + {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, ] [package.dependencies] @@ -1608,7 +1466,6 @@ dev = ["pre-commit", "pytest-asyncio", "tox"] name = "pytimeparse" version = "1.1.8" description = "Time expression parser" -category = "main" optional = false python-versions = "*" files = [ @@ -1620,7 +1477,6 @@ files = [ name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1680,7 +1536,6 @@ files = [ name = "reedsolo" version = "1.7.0" description = "Pure-Python Reed Solomon encoder/decoder" -category = "main" optional = false python-versions = "*" files = [ @@ -1690,26 +1545,24 @@ files = [ [[package]] name = "setuptools" -version = "68.2.0" +version = "69.0.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-68.2.0-py3-none-any.whl", hash = "sha256:af3d5949030c3f493f550876b2fd1dd5ec66689c4ee5d5344f009746f71fd5a8"}, - {file = "setuptools-68.2.0.tar.gz", hash = "sha256:00478ca80aeebeecb2f288d3206b0de568df5cd2b8fada1209843cc9a8d88a48"}, + {file = "setuptools-69.0.2-py3-none-any.whl", hash = "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2"}, + {file = "setuptools-69.0.2.tar.gz", hash = "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1719,125 +1572,126 @@ files = [ [[package]] name = "swagger-ui-bundle" -version = "0.0.9" -description = "swagger_ui_bundle - swagger-ui files in a pip package" -category = "main" +version = "1.1.0" +description = "Swagger UI bundled for usage with Python" optional = false -python-versions = "*" +python-versions = ">=3.7" files = [ - {file = "swagger_ui_bundle-0.0.9-py3-none-any.whl", hash = "sha256:cea116ed81147c345001027325c1ddc9ca78c1ee7319935c3c75d3669279d575"}, - {file = "swagger_ui_bundle-0.0.9.tar.gz", hash = "sha256:b462aa1460261796ab78fd4663961a7f6f347ce01760f1303bbbdf630f11f516"}, + {file = "swagger_ui_bundle-1.1.0-py3-none-any.whl", hash = "sha256:f7526f7bb99923e10594c54247265839bec97e96b0438561ac86faf40d40dd57"}, + {file = "swagger_ui_bundle-1.1.0.tar.gz", hash = "sha256:20673c3431c8733d5d1615ecf79d9acf30cff75202acaf21a7d9c7f489714529"}, ] [package.dependencies] -Jinja2 = ">=2.0" - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] +Jinja2 = ">=3.0.0,<4.0.0" [[package]] name = "typing-extensions" -version = "4.7.1" -description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" +version = "4.8.0" +description = "Backported and Experimental Type Hints for Python 3.8+" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, - {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, + {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, + {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, ] [[package]] name = "yarl" -version = "1.9.2" +version = "1.9.3" description = "Yet another URL library" -category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82"}, - {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8"}, - {file = "yarl-1.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528"}, - {file = "yarl-1.9.2-cp310-cp310-win32.whl", hash = "sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3"}, - {file = "yarl-1.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde"}, - {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6"}, - {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb"}, - {file = "yarl-1.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a"}, - {file = "yarl-1.9.2-cp311-cp311-win32.whl", hash = "sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8"}, - {file = "yarl-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051"}, - {file = "yarl-1.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38a3928ae37558bc1b559f67410df446d1fbfa87318b124bf5032c31e3447b74"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac9bb4c5ce3975aeac288cfcb5061ce60e0d14d92209e780c93954076c7c4367"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3da8a678ca8b96c8606bbb8bfacd99a12ad5dd288bc6f7979baddd62f71c63ef"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13414591ff516e04fcdee8dc051c13fd3db13b673c7a4cb1350e6b2ad9639ad3"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf74d08542c3a9ea97bb8f343d4fcbd4d8f91bba5ec9d5d7f792dbe727f88938"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e7221580dc1db478464cfeef9b03b95c5852cc22894e418562997df0d074ccc"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:494053246b119b041960ddcd20fd76224149cfea8ed8777b687358727911dd33"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:52a25809fcbecfc63ac9ba0c0fb586f90837f5425edfd1ec9f3372b119585e45"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:e65610c5792870d45d7b68c677681376fcf9cc1c289f23e8e8b39c1485384185"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:1b1bba902cba32cdec51fca038fd53f8beee88b77efc373968d1ed021024cc04"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:662e6016409828ee910f5d9602a2729a8a57d74b163c89a837de3fea050c7582"}, - {file = "yarl-1.9.2-cp37-cp37m-win32.whl", hash = "sha256:f364d3480bffd3aa566e886587eaca7c8c04d74f6e8933f3f2c996b7f09bee1b"}, - {file = "yarl-1.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6a5883464143ab3ae9ba68daae8e7c5c95b969462bbe42e2464d60e7e2698368"}, - {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5610f80cf43b6202e2c33ba3ec2ee0a2884f8f423c8f4f62906731d876ef4fac"}, - {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9a4e67ad7b646cd6f0938c7ebfd60e481b7410f574c560e455e938d2da8e0f4"}, - {file = "yarl-1.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:83fcc480d7549ccebe9415d96d9263e2d4226798c37ebd18c930fce43dfb9574"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fcd436ea16fee7d4207c045b1e340020e58a2597301cfbcfdbe5abd2356c2fb"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84e0b1599334b1e1478db01b756e55937d4614f8654311eb26012091be109d59"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3458a24e4ea3fd8930e934c129b676c27452e4ebda80fbe47b56d8c6c7a63a9e"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:838162460b3a08987546e881a2bfa573960bb559dfa739e7800ceeec92e64417"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de119f56f3c5f0e2fb4dee508531a32b069a5f2c6e827b272d1e0ff5ac040333"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:149ddea5abf329752ea5051b61bd6c1d979e13fbf122d3a1f9f0c8be6cb6f63c"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:674ca19cbee4a82c9f54e0d1eee28116e63bc6fd1e96c43031d11cbab8b2afd5"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:9b3152f2f5677b997ae6c804b73da05a39daa6a9e85a512e0e6823d81cdad7cc"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5415d5a4b080dc9612b1b63cba008db84e908b95848369aa1da3686ae27b6d2b"}, - {file = "yarl-1.9.2-cp38-cp38-win32.whl", hash = "sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7"}, - {file = "yarl-1.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:63c48f6cef34e6319a74c727376e95626f84ea091f92c0250a98e53e62c77c72"}, - {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9"}, - {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8"}, - {file = "yarl-1.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80"}, - {file = "yarl-1.9.2-cp39-cp39-win32.whl", hash = "sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623"}, - {file = "yarl-1.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18"}, - {file = "yarl-1.9.2.tar.gz", hash = "sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571"}, + {file = "yarl-1.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32435d134414e01d937cd9d6cc56e8413a8d4741dea36af5840c7750f04d16ab"}, + {file = "yarl-1.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9a5211de242754b5e612557bca701f39f8b1a9408dff73c6db623f22d20f470e"}, + {file = "yarl-1.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:525cd69eff44833b01f8ef39aa33a9cc53a99ff7f9d76a6ef6a9fb758f54d0ff"}, + {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc94441bcf9cb8c59f51f23193316afefbf3ff858460cb47b5758bf66a14d130"}, + {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e36021db54b8a0475805acc1d6c4bca5d9f52c3825ad29ae2d398a9d530ddb88"}, + {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0f17d1df951336a02afc8270c03c0c6e60d1f9996fcbd43a4ce6be81de0bd9d"}, + {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5f3faeb8100a43adf3e7925d556801d14b5816a0ac9e75e22948e787feec642"}, + {file = "yarl-1.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aed37db837ecb5962469fad448aaae0f0ee94ffce2062cf2eb9aed13328b5196"}, + {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:721ee3fc292f0d069a04016ef2c3a25595d48c5b8ddc6029be46f6158d129c92"}, + {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b8bc5b87a65a4e64bc83385c05145ea901b613d0d3a434d434b55511b6ab0067"}, + {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:dd952b9c64f3b21aedd09b8fe958e4931864dba69926d8a90c90d36ac4e28c9a"}, + {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:c405d482c320a88ab53dcbd98d6d6f32ada074f2d965d6e9bf2d823158fa97de"}, + {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9df9a0d4c5624790a0dea2e02e3b1b3c69aed14bcb8650e19606d9df3719e87d"}, + {file = "yarl-1.9.3-cp310-cp310-win32.whl", hash = "sha256:d34c4f80956227f2686ddea5b3585e109c2733e2d4ef12eb1b8b4e84f09a2ab6"}, + {file = "yarl-1.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:cf7a4e8de7f1092829caef66fd90eaf3710bc5efd322a816d5677b7664893c93"}, + {file = "yarl-1.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d61a0ca95503867d4d627517bcfdc28a8468c3f1b0b06c626f30dd759d3999fd"}, + {file = "yarl-1.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73cc83f918b69110813a7d95024266072d987b903a623ecae673d1e71579d566"}, + {file = "yarl-1.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d81657b23e0edb84b37167e98aefb04ae16cbc5352770057893bd222cdc6e45f"}, + {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a1a8443091c7fbc17b84a0d9f38de34b8423b459fb853e6c8cdfab0eacf613"}, + {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe34befb8c765b8ce562f0200afda3578f8abb159c76de3ab354c80b72244c41"}, + {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c757f64afe53a422e45e3e399e1e3cf82b7a2f244796ce80d8ca53e16a49b9f"}, + {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72a57b41a0920b9a220125081c1e191b88a4cdec13bf9d0649e382a822705c65"}, + {file = "yarl-1.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:632c7aeb99df718765adf58eacb9acb9cbc555e075da849c1378ef4d18bf536a"}, + {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b0b8c06afcf2bac5a50b37f64efbde978b7f9dc88842ce9729c020dc71fae4ce"}, + {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1d93461e2cf76c4796355494f15ffcb50a3c198cc2d601ad8d6a96219a10c363"}, + {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:4003f380dac50328c85e85416aca6985536812c082387255c35292cb4b41707e"}, + {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4d6d74a97e898c1c2df80339aa423234ad9ea2052f66366cef1e80448798c13d"}, + {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b61e64b06c3640feab73fa4ff9cb64bd8182de52e5dc13038e01cfe674ebc321"}, + {file = "yarl-1.9.3-cp311-cp311-win32.whl", hash = "sha256:29beac86f33d6c7ab1d79bd0213aa7aed2d2f555386856bb3056d5fdd9dab279"}, + {file = "yarl-1.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:f7271d6bd8838c49ba8ae647fc06469137e1c161a7ef97d778b72904d9b68696"}, + {file = "yarl-1.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:dd318e6b75ca80bff0b22b302f83a8ee41c62b8ac662ddb49f67ec97e799885d"}, + {file = "yarl-1.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c4b1efb11a8acd13246ffb0bee888dd0e8eb057f8bf30112e3e21e421eb82d4a"}, + {file = "yarl-1.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c6f034386e5550b5dc8ded90b5e2ff7db21f0f5c7de37b6efc5dac046eb19c10"}, + {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd49a908cb6d387fc26acee8b7d9fcc9bbf8e1aca890c0b2fdfd706057546080"}, + {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa4643635f26052401750bd54db911b6342eb1a9ac3e74f0f8b58a25d61dfe41"}, + {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e741bd48e6a417bdfbae02e088f60018286d6c141639359fb8df017a3b69415a"}, + {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c86d0d0919952d05df880a1889a4f0aeb6868e98961c090e335671dea5c0361"}, + {file = "yarl-1.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d5434b34100b504aabae75f0622ebb85defffe7b64ad8f52b8b30ec6ef6e4b9"}, + {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79e1df60f7c2b148722fb6cafebffe1acd95fd8b5fd77795f56247edaf326752"}, + {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:44e91a669c43f03964f672c5a234ae0d7a4d49c9b85d1baa93dec28afa28ffbd"}, + {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3cfa4dbe17b2e6fca1414e9c3bcc216f6930cb18ea7646e7d0d52792ac196808"}, + {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:88d2c3cc4b2f46d1ba73d81c51ec0e486f59cc51165ea4f789677f91a303a9a7"}, + {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cccdc02e46d2bd7cb5f38f8cc3d9db0d24951abd082b2f242c9e9f59c0ab2af3"}, + {file = "yarl-1.9.3-cp312-cp312-win32.whl", hash = "sha256:96758e56dceb8a70f8a5cff1e452daaeff07d1cc9f11e9b0c951330f0a2396a7"}, + {file = "yarl-1.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:c4472fe53ebf541113e533971bd8c32728debc4c6d8cc177f2bff31d011ec17e"}, + {file = "yarl-1.9.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:126638ab961633f0940a06e1c9d59919003ef212a15869708dcb7305f91a6732"}, + {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c99ddaddb2fbe04953b84d1651149a0d85214780e4d0ee824e610ab549d98d92"}, + {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dab30b21bd6fb17c3f4684868c7e6a9e8468078db00f599fb1c14e324b10fca"}, + {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:828235a2a169160ee73a2fcfb8a000709edf09d7511fccf203465c3d5acc59e4"}, + {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc391e3941045fd0987c77484b2799adffd08e4b6735c4ee5f054366a2e1551d"}, + {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51382c72dd5377861b573bd55dcf680df54cea84147c8648b15ac507fbef984d"}, + {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:28a108cb92ce6cf867690a962372996ca332d8cda0210c5ad487fe996e76b8bb"}, + {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8f18a7832ff85dfcd77871fe677b169b1bc60c021978c90c3bb14f727596e0ae"}, + {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:7eaf13af79950142ab2bbb8362f8d8d935be9aaf8df1df89c86c3231e4ff238a"}, + {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:66a6dbf6ca7d2db03cc61cafe1ee6be838ce0fbc97781881a22a58a7c5efef42"}, + {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1a0a4f3aaa18580038cfa52a7183c8ffbbe7d727fe581300817efc1e96d1b0e9"}, + {file = "yarl-1.9.3-cp37-cp37m-win32.whl", hash = "sha256:946db4511b2d815979d733ac6a961f47e20a29c297be0d55b6d4b77ee4b298f6"}, + {file = "yarl-1.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2dad8166d41ebd1f76ce107cf6a31e39801aee3844a54a90af23278b072f1ccf"}, + {file = "yarl-1.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bb72d2a94481e7dc7a0c522673db288f31849800d6ce2435317376a345728225"}, + {file = "yarl-1.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9a172c3d5447b7da1680a1a2d6ecdf6f87a319d21d52729f45ec938a7006d5d8"}, + {file = "yarl-1.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2dc72e891672343b99db6d497024bf8b985537ad6c393359dc5227ef653b2f17"}, + {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8d51817cf4b8d545963ec65ff06c1b92e5765aa98831678d0e2240b6e9fd281"}, + {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53ec65f7eee8655bebb1f6f1607760d123c3c115a324b443df4f916383482a67"}, + {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cfd77e8e5cafba3fb584e0f4b935a59216f352b73d4987be3af51f43a862c403"}, + {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e73db54c967eb75037c178a54445c5a4e7461b5203b27c45ef656a81787c0c1b"}, + {file = "yarl-1.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09c19e5f4404574fcfb736efecf75844ffe8610606f3fccc35a1515b8b6712c4"}, + {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6280353940f7e5e2efaaabd686193e61351e966cc02f401761c4d87f48c89ea4"}, + {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c25ec06e4241e162f5d1f57c370f4078797ade95c9208bd0c60f484834f09c96"}, + {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:7217234b10c64b52cc39a8d82550342ae2e45be34f5bff02b890b8c452eb48d7"}, + {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4ce77d289f8d40905c054b63f29851ecbfd026ef4ba5c371a158cfe6f623663e"}, + {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5f74b015c99a5eac5ae589de27a1201418a5d9d460e89ccb3366015c6153e60a"}, + {file = "yarl-1.9.3-cp38-cp38-win32.whl", hash = "sha256:8a2538806be846ea25e90c28786136932ec385c7ff3bc1148e45125984783dc6"}, + {file = "yarl-1.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:6465d36381af057d0fab4e0f24ef0e80ba61f03fe43e6eeccbe0056e74aadc70"}, + {file = "yarl-1.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2f3c8822bc8fb4a347a192dd6a28a25d7f0ea3262e826d7d4ef9cc99cd06d07e"}, + {file = "yarl-1.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7831566595fe88ba17ea80e4b61c0eb599f84c85acaa14bf04dd90319a45b90"}, + {file = "yarl-1.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ff34cb09a332832d1cf38acd0f604c068665192c6107a439a92abfd8acf90fe2"}, + {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe8080b4f25dfc44a86bedd14bc4f9d469dfc6456e6f3c5d9077e81a5fedfba7"}, + {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8535e111a064f3bdd94c0ed443105934d6f005adad68dd13ce50a488a0ad1bf3"}, + {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d155a092bf0ebf4a9f6f3b7a650dc5d9a5bbb585ef83a52ed36ba46f55cc39d"}, + {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:778df71c8d0c8c9f1b378624b26431ca80041660d7be7c3f724b2c7a6e65d0d6"}, + {file = "yarl-1.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9f9cafaf031c34d95c1528c16b2fa07b710e6056b3c4e2e34e9317072da5d1a"}, + {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ca6b66f69e30f6e180d52f14d91ac854b8119553b524e0e28d5291a724f0f423"}, + {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0e7e83f31e23c5d00ff618045ddc5e916f9e613d33c5a5823bc0b0a0feb522f"}, + {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:af52725c7c39b0ee655befbbab5b9a1b209e01bb39128dce0db226a10014aacc"}, + {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0ab5baaea8450f4a3e241ef17e3d129b2143e38a685036b075976b9c415ea3eb"}, + {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6d350388ba1129bc867c6af1cd17da2b197dff0d2801036d2d7d83c2d771a682"}, + {file = "yarl-1.9.3-cp39-cp39-win32.whl", hash = "sha256:e2a16ef5fa2382af83bef4a18c1b3bcb4284c4732906aa69422cf09df9c59f1f"}, + {file = "yarl-1.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:d92d897cb4b4bf915fbeb5e604c7911021a8456f0964f3b8ebbe7f9188b9eabb"}, + {file = "yarl-1.9.3-py3-none-any.whl", hash = "sha256:271d63396460b6607b588555ea27a1a02b717ca2e3f2cf53bdde4013d7790929"}, + {file = "yarl-1.9.3.tar.gz", hash = "sha256:4a14907b597ec55740f63e52d7fee0e9ee09d5b9d57a4f399a7423268e457b57"}, ] [package.dependencies] @@ -1846,5 +1700,5 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" -python-versions = ">=3.9,<4" -content-hash = "77746098165efa0d9d239d42ce2a21681d750d6ed85cb1962b0604aa1ab5c149" +python-versions = ">=3.11,<4" +content-hash = "055a21b4a379207cb3b081ceb326e05dddaefd98c98be77b7234800c0467592c" diff --git a/pyproject.toml b/pyproject.toml index 3e142fe8..093c2488 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ useLibraryCodeForTypes = true reportMissingImports = "information" [tool.poetry.dependencies] -python = ">=3.9,<4" +python = ">=3.11,<4" brewblox-service = "^3.3.2" pyserial-asyncio = "^0.6" protobuf = "^4.24.3" From d80f11848fdbeb9b38367c41499c59937d5739d1 Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Tue, 5 Dec 2023 22:43:23 +0100 Subject: [PATCH 17/27] implement invalid_if --- brewblox-proto | 2 +- brewblox_devcon_spark/codec/processor.py | 18 +++++++++++------- .../proto-compiled/ActuatorAnalogMock_pb2.py | 1 - .../codec/proto-compiled/ActuatorLogic_pb2.py | 1 - .../codec/proto-compiled/ActuatorOffset_pb2.py | 1 - .../codec/proto-compiled/ActuatorPwm_pb2.py | 1 - .../codec/proto-compiled/Balancer_pb2.py | 1 - .../codec/proto-compiled/Claims_pb2.py | 1 - .../codec/proto-compiled/Constraints_pb2.py | 1 - .../codec/proto-compiled/DS2408_pb2.py | 1 - .../codec/proto-compiled/DS2413_pb2.py | 1 - .../proto-compiled/DigitalActuator_pb2.py | 1 - .../codec/proto-compiled/DigitalInput_pb2.py | 1 - .../proto-compiled/DisplaySettings_pb2.py | 1 - .../codec/proto-compiled/EdgeCase_pb2.py | 13 ++++++------- .../codec/proto-compiled/FastPwm_pb2.py | 1 - .../codec/proto-compiled/IoArray_pb2.py | 1 - .../codec/proto-compiled/MockPins_pb2.py | 1 - .../codec/proto-compiled/MotorValve_pb2.py | 1 - .../codec/proto-compiled/Mutex_pb2.py | 1 - .../codec/proto-compiled/OneWireBus_pb2.py | 1 - .../proto-compiled/OneWireGpioModule_pb2.py | 1 - .../codec/proto-compiled/Pid_pb2.py | 1 - .../codec/proto-compiled/Screen_pb2.py | 1 - .../codec/proto-compiled/Sequence_pb2.py | 1 - .../proto-compiled/SetpointProfile_pb2.py | 1 - .../proto-compiled/SetpointSensorPair_pb2.py | 1 - .../codec/proto-compiled/Spark2Pins_pb2.py | 1 - .../codec/proto-compiled/Spark3Pins_pb2.py | 1 - .../codec/proto-compiled/SysInfo_pb2.py | 1 - .../proto-compiled/TempSensorCombi_pb2.py | 1 - .../proto-compiled/TempSensorExternal_pb2.py | 1 - .../codec/proto-compiled/TempSensorMock_pb2.py | 1 - .../proto-compiled/TempSensorOneWire_pb2.py | 1 - .../codec/proto-compiled/TouchSettings_pb2.py | 1 - .../codec/proto-compiled/WiFiSettings_pb2.py | 1 - .../codec/proto-compiled/brewblox_pb2.py | 15 ++++++--------- .../codec/proto-compiled/command_pb2.py | 1 - .../codec/proto-compiled/nanopb_pb2.py | 5 ----- firmware.ini | 10 +++++----- test/test_codec.py | 16 ++++++++++++++++ 41 files changed, 45 insertions(+), 68 deletions(-) diff --git a/brewblox-proto b/brewblox-proto index 67da1ac7..e6177a67 160000 --- a/brewblox-proto +++ b/brewblox-proto @@ -1 +1 @@ -Subproject commit 67da1ac735853fe7964ba86db4815876bdc14899 +Subproject commit e6177a67ae25f0dd428f35abaa7f99b61d59af4e diff --git a/brewblox_devcon_spark/codec/processor.py b/brewblox_devcon_spark/codec/processor.py index d2dd1e75..fd57ef76 100644 --- a/brewblox_devcon_spark/codec/processor.py +++ b/brewblox_devcon_spark/codec/processor.py @@ -9,7 +9,7 @@ from dataclasses import dataclass from functools import reduce from socket import htonl, ntohl -from typing import Any, Iterator, Optional, Union +from typing import Any, Iterator from brewblox_service import brewblox_logger from google.protobuf import json_format @@ -119,7 +119,7 @@ def _unit_name(self, unit_num: int) -> str: def _type_name(self, blockType_num: int) -> str: return brewblox_pb2.BlockType.Name(blockType_num) - def _encode_unit(self, value: Union[float, dict], unit_type: str, postfix: Optional[str]) -> float: + def _encode_unit(self, value: float | dict, unit_type: str, postfix: str | None) -> float: if isinstance(value, dict): user_value = value['value'] user_unit = value.get('unit') @@ -208,7 +208,7 @@ def pre_encode(self, if payload.maskMode == MaskMode.INCLUSIVE and not element.nested: payload.mask.append(element.field.number) - def _convert_value(value: Any) -> Union[str, int, float]: + def _convert_value(value: Any) -> str | int | float: if options.unit: unit_name = self._unit_name(options.unit) value = self._encode_unit(value, unit_name, element.postfix or None) @@ -278,6 +278,7 @@ def post_decode(self, * readonly: Ignored: decoding means reading from controller. * ignored: Strip value from output. * logged: Tag for filtering output data. + * *_invalid_if: Sets value to None if equal to invalid value. Supported codec options: * filter: If opts.filter == LOGGED, all values without options.logged are excluded from output. @@ -346,12 +347,15 @@ def post_decode(self, qty_system_unit = self._unit_name(options.unit) qty_user_unit = self._converter.to_user_unit(qty_system_unit) - def _convert_value(value: Union[float, int, str]) -> Any: + def _convert_value(value: float | int | str) -> float | int | str | None: + invalid = value in [options.uint_invalid_if or None, + options.sint_invalid_if or None] + if options.scale: value /= options.scale if options.unit: - if excluded: + if excluded or invalid: value = None else: value = self._converter.to_user_value(value, qty_system_unit) @@ -369,7 +373,7 @@ def _convert_value(value: Union[float, int, str]) -> Any: return value if options.objtype: - if excluded: + if excluded or invalid: value = None if opts.metadata == MetadataOpt.TYPED: @@ -381,7 +385,7 @@ def _convert_value(value: Union[float, int, str]) -> Any: return value - if excluded: + if excluded or invalid: return None if options.hexed: diff --git a/brewblox_devcon_spark/codec/proto-compiled/ActuatorAnalogMock_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/ActuatorAnalogMock_pb2.py index 47799896..e66ced65 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/ActuatorAnalogMock_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/ActuatorAnalogMock_pb2.py @@ -23,7 +23,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ActuatorAnalogMock_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['storedSetting']._options = None _BLOCK.fields_by_name['storedSetting']._serialized_options = b'\222?\0028 \212\265\030\005\020\200 0\001' diff --git a/brewblox_devcon_spark/codec/proto-compiled/ActuatorLogic_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/ActuatorLogic_pb2.py index 1a38dc82..b22d47cb 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/ActuatorLogic_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/ActuatorLogic_pb2.py @@ -22,7 +22,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ActuatorLogic_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _DIGITALCOMPARE.fields_by_name['result']._options = None _DIGITALCOMPARE.fields_by_name['result']._serialized_options = b'\212\265\030\002(\001' diff --git a/brewblox_devcon_spark/codec/proto-compiled/ActuatorOffset_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/ActuatorOffset_pb2.py index 418ff963..6e718728 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/ActuatorOffset_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/ActuatorOffset_pb2.py @@ -23,7 +23,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ActuatorOffset_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['targetId']._options = None _BLOCK.fields_by_name['targetId']._serialized_options = b'\222?\0028\020\212\265\030\002\030\004' diff --git a/brewblox_devcon_spark/codec/proto-compiled/ActuatorPwm_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/ActuatorPwm_pb2.py index cf2e52bc..ac7a5d89 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/ActuatorPwm_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/ActuatorPwm_pb2.py @@ -23,7 +23,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ActuatorPwm_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['actuatorId']._options = None _BLOCK.fields_by_name['actuatorId']._serialized_options = b'\222?\0028\020\212\265\030\002\030\006' diff --git a/brewblox_devcon_spark/codec/proto-compiled/Balancer_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/Balancer_pb2.py index 5fa94403..89bfd2b6 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/Balancer_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/Balancer_pb2.py @@ -21,7 +21,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Balancer_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BALANCEDACTUATOR.fields_by_name['id']._options = None _BALANCEDACTUATOR.fields_by_name['id']._serialized_options = b'\222?\0028\020\212\265\030\005\030\377\001(\001' diff --git a/brewblox_devcon_spark/codec/proto-compiled/Claims_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/Claims_pb2.py index 48b6b72e..afe38b77 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/Claims_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/Claims_pb2.py @@ -19,7 +19,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Claims_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _globals['_SETTINGMODE']._serialized_start=29 _globals['_SETTINGMODE']._serialized_end=67 diff --git a/brewblox_devcon_spark/codec/proto-compiled/Constraints_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/Constraints_pb2.py index cb9538da..6de69ff5 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/Constraints_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/Constraints_pb2.py @@ -21,7 +21,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Constraints_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _VALUECONSTRAINT.fields_by_name['value']._options = None _VALUECONSTRAINT.fields_by_name['value']._serialized_options = b'\222?\0028 \212\265\030\003\020\200 ' diff --git a/brewblox_devcon_spark/codec/proto-compiled/DS2408_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/DS2408_pb2.py index e7811fe2..32b0f391 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/DS2408_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/DS2408_pb2.py @@ -22,7 +22,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'DS2408_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _CHANNELID._options = None _CHANNELID._serialized_options = b'\020\001' diff --git a/brewblox_devcon_spark/codec/proto-compiled/DS2413_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/DS2413_pb2.py index c394737e..228fecbf 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/DS2413_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/DS2413_pb2.py @@ -22,7 +22,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'DS2413_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['address']._options = None _BLOCK.fields_by_name['address']._serialized_options = b'\212\265\030\002 \001' diff --git a/brewblox_devcon_spark/codec/proto-compiled/DigitalActuator_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/DigitalActuator_pb2.py index 45889f00..c7e78607 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/DigitalActuator_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/DigitalActuator_pb2.py @@ -24,7 +24,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'DigitalActuator_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['hwDevice']._options = None _BLOCK.fields_by_name['hwDevice']._serialized_options = b'\222?\0028\020\212\265\030\002\030\n' diff --git a/brewblox_devcon_spark/codec/proto-compiled/DigitalInput_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/DigitalInput_pb2.py index edeb8726..ad61980c 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/DigitalInput_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/DigitalInput_pb2.py @@ -22,7 +22,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'DigitalInput_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['hwDevice']._options = None _BLOCK.fields_by_name['hwDevice']._serialized_options = b'\222?\0028\020\212\265\030\002\030\n' diff --git a/brewblox_devcon_spark/codec/proto-compiled/DisplaySettings_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/DisplaySettings_pb2.py index a26e825f..2bef04f8 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/DisplaySettings_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/DisplaySettings_pb2.py @@ -21,7 +21,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'DisplaySettings_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _WIDGET.fields_by_name['pos']._options = None _WIDGET.fields_by_name['pos']._serialized_options = b'\222?\0028\010' diff --git a/brewblox_devcon_spark/codec/proto-compiled/EdgeCase_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/EdgeCase_pb2.py index 7bfcb63a..d618b85c 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/EdgeCase_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/EdgeCase_pb2.py @@ -15,13 +15,12 @@ import nanopb_pb2 as nanopb__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0e\x45\x64geCase.proto\x12\rblox.EdgeCase\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\">\n\x08Settings\x12\x17\n\x07\x61\x64\x64ress\x18\x01 \x01(\x06\x42\x06\x8a\xb5\x18\x02 \x01\x12\x19\n\x06offset\x18\x02 \x01(\x11\x42\t\x8a\xb5\x18\x05\x08\x06\x10\x80\x02\"<\n\x05State\x12\x18\n\x05value\x18\x01 \x01(\x11\x42\t\x8a\xb5\x18\x05\x08\x01\x10\x80\x02\x12\x19\n\tconnected\x18\x02 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\"(\n\nNestedLink\x12\x1a\n\nconnection\x18\x01 \x01(\rB\x06\x8a\xb5\x18\x02\x18\x02\"\x9e\x02\n\x05\x42lock\x12)\n\x08settings\x18\x01 \x01(\x0b\x32\x17.blox.EdgeCase.Settings\x12#\n\x05state\x18\x02 \x01(\x0b\x32\x14.blox.EdgeCase.State\x12\x14\n\x04link\x18\x03 \x01(\rB\x06\x8a\xb5\x18\x02\x18\x05\x12\x32\n\x0f\x61\x64\x64itionalLinks\x18\x04 \x03(\x0b\x32\x19.blox.EdgeCase.NestedLink\x12\x1d\n\nlistValues\x18\x05 \x03(\x02\x42\t\x8a\xb5\x18\x05\x08\x01\x10\x80\x02\x12\x19\n\x06\x64\x65ltaV\x18\x06 \x01(\rB\t\x8a\xb5\x18\x05\x08\x07\x10\x80\x02\x12\x16\n\x06logged\x18\x07 \x01(\rB\x06\x8a\xb5\x18\x02\x30\x01\x12\x10\n\x08unLogged\x18\x08 \x01(\r\x12\x17\n\x02ip\x18\n \x01(\rB\x0b\x92?\x02\x38 \x8a\xb5\x18\x02`\x01\"#\n\x07SubCase\x12\x10\n\x08subvalue\x18\x01 \x01(\r:\x06\x8a\xb5\x18\x02X\x01\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0e\x45\x64geCase.proto\x12\rblox.EdgeCase\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\">\n\x08Settings\x12\x17\n\x07\x61\x64\x64ress\x18\x01 \x01(\x06\x42\x06\x8a\xb5\x18\x02 \x01\x12\x19\n\x06offset\x18\x02 \x01(\x11\x42\t\x8a\xb5\x18\x05\x08\x06\x10\x80\x02\"<\n\x05State\x12\x18\n\x05value\x18\x01 \x01(\x11\x42\t\x8a\xb5\x18\x05\x08\x01\x10\x80\x02\x12\x19\n\tconnected\x18\x02 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\"(\n\nNestedLink\x12\x1a\n\nconnection\x18\x01 \x01(\rB\x06\x8a\xb5\x18\x02\x18\x02\"\xa2\x02\n\x05\x42lock\x12)\n\x08settings\x18\x01 \x01(\x0b\x32\x17.blox.EdgeCase.Settings\x12#\n\x05state\x18\x02 \x01(\x0b\x32\x14.blox.EdgeCase.State\x12\x14\n\x04link\x18\x03 \x01(\rB\x06\x8a\xb5\x18\x02\x18\x05\x12\x32\n\x0f\x61\x64\x64itionalLinks\x18\x04 \x03(\x0b\x32\x19.blox.EdgeCase.NestedLink\x12\x1f\n\nlistValues\x18\x05 \x03(\x11\x42\x0b\x8a\xb5\x18\x07\x08\x01\x10\x80\x02p\x01\x12\x1b\n\x06\x64\x65ltaV\x18\x06 \x01(\rB\x0b\x8a\xb5\x18\x07\x08\x07\x10\x80\x02h\x01\x12\x16\n\x06logged\x18\x07 \x01(\rB\x06\x8a\xb5\x18\x02\x30\x01\x12\x10\n\x08unLogged\x18\x08 \x01(\r\x12\x17\n\x02ip\x18\n \x01(\rB\x0b\x92?\x02\x38 \x8a\xb5\x18\x02`\x01\"#\n\x07SubCase\x12\x10\n\x08subvalue\x18\x01 \x01(\r:\x06\x8a\xb5\x18\x02X\x01\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'EdgeCase_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _SETTINGS.fields_by_name['address']._options = None _SETTINGS.fields_by_name['address']._serialized_options = b'\212\265\030\002 \001' @@ -36,9 +35,9 @@ _BLOCK.fields_by_name['link']._options = None _BLOCK.fields_by_name['link']._serialized_options = b'\212\265\030\002\030\005' _BLOCK.fields_by_name['listValues']._options = None - _BLOCK.fields_by_name['listValues']._serialized_options = b'\212\265\030\005\010\001\020\200\002' + _BLOCK.fields_by_name['listValues']._serialized_options = b'\212\265\030\007\010\001\020\200\002p\001' _BLOCK.fields_by_name['deltaV']._options = None - _BLOCK.fields_by_name['deltaV']._serialized_options = b'\212\265\030\005\010\007\020\200\002' + _BLOCK.fields_by_name['deltaV']._serialized_options = b'\212\265\030\007\010\007\020\200\002h\001' _BLOCK.fields_by_name['logged']._options = None _BLOCK.fields_by_name['logged']._serialized_options = b'\212\265\030\0020\001' _BLOCK.fields_by_name['ip']._options = None @@ -52,7 +51,7 @@ _globals['_NESTEDLINK']._serialized_start=189 _globals['_NESTEDLINK']._serialized_end=229 _globals['_BLOCK']._serialized_start=232 - _globals['_BLOCK']._serialized_end=518 - _globals['_SUBCASE']._serialized_start=520 - _globals['_SUBCASE']._serialized_end=555 + _globals['_BLOCK']._serialized_end=522 + _globals['_SUBCASE']._serialized_start=524 + _globals['_SUBCASE']._serialized_end=559 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/FastPwm_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/FastPwm_pb2.py index 04c5186b..35592031 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/FastPwm_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/FastPwm_pb2.py @@ -24,7 +24,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'FastPwm_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['hwDevice']._options = None _BLOCK.fields_by_name['hwDevice']._serialized_options = b'\222?\0028\020\212\265\030\002\030\n' diff --git a/brewblox_devcon_spark/codec/proto-compiled/IoArray_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/IoArray_pb2.py index 39ae426e..01599055 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/IoArray_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/IoArray_pb2.py @@ -21,7 +21,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'IoArray_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _DIGITALSTATE._options = None _DIGITALSTATE._serialized_options = b'\020\001' diff --git a/brewblox_devcon_spark/codec/proto-compiled/MockPins_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/MockPins_pb2.py index d268df1b..bf32466b 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/MockPins_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/MockPins_pb2.py @@ -22,7 +22,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'MockPins_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['channels']._options = None _BLOCK.fields_by_name['channels']._serialized_options = b'\222?\004\020\010x\001\212\265\030\002(\001' diff --git a/brewblox_devcon_spark/codec/proto-compiled/MotorValve_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/MotorValve_pb2.py index 2e548760..5e042240 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/MotorValve_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/MotorValve_pb2.py @@ -24,7 +24,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'MotorValve_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['hwDevice']._options = None _BLOCK.fields_by_name['hwDevice']._serialized_options = b'\222?\0028\020\212\265\030\002\030\013' diff --git a/brewblox_devcon_spark/codec/proto-compiled/Mutex_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/Mutex_pb2.py index a241807a..58deba9d 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/Mutex_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/Mutex_pb2.py @@ -21,7 +21,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Mutex_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['waitRemaining']._options = None _BLOCK.fields_by_name['waitRemaining']._serialized_options = b'\222?\0028 \212\265\030\007\010\003\020\350\007(\001' diff --git a/brewblox_devcon_spark/codec/proto-compiled/OneWireBus_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/OneWireBus_pb2.py index fc7d48b1..b853a50e 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/OneWireBus_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/OneWireBus_pb2.py @@ -21,7 +21,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'OneWireBus_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _COMMAND.fields_by_name['opcode']._options = None _COMMAND.fields_by_name['opcode']._serialized_options = b'\222?\0028\010' diff --git a/brewblox_devcon_spark/codec/proto-compiled/OneWireGpioModule_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/OneWireGpioModule_pb2.py index 4a68bdaa..ff84cd51 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/OneWireGpioModule_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/OneWireGpioModule_pb2.py @@ -21,7 +21,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'OneWireGpioModule_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _GPIOMODULECHANNEL.fields_by_name['id']._options = None _GPIOMODULECHANNEL.fields_by_name['id']._serialized_options = b'\222?\0028\010' diff --git a/brewblox_devcon_spark/codec/proto-compiled/Pid_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/Pid_pb2.py index d3a0465a..076b0997 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/Pid_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/Pid_pb2.py @@ -22,7 +22,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Pid_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['inputId']._options = None _BLOCK.fields_by_name['inputId']._serialized_options = b'\222?\0028\020\212\265\030\002\030\004' diff --git a/brewblox_devcon_spark/codec/proto-compiled/Screen_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/Screen_pb2.py index 74fde433..1c509222 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/Screen_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/Screen_pb2.py @@ -20,7 +20,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Screen_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _LAYOUTNODE.fields_by_name['parent']._options = None _LAYOUTNODE.fields_by_name['parent']._serialized_options = b'\222?\0028\010' diff --git a/brewblox_devcon_spark/codec/proto-compiled/Sequence_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/Sequence_pb2.py index 52c95648..1b6bfe04 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/Sequence_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/Sequence_pb2.py @@ -22,7 +22,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Sequence_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _ENABLEDISABLE.fields_by_name['target']._options = None _ENABLEDISABLE.fields_by_name['target']._serialized_options = b'\222?\0028\020\212\265\030\002\030\017' diff --git a/brewblox_devcon_spark/codec/proto-compiled/SetpointProfile_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/SetpointProfile_pb2.py index d94e06e1..f830b039 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/SetpointProfile_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/SetpointProfile_pb2.py @@ -21,7 +21,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'SetpointProfile_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _POINT.fields_by_name['time']._options = None _POINT.fields_by_name['time']._serialized_options = b'\222?\0028 \212\265\030\002\010\003' diff --git a/brewblox_devcon_spark/codec/proto-compiled/SetpointSensorPair_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/SetpointSensorPair_pb2.py index c5da9b54..ff3ff4d8 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/SetpointSensorPair_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/SetpointSensorPair_pb2.py @@ -22,7 +22,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'SetpointSensorPair_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['sensorId']._options = None _BLOCK.fields_by_name['sensorId']._serialized_options = b'\222?\0028\020\212\265\030\002\030\002' diff --git a/brewblox_devcon_spark/codec/proto-compiled/Spark2Pins_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/Spark2Pins_pb2.py index f98eaa05..a86f78aa 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/Spark2Pins_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/Spark2Pins_pb2.py @@ -22,7 +22,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Spark2Pins_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['hardware']._options = None _BLOCK.fields_by_name['hardware']._serialized_options = b'\212\265\030\002(\001' diff --git a/brewblox_devcon_spark/codec/proto-compiled/Spark3Pins_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/Spark3Pins_pb2.py index 2f429b01..354fce6f 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/Spark3Pins_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/Spark3Pins_pb2.py @@ -22,7 +22,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Spark3Pins_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['voltage5']._options = None _BLOCK.fields_by_name['voltage5']._serialized_options = b'\222?\0028\020\212\265\030\005\020\350\007(\001' diff --git a/brewblox_devcon_spark/codec/proto-compiled/SysInfo_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/SysInfo_pb2.py index 202fd380..a902688b 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/SysInfo_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/SysInfo_pb2.py @@ -21,7 +21,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'SysInfo_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['deviceId']._options = None _BLOCK.fields_by_name['deviceId']._serialized_options = b'\222?\002\010\014\212\265\030\004(\0018\001' diff --git a/brewblox_devcon_spark/codec/proto-compiled/TempSensorCombi_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/TempSensorCombi_pb2.py index 34051459..5e4a2428 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/TempSensorCombi_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/TempSensorCombi_pb2.py @@ -21,7 +21,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TempSensorCombi_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['value']._options = None _BLOCK.fields_by_name['value']._serialized_options = b'\222?\0028 \212\265\030\t\010\001\020\200 (\0010\001' diff --git a/brewblox_devcon_spark/codec/proto-compiled/TempSensorExternal_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/TempSensorExternal_pb2.py index 57085988..2ae818a9 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/TempSensorExternal_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/TempSensorExternal_pb2.py @@ -21,7 +21,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TempSensorExternal_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['timeout']._options = None _BLOCK.fields_by_name['timeout']._serialized_options = b'\222?\0028 \212\265\030\002\010\003' diff --git a/brewblox_devcon_spark/codec/proto-compiled/TempSensorMock_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/TempSensorMock_pb2.py index 6ad204f9..b3449297 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/TempSensorMock_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/TempSensorMock_pb2.py @@ -21,7 +21,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TempSensorMock_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _FLUCTUATION.fields_by_name['amplitude']._options = None _FLUCTUATION.fields_by_name['amplitude']._serialized_options = b'\222?\0028 \212\265\030\007\010\006\020\200 0\001' diff --git a/brewblox_devcon_spark/codec/proto-compiled/TempSensorOneWire_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/TempSensorOneWire_pb2.py index b509f507..ee7e1fab 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/TempSensorOneWire_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/TempSensorOneWire_pb2.py @@ -21,7 +21,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TempSensorOneWire_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['value']._options = None _BLOCK.fields_by_name['value']._serialized_options = b'\222?\0028 \212\265\030\t\010\001\020\200 (\0010\001' diff --git a/brewblox_devcon_spark/codec/proto-compiled/TouchSettings_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/TouchSettings_pb2.py index f68dc1ac..77cfabeb 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/TouchSettings_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/TouchSettings_pb2.py @@ -21,7 +21,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'TouchSettings_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['xOffset']._options = None _BLOCK.fields_by_name['xOffset']._serialized_options = b'\222?\0028\020' diff --git a/brewblox_devcon_spark/codec/proto-compiled/WiFiSettings_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/WiFiSettings_pb2.py index 09d26fe0..4c9d3531 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/WiFiSettings_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/WiFiSettings_pb2.py @@ -21,7 +21,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'WiFiSettings_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _BLOCK.fields_by_name['ssid']._options = None _BLOCK.fields_by_name['ssid']._serialized_options = b'\222?\002\010!' diff --git a/brewblox_devcon_spark/codec/proto-compiled/brewblox_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/brewblox_pb2.py index a6b3c7d3..3e07e734 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/brewblox_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/brewblox_pb2.py @@ -15,15 +15,12 @@ import nanopb_pb2 as nanopb__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0e\x62rewblox.proto\x12\x08\x62rewblox\x1a google/protobuf/descriptor.proto\x1a\x0cnanopb.proto\"|\n\x0bMessageOpts\x12$\n\x07objtype\x18\x03 \x01(\x0e\x32\x13.brewblox.BlockType\x12(\n\x04impl\x18\t \x03(\x0e\x32\x13.brewblox.BlockTypeB\x05\x92?\x02\x10\x05\x12\x16\n\x07subtype\x18\x0b \x01(\rB\x05\x92?\x02\x38\x10:\x05\x92?\x02\x30\x01\"\xf4\x01\n\tFieldOpts\x12 \n\x04unit\x18\x01 \x01(\x0e\x32\x12.brewblox.UnitType\x12\r\n\x05scale\x18\x02 \x01(\r\x12$\n\x07objtype\x18\x03 \x01(\x0e\x32\x13.brewblox.BlockType\x12\r\n\x05hexed\x18\x04 \x01(\x08\x12\x10\n\x08readonly\x18\x05 \x01(\x08\x12\x0e\n\x06logged\x18\x06 \x01(\x08\x12\x0e\n\x06hexstr\x18\x07 \x01(\x08\x12\x0f\n\x07ignored\x18\t \x01(\x08\x12\x10\n\x08\x62itfield\x18\n \x01(\x08\x12\x10\n\x08\x64\x61tetime\x18\x0b \x01(\x08\x12\x13\n\x0bipv4address\x18\x0c \x01(\x08:\x05\x92?\x02\x30\x01*\x8c\x02\n\x08UnitType\x12\n\n\x06NotSet\x10\x00\x12\x0b\n\x07\x43\x65lsius\x10\x01\x12\x12\n\x0eInverseCelsius\x10\x02\x12\n\n\x06Second\x10\x03\x12\n\n\x06Minute\x10\x04\x12\x08\n\x04Hour\x10\x05\x12\x10\n\x0c\x44\x65ltaCelsius\x10\x06\x12\x19\n\x15\x44\x65ltaCelsiusPerSecond\x10\x07\x12\x19\n\x15\x44\x65ltaCelsiusPerMinute\x10\x08\x12\x17\n\x13\x44\x65ltaCelsiusPerHour\x10\t\x12\x1a\n\x16\x44\x65ltaCelsiusMultSecond\x10\n\x12\x1a\n\x16\x44\x65ltaCelsiusMultMinute\x10\x0b\x12\x18\n\x14\x44\x65ltaCelsiusMultHour\x10\x0c*\xb8\n\n\tBlockType\x12\x0b\n\x07Invalid\x10\x00\x12\x19\n\x15ProcessValueInterface\x10\x01\x12\x17\n\x13TempSensorInterface\x10\x02\x12\x1f\n\x1bSetpointSensorPairInterface\x10\x04\x12\x1b\n\x17\x41\x63tuatorAnalogInterface\x10\x05\x12\x1c\n\x18\x41\x63tuatorDigitalInterface\x10\x06\x12\x15\n\x11\x42\x61lancerInterface\x10\x07\x12\x12\n\x0eMutexInterface\x10\x08\x12\x1a\n\x16OneWireDeviceInterface\x10\t\x12\x14\n\x10IoArrayInterface\x10\n\x12\x13\n\x0f\x44S2408Interface\x10\x0b\x12\x17\n\x13OneWireBusInterface\x10\x0c\x12\x15\n\x11IoModuleInterface\x10\r\x12\x1f\n\x1bOneWireDeviceBlockInterface\x10\x0e\x12\x14\n\x10\x45nablerInterface\x10\x0f\x12\x16\n\x12\x43laimableInterface\x10\x10\x12\x15\n\x11IoDriverInterface\x10\x11\x12\x15\n\x11SetpointInterface\x10\x12\x12\x19\n\x15StoredAnalogInterface\x10\x13\x12\x1b\n\x17StoredSetpointInterface\x10\x14\x12\x1a\n\x16StoredDigitalInterface\x10\x15\x12\x1e\n\x1a\x43onstrainedAnalogInterface\x10\x16\x12 \n\x1c\x43onstrainedSetpointInterface\x10\x17\x12\x1f\n\x1b\x43onstrainedDigitalInterface\x10\x18\x12\x1c\n\x18ScanningFactoryInterface\x10\x19\x12\x1c\n\x18I2CDiscoverableInterface\x10\x1a\x12\x14\n\x10\x44igitalInterface\x10\x1b\x12\x08\n\x03\x41ny\x10\xff\x01\x12\x0c\n\x07SysInfo\x10\x80\x02\x12\n\n\x05Ticks\x10\x81\x02\x12\x0f\n\nOneWireBus\x10\x82\x02\x12\x0e\n\tBoardPins\x10\x83\x02\x12\x13\n\x0eTempSensorMock\x10\xad\x02\x12\x16\n\x11TempSensorOneWire\x10\xae\x02\x12\x17\n\x12SetpointSensorPair\x10\xaf\x02\x12\x08\n\x03Pid\x10\xb0\x02\x12\x17\n\x12\x41\x63tuatorAnalogMock\x10\xb1\x02\x12\x10\n\x0b\x41\x63tuatorPin\x10\xb2\x02\x12\x10\n\x0b\x41\x63tuatorPwm\x10\xb3\x02\x12\x13\n\x0e\x41\x63tuatorOffset\x10\xb4\x02\x12\r\n\x08\x42\x61lancer\x10\xb5\x02\x12\n\n\x05Mutex\x10\xb6\x02\x12\x14\n\x0fSetpointProfile\x10\xb7\x02\x12\x11\n\x0cWiFiSettings\x10\xb8\x02\x12\x12\n\rTouchSettings\x10\xb9\x02\x12\x14\n\x0f\x44isplaySettings\x10\xba\x02\x12\x0b\n\x06\x44S2413\x10\xbb\x02\x12\x14\n\x0f\x41\x63tuatorOneWire\x10\xbc\x02\x12\x0b\n\x06\x44S2408\x10\xbd\x02\x12\x14\n\x0f\x44igitalActuator\x10\xbe\x02\x12\x0f\n\nSpark3Pins\x10\xbf\x02\x12\x0f\n\nSpark2Pins\x10\xc0\x02\x12\x0f\n\nMotorValve\x10\xc1\x02\x12\x12\n\rActuatorLogic\x10\xc2\x02\x12\r\n\x08MockPins\x10\xc3\x02\x12\x14\n\x0fTempSensorCombi\x10\xc4\x02\x12\x16\n\x11OneWireGpioModule\x10\xc5\x02\x12\r\n\x08Sequence\x10\xc6\x02\x12\x17\n\x12TempSensorExternal\x10\xc8\x02\x12\x0c\n\x07\x46\x61stPwm\x10\xc9\x02\x12\x11\n\x0c\x44igitalInput\x10\xca\x02:J\n\x05\x66ield\x12\x1d.google.protobuf.FieldOptions\x18\xd1\x86\x03 \x01(\x0b\x32\x13.brewblox.FieldOptsB\x05\x92?\x02\x18\x03:L\n\x03msg\x12\x1f.google.protobuf.MessageOptions\x18\xd1\x86\x03 \x01(\x0b\x32\x15.brewblox.MessageOptsB\x05\x92?\x02\x18\x03\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0e\x62rewblox.proto\x12\x08\x62rewblox\x1a google/protobuf/descriptor.proto\x1a\x0cnanopb.proto\"|\n\x0bMessageOpts\x12$\n\x07objtype\x18\x03 \x01(\x0e\x32\x13.brewblox.BlockType\x12(\n\x04impl\x18\t \x03(\x0e\x32\x13.brewblox.BlockTypeB\x05\x92?\x02\x10\x05\x12\x16\n\x07subtype\x18\x0b \x01(\rB\x05\x92?\x02\x38\x10:\x05\x92?\x02\x30\x01\"\xa6\x02\n\tFieldOpts\x12 \n\x04unit\x18\x01 \x01(\x0e\x32\x12.brewblox.UnitType\x12\r\n\x05scale\x18\x02 \x01(\r\x12$\n\x07objtype\x18\x03 \x01(\x0e\x32\x13.brewblox.BlockType\x12\r\n\x05hexed\x18\x04 \x01(\x08\x12\x10\n\x08readonly\x18\x05 \x01(\x08\x12\x0e\n\x06logged\x18\x06 \x01(\x08\x12\x0e\n\x06hexstr\x18\x07 \x01(\x08\x12\x0f\n\x07ignored\x18\t \x01(\x08\x12\x10\n\x08\x62itfield\x18\n \x01(\x08\x12\x10\n\x08\x64\x61tetime\x18\x0b \x01(\x08\x12\x13\n\x0bipv4address\x18\x0c \x01(\x08\x12\x17\n\x0fuint_invalid_if\x18\r \x01(\r\x12\x17\n\x0fsint_invalid_if\x18\x0e \x01(\x11:\x05\x92?\x02\x30\x01*\x8c\x02\n\x08UnitType\x12\n\n\x06NotSet\x10\x00\x12\x0b\n\x07\x43\x65lsius\x10\x01\x12\x12\n\x0eInverseCelsius\x10\x02\x12\n\n\x06Second\x10\x03\x12\n\n\x06Minute\x10\x04\x12\x08\n\x04Hour\x10\x05\x12\x10\n\x0c\x44\x65ltaCelsius\x10\x06\x12\x19\n\x15\x44\x65ltaCelsiusPerSecond\x10\x07\x12\x19\n\x15\x44\x65ltaCelsiusPerMinute\x10\x08\x12\x17\n\x13\x44\x65ltaCelsiusPerHour\x10\t\x12\x1a\n\x16\x44\x65ltaCelsiusMultSecond\x10\n\x12\x1a\n\x16\x44\x65ltaCelsiusMultMinute\x10\x0b\x12\x18\n\x14\x44\x65ltaCelsiusMultHour\x10\x0c*\xb8\n\n\tBlockType\x12\x0b\n\x07Invalid\x10\x00\x12\x19\n\x15ProcessValueInterface\x10\x01\x12\x17\n\x13TempSensorInterface\x10\x02\x12\x1f\n\x1bSetpointSensorPairInterface\x10\x04\x12\x1b\n\x17\x41\x63tuatorAnalogInterface\x10\x05\x12\x1c\n\x18\x41\x63tuatorDigitalInterface\x10\x06\x12\x15\n\x11\x42\x61lancerInterface\x10\x07\x12\x12\n\x0eMutexInterface\x10\x08\x12\x1a\n\x16OneWireDeviceInterface\x10\t\x12\x14\n\x10IoArrayInterface\x10\n\x12\x13\n\x0f\x44S2408Interface\x10\x0b\x12\x17\n\x13OneWireBusInterface\x10\x0c\x12\x15\n\x11IoModuleInterface\x10\r\x12\x1f\n\x1bOneWireDeviceBlockInterface\x10\x0e\x12\x14\n\x10\x45nablerInterface\x10\x0f\x12\x16\n\x12\x43laimableInterface\x10\x10\x12\x15\n\x11IoDriverInterface\x10\x11\x12\x15\n\x11SetpointInterface\x10\x12\x12\x19\n\x15StoredAnalogInterface\x10\x13\x12\x1b\n\x17StoredSetpointInterface\x10\x14\x12\x1a\n\x16StoredDigitalInterface\x10\x15\x12\x1e\n\x1a\x43onstrainedAnalogInterface\x10\x16\x12 \n\x1c\x43onstrainedSetpointInterface\x10\x17\x12\x1f\n\x1b\x43onstrainedDigitalInterface\x10\x18\x12\x1c\n\x18ScanningFactoryInterface\x10\x19\x12\x1c\n\x18I2CDiscoverableInterface\x10\x1a\x12\x14\n\x10\x44igitalInterface\x10\x1b\x12\x08\n\x03\x41ny\x10\xff\x01\x12\x0c\n\x07SysInfo\x10\x80\x02\x12\n\n\x05Ticks\x10\x81\x02\x12\x0f\n\nOneWireBus\x10\x82\x02\x12\x0e\n\tBoardPins\x10\x83\x02\x12\x13\n\x0eTempSensorMock\x10\xad\x02\x12\x16\n\x11TempSensorOneWire\x10\xae\x02\x12\x17\n\x12SetpointSensorPair\x10\xaf\x02\x12\x08\n\x03Pid\x10\xb0\x02\x12\x17\n\x12\x41\x63tuatorAnalogMock\x10\xb1\x02\x12\x10\n\x0b\x41\x63tuatorPin\x10\xb2\x02\x12\x10\n\x0b\x41\x63tuatorPwm\x10\xb3\x02\x12\x13\n\x0e\x41\x63tuatorOffset\x10\xb4\x02\x12\r\n\x08\x42\x61lancer\x10\xb5\x02\x12\n\n\x05Mutex\x10\xb6\x02\x12\x14\n\x0fSetpointProfile\x10\xb7\x02\x12\x11\n\x0cWiFiSettings\x10\xb8\x02\x12\x12\n\rTouchSettings\x10\xb9\x02\x12\x14\n\x0f\x44isplaySettings\x10\xba\x02\x12\x0b\n\x06\x44S2413\x10\xbb\x02\x12\x14\n\x0f\x41\x63tuatorOneWire\x10\xbc\x02\x12\x0b\n\x06\x44S2408\x10\xbd\x02\x12\x14\n\x0f\x44igitalActuator\x10\xbe\x02\x12\x0f\n\nSpark3Pins\x10\xbf\x02\x12\x0f\n\nSpark2Pins\x10\xc0\x02\x12\x0f\n\nMotorValve\x10\xc1\x02\x12\x12\n\rActuatorLogic\x10\xc2\x02\x12\r\n\x08MockPins\x10\xc3\x02\x12\x14\n\x0fTempSensorCombi\x10\xc4\x02\x12\x16\n\x11OneWireGpioModule\x10\xc5\x02\x12\r\n\x08Sequence\x10\xc6\x02\x12\x17\n\x12TempSensorExternal\x10\xc8\x02\x12\x0c\n\x07\x46\x61stPwm\x10\xc9\x02\x12\x11\n\x0c\x44igitalInput\x10\xca\x02:J\n\x05\x66ield\x12\x1d.google.protobuf.FieldOptions\x18\xd1\x86\x03 \x01(\x0b\x32\x13.brewblox.FieldOptsB\x05\x92?\x02\x18\x03:L\n\x03msg\x12\x1f.google.protobuf.MessageOptions\x18\xd1\x86\x03 \x01(\x0b\x32\x15.brewblox.MessageOptsB\x05\x92?\x02\x18\x03\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'brewblox_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - google_dot_protobuf_dot_descriptor__pb2.FieldOptions.RegisterExtension(field) - google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(msg) - DESCRIPTOR._options = None field._options = None field._serialized_options = b'\222?\002\030\003' @@ -37,12 +34,12 @@ _MESSAGEOPTS._serialized_options = b'\222?\0020\001' _FIELDOPTS._options = None _FIELDOPTS._serialized_options = b'\222?\0020\001' - _globals['_UNITTYPE']._serialized_start=450 - _globals['_UNITTYPE']._serialized_end=718 - _globals['_BLOCKTYPE']._serialized_start=721 - _globals['_BLOCKTYPE']._serialized_end=2057 + _globals['_UNITTYPE']._serialized_start=500 + _globals['_UNITTYPE']._serialized_end=768 + _globals['_BLOCKTYPE']._serialized_start=771 + _globals['_BLOCKTYPE']._serialized_end=2107 _globals['_MESSAGEOPTS']._serialized_start=76 _globals['_MESSAGEOPTS']._serialized_end=200 _globals['_FIELDOPTS']._serialized_start=203 - _globals['_FIELDOPTS']._serialized_end=447 + _globals['_FIELDOPTS']._serialized_end=497 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/command_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/command_pb2.py index 0ebf37dc..9fc30107 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/command_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/command_pb2.py @@ -21,7 +21,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'command_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None _PAYLOAD.fields_by_name['blockId']._options = None _PAYLOAD.fields_by_name['blockId']._serialized_options = b'\222?\0028\020' diff --git a/brewblox_devcon_spark/codec/proto-compiled/nanopb_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/nanopb_pb2.py index 17fc7dc5..51193fdc 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/nanopb_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/nanopb_pb2.py @@ -20,11 +20,6 @@ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'nanopb_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - google_dot_protobuf_dot_descriptor__pb2.FileOptions.RegisterExtension(nanopb_fileopt) - google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(nanopb_msgopt) - google_dot_protobuf_dot_descriptor__pb2.EnumOptions.RegisterExtension(nanopb_enumopt) - google_dot_protobuf_dot_descriptor__pb2.FieldOptions.RegisterExtension(nanopb) - DESCRIPTOR._options = None DESCRIPTOR._serialized_options = b'\n\030fi.kapsi.koti.jpa.nanopb' _globals['_FIELDTYPE']._serialized_start=985 diff --git a/firmware.ini b/firmware.ini index 2e497f07..b4b38464 100644 --- a/firmware.ini +++ b/firmware.ini @@ -1,7 +1,7 @@ [FIRMWARE] -firmware_version=1a38c071 -firmware_date=2023-10-30 -firmware_sha=1a38c071d1596b2f66322a7182b98e3608ab4f88 -proto_version=67da1ac7 -proto_date=2023-07-26 +firmware_version=fdd37845 +firmware_date=2023-12-05 +firmware_sha=fdd37845e5abc0e128606c5f3fbbfa39ab532411 +proto_version=e6177a67 +proto_date=2023-12-05 system_version=3.2.0 diff --git a/test/test_codec.py b/test/test_codec.py index 2de5fc50..e0d4ac8e 100644 --- a/test/test_codec.py +++ b/test/test_codec.py @@ -290,3 +290,19 @@ async def test_enum_decoding(app, client, cdc: Codec): payload = cdc.decode_payload(encoded_payload, opts=DecodeOpts(enums=ProtoEnumOpt.INT)) assert payload.content['storedState'] == 1 + + +async def test_invalid_if_decoding(app, client, cdc: Codec): + encoded_payload = cdc.encode_payload(DecodedPayload( + blockId=1, + blockType='EdgeCase', + content={ + 'listValues': [10, -1 / 256], # scaled + 'deltaV': 1 / 256, + }, + )) + + payload = cdc.decode_payload(encoded_payload) + assert payload.content['listValues'][0]['value'] == pytest.approx(10) + assert payload.content['listValues'][1]['value'] is None + assert payload.content['deltaV']['value'] is None From 387d79ba9ec8f5f070c81dc89506a27c3beb5026 Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Wed, 6 Dec 2023 15:58:28 +0100 Subject: [PATCH 18/27] use omit/null if zero instead --- brewblox-proto | 2 +- brewblox_devcon_spark/codec/processor.py | 17 ++++++++++++----- .../codec/proto-compiled/EdgeCase_pb2.py | 14 +++++++------- .../codec/proto-compiled/brewblox_pb2.py | 12 ++++++------ firmware.ini | 10 +++++----- test/test_codec.py | 8 +++++--- 6 files changed, 36 insertions(+), 27 deletions(-) diff --git a/brewblox-proto b/brewblox-proto index e6177a67..0fa3f6b2 160000 --- a/brewblox-proto +++ b/brewblox-proto @@ -1 +1 @@ -Subproject commit e6177a67ae25f0dd428f35abaa7f99b61d59af4e +Subproject commit 0fa3f6b24dee9d4165a95b2352b6475a0096644a diff --git a/brewblox_devcon_spark/codec/processor.py b/brewblox_devcon_spark/codec/processor.py index fd57ef76..38f348fc 100644 --- a/brewblox_devcon_spark/codec/processor.py +++ b/brewblox_devcon_spark/codec/processor.py @@ -348,14 +348,13 @@ def post_decode(self, qty_user_unit = self._converter.to_user_unit(qty_system_unit) def _convert_value(value: float | int | str) -> float | int | str | None: - invalid = value in [options.uint_invalid_if or None, - options.sint_invalid_if or None] + null_value = options.null_if_zero and value == 0 if options.scale: value /= options.scale if options.unit: - if excluded or invalid: + if excluded or null_value: value = None else: value = self._converter.to_user_value(value, qty_system_unit) @@ -373,7 +372,7 @@ def _convert_value(value: float | int | str) -> float | int | str | None: return value if options.objtype: - if excluded or invalid: + if excluded or null_value: value = None if opts.metadata == MetadataOpt.TYPED: @@ -385,7 +384,7 @@ def _convert_value(value: float | int | str) -> float | int | str | None: return value - if excluded or invalid: + if excluded or null_value: return None if options.hexed: @@ -412,6 +411,14 @@ def _convert_value(value: float | int | str) -> float | int | str | None: if options.unit: new_key = f'{element.key}[{qty_user_unit}]' + # Filter values that should be omitted entirely + if options.omit_if_zero: + if isinstance(new_value, (list, set)): + new_value = [v for v in new_value if v != 0] + elif new_value == 0: + del element.obj[element.key] + continue + # Convert value if isinstance(new_value, (list, set)): new_value = [_convert_value(v) for v in new_value] diff --git a/brewblox_devcon_spark/codec/proto-compiled/EdgeCase_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/EdgeCase_pb2.py index d618b85c..e24bc1d5 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/EdgeCase_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/EdgeCase_pb2.py @@ -15,7 +15,7 @@ import nanopb_pb2 as nanopb__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0e\x45\x64geCase.proto\x12\rblox.EdgeCase\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\">\n\x08Settings\x12\x17\n\x07\x61\x64\x64ress\x18\x01 \x01(\x06\x42\x06\x8a\xb5\x18\x02 \x01\x12\x19\n\x06offset\x18\x02 \x01(\x11\x42\t\x8a\xb5\x18\x05\x08\x06\x10\x80\x02\"<\n\x05State\x12\x18\n\x05value\x18\x01 \x01(\x11\x42\t\x8a\xb5\x18\x05\x08\x01\x10\x80\x02\x12\x19\n\tconnected\x18\x02 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\"(\n\nNestedLink\x12\x1a\n\nconnection\x18\x01 \x01(\rB\x06\x8a\xb5\x18\x02\x18\x02\"\xa2\x02\n\x05\x42lock\x12)\n\x08settings\x18\x01 \x01(\x0b\x32\x17.blox.EdgeCase.Settings\x12#\n\x05state\x18\x02 \x01(\x0b\x32\x14.blox.EdgeCase.State\x12\x14\n\x04link\x18\x03 \x01(\rB\x06\x8a\xb5\x18\x02\x18\x05\x12\x32\n\x0f\x61\x64\x64itionalLinks\x18\x04 \x03(\x0b\x32\x19.blox.EdgeCase.NestedLink\x12\x1f\n\nlistValues\x18\x05 \x03(\x11\x42\x0b\x8a\xb5\x18\x07\x08\x01\x10\x80\x02p\x01\x12\x1b\n\x06\x64\x65ltaV\x18\x06 \x01(\rB\x0b\x8a\xb5\x18\x07\x08\x07\x10\x80\x02h\x01\x12\x16\n\x06logged\x18\x07 \x01(\rB\x06\x8a\xb5\x18\x02\x30\x01\x12\x10\n\x08unLogged\x18\x08 \x01(\r\x12\x17\n\x02ip\x18\n \x01(\rB\x0b\x92?\x02\x38 \x8a\xb5\x18\x02`\x01\"#\n\x07SubCase\x12\x10\n\x08subvalue\x18\x01 \x01(\r:\x06\x8a\xb5\x18\x02X\x01\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0e\x45\x64geCase.proto\x12\rblox.EdgeCase\x1a\x0e\x62rewblox.proto\x1a\x0cnanopb.proto\">\n\x08Settings\x12\x17\n\x07\x61\x64\x64ress\x18\x01 \x01(\x06\x42\x06\x8a\xb5\x18\x02 \x01\x12\x19\n\x06offset\x18\x02 \x01(\x11\x42\t\x8a\xb5\x18\x05\x08\x06\x10\x80\x02\"<\n\x05State\x12\x18\n\x05value\x18\x01 \x01(\x11\x42\t\x8a\xb5\x18\x05\x08\x01\x10\x80\x02\x12\x19\n\tconnected\x18\x02 \x01(\x08\x42\x06\x8a\xb5\x18\x02(\x01\"(\n\nNestedLink\x12\x1a\n\nconnection\x18\x01 \x01(\rB\x06\x8a\xb5\x18\x02\x18\x02\"\xa4\x02\n\x05\x42lock\x12)\n\x08settings\x18\x01 \x01(\x0b\x32\x17.blox.EdgeCase.Settings\x12#\n\x05state\x18\x02 \x01(\x0b\x32\x14.blox.EdgeCase.State\x12\x14\n\x04link\x18\x03 \x01(\rB\x06\x8a\xb5\x18\x02\x18\x05\x12\x32\n\x0f\x61\x64\x64itionalLinks\x18\x04 \x03(\x0b\x32\x19.blox.EdgeCase.NestedLink\x12\x1f\n\nlistValues\x18\x05 \x03(\x11\x42\x0b\x8a\xb5\x18\x07\x08\x01\x10\x80\x02h\x01\x12\x1b\n\x06\x64\x65ltaV\x18\x06 \x01(\rB\x0b\x8a\xb5\x18\x07\x08\x07\x10\x80\x02p\x01\x12\x18\n\x06logged\x18\x07 \x01(\rB\x08\x8a\xb5\x18\x04\x30\x01h\x01\x12\x10\n\x08unLogged\x18\x08 \x01(\r\x12\x17\n\x02ip\x18\n \x01(\rB\x0b\x92?\x02\x38 \x8a\xb5\x18\x02`\x01\"#\n\x07SubCase\x12\x10\n\x08subvalue\x18\x01 \x01(\r:\x06\x8a\xb5\x18\x02X\x01\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -35,11 +35,11 @@ _BLOCK.fields_by_name['link']._options = None _BLOCK.fields_by_name['link']._serialized_options = b'\212\265\030\002\030\005' _BLOCK.fields_by_name['listValues']._options = None - _BLOCK.fields_by_name['listValues']._serialized_options = b'\212\265\030\007\010\001\020\200\002p\001' + _BLOCK.fields_by_name['listValues']._serialized_options = b'\212\265\030\007\010\001\020\200\002h\001' _BLOCK.fields_by_name['deltaV']._options = None - _BLOCK.fields_by_name['deltaV']._serialized_options = b'\212\265\030\007\010\007\020\200\002h\001' + _BLOCK.fields_by_name['deltaV']._serialized_options = b'\212\265\030\007\010\007\020\200\002p\001' _BLOCK.fields_by_name['logged']._options = None - _BLOCK.fields_by_name['logged']._serialized_options = b'\212\265\030\0020\001' + _BLOCK.fields_by_name['logged']._serialized_options = b'\212\265\030\0040\001h\001' _BLOCK.fields_by_name['ip']._options = None _BLOCK.fields_by_name['ip']._serialized_options = b'\222?\0028 \212\265\030\002`\001' _SUBCASE._options = None @@ -51,7 +51,7 @@ _globals['_NESTEDLINK']._serialized_start=189 _globals['_NESTEDLINK']._serialized_end=229 _globals['_BLOCK']._serialized_start=232 - _globals['_BLOCK']._serialized_end=522 - _globals['_SUBCASE']._serialized_start=524 - _globals['_SUBCASE']._serialized_end=559 + _globals['_BLOCK']._serialized_end=524 + _globals['_SUBCASE']._serialized_start=526 + _globals['_SUBCASE']._serialized_end=561 # @@protoc_insertion_point(module_scope) diff --git a/brewblox_devcon_spark/codec/proto-compiled/brewblox_pb2.py b/brewblox_devcon_spark/codec/proto-compiled/brewblox_pb2.py index 3e07e734..1521c4c6 100644 --- a/brewblox_devcon_spark/codec/proto-compiled/brewblox_pb2.py +++ b/brewblox_devcon_spark/codec/proto-compiled/brewblox_pb2.py @@ -15,7 +15,7 @@ import nanopb_pb2 as nanopb__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0e\x62rewblox.proto\x12\x08\x62rewblox\x1a google/protobuf/descriptor.proto\x1a\x0cnanopb.proto\"|\n\x0bMessageOpts\x12$\n\x07objtype\x18\x03 \x01(\x0e\x32\x13.brewblox.BlockType\x12(\n\x04impl\x18\t \x03(\x0e\x32\x13.brewblox.BlockTypeB\x05\x92?\x02\x10\x05\x12\x16\n\x07subtype\x18\x0b \x01(\rB\x05\x92?\x02\x38\x10:\x05\x92?\x02\x30\x01\"\xa6\x02\n\tFieldOpts\x12 \n\x04unit\x18\x01 \x01(\x0e\x32\x12.brewblox.UnitType\x12\r\n\x05scale\x18\x02 \x01(\r\x12$\n\x07objtype\x18\x03 \x01(\x0e\x32\x13.brewblox.BlockType\x12\r\n\x05hexed\x18\x04 \x01(\x08\x12\x10\n\x08readonly\x18\x05 \x01(\x08\x12\x0e\n\x06logged\x18\x06 \x01(\x08\x12\x0e\n\x06hexstr\x18\x07 \x01(\x08\x12\x0f\n\x07ignored\x18\t \x01(\x08\x12\x10\n\x08\x62itfield\x18\n \x01(\x08\x12\x10\n\x08\x64\x61tetime\x18\x0b \x01(\x08\x12\x13\n\x0bipv4address\x18\x0c \x01(\x08\x12\x17\n\x0fuint_invalid_if\x18\r \x01(\r\x12\x17\n\x0fsint_invalid_if\x18\x0e \x01(\x11:\x05\x92?\x02\x30\x01*\x8c\x02\n\x08UnitType\x12\n\n\x06NotSet\x10\x00\x12\x0b\n\x07\x43\x65lsius\x10\x01\x12\x12\n\x0eInverseCelsius\x10\x02\x12\n\n\x06Second\x10\x03\x12\n\n\x06Minute\x10\x04\x12\x08\n\x04Hour\x10\x05\x12\x10\n\x0c\x44\x65ltaCelsius\x10\x06\x12\x19\n\x15\x44\x65ltaCelsiusPerSecond\x10\x07\x12\x19\n\x15\x44\x65ltaCelsiusPerMinute\x10\x08\x12\x17\n\x13\x44\x65ltaCelsiusPerHour\x10\t\x12\x1a\n\x16\x44\x65ltaCelsiusMultSecond\x10\n\x12\x1a\n\x16\x44\x65ltaCelsiusMultMinute\x10\x0b\x12\x18\n\x14\x44\x65ltaCelsiusMultHour\x10\x0c*\xb8\n\n\tBlockType\x12\x0b\n\x07Invalid\x10\x00\x12\x19\n\x15ProcessValueInterface\x10\x01\x12\x17\n\x13TempSensorInterface\x10\x02\x12\x1f\n\x1bSetpointSensorPairInterface\x10\x04\x12\x1b\n\x17\x41\x63tuatorAnalogInterface\x10\x05\x12\x1c\n\x18\x41\x63tuatorDigitalInterface\x10\x06\x12\x15\n\x11\x42\x61lancerInterface\x10\x07\x12\x12\n\x0eMutexInterface\x10\x08\x12\x1a\n\x16OneWireDeviceInterface\x10\t\x12\x14\n\x10IoArrayInterface\x10\n\x12\x13\n\x0f\x44S2408Interface\x10\x0b\x12\x17\n\x13OneWireBusInterface\x10\x0c\x12\x15\n\x11IoModuleInterface\x10\r\x12\x1f\n\x1bOneWireDeviceBlockInterface\x10\x0e\x12\x14\n\x10\x45nablerInterface\x10\x0f\x12\x16\n\x12\x43laimableInterface\x10\x10\x12\x15\n\x11IoDriverInterface\x10\x11\x12\x15\n\x11SetpointInterface\x10\x12\x12\x19\n\x15StoredAnalogInterface\x10\x13\x12\x1b\n\x17StoredSetpointInterface\x10\x14\x12\x1a\n\x16StoredDigitalInterface\x10\x15\x12\x1e\n\x1a\x43onstrainedAnalogInterface\x10\x16\x12 \n\x1c\x43onstrainedSetpointInterface\x10\x17\x12\x1f\n\x1b\x43onstrainedDigitalInterface\x10\x18\x12\x1c\n\x18ScanningFactoryInterface\x10\x19\x12\x1c\n\x18I2CDiscoverableInterface\x10\x1a\x12\x14\n\x10\x44igitalInterface\x10\x1b\x12\x08\n\x03\x41ny\x10\xff\x01\x12\x0c\n\x07SysInfo\x10\x80\x02\x12\n\n\x05Ticks\x10\x81\x02\x12\x0f\n\nOneWireBus\x10\x82\x02\x12\x0e\n\tBoardPins\x10\x83\x02\x12\x13\n\x0eTempSensorMock\x10\xad\x02\x12\x16\n\x11TempSensorOneWire\x10\xae\x02\x12\x17\n\x12SetpointSensorPair\x10\xaf\x02\x12\x08\n\x03Pid\x10\xb0\x02\x12\x17\n\x12\x41\x63tuatorAnalogMock\x10\xb1\x02\x12\x10\n\x0b\x41\x63tuatorPin\x10\xb2\x02\x12\x10\n\x0b\x41\x63tuatorPwm\x10\xb3\x02\x12\x13\n\x0e\x41\x63tuatorOffset\x10\xb4\x02\x12\r\n\x08\x42\x61lancer\x10\xb5\x02\x12\n\n\x05Mutex\x10\xb6\x02\x12\x14\n\x0fSetpointProfile\x10\xb7\x02\x12\x11\n\x0cWiFiSettings\x10\xb8\x02\x12\x12\n\rTouchSettings\x10\xb9\x02\x12\x14\n\x0f\x44isplaySettings\x10\xba\x02\x12\x0b\n\x06\x44S2413\x10\xbb\x02\x12\x14\n\x0f\x41\x63tuatorOneWire\x10\xbc\x02\x12\x0b\n\x06\x44S2408\x10\xbd\x02\x12\x14\n\x0f\x44igitalActuator\x10\xbe\x02\x12\x0f\n\nSpark3Pins\x10\xbf\x02\x12\x0f\n\nSpark2Pins\x10\xc0\x02\x12\x0f\n\nMotorValve\x10\xc1\x02\x12\x12\n\rActuatorLogic\x10\xc2\x02\x12\r\n\x08MockPins\x10\xc3\x02\x12\x14\n\x0fTempSensorCombi\x10\xc4\x02\x12\x16\n\x11OneWireGpioModule\x10\xc5\x02\x12\r\n\x08Sequence\x10\xc6\x02\x12\x17\n\x12TempSensorExternal\x10\xc8\x02\x12\x0c\n\x07\x46\x61stPwm\x10\xc9\x02\x12\x11\n\x0c\x44igitalInput\x10\xca\x02:J\n\x05\x66ield\x12\x1d.google.protobuf.FieldOptions\x18\xd1\x86\x03 \x01(\x0b\x32\x13.brewblox.FieldOptsB\x05\x92?\x02\x18\x03:L\n\x03msg\x12\x1f.google.protobuf.MessageOptions\x18\xd1\x86\x03 \x01(\x0b\x32\x15.brewblox.MessageOptsB\x05\x92?\x02\x18\x03\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0e\x62rewblox.proto\x12\x08\x62rewblox\x1a google/protobuf/descriptor.proto\x1a\x0cnanopb.proto\"|\n\x0bMessageOpts\x12$\n\x07objtype\x18\x03 \x01(\x0e\x32\x13.brewblox.BlockType\x12(\n\x04impl\x18\t \x03(\x0e\x32\x13.brewblox.BlockTypeB\x05\x92?\x02\x10\x05\x12\x16\n\x07subtype\x18\x0b \x01(\rB\x05\x92?\x02\x38\x10:\x05\x92?\x02\x30\x01\"\xa0\x02\n\tFieldOpts\x12 \n\x04unit\x18\x01 \x01(\x0e\x32\x12.brewblox.UnitType\x12\r\n\x05scale\x18\x02 \x01(\r\x12$\n\x07objtype\x18\x03 \x01(\x0e\x32\x13.brewblox.BlockType\x12\r\n\x05hexed\x18\x04 \x01(\x08\x12\x10\n\x08readonly\x18\x05 \x01(\x08\x12\x0e\n\x06logged\x18\x06 \x01(\x08\x12\x0e\n\x06hexstr\x18\x07 \x01(\x08\x12\x0f\n\x07ignored\x18\t \x01(\x08\x12\x10\n\x08\x62itfield\x18\n \x01(\x08\x12\x10\n\x08\x64\x61tetime\x18\x0b \x01(\x08\x12\x13\n\x0bipv4address\x18\x0c \x01(\x08\x12\x14\n\x0comit_if_zero\x18\r \x01(\x08\x12\x14\n\x0cnull_if_zero\x18\x0e \x01(\x08:\x05\x92?\x02\x30\x01*\x8c\x02\n\x08UnitType\x12\n\n\x06NotSet\x10\x00\x12\x0b\n\x07\x43\x65lsius\x10\x01\x12\x12\n\x0eInverseCelsius\x10\x02\x12\n\n\x06Second\x10\x03\x12\n\n\x06Minute\x10\x04\x12\x08\n\x04Hour\x10\x05\x12\x10\n\x0c\x44\x65ltaCelsius\x10\x06\x12\x19\n\x15\x44\x65ltaCelsiusPerSecond\x10\x07\x12\x19\n\x15\x44\x65ltaCelsiusPerMinute\x10\x08\x12\x17\n\x13\x44\x65ltaCelsiusPerHour\x10\t\x12\x1a\n\x16\x44\x65ltaCelsiusMultSecond\x10\n\x12\x1a\n\x16\x44\x65ltaCelsiusMultMinute\x10\x0b\x12\x18\n\x14\x44\x65ltaCelsiusMultHour\x10\x0c*\xb8\n\n\tBlockType\x12\x0b\n\x07Invalid\x10\x00\x12\x19\n\x15ProcessValueInterface\x10\x01\x12\x17\n\x13TempSensorInterface\x10\x02\x12\x1f\n\x1bSetpointSensorPairInterface\x10\x04\x12\x1b\n\x17\x41\x63tuatorAnalogInterface\x10\x05\x12\x1c\n\x18\x41\x63tuatorDigitalInterface\x10\x06\x12\x15\n\x11\x42\x61lancerInterface\x10\x07\x12\x12\n\x0eMutexInterface\x10\x08\x12\x1a\n\x16OneWireDeviceInterface\x10\t\x12\x14\n\x10IoArrayInterface\x10\n\x12\x13\n\x0f\x44S2408Interface\x10\x0b\x12\x17\n\x13OneWireBusInterface\x10\x0c\x12\x15\n\x11IoModuleInterface\x10\r\x12\x1f\n\x1bOneWireDeviceBlockInterface\x10\x0e\x12\x14\n\x10\x45nablerInterface\x10\x0f\x12\x16\n\x12\x43laimableInterface\x10\x10\x12\x15\n\x11IoDriverInterface\x10\x11\x12\x15\n\x11SetpointInterface\x10\x12\x12\x19\n\x15StoredAnalogInterface\x10\x13\x12\x1b\n\x17StoredSetpointInterface\x10\x14\x12\x1a\n\x16StoredDigitalInterface\x10\x15\x12\x1e\n\x1a\x43onstrainedAnalogInterface\x10\x16\x12 \n\x1c\x43onstrainedSetpointInterface\x10\x17\x12\x1f\n\x1b\x43onstrainedDigitalInterface\x10\x18\x12\x1c\n\x18ScanningFactoryInterface\x10\x19\x12\x1c\n\x18I2CDiscoverableInterface\x10\x1a\x12\x14\n\x10\x44igitalInterface\x10\x1b\x12\x08\n\x03\x41ny\x10\xff\x01\x12\x0c\n\x07SysInfo\x10\x80\x02\x12\n\n\x05Ticks\x10\x81\x02\x12\x0f\n\nOneWireBus\x10\x82\x02\x12\x0e\n\tBoardPins\x10\x83\x02\x12\x13\n\x0eTempSensorMock\x10\xad\x02\x12\x16\n\x11TempSensorOneWire\x10\xae\x02\x12\x17\n\x12SetpointSensorPair\x10\xaf\x02\x12\x08\n\x03Pid\x10\xb0\x02\x12\x17\n\x12\x41\x63tuatorAnalogMock\x10\xb1\x02\x12\x10\n\x0b\x41\x63tuatorPin\x10\xb2\x02\x12\x10\n\x0b\x41\x63tuatorPwm\x10\xb3\x02\x12\x13\n\x0e\x41\x63tuatorOffset\x10\xb4\x02\x12\r\n\x08\x42\x61lancer\x10\xb5\x02\x12\n\n\x05Mutex\x10\xb6\x02\x12\x14\n\x0fSetpointProfile\x10\xb7\x02\x12\x11\n\x0cWiFiSettings\x10\xb8\x02\x12\x12\n\rTouchSettings\x10\xb9\x02\x12\x14\n\x0f\x44isplaySettings\x10\xba\x02\x12\x0b\n\x06\x44S2413\x10\xbb\x02\x12\x14\n\x0f\x41\x63tuatorOneWire\x10\xbc\x02\x12\x0b\n\x06\x44S2408\x10\xbd\x02\x12\x14\n\x0f\x44igitalActuator\x10\xbe\x02\x12\x0f\n\nSpark3Pins\x10\xbf\x02\x12\x0f\n\nSpark2Pins\x10\xc0\x02\x12\x0f\n\nMotorValve\x10\xc1\x02\x12\x12\n\rActuatorLogic\x10\xc2\x02\x12\r\n\x08MockPins\x10\xc3\x02\x12\x14\n\x0fTempSensorCombi\x10\xc4\x02\x12\x16\n\x11OneWireGpioModule\x10\xc5\x02\x12\r\n\x08Sequence\x10\xc6\x02\x12\x17\n\x12TempSensorExternal\x10\xc8\x02\x12\x0c\n\x07\x46\x61stPwm\x10\xc9\x02\x12\x11\n\x0c\x44igitalInput\x10\xca\x02:J\n\x05\x66ield\x12\x1d.google.protobuf.FieldOptions\x18\xd1\x86\x03 \x01(\x0b\x32\x13.brewblox.FieldOptsB\x05\x92?\x02\x18\x03:L\n\x03msg\x12\x1f.google.protobuf.MessageOptions\x18\xd1\x86\x03 \x01(\x0b\x32\x15.brewblox.MessageOptsB\x05\x92?\x02\x18\x03\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -34,12 +34,12 @@ _MESSAGEOPTS._serialized_options = b'\222?\0020\001' _FIELDOPTS._options = None _FIELDOPTS._serialized_options = b'\222?\0020\001' - _globals['_UNITTYPE']._serialized_start=500 - _globals['_UNITTYPE']._serialized_end=768 - _globals['_BLOCKTYPE']._serialized_start=771 - _globals['_BLOCKTYPE']._serialized_end=2107 + _globals['_UNITTYPE']._serialized_start=494 + _globals['_UNITTYPE']._serialized_end=762 + _globals['_BLOCKTYPE']._serialized_start=765 + _globals['_BLOCKTYPE']._serialized_end=2101 _globals['_MESSAGEOPTS']._serialized_start=76 _globals['_MESSAGEOPTS']._serialized_end=200 _globals['_FIELDOPTS']._serialized_start=203 - _globals['_FIELDOPTS']._serialized_end=497 + _globals['_FIELDOPTS']._serialized_end=491 # @@protoc_insertion_point(module_scope) diff --git a/firmware.ini b/firmware.ini index b4b38464..8ba0e38d 100644 --- a/firmware.ini +++ b/firmware.ini @@ -1,7 +1,7 @@ [FIRMWARE] -firmware_version=fdd37845 -firmware_date=2023-12-05 -firmware_sha=fdd37845e5abc0e128606c5f3fbbfa39ab532411 -proto_version=e6177a67 -proto_date=2023-12-05 +firmware_version=eedeeb7f +firmware_date=2023-12-06 +firmware_sha=eedeeb7f86a07b2696e9197195d891836c102ef3 +proto_version=0fa3f6b2 +proto_date=2023-12-06 system_version=3.2.0 diff --git a/test/test_codec.py b/test/test_codec.py index e0d4ac8e..8c9c516d 100644 --- a/test/test_codec.py +++ b/test/test_codec.py @@ -297,12 +297,14 @@ async def test_invalid_if_decoding(app, client, cdc: Codec): blockId=1, blockType='EdgeCase', content={ - 'listValues': [10, -1 / 256], # scaled - 'deltaV': 1 / 256, + 'listValues': [0, 10], # omit if zero + 'deltaV': 0, # null if zero + 'logged': 0, # omit if zero }, )) payload = cdc.decode_payload(encoded_payload) + assert len(payload.content['listValues']) == 1 assert payload.content['listValues'][0]['value'] == pytest.approx(10) - assert payload.content['listValues'][1]['value'] is None assert payload.content['deltaV']['value'] is None + assert 'logged' not in payload.content From 710c2c6f13e547d3d314b13a080b5f6cd3e2e1e0 Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Wed, 6 Dec 2023 16:14:46 +0100 Subject: [PATCH 19/27] pull develop firmware --- firmware.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware.ini b/firmware.ini index 8ba0e38d..48168875 100644 --- a/firmware.ini +++ b/firmware.ini @@ -1,7 +1,7 @@ [FIRMWARE] -firmware_version=eedeeb7f +firmware_version=f27f141c firmware_date=2023-12-06 -firmware_sha=eedeeb7f86a07b2696e9197195d891836c102ef3 +firmware_sha=f27f141cb66c348afb6735ed08a60d1814791b71 proto_version=0fa3f6b2 proto_date=2023-12-06 system_version=3.2.0 From 37618093e96d4b979390b4be7d61316d21f0439b Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Thu, 4 Jan 2024 13:54:58 +0100 Subject: [PATCH 20/27] Convert from aiohttp to FastAPI (#592) * WIP fastapi refactor * WIP model tweaks * WIP datastore rework * refactor modules * make runnable * conftest setup * deactivate tests * WIP test refactoring * simplify datastore handling * autodetect name; naming refactor * WIP test fixes * moved timing config to service config * convert integration tests * improve name consistency * get test coverage back to 100 * read firmware from root in tasks * parse all relevant options in parse_appenv.py * immediately raise error if incompatible controller * pull firmware with quote fix * don't mock fw config --- .gitignore | 2 + Dockerfile.service | 4 +- README.md | 12 +- brewblox_devcon_spark/__main__.py | 181 --- brewblox_devcon_spark/api/backup_api.py | 194 +-- brewblox_devcon_spark/api/blocks_api.py | 589 +++----- brewblox_devcon_spark/api/blocks_mqtt_api.py | 40 + brewblox_devcon_spark/api/debug_api.py | 175 +-- brewblox_devcon_spark/api/error_response.py | 45 - brewblox_devcon_spark/api/mqtt_api.py | 67 - brewblox_devcon_spark/api/settings_api.py | 60 +- brewblox_devcon_spark/api/sim_api.py | 82 +- brewblox_devcon_spark/api/system_api.py | 272 ++-- brewblox_devcon_spark/app_factory.py | 146 ++ brewblox_devcon_spark/backup_storage.py | 104 -- brewblox_devcon_spark/block_analysis.py | 41 +- brewblox_devcon_spark/block_backup.py | 99 ++ brewblox_devcon_spark/block_store.py | 129 -- brewblox_devcon_spark/broadcast.py | 82 ++ brewblox_devcon_spark/broadcaster.py | 92 -- brewblox_devcon_spark/codec/__init__.py | 98 +- brewblox_devcon_spark/codec/lookup.py | 81 +- brewblox_devcon_spark/codec/processor.py | 10 +- .../codec/unit_conversion.py | 33 +- .../{commander.py => command.py} | 113 +- brewblox_devcon_spark/connection/__init__.py | 21 +- .../connection/cbox_parser.py | 7 +- .../connection/connection_handler.py | 150 +- .../connection/connection_impl.py | 3 - .../connection/mock_connection.py | 50 +- .../connection/mqtt_connection.py | 135 +- .../connection/stream_connection.py | 114 +- brewblox_devcon_spark/const.py | 3 +- .../{controller.py => control.py} | 177 ++- brewblox_devcon_spark/datastore.py | 92 -- brewblox_devcon_spark/datastore_blocks.py | 130 ++ brewblox_devcon_spark/datastore_settings.py | 152 ++ brewblox_devcon_spark/exceptions.py | 35 +- brewblox_devcon_spark/global_store.py | 98 -- brewblox_devcon_spark/mdns.py | 24 +- brewblox_devcon_spark/models.py | 401 ++++-- brewblox_devcon_spark/mqtt.py | 34 + brewblox_devcon_spark/service_status.py | 256 ---- brewblox_devcon_spark/service_store.py | 129 -- brewblox_devcon_spark/state_machine.py | 209 +++ brewblox_devcon_spark/synchronization.py | 197 ++- brewblox_devcon_spark/time_sync.py | 82 +- brewblox_devcon_spark/twinkeydict.py | 7 +- brewblox_devcon_spark/utils.py | 257 ++++ brewblox_devcon_spark/ymodem.py | 31 +- docker-compose.yml | 43 + entrypoint.sh | 10 + firmware.ini | 6 +- parse_appenv.py | 40 + poetry.lock | 1280 +++++++++-------- pyproject.toml | 67 +- tasks.py | 49 +- test/conftest.py | 391 ++--- test/docker-compose.yml | 23 + test/test_app_factory.py | 24 + test/test_backup_storage.py | 88 -- test/test_block_analysis.py | 96 +- test/test_block_backup.py | 107 ++ test/test_block_store.py | 150 -- test/test_blocks_mqtt_api.py | 160 +++ test/test_broadcast.py | 118 ++ test/test_broadcaster.py | 128 -- test/test_cbox_parser.py | 6 +- test/test_codec.py | 85 +- test/test_command.py | 97 ++ test/test_commander.py | 80 -- test/test_connection_handler.py | 175 ++- test/{test_controller.py => test_control.py} | 134 +- test/test_datastore.py | 64 - test/test_datastore_blocks.py | 128 ++ test/test_datastore_settings.py | 172 +++ test/test_global_store.py | 123 -- test/test_integration.py | 844 ++++++----- test/test_main.py | 72 - test/test_mdns.py | 27 +- test/test_mqtt_api.py | 103 -- test/test_mqtt_connection.py | 129 +- test/test_processor.py | 17 +- test/test_service_status.py | 139 -- test/test_service_store.py | 117 -- test/test_sim_api.py | 52 - test/test_state_machine.py | 135 ++ test/test_stream_connection.py | 75 +- test/test_synchronization.py | 207 ++- test/test_time_sync.py | 107 +- test/test_twinkeydict.py | 6 +- test/test_unit_conversion.py | 12 +- test/test_utils.py | 70 + tox.ini | 33 - 94 files changed, 5922 insertions(+), 5612 deletions(-) delete mode 100644 brewblox_devcon_spark/__main__.py create mode 100644 brewblox_devcon_spark/api/blocks_mqtt_api.py delete mode 100644 brewblox_devcon_spark/api/error_response.py delete mode 100644 brewblox_devcon_spark/api/mqtt_api.py create mode 100644 brewblox_devcon_spark/app_factory.py delete mode 100644 brewblox_devcon_spark/backup_storage.py create mode 100644 brewblox_devcon_spark/block_backup.py delete mode 100644 brewblox_devcon_spark/block_store.py create mode 100644 brewblox_devcon_spark/broadcast.py delete mode 100644 brewblox_devcon_spark/broadcaster.py rename brewblox_devcon_spark/{commander.py => command.py} (76%) rename brewblox_devcon_spark/{controller.py => control.py} (82%) delete mode 100644 brewblox_devcon_spark/datastore.py create mode 100644 brewblox_devcon_spark/datastore_blocks.py create mode 100644 brewblox_devcon_spark/datastore_settings.py delete mode 100644 brewblox_devcon_spark/global_store.py create mode 100644 brewblox_devcon_spark/mqtt.py delete mode 100644 brewblox_devcon_spark/service_status.py delete mode 100644 brewblox_devcon_spark/service_store.py create mode 100644 brewblox_devcon_spark/state_machine.py create mode 100644 brewblox_devcon_spark/utils.py create mode 100644 docker-compose.yml create mode 100644 entrypoint.sh create mode 100644 parse_appenv.py create mode 100644 test/docker-compose.yml create mode 100644 test/test_app_factory.py delete mode 100644 test/test_backup_storage.py create mode 100644 test/test_block_backup.py delete mode 100644 test/test_block_store.py create mode 100644 test/test_blocks_mqtt_api.py create mode 100644 test/test_broadcast.py delete mode 100644 test/test_broadcaster.py create mode 100644 test/test_command.py delete mode 100644 test/test_commander.py rename test/{test_controller.py => test_control.py} (60%) delete mode 100644 test/test_datastore.py create mode 100644 test/test_datastore_blocks.py create mode 100644 test/test_datastore_settings.py delete mode 100644 test/test_global_store.py delete mode 100644 test/test_main.py delete mode 100644 test/test_mqtt_api.py delete mode 100644 test/test_service_status.py delete mode 100644 test/test_service_store.py delete mode 100644 test/test_sim_api.py create mode 100644 test/test_state_machine.py create mode 100644 test/test_utils.py delete mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index 5d0a0ffa..252d3383 100644 --- a/.gitignore +++ b/.gitignore @@ -71,3 +71,5 @@ eeprom.bin # Build files firmware/ + +.appenv diff --git a/Dockerfile.service b/Dockerfile.service index 2bc9257d..a84fd3a5 100644 --- a/Dockerfile.service +++ b/Dockerfile.service @@ -42,6 +42,8 @@ ENV PATH="$VENV/bin:$PATH" COPY --from=base /wheeley /wheeley COPY --from=base /app/firmware /app/firmware +COPY ./parse_appenv.py ./parse_appenv.py +COPY ./entrypoint.sh ./entrypoint.sh RUN <. +To set up the development environment, follow the instructions at . -Integration tests run against the firmware simulator. +## Tests + +Integration tests run against the firmware simulator. This firmware is downloaded, but not committed. +To ensure you have the latest firmware, run `invoke download-firmware`. +This also happens during `invoke update-firmware`. + +During tests, you may need to kill the pytest process. +This will cause it to skip test teardown, and leave leftover containers and processes. \ +To remove these leftovers, run `invoke testclean`. ## Firmware diff --git a/brewblox_devcon_spark/__main__.py b/brewblox_devcon_spark/__main__.py deleted file mode 100644 index 2c94bf2c..00000000 --- a/brewblox_devcon_spark/__main__.py +++ /dev/null @@ -1,181 +0,0 @@ -""" -Example of how to import and use the brewblox service -""" - -import json -from configparser import ConfigParser -from os import getenv - -from brewblox_service import brewblox_logger, http, mqtt, scheduler, service - -from brewblox_devcon_spark import (backup_storage, block_store, broadcaster, - codec, commander, connection, controller, - global_store, service_status, service_store, - synchronization, time_sync) -from brewblox_devcon_spark.api import (backup_api, blocks_api, debug_api, - error_response, mqtt_api, settings_api, - sim_api, system_api) -from brewblox_devcon_spark.models import (DiscoveryType, ServiceConfig, - ServiceFirmwareIni) - -FIRMWARE_INI = 'firmware/firmware.ini' -LOGGER = brewblox_logger(__name__) - - -def create_parser(): - parser = service.create_parser('spark') - - # Device options - group = parser.add_argument_group('Device communication') - group.add_argument('--simulation', - help='Start in simulation mode. Will not connect to a physical device. ' - 'This option takes precedence over other connection options except --mock. ' - 'The simulator is assigned the --device-id value if set or 123456789012345678901234. ' - 'If you are using multiple simulators or mocks, you need to assign them unique device IDs. ' - '[%(default)s]', - action='store_true') - group.add_argument('--mock', - help='Start in mocked mode. Will not connect to a controller or simulation process. ' - 'This option takes precedence over other connection options and --simulation. ' - 'The mock is assigned the --device-id value if set or 123456789012345678901234. ' - 'If you are using multiple simulators or mocks, you need to assign them unique device IDs. ' - '[%(default)s]', - action='store_true') - group.add_argument('--device-host', - help='Spark device URL host. ' - 'Will only connect if device ID matches advertised ID, or is not set. [%(default)s]') - group.add_argument('--device-port', - help='Spark device URL port when accessing a device over WiFi. [%(default)s]', - type=int, - default=8332) - group.add_argument('--device-serial', - help='Spark device serial port. Takes precedence over URL connections. ' - 'Will only connect if device ID matches advertised ID, or is not set. [%(default)s]') - group.add_argument('--device-id', - help='Spark serial number. Any spark is valid if not set. [%(default)s]', - type=str.lower) - group.add_argument('--discovery', - help='Enabled types of device discovery. ' - '--device-serial and --device-host disable discovery. ' - '--device-id specifies which discovered device is valid. ', - type=lambda s: DiscoveryType[s], - choices=list(DiscoveryType), - default=DiscoveryType.all) - group.add_argument('--display-ws-port', - help='Websocket port for the Spark simulation virtual display stream. [$(default)s]', - type=int, - default=7377) - - # Service network options - group = parser.add_argument_group('Service communication') - group.add_argument('--command-timeout', - help='Timeout period (in seconds) for controller commands. [$(default)s]', - type=float, - default=20) - group.add_argument('--broadcast-interval', - help='Interval (in seconds) between broadcasts of controller state. ' - 'Set to a value <= 0 to disable broadcasting. [%(default)s]', - type=float, - default=5) - group.add_argument('--isolated', - action='store_true', - help='Disable all outgoing network calls. [%(default)s]') - group.add_argument('--datastore-topic', - help='Synchronization topic for datastore updates', - default='brewcast/datastore') - - # Updater options - group = parser.add_argument_group('Firmware') - group.add_argument('--skip-version-check', - help='Skip firmware version check: will not raise error on mismatch', - action='store_true') - - # Backup options - group = parser.add_argument_group('Backup') - group.add_argument('--backup-interval', - help='Interval (in seconds) between backups of controller state. ' - 'Set to a value <= 0 to disable. [%(default)s]', - type=float, - default=3600) - group.add_argument('--backup-retry-interval', - help='Interval (in seconds) between backups of controller state ' - 'after startup, or after a failed backup. ' - 'Set to a value <= 0 to always use the value of --backup-interval. [%(default)s]', - type=float, - default=300) - - # Time sync options - group = parser.add_argument_group('Time Sync') - group.add_argument('--time-sync-interval', - help='Interval (in seconds) between sending UTC time to the controller. ' - 'Set to a value <= 0 to disable. [%(default)s]', - type=float, - default=900) - - return parser - - -def parse_ini(app) -> ServiceFirmwareIni: - parser = ConfigParser() - parser.read(FIRMWARE_INI) - config = ServiceFirmwareIni(parser['FIRMWARE'].items()) - LOGGER.info(f'firmware.ini: {config}') - return config - - -def main(): - parser = create_parser() - config = service.create_config(parser, model=ServiceConfig) - app = service.create_app(config) - app['ini'] = parse_ini(app) - - if getenv('ENABLE_DEBUGGER', False): # pragma: no cover - import debugpy - debugpy.listen(('0.0.0.0', 5678)) - LOGGER.info('Debugger is enabled and listening on 5678') - - if config.device_id is None and (config.simulation or config.mock): - config.device_id = '123456789012345678901234' - - async def setup(): - scheduler.setup(app) - http.setup(app) - mqtt.setup(app, - client_will=mqtt.Will( - topic=f'{config.state_topic}/{config.name}', - payload=json.dumps({ - 'key': config.name, - 'type': 'Spark.state', - 'data': None, - }) - )) - - global_store.setup(app) - service_store.setup(app) - block_store.setup(app) - - service_status.setup(app) - codec.setup(app) - connection.setup(app) - commander.setup(app) - synchronization.setup(app) - controller.setup(app) - - backup_storage.setup(app) - broadcaster.setup(app) - time_sync.setup(app) - - error_response.setup(app) - blocks_api.setup(app) - system_api.setup(app) - settings_api.setup(app) - mqtt_api.setup(app) - sim_api.setup(app) - backup_api.setup(app) - debug_api.setup(app) - - service.run_app(app, setup()) - - -if __name__ == '__main__': - main() diff --git a/brewblox_devcon_spark/api/backup_api.py b/brewblox_devcon_spark/api/backup_api.py index b82bb789..a8e298a8 100644 --- a/brewblox_devcon_spark/api/backup_api.py +++ b/brewblox_devcon_spark/api/backup_api.py @@ -3,123 +3,77 @@ """ -from aiohttp import web -from aiohttp_pydantic import PydanticView -from aiohttp_pydantic.oas.typing import r200 -from brewblox_service import brewblox_logger - -from brewblox_devcon_spark import backup_storage -from brewblox_devcon_spark.models import (Backup, BackupApplyResult, - BackupIdentity) - -LOGGER = brewblox_logger(__name__) -routes = web.RouteTableDef() - - -def setup(app: web.Application): - app.router.add_routes(routes) - - -class BackupView(PydanticView): - def __init__(self, request: web.Request) -> None: - super().__init__(request) - self.app = request.app - self.storage = backup_storage.fget(request.app) - - -@routes.view('/blocks/backup/save') -class BackupSaveView(BackupView): - async def post(self) -> r200[Backup]: - """ - Export service blocks to a portable format. - - Tags: Backup - """ - backup = await self.storage.save_portable() - return web.json_response( - backup.dict() - ) - - -@routes.view('/blocks/backup/load') -class BackupLoadView(BackupView): - async def post(self, args: Backup) -> r200[BackupApplyResult]: - """ - Import service blocks from a backup generated by /blocks/backup/save - - Tags: Backup - """ - result = await self.storage.load_portable(args) - return web.json_response( - result.dict() - ) - - -@routes.view('/blocks/backup/stored/all') -class BackupStoredAllView(BackupView): - async def post(self) -> r200[list[BackupIdentity]]: - """ - List all stored backup files. - - Tags: Backup - """ - result = await self.storage.all() - return web.json_response( - [v.dict() for v in result] - ) - - -@routes.view('/blocks/backup/stored/download') -class BackupStoredDownloadView(BackupView): - async def post(self, args: BackupIdentity) -> r200[Backup]: - """ - Download stored backup without applying it. - - Tags: Backup - """ - result = await self.storage.read(args) - return web.json_response( - result.dict() - ) - - -@routes.view('/blocks/backup/stored/upload') -class BackupStoredUploadView(BackupView): - async def post(self, args: Backup) -> r200[Backup]: - """ - Download stored backup without applying it. - - Tags: Backup - """ - result = await self.storage.write(args) - return web.json_response( - result.dict() - ) - - -@routes.view('/blocks/backup/stored/save') -class BackupStoredSaveView(BackupView): - async def post(self, args: BackupIdentity) -> r200[Backup]: - """ - Create new stored backup. - - Tags: Backup - """ - result = await self.storage.save(args) - return web.json_response( - result.dict() - ) - - -@routes.view('/blocks/backup/stored/load') -class BackupStoredLoadView(BackupView): - async def post(self, args: BackupIdentity) -> r200[BackupApplyResult]: - """ - Apply stored backup. - - Tags: Backup - """ - result = await self.storage.load(args) - return web.json_response( - result.dict() - ) +import logging + +from fastapi import APIRouter + +from .. import block_backup +from ..models import Backup, BackupApplyResult, BackupIdentity + +LOGGER = logging.getLogger(__name__) + + +router = APIRouter(prefix='/blocks/backup', tags=['Backup']) + + +@router.post('/save') +async def backup_save() -> Backup: + """ + Export service blocks to a portable format. + """ + backup = await block_backup.CV.get().save_portable() + return backup + + +@router.post('/load') +async def backup_load(args: Backup) -> BackupApplyResult: + """ + Import service blocks from a backup generated by /blocks/backup/save. + """ + result = await block_backup.CV.get().load_portable(args) + return result + + +@router.post('/stored/all') +async def backup_stored_all() -> list[BackupIdentity]: + """ + List all stored backup files. + """ + result = await block_backup.CV.get().all() + return result + + +@router.post('/stored/download') +async def backup_stored_download(args: BackupIdentity) -> Backup: + """ + Download stored backup without applying it. + """ + result = await block_backup.CV.get().read(args) + return result + + +@router.post('/stored/upload') +async def backup_stored_upload(args: Backup) -> Backup: + """ + Upload stored backup without applying it. + """ + result = await block_backup.CV.get().write(args) + return result + + +@router.post('/stored/save') +async def backup_stored_save(args: BackupIdentity) -> Backup: + """ + Create new stored backup. + """ + result = await block_backup.CV.get().save(args) + return result + + +@router.post('/stored/load') +async def backup_stored_load(args: BackupIdentity) -> BackupApplyResult: + """ + Apply stored backup. + """ + result = await block_backup.CV.get().load(args) + return result diff --git a/brewblox_devcon_spark/api/blocks_api.py b/brewblox_devcon_spark/api/blocks_api.py index 5e1f5044..5058ef57 100644 --- a/brewblox_devcon_spark/api/blocks_api.py +++ b/brewblox_devcon_spark/api/blocks_api.py @@ -2,356 +2,239 @@ REST API for Spark blocks """ - -import json - -from aiohttp import web -from aiohttp_pydantic import PydanticView -from aiohttp_pydantic.oas.typing import r200, r201 -from brewblox_service import brewblox_logger, mqtt - -from brewblox_devcon_spark import controller -from brewblox_devcon_spark.models import (Block, BlockIdentity, - BlockIdentityList, BlockList, - BlockNameChange, ServiceConfig) - -LOGGER = brewblox_logger(__name__) -routes = web.RouteTableDef() - - -def setup(app: web.Application): - app.router.add_routes(routes) - - -class BlocksView(PydanticView): - def __init__(self, request: web.Request) -> None: - super().__init__(request) - self.app = request.app - self.config: ServiceConfig = self.app['config'] - self.controller = controller.fget(self.app) - - async def publish(self, changed: list[Block] = None, deleted: list[BlockIdentity] = None): - changed = [v.dict() for v in changed] if changed else [] - deleted = [v.id for v in deleted] if deleted else [] - name = self.config.name - await mqtt.publish(self.app, - topic=f'{self.config.state_topic}/{name}/patch', - payload=json.dumps({ - 'key': name, - 'type': 'Spark.patch', - 'ttl': '1d', - 'data': { - 'changed': changed, - 'deleted': deleted, - }, - }), - err=False, - ) - - -@routes.view('/blocks/create') -class CreateView(BlocksView): - async def post(self, args: Block) -> r201[Block]: - """ - Create new block. - - Tags: Blocks - """ - block = await self.controller.create_block(args) - await self.publish(changed=[block]) - return web.json_response( - block.dict(), - status=201 - ) - - -@routes.view('/blocks/read') -class ReadView(BlocksView): - async def post(self, args: BlockIdentity) -> r200[Block]: - """ - Read block. - - Tags: Blocks - """ - block = await self.controller.read_block(args) - return web.json_response( - block.dict() - ) - - -@routes.view('/blocks/read/logged') -class ReadLoggedView(BlocksView): - async def post(self, args: BlockIdentity) -> r200[Block]: - """ - Read block. Data only includes logged fields. - - Tags: Blocks - """ - block = await self.controller.read_logged_block(args) - return web.json_response( - block.dict() - ) - - -@routes.view('/blocks/read/stored') -class ReadStoredView(BlocksView): - async def post(self, args: BlockIdentity) -> r200[Block]: - """ - Read block. Data only includes persistent fields. - - Tags: Blocks - """ - block = await self.controller.read_stored_block(args) - return web.json_response( - block.dict() - ) - - -@routes.view('/blocks/write') -class WriteView(BlocksView): - async def post(self, args: Block) -> r200[Block]: - """ - Write to existing block. - - Tags: Blocks - """ - block = await self.controller.write_block(args) - await self.publish(changed=[block]) - return web.json_response( - block.dict() - ) - - -@routes.view('/blocks/patch') -class PatchView(BlocksView): - async def post(self, args: Block) -> r200[Block]: - """ - Patch existing block. - - Tags: Blocks - """ - block = await self.controller.patch_block(args) - await self.publish(changed=[block]) - return web.json_response( - block.dict() - ) - - -@routes.view('/blocks/delete') -class DeleteView(BlocksView): - async def post(self, args: BlockIdentity) -> r200[BlockIdentity]: - """ - Delete block. - - Tags: Blocks - """ - ident = await self.controller.delete_block(args) - await self.publish(deleted=[ident]) - return web.json_response( - ident.dict() - ) - - -@routes.view('/blocks/batch/create') -class BatchCreateView(BlocksView): - async def post(self, args: BlockList) -> r200[list[Block]]: - """ - Create multiple blocks. - - Tags: Blocks - """ - blocks_in = args.__root__ - blocks_out = [] - for block in blocks_in: - blocks_out.append(await self.controller.create_block(block)) - await self.publish(changed=blocks_out) - return web.json_response([ - block.dict() for block in blocks_out - ], - status=201) - - -@routes.view('/blocks/batch/read') -class BatchReadView(BlocksView): - async def post(self, args: BlockIdentityList) -> r200[list[Block]]: - """ - Read multiple existing blocks. - - Tags: Blocks - """ - blocks_in = args.__root__ - blocks_out = [] - for block in blocks_in: - blocks_out.append(await self.controller.read_block(block)) - return web.json_response([ - block.dict() for block in blocks_out - ]) - - -@routes.view('/blocks/batch/write') -class BatchWriteView(BlocksView): - async def post(self, args: BlockList) -> r200[list[Block]]: - """ - Write multiple existing blocks. - - Tags: Blocks - """ - blocks_in = args.__root__ - blocks_out = [] - for block in blocks_in: - blocks_out.append(await self.controller.write_block(block)) - await self.publish(changed=blocks_out) - return web.json_response([ - block.dict() for block in blocks_out - ]) - - -@routes.view('/blocks/batch/patch') -class BatchPatchView(BlocksView): - async def post(self, args: BlockList) -> r200[list[Block]]: - """ - Patch multiple existing blocks. - - Tags: Blocks - """ - blocks_in = args.__root__ - blocks_out = [] - for block in blocks_in: - blocks_out.append(await self.controller.patch_block(block)) - await self.publish(changed=blocks_out) - return web.json_response([ - block.dict() for block in blocks_out - ]) - - -@routes.view('/blocks/batch/delete') -class BatchDeleteView(BlocksView): - async def post(self, args: BlockIdentityList) -> r200[list[Block]]: - """ - Delete multiple existing blocks. - - Tags: Blocks - """ - idents_in = args.__root__ - idents_out = [] - for ident in idents_in: - idents_out.append(await self.controller.delete_block(ident)) - await self.publish(deleted=idents_out) - return web.json_response([ - ident.dict() for ident in idents_out - ]) - - -@routes.view('/blocks/all/read') -class ReadAllView(BlocksView): - async def post(self) -> r200[list[Block]]: - """ - Read all blocks. - - Tags: Blocks - """ - blocks = await self.controller.read_all_blocks() - return web.json_response( - [v.dict() for v in blocks] - ) - - -@routes.view('/blocks/all/read/logged') -class ReadAllLoggedView(BlocksView): - async def post(self) -> r200[list[Block]]: - """ - Read all blocks. Only includes logged fields. - - Tags: Blocks - """ - blocks = await self.controller.read_all_logged_blocks() - return web.json_response( - [v.dict() for v in blocks] - ) - - -@routes.view('/blocks/all/read/stored') -class ReadAllStoredView(BlocksView): - async def post(self) -> r200[list[Block]]: - """ - Read all blocks. Only includes stored fields. - - Tags: Blocks - """ - blocks = await self.controller.read_all_stored_blocks() - return web.json_response( - [v.dict() for v in blocks] - ) - - -@routes.view('/blocks/all/delete') -class DeleteAllView(BlocksView): - async def post(self) -> r200[list[BlockIdentity]]: - """ - Delete all user blocks. - - Tags: Blocks - """ - idents = await self.controller.clear_blocks() - await self.publish(deleted=idents) - return web.json_response( - [v.dict() for v in idents] - ) - - -@routes.view('/blocks/cleanup') -class CleanupView(BlocksView): - async def post(self) -> r200[list[BlockIdentity]]: - """ - Clean unused block IDs. - - Tags: Blocks - """ - idents = await self.controller.remove_unused_ids() - return web.json_response( - [v.dict() for v in idents] - ) - - -@routes.view('/blocks/rename') -class RenameView(BlocksView): - async def post(self, args: BlockNameChange) -> r200[BlockIdentity]: - """ - Rename existing block. - - Tags: Blocks - """ - ident = await self.controller.rename_block(args) - return web.json_response( - ident.dict() - ) - - -@routes.view('/blocks/discover') -class DiscoverView(BlocksView): - async def post(self) -> r200[list[Block]]: - """ - Discover newly connected OneWire devices. - - Tags: Blocks - """ - blocks = await self.controller.discover_blocks() - return web.json_response( - [v.dict() for v in blocks] - ) - - -@routes.view('/blocks/validate') -class ValidateView(BlocksView): - async def post(self, block: Block) -> r200[Block]: - """ - Validate block data. - This checks whether the block can be serialized. - It will not be sent to the controller. - - Tags: Blocks - """ - block = await self.controller.validate(block) - return web.json_response( - block.dict() - ) +import logging + +from fastapi import APIRouter + +from .. import control, mqtt, utils +from ..models import Block, BlockIdentity, BlockNameChange + +LOGGER = logging.getLogger(__name__) + +router = APIRouter(prefix='/blocks', tags=['Blocks']) + + +def publish(changed: list[Block] = None, + deleted: list[BlockIdentity] = None): + config = utils.get_config() + mqtt_client = mqtt.CV.get() + changed = [v.model_dump(mode='json') for v in changed] if changed else [] + deleted = [v.id for v in deleted] if deleted else [] + mqtt_client.publish(f'{config.state_topic}/{config.name}/patch', + { + 'key': config.name, + 'type': 'Spark.patch', + 'ttl': '1d', + 'data': { + 'changed': changed, + 'deleted': deleted, + }, + }) + + +@router.post('/create', status_code=201) +async def blocks_create(args: Block) -> Block: + """ + Create new block. + """ + block = await control.CV.get().create_block(args) + publish(changed=[block]) + return block + + +@router.post('/read') +async def blocks_read(args: BlockIdentity) -> Block: + """ + Read existing block. + """ + block = await control.CV.get().read_block(args) + return block + + +@router.post('/read/logged') +async def blocks_read_logged(args: BlockIdentity) -> Block: + """ + Read existing block. Data only includes logged fields. + """ + block = await control.CV.get().read_logged_block(args) + return block + + +@router.post('/read/stored') +async def blocks_read_stored(args: BlockIdentity) -> Block: + """ + Read existing block. Data only includes stored fields. + """ + block = await control.CV.get().read_stored_block(args) + return block + + +@router.post('/write') +async def blocks_write(args: Block) -> Block: + """ + Write existing block. This will replace all fields. + """ + block = await control.CV.get().write_block(args) + publish(changed=[block]) + return block + + +@router.post('/patch') +async def blocks_patch(args: Block) -> Block: + """ + Patch existing block. This will only replace provided fields. + """ + block = await control.CV.get().patch_block(args) + publish(changed=[block]) + return block + + +@router.post('/delete') +async def blocks_delete(args: BlockIdentity) -> BlockIdentity: + """ + Delete existing user block. + """ + ident = await control.CV.get().delete_block(args) + publish(deleted=[ident]) + return ident + + +@router.post('/batch/create', status_code=201) +async def blocks_batch_create(args: list[Block]) -> list[Block]: + """ + Create multiple new blocks. + """ + ctrl = control.CV.get() + blocks = [await ctrl.create_block(block) + for block in args] + publish(changed=blocks) + return blocks + + +@router.post('/batch/read') +async def blocks_batch_read(args: list[BlockIdentity]) -> list[Block]: + """ + Read multiple existing blocks. + """ + ctrl = control.CV.get() + blocks = [await ctrl.read_block(ident) + for ident in args] + return blocks + + +@router.post('/batch/write') +async def blocks_batch_write(args: list[Block]) -> list[Block]: + """ + Write multiple existing blocks. This will replace all fields. + """ + ctrl = control.CV.get() + blocks = [await ctrl.write_block(block) + for block in args] + publish(changed=blocks) + return blocks + + +@router.post('/batch/patch') +async def blocks_batch_patch(args: list[Block]) -> list[Block]: + """ + Write multiple existing blocks. This will only replace provided fields. + """ + ctrl = control.CV.get() + blocks = [await ctrl.patch_block(block) + for block in args] + publish(changed=blocks) + return blocks + + +@router.post('/batch/delete') +async def blocks_batch_delete(args: list[BlockIdentity]) -> list[BlockIdentity]: + """ + Delete multiple existing user blocks. + """ + ctrl = control.CV.get() + idents = [await ctrl.delete_block(ident) + for ident in args] + publish(deleted=idents) + return idents + + +@router.post('/all/read') +async def blocks_all_read() -> list[Block]: + """ + Read all existing blocks. + """ + blocks = await control.CV.get().read_all_blocks() + return blocks + + +@router.post('/all/read/logged') +async def blocks_all_read_logged() -> list[Block]: + """ + Read all existing blocks. Only includes logged fields. + """ + blocks = await control.CV.get().read_all_logged_blocks() + return blocks + + +@router.post('/all/read/stored') +async def blocks_all_read_stored() -> list[Block]: + """ + Read all existing blocks. Only includes stored fields. + """ + blocks = await control.CV.get().read_all_stored_blocks() + return blocks + + +@router.post('/all/delete') +async def blocks_all_delete() -> list[BlockIdentity]: + """ + Delete all user blocks. + """ + idents = await control.CV.get().clear_blocks() + publish(deleted=idents) + return idents + + +@router.post('/cleanup') +async def blocks_cleanup() -> list[BlockIdentity]: + """ + Clean unused block IDs. + """ + idents = await control.CV.get().remove_unused_ids() + return idents + + +@router.post('/rename') +async def blocks_rename(args: BlockNameChange) -> BlockIdentity: + """ + Rename existing block. + """ + config = utils.get_config() + ctrl = control.CV.get() + ident = await ctrl.rename_block(args) + block = await ctrl.read_block(ident) + old_ident = BlockIdentity(id=args.existing, + serviceId=config.name) + publish(changed=[block], deleted=[old_ident]) + return ident + + +@router.post('/discover') +async def blocks_discover() -> list[Block]: + """ + Discover new automatically created blocks. + """ + blocks = await control.CV.get().discover_blocks() + publish(changed=blocks) + return blocks + + +@router.post('/validate') +async def blocks_validate(args: Block) -> Block: + """ + Validate block data. + This checks whether the block can be serialized. + It will not be sent to the controller. + """ + block = await control.CV.get().validate(args) + return block diff --git a/brewblox_devcon_spark/api/blocks_mqtt_api.py b/brewblox_devcon_spark/api/blocks_mqtt_api.py new file mode 100644 index 00000000..1eb1ebb6 --- /dev/null +++ b/brewblox_devcon_spark/api/blocks_mqtt_api.py @@ -0,0 +1,40 @@ +""" +MQTT API for Spark blocks +""" + +import logging + +from .. import control, mqtt, utils +from ..models import Block, BlockIdentity + +LOGGER = logging.getLogger(__name__) + + +def setup(): + config = utils.get_config() + mqtt_client = mqtt.CV.get() + ctrl = control.CV.get() + + @mqtt_client.subscribe(config.blocks_topic + '/create') + async def on_create(client, topic, payload, qos, properties): + block = Block.model_validate_json(payload) + if block.serviceId == config.name: + await ctrl.create_block(block) + + @mqtt_client.subscribe(config.blocks_topic + '/write') + async def on_write(client, topic, payload, qos, properties): + block = Block.model_validate_json(payload) + if block.serviceId == config.name: + await ctrl.write_block(block) + + @mqtt_client.subscribe(config.blocks_topic + '/patch') + async def on_patch(client, topic, payload, qos, properties): + block = Block.model_validate_json(payload) + if block.serviceId == config.name: + await ctrl.patch_block(block) + + @mqtt_client.subscribe(config.blocks_topic + '/delete') + async def on_delete(client, topic, payload, qos, properties): + ident = BlockIdentity.model_validate_json(payload) + if ident.serviceId == config.name: + await ctrl.delete_block(ident) diff --git a/brewblox_devcon_spark/api/debug_api.py b/brewblox_devcon_spark/api/debug_api.py index 2ad9067a..e6f874ae 100644 --- a/brewblox_devcon_spark/api/debug_api.py +++ b/brewblox_devcon_spark/api/debug_api.py @@ -2,139 +2,98 @@ REST API for system debugging """ -from aiohttp import web -from aiohttp_pydantic import PydanticView -from aiohttp_pydantic.oas.typing import r200 -from brewblox_service import brewblox_logger +import logging -from brewblox_devcon_spark import codec -from brewblox_devcon_spark.models import (DecodedPayload, EncodedMessage, - EncodedPayload, IntermediateRequest, - IntermediateResponse) +from fastapi import APIRouter -LOGGER = brewblox_logger(__name__) -routes = web.RouteTableDef() +from .. import codec +from ..models import (DecodedPayload, EncodedMessage, EncodedPayload, + IntermediateRequest, IntermediateResponse) +LOGGER = logging.getLogger(__name__) -def setup(app: web.Application): - app.router.add_routes(routes) +router = APIRouter(prefix='/_debug', tags=['Debug']) -class CodecView(PydanticView): - def __init__(self, request: web.Request) -> None: - super().__init__(request) - self.codec = codec.fget(request.app) +@router.post('/encode_request') +async def debug_encode_request(args: IntermediateRequest) -> EncodedMessage: + """ + Encode a Request object to a base64 string. + Te object encoded to protobuf bytes. + The bytes are encoded as base64 to make them ASCII-safe. -@routes.view('/_debug/encode_request') -class EncodeRequestView(CodecView): - async def post(self, args: IntermediateRequest) -> r200[EncodedMessage]: - """ - Encode a Request object to a base64 string. + Included payloads must have been encoded using /_debug/encode_payload. + """ + message = codec.CV.get().encode_request(args) + return EncodedMessage(message=message) - Te object encoded to protobuf bytes. - The bytes are encoded as base64 to make them ASCII-safe. - Included payloads must have been encoded using /_debug/encode_payload. +@router.post('/decode_request') +async def debug_decode_request(args: EncodedMessage) -> IntermediateRequest: + """ + Decode a Request object from a base64 string. - tags: Debug - """ - message = self.codec.encode_request(args) - return web.json_response( - EncodedMessage(message=message).dict() - ) + The base64 string is converted to bytes. + The bytes are decoded using protobuf. + Included payloads must be decoded using /_debug/decode_payload + """ + request = codec.CV.get().decode_request(args.message) + return request -@routes.view('/_debug/decode_request') -class DecodeRequestView(CodecView): - async def post(self, args: EncodedMessage) -> r200[IntermediateRequest]: - """ - Decode a Request object from a base64 string. - The base64 string is converted to bytes. - The bytes are decoded using protobuf. +@router.post('/encode_response') +async def debug_encode_response(args: IntermediateResponse) -> EncodedMessage: + """ + Encode a Response object to a base64 string. - Included payloads must be decoded using /_debug/decode_payload + Te object encoded to protobuf bytes. + The bytes are encoded as base64 to make them ASCII-safe. - tags: Debug - """ - request = self.codec.decode_request(args.message) - return web.json_response( - request.clean_dict() - ) + Included payloads must have been encoded using /_debug/encode_payload. + """ + message = codec.CV.get().encode_response(args) + return EncodedMessage(message=message) -@routes.view('/_debug/encode_response') -class EncodeResponseView(CodecView): - async def post(self, args: IntermediateResponse) -> r200[EncodedMessage]: - """ - Encode a Response object to a base64 string. +@router.post('/decode_response') +async def debug_decode_response(args: EncodedMessage) -> IntermediateResponse: + """ + Decode a Response object from a base64 string. - Te object encoded to protobuf bytes. - The bytes are encoded as base64 to make them ASCII-safe. + The base64 string is converted to bytes. + The bytes are decoded using protobuf. - Included payloads must have been encoded using /_debug/encode_payload. + Included payloads must be decoded using /_debug/decode_payload + """ + response = codec.CV.get().decode_response(args.message) + return response - tags: Debug - """ - message = self.codec.encode_response(args) - return web.json_response( - EncodedMessage(message=message).dict() - ) +@router.post('/encode_payload') +async def debug_encode_payload(args: DecodedPayload) -> EncodedPayload: + """ + Encode a Payload object. -@routes.view('/_debug/decode_response') -class DecodeResponseView(CodecView): - async def post(self, args: EncodedMessage) -> r200[IntermediateResponse]: - """ - Decode a Response object from a base64 string. + `content` is encoded to protobuf bytes, + and then encoded as base64 to make it ASCII-safe. - The base64 string is converted to bytes. - The bytes are decoded using protobuf. + This operation is symmetrical with /_debug/decode_payload. + """ + payload = codec.CV.get().encode_payload(args) + return payload - Included payloads must be decoded using /_debug/decode_payload - tags: Debug - """ - response = self.codec.decode_response(args.message) - return web.json_response( - response.clean_dict() - ) +@router.post('/decode_payload') +async def debug_decode_payload(args: EncodedPayload) -> DecodedPayload: + """ + Decode a Payload object. + `content` is converted to bytes, + and then decoded using protobuf. -@routes.view('/_debug/encode_payload') -class EncodePayloadView(CodecView): - async def post(self, args: DecodedPayload) -> r200[EncodedPayload]: - """ - Encode a Payload object. - - `content` is encoded to protobuf bytes, - and then encoded as base64 to make it ASCII-safe. - - This operation is symmetrical with /_debug/decode_payload. - - tags: Debug - """ - payload = self.codec.encode_payload(args) - return web.json_response( - payload.clean_dict() - ) - - -@routes.view('/_debug/decode_payload') -class DecodePayloadView(CodecView): - async def post(self, args: EncodedPayload) -> r200[DecodedPayload]: - """ - Decode a Payload object. - - `content` is converted to bytes, - and then decoded using protobuf. - - This operation is symmetrical with /_debug/encode_payload - - tags: Debug - """ - payload = self.codec.decode_payload(args) - return web.json_response( - payload.clean_dict() - ) + This operation is symmetrical with /_debug/encode_payload + """ + payload = codec.CV.get().decode_payload(args) + return payload diff --git a/brewblox_devcon_spark/api/error_response.py b/brewblox_devcon_spark/api/error_response.py deleted file mode 100644 index 5ff70f30..00000000 --- a/brewblox_devcon_spark/api/error_response.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -Catches Python error, and returns appropriate error codes -""" - -import json -import traceback - -from aiohttp import web -from brewblox_service import brewblox_logger, strex - -from brewblox_devcon_spark.exceptions import BrewbloxException - -LOGGER = brewblox_logger(__name__) - - -def setup(app: web.Application): - app.middlewares.append(controller_error_middleware) - - -@web.middleware -async def controller_error_middleware(request: web.Request, handler) -> web.Response: - try: - return await handler(request) - - except web.HTTPError: # pragma: no cover - raise - - except Exception as ex: - app = request.app - message = strex(ex) - debug = app['config'].debug - LOGGER.error(f'[{request.url}] => {message}', exc_info=debug) - - response = {'error': message} - - if debug: - response['traceback'] = traceback.format_tb(ex.__traceback__) - - if isinstance(ex, BrewbloxException): - http_error = ex.http_error - else: # pragma: no cover - http_error = web.HTTPInternalServerError - - raise http_error(text=json.dumps(response), - content_type='application/json') diff --git a/brewblox_devcon_spark/api/mqtt_api.py b/brewblox_devcon_spark/api/mqtt_api.py deleted file mode 100644 index 2dfe7803..00000000 --- a/brewblox_devcon_spark/api/mqtt_api.py +++ /dev/null @@ -1,67 +0,0 @@ -""" -MQTT API for Spark blocks -""" - -import json - -from aiohttp import web -from brewblox_service import brewblox_logger, features, mqtt - -from brewblox_devcon_spark import controller -from brewblox_devcon_spark.models import Block, BlockIdentity - -LOGGER = brewblox_logger(__name__) - -BLOCKS_TOPIC = 'brewcast/spark/blocks' - - -class MqttApi(features.ServiceFeature): - - def __init__(self, app: web.Application): - super().__init__(app) - self.name = app['config'].name - self.controller = controller.fget(app) - self.listeners = { - '/create': self._create, - '/write': self._write, - '/patch': self._patch, - '/delete': self._delete, - } - - async def startup(self, app: web.Application): - for path, listener in self.listeners.items(): - await mqtt.listen(app, BLOCKS_TOPIC + path, listener) - await mqtt.subscribe(app, BLOCKS_TOPIC + '/#') - - async def shutdown(self, app: web.Application): - await mqtt.unsubscribe(app, BLOCKS_TOPIC + '/#') - for path, listener in self.listeners.items(): - await mqtt.unlisten(app, BLOCKS_TOPIC + path, listener) - - async def _create(self, topic: str, msg: str): - block = Block(**json.loads(msg)) - if block.serviceId == self.name: - await self.controller.create_block(block) - - async def _write(self, topic: str, msg: str): - block = Block(**json.loads(msg)) - if block.serviceId == self.name: - await self.controller.write_block(block) - - async def _patch(self, topic: str, msg: str): - block = Block(**json.loads(msg)) - if block.serviceId == self.name: - await self.controller.patch_block(block) - - async def _delete(self, topic: str, msg: str): - ident = BlockIdentity(**json.loads(msg)) - if ident.serviceId == self.name: - await self.controller.delete_block(ident) - - -def setup(app: web.Application): - features.add(app, MqttApi(app)) - - -def fget(app: web.Application) -> MqttApi: - return features.get(app, MqttApi) diff --git a/brewblox_devcon_spark/api/settings_api.py b/brewblox_devcon_spark/api/settings_api.py index 2025cad2..65146e6d 100644 --- a/brewblox_devcon_spark/api/settings_api.py +++ b/brewblox_devcon_spark/api/settings_api.py @@ -2,50 +2,34 @@ REST API for persistent settings """ -from aiohttp import web -from aiohttp_pydantic import PydanticView -from aiohttp_pydantic.oas.typing import r200 -from brewblox_service import brewblox_logger -from pydantic import BaseModel -from brewblox_devcon_spark import service_status, service_store +import logging -LOGGER = brewblox_logger(__name__) -routes = web.RouteTableDef() +from fastapi import APIRouter +from .. import datastore_settings +from ..models import AutoconnectSettings -def setup(app: web.Application): - app.router.add_routes(routes) +LOGGER = logging.getLogger(__name__) +router = APIRouter(prefix='/settings', tags=['Settings']) -class AutoconnectSettings(BaseModel): - enabled: bool +@router.get('/enabled') +async def settings_enabled_get() -> AutoconnectSettings: + """ + Get enabled flag. + """ + enabled = datastore_settings.CV.get().service_settings.enabled + return AutoconnectSettings(enabled=enabled) -@routes.view('/settings/autoconnecting') -class AutoconnectingView(PydanticView): - async def get(self) -> r200[AutoconnectSettings]: - """ - Get autoconnecting flag. - Tags: Settings - """ - enabled = service_store.get_autoconnecting(self.request.app) - settings = AutoconnectSettings(enabled=enabled) - return web.json_response( - settings.dict() - ) - - async def put(self, args: AutoconnectSettings) -> r200[AutoconnectSettings]: - """ - Set autoconnecting flag. - - Tags: Settings - """ - enabled = service_store.set_autoconnecting(self.request.app, - args.enabled) - service_status.set_enabled(self.request.app, enabled) - settings = AutoconnectSettings(enabled=enabled) - return web.json_response( - settings.dict() - ) +@router.put('/enabled') +async def settings_enabled_put(args: AutoconnectSettings) -> AutoconnectSettings: + """ + Set enabled flag. + """ + store = datastore_settings.CV.get() + store.service_settings.enabled = args.enabled + await store.commit_service_settings() + return AutoconnectSettings(enabled=args.enabled) diff --git a/brewblox_devcon_spark/api/sim_api.py b/brewblox_devcon_spark/api/sim_api.py index 0071e71e..9cca075f 100644 --- a/brewblox_devcon_spark/api/sim_api.py +++ b/brewblox_devcon_spark/api/sim_api.py @@ -2,70 +2,42 @@ Simulator-specific endpoints """ -import asyncio -from weakref import WeakSet +import logging -from aiohttp import ClientSession, WSCloseCode, web -from aiohttp.http_websocket import WSMsgType -from brewblox_service import brewblox_logger, features +from fastapi import APIRouter, WebSocket +from httpx_ws import aconnect_ws -from brewblox_devcon_spark import service_status +from .. import exceptions, state_machine, utils -SPARK_WS_ADDR = 'ws://localhost:7377/' +LOGGER = logging.getLogger(__name__) -LOGGER = brewblox_logger(__name__) -routes = web.RouteTableDef() +router = APIRouter(prefix='/sim', tags=['Sim']) -class SocketCloser(features.ServiceFeature): +@router.websocket('/display') +async def sim_display_websocket(ws: WebSocket): # pragma: no cover + """ + Open a WebSocket to stream display buffer updates. + The full buffer will be sent in the initial push, + and subsequent updates will only include changed areas. + """ + config = utils.get_config() + state = state_machine.CV.get() - def __init__(self, app: web.Application) -> None: - super().__init__(app) - app['websockets'] = WeakSet() + if not config.simulation: + raise exceptions.ConnectionImpossible('Device is not a simulation') - async def before_shutdown(self, app: web.Application): - for ws in set(app['websockets']): - await ws.close(code=WSCloseCode.GOING_AWAY, - message='Server shutdown') - - -@routes.get('/sim/display') -async def stream_display(request: web.Request) -> web.Response: - ws = web.WebSocketResponse() - listen_task: asyncio.Task = None - - async def listen(): - async for msg in ws: # pragma: no cover - pass + await ws.accept() try: - await ws.prepare(request) - request.app['websockets'].add(ws) - listen_task = asyncio.create_task(listen()) - - await service_status.wait_synchronized(request.app) - - async with ClientSession() as session: - # `Connection: keep-alive` is required by server - async with session.ws_connect( - SPARK_WS_ADDR, - headers={'Connection': 'keep-alive, Upgrade'}, - ) as spark_ws: - request.app['websockets'].add(spark_ws) - try: - async for msg in spark_ws: - if msg.type == WSMsgType.BINARY: - await ws.send_bytes(msg.data) - finally: # pragma: no cover - request.app['websockets'].discard(spark_ws) - - finally: - request.app['websockets'].discard(ws) - listen_task and listen_task.cancel() - - return ws # pragma: no cover + await state.wait_synchronized() + async with aconnect_ws(url=f'ws://localhost:{config.simulation_display_port}/', + ) as client_ws: + while True: + msg = await client_ws.receive_bytes() + await ws.send_bytes(msg) -def setup(app: web.Application): - app.router.add_routes(routes) - features.add(app, SocketCloser(app)) + except Exception as ex: + LOGGER.error(utils.strex(ex), exc_info=config.debug) + raise ex diff --git a/brewblox_devcon_spark/api/system_api.py b/brewblox_devcon_spark/api/system_api.py index 379e7320..66261d3f 100644 --- a/brewblox_devcon_spark/api/system_api.py +++ b/brewblox_devcon_spark/api/system_api.py @@ -3,170 +3,123 @@ """ import asyncio -import json +import logging -from aiohttp import web -from aiohttp_pydantic import PydanticView -from aiohttp_pydantic.oas.typing import r200 -from brewblox_service import brewblox_logger, http, mqtt, scheduler, strex -from pydantic import BaseModel +from fastapi import APIRouter, BackgroundTasks +from httpx import AsyncClient -from brewblox_devcon_spark import (commander, connection, controller, - exceptions, service_status, ymodem) -from brewblox_devcon_spark.models import ServiceStatusDescription +from .. import command, control, exceptions, mqtt, state_machine, utils, ymodem +from ..models import FirmwareFlashResponse, PingResponse, StatusDescription -TRANSFER_TIMEOUT_S = 30 -STATE_TIMEOUT_S = 20 -CONNECT_INTERVAL_S = 3 -CONNECT_ATTEMPTS = 5 +ESP_URL_FMT = 'http://brewblox.blob.core.windows.net/firmware/{date}-{version}/brewblox-esp32.bin' -FLUSH_PERIOD_S = 3 -SHUTDOWN_DELAY_S = 1 -UPDATE_SHUTDOWN_DELAY_S = 5 +LOGGER = logging.getLogger(__name__) -ESP_URL_FMT = 'http://brewblox.blob.core.windows.net/firmware/{firmware_date}-{firmware_version}/brewblox-esp32.bin' +router = APIRouter(prefix='/system', tags=['System']) -LOGGER = brewblox_logger(__name__) -routes = web.RouteTableDef() +@router.get('/status') +async def system_status() -> StatusDescription: + """ + Get service status. + """ + desc = state_machine.CV.get().desc() + return desc -class FlashResponse(BaseModel): - address: str - version: str +@router.get('/ping') +async def system_ping_get() -> PingResponse: + """ + Ping the controller. + """ + await control.CV.get().noop() + return PingResponse() -def setup(app: web.Application): - app.router.add_routes(routes) +@router.post('/ping') +async def system_ping_post() -> PingResponse: + """ + Ping the controller. + """ + await control.CV.get().noop() + return PingResponse() -async def shutdown_soon(app: web.Application, wait: float): # pragma: no cover - async def delayed_shutdown(): - await asyncio.sleep(wait) - raise web.GracefulExit() - await scheduler.create(app, delayed_shutdown()) +@router.post('/reboot/controller') +async def system_reboot_controller(): + """ + Reboot the controller. + """ + await control.CV.get().reboot() + return {} -@routes.view('/system/status') -class StatusView(PydanticView): - async def get(self) -> r200[ServiceStatusDescription]: - """ - Get service status - Tags: System - """ - desc = service_status.desc(self.request.app) - return web.json_response( - desc.dict() - ) +@router.post('/reboot/service') +async def system_reboot_service(background_tasks: BackgroundTasks): + """ + Reboot the service. + """ + background_tasks.add_task(utils.graceful_shutdown, 'System API request') + return {} -@routes.view('/system/ping') -class PingView(PydanticView): - async def get(self): - """ - Ping the controller. +@router.post('/clear_wifi') +async def system_clear_wifi(): + """ + Clear Wifi settings on the controller. + The controller may reboot or lose connection. + """ + await control.CV.get().clear_wifi() + return {} - Tags: System - """ - await controller.fget(self.request.app).noop() - return web.Response() - async def post(self): - """ - Ping the controller. +@router.post('/factory_reset') +async def system_factory_reset(): + """ + Factory reset the controller. + This does not include firmware. + """ + await control.CV.get().factory_reset() + return {} - Tags: System - """ - await controller.fget(self.request.app).noop() - return web.Response() +class Flasher: -@routes.view('/system/reboot/controller') -class RebootControllerView(PydanticView): - async def post(self): - """ - Reboot the controller. + def __init__(self, background_tasks: BackgroundTasks) -> None: + self.config = utils.get_config() + self.fw_config = utils.get_fw_config() - Tags: System - """ - await controller.fget(self.request.app).reboot() - return web.Response() + self.state = state_machine.CV.get() + self.mqtt_client = mqtt.CV.get() + self.commander = command.CV.get() + self.client = AsyncClient() + self.background_tasks = background_tasks - -@routes.view('/system/reboot/service') -class RebootServiceView(PydanticView): - async def post(self): - """ - Reboot the service. - - Tags: System - """ - await shutdown_soon(self.request.app, SHUTDOWN_DELAY_S) - return web.Response() - - -@routes.view('/system/clear_wifi') -class ClearWifiView(PydanticView): - async def post(self): - """ - Clear Wifi settings on the controller. - The controller may reboot or lose connection. - - Tags: System - """ - await controller.fget(self.request.app).clear_wifi() - return web.Response() - - -@routes.view('/system/factory_reset') -class FactoryResetView(PydanticView): - async def post(self): - """ - Factory reset the controller. - - Tags: System - """ - await controller.fget(self.request.app).factory_reset() - return web.Response() - - -@routes.view('/system/flash') -class FlashView(PydanticView): # pragma: no cover - def __init__(self, request: web.Request) -> None: - super().__init__(request) - self.app = request.app - self.name: str = self.app['config'].name - self.simulation: bool = self.app['config'].simulation - self.topic: str = self.app['config'].state_topic + f'/{self.name}/update' - self.version: str = self.app['ini']['firmware_version'][:8] - self.date: str = self.app['ini']['firmware_date'] + self.notify_topic = f'{self.config.state_topic}/{self.config.name}/update' + self.fw_version = self.fw_config.firmware_version + self.fw_date = self.fw_config.firmware_date + self.fw_url = ESP_URL_FMT.format(date=self.fw_date, + version=self.fw_version) def _notify(self, msg: str): LOGGER.info(msg) - asyncio.create_task( - mqtt.publish(self.app, - self.topic, - json.dumps({ - 'key': self.name, - 'type': 'Spark.update', - 'data': { - 'log': [msg], - }, - }), - err=False)) - - async def post(self) -> r200[FlashResponse]: - """ - Flash the controller firmware. - - Tags: System - """ - desc = service_status.desc(self.app) + self.mqtt_client.publish(self.notify_topic, + { + 'key': self.config.name, + 'type': 'Spark.update', + 'data': { + 'log': [msg], + }, + }) + + async def run(self) -> FirmwareFlashResponse: # pragma: no cover + desc = self.state.desc() + device_id = desc.controller.device.device_id platform = desc.controller.platform connection_kind = desc.connection_kind address = desc.address - if desc.connection_status not in ['ACKNOWLEDGED', 'SYNCHRONIZED']: + if not self.state.is_acknowledged(): self._notify('Controller is not connected. Aborting update.') raise exceptions.NotConnected() @@ -175,57 +128,64 @@ async def post(self) -> r200[FlashResponse]: raise exceptions.IncompatibleFirmware() try: - self._notify(f'Started updating {self.name}@{address} to version {self.version} ({self.date})') + self._notify(f'Started updating {device_id}@{address} to version {self.fw_version} ({self.fw_date})') self._notify('Preparing update') - service_status.set_updating(self.app) - await asyncio.sleep(FLUSH_PERIOD_S) # Wait for in-progress commands to finish + self.state.set_updating() + + self._notify('Waiting for in-progress commands to finish') + await asyncio.wait_for( + self.commander.wait_empty(), + self.config.command_timeout.total_seconds()) self._notify('Sending update command to controller') - await commander.fget(self.app).firmware_update() + await self.commander.firmware_update() self._notify('Waiting for normal connection to close') - await connection.fget(self.app).end() await asyncio.wait_for( - service_status.wait_disconnected(self.app), STATE_TIMEOUT_S) - - if platform == 'esp32': # pragma: no cover - fw_url = ESP_URL_FMT.format(**self.app['ini']) + self.commander.end_connection(), + self.config.flash_disconnect_timeout.total_seconds()) + if platform == 'esp32': if connection_kind == 'TCP': host, _ = address.split(':') self._notify(f'Sending update prompt to {host}') self._notify('The Spark will now download and apply the new firmware') self._notify('The update is done when the service reconnects') - await http.session(self.app).post(f'http://{host}:80/firmware_update', data=fw_url) + await self.client.post(f'http://{host}:80/firmware_update', content=self.fw_url) if connection_kind == 'MQTT': topic = f'brewcast/cbox/fw/{address}' self._notify(f'Sending update prompt to {topic}') self._notify('The Spark will now download and apply the new firmware') self._notify('The update is done when the service reconnects') - await mqtt.publish(self.app, topic, fw_url) + self.mqtt_client.publish(topic, self.fw_url) if platform in ['photon', 'p1']: self._notify(f'Connecting to {address}') - conn = await ymodem.connect(address) - client = ymodem.OtaClient(self._notify) + ymodem_conn = await ymodem.connect(address) + ota_client = ymodem.OtaClient(self._notify) - with conn.autoclose(): + with ymodem_conn.autoclose(): await asyncio.wait_for( - client.send(conn, f'firmware/brewblox-{platform}.bin'), - TRANSFER_TIMEOUT_S) + ota_client.send(ymodem_conn, f'firmware/brewblox-{platform}.bin'), + self.config.flash_ymodem_timeout.total_seconds()) self._notify('Update done!') except Exception as ex: - self._notify(f'Failed to update firmware: {strex(ex)}') - raise exceptions.FirmwareUpdateFailed(strex(ex)) + self._notify(f'Failed to update firmware: {utils.strex(ex)}') + raise exceptions.FirmwareUpdateFailed(utils.strex(ex)) finally: self._notify('Restarting service...') - await shutdown_soon(self.app, UPDATE_SHUTDOWN_DELAY_S) + self.background_tasks.add_task(utils.graceful_shutdown, 'Firmware flash') + + response = FirmwareFlashResponse(address=address, version=self.fw_version) + return response + - response = FlashResponse(address=address, version=self.version) - return web.json_response( - response.dict() - ) +@router.post('/flash') +async def system_flash(background_tasks: BackgroundTasks) -> FirmwareFlashResponse: + flasher = Flasher(background_tasks) + response = await flasher.run() + return response diff --git a/brewblox_devcon_spark/app_factory.py b/brewblox_devcon_spark/app_factory.py new file mode 100644 index 00000000..3a4ff468 --- /dev/null +++ b/brewblox_devcon_spark/app_factory.py @@ -0,0 +1,146 @@ +import logging +import traceback +from contextlib import AsyncExitStack, asynccontextmanager +from pprint import pformat + +from fastapi import FastAPI, HTTPException, Request, status +from fastapi.exceptions import RequestValidationError, ResponseValidationError +from fastapi.responses import JSONResponse + +from . import (block_backup, broadcast, codec, command, connection, control, + datastore_blocks, datastore_settings, mqtt, state_machine, + synchronization, time_sync, utils) +from .api import (backup_api, blocks_api, blocks_mqtt_api, debug_api, + settings_api, sim_api, system_api) +from .models import ErrorResponse + +LOGGER = logging.getLogger(__name__) + + +def setup_logging(debug: bool, trace: bool): + level = logging.DEBUG if debug else logging.INFO + unimportant_level = logging.INFO if debug else logging.WARN + format = '%(asctime)s.%(msecs)03d [%(levelname).1s:%(name)s:%(lineno)d] %(message)s' + datefmt = '%Y/%m/%d %H:%M:%S' + + logging.basicConfig(level=level, format=format, datefmt=datefmt) + logging.captureWarnings(True) + + # Enables LOGGER.trace(msg) calls + # Trace logs are independent from debug logs + # You can enable either, neither, or both + utils.add_logging_level('TRACE', level + 5 if trace else level - 5) + + logging.getLogger('gmqtt').setLevel(unimportant_level) + logging.getLogger('httpx').setLevel(unimportant_level) + logging.getLogger('httpcore').setLevel(logging.WARN) + logging.getLogger('uvicorn.access').setLevel(unimportant_level) + logging.getLogger('uvicorn.error').disabled = True + + +def add_exception_handlers(app: FastAPI): + config = utils.get_config() + logger = logging.getLogger('devcon.error') + + @app.exception_handler(HTTPException) + async def on_http_error(request: Request, ex: HTTPException) -> JSONResponse: + msg = str(ex) + content = ErrorResponse(error=msg) + + if config.debug: + content.traceback = traceback.format_exception(None, ex, ex.__traceback__) + + logger.error(f'[{request.url}] => {msg}', exc_info=config.debug) + return JSONResponse(content.model_dump(mode='json', exclude_none=True), + status_code=ex.status_code) + + @app.exception_handler(RequestValidationError) + async def on_request_error(request: Request, ex: RequestValidationError) -> JSONResponse: + msg = utils.strex(ex) + content = ErrorResponse(error=msg, validation=ex.errors()) + + logger.error(f'[{request.url}] => {msg}') + return JSONResponse(content.model_dump(mode='json', exclude_none=True), + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY) + + @app.exception_handler(ResponseValidationError) + async def on_response_error(request: Request, ex: ResponseValidationError) -> JSONResponse: + msg = utils.strex(ex) + content = ErrorResponse(error=msg, validation=ex.errors()) + + logger.error(f'[{request.url}] => {msg}') + return JSONResponse(content.model_dump(mode='json', exclude_none=True), + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + + @app.exception_handler(Exception) + async def on_generic_error(request: Request, ex: Exception) -> JSONResponse: # pragma: no cover + msg = utils.strex(ex) + content = ErrorResponse(error=msg) + + if config.debug: + content.traceback = traceback.format_exception(None, ex, ex.__traceback__) + + logger.error(f'[{request.url}] => {msg}', exc_info=config.debug) + return JSONResponse(content.model_dump(exclude_none=True), + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + + +@asynccontextmanager +async def lifespan(app: FastAPI): + LOGGER.info(utils.get_config()) + LOGGER.trace('ROUTES:\n' + pformat(app.routes)) + LOGGER.trace('LOGGERS:\n' + pformat(logging.root.manager.loggerDict)) + + async with AsyncExitStack() as stack: + await stack.enter_async_context(mqtt.lifespan()) + await stack.enter_async_context(datastore_settings.lifespan()) + await stack.enter_async_context(datastore_blocks.lifespan()) + await stack.enter_async_context(connection.lifespan()) + await stack.enter_async_context(synchronization.lifespan()) + await stack.enter_async_context(broadcast.lifespan()) + await stack.enter_async_context(time_sync.lifespan()) + await stack.enter_async_context(block_backup.lifespan()) + yield + + +def create_app() -> FastAPI: + config = utils.get_config() + setup_logging(config.debug, config.trace) + + if config.debugger: # pragma: no cover + import debugpy + debugpy.listen(('0.0.0.0', 5678)) + LOGGER.info('Debugger is enabled and listening on 5678') + + # Call setup functions for modules + mqtt.setup() + state_machine.setup() + datastore_settings.setup() + datastore_blocks.setup() + codec.setup() + connection.setup() + command.setup() + control.setup() + block_backup.setup() + blocks_mqtt_api.setup() + + # Create app + # OpenApi endpoints are set to /api/doc for backwards compatibility + prefix = f'/{config.name}' + app = FastAPI(lifespan=lifespan, + docs_url=f'{prefix}/api/doc', + redoc_url=f'{prefix}/api/redoc', + openapi_url=f'{prefix}/openapi.json') + + # Set standardized error response + add_exception_handlers(app) + + # Include all endpoints declared by modules + app.include_router(blocks_api.router, prefix=prefix) + app.include_router(system_api.router, prefix=prefix) + app.include_router(settings_api.router, prefix=prefix) + app.include_router(sim_api.router, prefix=prefix) + app.include_router(backup_api.router, prefix=prefix) + app.include_router(debug_api.router, prefix=prefix) + + return app diff --git a/brewblox_devcon_spark/backup_storage.py b/brewblox_devcon_spark/backup_storage.py deleted file mode 100644 index 7965e856..00000000 --- a/brewblox_devcon_spark/backup_storage.py +++ /dev/null @@ -1,104 +0,0 @@ -""" -Store regular backups of blocks on disk -""" - -import asyncio -import json -from datetime import datetime -from pathlib import Path - -from aiohttp import web -from brewblox_service import brewblox_logger, features, repeater, strex - -from brewblox_devcon_spark import controller, exceptions, service_status -from brewblox_devcon_spark.models import (Backup, BackupApplyResult, - BackupIdentity, ServiceConfig) - -LOGGER = brewblox_logger(__name__) -BASE_BACKUP_DIR = Path('./backup') - - -class BackupStorage(repeater.RepeaterFeature): - - def __init__(self, app: web.Application): - super().__init__(app) - - config: ServiceConfig = app['config'] - self.name = config.name - self.dir = BASE_BACKUP_DIR / self.name - self.interval_s = config.backup_interval - self.retry_interval_s = config.backup_retry_interval - - if self.retry_interval_s <= 0: - self.retry_interval_s = self.interval_s - - self.last_ok = False - self.enabled = self.interval_s > 0 - - async def prepare(self): - self.dir.mkdir(mode=0o777, parents=True, exist_ok=True) - - if not self.enabled: - raise repeater.RepeaterCancelled() - - async def run(self): - try: - self.last_ok = False - interval = self.interval_s if self.last_ok else self.retry_interval_s - await asyncio.sleep(interval) - - if service_status.is_synchronized(self.app): - name = f'autosave_blocks_{self.name}_' + datetime.today().strftime('%Y-%m-%d') - await self.save(BackupIdentity(name=name)) - self.last_ok = True - - except Exception as ex: - LOGGER.debug(f'{self} exception: {strex(ex)}') - raise ex - - async def save_portable(self) -> Backup: - return await controller.fget(self.app).make_backup() - - async def load_portable(self, data: Backup) -> BackupApplyResult: - return await controller.fget(self.app).apply_backup(data) - - async def all(self) -> list[BackupIdentity]: - return [BackupIdentity(name=f.stem) - for f - in self.dir.glob('*.json') - if f.is_file()] - - async def read(self, ident: BackupIdentity) -> Backup: - infile = self.dir / f'{ident.name}.json' - LOGGER.debug(f'Reading backup from {infile.resolve()}') - - with infile.open('r') as f: - return Backup(**json.load(f)) - - async def write(self, data: Backup) -> Backup: - if not data.name: - raise exceptions.InvalidInput('Missing name in backup') - outfile = self.dir / f'{data.name}.json' - LOGGER.debug(f'Writing backup to {outfile.resolve()}') - - with outfile.open('w') as f: - json.dump(data.dict(), f) - return data - - async def save(self, ident: BackupIdentity): - data = await controller.fget(self.app).make_backup() - data.name = ident.name - await self.write(data) - return data - - async def load(self, ident: BackupIdentity) -> BackupApplyResult: - data = await self.read(ident) - return await controller.fget(self.app).apply_backup(data) - - -def setup(app: web.Application): - features.add(app, BackupStorage(app)) - - -def fget(app: web.Application) -> BackupStorage: - return features.get(app, BackupStorage) diff --git a/brewblox_devcon_spark/block_analysis.py b/brewblox_devcon_spark/block_analysis.py index b6648cf1..ed015a7d 100644 --- a/brewblox_devcon_spark/block_analysis.py +++ b/brewblox_devcon_spark/block_analysis.py @@ -2,10 +2,10 @@ Calculate block metadata """ -from typing import Any, Optional, TypedDict +from typing import Any -from brewblox_devcon_spark.codec import bloxfield -from brewblox_devcon_spark.models import Block +from .codec import bloxfield +from .models import Block, BlockClaim, BlockRelation # Relations to these blocks should be ignored, # as they have no impact on control logic @@ -31,19 +31,6 @@ ] -class BlockRelation(TypedDict): - source: str - target: str - claimed: Optional[bool] - relation: list[str] - - -class BlockClaim(TypedDict): - source: str - target: str - intermediate: list[str] - - def _find_nested_relations( parent_id: str, relation: list[str], @@ -115,20 +102,20 @@ def calculate_relations(blocks: list[Block]) -> list[BlockRelation]: Returns: list[BlockRelation]: Valid relations between blocks in `blocks`. """ - relations = [] + relations: list[BlockRelation] = [] for block in blocks: if block.type in IGNORED_RELATION_TYPES: continue relations += _find_nested_relations(block.id, [], block.data) for block in blocks: - claim = block.data.get('claimedBy') - if claim and claim['id']: + raw_claim = block.data.get('claimedBy') + if raw_claim and raw_claim['id']: target = block.id - source = claim['id'] + source = raw_claim['id'] for r in relations: # pragma: no branch - if r['target'] == target and r['source'] == source: - r['claimed'] = True + if r.target == target and r.source == source: + r.claimed = True break return relations @@ -156,19 +143,19 @@ def calculate_claims(blocks: list[Block]) -> list[BlockClaim]: intermediate=[])) def extended_claim(claim: BlockClaim) -> BlockClaim: - source_claim = claim_dict.get(claim['source']) + source_claim = claim_dict.get(claim.source) if not source_claim: return claim - grand_source = source_claim['source'] - if grand_source in claim['intermediate']: + grand_source = source_claim.source + if grand_source in claim.intermediate: return claim # Circular claim # Shift claim, look further up the tree return extended_claim( BlockClaim(source=grand_source, - target=claim['target'], - intermediate=[*claim['intermediate'], claim['source']])) + target=claim.target, + intermediate=[*claim.intermediate, claim.source])) return [extended_claim(c) for c in [*claim_dict.values(), *channel_claims]] diff --git a/brewblox_devcon_spark/block_backup.py b/brewblox_devcon_spark/block_backup.py new file mode 100644 index 00000000..ea1f821f --- /dev/null +++ b/brewblox_devcon_spark/block_backup.py @@ -0,0 +1,99 @@ +""" +Store regular backups of blocks locally +""" + +import asyncio +import logging +from contextlib import asynccontextmanager +from contextvars import ContextVar +from datetime import datetime, timedelta + +from . import control, exceptions, state_machine, utils +from .models import Backup, BackupApplyResult, BackupIdentity + +LOGGER = logging.getLogger(__name__) +CV: ContextVar['BackupStorage'] = ContextVar('block_backup.BackupStorage') + + +class BackupStorage: + + def __init__(self): + self.config = utils.get_config() + self.state = state_machine.CV.get() + self.ctrl = control.CV.get() + + self.dir = self.config.backup_root_dir / self.config.name + self.dir.mkdir(mode=0o777, parents=True, exist_ok=True) + + async def save_portable(self) -> Backup: + return await self.ctrl.make_backup() + + async def load_portable(self, data: Backup) -> BackupApplyResult: + return await self.ctrl.apply_backup(data) + + async def all(self) -> list[BackupIdentity]: + return [BackupIdentity(name=f.stem) + for f + in self.dir.glob('*.json') + if f.is_file()] + + async def read(self, ident: BackupIdentity) -> Backup: + infile = self.dir / f'{ident.name}.json' + LOGGER.debug(f'Reading backup from {infile.resolve()}') + + with infile.open('r') as f: + return Backup.model_validate_json(f.read()) + + async def write(self, data: Backup) -> Backup: + if not data.name: + raise exceptions.InvalidInput('Missing name in backup') + outfile = self.dir / f'{data.name}.json' + LOGGER.debug(f'Writing backup to {outfile.resolve()}') + + with outfile.open('w') as f: + f.write(data.model_dump_json()) + return data + + async def save(self, ident: BackupIdentity): + data = await self.ctrl.make_backup() + data.name = ident.name + await self.write(data) + return data + + async def load(self, ident: BackupIdentity) -> BackupApplyResult: + data = await self.read(ident) + return await self.ctrl.apply_backup(data) + + async def run(self): + if self.state.is_synchronized(): + dt = datetime.today().strftime('%Y-%m-%d') + await self.save(BackupIdentity(name=f'autosave_blocks_{self.config.name}_{dt}')) + + async def repeat(self): + normal_interval = self.config.backup_interval + retry_interval = self.config.backup_retry_interval + interval = normal_interval + + if normal_interval <= timedelta(): + LOGGER.warning(f'Cancelling block backups (interval={normal_interval})') + return + + while True: + try: + await asyncio.sleep(interval.total_seconds()) + interval = normal_interval + await self.run() + except Exception as ex: + LOGGER.error(utils.strex(ex), exc_info=self.config.debug) + interval = retry_interval + + +@asynccontextmanager +async def lifespan(): + storage = CV.get() + async with utils.task_context(storage.repeat()): + yield + + +def setup(): + CV.set(BackupStorage()) diff --git a/brewblox_devcon_spark/block_store.py b/brewblox_devcon_spark/block_store.py deleted file mode 100644 index 74c73a25..00000000 --- a/brewblox_devcon_spark/block_store.py +++ /dev/null @@ -1,129 +0,0 @@ -""" -Stores sid/nid relations for blocks -""" - -import asyncio -import warnings -from contextlib import suppress - -from aiohttp import web -from brewblox_service import brewblox_logger, features, http, strex - -from brewblox_devcon_spark import const -from brewblox_devcon_spark.datastore import (STORE_URL, FlushedStore, - non_isolated) -from brewblox_devcon_spark.models import StoreEntry -from brewblox_devcon_spark.twinkeydict import TwinKeyDict, TwinKeyError - -BLOCK_STORE_KEY = '{id}-blocks-db' -READY_TIMEOUT_S = 60 - -SYS_OBJECTS = [ - {'keys': keys, 'data': {}} - for keys in const.SYS_OBJECT_KEYS -] - -LOGGER = brewblox_logger(__name__) - - -class ServiceBlockStore(FlushedStore, TwinKeyDict[str, int, dict]): - """ - TwinKeyDict subclass to periodically flush contained objects to Redis. - """ - - def __init__(self, app: web.Application, defaults: list[StoreEntry]): - self: TwinKeyDict[str, int, dict] - FlushedStore.__init__(self, app) - TwinKeyDict.__init__(self) - - self.key: str = None - self._defaults = defaults - self._ready_event: asyncio.Event = None - - self.clear() # inserts defaults - - def __str__(self): - return f'<{type(self).__name__}>' - - async def startup(self, app: web.Application): - await FlushedStore.startup(self, app) - self._ready_event = asyncio.Event() - - async def shutdown(self, app: web.Application): - await FlushedStore.shutdown(self, app) - self._ready_event = None - - async def read(self, device_id: str): - key = BLOCK_STORE_KEY.format(id=device_id) - data = [] - - try: - self.key = None - self._ready_event.clear() - if not self.isolated: - resp = await http.session(self.app).post(f'{STORE_URL}/get', json={ - 'id': key, - 'namespace': const.SPARK_NAMESPACE, - }) - self.key = key - content = await resp.json() - content_value = content.get('value') or {} - data = content_value.get('data') or [] - LOGGER.info(f'{self} Read {len(data)} blocks') - - except asyncio.CancelledError: # pragma: no cover - raise - - except Exception as ex: - warnings.warn(f'{self} read error {strex(ex)}') - - finally: - # Clear -> load from database -> merge defaults - TwinKeyDict.clear(self) - for obj in data: - TwinKeyDict.__setitem__(self, obj['keys'], obj['data']) - for obj in self._defaults: - with suppress(TwinKeyError): - if obj['keys'] not in self: - self.__setitem__(obj['keys'], obj['data']) - - self._ready_event.set() - - @non_isolated - async def write(self): - await asyncio.wait_for(self._ready_event.wait(), READY_TIMEOUT_S) - if self.key is None: - raise RuntimeError('Document key not set - did read() fail?') - data: list[StoreEntry] = [ - {'keys': keys, 'data': content} - for keys, content in self.items() - ] - await http.session(self.app).post(f'{STORE_URL}/set', json={ - 'value': { - 'id': self.key, - 'namespace': const.SPARK_NAMESPACE, - 'data': data, - }, - }) - LOGGER.info(f'{self} Saved {len(data)} block(s)') - - def __setitem__(self, keys, item): - TwinKeyDict.__setitem__(self, keys, item) - self.set_changed() - - def __delitem__(self, keys): - TwinKeyDict.__delitem__(self, keys) - self.set_changed() - - def clear(self): - TwinKeyDict.clear(self) - for obj in self._defaults: - self.__setitem__(obj['keys'], obj['data']) - - -def setup(app: web.Application): - features.add(app, ServiceBlockStore(app, defaults=SYS_OBJECTS)) - - -def fget(app: web.Application) -> ServiceBlockStore: - return features.get(app, ServiceBlockStore) diff --git a/brewblox_devcon_spark/broadcast.py b/brewblox_devcon_spark/broadcast.py new file mode 100644 index 00000000..aa323512 --- /dev/null +++ b/brewblox_devcon_spark/broadcast.py @@ -0,0 +1,82 @@ +""" +Intermittently broadcasts status and blocks to the eventbus +""" + + +import asyncio +import logging +from contextlib import asynccontextmanager +from datetime import timedelta + +from . import const, control, mqtt, state_machine, utils +from .block_analysis import calculate_claims, calculate_relations +from .models import HistoryEvent, ServiceStateEvent, ServiceStateEventData + +LOGGER = logging.getLogger(__name__) + + +class Broadcaster: + + def __init__(self): + self.config = utils.get_config() + self.controller = control.CV.get() + + self.state_topic = f'{self.config.state_topic}/{self.config.name}' + self.history_topic = f'{self.config.history_topic}/{self.config.name}' + + async def run(self): + mqtt_client = mqtt.CV.get() + state = state_machine.CV.get() + blocks = [] + + try: + if state.is_synchronized(): + blocks, logged_blocks = await self.controller.read_all_broadcast_blocks() + + # Convert list to key/value format suitable for history + history_data = { + block.id: block.data + for block in logged_blocks + if not block.id.startswith(const.GENERATED_ID_PREFIX) + } + + mqtt_client.publish(self.history_topic, + HistoryEvent( + key=self.config.name, + data=history_data, + ).model_dump(mode='json')) + + finally: + # State event is always published + mqtt_client.publish(self.state_topic, + ServiceStateEvent( + key=self.config.name, + data=ServiceStateEventData( + status=state.desc(), + blocks=blocks, + relations=calculate_relations(blocks), + claims=calculate_claims(blocks) + ) + ).model_dump(mode='json'), + retain=True) + + async def repeat(self): + interval = self.config.broadcast_interval + + if interval <= timedelta(): + LOGGER.warning(f'Cancelling broadcaster (interval={interval})') + return + + while True: + try: + await asyncio.sleep(interval.total_seconds()) + await self.run() + except Exception as ex: + LOGGER.error(utils.strex(ex), exc_info=self.config.debug) + + +@asynccontextmanager +async def lifespan(): + bc = Broadcaster() + async with utils.task_context(bc.repeat()): + yield diff --git a/brewblox_devcon_spark/broadcaster.py b/brewblox_devcon_spark/broadcaster.py deleted file mode 100644 index 693a76e2..00000000 --- a/brewblox_devcon_spark/broadcaster.py +++ /dev/null @@ -1,92 +0,0 @@ -""" -Intermittently broadcasts status and blocks to the eventbus -""" - - -import asyncio -import json - -from aiohttp import web -from brewblox_service import brewblox_logger, features, mqtt, repeater, strex - -from brewblox_devcon_spark import const, controller, service_status -from brewblox_devcon_spark.block_analysis import (calculate_claims, - calculate_relations) -from brewblox_devcon_spark.models import ServiceConfig - -LOGGER = brewblox_logger(__name__) - - -class Broadcaster(repeater.RepeaterFeature): - - def __init__(self, app: web.Application): - super().__init__(app) - - config: ServiceConfig = app['config'] - self.name = config.name - self.interval = config.broadcast_interval - self.isolated = self.interval <= 0 or config.isolated - self.state_topic = f'{config.state_topic}/{config.name}' - self.history_topic = f'{config.history_topic}/{config.name}' - - async def prepare(self): - if self.isolated: - raise repeater.RepeaterCancelled() - - async def before_shutdown(self, app: web.Application): - await self.end() - - async def run(self): - try: - await asyncio.sleep(self.interval) - blocks = [] - - try: - if service_status.is_synchronized(self.app): - blocks, logged_blocks = await controller.fget(self.app).read_all_broadcast_blocks() - - # Convert list to key/value format suitable for history - history_data = { - block.id: block.data - for block in logged_blocks - if not block.id.startswith(const.GENERATED_ID_PREFIX) - } - - await mqtt.publish(self.app, - topic=self.history_topic, - payload=json.dumps({ - 'key': self.name, - 'data': history_data, - }), - err=False, - ) - - finally: - # State event is always published - await mqtt.publish(self.app, - topic=self.state_topic, - payload=json.dumps({ - 'key': self.name, - 'type': 'Spark.state', - 'data': { - 'status': service_status.desc(self.app).dict(), - 'blocks': [v.dict() for v in blocks], - 'relations': calculate_relations(blocks), - 'claims': calculate_claims(blocks), - }, - }), - retain=True, - err=False, - ) - - except Exception as ex: - LOGGER.debug(f'{self} exception: {strex(ex)}') - raise ex - - -def setup(app: web.Application): - features.add(app, Broadcaster(app)) - - -def fget(app: web.Application) -> Broadcaster: - return features.get(app, Broadcaster) diff --git a/brewblox_devcon_spark/codec/__init__.py b/brewblox_devcon_spark/codec/__init__.py index 12f8a6cb..4aa59fb4 100644 --- a/brewblox_devcon_spark/codec/__init__.py +++ b/brewblox_devcon_spark/codec/__init__.py @@ -2,20 +2,17 @@ Default exports for codec module """ +import logging from base64 import b64decode, b64encode +from contextvars import ContextVar from typing import Optional -from aiohttp import web -from brewblox_service import brewblox_logger, features, strex from google.protobuf import json_format -from brewblox_devcon_spark import exceptions -from brewblox_devcon_spark.models import (DecodedPayload, EncodedPayload, - IntermediateRequest, - IntermediateResponse) - -from . import pb2, time_utils, unit_conversion -from .lookup import COMBINED_LOOKUPS, INTERFACE_LOOKUPS, OBJECT_LOOKUPS +from .. import exceptions, utils +from ..models import (DecodedPayload, EncodedPayload, IntermediateRequest, + IntermediateResponse) +from . import lookup, pb2, time_utils, unit_conversion from .opts import (DateFormatOpt, DecodeOpts, FilterOpt, MetadataOpt, ProtoEnumOpt) from .processor import ProtobufProcessor @@ -24,7 +21,9 @@ DEPRECATED_TYPE_STR = 'DeprecatedObject' UNKNOWN_TYPE_STR = 'UnknownType' ERROR_TYPE_STR = 'ErrorObject' -LOGGER = brewblox_logger(__name__) + +LOGGER = logging.getLogger(__name__) +CV: ContextVar['Codec'] = ContextVar('codec.Codec') def split_type(type_str: str) -> tuple[str, Optional[str]]: @@ -41,20 +40,18 @@ def join_type(blockType: str, subtype: Optional[str]) -> str: return blockType -class Codec(features.ServiceFeature): - def __init__(self, app: web.Application, strip_readonly=True): - super().__init__(app) - self._processor = ProtobufProcessor(unit_conversion.fget(app), - strip_readonly) +class Codec: + def __init__(self, strip_readonly=True): + self._processor = ProtobufProcessor(strip_readonly) def encode_request(self, request: IntermediateRequest) -> str: try: message = pb2.command_pb2.Request() - json_format.ParseDict(request.clean_dict(), message) + json_format.ParseDict(request.model_dump(mode='json'), message) return b64encode(message.SerializeToString()).decode() except Exception as ex: - msg = strex(ex) + msg = utils.strex(ex) LOGGER.debug(msg, exc_info=True) raise exceptions.EncodeException(msg) @@ -74,18 +71,18 @@ def decode_request(self, b64_encoded: str) -> IntermediateRequest: return IntermediateRequest(**decoded) except Exception as ex: - msg = strex(ex) + msg = utils.strex(ex) LOGGER.debug(msg, exc_info=True) raise exceptions.DecodeException(msg) def encode_response(self, response: IntermediateResponse) -> str: try: message = pb2.command_pb2.Response() - json_format.ParseDict(response.clean_dict(), message) + json_format.ParseDict(response.model_dump(mode='json'), message) return b64encode(message.SerializeToString()).decode() except Exception as ex: - msg = strex(ex) + msg = utils.strex(ex) LOGGER.debug(msg, exc_info=True) raise exceptions.EncodeException(msg) @@ -105,7 +102,7 @@ def decode_response(self, b64_encoded: str) -> IntermediateResponse: return IntermediateResponse(**decoded) except Exception as ex: - msg = strex(ex) + msg = utils.strex(ex) LOGGER.debug(msg, exc_info=True) raise exceptions.DecodeException(msg) @@ -128,28 +125,28 @@ def encode_payload(self, payload: DecodedPayload) -> EncodedPayload: # Interface-only payload if payload.content is None: - lookup = next((v for v in COMBINED_LOOKUPS - if v.type_str == payload.blockType)) + impl = next((v for v in lookup.CV_COMBINED.get() + if v.type_str == payload.blockType)) return EncodedPayload( blockId=payload.blockId, - blockType=lookup.type_int, + blockType=impl.type_int, ) # Payload contains data - lookup = next((v for v in OBJECT_LOOKUPS - if v.type_str == payload.blockType - and v.subtype_str == payload.subtype)) + impl = next((v for v in lookup.CV_OBJECTS.get() + if v.type_str == payload.blockType + and v.subtype_str == payload.subtype)) - message = lookup.message_cls() + message = impl.message_cls() payload = self._processor.pre_encode(message.DESCRIPTOR, - payload.copy(deep=True)) + payload.model_copy(deep=True)) json_format.ParseDict(payload.content, message) content: str = b64encode(message.SerializeToString()).decode() return EncodedPayload( blockId=payload.blockId, - blockType=lookup.type_int, - subtype=lookup.subtype_int, + blockType=impl.type_int, + subtype=impl.subtype_int, content=content, mask=payload.mask, maskMode=payload.maskMode, @@ -161,7 +158,7 @@ def encode_payload(self, payload: DecodedPayload) -> EncodedPayload: raise exceptions.EncodeException(msg) except Exception as ex: - msg = strex(ex) + msg = utils.strex(ex) LOGGER.debug(msg, exc_info=True) raise exceptions.EncodeException(msg) @@ -180,14 +177,14 @@ def decode_payload(self, ) # First, try to find an object lookup - lookup = next((v for v in OBJECT_LOOKUPS - if payload.blockType in [v.type_str, v.type_int] - and payload.subtype in [v.subtype_str, v.subtype_int]), None) + impl = next((v for v in lookup.CV_OBJECTS.get() + if payload.blockType in [v.type_str, v.type_int] + and payload.subtype in [v.subtype_str, v.subtype_int]), None) - if lookup: + if impl: # We have an object lookup, and can decode the content int_enum = opts.enums == ProtoEnumOpt.INT - message = lookup.message_cls() + message = impl.message_cls() message.ParseFromString(b64decode(payload.content)) content: dict = json_format.MessageToDict( message=message, @@ -197,8 +194,8 @@ def decode_payload(self, ) decoded = DecodedPayload( blockId=payload.blockId, - blockType=lookup.type_str, - subtype=lookup.subtype_str, + blockType=impl.type_str, + subtype=impl.subtype_str, content=content, mask=payload.mask, maskMode=payload.maskMode, @@ -206,13 +203,13 @@ def decode_payload(self, return self._processor.post_decode(message.DESCRIPTOR, decoded, opts) # No object lookup found. Try the interfaces. - intf_lookup = next((v for v in INTERFACE_LOOKUPS - if payload.blockType in [v.type_str, v.type_int]), None) + intf_impl = next((v for v in lookup.CV_INTERFACES.get() + if payload.blockType in [v.type_str, v.type_int]), None) - if intf_lookup: + if intf_impl: return DecodedPayload( blockId=payload.blockId, - blockType=intf_lookup.type_str, + blockType=intf_impl.type_str, ) # No lookup of any kind found @@ -228,7 +225,7 @@ def decode_payload(self, ) except Exception as ex: - msg = strex(ex) + msg = utils.strex(ex) LOGGER.debug(msg, exc_info=True) return DecodedPayload( blockId=payload.blockId, @@ -241,13 +238,10 @@ def decode_payload(self, ) -def setup(app: web.Application): - unit_conversion.setup(app) - features.add(app, Codec(app)) - - -def fget(app: web.Application) -> Codec: - return features.get(app, Codec) +def setup(): + lookup.setup() + unit_conversion.setup() + CV.set(Codec()) __all__ = [ @@ -256,7 +250,7 @@ def fget(app: web.Application) -> Codec: 'Codec', 'setup', - 'fget', + 'CV', 'DecodeOpts', 'ProtoEnumOpt', diff --git a/brewblox_devcon_spark/codec/lookup.py b/brewblox_devcon_spark/codec/lookup.py index f9b51487..f18fad85 100644 --- a/brewblox_devcon_spark/codec/lookup.py +++ b/brewblox_devcon_spark/codec/lookup.py @@ -3,6 +3,7 @@ """ +from contextvars import ContextVar from dataclasses import dataclass from typing import Generator, Optional, Type @@ -19,6 +20,11 @@ BLOCK_INTERFACE_TYPE_END = 255 +CV_OBJECTS: ContextVar[list['ObjectLookup']] = ContextVar('lookup.objects') +CV_INTERFACES: ContextVar[list['InterfaceLookup']] = ContextVar('lookup.interfaces') +CV_COMBINED: ContextVar[list['InterfaceLookup']] = ContextVar('lookup.combined') + + @dataclass(frozen=True) class InterfaceLookup: type_str: str @@ -61,38 +67,43 @@ def _object_lookup_generator() -> Generator[ObjectLookup, None, None]: ) -OBJECT_LOOKUPS: list[ObjectLookup] = [ - # Actual objects - *_object_lookup_generator(), - - # Custom test objects - ObjectLookup( - type_str='EdgeCase', - type_int=9001, - subtype_str=None, - subtype_int=0, - message_cls=pb2.EdgeCase_pb2.Block, - ), - ObjectLookup( - type_str='EdgeCase', - type_int=9001, - subtype_str='SubCase', - subtype_int=1, - message_cls=pb2.EdgeCase_pb2.SubCase, - ), -] - -INTERFACE_LOOKUPS: list[InterfaceLookup] = [ - *_interface_lookup_generator(), - - # Custom test objects - InterfaceLookup( - type_str='EdgeCase', - type_int=9001, - ), -] - -COMBINED_LOOKUPS: list[InterfaceLookup] = [ - *OBJECT_LOOKUPS, - *INTERFACE_LOOKUPS, -] +def setup(): + objects: list[ObjectLookup] = [ + # Actual objects + *_object_lookup_generator(), + + # Custom test objects + ObjectLookup( + type_str='EdgeCase', + type_int=9001, + subtype_str=None, + subtype_int=0, + message_cls=pb2.EdgeCase_pb2.Block, + ), + ObjectLookup( + type_str='EdgeCase', + type_int=9001, + subtype_str='SubCase', + subtype_int=1, + message_cls=pb2.EdgeCase_pb2.SubCase, + ), + ] + + interfaces: list[InterfaceLookup] = [ + *_interface_lookup_generator(), + + # Custom test objects + InterfaceLookup( + type_str='EdgeCase', + type_int=9001, + ), + ] + + combined: list[InterfaceLookup] = [ + *objects, + *interfaces, + ] + + CV_OBJECTS.set(objects) + CV_INTERFACES.set(interfaces) + CV_COMBINED.set(combined) diff --git a/brewblox_devcon_spark/codec/processor.py b/brewblox_devcon_spark/codec/processor.py index 38f348fc..28efa267 100644 --- a/brewblox_devcon_spark/codec/processor.py +++ b/brewblox_devcon_spark/codec/processor.py @@ -3,6 +3,7 @@ """ import ipaddress +import logging import re from base64 import b64decode, b64encode from binascii import hexlify, unhexlify @@ -11,18 +12,17 @@ from socket import htonl, ntohl from typing import Any, Iterator -from brewblox_service import brewblox_logger from google.protobuf import json_format from google.protobuf.descriptor import Descriptor, FieldDescriptor from brewblox_devcon_spark.models import DecodedPayload, MaskMode +from . import unit_conversion from .opts import DateFormatOpt, DecodeOpts, FilterOpt, MetadataOpt from .pb2 import brewblox_pb2 from .time_utils import serialize_datetime -from .unit_conversion import UnitConverter -LOGGER = brewblox_logger(__name__) +LOGGER = logging.getLogger(__name__) @dataclass(frozen=True) @@ -38,8 +38,8 @@ class OptionElement(): class ProtobufProcessor(): _BREWBLOX_PROVIDER: FieldDescriptor = brewblox_pb2.field - def __init__(self, converter: UnitConverter, strip_readonly=True): - self._converter = converter + def __init__(self, strip_readonly=True): + self._converter = unit_conversion.CV.get() self._strip_readonly = strip_readonly symbols = re.escape('[]<>') diff --git a/brewblox_devcon_spark/codec/unit_conversion.py b/brewblox_devcon_spark/codec/unit_conversion.py index 95aae7ad..9771b6c4 100644 --- a/brewblox_devcon_spark/codec/unit_conversion.py +++ b/brewblox_devcon_spark/codec/unit_conversion.py @@ -2,23 +2,15 @@ User-configurable unit conversion """ +import logging +from contextvars import ContextVar from dataclasses import dataclass -from aiohttp import web -from brewblox_service import brewblox_logger, features from pint import UnitRegistry from brewblox_devcon_spark.exceptions import InvalidInput -LOGGER = brewblox_logger(__name__) - -# Pint makes multiple I/O calls while constructing its UnitRegistry -# As long as we never modify the unit registry, we can keep it in module scope -# This significantly reduces setup time for unit tests -_UREG = UnitRegistry() - SYSTEM_TEMP = 'degC' - FORMATS = { 'NotSet': '', 'Celsius': '{temp}', @@ -35,6 +27,14 @@ 'DeltaCelsiusMultHour': 'delta_{temp} * hour', } +# Pint makes multiple I/O calls while constructing its UnitRegistry +# As long as we never modify the unit registry, we can keep it in module scope +# This significantly reduces setup time for unit tests +_UREG = UnitRegistry() + +LOGGER = logging.getLogger(__name__) +CV: ContextVar['UnitConverter'] = ContextVar('unit_conversion.UnitConverter') + @dataclass(frozen=True) class UnitMapping: @@ -55,10 +55,9 @@ def derived_table(user_temp) -> dict[str, UnitMapping]: } -class UnitConverter(features.ServiceFeature): +class UnitConverter: - def __init__(self, app: web.Application): - super().__init__(app) + def __init__(self): # Init with system temp. All mappings will have system_value == user_value self._table = derived_table(SYSTEM_TEMP) @@ -93,9 +92,5 @@ def to_user_unit(self, id): return self._table[id].user_value -def setup(app: web.Application): - features.add(app, UnitConverter(app)) - - -def fget(app: web.Application) -> UnitConverter: - return features.get(app, UnitConverter) +def setup(): + CV.set(UnitConverter()) diff --git a/brewblox_devcon_spark/commander.py b/brewblox_devcon_spark/command.py similarity index 76% rename from brewblox_devcon_spark/commander.py rename to brewblox_devcon_spark/command.py index 20f27a3e..8a48995e 100644 --- a/brewblox_devcon_spark/commander.py +++ b/brewblox_devcon_spark/command.py @@ -4,30 +4,36 @@ """ import asyncio -from typing import Optional - -from aiohttp import web -from brewblox_service import brewblox_logger, features, strex - -from brewblox_devcon_spark import codec, connection, exceptions, service_status -from brewblox_devcon_spark.codec.opts import DecodeOpts -from brewblox_devcon_spark.models import (ControllerDescription, - DecodedPayload, DeviceDescription, - EncodedPayload, ErrorCode, - FirmwareBlock, FirmwareBlockIdentity, - FirmwareDescription, - HandshakeMessage, - IntermediateRequest, - IntermediateResponse, MaskMode, - Opcode, ServiceConfig) - -LOGGER = brewblox_logger(__name__) +import logging +from contextvars import ContextVar +from . import codec, connection, exceptions, state_machine, utils +from .codec.opts import DecodeOpts +from .models import (ControllerDescription, DecodedPayload, DeviceDescription, + EncodedPayload, ErrorCode, FirmwareBlock, + FirmwareBlockIdentity, FirmwareDescription, + HandshakeMessage, IntermediateRequest, + IntermediateResponse, MaskMode, Opcode) WELCOME_PREFIX = '!BREWBLOX' +HANDSHAKE_KEYS = [ + 'name', + 'firmware_version', + 'proto_version', + 'firmware_date', + 'proto_date', + 'system_version', + 'platform', + 'reset_reason_hex', + 'reset_data_hex', + 'device_id', +] + +LOGGER = logging.getLogger(__name__) +CV: ContextVar['CboxCommander'] = ContextVar('command.CboxCommander') -class SparkCommander(features.ServiceFeature): +class CboxCommander: default_decode_opts = codec.DecodeOpts() stored_decode_opts = codec.DecodeOpts(enums=codec.ProtoEnumOpt.INT) @@ -36,23 +42,19 @@ class SparkCommander(features.ServiceFeature): metadata=codec.MetadataOpt.POSTFIX, dates=codec.DateFormatOpt.SECONDS) - def __init__(self, app: web.Application): - super().__init__(app) - config: ServiceConfig = app['config'] + def __init__(self): + self.config = utils.get_config() + self.state = state_machine.CV.get() + self.codec = codec.CV.get() + self.conn = connection.CV.get() self._msgid = 0 - self._timeout = config.command_timeout self._active_messages: dict[int, asyncio.Future[IntermediateResponse]] = {} - self._codec = codec.fget(app) - self._conn = connection.fget(app) + self._empty_ev = asyncio.Event() + self._empty_ev.set() - def __str__(self): - return f'<{type(self).__name__} for {self._conn}>' - - async def startup(self, app: web.Application): - self._active_messages.clear() - self._conn.on_event = self._on_event - self._conn.on_response = self._on_response + self.conn.on_event = self._on_event + self.conn.on_response = self._on_response def _next_id(self): self._msgid = (self._msgid + 1) % 0xFFFF @@ -75,10 +77,10 @@ def _to_payload(self, else: payload = DecodedPayload(blockId=block.nid) - return self._codec.encode_payload(payload) + return self.codec.encode_payload(payload) def _to_block(self, payload: EncodedPayload, opts: DecodeOpts) -> FirmwareBlock: - payload = self._codec.decode_payload(payload, opts=opts) + payload = self.codec.decode_payload(payload, opts=opts) return FirmwareBlock( nid=payload.blockId, type=codec.join_type(payload.blockType, payload.subtype), @@ -87,7 +89,8 @@ def _to_block(self, payload: EncodedPayload, opts: DecodeOpts) -> FirmwareBlock: async def _on_event(self, msg: str): if msg.startswith(WELCOME_PREFIX): - handshake = HandshakeMessage(*msg.removeprefix('!').split(',')) + handshake_values = msg.removeprefix('!').split(',') + handshake = HandshakeMessage(**dict(zip(HANDSHAKE_KEYS, handshake_values))) LOGGER.info(handshake) desc = ControllerDescription( @@ -104,15 +107,15 @@ async def _on_event(self, msg: str): device_id=handshake.device_id, ), ) - service_status.set_acknowledged(self.app, desc) + self.state.set_acknowledged(desc) else: LOGGER.info(f'Spark log: `{msg}`') async def _on_response(self, msg: str): try: - # LOGGER.debug(f'response: {msg}') - response = self._codec.decode_response(msg) + LOGGER.trace(f'response: {msg}') + response = self.codec.decode_response(msg) # Get the Future object awaiting this request # the msgid field is key @@ -122,11 +125,11 @@ async def _on_response(self, msg: str): fut.set_result(response) except Exception as ex: - LOGGER.error(f'Error parsing message `{msg}` : {strex(ex)}') + LOGGER.error(f'Error parsing message `{msg}` : {utils.strex(ex)}') async def _execute(self, opcode: Opcode, - payload: Optional[EncodedPayload], + payload: EncodedPayload | None, ) -> list[EncodedPayload]: msg_id = self._next_id() @@ -136,14 +139,16 @@ async def _execute(self, payload=payload ) - msg = self._codec.encode_request(request) + msg = self.codec.encode_request(request) fut: asyncio.Future[IntermediateResponse] = asyncio.get_running_loop().create_future() self._active_messages[msg_id] = fut + self._empty_ev.clear() try: - # LOGGER.debug(f'request: {msg}') - await self._conn.send_request(msg) - response = await asyncio.wait_for(fut, timeout=self._timeout) + LOGGER.trace(f'request: {msg}') + await self.conn.send_request(msg) + response = await asyncio.wait_for(fut, + timeout=self.config.command_timeout.total_seconds()) if response.error != ErrorCode.OK: raise exceptions.CommandException(f'{opcode.name}, {response.error.name}') @@ -155,9 +160,8 @@ async def _execute(self, finally: del self._active_messages[msg_id] - - async def start_reconnect(self): - await self._conn.start_reconnect() + if not self._active_messages: + self._empty_ev.set() async def validate(self, block: FirmwareBlock) -> FirmwareBlock: request = IntermediateRequest( @@ -165,7 +169,7 @@ async def validate(self, block: FirmwareBlock) -> FirmwareBlock: opcode=Opcode.NONE, payload=self._to_payload(block), ) - self._codec.encode_request(request) + self.codec.encode_request(request) return block async def noop(self) -> None: @@ -297,10 +301,15 @@ async def firmware_update(self) -> None: None, ) + async def reset_connection(self) -> None: + await self.conn.reset() + + async def end_connection(self) -> None: + await self.conn.end() -def setup(app: web.Application): - features.add(app, SparkCommander(app)) + async def wait_empty(self) -> None: + await self._empty_ev.wait() -def fget(app: web.Application) -> SparkCommander: - return features.get(app, SparkCommander) +def setup(): + CV.set(CboxCommander()) diff --git a/brewblox_devcon_spark/connection/__init__.py b/brewblox_devcon_spark/connection/__init__.py index 2105dc95..e86070f2 100644 --- a/brewblox_devcon_spark/connection/__init__.py +++ b/brewblox_devcon_spark/connection/__init__.py @@ -1,12 +1,21 @@ -from aiohttp import web +from contextlib import asynccontextmanager from . import connection_handler, mqtt_connection +from .connection_handler import CV -def setup(app: web.Application): - mqtt_connection.setup(app) - connection_handler.setup(app) +@asynccontextmanager +async def lifespan(): + async with connection_handler.lifespan(): + yield -def fget(app: web.Application): - return connection_handler.fget(app) +def setup(): + mqtt_connection.setup() + connection_handler.setup() + + +__all__ = [ + 'setup', + 'CV' +] diff --git a/brewblox_devcon_spark/connection/cbox_parser.py b/brewblox_devcon_spark/connection/cbox_parser.py index 53f5752b..a9258459 100644 --- a/brewblox_devcon_spark/connection/cbox_parser.py +++ b/brewblox_devcon_spark/connection/cbox_parser.py @@ -2,13 +2,12 @@ Parses stream data into controlbox events and data """ +import logging import re from queue import Queue from typing import Generator -from brewblox_service import brewblox_logger - -LOGGER = brewblox_logger(__name__) +LOGGER = logging.getLogger(__name__) # Pattern: '{start}(?P[^{start}]*?){end}' EVENT_END = '>' @@ -17,7 +16,7 @@ DATA_PATTERN = re.compile('^(?P[^^]*?)\n') -class ControlboxParser(): +class CboxParser: def __init__(self): self._buffer: str = '' diff --git a/brewblox_devcon_spark/connection/connection_handler.py b/brewblox_devcon_spark/connection/connection_handler.py index bfa23dff..47a4fa89 100644 --- a/brewblox_devcon_spark/connection/connection_handler.py +++ b/brewblox_devcon_spark/connection/connection_handler.py @@ -1,48 +1,43 @@ import asyncio -from contextlib import suppress - -from aiohttp import web -from brewblox_service import brewblox_logger, features, repeater, strex - -from brewblox_devcon_spark import exceptions, service_status, service_store -from brewblox_devcon_spark.models import DiscoveryType, ServiceConfig +import logging +from contextlib import asynccontextmanager, suppress +from contextvars import ContextVar +from datetime import timedelta +from .. import exceptions, state_machine, utils +from ..models import DiscoveryType from .connection_impl import ConnectionCallbacks, ConnectionImplBase from .mock_connection import connect_mock from .mqtt_connection import discover_mqtt from .stream_connection import (connect_simulation, connect_tcp, connect_usb, discover_mdns, discover_usb) -LOGGER = brewblox_logger(__name__) - -BASE_RECONNECT_DELAY_S = 2 -MAX_RECONNECT_DELAY_S = 30 MAX_RETRY_COUNT = 20 -DISCOVERY_INTERVAL_S = 5 -DISCOVERY_TIMEOUT_S = 120 +LOGGER = logging.getLogger(__name__) + +CV: ContextVar['ConnectionHandler'] = ContextVar('connection_handler.ConnectionHandler') -def calc_backoff(value: float) -> float: +def calc_interval(value: timedelta | None) -> timedelta: + config = utils.get_config() + if value: - return min(MAX_RECONNECT_DELAY_S, round(1.5 * value)) + return min(value * config.connect_backoff, config.connect_interval_max) else: - return BASE_RECONNECT_DELAY_S + return config.connect_interval -class ConnectionHandler(repeater.RepeaterFeature, ConnectionCallbacks): - def __init__(self, app: web.Application): - super().__init__(app) +class ConnectionHandler(ConnectionCallbacks): + def __init__(self): + self.config = utils.get_config() + self.state = state_machine.CV.get() + self._enabled: bool = True + self._interval: timedelta = calc_interval(None) self._attempts: int = 0 self._impl: ConnectionImplBase = None - def __str__(self): - return f'<{type(self).__name__} for {self._impl}>' - - async def before_shutdown(self, app: web.Application): - await self.end() - @property def connected(self) -> bool: return self._impl is not None \ @@ -50,22 +45,20 @@ def connected(self) -> bool: @property def usb_compatible(self) -> bool: - config: ServiceConfig = self.app['config'] - # Simulations (internal or external) do not use USB - if config.mock or config.simulation: + if self.config.mock or self.config.simulation: return False # Hardcoded addresses take precedence over device discovery - if config.device_serial or config.device_host: - return config.device_serial is not None + if self.config.device_serial or self.config.device_host: + return self.config.device_serial is not None # USB is explicitly enabled - if config.discovery == DiscoveryType.usb: + if self.config.discovery == DiscoveryType.usb: return True # TCP is explicitly enabled - if config.discovery != DiscoveryType.all: + if self.config.discovery != DiscoveryType.all: return False # Spark models can be identified by device ID @@ -73,7 +66,7 @@ def usb_compatible(self) -> bool: # Spark 4 uses 6 bytes / 12 characters # Spark simulations can have variable length IDs # USB should only be disabled if we're sure it is not supported - if config.device_id and len(config.device_id) == 12: + if self.config.device_id and len(self.config.device_id) == 12: return False # We're not sure @@ -92,116 +85,123 @@ async def on_response(self, msg: str): """ async def discover(self) -> ConnectionImplBase: - config: ServiceConfig = self.app['config'] - - discovery_type = config.discovery + discovery_type = self.config.discovery LOGGER.info(f'Discovering devices... ({discovery_type})') try: - async with asyncio.timeout(DISCOVERY_TIMEOUT_S): + async with asyncio.timeout(self.config.discovery_timeout.total_seconds()): while True: if discovery_type in [DiscoveryType.all, DiscoveryType.usb]: - result = await discover_usb(self.app, self) + result = await discover_usb(self) if result: return result if discovery_type in [DiscoveryType.all, DiscoveryType.mdns]: - result = await discover_mdns(self.app, self) + result = await discover_mdns(self) if result: return result if discovery_type in [DiscoveryType.all, DiscoveryType.mqtt]: - result = await discover_mqtt(self.app, self) + result = await discover_mqtt(self) if result: return result - await asyncio.sleep(DISCOVERY_INTERVAL_S) + await asyncio.sleep(self.config.discovery_interval.total_seconds()) except asyncio.TimeoutError: raise ConnectionAbortedError('Discovery timeout') async def connect(self) -> ConnectionImplBase: - config: ServiceConfig = self.app['config'] - - mock = config.mock - simulation = config.simulation - device_serial = config.device_serial - device_host = config.device_host - device_port = config.device_port + mock = self.config.mock + simulation = self.config.simulation + device_serial = self.config.device_serial + device_host = self.config.device_host + device_port = self.config.device_port if mock: - return await connect_mock(self.app, self) + return await connect_mock(self) elif simulation: - return await connect_simulation(self.app, self) + return await connect_simulation(self) elif device_serial: - return await connect_usb(self.app, self, device_serial) + return await connect_usb(self, device_serial) elif device_host: - return await connect_tcp(self.app, self, device_host, device_port) + return await connect_tcp(self, device_host, device_port) else: return await self.discover() async def run(self): - """Implements RepeaterFeature.run""" - delay = service_store.get_reconnect_delay(self.app) - try: if self._attempts > MAX_RETRY_COUNT: raise ConnectionAbortedError('Retry attempts exhausted') - await asyncio.sleep(delay) - await service_status.wait_enabled(self.app) - + await self.state.wait_enabled() self._impl = await self.connect() await self._impl.connected.wait() - service_status.set_connected(self.app, - self._impl.kind, - self._impl.address) + self.state.set_connected(self._impl.kind, + self._impl.address) self._attempts = 0 - self._reconnect_interval = 0 + self._interval = calc_interval(None) await self._impl.disconnected.wait() raise ConnectionError('Disconnected') except ConnectionAbortedError as ex: - LOGGER.error(strex(ex)) - service_store.set_reconnect_delay(self.app, calc_backoff(delay)) + LOGGER.error(utils.strex(ex)) + self._interval = calc_interval(self._interval) # USB devices that were plugged in after container start are not visible # If we are potentially connecting to a USB device, we need to restart if self.usb_compatible: - raise web.GracefulExit() + utils.graceful_shutdown(utils.strex(ex)) else: self._attempts = 0 except Exception as ex: self._attempts += 1 if self._attempts == 1: - LOGGER.error(strex(ex)) + LOGGER.error(utils.strex(ex)) else: - LOGGER.debug(strex(ex)) + LOGGER.debug(utils.strex(ex), exc_info=True) finally: with suppress(Exception): await self._impl.close() - service_status.set_disconnected(self.app) + self._impl = None + self.state.set_disconnected() + + async def repeat(self): + while self._enabled: + try: + await self.run() + except Exception as ex: # pragma: no cover + LOGGER.error(utils.strex(ex), exc_info=self.config.debug) + + await asyncio.sleep(self._interval.total_seconds()) async def send_request(self, msg: str): if not self.connected: - raise exceptions.NotConnected(f'{self} not connected') + raise exceptions.NotConnected() await self._impl.send_request(msg) - async def start_reconnect(self): - # The run() function will handle cleanup, and then reconnect + async def reset(self): + # The run() function will handle cleanup if self._impl: await self._impl.close() + await self.state.wait_disconnected() + + async def end(self): + self._enabled = False + await self.reset() -def setup(app: web.Application): - features.add(app, ConnectionHandler(app)) +@asynccontextmanager +async def lifespan(): + async with utils.task_context(CV.get().repeat()): + yield -def fget(app: web.Application) -> ConnectionHandler: - return features.get(app, ConnectionHandler) +def setup(): + CV.set(ConnectionHandler()) diff --git a/brewblox_devcon_spark/connection/connection_impl.py b/brewblox_devcon_spark/connection/connection_impl.py index 86334495..a1fb4895 100644 --- a/brewblox_devcon_spark/connection/connection_impl.py +++ b/brewblox_devcon_spark/connection/connection_impl.py @@ -34,9 +34,6 @@ def __init__(self, self._connected = asyncio.Event() self._disconnected = asyncio.Event() - def __str__(self): - return f'<{type(self).__name__} for {self._kind} {self._address}>' - @property def kind(self) -> ConnectionKind_: return self._kind diff --git a/brewblox_devcon_spark/connection/mock_connection.py b/brewblox_devcon_spark/connection/mock_connection.py index 9e998cdc..3c0396e3 100644 --- a/brewblox_devcon_spark/connection/mock_connection.py +++ b/brewblox_devcon_spark/connection/mock_connection.py @@ -7,28 +7,22 @@ This prevents having to spin up a simulator in a separate process for tests. """ +import logging from datetime import datetime from itertools import count -from typing import Optional, Union - -from aiohttp import web -from brewblox_service import brewblox_logger - -from brewblox_devcon_spark import codec, const -from brewblox_devcon_spark.codec import bloxfield -from brewblox_devcon_spark.models import (DecodedPayload, EncodedPayload, - ErrorCode, FirmwareBlock, - IntermediateRequest, - IntermediateResponse, Opcode, - ResetData, ResetReason) +from .. import codec, const, utils +from ..codec import bloxfield +from ..models import (DecodedPayload, EncodedPayload, ErrorCode, FirmwareBlock, + IntermediateRequest, IntermediateResponse, Opcode, + ResetData, ResetReason) from .connection_impl import ConnectionCallbacks, ConnectionImplBase -LOGGER = brewblox_logger(__name__) +LOGGER = logging.getLogger(__name__) # an ErrorCode will be returned # a None value will cause no response to be returned -NEXT_ERROR: list[Union[ErrorCode, None]] = [] +NEXT_ERROR: list[ErrorCode | None] = [] def default_blocks() -> dict[int, FirmwareBlock]: @@ -82,15 +76,13 @@ def default_blocks() -> dict[int, FirmwareBlock]: class MockConnection(ConnectionImplBase): def __init__(self, - app: web.Application, device_id: str, callbacks: ConnectionCallbacks, ) -> None: super().__init__('MOCK', device_id, callbacks) - self.app = app self._start_time = datetime.now() - self._codec = codec.Codec(app, strip_readonly=False) + self._codec = codec.Codec(strip_readonly=False) self._id_counter = count(start=const.USER_NID_START) self._blocks: dict[int, FirmwareBlock] = default_blocks() @@ -139,21 +131,25 @@ def update_systime(self): sysinfo_block.data['systemTime'] = self._start_time.timestamp() + elapsed.total_seconds() async def welcome(self): + config = utils.get_config() + fw_config = utils.get_fw_config() welcome = [ '!BREWBLOX', - self.app['ini']['firmware_version'], - self.app['ini']['proto_version'], - self.app['ini']['firmware_date'], - self.app['ini']['proto_date'], - self.app['ini']['system_version'], + fw_config.firmware_version, + fw_config.proto_version, + fw_config.firmware_date, + fw_config.proto_date, + fw_config.system_version, 'mock', ResetReason.NONE.value, ResetData.NOT_SPECIFIED.value, - self.app['config'].device_id, + config.device_id, ] await self.on_event(','.join(welcome)) - async def handle_command(self, request: IntermediateRequest) -> Optional[IntermediateResponse]: # pragma: no cover + async def handle_command(self, + request: IntermediateRequest + ) -> IntermediateResponse | None: # pragma: no cover response = IntermediateResponse( msgId=request.msgId, error=ErrorCode.OK, @@ -278,8 +274,8 @@ async def close(self): self.disconnected.set() -async def connect_mock(app: web.Application, callbacks: ConnectionCallbacks) -> ConnectionImplBase: - device_id = app['config'].device_id - conn = MockConnection(app, device_id, callbacks) +async def connect_mock(callbacks: ConnectionCallbacks) -> ConnectionImplBase: + config = utils.get_config() + conn = MockConnection(config.device_id, callbacks) await conn.connect() return conn diff --git a/brewblox_devcon_spark/connection/mqtt_connection.py b/brewblox_devcon_spark/connection/mqtt_connection.py index c8c63d28..9918af8f 100644 --- a/brewblox_devcon_spark/connection/mqtt_connection.py +++ b/brewblox_devcon_spark/connection/mqtt_connection.py @@ -5,127 +5,106 @@ import asyncio -from typing import Optional - -from aiohttp import web -from brewblox_service import brewblox_logger, features, mqtt +import logging +from contextvars import ContextVar +from .. import mqtt, utils from .connection_impl import ConnectionCallbacks, ConnectionImplBase -LOGGER = brewblox_logger(__name__) - HANDSHAKE_TOPIC = 'brewcast/cbox/handshake/' LOG_TOPIC = 'brewcast/cbox/log/' REQUEST_TOPIC = 'brewcast/cbox/req/' RESPONSE_TOPIC = 'brewcast/cbox/resp/' -DISCOVERY_TIMEOUT_S = 3 +_DEVICES: ContextVar[dict[str, asyncio.Event]] = ContextVar('mqtt_connection.devices') +LOGGER = logging.getLogger(__name__) class MqttConnection(ConnectionImplBase): def __init__(self, - app: web.Application, device_id: str, callbacks: ConnectionCallbacks, ) -> None: super().__init__('MQTT', device_id, callbacks) + self.mqtt_client = mqtt.CV.get() - self.app = app self._device_id = device_id - self._request_topic = REQUEST_TOPIC + device_id self._response_topic = RESPONSE_TOPIC + device_id self._handshake_topic = HANDSHAKE_TOPIC + device_id self._log_topic = LOG_TOPIC + device_id - async def _handshake_cb(self, topic: str, msg: str): - if not msg: + async def _handshake_cb(self, client, topic, payload, qos, properties): + if not payload: self.disconnected.set() - async def _resp_cb(self, topic: str, msg: str): - await self.on_response(msg) + async def _resp_cb(self, client, topic, payload, qos, properties): + await self.on_response(payload.decode()) - async def _log_cb(self, topic: str, msg: str): - await self.on_event(msg) + async def _log_cb(self, client, topic, payload, qos, properties): + await self.on_event(payload.decode()) async def send_request(self, msg: str): - await mqtt.publish(self.app, self._request_topic, msg) + self.mqtt_client.publish(self._request_topic, msg) async def connect(self): - await mqtt.listen(self.app, self._handshake_topic, self._handshake_cb) - await mqtt.listen(self.app, self._response_topic, self._resp_cb) - await mqtt.listen(self.app, self._log_topic, self._log_cb) - - await mqtt.subscribe(self.app, self._handshake_topic) - await mqtt.subscribe(self.app, self._response_topic) - await mqtt.subscribe(self.app, self._log_topic) - + self.mqtt_client.subscribe(self._handshake_topic)(self._handshake_cb) + self.mqtt_client.subscribe(self._response_topic)(self._resp_cb) + self.mqtt_client.subscribe(self._log_topic)(self._log_cb) self.connected.set() async def close(self): - await mqtt.unsubscribe(self.app, self._handshake_topic) - await mqtt.unsubscribe(self.app, self._response_topic) - await mqtt.unsubscribe(self.app, self._log_topic) - - await mqtt.unlisten(self.app, self._handshake_topic, self._handshake_cb) - await mqtt.unlisten(self.app, self._response_topic, self._resp_cb) - await mqtt.unlisten(self.app, self._log_topic, self._log_cb) - + self.mqtt_client.unsubscribe(self._handshake_topic) + self.mqtt_client.unsubscribe(self._response_topic) + self.mqtt_client.unsubscribe(self._log_topic) self.disconnected.set() -class MqttDeviceTracker(features.ServiceFeature): - def __init__(self, app: web.Application): - super().__init__(app) - self._isolated = app['config'].isolated - self._handshake_topic = HANDSHAKE_TOPIC + '+' - self._devices: dict[str, asyncio.Event] = {} - - async def _handshake_cb(self, topic: str, msg: str): - device = topic.removeprefix(HANDSHAKE_TOPIC) - if msg: - LOGGER.debug(f'MQTT device published: {device}') - self._devices.setdefault(device, asyncio.Event()).set() - else: - LOGGER.debug(f'MQTT device removed: {device}') - self._devices.setdefault(device, asyncio.Event()).clear() - - async def startup(self, app: web.Application): - if not self._isolated: - await mqtt.listen(app, self._handshake_topic, self._handshake_cb) - await mqtt.subscribe(app, self._handshake_topic) - - async def before_shutdown(self, app: web.Application): - if not self._isolated: - await mqtt.unsubscribe(app, self._handshake_topic) - await mqtt.unlisten(app, self._handshake_topic, self._handshake_cb) +async def discover_mqtt(callbacks: ConnectionCallbacks) -> ConnectionImplBase | None: + config = utils.get_config() + if not config.device_id: + return None - async def discover(self, callbacks: ConnectionCallbacks, device_id: str) -> Optional[ConnectionImplBase]: - if self._isolated or not device_id: - return None + try: + devices = _DEVICES.get() + evt = devices.setdefault(config.device_id, asyncio.Event()) + await asyncio.wait_for(evt.wait(), + timeout=config.discovery_timeout_mqtt.total_seconds()) + conn = MqttConnection(config.device_id, callbacks) + await conn.connect() + return conn - try: - evt = self._devices.setdefault(device_id, asyncio.Event()) - await asyncio.wait_for(evt.wait(), timeout=DISCOVERY_TIMEOUT_S) - conn = MqttConnection(self.app, device_id, callbacks) - await conn.connect() - return conn + except asyncio.TimeoutError: + return None - except asyncio.TimeoutError: - return None +def setup(): + mqtt_client = mqtt.CV.get() + devices: dict[str, asyncio.Event] = {} + _DEVICES.set(devices) -def setup(app: web.Application): - features.add(app, MqttDeviceTracker(app)) + # We need to declare listened topics before connect + # If we subscribe to topic/+ here, we still receive messages for topic/id + @mqtt_client.subscribe(HANDSHAKE_TOPIC + '+') + async def on_handshake(client, topic: str, payload: bytes, qos, properties): + device = topic.removeprefix(HANDSHAKE_TOPIC) + if payload: + LOGGER.debug(f'MQTT device published: {device}') + devices.setdefault(device, asyncio.Event()).set() + else: + LOGGER.debug(f'MQTT device removed: {device}') + devices.setdefault(device, asyncio.Event()).clear() -def fget(app: web.Application) -> MqttDeviceTracker: - return features.get(app, MqttDeviceTracker) + @mqtt_client.subscribe(LOG_TOPIC + '+') + async def on_log(client, topic: str, payload: bytes, qos, properties): + pass + @mqtt_client.subscribe(REQUEST_TOPIC + '+') + async def on_request(client, topic: str, payload: bytes, qos, properties): + pass -async def discover_mqtt(app: web.Application, - callbacks: ConnectionCallbacks, - ) -> Optional[MqttConnection]: - device_id = app['config'].device_id - return await fget(app).discover(callbacks, device_id) + @mqtt_client.subscribe(RESPONSE_TOPIC + '+') + async def on_response(client, topic: str, payload: bytes, qos, properties): + pass diff --git a/brewblox_devcon_spark/connection/stream_connection.py b/brewblox_devcon_spark/connection/stream_connection.py index cd1b7256..c79b0b30 100644 --- a/brewblox_devcon_spark/connection/stream_connection.py +++ b/brewblox_devcon_spark/connection/stream_connection.py @@ -3,34 +3,25 @@ The connection itself is always TCP. For serial and simulation targets, the TCP server is a subprocess. """ - import asyncio +import logging +import os import platform +import signal from asyncio.subprocess import Process from contextlib import suppress from functools import partial from pathlib import Path -from typing import Optional -from aiohttp import web -from brewblox_service import brewblox_logger, strex from serial.tools import list_ports -from brewblox_devcon_spark import exceptions, mdns -from brewblox_devcon_spark.models import ServiceConfig - -from .cbox_parser import ControlboxParser +from .. import exceptions, mdns, utils +from .cbox_parser import CboxParser from .connection_impl import (ConnectionCallbacks, ConnectionImplBase, ConnectionKind_) -LOGGER = brewblox_logger(__name__) - -DISCOVERY_DNS_TIMEOUT_S = 20 BREWBLOX_DNS_TYPE = '_brewblox._tcp.local.' -SUBPROCESS_CONNECT_INTERVAL_S = 0.2 -SUBPROCESS_CONNECT_TIMEOUT_S = 10 USB_BAUD_RATE = 115200 -SIMULATION_CWD = 'simulator/' SIM_BINARIES = { 'x86_64': 'brewblox-amd64.sim', @@ -47,6 +38,8 @@ # Example result: (?:HWID_REGEX_ONE|HWID_REGEX_TWO) SPARK_DEVICE_REGEX = f'(?:{"|".join([dev for dev in SPARK_HWIDS])})' +LOGGER = logging.getLogger(__name__) + class StreamConnection(ConnectionImplBase): def __init__(self, @@ -56,7 +49,7 @@ def __init__(self, super().__init__(kind, address, callbacks) self._transport: asyncio.Transport = None - self._parser = ControlboxParser() + self._parser = CboxParser() def connection_made(self, transport: asyncio.Transport): self._transport = transport @@ -77,9 +70,9 @@ def pause_writing(self): # pragma: no cover def resume_writing(self): # pragma: no cover LOGGER.debug(f'{self} resume_writing') - def connection_lost(self, ex: Optional[Exception]): + def connection_lost(self, ex: Exception | None): if ex: - LOGGER.error(f'Connection closed with error: {strex(ex)}') + LOGGER.error(f'Connection closed with error: {utils.strex(ex)}') self.disconnected.set() async def send_request(self, msg: str): @@ -103,11 +96,11 @@ async def close(self): with suppress(Exception): await super().close() with suppress(Exception): - self._proc.terminate() + os.killpg(os.getpgid(self._proc.pid), signal.SIGTERM) + await self._proc.wait() -async def connect_tcp(app: web.Application, - callbacks: ConnectionCallbacks, +async def connect_tcp(callbacks: ConnectionCallbacks, host: str, port: int, ) -> ConnectionImplBase: @@ -116,46 +109,41 @@ async def connect_tcp(app: web.Application, return protocol -async def connect_subprocess(app: web.Application, - callbacks: ConnectionCallbacks, +async def connect_subprocess(callbacks: ConnectionCallbacks, port: int, proc: Process, kind: ConnectionKind_, address: str, ) -> ConnectionImplBase: # pragma: no cover + config = utils.get_config() + loop = asyncio.get_running_loop() factory = partial(SubprocessConnection, kind, address, callbacks, proc) - message = None + errors: set[str] = set() # We just started a subprocess # Give it some time to get started and respond to the port try: - async with asyncio.timeout(SUBPROCESS_CONNECT_TIMEOUT_S): + async with asyncio.timeout(config.subprocess_connect_timeout.total_seconds()): while True: if proc.returncode is not None: raise ChildProcessError(f'Subprocess exited with return code {proc.returncode}') try: - _, protocol = await asyncio.get_event_loop().create_connection(factory, 'localhost', port) + _, protocol = await loop.create_connection(factory, 'localhost', port) return protocol except OSError as ex: - message = strex(ex) - LOGGER.debug(f'Subprocess connection error: {message}') - await asyncio.sleep(SUBPROCESS_CONNECT_INTERVAL_S) + errors.add(utils.strex(ex)) + await asyncio.sleep(config.subprocess_connect_interval.total_seconds()) except asyncio.TimeoutError: with suppress(Exception): proc.terminate() - raise ConnectionError(message) + raise ConnectionError(str(errors)) -async def connect_simulation(app: web.Application, - callbacks: ConnectionCallbacks, - ) -> ConnectionImplBase: # pragma: no cover - config: ServiceConfig = app['config'] - device_id = config.device_id - port = config.device_port - display_ws_port = config.display_ws_port +async def connect_simulation(callbacks: ConnectionCallbacks) -> ConnectionImplBase: # pragma: no cover + config = utils.get_config() arch = platform.machine() binary = SIM_BINARIES.get(arch) @@ -164,54 +152,50 @@ async def connect_simulation(app: web.Application, f'No simulator available for architecture {arch}') binary_path = Path(f'firmware/{binary}').resolve() - workdir = Path(SIMULATION_CWD).resolve() + workdir = config.simulation_workdir.resolve() workdir.mkdir(mode=0o777, exist_ok=True) proc = await asyncio.create_subprocess_exec(binary_path, - '--device_id', device_id, - '--port', str(port), - '--display_ws_port', str(display_ws_port), - cwd=workdir) - return await connect_subprocess(app, callbacks, port, proc, 'SIM', binary) + '--device_id', config.device_id, + '--port', str(config.simulation_port), + '--display_ws_port', str(config.simulation_display_port), + cwd=workdir, + preexec_fn=os.setsid, + shell=False) + return await connect_subprocess(callbacks, config.simulation_port, proc, 'SIM', binary) -async def connect_usb(app: web.Application, - callbacks: ConnectionCallbacks, - device_serial: Optional[str] = None, - port: Optional[int] = None, +async def connect_usb(callbacks: ConnectionCallbacks, + device_serial: str | None = None, ) -> ConnectionImplBase: # pragma: no cover - config: ServiceConfig = app['config'] + config = utils.get_config() device_serial = device_serial or config.device_serial - port = port or config.device_port proc = await asyncio.create_subprocess_exec('/usr/bin/socat', - f'tcp-listen:{port},reuseaddr,fork', - f'file:{device_serial},raw,echo=0,b{USB_BAUD_RATE}') + f'tcp-listen:{config.device_port},reuseaddr,fork', + f'file:{device_serial},raw,echo=0,b{USB_BAUD_RATE}', + preexec_fn=os.setsid, + shell=False) - return await connect_subprocess(app, callbacks, port, proc, 'USB', device_serial) + return await connect_subprocess(callbacks, config.device_port, proc, 'USB', device_serial) -async def discover_mdns(app: web.Application, - callbacks: ConnectionCallbacks, - ) -> Optional[ConnectionImplBase]: - device_id = app['config'].device_id +async def discover_mdns(callbacks: ConnectionCallbacks) -> ConnectionImplBase | None: + config = utils.get_config() try: - resp = await mdns.discover_one(device_id, + resp = await mdns.discover_one(config.device_id, BREWBLOX_DNS_TYPE, - DISCOVERY_DNS_TIMEOUT_S) - return await connect_tcp(app, callbacks, resp.address, resp.port) + config.discovery_timeout_mdns) + return await connect_tcp(callbacks, resp.address, resp.port) except asyncio.TimeoutError: return None -async def discover_usb(app: web.Application, - callbacks: ConnectionCallbacks, - ) -> Optional[ConnectionImplBase]: # pragma: no cover - config: ServiceConfig = app['config'] - device_id = config.device_id +async def discover_usb(callbacks: ConnectionCallbacks) -> ConnectionImplBase | None: # pragma: no cover + config = utils.get_config() for usb_port in list_ports.grep(SPARK_DEVICE_REGEX): - if device_id is None or device_id.lower() == usb_port.serial_number.lower(): + if config.device_id is None or config.device_id.lower() == usb_port.serial_number.lower(): LOGGER.info(f'Discovered {[v for v in usb_port]}') - return await connect_usb(app, callbacks, usb_port.device) + return await connect_usb(callbacks, usb_port.device) else: LOGGER.info(f'Discarding {[v for v in usb_port]}') return None diff --git a/brewblox_devcon_spark/const.py b/brewblox_devcon_spark/const.py index 97704e45..e05664ca 100644 --- a/brewblox_devcon_spark/const.py +++ b/brewblox_devcon_spark/const.py @@ -7,7 +7,7 @@ OBJECT_LINK_POSTFIX_END = '>' GENERATED_ID_PREFIX = 'New|' -SPARK_NAMESPACE = 'spark-service' +SERVICE_NAMESPACE = 'spark-service' GLOBAL_NAMESPACE = 'brewblox-global' GLOBAL_UNITS_ID = 'units' GLOBAL_TIME_ZONE_ID = 'timeZone' @@ -32,3 +32,4 @@ # Relevant block types SEQUENCE_BLOCK_TYPE = 'Sequence' +SYSINFO_BLOCK_TYPE = 'SysInfo' diff --git a/brewblox_devcon_spark/controller.py b/brewblox_devcon_spark/control.py similarity index 82% rename from brewblox_devcon_spark/controller.py rename to brewblox_devcon_spark/control.py index e5f113ff..bcbcd577 100644 --- a/brewblox_devcon_spark/controller.py +++ b/brewblox_devcon_spark/control.py @@ -4,24 +4,19 @@ import asyncio import itertools +import logging import re from contextlib import asynccontextmanager, suppress +from contextvars import ContextVar from datetime import datetime, timezone from typing import Callable, Union -from aiohttp import web -from brewblox_service import brewblox_logger, features, strex +from . import (command, const, datastore_blocks, exceptions, state_machine, + twinkeydict, utils) +from .codec import bloxfield, sequence +from .models import (Backup, BackupApplyResult, Block, BlockIdentity, + BlockNameChange, FirmwareBlock, FirmwareBlockIdentity) -from brewblox_devcon_spark import (block_store, commander, const, exceptions, - service_status, twinkeydict) -from brewblox_devcon_spark.codec import bloxfield, sequence -from brewblox_devcon_spark.models import (Backup, BackupApplyResult, Block, - BlockIdentity, BlockNameChange, - FirmwareBlock, FirmwareBlockIdentity) - -LOGGER = brewblox_logger(__name__) - -SYNC_WAIT_TIMEOUT_S = 20 SID_PATTERN = re.compile(r'^[a-zA-Z]{1}[a-zA-Z0-9 _\-\(\)\|]{0,199}$') SID_RULES = """ An object ID must adhere to the following rules: @@ -30,6 +25,9 @@ - At most 200 characters """ +LOGGER = logging.getLogger(__name__) +CV: ContextVar['SparkController'] = ContextVar('controller.SparkController') + def merge(a: dict, b: dict): """Merges dict b into dict a""" @@ -65,17 +63,14 @@ def resolve_data_ids(data: Union[dict, list, tuple], resolve_data_ids(v, replacer) -class SparkController(features.ServiceFeature): +class SparkController: - def __init__(self, app: web.Application): - super().__init__(app) - self._name = app['config'].name - self._cmder = commander.fget(app) - self._store = block_store.fget(app) - self._discovery_lock: asyncio.Lock = None - self._conn_check_lock: asyncio.Lock = None + def __init__(self): + self.config = utils.get_config() + self.state = state_machine.CV.get() + self.cmder = command.CV.get() + self.store = datastore_blocks.CV.get() - async def startup(self, app: web.Application): self._discovery_lock = asyncio.Lock() self._conn_check_lock = asyncio.Lock() @@ -84,13 +79,13 @@ def _validate_sid(self, sid: str): raise exceptions.InvalidId(SID_RULES) if next((keys for keys in const.SYS_OBJECT_KEYS if sid == keys[0]), None): raise exceptions.InvalidId(f'Block ID `{sid}` is reserved for system objects') - if (sid, None) in self._store: + if (sid, None) in self.store: raise exceptions.ExistingId(f'Block ID `{sid}` is already in use') def _assign_sid(self, blockType: str): for i in itertools.count(start=1): # pragma: no cover name = f'{const.GENERATED_ID_PREFIX}{blockType}-{i}' - if (name, None) not in self._store: + if (name, None) not in self.store: return name def _find_nid(self, sid: str, blockType: str) -> int: @@ -101,7 +96,7 @@ def _find_nid(self, sid: str, blockType: str) -> int: return int(sid) try: - return self._store.right_key(sid) + return self.store.right_key(sid) except KeyError: raise exceptions.UnknownId(f'Block ID `{sid}` not found. type={blockType}') @@ -113,11 +108,11 @@ def _find_sid(self, nid: int, blockType: str) -> str: raise exceptions.DecodeException(f'Expected numeric block ID, got string `{nid}`') try: - sid = self._store.left_key(nid) + sid = self.store.left_key(nid) except KeyError: # If service ID not found, randomly generate one sid = self._assign_sid(blockType) - self._store[sid, nid] = dict() + self.store[sid, nid] = dict() return sid @@ -126,14 +121,14 @@ def _to_block_identity(self, block: FirmwareBlock) -> BlockIdentity: id=self._find_sid(block.nid, block.type), nid=block.nid, type=block.type, - serviceId=self._name + serviceId=self.config.name ) def _to_block(self, block: FirmwareBlock, find_sid=True) -> Block: block = Block( - **block.dict(), + **block.model_dump(), id=None, - serviceId=self._name + serviceId=self.config.name ) block.id = self._find_sid(block.nid, block.type) if find_sid else None @@ -159,7 +154,7 @@ def _to_firmware_block_identity(self, block: BlockIdentity) -> FirmwareBlockIden if nid is None: try: - nid = self._store.right_key(sid) + nid = self.store.right_key(sid) except KeyError: raise exceptions.UnknownId(f'Block ID `{sid}` not found. type={block.type}') @@ -174,7 +169,7 @@ def _to_firmware_block(self, block: Block, find_nid=True) -> FirmwareBlock: if nid is None: try: - nid = self._store.right_key(sid) if find_nid else 0 + nid = self.store.right_key(sid) if find_nid else 0 except KeyError: raise exceptions.UnknownId(f'Block ID `{sid}` not found. type={block.type}') @@ -200,12 +195,12 @@ async def _check_connection(self): to avoid weird interactions when prompting for a handshake. """ async with self._conn_check_lock: - if service_status.is_synchronized(self.app): + if self.state.is_synchronized(): LOGGER.info('Checking connection...') try: - await self._cmder.noop() + await self.cmder.noop() except Exception: - await self._cmder.start_reconnect() + await self.cmder.reset_connection() @asynccontextmanager async def _execute(self, desc: str): @@ -217,12 +212,18 @@ async def _execute(self, desc: str): desc (str): Human-readable function description, to be used in error messages. """ - if service_status.is_updating(self.app): + if self.state.is_updating(): raise exceptions.UpdateInProgress('Update is in progress') - await asyncio.wait_for( - service_status.wait_synchronized(self.app), - SYNC_WAIT_TIMEOUT_S) + self.state.check_compatible() + + try: + await asyncio.wait_for( + self.state.wait_synchronized(), + self.config.command_timeout.total_seconds()) + + except asyncio.TimeoutError: + raise exceptions.ConnectionException('Not connected') try: yield @@ -233,7 +234,7 @@ async def _execute(self, desc: str): raise ex except Exception as ex: - LOGGER.debug(f'Failed to execute {desc}: {strex(ex)}') + LOGGER.debug(f'Failed to execute {desc}: {utils.strex(ex)}') raise ex async def noop(self) -> None: @@ -242,7 +243,7 @@ async def noop(self) -> None: No data is written, but a welcome message is triggered as side effect. """ async with self._execute('Noop'): - await self._cmder.noop() + await self.cmder.noop() async def read_block(self, block: BlockIdentity) -> Block: """ @@ -260,7 +261,7 @@ async def read_block(self, block: BlockIdentity) -> Block: """ async with self._execute('Read block'): block = self._to_firmware_block_identity(block) - block = await self._cmder.read_block(block) + block = await self.cmder.read_block(block) block = self._to_block(block) return block @@ -283,7 +284,7 @@ async def read_logged_block(self, block: BlockIdentity) -> Block: """ async with self._execute('Read block (logged)'): block = self._to_firmware_block_identity(block) - block = await self._cmder.read_logged_block(block) + block = await self.cmder.read_logged_block(block) block = self._to_block(block) return block @@ -307,7 +308,7 @@ async def read_stored_block(self, block: BlockIdentity) -> Block: """ async with self._execute('Read block (stored)'): block = self._to_firmware_block_identity(block) - block = await self._cmder.read_stored_block(block) + block = await self.cmder.read_stored_block(block) block = self._to_block(block) return block @@ -327,7 +328,7 @@ async def write_block(self, block: Block) -> Block: """ async with self._execute('Write block'): block = self._to_firmware_block(block) - block = await self._cmder.write_block(block) + block = await self.cmder.write_block(block) block = self._to_block(block) return block @@ -347,7 +348,7 @@ async def patch_block(self, block: Block) -> Block: """ async with self._execute('Patch block'): block = self._to_firmware_block(block) - block = await self._cmder.patch_block(block) + block = await self.cmder.patch_block(block) block = self._to_block(block) return block @@ -375,20 +376,20 @@ async def create_block(self, block: Block) -> Block: # Avoid race conditions for the desired sid # Claim it with a placeholder until the spark create call returns placeholder_nid = object() - self._store[desired_sid, placeholder_nid] = 'PLACEHOLDER' + self.store[desired_sid, placeholder_nid] = 'PLACEHOLDER' try: - block = await self._cmder.create_block(block) + block = await self.cmder.create_block(block) finally: - del self._store[desired_sid, placeholder_nid] + del self.store[desired_sid, placeholder_nid] # It's possible there is a leftover entry with the generated nid # In this case, the newly created entry takes precedence with suppress(KeyError): - del self._store[None, block.nid] + del self.store[None, block.nid] # The placeholder is always removed - add real entry if create was ok - self._store[desired_sid, block.nid] = dict() + self.store[desired_sid, block.nid] = dict() block = self._to_block(block) return block @@ -410,16 +411,16 @@ async def delete_block(self, block: BlockIdentity) -> BlockIdentity: """ async with self._execute('Delete block'): block = self._to_firmware_block_identity(block) - await self._cmder.delete_block(block) + await self.cmder.delete_block(block) nid = block.nid - sid = self._store.left_key(nid) - del self._store[sid, nid] + sid = self.store.left_key(nid) + del self.store[sid, nid] ident = BlockIdentity( id=sid, nid=nid, type=block.type, - serviceId=self._name, + serviceId=self.config.name, ) return ident @@ -433,7 +434,7 @@ async def read_all_blocks(self) -> list[Block]: All present blocks on the controller. """ async with self._execute('Read all blocks'): - blocks = await self._cmder.read_all_blocks() + blocks = await self.cmder.read_all_blocks() blocks = self._to_block_list(blocks) return blocks @@ -449,7 +450,7 @@ async def read_all_logged_blocks(self) -> list[Block]: marked for logging, and units will use the postfixed format. """ async with self._execute('Read all blocks (logged)'): - blocks = await self._cmder.read_all_logged_blocks() + blocks = await self.cmder.read_all_logged_blocks() blocks = self._to_block_list(blocks) return blocks @@ -466,7 +467,7 @@ async def read_all_stored_blocks(self) -> list[Block]: Non-persistent fields will be absent or set to a default value. """ async with self._execute('Read all blocks (stored)'): - blocks = await self._cmder.read_all_stored_blocks() + blocks = await self.cmder.read_all_stored_blocks() blocks = self._to_block_list(blocks) return blocks @@ -482,7 +483,7 @@ async def read_all_broadcast_blocks(self) -> tuple[list[Block], list[Block]]: the second is suitable for logging. """ async with self._execute('Read all blocks (broadcast)'): - blocks, logged_blocks = await self._cmder.read_all_broadcast_blocks() + blocks, logged_blocks = await self.cmder.read_all_broadcast_blocks() blocks = self._to_block_list(blocks) logged_blocks = self._to_block_list(logged_blocks) return (blocks, logged_blocks) @@ -499,7 +500,7 @@ async def discover_blocks(self) -> list[Block]: """ async with self._execute('Discover blocks'): async with self._discovery_lock: - blocks = await self._cmder.discover_blocks() + blocks = await self.cmder.discover_blocks() blocks = self._to_block_list(blocks) return blocks @@ -514,10 +515,10 @@ async def clear_blocks(self) -> list[BlockIdentity]: IDs of all removed blocks. """ async with self._execute('Remove all blocks'): - blocks = await self._cmder.clear_blocks() + blocks = await self.cmder.clear_blocks() identities = [self._to_block_identity(v) for v in blocks] - self._store.clear() - await self._cmder.write_block(FirmwareBlock( + self.store.clear() + await self.cmder.write_block(FirmwareBlock( nid=const.DISPLAY_SETTINGS_NID, type='DisplaySettings', data={}, @@ -539,11 +540,11 @@ async def rename_block(self, change: BlockNameChange) -> BlockIdentity: The new sid + nid. """ self._validate_sid(change.desired) - self._store.rename((change.existing, None), (change.desired, None)) + self.store.rename((change.existing, None), (change.desired, None)) return BlockIdentity( id=change.desired, - nid=self._store.right_key(change.desired), - serviceId=self._name + nid=self.store.right_key(change.desired), + serviceId=self.config.name ) async def remove_unused_ids(self) -> list[BlockIdentity]: @@ -558,11 +559,11 @@ async def remove_unused_ids(self) -> list[BlockIdentity]: actual = [block.id for block in await self.read_all_blocks()] unused = [(sid, nid) - for (sid, nid) in self._store + for (sid, nid) in self.store if sid not in actual] for (sid, nid) in unused.copy(): - del self._store[sid, nid] - return [BlockIdentity(id=sid, nid=nid, serviceId=self._name) + del self.store[sid, nid] + return [BlockIdentity(id=sid, nid=nid, serviceId=self.config.name) for (sid, nid) in unused] async def make_backup(self) -> Backup: @@ -575,13 +576,13 @@ async def make_backup(self) -> Backup: JSON-ready backup data, compatible with apply_backup(). """ store_data = [{'keys': keys, 'data': content} - for keys, content in self._store.items()] + for keys, content in self.store.items()] blocks_data = await self.read_all_stored_blocks() timestamp = datetime\ .now(tz=timezone.utc)\ .isoformat(timespec='seconds')\ .replace('+00:00', 'Z') - controller_info = service_status.desc(self.app).controller + controller_info = self.state.desc().controller return Backup( blocks=[block for block in blocks_data], @@ -621,40 +622,38 @@ async def apply_backup(self, exported: Backup) -> BackupApplyResult: # First populate the datastore, to avoid unknown links for entry in exported.store: - keys = entry['keys'] - data = entry['data'] try: - self._store[keys] = data + self.store[entry.keys] = entry.data except twinkeydict.TwinKeyError: - sid, nid = keys - self._store.rename((None, nid), (sid, None)) - self._store[keys] = data + sid, nid = entry.keys + self.store.rename((None, nid), (sid, None)) + self.store[entry.keys] = entry.data # Now either create or write the objects, depending on whether they are system objects for block in exported.blocks: try: - block = block.copy(deep=True) + block = block.model_copy(deep=True) if block.nid is not None and block.nid < const.USER_NID_START: if block.nid in sys_nids: # Ignore deprecated system blocks await self.write_block(block) else: # Bypass self.create_block(), to avoid meddling with store IDs - await self._cmder.create_block(self._to_firmware_block(block)) + await self.cmder.create_block(self._to_firmware_block(block)) except Exception as ex: - message = f'failed to import block. Error={strex(ex)}, block={block}' + message = f'failed to import block. Error={utils.strex(ex)}, block={block}' error_log.append(message) LOGGER.error(message) used_nids = [b.nid for b in await self.read_all_blocks()] unused = [ - (sid, nid) for (sid, nid) in self._store + (sid, nid) for (sid, nid) in self.store if nid >= const.USER_NID_START and nid not in used_nids ] for sid, nid in unused: - del self._store[sid, nid] + del self.store[sid, nid] message = f'Removed unused alias [{sid},{nid}]' LOGGER.info(message) error_log.append(message) @@ -667,21 +666,21 @@ async def clear_wifi(self): The controller may reboot or lose connection. """ async with self._execute('Clear Wifi settings'): - await self._cmder.clear_wifi() + await self.cmder.clear_wifi() async def factory_reset(self) -> None: """ Prompt the controller to perform a factory reset. """ async with self._execute('Factory reset'): - await self._cmder.factory_reset() + await self.cmder.factory_reset() async def reboot(self) -> None: """ Prompt the controller to reboot itself. """ async with self._execute('Reboot'): - await self._cmder.reboot() + await self.cmder.reboot() async def validate(self, block: Block) -> Block: """ @@ -696,14 +695,10 @@ async def validate(self, block: Block) -> Block: """ async with self._execute('Validate block'): block = self._to_firmware_block(block, find_nid=False) - block = await self._cmder.validate(block) + block = await self.cmder.validate(block) block = self._to_block(block, find_sid=False) return block -def setup(app: web.Application): - features.add(app, SparkController(app)) - - -def fget(app: web.Application) -> SparkController: - return features.get(app, SparkController) +def setup(): + CV.set(SparkController()) diff --git a/brewblox_devcon_spark/datastore.py b/brewblox_devcon_spark/datastore.py deleted file mode 100644 index 7e77d793..00000000 --- a/brewblox_devcon_spark/datastore.py +++ /dev/null @@ -1,92 +0,0 @@ -""" -Base class for persistent data stores -""" - -import asyncio -import warnings -from abc import abstractmethod -from contextlib import suppress -from functools import wraps - -from aiohttp import web -from brewblox_service import brewblox_logger, http, repeater, strex - -LOGGER = brewblox_logger(__name__) - -STORE_URL = 'http://history:5000/history/datastore' -RETRY_INTERVAL_S = 1 -FLUSH_DELAY_S = 5 -SHUTDOWN_WRITE_TIMEOUT_S = 2 - - -def non_isolated(func): - @wraps(func) - async def wrapper(self, *args, **kwargs): - if self.isolated: - return - return await func(self, *args, **kwargs) - return wrapper - - -async def check_remote(app: web.Application): - if app['config'].isolated: - return - num_attempts = 0 - while True: - try: - await http.session(app).get(f'{STORE_URL}/ping') - return - except Exception as ex: - LOGGER.error(strex(ex)) - num_attempts += 1 - if num_attempts % 10 == 0: - LOGGER.info(f'Waiting for datastore... ({strex(ex)})') - await asyncio.sleep(RETRY_INTERVAL_S) - - -class FlushedStore(repeater.RepeaterFeature): - - def __init__(self, app: web.Application): - super().__init__(app) - self._isolated = app['config'].isolated - self._changed_event: asyncio.Event = None - - @property - def isolated(self): - return self._isolated - - def set_changed(self): - if self._changed_event: - self._changed_event.set() - - async def before_shutdown(self, app: web.Application): - await super().before_shutdown(app) - await self.end() - with suppress(Exception): - if self._changed_event.is_set(): - LOGGER.info(f'Writing data while closing {self}') - await asyncio.wait_for(self.write(), timeout=SHUTDOWN_WRITE_TIMEOUT_S) - self._changed_event = None - - async def prepare(self): - self._changed_event = asyncio.Event() - if self._isolated: - LOGGER.info(f'{self} is isolated (will not read/write datastore)') - raise repeater.RepeaterCancelled() - - async def run(self): - try: - await self._changed_event.wait() - await asyncio.sleep(FLUSH_DELAY_S) - await self.write() - self._changed_event.clear() - - except Exception as ex: - warnings.warn(f'{self} flush error {strex(ex)}') - - @abstractmethod - async def write(self): - """ - Must be implemented by child classes. - FlushedStore handles how and when write() is called. - """ diff --git a/brewblox_devcon_spark/datastore_blocks.py b/brewblox_devcon_spark/datastore_blocks.py new file mode 100644 index 00000000..0f1bec8e --- /dev/null +++ b/brewblox_devcon_spark/datastore_blocks.py @@ -0,0 +1,130 @@ +""" +Stores sid/nid relations for blocks +""" + +import asyncio +import logging +from contextlib import asynccontextmanager, suppress +from contextvars import ContextVar + +from httpx import AsyncClient + +from . import const, utils +from .models import (DatastoreSingleQuery, TwinKeyEntriesBox, + TwinKeyEntriesValue, TwinKeyEntry) +from .twinkeydict import TwinKeyDict, TwinKeyError + +SYS_OBJECTS: list[TwinKeyEntry] = [ + TwinKeyEntry(keys=keys, data={}) + for keys in const.SYS_OBJECT_KEYS +] + +LOGGER = logging.getLogger(__name__) + +CV: ContextVar['BlockStore'] = ContextVar('block_store.BlockStore') + + +class BlockStore(TwinKeyDict[str, int, dict]): + def __init__(self, defaults: list[TwinKeyEntry]): + super().__init__() + + self.config = utils.get_config() + self._changed_ev = asyncio.Event() + self._doc_id: str = None + self._defaults = defaults + self._client = AsyncClient(base_url=self.config.datastore_url) + + self.clear() # inserts defaults + + async def load(self, device_id: str): + doc_id = f'{device_id}-blocks-db' + data: list[TwinKeyEntry] = [] + + try: + self._doc_id = None + + query = DatastoreSingleQuery(id=doc_id, + namespace=const.SERVICE_NAMESPACE) + content = query.model_dump(mode='json') + resp = await utils.httpx_retry(lambda: self._client.post('/get', json=content)) + + try: + box = TwinKeyEntriesBox.model_validate_json(resp.text) + data = box.value.data + except (AttributeError, ValueError): + data = [] + LOGGER.info(f'Loaded {len(data)} block(s)') + + finally: + # Clear -> load from database -> merge defaults + super().clear() + for obj in data: + super().__setitem__(obj.keys, obj.data) + for obj in self._defaults: + with suppress(TwinKeyError): + if obj.keys not in self: + self.__setitem__(obj.keys, obj.data) + + self._doc_id = doc_id + + async def save(self): + if not self._doc_id: + raise ValueError('Document ID not set - did you forget to call load()?') + + data = [TwinKeyEntry(keys=k, data=v) + for k, v in self.items()] + box = TwinKeyEntriesBox( + value=TwinKeyEntriesValue( + id=self._doc_id, + namespace=const.SERVICE_NAMESPACE, + data=data + ) + ) + await self._client.post('/set', + json=box.model_dump(mode='json')) + LOGGER.info(f'Saved {len(data)} block(s)') + self._changed_ev.clear() + + async def repeat(self): + while True: + try: + await self._changed_ev.wait() + await asyncio.sleep(self.config.datastore_flush_delay.total_seconds()) + await self.save() + except Exception as ex: # pragma: no cover + LOGGER.error(utils.strex(ex), exc_info=self.config.debug) + + async def on_shutdown(self): + if not self._doc_id or not self._changed_ev.is_set(): + return + + try: + await asyncio.wait_for(self.save(), + timeout=self.config.datastore_shutdown_timeout.total_seconds()) + except Exception as ex: # pragma: no cover + LOGGER.error(utils.strex(ex), exc_info=self.config.debug) + + def __setitem__(self, keys: tuple[str, int], item: dict): + super().__setitem__(keys, item) + self._changed_ev.set() + + def __delitem__(self, keys: tuple[str | None, int | None]): + super().__delitem__(keys) + self._changed_ev.set() + + def clear(self): + super().clear() + for obj in self._defaults: + self.__setitem__(obj.keys, obj.data) + + +@asynccontextmanager +async def lifespan(): + store = CV.get() + async with utils.task_context(store.repeat()): + yield + await store.on_shutdown() + + +def setup(): + CV.set(BlockStore(defaults=SYS_OBJECTS)) diff --git a/brewblox_devcon_spark/datastore_settings.py b/brewblox_devcon_spark/datastore_settings.py new file mode 100644 index 00000000..c7a6aa4d --- /dev/null +++ b/brewblox_devcon_spark/datastore_settings.py @@ -0,0 +1,152 @@ +import asyncio +import logging +from contextlib import asynccontextmanager +from contextvars import ContextVar +from typing import Awaitable, Callable + +from httpx import AsyncClient + +from . import const, mqtt, utils +from .models import (DatastoreEvent, DatastoreSingleQuery, + DatastoreSingleValueBox, StoredServiceSettingsBox, + StoredServiceSettingsValue, StoredTimezoneSettingsBox, + StoredTimezoneSettingsValue, StoredUnitSettingsBox, + StoredUnitSettingsValue) + +Callback_ = Callable[[], Awaitable] + +LOGGER = logging.getLogger(__name__) + +CV: ContextVar['SettingsStore'] = ContextVar('settings_store.SettingsStore') + + +class SettingsStore: + + def __init__(self) -> None: + self.config = utils.get_config() + + self._ready_ev = asyncio.Event() + self._client = AsyncClient(base_url=self.config.datastore_url) + + self._service_settings = StoredServiceSettingsValue(id=self.config.name) + self._unit_settings = StoredUnitSettingsValue() + self._timezone_settings = StoredTimezoneSettingsValue() + + self._service_listeners: set[Callback_] = set() + self._global_listeners: set[Callback_] = set() + + async def _get_box(self, + query: DatastoreSingleQuery, + model: type[DatastoreSingleValueBox]) -> DatastoreSingleValueBox: + content = query.model_dump(mode='json') + resp = await utils.httpx_retry(lambda: self._client.post('/get', json=content)) + box = model.model_validate_json(resp.text) + return box + + async def _set_box(self, + box: DatastoreSingleValueBox): + await self._client.post('/set', json=box.model_dump(mode='json')) + + async def fetch_all(self): + async with asyncio.timeout(self.config.datastore_fetch_timeout.total_seconds()): + # Fetch service settings + box = await self._get_box( + query=DatastoreSingleQuery(id=self.config.name, + namespace=const.SERVICE_NAMESPACE), + model=StoredServiceSettingsBox) + self._service_settings = box.value or StoredServiceSettingsValue(id=self.config.name) + + # Fetch unit settings + box = await self._get_box( + query=DatastoreSingleQuery(id=const.GLOBAL_UNITS_ID, + namespace=const.GLOBAL_NAMESPACE), + model=StoredUnitSettingsBox) + self._unit_settings = box.value or StoredUnitSettingsValue() + + # Fetch timezone settings + box = await self._get_box( + query=DatastoreSingleQuery(id=const.GLOBAL_TIME_ZONE_ID, + namespace=const.GLOBAL_NAMESPACE), + model=StoredTimezoneSettingsBox) + self._timezone_settings = box.value or StoredTimezoneSettingsValue() + + async def on_service_store_event(self, evt: DatastoreEvent): + dirty = False + + for value in evt.changed: + if value.id == self.config.name: + settings = StoredServiceSettingsValue.model_validate(value.model_dump()) + if settings != self._service_settings: + LOGGER.info(f'Received service settings: {settings}') + self._service_settings = settings + dirty = True + + if dirty: + for cb in set(self._service_listeners): + await cb() + + async def on_global_store_event(self, evt: DatastoreEvent): + dirty = False + + for value in evt.changed: + if value.id == const.GLOBAL_UNITS_ID: + settings = StoredUnitSettingsValue.model_validate(value.model_dump()) + if settings != self.unit_settings: + LOGGER.info(f'Received unit settings: {settings}') + self._unit_settings = settings + dirty = True + + if value.id == const.GLOBAL_TIME_ZONE_ID: + settings = StoredTimezoneSettingsValue.model_validate(value.model_dump()) + if settings != self._timezone_settings: + LOGGER.info(f'Received timezone settings: {settings}') + self._timezone_settings = settings + dirty = True + + if dirty: + for cb in set(self._global_listeners): + await cb() + + @property + def service_settings_listeners(self) -> set[Callback_]: + return self._service_listeners + + @property + def global_settings_listeners(self) -> set[Callback_]: + return self._global_listeners + + @property + def service_settings(self) -> StoredServiceSettingsValue: + return self._service_settings + + @property + def unit_settings(self) -> StoredUnitSettingsValue: + return self._unit_settings + + @property + def timezone_settings(self) -> StoredTimezoneSettingsValue: + return self._timezone_settings + + async def commit_service_settings(self): + await self._set_box(StoredServiceSettingsBox(value=self._service_settings)) + + +@asynccontextmanager +async def lifespan(): + await CV.get().fetch_all() + yield + + +def setup(): + store = SettingsStore() + config = utils.get_config() + mqtt_client = mqtt.CV.get() + CV.set(store) + + @mqtt_client.subscribe(f'{config.datastore_topic}/{const.GLOBAL_NAMESPACE}') + async def on_global_change(client, topic, payload, qos, properties): + await CV.get().on_global_store_event(DatastoreEvent.model_validate_json(payload)) + + @mqtt_client.subscribe(f'{config.datastore_topic}/{const.SERVICE_NAMESPACE}') + async def on_service_change(client, topic, payload, qos, properties): + await CV.get().on_service_store_event(DatastoreEvent.model_validate_json(payload)) diff --git a/brewblox_devcon_spark/exceptions.py b/brewblox_devcon_spark/exceptions.py index 2f8975f0..b6713ce3 100644 --- a/brewblox_devcon_spark/exceptions.py +++ b/brewblox_devcon_spark/exceptions.py @@ -3,11 +3,16 @@ This improves clarity as to what actually went wrong. """ -from aiohttp import web +from fastapi import HTTPException, status -class BrewbloxException(Exception): - http_error = web.HTTPInternalServerError + +class BrewbloxException(HTTPException): + status_code = status.HTTP_500_INTERNAL_SERVER_ERROR + + def __init__(self, msg: str = '') -> None: + super().__init__(status_code=self.__class__.status_code, + detail=msg) ################################################################################################## @@ -16,7 +21,7 @@ class BrewbloxException(Exception): class InvalidInput(BrewbloxException): - http_error = web.HTTPBadRequest + status_code = status.HTTP_400_BAD_REQUEST class MissingInput(InvalidInput): @@ -29,7 +34,7 @@ class MissingInput(InvalidInput): class IdException(BrewbloxException): - http_error = web.HTTPBadRequest + status_code = status.HTTP_400_BAD_REQUEST class InvalidId(IdException): @@ -37,7 +42,7 @@ class InvalidId(IdException): class ExistingId(IdException): - http_error = web.HTTPConflict + status_code = status.HTTP_409_CONFLICT class UnknownId(IdException): @@ -54,11 +59,11 @@ class CommandException(BrewbloxException): class CommandParseException(CommandException): - http_error = web.HTTPFailedDependency + status_code = status.HTTP_424_FAILED_DEPENDENCY class CommandBuildException(CommandException): - http_error = web.HTTPBadRequest + status_code = status.HTTP_400_BAD_REQUEST class CRCFailed(CommandException): @@ -66,11 +71,11 @@ class CRCFailed(CommandException): class CommandTimeout(CommandException): - http_error = web.HTTPFailedDependency + status_code = status.HTTP_424_FAILED_DEPENDENCY class UpdateInProgress(CommandException): - http_error = web.HTTPFailedDependency + status_code = status.HTTP_424_FAILED_DEPENDENCY ################################################################################################## @@ -83,15 +88,15 @@ class CodecException(BrewbloxException): class EncodeException(CodecException): - http_error = web.HTTPBadRequest + status_code = status.HTTP_400_BAD_REQUEST class DecodeException(CodecException): - http_error = web.HTTPFailedDependency + status_code = status.HTTP_424_FAILED_DEPENDENCY class UnknownCodecType(CodecException): - http_error = web.HTTPUnprocessableEntity + status_code = status.HTTP_422_UNPROCESSABLE_ENTITY ################################################################################################## @@ -100,7 +105,7 @@ class UnknownCodecType(CodecException): class ConnectionException(BrewbloxException): - http_error = web.HTTPFailedDependency + status_code = status.HTTP_424_FAILED_DEPENDENCY class NotConnected(ConnectionException): @@ -120,7 +125,7 @@ class ConnectionPaused(ConnectionException): ################################################################################################## class FirmwareException(BrewbloxException): - http_error = web.HTTPFailedDependency + status_code = status.HTTP_424_FAILED_DEPENDENCY class IncompatibleFirmware(FirmwareException): diff --git a/brewblox_devcon_spark/global_store.py b/brewblox_devcon_spark/global_store.py deleted file mode 100644 index eba589b3..00000000 --- a/brewblox_devcon_spark/global_store.py +++ /dev/null @@ -1,98 +0,0 @@ -""" -Keeps track of global config -""" - -import json - -from aiohttp import web -from brewblox_service import brewblox_logger, features, http, mqtt, strex - -from brewblox_devcon_spark import const -from brewblox_devcon_spark.datastore import STORE_URL -from brewblox_devcon_spark.models import ServiceConfig - -LOGGER = brewblox_logger(__name__) - - -def default_units(): - return { - 'temperature': 'degC', - } - - -def default_time_zone(): - return { - 'name': 'Etc/UTC', - 'posixValue': 'UTC0', - } - - -class GlobalConfigStore(features.ServiceFeature): - def __init__(self, app: web.Application): - super().__init__(app) - config: ServiceConfig = app['config'] - self._isolated = config.isolated - self._datastore_topic = config.datastore_topic - self._global_topic = f'{self._datastore_topic}/{const.GLOBAL_NAMESPACE}' - - self.units = default_units() - self.time_zone = default_time_zone() - self.listeners = set() - - async def startup(self, app: web.Application): - if not self._isolated: - await mqtt.listen(app, self._global_topic, self._on_event) - await mqtt.subscribe(app, self._global_topic) - - async def before_shutdown(self, app: web.Application): - self.listeners.clear() - - async def shutdown(self, app: web.Application): - if not self._isolated: - await mqtt.unlisten(app, self._global_topic, self._on_event) - await mqtt.unsubscribe(app, self._global_topic) - - async def _on_event(self, topic: str, payload: str): - obj = json.loads(payload) - if self.update(obj.get('changed', [])): - for cb in set(self.listeners): - await cb() - - def update(self, values: list) -> bool: - changed = False - for value in values: - if value['id'] == const.GLOBAL_UNITS_ID: - units = {'temperature': value['temperature']} - changed = changed or units != self.units - self.units = units - if value['id'] == const.GLOBAL_TIME_ZONE_ID: - tz = { - 'name': value['name'], - 'posixValue': value['posixValue'] - } - changed = changed or tz != self.time_zone - self.time_zone = tz - - return changed - - async def read(self): - if self._isolated: - return - - try: - resp = await http.session(self.app).post(f'{STORE_URL}/mget', json={ - 'namespace': const.GLOBAL_NAMESPACE, - 'ids': [const.GLOBAL_UNITS_ID, const.GLOBAL_TIME_ZONE_ID], - }) - self.update((await resp.json())['values']) - - except Exception as ex: - LOGGER.error(f'{self} read error {strex(ex)}') - - -def setup(app: web.Application): - features.add(app, GlobalConfigStore(app)) - - -def fget(app: web.Application) -> GlobalConfigStore: - return features.get(app, GlobalConfigStore) diff --git a/brewblox_devcon_spark/mdns.py b/brewblox_devcon_spark/mdns.py index 391e9eb4..088506f8 100644 --- a/brewblox_devcon_spark/mdns.py +++ b/brewblox_devcon_spark/mdns.py @@ -3,25 +3,25 @@ """ import asyncio +import logging from collections import namedtuple from contextlib import suppress +from datetime import timedelta from socket import AF_INET, inet_aton, inet_ntoa -from typing import Generator, Optional +from typing import Generator from aiozeroconf import ServiceBrowser, ServiceStateChange, Zeroconf from aiozeroconf.aiozeroconf import ServiceInfo -from brewblox_service import brewblox_logger -DEFAULT_TIMEOUT_S = 5 SIM_ADDR = inet_aton('0.0.0.0') -LOGGER = brewblox_logger(__name__) +LOGGER = logging.getLogger(__name__) ConnectInfo = namedtuple('ConnectInfo', ['address', 'port', 'id']) async def _discover( - desired_id: Optional[str], + desired_id: str | None, dns_type: str, ) -> Generator[ConnectInfo, None, None]: queue: asyncio.Queue[ServiceInfo] = asyncio.Queue() @@ -44,7 +44,7 @@ def sync_change_handler(_, service_type, name, state_change): continue # discard unknown addresses and simulators addr = inet_ntoa(info.address) - id = info.properties.get(b'ID', bytes()).decode().lower() + id = info.properties.get(b'ID', b'').decode().lower() if not id: LOGGER.error(f'Invalid device: {info.name} @ {addr}:{info.port} has no ID TXT property') @@ -59,21 +59,21 @@ def sync_change_handler(_, service_type, name, state_change): async def discover_all( - desired_id: Optional[str], + desired_id: str | None, dns_type: str, - timeout_v: float, + timeout: timedelta, ) -> Generator[ConnectInfo, None, None]: with suppress(asyncio.TimeoutError): - async with asyncio.timeout(timeout_v): + async with asyncio.timeout(timeout.total_seconds()): async for res in _discover(desired_id, dns_type): # pragma: no branch yield res async def discover_one( - desired_id: Optional[str], + desired_id: str | None, dns_type: str, - timeout_v: Optional[float] = None, + timeout: timedelta, ) -> ConnectInfo: - async with asyncio.timeout(timeout_v): + async with asyncio.timeout(timeout.total_seconds()): async for res in _discover(desired_id, dns_type): # pragma: no branch return res diff --git a/brewblox_devcon_spark/models.py b/brewblox_devcon_spark/models.py index 36dc2a15..c5abe9bf 100644 --- a/brewblox_devcon_spark/models.py +++ b/brewblox_devcon_spark/models.py @@ -1,18 +1,13 @@ import enum -from dataclasses import dataclass -from typing import Any, Literal, Optional, TypedDict, Union +from datetime import timedelta +from pathlib import Path +from typing import Any, Literal, Self -from brewblox_service.models import BaseServiceConfig -from pydantic import BaseModel, Field, validator +from pydantic import (BaseModel, ConfigDict, Field, ValidationInfo, + computed_field, field_validator, model_validator) +from pydantic_settings import BaseSettings, SettingsConfigDict - -class ServiceFirmwareIni(TypedDict): - firmware_version: str - firmware_date: str - firmware_sha: str - proto_version: str - proto_date: str - system_version: str +from . import const class DiscoveryType(enum.Enum): @@ -29,61 +24,131 @@ def __str__(self): return self.name -class ServiceConfig(BaseServiceConfig): +class ServiceConfig(BaseSettings): + model_config = SettingsConfigDict( + env_file='.appenv', + env_prefix='brewblox_', + case_sensitive=False, + json_schema_extra='ignore', + ) + + # Generic options + name: str = '' # autodetect if not set + debug: bool = False + trace: bool = False + debugger: bool = False + + # MQTT options + mqtt_protocol: Literal['mqtt', 'mqtts'] = 'mqtt' + mqtt_host: str = 'eventbus' + mqtt_port: int = 1883 + + state_topic: str = 'brewcast/state' + history_topic: str = 'brewcast/history' + datastore_topic: str = 'brewcast/datastore' + blocks_topic: str = 'brewcast/spark/blocks' + + # HTTP client options + http_client_interval: timedelta = timedelta(seconds=1) + http_client_interval_max: timedelta = timedelta(minutes=1) + http_client_backoff: float = 1.1 + + # Datastore options + datastore_host: str = 'history' + datastore_port: int = 5000 + datastore_path: str = '/history/datastore' + + datastore_fetch_timeout: timedelta = timedelta(minutes=5) + datastore_flush_delay: timedelta = timedelta(seconds=5) + datastore_shutdown_timeout: timedelta = timedelta(seconds=2) + # Device options - simulation: bool - mock: bool - device_host: Optional[str] - device_port: int - device_serial: Optional[str] - device_id: Optional[str] - discovery: DiscoveryType - display_ws_port: int - - # Network options - command_timeout: float - broadcast_interval: float - isolated: bool - datastore_topic: str + device_id: str | None = None + discovery: DiscoveryType = DiscoveryType.all + + device_host: str | None = None + device_port: int = 8332 + device_serial: str | None = None + + mock: bool = False + + simulation: bool = False + simulation_port: int = 0 # any free port + simulation_display_port: int = 0 # any free port + simulation_workdir: Path = Path('./simulator') + + # Connection options + connect_interval: timedelta = timedelta(seconds=2) + connect_interval_max: timedelta = timedelta(seconds=30) + connect_backoff: float = 1.5 + + discovery_interval: timedelta = timedelta(seconds=5) + discovery_timeout: timedelta = timedelta(minutes=2) + discovery_timeout_mqtt: timedelta = timedelta(seconds=3) + discovery_timeout_mdns: timedelta = timedelta(seconds=20) + + subprocess_connect_interval: timedelta = timedelta(milliseconds=200) + subprocess_connect_timeout: timedelta = timedelta(seconds=10) + + handshake_timeout: timedelta = timedelta(minutes=2) + handshake_ping_interval: timedelta = timedelta(seconds=2) + + # Command options + command_timeout: timedelta = timedelta(seconds=20) + + # Broadcast options + broadcast_interval: timedelta = timedelta(seconds=5) # Firmware options - skip_version_check: bool + skip_version_check: bool = False # Backup options - backup_interval: float - backup_retry_interval: float + backup_interval: timedelta = timedelta(hours=1) + backup_retry_interval: timedelta = timedelta(minutes=5) + backup_root_dir: Path = Path('./backup') # Time sync options - time_sync_interval: float + time_sync_interval: timedelta = timedelta(minutes=15) + time_sync_retry_interval: timedelta = timedelta(seconds=10) + # Firmware flash options + flash_ymodem_timeout: timedelta = timedelta(seconds=30) + flash_disconnect_timeout: timedelta = timedelta(seconds=20) -class BlockIdentity(BaseModel): - id: Optional[str] - nid: Optional[int] - type: Optional[str] - serviceId: Optional[str] + @computed_field + @property + def datastore_url(self) -> str: + return f'http://{self.datastore_host}:{self.datastore_port}{self.datastore_path}' -class Block(BaseModel): - id: Optional[str] - nid: Optional[int] - type: str - serviceId: Optional[str] - data: dict[str, Any] +class FirmwareConfig(BaseModel): + firmware_version: str + firmware_date: str + firmware_sha: str + proto_version: str + proto_date: str + system_version: str -class BlockList(BaseModel): - __root__: list[Block] +class BlockIdentity(BaseModel): + id: str | None = None + nid: int | None = None + type: str | None = None + serviceId: str | None = None -class BlockIdentityList(BaseModel): - __root__: list[BlockIdentity] +class Block(BaseModel): + id: str | None = None + nid: int | None = None + type: str + serviceId: str | None = None + data: dict[str, Any] class FirmwareBlockIdentity(BaseModel): nid: int - type: Optional[str] - data: Optional[dict[str, Any]] + type: str | None = None + data: dict[str, Any] | None = None class FirmwareBlock(BaseModel): @@ -92,7 +157,7 @@ class FirmwareBlock(BaseModel): data: dict[str, Any] -class StoreEntry(TypedDict): +class TwinKeyEntry(BaseModel): keys: tuple[str, int] data: dict @@ -121,6 +186,9 @@ class ResetReason(enum.Enum): PANIC = '82' USER = '8C' + def __str__(self): + return self.name + class ResetData(enum.Enum): NOT_SPECIFIED = '00' @@ -132,6 +200,9 @@ class ResetData(enum.Enum): FIRMWARE_UPDATE_SUCCESS = '06' OUT_OF_MEMORY = '07' + def __str__(self): + return self.name + class Opcode(enum.Enum): NONE = 0 @@ -211,62 +282,47 @@ class MaskMode(enum.Enum): class BasePayload(BaseModel): blockId: int mask: list[int] = Field(default_factory=list) - maskMode: MaskMode = Field(default=MaskMode.NO_MASK) + maskMode: MaskMode = MaskMode.NO_MASK - @validator('maskMode', pre=True) + @field_validator('maskMode', mode='before') + @classmethod def from_raw_mask_mode(cls, v): if isinstance(v, str): return MaskMode[v] return MaskMode(v) - def clean_dict(self): - return { - **self.dict(), - 'maskMode': self.maskMode.name, - } - class EncodedPayload(BasePayload): - blockType: Optional[Union[int, str]] - subtype: Optional[Union[int, str]] - content: str = Field(default='') - - class Config: - # ensures integers in Union[int, str] are parsed correctly - smart_union = True + blockType: int | str | None = None + subtype: int | str | None = None + content: str = '' class DecodedPayload(BasePayload): - blockType: Optional[str] - subtype: Optional[str] - content: Optional[dict] + blockType: str | None = None + subtype: str | None = None + content: dict | None = None class BaseRequest(BaseModel): msgId: int opcode: Opcode - payload: Optional[BasePayload] + payload: BasePayload | None = None - @validator('opcode', pre=True) + @field_validator('opcode', mode='before') + @classmethod def from_raw_opcode(cls, v): if isinstance(v, str): return Opcode[v] return Opcode(v) - def clean_dict(self): - return { - **self.dict(), - 'opcode': self.opcode.name, - 'payload': self.payload.clean_dict() if self.payload else None, - } - class IntermediateRequest(BaseRequest): - payload: Optional[EncodedPayload] + payload: EncodedPayload | None = None class DecodedRequest(BaseRequest): - payload: Optional[DecodedPayload] + payload: DecodedPayload | None = None class BaseResponse(BaseModel): @@ -274,19 +330,13 @@ class BaseResponse(BaseModel): error: ErrorCode payload: list[BasePayload] - @validator('error', pre=True) + @field_validator('error', mode='before') + @classmethod def from_raw_error(cls, v): if isinstance(v, str): return ErrorCode[v] return ErrorCode(v) - def clean_dict(self): - return { - **self.dict(), - 'error': self.error.name, - 'payload': [v.clean_dict() for v in self.payload] - } - class IntermediateResponse(BaseResponse): payload: list[EncodedPayload] @@ -300,8 +350,7 @@ class EncodedMessage(BaseModel): message: str -@dataclass -class HandshakeMessage: +class HandshakeMessage(BaseModel): name: str firmware_version: str proto_version: str @@ -311,16 +360,18 @@ class HandshakeMessage: platform: str reset_reason_hex: str reset_data_hex: str - device_id: str = Field(default='') - reset_reason: str = Field(init=False) - reset_data: str = Field(init=False) + device_id: str + reset_reason: str = str(ResetReason.NONE) + reset_data: str = str(ResetData.NOT_SPECIFIED) - def __post_init__(self): - self.reset_reason = ResetReason(self.reset_reason_hex.upper()).name + @model_validator(mode='after') + def parse_reset_enums(self) -> Self: + self.reset_reason = str(ResetReason(self.reset_reason_hex.upper())) try: - self.reset_data = ResetData(self.reset_data_hex.upper()).name + self.reset_data = str(ResetData(self.reset_data_hex.upper())) except Exception: - self.reset_data = self.reset_data_hex.upper() + self.reset_data = str(ResetData.NOT_SPECIFIED) + return self class FirmwareDescription(BaseModel): @@ -329,8 +380,9 @@ class FirmwareDescription(BaseModel): firmware_date: str proto_date: str - @validator('firmware_version', 'proto_version') - def truncate_version(cls, v: str): + @field_validator('firmware_version', 'proto_version') + @classmethod + def truncate_version(cls, v: str, info: ValidationInfo): # We only compare the first 8 characters of git hashes return v[:8] @@ -338,9 +390,10 @@ def truncate_version(cls, v: str): class DeviceDescription(BaseModel): device_id: str - @validator('device_id') - def lower_device_id(cls, v: str): - return v.lower() + @model_validator(mode='after') + def lower_device_id(self) -> Self: + self.device_id = self.device_id.lower() + return self class ServiceDescription(BaseModel): @@ -384,16 +437,29 @@ class ControllerDescription(BaseModel): ] -class ServiceStatusDescription(BaseModel): +class StatusDescription(BaseModel): enabled: bool service: ServiceDescription - controller: Optional[ControllerDescription] - address: Optional[str] + controller: ControllerDescription | None = None + address: str | None = None - connection_kind: Optional[ConnectionKind_] + connection_kind: ConnectionKind_ | None = None connection_status: ConnectionStatus_ - firmware_error: Optional[FirmwareError_] - identity_error: Optional[IdentityError_] + firmware_error: FirmwareError_ | None = None + identity_error: IdentityError_ | None = None + + +class BlockRelation(BaseModel): + source: str + target: str + claimed: bool = False + relation: list[str] + + +class BlockClaim(BaseModel): + source: str + target: str + intermediate: list[str] class BackupIdentity(BaseModel): @@ -403,14 +469,127 @@ class BackupIdentity(BaseModel): class Backup(BaseModel): # Older backups won't have these fields # They will not be used when loading backups - name: Optional[str] - timestamp: Optional[str] - firmware: Optional[FirmwareDescription] - device: Optional[DeviceDescription] + name: str | None = None + timestamp: str | None = None + firmware: FirmwareDescription | None = None + device: DeviceDescription | None = None blocks: list[Block] - store: list[StoreEntry] + store: list[TwinKeyEntry] class BackupApplyResult(BaseModel): messages: list[str] + + +class AutoconnectSettings(BaseModel): + enabled: bool + + +class ErrorResponse(BaseModel): + error: str + validation: list | None = None + traceback: list[str] | None = None + + +class FirmwareFlashResponse(BaseModel): + address: str + version: str + + +class PingResponse(BaseModel): + ping: Literal['pong'] = 'pong' + + +class DatastoreSingleQuery(BaseModel): + namespace: str + id: str + + +class DatastoreMultiQuery(BaseModel): + namespace: str + ids: list[str] | None = None + filter: str | None = None + + +class DatastoreValue(BaseModel): + model_config = ConfigDict( + extra='allow', + ) + + namespace: str + id: str + + +class DatastoreSingleValueBox(BaseModel): + value: DatastoreValue | None + + +class DatastoreMultiValueBox(BaseModel): + values: list[DatastoreValue] + + +class TwinKeyEntriesValue(DatastoreValue): + namespace: str = const.SERVICE_NAMESPACE + + data: list[TwinKeyEntry] + + +class StoredServiceSettingsValue(DatastoreValue): + namespace: str = const.SERVICE_NAMESPACE + + enabled: bool = True + + +class StoredUnitSettingsValue(DatastoreValue): + namespace: str = const.GLOBAL_NAMESPACE + id: str = const.GLOBAL_UNITS_ID + + temperature: Literal['degC', 'degF'] = 'degC' + + +class StoredTimezoneSettingsValue(DatastoreValue): + namespace: str = const.GLOBAL_NAMESPACE + id: str = const.GLOBAL_TIME_ZONE_ID + + name: str = 'Etc/UTC' + posixValue: str = 'UTC0' + + +class TwinKeyEntriesBox(DatastoreSingleValueBox): + value: TwinKeyEntriesValue | None + + +class StoredServiceSettingsBox(DatastoreSingleValueBox): + value: StoredServiceSettingsValue | None + + +class StoredUnitSettingsBox(DatastoreSingleValueBox): + value: StoredUnitSettingsValue | None + + +class StoredTimezoneSettingsBox(DatastoreSingleValueBox): + value: StoredTimezoneSettingsValue | None + + +class DatastoreEvent(BaseModel): + changed: list[DatastoreValue] = Field(default_factory=list) + deleted: list[DatastoreValue] = Field(default_factory=list) + + +class ServiceStateEventData(BaseModel): + status: StatusDescription + blocks: list[Block] + relations: list[BlockRelation] + claims: list[BlockClaim] + + +class ServiceStateEvent(BaseModel): + key: str + type: Literal['Spark.state'] = 'Spark.state' + data: ServiceStateEventData + + +class HistoryEvent(BaseModel): + key: str + data: dict diff --git a/brewblox_devcon_spark/mqtt.py b/brewblox_devcon_spark/mqtt.py new file mode 100644 index 00000000..73c9bf1d --- /dev/null +++ b/brewblox_devcon_spark/mqtt.py @@ -0,0 +1,34 @@ +import json +from contextlib import asynccontextmanager +from contextvars import ContextVar + +from fastapi_mqtt.config import MQTTConfig +from fastapi_mqtt.fastmqtt import FastMQTT + +from . import utils + +CV: ContextVar[FastMQTT] = ContextVar('mqtt.client') + + +def setup(): + config = utils.get_config() + mqtt_config = MQTTConfig(host=config.mqtt_host, + port=config.mqtt_port, + ssl=(config.mqtt_protocol == 'mqtts'), + reconnect_retries=-1, + will_message_topic=f'{config.state_topic}/{config.name}', + will_message_payload=json.dumps({ + 'key': config.name, + 'type': 'Spark.state', + 'data': None, + })) + fmqtt = FastMQTT(config=mqtt_config) + CV.set(fmqtt) + + +@asynccontextmanager +async def lifespan(): + fmqtt = CV.get() + await fmqtt.connection() + yield + await fmqtt.client.disconnect() diff --git a/brewblox_devcon_spark/service_status.py b/brewblox_devcon_spark/service_status.py deleted file mode 100644 index 96d752da..00000000 --- a/brewblox_devcon_spark/service_status.py +++ /dev/null @@ -1,256 +0,0 @@ -""" -Awaitable events for tracking device and network status -""" - - -import asyncio -import warnings - -from aiohttp import web -from brewblox_service import brewblox_logger, features - -from brewblox_devcon_spark.models import (ConnectionKind_, - ControllerDescription, - DeviceDescription, - FirmwareDescription, ServiceConfig, - ServiceDescription, - ServiceFirmwareIni, - ServiceStatusDescription) - -LOGGER = brewblox_logger(__name__) - - -class ServiceStatus(features.ServiceFeature): - - def __init__(self, app: web.Application): - super().__init__(app) - ini: ServiceFirmwareIni = app['ini'] - config: ServiceConfig = app['config'] - - service_desc = ServiceDescription( - name=config.name, - firmware=FirmwareDescription( - firmware_version=ini['firmware_version'], - proto_version=ini['proto_version'], - firmware_date=ini['firmware_date'], - proto_date=ini['proto_date'], - ), - device=DeviceDescription( - device_id=config.device_id or '', - ), - ) - - self.status_desc = ServiceStatusDescription( - enabled=False, - service=service_desc, - controller=None, - address=None, - connection_kind=None, - connection_status='DISCONNECTED', - firmware_error=None, - identity_error=None, - ) - - self.enabled_ev: asyncio.Event = None - self.connected_ev: asyncio.Event = None - self.acknowledged_ev: asyncio.Event = None - self.synchronized_ev: asyncio.Event = None - self.disconnected_ev: asyncio.Event = None - self.updating_ev: asyncio.Event = None - - async def startup(self, _): - self.enabled_ev = asyncio.Event() - self.connected_ev = asyncio.Event() - self.acknowledged_ev = asyncio.Event() - self.synchronized_ev = asyncio.Event() - self.disconnected_ev = asyncio.Event() - self.updating_ev = asyncio.Event() - - def desc(self) -> ServiceStatusDescription: - return self.status_desc.copy() - - def set_enabled(self, enabled: bool): - self.status_desc.enabled = enabled - - if enabled: - self.enabled_ev.set() - else: - self.enabled_ev.clear() - - def set_connected(self, - connection_kind: ConnectionKind_, - address: str): - self.status_desc.address = address - self.status_desc.connection_kind = connection_kind - self.status_desc.connection_status = 'CONNECTED' - - LOGGER.info(f'>>> CONNECTED ({connection_kind})') - self.connected_ev.set() - self.acknowledged_ev.clear() - self.synchronized_ev.clear() - self.disconnected_ev.clear() - - def set_acknowledged(self, controller: ControllerDescription): - if self.synchronized_ev.is_set(): - # Do not revert to acknowledged if we're already synchronized. - # For there to be a meaningful change, - # there must have been a disconnect first. - return - - config: ServiceConfig = self.app['config'] - service = self.status_desc.service - - wildcard_id = not service.device.device_id - compatible_firmware = service.firmware.proto_version == controller.firmware.proto_version \ - or bool(config.skip_version_check) - matching_firmware = service.firmware.firmware_version == controller.firmware.firmware_version - compatible_identity = service.device.device_id == controller.device.device_id \ - or wildcard_id - - if not compatible_firmware: - warnings.warn('Handshake error: incompatible firmware') - - if not compatible_identity: - warnings.warn('Handshake error: incompatible device ID') - - # determine firmware_error - if not compatible_firmware: - firmware_error = 'INCOMPATIBLE' - elif not matching_firmware: - firmware_error = 'MISMATCHED' - else: - firmware_error = None - - # determine identity_error - if not compatible_identity: - identity_error = 'INCOMPATIBLE' - elif wildcard_id: - identity_error = 'WILDCARD_ID' - else: - identity_error = None - - self.status_desc.connection_status = 'ACKNOWLEDGED' - self.status_desc.controller = controller - self.status_desc.firmware_error = firmware_error - self.status_desc.identity_error = identity_error - - LOGGER.info('>>> ACKNOWLEDGED') - self.acknowledged_ev.set() - - def set_synchronized(self): - if not self.acknowledged_ev.is_set(): - raise RuntimeError('Failed to set synchronized status: ' - 'service is not acknowledged') - - self.status_desc.connection_status = 'SYNCHRONIZED' - - LOGGER.info('>>> SYNCHRONIZED') - self.synchronized_ev.set() - - def set_updating(self): - self.status_desc.connection_status = 'UPDATING' - - LOGGER.info('>>> UPDATING') - self.updating_ev.set() - - def set_disconnected(self): - self.status_desc.controller = None - self.status_desc.address = None - self.status_desc.connection_kind = None - self.status_desc.connection_status = 'DISCONNECTED' - self.status_desc.firmware_error = None - self.status_desc.identity_error = None - - LOGGER.info('>>> DISCONNECTED') - self.connected_ev.clear() - self.acknowledged_ev.clear() - self.synchronized_ev.clear() - self.updating_ev.clear() - self.disconnected_ev.set() - - -def setup(app: web.Application): - features.add(app, ServiceStatus(app)) - - -def fget(app: web.Application) -> ServiceStatus: - return features.get(app, ServiceStatus) - - -def desc(app: web.Application) -> ServiceStatusDescription: - return fget(app).desc() - - -def set_enabled(app: web.Application, enabled: bool): - fget(app).set_enabled(enabled) - - -def set_connected(app: web.Application, - kind: ConnectionKind_, - address: str): - fget(app).set_connected(kind, address) - - -def set_acknowledged(app: web.Application, - controller: ControllerDescription): - fget(app).set_acknowledged(controller) - - -def set_synchronized(app: web.Application): - fget(app).set_synchronized() - - -def set_updating(app: web.Application): - fget(app).set_updating() - - -def set_disconnected(app: web.Application): - fget(app).set_disconnected() - - -def is_enabled(app: web.Application) -> bool: - return fget(app).enabled_ev.is_set() - - -def is_connected(app: web.Application) -> bool: - return fget(app).connected_ev.is_set() - - -def is_acknowledged(app: web.Application) -> bool: - return fget(app).acknowledged_ev.is_set() - - -def is_synchronized(app: web.Application) -> bool: - return fget(app).synchronized_ev.is_set() - - -def is_disconnected(app: web.Application) -> bool: - return fget(app).disconnected_ev.is_set() - - -def is_updating(app: web.Application) -> bool: - return fget(app).updating_ev.is_set() - - -async def wait_enabled(app: web.Application) -> bool: - return await fget(app).enabled_ev.wait() - - -async def wait_connected(app: web.Application) -> bool: - return await fget(app).connected_ev.wait() - - -async def wait_acknowledged(app: web.Application) -> bool: - return await fget(app).acknowledged_ev.wait() - - -async def wait_synchronized(app: web.Application) -> bool: - return await fget(app).synchronized_ev.wait() - - -async def wait_updating(app: web.Application) -> bool: - return await fget(app).updating_ev.wait() - - -async def wait_disconnected(app: web.Application) -> bool: - return await fget(app).disconnected_ev.wait() diff --git a/brewblox_devcon_spark/service_store.py b/brewblox_devcon_spark/service_store.py deleted file mode 100644 index 068bc02d..00000000 --- a/brewblox_devcon_spark/service_store.py +++ /dev/null @@ -1,129 +0,0 @@ -""" -Stores persistent service-specific configuration -""" - -import asyncio -import json -import warnings -from contextlib import contextmanager - -from aiohttp import web -from brewblox_service import brewblox_logger, features, http, strex - -from brewblox_devcon_spark import const -from brewblox_devcon_spark.datastore import (STORE_URL, FlushedStore, - non_isolated) - -SERVICE_STORE_KEY = '{name}-service-db' -READY_TIMEOUT_S = 60 - -# Known fields -RECONNECT_DELAY_KEY = 'reconnect_delay' -AUTOCONNECTING_KEY = 'autoconnecting' - -SYS_OBJECTS = [ - {'keys': keys, 'data': {}} - for keys in const.SYS_OBJECT_KEYS -] - -LOGGER = brewblox_logger(__name__) - - -class ServiceConfigStore(FlushedStore): - """ - Database-backed configuration - """ - - def __init__(self, app: web.Application): - super().__init__(app) - self._config: dict = {} - self._ready_event: asyncio.Event = None - self.key: str = SERVICE_STORE_KEY.format(name=self.app['config'].name) - - def __str__(self): - return f'<{type(self).__name__}>' - - async def startup(self, app: web.Application): - await FlushedStore.startup(self, app) - self._ready_event = asyncio.Event() - - async def shutdown(self, app: web.Application): - await FlushedStore.shutdown(self, app) - self._ready_event = None - - @contextmanager - def open(self): - before = json.dumps(self._config) - yield self._config - after = json.dumps(self._config) - if before != after: - self.set_changed() - - async def read(self): - data = {} - - try: - self._ready_event.clear() - if not self.isolated: - resp = await http.session(self.app).post(f'{STORE_URL}/get', json={ - 'id': self.key, - 'namespace': const.SPARK_NAMESPACE, - }) - # `value` is None if no document is found. - resp_value = (await resp.json())['value'] - if resp_value: - LOGGER.info(f'{self} read {resp_value}') - data = resp_value.get('data', {}) - else: - warnings.warn(f'{self} found no config. Defaults will be used.') - - except Exception as ex: - warnings.warn(f'{self} read error {strex(ex)}') - - finally: - self._config = data - self._ready_event.set() - - @non_isolated - async def write(self): - await asyncio.wait_for(self._ready_event.wait(), READY_TIMEOUT_S) - await http.session(self.app).post(f'{STORE_URL}/set', json={ - 'value': { - 'id': self.key, - 'namespace': const.SPARK_NAMESPACE, - 'data': self._config, - }, - }) - LOGGER.info(f'{self} saved config: {self._config}') - - -def setup(app: web.Application): - features.add(app, ServiceConfigStore(app)) - - -def fget(app: web.Application) -> ServiceConfigStore: - return features.get(app, ServiceConfigStore) - -# Convenience functions for know configuration - - -def get_autoconnecting(app: web.Application) -> bool: - return bool(fget(app)._config.get(AUTOCONNECTING_KEY, True)) - - -def set_autoconnecting(app: web.Application, enabled: bool) -> bool: - enabled = bool(enabled) - with fget(app).open() as config: - config[AUTOCONNECTING_KEY] = enabled - return enabled - - -def get_reconnect_delay(app: web.Application) -> float: - return float(fget(app)._config.get(RECONNECT_DELAY_KEY, 0)) - - -def set_reconnect_delay(app: web.Application, value: float) -> float: - value = float(value) - with fget(app).open() as config: - config[RECONNECT_DELAY_KEY] = value - return value diff --git a/brewblox_devcon_spark/state_machine.py b/brewblox_devcon_spark/state_machine.py new file mode 100644 index 00000000..6cdbe383 --- /dev/null +++ b/brewblox_devcon_spark/state_machine.py @@ -0,0 +1,209 @@ +""" +Awaitable events for tracking device and network status +""" + + +import asyncio +import logging +from contextvars import ContextVar +from typing import Literal + +from . import exceptions, utils +from .models import (ConnectionKind_, ControllerDescription, DeviceDescription, + FirmwareDescription, ServiceDescription, + StatusDescription) + +LOGGER = logging.getLogger(__name__) +CV: ContextVar['StateMachine'] = ContextVar('state_machine.StateMachine') + + +class StateMachine: + + def __init__(self): + config = utils.get_config() + fw_config = utils.get_fw_config() + + service_desc = ServiceDescription( + name=config.name, + firmware=FirmwareDescription( + firmware_version=fw_config.firmware_version, + proto_version=fw_config.proto_version, + firmware_date=fw_config.firmware_date, + proto_date=fw_config.proto_date, + ), + device=DeviceDescription( + device_id=config.device_id or '', + ), + ) + + self._status_desc = StatusDescription( + enabled=False, + service=service_desc, + controller=None, + address=None, + connection_kind=None, + connection_status='DISCONNECTED', + firmware_error=None, + identity_error=None, + ) + + self._enabled_ev = asyncio.Event() + self._connected_ev = asyncio.Event() + self._acknowledged_ev = asyncio.Event() + self._synchronized_ev = asyncio.Event() + self._disconnected_ev = asyncio.Event() + self._updating_ev = asyncio.Event() + + # Initial state is disconnected + self._disconnected_ev.set() + + def desc(self) -> StatusDescription: + return self._status_desc + + def check_compatible(self) -> Literal[True]: + if self._status_desc.firmware_error == 'INCOMPATIBLE': + raise exceptions.IncompatibleFirmware() + + if self._status_desc.identity_error == 'INCOMPATIBLE': + raise exceptions.InvalidDeviceId() + + return True + + def set_enabled(self, enabled: bool): + self._status_desc.enabled = enabled + + if enabled: + self._enabled_ev.set() + else: + self._enabled_ev.clear() + + def is_enabled(self) -> bool: + return self._enabled_ev.is_set() + + async def wait_enabled(self): + await self._enabled_ev.wait() + + def set_connected(self, + connection_kind: ConnectionKind_, + address: str): + self._status_desc.address = address + self._status_desc.connection_kind = connection_kind + self._status_desc.connection_status = 'CONNECTED' + + LOGGER.info(f'>>> CONNECTED ({connection_kind})') + self._connected_ev.set() + self._acknowledged_ev.clear() + self._synchronized_ev.clear() + self._disconnected_ev.clear() + + def is_connected(self) -> bool: + return self._connected_ev.is_set() + + async def wait_connected(self) -> Literal[True]: + return await self._connected_ev.wait() + + def set_acknowledged(self, controller: ControllerDescription): + if self._synchronized_ev.is_set(): + # Do not revert to acknowledged if we're already synchronized. + # For there to be a meaningful change, + # there must have been a disconnect first. + return + + config = utils.get_config() + service = self._status_desc.service + + wildcard_id = not service.device.device_id + compatible_firmware = service.firmware.proto_version == controller.firmware.proto_version \ + or bool(config.skip_version_check) + matching_firmware = service.firmware.firmware_version == controller.firmware.firmware_version + compatible_identity = service.device.device_id == controller.device.device_id \ + or wildcard_id + + if not compatible_firmware: + LOGGER.warning('Handshake error: incompatible firmware') + + if not compatible_identity: + LOGGER.warning('Handshake error: incompatible device ID') + + # determine firmware_error + if not compatible_firmware: + firmware_error = 'INCOMPATIBLE' + elif not matching_firmware: + firmware_error = 'MISMATCHED' + else: + firmware_error = None + + # determine identity_error + if not compatible_identity: + identity_error = 'INCOMPATIBLE' + elif wildcard_id: + identity_error = 'WILDCARD_ID' + else: + identity_error = None + + self._status_desc.connection_status = 'ACKNOWLEDGED' + self._status_desc.controller = controller + self._status_desc.firmware_error = firmware_error + self._status_desc.identity_error = identity_error + + LOGGER.info('>>> ACKNOWLEDGED') + self._acknowledged_ev.set() + + def is_acknowledged(self) -> bool: + return self._acknowledged_ev.is_set() + + async def wait_acknowledged(self) -> Literal[True]: + return await self._acknowledged_ev.wait() + + def set_synchronized(self): + if not self._acknowledged_ev.is_set(): + raise RuntimeError('Failed to set synchronized status: ' + 'service is not acknowledged') + + self._status_desc.connection_status = 'SYNCHRONIZED' + + LOGGER.info('>>> SYNCHRONIZED') + self._synchronized_ev.set() + + def is_synchronized(self) -> bool: + return self._synchronized_ev.is_set() + + async def wait_synchronized(self) -> Literal[True]: + return await self._synchronized_ev.wait() + + def set_updating(self): + self._status_desc.connection_status = 'UPDATING' + + LOGGER.info('>>> UPDATING') + self._updating_ev.set() + + def is_updating(self) -> bool: + return self._updating_ev.is_set() + + async def wait_updating(self) -> Literal[True]: + return await self._updating_ev.wait() + + def set_disconnected(self): + self._status_desc.controller = None + self._status_desc.address = None + self._status_desc.connection_kind = None + self._status_desc.connection_status = 'DISCONNECTED' + self._status_desc.firmware_error = None + self._status_desc.identity_error = None + + LOGGER.info('>>> DISCONNECTED') + self._connected_ev.clear() + self._acknowledged_ev.clear() + self._synchronized_ev.clear() + self._updating_ev.clear() + self._disconnected_ev.set() + + def is_disconnected(self) -> bool: + return self._disconnected_ev.is_set() + + async def wait_disconnected(self) -> Literal[True]: + return await self._disconnected_ev.wait() + + +def setup(): + CV.set(StateMachine()) diff --git a/brewblox_devcon_spark/synchronization.py b/brewblox_devcon_spark/synchronization.py index 82d37104..ad99cae3 100644 --- a/brewblox_devcon_spark/synchronization.py +++ b/brewblox_devcon_spark/synchronization.py @@ -2,15 +2,13 @@ This feature continuously manages synchronization between the spark service, the spark controller, and the datastore. -At startup, configuration is fetched from the datastore. -For the global datastore, change callbacks are in place. -This service will be the only one to change any of the other settings, -and does not need to be notified of external changes. - After startup, `connection` and `synchronization` cooperate to advance a state machine. The state machine will progress linearly, but may revert to DISCONNECTED at any time. +This state machine is disabled if `enabled` is False in service settings. +`connection` will wait until enabled before it attempts to discover and connect. + - DISCONNECTED: The service is not connected at a transport level. - CONNECTED: The service is connected at a transport level, but has not yet received a handshake. @@ -25,6 +23,7 @@ The synchronization process consists of: +- Set enabled flag to `service_settings.enabled` value. - Wait for CONNECTED status. - Synchronize handshake: - Repeatedly prompt the controller to send a handshake, @@ -41,23 +40,18 @@ - Repeat """ - import asyncio +import logging +import traceback +from contextlib import asynccontextmanager from functools import wraps -from aiohttp import web -from brewblox_service import brewblox_logger, features, repeater, strex - -from brewblox_devcon_spark import (block_store, codec, commander, const, - datastore, exceptions, global_store, - service_status, service_store) -from brewblox_devcon_spark.codec.time_utils import serialize_duration -from brewblox_devcon_spark.models import FirmwareBlock +from . import (codec, command, const, datastore_blocks, datastore_settings, + exceptions, state_machine, utils) +from .codec.time_utils import serialize_duration +from .models import FirmwareBlock -HANDSHAKE_TIMEOUT_S = 120 -PING_INTERVAL_S = 2 - -LOGGER = brewblox_logger(__name__) +LOGGER = logging.getLogger(__name__) def subroutine(desc: str): @@ -72,154 +66,149 @@ async def wrapped(*args, **kwargs): try: return await func(*args, **kwargs) except Exception as ex: - LOGGER.error(f'Sync subroutine failed: {desc} - {strex(ex)}') + LOGGER.error(f'Sync subroutine failed: {desc} - {utils.strex(ex)}') raise ex return wrapped return wrapper -class SparkSynchronization(repeater.RepeaterFeature): - - def __init__(self, app: web.Application): - super().__init__(app) - self.unit_converter = codec.unit_conversion.fget(self.app) - self.codec = codec.fget(app) - self.commander = commander.fget(app) - self.service_store = service_store.fget(self.app) - self.global_store = global_store.fget(self.app) - self.block_store = block_store.fget(self.app) - - async def before_shutdown(self, app: web.Application): - await self.end() - - async def prepare(self): - # One-time datastore synchronization - await self._sync_datastore() - - async def run(self): - try: - await service_status.wait_connected(self.app) - await self.synchronize() - - except exceptions.IncompatibleFirmware: - LOGGER.error('Incompatible firmware version detected') - - except exceptions.InvalidDeviceId: - LOGGER.error('Invalid device ID detected') +class StateSynchronizer: - except Exception as ex: - LOGGER.error(f'Failed to sync: {strex(ex)}') - await self.commander.start_reconnect() - - await service_status.wait_disconnected(self.app) + def __init__(self): + self.config = utils.get_config() + self.state = state_machine.CV.get() + self.settings_store = datastore_settings.CV.get() + self.block_store = datastore_blocks.CV.get() + self.converter = codec.unit_conversion.CV.get() + self.commander = command.CV.get() @property def device_name(self) -> str: # Simulation services are identified by service name. # This prevents data conflicts when a simulation service # is reconfigured to start interacting with a controller. - if self.app['config'].simulation: - return 'simulator__' + self.app['config'].name - - return service_status.desc(self.app).controller.device.device_id + desc = self.state.desc() - async def synchronize(self): - await self._sync_handshake() - await self._sync_block_store() - await self._sync_sysinfo() - service_status.set_synchronized(self.app) + if desc.connection_kind == 'SIM': + return f'simulator__{self.config.name}' - @subroutine('sync datastore') - async def _sync_datastore(self): - self.global_store.listeners.add(self.on_global_store_change) - await datastore.check_remote(self.app) - await self.service_store.read() - await self.global_store.read() + return desc.controller.device.device_id + @subroutine('apply global settings') + async def _apply_global_settings(self): await self.set_converter_units() - autoconnecting = service_store.get_autoconnecting(self.app) - service_status.set_enabled(self.app, autoconnecting) + # This function may be invoked as a callback by datastore + # Here, we don't know if we're synchronized + if self.state.is_synchronized(): + await self.set_sysinfo_settings() + + @subroutine('apply service settings') + async def _apply_service_settings(self): + enabled = self.settings_store.service_settings.enabled + self.state.set_enabled(enabled) async def _prompt_handshake(self): try: LOGGER.info('prompting handshake...') await self.commander.version() except Exception as ex: - LOGGER.debug(f'Handshake prompt error: {strex(ex)}') + LOGGER.debug(f'Handshake prompt error: {utils.strex(ex)}', exc_info=True) @subroutine('sync handshake') async def _sync_handshake(self): - # Simultaneously prompt a handshake, and wait for it to be received - ack_task = asyncio.create_task(service_status.wait_acknowledged(self.app)) - try: - async with asyncio.timeout(HANDSHAKE_TIMEOUT_S): + # Periodically prompt a handshake until acknowledged by the controller + async with asyncio.timeout(self.config.handshake_timeout.total_seconds()): + async with utils.task_context(self.state.wait_acknowledged()) as ack_task: while not ack_task.done(): await self._prompt_handshake() - await asyncio.wait([ack_task], timeout=PING_INTERVAL_S) - finally: - ack_task.cancel() - - ack_task.result() - desc = service_status.desc(self.app) + # Returns early if acknowledged before timeout elapsed + await asyncio.wait([ack_task], + timeout=self.config.handshake_ping_interval.total_seconds()) - if desc.firmware_error == 'INCOMPATIBLE': - raise exceptions.IncompatibleFirmware() - - if desc.identity_error == 'INVALID': - raise exceptions.InvalidDeviceId() + self.state.check_compatible() @subroutine('sync block store') async def _sync_block_store(self): - await datastore.check_remote(self.app) - await self.block_store.read(self.device_name) + await self.block_store.load(self.device_name) @subroutine('sync controller settings') async def _sync_sysinfo(self): await self.set_sysinfo_settings() - async def on_global_store_change(self): - """Callback invoked by global_store""" - await self.set_converter_units() - - if service_status.is_synchronized(self.app): - await self.set_sysinfo_settings() - async def set_converter_units(self): - self.unit_converter.temperature = self.global_store.units['temperature'] - LOGGER.info(f'Service temperature unit set to {self.unit_converter.temperature}') + LOGGER.info('\n'.join(traceback.format_tb(None))) + self.converter.temperature = self.settings_store.unit_settings.temperature + LOGGER.info(f'Service temperature unit set to {self.converter.temperature}') async def set_sysinfo_settings(self): # Get time zone - tz_name = self.global_store.time_zone['name'] - tz_posix = self.global_store.time_zone['posixValue'] + tz_name = self.settings_store.timezone_settings.name + tz_posix = self.settings_store.timezone_settings.posixValue LOGGER.info(f'Spark time zone: {tz_posix} ({tz_name})') # Get temp unit - temp_unit_name = self.global_store.units['temperature'] + temp_unit_name = self.settings_store.unit_settings.temperature temp_unit_enum = 'TEMP_FAHRENHEIT' if temp_unit_name == 'degF' else 'TEMP_CELSIUS' LOGGER.info(f'Spark temp unit: {temp_unit_enum}') sysinfo = await self.commander.patch_block( FirmwareBlock( nid=const.SYSINFO_NID, - type='SysInfo', + type=const.SYSINFO_BLOCK_TYPE, data={ 'timeZone': tz_posix, 'tempUnit': temp_unit_enum, }, )) + if sysinfo.type != const.SYSINFO_BLOCK_TYPE: + raise exceptions.CommandException(f'Unexpected SysInfo block: {sysinfo}') + uptime = sysinfo.data['uptime']['value'] LOGGER.info(f'Spark uptime: {serialize_duration(uptime)}') update_freq = sysinfo.data['updatesPerSecond'] LOGGER.info(f'Spark updates per second: {update_freq}') + async def synchronize(self): + await self._apply_global_settings() + await self._apply_service_settings() + await self.state.wait_connected() + await self._sync_handshake() + await self._sync_block_store() + await self._sync_sysinfo() + self.state.set_synchronized() -def setup(app: web.Application): - features.add(app, SparkSynchronization(app)) + async def run(self): + try: + await self.synchronize() + + except exceptions.IncompatibleFirmware: + LOGGER.error('Incompatible firmware version detected') + + except exceptions.InvalidDeviceId: + LOGGER.error('Invalid device ID detected') + + except Exception as ex: + LOGGER.error(f'Failed to sync: {utils.strex(ex)}') + await self.commander.reset_connection() + + await self.state.wait_disconnected() + + async def repeat(self): + try: + self.settings_store.service_settings_listeners.add(self._apply_service_settings) + self.settings_store.global_settings_listeners.add(self._apply_global_settings) + while True: + await self.run() + finally: + self.settings_store.service_settings_listeners.remove(self._apply_service_settings) + self.settings_store.global_settings_listeners.remove(self._apply_global_settings) -def fget(app: web.Application) -> SparkSynchronization: - return features.get(app, SparkSynchronization) +@asynccontextmanager +async def lifespan(): + sync = StateSynchronizer() + async with utils.task_context(sync.repeat()): + yield diff --git a/brewblox_devcon_spark/time_sync.py b/brewblox_devcon_spark/time_sync.py index dc00ec29..b7f3712c 100644 --- a/brewblox_devcon_spark/time_sync.py +++ b/brewblox_devcon_spark/time_sync.py @@ -4,50 +4,52 @@ """ import asyncio -from datetime import datetime +import logging +from contextlib import asynccontextmanager +from datetime import datetime, timedelta -from aiohttp import web -from brewblox_service import brewblox_logger, features, repeater, strex +from . import const, control, state_machine, utils +from .models import Block -from brewblox_devcon_spark import const, controller, service_status -from brewblox_devcon_spark.models import Block, ServiceConfig +LOGGER = logging.getLogger(__name__) -LOGGER = brewblox_logger(__name__) -ERROR_INTERVAL_S = 10 +class TimeSync: -class TimeSync(repeater.RepeaterFeature): - - def __init__(self, app: web.Application): - super().__init__(app) - - config: ServiceConfig = app['config'] - self.interval_s = config.time_sync_interval - self.enabled = self.interval_s > 0 - - async def prepare(self): - if not self.enabled: - raise repeater.RepeaterCancelled() + def __init__(self) -> None: + self.config = utils.get_config() + self.state = state_machine.CV.get() + self.ctrl = control.CV.get() async def run(self): - try: - await service_status.wait_synchronized(self.app) - await controller.fget(self.app).patch_block(Block( - nid=const.SYSINFO_NID, - type='SysInfo', - data={'systemTime': datetime.now()}, - )) - await asyncio.sleep(self.interval_s) - - except Exception as ex: - LOGGER.debug(f'{self} exception: {strex(ex)}') - await asyncio.sleep(ERROR_INTERVAL_S) - raise ex - - -def setup(app: web.Application): - features.add(app, TimeSync(app)) - - -def fget(app: web.Application) -> TimeSync: - return features.get(app, TimeSync) + await self.state.wait_synchronized() + now = datetime.now() + await self.ctrl.patch_block(Block( + nid=const.SYSINFO_NID, + type=const.SYSINFO_BLOCK_TYPE, + data={'systemTime': now}, + )) + LOGGER.debug(f'Time sync: {now=}') + + async def repeat(self): + interval = self.config.time_sync_interval + retry_interval = self.config.time_sync_retry_interval + + if interval <= timedelta(): + LOGGER.warning(f'Cancelling time sync (interval={interval})') + return + + while True: + try: + await self.run() + await asyncio.sleep(interval.total_seconds()) + except Exception as ex: + LOGGER.error(utils.strex(ex), exc_info=self.config.debug) + await asyncio.sleep(retry_interval.total_seconds()) + + +@asynccontextmanager +async def lifespan(): + sync = TimeSync() + async with utils.task_context(sync.repeat()): + yield diff --git a/brewblox_devcon_spark/twinkeydict.py b/brewblox_devcon_spark/twinkeydict.py index 782cf2f9..bde5f6d5 100644 --- a/brewblox_devcon_spark/twinkeydict.py +++ b/brewblox_devcon_spark/twinkeydict.py @@ -4,15 +4,14 @@ When looking up objects with both left and right key, asserts that keys point to the same object. """ +import logging from collections.abc import MutableMapping from contextlib import suppress from dataclasses import dataclass from typing import (TYPE_CHECKING, Any, Generic, Hashable, Iterator, Optional, TypeVar) -from brewblox_service import brewblox_logger - -LOGGER = brewblox_logger(__name__) +LOGGER = logging.getLogger(__name__) LT = TypeVar('LT', bound=Hashable) RT = TypeVar('RT', bound=Hashable) @@ -132,7 +131,7 @@ def __getitem__(self, keys: SparseKeys_) -> VT: def __setitem__(self, keys: Keys_, item): if None in keys: - raise TwinKeyError('None keys not allowed') + raise TwinKeyError(f'None keys not allowed, {keys=}') with suppress(KeyError): # Checks whether key combo either matches, or does not exist diff --git a/brewblox_devcon_spark/utils.py b/brewblox_devcon_spark/utils.py new file mode 100644 index 00000000..4ddf18af --- /dev/null +++ b/brewblox_devcon_spark/utils.py @@ -0,0 +1,257 @@ +import asyncio +import logging +import os +import re +import signal +import socket +from configparser import ConfigParser +from contextlib import asynccontextmanager, suppress +from datetime import timedelta +from functools import lru_cache +from ipaddress import ip_address +from typing import Awaitable, Callable, Coroutine, Generator, TypeVar + +from dns.exception import DNSException +from dns.resolver import Resolver as DNSResolver +from httpx import Response + +from .models import FirmwareConfig, ServiceConfig + +VT = TypeVar('VT') +DVT = TypeVar('DVT') + +LOGGER = logging.getLogger(__name__) + + +@lru_cache +def get_config() -> ServiceConfig: # pragma: no cover + """ + Globally cached getter for service config. + When first called, config is parsed from env. + """ + config = ServiceConfig() + + if not config.device_id and (config.simulation or config.mock): + config.device_id = '123456789012345678901234' + + if not config.simulation_port: + config.simulation_port = get_free_port() + + if not config.simulation_display_port: + config.simulation_display_port = get_free_port() + + if not config.name: + config.name = autodetect_service_name() + + return config + + +@lru_cache +def get_fw_config() -> FirmwareConfig: + """ + Globally cached getter for firmware config. + When first called, config is parsed from the firmware.ini file. + """ + parser = ConfigParser() + parser.read('firmware/firmware.ini') + raw = dict(parser['FIRMWARE'].items()) + config = FirmwareConfig(**raw) + return config + + +def strex(ex: Exception): + """ + Formats exception as `Exception(message)` + """ + return f'{type(ex).__name__}({str(ex)})' + + +def graceful_shutdown(reason: str): + """ + Kills the parent of the current process with a SIGTERM. + The parent process is expected to catch this signal, + and initiate asgi shutdown. + + During asgi shutdown, all lifespan handlers get to clean up. + """ + LOGGER.warning(f'Sending shutdown signal, {reason=}') + os.kill(os.getppid(), signal.SIGTERM) + + +def autodetect_service_name() -> str: # pragma: no cover + """ + Automatically detects the Docker Compose service name of the running + container. + + Compose bridge networks implement DNS resolution to resolve the service name + to the IP address of one of the containers for the service. + By default, the generated container names for Compose services include the + service name. + Typically, this is formatted as `{project_name}-{service_name}-{container_num}`. + If we resolve the container IP address to its container name, + we can extract the service name from this known format. + """ + resolver = DNSResolver() + ip = str(ip_address(socket.gethostbyname(socket.gethostname()))) + answer = resolver.resolve_address(ip) + host = str(answer[0]).split('.')[0] + + # We can first apply a basic sanity check: does the hostname match the format? + match = re.fullmatch(r'.+[_-].+([_-])\d+', host) + if not match: + raise ValueError('Failed to autodetect service name. ' + + f'"{host}" is not formatted as a Compose container name.') + + # We need to identify the separactor character. + # Depending on the Compose version, the separator character is either _ or -. + # We know that the service name is always postfixed with a separator. + # The last found _ or - must then be the separator. + compose_name_sep = match[1] + + # Separating project name and service name is harder. + # Both the service and the project may include the separator character. + # For example, if the container name is `first_second_third_1`, + # we don't know if this is service `third` in project `first_second`, + # or service `second_third` in project `first`. + # + # The solution is to query all options + # until we get a result matching the known IP address for this host. + sections = host.split(compose_name_sep)[1:-1] + candidates = [compose_name_sep.join(sections[i:]) for i in range(len(sections))] + + for c in candidates: + with suppress(DNSException): + answer = resolver.resolve(c, 'A') + if ip in [str(r) for r in answer]: + return c + + raise RuntimeError(f'No service name found for {host=}, {ip=}, {candidates=}') + + +def get_free_port() -> int: + """ + Returns the next free port that is available on the OS + This is a bit of a hack, it does this by creating a new socket, and calling + bind with the 0 port. The operating system will assign a brand new port, + which we can find out using getsockname(). Once we have the new port + information we close the socket thereby returning it to the free pool. + This means it is technically possible for this function to return the same + port twice (for example if run in very quick succession), however operating + systems return a random port number in the default range (1024 - 65535), + and it is highly unlikely for two processes to get the same port number. + In other words, it is possible to flake, but incredibly unlikely. + """ + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(('0.0.0.0', 0)) + portnum = s.getsockname()[1] + + return portnum + + +@asynccontextmanager +async def task_context(coro: Coroutine, + cancel_timeout=timedelta(seconds=5) + ) -> Generator[asyncio.Task, None, None]: + """ + Wraps provided coroutine in an async task. + At the end of the context, the task is cancelled and awaited. + """ + task = asyncio.create_task(coro) + try: + yield task + finally: + task.cancel() + await asyncio.wait([task], timeout=cancel_timeout.total_seconds()) + + +def not_sentinel(value: VT, default_value: DVT) -> VT | DVT: + if value is not ...: + return value + return default_value + + +async def httpx_retry(func: Callable[[], Awaitable[Response]], + interval: timedelta = ..., + max_interval: timedelta = ..., + backoff: float = ...) -> Response: + """ + Retries a httpx request forever until a 2XX response is received. + The interval between requests will be multiplied with `backoff` + until it is equal to `max_interval`. + """ + config = get_config() + interval = not_sentinel(interval, config.http_client_interval) + max_interval = not_sentinel(max_interval, config.http_client_interval_max) + backoff = not_sentinel(backoff, config.http_client_backoff) + + while True: + resp = None + + try: + resp = await func() + if resp.is_success: + return resp + + except Exception as ex: + LOGGER.trace(strex(ex), exc_info=True) + + await asyncio.sleep(interval.total_seconds()) + interval = min(interval * backoff, max_interval) + + +def add_logging_level(level_name: str, level_num: int, method_name: str = ...): + """ + Comprehensively adds a new logging level to the `logging` module and the + currently configured logging class. + + `level_name` becomes an attribute of the `logging` module with the value + `level_num`. `method_name` becomes a convenience method for both `logging` + itself and the class returned by `logging.getLoggerClass()` (usually just + `logging.Logger`). If `method_name` is not specified, `level_name.lower()` is + used. + + To avoid accidental clobberings of existing attributes, this method will + raise an `AttributeError` if the level name is already an attribute of the + `logging` module or if the method name is already present + + Example + ------- + >>> add_logging_level('TRACE', logging.DEBUG - 5) + >>> logging.getLogger(__name__).setLevel("TRACE") + >>> logging.getLogger(__name__).trace('that worked') + >>> logging.trace('so did this') + >>> logging.TRACE + 5 + + Source (2023/12/11): + https://stackoverflow.com/questions/2183233/how-to-add-a-custom-loglevel-to-pythons-logging-facility + """ + method_name = not_sentinel(method_name, level_name.lower()) + + has_level = hasattr(logging, level_name) + has_method = hasattr(logging, method_name) + has_class_method = hasattr(logging.getLoggerClass(), method_name) + active_num = getattr(logging, level_name, None) + + # Repeat calls are only allowed if completely duplicate + if all([has_level, has_method, has_class_method]) and active_num == level_num: + return + elif any([has_level, has_method, has_class_method]): + raise AttributeError(f'{level_name=} or {level_num=} are already defined in logging') + + # This method was inspired by the answers to Stack Overflow post + # http://stackoverflow.com/q/2183233/2988730, especially + # http://stackoverflow.com/a/13638084/2988730 + + def log_for_level(self: logging.Logger, message, *args, **kwargs): + if self.isEnabledFor(level_num): + self._log(level_num, message, args, **kwargs) + + def log_to_root(message, *args, **kwargs): + logging.log(level_num, message, *args, **kwargs) + + logging.addLevelName(level_num, level_name) + setattr(logging, level_name, level_num) + setattr(logging.getLoggerClass(), method_name, log_for_level) + setattr(logging, method_name, log_to_root) diff --git a/brewblox_devcon_spark/ymodem.py b/brewblox_devcon_spark/ymodem.py index d9cc2dd7..e42adc48 100644 --- a/brewblox_devcon_spark/ymodem.py +++ b/brewblox_devcon_spark/ymodem.py @@ -41,25 +41,30 @@ """ import asyncio +import logging import math import os import re import subprocess from contextlib import contextmanager from dataclasses import dataclass +from datetime import timedelta from enum import IntEnum from pathlib import Path -from typing import Any, Awaitable, ByteString, Optional +from typing import Awaitable, ByteString import aiofiles -from brewblox_service import brewblox_logger, strex + +from . import utils YMODEM_TRIGGER_BAUD_RATE = 28800 YMODEM_TRANSFER_BAUD_RATE = 115200 -CONNECT_INTERVAL_S = 3 +USB_CONNECT_INTERVAL = timedelta(seconds=1) +TCP_CONNECT_INTERVAL = timedelta(seconds=3) +NAK_RETRY_DELAY = timedelta(milliseconds=100) CONNECT_ATTEMPTS = 5 -LOGGER = brewblox_logger(__name__) +LOGGER = logging.getLogger(__name__) class Control(IntEnum): @@ -75,8 +80,8 @@ class Control(IntEnum): @dataclass class Connection: - address: Any - process: Optional[subprocess.Popen] + address: str + process: subprocess.Popen | None transport: asyncio.Transport protocol: asyncio.Protocol @@ -127,11 +132,11 @@ def clear(self): self._queue.get_nowait() -def is_tcp(address) -> str: +def is_tcp(address: str) -> str: return ':' in address -async def connect_tcp(address, proc: subprocess.Popen = None) -> Connection: +async def connect_tcp(address: str, proc: subprocess.Popen = None) -> Connection: LOGGER.info(f'Connecting to {address}...') host, port = address.split(':') loop = asyncio.get_event_loop() @@ -141,7 +146,7 @@ async def connect_tcp(address, proc: subprocess.Popen = None) -> Connection: return conn -async def connect_usb(address) -> Connection: +async def connect_usb(address: str) -> Connection: LOGGER.info(f'Creating bridge for {address}') proc = subprocess.Popen([ @@ -153,10 +158,10 @@ async def connect_usb(address) -> Connection: last_err = None for _ in range(5): try: - await asyncio.sleep(1) + await asyncio.sleep(USB_CONNECT_INTERVAL.total_seconds()) return await connect_tcp('localhost:8332', proc) except OSError as ex: - last_err = strex(ex) + last_err = utils.strex(ex) LOGGER.debug(f'Subprocess connection error: {last_err}') raise ConnectionError(last_err) @@ -170,7 +175,7 @@ async def connect(address: str) -> Connection: for _ in range(CONNECT_ATTEMPTS): try: - await asyncio.sleep(CONNECT_INTERVAL_S) + await asyncio.sleep(TCP_CONNECT_INTERVAL.total_seconds()) return await connect_func(address) except ConnectionRefusedError: LOGGER.debug('Connection refused, retrying...') @@ -289,7 +294,7 @@ async def _send_data(self, conn: Connection, seq: int, data: list[int]) -> Contr if response == Control.NAK: LOGGER.debug('Retrying packet...') - await asyncio.sleep(0.1) + await asyncio.sleep(NAK_RETRY_DELAY.total_seconds()) response = await self._send_packet(conn, packet) return response diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..f02dc16a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,43 @@ +services: + eventbus: + image: brewblox/mosquitto:develop + ports: + - "1883:1883" + + victoria: + image: victoriametrics/victoria-metrics:v1.88.0 + command: >- + --retentionPeriod=100y + --influxMeasurementFieldSeparator=/ + --http.pathPrefix=/victoria + --search.latencyOffset=10s + + redis: + image: redis:6.0 + command: --appendonly yes + + history: + image: ghcr.io/brewblox/brewblox-history:develop + + sparkey: + restart: unless-stopped + build: + context: . + dockerfile: ./Dockerfile.service + volumes: + - type: bind + source: ./brewblox_devcon_spark + target: /app/brewblox_devcon_spark + - type: bind + source: ./parse_appenv.py + target: /app/parse_appenv.py + - type: bind + source: ./entrypoint.sh + target: /app/entrypoint.sh + environment: + - UVICORN_RELOAD=True + - BREWBLOX_DEBUG=True + - BREWBLOX_TRACE=False + - BREWBLOX_SIMULATION=True + ports: + - "5000:5000" diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 00000000..979450af --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,10 @@ +#!/bin/env bash +set -euo pipefail + +python3 ./parse_appenv.py "$@" >.appenv + +exec uvicorn \ + --host 0.0.0.0 \ + --port 5000 \ + --factory \ + brewblox_devcon_spark.app_factory:create_app diff --git a/firmware.ini b/firmware.ini index 48168875..aee8eda0 100644 --- a/firmware.ini +++ b/firmware.ini @@ -1,7 +1,7 @@ [FIRMWARE] -firmware_version=f27f141c -firmware_date=2023-12-06 -firmware_sha=f27f141cb66c348afb6735ed08a60d1814791b71 +firmware_version=f3ef0c6d +firmware_date=2024-01-03 +firmware_sha=f3ef0c6d34ee67040b70b572e31414700f963ddc proto_version=0fa3f6b2 proto_date=2023-12-06 system_version=3.2.0 diff --git a/parse_appenv.py b/parse_appenv.py new file mode 100644 index 00000000..161c8237 --- /dev/null +++ b/parse_appenv.py @@ -0,0 +1,40 @@ +import argparse +import shlex +import sys + + +def parse_cmd_args(raw_args: list[str]) -> tuple[argparse.Namespace, list[str]]: + parser = argparse.ArgumentParser() + parser.add_argument('-n', '--name') + parser.add_argument('--debug', action='store_true') + parser.add_argument('--mqtt-protocol') + parser.add_argument('--mqtt-host') + parser.add_argument('--mqtt-port') + parser.add_argument('--history-topic') + parser.add_argument('--datastore-topic') + parser.add_argument('--state-topic') + + parser.add_argument('--simulation', action='store_true') + parser.add_argument('--mock', action='store_true') + parser.add_argument('--device-host') + parser.add_argument('--device-serial') + parser.add_argument('--device-id') + parser.add_argument('--discovery') + parser.add_argument('--command-timeout') + parser.add_argument('--broadcast-interval') + parser.add_argument('--skip-version-check', action='store_true') + parser.add_argument('--backup-interval') + parser.add_argument('--backup-retry-interval') + parser.add_argument('--time-sync-interval') + + return parser.parse_known_args(raw_args) + + +if __name__ == '__main__': + args, unknown = parse_cmd_args(sys.argv[1:]) + if unknown: + print(f'WARNING: ignoring unknown CMD arguments: {unknown}', file=sys.stderr) + output = [f'brewblox_{k}={shlex.quote(str(v))}' + for k, v in vars(args).items() + if v is not None and v is not False] + print(*output, sep='\n') diff --git a/poetry.lock b/poetry.lock index 8cf57c59..3ba777f7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -12,174 +12,62 @@ files = [ ] [[package]] -name = "aiohttp" -version = "3.9.0" -description = "Async http client/server framework (asyncio)" +name = "aiozeroconf" +version = "0.1.8" +description = "Pure Python Multicast DNS Service Discovery Library for asyncio (Bonjour/Avahi compatible)" optional = false -python-versions = ">=3.8" +python-versions = "*" files = [ - {file = "aiohttp-3.9.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6896b8416be9ada4d22cd359d7cb98955576ce863eadad5596b7cdfbf3e17c6c"}, - {file = "aiohttp-3.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1736d87dad8ef46a8ec9cddd349fa9f7bd3a064c47dd6469c0d6763d3d49a4fc"}, - {file = "aiohttp-3.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c9e5f4d7208cda1a2bb600e29069eecf857e6980d0ccc922ccf9d1372c16f4b"}, - {file = "aiohttp-3.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8488519aa05e636c5997719fe543c8daf19f538f4fa044f3ce94bee608817cff"}, - {file = "aiohttp-3.9.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ab16c254e2312efeb799bc3c06897f65a133b38b69682bf75d1f1ee1a9c43a9"}, - {file = "aiohttp-3.9.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7a94bde005a8f926d0fa38b88092a03dea4b4875a61fbcd9ac6f4351df1b57cd"}, - {file = "aiohttp-3.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b777c9286b6c6a94f50ddb3a6e730deec327e9e2256cb08b5530db0f7d40fd8"}, - {file = "aiohttp-3.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:571760ad7736b34d05597a1fd38cbc7d47f7b65deb722cb8e86fd827404d1f6b"}, - {file = "aiohttp-3.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:deac0a32aec29608eb25d730f4bc5a261a65b6c48ded1ed861d2a1852577c932"}, - {file = "aiohttp-3.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4ee1b4152bc3190cc40ddd6a14715e3004944263ea208229ab4c297712aa3075"}, - {file = "aiohttp-3.9.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:3607375053df58ed6f23903aa10cf3112b1240e8c799d243bbad0f7be0666986"}, - {file = "aiohttp-3.9.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:65b0a70a25456d329a5e1426702dde67be0fb7a4ead718005ba2ca582d023a94"}, - {file = "aiohttp-3.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5a2eb5311a37fe105aa35f62f75a078537e1a9e4e1d78c86ec9893a3c97d7a30"}, - {file = "aiohttp-3.9.0-cp310-cp310-win32.whl", hash = "sha256:2cbc14a13fb6b42d344e4f27746a4b03a2cb0c1c3c5b932b0d6ad8881aa390e3"}, - {file = "aiohttp-3.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ac9669990e2016d644ba8ae4758688534aabde8dbbc81f9af129c3f5f01ca9cd"}, - {file = "aiohttp-3.9.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f8e05f5163528962ce1d1806fce763ab893b1c5b7ace0a3538cd81a90622f844"}, - {file = "aiohttp-3.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4afa8f71dba3a5a2e1e1282a51cba7341ae76585345c43d8f0e624882b622218"}, - {file = "aiohttp-3.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f929f4c9b9a00f3e6cc0587abb95ab9c05681f8b14e0fe1daecfa83ea90f8318"}, - {file = "aiohttp-3.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28185e36a78d247c55e9fbea2332d16aefa14c5276a582ce7a896231c6b1c208"}, - {file = "aiohttp-3.9.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a486ddf57ab98b6d19ad36458b9f09e6022de0381674fe00228ca7b741aacb2f"}, - {file = "aiohttp-3.9.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70e851f596c00f40a2f00a46126c95c2e04e146015af05a9da3e4867cfc55911"}, - {file = "aiohttp-3.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5b7bf8fe4d39886adc34311a233a2e01bc10eb4e842220235ed1de57541a896"}, - {file = "aiohttp-3.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c67a51ea415192c2e53e4e048c78bab82d21955b4281d297f517707dc836bf3d"}, - {file = "aiohttp-3.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:694df243f394629bcae2d8ed94c589a181e8ba8604159e6e45e7b22e58291113"}, - {file = "aiohttp-3.9.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3dd8119752dd30dd7bca7d4bc2a92a59be6a003e4e5c2cf7e248b89751b8f4b7"}, - {file = "aiohttp-3.9.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:eb6dfd52063186ac97b4caa25764cdbcdb4b10d97f5c5f66b0fa95052e744eb7"}, - {file = "aiohttp-3.9.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:d97c3e286d0ac9af6223bc132dc4bad6540b37c8d6c0a15fe1e70fb34f9ec411"}, - {file = "aiohttp-3.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:816f4db40555026e4cdda604a1088577c1fb957d02f3f1292e0221353403f192"}, - {file = "aiohttp-3.9.0-cp311-cp311-win32.whl", hash = "sha256:3abf0551874fecf95f93b58f25ef4fc9a250669a2257753f38f8f592db85ddea"}, - {file = "aiohttp-3.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:e18d92c3e9e22553a73e33784fcb0ed484c9874e9a3e96c16a8d6a1e74a0217b"}, - {file = "aiohttp-3.9.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:99ae01fb13a618b9942376df77a1f50c20a281390dad3c56a6ec2942e266220d"}, - {file = "aiohttp-3.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:05857848da443c8c12110d99285d499b4e84d59918a21132e45c3f0804876994"}, - {file = "aiohttp-3.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:317719d7f824eba55857fe0729363af58e27c066c731bc62cd97bc9c3d9c7ea4"}, - {file = "aiohttp-3.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1e3b3c107ccb0e537f309f719994a55621acd2c8fdf6d5ce5152aed788fb940"}, - {file = "aiohttp-3.9.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45820ddbb276113ead8d4907a7802adb77548087ff5465d5c554f9aa3928ae7d"}, - {file = "aiohttp-3.9.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:05a183f1978802588711aed0dea31e697d760ce9055292db9dc1604daa9a8ded"}, - {file = "aiohttp-3.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a4cd44788ea0b5e6bb8fa704597af3a30be75503a7ed1098bc5b8ffdf6c982"}, - {file = "aiohttp-3.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:673343fbc0c1ac44d0d2640addc56e97a052504beacd7ade0dc5e76d3a4c16e8"}, - {file = "aiohttp-3.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7e8a3b79b6d186a9c99761fd4a5e8dd575a48d96021f220ac5b5fa856e5dd029"}, - {file = "aiohttp-3.9.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6777a390e41e78e7c45dab43a4a0196c55c3b8c30eebe017b152939372a83253"}, - {file = "aiohttp-3.9.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7ae5f99a32c53731c93ac3075abd3e1e5cfbe72fc3eaac4c27c9dd64ba3b19fe"}, - {file = "aiohttp-3.9.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:f1e4f254e9c35d8965d377e065c4a8a55d396fe87c8e7e8429bcfdeeb229bfb3"}, - {file = "aiohttp-3.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:11ca808f9a6b63485059f5f6e164ef7ec826483c1212a44f268b3653c91237d8"}, - {file = "aiohttp-3.9.0-cp312-cp312-win32.whl", hash = "sha256:de3cc86f4ea8b4c34a6e43a7306c40c1275e52bfa9748d869c6b7d54aa6dad80"}, - {file = "aiohttp-3.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:ca4fddf84ac7d8a7d0866664936f93318ff01ee33e32381a115b19fb5a4d1202"}, - {file = "aiohttp-3.9.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f09960b5bb1017d16c0f9e9f7fc42160a5a49fa1e87a175fd4a2b1a1833ea0af"}, - {file = "aiohttp-3.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8303531e2c17b1a494ffaeba48f2da655fe932c4e9a2626c8718403c83e5dd2b"}, - {file = "aiohttp-3.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4790e44f46a4aa07b64504089def5744d3b6780468c4ec3a1a36eb7f2cae9814"}, - {file = "aiohttp-3.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1d7edf74a36de0e5ca50787e83a77cf352f5504eb0ffa3f07000a911ba353fb"}, - {file = "aiohttp-3.9.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94697c7293199c2a2551e3e3e18438b4cba293e79c6bc2319f5fd652fccb7456"}, - {file = "aiohttp-3.9.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a1b66dbb8a7d5f50e9e2ea3804b01e766308331d0cac76eb30c563ac89c95985"}, - {file = "aiohttp-3.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9623cfd9e85b76b83ef88519d98326d4731f8d71869867e47a0b979ffec61c73"}, - {file = "aiohttp-3.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f32c86dc967ab8c719fd229ce71917caad13cc1e8356ee997bf02c5b368799bf"}, - {file = "aiohttp-3.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f50b4663c3e0262c3a361faf440761fbef60ccdde5fe8545689a4b3a3c149fb4"}, - {file = "aiohttp-3.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:dcf71c55ec853826cd70eadb2b6ac62ec577416442ca1e0a97ad875a1b3a0305"}, - {file = "aiohttp-3.9.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:42fe4fd9f0dfcc7be4248c162d8056f1d51a04c60e53366b0098d1267c4c9da8"}, - {file = "aiohttp-3.9.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76a86a9989ebf82ee61e06e2bab408aec4ea367dc6da35145c3352b60a112d11"}, - {file = "aiohttp-3.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f9e09a1c83521d770d170b3801eea19b89f41ccaa61d53026ed111cb6f088887"}, - {file = "aiohttp-3.9.0-cp38-cp38-win32.whl", hash = "sha256:a00ce44c21612d185c5275c5cba4bab8d7c1590f248638b667ed8a782fa8cd6f"}, - {file = "aiohttp-3.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:d5b9345ab92ebe6003ae11d8092ce822a0242146e6fa270889b9ba965457ca40"}, - {file = "aiohttp-3.9.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:98d21092bf2637c5fa724a428a69e8f5955f2182bff61f8036827cf6ce1157bf"}, - {file = "aiohttp-3.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:35a68cd63ca6aaef5707888f17a70c36efe62b099a4e853d33dc2e9872125be8"}, - {file = "aiohttp-3.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d7f6235c7475658acfc1769d968e07ab585c79f6ca438ddfecaa9a08006aee2"}, - {file = "aiohttp-3.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db04d1de548f7a62d1dd7e7cdf7c22893ee168e22701895067a28a8ed51b3735"}, - {file = "aiohttp-3.9.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:536b01513d67d10baf6f71c72decdf492fb7433c5f2f133e9a9087379d4b6f31"}, - {file = "aiohttp-3.9.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c8b0a6487e8109427ccf638580865b54e2e3db4a6e0e11c02639231b41fc0f"}, - {file = "aiohttp-3.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7276fe0017664414fdc3618fca411630405f1aaf0cc3be69def650eb50441787"}, - {file = "aiohttp-3.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23170247ef89ffa842a02bbfdc425028574d9e010611659abeb24d890bc53bb8"}, - {file = "aiohttp-3.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b1a2ea8252cacc7fd51df5a56d7a2bb1986ed39be9397b51a08015727dfb69bd"}, - {file = "aiohttp-3.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2d71abc15ff7047412ef26bf812dfc8d0d1020d664617f4913df2df469f26b76"}, - {file = "aiohttp-3.9.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:2d820162c8c2bdbe97d328cd4f417c955ca370027dce593345e437b2e9ffdc4d"}, - {file = "aiohttp-3.9.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:2779f5e7c70f7b421915fd47db332c81de365678180a9f3ab404088f87ba5ff9"}, - {file = "aiohttp-3.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:366bc870d7ac61726f32a489fbe3d1d8876e87506870be66b01aeb84389e967e"}, - {file = "aiohttp-3.9.0-cp39-cp39-win32.whl", hash = "sha256:1df43596b826022b14998f0460926ce261544fedefe0d2f653e1b20f49e96454"}, - {file = "aiohttp-3.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:9c196b30f1b1aa3363a69dd69079ae9bec96c2965c4707eaa6914ba099fb7d4f"}, - {file = "aiohttp-3.9.0.tar.gz", hash = "sha256:09f23292d29135025e19e8ff4f0a68df078fe4ee013bca0105b2e803989de92d"}, + {file = "aiozeroconf-0.1.8.tar.gz", hash = "sha256:10dba96b32e53aa7f01ee80d2c481e4d98cf398c518339ee08a1e9539ba7971c"}, ] [package.dependencies] -aiosignal = ">=1.1.2" -attrs = ">=17.3.0" -frozenlist = ">=1.1.1" -multidict = ">=4.5,<7.0" -yarl = ">=1.0,<2.0" - -[package.extras] -speedups = ["Brotli", "aiodns", "brotlicffi"] +netifaces = "*" [[package]] -name = "aiohttp-pydantic" -version = "1.12.2" -description = "Aiohttp View using pydantic to validate request body and query sting regarding method annotations." +name = "annotated-types" +version = "0.6.0" +description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp_pydantic-1.12.2-py3-none-any.whl", hash = "sha256:49d6bd41880b02ece91c6e59108cdca0d503dbb2f8e04efbe638bd4f02b40aed"}, -] - -[package.dependencies] -aiohttp = "*" -pydantic = ">=1.7" -swagger-ui-bundle = "*" - -[package.extras] -ci = ["codecov (==2.1.11)", "pytest (==6.1.2)", "pytest-aiohttp (==0.3.0)", "pytest-cov (==2.10.1)", "readme-renderer (==29.0)", "twine (==3.4.2)"] -test = ["pytest (==6.1.2)", "pytest-aiohttp (==0.3.0)", "pytest-cov (==2.10.1)", "readme-renderer (==29.0)"] - -[[package]] -name = "aiomqtt" -version = "1.2.1" -description = "The idiomatic asyncio MQTT client, wrapped around paho-mqtt" -optional = false -python-versions = ">=3.8,<4.0" -files = [ - {file = "aiomqtt-1.2.1-py3-none-any.whl", hash = "sha256:3925b40b2b95b1905753d53ef3a9162e903cfab35ebe9647ab4d52e45ffb727f"}, - {file = "aiomqtt-1.2.1.tar.gz", hash = "sha256:7582f4341f08ef7110dd9ab3a559454dc28ccda1eac502ff8f08a73b238ecede"}, + {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, + {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, ] -[package.dependencies] -paho-mqtt = ">=1.6.0,<2.0.0" - [[package]] -name = "aiosignal" -version = "1.3.1" -description = "aiosignal: a list of registered asynchronous callbacks" +name = "anyio" +version = "3.7.1" +description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.7" files = [ - {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, - {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, + {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, + {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, ] [package.dependencies] -frozenlist = ">=1.1.0" - -[[package]] -name = "aiozeroconf" -version = "0.1.8" -description = "Pure Python Multicast DNS Service Discovery Library for asyncio (Bonjour/Avahi compatible)" -optional = false -python-versions = "*" -files = [ - {file = "aiozeroconf-0.1.8.tar.gz", hash = "sha256:10dba96b32e53aa7f01ee80d2c481e4d98cf398c518339ee08a1e9539ba7971c"}, -] +idna = ">=2.8" +sniffio = ">=1.1" -[package.dependencies] -netifaces = "*" +[package.extras] +doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] +test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (<0.22)"] [[package]] -name = "aresponses" -version = "2.1.6" -description = "Asyncio response mocking. Similar to the responses library used for 'requests'" +name = "asgi-lifespan" +version = "2.1.0" +description = "Programmatic startup/shutdown of ASGI apps." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "aresponses-2.1.6-py3-none-any.whl", hash = "sha256:06525f6911063f0f8d370cbc96bd273e6cddc89c7b5163ddf91e0c8abf148a32"}, - {file = "aresponses-2.1.6.tar.gz", hash = "sha256:231dfa0756e39ca9f1e82212038f98e773d1ed9c0993caf2667e25ba535697ca"}, + {file = "asgi-lifespan-2.1.0.tar.gz", hash = "sha256:5e2effaf0bfe39829cf2d64e7ecc47c7d86d676a6599f7afba378c31f5e3a308"}, + {file = "asgi_lifespan-2.1.0-py3-none-any.whl", hash = "sha256:ed840706680e28428c01e14afb3875d7d76d3206f3d5b2f2294e059b5c23804f"}, ] [package.dependencies] -aiohttp = ">=3.1.0,<4.dev0" -pytest-asyncio = "*" +sniffio = "*" [[package]] name = "attrs" @@ -359,22 +247,16 @@ files = [ bitarray = ">=2.8.0,<3.0.0" [[package]] -name = "brewblox-service" -version = "3.3.2" -description = "Scaffolding for Brewblox backend services" +name = "certifi" +version = "2023.11.17" +description = "Python package for providing Mozilla's CA Bundle." optional = false -python-versions = ">=3.9,<4" +python-versions = ">=3.6" files = [ - {file = "brewblox_service-3.3.2-py3-none-any.whl", hash = "sha256:2b0bb621956a61e7846fa30d8005df8428416a0e3fe1ae2729f081d36bf7d8e3"}, - {file = "brewblox_service-3.3.2.tar.gz", hash = "sha256:0bf13ecf42729978cb9e4b2b3f5b4c5b2623bdc1aab16581fa6ca299ca6b4eac"}, + {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, + {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, ] -[package.dependencies] -aiohttp = "==3.*" -aiohttp-pydantic = "==1.*" -aiomqtt = "==1.*" -pydantic = "==1.*" - [[package]] name = "cffi" version = "1.16.0" @@ -502,6 +384,20 @@ files = [ {file = "ciso8601-2.3.1.tar.gz", hash = "sha256:3212c7ffe5d8080270548b5f2692ffd2039683b6628a8d2ad456122cc5793c4c"}, ] +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "colorama" version = "0.4.6" @@ -649,6 +545,25 @@ files = [ {file = "debugpy-1.8.0.zip", hash = "sha256:12af2c55b419521e33d5fb21bd022df0b5eb267c3e178f1d374a63a2a6bdccd0"}, ] +[[package]] +name = "dnspython" +version = "2.4.2" +description = "DNS toolkit" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "dnspython-2.4.2-py3-none-any.whl", hash = "sha256:57c6fbaaeaaf39c891292012060beb141791735dbb4004798328fc2c467402d8"}, + {file = "dnspython-2.4.2.tar.gz", hash = "sha256:8dcfae8c7460a2f84b4072e26f1c9f4101ca20c071649cb7c34e8b6a93d58984"}, +] + +[package.extras] +dnssec = ["cryptography (>=2.6,<42.0)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=0.17.3)", "httpx (>=0.24.1)"] +doq = ["aioquic (>=0.9.20)"] +idna = ["idna (>=2.1,<4.0)"] +trio = ["trio (>=0.14,<0.23)"] +wmi = ["wmi (>=1.5.1,<2.0.0)"] + [[package]] name = "ecdsa" version = "0.18.0" @@ -689,6 +604,43 @@ reedsolo = ">=1.5.3,<1.8" dev = ["black", "coverage (>=6.0,<7.0)", "flake8 (>=3.2.0)", "flake8-gl-codeclimate", "flake8-import-order", "pre-commit", "pyelftools", "pytest", "pytest-rerunfailures"] hsm = ["python-pkcs11"] +[[package]] +name = "fastapi" +version = "0.104.1" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi-0.104.1-py3-none-any.whl", hash = "sha256:752dc31160cdbd0436bb93bad51560b57e525cbb1d4bbf6f4904ceee75548241"}, + {file = "fastapi-0.104.1.tar.gz", hash = "sha256:e5e4540a7c5e1dcfbbcf5b903c234feddcdcd881f191977a1c5dfd917487e7ae"}, +] + +[package.dependencies] +anyio = ">=3.7.1,<4.0.0" +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +starlette = ">=0.27.0,<0.28.0" +typing-extensions = ">=4.8.0" + +[package.extras] +all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "fastapi-mqtt" +version = "2.0.0" +description = "fastapi-mqtt is extension for MQTT protocol" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "fastapi_mqtt-2.0.0-py3-none-any.whl", hash = "sha256:99fa73e69476b67c4fa039ca4f66d0901d1898d75ccfafcccf44d56d1010f4dc"}, + {file = "fastapi_mqtt-2.0.0.tar.gz", hash = "sha256:13cfe3d7a8fbcb97b5db65263a0f09ac6e755198c811c7d39884dae1dc935955"}, +] + +[package.dependencies] +fastapi = ">=0.103" +gmqtt = ">=0.6.11" +pydantic = ">=2.3.0" +uvicorn = ">=0.19.0" + [[package]] name = "flake8" version = "6.1.0" @@ -705,6 +657,22 @@ mccabe = ">=0.7.0,<0.8.0" pycodestyle = ">=2.11.0,<2.12.0" pyflakes = ">=3.1.0,<3.2.0" +[[package]] +name = "flake8-pyproject" +version = "1.2.3" +description = "Flake8 plug-in loading the configuration from pyproject.toml" +optional = false +python-versions = ">= 3.6" +files = [ + {file = "flake8_pyproject-1.2.3-py3-none-any.whl", hash = "sha256:6249fe53545205af5e76837644dc80b4c10037e73a0e5db87ff562d75fb5bd4a"}, +] + +[package.dependencies] +Flake8 = ">=5" + +[package.extras] +dev = ["pyTest", "pyTest-cov"] + [[package]] name = "flake8-quotes" version = "3.3.2" @@ -719,75 +687,19 @@ files = [ flake8 = "*" [[package]] -name = "frozenlist" -version = "1.4.0" -description = "A list-like structure which implements collections.abc.MutableSequence" +name = "gmqtt" +version = "0.6.12" +description = "Client for MQTT protocol" optional = false -python-versions = ">=3.8" +python-versions = ">=3.5" files = [ - {file = "frozenlist-1.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:764226ceef3125e53ea2cb275000e309c0aa5464d43bd72abd661e27fffc26ab"}, - {file = "frozenlist-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d6484756b12f40003c6128bfcc3fa9f0d49a687e171186c2d85ec82e3758c559"}, - {file = "frozenlist-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9ac08e601308e41eb533f232dbf6b7e4cea762f9f84f6357136eed926c15d12c"}, - {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d081f13b095d74b67d550de04df1c756831f3b83dc9881c38985834387487f1b"}, - {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71932b597f9895f011f47f17d6428252fc728ba2ae6024e13c3398a087c2cdea"}, - {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:981b9ab5a0a3178ff413bca62526bb784249421c24ad7381e39d67981be2c326"}, - {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e41f3de4df3e80de75845d3e743b3f1c4c8613c3997a912dbf0229fc61a8b963"}, - {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6918d49b1f90821e93069682c06ffde41829c346c66b721e65a5c62b4bab0300"}, - {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e5c8764c7829343d919cc2dfc587a8db01c4f70a4ebbc49abde5d4b158b007b"}, - {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8d0edd6b1c7fb94922bf569c9b092ee187a83f03fb1a63076e7774b60f9481a8"}, - {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e29cda763f752553fa14c68fb2195150bfab22b352572cb36c43c47bedba70eb"}, - {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:0c7c1b47859ee2cac3846fde1c1dc0f15da6cec5a0e5c72d101e0f83dcb67ff9"}, - {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:901289d524fdd571be1c7be054f48b1f88ce8dddcbdf1ec698b27d4b8b9e5d62"}, - {file = "frozenlist-1.4.0-cp310-cp310-win32.whl", hash = "sha256:1a0848b52815006ea6596c395f87449f693dc419061cc21e970f139d466dc0a0"}, - {file = "frozenlist-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:b206646d176a007466358aa21d85cd8600a415c67c9bd15403336c331a10d956"}, - {file = "frozenlist-1.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:de343e75f40e972bae1ef6090267f8260c1446a1695e77096db6cfa25e759a95"}, - {file = "frozenlist-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad2a9eb6d9839ae241701d0918f54c51365a51407fd80f6b8289e2dfca977cc3"}, - {file = "frozenlist-1.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bd7bd3b3830247580de99c99ea2a01416dfc3c34471ca1298bccabf86d0ff4dc"}, - {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdf1847068c362f16b353163391210269e4f0569a3c166bc6a9f74ccbfc7e839"}, - {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38461d02d66de17455072c9ba981d35f1d2a73024bee7790ac2f9e361ef1cd0c"}, - {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5a32087d720c608f42caed0ef36d2b3ea61a9d09ee59a5142d6070da9041b8f"}, - {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd65632acaf0d47608190a71bfe46b209719bf2beb59507db08ccdbe712f969b"}, - {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261b9f5d17cac914531331ff1b1d452125bf5daa05faf73b71d935485b0c510b"}, - {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b89ac9768b82205936771f8d2eb3ce88503b1556324c9f903e7156669f521472"}, - {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:008eb8b31b3ea6896da16c38c1b136cb9fec9e249e77f6211d479db79a4eaf01"}, - {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e74b0506fa5aa5598ac6a975a12aa8928cbb58e1f5ac8360792ef15de1aa848f"}, - {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:490132667476f6781b4c9458298b0c1cddf237488abd228b0b3650e5ecba7467"}, - {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:76d4711f6f6d08551a7e9ef28c722f4a50dd0fc204c56b4bcd95c6cc05ce6fbb"}, - {file = "frozenlist-1.4.0-cp311-cp311-win32.whl", hash = "sha256:a02eb8ab2b8f200179b5f62b59757685ae9987996ae549ccf30f983f40602431"}, - {file = "frozenlist-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:515e1abc578dd3b275d6a5114030b1330ba044ffba03f94091842852f806f1c1"}, - {file = "frozenlist-1.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f0ed05f5079c708fe74bf9027e95125334b6978bf07fd5ab923e9e55e5fbb9d3"}, - {file = "frozenlist-1.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ca265542ca427bf97aed183c1676e2a9c66942e822b14dc6e5f42e038f92a503"}, - {file = "frozenlist-1.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:491e014f5c43656da08958808588cc6c016847b4360e327a62cb308c791bd2d9"}, - {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17ae5cd0f333f94f2e03aaf140bb762c64783935cc764ff9c82dff626089bebf"}, - {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e78fb68cf9c1a6aa4a9a12e960a5c9dfbdb89b3695197aa7064705662515de2"}, - {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5655a942f5f5d2c9ed93d72148226d75369b4f6952680211972a33e59b1dfdc"}, - {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c11b0746f5d946fecf750428a95f3e9ebe792c1ee3b1e96eeba145dc631a9672"}, - {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e66d2a64d44d50d2543405fb183a21f76b3b5fd16f130f5c99187c3fb4e64919"}, - {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:88f7bc0fcca81f985f78dd0fa68d2c75abf8272b1f5c323ea4a01a4d7a614efc"}, - {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5833593c25ac59ede40ed4de6d67eb42928cca97f26feea219f21d0ed0959b79"}, - {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:fec520865f42e5c7f050c2a79038897b1c7d1595e907a9e08e3353293ffc948e"}, - {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:b826d97e4276750beca7c8f0f1a4938892697a6bcd8ec8217b3312dad6982781"}, - {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ceb6ec0a10c65540421e20ebd29083c50e6d1143278746a4ef6bcf6153171eb8"}, - {file = "frozenlist-1.4.0-cp38-cp38-win32.whl", hash = "sha256:2b8bcf994563466db019fab287ff390fffbfdb4f905fc77bc1c1d604b1c689cc"}, - {file = "frozenlist-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:a6c8097e01886188e5be3e6b14e94ab365f384736aa1fca6a0b9e35bd4a30bc7"}, - {file = "frozenlist-1.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6c38721585f285203e4b4132a352eb3daa19121a035f3182e08e437cface44bf"}, - {file = "frozenlist-1.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a0c6da9aee33ff0b1a451e867da0c1f47408112b3391dd43133838339e410963"}, - {file = "frozenlist-1.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:93ea75c050c5bb3d98016b4ba2497851eadf0ac154d88a67d7a6816206f6fa7f"}, - {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f61e2dc5ad442c52b4887f1fdc112f97caeff4d9e6ebe78879364ac59f1663e1"}, - {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa384489fefeb62321b238e64c07ef48398fe80f9e1e6afeff22e140e0850eef"}, - {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10ff5faaa22786315ef57097a279b833ecab1a0bfb07d604c9cbb1c4cdc2ed87"}, - {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:007df07a6e3eb3e33e9a1fe6a9db7af152bbd8a185f9aaa6ece10a3529e3e1c6"}, - {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f4f399d28478d1f604c2ff9119907af9726aed73680e5ed1ca634d377abb087"}, - {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c5374b80521d3d3f2ec5572e05adc94601985cc526fb276d0c8574a6d749f1b3"}, - {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ce31ae3e19f3c902de379cf1323d90c649425b86de7bbdf82871b8a2a0615f3d"}, - {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7211ef110a9194b6042449431e08c4d80c0481e5891e58d429df5899690511c2"}, - {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:556de4430ce324c836789fa4560ca62d1591d2538b8ceb0b4f68fb7b2384a27a"}, - {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7645a8e814a3ee34a89c4a372011dcd817964ce8cb273c8ed6119d706e9613e3"}, - {file = "frozenlist-1.4.0-cp39-cp39-win32.whl", hash = "sha256:19488c57c12d4e8095a922f328df3f179c820c212940a498623ed39160bc3c2f"}, - {file = "frozenlist-1.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:6221d84d463fb110bdd7619b69cb43878a11d51cbb9394ae3105d082d5199167"}, - {file = "frozenlist-1.4.0.tar.gz", hash = "sha256:09163bdf0b2907454042edb19f887c6d33806adc71fbd54afc14908bfdc22251"}, + {file = "gmqtt-0.6.12-py3-none-any.whl", hash = "sha256:e8acad561d52734ae622c543042836fe7a9bd20a3894378794ec763be4901a39"}, + {file = "gmqtt-0.6.12.tar.gz", hash = "sha256:7df03792343089ae62dc7cd6f8be356861c4fc68768cefa22f3d8de5e7e5be48"}, ] +[package.extras] +test = ["atomicwrites (>=1.3.0)", "attrs (>=19.1.0)", "codecov (>=2.0.15)", "coverage (>=4.5.3)", "more-itertools (>=7.0.0)", "pluggy (>=0.11.0)", "py (>=1.8.0)", "pytest (>=5.4.0)", "pytest-asyncio (>=0.12.0)", "pytest-cov (>=2.7.1)", "six (>=1.12.0)", "uvloop (>=0.14.0)"] + [[package]] name = "grpcio" version = "1.59.3" @@ -922,6 +834,127 @@ grpcio = ">=1.59.3" protobuf = ">=4.21.6,<5.0dev" setuptools = "*" +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httpcore" +version = "1.0.2" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpcore-1.0.2-py3-none-any.whl", hash = "sha256:096cc05bca73b8e459a1fc3dcf585148f63e534eae4339559c9b8a8d6399acc7"}, + {file = "httpcore-1.0.2.tar.gz", hash = "sha256:9fc092e4799b26174648e54b74ed5f683132a464e95643b226e00c2ed2fa6535"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.13,<0.15" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<0.23.0)"] + +[[package]] +name = "httptools" +version = "0.6.1" +description = "A collection of framework independent HTTP protocol utils." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"}, + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"}, + {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"}, + {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"}, + {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"}, + {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"}, + {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"}, + {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"}, +] + +[package.extras] +test = ["Cython (>=0.29.24,<0.30.0)"] + +[[package]] +name = "httpx" +version = "0.25.2" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-0.25.2-py3-none-any.whl", hash = "sha256:a05d3d052d9b2dfce0e3896636467f8a5342fb2b902c819428e1ac65413ca118"}, + {file = "httpx-0.25.2.tar.gz", hash = "sha256:8b8fcaa0c8ea7b05edd69a094e63a2094c4efcb48129fb757361bc423c0ad9e8"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[[package]] +name = "httpx-ws" +version = "0.4.3" +description = "WebSockets support for HTTPX" +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx_ws-0.4.3-py3-none-any.whl", hash = "sha256:ce0485f3c66565fac4a6884e7db6dd002861512462e80a644d86d66f8321187a"}, + {file = "httpx_ws-0.4.3.tar.gz", hash = "sha256:bb0997ec4a3ff5f9d88631c0dc196ebbd7cee669b441b9f93221eefc4ccc7dd6"}, +] + +[package.dependencies] +anyio = "*" +httpcore = ">=0.17.3" +httpx = ">=0.23.1" +wsproto = "*" + [[package]] name = "idna" version = "3.4" @@ -955,92 +988,6 @@ files = [ {file = "invoke-2.2.0.tar.gz", hash = "sha256:ee6cbb101af1a859c7fe84f2a264c059020b0cb7fe3535f9424300ab568f6bd5"}, ] -[[package]] -name = "jinja2" -version = "3.1.2" -description = "A very fast and expressive template engine." -optional = false -python-versions = ">=3.7" -files = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, -] - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "markupsafe" -version = "2.1.3" -description = "Safely add untrusted strings to HTML/XML markup." -optional = false -python-versions = ">=3.7" -files = [ - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, - {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, -] - [[package]] name = "mccabe" version = "0.7.0" @@ -1052,89 +999,6 @@ files = [ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] -[[package]] -name = "multidict" -version = "6.0.4" -description = "multidict implementation" -optional = false -python-versions = ">=3.7" -files = [ - {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, - {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, - {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, - {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, - {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, - {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, - {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, - {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, - {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, - {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, - {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, - {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, - {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, - {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, - {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, -] - [[package]] name = "netifaces" version = "0.11.0" @@ -1185,19 +1049,6 @@ files = [ {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] -[[package]] -name = "paho-mqtt" -version = "1.6.1" -description = "MQTT version 5.0/3.1.1 client class" -optional = false -python-versions = "*" -files = [ - {file = "paho-mqtt-1.6.1.tar.gz", hash = "sha256:2a8291c81623aec00372b5a85558a372c747cbca8e9934dfe218638b8eefc26f"}, -] - -[package.extras] -proxy = ["PySocks"] - [[package]] name = "pint" version = "0.22" @@ -1281,55 +1132,154 @@ files = [ [[package]] name = "pydantic" -version = "1.10.13" -description = "Data validation and settings management using python type hints" +version = "2.5.2" +description = "Data validation using Python type hints" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-1.10.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:efff03cc7a4f29d9009d1c96ceb1e7a70a65cfe86e89d34e4a5f2ab1e5693737"}, - {file = "pydantic-1.10.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ecea2b9d80e5333303eeb77e180b90e95eea8f765d08c3d278cd56b00345d01"}, - {file = "pydantic-1.10.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1740068fd8e2ef6eb27a20e5651df000978edce6da6803c2bef0bc74540f9548"}, - {file = "pydantic-1.10.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84bafe2e60b5e78bc64a2941b4c071a4b7404c5c907f5f5a99b0139781e69ed8"}, - {file = "pydantic-1.10.13-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bc0898c12f8e9c97f6cd44c0ed70d55749eaf783716896960b4ecce2edfd2d69"}, - {file = "pydantic-1.10.13-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:654db58ae399fe6434e55325a2c3e959836bd17a6f6a0b6ca8107ea0571d2e17"}, - {file = "pydantic-1.10.13-cp310-cp310-win_amd64.whl", hash = "sha256:75ac15385a3534d887a99c713aa3da88a30fbd6204a5cd0dc4dab3d770b9bd2f"}, - {file = "pydantic-1.10.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c553f6a156deb868ba38a23cf0df886c63492e9257f60a79c0fd8e7173537653"}, - {file = "pydantic-1.10.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5e08865bc6464df8c7d61439ef4439829e3ab62ab1669cddea8dd00cd74b9ffe"}, - {file = "pydantic-1.10.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e31647d85a2013d926ce60b84f9dd5300d44535a9941fe825dc349ae1f760df9"}, - {file = "pydantic-1.10.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:210ce042e8f6f7c01168b2d84d4c9eb2b009fe7bf572c2266e235edf14bacd80"}, - {file = "pydantic-1.10.13-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8ae5dd6b721459bfa30805f4c25880e0dd78fc5b5879f9f7a692196ddcb5a580"}, - {file = "pydantic-1.10.13-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f8e81fc5fb17dae698f52bdd1c4f18b6ca674d7068242b2aff075f588301bbb0"}, - {file = "pydantic-1.10.13-cp311-cp311-win_amd64.whl", hash = "sha256:61d9dce220447fb74f45e73d7ff3b530e25db30192ad8d425166d43c5deb6df0"}, - {file = "pydantic-1.10.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4b03e42ec20286f052490423682016fd80fda830d8e4119f8ab13ec7464c0132"}, - {file = "pydantic-1.10.13-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f59ef915cac80275245824e9d771ee939133be38215555e9dc90c6cb148aaeb5"}, - {file = "pydantic-1.10.13-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a1f9f747851338933942db7af7b6ee8268568ef2ed86c4185c6ef4402e80ba8"}, - {file = "pydantic-1.10.13-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:97cce3ae7341f7620a0ba5ef6cf043975cd9d2b81f3aa5f4ea37928269bc1b87"}, - {file = "pydantic-1.10.13-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:854223752ba81e3abf663d685f105c64150873cc6f5d0c01d3e3220bcff7d36f"}, - {file = "pydantic-1.10.13-cp37-cp37m-win_amd64.whl", hash = "sha256:b97c1fac8c49be29486df85968682b0afa77e1b809aff74b83081cc115e52f33"}, - {file = "pydantic-1.10.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c958d053453a1c4b1c2062b05cd42d9d5c8eb67537b8d5a7e3c3032943ecd261"}, - {file = "pydantic-1.10.13-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4c5370a7edaac06daee3af1c8b1192e305bc102abcbf2a92374b5bc793818599"}, - {file = "pydantic-1.10.13-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d6f6e7305244bddb4414ba7094ce910560c907bdfa3501e9db1a7fd7eaea127"}, - {file = "pydantic-1.10.13-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3a3c792a58e1622667a2837512099eac62490cdfd63bd407993aaf200a4cf1f"}, - {file = "pydantic-1.10.13-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c636925f38b8db208e09d344c7aa4f29a86bb9947495dd6b6d376ad10334fb78"}, - {file = "pydantic-1.10.13-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:678bcf5591b63cc917100dc50ab6caebe597ac67e8c9ccb75e698f66038ea953"}, - {file = "pydantic-1.10.13-cp38-cp38-win_amd64.whl", hash = "sha256:6cf25c1a65c27923a17b3da28a0bdb99f62ee04230c931d83e888012851f4e7f"}, - {file = "pydantic-1.10.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8ef467901d7a41fa0ca6db9ae3ec0021e3f657ce2c208e98cd511f3161c762c6"}, - {file = "pydantic-1.10.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:968ac42970f57b8344ee08837b62f6ee6f53c33f603547a55571c954a4225691"}, - {file = "pydantic-1.10.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9849f031cf8a2f0a928fe885e5a04b08006d6d41876b8bbd2fc68a18f9f2e3fd"}, - {file = "pydantic-1.10.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56e3ff861c3b9c6857579de282ce8baabf443f42ffba355bf070770ed63e11e1"}, - {file = "pydantic-1.10.13-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f00790179497767aae6bcdc36355792c79e7bbb20b145ff449700eb076c5f96"}, - {file = "pydantic-1.10.13-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:75b297827b59bc229cac1a23a2f7a4ac0031068e5be0ce385be1462e7e17a35d"}, - {file = "pydantic-1.10.13-cp39-cp39-win_amd64.whl", hash = "sha256:e70ca129d2053fb8b728ee7d1af8e553a928d7e301a311094b8a0501adc8763d"}, - {file = "pydantic-1.10.13-py3-none-any.whl", hash = "sha256:b87326822e71bd5f313e7d3bfdc77ac3247035ac10b0c0618bd99dcf95b1e687"}, - {file = "pydantic-1.10.13.tar.gz", hash = "sha256:32c8b48dcd3b2ac4e78b0ba4af3a2c2eb6048cb75202f0ea7b34feb740efc340"}, + {file = "pydantic-2.5.2-py3-none-any.whl", hash = "sha256:80c50fb8e3dcecfddae1adbcc00ec5822918490c99ab31f6cf6140ca1c1429f0"}, + {file = "pydantic-2.5.2.tar.gz", hash = "sha256:ff177ba64c6faf73d7afa2e8cad38fd456c0dbe01c9954e71038001cd15a6edd"}, ] [package.dependencies] -typing-extensions = ">=4.2.0" +annotated-types = ">=0.4.0" +pydantic-core = "2.14.5" +typing-extensions = ">=4.6.1" [package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.14.5" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic_core-2.14.5-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:7e88f5696153dc516ba6e79f82cc4747e87027205f0e02390c21f7cb3bd8abfd"}, + {file = "pydantic_core-2.14.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4641e8ad4efb697f38a9b64ca0523b557c7931c5f84e0fd377a9a3b05121f0de"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:774de879d212db5ce02dfbf5b0da9a0ea386aeba12b0b95674a4ce0593df3d07"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ebb4e035e28f49b6f1a7032920bb9a0c064aedbbabe52c543343d39341a5b2a3"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b53e9ad053cd064f7e473a5f29b37fc4cc9dc6d35f341e6afc0155ea257fc911"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aa1768c151cf562a9992462239dfc356b3d1037cc5a3ac829bb7f3bda7cc1f9"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eac5c82fc632c599f4639a5886f96867ffced74458c7db61bc9a66ccb8ee3113"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae91f50ccc5810b2f1b6b858257c9ad2e08da70bf890dee02de1775a387c66"}, + {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6b9ff467ffbab9110e80e8c8de3bcfce8e8b0fd5661ac44a09ae5901668ba997"}, + {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:61ea96a78378e3bd5a0be99b0e5ed00057b71f66115f5404d0dae4819f495093"}, + {file = "pydantic_core-2.14.5-cp310-none-win32.whl", hash = "sha256:bb4c2eda937a5e74c38a41b33d8c77220380a388d689bcdb9b187cf6224c9720"}, + {file = "pydantic_core-2.14.5-cp310-none-win_amd64.whl", hash = "sha256:b7851992faf25eac90bfcb7bfd19e1f5ffa00afd57daec8a0042e63c74a4551b"}, + {file = "pydantic_core-2.14.5-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:4e40f2bd0d57dac3feb3a3aed50f17d83436c9e6b09b16af271b6230a2915459"}, + {file = "pydantic_core-2.14.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab1cdb0f14dc161ebc268c09db04d2c9e6f70027f3b42446fa11c153521c0e88"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aae7ea3a1c5bb40c93cad361b3e869b180ac174656120c42b9fadebf685d121b"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60b7607753ba62cf0739177913b858140f11b8af72f22860c28eabb2f0a61937"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2248485b0322c75aee7565d95ad0e16f1c67403a470d02f94da7344184be770f"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:823fcc638f67035137a5cd3f1584a4542d35a951c3cc68c6ead1df7dac825c26"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96581cfefa9123accc465a5fd0cc833ac4d75d55cc30b633b402e00e7ced00a6"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a33324437018bf6ba1bb0f921788788641439e0ed654b233285b9c69704c27b4"}, + {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9bd18fee0923ca10f9a3ff67d4851c9d3e22b7bc63d1eddc12f439f436f2aada"}, + {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:853a2295c00f1d4429db4c0fb9475958543ee80cfd310814b5c0ef502de24dda"}, + {file = "pydantic_core-2.14.5-cp311-none-win32.whl", hash = "sha256:cb774298da62aea5c80a89bd58c40205ab4c2abf4834453b5de207d59d2e1651"}, + {file = "pydantic_core-2.14.5-cp311-none-win_amd64.whl", hash = "sha256:e87fc540c6cac7f29ede02e0f989d4233f88ad439c5cdee56f693cc9c1c78077"}, + {file = "pydantic_core-2.14.5-cp311-none-win_arm64.whl", hash = "sha256:57d52fa717ff445cb0a5ab5237db502e6be50809b43a596fb569630c665abddf"}, + {file = "pydantic_core-2.14.5-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:e60f112ac88db9261ad3a52032ea46388378034f3279c643499edb982536a093"}, + {file = "pydantic_core-2.14.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6e227c40c02fd873c2a73a98c1280c10315cbebe26734c196ef4514776120aeb"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0cbc7fff06a90bbd875cc201f94ef0ee3929dfbd5c55a06674b60857b8b85ed"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:103ef8d5b58596a731b690112819501ba1db7a36f4ee99f7892c40da02c3e189"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c949f04ecad823f81b1ba94e7d189d9dfb81edbb94ed3f8acfce41e682e48cef"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1452a1acdf914d194159439eb21e56b89aa903f2e1c65c60b9d874f9b950e5d"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb4679d4c2b089e5ef89756bc73e1926745e995d76e11925e3e96a76d5fa51fc"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf9d3fe53b1ee360e2421be95e62ca9b3296bf3f2fb2d3b83ca49ad3f925835e"}, + {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:70f4b4851dbb500129681d04cc955be2a90b2248d69273a787dda120d5cf1f69"}, + {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:59986de5710ad9613ff61dd9b02bdd2f615f1a7052304b79cc8fa2eb4e336d2d"}, + {file = "pydantic_core-2.14.5-cp312-none-win32.whl", hash = "sha256:699156034181e2ce106c89ddb4b6504c30db8caa86e0c30de47b3e0654543260"}, + {file = "pydantic_core-2.14.5-cp312-none-win_amd64.whl", hash = "sha256:5baab5455c7a538ac7e8bf1feec4278a66436197592a9bed538160a2e7d11e36"}, + {file = "pydantic_core-2.14.5-cp312-none-win_arm64.whl", hash = "sha256:e47e9a08bcc04d20975b6434cc50bf82665fbc751bcce739d04a3120428f3e27"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:af36f36538418f3806048f3b242a1777e2540ff9efaa667c27da63d2749dbce0"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:45e95333b8418ded64745f14574aa9bfc212cb4fbeed7a687b0c6e53b5e188cd"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e47a76848f92529879ecfc417ff88a2806438f57be4a6a8bf2961e8f9ca9ec7"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d81e6987b27bc7d101c8597e1cd2bcaa2fee5e8e0f356735c7ed34368c471550"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34708cc82c330e303f4ce87758828ef6e457681b58ce0e921b6e97937dd1e2a3"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:652c1988019752138b974c28f43751528116bcceadad85f33a258869e641d753"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e4d090e73e0725b2904fdbdd8d73b8802ddd691ef9254577b708d413bf3006e"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5c7d5b5005f177764e96bd584d7bf28d6e26e96f2a541fdddb934c486e36fd59"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a71891847f0a73b1b9eb86d089baee301477abef45f7eaf303495cd1473613e4"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a717aef6971208f0851a2420b075338e33083111d92041157bbe0e2713b37325"}, + {file = "pydantic_core-2.14.5-cp37-none-win32.whl", hash = "sha256:de790a3b5aa2124b8b78ae5faa033937a72da8efe74b9231698b5a1dd9be3405"}, + {file = "pydantic_core-2.14.5-cp37-none-win_amd64.whl", hash = "sha256:6c327e9cd849b564b234da821236e6bcbe4f359a42ee05050dc79d8ed2a91588"}, + {file = "pydantic_core-2.14.5-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:ef98ca7d5995a82f43ec0ab39c4caf6a9b994cb0b53648ff61716370eadc43cf"}, + {file = "pydantic_core-2.14.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6eae413494a1c3f89055da7a5515f32e05ebc1a234c27674a6956755fb2236f"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcf4e6d85614f7a4956c2de5a56531f44efb973d2fe4a444d7251df5d5c4dcfd"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6637560562134b0e17de333d18e69e312e0458ee4455bdad12c37100b7cad706"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77fa384d8e118b3077cccfcaf91bf83c31fe4dc850b5e6ee3dc14dc3d61bdba1"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16e29bad40bcf97aac682a58861249ca9dcc57c3f6be22f506501833ddb8939c"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531f4b4252fac6ca476fbe0e6f60f16f5b65d3e6b583bc4d87645e4e5ddde331"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:074f3d86f081ce61414d2dc44901f4f83617329c6f3ab49d2bc6c96948b2c26b"}, + {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c2adbe22ab4babbca99c75c5d07aaf74f43c3195384ec07ccbd2f9e3bddaecec"}, + {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0f6116a558fd06d1b7c2902d1c4cf64a5bd49d67c3540e61eccca93f41418124"}, + {file = "pydantic_core-2.14.5-cp38-none-win32.whl", hash = "sha256:fe0a5a1025eb797752136ac8b4fa21aa891e3d74fd340f864ff982d649691867"}, + {file = "pydantic_core-2.14.5-cp38-none-win_amd64.whl", hash = "sha256:079206491c435b60778cf2b0ee5fd645e61ffd6e70c47806c9ed51fc75af078d"}, + {file = "pydantic_core-2.14.5-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:a6a16f4a527aae4f49c875da3cdc9508ac7eef26e7977952608610104244e1b7"}, + {file = "pydantic_core-2.14.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:abf058be9517dc877227ec3223f0300034bd0e9f53aebd63cf4456c8cb1e0863"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49b08aae5013640a3bfa25a8eebbd95638ec3f4b2eaf6ed82cf0c7047133f03b"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2d97e906b4ff36eb464d52a3bc7d720bd6261f64bc4bcdbcd2c557c02081ed2"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3128e0bbc8c091ec4375a1828d6118bc20404883169ac95ffa8d983b293611e6"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88e74ab0cdd84ad0614e2750f903bb0d610cc8af2cc17f72c28163acfcf372a4"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c339dabd8ee15f8259ee0f202679b6324926e5bc9e9a40bf981ce77c038553db"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3387277f1bf659caf1724e1afe8ee7dbc9952a82d90f858ebb931880216ea955"}, + {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ba6b6b3846cfc10fdb4c971980a954e49d447cd215ed5a77ec8190bc93dd7bc5"}, + {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ca61d858e4107ce5e1330a74724fe757fc7135190eb5ce5c9d0191729f033209"}, + {file = "pydantic_core-2.14.5-cp39-none-win32.whl", hash = "sha256:ec1e72d6412f7126eb7b2e3bfca42b15e6e389e1bc88ea0069d0cc1742f477c6"}, + {file = "pydantic_core-2.14.5-cp39-none-win_amd64.whl", hash = "sha256:c0b97ec434041827935044bbbe52b03d6018c2897349670ff8fe11ed24d1d4ab"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:79e0a2cdbdc7af3f4aee3210b1172ab53d7ddb6a2d8c24119b5706e622b346d0"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:678265f7b14e138d9a541ddabbe033012a2953315739f8cfa6d754cc8063e8ca"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b15e855ae44f0c6341ceb74df61b606e11f1087e87dcb7482377374aac6abe"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09b0e985fbaf13e6b06a56d21694d12ebca6ce5414b9211edf6f17738d82b0f8"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3ad873900297bb36e4b6b3f7029d88ff9829ecdc15d5cf20161775ce12306f8a"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2d0ae0d8670164e10accbeb31d5ad45adb71292032d0fdb9079912907f0085f4"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d37f8ec982ead9ba0a22a996129594938138a1503237b87318392a48882d50b7"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:35613015f0ba7e14c29ac6c2483a657ec740e5ac5758d993fdd5870b07a61d8b"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab4ea451082e684198636565224bbb179575efc1658c48281b2c866bfd4ddf04"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ce601907e99ea5b4adb807ded3570ea62186b17f88e271569144e8cca4409c7"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb2ed8b3fe4bf4506d6dab3b93b83bbc22237e230cba03866d561c3577517d18"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:70f947628e074bb2526ba1b151cee10e4c3b9670af4dbb4d73bc8a89445916b5"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4bc536201426451f06f044dfbf341c09f540b4ebdb9fd8d2c6164d733de5e634"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4791cf0f8c3104ac668797d8c514afb3431bc3305f5638add0ba1a5a37e0d88"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:038c9f763e650712b899f983076ce783175397c848da04985658e7628cbe873b"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:27548e16c79702f1e03f5628589c6057c9ae17c95b4c449de3c66b589ead0520"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97bee68898f3f4344eb02fec316db93d9700fb1e6a5b760ffa20d71d9a46ce3"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9b759b77f5337b4ea024f03abc6464c9f35d9718de01cfe6bae9f2e139c397e"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:439c9afe34638ace43a49bf72d201e0ffc1a800295bed8420c2a9ca8d5e3dbb3"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ba39688799094c75ea8a16a6b544eb57b5b0f3328697084f3f2790892510d144"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ccd4d5702bb90b84df13bd491be8d900b92016c5a455b7e14630ad7449eb03f8"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:81982d78a45d1e5396819bbb4ece1fadfe5f079335dd28c4ab3427cd95389944"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:7f8210297b04e53bc3da35db08b7302a6a1f4889c79173af69b72ec9754796b8"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:8c8a8812fe6f43a3a5b054af6ac2d7b8605c7bcab2804a8a7d68b53f3cd86e00"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:206ed23aecd67c71daf5c02c3cd19c0501b01ef3cbf7782db9e4e051426b3d0d"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2027d05c8aebe61d898d4cffd774840a9cb82ed356ba47a90d99ad768f39789"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40180930807ce806aa71eda5a5a5447abb6b6a3c0b4b3b1b1962651906484d68"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:615a0a4bff11c45eb3c1996ceed5bdaa2f7b432425253a7c2eed33bb86d80abc"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5e412d717366e0677ef767eac93566582518fe8be923361a5c204c1a62eaafe"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:513b07e99c0a267b1d954243845d8a833758a6726a3b5d8948306e3fe14675e3"}, + {file = "pydantic_core-2.14.5.tar.gz", hash = "sha256:6d30226dfc816dd0fdf120cae611dd2215117e4f9b124af8c60ab9093b6e8e71"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pydantic-settings" +version = "2.1.0" +description = "Settings management using Pydantic" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_settings-2.1.0-py3-none-any.whl", hash = "sha256:7621c0cb5d90d1140d2f0ef557bdf03573aac7035948109adf2574770b77605a"}, + {file = "pydantic_settings-2.1.0.tar.gz", hash = "sha256:26b1492e0a24755626ac5e6d715e9077ab7ad4fb5f19a8b7ed7011d52f36141c"}, +] + +[package.dependencies] +pydantic = ">=2.3.0" +python-dotenv = ">=0.21.0" [[package]] name = "pyflakes" @@ -1390,34 +1340,15 @@ pluggy = ">=0.12,<2.0" [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] -[[package]] -name = "pytest-aiohttp" -version = "1.0.5" -description = "Pytest plugin for aiohttp support" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-aiohttp-1.0.5.tar.gz", hash = "sha256:880262bc5951e934463b15e3af8bb298f11f7d4d3ebac970aab425aff10a780a"}, - {file = "pytest_aiohttp-1.0.5-py3-none-any.whl", hash = "sha256:63a5360fd2f34dda4ab8e6baee4c5f5be4cd186a403cabd498fced82ac9c561e"}, -] - -[package.dependencies] -aiohttp = ">=3.8.1" -pytest = ">=6.1.0" -pytest-asyncio = ">=0.17.2" - -[package.extras] -testing = ["coverage (==6.2)", "mypy (==0.931)"] - [[package]] name = "pytest-asyncio" -version = "0.21.1" +version = "0.23.2" description = "Pytest support for asyncio" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest-asyncio-0.21.1.tar.gz", hash = "sha256:40a7eae6dded22c7b604986855ea48400ab15b069ae38116e8c01238e9eeb64d"}, - {file = "pytest_asyncio-0.21.1-py3-none-any.whl", hash = "sha256:8666c1c8ac02631d7c51ba282e0c69a8a452b211ffedf2599099845da5c5c37b"}, + {file = "pytest-asyncio-0.23.2.tar.gz", hash = "sha256:c16052382554c7b22d48782ab3438d5b10f8cf7a4bdcae7f0f67f097d95beecc"}, + {file = "pytest_asyncio-0.23.2-py3-none-any.whl", hash = "sha256:ea9021364e32d58f0be43b91c6233fb8d2224ccef2398d6837559e587682808f"}, ] [package.dependencies] @@ -1425,7 +1356,7 @@ pytest = ">=7.0.0" [package.extras] docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] -testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] [[package]] name = "pytest-cov" @@ -1445,6 +1376,43 @@ pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] +[[package]] +name = "pytest-docker" +version = "2.0.1" +description = "Simple pytest fixtures for Docker and Docker Compose based tests" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pytest-docker-2.0.1.tar.gz", hash = "sha256:1c17e9202a566f85ed5ef269fe2815bd4899e90eb639622e5d14277372ca7524"}, + {file = "pytest_docker-2.0.1-py3-none-any.whl", hash = "sha256:7103f97b8c479c826b63d73cfb83383dc1970d35105ed1ce78a722c90c7fe650"}, +] + +[package.dependencies] +attrs = ">=19.2.0" +pytest = ">=4.0,<8.0" + +[package.extras] +docker-compose-v1 = ["docker-compose (>=1.27.3,<2.0)"] +tests = ["pytest-pycodestyle (>=2.0.0,<3.0)", "pytest-pylint (>=0.14.1,<1.0)", "requests (>=2.22.0,<3.0)"] + +[[package]] +name = "pytest-httpx" +version = "0.27.0" +description = "Send responses to httpx." +optional = false +python-versions = ">=3.9" +files = [ + {file = "pytest_httpx-0.27.0-py3-none-any.whl", hash = "sha256:24f6f53d507ab483bea8f89b975a1a111fb613ccab4d86e570be8991776e8bcc"}, + {file = "pytest_httpx-0.27.0.tar.gz", hash = "sha256:a33c4e8df415cc1232b3664869b6a8b8061c4c223335aca0b237cefbc01ba0eb"}, +] + +[package.dependencies] +httpx = "==0.25.*" +pytest = "==7.*" + +[package.extras] +testing = ["pytest-asyncio (==0.21.*)", "pytest-cov (==4.*)"] + [[package]] name = "pytest-mock" version = "3.12.0" @@ -1462,6 +1430,20 @@ pytest = ">=5.0" [package.extras] dev = ["pre-commit", "pytest-asyncio", "tox"] +[[package]] +name = "python-dotenv" +version = "1.0.0" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, + {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + [[package]] name = "pytimeparse" version = "1.1.8" @@ -1571,18 +1553,32 @@ files = [ ] [[package]] -name = "swagger-ui-bundle" -version = "1.1.0" -description = "Swagger UI bundled for usage with Python" +name = "sniffio" +version = "1.3.0" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, +] + +[[package]] +name = "starlette" +version = "0.27.0" +description = "The little ASGI library that shines." optional = false python-versions = ">=3.7" files = [ - {file = "swagger_ui_bundle-1.1.0-py3-none-any.whl", hash = "sha256:f7526f7bb99923e10594c54247265839bec97e96b0438561ac86faf40d40dd57"}, - {file = "swagger_ui_bundle-1.1.0.tar.gz", hash = "sha256:20673c3431c8733d5d1615ecf79d9acf30cff75202acaf21a7d9c7f489714529"}, + {file = "starlette-0.27.0-py3-none-any.whl", hash = "sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91"}, + {file = "starlette-0.27.0.tar.gz", hash = "sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75"}, ] [package.dependencies] -Jinja2 = ">=3.0.0,<4.0.0" +anyio = ">=3.4.0,<5" + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"] [[package]] name = "typing-extensions" @@ -1596,109 +1592,257 @@ files = [ ] [[package]] -name = "yarl" -version = "1.9.3" -description = "Yet another URL library" +name = "uvicorn" +version = "0.24.0.post1" +description = "The lightning-fast ASGI server." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.24.0.post1-py3-none-any.whl", hash = "sha256:7c84fea70c619d4a710153482c0d230929af7bcf76c7bfa6de151f0a3a80121e"}, + {file = "uvicorn-0.24.0.post1.tar.gz", hash = "sha256:09c8e5a79dc466bdf28dead50093957db184de356fcdc48697bad3bde4c2588e"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} +h11 = ">=0.8" +httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} +python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "uvloop" +version = "0.19.0" +description = "Fast implementation of asyncio event loop on top of libuv" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e"}, + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:78ab247f0b5671cc887c31d33f9b3abfb88d2614b84e4303f1a63b46c046c8bd"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:472d61143059c84947aa8bb74eabbace30d577a03a1805b77933d6bd13ddebbd"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45bf4c24c19fb8a50902ae37c5de50da81de4922af65baf760f7c0c42e1088be"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271718e26b3e17906b28b67314c45d19106112067205119dddbd834c2b7ce797"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:34175c9fd2a4bc3adc1380e1261f60306344e3407c20a4d684fd5f3be010fa3d"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e27f100e1ff17f6feeb1f33968bc185bf8ce41ca557deee9d9bbbffeb72030b7"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13dfdf492af0aa0a0edf66807d2b465607d11c4fa48f4a1fd41cbea5b18e8e8b"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e3d4e85ac060e2342ff85e90d0c04157acb210b9ce508e784a944f852a40e67"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca4956c9ab567d87d59d49fa3704cf29e37109ad348f2d5223c9bf761a332e7"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f467a5fd23b4fc43ed86342641f3936a68ded707f4627622fa3f82a120e18256"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:492e2c32c2af3f971473bc22f086513cedfc66a130756145a931a90c3958cb17"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2df95fca285a9f5bfe730e51945ffe2fa71ccbfdde3b0da5772b4ee4f2e770d5"}, + {file = "uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd"}, +] + +[package.extras] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] + +[[package]] +name = "watchfiles" +version = "0.21.0" +description = "Simple, modern and high performance file watching and code reload in python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa"}, + {file = "watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c"}, + {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9"}, + {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9"}, + {file = "watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293"}, + {file = "watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235"}, + {file = "watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7"}, + {file = "watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7"}, + {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0"}, + {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365"}, + {file = "watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400"}, + {file = "watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe"}, + {file = "watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078"}, + {file = "watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a"}, + {file = "watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c"}, + {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235"}, + {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7"}, + {file = "watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3"}, + {file = "watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094"}, + {file = "watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6"}, + {file = "watchfiles-0.21.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:4ea10a29aa5de67de02256a28d1bf53d21322295cb00bd2d57fcd19b850ebd99"}, + {file = "watchfiles-0.21.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:40bca549fdc929b470dd1dbfcb47b3295cb46a6d2c90e50588b0a1b3bd98f429"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9b37a7ba223b2f26122c148bb8d09a9ff312afca998c48c725ff5a0a632145f7"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec8c8900dc5c83650a63dd48c4d1d245343f904c4b64b48798c67a3767d7e165"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ad3fe0a3567c2f0f629d800409cd528cb6251da12e81a1f765e5c5345fd0137"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d353c4cfda586db2a176ce42c88f2fc31ec25e50212650c89fdd0f560ee507b"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:83a696da8922314ff2aec02987eefb03784f473281d740bf9170181829133765"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a03651352fc20975ee2a707cd2d74a386cd303cc688f407296064ad1e6d1562"}, + {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3ad692bc7792be8c32918c699638b660c0de078a6cbe464c46e1340dadb94c19"}, + {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06247538e8253975bdb328e7683f8515ff5ff041f43be6c40bff62d989b7d0b0"}, + {file = "watchfiles-0.21.0-cp38-none-win32.whl", hash = "sha256:9a0aa47f94ea9a0b39dd30850b0adf2e1cd32a8b4f9c7aa443d852aacf9ca214"}, + {file = "watchfiles-0.21.0-cp38-none-win_amd64.whl", hash = "sha256:8d5f400326840934e3507701f9f7269247f7c026d1b6cfd49477d2be0933cfca"}, + {file = "watchfiles-0.21.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:7f762a1a85a12cc3484f77eee7be87b10f8c50b0b787bb02f4e357403cad0c0e"}, + {file = "watchfiles-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6e9be3ef84e2bb9710f3f777accce25556f4a71e15d2b73223788d528fcc2052"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4c48a10d17571d1275701e14a601e36959ffada3add8cdbc9e5061a6e3579a5d"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c889025f59884423428c261f212e04d438de865beda0b1e1babab85ef4c0f01"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66fac0c238ab9a2e72d026b5fb91cb902c146202bbd29a9a1a44e8db7b710b6f"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4a21f71885aa2744719459951819e7bf5a906a6448a6b2bbce8e9cc9f2c8128"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c9198c989f47898b2c22201756f73249de3748e0fc9de44adaf54a8b259cc0c"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f57c4461cd24fda22493109c45b3980863c58a25b8bec885ca8bea6b8d4b28"}, + {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:853853cbf7bf9408b404754b92512ebe3e3a83587503d766d23e6bf83d092ee6"}, + {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d5b1dc0e708fad9f92c296ab2f948af403bf201db8fb2eb4c8179db143732e49"}, + {file = "watchfiles-0.21.0-cp39-none-win32.whl", hash = "sha256:59137c0c6826bd56c710d1d2bda81553b5e6b7c84d5a676747d80caf0409ad94"}, + {file = "watchfiles-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:6cb8fdc044909e2078c248986f2fc76f911f72b51ea4a4fbbf472e01d14faa58"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:08dca260e85ffae975448e344834d765983237ad6dc308231aa16e7933db763e"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:3ccceb50c611c433145502735e0370877cced72a6c70fd2410238bcbc7fe51d8"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57d430f5fb63fea141ab71ca9c064e80de3a20b427ca2febcbfcef70ff0ce895"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd5fad9b9c0dd89904bbdea978ce89a2b692a7ee8a0ce19b940e538c88a809c"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:be6dd5d52b73018b21adc1c5d28ac0c68184a64769052dfeb0c5d9998e7f56a2"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b3cab0e06143768499384a8a5efb9c4dc53e19382952859e4802f294214f36ec"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6ed10c2497e5fedadf61e465b3ca12a19f96004c15dcffe4bd442ebadc2d85"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43babacef21c519bc6631c5fce2a61eccdfc011b4bcb9047255e9620732c8097"}, + {file = "watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3"}, +] + +[package.dependencies] +anyio = ">=3.0.0" + +[[package]] +name = "websockets" +version = "12.0" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, + {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, + {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, + {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, + {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, + {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, + {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, + {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, + {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, + {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, + {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, + {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, + {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, + {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, + {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, + {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, + {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, + {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, + {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, + {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, + {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, + {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, + {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, + {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, + {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, +] + +[[package]] +name = "wsproto" +version = "1.2.0" +description = "WebSockets state-machine based protocol implementation" +optional = false +python-versions = ">=3.7.0" files = [ - {file = "yarl-1.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32435d134414e01d937cd9d6cc56e8413a8d4741dea36af5840c7750f04d16ab"}, - {file = "yarl-1.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9a5211de242754b5e612557bca701f39f8b1a9408dff73c6db623f22d20f470e"}, - {file = "yarl-1.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:525cd69eff44833b01f8ef39aa33a9cc53a99ff7f9d76a6ef6a9fb758f54d0ff"}, - {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc94441bcf9cb8c59f51f23193316afefbf3ff858460cb47b5758bf66a14d130"}, - {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e36021db54b8a0475805acc1d6c4bca5d9f52c3825ad29ae2d398a9d530ddb88"}, - {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0f17d1df951336a02afc8270c03c0c6e60d1f9996fcbd43a4ce6be81de0bd9d"}, - {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5f3faeb8100a43adf3e7925d556801d14b5816a0ac9e75e22948e787feec642"}, - {file = "yarl-1.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aed37db837ecb5962469fad448aaae0f0ee94ffce2062cf2eb9aed13328b5196"}, - {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:721ee3fc292f0d069a04016ef2c3a25595d48c5b8ddc6029be46f6158d129c92"}, - {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b8bc5b87a65a4e64bc83385c05145ea901b613d0d3a434d434b55511b6ab0067"}, - {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:dd952b9c64f3b21aedd09b8fe958e4931864dba69926d8a90c90d36ac4e28c9a"}, - {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:c405d482c320a88ab53dcbd98d6d6f32ada074f2d965d6e9bf2d823158fa97de"}, - {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9df9a0d4c5624790a0dea2e02e3b1b3c69aed14bcb8650e19606d9df3719e87d"}, - {file = "yarl-1.9.3-cp310-cp310-win32.whl", hash = "sha256:d34c4f80956227f2686ddea5b3585e109c2733e2d4ef12eb1b8b4e84f09a2ab6"}, - {file = "yarl-1.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:cf7a4e8de7f1092829caef66fd90eaf3710bc5efd322a816d5677b7664893c93"}, - {file = "yarl-1.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d61a0ca95503867d4d627517bcfdc28a8468c3f1b0b06c626f30dd759d3999fd"}, - {file = "yarl-1.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73cc83f918b69110813a7d95024266072d987b903a623ecae673d1e71579d566"}, - {file = "yarl-1.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d81657b23e0edb84b37167e98aefb04ae16cbc5352770057893bd222cdc6e45f"}, - {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a1a8443091c7fbc17b84a0d9f38de34b8423b459fb853e6c8cdfab0eacf613"}, - {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe34befb8c765b8ce562f0200afda3578f8abb159c76de3ab354c80b72244c41"}, - {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c757f64afe53a422e45e3e399e1e3cf82b7a2f244796ce80d8ca53e16a49b9f"}, - {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72a57b41a0920b9a220125081c1e191b88a4cdec13bf9d0649e382a822705c65"}, - {file = "yarl-1.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:632c7aeb99df718765adf58eacb9acb9cbc555e075da849c1378ef4d18bf536a"}, - {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b0b8c06afcf2bac5a50b37f64efbde978b7f9dc88842ce9729c020dc71fae4ce"}, - {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1d93461e2cf76c4796355494f15ffcb50a3c198cc2d601ad8d6a96219a10c363"}, - {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:4003f380dac50328c85e85416aca6985536812c082387255c35292cb4b41707e"}, - {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4d6d74a97e898c1c2df80339aa423234ad9ea2052f66366cef1e80448798c13d"}, - {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b61e64b06c3640feab73fa4ff9cb64bd8182de52e5dc13038e01cfe674ebc321"}, - {file = "yarl-1.9.3-cp311-cp311-win32.whl", hash = "sha256:29beac86f33d6c7ab1d79bd0213aa7aed2d2f555386856bb3056d5fdd9dab279"}, - {file = "yarl-1.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:f7271d6bd8838c49ba8ae647fc06469137e1c161a7ef97d778b72904d9b68696"}, - {file = "yarl-1.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:dd318e6b75ca80bff0b22b302f83a8ee41c62b8ac662ddb49f67ec97e799885d"}, - {file = "yarl-1.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c4b1efb11a8acd13246ffb0bee888dd0e8eb057f8bf30112e3e21e421eb82d4a"}, - {file = "yarl-1.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c6f034386e5550b5dc8ded90b5e2ff7db21f0f5c7de37b6efc5dac046eb19c10"}, - {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd49a908cb6d387fc26acee8b7d9fcc9bbf8e1aca890c0b2fdfd706057546080"}, - {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa4643635f26052401750bd54db911b6342eb1a9ac3e74f0f8b58a25d61dfe41"}, - {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e741bd48e6a417bdfbae02e088f60018286d6c141639359fb8df017a3b69415a"}, - {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c86d0d0919952d05df880a1889a4f0aeb6868e98961c090e335671dea5c0361"}, - {file = "yarl-1.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d5434b34100b504aabae75f0622ebb85defffe7b64ad8f52b8b30ec6ef6e4b9"}, - {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79e1df60f7c2b148722fb6cafebffe1acd95fd8b5fd77795f56247edaf326752"}, - {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:44e91a669c43f03964f672c5a234ae0d7a4d49c9b85d1baa93dec28afa28ffbd"}, - {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3cfa4dbe17b2e6fca1414e9c3bcc216f6930cb18ea7646e7d0d52792ac196808"}, - {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:88d2c3cc4b2f46d1ba73d81c51ec0e486f59cc51165ea4f789677f91a303a9a7"}, - {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cccdc02e46d2bd7cb5f38f8cc3d9db0d24951abd082b2f242c9e9f59c0ab2af3"}, - {file = "yarl-1.9.3-cp312-cp312-win32.whl", hash = "sha256:96758e56dceb8a70f8a5cff1e452daaeff07d1cc9f11e9b0c951330f0a2396a7"}, - {file = "yarl-1.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:c4472fe53ebf541113e533971bd8c32728debc4c6d8cc177f2bff31d011ec17e"}, - {file = "yarl-1.9.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:126638ab961633f0940a06e1c9d59919003ef212a15869708dcb7305f91a6732"}, - {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c99ddaddb2fbe04953b84d1651149a0d85214780e4d0ee824e610ab549d98d92"}, - {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dab30b21bd6fb17c3f4684868c7e6a9e8468078db00f599fb1c14e324b10fca"}, - {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:828235a2a169160ee73a2fcfb8a000709edf09d7511fccf203465c3d5acc59e4"}, - {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc391e3941045fd0987c77484b2799adffd08e4b6735c4ee5f054366a2e1551d"}, - {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51382c72dd5377861b573bd55dcf680df54cea84147c8648b15ac507fbef984d"}, - {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:28a108cb92ce6cf867690a962372996ca332d8cda0210c5ad487fe996e76b8bb"}, - {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8f18a7832ff85dfcd77871fe677b169b1bc60c021978c90c3bb14f727596e0ae"}, - {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:7eaf13af79950142ab2bbb8362f8d8d935be9aaf8df1df89c86c3231e4ff238a"}, - {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:66a6dbf6ca7d2db03cc61cafe1ee6be838ce0fbc97781881a22a58a7c5efef42"}, - {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1a0a4f3aaa18580038cfa52a7183c8ffbbe7d727fe581300817efc1e96d1b0e9"}, - {file = "yarl-1.9.3-cp37-cp37m-win32.whl", hash = "sha256:946db4511b2d815979d733ac6a961f47e20a29c297be0d55b6d4b77ee4b298f6"}, - {file = "yarl-1.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2dad8166d41ebd1f76ce107cf6a31e39801aee3844a54a90af23278b072f1ccf"}, - {file = "yarl-1.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bb72d2a94481e7dc7a0c522673db288f31849800d6ce2435317376a345728225"}, - {file = "yarl-1.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9a172c3d5447b7da1680a1a2d6ecdf6f87a319d21d52729f45ec938a7006d5d8"}, - {file = "yarl-1.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2dc72e891672343b99db6d497024bf8b985537ad6c393359dc5227ef653b2f17"}, - {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8d51817cf4b8d545963ec65ff06c1b92e5765aa98831678d0e2240b6e9fd281"}, - {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53ec65f7eee8655bebb1f6f1607760d123c3c115a324b443df4f916383482a67"}, - {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cfd77e8e5cafba3fb584e0f4b935a59216f352b73d4987be3af51f43a862c403"}, - {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e73db54c967eb75037c178a54445c5a4e7461b5203b27c45ef656a81787c0c1b"}, - {file = "yarl-1.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09c19e5f4404574fcfb736efecf75844ffe8610606f3fccc35a1515b8b6712c4"}, - {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6280353940f7e5e2efaaabd686193e61351e966cc02f401761c4d87f48c89ea4"}, - {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c25ec06e4241e162f5d1f57c370f4078797ade95c9208bd0c60f484834f09c96"}, - {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:7217234b10c64b52cc39a8d82550342ae2e45be34f5bff02b890b8c452eb48d7"}, - {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4ce77d289f8d40905c054b63f29851ecbfd026ef4ba5c371a158cfe6f623663e"}, - {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5f74b015c99a5eac5ae589de27a1201418a5d9d460e89ccb3366015c6153e60a"}, - {file = "yarl-1.9.3-cp38-cp38-win32.whl", hash = "sha256:8a2538806be846ea25e90c28786136932ec385c7ff3bc1148e45125984783dc6"}, - {file = "yarl-1.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:6465d36381af057d0fab4e0f24ef0e80ba61f03fe43e6eeccbe0056e74aadc70"}, - {file = "yarl-1.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2f3c8822bc8fb4a347a192dd6a28a25d7f0ea3262e826d7d4ef9cc99cd06d07e"}, - {file = "yarl-1.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7831566595fe88ba17ea80e4b61c0eb599f84c85acaa14bf04dd90319a45b90"}, - {file = "yarl-1.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ff34cb09a332832d1cf38acd0f604c068665192c6107a439a92abfd8acf90fe2"}, - {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe8080b4f25dfc44a86bedd14bc4f9d469dfc6456e6f3c5d9077e81a5fedfba7"}, - {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8535e111a064f3bdd94c0ed443105934d6f005adad68dd13ce50a488a0ad1bf3"}, - {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d155a092bf0ebf4a9f6f3b7a650dc5d9a5bbb585ef83a52ed36ba46f55cc39d"}, - {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:778df71c8d0c8c9f1b378624b26431ca80041660d7be7c3f724b2c7a6e65d0d6"}, - {file = "yarl-1.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9f9cafaf031c34d95c1528c16b2fa07b710e6056b3c4e2e34e9317072da5d1a"}, - {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ca6b66f69e30f6e180d52f14d91ac854b8119553b524e0e28d5291a724f0f423"}, - {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0e7e83f31e23c5d00ff618045ddc5e916f9e613d33c5a5823bc0b0a0feb522f"}, - {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:af52725c7c39b0ee655befbbab5b9a1b209e01bb39128dce0db226a10014aacc"}, - {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0ab5baaea8450f4a3e241ef17e3d129b2143e38a685036b075976b9c415ea3eb"}, - {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6d350388ba1129bc867c6af1cd17da2b197dff0d2801036d2d7d83c2d771a682"}, - {file = "yarl-1.9.3-cp39-cp39-win32.whl", hash = "sha256:e2a16ef5fa2382af83bef4a18c1b3bcb4284c4732906aa69422cf09df9c59f1f"}, - {file = "yarl-1.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:d92d897cb4b4bf915fbeb5e604c7911021a8456f0964f3b8ebbe7f9188b9eabb"}, - {file = "yarl-1.9.3-py3-none-any.whl", hash = "sha256:271d63396460b6607b588555ea27a1a02b717ca2e3f2cf53bdde4013d7790929"}, - {file = "yarl-1.9.3.tar.gz", hash = "sha256:4a14907b597ec55740f63e52d7fee0e9ee09d5b9d57a4f399a7423268e457b57"}, + {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"}, + {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"}, ] [package.dependencies] -idna = ">=2.0" -multidict = ">=4.0" +h11 = ">=0.9.0,<1" [metadata] lock-version = "2.0" python-versions = ">=3.11,<4" -content-hash = "055a21b4a379207cb3b081ceb326e05dddaefd98c98be77b7234800c0467592c" +content-hash = "c74110c6ad66915735859d348d00c6a70f1e38a5da6580d547496b367e1ed7c0" diff --git a/pyproject.toml b/pyproject.toml index 093c2488..03c345ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,16 +6,8 @@ authors = ["BrewPi "] license = "GPL-3.0" readme = "README.md" -[tool.pyright] -include = ["brewblox_devcon_spark"] -exclude = ["**/node_modules", "**/__pycache__"] - -useLibraryCodeForTypes = true -reportMissingImports = "information" - [tool.poetry.dependencies] python = ">=3.11,<4" -brewblox-service = "^3.3.2" pyserial-asyncio = "^0.6" protobuf = "^4.24.3" aiofiles = "^23.2.1" @@ -25,19 +17,74 @@ Pint = "^0.22" esptool = "^4.0" pytimeparse = "^1.1.8" ciso8601 = "^2.2.0" +fastapi = "^0.104.1" +uvicorn = { extras = ["standard"], version = "^0.24.0.post1" } +httpx = "^0.25.2" +fastapi-mqtt = "^2.0.0" +pydantic-settings = "^2.1.0" +websockets = "^12.0" +httpx-ws = "^0.4.3" +dnspython = "^2.4.2" [tool.poetry.group.dev.dependencies] pytest = "*" pytest-cov = "*" pytest-mock = "*" -pytest-aiohttp = "*" flake8 = "*" autopep8 = "*" -aresponses = "*" flake8-quotes = "*" invoke = "*" grpcio-tools = "^1.58.0" +flake8-pyproject = "*" +pytest-docker = "*" +pytest-asyncio = "*" +pytest-httpx = "*" +asgi-lifespan = "*" [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" + +[tool.pyright] +include = ["brewblox_devcon_spark"] +exclude = ["**/node_modules", "**/__pycache__"] +useLibraryCodeForTypes = true +reportMissingImports = "information" + +[tool.pytest.ini_options] +asyncio_mode = "auto" +addopts = """ + --ignore=brewblox_devcon_spark/codec/proto-compiled/ + --cov=brewblox_devcon_spark + --cov-branch + --cov-report=term-missing:skip-covered + --cov-fail-under=100 + --no-cov-on-fail + --durations=3 + """ + +[tool.coverage.report] +exclude_lines = [ + "pragma: no cover", + "def __repr__", + "if self.debug:", + "if settings.DEBUG", + "raise AssertionError", + "raise NotImplementedError", + "if 0:", + "if __name__ == .__main__.:", + "except asyncio.CancelledError:", +] + +[tool.coverage.run] +omit = [ + "brewblox_devcon_spark/ymodem.py", + "brewblox_devcon_spark/codec/proto-compiled/*", +] + +[tool.flake8] +max-line-length = 120 +exclude = "*_pb2.py,.venv" + +[tool.autopep8] +max-line-length = 120 diff --git a/tasks.py b/tasks.py index 57c8c4c6..620ad098 100644 --- a/tasks.py +++ b/tasks.py @@ -1,18 +1,25 @@ from configparser import ConfigParser +from contextlib import suppress from pathlib import Path from invoke import Context, task +from invoke.exceptions import UnexpectedExit -from brewblox_devcon_spark.models import ServiceFirmwareIni +from brewblox_devcon_spark.models import FirmwareConfig ROOT = Path(__file__).parent.resolve() FW_BASE_URL = 'https://brewblox.blob.core.windows.net/firmware' -def parse_ini() -> ServiceFirmwareIni: +def get_fw_config() -> FirmwareConfig: # pragma: no cover + """ + Globally cached getter for firmware config. + When first called, config is parsed from the firmware.ini file. + """ parser = ConfigParser() - parser.read((ROOT / 'firmware.ini').resolve()) - config = ServiceFirmwareIni(parser['FIRMWARE'].items()) + parser.read(ROOT / 'firmware.ini') + raw = dict(parser['FIRMWARE'].items()) + config = FirmwareConfig(**raw) return config @@ -32,11 +39,11 @@ def compile_proto(ctx: Context): @task def download_firmware(ctx: Context): + fw_config = get_fw_config() fw_dir = ROOT / 'firmware' - ini = parse_ini() - fw_date = ini['firmware_date'] - fw_version = ini['firmware_version'] + fw_date = fw_config.firmware_date + fw_version = fw_config.firmware_version fw_file = 'brewblox-release.tar.gz' url = f'{FW_BASE_URL}/{fw_date}-{fw_version}/{fw_file}' print(f'Downloading firmware release {fw_date}-{fw_version}') @@ -62,10 +69,10 @@ def update_firmware(ctx: Context, release='develop'): with ctx.cd(ROOT): ctx.run(f'curl -sSf -o firmware.ini "{url}"') - ini = parse_ini() - fw_date = ini['firmware_date'] - fw_version = ini['firmware_version'] - proto_version = ini['proto_version'] + fw_config = get_fw_config() + fw_date = fw_config.firmware_date + fw_version = fw_config.firmware_version + proto_version = fw_config.proto_version print(f'Updating to firmware release {fw_date}-{fw_version}') @@ -74,6 +81,24 @@ def update_firmware(ctx: Context, release='develop'): ctx.run(f'git checkout --quiet "{proto_version}"') +@task +def testclean(ctx: Context): + """ + Cleans up leftover test containers, networks, and simulators. + Container cleanup is normally done in test fixtures. + This is skipped if debugged tests are stopped halfway. + """ + result = ctx.run('docker ps -aq --filter "name=pytest"', hide='stdout') + containers = result.stdout.strip().replace('\n', ' ') + if containers: + ctx.run(f'docker rm -f {containers}') + ctx.run('docker network prune -f') + + with suppress(UnexpectedExit): + # returns 1 if nothing killed + ctx.run('pkill -ef -9 brewblox-amd64.sim') + + @task def build(ctx: Context): with ctx.cd(ROOT): @@ -83,6 +108,6 @@ def build(ctx: Context): @task(pre=[build]) -def local_docker(ctx: Context, tag='local'): +def image(ctx: Context, tag='local'): with ctx.cd(ROOT): ctx.run(f'docker build -t ghcr.io/brewblox/brewblox-devcon-spark:{tag} -f Dockerfile.service .') diff --git a/test/conftest.py b/test/conftest.py index 55ea3c21..f7b55eef 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -3,151 +3,176 @@ Any fixtures declared here are available to all test functions in this directory. """ +import asyncio import logging +from datetime import timedelta +from pathlib import Path +from typing import Generator +from unittest.mock import Mock import pytest -from aiohttp import test_utils -from brewblox_service import brewblox_logger, features, service, testing +from asgi_lifespan import LifespanManager +from fastapi import FastAPI +from httpx import AsyncClient +from pydantic_settings import BaseSettings, PydanticBaseSettingsSource +from pytest_docker.plugin import Services as DockerServices -from brewblox_devcon_spark.models import (DiscoveryType, ServiceConfig, - ServiceFirmwareIni) +from brewblox_devcon_spark import app_factory, utils +from brewblox_devcon_spark.models import Block, ServiceConfig -LOGGER = brewblox_logger(__name__) +LOGGER = logging.getLogger(__name__) -@pytest.fixture(scope='session', autouse=True) -def log_enabled(): - """Sets log level to DEBUG for all test functions. - Allows all logged messages to be captured during pytest runs""" - logging.getLogger().setLevel(logging.DEBUG) - logging.captureWarnings(True) +class TestConfig(ServiceConfig): + """ + An override for ServiceConfig that only uses + settings provided to __init__() + This makes tests independent from env values + and the content of .appenv + """ -@pytest.fixture -def app_config() -> ServiceConfig: - return ServiceConfig( - # From brewblox_service - name='test_app', - host='localhost', - port=1234, - debug=True, - mqtt_protocol='mqtt', - mqtt_host='eventbus', - mqtt_port=1883, - mqtt_path='/eventbus', - history_topic='brewcast/history', - state_topic='brewcast/state', + @classmethod + def settings_customise_sources( + cls, + settings_cls: type[BaseSettings], + init_settings: PydanticBaseSettingsSource, + env_settings: PydanticBaseSettingsSource, + dotenv_settings: PydanticBaseSettingsSource, + file_secret_settings: PydanticBaseSettingsSource, + ) -> tuple[PydanticBaseSettingsSource, ...]: + return (init_settings,) - # From brewblox_devcon_spark - device_serial='/dev/TESTEH', - device_id='1234', - device_port=8332, - display_ws_port=7377, - discovery=DiscoveryType.all, - simulation=False, + +@pytest.fixture(scope='session') +def docker_compose_file(): + return Path('./test/docker-compose.yml').resolve() + + +@pytest.fixture(autouse=True) +def config(monkeypatch: pytest.MonkeyPatch, + docker_services: DockerServices, + tmp_path: Path, + ) -> Generator[ServiceConfig, None, None]: + cfg = TestConfig( + name='sparkey', + debug=True, + trace=True, + mqtt_host='localhost', + mqtt_port=docker_services.port_for('eventbus', 1883), + http_client_interval=timedelta(milliseconds=100), + datastore_host='localhost', + datastore_port=docker_services.port_for('history', 5000), mock=True, - command_timeout=10, - broadcast_interval=5, - isolated=True, - backup_interval=3600, - backup_retry_interval=300, - time_sync_interval=900, - skip_version_check=False, - datastore_topic='brewcast/datastore', + simulation=True, + simulation_workdir=tmp_path / 'simulator', + simulation_port=utils.get_free_port(), + simulation_display_port=utils.get_free_port(), + device_id='1234', + connect_interval=timedelta(milliseconds=10), + connect_interval_max=timedelta(milliseconds=100), + discovery_interval=timedelta(milliseconds=10), + discovery_timeout=timedelta(seconds=10), + handshake_timeout=timedelta(seconds=20), + handshake_ping_interval=timedelta(milliseconds=20), + backup_root_dir=tmp_path / 'backup', ) + print(cfg) + monkeypatch.setattr(utils, 'get_config', lambda: cfg) + yield cfg -@pytest.fixture -def sys_args(app_config) -> list: - return [str(v) for v in [ - 'app_name', - '--debug', - '--name', app_config.name, - '--host', app_config.host, - '--port', app_config.port, - '--device-serial', app_config.device_serial, - '--device-id', app_config.device_id, - '--discovery', app_config.discovery, - '--command-timeout', app_config.command_timeout, - '--broadcast-interval', app_config.broadcast_interval, - '--backup-interval', app_config.backup_interval, - '--backup-retry-interval', app_config.backup_retry_interval, - '--isolated', - '--mock', - ]] +@pytest.fixture(autouse=True) +def setup_logging(config: ServiceConfig): + app_factory.setup_logging(True, True) @pytest.fixture -def app_ini() -> ServiceFirmwareIni: - return { - 'proto_version': '3f2243a', - 'proto_date': '2019-06-06', - 'firmware_version': 'd264dc6c', - 'firmware_date': '2019-07-03', - 'system_version': '3.1.0', - } +def caplog(caplog: pytest.LogCaptureFixture) -> pytest.LogCaptureFixture: + # caplog must be explicitly set to the desired level + # https://stackoverflow.com/questions/59875983/why-is-caplog-text-empty-even-though-the-function-im-testing-is-logging + caplog.set_level(logging.DEBUG) + return caplog -@pytest.fixture -def app(app_config, app_ini): - app = service.create_app(app_config) - app['ini'] = app_ini - return app +@pytest.fixture(autouse=True) +def m_kill(monkeypatch: pytest.MonkeyPatch) -> Generator[Mock, None, None]: + m = Mock(spec=utils.os.kill) + monkeypatch.setattr(utils.os, 'kill', m) + yield m -@pytest.fixture -async def setup(app): - pass +@pytest.fixture(autouse=True) +def m_sleep(monkeypatch: pytest.MonkeyPatch, request: pytest.FixtureRequest): + """ + Allows keeping track of calls to asyncio sleep. + For tests, we want to reduce all sleep durations. + Set a breakpoint in the wrapper to track all calls. + """ + real_func = asyncio.sleep + + async def wrapper(delay: float, *args, **kwargs): + if delay > 0.1: + print(f'asyncio.sleep({delay}) in {request.node.name}') + return await real_func(delay, *args, **kwargs) + monkeypatch.setattr('asyncio.sleep', wrapper) + yield @pytest.fixture -async def client(app, setup, aiohttp_client, aiohttp_server): - """Allows patching the app or aiohttp_client before yielding it. +def app() -> FastAPI: + """ + Override this in test modules to bootstrap required dependencies. - Any tests wishing to add custom behavior to app can override the fixture + IMPORTANT: This must NOT be an async fixture. + Contextvars assigned in async fixtures are invisible to test functions. """ - LOGGER.debug('Available features:') - for name, impl in app.get(features.FEATURES_KEY, {}).items(): - LOGGER.debug(f'Feature "{name}" = {impl}') - LOGGER.debug(app.on_startup) + app = FastAPI() + return app - test_server: test_utils.TestServer = await aiohttp_server(app) - test_client: test_utils.TestClient = await aiohttp_client(test_server) - return test_client +@pytest.fixture +async def manager(app: FastAPI) -> Generator[LifespanManager, None, None]: + """ + AsyncClient does not automatically send ASGI lifespan events to the app + https://asgi.readthedocs.io/en/latest/specs/lifespan.html -@pytest.fixture(scope='session') -def broker(): - with testing.docker_container( - name='mqtt-test-container', - ports={'mqtt': 1883, 'ws': 15675}, - args=['ghcr.io/brewblox/mosquitto:develop'], - ) as ports: - yield ports + For testing, this ensures that lifespan() functions are handled. + If you don't need to make HTTP requests, you can use the manager + without the `client` fixture. + """ + async with LifespanManager(app) as mgr: + yield mgr + + +@pytest.fixture +async def client(manager: LifespanManager) -> Generator[AsyncClient, None, None]: + async with AsyncClient(app=manager.app, base_url='http://test') as ac: + yield ac @pytest.fixture -def spark_blocks(): +def spark_blocks() -> list[Block]: return [ - { - 'id': 'balancer-1', - 'nid': 200, - 'type': 'Balancer', - 'data': {} - }, - { - 'id': 'mutex-1', - 'nid': 300, - 'type': 'Mutex', - 'data': { + Block( + id='balancer-1', + nid=200, + type='Balancer', + data={} + ), + Block( + id='mutex-1', + nid=300, + type='Mutex', + data={ 'differentActuatorWait': 43 } - }, - { - 'id': 'profile-1', - 'nid': 201, - 'type': 'SetpointProfile', - 'data': { + ), + Block( + id='profile-1', + nid=201, + type='SetpointProfile', + data={ 'points': [ { 'time': 1540376829, @@ -164,31 +189,31 @@ def spark_blocks(): ], 'targetId<>': 'setpoint-sensor-pair-2' } - }, - { - 'id': 'sensor-1', - 'nid': 202, - 'type': 'TempSensorMock', - 'data': { + ), + Block( + id='sensor-1', + nid=202, + type='TempSensorMock', + data={ 'value[celsius]': 20.89789201, 'connected': True } - }, - { - 'id': 'sensor-onewire-1', - 'nid': 203, - 'type': 'TempSensorOneWire', - 'data': { + ), + Block( + id='sensor-onewire-1', + nid=203, + type='TempSensorOneWire', + data={ 'value[celsius]': 20.89789201, 'offset[delta_degC]': 9, 'address': 'DEADBEEF' } - }, - { - 'id': 'setpoint-sensor-pair-1', - 'nid': 204, - 'type': 'SetpointSensorPair', - 'data': { + ), + Block( + id='setpoint-sensor-pair-1', + nid=204, + type='SetpointSensorPair', + data={ 'sensorId<>': 'sensor-1', 'setting': 0, 'value': 0, @@ -196,23 +221,23 @@ def spark_blocks(): 'filter': 1, # FILTER_15s 'filterThreshold': 2 } - }, - { - 'id': 'setpoint-sensor-pair-2', - 'nid': 205, - 'type': 'SetpointSensorPair', - 'data': { + ), + Block( + id='setpoint-sensor-pair-2', + nid=205, + type='SetpointSensorPair', + data={ 'sensorId<>': 0, 'setting': 0, 'value': 0, 'enabled': True } - }, - { - 'id': 'actuator-1', - 'nid': 206, - 'type': 'ActuatorAnalogMock', - 'data': { + ), + Block( + id='actuator-1', + nid=206, + type='ActuatorAnalogMock', + data={ 'setting': 20, 'minSetting': 10, 'maxSetting': 30, @@ -220,12 +245,12 @@ def spark_blocks(): 'minValue': 40, 'maxValue': 60 } - }, - { - 'id': 'actuator-pwm-1', - 'nid': 207, - 'type': 'ActuatorPwm', - 'data': { + ), + Block( + id='actuator-pwm-1', + nid=207, + type='ActuatorPwm', + data={ 'constrainedBy': { 'constraints': [ { @@ -244,12 +269,12 @@ def spark_blocks(): 'period': 4000, 'actuatorId<>': 'actuator-digital-1' } - }, - { - 'id': 'actuator-digital-1', - 'nid': 208, - 'type': 'DigitalActuator', - 'data': { + ), + Block( + id='actuator-digital-1', + nid=208, + type='DigitalActuator', + data={ 'channel': 1, 'constrainedBy': { 'constraints': [ @@ -267,21 +292,21 @@ def spark_blocks(): ] } } - }, - { - 'id': 'offset-1', - 'nid': 209, - 'type': 'ActuatorOffset', - 'data': { + ), + Block( + id='offset-1', + nid=209, + type='ActuatorOffset', + data={ 'targetId<>': 'setpoint-sensor-pair-1', 'referenceId<>': 'setpoint-sensor-pair-1' } - }, - { - 'id': 'pid-1', - 'nid': 210, - 'type': 'Pid', - 'data': { + ), + Block( + id='pid-1', + nid=210, + type='Pid', + data={ 'inputId<>': 'setpoint-sensor-pair-1', 'outputId<>': 'actuator-pwm-1', 'enabled': True, @@ -290,12 +315,12 @@ def spark_blocks(): 'ti': 3600, 'td': 60 } - }, - { - 'id': 'DisplaySettings', - 'nid': 7, - 'type': 'DisplaySettings', - 'data': { + ), + Block( + id='DisplaySettings', + nid=7, + type='DisplaySettings', + data={ 'widgets': [ { 'pos': 1, @@ -324,23 +349,23 @@ def spark_blocks(): ], 'name': 'test' } - }, - { - 'id': 'ds2413-hw-1', - 'nid': 211, - 'type': 'DS2413', - 'data': { + ), + Block( + id='ds2413-hw-1', + nid=211, + type='DS2413', + data={ 'address': '4444444444444444' } - }, - { - 'id': 'ow-act', - 'nid': 212, - 'type': 'DigitalActuator', - 'data': { + ), + Block( + id='ow-act', + nid=212, + type='DigitalActuator', + data={ 'channel': 1, 'invert': True, 'hwDevice': 'ds2413-hw-1' } - } + ) ] diff --git a/test/docker-compose.yml b/test/docker-compose.yml new file mode 100644 index 00000000..468d7c5d --- /dev/null +++ b/test/docker-compose.yml @@ -0,0 +1,23 @@ +name: brewblox-devcon-spark-test +services: + eventbus: + image: ghcr.io/brewblox/mosquitto:develop + ports: + - "1883" + redis: + image: redis:6.0 + ports: + - "6379" + victoria: + image: victoriametrics/victoria-metrics:v1.88.0 + command: >- + --influxMeasurementFieldSeparator=/ + --http.pathPrefix=/victoria + ports: + - "8428" + history: + image: ghcr.io/brewblox/brewblox-history:develop + ports: + - "5000" + environment: + - BREWBLOX_DEBUG=True diff --git a/test/test_app_factory.py b/test/test_app_factory.py new file mode 100644 index 00000000..5d1d09ed --- /dev/null +++ b/test/test_app_factory.py @@ -0,0 +1,24 @@ +""" +Tests brewblox_devcon_spark.app_factory +""" +import asyncio + +import pytest +from fastapi import FastAPI +from httpx import AsyncClient + +from brewblox_devcon_spark import app_factory, state_machine + +TESTED = app_factory.__name__ + + +@pytest.fixture +def app() -> FastAPI: + return app_factory.create_app() + + +async def test_startup(client: AsyncClient): + state = state_machine.CV.get() + await asyncio.wait_for(state.wait_synchronized(), timeout=5) + resp = await client.get('/sparkey/api/doc') + assert resp.status_code == 200 diff --git a/test/test_backup_storage.py b/test/test_backup_storage.py deleted file mode 100644 index f835c49e..00000000 --- a/test/test_backup_storage.py +++ /dev/null @@ -1,88 +0,0 @@ -""" -Tests brewblox_devcon_spark.broadcaster -""" - -from unittest.mock import AsyncMock - -import pytest -from brewblox_service import repeater, scheduler - -from brewblox_devcon_spark import (backup_storage, block_store, codec, - commander, connection, controller, - global_store, service_status, service_store, - synchronization) -from brewblox_devcon_spark.models import Backup, BackupIdentity, ServiceConfig - -TESTED = backup_storage.__name__ - - -@pytest.fixture(autouse=True) -def m_backup_dir(mocker, tmp_path): - mocker.patch(TESTED + '.BASE_BACKUP_DIR', tmp_path) - - -@pytest.fixture -async def setup(app): - config: ServiceConfig = app['config'] - config.backup_interval = 0.01 - config.backup_retry_interval = 0.01 - - service_status.setup(app) - scheduler.setup(app) - codec.setup(app) - block_store.setup(app) - global_store.setup(app) - service_store.setup(app) - connection.setup(app) - commander.setup(app) - synchronization.setup(app) - controller.setup(app) - backup_storage.setup(app) - - backup_storage.fget(app)._autostart = False - - -@pytest.fixture -async def synchronized(app, client): - await service_status.wait_synchronized(app) - - -async def test_inactive(app, client, synchronized): - config: ServiceConfig = app['config'] - config.backup_interval = -2 - config.backup_retry_interval = -1 - - storage = backup_storage.BackupStorage(app) - with pytest.raises(repeater.RepeaterCancelled): - await storage.prepare() - - assert storage.retry_interval_s == -2 - assert not storage.enabled - assert not storage.active - - -async def test_autosave(app, client, mocker, synchronized): - storage = backup_storage.fget(app) - await storage.prepare() - await storage.run() - - stored = await storage.all() - assert len(stored) == 1 - assert isinstance(stored[0], BackupIdentity) - - data = await storage.read(stored[0]) - assert isinstance(data, Backup) - - mocker.patch.object(controller.fget(app), 'make_backup', AsyncMock(side_effect=RuntimeError)) - with pytest.raises(RuntimeError): - await storage.run() - - # Synchronized is checked before controller call - # run() exits before the RuntimeError is raised - service_status.set_disconnected(app) - await service_status.wait_disconnected(app) - await storage.run() - - # No new entries were added - stored = await storage.all() - assert len(stored) == 1 diff --git a/test/test_block_analysis.py b/test/test_block_analysis.py index 24e19f19..8650f5d0 100644 --- a/test/test_block_analysis.py +++ b/test/test_block_analysis.py @@ -2,13 +2,11 @@ Tests brewblox_devcon_spark.block_analysis """ -from typing import Optional - from brewblox_devcon_spark import block_analysis -from brewblox_devcon_spark.models import Block +from brewblox_devcon_spark.models import Block, BlockClaim, BlockRelation -def blox_link(id: Optional[str], blockType: Optional[str] = None): +def blox_link(id: str | None, blockType: str | None = None): return { '__bloxtype': 'Link', 'id': id, @@ -16,7 +14,7 @@ def blox_link(id: Optional[str], blockType: Optional[str] = None): } -def blox_qty(value: Optional[float], unit: str = None): +def blox_qty(value: float | None, unit: str = None): return { '__bloxtype': 'Quantity', 'value': value, @@ -24,11 +22,11 @@ def blox_qty(value: Optional[float], unit: str = None): } -def temp_qty(value: Optional[float]): +def temp_qty(value: float | None): return blox_qty(value, 'degC') -def delta_temp_qty(value: Optional[float]): +def delta_temp_qty(value: float | None): return blox_qty(value, 'delta_degC') @@ -278,42 +276,62 @@ def make_blocks() -> list[Block]: def test_calculate_relations(): blocks = make_blocks() result = block_analysis.calculate_relations(blocks) - result = sorted(result, key=lambda v: v['source'] + ' ' + v['target']) + result = sorted(result, key=lambda v: f'{v.source} {v.target}') assert result == [ - {'source': 'Cool Actuator', 'target': 'Spark Pins', 'relation': ['hwDevice']}, - {'source': 'Cool PID', 'target': 'Cool PWM', 'relation': ['outputId'], 'claimed': True}, - { - 'source': 'Cool PWM', - 'target': 'Balancer', - 'relation': [ - 'constrainedBy', - 'constraints', - '0', - 'balanced', - 'balancerId', - ], - }, - {'source': 'Cool PWM', 'target': 'Cool Actuator', 'relation': ['actuatorId'], 'claimed': True}, - {'source': 'Heat Actuator', 'target': 'Spark Pins', 'relation': ['hwDevice']}, - {'source': 'Heat PID', 'target': 'Heat PWM', 'relation': ['outputId'], 'claimed': True}, - {'source': 'Heat PWM', 'target': 'Heat Actuator', 'relation': ['actuatorId'], 'claimed': True}, - {'source': 'Sensor', 'target': 'Setpoint', 'relation': ['sensorId']}, - {'source': 'Setpoint', 'target': 'Cool PID', 'relation': ['inputId']}, - {'source': 'Setpoint', 'target': 'Heat PID', 'relation': ['inputId']}, + BlockRelation(source='Cool Actuator', + target='Spark Pins', + relation=['hwDevice']), + BlockRelation(source='Cool PID', + target='Cool PWM', + claimed=True, + relation=['outputId']), + BlockRelation(source='Cool PWM', + target='Balancer', + relation=[ + 'constrainedBy', + 'constraints', + '0', + 'balanced', + 'balancerId', + ]), + BlockRelation(source='Cool PWM', + target='Cool Actuator', + claimed=True, + relation=['actuatorId']), + BlockRelation(source='Heat Actuator', + target='Spark Pins', + relation=['hwDevice']), + BlockRelation(source='Heat PID', + target='Heat PWM', + claimed=True, + relation=['outputId']), + BlockRelation(source='Heat PWM', + target='Heat Actuator', + claimed=True, + relation=['actuatorId']), + BlockRelation(source='Sensor', + target='Setpoint', + relation=['sensorId']), + BlockRelation(source='Setpoint', + target='Cool PID', + relation=['inputId']), + BlockRelation(source='Setpoint', + target='Heat PID', + relation=['inputId']), ] def test_calculate_claims(): blocks = make_blocks() result = block_analysis.calculate_claims(blocks) - result = sorted(result, key=lambda v: v['target'] + ' ' + v['source']) + result = sorted(result, key=lambda v: f'{v.target} {v.source}') assert result == [ - {'target': 'Cool Actuator', 'source': 'Cool PID', 'intermediate': ['Cool PWM']}, - {'target': 'Cool PWM', 'source': 'Cool PID', 'intermediate': []}, - {'target': 'Heat Actuator', 'source': 'Heat PID', 'intermediate': ['Heat PWM']}, - {'target': 'Heat PWM', 'source': 'Heat PID', 'intermediate': []}, - {'target': 'Spark Pins', 'source': 'Cool PID', 'intermediate': ['Cool Actuator', 'Cool PWM']}, - {'target': 'Spark Pins', 'source': 'Heat PID', 'intermediate': ['Heat Actuator', 'Heat PWM']}, + BlockClaim(target='Cool Actuator', source='Cool PID', intermediate=['Cool PWM']), + BlockClaim(target='Cool PWM', source='Cool PID', intermediate=[]), + BlockClaim(target='Heat Actuator', source='Heat PID', intermediate=['Heat PWM']), + BlockClaim(target='Heat PWM', source='Heat PID', intermediate=[]), + BlockClaim(target='Spark Pins', source='Cool PID', intermediate=['Cool Actuator', 'Cool PWM']), + BlockClaim(target='Spark Pins', source='Heat PID', intermediate=['Heat Actuator', 'Heat PWM']), ] @@ -342,9 +360,9 @@ def test_calculate_circular_claims(): ), ] result = block_analysis.calculate_claims(blocks) - result = sorted(result, key=lambda v: v['target']) + result = sorted(result, key=lambda v: v.target) assert result == [ - {'target': 'block-1', 'source': 'block-1', 'intermediate': ['block-3', 'block-2']}, - {'target': 'block-2', 'source': 'block-2', 'intermediate': ['block-1', 'block-3']}, - {'target': 'block-3', 'source': 'block-3', 'intermediate': ['block-2', 'block-1']}, + BlockClaim(target='block-1', source='block-1', intermediate=['block-3', 'block-2']), + BlockClaim(target='block-2', source='block-2', intermediate=['block-1', 'block-3']), + BlockClaim(target='block-3', source='block-3', intermediate=['block-2', 'block-1']), ] diff --git a/test/test_block_backup.py b/test/test_block_backup.py new file mode 100644 index 00000000..5e88a66e --- /dev/null +++ b/test/test_block_backup.py @@ -0,0 +1,107 @@ +""" +Tests brewblox_devcon_spark.backup +""" + +import asyncio +from contextlib import AsyncExitStack, asynccontextmanager +from datetime import timedelta + +import pytest +from asgi_lifespan import LifespanManager +from fastapi import FastAPI +from pytest_mock import MockerFixture + +from brewblox_devcon_spark import (block_backup, codec, command, connection, + control, datastore_blocks, + datastore_settings, mqtt, state_machine, + synchronization, utils) +from brewblox_devcon_spark.models import Backup, BackupIdentity + +TESTED = block_backup.__name__ + + +@asynccontextmanager +async def lifespan(app: FastAPI): + async with AsyncExitStack() as stack: + await stack.enter_async_context(mqtt.lifespan()) + await stack.enter_async_context(connection.lifespan()) + await stack.enter_async_context(synchronization.lifespan()) + yield + + +@pytest.fixture +def app() -> FastAPI(): + config = utils.get_config() + config.backup_interval = timedelta(milliseconds=1) + config.backup_retry_interval = timedelta(milliseconds=1) + + mqtt.setup() + state_machine.setup() + datastore_settings.setup() + datastore_blocks.setup() + codec.setup() + connection.setup() + command.setup() + control.setup() + block_backup.setup() + return FastAPI(lifespan=lifespan) + + +@pytest.fixture(autouse=True) +async def manager(manager: LifespanManager): + yield manager + + +@pytest.fixture(autouse=True) +async def synchronized(manager: LifespanManager): + await state_machine.CV.get().wait_synchronized() + + +async def test_inactive(): + storage = block_backup.CV.get() + config = utils.get_config() + config.backup_interval = timedelta() + config.backup_retry_interval = timedelta() + + # Early exit when backup_interval <= 0 + await asyncio.wait_for(storage.repeat(), timeout=1) + + +async def test_autosave(mocker: MockerFixture): + state = state_machine.CV.get() + storage = block_backup.CV.get() + ctrl = control.CV.get() + + await storage.run() + + stored = await storage.all() + assert len(stored) == 1 + assert isinstance(stored[0], BackupIdentity) + + data = await storage.read(stored[0]) + assert isinstance(data, Backup) + + m_make = mocker.patch.object(ctrl, 'make_backup', autospec=True) + m_make.return_value = Backup(blocks=[], store=[]) + + async with utils.task_context(storage.repeat()) as task: + await asyncio.sleep(0.1) + assert m_make.call_count > 0 + + m_make.side_effect = RuntimeError + m_make.reset_mock() + await asyncio.sleep(0.01) + assert m_make.call_count > 0 + assert not task.done() + + with pytest.raises(RuntimeError): + await storage.run() + + # Synchronized is checked before controller call + # run() exits before the RuntimeError is raised + state.set_disconnected() + await storage.run() + + # No new entries were added + stored = await storage.all() + assert len(stored) == 1 diff --git a/test/test_block_store.py b/test/test_block_store.py deleted file mode 100644 index cc370a49..00000000 --- a/test/test_block_store.py +++ /dev/null @@ -1,150 +0,0 @@ -""" -Tests brewblox_devcon_spark.block_store -""" - - -import asyncio - -import pytest -from aiohttp import web -from aresponses import ResponsesMockServer -from brewblox_service import http, scheduler - -from brewblox_devcon_spark import block_store, const, datastore -from brewblox_devcon_spark.models import ServiceConfig - -TESTED = block_store.__name__ -DATASTORE = datastore.__name__ - - -def read_objects(): - return block_store.SYS_OBJECTS[:1] + [ - {'keys': [f'key{i}', i+const.USER_NID_START], 'data': {}} - for i in range(10) - ] - - -def add_block_read_resp(aresponses, count, status=200): - async def handler(request): - return web.json_response({ - 'value': { - 'id': 'XXXX', - 'namespace': const.SPARK_NAMESPACE, - 'data': read_objects(), - }}, - status=status) - aresponses.add(path_pattern='/history/datastore/get', - method_pattern='POST', - response=handler, - repeat=count) - - -def add_write_resp(aresponses: ResponsesMockServer, count, status=200): - async def handler(request): - return web.json_response(await request.json(), status=status) - aresponses.add(path_pattern='/history/datastore/set', - method_pattern='POST', - response=handler, - repeat=count) - - -@pytest.fixture -async def setup(app, mocker): - mocker.patch(DATASTORE + '.FLUSH_DELAY_S', 0.01) - mocker.patch(DATASTORE + '.RETRY_INTERVAL_S', 0.01) - - config: ServiceConfig = app['config'] - config.isolated = False - http.setup(app) - scheduler.setup(app) - block_store.setup(app) - - -@pytest.fixture -def store(app): - return block_store.fget(app) - - -async def test_block_read(app, client, store, aresponses): - assert store.active - assert not store.isolated - - default_length = len(block_store.SYS_OBJECTS) - read_length = default_length + len(read_objects()) - 1 # overlapping item is merged - - assert len(store.items()) == default_length - - add_block_read_resp(aresponses, 1) - add_write_resp(aresponses, 1) - await store.read('device-id') - assert len(store.items()) == read_length - - # Defaults were added to read objects - # Give them time to flush - await asyncio.sleep(0.05) - - aresponses.assert_plan_strictly_followed() - - -async def test_block_write(app, client, store, aresponses): - default_length = len(block_store.SYS_OBJECTS) - read_length = default_length + len(read_objects()) - 1 # overlapping item is merged - - # Read and let flush - add_block_read_resp(aresponses, 1) - add_write_resp(aresponses, 1) - await store.read('device-id') - await asyncio.sleep(0.05) - - # write on add - add_write_resp(aresponses, 1) - store['inserted', 9001] = 'val' - assert len(store.items()) == read_length + 1 - await asyncio.sleep(0.05) - - # write on delete - add_write_resp(aresponses, 1) - del store['inserted', 9001] - assert len(store.items()) == read_length - await asyncio.sleep(0.05) - - aresponses.assert_plan_strictly_followed() - - -async def test_block_read_error(app, client, store, aresponses): - add_block_read_resp(aresponses, 1, 500) - - with pytest.warns(UserWarning, match='read error'): - await store.read('device-id') - - assert store.key is None - - with pytest.warns(UserWarning, match='flush error'): - await asyncio.sleep(0.05) - - # reset to normal - add_block_read_resp(aresponses, 1) - add_write_resp(aresponses, 1) - await store.read('device-id') - await asyncio.sleep(0.05) - - aresponses.assert_plan_strictly_followed() - - -@pytest.mark.filterwarnings('ignore: flush error') -async def test_block_write_error(app, client, store, aresponses): - add_block_read_resp(aresponses, 1) - add_write_resp(aresponses, 1) - await store.read('device-id') - await asyncio.sleep(0.05) - - # continue on write error - add_write_resp(aresponses, 1000, status=500) - with pytest.warns(UserWarning, match='flush error'): - store['inserted2', 9002] = 'val' - await asyncio.sleep(0.05) - - aresponses.assert_all_requests_matched() - aresponses.assert_called_in_order() - assert len(aresponses.history) > 3 - await store.shutdown(app) diff --git a/test/test_blocks_mqtt_api.py b/test/test_blocks_mqtt_api.py new file mode 100644 index 00000000..a1592f9d --- /dev/null +++ b/test/test_blocks_mqtt_api.py @@ -0,0 +1,160 @@ +""" +Tests brewblox_devcon_spark.api.blocks_mqtt_api +""" + +import asyncio +from contextlib import AsyncExitStack, asynccontextmanager + +import pytest +from asgi_lifespan import LifespanManager +from fastapi import FastAPI +from pytest_mock import MockerFixture + +from brewblox_devcon_spark import (codec, command, connection, control, + datastore_blocks, datastore_settings, + exceptions, mqtt, state_machine, + synchronization, utils) +from brewblox_devcon_spark.api import blocks_mqtt_api +from brewblox_devcon_spark.models import Block, BlockIdentity + +TESTED = blocks_mqtt_api.__name__ + + +@asynccontextmanager +async def lifespan(app: FastAPI): + async with AsyncExitStack() as stack: + await stack.enter_async_context(mqtt.lifespan()) + await stack.enter_async_context(connection.lifespan()) + await stack.enter_async_context(synchronization.lifespan()) + yield + + +@pytest.fixture +def app() -> FastAPI: + config = utils.get_config() + config.mock = True + + mqtt.setup() + state_machine.setup() + datastore_settings.setup() + datastore_blocks.setup() + codec.setup() + connection.setup() + command.setup() + control.setup() + blocks_mqtt_api.setup() + return FastAPI(lifespan=lifespan) + + +@pytest.fixture(autouse=True) +async def manager(manager: LifespanManager): + yield manager + + +def block_args(): + config = utils.get_config() + return Block( + id='testobj', + serviceId=config.name, + type='TempSensorOneWire', + data={ + 'value': 12345, + 'offset': 20, + 'address': 'FF' + } + ) + + +async def test_crud(mocker: MockerFixture): + config = utils.get_config() + mqtt_client = mqtt.CV.get() + ctrl = control.CV.get() + + def publish(endpoint: str, args: Block | BlockIdentity): + mqtt_client.publish(config.blocks_topic + endpoint, args.model_dump(mode='json')) + + def wrap(name: str) -> asyncio.Event: + """ + Transparently wraps function in controller. + As a side effect, an event is set. + This allows us to get a callback on function call. + """ + func = getattr(ctrl, name) + ev = asyncio.Event() + + async def wrapper(*args, **kwargs): + retv = await func(*args, **kwargs) + ev.set() + return retv + + setattr(ctrl, name, wrapper) + return ev + + create_ev = wrap('create_block') + write_ev = wrap('write_block') + patch_ev = wrap('patch_block') + delete_ev = wrap('delete_block') + + dummy = Block( + id='dummy', + serviceId='other', + type='TempSensorOneWire', + data={ + 'value': 12345, + 'offset': 20, + 'address': 'FF' + } + ) + + real = Block( + id='real', + serviceId=config.name, + type='TempSensorOneWire', + data={ + 'value': 12345, + 'offset': 20, + 'address': 'FF' + } + ) + + publish('/create', dummy) + publish('/create', real) + await asyncio.wait_for(create_ev.wait(), timeout=5) + create_ev.clear() + + assert await ctrl.read_block(BlockIdentity(id=real.id)) + with pytest.raises(exceptions.UnknownId): + await ctrl.read_block(BlockIdentity(id=dummy.id)) + + publish('/write', dummy) + publish('/write', real) + await asyncio.wait_for(write_ev.wait(), timeout=5) + write_ev.clear() + + assert await ctrl.read_block(BlockIdentity(id=real.id)) + with pytest.raises(exceptions.UnknownId): + await ctrl.read_block(BlockIdentity(id=dummy.id)) + + publish('/patch', dummy) + publish('/patch', real) + await asyncio.wait_for(patch_ev.wait(), timeout=5) + patch_ev.clear() + + assert await ctrl.read_block(BlockIdentity(id=real.id)) + with pytest.raises(exceptions.UnknownId): + await ctrl.read_block(BlockIdentity(id=dummy.id)) + + publish('/delete', dummy) + publish('/delete', real) + await asyncio.wait_for(delete_ev.wait(), timeout=5) + delete_ev.clear() + + with pytest.raises(exceptions.UnknownId): + assert await ctrl.read_block(BlockIdentity(id=real.id)) + with pytest.raises(exceptions.UnknownId): + await ctrl.read_block(BlockIdentity(id=dummy.id)) + + assert not create_ev.is_set() + assert not write_ev.is_set() + assert not patch_ev.is_set() + assert not delete_ev.is_set() diff --git a/test/test_broadcast.py b/test/test_broadcast.py new file mode 100644 index 00000000..5ea17a6a --- /dev/null +++ b/test/test_broadcast.py @@ -0,0 +1,118 @@ +""" +Tests brewblox_devcon_spark.broadcast +""" + +import asyncio +from contextlib import AsyncExitStack, asynccontextmanager +from datetime import timedelta +from unittest.mock import ANY, Mock, call + +import pytest +from asgi_lifespan import LifespanManager +from fastapi import FastAPI +from pytest_mock import MockerFixture + +from brewblox_devcon_spark import (broadcast, codec, command, connection, + control, datastore_blocks, + datastore_settings, exceptions, mqtt, + state_machine, synchronization, utils) +from brewblox_devcon_spark.connection import mock_connection +from brewblox_devcon_spark.models import ErrorCode + +TESTED = broadcast.__name__ + + +@asynccontextmanager +async def lifespan(app: FastAPI): + async with AsyncExitStack() as stack: + await stack.enter_async_context(mqtt.lifespan()) + await stack.enter_async_context(connection.lifespan()) + await stack.enter_async_context(synchronization.lifespan()) + yield + + +@pytest.fixture +def app() -> FastAPI(): + config = utils.get_config() + config.mock = True + config.broadcast_interval = timedelta(milliseconds=1) + + mqtt.setup() + state_machine.setup() + datastore_settings.setup() + datastore_blocks.setup() + codec.setup() + connection.setup() + command.setup() + control.setup() + return FastAPI(lifespan=lifespan) + + +@pytest.fixture(autouse=True) +async def manager(manager: LifespanManager): + yield manager + + +@pytest.fixture(autouse=True) +async def synchronized(manager: LifespanManager): + await state_machine.CV.get().wait_synchronized() + + +@pytest.fixture +def s_publish(mocker: MockerFixture): + mqtt_client = mqtt.CV.get() + m = mocker.spy(mqtt_client, 'publish') + return m + + +async def test_broadcast_unsync(s_publish: Mock): + state = state_machine.CV.get() + state._synchronized_ev.clear() + + b = broadcast.Broadcaster() + await b.run() + assert s_publish.call_count == 1 + + +async def test_broadcast(s_publish: Mock): + config = utils.get_config() + b = broadcast.Broadcaster() + await b.run() + + s_publish.assert_has_calls([ + call('brewcast/history/sparkey', ANY), + call('brewcast/state/sparkey', ANY, retain=True), + ]) + s_publish.reset_mock() + + async with broadcast.lifespan(): + mock_connection.NEXT_ERROR.append(ErrorCode.UNKNOWN_ERROR) + await asyncio.sleep(0.2) + assert s_publish.call_count > 1 + + # Early exit if interval <= 0 + s_publish.reset_mock() + config.broadcast_interval = timedelta() + async with utils.task_context(b.repeat()) as task: + await asyncio.sleep(0.1) + assert task.done() + assert s_publish.call_count == 0 + + +async def test_error(s_publish: Mock): + b = broadcast.Broadcaster() + mock_connection.NEXT_ERROR.append(ErrorCode.UNKNOWN_ERROR) + with pytest.raises(exceptions.CommandException): + await b.run() + + # Error over, resume normal work + # 1 * only state event + # 1 * history + state + await b.run() + assert s_publish.call_count == 3 + + +async def test_api_broadcaster(s_publish: Mock): + b = broadcast.Broadcaster() + await b.run() + assert s_publish.call_count == 2 diff --git a/test/test_broadcaster.py b/test/test_broadcaster.py deleted file mode 100644 index ce48c655..00000000 --- a/test/test_broadcaster.py +++ /dev/null @@ -1,128 +0,0 @@ -""" -Tests brewblox_devcon_spark.broadcaster -""" - -from unittest.mock import ANY, Mock, call - -import pytest -from brewblox_service import mqtt, repeater, scheduler -from pytest_mock import MockerFixture - -from brewblox_devcon_spark import (block_store, broadcaster, codec, commander, - connection, controller, exceptions, - global_store, service_status, service_store, - synchronization) -from brewblox_devcon_spark.connection import mock_connection -from brewblox_devcon_spark.models import ErrorCode, ServiceConfig - -TESTED = broadcaster.__name__ - - -@pytest.fixture(autouse=True) -def m_relations(mocker): - mocker.patch(TESTED + '.calculate_relations', - autospec=True, - side_effect=lambda blocks: {}) - mocker.patch(TESTED + '.calculate_claims', - autospec=True, - side_effect=lambda blocks: {}) - - -@pytest.fixture -async def setup(app, broker): - config: ServiceConfig = app['config'] - config.broadcast_interval = 0.01 - config.mqtt_host = 'localhost' - config.mqtt_port = broker['mqtt'] - config.history_topic = 'testcast/history' - config.state_topic = 'testcast/state' - - service_status.setup(app) - scheduler.setup(app) - mqtt.setup(app) - codec.setup(app) - block_store.setup(app) - global_store.setup(app) - service_store.setup(app) - connection.setup(app) - commander.setup(app) - synchronization.setup(app) - controller.setup(app) - - -@pytest.fixture -async def synchronized(app, client): - await service_status.wait_synchronized(app) - - -@pytest.fixture -def m_publish(app, mocker: MockerFixture): - m = mocker.spy(mqtt, 'publish') - return m - - -async def test_disabled(app, client, synchronized): - app['config'].broadcast_interval = 0 - app['config'].isolated = False - b = broadcaster.Broadcaster(app) - with pytest.raises(repeater.RepeaterCancelled): - await b.prepare() - - -async def test_broadcast_unsync(app, client, synchronized, m_publish: Mock): - service_status.fget(app).synchronized_ev.clear() - - app['config'].isolated = False - b = broadcaster.Broadcaster(app) - await b.prepare() - await b.run() - assert m_publish.call_count == 1 - - -async def test_broadcast(app, client, synchronized, m_publish: Mock): - app['config'].isolated = False - - b = broadcaster.Broadcaster(app) - await b.prepare() - await b.run() - - m_publish.assert_has_calls([ - call(app, - topic='testcast/history/test_app', - payload=ANY, - err=False, - ), - call(app, - topic='testcast/state/test_app', - payload=ANY, - retain=True, - err=False, - ), - ]) - - await b.before_shutdown(app) - - -async def test_error(app, client, synchronized, m_publish: Mock): - app['config'].isolated = False - b = broadcaster.Broadcaster(app) - await b.prepare() - - mock_connection.NEXT_ERROR.append(ErrorCode.UNKNOWN_ERROR) - with pytest.raises(exceptions.CommandException): - await b.run() - - # Error over, resume normal work - # 1 * only state event - # 1 * history + state - await b.run() - assert m_publish.call_count == 3 - - -async def test_api_broadcaster(app, client, m_publish: Mock): - await service_status.wait_synchronized(app) - app['config'].isolated = False - b = broadcaster.Broadcaster(app) - await b.prepare() - await b.run() - assert m_publish.call_count == 2 diff --git a/test/test_cbox_parser.py b/test/test_cbox_parser.py index a83a7d94..db8820a1 100644 --- a/test/test_cbox_parser.py +++ b/test/test_cbox_parser.py @@ -3,7 +3,7 @@ """ -from brewblox_devcon_spark.connection.cbox_parser import ControlboxParser +from brewblox_devcon_spark.connection.cbox_parser import CboxParser def serial_data(): @@ -43,7 +43,7 @@ def expected_data(): def test_parser(): - parser = ControlboxParser() + parser = CboxParser() actual_events = [] actual_data = [] @@ -58,7 +58,7 @@ def test_parser(): def test_parser_partial(): - parser = ControlboxParser() + parser = CboxParser() chunks = serial_data() parser.push(chunks[0]) diff --git a/test/test_codec.py b/test/test_codec.py index 8c9c516d..a5e2c3b7 100644 --- a/test/test_codec.py +++ b/test/test_codec.py @@ -2,11 +2,12 @@ Tests brewblox codec """ +from contextlib import asynccontextmanager + import pytest -from brewblox_service import scheduler +from fastapi import FastAPI -from brewblox_devcon_spark import (codec, connection, exceptions, - service_status, service_store) +from brewblox_devcon_spark import codec, connection, exceptions from brewblox_devcon_spark.codec import (Codec, DecodeOpts, MetadataOpt, ProtoEnumOpt) from brewblox_devcon_spark.models import (DecodedPayload, EncodedPayload, @@ -15,18 +16,18 @@ TEMP_SENSOR_TYPE_INT = 302 -@pytest.fixture -async def setup(app): - service_status.setup(app) - scheduler.setup(app) - codec.setup(app) - service_store.setup(app) - connection.setup(app) +@asynccontextmanager +async def lifespan(app: FastAPI): + async with connection.lifespan(): + yield -@pytest.fixture -def cdc(app) -> Codec: - return codec.fget(app) +@pytest.fixture(autouse=True) +def app() -> FastAPI: + # state_machine.setup() + codec.setup() + # connection.setup(app) + return FastAPI() async def test_type_conversion(): @@ -39,7 +40,9 @@ async def test_type_conversion(): assert codec.join_type(*split) == joined -async def test_encode_system_objects(app, client, cdc: Codec): +async def test_encode_system_objects(): + cdc = codec.CV.get() + types = [ 'SysInfo', 'OneWireBus', @@ -56,7 +59,9 @@ async def test_encode_system_objects(app, client, cdc: Codec): assert encoded -async def test_encode_errors(app, client, cdc: Codec): +async def test_encode_errors(): + cdc = codec.CV.get() + with pytest.raises(exceptions.EncodeException): cdc.encode_request({}) @@ -77,7 +82,9 @@ async def test_encode_errors(app, client, cdc: Codec): )) -async def test_decode_errors(app, client, cdc: Codec): +async def test_decode_errors(): + cdc = codec.CV.get() + with pytest.raises(exceptions.DecodeException): cdc.decode_request('Is this just fantasy?') @@ -99,7 +106,9 @@ async def test_decode_errors(app, client, cdc: Codec): assert error_object.blockType == 'UnknownType' -async def test_deprecated_object(app, client, cdc: Codec): +async def test_deprecated_object(): + cdc = codec.CV.get() + payload = cdc.encode_payload(DecodedPayload( blockId=1, blockType='DeprecatedObject', @@ -113,7 +122,9 @@ async def test_deprecated_object(app, client, cdc: Codec): assert payload.content == {'actualId': 100} -async def test_encode_constraint(app, client, cdc: Codec): +async def test_encode_constraint(): + cdc = codec.CV.get() + assert cdc.decode_payload(EncodedPayload( blockId=1, blockType='ActuatorPwm', @@ -133,7 +144,9 @@ async def test_encode_constraint(app, client, cdc: Codec): )) -async def test_encode_delta_sec(app, client, cdc: Codec): +async def test_encode_delta_sec(): + cdc = codec.CV.get() + # Check whether [delta_temperature / time] can be converted payload = cdc.encode_payload(DecodedPayload( blockId=1, @@ -144,7 +157,9 @@ async def test_encode_delta_sec(app, client, cdc: Codec): assert payload.content['deltaV[delta_degC / second]'] == pytest.approx(100, 0.1) -async def test_encode_submessage(app, client, cdc: Codec): +async def test_encode_submessage(): + cdc = codec.CV.get() + payload = cdc.encode_payload(DecodedPayload( blockId=1, blockType='EdgeCase', @@ -169,7 +184,9 @@ async def test_encode_submessage(app, client, cdc: Codec): assert payload.blockType == 'EdgeCase' -async def test_transcode_interfaces(app, client, cdc: Codec): +async def test_transcode_interfaces(): + cdc = codec.CV.get() + for type in [ 'EdgeCase', 'BalancerInterface', @@ -184,8 +201,10 @@ async def test_transcode_interfaces(app, client, cdc: Codec): assert payload.blockType == type -async def test_exclusive_mask(app, client, cdc: Codec): - rw_cdc = Codec(app, strip_readonly=False) +async def test_exclusive_mask(): + cdc = codec.CV.get() + rw_cdc = Codec(strip_readonly=False) + enc_payload = rw_cdc.encode_payload(DecodedPayload( blockId=1, blockType='EdgeCase', @@ -208,7 +227,9 @@ async def test_exclusive_mask(app, client, cdc: Codec): assert payload.content['deltaV[delta_degC / second]'] is None -async def test_postfixed_decoding(app, client, cdc: Codec): +async def test_postfixed_decoding(): + cdc = codec.CV.get() + payload = cdc.encode_payload(DecodedPayload( blockId=1, blockType='EdgeCase', @@ -224,7 +245,9 @@ async def test_postfixed_decoding(app, client, cdc: Codec): assert payload.content['state']['value[degC]'] == pytest.approx(10, 0.01) -async def test_ipv4_encoding(app, client, cdc: Codec): +async def test_ipv4_encoding(): + cdc = codec.CV.get() + payload = cdc.encode_payload(DecodedPayload( blockId=1, blockType='EdgeCase', @@ -236,7 +259,9 @@ async def test_ipv4_encoding(app, client, cdc: Codec): assert payload.content['ip'] == '192.168.0.1' -async def test_point_presence(app, client, cdc: Codec): +async def test_point_presence(): + cdc = codec.CV.get() + present_payload = cdc.encode_payload(DecodedPayload( blockId=1, blockType='SetpointProfile', @@ -265,7 +290,9 @@ async def test_point_presence(app, client, cdc: Codec): assert present_payload.content['points'][0]['time']['value'] == 0 -async def test_enum_decoding(app, client, cdc: Codec): +async def test_enum_decoding(): + cdc = codec.CV.get() + encoded_payload = cdc.encode_payload(DecodedPayload( blockId=1, blockType='DigitalActuator', @@ -292,7 +319,9 @@ async def test_enum_decoding(app, client, cdc: Codec): assert payload.content['storedState'] == 1 -async def test_invalid_if_decoding(app, client, cdc: Codec): +async def test_invalid_if_decoding(): + cdc = codec.CV.get() + encoded_payload = cdc.encode_payload(DecodedPayload( blockId=1, blockType='EdgeCase', diff --git a/test/test_command.py b/test/test_command.py new file mode 100644 index 00000000..791f528e --- /dev/null +++ b/test/test_command.py @@ -0,0 +1,97 @@ +""" +Tests brewblox_devcon_spark.command +""" + +import asyncio +from contextlib import asynccontextmanager +from datetime import timedelta + +import pytest +from asgi_lifespan import LifespanManager +from fastapi import FastAPI + +from brewblox_devcon_spark import (codec, command, connection, state_machine, + utils) +from brewblox_devcon_spark.connection import connection_handler +from brewblox_devcon_spark.models import ErrorCode, IntermediateResponse + +TESTED = command.__name__ + + +@asynccontextmanager +async def lifespan(app: FastAPI): + async with connection_handler.lifespan(): + yield + + +@pytest.fixture(autouse=True) +def app() -> FastAPI: + config = utils.get_config() + config.command_timeout = timedelta(seconds=1) + + state_machine.setup() + codec.setup() + connection_handler.setup() + command.setup() + return FastAPI(lifespan=lifespan) + + +async def test_acknowledge(manager: LifespanManager): + welcome = ','.join([ + '!BREWBLOX', + 'ed70d66f0', + '3f2243a', + '2019-06-18', + '2019-06-18', + '1.2.1-rc.2', + 'p1', + '78', + '0A', + '1234567F0CASE' + ]) + state = state_machine.CV.get() + conn = connection.CV.get() + + state.set_enabled(True) + await asyncio.wait_for(state.wait_connected(), timeout=5) + assert not state.is_acknowledged() + + await conn.on_event(welcome) + assert state.desc().firmware_error == 'INCOMPATIBLE' + + assert state.is_acknowledged() + assert state.desc().controller.device.device_id == '1234567f0case' + + +async def test_unexpected_event(caplog: pytest.LogCaptureFixture): + await connection.CV.get().on_event('hello world!') + record = caplog.records[-1] + assert record.levelname == 'INFO' + assert 'hello world' in record.message + + +async def test_unexpected_response(caplog: pytest.LogCaptureFixture): + response = IntermediateResponse( + msgId=123, + error=ErrorCode.OK, + payload=[] + ) + message = codec.CV.get().encode_response(response) + await connection.CV.get().on_response(message) + + record = caplog.records[-1] + assert record.levelname == 'ERROR' + assert 'Unexpected message' in record.message + + +async def test_firmware_update_call(manager: LifespanManager): + # We don't unit test OTA update logic because it makes very in-depth assumptions + # about how particle devices respond to YMODEM calls + # We'll check now whether the basic call works + state = state_machine.CV.get() + cmdr = command.CV.get() + + state.set_enabled(True) + await asyncio.wait_for(state.wait_connected(), timeout=5) + await cmdr.firmware_update() + await cmdr.wait_empty() diff --git a/test/test_commander.py b/test/test_commander.py deleted file mode 100644 index 81e16fef..00000000 --- a/test/test_commander.py +++ /dev/null @@ -1,80 +0,0 @@ -""" -Tests brewblox_devcon_spark.commander -""" - -import asyncio - -import pytest -from brewblox_service import scheduler -from brewblox_service.testing import matching - -from brewblox_devcon_spark import (codec, commander, connection, - service_status, service_store) -from brewblox_devcon_spark.models import (ErrorCode, IntermediateResponse, - ServiceConfig) - -TESTED = commander.__name__ - - -@pytest.fixture -async def setup(app): - config: ServiceConfig = app['config'] - config.command_timeout = 1 - - service_status.setup(app) - scheduler.setup(app) - service_store.setup(app) - codec.setup(app) - connection.setup(app) - commander.setup(app) - - -async def test_acknowledge(app, client): - welcome = ','.join([ - '!BREWBLOX', - 'ed70d66f0', - '3f2243a', - '2019-06-18', - '2019-06-18', - '1.2.1-rc.2', - 'p1', - '78', - '0A', - '1234567F0CASE' - ]) - service_status.set_enabled(app, True) - await asyncio.wait_for(service_status.wait_connected(app), timeout=5) - assert not service_status.is_acknowledged(app) - - with pytest.warns(UserWarning, match='incompatible device ID'): - await connection.fget(app).on_event(welcome) - - assert service_status.is_acknowledged(app) - assert service_status.desc(app).controller.device.device_id == '1234567f0case' - - -async def test_unexpected_event(app, client, mocker): - m_log_info = mocker.patch(TESTED + '.LOGGER.info', autospec=True) - await connection.fget(app).on_event('hello world!') - m_log_info.assert_called_with(matching(r'.*hello world!')) - - -async def test_unexpected_response(app, client, mocker): - m_log_error = mocker.patch(TESTED + '.LOGGER.error', autospec=True) - response = IntermediateResponse( - msgId=123, - error=ErrorCode.OK, - payload=[] - ) - message = codec.fget(app).encode_response(response) - await connection.fget(app).on_response(message) - m_log_error.assert_called_with(matching(r'.*Unexpected message')) - - -async def test_firmware_update_call(app, client, mocker): - # We don't unit test OTA update logic because it makes very in-depth assumptions - # about how particle devices respond to YMODEM calls - # We'll check now whether the basic call works - service_status.set_enabled(app, True) - await asyncio.wait_for(service_status.wait_connected(app), timeout=5) - await commander.fget(app).firmware_update() diff --git a/test/test_connection_handler.py b/test/test_connection_handler.py index 3ee156bd..7e6487a6 100644 --- a/test/test_connection_handler.py +++ b/test/test_connection_handler.py @@ -4,15 +4,17 @@ import asyncio -from unittest.mock import AsyncMock +from datetime import timedelta +from unittest.mock import AsyncMock, Mock import pytest -from brewblox_service import scheduler +from fastapi import FastAPI +from pytest_mock import MockerFixture -from brewblox_devcon_spark import exceptions, service_status, service_store +from brewblox_devcon_spark import exceptions, state_machine, utils from brewblox_devcon_spark.codec import unit_conversion from brewblox_devcon_spark.connection import connection_handler -from brewblox_devcon_spark.models import DiscoveryType, ServiceConfig +from brewblox_devcon_spark.models import DiscoveryType TESTED = connection_handler.__name__ @@ -36,26 +38,32 @@ def welcome_message(): ] -@pytest.fixture -async def setup(app): - service_status.setup(app) - scheduler.setup(app) - service_store.setup(app) - unit_conversion.setup(app) +@pytest.fixture(autouse=True) +def app() -> FastAPI: + state_machine.setup() + unit_conversion.setup() + return FastAPI() -async def test_calc_backoff(): - assert connection_handler.calc_backoff(0) == connection_handler.BASE_RECONNECT_DELAY_S - assert connection_handler.calc_backoff(None) == connection_handler.BASE_RECONNECT_DELAY_S - assert connection_handler.calc_backoff(1) == pytest.approx(2) - assert connection_handler.calc_backoff(2) == pytest.approx(3) - assert connection_handler.calc_backoff(10) == pytest.approx(15) - assert connection_handler.calc_backoff(60) == connection_handler.MAX_RECONNECT_DELAY_S +@pytest.mark.parametrize('value,expected', [ + (timedelta(), timedelta(seconds=2)), + (None, timedelta(seconds=2)), + (timedelta(seconds=1), timedelta(seconds=1, milliseconds=500)), + (timedelta(seconds=2), timedelta(seconds=3)), + (timedelta(seconds=10), timedelta(seconds=15)), + (timedelta(minutes=1), timedelta(seconds=30)), +]) +async def test_calc_interval(value: timedelta | None, expected: timedelta): + config = utils.get_config() + config.connect_interval = timedelta(seconds=2) + config.connect_interval_max = timedelta(seconds=30) + assert connection_handler.calc_interval(value) == expected -async def test_usb_compatible(app, client, mocker): - config: ServiceConfig = app['config'] - handler = connection_handler.ConnectionHandler(app) + +async def test_usb_compatible(): + config = utils.get_config() + handler = connection_handler.ConnectionHandler() # Set all relevant settings to default def reset(): @@ -65,6 +73,7 @@ def reset(): config.device_id = None config.simulation = False config.mock = False + config.discovery_timeout = timedelta(seconds=10) # The default is to enable USB reset() @@ -93,15 +102,18 @@ def reset(): assert not handler.usb_compatible -async def test_handler_discovery(app, client, mocker): - mocker.patch(TESTED + '.DISCOVERY_INTERVAL_S', 0) - - config: ServiceConfig = app['config'] +async def test_handler_discovery(mocker: MockerFixture): + config = utils.get_config() m_discover_usb: AsyncMock = mocker.patch(TESTED + '.discover_usb', autospec=True) m_discover_mdns: AsyncMock = mocker.patch(TESTED + '.discover_mdns', autospec=True) m_discover_mqtt: AsyncMock = mocker.patch(TESTED + '.discover_mqtt', autospec=True) def reset(): + config.discovery = DiscoveryType.all + config.device_id = '1234' + config.discovery_interval = timedelta() + config.discovery_timeout = timedelta(seconds=1) + m_discover_usb.reset_mock() m_discover_mdns.reset_mock() m_discover_mqtt.reset_mock() @@ -110,13 +122,13 @@ def reset(): m_discover_mdns.side_effect = None m_discover_mqtt.side_effect = None - handler = connection_handler.ConnectionHandler(app) + handler = connection_handler.ConnectionHandler() # Discovery order is USB -> mDNS -> MQTT config.discovery = DiscoveryType.all await handler.discover() - m_discover_usb.assert_awaited_once_with(app, handler) + m_discover_usb.assert_awaited_once_with(handler) m_discover_mdns.assert_not_awaited() m_discover_mqtt.assert_not_awaited() @@ -154,8 +166,7 @@ def reset(): reset() # Throw a timeout error after a while - mocker.patch(TESTED + '.DISCOVERY_TIMEOUT_S', 0.01) - + config.discovery_timeout = timedelta(milliseconds=1) config.discovery = DiscoveryType.all m_discover_usb.return_value = None m_discover_mdns.return_value = None @@ -165,8 +176,8 @@ def reset(): await handler.discover() -async def test_handler_connect_order(app, client, mocker): - config: ServiceConfig = app['config'] +async def test_handler_connect_order(mocker: MockerFixture): + config = utils.get_config() m_funcs: dict[str, AsyncMock] = { k: mocker.patch(f'{TESTED}.{k}', autospec=True) for k in [ @@ -187,7 +198,7 @@ def reset(): for f in m_funcs.values(): f.reset_mock() - handler = connection_handler.ConnectionHandler(app) + handler = connection_handler.ConnectionHandler() # Lowest prio: discovery # Discovery order is serial -> TCP -> MQTT @@ -200,7 +211,7 @@ def reset(): await handler.connect() - m_funcs['discover_usb'].assert_awaited_once_with(app, handler) + m_funcs['discover_usb'].assert_awaited_once_with(handler) for f in without('discover_usb'): f.assert_not_awaited() @@ -212,7 +223,7 @@ def reset(): await handler.connect() - m_funcs['connect_tcp'].assert_awaited_once_with(app, handler, 'hostface', 1234) + m_funcs['connect_tcp'].assert_awaited_once_with(handler, 'hostface', 1234) for f in without('connect_tcp'): f.assert_not_awaited() @@ -223,7 +234,7 @@ def reset(): await handler.connect() - m_funcs['connect_usb'].assert_awaited_once_with(app, handler, 'serialface') + m_funcs['connect_usb'].assert_awaited_once_with(handler, 'serialface') for f in without('connect_usb'): f.assert_not_awaited() @@ -234,7 +245,7 @@ def reset(): await handler.connect() - m_funcs['connect_simulation'].assert_awaited_once_with(app, handler) + m_funcs['connect_simulation'].assert_awaited_once_with(handler) for f in without('connect_simulation'): f.assert_not_awaited() @@ -245,96 +256,102 @@ def reset(): await handler.connect() - m_funcs['connect_mock'].assert_awaited_once_with(app, handler) + m_funcs['connect_mock'].assert_awaited_once_with(handler) for f in without('connect_mock'): f.assert_not_awaited() -async def test_handler_run(app, client): - handler = connection_handler.ConnectionHandler(app) +async def test_handler_run(): + config = utils.get_config() + state = state_machine.CV.get() + handler = connection_handler.ConnectionHandler() + handler.on_response = AsyncMock() + config.mock = True with pytest.raises(exceptions.NotConnected): await handler.send_request('') - runner = asyncio.create_task(handler.run()) - - service_status.set_enabled(app, True) - await asyncio.wait_for(service_status.wait_connected(app), - timeout=5) + async with utils.task_context(handler.run()) as task: + state.set_enabled(True) + await asyncio.wait_for(state.wait_connected(), + timeout=5) - # We're assuming here that mock_connection.send_request() - # immediately calls the on_response() callback - await handler.send_request('') - handler.on_response.assert_awaited_once() + # We're assuming here that mock_connection.send_request() + # immediately calls the on_response() callback + await handler.send_request('') + handler.on_response.assert_awaited_once() + assert not task.done() - assert not runner.done() - runner.cancel() +async def test_handler_disconnect(mocker: MockerFixture): + config = utils.get_config() + state = state_machine.CV.get() + handler = connection_handler.ConnectionHandler() -async def test_handler_disconnect(app, client, mocker): - handler = connection_handler.ConnectionHandler(app) + config.mock = True # can safely be called when not connected - await handler.start_reconnect() + await handler.reset() - service_status.set_enabled(app, True) + state.set_enabled(True) - runner = asyncio.create_task(handler.run()) - await asyncio.wait_for(service_status.wait_connected(app), - timeout=5) + async with utils.task_context(handler.repeat()) as task: + await asyncio.wait_for(state.wait_connected(), + timeout=5) - await handler.start_reconnect() + await handler.end() - # If connection signals closed, handler cleanly stops its run - # and calls service_status.set_disconnected() - await asyncio.wait_for(runner, timeout=5) - assert runner.exception() is None - assert service_status.is_disconnected(app) + # If connection signals closed, handler cleanly stops its run + # and calls state.set_disconnected() + await asyncio.wait([task], timeout=5) + assert task.exception() is None + assert state.is_disconnected() -async def test_handler_connect_error(app, client, mocker): - mocker.patch(TESTED + '.web.GracefulExit', DummyExit) - mocker.patch(TESTED + '.calc_backoff').return_value = 0.0001 +async def test_handler_connect_error(mocker: MockerFixture, m_kill: Mock): m_connect_mock: AsyncMock = mocker.patch(TESTED + '.connect_mock', autospec=True) m_connect_mock.side_effect = ConnectionRefusedError - service_status.set_enabled(app, True) + config = utils.get_config() + state = state_machine.CV.get() + handler = connection_handler.ConnectionHandler() - handler = connection_handler.ConnectionHandler(app) + config.mock = True + state.set_enabled(True) # Retry until attempts exhausted # This is a mock - it will not attempt to restart the service for _ in range(connection_handler.MAX_RETRY_COUNT * 2): await handler.run() + m_kill.assert_not_called() + # It immediately threw a connection abort once the retry count was exceeded assert m_connect_mock.await_count == connection_handler.MAX_RETRY_COUNT * 2 - 1 -async def test_handler_discovery_error(app, client, mocker): - mocker.patch(TESTED + '.web.GracefulExit', DummyExit) - mocker.patch(TESTED + '.DISCOVERY_INTERVAL_S', 0.001) - mocker.patch(TESTED + '.DISCOVERY_TIMEOUT_S', 0.01) - +async def test_handler_discovery_error(mocker: MockerFixture, m_kill: Mock): m_discover_usb = mocker.patch(TESTED + '.discover_usb', autospec=True) m_discover_usb.return_value = None - config: ServiceConfig = app['config'] + config = utils.get_config() config.mock = False config.simulation = False config.device_serial = None config.device_host = None config.discovery = DiscoveryType.usb + config.discovery_interval = timedelta() + config.discovery_timeout = timedelta(milliseconds=1) - service_status.set_enabled(app, True) + state = state_machine.CV.get() - handler = connection_handler.ConnectionHandler(app) - with pytest.raises(DummyExit): - await handler.run() + state.set_enabled(True) - assert service_store.get_reconnect_delay(app) > 0 - service_store.set_reconnect_delay(app, 0) + handler = connection_handler.ConnectionHandler() + m_kill.side_effect = RuntimeError + with pytest.raises(RuntimeError): + await handler.run() # No reboot is required when discovery does not involve USB m_discover_mqtt = mocker.patch(TESTED + '.discover_mqtt', autospec=True) diff --git a/test/test_controller.py b/test/test_control.py similarity index 60% rename from test/test_controller.py rename to test/test_control.py index 79970a15..49501fcc 100644 --- a/test/test_controller.py +++ b/test/test_control.py @@ -1,56 +1,71 @@ """ -Tests brewblox_devcon_spark.controller +Tests brewblox_devcon_spark.control """ import asyncio +from contextlib import AsyncExitStack, asynccontextmanager +from datetime import timedelta import pytest -from brewblox_service import scheduler - -from brewblox_devcon_spark import (block_store, codec, commander, connection, - const, controller, exceptions, global_store, - service_status, service_store, - synchronization) +from asgi_lifespan import LifespanManager +from fastapi import FastAPI +from pytest_mock import MockerFixture + +from brewblox_devcon_spark import (codec, command, connection, const, control, + datastore_blocks, datastore_settings, + exceptions, mqtt, state_machine, + synchronization, utils) from brewblox_devcon_spark.connection import mock_connection from brewblox_devcon_spark.models import (Block, BlockIdentity, ErrorCode, FirmwareBlock) -TESTED = controller.__name__ +TESTED = control.__name__ -@pytest.fixture -async def setup(app): - service_status.setup(app) - scheduler.setup(app) - codec.setup(app) - connection.setup(app) - commander.setup(app) - block_store.setup(app) - global_store.setup(app) - service_store.setup(app) - synchronization.setup(app) - controller.setup(app) +@asynccontextmanager +async def lifespan(app: FastAPI): + async with AsyncExitStack() as stack: + await stack.enter_async_context(mqtt.lifespan()) + await stack.enter_async_context(connection.lifespan()) + await stack.enter_async_context(synchronization.lifespan()) + yield @pytest.fixture -async def store(app, client): - return block_store.fget(app) +def app() -> FastAPI: + config = utils.get_config() + config.mock = True + + mqtt.setup() + state_machine.setup() + datastore_settings.setup() + datastore_blocks.setup() + codec.setup() + connection.setup() + command.setup() + control.setup() + return FastAPI(lifespan=lifespan) + + +@pytest.fixture(autouse=True) +async def manager(manager: LifespanManager): + yield manager async def test_merge(): - assert controller.merge( + assert control.merge( {}, {'a': True} ) == {'a': True} - assert controller.merge( + assert control.merge( {'a': False}, {'a': True} ) == {'a': True} - assert controller.merge( + assert control.merge( {'a': True}, {'b': True} ) == {'a': True, 'b': True} - assert controller.merge( + assert control.merge( {'nested': {'a': False, 'b': True}, 'second': {}}, {'nested': {'a': True}, 'second': 'empty'} ) == {'nested': {'a': True, 'b': True}, 'second': 'empty'} @@ -63,8 +78,8 @@ async def test_merge(): 'l12142|35234231', 'word'*50, ]) -async def test_validate_sid(sid, app, client): - controller.fget(app)._validate_sid(sid) +async def test_validate_sid(sid: str): + control.CV.get()._validate_sid(sid) @pytest.mark.parametrize('sid', [ @@ -80,17 +95,18 @@ async def test_validate_sid(sid, app, client): 'SparkPins', 'a;ljfoihoewr*&(%&^&*%*&^(*&^(', ]) -async def test_validate_sid_error(sid, app, client): +async def test_validate_sid_error(sid: str): with pytest.raises(exceptions.InvalidId): - controller.fget(app)._validate_sid(sid) + control.CV.get()._validate_sid(sid) + +async def test_to_firmware_block(): + store = datastore_blocks.CV.get() + ctrl = control.CV.get() -async def test_to_firmware_block(app, client, store): store['alias', 123] = dict() store['4-2', 24] = dict() - ctrl = controller.fget(app) - assert ctrl._to_firmware_block(Block(id='alias', type='', data={})).nid == 123 assert ctrl._to_firmware_block(Block(nid=840, type='', data={})).nid == 840 @@ -107,12 +123,13 @@ async def test_to_firmware_block(app, client, store): ctrl._to_firmware_block_identity(BlockIdentity()) -async def test_to_block(app, client, store): +async def test_to_block(): + store = datastore_blocks.CV.get() + ctrl = control.CV.get() + store['alias', 123] = dict() store['4-2', 24] = dict() - ctrl = controller.fget(app) - assert ctrl._to_block(FirmwareBlock(nid=123, type='', data={})).id == 'alias' # Service ID not found: create placeholder @@ -120,7 +137,10 @@ async def test_to_block(app, client, store): assert generated.id.startswith(const.GENERATED_ID_PREFIX) -async def test_resolve_data_ids(app, client, store): +async def test_resolve_data_ids(): + store = datastore_blocks.CV.get() + ctrl = control.CV.get() + store['eeney', 9001] = dict() store['miney', 9002] = dict() store['moo', 9003] = dict() @@ -144,9 +164,8 @@ def create_data(): }, } - ctrl = controller.fget(app) data = create_data() - controller.resolve_data_ids(data, ctrl._find_nid) + control.resolve_data_ids(data, ctrl._find_nid) assert data == { 'testval': 1, @@ -166,29 +185,30 @@ def create_data(): }, } - controller.resolve_data_ids(data, ctrl._find_sid) + control.resolve_data_ids(data, ctrl._find_sid) assert data == create_data() - controller.resolve_data_ids(data, ctrl._find_nid) + control.resolve_data_ids(data, ctrl._find_nid) data['input'] = 'eeney' with pytest.raises(exceptions.DecodeException): - controller.resolve_data_ids(data, ctrl._find_sid) + control.resolve_data_ids(data, ctrl._find_sid) -async def test_check_connection(app, client, mocker): - sim = connection.fget(app) - ctrl = controller.fget(app) - cmder = commander.fget(app) +async def test_check_connection(mocker: MockerFixture): + config = utils.get_config() + sim = connection.CV.get() + ctrl = control.CV.get() + cmder = command.CV.get() s_noop = mocker.spy(cmder, 'noop') - s_reconnect = mocker.spy(cmder, 'start_reconnect') + s_reset = mocker.spy(cmder, 'reset_connection') await ctrl.noop() await ctrl._check_connection() assert s_noop.await_count == 2 - assert s_reconnect.await_count == 0 + assert s_reset.await_count == 0 - cmder._timeout = 0.1 + config.command_timeout = timedelta(milliseconds=100) with pytest.raises(exceptions.CommandTimeout): mock_connection.NEXT_ERROR = [None] await ctrl.noop() @@ -201,17 +221,23 @@ async def test_check_connection(app, client, mocker): await ctrl.noop() await asyncio.sleep(0.01) - assert s_reconnect.await_count == 1 + assert s_reset.await_count == 1 # Should be a noop if not connected await sim.end() await ctrl._check_connection() assert s_noop.await_count == 6 + with pytest.raises(exceptions.ConnectionException): + await ctrl.noop() + + +async def test_start_update(): + state = state_machine.CV.get() + ctrl = control.CV.get() -async def test_start_update(app, client): - await service_status.wait_synchronized(app) - service_status.fget(app).set_updating() + await state.wait_synchronized() + state.set_updating() with pytest.raises(exceptions.UpdateInProgress): - await controller.fget(app).read_all_blocks() + await ctrl.read_all_blocks() diff --git a/test/test_datastore.py b/test/test_datastore.py deleted file mode 100644 index 46f16e22..00000000 --- a/test/test_datastore.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -Tests brewblox_devcon_spark.datastore -""" - -import asyncio - -import pytest -from aiohttp import web -from aresponses import ResponsesMockServer -from brewblox_service import http, scheduler - -from brewblox_devcon_spark import datastore -from brewblox_devcon_spark.models import ServiceConfig - -TESTED = datastore.__name__ - - -def add_check_resp(aresponses: ResponsesMockServer, count, status=200): - async def handler(request): - return web.json_response({'ping': 'pong'}, status=status) - aresponses.add(path_pattern='/history/datastore/ping', - method_pattern='GET', - response=handler, - repeat=count) - - -@pytest.fixture -async def setup(app, mocker): - mocker.patch(TESTED + '.FLUSH_DELAY_S', 0.01) - mocker.patch(TESTED + '.RETRY_INTERVAL_S', 0.01) - - config: ServiceConfig = app['config'] - config.isolated = False - - http.setup(app) - scheduler.setup(app) - - -async def test_check_remote(app, client, aresponses: ResponsesMockServer): - add_check_resp(aresponses, 10, 405) - add_check_resp(aresponses, 1) - await asyncio.wait_for(datastore.check_remote(app), timeout=1) - aresponses.assert_all_requests_matched() - - -async def test_cancel_check_remote(app, client, aresponses: ResponsesMockServer): - add_check_resp(aresponses, 100, 405) - # The function should respond to the CancelledError raised by wait_for() timeout - # Sadly, if this test fails, it will completely block test execution - with pytest.raises(asyncio.TimeoutError): - await asyncio.wait_for(datastore.check_remote(app), timeout=0.001) - - -async def test_non_isolated(app, event_loop): - class IsolatedTester(): - def __init__(self, isolated: bool): - self.isolated = isolated - - @datastore.non_isolated - async def func(self, val): - return val - - assert await IsolatedTester(True).func('testey') is None - assert await IsolatedTester(False).func('testey') == 'testey' diff --git a/test/test_datastore_blocks.py b/test/test_datastore_blocks.py new file mode 100644 index 00000000..88c3d392 --- /dev/null +++ b/test/test_datastore_blocks.py @@ -0,0 +1,128 @@ +""" +Tests brewblox_devcon_spark.datastore_blocks +""" + + +import asyncio +from datetime import timedelta +from unittest.mock import ANY + +import pytest +from asgi_lifespan import LifespanManager +from fastapi import FastAPI +from pytest_httpx import HTTPXMock + +from brewblox_devcon_spark import const, datastore_blocks, utils +from brewblox_devcon_spark.models import TwinKeyEntry + +TESTED = datastore_blocks.__name__ + + +def read_objects() -> list[TwinKeyEntry]: + return datastore_blocks.SYS_OBJECTS[:1] + [ + TwinKeyEntry(keys=(f'key{i}', i+const.USER_NID_START), data={}) + for i in range(10) + ] + + +@pytest.fixture +def app() -> FastAPI: + config = utils.get_config() + config.datastore_flush_delay = timedelta() + + datastore_blocks.setup() + return FastAPI() + + +@pytest.fixture(autouse=True) +async def manager(manager: LifespanManager): + yield manager + + +async def test_load_save(httpx_mock: HTTPXMock): + config = utils.get_config() + store = datastore_blocks.CV.get() + + default_length = len(datastore_blocks.SYS_OBJECTS) + read_length = default_length + len(read_objects()) - 1 # overlapping item is merged + + assert len(store.items()) == default_length + + # Can't save before loading + with pytest.raises(ValueError): + await store.save() + + # Shutdown does nothing, but also doesn't complain + await store.on_shutdown() + + # Invalid format + httpx_mock.add_response(url=f'{config.datastore_url}/get', + match_json={'id': '5678-blocks-db', + 'namespace': const.SERVICE_NAMESPACE}, + json={}) + + httpx_mock.add_response(url=f'{config.datastore_url}/get', + match_json={'id': '5678-blocks-db', + 'namespace': const.SERVICE_NAMESPACE}, + json={ + 'value': { + 'id': '5678-blocks-db', + 'namespace': const.SERVICE_NAMESPACE, + 'data': [v.model_dump(mode='json') + for v in read_objects()], + }, + }) + + httpx_mock.add_response(url=f'{config.datastore_url}/set', + match_json={ + 'value': { + 'id': '5678-blocks-db', + 'namespace': const.SERVICE_NAMESPACE, + 'data': ANY + }, + }) + + # First attempt gets invalid data, and falls back on defaults + await store.load('5678') + assert len(store.items()) == default_length + + await store.load('5678') + assert len(store.items()) == read_length + + async with utils.task_context(store.repeat()): + # Flush on insert + store['inserted', 9001] = {} + assert len(store.items()) == read_length + 1 + await asyncio.sleep(0) + + # Flush on remove + del store['inserted', 9001] + assert len(store.items()) == read_length + await asyncio.sleep(0) + + # Does nothing if not changed + await store.on_shutdown() + + assert len(httpx_mock.get_requests(url=f'{config.datastore_url}/set')) < 3 + + +async def test_load_error(httpx_mock: HTTPXMock): + config = utils.get_config() + store = datastore_blocks.CV.get() + + httpx_mock.add_response(url=f'{config.datastore_url}/get', + match_json={'id': '5678-blocks-db', + 'namespace': const.SERVICE_NAMESPACE}, + json={ + 'value': { + 'id': '5678-blocks-db', + 'namespace': const.SERVICE_NAMESPACE, + 'data': 'miniaturized giant hamsters from outer space', + }, + }) + + # Removed after load + store['inserted', 9001] = {} + + await store.load('5678') + assert len(store.items()) == len(datastore_blocks.SYS_OBJECTS) diff --git a/test/test_datastore_settings.py b/test/test_datastore_settings.py new file mode 100644 index 00000000..eb3e0dca --- /dev/null +++ b/test/test_datastore_settings.py @@ -0,0 +1,172 @@ +""" +Tests brewblox_devcon_spark.datastore_settings +""" + +import asyncio +from contextlib import asynccontextmanager +from datetime import timedelta + +import pytest +from asgi_lifespan import LifespanManager +from fastapi import FastAPI +from httpx import Request, Response +from pytest_httpx import HTTPXMock +from pytest_mock import MockerFixture + +from brewblox_devcon_spark import const, datastore_settings, mqtt, utils +from brewblox_devcon_spark.models import DatastoreSingleValueBox + +TESTED = datastore_settings.__name__ + + +@asynccontextmanager +async def lifespan(app: FastAPI): + async with mqtt.lifespan(): + yield + + +@pytest.fixture(autouse=True) +def app(mocker: MockerFixture) -> FastAPI: + config = utils.get_config() + config.datastore_fetch_timeout = timedelta(seconds=1) + + mqtt.setup() + datastore_settings.setup() + return FastAPI(lifespan=lifespan) + + +async def test_fetch_all(httpx_mock: HTTPXMock): + store = datastore_settings.CV.get() + config = utils.get_config() + + httpx_mock.add_response(url=f'{config.datastore_url}/get', + match_json={'id': config.name, + 'namespace': const.SERVICE_NAMESPACE}, + json={ + 'value': { + 'id': config.name, + 'namespace': const.SERVICE_NAMESPACE, + 'enabled': False, + }, + }) + + httpx_mock.add_response(url=f'{config.datastore_url}/get', + match_json={'id': const.GLOBAL_UNITS_ID, + 'namespace': const.GLOBAL_NAMESPACE}, + json={ + 'value': { + 'id': const.GLOBAL_UNITS_ID, + 'namespace': const.GLOBAL_NAMESPACE, + 'temperature': 'degF', + }, + }) + + httpx_mock.add_response(url=f'{config.datastore_url}/get', + match_json={'id': const.GLOBAL_TIME_ZONE_ID, + 'namespace': const.GLOBAL_NAMESPACE}, + json={ + 'value': { + 'id': const.GLOBAL_TIME_ZONE_ID, + 'namespace': const.GLOBAL_NAMESPACE, + 'name': 'Europe/Amsterdam', + 'posixValue': 'CET-1CEST,M3.5.0,M10.5.0/3', + }, + }) + + await store.fetch_all() + assert store.service_settings.enabled is False + assert store.unit_settings.temperature == 'degF' + assert store.timezone_settings.name == 'Europe/Amsterdam' + + +async def test_commit(httpx_mock: HTTPXMock): + config = utils.get_config() + store = datastore_settings.CV.get() + request_received = False + + async def request_callback(request: Request) -> Response: + assert request.url == f'{config.datastore_url}/set' + box = DatastoreSingleValueBox.model_validate_json(request.read()) + raw_value = box.value.model_dump() + assert raw_value['enabled'] is False + + nonlocal request_received + request_received = True + + return Response(status_code=200, json=box.model_dump(mode='json')) + + httpx_mock.add_callback(request_callback) + + store.service_settings.enabled = False + await store.commit_service_settings() + assert request_received + + +async def test_store_events(manager: LifespanManager): + config = utils.get_config() + mqtt_client = mqtt.CV.get() + store = datastore_settings.CV.get() + + service_evt_received = asyncio.Event() + global_evt_received = asyncio.Event() + + num_service_events = 0 + num_global_events = 0 + + async def service_evt_callback(): + assert store.service_settings.enabled is False + nonlocal num_service_events + num_service_events += 1 + service_evt_received.set() + + async def global_evt_callback(): + assert store.unit_settings.temperature == 'degF' + assert store.timezone_settings.name == 'Europe/Amsterdam' + nonlocal num_global_events + num_global_events += 1 + global_evt_received.set() + + store.service_settings_listeners.add(service_evt_callback) + store.global_settings_listeners.add(global_evt_callback) + + for _ in range(5): + mqtt_client.publish(f'brewcast/datastore/{const.GLOBAL_NAMESPACE}', + { + 'changed': [ + { + 'id': const.GLOBAL_UNITS_ID, + 'namespace': const.GLOBAL_NAMESPACE, + 'temperature': 'degF', + }, + { + 'id': const.GLOBAL_TIME_ZONE_ID, + 'namespace': const.GLOBAL_NAMESPACE, + 'name': 'Europe/Amsterdam', + 'posixValue': 'CET-1CEST,M3.5.0,M10.5.0/3', + } + ] + }) + + for _ in range(5): + mqtt_client.publish(f'brewcast/datastore/{const.SERVICE_NAMESPACE}', + { + 'changed': [ + { + 'id': config.name, + 'namespace': const.SERVICE_NAMESPACE, + 'enabled': False, + }, + { + 'id': 'spark-other', + 'namespace': const.SERVICE_NAMESPACE, + 'enabled': True, + } + ] + }) + + async with asyncio.timeout(10): + await service_evt_received.wait() + await global_evt_received.wait() + + assert num_global_events == 1 + assert num_service_events == 1 diff --git a/test/test_global_store.py b/test/test_global_store.py deleted file mode 100644 index 5027ad36..00000000 --- a/test/test_global_store.py +++ /dev/null @@ -1,123 +0,0 @@ -""" -Tests brewblox_devcon_spark.global_store -""" - - -import json -from unittest.mock import AsyncMock - -import pytest -from aiohttp import web -from aresponses import ResponsesMockServer -from brewblox_service import http, scheduler - -from brewblox_devcon_spark import const, global_store -from brewblox_devcon_spark.global_store import GlobalConfigStore -from brewblox_devcon_spark.models import ServiceConfig - -TESTED = global_store.__name__ - - -def add_read_resp(aresponses: ResponsesMockServer, count, status=200): - async def handler(request): - return web.json_response({ - 'values': [{ - 'id': const.GLOBAL_UNITS_ID, - 'namespace': const.GLOBAL_NAMESPACE, - 'temperature': 'degF', - }] - }, - status=status) - aresponses.add(path_pattern='/history/datastore/mget', - method_pattern='POST', - response=handler, - repeat=count) - - -@pytest.fixture(autouse=True) -def m_mqtt(mocker): - m = mocker.patch(TESTED + '.mqtt', autospec=True) - return m - - -@pytest.fixture -async def setup(app): - config: ServiceConfig = app['config'] - config.isolated = False - http.setup(app) - scheduler.setup(app) - global_store.setup(app) - - -@pytest.fixture -def store(app): - return global_store.fget(app) - - -async def test_read(app, client, store: GlobalConfigStore, aresponses): - add_read_resp(aresponses, 1) - - await store.read() - assert store.units['temperature'] == 'degF' - aresponses.assert_plan_strictly_followed() - - -async def test_read_error(app, client, store: GlobalConfigStore, aresponses): - add_read_resp(aresponses, 1, 405) - - await store.read() - assert store.units['temperature'] == 'degC' - aresponses.assert_plan_strictly_followed() - - -async def test_on_event(app, client, store: GlobalConfigStore): - cb = AsyncMock() - store.listeners.add(cb) - - # Empty - await store._on_event('topic', '{}') - assert store.units['temperature'] == 'degC' - cb.assert_not_awaited() - - # Unrelated - await store._on_event('topic', - json.dumps({'changed': [{ - 'id': 'imperialist_units', - 'namespace': const.GLOBAL_NAMESPACE, - 'temperature': 'degF', - }]})) - assert store.units['temperature'] == 'degC' - cb.assert_not_awaited() - - # Same - await store._on_event('topic', - json.dumps({'changed': [{ - 'id': const.GLOBAL_UNITS_ID, - 'namespace': const.GLOBAL_NAMESPACE, - 'temperature': 'degC', - }]})) - assert store.units['temperature'] == 'degC' - cb.assert_not_awaited() - - # Units changed - cb.reset_mock() - await store._on_event('topic', - json.dumps({'changed': [{ - 'id': const.GLOBAL_UNITS_ID, - 'namespace': const.GLOBAL_NAMESPACE, - 'temperature': 'degF', - }]})) - assert store.units['temperature'] == 'degF' - cb.assert_awaited_with() - - # Timezone changed - cb.reset_mock() - await store._on_event('topic', - json.dumps({'changed': [{ - 'id': const.GLOBAL_TIME_ZONE_ID, - 'namespace': const.GLOBAL_NAMESPACE, - 'name': 'Europe/Amsterdam', - 'posixValue': 'CET-1CEST,M3.5.0,M10.5.0/3', - }]})) - assert store.time_zone['name'] == 'Europe/Amsterdam' - cb.assert_awaited_with() diff --git a/test/test_integration.py b/test/test_integration.py index 35a09878..dd6dccbf 100644 --- a/test/test_integration.py +++ b/test/test_integration.py @@ -3,336 +3,428 @@ """ import asyncio -from shutil import rmtree -from unittest.mock import ANY, AsyncMock +from contextlib import AsyncExitStack, asynccontextmanager +from unittest.mock import ANY, Mock import pytest -from aiohttp.test_utils import TestClient -from brewblox_service import mqtt, scheduler -from brewblox_service.testing import find_free_port, response +from fastapi import FastAPI +from httpx import AsyncClient from pytest_mock import MockerFixture -from brewblox_devcon_spark import (backup_storage, block_store, codec, - commander, connection, const, controller, - global_store, service_status, service_store, - synchronization) -from brewblox_devcon_spark.__main__ import parse_ini +from brewblox_devcon_spark import (app_factory, block_backup, codec, command, + connection, const, control, + datastore_blocks, datastore_settings, mqtt, + state_machine, synchronization, utils) from brewblox_devcon_spark.api import (backup_api, blocks_api, debug_api, - error_response, settings_api, - system_api) -from brewblox_devcon_spark.connection import stream_connection -from brewblox_devcon_spark.models import (DecodedPayload, EncodedPayload, + settings_api, system_api) +from brewblox_devcon_spark.models import (Backup, Block, BlockIdentity, + DatastoreMultiQuery, DecodedPayload, + EncodedMessage, EncodedPayload, ErrorCode, IntermediateRequest, IntermediateResponse, Opcode, - ServiceConfig) + TwinKeyEntry) class DummmyError(BaseException): pass -def ret_ids(objects): - return {obj['id'] for obj in objects} +def ret_ids(objects: list[dict | Block]) -> set[str]: + try: + return {obj['id'] for obj in objects} + except TypeError: + return {obj.id for obj in objects} -@pytest.fixture(scope='module', autouse=True) -def simulator_file_cleanup(): - yield - rmtree('simulator/', ignore_errors=True) +def repeated_blocks(ids: list[str], base: Block) -> list[Block]: + return [Block(id=id, type=base.type, data=base.data) + for id in ids] @pytest.fixture -def block_args(): - return { - 'id': 'testobj', - 'type': 'TempSensorOneWire', - 'data': { +def block_args() -> Block: + return Block( + id='testobj', + type='TempSensorOneWire', + data={ 'value': 12345, 'offset': 20, 'address': 'FF' } - } + ) -@pytest.fixture(autouse=True) -def m_publish(app, mocker: MockerFixture): - m = mocker.spy(mqtt, 'publish') - return m +@asynccontextmanager +async def clear_datastore(): + config = utils.get_config() + client = AsyncClient(base_url=config.datastore_url) + query = DatastoreMultiQuery(namespace=const.SERVICE_NAMESPACE, filter='*') + content = query.model_dump(mode='json') + await asyncio.wait_for(utils.httpx_retry(lambda: client.post('/mdelete', json=content)), + timeout=5) + yield -@pytest.fixture(autouse=True) -def m_backup_dir(mocker: MockerFixture, tmp_path): - mocker.patch(backup_storage.__name__ + '.BASE_BACKUP_DIR', tmp_path / 'backup') +@asynccontextmanager +async def lifespan(app: FastAPI): + async with AsyncExitStack() as stack: + await stack.enter_async_context(clear_datastore()) + await stack.enter_async_context(mqtt.lifespan()) + # await stack.enter_async_context(datastore.lifespan()) + await stack.enter_async_context(connection.lifespan()) + await stack.enter_async_context(synchronization.lifespan()) + yield -@pytest.fixture(autouse=True) -def m_simulator_dir(mocker: MockerFixture, tmp_path): - mocker.patch(stream_connection.__name__ + '.SIMULATION_CWD', tmp_path / 'simulator') +@pytest.fixture +def app() -> FastAPI: + config = utils.get_config() + config.mock = False + config.simulation = True + mqtt.setup() + state_machine.setup() + datastore_settings.setup() + datastore_blocks.setup() + codec.setup() + connection.setup() + command.setup() + control.setup() + block_backup.setup() -def repeated_blocks(ids, args): - return [{ - 'id': id, - 'type': args['type'], - 'data': args['data'] - } for id in ids] + app = FastAPI(lifespan=lifespan) + app_factory.add_exception_handlers(app) -@pytest.fixture -async def setup(app, broker): - """App + controller routes""" - app['ini'] = parse_ini(app) + app.include_router(blocks_api.router) + app.include_router(system_api.router) + app.include_router(settings_api.router) + app.include_router(backup_api.router) + app.include_router(debug_api.router) - config: ServiceConfig = app['config'] - config.mock = False - config.simulation = True - config.isolated = True - config.device_id = '123456789012345678901234' - config.device_port = find_free_port() - config.display_ws_port = 0 # let firmware find its own free port - config.mqtt_host = 'localhost' - config.mqtt_port = broker['mqtt'] - config.state_topic = 'test_integration/state' - - service_status.setup(app) - scheduler.setup(app) - mqtt.setup(app) - codec.setup(app) - connection.setup(app) - commander.setup(app) - block_store.setup(app) - global_store.setup(app) - service_store.setup(app) - synchronization.setup(app) - controller.setup(app) - backup_storage.setup(app) - - error_response.setup(app) - blocks_api.setup(app) - backup_api.setup(app) - system_api.setup(app) - settings_api.setup(app) - debug_api.setup(app) + return app -@pytest.fixture -async def production_app(app): - app['config'].debug = False - return app +@pytest.fixture(autouse=True) +def s_publish(app: FastAPI, mocker: MockerFixture) -> Mock: + m = mocker.spy(mqtt.CV.get(), 'publish') + return m @pytest.fixture(autouse=True) -async def synchronized(app, client: TestClient): +async def synchronized(client: AsyncClient): + state = state_machine.CV.get() # Prevents test hangups if the connection fails - await asyncio.wait_for( - asyncio.gather( - service_status.wait_synchronized(app), - mqtt.fget(app).ready.wait(), - ), - timeout=5) + await asyncio.wait_for(state.wait_synchronized(), + timeout=5) -async def test_create(app, client: TestClient, block_args): +async def test_create(client: AsyncClient, block_args: Block): # Create object - retd = await response(client.post('/blocks/create', json=block_args), 201) - assert retd['id'] == block_args['id'] + resp = await client.post('/blocks/create', json=block_args.model_dump()) + assert Block.model_validate_json(resp.text).id == block_args.id # Conflict error: name already taken - await response(client.post('/blocks/create', json=block_args), 409) - - block_args['id'] = 'other_obj' - retd = await response(client.post('/blocks/create', json=block_args), 201) - assert retd['id'] == 'other_obj' - - -async def test_invalid_input(app, client: TestClient, block_args): - # 400 if input fails schema check - del block_args['type'] - retv = await response(client.post('/blocks/create', json=block_args), 400) - assert retv == [{ - 'in': 'body', - 'loc': ['type'], - 'msg': 'field required', - 'type': 'value_error.missing', - }] - - # 400 if input fails encoding - # This yields a JSON error - block_args['type'] = 'dummy' - retv = await response(client.post('/blocks/create', json=block_args), 400) + resp = await client.post('/blocks/create', json=block_args.model_dump()) + assert resp.status_code == 409 + + block_args.id = 'other_obj' + resp = await client.post('/blocks/create', json=block_args.model_dump()) + assert Block.model_validate_json(resp.text).id == block_args.id + + +async def test_invalid_input(client: AsyncClient, block_args: Block, mocker: MockerFixture): + ctrl = control.CV.get() + + # 422 if input fails schema check + raw = block_args.model_dump() + del raw['type'] + resp = await client.post('/blocks/create', json=raw) + assert resp.status_code == 422 + retv = resp.json() + assert 'RequestValidationError' in retv['error'] + assert 'traceback' not in retv + assert 'validation' in retv + + # 409 if input fails encoding + raw = block_args.model_dump() + raw['type'] = 'dummy' + resp = await client.post('/blocks/create', json=raw) + assert resp.status_code == 400 + retv = resp.json() assert 'dummy' in retv['error'] assert 'traceback' in retv + assert 'validation' not in retv + + # We need to simulate some bugs now + m = mocker.patch.object(ctrl, 'create_block', autospec=True) + + # 500 if output is invalid + # This is a programming error + m.side_effect = None + m.return_value = BlockIdentity() + resp = await client.post('/blocks/create', json=raw) + assert resp.status_code == 500 + retv = resp.json() + assert 'ResponseValidationError' in retv['error'] + assert 'traceback' not in retv + assert 'validation' in retv + +async def test_invalid_input_prod(client: AsyncClient, block_args: Block, mocker: MockerFixture): + ctrl = control.CV.get() + config = utils.get_config() + config.debug = False -async def test_invalid_input_prod(production_app, client: TestClient, block_args): - # 400 if input fails schema check - del block_args['type'] - retv = await response(client.post('/blocks/create', json=block_args), 400) - assert retv == [{ - 'in': 'body', - 'loc': ['type'], - 'msg': 'field required', - 'type': 'value_error.missing', - }] - - # 400 if input fails encoding - # This yields a JSON error - # For production errors, no traceback is returned - block_args['type'] = 'dummy' - retv = await response(client.post('/blocks/create', json=block_args), 400) + # 422 if input fails schema check + raw = block_args.model_dump() + del raw['type'] + resp = await client.post('/blocks/create', json=raw) + assert resp.status_code == 422 + retv = resp.json() + assert 'RequestValidationError' in retv['error'] + assert 'traceback' not in retv + assert 'validation' in retv + + # 409 if input fails encoding + raw = block_args.model_dump() + raw['type'] = 'dummy' + resp = await client.post('/blocks/create', json=raw) + assert resp.status_code == 400 + retv = resp.json() assert 'dummy' in retv['error'] assert 'traceback' not in retv + assert 'validation' not in retv + + # We need to simulate some bugs now + m = mocker.patch.object(ctrl, 'create_block', autospec=True) + + # 500 if output is invalid + # This is a programming error + m.side_effect = None + m.return_value = BlockIdentity() + resp = await client.post('/blocks/create', json=raw) + assert resp.status_code == 500 + retv = resp.json() + assert 'ResponseValidationError' in retv['error'] + assert 'traceback' not in retv + assert 'validation' in retv -async def test_create_performance(app, client: TestClient, block_args): +async def test_create_performance(client: AsyncClient, block_args: Block): num_items = 50 ids = [f'id{num}' for num in range(num_items)] blocks = repeated_blocks(ids, block_args) - await asyncio.gather(*(response(client.post('/blocks/create', json=block), 201) + await asyncio.gather(*(client.post('/blocks/create', json=block.model_dump(mode='json')) for block in blocks)) - retd = await response(client.post('/blocks/all/read')) - assert set(ids).issubset(ret_ids(retd)) + resp = await client.post('/blocks/all/read') + assert set(ids).issubset(ret_ids(resp.json())) -async def test_batch_create_read_delete(app, client: TestClient, block_args): +async def test_batch_create_read_delete(client: AsyncClient, block_args: Block): num_items = 50 ids = [f'id{num}' for num in range(num_items)] blocks = repeated_blocks(ids, block_args) + raw_idents = [{'id': block.id} for block in blocks] + raw_blocks = [block.model_dump(mode='json') for block in blocks] + + resp = await client.post('/blocks/batch/create', json=raw_blocks) + assert resp.status_code == 201 + assert len(resp.json()) == num_items + + resp = await client.post('/blocks/batch/read', json=raw_idents) + assert resp.status_code == 200 + assert len(resp.json()) == num_items + + resp = await client.post('/blocks/all/read') + assert resp.status_code == 200 + assert set(ids).issubset(ret_ids(resp.json())) + + resp = await client.post('/blocks/batch/delete', json=raw_idents) + assert resp.status_code == 200 + assert len(resp.json()) == num_items + + resp = await client.post('/blocks/all/read') + assert set(ids).isdisjoint(ret_ids(resp.json())) + - retv = await response(client.post('/blocks/batch/create', json=blocks), 201) - assert len(retv) == num_items +async def test_read(client: AsyncClient, block_args: Block): + resp = await client.post('/blocks/read', json={'id': 'testobj'}) + assert resp.status_code == 400 # Block does not exist - ident_args = [{'id': block['id']} for block in blocks] - retv = await response(client.post('/blocks/batch/read', json=ident_args)) - assert len(retv) == num_items + resp = await client.post('/blocks/create', json=block_args.model_dump()) + assert resp.status_code == 201 - retv = await response(client.post('/blocks/all/read')) - assert set(ids).issubset(ret_ids(retv)) + resp = await client.post('/blocks/read', json={'id': 'testobj'}) + assert Block.model_validate_json(resp.text).id == 'testobj' - retv = await response(client.post('/blocks/batch/delete', json=ident_args)) - assert len(retv) == num_items - retv = await response(client.post('/blocks/all/read')) - assert set(ids).isdisjoint(ret_ids(retv)) +async def test_read_performance(client: AsyncClient, block_args: Block): + resp = await client.post('/blocks/create', json=block_args.model_dump()) + assert resp.status_code == 201 + resps = await asyncio.gather(*(client.post('/blocks/read', json={'id': 'testobj'}) for _ in range(100))) + for resp in resps: + assert resp.status_code == 200 -async def test_read(app, client: TestClient, block_args): - await response(client.post('/blocks/read', json={'id': 'testobj'}), 400) # Object does not exist - await response(client.post('/blocks/create', json=block_args), 201) - retd = await response(client.post('/blocks/read', json={'id': 'testobj'})) - assert retd['id'] == 'testobj' +async def test_read_logged(client: AsyncClient, block_args: Block): + resp = await client.post('/blocks/create', json=block_args.model_dump()) + assert resp.status_code == 201 + resp = await client.post('/blocks/read/logged', json={'id': 'testobj'}) + retd = Block.model_validate_json(resp.text) + assert retd.id == 'testobj' + assert 'address' not in retd.data # address is not a logged field -async def test_read_performance(app, client: TestClient, block_args): - await response(client.post('/blocks/create', json=block_args), 201) - await asyncio.gather(*(response(client.post('/blocks/read', json={'id': 'testobj'})) for _ in range(100))) +async def test_write(client: AsyncClient, block_args: Block, s_publish: Mock): + resp = await client.post('/blocks/create', json=block_args.model_dump()) + assert resp.status_code == 201 -async def test_read_logged(app, client: TestClient, block_args): - await response(client.post('/blocks/create', json=block_args), 201) + resp = await client.post('/blocks/write', json=block_args.model_dump()) + assert resp.status_code == 200 + assert s_publish.call_count == 2 - retd = await response(client.post('/blocks/read/logged', json={'id': 'testobj'})) - assert retd['id'] == 'testobj' - assert 'address' not in retd['data'] # address is not a logged field +async def test_batch_write(client: AsyncClient, block_args: Block, s_publish: Mock): + resp = await client.post('/blocks/create', json=block_args.model_dump()) + assert resp.status_code == 201 -async def test_write(app, client: TestClient, block_args, m_publish): - await response(client.post('/blocks/create', json=block_args), 201) - assert await response(client.post('/blocks/write', json=block_args)) - assert m_publish.call_count == 2 + resp = await client.post('/blocks/batch/write', + json=[block_args.model_dump(), + block_args.model_dump(), + block_args.model_dump()]) + assert resp.status_code == 200 + assert s_publish.call_count == 2 -async def test_batch_write(app, client: TestClient, block_args, m_publish): - await response(client.post('/blocks/create', json=block_args), 201) - assert await response(client.post('/blocks/batch/write', json=[block_args, block_args, block_args])) - assert m_publish.call_count == 2 +async def test_patch(client: AsyncClient, block_args: Block, s_publish: Mock): + resp = await client.post('/blocks/create', json=block_args.model_dump()) + assert resp.status_code == 201 + resp = await client.post('/blocks/patch', json=block_args.model_dump()) + assert resp.status_code == 200 + assert s_publish.call_count == 2 -async def test_patch(app, client: TestClient, block_args, m_publish): - await response(client.post('/blocks/create', json=block_args), 201) - assert await response(client.post('/blocks/patch', json=block_args)) - assert m_publish.call_count == 2 +async def test_batch_patch(client: AsyncClient, block_args: Block, s_publish: Mock): + resp = await client.post('/blocks/create', json=block_args.model_dump()) + assert resp.status_code == 201 -async def test_batch_patch(app, client: TestClient, block_args, m_publish): - await response(client.post('/blocks/create', json=block_args), 201) - assert await response(client.post('/blocks/batch/patch', json=[block_args, block_args, block_args])) - assert m_publish.call_count == 2 + resp = await client.post('/blocks/batch/patch', + json=[block_args.model_dump(), + block_args.model_dump(), + block_args.model_dump()]) + assert resp.status_code == 200 + assert s_publish.call_count == 2 -async def test_delete(app, client: TestClient, block_args): - await response(client.post('/blocks/create', json=block_args), 201) +async def test_delete(client: AsyncClient, block_args: Block): + resp = await client.post('/blocks/create', json=block_args.model_dump()) + assert resp.status_code == 201 - retd = await response(client.post('/blocks/delete', json={'id': 'testobj'})) - assert retd['id'] == 'testobj' + resp = await client.post('/blocks/delete', json={'id': 'testobj'}) + assert BlockIdentity.model_validate_json(resp.text).id == 'testobj' - await response(client.post('/blocks/read', json={'id': 'testobj'}), 400) + resp = await client.post('/blocks/read', json={'id': 'testobj'}) + assert resp.status_code == 400 -async def test_nid_crud(app, client: TestClient, block_args): - created = await response(client.post('/blocks/create', json=block_args), 201) - nid = created['nid'] +async def test_nid_crud(client: AsyncClient, block_args: Block): + resp = await client.post('/blocks/create', json=block_args.model_dump()) + assert resp.status_code == 201 - created['data']['value'] = 5 - await response(client.post('/blocks/read', json={'nid': nid})) - await response(client.post('/blocks/write', json=created)) - await response(client.post('/blocks/delete', json={'nid': nid})) + created = Block.model_validate_json(resp.text) - await response(client.post('/blocks/read', json={'nid': nid}), 500) + created.data['value'] = 5 + resp = await client.post('/blocks/read', json={'nid': created.nid}) + assert resp.status_code == 200 + resp = await client.post('/blocks/write', json=created.model_dump()) + assert resp.status_code == 200 + resp = await client.post('/blocks/delete', json={'nid': created.nid}) + assert resp.status_code == 200 + resp = await client.post('/blocks/read', json={'nid': created.nid}) + assert resp.status_code == 500 -async def test_stored_blocks(app, client: TestClient, block_args): - retd = await response(client.post('/blocks/all/read/stored')) - base_num = len(retd) - await response(client.post('/blocks/create', json=block_args), 201) - retd = await response(client.post('/blocks/all/read/stored')) - assert len(retd) == 1 + base_num +async def test_stored_blocks(client: AsyncClient, block_args: Block): + resp = await client.post('/blocks/all/read/stored') + base_num = len(resp.json()) - retd = await response(client.post('/blocks/read/stored', json={'id': 'testobj'})) - assert retd['id'] == 'testobj' + resp = await client.post('/blocks/create', json=block_args.model_dump()) + assert resp.status_code == 201 + resp = await client.post('/blocks/all/read/stored') + assert len(resp.json()) == 1 + base_num - await response(client.post('/blocks/read/stored', json={'id': 'flappy'}), 400) + resp = await client.post('/blocks/read/stored', json={'id': 'testobj'}) + assert Block.model_validate_json(resp.text).id == 'testobj' + resp = await client.post('/blocks/read/stored', json={'id': 'flappy'}) + assert resp.status_code == 400 -async def test_delete_all(app, client: TestClient, block_args): - n_sys_obj = len(await response(client.post('/blocks/all/read'))) + +async def test_delete_all(client: AsyncClient, block_args: Block): + resp = await client.post('/blocks/all/read') + n_sys_obj = len(resp.json()) for i in range(5): - block_args['id'] = f'id{i}' - await response(client.post('/blocks/create', json=block_args), 201) + block_args.id = f'id{i}' + resp = await client.post('/blocks/create', json=block_args.model_dump()) + assert resp.status_code == 201 + + resp = await client.post('/blocks/all/read') + assert len(resp.json()) == n_sys_obj + 5 + + resp = await client.post('/blocks/all/delete') + assert len(resp.json()) == 5 - assert len(await response(client.post('/blocks/all/read'))) == 5 + n_sys_obj - await response(client.post('/blocks/all/delete')) - assert len(await response(client.post('/blocks/all/read'))) == n_sys_obj + resp = await client.post('/blocks/all/read') + assert len(resp.json()) == n_sys_obj -async def test_cleanup(app, client: TestClient, block_args): - store = block_store.fget(app) - await response(client.post('/blocks/create', json=block_args), 201) +async def test_cleanup(client: AsyncClient, block_args: Block): + store = datastore_blocks.CV.get() + resp = await client.post('/blocks/create', json=block_args.model_dump()) + assert resp.status_code == 201 + store['unused', 456] = {} - retv = await response(client.post('/blocks/cleanup')) - assert {'id': 'unused', 'nid': 456, 'type': None, 'serviceId': 'test_app'} in retv + + resp = await client.post('/blocks/cleanup') + retv = resp.json() + expected = { + 'id': 'unused', + 'nid': 456, + 'type': None, + 'serviceId': 'sparkey', + } + assert expected in retv assert not [v for v in retv if v['id'] == 'testobj'] -async def test_rename(app, client: TestClient, block_args): - await response(client.post('/blocks/create', json=block_args), 201) - existing = block_args['id'] +async def test_rename(client: AsyncClient, block_args: Block): + resp = await client.post('/blocks/create', json=block_args.model_dump()) + assert resp.status_code == 201 + existing = block_args.id desired = 'newname' - await response(client.post('/blocks/read', json={'id': desired}), 400) - await response(client.post('/blocks/rename', json={ + resp = await client.post('/blocks/read', json={'id': desired}) + assert resp.status_code == 400 + + resp = await client.post('/blocks/rename', json={ 'existing': existing, - 'desired': desired - })) - await response(client.post('/blocks/read', json={'id': desired})) + 'desired': desired, + }) + assert resp.status_code == 200 + + resp = await client.post('/blocks/read', json={'id': desired}) + assert resp.status_code == 200 -async def test_sequence(app, client: TestClient): +async def test_sequence(client: AsyncClient): setpoint_block = { 'id': 'setpoint', 'type': 'SetpointSensorPair', @@ -353,10 +445,14 @@ async def test_sequence(app, client: TestClient): } } - await response(client.post('/blocks/create', json=setpoint_block), 201) - retd = await response(client.post('/blocks/create', json=sequence_block), 201) + resp = await client.post('/blocks/create', json=setpoint_block) + assert resp.status_code == 201 - assert retd['data']['instructions'] == [ + resp = await client.post('/blocks/create', json=sequence_block) + assert resp.status_code == 201 + block = Block.model_validate_json(resp.text) + + assert block.data['instructions'] == [ '# This is a comment', 'SET_SETPOINT target=setpoint, setting=40.0C', 'WAIT_SETPOINT target=setpoint, precision=1.0dC', @@ -364,40 +460,46 @@ async def test_sequence(app, client: TestClient): ] -async def test_ping(app, client: TestClient): - await response(client.get('/system/ping')) - await response(client.post('/system/ping')) +async def test_ping(client: AsyncClient): + resp = await client.get('/system/ping') + assert resp.status_code == 200 + resp = await client.post('/system/ping') + assert resp.status_code == 200 + +async def test_settings_api(client: AsyncClient, block_args: Block): + resp = await client.post('/blocks/create', json=block_args.model_dump()) + assert resp.status_code == 201 -async def test_settings_api(app, client: TestClient, block_args): - await response(client.post('/blocks/create', json=block_args), 201) + resp = await client.get('/settings/enabled') + assert resp.json() == {'enabled': True} - retd = await response(client.get('/settings/autoconnecting')) - assert retd == {'enabled': True} + resp = await client.put('/settings/enabled', json={'enabled': False}) + assert resp.json() == {'enabled': False} - retd = await response(client.put('/settings/autoconnecting', json={'enabled': False})) - assert retd == {'enabled': False} - retd = await response(client.get('/settings/autoconnecting')) - assert retd == {'enabled': False} + resp = await client.get('/settings/enabled') + assert resp.json() == {'enabled': False} -async def test_discover(app, client: TestClient): - resp = await response(client.post('/blocks/discover')) - assert resp == [] +async def test_discover(client: AsyncClient): + resp = await client.post('/blocks/discover') + assert resp.json() == [] -async def test_validate(app, client: TestClient, block_args): +async def test_validate(client: AsyncClient, block_args: Block): validate_args = { - 'type': block_args['type'], - 'data': block_args['data'], + 'type': block_args.type, + 'data': block_args.data, } - await response(client.post('/blocks/validate', json=validate_args)) + resp = await client.post('/blocks/validate', json=validate_args) + assert resp.status_code == 200 invalid_data_obj = { - 'type': block_args['type'], - 'data': {**block_args['data'], 'invalid': True} + 'type': block_args.type, + 'data': {**block_args.data, 'invalid': True} } - await response(client.post('/blocks/validate', json=invalid_data_obj), 400) + resp = await client.post('/blocks/validate', json=invalid_data_obj) + assert resp.status_code == 400 invalid_link_obj = { 'type': 'SetpointSensorPair', @@ -410,102 +512,116 @@ async def test_validate(app, client: TestClient, block_args): 'filterThreshold': 2 } } - await response(client.post('/blocks/validate', json=invalid_link_obj), 400) + resp = await client.post('/blocks/validate', json=invalid_link_obj) + assert resp.status_code == 400 -async def test_backup_save(app, client: TestClient, block_args): - retd = await response(client.post('/blocks/backup/save')) - base_num = len(retd['blocks']) +async def test_backup_save(client: AsyncClient, block_args: Block): + resp = await client.post('/blocks/backup/save') + base_num = len(resp.json()['blocks']) - await response(client.post('/blocks/create', json=block_args), 201) - retd = await response(client.post('/blocks/backup/save')) - assert len(retd['blocks']) == 1 + base_num + resp = await client.post('/blocks/create', json=block_args.model_dump()) + assert resp.status_code == 201 + resp = await client.post('/blocks/backup/save') + assert len(resp.json()['blocks']) == base_num + 1 -async def test_backup_load(app, client: TestClient, spark_blocks): + +async def test_backup_load(client: AsyncClient, spark_blocks: list[Block]): # reverse the set, to ensure some blocks are written with invalid references - data = { - 'store': [{'keys': [block['id'], block['nid']], 'data': dict()} for block in spark_blocks], - 'blocks': spark_blocks[::-1], - } - resp = await response(client.post('/blocks/backup/load', json=data)) - assert resp == {'messages': []} + backup = Backup( + store=[TwinKeyEntry(keys=(block.id, block.nid), data={}) + for block in spark_blocks], + blocks=spark_blocks[::-1] + ) + + resp = await client.post('/blocks/backup/load', json=backup.model_dump()) + assert resp.json() == {'messages': []} - resp = await response(client.post('/blocks/all/read')) + resp = await client.post('/blocks/all/read') ids = ret_ids(spark_blocks) - resp_ids = ret_ids(resp) + resp_ids = ret_ids(resp.json()) assert set(ids).issubset(resp_ids) assert 'ActiveGroups' not in resp_ids assert 'SystemInfo' in resp_ids # Add an obsolete system block - data['blocks'].append({'nid': 1, 'type': 'Groups', 'data': {}}) + backup.blocks.append(Block( + nid=1, + type='Groups', + data={}, + )) # Add an unused store alias - data['store'].append({'keys': ['TROLOLOL', 9999], 'data': dict()}) + backup.store.append(TwinKeyEntry( + keys=('TROLOLOL', 9999), + data={})) # Add renamed type to store data - data['store'].append({'keys': ['renamed_display_settings', const.DISPLAY_SETTINGS_NID], 'data': dict()}) + backup.store.append(TwinKeyEntry( + keys=('renamed_display_settings', const.DISPLAY_SETTINGS_NID), + data={}, + )) # Add a Block that will fail to be created, and should be skipped - data['blocks'].append({ - 'id': 'derpface', - 'nid': 500, - 'type': 'INVALID', - 'data': {} - }) - - resp = await response(client.post('/blocks/backup/load', json=data)) - resp = resp['messages'] + backup.blocks.append(Block( + id='derpface', + nid=500, + type='INVALID', + data={} + )) + + resp = await client.post('/blocks/backup/load', json=backup.model_dump()) + resp = resp.json()['messages'] assert len(resp) == 2 assert 'derpface' in resp[0] assert 'TROLOLOL' in resp[1] - resp = await response(client.post('/blocks/all/read')) - resp_ids = ret_ids(resp) + resp = await client.post('/blocks/all/read') + resp_ids = ret_ids(resp.json()) assert 'renamed_display_settings' in resp_ids assert 'derpface' not in resp_ids -async def test_backup_stored(app, client: TestClient, block_args): - portable = await response(client.post('/blocks/backup/save')) - saved_stored = await response(client.post('/blocks/backup/stored/save', json={ - 'name': 'stored', - })) +async def test_backup_stored(client: AsyncClient, block_args: Block): + resp = await client.post('/blocks/backup/save') + portable = Backup.model_validate_json(resp.text) + + resp = await client.post('/blocks/backup/stored/save', json={'name': 'stored'}) + saved_stored = Backup.model_validate_json(resp.text) - assert saved_stored['blocks'] - assert saved_stored['store'] - assert saved_stored['timestamp'] - assert saved_stored['firmware'] - assert saved_stored['device'] + assert len(portable.blocks) == len(saved_stored.blocks) - assert len(portable['blocks']) == len(saved_stored['blocks']) + resp = await client.post('/blocks/backup/stored/download', json={'name': 'stored'}) + download_stored = Backup.model_validate_json(resp.text) - download_stored = await response(client.post('/blocks/backup/stored/download', json={ - 'name': 'stored', - })) assert saved_stored == download_stored - upload_stored = await response(client.post('/blocks/backup/stored/upload', json=download_stored)) + resp = await client.post('/blocks/backup/stored/upload', json=download_stored.model_dump()) + upload_stored = Backup.model_validate_json(resp.text) + assert saved_stored == upload_stored - download_stored['name'] = None - await response(client.post('/blocks/backup/stored/upload', json=download_stored), status=400) + download_stored.name = None + resp = await client.post('/blocks/backup/stored/upload', json=download_stored.model_dump()) + assert resp.status_code == 400 - all_stored = await response(client.post('/blocks/backup/stored/all')) - assert all_stored == [{'name': 'stored'}] + resp = await client.post('/blocks/backup/stored/all') + assert resp.json() == [{'name': 'stored'}] # Create block not in backup # Then restore backup - await response(client.post('/blocks/create', json=block_args), 201) - await response(client.post('/blocks/backup/stored/load', json={ - 'name': 'stored', - })) - ids = ret_ids(await response(client.post('/blocks/all/read'))) - assert block_args['id'] not in ids + resp = await client.post('/blocks/create', json=block_args.model_dump()) + assert resp.status_code == 201 + + resp = await client.post('/blocks/backup/stored/load', json={'name': 'stored'}) + assert resp.status_code == 200 + + resp = await client.post('/blocks/all/read') + assert block_args.id not in ret_ids(resp.json()) -async def test_read_all_logged(app, client: TestClient): +async def test_read_all_logged(client: AsyncClient): args = { 'id': 'pwm', 'type': 'ActuatorPwm', @@ -515,9 +631,14 @@ async def test_read_all_logged(app, client: TestClient): } } - await response(client.post('/blocks/create', json=args), 201) - all = await response(client.post('/blocks/all/read')) - logged = await response(client.post('/blocks/all/read/logged')) + resp = await client.post('/blocks/create', json=args) + assert resp.status_code == 201 + + resp = await client.post('/blocks/all/read') + all = resp.json() + + resp = await client.post('/blocks/all/read/logged') + logged = resp.json() # list_objects returns everything obj = all[-1] @@ -536,32 +657,34 @@ async def test_read_all_logged(app, client: TestClient): assert 'period' not in obj_data -async def test_system_status(app, client: TestClient): - await service_status.wait_synchronized(app) - resp = await response(client.get('/system/status')) +async def test_system_status(client: AsyncClient): + config = utils.get_config() + fw_config = utils.get_fw_config() + resp = await client.get('/system/status') + desc = resp.json() firmware_desc = { - 'firmware_version': ANY, - 'proto_version': ANY, - 'firmware_date': ANY, - 'proto_date': ANY, + 'firmware_version': fw_config.firmware_version, + 'proto_version': fw_config.proto_version, + 'firmware_date': fw_config.firmware_date, + 'proto_date': fw_config.proto_date, } device_desc = { - 'device_id': ANY, + 'device_id': config.device_id, } - assert resp == { + assert desc == { 'enabled': True, 'service': { - 'name': 'test_app', + 'name': 'sparkey', 'firmware': firmware_desc, 'device': device_desc, }, 'controller': { 'system_version': ANY, 'platform': ANY, - 'reset_reason': ANY, + 'reset_reason': 'NONE', 'firmware': firmware_desc, 'device': device_desc, }, @@ -572,26 +695,42 @@ async def test_system_status(app, client: TestClient): 'identity_error': None, } - service_status.set_disconnected(app) - await asyncio.sleep(0.01) - resp = await response(client.get('/system/status')) - assert resp['connection_status'] == 'DISCONNECTED' - assert resp['controller'] is None + await command.CV.get().end_connection() + + resp = await client.get('/system/status') + desc = resp.json() + + assert desc['connection_status'] == 'DISCONNECTED' + assert desc['controller'] is None -async def test_system_resets(app, client: TestClient, mocker): - sys_api = system_api.__name__ - mocker.patch(sys_api + '.shutdown_soon', AsyncMock()) +async def test_system_resets(client: AsyncClient, m_kill: Mock): + await client.post('/system/reboot/service') + m_kill.assert_called_once() - await response(client.post('/system/reboot/service')) - system_api.shutdown_soon.assert_awaited() + await client.post('/system/reboot/controller') + await client.post('/system/clear_wifi') + await client.post('/system/factory_reset') - await response(client.post('/system/reboot/controller')) - await response(client.post('/system/clear_wifi')) - await response(client.post('/system/factory_reset')) +async def test_system_flash(client: AsyncClient, m_kill: Mock): + state = state_machine.CV.get() -async def test_debug_encode_request(app, client: TestClient): + resp = await client.post('/system/flash') + assert resp.status_code == 424 # incompatible firmware + assert m_kill.call_count == 0 + assert state.is_synchronized() + + desc = state.desc() + desc.connection_kind = 'TCP' + desc.controller.platform == 'dummy' # not handled, but also not an error + resp = await client.post('/system/flash') + assert resp.status_code == 200 + assert m_kill.call_count == 1 + assert not state.is_connected() + + +async def test_debug_encode_request(client: AsyncClient): payload = DecodedPayload( blockId=123, blockType='TempSensorOneWire', @@ -602,9 +741,8 @@ async def test_debug_encode_request(app, client: TestClient): } ) - retv = await response(client.post('/_debug/encode_payload', - json=payload.clean_dict())) - payload = EncodedPayload(**retv) + resp = await client.post('/_debug/encode_payload', json=payload.model_dump(mode='json')) + payload = EncodedPayload.model_validate_json(resp.text) req = IntermediateRequest( msgId=1, @@ -612,24 +750,22 @@ async def test_debug_encode_request(app, client: TestClient): payload=payload, ) - retv = await response(client.post('/_debug/encode_request', - json=req.clean_dict())) - assert retv['message'] + resp = await client.post('/_debug/encode_request', json=req.model_dump(mode='json')) + msg = EncodedMessage.model_validate_json(resp.text) + + resp = await client.post('/_debug/decode_request', json=msg.model_dump(mode='json')) + req = IntermediateRequest.model_validate_json(resp.text) - retv = await response(client.post('/_debug/decode_request', - json=retv)) - req = IntermediateRequest(**retv) assert req.opcode == Opcode.BLOCK_WRITE - retv = await response(client.post('/_debug/decode_payload', - json=req.payload.clean_dict())) - payload = DecodedPayload(**retv) + resp = await client.post('/_debug/decode_payload', json=req.payload.model_dump(mode='json')) + payload = DecodedPayload.model_validate_json(resp.text) assert payload.content['value']['value'] == 0 # Readonly value assert payload.content['offset']['value'] == 20 -async def test_debug_encode_response(app, client: TestClient): +async def test_debug_encode_response(client: AsyncClient): payload = DecodedPayload( blockId=123, blockType='TempSensorOneWire', @@ -639,27 +775,27 @@ async def test_debug_encode_response(app, client: TestClient): 'address': 'FF' } ) - retv = await response(client.post('/_debug/encode_payload', - json=payload.clean_dict())) - payload = EncodedPayload(**retv) - resp = IntermediateResponse( + resp = await client.post('/_debug/encode_payload', json=payload.model_dump(mode='json')) + payload = EncodedPayload.model_validate_json(resp.text) + + iresp = IntermediateResponse( msgId=1, error=ErrorCode.INVALID_BLOCK, payload=[payload], ) - retv = await response(client.post('/_debug/encode_response', - json=resp.clean_dict())) - assert retv['message'] - - retv = await response(client.post('/_debug/decode_response', - json=retv)) - resp = IntermediateResponse(**retv) - assert resp.error == ErrorCode.INVALID_BLOCK - - retv = await response(client.post('/_debug/decode_payload', - json=resp.payload[0].clean_dict())) - payload = DecodedPayload(**retv) + resp = await client.post('/_debug/encode_response', json=iresp.model_dump(mode='json')) + msg = EncodedMessage.model_validate_json(resp.text) + + resp = await client.post('/_debug/decode_response', json=msg.model_dump(mode='json')) + iresp = IntermediateResponse.model_validate_json(resp.text) + + assert iresp.error == ErrorCode.INVALID_BLOCK + assert len(iresp.payload) == 1 + + payload = iresp.payload[0] + resp = await client.post('/_debug/decode_payload', json=payload.model_dump(mode='json')) + payload = DecodedPayload.model_validate_json(resp.text) assert payload.content['value']['value'] == 0 # Readonly value assert payload.content['offset']['value'] == 20 diff --git a/test/test_main.py b/test/test_main.py deleted file mode 100644 index 9235dfc4..00000000 --- a/test/test_main.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Tests brewblox_devcon_spark.__main__.py -""" - -import pytest -from brewblox_service import http, mqtt, scheduler, service - -from brewblox_devcon_spark import __main__ as main -from brewblox_devcon_spark import (backup_storage, block_store, broadcaster, - codec, commander, connection, controller, - global_store, service_status, service_store, - synchronization, time_sync) -from brewblox_devcon_spark.models import ServiceConfig - -TESTED = main.__name__ - - -@pytest.fixture(autouse=True) -def m_firmware_ini(mocker): - mocker.patch(TESTED + '.FIRMWARE_INI', 'firmware.ini') - - -@pytest.fixture -def m_service_funcs(app, mocker, event_loop): - def dummy_run(app, setup): - event_loop.run_until_complete(setup) - - mocker.patch(TESTED + '.service.create_config', autospec=True).return_value = app['config'] - mocker.patch(TESTED + '.service.create_app', autospec=True).return_value = app - mocker.patch(TESTED + '.service.run_app', dummy_run) - - -def test_parse(app, sys_args): - parser = main.create_parser() - config = service.create_config(parser, model=ServiceConfig, raw_args=sys_args[1:]) - assert isinstance(config, ServiceConfig) - - -def test_main(app, m_service_funcs): - main.main() - - assert None not in [ - scheduler.fget(app), - mqtt.fget(app), - http.fget(app), - - global_store.fget(app), - service_store.fget(app), - block_store.fget(app), - - service_status.fget(app), - codec.fget(app), - connection.fget(app), - commander.fget(app), - synchronization.fget(app), - controller.fget(app), - - backup_storage.fget(app), - broadcaster.fget(app), - time_sync.fget(app), - ] - - -@pytest.mark.parametrize('auto_id', [True, False]) -def test_simulation(auto_id, app, m_service_funcs): - config: ServiceConfig = app['config'] - config.device_id = None - config.mock = auto_id - config.simulation = auto_id - - main.main() - assert bool(config.device_id) is auto_id diff --git a/test/test_mdns.py b/test/test_mdns.py index 1c4af503..144e51b3 100644 --- a/test/test_mdns.py +++ b/test/test_mdns.py @@ -3,10 +3,13 @@ """ import asyncio +from datetime import timedelta from socket import inet_aton +from unittest.mock import Mock import pytest from aiozeroconf import ServiceInfo, ServiceStateChange +from pytest_mock import MockerFixture from brewblox_devcon_spark import mdns @@ -27,8 +30,8 @@ def __init__(self, conf, service_type, handlers): self.handlers[0](conf, service_type, name, ServiceStateChange.Removed) -@pytest.fixture -def conf_mock(mocker): +@pytest.fixture(autouse=True) +def conf_mock(mocker: MockerFixture) -> Mock: async def get_service_info(service_type, name): if name == 'id0': @@ -75,24 +78,24 @@ async def close(): return m -@pytest.fixture -def browser_mock(mocker): +@pytest.fixture(autouse=True) +def browser_mock(mocker: MockerFixture) -> Mock: return mocker.patch(TESTED + '.ServiceBrowser', ServiceBrowserMock) -async def test_discover_one(app, client, event_loop, browser_mock, conf_mock): - assert await mdns.discover_one(None, DNS_TYPE) == ('1.2.3.4', 1234, 'id1') - assert await mdns.discover_one('id2', DNS_TYPE) == ('4.3.2.1', 4321, 'id2') +async def test_discover_one(): + assert await mdns.discover_one(None, DNS_TYPE, timedelta(seconds=1)) == ('1.2.3.4', 1234, 'id1') + assert await mdns.discover_one('id2', DNS_TYPE, timedelta(seconds=1)) == ('4.3.2.1', 4321, 'id2') - assert await mdns.discover_one(None, DNS_TYPE, 1) == ('1.2.3.4', 1234, 'id1') - assert await mdns.discover_one('id2', DNS_TYPE, 1) == ('4.3.2.1', 4321, 'id2') + assert await mdns.discover_one(None, DNS_TYPE, timedelta(seconds=1)) == ('1.2.3.4', 1234, 'id1') + assert await mdns.discover_one('id2', DNS_TYPE, timedelta(seconds=1)) == ('4.3.2.1', 4321, 'id2') with pytest.raises(asyncio.TimeoutError): - await mdns.discover_one('leprechauns', DNS_TYPE, 0.1) + await mdns.discover_one('leprechauns', DNS_TYPE, timedelta(milliseconds=10)) -async def test_discover_all(app, client, event_loop, browser_mock, conf_mock): +async def test_discover_all(): retv = [] - async for res in mdns.discover_all(None, DNS_TYPE, 0.01): + async for res in mdns.discover_all(None, DNS_TYPE, timedelta(milliseconds=100)): retv.append(res) assert len(retv) == 2 diff --git a/test/test_mqtt_api.py b/test/test_mqtt_api.py deleted file mode 100644 index 0c192c22..00000000 --- a/test/test_mqtt_api.py +++ /dev/null @@ -1,103 +0,0 @@ -""" -Tests brewblox_devcon_spark.api.mqtt_api -""" - -import pytest -from brewblox_service import mqtt, scheduler - -from brewblox_devcon_spark import (block_store, codec, commander, connection, - controller, exceptions, global_store, - service_status, service_store, - synchronization) -from brewblox_devcon_spark.api import mqtt_api -from brewblox_devcon_spark.models import Block, BlockIdentity, ServiceConfig - -TESTED = mqtt_api.__name__ - - -@pytest.fixture -async def setup(app, broker): - config: ServiceConfig = app['config'] - config.mqtt_host = 'localhost' - config.mqtt_port = broker['mqtt'] - config.state_topic = 'test_mqtt/state' - - scheduler.setup(app) - mqtt.setup(app) - service_status.setup(app) - block_store.setup(app) - global_store.setup(app) - service_store.setup(app) - codec.setup(app) - connection.setup(app) - commander.setup(app) - synchronization.setup(app) - controller.setup(app) - - mqtt_api.setup(app) - - -def block_args(app): - return Block( - id='testobj', - serviceId=app['config'].name, - type='TempSensorOneWire', - data={ - 'value': 12345, - 'offset': 20, - 'address': 'FF' - } - ) - - -async def read(app, args: BlockIdentity): - return await controller.fget(app).read_block(args) - - -async def test_create(app, client): - api = mqtt_api.fget(app) - await api._create('topic', block_args(app).json()) - - assert await read(app, BlockIdentity(id='testobj')) - - -async def test_write(app, client): - api = mqtt_api.fget(app) - await api._create('topic', block_args(app).json()) - await api._write('topic', block_args(app).json()) - - assert await read(app, BlockIdentity(id='testobj')) - - -async def test_patch(app, client): - api = mqtt_api.fget(app) - await api._create('topic', block_args(app).json()) - await api._patch('topic', block_args(app).json()) - - assert await read(app, BlockIdentity(id='testobj')) - - -async def test_delete(app, client): - api = mqtt_api.fget(app) - await api._create('topic', block_args(app).json()) - await api._delete('topic', BlockIdentity( - id='testobj', - serviceId=app['config'].name, - ).json()) - - with pytest.raises(exceptions.UnknownId): - await read(app, BlockIdentity(id='testobj')) - - -async def test_unknown_service_id(app, client): - api = mqtt_api.fget(app) - - args = block_args(app) - args.serviceId = '' - await api._create('topic', args.json()) - await api._write('topic', args.json()) - await api._patch('topic', args.json()) - await api._delete('topic', args.json()) - - with pytest.raises(exceptions.UnknownId): - await read(app, BlockIdentity(id='testobj')) diff --git a/test/test_mqtt_connection.py b/test/test_mqtt_connection.py index 3aca8b86..f71af6dd 100644 --- a/test/test_mqtt_connection.py +++ b/test/test_mqtt_connection.py @@ -3,108 +3,106 @@ """ import asyncio +from contextlib import asynccontextmanager +from datetime import timedelta from unittest.mock import AsyncMock import pytest -from brewblox_service import mqtt, scheduler +from asgi_lifespan import LifespanManager +from fastapi import FastAPI +from brewblox_devcon_spark import mqtt, utils from brewblox_devcon_spark.connection import (connection_handler, mqtt_connection) -from brewblox_devcon_spark.models import ServiceConfig TESTED = mqtt_connection.__name__ +@asynccontextmanager +async def lifespan(app: FastAPI): + async with mqtt.lifespan(): + yield + + @pytest.fixture -async def setup(app, broker): - config: ServiceConfig = app['config'] - config.isolated = False - config.mqtt_host = 'localhost' - config.mqtt_port = broker['mqtt'] +def app() -> FastAPI: + mqtt.setup() + mqtt_connection.setup() - scheduler.setup(app) - mqtt.setup(app) - mqtt_connection.setup(app) + return FastAPI(lifespan=lifespan) @pytest.fixture(autouse=True) -async def synchronized(app, client): - await asyncio.wait_for(mqtt.fget(app).ready.wait(), timeout=5) +async def manager(manager: LifespanManager): + yield manager -async def test_mqtt_discovery(app, client, mocker): - mocker.patch(TESTED + '.DISCOVERY_TIMEOUT_S', 0.001) +async def test_mqtt_discovery(): + config = utils.get_config() + config.discovery_timeout_mqtt = timedelta(milliseconds=100) + + mqtt_client = mqtt.CV.get() recv = asyncio.Event() - config: ServiceConfig = app['config'] - device_id = config.device_id - async def recv_cb(topic: str, payload: str): + @mqtt_client.subscribe('brewcast/cbox/handshake/#') + async def recv_cb(client, topic, payload, qos, properties): recv.set() - await mqtt.listen(app, 'brewcast/cbox/handshake/#', recv_cb) - # Publish handshake message - await mqtt.publish(app, - topic=f'brewcast/cbox/handshake/{device_id}', - payload='handshake message') + mqtt_client.publish(f'brewcast/cbox/handshake/{config.device_id}', + 'handshake message') await recv.wait() - tracker = mqtt_connection.fget(app) - - assert await tracker.discover(AsyncMock(), None) is None - assert await tracker.discover(AsyncMock(), '09876') is None - - conn = await tracker.discover(AsyncMock(), device_id) + conn = await mqtt_connection.discover_mqtt(AsyncMock()) assert isinstance(conn, mqtt_connection.MqttConnection) - conn = await mqtt_connection.discover_mqtt(app, AsyncMock()) - assert isinstance(conn, mqtt_connection.MqttConnection) + # If device ID is not set, we can't discover + device_id = config.device_id + config.device_id = None + assert await mqtt_connection.discover_mqtt(AsyncMock()) is None + config.device_id = device_id # Publish LWT -> controller disconnected recv.clear() - await mqtt.publish(app, - topic=f'brewcast/cbox/handshake/{device_id}', - payload='') + mqtt_client.publish(f'brewcast/cbox/handshake/{config.device_id}', None) await recv.wait() # No longer discoverable - assert await tracker.discover(AsyncMock(), device_id) is None + assert await mqtt_connection.discover_mqtt(AsyncMock()) is None -async def test_mqtt_impl(app, client, mocker): - callbacks = AsyncMock(spec=connection_handler.ConnectionHandler(app)) - config: ServiceConfig = app['config'] - device_id = config.device_id +async def test_mqtt_impl(): + callbacks = AsyncMock(spec=connection_handler.ConnectionHandler) + config = utils.get_config() + mqtt_client = mqtt.CV.get() + recv_handshake = asyncio.Event() recv_req = asyncio.Event() recv_resp = asyncio.Event() recv_log = asyncio.Event() - async def on_handshake(topic: str, payload: str): + @mqtt_client.subscribe('brewcast/cbox/handshake/+') + async def on_handshake(client, topic, payload, qos, properties): recv_handshake.set() - async def on_request(topic: str, payload: str): + @mqtt_client.subscribe('brewcast/cbox/req/+') + async def on_request(client, topic, payload, qos, properties): resp_topic = topic.replace('/req/', '/resp/') - await mqtt.publish(app, resp_topic, payload[::-1]) + mqtt_client.publish(resp_topic, payload[::-1]) recv_req.set() - async def on_response(topic: str, payload: str): + @mqtt_client.subscribe('brewcast/cbox/resp/+') + async def on_response(client, topic, payload, qos, properties): recv_resp.set() - async def on_log(topic: str, payload: str): + @mqtt_client.subscribe('brewcast/cbox/log/+') + async def on_log(client, topic, payload, qos, properties): recv_log.set() - await mqtt.subscribe(app, '#') - await mqtt.listen(app, 'brewcast/cbox/handshake/#', on_handshake) - await mqtt.listen(app, 'brewcast/cbox/req/#', on_request) - await mqtt.listen(app, 'brewcast/cbox/resp/#', on_response) - await mqtt.listen(app, 'brewcast/cbox/log/#', on_log) - - await mqtt.publish(app, - topic=f'brewcast/cbox/handshake/{device_id}', - payload='handshake message') + mqtt_client.publish(f'brewcast/cbox/handshake/{config.device_id}', + 'handshake message') - impl = mqtt_connection.MqttConnection(app, device_id, callbacks) + impl = mqtt_connection.MqttConnection(config.device_id, callbacks) await impl.connect() assert impl.connected.is_set() @@ -113,36 +111,19 @@ async def on_log(topic: str, payload: str): await asyncio.wait_for(recv_resp.wait(), timeout=5) callbacks.on_response.assert_awaited_once_with('olleh') - await mqtt.publish(app, - topic=f'brewcast/cbox/log/{device_id}', - payload='log message') + mqtt_client.publish(f'brewcast/cbox/log/{config.device_id}', + 'log message') await asyncio.wait_for(recv_log.wait(), timeout=5) callbacks.on_event.assert_awaited_once_with('log message') # LWT is an empty message to handshake topic recv_handshake.clear() - await mqtt.publish(app, - topic=f'brewcast/cbox/handshake/{device_id}', - payload='') + mqtt_client.publish(f'brewcast/cbox/handshake/{config.device_id}', + None) await asyncio.wait_for(recv_handshake.wait(), timeout=5) assert impl.disconnected.is_set() # Can safely be called await impl.close() assert impl.disconnected.is_set() - - -async def test_isolated(app, client, mocker): - m_listen = mocker.spy(mqtt, 'listen') - m_unlisten = mocker.spy(mqtt, 'unlisten') - app['config'].isolated = True - - tracker = mqtt_connection.MqttDeviceTracker(app) - await tracker.startup(app) - assert await tracker.discover(AsyncMock(), '1234') is None - await tracker.before_shutdown(app) - await tracker.shutdown(app) - - assert m_listen.await_count == 0 - assert m_unlisten.await_count == 0 diff --git a/test/test_processor.py b/test/test_processor.py index b4c9d3d9..beb82550 100644 --- a/test/test_processor.py +++ b/test/test_processor.py @@ -11,18 +11,17 @@ @pytest.fixture -def degf_processor(app): - c = unit_conversion.UnitConverter(app) - c.temperature = 'degF' - m = ProtobufProcessor(c, strip_readonly=False) - return m +def degf_processor(): + unit_conversion.setup() + unit_conversion.CV.get().temperature = 'degF' + return ProtobufProcessor(strip_readonly=False) @pytest.fixture -def degc_processor(app): - c = unit_conversion.UnitConverter(app) - c.temperature = 'degC' - return ProtobufProcessor(c) +def degc_processor(): + unit_conversion.setup() + unit_conversion.CV.get().temperature = 'degC' + return ProtobufProcessor() @pytest.fixture diff --git a/test/test_service_status.py b/test/test_service_status.py deleted file mode 100644 index 88d2f2a2..00000000 --- a/test/test_service_status.py +++ /dev/null @@ -1,139 +0,0 @@ -""" -Tests brewblox_devcon_spark.service_status -""" - -import pytest - -from brewblox_devcon_spark import service_status -from brewblox_devcon_spark.models import (ControllerDescription, - DeviceDescription, - FirmwareDescription, ResetReason, - ServiceConfig, ServiceFirmwareIni) - -TESTED = service_status.__name__ - - -def make_desc(app) -> ControllerDescription: - config: ServiceConfig = app['config'] - ini: ServiceFirmwareIni = app['ini'] - - return ControllerDescription( - system_version='1.23', - platform='mock', - reset_reason=ResetReason.NONE.value, - firmware=FirmwareDescription( - firmware_version=ini['firmware_version'], - proto_version=ini['proto_version'], - firmware_date=ini['firmware_date'], - proto_date=ini['proto_date'], - ), - device=DeviceDescription( - device_id=config.device_id, - ), - ) - - -@pytest.fixture -async def setup(app): - service_status.setup(app) - - -async def test_state_machine(app, client): - service_status.set_connected(app, 'MOCK', 'Narnia') - assert service_status.is_connected(app) - assert not service_status.is_enabled(app) - assert not service_status.is_acknowledged(app) - assert not service_status.is_synchronized(app) - assert not service_status.is_updating(app) - assert not service_status.is_disconnected(app) - assert await service_status.wait_connected(app) - - # Error: acknowledge description should not change after sync - with pytest.raises(RuntimeError): - service_status.set_synchronized(app) - - # By itself, different firmware version does not cause a warning - desc = make_desc(app) - desc.firmware.firmware_version = 'outdated' - service_status.set_acknowledged(app, desc) - assert service_status.is_acknowledged(app) - assert service_status.desc(app).firmware_error == 'MISMATCHED' - - # If proto version differs, firmware is incompatible - desc = make_desc(app) - desc.firmware.proto_version = '' - with pytest.warns(UserWarning, match='incompatible firmware'): - service_status.set_acknowledged(app, desc) - assert service_status.is_acknowledged(app) - assert service_status.desc(app).firmware_error == 'INCOMPATIBLE' - - # If device_id is set and different, identity is incompatible - desc = make_desc(app) - desc.device.device_id = '007' - with pytest.warns(UserWarning, match='incompatible device ID'): - service_status.set_acknowledged(app, desc) - assert service_status.is_acknowledged(app) - assert service_status.desc(app).identity_error == 'INCOMPATIBLE' - - # Disconnect halfway - service_status.set_disconnected(app) - assert not service_status.is_connected(app) - assert not service_status.is_enabled(app) - assert not service_status.is_acknowledged(app) - assert not service_status.is_synchronized(app) - assert not service_status.is_updating(app) - assert service_status.is_disconnected(app) - assert await service_status.wait_disconnected(app) - - # Reconnect, re-acknowledge - service_status.set_connected(app, 'MOCK', 'Narnia') - service_status.set_acknowledged(app, make_desc(app)) - assert service_status.is_connected(app) - assert service_status.is_acknowledged(app) - assert not service_status.is_synchronized(app) - assert not service_status.is_updating(app) - assert not service_status.is_disconnected(app) - assert await service_status.wait_acknowledged(app) - - # Synchronize - service_status.set_synchronized(app) - assert service_status.is_connected(app) - assert service_status.is_acknowledged(app) - assert service_status.is_synchronized(app) - assert not service_status.is_updating(app) - assert not service_status.is_disconnected(app) - assert await service_status.wait_synchronized(app) - - # noop - controller may acknowledge multiple times - service_status.set_acknowledged(app, make_desc(app)) - assert service_status.is_synchronized(app) - - # Set updating status. - service_status.set_updating(app) - assert service_status.is_connected(app) - assert service_status.is_acknowledged(app) - assert service_status.is_synchronized(app) - assert service_status.is_updating(app) - assert not service_status.is_disconnected(app) - assert await service_status.wait_updating(app) - - # Disconnect again - service_status.set_disconnected(app) - assert not service_status.is_connected(app) - assert not service_status.is_enabled(app) - assert not service_status.is_acknowledged(app) - assert not service_status.is_synchronized(app) - assert not service_status.is_updating(app) - assert service_status.is_disconnected(app) - assert await service_status.wait_disconnected(app) - - -async def test_wildcard_error(app, client): - service_status.fget(app).status_desc.service.device.device_id = '' - - # Wildcard ID in service is not a hard error - # but does set identity_error field - service_status.set_connected(app, 'MOCK', 'Narnia') - service_status.set_acknowledged(app, make_desc(app)) - assert service_status.is_acknowledged(app) - assert service_status.desc(app).identity_error == 'WILDCARD_ID' diff --git a/test/test_service_store.py b/test/test_service_store.py deleted file mode 100644 index 8aa40bf5..00000000 --- a/test/test_service_store.py +++ /dev/null @@ -1,117 +0,0 @@ -""" -Tests brewblox_devcon_spark.service_store -""" - - -import asyncio - -import pytest -from aiohttp import web -from aresponses import ResponsesMockServer -from brewblox_service import http, scheduler - -from brewblox_devcon_spark import const, datastore, service_store - -TESTED = service_store.__name__ -DATASTORE = datastore.__name__ - - -def add_config_read_resp(aresponses, count, status=200): - async def handler(request): - return web.json_response({ - 'value': { - 'id': 'XXXX', - 'namespace': const.SPARK_NAMESPACE, - 'data': {'k1': 'v1', 'k2': 'v2'}, - }}, - status=status) - aresponses.add(path_pattern='/history/datastore/get', - method_pattern='POST', - response=handler, - repeat=count) - - -def add_write_resp(aresponses: ResponsesMockServer, count, status=200): - async def handler(request): - return web.json_response(await request.json(), status=status) - aresponses.add(path_pattern='/history/datastore/set', - method_pattern='POST', - response=handler, - repeat=count) - - -@pytest.fixture -async def setup(app, mocker): - mocker.patch(DATASTORE + '.FLUSH_DELAY_S', 0.01) - mocker.patch(DATASTORE + '.RETRY_INTERVAL_S', 0.01) - app['config'].isolated = False - http.setup(app) - scheduler.setup(app) - service_store.setup(app) - - -@pytest.fixture -def store(app): - return service_store.fget(app) - - -async def test_config_read(app, client, store, aresponses): - assert store.active - assert not store.isolated - - add_config_read_resp(aresponses, 1) - await store.read() - await asyncio.sleep(0.05) - - aresponses.assert_plan_strictly_followed() - - -async def test_config_read_error(app, client, store, aresponses): - assert store.active - assert not store.isolated - - add_config_read_resp(aresponses, 1, 405) - with pytest.warns(UserWarning, match='read error'): - await store.read() - await asyncio.sleep(0.05) - - with store.open() as cfg: - assert cfg == {} - - aresponses.assert_plan_strictly_followed() - - -async def test_config_read_empty(app, client, store, aresponses): - assert store.active - assert not store.isolated - - aresponses.add(path_pattern='/history/datastore/get', - method_pattern='POST', - response={'value': None}) - with pytest.warns(UserWarning, match='found no config'): - await store.read() - await asyncio.sleep(0.05) - - with store.open() as cfg: - assert cfg == {} - - aresponses.assert_plan_strictly_followed() - - -async def test_config_write(app, client, store, aresponses): - - add_config_read_resp(aresponses, 1) - add_write_resp(aresponses, 1) - await store.read() - - # Dummy - should not trigger write - with store.open() as cfg: - pass - await asyncio.sleep(0.05) - - # Actual write - with store.open() as cfg: - cfg['newkey'] = True - await asyncio.sleep(0.05) - - aresponses.assert_plan_strictly_followed() diff --git a/test/test_sim_api.py b/test/test_sim_api.py deleted file mode 100644 index 15098b2c..00000000 --- a/test/test_sim_api.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -Tests brewblox_devcon_spark.api.sim_api -""" - -import asyncio - -import pytest -from aiohttp import web -from aiohttp.client_ws import ClientWebSocketResponse -from aiohttp.http_websocket import WSMessage -from brewblox_service import scheduler - -from brewblox_devcon_spark.api import sim_api - -TESTED = sim_api.__name__ - -routes = web.RouteTableDef() - - -@routes.get('/test_sim_api/stream') -async def stream_handler(request: web.Request) -> web.Response: - ws = web.WebSocketResponse() - await ws.prepare(request) - while True: - print('sending...') - await ws.send_str('ignored') - await ws.send_bytes(bytes([1, 2, 3, 4])) - await asyncio.sleep(0.1) - return ws - - -@pytest.fixture -async def setup(app): - """App + controller routes""" - scheduler.setup(app) - sim_api.setup(app) - app.router.add_routes(routes) - - -@pytest.fixture(autouse=True) -async def m_wait_sync(mocker): - mocker.patch(TESTED + '.service_status.wait_synchronized', autospec=True) - - -async def test_sim_display(app, client, mocker): - mocker.patch(TESTED + '.SPARK_WS_ADDR', f'http://localhost:{client.port}/test_sim_api/stream') - - async with client.ws_connect('/sim/display') as ws: - ws: ClientWebSocketResponse - await ws.send_str('ignored') - msg: WSMessage = await ws.receive_bytes(timeout=1) - assert msg == bytes([1, 2, 3, 4]) diff --git a/test/test_state_machine.py b/test/test_state_machine.py new file mode 100644 index 00000000..10abdacb --- /dev/null +++ b/test/test_state_machine.py @@ -0,0 +1,135 @@ +""" +Tests brewblox_devcon_spark.state_machine +""" + +import pytest + +from brewblox_devcon_spark import state_machine, utils +from brewblox_devcon_spark.models import (ControllerDescription, + DeviceDescription, + FirmwareDescription, ResetReason) + +TESTED = state_machine.__name__ + + +def make_desc() -> ControllerDescription: + config = utils.get_config() + fw_config = utils.get_fw_config() + + return ControllerDescription( + system_version='1.23', + platform='mock', + reset_reason=ResetReason.NONE.value, + firmware=FirmwareDescription( + firmware_version=fw_config.firmware_version, + proto_version=fw_config.proto_version, + firmware_date=fw_config.firmware_date, + proto_date=fw_config.proto_date, + ), + device=DeviceDescription( + device_id=config.device_id, + ), + ) + + +async def test_state_machine(): + state = state_machine.StateMachine() + state.set_connected('MOCK', 'Narnia') + assert state.is_connected() + assert not state.is_enabled() + assert not state.is_acknowledged() + assert not state.is_synchronized() + assert not state.is_updating() + assert not state.is_disconnected() + assert await state.wait_connected() + + # Error: acknowledge description should not change after sync + with pytest.raises(RuntimeError): + state.set_synchronized() + + # By itself, different firmware version does not cause a warning + desc = make_desc() + desc.firmware.firmware_version = 'outdated' + state.set_acknowledged(desc) + assert state.is_acknowledged() + assert state.desc().firmware_error == 'MISMATCHED' + + # If proto version differs, firmware is incompatible + desc = make_desc() + desc.firmware.proto_version = '' + state.set_acknowledged(desc) + assert state.is_acknowledged() + assert state.desc().firmware_error == 'INCOMPATIBLE' + + # If device_id is set and different, identity is incompatible + desc = make_desc() + desc.device.device_id = '007' + state.set_acknowledged(desc) + assert state.is_acknowledged() + assert state.desc().identity_error == 'INCOMPATIBLE' + + # Disconnect halfway + state.set_disconnected() + assert not state.is_connected() + assert not state.is_enabled() + assert not state.is_acknowledged() + assert not state.is_synchronized() + assert not state.is_updating() + assert state.is_disconnected() + assert await state.wait_disconnected() + + # Reconnect, re-acknowledge + state.set_connected('MOCK', 'Narnia') + state.set_acknowledged(make_desc()) + assert state.is_connected() + assert state.is_acknowledged() + assert not state.is_synchronized() + assert not state.is_updating() + assert not state.is_disconnected() + assert await state.wait_acknowledged() + + # Synchronize + state.set_synchronized() + assert state.is_connected() + assert state.is_acknowledged() + assert state.is_synchronized() + assert not state.is_updating() + assert not state.is_disconnected() + assert await state.wait_synchronized() + + # noop - controller may acknowledge multiple times + state.set_acknowledged(make_desc()) + assert state.is_synchronized() + + # Set updating state. + state.set_updating() + assert state.is_connected() + assert state.is_acknowledged() + assert state.is_synchronized() + assert state.is_updating() + assert not state.is_disconnected() + assert await state.wait_updating() + + # Disconnect again + state.set_disconnected() + assert not state.is_connected() + assert not state.is_enabled() + assert not state.is_acknowledged() + assert not state.is_synchronized() + assert not state.is_updating() + assert state.is_disconnected() + assert await state.wait_disconnected() + + +async def test_wildcard_error(client): + config = utils.get_config() + config.device_id = '' + + state = state_machine.StateMachine() + + # Wildcard ID in service is not a hard error + # but does set identity_error field + state.set_connected('MOCK', 'Narnia') + state.set_acknowledged(make_desc()) + assert state.is_acknowledged() + assert state.desc().identity_error == 'WILDCARD_ID' diff --git a/test/test_stream_connection.py b/test/test_stream_connection.py index dddc0296..42e8263d 100644 --- a/test/test_stream_connection.py +++ b/test/test_stream_connection.py @@ -3,16 +3,30 @@ """ import asyncio +from typing import Generator import pytest -from brewblox_service.testing import find_free_port +from pytest_mock import MockerFixture +from brewblox_devcon_spark import utils from brewblox_devcon_spark.connection import stream_connection from brewblox_devcon_spark.mdns import ConnectInfo TESTED = stream_connection.__name__ +class EchoServerProtocol(asyncio.Protocol): + def connection_made(self, transport: asyncio.BaseTransport) -> None: + self.transport = transport + + def data_received(self, data: bytes) -> None: + msg = data.decode() + if 'error' in msg: + self.transport.write_eof() + return + self.transport.write(f'{msg}'.encode()) + + class DummyCallbacks(stream_connection.ConnectionCallbacks): def __init__(self) -> None: self.event_msg = None @@ -31,35 +45,22 @@ async def on_response(self, msg: str): @pytest.fixture -def test_port(): - return find_free_port() +def random_port() -> int: + return utils.get_free_port() -@pytest.fixture -async def echo_server(test_port): - async def echo_handler(reader: asyncio.StreamReader, writer: asyncio.StreamWriter): - try: - while True: - data = await reader.read(100) - if not data: - break - if data == b'error\n': - writer.write_eof() - break - writer.write(data+b'') - await writer.drain() - finally: - writer.close() - await writer.wait_closed() - server = await asyncio.start_server(echo_handler, 'localhost', test_port) - task = asyncio.create_task(server.serve_forever()) - yield server - task.cancel() - - -async def test_tcp_connection(app, client, echo_server, test_port): +@pytest.fixture(autouse=True) +async def echo_server(random_port: int) -> Generator[asyncio.Server, None, None]: + loop = asyncio.get_running_loop() + server = await loop.create_server(EchoServerProtocol, 'localhost', random_port) + async with server: + async with utils.task_context(server.serve_forever()): + yield server + + +async def test_tcp_connection(random_port: int): callbacks = DummyCallbacks() - impl = await stream_connection.connect_tcp(app, callbacks, 'localhost', test_port) + impl = await stream_connection.connect_tcp(callbacks, 'localhost', random_port) await impl.send_request('hello') await callbacks.response_ev.wait() @@ -77,26 +78,28 @@ async def test_tcp_connection(app, client, echo_server, test_port): assert callbacks.event_msg == 'event' -async def test_tcp_connection_close(app, client, echo_server, test_port): +async def test_tcp_connection_close(random_port: int): callbacks = DummyCallbacks() - impl = await stream_connection.connect_tcp(app, callbacks, 'localhost', test_port) + impl = await stream_connection.connect_tcp(callbacks, 'localhost', random_port) await impl.close() await asyncio.wait_for(impl.disconnected.wait(), timeout=5) await impl.close() # Can safely be called again -async def test_tcp_connection_error(app, client, echo_server, test_port): +async def test_tcp_connection_error(random_port: int): callbacks = DummyCallbacks() - impl = await stream_connection.connect_tcp(app, callbacks, 'localhost', test_port) + impl = await stream_connection.connect_tcp(callbacks, 'localhost', random_port) await impl.send_request('error') await asyncio.wait_for(impl.disconnected.wait(), timeout=5) -async def test_discover_mdns(app, client, echo_server, mocker, test_port): +async def test_discover_mdns(mocker: MockerFixture, random_port: int): + config = utils.get_config() + m_mdns_discover = mocker.patch(TESTED + '.mdns.discover_one', autospec=True) - m_mdns_discover.return_value = ConnectInfo('localhost', test_port, app['config'].device_id) + m_mdns_discover.return_value = ConnectInfo('localhost', random_port, config.device_id) callbacks = DummyCallbacks() - impl = await stream_connection.discover_mdns(app, callbacks) + impl = await stream_connection.discover_mdns(callbacks) await impl.send_request('mdns') await callbacks.response_ev.wait() @@ -105,8 +108,8 @@ async def test_discover_mdns(app, client, echo_server, mocker, test_port): assert callbacks.event_msg == 'event' -async def test_discover_mdns_none(app, client, mocker): +async def test_discover_mdns_none(mocker: MockerFixture): m_mdns_discover = mocker.patch(TESTED + '.mdns.discover_one', autospec=True) m_mdns_discover.side_effect = asyncio.TimeoutError callbacks = DummyCallbacks() - assert await stream_connection.discover_mdns(app, callbacks) is None + assert await stream_connection.discover_mdns(callbacks) is None diff --git a/test/test_synchronization.py b/test/test_synchronization.py index 855b492d..969da9a0 100644 --- a/test/test_synchronization.py +++ b/test/test_synchronization.py @@ -3,140 +3,189 @@ """ import asyncio +from contextlib import asynccontextmanager +from datetime import timedelta from unittest.mock import AsyncMock import pytest -from brewblox_service import brewblox_logger, scheduler +from asgi_lifespan import LifespanManager +from fastapi import FastAPI +from pytest_mock import MockerFixture -from brewblox_devcon_spark import (block_store, codec, commander, connection, - exceptions, global_store, service_status, - service_store, synchronization) -from brewblox_devcon_spark.models import ServiceStatusDescription +from brewblox_devcon_spark import (codec, command, connection, const, + datastore_blocks, datastore_settings, + exceptions, mqtt, state_machine, + synchronization, utils) +from brewblox_devcon_spark.models import FirmwareBlock TESTED = synchronization.__name__ -LOGGER = brewblox_logger(__name__) -def states(app): - status = service_status.fget(app) +def states(): + state = state_machine.CV.get() return [ - status.disconnected_ev.is_set(), - status.connected_ev.is_set(), - status.acknowledged_ev.is_set(), - status.synchronized_ev.is_set(), + state.is_disconnected(), + state.is_connected(), + state.is_acknowledged(), + state.is_synchronized(), ] -async def connect(app) -> synchronization.SparkSynchronization: - service_status.set_enabled(app, True) - await service_status.wait_connected(app) - s = synchronization.SparkSynchronization(app) - await s.synchronize() - await service_status.wait_synchronized(app) - return s +async def connect(): + store = datastore_settings.CV.get() + sync = synchronization.StateSynchronizer() + store.service_settings.enabled = True + await sync.synchronize() -async def disconnect(app): - service_status.set_enabled(app, False) - await connection.fget(app).start_reconnect() - await service_status.wait_disconnected(app) + +async def disconnect(): + store = datastore_settings.CV.get() + state = state_machine.CV.get() + conn = connection.CV.get() + + store.service_settings.enabled = False + state.set_enabled(False) + await conn.reset() + + +@asynccontextmanager +async def lifespan(app: FastAPI): + async with connection.lifespan(): + yield @pytest.fixture -async def setup(app): - app['config'].isolated = True - scheduler.setup(app) - service_status.setup(app) - codec.setup(app) - connection.setup(app) - commander.setup(app) - global_store.setup(app) - service_store.setup(app) - block_store.setup(app) +def app() -> FastAPI: + state_machine.setup() + mqtt.setup() + codec.setup() + datastore_settings.setup() + datastore_blocks.setup() + connection.setup() + command.setup() + return FastAPI(lifespan=lifespan) -async def test_sync_status(app, client): - await connect(app) - assert states(app) == [False, True, True, True] - await disconnect(app) - assert states(app) == [True, False, False, False] +@pytest.fixture +def m_load_blocks(app: FastAPI, mocker: MockerFixture): + m = mocker.patch.object(datastore_blocks.CV.get(), 'load', autospec=True) + return m - await connect(app) - assert states(app) == [False, True, True, True] +@pytest.fixture(autouse=True) +async def manager(manager: LifespanManager, m_load_blocks: AsyncMock): + yield manager -async def test_sync_errors(app, client, mocker): - mocker.patch(TESTED + '.datastore.check_remote', autospec=True, side_effect=RuntimeError) - with pytest.raises(RuntimeError): - await connect(app) +async def test_sync_status(m_sleep): + await connect() + assert states() == [False, True, True, True] - assert states(app) == [False, True, True, False] + await disconnect() + assert states() == [True, False, False, False] + await connect() + assert states() == [False, True, True, True] -async def test_write_error(app, client, mocker): - mocker.patch.object(commander.fget(app), 'patch_block', autospec=True, side_effect=RuntimeError) + +async def test_sync_errors(m_load_blocks: AsyncMock): + m_load_blocks.side_effect = RuntimeError with pytest.raises(RuntimeError): - await connect(app) + await connect() + + assert states() == [False, True, True, False] - assert states(app) == [False, True, True, False] +async def test_write_error(mocker: MockerFixture): + m_patch_block = mocker.patch.object(command.CV.get(), 'patch_block', autospec=True) + m_patch_block.return_value = FirmwareBlock( + nid=const.SYSINFO_NID, + type='ErrorObject', + data={'error': 'something went wrong'} + ) -async def test_timeout(app, client, mocker): - mocker.patch(TESTED + '.HANDSHAKE_TIMEOUT_S', 0.1) - mocker.patch.object(commander.fget(app), 'version', AsyncMock(side_effect=RuntimeError)) + s = synchronization.StateSynchronizer() + + with pytest.raises(exceptions.CommandException): + await s.synchronize() + + # synchronize raises error after acknowledge + assert states() == [False, True, True, False] + + # run catches error + await s.run() + + # state is reset to disconnected + assert states() == [True, False, False, False] + + +async def test_handshake_timeout(mocker: MockerFixture): + config = utils.get_config() + config.handshake_timeout = timedelta(milliseconds=100) + m_version = mocker.patch.object(command.CV.get(), 'version', autospec=True) + m_version.side_effect = RuntimeError with pytest.raises(asyncio.TimeoutError): - await connect(app) + await connect() + + +async def test_device_name(): + config = utils.get_config() + s = synchronization.StateSynchronizer() + config.mock = True + config.simulation = False + await connect() + assert s.device_name == config.device_id + await disconnect() -async def test_device_name(app, client): - s = await connect(app) - assert s.device_name == app['config'].device_id + config.mock = False + config.simulation = True + await connect() + assert s.device_name == f'simulator__{config.name}' - app['config'].simulation = True - assert s.device_name.startswith('simulator__') +async def test_on_global_store_change(): + store = datastore_settings.CV.get() + sync = synchronization.StateSynchronizer() -async def test_on_global_store_change(app, client): # Update during runtime - s = await connect(app) - global_store.fget(app).units['temperature'] = 'degF' - global_store.fget(app).time_zone['posixValue'] = 'Africa/Casablanca' - await s.on_global_store_change() + await connect() + store.unit_settings.temperature = 'degF' + store.timezone_settings.name = 'Africa/Casablanca' + await sync._apply_global_settings() # Should safely handle disconnected state - await disconnect(app) - await s.on_global_store_change() + await disconnect() + await sync._apply_global_settings() -async def test_incompatible_error(app, client, mocker): - m_desc: ServiceStatusDescription = mocker.patch(TESTED + '.service_status.desc').return_value - m_desc.firmware_error = 'INCOMPATIBLE' - m_desc.identity_error = None +async def test_incompatible_error(mocker: MockerFixture): + state = state_machine.CV.get() + state._status_desc.service.firmware.proto_version = 'ABCD' with pytest.raises(exceptions.IncompatibleFirmware): - await connect(app) - assert states(app) == [False, True, True, False] + await connect() + assert states() == [False, True, True, False] # run() catches IncompatibleFirmware with pytest.raises(asyncio.TimeoutError): - s = synchronization.SparkSynchronization(app) + s = synchronization.StateSynchronizer() await asyncio.wait_for(s.run(), timeout=0.2) -async def test_invalid_error(app, client, mocker): - m_desc: ServiceStatusDescription = mocker.patch(TESTED + '.service_status.desc').return_value - m_desc.firmware_error = None - m_desc.identity_error = 'INVALID' +async def test_invalid_error(mocker: MockerFixture): + state = state_machine.CV.get() + state._status_desc.service.device.device_id = 'XXX' with pytest.raises(exceptions.InvalidDeviceId): - await connect(app) - assert states(app) == [False, True, True, False] + await connect() + assert states() == [False, True, True, False] # run() catches InvalidDeviceId with pytest.raises(asyncio.TimeoutError): - s = synchronization.SparkSynchronization(app) + s = synchronization.StateSynchronizer() await asyncio.wait_for(s.run(), timeout=0.2) diff --git a/test/test_time_sync.py b/test/test_time_sync.py index 3b88875b..bd8ca6e1 100644 --- a/test/test_time_sync.py +++ b/test/test_time_sync.py @@ -3,64 +3,79 @@ """ import asyncio +from contextlib import AsyncExitStack, asynccontextmanager +from datetime import timedelta +from unittest.mock import Mock import pytest -from brewblox_service import repeater, scheduler +from asgi_lifespan import LifespanManager +from fastapi import FastAPI +from pytest_mock import MockerFixture -from brewblox_devcon_spark import (block_store, codec, commander, connection, - controller, global_store, service_status, - service_store, synchronization, time_sync) -from brewblox_devcon_spark.models import ServiceConfig +from brewblox_devcon_spark import (codec, command, connection, control, + datastore_blocks, datastore_settings, mqtt, + state_machine, synchronization, time_sync, + utils) TESTED = time_sync.__name__ -@pytest.fixture(autouse=True) -def m_error_interval(mocker): - mocker.patch(TESTED + '.ERROR_INTERVAL_S', 0.01) +@asynccontextmanager +async def lifespan(app: FastAPI): + async with AsyncExitStack() as stack: + await stack.enter_async_context(mqtt.lifespan()) + await stack.enter_async_context(connection.lifespan()) + await stack.enter_async_context(synchronization.lifespan()) + await state_machine.CV.get().wait_synchronized() + yield @pytest.fixture -def m_controller(mocker): - m = mocker.patch(TESTED + '.controller.fget') - return m.return_value +def app() -> FastAPI(): + config = utils.get_config() + config.mock = True + config.time_sync_interval = timedelta(milliseconds=1) + config.time_sync_retry_interval = timedelta(milliseconds=1) + + mqtt.setup() + state_machine.setup() + datastore_settings.setup() + datastore_blocks.setup() + codec.setup() + connection.setup() + command.setup() + control.setup() + return FastAPI(lifespan=lifespan) -@pytest.fixture -async def setup(app): - config: ServiceConfig = app['config'] - config.time_sync_interval = 0.01 - - service_status.setup(app) - scheduler.setup(app) - codec.setup(app) - block_store.setup(app) - global_store.setup(app) - service_store.setup(app) - connection.setup(app) - commander.setup(app) - synchronization.setup(app) - controller.setup(app) - time_sync.setup(app) +@pytest.fixture(autouse=True) +async def manager(manager: LifespanManager): + yield manager @pytest.fixture -async def synchronized(app, client): - await service_status.wait_synchronized(app) - - -async def test_sync(app, client, synchronized, m_controller): - await asyncio.sleep(0.1) - assert time_sync.fget(app).enabled - assert m_controller.patch_block.call_count > 0 - - -async def test_disabled_sync(app, client, synchronized): - app['config'].time_sync_interval = 0 - - sync = time_sync.TimeSync(app) - with pytest.raises(repeater.RepeaterCancelled): - await sync.prepare() - - assert not sync.enabled - assert not sync.active +def s_patch_block(app: FastAPI, mocker: MockerFixture) -> Mock: + s = mocker.spy(control.CV.get(), 'patch_block') + return s + + +async def test_sync(s_patch_block: Mock): + config = utils.get_config() + sync = time_sync.TimeSync() + await sync.run() + assert s_patch_block.call_count == 1 + + # Normal repeat + s_patch_block.reset_mock() + async with time_sync.lifespan(): + await asyncio.sleep(0.1) + assert s_patch_block.call_count >= 1 + + s_patch_block.reset_mock() + s_patch_block.side_effect = RuntimeError + await asyncio.sleep(0.1) + assert s_patch_block.call_count >= 1 + + # Disabled repeat if interval <= 0 + config.time_sync_interval = timedelta() + await asyncio.wait_for(sync.repeat(), timeout=1) diff --git a/test/test_twinkeydict.py b/test/test_twinkeydict.py index bf88394b..40b1d73c 100644 --- a/test/test_twinkeydict.py +++ b/test/test_twinkeydict.py @@ -24,7 +24,7 @@ def store(): return twinkeydict.TwinKeyDict() -def test_get_set(store, items): +def test_get_set(store: twinkeydict.TwinKeyDict, items: list[tuple]): assert not store store['tri', 'ang'] = 'le' assert store @@ -71,7 +71,7 @@ def test_get_set(store, items): store[None, None] = 'pancakes' -def test_pop_del(store, items): +def test_pop_del(store: twinkeydict.TwinKeyDict, items: list[tuple]): for left, right, value in items: store[left, right] = value @@ -91,7 +91,7 @@ def test_pop_del(store, items): assert (None, 'same') not in store -def test_rename(store): +def test_rename(store: twinkeydict.TwinKeyDict): store['wabber', 'jockey'] = 'alice' store.rename(('wabber', None), ('blobber', None)) with pytest.raises(twinkeydict.TwinKeyError): diff --git a/test/test_unit_conversion.py b/test/test_unit_conversion.py index aa0a91f6..8d421278 100644 --- a/test/test_unit_conversion.py +++ b/test/test_unit_conversion.py @@ -20,8 +20,8 @@ def unit_ids(): ] -def test_convert_default(app, unit_ids): - cv = unit_conversion.UnitConverter(app) +def test_convert_default(unit_ids): + cv = unit_conversion.UnitConverter() for tup in unit_ids: id, unit = tup assert cv.to_sys_value(10, id) == 10 @@ -29,8 +29,8 @@ def test_convert_default(app, unit_ids): assert unit == cv.to_user_unit(id) -def test_convert_sys(app): - cv = unit_conversion.UnitConverter(app) +def test_convert_sys(): + cv = unit_conversion.UnitConverter() assert cv.to_sys_value(10, 'Second', 'mins') == 600 assert cv.to_sys_value(10, 'Second', 'minutes') == 600 assert cv.to_sys_value(10, 'Second', 'min') == 600 @@ -39,8 +39,8 @@ def test_convert_sys(app): cv.to_sys_value(10, 'Second', 'm') -def test_update_config(app, unit_ids): - cv = unit_conversion.UnitConverter(app) +def test_update_config(unit_ids): + cv = unit_conversion.UnitConverter() cv.temperature = 'degF' assert cv.to_user_value(10, 'Celsius') == pytest.approx((10 * 9 / 5) + 32) assert cv.temperature == 'degF' diff --git a/test/test_utils.py b/test/test_utils.py new file mode 100644 index 00000000..954f74f9 --- /dev/null +++ b/test/test_utils.py @@ -0,0 +1,70 @@ +""" +Tests brewblox_devcon_spark.utils +""" +import asyncio +import logging +from datetime import timedelta +from unittest.mock import AsyncMock, Mock + +import pytest + +from brewblox_devcon_spark import utils + +TESTED = utils.__name__ + + +def test_not_sentinel(): + assert utils.not_sentinel('v', 'dv') == 'v' + assert utils.not_sentinel('', 'dv') == '' + assert utils.not_sentinel(None, 'dv') is None + assert utils.not_sentinel(..., 'dvt') == 'dvt' + assert utils.not_sentinel(..., ...) is ... + + +def test_add_logging_level(caplog: pytest.LogCaptureFixture): + logger = logging.getLogger('test_add_logging_level') + + utils.add_logging_level('GOSSIP', logging.WARNING - 1) + + logging.gossip('juicy') + logger.gossip('deets') + + record = caplog.records[-2] + assert record.levelname == 'GOSSIP' + assert record.levelno == logging.GOSSIP + assert record.message == 'juicy' + + record = caplog.records[-1] + assert record.levelname == 'GOSSIP' + assert record.levelno == logging.GOSSIP + assert record.message == 'deets' + + # Repeat calls are ok + utils.add_logging_level('GOSSIP', logging.WARNING - 1) + + # Mismatched level + with pytest.raises(AttributeError): + utils.add_logging_level('GOSSIP', logging.WARNING + 1) + + # Name is already in use + with pytest.raises(AttributeError): + utils.add_logging_level('Gossip', logging.WARNING - 1) + + +async def test_httpx_retry(): + m_ok = Mock() + m_ok.is_success = True + + m_nok = Mock() + m_nok.is_success = False + + f = AsyncMock() + f.return_value = m_ok + + await asyncio.wait_for(utils.httpx_retry(f, interval=timedelta(seconds=2)), timeout=1) + assert f.await_count == 1 + + f.reset_mock() + f.side_effect = [RuntimeError, m_nok, m_ok] + await asyncio.wait_for(utils.httpx_retry(f, interval=timedelta()), timeout=1) + assert f.await_count == 3 diff --git a/tox.ini b/tox.ini deleted file mode 100644 index b6cb424d..00000000 --- a/tox.ini +++ /dev/null @@ -1,33 +0,0 @@ -[pytest] -asyncio_mode = auto -addopts = - --ignore=brewblox_devcon_spark/codec/proto-compiled/ - --cov=brewblox_devcon_spark - --cov-branch - --cov-report=term-missing:skip-covered - --cov-fail-under=100 - --no-cov-on-fail - --durations=3 - - -[coverage:report] -exclude_lines = - pragma: no cover - def __repr__ - if self.debug: - if settings.DEBUG - raise AssertionError - raise NotImplementedError - if 0: - if __name__ == .__main__.: - - -[coverage:run] -omit = - brewblox_devcon_spark/ymodem.py - brewblox_devcon_spark/codec/proto-compiled/* - - -[flake8] -max-line-length = 120 -exclude=*_pb2.py,.venv From 70ad0f52c05223aa494d4268e3f2ef1a5f07bed7 Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Thu, 4 Jan 2024 15:07:59 +0100 Subject: [PATCH 21/27] parse enums by name --- brewblox_devcon_spark/models.py | 31 +++++++++++++++++++------------ test/conftest.py | 1 + 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/brewblox_devcon_spark/models.py b/brewblox_devcon_spark/models.py index c5abe9bf..8e629c24 100644 --- a/brewblox_devcon_spark/models.py +++ b/brewblox_devcon_spark/models.py @@ -10,6 +10,14 @@ from . import const +def parse_enum(cls: type[enum.Enum], v: Any): + """Return enum value if `v` matches either name or value""" + try: + return cls[v] + except KeyError: + return cls(v) + + class DiscoveryType(enum.Enum): all = 1 usb = 2 @@ -120,6 +128,11 @@ class ServiceConfig(BaseSettings): def datastore_url(self) -> str: return f'http://{self.datastore_host}:{self.datastore_port}{self.datastore_path}' + @field_validator('discovery', mode='before') + @classmethod + def parse_discovery(cls, v): + return parse_enum(DiscoveryType, v) + class FirmwareConfig(BaseModel): firmware_version: str @@ -286,10 +299,8 @@ class BasePayload(BaseModel): @field_validator('maskMode', mode='before') @classmethod - def from_raw_mask_mode(cls, v): - if isinstance(v, str): - return MaskMode[v] - return MaskMode(v) + def parse_mask_mode(cls, v): + return parse_enum(MaskMode, v) class EncodedPayload(BasePayload): @@ -311,10 +322,8 @@ class BaseRequest(BaseModel): @field_validator('opcode', mode='before') @classmethod - def from_raw_opcode(cls, v): - if isinstance(v, str): - return Opcode[v] - return Opcode(v) + def parse_opcode(cls, v): + return parse_enum(Opcode, v) class IntermediateRequest(BaseRequest): @@ -332,10 +341,8 @@ class BaseResponse(BaseModel): @field_validator('error', mode='before') @classmethod - def from_raw_error(cls, v): - if isinstance(v, str): - return ErrorCode[v] - return ErrorCode(v) + def parse_error(cls, v): + return parse_enum(ErrorCode, v) class IntermediateResponse(BaseResponse): diff --git a/test/conftest.py b/test/conftest.py index f7b55eef..1704789f 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -65,6 +65,7 @@ def config(monkeypatch: pytest.MonkeyPatch, datastore_port=docker_services.port_for('history', 5000), mock=True, simulation=True, + discovery='all', simulation_workdir=tmp_path / 'simulator', simulation_port=utils.get_free_port(), simulation_display_port=utils.get_free_port(), From 23b96c0d075f6dbaa7af3d34dc3a4832181586e6 Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Thu, 4 Jan 2024 19:06:20 +0100 Subject: [PATCH 22/27] flush blocks on execute --- brewblox_devcon_spark/app_factory.py | 1 - brewblox_devcon_spark/control.py | 55 ++++++------ brewblox_devcon_spark/datastore_blocks.py | 103 ++++++++++------------ brewblox_devcon_spark/synchronization.py | 16 +--- test/test_datastore_blocks.py | 89 ++++++++++++------- test/test_synchronization.py | 16 ---- 6 files changed, 136 insertions(+), 144 deletions(-) diff --git a/brewblox_devcon_spark/app_factory.py b/brewblox_devcon_spark/app_factory.py index 3a4ff468..d3f51dcb 100644 --- a/brewblox_devcon_spark/app_factory.py +++ b/brewblox_devcon_spark/app_factory.py @@ -94,7 +94,6 @@ async def lifespan(app: FastAPI): async with AsyncExitStack() as stack: await stack.enter_async_context(mqtt.lifespan()) await stack.enter_async_context(datastore_settings.lifespan()) - await stack.enter_async_context(datastore_blocks.lifespan()) await stack.enter_async_context(connection.lifespan()) await stack.enter_async_context(synchronization.lifespan()) await stack.enter_async_context(broadcast.lifespan()) diff --git a/brewblox_devcon_spark/control.py b/brewblox_devcon_spark/control.py index bcbcd577..14c3a83b 100644 --- a/brewblox_devcon_spark/control.py +++ b/brewblox_devcon_spark/control.py @@ -69,7 +69,7 @@ def __init__(self): self.config = utils.get_config() self.state = state_machine.CV.get() self.cmder = command.CV.get() - self.store = datastore_blocks.CV.get() + self.block_store = datastore_blocks.CV.get() self._discovery_lock = asyncio.Lock() self._conn_check_lock = asyncio.Lock() @@ -79,13 +79,13 @@ def _validate_sid(self, sid: str): raise exceptions.InvalidId(SID_RULES) if next((keys for keys in const.SYS_OBJECT_KEYS if sid == keys[0]), None): raise exceptions.InvalidId(f'Block ID `{sid}` is reserved for system objects') - if (sid, None) in self.store: + if (sid, None) in self.block_store: raise exceptions.ExistingId(f'Block ID `{sid}` is already in use') def _assign_sid(self, blockType: str): for i in itertools.count(start=1): # pragma: no cover name = f'{const.GENERATED_ID_PREFIX}{blockType}-{i}' - if (name, None) not in self.store: + if (name, None) not in self.block_store: return name def _find_nid(self, sid: str, blockType: str) -> int: @@ -96,7 +96,7 @@ def _find_nid(self, sid: str, blockType: str) -> int: return int(sid) try: - return self.store.right_key(sid) + return self.block_store.right_key(sid) except KeyError: raise exceptions.UnknownId(f'Block ID `{sid}` not found. type={blockType}') @@ -108,11 +108,11 @@ def _find_sid(self, nid: int, blockType: str) -> str: raise exceptions.DecodeException(f'Expected numeric block ID, got string `{nid}`') try: - sid = self.store.left_key(nid) + sid = self.block_store.left_key(nid) except KeyError: # If service ID not found, randomly generate one sid = self._assign_sid(blockType) - self.store[sid, nid] = dict() + self.block_store[sid, nid] = dict() return sid @@ -154,7 +154,7 @@ def _to_firmware_block_identity(self, block: BlockIdentity) -> FirmwareBlockIden if nid is None: try: - nid = self.store.right_key(sid) + nid = self.block_store.right_key(sid) except KeyError: raise exceptions.UnknownId(f'Block ID `{sid}` not found. type={block.type}') @@ -169,7 +169,7 @@ def _to_firmware_block(self, block: Block, find_nid=True) -> FirmwareBlock: if nid is None: try: - nid = self.store.right_key(sid) if find_nid else 0 + nid = self.block_store.right_key(sid) if find_nid else 0 except KeyError: raise exceptions.UnknownId(f'Block ID `{sid}` not found. type={block.type}') @@ -223,7 +223,7 @@ async def _execute(self, desc: str): self.config.command_timeout.total_seconds()) except asyncio.TimeoutError: - raise exceptions.ConnectionException('Not connected') + raise exceptions.NotConnected('Timed out waiting for synchronized state') try: yield @@ -237,6 +237,9 @@ async def _execute(self, desc: str): LOGGER.debug(f'Failed to execute {desc}: {utils.strex(ex)}') raise ex + finally: + asyncio.create_task(self.block_store.flush()) + async def noop(self) -> None: """ Send a Noop command to the controller. @@ -376,20 +379,20 @@ async def create_block(self, block: Block) -> Block: # Avoid race conditions for the desired sid # Claim it with a placeholder until the spark create call returns placeholder_nid = object() - self.store[desired_sid, placeholder_nid] = 'PLACEHOLDER' + self.block_store[desired_sid, placeholder_nid] = 'PLACEHOLDER' try: block = await self.cmder.create_block(block) finally: - del self.store[desired_sid, placeholder_nid] + del self.block_store[desired_sid, placeholder_nid] # It's possible there is a leftover entry with the generated nid # In this case, the newly created entry takes precedence with suppress(KeyError): - del self.store[None, block.nid] + del self.block_store[None, block.nid] # The placeholder is always removed - add real entry if create was ok - self.store[desired_sid, block.nid] = dict() + self.block_store[desired_sid, block.nid] = dict() block = self._to_block(block) return block @@ -414,8 +417,8 @@ async def delete_block(self, block: BlockIdentity) -> BlockIdentity: await self.cmder.delete_block(block) nid = block.nid - sid = self.store.left_key(nid) - del self.store[sid, nid] + sid = self.block_store.left_key(nid) + del self.block_store[sid, nid] ident = BlockIdentity( id=sid, nid=nid, @@ -517,7 +520,7 @@ async def clear_blocks(self) -> list[BlockIdentity]: async with self._execute('Remove all blocks'): blocks = await self.cmder.clear_blocks() identities = [self._to_block_identity(v) for v in blocks] - self.store.clear() + self.block_store.clear() await self.cmder.write_block(FirmwareBlock( nid=const.DISPLAY_SETTINGS_NID, type='DisplaySettings', @@ -540,10 +543,10 @@ async def rename_block(self, change: BlockNameChange) -> BlockIdentity: The new sid + nid. """ self._validate_sid(change.desired) - self.store.rename((change.existing, None), (change.desired, None)) + self.block_store.rename((change.existing, None), (change.desired, None)) return BlockIdentity( id=change.desired, - nid=self.store.right_key(change.desired), + nid=self.block_store.right_key(change.desired), serviceId=self.config.name ) @@ -559,10 +562,10 @@ async def remove_unused_ids(self) -> list[BlockIdentity]: actual = [block.id for block in await self.read_all_blocks()] unused = [(sid, nid) - for (sid, nid) in self.store + for (sid, nid) in self.block_store if sid not in actual] for (sid, nid) in unused.copy(): - del self.store[sid, nid] + del self.block_store[sid, nid] return [BlockIdentity(id=sid, nid=nid, serviceId=self.config.name) for (sid, nid) in unused] @@ -576,7 +579,7 @@ async def make_backup(self) -> Backup: JSON-ready backup data, compatible with apply_backup(). """ store_data = [{'keys': keys, 'data': content} - for keys, content in self.store.items()] + for keys, content in self.block_store.items()] blocks_data = await self.read_all_stored_blocks() timestamp = datetime\ .now(tz=timezone.utc)\ @@ -624,11 +627,11 @@ async def apply_backup(self, exported: Backup) -> BackupApplyResult: for entry in exported.store: try: - self.store[entry.keys] = entry.data + self.block_store[entry.keys] = entry.data except twinkeydict.TwinKeyError: sid, nid = entry.keys - self.store.rename((None, nid), (sid, None)) - self.store[entry.keys] = entry.data + self.block_store.rename((None, nid), (sid, None)) + self.block_store[entry.keys] = entry.data # Now either create or write the objects, depending on whether they are system objects for block in exported.blocks: @@ -648,12 +651,12 @@ async def apply_backup(self, exported: Backup) -> BackupApplyResult: used_nids = [b.nid for b in await self.read_all_blocks()] unused = [ - (sid, nid) for (sid, nid) in self.store + (sid, nid) for (sid, nid) in self.block_store if nid >= const.USER_NID_START and nid not in used_nids ] for sid, nid in unused: - del self.store[sid, nid] + del self.block_store[sid, nid] message = f'Removed unused alias [{sid},{nid}]' LOGGER.info(message) error_log.append(message) diff --git a/brewblox_devcon_spark/datastore_blocks.py b/brewblox_devcon_spark/datastore_blocks.py index 0f1bec8e..6c103556 100644 --- a/brewblox_devcon_spark/datastore_blocks.py +++ b/brewblox_devcon_spark/datastore_blocks.py @@ -1,15 +1,14 @@ """ Stores sid/nid relations for blocks """ - import asyncio import logging -from contextlib import asynccontextmanager, suppress +from contextlib import suppress from contextvars import ContextVar from httpx import AsyncClient -from . import const, utils +from . import const, exceptions, state_machine, utils from .models import (DatastoreSingleQuery, TwinKeyEntriesBox, TwinKeyEntriesValue, TwinKeyEntry) from .twinkeydict import TwinKeyDict, TwinKeyError @@ -29,21 +28,39 @@ def __init__(self, defaults: list[TwinKeyEntry]): super().__init__() self.config = utils.get_config() + self.state = state_machine.CV.get() self._changed_ev = asyncio.Event() - self._doc_id: str = None + self._flush_lock = asyncio.Lock() self._defaults = defaults self._client = AsyncClient(base_url=self.config.datastore_url) - - self.clear() # inserts defaults - - async def load(self, device_id: str): - doc_id = f'{device_id}-blocks-db' + self._doc_id: str | None = None + + def get_doc_id(self) -> str | None: + if not self.state.is_acknowledged(): + return None + + # Simulation services are identified by service name. + # This prevents data conflicts when a simulation service + # is reconfigured to start interacting with a controller. + desc = self.state.desc() + if desc.connection_kind == 'SIM': + device_name = f'simulator__{self.config.name}' + elif desc.connection_kind == 'MOCK': + device_name = f'mock__{self.config.name}' + else: + device_name = desc.controller.device.device_id + + return f'{device_name}-blocks-db' + + async def load(self): + self._doc_id = self.get_doc_id() data: list[TwinKeyEntry] = [] - try: - self._doc_id = None + if not self._doc_id: + raise exceptions.NotConnected('Not acknowledged before loading block store') - query = DatastoreSingleQuery(id=doc_id, + try: + query = DatastoreSingleQuery(id=self._doc_id, namespace=const.SERVICE_NAMESPACE) content = query.model_dump(mode='json') resp = await utils.httpx_retry(lambda: self._client.post('/get', json=content)) @@ -65,44 +82,28 @@ async def load(self, device_id: str): if obj.keys not in self: self.__setitem__(obj.keys, obj.data) - self._doc_id = doc_id + self._changed_ev.clear() - async def save(self): + async def flush(self): if not self._doc_id: - raise ValueError('Document ID not set - did you forget to call load()?') - - data = [TwinKeyEntry(keys=k, data=v) - for k, v in self.items()] - box = TwinKeyEntriesBox( - value=TwinKeyEntriesValue( - id=self._doc_id, - namespace=const.SERVICE_NAMESPACE, - data=data + raise exceptions.NotConnected('Not acknowledged before flushing block store') + + async with self._flush_lock: + if not self._changed_ev.is_set(): + return + + box = TwinKeyEntriesBox( + value=TwinKeyEntriesValue( + id=self._doc_id, + namespace=const.SERVICE_NAMESPACE, + data=[TwinKeyEntry(keys=k, data=v) + for k, v in self.items()] + ) ) - ) - await self._client.post('/set', - json=box.model_dump(mode='json')) - LOGGER.info(f'Saved {len(data)} block(s)') - self._changed_ev.clear() - - async def repeat(self): - while True: - try: - await self._changed_ev.wait() - await asyncio.sleep(self.config.datastore_flush_delay.total_seconds()) - await self.save() - except Exception as ex: # pragma: no cover - LOGGER.error(utils.strex(ex), exc_info=self.config.debug) - - async def on_shutdown(self): - if not self._doc_id or not self._changed_ev.is_set(): - return - - try: - await asyncio.wait_for(self.save(), - timeout=self.config.datastore_shutdown_timeout.total_seconds()) - except Exception as ex: # pragma: no cover - LOGGER.error(utils.strex(ex), exc_info=self.config.debug) + self._changed_ev.clear() + await self._client.post('/set', + json=box.model_dump(mode='json')) + LOGGER.info(f'Saved {len(box.value.data)} block(s)') def __setitem__(self, keys: tuple[str, int], item: dict): super().__setitem__(keys, item) @@ -118,13 +119,5 @@ def clear(self): self.__setitem__(obj.keys, obj.data) -@asynccontextmanager -async def lifespan(): - store = CV.get() - async with utils.task_context(store.repeat()): - yield - await store.on_shutdown() - - def setup(): CV.set(BlockStore(defaults=SYS_OBJECTS)) diff --git a/brewblox_devcon_spark/synchronization.py b/brewblox_devcon_spark/synchronization.py index ad99cae3..669d0e65 100644 --- a/brewblox_devcon_spark/synchronization.py +++ b/brewblox_devcon_spark/synchronization.py @@ -42,7 +42,6 @@ import asyncio import logging -import traceback from contextlib import asynccontextmanager from functools import wraps @@ -82,18 +81,6 @@ def __init__(self): self.converter = codec.unit_conversion.CV.get() self.commander = command.CV.get() - @property - def device_name(self) -> str: - # Simulation services are identified by service name. - # This prevents data conflicts when a simulation service - # is reconfigured to start interacting with a controller. - desc = self.state.desc() - - if desc.connection_kind == 'SIM': - return f'simulator__{self.config.name}' - - return desc.controller.device.device_id - @subroutine('apply global settings') async def _apply_global_settings(self): await self.set_converter_units() @@ -130,14 +117,13 @@ async def _sync_handshake(self): @subroutine('sync block store') async def _sync_block_store(self): - await self.block_store.load(self.device_name) + await self.block_store.load() @subroutine('sync controller settings') async def _sync_sysinfo(self): await self.set_sysinfo_settings() async def set_converter_units(self): - LOGGER.info('\n'.join(traceback.format_tb(None))) self.converter.temperature = self.settings_store.unit_settings.temperature LOGGER.info(f'Service temperature unit set to {self.converter.temperature}') diff --git a/test/test_datastore_blocks.py b/test/test_datastore_blocks.py index 88c3d392..2adc2652 100644 --- a/test/test_datastore_blocks.py +++ b/test/test_datastore_blocks.py @@ -12,8 +12,10 @@ from fastapi import FastAPI from pytest_httpx import HTTPXMock -from brewblox_devcon_spark import const, datastore_blocks, utils -from brewblox_devcon_spark.models import TwinKeyEntry +from brewblox_devcon_spark import (const, datastore_blocks, exceptions, + state_machine, utils) +from brewblox_devcon_spark.models import (ControllerDescription, + DeviceDescription, TwinKeyEntry) TESTED = datastore_blocks.__name__ @@ -30,6 +32,7 @@ def app() -> FastAPI: config = utils.get_config() config.datastore_flush_delay = timedelta() + state_machine.setup() datastore_blocks.setup() return FastAPI() @@ -39,34 +42,28 @@ async def manager(manager: LifespanManager): yield manager -async def test_load_save(httpx_mock: HTTPXMock): +async def test_load_flush(httpx_mock: HTTPXMock): config = utils.get_config() + state = state_machine.CV.get() store = datastore_blocks.CV.get() default_length = len(datastore_blocks.SYS_OBJECTS) read_length = default_length + len(read_objects()) - 1 # overlapping item is merged - assert len(store.items()) == default_length - - # Can't save before loading - with pytest.raises(ValueError): - await store.save() - - # Shutdown does nothing, but also doesn't complain - await store.on_shutdown() + assert len(store.items()) == 0 # Invalid format httpx_mock.add_response(url=f'{config.datastore_url}/get', - match_json={'id': '5678-blocks-db', + match_json={'id': f'mock__{config.name}-blocks-db', 'namespace': const.SERVICE_NAMESPACE}, json={}) httpx_mock.add_response(url=f'{config.datastore_url}/get', - match_json={'id': '5678-blocks-db', + match_json={'id': f'mock__{config.name}-blocks-db', 'namespace': const.SERVICE_NAMESPACE}, json={ 'value': { - 'id': '5678-blocks-db', + 'id': f'mock__{config.name}-blocks-db', 'namespace': const.SERVICE_NAMESPACE, 'data': [v.model_dump(mode='json') for v in read_objects()], @@ -76,46 +73,65 @@ async def test_load_save(httpx_mock: HTTPXMock): httpx_mock.add_response(url=f'{config.datastore_url}/set', match_json={ 'value': { - 'id': '5678-blocks-db', + 'id': f'mock__{config.name}-blocks-db', 'namespace': const.SERVICE_NAMESPACE, 'data': ANY }, }) - # First attempt gets invalid data, and falls back on defaults - await store.load('5678') - assert len(store.items()) == default_length - - await store.load('5678') - assert len(store.items()) == read_length + async with asyncio.timeout(10): + # Can't load before acknowledged + with pytest.raises(exceptions.NotConnected): + await store.load() + + # Can't flush before acknowledged + with pytest.raises(exceptions.NotConnected): + await store.flush() + + state.set_enabled(True) + state.set_connected('MOCK', '') + state.set_acknowledged(ControllerDescription( + system_version='', + platform='mock', + reset_reason='', + firmware=state.desc().service.firmware.model_copy(), + device=DeviceDescription(device_id='1234'), + )) + + # First attempt gets invalid data, and falls back on defaults + await store.load() + assert len(store.items()) == default_length + + await store.load() + assert len(store.items()) == read_length - async with utils.task_context(store.repeat()): - # Flush on insert + # flush on insert store['inserted', 9001] = {} assert len(store.items()) == read_length + 1 - await asyncio.sleep(0) + await store.flush() # Flush on remove del store['inserted', 9001] assert len(store.items()) == read_length - await asyncio.sleep(0) + await store.flush() # Does nothing if not changed - await store.on_shutdown() + await store.flush() - assert len(httpx_mock.get_requests(url=f'{config.datastore_url}/set')) < 3 + assert len(httpx_mock.get_requests(url=f'{config.datastore_url}/set')) == 2 async def test_load_error(httpx_mock: HTTPXMock): config = utils.get_config() + state = state_machine.CV.get() store = datastore_blocks.CV.get() httpx_mock.add_response(url=f'{config.datastore_url}/get', - match_json={'id': '5678-blocks-db', + match_json={'id': '1234-blocks-db', 'namespace': const.SERVICE_NAMESPACE}, json={ 'value': { - 'id': '5678-blocks-db', + 'id': '1234-blocks-db', 'namespace': const.SERVICE_NAMESPACE, 'data': 'miniaturized giant hamsters from outer space', }, @@ -124,5 +140,16 @@ async def test_load_error(httpx_mock: HTTPXMock): # Removed after load store['inserted', 9001] = {} - await store.load('5678') - assert len(store.items()) == len(datastore_blocks.SYS_OBJECTS) + state.set_enabled(True) + state.set_connected('TCP', '') + state.set_acknowledged(ControllerDescription( + system_version='', + platform='dummy', + reset_reason='', + firmware=state.desc().service.firmware.model_copy(), + device=DeviceDescription(device_id='1234'), + )) + + async with asyncio.timeout(10): + await store.load() + assert len(store.items()) == len(datastore_blocks.SYS_OBJECTS) diff --git a/test/test_synchronization.py b/test/test_synchronization.py index 969da9a0..7f6300a5 100644 --- a/test/test_synchronization.py +++ b/test/test_synchronization.py @@ -132,22 +132,6 @@ async def test_handshake_timeout(mocker: MockerFixture): await connect() -async def test_device_name(): - config = utils.get_config() - s = synchronization.StateSynchronizer() - - config.mock = True - config.simulation = False - await connect() - assert s.device_name == config.device_id - await disconnect() - - config.mock = False - config.simulation = True - await connect() - assert s.device_name == f'simulator__{config.name}' - - async def test_on_global_store_change(): store = datastore_settings.CV.get() sync = synchronization.StateSynchronizer() From 3703adcbc08663980e4fe0488c45ddf94c10a7cd Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Fri, 5 Jan 2024 16:32:12 +0100 Subject: [PATCH 23/27] use brewblox_spark_ prefix --- brewblox_devcon_spark/models.py | 2 +- parse_appenv.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/brewblox_devcon_spark/models.py b/brewblox_devcon_spark/models.py index 8e629c24..7b0b6063 100644 --- a/brewblox_devcon_spark/models.py +++ b/brewblox_devcon_spark/models.py @@ -35,7 +35,7 @@ def __str__(self): class ServiceConfig(BaseSettings): model_config = SettingsConfigDict( env_file='.appenv', - env_prefix='brewblox_', + env_prefix='brewblox_spark_', case_sensitive=False, json_schema_extra='ignore', ) diff --git a/parse_appenv.py b/parse_appenv.py index 161c8237..49eb0cb7 100644 --- a/parse_appenv.py +++ b/parse_appenv.py @@ -34,7 +34,7 @@ def parse_cmd_args(raw_args: list[str]) -> tuple[argparse.Namespace, list[str]]: args, unknown = parse_cmd_args(sys.argv[1:]) if unknown: print(f'WARNING: ignoring unknown CMD arguments: {unknown}', file=sys.stderr) - output = [f'brewblox_{k}={shlex.quote(str(v))}' + output = [f'brewblox_spark_{k}={shlex.quote(str(v))}' for k, v in vars(args).items() if v is not None and v is not False] print(*output, sep='\n') From 3ba2a55fb7ace33be6deecfe76df701cc54f8ab2 Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Fri, 5 Jan 2024 17:03:54 +0100 Subject: [PATCH 24/27] update locally use compose files --- docker-compose.yml | 17 +++++++++-------- test/docker-compose.yml | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index f02dc16a..d02df40f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,11 +6,12 @@ services: victoria: image: victoriametrics/victoria-metrics:v1.88.0 - command: >- - --retentionPeriod=100y - --influxMeasurementFieldSeparator=/ - --http.pathPrefix=/victoria - --search.latencyOffset=10s + command: --envflag.enable=true --envflag.prefix=VM_ + environment: + - VM_retentionPeriod=100y + - VM_influxMeasurementFieldSeparator=/ + - VM_http_pathPrefix=/victoria + - VM_search_latencyOffset=10s redis: image: redis:6.0 @@ -36,8 +37,8 @@ services: target: /app/entrypoint.sh environment: - UVICORN_RELOAD=True - - BREWBLOX_DEBUG=True - - BREWBLOX_TRACE=False - - BREWBLOX_SIMULATION=True + - BREWBLOX_SPARK_DEBUG=True + - BREWBLOX_SPARK_TRACE=False + - BREWBLOX_SPARK_SIMULATION=True ports: - "5000:5000" diff --git a/test/docker-compose.yml b/test/docker-compose.yml index 468d7c5d..a31626a1 100644 --- a/test/docker-compose.yml +++ b/test/docker-compose.yml @@ -20,4 +20,4 @@ services: ports: - "5000" environment: - - BREWBLOX_DEBUG=True + - BREWBLOX_HISTORY_DEBUG=True From 440c3efbac33761ef4bd73210a4e39ae0cd28ec8 Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Mon, 8 Jan 2024 16:36:48 +0100 Subject: [PATCH 25/27] api -> endpoints, control -> api (#593) --- brewblox_devcon_spark/api/__init__.py | 3 - brewblox_devcon_spark/app_factory.py | 20 ++--- brewblox_devcon_spark/block_backup.py | 12 +-- brewblox_devcon_spark/broadcast.py | 6 +- brewblox_devcon_spark/endpoints/__init__.py | 21 ++++++ .../http_backup.py} | 3 +- .../http_blocks.py} | 58 +++++++-------- .../debug_api.py => endpoints/http_debug.py} | 2 +- .../http_settings.py} | 3 +- .../{api/sim_api.py => endpoints/http_sim.py} | 0 .../http_system.py} | 15 ++-- .../mqtt_blocks.py} | 14 ++-- .../{control.py => spark_api.py} | 6 +- brewblox_devcon_spark/time_sync.py | 6 +- test/test_app_factory.py | 3 - test/test_block_analysis.py | 4 - test/test_block_backup.py | 16 ++-- test/test_broadcast.py | 12 +-- test/test_codec.py | 4 - ...t_bloxfield.py => test_codec_bloxfield.py} | 4 - ...t_processor.py => test_codec_processor.py} | 4 - ...est_sequence.py => test_codec_sequence.py} | 4 - ...time_utils.py => test_codec_time_utils.py} | 4 - ...rsion.py => test_codec_unit_conversion.py} | 4 - test/test_command.py | 4 - ...rser.py => test_connection_cbox_parser.py} | 5 -- test/test_connection_handler.py | 5 -- ....py => test_connection_mqtt_connection.py} | 4 - ...y => test_connection_stream_connection.py} | 4 - test/test_datastore_blocks.py | 5 -- test/test_datastore_settings.py | 4 - ...t_api.py => test_endpoints_mqtt_blocks.py} | 38 +++++----- test/test_integration.py | 30 +++----- test/test_mdns.py | 4 - test/{test_control.py => test_spark_api.py} | 74 +++++++++---------- test/test_state_machine.py | 4 - test/test_synchronization.py | 4 - test/test_time_sync.py | 14 ++-- test/test_twinkeydict.py | 5 -- test/test_utils.py | 3 - 40 files changed, 168 insertions(+), 267 deletions(-) delete mode 100644 brewblox_devcon_spark/api/__init__.py create mode 100644 brewblox_devcon_spark/endpoints/__init__.py rename brewblox_devcon_spark/{api/backup_api.py => endpoints/http_backup.py} (96%) rename brewblox_devcon_spark/{api/blocks_api.py => endpoints/http_blocks.py} (78%) rename brewblox_devcon_spark/{api/debug_api.py => endpoints/http_debug.py} (98%) rename brewblox_devcon_spark/{api/settings_api.py => endpoints/http_settings.py} (95%) rename brewblox_devcon_spark/{api/sim_api.py => endpoints/http_sim.py} (100%) rename brewblox_devcon_spark/{api/system_api.py => endpoints/http_system.py} (94%) rename brewblox_devcon_spark/{api/blocks_mqtt_api.py => endpoints/mqtt_blocks.py} (80%) rename brewblox_devcon_spark/{control.py => spark_api.py} (99%) rename test/{test_bloxfield.py => test_codec_bloxfield.py} (95%) rename test/{test_processor.py => test_codec_processor.py} (98%) rename test/{test_sequence.py => test_codec_sequence.py} (99%) rename test/{test_time_utils.py => test_codec_time_utils.py} (97%) rename test/{test_unit_conversion.py => test_codec_unit_conversion.py} (96%) rename test/{test_cbox_parser.py => test_connection_cbox_parser.py} (96%) rename test/{test_mqtt_connection.py => test_connection_mqtt_connection.py} (98%) rename test/{test_stream_connection.py => test_connection_stream_connection.py} (98%) rename test/{test_blocks_mqtt_api.py => test_endpoints_mqtt_blocks.py} (79%) rename test/{test_control.py => test_spark_api.py} (76%) diff --git a/brewblox_devcon_spark/api/__init__.py b/brewblox_devcon_spark/api/__init__.py deleted file mode 100644 index dbe1a780..00000000 --- a/brewblox_devcon_spark/api/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -Namespace for all REST API modules. -""" diff --git a/brewblox_devcon_spark/app_factory.py b/brewblox_devcon_spark/app_factory.py index d3f51dcb..ea037c38 100644 --- a/brewblox_devcon_spark/app_factory.py +++ b/brewblox_devcon_spark/app_factory.py @@ -7,11 +7,9 @@ from fastapi.exceptions import RequestValidationError, ResponseValidationError from fastapi.responses import JSONResponse -from . import (block_backup, broadcast, codec, command, connection, control, - datastore_blocks, datastore_settings, mqtt, state_machine, - synchronization, time_sync, utils) -from .api import (backup_api, blocks_api, blocks_mqtt_api, debug_api, - settings_api, sim_api, system_api) +from . import (block_backup, broadcast, codec, command, connection, + datastore_blocks, datastore_settings, endpoints, mqtt, + spark_api, state_machine, synchronization, time_sync, utils) from .models import ErrorResponse LOGGER = logging.getLogger(__name__) @@ -119,9 +117,9 @@ def create_app() -> FastAPI: codec.setup() connection.setup() command.setup() - control.setup() + spark_api.setup() block_backup.setup() - blocks_mqtt_api.setup() + endpoints.setup() # Create app # OpenApi endpoints are set to /api/doc for backwards compatibility @@ -135,11 +133,7 @@ def create_app() -> FastAPI: add_exception_handlers(app) # Include all endpoints declared by modules - app.include_router(blocks_api.router, prefix=prefix) - app.include_router(system_api.router, prefix=prefix) - app.include_router(settings_api.router, prefix=prefix) - app.include_router(sim_api.router, prefix=prefix) - app.include_router(backup_api.router, prefix=prefix) - app.include_router(debug_api.router, prefix=prefix) + for router in endpoints.routers: + app.include_router(router, prefix=prefix) return app diff --git a/brewblox_devcon_spark/block_backup.py b/brewblox_devcon_spark/block_backup.py index ea1f821f..1c3e992d 100644 --- a/brewblox_devcon_spark/block_backup.py +++ b/brewblox_devcon_spark/block_backup.py @@ -8,7 +8,7 @@ from contextvars import ContextVar from datetime import datetime, timedelta -from . import control, exceptions, state_machine, utils +from . import exceptions, spark_api, state_machine, utils from .models import Backup, BackupApplyResult, BackupIdentity LOGGER = logging.getLogger(__name__) @@ -20,16 +20,16 @@ class BackupStorage: def __init__(self): self.config = utils.get_config() self.state = state_machine.CV.get() - self.ctrl = control.CV.get() + self.api = spark_api.CV.get() self.dir = self.config.backup_root_dir / self.config.name self.dir.mkdir(mode=0o777, parents=True, exist_ok=True) async def save_portable(self) -> Backup: - return await self.ctrl.make_backup() + return await self.api.make_backup() async def load_portable(self, data: Backup) -> BackupApplyResult: - return await self.ctrl.apply_backup(data) + return await self.api.apply_backup(data) async def all(self) -> list[BackupIdentity]: return [BackupIdentity(name=f.stem) @@ -55,14 +55,14 @@ async def write(self, data: Backup) -> Backup: return data async def save(self, ident: BackupIdentity): - data = await self.ctrl.make_backup() + data = await self.api.make_backup() data.name = ident.name await self.write(data) return data async def load(self, ident: BackupIdentity) -> BackupApplyResult: data = await self.read(ident) - return await self.ctrl.apply_backup(data) + return await self.api.apply_backup(data) async def run(self): if self.state.is_synchronized(): diff --git a/brewblox_devcon_spark/broadcast.py b/brewblox_devcon_spark/broadcast.py index aa323512..034c0ca2 100644 --- a/brewblox_devcon_spark/broadcast.py +++ b/brewblox_devcon_spark/broadcast.py @@ -8,7 +8,7 @@ from contextlib import asynccontextmanager from datetime import timedelta -from . import const, control, mqtt, state_machine, utils +from . import const, mqtt, spark_api, state_machine, utils from .block_analysis import calculate_claims, calculate_relations from .models import HistoryEvent, ServiceStateEvent, ServiceStateEventData @@ -19,7 +19,7 @@ class Broadcaster: def __init__(self): self.config = utils.get_config() - self.controller = control.CV.get() + self.api = spark_api.CV.get() self.state_topic = f'{self.config.state_topic}/{self.config.name}' self.history_topic = f'{self.config.history_topic}/{self.config.name}' @@ -31,7 +31,7 @@ async def run(self): try: if state.is_synchronized(): - blocks, logged_blocks = await self.controller.read_all_broadcast_blocks() + blocks, logged_blocks = await self.api.read_all_broadcast_blocks() # Convert list to key/value format suitable for history history_data = { diff --git a/brewblox_devcon_spark/endpoints/__init__.py b/brewblox_devcon_spark/endpoints/__init__.py new file mode 100644 index 00000000..15a0b693 --- /dev/null +++ b/brewblox_devcon_spark/endpoints/__init__.py @@ -0,0 +1,21 @@ +""" +Namespace for all REST API modules. +""" + +from fastapi import APIRouter + +from . import (http_backup, http_blocks, http_debug, http_settings, http_sim, + http_system, mqtt_blocks) + +routers: list[APIRouter] = [ + http_backup.router, + http_blocks.router, + http_debug.router, + http_settings.router, + http_sim.router, + http_system.router, +] + + +def setup(): + mqtt_blocks.setup() diff --git a/brewblox_devcon_spark/api/backup_api.py b/brewblox_devcon_spark/endpoints/http_backup.py similarity index 96% rename from brewblox_devcon_spark/api/backup_api.py rename to brewblox_devcon_spark/endpoints/http_backup.py index a8e298a8..ac06489b 100644 --- a/brewblox_devcon_spark/api/backup_api.py +++ b/brewblox_devcon_spark/endpoints/http_backup.py @@ -1,8 +1,7 @@ """ -REST API for handling backups of controller data +REST endpoints for handling backups of controller data """ - import logging from fastapi import APIRouter diff --git a/brewblox_devcon_spark/api/blocks_api.py b/brewblox_devcon_spark/endpoints/http_blocks.py similarity index 78% rename from brewblox_devcon_spark/api/blocks_api.py rename to brewblox_devcon_spark/endpoints/http_blocks.py index 5058ef57..6a98696e 100644 --- a/brewblox_devcon_spark/api/blocks_api.py +++ b/brewblox_devcon_spark/endpoints/http_blocks.py @@ -1,12 +1,12 @@ """ -REST API for Spark blocks +REST endpoints for Spark blocks """ import logging from fastapi import APIRouter -from .. import control, mqtt, utils +from .. import mqtt, spark_api, utils from ..models import Block, BlockIdentity, BlockNameChange LOGGER = logging.getLogger(__name__) @@ -37,7 +37,7 @@ async def blocks_create(args: Block) -> Block: """ Create new block. """ - block = await control.CV.get().create_block(args) + block = await spark_api.CV.get().create_block(args) publish(changed=[block]) return block @@ -47,7 +47,7 @@ async def blocks_read(args: BlockIdentity) -> Block: """ Read existing block. """ - block = await control.CV.get().read_block(args) + block = await spark_api.CV.get().read_block(args) return block @@ -56,7 +56,7 @@ async def blocks_read_logged(args: BlockIdentity) -> Block: """ Read existing block. Data only includes logged fields. """ - block = await control.CV.get().read_logged_block(args) + block = await spark_api.CV.get().read_logged_block(args) return block @@ -65,7 +65,7 @@ async def blocks_read_stored(args: BlockIdentity) -> Block: """ Read existing block. Data only includes stored fields. """ - block = await control.CV.get().read_stored_block(args) + block = await spark_api.CV.get().read_stored_block(args) return block @@ -74,7 +74,7 @@ async def blocks_write(args: Block) -> Block: """ Write existing block. This will replace all fields. """ - block = await control.CV.get().write_block(args) + block = await spark_api.CV.get().write_block(args) publish(changed=[block]) return block @@ -84,7 +84,7 @@ async def blocks_patch(args: Block) -> Block: """ Patch existing block. This will only replace provided fields. """ - block = await control.CV.get().patch_block(args) + block = await spark_api.CV.get().patch_block(args) publish(changed=[block]) return block @@ -94,7 +94,7 @@ async def blocks_delete(args: BlockIdentity) -> BlockIdentity: """ Delete existing user block. """ - ident = await control.CV.get().delete_block(args) + ident = await spark_api.CV.get().delete_block(args) publish(deleted=[ident]) return ident @@ -104,8 +104,8 @@ async def blocks_batch_create(args: list[Block]) -> list[Block]: """ Create multiple new blocks. """ - ctrl = control.CV.get() - blocks = [await ctrl.create_block(block) + api = spark_api.CV.get() + blocks = [await api.create_block(block) for block in args] publish(changed=blocks) return blocks @@ -116,8 +116,8 @@ async def blocks_batch_read(args: list[BlockIdentity]) -> list[Block]: """ Read multiple existing blocks. """ - ctrl = control.CV.get() - blocks = [await ctrl.read_block(ident) + api = spark_api.CV.get() + blocks = [await api.read_block(ident) for ident in args] return blocks @@ -127,8 +127,8 @@ async def blocks_batch_write(args: list[Block]) -> list[Block]: """ Write multiple existing blocks. This will replace all fields. """ - ctrl = control.CV.get() - blocks = [await ctrl.write_block(block) + api = spark_api.CV.get() + blocks = [await api.write_block(block) for block in args] publish(changed=blocks) return blocks @@ -139,8 +139,8 @@ async def blocks_batch_patch(args: list[Block]) -> list[Block]: """ Write multiple existing blocks. This will only replace provided fields. """ - ctrl = control.CV.get() - blocks = [await ctrl.patch_block(block) + api = spark_api.CV.get() + blocks = [await api.patch_block(block) for block in args] publish(changed=blocks) return blocks @@ -151,8 +151,8 @@ async def blocks_batch_delete(args: list[BlockIdentity]) -> list[BlockIdentity]: """ Delete multiple existing user blocks. """ - ctrl = control.CV.get() - idents = [await ctrl.delete_block(ident) + api = spark_api.CV.get() + idents = [await api.delete_block(ident) for ident in args] publish(deleted=idents) return idents @@ -163,7 +163,7 @@ async def blocks_all_read() -> list[Block]: """ Read all existing blocks. """ - blocks = await control.CV.get().read_all_blocks() + blocks = await spark_api.CV.get().read_all_blocks() return blocks @@ -172,7 +172,7 @@ async def blocks_all_read_logged() -> list[Block]: """ Read all existing blocks. Only includes logged fields. """ - blocks = await control.CV.get().read_all_logged_blocks() + blocks = await spark_api.CV.get().read_all_logged_blocks() return blocks @@ -181,7 +181,7 @@ async def blocks_all_read_stored() -> list[Block]: """ Read all existing blocks. Only includes stored fields. """ - blocks = await control.CV.get().read_all_stored_blocks() + blocks = await spark_api.CV.get().read_all_stored_blocks() return blocks @@ -190,7 +190,7 @@ async def blocks_all_delete() -> list[BlockIdentity]: """ Delete all user blocks. """ - idents = await control.CV.get().clear_blocks() + idents = await spark_api.CV.get().clear_blocks() publish(deleted=idents) return idents @@ -200,7 +200,7 @@ async def blocks_cleanup() -> list[BlockIdentity]: """ Clean unused block IDs. """ - idents = await control.CV.get().remove_unused_ids() + idents = await spark_api.CV.get().remove_unused_ids() return idents @@ -210,9 +210,9 @@ async def blocks_rename(args: BlockNameChange) -> BlockIdentity: Rename existing block. """ config = utils.get_config() - ctrl = control.CV.get() - ident = await ctrl.rename_block(args) - block = await ctrl.read_block(ident) + api = spark_api.CV.get() + ident = await api.rename_block(args) + block = await api.read_block(ident) old_ident = BlockIdentity(id=args.existing, serviceId=config.name) publish(changed=[block], deleted=[old_ident]) @@ -224,7 +224,7 @@ async def blocks_discover() -> list[Block]: """ Discover new automatically created blocks. """ - blocks = await control.CV.get().discover_blocks() + blocks = await spark_api.CV.get().discover_blocks() publish(changed=blocks) return blocks @@ -236,5 +236,5 @@ async def blocks_validate(args: Block) -> Block: This checks whether the block can be serialized. It will not be sent to the controller. """ - block = await control.CV.get().validate(args) + block = await spark_api.CV.get().validate(args) return block diff --git a/brewblox_devcon_spark/api/debug_api.py b/brewblox_devcon_spark/endpoints/http_debug.py similarity index 98% rename from brewblox_devcon_spark/api/debug_api.py rename to brewblox_devcon_spark/endpoints/http_debug.py index e6f874ae..2b03623f 100644 --- a/brewblox_devcon_spark/api/debug_api.py +++ b/brewblox_devcon_spark/endpoints/http_debug.py @@ -1,5 +1,5 @@ """ -REST API for system debugging +REST endpoints for system debugging """ import logging diff --git a/brewblox_devcon_spark/api/settings_api.py b/brewblox_devcon_spark/endpoints/http_settings.py similarity index 95% rename from brewblox_devcon_spark/api/settings_api.py rename to brewblox_devcon_spark/endpoints/http_settings.py index 65146e6d..e6dae1a2 100644 --- a/brewblox_devcon_spark/api/settings_api.py +++ b/brewblox_devcon_spark/endpoints/http_settings.py @@ -1,8 +1,7 @@ """ -REST API for persistent settings +REST endpoints for persistent settings """ - import logging from fastapi import APIRouter diff --git a/brewblox_devcon_spark/api/sim_api.py b/brewblox_devcon_spark/endpoints/http_sim.py similarity index 100% rename from brewblox_devcon_spark/api/sim_api.py rename to brewblox_devcon_spark/endpoints/http_sim.py diff --git a/brewblox_devcon_spark/api/system_api.py b/brewblox_devcon_spark/endpoints/http_system.py similarity index 94% rename from brewblox_devcon_spark/api/system_api.py rename to brewblox_devcon_spark/endpoints/http_system.py index 66261d3f..9d222d09 100644 --- a/brewblox_devcon_spark/api/system_api.py +++ b/brewblox_devcon_spark/endpoints/http_system.py @@ -1,5 +1,5 @@ """ -Specific endpoints for using system objects +REST endpoints for system level blocks and commands """ import asyncio @@ -8,7 +8,8 @@ from fastapi import APIRouter, BackgroundTasks from httpx import AsyncClient -from .. import command, control, exceptions, mqtt, state_machine, utils, ymodem +from .. import (command, exceptions, mqtt, spark_api, state_machine, utils, + ymodem) from ..models import FirmwareFlashResponse, PingResponse, StatusDescription ESP_URL_FMT = 'http://brewblox.blob.core.windows.net/firmware/{date}-{version}/brewblox-esp32.bin' @@ -32,7 +33,7 @@ async def system_ping_get() -> PingResponse: """ Ping the controller. """ - await control.CV.get().noop() + await spark_api.CV.get().noop() return PingResponse() @@ -41,7 +42,7 @@ async def system_ping_post() -> PingResponse: """ Ping the controller. """ - await control.CV.get().noop() + await spark_api.CV.get().noop() return PingResponse() @@ -50,7 +51,7 @@ async def system_reboot_controller(): """ Reboot the controller. """ - await control.CV.get().reboot() + await spark_api.CV.get().reboot() return {} @@ -69,7 +70,7 @@ async def system_clear_wifi(): Clear Wifi settings on the controller. The controller may reboot or lose connection. """ - await control.CV.get().clear_wifi() + await spark_api.CV.get().clear_wifi() return {} @@ -79,7 +80,7 @@ async def system_factory_reset(): Factory reset the controller. This does not include firmware. """ - await control.CV.get().factory_reset() + await spark_api.CV.get().factory_reset() return {} diff --git a/brewblox_devcon_spark/api/blocks_mqtt_api.py b/brewblox_devcon_spark/endpoints/mqtt_blocks.py similarity index 80% rename from brewblox_devcon_spark/api/blocks_mqtt_api.py rename to brewblox_devcon_spark/endpoints/mqtt_blocks.py index 1eb1ebb6..afe9c058 100644 --- a/brewblox_devcon_spark/api/blocks_mqtt_api.py +++ b/brewblox_devcon_spark/endpoints/mqtt_blocks.py @@ -1,10 +1,10 @@ """ -MQTT API for Spark blocks +MQTT endpoints for Spark blocks """ import logging -from .. import control, mqtt, utils +from .. import mqtt, spark_api, utils from ..models import Block, BlockIdentity LOGGER = logging.getLogger(__name__) @@ -13,28 +13,28 @@ def setup(): config = utils.get_config() mqtt_client = mqtt.CV.get() - ctrl = control.CV.get() + api = spark_api.CV.get() @mqtt_client.subscribe(config.blocks_topic + '/create') async def on_create(client, topic, payload, qos, properties): block = Block.model_validate_json(payload) if block.serviceId == config.name: - await ctrl.create_block(block) + await api.create_block(block) @mqtt_client.subscribe(config.blocks_topic + '/write') async def on_write(client, topic, payload, qos, properties): block = Block.model_validate_json(payload) if block.serviceId == config.name: - await ctrl.write_block(block) + await api.write_block(block) @mqtt_client.subscribe(config.blocks_topic + '/patch') async def on_patch(client, topic, payload, qos, properties): block = Block.model_validate_json(payload) if block.serviceId == config.name: - await ctrl.patch_block(block) + await api.patch_block(block) @mqtt_client.subscribe(config.blocks_topic + '/delete') async def on_delete(client, topic, payload, qos, properties): ident = BlockIdentity.model_validate_json(payload) if ident.serviceId == config.name: - await ctrl.delete_block(ident) + await api.delete_block(ident) diff --git a/brewblox_devcon_spark/control.py b/brewblox_devcon_spark/spark_api.py similarity index 99% rename from brewblox_devcon_spark/control.py rename to brewblox_devcon_spark/spark_api.py index 14c3a83b..bd65e9c8 100644 --- a/brewblox_devcon_spark/control.py +++ b/brewblox_devcon_spark/spark_api.py @@ -26,7 +26,7 @@ """ LOGGER = logging.getLogger(__name__) -CV: ContextVar['SparkController'] = ContextVar('controller.SparkController') +CV: ContextVar['SparkApi'] = ContextVar('spark_api.SparkApi') def merge(a: dict, b: dict): @@ -63,7 +63,7 @@ def resolve_data_ids(data: Union[dict, list, tuple], resolve_data_ids(v, replacer) -class SparkController: +class SparkApi: def __init__(self): self.config = utils.get_config() @@ -704,4 +704,4 @@ async def validate(self, block: Block) -> Block: def setup(): - CV.set(SparkController()) + CV.set(SparkApi()) diff --git a/brewblox_devcon_spark/time_sync.py b/brewblox_devcon_spark/time_sync.py index b7f3712c..ba41f256 100644 --- a/brewblox_devcon_spark/time_sync.py +++ b/brewblox_devcon_spark/time_sync.py @@ -8,7 +8,7 @@ from contextlib import asynccontextmanager from datetime import datetime, timedelta -from . import const, control, state_machine, utils +from . import const, spark_api, state_machine, utils from .models import Block LOGGER = logging.getLogger(__name__) @@ -19,12 +19,12 @@ class TimeSync: def __init__(self) -> None: self.config = utils.get_config() self.state = state_machine.CV.get() - self.ctrl = control.CV.get() + self.api = spark_api.CV.get() async def run(self): await self.state.wait_synchronized() now = datetime.now() - await self.ctrl.patch_block(Block( + await self.api.patch_block(Block( nid=const.SYSINFO_NID, type=const.SYSINFO_BLOCK_TYPE, data={'systemTime': now}, diff --git a/test/test_app_factory.py b/test/test_app_factory.py index 5d1d09ed..6f6a8655 100644 --- a/test/test_app_factory.py +++ b/test/test_app_factory.py @@ -1,6 +1,3 @@ -""" -Tests brewblox_devcon_spark.app_factory -""" import asyncio import pytest diff --git a/test/test_block_analysis.py b/test/test_block_analysis.py index 8650f5d0..66f9c03a 100644 --- a/test/test_block_analysis.py +++ b/test/test_block_analysis.py @@ -1,7 +1,3 @@ -""" -Tests brewblox_devcon_spark.block_analysis -""" - from brewblox_devcon_spark import block_analysis from brewblox_devcon_spark.models import Block, BlockClaim, BlockRelation diff --git a/test/test_block_backup.py b/test/test_block_backup.py index 5e88a66e..3525337d 100644 --- a/test/test_block_backup.py +++ b/test/test_block_backup.py @@ -1,7 +1,3 @@ -""" -Tests brewblox_devcon_spark.backup -""" - import asyncio from contextlib import AsyncExitStack, asynccontextmanager from datetime import timedelta @@ -12,9 +8,9 @@ from pytest_mock import MockerFixture from brewblox_devcon_spark import (block_backup, codec, command, connection, - control, datastore_blocks, - datastore_settings, mqtt, state_machine, - synchronization, utils) + datastore_blocks, datastore_settings, mqtt, + spark_api, state_machine, synchronization, + utils) from brewblox_devcon_spark.models import Backup, BackupIdentity TESTED = block_backup.__name__ @@ -42,7 +38,7 @@ def app() -> FastAPI(): codec.setup() connection.setup() command.setup() - control.setup() + spark_api.setup() block_backup.setup() return FastAPI(lifespan=lifespan) @@ -70,7 +66,7 @@ async def test_inactive(): async def test_autosave(mocker: MockerFixture): state = state_machine.CV.get() storage = block_backup.CV.get() - ctrl = control.CV.get() + api = spark_api.CV.get() await storage.run() @@ -81,7 +77,7 @@ async def test_autosave(mocker: MockerFixture): data = await storage.read(stored[0]) assert isinstance(data, Backup) - m_make = mocker.patch.object(ctrl, 'make_backup', autospec=True) + m_make = mocker.patch.object(api, 'make_backup', autospec=True) m_make.return_value = Backup(blocks=[], store=[]) async with utils.task_context(storage.repeat()) as task: diff --git a/test/test_broadcast.py b/test/test_broadcast.py index 5ea17a6a..39f70bc5 100644 --- a/test/test_broadcast.py +++ b/test/test_broadcast.py @@ -1,7 +1,3 @@ -""" -Tests brewblox_devcon_spark.broadcast -""" - import asyncio from contextlib import AsyncExitStack, asynccontextmanager from datetime import timedelta @@ -13,9 +9,9 @@ from pytest_mock import MockerFixture from brewblox_devcon_spark import (broadcast, codec, command, connection, - control, datastore_blocks, - datastore_settings, exceptions, mqtt, - state_machine, synchronization, utils) + datastore_blocks, datastore_settings, + exceptions, mqtt, spark_api, state_machine, + synchronization, utils) from brewblox_devcon_spark.connection import mock_connection from brewblox_devcon_spark.models import ErrorCode @@ -44,7 +40,7 @@ def app() -> FastAPI(): codec.setup() connection.setup() command.setup() - control.setup() + spark_api.setup() return FastAPI(lifespan=lifespan) diff --git a/test/test_codec.py b/test/test_codec.py index a5e2c3b7..6759d0f8 100644 --- a/test/test_codec.py +++ b/test/test_codec.py @@ -1,7 +1,3 @@ -""" -Tests brewblox codec -""" - from contextlib import asynccontextmanager import pytest diff --git a/test/test_bloxfield.py b/test/test_codec_bloxfield.py similarity index 95% rename from test/test_bloxfield.py rename to test/test_codec_bloxfield.py index 6aa24c2f..c5a59467 100644 --- a/test/test_bloxfield.py +++ b/test/test_codec_bloxfield.py @@ -1,7 +1,3 @@ -""" -Tests brewblox_devcon_spark.codec.bloxfield -""" - from brewblox_devcon_spark.codec import bloxfield diff --git a/test/test_processor.py b/test/test_codec_processor.py similarity index 98% rename from test/test_processor.py rename to test/test_codec_processor.py index beb82550..83fb2964 100644 --- a/test/test_processor.py +++ b/test/test_codec_processor.py @@ -1,7 +1,3 @@ -""" -Tests brewblox_devcon_spark.codec.processor -""" - import pytest from brewblox_devcon_spark.codec import (DecodeOpts, ProtobufProcessor, diff --git a/test/test_sequence.py b/test/test_codec_sequence.py similarity index 99% rename from test/test_sequence.py rename to test/test_codec_sequence.py index 2ee61484..111d4e8f 100644 --- a/test/test_sequence.py +++ b/test/test_codec_sequence.py @@ -1,7 +1,3 @@ -""" -Tests brewblox_devcon_spark.codec.sequence -""" - import pytest from brewblox_devcon_spark.codec import sequence diff --git a/test/test_time_utils.py b/test/test_codec_time_utils.py similarity index 97% rename from test/test_time_utils.py rename to test/test_codec_time_utils.py index 05505dc3..765792b4 100644 --- a/test/test_time_utils.py +++ b/test/test_codec_time_utils.py @@ -1,7 +1,3 @@ -""" -Tests brewblox_devcon_spark.codec.time_utils -""" - from datetime import datetime, timedelta, timezone import pytest diff --git a/test/test_unit_conversion.py b/test/test_codec_unit_conversion.py similarity index 96% rename from test/test_unit_conversion.py rename to test/test_codec_unit_conversion.py index 8d421278..ce10c69d 100644 --- a/test/test_unit_conversion.py +++ b/test/test_codec_unit_conversion.py @@ -1,7 +1,3 @@ -""" -Tests brewblox_devcon_spark.codec.unit_conversion -""" - import pytest from brewblox_devcon_spark.codec import unit_conversion diff --git a/test/test_command.py b/test/test_command.py index 791f528e..e2953bc6 100644 --- a/test/test_command.py +++ b/test/test_command.py @@ -1,7 +1,3 @@ -""" -Tests brewblox_devcon_spark.command -""" - import asyncio from contextlib import asynccontextmanager from datetime import timedelta diff --git a/test/test_cbox_parser.py b/test/test_connection_cbox_parser.py similarity index 96% rename from test/test_cbox_parser.py rename to test/test_connection_cbox_parser.py index db8820a1..14752eb7 100644 --- a/test/test_cbox_parser.py +++ b/test/test_connection_cbox_parser.py @@ -1,8 +1,3 @@ -""" -Tests brewblox_devcon_spark.connection.cbox_parser -""" - - from brewblox_devcon_spark.connection.cbox_parser import CboxParser diff --git a/test/test_connection_handler.py b/test/test_connection_handler.py index 7e6487a6..0d5ab776 100644 --- a/test/test_connection_handler.py +++ b/test/test_connection_handler.py @@ -1,8 +1,3 @@ -""" -Tests brewblox_devcon_spark.connection.connection_handler -""" - - import asyncio from datetime import timedelta from unittest.mock import AsyncMock, Mock diff --git a/test/test_mqtt_connection.py b/test/test_connection_mqtt_connection.py similarity index 98% rename from test/test_mqtt_connection.py rename to test/test_connection_mqtt_connection.py index f71af6dd..7e270af4 100644 --- a/test/test_mqtt_connection.py +++ b/test/test_connection_mqtt_connection.py @@ -1,7 +1,3 @@ -""" -Tests brewblox_devcon_spark.connection.mqtt_connection -""" - import asyncio from contextlib import asynccontextmanager from datetime import timedelta diff --git a/test/test_stream_connection.py b/test/test_connection_stream_connection.py similarity index 98% rename from test/test_stream_connection.py rename to test/test_connection_stream_connection.py index 42e8263d..096f4854 100644 --- a/test/test_stream_connection.py +++ b/test/test_connection_stream_connection.py @@ -1,7 +1,3 @@ -""" -Tests brewblox_devcon_spark.connection.stream_connection.py -""" - import asyncio from typing import Generator diff --git a/test/test_datastore_blocks.py b/test/test_datastore_blocks.py index 2adc2652..0cacb4ac 100644 --- a/test/test_datastore_blocks.py +++ b/test/test_datastore_blocks.py @@ -1,8 +1,3 @@ -""" -Tests brewblox_devcon_spark.datastore_blocks -""" - - import asyncio from datetime import timedelta from unittest.mock import ANY diff --git a/test/test_datastore_settings.py b/test/test_datastore_settings.py index eb3e0dca..52c7f4f7 100644 --- a/test/test_datastore_settings.py +++ b/test/test_datastore_settings.py @@ -1,7 +1,3 @@ -""" -Tests brewblox_devcon_spark.datastore_settings -""" - import asyncio from contextlib import asynccontextmanager from datetime import timedelta diff --git a/test/test_blocks_mqtt_api.py b/test/test_endpoints_mqtt_blocks.py similarity index 79% rename from test/test_blocks_mqtt_api.py rename to test/test_endpoints_mqtt_blocks.py index a1592f9d..1e5020c4 100644 --- a/test/test_blocks_mqtt_api.py +++ b/test/test_endpoints_mqtt_blocks.py @@ -1,7 +1,3 @@ -""" -Tests brewblox_devcon_spark.api.blocks_mqtt_api -""" - import asyncio from contextlib import AsyncExitStack, asynccontextmanager @@ -10,14 +6,14 @@ from fastapi import FastAPI from pytest_mock import MockerFixture -from brewblox_devcon_spark import (codec, command, connection, control, +from brewblox_devcon_spark import (codec, command, connection, datastore_blocks, datastore_settings, - exceptions, mqtt, state_machine, + exceptions, mqtt, spark_api, state_machine, synchronization, utils) -from brewblox_devcon_spark.api import blocks_mqtt_api +from brewblox_devcon_spark.endpoints import mqtt_blocks from brewblox_devcon_spark.models import Block, BlockIdentity -TESTED = blocks_mqtt_api.__name__ +TESTED = mqtt_blocks.__name__ @asynccontextmanager @@ -41,8 +37,8 @@ def app() -> FastAPI: codec.setup() connection.setup() command.setup() - control.setup() - blocks_mqtt_api.setup() + spark_api.setup() + mqtt_blocks.setup() return FastAPI(lifespan=lifespan) @@ -68,7 +64,7 @@ def block_args(): async def test_crud(mocker: MockerFixture): config = utils.get_config() mqtt_client = mqtt.CV.get() - ctrl = control.CV.get() + api = spark_api.CV.get() def publish(endpoint: str, args: Block | BlockIdentity): mqtt_client.publish(config.blocks_topic + endpoint, args.model_dump(mode='json')) @@ -79,7 +75,7 @@ def wrap(name: str) -> asyncio.Event: As a side effect, an event is set. This allows us to get a callback on function call. """ - func = getattr(ctrl, name) + func = getattr(api, name) ev = asyncio.Event() async def wrapper(*args, **kwargs): @@ -87,7 +83,7 @@ async def wrapper(*args, **kwargs): ev.set() return retv - setattr(ctrl, name, wrapper) + setattr(api, name, wrapper) return ev create_ev = wrap('create_block') @@ -122,27 +118,27 @@ async def wrapper(*args, **kwargs): await asyncio.wait_for(create_ev.wait(), timeout=5) create_ev.clear() - assert await ctrl.read_block(BlockIdentity(id=real.id)) + assert await api.read_block(BlockIdentity(id=real.id)) with pytest.raises(exceptions.UnknownId): - await ctrl.read_block(BlockIdentity(id=dummy.id)) + await api.read_block(BlockIdentity(id=dummy.id)) publish('/write', dummy) publish('/write', real) await asyncio.wait_for(write_ev.wait(), timeout=5) write_ev.clear() - assert await ctrl.read_block(BlockIdentity(id=real.id)) + assert await api.read_block(BlockIdentity(id=real.id)) with pytest.raises(exceptions.UnknownId): - await ctrl.read_block(BlockIdentity(id=dummy.id)) + await api.read_block(BlockIdentity(id=dummy.id)) publish('/patch', dummy) publish('/patch', real) await asyncio.wait_for(patch_ev.wait(), timeout=5) patch_ev.clear() - assert await ctrl.read_block(BlockIdentity(id=real.id)) + assert await api.read_block(BlockIdentity(id=real.id)) with pytest.raises(exceptions.UnknownId): - await ctrl.read_block(BlockIdentity(id=dummy.id)) + await api.read_block(BlockIdentity(id=dummy.id)) publish('/delete', dummy) publish('/delete', real) @@ -150,9 +146,9 @@ async def wrapper(*args, **kwargs): delete_ev.clear() with pytest.raises(exceptions.UnknownId): - assert await ctrl.read_block(BlockIdentity(id=real.id)) + assert await api.read_block(BlockIdentity(id=real.id)) with pytest.raises(exceptions.UnknownId): - await ctrl.read_block(BlockIdentity(id=dummy.id)) + await api.read_block(BlockIdentity(id=dummy.id)) assert not create_ev.is_set() assert not write_ev.is_set() diff --git a/test/test_integration.py b/test/test_integration.py index dd6dccbf..adfb1f99 100644 --- a/test/test_integration.py +++ b/test/test_integration.py @@ -1,7 +1,3 @@ -""" -Integration tests: API calls against the firmware simulator. -""" - import asyncio from contextlib import AsyncExitStack, asynccontextmanager from unittest.mock import ANY, Mock @@ -12,11 +8,10 @@ from pytest_mock import MockerFixture from brewblox_devcon_spark import (app_factory, block_backup, codec, command, - connection, const, control, - datastore_blocks, datastore_settings, mqtt, - state_machine, synchronization, utils) -from brewblox_devcon_spark.api import (backup_api, blocks_api, debug_api, - settings_api, system_api) + connection, const, datastore_blocks, + datastore_settings, endpoints, mqtt, + spark_api, state_machine, synchronization, + utils) from brewblox_devcon_spark.models import (Backup, Block, BlockIdentity, DatastoreMultiQuery, DecodedPayload, EncodedMessage, EncodedPayload, @@ -89,18 +84,15 @@ def app() -> FastAPI: codec.setup() connection.setup() command.setup() - control.setup() + spark_api.setup() block_backup.setup() app = FastAPI(lifespan=lifespan) app_factory.add_exception_handlers(app) - app.include_router(blocks_api.router) - app.include_router(system_api.router) - app.include_router(settings_api.router) - app.include_router(backup_api.router) - app.include_router(debug_api.router) + for router in endpoints.routers: + app.include_router(router) return app @@ -134,7 +126,7 @@ async def test_create(client: AsyncClient, block_args: Block): async def test_invalid_input(client: AsyncClient, block_args: Block, mocker: MockerFixture): - ctrl = control.CV.get() + api = spark_api.CV.get() # 422 if input fails schema check raw = block_args.model_dump() @@ -157,7 +149,7 @@ async def test_invalid_input(client: AsyncClient, block_args: Block, mocker: Moc assert 'validation' not in retv # We need to simulate some bugs now - m = mocker.patch.object(ctrl, 'create_block', autospec=True) + m = mocker.patch.object(api, 'create_block', autospec=True) # 500 if output is invalid # This is a programming error @@ -172,7 +164,7 @@ async def test_invalid_input(client: AsyncClient, block_args: Block, mocker: Moc async def test_invalid_input_prod(client: AsyncClient, block_args: Block, mocker: MockerFixture): - ctrl = control.CV.get() + api = spark_api.CV.get() config = utils.get_config() config.debug = False @@ -197,7 +189,7 @@ async def test_invalid_input_prod(client: AsyncClient, block_args: Block, mocker assert 'validation' not in retv # We need to simulate some bugs now - m = mocker.patch.object(ctrl, 'create_block', autospec=True) + m = mocker.patch.object(api, 'create_block', autospec=True) # 500 if output is invalid # This is a programming error diff --git a/test/test_mdns.py b/test/test_mdns.py index 144e51b3..6650709f 100644 --- a/test/test_mdns.py +++ b/test/test_mdns.py @@ -1,7 +1,3 @@ -""" -Tests brewblox_devcon_spark.mdns -""" - import asyncio from datetime import timedelta from socket import inet_aton diff --git a/test/test_control.py b/test/test_spark_api.py similarity index 76% rename from test/test_control.py rename to test/test_spark_api.py index 49501fcc..8ad1eb2e 100644 --- a/test/test_control.py +++ b/test/test_spark_api.py @@ -1,7 +1,3 @@ -""" -Tests brewblox_devcon_spark.control -""" - import asyncio from contextlib import AsyncExitStack, asynccontextmanager from datetime import timedelta @@ -11,15 +7,15 @@ from fastapi import FastAPI from pytest_mock import MockerFixture -from brewblox_devcon_spark import (codec, command, connection, const, control, +from brewblox_devcon_spark import (codec, command, connection, const, datastore_blocks, datastore_settings, - exceptions, mqtt, state_machine, + exceptions, mqtt, spark_api, state_machine, synchronization, utils) from brewblox_devcon_spark.connection import mock_connection from brewblox_devcon_spark.models import (Block, BlockIdentity, ErrorCode, FirmwareBlock) -TESTED = control.__name__ +TESTED = spark_api.__name__ @asynccontextmanager @@ -43,7 +39,7 @@ def app() -> FastAPI: codec.setup() connection.setup() command.setup() - control.setup() + spark_api.setup() return FastAPI(lifespan=lifespan) @@ -53,19 +49,19 @@ async def manager(manager: LifespanManager): async def test_merge(): - assert control.merge( + assert spark_api.merge( {}, {'a': True} ) == {'a': True} - assert control.merge( + assert spark_api.merge( {'a': False}, {'a': True} ) == {'a': True} - assert control.merge( + assert spark_api.merge( {'a': True}, {'b': True} ) == {'a': True, 'b': True} - assert control.merge( + assert spark_api.merge( {'nested': {'a': False, 'b': True}, 'second': {}}, {'nested': {'a': True}, 'second': 'empty'} ) == {'nested': {'a': True, 'b': True}, 'second': 'empty'} @@ -79,7 +75,7 @@ async def test_merge(): 'word'*50, ]) async def test_validate_sid(sid: str): - control.CV.get()._validate_sid(sid) + spark_api.CV.get()._validate_sid(sid) @pytest.mark.parametrize('sid', [ @@ -97,49 +93,49 @@ async def test_validate_sid(sid: str): ]) async def test_validate_sid_error(sid: str): with pytest.raises(exceptions.InvalidId): - control.CV.get()._validate_sid(sid) + spark_api.CV.get()._validate_sid(sid) async def test_to_firmware_block(): store = datastore_blocks.CV.get() - ctrl = control.CV.get() + api = spark_api.CV.get() store['alias', 123] = dict() store['4-2', 24] = dict() - assert ctrl._to_firmware_block(Block(id='alias', type='', data={})).nid == 123 - assert ctrl._to_firmware_block(Block(nid=840, type='', data={})).nid == 840 + assert api._to_firmware_block(Block(id='alias', type='', data={})).nid == 123 + assert api._to_firmware_block(Block(nid=840, type='', data={})).nid == 840 - assert ctrl._to_firmware_block_identity(BlockIdentity(id='alias')).nid == 123 - assert ctrl._to_firmware_block_identity(BlockIdentity(nid=840)).nid == 840 + assert api._to_firmware_block_identity(BlockIdentity(id='alias')).nid == 123 + assert api._to_firmware_block_identity(BlockIdentity(nid=840)).nid == 840 # When both present, NID takes precedence - assert ctrl._to_firmware_block(Block(id='alias', nid=444, type='', data={})).nid == 444 + assert api._to_firmware_block(Block(id='alias', nid=444, type='', data={})).nid == 444 with pytest.raises(exceptions.UnknownId): - ctrl._to_firmware_block(Block(type='', data={})) + api._to_firmware_block(Block(type='', data={})) with pytest.raises(exceptions.UnknownId): - ctrl._to_firmware_block_identity(BlockIdentity()) + api._to_firmware_block_identity(BlockIdentity()) async def test_to_block(): store = datastore_blocks.CV.get() - ctrl = control.CV.get() + api = spark_api.CV.get() store['alias', 123] = dict() store['4-2', 24] = dict() - assert ctrl._to_block(FirmwareBlock(nid=123, type='', data={})).id == 'alias' + assert api._to_block(FirmwareBlock(nid=123, type='', data={})).id == 'alias' # Service ID not found: create placeholder - generated = ctrl._to_block(FirmwareBlock(nid=456, type='', data={})) + generated = api._to_block(FirmwareBlock(nid=456, type='', data={})) assert generated.id.startswith(const.GENERATED_ID_PREFIX) async def test_resolve_data_ids(): store = datastore_blocks.CV.get() - ctrl = control.CV.get() + api = spark_api.CV.get() store['eeney', 9001] = dict() store['miney', 9002] = dict() @@ -165,7 +161,7 @@ def create_data(): } data = create_data() - control.resolve_data_ids(data, ctrl._find_nid) + spark_api.resolve_data_ids(data, api._find_nid) assert data == { 'testval': 1, @@ -185,59 +181,59 @@ def create_data(): }, } - control.resolve_data_ids(data, ctrl._find_sid) + spark_api.resolve_data_ids(data, api._find_sid) assert data == create_data() - control.resolve_data_ids(data, ctrl._find_nid) + spark_api.resolve_data_ids(data, api._find_nid) data['input'] = 'eeney' with pytest.raises(exceptions.DecodeException): - control.resolve_data_ids(data, ctrl._find_sid) + spark_api.resolve_data_ids(data, api._find_sid) async def test_check_connection(mocker: MockerFixture): config = utils.get_config() sim = connection.CV.get() - ctrl = control.CV.get() + api = spark_api.CV.get() cmder = command.CV.get() s_noop = mocker.spy(cmder, 'noop') s_reset = mocker.spy(cmder, 'reset_connection') - await ctrl.noop() - await ctrl._check_connection() + await api.noop() + await api._check_connection() assert s_noop.await_count == 2 assert s_reset.await_count == 0 config.command_timeout = timedelta(milliseconds=100) with pytest.raises(exceptions.CommandTimeout): mock_connection.NEXT_ERROR = [None] - await ctrl.noop() + await api.noop() await asyncio.sleep(0.01) assert s_noop.await_count == 4 with pytest.raises(exceptions.CommandTimeout): mock_connection.NEXT_ERROR = [None, ErrorCode.INSUFFICIENT_HEAP] - await ctrl.noop() + await api.noop() await asyncio.sleep(0.01) assert s_reset.await_count == 1 # Should be a noop if not connected await sim.end() - await ctrl._check_connection() + await api._check_connection() assert s_noop.await_count == 6 with pytest.raises(exceptions.ConnectionException): - await ctrl.noop() + await api.noop() async def test_start_update(): state = state_machine.CV.get() - ctrl = control.CV.get() + api = spark_api.CV.get() await state.wait_synchronized() state.set_updating() with pytest.raises(exceptions.UpdateInProgress): - await ctrl.read_all_blocks() + await api.read_all_blocks() diff --git a/test/test_state_machine.py b/test/test_state_machine.py index 10abdacb..521ee5fb 100644 --- a/test/test_state_machine.py +++ b/test/test_state_machine.py @@ -1,7 +1,3 @@ -""" -Tests brewblox_devcon_spark.state_machine -""" - import pytest from brewblox_devcon_spark import state_machine, utils diff --git a/test/test_synchronization.py b/test/test_synchronization.py index 7f6300a5..16bf6815 100644 --- a/test/test_synchronization.py +++ b/test/test_synchronization.py @@ -1,7 +1,3 @@ -""" -Tests brewblox_devcon_spark.synchronization -""" - import asyncio from contextlib import asynccontextmanager from datetime import timedelta diff --git a/test/test_time_sync.py b/test/test_time_sync.py index bd8ca6e1..42e862e9 100644 --- a/test/test_time_sync.py +++ b/test/test_time_sync.py @@ -1,7 +1,3 @@ -""" -Tests brewblox_devcon_spark.time_sync -""" - import asyncio from contextlib import AsyncExitStack, asynccontextmanager from datetime import timedelta @@ -12,10 +8,10 @@ from fastapi import FastAPI from pytest_mock import MockerFixture -from brewblox_devcon_spark import (codec, command, connection, control, +from brewblox_devcon_spark import (codec, command, connection, datastore_blocks, datastore_settings, mqtt, - state_machine, synchronization, time_sync, - utils) + spark_api, state_machine, synchronization, + time_sync, utils) TESTED = time_sync.__name__ @@ -44,7 +40,7 @@ def app() -> FastAPI(): codec.setup() connection.setup() command.setup() - control.setup() + spark_api.setup() return FastAPI(lifespan=lifespan) @@ -55,7 +51,7 @@ async def manager(manager: LifespanManager): @pytest.fixture def s_patch_block(app: FastAPI, mocker: MockerFixture) -> Mock: - s = mocker.spy(control.CV.get(), 'patch_block') + s = mocker.spy(spark_api.CV.get(), 'patch_block') return s diff --git a/test/test_twinkeydict.py b/test/test_twinkeydict.py index 40b1d73c..88ef696a 100644 --- a/test/test_twinkeydict.py +++ b/test/test_twinkeydict.py @@ -1,8 +1,3 @@ -""" -Tests brewblox_devcon_spark.simple_store -""" - - import pytest from brewblox_devcon_spark import twinkeydict diff --git a/test/test_utils.py b/test/test_utils.py index 954f74f9..1d51c481 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -1,6 +1,3 @@ -""" -Tests brewblox_devcon_spark.utils -""" import asyncio import logging from datetime import timedelta From 268720a241a8e06939cafdc9c1657f14ba493706 Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Thu, 11 Jan 2024 21:05:51 +0100 Subject: [PATCH 26/27] use models for spark events --- .../endpoints/http_blocks.py | 22 ++++++++-------- .../endpoints/http_system.py | 14 +++++------ brewblox_devcon_spark/models.py | 25 +++++++++++++++++-- test/test_integration.py | 2 ++ 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/brewblox_devcon_spark/endpoints/http_blocks.py b/brewblox_devcon_spark/endpoints/http_blocks.py index 6a98696e..60a1a34e 100644 --- a/brewblox_devcon_spark/endpoints/http_blocks.py +++ b/brewblox_devcon_spark/endpoints/http_blocks.py @@ -7,7 +7,8 @@ from fastapi import APIRouter from .. import mqtt, spark_api, utils -from ..models import Block, BlockIdentity, BlockNameChange +from ..models import (Block, BlockIdentity, BlockNameChange, ServicePatchEvent, + ServicePatchEventData) LOGGER = logging.getLogger(__name__) @@ -18,18 +19,15 @@ def publish(changed: list[Block] = None, deleted: list[BlockIdentity] = None): config = utils.get_config() mqtt_client = mqtt.CV.get() - changed = [v.model_dump(mode='json') for v in changed] if changed else [] - deleted = [v.id for v in deleted] if deleted else [] + changed = changed or [] + deleted = [v.id for v in (deleted or [])] mqtt_client.publish(f'{config.state_topic}/{config.name}/patch', - { - 'key': config.name, - 'type': 'Spark.patch', - 'ttl': '1d', - 'data': { - 'changed': changed, - 'deleted': deleted, - }, - }) + ServicePatchEvent( + key=config.name, + data=ServicePatchEventData( + changed=changed, + deleted=deleted) + ).model_dump(mode='json')) @router.post('/create', status_code=201) diff --git a/brewblox_devcon_spark/endpoints/http_system.py b/brewblox_devcon_spark/endpoints/http_system.py index 9d222d09..fb27c642 100644 --- a/brewblox_devcon_spark/endpoints/http_system.py +++ b/brewblox_devcon_spark/endpoints/http_system.py @@ -10,7 +10,8 @@ from .. import (command, exceptions, mqtt, spark_api, state_machine, utils, ymodem) -from ..models import FirmwareFlashResponse, PingResponse, StatusDescription +from ..models import (FirmwareFlashResponse, PingResponse, ServiceUpdateEvent, + ServiceUpdateEventData, StatusDescription) ESP_URL_FMT = 'http://brewblox.blob.core.windows.net/firmware/{date}-{version}/brewblox-esp32.bin' @@ -105,13 +106,10 @@ def __init__(self, background_tasks: BackgroundTasks) -> None: def _notify(self, msg: str): LOGGER.info(msg) self.mqtt_client.publish(self.notify_topic, - { - 'key': self.config.name, - 'type': 'Spark.update', - 'data': { - 'log': [msg], - }, - }) + ServiceUpdateEvent( + key=self.config.name, + data=ServiceUpdateEventData(log=[msg]) + ).model_dump(mode='json')) async def run(self) -> FirmwareFlashResponse: # pragma: no cover desc = self.state.desc() diff --git a/brewblox_devcon_spark/models.py b/brewblox_devcon_spark/models.py index 7b0b6063..b6242440 100644 --- a/brewblox_devcon_spark/models.py +++ b/brewblox_devcon_spark/models.py @@ -584,6 +584,11 @@ class DatastoreEvent(BaseModel): deleted: list[DatastoreValue] = Field(default_factory=list) +class HistoryEvent(BaseModel): + key: str + data: dict + + class ServiceStateEventData(BaseModel): status: StatusDescription blocks: list[Block] @@ -597,6 +602,22 @@ class ServiceStateEvent(BaseModel): data: ServiceStateEventData -class HistoryEvent(BaseModel): +class ServicePatchEventData(BaseModel): + changed: list[Block] = Field(default_factory=list) + deleted: list[str] = Field(default_factory=list) + + +class ServicePatchEvent(BaseModel): key: str - data: dict + type: Literal['Spark.patch'] = 'Spark.patch' + data: ServicePatchEventData + + +class ServiceUpdateEventData(BaseModel): + log: list[str] + + +class ServiceUpdateEvent(BaseModel): + key: str + type: Literal['Spark.update'] = 'Spark.update' + data: ServiceUpdateEventData diff --git a/test/test_integration.py b/test/test_integration.py index adfb1f99..078661aa 100644 --- a/test/test_integration.py +++ b/test/test_integration.py @@ -150,6 +150,7 @@ async def test_invalid_input(client: AsyncClient, block_args: Block, mocker: Moc # We need to simulate some bugs now m = mocker.patch.object(api, 'create_block', autospec=True) + mocker.patch(endpoints.http_blocks.__name__ + '.publish') # 500 if output is invalid # This is a programming error @@ -190,6 +191,7 @@ async def test_invalid_input_prod(client: AsyncClient, block_args: Block, mocker # We need to simulate some bugs now m = mocker.patch.object(api, 'create_block', autospec=True) + mocker.patch(endpoints.http_blocks.__name__ + '.publish') # 500 if output is invalid # This is a programming error From 744ae05d0b5fc950ec10c7a9c2e290649139f28b Mon Sep 17 00:00:00 2001 From: Bob Steers Date: Tue, 16 Jan 2024 14:52:17 +0100 Subject: [PATCH 27/27] rollback protobuf dependency to prevent segfault --- .vscode/launch.json | 4 ++-- brewblox_devcon_spark/app_factory.py | 3 +++ poetry.lock | 30 +++++++++++++++------------- pyproject.toml | 2 +- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 7336ebfa..099eaba6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,7 @@ "type": "python", "request": "attach", "port": 5678, - "host": "localhost", + "host": "buildbot32.local", "pathMappings": [ { "localRoot": "${workspaceFolder}", @@ -16,4 +16,4 @@ } ], "compounds": [] -} +} \ No newline at end of file diff --git a/brewblox_devcon_spark/app_factory.py b/brewblox_devcon_spark/app_factory.py index ea037c38..8cbd32bf 100644 --- a/brewblox_devcon_spark/app_factory.py +++ b/brewblox_devcon_spark/app_factory.py @@ -105,6 +105,9 @@ def create_app() -> FastAPI: setup_logging(config.debug, config.trace) if config.debugger: # pragma: no cover + import faulthandler + faulthandler.enable() + import debugpy debugpy.listen(('0.0.0.0', 5678)) LOGGER.info('Debugger is enabled and listening on 5678') diff --git a/poetry.lock b/poetry.lock index 3ba777f7..2e2ebe4f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1090,22 +1090,24 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "protobuf" -version = "4.25.1" +version = "4.24.4" description = "" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "protobuf-4.25.1-cp310-abi3-win32.whl", hash = "sha256:193f50a6ab78a970c9b4f148e7c750cfde64f59815e86f686c22e26b4fe01ce7"}, - {file = "protobuf-4.25.1-cp310-abi3-win_amd64.whl", hash = "sha256:3497c1af9f2526962f09329fd61a36566305e6c72da2590ae0d7d1322818843b"}, - {file = "protobuf-4.25.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:0bf384e75b92c42830c0a679b0cd4d6e2b36ae0cf3dbb1e1dfdda48a244f4bcd"}, - {file = "protobuf-4.25.1-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:0f881b589ff449bf0b931a711926e9ddaad3b35089cc039ce1af50b21a4ae8cb"}, - {file = "protobuf-4.25.1-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:ca37bf6a6d0046272c152eea90d2e4ef34593aaa32e8873fc14c16440f22d4b7"}, - {file = "protobuf-4.25.1-cp38-cp38-win32.whl", hash = "sha256:abc0525ae2689a8000837729eef7883b9391cd6aa7950249dcf5a4ede230d5dd"}, - {file = "protobuf-4.25.1-cp38-cp38-win_amd64.whl", hash = "sha256:1484f9e692091450e7edf418c939e15bfc8fc68856e36ce399aed6889dae8bb0"}, - {file = "protobuf-4.25.1-cp39-cp39-win32.whl", hash = "sha256:8bdbeaddaac52d15c6dce38c71b03038ef7772b977847eb6d374fc86636fa510"}, - {file = "protobuf-4.25.1-cp39-cp39-win_amd64.whl", hash = "sha256:becc576b7e6b553d22cbdf418686ee4daa443d7217999125c045ad56322dda10"}, - {file = "protobuf-4.25.1-py3-none-any.whl", hash = "sha256:a19731d5e83ae4737bb2a089605e636077ac001d18781b3cf489b9546c7c80d6"}, - {file = "protobuf-4.25.1.tar.gz", hash = "sha256:57d65074b4f5baa4ab5da1605c02be90ac20c8b40fb137d6a8df9f416b0d0ce2"}, + {file = "protobuf-4.24.4-cp310-abi3-win32.whl", hash = "sha256:ec9912d5cb6714a5710e28e592ee1093d68c5ebfeda61983b3f40331da0b1ebb"}, + {file = "protobuf-4.24.4-cp310-abi3-win_amd64.whl", hash = "sha256:1badab72aa8a3a2b812eacfede5020472e16c6b2212d737cefd685884c191085"}, + {file = "protobuf-4.24.4-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8e61a27f362369c2f33248a0ff6896c20dcd47b5d48239cb9720134bef6082e4"}, + {file = "protobuf-4.24.4-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:bffa46ad9612e6779d0e51ae586fde768339b791a50610d85eb162daeb23661e"}, + {file = "protobuf-4.24.4-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:b493cb590960ff863743b9ff1452c413c2ee12b782f48beca77c8da3e2ffe9d9"}, + {file = "protobuf-4.24.4-cp37-cp37m-win32.whl", hash = "sha256:dbbed8a56e56cee8d9d522ce844a1379a72a70f453bde6243e3c86c30c2a3d46"}, + {file = "protobuf-4.24.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6b7d2e1c753715dcfe9d284a25a52d67818dd43c4932574307daf836f0071e37"}, + {file = "protobuf-4.24.4-cp38-cp38-win32.whl", hash = "sha256:02212557a76cd99574775a81fefeba8738d0f668d6abd0c6b1d3adcc75503dbe"}, + {file = "protobuf-4.24.4-cp38-cp38-win_amd64.whl", hash = "sha256:2fa3886dfaae6b4c5ed2730d3bf47c7a38a72b3a1f0acb4d4caf68e6874b947b"}, + {file = "protobuf-4.24.4-cp39-cp39-win32.whl", hash = "sha256:b77272f3e28bb416e2071186cb39efd4abbf696d682cbb5dc731308ad37fa6dd"}, + {file = "protobuf-4.24.4-cp39-cp39-win_amd64.whl", hash = "sha256:9fee5e8aa20ef1b84123bb9232b3f4a5114d9897ed89b4b8142d81924e05d79b"}, + {file = "protobuf-4.24.4-py3-none-any.whl", hash = "sha256:80797ce7424f8c8d2f2547e2d42bfbb6c08230ce5832d6c099a37335c9c90a92"}, + {file = "protobuf-4.24.4.tar.gz", hash = "sha256:5a70731910cd9104762161719c3d883c960151eea077134458503723b60e3667"}, ] [[package]] @@ -1845,4 +1847,4 @@ h11 = ">=0.9.0,<1" [metadata] lock-version = "2.0" python-versions = ">=3.11,<4" -content-hash = "c74110c6ad66915735859d348d00c6a70f1e38a5da6580d547496b367e1ed7c0" +content-hash = "4a86a91d2245ae66a3805d5b16acdbba77ddbf8fd4e467314084528cb613484b" diff --git a/pyproject.toml b/pyproject.toml index 03c345ab..ff19111b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ readme = "README.md" [tool.poetry.dependencies] python = ">=3.11,<4" pyserial-asyncio = "^0.6" -protobuf = "^4.24.3" +protobuf = "<4.25" aiofiles = "^23.2.1" aiozeroconf = "^0.1.8" debugpy = "^1.5.1"