pyanalyze.signature

The Signature object and associated functionality. This provides a way to represent rich callable objects and type check calls.

class pyanalyze.signature.MaximumPositionalArgs(value: T, applicable_to: Tuple[str, ...] = (), from_command_line: bool = False, priority: int = 0)

If calls have more than this many positional arguments, attempt to turn them into keyword arguments.

exception pyanalyze.signature.InvalidSignature

Raised when an invalid signature is encountered.

class pyanalyze.signature.PossibleArg(name: str | None)
class pyanalyze.signature.PosOrKeyword(name: str, is_required: bool)
class pyanalyze.signature.ActualArguments(positionals: List[Tuple[bool, Composite]], star_args: Value | None, keywords: Dict[str, Tuple[bool, Composite]], star_kwargs: Value | None, kwargs_required: bool, pos_or_keyword_params: Container[int | str], ellipsis: bool = False, param_spec: TypeVarValue | None = None)

Represents the actual arguments to a call.

Before creating this class, we decompose *args and **kwargs arguments of known composition into additional positional and keyword arguments, and we merge multiple *args or **kwargs.

Creating the ActualArguments for a call is independent of the signature of the callee.

class pyanalyze.signature.CallReturn(return_value: Value, sig: Signature, is_error: bool = False, used_any_for_match: bool = False, remaining_arguments: ActualArguments | None = None)

Return value of a preprocessed call.

This returns data that is useful for overload resolution.

return_value: Value

The return value of the function.

sig: Signature

Signature that was used for this call.

is_error: bool

Whether there was an error in this call. Used only for overload resolutioon.

used_any_for_match: bool

Whether Any was used for this match. Used only for overload resolution.

remaining_arguments: ActualArguments | None

Arguments that still need to be processed. Used only for overload resolution.

class pyanalyze.signature.ImplReturn(return_value: Value, constraint: AbstractConstraint = NullConstraint(), no_return_unless: AbstractConstraint = NullConstraint())

Return value of impl functions.

These functions return either a single pyanalyze.value.Value object, indicating what the function returns, or an instance of this class.

return_value: Value

The return value of the function.

constraint: AbstractConstraint

A pyanalyze.stacked_scopes.Constraint indicating things that are true if the function returns a truthy value.

no_return_unless: AbstractConstraint

A pyanalyze.stacked_scopes.Constraint indicating things that are true unless the function does not return.

class pyanalyze.signature.CallContext(vars: Dict[str, Value], visitor: NameCheckVisitor, composites: Dict[str, Composite], node: AST | None)

The context passed to an impl function.

vars: Dict[str, Value]

Dictionary of variable names passed to the function.

visitor: NameCheckVisitor

Using the visitor can allow various kinds of advanced logic in impl functions.

node: AST | None

AST node corresponding to the function call. Useful for showing errors.

varname_for_arg(arg: str) VarnameWithOrigin | None

Return a varname corresponding to the given function argument.

This is useful for creating a pyanalyze.stacked_scopes.Constraint referencing the argument.

show_error(message: str, error_code: Error = Error(name='incompatible_call', description='Incompatible arguments to a function call.'), *, arg: str | None = None, node: AST | None = None, detail: str | None = None) None

Show an error.

If the arg parameter is given, we attempt to find the AST for that argument to the function and point the error to it.

class pyanalyze.signature.ParameterKind(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)

Kinds of parameters.

ELLIPSIS = 6

Special kind for Callable[…, T]. Such callables are compatible with any other callable. There can only be one ELLIPSIS parameter and it must be the last one.

class pyanalyze.signature.SigParameter(name: str, kind: ~pyanalyze.signature.ParameterKind = ParameterKind.POSITIONAL_OR_KEYWORD, default: ~pyanalyze.value.Value | None = None, annotation: ~pyanalyze.value.Value = AnyValue(source=<AnySource.unannotated: 6>))

Represents a single parameter to a callable.

name: str

Name of the parameter.

kind: ParameterKind = 1

How the parameter can be passed.

default: Value | None = None

The default for the parameter, or None if there is no default.

annotation: Value = AnyValue(source=<AnySource.unannotated: 6>)

Type annotation for the parameter.

empty

alias of _empty

class pyanalyze.signature.Signature(parameters: Dict[str, SigParameter], return_value: Value, impl: Callable[[CallContext], Value | ImplReturn] | None = None, callable: object | None = None, is_asynq: bool = False, has_return_annotation: bool = True, allow_call: bool = False, evaluator: Evaluator | None = None, deprecated: str | None = None)

Represents the signature of a Python callable.

This is used to type check function calls and it powers the pyanalyze.value.CallableValue class.

parameters: Dict[str, SigParameter]

An ordered mapping of the signature’s parameters.

return_value: Value

What the callable returns.

impl: Callable[[CallContext], Value | ImplReturn] | None = None

impl function for this signature.

callable: object | None = None

The callable that this signature represents.

is_asynq: bool = False

Whether this signature represents an asynq function.

allow_call: bool = False

Whether type checking can call the actual function to retrieve a precise return value.

evaluator: Evaluator | None = None

Type evaluator for this function.

deprecated: str | None = None

Deprecation message for this callable.

bind_arguments(actual_args: ActualArguments, ctx: CheckCallContext) Composite]] | None

Attempt to bind the parameters in the signature to the arguments actually passed in.

