Skip to content

Commit

Permalink
PEP 585 deduplication x 3.
Browse files Browse the repository at this point in the history
This commit is the last in a commit chain reducing the space complexity
of PEP 585-compliant type hints (e.g., `list[str]`) by deduplicating
those hints across all `@beartype`-decorated callables. Currently,
PEP 484-compliant type hints (e.g., `List[str]`) and indeed *most* other
PEP-compliant type hints are already effectively deduplicated by caching
hidden in the standard `typing` module; ergo `List[str] is List[str]`.
Despite deprecating PEP 484, PEP 585 fails to deduplicate its hints;
ergo, `list[str] is not list[str]`. Once this commit chain is finalized,
`@beartype` will internally deduplicate duplicated PEP 585-compliant
type hints at decoration time via a thread-safe cache from the
machine-readable string representations of such hints to such hints.

Specifically, this commit fundamentally refactors our new
`beartype._util.hint.pep.utilpepconv` utility submodule to uniquely
differentiate between root and non-root type hints -- each of which
requires a moderately different form of deduplication in specific and
coercion in general. Since tests pass, **all systems are a go for
beartype 0.9.0.** (*Incoming commingling!*)
  • Loading branch information
leycec committed Oct 21, 2021
1 parent 6dca97f commit 9d52a0b
Show file tree
Hide file tree
Showing 8 changed files with 424 additions and 304 deletions.
156 changes: 0 additions & 156 deletions beartype/_decor/_cache/cachehint.py

This file was deleted.

23 changes: 11 additions & 12 deletions beartype/_decor/_code/_pep/_pephint.py
Expand Up @@ -105,7 +105,6 @@
CallableScope,
add_func_scope_attr,
)
from beartype._util.hint.utilhintconv import reduce_hint
from beartype._util.hint.pep.proposal.pep484.utilpep484generic import (
get_hint_pep484_generic_base_erased_from_unerased)
from beartype._util.hint.pep.proposal.pep484585.utilpep484585 import (
Expand Down Expand Up @@ -141,6 +140,7 @@
is_hint_pep_typing,
warn_if_hint_pep_deprecated,
)
from beartype._util.hint.utilhintconv import sanify_hint_child
from beartype._util.hint.utilhinttest import is_hint_ignorable
from beartype._util.kind.utilkinddict import update_mapping
from beartype._util.py.utilpyversion import (
Expand Down Expand Up @@ -696,17 +696,16 @@ def _enqueue_hint_child(pith_child_expr: str) -> str:
pith_curr_var_name = hint_curr_meta[_HINT_META_INDEX_PITH_VAR_NAME]
indent_curr = hint_curr_meta[_HINT_META_INDEX_INDENT]

# Reduce the currently visited hint to a lower-level hint-like object
# associated with this hint if this hint satisfies a condition,
# simplifying type-checking generation logic below by transparently
# converting hints we'd rather *NOT* explicitly support (e.g.,
# third-party "numpy.typing.NDArray" hints) into semantically
# equivalent hints we would (e.g., first-party beartype validators).
# If this is a child hint rather than the root hint, sanify (i.e.,
# sanitize) this hint if this hint is reducible *OR* preserve this hint
# otherwise (i.e., if this hint is irreducible).
#
# Note that parameters are intentionally passed positionally to both
# optimize memoization efficiency and circumvent memoization warnings.
hint_curr = reduce_hint(
hint_curr, hint_curr_exception_prefix)
# Note that the root hint has already been permanently sanified by the
# calling "beartype._decor._code.codemain" submodule and thus need
# *NOT* be inefficiently resanified here.
if hints_meta_index_curr:
hint_curr = sanify_hint_child(
hint=hint_curr, exception_prefix=hint_curr_exception_prefix)

#FIXME: Comment this sanity check out after we're sufficiently
#convinced this algorithm behaves as expected. While useful, this check
Expand Down Expand Up @@ -1617,7 +1616,7 @@ def _enqueue_hint_child(pith_child_expr: str) -> str:
# If this is *NOT* a beartype validator, raise an
# exception.
#
# Note that the previously called reduce_hint()
# Note that the previously called sanify_hint_child()
# function validated only the first such to be a
# beartype validator. All remaining arguments have yet
# to be validated, so we do so now for consistency and
Expand Down
10 changes: 5 additions & 5 deletions beartype/_decor/_code/codemain.py
Expand Up @@ -22,7 +22,6 @@

# ....................{ IMPORTS }....................
from beartype.roar import BeartypeDecorParamNameException
from beartype._decor._cache.cachehint import coerce_hint_pep
from beartype._decor._code.codesnip import (
ARG_NAME_GETRANDBITS,
CODE_INIT_ARGS_LEN,
Expand All @@ -38,6 +37,7 @@
from beartype._decor._data import BeartypeData
from beartype._util.hint.pep.proposal.pep484585.utilpep484585func import (
reduce_hint_pep484585_func_return)
from beartype._util.hint.utilhintconv import sanify_hint_root
from beartype._util.hint.utilhinttest import is_hint_ignorable
from beartype._util.text.utiltextlabel import (
prefix_callable_decorated_param,
Expand Down Expand Up @@ -343,10 +343,10 @@ def _code_check_params(data: BeartypeData) -> str:
# supported PEP-compliant hint).
#
# Do this first *BEFORE* passing this hint to any further callables.
hint = coerce_hint_pep(
hint = sanify_hint_root(
hint=hint,
func=func,
pith_name=param_name,
hint=hint,
exception_prefix=f'{exception_prefix}type hint ',
)

Expand Down Expand Up @@ -468,10 +468,10 @@ def _code_check_return(data: BeartypeData) -> str:
# is both PEP-compliant and supported, *OR* raise an exception
# otherwise (i.e., if this hint is neither PEP-noncompliant nor a
# supported PEP-compliant hint).
hint = coerce_hint_pep(
hint = sanify_hint_root(
hint=hint,
func=func,
pith_name='return',
hint=hint,
exception_prefix=(
f'{prefix_callable_decorated_return(func)}type hint '),
)
Expand Down
8 changes: 3 additions & 5 deletions beartype/_decor/_error/_errorsleuth.py
Expand Up @@ -19,7 +19,6 @@
HINT_SIGNS_SUPPORTED_DEEP,
HINT_SIGNS_ORIGIN_ISINSTANCEABLE,
)
from beartype._util.hint.utilhintconv import reduce_hint
from beartype._util.hint.pep.utilpepget import (
get_hint_pep_args,
get_hint_pep_sign,
Expand All @@ -28,6 +27,7 @@
is_hint_pep,
is_hint_pep_args,
)
from beartype._util.hint.utilhintconv import sanify_hint_child
from beartype._util.hint.utilhinttest import is_hint_ignorable
from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_7
from typing import Any, Callable, Optional, Tuple
Expand Down Expand Up @@ -176,10 +176,8 @@ def hint(self, hint: Any) -> None:

# Reduce the currently visited hint to a lower-level hint-like object
# associated with this hint if this hint satisfies a condition.
#
# Note that parameters are intentionally passed positionally to both
# optimize memoization efficiency and circumvent memoization warnings.
hint = reduce_hint(hint, self.exception_prefix)
hint = sanify_hint_child(
hint=hint, exception_prefix=self.exception_prefix)

# If this hint is PEP-compliant...
if is_hint_pep(hint):
Expand Down

0 comments on commit 9d52a0b

Please sign in to comment.