Add conformance tests from Blender docs and generate ContextDict TypedDict
Add conformance test files for bpy.ops, bpy.types.Object, bpy.types.Context, bpy.msgbus, and bpy.data from official Blender documentation for both 5.0 and 5.1. Generate a ContextDict TypedDict from Context properties so that Context.copy() returns precisely typed values instead of dict[str, object], fixing type errors when unpacking into temp_override(). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+38
-2
@@ -384,7 +384,9 @@ def generate_property_stub(
|
||||
else:
|
||||
result += f"{indent} ...\n"
|
||||
result += f"{indent}@{prop['name']}.setter\n"
|
||||
result += f"{indent}def {prop['name']}(self, value: {setter_type}) -> None: ...\n"
|
||||
result += (
|
||||
f"{indent}def {prop['name']}(self, value: {setter_type}) -> None: ...\n"
|
||||
)
|
||||
return result
|
||||
|
||||
result = f"{indent}{prop['name']}: {prop['type']}\n"
|
||||
@@ -553,6 +555,31 @@ def collect_inherited_info(
|
||||
return cache
|
||||
|
||||
|
||||
def _generate_context_dict(context_struct: StructData) -> str:
|
||||
"""Generate a ContextDict TypedDict from Context properties.
|
||||
|
||||
This TypedDict mirrors all Context properties so that Context.copy()
|
||||
can return a precisely typed dict instead of dict[str, object].
|
||||
"""
|
||||
lines: list[str] = ["class ContextDict(TypedDict):"]
|
||||
lines.append(' """Dictionary returned by Context.copy() with all context members."""')
|
||||
method_names = {m["name"] for m in context_struct["methods"]}
|
||||
for prop in context_struct["properties"]:
|
||||
if prop["name"] in method_names:
|
||||
continue
|
||||
lines.append(f" {prop['name']}: {prop['type']}")
|
||||
lines.append("")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def _patch_context_copy_return_type(context_struct: StructData) -> None:
|
||||
"""Change Context.copy() return type from dict[str, object] to ContextDict."""
|
||||
for method in context_struct["methods"]:
|
||||
if method["name"] == "copy":
|
||||
method["return_type"] = "ContextDict"
|
||||
break
|
||||
|
||||
|
||||
def generate_types_stub(
|
||||
structs: list[StructData], python_version: str = "3.11", doc: str = ""
|
||||
) -> str:
|
||||
@@ -570,7 +597,7 @@ def generate_types_stub(
|
||||
all_type_strs_parts.append(m["return_type"])
|
||||
all_type_strs = " ".join(all_type_strs_parts)
|
||||
|
||||
typing_imports = ["Generic", "TypeVar"]
|
||||
typing_imports = ["Generic", "TypedDict", "TypeVar"]
|
||||
if "Literal[" in all_type_strs:
|
||||
typing_imports.append("Literal")
|
||||
if re.search(r"\bSelf\b", all_type_strs):
|
||||
@@ -615,6 +642,11 @@ def generate_types_stub(
|
||||
sorted_structs = topological_sort_structs(structs)
|
||||
inherited_map = collect_inherited_info(sorted_structs)
|
||||
|
||||
# Patch Context.copy() return type and generate ContextDict TypedDict
|
||||
context_struct = next((s for s in sorted_structs if s["name"] == "Context"), None)
|
||||
if context_struct:
|
||||
_patch_context_copy_return_type(context_struct)
|
||||
|
||||
for struct in sorted_structs:
|
||||
info = inherited_map.get(struct["name"], _InheritedInfo(set(), set()))
|
||||
# Force writable properties to readonly when they override a parent's
|
||||
@@ -622,6 +654,10 @@ def generate_types_stub(
|
||||
for prop in struct["properties"]:
|
||||
if not prop["is_readonly"] and prop["name"] in info.readonly_props:
|
||||
prop["is_readonly"] = True
|
||||
# Emit ContextDict TypedDict right before the Context class
|
||||
if struct["name"] == "Context":
|
||||
parts.append("")
|
||||
parts.append(_generate_context_dict(struct))
|
||||
parts.append("")
|
||||
parts.append(generate_struct_stub(struct, info.methods))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user