Skip to content

Commit

Permalink
Postponed evaluation of non-referential hints x 12.
Browse files Browse the repository at this point in the history
This commit is the last in a commit chain generalizing @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 references** (i.e.,
references to user-defined *objects* that have yet to be defined in the
current lexical scope, regardless of whether those objects are types or
not), en-route to resolving feature request #226 kindly submitted by
Oxford Aspiring Algorithms Aficionado (AAA) @eohomegrownapps (Euan Ong).
Specifically, this commit finishes migrating the PEP 563-specific
stringified type hint resolution implemented by our public (albeit
undocumented) `beartype.peps.resolve_pep563()` resolver to a new private
`beartype._check.forward.fwdhint.resolve_hint()` resolver, implementing
a PEP 563-agnostic stringified type hint resolution. Needless to say,
everything works, everything is tested, and even the Sun itself has now
return to its comfortable orbit. Praise the Sun! In theory, this
resolution should also suffice to resolve *all* other outstanding issues
pertaining to both forward hints and PEP 563. In practice, everything
else that is broken is probably still broken. Let us exhaustedly pass
out already, GitHub! (*Pneumatic phlegmatics!*)
  • Loading branch information
leycec committed Aug 26, 2023
1 parent 9f58fc6 commit a18d9a5
Show file tree
Hide file tree
Showing 9 changed files with 333 additions and 191 deletions.
10 changes: 0 additions & 10 deletions beartype/_check/checkcall.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
'''

# ....................{ IMPORTS }....................
from beartype.peps import resolve_pep563
from beartype.roar import BeartypeDecorWrappeeException
from beartype.typing import (
Callable,
Expand Down Expand Up @@ -398,15 +397,6 @@ def reinit(
exception_cls=BeartypeDecorWrappeeException,
)

#FIXME: Stop calling this function in favour of
#"self.func_wrappee_scope_forward", please.
# Resolve all postponed hints on this callable if any *BEFORE* parsing
# the actual hints these postponed hints refer to.
resolve_pep563(
func=self.func_wrappee,
cls_stack=self.cls_stack,
)

# Efficiently reduce this local scope back to the dictionary of all
# parameters unconditionally required by *ALL* wrapper functions.
self.func_wrapper_scope.clear()
Expand Down
13 changes: 5 additions & 8 deletions beartype/_check/convert/convcoerce.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,11 @@ def coerce_func_hint_root(
# refers *BEFORE* performing any subsequent logic with this hint -- *ALL* of
# which assumes this hint to be a non-string hint.
if isinstance(hint, str):
pass

#FIXME: Uncomment after worky, please. *sigh*
# hint = resolve_hint(
# hint=hint,
# bear_call=bear_call,
# exception_prefix=exception_prefix,
# )
hint = resolve_hint(
hint=hint,
bear_call=bear_call,
exception_prefix=exception_prefix,
)
# Else, this hint is *NOT* stringified.
#
# In either case, this hint is guaranteed to now be a non-string hint.
Expand Down
251 changes: 168 additions & 83 deletions beartype/_check/forward/_fwdref.py

Large diffs are not rendered by default.

15 changes: 3 additions & 12 deletions beartype/_check/forward/fwdscope.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ def __missing__(self, hint_name: str) -> Type[
Returns
----------
_BeartypeForwardRefIndexableABC
Type[_BeartypeForwardRefIndexableABC]
Forward reference proxy deferring the resolution of this unresolved
type hint.
Expand All @@ -181,18 +181,9 @@ def __missing__(self, hint_name: str) -> Type[
)
# Else, this type hint name is syntactically valid.

# If this type hint name contains *NO* "." delimiters and is thus
# relative to this scope...
if '.' not in hint_name:
# Canonicalize this relative type hint name into an absolute type
# hint name relative to the name of this scope.
hint_name = f'{self._scope_name}.{hint_name}'
# Else, this type hint name contains one or more "." delimiters and is
# thus absolute (i.e., fully-qualified). In this case, preserve this
# name as is.

# Forward reference proxy to be returned.
forwardref_subtype = make_forwardref_indexable_subtype(hint_name)
forwardref_subtype = make_forwardref_indexable_subtype(
self._scope_name, hint_name)

# Return this proxy. The superclass dict.__getitem__() dunder method
# then implicitly maps the passed unresolved type hint name to this
Expand Down
2 changes: 1 addition & 1 deletion beartype/_conf/confcls.py
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,7 @@ def __repr__(self) -> str:

return (
f'{self.__class__.__name__}('
f'claw_is_pep526={repr(self._claw_is_pep526)}'
f'claw_is_pep526={repr(self._claw_is_pep526)}'
f', is_color={repr(self._is_color)}'
f', is_debug={repr(self._is_debug)}'
f', is_pep484_tower={repr(self._is_pep484_tower)}'
Expand Down
52 changes: 22 additions & 30 deletions beartype/_data/hint/datahinttyping.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
GeneratorType,
)

# ....................{ HINTS ~ boolean }....................
# ....................{ BOOL }....................
BoolTristate = Literal[True, False, None]
'''
PEP-compliant type hint matching a **tri-state boolean** whose value may be
Expand Down Expand Up @@ -72,7 +72,7 @@
:data:`None` does *not* suffice to decide this decision problem.
'''

# ....................{ HINTS ~ callable ~ early }....................
# ....................{ CALLABLE ~ early }....................
# Callable-specific type hints required by subsequent type hints below.

CallableAny = Callable[..., Any]
Expand All @@ -81,7 +81,7 @@
all possible callable signatures.
'''

