Skip to content

fix(deps): update dependency beartype to >=0.21.0,<0.22.0 #21

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

renovate[bot]
Copy link

@renovate renovate bot commented Mar 5, 2025

This PR contains the following updates:

Package Type Update Change Age Adoption Passing Confidence
beartype (source) dependencies minor >=0.19.0,<0.20.0 -> >=0.21.0,<0.22.0 age adoption passing confidence
beartype (changelog) project.dependencies minor >=0.19.0,<0.20.0 -> >=0.21.0,<0.22.0 age adoption passing confidence

Warning

Some dependencies could not be looked up. Check the Dependency Dashboard for more information.


Release Notes

beartype/beartype (beartype)

v0.21.0: Beartype 0.21.0: Curses, It's Recursion!

Beartype 0.21.0 consoles your codebase as it shudders under the oppressive tidal wave of bugs. Much like its predecessors, @​beartype 0.21.0 is here to help. Unlike its predecessors, @​beartype 0.21.0 claims it solves more problems than it creates for once. Is @​beartype 0.21.0.... lying!? 🫢

pip install --upgrade 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.0: this can't be what you've waited months for

@​beartype 0.21.0 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:

Thanks so much, masters of fintech.

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.0 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.0 is for that somebody. When you need recursion, you need @​beartype 0.21.0.

@​beartype 0.21.0 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.0 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.0: 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.0 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. The only catch? This unworldly magic needs Python ≥ 3.12, which is quite the catch indeed. Behold! Unworldly magic:

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 enabling `is_pep557_fields=True` – much to the dismay of everybody else in the office. The only catch? Actually, there are multiple catches. No relative forward references and no [PEP 563][] support means no `from __future__ import annotations`. That's why dataclass field checking remains disabled by default. You have to opt in, because the best things in life are dangerous and reckless and hurt a lot. Like, *a lot* a lot:

```python

##### @&#8203;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:

```python
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.0: 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.0 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][]:

