-
Notifications
You must be signed in to change notification settings - Fork 383
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make Experiment objects picklable #874
Comments
Hi @vnmabus! Making the BTW, the webpage you linked is broken / doesn't load. |
Strange, the webpage loads for me. It is http://gael-varoquaux.info/programming/decoration-in-python-done-right-decorating-and-pickling.html from one of the lead scikit-learn developers. |
Strange, the link you just posted doesn't work for me either. Are you sure your browser didn't cache it? |
I just tried in my phone, and it works as well as in my laptop. |
@thequilo For me the link works. I think that is an issue with our University network. To summarize the webpage: Use a closure with |
You are right, I was able to access the webpage outside of the University network. The problem is by far not only the I looked into the source of
So in conclusion: I don't know how to make the decorators pickleable, a simple |
May I ask why |
That dependency was added before I started to work with sacred, so I don't know exactly why it was added, but it has a few nice properties and claims to "[go] way beyond existing mechanisms such as functools.wraps() to ensure that decorators preserve introspectability, signatures, type checking abilities etc. The decorators that can be constructed using this module will work in far more scenarios than typical decorators and provide more predictable and consistent behaviour" (source). I think that main benefit for sacred is that it makes it possible to detect if a wrapped function is bound or not, so something like this is possible: class A:
@ex.capture
def f(self, _config):
pass
@classmethod
@ex.capture
def f2(cls, _config):
pass
A.f2()
A().f() Without I'm not sure, though, if this functionality is actually required for |
I think that if you used |
Not exactly, import functools
def decorator(fn):
@functools.wraps(fn)
def wrapper():
return fn()
return wrapper
class A:
@decorator
def f(self):
pass
import inspect
inspect.ismethod(A().f.__wrapped__) gives |
Ok, so for checking parameters you have to inspect the Thus, both |
No, it only uses the |
I tried to remove |
I found a hacky way to make captured functions pickleable and keep the config/run/... attributes. I'm really not sure if something like this should be done or whether it bears any severe problems. Anyway, I figured I'll share it so that others can comment on it: import functools
class _PickleableFunctionProxy:
"""
This class is the key to making decorated functions pickleable when the
decorator returns an object.
It stores the wrapped function as a class variable and tells pickle
to serialize the stored reference instead of the object that is now at the
original function's location
"""
_stored_functions = {}
@classmethod
def _store_function(cls, fn):
fn_id = fn.__module__ + fn.__qualname__
cls._stored_functions[fn_id] = fn
return fn_id
@classmethod
def _load(cls, fn_id):
return cls(cls._stored_functions[fn_id])
def __init__(self, wrapped):
self.__wrapped__ = wrapped
self._fn_id = self._store_function(wrapped)
def __reduce_ex__(self, protocol):
return _PickleableFunctionProxy._load, (self._fn_id,)
def __call__(self, *args, **kwargs):
return self.__wrapped__(*args, **kwargs)
class CapturedFunction:
def __init__(self, wrapped, prefix=None):
self.logger = None
self.run = None
self.config = {}
self.rnd = None
self.prefix = prefix
functools.update_wrapper(self, wrapped)
self.__wrapped__ = _PickleableFunctionProxy(wrapped)
@property
def uses_randomness(self):
return "_seed" in self.arguments or "_rnd" in self.arguments
def __str__(self) -> str:
return self.__wrapped__.__wrapped__.__str__()
def __call__(self, *args, **kwargs):
# Captured function logic...
... |
Are there any news on this? :) |
Why are you building a hacky register instead of just following the |
That question is not so easy to answer (I myself had to think about it a bit) and could be a good starting point for a discussion. Pickle treats functions as immutable singleton objects, meaning that it assumes that you get the same object when you import the function (by name) multiple times (also across interpreter instances). The problem with captured function is that they have a mutable state (the
I'm not sure which one is best. There might even be alternatives. I currently prefer 1. |
Would it be possible to use class callables instead of functions, so that I would really like this problem to be solved, as it would make my life way easier. |
Experiment
objects should be picklable, so that one can create them in Python and then run them in parallel with multiprocessing/multinode utilities.Currently, trying to pickle an Experiment gives me:
because the
cli_option
decorator does not usefunctools.wraps
(see this for more info).The text was updated successfully, but these errors were encountered: