pycroscope.extensions¶
Extensions to the type system supported by pycroscope. These can be imported at runtime and used in user code.
Several type system extensions are used with the Annotated type from
PEP 593. This allows them to
be gracefully ignored by other type checkers.
- class pycroscope.extensions.CustomCheck¶
A mechanism for extending the type system with user-defined checks.
To use this, create a subclass of
CustomCheckthat overrides thecan_assignmethod, and place it in anAnnotatedannotation. The return value is equivalent to that ofpycroscope.value.Value.can_assign().A simple example is
LiteralOnly, which is also exposed by pycroscope itself:class LiteralOnly(CustomCheck): def can_assign(self, value: "Value", ctx: "CanAssignContext") -> "CanAssign": for subval in pycroscope.value.flatten_values(value): if not isinstance(subval, pycroscope.value.KnownValue): return pycroscope.value.CanAssignError("Value must be a literal") return {} def func(arg: Annotated[str, LiteralOnly()]) -> None: ... func("x") # ok func(str(some_call())) # error
It is also possible to customize checks in the other direction by overriding the
can_be_assigned()method. For example, if the aboveCustomCheckoverrode thecan_be_assignedmethod instead, a value of typeAnnotated[str, LiteralOnly()]could only be passed to functions that take aLiteralparameter.A
CustomCheckcan also be generic over aTypeVar. To implement support forTypeVar, two more methods must be overridden:walk_values()should yield allTypeVarobjects contained in the check, wrapped in apycroscope.value.TypeVarValue.substitute_typevars()takes a map fromTypeVartopycroscope.value.Valueobjects and returns a newCustomCheck.
- class pycroscope.extensions.PredicateCheck¶
Base class for checks that represent a predicate constraint.
- class pycroscope.extensions.LiteralOnly¶
Custom check that allows only values pycroscope infers as literals.
Example:
def func(arg: Annotated[str, LiteralOnly()]) -> None: ... func("x") # ok func(str(some_call())) # error
This can be useful to prevent user-controlled input in security-sensitive APIs.
- class pycroscope.extensions.NoAny(deep: bool = False, allowed_sources: ~collections.abc.Container[AnySource] = <factory>)¶
Custom check that disallows passing Any.
- class pycroscope.extensions.ValidRegex¶
Custom check that allows only values that are valid regular expressions.
Example:
def func(arg: Annotated[str, ValidRegex()]) -> None: ... func(".*") # ok func("[") # error
- class pycroscope.extensions.AsynqCallable(args: Literal[Ellipsis] | tuple[object, ...], return_type: object)¶
Represents an asynq function (a function decorated with
@asynq()).Similar to
Callable, butAsynqCallablealso supports calls through.asynq(). Because asynq functions can also be called synchronously, an asynq function is assignable to a non-asynq function, but not the reverse.The first argument should be the argument list, as for
Callable. Examples:AsynqCallable[..., int] # may take any arguments, returns an int AsynqCallable[[int], str] # takes an int, returns a str
- class pycroscope.extensions.Intersection(args: tuple[object, ...])¶
An intersection of two (or more) types.
The intersection of two fully static types is the static type that contains all elements common to both types. The intersection of two gradual types is a gradual type that can materialize to all types that are materializations of both elements of the intersection.
- class pycroscope.extensions.Overlapping(arg: object)¶
A type that accepts values whose type overlaps with another type.
Overlapping[T]accepts a value of typeUifT & Uis notNever.
- class pycroscope.extensions.ParameterTypeGuard(varname: str, guarded_type: object)¶
A guard on an arbitrary parameter. Used with
Annotated.Example usage:
def is_int(arg: object) -> Annotated[bool, ParameterTypeGuard["arg", int]]: return isinstance(arg, int)
- class pycroscope.extensions.NoReturnGuard(varname: str, guarded_type: object)¶
A no-return guard on an arbitrary parameter. Used with
Annotated.If the function returns, then the condition is true.
Example usage:
def assert_is_int(arg: object) -> Annotated[bool, NoReturnGuard["arg", int]]: assert isinstance(arg, int)
- class pycroscope.extensions.TypeGuard(guarded_type: object)¶
Type guards, as defined in PEP 647.
New code should instead use
typing_extensions.TypeGuardor (in Python 3.10 and higher)typing.TypeGuard.Example usage:
def is_int_list(arg: list[Any]) -> TypeGuard[list[int]]: return all(isinstance(elt, int) for elt in arg)
- class pycroscope.extensions.ExternalType(type_path: str)¶
ExternalType is a way to refer to a type that is not imported at runtime. The type must be given as a string representing a fully qualified name.
Example usage:
from pycroscope.extensions import ExternalType def function(arg: ExternalType["other_module.Type"]) -> None: pass
To resolve the type, pycroscope will import other_module, but the module using ExternalType does not have to import other_module.
typing.TYPE_CHECKING can be used in a similar fashion, but ExternalType can be more convenient when programmatically generating types. Our motivating use case is our database schema definition file: we would like to map each column to the enum it corresponds to, but those enums are defined in code that should not be imported by the schema definition.
- pycroscope.extensions.reveal_type(value: _T) _T¶
Inspect the inferred type of an expression.
Calling this function will make pycroscope print out the argument’s inferred value in a human-readable format. At runtime it does nothing.
This is automatically exposed as a global during type checking, so in code that is not run at import, reveal_type() can be used without being imported.
Example:
def f(x: int) -> None: reveal_type(x) # Revealed type is "int"
At runtime this returns the argument unchanged.
- pycroscope.extensions.reveal_locals() None¶
Reveal the types of all local variables.
When the type checker encounters a call to this function, it prints the type of all variables in the local scope.
This does nothing at runtime.
- pycroscope.extensions.assert_type(val: _T, typ: Any) _T¶
Assert the inferred static type of an expression.
When a static type checker encounters a call to this function, it checks that the inferred type of val matches the typ argument, and if it dooes not, it emits an error.
Example:
def f(x: int) -> None: assert_type(x, int) # ok assert_type(x, str) # error
This is useful for checking that the type checker interprets a complicated set of type annotations in the way the user intended.
At runtime this returns the first argument unchanged.
- pycroscope.extensions.assert_error() Generator[None]¶
Context manager that asserts that code produces a type checker error.
Example:
with assert_error(): # ok 1 + "x" with assert_error(): # error: no error found in this block 1 + 1
- pycroscope.extensions.deprecated(__msg: str) Callable[[_T], _T]¶
Indicate that a class, function or overload is deprecated.
Usage:
@deprecated("Use B instead") class A: pass @deprecated("Use g instead") def f(): pass @deprecated("int support is deprecated") @overload def g(x: int) -> int: ... @overload def g(x: str) -> int: ...
When this decorator is applied to an object, the type checker will generate a diagnostic on usage of the deprecated object.
No runtime warning is issued. The decorator sets the
__deprecated__attribute on the decorated object to the deprecation message passed to the decorator.See PEP 702 for details.
- pycroscope.extensions.get_overloads(fully_qualified_name: str) list[Callable[[...], Any]]¶
Return all defined runtime overloads for this fully qualified name.
- pycroscope.extensions.get_type_evaluations(fully_qualified_name: str) Sequence[Callable[[...], Any]]¶
Return the type evaluation function for this fully qualified name, or None.
- pycroscope.extensions.overload(func: Callable[[...], Any]) Callable[[...], Any]¶
A version of typing.overload that is inspectable at runtime.
If this decorator is used for a function some_module.some_function, calling
pycroscope.extensions.get_overloads("some_module.some_function")will return all the runtime overloads.
- pycroscope.extensions.patch_typing_overload() None¶
Monkey-patch
typing.overloadwith our custom@overloaddecorator.This allows files imported after this file to use the
@overloaddecorator and have it be recognized by pycroscope.
- pycroscope.extensions.evaluated(func: Callable[[...], Any]) Callable[[...], Any]¶
Marks a type evaluation function.
- pycroscope.extensions.is_provided(arg: Any) bool¶
Helper function for type evaluators.
May not be called at runtime.
- pycroscope.extensions.is_positional(arg: Any) bool¶
Helper function for type evaluators.
May not be called at runtime.
- pycroscope.extensions.is_keyword(arg: Any) bool¶
Helper function for type evaluators.
May not be called at runtime.
- pycroscope.extensions.is_of_type(arg: Any, type: Any, *, exclude_any: bool = False) bool¶
Helper function for type evaluators.
May not be called at runtime.
- pycroscope.extensions.show_error(message: str, *, argument: Any | None = None) bool¶
Helper function for type evaluators.
May not be called at runtime.
- pycroscope.extensions.has_extra_keys(value_type: object = typing.Any) Callable[[_T], _T]¶
Decorator for
TypedDicttypes, indicating that the dict has additional keys of the given type.This is an experimental feature.
Example usage:
@has_extra_keys(str) class TD(TypedDict): a: int def f(x: TD) -> None: assert_type(x["a"], int) assert_type(x["arbitrary_key"], str)
- class pycroscope.extensions.EnumName¶
A type representing the names of members of an enum.
Equivalent to a Literal type, but using this will produce nicer error messages for users.