1 import math 2 import sys 3 from datetime import date, datetime, timedelta, timezone 4 from decimal import Decimal 5 from typing import Any, Dict, Iterable, Iterator, List, NamedTuple, Set, Tuple 6 7 if sys.version_info < (3, 9): 8 from typing_extensions import Annotated 9 else: 10 from typing import Annotated 11 12 import annotated_types as at 13 14 15 class Case(NamedTuple): 16 """ 17 A test case for `annotated_types`. 18 """ 19 20 annotation: Any 21 valid_cases: Iterable[Any] 22 invalid_cases: Iterable[Any] 23 24 25 def cases() -> Iterable[Case]: 26 # Gt, Ge, Lt, Le 27 yield Case(Annotated[int, at.Gt(4)], (5, 6, 1000), (4, 0, -1)) 28 yield Case(Annotated[float, at.Gt(0.5)], (0.6, 0.7, 0.8, 0.9), (0.5, 0.0, -0.1)) 29 yield Case( 30 Annotated[datetime, at.Gt(datetime(2000, 1, 1))], 31 [datetime(2000, 1, 2), datetime(2000, 1, 3)], 32 [datetime(2000, 1, 1), datetime(1999, 12, 31)], 33 ) 34 yield Case( 35 Annotated[datetime, at.Gt(date(2000, 1, 1))], 36 [date(2000, 1, 2), date(2000, 1, 3)], 37 [date(2000, 1, 1), date(1999, 12, 31)], 38 ) 39 yield Case( 40 Annotated[datetime, at.Gt(Decimal('1.123'))], 41 [Decimal('1.1231'), Decimal('123')], 42 [Decimal('1.123'), Decimal('0')], 43 ) 44 45 yield Case(Annotated[int, at.Ge(4)], (4, 5, 6, 1000, 4), (0, -1)) 46 yield Case(Annotated[float, at.Ge(0.5)], (0.5, 0.6, 0.7, 0.8, 0.9), (0.4, 0.0, -0.1)) 47 yield Case( 48 Annotated[datetime, at.Ge(datetime(2000, 1, 1))], 49 [datetime(2000, 1, 2), datetime(2000, 1, 3)], 50 [datetime(1998, 1, 1), datetime(1999, 12, 31)], 51 ) 52 53 yield Case(Annotated[int, at.Lt(4)], (0, -1), (4, 5, 6, 1000, 4)) 54 yield Case(Annotated[float, at.Lt(0.5)], (0.4, 0.0, -0.1), (0.5, 0.6, 0.7, 0.8, 0.9)) 55 yield Case( 56 Annotated[datetime, at.Lt(datetime(2000, 1, 1))], 57 [datetime(1999, 12, 31), datetime(1999, 12, 31)], 58 [datetime(2000, 1, 2), datetime(2000, 1, 3)], 59 ) 60 61 yield Case(Annotated[int, at.Le(4)], (4, 0, -1), (5, 6, 1000)) 62 yield Case(Annotated[float, at.Le(0.5)], (0.5, 0.0, -0.1), (0.6, 0.7, 0.8, 0.9)) 63 yield Case( 64 Annotated[datetime, at.Le(datetime(2000, 1, 1))], 65 [datetime(2000, 1, 1), datetime(1999, 12, 31)], 66 [datetime(2000, 1, 2), datetime(2000, 1, 3)], 67 ) 68 69 # Interval 70 yield Case(Annotated[int, at.Interval(gt=4)], (5, 6, 1000), (4, 0, -1)) 71 yield Case(Annotated[int, at.Interval(gt=4, lt=10)], (5, 6), (4, 10, 1000, 0, -1)) 72 yield Case(Annotated[float, at.Interval(ge=0.5, le=1)], (0.5, 0.9, 1), (0.49, 1.1)) 73 yield Case( 74 Annotated[datetime, at.Interval(gt=datetime(2000, 1, 1), le=datetime(2000, 1, 3))], 75 [datetime(2000, 1, 2), datetime(2000, 1, 3)], 76 [datetime(2000, 1, 1), datetime(2000, 1, 4)], 77 ) 78 79 yield Case(Annotated[int, at.MultipleOf(multiple_of=3)], (0, 3, 9), (1, 2, 4)) 80 yield Case(Annotated[float, at.MultipleOf(multiple_of=0.5)], (0, 0.5, 1, 1.5), (0.4, 1.1)) 81 82 # lengths 83 84 yield Case(Annotated[str, at.MinLen(3)], ('123', '1234', 'x' * 10), ('', '1', '12')) 85 yield Case(Annotated[str, at.Len(3)], ('123', '1234', 'x' * 10), ('', '1', '12')) 86 yield Case(Annotated[List[int], at.MinLen(3)], ([1, 2, 3], [1, 2, 3, 4], [1] * 10), ([], [1], [1, 2])) 87 yield Case(Annotated[List[int], at.Len(3)], ([1, 2, 3], [1, 2, 3, 4], [1] * 10), ([], [1], [1, 2])) 88 89 yield Case(Annotated[str, at.MaxLen(4)], ('', '1234'), ('12345', 'x' * 10)) 90 yield Case(Annotated[str, at.Len(0, 4)], ('', '1234'), ('12345', 'x' * 10)) 91 yield Case(Annotated[List[str], at.MaxLen(4)], ([], ['a', 'bcdef'], ['a', 'b', 'c']), (['a'] * 5, ['b'] * 10)) 92 yield Case(Annotated[List[str], at.Len(0, 4)], ([], ['a', 'bcdef'], ['a', 'b', 'c']), (['a'] * 5, ['b'] * 10)) 93 94 yield Case(Annotated[str, at.Len(3, 5)], ('123', '12345'), ('', '1', '12', '123456', 'x' * 10)) 95 yield Case(Annotated[str, at.Len(3, 3)], ('123',), ('12', '1234')) 96 97 yield Case(Annotated[Dict[int, int], at.Len(2, 3)], [{1: 1, 2: 2}], [{}, {1: 1}, {1: 1, 2: 2, 3: 3, 4: 4}]) 98 yield Case(Annotated[Set[int], at.Len(2, 3)], ({1, 2}, {1, 2, 3}), (set(), {1}, {1, 2, 3, 4})) 99 yield Case(Annotated[Tuple[int, ...], at.Len(2, 3)], ((1, 2), (1, 2, 3)), ((), (1,), (1, 2, 3, 4))) 100 101 # Timezone 102 103 yield Case( 104 Annotated[datetime, at.Timezone(None)], [datetime(2000, 1, 1)], [datetime(2000, 1, 1, tzinfo=timezone.utc)] 105 ) 106 yield Case( 107 Annotated[datetime, at.Timezone(...)], [datetime(2000, 1, 1, tzinfo=timezone.utc)], [datetime(2000, 1, 1)] 108 ) 109 yield Case( 110 Annotated[datetime, at.Timezone(timezone.utc)], 111 [datetime(2000, 1, 1, tzinfo=timezone.utc)], 112 [datetime(2000, 1, 1), datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=6)))], 113 ) 114 yield Case( 115 Annotated[datetime, at.Timezone('Europe/London')], 116 [datetime(2000, 1, 1, tzinfo=timezone(timedelta(0), name='Europe/London'))], 117 [datetime(2000, 1, 1), datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=6)))], 118 ) 119 120 # Quantity 121 122 yield Case(Annotated[float, at.Unit(unit='m')], (5, 4.2), ('5m', '4.2m')) 123 124 # predicate types 125 126 yield Case(at.LowerCase[str], ['abc', 'foobar'], ['', 'A', 'Boom']) 127 yield Case(at.UpperCase[str], ['ABC', 'DEFO'], ['', 'a', 'abc', 'AbC']) 128 yield Case(at.IsDigit[str], ['123'], ['', 'ab', 'a1b2']) 129 yield Case(at.IsAscii[str], ['123', 'foo bar'], ['£100', '😊', 'whatever 👀']) 130 131 yield Case(Annotated[int, at.Predicate(lambda x: x % 2 == 0)], [0, 2, 4], [1, 3, 5]) 132 133 yield Case(at.IsFinite[float], [1.23], [math.nan, math.inf, -math.inf]) 134 yield Case(at.IsNotFinite[float], [math.nan, math.inf], [1.23]) 135 yield Case(at.IsNan[float], [math.nan], [1.23, math.inf]) 136 yield Case(at.IsNotNan[float], [1.23, math.inf], [math.nan]) 137 yield Case(at.IsInfinite[float], [math.inf], [math.nan, 1.23]) 138 yield Case(at.IsNotInfinite[float], [math.nan, 1.23], [math.inf]) 139 140 # check stacked predicates 141 yield Case(at.IsInfinite[Annotated[float, at.Predicate(lambda x: x > 0)]], [math.inf], [-math.inf, 1.23, math.nan]) 142 143 # doc 144 yield Case(Annotated[int, at.doc("A number")], [1, 2], []) 145 146 # custom GroupedMetadata 147 class MyCustomGroupedMetadata(at.GroupedMetadata): 148 def __iter__(self) -> Iterator[at.Predicate]: 149 yield at.Predicate(lambda x: float(x).is_integer()) 150 151 yield Case(Annotated[float, MyCustomGroupedMetadata()], [0, 2.0], [0.01, 1.5]) 152