New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TypeError: TypedDict does not support instance and class checks #55
Comments
...ohnoes. You caught us red-handed – or honey-pawed, as it were. badum ching A long time ago in a codebase far, far away, Welp, this is awkward. Some unspecified recent release of Thanks so much for the compassionate assistance, Master Kyle. Let's get this patched up for everyone and pretend this never happened! The sweat pouring off my Klingon-like forehead is real. 😓 💦 |
Welp, this is even more embarrassing... Turns out we actually never supported On the bright side, at least we now know why a non-existent feature was never tested. This is a pretty critical omission, as |
Thanks! I'm always impressed by your liberal use of puns and references I would be happy to test and give feedback before you release, if that would help. And no rush! |
Oh, thank you so much. You are too kind and complimentary. This elicits feelings of warmth and generosity, which means I now want to get this done even more! Nice-guy Worf approves. Please do liberally test this after ...if we type-check it, they will comeIn theory, it should be trivial for
wut you gonna do TypeDict Rant, Let's Go!Lastly, I kinda agree with mpkocher's well-written
It's a crying shame. Guido & Co. were clearly trying to extend the idea of fixed-length tuple type hints (e.g., I am sighing. |
This commit is the last in a commit chain adding support for superficially type-checking **typed dictionaries** (i.e., `typing.TypedDict` and `typing_extensions.TypedDict` subclasses), resolving issue #55 kindly submitted by Kyle (@KyleKing), the undisputed King of Parexel AI Labs. Specifically, this commit: * Refines our recently added `beartype._util.hint.pep.proposal.utilpep593.is_hint_pep593()` tester to additionally test for the existence of the `__total__` dunder attribute necessarily added to typed dictionaries. * Improves our private `beartype._util.hint.pep.utilpepget.get_hint_pep_sign_or_none()` getter to uniquely identify typed dictionaries as `HintSignTypedDict`. * Exhaustively tests `@beartype` support for typed dictionaries, including those defined via both: * The official `typing.TypedDict` factory under Python ≥ 3.8. * The third-party `typing_extensions.TypedDict` factory under Python 3.6 and 3.7. (*Unrequited requiem!*)
Brutally resolved! In commit 342e5fc (and the upcoming Clearly, this is... suboptimal. But we can agree that something beats nothing. Well, conditionally depending on the exact nature of that something. So sometimes. And this is one of those times. Thanks again for the thoughtful heads-up and bearing hyuk hyuk with us here, Kyle the undisputed King of Parexel AI Labs. You deserve all the academic citations that the @beartype community has to give. Reference Kyle, everyone! He rocks that AI. 📝 |
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 484], [PEP 563][PEP 563], [PEP 585][PEP 585], [PEP 586][PEP 586], [PEP 589][PEP 589], and [PEP 593][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 484], [PEP 585][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][PEP 484]-compliant `typing.AsyncGenerator[...]` type hint. * A [PEP 585][PEP 585]-compliant `collections.abc.AsyncGenerator[...]` type hint. * Synchronous generators to be annotated with either: * A [PEP 484][PEP 484]-compliant `typing.Generator[...]` type hint. * A [PEP 585][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][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.](https://www.youtube.com/watch?v=zTABEQ4Qh5Y) How could this be *our* fault!?!? <sup>it's totally our fault</sup> * **[PEP 484][PEP 484].** `@beartype` now: * Deeply type-checks [PEP 484][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][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][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][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][PEP 484]-compliant type hints deprecated by [PEP 585][PEP 585] under Python ≥ 3.9. Specifically: * A [new "PEP 484 Deprecations" subsection](https://github.com/beartype/beartype#pep-484-deprecations) 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](https://github.com/beartype/beartype#pep-484-deprecations) 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][PEP 563].** `@beartype` now: * Reverts our unconditionally enable of [PEP 563][PEP 563] under Python ≥ 3.10 in solidarity with the recent (pydantic|FastAPI)-led casus belli. [PEP 563][PEP 563] must now be explicitly enabled under *all* Python interpreters via the standard `from __future__ import annotation` pragma. * Explicitly avoids resolving [PEP 563][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][PEP 585].** `@beartype` now: * Deeply type-checks supports [PEP 585][PEP 585]-compliant `type` type hints, exactly as described for [PEP 484][PEP 484]-compliant `typing.Type` type hints above. * Partially deeply type-checks [PEP 585][PEP 585]-compliant `collections.abc.Coroutine` type hints, exactly as described for [PEP 484][PEP 484]-compliant `typing.Coroutine` type hints above. * **Deduplicates all [PEP 585][PEP 585] type hints,** dramatically reducing both the space and time complexity associated with such hints. [PEP 484][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 484], [PEP 585][PEP 585] fails to deduplicate its hints (e.g., `list[str] is not list[str]`). `@beartype` now internally deduplicates duplicated [PEP 585][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][PEP 586]** (i.e., `Literal`). `@beartype` now transparently supports both the official [PEP 586-compliant `typing.Literal` type hint][PEP 586] *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][PEP 589]** (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][PEP 593]** (i.e., `Annotated`). `@beartype` now transparently supports both the official [PEP 593-compliant `typing.Annotated` type hint][PEP 593] *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][PEP 484]-compliant `typing.Type[{type}]` and [PEP 585][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][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][PEP 586] `typing_extensions.Literal` and [PEP 593][PEP 593] `typing_extensions.Annotated` backports,** resolving issue #52 kindly submitted by cutting-edge Microsoft graph luminary @pbourke. See above for details. * **[PEP 589][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'. [PEP 484]: https://www.python.org/dev/peps/pep-0484/#id33 [PEP 560]: https://www.python.org/dev/peps/pep-0560 [PEP 563]: https://www.python.org/dev/peps/pep-0563 [PEP 585]: https://www.python.org/dev/peps/pep-0585 [PEP 586]: https://www.python.org/dev/peps/pep-0586 [PEP 589]: https://www.python.org/dev/peps/pep-0589 [PEP 593]: https://www.python.org/dev/peps/pep-0593 [PEP 3119]: https://www.python.org/dev/peps/pep-3119/#overloading-isinstance-and-issubclass (*Reticulated ticks gesticulate; ant antics masticate!*)
I saw in #7 that TypedDict should be supported from 0.7.0 and partial support has been in beartype since 0.1.0, but I'm running into errors with Python 3.7.12 and 3.9.7, so I'm not sure if this snippet should be working:
Should beartype properly check TypedDicts? There were similar errors found in Pydantic and some notes here. TypedDict can't be used with isinstance by design. There is a reference to tests for TypedDict, but they don't appear to be present
And this isn't promising 😅
beartype/beartype/_data/hint/pep/sign/datapepsigns.py
Line 131 in b7ab8b0
The text was updated successfully, but these errors were encountered: