Source code for sceptre.hooks

import abc
import logging
from functools import wraps
from typing import TYPE_CHECKING, List

from sceptre.helpers import _call_func_on_values
from sceptre.resolvers import CustomYamlTagBase

if TYPE_CHECKING:
    from sceptre.stack import Stack


[docs]class Hook(CustomYamlTagBase, metaclass=abc.ABCMeta): """ Hook is an abstract base class that should be subclassed by all hooks. """ logger = logging.getLogger(__name__)
[docs] @abc.abstractmethod def run(self): """ run is an abstract method which must be overwritten by all inheriting classes. Run should execute the logic of the hook. """ pass # pragma: no cover
[docs]class HookProperty(object): """ This is a descriptor class used to store an attribute that may contain Hook objects. Used to setup Hooks when added as a attribute. Supports nested dictionary and lists. :param name: Attribute suffix used to store the property in the instance. :type name: str """ def __init__(self, name): self.name = "_" + name self.logger = logging.getLogger(__name__) def __get__(self, instance, type): """ Attribute getter for Hook containing data structure. :return: The attribute stored with the suffix ``name`` in the instance. :rtype: dict or list """ return getattr(instance, self.name) def __set__(self, instance: "Stack", value): """ Attribute setter which adds a stack reference to any hooks in the data structure `value` and calls the setup method. """ def setup(attr, key, value: Hook): attr[key] = clone = value.clone_for_stack(instance) clone.setup() _call_func_on_values(setup, value, Hook) setattr(instance, self.name, value)
[docs]def execute_hooks(hooks): """ Searches through dictionary or list for Resolver objects and replaces them with the resolved value. Supports nested dictionaries and lists. Does not detect Resolver objects used as keys in dictionaries. :param attr: A complex data structure to search through. :type attr: dict or list :return: A complex data structure without Resolver objects. :rtype: dict or list """ if isinstance(hooks, list): for hook in hooks: if isinstance(hook, Hook): hook.run()
[docs]def add_stack_hooks(func): """ A function decorator to trigger the before and after hooks, relative to the decorated function's name. :param func: a function that operates on a stack :type func: function """ @wraps(func) def decorated(self, *args, **kwargs): execute_hooks(self.stack.hooks.get("before_" + func.__name__)) response = func(self, *args, **kwargs) execute_hooks(self.stack.hooks.get("after_" + func.__name__)) return response return decorated
[docs]def add_stack_hooks_with_aliases(function_aliases: List[str]): """ Returns a decorator to trigger the before and after hooks, relative to the decorated function's name AS WELL AS the passed function alias names. :param function_aliases: The list of OTHER functions to trigger hooks around. :return: The hook-triggering decorator. """ def decorator(func): all_hook_names = [func.__name__] + function_aliases @wraps(func) def decorated(self, *args, **kwargs): for hook in all_hook_names: before_hook_name = f"before_{hook}" execute_hooks(self.stack.hooks.get(before_hook_name)) response = func(self, *args, **kwargs) for hook in all_hook_names: after_hook_name = f"after_{hook}" execute_hooks(self.stack.hooks.get(after_hook_name)) return response return decorated return decorator