```python

##### 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.0: 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]

##### @&#8203;beartype now type-checks this infinitely recursive list in O(1) time. Of
##### course, that's impossible. But @&#8203;beartype doesn't even care anymore! F- it!
from beartype import beartype
@&#8203;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 "<@&#8203;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.0: "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.0 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.0: 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.0: 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:

  1. Type-checks recursive data structures to only a single level of nested recursion.
  2. 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 height k would necessitate linear-time O(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 recursive type alias for each function call. Space and time efficiency rapidly spirals into the gutter and then clutches its aching head like in a depressing Leaving Los Vegas Silicon Valley scene.

Only Python ≥ 3.12!? Only one layer of recursion!?

@​beartype 0.21.0: 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":

  1. Define a normal unbound type variable.
  2. Define a normal PEP 484 or PEP 585 generic parametrized by that variable.
  3. 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]

@&#8203;beartype
def destroy_the_universe(with_danger_list: IndirectlyRecursiveList) -> None:
    '''
    Prove that @&#8203;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 @&#8203;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 @&#8203;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 "<@&#8203;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.0: 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.0: 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 @&#8203;beartype stuff. Look. I don't know either.
from beartype import beartype, BeartypeConf, FrozenDict

##### Define a new @&#8203;beartype decorator named @&#8203;riskytype. Unlike @&#8203;beartype, @&#8203;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 @&#8203;riskytype. *gulp*
@&#8203;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 @&#8203;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 @&#8203;beartype dares.'}))
          ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<@&#8203;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 @&#8203;beartype dares.'}
violates type hint list[str], as set {'This is beyond sus...',
'...where not even @&#8203;beartype dares.'} not list or tuple.

@​beartype 0.21.0: 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.0 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

@&#8203;beartype(conf=BeartypeConf(is_pep557_fields=True))  # <-- check it like a bear boss
@&#8203;dataclass
class MuhDataclass(object):
    muh_int: int
    muh_classvar: ClassVar[str] = 'This is fine. Srsly. Does @&#8203;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:
    • Standard PEP-compliant hintsexcept relative forward references, which are currently unsupported. (See below! It sucks.) 😢
    • PEP 526 typing.ClassVar[...] type hints. 🥰
    • PEP 557 dataclasses.InitVar[...] type hints. 🏩

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 enabling is_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
    
    @&#8203;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.0: 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.0 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.0: we heard you wanted some FrozenDict with your FrozenDict

Lastly but Beastly (but not Leastly)...

we doin' this

...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, @​felix-hilden, @​simonprovost, @​JWCS, @​patrick-kidger, @​EtaoinWu, @​iamrecursion, @​Moosems, @​langfield, @​sylvorg, @​mzealey, @​thetianshuhuang, @​RomainBrault, @​ddorian, @​rg936672, @​alisaifee, @​ArneBachmannDLR, @​JelleZijlstra, @​tactile-metrology, @​RobPasMue, @​GithubCamouflaged, @​kloczek, @​uriyasama, @​danielgafni, @​JWCS, @​rbroderi, @​AlanCoding, @​tvdboom, @​crypdick, @​jvesely, @​komodovaran, @​kaparoo, @​MaximilienLC, @​fleimgruber, @​alexoshin, @​gabrieldemarmiesse, @​James4Ever0, @​NLPShenanigans, @​rtbs-dev, @​yurivict, @​st--, @​murphyk, @​dosisod, @​Rogdham, @​alisaifee, @​denisrosset, @​damarro3, @​ruancomelli, @​jondequinor, @​harshita-gupta, @​jakebailey, @​denballakh, @​jaanli, @​creatorrr, @​msvensson222, @​avolchek, @​femtomc, @​AdrienPensart, @​jakelongo, @​Artur-Galstyan, @​ArneBachmann, @​danielward27, @​WeepingClown13, @​rbnhd, @​radomirgr, @​rwiegan, @​brettc, @​spagdoon0411, @​helderco, @​paulwouters, @​jamesbraza, @​dcharatan, @​kasium, @​AdrienPensart, @​sunildkumar, @​peske, @​mentalisttraceur, @​awf, @​PhilipVinc, @​dcharatan, @​empyrealapp, @​rlkelly, @​KyleKing, @​skeggse, @​RomainBrault, @​deepyaman, @​adamtheturtle, @​minmax, @​jedie, @​pablovela5620, @​thiswillbeyourgithub, @​Logan-Pageler, @​knyazer, @​ilyapoz, @​yuzhichang, @​Fedezzab, @​antonioan, @​im-Kitsch, @​mthramann, @​fbartolic, @​rgallardone, @​frrad, @​jonnyhyman, @​jennydaman, @​likewei92, @​acec2127, @​Glinte, @​rudimichal, @​woutdenolf, @​PauloHMTeixeira.

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.0 stands poetically before the burning wreckage of your competitor's codebase

v0.20.2: Beartype 0.20.2: Endless Version Churn Churns Versions Endlessly

@​beartype 0.20.2 teleports seemingly from out of nowhere into the former safety of your Ungendered Person Cave™, where not even smelly bears that eat all your bugs are welcome:

pip install --upgrade beartype

@​beartype 0.20.2 apologizes for the bald-faced transgressions of @​beartype 0.20.1, which tried to resolve issue #​512 for @​rg936672 but mostly did nothing except sit around and read fantasy books. @​beartype 0.20.2 would prefer it if @​beartype 0.20.1 were never spoken of again. "What @​beartype 0.20.1?", am I right?

@​beartype 0.20.2 and your moral compass knows I'm right. Which is why...

@​beartype 0.20.2 is 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:

Thanks so much, masters of fintech and metrology.

The Masters of Fintech and Metrology. That's who.

What's This All About, Then?

It's better you not know about issues #​512 or #​514. In the land of the blind bug-crusher, ignorance is bliss and the one-eyed @​beartype is king. By the overwhelming power of ignorance, no problems never existed. "What problems?", am I right?

Wait. Does "no problems never existed" actually mean "some problems always existed"? Double negatives destroy yet another convincing one-liner. Thanks fer nuffin', incomprehensible English language!

Lastly but Beastly (but not Leastly)...

we doin' this

...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 Second Rule of Bear Club Is You Crush Bugs

Bear Club welcomes long-standing user @​rg936672 to bathe in the lukewarm ambiance wafting off @​beartype 0.20.2. It feels good. Therefore, it is good.

v0.20.1: Beartype 0.20.1: Be Astonished! It's... @​beartype?

@​beartype 0.20.1 materializes before your astonished keyboard with an explosive fizzy sizzling not unlike that of a flimsy soda can bursting its aluminum seams all over your astonished keyboard:

pip install --upgrade beartype

Because I am a bald middle-aged man, I still use pip. Someday I will update my priors and switch to uv. Someday... but not today. 🥹

@​beartype 0.20.1 brings a ton of fun stuff! It almost made the cut for a full-fledged minor @​beartype 0.21.0 release. In the end, I couldn't bear the thought of another never-ending release candidate cycle. And neither could you. Which is why...

@​beartype 0.20.1 is the shark in this metaphor. pretty sure that makes you the diver.

@​beartype 0.20.1 is 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:

Thanks so much, masters of fintech and metrology.

The Masters of Fintech and Metrology. That's who.

Let's start with the good stuff.

Dataclass Type-checking: Everybody Gets What Everybody Wants

In the now-legendary GitHub poll "Tell @​leycec What to Do", everybody told @​leycec to type-check dataclasses. Thus, @​beartype 0.20.1 now type-checks dataclasses... sorta.

Type-checking dataclasses is hard. I am soft-bellied and lazy. After combining these things, you get half-hearted dataclass type-checking. @​beartype only conditionally type-checks dataclasses when you explicitly tell @​beartype to type-check dataclasses by enabling our newly introduced BeartypeConf(is_check_pep557: bool = False) configuration option. For safety, this option is disabled by default.

When you want dataclass type-checking, you have to enable dataclass type-checking – like so:

from beartype import beartype, BeartypeConf
from dataclasses import dataclass, InitVar
from typing import ClassVar

@&#8203;beartype(conf=BeartypeConf(is_check_pep557=True))  # <-- check it like a boss
@&#8203;dataclass
class MuhDataclass(object):
    muh_int: int
    muh_classvar: ClassVar[str] = 'This is fine. Srsly. Does @&#8203;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' value '0xCAFEBABE' violates type hint <class 'int'>,
as str '0xCAFEBABE' not instance of int.

As the above example demonstrates, this preliminary functionality supports standard dataclass fields annotated by either:

  • Standard PEP-compliant hints – except relative forward references, which are currently unsupported. (See below! It sucks.) 😢
  • PEP 526-compliant typing.ClassVar[...] type hints. 🥰
  • PEP 557-compliant dataclasses.InitVar[...] type hints. 😸

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:

  • Frozen dataclasses (e.g., @dataclass(frozen=True)), which are currently guaranteed to raise exceptions when enabling is_check_pep557=True.

  • Slotted dataclasses (e.g., @dataclass(slots=True)), which are currently guaranteed to raise exceptions when enabling is_check_pep557=True.

  • 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 enabling is_check_pep557=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_check_pep557=True: e.g.,

    from dataclasses import dataclass
    
    @&#8203;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_check_pep557 will continue defaulting to False. Someday, this will surely work for everybody. Until then, let us collectively sob. 😭

teach a dev to crush bugs for a day and he'll crush bugs for a life

The Click Ecosystem: Now Best Friends with Beartype

Previously, @​beartype only supported the core Click project for generating Pythonic CLIs and TUIs. Now, @​beartype 0.20.1 extensibly supports the full Click ecosystem of Click-based projects – including the new BigBoy™, rich-click. Combine the venerable powers of Click + Rich. Truly, your app will own the terminal:

from beartype import beartype
from rich_click import command

@&#8203;beartype  # <-- actually works now. i know. i'm stunned, too.
@&#8203;command()
def do_something_for_leycecs_sake() -> int:
    return 0xFEEDFACE  # Feed that face! Feed it good.

your silky smooth Rich TUI has never felt so good

That's it, folks! That's all we've got. It wasn't much, but it was still more than you wanted on a Friday morning. This has been @​beartype 0.20.1. Thanks so much for having us. If anyone needs us, we'll be playing video games and listening to Finnish viking metal all night. 🤘

Lastly but Beastly (but not Leastly)...

we doin' this

...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

Bear Club welcomes long-standing users @​RomainBrault, @​rg936672, and [@


Configuration

📅 Schedule: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about these updates again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate bot added the dependencies Pull requests that update a dependency file label Mar 5, 2025
@renovate renovate bot force-pushed the renovate/beartype-0.x branch from 58f409d to 062d899 Compare March 9, 2025 04:30
@renovate renovate bot changed the base branch from main to beta March 9, 2025 04:30
@renovate renovate bot force-pushed the renovate/beartype-0.x branch from 062d899 to 5d6be96 Compare March 9, 2025 05:33
@renovate renovate bot changed the base branch from beta to main March 9, 2025 05:33
@renovate renovate bot force-pushed the renovate/beartype-0.x branch from 5d6be96 to b67c499 Compare March 21, 2025 17:08
@renovate renovate bot changed the title fix(deps): update dependency beartype to >=0.20.0,<0.21.0 fix(deps): update dependency beartype to >=0.20.1,<0.21.0 Mar 21, 2025
@renovate renovate bot force-pushed the renovate/beartype-0.x branch from b67c499 to d1581b6 Compare March 22, 2025 19:37
@renovate renovate bot changed the title fix(deps): update dependency beartype to >=0.20.1,<0.21.0 fix(deps): update dependency beartype to >=0.20.2,<0.21.0 Mar 22, 2025
@renovate renovate bot force-pushed the renovate/beartype-0.x branch from d1581b6 to 104fbf8 Compare April 12, 2025 07:41
@renovate renovate bot force-pushed the renovate/beartype-0.x branch from 104fbf8 to 1aec2d0 Compare May 24, 2025 08:21
@renovate renovate bot changed the title fix(deps): update dependency beartype to >=0.20.2,<0.21.0 fix(deps): update dependency beartype to >=0.21.0,<0.22.0 May 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dependencies Pull requests that update a dependency file
Projects
None yet
Development

Successfully merging this pull request may close these issues.

0 participants