Proxy
Proxies are commonly used to add additional functionality to their target object or enforce assertions prior to forwarding operations to the target. For example, a proxy can wrap sensitive objects with access control or provide caching for expensive operations.
Two valuable properties that a proxy can provide are transparency and
lazy resolution. A transparent proxy behaves identically to its target object
by forwarding all operations on itself to the target. For example, given a
proxy p
of an arbitrary object v
, the types of v
and p
will be
equivalent, i.e., isinstance(p, type(v))
and any operation on p
will invoke the corresponding operation on v
.
A lazy or virtual proxy provides just-in-time resolution of its target object. In this case, the proxy is initialized with a factory rather than the target object. A factory is any object that is callable like a function and returns the target object. The proxy is lazy in that it does not call the factory to retrieve the target until it is first accessed. This process is referred to as resolving the proxy. Functionally, proxies have both pass-by-reference and pass-by-value attributes. The eventual user of the proxied data gets a copy, but unnecessary copies are avoided when the proxy is passed between multiple functions.
Creating Proxies¶
resolve_object()
will be called when the proxy p
does its
just-in-time resolution, and then p
will behave exactly like obj
.
A factory for a Proxy
can be
any callable object (i.e., object which implements __call__
).
Proxies are powerful because they can intercept and redefine functionality of an object while emulating the rest of the objects behavior.
- A proxy is an instance of its wrapped object.
- The proxy can do everything the numpy array can.
The Proxy
intercepts all calls to the object's magic functions
(__func_name__()
functions) and forwards the calls to target object.
If the target object has not yet been resolved and cached inside the proxy, the factory is invoked to retrieve the target object.
Generally, a proxy is only ever resolved once. However, when a proxy is serialized, only the factory is serialized, and when the proxy is deserialized again and used, the factory will be called again to resolve the object. In other words, only the factory of the proxy is serialized, not the cached target object.
Interacting with Proxies¶
While a proxy can be used just as one would normally use the proxy's target object, additional functions are provided for interacting with the proxy directly.
from proxystore.proxy import Proxy
from proxystore.proxy import extract
from proxystore.proxy import is_resolved
from proxystore.proxy import resolve
p = Proxy(...)
# Check if a proxy has been resolved yet
is_resolved(p)
# Force a proxy to resolve itself
resolve(p)
# Extract the wrapped object from the proxy
x = extract(p)
assert not isinstance(x, Proxy)