Files
workinf_Blender_Wasm/tools/web/generate-script-entry-inventory.py
mes123456 380cbed4ff
Some checks are pending
M6 deployable RC / quick (push) Waiting to run
M6 deployable RC / chromium (push) Blocked by required conditions
M6 deployable RC / release (push) Blocked by required conditions
Checkpoint web parity through Chromium input tasks
2026-08-19 10:39:03 -04:00

67 lines
5.4 KiB
Python

"""Inventory Blender 5.2 scripting entry points without executing user script text."""
import hashlib
import json
import sys
from pathlib import Path
import bpy
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 text(value):
return value.decode("utf-8") if isinstance(value, bytes) else value
def runtime_identity():
binary = Path(bpy.app.binary_path)
return {"blenderVersion": text(bpy.app.version_string), "versionTuple": list(bpy.app.version), "buildDate": text(bpy.app.build_date), "buildTime": text(bpy.app.build_time), "buildHash": text(bpy.app.build_hash), "buildBranch": text(bpy.app.build_branch), "buildPlatform": text(bpy.app.build_platform), "buildType": text(bpy.app.build_type), "binarySha256": sha256_file(binary)}
def create_fixture(output_path):
bpy.ops.wm.read_factory_settings(use_empty=True)
texts = [("InternalSafe.py", "value = 7\nprint(value)\n", False, ""), ("ModuleAutorun.py", "def register():\n return 'blocked'\n", True, ""), ("ExternalProject.py", "message = 'project relative'\n", False, "//scripts/external_project.py")]
for name, source, use_module, filepath in texts:
value = bpy.data.texts.new(name); value.write(source); value.use_module = use_module; value.filepath = filepath
mesh = bpy.data.meshes.new("M13 Script Inventory Mesh"); mesh.from_pydata([(0, 0, 0), (1, 0, 0), (0, 1, 0)], [], [(0, 1, 2)]); mesh.update()
obj = bpy.data.objects.new("M13 Script Inventory Object", mesh); bpy.context.scene.collection.objects.link(obj)
obj["driver_source"] = 1.0
driver = obj.driver_add('location', 0).driver; driver.expression = "var * 2"; variable = driver.variables.new(); variable.name = "var"; variable.type = "SINGLE_PROP"; variable.targets[0].id = obj; variable.targets[0].data_path = '[\"driver_source\"]'
output_path.parent.mkdir(parents=True, exist_ok=True); bpy.ops.wm.save_as_mainfile(filepath=str(output_path), compress=False)
def entry_inventory():
text_entries = []
for value in sorted(bpy.data.texts, key=lambda item: item.name):
source = value.as_string(); text_entries.append({"name": value.name, "byteLength": len(source.encode("utf-8")), "lineCount": len(source.split("\n")), "useModule": bool(value.use_module), "filepath": value.filepath, "internal": not bool(value.filepath)})
console_ops = [name for name in ("execute", "history_append", "scrollback_append") if hasattr(bpy.ops.console, name)]
driver_entries = []
for obj in sorted(bpy.data.objects, key=lambda item: item.name):
if not obj.animation_data: continue
for item in obj.animation_data.drivers:
driver_entries.append({"object": obj.name, "dataPath": item.data_path, "arrayIndex": item.array_index, "expression": item.driver.expression, "variableCount": len(item.driver.variables)})
handler_groups = sorted(name for name in dir(bpy.app.handlers) if not name.startswith("_") and isinstance(getattr(bpy.app.handlers, name), list))
addon_ops = [name for name in ("addon_install", "addon_enable", "addon_disable", "addon_remove") if hasattr(bpy.ops.preferences, name)]
return {"text": {"count": len(text_entries), "entries": text_entries}, "pythonConsole": {"operatorIds": [f"console.{name}" for name in console_ops], "available": len(console_ops) == 3}, "autorun": {"moduleTextNames": [item["name"] for item in text_entries if item["useModule"]], "defaultExecution": "DENY"}, "driverExpressions": {"count": len(driver_entries), "entries": driver_entries, "defaultExecution": "DENY"}, "handlers": {"groups": handler_groups, "defaultExecution": "DENY"}, "addons": {"operatorIds": [f"preferences.{name}" for name in addon_ops], "enabledAddons": sorted(addon.module for addon in bpy.context.preferences.addons), "defaultExecution": "DENY"}}
def main(output_dir, report_path):
output_dir = Path(output_dir).resolve(); report_path = Path(report_path).resolve(); output_dir.mkdir(parents=True, exist_ok=True); report_path.parent.mkdir(parents=True, exist_ok=True)
fixture_path = output_dir / "script-entry-inventory.blend"; create_fixture(fixture_path)
report = {"schemaVersion": 1, "task": "M13-01A", "operation": "BLENDER_SCRIPT_ENTRY_INVENTORY", "runtime": runtime_identity(), "sourceAnchor": "blender-5.2.0/source/blender/python", "executionPolicy": {"text": "READ_METADATA_ONLY", "pythonConsole": "DENY", "autorun": "DENY", "driverExpression": "DENY", "handler": "DENY", "addon": "DENY"}, "inventory": entry_inventory(), "fixture": {"name": fixture_path.name, "byteLength": fixture_path.stat().st_size, "sha256": sha256_file(fixture_path)}, "nextTask": "M13-01B"}
report_path.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
print("script-entry-inventory-generated texts=%s drivers=%s consoleOps=%s addonOps=%s next=%s" % (report["inventory"]["text"]["count"], report["inventory"]["driverExpressions"]["count"], len(report["inventory"]["pythonConsole"]["operatorIds"]), len(report["inventory"]["addons"]["operatorIds"]), report["nextTask"]))
if __name__ == "__main__":
args = sys.argv[sys.argv.index("--") + 1 :] if "--" in sys.argv else []
if len(args) != 2: raise SystemExit("usage: blender --background --python generate-script-entry-inventory.py -- OUTPUT_DIR REPORT")
main(args[0], args[1])