Beartype 0.21.0 Release Candidate 0 consoles your codebase as it shudders under the oppressive tidal wave of bugs. Much like its predecessors, @beartype 0.21.0rc0
is here to help. Unlike its predecessors, @beartype 0.21.0rc0
claims it solves more problems than it creates for once. Is @beartype 0.21.0rc0
.... lying!? 🫢
pip install --upgrade --pre beartype # <-- blast all bugs into the git pit
Let your test suite show the truth – even if @leycec just wants to play obscure French video games with titles like Clair Obscur: Expedition 33 (Because This Couldn't Be More Pretentiously Pseudorandom) the entire weekend and pretend our issue tracker isn't collapsing under its own ponderous weight:
@beartype 0.21.0rc0
: this can't be what you've waited months for
@beartype 0.21.0rc0
is gratefully brought to you by...
GitHub Sponsors: When You Befriend the Bear, You've got a Bear for Life
This release comes courtesy these proud GitHub Sponsors, without whom @leycec's cats would currently be eating grasshoppers:
- @sesco-llc (SESCO Enterprises), "The Power of Innovation in Trading": this inspires me to get out of the house and do something
https://sescollc.com - @DylanModesitt (Dylan Modesitt), quantitative strategies energy trading associate: ...wikipedia, don't fail me now!
https://dylanmodesitt.com - @tactile-metrology (Tactile Metrology), "Software and hardware that you can touch." When I want to be touched by software and hardware, I call @tactile-metrology:
https://metrolo.gy imagine if this domain actually worked. how cool would that be!?
Thanks so much, masters of fintech and metrology.
The Masters of Fintech and Metrology. That's who.
Let's get this pawful party started.
tl;dr: Explosive Recursion Never Felt So Good
@beartype 0.21.0rc0
is obsessed with recursive data structures. They're more common than you might think! Okay. They're totally rare. We all learn about recursive data structures as poverty-stricken undergrads who subsist on years-old cup ramen and then pretend we never learned about them. You'll never need to implement a recursive data structure in pure-Python, because somebody else already did that for you. Graphs, heaps, queues, linked lists, skip lists, trees, and (our personal favourite) tries are all sufficiently awesome that you're already using most of them... because somebody else made them. That's why you're using them! Right? Ain't nobody got spare time or brain space to hack out a pure-Python red-black binary tree in 2025. But somebody did.
@beartype 0.21.0rc0
is for that somebody. When you need recursion, you need @beartype 0.21.0rc0
.
@beartype 0.21.0rc0
also acknowledges that 2025 is Humanity on Hard Mode™. The planet isn't doing well. Humanity isn't doing well. Industrial civilization isn't doing well. The US isn't doing well. Even Canada's looking a bit shaky – and we face literal death just by going outside six months of the year. Let's not even mention the deer flies, black flies, mosquitos, ticks, or rabid raccoons. Gods. Anything but the rabid raccoons. Therefore, wherever you are, whatever you face, whenever the darkness erupts and starts gnawing on your codebase...
@beartype 0.21.0rc0
will be there. We got your codebase's back. In fact, we're currently scratching that back. Feels good, right? These paws have claws – but only for bugs. Your code got lucky.
@beartype 0.21.0rc0
: a familiar face you can trust
Synopsis: When You're So Verbose Even Your Synopsis Is a White Paper
Let @beartype assuage, massage, and presage ...wat? it's my release party and i'll rhyme if i wanna those issues away. @beartype 0.21.1rc0
promises it delivers first-class best-of-breed hyphenated-jargon-hype-train support for:
-
Recursive type hints! That's right. Now you too can revel in the disgusting power of infinitely deep data structures with PEP 695-compliant recursive
type
aliases:# Type hint matching an infinitely recursive list. Look, I don't know. This is for # the extreme sports coders that like to live dangerously and code even harder. type RecursiveListExplodesYourApp = list[RecursiveListExplodesYourApp]
-
Opt-in dataclass field checking! That's right. Now you too can type-check
@dataclass
fields on assignment by enablingis_pep557_fields=True
– much to the dismay of everybody else in the office:# @beartype: It might not be Pydantic, but at least it cost you nothing. beartype_this_package(conf=BeartypeConf(is_pep557_fields=True)) # <-- magical explosions?
-
Generalized hint overrides! That's right. Now you too can replace all
list[str]
type hints with... uhh,list[str] | tuple[str, ...]
. Pretend somebody wants this:# Users can now pass tuples of strings to all callables annotated as # accepting only lists of strings. waaaaaaaaaaaaaaaaaaaaaaaaaaaaaaat? beartype_this_package(conf=BeartypeConf(hint_overrides=FrozenDict({ list[str]: list[str] | tuple[str, ...]}))) # <-- pretend this makes sense
-
Frozen dictionaries! It's happening, because
beartype.FrozenDict
is making it happen:from beartype import FrozenDict # Finally, a set of frozen dictionaries! Yes, it's all true. Now you too can use # dictionaries as dictionaries keys or set members. Why? Because you can. freezing_my_dict_off = { FrozenDict({'My ganglia!': 'It hurts.'}), FrozenDict({'What even is a ganglia?': 'No idea. But it surely hurts.'}), }
-
Probably other stuff! But nobody cares, because nobody even read this far. WAIT. You're reading this far. Clearly, you're somebody – somebody awesome who actually has hair and is profoundly changing the world! It only goes to show you can't believe anything you read in a changelog anymore. 2025: "So even the changelogs lie now, huh?"
@beartype 0.21.0rc0
: If you don't feel like a wild animal while coding, can it be called coding?
Recursion: Still Destroying Lives after All These Years
@beartype 0.21.0rc0
now officially supports all possible forms of recursion in type hints. This includes directly recursive PEP 695 type
aliases, indirectly recursive PEP 484 generics, and @beartype-specific hint overrides. Which you prefer depends on which bitter pill you're willing to swallow:
-
If you're willing to require Python ≥ 3.12 as a mandatory dependency, prefer PEP 695
type
aliases. They're concise. They're descriptive. They're elegant. They "just work" intuitively in the exact way you expect them to:# Annotate recursive data structures with a "simple" one-liner. \o/ type RecursiveListExplodesYourApp = list[RecursiveListExplodesYourApp]
-
If you're unwilling to require Python ≥ 3.12 as a mandatory dependency, fallback to PEP 484 self-subscripted generics. They're unconcise. They're non-descriptive. They're inelegant. They require heavy lifting on your part before they start working. But they do work under Python ≥ 3.9, which is more than we can say for PEP 695:
# One line that makes sense (above) or five lines that don't make sense (below)? # Let the cat decide. ¯\_(ツ)_/¯ from typing import TypeVar T = TypeVar('T') class GenericList(list[T]): pass GenericRecursiveListExplodesYourApp = GenericList[GenericList]
Let's take this one recursive app destroyer at a time.
@beartype 0.21.0rc0
: this is the biggest animated gif i have ever seen
Direct Recursion via PEP 695 Type Aliases: Because How Much More Broken Could Your App Get?
Directly recursive PEP 695 type aliases is what everybody who wants recursive type hints wants. Against all odds, you're actually reading this. You want recursive type hints. Thus, you want:
# Type hint matching an infinitely recursive list. Look, I don't know. This is for
# the extreme sports coders that like to live dangerously and code even harder.
type RecursiveListExplodesYourApp = list[RecursiveListExplodesYourApp]
# @beartype now type-checks this infinitely recursive list in O(1) time. Of
# course, that's impossible. But @beartype doesn't even care anymore! F- it!
from beartype import beartype
@beartype
def dangerous_func_means_well(oh_gods: RecursiveListExplodesYourApp) -> None:
'''
Dangerous function iteratively recurses into the passed infinitely recursive
list until either bottoming out at the same list... *or blowing up.*
Which do you hope happens first?
'''
# The growing terror you feel just speed-reading this code is real.
seen_horror_ids: set[int] = set()
unseen_horrors: list[RecursiveListExplodesYourApp] = list((oh_gods,))
# Recurse into that terror. Recurse until your face is numb, like mine.
while unseen_horrors:
# Pop that terror like a meat balloon. If it squishes, you must pop it.
seen_horror = unseen_horrors.pop()
print(f'Visiting infinitely recursive list: {id(seen_horror)}')
# Awaken from this logic nightmare, gentle reader!
if id(seen_horror) in seen_horror_ids:
print("Recursion detected! We're outta here, suckers!")
break
# Pretend this does what the docstring says this does. *gulp*
seen_horror_ids.add(id(seen_horror))
unseen_horrors.append(seen_horror[0])
# Infinitely recursive list. If you try this in the office, you may regret the
# fateful day your career decisions plummeted down a cliff.
OH_GODS = []
OH_GODS.append(OH_GODS)
# Pass this function valid input. Stare down the infinitely recursive list?
# Don't mind if I do! Gape in awe before infinity blows up your call stack.
dangerous_func_means_well(OH_GODS)
# Pass this function invalid input. No! No! Gods! NOOOOOOOOOOOOOOOOOOOOOOO!
dangerous_func_means_well(['Open-source', 'sells,' 'but', "who's", 'buying?'])
...which raises the expected output and exception traceback:
Visiting infinitely recursive list: 133179499204736
Visiting infinitely recursive list: 133179499204736
Recursion detected! We're outta here, suckers!
Traceback (most recent call last):
File "/home/leycec/tmp/mopy.py", line 48, in <module>
dangerous_func_means_well(['Open-source', 'sells,' 'but', "who's", 'buying?'])
~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<@beartype(__main__.dangerous_func_means_well) at 0x792044b9c360>", line 40,
in dangerous_func_means_well
beartype.roar.BeartypeCallHintParamViolation: Function __main__.dangerous_func_means_well() parameter oh_gods=['Open-source', 'sells,but',
"who's", 'buying?'] violates type hint RecursiveListExplodesYourApp, as list index 2
item str "who's" not instance of list.
Pore one out for the unsuspecting @beartype users that actually tried to run the above example. Their smoking CPUs are no longer with us. What remains of the ruin of their motherboards is now locked into a segfaulting bootloop featuring a cackling ASCII-art bear. It is sad.
@beartype 0.21.0rc0
: "zomg so cuuuuuute oh my brain hurts nooooooooooooooo"
What's the Catch?
What? Catch? Surely you jest! There's no... oh, who am I kidding. There are huge catches associated with PEP 695. For one, @beartype intentionally does not support older PEP-noncompliant variants of recursive type hints that used stringified forward references. You might occasionally see crufty stuff like this floating around StackOverflow, older codebases, or the mypy
issue tracker:
HorrifyingRecursiveTypeHint = Union[str, 'HorrifyingRecursiveTypeHint']
@beartype doesn't support that. Using stringified forward references to induce recursion is non-standard. @beartype probably could support that, but there's not much point in supporting non-standards when standardized alternatives exist. That's why...
@beartype 0.21.0rc0
only supports PEP 695: the only standard for defining recursive type hints. Everything else was just something mypy
made up. Recursive type
aliases now work wonderfully under Python ≥ 3.12 – but that's the gotcha here.
@beartype 0.21.0rc0
: rambo with a sword is something that happened only on an alternate timeline... but it still happened
Oh, Gods! Here It Comes!
That's right. You love to hate it. PEP 695 is unusable under Python ≤ 3.11. Attempting to define any type
alias under Python ≤ 3.11 results in CPython raising an unreadable "SyntaxError: invalid syntax"
exception.
In a year or two, this will be significantly less of a hard blocker for everyone. Increasingly, nobody cares about Python ≤ 3.11. Do you care about Python ≤ 3.11? Maybe – but you probably shouldn't, unless your huge userbase is obsessed by Python ≤ 3.11. In that case, you're kinda screwed. You have to choose between your love for recursion and your love for having users. Tough choice. I'd choose recursion, personally.
beartype 0.21.rc0
: users who hate recursion are users who make your face contort into a tight rictus of agony
Python ≥ 3.12? Is That Really the Only Catch?
Absolutely! Totally! How could anything else possibly go wrong!
...oh, who am I kidding!?!?!? There is yet another huge catch associated with PEP 695. @beartype does not deeply type-check recursive data structures to a countably infinite depth of nested recursion. Instead, @beartype:
- Type-checks recursive data structures to only a single level of nested recursion.
- Silently accepts all deeper levels of nested recursion in recursive data structures.
Let's just accept this is happening. But why is this happening? Coupla reasons, fam:
- Constant-time
O(1)
time complexity. Deeply type-checking a recursive data structure with recursive heightk
would necessitate linear-timeO(k)
time complexity in @beartype – violating @beartype's fundamental efficiency guarantee. - Space safety. Recursion in super-unsafe in Python. Python lacks tail recursion (which sucks) and allocates recursive call frames on the stack rather than the heap (which also sucks). This means that @beartype cannot safely type-check arbitrarily large recursive data structures through recursion. Thankfully, we are all awesome. Therefore, we all know that all recursive algorithms can be implemented iteratively rather than recursively. In theory, this means @beartype could recursively type-check arbitrarily large recursive data structures through iteration rather than recursion. In practice, I am already so tired I can barely see the backs of my eyelids. There may never exist enough lifetimes in the known Universe for me to do that.
- Time safety. Even an iterative approach to recursive type-checking rapidly bumps up against unpleasant real-world edge cases like infinitely recursive containers (i.e., containers that contain themselves as items like
bad_list = []; bad_list.append(bad_list)
). Of course, an iterative approach could be protected against these edge cases by dynamically generating type-checking code that maintains:- For each recursive
type
alias to be type-checked, one set of the IDs of all previously type-checked objects. But now @beartype would need to allocate and append to one friggin' set for each recursivetype
alias for each function call. Space and time efficiency rapidly spirals into the gutter and then clutches its aching head like in a depressing LeavingLos VegasSilicon Valley scene.
- For each recursive
Only Python ≥ 3.12!? Only one layer of recursion!?
@beartype 0.21.0rc0
: let's get sweaty, together
Let's assume you hate requiring Python ≥ 3.12. You still love Python 3.9, even though nobody else does. You walk your own dark road. In this case, you want...
Indirect Recursion via Self-subscripting Generics: Because Your App Could Get A Lot More Broke, Apparently
Self-subscripting generics, huh? You may now be thinking:
"But what does that even mean? How can a generic subscript itself? What even are generics? What does "subscription" mean? What does anything mean in a post-modern world of fluid subjectivity?"
Continue reading as you walk your own dark road.
Two months ago, ostensible typing genius @EtaoinWu (Yue Wu) invented indirectly recursive type hints at #510. It probably wasn't even an accident. @EtaoinWu probably did it on purpose. Some people are like that. They just like smashing things with their brain hammers until something finally gives. This is that thing.
In the darkness of my man-lair, I realized that @EtaoinWu's approach can be generalized to create indirectly recursive type hints under Python ≤ 3.11. Since Python ≤ 3.11 fails to support PEP 695 recursive type
aliases, it was previously believed that recursive type hints could only be "officially" created under Python ≥ 3.12.
Not so. By abusing PEP 484 or PEP 585 generics, you can actually create recursive type hints under Python ≤ 3.11. These hints are fully PEP-compliant. They're valid. They satisfy typing
standards. Much like me, however, they're also super weird. You'll frown at them when you see them awkwardly shuffling past you on the sidewalk. You'll also have no choice but to use them if you want to type recursive data structures under Python ≤ 3.11.
To induce recursion without directly defining a PEP 695-compliant recursive type
alias, "simply":
- Define a normal unbound type variable.
- Define a normal PEP 484 or PEP 585 generic parametrized by that variable.
- Subscript that generic by itself.
Behold! This is indirect recursion via self-subscripting generics:
CAUTION: Merely reading this code abomination could cause your sanity to slip even further into the yawning abyss off the coast of California known only as R'lyeh.
# Import boring stuff you've long grown to loathe. Kinda sad, actually.
# Shouldn't boilerplate like this spark joy instead? I agree.
from beartype import beartype
from typing import TypeVar
# Define a normal unbound type variable. Enjoy it. This is the last normal
# one-liner you will ever see.
T = TypeVar('T')
# Define a normal PEP 585-compliant generic parametrized by that variable. Still
# normal. Still sane. Feels good. Yet, that feeling of unspeakable horror...
class GenericList(list[T]):
pass
# Define a type hint subscripting that generic... *BY ITSELF!?*
#
# Indeed. This type hint, for example, matches an infinitely recursive list
# (i.e., a list such that all items of this list are also infinitely recursive
# lists of the same type).
IndirectlyRecursiveList = GenericList[GenericList]
@beartype
def destroy_the_universe(with_danger_list: IndirectlyRecursiveList) -> None:
'''
Prove that @beartype will let you destroy the universe, if only your
intentions are pure.
'''
print(with_danger_list)
# Define an infinitely recursive non-empty list satisfying this type hint.
super_danger_list = IndirectlyRecursiveList()
super_danger_list.append(super_danger_list)
# Prove that @beartype accepts your risky life choices.
destroy_the_universe(super_danger_list)
# Define a boring non-recursive non-empty list violating this type hint.
super_boring_list = IndirectlyRecursiveList([
'Super', 'boring', 'list', 'hates', 'fun.'])
# Prove that @beartype rejects your safe life choices.
destroy_the_universe(super_boring_list)
...which prints the expected output and exception traceback:
[[...]]
Traceback (most recent call last):
File "/home/leycec/tmp/mopy.py", line 45, in <module>
destroy_the_universe(super_boring_list)
~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
File "<@beartype(__main__.destroy_the_universe) at 0x775b159d1440>", line 53, in destroy_the_universe
beartype.roar.BeartypeCallHintParamViolation: Function
__main__.destroy_the_universe() parameter
with_danger_list=['Super', 'boring', 'list', 'hates', 'fun.'] violates type hint
__main__.GenericList[__main__.GenericList], as generic superclass list[~T] of
<class "__main__.GenericList"> index 3 item str 'hates' not instance of
<class "__main__.GenericList">.
WOAH. The official repr()
string for an infinitely recursive list generic is [[...]]
. CPython just did that. We didn't do anything to make CPython do that. Somehow, that discovery is the coolest part of this whole changelog. I feel sad. 😭
Voila! You've just created a recursive type hint that works under literally all Python versions – including Python ≤ 3.11. Nobody intended for anyone to do this. Thanks to the sickening force of the human mind, you can now do this.
Kinda surprised that nobody ever thought to subscript a generic by itself. Or did they!? Yeah... they probably did. But no @beartype users ever did that or somebody would have pounded their fists on our issue tracker about that. Or would they!? Yeah... they probably would. 😅 💦
@beartype 0.21.0rc0
: these tears i shed for your code are manly
Does Anyone Even Care About Recursive Data Structures?
No idea. I care in the abstract sense of the word "care." Computer science is a super-fun literary puzzle with real-world implications – which makes it even funner than "normal" puzzles, which are still fun but don't touch the real world in any meaningful way. The lolz. That's what I'm saying. I did this for the lolz.
If you're reading this from the comfort of your PodBed™ in the Year 2075, please know that I did everything I could to make your life better. I solved puzzles. I meant well. Now, future human (or human-like AI construct), my future is your grim struggle for daily sustenance wondrous present in a utopian dream-world.
May this small piece of the recursive puzzle assist you in your own puzzle-wrangling.
@beartype 0.21.0rc0
: because nobody tells you what to do anymore
Hint Overrides: Break Type Hint Standards Over Your Knees, Because You Can
Previously, @beartype hint overrides sorta but not really worked. Now, @beartype hint overrides actually do work for all possible use cases. Of course, I never got around to documenting hint overrides.
But don't let that sensible obstacle that should deter you deter you! Use undocumented APIs. Live a little. Let your dangling docstrings hang all out.
Lie to your userbase (and yourself) by globally replacing type hints without anyone's consent or knowledge. Not sure why anyone would want to behave like this, honestly. Therefore, @beartype allows you to behave like this. We support bad habits and so should you:
# Import tons of weirdo @beartype stuff. Look. I don't know either.
from beartype import beartype, BeartypeConf, FrozenDict
# Define a new @beartype decorator named @riskytype. Unlike @beartype, @riskytpe
# performs dangerous type hint overrides by replacing all "list[str]" type hints
# with "list[str] | tuple[str, ...]". Feels good. Users can now pass tuples of
# strings to all callables annotated as accepting only lists of strings. waaat?
riskytype = beartype(conf=BeartypeConf(hint_overrides=FrozenDict({
list[str]: list[str] | tuple[str, ...]})))
# Define a risky function decorated by @riskytype. *gulp*
@riskytype
def risky_func(risky_arg: list[str]) -> str:
return risky_arg[0]
# Prove that this function walks on the wild side.
print(risky_func(['This is fine.', 'You can tell because of my sweaty hand.']))
print(risky_func(('This is totally sus.', 'Users are panicking already.')))
# Prove that this function still occasionally behaves itself.
print(risky_func({'This is beyond sus...', '...where not even @beartype dares.'}))
...which prints the expected output and exception traceback:
This is fine.
This is totally sus.
Traceback (most recent call last):
File "/home/leycec/tmp/mopy.py", line 23, in <module>
print(risky_func({'This is beyond sus...', '...where not even @beartype dares.'}))
~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<@beartype(__main__.risky_func) at 0x7370c39ce840>", line 48, in risky_func
beartype.roar.BeartypeCallHintParamViolation: Function __main__.risky_func()
parameter risky_arg={'This is beyond sus...', '...where not even @beartype dares.'}
violates type hint list[str], as set {'This is beyond sus...',
'...where not even @beartype dares.'} not list or tuple.
@beartype 0.21.0rc0
: when you feel the need to suck on a bottle in the darkness as an afro ninja looks on in shock
Dataclass Type-checking: Check Fields Like Its 2077
In the now-legendary GitHub poll "Tell @leycec What to Do", everybody told @leycec to type-check PEP 557 dataclass fields on assignment. Thus, @beartype 0.21.0rc0
now type-checks dataclasses... sorta.
That's sorta right. Sorta means this mostly works, but might not. Type-checking dataclasses is hard. I am soft-bellied and lazy. After combining these adjectives, you get half-hearted dataclass type-checking.
@beartype only conditionally type-checks dataclass fields when you explicitly tell @beartype to type-check dataclass fields by enabling our newly introduced BeartypeConf(is_pep557_fields: bool = False)
configuration option. For safety, this option is disabled by default. Ever since @beartype accidentally blew up PyTorch, your safety is our paramount concern. I can't have Microsoft breathing down my neckbeard again. Please! Not that...
When you want dataclass type-checking, you have to enable dataclass type-checking – like so:
# Import so many things you can barely see what matters anymore.
from beartype import beartype, BeartypeConf
from dataclasses import dataclass, InitVar
from typing import ClassVar
@beartype(conf=BeartypeConf(is_pep557_fields=True)) # <-- check it like a bear boss
@dataclass
class MuhDataclass(object):
muh_int: int
muh_classvar: ClassVar[str] = 'This is fine. Srsly. Does @leycec not have Buddha nature?'
muh_initvar: InitVar[bytes] = b'This is fine, too. No joke. No shade. No idea.'
# *GOOD.*
good_data = MuhDataclass(muh_int=42) # <-- this works
good_data.muh_int = 0xCAFEBABE # <------- this works, too
# *BAD.*
bad_data = MuhDataclass(muh_int=42) # <-- still works
bad_data.muh_int = '0xCAFEBABE' # <------ this fails! *YAY*!
...which raises the expected type-checking violation:
beartype.roar.BeartypeDecorHintParamDefaultViolation: Dataclass MuhDataclass(muh_int=42)
attribute 'muh_int' new value '0xCAFEBABE' violates type hint <class 'int'>,
as str '0xCAFEBABE' not instance of int.
As the above example demonstrates, this preliminary functionality supports cafe babes. Uhh... I mean, this supports:
- Default dataclasses decorated by the
@dataclass
decorator passed no keyword parameters. 👍 - Frozen dataclasses decorated by
@dataclass(frozen=True)
. 🫰 - Slotted dataclasses decorated by
@dataclass(slots=True)
. 🤑 - Standard dataclass fields annotated by either:
Truly, now you too can get Poor Man's Pydantic© for free from the comfort of your own AI-assisted keyboard while doing even less work than you ordinarily would while nursing a video game hangover on Sunday morning. @leycec did all the work for you and painfully regretted deeply cherished this character- and morale-building life lesson.
who did this to you, @beartype!?! oh, it was just dataclasses.
What's the Catch? What're You not Telling Us!?!?
Thanks to the non-triviality of dataclasses and my own moral failings (read: "I am laziness incarnate"), this functionality currently fails to support all possible dataclass configurations and use cases. Popular edge cases not supported include:
-
Dataclass subclasses (i.e., dataclasses subclassing other dataclasses). This is completely untested. No idea what happens. Could blow up everything. Could do nothing, which is better than just blowing up everything.
-
PEP 563 (i.e.,
from __future__ import annotations
), which almost certainly raises exceptions when enablingis_pep557_fields=True
. -
Dataclass fields annotated by one or more relative forward references (i.e., strings referring to the names of currently undefined types, subsequently defined in the current submodule), which almost certainly raises exceptions when enabling
is_pep557_fields=True
: e.g.,from dataclasses import dataclass @dataclass class UnsupportedDataclass(object): unsupported_field: 'UndefinedType' # <-- BREAKS EVERYTHING, YO class UndefinedType(object): pass
Until @beartype fully supports all of the above edge cases, is_pep557_field
will continue defaulting to False
. Someday, this will surely work for everybody. Until then, let us collectively sob. 😭
@beartype 0.21.0rc0
: teach a dev to crush bugs for a day and he'll crush bugs for a life
Frozen Dictionaries: The Core Type Python Denied You, Beartype Gives You
Python needs an official frozendict
implementation, if only to shut down continual demands for an official frozendict
implementation. Thankfully, you use @beartype.
@beartype 0.21.0rc0
now offers a public frozen dictionary type for you: beartype.FrozenDict
! It actually works! It's mostly still C-based and thus fast! We tested everything and then some! We stuffed everything inside these things and they still pretended to work! We use frozen dictionaries everywhere in the @beartype codebase! Now, so can you!
beartype.FrozenDict
: because Hell will freeze over before Python ever gets an official frozendict
implementation.
from beartype import FrozenDict
# Finally, a set of frozen dictionaries! Yes, it's all true. Now you too can use
# dictionaries as dictionaries keys or set members. Why? Because you can.
freezing_my_dict_off = {
FrozenDict({'My ganglia!': 'It hurts.'}),
FrozenDict({'What even is a ganglia?': 'No idea. But it surely hurts.'}),
}
@beartype 0.21.0rc0
: we heard you wanted some FrozenDict
with your FrozenDict
Lastly but Beastly (but not Leastly)...
...to financially feed @leycec and his friendly @beartype through our ancient GitHub Sponsors profile that predates the existence of dinosaur-like AI chatbots. Come for the candid insider photos of a sordid and disreputable life in the Canadian interior; stay for the GitHub badge and warm feelings of general goodwill.
Cue hypnagogic rave music that encourages fiscal irresponsibility. 🎵 🎹 🎶
Bear Club: The First Rule of Bear Club Is You Crush Bugs
@beartype high-fives the reclusive secret society of worldwide bear bros who might possibly care about this. You are the select few. The elect enlightened. You are:
@posita, @wesselb, @tusharsadhwani, @JWCS, @patrick-kidger, @EtaoinWu, @iamrecursion, @Moosems, @langfield, @sylvorg, @mzealey, @thetianshuhuang, @RomainBrault, @ddorian, @rg936672, @alisaifee, @ArneBachmannDLR.
The burden of QA is high – but you have chosen to carry the smelly torch. Keep that suspiciously purple flame alive! The recursive data structure you crush the bugs out of tonight may very well be your own.
@beartype 0.21.0rc0
stands poetically before the burning wreckage of your competitor's codebase