typing_validation.plugins

The extension point: how a parametrised class declares the way its type arguments are validated.

The hook needs no machinery of its own, because the dispatch point already exists. It is the generic-class arm — the one reached when a parametrised type’s origin is a plain class the core knows nothing about — which is precisely where the core has run out of things it can determine on its own.

It is also free. int, list[int], dict[str, int], unions and literals all resolve long before that arm, so nothing that is not already unknown pays anything for its existence.

PluginCheck

PluginCheck = PluginCheck

What a plugin must provide: given a value and the type arguments, say whether the value is valid. Nothing more is required.

Asking every plugin author to also emit source for the compiled path would be an absurd toll for supporting one type. A plugin may optionally supply more — structure, diagnostics, an emitter — but a boolean is the whole obligation.

PluginComponents

PluginComponents = PluginComponents

Optional: which of a parametrised class’s type arguments the core validates, by position.

Not every type argument is one, and numpy.ndarray[shape, dtype] has one of each. The shape is an ordinary type, which the core checks the array’s .shape tuple against. numpy.dtype[numpy.uint8] is a specification the plugin interprets, and never a validation target in its own right — so NumPy declares (0,).

The core cannot tell them apart, and guessing wrong is not harmless. Treating every argument as a component makes numpy.dtype[numpy.uint8] one — and it is itself a parametrised NumPy class with no validator of its own, so totality poisons it, and with it every array type there is. Declaring components is what keeps totality propagating through the arguments that deserve it and out of the ones that do not.

Absent this, a plugin’s arguments are opaque: the plugin checks them, and totality does not reach inside.

plugin_import

plugin_import(origin, /)[source]

The import that would enable this distribution’s plugin for a class, or None if we ship no plugin for it.

A class we ship a plugin for is one whose arguments are checkable, so leaving them unchecked would report success we had not earned. Answering non-None here is therefore what turns an unregistered parametrised class from “arguments unchecked, by design” into an error.

Parameters:

origin (Any)

Return type:

str | None

register_validator

register_validator(cls, check, /, components=None)[source]

Declare how the type arguments of a parametrised class are validated.

This is the route for classes you do not own. For a class you do own, define a __validate__ classmethod instead, and a __validate_components__ class attribute if you want components:

class Box[T]:
    __validate_components__ = (0,)

    @classmethod
    def __validate__(cls, val, args):
        return is_valid(val.item, args[0])

cls is the class unparametrised — numpy.ndarray, not numpy.ndarray[shape, dtype]. components says which type arguments the core validates, by position, so that totality propagates through them; see PluginComponents for why the core cannot work that out for itself.

Raises:

TypeError – if cls is not a class, check is not callable, or components is not a sequence of positions.

Parameters:
Return type:

None

registered_components

registered_components(cls, /)[source]

Which of a class’s type arguments the core validates, by position, or None if it declared none.

Parameters:

cls (type)

Return type:

tuple[int, …] | None

registered_validator

registered_validator(cls, /)[source]

The validator registered for a class, or None if there is none.

Parameters:

cls (type)

Return type:

PluginCheck | None

unsupported_explanation

unsupported_explanation(origin, /)[source]

Why a parametrised class could not be validated, and what would fix it.

Parameters:

origin (Any)

Return type:

str