Skip to content

Commit

Permalink
beartype.peps.resolve_pep563() refactor x 1.
Browse files Browse the repository at this point in the history
This commit is the first in a commit chain fundamentally refactoring our
public **PEP 563 resolver** (i.e., the `beartype.peps.resolve_pep563()`
function) from its current ad-hoc (and frankly broken) implementation to
our newly resuscitated support for stringified type hints via our
private `beartype._check.forward.fwdhint.resolve_hint()` function.
Specifically, this commit:

* Defines a new private `beartype._check.checkcall.make_beartype_call()`
  factory function streamlining the instantiation and initialization of
  `beartype._check.checkcall.BeartypeCall` dataclass instances.
* Splits our PEP 563 unit tests into:
  * The existing `beartype_test.a00_unit.a40_api.peps.test_pep563`
    submodule, containing only unit tests specific to the
    `beartype.peps.resolve_pep563()` function.
  * A new `beartype_test.a70_decor.a40_code.a90_pep.test_decorpep563`
    submodule, containing all remaining PEP 563-specific unit tests.

(*Splurge on a spongy purge!*)
  • Loading branch information
leycec committed Aug 30, 2023
1 parent e82a1f7 commit a98121f
Show file tree
Hide file tree
Showing 7 changed files with 416 additions and 348 deletions.
72 changes: 62 additions & 10 deletions beartype/_check/checkcall.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,21 @@
LexicalScope,
TypeStack,
)
from beartype._util.func.utilfunccodeobj import (
get_func_codeobj,
# get_func_codeobj_or_none,
)
from beartype._util.cache.pool.utilcachepoolobjecttyped import (
acquire_object_typed)
from beartype._util.func.utilfunccodeobj import get_func_codeobj
from beartype._util.func.utilfunctest import (
is_func_coro,
is_func_nested,
)
from beartype._util.func.utilfuncwrap import (
# unwrap_func_all,
unwrap_func_all_closures_isomorphic,
)
from beartype._util.func.utilfuncwrap import unwrap_func_all_closures_isomorphic

# ....................{ CLASSES }....................
class BeartypeCall(object):
'''
**Beartype data** (i.e., object aggregating *all* metadata for the callable
currently being decorated by the :func:`beartype.beartype` decorator).**
**Beartype call metadata** (i.e., object encapsulating *all* metadata for
the user-defined callable currently being decorated by the
:func:`beartype.beartype` decorator).
Design
----------
Expand Down Expand Up @@ -472,6 +469,61 @@ def reinit(
self.func_wrapper_code_call_prefix = ''
self.func_wrapper_code_signature_prefix = ''

# ....................{ FACTORIES }....................
#FIXME: Unit test us up, please.
def make_beartype_call(
# Mandatory parameters.
func: Callable,
conf: BeartypeConf,

# Variadic keyword parameters.
**kwargs
) -> BeartypeCall:
'''
**Beartype call metadata** (i.e., object encapsulating *all* metadata for
the passed user-defined callable, typically currently being decorated by the
:func:`beartype.beartype` decorator).
Caveats
----------
**This higher-level factory function should always be called in lieu of
instantiating the** :class:`.BeartypeCall` **class directly.** Why?
Brute-force efficiency. This factory efficiently reuses previously
instantiated :class:`.BeartypeCall` objects rather than inefficiently
instantiating new :class:`.BeartypeCall` objects.
**The caller must pass the metadata returned by this factory back to the**
:func:`beartype._util.cache.pool.utilcachepoolobjecttyped.release_object_typed`
**function.** If accidentally omitted, this metadata will simply be
garbage-collected rather than available for efficient reuse by this factory.
Although hardly a worst-case outcome, omitting that explicit call largely
defeats the purpose of calling this factory in the first place.
Parameters
----------
func : Callable
Callable to be described.
conf : BeartypeConf
Beartype configuration configuring :func:`beartype.beartype` uniquely
specific to this callable.
All remaining keyword parameters are passed as is to the
:meth:`.BeartypeCall.reinit` method.
Returns
----------
BeartypeCall
Beartype call metadata describing this callable.
'''

# Previously cached callable metadata reinitialized from that callable.
bear_call = acquire_object_typed(BeartypeCall)
bear_call.reinit(func, conf, **kwargs)

# Return this metadata.
return bear_call

# ....................{ GLOBALS ~ private }....................
_TypeStackOrNone = NoneTypeOr[tuple]
'''
Expand Down
9 changes: 1 addition & 8 deletions beartype/_check/forward/fwdhint.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,9 @@

