Skip to content

Commit

Permalink
"typing_extensions.Annotated" x 22.
Browse files Browse the repository at this point in the history
This commit is the next in a commit chain adding transparent support
for the third-party `typing_extensions.Annotated` type hint back-ported
to Python < 3.9, en route to resolving #34. Once finalized, this will
enable usage of beartype validators under Python < 3.9 via this hint.
Specifically, this commit continues disastrously breaking literally
everything by continuing to disembowel the feckless
`beartype._util.hint.data.pep.datapep` submodule and its untrustworthy
`beartype._util.hint.data.pep.proposal` crony subpackage in favour of
`beartype._util.hint.data.pep.sign`, which is the only subpackage left
standing. Save us from our reckless selves, GitHub! (*Provisional revisionism!*)
  • Loading branch information
leycec committed Jul 8, 2021
1 parent e7c96ed commit e6c3843
Show file tree
Hide file tree
Showing 19 changed files with 470 additions and 426 deletions.
30 changes: 13 additions & 17 deletions beartype/_decor/_code/_pep/_pephint.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,12 @@
acquire_object_typed,
release_object_typed,
)
from beartype._util.data.hint.datahint import HINTS_IGNORABLE_SHALLOW
from beartype._util.data.hint.pep.datapepattr import (
HINT_PEP586_ATTR_LITERAL,
HINT_PEP593_ATTR_ANNOTATED,
)
from beartype._util.data.hint.pep.datapeprepr import (
HINT_REPRS_IGNORABLE_SHALLOW)
from beartype._util.data.hint.pep.sign.datapepsigns import (
HintSignForwardRef,
HintSignGeneric,
Expand Down Expand Up @@ -141,7 +142,7 @@
is_hint_pep_subscripted,
is_hint_pep_tuple_empty,
is_hint_pep_typing,
warn_if_hint_pep_sign_deprecated,
warn_if_hint_pep_deprecated,
)
from beartype._util.hint.utilhintget import get_hint_forwardref_classname
from beartype._util.hint.utilhinttest import is_hint_ignorable
Expand Down Expand Up @@ -857,14 +858,10 @@ def _enqueue_hint_child(pith_child_expr: str) -> str:
hint_sign=hint_curr_sign, hint_label=hint_curr_label)
# Else, this attribute is supported.

# If this sign and thus this hint is deprecated, emit a non-fatal
# warning warning users of this deprecation.
# If this hint is deprecated, emit a non-fatal warning.
# print(f'Testing {hint_curr_label} hint {repr(hint_curr)} for deprecation...')
warn_if_hint_pep_sign_deprecated(
hint=hint_curr,
hint_sign=hint_curr_sign,
hint_label=hint_curr_label,
)
warn_if_hint_pep_deprecated(
hint=hint_curr, hint_label=hint_curr_label)

