Base: improve exception

For better FC and Python exception mapping.
This commit is contained in:
Zheng, Lei
2019-06-21 10:46:43 +08:00
committed by wmayer
parent ea8965cbc2
commit 8f16147a40
9 changed files with 311 additions and 110 deletions

View File

@@ -194,6 +194,7 @@ Base::ConsoleObserverFile *Application::_pConsoleObserverFile =0;
AppExport std::map<std::string,std::string> Application::mConfig;
BaseExport extern PyObject* Base::BaseExceptionFreeCADError;
BaseExport extern PyObject* Base::BaseExceptionFreeCADAbort;
//**************************************************************************
// Construction and destruction
@@ -314,6 +315,10 @@ Application::Application(std::map<std::string,std::string> &mConfig)
Py_INCREF(Base::BaseExceptionFreeCADError);
PyModule_AddObject(pBaseModule, "FreeCADError", Base::BaseExceptionFreeCADError);
Base::BaseExceptionFreeCADAbort = PyErr_NewException("Base.FreeCADAbort", PyExc_BaseException, NULL);
Py_INCREF(Base::BaseExceptionFreeCADAbort);
PyModule_AddObject(pBaseModule, "FreeCADAbort", Base::BaseExceptionFreeCADAbort);
// Python types
Base::Interpreter().addType(&Base::VectorPy ::Type,pBaseModule,"Vector");
Base::Interpreter().addType(&Base::MatrixPy ::Type,pBaseModule,"Matrix");
@@ -1319,6 +1324,7 @@ void Application::initTypes(void)
Base::Type ::init();
Base::BaseClass ::init();
Base::Exception ::init();
Base::AbortException ::init();
Base::Persistence ::init();
// Complex data classes
@@ -1461,6 +1467,8 @@ void Application::initTypes(void)
new ExceptionProducer<Base::TypeError>;
new ExceptionProducer<Base::ValueError>;
new ExceptionProducer<Base::IndexError>;
new ExceptionProducer<Base::NameError>;
new ExceptionProducer<Base::ImportError>;
new ExceptionProducer<Base::AttributeError>;
new ExceptionProducer<Base::RuntimeError>;
new ExceptionProducer<Base::BadGraphError>;

View File

