Fix serialization of CalledProcessError

This commit is contained in:
Anders Ingemann 2015-01-25 17:31:46 +01:00
parent 17a4511ee1
commit e9a3845281
2 changed files with 32 additions and 2 deletions

View file

@ -3,9 +3,14 @@ import os
def log_check_call(command, stdin=None, env=None, shell=False, cwd=None):
status, stdout, stderr = log_call(command, stdin, env, shell, cwd)
from subprocess import CalledProcessError
if status != 0:
from subprocess import CalledProcessError
raise CalledProcessError(status, ' '.join(command), '\n'.join(stderr))
e = CalledProcessError(status, ' '.join(command), '\n'.join(stderr))
# Fix Pyro4's fixIronPythonExceptionForPickle() by setting the args property,
# even though we use our own serialization (at least I think that's the problem).
# See bootstrapvz.remote.serialize_called_process_error for more info.
setattr(e, 'args', (status, ' '.join(command), '\n'.join(stderr)))
raise e
return stdout

View file

@ -33,6 +33,7 @@ supported_exceptions = ['bootstrapvz.common.exceptions.ManifestError',
'bootstrapvz.base.pkg.exceptions.SourceError',
'bootstrapvz.common.exceptions.UnitError',
'bootstrapvz.common.fsm_proxy.FSMProxyError',
'subprocess.CalledProcessError',
]
@ -41,6 +42,8 @@ def register_deserialization_handlers():
SerializerBase.register_dict_to_class(supported_class, deserialize)
for supported_exc in supported_exceptions:
SerializerBase.register_dict_to_class(supported_exc, deserialize_exception)
import subprocess
SerializerBase.register_class_to_dict(subprocess.CalledProcessError, serialize_called_process_error)
def unregister_deserialization_handlers():
@ -73,6 +76,28 @@ def deserialize(fq_classname, data):
return instance
def serialize_called_process_error(obj):
# This is by far the weirdest exception serialization.
# There is a bug in both Pyro4 and the Python subprocess module.
# CalledProcessError does not populate its args property,
# although according to https://docs.python.org/2/library/exceptions.html#exceptions.BaseException.args
# it should...
# So we populate that property during serialization instead
# (the code is grabbed directly from Pyro4's class_to_dict())
# However, Pyro4 still cannot figure out to call the deserializer
# unless we also use setattr() on the exception to set the args below
# (before throwing it).
# Mind you, the error "__init__() takes at least 3 arguments (2 given)"
# is thrown *on the server* if we don't use setattr().
# It's all very confusing to me and I'm not entirely
# sure what the exact problem is. Regardless - it works, so there.
return {'__class__': obj.__class__.__module__ + '.' + obj.__class__.__name__,
'__exception__': True,
'args': (obj.returncode, obj.cmd, obj.output),
'attributes': vars(obj) # add custom exception attributes
}
def get_class_object(fq_classname):
parts = fq_classname.split('.')
module_name = '.'.join(parts[:-1])