Source code for beartype._decor.decorcache

#!/usr/bin/env python3
# --------------------( LICENSE                            )--------------------
# Copyright (c) 2014-2024 Beartype authors.
# See "LICENSE" for further details.

'''
**Memoized beartype decorator.**

This private submodule defines the core :func:`beartype.beartype` decorator,
conditionally imported (in order):

#. Into the parent :mod:`beartype._decor.decormain` submodule if this decorator
   is *not* currently reducing to a noop (e.g., due to ``python3 -O``
   optimization).
#. Into the root :mod:`beartype.__init__` submodule if the :mod:`beartype`
   package is *not* currently being installed by :mod:`setuptools`.

This private submodule is literally the :func:`beartype.beartype` decorator,
despite *not* actually being that decorator (due to being unmemoized).

This private submodule is *not* intended for importation by downstream callers.
'''

# ....................{ IMPORTS                            }....................
from beartype.roar import BeartypeConfException
from beartype.typing import (
    Dict,
    Optional,
)
from beartype._conf.confcls import (
    BEARTYPE_CONF_DEFAULT,
    BeartypeConf,
)
from beartype._data.hint.datahinttyping import (
    BeartypeConfedDecorator,
    BeartypeReturn,
    BeartypeableT,
)
from beartype._decor.decorcore import beartype_object

# ....................{ DECORATORS                         }....................
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# CAUTION: Synchronize the signature of this non-identity decorator with the
# identity decorator defined by the "beartype._decor.decormain" submodule.
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# CAUTION: The parent "beartype._decor.decormain" submodule intentionally
# defines the docstring for this decorator.
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
[docs]def beartype( # Optional positional or keyword parameters. obj: Optional[BeartypeableT] = None, # Optional keyword-only parameters. *, conf: BeartypeConf = BEARTYPE_CONF_DEFAULT, ) -> BeartypeReturn: # If "conf" is *NOT* a configuration, raise an exception. if not isinstance(conf, BeartypeConf): raise BeartypeConfException( f'{repr(conf)} not beartype configuration.') # Else, "conf" is a configuration. # # If passed an object to be decorated, this decorator is in decoration # rather than configuration mode. In this case, decorate this object with # type-checking configured by this configuration. # # Note this branch is typically *ONLY* entered when the "conf" parameter # is *NOT* explicitly passed and thus defaults to the default # configuration. While callers may technically run this decorator in # decoration mode with a non-default configuration, doing so would be both # highly irregular *AND* violate PEP 561-compliance by violating the # decorator overloads declared above. Nonetheless, we're largely permissive # here; callers that are doing this are sufficiently intelligent to be # trusted to violate PEP 561-compliance if they so choose. So... *shrug* elif obj is not None: return beartype_object(obj, conf) # Else, we were passed *NO* object to be decorated. In this case, this # decorator is in configuration rather than decoration mode. # Private decorator (possibly previously generated and cached by a prior # call to this decorator also in configuration mode) generically applying # this configuration to any beartypeable object passed to that decorator # if a prior call to this public decorator has already been passed the same # configuration (and thus generated and cached this private decorator) *OR* # "None" otherwise (i.e., if this is the first call to this public # decorator passed this configuration in configuration mode). Phew! beartype_confed_cached = _bear_conf_to_decor.get(conf) # If a prior call to this public decorator has already been passed the same # configuration (and thus generated and cached this private decorator), # return this private decorator for subsequent use in decoration mode. if beartype_confed_cached: return beartype_confed_cached # Else, this is the first call to this public decorator passed this # configuration in configuration mode. # Define a private decorator generically applying this configuration to any # beartypeable object passed to this decorator. def beartype_confed(obj: BeartypeableT) -> BeartypeableT: ''' Decorate the passed **beartypeable** (i.e., pure-Python callable or class) with optimal type-checking dynamically generated unique to that beartypeable under the beartype configuration passed to a prior call to the :func:`beartype.beartype` decorator. Parameters ---------- obj : BeartypeableT Beartypeable to be decorated. Returns ---------- BeartypeableT Either: * If the passed object is a class, this existing class embellished with dynamically generated type-checking. * If the passed object is a callable, a new callable wrapping that callable with dynamically generated type-checking. See Also ---------- :func:`beartype.beartype` Further details. ''' # Decorate this object with type-checking configured by this # configuration. return beartype_object(obj, conf) # Cache this private decorator against this configuration. _bear_conf_to_decor[conf] = beartype_confed # Return this private decorator. return beartype_confed
# ....................{ SINGLETONS }.................... _bear_conf_to_decor: Dict[BeartypeConf, BeartypeConfedDecorator] = {} ''' Non-thread-safe **beartype decorator cache.** This cache is implemented as a singleton dictionary mapping from each **beartype configuration** (i.e., self-caching dataclass encapsulating all flags, options, settings, and other metadata configuring the current decoration of the decorated callable or class) to the corresponding **configured beartype decorator** (i.e., closure created and returned from the :func:`beartype.beartype` decorator when passed a beartype configuration via the optional ``conf`` parameter rather than an object to be decorated via the optional ``obj`` parameter). Caveats ---------- **This cache is not thread-safe.** Although rendering this cache thread-safe would be trivial, doing so would needlessly reduce efficiency. This cache is merely a runtime optimization and thus need *not* be thread-safe. '''