1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
|
"""Verify type hints behave corectly.
This doesn't have the test_ prefix, since runtime testing isn't particularly useful.
"""
from collections.abc import AsyncGenerator, Generator
from typing import List, NoReturn, Union
from typing_extensions import assert_never, assert_type
import outcome
from outcome import Error, Maybe, Outcome, Value, acapture, capture
class Super:
"""A superclass, inheriting ultimately from object."""
class Sub(Super):
"""A subclass."""
maybe: Maybe[float] = capture(len, [])
assert_type(maybe, Union[Value[float], Error])
# Check that this is immutable.
outcome.__version__ = 'dev' # type: ignore[misc]
def maybe_test_val_first(maybe: Maybe[float]) -> None:
"""Check behaviour of the Maybe annotation, when checking Value first."""
assert_type(maybe, Union[Value[float], Error])
# Check narrowing.
if isinstance(maybe, Value):
assert_type(maybe, Value[float])
assert_type(maybe.value, float)
maybe.error # type: ignore[attr-defined]
else:
assert_type(maybe, Error)
if isinstance(maybe, Error):
assert_type(maybe, Error)
assert_type(maybe.error, BaseException)
maybe.value # type: ignore[attr-defined]
else:
assert_never(maybe)
def maybe_test_err_first(maybe: Maybe[float]) -> None:
"""Check behaviour of the Maybe annotation, when checking Error first."""
assert_type(maybe, Union[Value[float], Error])
# Check narrowing.
if isinstance(maybe, Error):
assert_type(maybe, Error)
assert_type(maybe.error, BaseException)
maybe.value # type: ignore[attr-defined]
else:
assert_type(maybe, Value[float])
if isinstance(maybe, Value):
assert_type(maybe, Value[float])
assert_type(maybe.value, float)
maybe.error # type: ignore[attr-defined]
else:
assert_never(maybe)
def value_variance_test() -> None:
"""Check variance behaves as expected."""
value: Value[Super]
value_sub: Value[Sub] = Value(Sub())
value_super: Value[object] = Value(None)
value = value_sub # Is covariant.
value = value_super # type: ignore[assignment]
def outcome_test() -> None:
"""Test assigning either type to the base class."""
value: Value[List[str]] = Value(['a', 'b', 'c'])
bad_value: Value[float] = Value(48.3)
error: Error = Error(Exception())
outcome_good: Outcome[List[str]] = value
outcome_mismatch: Outcome[bool] = value # type: ignore[assignment]
outcome_err: Outcome[List[str]] = error
assert_type(outcome_good.unwrap(), List[str])
assert_type(outcome_err.unwrap(), List[str])
assert_type(value.unwrap(), List[str])
assert_type(error.unwrap(), NoReturn)
def sync_raises() -> NoReturn:
raise NotImplementedError
def sync_generator_one() -> Generator[int, str, List[str]]:
word: str = (yield 5)
assert len(word) == 3
return ['a', 'b', 'c']
def sync_generator_two() -> Generator[str, int, List[str]]:
three: int = (yield 'house')
assert three.bit_length() == 2
return ['a', 'b', 'c']
def sync_none() -> bool:
return True
def sync_one(param: float) -> int:
return round(param)
def sync_capture_test() -> None:
"""Test synchronous behaviour."""
assert_type(capture(sync_none), Union[Value[bool], Error])
assert_type(capture(sync_one, 3.14), Union[Value[int], Error])
assert_type(capture(sync_one, param=3.14), Union[Value[int], Error])
assert_type(capture(sync_raises), Error)
capture(sync_one) # type: ignore[arg-type, call-arg]
capture(sync_none, 1, 2) # type: ignore[arg-type, call-arg]
async def sync_gen_test() -> None:
"""Check send methods."""
value_one: Value[str] = Value('abc')
value_two: Value[int] = Value(3)
error: Error = Error(Exception())
outcome_one: Outcome[str] = [error, value_one][1]
outcome_two: Outcome[int] = [error, value_two][1]
assert_type(outcome_one.send(sync_generator_one()), int)
assert_type(outcome_two.send(sync_generator_two()), str)
outcome_one.send(sync_generator_two()) # type: ignore[arg-type]
outcome_two.send(sync_generator_one()) # type: ignore[arg-type]
assert_type(value_one.send(sync_generator_one()), int)
assert_type(value_two.send(sync_generator_two()), str)
value_one.send(sync_generator_two()) # type: ignore[arg-type]
value_two.send(sync_generator_one()) # type: ignore[arg-type]
# Error doesn't care.
assert_type(error.send(sync_generator_one()), int)
assert_type(error.send(sync_generator_two()), str)
async def async_none() -> bool:
return True
async def async_raises() -> NoReturn:
raise NotImplementedError
async def async_one(param: float) -> int:
return round(param)
async def async_generator_one() -> AsyncGenerator[int, str]:
assert_type((yield 5), str)
async def async_generator_two() -> AsyncGenerator[str, int]:
assert_type((yield ''), int)
async def async_capture_test() -> None:
"""Test asynchronous behaviour."""
assert_type(await acapture(async_none), Union[Value[bool], Error])
assert_type(await acapture(async_one, 3.14), Union[Value[int], Error])
assert_type(
await acapture(async_one, param=3.14), Union[Value[int], Error]
)
assert_type(await acapture(async_raises), Error)
capture(async_one) # type: ignore[arg-type, call-arg]
capture(async_none, 1, 2) # type: ignore[arg-type, call-arg]
async def async_gen_test() -> None:
value_one: Value[str] = Value('abc')
value_two: Value[int] = Value(3)
error: Error = Error(Exception())
outcome_one: Outcome[str] = [error, value_one][1]
outcome_two: Outcome[int] = [error, value_two][1]
assert_type(await outcome_one.asend(async_generator_one()), int)
assert_type(await outcome_two.asend(async_generator_two()), str)
await outcome_one.asend(async_generator_two()) # type: ignore[arg-type]
await outcome_two.asend(async_generator_one()) # type: ignore[arg-type]
assert_type(await value_one.asend(async_generator_one()), int)
assert_type(await value_two.asend(async_generator_two()), str)
await value_one.asend(async_generator_two()) # type: ignore[arg-type]
await value_two.asend(async_generator_one()) # type: ignore[arg-type]
# Error doesn't care.
assert_type(await error.asend(async_generator_one()), int)
assert_type(await error.asend(async_generator_two()), str)
|