Skip to content

Commit

Permalink
Postponed evaluation of non-referential hints x 2.
Browse files Browse the repository at this point in the history
This commit is the next 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:

* Defines a new private `beartype._util.cls.utilclsmake` submodule.
* Defines a new `make_type()` utility function dynamically creating and
  returning new classes subclassing arbitrary superclasses containing
  arbitrary class attributes. Surprisingly, this function has been
  exhaustively tested and documented. Praise be to summer in Canada.

(*Untold cold calderas under an endurable morass!*)
  • Loading branch information
leycec committed Aug 12, 2023
1 parent 1f33612 commit 30c90a0
Show file tree
Hide file tree
Showing 39 changed files with 329 additions and 106 deletions.
4 changes: 2 additions & 2 deletions beartype/_cave/_caveabc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# See "LICENSE" for further details.

'''
**Beartype cave-specific abstract base classes (ABCs).**
:mod:`beartype.cave`-specific **abstract base classes (ABCs).**
'''

# ....................{ TODO }....................
Expand All @@ -27,7 +27,7 @@
# Fortunately, doing so is trivial; simply use the three-argument form of
# the type() constructor, as demonstrated by this StackOverflow answer:
# https://stackoverflow.com/a/14219244/2809027
# * *WAIT!* There's no need to call the type() constructor diroctly. Instead,
# * *WAIT!* There's no need to call the type() constructor directly. Instead,
# define a new make_type() function in this new submodule copied from the
# betse.util.type.classes.define_class() function (but renamed, obviously).
#* Replace the current manual definition of "_BoolType" below with an in-place
Expand Down
4 changes: 2 additions & 2 deletions beartype/_check/checkcall.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class BeartypeCall(object):
memoized callables** (e.g.,
:func:`beartype._check.code.codemake.make_func_wrapper_code`) **and
higher-level callables** (e.g.,
:func:`beartype._decor._wrap.wrapmain.generate_code`). Instead, memoized
:func:`beartype._decor.wrap.wrapmain.generate_code`). Instead, memoized
callables *must* return that state as additional return values up the call
stack to those higher-level callables. By definition, memoized callables
are *not* recalled on subsequent calls passed the same parameters. Since
Expand Down Expand Up @@ -276,7 +276,7 @@ def reinit(
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

# Avoid circular import dependencies.
from beartype._decor._error.errormain import get_beartype_violation
from beartype._decor.error.errormain import get_beartype_violation

# If this callable is uncallable, raise an exception.
if not callable(func):
Expand Down
2 changes: 1 addition & 1 deletion beartype/_check/checkmagic.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
'''
Name of the **private exception raising parameter** (i.e.,
:mod:`beartype`-specific parameter whose default value is the
:func:`beartype._decor._error.errormain.get_beartype_violation`
:func:`beartype._decor.error.errormain.get_beartype_violation`
function raising human-readable exceptions on call-time type-checking failures
passed to each wrapper function generated by the :func:`beartype.beartype`
decorator).
Expand Down
8 changes: 4 additions & 4 deletions beartype/_check/checkmake.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

# ....................{ TODO }....................
#FIXME: Create a new make_func_raiser_code() factory. After doing so, refactor
#the lower-level beartype._decor._wrap._wrapcode.make_func_wrapper_code()
#the lower-level beartype._decor.wrap._wrapcode.make_func_wrapper_code()
#factory in terms of that higher-level make_func_raiser_code() factory, please.
#
#Note that doing so *WILL* prove non-trivial. That's why this submodule has
Expand All @@ -23,10 +23,10 @@
#raising an exception by calling a beartype-specific exception handler that does
#*NOT* currently exist. To create that handler, we'll need to:
#* Generalize the existing decoration-specific
# "beartype._decor._error.errormain" submodule into a new general-purpose
# "beartype._decor.error.errormain" submodule into a new general-purpose
# "beartype._check._checkerror" submodule. To do so, initially just copy the
# former to the latter. Do *NOT* bother generalizing any other submodules of
# the "beartype._decor._error" subpackage, for the moment. One thing at a time.
# the "beartype._decor.error" subpackage, for the moment. One thing at a time.
#* Rename the *COPIED* beartype._check._checkerror.get_beartype_violation()
# getter to get_func_raiser_violation().
#* Refactor get_func_raiser_violation() to have a signature resembling:
Expand All @@ -47,7 +47,7 @@
# adds "ARG_NAME_RAISE_EXCEPTION" to "func_wrapper_scope" into the
# make_func_raiser_code() factory instead.
#* Refactor the original lower-level
# beartype._decor._error.errormain.get_beartype_violation() getter in terms of
# beartype._decor.error.errormain.get_beartype_violation() getter in terms of
# the new higher-level get_func_raiser_violation() getter.
#* Define a new make_func_raiser_code() factory. Note that:
# * This factory will need to generate a code snippet raising an exception. The
Expand Down
4 changes: 2 additions & 2 deletions beartype/_check/code/codemake.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ class variable or method annotated by this hint *or* :data:`None`).

# True only if one or more PEP-compliant type hints visitable from this
# root hint require a pseudo-random integer. If true, the higher-level
# beartype._decor._wrap.wrapmain.generate_code() function prefixes the body
# beartype._decor.wrap.wrapmain.generate_code() function prefixes the body
# of this wrapper function with code generating such an integer.
is_var_random_int_needed = False

Expand Down Expand Up @@ -661,7 +661,7 @@ def _enqueue_hint_child(pith_child_expr: str) -> str:
# otherwise (i.e., if this hint is irreducible).
#
# Note that the root hint has already been permanently sanified by the
# calling "beartype._decor._wrap.wrapmain" submodule and thus need
# calling "beartype._decor.wrap.wrapmain" submodule and thus need
# *NOT* be inefficiently resanified here.
if hints_meta_index_curr:
hint_curr = sanify_hint_any(
Expand Down
2 changes: 1 addition & 1 deletion beartype/_decor/_decormore.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from beartype._data.hint.datahinttyping import (
BeartypeableT,
)
from beartype._decor._wrap.wrapmain import generate_code
from beartype._decor.wrap.wrapmain import generate_code
from beartype._util.cache.pool.utilcachepoolobjecttyped import (
acquire_object_typed,
release_object_typed,
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ class variable or method annotated by this hint *or* :data:`None`).
the current call to that function) if that function generated such an
integer *or* ``None`` otherwise (i.e., if that function generated *no*
such integer). See the same parameter accepted by the higher-level
:func:`beartype._decor._error.errormain.get_beartype_violation`
:func:`beartype._decor.error.errormain.get_beartype_violation`
function for further details.
Attributes (Private)
Expand Down Expand Up @@ -288,12 +288,12 @@ def find_cause(self) -> 'ViolationCause':
This method is intentionally generalized to support objects both
satisfying and *not* satisfying hints as equally valid use cases. While
the parent
:func:`beartype._decor._error.errormain.get_beartype_violation` function
:func:`beartype._decor.error.errormain.get_beartype_violation` function
calling this method is *always* passed an object *not* satisfying the
passed hint, this method is under no such constraints. Why? Because this
method is also called to find which of an arbitrary number of objects
transitively nested in the object passed to
:func:`beartype._decor._error.errormain.get_beartype_violation` fails to
:func:`beartype._decor.error.errormain.get_beartype_violation` fails to
satisfy the corresponding hint transitively nested in the hint passed to
that function.
Expand Down Expand Up @@ -356,7 +356,7 @@ def find_cause(self) -> 'ViolationCause':
# If this hint is a tuple union...
if isinstance(self.hint, tuple):
# Avoid circular import dependencies.
from beartype._decor._error._errortype import (
from beartype._decor.error._errortype import (
find_cause_instance_types_tuple)

# Defer to the getter function specific to tuple unions.
Expand All @@ -366,7 +366,7 @@ def find_cause(self) -> 'ViolationCause':
# getter deferred to below raises a human-readable exception.
else:
# Avoid circular import dependencies.
from beartype._decor._error._errortype import (
from beartype._decor.error._errortype import (
find_cause_instance_type)

# Defer to the getter function specific to classes.
Expand All @@ -388,7 +388,7 @@ def find_cause(self) -> 'ViolationCause':
# type origin. In this case, this hint was type-checked shallowly.
):
# Avoid circular import dependencies.
from beartype._decor._error._errortype import (
from beartype._decor.error._errortype import (
find_cause_type_instance_origin)

# Defer to the getter function supporting hints originating from
Expand All @@ -399,7 +399,7 @@ def find_cause(self) -> 'ViolationCause':
# was type-checked deeply.
else:
# Avoid circular import dependencies.
from beartype._decor._error.errormain import (
from beartype._decor.error.errormain import (
PEP_HINT_SIGN_TO_GET_CAUSE_FUNC)

# Getter function returning the desired string for this attribute
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
HintSignForwardRef,
HintSignType,
)
from beartype._decor._error._errorcause import ViolationCause
from beartype._decor._error._util.errorutilcolor import color_hint
from beartype._decor.error._errorcause import ViolationCause
from beartype._decor.error._util.errorutilcolor import color_hint
from beartype._util.cls.utilclstest import is_type_subclass
from beartype._util.cls.pep.utilpep3119 import (
die_unless_type_isinstanceable,
Expand All @@ -36,7 +36,7 @@
get_hint_pep_origin_type_isinstanceable_or_none)
from beartype._util.text.utiltextjoin import join_delimited_disjunction_types
from beartype._util.text.utiltextlabel import label_type
from beartype._decor._error._util.errorutiltext import represent_pith
from beartype._decor.error._util.errorutiltext import represent_pith

# ....................{ GETTERS ~ instance : type }....................
def find_cause_instance_type(cause: ViolationCause) -> ViolationCause:
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
'''

# ....................{ IMPORTS }....................
from beartype._decor._error._errorcause import ViolationCause
from beartype._decor.error._errorcause import ViolationCause
from beartype._data.hint.pep.sign.datapepsigns import HintSignLiteral
from beartype._util.hint.pep.proposal.utilpep586 import (
get_hint_pep586_literals)
from beartype._util.text.utiltextjoin import join_delimited_disjunction
from beartype._decor._error._util.errorutiltext import represent_pith
from beartype._decor.error._util.errorutiltext import represent_pith

# ....................{ GETTERS }....................
def find_cause_literal(cause: ViolationCause) -> ViolationCause:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
# ....................{ IMPORTS }....................
from beartype.roar._roarexc import _BeartypeCallHintPepRaiseException
from beartype._data.hint.pep.sign.datapepsigns import HintSignAnnotated
from beartype._decor._error._errorcause import ViolationCause
from beartype._decor._error._util.errorutiltext import represent_pith
from beartype._decor.error._errorcause import ViolationCause
from beartype._decor.error._util.errorutiltext import represent_pith
from beartype._util.hint.pep.proposal.utilpep593 import (
get_hint_pep593_metadata,
get_hint_pep593_metahint,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

# ....................{ IMPORTS }....................
from beartype._data.hint.pep.sign.datapepsigns import HintSignNoReturn
from beartype._decor._error._errorcause import ViolationCause
from beartype._decor._error._util.errorutiltext import represent_pith
from beartype._decor.error._errorcause import ViolationCause
from beartype._decor.error._util.errorutiltext import represent_pith
from beartype._util.text.utiltextlabel import label_callable

# ....................{ GETTERS }....................
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
# ....................{ IMPORTS }....................
from beartype.roar._roarexc import _BeartypeCallHintPepRaiseException
from beartype._data.hint.pep.sign.datapepsignset import HINT_SIGNS_UNION
from beartype._decor._error._errorcause import ViolationCause
from beartype._decor._error._util.errorutilcolor import color_hint
from beartype._decor._error._util.errorutiltext import represent_pith
from beartype._decor.error._errorcause import ViolationCause
from beartype._decor.error._util.errorutilcolor import color_hint
from beartype._decor.error._util.errorutiltext import represent_pith
from beartype._util.hint.pep.utilpepget import (
get_hint_pep_origin_type_isinstanceable_or_none)
from beartype._util.hint.pep.utilpeptest import is_hint_pep
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

# ....................{ IMPORTS }....................
from beartype._data.hint.pep.sign.datapepsigns import HintSignGeneric
from beartype._decor._error._errorcause import ViolationCause
from beartype._decor._error._errortype import find_cause_instance_type
from beartype._decor.error._errorcause import ViolationCause
from beartype._decor.error._errortype import find_cause_instance_type
from beartype._util.hint.pep.proposal.pep484585.utilpep484585generic import (
get_hint_pep484585_generic_type,
iter_hint_pep484585_generic_bases_unerased_tree,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
from beartype._data.hint.pep.sign.datapepsigns import HintSignTuple
from beartype._data.hint.pep.sign.datapepsignset import (
HINT_SIGNS_SEQUENCE_ARGS_1)
from beartype._decor._error._errorcause import ViolationCause
from beartype._decor._error._errortype import (
from beartype._decor.error._errorcause import ViolationCause
from beartype._decor.error._errortype import (
find_cause_type_instance_origin)
from beartype._decor._error._util.errorutilcolor import color_type
from beartype._decor._error._util.errorutiltext import represent_pith
from beartype._decor.error._util.errorutilcolor import color_type
from beartype._decor.error._util.errorutiltext import represent_pith
from beartype._util.hint.pep.proposal.pep484585.utilpep484585 import (
is_hint_pep484585_tuple_empty)
from beartype._util.hint.utilhinttest import is_hint_ignorable
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def prefix_beartypeable_arg_value(
assert isinstance(arg_name, str), f'{repr(arg_name)} not string.'

# Avoid circular import dependencies.
from beartype._decor._error._util.errorutilcolor import color_repr
from beartype._decor.error._util.errorutilcolor import color_repr

# Human-readable string depicting this parameter name and value.
arg_name_value = color_repr(f'{arg_name}={represent_object(arg_value)}')
Expand Down Expand Up @@ -76,7 +76,7 @@ def prefix_beartypeable_return_value(
'''

# Avoid circular import dependencies.
from beartype._decor._error._util.errorutilcolor import color_repr
from beartype._decor.error._util.errorutilcolor import color_repr

# Create and return this label.
return (
Expand All @@ -103,7 +103,7 @@ def represent_pith(pith: object) -> str:
'''

# Avoid circular import dependencies.
from beartype._decor._error._util.errorutilcolor import (
from beartype._decor.error._util.errorutilcolor import (
color_error,
color_repr,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,13 @@
HINT_SIGNS_ORIGIN_ISINSTANCEABLE,
HINT_SIGNS_UNION,
)
from beartype._decor._error._errorcause import ViolationCause
from beartype._decor._error._util.errorutilcolor import (
from beartype._decor.error._errorcause import ViolationCause
from beartype._decor.error._util.errorutilcolor import (
color_hint,
color_repr,
strip_text_ansi_if_configured,
)
from beartype._decor._error._util.errorutiltext import (
from beartype._decor.error._util.errorutiltext import (
prefix_beartypeable_arg_value,
prefix_beartypeable_return_value,
)
Expand Down Expand Up @@ -417,24 +417,24 @@ def _init() -> None:
'''

# Defer heavyweight imports.
from beartype._decor._error._errortype import (
from beartype._decor.error._errortype import (
find_cause_instance_type_forwardref,
find_cause_subclass_type,
find_cause_type_instance_origin,
)
from beartype._decor._error._pep._pep484._errornoreturn import (
from beartype._decor.error._pep._pep484._errornoreturn import (
find_cause_noreturn)
from beartype._decor._error._pep._pep484._errorunion import (
from beartype._decor.error._pep._pep484._errorunion import (
find_cause_union)
from beartype._decor._error._pep._pep484585._errorgeneric import (
from beartype._decor.error._pep._pep484585._errorgeneric import (
find_cause_generic)
from beartype._decor._error._pep._pep484585._errorsequence import (
from beartype._decor.error._pep._pep484585._errorsequence import (
find_cause_sequence_args_1,
find_cause_tuple,
)
from beartype._decor._error._pep._errorpep586 import (
from beartype._decor.error._pep._errorpep586 import (
find_cause_literal)
from beartype._decor._error._pep._errorpep593 import (
from beartype._decor.error._pep._errorpep593 import (
find_cause_annotated)

# Map each originative sign to the appropriate getter *BEFORE* any other
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,18 @@
# from beartype._data.hint.datahinttyping import BeartypeableT

# ....................{ SUBCLASSES }....................
class BeartypeableScopeDeferrer(dict):
class BeartypeForwardScope(dict):
'''
**Beartypeable scope deferrer** (i.e., dictionary deferring the resolution
of a local or global scope of a **beartypeable** (i.e., class or callable
decorated by the :func:`beartype.beartype` decorator) when dynamically
evaluating stringified type hints for this beartypeable, including both
forward references *and* :pep:`563`-postponed type hints).
**Forward scope** (i.e., dictionary deferring the resolution of a local or
global scope of an arbitrary class or callable when dynamically evaluating
stringified type hints for that class or callable, including both forward
references *and* :pep:`563`-postponed type hints).
Attributes
----------
module_name : str
Fully-qualified name of the (sub)module declaring the local or global
scope deferred by this deferrer.
scope deferred by this forward scope.
'''

# ..................{ INITIALIZERS }..................
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
CodeGenerated,
TypeStack,
)
from beartype._decor._wrap.wrapsnip import (
from beartype._decor.wrap.wrapsnip import (
CODE_HINT_ROOT_PREFIX,
CODE_HINT_ROOT_SUFFIX,
CODE_HINT_ROOT_SUFFIX_CLS_STACK,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
bear_typistry,
register_typistry_forwardref,
)
from beartype._decor._wrap.wrapsnip import (
from beartype._decor.wrap.wrapsnip import (
CODE_INIT_ARGS_LEN,
CODE_PITH_ROOT_PARAM_NAME_PLACEHOLDER,
CODE_RETURN_CHECK_PREFIX,
Expand All @@ -58,7 +58,7 @@
PARAM_KIND_TO_CODE_LOCALIZE,
PEP484_CODE_CHECK_NORETURN,
)
from beartype._decor._wrap._wrapcode import make_func_wrapper_code
from beartype._decor.wrap._wrapcode import make_func_wrapper_code
from beartype._util.error.utilerror import (
EXCEPTION_PLACEHOLDER,
reraise_exception_placeholder,
Expand Down Expand Up @@ -92,7 +92,7 @@
def generate_code(
bear_call: BeartypeCall,

# "beartype._decor._wrap.wrapsnip" string globals required only for
# "beartype._decor.wrap.wrapsnip" string globals required only for
# their bound "str.format" methods.
CODE_RETURN_UNCHECKED_format: Callable = CODE_RETURN_UNCHECKED.format,
) -> str:
Expand Down
File renamed without changes.
Loading

0 comments on commit 30c90a0

Please sign in to comment.