# ....................{ IMPORTS }....................
from beartype.roar import BeartypeDecorHintForwardRefException
# from beartype._cave._cavemap import NoneTypeOr
from beartype._check.checkcall import BeartypeCall
from beartype._check.forward.fwdscope import BeartypeForwardScope
# from beartype._check.forward._fwdref import make_forwardref_indexable_subtype
from beartype._data.hint.datahinttyping import (
# LexicalScope,
TypeException,
# TypeStack,
)
from beartype._data.hint.datahinttyping import TypeException
from beartype._data.kind.datakinddict import DICT_EMPTY
from beartype._data.kind.datakindset import FROZENSET_EMPTY
from beartype._util.cls.utilclsget import get_type_locals
Expand All @@ -32,7 +26,6 @@
get_func_locals,
)
from beartype._util.module.utilmodget import get_object_module_name
from beartype._util.text.utiltextident import is_identifier
from builtins import __dict__ as func_builtins # type: ignore[attr-defined]

# ....................{ RESOLVERS }....................
Expand Down
9 changes: 4 additions & 5 deletions beartype/_decor/_decormore.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
MethodDecoratorPropertyType,
MethodDecoratorStaticType,
)
from beartype._check.checkcall import BeartypeCall
from beartype._check.checkcall import make_beartype_call
from beartype._conf.confcls import BeartypeConf
from beartype._conf.confenum import BeartypeStrategy
from beartype._data.hint.datahinttyping import (
Expand Down Expand Up @@ -71,7 +71,7 @@ def beartype_func(
specific to this callable.
All remaining keyword parameters are passed as is to the
:meth:`.BeartypeCall.reinit` method.
:meth:`beartype._check.checkcall.BeartypeCall.reinit` method.
Returns
----------
Expand Down Expand Up @@ -108,9 +108,8 @@ def beartype_func(
return func # type: ignore[return-value]
# Else, that callable is beartypeable. Let's do this, folks.

# Previously cached callable metadata reinitialized from that callable.
bear_call = acquire_object_typed(BeartypeCall)
bear_call.reinit(func, conf, **kwargs)
# Beartype call metadata describing that callable.
bear_call = make_beartype_call(func, conf, **kwargs)

# Generate the raw string of Python statements implementing this wrapper.
func_wrapper_code = generate_code(bear_call)
Expand Down
18 changes: 12 additions & 6 deletions beartype/peps/_pep563.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,15 @@
# See "LICENSE" for further details.

'''
**Beartype** :pep:`563` **support.** (i.e., low-level functions resolving
stringified PEP-compliant type hints implicitly postponed by the active Python
Beartype :pep:`563` **support** (i.e., low-level functions resolving stringified
:pep:`563`-compliant type hints implicitly postponed by the active Python
interpreter via a ``from __future__ import annotations`` statement at the head
of the external user-defined module currently being introspected).
This private submodule is *not* intended for importation by downstream callers.
'''

# ....................{ TODO }....................
#FIXME: *CRITICAL*. Refactor this submodule to defer to the new resolve_hint()
#resolver, please.

#FIXME: Conditionally emit a non-fatal PEP 563-specific warning when the active
#Python interpreter targets Python >= 3.10 *AND* the passed callable is nested.

Expand All @@ -27,6 +24,10 @@
FrozenSet,
Optional,
)
from beartype._conf.confcls import (
BEARTYPE_CONF_DEFAULT,
BeartypeConf,
)
from beartype._data.hint.datahinttyping import (
LexicalScope,
TypeStack,
Expand All @@ -53,6 +54,7 @@ def resolve_pep563(
func: Callable,

# Optional parameters.
conf: BeartypeConf = BEARTYPE_CONF_DEFAULT,
cls_stack: TypeStack = None,
) -> None:
'''
Expand Down Expand Up @@ -84,6 +86,10 @@ def resolve_pep563(
----------
func : Callable
Callable to resolve postponed annotations on.
conf : BeartypeConf, optional
Beartype configuration configuring :func:`beartype.beartype` uniquely
specific to this callable. Defaults to :data`.BEARTYPE_CONF_DEFAULT`,
the default beartype configuration.
cls_stack : TypeStack
Either:
Expand Down Expand Up @@ -147,7 +153,7 @@ def get_str(self) -> my_str:
Raises
----------
beartype.roar.BeartypePep563Exception
BeartypePep563Exception
If either:
* ``func`` is *not* a pure-Python callable.
Expand Down

0 comments on commit a98121f

Please sign in to comment.