Skip to content

Commit 8c979fa

Browse files
committed
Demo: experiment with supporting dot notation for Member
This adds a general mechanism of allowing dot notation to access type aliases defined in a class if the class inherits from `typing.SupportAliases`. I updated one of the tests for it. Thoughts?
1 parent 14537c2 commit 8c979fa

3 files changed

Lines changed: 96 additions & 31 deletions

File tree

tests/test_fastapilike_2.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@ class Field[T: FieldArgs](typing.InitField[T]):
5252
type Public[T] = typing.NewProtocol[
5353
*[
5454
typing.Member[
55-
typing.GetName[p],
56-
FixPublicType[typing.GetType[p], typing.GetInit[p]],
57-
typing.GetQuals[p],
55+
p.name,
56+
FixPublicType[p.type, p.init],
57+
p.quals,
5858
]
5959
for p in typing.Iter[typing.Attrs[T]]
6060
if not typing.IsSub[
61-
Literal[True], GetFieldItem[typing.GetInit[p], Literal["hidden"]]
61+
Literal[True], GetFieldItem[p.init, Literal["hidden"]]
6262
]
6363
]
6464
]
@@ -82,15 +82,15 @@ class Field[T: FieldArgs](typing.InitField[T]):
8282
type Create[T] = typing.NewProtocol[
8383
*[
8484
typing.Member[
85-
typing.GetName[p],
86-
typing.GetType[p],
87-
typing.GetQuals[p],
88-
GetDefault[typing.GetInit[p]],
85+
p.name,
86+
p.type,
87+
p.quals,
88+
GetDefault[p.init],
8989
]
9090
for p in typing.Iter[typing.Attrs[T]]
9191
if not typing.IsSub[
9292
Literal[True],
93-
GetFieldItem[typing.GetInit[p], Literal["primary_key"]],
93+
GetFieldItem[p.init, Literal["primary_key"]],
9494
]
9595
]
9696
]
@@ -116,15 +116,15 @@ class Field[T: FieldArgs](typing.InitField[T]):
116116
type Update[T] = typing.NewProtocol[
117117
*[
118118
typing.Member[
119-
typing.GetName[p],
120-
typing.GetType[p] | None,
121-
typing.GetQuals[p],
119+
p.name,
120+
p.type | None,
121+
p.quals,
122122
Literal[None],
123123
]
124124
for p in typing.Iter[typing.Attrs[T]]
125125
if not typing.IsSub[
126126
Literal[True],
127-
GetFieldItem[typing.GetInit[p], Literal["primary_key"]],
127+
GetFieldItem[p.init, Literal["primary_key"]],
128128
]
129129
]
130130
]
@@ -141,13 +141,13 @@ class Field[T: FieldArgs](typing.InitField[T]):
141141
typing.Param[Literal["self"], Self],
142142
*[
143143
typing.Param[
144-
typing.GetName[p],
145-
typing.GetType[p],
144+
p.name,
145+
p.type,
146146
# All arguments are keyword-only
147147
# It takes a default if a default is specified in the class
148148
Literal["keyword"]
149149
if typing.IsSub[
150-
GetDefault[typing.GetInit[p]],
150+
GetDefault[p.init],
151151
Never,
152152
]
153153
else Literal["keyword", "default"],

typemap/type_eval/_eval_typing.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
_UnpackGenericAlias as typing_UnpackGenericAlias,
1919
)
2020

21+
from typemap.typing import _NestedGenericAlias
22+
2123

2224
if typing.TYPE_CHECKING:
2325
from typing import Any, Sequence
@@ -453,6 +455,42 @@ def _eval_applied_type_alias(obj: types.GenericAlias, ctx: EvalContext):
453455
return evaled
454456

455457

458+
@_eval_types_impl.register
459+
def _eval_nested_generic_alias(obj: _NestedGenericAlias, ctx: EvalContext):
460+
base, alias = obj.__args__
461+
462+
# TODO: what if it has parameters of its own
463+
464+
func = alias.evaluate_value
465+
466+
# obj.__args__ matches the declared parameter order, but args are expected
467+
# to be in the same order as func.__code__.co_freevars.
468+
args_by_name = dict(
469+
zip(
470+
(p.__name__ for p in base.__origin__.__type_params__),
471+
base.__args__,
472+
strict=False,
473+
)
474+
)
475+
476+
args = tuple(
477+
types.CellType(_eval_types(args_by_name[name], ctx))
478+
if name in args_by_name
479+
else value
480+
for name, value in zip(
481+
func.__code__.co_freevars, func.__closure__, strict=False
482+
)
483+
)
484+
mod = sys.modules[obj.__module__]
485+
486+
ff = types.FunctionType(func.__code__, mod.__dict__, None, None, args)
487+
unpacked = ff(annotationlib.Format.VALUE)
488+
489+
evaled = _eval_types(unpacked, ctx)
490+
491+
return evaled
492+
493+
456494
@_eval_types_impl.register
457495
def _eval_applied_class(obj: typing_GenericAlias, ctx: EvalContext):
458496
"""Eval a typing._GenericAlias -- an applied user-defined class"""

typemap/typing.py

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,29 @@ def __typing_unpacked_tuple_args__(self):
5353
###
5454

5555

56+
class SupportAliases:
57+
@classmethod
58+
def __class_getitem__(cls, args):
59+
# Return an _AliasSupportingGenericAlias instead of a _GenericAlias
60+
res = super().__class_getitem__(args)
61+
return _AliasSupportingGenericAlias(res.__origin__, res.__args__)
62+
63+
64+
class _NestedGenericAlias(_GenericAlias, _root=True):
65+
pass
66+
67+
68+
class _AliasSupportingGenericAlias(_GenericAlias, _root=True):
69+
def __getattr__(self, attr):
70+
res = super().__getattr__(attr)
71+
if isinstance(res, typing.TypeAliasType):
72+
res = _NestedGenericAlias(NestedAlias, (self, res))
73+
return res
74+
75+
76+
###
77+
78+
5679
# Not type-level computation but related
5780

5881

@@ -132,21 +155,21 @@ class Member[
132155
Q: MemberQuals = typing.Never,
133156
I = typing.Never,
134157
D = typing.Never,
135-
]:
136-
name: N
137-
typ: T
138-
quals: Q
139-
init: I
140-
definer: D
158+
](SupportAliases):
159+
type name = N
160+
type type = T
161+
type quals = Q
162+
type init = I
163+
type definer = D
141164

