Skip to content

Commit

Permalink
Postponed evaluation of self-referential type hints.
Browse files Browse the repository at this point in the history
This commit generalizes @beartype's support for forward references from
**type forward references** (i.e., references to user-defined *types*
that have yet to be defined in the current lexical scope) to **arbitrary
forward self-references** (i.e., references to user-defined instances of
the currently decorated class – which has yet to be defined in the
current lexical scope and thus requires forward references), resolving
feature request #217 kindly submitted by Google X JAX "Acronym Maestro"
@patrick-kidger (Patrick Kidger). Specifically, this commit unit tests
that the prior commit chain resolving feature request #226 "Postponed
evaluation of non-referential hints" also perfectly resolved this
adjacent feature request as well. Laziness prevails.
(*Prevalent multivalence predominates!*)
  • Loading branch information
leycec committed Aug 29, 2023
1 parent a18d9a5 commit 4f71159
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
# package-specific submodules at module scope.
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

# ....................{ TESTS ~ pass }....................
def test_pep484_forwardref_data_pass() -> None:
# ....................{ TESTS }....................
def test_pep484_forwardref_data() -> None:
'''
Test successful usage of the :func:`beartype.beartype` decorator with
respect to both PEP-compliant and -noncompliant forward references by
Expand All @@ -31,6 +31,7 @@ def test_pep484_forwardref_data_pass() -> None:
# Defer test-specific imports.
from beartype_test.a00_unit.data.hint.data_hintref import (
TheDarkestEveningOfTheYear,
WithSluggishSurge,
but_i_have_promises,
of_easy_wind,
stopping_by_woods_on,
Expand All @@ -39,7 +40,7 @@ def test_pep484_forwardref_data_pass() -> None:
# between_the_woods_and_frozen_lake,
)

# ..................{ UNNESTED }..................
# ..................{ LOCALS }..................
# Objects passed below to exercise forward references.
MILES_TO_GO = TheDarkestEveningOfTheYear('And miles to go before I sleep')
WOODS = TheDarkestEveningOfTheYear('The woods are lovely, dark and deep,')
Expand All @@ -49,13 +50,16 @@ def test_pep484_forwardref_data_pass() -> None:
TheDarkestEveningOfTheYear('With burning smoke, or where bitumen lakes'),
TheDarkestEveningOfTheYear('On black bare pointed islets ever beat'),
]
RUGGED_AND_DARK = WithSluggishSurge()

# ..................{ PASS }..................
# Assert these forward-referencing callables return the expected values.
assert but_i_have_promises(MILES_TO_GO) == MILES_TO_GO
assert of_easy_wind(WOODS) == WOODS
assert stopping_by_woods_on(LAKE) == LAKE
assert the_woods_are_lovely(KNOW) == KNOW
assert its_fields_of_snow(WITH_BURNING_SMOKE) == WITH_BURNING_SMOKE[0]
assert but_i_have_promises(MILES_TO_GO) is MILES_TO_GO
assert of_easy_wind(WOODS) is WOODS
assert stopping_by_woods_on(LAKE) is LAKE
assert the_woods_are_lovely(KNOW) is KNOW
assert its_fields_of_snow(WITH_BURNING_SMOKE) is WITH_BURNING_SMOKE[0]
assert RUGGED_AND_DARK.or_where_the_secret_caves() is RUGGED_AND_DARK

#FIXME: Disabled until we decide whether we want to bother trying to
#resolve nested forward references or not.
Expand All @@ -73,7 +77,7 @@ def test_pep484_forwardref_data_pass() -> None:
# assert to_stop_without(MY_LITTLE_HORSE) == MY_LITTLE_HORSE
# assert to_watch_his_woods(STOP) == STOP


# ....................{ TESTS ~ pass }....................
def test_pep484_forwardref_arg_pass() -> None:
'''
Test successful usage of the :func:`beartype.beartype` decorator for a
Expand Down
26 changes: 24 additions & 2 deletions beartype_test/a00_unit/data/hint/data_hintref.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,17 @@
# ....................{ IMPORTS }....................
from beartype import beartype
from beartype.typing import (
# Generic,
Generic,
List,
TypeVar,
Union,
)
from dataclasses import dataclass

# ....................{ PRIVATE ~ hints }....................
_T = TypeVar('_T')
'''
Arbitrary type variable reference in type hints defined below.
'''

# ....................{ FUNCTIONS ~ pep : discrete }....................
# Arbitrary functions annotated by PEP-compliant forward references defined as
Expand Down Expand Up @@ -124,6 +130,22 @@ class TheDarkestEveningOfTheYear(str):

pass


class WithSluggishSurge(Generic[_T]):
'''
Arbitrary generic declaring a method annotated by a forward reference
referring to an instance of this same generic.
'''

@beartype
def or_where_the_secret_caves(self) -> 'WithSluggishSurge[_T]':
'''
Arbitrary method annotated by a forward reference referring to an
instance of this same generic.
'''

return self

# ....................{ CLOSURES }....................
#FIXME: Technically, @beartype *MAYBE* could actually resolve nested forward
#references by dynamically inspecting the call stack (depending on whether
Expand Down

0 comments on commit 4f71159

Please sign in to comment.