Skip to content

Commit c4ceb7f

Browse files
committed
Add GetMember.
1 parent 419bfb8 commit c4ceb7f

4 files changed

Lines changed: 50 additions & 10 deletions

File tree

pep.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,9 @@ Object inspection
540540
* ``Attrs[T]``: like ``Members[T]`` but only returns attributes (not
541541
methods).
542542

543+
* ``GetMember[T, S: Literal[str]]``: Produces a ``Member`` type for the
544+
member named ``S`` from the class ``T``.
545+
543546
* ``Member[N: Literal[str], T, Q: MemberQuals, Init, D]``: ``Member``,
544547
is a simple type, not an operator, that is used to describe members
545548
of classes. Its type parameters encode the information about each

tests/test_type_eval.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
GenericCallable,
2626
GetArg,
2727
GetArgs,
28+
GetMember,
2829
GetMemberType,
2930
GetName,
3031
GetType,
@@ -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

typemap/type_eval/_eval_operators.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
GetAnnotations,
2626
GetArg,
2727
GetArgs,
28+
GetMember,
2829
GetMemberType,
2930
InitField,
3031
IsSubSimilar,
@@ -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

typemap/typing.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ class FromUnion[T]:
131131
pass
132132

133133

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

0 commit comments

Comments
 (0)