@@ -31,6 +31,8 @@
#include "Console.h"
#include <CXX/Objects.hxx>
FC_LOG_LEVEL_INIT("Exception", true, true);
using namespace Base;
@@ -88,32 +90,17 @@ const char* Exception::what(void) const throw()
void Exception::ReportException (void) const
{
if (!_isReported) {
std::string str = "";
if (!_sErrMsg.empty())
str+= (_sErrMsg + " ");
if (!_function.empty()) {
str+="In ";
str+=_function;
str+= " ";
}
std::string _linestr = std::to_string(_line);
if (!_file.empty() && !_linestr.empty()) {
// strip absolute path
std::size_t pos = _file.find("src");
if (pos!=std::string::npos) {
str+="in ";
str+= _file.substr(pos);
str+= ":";
str+=_linestr;
}
}
Console().Error("Exception (%s): %s \n",Console().Time(),str.c_str());
const char *msg;
if(_sErrMsg.empty())
msg = typeid(*this).name();
else
msg = _sErrMsg.c_str();
#ifdef FC_DEBUG
if(_function.size()) {
_FC_ERR(_file.c_str(),_line, _function << " -- " << msg);
}else
#endif
_FC_ERR(_file.c_str(),_line,msg);
_isReported = true;
}
}
@@ -164,6 +151,7 @@ void Exception::setPyObject( PyObject * pydict)
// ---------------------------------------------------------
TYPESYSTEM_SOURCE(Base::AbortException,Base::Exception);
AbortException::AbortException(const char * sMessage)
: Exception( sMessage )
@@ -315,32 +303,17 @@ const char* FileException::what() const throw()
void FileException::ReportException (void) const
{
if (!_isReported) {
std::string str = "";
if (!_sErrMsgAndFileName.empty())
str+= (_sErrMsgAndFileName + " ");
if (!_function.empty()) {
str+="In ";
str+=_function;
str+= " ";
}
std::string _linestr = std::to_string(_line);
if (!_file.empty() && !_linestr.empty()) {
// strip absolute path
std::size_t pos = _file.find("src");
if (pos!=std::string::npos) {
str+="in ";
str+= _file.substr(pos);
str+= ":";
str+=_linestr;
}
}
Console().Error("Exception (%s): %s \n",Console().Time(),str.c_str());
const char *msg;
if(_sErrMsgAndFileName.empty())
msg = typeid(*this).name();
else
msg = _sErrMsgAndFileName.c_str();
#ifdef FC_DEBUG
if(_function.size()) {
_FC_ERR(_file.c_str(),_line, _function << " -- " << msg);
}else
#endif
_FC_ERR(_file.c_str(),_line,msg);
_isReported = true;
}
}
@@ -363,6 +336,10 @@ void FileException::setPyObject( PyObject * pydict)
}
}
PyObject * FileException::getPyExceptionType() const {
return PyExc_IOError;
}
// ---------------------------------------------------------
@@ -544,6 +521,10 @@ TypeError::TypeError(const TypeError &inst)
{
}
PyObject *TypeError::getPyExceptionType() const {
return PyExc_TypeError;
}
// ---------------------------------------------------------
ValueError::ValueError()
@@ -566,6 +547,10 @@ ValueError::ValueError(const ValueError &inst)
{
}
PyObject *ValueError::getPyExceptionType() const {
return PyExc_ValueError;
}
// ---------------------------------------------------------
IndexError::IndexError()
@@ -588,6 +573,62 @@ IndexError::IndexError(const IndexError &inst)
{
}
PyObject *IndexError::getPyExceptionType() const {
return PyExc_IndexError;
}
// ---------------------------------------------------------
NameError::NameError()
: Exception()
{
}
NameError::NameError(const char * sMessage)
: Exception(sMessage)
{
}
NameError::NameError(const std::string& sMessage)
: Exception(sMessage)
{
}
NameError::NameError(const NameError &inst)
: Exception(inst)
{
}
PyObject *NameError::getPyExceptionType() const {
return PyExc_NameError;
}
// ---------------------------------------------------------
ImportError::ImportError()
: Exception()
{
}
ImportError::ImportError(const char * sMessage)
: Exception(sMessage)
{
}
ImportError::ImportError(const std::string& sMessage)
: Exception(sMessage)
{
}
ImportError::ImportError(const ImportError &inst)
: Exception(inst)
{
}
PyObject *ImportError::getPyExceptionType() const {
return PyExc_ImportError;
}
// ---------------------------------------------------------
AttributeError::AttributeError()
@@ -610,6 +651,10 @@ AttributeError::AttributeError(const AttributeError &inst)
{
}
PyObject *AttributeError::getPyExceptionType() const {
return PyExc_AttributeError;
}
// ---------------------------------------------------------
RuntimeError::RuntimeError()
@@ -632,6 +677,10 @@ RuntimeError::RuntimeError(const RuntimeError &inst)
{
}
PyObject *RuntimeError::getPyExceptionType() const {
return PyExc_RuntimeError;
}
// ---------------------------------------------------------
BadGraphError::BadGraphError()
@@ -676,6 +725,10 @@ NotImplementedError::NotImplementedError(const NotImplementedError &inst)
{
}
PyObject *NotImplementedError::getPyExceptionType() const {
return PyExc_NotImplementedError;
}
// ---------------------------------------------------------
DivisionByZeroError::DivisionByZeroError()
@@ -698,6 +751,10 @@ DivisionByZeroError::DivisionByZeroError(const DivisionByZeroError &inst)
{
}
PyObject *DivisionByZeroError::getPyExceptionType() const {
return PyExc_ZeroDivisionError;
}
// ---------------------------------------------------------
ReferencesError::ReferencesError()
@@ -720,6 +777,10 @@ ReferencesError::ReferencesError(const ReferencesError &inst)
{
}
PyObject *ReferencesError::getPyExceptionType() const {
return PyExc_ReferenceError;
}
// ---------------------------------------------------------
ExpressionError::ExpressionError()
@@ -786,6 +847,10 @@ UnicodeError::UnicodeError(const UnicodeError &inst)
{
}
PyObject *UnicodeError::getPyExceptionType() const {
return PyExc_UnicodeError;
}
// ---------------------------------------------------------
OverflowError::OverflowError()
@@ -808,6 +873,10 @@ OverflowError::OverflowError(const OverflowError &inst)
{
}
PyObject *OverflowError::getPyExceptionType() const {
return PyExc_OverflowError;
}
// ---------------------------------------------------------
UnderflowError::UnderflowError()
@@ -830,6 +899,10 @@ UnderflowError::UnderflowError(const UnderflowError &inst)
{
}
PyObject *UnderflowError::getPyExceptionType() const {
return PyExc_ArithmeticError;
}
// ---------------------------------------------------------
UnitsMismatchError::UnitsMismatchError()
@@ -852,6 +925,10 @@ UnitsMismatchError::UnitsMismatchError(const UnitsMismatchError &inst)
{
}
PyObject *UnitsMismatchError::getPyExceptionType() const {
return PyExc_ArithmeticError;
}
// ---------------------------------------------------------
CADKernelError::CADKernelError()

