updates for typing functions on top level modules

This commit is contained in:
Blake Harnden 2020-01-13 10:06:18 -08:00
parent ab3659183e
commit 4e71759ac9
5 changed files with 209 additions and 133 deletions

View file

@ -134,7 +134,7 @@ def create_nodes(
def create_links( def create_links(
session: Session, link_protos: List[core_pb2.Link] session: Session, link_protos: List[core_pb2.Link]
) -> Tuple[List[NodeBase], List[Type[Exception]]]: ) -> Tuple[List[NodeBase], List[Exception]]:
""" """
Create links using a thread pool and wait for completion. Create links using a thread pool and wait for completion.

View file

@ -4,8 +4,111 @@ Common support for configurable CORE objects.
import logging import logging
from collections import OrderedDict from collections import OrderedDict
from typing import TYPE_CHECKING, Dict, List, Tuple, Type, Union
from core.emane.nodes import EmaneNet
from core.emulator.data import ConfigData from core.emulator.data import ConfigData
from core.emulator.enumerations import ConfigDataTypes
from core.nodes.network import WlanNode
if TYPE_CHECKING:
from core.location.mobility import WirelessModel
class ConfigGroup:
"""
Defines configuration group tabs used for display by ConfigurationOptions.
"""
def __init__(self, name: str, start: int, stop: int) -> None:
"""
Creates a ConfigGroup object.
:param str name: configuration group display name
:param int start: configurations start index for this group
:param int stop: configurations stop index for this group
"""
self.name = name
self.start = start
self.stop = stop
class Configuration:
"""
Represents a configuration options.
"""
def __init__(
self,
_id: str,
_type: ConfigDataTypes,
label: str = None,
default: str = "",
options: List[str] = None,
) -> None:
"""
Creates a Configuration object.
:param str _id: unique name for configuration
:param core.enumerations.ConfigDataTypes _type: configuration data type
:param str label: configuration label for display
:param str default: default value for configuration
:param list options: list options if this is a configuration with a combobox
"""
self.id = _id
self.type = _type
self.default = default
if not options:
options = []
self.options = options
if not label:
label = _id
self.label = label
def __str__(self):
return f"{self.__class__.__name__}(id={self.id}, type={self.type}, default={self.default}, options={self.options})"
class ConfigurableOptions:
"""
Provides a base for defining configuration options within CORE.
"""
name = None
bitmap = None
options = []
@classmethod
def configurations(cls) -> List[Configuration]:
"""
Provides the configurations for this class.
:return: configurations
:rtype: list[Configuration]
"""
return cls.options
@classmethod
def config_groups(cls) -> List[ConfigGroup]:
"""
Defines how configurations are grouped.
:return: configuration group definition
:rtype: list[ConfigGroup]
"""
return [ConfigGroup("Options", 1, len(cls.configurations()))]
@classmethod
def default_values(cls) -> Dict[str, str]:
"""
Provides an ordered mapping of configuration keys to default values.
:return: ordered configuration mapping default values
:rtype: OrderedDict
"""
return OrderedDict(
[(config.id, config.default) for config in cls.configurations()]
)
class ConfigShim: class ConfigShim:
@ -14,7 +117,7 @@ class ConfigShim:
""" """
@classmethod @classmethod
def str_to_dict(cls, key_values): def str_to_dict(cls, key_values: str) -> Dict[str, str]:
""" """
Converts a TLV key/value string into an ordered mapping. Converts a TLV key/value string into an ordered mapping.
@ -30,7 +133,7 @@ class ConfigShim:
return values return values
@classmethod @classmethod
def groups_to_str(cls, config_groups): def groups_to_str(cls, config_groups: List[ConfigGroup]) -> str:
""" """
Converts configuration groups to a TLV formatted string. Converts configuration groups to a TLV formatted string.
@ -47,7 +150,14 @@ class ConfigShim:
return "|".join(group_strings) return "|".join(group_strings)
@classmethod @classmethod
def config_data(cls, flags, node_id, type_flags, configurable_options, config): def config_data(
cls,
flags: int,
node_id: int,
type_flags: int,
configurable_options: ConfigurableOptions,
config: Dict[str, str],
) -> ConfigData:
""" """
Convert this class to a Config API message. Some TLVs are defined Convert this class to a Config API message. Some TLVs are defined
by the class, but node number, conf type flags, and values must by the class, but node number, conf type flags, and values must
@ -102,50 +212,22 @@ class ConfigShim:
) )
class Configuration:
"""
Represents a configuration options.
"""
def __init__(self, _id, _type, label=None, default="", options=None):
"""
Creates a Configuration object.
:param str _id: unique name for configuration
:param core.enumerations.ConfigDataTypes _type: configuration data type
:param str label: configuration label for display
:param str default: default value for configuration
:param list options: list options if this is a configuration with a combobox
"""
self.id = _id
self.type = _type
self.default = default
if not options:
options = []
self.options = options
if not label:
label = _id
self.label = label
def __str__(self):
return f"{self.__class__.__name__}(id={self.id}, type={self.type}, default={self.default}, options={self.options})"
class ConfigurableManager: class ConfigurableManager:
""" """
Provides convenience methods for storing and retrieving configuration options for nodes. Provides convenience methods for storing and retrieving configuration options for
nodes.
""" """
_default_node = -1 _default_node = -1
_default_type = _default_node _default_type = _default_node
def __init__(self): def __init__(self) -> None:
""" """
Creates a ConfigurableManager object. Creates a ConfigurableManager object.
""" """
self.node_configurations = {} self.node_configurations = {}
def nodes(self): def nodes(self) -> List[int]:
""" """
Retrieves the ids of all node configurations known by this manager. Retrieves the ids of all node configurations known by this manager.
@ -154,7 +236,7 @@ class ConfigurableManager:
""" """
return [x for x in self.node_configurations if x != self._default_node] return [x for x in self.node_configurations if x != self._default_node]
def config_reset(self, node_id=None): def config_reset(self, node_id: int = None) -> None:
""" """
Clears all configurations or configuration for a specific node. Clears all configurations or configuration for a specific node.
@ -166,7 +248,13 @@ class ConfigurableManager:
elif node_id in self.node_configurations: elif node_id in self.node_configurations:
self.node_configurations.pop(node_id) self.node_configurations.pop(node_id)
def set_config(self, _id, value, node_id=_default_node, config_type=_default_type): def set_config(
self,
_id: str,
value: str,
node_id: int = _default_node,
config_type: str = _default_type,
) -> None:
""" """
Set a specific configuration value for a node and configuration type. Set a specific configuration value for a node and configuration type.
@ -180,7 +268,12 @@ class ConfigurableManager:
node_type_configs = node_configs.setdefault(config_type, OrderedDict()) node_type_configs = node_configs.setdefault(config_type, OrderedDict())
node_type_configs[_id] = value node_type_configs[_id] = value
def set_configs(self, config, node_id=_default_node, config_type=_default_type): def set_configs(
self,
config: Dict[str, str],
node_id: int = _default_node,
config_type: str = _default_type,
) -> None:
""" """
Set configurations for a node and configuration type. Set configurations for a node and configuration type.
@ -196,8 +289,12 @@ class ConfigurableManager:
node_configs[config_type] = config node_configs[config_type] = config
def get_config( def get_config(
self, _id, node_id=_default_node, config_type=_default_type, default=None self,
): _id: str,
node_id: int = _default_node,
config_type: str = _default_type,
default: str = None,
) -> str:
""" """
Retrieves a specific configuration for a node and configuration type. Retrieves a specific configuration for a node and configuration type.
@ -214,7 +311,9 @@ class ConfigurableManager:
result = node_type_configs.get(_id, default) result = node_type_configs.get(_id, default)
return result return result
def get_configs(self, node_id=_default_node, config_type=_default_type): def get_configs(
self, node_id: int = _default_node, config_type: str = _default_type
) -> Dict[str, str]:
""" """
Retrieve configurations for a node and configuration type. Retrieve configurations for a node and configuration type.
@ -229,7 +328,7 @@ class ConfigurableManager:
result = node_configs.get(config_type) result = node_configs.get(config_type)
return result return result
def get_all_configs(self, node_id=_default_node): def get_all_configs(self, node_id: int = _default_node) -> List[Dict[str, str]]:
""" """
Retrieve all current configuration types for a node. Retrieve all current configuration types for a node.
@ -240,72 +339,12 @@ class ConfigurableManager:
return self.node_configurations.get(node_id) return self.node_configurations.get(node_id)
class ConfigGroup:
"""
Defines configuration group tabs used for display by ConfigurationOptions.
"""
def __init__(self, name, start, stop):
"""
Creates a ConfigGroup object.
:param str name: configuration group display name
:param int start: configurations start index for this group
:param int stop: configurations stop index for this group
"""
self.name = name
self.start = start
self.stop = stop
class ConfigurableOptions:
"""
Provides a base for defining configuration options within CORE.
"""
name = None
bitmap = None
options = []
@classmethod
def configurations(cls):
"""
Provides the configurations for this class.
:return: configurations
:rtype: list[Configuration]
"""
return cls.options
@classmethod
def config_groups(cls):
"""
Defines how configurations are grouped.
:return: configuration group definition
:rtype: list[ConfigGroup]
"""
return [ConfigGroup("Options", 1, len(cls.configurations()))]
@classmethod
def default_values(cls):
"""
Provides an ordered mapping of configuration keys to default values.
:return: ordered configuration mapping default values
:rtype: OrderedDict
"""
return OrderedDict(
[(config.id, config.default) for config in cls.configurations()]
)
class ModelManager(ConfigurableManager): class ModelManager(ConfigurableManager):
""" """
Helps handle setting models for nodes and managing their model configurations. Helps handle setting models for nodes and managing their model configurations.
""" """
def __init__(self): def __init__(self) -> None:
""" """
Creates a ModelManager object. Creates a ModelManager object.
""" """
@ -313,7 +352,9 @@ class ModelManager(ConfigurableManager):
self.models = {} self.models = {}
self.node_models = {} self.node_models = {}
def set_model_config(self, node_id, model_name, config=None): def set_model_config(
self, node_id: int, model_name: str, config: Dict[str, str] = None
) -> None:
""" """
Set configuration data for a model. Set configuration data for a model.
@ -341,7 +382,7 @@ class ModelManager(ConfigurableManager):
# set configuration # set configuration
self.set_configs(model_config, node_id=node_id, config_type=model_name) self.set_configs(model_config, node_id=node_id, config_type=model_name)
def get_model_config(self, node_id, model_name): def get_model_config(self, node_id: int, model_name: str) -> Dict[str, str]:
""" """
Retrieve configuration data for a model. Retrieve configuration data for a model.
@ -363,7 +404,12 @@ class ModelManager(ConfigurableManager):
return config return config
def set_model(self, node, model_class, config=None): def set_model(
self,
node: Union[WlanNode, EmaneNet],
model_class: "WirelessModel",
config: Dict[str, str] = None,
) -> None:
""" """
Set model and model configuration for node. Set model and model configuration for node.
@ -379,7 +425,9 @@ class ModelManager(ConfigurableManager):
config = self.get_model_config(node.id, model_class.name) config = self.get_model_config(node.id, model_class.name)
node.setmodel(model_class, config) node.setmodel(model_class, config)
def get_models(self, node): def get_models(
self, node: Union[WlanNode, EmaneNet]
) -> List[Tuple[Type, Dict[str, str]]]:
""" """
Return a list of model classes and values for a net if one has been Return a list of model classes and values for a net if one has been
configured. This is invoked when exporting a session to XML. configured. This is invoked when exporting a session to XML.

