diff --git a/daemon/core/api/grpc/client.py b/daemon/core/api/grpc/client.py index a1559d64..c66b5b7c 100644 --- a/daemon/core/api/grpc/client.py +++ b/daemon/core/api/grpc/client.py @@ -4,10 +4,11 @@ gRpc client for interfacing with CORE. import logging import threading +from collections.abc import Callable, Generator, Iterable from contextlib import contextmanager from pathlib import Path from queue import Queue -from typing import Any, Callable, Dict, Generator, Iterable, List, Optional, Tuple +from typing import Any, Optional import grpc @@ -235,7 +236,7 @@ class CoreGrpcClient: def start_session( self, session: wrappers.Session, definition: bool = False - ) -> Tuple[bool, List[str]]: + ) -> tuple[bool, list[str]]: """ Start a session. @@ -285,7 +286,7 @@ class CoreGrpcClient: response = self.stub.DeleteSession(request) return response.result - def get_sessions(self) -> List[wrappers.SessionSummary]: + def get_sessions(self) -> list[wrappers.SessionSummary]: """ Retrieves all currently known sessions. @@ -354,7 +355,7 @@ class CoreGrpcClient: self, session_id: int, handler: Callable[[wrappers.Event], None], - events: List[wrappers.EventType] = None, + events: list[wrappers.EventType] = None, ) -> grpc.Future: """ Listen for session events. @@ -428,7 +429,7 @@ class CoreGrpcClient: def get_node( self, session_id: int, node_id: int - ) -> Tuple[wrappers.Node, List[wrappers.Interface], List[wrappers.Link]]: + ) -> tuple[wrappers.Node, list[wrappers.Interface], list[wrappers.Link]]: """ Get node details. @@ -536,7 +537,7 @@ class CoreGrpcClient: command: str, wait: bool = True, shell: bool = False, - ) -> Tuple[int, str]: + ) -> tuple[int, str]: """ Send command to a node and get the output. @@ -575,7 +576,7 @@ class CoreGrpcClient: def add_link( self, session_id: int, link: wrappers.Link, source: str = None - ) -> Tuple[bool, wrappers.Interface, wrappers.Interface]: + ) -> tuple[bool, wrappers.Interface, wrappers.Interface]: """ Add a link between nodes. @@ -646,7 +647,7 @@ class CoreGrpcClient: def get_mobility_config( self, session_id: int, node_id: int - ) -> Dict[str, wrappers.ConfigOption]: + ) -> dict[str, wrappers.ConfigOption]: """ Get mobility configuration for a node. @@ -660,7 +661,7 @@ class CoreGrpcClient: return wrappers.ConfigOption.from_dict(response.config) def set_mobility_config( - self, session_id: int, node_id: int, config: Dict[str, str] + self, session_id: int, node_id: int, config: dict[str, str] ) -> bool: """ Set mobility configuration for a node. @@ -706,7 +707,7 @@ class CoreGrpcClient: response = self.stub.GetConfig(request) return wrappers.CoreConfig.from_proto(response) - def get_service_defaults(self, session_id: int) -> List[wrappers.ServiceDefault]: + def get_service_defaults(self, session_id: int) -> list[wrappers.ServiceDefault]: """ Get default services for different default node models. @@ -723,7 +724,7 @@ class CoreGrpcClient: return defaults def set_service_defaults( - self, session_id: int, service_defaults: Dict[str, List[str]] + self, session_id: int, service_defaults: dict[str, list[str]] ) -> bool: """ Set default services for node models. @@ -829,7 +830,7 @@ class CoreGrpcClient: def get_wlan_config( self, session_id: int, node_id: int - ) -> Dict[str, wrappers.ConfigOption]: + ) -> dict[str, wrappers.ConfigOption]: """ Get wlan configuration for a node. @@ -843,7 +844,7 @@ class CoreGrpcClient: return wrappers.ConfigOption.from_dict(response.config) def set_wlan_config( - self, session_id: int, node_id: int, config: Dict[str, str] + self, session_id: int, node_id: int, config: dict[str, str] ) -> bool: """ Set wlan configuration for a node. @@ -861,7 +862,7 @@ class CoreGrpcClient: def get_emane_model_config( self, session_id: int, node_id: int, model: str, iface_id: int = -1 - ) -> Dict[str, wrappers.ConfigOption]: + ) -> dict[str, wrappers.ConfigOption]: """ Get emane model configuration for a node or a node's interface. @@ -909,7 +910,7 @@ class CoreGrpcClient: with open(file_path, "w") as xml_file: xml_file.write(response.data) - def open_xml(self, file_path: Path, start: bool = False) -> Tuple[bool, int]: + def open_xml(self, file_path: Path, start: bool = False) -> tuple[bool, int]: """ Load a local scenario XML file to open as a new session. @@ -940,7 +941,7 @@ class CoreGrpcClient: response = self.stub.EmaneLink(request) return response.result - def get_ifaces(self) -> List[str]: + def get_ifaces(self) -> list[str]: """ Retrieves a list of interfaces available on the host machine that are not a part of a CORE session. @@ -964,7 +965,7 @@ class CoreGrpcClient: def get_node_config_service( self, session_id: int, node_id: int, name: str - ) -> Dict[str, str]: + ) -> dict[str, str]: """ Retrieves information for a specific config service on a node. @@ -982,7 +983,7 @@ class CoreGrpcClient: def get_config_service_rendered( self, session_id: int, node_id: int, name: str - ) -> Dict[str, str]: + ) -> dict[str, str]: """ Retrieve the rendered config service files for a node. @@ -1129,7 +1130,7 @@ class CoreGrpcClient: def get_wireless_config( self, session_id: int, node_id: int - ) -> Dict[str, wrappers.ConfigOption]: + ) -> dict[str, wrappers.ConfigOption]: request = GetWirelessConfigRequest(session_id=session_id, node_id=node_id) response = self.stub.GetWirelessConfig(request) return wrappers.ConfigOption.from_dict(response.config) @@ -1156,7 +1157,7 @@ class CoreGrpcClient: self.channel = None @contextmanager - def context_connect(self) -> Generator: + def context_connect(self) -> Generator[None, None, None]: """ Makes a context manager based connection to the server, will close after context ends. diff --git a/daemon/core/api/grpc/events.py b/daemon/core/api/grpc/events.py index 82f03c20..65a20296 100644 --- a/daemon/core/api/grpc/events.py +++ b/daemon/core/api/grpc/events.py @@ -1,6 +1,7 @@ import logging +from collections.abc import Iterable from queue import Empty, Queue -from typing import Iterable, Optional +from typing import Optional from core.api.grpc import core_pb2, grpcutils from core.api.grpc.grpcutils import convert_link_data diff --git a/daemon/core/api/grpc/grpcutils.py b/daemon/core/api/grpc/grpcutils.py index 434314a4..a05a5e8f 100644 --- a/daemon/core/api/grpc/grpcutils.py +++ b/daemon/core/api/grpc/grpcutils.py @@ -1,7 +1,7 @@ import logging import time from pathlib import Path -from typing import Any, Dict, List, Optional, Tuple, Type, Union +from typing import Any, Optional, Union import grpc from grpc import ServicerContext @@ -63,8 +63,8 @@ class CpuUsage: def add_node_data( - _class: Type[NodeBase], node_proto: core_pb2.Node -) -> Tuple[Position, NodeOptions]: + _class: type[NodeBase], node_proto: core_pb2.Node +) -> tuple[Position, NodeOptions]: """ Convert node protobuf message to data for creating a node. @@ -118,7 +118,7 @@ def link_iface(iface_proto: core_pb2.Interface) -> InterfaceData: def add_link_data( link_proto: core_pb2.Link, -) -> Tuple[InterfaceData, InterfaceData, LinkOptions]: +) -> tuple[InterfaceData, InterfaceData, LinkOptions]: """ Convert link proto to link interfaces and options data. @@ -145,8 +145,8 @@ def add_link_data( def create_nodes( - session: Session, node_protos: List[core_pb2.Node] -) -> Tuple[List[NodeBase], List[Exception]]: + session: Session, node_protos: list[core_pb2.Node] +) -> tuple[list[NodeBase], list[Exception]]: """ Create nodes using a thread pool and wait for completion. @@ -176,8 +176,8 @@ def create_nodes( def create_links( - session: Session, link_protos: List[core_pb2.Link] -) -> Tuple[List[NodeBase], List[Exception]]: + session: Session, link_protos: list[core_pb2.Link] +) -> tuple[list[NodeBase], list[Exception]]: """ Create links using a thread pool and wait for completion. @@ -200,8 +200,8 @@ def create_links( def edit_links( - session: Session, link_protos: List[core_pb2.Link] -) -> Tuple[List[None], List[Exception]]: + session: Session, link_protos: list[core_pb2.Link] +) -> tuple[list[None], list[Exception]]: """ Edit links using a thread pool and wait for completion. @@ -235,7 +235,7 @@ def convert_value(value: Any) -> str: return value -def convert_session_options(session: Session) -> Dict[str, common_pb2.ConfigOption]: +def convert_session_options(session: Session) -> dict[str, common_pb2.ConfigOption]: config_options = {} for option in session.options.options: value = session.options.get(option.id) @@ -252,9 +252,9 @@ def convert_session_options(session: Session) -> Dict[str, common_pb2.ConfigOpti def get_config_options( - config: Dict[str, str], - configurable_options: Union[ConfigurableOptions, Type[ConfigurableOptions]], -) -> Dict[str, common_pb2.ConfigOption]: + config: dict[str, str], + configurable_options: Union[ConfigurableOptions, type[ConfigurableOptions]], +) -> dict[str, common_pb2.ConfigOption]: """ Retrieve configuration options in a form that is used by the grpc server. @@ -283,7 +283,7 @@ def get_config_options( def get_node_proto( - session: Session, node: NodeBase, emane_configs: List[NodeEmaneConfig] + session: Session, node: NodeBase, emane_configs: list[NodeEmaneConfig] ) -> core_pb2.Node: """ Convert CORE node to protobuf representation. @@ -390,7 +390,7 @@ def get_node_proto( ) -def get_links(session: Session, node: NodeBase) -> List[core_pb2.Link]: +def get_links(session: Session, node: NodeBase) -> list[core_pb2.Link]: """ Retrieve a list of links for grpc to use. @@ -435,7 +435,7 @@ def convert_iface(iface: CoreInterface) -> core_pb2.Interface: ) -def convert_core_link(core_link: CoreLink) -> List[core_pb2.Link]: +def convert_core_link(core_link: CoreLink) -> list[core_pb2.Link]: """ Convert core link to protobuf data. @@ -581,7 +581,7 @@ def convert_link( ) -def parse_proc_net_dev(lines: List[str]) -> Dict[str, Any]: +def parse_proc_net_dev(lines: list[str]) -> dict[str, dict[str, float]]: """ Parse lines of output from /proc/net/dev. @@ -599,7 +599,7 @@ def parse_proc_net_dev(lines: List[str]) -> Dict[str, Any]: return stats -def get_net_stats() -> Dict[str, Dict]: +def get_net_stats() -> dict[str, dict[str, float]]: """ Retrieve status about the current interfaces in the system @@ -728,7 +728,7 @@ def get_nem_id( return nem_id -def get_emane_model_configs_dict(session: Session) -> Dict[int, List[NodeEmaneConfig]]: +def get_emane_model_configs_dict(session: Session) -> dict[int, list[NodeEmaneConfig]]: """ Get emane model configuration protobuf data. @@ -751,7 +751,7 @@ def get_emane_model_configs_dict(session: Session) -> Dict[int, List[NodeEmaneCo return configs -def get_hooks(session: Session) -> List[core_pb2.Hook]: +def get_hooks(session: Session) -> list[core_pb2.Hook]: """ Retrieve hook protobuf data for a session. @@ -767,7 +767,7 @@ def get_hooks(session: Session) -> List[core_pb2.Hook]: return hooks -def get_default_services(session: Session) -> List[ServiceDefaults]: +def get_default_services(session: Session) -> list[ServiceDefaults]: """ Retrieve the default service sets for a given session. diff --git a/daemon/core/api/grpc/server.py b/daemon/core/api/grpc/server.py index f657c20a..69822252 100644 --- a/daemon/core/api/grpc/server.py +++ b/daemon/core/api/grpc/server.py @@ -5,9 +5,11 @@ import signal import sys import tempfile import time +from collections.abc import Iterable from concurrent import futures from pathlib import Path -from typing import Iterable, Optional, Pattern, Type +from re import Pattern +from typing import Optional import grpc from grpc import ServicerContext @@ -105,7 +107,7 @@ from core.services.coreservices import ServiceManager logger = logging.getLogger(__name__) _ONE_DAY_IN_SECONDS: int = 60 * 60 * 24 -_INTERFACE_REGEX: Pattern = re.compile(r"beth(?P[0-9a-fA-F]+)") +_INTERFACE_REGEX: Pattern[str] = re.compile(r"beth(?P[0-9a-fA-F]+)") _MAX_WORKERS = 1000 @@ -171,7 +173,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): return session def get_node( - self, session: Session, node_id: int, context: ServicerContext, _class: Type[NT] + self, session: Session, node_id: int, context: ServicerContext, _class: type[NT] ) -> NT: """ Retrieve node given session and node id @@ -210,7 +212,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): def validate_service( self, name: str, context: ServicerContext - ) -> Type[ConfigService]: + ) -> type[ConfigService]: """ Validates a configuration service is a valid known service. diff --git a/daemon/core/api/grpc/wrappers.py b/daemon/core/api/grpc/wrappers.py index d3167a98..b7b172fe 100644 --- a/daemon/core/api/grpc/wrappers.py +++ b/daemon/core/api/grpc/wrappers.py @@ -1,7 +1,7 @@ from dataclasses import dataclass, field from enum import Enum from pathlib import Path -from typing import Any, Dict, List, Optional, Set, Tuple +from typing import Any, Optional from core.api.grpc import ( common_pb2, @@ -114,13 +114,13 @@ class EventType: class ConfigService: group: str name: str - executables: List[str] - dependencies: List[str] - directories: List[str] - files: List[str] - startup: List[str] - validate: List[str] - shutdown: List[str] + executables: list[str] + dependencies: list[str] + directories: list[str] + files: list[str] + startup: list[str] + validate: list[str] + shutdown: list[str] validation_mode: ConfigServiceValidationMode validation_timer: int validation_period: float @@ -147,8 +147,8 @@ class ConfigService: class ConfigServiceConfig: node_id: int name: str - templates: Dict[str, str] - config: Dict[str, str] + templates: dict[str, str] + config: dict[str, str] @classmethod def from_proto( @@ -164,15 +164,15 @@ class ConfigServiceConfig: @dataclass class ConfigServiceData: - templates: Dict[str, str] = field(default_factory=dict) - config: Dict[str, str] = field(default_factory=dict) + templates: dict[str, str] = field(default_factory=dict) + config: dict[str, str] = field(default_factory=dict) @dataclass class ConfigServiceDefaults: - templates: Dict[str, str] - config: Dict[str, "ConfigOption"] - modes: Dict[str, Dict[str, str]] + templates: dict[str, str] + config: dict[str, "ConfigOption"] + modes: dict[str, dict[str, str]] @classmethod def from_proto( @@ -211,7 +211,7 @@ class Service: @dataclass class ServiceDefault: model: str - services: List[str] + services: list[str] @classmethod def from_proto(cls, proto: services_pb2.ServiceDefaults) -> "ServiceDefault": @@ -220,15 +220,15 @@ class ServiceDefault: @dataclass class NodeServiceData: - executables: List[str] = field(default_factory=list) - dependencies: List[str] = field(default_factory=list) - dirs: List[str] = field(default_factory=list) - configs: List[str] = field(default_factory=list) - startup: List[str] = field(default_factory=list) - validate: List[str] = field(default_factory=list) + executables: list[str] = field(default_factory=list) + dependencies: list[str] = field(default_factory=list) + dirs: list[str] = field(default_factory=list) + configs: list[str] = field(default_factory=list) + startup: list[str] = field(default_factory=list) + validate: list[str] = field(default_factory=list) validation_mode: ServiceValidationMode = ServiceValidationMode.NON_BLOCKING validation_timer: int = 5 - shutdown: List[str] = field(default_factory=list) + shutdown: list[str] = field(default_factory=list) meta: str = None @classmethod @@ -266,7 +266,7 @@ class NodeServiceConfig: node_id: int service: str data: NodeServiceData - files: Dict[str, str] = field(default_factory=dict) + files: dict[str, str] = field(default_factory=dict) @classmethod def from_proto(cls, proto: services_pb2.NodeServiceConfig) -> "NodeServiceConfig": @@ -282,11 +282,11 @@ class NodeServiceConfig: class ServiceConfig: node_id: int service: str - files: List[str] = None - directories: List[str] = None - startup: List[str] = None - validate: List[str] = None - shutdown: List[str] = None + files: list[str] = None + directories: list[str] = None + startup: list[str] = None + validate: list[str] = None + shutdown: list[str] = None def to_proto(self) -> services_pb2.ServiceConfig: return services_pb2.ServiceConfig( @@ -339,8 +339,8 @@ class InterfaceThroughput: @dataclass class ThroughputsEvent: session_id: int - bridge_throughputs: List[BridgeThroughput] - iface_throughputs: List[InterfaceThroughput] + bridge_throughputs: list[BridgeThroughput] + iface_throughputs: list[InterfaceThroughput] @classmethod def from_proto(cls, proto: core_pb2.ThroughputsEvent) -> "ThroughputsEvent": @@ -428,19 +428,19 @@ class ConfigOption: label: str = None type: ConfigOptionType = None group: str = None - select: List[str] = None + select: list[str] = None @classmethod def from_dict( - cls, config: Dict[str, common_pb2.ConfigOption] - ) -> Dict[str, "ConfigOption"]: + cls, config: dict[str, common_pb2.ConfigOption] + ) -> dict[str, "ConfigOption"]: d = {} for key, value in config.items(): d[key] = ConfigOption.from_proto(value) return d @classmethod - def to_dict(cls, config: Dict[str, "ConfigOption"]) -> Dict[str, str]: + def to_dict(cls, config: dict[str, "ConfigOption"]) -> dict[str, str]: return {k: v.value for k, v in config.items()} @classmethod @@ -671,7 +671,7 @@ class EmaneModelConfig: node_id: int model: str iface_id: int = -1 - config: Dict[str, ConfigOption] = None + config: dict[str, ConfigOption] = None @classmethod def from_proto(cls, proto: emane_pb2.GetEmaneModelConfig) -> "EmaneModelConfig": @@ -725,8 +725,8 @@ class Node: type: NodeType = NodeType.DEFAULT model: str = None position: Position = Position(x=0, y=0) - services: Set[str] = field(default_factory=set) - config_services: Set[str] = field(default_factory=set) + services: set[str] = field(default_factory=set) + config_services: set[str] = field(default_factory=set) emane: str = None icon: str = None image: str = None @@ -737,19 +737,19 @@ class Node: canvas: int = None # configurations - emane_model_configs: Dict[ - Tuple[str, Optional[int]], Dict[str, ConfigOption] + emane_model_configs: dict[ + tuple[str, Optional[int]], dict[str, ConfigOption] ] = field(default_factory=dict, repr=False) - wlan_config: Dict[str, ConfigOption] = field(default_factory=dict, repr=False) - wireless_config: Dict[str, ConfigOption] = field(default_factory=dict, repr=False) - mobility_config: Dict[str, ConfigOption] = field(default_factory=dict, repr=False) - service_configs: Dict[str, NodeServiceData] = field( + wlan_config: dict[str, ConfigOption] = field(default_factory=dict, repr=False) + wireless_config: dict[str, ConfigOption] = field(default_factory=dict, repr=False) + mobility_config: dict[str, ConfigOption] = field(default_factory=dict, repr=False) + service_configs: dict[str, NodeServiceData] = field( default_factory=dict, repr=False ) - service_file_configs: Dict[str, Dict[str, str]] = field( + service_file_configs: dict[str, dict[str, str]] = field( default_factory=dict, repr=False ) - config_service_configs: Dict[str, ConfigServiceData] = field( + config_service_configs: dict[str, ConfigServiceData] = field( default_factory=dict, repr=False ) @@ -849,18 +849,18 @@ class Node: wireless_config={k: v.to_proto() for k, v in self.wireless_config.items()}, ) - def set_wlan(self, config: Dict[str, str]) -> None: + def set_wlan(self, config: dict[str, str]) -> None: for key, value in config.items(): option = ConfigOption(name=key, value=value) self.wlan_config[key] = option - def set_mobility(self, config: Dict[str, str]) -> None: + def set_mobility(self, config: dict[str, str]) -> None: for key, value in config.items(): option = ConfigOption(name=key, value=value) self.mobility_config[key] = option def set_emane_model( - self, model: str, config: Dict[str, str], iface_id: int = None + self, model: str, config: dict[str, str], iface_id: int = None ) -> None: key = (model, iface_id) config_options = self.emane_model_configs.setdefault(key, {}) @@ -873,23 +873,23 @@ class Node: class Session: id: int = None state: SessionState = SessionState.DEFINITION - nodes: Dict[int, Node] = field(default_factory=dict) - links: List[Link] = field(default_factory=list) + nodes: dict[int, Node] = field(default_factory=dict) + links: list[Link] = field(default_factory=list) dir: str = None user: str = None - default_services: Dict[str, Set[str]] = field(default_factory=dict) + default_services: dict[str, set[str]] = field(default_factory=dict) location: SessionLocation = SessionLocation( x=0.0, y=0.0, z=0.0, lat=47.57917, lon=-122.13232, alt=2.0, scale=150.0 ) - hooks: Dict[str, Hook] = field(default_factory=dict) - metadata: Dict[str, str] = field(default_factory=dict) + hooks: dict[str, Hook] = field(default_factory=dict) + metadata: dict[str, str] = field(default_factory=dict) file: Path = None - options: Dict[str, ConfigOption] = field(default_factory=dict) - servers: List[Server] = field(default_factory=list) + options: dict[str, ConfigOption] = field(default_factory=dict) + servers: list[Server] = field(default_factory=list) @classmethod def from_proto(cls, proto: core_pb2.Session) -> "Session": - nodes: Dict[int, Node] = {x.id: Node.from_proto(x) for x in proto.nodes} + nodes: dict[int, Node] = {x.id: Node.from_proto(x) for x in proto.nodes} links = [Link.from_proto(x) for x in proto.links] default_services = {x.model: set(x.services) for x in proto.default_services} hooks = {x.file: Hook.from_proto(x) for x in proto.hooks} @@ -987,7 +987,7 @@ class Session: self.links.append(link) return link - def set_options(self, config: Dict[str, str]) -> None: + def set_options(self, config: dict[str, str]) -> None: for key, value in config.items(): option = ConfigOption(name=key, value=value) self.options[key] = option @@ -995,9 +995,9 @@ class Session: @dataclass class CoreConfig: - services: List[Service] = field(default_factory=list) - config_services: List[ConfigService] = field(default_factory=list) - emane_models: List[str] = field(default_factory=list) + services: list[Service] = field(default_factory=list) + config_services: list[ConfigService] = field(default_factory=list) + emane_models: list[str] = field(default_factory=list) @classmethod def from_proto(cls, proto: core_pb2.GetConfigResponse) -> "CoreConfig": @@ -1088,7 +1088,7 @@ class ConfigEvent: node_id: int object: str type: int - data_types: List[int] + data_types: list[int] data_values: str captions: str bitmap: str