Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 55 additions & 64 deletions tests/test_type_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import pytest

from typemap.type_eval import _ensure_context, eval_typing
from typemap.type_eval._eval_operators import TypeMapError
from typemap.typing import _BoolLiteral

from typemap_extensions import (
Expand Down Expand Up @@ -414,8 +415,8 @@ def test_getmember_01():
Member[Literal["x"], int, Never, Never, TA]
| Member[Literal["x"], str, Never, Never, TB]
)
d = eval_typing(GetMember[TA | TB, Literal[""]])
assert d == Never
with pytest.raises(TypeMapError):
eval_typing(GetMember[TA | TB, Literal[""]])


type OnlyIntToSet[T] = set[T] if IsAssignable[T, int] else T
Expand Down Expand Up @@ -727,6 +728,8 @@ class B(A):


def test_getarg_never():
# Never decomposes to an empty union, so the operator never runs
# and _mk_union returns Never
d = eval_typing(GetArg[Never, object, Literal[0]])
assert d is Never

Expand Down Expand Up @@ -830,8 +833,8 @@ def test_eval_getarg_callable_02():
gc = GenericCallable[tuple[T], f]
t = eval_typing(GetArg[gc, GenericCallable, Literal[0]])
assert t == tuple[T]
gc_f = eval_typing(GetArg[gc, GenericCallable, Literal[1]])
assert gc_f == Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[gc, GenericCallable, Literal[1]])

# Params wrapped
f = Callable[
Expand All @@ -848,8 +851,8 @@ def test_eval_getarg_callable_02():
]
t = eval_typing(GetArg[gc, GenericCallable, Literal[0]])
assert t == tuple[T]
gc_f = eval_typing(GetArg[gc, GenericCallable, Literal[1]])
assert gc_f == Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[gc, GenericCallable, Literal[1]])


type IndirectProtocol[T] = NewProtocol[*[m for m in Iter[Members[T]]],]
Expand Down Expand Up @@ -996,8 +999,8 @@ def f[T](self, x: T, /, y: T, *, z: T) -> T: ...
_T = eval_typing(
GetArg[GetArg[gc, GenericCallable, Literal[0]], tuple, Literal[0]]
)
f = eval_typing(GetArg[gc, GenericCallable, Literal[1]])
assert f is Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[gc, GenericCallable, Literal[1]])


def test_eval_getarg_callable_08():
Expand All @@ -1010,8 +1013,8 @@ def f[T](cls, x: T, /, y: T, *, z: T) -> T: ...
_T = eval_typing(
GetArg[GetArg[gc, GenericCallable, Literal[0]], tuple, Literal[0]]
)
f = eval_typing(GetArg[gc, GenericCallable, Literal[1]])
assert f is Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[gc, GenericCallable, Literal[1]])


def test_eval_getarg_callable_09():
Expand All @@ -1024,8 +1027,8 @@ def f[T](x: T, /, y: T, *, z: T) -> T: ...
_T = eval_typing(
GetArg[GetArg[gc, GenericCallable, Literal[0]], tuple, Literal[0]]
)
f = eval_typing(GetArg[gc, GenericCallable, Literal[1]])
assert f is Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[gc, GenericCallable, Literal[1]])


def test_eval_getarg_tuple():
Expand Down Expand Up @@ -1108,37 +1111,10 @@ def test_eval_getarg_list():
assert arg == Any

# indexing with 1 always fails
t = list[int]
arg = eval_typing(GetArg[t, list, Literal[1]])
assert arg == Never

t = List[int]
arg = eval_typing(GetArg[t, list, Literal[1]])
assert arg == Never

t = list
arg = eval_typing(GetArg[t, list, Literal[1]])
assert arg == Never

t = List
arg = eval_typing(GetArg[t, list, Literal[1]])
assert arg == Never

t = list[int]
arg = eval_typing(GetArg[t, List, Literal[1]])
assert arg == Never

t = List[int]
arg = eval_typing(GetArg[t, List, Literal[1]])
assert arg == Never

t = list
arg = eval_typing(GetArg[t, List, Literal[1]])
assert arg == Never

t = List
arg = eval_typing(GetArg[t, List, Literal[1]])
assert arg == Never
for t in [list[int], List[int], list, List]:
for base in [list, List]:
with pytest.raises(TypeMapError):
eval_typing(GetArg[t, base, Literal[1]])


@pytest.mark.xfail(reason="Should this work?")
Expand All @@ -1160,12 +1136,14 @@ class A[T]:
t = A[int]
assert eval_typing(GetArg[t, A, Literal[0]]) is int
assert eval_typing(GetArg[t, A, Literal[-1]]) is int
assert eval_typing(GetArg[t, A, Literal[1]]) == Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[t, A, Literal[1]])

t = A
assert eval_typing(GetArg[t, A, Literal[0]]) == Any
assert eval_typing(GetArg[t, A, Literal[-1]]) == Any
assert eval_typing(GetArg[t, A, Literal[1]]) == Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[t, A, Literal[1]])


def test_eval_getarg_custom_02():
Expand All @@ -1177,12 +1155,14 @@ class A(Generic[T]):
t = A[int]
assert eval_typing(GetArg[t, A, Literal[0]]) is int
assert eval_typing(GetArg[t, A, Literal[-1]]) is int
assert eval_typing(GetArg[t, A, Literal[1]]) == Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[t, A, Literal[1]])

t = A
assert eval_typing(GetArg[t, A, Literal[0]]) == Any
assert eval_typing(GetArg[t, A, Literal[-1]]) == Any
assert eval_typing(GetArg[t, A, Literal[1]]) == Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[t, A, Literal[1]])


def test_eval_getarg_custom_03():
Expand All @@ -1192,12 +1172,14 @@ class A[T = str]:
t = A[int]
assert eval_typing(GetArg[t, A, Literal[0]]) is int
assert eval_typing(GetArg[t, A, Literal[-1]]) is int
assert eval_typing(GetArg[t, A, Literal[1]]) == Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[t, A, Literal[1]])

t = A
assert eval_typing(GetArg[t, A, Literal[0]]) is str
assert eval_typing(GetArg[t, A, Literal[-1]]) is str
assert eval_typing(GetArg[t, A, Literal[1]]) == Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[t, A, Literal[1]])


def test_eval_getarg_custom_04():
Expand All @@ -1209,12 +1191,14 @@ class A(Generic[T]):
t = A[int]
assert eval_typing(GetArg[t, A, Literal[0]]) is int
assert eval_typing(GetArg[t, A, Literal[-1]]) is int
assert eval_typing(GetArg[t, A, Literal[1]]) == Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[t, A, Literal[1]])

t = A
assert eval_typing(GetArg[t, A, Literal[0]]) is str
assert eval_typing(GetArg[t, A, Literal[-1]]) is str
assert eval_typing(GetArg[t, A, Literal[1]]) == Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[t, A, Literal[1]])


TestTypeVar = TypeVar("TestTypeVar")
Expand All @@ -1228,12 +1212,14 @@ class ATree(Generic[TestTypeVar]):
t = ATree[int]
assert eval_typing(GetArg[t, ATree, Literal[0]]) is int
assert eval_typing(GetArg[t, ATree, Literal[-1]]) is int
assert eval_typing(GetArg[t, ATree, Literal[1]]) == Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[t, ATree, Literal[1]])

t = ATree
assert eval_typing(GetArg[t, ATree, Literal[0]]) is Any
assert eval_typing(GetArg[t, ATree, Literal[-1]]) is Any
assert eval_typing(GetArg[t, ATree, Literal[1]]) == Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[t, ATree, Literal[1]])


def test_eval_getarg_custom_06():
Expand All @@ -1246,12 +1232,14 @@ class ATree(Generic[A]):
t = ATree[int]
assert eval_typing(GetArg[t, ATree, Literal[0]]) is int
assert eval_typing(GetArg[t, ATree, Literal[-1]]) is int
assert eval_typing(GetArg[t, ATree, Literal[1]]) == Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[t, ATree, Literal[1]])

t = ATree
assert eval_typing(GetArg[t, ATree, Literal[0]]) is Any
assert eval_typing(GetArg[t, ATree, Literal[-1]]) is Any
assert eval_typing(GetArg[t, ATree, Literal[1]]) == Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[t, ATree, Literal[1]])


# Doubly recursive generic types
Expand All @@ -1275,12 +1263,14 @@ def test_eval_getarg_custom_07():
t = ABTree[int, str]
assert eval_typing(GetArg[t, ABTree, Literal[0]]) is int
assert eval_typing(GetArg[t, ABTree, Literal[1]]) is str
assert eval_typing(GetArg[t, ABTree, Literal[2]]) == Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[t, ABTree, Literal[2]])

