diff --git a/test/collection/test_validator.py b/test/collection/test_validator.py index 6c7e655c5..247cbbd84 100644 --- a/test/collection/test_validator.py +++ b/test/collection/test_validator.py @@ -1,4 +1,4 @@ -from typing import Any, List +from typing import Any, List, Sequence, Union import numpy as np import pandas as pd @@ -37,3 +37,28 @@ def test_validator(inputs: Any, expected: List[Any], error: bool) -> None: _validate_input(_ValidateArgument(expected=expected, name="test", value=inputs)) else: _validate_input(_ValidateArgument(expected=expected, name="test", value=inputs)) + + +@pytest.mark.parametrize( + "inputs,expected,error", + [ + # every element matches one of the union members -> valid + (["a", 1, "b", 2], [Sequence[Union[str, int]]], False), + (["a", "b"], [Sequence[Union[str, int]]], False), + ([1, 2], [Sequence[Union[str, int]]], False), + # one element (a float) matches neither union member -> must be rejected. + # regression test: the old implementation flattened the per-element check + # into a single any() across (value, union_arg) pairs, so it only took one + # element matching one type to pass the whole sequence, even if other + # elements didn't match anything. + (["a", 3.14], [Sequence[Union[str, int]]], True), + ([3.14, "a"], [Sequence[Union[str, int]]], True), + ([1, 3.14], [Sequence[Union[str, int]]], True), + ], +) +def test_validator_sequence_of_union(inputs: Any, expected: List[Any], error: bool) -> None: + if error: + with pytest.raises(WeaviateInvalidInputError): + _validate_input(_ValidateArgument(expected=expected, name="test", value=inputs)) + else: + _validate_input(_ValidateArgument(expected=expected, name="test", value=inputs)) diff --git a/weaviate/validator.py b/weaviate/validator.py index e9d411dae..46923b69c 100644 --- a/weaviate/validator.py +++ b/weaviate/validator.py @@ -56,7 +56,9 @@ def _is_valid(expected: Any, value: Any) -> bool: if len(args) == 1: if get_origin(args[0]) is Union: union_args = get_args(args[0]) - return any(isinstance(val, union_arg) for val in value for union_arg in union_args) + return all( + any(isinstance(val, union_arg) for union_arg in union_args) for val in value + ) else: return all(isinstance(val, args[0]) for val in value) # bool is a subclass of int, so isinstance(True, int) is True. Reject a