Last active
April 8, 2026 11:11
-
-
Save JacquesLucke/11fecc6ea86ef36ea72f76ca547e795b to your computer and use it in GitHub Desktop.
Revisions
-
JacquesLucke revised this gist
Feb 27, 2021 . 1 changed file with 3 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 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", "Gizmo", "GizmoGroup", ]) -
JacquesLucke revised this gist
Feb 21, 2021 . 1 changed file with 9 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 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): -
JacquesLucke revised this gist
Jan 12, 2020 . 1 changed file with 20 additions and 8 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 = {} 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_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_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: 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_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__): -
JacquesLucke revised this gist
Dec 1, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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", "RenderEngine" ]) -
JacquesLucke revised this gist
Dec 1, 2018 . 1 changed file with 3 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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__): 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 -
JacquesLucke created this gist
Nov 30, 2018 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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