Skip to content

Commit e9cece0

Browse files
committed
Replace string-based ParamQuals with ParamKind IntEnum
Model parameter qualifiers on inspect._ParameterKind instead of using ad-hoc string literals. Drop the ParamQuals Literal type alias and bind Param's Q directly to the ParamKind enum.
1 parent f1180b1 commit e9cece0

9 files changed

Lines changed: 222 additions & 127 deletions

pep.rst

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -393,18 +393,32 @@ Extended Callables, take 2
393393

394394
We introduce a ``Param`` type the contains all the information about a function param::
395395

396-
class Param[N: str | None, T, Q: ParamQuals = typing.Never]:
396+
class ParamKind(enum.IntEnum):
397+
"""Parameter kind qualifiers, modeled on inspect._ParameterKind."""
398+
POSITIONAL_ONLY = 0
399+
POSITIONAL_OR_KEYWORD = 1
400+
VAR_POSITIONAL = 2
401+
KEYWORD_ONLY = 3
402+
VAR_KEYWORD = 4
403+
404+
class Param[N: str | None, T, Q: ParamKind = typing.Never, D = typing.Never]:
397405
pass
398406

399-
ParamQuals = typing.Literal["*", "**", "default", "keyword"]
400-
401-
type PosParam[N: str | None, T] = Param[N, T, Literal["positional"]]
402-
type PosDefaultParam[N: str | None, T] = Param[N, T, Literal["positional", "default"]]
403-
type DefaultParam[N: str, T] = Param[N, T, Literal["default"]]
404-
type NamedParam[N: str, T] = Param[N, T, Literal["keyword"]]
405-
type NamedDefaultParam[N: str, T] = Param[N, T, Literal["keyword", "default"]]
406-
type ArgsParam[T] = Param[Literal[None], T, Literal["*"]]
407-
type KwargsParam[T] = Param[Literal[None], T, Literal["**"]]
407+
type PosParam[N: str | None, T] = Param[
408+
N, T, Literal[ParamKind.POSITIONAL_ONLY]
409+
]
410+
type PosDefaultParam[N: str | None, T] = Param[
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
415+
]
416+
type NamedParam[N: str, T] = Param[N, T, Literal[ParamKind.KEYWORD_ONLY]]
417+
type NamedDefaultParam[N: str, T] = Param[
418+
N, T, Literal[ParamKind.KEYWORD_ONLY], T
419+
]
420+
type ArgsParam[T] = Param[Literal[None], T, Literal[ParamKind.VAR_POSITIONAL]]
421+
type KwargsParam[T] = Param[Literal[None], T, Literal[ParamKind.VAR_KEYWORD]]
408422

409423
And then, we can represent the type of a function like::
410424

@@ -424,13 +438,13 @@ as (we are omitting the ``Literal`` in places)::
424438

425439
Callable[
426440
[
427-
Param["a", int, "positional"],
441+
Param["a", int, ParamKind.POSITIONAL_ONLY],
428442
Param["b", int],
429-
Param["c", int, "default"],
430-
Param[None, int, "*"],
431-
Param["d", int, "keyword"],
432-
Param["e", int, Literal["default", "keyword"]],
433-
Param[None, int, "**"],
443+
Param["c", int, ParamKind.POSITIONAL_OR_KEYWORD, int],
444+
Param[None, int, ParamKind.VAR_POSITIONAL],
445+
Param["d", int, ParamKind.KEYWORD_ONLY],
446+
Param["e", int, ParamKind.KEYWORD_ONLY, int],
447+
Param[None, int, ParamKind.VAR_KEYWORD],
434448
],
435449
int,
436450
]
@@ -442,10 +456,10 @@ or, using the type abbreviations we provide::
442456
[
443457
PosParam["a", int],
444458
Param["b", int],
445-
DefaultParam["c", int],
459+
DefaultParam["c", int], # D=int
446460
ArgsParam[int],
447461
NamedParam["d", int],
448-
NamedDefaultParam["e", int],
462+
NamedDefaultParam["e", int], # D=int
449463
KwargsParam[int],
450464
],
451465
int,
@@ -1129,13 +1143,14 @@ dataclasses-style method generation
11291143
typing.GetName[p],
11301144
typing.GetType[p],
11311145
# All arguments are keyword-only
1146+
Literal[typing.ParamKind.KEYWORD_ONLY],
11321147
# It takes a default if a default is specified in the class
1133-
Literal["keyword"]
1134-
if typing.IsAssignable[
1148+
typing.GetType[p]
1149+
if not typing.IsAssignable[
11351150
GetDefault[typing.GetInit[p]],
11361151
Never,
11371152
]
1138-
else Literal["keyword", "default"],
1153+
else Never,
11391154
]
11401155
for p in typing.Iter[typing.Attrs[T]]
11411156
],

tests/test_eval_call_with_types.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
Iter,
1313
Members,
1414
Param,
15+
ParamKind,
1516
)
1617

1718

@@ -27,7 +28,10 @@ def test_eval_call_with_types_callable_02():
2728

