Fix #123682: extension repo URL/access token changes don't re-sync

When changing the URL or access token, re-synchronize remote data
automatically.

This changes automatic synchronization to use a background task that
runs based on a timer instead of a modal operator since the operator
is more intrusive and not so well suited to running based on changes
to RNA.
This commit is contained in:
Campbell Barton 2024-06-25 15:52:04 +10:00
parent 2cc6de8f7e
commit 4712aca2a8
4 changed files with 58 additions and 26 deletions

@ -269,41 +269,39 @@ def repos_to_notify():
# Handlers
@bpy.app.handlers.persistent
def extenion_repos_sync(*_):
def extenion_repos_sync(repo, *_):
# Ignore in background mode as this is for the UI to stay in sync.
# Automated tasks must sync explicitly.
if bpy.app.background:
return
# This is called from operators (create or an explicit call to sync)
# so calling a modal operator is "safe".
if (active_repo := repo_active_or_none()) is None:
return
print_debug("SYNC:", active_repo.name)
print_debug("SYNC:", repo.name)
# There may be nothing to upgrade.
if not active_repo.use_remote_url:
if not repo.use_remote_url:
return
if not bpy.app.online_access:
if not repo.remote_url.startswith("file://"):
return
# NOTE: both `extensions.repo_sync_all` and `bl_extension_notify.update_non_blocking` can be used here.
# Call the modal operator in this case as this handler is called after adding a repository.
# The operator has the benefit of showing a progress bar and reporting errors.
# Since this function is used after adding a new repository, it's important the user is aware of any
# errors synchronizing data as there may be connection/access issues they need to resolve.
if not bpy.ops.extensions.repo_sync_all.poll():
print("skipping sync, poll failed")
# Call the non-blocking update because the updates are queued and can be de-duplicated.
# They're less intrusive as they don't use a modal operator.
from .bl_extension_notify import update_non_blocking
from .bl_extension_ops import extension_repos_read
repos_all = extension_repos_read()
repos_notify = [repo_iter for repo_iter in repos_all if repo_iter.name == repo.name]
# The repository may be disabled or invalid for some other reason, in this case skip an update.
if not repos_notify:
return
from contextlib import redirect_stdout
import io
stdout = io.StringIO()
update_non_blocking(repos_fn=lambda: [(repo_iter, True) for repo_iter in repos_notify], immediate=True)
with redirect_stdout(stdout):
bpy.ops.extensions.repo_sync_all('INVOKE_DEFAULT', use_active_only=True)
if text := stdout.getvalue():
repo_status_text.from_message("Sync \"{:s}\"".format(active_repo.name), text)
# Without this the preferences wont show update text.
from .bl_extension_ui import notify_info
notify_info.update_show_in_preferences()
@bpy.app.handlers.persistent

@ -715,6 +715,23 @@ class notify_info:
notify_info._update_state = True
return in_progress
def update_show_in_preferences():
"""
An update was triggered externally (not from the interface).
Without this call, the message in the preferences UI will not display.
"""
# Skip checking updates when:
# - False, updates are running already no need to request displaying again.
# - None, updates have not yet run, meaning they will be displayed when the preferences are next drawn.
if notify_info._update_state is not True:
return
# NOTE: we could assert `bl_extension_notify.update_in_progress` since it is expected
# this function is called when updates have been started, however it's functionally a NOP
# where this case is detected and the notification state will switch to being "complete"
# (when `update_ensure` runs).
notify_info._update_state = False
def extensions_panel_draw_online_extensions_request_impl(
self,

@ -375,9 +375,13 @@ static int preferences_extension_repo_add_exec(bContext *C, wmOperator *op)
U.active_extension_repo = BLI_findindex(&U.extension_repos, new_repo);
U.runtime.is_dirty = true;
BKE_callback_exec_null(bmain, BKE_CB_EVT_EXTENSION_REPOS_UPDATE_POST);
{
PointerRNA new_repo_ptr = RNA_pointer_create(nullptr, &RNA_UserExtensionRepo, new_repo);
PointerRNA *pointers[] = {&new_repo_ptr};
BKE_callback_exec_null(bmain, BKE_CB_EVT_EXTENSION_REPOS_SYNC);
BKE_callback_exec_null(bmain, BKE_CB_EVT_EXTENSION_REPOS_UPDATE_POST);
BKE_callback_exec(bmain, pointers, ARRAY_SIZE(pointers), BKE_CB_EVT_EXTENSION_REPOS_SYNC);
}
/* There's no dedicated notifier for the Preferences. */
WM_event_add_notifier(C, NC_WINDOW, nullptr);

@ -349,6 +349,18 @@ static void rna_userdef_asset_library_path_set(PointerRNA *ptr, const char *valu
BKE_preferences_asset_library_path_set(library, value);
}
/**
* Use sparingly as a sync may be time consuming.
* Any change that may cause loading remote data to change behavior
* (such as the URL or access token) must use this update function.
*/
static void rna_userdef_extension_sync_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr)
{
BKE_callback_exec(bmain, &ptr, 1, BKE_CB_EVT_EXTENSION_REPOS_SYNC);
WM_main_add_notifier(NC_WINDOW, nullptr);
USERDEF_TAG_DIRTY;
}
static void rna_userdef_extension_repo_name_set(PointerRNA *ptr, const char *value)
{
bUserExtensionRepo *repo = (bUserExtensionRepo *)ptr->data;
@ -6832,7 +6844,7 @@ static void rna_def_userdef_filepaths_extension_repo(BlenderRNA *brna)
"Remote URL to the extension repository, "
"the file-system may be referenced using the file URI scheme: \"file://\"");
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_EDITOR_FILEBROWSER);
RNA_def_property_update(prop, 0, "rna_userdef_update");
RNA_def_property_update(prop, 0, "rna_userdef_extension_sync_update");
prop = RNA_def_property(srna, "access_token", PROP_STRING, PROP_PASSWORD);
RNA_def_property_ui_text(
@ -6841,6 +6853,7 @@ static void rna_def_userdef_filepaths_extension_repo(BlenderRNA *brna)
"rna_userdef_extension_repo_access_token_get",
"rna_userdef_extension_repo_access_token_length",
"rna_userdef_extension_repo_access_token_set");
RNA_def_property_update(prop, 0, "rna_userdef_extension_sync_update");
prop = RNA_def_property(srna, "source", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, source_type_items);
@ -6874,7 +6887,7 @@ static void rna_def_userdef_filepaths_extension_repo(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_access_token", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", USER_EXTENSION_REPO_FLAG_USE_ACCESS_TOKEN);
RNA_def_property_ui_text(prop, "Requires Access Token", "Repository requires an access token");
RNA_def_property_update(prop, 0, "rna_userdef_update");
RNA_def_property_update(prop, 0, "rna_userdef_extension_sync_update");
prop = RNA_def_property(srna, "use_custom_directory", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(