comparison libervia/backend/core/main.py @ 4193:730f542e4ad0

core: add new `init_script_path` option: `init_script_path` option can be used in `[DEFAULTS]` to run a script at the end of backend initialisation. A new `init_pre_script` method is used to wait for backend to reach this stage (designed to be used mostly by CLI frontend), then the usual `ready_get` method is finished once the script is finished.
author Goffi <goffi@goffi.org>
date Wed, 13 Dec 2023 22:00:22 +0100
parents 3b95704ab777
children e11b13418ba6
comparison
equal deleted inserted replaced
4192:1d24ff583794 4193:730f542e4ad0
20 import os.path 20 import os.path
21 import uuid 21 import uuid
22 import hashlib 22 import hashlib
23 import copy 23 import copy
24 from pathlib import Path 24 from pathlib import Path
25 from typing import Optional, List, Tuple, Dict 25 from typing import Optional, List, Tuple, Dict, cast
26 26
27 from wokkel.data_form import Option 27 from wokkel.data_form import Option
28 from libervia import backend 28 from libervia import backend
29 from libervia.backend.core.i18n import _, D_, language_switch 29 from libervia.backend.core.i18n import _, D_, language_switch
30 from libervia.backend.core import patches 30 from libervia.backend.core import patches
47 from libervia.backend.tools import utils 47 from libervia.backend.tools import utils
48 from libervia.backend.tools import image 48 from libervia.backend.tools import image
49 from libervia.backend.tools.common import dynamic_import 49 from libervia.backend.tools.common import dynamic_import
50 from libervia.backend.tools.common import regex 50 from libervia.backend.tools.common import regex
51 from libervia.backend.tools.common import data_format 51 from libervia.backend.tools.common import data_format
52 from libervia.backend.tools.common import async_process
52 from libervia.backend.stdui import ui_contact_list, ui_profile_manager 53 from libervia.backend.stdui import ui_contact_list, ui_profile_manager
53 import libervia.backend.plugins 54 import libervia.backend.plugins
54 55
55 56
56 log = getLogger(__name__) 57 log = getLogger(__name__)
64 self._cb_map = {} # map from callback_id to callbacks 65 self._cb_map = {} # map from callback_id to callbacks
65 # dynamic menus. key: callback_id, value: menu data (dictionnary) 66 # dynamic menus. key: callback_id, value: menu data (dictionnary)
66 self._menus = {} 67 self._menus = {}
67 self._menus_paths = {} # path to id. key: (menu_type, lower case tuple of path), 68 self._menus_paths = {} # path to id. key: (menu_type, lower case tuple of path),
68 # value: menu id 69 # value: menu id
70
71 # like initialised, but launched before init script is done, mainly useful for CLI
72 # frontend, so it can be used in init script, while other frontends are waiting.
73 self.init_pre_script = defer.Deferred()
69 self.initialised = defer.Deferred() 74 self.initialised = defer.Deferred()
70 self.profiles = {} 75 self.profiles = {}
71 self.plugins = {} 76 self.plugins = {}
72 # map for short name to whole namespace, 77 # map for short name to whole namespace,
73 # extended by plugins with register_namespace 78 # extended by plugins with register_namespace
141 log.exception("Could not initialize bridge") 146 log.exception("Could not initialize bridge")
142 # because init is not complete at this stage, we use callLater 147 # because init is not complete at this stage, we use callLater
143 reactor.callLater(0, self.stop) 148 reactor.callLater(0, self.stop)
144 return 149 return
145 150
151 self.bridge.register_method("init_pre_script", lambda: self.init_pre_script)
146 self.bridge.register_method("ready_get", lambda: self.initialised) 152 self.bridge.register_method("ready_get", lambda: self.initialised)
147 self.bridge.register_method("version_get", lambda: self.full_version) 153 self.bridge.register_method("version_get", lambda: self.full_version)
148 self.bridge.register_method("features_get", self.features_get) 154 self.bridge.register_method("features_get", self.features_get)
149 self.bridge.register_method("profile_name_get", self.memory.get_profile_name) 155 self.bridge.register_method("profile_name_get", self.memory.get_profile_name)
150 self.bridge.register_method("profiles_list_get", self.memory.get_profiles_list) 156 self.bridge.register_method("profiles_list_get", self.memory.get_profiles_list)
237 except Exception as e: 243 except Exception as e:
238 log.error(f"Could not initialize backend: {e}") 244 log.error(f"Could not initialize backend: {e}")
239 sys.exit(1) 245 sys.exit(1)
240 self._add_base_menus() 246 self._add_base_menus()
241 247
248 self.init_pre_script.callback(None)
249
250 init_script = self.memory.config_get(None, "init_script_path")
251 if init_script is not None:
252 init_script = cast(str, init_script)
253 script_path = Path(init_script)
254 if not script_path.exists():
255 log.error(f"Init script doesn't exist: {init_script}")
256 sys.exit(C.EXIT_BAD_ARG)
257 elif not script_path.is_file():
258 log.error(f"Init script is not a file: {init_script}")
259 sys.exit(C.EXIT_BAD_ARG)
260 else:
261 log.info(f"Running init script {init_script!r}.")
262 try:
263 await async_process.run(
264 str(init_script),
265 verbose=True
266 )
267 except RuntimeError as e:
268 log.error(f"Init script failed: {e}")
269 self.stopService()
270 sys.exit(C.EXIT_ERROR)
271
242 self.initialised.callback(None) 272 self.initialised.callback(None)
243 log.info(_("Backend is ready")) 273 log.info(_("Backend is ready"))
244 274
245 # profile autoconnection must be done after self.initialised is called because 275 # profile autoconnection must be done after self.initialised is called because
246 # start_session waits for it. 276 # start_session waits for it.