Skip to content

Commit 520b6e2

Browse files
fix: resolve flaky tenant input validation in Python 3.12
Use instead of for runtime issubclass/isinstance checks in the input validator. The typing module's Sequence can produce flaky isinstance() results in Python 3.12+ due to internal changes in the typing module, causing valid Sequence inputs (e.g., a list of Tenant objects) to be incorrectly rejected. Also adds targeted test coverage for the Sequence[Union[...]] validation pattern that reproduces the reported failure scenario.
1 parent f26bee0 commit 520b6e2

2 files changed

Lines changed: 12 additions & 3 deletions

File tree

test/collection/test_validator.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, List
1+
from typing import Any, List, Sequence, Union
22

33
import numpy as np
44
import pandas as pd
@@ -28,6 +28,14 @@
2828
False,
2929
),
3030
(pl.Series([1, 1]), [_ExtraTypes.PANDAS, _ExtraTypes.NUMPY, List], True),
31+
# Tests for Sequence[Union[...]] pattern, which was flaky in Python 3.12
32+
(["a", 1], [Sequence[Union[str, int]]], False),
33+
([1, "a"], [Sequence[Union[str, int]]], False),
34+
([1, 2], [Sequence[Union[str, int]]], False),
35+
(["a", "b"], [Sequence[Union[str, int]]], False),
36+
(["a", 1], [str, Sequence[Union[str, int]]], False), # matches Sequence[Union[str, int]]
37+
# Non-sequence values are not valid Sequence types: int is not iterable
38+
(42, [Sequence[Union[str, int]]], True),
3139
],
3240
)
3341
def test_validator(inputs: Any, expected: List[Any], error: bool) -> None:

weaviate/validator.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from collections.abc import Sequence as ABCSequence
12
from dataclasses import dataclass
23
from typing import Any, List, Sequence, Union, get_args, get_origin
34

@@ -48,9 +49,9 @@ def _is_valid(expected: Any, value: Any) -> bool:
4849
args = get_args(expected)
4950
return any(isinstance(value, arg) for arg in args)
5051
if expected_origin is not None and (
51-
issubclass(expected_origin, Sequence) or expected_origin is list
52+
issubclass(expected_origin, ABCSequence) or expected_origin is list
5253
):
53-
if not isinstance(value, Sequence) and not isinstance(value, list):
54+
if not isinstance(value, (ABCSequence, list)):
5455
return False
5556
args = get_args(expected)
5657
if len(args) == 1:

0 commit comments

Comments
 (0)