typechecked packageπŸ”—

Type hint related validators and utilities.

class typechecked.ErrorTag(
value,
names=<not given>,
*values,
module=None,
qualname=None,
type=None,
start=1,
boundary=None,
)[source]πŸ”—

Bases: str, Enum

Base class for error tag enums.

ErrorTags are used to identify specific error condition sources in the typechecked package.

Tests use these tags to assert specific error condition paths at a much more granular level than just checking the exception type or message.

class typechecked.Immutable(*args, **kwargs)[source]πŸ”—

Bases: Protocol

Protocol for user defined immutable types.

This class can be used as a base class for user defined types to indicate that the type is immutable.

Example:

When applied to an existing class, it indicates that the sub-class is an immutable version of the base class.

Note that the protocol does not enforce immutability at runtime - it is only used for type hinting and validation purposes. It is the responsibility of the user to ensure that the sub-class is indeed immutable.

For example:

from typechecked.types import Immutable

class Point2D:
    def __init__(self, x: float, y: float) -> None:
        self._x = x
        self._y = y

    @property
    def x(self) -> float:
        return self._x

    @x.setter
    def x(self, value: float) -> None:
        self._x = value

    @property
    def y(self) -> float:
        return self._y

    @y.setter
    def y(self, value: float) -> None:
        self._y = value

class ImmutablePoint2D(Point2D, Immutable):
    '''An immutable version of Point2D.

    We inherit from Point2D to get the interface, and Immutable to mark it.
    We must override the setters to enforce immutability.
    '''
    def __init__(self, x: float, y: float) -> None:
        super().__init__(x, y)

    @property
    def x(self) -> float:
        return self._x

    @x.setter
    def x(self, value: float) -> None:
        raise AttributeError("Cannot modify immutable instance.")

    @property
    def y(self) -> float:
        return self._y

    @y.setter
    def y(self, value: float) -> None:
        raise AttributeError("Cannot modify immutable instance.")

This indicates that ImmutablePoint2D is an immutable version of Point2D and should be treated as such during validation and serialization.

It is also used for type hinting purposes to indicate immutability for user defined types.

Checking against this protocol can be done using isinstance():

It uses the existence of an __immutable__ attribute to mark a user-defined class as immutable for validation purposes.

Additionally, the following built-in immutable types are explicitly registered as virtual subclasses of this protocol: bool, int, float, complex, str, bytes, and type(None).

This allows isinstance checks to recognize these built-in types as immutable even though they do not explicitly inherit from the Immutable protocol.

class typechecked.ImmutableTypedDict[source]πŸ”—

Bases: TypedDict

TypedDict subclass to mark a TypedDict as immutable.

This class can be used as a base class for user defined TypedDicts to indicate that the TypedDict is intended to be immutable.

Note

This class does not enforce immutability at runtime and does not modify the runtime behavior of the TypedDict. It simply adds a special key __immutable__ to the definition of the TypedDict to serve as a marker for validation purposes.

That special key uses the NotRequired and Never types to indicate that the key should not be provided in instances of the TypedDict.

It can be manually added to existing TypedDict definitions to mark them as immutable without needing to inherit from this class directly. Because the key is marked as NotRequired[Never], type checkers will enforce that the key is NOT provided when creating instances. It has to be added to the TypedDict definition itself using a a β€˜total=False’ TypedDict inheritance to avoid making the key required.

Example:

If a TypedDict inherits from ImmutableTypedDict, typechecked will treat instances of that TypedDict as immutable for β€˜is_immutable’ checks and validation.

Instances are still standard Python dict objects by default, so it is the responsibility of the user to ensure they are not modified after creation. This class does not prevent runtime modification; its primary purpose is to help static type checkers and validation tools to detect and flag such misuse.

The runtime enforcement of immutability is outside the scope of this class, though it can be implemented separately using a wrapper, proxy, or β€˜mimic’ class that structurally matches the TypedDict but is not a regular Python dict if desired.

If a TypedDict definition is generated from a ImmutableTypedDict and a proxy or wrapper class is created to enforce immutability at runtime, that wrapper class should use the Immutable protocol to indicate its immutability rather than inheriting from ImmutableTypedDict because it is not a TypedDict itself.

