def keyconfig_test(kc):

    def testEntry(kc, entry, src=None, parent=None):
        result = False

        idname, spaceid, regionid, children = entry

        km = kc.keymaps.find(idname, space_type=spaceid, region_type=regionid)

        if km:
            km = km.active()
            is_modal = km.is_modal

            if src:
                for item in km.keymap_items:
                    if src.compare(item):
                        print("===========")
                        print(parent.name)
                        print(_kmistr(src, is_modal).strip())
                        print(km.name)
                        print(_kmistr(item, is_modal).strip())
                        result = True

                for child in children:
                    if testEntry(kc, child, src, parent):
                        result = True
            else:
                for i in range(len(km.keymap_items)):
                    src = km.keymap_items[i]

                    for child in children:
                        if testEntry(kc, child, src, km):
                            result = True

                    for j in range(len(km.keymap_items) - i - 1):
                        item = km.keymap_items[j + i + 1]
                        if src.compare(item):
                            print("===========")
                            print(km.name)
                            print(_kmistr(src, is_modal).strip())
                            print(_kmistr(item, is_modal).strip())
                            result = True

                for child in children:
                    if testEntry(kc, child):
                        result = True

        return result

    # -------------------------------------------------------------------------
    # Function body

    from bl_keymap_utils import keymap_hierarchy
    result = False
    for entry in keymap_hierarchy.generate():
        if testEntry(kc, entry):
            result = True
    return result
示例#2
0
def check_maps():
    maps = {}

    def fill_maps(seq):
        for km_name, km_space_type, km_region_type, km_sub in seq:
            maps[km_name] = (km_space_type, km_region_type)
            fill_maps(km_sub)

    fill_maps(keymap_hierarchy.generate())

    import bpy
    keyconf = bpy.context.window_manager.keyconfigs.active
    maps_bl = set(keyconf.keymaps.keys())
    maps_py = set(maps.keys())

    err = False
    # Check keyconfig contains only maps that exist in blender
    test = maps_py - maps_bl
    if test:
        print(
            "Keymaps that are in 'bl_keymap_utils.keymap_hierarchy' but not blender"
        )
        for km_id in test:
            if callable(km_id):
                # Keymap functions of tools are not in blender anyway...
                continue
            print("\t%s" % km_id)
            # TODO T65963, broken keymap hierarchy tests disabled until fixed.
            # err = True

    test = maps_bl - maps_py
    if test:
        print(
            "Keymaps that are in blender but not in 'bl_keymap_utils.keymap_hierarchy'"
        )
        for km_id in test:
            km = keyconf.keymaps[km_id]
            print("    ('%s', '%s', '%s', [])," %
                  (km_id, km.space_type, km.region_type))
        # TODO T65963, broken keymap hierarchy tests disabled until fixed.
        # err = True

    # Check space/region's are OK
    print("Comparing keymap space/region types...")
    for km_id, km in keyconf.keymaps.items():
        km_py = maps.get(km_id)
        if km_py is not None:
            km_space_type, km_region_type = km_py
            if km_space_type != km.space_type or km_region_type != km.region_type:
                print("  Error:")
                print("    expected -- ('%s', '%s', '%s', [])," %
                      (km_id, km.space_type, km.region_type))
                print("    got      -- ('%s', '%s', '%s', [])," %
                      (km_id, km_space_type, km_region_type))
    print("done!")

    return err
示例#3
0
def get_keymap_dict(keyconfig):
    """
    get dictionary of key_name(str) and path_to_config_file(str)
    Args:
        keyconfig (bpy.types.KeyConfig):
    Returns:
        dict: keymap dict to display
    """
    keymaps = [(km, keyconfig) for km in keyconfig.keymaps]
    keymap_dict = {}
    level = 0
    for entry in keymap_hierarchy.generate():
        add_entry_to_dict(keymaps, entry, keymap_dict, level)
    return keymap_dict
def check_maps():
    maps = {}

    def fill_maps(seq):
        for km_name, km_space_type, km_region_type, km_sub in seq:
            maps[km_name] = (km_space_type, km_region_type)
            fill_maps(km_sub)

    fill_maps(keymap_hierarchy.generate())

    import bpy
    keyconf = bpy.context.window_manager.keyconfigs.active
    maps_bl = set(keyconf.keymaps.keys())
    maps_py = set(maps.keys())

    err = False
    # Check keyconfig contains only maps that exist in blender
    test = maps_py - maps_bl
    if test:
        print("Keymaps that are in 'bl_keymap_utils.keymap_hierarchy' but not blender")
        for km_id in sorted(test):
            print("\t%s" % km_id)
        err = True

    test = maps_bl - maps_py
    if test:
        print("Keymaps that are in blender but not in 'bl_keymap_utils.keymap_hierarchy'")
        for km_id in sorted(test):
            km = keyconf.keymaps[km_id]
            print("    ('%s', '%s', '%s', [])," % (km_id, km.space_type, km.region_type))
        err = True

    # Check space/region's are OK
    print("Comparing keymap space/region types...")
    for km_id, km in keyconf.keymaps.items():
        km_py = maps.get(km_id)
        if km_py is not None:
            km_space_type, km_region_type = km_py
            if km_space_type != km.space_type or km_region_type != km.region_type:
                print("  Error:")
                print("    expected -- ('%s', '%s', '%s', [])," % (km_id, km.space_type, km.region_type))
                print("    got      -- ('%s', '%s', '%s', [])," % (km_id, km_space_type, km_region_type))
    print("done!")

    return err
