Skip to content

Commit cb7005e

Browse files
committed
Replace HAS_DEFAULT ParamKind with D generic on Param
Instead of encoding "has default" as a qualifier in the Q parameter, add a D generic (defaulting to Never) that holds the default's type. When a parameter has a default, D=T; otherwise D=Never. Also add a GetDefault helper operator for extracting D from a Param.
1 parent 746225d commit cb7005e

7 files changed

Lines changed: 76 additions & 48 deletions

File tree

pep.rst

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -400,21 +400,22 @@ We introduce a ``Param`` type the contains all the information about a function
400400
VAR_POSITIONAL = 2
401401
KEYWORD_ONLY = 3
402402
VAR_KEYWORD = 4
403-
HAS_DEFAULT = 5
404403

405-
class Param[N: str | None, T, Q: ParamKind = typing.Never]:
404+
class Param[N: str | None, T, Q: ParamKind = typing.Never, D = typing.Never]:
406405
pass
407406

408407
type PosParam[N: str | None, T] = Param[
409408
N, T, Literal[ParamKind.POSITIONAL_ONLY]
410409
]
411410
type PosDefaultParam[N: str | None, T] = Param[
412-
N, T, Literal[ParamKind.POSITIONAL_ONLY, ParamKind.HAS_DEFAULT]
411+
N, T, Literal[ParamKind.POSITIONAL_ONLY], T
412+
]
413+
type DefaultParam[N: str, T] = Param[
414+
N, T, Literal[ParamKind.POSITIONAL_OR_KEYWORD], T
413415
]
414-
type DefaultParam[N: str, T] = Param[N, T, Literal[ParamKind.HAS_DEFAULT]]
415416
type NamedParam[N: str, T] = Param[N, T, Literal[ParamKind.KEYWORD_ONLY]]
416417
type NamedDefaultParam[N: str, T] = Param[
417-
N, T, Literal[ParamKind.KEYWORD_ONLY, ParamKind.HAS_DEFAULT]
418+
N, T, Literal[ParamKind.KEYWORD_ONLY], T
418419
]
419420
type ArgsParam[T] = Param[Literal[None], T, Literal[ParamKind.VAR_POSITIONAL]]
420421
type KwargsParam[T] = Param[Literal[None], T, Literal[ParamKind.VAR_KEYWORD]]
@@ -439,10 +440,10 @@ as (we are omitting the ``Literal`` in places)::
439440
[
440441
Param["a", int, ParamKind.POSITIONAL_ONLY],
441442
Param["b", int],
442-
Param["c", int, ParamKind.HAS_DEFAULT],
443+
Param["c", int, ParamKind.POSITIONAL_OR_KEYWORD, int],
443444
Param[None, int, ParamKind.VAR_POSITIONAL],
444445
Param["d", int, ParamKind.KEYWORD_ONLY],
445-
Param["e", int, Literal[ParamKind.HAS_DEFAULT, ParamKind.KEYWORD_ONLY]],
446+
Param["e", int, ParamKind.KEYWORD_ONLY, int],
446447
Param[None, int, ParamKind.VAR_KEYWORD],
447448
],
448449
int,
@@ -455,10 +456,10 @@ or, using the type abbreviations we provide::
455456
[
456457
PosParam["a", int],
457458
Param["b", int],
458-
DefaultParam["c", int],
459+
DefaultParam["c", int], # D=int
459460
ArgsParam[int],
460461
NamedParam["d", int],
461-
NamedDefaultParam["e", int],
462+
NamedDefaultParam["e", int], # D=int
462463
KwargsParam[int],
463464
],
464465
int,
@@ -1142,13 +1143,14 @@ dataclasses-style method generation
11421143
typing.GetName[p],
11431144
typing.GetType[p],
11441145
# All arguments are keyword-only
1146+
Literal[typing.ParamKind.KEYWORD_ONLY],
11451147
# It takes a default if a default is specified in the class
1146-
Literal[typing.ParamKind.KEYWORD_ONLY]
1147-
if typing.IsAssignable[
1148+
typing.GetType[p]
1149+
if not typing.IsAssignable[
11481150
GetDefault[typing.GetInit[p]],
11491151
Never,
11501152
]
1151-
else Literal[typing.ParamKind.KEYWORD_ONLY, typing.ParamKind.HAS_DEFAULT],
1153+
else Never,
11521154
]
11531155
for p in typing.Iter[typing.Attrs[T]]
11541156
],

tests/test_fastapilike_1.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import enum
33
import textwrap
44

