addon improvements/fixes
- better error reporting when an addon fails to load - upload an addon which loads partly but then fails (eg, module loads but class register fails) - bugfix addon loading, failier to load would leave _bpy_types._register_immediate = False - added which change on disk are reloaded when enabling. - bpy.path.module_names() now returns (module_name, module_path) pairs.
This commit is contained in:
@@ -183,7 +183,7 @@ def module_names(path, recursive=False):
|
|||||||
:type path: string
|
:type path: string
|
||||||
:arg recursive: Also return submodule names for packages.
|
:arg recursive: Also return submodule names for packages.
|
||||||
:type recursive: bool
|
:type recursive: bool
|
||||||
:return: a list of strings.
|
:return: a list of string pairs (module_name, module_file).
|
||||||
:rtype: list
|
:rtype: list
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -193,13 +193,15 @@ def module_names(path, recursive=False):
|
|||||||
|
|
||||||
for filename in sorted(_os.listdir(path)):
|
for filename in sorted(_os.listdir(path)):
|
||||||
if filename.endswith(".py") and filename != "__init__.py":
|
if filename.endswith(".py") and filename != "__init__.py":
|
||||||
modules.append(filename[0:-3])
|
fullpath = join(path, filename)
|
||||||
|
modules.append((filename[0:-3], fullpath))
|
||||||
elif ("." not in filename):
|
elif ("." not in filename):
|
||||||
directory = join(path, filename)
|
directory = join(path, filename)
|
||||||
if isfile(join(directory, "__init__.py")):
|
fullpath = join(directory, "__init__.py")
|
||||||
modules.append(filename)
|
if isfile(fullpath):
|
||||||
|
modules.append((filename, fullpath))
|
||||||
if recursive:
|
if recursive:
|
||||||
for mod_name in module_names(directory, True):
|
for mod_name, mod_path in module_names(directory, True):
|
||||||
modules.append("%s.%s" % (filename, mod_name))
|
modules.append(("%s.%s" % (filename, mod_name), mod_path))
|
||||||
|
|
||||||
return modules
|
return modules
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ def modules_from_path(path, loaded_modules):
|
|||||||
|
|
||||||
modules = []
|
modules = []
|
||||||
|
|
||||||
for mod_name in _bpy.path.module_names(path):
|
for mod_name, mod_path in _bpy.path.module_names(path):
|
||||||
mod = _test_import(mod_name, loaded_modules)
|
mod = _test_import(mod_name, loaded_modules)
|
||||||
if mod:
|
if mod:
|
||||||
modules.append(mod)
|
modules.append(mod)
|
||||||
|
|||||||
@@ -586,7 +586,8 @@ def _register_module(module):
|
|||||||
bpy_types.register(t)
|
bpy_types.register(t)
|
||||||
except:
|
except:
|
||||||
import traceback
|
import traceback
|
||||||
print("bpy.utils._register_module(): Module '%s' failed to register class '%s.%s'" % (module, t.__module__, t.__name__))
|
import sys
|
||||||
|
print("bpy.utils._register_module(): '%s' failed to register class '%s.%s'" % (sys.modules[module].__file__, t.__module__, t.__name__))
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -875,12 +875,12 @@ class USERPREF_PT_addons(bpy.types.Panel):
|
|||||||
modules_stale = set(USERPREF_PT_addons._addons_fake_modules.keys())
|
modules_stale = set(USERPREF_PT_addons._addons_fake_modules.keys())
|
||||||
|
|
||||||
for path in paths:
|
for path in paths:
|
||||||
for mod_name in bpy.path.module_names(path):
|
for mod_name, mod_path in bpy.path.module_names(path):
|
||||||
modules_stale -= {mod_name}
|
modules_stale -= {mod_name}
|
||||||
mod = USERPREF_PT_addons._addons_fake_modules.get(mod_name)
|
mod = USERPREF_PT_addons._addons_fake_modules.get(mod_name)
|
||||||
if mod:
|
if mod:
|
||||||
if mod.__time__ != os.path.getmtime(mod_path):
|
if mod.__time__ != os.path.getmtime(mod_path):
|
||||||
print("Reloading", mod_name, mod.__time__, os.path.getmtime(mod_path), mod_path)
|
print("reloading addon:", mod_name, mod.__time__, os.path.getmtime(mod_path), mod_path)
|
||||||
del USERPREF_PT_addons._addons_fake_modules[mod_name]
|
del USERPREF_PT_addons._addons_fake_modules[mod_name]
|
||||||
mod = None
|
mod = None
|
||||||
|
|
||||||
@@ -1052,20 +1052,66 @@ class WM_OT_addon_enable(bpy.types.Operator):
|
|||||||
module_name = self.properties.module
|
module_name = self.properties.module
|
||||||
|
|
||||||
# note, this still gets added to _bpy_types.TypeMap
|
# note, this still gets added to _bpy_types.TypeMap
|
||||||
|
|
||||||
|
import sys
|
||||||
import bpy_types as _bpy_types
|
import bpy_types as _bpy_types
|
||||||
|
|
||||||
|
|
||||||
_bpy_types._register_immediate = False
|
_bpy_types._register_immediate = False
|
||||||
|
|
||||||
try:
|
def handle_error():
|
||||||
mod = __import__(module_name)
|
|
||||||
_bpy_types._register_module(module_name)
|
|
||||||
mod.register()
|
|
||||||
except:
|
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
_bpy_types._register_immediate = True
|
||||||
|
|
||||||
|
|
||||||
|
# reload if the mtime changes
|
||||||
|
mod = sys.modules.get(module_name)
|
||||||
|
if mod:
|
||||||
|
mtime_orig = getattr(mod, "__time__", 0)
|
||||||
|
mtime_new = os.path.getmtime(mod.__file__)
|
||||||
|
if mtime_orig != mtime_new:
|
||||||
|
print("module changed on disk:", mod.__file__, "reloading...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
reload(mod)
|
||||||
|
except:
|
||||||
|
handle_error()
|
||||||
|
del sys.modules[module_name]
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
# Split registering up into 3 steps so we can undo if it fails par way through
|
||||||
|
# 1) try import
|
||||||
|
try:
|
||||||
|
mod = __import__(module_name)
|
||||||
|
mod.__time__ = os.path.getmtime(mod.__file__)
|
||||||
|
except:
|
||||||
|
handle_error()
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
ext = context.user_preferences.addons.new()
|
# 2) try register collected modules
|
||||||
ext.module = module_name
|
try:
|
||||||
|
_bpy_types._register_module(module_name)
|
||||||
|
except:
|
||||||
|
handle_error()
|
||||||
|
del sys.modules[module_name]
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
# 3) try run the modules register function
|
||||||
|
try:
|
||||||
|
mod.register()
|
||||||
|
except:
|
||||||
|
handle_error()
|
||||||
|
_bpy_types._unregister_module(module_name)
|
||||||
|
del sys.modules[module_name]
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
# * OK loaded successfully! *
|
||||||
|
# just incase its enabled alredy
|
||||||
|
ext = context.user_preferences.addons.get(module_name)
|
||||||
|
if not ext:
|
||||||
|
ext = context.user_preferences.addons.new()
|
||||||
|
ext.module = module_name
|
||||||
|
|
||||||
# check if add-on is written for current blender version, or raise a warning
|
# check if add-on is written for current blender version, or raise a warning
|
||||||
info = addon_info_get(mod)
|
info = addon_info_get(mod)
|
||||||
@@ -1097,15 +1143,13 @@ class WM_OT_addon_disable(bpy.types.Operator):
|
|||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
|
# could be in more then once, unlikely but better do this just incase.
|
||||||
addons = context.user_preferences.addons
|
addons = context.user_preferences.addons
|
||||||
ok = True
|
|
||||||
while ok: # incase its in more then once.
|
while module_name in addons:
|
||||||
ok = False
|
addon = addons.get(module_name)
|
||||||
for ext in addons:
|
if addon:
|
||||||
if ext.module == module_name:
|
addons.remove(addon)
|
||||||
addons.remove(ext)
|
|
||||||
ok = True
|
|
||||||
break
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user