# Tuple of all arguments subscripting this hint if any *OR* the
# empty tuple otherwise (e.g., if this hint is its own unsubscripted
Expand Down Expand Up @@ -1111,13 +1108,12 @@ def _enqueue_hint_child(pith_child_expr: str) -> str:
# differs from "typing" pseudo-containers, which narrow the current
# pith expression and thus do benefit from these expressions.
if hint_curr_sign in HINT_SIGNS_UNION:
# Assert this union is subscripted by one or more child hints.
# Note this should *ALWAYS* be the case, as:
#
# Assert this union to be subscripted by one or more child
# hints. Note this should *ALWAYS* be the case, as:
# * The unsubscripted "typing.Union" object is explicitly
# listed in the "HINTS_IGNORABLE_SHALLOW" set and should thus
# have already been ignored when present.
# * The "typing" module explicitly prohibits empty
# listed in the "HINT_REPRS_IGNORABLE_SHALLOW" set and should
# thus have already been ignored when present.
# * The "typing" module explicitly prohibits empty union
# subscription: e.g.,
# >>> typing.Union[]
# SyntaxError: invalid syntax
Expand All @@ -1127,7 +1123,7 @@ def _enqueue_hint_child(pith_child_expr: str) -> str:
f'{hint_curr_label} {repr(hint_curr)} unsubscripted.')
# Else, this union is subscripted by two or more arguments. Why
# two rather than one? Because the "typing" module reduces
# unions of one argument to that argument: e.g.,
# unions of one argument to simply that argument: e.g.,
# >>> import typing
# >>> typing.Union[int]
# int
Expand Down Expand Up @@ -1156,7 +1152,7 @@ def _enqueue_hint_child(pith_child_expr: str) -> str:
# have already been ignored after a call to the
# is_hint_ignorable() tester passed this union on handling
# the parent hint of this union.
assert hint_child not in HINTS_IGNORABLE_SHALLOW, (
assert repr(hint) not in HINT_REPRS_IGNORABLE_SHALLOW, (
f'{hint_curr_label} {repr(hint_curr)} child '
f'{repr(hint_child)} ignorable but not ignored.')

Expand Down
5 changes: 2 additions & 3 deletions beartype/_decor/_error/_proposal/_errorpep484union.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
# ....................{ IMPORTS }....................
from beartype.roar._roarexc import _BeartypeCallHintPepRaiseException
from beartype._decor._error._errorsleuth import CauseSleuth
from beartype._util.data.hint.pep.proposal.datapep484 import (
HINT_PEP484_SIGNS_UNION)
from beartype._util.data.hint.pep.sign.datapepsignset import HINT_SIGNS_UNION
from beartype._util.hint.pep.utilhintpepget import (
get_hint_pep_type_stdlib_or_none)
from beartype._util.hint.pep.utilhintpeptest import is_hint_pep
Expand Down Expand Up @@ -43,7 +42,7 @@ def get_cause_or_none_union(sleuth: CauseSleuth) -> Optional[str]:
Type-checking error cause sleuth.
'''
assert isinstance(sleuth, CauseSleuth), f'{repr(sleuth)} not cause sleuth.'
assert sleuth.hint_sign in HINT_PEP484_SIGNS_UNION, (
assert sleuth.hint_sign in HINT_SIGNS_UNION, (
f'{repr(sleuth.hint)} not union.')

# Subset of all classes shallowly associated with these child hints (i.e.,
Expand Down
5 changes: 2 additions & 3 deletions beartype/_decor/_error/errormain.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,6 @@
get_cause_or_none_literal)
from beartype._decor._error._proposal._errorpep593 import (
get_cause_or_none_annotated)
from beartype._util.data.hint.pep.proposal.datapep484 import (
HINT_PEP484_SIGNS_UNION)
from beartype._util.data.hint.pep.sign.datapepsigns import (
HintSignForwardRef,
HintSignGeneric,
Expand All @@ -101,6 +99,7 @@
from beartype._util.data.hint.pep.sign.datapepsignset import (
HINT_SIGNS_SEQUENCE_ARGS_1,
HINT_SIGNS_TYPE_STDLIB,
HINT_SIGNS_UNION,
)
from beartype._util.hint.utilhinttest import die_unless_hint
from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_9
Expand Down Expand Up @@ -351,7 +350,7 @@ def _init() -> None:
get_cause_or_none_sequence_args_1)

# Map each unifying "typing" attribute to the appropriate getter.
for pep_sign_type_union in HINT_PEP484_SIGNS_UNION:
for pep_sign_type_union in HINT_SIGNS_UNION:
PEP_HINT_SIGN_TO_GET_CAUSE_FUNC[pep_sign_type_union] = (
get_cause_or_none_union)

Expand Down
20 changes: 13 additions & 7 deletions beartype/_util/data/hint/pep/datapeprepr.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,13 @@
# The majority of this dictionary is initialized with automated inspection
# below in the _init() function. The *ONLY* key-value pairs explicitly defined
# here are those *NOT* amenable to such inspection.
HINT_REPRS_IGNORABLE = {
HINT_REPRS_IGNORABLE_SHALLOW = {
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# CAUTION: Synchronize changes to this set with the corresponding
# testing-specific set
# "beartype_test.a00_unit.data.hint.pep.data_hintpep.HINTS_PEP_IGNORABLE_SHALLOW".
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

# ..................{ NON-PEP }..................
# The PEP-noncompliant builtin "object" type is the transitive superclass
# of all classes, parameters and return values annotated as "object"
Expand All @@ -201,9 +207,9 @@
# semantically synonymous with the ignorable PEP-noncompliant
# "beartype.cave.AnyType" and hence "object" types. Since PEP
# 484 stipulates that *ANY* unsubscripted subscriptable PEP-compliant
# singleton including "typing.Generic" semantically expands to that
# singelton subscripted by an implicit "Any" argument, "Generic"
# semantically expands to the implicit "Generic[Any]" singleton.
# singleton including "typing.Generic" semantically expands to that
# singelton subscripted by an implicit "Any" argument, "Generic"
# semantically expands to the implicit "Generic[Any]" singleton.
"<class 'typing.Generic'>",
"<class 'typing_extensions.Generic'>",

Expand Down Expand Up @@ -255,7 +261,7 @@ def _init() -> None:
global \
HINT_BARE_REPRS_DEPRECATED, \
HINT_PEP484_BARE_REPRS_DEPRECATED, \
HINT_REPRS_IGNORABLE
HINT_REPRS_IGNORABLE_SHALLOW

# ..................{ HINTS }..................
# Length of the ignorable substring prefixing the name of each sign.
Expand Down Expand Up @@ -408,7 +414,7 @@ def _init() -> None:
# For each shallowly ignorable typing attribute name...
for typing_attr_name in _HINT_TYPING_ATTR_NAMES_IGNORABLE:
# Add that attribute relative to this module to this set.
HINT_REPRS_IGNORABLE.add(
HINT_REPRS_IGNORABLE_SHALLOW.add(
f'{typing_module_name}.{typing_attr_name}')

# For the name of each sign and that sign...
Expand All @@ -432,7 +438,7 @@ def _init() -> None:
HINT_PEP484_BARE_REPRS_DEPRECATED = frozenset(
HINT_PEP484_BARE_REPRS_DEPRECATED)
HINT_BARE_REPRS_DEPRECATED = HINT_PEP484_BARE_REPRS_DEPRECATED
HINT_REPRS_IGNORABLE = frozenset(HINT_REPRS_IGNORABLE)
HINT_REPRS_IGNORABLE_SHALLOW = frozenset(HINT_REPRS_IGNORABLE_SHALLOW)

# Initialize this submodule.
_init()
6 changes: 5 additions & 1 deletion beartype/_util/data/hint/pep/sign/datapepsignset.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,11 @@
'''


HINT_SIGNS_UNION = frozenset((HintSignOptional, HintSignUnion))
HINT_SIGNS_UNION = frozenset((
# ..................{ PEP 484 }..................
HintSignOptional,
HintSignUnion,
))
'''
Frozen set of all **union signs** (i.e., arbitrary objects uniquely identifying
:pep:`484`-compliant type hints unifying one or more subscripted type hint
Expand Down
14 changes: 7 additions & 7 deletions beartype/_util/hint/pep/proposal/utilhintpep484.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
from beartype._util.cache.utilcachecall import callable_cached
from beartype._util.data.hint.pep.proposal.datapep484 import (
HINT_PEP484_TYPE_FORWARDREF,
HINT_PEP484_SIGNS_UNION,
)
from beartype._util.data.hint.pep.sign.datapepsigncls import HintSign
from beartype._util.data.hint.pep.sign.datapepsigns import (
HintSignGeneric,
HintSignNewType,
)
from beartype._util.data.hint.pep.sign.datapepsignset import HINT_SIGNS_UNION
from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_7
from beartype._util.utilobject import is_object_subclass
from types import FunctionType
Expand Down Expand Up @@ -80,11 +80,11 @@ def noop(param_hint_ignorable: Generic[T]) -> T: pass
shallowly ignorable child hints is thus the widest possible union,
which is so wide as to constrain nothing and convey no meaningful
semantics. Since there exist a countably infinite number of possible
:data:`Union` subscriptions by one or more shallowly ignorable type
hints, these subscriptions *cannot* be explicitly listed in the
:data:`HINTS_IGNORABLE_SHALLOW` frozenset. Instead, these subscriptions
are dynamically detected by this tester at runtime and thus referred to
as **deeply ignorable type hints.**
:data:`Union` subscriptions by one or more ignorable type hints, these
subscriptions *cannot* be explicitly listed in the
:data:`HINT_REPRS_IGNORABLE_SHALLOW` frozenset. Instead, these
subscriptions are dynamically detected by this tester at runtime and thus
referred to as **deeply ignorable type hints.**
This tester is intentionally *not* memoized (e.g., by the
:func:`callable_cached` decorator), as this tester is only safely callable
Expand Down Expand Up @@ -152,7 +152,7 @@ def noop(param_hint_ignorable: Generic[T]) -> T: pass
#
# If this hint is a union, return true only if one or more child hints of
# this union are recursively ignorable. See the function docstring.
elif hint_sign in HINT_PEP484_SIGNS_UNION:
elif hint_sign in HINT_SIGNS_UNION:
return any(
is_hint_ignorable(hint_child)
for hint_child in get_hint_pep_args(hint)
Expand Down
4 changes: 2 additions & 2 deletions beartype/_util/hint/pep/utilhintpepget.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
BeartypeDecorHintPepSignException,
)
from beartype._util.cache.utilcachecall import callable_cached
from beartype._util.data.hint.pep.sign.datapepsigncls import HintSign
from beartype._util.data.hint.pep.sign.datapepsignmap import (
from beartype._util.data.hint.pep.datapeprepr import (
HINT_BARE_REPR_TO_SIGN,
HINT_TYPE_NAME_TO_SIGN,
)
from beartype._util.data.hint.pep.sign.datapepsigncls import HintSign
from beartype._util.data.hint.pep.sign.datapepsigns import (
HintSignGeneric,
HintSignNewType,
Expand Down
4 changes: 2 additions & 2 deletions beartype/_util/hint/utilhinttest.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
is_hint_pep_supported,
)
from beartype._util.data.hint.datahint import HINT_BASES_FORWARDREF
from beartype._util.data.hint.pep.datapeprepr import HINT_REPRS_IGNORABLE
from beartype._util.data.hint.pep.datapeprepr import HINT_REPRS_IGNORABLE_SHALLOW

# See the "beartype.cave" submodule for further commentary.
__all__ = ['STAR_IMPORTS_CONSIDERED_HARMFUL']
Expand Down Expand Up @@ -191,7 +191,7 @@ def is_hint_ignorable(hint: object) -> bool:
'''

# If this hint is shallowly ignorable, return true.
if repr(hint) in HINT_REPRS_IGNORABLE:
if repr(hint) in HINT_REPRS_IGNORABLE_SHALLOW:
return True
# Else, this hint is *NOT* shallowly ignorable.

Expand Down
6 changes: 3 additions & 3 deletions beartype_test/a00_unit/data/hint/data_hint.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
NoneType,
)
from beartype._cave._cavemap import NoneTypeOr
from beartype._util.data.hint.datahint import HINTS_IGNORABLE_SHALLOW
from beartype_test.a00_unit.data.hint.pep.data_hintpep import (
HINTS_PEP_HASHABLE,
HINTS_PEP_IGNORABLE_SHALLOW,
HINTS_PEP_IGNORABLE_DEEP,
)

