PyAPI: postpone loading add-ons until after key-maps have been defined

Loading add-ons after key-maps resolves a problem where add-ons would
setup shortcuts before Blender had created the key-maps.
Making add-ons have to declare the key-maps using region & window types
matching Blender's internal values.

This PR a pitfall pointed out in #110030.

Ref !110092
This commit is contained in:
Campbell Barton 2023-09-05 11:35:14 +10:00
parent bedfc68e3f
commit 6de294a191
4 changed files with 49 additions and 20 deletions

View File

@ -57,7 +57,9 @@ def main():
# Initializes Python classes.
# (good place to run a profiler or trace).
utils.load_scripts()
# Postpone loading `extensions` scripts (add-ons & app-templates),
# until after the key-maps have been initialized.
utils.load_scripts(extensions=False)
main()

View File

@ -189,7 +189,7 @@ _global_loaded_modules = [] # store loaded module names for reloading.
import bpy_types as _bpy_types # keep for comparisons, never ever reload this.
def load_scripts(*, reload_scripts=False, refresh_scripts=False):
def load_scripts(*, reload_scripts=False, refresh_scripts=False, extensions=True):
"""
Load scripts and run each modules register function.
@ -199,6 +199,8 @@ def load_scripts(*, reload_scripts=False, refresh_scripts=False):
:arg refresh_scripts: only load scripts which are not already loaded
as modules.
:type refresh_scripts: bool
:arg: extensions: Loads additional scripts (add-ons & app-templates).
:type: extensions: bool
"""
use_time = use_class_register_check = _bpy.app.debug_python
use_user = not _is_factory_startup
@ -306,21 +308,8 @@ def load_scripts(*, reload_scripts=False, refresh_scripts=False):
for mod in modules_from_path(path, loaded_modules):
test_register(mod)
# load template (if set)
if any(_bpy.utils.app_template_paths()):
import bl_app_template_utils
bl_app_template_utils.reset(reload_scripts=reload_scripts)
del bl_app_template_utils
# Deal with add-ons separately.
_initialize_once = getattr(_addon_utils, "_initialize_once", None)
if _initialize_once is not None:
# First time, use fast-path.
_initialize_once()
del _addon_utils._initialize_once
else:
_addon_utils.reset_all(reload_scripts=reload_scripts)
del _initialize_once
if extensions:
load_scripts_extensions(reload_scripts=reload_scripts)
if reload_scripts:
_bpy.context.window_manager.tag_script_reload()
@ -342,6 +331,31 @@ def load_scripts(*, reload_scripts=False, refresh_scripts=False):
)
def load_scripts_extensions(*, reload_scripts=False):
"""
Load extensions scripts (add-ons and app-templates)
:arg reload_scripts: Causes all scripts to have their unregister method
called before loading.
:type reload_scripts: bool
"""
# load template (if set)
if any(_bpy.utils.app_template_paths()):
import bl_app_template_utils
bl_app_template_utils.reset(reload_scripts=reload_scripts)
del bl_app_template_utils
# deal with addons separately
_initialize = getattr(_addon_utils, "_initialize", None)
if _initialize is not None:
# first time, use fast-path
_initialize()
del _addon_utils._initialize
else:
_addon_utils.reset_all(reload_scripts=reload_scripts)
del _initialize
def script_path_user():
"""returns the env var and falls back to home dir or None"""
path = _user_resource('SCRIPTS')

View File

@ -133,6 +133,8 @@ CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_TOOLS, "wm.tool");
CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_MSGBUS_PUB, "wm.msgbus.pub");
CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_MSGBUS_SUB, "wm.msgbus.sub");
static void wm_init_scripts_extensions_once(bContext *C);
static void wm_init_reports(bContext *C)
{
ReportList *reports = CTX_wm_reports(C);
@ -358,6 +360,13 @@ void WM_init(bContext *C, int argc, const char **argv)
STRNCPY(G.lib, BKE_main_blendfile_path_from_global());
CTX_py_init_set(C, true);
WM_keyconfig_init(C);
/* Load add-ons after key-maps have been initialized (but before the blend file has been read),
* important to guarantee default key-maps have been declared & before post-read handlers run. */
wm_init_scripts_extensions_once(C);
wm_homefile_read_post(C, params_file_read_post);
}
@ -416,6 +425,13 @@ void WM_init_splash(bContext *C)
CTX_wm_window_set(C, prevwin);
}
/** Load add-ons & app-templates once on startup. */
static void wm_init_scripts_extensions_once(bContext *C)
{
const char *imports[] = {"bpy", nullptr};
BPY_run_string_eval(C, imports, "bpy.utils.load_scripts_extensions()");
}
/* free strings of open recent files */
static void free_openrecent()
{

View File

@ -545,9 +545,6 @@ int main(int argc,
"this is not intended for typical usage\n\n");
#endif
CTX_py_init_set(C, true);
WM_keyconfig_init(C);
#ifdef WITH_FREESTYLE
/* Initialize Freestyle. */
FRS_init();