Skip to content

Commit

Permalink
"typing_extensions.Annotated" x 10.
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 refactors the codebase to identify
forward references via the internal
`beartype._util.hint.data.pep.sign.datapepsigns.HintSignForwardRef`
singleton rather than the external `typing.ForwardRef` class as well as
dramatically simplifying the definition of sign sets with a new
`beartype._util.hint.data.pep.sign.datapepsignset` submodule. (*Thaumaturgic urges!*)
  • Loading branch information
leycec committed Jun 12, 2021
1 parent 2e3e062 commit 1bcb84d
Show file tree
Hide file tree
Showing 45 changed files with 481 additions and 174 deletions.
20 changes: 10 additions & 10 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ efficiency, portability, and thrilling puns.
>>> quote_wiggum([b"Oh, my God! A horrible plane crash!", b"Hey, everybody! Get a load of this flaming wreckage!",])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 30, in __beartyped_quote_wiggum
File "<string>", line 30, in quote_wiggum
File "/home/springfield/beartype/lib/python3.9/site-packages/beartype/_decor/_code/_pep/_error/errormain.py", line 220, in raise_pep_call_exception
raise exception_cls(
beartype.roar.BeartypeCallHintPepParamException: @beartyped
Expand Down Expand Up @@ -355,7 +355,7 @@ the former as a default behaviour.
Hello, world!
Traceback (most recent call last):
File "<stdin>", line 6, in <module>
File "<string>", line 17, in __beartyped_main
File "<string>", line 17, in main
File "/home/leycec/py/beartype/beartype/_decor/_code/_pep/_error/errormain.py", line 218, in raise_pep_call_exception
raise exception_cls(
beartype.roar.BeartypeCallHintPepReturnException: @beartyped main() return
Expand Down Expand Up @@ -2780,7 +2780,7 @@ Let's see the wrapper function ``@beartype`` dynamically generated from that:
.. code-block:: python
def __beartyped_law_of_the_jungle_4(
def law_of_the_jungle_4(
*args,
__beartype_func=__beartype_func,
__beartypistry=__beartypistry,
Expand Down Expand Up @@ -2813,7 +2813,7 @@ Let's see the wrapper function ``@beartype`` dynamically generated from that:
Let's dismantle this bit by bit:
* The code comments above are verbatim as they appear in the generated code.
* ``__beartyped_law_of_the_jungle_4()`` is the ad-hoc function name
* ``law_of_the_jungle_4()`` is the ad-hoc function name
``@beartype`` assigned this wrapper function.
* ``__beartype_func`` is the original ``law_of_the_jungle_4()`` function.
* ``__beartypistry`` is a thread-safe global registry of all types, tuples of
Expand Down Expand Up @@ -2857,7 +2857,7 @@ Let's see the wrapper function ``@beartype`` dynamically generated from that:
.. code-block:: python
def __beartyped_law_of_the_jungle_5(
def law_of_the_jungle_5(
*args,
__beartype_func=__beartype_func,
__beartypistry=__beartypistry,
Expand Down Expand Up @@ -2919,7 +2919,7 @@ Let's see the wrapper function ``@beartype`` dynamically generated from that:
.. code-block:: python
def __beartyped_law_of_the_jungle_6(
def law_of_the_jungle_6(
*args,
__beartype_func=__beartype_func,
__beartypistry=__beartypistry,
Expand Down Expand Up @@ -2977,7 +2977,7 @@ Let's see the wrapper function ``@beartype`` dynamically generated from that:
.. code-block:: python
def __beartyped_law_of_the_jungle_7(
def law_of_the_jungle_7(
*args,
__beartype_func=__beartype_func,
__beartypistry=__beartypistry,
Expand Down Expand Up @@ -3062,7 +3062,7 @@ Let's see the wrapper function ``@beartype`` dynamically generated from that:
.. code-block:: python
def __beartyped_law_of_the_jungle_8(
def law_of_the_jungle_8(
*args,
__beartype_func=__beartype_func,
__beartypistry=__beartypistry,
Expand Down Expand Up @@ -3368,10 +3368,10 @@ you will want to at least:
iteration or other expensive operations should be memoized via our internal
`@callable_cached`_ decorator.
* Define a new data utility submodule for this PEP residing under the
`"beartype._util.hint.data.pep.proposal" subpackage <beartype util data
`"beartype._util.data.hint.pep.proposal" subpackage <beartype util data
pep_>`__ adding various signs (i.e., arbitrary objects uniquely identifying
type hints compliant with this PEP) to various global variables defined by
the parent `"beartype._util.hint.data.pep.utilhintdatapep" submodule
the parent `"beartype._util.data.hint.pep.utilhintdatapep" submodule
<_beartype util data pep parent>`__.
* Define a new test data submodule for this PEP residing under the
`"beartype_test.unit.data.hint.pep.proposal" subpackage <beartype test data
Expand Down
41 changes: 21 additions & 20 deletions beartype/_decor/_code/_pep/_pephint.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,22 +93,23 @@
add_func_scope_type,
add_func_scope_types,
)
from beartype._util.hint.data.datahint import HINTS_IGNORABLE_SHALLOW
from beartype._util.hint.data.pep.datapep import (
HINT_PEP_SIGNS_SUPPORTED_DEEP,
HINT_PEP_SIGNS_SEQUENCE_STANDARD,
HINT_PEP_SIGNS_TUPLE,
HINT_PEP_SIGNS_TYPE_ORIGIN_STDLIB,
from beartype._util.data.hint.datahint import HINTS_IGNORABLE_SHALLOW
from beartype._util.data.hint.pep.datapep import (
HINT_SIGNS_SUPPORTED_DEEP,
HINT_SIGNS_SEQUENCE_STANDARD,
HINT_SIGNS_TUPLE,
HINT_SIGNS_TYPE_ORIGIN_STDLIB,
)
from beartype._util.hint.data.pep.proposal.datapep484 import (
HINT_PEP484_TYPE_FORWARDREF,
HINT_PEP484_SIGNS_UNION,
)
from beartype._util.hint.data.pep.datapepattr import (
from beartype._util.data.hint.pep.proposal.datapep484 import (
HINT_PEP484_SIGNS_UNION)
from beartype._util.data.hint.pep.datapepattr import (
HINT_PEP586_ATTR_LITERAL,
HINT_PEP593_ATTR_ANNOTATED,
)
from beartype._util.hint.data.pep.sign.datapepsigns import HintSignGeneric
from beartype._util.data.hint.pep.sign.datapepsigns import (
HintSignForwardRef,
HintSignGeneric,
)
from beartype._util.hint.pep.proposal.utilhintpep484 import (
get_hint_pep484_generic_base_erased_from_unerased,
get_hint_pep484_newtype_class,
Expand Down Expand Up @@ -1045,7 +1046,7 @@ def _enqueue_hint_child(pith_child_expr: str) -> str:
# for that attribute *MUST* also be added to the parallel:
# * "beartype._util.hint.pep.errormain" submodule, which
# raises exceptions on the current pith failing this check.
# * "beartype._util.hint.data.pep.datapep.HINT_PEP_SIGNS_SUPPORTED_DEEP"
# * "beartype._util.data.hint.pep.datapep.HINT_SIGNS_SUPPORTED_DEEP"
# frozen set of all supported unsubscripted "typing" attributes
# for which this function generates deeply type-checking code.
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Expand Down Expand Up @@ -1313,9 +1314,9 @@ def _enqueue_hint_child(pith_child_expr: str) -> str:
(
# Originating from an origin type and thus trivially
# supportable with shallow type-checking *AND*...
hint_curr_sign in HINT_PEP_SIGNS_TYPE_ORIGIN_STDLIB and
hint_curr_sign in HINT_SIGNS_TYPE_ORIGIN_STDLIB and
# Currently unsupported with deep type-checking...
hint_curr_sign not in HINT_PEP_SIGNS_SUPPORTED_DEEP
hint_curr_sign not in HINT_SIGNS_SUPPORTED_DEEP
)
):
# Then generate trivial code shallowly type-checking the current
Expand Down Expand Up @@ -1364,9 +1365,9 @@ def _enqueue_hint_child(pith_child_expr: str) -> str:
# If this hint is either...
elif (
# A standard sequence (e.g., "typing.List[int]") *OR*...
hint_curr_sign in HINT_PEP_SIGNS_SEQUENCE_STANDARD or (
hint_curr_sign in HINT_SIGNS_SEQUENCE_STANDARD or (
# A tuple *AND*...
hint_curr_sign in HINT_PEP_SIGNS_TUPLE and
hint_curr_sign in HINT_SIGNS_TUPLE and
# This tuple is subscripted by exactly two child hints
# *AND*...
hint_childs_len == 2 and
Expand Down Expand Up @@ -1404,7 +1405,7 @@ def _enqueue_hint_child(pith_child_expr: str) -> str:
# TypeError: Too many parameters for typing.List; actual 2, expected 1
assert (
hint_childs_len == 1 or
hint_curr_sign in HINT_PEP_SIGNS_TUPLE
hint_curr_sign in HINT_SIGNS_TUPLE
), (
f'{hint_curr_label} {repr(hint_curr)} sequence '
f'subscripted by multiple arguments.')
Expand Down Expand Up @@ -1477,7 +1478,7 @@ def _enqueue_hint_child(pith_child_expr: str) -> str:
# Note that the "..." substring here is *NOT* a literal ellipses.
#
# This is what happens when non-human-readable APIs are promoted.
elif hint_curr_sign in HINT_PEP_SIGNS_TUPLE:
elif hint_curr_sign in HINT_SIGNS_TUPLE:
# Assert this tuple was subscripted by at least one child hint
# if this tuple is PEP 484-compliant. Note that the "typing"
# module should have already guaranteed this on our behalf.
Expand Down Expand Up @@ -1646,7 +1647,7 @@ def _enqueue_hint_child(pith_child_expr: str) -> str:

# ..............{ FORWARDREF }..............
# If this hint is a forward reference...
elif hint_curr_sign is HINT_PEP484_TYPE_FORWARDREF:
elif hint_curr_sign is HintSignForwardRef:
# Possibly unqualified classname referred to by this hint.
hint_curr_forwardref_classname = get_hint_forwardref_classname(
hint_curr)
Expand Down
2 changes: 1 addition & 1 deletion beartype/_decor/_code/_pep/pepcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def pep_code_check_param(

# Python code template localizing this parameter if this kind of parameter
# is supported *OR* "None" otherwise.
PARAM_LOCALIZE_TEMPLATE = PARAM_KIND_TO_PEP_CODE_LOCALIZE.get(
PARAM_LOCALIZE_TEMPLATE = PARAM_KIND_TO_PEP_CODE_LOCALIZE.get( # type: ignore
param.kind, None)

# If this kind of parameter is unsupported...
Expand Down
2 changes: 1 addition & 1 deletion beartype/_decor/_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ def reinit(self, func: Callable) -> None:
raise_pep_call_exception)

# Machine-readable name of the wrapper function to be generated.
self.func_wrapper_name = f'__beartyped_{func.__name__}'
self.func_wrapper_name = func.__name__

# Nullify all remaining attributes for safety *BEFORE* passing this
# object to any functions (e.g., resolve_hints_pep563_if_active()).
Expand Down
2 changes: 1 addition & 1 deletion beartype/_decor/_error/_errorgeneric.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from beartype._decor._error._errortype import (
get_cause_or_none_type)
from beartype._decor._error._errorsleuth import CauseSleuth
from beartype._util.hint.data.pep.sign.datapepsigns import HintSignGeneric
from beartype._util.data.hint.pep.sign.datapepsigns import HintSignGeneric
from beartype._util.hint.pep.proposal.utilhintpep484 import (
get_hint_pep484_generic_base_erased_from_unerased)
from beartype._util.hint.pep.proposal.utilhintpep585 import (
Expand Down
14 changes: 7 additions & 7 deletions beartype/_decor/_error/_errorsequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
# ....................{ IMPORTS }....................
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.datapep import (
HINT_PEP_SIGNS_SEQUENCE_STANDARD,
HINT_PEP_SIGNS_TUPLE,
from beartype._util.data.hint.pep.datapep import (
HINT_SIGNS_SEQUENCE_STANDARD,
HINT_SIGNS_TUPLE,
)
from beartype._util.hint.pep.utilhintpeptest import is_hint_pep_tuple_empty
from beartype._util.hint.utilhinttest import is_hint_ignorable
Expand Down Expand Up @@ -44,7 +44,7 @@ def get_cause_or_none_sequence_standard(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_PEP_SIGNS_SEQUENCE_STANDARD, (
assert sleuth.hint_sign in HINT_SIGNS_SEQUENCE_STANDARD, (
f'{repr(sleuth.hint)} not standard sequence hint.')

# Assert this sequence was subscripted by exactly one argument. Note that
Expand Down Expand Up @@ -86,7 +86,7 @@ def get_cause_or_none_tuple(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_PEP_SIGNS_TUPLE, (
assert sleuth.hint_sign in HINT_SIGNS_TUPLE, (
f'{repr(sleuth.hint_sign)} not tuple hint.')

# Human-readable string describing the failure of this pith to be a tuple
Expand Down Expand Up @@ -198,8 +198,8 @@ def _get_cause_or_none_sequence(sleuth: CauseSleuth) -> Optional[str]:
# expected variadic sequence, as the caller guarantees this to be the case.
assert isinstance(sleuth, CauseSleuth), f'{repr(sleuth)} not cause sleuth.'
assert (
sleuth.hint_sign in HINT_PEP_SIGNS_SEQUENCE_STANDARD or (
sleuth.hint_sign in HINT_PEP_SIGNS_TUPLE and
sleuth.hint_sign in HINT_SIGNS_SEQUENCE_STANDARD or (
sleuth.hint_sign in HINT_SIGNS_TUPLE and
len(sleuth.hint_childs) == 2 and
sleuth.hint_childs[1] is Ellipsis
)
Expand Down
5 changes: 2 additions & 3 deletions beartype/_decor/_error/_errortype.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.hint.data.pep.proposal.datapep484 import (
HINT_PEP484_TYPE_FORWARDREF)
from beartype._util.data.hint.pep.sign.datapepsigns import HintSignForwardRef
from beartype._util.hint.nonpep.utilhintnonpeptest import (
die_unless_hint_nonpep_tuple)
from beartype._util.hint.pep.utilhintpepget import (
Expand Down Expand Up @@ -47,7 +46,7 @@ def get_cause_or_none_forwardref(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_PEP484_TYPE_FORWARDREF, (
assert sleuth.hint_sign is HintSignForwardRef, (
f'PEP type hint sign {repr(sleuth.hint_sign)} not forward reference.')

# Fully-qualified classname referred to by this forward reference relative
Expand Down
2 changes: 1 addition & 1 deletion beartype/_decor/_error/_proposal/_errorpep484union.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# ....................{ IMPORTS }....................
from beartype.roar._roarexc import _BeartypeCallHintPepRaiseException
from beartype._decor._error._errorsleuth import CauseSleuth
from beartype._util.hint.data.pep.proposal.datapep484 import (
from beartype._util.data.hint.pep.proposal.datapep484 import (
HINT_PEP484_SIGNS_UNION)
from beartype._util.hint.pep.utilhintpepget import (
get_hint_pep_stdlib_type_or_none)
Expand Down
2 changes: 1 addition & 1 deletion beartype/_decor/_error/_proposal/_errorpep586.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
# ....................{ IMPORTS }....................
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.datapepattr import (
from beartype._util.data.hint.pep.datapepattr import (
HINT_PEP586_ATTR_LITERAL)
from beartype._util.text.utiltextjoin import join_delimited_disjunction
from beartype._util.text.utiltextrepr import represent_object
Expand Down
2 changes: 1 addition & 1 deletion beartype/_decor/_error/_proposal/_errorpep593.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
# ....................{ IMPORTS }....................
from beartype.roar._roarexc import _BeartypeCallHintPepRaiseException
from beartype._decor._error._errorsleuth import CauseSleuth
from beartype._util.hint.data.pep.datapepattr import (
from beartype._util.data.hint.pep.datapepattr import (
HINT_PEP593_ATTR_ANNOTATED)
from beartype._util.hint.pep.proposal.utilhintpep593 import (
get_hint_pep593_metadata,
Expand Down
25 changes: 13 additions & 12 deletions beartype/_decor/_error/errormain.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,17 @@
get_cause_or_none_literal)
from beartype._decor._error._proposal._errorpep593 import (
get_cause_or_none_annotated)
from beartype._util.hint.data.pep.datapep import (
HINT_PEP_SIGNS_SEQUENCE_STANDARD,
HINT_PEP_SIGNS_TUPLE,
HINT_PEP_SIGNS_TYPE_ORIGIN_STDLIB,
from beartype._util.data.hint.pep.datapep import (
HINT_SIGNS_SEQUENCE_STANDARD,
HINT_SIGNS_TUPLE,
HINT_SIGNS_TYPE_ORIGIN_STDLIB,
)
from beartype._util.hint.data.pep.proposal.datapep484 import (
HINT_PEP484_TYPE_FORWARDREF,
HINT_PEP484_SIGNS_UNION,
from beartype._util.data.hint.pep.proposal.datapep484 import (
HINT_PEP484_SIGNS_UNION)
from beartype._util.data.hint.pep.sign.datapepsigns import (
HintSignForwardRef,
HintSignGeneric,
)
from beartype._util.hint.data.pep.sign.datapepsigns import HintSignGeneric
from beartype._util.hint.utilhinttest import die_unless_hint
from beartype._util.py.utilpyversion import IS_PYTHON_AT_LEAST_3_9
from beartype._util.text.utiltextlabel import (
Expand Down Expand Up @@ -340,17 +341,17 @@ def _init() -> None:
# Map each originative "typing" attribute to the appropriate getter
# *BEFORE* mapping any other attributes. This is merely a generalized
# fallback subsequently replaced by attribute-specific getters.
for pep_sign_type_origin in HINT_PEP_SIGNS_TYPE_ORIGIN_STDLIB:
for pep_sign_type_origin in HINT_SIGNS_TYPE_ORIGIN_STDLIB:
PEP_HINT_SIGN_TO_GET_CAUSE_FUNC[pep_sign_type_origin] = (
get_cause_or_none_type_origin)

# Map each standard sequence "typing" attribute to the appropriate getter.
for pep_sign_sequence_standard in HINT_PEP_SIGNS_SEQUENCE_STANDARD:
for pep_sign_sequence_standard in HINT_SIGNS_SEQUENCE_STANDARD:
PEP_HINT_SIGN_TO_GET_CAUSE_FUNC[pep_sign_sequence_standard] = (
get_cause_or_none_sequence_standard)

# Map each tuple "typing" attribute to the appropriate getter.
for pep_sign_tuple in HINT_PEP_SIGNS_TUPLE:
for pep_sign_tuple in HINT_SIGNS_TUPLE:
PEP_HINT_SIGN_TO_GET_CAUSE_FUNC[pep_sign_tuple] = (
get_cause_or_none_tuple)

Expand All @@ -362,7 +363,7 @@ def _init() -> None:
# Map each "typing" attribute validated by a unique getter specific to that
# attribute to that getter.
PEP_HINT_SIGN_TO_GET_CAUSE_FUNC.update({
HINT_PEP484_TYPE_FORWARDREF: get_cause_or_none_forwardref,
HintSignForwardRef: get_cause_or_none_forwardref,
HintSignGeneric: get_cause_or_none_generic,
})

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,9 @@
'''

# ....................{ IMPORTS }....................
from beartype._util.hint.data.pep.datapep import (
HINT_PEP_SIGNS_IGNORABLE,
)
from beartype._util.hint.data.pep.proposal.datapep484 import (
HINT_PEP484_TYPE_FORWARDREF,
)
from beartype._util.data.hint.pep.datapep import HINT_SIGNS_IGNORABLE
from beartype._util.data.hint.pep.proposal.datapep484 import (
HINT_PEP484_TYPE_FORWARDREF)

# See the "beartype.cave" submodule for further commentary.
__all__ = ['STAR_IMPORTS_CONSIDERED_HARMFUL']
Expand Down Expand Up @@ -45,7 +42,7 @@
'''


HINTS_IGNORABLE_SHALLOW = HINT_PEP_SIGNS_IGNORABLE | {
HINTS_IGNORABLE_SHALLOW = HINT_SIGNS_IGNORABLE | {
# The PEP-noncompliant builtin "object" type is the transitive superclass
# of all classes, parameters and return values annotated as "object"
# unconditionally match *ALL* objects under isinstance()-based type
Expand Down
File renamed without changes.

0 comments on commit 1bcb84d

Please sign in to comment.