Source code for gort.recipes.base
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# @Author: José Sánchez-Gallego (gallegoj@uw.edu)
# @Date: 2023-08-13
# @Filename: base.py
# @License: BSD 3-clause (http://www.opensource.org/licenses/BSD-3-Clause)
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Type
from gort.enums import Event
if TYPE_CHECKING:
from gort.gort import Gort
__all__ = ["recipes", "BaseRecipe"]
recipes: dict[str, Type[BaseRecipe]] = {}
class RegisterRecipe(type):
"""Metaclass to register recipes."""
def __new__(cls, name, bases, class_dict):
cls = type.__new__(cls, name, bases, class_dict)
if name == "BaseRecipe":
return cls
assert issubclass(cls, BaseRecipe)
if "name" not in class_dict:
raise ValueError(f"name attribute not defined in {name}.")
# This is a hacked version of abc.ABCMeta, but this way we
# don't need to chain two metaclasses.
if "recipe" not in class_dict:
raise ValueError(f"recipe method not defined in {name}.")
recipes[class_dict["name"]] = cls
return cls
[docs]
class BaseRecipe(object, metaclass=RegisterRecipe):
"""Base class for recipes."""
name: str | None = None
def __init__(self, gort: Gort):
self.gort = gort
[docs]
async def recipe(self, *args, **kwargs):
"""The recipe. Must be overridden by the subclass."""
return
async def __call__(self, *args, **kwargs):
"""Executes the recipe and sends event notifications."""
payload: dict[str, Any] = {
"recipe_name": self.name,
"args": list(args),
"kwargs": kwargs,
}
await self.gort.notify_event(Event.RECIPE_START, payload=payload)
try:
await self.recipe(*args, **kwargs)
except Exception as err:
error_payload = payload.copy()
error_payload["error"] = str(err)
await self.gort.notify_event(Event.RECIPE_FAILED, payload=error_payload)
raise
else:
await self.gort.notify_event(Event.RECIPE_END, payload=payload)