From b89f36440a406ddccb7e94e1d17ebca1108185dc Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Mon, 26 Oct 2020 21:01:14 +0300 Subject: [PATCH] Make Exception picklable There is a bug in pickle [1], due to which it cannot dump Exception subclasses with different super() args: Simple reproducer: In [29]: import clickhouse_driver.errors as err In [30]: pickle.loads(pickle.dumps(err.ServerException(message='foo', code=1, nested=Exception('foo')))) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) in ----> 1 pickle.loads(pickle.dumps(err.ServerException(message='foo', code=1, nested=Exception('foo')))) TypeError: __init__() missing 2 required positional arguments: 'message' and 'code' [1]: https://bugs.python.org/issue37287 --- clickhouse_driver/errors.py | 8 ++++++-- tests/test_errors.py | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 tests/test_errors.py diff --git a/clickhouse_driver/errors.py b/clickhouse_driver/errors.py index 86b59d38..e3f9c5d7 100644 --- a/clickhouse_driver/errors.py +++ b/clickhouse_driver/errors.py @@ -382,16 +382,18 @@ class Error(Exception): def __init__(self, message=None): self.message = message + super().__init__(message) def __str__(self): message = ' ' + self.message if self.message is not None else '' return 'Code: {}.{}'.format(self.code, message) class ServerException(Error): - def __init__(self, message, code, nested=None): + def __init__(self, message, code=None, nested=None): self.message = message self.code = code self.nested = nested + super().__init__(message) def __str__(self): nested = '\nNested: {}'.format(self.nested) if self.nested else '' @@ -403,6 +405,7 @@ class Error(Exception): def __init__(self, message=None): self.message = message + super(Error, self).__init__(message) def __unicode__(self): message = ' ' + self.message if self.message is not None else '' @@ -412,10 +415,11 @@ def __str__(self): return compat.text_type(self).encode('utf-8') class ServerException(Error): - def __init__(self, message, code, nested=None): + def __init__(self, message, code=None, nested=None): self.message = message self.code = code self.nested = nested + super(ServerException, self).__init__(message) def __unicode__(self): nested = '\nNested: {}'.format(self.nested) if self.nested else '' diff --git a/tests/test_errors.py b/tests/test_errors.py new file mode 100644 index 00000000..7d5f1d66 --- /dev/null +++ b/tests/test_errors.py @@ -0,0 +1,14 @@ +import pickle +import clickhouse_driver.errors as err + +def picklable(o): + picked = pickle.loads(pickle.dumps(o)) + assert repr(o) == repr(picked) + assert str(o) == str(picked) + +def test_exception_picklable(): + picklable(err.Error('foo')) + picklable(err.Error(message='foo')) + + picklable(err.ServerException('foo', 0, Exception())) + picklable(err.ServerException(message='foo', code=0, nested=Exception()))