diff libervia/backend/tools/trigger.py @ 4182:4dc00e848961

core (tools/trigger): new `add_with_check` method: this method add a wrapper which will skip the trigger is the plugin is not available in the session (e.g. a component which doesn't use the plugin).
author Goffi <goffi@goffi.org>
date Sat, 09 Dec 2023 19:19:26 +0100
parents 4b842c1fb686
children 0d7bb4df2343
line wrap: on
line diff
--- a/libervia/backend/tools/trigger.py	Sat Dec 09 19:17:34 2023 +0100
+++ b/libervia/backend/tools/trigger.py	Sat Dec 09 19:19:26 2023 +0100
@@ -19,8 +19,11 @@
 
 """Misc usefull classes"""
 
+import inspect
+from typing import Callable
 from libervia.backend.core.i18n import _
 from libervia.backend.core.log import getLogger
+from libervia.backend.core.core_types import SatXMPPEntity
 
 log = getLogger(__name__)
 
@@ -49,7 +52,18 @@
     def __init__(self):
         self.__triggers = {}
 
-    def add(self, point_name, callback, priority=0):
+    def is_available(self, args: list):
+        """Check if plugin used in a client context, and if it's available"""
+        if not args or not isinstance(args[0], SatXMPPEntity):
+            # we are not in the context of a client
+            return True
+        client = args[0]
+        if not client.is_component:
+            # plugins are always avaialble for normal clients
+            return True
+
+
+    def add(self, point_name, callback: Callable, priority=0):
         """Add a trigger to a point
 
         @param point_name: name of the point when the trigger should be run
@@ -74,6 +88,43 @@
             key=lambda trigger_tuple: trigger_tuple[0], reverse=True
         )
 
+    def add_with_check(
+        self,
+        point_name: str,
+        plugin,
+        callback: Callable,
+        priority: int=0
+    ) -> None:
+        """Like [Add], but check session before running the trigger
+
+        This method is to be used for triggers which can run in components and are
+        expecting a ``client``: as all plugins are not run for component, a check will be
+        added before running the trigger, if the plugin is not valid for this component,
+        the trigger is ignored. ``client`` must be the first positional argument.
+
+        @param point_name: name of the trigger point
+        @param plugin: instance of the plugin using this trigger. This is necessary to
+            check if the plugin is available in the session.
+        @param callback: method to call at the trigger point
+        @param priority: callback will be called in priority order, biggest first
+        """
+        if inspect.iscoroutinefunction(callback):
+            async def async_wrapper(client: SatXMPPEntity, *args, **kwargs):
+                if client.is_component and plugin not in client.plugins:
+                    log.debug(f"Ignoring {callback} as parent plugin is not available")
+                    return True
+                else:
+                    return await callback(client, *args, **kwargs)
+            self.add(point_name, async_wrapper, priority)
+        else:
+            def sync_wrapper(client: SatXMPPEntity, *args, **kwargs):
+                if client.is_component and plugin not in client.plugins:
+                    log.debug(f"Ignoring {callback} as parent plugin is not available")
+                    return True
+                else:
+                    return callback(client, *args, **kwargs)
+            self.add(point_name, sync_wrapper, priority)
+
     def remove(self, point_name, callback):
         """Remove a trigger from a point