Fix #146802: Py-defined property min/max handling is broken.

Regression from 469f54f484, somehow the clamping for default set of Int
properties was removed in this big refactor, merely add it back, and add
some unittests covering min/max handling.

Pull Request: https://projects.blender.org/blender/blender/pulls/146975
This commit is contained in:
Bastien Montagne
2025-09-29 15:01:08 +02:00
committed by Bastien Montagne
parent 8f8d267a69
commit 1c64e403c4
2 changed files with 93 additions and 13 deletions

View File

@@ -38,11 +38,21 @@ class TestPropertyGroup(bpy.types.PropertyGroup):
class TestPropNumerical(unittest.TestCase):
default_value = 0
custom_value = 1
min_value = -1
max_value = 5
def setUp(self):
id_type.test_bool = BoolProperty(default=bool(self.default_value))
id_type.test_int = IntProperty(default=int(self.default_value))
id_type.test_float = FloatProperty(default=float(self.default_value))
id_type.test_int = IntProperty(
default=int(self.default_value),
min=int(self.min_value),
max=int(self.max_value),
)
id_type.test_float = FloatProperty(
default=float(self.default_value),
min=float(self.min_value),
max=float(self.max_value),
)
self.test_bool_storage = bool(self.custom_value)
self.test_int_storage = int(self.custom_value)
@@ -64,11 +74,15 @@ class TestPropNumerical(unittest.TestCase):
)
id_type.test_int_getset = IntProperty(
default=int(self.default_value),
min=int(self.min_value),
max=int(self.max_value),
get=lambda s: self.test_int_storage,
set=int_set_,
)
id_type.test_float_getset = FloatProperty(
default=float(self.default_value),
min=float(self.min_value),
max=float(self.max_value),
get=lambda s: self.test_float_storage,
set=float_set_,
)
@@ -80,11 +94,15 @@ class TestPropNumerical(unittest.TestCase):
)
id_type.test_int_transform = IntProperty(
default=int(self.default_value),
min=int(self.min_value),
max=int(self.max_value),
get_transform=lambda s, c_v, isset: c_v,
set_transform=lambda s, n_v, c_v, isset: n_v,
)
id_type.test_float_transform = FloatProperty(
default=float(self.default_value),
min=float(self.min_value),
max=float(self.max_value),
get_transform=lambda s, c_v, isset: c_v,
set_transform=lambda s, n_v, c_v, isset: n_v,
)
@@ -98,6 +116,8 @@ class TestPropNumerical(unittest.TestCase):
)
id_type.test_int_getset_transform = IntProperty(
default=int(self.default_value),
min=int(self.min_value),
max=int(self.max_value),
get=lambda s: self.test_int_storage,
set=int_set_,
get_transform=lambda s, c_v, isset: c_v,
@@ -105,6 +125,8 @@ class TestPropNumerical(unittest.TestCase):
)
id_type.test_float_getset_transform = FloatProperty(
default=float(self.default_value),
min=float(self.min_value),
max=float(self.max_value),
get=lambda s: self.test_float_storage,
set=float_set_,
get_transform=lambda s, c_v, isset: c_v,
@@ -128,7 +150,22 @@ class TestPropNumerical(unittest.TestCase):
del id_type.test_int_getset_transform
del id_type.test_bool_getset_transform
def do_test_access(self, prop_name, py_type, expected_value):
def do_min_max_expect_success(self, prop_name, py_type):
# This property is expected to properly clamp set values within required range.
setattr(id_inst, prop_name, py_type(self.min_value - 1))
self.assertEqual(getattr(id_inst, prop_name), py_type(self.min_value))
setattr(id_inst, prop_name, py_type(self.max_value + 1))
self.assertEqual(getattr(id_inst, prop_name), py_type(self.max_value))
def do_min_max_expect_failure(self, prop_name, py_type):
# This property is not expected to properly clamp set values within required range.
# This happens when using custom setters.
setattr(id_inst, prop_name, py_type(self.min_value - 1))
self.assertNotEqual(getattr(id_inst, prop_name), py_type(self.min_value))
setattr(id_inst, prop_name, py_type(self.max_value + 1))
self.assertNotEqual(getattr(id_inst, prop_name), py_type(self.max_value))
def do_test_access(self, prop_name, py_type, expected_value, do_min_max=None):
v = getattr(id_inst, prop_name)
self.assertIsInstance(v, py_type)
self.assertEqual(v, expected_value)
@@ -136,42 +173,84 @@ class TestPropNumerical(unittest.TestCase):
self.assertEqual(getattr(id_inst, prop_name), expected_value)
setattr(id_inst, prop_name, py_type(self.custom_value))
self.assertEqual(getattr(id_inst, prop_name), py_type(self.custom_value))
if do_min_max:
do_min_max(prop_name, py_type)
def test_access_bool(self):
self.do_test_access("test_bool", bool, bool(self.default_value))
def test_access_int(self):
self.do_test_access("test_int", int, int(self.default_value))
self.do_test_access(
"test_int",
int,
int(self.default_value),
do_min_max=self.do_min_max_expect_success,
)
def test_access_float(self):
self.do_test_access("test_float", float, float(self.default_value))
self.do_test_access(
"test_float",
float,
float(self.default_value),
do_min_max=self.do_min_max_expect_success,
)
def test_access_bool_getset(self):
self.do_test_access("test_bool_getset", bool, bool(self.custom_value))
def test_access_int_getset(self):
self.do_test_access("test_int_getset", int, int(self.custom_value))
self.do_test_access(
"test_int_getset",
int,
int(self.custom_value),
do_min_max=self.do_min_max_expect_failure,
)
def test_access_float_getset(self):
self.do_test_access("test_float_getset", float, float(self.custom_value))
self.do_test_access(
"test_float_getset",
float,
float(self.custom_value),
do_min_max=self.do_min_max_expect_failure,
)
def test_access_bool_transform(self):
self.do_test_access("test_bool_transform", bool, bool(self.default_value))
def test_access_int_transform(self):
self.do_test_access("test_int_transform", int, int(self.default_value))
self.do_test_access(
"test_int_transform",
int,
int(self.default_value),
do_min_max=self.do_min_max_expect_success,
)
def test_access_float_transform(self):
self.do_test_access("test_float_transform", float, float(self.default_value))
self.do_test_access(
"test_float_transform",
float,
float(self.default_value),
do_min_max=self.do_min_max_expect_success,
)
def test_access_bool_getset_transform(self):
self.do_test_access("test_bool_getset_transform", bool, bool(self.custom_value))
def test_access_int_getset_transform(self):
self.do_test_access("test_int_getset_transform", int, int(self.custom_value))
self.do_test_access(
"test_int_getset_transform",
int,
int(self.custom_value),
do_min_max=self.do_min_max_expect_failure,
)
def test_access_float_getset_transform(self):
self.do_test_access("test_float_getset_transform", float, float(self.custom_value))
self.do_test_access(
"test_float_getset_transform",
float,
float(self.custom_value),
do_min_max=self.do_min_max_expect_failure,
)
# TODO: Add expected failure cases (e.g. handling of out-of range values).