Govern task context and advance execution pointer
This commit is contained in:
129
tools/web/generate-blender-family-inventory.py
Normal file
129
tools/web/generate-blender-family-inventory.py
Normal file
@@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate deterministic Blender 5.2 modifier, constraint, and node inventories."""
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def text(value):
|
||||
return value.decode("utf-8", errors="replace") if isinstance(value, bytes) else str(value)
|
||||
|
||||
|
||||
def sha256_file(path):
|
||||
digest = hashlib.sha256()
|
||||
with open(path, "rb") as handle:
|
||||
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
|
||||
digest.update(chunk)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def property_info(prop):
|
||||
result = {
|
||||
"identifier": prop.identifier,
|
||||
"type": prop.type,
|
||||
"readOnly": bool(prop.is_readonly),
|
||||
}
|
||||
if hasattr(prop, "array_length"):
|
||||
result["arrayLength"] = prop.array_length
|
||||
return result
|
||||
|
||||
|
||||
def enum_item_info(item):
|
||||
return {
|
||||
"identifier": item.identifier,
|
||||
"name": text(item.name),
|
||||
"description": text(item.description),
|
||||
"value": item.value,
|
||||
}
|
||||
|
||||
|
||||
def enum_inventory(rna_type, property_name):
|
||||
enum = rna_type.bl_rna.properties[property_name].enum_items
|
||||
entries = [enum_item_info(item) for item in enum]
|
||||
entries.sort(key=lambda item: item["identifier"])
|
||||
return entries
|
||||
|
||||
|
||||
def node_properties(rna):
|
||||
properties = [property_info(prop) for prop in rna.properties if prop.identifier != "rna_type"]
|
||||
properties.sort(key=lambda prop: prop["identifier"])
|
||||
return properties
|
||||
|
||||
|
||||
def node_inventory():
|
||||
entries = []
|
||||
for class_name in sorted(name for name in dir(bpy.types) if not name.startswith("_")):
|
||||
cls = getattr(bpy.types, class_name)
|
||||
try:
|
||||
if not isinstance(cls, type) or not issubclass(cls, bpy.types.Node) or cls is bpy.types.Node:
|
||||
continue
|
||||
is_registered = getattr(cls, "is_registered_node_type", None)
|
||||
if is_registered is None or not is_registered():
|
||||
continue
|
||||
rna = getattr(cls, "bl_rna", None)
|
||||
identifier = text(getattr(rna, "identifier", "")) if rna else ""
|
||||
family = next((prefix[:-4] for prefix in ("ShaderNode", "GeometryNode", "CompositorNode") if identifier.startswith(prefix)), None)
|
||||
if not family:
|
||||
continue
|
||||
entries.append({
|
||||
"family": family,
|
||||
"className": class_name,
|
||||
"rnaIdentifier": identifier,
|
||||
"properties": node_properties(rna),
|
||||
})
|
||||
except (AttributeError, RuntimeError, TypeError):
|
||||
continue
|
||||
entries.sort(key=lambda entry: (entry["family"], entry["rnaIdentifier"], entry["className"]))
|
||||
return entries
|
||||
|
||||
|
||||
def main():
|
||||
if "--" not in sys.argv or len(sys.argv[sys.argv.index("--") + 1:]) != 1:
|
||||
raise SystemExit("usage: blender --background --factory-startup --python generate-blender-family-inventory.py -- OUTPUT.json")
|
||||
output = pathlib.Path(sys.argv[sys.argv.index("--") + 1]).resolve()
|
||||
version = tuple(int(value) for value in bpy.app.version)
|
||||
if version != (5, 2, 0):
|
||||
raise RuntimeError(f"expected Blender 5.2.0, got {version}")
|
||||
modifiers = enum_inventory(bpy.types.Modifier, "type")
|
||||
constraints = enum_inventory(bpy.types.Constraint, "type")
|
||||
nodes = node_inventory()
|
||||
family_counts = {family: sum(1 for node in nodes if node["family"] == family) for family in ("Shader", "Geometry", "Compositor")}
|
||||
binary_path = pathlib.Path(bpy.app.binary_path).resolve()
|
||||
inventory = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M15-01C",
|
||||
"operation": "BLENDER_FAMILY_INVENTORY",
|
||||
"sourceAnchor": "blender-5.2.0/source/blender/makesrna",
|
||||
"runtime": {
|
||||
"blenderVersion": text(bpy.app.version_string),
|
||||
"versionTuple": list(version),
|
||||
"buildHash": text(bpy.app.build_hash),
|
||||
"buildBranch": text(bpy.app.build_branch),
|
||||
"buildPlatform": text(bpy.app.build_platform),
|
||||
"buildType": text(bpy.app.build_type),
|
||||
"buildDate": text(bpy.app.build_date),
|
||||
"buildTime": text(bpy.app.build_time),
|
||||
"binarySha256": sha256_file(binary_path),
|
||||
},
|
||||
"summary": {
|
||||
"modifierCount": len(modifiers),
|
||||
"constraintCount": len(constraints),
|
||||
"nodeCount": len(nodes),
|
||||
"nodesByFamily": family_counts,
|
||||
},
|
||||
"modifiers": modifiers,
|
||||
"constraints": constraints,
|
||||
"nodes": nodes,
|
||||
"nextTask": "M15-01D",
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(inventory, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print(f"blender-family-inventory-generated modifiers={len(modifiers)} constraints={len(constraints)} nodes={len(nodes)} output={output}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user