It can be checked against the Immutable protocol using issubclass():

Example:

Note

isinstance() checks will not work for TypedDict instances; use issubclass() on the type instead.

While the class definition is a subclass of Immutable, instances are standard Python dict objects at runtime and cannot be distinguished from mutable dictionaries.

exception typechecked.TypeCheckError(
msg: str,
*,
tag: ErrorTag,
)[source]πŸ”—

Bases: TaggedException[TypeError], TypeError

Class for all TypeChecked type check errors.

It is a specialized TypeError that includes a tag code to identify the specific location in the code where the error was raised.

This tag code is primarily used for testing and debugging purposes.

Usage:

raise TypeCheckError(β€œAn error occurred”, tag=MyErrorTags.SOME_ERROR)

Parameters:
  • msg (str) – The error message.

  • tag (ErrorTag) – The tag code.

Raises a TypeCheckError with the given message and tag.

Parameters:
  • msg (str) – The error message.

  • tag (ErrorTag) – The tag code.

typechecked.clear_typechecked_cache() None[source]πŸ”—

Clear the internal type hint validation cache.

typechecked.is_immutable(
obj: Any,
depth: int = 200,
) bool[source]πŸ”—

Check if the given object is immutable.

It recursively checks container types to ensure all contained elements are also immutable.

Parameters:
  • obj (Any) – The object to check.

  • depth (int) – Maximum recursion depth to prevent infinite loops on cyclic references.

Return bool:

True if the object is immutable, False otherwise.

Raises:
  • TypeError – If the β€˜depth’ parameter is not an integer.

  • ValueError – If the β€˜depth’ parameter is not a positive integer.

  • RecursionError – If the maximum recursion depth is exceeded.

typechecked.is_immutable_data_typehint(
type_hint: Any,
) bool[source]πŸ”—

Check if a type hint represents an immutable data type.

Parameters:

type_hint (Any) – The type hint to check.

Return bool:

True if the type hint represents an immutable data type, False otherwise.

typechecked.is_immutable_typeddict_typehint(
type_hint: Any,
) TypeGuard[type[ImmutableTypedDict]][source]πŸ”—

Safely check if a type hint inherits from ImmutableTypedDict.

This function is a TypeGuard, which allows static type checkers to understand that if this function returns True, the given type_hint is a class that inherits from ImmutableTypedDict.

Parameters:

type_hint (Any) – The type hint to check.

Return bool:

True if the type hint inherits from ImmutableTypedDict, False otherwise.

typechecked.isinstance_of_typehint(
obj: Any,
type_hint: Any,
*,
strict_typed_dict: bool = False,
depth: int = 50,
consume_iterators: bool = False,
noncachable_types: set[type[Any]] | None = None,
) bool[source]πŸ”—

Check if an object is an instance of a given type hint. Supports basic types, generics, Union, Literal, and TypedDict.

This function acts like a runtime version of isinstance() for type hints.

Example:

It can validate whether an object conforms to complex type hints the same way that static type checkers do - but at runtime. This makes it useful for validating function arguments, configuration data, or any other data structures against expected types at runtime.

This is much, much slower than a normal isinstance() check due to the complexity of full type hint validation. It is not intended for performance-critical paths. If you can use normal isinstance() checks, do so.

It does, however, use an internal cache to speed up repeated checks for the same object and type hint combination, especially for immutable objects and it caches internal validation results for immutable sub-objects.

While caching improves performance for repeated checks, the first-time validation of complex type hints may still be relatively slow.

Slow in this context means on the order of milliseconds for complex nested structures. And it can be much slower if the structure is very deep or complex. An example of a pathological case would be a list of millions of items where each item must be validated against a complex type hint. A type like list[str | int | dict[str, list[float | None]]] with millions of items would be very slow to validate the first time.

In such cases, consider simplifying the type hints. For example, using list[Any] or list[object] would be much faster, though less precise or simply write a custom validation function for your specific use case.

It is MUCH faster to do something like:

than to do: .. code-block:: python

valid = isinstance_of_typehint(data, list[str | int | dict[str, list[float | None]]])

This can be greatly mitigated by caching if you use immutable objects and repeatedly check the same type hints against them or sub-objects within them.