t = ABTree
assert eval_typing(GetArg[t, ABTree, Literal[0]]) is Any
assert eval_typing(GetArg[t, ABTree, Literal[1]]) is Any
assert eval_typing(GetArg[t, ABTree, Literal[2]]) == Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[t, ABTree, Literal[2]])


T = TypeVar("T")
Expand All @@ -1302,12 +1292,14 @@ def test_eval_getarg_custom_08():
t = Container[int]
assert eval_typing(GetArg[t, Container, Literal[0]]) is int
assert eval_typing(GetArg[t, Container, Literal[-1]]) is int
assert eval_typing(GetArg[t, Container, Literal[1]]) == Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[t, Container, Literal[1]])

t = Container
assert eval_typing(GetArg[t, Container, Literal[0]]) is Any
assert eval_typing(GetArg[t, Container, Literal[-1]]) is Any
assert eval_typing(GetArg[t, Container, Literal[1]]) == Never
with pytest.raises(TypeMapError):
eval_typing(GetArg[t, Container, Literal[1]])


def test_eval_getargs_generic_callable_01():
Expand Down Expand Up @@ -1796,10 +1788,10 @@ def test_eval_slice_02():


def test_eval_slice_03():
d = eval_typing(Slice[int, Literal[1], Literal[2]])
assert d == Never
d = eval_typing(Slice[dict[int, str], Literal[1], Literal[2]])
assert d == Never
with pytest.raises(TypeMapError):
eval_typing(Slice[int, Literal[1], Literal[2]])
with pytest.raises(TypeMapError):
eval_typing(Slice[dict[int, str], Literal[1], Literal[2]])


def test_eval_literal_idempotent_01():
Expand Down Expand Up @@ -2679,7 +2671,6 @@ def test_type_eval_annotated_04():
# RaiseError tests

from typemap_extensions import RaiseError
from typemap.type_eval import TypeMapError


def test_raise_error_basic():
Expand Down
18 changes: 11 additions & 7 deletions typemap/type_eval/_eval_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -879,7 +879,7 @@ def _eval_GetMember(tp, prop, *, ctx):
if name in hints:
return _hint_to_member(name, *hints[name], ctx=ctx)
else:
return typing.Never
raise TypeMapError(f"GetMember: {name!r} not found in {tp!r}")


##################################################################
Expand Down Expand Up @@ -1077,18 +1077,20 @@ def _eval_GetArg(tp, base, idx, *, ctx) -> typing.Any:
base_head = _typing_inspect.get_head(base)
args = _get_args(tp, base_head, ctx)
if args is None:
return typing.Never
raise TypeMapError(f"GetArg: {tp!r} is not a subclass of {base!r}")

try:
idx_val = _eval_literal(idx, ctx)

if base_head is GenericCallable and idx_val >= 1:
# Disallow access to callable lambda
return typing.Never
raise TypeMapError(
f"GetArg: cannot access index {idx_val} of GenericCallable"
)

return _fix_type(args[idx_val])
except IndexError:
return typing.Never
raise TypeMapError(f"GetArg: index out of range for {tp!r} as {base!r}")


@type_eval.register_evaluator(GetArgs)
Expand All @@ -1097,7 +1099,7 @@ def _eval_GetArgs(tp, base, *, ctx) -> typing.Any:
base_head = _typing_inspect.get_head(base)
args = _get_args(tp, base_head, ctx)
if args is None:
return typing.Never
raise TypeMapError(f"GetArgs: {tp!r} is not a subclass of {base!r}")

if base_head is GenericCallable:
# Disallow access to callable lambda
Expand Down Expand Up @@ -1125,7 +1127,9 @@ def _eval_GetSpecialAttr(tp, attr, *, ctx) -> typing.Any:
elif attr.__args__[0] == "__qualname__":
return typing.Literal[tp.__qualname__]
else:
return typing.Never
raise TypeMapError(
f"GetSpecialAttr: {attr.__args__[0]!r} is not a valid attribute"
)


@type_eval.register_evaluator(GetAnnotations)
Expand Down Expand Up @@ -1178,7 +1182,7 @@ def _eval_Slice(tp, start, end, *, ctx):
):
return tp.__origin__[tp.__args__[0][start:end]]
else:
return typing.Never
raise TypeMapError(f"Slice: {tp!r} is not a tuple or string Literal")


# String literals
Expand Down
Loading