SVN maintenance.
[blender.git] / release / scripts / modules / bpy / utils.py
index 1792425268b45aa2899f4dfd4e383d3a5b53478c..e00c12e3c0659b3f287d01a0ac77aaa81573e9cf 100644 (file)
@@ -23,12 +23,16 @@ This module contains utility functions specific to blender but
 not assosiated with blenders internal data.
 """
 
-import bpy as _bpy
-import os as _os
-import sys as _sys
+from _bpy import register_class
+from _bpy import unregister_class
 
 from _bpy import blend_paths
 from _bpy import script_paths as _bpy_script_paths
+from _bpy import user_resource as _user_resource
+
+import bpy as _bpy
+import os as _os
+import sys as _sys
 
 
 def _test_import(module_name, loaded_modules):
@@ -99,9 +103,6 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
     import traceback
     import time
 
-    # must be set back to True on exits
-    _bpy_types._register_immediate = False
-
     t_main = time.time()
 
     loaded_modules = set()
@@ -111,7 +112,6 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
 
     if reload_scripts:
         _bpy_types.TypeMap.clear()
-        _bpy_types.PropertiesMap.clear()
 
         # just unload, dont change user defaults, this means we can sync to reload.
         # note that they will only actually reload of the modification time changes.
@@ -120,7 +120,6 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
             addon_disable(module_name, default_set=False)
 
     def register_module_call(mod):
-        _bpy_types._register_module(mod.__name__)
         register = getattr(mod, "register", None)
         if register:
             try:
@@ -131,7 +130,6 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
             print("\nWarning! '%s' has no register function, this is now a requirement for registerable scripts." % mod.__file__)
 
     def unregister_module_call(mod):
-        _bpy_types._unregister_module(mod.__name__)
         unregister = getattr(mod, "unregister", None)
         if unregister:
             try:
@@ -140,6 +138,7 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
                 traceback.print_exc()
 
     def test_reload(mod):
+        import imp
         # reloading this causes internal errors
         # because the classes from this module are stored internally
         # possibly to refresh internal references too but for now, best not to.
@@ -147,7 +146,7 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
             return mod
 
         try:
-            return reload(mod)
+            return imp.reload(mod)
         except:
             traceback.print_exc()
 
@@ -197,18 +196,14 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
                 for mod in modules_from_path(path, loaded_modules):
                     test_register(mod)
 
-    _bpy_types._register_immediate = True
-
     # deal with addons seperately
-    addon_reset_all()
-
+    addon_reset_all(reload_scripts)
 
     # run the active integration preset
     filepath = preset_find(_bpy.context.user_preferences.inputs.active_keyconfig, "keyconfig")
     if filepath:
         keyconfig_set(filepath)
 
-
     if reload_scripts:
         import gc
         print("gc.collect() -> %d" % gc.collect())
@@ -339,7 +334,14 @@ def addon_check(module_name):
     loaded_default = module_name in _bpy.context.user_preferences.addons
 
     mod = _sys.modules.get(module_name)
-    loaded_state = mod and getattr(mod, "__addon_enabled__")
+    loaded_state = mod and getattr(mod, "__addon_enabled__", Ellipsis)
+
+    if loaded_state is Ellipsis:
+        print("Warning: addon-module %r found module but without"
+               " __addon_enabled__ field, possible name collision from file: %r" %
+               (module_name, getattr(mod, "__file__", "<unknown>")))
+
+        loaded_state = False
 
     return loaded_default, loaded_state
 
@@ -358,15 +360,11 @@ def addon_enable(module_name, default_set=True):
     import os
     import sys
     import bpy_types as _bpy_types
-
-
-    _bpy_types._register_immediate = False
+    import imp
 
     def handle_error():
         import traceback
         traceback.print_exc()
-        _bpy_types._register_immediate = True
-
 
     # reload if the mtime changes
     mod = sys.modules.get(module_name)
@@ -378,7 +376,7 @@ def addon_enable(module_name, default_set=True):
             print("module changed on disk:", mod.__file__, "reloading...")
 
             try:
-                reload(mod)
+                imp.reload(mod)
             except:
                 handle_error()
                 del sys.modules[module_name]
@@ -396,19 +394,13 @@ def addon_enable(module_name, default_set=True):
         return None
 
     # 2) try register collected modules
-    try:
-        _bpy_types._register_module(module_name)
-    except:
-        handle_error()
-        del sys.modules[module_name]
-        return None
+    # removed, addons need to handle own registration now.
 
     # 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 None
 
@@ -419,12 +411,11 @@ def addon_enable(module_name, default_set=True):
         if not ext:
             ext = _bpy.context.user_preferences.addons.new()
             ext.module = module_name
-    
-    _bpy_types._register_immediate = True
 
     mod.__addon_enabled__ = True
 
-    print("\tbpy.utils.addon_enable", mod.__name__)
+    if _bpy.app.debug:
+        print("\tbpy.utils.addon_enable", mod.__name__)
 
     return mod
 
@@ -447,7 +438,6 @@ def addon_disable(module_name, default_set=True):
         mod.__addon_enabled__ = False
 
         try:
-            _bpy_types._unregister_module(module_name, free=False)  # dont free because we may want to enable again.
             mod.unregister()
         except:
             traceback.print_exc()
@@ -462,21 +452,37 @@ def addon_disable(module_name, default_set=True):
             addon = addons.get(module_name)
             if addon:
                 addons.remove(addon)
-    
-    print("\tbpy.utils.addon_disable", module_name)
+
+    if _bpy.app.debug:
+        print("\tbpy.utils.addon_disable", module_name)
 
 
-def addon_reset_all():
+def addon_reset_all(reload_scripts=False):
     """
     Sets the addon state based on the user preferences.
     """
+    import imp
 
-    paths = script_paths("addons") + script_paths("addons_contrib")
+    # RELEASE SCRIPTS: official scripts distributed in Blender releases
+    paths = script_paths("addons")
+
+    # CONTRIB SCRIPTS: good for testing but not official scripts yet
+    paths += script_paths("addons_contrib")
+
+    # EXTERN SCRIPTS: external projects scripts
+    paths += script_paths("addons_extern")
 
     for path in paths:
         _sys_path_ensure(path)
         for mod_name, mod_path in _bpy.path.module_names(path):
             is_enabled, is_loaded = addon_check(mod_name)
+
+            # first check if reload is needed before changing state.
+            if reload_scripts:
+                mod = _sys.modules.get(mod_name)
+                if mod:
+                    imp.reload(mod)
+
             if is_enabled == is_loaded:
                 pass
             elif is_enabled:
@@ -485,12 +491,13 @@ def addon_reset_all():
                 print("\taddon_reset_all unloading", mod_name)
                 addon_disable(mod_name)
 
+
 def preset_find(name, preset_path, display_name=False):
     if not name:
         return None
-    
+
     for directory in preset_paths(preset_path):
-        
+
         if display_name:
             filename = ""
             for fn in _os.listdir(directory):
@@ -533,8 +540,91 @@ def keyconfig_set(filepath):
             keyconfigs.remove(kc_dupe)
         else:
             break
-    
+
     kc_new.name = name
     keyconfigs.active = kc_new
 
 
+def user_resource(type, path="", create=False):
+    """
+    Return a user resource path (normally from the users home directory).
+
+    :arg type: Resource type in ['DATAFILES', 'CONFIG', 'SCRIPTS', 'AUTOSAVE'].
+    :type type: string
+    :arg subdir: Optional subdirectory.
+    :type subdir: string
+    :arg create: Treat the path as a directory and create it if its not existing.
+    :type create: boolean
+    :return: a path.
+    :rtype: string
+    """
+
+    target_path = _user_resource(type, path)
+
+    if create:
+        # should always be true.
+        if target_path:
+            # create path if not existing.
+            if not _os.path.exists(target_path):
+                try:
+                    _os.makedirs(target_path)
+                except:
+                    import traceback
+                    traceback.print_exc()
+                    target_path = ""
+            elif not _os.path.isdir(target_path):
+                print("Path %r found but isn't a directory!" % target_path)
+                target_path = ""
+
+    return target_path
+
+
+def _bpy_module_classes(module, is_registered=False):
+    typemap_list = _bpy_types.TypeMap.get(module, ())
+    i = 0
+    while i < len(typemap_list):
+        cls_weakref, path, line = typemap_list[i]
+        cls = cls_weakref()
+
+        if cls is None:
+            del typemap_list[i]
+        else:
+            if is_registered == ("bl_rna" in cls.__dict__):
+                yield (cls, path, line)
+            i += 1
+
+
+def register_module(module, verbose=False):
+    import traceback
+    if verbose:
+        print("bpy.utils.register_module(%r): ..." % module)
+    for cls, path, line in _bpy_module_classes(module, is_registered=False):
+        if verbose:
+            print("    %s of %s:%s" % (cls, path, line))
+        try:
+            register_class(cls)
+        except:
+            print("bpy.utils.register_module(): failed to registering class '%s.%s'" % (cls.__module__, cls.__name__))
+            print("\t", path, "line", line)
+            traceback.print_exc()
+    if verbose:
+        print("done.\n")
+    if "cls" not in locals():
+        raise Exception("register_module(%r): defines no classes" % module)
+
+
+def unregister_module(module, verbose=False):
+    import traceback
+    if verbose:
+        print("bpy.utils.unregister_module(%r): ..." % module)
+    for cls, path, line in _bpy_module_classes(module, is_registered=True):
+        if verbose:
+            print("    %s of %s:%s" % (cls, path, line))
+        try:
+            unregister_class(cls)
+        except:
+            print("bpy.utils.unregister_module(): failed to unregistering class '%s.%s'" % (cls.__module__, cls.__name__))
+            print("\t", path, "line", line)
+            traceback.print_exc()
+    if verbose:
+        print("done.\n")