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
This commit is contained in:
Angus Lees 2016-02-19 14:28:58 +11:00
parent fa04ee7df3
commit 9d28946395
2 changed files with 27 additions and 4 deletions

View File

@ -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):

View File

@ -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):