# ....................{ HINTS ~ typevar }....................
# ....................{ TYPEVAR ~ early }....................
# Type variables required by subsequent type hints below.

BeartypeableT = TypeVar(
Expand Down Expand Up @@ -127,14 +127,7 @@
including metaclasses and method-resolution orders (MRO) of those classes.
'''

# ....................{ HINTS ~ callable ~ args }....................
CallableMethodGetitemArg = Union[int, slice]
'''
PEP-compliant type hint matching the standard type of the single positional
argument accepted by the ``__getitem__` dunder method.
'''

# ....................{ HINTS ~ callable ~ late }....................
# ....................{ CALLABLE }....................
# Callable-specific type hints *NOT* required by subsequent type hints below.

CallableTester = Callable[[object], bool]
Expand All @@ -160,7 +153,14 @@
* Pure-Python callable stack frames.
'''

# ....................{ HINTS ~ callable ~ late : decor }....................
# ....................{ CALLABLE ~ args }....................
CallableMethodGetitemArg = Union[int, slice]
'''
PEP-compliant type hint matching the standard type of the single positional
argument accepted by the ``__getitem__` dunder method.
'''

# ....................{ CALLABLE ~ decor }....................
BeartypeConfedDecorator = Callable[[BeartypeableT], BeartypeableT]
'''
PEP-compliant type hint matching a **configured beartype decorator** (i.e.,
Expand All @@ -177,7 +177,7 @@
in both configuration and decoration modes.
'''

# ....................{ HINTS ~ code }....................
# ....................{ CODE }....................
LexicalScope = Dict[str, Any]
'''
PEP-compliant type hint matching a **lexical scope** (i.e., dictionary mapping
Expand Down Expand Up @@ -208,15 +208,7 @@
List['YoClass']]``).
'''

# ....................{ HINTS ~ dict }....................
DictStrToAny = Dict[str, object]
'''
PEP-compliant type hint matching a dictionary whose keys are *all* strings
(e.g., keyword arguments ``**kwargs`` dictionary, object ``__dict__``
dictionary).
'''


# ....................{ DICT }....................
MappingStrToAny = Mapping[str, object]
'''
PEP-compliant type hint matching a mapping whose keys are *all* strings.
Expand All @@ -232,13 +224,13 @@
their identifying sign).
'''

# ....................{ HINTS ~ iterable }....................
# ....................{ ITERABLE }....................
IterableStrs = Iterable[str]
'''
PEP-compliant type hint matching *any* iterable of zero or more strings.
'''

# ....................{ HINTS ~ path }....................
# ....................{ PATH }....................
CommandWords = IterableStrs
'''
PEP-compliant type hint matching **command words** (i.e., an iterable of one or
Expand All @@ -247,7 +239,7 @@
test-specific :mod:`beartype_test._util.command.pytcmdrun` submodule).
'''

# ....................{ HINTS ~ kind : tuple }....................
# ....................{ TUPLE }....................
TupleTypes = Tuple[type, ...]
'''
PEP-compliant type hint matching a tuple of zero or more classes.
Expand All @@ -266,7 +258,7 @@
to the :func:`isinstance` and :func:`issubclass` builtins.
'''

# ....................{ HINTS ~ kind : tuple : stack }....................
# ....................{ TUPLE ~ stack }....................
TypeStack = Optional[Tuple[type, ...]]
'''
PEP-compliant type hint matching a **type stack** (i.e., either tuple of zero or
Expand Down Expand Up @@ -338,7 +330,7 @@ class hierarchy on the current call stack (if any) by leveraging the total
metadata, as trivially provided by the length of this tuple.
'''

# ....................{ HINTS ~ mod : importlib }....................
# ....................{ MODULE ~ importlib }....................
# Type hints specific to the standard "importlib" package.

ImportPathHook = Callable[[str], PathEntryFinder]
Expand All @@ -348,7 +340,7 @@ class hierarchy on the current call stack (if any) by leveraging the total
creating and leveraging a new :class:`importlib.machinery.FileLoader` instance).
'''

# ....................{ HINTS ~ mod : pathlib }....................
# ....................{ MODULE ~ pathlib }....................
# Type hints specific to the standard "pathlib" package.

PathnameLike = Union[str, Path]
Expand All @@ -366,7 +358,7 @@ class hierarchy on the current call stack (if any) by leveraging the total
instances definitely encapsulating pathnames).
'''

# ....................{ HINTS ~ pep : 484 }....................
# ....................{ PEP 484 }....................
# Type hints required to fully comply with PEP 484.

Pep484TowerComplex = Union[complex, float, int]
Expand All @@ -382,7 +374,7 @@ class hierarchy on the current call stack (if any) by leveraging the total
(i.e., both floating-point numbers and integers).
'''

