Fix mypy compatibility and add 5.0 conformance tests
- Qualify bare "object" as builtins.object in bpy.types to avoid shadowing by Context.object and similar properties - Use _Sequence alias import to fix mypy "collections.abc.Sequence not defined" - Filter out variables that clash with submodule re-exports (e.g. bpy.app) - Add 5.0 conformance tests from Blender 5.0 mathutils documentation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a523a9fcb7
commit
8ef75df9eb
18
conformance/5.0/test_mathutils.py
Normal file
18
conformance/5.0/test_mathutils.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import mathutils
|
||||||
|
from math import radians
|
||||||
|
|
||||||
|
vec = mathutils.Vector((1.0, 2.0, 3.0))
|
||||||
|
|
||||||
|
mat_rot = mathutils.Matrix.Rotation(radians(90.0), 4, "X")
|
||||||
|
mat_trans = mathutils.Matrix.Translation(vec)
|
||||||
|
|
||||||
|
mat = mat_trans @ mat_rot
|
||||||
|
mat.invert()
|
||||||
|
|
||||||
|
mat3 = mat.to_3x3()
|
||||||
|
quat1 = mat.to_quaternion()
|
||||||
|
quat2 = mat3.to_quaternion()
|
||||||
|
|
||||||
|
quat_diff = quat1.rotation_difference(quat2)
|
||||||
|
|
||||||
|
print(quat_diff.angle)
|
||||||
37
conformance/5.0/test_mathutils_color.py
Normal file
37
conformance/5.0/test_mathutils_color.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import mathutils
|
||||||
|
|
||||||
|
# Color values are represented as RGB values from 0 - 1, this is blue.
|
||||||
|
col = mathutils.Color((0.0, 0.0, 1.0))
|
||||||
|
|
||||||
|
# As well as r/g/b attribute access you can adjust them by h/s/v.
|
||||||
|
col.s *= 0.5
|
||||||
|
|
||||||
|
# You can access its components by attribute or index.
|
||||||
|
print("Color R:", col.r)
|
||||||
|
print("Color G:", col[1])
|
||||||
|
print("Color B:", col[-1])
|
||||||
|
print("Color HSV: {:.2f}, {:.2f}, {:.2f}".format(*col))
|
||||||
|
|
||||||
|
|
||||||
|
# Components of an existing color can be set.
|
||||||
|
col[:] = 0.0, 0.5, 1.0
|
||||||
|
|
||||||
|
# Components of an existing color can use slice notation to get a tuple.
|
||||||
|
print("Values: {:f}, {:f}, {:f}".format(*col))
|
||||||
|
|
||||||
|
# Colors can be added and subtracted.
|
||||||
|
col += mathutils.Color((0.25, 0.0, 0.0))
|
||||||
|
|
||||||
|
# Color can be multiplied, in this example color is scaled to 0-255
|
||||||
|
# can printed as integers.
|
||||||
|
print("Color: {:d}, {:d}, {:d}".format(*(int(c) for c in (col * 255.0))))
|
||||||
|
|
||||||
|
# This example prints the color as hexadecimal.
|
||||||
|
print(
|
||||||
|
"Hexadecimal: {:02x}{:02x}{:02x}".format(
|
||||||
|
int(col.r * 255), int(col.g * 255), int(col.b * 255)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Direct buffer access is supported at runtime via C buffer protocol
|
||||||
|
# but not expressible in type stubs (requires Python 3.12+ __buffer__).
|
||||||
35
conformance/5.0/test_mathutils_euler.py
Normal file
35
conformance/5.0/test_mathutils_euler.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import mathutils
|
||||||
|
import math
|
||||||
|
|
||||||
|
# Create a new euler with default axis rotation order.
|
||||||
|
eul = mathutils.Euler((0.0, math.radians(45.0), 0.0), "XYZ")
|
||||||
|
|
||||||
|
# Rotate the euler.
|
||||||
|
eul.rotate_axis("Z", math.radians(10.0))
|
||||||
|
|
||||||
|
# You can access its components by attribute or index.
|
||||||
|
print("Euler X", eul.x)
|
||||||
|
print("Euler Y", eul[1])
|
||||||
|
print("Euler Z", eul[-1])
|
||||||
|
|
||||||
|
# Components of an existing euler can be set.
|
||||||
|
eul[:] = 1.0, 2.0, 3.0
|
||||||
|
|
||||||
|
# Components of an existing euler can use slice notation to get a tuple.
|
||||||
|
print("Values: {:f}, {:f}, {:f}".format(*eul))
|
||||||
|
|
||||||
|
# The order can be set at any time too.
|
||||||
|
eul.order = "ZYX"
|
||||||
|
|
||||||
|
# Eulers can be used to rotate vectors.
|
||||||
|
vec = mathutils.Vector((0.0, 0.0, 1.0))
|
||||||
|
vec.rotate(eul)
|
||||||
|
|
||||||
|
# Often its useful to convert the euler into a matrix so it can be used as
|
||||||
|
# transformations with more flexibility.
|
||||||
|
mat_rot = eul.to_matrix()
|
||||||
|
mat_loc = mathutils.Matrix.Translation((2.0, 3.0, 4.0))
|
||||||
|
mat = mat_loc @ mat_rot.to_4x4()
|
||||||
|
|
||||||
|
# Direct buffer access is supported at runtime via C buffer protocol
|
||||||
|
# but not expressible in type stubs (requires Python 3.12+ __buffer__).
|
||||||
35
conformance/5.0/test_mathutils_matrix.py
Normal file
35
conformance/5.0/test_mathutils_matrix.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import mathutils
|
||||||
|
import math
|
||||||
|
|
||||||
|
# Create a location matrix.
|
||||||
|
mat_loc = mathutils.Matrix.Translation((2.0, 3.0, 4.0))
|
||||||
|
|
||||||
|
# Create an identity matrix.
|
||||||
|
mat_sca = mathutils.Matrix.Scale(0.5, 4, (0.0, 0.0, 1.0))
|
||||||
|
|
||||||
|
# Create a rotation matrix.
|
||||||
|
mat_rot = mathutils.Matrix.Rotation(math.radians(45.0), 4, "X")
|
||||||
|
|
||||||
|
# Combine transformations.
|
||||||
|
mat_out = mat_loc @ mat_rot @ mat_sca
|
||||||
|
print(mat_out)
|
||||||
|
|
||||||
|
# Extract components back out of the matrix as two vectors and a quaternion.
|
||||||
|
loc, rot, sca = mat_out.decompose()
|
||||||
|
print(loc, rot, sca)
|
||||||
|
|
||||||
|
# Recombine extracted components.
|
||||||
|
mat_out2 = mathutils.Matrix.LocRotScale(loc, rot, sca)
|
||||||
|
print(mat_out2)
|
||||||
|
|
||||||
|
# It can also be useful to access components of a matrix directly.
|
||||||
|
mat = mathutils.Matrix()
|
||||||
|
mat[0][0], mat[1][0], mat[2][0] = 0.0, 1.0, 2.0
|
||||||
|
|
||||||
|
mat[0][0:3] = 0.0, 1.0, 2.0
|
||||||
|
|
||||||
|
# Each item in a matrix is a vector so vector utility functions can be used.
|
||||||
|
mat[0].xyz = 0.0, 1.0, 2.0
|
||||||
|
|
||||||
|
# Direct buffer access is supported at runtime via C buffer protocol
|
||||||
|
# but not expressible in type stubs (requires Python 3.12+ __buffer__).
|
||||||
40
conformance/5.0/test_mathutils_quaternion.py
Normal file
40
conformance/5.0/test_mathutils_quaternion.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import mathutils
|
||||||
|
import math
|
||||||
|
|
||||||
|
# A new rotation 90 degrees about the Y axis.
|
||||||
|
quat_a = mathutils.Quaternion((0.7071068, 0.0, 0.7071068, 0.0))
|
||||||
|
|
||||||
|
# Passing values to Quaternion's directly can be confusing so axis, angle
|
||||||
|
# is supported for initializing too.
|
||||||
|
quat_b = mathutils.Quaternion((0.0, 1.0, 0.0), math.radians(90.0))
|
||||||
|
|
||||||
|
print("Check quaternions match", quat_a == quat_b)
|
||||||
|
|
||||||
|
# Like matrices, quaternions can be multiplied to accumulate rotational values.
|
||||||
|
quat_a = mathutils.Quaternion((0.0, 1.0, 0.0), math.radians(90.0))
|
||||||
|
quat_b = mathutils.Quaternion((0.0, 0.0, 1.0), math.radians(45.0))
|
||||||
|
quat_out = quat_a @ quat_b
|
||||||
|
|
||||||
|
# Print the quaternion, euler degrees for mere mortals and (axis, angle).
|
||||||
|
print("Final Rotation:")
|
||||||
|
print(quat_out)
|
||||||
|
print("{:.2f}, {:.2f}, {:.2f}".format(*(math.degrees(a) for a in quat_out.to_euler())))
|
||||||
|
print(
|
||||||
|
"({:.2f}, {:.2f}, {:.2f}), {:.2f}".format(
|
||||||
|
*quat_out.axis, math.degrees(quat_out.angle)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Multiple rotations can be interpolated using the exponential map.
|
||||||
|
quat_c = mathutils.Quaternion((1.0, 0.0, 0.0), math.radians(15.0))
|
||||||
|
exp_avg = (
|
||||||
|
quat_a.to_exponential_map()
|
||||||
|
+ quat_b.to_exponential_map()
|
||||||
|
+ quat_c.to_exponential_map()
|
||||||
|
) / 3.0
|
||||||
|
quat_avg = mathutils.Quaternion(exp_avg)
|
||||||
|
print("Average rotation:")
|
||||||
|
print(quat_avg)
|
||||||
|
|
||||||
|
# Direct buffer access is supported at runtime via C buffer protocol
|
||||||
|
# but not expressible in type stubs (requires Python 3.12+ __buffer__).
|
||||||
58
conformance/5.0/test_mathutils_vector.py
Normal file
58
conformance/5.0/test_mathutils_vector.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import mathutils
|
||||||
|
|
||||||
|
# Zero length vector.
|
||||||
|
vec = mathutils.Vector((0.0, 0.0, 1.0))
|
||||||
|
|
||||||
|
# Unit length vector.
|
||||||
|
vec_a = vec.normalized()
|
||||||
|
|
||||||
|
vec_b = mathutils.Vector((0.0, 1.0, 2.0))
|
||||||
|
|
||||||
|
vec2d = mathutils.Vector((1.0, 2.0))
|
||||||
|
vec3d = mathutils.Vector((1.0, 0.0, 0.0))
|
||||||
|
vec4d = vec_a.to_4d()
|
||||||
|
|
||||||
|
# Other `mathutils` types.
|
||||||
|
quat = mathutils.Quaternion()
|
||||||
|
matrix = mathutils.Matrix()
|
||||||
|
|
||||||
|
# Comparison operators can be done on Vector classes:
|
||||||
|
|
||||||
|
# (In)equality operators == and != test component values, e.g. 1,2,3 != 3,2,1
|
||||||
|
vec_a == vec_b
|
||||||
|
vec_a != vec_b
|
||||||
|
|
||||||
|
# Ordering operators >, >=, > and <= test vector length.
|
||||||
|
vec_a > vec_b
|
||||||
|
vec_a >= vec_b
|
||||||
|
vec_a < vec_b
|
||||||
|
vec_a <= vec_b
|
||||||
|
|
||||||
|
|
||||||
|
# Math can be performed on Vector classes.
|
||||||
|
vec_a + vec_b
|
||||||
|
vec_a - vec_b
|
||||||
|
vec_a @ vec_b
|
||||||
|
vec_a * 10.0
|
||||||
|
matrix @ vec_a
|
||||||
|
quat @ vec_a
|
||||||
|
-vec_a
|
||||||
|
|
||||||
|
|
||||||
|
# You can access a vector object like a sequence.
|
||||||
|
x = vec_a[0]
|
||||||
|
len(vec)
|
||||||
|
vec_a[:] = vec_b
|
||||||
|
vec_a[:] = 1.0, 2.0, 3.0
|
||||||
|
vec2d[:] = vec3d[:2]
|
||||||
|
|
||||||
|
|
||||||
|
# Vectors support 'swizzle' operations.
|
||||||
|
# See https://en.wikipedia.org/wiki/Swizzling_(computer_graphics)
|
||||||
|
vec.xyz = vec.zyx
|
||||||
|
vec.xy = vec4d.zw
|
||||||
|
vec.xyz = vec4d.wzz
|
||||||
|
vec4d.wxyz = vec.yxyx
|
||||||
|
|
||||||
|
# Direct buffer access is supported at runtime via C buffer protocol
|
||||||
|
# but not expressible in type stubs (requires Python 3.12+ __buffer__).
|
||||||
@ -600,7 +600,9 @@ def generate_types_stub(
|
|||||||
if "Sequence[" in all_type_strs:
|
if "Sequence[" in all_type_strs:
|
||||||
# Use fully qualified import to avoid shadowing by bpy.types.Sequence
|
# Use fully qualified import to avoid shadowing by bpy.types.Sequence
|
||||||
# (the video sequencer strip type)
|
# (the video sequencer strip type)
|
||||||
|
imports.append("import collections")
|
||||||
imports.append("import collections.abc")
|
imports.append("import collections.abc")
|
||||||
|
imports.append("from collections.abc import Sequence as _Sequence")
|
||||||
|
|
||||||
parts: list[str] = []
|
parts: list[str] = []
|
||||||
if doc:
|
if doc:
|
||||||
@ -629,10 +631,14 @@ def generate_types_stub(
|
|||||||
result = "\n".join(parts)
|
result = "\n".join(parts)
|
||||||
class_names = {s["name"] for s in structs}
|
class_names = {s["name"] for s in structs}
|
||||||
result = strip_self_module_prefix(result, "bpy.types", class_names)
|
result = strip_self_module_prefix(result, "bpy.types", class_names)
|
||||||
# Qualify all bare Sequence[ references to avoid shadowing by bpy.types.Sequence
|
# Replace bare Sequence[ with _Sequence[ to avoid shadowing by bpy.types.Sequence
|
||||||
result = re.sub(
|
result = re.sub(r"(?<!\.)(?<!\w)\bSequence\[", "_Sequence[", result)
|
||||||
r"(?<!\.)(?<!\w)\bSequence\[", "collections.abc.Sequence[", result
|
# Qualify bare "object" in type annotations to avoid shadowing by
|
||||||
)
|
# properties named "object" (e.g. Context.object: Object).
|
||||||
|
result = re.sub(r"(?<=: )object\b", "builtins.object", result)
|
||||||
|
result = re.sub(r"(?<=\| )object\b", "builtins.object", result)
|
||||||
|
result = re.sub(r"(?<=\[)object\b", "builtins.object", result)
|
||||||
|
result = re.sub(r"(?<=-> )object\b", "builtins.object", result)
|
||||||
return prune_unused_imports(result)
|
return prune_unused_imports(result)
|
||||||
|
|
||||||
|
|
||||||
@ -755,10 +761,12 @@ def generate_module_stub(
|
|||||||
parts.append('_T = TypeVar("_T")')
|
parts.append('_T = TypeVar("_T")')
|
||||||
parts.append("")
|
parts.append("")
|
||||||
|
|
||||||
# Variables
|
# Variables (skip those that clash with submodule re-exports)
|
||||||
|
sub_names = set(submodule_names or [])
|
||||||
if module_data["variables"]:
|
if module_data["variables"]:
|
||||||
parts.append("")
|
parts.append("")
|
||||||
for var in module_data["variables"]:
|
for var in module_data["variables"]:
|
||||||
|
if var["name"] not in sub_names:
|
||||||
parts.append(generate_variable_stub(var))
|
parts.append(generate_variable_stub(var))
|
||||||
|
|
||||||
# Functions
|
# Functions
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user