Index: release/scripts/modules/bpy_types.py
===================================================================
--- release/scripts/modules/bpy_types.py	(revision 34509)
+++ release/scripts/modules/bpy_types.py	(working copy)
@@ -553,82 +553,104 @@
 import collections
 
 TypeMap = {}
-# Properties (IDPropertyGroup) are different from types because they need to be registered
-# before adding sub properties to them, so they are registered on definition
-# and unregistered on unload
-PropertiesMap = {}
 
 # Using our own loading function we set this to false
 # so when running a script directly in the text editor
-# registers moduals instantly.
+# registers modules instantly.
 _register_immediate = True
 
+class DelayedRegistration:
+    def __enter__(self):
+        global _register_immediate
+        _register_immediate = False
 
-def _unregister_module(module, free=True):
-    for t in TypeMap.get(module, ()):
+    def __exit__(self, *args):
+        global _register_immediate
+        _register_immediate = True
+
+class TypeEntry:
+    def __init__(self, cls, path, line):
+        self.cls = cls
+        self.path = path
+        self.line = line
+        self.registered = False
+        
+    def register(self):
+        if self.registered:
+            return
+        
+        import bpy
+        t = self.cls
         try:
-            bpy_types.unregister(t)
+            bpy_types.register(t)
+            if hasattr(t, "register"):
+                t.register()
+            self.registered = True
+            
+            if bpy.app.debug:
+                print("Registered class '%s.%s'" % (t.__module__, t.__name__))
+                print("\t", self.path, "line", self.line)
         except:
             import traceback
-            print("bpy.utils._unregister_module(): Module '%s' failed to unregister class '%s.%s'" % (module, t.__module__, t.__name__))
-            traceback.print_exc()
+            print("bpy.utils._register_module(): failed to register class '%s.%s'" % (t.__module__, t.__name__))
+            print("\t", self.path, "line", self.line)
+            traceback.print_last()
+        
+    def unregister(self):
+        if not self.registered:
+            return
 
-    if free == True and module in TypeMap:
-        del TypeMap[module]
-
-    for t in PropertiesMap.get(module, ()):
+        t = self.cls
         try:
+            if hasattr(t, "unregister"):
+                t.unregister()
             bpy_types.unregister(t)
+            self.registered = False
         except:
             import traceback
-            print("bpy.utils._unload_module(): Module '%s' failed to unregister class '%s.%s'" % (module, t.__module__, t.__name__))
-            traceback.print_exc()
+            print("bpy.utils._unregister_module(): failed to unregister class '%s.%s'" % (t.__module__, t.__name__))
+            print("\t", self.path, "line", self.line)
+            print("\t", self.text)
+            traceback.print_last()
 
-    if free == True and module in PropertiesMap:
-        del PropertiesMap[module]
+def _unregister_module(module, free=True):
+    for entry in reversed(TypeMap.get(module, ())):
+        entry.unregister()
 
+    if free == True and module in TypeMap:
+        del TypeMap[module]
 
 def _register_module(module):
-    for t in TypeMap.get(module, ()):
-        try:
-            bpy_types.register(t)
-        except:
-            import traceback
-            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()
+    for entry in TypeMap.get(module, ()):
+        entry.register()
 
 
 class RNAMeta(type):
-    @classmethod
-    def _register_immediate(cls):
-        return _register_immediate
-
     def __new__(cls, name, bases, classdict, **args):
+        global TypeMap
+        import traceback
         result = type.__new__(cls, name, bases, classdict)
         if bases and bases[0] != StructRNA:
             module = result.__module__
 
-            ClassMap = TypeMap
+            sf = traceback.extract_stack(limit=2)[0]
+            entry = TypeEntry(result, sf[0], sf[1])
 
             # Register right away if needed
-            if cls._register_immediate():
-                bpy_types.register(result)
-                ClassMap = PropertiesMap
+            if _register_immediate:
+                entry.register()
 
             # first part of packages only
             if "." in module:
                 module = module[:module.index(".")]
+                
+            TypeMap.setdefault(module, []).append(entry)
 
