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
4 changes: 3 additions & 1 deletion pep.rst
Original file line number Diff line number Diff line change
Expand Up @@ -778,10 +778,12 @@ All of the operators in this section are :ref:`lifted over union types
Raise error
'''''''''''

* ``RaiseError[S: Literal[str]]``: If this type needs to be evaluated
* ``RaiseError[S: Literal[str], *Ts]``: If this type needs to be evaluated
to determine some actual type, generate a type error with the
provided message.

Any additional type arguments should be included in the message.

Update class
''''''''''''

Expand Down
2 changes: 1 addition & 1 deletion tests/test_fastapilike_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class _Default:

type AddInit[T] = NewProtocol[
InitFnType[T],
*Members[T]],
*Members[T],
]

but we struggle here because typing wants to unpack the Members tuple
Expand Down
26 changes: 26 additions & 0 deletions tests/test_type_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -1565,3 +1565,29 @@ def test_type_eval_annotated_03():
def test_type_eval_annotated_04():
res = eval_typing(GetAnnotations[GetMemberType[AnnoTest, Literal["b"]]])
assert res == Literal["blah"]


##############
# RaiseError tests

from typemap.typing import RaiseError
from typemap.type_eval import TypeMapError


def test_raise_error_basic():
with pytest.raises(TypeMapError, match="Test error message"):
eval_typing(RaiseError[Literal["Test error message"]])


def test_raise_error_with_types():
with pytest.raises(TypeMapError, match="Broadcast mismatch.*int.*str"):
eval_typing(RaiseError[Literal["Broadcast mismatch"], int, str])


def test_raise_error_with_literal_types():
with pytest.raises(
TypeMapError, match="Shape mismatch.*Literal\\[4\\].*Literal\\[3\\]"
):
eval_typing(
RaiseError[Literal["Shape mismatch"], Literal[4], Literal[3]]
)
3 changes: 3 additions & 0 deletions typemap/type_eval/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
# This one is imported for registering handlers
from . import _eval_operators # noqa

from ._eval_operators import TypeMapError


__all__ = (
"eval_typing",
Expand All @@ -23,6 +25,7 @@
"flatten_class",
"issubtype",
"issubsimilar",
"TypeMapError",
"_EvalProxy",
"_get_current_context",
)
25 changes: 25 additions & 0 deletions typemap/type_eval/_eval_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
Members,
NewProtocol,
Param,
RaiseError,
Slice,
SpecialFormEllipsis,
StrConcat,
Expand Down Expand Up @@ -997,6 +998,30 @@ def func(*args, ctx):
##################################################################


class TypeMapError(TypeError):
"""Exception raised when RaiseError is evaluated."""

pass


@type_eval.register_evaluator(RaiseError)
@_lift_evaluated
def _eval_RaiseError(msg, *extra_types, ctx):
"""Evaluate RaiseError by raising a TypeMapError.

RaiseError[S, *Ts] raises a type error with message S,
including the string representations of any extra type arguments.
"""
message = _from_literal(msg)
if extra_types:
type_strs = ", ".join(repr(t) for t in extra_types)
message = f"{message}: {type_strs}"
raise TypeMapError(message)


##################################################################


def _add_quals(typ, quals):
for qual in (typing.ClassVar, typing.Final):
if type_eval.issubsimilar(typing.Literal[qual.__name__], quals):
Expand Down
13 changes: 13 additions & 0 deletions typemap/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,19 @@ class NewProtocol[*T]:
pass


class RaiseError[S: str, *Ts]:
"""Raise a type error with the given message when evaluated.

RaiseError[S: Literal[str], *Ts]: If this type needs to be evaluated
to determine some actual type, generate a type error with the
provided message.

Any additional type arguments should be included in the message.
"""

pass


##################################################################

# TODO: type better
Expand Down