142165

143166
ParamQuals = Literal["*", "**", "keyword", "positional", "default"]
144167

145168

146-
class Param[N: str | None, T, Q: ParamQuals = typing.Never]:
147-
name: N
148-
typ: T
149-
quals: Q
169+
class Param[N: str | None, T, Q: ParamQuals = typing.Never](SupportAliases):
170+
type name = N
171+
type type = T
172+
type quals = Q
150173

151174

152175
type PosParam[N: str | None, T] = Param[N, T, Literal["positional"]]
@@ -160,11 +183,15 @@ class Param[N: str | None, T, Q: ParamQuals = typing.Never]:
160183
type KwargsParam[T] = Param[Literal[None], T, Literal["**"]]
161184

162185

163-
type GetName[T: Member | Param] = GetMemberType[T, Literal["name"]]
164-
type GetType[T: Member | Param] = GetMemberType[T, Literal["typ"]]
165-
type GetQuals[T: Member | Param] = GetMemberType[T, Literal["quals"]]
166-
type GetInit[T: Member] = GetMemberType[T, Literal["init"]]
167-
type GetDefiner[T: Member] = GetMemberType[T, Literal["definer"]]
186+
type GetName[T: Member | Param] = T.name
187+
type GetType[T: Member | Param] = T.type
188+
type GetQuals[T: Member | Param] = T.quals
189+
type GetInit[T: Member] = T.init
190+
type GetDefiner[T: Member] = T.definer
191+
192+
193+
class NestedAlias[Obj, Alias]:
194+
pass
168195

169196

170197
class Attrs[T](_TupleLikeOperator):

0 commit comments

Comments
 (0)