Using better action ids
This commit is contained in:
parent
2487078c8e
commit
33af22443e
|
@ -5,10 +5,13 @@ import click
|
||||||
from jeeves.cli.echo import info, error, title, success
|
from jeeves.cli.echo import info, error, title, success
|
||||||
from jeeves.core.parsers import FlowParser
|
from jeeves.core.parsers import FlowParser
|
||||||
from jeeves.core.executor import Executor
|
from jeeves.core.executor import Executor
|
||||||
|
from jeeves.core.registry import ActionRegistry
|
||||||
|
|
||||||
|
|
||||||
@click.group()
|
@click.group()
|
||||||
def main():
|
def main():
|
||||||
|
# TODO: Check if Jeevesfile in cwd, execute directly
|
||||||
|
ActionRegistry.autodiscover()
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
PROVIDED_ACTIONS = [
|
from jeeves.core.actions.shell import ScriptAction
|
||||||
"jeeves.core.actions.shell:ScriptAction",
|
from jeeves.core.actions.docker import DockerBuildAction, DockerRunAction
|
||||||
"jeeves.core.actions.docker:DockerAction",
|
|
||||||
|
__all__ = [
|
||||||
|
# Shell
|
||||||
|
ScriptAction,
|
||||||
|
# Docker
|
||||||
|
DockerBuildAction,
|
||||||
|
DockerRunAction,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
PROVIDED_ACTIONS = {action.id: action for action in __all__}
|
||||||
|
|
|
@ -6,7 +6,7 @@ from typing import Text
|
||||||
import pydantic
|
import pydantic
|
||||||
|
|
||||||
from jeeves.core.objects import Result
|
from jeeves.core.objects import Result
|
||||||
from .base import Action
|
from jeeves.core.actions.base import Action
|
||||||
|
|
||||||
|
|
||||||
class ScriptAction(Action):
|
class ScriptAction(Action):
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from jeeves.core.objects import Flow, Result, Execution, ExecutionStep
|
from jeeves.core.objects import Flow, Result, Execution, ExecutionStep
|
||||||
|
from jeeves.core.registry import ActionRegistry
|
||||||
|
|
||||||
|
|
||||||
class Executor:
|
class Executor:
|
||||||
|
@ -20,7 +21,8 @@ class Executor:
|
||||||
|
|
||||||
def execute_step(self, step: ExecutionStep):
|
def execute_step(self, step: ExecutionStep):
|
||||||
try:
|
try:
|
||||||
step.result = step.task.action.execute(workspace=self._execution.workspace)
|
action = ActionRegistry.get_action_cls(step.task.type)(parameters=step.task.parameters)
|
||||||
|
step.result = action.execute(workspace=self._execution.workspace)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
# Catch unhandled exceptions, mark the result as unsuccessful
|
# Catch unhandled exceptions, mark the result as unsuccessful
|
||||||
# and append the error as output.
|
# and append the error as output.
|
||||||
|
|
|
@ -6,8 +6,6 @@ from dataclasses import field
|
||||||
|
|
||||||
import pydantic
|
import pydantic
|
||||||
|
|
||||||
from jeeves.core.registry import ActionRegistry
|
|
||||||
|
|
||||||
|
|
||||||
class BaseObject(pydantic.BaseModel):
|
class BaseObject(pydantic.BaseModel):
|
||||||
pass
|
pass
|
||||||
|
@ -23,12 +21,12 @@ class Task(BaseObject):
|
||||||
type: Text
|
type: Text
|
||||||
parameters: Optional[Dict[Any, Any]] = None
|
parameters: Optional[Dict[Any, Any]] = None
|
||||||
|
|
||||||
@property
|
|
||||||
def action(self):
|
class Argument(BaseObject):
|
||||||
"""
|
name: Text
|
||||||
Returns the instanced :any:`jeeves.core.actions.base.Action` defined in ``type`` for this ``Task``.
|
default: Any
|
||||||
"""
|
type: Text = "text"
|
||||||
return ActionRegistry.get_action_cls(self.type)(parameters=self.parameters)
|
required: bool = False
|
||||||
|
|
||||||
|
|
||||||
class Flow(BaseObject):
|
class Flow(BaseObject):
|
||||||
|
|
|
@ -27,47 +27,42 @@ class ActionRegistry(metaclass=Singleton):
|
||||||
"""Loads all provided actions."""
|
"""Loads all provided actions."""
|
||||||
# TODO: Third party plugins
|
# TODO: Third party plugins
|
||||||
registry = cls()
|
registry = cls()
|
||||||
for action_namespace in PROVIDED_ACTIONS:
|
for action_id, action_cls in PROVIDED_ACTIONS.items():
|
||||||
registry.register_action(cls.get_action_cls(action_namespace))
|
registry.register_action(action_id, action_cls)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register_action(cls, action_cls):
|
def register_action(cls, action_id, action_cls):
|
||||||
registry = cls()
|
registry = cls()
|
||||||
# namespace = action_cls.id
|
|
||||||
namespace = f"{action_cls.__module__}:{action_cls.__name__}"
|
|
||||||
|
|
||||||
if namespace in registry.actions:
|
if action_id in registry.actions:
|
||||||
raise cls.ActionNamespaceConflict(
|
raise cls.ActionIDConflict(
|
||||||
f"Namespace {namespace} is already registered"
|
f"Action ID '{action_id}' is already registered"
|
||||||
)
|
)
|
||||||
|
|
||||||
registry.actions[namespace] = action_cls
|
registry.actions[action_id] = action_cls
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_action_cls(cls, namespace) -> Type[Action]:
|
def get_action_cls(cls, action_id) -> Type[Action]:
|
||||||
"""Returns the class for the provided action namespace"""
|
"""Returns the class for the provided action ID"""
|
||||||
# Right now actions are being imported and returned dinamically because it's easier,
|
# Right now actions are being imported and returned dinamically because it's easier,
|
||||||
# but we will need a way of autodiscover all (or register them manually) and
|
# but we will need a way of autodiscover all (or register them manually) and
|
||||||
# referencing them on a list so the user knows which actions are available.
|
# referencing them on a list so the user knows which actions are available.
|
||||||
|
|
||||||
modulename, clsname = namespace.split(":")
|
|
||||||
try:
|
try:
|
||||||
module = __import__(f"{modulename}", fromlist=(clsname,), level=0)
|
return cls.actions[action_id]
|
||||||
action_cls = getattr(module, clsname)
|
except IndexError as error:
|
||||||
return action_cls
|
raise cls.ActionDoesNotExist(f"Error importing action {action_id}: {error}")
|
||||||
except ModuleNotFoundError as error:
|
|
||||||
raise cls.ActionDoesNotExist(f"Error importing action {namespace}: {error}")
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_action(
|
def get_action(
|
||||||
cls, namespace: Text, parameters: Optional[Dict[Any, Any]] = None
|
cls, action_id: Text, parameters: Optional[Dict[Any, Any]] = None
|
||||||
) -> Action:
|
) -> Action:
|
||||||
"""Returns the instanced action for the provided namespace"""
|
"""Returns the instanced action for the provided action_id"""
|
||||||
action_cls: Type[Action] = cls.get_action_cls(namespace)
|
action_cls: Type[Action] = cls.get_action_cls(action_id)
|
||||||
return action_cls(parameters=parameters or {})
|
return action_cls(parameters=parameters or {})
|
||||||
|
|
||||||
class ActionNamespaceConflict(Exception):
|
class ActionIDConflict(Exception):
|
||||||
"""Raised when an action is defined with an already registered namespace"""
|
"""Raised when an action is defined with an already registered action id"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
Reference in New Issue