-            ClassMap.setdefault(module, []).append(result)
-
         return result
 
 
-class RNAMetaRegister(RNAMeta, StructMetaIDProp):
-    @classmethod
-    def _register_immediate(cls):
-        return True
+class RNAMetaIDProp(RNAMeta, StructMetaIDProp):
+    pass
 
 
 class OrderedMeta(RNAMeta):
@@ -685,10 +707,9 @@
         return ops.macro_define(self, opname)
 
 
-class IDPropertyGroup(StructRNA, metaclass=RNAMetaRegister):
+class IDPropertyGroup(StructRNA, metaclass=RNAMetaIDProp):
         __slots__ = ()
 
-
 class RenderEngine(StructRNA, metaclass=RNAMeta):
     __slots__ = ()
 
Index: release/scripts/modules/bpy/utils.py
===================================================================
--- release/scripts/modules/bpy/utils.py	(revision 34509)
+++ release/scripts/modules/bpy/utils.py	(working copy)
@@ -100,107 +100,103 @@
     import traceback
     import time
 
-    # must be set back to True on exits
-    _bpy_types._register_immediate = False
-
-    t_main = time.time()
-
-    loaded_modules = set()
-
-    if refresh_scripts:
-        original_modules = _sys.modules.values()
-
-    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.
-        # this `wont` work for packages so... its not perfect.
-        for module_name in [ext.module for ext in _bpy.context.user_preferences.addons]:
-            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:
+    with _bpy_types.DelayedRegistration(): 
+        t_main = time.time()
+    
+        loaded_modules = set()
+    
+        if refresh_scripts:
+            original_modules = _sys.modules.values()
+    
+        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.
+            # this `wont` work for packages so... its not perfect.
+            for module_name in [ext.module for ext in _bpy.context.user_preferences.addons]:
+                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:
+                    register()
+                except:
+                    traceback.print_exc()
+            else:
+                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:
+                    unregister()
+                except:
+                    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.
+            if mod == _bpy_types:
+                return mod
+    
             try:
-                register()
+                return imp.reload(mod)
             except:
                 traceback.print_exc()
-        else:
-            print("\nWarning! '%s' has no register function, this is now a requirement for registerable scripts." % mod.__file__)
+    
+        def test_register(mod):
+    
+            if refresh_scripts and mod in original_modules:
+                return
+    
+            if reload_scripts and mod:
+                print("Reloading:", mod)
+                mod = test_reload(mod)
+    
+            if mod:
+                register_module_call(mod)
+                _global_loaded_modules.append(mod.__name__)
+    
+        if reload_scripts:
+    
+            # module names -> modules
+            _global_loaded_modules[:] = [_sys.modules[mod_name] for mod_name in _global_loaded_modules]
+    
+            # loop over and unload all scripts
+            _global_loaded_modules.reverse()
+            for mod in _global_loaded_modules:
+                unregister_module_call(mod)
+    
+            for mod in _global_loaded_modules:
+                test_reload(mod)
+    
+            _global_loaded_modules[:] = []
+    
+        user_path = user_script_path()
+    
+        for base_path in script_paths():
+            for path_subdir in ("", "ui", "op", "io", "keyingsets", "modules"):
+                path = _os.path.join(base_path, path_subdir)
+                if _os.path.isdir(path):
+                    _sys_path_ensure(path)
+    
+                    # only add this to sys.modules, dont run
+                    if path_subdir == "modules":
+                        continue
+    
+                    if user_path != base_path and path_subdir == "":
+                        continue  # avoid loading 2.4x scripts
+    
+                    for mod in modules_from_path(path, loaded_modules):
+                        test_register(mod)
 
