question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[FAQ] Document how to disable @beartype

See original GitHub issue

For example, do we ever intend it to detect the issue in either of these:

from beartype import beartype

@beartype
def fn(n: int) -> None:
    n = "waffle"

@beartype
def fn2() -> None: 
    n = 5
    n = "waffle"

Or is that out of the scope of the project?

Basically, I would like to be able to completely ditch mypy in favor of this project, and this is what’s holding me back.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:1
  • Comments:13 (7 by maintainers)

github_iconTop GitHub Comments

2reactions
TeamSpen210commented, Feb 12, 2022

One thing I did notice while looking through the __debug__ handling is that since all of beartype is contained in submodules, you could move the __debug__ check into beartype.__init__, and then only import the actual core components if __debug__ is off. Then in “optimised” mode, most of beartype won’t even need to be loaded, probably just the config if anything. It’d probably be negligible, but might as well.

2reactions
leyceccommented, Feb 12, 2022

Superb repeated saves by @TeamSpen210 – as expected of the greatest issue outfielder of all time.

So, we’ve dramatically leapt off of the original issue of static type checking and are now hip-deep in tangentially unrelated but nonetheless interesting waters: namely, “How do I curbstomp @beartype from doing what it wants to do?”

Let’s see if we can’t incrementally unpack this Gordion knot.

Will python’s -O flag skip runtime checking and reduce its cost to an empty function call?

Yup. This is happening already.

Is there any sense in adding a finer resolution way of turning off runtime checking, such as turning it off for some places/types/functions and not others?

Yup. This is happening already. Specifically, our new configuration API released under beartype 0.10.0 and only documented here currently (just 'cause) provides a new O(0) no-time type-checking strategy that selectively disables specific @beartype decorations enabling that strategy.

Example or it didn’t happen, so:

# Import the requisite machinery.
from beartype import (
    beartype,
    BeartypeConf,
    BeartypeStrategy,
)

# Global runtime type-checking strategy, where
# "I_AM_A_RELEASE_BUILD" is externally defined
# by your app stack to be True for release builds.
# Specifically:
# * If a release build, reduce @beartype to a noop
#   via the no-time type-checking strategy.
# * If a debug build, default @beartype to the usual
#   constant-time type-checking strategy.
_beartype_strategy = (
    BeartypeStrategy.O0
    if I_AM_A_RELEASE_BUILD else
    BeartypeStrategy.O1
)

# Beartype decorator configured with this strategy.
# Use this rather than the vanilla @beartype
# decorator everywhere throughout your app stack.
goodbeartype = beartype(
    conf=BeartypeConf(_beartype_strategy))

# Use @goodbeartype rather than @beartype everywhere.
@goodbeartype
def muh_performance_critical_func(big_list: list[int]) -> int:
    return len(big_list)

That’s actually useful fodder for a new FAQ entry. 🤔

Of course, it’s probably pertinent to note here that @beartype is so fast and adds so little runtime overhead that it will never be a performance bottleneck – unless you’re implementing a real-time digital signal processing operating system in pure-Python, in which case I concede the point.

Seriously. We’re unbelievably fast. Profile us if you don’t believe us – which you shouldn’t, because your app stack depends on us telling the truth. Never trust anyone. Not even me.

Actually if annotations get evaluated when __debug__ is False it is significant with large data.

Fascinating. Absolutely no snark here, because customer is king. You are customer, ergo king. Still… Have you actually profiled annotations as consuming non-trivial space for your use case or is this a bit of worst-case theory-crafting?

The reason I ask is that type hints originating from the standard typing module are self-memoizing and thus extremely space-efficient. For example, if you repeatedly annotate callables with the typing.List[str] type hint, only one instance of that hint is instantiated for the lifetime of the active Python process: e.g.,

>>> import typing
>>> typing.List[str] is typing.List[str]
True

Of course, PEP 585 deprecates typing.List by list. Is list[str] similarly self-caching? Nope. CPython devs completely dropped the ball here, because they’re replacing something good (i.e., self-memoizing PEP 484 type hints) with something less good (i.e., non-self-memoizing PEP 585 type hints):

>>> list[str] is list[str]
False

You are now possibly cogitating: “Well, this is all a great ball of steaming matter of a nondescript nature.” You’re not wrong. Fortunately, @beartype solves that issue, too. Internally, @beartype caches all non-self-memoizing PEP 585 type hints. So, when you use @beartype, there’s actually no space costs for either PEP 484 or 585 type hints.

Great, right? Almost. Remember that no-time type-checking strategy we enabled above? Okay. When we say “no-time,” we mean literally that. @beartype immediately reduces to a noop when configured by that strategy. This means it does nothing, because doing something would violate the meaning of “no-time,” right?

Unfortunately, doing something includes (wait for it) caching all non-self-memoizing PEP 585 type hints. That no longer happens, so PEP 585 type hints will suddenly begin consuming space when you disable @beartype.

Of course… that’s why you shouldn’t disable @beartype unless you’re certain you need to. Again, please profile @beartype against your usual real-world use case. If we’re surprisingly slow, we’ll do our utmost to resolve that for you and the world.

Back to the question at hand. Let’s suppose you actually have profiled:

  • @beartype and it’s stupidly slow, so you need to disable it with the above no-time strategy in release builds.
  • Annotations and they’re stupidly big, so you also need to disable them.

One or both of these are probably not going to be the case. But let’s suppose they are. In other words, we are now on the worst historical timeline and the Four Arthritic Horsemen of the apocalypse are now rearing outside your bedroom window.

Can you do anything about that? You can – but you really don’t want to. As @TeamSpen210 yet again correctly suggests, you can manually litter the top of every Python module across your entire app stack with a futures import enabling PEP 563:

from __future__ import annotations

Please don’t do that. Although @beartype technically is the most PEP 563-compliant runtime type checker, enabling PEP 563 will:

  • (A) Break everything else that depends on annotations (e.g., pydantic).
  • (B) Introduce weird corner case subtleties that could conceivably break @beartype (when you do actually run it). Nested callables and closures are especially susceptible to breaking here.
  • © Dramatically slow down @beartype (when you do actually run it).

tl;dr

Please profile. We promise you’ll be pleasantly surprised.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Unbearably fast near-real-time runtime type-checking ... - GitHub
Beartype by fabricating your own PEP-compliant beartype validators, enabling beartype to validate arbitrary objects against actual Boto3 types at runtime when ...
Read more >
beartype - PyPI
Beartype is an open-source pure-Python runtime type checker emphasizing efficiency, portability, and thrilling puns.
Read more >
Beartype: Unbearably fast O(1) runtime type-checking in pure ...
Beartype : Unbearably fast O(1) runtime type-checking in pure Python. Beartype is an open-source pure-Python PEP-compliant constant-time runtime type checker ...
Read more >
beartype: Documentation - Openbase
So let's do this. $ python3 .. code-block:: python. Import the @beartype decorator. from beartype import beartype ...
Read more >
How to uninstall or remove bear software package from ...
You can uninstall or removes an installed bear package itself from Ubuntu 16.04 LTS (Xenial Xerus) through the terminal,. $ sudo apt-get remove...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found