Extensions: add the blender version to the remote URL

Add query arguments to the URL when connecting to the remote server.

This information is stripped to create relative paths and for error
messages to prevent overly verbose URS's.

Based on !122234.

Co-authored-by: Dalai Felinto <dalai@blender.org>
This commit is contained in:
Campbell Barton 2024-05-28 19:53:40 +10:00
parent 171319d9ef
commit b15bc1b5bb
4 changed files with 87 additions and 15 deletions

@ -168,7 +168,7 @@ def sync_status_generator(repos_notify):
cmd_batch_partial.append(partial(
bl_extension_utils.repo_sync,
directory=repo_item.directory,
remote_url=repo_item.remote_url,
remote_url=bl_extension_ops.url_params_append_defaults(repo_item.remote_url),
online_user_agent=bl_extension_ops.online_user_agent_from_blender(),
access_token=repo_item.access_token if repo_item.use_access_token else "",
# Never sleep while there is no input, as this blocks Blender.

@ -67,6 +67,10 @@ rna_prop_enable_on_install_type_map = {
}
def url_params_append_defaults(url):
return bl_extension_utils.url_params_append_for_blender(url, blender_version=bpy.app.version)
def rna_prop_repo_enum_local_only_itemf(_self, context):
if context is None:
result = []
@ -1003,7 +1007,7 @@ class BlPkgRepoSync(Operator, _BlPkgCmdMixIn):
partial(
bl_extension_utils.repo_sync,
directory=directory,
remote_url=repo_item.remote_url,
remote_url=url_params_append_defaults(repo_item.remote_url),
online_user_agent=online_user_agent_from_blender(),
access_token=repo_item.access_token,
use_idle=is_modal,
@ -1080,7 +1084,7 @@ class BlPkgRepoSyncAll(Operator, _BlPkgCmdMixIn):
cmd_batch.append(partial(
bl_extension_utils.repo_sync,
directory=repo_item.directory,
remote_url=repo_item.remote_url,
remote_url=url_params_append_defaults(repo_item.remote_url),
online_user_agent=online_user_agent_from_blender(),
access_token=repo_item.access_token,
use_idle=is_modal,
@ -1208,7 +1212,7 @@ class BlPkgPkgUpgradeAll(Operator, _BlPkgCmdMixIn):
cmd_batch.append(partial(
bl_extension_utils.pkg_install,
directory=repo_item.directory,
remote_url=repo_item.remote_url,
remote_url=url_params_append_defaults(repo_item.remote_url),
pkg_id_sequence=pkg_id_sequence,
online_user_agent=online_user_agent_from_blender(),
access_token=repo_item.access_token,
@ -1308,7 +1312,7 @@ class BlPkgPkgInstallMarked(Operator, _BlPkgCmdMixIn):
cmd_batch.append(partial(
bl_extension_utils.pkg_install,
directory=repo_item.directory,
remote_url=repo_item.remote_url,
remote_url=url_params_append_defaults(repo_item.remote_url),
pkg_id_sequence=pkg_id_sequence,
online_user_agent=online_user_agent_from_blender(),
access_token=repo_item.access_token,
@ -1826,7 +1830,7 @@ class BlPkgPkgInstall(Operator, _BlPkgCmdMixIn):
partial(
bl_extension_utils.pkg_install,
directory=directory,
remote_url=repo_item.remote_url,
remote_url=url_params_append_defaults(repo_item.remote_url),
pkg_id_sequence=(pkg_id,),
online_user_agent=online_user_agent_from_blender(),
access_token=repo_item.access_token,

@ -25,6 +25,7 @@ __all__ = (
# Public Stand-Alone Utilities.
"pkg_theme_file_list",
"url_params_append_for_blender",
"file_mtime_or_none",
# Public API.
@ -277,6 +278,52 @@ def pkg_theme_file_list(directory: str, pkg_idname: str) -> Tuple[str, List[str]
return theme_dir, theme_files
def _url_params_append(url: str, params: Dict[str, str]) -> str:
import urllib
import urllib.parse
# Remove empty parameters.
params = {key: value for key, value in params.items() if value is not None and value != ""}
if not params:
return url
# Parse the URL to get its scheme, domain, and query parameters.
parsed_url = urllib.parse.urlparse(url)
# Combine existing query parameters with new parameters
existing_params = urllib.parse.parse_qsl(parsed_url.query)
all_params = dict(existing_params)
all_params.update(params)
# Encode all parameters into a new query string
new_query = urllib.parse.urlencode(all_params)
# Combine the scheme, netloc, path, and new query string to form the new URL
new_url = urllib.parse.urlunparse((
parsed_url.scheme,
parsed_url.netloc,
parsed_url.path,
parsed_url.params,
new_query,
parsed_url.fragment,
))
return new_url
def url_params_append_for_blender(url: str, blender_version: Tuple[int, int, int]) -> str:
# `blender_version` is typically `bpy.app.version`.
# While this won't cause errors, it's redundant to add this information to file URL's.
if url.startswith("file://"):
return url
params = {
"blender_version": "{:d}.{:d}.{:d}".format(*blender_version),
}
return _url_params_append(url, params)
# -----------------------------------------------------------------------------
# Public Repository Actions
#

@ -676,6 +676,23 @@ def remote_url_get(url: str) -> str:
return url
def remote_url_params_strip(url: str) -> str:
# Parse the URL to get its scheme, domain, and query parameters.
parsed_url = urllib.parse.urlparse(url)
# Combine the scheme, netloc, path without any other parameters, stripping the URL.
new_url = urllib.parse.urlunparse((
parsed_url.scheme,
parsed_url.netloc,
parsed_url.path,
None, # `parsed_url.params,`
None, # `parsed_url.query,`
None, # `parsed_url.fragment,`
))
return new_url
# -----------------------------------------------------------------------------
# ZipFile Helpers
@ -1072,18 +1089,19 @@ def url_retrieve_exception_as_message(
Provides more user friendly messages when reading from a URL fails.
"""
# These exceptions may occur when reading from the file-system or a URL.
url_strip = remote_url_params_strip(url)
if isinstance(ex, FileNotFoundError):
return "{:s}: file-not-found ({:s}) reading {!r}!".format(prefix, str(ex), url)
return "{:s}: file-not-found ({:s}) reading {!r}!".format(prefix, str(ex), url_strip)
if isinstance(ex, TimeoutError):
return "{:s}: timeout ({:s}) reading {!r}!".format(prefix, str(ex), url)
return "{:s}: timeout ({:s}) reading {!r}!".format(prefix, str(ex), url_strip)
if isinstance(ex, urllib.error.URLError):
if isinstance(ex, urllib.error.HTTPError):
if ex.code == 403:
return "{:s}: HTTP error (403) access token may be incorrect, reading {!r}!".format(prefix, url)
return "{:s}: HTTP error ({:s}) reading {!r}!".format(prefix, str(ex), url)
return "{:s}: URL error ({:s}) reading {!r}!".format(prefix, str(ex), url)
return "{:s}: HTTP error (403) access token may be incorrect, reading {!r}!".format(prefix, url_strip)
return "{:s}: HTTP error ({:s}) reading {!r}!".format(prefix, str(ex), url_strip)
return "{:s}: URL error ({:s}) reading {!r}!".format(prefix, str(ex), url_strip)
return "{:s}: unexpected error ({:s}) reading {!r}!".format(prefix, str(ex), url)
return "{:s}: unexpected error ({:s}) reading {!r}!".format(prefix, str(ex), url_strip)
def pkg_idname_is_valid_or_error(pkg_idname: str) -> Optional[str]:
@ -2328,6 +2346,9 @@ class subcmd_client:
# Ensure a private directory so a local cache can be created.
local_cache_dir = repo_local_private_dir_ensure_with_subdir(local_dir=local_dir, subdir="cache")
# Needed so relative paths can be properly calculated.
remote_url_strip = remote_url_params_strip(remote_url)
# TODO: this could be optimized to only lookup known ID's.
json_data_pkg_info_map: Dict[str, Dict[str, Any]] = {
pkg_info["id"]: pkg_info for pkg_info in pkg_repo_data.data
@ -2376,10 +2397,10 @@ class subcmd_client:
# Remote path.
if pkg_archive_url.startswith("./"):
if remote_url_has_filename_suffix(remote_url):
filepath_remote_archive = remote_url.rpartition("/")[0] + pkg_archive_url[1:]
if remote_url_has_filename_suffix(remote_url_strip):
filepath_remote_archive = remote_url_strip.rpartition("/")[0] + pkg_archive_url[1:]
else:
filepath_remote_archive = remote_url.rstrip("/") + pkg_archive_url[1:]
filepath_remote_archive = remote_url_strip.rstrip("/") + pkg_archive_url[1:]
else:
filepath_remote_archive = pkg_archive_url