Skip to content

Commit 014df6f

Browse files
committed
Merge the Member and Param, make GetArg on Callable return Params
1 parent 8bb2e66 commit 014df6f

4 files changed

Lines changed: 87 additions & 32 deletions

File tree

spec-draft.rst

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -153,12 +153,12 @@ Methods are returned as callables using the new ``Param`` based extended callabl
153153

154154
TODO: What do we do about decorators in general, *at runtime*... This seems pretty cursed. We can probably sometimes evaluate them, if there are annotations at runtime.
155155

156-
We also have helpers for extracting those names; they are all definable in terms of ``GetArg``.
157-
(These names are too long -- but we can't do ``Type``. I kind of want to do the *longer* ``MemberName``?)
156+
We also have helpers for extracting those names; they are all definable in terms of ``GetArg``. (Some of them are shared with ``Param``, discussed below.)
157+
(These names are too long -- but we can't do ``Type``.)
158158

159-
* ``GetName[T: Member]``
160-
* ``GetType[T: Member]``
161-
* ``GetQuals[T: Member]``
159+
* ``GetName[T: Member | Param]``
160+
* ``GetType[T: Member | Param]``
161+
* ``GetQuals[T: Member | Param]``
162162
* ``GetDefiner[T: Member]``
163163

164164
* ``NewProtocolWithBases[Bases, Ps: tuple[Member]]`` - A variant that allows specifying bases too. (UNIMPLEMENTED)
@@ -174,14 +174,10 @@ We also have helpers for extracting those names; they are all definable in terms
174174
Callable inspection and creation
175175
--------------------------------
176176

177-
* TODO: Should ``GetArg`` on a callable automatically convert ``[int, str]`` or whatever into something using ``Param``? Or should a separate operator be needed?
177+
``Callable`` types always have their arguments exposed in the extended Callable format discussed above.
178178

179-
* ``GetParamName[T: Param]``
180-
* ``GetParamType[T: Param]``
181-
* ``GetParamQuals[T: Param]``
179+
The names, type, and qualifiers share getter operations with ``Member``.
182180

183-
This is unsatisfying; maybe they all need to be just ``ParamName`` and also ``MemberName`` above.
184-
We could also merge the getters for ``Param`` and ``Member``.
185181

186182
----
187183

tests/test_type_eval.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
Length,
3030
Member,
3131
NewProtocol,
32+
Param,
3233
SpecialFormEllipsis,
3334
StrConcat,
3435
StrSlice,
@@ -349,7 +350,8 @@ def test_eval_getargs():
349350
assert args == tuple[Any, Any]
350351

351352

352-
def test_eval_getarg_callable():
353+
@unittest.skip
354+
def test_eval_getarg_callable_old():
353355
# oh hmmmmmmm -- yeah maybe callable could be fully bespoke if we
354356
# disallowed putting Callable here...!
355357
t = Callable[[int, str], str]
@@ -377,6 +379,38 @@ def test_eval_getarg_callable():
377379
assert args == Any
378380

379381

382+
def test_eval_getarg_callable():
383+
t = Callable[[int, str], str]
384+
args = eval_typing(GetArg[t, Callable, 0])
385+
assert (
386+
args
387+
== tuple[
388+
Param[Literal[None], int, Never], Param[Literal[None], str, Never]
389+
]
390+
)
391+
392+
t = Callable[int, str]
393+
args = eval_typing(GetArg[t, Callable, 0])
394+
assert args == tuple[Param[Literal[None], int, Never]]
395+
396+
t = Callable[[], str]
397+
args = eval_typing(GetArg[t, Callable, 0])
398+
assert args == tuple[()]
399+
400+
# XXX: Is this what we want? Or should it be *args, **kwargs
401+
t = Callable[..., str]
402+
args = eval_typing(GetArg[t, Callable, 0])
403+
assert args == SpecialFormEllipsis
404+
405+
t = Callable
406+
args = eval_typing(GetArg[t, Callable, 0])
407+
assert args == SpecialFormEllipsis
408+
409+
t = Callable
410+
args = eval_typing(GetArg[t, Callable, 1])
411+
assert args == Any
412+
413+
380414
def test_eval_getarg_tuple():
381415
t = tuple[int, ...]
382416
args = eval_typing(GetArg[t, tuple, 1])

typemap/type_eval/_eval_operators.py

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,22 @@ def _eval_GetAttr(tp, prop, *, ctx):
513513
return typing.Never
514514

515515

516+
def _fix_callable_args(base, args):
517+
idx = FUNC_LIKES[base]
518+
if idx >= len(args):
519+
return args
520+
args = list(args)
521+
special = _fix_type(args[idx])
522+
if typing.get_origin(special) is tuple:
523+
args[idx] = tuple[
524+
*[
525+
t if isinstance(t, Param) else Param[typing.Literal[None], t]
526+
for t in typing.get_args(special)
527+
]
528+
]
529+
return tuple(args)
530+
531+
516532
def _get_raw_args(tp, base_head, ctx) -> typing.Any:
517533
evaled = _eval_types(tp, ctx)
518534

@@ -521,7 +537,11 @@ def _get_raw_args(tp, base_head, ctx) -> typing.Any:
521537
return None
522538

523539
if tp_head is base_head:
524-
return typing.get_args(evaled)
540+
args = typing.get_args(evaled)
541+
if _is_method_like(tp):
542+
args = _fix_callable_args(base_head, args)
543+
544+
return args
525545

526546
# Scan the fully-annotated MRO to find the base
527547
box = _apply_generic.box(tp)
@@ -544,10 +564,12 @@ def _get_args(tp, base, ctx) -> typing.Any:
544564
def _fix_type(tp):
545565
"""Fix up a type getting returned from GetArg
546566
547-
In particular, this means turning a list into a tuple of the list
548-
elements and turning ... into SpecialFormEllipsis.
567+
In particular, this means turning a list into a tuple of the
568+
Paramified list elements and turning ... into SpecialFormEllipsis.
569+
549570
"""
550571
if isinstance(tp, (tuple, list)):
572+
# XXX: Can we always do this or is it a problem
551573
return tuple[*tp]
552574
elif tp is ...:
553575
return SpecialFormEllipsis
@@ -714,13 +736,16 @@ def _add_quals(typ, quals):
714736
return typ
715737

716738

739+
FUNC_LIKES = {
740+
GenericCallable: 0,
741+
collections.abc.Callable: 0,
742+
staticmethod: 0,
743+
classmethod: 1,
744+
}
745+
746+
717747
def _is_method_like(typ):
718-
return typing.get_origin(typ) in (
719-
GenericCallable,
720-
collections.abc.Callable,
721-
staticmethod,
722-
classmethod,
723-
)
748+
return typing.get_origin(typ) in FUNC_LIKES
724749

725750

726751
@type_eval.register_evaluator(NewProtocol)

typemap/typing.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,25 +34,25 @@ class GenericCallable[
3434

3535

3636
class Member[N: str, T, Q: MemberQuals = typing.Never, D = typing.Never]:
37-
pass
38-
39-
40-
type GetName[T: Member] = GetArg[T, Member, 0] # type: ignore[valid-type]
41-
type GetType[T: Member] = GetArg[T, Member, 1] # type: ignore[valid-type]
42-
type GetQuals[T: Member] = GetArg[T, Member, 2] # type: ignore[valid-type]
43-
type GetDefiner[T: Member] = GetArg[T, Member, 3] # type: ignore[valid-type]
37+
name: N
38+
typ: T
39+
quals: Q
40+
definer: D
4441

4542

4643
ParamQuals = typing.Literal["*", "**", "="]
4744

4845

4946
class Param[N: str | None, T, Q: ParamQuals = typing.Never]:
50-
pass
47+
name: N
48+
typ: T
49+
quals: Q
5150

5251

53-
type GetParamName[T: Param] = GetArg[T, Param, 0] # type: ignore[valid-type]
54-
type GetParamType[T: Param] = GetArg[T, Param, 1] # type: ignore[valid-type]
55-
type GetParamQuals[T: Param] = GetArg[T, Param, 2] # type: ignore[valid-type]
52+
type GetName[T: Member | Param] = GetAttr[T, typing.Literal["name"]]
53+
type GetType[T: Member | Param] = GetAttr[T, typing.Literal["typ"]]
54+
type GetQuals[T: Member | Param] = GetAttr[T, typing.Literal["quals"]]
55+
type GetDefiner[T: Member] = GetAttr[T, typing.Literal["definer"]]
5656

5757

5858
class Attrs[T]:

0 commit comments

Comments
 (0)