Merge branch 'blender-v4.2-release'

This commit is contained in:
Campbell Barton 2024-07-08 15:31:44 +10:00
commit e52783033d
10 changed files with 118 additions and 50 deletions

@ -72,7 +72,8 @@ watch_check_ruff:
check_pylint: check_pylint:
@cd "$(BASE_DIR)" && \ @cd "$(BASE_DIR)" && \
pylint $(PY_FILES) \ pylint $(PY_FILES) \
--disable=C0103,C0111,C0201,C0301,C0302,C0415,R1702,R1705,R0902,R0903,R0913,E0611,E0401,I1101,R0801,C0209,W0511,W0718,W0719,C0413,R0911,R0912,R0914,R0915 \ --enable=useless-suppression \
--disable=C0103,C0111,C0201,C0301,C0302,C0415,R0401,R1702,R1705,R0902,R0903,R0913,E0611,E0401,I1101,R0801,C0209,W0511,W0718,W0719,C0413,R0911,R0912,R0914,R0915 \
--msg-template='{abspath}:{line}:{column}: {msg_id}: {msg} ({symbol})' --msg-template='{abspath}:{line}:{column}: {msg_id}: {msg} ({symbol})'
watch_check_pylint: watch_check_pylint:
@cd "$(BASE_DIR)" && \ @cd "$(BASE_DIR)" && \

@ -400,7 +400,6 @@ def monkeypatch_extenions_repos_update_pre_impl():
def monkeypatch_extenions_repos_update_post_impl(): def monkeypatch_extenions_repos_update_post_impl():
import os import os
# pylint: disable-next=redefined-outer-name
from . import bl_extension_ops from . import bl_extension_ops
repo_cache_store = repo_cache_store_ensure() repo_cache_store = repo_cache_store_ensure()
@ -437,7 +436,7 @@ def monkeypatch_extensions_repos_update_pre(*_):
except Exception as ex: except Exception as ex:
print_debug("ERROR", str(ex)) print_debug("ERROR", str(ex))
try: try:
monkeypatch_extensions_repos_update_pre._fn_orig() monkeypatch_extensions_repos_update_pre.fn_orig()
except Exception as ex: except Exception as ex:
print_debug("ERROR", str(ex)) print_debug("ERROR", str(ex))
@ -446,7 +445,7 @@ def monkeypatch_extensions_repos_update_pre(*_):
def monkeypatch_extenions_repos_update_post(*_): def monkeypatch_extenions_repos_update_post(*_):
print_debug("POST:") print_debug("POST:")
try: try:
monkeypatch_extenions_repos_update_post._fn_orig() monkeypatch_extenions_repos_update_post.fn_orig()
except Exception as ex: except Exception as ex:
print_debug("ERROR", str(ex)) print_debug("ERROR", str(ex))
try: try:
@ -458,40 +457,50 @@ def monkeypatch_extenions_repos_update_post(*_):
def monkeypatch_install(): def monkeypatch_install():
import addon_utils import addon_utils
# pylint: disable-next=protected-access
handlers = bpy.app.handlers._extension_repos_update_pre handlers = bpy.app.handlers._extension_repos_update_pre
# pylint: disable-next=protected-access
fn_orig = addon_utils._initialize_extension_repos_pre fn_orig = addon_utils._initialize_extension_repos_pre
fn_override = monkeypatch_extensions_repos_update_pre fn_override = monkeypatch_extensions_repos_update_pre
for i, fn in enumerate(handlers): for i, fn in enumerate(handlers):
if fn is fn_orig: if fn is fn_orig:
handlers[i] = fn_override handlers[i] = fn_override
fn_override._fn_orig = fn_orig fn_override.fn_orig = fn_orig
break break
# pylint: disable-next=protected-access
handlers = bpy.app.handlers._extension_repos_update_post handlers = bpy.app.handlers._extension_repos_update_post
# pylint: disable-next=protected-access
fn_orig = addon_utils._initialize_extension_repos_post fn_orig = addon_utils._initialize_extension_repos_post
fn_override = monkeypatch_extenions_repos_update_post fn_override = monkeypatch_extenions_repos_update_post
for i, fn in enumerate(handlers): for i, fn in enumerate(handlers):
if fn is fn_orig: if fn is fn_orig:
handlers[i] = fn_override handlers[i] = fn_override
fn_override._fn_orig = fn_orig fn_override.fn_orig = fn_orig
break break
def monkeypatch_uninstall(): def monkeypatch_uninstall():
# pylint: disable-next=protected-access
handlers = bpy.app.handlers._extension_repos_update_pre handlers = bpy.app.handlers._extension_repos_update_pre
fn_override = monkeypatch_extensions_repos_update_pre fn_override = monkeypatch_extensions_repos_update_pre
for i, fn in enumerate(handlers): for i, fn in enumerate(handlers):
if fn is fn_override: if fn is fn_override:
handlers[i] = fn_override._fn_orig handlers[i] = fn_override.fn_orig
del fn_override._fn_orig del fn_override.fn_orig
break break
# pylint: disable-next=protected-access
handlers = bpy.app.handlers._extension_repos_update_post handlers = bpy.app.handlers._extension_repos_update_post
fn_override = monkeypatch_extenions_repos_update_post fn_override = monkeypatch_extenions_repos_update_post
for i, fn in enumerate(handlers): for i, fn in enumerate(handlers):
if fn is fn_override: if fn is fn_override:
handlers[i] = fn_override._fn_orig handlers[i] = fn_override.fn_orig
del fn_override._fn_orig del fn_override.fn_orig
break break
@ -636,9 +645,11 @@ def register():
from bl_ui.space_userpref import USERPREF_MT_interface_theme_presets from bl_ui.space_userpref import USERPREF_MT_interface_theme_presets
USERPREF_MT_interface_theme_presets.append(theme_preset_draw) USERPREF_MT_interface_theme_presets.append(theme_preset_draw)
# pylint: disable-next=protected-access
handlers = bpy.app.handlers._extension_repos_sync handlers = bpy.app.handlers._extension_repos_sync
handlers.append(extenion_repos_sync) handlers.append(extenion_repos_sync)
# pylint: disable-next=protected-access
handlers = bpy.app.handlers._extension_repos_files_clear handlers = bpy.app.handlers._extension_repos_files_clear
handlers.append(extenion_repos_files_clear) handlers.append(extenion_repos_files_clear)
@ -676,10 +687,12 @@ def unregister():
from bl_ui.space_userpref import USERPREF_MT_interface_theme_presets from bl_ui.space_userpref import USERPREF_MT_interface_theme_presets
USERPREF_MT_interface_theme_presets.remove(theme_preset_draw) USERPREF_MT_interface_theme_presets.remove(theme_preset_draw)
# pylint: disable-next=protected-access
handlers = bpy.app.handlers._extension_repos_sync handlers = bpy.app.handlers._extension_repos_sync
if extenion_repos_sync in handlers: if extenion_repos_sync in handlers:
handlers.remove(extenion_repos_sync) handlers.remove(extenion_repos_sync)
# pylint: disable-next=protected-access
handlers = bpy.app.handlers._extension_repos_files_clear handlers = bpy.app.handlers._extension_repos_files_clear
if extenion_repos_files_clear in handlers: if extenion_repos_files_clear in handlers:
handlers.remove(extenion_repos_files_clear) handlers.remove(extenion_repos_files_clear)