示例#5
0
def dump_rna_messages(msgs, reports, settings, verbose=False):
    """
    Dump into messages dict all RNA-defined UI messages (labels en tooltips).
    """
    def class_blacklist():
        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",
            )
        }

        # More builtin classes we don't need to parse.
        blacklist_rna_class |= {
            cls
            for cls in bpy.types.Property.__subclasses__()
        }

        return blacklist_rna_class

    check_ctxt_rna = check_ctxt_rna_tip = None
    check_ctxt = reports["check_ctxt"]
    if check_ctxt:
        check_ctxt_rna = {
            "multi_lines": check_ctxt.get("multi_lines"),
            "not_capitalized": check_ctxt.get("not_capitalized"),
            "end_point": check_ctxt.get("end_point"),
            "undoc_ops": check_ctxt.get("undoc_ops"),
            "spell_checker": check_ctxt.get("spell_checker"),
            "spell_errors": check_ctxt.get("spell_errors"),
        }
        check_ctxt_rna_tip = check_ctxt_rna
        check_ctxt_rna_tip["multi_rnatip"] = check_ctxt.get("multi_rnatip")

    default_context = settings.DEFAULT_CONTEXT

    # Function definitions
    def walk_properties(cls):
        # This handles properties whose name is the same as their identifier.
        # Usually, it means that those are internal properties not exposed in the UI, however there are some cases
        # where the UI label is actually defined and same as the identifier (color spaces e.g., `RGB` etc.).
        # So we only exclude those properties in case they belong to an operator for now.
        def prop_name_validate(cls, prop_name, prop_identifier):
            if prop_name != prop_identifier:
                return True
            # Heuristic: A lot of operator's HIDDEN properties have no UI label/description.
            # While this is not ideal (for API doc purposes, description should always be provided),
            # for now skip those properties.
            # NOTE: keep in sync with C code in ui_searchbox_region_draw_cb__operator().
            if issubclass(
                    cls,
                    bpy.types.OperatorProperties) and "_OT_" in cls.__name__:
                return False
            # Heuristic: If UI label is not capitalized, it is likely a private (undocumented) property,
            # that can be skipped.
            if prop_name and not prop_name[0].isupper():
                return False
            return True

        bl_rna = cls.bl_rna
        # Get our parents' properties, to not export them multiple times.
        bl_rna_base = bl_rna.base
        bl_rna_base_props = set()
        if bl_rna_base:
            bl_rna_base_props |= set(bl_rna_base.properties.values())
        for cls_base in cls.__bases__:
            bl_rna_base = getattr(cls_base, "bl_rna", None)
            if not bl_rna_base:
                continue
            bl_rna_base_props |= set(bl_rna_base.properties.values())

        props = sorted(bl_rna.properties, key=lambda p: p.identifier)
        for prop in props:
            # Only write this property if our parent hasn't got it.
            if prop in bl_rna_base_props:
                continue
            if prop.identifier in {"rna_type", "bl_icon", "icon"}:
                continue
            reports["rna_props"].append((cls, prop))

            msgsrc = "bpy.types.{}.{}".format(bl_rna.identifier,
                                              prop.identifier)
            msgctxt = prop.translation_context or default_context

            if prop.name and prop_name_validate(cls, prop.name,
                                                prop.identifier):
                process_msg(msgs, msgctxt, prop.name, msgsrc, reports,
                            check_ctxt_rna, settings)
            if prop.description:
                process_msg(msgs, default_context, prop.description, msgsrc,
                            reports, check_ctxt_rna_tip, settings)

            if isinstance(prop, bpy.types.EnumProperty):
                done_items = set()
                for item in prop.enum_items:
                    msgsrc = "bpy.types.{}.{}:'{}'".format(
                        bl_rna.identifier, prop.identifier, item.identifier)
                    done_items.add(item.identifier)
                    if item.name and prop_name_validate(
                            cls, item.name, item.identifier):
                        process_msg(msgs, msgctxt, item.name, msgsrc, reports,
                                    check_ctxt_rna, settings)
                    if item.description:
                        process_msg(msgs, default_context, item.description,
                                    msgsrc, reports, check_ctxt_rna_tip,
                                    settings)
                for item in prop.enum_items_static:
                    if item.identifier in done_items:
                        continue
                    msgsrc = "bpy.types.{}.{}:'{}'".format(
                        bl_rna.identifier, prop.identifier, item.identifier)
                    done_items.add(item.identifier)
                    if item.name and prop_name_validate(
                            cls, item.name, item.identifier):
                        process_msg(msgs, msgctxt, item.name, msgsrc, reports,
                                    check_ctxt_rna, settings)
                    if item.description:
                        process_msg(msgs, default_context, item.description,
                                    msgsrc, reports, check_ctxt_rna_tip,
                                    settings)

    def walk_tools_definitions(cls):
        from bl_ui.space_toolsystem_common import ToolDef

        bl_rna = cls.bl_rna
        op_default_context = bpy.app.translations.contexts.operator_default

        def process_tooldef(tool_context, tool):
            if not isinstance(tool, ToolDef):
                if callable(tool):
                    for t in tool(None):
                        process_tooldef(tool_context, t)
                return
            msgsrc = "bpy.types.{} Tools: '{}', '{}'".format(
                bl_rna.identifier, tool_context, tool.idname)
            if tool.label:
                process_msg(msgs, op_default_context, tool.label, msgsrc,
                            reports, check_ctxt_rna, settings)
            # Callable (function) descriptions must handle their translations themselves.
            if tool.description and not callable(tool.description):
                process_msg(msgs, default_context, tool.description, msgsrc,
                            reports, check_ctxt_rna_tip, settings)

        for tool_context, tools_defs in cls.tools_all():
            for tools_group in tools_defs:
                if tools_group is None:
                    continue
                elif isinstance(tools_group, tuple) and not isinstance(
                        tools_group, ToolDef):
                    for tool in tools_group:
                        process_tooldef(tool_context, tool)
                else:
                    process_tooldef(tool_context, tools_group)

    blacklist_rna_class = class_blacklist()

    def walk_class(cls):
        bl_rna = cls.bl_rna
        msgsrc = "bpy.types." + bl_rna.identifier
        msgctxt = bl_rna.translation_context or default_context

        if bl_rna.name and (bl_rna.name != bl_rna.identifier or
                            (msgctxt != default_context
                             and not hasattr(bl_rna, 'bl_label'))):
            process_msg(msgs, msgctxt, bl_rna.name, msgsrc, reports,
                        check_ctxt_rna, settings)

        if bl_rna.description:
            process_msg(msgs, default_context, bl_rna.description, msgsrc,
                        reports, check_ctxt_rna_tip, settings)
        elif cls.__doc__:  # XXX Some classes (like KeyingSetInfo subclasses) have void description... :(
            process_msg(msgs, default_context, cls.__doc__, msgsrc, reports,
                        check_ctxt_rna_tip, settings)

        # Panels' "tabs" system.
        if hasattr(bl_rna, 'bl_category') and bl_rna.bl_category:
            process_msg(msgs, default_context, bl_rna.bl_category, msgsrc,
                        reports, check_ctxt_rna, settings)

        if hasattr(bl_rna, 'bl_label') and bl_rna.bl_label:
            process_msg(msgs, msgctxt, bl_rna.bl_label, msgsrc, reports,
                        check_ctxt_rna, settings)

        # Tools Panels definitions.
        if hasattr(bl_rna, 'tools_all') and bl_rna.tools_all:
            walk_tools_definitions(cls)

        walk_properties(cls)

    def walk_keymap_hierarchy(hier, msgsrc_prev):
        km_i18n_context = bpy.app.translations.contexts.id_windowmanager
        for lvl in hier:
            msgsrc = msgsrc_prev + "." + lvl[1]
            if isinstance(
                    lvl[0],
                    str):  # Can be a function too, now, with tool system...
                process_msg(msgs, km_i18n_context, lvl[0], msgsrc, reports,
                            None, settings)
            if lvl[3]:
                walk_keymap_hierarchy(lvl[3], msgsrc)

    # Dump Messages
    operator_categories = {}

    def process_cls_list(cls_list):
        if not cls_list:
            return

        def full_class_id(cls):
            """Gives us 'ID.Light.AreaLight' which is best for sorting."""
            # Always the same issue, some classes listed in blacklist should actually no more exist (they have been
            # unregistered), but are still listed by __subclasses__() calls... :/
            if cls in blacklist_rna_class:
                return cls.__name__
            cls_id = ""
            bl_rna = getattr(cls, "bl_rna", None)
            # It seems that py-defined 'wrappers' RNA classes (like `MeshEdge` in `bpy_types.py`) need to be accessed
            # once from `bpy.types` before they have a valid `bl_rna` member.
            # Weirdly enough, this is only triggered on release builds, debug builds somehow do not have that issue.
            if bl_rna is None:
                if getattr(bpy.types, cls.__name__, None) is not None:
                    bl_rna = getattr(cls, "bl_rna", None)
                if bl_rna is None:
                    raise TypeError("Unknown RNA class")
            while bl_rna:
                cls_id = bl_rna.identifier + "." + cls_id
                bl_rna = bl_rna.base
            return cls_id

        def operator_category(cls):
            """Extract operators' categories, as displayed in 'search' space menu."""
            # NOTE: keep in sync with C code in ui_searchbox_region_draw_cb__operator().
            if issubclass(
                    cls,
                    bpy.types.OperatorProperties) and "_OT_" in cls.__name__:
                cat_id = cls.__name__.split("_OT_")[0]
                if cat_id not in operator_categories:
                    cat_str = cat_id.capitalize() + ":"
                    operator_categories[cat_id] = cat_str

        if verbose:
            print(cls_list)
        cls_list.sort(key=full_class_id)
        for cls in cls_list:
            if verbose:
                print(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:
                operator_category(cls)
                walk_class(cls)
            # Recursively process subclasses.
            process_cls_list(cls.__subclasses__())

    # FIXME Workaround weird new (blender 3.2) issue where some classes (like `bpy.types.Modifier`)
    # are not listed by `bpy.types.ID.__base__.__subclasses__()` until they are accessed from
    # `bpy.types` (eg just executing `bpy.types.Modifier`).
    cls_dir = dir(bpy.types)
    for cls_name in cls_dir:
        getattr(bpy.types, cls_name)

    # Parse everything (recursively parsing from bpy_struct "class"...).
    process_cls_list(bpy.types.ID.__base__.__subclasses__())

    # Finalize generated 'operator categories' messages.
    for cat_str in operator_categories.values():
        process_msg(msgs, bpy.app.translations.contexts.operator_default,
                    cat_str, "Generated operator category", reports,
                    check_ctxt_rna, settings)

    # And parse keymaps!
    from bl_keymap_utils import keymap_hierarchy
    walk_keymap_hierarchy(keymap_hierarchy.generate(), "KM_HIERARCHY")
示例#6
0
def draw_hierarchy(display_keymaps, layout):
    from bl_keymap_utils import keymap_hierarchy
    for entry in keymap_hierarchy.generate():
        draw_entry(display_keymaps, entry, layout)
示例#7
0
def dump_rna_messages(msgs, reports, settings, verbose=False):
    """
    Dump into messages dict all RNA-defined UI messages (labels en tooltips).
    """
    def class_blacklist():
        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",
        )
        }

        # More builtin classes we don't need to parse.
        blacklist_rna_class |= {cls for cls in bpy.types.Property.__subclasses__()}

        # None of this seems needed anymore, and it's broken anyway with current master (blender 2.79.1)...
        """
        _rna = {getattr(bpy.types, cls) for cls in dir(bpy.types)}

        # Classes which are attached to collections can be skipped too, these are api access only.
        # XXX This is not true, some of those show in UI, see e.g. tooltip of KeyingSets.active...
        #~ 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:
                        #~ 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 hierarchy.
        # 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
    check_ctxt = reports["check_ctxt"]
    if check_ctxt:
        check_ctxt_rna = {
            "multi_lines": check_ctxt.get("multi_lines"),
            "not_capitalized": check_ctxt.get("not_capitalized"),
            "end_point": check_ctxt.get("end_point"),
            "undoc_ops": check_ctxt.get("undoc_ops"),
            "spell_checker": check_ctxt.get("spell_checker"),
            "spell_errors": check_ctxt.get("spell_errors"),
        }
        check_ctxt_rna_tip = check_ctxt_rna
        check_ctxt_rna_tip["multi_rnatip"] = check_ctxt.get("multi_rnatip")

    default_context = settings.DEFAULT_CONTEXT

    # Function definitions
    def walk_properties(cls):
        bl_rna = cls.bl_rna
        # Get our parents' properties, to not export them multiple times.
        bl_rna_base = bl_rna.base
        if bl_rna_base:
            bl_rna_base_props = set(bl_rna_base.properties.values())
        else:
            bl_rna_base_props = set()

        props = sorted(bl_rna.properties, key=lambda p: p.identifier)
        for prop in props:
            # Only write this property if our parent hasn't got it.
            if prop in bl_rna_base_props:
                continue
            if prop.identifier == "rna_type":
                continue
            reports["rna_props"].append((cls, prop))

            msgsrc = "bpy.types.{}.{}".format(bl_rna.identifier, prop.identifier)
            msgctxt = prop.translation_context or default_context

            if prop.name and (prop.name != prop.identifier or msgctxt != default_context):
                process_msg(msgs, msgctxt, prop.name, msgsrc, reports, check_ctxt_rna, settings)
            if prop.description:
                process_msg(msgs, default_context, prop.description, msgsrc, reports, check_ctxt_rna_tip, settings)

            if isinstance(prop, bpy.types.EnumProperty):
                done_items = set()
                for item in prop.enum_items:
                    msgsrc = "bpy.types.{}.{}:'{}'".format(bl_rna.identifier, prop.identifier, item.identifier)
                    done_items.add(item.identifier)
                    if item.name and item.name != item.identifier:
                        process_msg(msgs, msgctxt, item.name, msgsrc, reports, check_ctxt_rna, settings)
                    if item.description:
                        process_msg(msgs, default_context, item.description, msgsrc, reports, check_ctxt_rna_tip,
                                    settings)
                for item in prop.enum_items_static:
                    if item.identifier in done_items:
                        continue
                    msgsrc = "bpy.types.{}.{}:'{}'".format(bl_rna.identifier, prop.identifier, item.identifier)
                    done_items.add(item.identifier)
                    if item.name and item.name != item.identifier:
                        process_msg(msgs, msgctxt, item.name, msgsrc, reports, check_ctxt_rna, settings)
                    if item.description:
                        process_msg(msgs, default_context, item.description, msgsrc, reports, check_ctxt_rna_tip,
                                    settings)

    def walk_tools_definitions(cls):
        from bl_ui.space_toolsystem_common import ToolDef

        bl_rna = cls.bl_rna
        op_default_context = bpy.app.translations.contexts.operator_default

        def process_tooldef(tool_context, tool):
            if not isinstance(tool, ToolDef):
                if callable(tool):
                    for t in tool(None):
                        process_tooldef(tool_context, t)
                return
            msgsrc = "bpy.types.{} Tools: '{}', '{}'".format(bl_rna.identifier, tool_context, tool.idname)
            if tool.label:
                process_msg(msgs, op_default_context, tool.label, msgsrc, reports, check_ctxt_rna, settings)
            # Callable (function) descriptions must handle their translations themselves.
            if tool.description and not callable(tool.description):
                process_msg(msgs, default_context, tool.description, msgsrc, reports, check_ctxt_rna_tip, settings)

        for tool_context, tools_defs in cls.tools_all():
            for tools_group in tools_defs:
                if tools_group is None:
                    continue
                elif isinstance(tools_group, tuple) and not isinstance(tools_group, ToolDef):
                    for tool in tools_group:
                        process_tooldef(tool_context, tool)
                else:
                    process_tooldef(tool_context, tools_group)

    blacklist_rna_class = class_blacklist()

    def walk_class(cls):
        bl_rna = cls.bl_rna
        msgsrc = "bpy.types." + bl_rna.identifier
        msgctxt = bl_rna.translation_context or default_context

        if bl_rna.name and (bl_rna.name != bl_rna.identifier or msgctxt != default_context):
            process_msg(msgs, msgctxt, bl_rna.name, msgsrc, reports, check_ctxt_rna, settings)

        if bl_rna.description:
            process_msg(msgs, default_context, bl_rna.description, msgsrc, reports, check_ctxt_rna_tip, settings)
        elif cls.__doc__:  # XXX Some classes (like KeyingSetInfo subclasses) have void description... :(
            process_msg(msgs, default_context, cls.__doc__, msgsrc, reports, check_ctxt_rna_tip, settings)

        # Panels' "tabs" system.
        if hasattr(bl_rna, 'bl_category') and bl_rna.bl_category:
            process_msg(msgs, default_context, bl_rna.bl_category, msgsrc, reports, check_ctxt_rna, settings)

        if hasattr(bl_rna, 'bl_label') and bl_rna.bl_label:
            process_msg(msgs, msgctxt, bl_rna.bl_label, msgsrc, reports, check_ctxt_rna, settings)

        # Tools Panels definitions.
        if hasattr(bl_rna, 'tools_all') and bl_rna.tools_all:
            walk_tools_definitions(cls)

        walk_properties(cls)

    def walk_keymap_hierarchy(hier, msgsrc_prev):
        km_i18n_context = bpy.app.translations.contexts.id_windowmanager
        for lvl in hier:
            msgsrc = msgsrc_prev + "." + lvl[1]
            if isinstance(lvl[0], str):  # Can be a function too, now, with tool system...
                process_msg(msgs, km_i18n_context, lvl[0], msgsrc, reports, None, settings)
            if lvl[3]:
                walk_keymap_hierarchy(lvl[3], msgsrc)

    # Dump Messages
    operator_categories = {}

    def process_cls_list(cls_list):
        if not cls_list:
            return

        def full_class_id(cls):
            """Gives us 'ID.Light.AreaLight' which is best for sorting."""
            # Always the same issue, some classes listed in blacklist should actually no more exist (they have been
            # unregistered), but are still listed by __subclasses__() calls... :/
            if cls in blacklist_rna_class:
                return cls.__name__
            cls_id = ""
            bl_rna = cls.bl_rna
            while bl_rna:
                cls_id = bl_rna.identifier + "." + cls_id
                bl_rna = bl_rna.base
            return cls_id

        def operator_category(cls):
            """Extract operators' categories, as displayed in 'search' space menu."""
            # NOTE: keep in sync with C code in ui_searchbox_region_draw_cb__operator().
            if issubclass(cls, bpy.types.OperatorProperties) and "_OT_" in cls.__name__:
                cat_id = cls.__name__.split("_OT_")[0]
                if cat_id not in operator_categories:
                    cat_str = cat_id.capitalize() + ":"
                    operator_categories[cat_id] = cat_str

        if verbose:
            print(cls_list)
        cls_list.sort(key=full_class_id)
        for cls in cls_list:
            if verbose:
                print(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:
                operator_category(cls)
                walk_class(cls)
            # Recursively process subclasses.
            process_cls_list(cls.__subclasses__())

    # Parse everything (recursively parsing from bpy_struct "class"...).
    process_cls_list(bpy.types.ID.__base__.__subclasses__())

    # Finalize generated 'operator categories' messages.
    for cat_str in operator_categories.values():
        process_msg(msgs, bpy.app.translations.contexts.operator_default, cat_str, "Generated operator category",
                    reports, check_ctxt_rna, settings)

    # And parse keymaps!
    from bl_keymap_utils import keymap_hierarchy
    walk_keymap_hierarchy(keymap_hierarchy.generate(), "KM_HIERARCHY")
def dump_rna_messages(msgs, reports, settings, verbose=False):
    """
    Dump into messages dict all RNA-defined UI messages (labels en tooltips).
    """
    def class_blacklist():
        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",
            )
        }

        # More builtin classes we don't need to parse.
        blacklist_rna_class |= {
            cls
            for cls in bpy.types.Property.__subclasses__()
        }

        # None of this seems needed anymore, and it's broken anyway with current master (blender 2.79.1)...
        """
        _rna = {getattr(bpy.types, cls) for cls in dir(bpy.types)}

        # Classes which are attached to collections can be skipped too, these are api access only.
        # XXX This is not true, some of those show in UI, see e.g. tooltip of KeyingSets.active...
        #~ 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:
                        #~ 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 hierarchy.
        # 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
    check_ctxt = reports["check_ctxt"]
    if check_ctxt:
        check_ctxt_rna = {
            "multi_lines": check_ctxt.get("multi_lines"),
            "not_capitalized": check_ctxt.get("not_capitalized"),
            "end_point": check_ctxt.get("end_point"),
            "undoc_ops": check_ctxt.get("undoc_ops"),
            "spell_checker": check_ctxt.get("spell_checker"),
            "spell_errors": check_ctxt.get("spell_errors"),
        }
        check_ctxt_rna_tip = check_ctxt_rna
        check_ctxt_rna_tip["multi_rnatip"] = check_ctxt.get("multi_rnatip")

    default_context = settings.DEFAULT_CONTEXT

    # Function definitions
    def walk_properties(cls):
        bl_rna = cls.bl_rna
        # Get our parents' properties, to not export them multiple times.
        bl_rna_base = bl_rna.base
        if bl_rna_base:
            bl_rna_base_props = set(bl_rna_base.properties.values())
        else:
            bl_rna_base_props = set()

        props = sorted(bl_rna.properties, key=lambda p: p.identifier)
        for prop in props:
            # Only write this property if our parent hasn't got it.
            if prop in bl_rna_base_props:
                continue
            if prop.identifier == "rna_type":
                continue
            reports["rna_props"].append((cls, prop))

            msgsrc = "bpy.types.{}.{}".format(bl_rna.identifier,
                                              prop.identifier)
            msgctxt = prop.translation_context or default_context

            if prop.name and (prop.name != prop.identifier
                              or msgctxt != default_context):
                process_msg(msgs, msgctxt, prop.name, msgsrc, reports,
                            check_ctxt_rna, settings)
            if prop.description:
                process_msg(msgs, default_context, prop.description, msgsrc,
                            reports, check_ctxt_rna_tip, settings)

            if isinstance(prop, bpy.types.EnumProperty):
                done_items = set()
                for item in prop.enum_items:
                    msgsrc = "bpy.types.{}.{}:'{}'".format(
                        bl_rna.identifier, prop.identifier, item.identifier)
                    done_items.add(item.identifier)
                    if item.name and item.name != item.identifier:
                        process_msg(msgs, msgctxt, item.name, msgsrc, reports,
                                    check_ctxt_rna, settings)
                    if item.description:
                        process_msg(msgs, default_context, item.description,
                                    msgsrc, reports, check_ctxt_rna_tip,
                                    settings)
                for item in prop.enum_items_static:
                    if item.identifier in done_items:
                        continue
                    msgsrc = "bpy.types.{}.{}:'{}'".format(
                        bl_rna.identifier, prop.identifier, item.identifier)
                    done_items.add(item.identifier)
                    if item.name and item.name != item.identifier:
                        process_msg(msgs, msgctxt, item.name, msgsrc, reports,
                                    check_ctxt_rna, settings)
                    if item.description:
                        process_msg(msgs, default_context, item.description,
                                    msgsrc, reports, check_ctxt_rna_tip,
                                    settings)

    def walk_tools_definitions(cls):
        from bl_ui.space_toolsystem_common import ToolDef

        bl_rna = cls.bl_rna
        op_default_context = bpy.app.translations.contexts.operator_default

        def process_tooldef(tool_context, tool):
            if not isinstance(tool, ToolDef):
                if callable(tool):
                    for t in tool(None):
                        process_tooldef(tool_context, t)
                return
            msgsrc = "bpy.types.{} Tools: '{}', '{}'".format(
                bl_rna.identifier, tool_context, tool.idname)
            if tool.label:
                process_msg(msgs, op_default_context, tool.label, msgsrc,
                            reports, check_ctxt_rna, settings)
            # Callable (function) descriptions must handle their translations themselves.
            if tool.description and not callable(tool.description):
                process_msg(msgs, default_context, tool.description, msgsrc,
                            reports, check_ctxt_rna_tip, settings)

        for tool_context, tools_defs in cls.tools_all():
            for tools_group in tools_defs:
                if tools_group is None:
                    continue
                elif isinstance(tools_group, tuple) and not isinstance(
                        tools_group, ToolDef):
                    for tool in tools_group:
                        process_tooldef(tool_context, tool)
                else:
                    process_tooldef(tool_context, tools_group)

    blacklist_rna_class = class_blacklist()

    def walk_class(cls):
        bl_rna = cls.bl_rna
        msgsrc = "bpy.types." + bl_rna.identifier
        msgctxt = bl_rna.translation_context or default_context

        if bl_rna.name and (bl_rna.name != bl_rna.identifier
                            or msgctxt != default_context):
            process_msg(msgs, msgctxt, bl_rna.name, msgsrc, reports,
                        check_ctxt_rna, settings)

        if bl_rna.description:
            process_msg(msgs, default_context, bl_rna.description, msgsrc,
                        reports, check_ctxt_rna_tip, settings)
        elif cls.__doc__:  # XXX Some classes (like KeyingSetInfo subclasses) have void description... :(
            process_msg(msgs, default_context, cls.__doc__, msgsrc, reports,
                        check_ctxt_rna_tip, settings)

        # Panels' "tabs" system.
        if hasattr(bl_rna, 'bl_category') and bl_rna.bl_category:
            process_msg(msgs, default_context, bl_rna.bl_category, msgsrc,
                        reports, check_ctxt_rna, settings)

        if hasattr(bl_rna, 'bl_label') and bl_rna.bl_label:
            process_msg(msgs, msgctxt, bl_rna.bl_label, msgsrc, reports,
                        check_ctxt_rna, settings)

        # Tools Panels definitions.
        if hasattr(bl_rna, 'tools_all') and bl_rna.tools_all:
            walk_tools_definitions(cls)

        walk_properties(cls)

    def walk_keymap_hierarchy(hier, msgsrc_prev):
        km_i18n_context = bpy.app.translations.contexts.id_windowmanager
        for lvl in hier:
            msgsrc = msgsrc_prev + "." + lvl[1]
            if isinstance(
                    lvl[0],
                    str):  # Can be a function too, now, with tool system...
                process_msg(msgs, km_i18n_context, lvl[0], msgsrc, reports,
                            None, settings)
            if lvl[3]:
                walk_keymap_hierarchy(lvl[3], msgsrc)

    # Dump Messages
    operator_categories = {}

    def process_cls_list(cls_list):
        if not cls_list:
            return

        def full_class_id(cls):
            """Gives us 'ID.Light.AreaLight' which is best for sorting."""
            # Always the same issue, some classes listed in blacklist should actually no more exist (they have been
            # unregistered), but are still listed by __subclasses__() calls... :/
            if cls in blacklist_rna_class:
                return cls.__name__
            cls_id = ""
            bl_rna = cls.bl_rna
            while bl_rna:
                cls_id = bl_rna.identifier + "." + cls_id
                bl_rna = bl_rna.base
            return cls_id

        def operator_category(cls):
            """Extract operators' categories, as displayed in 'search' space menu."""
            # NOTE: keep in sync with C code in ui_searchbox_region_draw_cb__operator().
            if issubclass(
                    cls,
                    bpy.types.OperatorProperties) and "_OT_" in cls.__name__:
                cat_id = cls.__name__.split("_OT_")[0]
                if cat_id not in operator_categories:
                    cat_str = cat_id.capitalize() + ":"
                    operator_categories[cat_id] = cat_str

        if verbose:
            print(cls_list)
        cls_list.sort(key=full_class_id)
        for cls in cls_list:
            if verbose:
                print(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:
                operator_category(cls)
                walk_class(cls)
            # Recursively process subclasses.
            process_cls_list(cls.__subclasses__())

    # Parse everything (recursively parsing from bpy_struct "class"...).
    process_cls_list(bpy.types.ID.__base__.__subclasses__())

    # Finalize generated 'operator categories' messages.
    for cat_str in operator_categories.values():
        process_msg(msgs, bpy.app.translations.contexts.operator_default,
                    cat_str, "Generated operator category", reports,
                    check_ctxt_rna, settings)

    # And parse keymaps!
    from bl_keymap_utils import keymap_hierarchy
    walk_keymap_hierarchy(keymap_hierarchy.generate(), "KM_HIERARCHY")
示例#9
0
def draw_hierarchy(display_keymaps, layout):
    from bl_keymap_utils import keymap_hierarchy
    for entry in keymap_hierarchy.generate():
        draw_entry(display_keymaps, entry, layout)
示例#10
0
def dump_rna_messages(msgs, reports, settings, verbose=False):
    """
    Dump into messages dict all RNA-defined UI messages (labels en tooltips).
    """
    def class_blacklist():
        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",
            )
        }

        # More builtin classes we don't need to parse.
        blacklist_rna_class |= {
            cls
            for cls in bpy.types.Property.__subclasses__()
        }

        return blacklist_rna_class

    check_ctxt_rna = check_ctxt_rna_tip = None
    check_ctxt = reports["check_ctxt"]
    if check_ctxt:
        check_ctxt_rna = {
            "multi_lines": check_ctxt.get("multi_lines"),
            "not_capitalized": check_ctxt.get("not_capitalized"),
            "end_point": check_ctxt.get("end_point"),
            "undoc_ops": check_ctxt.get("undoc_ops"),
            "spell_checker": check_ctxt.get("spell_checker"),
            "spell_errors": check_ctxt.get("spell_errors"),
        }
        check_ctxt_rna_tip = check_ctxt_rna
        check_ctxt_rna_tip["multi_rnatip"] = check_ctxt.get("multi_rnatip")

    default_context = settings.DEFAULT_CONTEXT

    # Function definitions
    def walk_properties(cls):
        bl_rna = cls.bl_rna
        # Get our parents' properties, to not export them multiple times.
        bl_rna_base = bl_rna.base
        if bl_rna_base:
            bl_rna_base_props = set(bl_rna_base.properties.values())
        else:
            bl_rna_base_props = set()

        props = sorted(bl_rna.properties, key=lambda p: p.identifier)
        for prop in props:
            # Only write this property if our parent hasn't got it.
            if prop in bl_rna_base_props:
                continue
            if prop.identifier == "rna_type":
                continue
            reports["rna_props"].append((cls, prop))

            msgsrc = "bpy.types.{}.{}".format(bl_rna.identifier,
                                              prop.identifier)
            msgctxt = prop.translation_context or default_context

            if prop.name and (prop.name != prop.identifier
                              or msgctxt != default_context):
                process_msg(msgs, msgctxt, prop.name, msgsrc, reports,
                            check_ctxt_rna, settings)
            if prop.description:
                process_msg(msgs, default_context, prop.description, msgsrc,
                            reports, check_ctxt_rna_tip, settings)

            if isinstance(prop, bpy.types.EnumProperty):
                done_items = set()
                for item in prop.enum_items:
                    msgsrc = "bpy.types.{}.{}:'{}'".format(
                        bl_rna.identifier, prop.identifier, item.identifier)
                    done_items.add(item.identifier)
                    if item.name and item.name != item.identifier:
                        process_msg(msgs, msgctxt, item.name, msgsrc, reports,
                                    check_ctxt_rna, settings)
                    if item.description:
                        process_msg(msgs, default_context, item.description,
                                    msgsrc, reports, check_ctxt_rna_tip,
                                    settings)
                for item in prop.enum_items_static:
                    if item.identifier in done_items:
                        continue
                    msgsrc = "bpy.types.{}.{}:'{}'".format(
                        bl_rna.identifier, prop.identifier, item.identifier)
                    done_items.add(item.identifier)
                    if item.name and item.name != item.identifier:
                        process_msg(msgs, msgctxt, item.name, msgsrc, reports,
                                    check_ctxt_rna, settings)
                    if item.description:
                        process_msg(msgs, default_context, item.description,
                                    msgsrc, reports, check_ctxt_rna_tip,
                                    settings)

    def walk_tools_definitions(cls):
        from bl_ui.space_toolsystem_common import ToolDef

        bl_rna = cls.bl_rna
        op_default_context = bpy.app.translations.contexts.operator_default

        def process_tooldef(tool_context, tool):
            if not isinstance(tool, ToolDef):
                if callable(tool):
                    for t in tool(None):
                        process_tooldef(tool_context, t)
                return
            msgsrc = "bpy.types.{} Tools: '{}', '{}'".format(
                bl_rna.identifier, tool_context, tool.idname)
            if tool.label:
                process_msg(msgs, op_default_context, tool.label, msgsrc,
                            reports, check_ctxt_rna, settings)
            # Callable (function) descriptions must handle their translations themselves.
            if tool.description and not callable(tool.description):
                process_msg(msgs, default_context, tool.description, msgsrc,
                            reports, check_ctxt_rna_tip, settings)

        for tool_context, tools_defs in cls.tools_all():
            for tools_group in tools_defs:
                if tools_group is None:
                    continue
                elif isinstance(tools_group, tuple) and not isinstance(
                        tools_group, ToolDef):
                    for tool in tools_group:
                        process_tooldef(tool_context, tool)
                else:
                    process_tooldef(tool_context, tools_group)

    blacklist_rna_class = class_blacklist()

    def walk_class(cls):
        bl_rna = cls.bl_rna
        msgsrc = "bpy.types." + bl_rna.identifier
        msgctxt = bl_rna.translation_context or default_context

        if bl_rna.name and (bl_rna.name != bl_rna.identifier
                            or msgctxt != default_context):
            process_msg(msgs, msgctxt, bl_rna.name, msgsrc, reports,
                        check_ctxt_rna, settings)

        if bl_rna.description:
            process_msg(msgs, default_context, bl_rna.description, msgsrc,
                        reports, check_ctxt_rna_tip, settings)
        elif cls.__doc__:  # XXX Some classes (like KeyingSetInfo subclasses) have void description... :(
            process_msg(msgs, default_context, cls.__doc__, msgsrc, reports,
                        check_ctxt_rna_tip, settings)

        # Panels' "tabs" system.
        if hasattr(bl_rna, 'bl_category') and bl_rna.bl_category:
            process_msg(msgs, default_context, bl_rna.bl_category, msgsrc,
                        reports, check_ctxt_rna, settings)

        if hasattr(bl_rna, 'bl_label') and bl_rna.bl_label:
            process_msg(msgs, msgctxt, bl_rna.bl_label, msgsrc, reports,
                        check_ctxt_rna, settings)

        # Tools Panels definitions.
        if hasattr(bl_rna, 'tools_all') and bl_rna.tools_all:
            walk_tools_definitions(cls)

        walk_properties(cls)

    def walk_keymap_hierarchy(hier, msgsrc_prev):
        km_i18n_context = bpy.app.translations.contexts.id_windowmanager
        for lvl in hier:
            msgsrc = msgsrc_prev + "." + lvl[1]
            if isinstance(
                    lvl[0],
                    str):  # Can be a function too, now, with tool system...
                process_msg(msgs, km_i18n_context, lvl[0], msgsrc, reports,
                            None, settings)
            if lvl[3]:
                walk_keymap_hierarchy(lvl[3], msgsrc)

    # Dump Messages
    operator_categories = {}

    def process_cls_list(cls_list):
        if not cls_list:
            return

        def full_class_id(cls):
            """Gives us 'ID.Light.AreaLight' which is best for sorting."""
            # Always the same issue, some classes listed in blacklist should actually no more exist (they have been
            # unregistered), but are still listed by __subclasses__() calls... :/
            if cls in blacklist_rna_class:
                return cls.__name__
            cls_id = ""
            bl_rna = cls.bl_rna
            while bl_rna:
                cls_id = bl_rna.identifier + "." + cls_id
                bl_rna = bl_rna.base
            return cls_id

        def operator_category(cls):
            """Extract operators' categories, as displayed in 'search' space menu."""
            # NOTE: keep in sync with C code in ui_searchbox_region_draw_cb__operator().
            if issubclass(
                    cls,
                    bpy.types.OperatorProperties) and "_OT_" in cls.__name__:
                cat_id = cls.__name__.split("_OT_")[0]
                if cat_id not in operator_categories:
                    cat_str = cat_id.capitalize() + ":"
                    operator_categories[cat_id] = cat_str

        if verbose:
            print(cls_list)
        cls_list.sort(key=full_class_id)
        for cls in cls_list:
            if verbose:
                print(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:
                operator_category(cls)
                walk_class(cls)
            # Recursively process subclasses.
            process_cls_list(cls.__subclasses__())

    # Parse everything (recursively parsing from bpy_struct "class"...).
    process_cls_list(bpy.types.ID.__base__.__subclasses__())

    # Finalize generated 'operator categories' messages.
    for cat_str in operator_categories.values():
        process_msg(msgs, bpy.app.translations.contexts.operator_default,
                    cat_str, "Generated operator category", reports,
                    check_ctxt_rna, settings)

    # And parse keymaps!
    from bl_keymap_utils import keymap_hierarchy
    walk_keymap_hierarchy(keymap_hierarchy.generate(), "KM_HIERARCHY")