comparison libervia/backend/tools/trigger.py @ 4071:4b842c1fb686

refactoring: renamed `sat` package to `libervia.backend`
author Goffi <goffi@goffi.org>
date Fri, 02 Jun 2023 11:49:51 +0200
parents sat/tools/trigger.py@524856bd7b19
children 4dc00e848961
comparison
equal deleted inserted replaced
4070:d10748475025 4071:4b842c1fb686
1 #!/usr/bin/env python3
2
3
4 # SAT: a jabber client
5 # Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org)
6
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
16
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20 """Misc usefull classes"""
21
22 from libervia.backend.core.i18n import _
23 from libervia.backend.core.log import getLogger
24
25 log = getLogger(__name__)
26
27
28 class TriggerException(Exception):
29 pass
30
31
32 class SkipOtherTriggers(Exception):
33 """ Exception to raise if normal behaviour must be followed instead of following triggers list """
34
35 pass
36
37
38 class TriggerManager(object):
39 """This class manage triggers: code which interact to change the behaviour of SàT"""
40
41 try: # FIXME: to be removed when a better solution is found
42 MIN_PRIORITY = float("-inf")
43 MAX_PRIORITY = float("+inf")
44 except: # XXX: Pyjamas will bug if you specify ValueError here
45 # Pyjamas uses the JS Float class
46 MIN_PRIORITY = Number.NEGATIVE_INFINITY
47 MAX_PRIORITY = Number.POSITIVE_INFINITY
48
49 def __init__(self):
50 self.__triggers = {}
51
52 def add(self, point_name, callback, priority=0):
53 """Add a trigger to a point
54
55 @param point_name: name of the point when the trigger should be run
56 @param callback: method to call at the trigger point
57 @param priority: callback will be called in priority order, biggest
58 first
59 """
60 if point_name not in self.__triggers:
61 self.__triggers[point_name] = []
62 if priority != 0 and priority in [
63 trigger_tuple[0] for trigger_tuple in self.__triggers[point_name]
64 ]:
65 if priority in (self.MIN_PRIORITY, self.MAX_PRIORITY):
66 log.warning(_("There is already a bound priority [%s]") % point_name)
67 else:
68 log.debug(
69 _("There is already a trigger with the same priority [%s]")
70 % point_name
71 )
72 self.__triggers[point_name].append((priority, callback))
73 self.__triggers[point_name].sort(
74 key=lambda trigger_tuple: trigger_tuple[0], reverse=True
75 )
76
77 def remove(self, point_name, callback):
78 """Remove a trigger from a point
79
80 @param point_name: name of the point when the trigger should be run
81 @param callback: method to remove, must exists in the trigger point
82 """
83 for trigger_tuple in self.__triggers[point_name]:
84 if trigger_tuple[1] == callback:
85 self.__triggers[point_name].remove(trigger_tuple)
86 return
87 raise TriggerException("Trying to remove an unexisting trigger")
88
89 def point(self, point_name, *args, **kwargs):
90 """This put a trigger point
91
92 All the triggers for that point will be run
93 @param point_name: name of the trigger point
94 @param *args: args to transmit to trigger
95 @param *kwargs: kwargs to transmit to trigger
96 if "triggers_no_cancel" is present, it will be popup out
97 when set to True, this argument don't let triggers stop
98 the workflow
99 @return: True if the action must be continued, False else
100 """
101 if point_name not in self.__triggers:
102 return True
103
104 can_cancel = not kwargs.pop('triggers_no_cancel', False)
105
106 for priority, trigger in self.__triggers[point_name]:
107 try:
108 if not trigger(*args, **kwargs) and can_cancel:
109 return False
110 except SkipOtherTriggers:
111 break
112 return True
113
114 def return_point(self, point_name, *args, **kwargs):
115 """Like point but trigger must return (continue, return_value)
116
117 All triggers for that point must return a tuple with 2 values:
118 - continue, same as for point, if False action must be finished
119 - return_value: value to return ONLY IF CONTINUE IS FALSE
120 @param point_name: name of the trigger point
121 @return: True if the action must be continued, False else
122 """
123
124 if point_name not in self.__triggers:
125 return True
126
127 for priority, trigger in self.__triggers[point_name]:
128 try:
129 cont, ret_value = trigger(*args, **kwargs)
130 if not cont:
131 return False, ret_value
132 except SkipOtherTriggers:
133 break
134 return True, None