Skip to content

Commit

Permalink
beartype.vale.Is[...] + functions.partial.
Browse files Browse the repository at this point in the history
This commit generalizes the `beartype.vale.Is[...]` validator factory to
accept **partials** (i.e., high-level pure-Python
:class:`functools.partial` callable objects wrapping lower-level
pure-Python callables), resolving feature request #363 kindly submitted
by "Computer Graphics and Visualization" sufferer @sylvorg, who
graciously sacrificed his entire undergraduate GPA for the gradual
betterment of @beartype. Your journey of woe and hardship will *not* be
forgotten, @sylvorg! Specifically, @beartype now accepts insane beartype
validators resembling:

```python
from beartype.door import is_bearable
from beartype.typing import Annotated
from beartype.vale import Is
from functools import partial

def is_true(ignorable_arg, obj):
    '''
    Tester function returning :data:`True` only if the passed object
    evaluates to :data:`True` when coerced into a boolean and whose first
    parameter is ignorable.
    '''

    return bool(obj)

# Partial of the is_true() tester defined above, effectively ignoring the
# "ignorable_arg" parameter accepted by that tester.
is_true_partial = partial(is_true, 'Gods. This code is literally unreadable.')

# Beartype validator matching only objects that evaluate to "True".
Truthy = Annotated[object, Is[is_true_partial]]

assert is_bearable('', Truthy) is False
assert is_bearable('Even lies are true now, huh?', Truthy) is True
```

Is this valuable? I have no idea. Let's pretend I did something useful
tonight so that I can sleep without self-recrimination. (*Confident confetti!*)
  • Loading branch information
leycec committed Apr 11, 2024
1 parent ce6cfbe commit 19c5fe8
Show file tree
Hide file tree
Showing 52 changed files with 934 additions and 432 deletions.
79 changes: 60 additions & 19 deletions beartype/_cave/_cavefast.py
Expand Up @@ -206,20 +206,6 @@ class by that name. To circumvents this obvious oversight, this global globally
'''

# ....................{ TYPES ~ call }....................
CallablePartialType = _functools.partial
'''
Type of all **pure-Python partial callables** (i.e., callables dynamically
wrapped by the function-like :class:`functools.partial` class, implemented in
pure Python).
Caveats
----------
This type does *not* distinguish between whether the original callable wrapped
by :class:`functools.partial` is C-based or pure Python -- only that some
callable of indeterminate origin is in fact wrapped.
'''


CallableCodeObjectType: Any = type((lambda: None).__code__)
'''
Type of all **code objects** (i.e., C-based objects underlying all pure-Python
Expand Down Expand Up @@ -549,14 +535,69 @@ class or instance attributes).
applies to generators implicitly created by Python itself.
'''

# ....................{ TYPES ~ class }....................
ClassDictType = type(type.__dict__)
# ....................{ TYPES ~ call : module : functools }....................
CallableFunctoolsPartialType = _functools.partial
'''
Type of all **pure-Python class dictionaries** (i.e., immutable mappings
officially referred to as "mapping proxies," whose keys are strictly constrained
for both efficiency and correctness to be Python identifier strings).
Pure-Python type of all **partial callables** (i.e., possibly C-based callable
wrapped by the pure-Python callable :class:`functools.partial` type).
Caveats
-------
This type does *not* distinguish between whether the original callable wrapped
by :class:`functools.partial` is C-based or pure Python -- only that some
callable of indeterminate origin is in fact wrapped.
'''


@_functools.lru_cache
def _lru_cache_func(n: int) -> int:
'''
Arbitrary :func:`functools.lru_cache`-memoized function defined solely to
inspect various dunder attributes common to all such functions.
'''

return n + 1


# If this submodule is currently being statically type-checked by a pure static
# type-checker, ignore false positives complaining that this type is not a type.
if TYPE_CHECKING:
class CallableFunctoolsLruCacheType(object): pass
# Else, this submodule is *NOT* currently being statically type-checked by a
# pure static type-checker. In this case, define this type properly. *sigh*
else:
CallableFunctoolsLruCacheType = type(_lru_cache_func)
'''
C-based type of all low-level private objects created and returned by the
:func:`functools.lru_cache` decorator (e.g.,
:class:`functools._lru_cache_wrapper`).
This type enables functionality elsewhere to reliably detect when a callable
has been decorated by that decorator.
'''
# print(f'LRU_CACHE_TYPE: {LRU_CACHE_TYPE}')


# Delete temporary private callables defined above as a negligible safety (and
# possible space complexity) measure.
del _lru_cache_func

# ....................{ TYPES ~ class }....................
# If this submodule is currently being statically type-checked by a pure static
# type-checker, ignore false positives complaining that this type is not a type.
if TYPE_CHECKING:
class ClassDictType(object): pass
# Else, this submodule is *NOT* currently being statically type-checked by a
# pure static type-checker. In this case, define this type properly. *sigh*
else:
ClassDictType = type(type.__dict__)
'''
Type of all **pure-Python class dictionaries** (i.e., immutable mappings
officially referred to as "mapping proxies," whose keys are strictly
constrained for both efficiency and correctness to be Python identifier
strings).
'''

# ....................{ TYPES ~ data }....................
ContainerType = _Container
'''
Expand Down
2 changes: 1 addition & 1 deletion beartype/_data/hint/datahintfactory.py
Expand Up @@ -24,7 +24,7 @@
TYPE_CHECKING,
)
from beartype._util.hint.utilhintfactory import TypeHintTypeFactory
from beartype._util.module.lib.utiltyping import import_typing_attr_or_fallback
from beartype._util.api.utilapityping import import_typing_attr_or_fallback