Where isinstance_of_typehint is most useful is in validating configuration data or other data structures that are not performance-critical but need to be validated against complex type hints at runtime.

Things like nested configuration dictionaries (particularly TypedDicts), JSON data structures, or other complex data that benefit from type hint validation but are not performance-critical.

This is a simple and easily understandable method for runtime type hint validation, but it cannot cover all edge cases due to the complexity of Python’s type hinting system. It is designed to handle the most common use cases effectively.

If you need both performance and type hint validation, consider using specialized libraries like pydantic or attrs that are optimized for runtime data validation with type hints. They are more complex to use but can offer far better performance for specific use cases.

A Immutable superclass can be used to mark user-defined classes as immutable for caching purposes. There is also a ImmutableTypedDict type that can be used to mark immutable TypedDicts. If your objects are immutable, caching will be much more effective.

The checker automatically treats built-in immutable types (NoneType, bool, int, float, complex, str, bytes) as immutable for caching purposes and when composed using immutable containers such as frozenset, tuple, and MappingProxyType.

While it tries to be efficient, it cannot be optimized for high-performance scenarios in general. You should benchmark your specific use case if performance is a concern.

Warning

Type hints cannot be strings. If you pass a string type hint (e.g., a forward reference), a TypeCheckedValueError will be raised.

The object itself can be of any type, including user-defined classes and use forward references, but the type hint itself must be a valid type hint object without using a string to represent types.

Examples:

Deeply nested or cyclic structures may lead to performance issues or maximum recursion depth errors. The depth parameter can help mitigate this by limiting the recursion depth.

It is important to note that type hint validation is not foolproof and may not cover all edge cases. This is designed for common use cases.

Because of the complexity of type hint checks, this function may not be able to definitively determine type hint compliance for all type hints, especially with deeply nested structures.

The depth parameter limits the recursion depth for nested structures. If the depth limit is reached, the function will return True for validity, but will not validate any deeper levels of the object. This is to prevent infinite recursion in case of cyclic references or excessively deep structures.

The depth parameter is defined as the number of nested levels to check within the object structure (including the top-level object). For example:

  • int has a depth of 0.

  • list[int] has a depth of 1.

  • list[list[int]] has a depth of 2.

  • dict[str, list[int]] has a depth of 2.

If strict_typed_dict parameter is False, it can validate any Mapping subclass against a TypedDict definition. If True, it will only validate actual dict instances. The default is False (allow any Mapping subclass).

Iterators are single-pass by nature. By default, this function will not consume iterators during validation to avoid side effects. If consume_iterators is set to True, it will consume iterators to validate their contents, but this may exhaust the iterator and affect subsequent usage.

Parameters:
  • obj (Any) – The object to check against the type hint.

  • type_hint (Any) – The type hint to check against.

  • strict_typed_dict (bool) – Whether to enforce that TypedDict checks require actual TypedDict instances.

  • depth (int) – (default=50) The recursion depth limit for nested structures.

  • consume_iterators (bool) – (optional, default=False) Whether to consume iterators during validation.

  • noncachable_types (set[type[Any]] | None) – (optional, default=False) Set of types that should not be cached during validation. This is intended for types that are immutable but have high variability (e.g., datetime) making caching less effective (and tending to bloat the cache without significant performance benefit). The internal default set includes NoneType, bool, int, float, complex, str, and bytes and they will always be treated as non-cachable.

Return bool:

True if the object matches the type hint, False otherwise.

Raises:

TypeCheckError – If any failure occurs during validation.

typechecked.validate_immutable(
obj: Any,
name: str,
message: str | None = None,
depth: int = 200,
) bool[source]πŸ”—

Validate that the given object is immutable.

It recursively checks container types to ensure all contained elements are also immutable.

The β€˜name’ parameter is used in the error message to identify the object being validated.

Parameters:
  • obj (Any) – The object to check.

  • name (str) – The name of the object being validated.

  • message (str | None) – Custom error message to use if validation fails. If None, a default message is used.

  • depth (int) – Maximum recursion depth to prevent infinite loops on cyclic references.

Return bool:

True if the object is immutable.

Raises:
  • TypeCheckError – If the object is not immutable

  • TypeError – If the calling parameters are of incorrect types.

  • ValueError – If the calling parameters have invalid values.