-    def unregister_module_call(mod):
-        _bpy_types._unregister_module(mod.__name__)
-        unregister = getattr(mod, "unregister", None)
-        if unregister:
-            try:
-                unregister()
-            except:
-                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.
-        if mod == _bpy_types:
-            return mod
-
-        try:
-            return imp.reload(mod)
-        except:
-            traceback.print_exc()
-
-    def test_register(mod):
-
-        if refresh_scripts and mod in original_modules:
-            return
-
-        if reload_scripts and mod:
-            print("Reloading:", mod)
-            mod = test_reload(mod)
-
-        if mod:
-            register_module_call(mod)
-            _global_loaded_modules.append(mod.__name__)
-
-    if reload_scripts:
-
-        # module names -> modules
-        _global_loaded_modules[:] = [_sys.modules[mod_name] for mod_name in _global_loaded_modules]
-
-        # loop over and unload all scripts
-        _global_loaded_modules.reverse()
-        for mod in _global_loaded_modules:
-            unregister_module_call(mod)
-
-        for mod in _global_loaded_modules:
-            test_reload(mod)
-
-        _global_loaded_modules[:] = []
-
-    user_path = user_script_path()
-
-    for base_path in script_paths():
-        for path_subdir in ("", "ui", "op", "io", "keyingsets", "modules"):
-            path = _os.path.join(base_path, path_subdir)
-            if _os.path.isdir(path):
-                _sys_path_ensure(path)
-
-                # only add this to sys.modules, dont run
-                if path_subdir == "modules":
-                    continue
-
-                if user_path != base_path and path_subdir == "":
-                    continue  # avoid loading 2.4x scripts
-
-                for mod in modules_from_path(path, loaded_modules):
-                    test_register(mod)
-
-    _bpy_types._register_immediate = True
-
     # deal with addons seperately
     addon_reset_all(reload_scripts)
 
@@ -366,76 +362,73 @@
     import sys
     import bpy_types as _bpy_types
     import imp
-
-    _bpy_types._register_immediate = False
-
-    def handle_error():
-        import traceback
-        traceback.print_exc()
-        _bpy_types._register_immediate = True
-
-    # reload if the mtime changes
-    mod = sys.modules.get(module_name)
-    if mod:
-        mod.__addon_enabled__ = False
-        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:
-                imp.reload(mod)
-            except:
-                handle_error()
-                del sys.modules[module_name]
-                return None
+    
+    with _bpy_types.DelayedRegistration():
+        
+        def handle_error():
+            import traceback
+            traceback.print_exc()
+    
+        # reload if the mtime changes
+        mod = sys.modules.get(module_name)
+        if mod:
             mod.__addon_enabled__ = False
+            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:
+                    imp.reload(mod)
+                except:
+                    handle_error()
+                    del sys.modules[module_name]
+                    return None
+                mod.__addon_enabled__ = False
+    
+        # 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__)
+            mod.__addon_enabled__ = False
+        except:
+            handle_error()
+            return None
+    
+        # 2) try register collected modules
+        try:
+            _bpy_types._register_module(module_name)
+        except:
+            handle_error()
+            del sys.modules[module_name]
+            return None
+    
+        # 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
+    
+        # * OK loaded successfully! *
+        if default_set:
+            # just incase its enabled alredy
+            ext = _bpy.context.user_preferences.addons.get(module_name)
+            if not ext:
+                ext = _bpy.context.user_preferences.addons.new()
+                ext.module = module_name
+    
+        mod.__addon_enabled__ = True
+    
+        if _bpy.app.debug:
+            print("\tbpy.utils.addon_enable", mod.__name__)
+    
+        return mod
 
-    # 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__)
-        mod.__addon_enabled__ = False
-    except:
-        handle_error()
-        return None
 
-    # 2) try register collected modules
-    try:
-        _bpy_types._register_module(module_name)
-    except:
-        handle_error()
-        del sys.modules[module_name]
-        return None
-
-    # 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
-
-    # * OK loaded successfully! *
-    if default_set:
-        # just incase its enabled alredy
-        ext = _bpy.context.user_preferences.addons.get(module_name)
-        if not ext:
-            ext = _bpy.context.user_preferences.addons.new()
-            ext.module = module_name
-
-    _bpy_types._register_immediate = True
-
-    mod.__addon_enabled__ = True
-
-    if _bpy.app.debug:
-        print("\tbpy.utils.addon_enable", mod.__name__)
-
-    return mod
-
-
 def addon_disable(module_name, default_set=True):
     """
     Disables an addon by name.
