Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2021-02-16 11:53:24 +0300
committerCampbell Barton <ideasman42@gmail.com>2021-02-16 12:52:09 +0300
commita059f072741d671ad882c44e93f6f62bb38f27d8 (patch)
tree00ebbff11347ce71df6fc034d338ce29913fd9bb /release/scripts/startup/bl_operators/userpref.py
parent36814ddc94b56a66675b854bc0d7485828ddcd31 (diff)
Fix add-on & app-template installation logic for overwriting modules
The logic to remove one Python module before installing another only worked in simple cases where a file replaced a file. - Installing a single file add-on over a Python package with the same name caused an error as the directory isn't empty. - Removing existing module directories from the zip-file did nothing as the directories from the zip-file that end with a slash were compared with directories from `os.listdir` that don't. - `module_filesystem_remove` assumed ZipFile.namelist() was a list of files in the root of the zip-file when it's a list of all files. While I couldn't find any bugs caused by this, it performed checks that don't make sense, comparing files at different depths of the file-system.
Diffstat (limited to 'release/scripts/startup/bl_operators/userpref.py')
-rw-r--r--release/scripts/startup/bl_operators/userpref.py43
1 files changed, 33 insertions, 10 deletions
diff --git a/release/scripts/startup/bl_operators/userpref.py b/release/scripts/startup/bl_operators/userpref.py
index 7f2fa1292ee..7c51d49d8e7 100644
--- a/release/scripts/startup/bl_operators/userpref.py
+++ b/release/scripts/startup/bl_operators/userpref.py
@@ -36,16 +36,36 @@ from bpy.props import (
from bpy.app.translations import pgettext_tip as tip_
-def module_filesystem_remove(path_base, module_name):
+def _zipfile_root_namelist(file_to_extract):
+ # Return a list of root paths from zipfile.ZipFile.namelist.
import os
+ root_paths = []
+ for f in file_to_extract.namelist():
+ # Python's `zipfile` API always adds a separate at the end of directories.
+ # use `os.path.normpath` instead of `f.removesuffix(os.sep)`
+ # since paths could be stored as `./paths/./`.
+ #
+ # Note that `..` prefixed paths can exist in ZIP files but they don't write to parent directory when extracting.
+ # Nor do they pass the `os.sep not in f` test, this is important,
+ # otherwise `shutil.rmtree` below could made to remove directories outside the installation directory.
+ f = os.path.normpath(f)
+ if os.sep not in f:
+ root_paths.append(f)
+ return root_paths
+
+
+def _module_filesystem_remove(path_base, module_name):
+ # Remove all Python modules with `module_name` in `base_path`.
+ # The `module_name` is expected to be a result from `_zipfile_root_namelist`.
+ import os
+ import shutil
module_name = os.path.splitext(module_name)[0]
for f in os.listdir(path_base):
f_base = os.path.splitext(f)[0]
if f_base == module_name:
f_full = os.path.join(path_base, f)
-
if os.path.isdir(f_full):
- os.rmdir(f_full)
+ shutil.rmtree(f_full)
else:
os.remove(f_full)
@@ -635,11 +655,12 @@ class PREFERENCES_OT_addon_install(Operator):
traceback.print_exc()
return {'CANCELLED'}
+ file_to_extract_root = _zipfile_root_namelist(file_to_extract)
if self.overwrite:
- for f in file_to_extract.namelist():
- module_filesystem_remove(path_addons, f)
+ for f in file_to_extract_root:
+ _module_filesystem_remove(path_addons, f)
else:
- for f in file_to_extract.namelist():
+ for f in file_to_extract_root:
path_dest = os.path.join(path_addons, os.path.basename(f))
if os.path.exists(path_dest):
self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
@@ -655,7 +676,7 @@ class PREFERENCES_OT_addon_install(Operator):
path_dest = os.path.join(path_addons, os.path.basename(pyfile))
if self.overwrite:
- module_filesystem_remove(path_addons, os.path.basename(pyfile))
+ _module_filesystem_remove(path_addons, os.path.basename(pyfile))
elif os.path.exists(path_dest):
self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
return {'CANCELLED'}
@@ -878,11 +899,13 @@ class PREFERENCES_OT_app_template_install(Operator):
traceback.print_exc()
return {'CANCELLED'}
+ # _module_extract_prepare(file_to_extract)
+ file_to_extract_root = _zipfile_root_namelist(file_to_extract)
if self.overwrite:
- for f in file_to_extract.namelist():
- module_filesystem_remove(path_app_templates, f)
+ for f in file_to_extract_root:
+ _module_filesystem_remove(path_app_templates, f)
else:
- for f in file_to_extract.namelist():
+ for f in file_to_extract_root:
path_dest = os.path.join(path_app_templates, os.path.basename(f))
if os.path.exists(path_dest):
self.report({'WARNING'}, "File already installed to %r\n" % path_dest)