# ....................{ HINTS ~ type }....................
# ....................{ TYPE }....................
TypeException = Type[Exception]
'''
PEP-compliant type hint matching *any* exception class.
Expand Down
11 changes: 7 additions & 4 deletions beartype/_util/cls/utilclsmake.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@
from beartype.roar._roarexc import _BeartypeUtilTypeException
from beartype.typing import (
Optional,
Tuple,
Type,
)
from beartype._cave._cavemap import NoneTypeOr
from beartype._data.hint.datahinttyping import LexicalScope
from beartype._data.hint.datahinttyping import (
LexicalScope,
TupleTypes,
TypeException,
)
from beartype._data.kind.datakinddict import DICT_EMPTY
from beartype._util.text.utiltextident import die_unless_identifier

Expand All @@ -29,10 +32,10 @@ def make_type(

# Optional arguments.
type_module_name: Optional[str] = None,
type_bases: Optional[Tuple[type, ...]] = None,
type_bases: Optional[TupleTypes] = None,
type_scope: Optional[LexicalScope] = None,
type_doc: Optional[str] = None,
exception_cls: Type[Exception] = _BeartypeUtilTypeException,
exception_cls: TypeException = _BeartypeUtilTypeException,
) -> type:
'''
Dynamically create and return a new class with the passed name subclassing
Expand Down

0 comments on commit a18d9a5

Please sign in to comment.