From d12c162594f6a54f69504a87f414ea8465b5b74d Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Fri, 9 Jan 2026 09:59:55 -0700 Subject: [PATCH] fix: Handle arrays in BooleanValidator --- _plotly_utils/basevalidators.py | 19 ++++++++--- .../validators/test_boolean_validator.py | 32 +++++++++++++++++++ 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/_plotly_utils/basevalidators.py b/_plotly_utils/basevalidators.py index c4d40f9e178..7cf4a0ad6c1 100644 --- a/_plotly_utils/basevalidators.py +++ b/_plotly_utils/basevalidators.py @@ -632,24 +632,35 @@ class BooleanValidator(BaseValidator): "description": "A boolean (true/false) value.", "requiredOpts": [], "otherOpts": [ + "arrayOk", "dflt" ] }, """ - def __init__(self, plotly_name, parent_name, **kwargs): + def __init__(self, plotly_name, parent_name, array_ok=False, **kwargs): super(BooleanValidator, self).__init__( plotly_name=plotly_name, parent_name=parent_name, **kwargs ) + self.array_ok = array_ok def description(self): - return """\ - The '{plotly_name}' property must be specified as a bool - (either True, or False)""".format(plotly_name=self.plotly_name) + desc = """\ + The '{plotly_name}' property is a boolean and must be specified as: + - A boolean value: True or False""".format(plotly_name=self.plotly_name) + if self.array_ok: + desc += """ + - A tuple or list of the above""" + return desc def validate_coerce(self, v): if is_none_or_typed_array_spec(v): pass + elif self.array_ok and is_simple_array(v): + invalid_els = [e for e in v if not isinstance(e, bool)] + if invalid_els: + self.raise_invalid_elements(invalid_els[:10]) + v = to_scalar_or_list(v) elif not isinstance(v, bool): self.raise_invalid_val(v) diff --git a/tests/test_plotly_utils/validators/test_boolean_validator.py b/tests/test_plotly_utils/validators/test_boolean_validator.py index f4f80335a85..8a0827058f2 100644 --- a/tests/test_plotly_utils/validators/test_boolean_validator.py +++ b/tests/test_plotly_utils/validators/test_boolean_validator.py @@ -12,6 +12,14 @@ def validator(request): return BooleanValidator("prop", "parent", dflt=request.param) +@pytest.fixture(params=[True, False]) +def validator_aok(request): + return BooleanValidator("prop", "parent", dflt=request.param, array_ok=True) + + +# Array not ok (default) + + # Acceptance @pytest.mark.parametrize("val", [True, False]) def test_acceptance(val, validator): @@ -25,3 +33,27 @@ def test_rejection(val, validator): validator.validate_coerce(val) assert "Invalid value" in str(validation_failure.value) + + +# Array ok + + +# Acceptance +@pytest.mark.parametrize("val", [(True, False), [True, False]]) +def test_acceptance_aok(val, validator_aok): + v = validator_aok.validate_coerce(val) + if isinstance(val, list): + assert val == v + else: + assert val == tuple(v) + + +# Rejection +@pytest.mark.parametrize( + "val", [(True, "Planet Express"), ["Hubert Farnsworth", False]] +) +def test_rejection_aok(val, validator_aok): + with pytest.raises(ValueError) as validation_failure: + validator_aok.validate_coerce(val) + + assert "Invalid elements" in str(validation_failure.value)