dissallow access to the context while addons import and register.

Since the window manager is needed for keymaps this is kept as an exception.

some addons will need updating, but in every case I've seen addons should not be accessing the context while registering.
(bad stuff! - declaring the scene as a global variable - which crashes when the users loads a new file, manipulating the active object or scene... tsk tsk)
This commit is contained in:
Campbell Barton 2012-12-19 07:27:23 +00:00
parent 5837c7f092
commit ef665b3d18
2 changed files with 26 additions and 9 deletions

@ -29,6 +29,14 @@ __all__ = (
) )
import bpy as _bpy import bpy as _bpy
_user_preferences = _bpy.context.user_preferences
class _RestrictedContext():
__slots__ = ()
@property
def window_manager(self):
return _bpy.data.window_managers[0]
_ctx_restricted = _RestrictedContext()
error_duplicates = False error_duplicates = False
@ -201,7 +209,7 @@ def check(module_name):
:rtype: tuple of booleans :rtype: tuple of booleans
""" """
import sys import sys
loaded_default = module_name in _bpy.context.user_preferences.addons loaded_default = module_name in _user_preferences.addons
mod = sys.modules.get(module_name) mod = sys.modules.get(module_name)
loaded_state = mod and getattr(mod, "__addon_enabled__", Ellipsis) loaded_state = mod and getattr(mod, "__addon_enabled__", Ellipsis)
@ -259,6 +267,11 @@ def enable(module_name, default_set=True, persistent=False):
# Split registering up into 3 steps so we can undo # Split registering up into 3 steps so we can undo
# if it fails par way through. # if it fails par way through.
# first disable the context, using the context at all is
# really bad while loading an addon, don't do it!
ctx = _bpy.context
_bpy.context = _ctx_restricted
# 1) try import # 1) try import
try: try:
mod = __import__(module_name) mod = __import__(module_name)
@ -266,6 +279,7 @@ def enable(module_name, default_set=True, persistent=False):
mod.__addon_enabled__ = False mod.__addon_enabled__ = False
except: except:
handle_error() handle_error()
_bpy.context = ctx
return None return None
# 2) try register collected modules # 2) try register collected modules
@ -279,14 +293,18 @@ def enable(module_name, default_set=True, persistent=False):
getattr(mod, "__file__", module_name)) getattr(mod, "__file__", module_name))
handle_error() handle_error()
del sys.modules[module_name] del sys.modules[module_name]
_bpy.context = ctx
return None return None
# finally restore the context
_bpy.context = ctx
# * OK loaded successfully! * # * OK loaded successfully! *
if default_set: if default_set:
# just in case its enabled already # just in case its enabled already
ext = _bpy.context.user_preferences.addons.get(module_name) ext = _user_preferences.addons.get(module_name)
if not ext: if not ext:
ext = _bpy.context.user_preferences.addons.new() ext = _user_preferences.addons.new()
ext.module = module_name ext.module = module_name
mod.__addon_enabled__ = True mod.__addon_enabled__ = True
@ -327,7 +345,7 @@ def disable(module_name, default_set=True):
(module_name, "disabled" if mod is None else "loaded")) (module_name, "disabled" if mod is None else "loaded"))
# could be in more then once, unlikely but better do this just in case. # could be in more then once, unlikely but better do this just in case.
addons = _bpy.context.user_preferences.addons addons = _user_preferences.addons
if default_set: if default_set:
while module_name in addons: while module_name in addons:

@ -57,6 +57,7 @@ import sys as _sys
import addon_utils as _addon_utils import addon_utils as _addon_utils
_user_preferences = _bpy.context.user_preferences
_script_module_dirs = "startup", "modules" _script_module_dirs = "startup", "modules"
@ -132,8 +133,6 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
""" """
use_time = _bpy.app.debug_python use_time = _bpy.app.debug_python
prefs = _bpy.context.user_preferences
if use_time: if use_time:
import time import time
t_main = time.time() t_main = time.time()
@ -150,7 +149,7 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
# to reload. note that they will only actually reload of the # to reload. note that they will only actually reload of the
# modification time changes. This `won't` work for packages so... # modification time changes. This `won't` work for packages so...
# its not perfect. # its not perfect.
for module_name in [ext.module for ext in prefs.addons]: for module_name in [ext.module for ext in _user_preferences.addons]:
_addon_utils.disable(module_name, default_set=False) _addon_utils.disable(module_name, default_set=False)
def register_module_call(mod): def register_module_call(mod):
@ -235,7 +234,7 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
_addon_utils.reset_all(reload_scripts) _addon_utils.reset_all(reload_scripts)
# run the active integration preset # run the active integration preset
filepath = preset_find(prefs.inputs.active_keyconfig, "keyconfig") filepath = preset_find(_user_preferences.inputs.active_keyconfig, "keyconfig")
if filepath: if filepath:
keyconfig_set(filepath) keyconfig_set(filepath)
@ -264,7 +263,7 @@ def script_path_user():
def script_path_pref(): def script_path_pref():
"""returns the user preference or None""" """returns the user preference or None"""
path = _bpy.context.user_preferences.filepaths.script_directory path = _user_preferences.filepaths.script_directory
return _os.path.normpath(path) if path else None return _os.path.normpath(path) if path else None