def _dyn_owner_apply(draw_func): from _bpy import _bl_owner_id_get owner_id = _bl_owner_id_get() if owner_id is not None: draw_func._owner = owner_id
def enable(module_name, *, default_set=False, persistent=False, handle_error=None): """ Enables an addon by name. :arg module_name: the name of the addon and module. :type module_name: string :arg default_set: Set the user-preference. :type default_set: bool :arg persistent: Ensure the addon is enabled for the entire session (after loading new files). :type persistent: bool :arg handle_error: Called in the case of an error, taking an exception argument. :type handle_error: function :return: the loaded module or None on failure. :rtype: module """ import os import sys from bpy_restrict_state import RestrictBlend if handle_error is None: def handle_error(_ex): import traceback traceback.print_exc() # reload if the mtime changes mod = sys.modules.get(module_name) # chances of the file _not_ existing are low, but it could be removed if mod and os.path.exists(mod.__file__): if getattr(mod, "__addon_enabled__", False): # This is an unlikely situation, # re-register if the module is enabled. # Note: the UI doesn't allow this to happen, # in most cases the caller should 'check()' first. try: mod.unregister() except Exception as ex: print( "Exception in module unregister():", repr(getattr(mod, "__file__", module_name)), ) handle_error(ex) return None mod.__addon_enabled__ = False mtime_orig = getattr(mod, "__time__", 0) mtime_new = os.path.getmtime(mod.__file__) if mtime_orig != mtime_new: import importlib print("module changed on disk:", repr(mod.__file__), "reloading...") try: importlib.reload(mod) except Exception as ex: handle_error(ex) del sys.modules[module_name] return None mod.__addon_enabled__ = False # add the addon first it may want to initialize its own preferences. # must remove on fail through. if default_set: _addon_ensure(module_name) # Split registering up into 3 steps so we can undo # if it fails par way through. # Disable the context: using the context at all # while loading an addon is really bad, don't do it! with RestrictBlend(): # 1) try import try: mod = __import__(module_name) if mod.__file__ is None: # This can happen when the addon has been removed but there are # residual `.pyc` files left behind. raise ImportError(name=module_name) mod.__time__ = os.path.getmtime(mod.__file__) mod.__addon_enabled__ = False except Exception as ex: # if the addon doesn't exist, don't print full traceback if type(ex) is ImportError and ex.name == module_name: print("addon not found:", repr(module_name)) else: handle_error(ex) if default_set: _addon_remove(module_name) return None # 1.1) Fail when add-on is too old. # This is a temporary 2.8x migration check, so we can manage addons that are supported. if mod.bl_info.get("blender", (0, 0, 0)) < (2, 80, 0): if _bpy.app.debug: print( "Warning: Add-on '%s' was not upgraded for 2.80, ignoring" % module_name) return None # 2) Try register collected modules. # Removed register_module, addons need to handle their own registration now. from _bpy import _bl_owner_id_get, _bl_owner_id_set owner_id_prev = _bl_owner_id_get() _bl_owner_id_set(module_name) # 3) Try run the modules register function. try: mod.register() except Exception as ex: print( "Exception in module register():", getattr(mod, "__file__", module_name), ) handle_error(ex) del sys.modules[module_name] if default_set: _addon_remove(module_name) return None finally: _bl_owner_id_set(owner_id_prev) # * OK loaded successfully! * mod.__addon_enabled__ = True mod.__addon_persistent__ = persistent if _bpy.app.debug_python: print("\taddon_utils.enable", mod.__name__) return mod