Spaces:
Running
Running
import bpy | |
import inspect | |
import time | |
import pickle | |
import mathutils | |
import os | |
import bpy_types | |
import addon_utils | |
import sys | |
INFO_MEMBER = "__info" | |
def get_info(name="", descr="", bases=None): | |
return {"name": name, | |
"descr": descr, | |
"bases": bases} | |
################################################################## | |
g_bpy_types = {} | |
def doc_from_bpy_struct(bl_rna): | |
bases = [] | |
try: | |
base = bl_rna.base | |
while base: | |
bases.append(type(base).__name__) | |
base = base.base | |
except: | |
if not bases: | |
bases = None | |
return get_info(name=bl_rna.name, descr=bl_rna.description, bases=bases) | |
def bpy_type_first_step(bpy_type): | |
def is_member_from_base_class(bpy_type, identifier): | |
if identifier in bpy.types.ID.bl_rna.properties: | |
return True | |
bases = bpy_type.mro()[1:] | |
for base in bases: | |
if not hasattr(base, "bl_rna"): | |
continue | |
if identifier in base.bl_rna.properties: | |
return True | |
return False | |
info = doc_from_bpy_struct(bpy_type.bl_rna) | |
data = {INFO_MEMBER: info} | |
for prop in bpy_type.bl_rna.properties: | |
identifier = prop.identifier | |
if is_member_from_base_class(bpy_type, identifier): | |
continue | |
if prop.type == 'POINTER': | |
srna_type = prop.fixed_type.identifier | |
try: | |
pointer_type = getattr(bpy.types, srna_type) | |
data[identifier] = pointer_type | |
except Exception: | |
pass | |
continue | |
if prop.type == 'COLLECTION': | |
if prop.srna: | |
srna_type = prop.srna.identifier | |
pointer_type = getattr(bpy.types, srna_type) | |
data[identifier] = pointer_type | |
elif srna_type := prop.fixed_type.identifier: | |
pointer_type = getattr(bpy.types, srna_type) | |
data[identifier] = [pointer_type] | |
continue | |
info_member = doc_from_bpy_struct(prop) | |
data[identifier] = {INFO_MEMBER: info_member} | |
return data | |
def bpy_types_first_step(): | |
global g_bpy_types | |
for bpy_type_name in dir(bpy.types): | |
bpy_type = getattr(bpy.types, bpy_type_name) | |
if not hasattr(bpy_type, "bl_rna"): | |
continue | |
g_bpy_types[bpy_type] = bpy_type_first_step(bpy_type) | |
def bpy_types_second_step(): | |
global g_bpy_types | |
for bpy_type, map in g_bpy_types.items(): | |
for key, val in map.items(): | |
if hasattr(val, "bl_rna"): | |
map[key] = g_bpy_types[val] | |
elif isinstance(val, list): | |
val[0] = g_bpy_types[val[0]] | |
################################################################## | |
bases_builtin = {int, bool, float, str, bytes, tuple, list, | |
set, dict, mathutils.Vector, mathutils.Color, type(None)} | |
def is_member_inherited(obj, member): | |
mro_bases = inspect.getmro(type(obj)) | |
mro_bases_set = set(mro_bases) | |
intersection = mro_bases_set.intersection(bases_builtin) | |
for base in intersection: | |
if hasattr(base, member): | |
return True | |
return False | |
def get_doc_recursive(parent, member): | |
ob = getattr(parent, member) | |
member_info = getattr(type(parent), member, ob) | |
if type(member_info) in bases_builtin or member == "bpy_func": | |
descr = type(member_info).__name__ | |
return {INFO_MEMBER: get_info(descr=descr)} | |
if hasattr(type(ob), "bl_rna"): | |
return g_bpy_types[type(ob)] | |
if "bl_rna" in dir(ob): | |
return g_bpy_types[ob] | |
result = {} | |
descr = member_info.__doc__ if member_info.__doc__ else type(ob).__name__ | |
result[INFO_MEMBER] = get_info(descr=descr) | |
for name in dir(ob): | |
if name.startswith("_"): | |
continue | |
if is_member_inherited(ob, name): | |
continue | |
ob_member = getattr(ob, name, None) | |
if ob_member == parent: | |
descr = type(parent).__name__ | |
result[name] = {INFO_MEMBER: get_info(descr=descr)} | |
continue | |
if ob_member == os: | |
continue | |
if ob_member == bpy: | |
continue | |
if ob_member == bpy_types: | |
continue | |
if ob_member == addon_utils: | |
continue | |
if ob_member == sys: | |
continue | |
if name == "addon_install": | |
# This raises a Error | |
continue | |
result[name] = get_doc_recursive(ob, name) | |
return result | |
################################################################## | |
def print_doc_recursive(map, indent, name, max_step=3): | |
time.sleep(.5) | |
prefix = indent * '|' | |
print(prefix + name) | |
for key, val in map.items(): | |
if key == INFO_MEMBER: | |
print(prefix + val.replace('\n', '\n' + prefix) + '\n' + prefix) | |
elif indent < max_step: | |
name_next = name + '.' + key | |
if isinstance(val, list): | |
print_doc_recursive(val[0], indent + 1, | |
name_next + "[0]", max_step=max_step) | |
else: | |
print_doc_recursive( | |
val, indent + 1, name_next, max_step=max_step) | |
def main(): | |
print("-------------------------------------------------------------") | |
bpy_types_first_step() | |
bpy_types_second_step() | |
members = ( | |
"app", | |
"context", | |
"data", | |
"msgbus", | |
"ops", | |
"path", | |
"props", | |
"types", | |
"utils", | |
) | |
result = { | |
"bpy": {INFO_MEMBER: get_info(descr=bpy.__doc__)}, | |
"__info": {"bases": None}, | |
} | |
for member in members: | |
result["bpy"][member] = get_doc_recursive(bpy, member) | |
# Reference some types at the beginning | |
result["bpy_struct"] = result["bpy"]["types"]["bpy_struct"] | |
result["bpy_types"] = result["bpy"]["types"] | |
if False: | |
print(result["bpy"]["props"]["BoolProperty"]) | |
return | |
# print_doc_recursive(result, 1, "bpy") | |
bpy_doc_dir = "D:/Dev/function-calling/routersbpy_doc_v41.pkl" | |
with open(bpy_doc_dir, "wb") as file: | |
# print(result["types"]["bpy_func"]) | |
pickle.dump(result, file, protocol=pickle.HIGHEST_PROTOCOL) | |
print(f"File '{bpy_doc_dir}' has been updated.") | |
if __name__ == '__main__': | |
main() |