Skip to content

Instantly share code, notes, and snippets.

@JacquesLucke
Last active April 8, 2026 11:11
Show Gist options
  • Select an option

  • Save JacquesLucke/11fecc6ea86ef36ea72f76ca547e795b to your computer and use it in GitHub Desktop.

Select an option

Save JacquesLucke/11fecc6ea86ef36ea72f76ca547e795b to your computer and use it in GitHub Desktop.

Revisions

  1. JacquesLucke revised this gist Feb 27, 2021. 1 changed file with 3 additions and 2 deletions.
    5 changes: 3 additions & 2 deletions auto_load.py
    Original file line number Diff line number Diff line change
    @@ -94,7 +94,7 @@ def iter_my_deps_from_annotations(cls, my_classes):

    def get_dependency_from_annotation(value):
    if blender_version >= (2, 93):
    if type(value).__name__ == "bpy_prop_deferred":
    if isinstance(value, bpy.props._PropertyDeferred):
    return value.keywords.get("type")
    else:
    if isinstance(value, tuple) and len(value) == 2:
    @@ -134,7 +134,8 @@ def get_register_base_types():
    "Panel", "Operator", "PropertyGroup",
    "AddonPreferences", "Header", "Menu",
    "Node", "NodeSocket", "NodeTree",
    "UIList", "RenderEngine"
    "UIList", "RenderEngine",
    "Gizmo", "GizmoGroup",
    ])


  2. JacquesLucke revised this gist Feb 21, 2021. 1 changed file with 9 additions and 3 deletions.
    12 changes: 9 additions & 3 deletions auto_load.py
    Original file line number Diff line number Diff line change
    @@ -13,6 +13,8 @@
    "unregister",
    )

    blender_version = bpy.app.version

    modules = None
    ordered_classes = None

    @@ -91,9 +93,13 @@ def iter_my_deps_from_annotations(cls, my_classes):
    yield dependency

    def get_dependency_from_annotation(value):
    if isinstance(value, tuple) and len(value) == 2:
    if value[0] in (bpy.props.PointerProperty, bpy.props.CollectionProperty):
    return value[1]["type"]
    if blender_version >= (2, 93):
    if type(value).__name__ == "bpy_prop_deferred":
    return value.keywords.get("type")
    else:
    if isinstance(value, tuple) and len(value) == 2:
    if value[0] in (bpy.props.PointerProperty, bpy.props.CollectionProperty):
    return value[1]["type"]
    return None

    def iter_my_deps_from_parent_id(cls, my_classes_by_idname):
  3. JacquesLucke revised this gist Jan 12, 2020. 1 changed file with 20 additions and 8 deletions.
    28 changes: 20 additions & 8 deletions auto_load.py
    Original file line number Diff line number Diff line change
    @@ -71,28 +71,40 @@ def get_ordered_classes_to_register(modules):
    return toposort(get_register_deps_dict(modules))

    def get_register_deps_dict(modules):
    my_classes = set(iter_my_classes(modules))
    my_classes_by_idname = {cls.bl_idname : cls for cls in my_classes if hasattr(cls, "bl_idname")}

    deps_dict = {}
    classes_to_register = set(iter_classes_to_register(modules))
    for cls in classes_to_register:
    deps_dict[cls] = set(iter_own_register_deps(cls, classes_to_register))
    for cls in my_classes:
    deps_dict[cls] = set(iter_my_register_deps(cls, my_classes, my_classes_by_idname))
    return deps_dict

    def iter_own_register_deps(cls, own_classes):
    yield from (dep for dep in iter_register_deps(cls) if dep in own_classes)
    def iter_my_register_deps(cls, my_classes, my_classes_by_idname):
    yield from iter_my_deps_from_annotations(cls, my_classes)
    yield from iter_my_deps_from_parent_id(cls, my_classes_by_idname)

    def iter_register_deps(cls):
    def iter_my_deps_from_annotations(cls, my_classes):
    for value in typing.get_type_hints(cls, {}, {}).values():
    dependency = get_dependency_from_annotation(value)
    if dependency is not None:
    yield dependency
    if dependency in my_classes:
    yield dependency

    def get_dependency_from_annotation(value):
    if isinstance(value, tuple) and len(value) == 2:
    if value[0] in (bpy.props.PointerProperty, bpy.props.CollectionProperty):
    return value[1]["type"]
    return None

    def iter_classes_to_register(modules):
    def iter_my_deps_from_parent_id(cls, my_classes_by_idname):
    if bpy.types.Panel in cls.__bases__:
    parent_idname = getattr(cls, "bl_parent_id", None)
    if parent_idname is not None:
    parent_cls = my_classes_by_idname.get(parent_idname)
    if parent_cls is not None:
    yield parent_cls

    def iter_my_classes(modules):
    base_types = get_register_base_types()
    for cls in get_classes_in_modules(modules):
    if any(base in base_types for base in cls.__bases__):
  4. JacquesLucke revised this gist Dec 1, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion auto_load.py
    Original file line number Diff line number Diff line change
    @@ -116,7 +116,7 @@ def get_register_base_types():
    "Panel", "Operator", "PropertyGroup",
    "AddonPreferences", "Header", "Menu",
    "Node", "NodeSocket", "NodeTree",
    "UIList"
    "UIList", "RenderEngine"
    ])


  5. JacquesLucke revised this gist Dec 1, 2018. 1 changed file with 3 additions and 2 deletions.
    5 changes: 3 additions & 2 deletions auto_load.py
    Original file line number Diff line number Diff line change
    @@ -96,7 +96,8 @@ def iter_classes_to_register(modules):
    base_types = get_register_base_types()
    for cls in get_classes_in_modules(modules):
    if any(base in base_types for base in cls.__bases__):
    yield cls
    if not getattr(cls, "is_registered", False):
    yield cls

    def get_classes_in_modules(modules):
    classes = set()
    @@ -134,4 +135,4 @@ def toposort(deps_dict):
    else:
    unsorted.append(value)
    deps_dict = {value : deps_dict[value] - sorted_values for value in unsorted}
    return sorted_list
    return sorted_list
  6. JacquesLucke created this gist Nov 30, 2018.
    137 changes: 137 additions & 0 deletions auto_load.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,137 @@
    import os
    import bpy
    import sys
    import typing
    import inspect
    import pkgutil
    import importlib
    from pathlib import Path

    __all__ = (
    "init",
    "register",
    "unregister",
    )

    modules = None
    ordered_classes = None

    def init():
    global modules
    global ordered_classes

    modules = get_all_submodules(Path(__file__).parent)
    ordered_classes = get_ordered_classes_to_register(modules)

    def register():
    for cls in ordered_classes:
    bpy.utils.register_class(cls)

    for module in modules:
    if module.__name__ == __name__:
    continue
    if hasattr(module, "register"):
    module.register()

    def unregister():
    for cls in reversed(ordered_classes):
    bpy.utils.unregister_class(cls)

    for module in modules:
    if module.__name__ == __name__:
    continue
    if hasattr(module, "unregister"):
    module.unregister()


    # Import modules
    #################################################

    def get_all_submodules(directory):
    return list(iter_submodules(directory, directory.name))

    def iter_submodules(path, package_name):
    for name in sorted(iter_submodule_names(path)):
    yield importlib.import_module("." + name, package_name)

    def iter_submodule_names(path, root=""):
    for _, module_name, is_package in pkgutil.iter_modules([str(path)]):
    if is_package:
    sub_path = path / module_name
    sub_root = root + module_name + "."
    yield from iter_submodule_names(sub_path, sub_root)
    else:
    yield root + module_name


    # Find classes to register
    #################################################

    def get_ordered_classes_to_register(modules):
    return toposort(get_register_deps_dict(modules))

    def get_register_deps_dict(modules):
    deps_dict = {}
    classes_to_register = set(iter_classes_to_register(modules))
    for cls in classes_to_register:
    deps_dict[cls] = set(iter_own_register_deps(cls, classes_to_register))
    return deps_dict

    def iter_own_register_deps(cls, own_classes):
    yield from (dep for dep in iter_register_deps(cls) if dep in own_classes)

    def iter_register_deps(cls):
    for value in typing.get_type_hints(cls, {}, {}).values():
    dependency = get_dependency_from_annotation(value)
    if dependency is not None:
    yield dependency

    def get_dependency_from_annotation(value):
    if isinstance(value, tuple) and len(value) == 2:
    if value[0] in (bpy.props.PointerProperty, bpy.props.CollectionProperty):
    return value[1]["type"]
    return None

    def iter_classes_to_register(modules):
    base_types = get_register_base_types()
    for cls in get_classes_in_modules(modules):
    if any(base in base_types for base in cls.__bases__):
    yield cls

    def get_classes_in_modules(modules):
    classes = set()
    for module in modules:
    for cls in iter_classes_in_module(module):
    classes.add(cls)
    return classes

    def iter_classes_in_module(module):
    for value in module.__dict__.values():
    if inspect.isclass(value):
    yield value

    def get_register_base_types():
    return set(getattr(bpy.types, name) for name in [
    "Panel", "Operator", "PropertyGroup",
    "AddonPreferences", "Header", "Menu",
    "Node", "NodeSocket", "NodeTree",
    "UIList"
    ])


    # Find order to register to solve dependencies
    #################################################

    def toposort(deps_dict):
    sorted_list = []
    sorted_values = set()
    while len(deps_dict) > 0:
    unsorted = []
    for value, deps in deps_dict.items():
    if len(deps) == 0:
    sorted_list.append(value)
    sorted_values.add(value)
    else:
    unsorted.append(value)
    deps_dict = {value : deps_dict[value] - sorted_values for value in unsorted}
    return sorted_list