|
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": |
|
|
|
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) |
|
|
|
|
|
result["bpy_struct"] = result["bpy"]["types"]["bpy_struct"] |
|
result["bpy_types"] = result["bpy"]["types"] |
|
|
|
if False: |
|
print(result["bpy"]["props"]["BoolProperty"]) |
|
return |
|
|
|
|
|
bpy_doc_dir = "D:/Dev/function-calling/routersbpy_doc_v41.pkl" |
|
with open(bpy_doc_dir, "wb") as file: |
|
|
|
pickle.dump(result, file, protocol=pickle.HIGHEST_PROTOCOL) |
|
|
|
print(f"File '{bpy_doc_dir}' has been updated.") |
|
|
|
|
|
if __name__ == '__main__': |
|
main() |