5-
from typing import Annotated, Callable, Literal, Union, Self
5+
from typing import Annotated, Callable, Literal, Never, Union, Self
66

77
from typemap.type_eval import eval_typing
88
from typemap_extensions import (
@@ -54,12 +54,13 @@ class _Default:
5454
Param[
5555
GetName[p],
5656
DropAnnotations[GetType[p]],
57-
Literal[ParamKind.KEYWORD_ONLY, ParamKind.HAS_DEFAULT]
57+
Literal[ParamKind.KEYWORD_ONLY],
58+
DropAnnotations[GetType[p]]
5859
if IsAssignable[
5960
Literal[PropQuals.HAS_DEFAULT],
6061
GetAnnotations[GetType[p]],
6162
]
62-
else Literal[ParamKind.KEYWORD_ONLY],
63+
else Never,
6364
]
6465
for p in Iter[Attrs[T]]
6566
],

tests/test_fastapilike_2.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,16 +151,14 @@ class Field[T: FieldArgs](typing.InitField[T]):
151151
typing.GetName[p],
152152
typing.GetType[p],
153153
# All arguments are keyword-only
154+
Literal[typing.ParamKind.KEYWORD_ONLY],
154155
# It takes a default if a default is specified in the class
155-
Literal[typing.ParamKind.KEYWORD_ONLY]
156-
if typing.IsAssignable[
156+
typing.GetType[p]
157+
if not typing.IsAssignable[
157158
GetDefault[typing.GetInit[p]],
158159
Never,
159160
]
160-
else Literal[
161-
typing.ParamKind.KEYWORD_ONLY,
162-
typing.ParamKind.HAS_DEFAULT,
163-
],
161+
else Never,
164162
]
165163
for p in typing.Iter[typing.Attrs[T]]
166164
],

tests/test_type_dir.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -378,10 +378,10 @@ def test_type_members_func_1():
378378
str(typ)
379379
== f"\
380380
typing.Callable[[\
381-
typemap.typing.Param[typing.Literal['self'], tests.test_type_dir.Base[int], {PK}], \
382-
typemap.typing.Param[typing.Literal['a'], int | None, {PK}], \
381+
typemap.typing.Param[typing.Literal['self'], tests.test_type_dir.Base[int], {PK}, typing.Never], \
382+
typemap.typing.Param[typing.Literal['a'], int | None, {PK}, typing.Never], \
383383
typemap.typing.Param[typing.Literal['b'], int, typing.Literal[\
384-
<ParamKind.KEYWORD_ONLY: 3>, <ParamKind.HAS_DEFAULT: 5>]]], \
384+
<ParamKind.KEYWORD_ONLY: 3>], int]], \
385385
dict[str, int]]"
386386
)
387387

@@ -400,7 +400,7 @@ def test_type_members_func_2():
400400
assert (
401401
str(typ)
402402
== f"\
403-
classmethod[tests.test_type_dir.Base[int], tuple[typemap.typing.Param[typing.Literal['a'], int | None, {PK}], typemap.typing.Param[typing.Literal['b'], ~K, {PK}]], dict[str, int]]"
403+
classmethod[tests.test_type_dir.Base[int], tuple[typemap.typing.Param[typing.Literal['a'], int | None, {PK}, typing.Never], typemap.typing.Param[typing.Literal['b'], ~K, {PK}, typing.Never]], dict[str, int]]"
404404
)
405405

406406

@@ -416,7 +416,7 @@ def test_type_members_func_3():
416416
assert (
417417
str(typ)
418418
== f"\
419-
typemap.typing.GenericCallable[tuple[Z], staticmethod[tuple[typemap.typing.Param[typing.Literal['a'], int | typing.Literal['gotcha!'] | Z | None, {PK}], typemap.typing.Param[typing.Literal['b'], ~K, {PK}]], dict[str, int | Z]]]"
419+
typemap.typing.GenericCallable[tuple[Z], staticmethod[tuple[typemap.typing.Param[typing.Literal['a'], int | typing.Literal['gotcha!'] | Z | None, {PK}, typing.Never], typemap.typing.Param[typing.Literal['b'], ~K, {PK}, typing.Never]], dict[str, int | Z]]]"
420420
)
421421

422422

