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.
Comments
  • feat: adding math.TypeHint and is_subclass

    feat: adding math.TypeHint and is_subclass

    closes #133

    lots of things to be cleaned-up, documented, and discussed here. But opening a PR to discuss progress on this.

    You can see currently passing checks in test_is_subtype (not sure where I should be putting the test in the suite).

    Will ping you with more specific questions/topics when ready

    opened by tlambert03 27
  • [Documentation] Document @dataclass integration better

    [Documentation] Document @dataclass integration better

    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 27
  • Initial attempt a caching protocol implementation

    Initial attempt a caching protocol implementation

    Initial stab at porting numerary.types.CachingProtocolMeta to beartype. It only implements a naive cache that will grow indefinitely. (I.e., it does not include an LRU mechanism.) That can be addressed in a subsequent PR (before this one lands, if necessary). Further, this port does not include numerary's include/exclude mechanism, which should keep this implementation simpler.

    Eventually, this should fix posita/numerary#8.

    opened by posita 24
  • 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
  • [Upstream Bug] VSCode + Pylance + Pyright linter errors on

    [Upstream Bug] VSCode + Pylance + Pyright linter errors on "beartype.typing.TypeAlias"

    I get errors when trying to use the beartype.typing.TypeAlias instead of typing.TypeAlias, here are some of the error messages I get: Key: TypeAlias = str
    Expression of type "Type[str]" cannot be assigned to declared type "TypeAlias" "Type[type]" is incompatible with "Type[TypeAlias]"PylancereportGeneralTypeIssues

    CallbackFunction: TypeAlias = Callable[[Key], Any] Expression of type "(Unknown) -> Any" cannot be assigned to declared type "TypeAlias" "object" is incompatible with "TypeAlias"PylancereportGeneralTypeIssues

    def press(key: Key): print(f"'{key}' pressed") Expected class type but received "TypeAlias"PylancereportGeneralTypeIssues

    upstreamwontfix whatyougonnado 
    opened by rbroderi 17
  • 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
  • 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
  • Back-port caching protocol implementation to Python 3.7

    Back-port caching protocol implementation to Python 3.7

    #86 introduced a caching protocol implementation to beartype, but it was limited to Python>=3.8. It is theoretically possible to provide 3.7 support through typing_extensions. This PR attempts to achieve that possibility.

    opened by posita 15
  • 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 14
  • [Bug] Type-checking violations for lambda-style functional validators defined in IPython REPL omit source code

    [Bug] Type-checking violations for lambda-style functional validators defined in IPython REPL omit source code

    I really like the idea of functional validators. They make the source code much more robust while keeping it lean and clean. My only concern is that when the bear roars, the user has no idea what she/he did wrong. Probably, I am using beartype the wrong way.

    Using a simple type the user gets an kind of helpful warning, e.g.,

    >>> import numpy as np
    >>> from beartype import beartype
    >>> from beartype.vale import Is
    >>> from numpy.typing import NDArray
    >>> from typing_extensions import Annotated
    >>> @beartype
    ... def func(a: int) -> int:
    ...     return a
    ... 
    >>> 
    >>> func(5)
    5
    >>> func(5.)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<@beartype(__main__.func) at 0x7f6f2cb03940>", line 21, in func
      File "/home/user/anaconda3/envs/py3.8/lib/python3.8/site-packages/beartype/_decor/_error/errormain.py", line 301, in raise_pep_call_exception
        raise exception_cls(  # type: ignore[misc]
    beartype.roar.BeartypeCallHintParamViolation: @beartyped func() parameter a=5.0 violates type hint <class 'int'>, as 5.0 not instance of int.
    

    so it is clear, that an integer was expected. In this case it is trivial, but probably it would be helpful to also print the type of the input variable. So that it becomes clearer. Using a validator these error are getting very cryptic. Lets define three super handy validators.

    >>> Float2DArray = Annotated[
    ...     NDArray[np.floating],
    ...     Is[lambda arr: arr.ndim == 2],
    ... ]
    >>> FloatMatrix = Annotated[
    ...     Float2DArray,
    ...     Is[lambda arr: arr.shape[0] == arr.shape[1]],
    ... ]
    >>> SymmetricFloatMatrix = Annotated[
    ...     FloatMatrix,
    ...     Is[lambda arr: np.allclose(arr, arr.T)],
    ... ]
    >>> 
    >>> @beartype
    ... def func(arr: SymmetricFloatMatrix) -> SymmetricFloatMatrix:
    ...     return arr
    ...
    >>> 
    >>> # this runs fine
    >>> mat = np.zeros((4, 4))
    >>> func(mat)
    array([[0., 0., 0., 0.],
           [0., 0., 0., 0.],
           [0., 0., 0., 0.],
           [0., 0., 0., 0.]])
    

    So far, I am happy. But now parsing an array of wrong shape,

    >>> # not 2D
    >>> mat = np.zeros((4, ))
    >>> func(mat)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<@beartype(__main__.func) at 0x7f6f2f37e820>", line 53, in func
      File "/home/user/anaconda3/envs/py3.8/lib/python3.8/site-packages/beartype/_decor/_error/errormain.py", line 301, in raise_pep_call_exception
        raise exception_cls(  # type: ignore[misc]
    beartype.roar.BeartypeCallHintParamViolation: @beartyped func() parameter arr="array([0., 0., 0., 0.])" violates type hint typing_extensions.Annotated[numpy.ndarray, Is[<lambda>], Is[<lambda>], Is[<lambda>]], as "array([0., 0., 0., 0.])" violates validator Is[<lambda>]:
        False == Is[<lambda>].
    

    a non quadratic array (matrix), or

    >>> # not quadratic
    >>> mat = np.zeros((4, 5))
    >>> func(mat)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<@beartype(__main__.func) at 0x7f6f2f37e820>", line 53, in func
      File "/home/user/anaconda3/envs/py3.8/lib/python3.8/site-packages/beartype/_decor/_error/errormain.py", line 301, in raise_pep_call_exception
        raise exception_cls(  # type: ignore[misc]
    beartype.roar.BeartypeCallHintParamViolation: @beartyped func() parameter arr="array([[0., 0., 0., 0., 0.],
           [0., 0., 0., 0., 0.],
           [0., 0., 0., 0., 0.],
        ...[ violates type hint typing_extensions.Annotated[numpy.ndarray, Is[<lambda>], Is[<lambda>], Is[<lambda>]], as "array([[0., 0., 0., 0., 0.],
           [0., 0., 0., 0., 0.],
           [0., 0., 0., 0., 0.],
        ...[ violates validator Is[<lambda>]:
        False == Is[<lambda>].
    

    a non symmetric matrix,

    >>> # not symmetric
    >>> mat = np.zeros((4, 4))
    >>> mat[0, 1] = 2
    >>> func(mat)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<@beartype(__main__.func) at 0x7f6f2f37e820>", line 53, in func
      File "/home/user/anaconda3/envs/py3.8/lib/python3.8/site-packages/beartype/_decor/_error/errormain.py", line 301, in raise_pep_call_exception
        raise exception_cls(  # type: ignore[misc]
    beartype.roar.BeartypeCallHintParamViolation: @beartyped func() parameter arr="array([[0., 2., 0., 0.],
           [0., 0., 0., 0.],
           [0., 0., 0., 0.],
           [0., 0., 0... violates type hint typing_extensions.Annotated[numpy.ndarray, Is[<lambda>], Is[<lambda>], Is[<lambda>]], as "array([[0., 2., 0., 0.],
           [0., 0., 0., 0.],
           [0., 0., 0., 0.],
           [0., 0., 0... violates validator Is[<lambda>]:
        False == Is[<lambda>].
    

    In each case I am getting errors that some Is[<lambda>] are not fulfiled. If I remember correctly, older versions of beartype did print the lambda functions code. Using a wrong type, the error is more helpful, e.g.,

    >>> func(5)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<@beartype(__main__.func) at 0x7f6f2f37e820>", line 53, in func
      File "/home/user/anaconda3/envs/py3.8/lib/python3.8/site-packages/beartype/_decor/_error/errormain.py", line 301, in raise_pep_call_exception
        raise exception_cls(  # type: ignore[misc]
    beartype.roar.BeartypeCallHintParamViolation: @beartyped func() parameter arr=5 violates type hint typing_extensions.Annotated[numpy.ndarray, Is[<lambda>], Is[<lambda>], Is[<lambda>]], as 5 not instance of <protocol "numpy.ndarray">.
    

    I would love to see a list with requirements that the variable needs to fulfill. So something like:

    beartype.roar.BeartypeCallHintParamViolation: @beartyped func() parameter arr="array([[0., 2., 0., 0.],
           [0., 0., 0., 0.],
           [0., 0., 0., 0.],
           [0., 0., 0..
    violates type hint SymmetricFloatMatrix:
           - numpy.typing.NDArray[numpy.floating]
           - lambda arr: arr.ndim == 2
           - lambda arr: arr.shape[0] == arr.shape[1]
           - lambda arr: np.allclose(arr, arr.T)
    

    So back to the original question. Is is possible to improve readability of the errors?

    opened by braniii 13
  • [FAQ] Document how to disable @beartype

    [FAQ] Document how to disable @beartype

    For example, do we ever intend it to detect the issue in either of these:

    from beartype import beartype
    
    @beartype
    def fn(n: int) -> None:
        n = "waffle"
    
    @beartype
    def fn2() -> None: 
        n = 5
        n = "waffle"
    

    Or is that out of the scope of the project?

    Basically, I would like to be able to completely ditch mypy in favor of this project, and this is what's holding me back.

    opened by langfield 13
  • [Feature Request] Globally Suppress ANSI Sequences

    [Feature Request] Globally Suppress ANSI Sequences

    For applications, it would be nice if the color setting could be globally configurable by an environment variable. Related to #162 and #178

    Maybe export BEARTYPE_NO_COLOR=1 like better-exceptions docs and better_exceptions/color.py#L90

    os.getenv("BEARTYPE_NO_COLOR") or
    conf.is_color is False or
    (conf.is_color is None and not is_stdout_terminal())
    

    From: https://github.com/beartype/beartype/blob/308925d9e1b9e1e69886e0ed6904648612b01e7d/beartype/_decor/_error/_util/errorutilcolor.py#L179-L193

    The caveat is that this would be the solitary environment configuration and there might be a better way for beartype to support global configuration

    I have a particular issue with structlog "rich" exceptions printing the ANSI codes without formatting, but more generally there have been issues with the ANSI codes appearing in stdout

    opened by KyleKing 1
  • [Documentation]

    [Documentation] "`@beartype` + `pytest` ftw!!!" — Smart Guy @Jasha10

    Discussed in https://github.com/beartype/beartype/discussions/199

    Originally posted by Jasha10 December 18, 2022 The thesis here is that pytest tests and fixtures can (and should!) be decorated with @beartype. Doing so makes your tests robust against mis-typed fixtures and mis-typed @mark.parametrize decorators.

    Motivation

    Sometimes using the pytest framework is risky:

    from pytest import fixture
    
    @fixture
    def foo_int() -> int:
        return 123
    
    def test_foo(foo_int: str) -> None:
        assert foo_int == "abc"
    

    It's easy to see that the above test will fail since 123 != "abc". But can you spot the type error?

    $ pytest test_foo.py
    ...
    FAILED test_foo.py::test_foo - AssertionError: assert 123 == 'abc'
    1 failed in 0.05s
    

    Running static checkers like mypy and pyright on the above code will not produce any errors. ... But the foo_int argument passed to the test_foo function actually doesn't have type str, despite what the type annotation foo_int: str would lead you to believe. I guess static typing can't solve everything.

    Enter beartype!

    With the addition of a decorator, you can ensure that your mis-typed code will crash spectacularly:

    from beartype import beartype
    from pytest import fixture
    
    @fixture
    def foo_int() -> int:
        return 123
    
    @beartype  # Add decorator here!
    def test_foo(foo_int: str) -> None:
        assert foo_int == "abc"
    
    $ pytest test_foo.py -q
    ...
    E   beartype.roar.BeartypeCallHintParamViolation: @beartyped test_foo.test_foo() parameter foo_int=123 violates type hint <class 'str'>, as int 123 not instance of str.
    
    <@beartype(test_foo.test_foo) at 0x7f18eac17130>:22: BeartypeCallHintParamViolation
    ====================== short test summary info =======================
    FAILED test_foo.py::test_foo - beartype.roar.BeartypeCallHintParamViolation: @beartyped test_foo...
    1 failed in 0.10s
    

    The test doesn't run! We are immediately given a bear-typed slap on the wrist. The above diagnostic info is enough to piece together that the signature of test_foo is mistyped.

    What advantages are there to using @beartype with pytest? Why should I care whether my test fails with a BeartypeCallHintParamViolation or with a standard pytest AssertionError as in the first example above? Using @beartype, you can follow the Single Responsibility Principle in your test code. Your test functions' body needs only to worry about test logic, not about verifying that the fixtures or parametrize'd arguments are correctly-typed.

    Recipes:

    Beartype works with pytest tests:

    from beartype import beartype
    @beartype  # ensure `foo_int` *actually* has type `str`
    def test_foo(foo_int: str) -> None:
        assert foo_int == "abc"
    

    Beartype works with pytest fixtures:

    from beartype import beartype
    from pytest import fixture
    @fixture
    @beartype  # ensure the `foo_int` fixture *actually* returns an `int`
    def foo_int() -> int:
        return 123
    

    Beartype can make sure your @mark.parametrize arguments are up to snuff:

    from beartype import beartype
    from pytest import mark
    @mark.parametrize("arg", [1, 2, "three"])
    @beartype  # Ensure that `arg` *actually* has type `int`
    def test_small_ints(arg: int) -> None:
        assert arg < 4
    ```</div>
    opened by leycec 5
  • Feature Request: new Validator `beartype.vale.IsBearable[...]`

    Feature Request: new Validator `beartype.vale.IsBearable[...]`

    Would be nice to be able to use valid typehints that beartype understands via a declarative syntax, such that

    Numpy2DFloatArray = Annotated[np.ndarray, Is[lambda array:  # from readme
        array.ndim == 2 and np.issubdtype(array.dtype, np.floating)]]
    
    my_pytree = {'surprise':np.array([[1,2],[1,2]])}
     
    # this: 
    IsAttr['surprise', IsBearable[Numpy2DFloatArray]]
    
    # works the same-but-better-than
    IsAttr['surprise', Is[lambda x: is_bearable(x, Numpy2DFloatArray)]]
    
    # since this wont work: 
    IsAttr['surprise', IsInstance[...profit?]]
    

    This is great for all those now-all-too-common times normal isinstance-able stuffs fails, but beartype.door hums along happily.

    but...but wait! Isn't this exactly what the #192 plugin system will do!?

    I mean... yes? Ostensibly, the IsInstance thing will work once the protocol thing happens... right? the __mro_order__ is getting overwritten in time such that I assume the beartypish checks will be enforced by, say, subclasses? or compatible ducktypes? I don't really know anymore (where am I? what time is it?) :upside_down_face:

    But right now, without that beautiful plugin API, I'm hacking stuff together using functions that approximate parametric validators e.g.

    level1=lambda x,y: Annotated[x, IsAttr['inside', IsEqual[y]]]
    

    ..... and as soon as I nest these, I need to check via is_bearable:

    level2=lambda x,y: Annotated[x, IsAttr['super-inside', IsBearable[level1(x,y)]]]
    

    Yeah? Is this...even making sense anymore?

    Am I actually dead already? Is this purgatory?

    opened by tbsexton 3
  • [Documentation] Push `jaxtyping` over `torchtyping` for type-checking PyTorch tensors

    [Documentation] Push `jaxtyping` over `torchtyping` for type-checking PyTorch tensors

    Hi. I was trying to use beartype as a drop-in replacement for typeguard with torchtyping.

    from torch import rand
    from torchtyping import TensorType, patch_typeguard
    from typeguard import typechecked
    
    patch_typeguard() 
    
    @typechecked
    def func(x: TensorType["batch"],
             y: TensorType["batch"]) -> TensorType["batch"]:
        return x + y
    
    func(rand(3), rand(1))
    

    This raises the expected error.

    TypeError: Dimension 'batch' of inconsistent size. Got both 1 and 3.
    

    but it doesn't work. Should we manually write patch_beartype like patch_typeguard?

    from torch import rand
    from torchtyping import TensorType
    from beartype import beartype
    
    @beartype
    def func(x: TensorType["batch"],
             y: TensorType["batch"]) -> TensorType["batch"]:
        return x + y
    
    func(rand(3), rand(1))
    
    opened by junhsss 8
  • [Feature Request] Plugin API, Phase IV: The `__beartype_door__()` protocol

    [Feature Request] Plugin API, Phase IV: The `__beartype_door__()` protocol

    This feature request is the fourth and final milestone on the great journey to a first-class best-of-breed plugin architecture for @beartype. This milestone is orthogonal to both the second milestone (i.e., beartype.sign) and third milestone (i.e., __beartype_hint__()); that is, these three plugin milestones may be implemented in any order.

    In this phase, we introduce a new public __beartype_door__() protocol enabling users to associate arbitrary user-defined classes with arbitrary user-defined beartype.door.TypeHint subclasses. This is the ultimate power of @beartype plugins.

    A Brief History of Hibernating Bears

    Let's recap:

    • The proposed beartype.sign subpackage is mostly useful for customizing type-checking for type hints defined by other people (e.g., third-party NumPy type hints like numpy.typing.NDArray[...]).
    • The proposed __beartype_hint__() protocol is mostly useful for customizing type-checking for type hints defined by you (e.g., your dataclasses in your codebase).

    By compare, the __beartype_door__() protocol is mostly useful for customizing other things than type-checking. I mean, it should still be useful for customizing type-checking – but if all you need to do is that, you'll be better served by just leveraging the __beartype_hint__() protocol instead.

    So what "other things than type-checking are there"? Increasingly many, actually. The beartype.door.TypeHint class publishes a general-purpose type hint introspection framework. One popular use of this class is collating type hints (i.e., ordering type hints according to the subhint relation). By enabling users to associate arbitrary user-defined classes with arbitrary user-defined beartype.door.TypeHint subclasses, the __beartype_door__() protocol effectively enables users to control how those classes will collate against other classes and type hints.

    Downstream third-party packages are expected to eventually use the beartype.door.TypeHint API to implement their own business logic. For example, Plum intends to possibly use TypeHint to efficiently implement PEP-compliant multiple dispatch based on type hints. A TypeHint plugin system thus indirectly serves as a plugin system for those third-party packages, too.

    Show Me the QA Money

    Yet again, we easily define this protocol using the existing typing.Protocol machinery standardized by PEP 544:

    from beartype.door import TypeHint
    from beartype.typing import Any, Protocol
    
    def BeartypeDoorable(Protocol):
        @classmethod
        def __beartype_door__(cls) -> TypeHint:
            '''
            **Beartype type hint wrapper transform** (i.e., :mod:`beartype-specific
            dunder class method returning a new :class:`TypeHint` instance presumably
            augmented with additional class-specific behaviour, which the
            :class:`TypeHint` superclass will then use in place of the default
            :class:`TypeHint` instance for classes when wrapping this class).
            '''
    
            pass
    

    User-defined subclasses then subclass the BeartypeDoorable protocol as with the BeartypeHintable protocol introduced in the third milestone – but returning a full-blown custom beartype.door.TypeHint instance rather than a custom type hint. The sound of cats gleefully purring on your lap can now be distantly heard.

    Obligatory Shout-outs

    Praise be to @tbsexton, @langfield, and @antonagestam (of phantom-types fame)! Their words of wisdom at discussion #188: "Plugins, Protocols, and Unholy Algebras" and elsewhere strongly inspired both this and the comparable __beartype_hint__() protocol.

    @tbsexton: You'll probably prefer this one.

    @langfield: You'll probably prefer the __beartype_hint__() protocol, because it offers similar syntactic sugar for validating types as @antonagestam's phantom-types.

    @antonagestam: Ditto. :wink:

    Thanks so much for all the typing enthusiasm, everyone! Let's see if @leycec moves forward on these New Year's resolutions in 2023 or if he just keeps reading endless volumes of black-and-white Japanese manga from the 80's like usual.

    opened by leycec 1
  • [Feature Request] Plugin API, Phase III: The `__beartype_hint__()` protocol

    [Feature Request] Plugin API, Phase III: The `__beartype_hint__()` protocol

    This feature request is the third milestone on the great journey to a first-class best-of-breed plugin architecture for @beartype. This milestone is orthogonal to the second milestone (i.e., beartype.sign); that is, these two plugin milestones may be implemented in any order. Indeed, despite offering more powerful plugin machinery than the second milestone, this milestone is arguably simpler to implement and document. In other words, we might want to do this one first. universe: 0. @beartype: 1.

    In this phase, we introduce a new public __beartype_hint__() protocol enabling users to attach type hint transforms to arbitrary user-defined classes. Since type hint transforms include Turing-complete beartype validators (e.g., typing.Annotated[muh_class, beartype.vale.Is[muh_instance: bool(muh_instance)]]), this protocol effectively enables users to define arbitrary runtime type-checking. Plugin architecture, I summon thee! :mage_man:

    By default, @beartype type-checks any passed parameter or return value against a user-defined type cls with a trivial isinstance(obj, cls) test at runtime. Now, however, you'll be able to enhance how your classes are type-checked with additional constraint checks dynamically defined by you at runtime and (more importantly) permanently attached to those classes and all instances of those classes.

    Dig Deep Like Shovel Knight on a Digging Bender

    Let's dig deep into what exactly we're talking about here.

    Consider the following PEP 544-compliant protocol:

    from beartype import beartype
    from dataclasses import dataclass
    
    @beartype
    @dataclass
    def DoorsOfPerception(object):
        heaven_or_hell: Literal['heaven', 'hell']
        circle_of_hell: Optional[Literal[
            'limbo', 'lust', 'gluttony', 'greed', 'wrath',
            'heresy', 'violence', 'fraud', 'treachery',
        ]]
    

    @beartype deeply type-checks typing.Literal[...] type hints and thus validates the heaven_or_hell field as expected. But what about the circle_of_hell type hint? Clearly, Heaven has no Circles of Hell. Clearly. We thus want @beartype to validate that:

    • If heaven_or_hell == 'heaven', then circle_of_hell is None.
    • If heaven_or_hell == 'hell', then circle_of_hell in ('limbo', 'lust', 'gluttony', 'greed', 'wrath', 'heresy', 'violence', 'fraud', 'treachery').

    In other words, we want @beartype to type-check conditionally depending upon the contents of the DoorsOfPerception instance being type-checked. How can we do this depraved act of fiendish difficulty? Simple.

    Digging a Hole So Deep We Ended up on the Moon

    We use the __beartype_hint__() protocol to instruct @beartype to apply additional constraints when runtime type-checking.

    First, we easily define this protocol using the existing typing.Protocol machinery standardized by PEP 544:

    from beartype.typing import Any, Protocol
    
    def BeartypeHintable(Protocol):
        @classmethod
        def __beartype_hint__(cls) -> Any:
            '''
            **Beartype type hint transform** (i.e., :mod:`beartype-specific
            dunder class method returning a new PEP-compliant type hint
            constraining this class with additional runtime type-checking).
            '''
    
            pass
    

    Second, we augment our previously defined DoorsOfPerception dataclass with additional runtime type-checking defined via this protocol:

    from beartype import BeartypeHintable, beartype
    from beartype.typing import Any, Annotated
    from beartype.vale import Is, IsAttr, IsEqual
    from dataclasses import dataclass
    
    @beartype
    @dataclass
    def DoorsOfPerception(BeartypeHintable):
        heaven_or_hell: Literal['heaven', 'hell']
        circle_of_hell: Optional[Literal[
            'limbo', 'lust', 'gluttony', 'greed', 'wrath',
            'heresy', 'violence', 'fraud', 'treachery',
        ]]
    
        @classmethod
        def __beartype_hint__(cls) -> Any:
            return Annotated[cls,
                IsAttr['heaven_or_hell',
                    (IsEqual['heaven'] & IsAttr['circle_of_hell',  IsEqual[None]]) |
                    (IsEqual['hell'  ] & IsAttr['circle_of_hell', ~IsEqual[None]])
                ]
            ]
    

    Done and done. @beartype now detects that DoorsOfPerception satisfies the BeartypeHintable protocol and implicitly applies the additional conditional constraints defined by the __beartype_hint__() dunder method when type-checking instances of that dataclass at runtime.

    Shorthand for Shorties

    Okay. Everything is great, but it's also a bit long-winded. Can we improvise some sort of convenient syntactic sugar that elides away all of the boilerplate implied by actual real-world usage of the BeartypeHintable protocol?

    Yes. Yes, we can. Consider this terse shorthand for implementing the exact same dataclass as implemented above – but with far fewer lines of DRY-violating scaffolding:

    from beartype import beartype
    from beartype.typing import Any, Annotated
    from beartype.vale import Is, IsAttr, IsEqual
    from dataclasses import dataclass
    
    @beartype
    @dataclass
    def DoorsOfPerception(IsAttr['heaven_or_hell',
        (IsEqual['heaven'] & IsAttr['circle_of_hell',  IsEqual[None]]) |
        (IsEqual['hell'  ] & IsAttr['circle_of_hell', ~IsEqual[None]])
    ]):
        heaven_or_hell: Literal['heaven', 'hell']
        circle_of_hell: Optional[Literal[
            'limbo', 'lust', 'gluttony', 'greed', 'wrath',
            'heresy', 'violence', 'fraud', 'treachery',
        ]]
    

    ...wut u say!?!?

    So. The idea here is that beartype validators (i.e., public beartype.vale.Is*[...] constraint factories) can be generalized to be subclassable. When subclassed, beartype validators will automatically:

    • Replace themselves in the method order resolution (MRO) (__mro__) of the class being subclassed with the beartype.BeartypeHintable protocol.
    • Dynamically monkey-patch the class being subclassed by adding an appropriate __beartype_hint__() dunder classmethod to that class.

    You are possibly now muttering to yourself in the candle-lit darkness of your own peculiar (wo)mancave: "B-b-but... that's impossible!" Actually, it's all too possible; I used to hate these sorts of maleficent shenanigans, because the typing module does exactly this sort of thing all over the place. PEP 484 refers to this decadent behaviour as "type erasure."

    In fact, this sleight-of-hand was standardized over five years ago by PEP 560 via the mostly undocumented __mro_entries__() dunder method. By abusing __mro_entries__(), any type (including the type of beartype validators) can transparently replace itself on-the-fly with any other arbitrary type(s) in the MRO of subclasses.

    In this case, the type of beartype validators will define a trivial __mro_entries__() that just replaces itself with BeartypeHintable when subclassed. Darkness: my old friend.

    \o/

    This syntactic sugar is strongly inspired by @antonagestam's wondrous phantom-types, which applies a similar syntax to similar great effect (albeit implemented in a completely different low-level way with __instancecheck__() dunder metaclass methods, which itself is endlessly fascinating). Thanks so much, @antonagestam!

    That's Just How We Roll in 2023

    In theory, the BeartypeHintable protocol should suffice to provide a fully-complete plugin architecture. The __beartype_hint__() dunder method can be implemented to return Turing-complete beartype validators capable of doing literally anything including bad stuff but let's ignore that (i.e., typing.Annotated[{user_type}, beartype.vale.Is[{plugin_func}]]).

    Users: commence rejoicing once @leycec actually does something about this.

    opened by leycec 5
Releases(v0.11.0)
  • v0.11.0(Sep 18, 2022)

    Beartype 0.11.0 released.

    This minor release unleashes a major firestorm of support for class decoration, colourful exceptions, pyright + PyLance + VSCode, the Decidedly Object-Orientedly Recursive (DOOR) API, the Python Enhancement Proposals (PEPs) API, PEP 484, PEP 544, PEP 561, PEP 563, PEP 585, PEP 604, PEP 612, and PEP 647.

    This minor release resolves a mammoth 29 issues and merges 12 pull requests. Noteworthy changes include:

    Compatibility Improved

    • Class decoration. The @beartype decorator now decorates both higher-level classes and lower-level callables (i.e., functions, methods), resolving feature request #152 kindly submitted by @posita the positively sublime. All possible edge cases are supported, including:
      • Classes defining methods decorated by builtin decorators: i.e.,
        • Class methods via @classmethod.
        • Static methods via @staticmethod.
        • Property getters, setters, and deleters via @property.
      • Arbitrarily deeply nested (i.e., inner) classes.
      • Arbitrarily deeply nested (i.e., inner) classes whose type hints are postponed under PEP 563. Since this was surprisingly trivial, @leycec probably should have done this a few years ago. He didn't. This is why he laments into his oatmeal in late 2022.
    • PEP 484- and PEP 585-compliant nested generics. @beartype now supports arbitrarily complex PEP 484- and PEP 585-compliant inheritance trees subclassing non-trivial combinations of the typing.Generic superclass and other typing pseudo-superclasses, resolving issue #140 kindly submitted by @langfield (William Blake – yes, that William Blake). Notably, this release extricated our transitive visitation of the tree of all pseudo-superclasses of any PEP 484- and 585-compliant generic type hint (...don't ask) from its prior hidden sacred cave deep within the private beartype._decor._code._pep._pephint submodule into a new reusable iter_hint_pep484585_generic_bases_unerased_tree() generator, which is now believed to be the most fully-compliant algorithm for traversing generic inheritance trees at runtime. This cleanly resolved all lingering issues surrounding generics, dramatically reduced the likelihood of more issues surrounding generics, and streamlined the resolution of any more issues surrounding generics should they arise... which they won't. Generics: we have resoundingly beaten you. Stay down, please.
    • PEP 544 compatibility. @beartype now supports arbitrarily complex PEP 544-compliant inheritance trees subclassing non-trivial combinations of the typing.Protocol + abc.ABC superclasses, resolving #117 kindly submitted by too-entertaining pun master @twoertwein (Torsten Wörtwein). Notably, @beartype now:
      • Correctly detects non-protocols as non-protocols. Previously, @beartype erroneously detected a subset of non-protocols as PEP 544-compliant protocols. It's best not to ask why.
      • Ignores both the unsubscripted beartype.typing.Protocol superclass and parametrizations of that superclass by one or more type variables (e.g., beartype.typing.Protocol[typing.TypeVar('T')]) as semantically meaningless in accordance with similar treatment of the typing.Protocol superclass.
      • Permits caller-defined abstract protocols subclassing our caching beartype.typing.Protocol superclass to themselves be subclassed by one or more concrete subclasses. Previously, attempting to do so would raise non-human-readable exceptions from the typing module; now, doing so behaves as expected.
      • Relaxed our prior bad assumption that the second-to-last superclass of all generics – and thus protocols – is the typing.Generic superclass. That assumption only holds for standard generics and protocols; non-standard protocols subclassing non-typing superclasses (e.g., the abc.ABC superclass) after the list typing superclass in their method resolution order (MRO) flagrantly violate this assumption. Well, that's fine. We're fine with that. What's not fine about that? Fine. This is fine.
      • Avoids a circular import dependency. Previously, our caching beartype.typing.Protocol superclass leveraged the general-purpose @beartype._util.cache.utilcachecall.callable_cached decorator to memoize its subscription; however, since that decorator transitively imports from the beartype.typing subpackage, doing so induced a circular import dependency. To circumvent this, a new @beartype.typing._typingcache.callable_cached_minimal decorator implementing only the minimal subset of the full @beartype._util.cache.utilcachecall.callable_cached decorator has been defined; the beartype.typing subpackage now safely defers to this minimal variant for all its caching needs.
    • PEP 563 compatibility. @beartype now resolves PEP 563-postponed self-referential type hints (i.e., type hints circularly referring to the class currently being decorated). Caveat: this support requires that external callers decorate the class being referred to (rather than the method doing the referring) by the @beartype decorator. For this and similar reasons, users are advised to begin refactoring their object-oriented codebases to decorate their classes rather than methods with @beartype.
    • PEP 612 partial shallow compatibility. @beartype now shallowly detects PEP 612-compliant typing.ParamSpec objects by internally associating such objects with our beartype._data.hint.pep.sign.datapepsigns.HintSignParamSpec singleton, enabling @beartype to portably introspect Callable[typing.ParamSpec(...), ...] type hints.
    • Static type-checking. @beartype is now substantially more compliant with static type-checkers, including:
      • Microsoft pyright + PyLance + VSCode. @beartype now officially supports pyright, Microsoft's in-house static type-checker oddly implemented in pure-TypeScript, gulp resolving issues #126 and #127 kindly submitted by fellow Zelda aficionado @rbroderi. Specifically, this release resolves several hundred false warnings and errors issued by pyright against the @beartype codebase. It is, indeed, dangerous to go alone – but we did it anyway.
      • mypy beartype.typing.Protocol compatibility. The @beartype-specific beartype.typing.Protocol superclass implementing PEP 544-compliant fast caching protocols is now fully compatible with mypy, Python's official static type-checker. Specifically, beartype.typing.Protocol now circumvents:
        • python/mypy#11013 by explicitly annotating the type of its __slots__ as Any.
        • python/mypy#9282 by explicitly setting the typing.TypeVar() bounds parameter to this superclass.
    • PEP 647 compatibility. @beartype now supports arbitrarily complex type narrowing in PEP 647-compliant static type-checkers (e.g., mypy, pyright), resolving issues #164 and #165 kindly submitted in parallel by foxy machine learning gurus @justinchuby (Justin Chuby) and @rsokl (Ryan Soklaski). Thanks to their earnest dedication, @beartype is now believed to be the most fully complete type narrower. Specifically, the return of both the beartype.door.is_bearable() function and corresponding beartype.door.TypeHint.is_bearable() method are now annotated by the PEP 647-compliant typing.TypeGuard[...] type hint under both Python ≥ 3.10 and Python < 3.10 when the optional third-party typing_extensions dependency is installed. Doing so substantially reduces false positives from static type checkers on downstream codebases deferring to these callables. Thanks so much for improving @beartype so much, @justinchuby and @rsokl!
    • @{classmethod,staticmethod,property} chaining. The @beartype decorator now implicitly supports callables decorated by both @beartype and one of the builtin method decorators @classmethod, @staticmethod, or @property regardless of decoration order, resolving issue #80 kindly requested by @qiujiangkun (AKA, Type Genius-kun). Previously, @beartype explicitly raised an exception when ordered after one of those builtin method decorators. This releseae relaxes this constraint, enabling callers to list @beartype either before or after one of those builtin method decorators.
    • beartype.vale.Is[...] integration. Functional validators (i.e., beartype.vale.Is[...]) now integrate more cleanly with the remainder of the Python ecosystem, including:
      • IPython. Functional validators localized to a sufficiently intelligent REPL (e.g., IPython) that caches locally defined callables to the standard linecache module now raise human-readable errors on type-checking, resolving issue #123 kindly submitted by typing brain-child @braniii. Relatedly, @beartype now permissively accepts both physical on-disk files and dynamic in-memory fake files cached with linecache as the files defining an arbitrary callable.
      • NumPy, which publishes various bool-like tester functions (i.e., functions returning a non-bool object whose class defines at least one of the __bool__() or __len__() dunder methods and is thus implicitly convertible into a bool). Functional validators now support subscription by these functions, resolving issue #153 kindly submitted by molecular luminary @braniii (Daniel Nagel). Specifically, @beartype now unconditionally wraps all tester callables subscripting (indexing) beartype.vale.Is with a new private _is_valid_bool() closure that (in order):
        1. Detects when those tester callables return bool-like objects.
        2. Coerces those objects into corresponding bool values.
        3. Returns those bool values instead.
    • Moar fake builtin types.@beartype now detects all known fake builtin types (i.e., C-based types falsely advertising themselves as being builtin and thus not require explicit importation), succinctly resolving issue #158 kindly submitted by the decorous typing gentleman @langfield. Specifically, @beartype now recognizes instances of all of the following as fake builtin types:
      • beartype.cave.AsyncCoroutineCType.
      • beartype.cave.AsyncGeneratorCType.
      • beartype.cave.CallableCodeObjectType.
      • beartype.cave.CallableFrameType.
      • beartype.cave.ClassDictType.
      • beartype.cave.ClassType.
      • beartype.cave.ClosureVarCellType.
      • beartype.cave.EllipsisType.
      • beartype.cave.ExceptionTracebackType.
      • beartype.cave.FunctionType.
      • beartype.cave.FunctionOrMethodCType.
      • beartype.cave.GeneratorCType.
      • beartype.cave.MethodBoundInstanceDunderCType.
      • beartype.cave.MethodBoundInstanceOrClassType.
      • beartype.cave.MethodDecoratorBuiltinTypes.
      • beartype.cave.MethodUnboundClassCType.
      • beartype.cave.MethodUnboundInstanceDunderCType.
      • beartype.cave.MethodUnboundInstanceNondunderCType.
      • beartype.cave.MethodUnboundPropertyNontrivialCExtensionType.
      • beartype.cave.MethodUnboundPropertyTrivialCExtensionType.

    Compatibility Broken

    • Python 3.6.x support dropped. This release unilaterally drops support for the Python 3.6.x series, which somnambulantly collided with its End-of-Life (EOL) a year ago and now constitutes a compelling security risk. Doing so substantially streamlines the codebase, whose support for Python 3.6.x required an unmaintainable writhing nest of wicked corner cases. We all now breathe a sigh of contentment in the temporary stillness of morning.
    • beartype.cave deprecation removals. This release removes all deprecated third-party attributes from the beartype.cave submodule. The continued existence of these attributes substantially increased the cost of importing anything from our mostly undocumented beartype.cave submodule, rendering that submodule even less useful than it already is. Specifically, this release removes these previously deprecated attributes:
      • beartype.cave.NumpyArrayType.
      • beartype.cave.NumpyScalarType.
      • beartype.cave.SequenceOrNumpyArrayTypes.
      • beartype.cave.SequenceMutableOrNumpyArrayTypes.
      • beartype.cave.SetuptoolsVersionTypes.
      • beartype.cave.VersionComparableTypes.
      • beartype.cave.VersionTypes.

    Exceptions Improved

    • Colour – the sensation formerly known as "color." @beartype now emits colourized type-checking violations (i.e., beartype.roar.BeartypeCallHintViolation exceptions) raised by both @beartype-decorated callables and statement-level type-checkers (e.g., beartype.door.die_if_unbearable(), beartype.door.TypeHint.die_if_unbearable()), resolving issue #161 kindly submitted by foxy machine learning expert @justinchuby (Justin Chu). When standard output is attached to an interactive terminal (TTY), ANSII-flavoured colours now syntactically highlight various substrings of those violations for improved visibility, readability, and debuggability. Since all actively maintained versions of Windows (i.e., Windows ≥ 10) now widely support ANSII escape sequences across both Microsoft-managed terminals (e.g., Windows Terminal) and Microsoft-managed Integrated Development Environments (IDEs) (e.g., VSCode), this supports extends to Windows as well. The bad old days of non-standard behaviour are behind us all. Thanks so much to @justinchuby for his immense contribution to the righteous cause of eye-pleasing user experience (UX)!
    • Types disambiguated. @beartype now explicitly disambiguates the types of parameters and returns that violate type-checking in exception messages raised by the @beartype decorator, resolving issue #124 kindly submitted by typing brain-child @braniii. Thus was justice restored to the QAverse.
    • Stack frame squelched. @beartype now intentionally squelches (i.e., hides) the ignorable stack frame encapsulating the call to our private beartype._decor._error.errormain.get_beartype_violation() getter from the parent type-checking wrapper function generated by the :mod:beartype.beartype decorator, resolving issue #140 kindly submitted by @langfield (William Blake – yes, that William Blake). That stack frame only needlessly complicated visual inspection of type-checking violations in tracebacks – especially from testing frameworks like :mod:pytest that recapitulate the full definition of the get_beartype_violation() getter (including verbose docstring) in those tracebacks. Specifically, this release:
      • Renamed the poorly named raise_pep_call_exception() function to get_beartype_violation() for clarity.
      • Refactored get_beartype_violation() to return rather than raise BeartypeCallHintViolation exceptions (while still raising all other types of unexpected exceptions for robustness).
      • Refactored type-checking wrapper functions to directly raise the exception returned by calling get_beartype_violation().
    • None type. The type of the None singleton is no longer erroneously labelled as a PEP 544-compliant protocol in type-checking violations. Let's pretend that never happened.
    • beartype.abby.die_if_unbearable() violations. The beartype.abby.die_if_unbearable() validator function no longer raises non-human-readable exception messages prefixed by the unexpected substring "@beartyped beartype.abby._abbytest._get_type_checker._die_if_unbearable() return". "Surely that never happened, @beartype!"

    Features Added

    • beartype.door. @beartype now provides a new public framework for introspecting, sorting, and type-checking type hints at runtime in constant time. N-n-now... hear me out here. @leycec came up with a ludicrous acronym and we're going to have to learn to live with it: the Decidedly Object-Orientedly Recursive (DOOR) API. Or, beartype.door for short. Open the door to a whole new type-hinting world, everyone. beartype.door enables type hint arithmetic via an object-oriented type hint class hierarchy encapsulating the crude non-object-oriented type hint declarative API standardized by the typing module, resolving issues #133 and 138 kindly submitted by Harvard microscopist and general genius @tlambert03. The new beartype.door subpackage defines a public:
      • TypeHint({type_hint}) superclass, enabling rich comparisons between pairs of arbitrary type hints. Altogether, this class implements a partial ordering over the countably infinite set of all type hints. Pedagogical excitement ensues. Instances of this class efficiently satisfy both the collections.abc.Sequence and collections.abc.FrozenSet abstract base classes (ABC) and thus behave just like tuples and frozen sets over child type hints. Public attributes defined by this class include:
        • A pair of die_if_unbearable() and is_bearable() runtime type-checking methods, analogous in behaviour to the existing beartype.abby.die_if_unbearable() and beartype.abby.is_bearable() runtime type-checking functions.
        • TypeHint.is_bearable(), currently implemented in terms of the procedural beartype.abby.is_bearable() tester.
        • An is_ignorable property evaluating to True only if the current type hint is semantically ignorable (e.g., object, typing.Any). There exist a countably infinite number of semantically ignorable type hints. The more you know, the less you want to read this changeset.
        • The equality comparison operator (e.g., ==), enabling type hints to be compared according to semantic equivalence.
        • Rich comparison operators (e.g., <=, >), enabling type hints to be compared and sorted according to semantic narrowing.
        • A sane __bool__() dunder method, enabling type hint wrappers to be trivially evaluated as booleans according to the child type hints subscripting the wrapped type hints.
        • A sane __len__() dunder method, enabling type hint wrappers to be trivially sized according to the child type hints subscripting the wrapped type hints.
        • A sane __contains__() dunder method, enabling type hint wrappers to be tested for child type hint membership – just like builtin sets, frozen sets, and dictionaries.
        • A sane __getindex__() dunder method, enabling type hint wrappers to be subscripted by both positive and negative indices as well as slices of such indices – just like builtin tuples.
      • beartype.door.AnnotatedTypeHint subclass.
      • beartype.door.CallableTypeHint subclass.
      • beartype.door.LiteralTypeHint subclass.
      • beartype.door.NewTypeTypeHint subclass.
      • beartype.door.TupleTypeHint subclass.
      • beartype.door.TypeVarTypeHint subclass.
      • beartype.door.UnionTypeHint subclass.
      • is_subtype({type_hint_a}, {type_hint_b}) function, enabling @beartype users to decide whether any type hint is a subtype (i.e., narrower type hint) of any other type hint.
      • beartype.roar.BeartypeDoorNonpepException type, raised when the beartype.door.TypeHint constructor is passed an object that is not a PEP-compliant type hint currently supported by the DOOR API. Thanks so much to @tlambert03 for his phenomenal work here. He ran GitHub's PR gauntlet so that you did not have to. Praise be to him. Some people are the living embodiment of quality. @tlambert03 is one such people.
    • beartype.peps. @beartype now publicizes runtime support for typing-centric Python Enhancement Proposals (PEPs) that currently lack official runtime support via a new public subpackage: beartype.peps. Notably, @beartype now provides:
      • A new public beartype.peps.resolve_pep563() function resolving PEP 563-postponed type hints on behalf of third-party Python packages. This function is intended to be "the final word" on runtime resolution of PEP 563. May no other third-party package suffer as we have suffered. This commit is for you, everyone. And "by everyone," we of course mostly mean @wesselb of Plum fame. See also wesselb/plum#53.
    • beartype.vale.Is*[...] {&,|} short-circuiting. &- and |-chained beartype validators now explicitly short-circuit when raising human-readable exceptions from type-checking violations against those validators, resolving issue #125 kindly submitted by typing brain-child @braniii.

    Features Optimized

    • beartype.abby.is_bearable() when returning False. Previously, the public beartype.abby.is_bearable() runtime type-checker behaved reasonably optimally when the passed object satisfied the passed type hint but extremely suboptimally when that object violated that hint; this was due to our current naive implementation of that tester using the standard Easier to Ask for Permission than Forgiveness (EAFP) approach. This release fundamentally refactored beartype.abby.is_bearable() in terms of our new private beartype._check.checkmake.make_func_tester() type-checking tester function factory function. Ad-hoc profiling shows a speedup on the order of eight orders of magnitude – the single most intense optimization @beartype has ever brought to bear (heh). Our core code generation API now transparently generates both:
      • Runtime type-checking testers (i.e., functions merely returning False on type-checking violations).
      • Runtime type-checking validators (i.e., functions raising exceptions on type-checking violations).
    • PEP 604-compliant new unions (e.g., int | str | None). Since these unions are non-self-caching type hints (i.e., hints that do not implicitly cache themselves to reduce space and time consumption), @beartype now efficiently coerces these unions into singletons in the same manner as PEP 585-compliant type hints – which are similarly non-self-caching.

    Features Deprecated

    • beartype.abbybeartype.door. This release officially deprecates the poorly named beartype.abby subpackage in favour of the sorta less poorly named beartype.door subpackage, whose name actually means something – even if that something is a punny acronym no one will ever find funny. Specifically:
      • beartype.abby.die_if_unbearable() has been moved to beartype.door.die_if_unbearable().
      • beartype.abby.is_bearable() has been moved to beartype.door.is_bearable(). To preserve backward compatibility, the beartype.abby subpackage continues to dynamically exist (and thus be importable from) – albeit as a deprecated alias of the beartype.door subpackage.

    Deprecations Resolved

    • Setuptools licensing. This release resolves a mostly negligible setuptools deprecation warning concerning the deprecated license_file setting in the top-level setup.cfg file. Next!

    Tests Improved

    • PEP 544 compatibility. All PEP 544-specific test type hints have been generalized to apply to both the non-caching typing.Protocol superclass and our caching beartype.typing.Protocol superclass.
    • PEP 561 compatibility via pyright. Our test suite now enforces static type-checking with pyright. Notably:
      • A new test_pep561_pyright functional test statically type-checks the @beartype codebase against the external pyright command in the current ${PATH} (if available) specific to the version of the active Python interpreter currently being tested. For personal sanity, this test is currently ignored on remote continuous integration (CI) workflows. Let this shrieking demon finally die!
      • The private beartype_test.util.cmd.pytcmdrun submodule underlying our cross-platform portable forking of testing subprocesses now transparently supports vanilla Windows shells (e.g., CMD.exe, PowerShell).
    • Tarball compatibility. beartype may now be fully tested from non-git repositories, including source tarballs containing the beartype_test package. Previously, three functional tests making inappropriate assumptions about the existence of a top-level .git/ directory failed when exercised from a source tarball.
    • Sphinx documentation. Our test suite now exercises that our documentation successfully builds with Sphinx via a new test_sphinx_build() functional test. This was surprisingly non-trivial – thanks to the pytest-specific sphinx.testing subpackage being mostly undocumented, behaving non-orthogonally, and suffering a host of unresolved issues that required we monkey-patch the core pathlib.Path class. Insanity, thy name is Sphinx.
    • GitHub Actions dependencies bumped. This release bumps our GitHub Actions-based continuous integration (CI) workflows to both the recently released checkout@v3 and setup-python@v3 actions, inspired by a pair of sadly closed PRs by @RotekHandelsGmbH CTO @bitranox (Robert Nowotny). Thanks so much for the great idea, @bitranox!
    • beartype.door conformance. A new smoke test guarantees conformance between our DOOR API and abstract base classes (ABCs) published by the standard typing module.
    • python/mypy#13627 circumvention. This release pins our GitHub Actions-based CI workflow to Python 3.10.6 rather than 3.10.7, resolving a mypy-specific complaint inducing spurious test failures.

    Documentation Improved

    • beartype.abby documented. The new "Beartype At Any Time API" subsection of our front-facing README.rst file now documents our public beartype.abby API, resolving issue #139 kindly submitted by @gelatinouscube42 (i.e., the user whose username is the answer to the question: "What is the meaning of collagen sustainably harvested from animal body parts?").

    • GitHub Sponsors activated. @beartype is now proudly financially supported by GitHub Sponsors. Specifically, this release:

      • Defines a new GitHub-specific funding configuration (i.e., .github/FUNDING.yml).
      • Injects a hopefully non-intrusive advertising template gulp at the head of our README.rst documentation.
    • Sphinx configuration sanitized. As the first tentative step towards chain refactoring our documentation from its current monolithic home in our top-level README.rst file to its eventual modular home at ReadTheDocs (RTD), en-route to resolving issue #8 (!) kindly submitted a literal lifetime ago by visionary computer vision export and long-standing phenomenal Finn @felix-hilden (Felix Hildén):

      • Our core Sphinx configuration has been resurrected from its early grave – which now actually builds nothing without raising errors. Is this an accomplishment? In 2022, mere survival is an accomplishment! So... yes. Significant improvements include:
        • Activation and configuration of the effectively mandatory autosectionlabels builtin Sphinx extension.
      • Our doc/source/404.rst file has been temporarily moved aside, resolving a non-fatal warning pertaining to that file. Look, we're not here to actually solve deep issues; we're here to just get documentation building, which it's not. Sphinx, you have much to answer for.
      • Our top-level sphinx entry point now:
        • Temporarily disables Sphinx's nit-picky mode (i.e., the -n option previously passed to sphinx-build) due to Sphinx's autodoc extension locally failing to generate working references.
        • Unconditionally disables Sphinx caching by forcing all target documentation files to be rebuilt regardless of whether their underlying source files have since been modified or not, obviating spurious build issues.

    (Impossible journey on an implacable placard-studded gurney!)

    Source code(tar.gz)
    Source code(zip)
  • v0.10.4(Mar 15, 2022)

    Beartype 0.10.4 released.

    This patch release adumbrates with breathless support for mypy ≥ 0.940, the static type checker formerly known as "The Static Type Checker Whose Name Shall not Be Spoken."

    This patch release resolves 5 issues and merges 0 pull requests. Noteworthy changes include:

    Compatibility Improved

    • mypy ≥ 0.940. The beartype codebase now sports improved compatibility with the recently released mypy 0.94x series, which previously outted the @beartype decorator with a "Condition can't be inferred, unable to merge overloads [misc]" fatal error at static type-checking time. Specifically, this release fundamentally refactors (and in so doing mildly optimizes) our private beartype._decor.main submodule to leverage conditional overloads under the astute tutelage of mypy maestro @cdce8p; the @beartype.beartype decorator itself now resides in a new private beartype._decor.cache.cachedecor submodule, because obfuscation is the key to all successful open-source efforts. Doing so resolves issues #111 and #112 dual-reported concurrently by cutting-edge Microsoft luminary @daxpryce and German typing bad-ass @twoertwein.

    Features Optimized

    • Importation time. The first external importation from the beartype codebase is now significantly faster when the definition of the @beartype.beartype decorator reduces to a noop (e.g., due to python3 -O optimization), partially resolving issue #94 kindly requested by the well-tanned and -toned typing star @matanster.

    Issues Resolved

    • beartype.abby under python3 -O. This release resolves an unreported critical defect in our new functional API (i.e., the pair of beartype.abby.is_bearable() and beartype.abby.die_if_unbearable() functions), which previously reduced to a noop when the @beartype.beartype decorator reduced to a noop (e.g., due to python3 -O optimization). By extricating the @beartype.beartype decorator into the beartype._decor.cache.cachedecor submodule (as described above), our functional API now directly defers to that decorator regardless of what the beartype package externally presents to third-party code.

    (Ironwrought irony untaught!)

    Source code(tar.gz)
    Source code(zip)
  • v0.10.3(Mar 11, 2022)

    Beartype 0.10.3 released.

    This patch release positively vibrates with superlative support for functional beartype validators (i.e., beartype.vale.Is[...]).

    This patch release resolves 5 issues and merges 2 pull requests. Noteworthy changes include:

    Issues Resolved

    • beartype.vale.Is[@beartype(...)].** The functional beartype validator API (i.e., the beartype.vale.Is[...] factory) now permissively accepts any low-level callable accepting one parameter wrapped by a higher-level callable, resolving issue #104 kindly submitted by the munificent typing maestro @dycw (Derek Wan). Specifically, the beartype.vale.Is[...] factory may now be subscripted (indexed) with any validation function wrapped by a decorator wrapper wrapped by the standard @functools.wraps decorator, including any @beartype-decorated validation function. Thus is the circle of validation complete. Cue Hakuna Matata.
    • setup.py circularity. Our top-level beartype.__init__ submodule no longer implicitly imports from any beartype submodule (except the guaranteeably safe beartype.meta submodule) when imported at install time by our root setup.py script, resolving issue #108 kindly discovered by @posita in a distressing comment embedded within the murky depths of PR #103. Specifically, setup.py now dynamically populates the standard sys.modules list with a fake beartype.__is_installing__ "module;" beartype.__init__ then detects the presence of that "module" and avoids implicitly importing from unsafe beartype submodules. In short: insane hackery. That's just how we roll.

    Tests Improved

    • Import isolation. The test_package_import_isolation() integration test minimizing importation costs by ensuring that the first import of the top-level lightweight :mod:beartype package does not accidentally import from one or more heavyweight third-party packages now omits the third-party typing_extensions module from scrutiny. Thanks a bundle of crypto that I do not have to @posita for his deep profiling sacrifice at #103! @posita: still dah best in 2022.

    (Jubilant sibilance entranced a sigil's brilliance!)

    Source code(tar.gz)
    Source code(zip)
  • v0.10.2(Feb 22, 2022)

    Beartype 0.10.2 released.

    This patch release serves up salacious support for improved compliance with PEP 3102 -- Python Keyword-Only Parameters.

    This patch release resolves 1 issue and merges 0 pull request. it is sad Noteworthy changes include:

    Compatibility Improved

    • PEP 3102 -- Python Keyword-Only Parameters, resolving issue #102 kindly submitted by stalwart parameter-passing quarterback @dycw (Derek Wan). Previously, @beartype only supported optional keyword-only parameters strictly preceding mandatory keyword-only parameters in callable signatures. Now, @beartype supports callables whose optional and mandatory keyword-only parameters are heterogeneously mixed in any arbitrary order in callable signatures.

    Features Optimized

    • Argument parsing. @beartype now parses callable signatures significantly faster, thanks to abandoning the extremely inefficient (albeit well-tested) inspect.signature() standard parser in favour of our extremely efficient (albeit not so well-tested) beartype._util.func.arg.utilfuncargiter.iter_func_args home-grown parser -- now believed to be the fastest pure-Python argument parser. Doing so significantly optimizes @beartype at decoration time. While still comparatively slow, the @beartype decorator is now within two orders of magnitude of the fastest possible decorator (i.e., noop identity decorator).

    Source code(tar.gz)
    Source code(zip)
  • v0.10.1(Feb 19, 2022)

    Beartype 0.10.1 released.

    This patch release cooks up scrumptious support for fast PEP 544 -- Protocols: Structural subtyping (static duck typing) and improved compliance with PEP 570 -- Python Positional-Only Parameters.

    This patch release resolves 3 issues and merges 1 pull request. Noteworthy changes include:

    Compatibility Improved

    • **PEP 570 -- Python Positional-Only Parameters, resolving issue #101 kindly submitted by typing luminary @dycw (Derek Wan). Previously, @beartype only supported positional-only parameters followed by one or more standard parameters in the signature of a callable. Now, @beartype unconditionally supports callables passed any permutation of positional-only and non-positional-only parameters– including callables passed only one or more positional-only parameters.

    Compatibility Optimized

    • PEP 544 -- Protocols: Structural subtyping (static duck typing). The beartype.typing.Protocol superclass has now been significantly optimized to internally cache structural subtyping checks across isinstance() calls the same protocol and objects of the same type checked against that protocol. Doing so ensures that:
      • Only the first call to a @beartype-decorated callable annotated by one or more beartype.typing.protocol-style protocols will be as slow as equivalent calls to a @beartype-decorated callable annotated by one or more typing.Protocol-style protocols.
      • All subsequent calls will be memoized to leverage the results internally cached by the first such call, a ludicrous speedup over the stock typing.Protocol approach.

    This optimization is entirely thanks to @posita the dynamite ("He's TNT!") devop, who miraculously migrated this caching behaviour from his own in-house numerary.types implementation. Thunderous applause for this third out of three tremendous contributions! Likewise, thanks so much for the grand critique that helped fuel this in my awkward and questionable absence, @TeamSpen210.

    You both are the stuff open-source dreams are made of. (Heated cleats!)

    Source code(tar.gz)
    Source code(zip)
  • v0.10.0(Feb 9, 2022)

    Beartype 0.10.0 released.

    This release titillates with scintillating support for PEP 557 -- Data Classes, PEP 570 -- Python Positional-Only Parameters, and PEP 604 -- Allow writing union types as X | Y.

    This release resolves a bone-crushing 30 issues (mostly shameless dupes of one another, admittedly) and merges 3 pull requests. World-girdling changes include:

    Compatibility Added

    • PEP 557 -- Data Classes. @beartype now supports dataclasses (i.e., types decorated by the standard @dataclasses.dataclass decorator), resolving issue #56 kindly submitted by @JulesGM (Jules Gagnon-Marchand) the Big Brain NLP researcher. Specifically, @beartype now transparently type-checks:
      • Dataclass-specific initialization-only instance variable type hints (i.e., dataclasses.InitVar[...]).
      • The implicit __init__() method generated by @dataclass for dataclasses through a clever one-liner employed by @antonagestam (Anton Agestam) the ageless Swede that I stan for.
    • PEP 570 -- Python Positional-Only Parameters. @beartype now supports positional-only arguments and no one cares. Given the triviality, the rear view mirror of regret suggests we kinda should've implemented this sooner. Better late than never, best @beartype friends for life (BBFFL).
    • PEP 604 -- Allow writing union types as X | Y. @beartype now supports new-style set unions (e.g., int | float), resolving issue #71 kindly submitted by pro typing aficionado Derek Wan (@dycw). Thanks to Derek for the helpful heads up that @beartype was headed straight for typing disaster under Python ≥ 3.10. Since we dodged another bullet there, this must mean we have now activated bullet time. Goooooo, slomo!

    Compatibility Improved

    • PEP 484 -- Type Hints, including:
      • typing.{Binary,Text,}IO[...] deep type-checking. @beartype now deeply type-checks subscripted typing.{Binary,Text,}IO[...] type hints, resolving issue #75 kindly submitted by Niklas "If I had a nickel for every lass..." Rosenstein. Notably:
        • Since the typing.BinaryIO protocol and its typing.IO superclass share the exact same API, the typing.BinaryIO protocol is lamentably useless for all practical purposes. This protocol cannot be leveraged to detect binary file handles. Can binary file handles be detected at runtime then? Yes, we can! A binary file handle is any object satisfying the typing.IO protocol but not the typing.TextIO protocol. To implement this distinction, @beartype necessarily invented a novel form of type-checking and a new variant of type elision: anti-structural subtyping. Whereas structural subtyping checks that one class matches the API of another class (referred to as a "protocol"), anti-structural subtyping checks that one class does not match the API of another class (referred to as an "anti-protocol"). @beartype public exposes this functionality via the new beartype.vale.IsInstance[...] validator, enabling anyone to trivially perform anti-structural subtyping. In this case, @beartype internally reduces all useless typing.BinaryIO type hints to substantially more useful typing.Annotated[typing.IO, ~beartype.vale.IsInstance[typing.TextIO]] type hints.
    • Unsubscripted NumPy type hints. @beartype now supports untyped NumPy array type hints (i.e., the unsubscripted numpy.typing.NDArray and subscripted numpy.typing.NDArray[typing.Any] type hints), resolving issue #69 kindly submitted by @Jasha10, the stylish boy wonder dual-wielding the double thumbs-up and coke-bottle glasses that signify elementary genius. Specifically, this commit now detects and reduces these hints to the equivalent numpy.ndarray type.
    • Mypy ≥ 0.920. @beartype now squelches ignorable mypy complaints first introduced by mypy 0.920, including:
      • Explicit reexport errors. beartype now squelches implicit reexport complaints from mypy with respect to public attributes published by the beartype.cave subpackage, resolving issue #57 kindly reopened by Göteborg melodic death metal protégé and brightest academic luminary @antonagestam. This subpackage is now compatible with both the --no-implicit-reexport mypy CLI option and equivalent no_implicit_reexport = True configuration setting in .mypy.ini.
      • Version-dependent errors. Previously, mypy permitted imports against standard library modules introduced in newer CPython versions to be squelched with the usual "# type: ignore[attr-defined]" pragma. Since mypy now ignores these pragmas, @beartype now silences its complaints through... unconventional means. A bear do wut a bear gotta do.

    Features Added

    • Compatibility API. beartype now publishes a new beartype.typing API as a typing compatibility layer improving forward compatibility with future Python releases, resolving issue #81 kindly submitted by the honorable @qiujiangkun (Qiu Jiangkun). Consider resolving PEP 585 deprecations by importing from our new beartype.typing API rather than the standard typing API. A battery of new unit tests ensure conformance:
      • Between beartype.typing and typing across all Python versions.
      • With mypy when importing from beartype.typing.
    • Configuration API (i.e., public attributes of the beartype package enabling end users to configure the @beartype decorator, including configuring alternative type-checking strategies other than constant-time runtime type-checking). Specifically, beartype now publishes:
      • beartype.BeartypeStrategy, an enumeration of all type-checking strategies to eventually be fully supported by future beartype releases – including:
        • BeartypeStrategy.O0, disabling type-checking for a callable by reducing @beartype to the identity decorator for that callable. Although currently useless, this strategy will usefully allow end users to selectively prevent callables from being type-checked by our as-yet-unimplemented import hook. When implemented, that hook will type-check all callables in a given package by default. Some means is needed to prevent that from happening for select callables. This is that means.
        • BeartypeStrategy.O1, our default O(1) constant-time strategy type-checking a single randomly selected item of a container that you currently enjoy. Since this is the default, this strategy need not be explicitly configured. Of course, you're going to do that anyway, aren't you? </sigh>
        • BeartypeStrategy.Ologn, a new O(lgn) logarithmic strategy type-checking a randomly selected number of items j of a container obj such that j = len(obj). This strategy is currently unimplemented (but will be implemented by a future beartype release).
        • BeartypeStrategy.On, a new O(n) linear strategy deterministically type-checking all items of a container. This strategy is currently unimplemented (but will be implemented by a future beartype release).
      • beartype.BeartypeConf, a simple dataclass encapsulating all flags, options, settings, and other metadata configuring the current decoration of the decorated callable or class. For efficiency, this dataclass internally self-caches itself (i.e., BeartypeConf(*args, **kwargs) is BeartypeConf(*args, **kwargs)). The __init__() method of this dataclass currently accepts these optional parameters:
        • An is_debug boolean instance variable. When enabled, @beartype emits debugging information for the decorated callable – including the code for the wrapper function dynamically generated by @beartype that type-checks that callable.
        • A strategy instance variable whose value must be a BeartypeStrategy enumeration member. This is how you notify @beartype of which strategy to apply to each callable.
      • Wrapper function debuggability. Enabling the is_debug parameter to the BeartypeConf.__init__ method significantly improves the debuggability of type-checking wrapper functions generated by @beartype. This configuration option is entirely thanks to @posita the positive Numenorean, who pined longingly for debuggable wrapper functions and now receives proportionately. Praise be to @posita! He makes bears better. Specifically, enabling this option enables developer-friendly logic like:
        • Pretty-printing to stdout (standard output) the definitions of those functions, including line number prefixes for readability.

        • Enabling those functions to be debugged. Thanks to a phenomenal pull request by the dynamic dual threat that is @posita + @TeamSpen210, @beartype now conditionally caches the bodies of type-checking wrapper functions with the standard (albeit poorly documented) linecache module. Thanks so much! Bear Clan 2022!!!

        • Suffixing the declarations of @beartype-specific hidden private "special" parameters passed to those functions with comments embedding their human-readable representations. Safely generating these comments consumes non-trivial wall clock at decoration time and is thus conditionally enabled for external callers requesting @beartype debugging. For example, note the "# is"-prefixed comments in the following signature of a @beartype-generated wrapper function for an asynchronous callable with signature async def control_the_car(said_the: Union[str, int], biggest_greenest_bat: Union[str, float]) -> Union[str, float]:

          (line 0001) async def control_the_car(
          (line 0002)     *args,
          (line 0003)     __beartype_func=__beartype_func, # is <function test_decor_async_coroutine.<locals>.control_the_car at 0x7>
          (line 0004)     __beartype_raise_exception=__beartype_raise_exception, # is <function raise_pep_call_exception at 0x7fa13d>
          (line 0005)     __beartype_object_140328307018000=__beartype_object_140328307018000, # is (<class 'int'>, <class 'str'>)
          (line 0006)     __beartype_object_140328306652816=__beartype_object_140328306652816, # is (<class 'float'>, <class 'str'>)
          (line 0007)     **kwargs
          (line 0008) ):
          
    • Decorator modality. @beartype now supports two orthogonal modes of operation:
      • Decoration mode (i.e., the standard mode where @beartype directly decorates a callable without being passed parameters). In this mode, @beartype reverts to the default configuration of constant-time runtime type-checking and no debugging behaviour.

      • Configuration mode (i.e., the new mode where @beartype is called as a function passed a BeartypeConf object via the keyword-only conf parameter). In this mode, @beartype efficiently creates, caches, and returns a memoized decorator encapsulating the passed configuration: e.g.,

        from beartype import beartype, BeartypeConf, BeartypeStrategy
        
        @beartype(conf=BeartypeConf(strategy=BeartypeStrategy.On))
        def muh_func(list_checked_in_linear_time: list[int]) -> int:
            return len(list_checked_in_linear_time)
        
    • Declarative instance validator. beartype now publishes a new beartype.vale.IsInstance[...] validator enforcing instancing of one or more classes, generalizing isinstanceable type hints (i.e., normal pure-Python or C-based classes that can be passed as the second parameter to the isinstance() builtin). Unlike standard isinstanceable type hints, beartype.vale.IsInstance[...] supports various set theoretic operators. Critically, this includes negation. Instance validators prefixed by the negation operator ~ match all objects that are not instances of the classes subscripting those validators. Wait. Wait just a hot minute there. Doesn't a typing.Annotated_ type hint necessarily match instances of the class subscripting that type hint? Yup. This means type hints of the form typing.Annotated[{superclass}, ~IsInstance[{subclass}] match all instances of a superclass that are not also instances of a subclass. And... pretty sure we just invented type hint arithmetic right there. That sounded intellectual and thus boring. Yet, the disturbing fact that Python booleans are integers yup while Python strings are infinitely recursive sequences of strings yup means that type hint arithmetic can save your codebase from Guido's younger self. Consider this instance validator matching only non-boolean integers, which cannot be expressed with any isinstanceable type hint (e.g., int) or other combination of standard off-the-shelf type hints (e.g., unions): Annotated[int, ~IsInstance[bool]]. ← bruh
    • Functional API. beartype now publishes a new public beartype.abby subpackage enabling users to type-check anything anytime against any PEP-compliant type hints, resolving feature request #79 kindly submitted by (...wait for it) typing Kung Fu master @qiujiangkun (Qiu Jiangkun). This subpackage is largely thanks to @qiujiangkuni, whose impeccable code snippets drive our initial implementation. This subpackage provides these utility functions:
      • beartype.abby.is_bearable(), strictly returning a boolean signifying whether the passed arbitrary object satisfies the passed type hint or not (e.g., is_bearable(['the', 'centre', 'cannot', 'hold;'], list[int]) is False).
      • beartype.abby.die_if_unbearable(), raising the new beartype.roar.BeartypeAbbyHintViolation exception when the passed arbitrary object violates the passed type hint.

    Features Improved

    • Exception message granularity, including exceptions raised for:
      • Disordered builtin decorators. @beartype now raises instructive exceptions when decorating an uncallable descriptor created by a builtin decorator (i.e., @property, @classmethod, @staticmethod) due to the caller incorrectly ordering @beartype above rather than below that decorator, resolving issue #80 kindly submitted by typing academician @qiujiangkun (Qiu Jiangkun). Specifically, @beartype now raises human-readable exceptions suffixed by examples instructing callers to reverse decoration ordering.
      • Beartype validators. @beartype now appends a detailed pretty-printed diagnosis of how any object either satisfies or fails to satisfy any beartype validator to exception messages raised by high-level validators synthesized from lower-level validators (e.g., via overloaded set theoretic operators like |, &, and ~), resolving issue #72 kindly submitted by the unwreckable type-hinting guru Derek Wan (@dycw). This diagnostic trivializes validation failures in non-trivial use cases involving multiple nested conjunctions, disjunctions, and/or negations.

    Features Optimized

    • @beartype call-time performance. @beartype now generates faster type-checking wrapper functions with a vast and undocumented arsenal of absolutely "legal" weaponry, including:
      • typing.{Generic,Protocol} deduplication. @beartype now microoptimizes away redundant isinstance() checks in wrapper functions checking @beartype-decorated callables annotated by PEP 484-compliant subgenerics or PEP 585-compliant subprotocols (i.e., user-defined classes subclassing user-defined classes subclassing typing.{Generic, Protocol}), resolving issue #76 kindly submitted by @posita the positive numerics QA guru and restoring the third-party numerary package to its glory. Our generics workflow has been refactored from the ground-up to stop behaving insane. @beartype now performs an inner breadth-first search (BFS) across generic pseudo-superclasses in its existing outer BFS that generates type-checking code. When you're nesting a BFS-in-a-BFS, your code went full-send. There's no going back from that.
    • Worst-case nested data structures. @beartype now resolves a performance regression in type-checking wrapper functions passed worst-case nested data structures violating PEP-compliant type hints, resolving issue #91 kindly submitted by Cuban type-checking revolutionary @mvaled (Manuel Vázquez Acosta). Specifically, this commit safeguards our low-level represent_object() function stringifying objects embedded in exception messages describing type-checking violations against worst-case behaviour. A new unit test shieldwalls against further performance regressions. All our gratitude to @mvaled for unveiling the darkness in the bear's heart.
    • @beartype decoration-time performance. The @beartype decorator has been restored to its prior speed, resolving performance regressions present throughout our [0.8.0, 0.10.0) release cycles. Significant decoration-time optimizations include:
      • Code objects. @beartype now directly accesses the code object underlying the possibly unwrapped callable being decorated via a temporary cache rather than indirectly accessing that code object by repeatedly (and expensively) unwrapping that callable, dramatically optimizing low-level utility functions operating on code objects.
      • Exception messages. @beartype now defers calling expensive exception handling-specific functions until an exception is raised, dramatically restoring our decoration-time performance to the pre-0.8.0 era – which isn't that great, honestly. But we'll take anything. Substantial optimizations remain, but we are dog-tired. Moreover, DQXIS:EofaEA (...that's some catchy name right there) ain't gonna play itself – OR IS IT!?! Cue creepy AI.
      • Fixed lists. @beartype now internally lelaxes inapplicable safety measures previously imposed by our internal FixedList container type. Notably, this type previously detected erroneous attempts to extend the length of a fixed list by subversively assigning a slice of that fixed list to a container whose length differs from that of that slice. While advisable in theory, @beartype never actually sliced any fixed list -- let alone used such a slice as the left-hand side (LHS) of an assignment. Disabling this detection measurably improves the efficiency of fixed lists across the codebase -- which is, after all, the entire raison d'etre for fixed lists in the first place. </shaking_my_head>
      • Parameter introspection. @beartype now introspects callable signatures using a homegrown lightweight parameter parsing API. @beartype previously introspected signatures using the standard heavyweight inspect module, which proved... inadvisable. All references to that module have been removed from timing-critical code paths. All remaining references reside only in timing-agnostic code paths (e.g., raising human-readable exceptions for beartype validators defined as anonymous lambda functions).
    • @beartype importation-time performance. The beartype package now avoids unconditionally importing optional first- and third-party subpackages, improving the efficiency of the initial from beartype import beartype statement in particular. beartype now intentionally defers these imports from global module scope to the local callable scope that requires them. A new functional test guarantees this to be the case.

    Features Deprecated

    • Badly named exception classes, to be removed in beartype 0.1.0. This includes:
      • beartype.roar.BeartypeCallHintPepException, deprecated by beartype.roar.BeartypeCallHintViolation.
      • beartype.roar.BeartypeCallHintPepParamException, deprecated by beartype.roar.BeartypeCallHintParamViolation.
      • beartype.roar.BeartypeCallHintPepReturnException, deprecated by beartype.roar.BeartypeCallHintReturnViolation.

    Documentation Revised

    • The Frequently Asked Questions (FAQ) section of our front-facing README.rst documentation now sports a medley of new entries, including instructions on:

      • Boto3 integration, enabling end users to type-check runtime types dynamically fabricated by Boto3 (i.e., the official Amazon Web Services (AWS) Software Development Kit (SDK) for Python), resolving issue #68 kindly submitted by Paul Hutchings (@paulhutchings) – the supremely skilled sloth rockin' big shades and ever bigger enthusiasm for well-typed Python web apps. Relatedly, the @beartype organization now officially hosts bearboto3, Boto3 @beartype bindings by (wait for it) @paulhutchings.
      • Mock type type-checking, resolving issue #92 kindly submitted by @Masoudas (Masoud Aghamohamadian-Sharbaf – wish I had an awesome name like that). Gratuitous shoutouts to @TeamSpen210 for the quick save with a ludicrous two-liner solving everything.

    The hype train is now boarding. All aboooooooard! (Classless masterless masterclass!)

    Source code(tar.gz)
    Source code(zip)
  • 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)
Owner
beartype
Unbearably fast O(1) runtime type-checking in pure Python.
beartype
Python classes with types validation at runtime.

typedclasses Python classes with types validation at runtime. (Experimental & Under Development) Installation You can install this library using Pytho

Izhar Ahmad 8 Feb 6, 2022
Flake8 extension for checking quotes in python

Flake8 Extension to lint for quotes. Major update in 2.0.0 We automatically encourage avoiding escaping quotes as per PEP 8. To disable this, use --no

Zachary Heller 157 Dec 13, 2022
flake8 plugin to run black for checking Python coding style

flake8-black Introduction This is an MIT licensed flake8 plugin for validating Python code style with the command line code formatting tool black. It

Peter Cock 146 Dec 15, 2022
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 4k Dec 31, 2022
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 9.2k Jan 3, 2023
Mypy stubs, i.e., type information, for numpy, pandas and matplotlib

Mypy type stubs for NumPy, pandas, and Matplotlib This is a PEP-561-compliant stub-only package which provides type information for matplotlib, numpy

Predictive Analytics Lab 194 Dec 19, 2022
Simple Python style checker in one Python file

pycodestyle (formerly called pep8) - Python style guide checker pycodestyle is a tool to check your Python code against some of the style conventions

Python Code Quality Authority 4.7k Jan 1, 2023
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 14.4k Jan 8, 2023
A Python Parser

parso - A Python Parser Parso is a Python parser that supports error recovery and round-trip parsing for different Python versions (in multiple Python

Dave Halter 520 Dec 26, 2022
A simple program which checks Python source files for errors

Pyflakes A simple program which checks Python source files for errors. Pyflakes analyzes programs and detects various errors. It works by parsing the

Python Code Quality Authority 1.2k Dec 30, 2022
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 2.1k Jan 1, 2023
Tool to check the completeness of MANIFEST.in for Python packages

check-manifest Are you a Python developer? Have you uploaded packages to the Python Package Index? Have you accidentally uploaded broken packages with

Marius Gedminas 270 Dec 26, 2022
A python documentation linter which checks that the docstring description matches the definition.

Darglint A functional docstring linter which checks whether a docstring's description matches the actual function/method implementation. Darglint expe

Terrence Reilly 463 Dec 31, 2022
Flake8 plugin that checks import order against various Python Style Guides

flake8-import-order A flake8 and Pylama plugin that checks the ordering of your imports. It does not check anything else about the imports. Merely tha

Python Code Quality Authority 270 Nov 24, 2022
Check for python builtins being used as variables or parameters

Flake8 Builtins plugin Check for python builtins being used as variables or parameters. Imagine some code like this: def max_values(list, list2):

Gil Forcada Codinachs 98 Jan 8, 2023
Custom Python linting through AST expressions

bellybutton bellybutton is a customizable, easy-to-configure linting engine for Python. What is this good for? Tools like pylint and flake8 provide, o

H. Chase Stevens 249 Dec 31, 2022
Naming Convention checker for Python

PEP 8 Naming Conventions Check your code against PEP 8 naming conventions. This module provides a plugin for flake8, the Python code checker. (It repl

Python Code Quality Authority 411 Dec 23, 2022
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 967 Jan 7, 2023
Flake8 extension for enforcing trailing commas in python

Flake8 Extension to enforce better comma placement. Usage If you are using flake8 it's as easy as: pip install flake8-commas Now you can avoid those a

Python Code Quality Authority 127 Sep 3, 2022