From 76ea80b1784e209a2490fbdac25f215036c961ec Mon Sep 17 00:00:00 2001 From: norohind <60548839+norohind@users.noreply.github.com> Date: Sun, 10 Apr 2022 23:08:31 +0300 Subject: [PATCH] Introduce hooks support --- FAPI/__init__.py | 5 ++++ Hook.py | 11 +++++++++ HookSystem.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++ hooks/testHook.py | 11 +++++++++ 4 files changed, 85 insertions(+) create mode 100644 Hook.py create mode 100644 HookSystem.py create mode 100644 hooks/testHook.py diff --git a/FAPI/__init__.py b/FAPI/__init__.py index a32b9b2..734db8f 100644 --- a/FAPI/__init__.py +++ b/FAPI/__init__.py @@ -1,5 +1,8 @@ from . import Queries import DB +import HookSystem + +hook_system = HookSystem.HookSystem() def update_squad(squad_id: int, suppress_absence=False) -> None | int: @@ -15,10 +18,12 @@ def update_squad(squad_id: int, suppress_absence=False) -> None | int: # Squad not found FDEV if not suppress_absence: operation_id = DB.delete_squadron(squad_id) + hook_system.notify_deleted(operation_id) else: # Then we got valid squad_info dict news_info = Queries.get_squad_news(squad_id) operation_id = DB.insert_info_news(news_info, squad_info) + hook_system.notify_inserted(operation_id) return operation_id diff --git a/Hook.py b/Hook.py new file mode 100644 index 0000000..0ac0a79 --- /dev/null +++ b/Hook.py @@ -0,0 +1,11 @@ +from abc import ABC, abstractmethod + + +class Hook(ABC): # See at Hook class as to observer in observer pattern + """ + In order to implement hook, subclass this class and pass instance to HookSystem.add_hook + """ + + @abstractmethod + def update(self, operation_id: int) -> None: + raise NotImplemented diff --git a/HookSystem.py b/HookSystem.py new file mode 100644 index 0000000..a3a1e2f --- /dev/null +++ b/HookSystem.py @@ -0,0 +1,58 @@ +import os +import functools +import threading +from Hook import Hook +import importlib.machinery + + +def check_int(func: callable) -> callable: + @functools.wraps(func) + def decorated(self, operation_id: int | None) -> None: + if type(operation_id) == int: + return func(self, operation_id) + + return decorated + + +class HookSystem: + hooks_inserted: list[Hook] = list() + hooks_deleted: list[Hook] = list() + + def __init__(self): + # hooks load + for file_name in sorted(os.listdir('hooks')): + if file_name.endswith('.py') and not file_name[0] in ['.', '_']: + path = os.path.join('hooks', file_name) + hook_name = file_name[:-3] + module = importlib.machinery.SourceFileLoader(hook_name, path).load_module() + setup_func = getattr(module, 'setup', None) + if setup_func is not None: + setup_func(self) + + else: + raise AttributeError(f'No setup method in {file_name} hook') + + def add_on_insert_hook(self, hook: Hook) -> None: + self.hooks_inserted.append(hook) + + def remove_on_insert_hook(self, hook: Hook) -> None: + self.hooks_inserted.remove(hook) + + def add_on_delete_hook(self, hook: Hook) -> None: + self.hooks_deleted.append(hook) + + def remove_on_delete_hook(self, hook: Hook) -> None: + self.hooks_deleted.remove(hook) + + @check_int + def notify_inserted(self, operation_id: int | None) -> None: + self._notify(operation_id, self.hooks_inserted) + + @check_int + def notify_deleted(self, operation_id: int) -> None: + self._notify(operation_id, self.hooks_deleted) + + @staticmethod + def _notify(operation_id, hooks: list[Hook]) -> None: + for hook in hooks: + threading.Thread(target=hook.update, args=(operation_id,)).start() diff --git a/hooks/testHook.py b/hooks/testHook.py new file mode 100644 index 0000000..3f5b872 --- /dev/null +++ b/hooks/testHook.py @@ -0,0 +1,11 @@ +from HookSystem import HookSystem +from Hook import Hook + + +class testHook(Hook): + def update(self, operation_id: int) -> None: + print('update') + + +def setup(hook_system: HookSystem) -> None: + hook_system.add_on_insert_hook(testHook())