Repository URL to install this package:
|
Version:
0.18.5 ▾
|
#!/usr/bin/env python3
# --------------------( LICENSE )--------------------
# Copyright (c) 2014-2024 Beartype authors.
# See "LICENSE" for further details.
'''
Beartype **type-checking code classes** (i.e., low-level classes storing
metadata describing each iteration of the breadth-first search (BFS) dynamically
generating pure-Python code snippets type-checking arbitrary objects against
PEP-compliant type hints).
This private submodule is *not* intended for importation by downstream callers.
'''
# ....................{ TODO }....................
# All "FIXME:" comments for this submodule reside in this package's "__init__"
# submodule to improve maintainability and readability here.
# ....................{ IMPORTS }....................
from beartype.typing import (
TYPE_CHECKING,
)
from beartype._check.code.snip.codesnipstr import (
CODE_HINT_CHILD_PLACEHOLDER_PREFIX,
CODE_HINT_CHILD_PLACEHOLDER_SUFFIX,
)
from beartype._util.cache.pool.utilcachepoollistfixed import (
FIXED_LIST_SIZE_MEDIUM,
FixedList,
)
# ....................{ CLASSES }....................
#FIXME: Unit test us up, please.
class HintsMeta(FixedList):
'''
**Type-checking metadata list** (i.e., low-level fixed list of all metadata
describing all visitable hints currently discovered by the breadth-first
search (BFS) dynamically generating pure-Python code snippets type-checking
arbitrary objects against type hints).
This list acts as a standard First In First Out (FILO) queue, enabling this
BFS to be implemented as an efficient imperative algorithm rather than an
inefficient -- and dangerous, due to both unavoidable stack exhaustion and
avoidable infinite recursion -- recursive algorithm.
Note that this list is guaranteed by the previously called
``_die_if_hint_repr_exceeds_child_limit()`` function to be larger than the
number of hints transitively visitable from this root hint. Ergo, *all*
indexation into this list performed by this BFS is guaranteed to be safe.
Attributes
----------
index_last : int
0-based index of metadata describing the last visitable hint in this
list. For efficiency, this integer also uniquely identifies the
current child type hint of the currently visited parent type hint.
'''
# ..................{ CLASS VARIABLES }..................
# Slot all instance variables defined on this object to minimize the time
# complexity of both reading and writing variables across frequently
# called @beartype decorations. Slotting has been shown to reduce read and
# write costs by approximately ~10%, which is non-trivial.
__slots__ = (
'index_last',
)
# Squelch false negatives from mypy. This is absurd. This is mypy. See:
# https://github.com/python/mypy/issues/5941
if TYPE_CHECKING:
index_last: int
# ..................{ INITIALIZERS }..................
def __init__(self) -> None:
'''
Initialize this type-checking metadata list.
'''
# Initialize our superclass.
super().__init__(
size=FIXED_LIST_SIZE_MEDIUM,
#FIXME: *NO*. We actually need these to be independent copies. Ergo,
#there's *NO* alternative but to iteratively define one new instance
#of this dataclass for each index of this list. Indeed, this is
#actually a profound benefit. How? We can precompute the values of
#* "hint_curr_placeholder" *AT PYTHON STARTUP*. Just iteratively
# assign each "hint_curr_placeholder" according to its index. Do so
# based on the current logic of the enqueue_hint_child() method.
#* "pith_curr_var_name" *AT PYTHON STARTUP* in the exact same way.
#
#Indeed, this suggests we probably no longer need either:
#* "pith_curr_var_name_index".
#* The entire "codesnipcls" submodule.
#
#Well, isn't this turning out to be a significant facepalm.
#FIXME: Actually, the default of "None" here is fine. Let's instead:
#* Redefine the __getitem__() dunder method to dynamically inspect
# the item at the passed index. If:
# * "None", then replace this item with a new "HintMeta" instance
# suitable for the passed index.
# * Else, return the existing "HintMeta" instance at this index.
#* Refactor the enqueue_hint_child() method to then reassign the
# fields of this "HintMeta" instance to the desired values.
#
#This approach avoids expensive up-front computation at app startup,
#instead amortizing these costs across the app lifetime. Heh.
# obj_init=HintMeta(),
)
# 0-based index of metadata describing the last visitable hint in this
# list, initialized to "-1" to ensure that the initial incrementation of
# this index by the enqueue_hint_child() method initializes index 0 of
# this list.
self.index_last = 0
# ..................{ METHODS }..................
def enqueue_hint_child(self, pith_child_expr: str) -> str:
'''
**Enqueue** (i.e., append) a new tuple of metadata describing the
currently iterated child type hint to the end of the ``hints_meta``
queue, enabling this hint to be visited by the ongoing breadth-first
search (BFS) traversing over this queue.
Parameters
----------
pith_child_expr : str
Python code snippet evaluating to the child pith to be type-checked
against the currently iterated child type hint.
This closure also implicitly expects the following local variables of
the outer scope to be set to relevant values:
hint_child : object
Currently iterated child type hint subscripting the currently
visited type hint.
Returns
-------
str
Placeholder string to be subsequently replaced by code type-checking
this child pith against this child type hint.
'''
# print(f'pith_child_expr: {pith_child_expr}')
# Increment the 0-based index of metadata describing the last visitable
# hint in this list (which also serves as the unique identifier of the
# currently iterated child hint) *BEFORE* overwriting the existing
# metadata at this index.
#
# Note this index is guaranteed to *NOT* exceed the fixed length of this
# list, by prior validation.
self.index_last += 1
# Placeholder string to be globally replaced by code type-checking the
# child pith against this child hint, intentionally prefixed and
# suffixed by characters that:
#
# * Are intentionally invalid as Python code, guaranteeing that the
# top-level call to the exec() builtin performed by the @beartype
# decorator will raise a "SyntaxError" exception if the caller fails
# to replace all placeholder substrings generated by this method.
# * Protect the identifier embedded in this substring against ambiguous
# global replacements of larger identifiers containing this
# identifier. If this identifier were *NOT* protected in this manner,
# then the first substring "0" generated by this method would
# ambiguously overlap with the subsequent substring "10" generated by
# this method, which would then produce catastrophically erroneous and
# undebuggable Python code.
hint_child_placeholder = (
f'{CODE_HINT_CHILD_PLACEHOLDER_PREFIX}'
f'{str(self.index_last)}'
f'{CODE_HINT_CHILD_PLACEHOLDER_SUFFIX}'
)
#FIXME: Implement us up, please. This will prove non-trivial. *sigh*
# # Create and insert a new tuple of metadata describing this child hint
# # at this index of this list.
# #
# # Note that this assignment is guaranteed to be safe, as
# # "FIXED_LIST_SIZE_MEDIUM" is guaranteed to be substantially larger than
# # "hints_meta_index_last".
# self[self.index_last] = (
# hint_child,
# hint_child_placeholder,
# pith_child_expr,
# pith_curr_var_name_index,
# indent_level_child,
# )
# Return this placeholder string.
return hint_child_placeholder
#FIXME: Unit test us up, please.
class HintMeta(object):
'''
**Type-checking metadata dataclass** (i.e., low-level class storing metadata
describing each iteration of the breadth-first search (BFS) dynamically
generating pure-Python code snippets type-checking arbitrary objects against
type hints).
Attributes
----------
hint_curr : object
Type hint currently visited by this BFS.
hint_curr_placeholder : str
**Type-checking placeholder substring** to be globally replaced in the
**returned Python code snippet** (i.e., the ``func_wrapper_code`` local)
by a Python code snippet type-checking the **current pith expression**
(i.e., the ``pith_curr_var_name`` local) against the currently visited
hint (i.e., the :attr:`hint_curr` instance variable).
This substring provides indirection enabling the currently visited
parent hint to defer and delegate the generation of code type-checking
each child argument of that hint to the later time at which that child
argument is visited.
Example
-------
For example, the
:func:`beartype._check.code.codemake.make_check_expr` factory might
generate intermediary code resembling the following on visiting the
:obj:`typing.Union` parent type hint of a subscripted ``Union[int,
str]`` type hint *before* visiting either the :class:`int` or
:class:`str` child type hints of that parent type hint:
.. code-block:: python
if not (
@{0}! or
@{1}!
):
raise get_func_pith_violation(
func=__beartype_func,
pith_name=$%PITH_ROOT_NAME/~,
pith_value=__beartype_pith_root,
)
Note the unique substrings ``"@{0}!"`` and ``"@{1}!"`` in that code,
which that factory iteratively replaces with code type-checking each of
the child type hints (e.g., :class:`int`, :class:`str`) subscripting
that :obj:`typing.Union` parent type hint. The final code memoized by
that factory might then resemble:
.. code-block:: python
if not (
isinstance(__beartype_pith_root, int) or
isinstance(__beartype_pith_root, str)
):
raise get_func_pith_violation(
func=__beartype_func,
pith_name=$%PITH_ROOT_NAME/~,
pith_value=__beartype_pith_root,
)
pith_curr_expr : str
**Pith expression** (i.e., Python code snippet evaluating to the value
of) the current **pith** (i.e., possibly nested object of the passed
parameter or return to be type-checked against the currently visited
type hint).
Note that this expression is intentionally *not* an assignment
expression but rather the original inefficient expression provided by
the parent type hint of the currently visited type hint.
pith_curr_var_name_index : int
**Pith variable name index** (i.e., 0-based integer suffixing the name
of each local variable assigned the value of the current pith in a
assignment expression, thus uniquifying this variable in the body of the
current wrapper function). Indexing the
:obj:`beartype._check.code.snip.codesnipcls.PITH_INDEX_TO_VAR_NAME`
dictionary singleton by this integer efficiently yields the current
**pith variable name** locally storing the value of the current pith.
Note that this integer is intentionally incremented as an efficient
low-level scalar rather than as an inefficient high-level
:class:`itertools.Counter` object. Since both are equally thread-safe in
the internal context of a function body, the former is preferable.
indent_level_curr : int
**Indentation level** (i.e., 1-based positive integer providing the
current level of indentation appropriate for the currently visited type
hint). Indexing the
:obj:`beartype._data.code.datacodeindent.INDENT_LEVEL_TO_CODE`
dictionary singleton by this integer efficiently yields the current
**indendation string** suitable for prefixing each line of code
type-checking the current pith against the current type hint.
'''
# ..................{ CLASS VARIABLES }..................
# Slot all instance variables defined on this object to minimize the time
# complexity of both reading and writing variables across frequently called
# cache dunder methods. Slotting has been shown to reduce read and write
# costs by approximately ~10%, which is non-trivial.
__slots__ = (
'hint_curr',
'hint_curr_placeholder',
'pith_curr_expr',
'pith_curr_var_name_index',
'indent_level_curr',
)
# Squelch false negatives from mypy. This is absurd. This is mypy. See:
# https://github.com/python/mypy/issues/5941
if TYPE_CHECKING:
hint_curr: object
hint_curr_placeholder: str
pith_curr_expr: str
pith_curr_var_name_index: int
indent_level_curr: int
# ..................{ INITIALIZERS }..................
def __init__(
self,
# Mandatory parameters.
hint_curr_placeholder: str,
pith_curr_var_name_index: int,
# Optional parameters.
hint_curr: object = None,
pith_curr_expr: str = '',
indent_level_curr: int = 2,
) -> None:
'''
Initialize this type-checking metadata dataclass.
Parameters
----------
hint_curr : object
Type hint currently visited by this BFS.
hint_curr_placeholder : str
Type-checking placeholder substring. See the class docstring.
pith_curr_expr : str
Pith expression. See the class docstring.
pith_curr_var_name_index : int
Pith variable name index. See the class docstring.
indent_level_curr : int
Indentation level. See the class docstring.
'''
assert isinstance(hint_curr_placeholder, str)
assert isinstance(pith_curr_expr, str)
assert isinstance(pith_curr_var_name_index, int)
assert isinstance(indent_level_curr, int)
assert hint_curr_placeholder
assert pith_curr_expr
assert pith_curr_var_name_index >= 0
assert indent_level_curr > 1
# Classify all passed parameters.
self.hint_curr = hint_curr
self.hint_curr_placeholder = hint_curr_placeholder
self.pith_curr_expr = pith_curr_expr
self.pith_curr_var_name_index = pith_curr_var_name_index
self.indent_level_curr = indent_level_curr