View File

@@ -79,6 +79,12 @@
#endif
#define FC_THROWM(_exception,_msg) do {\
std::stringstream ss;\
ss << _msg;\
THROWM(_exception,ss.str().c_str());\
}while(0)
namespace Base
{
@@ -105,17 +111,24 @@ public:
inline int getLine() const;
inline std::string getFunction() const;
inline bool getTranslatable() const;
inline bool getReported() const { return _isReported; }
/// setter methods for including debug information
/// intended to use via macro for autofilling of debugging information
inline void setDebugInformation(const std::string & file, const int line, const std::string & function);
inline void setTranslatable(bool translatable);
inline void setReported(bool reported) { _isReported = reported; }
/// returns a Python dictionary containing the exception data
virtual PyObject * getPyObject(void);
/// returns sets the exception data from a Python dictionary
virtual void setPyObject( PyObject * pydict);
/// returns the corresponding python exception type
virtual PyObject * getPyExceptionType() const {return 0;}
protected:
/* sMessage may be:
* - a UI compliant string susceptible to being translated and shown to the user in the UI
@@ -143,6 +156,7 @@ protected:
*/
class BaseExport AbortException : public Exception
{
TYPESYSTEM_HEADER();
public:
/// Construction
AbortException(const char * sMessage);
@@ -248,6 +262,8 @@ public:
virtual PyObject * getPyObject(void);
/// returns sets the exception data from a Python dictionary
virtual void setPyObject( PyObject * pydict);
virtual PyObject * getPyExceptionType() const override;
protected:
FileInfo file;
// necessary for what() legacy behaviour as it returns a buffer that
@@ -398,6 +414,7 @@ public:
TypeError(const TypeError &inst);
/// Destruction
virtual ~TypeError() throw() {}
virtual PyObject * getPyExceptionType() const override;
};
/**
@@ -415,6 +432,7 @@ public:
ValueError(const ValueError &inst);
/// Destruction
virtual ~ValueError() throw() {}
virtual PyObject * getPyExceptionType() const override;
};
/**
@@ -432,6 +450,35 @@ public:
IndexError(const IndexError &inst);
/// Destruction
virtual ~IndexError() throw() {}
virtual PyObject * getPyExceptionType() const override;
};
class BaseExport NameError : public Exception
{
public:
/// Construction
NameError();
NameError(const char * sMessage);
NameError(const std::string& sMessage);
/// Construction
NameError(const NameError &inst);
/// Destruction
virtual ~NameError() throw() {}
virtual PyObject * getPyExceptionType() const override;
};
class BaseExport ImportError : public Exception
{
public:
/// Construction
ImportError();
ImportError(const char * sMessage);
ImportError(const std::string& sMessage);
/// Construction
ImportError(const ImportError &inst);
/// Destruction
virtual ~ImportError() throw() {}
virtual PyObject * getPyExceptionType() const override;
};
/**
@@ -449,6 +496,7 @@ public:
AttributeError(const AttributeError &inst);
/// Destruction
virtual ~AttributeError() throw() {}
virtual PyObject * getPyExceptionType() const override;
};
/**
@@ -466,6 +514,7 @@ public:
RuntimeError(const RuntimeError &inst);
/// Destruction
virtual ~RuntimeError() throw() {}
virtual PyObject * getPyExceptionType() const override;
};
/**
@@ -500,10 +549,11 @@ public:
NotImplementedError(const NotImplementedError &inst);
/// Destruction
virtual ~NotImplementedError() throw() {}
virtual PyObject * getPyExceptionType() const override;
};
/**
* The DivisionByZeroError can be used to indicate a division by zero.
* The ZeroDivisionError can be used to indicate a division by zero.
* @author Werner Mayer
*/
class BaseExport DivisionByZeroError : public Exception
@@ -517,10 +567,11 @@ public:
DivisionByZeroError(const DivisionByZeroError &inst);
/// Destruction
virtual ~DivisionByZeroError() throw() {}
virtual PyObject * getPyExceptionType() const override;
};
/**
* The ReferencesError can be used to indicate a reference counter has the wrong value.
* The ReferenceError can be used to indicate a reference counter has the wrong value.
* @author Werner Mayer
*/
class BaseExport ReferencesError : public Exception
@@ -534,6 +585,7 @@ public:
ReferencesError(const ReferencesError &inst);
/// Destruction
virtual ~ReferencesError() throw() {}
virtual PyObject * getPyExceptionType() const override;
};
/**
@@ -586,6 +638,7 @@ public:
UnicodeError(const UnicodeError &inst);
/// Destruction
virtual ~UnicodeError() throw() {}
virtual PyObject * getPyExceptionType() const override;
};
/**
@@ -603,6 +656,7 @@ public:
OverflowError(const OverflowError &inst);
/// Destruction
virtual ~OverflowError() throw() {}
virtual PyObject * getPyExceptionType() const override;
};
/**
@@ -620,6 +674,7 @@ public:
UnderflowError(const UnderflowError &inst);
/// Destruction
virtual ~UnderflowError() throw() {}
virtual PyObject * getPyExceptionType() const override;
};
/**
@@ -637,6 +692,7 @@ public:
UnitsMismatchError(const UnitsMismatchError &inst);
/// Destruction
virtual ~UnitsMismatchError() throw() {}
virtual PyObject * getPyExceptionType() const override;
};
/* The CADKernelError can be used to indicate an exception originating in the CAD Kernel

View File

@@ -51,10 +51,21 @@ using namespace Base;
#error "Use Python2.5.x or higher"
#endif
PyException::PyException(const Py::Object &obj) {
_sErrMsg = obj.as_string();
// WARNING: we are assumming that python type object will never be
// destroied, so we don't keep reference here to save book-keeping in
// our copy constructor and desctructor
_exceptionType = (PyObject*)obj.ptr()->ob_type;
_errorType = obj.ptr()->ob_type->tp_name;
}
PyException::PyException(void)
{
PP_Fetch_Error_Text(); /* fetch (and clear) exception */
setPyObject(PP_PyDict_Object);
std::string prefix = PP_last_error_type; /* exception name text */
// prefix += ": ";
std::string error = PP_last_error_info; /* exception data text */
@@ -70,6 +81,16 @@ PyException::PyException(void)
_sErrMsg = error;
_errorType = prefix;
_exceptionType = PP_last_exception_type;
if(PP_last_exception_type) {
// WARNING: we are assumming that python type object will never be
// destroied, so we don't keep reference here to save book-keeping in
// our copy constructor and desctructor
Py_DECREF(PP_last_exception_type);
PP_last_exception_type = 0;
}
_stackTrace = PP_last_error_trace; /* exception traceback text */
@@ -86,31 +107,44 @@ PyException::~PyException() throw()
void PyException::ThrowException(void)
{
PyException myexcp = PyException();
PyException myexcp;
myexcp.ReportException();
myexcp.raiseException();
}
void PyException::raiseException() {
PyGILStateLocker locker;
if (PP_PyDict_Object!=NULL) {
// delete the Python dict upon destruction of edict
Py::Dict edict(PP_PyDict_Object, true);
PP_PyDict_Object = 0;
if (!edict.hasKey("sclassname"))
throw myexcp;
std::string exceptionname = static_cast<std::string>(Py::String(edict.getItem("sclassname")));
if (!Base::ExceptionFactory::Instance().CanProduce(exceptionname.c_str()))
throw myexcp;
std::string exceptionname;
if (_exceptionType == Base::BaseExceptionFreeCADAbort)
edict.setItem("sclassname",
Py::String(typeid(Base::AbortException).name()));
if(_isReported)
edict.setItem("breported", Py::True());
Base::ExceptionFactory::Instance().raiseException(edict.ptr());
}
else
throw myexcp;
if (_exceptionType == Base::BaseExceptionFreeCADAbort) {
Base::AbortException e(_sErrMsg.c_str());
e.setReported(_isReported);
throw e;
}
throw *this;
}
void PyException::ReportException (void) const
{
Base::Console().Error("%s%s: %s\n",
_stackTrace.c_str(), _errorType.c_str(), what());
if (!_isReported) {
_isReported = true;
Base::Console().Error("%s%s: %s\n",
_stackTrace.c_str(), _errorType.c_str(), what());
}
}
// ---------------------------------------------------------

