Skip to content

Commit

Permalink
"typing_extensions.Annotated" x 3.
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 defines a new private
`beartype._util.hint.data.pep.utilhintdatapep` submodule declaring
globally usable signs agnostic of any Python version or module and thus
transparently supporting both the `typing` and `typing_extensions`
modules. (*Rancid ancillaries distil acidic artilleries!*)
  • Loading branch information
leycec committed May 29, 2021
1 parent 783e699 commit 626879c
Show file tree
Hide file tree
Showing 15 changed files with 590 additions and 372 deletions.
642 changes: 337 additions & 305 deletions README.rst

Large diffs are not rendered by default.

16 changes: 15 additions & 1 deletion beartype/_decor/_code/_pep/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# See "LICENSE" for further details.

# ....................{ TODO }....................
#FIXME: Add support for Python 3.10 and thus:
#FIXME: Add support for Python 3.10-specific PEPs and thus:
#* PEP 604-compliance (e.g., "def square(number: int | float): pass"). Note
# PEP 604 thankfully preserves backward compatibility with "typing.Union":
# The existing typing.Union and | syntax should be equivalent.
Expand All @@ -27,6 +27,20 @@
# singletons are actually a new unique category of callable-specific type
# variables. See also:
# https://www.python.org/dev/peps/pep-0612
#* PEP 647-compliance. PEP 647 introduces a silly new subscriptable
# "typing.TypeGuard" attribute. With respect to runtime type-checking, *ALL*
# "typing.TypeGuard" subscriptions unconditionally reduce to "bool": e.g.,
# from typing import TypeGuard, Union
#
# # This...
# def muh_func(muh_param: object) -> TypeGuard[str]:
# return isinstance(muh_param, str) # <-- gods help us
#
# # This conveys the exact same runtime semantics as this.
# def muh_func(muh_param: object) -> bool:
# return isinstance(muh_param, str) # <-- gods help us
# Lastly, note that (much like "typing.NoReturn") "typing.TypeGuard"
# subscriptions are *ONLY* usable as return annotations. Raise exceptions, yo.

