Source code for flask_unchained.hooks.run_hooks_hook

import networkx as nx

from collections import namedtuple
from importlib import import_module
from typing import *

from ..app_factory_hook import AppFactoryHook
from ..bundles import Bundle
from ..flask_unchained import FlaskUnchained


HookTuple = namedtuple('HookTuple', ('HookClass', 'bundle'))


[docs]class RunHooksHook(AppFactoryHook): """ An internal hook to discover and run all the other hooks. """ bundle_module_names = ['hooks'] """ The default module this hook loads from. Override by setting the ``hooks_module_names`` attribute on your bundle class. """
[docs] def run_hook(self, app: FlaskUnchained, bundles: List[Bundle], unchained_config: Optional[Dict[str, Any]] = None, ) -> None: """ Collect hooks from Flask Unchained and the list of bundles, resolve their correct order, and run them in that order to build (boot) the app instance. """ hook_tuples = self.collect_from_bundles( bundles, _initial_objects=self.collect_unchained_hooks()) for HookClass, bundle in self.resolve_hook_order(hook_tuples): hook = HookClass(self.unchained, bundle) hook.run_hook(app, bundles, unchained_config) hook.update_shell_context(self.unchained._shell_ctx)
[docs] def collect_from_bundle(self, bundle: Bundle) -> Dict[str, HookTuple]: """ Collect hooks from a bundle hierarchy. """ return {hook_class_name: HookTuple(HookClass, bundle) for hook_class_name, HookClass in super().collect_from_bundle(bundle).items()}
[docs] def collect_unchained_hooks(self) -> Dict[str, HookTuple]: """ Collect hooks built into Flask Unchained that should always run. """ unchained_hooks_pkg = import_module('flask_unchained.hooks') return {hook_class_name: HookTuple(HookClass, bundle=None) for hook_class_name, HookClass in self._collect_from_package(unchained_hooks_pkg).items()}
[docs] def type_check(self, obj: Any) -> bool: """ Returns True if ``obj`` is a subclass of :class:`~flask_unchained.AppFactoryHook`. """ is_hook_cls = isinstance(obj, type) and issubclass(obj, AppFactoryHook) return is_hook_cls and obj not in {AppFactoryHook, RunHooksHook}
def resolve_hook_order(self, hook_tuples: Dict[str, HookTuple]) -> List[HookTuple]: dag = nx.DiGraph() for hook_tuple in hook_tuples.values(): HookClass, _ = hook_tuple dag.add_node(HookClass.name, hook_tuple=hook_tuple) for dep_name in HookClass.run_after: dag.add_edge(HookClass.name, dep_name) for successor_name in HookClass.run_before: dag.add_edge(successor_name, HookClass.name) try: order = reversed(list(nx.topological_sort(dag))) except nx.NetworkXUnfeasible: msg = 'Circular dependency detected between hooks' problem_graph = ', '.join(f'{a} -> {b}' for a, b in nx.find_cycle(dag)) raise Exception(f'{msg}: {problem_graph}') rv = [] for hook_name in order: hook_tuple = dag.nodes[hook_name].get('hook_tuple') if hook_tuple: rv.append(hook_tuple) return rv