Skip to content

Commit fbca993

Browse files
Minor ParamSpec improvements (#385)
* make Ellipsis a ParameterKind * P.args and P.kwargs * fix __str__ * changelog
1 parent 94d5f3a commit fbca993

9 files changed

+181
-91
lines changed

docs/changelog.md

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Unreleased
44

5+
- Small improvements to `ParamSpec` support (#385)
56
- Allow `CustomCheck` to customize what values
67
a value can be assigned to (#383)
78
- Fix incorrect inference of `self` argument on

pyanalyze/annotations.py

+22-32
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
TypeGuard,
6060
)
6161
from .find_unused import used
62-
from .signature import SigParameter, Signature, ParameterKind
62+
from .signature import ELLIPSIS_PARAM, SigParameter, Signature, ParameterKind
6363
from .safe import is_typing_name, is_instance_of_typing_name
6464
from . import type_evaluation
6565
from .value import (
@@ -75,6 +75,8 @@
7575
MultiValuedValue,
7676
NO_RETURN_VALUE,
7777
NoReturnGuardExtension,
78+
ParamSpecArgsValue,
79+
ParamSpecKwargsValue,
7880
ParameterTypeGuardExtension,
7981
TypeGuardExtension,
8082
TypedValue,
@@ -391,7 +393,7 @@ def _type_from_runtime(val: Any, ctx: Context, is_typeddict: bool = False) -> Va
391393
args = typing_inspect.get_args(val)
392394
return _value_of_origin_args(Callable, args, val, ctx)
393395
elif val is AsynqCallable:
394-
return CallableValue(Signature.make([], is_ellipsis_args=True, is_asynq=True))
396+
return CallableValue(Signature.make([ELLIPSIS_PARAM], is_asynq=True))
395397
elif isinstance(val, type):
396398
return _maybe_typed_value(val)
397399
elif val is None:
@@ -425,6 +427,10 @@ def _type_from_runtime(val: Any, ctx: Context, is_typeddict: bool = False) -> Va
425427
return TypeVarValue(tv, bound=bound, constraints=constraints)
426428
elif is_instance_of_typing_name(val, "ParamSpec"):
427429
return TypeVarValue(val, is_paramspec=True)
430+
elif is_instance_of_typing_name(val, "ParamSpecArgs"):
431+
return ParamSpecArgsValue(val.__origin__)
432+
elif is_instance_of_typing_name(val, "ParamSpecKwargs"):
433+
return ParamSpecKwargsValue(val.__origin__)
428434
elif is_typing_name(val, "Final") or is_typing_name(val, "ClassVar"):
429435
return AnyValue(AnySource.incomplete_annotation)
430436
elif typing_inspect.is_classvar(val) or typing_inspect.is_final_type(val):
@@ -467,14 +473,9 @@ def _type_from_runtime(val: Any, ctx: Context, is_typeddict: bool = False) -> Va
467473
[TypeGuardExtension(_type_from_runtime(val.__type__, ctx))],
468474
)
469475
elif isinstance(val, AsynqCallable):
470-
params, is_ellipsis_args = _callable_args_from_runtime(
471-
val.args, "AsynqCallable", ctx
472-
)
476+
params = _callable_args_from_runtime(val.args, "AsynqCallable", ctx)
473477
sig = Signature.make(
474-
params,
475-
_type_from_runtime(val.return_type, ctx),
476-
is_asynq=True,
477-
is_ellipsis_args=is_ellipsis_args,
478+
params, _type_from_runtime(val.return_type, ctx), is_asynq=True
478479
)
479480
return CallableValue(sig)
480481
elif isinstance(val, ExternalType):
@@ -506,22 +507,22 @@ def _type_from_runtime(val: Any, ctx: Context, is_typeddict: bool = False) -> Va
506507

507508
def _callable_args_from_runtime(
508509
arg_types: Any, label: str, ctx: Context
509-
) -> Tuple[Sequence[SigParameter], bool]:
510+
) -> Sequence[SigParameter]:
510511
if arg_types is Ellipsis or arg_types == [Ellipsis]:
511-
return [], True
512+
return [ELLIPSIS_PARAM]
512513
elif type(arg_types) in (tuple, list):
513514
if len(arg_types) == 1:
514515
(arg,) = arg_types
515516
if arg is Ellipsis:
516-
return [], True
517+
return [ELLIPSIS_PARAM]
517518
elif is_typing_name(getattr(arg, "__origin__", None), "Concatenate"):
518519
return _args_from_concatenate(arg, ctx)
519520
elif is_instance_of_typing_name(arg, "ParamSpec"):
520521
param_spec = TypeVarValue(arg, is_paramspec=True)
521522
param = SigParameter(
522523
"__P", kind=ParameterKind.PARAM_SPEC, annotation=param_spec
523524
)
524-
return [param], False
525+
return [param]
525526
types = [_type_from_runtime(arg, ctx) for arg in arg_types]
526527
params = [
527528
SigParameter(
@@ -533,23 +534,21 @@ def _callable_args_from_runtime(
533534
)
534535
for i, typ in enumerate(types)
535536
]
536-
return params, False
537+
return params
537538
elif is_instance_of_typing_name(arg_types, "ParamSpec"):
538539
param_spec = TypeVarValue(arg_types, is_paramspec=True)
539540
param = SigParameter(
540541
"__P", kind=ParameterKind.PARAM_SPEC, annotation=param_spec
541542
)
542-
return [param], False
543+
return [param]
543544
elif is_typing_name(getattr(arg_types, "__origin__", None), "Concatenate"):
544545
return _args_from_concatenate(arg_types, ctx)
545546
else:
546547
ctx.show_error(f"Invalid arguments to {label}: {arg_types!r}")
547-
return [], True
548+
return [ELLIPSIS_PARAM]
548549

549550

550-
def _args_from_concatenate(
551-
concatenate: Any, ctx: Context
552-
) -> Tuple[Sequence[SigParameter], bool]:
551+
def _args_from_concatenate(concatenate: Any, ctx: Context) -> Sequence[SigParameter]:
553552
types = [_type_from_runtime(arg, ctx) for arg in concatenate.__args__]
554553
params = [
555554
SigParameter(
@@ -561,7 +560,7 @@ def _args_from_concatenate(
561560
)
562561
for i, annotation in enumerate(types)
563562
]
564-
return params, False
563+
return params
565564

566565

567566
def _get_typeddict_value(
@@ -987,14 +986,8 @@ def _value_of_origin_args(
987986
*arg_types, return_type = args
988987
if len(arg_types) == 1 and isinstance(arg_types[0], list):
989988
arg_types = arg_types[0]
990-
params, is_ellipsis_args = _callable_args_from_runtime(
991-
arg_types, "Callable", ctx
992-
)
993-
sig = Signature.make(
994-
params,
995-
_type_from_runtime(return_type, ctx),
996-
is_ellipsis_args=is_ellipsis_args,
997-
)
989+
params = _callable_args_from_runtime(arg_types, "Callable", ctx)
990+
sig = Signature.make(params, _type_from_runtime(return_type, ctx))
998991
return CallableValue(sig)
999992
elif is_typing_name(origin, "Annotated"):
1000993
origin, metadata = args
@@ -1077,10 +1070,7 @@ def _make_callable_from_value(
10771070
if args == KnownValue(Ellipsis):
10781071
return CallableValue(
10791072
Signature.make(
1080-
[],
1081-
return_annotation=return_annotation,
1082-
is_ellipsis_args=True,
1083-
is_asynq=is_asynq,
1073+
[ELLIPSIS_PARAM], return_annotation=return_annotation, is_asynq=is_asynq
10841074
)
10851075
)
10861076
elif isinstance(args, SequenceIncompleteValue):

pyanalyze/arg_spec.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from .stacked_scopes import Composite, uniq_chain
2323
from .signature import (
2424
ANY_SIGNATURE,
25+
ELLIPSIS_PARAM,
2526
ConcreteSignature,
2627
Impl,
2728
MaybeSignature,
@@ -642,9 +643,8 @@ def _uncached_get_argspec(
642643
inspect_sig = self._safe_get_signature(constructor)
643644
if inspect_sig is None:
644645
return Signature.make(
645-
[],
646+
[ELLIPSIS_PARAM],
646647
return_type,
647-
is_ellipsis_args=True,
648648
callable=obj,
649649
allow_call=allow_call,
650650
)
@@ -703,9 +703,8 @@ def _uncached_get_argspec(
703703
def _make_any_sig(self, obj: object) -> Signature:
704704
if FunctionsSafeToCall.contains(obj, self.options):
705705
return Signature.make(
706-
[],
706+
[ELLIPSIS_PARAM],
707707
AnyValue(AnySource.inference),
708-
is_ellipsis_args=True,
709708
is_asynq=True,
710709
allow_call=True,
711710
callable=obj,

0 commit comments

Comments
 (0)