Beartype Import Hooks

Beartype import hooks enforce type hints across your entire app in two lines of code with no runtime overhead. This is beartype import hooks in ten seconds. dyslexia notwithstanding

# Add *ONE* of the following semantically equivalent two-liners to the very
# top of your "{your_package}.__init__" submodule. Start with *THE FAST WAY*.

# ....................{ THE FAST WAY                    }....................
from beartype.claw import beartype_this_package  # <-- this is boring, but...
beartype_this_package()                          # <-- the fast way

# ....................{ THE LESS FAST WAY               }....................
from beartype.claw import beartype_package       # <-- still boring, but...
beartype_package('{your_package}')               # <-- the less fast way

# ....................{ THE MORE SLOW WAY               }....................
from beartype.claw import beartype_packages      # <-- boring intensifies
beartype_packages(('{your_package}',))           # <-- the more slow way

Beartype import hooks extend the surprisingly sharp claws of beartype to your full app stack, whether anyone else wanted you to do that or not. Claw your way to the top of the bug heap; then sit on that heap with a smug expression. Do it for the new guy sobbing quietly in his cubicle.

Import Hooks Overview

Beartype import hooks implicitly perform both:

  • Standard runtime type-checking (ala the beartype.beartype() decorator).

  • Standard static type-checking (ala mypy and pyright) but at runtime – and that ain’t standard.

Automate the beartype.beartype() decorator away today with magical import hooks published by the beartype.claw subpackage. When you install import hooks from beartype, you augment beartype from a pure-runtime second-generation type-checker into a hybrid runtime-static third-generation type-checker. That’s right.

Beartype is now a tentacular cyberpunk horror like that mutant brain baby from Katsuhiro Otomo’s dystopian 80’s masterpiece Akira. You can’t look away!

mutant brain baby

May Neo-Tokyo have mercy on your codebase’s soul.

Import Hooks Overview, Part Deux

Beartype import hooks is a hobbit hole so deep we had to deescalate it with decrepit manga panels from Akira. Prepare to enter that hole.

What Is beartype_this_package()?

Let’s begin by outlining exactly what beartype_this_package() does.