Expand Down Expand Up @@ -161,8 +161,8 @@ def __repr__(self) -> str:

# ....................{ NOT ~ tuples }....................
HINTS_IGNORABLE = (
# Shallowly ignorable type hints.
HINTS_IGNORABLE_SHALLOW |
# Shallowly ignorable PEP-compliant type hints.
HINTS_PEP_IGNORABLE_SHALLOW |
# Deeply ignorable PEP-compliant type hints.
HINTS_PEP_IGNORABLE_DEEP
)
Expand Down
24 changes: 12 additions & 12 deletions beartype_test/a00_unit/data/hint/data_hintmeta.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ class NonPepHintMetadata(object):
is_supported : bool
``True`` only if this hint is currently supported by
the :func:`beartype.beartype` decorator. Defaults to ``True``.
piths_satisfied_meta : Tuple[PepHintPithSatisfiedMetadata]
Tuple of zero or more :class:`PepHintPithSatisfiedMetadata` instances,
piths_satisfied_meta : Tuple[HintPithSatisfiedMetadata]
Tuple of zero or more :class:`HintPithSatisfiedMetadata` instances,
each describing an object satisfying this hint when either passed as a
parameter *or* returned as a value annotated by this hint. Defaults to
the empty tuple.
piths_unsatisfied_meta : Tuple[PepHintPithUnsatisfiedMetadata]
Tuple of zero or more :class:`PepHintPithUnsatisfiedMetadata`
piths_unsatisfied_meta : Tuple[HintPithUnsatisfiedMetadata]
Tuple of zero or more :class:`HintPithUnsatisfiedMetadata`
instances, each describing an object *not* satisfying this hint when
either passed as a parameter *or* returned as a value annotated by this
hint. Defaults to the empty tuple.
Expand All @@ -69,8 +69,8 @@ def __init__(
# Optional parameters.
is_ignorable: bool = False,
is_supported: bool = True,
piths_satisfied_meta: 'Tuple[PepHintPithSatisfiedMetadata]' = (),
piths_unsatisfied_meta: 'Tuple[PepHintPithUnsatisfiedMetadata]' = (),
piths_satisfied_meta: 'Tuple[HintPithSatisfiedMetadata]' = (),
piths_unsatisfied_meta: 'Tuple[HintPithUnsatisfiedMetadata]' = (),
) -> None:
assert isinstance(is_ignorable, bool), (
f'{repr(is_ignorable)} not bool.')
Expand All @@ -79,17 +79,17 @@ def __init__(
assert isinstance(piths_unsatisfied_meta, tuple), (
f'{repr(piths_unsatisfied_meta)} not tuple.')
assert all(
isinstance(pith_satisfied_meta, PepHintPithSatisfiedMetadata)
isinstance(pith_satisfied_meta, HintPithSatisfiedMetadata)
for pith_satisfied_meta in piths_satisfied_meta
), (
f'{repr(piths_satisfied_meta)} not tuple of '
f'"PepHintPithSatisfiedMetadata" instances.')
f'"HintPithSatisfiedMetadata" instances.')
assert all(
isinstance(pith_unsatisfied_meta, PepHintPithUnsatisfiedMetadata)
isinstance(pith_unsatisfied_meta, HintPithUnsatisfiedMetadata)
for pith_unsatisfied_meta in piths_unsatisfied_meta
), (
f'{repr(piths_unsatisfied_meta)} not tuple of '
f'"PepHintPithUnsatisfiedMetadata" instances.')
f'"HintPithUnsatisfiedMetadata" instances.')

# Classify all passed parameters.
self.hint = hint
Expand Down Expand Up @@ -307,7 +307,7 @@ def __repr__(self) -> str:
))

# ....................{ CLASSES ~ hint : [un]satisfied }....................
class PepHintPithSatisfiedMetadata(object):
class HintPithSatisfiedMetadata(object):
'''
**Type hint-satisfying pith metadata** (i.e., dataclass whose instance
variables describe an object satisfying a type hint when either passed as a
Expand Down Expand Up @@ -370,7 +370,7 @@ def __repr__(self) -> str:
))


class PepHintPithUnsatisfiedMetadata(object):
class HintPithUnsatisfiedMetadata(object):
'''
**Type hint-unsatisfying pith metadata** (i.e., dataclass whose instance
variables describe an object *not* satisfying a type hint when either
Expand Down

0 comments on commit e6c3843

Please sign in to comment.