diferentes tipos de excepciones en python
Manejo de excepciones polimórficas: ¿cómo detectar la excepción de subclase? (2)
Mis habilidades con Python están oxidadas y no lo he probado, por lo que es posible que necesiten más mejoras, pero intente agregar el método de traducción de excepción para manejar el tipo de excepción de la clase base:
static PyObject *clusterExecutionAsClusterExceptionType = NULL;
static void translateClusterExecutionAsClusterException(ClusterException const &exception) {
ClusterExecutionException* upcasted = dynamic_cast<ClusterExecutionException*>(&exception);
if (upcasted)
{
assert(clusterExecutionAsClusterExceptionType != NULL);
boost::python::object pythonExceptionInstance(*upcasted);
PyErr_SetObject(clusterExecutionAsClusterExceptionType, pythonExceptionInstance.ptr());
}
}
register_exception_translator<ClusterException>(&translateClusterExecutionAsClusterException);
Tengo la siguiente jerarquía simple de dos excepciones de C ++:
class LIB_EXP ClusterException : public std::exception {
public:
ClusterException() { }
ClusterException(const std::string& what) { init(what); }
virtual const char* what() const throw() { return what_.c_str(); }
virtual ~ClusterException() throw() {}
virtual ClusterException* clone() { return new ClusterException(*this); }
protected:
void init(const std::string& what) { what_ = what; }
private:
std::string what_;
};
class LIB_EXP ClusterExecutionException : public ClusterException {
public:
ClusterExecutionException(const std::string& jsonResponse);
std::string getErrorType() const throw() { return errorType_; }
std::string getClusterResponse() const throw() { return clusterResponse_; }
virtual ~ClusterExecutionException() throw() {}
virtual ClusterExecutionException* clone() { return new ClusterExecutionException(*this); }
private:
std::string errorType_;
std::string clusterResponse_;
};
Luego los exporto a Python con Boost-Python de la siguiente manera. Tenga en cuenta mi uso de las bases
para asegurarse de que la relación de herencia se conserve en la traducción:
class_<ClusterException> clusterException("ClusterException", no_init);
clusterException.add_property("message", &ClusterException::what);
clusterExceptionType = clusterException.ptr();
register_exception_translator<ClusterException>(&translateClusterException);
class_<ClusterExecutionException, bases<ClusterException> > clusterExecutionException("ClusterExecutionException", no_init);
clusterExecutionException.add_property("message", &ClusterExecutionException::what)
.add_property("errorType", &ClusterExecutionException::getErrorType)
.add_property("clusterResponse", &ClusterExecutionException::getClusterResponse);
clusterExecutionExceptionType = clusterExecutionException.ptr();
register_exception_translator<ClusterExecutionException>(&translateClusterExecutionException);
Entonces el método de traducción de excepción:
static PyObject *clusterExceptionType = NULL;
static void translateClusterException(ClusterException const &exception) {
assert(clusterExceptionType != NULL);
boost::python::object pythonExceptionInstance(exception);
PyErr_SetObject(clusterExceptionType, pythonExceptionInstance.ptr());
}
static PyObject *clusterExecutionExceptionType = NULL;
static void translateClusterExecutionException(ClusterExecutionException const &exception) {
assert(clusterExecutionExceptionType != NULL);
boost::python::object pythonExceptionInstance(exception);
PyErr_SetObject(clusterExecutionExceptionType, pythonExceptionInstance.ptr());
}
Creé la siguiente función C ++ de prueba que arroja las excepciones:
static void boomTest(int exCase) {
switch (exCase) {
case 0: throw ClusterException("Connection to server failed");
break;
case 1: throw ClusterExecutionException("Error X while executing in the cluster");
break;
default: throw std::runtime_error("Unknown exception type");
}
}
Finalmente, el código de prueba de Python que llama a mi C ++ boomTest
:
import cluster
reload(cluster)
from cluster import ClusterException, ClusterExecutionException
def test_exception(exCase):
try:
cluster.boomTest(exCase)
except ClusterException as ex:
print ''Success! ClusterException gracefully handled:'' /
''/n message="%s"'' % ex.message
except ClusterExecutionException as ex:
print ''Success! ClusterExecutionException gracefully handled:'' /
''/n message="%s"'' /
''/n errorType="%s"'' /
''/n clusterResponse="%s"'' % (ex.message, ex.errorType, ex.clusterResponse)
except:
print ''Caught unknown exception: %s "%s"'' % (sys.exc_info()[0], sys.exc_info()[1])
def main():
print ''/n************************ throwing ClusterException ***********************************************************************''
test_exception(0)
print ''/n************************ throwing ClusterExecutionException **************************************************************''
test_exception(1)
print ''/n************************ throwing std::runtime_error *********************************************************************''
test_exception(2)
if __name__ == "__main__":
main()
Hasta aquí todo funciona. Sin embargo, si ClusterExecutionException
controlador de captura de ClusterExecutionException
de Python, esta excepción se detectará y se ClusterExecutionException
a una excepción desconocida en lugar de ser capturada como su ClusterException
base.
He intentado en Boost-Python al registrar la traducción de excepción de ClusterExecutionException
para registrarla como su ClusterException
base y luego queda atrapada "polimórficamente", pero luego no se ClusterExecutionException
como ClusterExecutionException
. ¿Cómo se puede hacer para que ClusterExecutionException
quede atrapada como ClusterException
y ClusterExecutionException
? Por supuesto, he intentado registrar esta excepción ClusterExecutionException
como ClusterException
y ClusterExecutionException
pero sigue una última estrategia de ganancias, y solo una funciona, no ambas.
¿Hay alguna otra forma de resolver esto?
ACTUALIZACIÓN 1: El verdadero grial de este problema es descubrir en el lado de C ++ el tipo de la declaración except
Python, por ejemplo, except ClusterException as ex:
que es desconocida una vez dentro del lado de C ++. La excepción se traduce por Boost.Python llamará a la función de traducción que corresponde al tipo dinámico de la excepción y el tipo estático de captura de Python no se conoce.
ACTUALIZACIÓN 2: Como se sugiere cambiar el código de Python a lo siguiente, es decir, agregar print(type(ex).__bases__)
da:
def test_exception(exCase):
try:
cluster.boomTest(exCase)
except ClusterException as ex:
print(type(ex).__bases__)
print ''Success! ClusterException gracefully handled:'' /
''/n message="%s"'' % ex.message
except ClusterExecutionException as ex:
print(type(ex).__bases__)
print ''Success! ClusterExecutionException gracefully handled:'' /
''/n message="%s"'' /
''/n errorType="%s"'' /
''/n clusterResponse="%s"'' % (ex.message, ex.errorType, ex.clusterResponse)
except:
print ''Caught unknown exception: %s "%s"'' % (sys.exc_info()[0], sys.exc_info()[1])
y el resultado:
************************ throwing ClusterException ***********************************************************************
(<type ''Boost.Python.instance''>,)
Success! ClusterException gracefully handled:
message="Connection to server failed"
************************ throwing ClusterExecutionException **************************************************************
(<class ''cluster.ClusterException''>,)
Success! ClusterExecutionException gracefully handled:
message="Error X while executing in the cluster"
errorType="LifeCycleException"
clusterResponse="{ "resultStatus": "Error", "errorType": "LifeCycleException", "errorMessage": "Error X while executing in the cluster" }"
Lo que significa que la relación de herencia se "ve" desde Python. Pero el manejo polimórfico aún no funciona.
ACTUALIZACIÓN 3 Este es el resultado de ejecutar VS dumpbin.exe
:
El comando que utilicé es:
dumpbin.exe /EXPORTS /SYMBOLS C:/ClusterDK/x64/Debug/ClusterDK.dll > c:/temp/dumpbin.out
y las partes relevantes de la salida:
Microsoft (R) COFF/PE Dumper Version 11.00.50727.1
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file C:/ClusterDK/x64/Debug/ClusterDK.dll
File Type: DLL
Section contains the following exports for ClusterDK.dll
00000000 characteristics
5A1689DA time date stamp Thu Nov 23 09:42:02 2017
0.00 version
1 ordinal base
78 number of functions
78 number of names
ordinal hint RVA name
8 7 00004485 ??0ClusterException@cluster@@QEAA@AEBV01@@Z = @ILT+13440(??0ClusterException@cluster@@QEAA@AEBV01@@Z)
9 8 00001659 ??0ClusterException@cluster@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z = @ILT+1620(??0ClusterException@cluster@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z)
10 9 00001F1E ??0ClusterException@cluster@@QEAA@XZ = @ILT+3865(??0ClusterException@cluster@@QEAA@XZ)
11 A 00004D4F ??0ClusterExecutionException@cluster@@QEAA@AEBV01@@Z = @ILT+15690(??0ClusterExecutionException@cluster@@QEAA@AEBV01@@Z)
12 B 000010AA ??0ClusterExecutionException@cluster@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z = @ILT+165(??0ClusterExecutionException@cluster@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z)
27 1A 000035D0 ??1ClusterException@cluster@@UEAA@XZ = @ILT+9675(??1ClusterException@cluster@@UEAA@XZ)
28 1B 00003C7E ??1ClusterExecutionException@cluster@@UEAA@XZ = @ILT+11385(??1ClusterExecutionException@cluster@@UEAA@XZ)
37 24 00002BD5 ??4ClusterException@cluster@@QEAAAEAV01@AEBV01@@Z = @ILT+7120(??4ClusterException@cluster@@QEAAAEAV01@AEBV01@@Z)
38 25 000034D1 ??4ClusterExecutionException@cluster@@QEAAAEAV01@AEBV01@@Z = @ILT+9420(??4ClusterExecutionException@cluster@@QEAAAEAV01@AEBV01@@Z)
46 2D 000D2220 ??_7ClusterException@cluster@@6B@ = ??_7ClusterException@cluster@@6B@ (const cluster::ClusterException::`vftable'')
47 2E 000D2248 ??_7ClusterExecutionException@cluster@@6B@ = ??_7ClusterExecutionException@cluster@@6B@ (const cluster::ClusterExecutionException::`vftable'')
52 33 00004BB5 ?clone@ClusterException@cluster@@UEAAPEAV12@XZ = @ILT+15280(?clone@ClusterException@cluster@@UEAAPEAV12@XZ)
53 34 00004D31 ?clone@ClusterExecutionException@cluster@@UEAAPEAV12@XZ = @ILT+15660(?clone@ClusterExecutionException@cluster@@UEAAPEAV12@XZ)
61 3C 00001D43 ?getErrorType@ClusterExecutionException@cluster@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ = @ILT+3390(?getErrorType@ClusterExecutionException@cluster@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ)
69 44 0000480E ?init@ClusterException@cluster@@IEAAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z = @ILT+14345(?init@ClusterException@cluster@@IEAAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z)
78 4D 000032FB ?what@ClusterException@cluster@@UEBAPEBDXZ = @ILT+8950(?what@ClusterException@cluster@@UEBAPEBDXZ)
Summary
4000 .data
5000 .idata
12000 .pdata
54000 .rdata
2000 .reloc
1000 .rsrc
C9000 .text
1000 .tls
No sé sobre su código C ++. Pero hay un pequeño problema con tu código python. ClusterExecutionException
antes de ClusterException
. Siempre debe colocar el manejador de excepciones hijo antes de la excepción base.
En test_exception
si ClusterExecutionException
provocó, será capturado por ClusterException
antes de que pueda llegar a ClusterExecutionException
.
el código debe ser el siguiente código
def test_exception(exCase):
try:
cluster.boomTest(exCase)
except ClusterExecutionException as ex:
print ''Success! ClusterExecutionException gracefully handled:'' /
''/n message="%s"'' /
''/n errorType="%s"'' /
''/n clusterResponse="%s"'' % (ex.message, ex.errorType, ex.clusterResponse)
except ClusterException as ex:
print ''Success! ClusterException gracefully handled:'' /
''/n message="%s"'' % ex.message
except:
print ''Caught unknown exception: %s "%s"'' % (sys.exc_info()[0], sys.exc_info()[1])
ahora I have tried in Boost-Python while registering the exception translation of ClusterExecutionException to register it as its base ClusterException
, lo que ha mencionado en cuestión.