Extensions: add network timeout and connection limit preferences

These settings are used when running extension updates but may be
used for any operations that perform online access.
This commit is contained in:
Campbell Barton 2024-06-14 15:28:00 +10:00
parent 176864ab03
commit e93f0ee53a
11 changed files with 70 additions and 13 deletions

@ -102,6 +102,10 @@ const UserDef U_default = {
.user_menus = {NULL},
.keyconfigstr = "Blender",
.network_timeout = 10,
.network_connection_limit = 5,
.undosteps = 32,
.undomemory = 0,
.gp_manhattandist = 1,

@ -59,10 +59,6 @@ def _local_module_reload():
class BlExtPreferences(AddonPreferences):
bl_idname = __name__
timeout: IntProperty(
name="Time Out",
default=10,
)
show_development_reports: BoolProperty(
name="Show Development Reports",
description=(

@ -175,6 +175,10 @@ def sync_status_generator(repos_fn):
from functools import partial
prefs = bpy.context.preferences
network_timeout = prefs.system.network_timeout
network_connection_limit = prefs.system.network_connection_limit
cmd_batch_partial = []
for repo_item, do_online_sync in repos_and_do_online:
# Local only repositories should still refresh, but not run the sync.
@ -186,6 +190,7 @@ def sync_status_generator(repos_fn):
remote_url=bl_extension_ops.url_append_defaults(repo_item.remote_url),
online_user_agent=bl_extension_ops.online_user_agent_from_blender(),
access_token=repo_item.access_token,
timeout=network_timeout,
# Never sleep while there is no input, as this blocks Blender.
use_idle=False,
# Needed so the user can exit blender without warnings about a broken pipe.
@ -222,7 +227,7 @@ def sync_status_generator(repos_fn):
# Used as a prefix in status.
title="Update",
batch=cmd_batch_partial,
batch_job_limit=5,
batch_job_limit=network_connection_limit,
)
del cmd_batch_partial

@ -1105,6 +1105,8 @@ class EXTENSIONS_OT_repo_sync(Operator, _ExtCmdMixIn):
self.report({'ERROR'}, str(ex))
return {'CANCELLED'}
prefs = bpy.context.preferences
# Needed to refresh.
self.repo_directory = directory
@ -1121,6 +1123,7 @@ class EXTENSIONS_OT_repo_sync(Operator, _ExtCmdMixIn):
remote_url=url_append_defaults(repo_item.remote_url),
online_user_agent=online_user_agent_from_blender(),
access_token=repo_item.access_token,
timeout=prefs.system.network_timeout,
use_idle=is_modal,
)
)
@ -1201,6 +1204,8 @@ class EXTENSIONS_OT_repo_sync_all(Operator, _ExtCmdMixIn):
self.report({'WARNING'}, str(ex))
return None
prefs = bpy.context.preferences
# It's only required to lock remote repositories, local repositories can refresh without being modified,
# this is essential for system repositories which may be read-only.
repos_lock = []
@ -1216,6 +1221,7 @@ class EXTENSIONS_OT_repo_sync_all(Operator, _ExtCmdMixIn):
remote_url=url_append_defaults(repo_item.remote_url),
online_user_agent=online_user_agent_from_blender(),
access_token=repo_item.access_token,
timeout=prefs.system.network_timeout,
use_idle=is_modal,
))
repos_lock.append(repo_item.directory)
@ -1388,8 +1394,9 @@ class EXTENSIONS_OT_package_upgrade_all(Operator, _ExtCmdMixIn):
assert False, "unreachable" # Poll prevents this.
return None
# TODO: use a preference.
network_connection_limit = 5
prefs = context.preferences
network_connection_limit = prefs.system.network_connection_limit
# NOTE: Unless we have a "clear-cache" operator - there isn't a great place to apply cache-clearing.
# So when cache is disabled simply clear all cache before performing an update.
@ -1449,6 +1456,7 @@ class EXTENSIONS_OT_package_upgrade_all(Operator, _ExtCmdMixIn):
online_user_agent=online_user_agent_from_blender(),
blender_version=bpy.app.version,
access_token=repo_item.access_token,
timeout=prefs.system.network_timeout,
use_cache=repo_item.use_cache,
use_idle=is_modal,
))
@ -1538,8 +1546,9 @@ class EXTENSIONS_OT_package_install_marked(Operator, _ExtCmdMixIn):
self._repo_map_packages_addon_only = []
package_count = 0
# TODO: use a preference.
network_connection_limit = 5
prefs = bpy.context.preferences
network_connection_limit = prefs.system.network_connection_limit
cmd_batch = []
for repo_index, pkg_id_sequence in sorted(repo_pkg_map.items()):
@ -1564,6 +1573,7 @@ class EXTENSIONS_OT_package_install_marked(Operator, _ExtCmdMixIn):
online_user_agent=online_user_agent_from_blender(),
blender_version=bpy.app.version,
access_token=repo_item.access_token,
timeout=prefs.system.network_timeout,
use_cache=repo_item.use_cache,
use_idle=is_modal,
))
@ -2153,6 +2163,8 @@ class EXTENSIONS_OT_package_install(Operator, _ExtCmdMixIn):
self.report({'ERROR'}, "Package ID not set")
return None
prefs = bpy.context.preferences
# Detect upgrade.
repo_cache_store = repo_cache_store_ensure()
pkg_manifest_local = repo_cache_store.refresh_local_from_directory(
@ -2191,6 +2203,7 @@ class EXTENSIONS_OT_package_install(Operator, _ExtCmdMixIn):
online_user_agent=online_user_agent_from_blender(),
blender_version=bpy.app.version,
access_token=repo_item.access_token,
timeout=prefs.system.network_timeout,
use_cache=repo_item.use_cache,
use_idle=is_modal,
)