As the simplest and most convenient of several import hooks published by the beartype.claw subpackage, beartype_this_package() type-checks all subsequently imported submodules of {your_package}. Notably, beartype_this_package():

  • Implicitly decorates all callables and classes across {your_package} by the beartype.beartype() decorator. Rejoice, fellow mammals! You no longer need to explicitly decorate anything by beartype.beartype() ever again. Of course, you can if you want to – but there’s no compelling reason to do so and many compelling reasons not to do so. You have probably just thought of five, but there are even more.

  • Implicitly appends every PEP 526-compliant annotated variable assignment (e.g., muh_int: int = 'Pretty sure this isn't an integer, but not sure.') across {your_package} by a new statement at the same indentation level calling the beartype.door.die_if_unbearable() function passed both that variable and that type hint. Never do that manually. Now, you never do.

Examples or we’re lying again. beartype_this_package() transforms your {your_package}.{buggy_submodule} from this quietly broken code that you insist you never knew about, you swear:

# This is "{your_package}.{buggy_submodule}". It is bad, but you never knew.
import typing as t

bad_global: int = 'My eyes! The goggles do nothing.'  # <-- no exception

def bad_function() -> str:
    return b"I could've been somebody, instead of a bum byte string."
bad_function()  # <-- no exception

class BadClass(object):
    def bad_method(self) -> t.NoReturn:
        return 'Nobody puts BadClass in the corner.'
BadClass().bad_method()  # <-- no exception

…into this loudly broken code that even your unionized QA team can no longer ignore:

# This is "{your_package}.{buggy_submodule}" on beartype_this_package().
# Any questions? Actually, that was rhetorical. No questions, please.
from beartype import beartype
from beartype.door import die_if_unbearable
import typing as t

bad_global: int = 'My eyes! The goggles do nothing.'
die_if_unbearable(bad_global, int)  # <-- raises exception

@beartype
def bad_function() -> str:
    return b"I could've been somebody, instead of a bum byte string."
bad_function()  # <-- raises exception

@beartype
class BadClass(object):
    def bad_method(self) -> t.NoReturn:
        return 'Nobody puts BadClass in the corner.'
BadClass().bad_method()  # <-- raises exception

By doing nothing, you saved five lines of extraneous boilerplate you no longer need to maintain, preserved DRY (Don’t Repeat Yourself), and mended your coworker’s career, who you would have blamed for all this. You had nothing to do with that code. It’s a nothingburger!

Beartype believes you. This is why we beartype_this_package().

looks kinda bad

This is what happens when we don’t beartype_this_package().

Why Is beartype_this_package()?

Let’s continue by justifying why you want to use beartype_this_package(). Don’t worry. The “why?” is easier than the “what?”. It often is. The answer is: “Safety is my middle name.” <– more lies

beartype_this_package() isolates its bug-hunting action to the current package. This is what everyone wants to try first. Type-checking only your first-party package under your control is the safest course of action, because you rigorously stress-tested your package with beartype. You did, didn’t you? You’re not making us look bad here? Don’t make us look bad. We already have GitHub and Reddit for that.

Other beartype import hooks – like beartype_packages() or beartyping() – can be (mis)used to dangerously type-check other third-party packages outside your control that have probably never been stress-tested with beartype. Those packages could raise type-checking violations at runtime that you have no control over. If they don’t now, they could later. Forward compatibility is out the window. git blame has things to say about that.

If beartype_this_package() fails, there is no hope for your package. Even though it might be beartype’s fault, beartype will still blame you for its mistakes.

Import Hooks API

Beartype import hooks come in two flavours:

  • Global import hooks, whose effects encompass all subsequently imported packages and modules matching various patterns.

  • Local import hooks, whose effects are isolated to only specific packages and modules imported inside specific blocks of code. Any subsequently imported packages and modules remain unaffected.

Global Import Hooks

Global beartype import hooks are… well, global. Their claws extend to a horizontal slice of your full stack. These hooks globally type-check all annotated callables, classes, and variable assignments in all subsequently imported packages and modules matching various patterns.

With great globality comes great responsibility.

beartype.claw.beartype_this_package(*, conf: beartype.BeartypeConf = beartype.BeartypeConf()) None[source]
Parameters:

conf (beartype.BeartypeConf) – Beartype configuration. Defaults to the default configuration performing \(O(1)\) type-checking.

Raises:

beartype.roar.BeartypeClawHookException

If either:

  • This function is not called from a module (i.e., this function is called directly from within a read–eval–print loop (REPL)).

  • conf is not a beartype configuration.

Self-package runtime-static type-checking import hook. This hook accepts no package or module names, instead type-checking all annotated callables, classes, and variable assignments across all submodules of the current package (i.e., the caller-defined package directly calling this function).

This hook only applies to subsequent imports performed after this hook, as the term “import hook” implies; previously imported submodules and subpackages remain unaffected.

This hook is typically called as the first statement in the __init__ submodule of whichever (sub)package you would like to type-check. If you call this hook from:

  • Your top-level {your_package}.__init__ submodule, this hook type-checks your entire package. This includes all submodules and subpackages across your entire package.

  • Some mid-level {your_package}.{your_subpackage}.__init__ submodule, this hook type-checks only that subpackage. This includes only submodules and subsubpackages of that subpackage. All other submodules and subpackages of your package remain unaffected (i.e., will not be type-checked).

# At the top of your "{your_package}.__init__" submodule:
from beartype import BeartypeConf                # <-- boilerplate
from beartype.claw import beartype_this_package  # <-- boilerplate: the revenge
beartype_this_package(conf=BeartypeConf(is_color=False))  # <-- no color is best color

This hook is effectively syntactic sugar for the following idiomatic one-liners that are so cumbersome, fragile, and unreadable that no one should even be reading this:

beartype_this_package()                            # <-- this...
beartype_package(__name__.rpartition('.')[0])      # <-- ...is equivalent to this...
beartype_packages((__name__.rpartition('.')[0],))  # <-- ...is equivalent to this.

When in doubt, have no doubt. Just call beartype_this_package().

New in version 0.15.0.

fierce determined face

beartype_this_package(): It do be like that.

beartype.claw.beartype_package(package_name: str, *, conf: beartype.BeartypeConf = beartype.BeartypeConf()) None[source]
Parameters:
  • package_name (str) – Absolute name of the package or module to be type-checked.

  • conf (beartype.BeartypeConf) – Beartype configuration. Defaults to the default configuration performing \(O(1)\) type-checking.

Raises:

beartype.roar.BeartypeClawHookException

If either:

  • conf is not a beartype configuration.

  • package_name is either:

    • Not a string.

    • The empty string.

    • A non-empty string that is not a valid package or module name (i.e., "."-delimited concatenation of valid Python identifiers).

Uni-package runtime-static type-checking import hook. This hook accepts only a single package or single module name, type-checking all annotated callables, classes, and variable assignments across either:

  • If the passed name is that of a (sub)package, all submodules of that (sub)package.

  • If the passed name is that of a (sub)module, only that (sub)module.

This hook should be called before that package or module is imported; when erroneously called after that package or module is imported, this hook silently reduces to a noop (i.e., does nothing regardless of how many times you squint at it suspiciously).

This hook is typically called as the first statement in the __init__ submodule of your top-level {your_package}.__init__ submodule.

# At the top of your "{your_package}.__init__" submodule:
from beartype import BeartypeConf           # <-- <Ctrl-c> <Ctrl-v>
from beartype.claw import beartype_package  # <-- <Ctrl-c> <Ctrl-v> x 2
beartype_package('your_package', conf=BeartypeConf(is_debug=True))
                # ^-- they said explicit is better than implicit,
                #     but all i got was this t-shirt and a hicky.

Of course, that’s fairly worthless. Just call beartype_this_package(), right? But what if you want to type-check just one subpackage or submodule of your package rather than your entire package? In that case, beartype_this_package() is overbearing. badum ching Enter beartype_package(), the outer limits of QA where you control the horizontal and the vertical:

# Just because you can do something, means you should do something.
beartype_package('good_package.m.A.A.d_submodule')  # <-- fine-grained precision strike

beartype_package() shows it true worth, however, in type-checking other people’s code. Because the beartype.claw API is a permissive Sarlacc pit, beartype_package() happily accepts the absolute name of any package or module – whether they wanted you to do that or not:

# Whenever you want to break something over your knee, never leave your
# favorite IDE [read: Vim] without beartype_package().
beartype_package('somebody_elses_package')  # <-- blow it up like you just don't care

This hook is effectively syntactic sugar for passing the beartype_packages() function a 1-tuple containing only this package or module name.

beartype_package('your_package')      # <-- this...
beartype_packages(('your_package',))  # <-- ...is equivalent to this.

Pretend you didn’t see that. Just call beartype_package().

New in version 0.15.0.

wizened psychic baby lady

Truer words were never spoken, wizened psychic baby lady.

beartype.claw.beartype_packages(package_names: collections.abc.Iterable[str], *, conf: beartype.BeartypeConf = beartype.BeartypeConf()) None[source]
Parameters:
  • package_name (collections.abc.Iterable[str]) – Iterable of the absolute names of one or more packages or modules to be type-checked.

  • conf (beartype.BeartypeConf) – Beartype configuration. Defaults to the default configuration performing \(O(1)\) type-checking.

Raises:

beartype.roar.BeartypeClawHookException

If either:

  • conf is not a beartype configuration.

  • package_names is either:

    • Not an iterable.

    • The empty iterable.

    • A non-empty iterable containing at least one item that is either:

      • Not a string.

      • The empty string.

      • A non-empty string that is not a valid package or module name (i.e., "."-delimited concatenation of valid Python identifiers).

Multi-package runtime-static type-checking import hook. This hook accepts one or more package and module names in any arbitrary order (i.e., order is insignificant), type-checking all annotated callables, classes, and variable assignments across:

  • For each passed name that is a (sub)package, all submodules of that (sub)package.

  • For each passed name that is a (sub)module, only that (sub)module.

This hook should be called before those packages and modules are imported; when erroneously called after those packages and modules are imported, this hook silently reduces to a noop. Squinting still does nothing.

This hook is typically called as the first statement in the __init__ submodule of your top-level {your_package}.__init__ submodule.

# At the top of your "{your_package}.__init__" submodule:
from beartype import BeartypeConf            # <-- copy-pasta
from beartype.claw import beartype_packages  # <-- copy-pasta intensifies
beartype_packages((
    'your_package',
    'some_package.published_by.the_rogue_ai.Johnny_Twobits',  # <-- seems trustworthy
    'numpy',  # <-- ...heh. no one knows what will happen here!
    'scipy',  # <-- ...but we can guess, can't we? *sigh*
), conf=BeartypeConf(is_pep484_tower=True))  # <-- so. u 2 h8 precision.

This hook is the penultimate force in global import hooks. The terser beartype_this_package() and beartype_package() hooks are effectively syntactic sugar for this verboser hook.

One hook to QA them all, and in the darkness of your codebase bind them.

New in version 0.15.0.

it's the end of the road as we know it, and i feel fine

It’s almost as if we know what “penultimate” means.

beartype.claw.beartype_all(*, conf: beartype.BeartypeConf = beartype.BeartypeConf()) None[source]
Parameters:

conf (beartype.BeartypeConf) – Beartype configuration. Defaults to the default configuration performing \(O(1)\) type-checking.

Raises:

beartype.roar.BeartypeClawHookException – If conf is not a beartype configuration.

All-packages runtime-static type-checking import hook. This hook accepts no package or module names, instead type-checking all callables, classes, and variable assignments across all submodules of all packages.

This hook should be called before those packages and modules are imported; when erroneously called after those packages and modules are imported, this hook silently reduces to a noop. Not even squinting can help you now.

This hook is typically called as the first statement in the __init__ submodule of your top-level {your_package}.__init__ submodule.

# At the top of your "{your_package}.__init__" submodule,
from beartype import BeartypeConf       # <-- @beartype seemed so innocent, once
from beartype.claw import beartype_all  # <-- where did it all go wrong?
beartype_all(conf=BeartypeConf(claw_is_pep526=False))  # <-- U WILL BE ASSIMILATE

This hook is the ultimate import hook, spasmodically unleashing a wave of bug-defenestrating action over the entire Python ecosystem. After calling this hook, any package or module authored by anybody (including packages and modules in CPython’s standard library) will be subject to the iron claw of beartype.claw. Its rule is law!

This hook is the runtime equivalent of a full-blown pure-static type-checker like mypy or pyright, enabling full-stack runtime-static type-checking over your entire app. This includes submodules defined by both:

  • First-party proprietary packages authored explicitly for this app.

  • Third-party open-source packages authored and maintained elsewhere.

Nothing is isolated. Everything is permanent. Do not trust this hook.

Caveat Emptor: Empty Promises Not Even a Cat Would Eat

This hook imposes type-checking on all downstream packages importing your package, which may not necessarily want, expect, or tolerate type-checking. This hook is not intended to be called from intermediary APIs, libraries, frameworks, or other middleware. Packages imported by other packages should not call this hook. This hook is only intended to be called from full-stack end-user applications as a convenient alternative to manually passing the names of all packages to be type-checked to the more granular beartype_packages() hook.

This hook is the extreme QA nuclear option. Because this hook is the extreme QA nuclear option, most codebases should not call this hook.

beartype cannot be held responsible for a sudden rupture in the plenæne of normalcy, the space-time continuum, or your once-stable job. Pour one out for those who are about to vitriolically explode their own code.

Nuke Python from orbit. Because now you can.

New in version 0.15.0.

quiet, safe life

The beartype_all() lifestyle. Short but sweet.

Import Hook Configuration

Beartype import hooks accept an optional keyword-only conf parameter whose value is a beartype configuration (i.e., beartype.BeartypeConf instance), defaulting to the default beartype configuration BeartypeConf(). Unsurprisingly, that configuration configures the behaviour of its hook: e.g.,

# In your "{your_package}.__init__" submodule, enable @beartype's support for
# the PEP 484-compliant implicit numeric tower (i.e., expand "int" to "int |
# float" and "complex" to "int | float | complex"):
from beartype import BeartypeConf           # <-- it all seems so familiar
from beartype.claw import beartype_package  # <-- boil it up, boilerplate
beartype_package('your_package', conf=BeartypeConf(is_pep484_tower=True))  # <-- *UGH.*

Equally unsurprisingly, beartype.BeartypeConf has been equipped with import hook-aware super powers. Fine-tune the behaviour of our import hooks for your exact needs, including:

  • BeartypeConf(claw_is_pep526: bool = True). By default, beartype.claw type-checks annotated variable assignments like muh_int: int = 'Pretty sure this isn't an integer.'. Although this is usually what everyone wants, this may not be what someone suspicious wearing aviator goggles, a red velvet cape, and too-tight black leather wants. Nobody knows what those people want. If you are such a person, consider disabling this option to reduce type safety and destroy your code like Neo-Tokyo vs. Mecha-Baby-Godzilla: …who will win!?!?

  • BeartypeConf(warning_cls_on_decorator_exception: Optional[Type[Warning]] = None). By default, beartype.claw emits non-fatal warnings rather than fatal exceptions raised by the beartype.beartype() decorator at decoration time. This is usually what everyone wants, because beartype.beartype() currently fails to support all possible edge cases and is thus likely to raise at least one exception while decorating your entire package. To improve the resilience of beartype.claw against those edge cases, beartype.beartype() emits one warning for each decoration exception and then simply continues to the next decoratable callable or class. This is occasionally unhelpful. What if you really do want beartype.claw to raise a fatal exception on the first such edge case in your codebase – perhaps because you want to either see the full exception traceback or punish your coworkers who are violating typing standards by trying to use an imported module as a type hint? …this actually happened In this case, consider:

    • Passing None as the value of this parameter. Doing so forces beartype.claw to act strictly, inflexibly, and angrily. Expect spittle-flecked mouth frothing and claws all over the place:

    # In your "{your_package}.__init__" submodule, raise exceptions because you
    # hate worky. The CI pipeline you break over your knee may just be your own.
    from beartype import BeartypeConf                # <-- boiling boilerplate...
    from beartype.claw import beartype_this_package  # <-- ...ain't even lukewarm
    beartype_this_package(conf=BeartypeConf(warning_cls_on_decorator_exception=None))  # <-- *ohboy*