Skip to content

Commit 62ff846

Browse files
committed
Only process Param types when wrapped in Params
_callable_type_to_signature now treats standard callables (Callable[[type, ...], ret]) separately from extended callables (Callable[Params[Param[...], ...], ret]). All test files updated to use Params wrapper consistently.
1 parent 0243b11 commit 62ff846

File tree

7 files changed

+95
-45
lines changed

7 files changed

+95
-45
lines changed

tests/test_dataclass_like.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class Field[T: FieldArgs](typing.InitField[T]):
5353
type InitFnType[T] = typing.Member[
5454
Literal["__init__"],
5555
Callable[
56-
[
56+
typing.Params[
5757
typing.Param[Literal["self"], Self],
5858
*[
5959
typing.Param[

tests/test_eval_call_with_types.py

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
Iter,
1111
Members,
1212
Param,
13+
Params,
1314
)
1415

1516

@@ -19,36 +20,46 @@ def test_eval_call_with_types_callable_01():
1920

2021

2122
def test_eval_call_with_types_callable_02():
22-
res = eval_call_with_types(Callable[[Param[Literal["x"], int]], int], int)
23+
res = eval_call_with_types(
24+
Callable[Params[Param[Literal["x"], int]], int], int
25+
)
2326
assert res is int
2427

2528

2629
def test_eval_call_with_types_callable_03():
2730
res = eval_call_with_types(
28-
Callable[[Param[Literal["x"], int, Literal["keyword"]]], int], x=int
31+
Callable[Params[Param[Literal["x"], int, Literal["keyword"]]], int],
32+
x=int,
2933
)
3034
assert res is int
3135

3236

3337
def test_eval_call_with_types_callable_04():
3438
class C: ...
3539

36-
res = eval_call_with_types(Callable[[Param[Literal["self"], Self]], int], C)
40+
res = eval_call_with_types(
41+
Callable[Params[Param[Literal["self"], Self]], int], C
42+
)
3743
assert res is int
3844

3945

4046
def test_eval_call_with_types_callable_05():
4147
class C: ...
4248

43-
res = eval_call_with_types(Callable[[Param[Literal["self"], Self]], C], C)
49+
res = eval_call_with_types(
50+
Callable[Params[Param[Literal["self"], Self]], C], C
51+
)
4452
assert res is C
4553

4654

4755
def test_eval_call_with_types_callable_06():
4856
class C: ...
4957

5058
res = eval_call_with_types(
51-
Callable[[Param[Literal["self"], Self], Param[Literal["x"], int]], int],
59+
Callable[
60+
Params[Param[Literal["self"], Self], Param[Literal["x"], int]],
61+
int,
62+
],
5263
C,
5364
int,
5465
)
@@ -60,7 +71,7 @@ class C: ...
6071

6172
res = eval_call_with_types(
6273
Callable[
63-
[
74+
Params[
6475
Param[Literal["self"], Self],
6576
Param[Literal["x"], int, Literal["keyword"]],
6677
],
@@ -74,13 +85,15 @@ class C: ...
7485

7586
def test_eval_call_with_types_callable_08():
7687
T = TypeVar("T")
77-
res = eval_call_with_types(Callable[[Param[Literal["x"], T]], str], int)
88+
res = eval_call_with_types(
89+
Callable[Params[Param[Literal["x"], T]], str], int
90+
)
7891
assert res is str
7992

8093

8194
def test_eval_call_with_types_callable_09():
8295
T = TypeVar("T")
83-
res = eval_call_with_types(Callable[[Param[Literal["x"], T]], T], int)
96+
res = eval_call_with_types(Callable[Params[Param[Literal["x"], T]], T], int)
8497
assert res is int
8598

8699

@@ -89,7 +102,9 @@ def test_eval_call_with_types_callable_10():
89102

90103
class C(Generic[T]): ...
91104

92-
res = eval_call_with_types(Callable[[Param[Literal["x"], C[T]]], T], C[int])
105+
res = eval_call_with_types(
106+
Callable[Params[Param[Literal["x"], C[T]]], T], C[int]
107+
)
93108
assert res is int
94109

95110

@@ -102,9 +117,13 @@ class D(C[int]): ...
102117

103118
class E(D): ...
104119

105-
res = eval_call_with_types(Callable[[Param[Literal["x"], C[T]]], T], D)
120+
res = eval_call_with_types(
121+
Callable[Params[Param[Literal["x"], C[T]]], T], D
122+
)
106123
assert res is int
107-
res = eval_call_with_types(Callable[[Param[Literal["x"], C[T]]], T], E)
124+
res = eval_call_with_types(
125+
Callable[Params[Param[Literal["x"], C[T]]], T], E
126+
)
108127
assert res is int
109128

110129

@@ -206,7 +225,10 @@ def test_eval_call_with_types_bind_error_01():
206225
ValueError, match="Type variable T is already bound to int, but got str"
207226
):
208227
eval_call_with_types(
209-
Callable[[Param[Literal["x"], T], Param[Literal["y"], T]], T],
228+
Callable[
229+
Params[Param[Literal["x"], T], Param[Literal["y"], T]],
230+
T,
231+
],
210232
int,
211233
str,
212234
)
@@ -230,7 +252,10 @@ class C(Generic[T]): ...
230252
ValueError, match="Type variable T is already bound to int, but got str"
231253
):
232254
eval_call_with_types(
233-
Callable[[Param[Literal["x"], C[T]], Param[Literal["y"], C[T]]], T],
255+
Callable[
256+
Params[Param[Literal["x"], C[T]], Param[Literal["y"], C[T]]],
257+
T,
258+
],
234259
C[int],
235260
C[str],
236261
)

tests/test_fastapilike_1.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
Member,
2020
Members,
2121
Param,
22+
Params,
2223
)
2324

2425
from . import format_helper
@@ -47,7 +48,7 @@ class _Default:
4748
type InitFnType[T] = Member[
4849
Literal["__init__"],
4950
Callable[
50-
[
51+
Params[
5152
Param[Literal["self"], Self],
5253
*[
5354
Param[

tests/test_fastapilike_2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ class Field[T: FieldArgs](typing.InitField[T]):
142142
type InitFnType[T] = typing.Member[
143143
Literal["__init__"],
144144
Callable[
145-
[
145+
typing.Params[
146146
typing.Param[Literal["self"], Self],
147147
*[
148148
typing.Param[

tests/test_schemalike.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import Callable, Literal
44

55
from typemap.type_eval import eval_typing
6+
from typemap.typing import Params
67
from typemap_extensions import (
78
NewProtocol,
89
Iter,
@@ -46,7 +47,7 @@ class Property:
4647
Member[
4748
StrConcat[Literal["get_"], p.name],
4849
Callable[
49-
[
50+
Params[
5051
Param[Literal["self"], Schemaify[T]],
5152
NamedParam[Literal["schema"], Schema, Literal["keyword"]],
5253
],
@@ -63,16 +64,17 @@ def test_schema_like_1():
6364
tgt = eval_typing(Schemaify[Property])
6465
fmt = format_helper.format_class(tgt)
6566

66-
assert fmt == textwrap.dedent("""\
67+
getter_params = "self: Self, *, schema: tests.test_schemalike.Schema"
68+
assert fmt == textwrap.dedent(f"""\
6769
class Schemaify[tests.test_schemalike.Property]:
6870
name: str
6971
required: bool
7072
multi: bool
7173
typ: tests.test_schemalike.Type
7274
expr: tests.test_schemalike.Expression | None
73-
def get_name(self: Self, *, schema: tests.test_schemalike.Schema) -> str: ...
74-
def get_required(self: Self, *, schema: tests.test_schemalike.Schema) -> bool: ...
75-
def get_multi(self: Self, *, schema: tests.test_schemalike.Schema) -> bool: ...
76-
def get_typ(self: Self, *, schema: tests.test_schemalike.Schema) -> tests.test_schemalike.Type: ...
77-
def get_expr(self: Self, *, schema: tests.test_schemalike.Schema) -> tests.test_schemalike.Expression | None: ...
75+
def get_name({getter_params}) -> str: ...
76+
def get_required({getter_params}) -> bool: ...
77+
def get_multi({getter_params}) -> bool: ...
78+
def get_typ({getter_params}) -> tests.test_schemalike.Type: ...
79+
def get_expr({getter_params}) -> tests.test_schemalike.Expression | None: ...
7880
""")

tests/test_type_eval.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,10 @@ def f[T](self, x: T) -> OnlyIntToSet[T]: ...
458458
assert (
459459
f
460460
== Callable[
461-
Params[Param[Literal["self"], Self], Param[Literal["x"], Vs[0]]],
461+
Params[
462+
Param[Literal["self"], Self],
463+
Param[Literal["x"], Vs[0]],
464+
],
462465
OnlyIntToSet[Vs[0]],
463466
]
464467
)
@@ -1283,7 +1286,7 @@ def test_eval_getarg_custom_08():
12831286
def test_eval_getargs_generic_callable_01():
12841287
T = TypeVar("T")
12851288
t = GenericCallable[
1286-
tuple[T], lambda T: Callable[[Param[Literal["x"], T]], int]
1289+
tuple[T], lambda T: Callable[Params[Param[Literal["x"], T]], int]
12871290
]
12881291
args = eval_typing(GetArgs[t, GenericCallable])
12891292
assert args == tuple[tuple[T]]
@@ -1802,7 +1805,7 @@ def test_callable_to_signature_01():
18021805
# **kwargs: int
18031806
# ) -> int:
18041807
callable_type = Callable[
1805-
[
1808+
Params[
18061809
Param[None, int],
18071810
Param[Literal["b"], int],
18081811
Param[Literal["c"], int, Literal["default"]],
@@ -2451,14 +2454,20 @@ def g(self) -> str: ...
24512454
Member[Literal["b"], str, Never, Never, B],
24522455
Member[
24532456
Literal["f"],
2454-
Callable[Params[Param[Literal["self"], Self]], int],
2457+
Callable[
2458+
Params[Param[Literal["self"], Self]],
2459+
int,
2460+
],
24552461
Literal["ClassVar"],
24562462
object,
24572463
B,
24582464
],
24592465
Member[
24602466
Literal["g"],
2461-
Callable[Params[Param[Literal["self"], Self]], str],
2467+
Callable[
2468+
Params[Param[Literal["self"], Self]],
2469+
str,
2470+
],
24622471
Literal["ClassVar"],
24632472
object,
24642473
B,

typemap/type_eval/_eval_operators.py

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -493,22 +493,13 @@ def __repr__(self):
493493

494494

495495
def _callable_type_to_signature(callable_type: object) -> inspect.Signature:
496-
"""Convert a Callable type with Param specs to an inspect.Signature.
496+
"""Convert a Callable type to an inspect.Signature.
497497
498-
The callable_type should be of the form:
499-
Callable[
500-
[
501-
Param[name, type, quals],
502-
...
503-
],
504-
return_type,
505-
]
498+
Extended callables use the form:
499+
Callable[Params[Param[name, type, quals], ...], return_type]
506500
507-
Where:
508-
- name is None for positional-only or variadic params, or a string
509-
- type is the parameter type annotation
510-
- quals is a Literal with any of: "*", "**", "keyword", "default"
511-
or Never if no qualifiers
501+
Standard callables use the form:
502+
Callable[[type, ...], return_type]
512503
"""
513504
args = typing.get_args(callable_type)
514505
if (
@@ -552,6 +543,28 @@ def _callable_type_to_signature(callable_type: object) -> inspect.Signature:
552543
# Unwrap Params wrapper
553544
if typing.get_origin(param_types) is Params:
554545
param_types = list(typing.get_args(param_types))
546+
else:
547+
# Standard callable (no Params wrapping) — build simple
548+
# positional parameters from the type list
549+
if isinstance(param_types, (list, tuple)):
550+
params = []
551+
for i, t in enumerate(param_types):
552+
params.append(
553+
inspect.Parameter(
554+
f"_arg{i}",
555+
kind=inspect.Parameter.POSITIONAL_ONLY,
556+
annotation=t,
557+
)
558+
)
559+
if return_type is type(None):
560+
return_type = None
561+
return inspect.Signature(
562+
parameters=params,
563+
return_annotation=return_type,
564+
)
565+
raise TypeError(
566+
f"Expected Params[...] or list of types, got {param_types}"
567+
)
555568

556569
# Handle the case where param_types is a list of Param types
557570
if not isinstance(param_types, (list, tuple)):
@@ -698,10 +711,10 @@ def _callable_type_to_method(name, typ, ctx):
698711
else:
699712
cls_typ = type[typing.Self] # type: ignore[name-defined]
700713
cls_param = Param[typing.Literal["cls"], cls_typ, quals]
701-
typ = typing.Callable[[cls_param] + list(typing.get_args(params)), ret]
714+
typ = typing.Callable[Params[cls_param, *typing.get_args(params)], ret]
702715
elif head is staticmethod:
703716
params, ret = typing.get_args(typ)
704-
typ = typing.Callable[list(typing.get_args(params)), ret]
717+
typ = typing.Callable[Params[*typing.get_args(params)], ret]
705718
else:
706719
params, ret = typing.get_args(typ)
707720
# Unwrap Params wrapper if present
@@ -729,7 +742,7 @@ def _callable_type_to_method(name, typ, ctx):
729742
for p in param_list
730743
]
731744
ret = type(None) if name == "__init__" else ret
732-
typ = typing.Callable[param_list, ret]
745+
typ = typing.Callable[Params[*param_list], ret]
733746
head = lambda x: x
734747

735748
func = _signature_to_function(name, _callable_type_to_signature(typ))

0 commit comments

Comments
 (0)