Tip

Feed the bear! The bear is rooting around in your refuse pile. You feel sadness.

# Too Long; Didn’t Read (tl;dr)

Let’s type-check like greased lightning! Thanks to cheatsheets like this, you no longer have to know how to use software to use software. `\o/`

    # ..................{              IMPORTS                }..................
    # Import the core @beartype decorator.
    from beartype import beartype

    # Import type hint factories from "beartype.typing", a stand-in replacement
    # for the standard "typing" module providing improved forward compatibility
    # with future Python releases. For example:
    # * "beartype.typing.Set is set" under Python ≥ 3.9 to satisfy PEP 585.
    # * "beartype.typing.Set is typing.Set" under Python < 3.9 to satisfy PEP 484.
    from beartype import typing

    # Or, directly import these factories from the standard "typing" module. Note
    # that PEP 585 deprecated many of these under Python ≥ 3.9, where @beartype
    # now emits non-fatal deprecation warnings at decoration time. See also:
    #     https://docs.python.org/3/library/typing.html
    import typing

    # Or, directly import PEP 585 type hints. Note this requires Python ≥ 3.9.
    from collections import abc

    # Import backported type hint factories from "typing_extensions", improving
    # portability across Python versions (e.g., "typing.Literal" needs Python ≥
    # 3.9, but "typing_extensions.Literal" only needs Python ≥ 3.6).
    import typing_extensions

    # Import beartype-specific types to annotate callables with.
    from beartype.cave import NoneType, NoneTypeOr, RegexTypes, ScalarTypes

    # Import official abstract base classes (ABCs), too.
    from numbers import Integral, Real

    # Import user-defined classes, too.
    from my_package.my_module import MyClass

    # ..................{              TYPEVARS               }..................
    # PEP 484 type variable. While @beartype only partially supports type
    # variables at the moment, @beartype 1.0.0.0.0.0.0.0 is expected to fully
    # support type variables.
    T = typing.TypeVar('T')

    # ..................{              FUNCTIONS              }..................
    # Decorate functions with @beartype and...
    @beartype
    def my_function(
        # Annotate builtin types as is.
        param_must_satisfy_builtin_type: str,

        # Annotate user-defined classes as is, too. Note this covariantly
        # matches all instances of both this class and subclasses of this class.
        param_must_satisfy_user_type: MyClass,

        # Annotate PEP 604 type hint unions. Note this requires Python ≥ 3.10.
        param_must_satisfy_pep604_union: dict | tuple | None,

        # Annotate PEP 484 type hint unions. All Python versions support this.
        param_must_satisfy_pep484_union: typing.Union[
            dict, T, tuple[MyClass, ...]],

        # Annotate PEP 593 metatypes, indexed by a type hint followed by zero or
        # more arbitrary objects. See "VALIDATORS" below for real-world usage.
        param_must_satisfy_pep593: typing.Annotated[
            typing.Set[int], range(5), True],

        # Annotate PEP 586 literals, indexed by either a boolean, byte string,
        # integer, string, "enum.Enum" member, or "None".
        param_must_satisfy_pep586: typing.Literal[
            'This parameter must equal this string.'],

        # Annotate PEP 585 builtin container types, indexed by the types of items
        # these containers are expected to contain.
        param_must_satisfy_pep585_builtin: list[str],

        # Annotate PEP 585 standard collection types, indexed too.
        param_must_satisfy_pep585_collection: abc.MutableSequence[str],

        # Annotate PEP 544 protocols, either unindexed or indexed by one or more
        # type variables.
        param_must_satisfy_pep544: typing.SupportsRound[T],

        # Annotate PEP 484 non-standard container types defined by the "typing"
        # module, optionally indexed and only usable as type hints. Note that
        # these types have all been deprecated by PEP 585 under Python ≥ 3.9. See
        # also: https://docs.python.org/3/library/typing.html
        param_must_satisfy_pep484_typing: typing.List[int],

        # Annotate PEP 484 relative forward references dynamically resolved at
        # call time as unqualified classnames relative to the current submodule.
        # Note this class is defined below and that beartype-specific absolute
        # forward references are also supported.
        param_must_satisfy_pep484_relative_forward_ref: 'MyOtherClass',

        # Annotate PEP types indexed by relative forward references. Forward
        # references are supported everywhere standard types are.
        param_must_satisfy_pep484_indexed_relative_forward_ref: (
            typing.Union['MyPep484Generic', set['MyPep585Generic']]),

        # Annotate beartype-specific types predefined by the beartype cave.
        param_must_satisfy_beartype_type_from_cave: NoneType,

        # Annotate beartype-specific unions of types as tuples.
        param_must_satisfy_beartype_union: (dict, MyClass, int),

        # Annotate beartype-specific unions predefined by the beartype cave.
        param_must_satisfy_beartype_union_from_cave: ScalarTypes,

        # Annotate beartype-specific unions concatenated together.
        param_must_satisfy_beartype_union_concatenated: (
            abc.Iterator,) + ScalarTypes,

        # Annotate beartype-specific absolute forward references dynamically
        # resolved at call time as fully-qualified "."-delimited classnames.
        param_must_satisfy_beartype_absolute_forward_ref: (
            'my_package.my_module.MyClass'),

        # Annotate beartype-specific forward references in unions of types, too.
        param_must_satisfy_beartype_union_with_forward_ref: (
            abc.Iterable, 'my_package.my_module.MyOtherClass', NoneType),

        # Annotate PEP 604 optional types. Note this requires Python ≥ 3.10.
        param_must_satisfy_pep604_optional: float | bytes | None = None,

        # Annotate PEP 484 optional types. All Python versions support this.
        param_must_satisfy_pep484_optional: typing.Optional[float, bytes] = None,

        # Annotate beartype-specific optional types.
        param_must_satisfy_beartype_type_optional: NoneTypeOr[float] = None,

        # Annotate beartype-specific optional unions of types.
        param_must_satisfy_beartype_tuple_optional: NoneTypeOr[float, int] = None,

        # Annotate variadic positional arguments as above, too.
        *args: ScalarTypes + (Real, 'my_package.my_module.MyScalarType'),

        # Annotate keyword-only arguments as above, too.
        param_must_be_passed_by_keyword_only: abc.Sequence[
            typing.Union[bool, list[str]]],

    # Annotate return types as above, too.
    ) -> Union[Integral, 'MyPep585Generic', bool]:
        return 0xDEADBEEF

    # Decorate coroutines as above but returning a coroutine type.
    @beartype
    async def my_coroutine() -> abc.Coroutine[None, None, int]:
        from async import sleep
        await sleep(0)
        return 0xDEFECA7E

    # ..................{              GENERATORS             }..................
    # Decorate synchronous generators as above but returning a synchronous
    # generator type.
    @beartype
    def my_sync_generator() -> abc.Generator[int, None, None]:
        yield from range(0xBEEFBABE, 0xCAFEBABE)

    # Decorate asynchronous generators as above but returning an asynchronous
    # generator type.
    @beartype
    async def my_async_generator() -> abc.AsyncGenerator[int, None]:
        from async import sleep
        await sleep(0)
        yield 0x8BADF00D

    # ..................{              CLASSES                }..................
    # Decorate classes with @beartype – which then automatically decorates all
    # methods and properties of those classes with @beartype.
    @beartype
    class MyOtherClass:
        # Annotate instance methods as above without annotating "self".
        def __init__(self, scalar: ScalarTypes) -> None:
            self._scalar = scalar

        # Annotate class methods as above without annotating "cls".
        @classmethod
        def my_classmethod(cls, regex: RegexTypes, wut: str) -> (
            Callable[(), str]):
            import re
            return lambda: re.sub(regex, 'unbearable', str(cls._scalar) + wut)

        # Annotate static methods as above, too.
        @staticmethod
        def my_staticmethod(callable: abc.Callable[[str], T], text: str) -> T:
            return callable(text)

        # Annotate property getter methods as above, too.
        @property
        def my_gettermethod(self) -> abc.Iterator[int]:
            return range(0x0B00B135 + int(self._scalar), 0xB16B00B5)

        # Annotate property setter methods as above, too.
        @my_gettermethod.setter
        def my_settermethod(self, bad: Integral = 0xBAAAAAAD) -> None:
            self._scalar = bad if bad else 0xBADDCAFE

        # Annotate methods accepting or returning instances of the class
        # currently being declared with relative forward references.
        def my_selfreferential_method(self) -> list['MyOtherClass']:
            return [self] * 42

    # ..................{              GENERICS               }..................
    # Decorate PEP 585 generics with @beartype. Note this requires Python ≥ 3.9.
    @beartype
    class MyPep585Generic(tuple[int, float]):
        def __new__(cls, integer: int, real: float) -> tuple[int, float]:
            return tuple.__new__(cls, (integer, real))

    # Decorate PEP 484 generics with @beartype, too.
    @beartype
    class MyPep484Generic(typing.Tuple[str, ...]):
        def __new__(cls, *args: str) -> typing.Tuple[str, ...]:
            return tuple.__new__(cls, args)

    # ..................{              PROTOCOLS              }..................
    # PEP 544 protocol referenced below in type hints. Note this requires Python
    # ≥ 3.8 and that protocols *MUST* be explicitly decorated by the
    # @runtime_checkable decorator to be usable with @beartype.
    @typing.runtime_checkable   # <---- mandatory boilerplate line. it is sad.
    class MyProtocol(typing.Protocol):
        def my_method(self) -> str:
            return (
                'Objects satisfy this protocol only if their classes '
                'define a method with the same signature as this method.'
            )

    # ..................{              DATACLASSES            }..................
    # Import the requisite machinery. Note this requires Python ≥ 3.8.
    from dataclasses import dataclass, InitVar

    # Decorate dataclasses with @beartype, which then automatically decorates all
    # methods and properties of those dataclasses with @beartype – including the
    # __init__() constructors created by @dataclass. Fields are type-checked only
    # at instantiation time. Fields are *NOT* type-checked when reassigned.
    #
    # Decoration order is significant. List @beartype before @dataclass, please.
    @beartype
    @dataclass
    class MyDataclass(object):
        # Annotate fields with type hints.
        field_must_satisfy_builtin_type: InitVar[str]
        field_must_satisfy_pep604_union: str | None = None

        # Annotate methods as above.
        def __post_init__(self, field_must_satisfy_builtin_type: str) -> None:
            if self.field_must_satisfy_pep604_union is None:
                self.field_must_satisfy_pep604_union = (
                    field_must_satisfy_builtin_type)

    # ..................{              NAMED TUPLES           }..................
    # Import the requisite machinery.
    from typing import NamedTuple

    # Decorate named tuples with @beartype.
    @beartype
    class MyNamedTuple(NamedTuple):
        # Annotate fields with type hints.
        field_must_satisfy_builtin_type: str

    # ..................{             CONFIGURATION           }..................
    # Import beartype's configuration API to configure runtime type-checking.
    from beartype import BeartypeConf, BeartypeStrategy

    # Dynamically create your own @beartype decorator, configured for your needs.
    bugbeartype = beartype(conf=BeartypeConf(
        # Optionally disable or enable output of colors (i.e., ANSI escape
        # sequences) in type-checking violations via this tri-state boolean:
        # * "None" conditionally enables colors when standard output is attached
        #   to an interactive terminal. [DEFAULT]
        # * "True" unconditionally enables colors.
        # * "False" unconditionally disables colors.
        is_color=False,  # <-- disable color entirely

        # Optionally enable developer-friendly debugging.
        is_debug=True,

        # Optionally enable PEP 484's implicit numeric tower by:
        # * Expanding all "float" type hints to "float | int".
        # * Expanding all "complex" type hints to "complex | float | int".
        is_pep484_tower=True,

        # Optionally switch to a different type-checking strategy:
        # * "BeartypeStrategy.O1" type-checks in O(1) constant time. [DEFAULT]
        # * "BeartypeStrategy.On" type-checks in O(n) linear time.
        #   (Currently unimplemented but roadmapped for a future release.)
        # * "BeartypeStrategy.Ologn" type-checks in O(log n) logarithmic time.
        #   (Currently unimplemented but roadmapped for a future release.)
        # * "strategy=BeartypeStrategy.O0" disables type-checking entirely.
        strategy=BeartypeStrategy.On,  # <-- enable linear-time type-checking
    ))

    # Decorate with your decorator instead of the vanilla @beartype decorator.
    @bugbeartype
    def muh_configured_func(list_checked_in_On_time: list[float]) -> set[str]:
        return set(str(item) for item in list_checked_in_On_time)

    # ..................{             VALIDATORS              }..................
    # Import beartype's PEP 593 validator API to validate arbitrary constraints.
    # Note this requires either:
    # * Python ≥ 3.9.0.
    # * typing_extensions ≥ 3.9.0.0.
    from beartype.vale import Is, IsAttr, IsEqual
    from typing import Annotated   # <--------------- if Python ≥ 3.9.0
    #from typing_extensions import Annotated   # <--- if Python < 3.9.0

    # Import third-party packages to validate.
    import numpy as np

    # Validator matching only two-dimensional NumPy arrays of 64-bit floats,
    # specified with a single caller-defined lambda function.
    NumpyArray2DFloat = Annotated[np.ndarray, Is[
        lambda arr: arr.ndim == 2 and arr.dtype == np.dtype(np.float64)]]

    # Validator matching only one-dimensional NumPy arrays of 64-bit floats,
    # specified with two declarative expressions. Although verbose, this
    # approach generates optimal reusable code that avoids function calls.
    IsNumpyArray1D = IsAttr['ndim', IsEqual[1]]
    IsNumpyArrayFloat = IsAttr['dtype', IsEqual[np.dtype(np.float64)]]
    NumpyArray1DFloat = Annotated[np.ndarray, IsNumpyArray1D, IsNumpyArrayFloat]

    # Validator matching only empty NumPy arrays, equivalent to but faster than:
    #     NumpyArrayEmpty = Annotated[np.ndarray, Is[lambda arr: arr.size != 0]]
    IsNumpyArrayEmpty = IsAttr['size', IsEqual[0]]
    NumpyArrayEmpty = Annotated[np.ndarray, IsNumpyArrayEmpty]

    # Validator composed with standard operators from the above validators,
    # permissively matching all of the following:
    # * Empty NumPy arrays of any dtype *except* 64-bit floats.
    # * Non-empty one- and two-dimensional NumPy arrays of 64-bit floats.
    NumpyArrayEmptyNonFloatOrNonEmptyFloat1Or2D = Annotated[np.ndarray,
        # "&" creates a new validator matching when both operands match, while
        # "|" creates a new validator matching when one or both operands match;
        # "~" creates a new validator matching when its operand does not match.
        # Group operands to enforce semantic intent and avoid precedence woes.
        (IsNumpyArrayEmpty & ~IsNumpyArrayFloat) | (
            ~IsNumpyArrayEmpty & IsNumpyArrayFloat (
                IsNumpyArray1D | IsAttr['ndim', IsEqual[2]]
            )
        )
    ]

    # Decorate functions accepting validators like usual and...
    @beartype
    def my_validated_function(
        # Annotate validators just like standard type hints.
        param_must_satisfy_validator: NumpyArrayEmptyOrNonemptyFloat1Or2D,
    # Combine validators with standard type hints, too.
    ) -> list[NumpyArrayEmptyNonFloatOrNonEmptyFloat1Or2D]:
        return (
            [param_must_satisfy_validator] * 0xFACEFEED
            if bool(param_must_satisfy_validator) else
            [np.array([i], np.dtype=np.float64) for i in range(0xFEEDFACE)]
        )

    # ..................{             NUMPY                   }..................
    # Import NumPy-specific type hints validating NumPy array constraints. Note:
    # * These hints currently only validate array dtypes. To validate additional
    #   constraints like array shapes, prefer validators instead. See above.
    # * This requires NumPy ≥ 1.21.0 and either:
    #   * Python ≥ 3.9.0.
    #   * typing_extensions ≥ 3.9.0.0.
    from numpy.typing import NDArray

    # NumPy type hint matching all NumPy arrays of 64-bit floats. Internally,
    # beartype reduces this to the equivalent validator:
    #     NumpyArrayFloat = Annotated[
    #         np.ndarray, IsAttr['dtype', IsEqual[np.dtype(np.float64)]]]
    NumpyArrayFloat = NDArray[np.float64]

    # Decorate functions accepting NumPy type hints like usual and...
    @beartype
    def my_numerical_function(
        # Annotate NumPy type hints just like standard type hints.
        param_must_satisfy_numpy: NumpyArrayFloat,
    # Combine NumPy type hints with standard type hints, too.
    ) -> tuple[NumpyArrayFloat, int]:
        return (param_must_satisfy_numpy, len(param_must_satisfy_numpy))

Beartype: it just sorta works.