# ....................{ FACTORIES }....................
# Portably import the PEP 647-compliant "typing.TypeGuard" type hint factory
Expand Down
41 changes: 0 additions & 41 deletions beartype/_data/module/datamodfunctools.py

This file was deleted.

17 changes: 8 additions & 9 deletions beartype/_decor/_decornontype.py
Expand Up @@ -32,16 +32,15 @@
BeartypeableT,
)
from beartype._decor.wrap.wrapmain import generate_code
from beartype._util.cache.pool.utilcachepoolobjecttyped import (
release_object_typed)
from beartype._util.func.module.utilfuncmodbear import (
from beartype._util.api.utilapibeartype import (
is_func_unbeartypeable,
set_func_beartyped,
)
from beartype._util.func.module.utilfuncmodtest import (
is_func_contextlib_contextmanager,
is_func_functools_lru_cache,
)
from beartype._util.api.utilapicontextlib import (
is_func_contextlib_contextmanager)
from beartype._util.api.utilapifunctools import is_func_functools_lru_cache
from beartype._util.cache.pool.utilcachepoolobjecttyped import (
release_object_typed)
from beartype._util.func.utilfuncmake import make_func
from beartype._util.func.utilfunctest import is_func_python
from beartype._util.func.utilfuncwrap import unwrap_func_once
Expand Down Expand Up @@ -674,8 +673,8 @@ def beartype_pseudofunc(pseudofunc: BeartypeableT, **kwargs) -> BeartypeableT:
# def muh_lru_cache() -> None: pass
if is_func_functools_lru_cache(pseudofunc):
# Return a new callable decorating that callable with type-checking.
return beartype_pseudofunc_functools_lru_cache( # type: ignore[return-value]
pseudofunc=pseudofunc, **kwargs)
return beartype_pseudofunc_functools_lru_cache( # type: ignore
pseudofunc=pseudofunc, **kwargs) # pyright: ignore
# Else, this is *NOT* a C-based @functools.lru_cache-memoized callable.

# Replace the existing bound method descriptor to this __call__() dunder
Expand Down
File renamed without changes.
Expand Up @@ -15,7 +15,7 @@
from beartype._util.func.pep.utilpep484func import (
is_func_pep484_notypechecked)
from beartype._util.func.utilfuncget import get_func_annotations_or_none
from beartype._util.module.lib.utilsphinx import is_sphinx_autodocing
from beartype._util.api.utilapisphinx import is_sphinx_autodocing
from beartype._util.py.utilpyinterpreter import is_python_optimized
from collections.abc import Callable

Expand Down
Expand Up @@ -4,9 +4,8 @@
# See "LICENSE" for further details.

'''
Project-wide **module-specific callable testers** (i.e., utility functions
dynamically validating and inspecting various properties of passed callables
declared by standard modules and packages in Python's standard library).
Project-wide :mod:`contextlib` utilities (i.e., low-level callables handling the
standard :mod:`contextlib` module).
This private submodule is *not* intended for importation by downstream callers.
'''
Expand Down Expand Up @@ -48,13 +47,13 @@ def is_func_contextlib_contextmanager(func: Any) -> TypeGuard[Callable]:
Object to be inspected.
Returns
----------
-------
bool
:data:`True` only if this object is a
:func:`contextlib.contextmanager`-based isomorphic decorator closure.
See Also
----------
--------
beartype._data.func.datafunc.CONTEXTLIB_CONTEXTMANAGER_CO_NAME_QUALNAME
Further discussion.
'''
Expand Down Expand Up @@ -101,37 +100,3 @@ def is_func_contextlib_contextmanager(func: Any) -> TypeGuard[Callable]:
# callable satisfies the is_func_wrapper_isomorphic() tester, but that
# there's no benefit and a minor efficiency cost to doing so.
return func_codeobj_name == CONTEXTLIB_CONTEXTMANAGER_CODEOBJ_NAME


def is_func_functools_lru_cache(func: Any) -> TypeGuard[Callable]:
'''
:data:`True` only if the passed object is a
:func:`functools.lru_cache`-memoized **pseudo-callable** (i.e., low-level
C-based callable object both created and returned by the standard
:func:`functools.lru_cache` decorator).
This tester enables callers to detect when a user-defined callable has been
decorated by the :func:`functools.lru_cache` decorator, which creates
low-level C-based callable objects requiring special handling elsewhere.
Parameters
----------
func : object
Object to be inspected.
Returns
----------
bool
:data:`True` only if this object is a
:func:`functools.lru_cache`-memoized callable.
'''

# Defer heavyweight tester-specific imports with potential side effects --
# notably, increased costs to space and time complexity.
from beartype._data.module.datamodfunctools import (
LRU_CACHE_TYPE)

# Return true only if the type of that callable is the low-level C-based
# private type of all objects created and returned by the standard
# @functools.lru_cache decorator.
return type(func) is LRU_CACHE_TYPE
141 changes: 141 additions & 0 deletions beartype/_util/api/utilapifunctools.py
@@ -0,0 +1,141 @@
#!/usr/bin/env python3
# --------------------( LICENSE )--------------------
# Copyright (c) 2014-2024 Beartype authors.
# See "LICENSE" for further details.

'''
Project-wide :mod:`functools` utilities (i.e., low-level callables handling the
standard :mod:`functools` module).
This private submodule is *not* intended for importation by downstream callers.
'''

# ....................{ IMPORTS }....................
from beartype.typing import (
Any,
Tuple,
)
from beartype._cave._cavefast import (
CallableFunctoolsLruCacheType,
CallableFunctoolsPartialType,
)
from beartype._data.hint.datahintfactory import TypeGuard
from beartype._data.hint.datahinttyping import DictStrToAny
from collections.abc import (
Callable,
# Generator,
)

# ....................{ TESTERS }....................
def is_func_functools_lru_cache(func: Any) -> TypeGuard[
CallableFunctoolsLruCacheType]:
'''
:data:`True` only if the passed object is a
:func:`functools.lru_cache`-memoized **pseudo-callable** (i.e., low-level
C-based callable object both created and returned by the standard
:func:`functools.lru_cache` decorator).
This tester enables callers to detect when a user-defined callable has been
decorated by the :func:`functools.lru_cache` decorator, which creates
low-level C-based callable objects requiring special handling elsewhere.
Parameters
----------
func : object
Object to be inspected.
Returns
-------
bool
:data:`True` only if this object is a
:func:`functools.lru_cache`-memoized callable.
'''

# Defer heavyweight tester-specific imports with potential side effects --
# notably, increased costs to space and time complexity.

# Return true only if the type of that callable is the low-level C-based
# private type of all objects created and returned by the standard
# @functools.lru_cache decorator.
return isinstance(func, CallableFunctoolsLruCacheType)


def is_func_functools_partial(func: Any) -> TypeGuard[
CallableFunctoolsPartialType]:
'''
:data:`True` only if the passed object is a **partial** (i.e., pure-Python
callable :class:`functools.partial` object wrapping a possibly C-based
callable).
Parameters
----------
func : object
Object to be inspected.
Returns
-------
bool
:data:`True` only if this object is a
:func:`functools.partial`-wrapped callable.
'''

# Return true only if the type of that callable is the high-level
# pure-Python public type of all objects created and returned by the
# standard functools.partial() factory.
return isinstance(func, CallableFunctoolsPartialType)

# ....................{ GETTERS }....................
def get_func_functools_partial_args(
func: CallableFunctoolsPartialType) -> Tuple[tuple, DictStrToAny]:
'''
2-tuple ``(args, kwargs)`` providing the positional and keyword parameters
with which the passed **partial** (i.e., pure-Python callable
:class:`functools.partial` object directly wrapping this possibly C-based
callable) was originally partialized.
Parameters
----------
func : CallableFunctoolsPartialType
Partial to be inspected.
Returns
-------
Tuple[tuple, DictStrToAny]
2-tuple ``(args, kwargs)`` such that:
* ``args`` is the tuple of the zero or more positional parameters passed
to the callable partialized by this partial.
* ``kwargs`` is the dictionary mapping from the name to value of the
zero or more keyword parameters passed to the callable partialized by
this partial.
'''
assert isinstance(func, CallableFunctoolsPartialType), (
f'{repr(func)} not "function.partial"-wrapped callable.')

# Return a 2-tuple providing the positional and keyword parameters with
# which this partial was originally partialized.
return (func.args, func.keywords)

# ....................{ UNWRAPPERS }....................
def unwrap_func_functools_partial_once(
func: CallableFunctoolsPartialType) -> Callable:
'''
Possibly C-based callable directly wrapped by the passed **partial** (i.e.,
pure-Python callable :class:`functools.partial` object directly wrapping
this possibly C-based callable).
Parameters
----------
func : CallableFunctoolsPartialType
Partial to be unwrapped.
Returns
-------
Callable
Possibly C-based callable directly wrapped by this partial.
'''
assert isinstance(func, CallableFunctoolsPartialType), (
f'{repr(func)} not "function.partial"-wrapped callable.')

# Return the public "func" instance variable of this partial wrapper as is.
return func.func

0 comments on commit 19c5fe8

Please sign in to comment.