tests/test_type_eval.py

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,19 @@ def test_eval_types_4():
150150
[
151151
Param[Literal["a"], int, Literal[ParamKind.POSITIONAL_ONLY]],
152152
Param[Literal["b"], int],
153-
Param[Literal["c"], int, Literal[ParamKind.HAS_DEFAULT]],
153+
Param[
154+
Literal["c"],
155+
int,
156+
Literal[ParamKind.POSITIONAL_OR_KEYWORD],
157+
int,
158+
],
154159
Param[None, int, Literal[ParamKind.VAR_POSITIONAL]],
155160
Param[Literal["d"], int, Literal[ParamKind.KEYWORD_ONLY]],
156161
Param[
157162
Literal["e"],
158163
int,
159-
Literal[ParamKind.HAS_DEFAULT, ParamKind.KEYWORD_ONLY],
164+
Literal[ParamKind.KEYWORD_ONLY],
165+
int,
160166
],
161167
Param[None, int, Literal[ParamKind.VAR_KEYWORD]],
162168
],
@@ -169,13 +175,19 @@ def test_eval_types_4():
169175
[
170176
Param[Literal["a"], int, Literal[ParamKind.POSITIONAL_ONLY]],
171177
Param[Literal["b"], int],
172-
Param[Literal["c"], int, Literal[ParamKind.HAS_DEFAULT]],
178+
Param[
179+
Literal["c"],
180+
int,
181+
Literal[ParamKind.POSITIONAL_OR_KEYWORD],
182+
int,
183+
],
173184
Param[None, int, Literal[ParamKind.VAR_POSITIONAL]],
174185
Param[Literal["d"], int, Literal[ParamKind.KEYWORD_ONLY]],
175186
Param[
176187
Literal["e"],
177188
int,
178-
Literal[ParamKind.HAS_DEFAULT, ParamKind.KEYWORD_ONLY],
189+
Literal[ParamKind.KEYWORD_ONLY],
190+
int,
179191
],
180192
Param[None, int, Literal[ParamKind.VAR_KEYWORD]],
181193
],
@@ -1509,13 +1521,16 @@ def test_callable_to_signature_01():
15091521
[
15101522
Param[None, int],
15111523
Param[Literal["b"], int],
1512-
Param[Literal["c"], int, Literal[ParamKind.HAS_DEFAULT]],
1524+
Param[
1525+
Literal["c"], int, Literal[ParamKind.POSITIONAL_OR_KEYWORD], int
1526+
],
15131527
Param[None, int, Literal[ParamKind.VAR_POSITIONAL]],
15141528
Param[Literal["d"], int, Literal[ParamKind.KEYWORD_ONLY]],
15151529
Param[
15161530
Literal["e"],
15171531
int,
1518-
Literal[ParamKind.HAS_DEFAULT, ParamKind.KEYWORD_ONLY],
1532+
Literal[ParamKind.KEYWORD_ONLY],
1533+
int,
15191534
],
15201535
Param[None, int, Literal[ParamKind.VAR_KEYWORD]],
15211536
],
@@ -1544,13 +1559,16 @@ class C:
15441559
tuple[
15451560
Param[None, int],
15461561
Param[Literal["b"], int],
1547-
Param[Literal["c"], int, Literal[ParamKind.HAS_DEFAULT]],
1562+
Param[
1563+
Literal["c"], int, Literal[ParamKind.POSITIONAL_OR_KEYWORD], int
1564+
],
15481565
Param[None, int, Literal[ParamKind.VAR_POSITIONAL]],
15491566
Param[Literal["d"], int, Literal[ParamKind.KEYWORD_ONLY]],
15501567
Param[
15511568
Literal["e"],
15521569
int,
1553-
Literal[ParamKind.HAS_DEFAULT, ParamKind.KEYWORD_ONLY],
1570+
Literal[ParamKind.KEYWORD_ONLY],
1571+
int,
15541572
],
15551573
Param[None, int, Literal[ParamKind.VAR_KEYWORD]],
15561574
],
@@ -1574,13 +1592,16 @@ class C:
15741592
tuple[
15751593
Param[None, int],
15761594
Param[Literal["b"], int],
1577-
Param[Literal["c"], int, Literal[ParamKind.HAS_DEFAULT]],
1595+
Param[
1596+
Literal["c"], int, Literal[ParamKind.POSITIONAL_OR_KEYWORD], int
1597+
],
15781598
Param[None, int, Literal[ParamKind.VAR_POSITIONAL]],
15791599
Param[Literal["d"], int, Literal[ParamKind.KEYWORD_ONLY]],
15801600
Param[
15811601
Literal["e"],
15821602
int,
1583-
Literal[ParamKind.HAS_DEFAULT, ParamKind.KEYWORD_ONLY],
1603+
Literal[ParamKind.KEYWORD_ONLY],
1604+
int,
15841605
],
15851606
Param[None, int, Literal[ParamKind.VAR_KEYWORD]],
15861607
],

typemap/type_eval/_eval_operators.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -405,10 +405,10 @@ def _callable_type_to_signature(callable_type: object) -> inspect.Signature:
405405
else:
406406
kind = inspect.Parameter.POSITIONAL_OR_KEYWORD
407407

408-
# Handle default value
408+
# Handle default value from the 4th Param arg (D)
409+
default_type = param_args[3] if len(param_args) > 3 else typing.Never
409410
default: typing.Any
410-
if ParamKind.HAS_DEFAULT in quals:
411-
# We don't have the actual default value, use a sentinel
411+
if default_type is not typing.Never:
412412
default = _DUMMY_DEFAULT
413413
else:
414414
default = inspect.Parameter.empty
@@ -457,8 +457,9 @@ def fn(*args, **kwargs):
457457

458458

459459
def _is_pos_only(param):
460-
name, _, quals = typing.get_args(param)
461-
qual_set = _get_quals(quals)
460+
args = typing.get_args(param)
461+
name = args[0]
462+
qual_set = _get_quals(args[2] if len(args) > 2 else typing.Never)
462463
return ParamKind.POSITIONAL_ONLY in qual_set or (
463464
name is None
464465
and not (qual_set & {ParamKind.VAR_POSITIONAL, ParamKind.VAR_KEYWORD})
@@ -562,13 +563,14 @@ def _ann(x):
562563
quals.append(ParamKind.POSITIONAL_ONLY)
563564
else:
564565
quals.append(ParamKind.POSITIONAL_OR_KEYWORD)
565-
if p.default is not empty:
566-
quals.append(ParamKind.HAS_DEFAULT)
566+
ann_type = _ann(ann)
567+
has_default = p.default is not empty
567568
params.append(
568569
Param[
569570
typing.Literal[p.name],
570-
_ann(ann),
571+
ann_type,
571572
typing.Literal[*quals],
573+
ann_type if has_default else typing.Never,
572574
]
573575
)
574576

typemap/typing.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,29 +149,32 @@ class ParamKind(enum.IntEnum):
149149
VAR_POSITIONAL = 2
150150
KEYWORD_ONLY = 3
151151
VAR_KEYWORD = 4
152-
HAS_DEFAULT = 5
153152

154153

155154
class Param[
156155
N: str | None,
157156
T,
158157
Q: ParamKind = Literal[ParamKind.POSITIONAL_OR_KEYWORD],
158+
D = typing.Never,
159159
]:
160160
name: N
161161
typ: T
162162
quals: Q
163+
default: D
163164

164165

165166
type PosParam[N: str | None, T] = Param[
166167
N, T, Literal[ParamKind.POSITIONAL_ONLY]
167168
]
168169
type PosDefaultParam[N: str | None, T] = Param[
169-
N, T, Literal[ParamKind.POSITIONAL_ONLY, ParamKind.HAS_DEFAULT]
170+
N, T, Literal[ParamKind.POSITIONAL_ONLY], T
171+
]
172+
type DefaultParam[N: str, T] = Param[
173+
N, T, Literal[ParamKind.POSITIONAL_OR_KEYWORD], T
170174
]
171-
type DefaultParam[N: str, T] = Param[N, T, Literal[ParamKind.HAS_DEFAULT]]
172175
type NamedParam[N: str, T] = Param[N, T, Literal[ParamKind.KEYWORD_ONLY]]
173176
type NamedDefaultParam[N: str, T] = Param[
174-
N, T, Literal[ParamKind.KEYWORD_ONLY, ParamKind.HAS_DEFAULT]
177+
N, T, Literal[ParamKind.KEYWORD_ONLY], T
175178
]
176179
type ArgsParam[T] = Param[Literal[None], T, Literal[ParamKind.VAR_POSITIONAL]]
177180
type KwargsParam[T] = Param[Literal[None], T, Literal[ParamKind.VAR_KEYWORD]]
@@ -180,6 +183,7 @@ class Param[
180183
type GetName[T: Member | Param] = GetMemberType[T, Literal["name"]]
181184
type GetType[T: Member | Param] = GetMemberType[T, Literal["typ"]]
182185
type GetQuals[T: Member | Param] = GetMemberType[T, Literal["quals"]]
186+
type GetDefault[T: Param] = GetMemberType[T, Literal["default"]]
183187
type GetInit[T: Member] = GetMemberType[T, Literal["init"]]
184188
type GetDefiner[T: Member] = GetMemberType[T, Literal["definer"]]
185189

0 commit comments

Comments
 (0)