Mercurial > libervia-web
annotate libervia/server/tasks.py @ 1216:b2d067339de3
python 3 port:
/!\ Python 3.6+ is now needed to use libervia
/!\ instability may occur and features may not be working anymore, this will improve with time
/!\ TxJSONRPC dependency has been removed
The same procedure as in backend has been applied (check backend commit ab2696e34d29 logs
for details). Removed now deprecated code (Pyjamas compiled browser part, legacy blog,
JSON RPC related code).
Adapted code to work without `html` and `themes` dirs.
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 13 Aug 2019 19:12:31 +0200 |
parents | 170802865156 |
children | 987595a254b0 |
rev | line source |
---|---|
1146 | 1 #!/usr/bin/python |
2 # -*- coding: utf-8 -*- | |
3 | |
4 # Libervia: a Salut à Toi frontend | |
5 # Copyright (C) 2011-2019 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 import os | |
20 import os.path | |
21 from twisted.internet import defer | |
22 from twisted.python.procutils import which | |
23 from sat.core import exceptions | |
24 from sat.core.i18n import _ | |
25 from libervia.server.constants import Const as C | |
26 from collections import OrderedDict | |
27 from sat.core.log import getLogger | |
28 from sat.tools.common import async_process | |
29 | |
30 log = getLogger(__name__) | |
31 | |
32 | |
33 class TasksManager(object): | |
34 """Handle tasks of a Libervia site""" | |
1216 | 35 FILE_EXTS = {'py'} |
1146 | 36 |
37 def __init__(self, host, site_resource): | |
38 """ | |
39 @param site_resource(LiberviaRootResource): root resource of the site to manage | |
40 """ | |
41 self.host = host | |
42 self.resource = site_resource | |
43 self.tasks_dir = os.path.join(self.resource.site_path, C.TASKS_DIR) | |
44 self.tasks = OrderedDict() | |
45 self.parseTasks() | |
46 self._build_path = None | |
47 self._current_task = None | |
48 | |
49 @property | |
50 def site_path(self): | |
51 return self.resource.site_path | |
52 | |
53 @property | |
54 def build_path(self): | |
55 """path where generated files will be build for this site""" | |
56 if self._build_path is None: | |
57 self._build_path = self.host.getBuildPath(self.site_name) | |
58 return self._build_path | |
59 | |
60 def getConfig(self, key, default=None, value_type=None): | |
1147
02afab1b15c5
server, pages, tasks: moved getConfig to backend, and added shorcut version in LiberviaPage and TasksManager
Goffi <goffi@goffi.org>
parents:
1146
diff
changeset
|
61 return self.host.getConfig(self.resource, key=key, default=default, |
02afab1b15c5
server, pages, tasks: moved getConfig to backend, and added shorcut version in LiberviaPage and TasksManager
Goffi <goffi@goffi.org>
parents:
1146
diff
changeset
|
62 value_type=value_type) |
1146 | 63 |
64 @property | |
65 def site_name(self): | |
66 return self.resource.site_name | |
67 | |
68 @property | |
69 def task_data(self): | |
1216 | 70 return self.tasks[self._current_task]['data'] |
1146 | 71 |
72 def validateData(self, data): | |
73 """Check values in data""" | |
74 | |
1216 | 75 for var, default, allowed in (("ON_ERROR", "stop", ("continue", "stop")), |
76 ("LOG_OUTPUT", True, bool), | |
77 ("WATCH_DIRS", [], list)): | |
1146 | 78 value = data.setdefault(var, default) |
79 if isinstance(allowed, type): | |
80 if not isinstance(value, allowed): | |
81 raise ValueError( | |
1216 | 82 _("Unexpected value for {var}, {allowed} is expected.") |
1146 | 83 .format(var = var, allowed = allowed)) |
84 else: | |
85 if not value in allowed: | |
1216 | 86 raise ValueError(_("Unexpected value for {var}: {value}").format( |
1146 | 87 var = var, value = value)) |
88 | |
1216 | 89 for var, default, allowed in [["ON_ERROR", "stop", ("continue", "stop")]]: |
1146 | 90 value = data.setdefault(var, default) |
91 if not value in allowed: | |
1216 | 92 raise ValueError(_("Unexpected value for {var}: {value}").format( |
1146 | 93 var = var, value = value)) |
94 | |
95 def parseTasks(self): | |
96 if not os.path.isdir(self.tasks_dir): | |
1216 | 97 log.debug(_("{name} has no task to launch.").format( |
98 name = self.resource.site_name or "default site")) | |
1146 | 99 return |
100 filenames = os.listdir(self.tasks_dir) | |
101 filenames.sort() | |
102 for filename in filenames: | |
103 filepath = os.path.join(self.tasks_dir, filename) | |
1216 | 104 if not filename.startswith('task_') or not os.path.isfile(filepath): |
1146 | 105 continue |
106 task_name, ext = os.path.splitext(filename) | |
107 task_name = task_name[5:].lower().strip() | |
108 if not task_name: | |
109 continue | |
110 if ext[1:] not in self.FILE_EXTS: | |
111 continue | |
112 if task_name in self.tasks: | |
113 raise exceptions.ConflictError( | |
1216 | 114 "A task with the name [{name}] already exists".format( |
1146 | 115 name=task_name)) |
1216 | 116 task_data = {"__name__": "{site_name}.task.{name}".format( |
1146 | 117 site_name=self.site_name, name=task_name)} |
118 self.tasks[task_name] = { | |
1216 | 119 'path': filepath, |
120 'data': task_data, | |
1146 | 121 } |
1216 | 122 exec(compile(open(filepath, "rb").read(), filepath, 'exec'), task_data) |
1154
a1625e68b726
server (tasks): task can now use a "prepare" method to prepare data before running (e.g. WATCH_DIRS)
Goffi <goffi@goffi.org>
parents:
1147
diff
changeset
|
123 # we launch prepare, which is a method used to prepare |
a1625e68b726
server (tasks): task can now use a "prepare" method to prepare data before running (e.g. WATCH_DIRS)
Goffi <goffi@goffi.org>
parents:
1147
diff
changeset
|
124 # data at runtime (e.g. set WATCH_DIRS using config) |
a1625e68b726
server (tasks): task can now use a "prepare" method to prepare data before running (e.g. WATCH_DIRS)
Goffi <goffi@goffi.org>
parents:
1147
diff
changeset
|
125 try: |
a1625e68b726
server (tasks): task can now use a "prepare" method to prepare data before running (e.g. WATCH_DIRS)
Goffi <goffi@goffi.org>
parents:
1147
diff
changeset
|
126 prepare = task_data['prepare'] |
a1625e68b726
server (tasks): task can now use a "prepare" method to prepare data before running (e.g. WATCH_DIRS)
Goffi <goffi@goffi.org>
parents:
1147
diff
changeset
|
127 except KeyError: |
a1625e68b726
server (tasks): task can now use a "prepare" method to prepare data before running (e.g. WATCH_DIRS)
Goffi <goffi@goffi.org>
parents:
1147
diff
changeset
|
128 pass |
a1625e68b726
server (tasks): task can now use a "prepare" method to prepare data before running (e.g. WATCH_DIRS)
Goffi <goffi@goffi.org>
parents:
1147
diff
changeset
|
129 else: |
a1625e68b726
server (tasks): task can now use a "prepare" method to prepare data before running (e.g. WATCH_DIRS)
Goffi <goffi@goffi.org>
parents:
1147
diff
changeset
|
130 prepare(self) |
1146 | 131 self.validateData(task_data) |
1155
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
132 if self.host.options['dev_mode']: |
1166
5baf7ece44a0
server (tasks): fixed crash when --dev_mode is used
Goffi <goffi@goffi.org>
parents:
1155
diff
changeset
|
133 dirs = task_data.get('WATCH_DIRS', []) |
1155
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
134 for dir_ in dirs: |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
135 self.host.files_watcher.watchDir( |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
136 dir_, auto_add=True, recursive=True, |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
137 callback=self._autorunTask, task_name=task_name) |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
138 |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
139 def _autorunTask(self, host, filepath, flags, task_name): |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
140 """Called when an event is received from a watched directory""" |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
141 if flags == ['create']: |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
142 return |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
143 return self.runTask(task_name) |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
144 |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
145 @defer.inlineCallbacks |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
146 def runTask(self, task_name): |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
147 """Run a single task |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
148 |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
149 @param task_name(unicode): name of the task to run |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
150 """ |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
151 task_value = self.tasks[task_name] |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
152 self._current_task = task_name |
1216 | 153 log.info(_('== running task "{task_name}" for {site_name} =='.format( |
1155
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
154 task_name=task_name, site_name=self.site_name))) |
1216 | 155 data = task_value['data'] |
1155
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
156 os.chdir(self.site_path) |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
157 try: |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
158 yield data['start'](self) |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
159 except Exception as e: |
1216 | 160 on_error = data['ON_ERROR'] |
161 if on_error == 'stop': | |
1155
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
162 raise e |
1216 | 163 elif on_error == 'continue': |
164 log.warning(_('Task "{task_name}" failed for {site_name}: {reason}') | |
1155
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
165 .format(task_name=task_name, site_name=self.site_name, reason=e)) |
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
166 else: |
1216 | 167 raise exceptions.InternalError("we should never reach this point") |
1155
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
168 self._current_task = None |
1146 | 169 |
170 @defer.inlineCallbacks | |
171 def runTasks(self): | |
172 """Run all the tasks found""" | |
173 old_path = os.getcwd() | |
1216 | 174 for task_name, task_value in self.tasks.items(): |
1155
813d54af8c0c
server (tasks): tasks can now be automatically ran when something happen in a watched dir:
Goffi <goffi@goffi.org>
parents:
1154
diff
changeset
|
175 yield self.runTask(task_name) |
1146 | 176 os.chdir(old_path) |
177 | |
178 def findCommand(self, name, *args): | |
179 """Find full path of a shell command | |
180 | |
181 @param name(unicode): name of the command to find | |
182 @param *args(unicode): extra names the command may have | |
183 @return (unicode): full path of the command | |
184 @raise exceptions.NotFound: can't find this command | |
185 """ | |
186 names = (name,) + args | |
187 for n in names: | |
188 try: | |
1216 | 189 cmd_path = which(n)[0] |
1146 | 190 except IndexError: |
191 pass | |
1189
170802865156
server (tasks): fixed findCommand when first name can't be found
Goffi <goffi@goffi.org>
parents:
1166
diff
changeset
|
192 else: |
170802865156
server (tasks): fixed findCommand when first name can't be found
Goffi <goffi@goffi.org>
parents:
1166
diff
changeset
|
193 return cmd_path |
1146 | 194 raise exceptions.NotFound(_( |
1216 | 195 "Can't find {name} command, did you install it?").format(name=name)) |
1146 | 196 |
197 def runCommand(self, command, *args, **kwargs): | |
1216 | 198 kwargs['verbose'] = self.task_data["LOG_OUTPUT"] |
1146 | 199 return async_process.CommandProtocol.run(command, *args, **kwargs) |