Nomenclature: - parameters: the formal parameters of the callable - arguments: the arguments passed in in this call - bound arguments: the mapping of parameter names to values produced by this call

check_call(args: ~typing.Iterable[~typing.Tuple[~pyanalyze.stacked_scopes.Composite, None | str | ~pyanalyze.signature.PossibleArg | ~typing.Literal[*args, **kwargs, ellipsis] | ~pyanalyze.value.TypeVarValue | ~pyanalyze.signature.PosOrKeyword]], visitor: NameCheckVisitor, node: ~ast.AST | None) Value

Type check a call to this Signature with the given arguments.

This may call the impl function or the underlying callable, but normally just uses inspect.Signature.bind().

maybe_show_too_many_pos_args_error(*, args: ~typing.Sequence[~typing.Tuple[~pyanalyze.stacked_scopes.Composite, None | str | ~pyanalyze.signature.PossibleArg | ~typing.Literal[*args, **kwargs, ellipsis] | ~pyanalyze.value.TypeVarValue | ~pyanalyze.signature.PosOrKeyword]], bound_args: ~typing.Dict[str, ~typing.Tuple[int | str | ~typing.Literal[default, *args, **kwargs, unknown], ~pyanalyze.stacked_scopes.Composite]], ctx: ~pyanalyze.signature.CheckCallContext, node: ~ast.Call) None

Show an error if the call to this Signature has too many positional arguments.

can_assign(other: Signature | OverloadedSignature, ctx: CanAssignContext) CanAssignError

Equivalent of pyanalyze.value.Value.can_assign(). Checks whether another Signature is compatible with this Signature.

get_asynq_value() Signature

Return the Signature for the .asynq attribute of an pyanalyze.extensions.AsynqCallable.

classmethod make(parameters: Iterable[SigParameter], return_annotation: Value | None = None, *, impl: Callable[[CallContext], Value | ImplReturn] | None = None, callable: object | None = None, has_return_annotation: bool = True, is_asynq: bool = False, allow_call: bool = False, evaluator: Evaluator | None = None, deprecated: str | None = None) Signature

Create a Signature object.

This is more convenient to use than the constructor because it abstracts away the creation of the underlying inspect.Signature.

pyanalyze.signature.ANY_SIGNATURE = Signature(parameters={'...': SigParameter(name='...', kind=<ParameterKind.ELLIPSIS: 6>, default=None, annotation=AnyValue(source=<AnySource.unannotated: 6>))}, return_value=AnyValue(source=<AnySource.explicit: 2>), impl=None, callable=None, is_asynq=False, has_return_annotation=True, allow_call=False, evaluator=None, deprecated=None)

Signature that should be compatible with any other Signature.

pyanalyze.signature.preprocess_args(args: ~typing.Iterable[~typing.Tuple[~pyanalyze.stacked_scopes.Composite, None | str | ~pyanalyze.signature.PossibleArg | ~typing.Literal[*args, **kwargs, ellipsis] | ~pyanalyze.value.TypeVarValue | ~pyanalyze.signature.PosOrKeyword]], ctx: ~pyanalyze.signature.CheckCallContext) ActualArguments | None

Preprocess the argument list. Produces an ActualArguments object.

class pyanalyze.signature.OverloadedSignature(sigs: Sequence[Signature])

Represent an overloaded function.

check_call(args: ~typing.Iterable[~typing.Tuple[~pyanalyze.stacked_scopes.Composite, None | str | ~pyanalyze.signature.PossibleArg | ~typing.Literal[*args, **kwargs, ellipsis] | ~pyanalyze.value.TypeVarValue | ~pyanalyze.signature.PosOrKeyword]], visitor: NameCheckVisitor, node: ~ast.AST | None) Value

Check a call to an overloaded function.

The way overloads are handled is not well specified in any PEPs. Useful resources include:

Our behavior is closer to mypy. The general rule is to pick the first overload that matches and return an error otherwise, but there are two twists: Any and unions.

Before we do a full check, we first check only whether the argument names and numbers match by calling Signature.bind_arguments() (a trick we picked up from pyright). This makes for better error messages.

If an overload matched only due to Any, we continue looking for more overloads. If there are other matching overloads, we return Any (with AnySource.multiple_overload_matches). This is different from pyright’s behavior: pyright picks the first overload regardless of Any, which is unsafe in general. Returning a Union would be more precise, but may lead to false positives according to experience from other type checkers. A match “due to Any” is defined as a check that succeeded because Any was on the right-hand side but not the left-hand side of a typecheck.

This is implemented by setting a flag on the pyanalyze.value.CanAssignContext when a type check succeeds due to Any. This flag gets propagated to ImplReturn.used_any_for_match.

If an overload does not match, but one of the arguments passed was a Union, we try all the components of the Union separately. If some of them match, we subtract them from the Union and try the remaining overloads with a narrower Union. In this case, we return a Union of the return values of all the matching overloads on success.

The decomposition happens in the private _check_param_type_compatibility method of Signature. When we perform decomposition, this method returns a pyanalyze.value.Value representing the remaining union members. This value is then used to construct a new ActualArguments object, which ends up in ImplReturn.remaining_arguments.

An overload that matches without requiring use of Any or union decomposition is called a “clean match”.

class pyanalyze.signature.BoundMethodSignature(signature: Signature | OverloadedSignature, self_composite: Composite, return_override: Value | None = None)

Signature for a method bound to a particular value.