from .zombie import UNARY_METHODS, BINARY_METHODS, RBINARY_METHODS, ARBITRARY_METHODS
from ..functions import call
[docs]class Proxy:
"""
Base class for proxy types.
"""
__slots__ = ("_proxy_obj_",)
def __init__(self, obj):
self._proxy_obj_ = obj
def __repr__(self):
return f"{type(self).__name__}({repr(self.__get_object())})"
def __get_object(self):
return self._proxy_obj_
def __call__(self, *args, **kwargs):
return self._proxy_obj_(*args, **kwargs)
def __getattr__(self, attr):
obj = self._proxy_obj_
return getattr(obj, attr)
# noinspection PyMissingConstructor
[docs]class deferred(Proxy):
"""
Wraps uninitialized object into a proxy shell.
Object is declared as a thunk and is initialized the first time some
attribute or method is requested.
The proxy delegates all methods to the lazy object. Proxies work nicely with
duck typing, but are a poor fit to code that relies in explicit instance
checks since the deferred object is a :class:`Proxy` instance.
Usage:
>>> from operator import add
>>> x = sk.deferred(add, 40, 2) # add function not called yet
>>> print(x) # any interaction triggers object construction!
42
"""
__slots__ = ("_deferred_constructor_",)
def __init__(self, func, *args, **kwargs):
init = lambda: func(*args, **kwargs)
self._deferred_constructor_ = init
def __repr__(self):
# return f"{type(self).__name__}({repr(self._Proxy__get_object())})"
return f"Proxy({repr(self._Proxy__get_object())})"
# noinspection PyPep8Naming
def _Proxy__get_object(self):
try:
return self._proxy_obj_
except AttributeError:
self._proxy_obj_ = obj = self._deferred_constructor_()
return obj
def __getattr__(self, attr):
if attr == "_proxy_obj_":
self._proxy_obj_ = obj = self._deferred_constructor_()
del self._deferred_constructor_
return obj
try:
obj = self._proxy_obj_
except KeyError:
obj = self._Proxy__get_object()
return getattr(obj, attr)
@call()
def _patch_proxy():
definitions = []
template = (
"def __{name}__(self{sep}{args}):\n"
" return self._proxy_obj_.__{name}__({args})"
)
for name in UNARY_METHODS:
code = template.format(name=name, sep="", args="")
definitions.append(code)
for name in BINARY_METHODS + RBINARY_METHODS:
code = template.format(name=name, sep=", ", args="other")
definitions.append(code)
for name in ARBITRARY_METHODS:
code = template.format(name=name, sep=", ", args="*args, **kwargs")
definitions.append(code)
ns = {}
code = "\n".join(definitions)
exec(code, {}, ns)
for k, v in ns.items():
setattr(Proxy, k, v)