From 9d289463957b74bed29e30f93326ee1e664d2873 Mon Sep 17 00:00:00 2001 From: Angus Lees Date: Fri, 19 Feb 2016 14:28:58 +1100 Subject: [PATCH] Make ProcessExecutionError picklable Serialising/deserialising exceptions in python follows the protocol defined by pickle. It relies on the base Exception.__init__ setting self.args, and later resurrects exceptions with class(*args). This change rewrites processutils.ProcessExecutionError so it survives a pickle.dumps/loads round-trip. Change-Id: I9b8d104f60df868be7b808c72c932d08f1752777 --- oslo_concurrency/processutils.py | 15 +++++++++++---- oslo_concurrency/tests/unit/test_processutils.py | 16 ++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/oslo_concurrency/processutils.py b/oslo_concurrency/processutils.py index 436922a..b74e5f2 100644 --- a/oslo_concurrency/processutils.py +++ b/oslo_concurrency/processutils.py @@ -63,26 +63,33 @@ class UnknownArgumentError(Exception): class ProcessExecutionError(Exception): def __init__(self, stdout=None, stderr=None, exit_code=None, cmd=None, description=None): + super(ProcessExecutionError, self).__init__( + stdout, stderr, exit_code, cmd, description) self.exit_code = exit_code self.stderr = stderr self.stdout = stdout self.cmd = cmd self.description = description + def __str__(self): + description = self.description if description is None: description = _("Unexpected error while running command.") + + exit_code = self.exit_code if exit_code is None: exit_code = '-' + message = _('%(description)s\n' 'Command: %(cmd)s\n' 'Exit code: %(exit_code)s\n' 'Stdout: %(stdout)r\n' 'Stderr: %(stderr)r') % {'description': description, - 'cmd': cmd, + 'cmd': self.cmd, 'exit_code': exit_code, - 'stdout': stdout, - 'stderr': stderr} - super(ProcessExecutionError, self).__init__(message) + 'stdout': self.stdout, + 'stderr': self.stderr} + return message class NoRootWrapSpecified(Exception): diff --git a/oslo_concurrency/tests/unit/test_processutils.py b/oslo_concurrency/tests/unit/test_processutils.py index b868cb5..0fd8045 100644 --- a/oslo_concurrency/tests/unit/test_processutils.py +++ b/oslo_concurrency/tests/unit/test_processutils.py @@ -19,6 +19,7 @@ import errno import logging import multiprocessing import os +import pickle import resource import stat import subprocess @@ -467,6 +468,21 @@ grep foo def test_binary_undecodable_bytes_error(self): self.check_undecodable_bytes_error(True) + def test_picklable(self): + exc = processutils.ProcessExecutionError( + stdout='my stdout', stderr='my stderr', + exit_code=42, cmd='my cmd', + description='my description') + exc_message = str(exc) + + exc = pickle.loads(pickle.dumps(exc)) + self.assertEqual('my stdout', exc.stdout) + self.assertEqual('my stderr', exc.stderr) + self.assertEqual(42, exc.exit_code) + self.assertEqual('my cmd', exc.cmd) + self.assertEqual('my description', exc.description) + self.assertEqual(exc_message, str(exc)) + class ProcessExecutionErrorLoggingTest(test_base.BaseTestCase): def setUp(self):