[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [55920] trunk/blender/release/scripts/ modules/bl_i18n_utils: Various edits preparing addons' translations tools ( not everything yet functionnal/tested, though).

Bastien Montagne montagne29 at wanadoo.fr
Tue Apr 9 10:56:36 CEST 2013


Revision: 55920
          http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=55920
Author:   mont29
Date:     2013-04-09 08:56:35 +0000 (Tue, 09 Apr 2013)
Log Message:
-----------
Various edits preparing addons' translations tools (not everything yet functionnal/tested, though).

Also workaround a nasty bug, where unregistered py classes remain listed in relevant __subclasses__() calls, which would lead to crash with python addons i18n tools (main translation was not affected, as messages extracting tools are executed in a brand new "factory startup" Blender ;) ).

Modified Paths:
--------------
    trunk/blender/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py
    trunk/blender/release/scripts/modules/bl_i18n_utils/utils.py

Modified: trunk/blender/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py
===================================================================
--- trunk/blender/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py	2013-04-09 05:02:10 UTC (rev 55919)
+++ trunk/blender/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py	2013-04-09 08:56:35 UTC (rev 55920)
@@ -62,6 +62,17 @@
         "spell_errors": {},
     }
 
+def _diff_check_ctxt(check_ctxt, minus_check_ctxt):
+    """Returns check_ctxt - minus_check_ctxt"""
+    for key in check_ctxt:
+        if isinstance(check_ctxt[key], set):
+            for warning in minus_check_ctxt[key]:
+                if warning in check_ctxt[key]:
+                    check_ctxt[key].remove(warning)
+        elif isinstance(check_ctxt[key], dict):
+            for warning in minus_check_ctxt[key]:
+                if warning in check_ctxt[key]:
+                    del check_ctxt[key][warning]
 
 def _gen_reports(check_ctxt):
     return {
@@ -176,45 +187,6 @@
             _print("\t\t{}".format("\n\t\t".join(pot.msgs[key].sources)))
 
 
-def enable_addons(addons={}, support={}, disable=False):
-    """
-    Enable (or disable) addons based either on a set of names, or a set of 'support' types.
-    Returns the list of all affected addons (as fake modules)!
-    """
-    import addon_utils
-
-    userpref = bpy.context.user_preferences
-    used_ext = {ext.module for ext in userpref.addons}
-
-    ret = [mod for mod in addon_utils.modules(addon_utils.addons_fake_modules)
-               if ((addons and mod.__name__ in addons) or
-                   (not addons and addon_utils.module_bl_info(mod)["support"] in support))]
-
-    for mod in ret:
-        module_name = mod.__name__
-        if disable:
-            if module_name not in used_ext:
-                continue
-            print("    Disabling module ", module_name)
-            bpy.ops.wm.addon_disable(module=module_name)
-        else:
-            if module_name in used_ext:
-                continue
-            print("    Enabling module ", module_name)
-            bpy.ops.wm.addon_enable(module=module_name)
-
-    # XXX There are currently some problems with bpy/rna...
-    #     *Very* tricky to solve!
-    #     So this is a hack to make all newly added operator visible by
-    #     bpy.types.OperatorProperties.__subclasses__()
-    for cat in dir(bpy.ops):
-        cat = getattr(bpy.ops, cat)
-        for op in dir(cat):
-            getattr(cat, op).get_rna()
-
-    return ret
-
-
 def process_msg(msgs, msgctxt, msgid, msgsrc, reports, check_ctxt, settings):
     if filter_message(msgid):
         reports["messages_skipped"].add((msgid, msgsrc))
@@ -235,51 +207,73 @@
 
 
 ##### RNA #####
-def dump_messages_rna(msgs, reports, settings):
+def dump_rna_messages(msgs, reports, settings):
     """
     Dump into messages dict all RNA-defined UI messages (labels en tooltips).
     """
     def class_blacklist():
-        blacklist_rna_class = [
-            # core classes
-            "Context", "Event", "Function", "UILayout", "UnknownType",
-            # registerable classes
-            "Panel", "Menu", "Header", "RenderEngine", "Operator", "OperatorMacro", "Macro", "KeyingSetInfo",
-            # window classes
-            "Window",
-        ]
+        blacklist_rna_class = {getattr(bpy.types, cls_id) for cls_id in (
+                # core classes
+                "Context", "Event", "Function", "UILayout", "UnknownType", "Property", "Struct",
+                # registerable classes
+                "Panel", "Menu", "Header", "RenderEngine", "Operator", "OperatorMacro", "Macro", "KeyingSetInfo",
+                # window classes
+                "Window",
+            )
+        }
 
-        # Collect internal operators
-        # extend with all internal operators
-        # note that this uses internal api introspection functions
-        # all possible operator names
-        op_ids = set(cls.bl_rna.identifier for cls in bpy.types.OperatorProperties.__subclasses__()) | \
-                 set(cls.bl_rna.identifier for cls in bpy.types.Operator.__subclasses__()) | \
-                 set(cls.bl_rna.identifier for cls in bpy.types.OperatorMacro.__subclasses__())
+        # More builtin classes we don't need to parse.
+        blacklist_rna_class |= {cls for cls in bpy.types.Property.__subclasses__()}
 
-        get_instance = __import__("_bpy").ops.get_instance
-#        path_resolve = type(bpy.context).__base__.path_resolve
-        for idname in op_ids:
-            op = get_instance(idname)
-            # XXX Do not skip INTERNAL's anymore, some of those ops show up in UI now!
-#            if 'INTERNAL' in path_resolve(op, "bl_options"):
-#                blacklist_rna_class.append(idname)
+        _rna = {getattr(bpy.types, cls) for cls in dir(bpy.types)}
 
-        # Collect builtin classes we don't need to doc
-        blacklist_rna_class.append("Property")
-        blacklist_rna_class.extend([cls.__name__ for cls in bpy.types.Property.__subclasses__()])
-
-        # Collect classes which are attached to collections, these are api access only.
-        collection_props = set()
-        for cls_id in dir(bpy.types):
-            cls = getattr(bpy.types, cls_id)
+        # Classes which are attached to collections can be skipped too, these are api access only.
+        for cls in _rna:
             for prop in cls.bl_rna.properties:
                 if prop.type == 'COLLECTION':
                     prop_cls = prop.srna
                     if prop_cls is not None:
-                        collection_props.add(prop_cls.identifier)
-        blacklist_rna_class.extend(sorted(collection_props))
+                        blacklist_rna_class.add(prop_cls.__class__)
 
+        # Now here is the *ugly* hack!
+        # Unfortunately, all classes we want to access are not available from bpy.types (OperatorProperties subclasses
+        # are not here, as they have the same name as matching Operator ones :( ). So we use __subclasses__() calls
+        # to walk through all rna hierachy.
+        # But unregistered classes remain listed by relevant __subclasses__() calls (be it a Py or BPY/RNA bug),
+        # and obviously the matching RNA struct exists no more, so trying to access their data (even the identifier)
+        # quickly leads to segfault!
+        # To address this, we have to blacklist classes which __name__ does not match any __name__ from bpy.types
+        # (we can't use only RNA identifiers, as some py-defined classes has a different name that rna id,
+        # and we can't use class object themselves, because OperatorProperties subclasses are not in bpy.types!)...
+
+        _rna_clss_ids = {cls.__name__ for cls in _rna} | {cls.bl_rna.identifier for cls in _rna}
+
+        # All registrable types.
+        blacklist_rna_class |= {cls for cls in bpy.types.OperatorProperties.__subclasses__() +
+                                               bpy.types.Operator.__subclasses__() +
+                                               bpy.types.OperatorMacro.__subclasses__() +
+                                               bpy.types.Header.__subclasses__() +
+                                               bpy.types.Panel.__subclasses__() +
+                                               bpy.types.Menu.__subclasses__() +
+                                               bpy.types.UIList.__subclasses__()
+                                    if cls.__name__ not in _rna_clss_ids}        
+
+        # Collect internal operators
+        # extend with all internal operators
+        # note that this uses internal api introspection functions
+        # XXX Do not skip INTERNAL's anymore, some of those ops show up in UI now!
+        # all possible operator names
+        #op_ids = (set(cls.bl_rna.identifier for cls in bpy.types.OperatorProperties.__subclasses__()) |
+                  #set(cls.bl_rna.identifier for cls in bpy.types.Operator.__subclasses__()) | 
+                  #set(cls.bl_rna.identifier for cls in bpy.types.OperatorMacro.__subclasses__()))
+
+        #get_instance = __import__("_bpy").ops.get_instance
+        #path_resolve = type(bpy.context).__base__.path_resolve
+        #for idname in op_ids:
+            #op = get_instance(idname)
+            #if 'INTERNAL' in path_resolve(op, "bl_options"):
+                #blacklist_rna_class.add(idname)
+
         return blacklist_rna_class
 
     check_ctxt_rna = check_ctxt_rna_tip = None
@@ -337,17 +331,6 @@
 
     def walk_class(cls):
         bl_rna = cls.bl_rna
-        reports["rna_structs"].append(cls)
-        if bl_rna.identifier in blacklist_rna_class:
-            reports["rna_structs_skipped"].append(cls)
-            return
-
-        # XXX translation_context of Operator sub-classes are not "good"!
-        #     So ignore those Operator sub-classes (anyway, will get the same from OperatorProperties sub-classes!)...
-        if issubclass(cls, bpy.types.Operator):
-            reports["rna_structs_skipped"].append(cls)
-            return
-
         msgsrc = "bpy.types." + bl_rna.identifier
         msgctxt = bl_rna.translation_context or default_context
 
@@ -388,7 +371,12 @@
 
         cls_list.sort(key=full_class_id)
         for cls in cls_list:
-            walk_class(cls)
+            reports["rna_structs"].append(cls)
+            # Ignore those Operator sub-classes (anyway, will get the same from OperatorProperties sub-classes!)...
+            if (cls in blacklist_rna_class) or issubclass(cls, bpy.types.Operator):
+                reports["rna_structs_skipped"].append(cls)
+            else:
+                walk_class(cls)
             # Recursively process subclasses.
             process_cls_list(cls.__subclasses__())
 
@@ -796,14 +784,14 @@
 
     # Enable all wanted addons.
     # For now, enable all official addons, before extracting msgids.
-    addons = enable_addons(support={"OFFICIAL"})
+    addons = utils.enable_addons(support={"OFFICIAL"})
     # Note this is not needed if we have been started with factory settings, but just in case...
-    enable_addons(support={"COMMUNITY", "TESTING"}, disable=True)
+    utils.enable_addons(support={"COMMUNITY", "TESTING"}, disable=True)
 
     reports = _gen_reports(_gen_check_ctxt(settings) if do_checks else None)
 
     # Get strings from RNA.
-    dump_messages_rna(msgs, reports, settings)

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list