Move actions to pydantic

This commit is contained in:
Marek Wolan
2023-09-04 10:20:06 +01:00
parent d55fd835c7
commit 33b4911cb1

View File

@@ -1,3 +1,4 @@
# flake8: noqa
"""Core of the PrimAITE Simulator.""" """Core of the PrimAITE Simulator."""
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Callable, Dict, List, Optional, Union from typing import Callable, Dict, List, Optional, Union
@@ -10,7 +11,7 @@ from primaite import getLogger
_LOGGER = getLogger(__name__) _LOGGER = getLogger(__name__)
class ActionPermissionValidator(ABC): class ActionPermissionValidator(BaseModel):
""" """
Base class for action validators. Base class for action validators.
@@ -33,7 +34,7 @@ class AllowAllValidator(ActionPermissionValidator):
return True return True
class Action: class Action(BaseModel):
""" """
This object stores data related to a single action. This object stores data related to a single action.
@@ -41,34 +42,22 @@ class Action:
the action can be performed or not. the action can be performed or not.
""" """
def __init__( func: Callable[[List[str], Dict], None]
self, func: Callable[[List[str], Dict], None], validator: ActionPermissionValidator = AllowAllValidator()
) -> None:
""" """
Save the functions that are for this action.
Here's a description for the intended use of both of these.
``func`` is a function that accepts a request and a context dict. Typically this would be a lambda function ``func`` is a function that accepts a request and a context dict. Typically this would be a lambda function
that invokes a class method of your SimComponent. For example if the component is a node and the action is for that invokes a class method of your SimComponent. For example if the component is a node and the action is for
turning it off, then the SimComponent should have a turn_off(self) method that does not need to accept any args. turning it off, then the SimComponent should have a turn_off(self) method that does not need to accept any args.
Then, this Action will be given something like ``func = lambda request, context: self.turn_off()``. Then, this Action will be given something like ``func = lambda request, context: self.turn_off()``.
``validator`` is an instance of a subclass of `ActionPermissionValidator`. This is essentially a callable that
accepts `request` and `context` and returns a boolean to represent whether the permission is granted to perform
the action.
:param func: Function that performs the request.
:type func: Callable[[List[str], Dict], None]
:param validator: Function that checks if the request is authenticated given the context. By default, if no
validator is provided, an 'allow all' validator is added which permits all requests.
:type validator: ActionPermissionValidator
""" """
self.func: Callable[[List[str], Dict], None] = func validator: ActionPermissionValidator = AllowAllValidator()
self.validator: ActionPermissionValidator = validator """
``validator`` is an instance of `ActionPermissionValidator`. This is essentially a callable that
accepts `request` and `context` and returns a boolean to represent whether the permission is granted to perform
the action. The default validator will allow
"""
class ActionManager: class ActionManager(BaseModel):
""" """
ActionManager is used by `SimComponent` instances to keep track of actions. ActionManager is used by `SimComponent` instances to keep track of actions.
@@ -76,9 +65,8 @@ class ActionManager:
class is responsible for providing a consistent API for processing actions as well as helpful error messages. class is responsible for providing a consistent API for processing actions as well as helpful error messages.
""" """
def __init__(self) -> None: actions: Dict[str, Action] = {}
"""Initialise ActionManager with an empty action lookup.""" """maps action verb to an action object."""
self.actions: Dict[str, Action] = {}
def process_request(self, request: List[str], context: Dict) -> None: def process_request(self, request: List[str], context: Dict) -> None:
"""Process an action request. """Process an action request.
@@ -125,6 +113,11 @@ class ActionManager:
self.actions[name] = action self.actions[name] = action
def list_actions(self) -> List[List[str]]:
actions = []
for act_name, act in self.actions.items():
pass # TODO:
class SimComponent(BaseModel): class SimComponent(BaseModel):
"""Extension of pydantic BaseModel with additional methods that must be defined by all classes in the simulator.""" """Extension of pydantic BaseModel with additional methods that must be defined by all classes in the simulator."""
@@ -178,6 +171,14 @@ class SimComponent(BaseModel):
} }
return state return state
def possible_actions(self) -> List[List[str]]:
"""Enumerate all actions that this component can accept.
:return: List of all action strings that can be passed to this component.
:rtype: List[Dict[str]]
"""
action_list = ActionManager # TODO: extract possible actions? how to do this neatly?
def apply_action(self, action: List[str], context: Dict = {}) -> None: def apply_action(self, action: List[str], context: Dict = {}) -> None:
""" """
Apply an action to a simulation component. Action data is passed in as a 'namespaced' list of strings. Apply an action to a simulation component. Action data is passed in as a 'namespaced' list of strings.
@@ -230,7 +231,3 @@ class SimComponent(BaseModel):
_LOGGER.warn(msg) _LOGGER.warn(msg)
raise RuntimeWarning(msg) raise RuntimeWarning(msg)
self._parent = new_parent self._parent = new_parent
@parent.deleter
def parent(self) -> None:
self._parent = None