Skip to content
Merged
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
43 changes: 40 additions & 3 deletions tests/test_type_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@
Members,
NewProtocol,
Param,
Slice,
SpecialFormEllipsis,
StrConcat,
StrSlice,
Uppercase,
)

Expand Down Expand Up @@ -240,12 +240,12 @@ def test_type_strings_4():


def test_type_strings_5():
d = eval_typing(StrSlice[Literal["abcd"], Literal[0], Literal[1]])
d = eval_typing(Slice[Literal["abcd"], Literal[0], Literal[1]])
assert d == Literal["a"]


def test_type_strings_6():
d = eval_typing(StrSlice[Literal["abcd"], Literal[1], Literal[None]])
d = eval_typing(Slice[Literal["abcd"], Literal[1], Literal[None]])
assert d == Literal["bcd"]


Expand Down Expand Up @@ -1043,6 +1043,43 @@ def test_eval_length_01():
assert d == Literal[None]


def test_eval_slice_01():
t = tuple[Literal[0], Literal[1], Literal[2], Literal[3], Literal[4]]
d = eval_typing(Slice[t, Literal[1], Literal[3]])
assert d == tuple[Literal[1], Literal[2]]
d = eval_typing(Slice[t, Literal[1], Literal[None]])
assert d == tuple[Literal[1], Literal[2], Literal[3], Literal[4]]
d = eval_typing(Slice[t, Literal[None], Literal[3]])
assert d == tuple[Literal[0], Literal[1], Literal[2]]
d = eval_typing(Slice[t, Literal[None], Literal[None]])
assert (
d == tuple[Literal[0], Literal[1], Literal[2], Literal[3], Literal[4]]
)
d = eval_typing(Slice[t, Literal[1], Literal[1]])
assert d == tuple[()]


def test_eval_slice_02():
t = Literal["abcde"]
d = eval_typing(Slice[t, Literal[1], Literal[3]])
assert d == Literal["bc"]
d = eval_typing(Slice[t, Literal[1], Literal[None]])
assert d == Literal["bcde"]
d = eval_typing(Slice[t, Literal[None], Literal[3]])
assert d == Literal["abc"]
d = eval_typing(Slice[t, Literal[None], Literal[None]])
assert d == Literal["abcde"]
d = eval_typing(Slice[t, Literal[1], Literal[1]])
assert d == Literal[""]


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


def test_eval_literal_idempotent_01():
t = Literal[int]
for _ in range(5):
Expand Down
21 changes: 19 additions & 2 deletions typemap/type_eval/_eval_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@
Members,
NewProtocol,
Param,
Slice,
SpecialFormEllipsis,
StrConcat,
StrSlice,
Uncapitalize,
Uppercase,
)
Expand Down Expand Up @@ -896,6 +896,24 @@ def _eval_Length(tp, *, ctx) -> typing.Any:
raise TypeError(f"Invalid type argument to Length: {tp} is not a tuple")


@type_eval.register_evaluator(Slice)
@_lift_over_unions
def _eval_Slice(tp, start, end, *, ctx):
tp = _eval_types(tp, ctx)
start = _eval_literal(start, ctx)
end = _eval_literal(end, ctx)
if _typing_inspect.is_generic_alias(tp) and tp.__origin__ is tuple:
return tp.__origin__[tp.__args__[start:end]]
elif (
_typing_inspect.is_generic_alias(tp)
and tp.__origin__ is typing.Literal
and isinstance(tp.__args__[0], str)
):
return tp.__origin__[tp.__args__[0][start:end]]
else:
return typing.Never


# String literals


Expand All @@ -912,7 +930,6 @@ def func(*args, ctx):
_string_literal_op(Capitalize, op=str.capitalize)
_string_literal_op(Uncapitalize, op=lambda s: s[0:1].lower() + s[1:])
_string_literal_op(StrConcat, op=lambda s, t: s + t)
_string_literal_op(StrSlice, op=lambda s, start, end: s[start:end])


##################################################################
Expand Down
8 changes: 4 additions & 4 deletions typemap/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ class Length[S: tuple]:
pass


class Slice[S: str | tuple, Start: int | None, End: int | None]:
pass


class Uppercase[S: str]:
pass

Expand All @@ -167,10 +171,6 @@ class StrConcat[S: str, T: str]:
pass


class StrSlice[S: str, Start: int | None, End: int | None]:
pass


class NewProtocol[*T]:
pass

Expand Down