Skip to content

Commit 794a4ec

Browse files
authored
Add GetSpecialAttr. (#61)
Allows getting the `__name__`, `__module__`, `__qualname__` special attributes. ```py d = eval_typing(GetSpecialAttr[int, Literal["__name__"]]) assert d == Literal["int"] d = eval_typing(GetSpecialAttr[list[int], Literal["__name__"]]) assert d == Literal["list"] d = eval_typing(GetSpecialAttr[C, Literal["__name__"]]) assert d == Literal["C"] d = eval_typing(GetSpecialAttr[C, Literal["__module__"]]) assert d == Literal["tests.test_type_eval"] d = eval_typing(GetSpecialAttr[C, Literal["__qualname__"]]) assert d == Literal["test_eval_typename_03.<locals>.C"] ```
1 parent fe3a091 commit 794a4ec

4 files changed

Lines changed: 114 additions & 0 deletions

File tree

pep.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,13 @@ Basic operators
580580
* ``GetMemberType[T, S: Literal[str]]``: Extract the type of the
581581
member named ``S`` from the class ``T``.
582582

583+
584+
* ``GetSpecialAttr[T: type, Attr: Literal[str]]``: Extract the value
585+
of special attribute named ``Attr`` from the class ``T``. Valid
586+
attributes are ``__name__``, ``__module__``, and ``__qualname__``.
587+
Returns the value as a ``Literal[str]``.
588+
589+
583590
* ``Length[T: tuple]`` - get the length of a tuple as an int literal
584591
(or ``Literal[None]`` if it is unbounded)
585592

tests/test_type_eval.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
GetMember,
2929
GetMemberType,
3030
GetName,
31+
GetSpecialAttr,
3132
GetType,
3233
GetAnnotations,
3334
IsSub,
@@ -990,6 +991,85 @@ class Container2[T]: ...
990991
assert eval_typing(GetArg[t, Container, Literal[1]]) == Never
991992

992993

994+
class OuterType:
995+
class InnerType:
996+
pass
997+
998+
999+
def test_eval_typename_01():
1000+
d = eval_typing(GetSpecialAttr[int, Literal["__name__"]])
1001+
assert d == Literal["int"]
1002+
d = eval_typing(GetSpecialAttr[str, Literal["__name__"]])
1003+
assert d == Literal["str"]
1004+
d = eval_typing(GetSpecialAttr[list[int], Literal["__name__"]])
1005+
assert d == Literal["list"]
1006+
d = eval_typing(GetSpecialAttr[list[str], Literal["__name__"]])
1007+
assert d == Literal["list"]
1008+
1009+
class C:
1010+
pass
1011+
1012+
d = eval_typing(GetSpecialAttr[OuterType, Literal["__name__"]])
1013+
assert d == Literal["OuterType"]
1014+
d = eval_typing(GetSpecialAttr[OuterType.InnerType, Literal["__name__"]])
1015+
assert d == Literal["InnerType"]
1016+
d = eval_typing(GetSpecialAttr[C, Literal["__name__"]])
1017+
assert d == Literal["C"]
1018+
1019+
d = eval_typing(GetSpecialAttr[GetA1[int, str], Literal["__name__"]])
1020+
assert d == Literal["int"]
1021+
1022+
1023+
def test_eval_typename_02():
1024+
d = eval_typing(GetSpecialAttr[int, Literal["__module__"]])
1025+
assert d == Literal["builtins"]
1026+
d = eval_typing(GetSpecialAttr[str, Literal["__module__"]])
1027+
assert d == Literal["builtins"]
1028+
d = eval_typing(GetSpecialAttr[list[int], Literal["__module__"]])
1029+
assert d == Literal["builtins"]
1030+
d = eval_typing(GetSpecialAttr[list[str], Literal["__module__"]])
1031+
assert d == Literal["builtins"]
1032+
1033+
class C:
1034+
pass
1035+
1036+
d = eval_typing(GetSpecialAttr[OuterType, Literal["__module__"]])
1037+
assert d == Literal["tests.test_type_eval"]
1038+
d = eval_typing(GetSpecialAttr[OuterType.InnerType, Literal["__module__"]])
1039+
assert d == Literal["tests.test_type_eval"]
1040+
d = eval_typing(GetSpecialAttr[C, Literal["__module__"]])
1041+
assert d == Literal["tests.test_type_eval"]
1042+
1043+
d = eval_typing(GetSpecialAttr[GetA1[int, str], Literal["__module__"]])
1044+
assert d == Literal["builtins"]
1045+
1046+
1047+
def test_eval_typename_03():
1048+
d = eval_typing(GetSpecialAttr[int, Literal["__qualname__"]])
1049+
assert d == Literal["int"]
1050+
d = eval_typing(GetSpecialAttr[str, Literal["__qualname__"]])
1051+
assert d == Literal["str"]
1052+
d = eval_typing(GetSpecialAttr[list[int], Literal["__qualname__"]])
1053+
assert d == Literal["list"]
1054+
d = eval_typing(GetSpecialAttr[list[str], Literal["__qualname__"]])
1055+
assert d == Literal["list"]
1056+
1057+
class C:
1058+
pass
1059+
1060+
d = eval_typing(GetSpecialAttr[OuterType, Literal["__qualname__"]])
1061+
assert d == Literal["OuterType"]
1062+
d = eval_typing(
1063+
GetSpecialAttr[OuterType.InnerType, Literal["__qualname__"]]
1064+
)
1065+
assert d == Literal["OuterType.InnerType"]
1066+
d = eval_typing(GetSpecialAttr[C, Literal["__qualname__"]])
1067+
assert d == Literal["test_eval_typename_03.<locals>.C"]
1068+
1069+
d = eval_typing(GetSpecialAttr[GetA1[int, str], Literal["__qualname__"]])
1070+
assert d == Literal["int"]
1071+
1072+
9931073
type _Works[Ts, I] = Literal[True]
9941074
type Works[Ts] = _Works[Ts, Length[Ts]]
9951075

typemap/type_eval/_eval_operators.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
GetArgs,
2828
GetMember,
2929
GetMemberType,
30+
GetSpecialAttr,
3031
InitField,
3132
IsSubSimilar,
3233
IsSubtype,
@@ -923,6 +924,28 @@ def _eval_GetArgs(tp, base, *, ctx) -> typing.Any:
923924
return tuple[*args] # type: ignore[valid-type]
924925

925926

927+
@type_eval.register_evaluator(GetSpecialAttr)
928+
@_lift_over_unions
929+
def _eval_GetSpecialAttr(tp, attr, *, ctx) -> typing.Any:
930+
if not (
931+
_typing_inspect.is_generic_alias(attr)
932+
and attr.__origin__ is typing.Literal
933+
and isinstance(attr.__args__[0], str)
934+
):
935+
raise TypeError(
936+
f"Invalid type argument to GetSpecialAttr: "
937+
f"{attr} is not a string Literal"
938+
)
939+
if attr.__args__[0] == "__name__":
940+
return typing.Literal[tp.__name__]
941+
elif attr.__args__[0] == "__module__":
942+
return typing.Literal[tp.__module__]
943+
elif attr.__args__[0] == "__qualname__":
944+
return typing.Literal[tp.__qualname__]
945+
else:
946+
return typing.Never
947+
948+
926949
@type_eval.register_evaluator(GetAnnotations)
927950
def _eval_GetAnnotations(tp, *, ctx) -> typing.Any:
928951
tp = _eval_types(tp, ctx=ctx)

typemap/typing.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@ class GetArgs[Tp, Base]:
147147
pass
148148

149149

150+
class GetSpecialAttr[T: type, Attr: str]:
151+
pass
152+
153+
150154
class Length[S: tuple]:
151155
pass
152156

0 commit comments

Comments
 (0)