Unbearably fast O(1) runtime type-checking in pure Python.

Overview

beartype —[ the bare-metal type checker ]—

beartype continuous integration (CI) status beartype Read The Docs (RTD) status

Look for the bare necessities,
  the simple bare necessities.
Forget about your worries and your strife.

                        — The Jungle Book.

Beartype is an open-source pure-Python PEP-compliant constant-time runtime type checker emphasizing efficiency, portability, and thrilling puns.

Beartype brings Rust- and C++-inspired zero-cost abstractions into the lawless world of dynamically-typed Python by enforcing type safety at the granular level of functions and methods against type hints standardized by the Python community in O(1) non-amortized worst-case time with negligible constant factors. If the prior sentence was unreadable jargon, see our friendly and approachable FAQ for a human-readable synopsis.

Beartype is portably implemented in Python 3, continuously stress-tested via GitHub Actions + tox + pytest, and permissively distributed under the MIT license. Beartype has no runtime dependencies, only one test-time dependency, and only one documentation-time dependency. Beartype supports all actively developed Python versions, all Python package managers, and multiple platform-specific package managers.

tl;dr:

  1. Install beartype:

    pip3 install beartype
    
  2. Decorate functions and methods annotated by PEP-compliant type hints with the @beartype.beartype decorator:

    from beartype import beartype
    from collections.abc import Iterable
    from typing import Optional
    
    @beartype
    def print_messages(messages: Optional[Iterable[str]] = ('Hello, world.',)):
        print('\n'.join(messages))

Quality assurance has now been assured.


News

Beartype has a roadmap forward to our first major milestone: beartype 1.0.0, delivering perfect constant-time compliance with all annotation standards by late 2021. ...in theory

Join the strangely enticing conversation and be a part of the spicy runtime type-checker that goes up to eleven.

Install

Let's install beartype with pip, because community standards are good:

pip3 install beartype

Let's install beartype with Anaconda, because corporate standards are (occasionally) good too:

conda config --add channels conda-forge
conda install beartype

macOS

Let's install beartype with Homebrew on macOS courtesy our third-party tap:

brew install beartype/beartype/beartype

Let's install beartype with MacPorts on macOS:

sudo port install py-beartype

A big bear hug to our official macOS package maintainer @harens for packaging beartype for our Apple-appreciating audience.

Linux

Let's install beartype with emerge on Gentoo courtesy a third-party overlay, because source-based Linux distributions are the CPU-bound nuclear option:

emerge --ask app-eselect/eselect-repository
mkdir -p /etc/portage/repos.conf
eselect repository enable raiagent
emerge --sync raiagent
emerge beartype

Overview

Beartype is a novel first line of defense. In Python's vast arsenal of software quality assurance (SQA), beartype holds the shield wall against breaches in type safety by improper parameter and return values violating developer expectations.

Beartype is unopinionated. Beartype inflicts no developer constraints beyond importation and usage of a single configuration-free decorator. Beartype is trivially integrated into new and existing applications, stacks, modules, and scripts already annotating callables with PEP-compliant industry-standard type hints.

Beartype is zero-cost. Beartype inflicts no harmful developer tradeoffs, instead stressing expense-free strategies at both:

Versus Static Type Checkers

Like competing static type checkers operating at the coarse-grained application level via ad-hoc heuristic type inference (e.g., Pyre, mypy, pyright, pytype), beartype effectively imposes no runtime overhead. Unlike static type checkers:

  • Beartype operates exclusively at the fine-grained callable level of pure-Python functions and methods via the standard decorator design pattern. This renders beartype natively compatible with all interpreters and compilers targeting the Python language – including PyPy, Numba, Nuitka, and (wait for it) CPython itself.
  • Beartype enjoys deterministic Turing-complete access to the actual callables, objects, and types being type-checked. This enables beartype to solve dynamic problems decidable only at runtime – including type-checking of arbitrary objects whose:

Versus Runtime Type Checkers

Unlike comparable runtime type checkers (e.g., enforce, pytypes, typeguard), beartype decorates callables with dynamically generated wrappers efficiently type-checking each parameter passed to and value returned from those callables in constant time. Since "performance by default" is our first-class concern, generated wrappers are guaranteed to:

Frequently Asked Questions (FAQ)

What is beartype?

Why, it's the world's first O(1) runtime type checker in any dynamically-typed lang... oh, forget it.

You know typeguard? Then you know beartype – more or less. beartype is typeguard's younger, faster, and slightly sketchier brother who routinely ingests performance-enhancing anabolic nootropics.

What is typeguard?

Okay. Work with us here, people.

You know how in low-level statically-typed memory-unsafe languages that no one should use like C and C++, the compiler validates at compilation time the types of all values passed to and returned from all functions and methods across the entire codebase?

$ gcc -Werror=int-conversion -xc - <<EOL
#include <stdio.h>
int main() {
    printf("Hello, world!");
    return "Goodbye, world.";
}
EOL
<stdin>: In function ‘main’:
<stdin>:4:11: error: returning ‘char *’ from a function with return type
‘int’ makes integer from pointer without a cast [-Werror=int-conversion]
cc1: some warnings being treated as errors

You know how in high-level duck-typed languages that everyone should use instead like Python and Ruby, the interpreter performs no such validation at any interpretation phase but instead permits any arbitrary values to be passed to or returned from any function or method?

$ python3 - <<EOL
def main() -> int:
    print("Hello, world!");
    return "Goodbye, world.";
main()
EOL

Hello, world!

Runtime type checkers like beartype and typeguard selectively shift the dial on type safety in Python from duck to static typing while still preserving all of the permissive benefits of the former as a default behaviour.

$ python3 - <<EOL
from beartype import beartype
@beartype
def main() -> int:
    print("Hello, world!");
    return "Goodbye, world.";
main()
EOL

Hello, world!
Traceback (most recent call last):
  File "<stdin>", line 6, in <module>
  File "<string>", line 17, in __beartyped_main
  File "/home/leycec/py/beartype/beartype/_decor/_code/_pep/_error/peperror.py", line 218, in raise_pep_call_exception
    raise exception_cls(
beartype.roar.BeartypeCallHintPepReturnException: @beartyped main() return
'Goodbye, world.' violates type hint <class 'int'>, as value 'Goodbye,
world.' not int.

When should I use beartype?

Use beartype to assure the quality of Python code beyond what tests alone can assure. If you have yet to test, do that first with a pytest-based test suite, tox configuration, and continuous integration (CI). If you have any time, money, or motivation left, annotate callables with PEP-compliant type hints and decorate those callables with the @beartype.beartype decorator.

Prefer beartype over other runtime type checkers whenever you lack control over the objects passed to or returned from your callables – especially whenever you cannot limit the size of those objects. This includes common developer scenarios like:

  • You are the author of an open-source library intended to be reused by a general audience.
  • You are the author of a public app accepting as input or generating as output sufficiently large data internally passed to or returned from app callables.

Prefer beartype over static type checkers whenever:

Even where none of the above apply, still use beartype. It's free as in beer and speech and cost-free at installation- and runtime. Leverage beartype until you find something that suites you better, because beartype is always better than nothing.

Why should I use beartype?

The idea of beartype is that it never costs you anything. It might not do quite as much as you'd like, but it will always do something – which is more than Python's default behaviour, which is to do nothing and ignore type hints altogether. This means you can always safely add beartype to any Python package, module, app, or script regardless of size, scope, funding, or audience and never worry about your back-end Django server taking a nosedive on St. Patty's Day just because your front-end React client helpfully sent a 5MB JSON file serializing a doubly-nested list of integers.

The idea of typeguard is that it does everything. If you annotate a function decorated by typeguard as accepting a triply-nested list of integers and then pass that function a list containing 1,000 nested lists each containing 1,000 nested lists each containing 1,000 integers, every call to that function will check every integer transitively nested in that list – even if that list never changes. Did we mention that list transitively contains 1,000,000,000 integers in total?

$ python3 -m timeit -n 1 -r 1 -s '
from typeguard import typechecked
@typechecked
def behold(the_great_destroyer_of_apps: list[list[list[int]]]) -> int:
    return len(the_great_destroyer_of_apps)
' 'behold([[[0]*1000]*1000]*1000)'

1 loop, best of 1: 6.42e+03 sec per loop

Yes, 6.42e+03 sec per loop == 6420 seconds == 107 minutes == 1 hour, 47 minutes to check a single list once. Yes, it's an uncommonly large list, but it's still just a list. This is the worst-case cost of a single call to a function decorated by a naïve runtime type checker.

What does beartype do?

Generally, as little as it can while still satisfying the accepted definition of "runtime type checker." Specifically, beartype performs a one-way random walk over the expected data structure of objects passed to and returned from @beartype-decorated functions and methods.

Consider the prior example of a function annotated as accepting a triply-nested list of integers passed a list containing 1,000 nested lists each containing 1,000 nested lists each containing 1,000 integers.

When decorated by typeguard, every call to that function checks every integer nested in that list.

When decorated by beartype, every call to the same function checks only a single random integer contained in a single random nested list contained in a single random nested list contained in that parent list. This is what we mean by the quaint phrase "one-way random walk over the expected data structure."

$ python3 -m timeit -n 1024 -r 4 -s '
from beartype import beartype
@beartype
def behold(the_great_destroyer_of_apps: list[list[list[int]]]) -> int:
   return len(the_great_destroyer_of_apps)
' 'behold([[[0]*1000]*1000]*1000)'

1024 loops, best of 4: 13.8 usec per loop

13.8 usec per loop == 13.8 microseconds = 0.0000138 seconds to transitively check only a random integer nested in a single triply-nested list passed to each call of that function. This is the worst-case cost of a single call to a function decorated by an O(1) runtime type checker.

Usage

Beartype makes type-checking painless, portable, and possibly fun. Just:

Decorate functions and methods annotated by standard type hints with the @beartype.beartype decorator, which wraps those functions and methods in performant type-checking dynamically generated on-the-fly.

Toy Example

Let's see what that looks like for a "Hello, Jungle!" toy example. Just:

  1. Import the @beartype.beartype decorator:

    from beartype import beartype
  2. Decorate any annotated function with that decorator:

    from sys import stderr, stdout
    from typing import TextIO
    
    @beartype
    def hello_jungle(
        sep: str = ' ',
        end: str = '\n',
        file: TextIO = stdout,
        flush: bool = False,
    ):
        '''
        Print "Hello, Jungle!" to a stream, or to sys.stdout by default.
    
        Optional keyword arguments:
        file:  a file-like object (stream); defaults to the current sys.stdout.
        sep:   string inserted between values, default a space.
        end:   string appended after the last value, default a newline.
        flush: whether to forcibly flush the stream.
        '''
    
        print('Hello, Jungle!', sep, end, file, flush)
  3. Call that function with valid parameters and caper as things work:

    >>> hello_jungle(sep='...ROOOAR!!!!', end='uhoh.', file=stderr, flush=True)
    Hello, Jungle! ...ROOOAR!!!! uhoh.
  4. Call that function with invalid parameters and cringe as things blow up with human-readable exceptions exhibiting the single cause of failure:

    >>> hello_jungle(sep=(
    ...     b"What? Haven't you ever seen a byte-string separator before?"))
    BeartypeCallHintPepParamException: @beartyped hello_jungle() parameter
    sep=b"What? Haven't you ever seen a byte-string separator before?"
    violates type hint <class 'str'>, as value b"What? Haven't you ever seen
    a byte-string separator before?" not str.

Industrial Example

Let's wrap the third-party numpy.empty_like() function with automated runtime type checking to demonstrate beartype's support for non-trivial combinations of nested type hints compliant with different PEPs:

from beartype import beartype
from collections.abc import Sequence
from numpy import dtype, empty_like, ndarray
from typing import Optional, Union

@beartype
def empty_like_bear(
    prototype: object,
    dtype: Optional[dtype] = None,
    order: str = 'K',
    subok: bool = True,
    shape: Optional[Union[int, Sequence[int]]] = None,
) -> ndarray:
    return empty_like(prototype, dtype, order, subok, shape)

Note the non-trivial hint for the optional shape parameter, synthesized from a PEP 484-compliant optional of a PEP 484-compliant union of a builtin type and a PEP 585-compliant subscripted abstract base class (ABC), accepting as valid either:

  • The None singleton.
  • An integer.
  • A sequence of integers.

Let's call that wrapper with both valid and invalid parameters:

>>> empty_like_bear(([1,2,3], [4,5,6]), shape=(2, 2))
array([[94447336794963,              0],
       [             7,             -1]])
>>> empty_like_bear(([1,2,3], [4,5,6]), shape=([2], [2]))
BeartypeCallHintPepParamException: @beartyped empty_like_bear() parameter
shape=([2], [2]) violates type hint typing.Union[int,
collections.abc.Sequence, NoneType], as ([2], [2]):
* Not <class "builtins.NoneType"> or int.
* Tuple item 0 value [2] not int.

Note the human-readable message of the raised exception, containing a bulleted list enumerating the various ways this invalid parameter fails to satisfy its type hint, including the types and indices of the first container item failing to satisfy the nested Sequence[int] hint.

See the "Decoration" section for actual code dynamically generated by beartype for real-world use cases resembling those above. Fun!

Would You Like to Know More?

If you know type hints, you know beartype. Since beartype is driven entirely by tool-agnostic community standards, beartype's public API is simply the summation of those standards. As the end user, all you need to know is that decorated callables magically begin raising human-readable exceptions when you pass parameter or return values that violate the PEP-compliant type hints annotating those parameter or return values.

If you don't know type hints, this is your moment to go deep on the hardest hammer in Python's SQA toolbox. Here are a few friendly primers to guide you on your maiden voyage through the misty archipelagos of type hinting:

Onward to the cheats!

Cheatsheet

Let's type-check like greased lightning:

# Import the core @beartype decorator.
from beartype import beartype

# Import PEP 593-compliant type hints. Note this requires Python ≥ 3.9.
from typing import Annotated

# Import PEP 585-compliant type hints. Note this requires Python ≥ 3.9.
from collections.abc import (
    Callable, Generator, Iterable, MutableSequence, Sequence)

# Import PEP 544-compliant type hints. Note this requires Python ≥ 3.8.
from typing import Protocol, runtime_checkable

# Import PEP 484-compliant type hints, too. Note that many of these types
# have been deprecated by PEP 585-compliant type hints under Python ≥ 3.9,
# where @beartype emits non-fatal deprecation warnings at decoration time.
# See also: https://docs.python.org/3/library/typing.html
from typing import Any, List, Optional, Tuple, TypeVar, Union

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

# Import standard abstract base classes (ABCs) for use with @beartype, too.
from numbers import Integral, Real

# Import user-defined classes for use with @beartype, too.
from my_package.my_module import MyClass

# User-defined PEP 544-compliant 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.
@runtime_checkable
class MyProtocol(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.'
        )

# User-defined PEP 484-compliant type variable. Note that @beartype currently
# ignores type variables, but that @beartype 0.9.0 is expected to fully
# support type variables. See also: https://github.com/beartype/beartype/issues/7
T = TypeVar('T')

# 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 593-compliant types, indexed by a type checked by
    # @beartype followed by arbitrary objects ignored by @beartype.
    param_must_satisfy_pep593: Annotated[dict[int, bool], range(5), True],

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

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

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

    # Annotate PEP 484-compliant 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: List[int],

    # Annotate PEP 484-compliant unions of arbitrary types, including
    # builtin types, type variables, and PEP 585-compliant type hints.
    param_must_satisfy_pep484_union: Union[dict, T, tuple[MyClass, ...]],

    # Annotate PEP 484-compliant relative forward references dynamically
    # resolved at call time as unqualified classnames relative to the
    # current user-defined 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-compliant types indexed by similar references. Note that
    # forward references are supported everywhere standard types are.
    param_must_satisfy_pep484_hint_relative_forward_ref: (
        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: (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: (
        Iterable, 'my_package.my_module.MyOtherClass', NoneType),

    # Annotate PEP 484-compliant optional types. Note that parameters
    # annotated by this type typically default to the "None" singleton.
    param_must_satisfy_pep484_optional: Optional[float] = None,

    # Annotate PEP 484-compliant optional unions of types.
    param_must_satisfy_pep484_optional_union: (
        Optional[Union[float, int]]) = 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: VersionTypes + (Real, 'my_package.my_module.MyVersionType'),

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

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

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

# User-defined class referenced in forward references above.
class MyOtherClass:
    # Decorate instance methods as above without annotating "self".
    @beartype
    def __init__(self, scalar: ScalarTypes) -> NoneType:
        self._scalar = scalar

    # Decorate class methods as above without annotating "cls". When
    # chaining decorators, "@beartype" should typically be specified last.
    @classmethod
    @beartype
    def bare_classmethod(cls, regex: RegexTypes, wut: str) -> (
        Callable[(), str]):
        import re
        return lambda: re.sub(regex, 'unbearable', str(cls._scalar) + wut)

    # Decorate static methods as above.
    @staticmethod
    @beartype
    def bare_staticmethod(callable: Callable, *args: str) -> Any:
        return callable(*args)

    # Decorate property getter methods as above.
    @property
    @beartype
    def bare_gettermethod(self) -> Iterator[int]:
        return range(0x0B00B135 + int(self._scalar), 0xB16B00B5)

    # Decorate property setter methods as above.
    @bare_gettermethod.setter
    @beartype
    def bare_settermethod(self, bad: Integral = 0xBAAAAAAD) -> NoneType:
        self._scalar = bad if bad else 0xBADDCAFE

# User-defined PEP 585-compliant generic referenced above in type hints.
# Note this requires Python ≥ 3.9.
class MyPep585Generic(tuple[int, float]):
    # Decorate static class methods as above without annotating "cls".
    @beartype
    def __new__(cls, integer: int, real: float) -> tuple[int, float]:
        return tuple.__new__(cls, (integer, real))

# User-defined PEP 484-compliant generic referenced above in type hints.
class MyPep484Generic(Tuple[str, ...]):
    # Decorate static class methods as above without annotating "cls".
    @beartype
    def __new__(cls, *args: str) -> Tuple[str, ...]:
        return tuple.__new__(cls, args)

Features

Let's chart current and future compliance with Python's typing landscape:

category feature versions partially supporting versions fully supporting
decoratable classes none none
  coroutines none none
  functions 0.1.0current 0.1.0current
  generators 0.1.0current 0.1.0current
  methods 0.1.0current 0.1.0current
parameters optional 0.1.0current 0.1.0current
  keyword-only 0.1.0current 0.1.0current
  positional-only none none
  variadic keyword none none
  variadic positional 0.1.0current 0.1.0current
hints covariant 0.1.0current 0.1.0current
  contravariant none none
  absolute forward references 0.1.0current 0.1.0current
  relative forward references 0.4.0current 0.4.0current
  tuple unions 0.1.0current 0.1.0current
builtins None 0.5.2current 0.5.2current
  dict 0.5.0current none
  frozenset 0.5.0current none
  list 0.5.0current 0.5.0current
  set 0.5.0current none
  tuple 0.5.0current 0.5.0current
  type 0.5.0current none
collections collections.ChainMap 0.5.0current none
  collections.Counter 0.5.0current none
  collections.OrderedDict 0.5.0current none
  collections.defaultdict 0.5.0current none
  collections.deque 0.5.0current none
collections.abc collections.abc.AsyncGenerator 0.5.0current none
  collections.abc.AsyncIterable 0.5.0current none
  collections.abc.AsyncIterator 0.5.0current none
  collections.abc.Awaitable 0.5.0current none
  collections.abc.ByteString 0.5.0current 0.5.0current
  collections.abc.Callable 0.5.0current none
  collections.abc.Collection 0.5.0current none
  collections.abc.Container 0.5.0current none
  collections.abc.Coroutine 0.5.0current none
  collections.abc.Generator 0.5.0current none
  collections.abc.ItemsView 0.5.0current none
  collections.abc.Iterable 0.5.0current none
  collections.abc.Iterator 0.5.0current none
  collections.abc.KeysView 0.5.0current none
  collections.abc.Mapping 0.5.0current none
  collections.abc.MappingView 0.5.0current none
  collections.abc.MutableMapping 0.5.0current none
  collections.abc.MutableSequence 0.5.0current 0.5.0current
  collections.abc.MutableSet 0.5.0current none
  collections.abc.Reversible 0.5.0current none
  collections.abc.Sequence 0.5.0current 0.5.0current
  collections.abc.Set 0.5.0current none
  collections.abc.ValuesView 0.5.0current none
contextlib contextlib.AbstractAsyncContextManager 0.5.0current none
  contextlib.AbstractContextManager 0.5.0current none
re re.Match 0.5.0current none
  re.Pattern 0.5.0current none
typing typing.AbstractSet 0.2.0current none
  typing.Annotated 0.4.0current 0.4.0current
  typing.Any 0.2.0current 0.2.0current
  typing.AnyStr 0.4.0current none
  typing.AsyncContextManager 0.4.0current none
  typing.AsyncGenerator 0.2.0current none
  typing.AsyncIterable 0.2.0current none
  typing.AsyncIterator 0.2.0current none
  typing.Awaitable 0.2.0current none
  typing.BinaryIO 0.4.0current none
  typing.ByteString 0.2.0current 0.2.0current
  typing.Callable 0.2.0current none
  typing.ChainMap 0.2.0current none
  typing.ClassVar none none
  typing.Collection 0.2.0current none
  typing.Container 0.2.0current none
  typing.ContextManager 0.4.0current none
  typing.Coroutine 0.2.0current none
  typing.Counter 0.2.0current none
  typing.DefaultDict 0.2.0current none
  typing.Deque 0.2.0current none
  typing.Dict 0.2.0current none
  typing.Final none none
  typing.ForwardRef 0.4.0current 0.4.0current
  typing.FrozenSet 0.2.0current none
  typing.Generator 0.2.0current none
  typing.Generic 0.4.0current 0.4.0current
  typing.Hashable 0.2.0current none
  typing.IO 0.4.0current none
  typing.ItemsView 0.2.0current none
  typing.Iterable 0.2.0current none
  typing.Iterator 0.2.0current none
  typing.KeysView 0.2.0current none
  typing.List 0.2.0current 0.3.0current
  typing.Literal none none
  typing.Mapping 0.2.0current none
  typing.MappingView 0.2.0current none
  typing.Match 0.4.0current none
  typing.MutableMapping 0.2.0current none
  typing.MutableSequence 0.2.0current 0.3.0current
  typing.MutableSet 0.2.0current none
  typing.NamedTuple 0.1.0current none
  typing.NewType 0.4.0current 0.4.0current
  typing.NoReturn 0.4.0current 0.4.0current
  typing.Optional 0.2.0current 0.2.0current
  typing.OrderedDict 0.2.0current none
  typing.Pattern 0.4.0current none
  typing.Protocol 0.4.0current 0.4.0current
  typing.Reversible 0.2.0current none
  typing.Sequence 0.2.0current 0.3.0current
  typing.Set 0.2.0current none
  typing.Sized 0.2.0current 0.2.0current
  typing.SupportsAbs 0.4.0current 0.4.0current
  typing.SupportsBytes 0.4.0current 0.4.0current
  typing.SupportsComplex 0.4.0current 0.4.0current
  typing.SupportsFloat 0.4.0current 0.4.0current
  typing.SupportsIndex 0.4.0current 0.4.0current
  typing.SupportsInt 0.4.0current 0.4.0current
  typing.SupportsRound 0.4.0current 0.4.0current
  typing.Text 0.1.0current 0.1.0current
  typing.TextIO 0.4.0current none
  typing.Tuple 0.2.0current 0.4.0current
  typing.Type 0.2.0current none
  typing.TypedDict 0.1.0current none
  typing.TypeVar 0.4.0current none
  typing.Union 0.2.0current 0.2.0current
  typing.ValuesView 0.2.0current none
  typing.TYPE_CHECKING 0.5.0current 0.5.0current
  @typing.final none none
  @typing.no_type_check 0.5.0current 0.5.0current
PEP 484 0.2.0current none
  544 0.4.0current 0.4.0current
  560 0.4.0current 0.4.0current
  563 0.1.1current 0.1.1current
  572 0.3.0current 0.4.0current
  585 0.5.0current 0.5.0current
  586 none none
  589 none none
  591 none none
  593 0.4.0current 0.4.0current
packages PyPI 0.1.0current
  Anaconda 0.1.0current
  Gentoo Linux 0.2.0current
  macOS Homebrew 0.5.1current
  macOS MacPorts 0.5.1current
Python 3.5 0.1.00.3.0
  3.6 0.1.0current
  3.7 0.1.0current
  3.8 0.1.0current
  3.9 0.3.2current

Timings

Let's profile beartype against other runtime type-checkers with a battery of surely fair, impartial, and unbiased use cases:

$ bin/profile.bash

beartype profiler [version]: 0.0.2

python    [basename]: python3.9
python    [version]: Python 3.9.0
beartype  [version]: 0.5.2
typeguard [version]: 2.9.1

===================================== str =====================================
profiling regime:
   number of meta-loops:      3
   number of loops:           100
   number of calls each loop: 100
decoration         [none     ]: 100 loops, best of 3: 359 nsec per loop
decoration         [beartype ]: 100 loops, best of 3: 389 usec per loop
decoration         [typeguard]: 100 loops, best of 3: 13.5 usec per loop
decoration + calls [none     ]: 100 loops, best of 3: 14.8 usec per loop
decoration + calls [beartype ]: 100 loops, best of 3: 514 usec per loop
decoration + calls [typeguard]: 100 loops, best of 3: 6.34 msec per loop

=============================== Union[int, str] ===============================
profiling regime:
   number of meta-loops:      3
   number of loops:           100
   number of calls each loop: 100
decoration         [none     ]: 100 loops, best of 3: 1.83 usec per loop
decoration         [beartype ]: 100 loops, best of 3: 433 usec per loop
decoration         [typeguard]: 100 loops, best of 3: 15.6 usec per loop
decoration + calls [none     ]: 100 loops, best of 3: 17.7 usec per loop
decoration + calls [beartype ]: 100 loops, best of 3: 572 usec per loop
decoration + calls [typeguard]: 100 loops, best of 3: 10 msec per loop

=========================== List[int] of 1000 items ===========================
profiling regime:
   number of meta-loops:      1
   number of loops:           1
   number of calls each loop: 7485
decoration         [none     ]: 1 loop, best of 1: 10.1 usec per loop
decoration         [beartype ]: 1 loop, best of 1: 1.3 msec per loop
decoration         [typeguard]: 1 loop, best of 1: 41.1 usec per loop
decoration + calls [none     ]: 1 loop, best of 1: 1.24 msec per loop
decoration + calls [beartype ]: 1 loop, best of 1: 18.3 msec per loop
decoration + calls [typeguard]: 1 loop, best of 1: 104 sec per loop

============ List[Sequence[MutableSequence[int]]] of 10 items each ============
profiling regime:
   number of meta-loops:      1
   number of loops:           1
   number of calls each loop: 7485
decoration         [none     ]: 1 loop, best of 1: 11.8 usec per loop
decoration         [beartype ]: 1 loop, best of 1: 1.77 msec per loop
decoration         [typeguard]: 1 loop, best of 1: 48.9 usec per loop
decoration + calls [none     ]: 1 loop, best of 1: 1.19 msec per loop
decoration + calls [beartype ]: 1 loop, best of 1: 81.2 msec per loop
decoration + calls [typeguard]: 1 loop, best of 1: 17.3 sec per loop

Note

  • sec = seconds.
  • msec = milliseconds = 10-3 seconds.
  • usec = microseconds = 10-6 seconds.
  • nsec = nanoseconds = 10-9 seconds.

ELI5

beartype is:

  • At least twenty times faster (i.e., 20,000%) and consumes three orders of magnitude less time in the worst case than typeguard – the only comparable runtime type-checker also compatible with most modern Python versions.
  • Asymptotically faster in the best case than typeguard, which scales linearly (rather than not at all) with the size of checked containers.
  • Constant across type hints, taking roughly the same time to check parameters and return values hinted by the builtin type str as it does to check those hinted by the unified type Union[int, str] as it does to check those hinted by the container type List[object]. typeguard is variable across type hints, taking significantly longer to check List[object] as as it does to check Union[int, str], which takes roughly twice the time as it does to check str.

beartype performs most of its work at decoration time. The @beartype decorator consumes most of the time needed to first decorate and then repeatedly call a decorated function. beartype is thus front-loaded. After paying the initial cost of decoration, each type-checked call thereafter incurs comparatively little overhead.

Conventional runtime type checkers perform most of their work at call time. The @typeguard.typechecked and similar decorators consume almost none of the time needed to first decorate and then repeatedly call a decorated function. They are thus back-loaded. Although the initial cost of decoration is essentially free, each type-checked call thereafter incurs significant overhead.

How Much Does All This Cost?

Beartype dynamically generates functions wrapping decorated callables with constant-time runtime type-checking. This separation of concerns means that beartype exhibits different cost profiles at decoration and call time. Whereas standard runtime type-checking decorators are fast at decoration time and slow at call time, beartype is the exact opposite.

At call time, wrapper functions generated by the @beartype decorator are guaranteed to unconditionally run in O(1) non-amortized worst-case time with negligible constant factors regardless of type hint complexity or nesting. This is not an amortized average-case analysis. Wrapper functions really are O(1) time in the best, average, and worst cases.

At decoration time, performance is slightly worse. Internally, beartype non-recursively iterates over type hints at decoration time with a micro-optimized breadth-first search (BFS). Since this BFS is memoized, its cost is paid exactly once per type hint per process; subsequent references to the same hint over different parameters and returns of different callables in the same process reuse the results of the previously memoized BFS for that hint. The @beartype decorator itself thus runs in:

  • O(1) amortized average-case time.
  • O(k) non-amortized worst-case time for k the number of child type hints nested in a parent type hint and including that parent.

Since we generally expect a callable to be decorated only once but called multiple times per process, we might expect the cost of decoration to be ignorable in the aggregate. Interestingly, this is not the case. Although only paid once and obviated through memoization, decoration time is sufficiently expensive and call time sufficiently inexpensive that beartype spends most of its wall-clock merely decorating callables. The actual function wrappers dynamically generated by @beartype consume comparatively little wall-clock, even when repeatedly called many times.

That's Some Catch, That Catch-22

Beartype's greatest strength is that it checks types in constant time.

Beartype's greatest weakness is that it checks types in constant time.

Only so many type-checks can be stuffed into a constant slice of time with negligible constant factors. Let's detail exactly what (and why) beartype stuffs into its well-bounded slice of the CPU pie.

Standard runtime type checkers naïvely brute-force the problem by type-checking all child objects transitively reachable from parent objects passed to and returned from callables in O(n) linear time for n such objects. This approach avoids false positives (i.e., raising exceptions for valid objects) and false negatives (i.e., failing to raise exceptions for invalid objects), which is good. But this approach also duplicates work when those objects remain unchanged over multiple calls to those callables, which is bad.

Beartype circumvents that badness by generating code at decoration time performing a one-way random tree walk over the expected nested structure of those objects at call time. For each expected nesting level of each container passed to or returned from each callable decorated by @beartype starting at that container and ending either when a check fails or all checks succeed, that callable performs these checks (in order):

  1. A shallow type-check that the current possibly nested container is an instance of the type given by the current possibly nested type hint.
  2. A deep type-check that an item randomly selected from that container itself satisfies the first check.

For example, given a parameter's type hint list[tuple[Sequence[str]]], beartype generates code at decoration time performing these checks at call time (in order):

  1. A check that the object passed as this parameter is a list.
  2. A check that an item randomly selected from this list is a tuple.
  3. A check that an item randomly selected from this tuple is a sequence.
  4. A check that an item randomly selected from this sequence is a string.

Beartype thus performs one check for each possibly nested type hint for each annotated parameter or return object for each call to each decorated callable. This deep randomness gives us soft statistical expectations as to the number of calls needed to check everything. Specifically, it can be shown that beartype type-checks on average all child objects transitively reachable from parent objects passed to and returned from callables in O(n log n) calls to those callables for n such objects. Praise RNGesus!

Beartype avoids false positives and rarely duplicates work when those objects remain unchanged over multiple calls to those callables, which is good. Sadly, beartype also invites false negatives, because this approach only checks a vertical slice of the full container structure each call, which is bad.

We claim without evidence that false negatives are unlikely under the optimistic assumption that most real-world containers are homogenous (i.e., contain only items of the same type) rather than heterogenous (i.e., contain items of differing types). Examples of homogenous containers include (byte-)strings, ranges, streams, memory views, method resolution orders (MROs), generic alias parameters, lists returned by the dir builtin, iterables generated by the os.walk function, standard NumPy arrays, Pandas DataFrame columns, PyTorch tensors, NetworkX graphs, and really all scientific containers ever.

Nobody Expects the Linearithmic Time

Math time, people. it's happening

Most runtime type-checkers exhibit O(n) time complexity (where n is the total number of items recursively contained in a container to be checked) by recursively and repeatedly checking all items of all containers passed to or returned from all calls of decorated callables.

beartype guarantees O(1) time complexity by non-recursively but repeatedly checking one random item at all nesting levels of all containers passed to or returned from all calls of decorated callables, thus amortizing the cost of deeply checking containers across calls. (See the subsection on @beartype-generated code deeply type-checking arbitrarily nested containers in constant time for what this means in practice.)

beartype exploits the well-known coupon collector's problem applied to abstract trees of nested type hints, enabling us to statistically predict the number of calls required to fully type-check all items of an arbitrary container on average. Formally, let:

  • E(T) be the expected number of calls needed to check all items of a container containing only non-container items (i.e., containing no nested subcontainers) either passed to or returned from a @beartype-decorated callable.
  • γ ≈ 0.5772156649 be the Euler–Mascheroni constant.

Then:

https://render.githubusercontent.com/render/math?math=%5Cdisplaystyle+E%28T%29+%3D+n+%5Clog+n+%2B+%5Cgamma+n+%2B+%5Cfrac%7B1%7D%7B2%7D+%2B+O%5Cleft%28%5Cfrac%7B1%7D%7Bn%7D%5Cright%29

The summation ½ + O(1/n) is strictly less than 1 and thus negligible. While non-negligible, the term γn grows significantly slower than the term nlogn. So this reduces to:

https://render.githubusercontent.com/render/math?math=%5Cdisplaystyle+E%28T%29+%3D+O%28n+%5Clog+n%29

We now generalize this bound to the general case. When checking a container containing no subcontainers, beartype only randomly samples one item from that container on each call. When checking a container containing arbitrarily many nested subcontainers, however, beartype randomly samples one random item from each nesting level of that container on each call.

In general, beartype thus samples h random items from a container on each call, where h is that container's height (i.e., maximum number of edges on the longest path from that container to a non-container leaf item reachable from items directly contained in that container). Since h ≥ 1, beartype samples at least as many items each call as assumed in the usual coupon collector's problem and thus paradoxically takes a fewer number of calls on average to check all items of a container containing arbitrarily many subcontainers as it does to check all items of a container containing no subcontainers.

Ergo, the expected number of calls E(S) needed to check all items of an arbitrary container exhibits the same or better growth rate and remains bound above by at least the same upper bounds – but probably tighter: e.g.,

https://render.githubusercontent.com/render/math?math=%5Cdisplaystyle+E%28S%29+%3D+O%28E%28T%29%29+%3D+O%28n+%5Clog+n%29%0A

Fully checking a container takes no more calls than that container's size times the logarithm of that size on average. For example, fully checking a list of 50 integers is expected to take 225 calls on average.

Compliance

beartype is fully compliant with these Python Enhancement Proposals (PEPs):

beartype is currently not compliant whatsoever with these PEPs:

See also the PEP and typing categories of our features matrix for further details.

Full Compliance

beartype deeply type-checks (i.e., directly checks the types of and recursively checks the types of items contained in) parameters and return values annotated with these typing types:

beartype also fully supports callables decorated by these typing decorators:

Lastly, beartype fully supports these typing constants:

Partial Compliance

beartype currently only shallowly type-checks (i.e., only directly checks the types of) parameters and return values annotated with these typing types:

Subsequent beartype versions will deeply type-check these typing types while preserving our O(1) time complexity (with negligible constant factors) guarantee.

No Compliance

beartype currently silently ignores these typing types at decoration time:

beartype currently raises exceptions at decoration time when passed these typing types:

Subsequent beartype versions will first shallowly and then deeply type-check these typing types while preserving our O(1) time complexity (with negligible constant factors) guarantee.

Tutorial

Let's begin with the simplest type of type-checking supported by @beartype.

Builtin Types

Builtin types like dict, int, list, set, and str are trivially type-checked by annotating parameters and return values with those types as is.

Let's declare a simple beartyped function accepting a string and a dictionary and returning a tuple:

from beartype import beartype

@beartype
def law_of_the_jungle(wolf: str, pack: dict) -> tuple:
    return (wolf, pack[wolf]) if wolf in pack else None

Let's call that function with good types:

>>> law_of_the_jungle(wolf='Akela', pack={'Akela': 'alone', 'Raksha': 'protection'})
('Akela', 'alone')

Good function. Let's call it again with bad types:

>>> law_of_the_jungle(wolf='Akela', pack=['Akela', 'Raksha'])
Traceback (most recent call last):
  File "<ipython-input-10-7763b15e5591>", line 1, in <module>
    law_of_the_jungle(wolf='Akela', pack=['Akela', 'Raksha'])
  File "<string>", line 22, in __law_of_the_jungle_beartyped__
beartype.roar.BeartypeCallTypeParamException: @beartyped law_of_the_jungle() parameter pack=['Akela', 'Raksha'] not a <class 'dict'>.

The beartype.roar submodule publishes exceptions raised at both decoration time by @beartype and at runtime by wrappers generated by @beartype. In this case, a runtime type exception describing the improperly typed pack parameter is raised.

Good function! Let's call it again with good types exposing a critical issue in this function's implementation and/or return type annotation:

>>> law_of_the_jungle(wolf='Leela', pack={'Akela': 'alone', 'Raksha': 'protection'})
Traceback (most recent call last):
  File "<ipython-input-10-7763b15e5591>", line 1, in <module>
    law_of_the_jungle(wolf='Leela', pack={'Akela': 'alone', 'Raksha': 'protection'})
  File "<string>", line 28, in __law_of_the_jungle_beartyped__
beartype.roar.BeartypeCallTypeReturnException: @beartyped law_of_the_jungle() return value None not a <class 'tuple'>.

Bad function. Let's conveniently resolve this by permitting this function to return either a tuple or None as detailed below:

>>> from beartype.cave import NoneType
>>> @beartype
... def law_of_the_jungle(wolf: str, pack: dict) -> (tuple, NoneType):
...     return (wolf, pack[wolf]) if wolf in pack else None
>>> law_of_the_jungle(wolf='Leela', pack={'Akela': 'alone', 'Raksha': 'protection'})
None

The beartype.cave submodule publishes generic types suitable for use with the @beartype decorator and anywhere else you might need them. In this case, the type of the None singleton is imported from this submodule and listed in addition to tuple as an allowed return type from this function.

Note that usage of the beartype.cave submodule is entirely optional (but more efficient and convenient than most alternatives). In this case, the type of the None singleton can also be accessed directly as type(None) and listed in place of NoneType above: e.g.,

>>> @beartype
... def law_of_the_jungle(wolf: str, pack: dict) -> (tuple, type(None)):
...     return (wolf, pack[wolf]) if wolf in pack else None
>>> law_of_the_jungle(wolf='Leela', pack={'Akela': 'alone', 'Raksha': 'protection'})
None

Of course, the beartype.cave submodule also publishes types not accessible directly like RegexCompiledType (i.e., the type of all compiled regular expressions). All else being equal, beartype.cave is preferable.

Good function! The type hints applied to this function now accurately document this function's API. All's well that ends typed well. Suck it, Shere Khan.

Arbitrary Types

Everything above also extends to:

  • Arbitrary types like user-defined classes and stock classes in the Python stdlib (e.g., argparse.ArgumentParser) – all of which are also trivially type-checked by annotating parameters and return values with those types.
  • Arbitrary callables like instance methods, class methods, static methods, and generator functions and methods – all of which are also trivially type-checked with the @beartype decorator.

Let's declare a motley crew of beartyped callables doing various silly things in a strictly typed manner, just 'cause:

from beartype import beartype
from beartype.cave import GeneratorType, IterableType, NoneType

class MaximsOfBaloo(object):
    @beartype
    def __init__(self, sayings: IterableType):
        self.sayings = sayings

@beartype
def inform_baloo(maxims: MaximsOfBaloo) -> GeneratorType:
    for saying in maxims.sayings:
        yield saying

For genericity, the MaximsOfBaloo class initializer accepts any generic iterable (via the beartype.cave.IterableType tuple listing all valid iterable types) rather than an overly specific list or tuple type. Your users may thank you later.

For specificity, the inform_baloo generator function has been explicitly annotated to return a beartype.cave.GeneratorType (i.e., the type returned by functions and methods containing at least one yield statement). Type safety brings good fortune for the New Year.

Let's iterate over that generator with good types:

>>> maxims = MaximsOfBaloo(sayings={
...     '''If ye find that the Bullock can toss you,
...           or the heavy-browed Sambhur can gore;
...      Ye need not stop work to inform us:
...           we knew it ten seasons before.''',
...     '''“There is none like to me!” says the Cub
...           in the pride of his earliest kill;
...      But the jungle is large and the Cub he is small.
...           Let him think and be still.''',
... })
>>> for maxim in inform_baloo(maxims): print(maxim.splitlines()[-1])
       Let him think and be still.
       we knew it ten seasons before.

Good generator. Let's call it again with bad types:

>>> for maxim in inform_baloo([
...     'Oppress not the cubs of the stranger,',
...     '     but hail them as Sister and Brother,',
... ]): print(maxim.splitlines()[-1])
Traceback (most recent call last):
  File "<ipython-input-10-7763b15e5591>", line 30, in <module>
    '     but hail them as Sister and Brother,',
  File "<string>", line 12, in __inform_baloo_beartyped__
beartype.roar.BeartypeCallTypeParamException: @beartyped inform_baloo() parameter maxims=['Oppress not the cubs of the stranger,', '     but hail them as Sister and ...'] not a <class '__main__.MaximsOfBaloo'>.

Good generator! The type hints applied to these callables now accurately document their respective APIs. Thanks to the pernicious magic of beartype, all ends typed well... yet again.

Unions of Types

That's all typed well, but everything above only applies to parameters and return values constrained to singular types. In practice, parameters and return values are often relaxed to any of multiple types referred to as unions of types. You can thank set theory for the jargon... unless you hate set theory. Then it's just our fault.

Unions of types are trivially type-checked by annotating parameters and return values with the typing.Union type hint containing those types. Let's declare another beartyped function accepting either a mapping or a string and returning either another function or an integer:

from beartype import beartype
from collections.abc import Callable, Mapping
from numbers import Integral
from typing import Any, Union

@beartype
def toomai_of_the_elephants(memory: Union[Integral, Mapping[Any, Any]]) -> (
    Union[Integral, Callable[(Any,), Any]]):
    return memory if isinstance(memory, Integral) else lambda key: memory[key]

For genericity, the toomai_of_the_elephants function both accepts and returns any generic integer (via the standard numbers.Integral abstract base class (ABC) matching both builtin integers and third-party integers from frameworks like NumPy and SymPy) rather than an overly specific int type. The API you relax may very well be your own.

Let's call that function with good types:

>>> memory_of_kala_nag = {
...     'remember': 'I will remember what I was, I am sick of rope and chain—',
...     'strength': 'I will remember my old strength and all my forest affairs.',
...     'not sell': 'I will not sell my back to man for a bundle of sugar-cane:',
...     'own kind': 'I will go out to my own kind, and the wood-folk in their lairs.',
...     'morning':  'I will go out until the day, until the morning break—',
...     'caress':   'Out to the wind’s untainted kiss, the water’s clean caress;',
...     'forget':   'I will forget my ankle-ring and snap my picket stake.',
...     'revisit':  'I will revisit my lost loves, and playmates masterless!',
... }
>>> toomai_of_the_elephants(len(memory_of_kala_nag['remember']))
56
>>> toomai_of_the_elephants(memory_of_kala_nag)('remember')
'I will remember what I was, I am sick of rope and chain—'

Good function. Let's call it again with a tastelessly bad type:

>>> toomai_of_the_elephants(
...     'Shiv, who poured the harvest and made the winds to blow,')
BeartypeCallHintPepParamException: @beartyped toomai_of_the_elephants()
parameter memory='Shiv, who poured the harvest and made the winds to blow,'
violates type hint typing.Union[numbers.Integral, collections.abc.Mapping],
as 'Shiv, who poured the harvest and made the winds to blow,' not <protocol
ABC "collections.abc.Mapping"> or <protocol "numbers.Integral">.

Good function! The type hints applied to this callable now accurately documents its API. All ends typed well... still again and again.

Optional Types

That's also all typed well, but everything above only applies to mandatory parameters and return values whose types are never NoneType. In practice, parameters and return values are often relaxed to optionally accept any of multiple types including NoneType referred to as optional types.

Optional types are trivially type-checked by annotating optional parameters (parameters whose values default to None) and optional return values (callables returning None rather than raising exceptions in edge cases) with the typing.Optional type hint indexed by those types.

Let's declare another beartyped function accepting either an enumeration type or None and returning either an enumeration member or None:

from beartype import beartype
from beartype.cave import EnumType, EnumMemberType
from typing import Optional

@beartype
def tell_the_deep_sea_viceroys(story: Optional[EnumType] = None) -> (
    Optional[EnumMemberType]):
    return story if story is None else list(story.__members__.values())[-1]

For efficiency, the typing.Optional type hint creates, caches, and returns new tuples of types appending NoneType to the original types it's indexed with. Since efficiency is good, typing.Optional is also good.

Let's call that function with good types:

>>> from enum import Enum
>>> class Lukannon(Enum):
...     WINTER_WHEAT = 'The Beaches of Lukannon—the winter wheat so tall—'
...     SEA_FOG      = 'The dripping, crinkled lichens, and the sea-fog drenching all!'
...     PLAYGROUND   = 'The platforms of our playground, all shining smooth and worn!'
...     HOME         = 'The Beaches of Lukannon—the home where we were born!'
...     MATES        = 'I met my mates in the morning, a broken, scattered band.'
...     CLUB         = 'Men shoot us in the water and club us on the land;'
...     DRIVE        = 'Men drive us to the Salt House like silly sheep and tame,'
...     SEALERS      = 'And still we sing Lukannon—before the sealers came.'
>>> tell_the_deep_sea_viceroys(Lukannon)
<Lukannon.SEALERS: 'And still we sing Lukannon—before the sealers came.'>
>>> tell_the_deep_sea_viceroys()
None

You may now be pondering to yourself grimly in the dark: "...but could we not already do this just by manually annotating optional types with typing.Union type hints explicitly indexed by NoneType?"

You would, of course, be correct. Let's grimly redeclare the same function accepting and returning the same types – only annotated with NoneType rather than typing.Optional:

from beartype import beartype
from beartype.cave import EnumType, EnumMemberType, NoneType
from typing import Union

@beartype
def tell_the_deep_sea_viceroys(story: Union[EnumType, NoneType] = None) -> (
    Union[EnumMemberType, NoneType]):
    return list(story.__members__.values())[-1] if story is not None else None

Since typing.Optional internally reduces to typing.Union, these two approaches are semantically equivalent. The former is simply syntactic sugar simplifying the latter.

Whereas typing.Union accepts an arbitrary number of child type hints, however, typing.Optional accepts only a single child type hint. This can be circumvented by either indexing typing.Optional by typing.Union or indexing typing.Union by NoneType. Let's exhibit the former approach by declaring another beartyped function accepting either an enumeration type, enumeration type member, or None and returning either an enumeration type, enumeration type member, or None:

from beartype import beartype
from beartype.cave import EnumType, EnumMemberType, NoneType
from typing import Optional, Union

@beartype
def sang_them_up_the_beach(
    woe: Optional[Union[EnumType, EnumMemberType]] = None) -> (
    Optional[Union[EnumType, EnumMemberType]]):
    return woe if isinstance(woe, (EnumMemberType, NoneType)) else (
        list(woe.__members__.values())[-1])

Let's call that function with good types:

>>> sang_them_up_the_beach(Lukannon)
<Lukannon.SEALERS: 'And still we sing Lukannon—before the sealers came.'>
>>> sang_them_up_the_beach()
None

Behold! The terrifying power of the typing.Optional type hint, resplendent in its highly over-optimized cache utilization.

Decoration

Let's take a deep dive into the deep end of runtime type checking – the beartype way. In this subsection, we show code generated by the @beartype decorator in real-world use cases and tell why that code is the fastest possible code type-checking those cases.

Identity Decoration

We begin by wading into the torpid waters of the many ways beartype avoids doing any work whatsoever, because laziness is the virtue we live by. The reader may recall that the fastest decorator at decoration- and call-time is the identity decorator returning its decorated callable unmodified: e.g.,

from collections.abc import Callable

def identity_decorator(func: Callable): -> Callable:
    return func

beartype silently reduces to the identity decorator whenever it can, which is surprisingly often. Our three weapons are laziness, surprise, ruthless efficiency, and an almost fanatical devotion to constant-time type checking.

Unconditional Identity Decoration

Let's define a trivial function annotated by no type hints:

def law_of_the_jungle(strike_first_and_then_give_tongue):
    return strike_first_and_then_give_tongue

Let's decorate that function by @beartype and verify that @beartype reduced to the identity decorator by returning that function unmodified:

>>> from beartype import beartype
>>> beartype(law_of_the_jungle) is law_of_the_jungle
True

We've verified that @beartype reduces to the identity decorator when decorating unannotated callables. That's but the tip of the iceberg, though. @beartype unconditionally reduces to a noop when:

Shallow Identity Decoration

Let's define a trivial function annotated by the PEP 484-compliant typing.Any type hint:

from typing import Any

def law_of_the_jungle_2(never_order_anything_without_a_reason: Any) -> Any:
    return never_order_anything_without_a_reason

Again, let's decorate that function by @beartype and verify that @beartype reduced to the identity decorator by returning that function unmodified:

>>> from beartype import beartype
>>> beartype(law_of_the_jungle_2) is law_of_the_jungle_2
True

We've verified that @beartype reduces to the identity decorator when decorating callables annotated by typing.Any – a novel category of type hint we refer to as shallowly ignorable type hints (known to be ignorable by constant-time lookup in a predefined frozen set). That's but the snout of the crocodile, though. @beartype conditionally reduces to a noop when all type hints annotating the decorated callable are shallowly ignorable. These include:

  • object, the root superclass of Python's class hierarchy. Since all objects are instances of object, object conveys no meaningful constraints as a type hint and is thus shallowly ignorable.
  • typing.Any, equivalent to object.
  • typing.Generic, equivalent to typing.Generic[typing.Any], which conveys no meaningful constraints as a type hint and is thus shallowly ignorable.
  • typing.Protocol, equivalent to typing.Protocol[typing.Any] and shallowly ignorable for similar reasons.
  • typing.Union, equivalent to typing.Union[typing.Any], equivalent to Any.
  • typing.Optional, equivalent to typing.Optional[typing.Any], equivalent to Union[Any, type(None)]. Since any union subscripted by ignorable type hints is itself ignorable, [1] typing.Optional is shallowly ignorable as well.
[1] Unions are only as narrow as their widest subscripted argument. However, ignorable type hints are ignorable because they are maximally wide. Unions subscripted by ignorable arguments are thus the widest possible unions, conveying no meaningful constraints and thus themselves ignorable.

Deep Identity Decoration

Let's define a trivial function annotated by a non-trivial PEP 484-, 585- and 593-compliant type hint that superficially appears to convey meaningful constraints:

from typing import Annotated, NewType, Union

hint = Union[str, list[int], NewType('MetaType', Annotated[object, 53])]
def law_of_the_jungle_3(bring_them_to_the_pack_council: hint) -> hint:
    return bring_them_to_the_pack_council

Despite appearances, it can be shown by exhaustive (and frankly exhausting) reduction that that hint is actually ignorable. Let's decorate that function by @beartype and verify that @beartype reduced to the identity decorator by returning that function unmodified:

>>> from beartype import beartype
>>> beartype(law_of_the_jungle_3) is law_of_the_jungle_3
True

We've verified that @beartype reduces to the identity decorator when decorating callables annotated by the above object – a novel category of type hint we refer to as deeply ignorable type hints (known to be ignorable only by recursive linear-time inspection of subscripted arguments). That's but the trunk of the elephant, though. @beartype conditionally reduces to a noop when all type hints annotating the decorated callable are deeply ignorable. These include:

Constant Decoration

We continue by trundling into the turbid waters out at sea, where beartype reluctantly performs its minimal amount of work with a heavy sigh.

Constant Builtin Type Decoration

Let's define a trivial function annotated by type hints that are builtin types:

from beartype import beartype

@beartype
def law_of_the_jungle_4(he_must_be_spoken_for_by_at_least_two: int):
    return he_must_be_spoken_for_by_at_least_two

Let's see the wrapper function @beartype dynamically generated from that:

def __beartyped_law_of_the_jungle_4(
    *args,
    __beartype_func=__beartype_func,
    __beartypistry=__beartypistry,
    **kwargs
):
    # Localize the number of passed positional arguments for efficiency.
    __beartype_args_len = len(args)
    # Localize this positional or keyword parameter if passed *OR* to the
    # sentinel value "__beartypistry" guaranteed to never be passed otherwise.
    __beartype_pith_0 = (
        args[0] if __beartype_args_len > 0 else
        kwargs.get('he_must_be_spoken_for_by_at_least_two', __beartypistry)
    )

    # If this parameter was passed...
    if __beartype_pith_0 is not __beartypistry:
        # Type-check this passed parameter or return value against this
        # PEP-compliant type hint.
        if not isinstance(__beartype_pith_0, int):
            __beartype_raise_pep_call_exception(
                func=__beartype_func,
                pith_name='he_must_be_spoken_for_by_at_least_two',
                pith_value=__beartype_pith_0,
            )

    # Call this function with all passed parameters and return the value
    # returned from this call.
    return __beartype_func(*args, **kwargs)

Let's dismantle this bit by bit:

  • The code comments above are verbatim as they appear in the generated code.
  • __beartyped_law_of_the_jungle_4() is the ad-hoc function name @beartype assigned this wrapper function.
  • __beartype_func is the original law_of_the_jungle_4() function.
  • __beartypistry is a thread-safe global registry of all types, tuples of types, and forward references to currently undeclared types visitable from type hints annotating callables decorated by @beartype. We'll see more about the __beartypistry in a moment. For know, just know that __beartypistry is a private singleton of the beartype package. This object is frequently accessed and thus localized to the body of this wrapper rather than accessed as a global variable, which would be mildly slower.
  • __beartype_pith_0 is the value of the first passed parameter, regardless of whether that parameter is passed as a positional or keyword argument. If unpassed, the value defaults to the __beartypistry. Since no caller should access (let alone pass) that object, that object serves as an efficient sentinel value enabling us to discern passed from unpassed parameters. beartype internally favours the term "pith" (which we absolutely just made up) to transparently refer to the arbitrary object currently being type-checked against its associated type hint.
  • isinstance(__beartype_pith_0, int) tests whether the value passed for this parameter satisfies the type hint annotating this parameter.
  • __beartype_raise_pep_call_exception() raises a human-readable exception if this value fails this type-check.

So good so far. But that's easy. Let's delve deeper.

Constant Non-Builtin Type Decoration

Let's define a trivial function annotated by type hints that are pure-Python classes rather than builtin types:

from argparse import ArgumentParser
from beartype import beartype

@beartype
def law_of_the_jungle_5(a_cub_may_be_bought_at_a_price: ArgumentParser):
    return a_cub_may_be_bought_at_a_price

Let's see the wrapper function @beartype dynamically generated from that:

def __beartyped_law_of_the_jungle_5(
    *args,
    __beartype_func=__beartype_func,
    __beartypistry=__beartypistry,
    **kwargs
):
    # Localize the number of passed positional arguments for efficiency.
    __beartype_args_len = len(args)
    # Localize this positional or keyword parameter if passed *OR* to the
    # sentinel value "__beartypistry" guaranteed to never be passed otherwise.
    __beartype_pith_0 = (
        args[0] if __beartype_args_len > 0 else
        kwargs.get('a_cub_may_be_bought_at_a_price', __beartypistry)
    )

    # If this parameter was passed...
    if __beartype_pith_0 is not __beartypistry:
        # Type-check this passed parameter or return value against this
        # PEP-compliant type hint.
        if not isinstance(__beartype_pith_0, __beartypistry['argparse.ArgumentParser']):
            __beartype_raise_pep_call_exception(
                func=__beartype_func,
                pith_name='a_cub_may_be_bought_at_a_price',
                pith_value=__beartype_pith_0,
            )

    # Call this function with all passed parameters and return the value
    # returned from this call.
    return __beartype_func(*args, **kwargs)

The result is largely the same. The only meaningful difference is the type-check on line 20:

if not isinstance(__beartype_pith_0, __beartypistry['argparse.ArgumentParser']):

Since we annotated that function with a pure-Python class rather than builtin type, @beartype registered that class with the __beartypistry at decoration time and then subsequently looked that class up with its fully-qualified classname at call time to perform this type-check.

So good so far... so what! Let's spelunk harder.

Constant Shallow Sequence Decoration

Let's define a trivial function annotated by type hints that are PEP 585-compliant builtin types subscripted by ignorable arguments:

from beartype import beartype

@beartype
def law_of_the_jungle_6(all_the_jungle_is_thine: list[object]):
    return all_the_jungle_is_thine

Let's see the wrapper function @beartype dynamically generated from that:

def __beartyped_law_of_the_jungle_6(
    *args,
    __beartype_func=__beartype_func,
    __beartypistry=__beartypistry,
    **kwargs
):
    # Localize the number of passed positional arguments for efficiency.
    __beartype_args_len = len(args)
    # Localize this positional or keyword parameter if passed *OR* to the
    # sentinel value "__beartypistry" guaranteed to never be passed otherwise.
    __beartype_pith_0 = (
        args[0] if __beartype_args_len > 0 else
        kwargs.get('all_the_jungle_is_thine', __beartypistry)
    )

    # If this parameter was passed...
    if __beartype_pith_0 is not __beartypistry:
        # Type-check this passed parameter or return value against this
        # PEP-compliant type hint.
        if not isinstance(__beartype_pith_0, list):
            __beartype_raise_pep_call_exception(
                func=__beartype_func,
                pith_name='all_the_jungle_is_thine',
                pith_value=__beartype_pith_0,
            )

    # Call this function with all passed parameters and return the value
    # returned from this call.
    return __beartype_func(*args, **kwargs)

We are still within the realm of normalcy. Correctly detecting this type hint to be subscripted by an ignorable argument, @beartype only bothered type-checking this parameter to be an instance of this builtin type:

if not isinstance(__beartype_pith_0, list):

It's time to iteratively up the ante.

Constant Deep Sequence Decoration

Let's define a trivial function annotated by type hints that are PEP 585-compliant builtin types subscripted by builtin types:

from beartype import beartype

@beartype
def law_of_the_jungle_7(kill_everything_that_thou_canst: list[str]):
    return kill_everything_that_thou_canst

Let's see the wrapper function @beartype dynamically generated from that:

def __beartyped_law_of_the_jungle_7(
    *args,
    __beartype_func=__beartype_func,
    __beartypistry=__beartypistry,
    **kwargs
):
    # Generate and localize a sufficiently large pseudo-random integer for
    # subsequent indexation in type-checking randomly selected container items.
    __beartype_random_int = __beartype_getrandbits(64)
    # Localize the number of passed positional arguments for efficiency.
    __beartype_args_len = len(args)
    # Localize this positional or keyword parameter if passed *OR* to the
    # sentinel value "__beartypistry" guaranteed to never be passed otherwise.
    __beartype_pith_0 = (
        args[0] if __beartype_args_len > 0 else
        kwargs.get('kill_everything_that_thou_canst', __beartypistry)
    )

    # If this parameter was passed...
    if __beartype_pith_0 is not __beartypistry:
        # Type-check this passed parameter or return value against this
        # PEP-compliant type hint.
        if not (
            # True only if this pith shallowly satisfies this hint.
            isinstance(__beartype_pith_0, list) and
            # True only if either this pith is empty *OR* this pith is
            # both non-empty and deeply satisfies this hint.
            (not __beartype_pith_0 or isinstance(__beartype_pith_0[__beartype_random_int % len(__beartype_pith_0)], str))
        ):
            __beartype_raise_pep_call_exception(
                func=__beartype_func,
                pith_name='kill_everything_that_thou_canst',
                pith_value=__beartype_pith_0,
            )

    # Call this function with all passed parameters and return the value
    # returned from this call.
    return __beartype_func(*args, **kwargs)

We have now diverged from normalcy. Let's dismantle this iota by iota:

  • __beartype_random_int is a pseudo-random unsigned 32-bit integer whose bit length intentionally corresponds to the number of bits generated by each call to Python's C-based Mersenne Twister internally performed by the random.getrandbits function generating this integer. Exceeding this length would cause that function to internally perform that call multiple times for no gain. Since the cost of generating integers to this length is the same as generating integers of smaller lengths, this length is preferred. Since most sequences are likely to contain fewer items than this integer, pseudo-random sequence items are indexable by taking the modulo of this integer with the sizes of those sequences. For big sequences containing more than this number of items, beartype deeply type-checks leading items with indices in this range while ignoring trailing items. Given the practical infeasibility of storing big sequences in memory, this seems an acceptable real-world tradeoff. Suck it, big sequences!
  • As before, @beartype first type-checks this parameter to be a list.
  • @beartype then type-checks this parameter to either be:
    • not __beartype_pith_0, an empty list.
    • isinstance(__beartype_pith_0[__beartype_random_int % len(__beartype_pith_0)], str), a non-empty list whose pseudo-randomly indexed list item satisfies this nested builtin type.

Well, that escalated quickly.

Constant Nested Deep Sequence Decoration

Let's define a trivial function annotated by type hints that are PEP 585-compliant builtin types recursively subscripted by instances of themselves, because we are typing masochists:

from beartype import beartype

@beartype
def law_of_the_jungle_8(pull_thorns_from_all_wolves_paws: (
    list[list[list[str]]])):
    return pull_thorns_from_all_wolves_paws

Let's see the wrapper function @beartype dynamically generated from that:

def __beartyped_law_of_the_jungle_8(
    *args,
    __beartype_func=__beartype_func,
    __beartypistry=__beartypistry,
    **kwargs
):
    # Generate and localize a sufficiently large pseudo-random integer for
    # subsequent indexation in type-checking randomly selected container items.
    __beartype_random_int = __beartype_getrandbits(32)
    # Localize the number of passed positional arguments for efficiency.
    __beartype_args_len = len(args)
    # Localize this positional or keyword parameter if passed *OR* to the
    # sentinel value "__beartypistry" guaranteed to never be passed otherwise.
    __beartype_pith_0 = (
        args[0] if __beartype_args_len > 0 else
        kwargs.get('pull_thorns_from_all_wolves_paws', __beartypistry)
    )

    # If this parameter was passed...
    if __beartype_pith_0 is not __beartypistry:
        # Type-check this passed parameter or return value against this
        # PEP-compliant type hint.
        if not (
            # True only if this pith shallowly satisfies this hint.
            isinstance(__beartype_pith_0, list) and
            # True only if either this pith is empty *OR* this pith is
            # both non-empty and deeply satisfies this hint.
            (not __beartype_pith_0 or (
                # True only if this pith shallowly satisfies this hint.
                isinstance(__beartype_pith_1 := __beartype_pith_0[__beartype_random_int % len(__beartype_pith_0)], list) and
                # True only if either this pith is empty *OR* this pith is
                # both non-empty and deeply satisfies this hint.
                (not __beartype_pith_1 or (
                    # True only if this pith shallowly satisfies this hint.
                    isinstance(__beartype_pith_2 := __beartype_pith_1[__beartype_random_int % len(__beartype_pith_1)], list) and
                    # True only if either this pith is empty *OR* this pith is
                    # both non-empty and deeply satisfies this hint.
                    (not __beartype_pith_2 or isinstance(__beartype_pith_2[__beartype_random_int % len(__beartype_pith_2)], str))
                ))
            ))
        ):
            __beartype_raise_pep_call_exception(
                func=__beartype_func,
                pith_name='pull_thorns_from_all_wolves_paws',
                pith_value=__beartype_pith_0,
            )

    # Call this function with all passed parameters and return the value
    # returned from this call.
    return __beartype_func(*args, **kwargs)

We are now well beyond the deep end, where the benthic zone and the cruel denizens of the fathomless void begins. Let's dismantle this pascal by pascal:

  • __beartype_pith_1 := __beartype_pith_0[__beartype_random_int % len(__beartype_pith_0)], a PEP 572-style assignment expression localizing repeatedly accessed random items of the first nested list for efficiency.
  • __beartype_pith_2 := __beartype_pith_1[__beartype_random_int % len(__beartype_pith_1)], a similar expression localizing repeatedly accessed random items of the second nested list.
  • The same __beartype_random_int pseudo-randomly indexes all three lists.
  • Under older Python interpreters lacking PEP 572 support, @beartype generates equally valid (albeit less efficient) code repeating each nested list item access.

In the kingdom of the linear-time runtime type checkers, the constant-time runtime type checker really stands out like a sore giant squid, doesn't it?

See the Developers section for further commentary on runtime optimization from the higher-level perspective of architecture and internal API design.

Developers

Let's contribute pull requests to beartype for the good of typing. The primary maintainer of this repository is a friendly beardless Canadian guy who guarantees that he will always be nice and congenial and promptly merge all requests that pass continuous integration (CI) tests.

And thanks for merely reading this! Like all open-source software, beartype thrives on community contributions, activity, and interest. This means you, stalwart Python hero.

beartype has two problem spots (listed below in order of decreasing importance and increasing complexity) that could always benefit from a volunteer army of good GitHub Samaritans.

Workflow

Let's take this from the top.

  1. Create a GitHub user account.

  2. Login to GitHub with that account.

  3. Click the "Fork" button in the upper right-hand corner of the "beartype/beartype" repository page.

  4. Click the "Code" button in the upper right-hand corner of your fork page that appears.

  5. Copy the URL that appears.

  6. Open a terminal.

  7. Change to the desired parent directory of your local fork.

  8. Clone your fork, replacing {URL} with the previously copied URL.

    git clone {URL}
    
  9. Add a new remote referring to this upstream repository.

    git remote add upstream https://github.com/beartype/beartype.git
    
  10. Uninstall all previously installed versions of beartype. For example, if you previously installed beartype with pip, manually uninstall beartype with pip.

    pip uninstall beartype
    
  11. Install beartype with pip in editable mode. This synchronizes changes made to your fork against the beartype package imported in Python. Note the [dev] extra installs developer-specific mandatory dependencies required at test or documentation time.

    pip3 install -e .[dev]
    
  12. Create a new branch to isolate changes to, replacing {branch_name} with the desired name.

    git checkout -b {branch_name}
    
  13. Make changes to this branch in your favourite Integrated Development Environment (IDE). Of course, this means Vim.

  14. Test these changes. Note this command assumes you have installed all major versions of both CPython and PyPy supported by the next stable release of beartype you are hacking on. If this is not the case, install these versions with pyenv. This is vital, as type hinting support varies significantly between major versions of different Python interpreters.

    tox
    

    The resulting output should ideally be suffixed by a synopsis resembling:

    ________________________________ summary _______________________________
    py36: commands succeeded
    py37: commands succeeded
    py38: commands succeeded
    py39: commands succeeded
    pypy36: commands succeeded
    pypy37: commands succeeded
    congratulations :)
    
  15. Stage these changes.

    git add -a
    
  16. Commit these changes.

    git commit
    
  17. Push these changes to your remote fork.

    git push
    
  18. Click the "Create pull request" button in the upper right-hand corner of your fork page.

  19. Afterward, routinely pull upstream changes to avoid desynchronization with the "beartype/beartype" repository.

    git checkout main && git pull upstream main
    

Moar Depth

So, you want to help beartype deeply type-check even more type hints than she already does? Let us help you help us, because you are awesome.

First, an egregious lore dump. It's commonly assumed that beartype only internally implements a single type-checker. After all, every other static and runtime type-checker only internally implements a single type-checker. Why would a type-checker internally implement several divergent overlapping type-checkers and... what would that even mean? Who would be so vile, cruel, and sadistic as to do something like that?

We would. beartype often violates assumptions. This is no exception. Externally, of course, beartype presents itself as a single type-checker. Internally, beartype is implemented as a two-phase series of orthogonal type-checkers. Why? Because efficiency, which is the reason we are all here. These type-checkers are (in the order that callables decorated by beartype perform them at runtime):

  1. Testing phase. In this fast first pass, each callable decorated by @beartype only tests whether all parameters passed to and values returned from the current call to that callable satisfy all type hints annotating that callable. This phase does not raise human-readable exceptions (in the event that one or more parameters or return values fails to satisfy these hints). @beartype highly optimizes this phase by dynamically generating one wrapper function wrapping each decorated callable with unique pure-Python performing these tests in O(1) constant-time. This phase is always unconditionally performed by code dynamically generated and returned by:
    • The fast-as-lightning pep_code_check_hint() function declared in the "beartype._decor._code._pep._pephint" submodule, which generates memoized O(1) code type-checking an arbitrary object against an arbitrary PEP-compliant type hint by iterating over all child hints nested in that hint with a highly optimized breadth-first search (BFS) leveraging extreme caching, fragile cleverness, and other salacious micro-optimizations.
  2. Error phase. In this slow second pass, each call to a callable decorated by @beartype that fails the fast first pass (due to one or more parameters or return values failing to satisfy these hints) recursively discovers the exact underlying cause of that failure and raises a human-readable exception precisely detailing that cause. @beartype does not optimize this phase whatsoever. Whereas the implementation of the first phase is uniquely specific to each decorated callable and constrained to O(1) constant-time non-recursive operation, the implementation of the second phase is generically shared between all decorated callables and generalized to O(n) linear-time recursive operation. Efficiency no longer matters when you're raising exceptions. Exception handling is slow in any language and doubly slow in dynamically-typed (and mostly interpreted) languages like Python, which means that performance is mostly a non-concern in "cold" code paths guaranteed to raise exceptions. This phase is only conditionally performed when the first phase fails by:
    • The slow-as-molasses raise_pep_call_exception() function declared in the "beartype._decor._code._pep._error.peperror" submodule, which generates human-readable exceptions after performing unmemoized O(n) type-checking of an arbitrary object against a PEP-compliant type hint by recursing over all child hints nested in that hint with an unoptimized recursive algorithm prioritizing debuggability, readability, and maintainability.

This separation of concerns between performant O(1) testing on the one hand and perfect O(n) error handling on the other preserves both runtime performance and readable errors at a cost of developer pain. This is good! ...what?

Secondly, the same separation of concerns also complicates the development of @beartype. This is bad. Since @beartype internally implements two divergent type-checkers, deeply type-checking a new category of type hint requires adding that support to (wait for it) two divergent type-checkers – which, being fundamentally distinct codebases sharing little code in common, requires violating the Don't Repeat Yourself (DRY) principle by reinventing the wheel in the second type-checker. Such is the high price of high-octane performance. You probably thought this would be easier and funner. So did we.

Thirdly, this needs to be tested. After surmounting the above roadblocks by deeply type-checking that new category of type hint in both type-checkers, you'll now add one or more unit tests exhaustively exercising that checking. Thankfully, we already did all of the swole lifting for you. All you need to do is add at least one PEP-compliant type hint, one object satisfying that hint, and one object not satisfying that hint to:

You're done! Praise Guido.

Moar Compliance

So, you want to help beartype comply with even more Python Enhancement Proposals (PEPs) than she already complies with? Let us help you help us, because you are young and idealistic and you mean well.

You will need a spare life to squander. A clone would be most handy. In short, you will want to at least:

You're probably not done by a long shot! But the above should at least get you fitfully started, though long will you curse our names. Praise Cleese.

License

beartype is open-source software released under the permissive MIT license.

Funding

beartype is currently financed as a purely volunteer open-source project – which is to say, it's unfinanced. Prior funding sources (yes, they once existed) include:

  1. A Paul Allen Discovery Center award from the Paul G. Allen Frontiers Group under the administrative purview of the Paul Allen Discovery Center at Tufts University over the period 2015—2018 preceding the untimely death of Microsoft co-founder Paul Allen, during which beartype was maintained as the private @type_check decorator in the Bioelectric Tissue Simulation Engine (BETSE). Phew!

Authors

beartype is developed with the grateful assistance of a volunteer community of enthusiasts, including (in chronological order of issue or pull request):

  1. Cecil Curry (@leycec). Hi! It's me. From beartype's early gestation as a nondescript @type_check decorator in the Bioelectric Tissue Simulation Engine (BETSE) to its general-audience release as a public package supported across multiple Python and platform-specific package managers, I shepherd the fastest, hardest, and deepest runtime type-checking solution in any dynamically-typed language towards a well-typed future of PEP-compliance and boundless quality assurance. Cue epic taiko drumming.
  2. Felix Hildén (@felix-hilden), the Finnish computer vision expert world-renowned for his effulgent fun-loving disposition and:
  3. @harens, the boisterous London developer acclaimed for his defense of British animals that quack pridefully as they peck you in city parks as well as maintaining our first third-party package, a macOS-specific MacPorts Portfile estimated to solve all your problems.
  4. @Heliotrop3, the perennial flowering plant genus from Peru whose "primal drive for ruthless efficiency makes overcoming these opportunities for growth [and incoming world conquest] inevitable" as well as:

See Also

External beartype resources include:

Runtime type checkers (i.e., third-party mostly pure-Python packages dynamically validating Python callable types at Python runtime, typically via decorators, explicit function calls, and import hooks) include:

Static type checkers (i.e., third-party tooling not implemented in Python statically validating Python callable and/or variable types across a full application stack at tool rather than Python runtime) include:

  • mypy.
  • Pyre, published by FaceBook. ...yah.
  • pyright, published by Microsoft.
  • pytype, published by Google.
Issues
  • 3.9+ PEP 585 - type hints for builtin types

    3.9+ PEP 585 - type hints for builtin types

    In case you haven't seen this: https://www.python.org/dev/peps/pep-0585/

    Relevant:

    >>> isinstance([1, 2, 3], list[str])
    TypeError: isinstance() arg 2 cannot be a parameterized generic
    
    opened by terrdavis 19
  • README suggestion

    README suggestion

    This all looks splendid. And I can see that you are laying down code at high-speed, so this may already be on the agenda but just as a suggestion: it may make sense to have part of the README explain how the O(1) type checking is performed for arbitrarily long containers.

    opened by albanie 16
  • macOS Package Management Documentation

    macOS Package Management Documentation

    Hi there! 👋

    I was so impressed by @leycec's way of writing issues/README/etc. (it's amazing ✨) that I felt compelled to help in the way I knew best, macOS package management.

    Unfortunately, Homebrew doesn't really do python modules, so following https://github.com/macports/macports-ports/pull/9756, beartype has been added to MacPorts! Here is the Project Page.

    I am notified whenever you release a new version on GitHub, so I will aim to update the Portfile with your release (and I have generally been pretty consistent with this for the other ports I maintain).

    I opened this issue since I thought it might be useful to document this. Let me know if you have any questions about this, and thank you very much for this project 👍.

    Packaging status

    EDIT: I nearly forgot, here's how to install the project:

    sudo port install py-beartype
    
    opened by harens 16
  • `beartype`d modules with forward references can't be `importlib.reload`ed

    `beartype`d modules with forward references can't be `importlib.reload`ed

    This is probably a super esoteric use case, but I stumbled across this (quite on accident):

    # karma.py
    
    from __future__ import annotations
    from beartype import beartype
    
    class Chameleon:
        def __init__(self, colors: str):
            self.colors = colors
        @classmethod
        @beartype
        def like_my_dreams(cls) -> Chameleon:
            return Chameleon("red, gold, and green")
    
    # test_karma.py
    
    import unittest
    
    class TestReloadBearTyped(unittest.TestCase):
        def test_1_imported(self) -> None:
            import karma
            assert karma.Chameleon.like_my_dreams().colors == "red, gold, and green"
    
        def test_2_reloaded(self) -> None:
            import karma
            from importlib import reload
            karma = reload(karma)  # <-- this pulls the rug out from under the bear (comment this out and all tests pass)
            assert karma.Chameleon.like_my_dreams().colors == "red, gold, and green"
    
        def test_3_imported_again(self) -> None:
            self.test_1_imported()  # <-- once the beartyped module is reloaded in the prior test, this fails too
    
    if __name__ == "__main__":
        unittest.main()
    

    Results:

    % python test_karma.py
    .EE
    ======================================================================
    ERROR: test_2_reloaded (__main__.TestReloadBearTyped)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/…/test_karma.py", line 14, in test_2_reloaded
        assert karma.Chameleon.like_my_dreams().colors == "red, gold, and green"
      File "<string>", line 18, in like_my_dreams
      File "/…/lib/python3.9/site-packages/beartype/_decor/_error/errormain.py", line 316, in raise_pep_call_exception
        raise exception_cls(  # type: ignore[misc]
    beartype.roar.BeartypeCallHintPepReturnException: @beartyped Chameleon.like_my_dreams() return <karma.Chameleon object at 0x105c30eb0> violates type hint <class 'karma.Chameleon'>, as <karma.Chameleon object at 0x105c30eb0> not <class "karma.Chameleon">.
    
    ======================================================================
    ERROR: test_3_imported_again (__main__.TestReloadBearTyped)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/…/test_karma.py", line 17, in test_3_imported_again
        self.test_1_imported()
      File "/…/test_karma.py", line 8, in test_1_imported
        assert karma.Chameleon.like_my_dreams().colors == "red, gold, and green"
      File "<string>", line 18, in like_my_dreams
      File "/…/lib/python3.9/site-packages/beartype/_decor/_error/errormain.py", line 316, in raise_pep_call_exception
        raise exception_cls(  # type: ignore[misc]
    beartype.roar.BeartypeCallHintPepReturnException: @beartyped Chameleon.like_my_dreams() return <karma.Chameleon object at 0x105c30ee0> violates type hint <class 'karma.Chameleon'>, as <karma.Chameleon object at 0x105c30ee0> not <class "karma.Chameleon">.
    
    ----------------------------------------------------------------------
    Ran 3 tests in 0.043s
    
    FAILED (errors=2)
    

    Juggling Bear

    opened by posita 13
  • beartype performance is substantially slower than no beartype

    beartype performance is substantially slower than no beartype

    Hello,

    I was quite interested when I found this stackoverflow answer about beartype... As a POC, I cooked up a performance test using beartype, Enthought traits, traitlets, and plain-ole-python-ducktyping...

    But... I found that beartype was pretty slow in my test.... As an attempt to be as fair as possible, I used assert to enforce types in my duck-typed function (ref - main_duck_assert())...

    I also confess that Enthought traits are compiled, so the Enthought traits data below is mostly just an FYI...

    Running my comparison 100,000 times...

    $ python test_type.py
    timeit duck getattr time: 0.0395 seconds
    timeit duck assert  time: 0.0417 seconds
    timeit traits       time: 0.0633 seconds
    timeit traitlets    time: 0.5236 seconds
    timeit bear         time: 0.0782 seconds
    $
    

    Question Am I doing something wrong with bear-typing (ref my POC code below)? Is there a way to improve the "beartyped" performance?

    My rig...

    • Linux under VMWare (running on a Lenovo T430); kernel version 4.19.0-12-amd64
    • Python 3.7.0
    • beartype version 0.8.1
    • Enthought traits version 6.3.0
    • traitlets, version 5.1.0
    from beartype import beartype
    
    from traits.api import HasTraits as eHasTraits
    from traits.api import Unicode as eUnicode
    from traits.api import Int as eInt
    
    from traitlets import HasTraits as tHasTraitlets
    from traitlets import Unicode as tUnicode
    from traitlets import Integer as tInteger
    
    from timeit import timeit
    
    def main_duck_getattr(arg01="__undefined__", arg02=0):
        """Proof of concept code implenting duck-typed args and getattr"""
        getattr(arg01, "capitalize")  # Type-checking with attributes
        getattr(arg02, "to_bytes")    # Type-checking with attributes
    
        str_len = len(arg01) + arg02
        getattr(str_len, "to_bytes")
        return ("duck_bar", str_len,)
    
    def main_duck_assert(arg01="__undefined__", arg02=0):
        """Proof of concept code implenting duck-typed args and assert"""
        assert isinstance(arg01, str)
        assert isinstance(arg02, int)
    
        str_len = len(arg01) + arg02
        assert isinstance(str_len, int)
        return ("duck_bar", str_len,)
    
    
    class MainTraits(eHasTraits):
        """Proof of concept code implenting Enthought traits args"""
        arg01 = eUnicode()
        arg02 = eInt()
        def __init__(self, *args, **kwargs):
            super(MainTraits, self).__init__(*args, **kwargs)
    
        def run(self, arg01="__undefined__", arg02=0):
            self.arg01 = arg01
            self.arg02 = arg02
            self.str_len = len(self.arg01) + self.arg02
            return ("traits_bar", self.str_len)
    
    class MainTraitlets(tHasTraitlets):
        """Proof of concept code implenting traitlets args"""
        arg01 = tUnicode()
        arg02 = tInteger()
        def __init__(self, *args, **kwargs):
            super(MainTraitlets, self).__init__(*args, **kwargs)
    
        def run(self, arg01="__undefined__", arg02=0):
            self.arg01 = arg01
            self.arg02 = arg02
            self.str_len = len(self.arg01) + self.arg02
            return ("traitlets_bar", self.str_len)
    
    @beartype
    def main_bear(arg01: str="__undefined__", arg02: int=0) -> tuple:
        """Proof of concept code implenting bear-typed args"""
        str_len = len(arg01) + arg02
        return ("bear_bar", str_len,)
    
    if __name__=="__main__":
        num_loops = 100000
    
        duck_result_getattr = timeit('main_duck_getattr("foo", 1)', setup="from __main__ import main_duck_getattr", number=num_loops)
        print("timeit duck getattr time:", round(duck_result_getattr, 4), "seconds")
    
        duck_result_assert = timeit('main_duck_assert("foo", 1)', setup="from __main__ import main_duck_assert", number=num_loops)
        print("timeit duck assert  time:", round(duck_result_assert, 4), "seconds")
    
        traits_result = timeit('mm.run("foo", 1)', setup="from __main__ import MainTraits;mm = MainTraits()", number=num_loops)
        print("timeit traits       time:", round(traits_result, 4), "seconds")
    
        traitlets_result = timeit('tt.run("foo", 1)', setup="from __main__ import MainTraitlets;tt = MainTraitlets()", number=num_loops)
        print("timeit traitlets    time:", round(traitlets_result, 4), "seconds")
    
        bear_result = timeit('main_bear("foo", 1)', setup="from __main__ import main_bear", number=num_loops)
        print("timeit bear         time:", round(bear_result, 4), "seconds")
    
    opened by mpenning 13
  • Numeric Tower / Union[float, int]

    Numeric Tower / Union[float, int]

    I think I found an edge case with the annotation float when passed an int in beartype. For convenience, PEP484 accepts int when float is annotated: https://www.python.org/dev/peps/pep-0484/#the-numeric-tower

    from beartype import beartype
    
    
    @beartype
    def measure_cave(length: float, width: float):
        return length * width
    
    
    measure_cave(12.0, 12.0)  # PASS
    measure_cave(12, 12)  # type hint <class 'float'>, as "12" not instance of float.
    

    I'm currently using Union[float, int] to work with beartype and wondered if you would consider implementing the PEP484 "numeric tower" logic or keep beartype as-is?

    opened by KyleKing 12
  • PEP 561 compliance

    PEP 561 compliance

    Hi @leycec and the @beartype organisation 👋

    I'm currently rewriting large parts of a project of mine, and I'm planning on using beartype 🐻 with it! (because beartype is awesome 😎)

    Part of the rewrite involves creating a python module that users will be able to import. Currently, my project is PEP 561 compatible. However, beartype causes some issues with this making it no longer the case.

    For instance, if I were to run mypy --strict on my code, I now get lots of the following errors:

    Untyped decorator makes function "..." untyped
    Skipping analyzing 'beartype.cave': found module but no type hints or library stubs
    Skipping analyzing 'beartype': found module but no type hints or library stubs
    

    This then has the knock on effect that the functions in my project appear untyped when imported, making it no longer PEP 561 compliant.

    Assuming beartype is properly annotated, which I'm 200% sure it is, it should hopefully be as simple as adding a py.typed file in the project directory. See the mypy docs for more info. This could then be another PEP ticked off in the compliance list.

    Since you have a lot (and I really do mean a lot) more experience with these different PEPs and type checking than I do, please let me know if I'm missing anything. Thank you for your help and for maintaining this great project.

    opened by harens 12
  • Cannot use numpy.typing.NDArray type hints

    Cannot use numpy.typing.NDArray type hints

    Numpy v1.21.0 introduced runtime subscriptable aliases for ndarrays. The feature is really cool and I really like the direction that it seems to be headed in.

    However, these new annotations do not seem compatible with the @beartype decorator.

    System info

    OS: Windows 10, 20H2 Python: 3.8.10 Beartype: 0.71.0 Numpy: 1.21.0

    Sample Code

    Running this sample code:

    from beartype import beartype
    import numpy as np
    import numpy.typing as npt
    
    @beartype
    def foo(arr: npt.NDArray[np.float64]) -> npt.NDArray[np.float64]:
        return arr
    
    foo(np.zeros(3))
    

    Yields this error:

    Traceback (most recent call last):
      File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\beartype\_util\cls\utilclstest.py", line 174, in die_unless_type_isinstanceable
        isinstance(None, cls)  # type: ignore[arg-type]
      File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\numpy\typing\_generic_alias.py", line 137, in __instancecheck__
        raise TypeError("isinstance() argument 2 cannot be a "
    TypeError: isinstance() argument 2 cannot be a parameterized generic
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
      File "<stdin>", line 2, in <module>
      File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\beartype\_decor\main.py", line 268, in beartype
        func_wrapper_code = generate_code(func_data)
      File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\beartype\_decor\_code\codemain.py", line 162, in generate_code
        code_params = _code_check_params(data)
      File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\beartype\_decor\_code\codemain.py", line 324, in _code_check_params
        hint = coerce_hint_pep(
      File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\beartype\_decor\_cache\cachehint.py", line 254, in coerce_hint_pep
        die_unless_hint(hint=hint, hint_label=hint_label)
      File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\beartype\_util\hint\utilhinttest.py", line 104, in die_unless_hint
        die_unless_hint_nonpep(hint=hint, hint_label=hint_label)
      File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\beartype\_util\hint\nonpep\utilhintnonpeptest.py", line 111, in die_unless_hint_nonpep
        die_unless_hint_nonpep_type(
      File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\beartype\_util\hint\nonpep\utilhintnonpeptest.py", line 200, in die_unless_hint_nonpep_type
        die_unless_type_isinstanceable(
      File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\beartype\_util\cls\utilclstest.py", line 208, in die_unless_type_isinstanceable
        raise exception_cls(exception_message) from exception
    beartype.roar.BeartypeDecorHintNonPepException: @beartyped foo() parameter "arr" type hint numpy.ndarray[typing.Any, numpy.dtype[numpy.float64]] uncheckable at runtime (i.e., not passable as 
    second parameter to isinstance() due to raising "isinstance() argument 2 cannot be a parameterized generic" from metaclass __instancecheck__() method).
    
    

    Knowing that Beartype leverages the Python built-in Annotated type, it roughly makes sense why this error would be occurring. While creating a Beartype numpy array using Annotated and lambda still has its uses, static type checkers such as Pylance/Pyright are now equipped to recognize this new Numpy typing syntax (that's how I initially found out about this change; my code had a ton of new errors I didn't understand), but I don't know if they can recognize the Beartype version in the same way.

    opened by Antyos 10
  • Popular value constraint

    Popular value constraint

    Hello! I'm creating this issue to see how people feels about choosing the most popular constrains so we can agree which one are more important/urgent, feel free also to comment improve the list if you feel like I'm missing something. I'm gonna create some categories as well for visibility

    String Constraints

    • Email
    • Uuid
    • Choice
    • Language
    • Locale
    • Country
    • Currency

    Comparison Constraints

    • EqualTo
    • NotEqualTo
    • IdenticalTo
    • NotIdenticalTo
    • LessThan
    • GreaterThan
    • Range
    • DivisibleBy

    The implementation I think should be something like:

        passports: Annotated[List[str], Country[alpha3=True]],
        age: Annotated[date, Range["1901-12-12", "2001-12-12"]],
        pets: Annotated[int, GreaterThan[-1]],
        bio: Annotated[Optional[str], Range[50, 500]],
    
    opened by Saphyel 9
  • Afford returning NotImplemented from all binary dunder methods

    Afford returning NotImplemented from all binary dunder methods

    This expands on 57e5e99 to implicitly tolerate returning NotImplemented from binary dunder methods, irrespective of return type (not just those returning bool). Ref. #38.

    opened by posita 9
  • [Feature Request] Granular warning messages

    [Feature Request] Granular warning messages

    I would like to apply beartype as an aspect-decorator via justuse use(thing) @ (isfunction, "", beartype) but with only

    g:\Python398\lib\site-packages\beartype\_util\hint\pep\utilpeptest.py:373: BeartypeDecorHintPep585DeprecationWarning: PEP 484 type hint typing.Callable[..., 
    typing.Any] deprecated by PEP 585 scheduled for removal in the first Python version released after October 5th, 2025. To resolve this, either drop Python < 3.9 support and globally replace this hint by the equivalent PEP 585 type hint (e.g., "typing.List[int]" by "list[int]") or see this discussion topic for saner and more portable solutions:
        https://github.com/beartype/beartype#pep-484-deprecations
      warn(
    

    as warning, there's not much to go on..

    opened by amogorkon 12
  • [Feature Request] Granular exception messages for synthesized beartype validators

    [Feature Request] Granular exception messages for synthesized beartype validators

    Hi, I have been studying this library and noticed that the README suggests one prefer conjoining ("and"-ing) conditions over enumerating them (","-separating) them.

    from dataclasses import dataclass
    from typing import Annotated
    from beartype import beartype
    
    @dataclass
    class Foo:
        x: int
        y: int
    
    @beartype
    def conjoined(
        x, y
    ) -> Annotated[
        "Foo",
        Is[lambda foo: foo.x + foo.y >= 0] # force separation of lines so only 1 lambda per line
        & Is[lambda foo: foo.x + foo.y <= 10],
    ]:
        return Foo(x, y)
    
    conjoined(100, 100)
    
    BeartypeCallHintPepReturnException: @beartyped conjoined() return "Foo(x=100, y=100)" violates type hint typing.Annotated[ForwardRef('Foo'), Is[lambda foo: foo.x + foo.y >= 0] & Is[lambda foo: foo.x + foo.y <= 10]], as "Foo(x=100, y=100)" violates validator Is[lambda foo: foo.x + foo.y >= 0] & Is[lambda foo: foo.x + foo.y <= 10].
    

    However, this doesn't go in and tell me which condition failed (maybe 1, maybe all).

    Enumeration seems to do the trick though:

    @beartype
    def enumerated(
        x, y
    ) -> Annotated[
        "Foo",
        Is[lambda foo: foo.x + foo.y >= 0],
        Is[lambda foo: foo.x + foo.y <= 10],
    ]:
        return Foo(x, y)
    
    enumerated(100, 100)
    
    BeartypeCallHintPepReturnException: @beartyped enumerated() return "Foo(x=100, y=100)" violates type hint typing.Annotated[ForwardRef('Foo'), Is[lambda foo: foo.x + foo.y >= 0], Is[lambda foo: foo.x + foo.y <= 10]], as "Foo(x=100, y=100)" violates validator Is[lambda foo: foo.x + foo.y <= 10].
    

    To be it seems enumerated conditions could / should be preferred. Could you explain any arguments in favour of conjunction?

    opened by dycw 5
  • [Feature Request] PEP 604 Support

    [Feature Request] PEP 604 Support

    Hi @leycec , thanks for a great library!

    Upon discovered of this decorator, I thought I'd give beartype a run in my codebase -- I am a heavy user of dataclasses. And, of course, a heavy user of runtime-checks!

    After the proof-of-concept work, everything still works. But I did notice:

    • the from future import __annotations__ had to be removed (I'm a 3.9 user). Is this required by your users?
    • the initial performance was poorer -- I'd continue to move the handcrafted runtime checks into Is[]. I'd report back later on this.
    opened by dycw 8
  • [Feature Request] Full-fat O(n) linear-time type-checking option

    [Feature Request] Full-fat O(n) linear-time type-checking option

    Python version: 3.8

    I have a class to store data. The structure of the class is the following:

    from typing import List
    
    from beartype import beartype
    
    class Data:
        def __init__(self):
            self.__data1 = None
        
        @beartype
        def set_data1(self, data1: List[int]):
            self.__data1 = data1
    
        @beartype
        def get_data1(self) -> List[int]:
            return self.__data1
    

    I have implemented some Unit Test in order to check that everything is correct (they're many data attributes and I like to make a double check). I have implemented the Unit Test as follows:

    class TestModelData(unittest.TestCase):
    
        def setUp(self) -> None:
            self.instance = Data()
            self.instance .set_data1([1, 2, 3])
    
        def test_data1(self):
            with self.assertRaises(BeartypeCallHintPepParamException):
                self.instance.set_data(['1', 2, 3])
    
    if __name__ == '__main__':
        unittest.main()
    

    The bug I think I have found (unless there is something out of my understanding) is the following. The test sometimes passes, and sometimes it doesn't. The Exception is not always raised, but only sometimes.

    opened by damarro3 4
  • Feature suggestion: decorator with dataclasses

    Feature suggestion: decorator with dataclasses

    Big fan of the project. I was thinking that something like

    @beartype
    @dataclasses.dataclass
    class Whatever:
        something: int
        other_thing: string
    
    
    if __name__ == "__main__":
        asd = Whatever(123.321, b"Other error")  # Beartype error
    

    Would be really great.

    opened by JulesGM 5
  • [Feature Request] Add a `beartype.overload` decorator to accept overloaded functions

    [Feature Request] Add a `beartype.overload` decorator to accept overloaded functions

    Greetings

    Hi, dear weird bear aficionado! First of all, thanks for this awesome library! I've just started using it and it looks great, I can't wait to write runtime-type-safe functions everywhere.

    What I would like to see

    One functionality that I would love to see here is the ability to write overloaded functions, akin to typing.overload, but at runtime. Something like this:

    from beartype import overload
    
    @overload
    def greet(name: str, age: int) -> None: ...
    
    @greet.overload
    def greet(age: int) -> None: ...
    
    @greet.implement
    def greet(name_or_age, age=None):
        if age is None:
            age = name_or_age
    
            if age > 20:
                print('Hello, unknown person! You are allowed to enter.')
            else:
                print('Go back home, unknown child!')
        else:
            name = name_or_age
    
            if age > 20:
                print(f'Hello, {name}! You are allowed to enter.')
            else:
                print(f'Go back home, {name}!')
    
    greet('Ruan', 24) # prints "Hello, Ruan! You are allowed to enter."
    greet(24) # prints "Hello, unknown person! You are allowed to enter."
    greet('Ruan') # ROOOOAARRR!!! - because there are no overloads matching the signature "greet(str)"
    

    Note that this is not equivalent to writing greet(name_or_age: Union[str, int], age: Optional[int]) since, for instance, greet(10, 10) is invalid.

    Basically, beartype should test function calls against all of the overloaded signatures. If any of them matches, it's okay. Otherwise, ROAR!

    Also note that here I separated the @overload part from the @implement one. This is because...

    What I am not proposing

    I am not proposing function dispatching here. So the following is not what I wish to see:

    from beartype import dispatch
    
    @dispatch
    def greet(name: str, age: int) -> None:
        if age > 20:
            print(f'Hello, {name}! You are allowed to enter.')
        else:
            print(f'Go back home, {name}!')
    
    @dispatch
    def greet(age: int) -> None:
        if age > 20:
            print('Hello, unknown person! You are allowed to enter.')
        else:
            print('Go back home, unknown child!')
    

    Function dispatching brings a lot of issues such as deciding which implementation to choose for a given signature. And also, there are already some libraries out there that implement single and even multiple dispatching, I don't believe that that is a job for beartype.

    So...

    That is why I separated the @overload and the @implement parts. The way I see it, beartype should first check for the first overload, and then the second, the third and so on until one matching overload is found. If this is the case, then beartype should just call the @implement part without any further checks.

    Does that make sense to you? Looking forward to seeing your opinion on this!

    opened by ruancomelli 21
  • deep type-checking support of core data structures and abstract base classes

    deep type-checking support of core data structures and abstract base classes

    beartype currently only shallowly type-checks these type hints. It should, if at all possible, do this in constant time, runtime.

    This issue was copy-pasted from https://github.com/beartype/beartype/issues/7 — so see that issue for details. This issue tracks deep type-checking specifically. Might even split this one up further at some point.

    opened by jondequinor 2
  • [Community] Gitter chat – does anyone want this?

    [Community] Gitter chat – does anyone want this?

    This is an open issue harvesting the wisdom of the crowds and my intellectual superiors. That means you, of course!

    More than a few high-profile open-source projects have corresponding Gitter.im channels (e.g., mypy, NumPy, Python typing in general). Is this something any beartype enthusiasts would be interested in? If so, drop me an emoji here and we'll see about spinning something up. Then you can press me with awkward technical questions and I can indulge you with awkward anime memes. ...it'll happen.

    The advantage over GitHub itself is (of course) real-time feedback. The disadvantage over GitHub itself is that Gitter channels thrive on activity, interest, and hype – and die in their absence. Do we have enough here? Probably... not. At least not yet. We're no mypy... yet.

    Still. You know. I kinda like the idea of a derelict beartype treehouse. :evergreen_tree: :derelict_house: :evergreen_tree:

    opened by leycec 0
  • [Feature Request] Automagical Pytest Extension

    [Feature Request] Automagical Pytest Extension

    @Skylion007 of @facebookresearch fame kindly requested automation-on-automation action to automagically decorate all callables in a given package with @beartype at testing time without needing to explicitly do so or even calling an automagical import hook:

    Having this usable as a PyTest extension to decorate all PyTest testsuite calls like TypeGuard does would be automagical. One of our projects used to use Typeguard's PyTest setup for the CI, but we had to drop it because it was too slow with our PyTorch tests.

    This feature request tracks a solution building on the preceding request for a beartype import hook: a beartype pytest extension. Since typeguard already pioneered this dev space, our extension will probably superficially resemble theirs by exposing:

    • A new --beartype-modules={module_name_1},...,{module_name_N} option accepted by the pytest CLI command, applying @beartype to all pure-Python callables of all listed modules and submodules thereof.
    • A new modulelist={module_name_1},...,{module_name_N} option (or something something) of a new [pytest-beartype] section of a pytest.ini configuration – alternate on-disk syntax triggering the same automation.

    These feature requests keep receiving cute emoji, which means they will all happen. Let's look forward to beartype 0.9.0: The "It Finally Does Many Things You Always Wanted" Edition.

    opened by leycec 0
  • [Feature request] Automagical Import Hook

    [Feature request] Automagical Import Hook

    @ZeevoX of SentiPy and Sentiment Investor fame kindly requested automation to automagically decorate all callables in a given package with @beartype without needing to explicitly do so:

    Is it necessary to annotate each function with @beartype? Is there a way of more concisely adding beartype everywhere automagically?

    This feature request tracks our eventual (hopefully not too eventual) solution: a beartype import hook. Specifically:

    • We need to implement a new AST-based beartype.hook.beartype_everything() import hook inspired by typeguard's comparable typeguard.importhook.install_import_hook() function. That's only 162 lines of pure Python, which means this is mostly trivial, but it's a dense 162 lines, which means this might take an undefinable quantum of space-time.
    • Interested third parties will then call our import hook at the top of their top-level __init__ submodules to globally and transparently apply @beartype across their entire codebases.

    Grab that popcorn! @leycec's Wild Ride is now boarding for an improved @beartype user experience (UX).

    opened by leycec 2
Releases(v0.9.1)
  • v0.9.1(Nov 6, 2021)

    Beartype 0.9.1 released.

    This patch release delivers obstreperous support for Sphinx (curse ye and yer little side effects too, autodoc extension!), generator callables, readable exception messages, and Python 3.10 CI.

    This release resolves 4 issues and merges 0 pull requests. Fearsome changes include:

    Compatibility Improved

    • Sphinx. @beartype now explicitly provides first-class support for Sphinx's autodoc extension (i.e., sphinx.ext.autodoc), resolving issue #61 kindly submitted by SeldonIO/alibi ML dev maestro supremum Janis Klaise (@jklaise). @beartype now transparently ignores autodoc-mocked module attributes (e.g., produced by the autodoc_mock_imports list global in Sphinx-specific doc{s,}/conf.py configuration files of downstream documentation trees) used as type hints annotating @beartype-decorated callables, rendering @beartype compatible with autodoc-documented codebases. Since Sphinx lacks a public API for detecting when autodoc is currently generating documentation (see: sphinx-doc/sphinx#9805), @beartype now performs its own ad-hoc detection at decoration time with micro-optimized runtime complexity:
      • O(1) in the common case (i.e., Sphinx's autodoc extension has not been previously imported under the active Python interpreter).
      • O(n) in the worst case (i.e., Sphinx's autodoc extension has been previously imported under the active Python interpreter), where n is the height of the call stack leading to @beartype. Thanks, Sphinx. Thanks alot.
    • Generator callables. @beartype now relaxes the requirement that [a]synchronous generator callables be annotated by PEP 484 -compliant typing.{Async,}Generator[...] or PEP 585-compliant collections.abc.{Async,}Generator[...] type hints to additionally accept PEP 484-compliant typing.{Async,}Iterable[...] and typing.{Async,}Iterator[...] as well as PEP 585-compliant collections.abc.{Async,}Iterable[...] and collections.abc.{Async,}Iterator[...] type hints, resolving issue #65 kindly submitted by the @posita the positronic brain emitter.

    Features Improved

    • Exception message readability. @beartype now emits more human-readable, disambiguous, and useful exception messages or my middle name isn't "Brain Trust." Notably:
      • @beartype-decorated callables annotated by one or more numeric types (e.g., int, float, complex) now raise exceptions on type-checking failures whose exception messages disambiguate those numbers from strings, resolving issue #63 kindly submitted by @jefcolbi. Previously, these messages double-quoted all numbers for disambiguity with the possibly preceding sequence index of those numbers in their parent containers – introducing yet another ambiguity between numbers and strings. Numbers are now preserve in their vanilla unquoted form; meanwhile, sequence items are additionally disambiguated from sequence values with additional explanatory substrings.
      • Edge-case exception messages emitted by our memoized code generator no longer contain the mostly inappropriate substring `"wrapper parameter" except where absolutely appropriate.
      • Annotation-centric exception messages now guaranteeably contain the critical substring "type hint".

    Issues Resolved

    • #61, kindly submitted by SeldonIO/alibi ML dev maestro supremum Janis Klaise (@jklaise). See "Compatibility Improved" above.
    • #62, kindly submitted by Cal Leeming (@foxx), may his serious eyebrow-raising monochrome GitHub avatar be a guiding light to us all. See "Documentation Revised" below. (Thanks again to the foxy @foxx for appraising us all of hitherto unknown typing horrors. And just in time for Halloween too.)
    • #63, kindly submitted by @jefcolbi. See "Features Improved" above.
    • #65, kindly submitted by @posita the positronic brain emitter. See "Compatibility Improved" above.

    Tests Improved

    • Python 3.10 CI. This release resurrects Python 3.10 support in our GitHub Actions-based continuous integration (CI) configuration, circumventing upstream issue actions/setup-python#160 by coercively double-quoting all Python version strings specified as the values of "python-version:" keys. Ya!

    Documentation Revised

    • Reverse dependency showcase. The introduction to our front-facing README.rst documentation now gratefully advertises popular reverse dependencies (i.e., downstream open-source Python projects directly leveraging beartype) with a new on-brand icon bar. Gaze upon iconic icons and know the stylish face of unabashed beauty.

    • Type hint elision. The new "Type Hint Connectives" subsection of our front-facing README.rst documentation documents various means of circumventing counter-intuitive historicity in Python's core type hierarchy with (wait for it) beartype validators, resolving issue #62 kindly submitted by Cal Leeming (@foxx). Specifically, this subsection documents how to effectively declare:

      • A new PEP-compliant int - bool type hint as a beartype validator matching the set of all integers that are not booleans.
      • A new PEP-compliant Sequence - str type hint as a beartype validator matching the set of all sequences that are not strings.

    (Unleaded laden jelly-like underbellies!)

    Source code(tar.gz)
    Source code(zip)
  • v0.9.0(Oct 22, 2021)

    Beartype 0.9.0 released.

    This release adds voluminous support for asynchronous callables (including both coroutines and asynchronous generators) while improving existing support for typed NumPy arrays (i.e., numpy.typed.NDArray type hints), beartype validators (i.e., beartype.vale type hints), PEP 484, PEP 563, PEP 585, PEP 586, PEP 589, and PEP 593.

    This release resolves 6 issues and merges 2 pull requests. Non-cringe-worthy changes include:

    Compatibility Improved

    • Asynchronous coroutines. @beartype now transparently supports coroutines (i.e., callables declared with async def rather than merely def) with the exact same shallow and deep type-checking semantics as standard synchronous callables. Notably, @beartype now accepts all valid variants of coroutine type hints standardized by PEP 484, PEP 585, and mypy. This includes:
      • async def coroutine(...) -> {return}, which @beartype now wraps with an asynchronous coroutine first awaiting the decorated coroutine() and then validating the value returned by coroutine() satisfies the {return} type hint.
      • async def coroutine(...) -> typing.Coroutine[{yield}, {send}, {return}], which @beartype now wraps with an asynchronous coroutine first awaiting the decorated coroutine() and then validating the value returned by coroutine() satisfies the {return} type hint (while continuing to silently ignore the {yield} and {send} child type hints).
    • Asynchronous and synchronous generators. @beartype now transparently supports asynchronous generators (i.e., generators declared with async def rather than merely def) with the exact same shallow and deep type-checking semantics as standard synchronous generators. Notably, @beartype now requires the returns of:
      • Asynchronous generators to be annotated with either:
        • A PEP 484-compliant typing.AsyncGenerator[...] type hint.
        • A PEP 585-compliant collections.abc.AsyncGenerator[...] type hint.
      • Synchronous generators to be annotated with either:
        • A PEP 484-compliant typing.Generator[...] type hint.
        • A PEP 585-compliant collections.abc.Generator[...] type hint.
    • Beartype validators under Python 3.6 and 3.7. Beartype validators (which previously required Python ≥ 3.8) are now portably usable across all supported Python versions, including Python 3.6 and 3.7. beartype 0.9.0 refactors the entire beartype.vale class hierarchy to leverage the widely supported __getitem__() dunder method supported by Python ≥ 3.6 rather than the PEP 560-compliant __class_getitem__() dunder method supported only under Python ≥ 3.8 and not supported by mypy. Naturally, this was pain.
    • Typed NumPy arrays. @beartype now accepts all valid variants of numpy.typing.NDArray[{dtype}] type hints also accepted by mypy, where {dtype} is either:
      • An actual NumPy dtype (e.g., numpy.typing.NDArray[numpy.dtype(numpy.float64)]).
      • An object safely coercible into an actual NumPy dtype, including: * A scalar NumPy type (e.g., numpy.typing.NDArray[numpy.float64]).
      • A scalar NumPy abstract base class (ABC) (e.g., numpy.typing.NDArray[numpy.floating]). Previously, @beartype rejected scalar NumPy ABCs. Since @beartype now accepts these ABCs, the most portable means of type-checking NumPy arrays of arbitrary precision is to subscript numpy.typing.NDArray by the appropriate scalar ABC: e.g.,
      • numpy.typing.NDArray[numpy.floating] rather than numpy.typing.NDArray[numpy.float64], matching any floating-point NumPy array (regardless of precision).
      • numpy.typing.NDArray[numpy.integer] rather than numpy.typing.NDArray[numpy.int64]., matching any integer NumPy array (regardless of precision). Lastly, @beartype now supports these hints across all Python versions. Under Python ≥ 3.9, this support works as expected out-of-the-box. Under Python 3.6, 3.7, and 3.8:
      • If the optional third-party typing_extensions package is also importable, @beartype now deeply type-checks these hints as expected.
      • Else, @beartype now only shallowly type-checks these hints by internally reducing all numpy.typing.NDArray[{dtype}] type hints to the untyped NumPy array class numpy.ndarray. Since this is admittedly non-ideal, @beartype now emits one non-fatal warning of category beartype.roar.BeartypeDecorHintNonpepNumpyWarning at decoration time for each such reduction. Don't blame us. We voted for Kodos. How could this be our fault!?!? it's totally our fault
    • PEP 484. @beartype now:
      • Deeply type-checks PEP 484-compliant typing.Type type hints, validating parameters and returns to be subclasses of the subscripted type. This includes all syntactic variants standardized by PEP 484:
        • typing.Type, matching any issubclassable type (i.e., normal class passable as the second parameter to the issubclass() builtin).
        • typing.Type[Any], also matching any issubclassable type.
        • typing.Type[{type}], matching both the issubclassable type {type} and any subclass of that type.
        • typing.Type[{forward_ref}], first dynamically resolving the forward reference {forward_ref}' to an issubclassable type{type}` at call time and then matching both that type and any subclass of that type.
        • typing.Type[typing.Union[{type1}, {type2}, ..., {typeN}], permissively matching the issubclassable types {type1}, {type2}, and {typeN} as well as any subclass of those types.
      • Partially deeply type-checks PEP 484-compliant typing.Coroutine type hints, validating callables to be coroutines. Given a type hint typing.Coroutine[{yield}, {send}, {return}], @beartype now deeply type-checks the {return} child type hint (while continuing to silently ignore the {yield} and {send} child type hints).
      • Shallowly type-checks PEP 484-compliant parametrized type variables (i.e., typing.TypeVar instances instantiated with either two or more constraints or one upper bound). @beartype continues to silently ignore unparametrized type variables (i.e., type variables instantiated with neither constraints nor upper bounds). Notably, @beartype now shallowly type-checks any type variable instantiated with:
        • Constraints passed as positional parameters (e.g., typing.TypeVar('T', str, bytes)) as a union of those positional parameters instead (e.g., as typing.Union[str, bytes]).
        • An upper bound passed as the bound keyword parameter (e.g., typing.TypeVar('T', bound=float)) as that upper bound instead (e.g., as float).
      • Emits more self-explanatory deprecation warnings for PEP 484-compliant type hints deprecated by PEP 585 under Python ≥ 3.9. Specifically:
        • A new "PEP 484 Deprecations" subsection has been added to our front-facing README.rst documentation.
        • Our existing beartype.roar.BeartypeDecorHintPepDeprecatedWarning class has been refined into a new beartype.roar.BeartypeDecorHintPep484DeprecationWarning class specific to this category of deprecation, enabling downstream consumers (this means you) to selectively ignore only this category of deprecation.
        • This deprecation warning message now references this "PEP 484 Deprecations" subsection subsection with improved clarity and an anxiety-provoking speech invoking developer horror by suggesting your codebase will die in 2025 unless you do something painful today.
    • PEP 563. @beartype now:
      • Reverts our unconditionally enable of PEP 563 under Python ≥ 3.10 in solidarity with the recent (pydantic|FastAPI)-led casus belli. PEP 563 must now be explicitly enabled under all Python interpreters via the standard from __future__ import annotation pragma.
      • Explicitly avoids resolving PEP 563-postponed type hints that are relative forward references to parent callables or classes; by definition, parent callables and classes have yet to be defined and thus cannot be reasonably resolved and thus must be preserved as relative forward references until eventually resolved at call time.
    • PEP 585. @beartype now:
      • Deeply type-checks supports PEP 585-compliant type type hints, exactly as described for PEP 484-compliant typing.Type type hints above.
      • Partially deeply type-checks PEP 585-compliant collections.abc.Coroutine type hints, exactly as described for PEP 484-compliant typing.Coroutine type hints above.
      • Deduplicates all PEP 585 type hints, dramatically reducing both the space and time complexity associated with such hints. PEP 484-compliant type hints (e.g., List[str]) and indeed most other PEP-compliant type hints are already effectively deduplicated by caching hidden in the standard typing module (e.g., List[str] is List[str]). Despite deprecating PEP 484, PEP 585 fails to deduplicate its hints (e.g., list[str] is not list[str]). @beartype now internally deduplicates duplicated PEP 585-compliant type hints at decoration time via a thread-safe cache from the machine-readable string representations of such hints to such hints.
    • PEP 586 (i.e., Literal). @beartype now transparently supports both the official PEP 586-compliant typing.Literal type hint and its quasi-official typing_extensions.Literal backport to older Python versions. Thanks to cutting-edge Microsoft luminary @pbourke for his detailed assessment and resolution of everything that wrong with @beartype.
    • PEP 588 (i.e., TypedDict). @beartype now shallowly type-checks typed dictionaries (i.e., both typing.TypedDict type hints under Python ≥ 3.8 and typing_extensions.TypedDict type hints under Python < 3.8) by internally reducing these hints to simply Mapping[str, object]. Doing so was surprisingly non-trivial, as the Python 3.8-specific implementation of the typing.TypedDict subclass is functionally deficient and (presumably) never meaningfully unit-tested. It's not a good look.
    • PEP 593 (i.e., Annotated). @beartype now transparently supports both the official PEP 593-compliant typing.Annotated type hint and its quasi-official typing_extensions.Annotated backport to older Python versions. Thanks to cutting-edge Microsoft luminary @pbourke for his detailed assessment and resolution of everything that wrong with @beartype... yet again.

    Features Added

    • Subclass beartype validator. The new beartype.vale.IsSubclass[{type}] beartype validator validates arbitrary objects and object attributes to be subclasses of the superclass {type}. Whereas the comparable PEP 484-compliant typing.Type[{type}] and PEP 585-compliant type[{type}] type hints validate the same semantics only on @beartype-decorated callable parameters and returns annotated by those hints, beartype.vale.IsSubclass[{type}] validates those semantics on any objects reachable with beartype validators – including arbitrary deeply nested attributes of when coupled with the existing beartype.vale.IsAttr[{attr_name}, {validator}] beartype validator. In fact, @beartype internally reduces all typed NumPy arrays subscripted by scalar NumPy ABCs** (e.g., numpy.typing.NDArray[numpy.floating]) to semantically equivalent beartype validators (e.g., typing.Annotated[numpy.ndarray, beartype.vale.IsAttr['dtype', beartype.vale.IsAttr['type', beartype.vale.IsSubclass[numpy.floating]]]]).

    Features Deprecated

    beartype 0.9.0 deprecates decrepit relics of a long-forgotten past littering the beartype codebase with unseemly monoliths to human hubris. Specifically, importing these deprecated attributes under beartype ≥ 9.0 now emits non-fatal DeprecationWarning warnings at runtime:

    • beartype.cave.HintPep585Type, which should now be accessed as the non-deprecated beartype.cave.HintGenericSubscriptedType attribute.
    • beartype.cave.NumpyArrayType, which should now be accessed directly as numpy.ndarray.
    • beartype.cave.NumpyScalarType, which should now be accessed directly as numpy.generic.
    • beartype.cave.SequenceOrNumpyArrayTypes, which should now be annotated as typing.Union[collections.abc.Sequence, numpy.ndarray].
    • beartype.cave.SequenceMutableOrNumpyArrayTypes, which should now be annotated directly as typing.Union[collections.abc.MutableSequence, numpy.ndarray].
    • beartype.cave.SetuptoolsVersionTypes, which should now be accessed directly as packaging.version.Version.
    • beartype.cave.VersionComparableTypes, which should now be annotated directly as typing.Union[tuple, packaging.version.Version].
    • beartype.cave.VersionTypes, which should now be annotated directly as typing.Union[str, tuple, packaging.version.Version].
    • beartype.roar.BeartypeDecorHintNonPepException, which should now be accessed as the non-deprecated beartype.roar.BeartypeDecorHintNonpepException attribute.
    • beartype.roar.BeartypeDecorHintNonPepNumPyException, which should now be accessed as the non-deprecatedbeartype.roar.BeartypeDecorHintNonpepNumpyException` attribute.
    • beartype.roar.BeartypeDecorHintPepDeprecatedWarning, which should now be accessed as the non-deprecatedbeartype.roar.BeartypeDecorHintPepDeprecationWarning` attribute.

    Issues Resolved

    • Typed NumPy arrays subscripted by scalar NumPy ABCs (e.g., numpy.typing.NDArray[numpy.floating]), resolving issue #48 kindly submitted by @braniii the bran-eating brainiac. See above for details.
    • PEP 563-postponed relative forward references, resolving issue #49 kindly submitted by the positive positron emitter and all-around stand-up statistics guru @posita. See above for details. Thanks to @posita for his ongoing commitment to awesome posita/dyce rolling type-checked by the growling bear!
    • PEP 586 typing_extensions.Literal and PEP 593 typing_extensions.Annotated backports, resolving issue #52 kindly submitted by cutting-edge Microsoft graph luminary @pbourke. See above for details.
    • PEP 589 typed dictionaries (i.e., TypedDict), resolving issue #55 kindly submitted by Kyle (@KyleKing), the undisputed King of Parexel AI Labs.
    • mypy-specific ~~logorrhea~~ valid complaints, including:
      • Implicit reexport complaints beartype 0.9.0 is now compatible with both the --no-implicit-reexport mypy CLI option and equivalent no_implicit_reexport = True configuration setting in .mypy.ini, resolving issue #57 kindly submitted by Göteborg melodic death metal protégé and assumed academic luminary @antonagestam. Specifically, beartype now internally reexports all exception and warning classes in the private beartype.roar.__init__ submodule under... their exact same names. Look. We don't make the rules. We just circumvent them.
      • Beartype validator subscription complaints. beartype 0.9.0 resolves a long-standing integration issue when subjecting beartype to static type checking by a static type checker that is almost certainly mypy. Previously, mypy erroneously emitted one false positive for each otherwise valid beartype validator (e.g., error: The type "Type[IsAttr]" is not generic and not indexable [misc]). Mypy and presumably other static type checkers now no longer do so, substantially improving the usability of downstream packages leveraging both static type checking and beartype validators.
    • Windows-compatible testing, kindly submitted by cutting-edge Microsoft luminary @pbourke. Previously, our test_doc_readme() unit test non-portably assumed UTF-8 to be the default file encoding under all platforms and thus loudly failed under Windows, which still defaults to the single-byte encoding cp1252. Thanks to @pbourke and the valiant team at microsoft/graspologic, the logical statistical graph grasper.

    Tests Improved

    • Asynchronous testing. Our new beartype_test.conftest pytest plugin effectively implements the useful subset of the mostly unmaintained, overly obfuscatory, and poorly commented and documented pytest-asyncio project -- which, unsurprisingly, has an outrageous number of unresolved issues and unmerged pull requests. Thus dies another prospective mandatory test dependency. We now give thanks.

    • Git-agnostic testing. The beartype test suite has been generalized to support testing from arbitrary directory layouts, including testing of both local clones of our remote git repository and local extractions of our remote PyPI- and GitHub-hosted source tarballs. Previously, our test suite only supported the former – due to bad assumptions that will haunt our issue tracker like the stench of uncooked salmon on the banks of Bear River.

    • PyPy 3.6 testing support dropped. beartype 0.9.0 now circumvents obscure non-human-readable exceptions raised by the macOS-specific implementation of PyPy 3.6 when testing under GitHub Actions-based continuous integration (CI), resolving pypy/pypy#3314. Since Python 3.6 has almost hit its official End of Life (EoL) anyway, we've chosen the Easy Road: unconditionally omit PyPy 3.6 from testing and pretend this never happened. You didn't see nuffin'.

    (Reticulated ticks gesticulate; ant antics masticate!)

    Source code(tar.gz)
    Source code(zip)
  • v0.8.1(Aug 21, 2021)

    Beartype 0.8.1 released.

    This minor release resolves a significant edge case with typed NumPy arrays (i.e., numpy.typed.NDArray type hints). This release resolves 1 issue – albeit a significant issue for NumPy users. Changes include:

    Issues Resolved

    • #47, kindly submitted by @braniii the awesome brainiac. @beartype now generates syntactically valid type-checking for typed NumPy arrays nested in beartype validators nested in fixed-length tuple type hints (e.g., Tuple[ Annotated[NDArray[np.floating], Is[lambda arr: arr.ndim == 1]], Annotated[NDArray[np.floating], Is[lambda arr: arr.ndim == 1]]]). Previously, @beartype erroneously chained assignment expressions sans parens protection. @beartype now avoids chaining assignment expressions altogether.

    Interestingly, whereas chained assignments are syntactically valid, chained assignment expressions are syntactically invalid unless protected with parens under Python ≥ 3.8:

    >>> a = b =    'Mother*Teacher*Destroyer'  # <-- fine
    >>> (a :=      "Mother's Abomination")     # <-- fine
    >>> (a := (b := "Mother's Illumination"))  # <-- fine
    >>> (a := b := "Mother's Illumination")    # <-- not fine
    SyntaxError: invalid syntax
    

    The more you know, the less you know. Thanks again to @braniii for all the vigorous enthusiasm and brain-melting subtleties. You made data science a safer place for us. (Elucidative sedatives redact acidity!)

    Source code(tar.gz)
    Source code(zip)
  • v0.8.0(Aug 18, 2021)

    Beartype 0.8.0 released.

    This release brings undeniable support for typed NumPy arrays (i.e., numpy.typed.NDArray type hints), typing backports (i.e., public public attributes of the third-party typing_extensions package, enabling typing_ types introduced by newer Python versions to be used under older Python versions), and portable beartype validators (i.e., beartype.vale type hints usable under all Python versions via the typing_extensions.Annotated backport). This release resolves 4 issues and merges 2 pull requests. Changes include:

    Compatibility Improved

    • Typed NumPy arrays (i.e., numpy.typed.NDArray type hints). The @beartype decorator now transparently supports the third-party numpy.typing.NDArray type hint newly introduced by NumPy ≥ 1.21.0, resolving issue #42 kindly submitted by NumPy extraordinaire @Antyos. Note that usage of typed NumPy arrays under Python < 3.9.0 requires installation of the third-party typing-extensions package, which @beartype will then automagically detect and leverage internally.
    • Typing backports (i.e., typing_extensions type hints). The @beartype decorator now transparently supports all public type hints of the third-party typing_extensions package, resolving issue #34 kindly submitted by Ryan Soklaski (@rsokl) of considerable MIT Beaver Works Summer Institute and Python Like You Mean It fame.
    • Python < 3.9.0 beartype validators. The @beartype decorator now portably supports beartype validators (i.e., beartype.vale objects annotating typing{_extensions}.Annotated type hints) across all Python versions, also resolving issue #34 kindly submitted by Ryan Soklaski (@rsokl). hallowed be his username Note that usage of beartype validators under Python < 3.9.0 requires:
      • Installation of the third-party typing-extensions package.
      • Annotation of beartype.vale objects by typing_extensions.Annotated (rather than typing.Annotated).
    • Python >= 3.10.0 typing.NewType type hints. This release resolves a minor incompatibility recently introduced by Python 3.10.0rc1, which (waitforit) broke backward compatibility with prior implementations of the public typing.NewType type hint. Previously, that hint was implemented as a closure; Python ≥ 3.10 fundamentally refactored that hint to a pure-Python class instance – significantly complicating cross-version detection. yuwastemytime, CPython?
    • Binary dunder method return annotations (i.e., dunder methods accepting exactly two arguments). Previously, @beartype adopted mypy's permissive approach of implicitly coercing the return annotations of binary dunder methods returning only booleans to instead return typing.Union[bool, type(NotImplemented)]. beartype now expands that approach to all binary dunder methods regardless of return annotation. Thanks to pull request #40 from Matt Bogosian (@posita), Dropbox's positive code genius!

    Issues Resolved

    • #44, documenting beartype's new project shield generously ginned up by Matt Bogosian (@posita), Dropbox's positive code genius.
    • #42, enabling typed NumPy arrays to be used under both Python ≥ 3.9.0 natively and Python < 3.9.0 via the typing_extensions.Annotated backport.
    • #40, including:
      • Fake builtin types (i.e., non-builtin classes erroneously masquerading as builtin). Previously, @beartype incorrectly generated syntactically invalid code for decorated callables annotated by one or more fake builtin types – which, miraculously, there are hardly any of. Still... it's not a good look, beartype. Thanks to Matt Bogosian (@posita), Dropbox's positive code genius, for first detecting this horrifying edge case in pull request #40.
    • #34, enabling beartype validators to be used under Python < 3.9.0 via the typing_extensions.Annotated backport.

    Documentation Revised

    • Badge (i.e., shield intended for display by downstream projects). Our front-facing README.rst documentation now provides a post-installation advisory suggesting public declaration of @beartype project compatibility with reST- and Markdown-friendly text exhibiting a beautiful @beartype badge. Unsurprisingly, @posita made that too... because what doesn't @posita do? Nuthin'! I'm pretty sure that means @posita does everything.

    Tests Improved

    • Optional dependencies. Our GitHub Actions-based continuous integration (CI) configuration now installs optional test-time dependencies. Although beartype has no mandatory runtime dependencies, fully exercising all tests necessitates installing these test-time dependencies:

      • mypy, enabling tests for mypy-based PEP 561 compliance.
      • NumPy, enabling tests for numpy.typing.NDArray type hints. This proved surprisingly non-trivial. Apple's patently broken "Accelerate" BLAS replacement (as documented at numpy/numpy#15947) blocks NumPy ≥ 1.18.0 installation under default CI configurations, necessitating that we:
        • Only conditionally install pip packages with --force-reinstall under macOS. CI gonna CI.
        • Avoid installing NumPy altogether under macOS + PyPy, where even the --force-reinstall option fails to improve matters. You can only do so much when your core platform is fundamentally broken. Thanks, Apple.
      • typing_extensions, enabling tests for typing attributes unavailable under the active Python interpreter.

    Thanks yet again to Badge Connoisseur @posita for his tremendous efforts – which we are now eternally indebted to and can only repay across the span of several gruelling lifetimes. "It will all be worth it," I tell my future self.

    (Clogged clogs and unlogged cogs!)

    Source code(tar.gz)
    Source code(zip)
  • v0.7.1(Jun 30, 2021)

    This release delivers eleventh-hour support for the NotImplemented singleton and improved compliance with static analysis tooling like static type-checkers (e.g., mypy) and intelligent IDEs (e.g., not Vim). This release resolves 4 outstanding issues and merges 1 pending pull request.

    Changes include:

    Compatibility Improved

    • NotImplemented singleton. @beartype now explicitly supports the builtin NotImplemented singleton with the same behaviour implemented by mypy to support this singleton, resolving issue #38 kindly reported by Matt the positive @posita. Specifically, @beartype now implicitly coerces binary dunder methods (e.g., __eq__()) annotated as returning booleans to instead be annotated as returning either booleans or the NotImplemented singleton (e.g., from def __eq__(self, other: object) -> bool to def __eq__(self, other: object) -> Union[bool, type(NotImplemented)]).
    • Static analysis. @beartype now better complies with static analysis performed by both static type-checkers (e.g., mypy) and intelligent IDEs (e.g., not Vim), resolving both issues #36 (kindly reported by @jonathanmorley the majestic code maestro) and #39 (again kindly reported by Matt the positive @posita). Specifically, the @beartype decorator itself is now annotated as returning a callable having the exact same signature as the passed callable. Previously, @beartype was annotated as merely returning a callable of arbitrary signature. Thanks again to @jonathanmorley the majestic code maestro for both the initial issue and pull request resolving that issue!

    (Frenetic phrenology eulogizes occipital antics!)

    Source code(tar.gz)
    Source code(zip)
  • v0.7.0(May 25, 2021)

    Beartype 0.7.0 released.

    This release brings titillating support for beartype validators, Python 3.10, PEP 563 – "Postponed Evaluation of Annotations", and PEP 586 – "Literal Types".

    This release resolves 4 outstanding issues and merges 1 pending pull request. Significant changes include:

    Features Added

    • Beartype validators, the world's first PEP-compliant validation API. Validate anything with two-line type hints designed by you, built by the @beartype decorator for you. The new public beartype.vale subpackage enables beartype users to design their own PEP-compliant type hints enforcing arbitrary runtime constraints on the internal structure and contents of parameters and returns via user-defined lambda functions and nestable declarative expressions leveraging familiar typing syntax – all seamlessly composable with standard type hints through an expressive domain-specific language (DSL). Specifically, @beartype-decorated callables may now be annotated by type hints of the form typing.Annotated[{cls}, beartype.vale.Is[lambda obj: {test_expr1}], ..., beartype.vale.Is[lambda obj: {test_exprN}]], where:
      • {cls} is any arbitrary class (e.g., str, numpy.ndarray).
      • {test_expr1} and {test_exprN} are any arbitrary expressions evaluating to booleans (e.g., len(obj) <= 80, obj.dtype == np.dtype(np.float64)). beartype.vale.Is may also be subscripted (indexed) by non-lambda callables with similar signatures. For convenience, beartype.vale.Is objects support a rich domain-specific language (DSL) enabling new validators to be synthesized from existing validators with Pythonic set operators:
      • Negation with ~beartype.vale.Is[lambda obj: {test_expr}], equivalent to beartype.vale.Is[lambda obj: not {test_expr}].
      • And-ing with beartype.vale.Is[lambda obj: {test_expr1}] & beartype.vale.Is[lambda obj: {test_expr2}], equivalent to beartype.vale.Is[lambda obj: {test_expr1} and {test_expr2}].
      • Or-ing with beartype.vale.Is[lambda obj: {test_expr1}] | beartype.vale.Is[lambda obj: {test_expr2}], equivalent to beartype.vale.Is[lambda obj: {test_expr1} or {test_expr2}]. This syntax fully complies with PEP 593 and thus requires Python ≥ 3.9. See Beartype validators for full usage instructions, complete with real-world examples including tensors. Rejoice machine learning data scientists! This resolves issue #32, kindly submitted by fashionable London steampunk cat pimp @Saphyel (Carlos Jimenez).

    Features Optimized

    • Package importation. The first importation of both the beartype package and @beartype decorator has been significantly optimized, now consuming on the order of microseconds rather than milliseconds (or even seconds in the worst case). This critical optimization should significantly improve runtime performance for short-lived CLI applications. Isn't that great, guys? ...guys? awkward cough
    • Wrapper function attributes. The @beartype decorator now generates unconditionally faster type-checking wrapper functions. Previously, attributes accessed in the bodies of those functions were indirectly passed to those functions via a common dictionary singleton referred to as the "beartypistry" directly passed to those functions; while trivial, this approach had the measurable harm of one dictionary lookup for each attribute access in those functions. Now, the same attributes are instead directly passed as optional private beartype-specific parameters to these functions; while non-trivial, this approach has the measurable benefit of avoiding any dictionary lookups by instead localizing all requisite attributes to the signatures of those functions. Of course, this isn't just an optimization; this is also a hard prerequisite for supporting both "PEP 586 -- Literal Types" and beartype validators. The beartypistry singleton remains used only to dynamically resolve forward references to undeclared user types.

    Compatibility Improved

    • Python ≥ 3.10.0. @beartype now officially supports Python 3.10, currently in beta but maybe-soon-to-be-released thanks to Python's accelerated release schedule. Python 3.10 significantly broke backwards compatibility with runtime introspection of type hints and thus runtime type checkers, complicating support for Python 3.10 for most runtime type checkers (including us). Specifically, Python 3.10 unconditionally enables "PEP 563 -- Postponed Evaluation of Annotations" – an abysmal standard preferentially improving the efficiency of statically type-checked applications by reducing the efficiency of applications also checked by runtime type checkers. We can only protest with skinny fists lifted like antennas to GitHub. Praise be to Guido.
    • PEP 563 – "Postponed Evaluation of Annotations". While beartype 0.1.1 only partially supported PEP 563, @beartype now fully supports all edge cases associated with PEP 563 – including postponed methods, nested functions, closures, and forward references. Forward references merit particular mention. Why? Because of course, forward references are fundamentally indistinguishable from PEP 563-postponed type hints, because PEP 563 was never intended to be usable at runtime. Unsurprisingly, it isn't. While numerous Python packages superficially support PEP 563 by deferring to the broken typing.get_type_hints() function, @beartype is the first and thus far only annotation-based Python package to fully support PEP 563 and thus Python 3.10.
    • PEP 586 – "Literal Types". The @beartype decorator now fully supports the new typing.Literal type hint introduced by Python ≥ 3.9. Note, however, that beartype validators offer similar but significantly more practical support for type hint-based equality comparison in our new beartype.vale.IsEqual class.

    Issues Resolved

    • typing.OrderedDict under Python 3.7.0 and 3.7.1. @beartype now conditionally imports the typing.OrderedDict singleton only if the active Python interpreter targets Python ≥ 3.7.2, the patch release that bizarrely changed the public typing API by introducing this new public attribute. Doing so improves compatibility with both Python 3.7.0 and 3.7.1 and resolves issue #33 – kindly reported by @aiporre, the dancing unicorn that radiates sparkles named Ariel.

    Tests Improved

    • Test coverage. The test suite for @beartype now automatically generates test coverage metrics – resolving #20. This includes:
      • Locally via the third-party coverage package (if importable under the active Python interpreter). @beartype intentionally leverages the coverage package directly rather than its higher-level pytest-cov wrapper, as the latter offers no tangible benefits over the former while suffering various tangible harms. These include:
        • Insufficient configurability, preventing us from sanely generating XML-formatted reports via our existing tox.ini configuration.
        • Ambiguous output, preventing us from sanely differentiating expected from unexpected behaviours.
        • Argumentative and strongly opinionated developers, which is frankly never a good look for open-source volunteerism.
      • Remotely via the third-party Codecov.io coverage service, integrated with the codecov/codecov-action now performed on each commit and pull request by our GitHub Actions continuous integration (CI) workflow.
    • Python Development Mode (PDM). The PDM (e.g., -X dev, PYTHONDEVMODE) is now enabled by default under both pytest and tox and thus continuous integration (CI), mildly improving the robustness of our test suite in edge cases that absolutely should never apply (e.g., GIL and memory safety violations) but probably will, because bad things always happen to good coders. It's, like, a law.

    Documentation Revised

    • See Also. The See Also section of our front-facing README.rst documentation has been significantly expanded with:

      • A comparative review of all known runtime type checkers.
      • A new Runtime Data Validators subsection enumerating all known runtime validation (e.g., contract, trait) packages.

    (Winsome winners ransom random dendritic endoscopy!)

    Source code(tar.gz)
    Source code(zip)
  • v0.6.0(Mar 4, 2021)

    Beartype 0.6.0 released.

    This release brings explicit support for None, subscripted generics, and PEP 561 compliance after resolving 10 issues and merging 8 pull requests. Changes include:

    Compatibility Improved

    • PEP 484-compliant None singleton. As a return type hint, None is typically used to annotate callables containing no explicit return statement and thus implicitly returning None. @beartype now implicitly reduces None at all nesting levels of type hints to that singleton's type per PEP 484.
    • PEP 561 compliance. beartype now fully conforms to PEP 561, resolving issue #25 kindly submitted by best macOS package manager ever @harens. In useful terms, this means that:
      • beartype now complies with mypy, Python's popular third-party static type checker. If your package had no mypy errors or warnings before adding beartype as a mandatory dependency, your package will still have no mypy errors or warnings after adding beartype as a mandatory dependency.
      • beartype preserves PEP 561 compliance. If your package was PEP 561-compliant before adding beartype as a mandatory dependency, your package will still be PEP 561-compliant after adding beartype as a mandatory dependency. Of course, if your package currently is not PEP 561-compliant, beartype can't help you there. We'd love to, really. It's us. Not you.
      • The beartype codebase is now mostly statically rather than dynamically typed, much to our public shame. Thus begins the eternal struggle to preserve duck typing in a world that hates bugs.
      • The beartype package now contains a top-level py.typed file, publicly declaring this package to be PEP 561-compliant.
    • Subscripted generics (i.e., user-defined generics subscripted by one or more type hints), resolving issue #29 kindly submitted by indefatigable test engineer and anthropomorphic Siberian Husky @eehusky. Since it's best not to ask too many questions about subscripted generics, we instead refer you to the issue report that nearly broke a Canadian man.

    Compatibility Broken

    • None. This release preserves backward compatibility with the prior stable release.

    Packaging Improved

    • New optional installation-time extras, enabling both beartype developers and automation tooling to trivially install recommended (but technically optional) dependencies. These include:
      • pip install -e .[dev], installing beartype in editable mode as well as all dependencies required to both locally test beartype and build documentation for beartype from the command line.
      • pip install beartype[doc-rtd], installing beartype as well as all dependencies required to build documentation from the external third-party Read The Docs (RTD) host.
    • Homebrew- and MacPorts-based macOS installation. Our front-facing README.rst file now documents beartype installation with both Homebrew and MacPorts on macOS, entirely courtesy the third-party Homebrew tap and Portfile maintained by build automation specialist and mild-mannered student @harens. Thanks a London pound, Haren!

    Features Added

    • New beartype.cave types and type tuples, including:

      • beartype.cave.CallableCTypes, a tuple of all C-based callable types (i.e., types whose instances are callable objects implemented in low-level C rather than high-level Python).
      • beartype.cave.HintGenericSubscriptedType, the C-based type of all subscripted generics if the active Python interpreter targets Python >= 3.9 or beartype.cave.UnavailableType otherwise. This type was previously named beartype.cave.HintPep585Type before we belatedly realized this type broadly applies to numerous categories of PEP-compliant type hints, including PEP 484-compliant subscripted generics.

    Features Optimized

    • O(n)O(1) exception handling. @beartype now internally raises human-readable exceptions in the event of type-checking violations with an O(1) rather than O(n) algorithm, significantly reducing time complexity for the edge case of invalid large sequences either passed to or returned from @beartype-decorated callables. For forward compatibility with a future version of beartype enabling users to explicitly switch between constant- and linear-time checking, the prior O(n) exception-handling algorithm has been preserved in a presently disabled form.
    • O(n)O(1) callable introspection during internal memoization. @beartype now avoids calling the inefficient stdlib inspect module from our private @beartype._util.cache.utilcachecall.callable_cached decorator memoizing functions throughout the beartype codebase. The prior O(n) logic performed by that call has been replaced by equivalent O(1) logic performed by a call to our newly definedbeartype._util.func.utilfuncargsubmodule, optimizing function argument introspection without the unnecessary overhead ofinspect`.
    • Code object caching. @beartype now temporarily caches the code object for the currently decorated callable to support efficient introspection of that callable throughout the decoration process. Relatedly, this also has the beneficial side effect of explicitly raising human-readable exceptions from the @beartype decorator on attempting to decorate C-based callables, which @beartype now explicitly does not support, because C-based callables have no code objects and thus no efficient means of introspection. Fortunately, sane code only ever applies @beartype to pure-Python callables anyway. ...right, sane code? Right!?!?

    Features Deprecated

    • The ambiguously named beartype.cave.HintPep585Type type, to be officially removed in beartype 0.1.0.

    Issues Resolved

    • Unsafe str.replace() calls. @beartype now wraps all unsafe internal calls to the low-level str.replace() method with calls to the considerably safer high-level beartype._util.text.utiltextmunge.replace_str_substrs() function, guaranteeing that memoized placeholder strings are properly unmemoized during decoration-time code generation. Thanks to temperate perennial flowering plant @Heliotrop3 for this astute observation and resolution to long-standing background issue #11.
    • KeyPool release validation. @beartype now validates that objects passed to the release() method of the private beartype._util.cache.pool.utilcachepool.KeyPool class have been previously returned from the acquire() method of that class. Thanks to @Heliotrop3, the formidable bug assassin, for their unswerving dedication to the cause of justice with this resolution to issue #13.
    • Least Recently Used (LRU) cache. @beartype now internally provides a highly microoptimized Least Recently Used (LRU) cache for subsequent use throughout the codebase, particularly with respect to caching iterators over dictionaries, sets, and other non-sequence containers. This resolves issue #17, again graciously submitted by open-source bug mercenary @Heliotrop3.
    • Callable labelling. @beartype now internally provides a private beartype._util.func.utilfuncorigin.get_callable_origin_label getter synthesizing human-readable labels for the files declaring arbitrary callables, a contribution by master code-mangler @Heliotrop3 resolving issue #18. Thanks again for all the insidious improvements, Tyler! You are the master of everyone's code domain.
    • Release automation. Our release workflow has now been migrated from the unmaintained create-release GitHub Action to @ncipollo's actively maintained release-action, resolving issue #22 kindly submitted by human-AI-hybrid @Heliotrop3.

    Tests Improved

    • Microsoft Windows and macOS exercised under CI, resolving issue #21. Since doing so increases our consumption of Microsoft resources that we care deeply about, care has been taken to reduce the cost of our CI workflow. This includes:
      • Replacing our prior use of the external third-party tox-gh-actions GitHub Action streamlining tox usage with our own ad-hoc build matrix that appears to be simpler and faster despite offering basically identical functionality.
      • Removing our prior installation of optional dependencies, especially including NumPy. Yeah. Let's not do that anymore. Thanks to dedicated issue reporter @Heliotrop3 for his unsustainable deep-code trawling of the beartype codebase for unresolved FIXME: comments.
    • PyPy 3.7 exercised under CI. Our tox and GitHub Actions-based continuous integration (CI) configurations now both correctly exercise themselves against both PyPy 3.6 and 3.7, resolving the upstream actions/setup-python#171 issue for beartype.
    • CI thresholded. Our CI configuration now caps tests to a sane maximum duration of time to avoid a repeat of the pull request we do not talk about here. Okay, it was #23. I blame only myself.
    • New functional tests, including:
      • A CPython-specific mypy functional test, optionally exercising our conformance to static type-checking standards when the third-party mypy package is installed under CPython. This test is sufficiently critical that we perform it under our CI workflow, guaranteeing test failures on any push or PR violating mypy expectations.
      • A README.rst functional test, optionally exercising the syntactic validity of our front-facing README.rst documentation when the third-party docutils package (i.e., the reference reST parser) is installed. This test is sufficiently expensive that we currently avoid performing it under our CI workflow.
    • New unit tests, including:
      • Text munging unit tests, exercising the private beartype._util.text.utiltextmunge submodule with lavish attention to regex-based fuzzy testing of the critical number_lines() function. Humble git log shout outs go out to @Heliotrop3 for this mythic changeset that warps the fragile fabric of the GitHub cloud to its own pellucid yet paradoxically impenetrable intentions, resolving issue #24.

    Documentation Revised

    • Sphinx skeleton. The beartype repository now defines a largely unpopulated skeleton for Sphinx-generated documentation formatted as reST and typically converted to HTML to be hosted at Read The Docs (RTD), generously contributed by @felix-hilden, Finnish computer vision expert and our first contributor! This skeleton enables:
      • An HTTP 404 redirect page on missing page hits.
      • The standard retinue of builtin Sphinx extensions (e.g., autodoc, viewcode).
      • MathJax configured for remote implicit downloads.
      • Napolean configured for NumPy-formatted docstrings.
      • An optional dependency on sphinx_rtd_theme, a third-party Sphinx extension providing RTD's official Sphinx HTML.
      • A badge (i.e., shield, status icon) on our front-facing README.rst documentation signifying the success of the most recent attempt to build and host this skeleton at RTD.
      • A top-level sphinx script, building Sphinx-based package documentation when manually run from the command line by interactive developers.
    • A beautiful banner graphic that makes grown adults openly weep, featuring the official beartype mascot "Mr. Nectar Palm" – again courtesy @felix-hilden, because sleep is for the weak and Felix has never known the word.
    • A new prefacing "tl;dr" section that's redundant with numerous other sections, but we're surprisingly okay with that.
    • A new "Usage" section that accidentally became a new tutorial and division of the existing "Overview" section into various subsections highlighting tradeoffs between beartype and existing type checkers, resolving clarity concerns raised by @kevinjacobs-progenity at issue #7. Thanks for the invaluable commentary, Kevin!
    • A new "Frequently Asked Questions (FAQ)" section, inspired by the admission from several prospective users that they have utterly no idea what @leycec is talking about. Fair play, users. You win this round.
    • A new "Workflow" subsection of the "Developer" section, listing developer-specific instructions for forking, cloning, installing, modifying, and submitting PRs for beartype in a live manner.
    • Properly rendered code blocks, kindly reported by humane human extraordinaire @harens in discussion topic #28. Thanks and may the little-seen English sun eternally shine upon ye, Haren!

    API Changed

    • Added:
      • beartype.cave.CallableCTypes.
      • beartype.cave.HintGenericSubscriptedType.
    • Deprecated:
      • beartype.cave.HintPep585Type.

    (Exogenous exhaustion!)

    Source code(tar.gz)
    Source code(zip)
  • v0.5.1(Dec 6, 2020)

    Beartype 0.5.1 released.

    This stable release significantly improves our front-facing README.rst as well as resolving an unrelated suite of minor issues.

    Specific changes include:

    Documentation Improved

    • Overview commentary. The new top-level Overview section better outlines beartype's various tradeoffs, advantages, and disadvantages versus competing type checkers.
    • Decorator commentary. The new top-level Decorator section documents code generated by @beartype under real-world use cases.
    • Developer commentary. The new top-level Developer section assists other developers in contributing to @beartype.

    Issues Resolved

    • Deeply ignorable new types. @beartype now correctly ignores calls to typing.NewType passed ignorable type hints rather than merely object.
    • PEP 544-compliant protocol detection. @beartype now reports all builtin types (e.g., int) to not be PEP 544-compliant protocols, despite a proper subset of builtin types erroneously claiming to be PEP 544-compliant protocols for strange and probably spurious reasons.

    Tests Improved

    • Python 3.9 exercised under CI. Our GitHub Actions-based continuous integration (CI) configuration now correctly exercises itself against Python 3.9 by explicitly passing the --skip-missing-interpreters=false CLI option to force CI failures when one or more Python environments are unavailable, resolving the upstream tox-dev/tox#903 issue for @beartype.

    (Fastidious fast tracks blast perfidious racks!)

    Source code(tar.gz)
    Source code(zip)
  • v0.5.0(Dec 2, 2020)

    Beartype 0.5.0 released.

    This stable release implements full compliance for PEP 585 -- Type Hinting Generics in Standard Collections, where "full compliance" means "@beartype deeply type-checks all categories of type hints deeply type-checked by prior stable releases and shallowly type-checks the remainder." See our compliance list and feature matrix for deeply exhausting, enervating, and deenergizing details.

    Specific changes include:

    Compatibility Improved

    • PEP 585. @beartype is now fully compliant with PEP 585 -- Type Hinting Generics in Standard Collections. Since that PEP supercedes and largely obsoletes the overwhelming majority of PEP 484, compliance with PEP 585 is critical for all static and runtime type checkers to guarantee forward compatibility with Python's type-checking ecosystem. Note this compliance includes:
      • Deep type-checking support for all comparable type hints with deep type-checking support in prior @beartype versions, including:
        • Subscriptions of these C-based builtin types and pure-Python abstract base classes (ABCs) declared by the stdlib:
          • list.
          • tuple.
          • collections.abc.ByteString.
          • collections.abc.MutableSequence.
          • collections.abc.Sequence.
        • User-defined PEP 585-compliant generics (e.g., class ListOfInts(list[int]): pass), which were implemented in a completely non-orthogonal manner to both PEP 484-compliant generics and PEP 544-compliant protocols and thus required "extreme" handling for PEP 585-specific edge cases.
    • Unhashable PEP-compliant type hints. @beartype now supports PEP-compliant type hints not hashable by the hash() builtin and thus impermissible for use as dictionary keys, set members, and memoized callable parameters. While all PEP 484-compliant type hints are hashable, many such hints (e.g., typing.Callable[[], str]) are unhashable when converted into equivalent PEP 585-compliant type hints (e.g., collections.abc.Callable[[], str]), necessitating that @beartype now permissively accept both hashable and unhashable type hints. The disadvantage of the latter is that the private @beartype._util.cache.utilcachecall.callable_cached decorator underlying internal decoration-time memoization performed by @beartype cannot, by definition, memoize calls passed one or more unhashable objects. Ergo, callables accepting one or more unhashable PEP-compliant type hints incur a performance penalty versus hashable PEP-compliant type hints. Note this penalty is only paid at decoration rather than call time and should thus be entirely negligible. Unhashable PEP-compliant type hints include:
      • PEP 585-compliant type hints subscripted by one or more unhashable objects (e.g., `collections.abc.Callable[[], str]).
      • PEP 586-compliant type hints subscripted by an unhashable object (e.g., typing.Literal[[]], a literal empty list).
      • PEP 593-compliant type hints subscripted by one or more unhashable objects (e.g., typing.Annotated[typing.Any, []], the typing.Any singleton annotated by an empty list).
    • @typing.no_type_check. @beartype now supports the PEP 484-compliant @typing.no_type_check decorator by silently ignoring (and thus reducing to a noop for) all callables decorated by that decorator.
    • typing.TYPE_CHECKING. @beartype now supports the PEP 484-compliant typing.TYPE_CHECKING boolean constant by silently reducing to the identity decorator when this boolean is True (i.e., during external static type checking).

    Compatibility Broken

    • Call-time PEP-noncompliant type hint exception classes removed. Since all PEP-noncompliant type hints supported by @beartype (e.g., tuple unions) are now internally coerced into equivalent PEP-compliant type hints, PEP-noncompliant type hint exception classes are now obsolete and have been summarily removed. These include:
      • beartype.roar.BeartypeCallHintNonPepException.
      • beartype.roar.BeartypeCallHintNonPepParamException.
      • beartype.roar.BeartypeCallHintNonPepReturnException.

    Features Deprecated

    • PEP 484-compliant type hints deprecated by PEP 585. @beartype now emits non-fatal warnings of class beartype.roar.BeartypeDecorHintPepDeprecatedWarning under Python >= 3.9 for each PEP 484-compliant type hint deprecated by PEP 585 annotating each decorated callables. Critically, note that this deprecates most PEP 484-compliant type hints accepted without warning by prior stable releases and, indeed, most of the existing typing stdlib module. Affected type hints include subscriptions of any of the following objects:
      • typing.AbstractSet.
      • typing.AsyncGenerator.
      • typing.AsyncIterable.
      • typing.AsyncIterator.
      • typing.Awaitable.
      • typing.ByteString.
      • typing.Callable.
      • typing.ChainMap.
      • typing.Collection.
      • typing.Container.
      • typing.ContextManager.
      • typing.Coroutine.
      • typing.Counter.
      • typing.DefaultDict.
      • typing.Deque.
      • typing.Dict.
      • typing.FrozenSet.
      • typing.Generator.
      • typing.ItemsView.
      • typing.Iterable.
      • typing.Iterator.
      • typing.KeysView.
      • typing.List.
      • typing.MappingView.
      • typing.Mapping.
      • typing.Match.
      • typing.MutableMapping.
      • typing.MutableSequence.
      • typing.MutableSet.
      • typing.Pattern.
      • typing.Reversible.
      • typing.Sequence.
      • typing.Set.
      • typing.Tuple.
      • typing.Type.
      • typing.ValuesView.

    Features Optimized

    • Standard PEP-noncompliant class type hints. @beartype now efficiently registers type hints that are standard classes not explicitly compliant with any existing PEP at decoration time and accesses these hints at call time via the standard beartypistry singleton used to resolve all other type hints rather than inefficiently accessing these hints at call time via dictionary lookup into the __annotations__ dunder attribute of the decorated callable, eliminating one dictionary lookup for each such hint for each call to each callable annotated by one or more such hints.
    • Beartype-specific tuple unions < PEP 484 unions. Beartype-specific tuple unions are now a strict semantic subset of PEP 484 unions. Previously, tuple unions and PEP 484 unions were implemented in disparate subpackages -- and the subpackage implementing tuple unions has become poorly documented, maintained, and tested. To reduce bit-rot across the codebase and unify the implementation of tuple and PEP 484 unions, @beartype now silently coerces all tuple unions into the equivalent PEP 484 unions at decoration time. Doing so resolves all outstanding performance and usability issues with tuple unions, including:
      • Tuple unions are now efficiently registered at decoration time and accessed at call time via the standard beartypistry singleton used to resolve all other type hints rather than inefficiently accessed at call time via dictionary lookup on the __annotations__ dunder attribute of the decorated callable, eliminating one dictionary lookup for each such hint for each call to each callable annotated by one or more such hints.
      • Duplicate types in tuple unions are now silently ignored rather than type-checked (e.g., (bool, str, bool) is now type-checked as (bool, str)).
      • Forward references in tuple unions (e.g., ('muh.TypeName', str)) are now efficiently resolved at call time via the standard beartypistry singleton used to resolve all other forward references rather than inefficiently resolved at call time via non-standard iteration hard-coded into all wrapper functions generated by @beartype for callables annotated by these tuple unions.
      • Registration and optimized access of types in tuple unions via our internal beartypistry type registrar.
    • Decoration-time memoization, particularly with respect to PEP 484 union type hints. All internal callables decorated by the private @beartype._util.cache.utilcachecall.callable_cached decorator are now efficiently called with positional arguments rather than inefficiently called with equivalent keyword arguments. Memoizing keyword arguments is substantially more space- and time-intensive than memoizing the equivalent positional arguments, partially defeating the purpose of memoization in the first place. To enforce this, these internal callables now emit new non-fatal private beartype.roar._BeartypeUtilCallableCachedKwargsWarning warnings when passed one or more keyword arguments.

    Issues Resolved

    Tests Improved

    • Third-party package tests restored. Unit tests conditionally depending upon the importability of third-party packages have been restored to their prior working state after an embarassingly elongated period of being ignored. These include:
      • The test_api_cave_lib_numpy() unit test exercising NumPy integration in the beartype.cave submodule.
      • The test_api_cave_lib_setuptools() unit test exercising setuptools integration in the beartype.cave submodule.

    API Changed

    • In the beartype.roar submodule:
      • Added:
        • BeartypeDecorHintPepDeprecatedWarning.
      • Removed:
        • BeartypeCallHintNonPepException.
        • BeartypeCallHintNonPepParamException.
        • BeartypeCallHintNonPepReturnException.

    (Styled turnstiles burn mile-high piles of corrugated corruption!)

    Source code(tar.gz)
    Source code(zip)
  • v0.4.0(Nov 19, 2020)

    Beartype 0.4.0 released.

    This stable release significantly improves beartype's compliance with Python Enhancement Proposals (PEPs), including full compliance with:

    • PEP 483 -- The Theory of Type Hints.
    • PEP 484 -- Type Hints.
    • PEP 544 -- Protocols: Structural subtyping (static duck typing).
    • PEP 560 -- Core support for typing module and generic types.
    • PEP 563 -- Postponed Evaluation of Annotations.
    • PEP 593 -- Flexible function and variable annotations.

    With respect to PEPs 483 and 484, "full compliance" means "beartype deeply type-checks a lot and at least shallowly type-checks the remainder." With respect to the remaining PEPs, "full compliance" means "beartype deeply type-checks everything."

    Specific changes include:

    Compatibility Broken

    • Call-time type hint exception classes renamed. For orthogonality with decoration-time type hint exception classes, all call-time type hint exception classes have been renamed from beartype.roar.BeartypeCallCheck*Exception to beartype.roar.BeartypeCallHint*Exception (e.g., from beartype.roar.BeartypeCallCheckPepParamException to beartype.roar.BeartypeCallHintPepParamException).

    Features Added

    • Deep typing.Annotated type-checking. Under Python ≥ 3.9, the @beartype decorator now deeply type-checks parameters and return values annotated by PEP 593 (i.e., "Flexible function and variable annotations")-compliant typing.Annotated type hints in guaranteed constant time.
    • Deep typing.Generic type-checking. The @beartype decorator now type-checks both the shallow types and deep contents of parameters and return values annotated by PEP 484-compliant generics (i.e., type hints subclassing a combination of one or more of the typing.Generic superclass and/or other typing non-class pseudo-superclasses) in guaranteed constant time. Specifically, beartype iteratively walks up the superclass hierarchy of each generic and deeply type-checks that the current parameter or return value satisfies all constraints implied by that superclass. This includes:
      • typing.IO. This was no small feat, as this abstract base class (ABC) fails to leverage structural subtyping (e.g., via the PEP 544-compliant typing.Protocol ABC) and is thus unusable at runtime... like most typing objects, sadly. Our solution is to ignore the existing implementation of these classes, declare our own internal typing.Protocol-based variants of these classes, and implicitly substitute all instances of these classes with our own variants during our breadth-first traversal (BFS) over PEP-compliant type hints.
      • typing.BinaryIO.
      • typing.TextIO.
    • Deep typing.NewType support. The @beartype decorator now deeply type-checks parameters and return values annotated by PEP 484-compliant new types (i.e., closures generated by the typing.NewType closure factory) in guaranteed constant time.
    • Deep typing.NoReturn support. The @beartype decorator now fully type-checks that callables annotated by the typing.NoReturn singleton return no values (i.e., either halt the active Python process or raise an exception) in guaranteed constant time.
    • Deep typing.Protocol type-checking. Under Python ≥ 3.8, the @beartype decorator now type-checks both the shallow types and deep contents of parameters and return values annotated by PEP 544 (i.e., "Protocols: Structural subtyping (static duck typing)")-compliant protocols (i.e., type hints subclassing a combination of one or more of the typing.Protocol superclass and/or other typing non-class pseudo-superclasses) in guaranteed constant time, similarly to how beartype type-checks generics. This includes:
      • typing.SupportsAbs.
      • typing.SupportsBytes.
      • typing.SupportsComplex.
      • typing.SupportsIndex.
      • typing.SupportsInt.
      • typing.SupportsFloat.
      • typing.SupportsRound.
    • Deep typing.Tuple type-checking. The @beartype decorator now type-checks both the shallow types and deep contents of parameters and return values annotated by PEP 484-compliant typing.Tuple type hints in guaranteed constant time. The three syntactic variants standardized by PEP 484 are all supported, including:
      • typing.Tuple[()], type-checking empty tuples.
      • typing.Tuple[{typename}, ...], type-checking tuples containing arbitrarily many items all satisfying the PEP-compliant child hint {typename} (e.g., typing.Tuple[str, ...], a tuple of strings).
      • typing.Tuple[{typename1}, ..., {typenameN}], type-checking tuples containing exactly N items each satisfying a unique PEP-compliant child hint {typenameI} (e.g., typing.Tuple[str, int, float], a tuple containing exactly one string, one integer, and one floating-point number in that order).
    • Shallow typing.AsyncContextManager, typing.ContextManager, typing.Match, and typing.Pattern support. The @beartype decorator now type-checks the shallow types (but not deep contents) of parameters and return values annotated by the PEP 484-compliant typing.AsyncContextManager, typing.ContextManager, typing.Match, and typing.Pattern objects. This was no small feat, as the implementations of:
      • typing.AsyncContextManager and typing.ContextManager define __repr__() dunder methods returning erroneous machine-readable representations obstructing proper support under prior releases.
      • typing.Match and typing.Pattern under Python 3.6 are non-trivially obtuse and basically broken at runtime.
    • Shallow type variable support. The @beartype decorator now shallowly type-checks all otherwise supported PEP-compliant type hints parametrized by one or more type variables (e.g., List[T], where T = TypeVar('T')). Previously, this decorator raised exceptions when decorating callables with such hints. This decorator does not yet deeply type-check type variables to be constrained across callable parameters and return values or class methods. For def muh_func(muh_param: List[T]) -> T: return muh_param[0], as example, this decorator now shallowly type-checks the passed parameter muh_param to be a list but does not yet deeply type-check that this function returns values of the same types as items of this list.

    Features Optimized

    • Nested PEP-compliant type hint type-checking. Under Python ≥ 3.8, the @beartype decorator now generates optimized code when deeply type-checking items contained in arbitrarily nested PEP-compliant type hints (e.g., typing.List[typing.Union[bool, str, typing.List[int]]]) with PEP 572-style assignment expressions, generalizing similar sequence-specific optimizations introduced with beartype 0.3.0.
    • PEP-compliant type hint breadth-first search (BFS). The @beartype decorator now internally catalogues PEP-compliant type hints during the breadth-first search (BFS) it performs over these hints as simple tuples rather than fixed lists, as the former are both mildly faster than the latter and significantly more maintainable (which is the main gain here).

    Issues Resolved

    • PEP-compliant type hint code generation memoization. The @beartype decorator now properly memoizes the type-checking code it generates for PEP-compliant type hints. Prior beartype releases silently failed to memoize this code across different callables annotated by the same hints.

    API Changed

    • In the beartype.cave submodule, added:
      • A new public beartype.cave.HintPep585Type attribute, defined as the C-based type of all PEP 585-compliant type hints (i.e., C-based type hint instantiated by subscripting either a concrete builtin container class like list or tuple or an abstract base class (ABC) declared by the collections.abc submodule like collections.abc.Iterable or collections.abc.Sequence) if the active Python interpreter targets at least Python 3.9.0 or beartype.cave.UnavailableType otherwise. This is a prerequisite for PEP 585 support in a subsequent stable release.
    • In the beartype.roar submodule, renamed:
      • BeartypeCallCheckException to BeartypeCallHintException.
      • BeartypeCallCheckPepException to BeartypeCallHintPepException.
      • BeartypeCallCheckPepParamException to BeartypeCallHintPepParamException.
      • BeartypeCallCheckPepReturnException to BeartypeCallHintPepReturnException.
      • BeartypeCallCheckNonPepException to BeartypeCallHintNonPepException.
      • BeartypeCallCheckNonPepParamException to BeartypeCallHintNonPepParamException.
      • BeartypeCallCheckNonPepReturnException to BeartypeCallHintNonPepReturnException.
    Source code(tar.gz)
    Source code(zip)
  • v0.3.2(Oct 11, 2020)

    Beartype 0.3.2 released. Changes include:

    Compatibility Improved

    • Python 3.9 support. The @beartype decorator now officially supports the first stable release of the Python 3.9.x series (i.e., Python 3.9.0).

    Features Optimized

    • Deeply ignorable unions. Arbitrary PEP-compliant unions subscripted by one or more shallowly ignorable arguments (e.g., typing.Union[bool, str, typing.Any]) are now ignored by silently reducing to type-checking noops.

    (Lurid absurdities abstain from a tizzy-fitting kitten's kitsch ditties!)

    Source code(tar.gz)
    Source code(zip)
  • v0.3.1(Oct 5, 2020)

    Beartype 0.3.1 released. Changes include:

    Compatibility Broken

    • Python 3.5 unsupported. This release drops support for Python 3.5, which recently hit its End of Life (EOL) in mid-September and thus now constitutes a demonstrable security risk.

    Issues Resolved

    • #5, a critical edge case induced by repeated @beartype decorations of different callables annotated by the same PEP-compliant type hints. To resolve this, all metadata (notably, the is_func_wrapper_needs_random_int boolean previously not memoized) required to communicate state between lower-level memoized callables and higher-level callables calling the former is now properly passed up the call stack as return values and thus properly memoized.

    Tests Improved

    • test_p484(), an existing unit test augmented to exercise this edge case in the general case, reducing the likelihood of related issues.
    • test_p484_sequence_standard_cached(), a newly defined unit test exercising this exact edge case, reducing the likelihood of regressions.

    (Sandblasted anthers of the crassly amped amphitheater!)

    Source code(tar.gz)
    Source code(zip)
  • v0.3.0(Oct 1, 2020)

    Beartype 0.3.0

    Changes include:

    Compatibility Broken

    • None. This release preserves backward compatibility with the prior stable release.

    Dependencies Bumped

    • None. Not that we had any dependencies to begin with, of course.

    Features Added

    • Deep sequence type-checking. The @beartype.beartype decorator now type-checks both the shallow types and deep contents of parameters and return values annotated with typing-based sequences in guaranteed constant time, exploiting a variant of the well-known coupon collector's problem by randomly type-checking one item at each nesting level of each sequence on each call of each decorated callable. Currently supported typing-based sequences include:
      • typing.List.
      • typing.MutableSequence.
      • typing.Sequence.
    • Human-readable exception messages on type-checking failures. When passed parameters or returning values violating PEP-compliant type hints, callables decorated by the @beartype.beartype decorator now raise human-readable exceptions exhibiting the exact cause(s) of those violations, including violations deeply nested in subcontainers (e.g., "@beartyped pep_hinted() parameter pep_hinted_param=[([37],)] violates PEP type hint typing.List[typing.Sequence[typing.MutableSequence[str]]], as list item 0 tuple item 0 list item 0 value "37" not str.") Currently supported typing type hints that now generate human-readable exception messages include:
      • typing.List.
      • typing.MutableSequence.
      • typing.Optional.
      • typing.Sequence.
      • typing.Union.

    Features Optimized

    • Deep sequence type-checking. Under Python >= 3.8, the @beartype.beartype decorator generates optimal code deeply type-checking items contained in nested subsequences (e.g., List[List[List[str]]]) with PEP 572-style assignment expressions -- the controversial syntactic change that would prompt Guido to voluntarily abdicate his ancestral leadership role as BDFL. Thanks to beartype 0.3.0, assignment expressions have finally proved their real-world utility. This optimization has been quantitatively profiled to generate type-checking code at least twice as fast as the equivalent code generated under Python < 3.8 in the case of triply-nested sequences. Since this performance gap only increases as the nesting level increases, a doubling of performance is only the minimum improvement observable under Python >= 3.8. Sequences more than triply-nested can expect a comparably dramatic speedup.
    • User-defined callables not accepting positional arguments. A local variable previously unconditionally defined in the bodies of all wrapper functions dynamically generated and returned by the @beartype.beartype decorator is now only conditionally defined in the bodies of wrapper functions wrapping user-defined callables accepting one or more positional arguments.
    • Wrapper function generation. Wrapper functions are now confined to internally access only the exact set of global attributes they specifically require rather than the complete set of such attributes accessible from the private beartype._decor.main submodule.

    Features Deprecated

    • None. You don't deprecate what ain't broke.

    Features Removed

    • None. You don't remove what's worth keepin'.

    Tests Improved

    • Deep type-checking. Exhaustive tests guaranteeing the sanity of deep type-checking of all supported combinations of typing sequences and typing unions have been added.
    • Human-readable exception messages on type-checking failures. Exhaustive tests guaranteeing the readability of these messages have also been added by iteratively matching these messages against iterables of arbitrary regular expressions expected to both match and not match these messages.

    Documentation Revised

    • Installation instructions, including new Gentoo-specific installation instructions courtesy the third-party raiagent overlay.

    API Changed

    • Nothing. Not publicly, anyway. Violate privacy encapsulation and the cuddly kitten gets it!

    (Spherical orthodontics adhere to a glib gibberings of orthopedic ornithopters!)

    Source code(tar.gz)
    Source code(zip)
Performant type-checking for python.

Pyre is a performant type checker for Python compliant with PEP 484. Pyre can analyze codebases with millions of lines of code incrementally – providi

Facebook 5.7k Nov 22, 2021
Inspects Python source files and provides information about type and location of classes, methods etc

prospector About Prospector is a tool to analyse Python code and output information about errors, potential problems, convention violations and comple

Python Code Quality Authority 1.5k Nov 24, 2021
A static type analyzer for Python code

pytype - ? ✔ Pytype checks and infers types for your Python code - without requiring type annotations. Pytype can: Lint plain Python code, flagging c

Google 3.5k Nov 23, 2021
Static type checker for Python

Static type checker for Python Speed Pyright is a fast type checker meant for large Python source bases. It can run in a “watch” mode and performs fas

Microsoft 7.4k Dec 2, 2021
Data parsing and validation using Python type hints

pydantic Data validation and settings management using Python type hinting. Fast and extensible, pydantic plays nicely with your linters/IDE/brain. De

Samuel Colvin 8.2k Dec 1, 2021
Run-time type checker for Python

This library provides run-time type checking for functions defined with PEP 484 argument (and return) type annotations. Four principal ways to do type

Alex Grönholm 765 Nov 25, 2021
Typical: Fast, simple, & correct data-validation using Python 3 typing.

typical: Python's Typing Toolkit Introduction Typical is a library devoted to runtime analysis, inference, validation, and enforcement of Python types

Sean 139 Nov 27, 2021
pycallgraph is a Python module that creates call graphs for Python programs.

Project Abandoned Many apologies. I've stopped maintaining this project due to personal time constraints. Blog post with more information. I'm happy t

gak 1.6k Nov 25, 2021
Turn your Python and Javascript code into DOT flowcharts

Notes from 2017 This is an older project which I am no longer working on. It was built before ES6 existed and before Python 3 had much usage. While it

Scott Rogowski 1.1k Nov 22, 2021
Find dead Python code

Vulture - Find dead code Vulture finds unused code in Python programs. This is useful for cleaning up and finding errors in large code bases. If you r

Jendrik Seipp 1.9k Nov 26, 2021
Code audit tool for python.

Pylama Code audit tool for Python and JavaScript. Pylama wraps these tools: pycodestyle (formerly pep8) © 2012-2013, Florent Xicluna; pydocstyle (form

Kirill Klenov 822 Nov 28, 2021
The strictest and most opinionated python linter ever!

wemake-python-styleguide Welcome to the strictest and most opinionated python linter ever. wemake-python-styleguide is actually a flake8 plugin with s

wemake.services 1.7k Nov 23, 2021
The uncompromising Python code formatter

The Uncompromising Code Formatter “Any color you like.” Black is the uncompromising Python code formatter. By using it, you agree to cede control over

Python Software Foundation 23.8k Dec 2, 2021
A Python utility / library to sort imports.

Read Latest Documentation - Browse GitHub Code Repository isort your imports, so you don't have to. isort is a Python utility / library to sort import

Python Code Quality Authority 4.3k Nov 24, 2021
A formatter for Python files

YAPF Introduction Most of the current formatters for Python --- e.g., autopep8, and pep8ify --- are made to remove lint errors from code. This has som

Google 12.2k Nov 24, 2021
Collection of library stubs for Python, with static types

typeshed About Typeshed contains external type annotations for the Python standard library and Python builtins, as well as third party packages as con

Python 2.5k Nov 24, 2021
Optional static typing for Python 3 and 2 (PEP 484)

Mypy: Optional Static Typing for Python Got a question? Join us on Gitter! We don't have a mailing list; but we are always happy to answer questions o

Python 11.9k Nov 25, 2021
A static analysis tool for Python

pyanalyze Pyanalyze is a tool for programmatically detecting common mistakes in Python code, such as references to undefined variables and some catego

Quora 146 Dec 1, 2021
Typing-toolbox for Python 3 _and_ 2.7 w.r.t. PEP 484.

Welcome to the pytypes project pytypes is a typing toolbox w.r.t. PEP 484 (PEP 526 on the road map, later also 544 if it gets accepted). Its main feat

Stefan Richthofer 163 Nov 21, 2021