View File

@@ -65,7 +65,10 @@ class BaseExport PyException : public Exception
public:
/// constructor does the whole job
PyException(void);
PyException(const Py::Object &obj);
~PyException() throw();
void raiseException();
/// this method determines if the original exception
/// can be reconstructed or not, if yes throws the reconstructed version
@@ -75,11 +78,13 @@ public:
/// this function returns the stack trace
const std::string &getStackTrace(void) const {return _stackTrace;}
const std::string &getErrorType(void) const {return _errorType;}
virtual PyObject *getPyExceptionType(void) const override {return _exceptionType;}
void ReportException (void) const;
protected:
std::string _stackTrace;
std::string _errorType;
PyObject *_exceptionType;
};
/**

View File

@@ -37,6 +37,7 @@
using namespace Base;
PyObject* Base::BaseExceptionFreeCADError = 0;
PyObject* Base::BaseExceptionFreeCADAbort = 0;
// Constructor
PyObjectBase::PyObjectBase(void* p,PyTypeObject *T)

View File

@@ -147,7 +147,11 @@ inline void Assert(int expr, char *msg) // C++ assert
/// return with no return value if nothing happens
#define Py_Return return Py_INCREF(Py_None), Py_None
/// returns an error
#define Py_Error(E, M) {PyErr_SetString(E, M); return NULL;}
#define Py_Error(E, M) _Py_Error(return(NULL),E,M)
#define _Py_Error(R, E, M) {PyErr_SetString(E, M); R;}
/// returns an error
#define Py_ErrorObj(E, O) _Py_ErrorObj(return(NULL),E,O)
#define _Py_ErrorObj(R, E, O) {PyErr_SetObject(E, O); R;}
/// checks on a condition and returns an error on failure
#define Py_Try(F) {if (!(F)) return NULL;}
/// assert which returns with an error on failure
@@ -411,6 +415,7 @@ BaseExport extern PyObject* BaseExceptionFreeCADError;
#define PY_FCERROR (Base::BaseExceptionFreeCADError ? \
BaseExceptionFreeCADError : PyExc_RuntimeError)
BaseExport extern PyObject* BaseExceptionFreeCADAbort;
/** Exception handling for python callback functions
* Is a convenience macro to manage the exception handling of python callback
@@ -451,16 +456,25 @@ BaseExport extern PyObject* BaseExceptionFreeCADError;
*/
#define PY_TRY try
#ifndef DONT_CATCH_CXX_EXCEPTIONS
/// see docu of PY_TRY
# define PY_CATCH catch(Base::Exception &e) \
#define __PY_CATCH(R) \
catch(Base::AbortException &e) \
{ \
std::string str; \
str += "FreeCAD exception thrown ("; \
str += e.what(); \
str += ")"; \
e.ReportException(); \
Py_Error(Base::BaseExceptionFreeCADError,str.c_str()); \
_Py_ErrorObj(R,Base::BaseExceptionFreeCADAbort,e.getPyObject());\
} \
catch(Base::Exception &e) \
{ \
e.ReportException(); \
auto pye = e.getPyExceptionType(); \
if(!pye) { \
pye = Base::BaseExceptionFreeCADError; \
std::string str; \
str += "FreeCAD exception thrown ("; \
str += e.what(); \
str += ")"; \
e.setMessage(str); \
} \
_Py_ErrorObj(R,pye,e.getPyObject()); \
} \
catch(std::exception &e) \
{ \
@@ -469,52 +483,33 @@ BaseExport extern PyObject* BaseExceptionFreeCADError;
str += e.what(); \
str += ")"; \
Base::Console().Error(str.c_str()); \
Py_Error(Base::BaseExceptionFreeCADError,str.c_str()); \
_Py_Error(R,Base::BaseExceptionFreeCADError,str.c_str()); \
} \
catch(const Py::Exception&) \
{ \
return NULL; \
R; \
} \
catch(const char *e) \
{ \
Py_Error(Base::BaseExceptionFreeCADError,e); \
_Py_Error(R,Base::BaseExceptionFreeCADError,e); \
} \
#ifndef DONT_CATCH_CXX_EXCEPTIONS
/// see docu of PY_TRY
# define _PY_CATCH(R) \
__PY_CATCH(R) \
catch(...) \
{ \
Py_Error(Base::BaseExceptionFreeCADError,"Unknown C++ exception"); \
_Py_Error(R,Base::BaseExceptionFreeCADError,"Unknown C++ exception"); \
}
#else
/// see docu of PY_TRY
# define PY_CATCH catch(Base::Exception &e) \
{ \
std::string str; \
str += "FreeCAD exception thrown ("; \
str += e.what(); \
str += ")"; \
e.ReportException(); \
Py_Error(Base::BaseExceptionFreeCADError,str.c_str()); \
} \
catch(std::exception &e) \
{ \
std::string str; \
str += "STL exception thrown ("; \
str += e.what(); \
str += ")"; \
Base::Console().Error(str.c_str()); \
Py_Error(Base::BaseExceptionFreeCADError,str.c_str()); \
} \
catch(const Py::Exception&) \
{ \
return NULL; \
} \
catch(const char *e) \
{ \
Py_Error(Base::BaseExceptionFreeCADError,e); \
}
# define _PY_CATCH(R) __PY_CATCH(R)
#endif // DONT_CATCH_CXX_EXCEPTIONS
#define PY_CATCH _PY_CATCH(return(NULL))
/** Python helper class
* This class encapsulate the Decoding of UTF8 to a python object.
* Including exception handling.

View File

@@ -16,6 +16,7 @@ is provided on an as is basis, without warranties of any kind.
#include <assert.h>
#include <compile.h>
#include <eval.h>
#include <frameobject.h>
#if PY_VERSION_HEX <= 0x02050000
#error "Use Python2.5.x or higher"
@@ -219,6 +220,7 @@ char PP_last_error_trace[MAX]; /* exception traceback text */
PyObject *PP_last_traceback = NULL; /* saved exception traceback object */
PyObject *PP_PyDict_Object = NULL; /* saved exception dictionary object */
PyObject *PP_last_exception_type = NULL; /* saved exception python type */
void PP_Fetch_Error_Text()
@@ -254,7 +256,8 @@ void PP_Fetch_Error_Text()
}
else
{
strcpy(PP_last_error_type, "<unknown exception type>");
/* strcpy(PP_last_error_type, "<unknown exception type>"); */
PP_last_error_type[0] = '\0';
}
Py_XDECREF(pystring);
@@ -318,11 +321,32 @@ void PP_Fetch_Error_Text()
PP_last_error_trace[MAX-1] = '\0';
free(tempstr); /* it's a strdup */
}
else
strcpy(PP_last_error_trace, "<unknown exception traceback>");
else {
PyFrameObject* frame = PyEval_GetFrame();
if(!frame)
return;
int line = PyFrame_GetLineNumber(frame);
#if PY_MAJOR_VERSION >= 3
const char *file = PyUnicode_AsUTF8(frame->f_code->co_filename);
#else
const char *file = PyString_AsString(frame->f_code->co_filename);
#endif
#ifdef FC_OS_WIN32
const char *_f = strstr(file, "\\src\\");
#else
const char *_f = strstr(file, "/src/");
#endif
/* strcpy(PP_last_error_trace, "<unknown exception traceback>"); */
snprintf(PP_last_error_trace,sizeof(PP_last_error_trace),"%s(%d)",(_f?_f+5:file),line);
}
Py_XDECREF(pystring);
Py_XDECREF(PP_last_exception_type);
if(errobj) {
PP_last_exception_type = errobj;
Py_INCREF(errobj);
}else
PP_last_exception_type = 0;
Py_XDECREF(errobj);
Py_XDECREF(errdata); /* this function owns all 3 objects */
Py_XDECREF(PP_last_traceback); /* they've been NULL'd out in Python */

View File

@@ -182,6 +182,7 @@ extern char PP_last_error_trace[]; /* exception traceback text */
extern PyObject *PP_PyDict_Object; /* saved PyDict object */
extern PyObject *PP_last_traceback; /* saved exception traceback object */
extern PyObject *PP_last_exception_type; /* saved exception type */
#ifdef __cplusplus