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 withasync def
rather than merelydef
) 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 decoratedcoroutine()
and then validating the value returned bycoroutine()
satisfies the{return}
type hint.async def coroutine(...) -> typing.Coroutine[{yield}, {send}, {return}]
, which@beartype
now wraps with an asynchronous coroutine first awaiting the decoratedcoroutine()
and then validating the value returned bycoroutine()
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 withasync def
rather than merelydef
) 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:
- Synchronous generators to be annotated with either:
- 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 entirebeartype.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 ofnumpy.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 subscriptnumpy.typing.NDArray
by the appropriate scalar ABC: e.g., numpy.typing.NDArray[numpy.floating]
rather thannumpy.typing.NDArray[numpy.float64]
, matching any floating-point NumPy array (regardless of precision).numpy.typing.NDArray[numpy.integer]
rather thannumpy.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 allnumpy.typing.NDArray[{dtype}]
type hints to the untyped NumPy array classnumpy.ndarray
. Since this is admittedly non-ideal,@beartype
now emits one non-fatal warning of categorybeartype.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
- An actual NumPy dtype (e.g.,
- 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 theissubclass()
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 hinttyping.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., astyping.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., asfloat
).
- Constraints passed as positional parameters (e.g.,
- 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 newbeartype.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.
- A new "PEP 484 Deprecations" subsection has been added to our front-facing
- Deeply type-checks PEP 484-compliant
- 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.
- 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
- PEP 585.
@beartype
now:- Deeply type-checks supports PEP 585-compliant
type
type hints, exactly as described for PEP 484-complianttyping.Type
type hints above. - Partially deeply type-checks PEP 585-compliant
collections.abc.Coroutine
type hints, exactly as described for PEP 484-complianttyping.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 standardtyping
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.
- Deeply type-checks supports PEP 585-compliant
- PEP 586 (i.e.,
Literal
).@beartype
now transparently supports both the official PEP 586-complianttyping.Literal
type hint and its quasi-officialtyping_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., bothtyping.TypedDict
type hints under Python ≥ 3.8 andtyping_extensions.TypedDict
type hints under Python < 3.8) by internally reducing these hints to simplyMapping[str, object]
. Doing so was surprisingly non-trivial, as the Python 3.8-specific implementation of thetyping.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-complianttyping.Annotated
type hint and its quasi-officialtyping_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-complianttyping.Type[{type}]
and PEP 585-complianttype[{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 existingbeartype.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-deprecatedbeartype.cave.HintGenericSubscriptedType
attribute.beartype.cave.NumpyArrayType
, which should now be accessed directly asnumpy.ndarray
.beartype.cave.NumpyScalarType
, which should now be accessed directly asnumpy.generic
.beartype.cave.SequenceOrNumpyArrayTypes
, which should now be annotated astyping.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 aspackaging.version.Version
.beartype.cave.VersionComparableTypes
, which should now be annotated directly astyping.Union[tuple, packaging.version.Version]
.beartype.cave.VersionTypes
, which should now be annotated directly astyping.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 593typing_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
logorrheavalid complaints, including:- Implicit reexport complaints
beartype 0.9.0
is now compatible with both the--no-implicit-reexport
mypy CLI option and equivalentno_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 privatebeartype.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.
- Implicit reexport complaints
- 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 encodingcp1252
. 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 documentedpytest-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 remotegit
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!)