From f400f30fc3f7d5aec09c4ebb8875c53e3344c91d Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Wed, 17 Nov 2021 15:01:16 +0000 Subject: [PATCH 1/3] exekall: Remove dead variable --- tools/exekall/exekall/_utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/exekall/exekall/_utils.py b/tools/exekall/exekall/_utils.py index dcb6e8967..c6ecbc03c 100644 --- a/tools/exekall/exekall/_utils.py +++ b/tools/exekall/exekall/_utils.py @@ -319,7 +319,6 @@ def is_serializable(obj, raise_excep=False): """ Try to Pickle the object to see if that raises any exception. """ - stream = io.StringIO() try: # This may be slow for big objects but it is the only way to be sure # it can actually be serialized -- GitLab From 47dc9f8b945755168654a75d093fd1765c7c11a2 Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Wed, 17 Nov 2021 16:51:04 +0000 Subject: [PATCH 2/3] exekall._utils: Fix ExceptionPickler memory leak FIX Workaround the memory leak described by: https://bugs.python.org/issue45830 --- tools/exekall/exekall/_utils.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/exekall/exekall/_utils.py b/tools/exekall/exekall/_utils.py index c6ecbc03c..e49879c80 100644 --- a/tools/exekall/exekall/_utils.py +++ b/tools/exekall/exekall/_utils.py @@ -253,8 +253,12 @@ class ExceptionPickler(pickle.Pickler): raise KeyError def __init__(self, *args, **kwargs): - self.dispatch_table = self._DispatchTable(self) + + # Due to this issue, it is critical that "dispatch_table" is set after + # calling super().__init__ + # https://bugs.python.org/issue45830 super().__init__(*args, **kwargs) + self.dispatch_table = self._DispatchTable(self) @staticmethod def _make_excep(excep_cls, dct): -- GitLab From f3e94d243b9759437d52bc5c364c47f974d67358 Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Wed, 17 Nov 2021 16:51:52 +0000 Subject: [PATCH 3/3] exekall._utils: Reduce peak memory consumption of is_serializable Pickle into a fake file that discards data rather than building up a bytestring in memory. --- tools/exekall/exekall/_utils.py | 40 +++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/tools/exekall/exekall/_utils.py b/tools/exekall/exekall/_utils.py index e49879c80..a805f087e 100644 --- a/tools/exekall/exekall/_utils.py +++ b/tools/exekall/exekall/_utils.py @@ -318,24 +318,30 @@ class ExceptionPickler(pickle.Pickler): pickler = cls(f, **kwargs) return pickler.dump(obj) + @classmethod + def is_serializable(cls, obj, raise_excep=False): + """ + Try to Pickle the object to see if that raises any exception. + """ + class DevNull: + def write(self, _): + pass + try: + # This may be slow for big objects but it is the only way to be + # sure it can actually be serialized + cls.dump_file(DevNull(), obj) + except (TypeError, pickle.PickleError, AttributeError) as e: + debug('Cannot serialize instance of {}: {}'.format( + type(obj).__qualname__, str(e) + )) + if raise_excep: + raise NotSerializableError(obj) from e + return False + else: + return True -def is_serializable(obj, raise_excep=False): - """ - Try to Pickle the object to see if that raises any exception. - """ - try: - # This may be slow for big objects but it is the only way to be sure - # it can actually be serialized - ExceptionPickler.dump_bytestring(obj) - except (TypeError, pickle.PickleError, AttributeError) as e: - debug('Cannot serialize instance of {}: {}'.format( - type(obj).__qualname__, str(e) - )) - if raise_excep: - raise NotSerializableError(obj) from e - return False - else: - return True + +is_serializable = ExceptionPickler.is_serializable def once(callable_): -- GitLab