@ -404,7 +404,7 @@ def repo_iter_valid_only(context, *, exclude_remote, exclude_system):
if (not repo_item.use_remote_url) and (repo_item.source == 'SYSTEM'): if (not repo_item.use_remote_url) and (repo_item.source == 'SYSTEM'):
continue continue
# Ignore repositories that have invalid settings. # Ignore repositories that have invalid settings.
directory, remote_url = repo_paths_or_none(repo_item) directory, _remote_url = repo_paths_or_none(repo_item)
if directory is None: if directory is None:
continue continue
yield repo_item yield repo_item
@ -494,16 +494,17 @@ def repo_cache_store_refresh_from_prefs(repo_cache_store, include_disabled=False
return repos return repos
def _preferences_ensure_disabled(*, repo_item, pkg_id_sequence, default_set): def _preferences_ensure_disabled(
*,
repo_item, # `RepoItem`
pkg_id_sequence, # `List[str]`
default_set, # `bool`
error_fn, # `Callable[[Exception], None]`
): # `-> Dict[str, Tuple[boo, bool]]`
import sys import sys
import addon_utils import addon_utils
result = {} result = {}
errors = []
def handle_error(ex):
print("Error:", ex)
errors.append(str(ex))
modules_clear = [] modules_clear = []
@ -530,7 +531,7 @@ def _preferences_ensure_disabled(*, repo_item, pkg_id_sequence, default_set):
if not hasattr(repo_module, pkg_id): if not hasattr(repo_module, pkg_id):
print("Repo module \"{:s}.{:s}\" not a sub-module!".format(".".join(module_base_elem), pkg_id)) print("Repo module \"{:s}.{:s}\" not a sub-module!".format(".".join(module_base_elem), pkg_id))
addon_utils.disable(addon_module_name, default_set=default_set, handle_error=handle_error) addon_utils.disable(addon_module_name, default_set=default_set, handle_error=error_fn)
modules_clear.append(pkg_id) modules_clear.append(pkg_id)
@ -569,11 +570,12 @@ def _preferences_ensure_disabled(*, repo_item, pkg_id_sequence, default_set):
continue continue
delattr(repo_module, pkg_id) delattr(repo_module, pkg_id)
return result, errors return result
def _preferences_ensure_enabled(*, repo_item, pkg_id_sequence, result, handle_error): def _preferences_ensure_enabled(*, repo_item, pkg_id_sequence, result, handle_error):
import addon_utils import addon_utils
_ = repo_item, pkg_id_sequence
for addon_module_name, (loaded_default, loaded_state) in result.items(): for addon_module_name, (loaded_default, loaded_state) in result.items():
# The module was not loaded, so no need to restore it. # The module was not loaded, so no need to restore it.
if not loaded_state: if not loaded_state:
@ -639,6 +641,7 @@ def _preferences_ui_redraw():
def _preferences_ui_refresh_addons(): def _preferences_ui_refresh_addons():
import addon_utils import addon_utils
# TODO: make a public method. # TODO: make a public method.
# pylint: disable-next=protected-access
addon_utils.modules._is_first = True addon_utils.modules._is_first = True
@ -928,6 +931,7 @@ def _extensions_repo_sync_wheels(repo_cache_store, extensions_enabled):
local_dir = os.path.join(extensions, ".local") local_dir = os.path.join(extensions, ".local")
# WARNING: bad level call, avoid making this a public function just now. # WARNING: bad level call, avoid making this a public function just now.
# pylint: disable-next=protected-access
addon_utils._extension_sync_wheels( addon_utils._extension_sync_wheels(
local_dir=local_dir, local_dir=local_dir,
wheel_list=wheel_list, wheel_list=wheel_list,
@ -1347,6 +1351,7 @@ class EXTENSIONS_OT_repo_sync(Operator, _ExtCmdMixIn):
repos_lock.append(repo_item.directory) repos_lock.append(repo_item.directory)
# Lock repositories. # Lock repositories.
# pylint: disable-next=attribute-defined-outside-init
self.repo_lock = bl_extension_utils.RepoLock( self.repo_lock = bl_extension_utils.RepoLock(
repo_directories=repos_lock, repo_directories=repos_lock,
cookie=cookie_from_session(), cookie=cookie_from_session(),
@ -1444,6 +1449,7 @@ class EXTENSIONS_OT_repo_sync_all(Operator, _ExtCmdMixIn):
repos_lock.append(repo_item.directory) repos_lock.append(repo_item.directory)
# Lock repositories. # Lock repositories.
# pylint: disable-next=attribute-defined-outside-init
self.repo_lock = bl_extension_utils.RepoLock( self.repo_lock = bl_extension_utils.RepoLock(
repo_directories=repos_lock, repo_directories=repos_lock,
cookie=cookie_from_session(), cookie=cookie_from_session(),
@ -1496,11 +1502,15 @@ class EXTENSIONS_OT_repo_refresh_all(Operator):
# Re-generate JSON meta-data from TOML files (needed for offline repository). # Re-generate JSON meta-data from TOML files (needed for offline repository).
repo_cache_store.refresh_remote_from_directory( repo_cache_store.refresh_remote_from_directory(
directory=repo_item.directory, directory=repo_item.directory,
# NOTE: this isn't a problem as the callback isn't stored.
# pylint: disable-next=cell-var-from-loop
error_fn=lambda ex: self._exceptions_as_report(repo_item.name, ex), error_fn=lambda ex: self._exceptions_as_report(repo_item.name, ex),
force=True, force=True,
) )
repo_cache_store.refresh_local_from_directory( repo_cache_store.refresh_local_from_directory(
directory=repo_item.directory, directory=repo_item.directory,
# NOTE: this isn't a problem as the callback isn't stored.
# pylint: disable-next=cell-var-from-loop
error_fn=lambda ex: self._exceptions_as_report(repo_item.name, ex), error_fn=lambda ex: self._exceptions_as_report(repo_item.name, ex),
) )
@ -1532,7 +1542,9 @@ class EXTENSIONS_OT_repo_enable_from_drop(Operator):
print(self.repo_index) print(self.repo_index)
if (repo := repo_lookup_by_index_or_none_with_report(self.repo_index, self.report)) is None: if (repo := repo_lookup_by_index_or_none_with_report(self.repo_index, self.report)) is None:
return {'CANCELLED'} return {'CANCELLED'}
# pylint: disable-next=attribute-defined-outside-init
self._repo_name = repo.name self._repo_name = repo.name
# pylint: disable-next=attribute-defined-outside-init
self._repo_remote_url = repo.remote_url self._repo_remote_url = repo.remote_url
wm = context.window_manager wm = context.window_manager
@ -1599,8 +1611,11 @@ class EXTENSIONS_OT_package_upgrade_all(Operator, _ExtCmdMixIn):
def exec_command_iter(self, is_modal): def exec_command_iter(self, is_modal):
from . import bl_extension_utils from . import bl_extension_utils
# pylint: disable-next=attribute-defined-outside-init
self._repo_directories = set() self._repo_directories = set()
# pylint: disable-next=attribute-defined-outside-init
self._addon_restore = [] self._addon_restore = []
# pylint: disable-next=attribute-defined-outside-init
self._theme_restore = _preferences_theme_state_create() self._theme_restore = _preferences_theme_state_create()
use_active_only = self.use_active_only use_active_only = self.use_active_only
@ -1690,6 +1705,7 @@ class EXTENSIONS_OT_package_upgrade_all(Operator, _ExtCmdMixIn):
return None return None
# Lock repositories. # Lock repositories.
# pylint: disable-next=attribute-defined-outside-init
self.repo_lock = bl_extension_utils.RepoLock( self.repo_lock = bl_extension_utils.RepoLock(
repo_directories=list(self._repo_directories), repo_directories=list(self._repo_directories),
cookie=cookie_from_session(), cookie=cookie_from_session(),
@ -1698,10 +1714,11 @@ class EXTENSIONS_OT_package_upgrade_all(Operator, _ExtCmdMixIn):
return None return None
for repo_item, pkg_id_sequence in handle_addons_info: for repo_item, pkg_id_sequence in handle_addons_info:
result, errors = _preferences_ensure_disabled( result = _preferences_ensure_disabled(
repo_item=repo_item, repo_item=repo_item,
pkg_id_sequence=pkg_id_sequence, pkg_id_sequence=pkg_id_sequence,
default_set=False, default_set=False,
error_fn=lambda ex: self.report({'ERROR'}, str(ex)),
) )
self._addon_restore.append((repo_item, pkg_id_sequence, result)) self._addon_restore.append((repo_item, pkg_id_sequence, result))
@ -1765,7 +1782,9 @@ class EXTENSIONS_OT_package_install_marked(Operator, _ExtCmdMixIn):
error_fn=self.error_fn_from_exception, error_fn=self.error_fn_from_exception,
)) ))
repo_pkg_map = _pkg_marked_by_repo(repo_cache_store, pkg_manifest_remote_all) repo_pkg_map = _pkg_marked_by_repo(repo_cache_store, pkg_manifest_remote_all)
# pylint: disable-next=attribute-defined-outside-init
self._repo_directories = set() self._repo_directories = set()
# pylint: disable-next=attribute-defined-outside-init
self._repo_map_packages_addon_only = [] self._repo_map_packages_addon_only = []
package_count = 0 package_count = 0
@ -1819,6 +1838,7 @@ class EXTENSIONS_OT_package_install_marked(Operator, _ExtCmdMixIn):
return None return None
# Lock repositories. # Lock repositories.
# pylint: disable-next=attribute-defined-outside-init
self.repo_lock = bl_extension_utils.RepoLock( self.repo_lock = bl_extension_utils.RepoLock(
repo_directories=list(self._repo_directories), repo_directories=list(self._repo_directories),
cookie=cookie_from_session(), cookie=cookie_from_session(),
@ -1919,7 +1939,9 @@ class EXTENSIONS_OT_package_uninstall_marked(Operator, _ExtCmdMixIn):
repo_pkg_map = _pkg_marked_by_repo(repo_cache_store, pkg_manifest_local_all) repo_pkg_map = _pkg_marked_by_repo(repo_cache_store, pkg_manifest_local_all)
package_count = 0 package_count = 0
# pylint: disable-next=attribute-defined-outside-init
self._repo_directories = set() self._repo_directories = set()
# pylint: disable-next=attribute-defined-outside-init
self._theme_restore = _preferences_theme_state_create() self._theme_restore = _preferences_theme_state_create()
# Track add-ons to disable before uninstalling. # Track add-ons to disable before uninstalling.
@ -1958,6 +1980,7 @@ class EXTENSIONS_OT_package_uninstall_marked(Operator, _ExtCmdMixIn):
return None return None
# Lock repositories. # Lock repositories.
# pylint: disable-next=attribute-defined-outside-init
self.repo_lock = bl_extension_utils.RepoLock( self.repo_lock = bl_extension_utils.RepoLock(
repo_directories=list(self._repo_directories), repo_directories=list(self._repo_directories),
cookie=cookie_from_session(), cookie=cookie_from_session(),
@ -1966,11 +1989,12 @@ class EXTENSIONS_OT_package_uninstall_marked(Operator, _ExtCmdMixIn):
return None return None
for repo_item, pkg_id_sequence in handle_addons_info: for repo_item, pkg_id_sequence in handle_addons_info:
# No need to store the result (`_`) because the add-ons aren't going to be enabled again. # No need to store the result because the add-ons aren't going to be enabled again.
_, errors = _preferences_ensure_disabled( _preferences_ensure_disabled(
repo_item=repo_item, repo_item=repo_item,
pkg_id_sequence=pkg_id_sequence, pkg_id_sequence=pkg_id_sequence,
default_set=True, default_set=True,
error_fn=lambda ex: self.report({'ERROR'}, str(ex)),
) )
return bl_extension_utils.CommandBatch( return bl_extension_utils.CommandBatch(
@ -2066,7 +2090,9 @@ class EXTENSIONS_OT_package_install_files(Operator, _ExtCmdMixIn):
pkg_is_legacy_addon, pkg_is_legacy_addon,
) )
# pylint: disable-next=attribute-defined-outside-init
self._addon_restore = [] self._addon_restore = []
# pylint: disable-next=attribute-defined-outside-init
self._theme_restore = _preferences_theme_state_create() self._theme_restore = _preferences_theme_state_create()
# Happens when run from scripts and this argument isn't passed in. # Happens when run from scripts and this argument isn't passed in.
@ -2138,7 +2164,9 @@ class EXTENSIONS_OT_package_install_files(Operator, _ExtCmdMixIn):
return None return None
# Collect package ID's. # Collect package ID's.
# pylint: disable-next=attribute-defined-outside-init
self.repo_directory = directory self.repo_directory = directory
# pylint: disable-next=attribute-defined-outside-init
self.pkg_id_sequence = pkg_id_sequence self.pkg_id_sequence = pkg_id_sequence
# Detect upgrade. # Detect upgrade.
@ -2151,15 +2179,17 @@ class EXTENSIONS_OT_package_install_files(Operator, _ExtCmdMixIn):
if pkg_manifest_local is not None: if pkg_manifest_local is not None:
pkg_id_sequence_upgrade = [pkg_id for pkg_id in pkg_id_sequence if pkg_id in pkg_manifest_local] pkg_id_sequence_upgrade = [pkg_id for pkg_id in pkg_id_sequence if pkg_id in pkg_manifest_local]
if pkg_id_sequence_upgrade: if pkg_id_sequence_upgrade:
result, errors = _preferences_ensure_disabled( result = _preferences_ensure_disabled(
repo_item=repo_item, repo_item=repo_item,
pkg_id_sequence=pkg_id_sequence_upgrade, pkg_id_sequence=pkg_id_sequence_upgrade,
default_set=False, default_set=False,
error_fn=lambda ex: self.report({'ERROR'}, str(ex)),
) )
self._addon_restore.append((repo_item, pkg_id_sequence_upgrade, result)) self._addon_restore.append((repo_item, pkg_id_sequence_upgrade, result))
del repo_cache_store, pkg_manifest_local del repo_cache_store, pkg_manifest_local
# Lock repositories. # Lock repositories.
# pylint: disable-next=attribute-defined-outside-init
self.repo_lock = bl_extension_utils.RepoLock( self.repo_lock = bl_extension_utils.RepoLock(
repo_directories=[repo_item.directory], repo_directories=[repo_item.directory],
cookie=cookie_from_session(), cookie=cookie_from_session(),
@ -2503,7 +2533,9 @@ class EXTENSIONS_OT_package_install(Operator, _ExtCmdMixIn):
if not self._is_ready_to_execute(): if not self._is_ready_to_execute():
return None return None
# pylint: disable-next=attribute-defined-outside-init
self._addon_restore = [] self._addon_restore = []
# pylint: disable-next=attribute-defined-outside-init
self._theme_restore = _preferences_theme_state_create() self._theme_restore = _preferences_theme_state_create()
directory = _repo_dir_and_index_get(self.repo_index, self.repo_directory, self.report) directory = _repo_dir_and_index_get(self.repo_index, self.repo_directory, self.report)
@ -2531,15 +2563,17 @@ class EXTENSIONS_OT_package_install(Operator, _ExtCmdMixIn):
if is_installed: if is_installed:
pkg_id_sequence = (pkg_id,) pkg_id_sequence = (pkg_id,)
result, errors = _preferences_ensure_disabled( result = _preferences_ensure_disabled(
repo_item=repo_item, repo_item=repo_item,
pkg_id_sequence=pkg_id_sequence, pkg_id_sequence=pkg_id_sequence,
default_set=False, default_set=False,
error_fn=lambda ex: self.report({'ERROR'}, str(ex)),
) )
self._addon_restore.append((repo_item, pkg_id_sequence, result)) self._addon_restore.append((repo_item, pkg_id_sequence, result))
del pkg_id_sequence del pkg_id_sequence
# Lock repositories. # Lock repositories.
# pylint: disable-next=attribute-defined-outside-init
self.repo_lock = bl_extension_utils.RepoLock( self.repo_lock = bl_extension_utils.RepoLock(
repo_directories=[repo_item.directory], repo_directories=[repo_item.directory],
cookie=cookie_from_session(), cookie=cookie_from_session(),
@ -2729,7 +2763,7 @@ class EXTENSIONS_OT_package_install(Operator, _ExtCmdMixIn):
) )
layout = self.layout layout = self.layout
_repo_index, repo_name, pkg_id, item_remote = self._drop_variables _repo_index, repo_name, _pkg_id, item_remote = self._drop_variables
layout.label(text="Do you want to install the following {:s}?".format(item_remote.type)) layout.label(text="Do you want to install the following {:s}?".format(item_remote.type))
@ -2809,7 +2843,6 @@ class EXTENSIONS_OT_package_install(Operator, _ExtCmdMixIn):
repo_from_url_name, # `str` repo_from_url_name, # `str`
url, # `str` url, # `str`
): ):
import string
from .bl_extension_utils import ( from .bl_extension_utils import (
platform_from_this_system, platform_from_this_system,
) )
@ -2945,6 +2978,7 @@ class EXTENSIONS_OT_package_uninstall(Operator, _ExtCmdMixIn):
def exec_command_iter(self, is_modal): def exec_command_iter(self, is_modal):
from . import bl_extension_utils from . import bl_extension_utils
# pylint: disable-next=attribute-defined-outside-init
self._theme_restore = _preferences_theme_state_create() self._theme_restore = _preferences_theme_state_create()
directory = _repo_dir_and_index_get(self.repo_index, self.repo_directory, self.report) directory = _repo_dir_and_index_get(self.repo_index, self.repo_directory, self.report)
@ -2959,13 +2993,16 @@ class EXTENSIONS_OT_package_uninstall(Operator, _ExtCmdMixIn):
self.report({'ERROR'}, "Package ID not set") self.report({'ERROR'}, "Package ID not set")
return None return None
_, errors = _preferences_ensure_disabled( # No need to store the result because the add-ons aren't going to be enabled again.
_preferences_ensure_disabled(
repo_item=repo_item, repo_item=repo_item,
pkg_id_sequence=(pkg_id,), pkg_id_sequence=(pkg_id,),
default_set=True, default_set=True,
error_fn=lambda ex: self.report({'ERROR'}, str(ex)),
) )
# Lock repositories. # Lock repositories.
# pylint: disable-next=attribute-defined-outside-init
self.repo_lock = bl_extension_utils.RepoLock( self.repo_lock = bl_extension_utils.RepoLock(
repo_directories=[repo_item.directory], repo_directories=[repo_item.directory],
cookie=cookie_from_session(), cookie=cookie_from_session(),
@ -3388,7 +3425,7 @@ class EXTENSIONS_OT_userpref_tags_set(Operator):
return {'CANCELLED'} return {'CANCELLED'}
tags_clear(wm, tags_attr) tags_clear(wm, tags_attr)
if self.value is False: if value is False:
tags_refresh(wm, tags_attr, default_value=False) tags_refresh(wm, tags_attr, default_value=False)
_preferences_ui_redraw() _preferences_ui_redraw()

@ -561,7 +561,7 @@ def addons_panel_draw_items(
return module_names return module_names
def addons_panel_draw_error_duplicates(layout, error_duplicates): def addons_panel_draw_error_duplicates(layout):
import addon_utils import addon_utils
box = layout.box() box = layout.box()
row = box.row() row = box.row()
@ -609,7 +609,7 @@ def addons_panel_draw_impl(
# First show any errors, this should be an exceptional situation that should be resolved, # First show any errors, this should be an exceptional situation that should be resolved,
# otherwise add-ons may not behave correctly. # otherwise add-ons may not behave correctly.
if addon_utils.error_duplicates: if addon_utils.error_duplicates:
addons_panel_draw_error_duplicates(layout, addon_utils.error_duplicates) addons_panel_draw_error_duplicates(layout)
if addon_utils.error_encoding: if addon_utils.error_encoding:
addons_panel_draw_error_generic( addons_panel_draw_error_generic(
layout, ( layout, (
@ -752,6 +752,7 @@ def addons_panel_draw(panel, context):
# Light weight wrapper for extension local and remote extension manifest data. # Light weight wrapper for extension local and remote extension manifest data.
# Used for display purposes. Includes some information for filtering. # Used for display purposes. Includes some information for filtering.
# pylint: disable-next=wrong-import-order
from collections import namedtuple from collections import namedtuple
ExtensionUI = namedtuple( ExtensionUI = namedtuple(
@ -899,15 +900,14 @@ class ExtensionUI_FilterParams:
if is_addon: if is_addon:
if is_installed: if is_installed:
# Currently we only need to know the module name once installed. # Currently we only need to know the module name once installed.
addon_module_name = repo_module_prefix + pkg_id
# pylint: disable-next=possibly-used-before-assignment # pylint: disable-next=possibly-used-before-assignment
addon_module_name = repo_module_prefix + pkg_id
is_enabled = addon_module_name in self.addons_enabled is_enabled = addon_module_name in self.addons_enabled
else: else:
is_enabled = False is_enabled = False
addon_module_name = None addon_module_name = None
elif is_theme: elif is_theme:
# pylint: disable-next=possibly-used-before-assignment
is_enabled = (repo_index, pkg_id) == self.active_theme_info is_enabled = (repo_index, pkg_id) == self.active_theme_info
addon_module_name = None addon_module_name = None
else: else:

@ -292,8 +292,9 @@ def command_output_from_json_0(
# Internal Functions. # Internal Functions.
# #
# pylint: disable-next=useless-return
def repositories_validate_or_errors(repos: Sequence[str]) -> Optional[InfoItemSeq]: def repositories_validate_or_errors(repos: Sequence[str]) -> Optional[InfoItemSeq]:
_ = repos
return None return None
@ -853,6 +854,8 @@ class CommandBatch:
def _exec_blocking_single( def _exec_blocking_single(
self, self,
report_fn: Callable[[str, str], None], report_fn: Callable[[str, str], None],
# TODO: investigate using this or removing it.
# pylint: disable-next=unused-argument
request_exit_fn: Callable[[], bool], request_exit_fn: Callable[[], bool],
) -> bool: ) -> bool:
for cmd in self._batch: for cmd in self._batch:
@ -1298,6 +1301,7 @@ def pkg_manifest_params_compatible_or_error(
item=item, item=item,
filter_blender_version=this_blender_version, filter_blender_version=this_blender_version,
filter_platform=this_platform, filter_platform=this_platform,
# pylint: disable-next=unnecessary-lambda
skip_message_fn=lambda msg: result_report.append(msg), skip_message_fn=lambda msg: result_report.append(msg),
error_fn=error_fn, error_fn=error_fn,
) )
@ -1891,6 +1895,7 @@ class RepoCacheStore:
) -> Optional[Dict[str, PkgManifest_Normalized]]: ) -> Optional[Dict[str, PkgManifest_Normalized]]:
for repo_entry in self._repos: for repo_entry in self._repos:
if directory == repo_entry.directory: if directory == repo_entry.directory:
# pylint: disable-next=protected-access
return repo_entry._json_data_refresh(force=force, error_fn=error_fn) return repo_entry._json_data_refresh(force=force, error_fn=error_fn)
raise ValueError("Directory {:s} not a known repo".format(directory)) raise ValueError("Directory {:s} not a known repo".format(directory))
@ -1927,6 +1932,7 @@ class RepoCacheStore:
# While we could yield a valid manifest here, # While we could yield a valid manifest here,
# leave it to the caller to skip "remote" data for local-only repositories. # leave it to the caller to skip "remote" data for local-only repositories.
if repo_entry.remote_url: if repo_entry.remote_url:
# pylint: disable-next=protected-access
yield repo_entry._json_data_ensure( yield repo_entry._json_data_ensure(
check_files=check_files, check_files=check_files,
ignore_missing=ignore_missing, ignore_missing=ignore_missing,

@ -232,6 +232,7 @@ def force_exit_ok_enable() -> None:
def execfile(filepath: str) -> Dict[str, Any]: def execfile(filepath: str) -> Dict[str, Any]:
global_namespace = {"__file__": filepath, "__name__": "__main__"} global_namespace = {"__file__": filepath, "__name__": "__main__"}
with open(filepath, "rb") as fh: with open(filepath, "rb") as fh:
# pylint: disable-next=exec-used
exec(compile(fh.read(), filepath, 'exec'), global_namespace) exec(compile(fh.read(), filepath, 'exec'), global_namespace)
return global_namespace return global_namespace
@ -446,6 +447,7 @@ def sha256_from_file_or_error(
(exact hashing method may change). (exact hashing method may change).
""" """
try: try:
# pylint: disable-next=consider-using-with
fh_context = open(filepath, 'rb') fh_context = open(filepath, 'rb')
except Exception as ex: except Exception as ex:
return "error opening file: {:s}".format(str(ex)) return "error opening file: {:s}".format(str(ex))
@ -531,6 +533,8 @@ def rmtree_with_fallback_or_error(
if sys.version_info >= (3, 12): if sys.version_info >= (3, 12):
shutil.rmtree(path, onexc=lambda *args: errors.append(args)) shutil.rmtree(path, onexc=lambda *args: errors.append(args))
else: else:
# Ignore as the deprecated logic is only used for older Python versions.
# pylint: disable-next=deprecated-argument
shutil.rmtree(path, onerror=lambda *args: errors.append((args[0], args[1], args[2][1]))) shutil.rmtree(path, onerror=lambda *args: errors.append((args[0], args[1], args[2][1])))
# Happy path (for practically all cases). # Happy path (for practically all cases).
@ -642,7 +646,7 @@ def pkg_manifest_from_dict_and_validate_impl(
for key in PkgManifest._fields: for key in PkgManifest._fields:
val = data.get(key, ...) val = data.get(key, ...)
if val is ...: if val is ...:
# pylint: disable-next=no-member # pylint: disable-next=no-member,protected-access
val = PkgManifest._field_defaults.get(key, ...) val = PkgManifest._field_defaults.get(key, ...)
# `pkg_manifest_is_valid_or_error{_all}` will have caught this, assert all the same. # `pkg_manifest_is_valid_or_error{_all}` will have caught this, assert all the same.
assert val is not ... assert val is not ...
@ -820,6 +824,7 @@ def pkg_manifest_from_archive_and_validate(
strict: bool, strict: bool,
) -> Union[PkgManifest, str]: ) -> Union[PkgManifest, str]:
try: try:
# pylint: disable-next=consider-using-with
zip_fh_context = zipfile.ZipFile(filepath, mode="r") zip_fh_context = zipfile.ZipFile(filepath, mode="r")
except Exception as ex: except Exception as ex:
return "Error extracting archive \"{:s}\"".format(str(ex)) return "Error extracting archive \"{:s}\"".format(str(ex))
@ -836,6 +841,7 @@ def pkg_is_legacy_addon(filepath: str) -> bool:
return True return True
try: try:
# pylint: disable-next=consider-using-with
zip_fh_context = zipfile.ZipFile(filepath, mode="r") zip_fh_context = zipfile.ZipFile(filepath, mode="r")
except Exception: except Exception:
return False return False
@ -1450,12 +1456,12 @@ def pkg_manifest_tags_valid_or_error(
# #
# However manifests from severs that don't adhere to strict rules are not prevented from loading. # However manifests from severs that don't adhere to strict rules are not prevented from loading.
# pylint: disable-next=useless-return
def pkg_manifest_validate_field_nop( def pkg_manifest_validate_field_nop(
value: Any, value: Any,
strict: bool, strict: bool,
) -> Optional[str]: ) -> Optional[str]:
_ = strict, value _ = strict, value
# pylint: disable-next=useless-return
return None return None
@ -1816,11 +1822,11 @@ def pkg_manifest_is_valid_or_error_impl(
is_default_value = False is_default_value = False
x_val = data.get(x_key, ...) x_val = data.get(x_key, ...)
if x_val is ...: if x_val is ...:
# pylint: disable-next=no-member # pylint: disable-next=no-member, protected-access
x_val = PkgManifest._field_defaults.get(x_key, ...) x_val = PkgManifest._field_defaults.get(x_key, ...)
if from_repo: if from_repo:
if x_val is ...: if x_val is ...:
# pylint: disable-next=no-member # pylint: disable-next=no-member, protected-access
x_val = PkgManifest_Archive._field_defaults.get(x_key, ...) x_val = PkgManifest_Archive._field_defaults.get(x_key, ...)
if x_val is ...: if x_val is ...:
error_list.append("missing \"{:s}\"".format(x_key)) error_list.append("missing \"{:s}\"".format(x_key))
@ -2250,7 +2256,7 @@ def pkg_manifest_toml_is_valid_or_error(filepath: str, strict: bool) -> Tuple[Op
return None, result return None, result
def pkg_manifest_detect_duplicates(pkg_idname: str, pkg_items: List[PkgManifest]) -> Optional[str]: def pkg_manifest_detect_duplicates(pkg_items: List[PkgManifest]) -> Optional[str]:
""" """
When a repository includes multiple packages with the same ID, ensure they don't conflict. When a repository includes multiple packages with the same ID, ensure they don't conflict.
@ -3206,7 +3212,7 @@ class subcmd_server:
for pkg_idname, pkg_items in repo_data_idname_map.items(): for pkg_idname, pkg_items in repo_data_idname_map.items():
if len(pkg_items) == 1: if len(pkg_items) == 1:
continue continue
if (error := pkg_manifest_detect_duplicates(pkg_idname, pkg_items)) is not None: if (error := pkg_manifest_detect_duplicates(pkg_items)) is not None:
msglog.warn("archive found with duplicates for id {:s}: {:s}".format(pkg_idname, error)) msglog.warn("archive found with duplicates for id {:s}: {:s}".format(pkg_idname, error))
if html: if html:
@ -3354,6 +3360,7 @@ class subcmd_client:
directories_to_clean: List[str] = [] directories_to_clean: List[str] = []
with CleanupPathsContext(files=(), directories=directories_to_clean): with CleanupPathsContext(files=(), directories=directories_to_clean):
try: try:
# pylint: disable-next=consider-using-with
zip_fh_context = zipfile.ZipFile(filepath_archive, mode="r") zip_fh_context = zipfile.ZipFile(filepath_archive, mode="r")
except Exception as ex: except Exception as ex:
msglog.error("Error extracting archive: {:s}".format(str(ex))) msglog.error("Error extracting archive: {:s}".format(str(ex)))
@ -3556,9 +3563,6 @@ class subcmd_client:
has_fatal_error = True has_fatal_error = True
continue continue
def error_handle(ex: Exception) -> None:
msglog.error("{:s}: {:s}".format(pkg_idname, str(ex)))
pkg_info_list = [ pkg_info_list = [
pkg_info for pkg_info in pkg_info_list pkg_info for pkg_info in pkg_info_list
if not repository_filter_skip( if not repository_filter_skip(
@ -3566,7 +3570,10 @@ class subcmd_client:
filter_blender_version=blender_version_tuple, filter_blender_version=blender_version_tuple,
filter_platform=platform_this, filter_platform=platform_this,
skip_message_fn=None, skip_message_fn=None,
error_fn=error_handle, error_fn=lambda ex: any_as_none(
# pylint: disable-next=cell-var-from-loop
msglog.error("{:s}: {:s}".format(pkg_idname, str(ex))),
),
) )
] ]
@ -4023,6 +4030,7 @@ class subcmd_author:
with CleanupPathsContext(files=(outfile_temp,), directories=()): with CleanupPathsContext(files=(outfile_temp,), directories=()):
try: try:
# pylint: disable-next=consider-using-with
zip_fh_context = zipfile.ZipFile(outfile_temp, 'w', zipfile.ZIP_DEFLATED, compresslevel=9) zip_fh_context = zipfile.ZipFile(outfile_temp, 'w', zipfile.ZIP_DEFLATED, compresslevel=9)
except Exception as ex: except Exception as ex:
msglog.fatal_error("Error creating archive \"{:s}\"".format(str(ex))) msglog.fatal_error("Error creating archive \"{:s}\"".format(str(ex)))
@ -4181,6 +4189,7 @@ class subcmd_author:
# extract the archive into a temporary directory and run validation there. # extract the archive into a temporary directory and run validation there.
try: try:
# pylint: disable-next=consider-using-with
zip_fh_context = zipfile.ZipFile(pkg_source_archive, mode="r") zip_fh_context = zipfile.ZipFile(pkg_source_archive, mode="r")
except Exception as ex: except Exception as ex:
msglog.status("Error extracting archive \"{:s}\"".format(str(ex))) msglog.status("Error extracting archive \"{:s}\"".format(str(ex)))
@ -4826,6 +4835,7 @@ def main(
# While this is typically the case, is only guaranteed to be `TextIO` so check `reconfigure` is available. # While this is typically the case, is only guaranteed to be `TextIO` so check `reconfigure` is available.
if not isinstance(fh, io.TextIOWrapper): if not isinstance(fh, io.TextIOWrapper):
continue continue
# pylint: disable-next=no-member; False positive.
if fh.encoding.lower().partition(":")[0] == "utf-8": if fh.encoding.lower().partition(":")[0] == "utf-8":
continue continue
fh.reconfigure(encoding="utf-8") fh.reconfigure(encoding="utf-8")

@ -91,7 +91,9 @@ class HTTPServerContext:
http_thread.daemon = True http_thread.daemon = True
http_thread.start() http_thread.start()
# pylint: disable-next=attribute-defined-outside-init
self._http_thread = http_thread self._http_thread = http_thread
# pylint: disable-next=attribute-defined-outside-init
self._http_server = http_server self._http_server = http_server
def __exit__(self, _type: Any, _value: Any, traceback: Any) -> None: def __exit__(self, _type: Any, _value: Any, traceback: Any) -> None:

@ -111,6 +111,7 @@ classifiers = [
cwd=temp_dir, cwd=temp_dir,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
check=True,
) )
result = search(temp_dir, lambda entry: entry.name.endswith(".whl")) result = search(temp_dir, lambda entry: entry.name.endswith(".whl"))

@ -15,9 +15,8 @@ import sys
import tempfile import tempfile
import tomllib import tomllib
import unittest import unittest
import zipfile
import unittest.util import unittest.util
import zipfile
from typing import ( from typing import (
Any, Any,

@ -291,6 +291,8 @@ def run_blender(
}, },
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
# Allow the caller to read a non-zero return-code.
check=False,
) )
stdout = output.stdout.decode("utf-8") stdout = output.stdout.decode("utf-8")
stderr = output.stderr.decode("utf-8") stderr = output.stderr.decode("utf-8")
@ -754,11 +756,8 @@ class TestPlatform(TestWithTempBlenderUser_MixIn, unittest.TestCase):
def main() -> None: def main() -> None:
global TEMP_DIR_BLENDER_USER # pylint: disable-next=global-statement
global TEMP_DIR_REMOTE global TEMP_DIR_BLENDER_USER, TEMP_DIR_REMOTE, TEMP_DIR_LOCAL, TEMP_DIR_TMPDIR, TEMP_DIR_REMOTE_AS_URL
global TEMP_DIR_LOCAL
global TEMP_DIR_TMPDIR
global TEMP_DIR_REMOTE_AS_URL
with tempfile.TemporaryDirectory() as temp_prefix: with tempfile.TemporaryDirectory() as temp_prefix:
TEMP_DIR_BLENDER_USER = os.path.join(temp_prefix, "bl_ext_blender") TEMP_DIR_BLENDER_USER = os.path.join(temp_prefix, "bl_ext_blender")