#FIXME: Resurrect memoization support. To do so, we'll probably need to
#abandon the @callable_cached decorator employed below in favour of a manually
Expand Down
8 changes: 4 additions & 4 deletions beartype/_decor/_code/_pep/_pephint.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@
)
from beartype._util.hint.data.utilhintdata import HINTS_IGNORABLE_SHALLOW
from beartype._util.hint.data.pep.utilhintdatapepattr import (
HINT_PEP586_SIGN_LITERAL,
HINT_PEP593_SIGN_ANNOTATED,
HINT_PEP586_ATTR_LITERAL,
HINT_PEP593_ATTR_ANNOTATED,
)
from beartype._util.hint.pep.proposal.utilhintpep484 import (
get_hint_pep484_generic_base_erased_from_unerased,
Expand Down Expand Up @@ -1557,7 +1557,7 @@ def _enqueue_hint_child(pith_child_expr: str) -> str:
# beartype-specific (i.e., metahint whose second argument is an
# instance of the "beartype._vale._valesub._SubscriptedIs" class
# produced by subscripting the "Is" class). In this case...
elif hint_curr_sign is HINT_PEP593_SIGN_ANNOTATED:
elif hint_curr_sign is HINT_PEP593_ATTR_ANNOTATED:
# PEP-compliant type hint annotated by this metahint, localized
# to the "hint_child" local variable to satisfy the public API
# of the _enqueue_hint_child() closure called below.
Expand Down Expand Up @@ -1880,7 +1880,7 @@ def _enqueue_hint_child(pith_child_expr: str) -> str:
# objects that are instances of only *SIX* possible types, which is
# sufficiently limiting as to render this singleton patently absurd
# and a farce that we weep to even implement. In this case...
elif hint_curr_sign is HINT_PEP586_SIGN_LITERAL:
elif hint_curr_sign is HINT_PEP586_ATTR_LITERAL:
# If this hint does *NOT* comply with PEP 586 despite being a
# "typing.Literal" subscription, raise an exception. *sigh*
die_unless_hint_pep586(hint_curr)
Expand Down
4 changes: 2 additions & 2 deletions beartype/_decor/_error/_proposal/_errorpep586.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from beartype._decor._error._errorsleuth import CauseSleuth
from beartype._decor._error._errortype import get_cause_or_none_type_origin
from beartype._util.hint.data.pep.utilhintdatapepattr import (
HINT_PEP586_SIGN_LITERAL)
HINT_PEP586_ATTR_LITERAL)
from beartype._util.text.utiltextjoin import join_delimited_disjunction
from beartype._util.text.utiltextrepr import represent_object
from typing import Optional
Expand All @@ -40,7 +40,7 @@ def get_cause_or_none_literal(sleuth: CauseSleuth) -> Optional[str]:
Type-checking error cause sleuth.
'''
assert isinstance(sleuth, CauseSleuth), f'{repr(sleuth)} not cause sleuth.'
assert sleuth.hint_sign is HINT_PEP586_SIGN_LITERAL, (
assert sleuth.hint_sign is HINT_PEP586_ATTR_LITERAL, (
f'{repr(sleuth.hint_sign)} not literal.')

# If this pith is equal to any literal object subscripting this hint, this
Expand Down
4 changes: 2 additions & 2 deletions beartype/_decor/_error/_proposal/_errorpep593.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from beartype.roar._roarexc import _BeartypeCallHintPepRaiseException
from beartype._decor._error._errorsleuth import CauseSleuth
from beartype._util.hint.data.pep.utilhintdatapepattr import (
HINT_PEP593_SIGN_ANNOTATED)
HINT_PEP593_ATTR_ANNOTATED)
from beartype._util.hint.pep.proposal.utilhintpep593 import (
get_hint_pep593_metadata,
get_hint_pep593_metahint,
Expand Down Expand Up @@ -46,7 +46,7 @@ def get_cause_or_none_annotated(sleuth: CauseSleuth) -> Optional[str]:
Type-checking error cause sleuth.
'''
assert isinstance(sleuth, CauseSleuth), f'{repr(sleuth)} not cause sleuth.'
assert sleuth.hint_sign is HINT_PEP593_SIGN_ANNOTATED, (
assert sleuth.hint_sign is HINT_PEP593_ATTR_ANNOTATED, (
f'{repr(sleuth.hint_sign)} not annotated.')

# PEP-compliant type hint annotated by this metahint.
Expand Down
1 change: 0 additions & 1 deletion beartype/_util/func/utilfuncmake.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,4 +305,3 @@ def make_func(
#
# # Return this copy.
# return func_copy

6 changes: 3 additions & 3 deletions beartype/_util/hint/data/pep/proposal/utilhintdatapep544.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def _init() -> None:
# ..................{ IMPORTS }..................
# Defer Python version-specific imports.
from beartype._util.hint.data.pep.utilhintdatapepattr import (
HINT_PEP_SIGN_LIST)
HINT_PEP_ATTR_LIST)
from typing import (
Any,
AnyStr,
Expand Down Expand Up @@ -229,7 +229,7 @@ def readline(self, limit: int = -1) -> AnyStr:
pass

@abstractmethod
def readlines(self, hint: int = -1) -> HINT_PEP_SIGN_LIST[AnyStr]:
def readlines(self, hint: int = -1) -> HINT_PEP_ATTR_LIST[AnyStr]:
pass

@abstractmethod
Expand Down Expand Up @@ -257,7 +257,7 @@ def write(self, s: AnyStr) -> int:
pass

@abstractmethod
def writelines(self, lines: HINT_PEP_SIGN_LIST[AnyStr]) -> None:
def writelines(self, lines: HINT_PEP_ATTR_LIST[AnyStr]) -> None:
pass

@abstractmethod
Expand Down
32 changes: 28 additions & 4 deletions beartype/_util/hint/data/pep/utilhintdatapep.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,34 @@
HINT_PEP586_SIGNS_SUPPORTED_DEEP)
from beartype._util.hint.data.pep.proposal.utilhintdatapep593 import (
HINT_PEP593_SIGNS_SUPPORTED_DEEP)
from beartype._util.lib.utilliboptional import (
IS_LIB_TYPING_EXTENSIONS)

# See the "beartype.cave" submodule for further commentary.
__all__ = ['STAR_IMPORTS_CONSIDERED_HARMFUL']

# ....................{ MODULES }....................
HINT_PEP_MODULE_NAMES = frozenset(
(
# Name of the official typing module bundled with the Python stdlib.
'typing',
) + (
# If the third-party "typing_extensions" module backporting "typing"
# hints introduced in newer Python versions to older Python versions is
# importable under the active Python interpreter, a 1-tuple of the name
# of that module;
('typing_extensions',)
if IS_LIB_TYPING_EXTENSIONS else
# Else, the empty tuple.
()
)
)
'''
Frozen set of the unqualified names of all top-level **hinting modules** (i.e.,
modules declaring attributes usable for creating PEP-compliant type hints
accepted by both static and runtime type checkers).
'''

# ....................{ SIGNS }....................
HINT_PEP_SIGNS_DEPRECATED = (
HINT_PEP484_SIGNS_DEPRECATED
Expand All @@ -65,7 +89,7 @@
Further commentary.
'''

# ....................{ SETS ~ category }....................
# ....................{ SIGNS ~ category }....................
HINT_PEP_SIGNS_SEQUENCE_STANDARD = (
HINT_PEP484_SIGNS_SEQUENCE_STANDARD |
HINT_PEP585_SIGNS_SEQUENCE_STANDARD
Expand Down Expand Up @@ -117,7 +141,7 @@
constraining *all* items of compliant tuples).
'''

# ....................{ SETS ~ supported }....................
# ....................{ SIGNS ~ supported }....................
HINT_PEP_SIGNS_SUPPORTED_SHALLOW = (
HINT_PEP484_SIGNS_SUPPORTED_SHALLOW
)
Expand Down Expand Up @@ -148,7 +172,7 @@
type hint annotated by a subscription of that attribute.
'''

# ....................{ SETS ~ type }....................
# ....................{ SIGNS ~ type }....................
HINT_PEP_SIGNS_TYPE = (
HINT_PEP484_SIGNS_TYPE |
HINT_PEP585_SIGNS_TYPE
Expand Down Expand Up @@ -180,7 +204,7 @@
by the :func:`beartype.beartype` decorator.
'''

# ....................{ SETS ~ supported : all }....................
# ....................{ SIGNS ~ supported : all }....................
HINT_PEP_SIGNS_SUPPORTED = (
# Set of all deeply supported signs.
HINT_PEP_SIGNS_SUPPORTED_DEEP |
Expand Down
28 changes: 14 additions & 14 deletions beartype/_util/hint/data/pep/utilhintdatapepattr.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
# See "LICENSE" for further details.

'''
**Beartype Python version-agnostic signs** (i.e., arbitrary objects uniquely
identifying PEP-compliant type hints in a safe, non-deprecated manner
Project-wide **Python version-agnostic type hint attributes** (i.e., attributes
usable for creating PEP-compliant type hints in a safe, non-deprecated manner
regardless of the Python version targeted by the active Python interpreter).
This private submodule is *not* intended for importation by downstream callers.
Expand All @@ -16,28 +16,28 @@
from beartype._util.utilobject import SENTINEL
from typing import Any

# ....................{ SIGNS ~ declare }....................
# ....................{ ATTRS ~ declare }....................
# Default *ALL* signs (particularly signs with *NO* sane fallbacks) to a
# sentinel guaranteed to *NEVER* match.

# Initialized below.
HINT_PEP_SIGN_LIST: Any = SENTINEL
HINT_PEP_ATTR_LIST: Any = SENTINEL
'''
**List sign** (i.e., arbitrary object uniquely identifying PEP-compliant list
type hints) importable under the active Python interpreter.
'''


# Initialized below.
HINT_PEP_SIGN_TUPLE: Any = SENTINEL
HINT_PEP_ATTR_TUPLE: Any = SENTINEL
'''
**Tuple sign** (i.e., arbitrary object uniquely identifying PEP-compliant tuple
type hints) importable under the active Python interpreter.
'''


# Initialized below.
HINT_PEP586_SIGN_LITERAL: Any = SENTINEL
HINT_PEP586_ATTR_LITERAL: Any = SENTINEL
'''
**Literal sign** (i.e., arbitrary object uniquely identifying
:pep:`586`-compliant type hints) importable under the active Python
Expand All @@ -46,14 +46,14 @@


# Initialized below.
HINT_PEP593_SIGN_ANNOTATED: Any = SENTINEL
HINT_PEP593_ATTR_ANNOTATED: Any = SENTINEL
'''
**Annotated sign** (i.e., arbitrary object uniquely identifying
:pep:`593`-compliant type metahints) importable under the active Python
interpreter.
'''

# ....................{ SIGNS ~ define }....................
# ....................{ ATTRS ~ define }....................
# Signs conditionally dependent on the major version of Python targeted by
# the active Python interpreter.

Expand All @@ -62,19 +62,19 @@
from typing import Annotated, Literal # type: ignore[attr-defined]

# Initialize PEP 585-compliant types.
HINT_PEP_SIGN_LIST = list
HINT_PEP_SIGN_TUPLE = tuple
HINT_PEP_ATTR_LIST = list
HINT_PEP_ATTR_TUPLE = tuple

# Initialize PEP 586-compliant types.
HINT_PEP586_SIGN_LITERAL = Literal
HINT_PEP586_ATTR_LITERAL = Literal

# Initialize PEP 593-compliant types.
HINT_PEP593_SIGN_ANNOTATED = Annotated
HINT_PEP593_ATTR_ANNOTATED = Annotated
# Else...
else:
from typing import List, Tuple

# Default PEP 585-compliant types unavailable under this interpreter to
# corresponding albeit deprecated "typing" singletons.
HINT_PEP_SIGN_LIST = List
HINT_PEP_SIGN_TUPLE = Tuple
HINT_PEP_ATTR_LIST = List
HINT_PEP_ATTR_TUPLE = Tuple

0 comments on commit 626879c

Please sign in to comment.