View file

@ -1611,14 +1611,14 @@ class Session:
self.add_remove_control_interface(node=node, remove=False) self.add_remove_control_interface(node=node, remove=False)
self.services.boot_services(node) self.services.boot_services(node)
def boot_nodes(self) -> List[ServiceBootError]: def boot_nodes(self) -> List[Exception]:
""" """
Invoke the boot() procedure for all nodes and send back node Invoke the boot() procedure for all nodes and send back node
messages to the GUI for node messages that had the status messages to the GUI for node messages that had the status
request flag. request flag.
:return: service boot exceptions :return: service boot exceptions
:rtype: list[core.services.coreservices.ServiceBootError] :rtype: list[Exception]
""" """
with self._nodes_lock: with self._nodes_lock:
funcs = [] funcs = []

View file

@ -9,7 +9,7 @@ class CoreCommandError(subprocess.CalledProcessError):
Used when encountering internal CORE command errors. Used when encountering internal CORE command errors.
""" """
def __str__(self): def __str__(self) -> str:
return ( return (
f"Command({self.cmd}), Status({self.returncode}):\n" f"Command({self.cmd}), Status({self.returncode}):\n"
f"stdout: {self.output}\nstderr: {self.stderr}" f"stdout: {self.output}\nstderr: {self.stderr}"

View file

@ -15,15 +15,33 @@ import random
import shlex import shlex
import sys import sys
from subprocess import PIPE, STDOUT, Popen from subprocess import PIPE, STDOUT, Popen
from typing import (
TYPE_CHECKING,
Any,
Callable,
Dict,
Iterable,
List,
Optional,
Tuple,
Type,
Union,
)
import netaddr import netaddr
from core.errors import CoreCommandError, CoreError from core.errors import CoreCommandError, CoreError
if TYPE_CHECKING:
from core.emulator.session import Session
from core.nodes.base import CoreNode
DEVNULL = open(os.devnull, "wb") DEVNULL = open(os.devnull, "wb")
def execute_file(path, exec_globals=None, exec_locals=None): def execute_file(
path: str, exec_globals: Dict[str, str] = None, exec_locals: Dict[str, str] = None
) -> None:
""" """
Provides an alternative way to run execfile to be compatible for Provides an alternative way to run execfile to be compatible for
both python2/3. both python2/3.
@ -41,7 +59,7 @@ def execute_file(path, exec_globals=None, exec_locals=None):
exec(data, exec_globals, exec_locals) exec(data, exec_globals, exec_locals)
def hashkey(value): def hashkey(value: Union[str, int]) -> int:
""" """
Provide a consistent hash that can be used in place Provide a consistent hash that can be used in place
of the builtin hash, that no longer behaves consistently of the builtin hash, that no longer behaves consistently
@ -57,7 +75,7 @@ def hashkey(value):
return int(hashlib.sha256(value).hexdigest(), 16) return int(hashlib.sha256(value).hexdigest(), 16)
def _detach_init(): def _detach_init() -> None:
""" """
Fork a child process and exit. Fork a child process and exit.
@ -69,7 +87,7 @@ def _detach_init():
os.setsid() os.setsid()
def _valid_module(path, file_name): def _valid_module(path: str, file_name: str) -> bool:
""" """
Check if file is a valid python module. Check if file is a valid python module.
@ -91,7 +109,7 @@ def _valid_module(path, file_name):
return True return True
def _is_class(module, member, clazz): def _is_class(module: Any, member: Type, clazz: Type) -> bool:
""" """
Validates if a module member is a class and an instance of a CoreService. Validates if a module member is a class and an instance of a CoreService.
@ -113,7 +131,7 @@ def _is_class(module, member, clazz):
return True return True
def close_onexec(fd): def close_onexec(fd: int) -> None:
""" """
Close on execution of a shell process. Close on execution of a shell process.
@ -124,7 +142,7 @@ def close_onexec(fd):
fcntl.fcntl(fd, fcntl.F_SETFD, fdflags | fcntl.FD_CLOEXEC) fcntl.fcntl(fd, fcntl.F_SETFD, fdflags | fcntl.FD_CLOEXEC)
def which(command, required): def which(command: str, required: bool) -> str:
""" """
Find location of desired executable within current PATH. Find location of desired executable within current PATH.
@ -146,7 +164,7 @@ def which(command, required):
return found_path return found_path
def make_tuple(obj): def make_tuple(obj: Any) -> Tuple[Any]:
""" """
Create a tuple from an object, or return the object itself. Create a tuple from an object, or return the object itself.
@ -160,7 +178,7 @@ def make_tuple(obj):
return (obj,) return (obj,)
def make_tuple_fromstr(s, value_type): def make_tuple_fromstr(s: str, value_type: Callable) -> Tuple[Any]:
""" """
Create a tuple from a string. Create a tuple from a string.
@ -179,7 +197,7 @@ def make_tuple_fromstr(s, value_type):
return tuple(value_type(i) for i in values) return tuple(value_type(i) for i in values)
def mute_detach(args, **kwargs): def mute_detach(args: List[str], **kwargs: Dict[str, Any]) -> int:
""" """
Run a muted detached process by forking it. Run a muted detached process by forking it.
@ -195,7 +213,13 @@ def mute_detach(args, **kwargs):
return Popen(args, **kwargs).pid return Popen(args, **kwargs).pid
def cmd(args, env=None, cwd=None, wait=True, shell=False): def cmd(
args: str,
env: Dict[str, str] = None,
cwd: str = None,
wait: bool = True,
shell: bool = False,
) -> str:
""" """
Execute a command on the host and return a tuple containing the exit status and Execute a command on the host and return a tuple containing the exit status and
result string. stderr output is folded into the stdout result string. result string. stderr output is folded into the stdout result string.
@ -227,7 +251,7 @@ def cmd(args, env=None, cwd=None, wait=True, shell=False):
raise CoreCommandError(-1, args) raise CoreCommandError(-1, args)
def file_munge(pathname, header, text): def file_munge(pathname: str, header: str, text: str) -> None:
""" """
Insert text at the end of a file, surrounded by header comments. Insert text at the end of a file, surrounded by header comments.
@ -245,7 +269,7 @@ def file_munge(pathname, header, text):
append_file.write(f"# END {header}\n") append_file.write(f"# END {header}\n")
def file_demunge(pathname, header): def file_demunge(pathname: str, header: str) -> None:
""" """
Remove text that was inserted in a file surrounded by header comments. Remove text that was inserted in a file surrounded by header comments.
@ -273,7 +297,9 @@ def file_demunge(pathname, header):
write_file.write("".join(lines)) write_file.write("".join(lines))
def expand_corepath(pathname, session=None, node=None): def expand_corepath(
pathname: str, session: "Session" = None, node: "CoreNode" = None
) -> str:
""" """
Expand a file path given session information. Expand a file path given session information.
@ -296,7 +322,7 @@ def expand_corepath(pathname, session=None, node=None):
return pathname return pathname
def sysctl_devname(devname): def sysctl_devname(devname: str) -> Optional[str]:
""" """
Translate a device name to the name used with sysctl. Translate a device name to the name used with sysctl.
@ -309,7 +335,7 @@ def sysctl_devname(devname):
return devname.replace(".", "/") return devname.replace(".", "/")
def load_config(filename, d): def load_config(filename: str, d: Dict[str, str]) -> None:
""" """
Read key=value pairs from a file, into a dict. Skip comments; strip newline Read key=value pairs from a file, into a dict. Skip comments; strip newline
characters and spacing. characters and spacing.
@ -332,7 +358,7 @@ def load_config(filename, d):
logging.exception("error reading file to dict: %s", filename) logging.exception("error reading file to dict: %s", filename)
def load_classes(path, clazz): def load_classes(path: str, clazz: Type) -> List[Type]:
""" """
Dynamically load classes for use within CORE. Dynamically load classes for use within CORE.
@ -375,7 +401,7 @@ def load_classes(path, clazz):
return classes return classes
def load_logging_config(config_path): def load_logging_config(config_path: str) -> None:
""" """
Load CORE logging configuration file. Load CORE logging configuration file.
@ -387,7 +413,9 @@ def load_logging_config(config_path):
logging.config.dictConfig(log_config) logging.config.dictConfig(log_config)
def threadpool(funcs, workers=10): def threadpool(
funcs: List[Tuple[Callable, Iterable[Any], Dict[Any, Any]]], workers: int = 10
) -> Tuple[List[Any], List[Exception]]:
""" """
Run provided functions, arguments, and keywords within a threadpool Run provided functions, arguments, and keywords within a threadpool
collecting results and exceptions. collecting results and exceptions.
@ -413,7 +441,7 @@ def threadpool(funcs, workers=10):
return results, exceptions return results, exceptions
def random_mac(): def random_mac() -> str:
""" """
Create a random mac address using Xen OID 00:16:3E. Create a random mac address using Xen OID 00:16:3E.
@ -427,7 +455,7 @@ def random_mac():
return str(mac) return str(mac)
def validate_mac(value): def validate_mac(value: str) -> str:
""" """
Validate mac and return unix formatted version. Validate mac and return unix formatted version.
@ -443,7 +471,7 @@ def validate_mac(value):
raise CoreError(f"invalid mac address {value}: {e}") raise CoreError(f"invalid mac address {value}: {e}")
def validate_ip(value): def validate_ip(value: str) -> str:
""" """
Validate ip address with prefix and return formatted version. Validate ip address with prefix and return formatted version.