]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
vici: Export timeout in event listener of Python bindings python-decorators
authorTobias Brunner <tobias@strongswan.org>
Fri, 3 Oct 2025 09:42:11 +0000 (11:42 +0200)
committerTobias Brunner <tobias@strongswan.org>
Fri, 3 Oct 2025 10:38:20 +0000 (12:38 +0200)
This allows running periodic tasks (e.g. check some outside condition)
and stop listening by raising the StopListening exception.

src/libcharon/plugins/vici/python/vici/event_listener.py

index 8594875aacc2e335afa3bfc9ca1cc5468f53063d..34773395eb0caf68e6b69b16db1904f69a95616f 100644 (file)
@@ -1,4 +1,7 @@
 from functools import wraps
+import inspect
+
+from .protocol import RECV_TIMEOUT_DEFAULT
 
 
 class StopListening(Exception):
@@ -19,6 +22,7 @@ class EventListener(object):
         """
         self.event_map = {}
         self.disconnect_list = []
+        self.timeout_list = []
         self.session = session
 
     def set_session(self, session):
@@ -72,7 +76,30 @@ class EventListener(object):
             return wrapper
         return decorator
 
-    def listen(self):
+    def on_timeout(self):
+        """Decorator to mark a function as a listener for when a timeout occurs
+        while waiting for events. Only has an effect if :func:`~listen()` is
+        called with a timeout.
+
+        The decorated function may either take no or two arguments (both will be
+        set to `None`). So this may be applied to a function that's also
+        decorated with :func:`~on_events()`. It may raise
+        :class:`~StopListening` to stop listening and let :func:`~listen()`
+        return.
+
+        :return: decorator function
+        :rtype: any
+        """
+        def decorator(func):
+            self.timeout_list.append(func)
+
+            @wraps(func)
+            def wrapper(*args, **kwargs):
+                return func(*args, **kwargs)
+            return wrapper
+        return decorator
+
+    def listen(self, timeout=RECV_TIMEOUT_DEFAULT):
         """Dispatch events registered via decorators of this instance.
 
         An active session has to be set before calling this. After getting
@@ -83,12 +110,28 @@ class EventListener(object):
         This method does not return unless :class:`~StopListening` or an
         unexpected exception is raised or if the current session is disonnected
         and no new session is set in a listener.
+
+        The optional timeout allows calling functions decorated with
+        :func:`~on_timeout()` if no event has been received for that time. Which
+        may be used to abort listening or perform periodic tasks while
+        continuing to listen for events.
+
+        :param timeout: timeout to wait for events, in fractions of a second
+        :type timeout: float
         """
         while True:
             try:
                 if self.session is None:
                     break
-                for label, event in self.session.listen(self.event_map.keys()):
+                for label, event in self.session.listen(self.event_map.keys(),
+                                                        timeout):
+                    if label is None and event is None:
+                        for func in self.timeout_list:
+                            if len(inspect.signature(func).parameters) > 0:
+                                func(label, event)
+                            else:
+                                func()
+                        continue
                     name = label.decode()
                     if name in self.event_map:
                         self.event_map[name](name, event)