Skip to content

Commit

Permalink
PEP 613 x 2.
Browse files Browse the repository at this point in the history
This commit is the last in a commit chain adding support for deprecated
**PEP 613 type aliases** (i.e., the `typing.TypeAlias` type hint
singleton), resolving feature request #328 kindly submitted by
spaghetti-loving Bay Area pasta guru @jamesbraza (James Braza). Although
since deprecated by **PEP 695 type aliases** (e.g., type hints of the
form `type {alias_name} = {alias_value}` under Python â<U+0089>¥ 3.12),
PEP 613 type aliases are still widely prevalent throughout the
open-source community. (*Humongous fungus high in dextrose is a dextrous monstrosity!*)
  • Loading branch information
leycec committed Feb 28, 2024
1 parent 438fe70 commit 6c2733e
Show file tree
Hide file tree
Showing 17 changed files with 784 additions and 380 deletions.
22 changes: 7 additions & 15 deletions beartype/_check/code/codemake.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,6 @@
release_object_typed,
)
from beartype._util.func.utilfuncscope import add_func_scope_attr
from beartype._util.hint.pep.proposal.pep484.utilpep484 import (
warn_if_hint_pep484_deprecated)
from beartype._util.hint.pep.proposal.pep484585.utilpep484585 import (
is_hint_pep484585_tuple_empty)
from beartype._util.hint.pep.proposal.pep484585.utilpep484585arg import (
Expand Down Expand Up @@ -719,23 +717,15 @@ def _enqueue_hint_child(pith_child_expr: str) -> str:
hint_curr_sign = get_hint_pep_sign(hint_curr)
# print(f'Visiting PEP type hint {repr(hint_curr)} sign {repr(hint_curr_sign)}...')

#FIXME: Non-ideal. Shift elsewhere, please. See this function for
#"FIXME:" comments pertaining to this.
# If this is a PEP 484-compliant type hint deprecated by an
# equivalent PEP 585-compliant type hint, emit a non-fatal warning.
# print(f'Testing {hint_curr_exception_prefix} hint {repr(hint_curr)} for deprecation...')
warn_if_hint_pep484_deprecated(
hint=hint_curr, exception_prefix=_EXCEPTION_PREFIX)

#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# NOTE: Whenever adding support for (i.e., when generating code
# type-checking) a new "typing" attribute below, similar support
# for that attribute *MUST* also be added to the parallel:
# * "beartype._util.hint.pep.errorget" submodule, which
# raises exceptions on the current pith failing this check.
# * "beartype._check.error" subpackage, which raises exceptions on
# the current pith failing this check.
# * "beartype._data.hint.pep.sign.datapepsignset.HINT_SIGNS_SUPPORTED_DEEP"
# frozen set of all signs for which this function generates
# deeply type-checking code.
# frozen set of all signs for which this function generates deeply
# type-checking code.
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

#FIXME: Python 3.10 provides proper syntactic support for "case"
Expand All @@ -745,7 +735,9 @@ def _enqueue_hint_child(pith_child_expr: str) -> str:
#have to preserve this for basically forever. What you gonna do?
#FIXME: Actually, we should probably just leverage a hypothetical
#"beartype.vale.IsInline[...]" validator to coerce this slow O(n)
#procedural logic into fast O(1) object-oriented logic.
#procedural logic into fast O(1) object-oriented logic. Of course,
#object-oriented logic is itself slow -- so we only do this if we
#can sufficiently memoize that logic. Consideration!

# Switch on (as in, pretend Python provides a "case" statement)
# the sign identifying this hint to decide which type of code to
Expand Down
91 changes: 90 additions & 1 deletion beartype/_check/convert/convreduce.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,46 @@
HintSignTypeGuard,
HintSignTypeVar,
HintSignTypedDict,
HintSignAbstractSet,
HintSignAsyncContextManager,
HintSignAsyncGenerator,
HintSignAsyncIterable,
HintSignAsyncIterator,
HintSignAwaitable,
HintSignByteString,
HintSignCallable,
HintSignChainMap,
HintSignCollection,
HintSignContainer,
HintSignContextManager,
HintSignCoroutine,
HintSignCounter,
HintSignDefaultDict,
HintSignDeque,
HintSignDict,
HintSignFrozenSet,
HintSignGenerator,
HintSignHashable,
HintSignItemsView,
HintSignIterable,
HintSignIterator,
HintSignKeysView,
HintSignList,
HintSignMappingView,
HintSignMapping,
HintSignMatch,
HintSignMutableMapping,
HintSignMutableSequence,
HintSignMutableSet,
HintSignOrderedDict,
HintSignPattern,
HintSignReversible,
HintSignSequence,
HintSignSet,
HintSignSized,
HintSignTuple,
HintSignType,
HintSignValuesView,
)
from beartype._data.hint.datahinttyping import TypeStack
from beartype._util.cache.utilcachecall import callable_cached
Expand All @@ -51,7 +91,9 @@
from beartype._util.hint.nonpep.mod.utilmodpandera import (
reduce_hint_pandera)
from beartype._util.hint.pep.proposal.pep484.utilpep484 import (
reduce_hint_pep484_none)
reduce_hint_pep484_deprecated,
reduce_hint_pep484_none,
)
from beartype._util.hint.pep.proposal.pep484.utilpep484generic import (
reduce_hint_pep484_generic)
from beartype._util.hint.pep.proposal.pep484.utilpep484newtype import (
Expand Down Expand Up @@ -514,6 +556,53 @@ def reduce_hint_pep{pep_number}(


_HINT_SIGN_TO_REDUCE_HINT_UNCACHED: _HintSignToReduceHintUncached = {
# ..................{ PEP 484 }..................
# Preserve deprecated PEP 484-compliant type hints as is while emitting one
# non-fatal deprecation warning for each.
#
# Note that, to ensure that one such warning is emitted for each such hint,
# these reducers are intentionally uncached rather than cached.
HintSignAbstractSet: reduce_hint_pep484_deprecated,
HintSignAsyncContextManager: reduce_hint_pep484_deprecated,
HintSignAsyncGenerator: reduce_hint_pep484_deprecated,
HintSignAsyncIterable: reduce_hint_pep484_deprecated,
HintSignAsyncIterator: reduce_hint_pep484_deprecated,
HintSignAwaitable: reduce_hint_pep484_deprecated,
HintSignByteString: reduce_hint_pep484_deprecated,
HintSignCallable: reduce_hint_pep484_deprecated,
HintSignChainMap: reduce_hint_pep484_deprecated,
HintSignCollection: reduce_hint_pep484_deprecated,
HintSignContainer: reduce_hint_pep484_deprecated,
HintSignContextManager: reduce_hint_pep484_deprecated,
HintSignCoroutine: reduce_hint_pep484_deprecated,
HintSignCounter: reduce_hint_pep484_deprecated,
HintSignDefaultDict: reduce_hint_pep484_deprecated,
HintSignDeque: reduce_hint_pep484_deprecated,
HintSignDict: reduce_hint_pep484_deprecated,
HintSignFrozenSet: reduce_hint_pep484_deprecated,
HintSignGenerator: reduce_hint_pep484_deprecated,
HintSignHashable: reduce_hint_pep484_deprecated,
HintSignItemsView: reduce_hint_pep484_deprecated,
HintSignIterable: reduce_hint_pep484_deprecated,
HintSignIterator: reduce_hint_pep484_deprecated,
HintSignKeysView: reduce_hint_pep484_deprecated,
HintSignList: reduce_hint_pep484_deprecated,
HintSignMappingView: reduce_hint_pep484_deprecated,
HintSignMapping: reduce_hint_pep484_deprecated,
HintSignMatch: reduce_hint_pep484_deprecated,
HintSignMutableMapping: reduce_hint_pep484_deprecated,
HintSignMutableSequence: reduce_hint_pep484_deprecated,
HintSignMutableSet: reduce_hint_pep484_deprecated,
HintSignOrderedDict: reduce_hint_pep484_deprecated,
HintSignPattern: reduce_hint_pep484_deprecated,
HintSignReversible: reduce_hint_pep484_deprecated,
HintSignSequence: reduce_hint_pep484_deprecated,
HintSignSet: reduce_hint_pep484_deprecated,
HintSignSized: reduce_hint_pep484_deprecated,
HintSignTuple: reduce_hint_pep484_deprecated,
HintSignType: reduce_hint_pep484_deprecated,
HintSignValuesView: reduce_hint_pep484_deprecated,

# ..................{ PEP 613 }..................
# Reduce PEP 613-compliant "typing.TypeAlias" type hints to an arbitrary
# ignorable type hint *AND* emit a non-fatal deprecation warning.
Expand Down
1 change: 1 addition & 0 deletions beartype/_data/hint/pep/datapeprepr.py
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,7 @@ def _init() -> None:
# Uncomment as needed to display the contents of these objects.

# from pprint import pformat
# print(f'HINTS_PEP484_REPR_PREFIX_DEPRECATED: {pformat(HINTS_PEP484_REPR_PREFIX_DEPRECATED)}')
# print(f'HINT_REPR_PREFIX_ARGS_0_OR_MORE_TO_SIGN: {pformat(HINT_REPR_PREFIX_ARGS_0_OR_MORE_TO_SIGN)}')
# print(f'HINT_REPR_PREFIX_ARGS_1_OR_MORE_TO_SIGN: {pformat(HINT_REPR_PREFIX_ARGS_1_OR_MORE_TO_SIGN)}')
# print(f'HINT_TYPE_NAME_TO_SIGN: {pformat(HINT_TYPE_NAME_TO_SIGN)}')
Expand Down
4 changes: 0 additions & 4 deletions beartype/_data/hint/pep/sign/datapepsigns.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@
This private submodule is *not* intended for importation by downstream callers.
'''

# ....................{ TODO }....................
#FIXME: Embed the number of each PEP for PEP-specific signs into the names of
#those signs (e.g., rename "HintSignAnnotated" to "HintSignPep593Annotated").

# ....................{ IMPORTS }....................
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# CAUTION: Attributes imported here at module scope *MUST* be explicitly
Expand Down
16 changes: 15 additions & 1 deletion beartype/_data/hint/pep/sign/datapepsignset.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,21 @@
HintSignValuesView,
)

# ....................{ SIGNS ~ bare }....................
# ....................{ SETS ~ deprecated }....................
#FIXME: Currently unused but preserved for posterity. *shrug*
# HINT_SIGNS_DEPRECATED = frozenset((
# # ..................{ PEP 613 }..................
# # PEP 613-compliant "typing.TypeAlias" type hint singletons have been
# # deprecated by PEP 695-compliant type aliases under Python >= 3.12.
# HintSignTypeAlias,
# ))
# '''
# Frozen set of all **deprecated signs** (i.e., arbitrary objects uniquely
# identifying PEP-compliant type hints unconditionally obsoleted by equivalent
# PEP-compliant type hints standardized by more recently released PEPs).
# '''

# ....................{ SIGNS ~ ignorable }....................
HINT_SIGNS_BARE_IGNORABLE = frozenset((
# ..................{ PEP 484 }..................
# The "Any" singleton is semantically synonymous with the ignorable
Expand Down
46 changes: 24 additions & 22 deletions beartype/_util/error/utilerrwarn.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,33 +150,35 @@ def reissue_warnings_placeholder(
# Munged warning message to be issued below.
warning_message_new: Any = None

# If this warning is... *ALREADY A STRING!?* What is going on here?
# Look. Not even we know. But mypy claims that warnings recorded by
# calls to the standard "warnings.catch_warnings(record=True)" function
# satisfy the union "Warning | str". Technically, that makes no sense.
# Pragmatically, that makes no sense. But mypy says it's true. We are
# too tired to argue with static type-checkers at 4:11AM in the morning.
if isinstance(warning, str): # pragma: no cover
warning_message_new = warning
# Else, this warning is actually a warning.
#
# If this is an conventional warning...
elif is_exception_message_str(warning):
# If either...
if (
# This warning is... *ALREADY A STRING!?* What is going on here?
# Look. Not even we know. But mypy claims that warnings recorded by
# calls to the standard "warnings.catch_warnings(record=True)"
# function satisfy the union "Warning | str". Technically, that
# makes no sense. Pragmatically, that makes no sense. But mypy says
# it's true. We are too tired to argue with static type-checkers at
# 4:11AM in the morning.
isinstance(warning, str) or
# This warning is conventional...
is_exception_message_str(warning)
# Then this warning is or has a standard message. In this case...
):
# Original warning message, coerced from the original warning.
#
# Note that the poorly named "message" attribute is the original warning
# rather warning message. Just as with exceptions, coercing this warning
# into a string reliably retrieves its message.
# Note that the poorly named "message" attribute is the original
# warning rather warning message. Just as with exceptions, coercing
# this warning into a string reliably retrieves its message.
warning_message_old = str(warning)

# Munged warning message globally replacing all instances of this source
# substring with this target substring.
# Munged warning message globally replacing all instances of this
# source substring with this target substring.
#
# Note that we intentionally call the lower-level str.replace() method
# rather than the higher-level
# beartype._util.text.utiltextmunge.replace_str_substrs() function here,
# as the latter unnecessarily requires this warning message to contain
# one or more instances of this source substring.
# Note that we intentionally call the lower-level str.replace()
# method rather than the higher-level
# beartype._util.text.utiltextmunge.replace_str_substrs() function
# here, as the latter unnecessarily requires this warning message to
# contain one or more instances of this source substring.
warning_message_new = warning_message_old.replace(
source_str, target_str)

Expand Down

0 comments on commit 6c2733e

Please sign in to comment.