comparison src/server/utils.py @ 1063:4b69f69c6ffd

server: new ProgressHandler helper class, to handle progressing actions
author Goffi <goffi@goffi.org>
date Sun, 11 Mar 2018 19:30:37 +0100
parents f2170536ba23
children 808ec98de8b3
comparison
equal deleted inserted replaced
1062:c80649cdadd5 1063:4b69f69c6ffd
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details. 15 # GNU Affero General Public License for more details.
16 16
17 # You should have received a copy of the GNU Affero General Public License 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/>. 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 from sat.core.i18n import _
20 from twisted.internet import reactor
21 from twisted.internet import defer
22 from sat.core import exceptions
23 from sat.core.log import getLogger
19 import urllib 24 import urllib
25 log = getLogger(__name__)
20 26
21 27
22 def quote(value, safe='@'): 28 def quote(value, safe='@'):
23 """shortcut to quote an unicode value for URL""" 29 """shortcut to quote an unicode value for URL"""
24 return urllib.quote(value.encode('utf-8'), safe=safe) 30 return urllib.quote(value.encode('utf-8'), safe=safe)
31
32
33 class ProgressHandler(object):
34 """class to help the management of progressions"""
35 handlers = {}
36
37 def __init__(self, host, progress_id, profile):
38 self.host = host
39 self.progress_id = progress_id
40 self.profile = profile
41
42 @classmethod
43 def _signal(cls, name, progress_id, data, profile):
44 handlers = cls.handlers
45 if profile in handlers and progress_id in handlers[profile]:
46 handler_data = handlers[profile][progress_id]
47 timeout = handler_data[u'timeout']
48 if timeout.active():
49 timeout.cancel()
50 cb = handler_data[name]
51 if cb is not None:
52 cb(data)
53 if name == u'started':
54 pass
55 elif name == u'finished':
56 handler_data[u'deferred'].callback(data)
57 handler_data[u'instance'].unregister_handler()
58 elif name == u'error':
59 handler_data[u'deferred'].errback(Exception(data))
60 handler_data[u'instance'].unregister_handler()
61
62 def _timeout(self):
63 log.warning(_(u"No progress received, cancelling handler: {progress_id} [{profile}]").format(
64 progress_id = self.progress_id, profile = self.profile))
65
66 def unregister_handler(self):
67 """remove a previously registered handler"""
68 try:
69 del self.handlers[self.profile][self.progress_id]
70 except KeyError:
71 log.warning(_(u"Trying to remove unknown handler: {progress_id} [{profile}]").format(
72 progress_id = self.progress_id, profile = self.profile))
73 else:
74 if not self.handlers[self.profile]:
75 self.handlers[self.profile]
76
77 def register(self, started_cb=None, finished_cb=None, error_cb=None, timeout=30):
78 """register the signals to handle progression
79
80 @param started_cb(callable, None): method to call when progressStarted signal is received
81 @param finished_cb(callable, None): method to call when progressFinished signal is received
82 @param error_cb(callable, None): method to call when progressError signal is received
83 @param timeout(int): progress time out
84 if nothing happen in this progression during this delay,
85 an exception is raised
86 @return (D(dict[unicode,unicode])): a deferred called when progression is finished
87 """
88 handler_data = self.handlers.setdefault(self.profile, {}).setdefault(self.progress_id, {})
89 if handler_data:
90 raise exceptions.ConflictError(u"There is already one handler for this progression")
91 handler_data[u'instance'] = self
92 deferred = handler_data[u'deferred'] = defer.Deferred()
93 handler_data[u'started'] = started_cb
94 handler_data[u'finished'] = finished_cb
95 handler_data[u'error'] = error_cb
96 handler_data[u'timeout'] = reactor.callLater(timeout, self._timeout)
97 return deferred