@ -464,6 +464,7 @@ def repo_sync(
remote_url: str,
online_user_agent: str,
access_token: str,
timeout: float,
use_idle: bool,
force_exit_ok: bool = False,
dry_run: bool = False,
@ -485,6 +486,7 @@ def repo_sync(
"--remote-url", remote_url,
"--online-user-agent", online_user_agent,
"--access-token", access_token,
"--timeout", "{:g}".format(timeout),
*(("--force-exit-ok",) if force_exit_ok else ()),
*(("--demote-connection-errors-to-status",) if demote_connection_errors_to_status else ()),
*(("--extension-override", extension_override) if extension_override else ()),
@ -558,6 +560,7 @@ def pkg_install(
blender_version: Tuple[int, int, int],
online_user_agent: str,
access_token: str,
timeout: float,
use_cache: bool,
use_idle: bool,
) -> Generator[InfoItemSeq, None, None]:
@ -573,6 +576,7 @@ def pkg_install(
"--online-user-agent", online_user_agent,
"--access-token", access_token,
"--local-cache", str(int(use_cache)),
"--timeout", "{:g}".format(timeout),
], use_idle=use_idle)
yield [COMPLETE_ITEM]

@ -993,6 +993,7 @@ def url_retrieve_to_data_iter(
"""
from urllib.error import ContentTooShortError
from urllib.request import urlopen
import socket
request = urllib.request.Request(
url,
@ -1000,7 +1001,10 @@ def url_retrieve_to_data_iter(
headers=headers,
)
with contextlib.closing(urlopen(request, timeout=timeout_in_seconds)) as fp:
with (
urlopen(request, timeout=timeout_in_seconds) if (timeout_in_seconds > 0.0) else
urlopen(request)
) as fp:
response_headers = fp.info()
size = -1
@ -1010,7 +1014,7 @@ def url_retrieve_to_data_iter(
yield (b'', size, response_headers)
if timeout_in_seconds == -1.0:
if timeout_in_seconds <= 0.0:
while True:
block = fp.read(chunk_size)
if not block:

@ -741,6 +741,9 @@ class USERPREF_PT_system_network(SystemPanel, CenterAlignMixIn, Panel):
text = iface_("Disabled on startup, overriding the preference.")
row.label(text=text, translate=False)
layout.row().prop(system, "network_timeout", text="Time Out")
layout.row().prop(system, "network_connection_limit", text="Connection Limit")
class USERPREF_PT_system_memory(SystemPanel, CenterAlignMixIn, Panel):
bl_label = "Memory & Limits"

@ -29,7 +29,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 58
#define BLENDER_FILE_SUBVERSION 59
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

@ -997,6 +997,11 @@ void blo_do_versions_userdef(UserDef *userdef)
}
}
if (!USER_VERSION_ATLEAST(402, 59)) {
userdef->network_timeout = 10;
userdef->network_connection_limit = 5;
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a USER_VERSION_ATLEAST check.

@ -936,7 +936,14 @@ typedef struct UserDef {
/** Flag for all extensions (#eUserPref_ExtensionFlag). */
char extension_flag;
char _pad14[5];
/* Network settings, used by extensions but not specific to extensions. */
/** Time in seconds to wait before timing out online operation (0 uses the systems default). */
uint8_t network_timeout;
/** Maximum number of simulations connection limit for online operations. */
uint8_t network_connection_limit;
char _pad14[3];
short undosteps;
int undomemory;

@ -6228,6 +6228,22 @@ static void rna_def_userdef_system(BlenderRNA *brna)
RNA_def_property_editable_func(prop, "rna_userdef_use_online_access_editable");
RNA_def_property_update(prop, 0, "rna_userdef_update");
prop = RNA_def_property(srna, "network_timeout", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, nullptr, "network_timeout");
RNA_def_property_ui_text(
prop,
"Network Timeout",
"The time in seconds to wait for online operations before a connection may "
"fail with a time-out error. Zero uses the systems default");
prop = RNA_def_property(srna, "network_connection_limit", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, nullptr, "network_connection_limit");
RNA_def_property_ui_text(
prop,
"Network Connection Limit",
"Limit the number of simultaneous internet connections online operations may make at once. "
"Zero disables the limit");
/* Audio */
prop = RNA_def_property(srna, "audio_mixing_buffer", PROP_ENUM, PROP_NONE);