Skip to content

Commit c328baf

Browse files
committed
Move functions to _apply_generic.
1 parent 6107388 commit c328baf

2 files changed

Lines changed: 104 additions & 105 deletions

File tree

typemap/type_eval/_apply_generic.py

Lines changed: 104 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,6 @@ def get_local_defns(
340340
],
341341
]:
342342
from typemap.typing import GenericCallable, Overloaded
343-
from ._eval_operators import _function_type, _function_type_from_sig
344343

345344
annos: dict[str, Any] = {}
346345
dct: dict[str, Any] = {}
@@ -381,8 +380,6 @@ def get_local_defns(
381380
definition_cls = boxed.canonical_cls
382381

383382
def _make_lambda(fn, o, sa, tp, recv_cls, def_cls):
384-
from ._eval_operators import _function_type_from_sig
385-
386383
def lam(*vs):
387384
args = dict(sa)
388385
args.update(
@@ -440,6 +437,110 @@ def lam(*vs):
440437
return annos, dct
441438

442439

440+
def _function_type_from_sig(sig, func_type, *, receiver_type):
441+
from typemap.typing import Param
442+
443+
empty = inspect.Parameter.empty
444+
445+
def _ann(x):
446+
return typing.Any if x is empty else None if x is type(None) else x
447+
448+
specified_receiver = receiver_type
449+
450+
params = []
451+
for i, p in enumerate(sig.parameters.values()):
452+
ann = p.annotation
453+
# Special handling for first argument on methods.
454+
if i == 0 and receiver_type and func_type is not staticmethod:
455+
if ann is empty:
456+
ann = receiver_type
457+
else:
458+
if (
459+
func_type is classmethod
460+
and typing.get_origin(ann) is type
461+
and (receiver_args := typing.get_args(ann))
462+
):
463+
# The annotation for cls in a classmethod should be type[C]
464+
specified_receiver = receiver_args[0]
465+
else:
466+
specified_receiver = ann
467+
468+
quals = []
469+
if p.kind == inspect.Parameter.VAR_POSITIONAL:
470+
quals.append("*")
471+
if p.kind == inspect.Parameter.VAR_KEYWORD:
472+
quals.append("**")
473+
if p.kind == inspect.Parameter.KEYWORD_ONLY:
474+
quals.append("keyword")
475+
if p.kind == inspect.Parameter.POSITIONAL_ONLY:
476+
quals.append("positional")
477+
if p.default is not empty:
478+
quals.append("default")
479+
params.append(
480+
Param[
481+
typing.Literal[p.name],
482+
_ann(ann),
483+
typing.Literal[*quals] if quals else typing.Never,
484+
]
485+
)
486+
487+
ret = _ann(sig.return_annotation)
488+
489+
# TODO: Is doing the tuple for staticmethod/classmethod legit?
490+
# Putting a list in makes it unhashable...
491+
f: typing.Any # type: ignore[annotation-unchecked]
492+
if func_type is staticmethod:
493+
f = staticmethod[tuple[*params], ret]
494+
elif func_type is classmethod:
495+
f = classmethod[specified_receiver, tuple[*params[1:]], ret]
496+
else:
497+
f = typing.Callable[params, ret]
498+
499+
return f
500+
501+
502+
def _function_type(
503+
func, *, receiver_type
504+
) -> type[typing.Callable | classmethod | staticmethod | GenericCallable]:
505+
from typemap.typing import GenericCallable
506+
507+
root = inspect.unwrap(func)
508+
sig = inspect.signature(root)
509+
f = _function_type_from_sig(sig, type(func), receiver_type=receiver_type)
510+
511+
if root.__type_params__:
512+
# Must store a lambda that performs type variable substitution
513+
type_params = root.__type_params__
514+
callable_lambda = _create_generic_callable_lambda(f, type_params)
515+
f = GenericCallable[tuple[*type_params], callable_lambda] # type: ignore[misc,valid-type]
516+
return f
517+
518+
519+
def _create_generic_callable_lambda(
520+
f: typing.Callable | classmethod | staticmethod,
521+
type_params: tuple[typing.TypeVar, ...],
522+
):
523+
if typing.get_origin(f) in (staticmethod, classmethod):
524+
return lambda *vs: substitute(
525+
f, dict(zip(type_params, vs, strict=True))
526+
)
527+
528+
else:
529+
# Callable params are stored as a list
530+
params, ret = typing.get_args(f)
531+
532+
return lambda *vs: typing.Callable[
533+
[
534+
substitute(
535+
p,
536+
dict(zip(type_params, vs, strict=True)),
537+
)
538+
for p in params
539+
],
540+
substitute(ret, dict(zip(type_params, vs, strict=True))),
541+
]
542+
543+
443544
def flatten_class_new_proto(cls: type) -> type:
444545
# This is a hacky version of flatten_class that works by using
445546
# NewProtocol on Members!

typemap/type_eval/_eval_operators.py

Lines changed: 0 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -679,108 +679,6 @@ def _callable_type_to_method(name, typ, ctx):
679679
return head(func)
680680

681681

682-
def _function_type_from_sig(sig, func_type, *, receiver_type):
683-
empty = inspect.Parameter.empty
684-
685-
def _ann(x):
686-
return typing.Any if x is empty else None if x is type(None) else x
687-
688-
specified_receiver = receiver_type
689-
690-
params = []
691-
for i, p in enumerate(sig.parameters.values()):
692-
ann = p.annotation
693-
# Special handling for first argument on methods.
694-
if i == 0 and receiver_type and func_type is not staticmethod:
695-
if ann is empty:
696-
ann = receiver_type
697-
else:
698-
if (
699-
func_type is classmethod
700-
and typing.get_origin(ann) is type
701-
and (receiver_args := typing.get_args(ann))
702-
):
703-
# The annotation for cls in a classmethod should be type[C]
704-
specified_receiver = receiver_args[0]
705-
else:
706-
specified_receiver = ann
707-
708-
quals = []
709-
if p.kind == inspect.Parameter.VAR_POSITIONAL:
710-
quals.append("*")
711-
if p.kind == inspect.Parameter.VAR_KEYWORD:
712-
quals.append("**")
713-
if p.kind == inspect.Parameter.KEYWORD_ONLY:
714-
quals.append("keyword")
715-
if p.kind == inspect.Parameter.POSITIONAL_ONLY:
716-
quals.append("positional")
717-
if p.default is not empty:
718-
quals.append("default")
719-
params.append(
720-
Param[
721-
typing.Literal[p.name],
722-
_ann(ann),
723-
typing.Literal[*quals] if quals else typing.Never,
724-
]
725-
)
726-
727-
ret = _ann(sig.return_annotation)
728-
729-
# TODO: Is doing the tuple for staticmethod/classmethod legit?
730-
# Putting a list in makes it unhashable...
731-
f: typing.Any # type: ignore[annotation-unchecked]
732-
if func_type is staticmethod:
733-
f = staticmethod[tuple[*params], ret]
734-
elif func_type is classmethod:
735-
f = classmethod[specified_receiver, tuple[*params[1:]], ret]
736-
else:
737-
f = typing.Callable[params, ret]
738-
739-
return f
740-
741-
742-
def _function_type(
743-
func, *, receiver_type
744-
) -> type[typing.Callable | classmethod | staticmethod | GenericCallable]:
745-
root = inspect.unwrap(func)
746-
sig = inspect.signature(root)
747-
f = _function_type_from_sig(sig, type(func), receiver_type=receiver_type)
748-
749-
if root.__type_params__:
750-
# Must store a lambda that performs type variable substitution
751-
type_params = root.__type_params__
752-
callable_lambda = _create_generic_callable_lambda(f, type_params)
753-
f = GenericCallable[tuple[*type_params], callable_lambda] # type: ignore[misc,valid-type]
754-
return f
755-
756-
757-
def _create_generic_callable_lambda(
758-
f: typing.Callable | classmethod | staticmethod,
759-
type_params: tuple[typing.TypeVar, ...],
760-
):
761-
if typing.get_origin(f) in (staticmethod, classmethod):
762-
return lambda *vs: _apply_generic.substitute(
763-
f, dict(zip(type_params, vs, strict=True))
764-
)
765-
766-
else:
767-
# Callable params are stored as a list
768-
params, ret = typing.get_args(f)
769-
770-
return lambda *vs: typing.Callable[
771-
[
772-
_apply_generic.substitute(
773-
p,
774-
dict(zip(type_params, vs, strict=True)),
775-
)
776-
for p in params
777-
],
778-
_apply_generic.substitute(
779-
ret, dict(zip(type_params, vs, strict=True))
780-
),
781-
]
782-
783-
784682
def _hint_to_member(n, t, qs, init, d, *, ctx):
785683
return Member[
786684
typing.Literal[n],

0 commit comments

Comments
 (0)