FIX Don't send ScoringMonitor._log to the pickler - #34821
Conversation
FrancoisPgm
left a comment
There was a problem hiding this comment.
Apart from a nitpick and a suggestion for a test to make codecov happy, LGTM, thanks.
|
|
||
| def persistent_id(self, obj): | ||
| if id(obj) in self._watched: | ||
| self.walked_through.add(obj.__class__.__name__) |
There was a problem hiding this comment.
Codecov seems unhappy that this line is not executed, a test could be added to test _TraversalRecorder with a callback that deliberately sends an unpicklable attr. Here is a suggestion for such a test:
class UnpicklableCallback:
"""A callback that can't be pickled.
This callbacks sends to pickle an attr that can be mutated during pickling by
another thread."""
def __init__(self):
self.unpicklable_attr = []
self._listener_handle = open_listener(self.unpicklable_attr.append, owner=self)
def setup(self, estimator, context):
pass
def on_fit_task_begin(self, estimator, context):
pass
def on_fit_task_end(self, estimator, context):
pass
def teardown(self, estimator, context):
pass
def test_TraversalRecorder(monkeypatch):
"""Test _checked's assert does fail with unpicklable callback."""
callback = UnpicklableCallback()
monkeypatch.setattr(
callback.__class__,
"on_fit_task_begin",
_checked(callback.__class__.on_fit_task_begin),
)
with pytest.raises(AssertionError):
MaxIterEstimator(max_iter=3).set_callbacks(callback).fit()
There was a problem hiding this comment.
I don't like adding a test to cover the test. The fact that the test fails on main is enough, that's what we usually consider enough. So I refactored it in a way that avoids the uncovered line.
lesteve
left a comment
There was a problem hiding this comment.
I am not going to pretend I understand everything that is going on, but the fix seems reasonable and this will make CircleCI green again on main
Fixes #34816
It should also fix the main CI.
Sending the
_log, that is being mutated by threads or subprocesses, to the pickler raises a pickling error, as seen in #34816. The error was added in Python 3.14.7 but the bug was already there silently before. This is why it was caught by #34806: the doc build bumped python version to 3.14.7.The issue and the fix is very similar to the one for RecordingCallback, see #34811, except that here we can't send an empty log since we want to be able to pickle and unpickle the callback and retrieve the log. So we send a snapshot of the log instead of the live log being mutated.
I wrote a test, heavily helped by claude, to ensure that we don't send to the pickler the live containers that the listener threads mutate. This way it should prevent us to do the same error for future callbacks. (note that progressbar is not impacted since the queue lives at the module level and is thus not sent to the pickler)