2829
def test_eval_call_with_types_callable_03():
2930
res = eval_call_with_types(
30-
Callable[[Param[Literal["x"], int, Literal["keyword"]]], int], x=int
31+
Callable[
32+
[Param[Literal["x"], int, Literal[ParamKind.KEYWORD_ONLY]]], int
33+
],
34+
x=int,
3135
)
3236
assert res is int
3337

@@ -64,7 +68,7 @@ class C: ...
6468
Callable[
6569
[
6670
Param[Literal["self"], Self],
67-
Param[Literal["x"], int, Literal["keyword"]],
71+
Param[Literal["x"], int, Literal[ParamKind.KEYWORD_ONLY]],
6872
],
6973
int,
7074
],

tests/test_fastapilike_1.py

Lines changed: 5 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 (
@@ -18,6 +18,7 @@
1818
GetQuals,
1919
Member,
2020
Members,
21+
ParamKind,
2122
Param,
2223
)
2324

@@ -53,12 +54,13 @@ class _Default:
5354
Param[
5455
GetName[p],
5556
DropAnnotations[GetType[p]],
56-
Literal["keyword", "default"]
57+
Literal[ParamKind.KEYWORD_ONLY],
58+
DropAnnotations[GetType[p]]
5759
if IsAssignable[
5860
Literal[PropQuals.HAS_DEFAULT],
5961
GetAnnotations[GetType[p]],
6062
]
61-
else Literal["keyword"],
63+
else Never,
6264
]
6365
for p in Iter[Attrs[T]]
6466
],

tests/test_fastapilike_2.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,13 +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["keyword"]
156-
if typing.IsAssignable[
156+
typing.GetType[p]
157+
if not typing.IsAssignable[
157158
GetDefault[typing.GetInit[p]],
158159
Never,
159160
]
160-
else Literal["keyword", "default"],
161+
else Never,
161162
]
162163
for p in typing.Iter[typing.Attrs[T]]
163164
],

tests/test_schemalike.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
Member,
1313
NamedParam,
1414
Param,
15+
ParamKind,
1516
StrConcat,
1617
)
1718

@@ -50,7 +51,11 @@ class Property:
5051
Callable[
5152
[
5253
Param[Literal["self"], Schemaify[T]],
53-
NamedParam[Literal["schema"], Schema, Literal["keyword"]],
54+
NamedParam[
55+
Literal["schema"],
56+
Schema,
57+
Literal[ParamKind.KEYWORD_ONLY],
58+
],
5459
],
5560
GetType[p],
5661
],

tests/test_type_dir.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -373,14 +373,15 @@ def test_type_members_func_1():
373373
assert name == typing.Literal["foo"]
374374
assert quals == typing.Literal["ClassVar"]
375375

376+
PK = "typing.Literal[<ParamKind.POSITIONAL_OR_KEYWORD: 1>]"
376377
assert (
377378
str(typ)
378-
== "\
379+
== f"\
379380
typing.Callable[[\
380-
typemap.typing.Param[typing.Literal['self'], tests.test_type_dir.Base[int], typing.Never], \
381-
typemap.typing.Param[typing.Literal['a'], int | None, typing.Never], \
382-
typemap.typing.Param[typing.Literal['b'], int, typing.Literal['keyword', \
383-
'default']]], \
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], \
383+
typemap.typing.Param[typing.Literal['b'], int, typing.Literal[\
384+
<ParamKind.KEYWORD_ONLY: 3>], int]], \
384385
dict[str, int]]"
385386
)
386387

@@ -395,10 +396,11 @@ def test_type_members_func_2():
395396
assert name == typing.Literal["cbase"]
396397
assert quals == typing.Literal["ClassVar"]
397398

399+
PK = "typing.Literal[<ParamKind.POSITIONAL_OR_KEYWORD: 1>]"
398400
assert (
399401
str(typ)
400-
== "\
401-
classmethod[tests.test_type_dir.Base[int], tuple[typemap.typing.Param[typing.Literal['a'], int | None, typing.Never], typemap.typing.Param[typing.Literal['b'], ~K, typing.Never]], dict[str, int]]"
402+
== f"\
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]]"
402404
)
403405

404406

@@ -420,7 +422,7 @@ def test_type_members_func_3():
420422
)
421423
assert (
422424
str(evaled)
423-
== "staticmethod[tuple[typemap.typing.Param[typing.Literal['a'], int | typing.Literal['gotcha!'] | Z | None, typing.Never], typemap.typing.Param[typing.Literal['b'], ~K, typing.Never]], dict[str, int | Z]]"
425+
== "staticmethod[tuple[typemap.typing.Param[typing.Literal['a'], int | typing.Literal['gotcha!'] | Z | None, typing.Literal[<ParamKind.POSITIONAL_OR_KEYWORD: 1>], typing.Never], typemap.typing.Param[typing.Literal['b'], ~K, typing.Literal[<ParamKind.POSITIONAL_OR_KEYWORD: 1>], typing.Never]], dict[str, int | Z]]"
424426
)
425427

426428

0 commit comments

Comments
 (0)