Skip to content

Commit ff382a7

Browse files
authored
Replace GetAttr with GetMember. (#63)
`GetAttr` currently gets the type of a member. This naming is inconsistent since we distinguish between `Attrs` and `Members`, and also `Attrs` returns `Member` types. - Rename `GetAttr` to `GetMemberType`. - Add `GetMember` to access specific members.
1 parent dbcb7d4 commit ff382a7

7 files changed

Lines changed: 83 additions & 42 deletions

File tree

pep.rst

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -574,8 +574,8 @@ Basic operators
574574
cannot be.
575575

576576

577-
* ``GetAttr[T, S: Literal[str]]``: Extract the type of the member
578-
named ``S`` from the class ``T``.
577+
* ``GetMemberType[T, S: Literal[str]]``: Extract the type of the
578+
member named ``S`` from the class ``T``.
579579

580580
* ``Length[T: tuple]`` - get the length of a tuple as an int literal
581581
(or ``Literal[None]`` if it is unbounded)
@@ -604,6 +604,9 @@ Object inspection
604604
* ``Attrs[T]``: like ``Members[T]`` but only returns attributes (not
605605
methods).
606606

607+
* ``GetMember[T, S: Literal[str]]``: Produces a ``Member`` type for the
608+
member named ``S`` from the class ``T``.
609+
607610
* ``Member[N: Literal[str], T, Q: MemberQuals, Init, D]``: ``Member``,
608611
is a simple type, not an operator, that is used to describe members
609612
of classes. Its type parameters encode the information about each
@@ -901,7 +904,7 @@ type-annotated attribute of ``K``, while calling ``NewProtocol`` with
901904

902905
``GetName`` is a getter operator that fetches the name of a ``Member``
903906
as a literal type--all of these mechanisms lean very heavily on literal types.
904-
``GetAttr`` gets the type of an attribute from a class.
907+
``GetMemberType`` gets the type of an attribute from a class.
905908

906909
::
907910

@@ -914,7 +917,7 @@ as a literal type--all of these mechanisms lean very heavily on literal types.
914917
*[
915918
Member[
916919
GetName[c],
917-
ConvertField[GetAttr[ModelT, GetName[c]]],
920+
ConvertField[GetMemberType[ModelT, GetName[c]]],
918921
]
919922
for c in Iter[Attrs[K]]
920923
]
@@ -1183,7 +1186,8 @@ We could do potentially better but it would require more meachinery.
11831186
* ``Member[T]``, when statically checking a type alias, could be
11841187
treated as having some type like ``tuple[Member[KeyOf[T], object,
11851188
str, ..., ...], ...]``
1186-
* ``GetAttr[T, S: KeyOf[T]]`` - but this isn't supported yet. TS supports it.
1189+
* ``GetMemberType[T, S: KeyOf[T]]`` - but this isn't supported yet.
1190+
TS supports it.
11871191
* We would also need to do context sensitive type bound inference
11881192

11891193

tests/test_fastapilike_2.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
IsSub,
1919
FromUnion,
2020
GetArg,
21-
GetAttr,
21+
GetMemberType,
2222
GetType,
2323
GetName,
2424
GetQuals,
@@ -46,7 +46,7 @@ class Field[T: FieldArgs](InitField[T]):
4646
####
4747

4848
# TODO: Should this go into the stdlib?
49-
type GetFieldItem[T: InitField, K] = GetAttr[
49+
type GetFieldItem[T: InitField, K] = GetMemberType[
5050
GetArg[T, InitField, Literal[0]], K
5151
]
5252

tests/test_qblike.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
GetType,
1717
Member,
1818
GetName,
19-
GetAttr,
19+
GetMemberType,
2020
GetArg,
2121
)
2222

@@ -49,7 +49,7 @@ def select[K: BaseTypedDict](
4949
*[
5050
Member[
5151
GetName[c],
52-
FilterLinks[GetAttr[A, GetName[c]]],
52+
FilterLinks[GetMemberType[A, GetName[c]]],
5353
]
5454
for c in Iter[Attrs[K]]
5555
]
@@ -118,7 +118,7 @@ class select[...]:
118118
z: tests.test_qblike.Link[tests.test_qblike.PropsOnly[tests.test_qblike.Tgt]]
119119
""")
120120

121-
res = eval_typing(GetAttr[ret, Literal["z"]])
121+
res = eval_typing(GetMemberType[ret, Literal["z"]])
122122
tgt = res.__args__[0]
123123
# XXX: this should probably be pre-evaluated already?
124124
tgt = eval_typing(tgt)

tests/test_qblike_2.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
GetType,
1313
Member,
1414
GetName,
15-
GetAttr,
15+
GetMemberType,
1616
GetArg,
1717
)
1818

@@ -58,7 +58,7 @@ class MultiLink[T](Link[T]):
5858
5959
``GetName`` is a getter operator that fetches the name of a ``Member``
6060
as a literal type--all of these mechanisms lean very heavily on literal types.
61-
``GetAttr`` gets the type of an attribute from a class.
61+
``GetMemberType`` gets the type of an attribute from a class.
6262
6363
"""
6464

@@ -72,7 +72,7 @@ def select[ModelT, K: BaseTypedDict](
7272
*[
7373
Member[
7474
GetName[c],
75-
ConvertField[GetAttr[ModelT, GetName[c]]],
75+
ConvertField[GetMemberType[ModelT, GetName[c]]],
7676
]
7777
for c in Iter[Attrs[K]]
7878
]
@@ -198,7 +198,7 @@ class select[...]:
198198
posts: list[tests.test_qblike_2.PropsOnly[tests.test_qblike_2.Post]]
199199
""")
200200

201-
res = eval_typing(GetAttr[ret, Literal["posts"]])
201+
res = eval_typing(GetMemberType[ret, Literal["posts"]])
202202
tgt = res.__args__[0]
203203
# XXX: this should probably be pre-evaluated already?
204204
fmt = format_helper.format_class(tgt)

tests/test_type_eval.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
GenericCallable,
2626
GetArg,
2727
GetArgs,
28-
GetAttr,
28+
GetMember,
29+
GetMemberType,
2930
GetName,
3031
GetType,
3132
GetAnnotations,
@@ -198,27 +199,27 @@ def test_eval_arg_order():
198199

199200

200201
def test_type_getattr_union_1():
201-
d = eval_typing(GetAttr[TA | TB, Literal["x"]])
202+
d = eval_typing(GetMemberType[TA | TB, Literal["x"]])
202203
assert d == int | str
203204

204205

205206
def test_type_getattr_union_2():
206-
d = eval_typing(GetAttr[TA, Literal["x"] | Literal["y"]])
207+
d = eval_typing(GetMemberType[TA, Literal["x"] | Literal["y"]])
207208
assert d == int | list[float]
208209

209210

210211
def test_type_getattr_union_3():
211-
d = eval_typing(GetAttr[TA | TB, Literal["x"] | Literal["y"]])
212+
d = eval_typing(GetMemberType[TA | TB, Literal["x"] | Literal["y"]])
212213
assert d == int | list[float] | str | list[object]
213214

214215

215216
def test_type_getattr_union_4():
216-
d = eval_typing(GetAttr[TA, Literal["x", "y"]])
217+
d = eval_typing(GetMemberType[TA, Literal["x", "y"]])
217218
assert d == int | list[float]
218219

219220

220221
def test_type_getattr_union_5():
221-
d = eval_typing(GetAttr[TA, Literal["x", "y"] | Literal["z"]])
222+
d = eval_typing(GetMemberType[TA, Literal["x", "y"] | Literal["z"]])
222223
assert d == int | list[float] | TB
223224

224225

@@ -379,6 +380,21 @@ def test_type_from_union_06():
379380
assert _is_generic_permutation(n, tuple[int, list[IntTree]])
380381

381382

383+
def test_getmember_01():
384+
d = eval_typing(GetMember[TA, Literal["x"]])
385+
assert d == Member[Literal["x"], int, Never, Never, TA]
386+
d = eval_typing(GetMemberType[TA, Literal["a"]])
387+
assert d == Never
388+
389+
d = eval_typing(GetMember[TA | TB, Literal["x"]])
390+
assert d == (
391+
Member[Literal["x"], int, Never, Never, TA]
392+
| Member[Literal["x"], str, Never, Never, TB]
393+
)
394+
d = eval_typing(GetMember[TA | TB, Literal[""]])
395+
assert d == Never
396+
397+
382398
def test_getarg_never():
383399
d = eval_typing(GetArg[Never, object, Literal[0]])
384400
assert d is Never
@@ -1537,15 +1553,15 @@ class AnnoTest:
15371553

15381554

15391555
def test_type_eval_annotated_02():
1540-
res = eval_typing(IsSub[GetAttr[AnnoTest, Literal["a"]], int])
1556+
res = eval_typing(IsSub[GetMemberType[AnnoTest, Literal["a"]], int])
15411557
assert res == _BoolLiteral[True]
15421558

15431559

15441560
def test_type_eval_annotated_03():
1545-
res = eval_typing(Uppercase[GetAttr[AnnoTest, Literal["b"]]])
1561+
res = eval_typing(Uppercase[GetMemberType[AnnoTest, Literal["b"]]])
15461562
assert res == Literal["TEST"]
15471563

15481564

15491565
def test_type_eval_annotated_04():
1550-
res = eval_typing(GetAnnotations[GetAttr[AnnoTest, Literal["b"]]])
1566+
res = eval_typing(GetAnnotations[GetMemberType[AnnoTest, Literal["b"]]])
15511567
assert res == Literal["blah"]

typemap/type_eval/_eval_operators.py

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
GetAnnotations,
2626
GetArg,
2727
GetArgs,
28-
GetAttr,
28+
GetMember,
29+
GetMemberType,
2930
InitField,
3031
IsSubSimilar,
3132
IsSubtype,
@@ -657,19 +658,20 @@ def _get_function_hint_namespaces(func, receiver_type=None):
657658
return globalns, localns
658659

659660

661+
def _hint_to_member(n, t, qs, init, d, *, ctx):
662+
return Member[
663+
typing.Literal[n],
664+
_eval_types(t, ctx),
665+
_mk_literal_union(*qs),
666+
init,
667+
d,
668+
]
669+
670+
660671
def _hints_to_members(hints, ctx):
661672
"""Convert a hints dictionary to a tuple of Member types."""
662673
return tuple[
663-
*[
664-
Member[
665-
typing.Literal[n],
666-
_eval_types(t, ctx),
667-
_mk_literal_union(*qs),
668-
init,
669-
d,
670-
]
671-
for n, (t, qs, init, d) in hints.items()
672-
]
674+
*[_hint_to_member(n, *hint, ctx=ctx) for n, hint in hints.items()]
673675
]
674676

675677

@@ -690,6 +692,21 @@ def _eval_Members(tp, *, ctx):
690692
return _hints_to_members(hints, ctx)
691693

692694

695+
@type_eval.register_evaluator(GetMember)
696+
@_lift_over_unions
697+
def _eval_GetMember(tp, prop, *, ctx):
698+
# XXX: extras?
699+
name = _from_literal(prop)
700+
hints = {
701+
**get_annotated_type_hints(tp, include_extras=True),
702+
**get_annotated_method_hints(tp),
703+
}
704+
if name in hints:
705+
return _hint_to_member(name, *hints[name], ctx=ctx)
706+
else:
707+
return typing.Never
708+
709+
693710
##################################################################
694711

695712

@@ -705,9 +722,9 @@ def _eval_FromUnion(tp, *, ctx):
705722
##################################################################
706723

707724

708-
@type_eval.register_evaluator(GetAttr)
725+
@type_eval.register_evaluator(GetMemberType)
709726
@_lift_over_unions
710-
def _eval_GetAttr(tp, prop, *, ctx):
727+
def _eval_GetMemberType(tp, prop, *, ctx):
711728
# XXX: extras?
712729
name = _from_literal(prop)
713730
hints = {

typemap/typing.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,11 @@ class Param[N: str | None, T, Q: ParamQuals = typing.Never]:
112112
type KwargsParam[T] = Param[Literal[None], T, Literal["**"]]
113113

114114

115-
type GetName[T: Member | Param] = GetAttr[T, Literal["name"]]
116-
type GetType[T: Member | Param] = GetAttr[T, Literal["typ"]]
117-
type GetQuals[T: Member | Param] = GetAttr[T, Literal["quals"]]
118-
type GetInit[T: Member] = GetAttr[T, Literal["init"]]
119-
type GetDefiner[T: Member] = GetAttr[T, Literal["definer"]]
115+
type GetName[T: Member | Param] = GetMemberType[T, Literal["name"]]
116+
type GetType[T: Member | Param] = GetMemberType[T, Literal["typ"]]
117+
type GetQuals[T: Member | Param] = GetMemberType[T, Literal["quals"]]
118+
type GetInit[T: Member] = GetMemberType[T, Literal["init"]]
119+
type GetDefiner[T: Member] = GetMemberType[T, Literal["definer"]]
120120

121121

122122
class Attrs[T]:
@@ -131,7 +131,11 @@ class FromUnion[T]:
131131
pass
132132

133133

134-
class GetAttr[Lhs, Prop]:
134+
class GetMember[Lhs, Prop]:
135+
pass
136+
137+
138+
class GetMemberType[Lhs, Prop]:
135139
pass
136140

137141

0 commit comments

Comments
 (0)