From cc6beabc07a8aebbbe0cb54038a053880d5d6104 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Thu, 24 Oct 2024 14:08:24 -0700 Subject: [PATCH] Include original pickle error message for better debugging When the pickling throws a warning and dill is not installed, include the original pickle error. Add a test case for an object that even dill cannot pickle to ensure error path is hit as expected. --- reflex/state.py | 7 +++++-- tests/units/test_state.py | 8 ++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/reflex/state.py b/reflex/state.py index e80013418..cb7d3a3f1 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -2053,7 +2053,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow): """ try: return pickle.dumps((self._to_schema(), self)) - except (pickle.PicklingError, AttributeError): + except (pickle.PicklingError, AttributeError) as og_pickle_error: error = ( f"Failed to serialize state {self.get_full_name()} due to unpicklable object. " "This state will not be persisted. " @@ -2063,7 +2063,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow): return dill.dumps((self._to_schema(), self)) except ImportError: - error += "Consider `pip install 'dill>=0.3.8'` for more exotic serialization support. " + error += ( + f"Pickle error: {og_pickle_error}. " + "Consider `pip install 'dill>=0.3.8'` for more exotic serialization support." + ) except (pickle.PicklingError, TypeError, ValueError) as ex: error += f"Dill was also unable to pickle the state: {ex}" console.warn(error) diff --git a/tests/units/test_state.py b/tests/units/test_state.py index 23d799f6d..e223b4f19 100644 --- a/tests/units/test_state.py +++ b/tests/units/test_state.py @@ -3360,6 +3360,7 @@ def test_fallback_pickle(): class DillState(BaseState): _o: Obj | None = None _f: Callable | None = None + _g: Any = None state = DillState(_reflex_internal_init=True) # type: ignore state._o = Obj(_f=lambda: 42) @@ -3370,3 +3371,10 @@ def test_fallback_pickle(): unpickled_state = BaseState._deserialize(pk) assert unpickled_state._f() == 420 assert unpickled_state._o._f() == 42 + + # Some object, like generator, are still unpicklable with dill. + state._g = (i for i in range(10)) + pk = state._serialize() + assert len(pk) == 0 + with pytest.raises(EOFError): + BaseState._deserialize(pk)