pypi beartype 0.9.0
Beartype 0.9.0

latest releases: 0.21.0, 0.21.0rc0, 0.20.2...
3 years ago

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-deprecated beartype.roar.BeartypeDecorHintNonpepNumpyException` attribute.
  • beartype.roar.BeartypeDecorHintPepDeprecatedWarning, which should now be accessed as the non-deprecated beartype.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!)

Don't miss a new beartype release

NewReleases is sending notifications on new releases.