Re: Calling a python function from C++ (from Jeff Holle)
An underlining concern I have is error handling. When PyImport_ImportModule fails, a NULL pointer is returned. Seems like the underlining machinery in "handle<>" is intolerant of this.
What makes you say that?
You are correct about the success in compiling your much cleaner example, and it works :-) , but has a flaw. When I hide the needed python script, the call to PyImport_ImportModule returns NULL. When this occurres, both your example and what I had created, but commented out throws something because I can catch it with "catch(...)". I don't however know how to be more specific. Any hints? Whatever it is, it doesn't inherit from "exception". If I was operating on windows, I would anticipate it was a "structured exception", or whatever Microsoft calls their propritary stuff. Don't know of the equivalent on linux though. Without the "catch (...)", the command line program exits with "Aborted".
I've explored whats actually going on some with my debugger and now see what is being thrown. It is "error_already_set". This is a trivial object (it has no attributes at all), and isn't much better than "catch (...)". However, its name implies something. Is there a meaningful error message set somewhere else that can be accessed within the catch clause of "error_already_set"? Jeff Holle wrote:
An underlining concern I have is error handling. When PyImport_ImportModule fails, a NULL pointer is returned. Seems like the underlining machinery in "handle<>" is intolerant of this.
What makes you say that?
You are correct about the success in compiling your much cleaner example, and it works :-) , but has a flaw.
When I hide the needed python script, the call to PyImport_ImportModule returns NULL. When this occurres, both your example and what I had created, but commented out throws something because I can catch it with "catch(...)". I don't however know how to be more specific. Any hints? Whatever it is, it doesn't inherit from "exception".
If I was operating on windows, I would anticipate it was a "structured exception", or whatever Microsoft calls their propritary stuff. Don't know of the equivalent on linux though. Without the "catch (...)", the command line program exits with "Aborted".
Given what the name of this exception, I assume there is a source of information to obtain useful information about it. To this end, I've dug a bit into the Python C API and found the added the following to my code example: catch (error_already_set& x) { PyObject *err_type,*err_value,*err_traceback; PyErr_Fetch(&err_type,&err_value,&err_traceback); cout << "something bad happened" << endl; } While I see that err_type and error_value are not null pointers after the call to PyErr_Fetch, I don't know what to do with either to get at a useful message. Can anybody provide any hints? Jeffrey Holle wrote:
I've explored whats actually going on some with my debugger and now see what is being thrown. It is "error_already_set". This is a trivial object (it has no attributes at all), and isn't much better than "catch (...)". However, its name implies something. Is there a meaningful error message set somewhere else that can be accessed within the catch clause of "error_already_set"?
Jeff Holle wrote:
An underlining concern I have is error handling. When PyImport_ImportModule fails, a NULL pointer is returned. Seems like the underlining machinery in "handle<>" is intolerant of this.
What makes you say that?
You are correct about the success in compiling your much cleaner example, and it works :-) , but has a flaw.
When I hide the needed python script, the call to PyImport_ImportModule returns NULL. When this occurres, both your example and what I had created, but commented out throws something because I can catch it with "catch(...)". I don't however know how to be more specific. Any hints? Whatever it is, it doesn't inherit from "exception".
If I was operating on windows, I would anticipate it was a "structured exception", or whatever Microsoft calls their propritary stuff. Don't know of the equivalent on linux though. Without the "catch (...)", the command line program exits with "Aborted".
Straight from my codebase: std::string getPythonErrorString() { // Extra paranoia... if (!PyErr_Occurred()) { return "No Python error"; } PyObject *type, *value, *traceback; PyErr_Fetch(&type, &value, &traceback); PyErr_Clear(); std::string message = "Python error: "; if (type) { type = PyObject_Str(type); message += PyString_AsString(type); } if (value) { value = PyObject_Str(value); message += ": "; message += PyString_AsString(value); } Py_XDECREF(type); Py_XDECREF(value); Py_XDECREF(traceback); return message; } void checkForPythonError() { if (PyErr_Occurred()) { throw PythonError(getPythonErrorString()); } } void requirePythonError() { if (!PyErr_Occurred()) { throw PythonError("Boost.Python exception, " "but no Python error set."); } } ... PYR_DEFINE_RUNTIME_ERROR(PythonError); std::string getPythonErrorString(); void checkForPythonError(); void requirePythonError(); /** * Allows us to check for Python errors at the end of a Python * code block whether control left the end of the block or * 'return' was used. */ struct PythonCodeErrorSentry { ~PythonCodeErrorSentry() { if (!std::uncaught_exception()) { checkForPythonError(); } } }; #define PYR_NO_UNUSED_WARNING(x) ((void)&(x)) #define PYR_BEGIN_PYTHON_CODE() \ try { \ PythonCodeErrorSentry sentry__; \ PYR_NO_UNUSED_WARNING(sentry__); // silly gcc #define PYR_END_PYTHON_CODE() \ } \ catch (const boost::python::error_already_set&) { \ requirePythonError(); \ throw PythonError(getPythonErrorString()); \ } May have bugs, but it should give you an idea of what to do. Notice I don't do anything with the stacktrace object yet, but it wouldn't take much more work. HTH, Chad On Wed, 30 Jun 2004 02:39:46 -0400, Jeffrey Holle <jeff.holle@verizon.net> wrote:
Given what the name of this exception, I assume there is a source of information to obtain useful information about it. To this end, I've dug a bit into the Python C API and found the added the following to my code example:
catch (error_already_set& x) { PyObject *err_type,*err_value,*err_traceback; PyErr_Fetch(&err_type,&err_value,&err_traceback); cout << "something bad happened" << endl; }
While I see that err_type and error_value are not null pointers after the call to PyErr_Fetch, I don't know what to do with either to get at a useful message. Can anybody provide any hints?
Jeffrey Holle wrote:
I've explored whats actually going on some with my debugger and now see what is being thrown. It is "error_already_set". This is a trivial object (it has no attributes at all), and isn't much better than "catch (...)". However, its name implies something. Is there a meaningful error message set somewhere else that can be accessed within the catch clause of "error_already_set"?
Jeff Holle wrote:
An underlining concern I have is error handling. When PyImport_ImportModule fails, a NULL pointer is returned. Seems like the underlining machinery in "handle<>" is intolerant of this.
What makes you say that?
You are correct about the success in compiling your much cleaner example, and it works :-) , but has a flaw.
When I hide the needed python script, the call to PyImport_ImportModule returns NULL. When this occurres, both your example and what I had created, but commented out throws something because I can catch it with "catch(...)". I don't however know how to be more specific. Any hints? Whatever it is, it doesn't inherit from "exception".
If I was operating on windows, I would anticipate it was a "structured exception", or whatever Microsoft calls their propritary stuff. Don't know of the equivalent on linux though. Without the "catch (...)", the command line program exits with "Aborted".
_______________________________________________ C++-sig mailing list C++-sig@python.org http://mail.python.org/mailman/listinfo/c++-sig
Chad Austin <caustin@gmail.com> writes:
std::string getPythonErrorString(); void checkForPythonError(); void requirePythonError();
/** * Allows us to check for Python errors at the end of a Python * code block whether control left the end of the block or * 'return' was used. */ struct PythonCodeErrorSentry { ~PythonCodeErrorSentry() { if (!std::uncaught_exception()) { checkForPythonError(); } } };
Watch that you don't ever destroy one of these from within a catch block (directly or indirectly), because std::uncaught_exception() will be false at that point. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
I want to thank you for this. I've attempted to improve it, based on code I've found on the internet. It basically adds "Extra Extraa paranoia" about the nature of whats returned from PyErr_Fetch. Don't know if this level of paranoia is needed. But I try to assume nothing. I've attempted to tackle the traceback info too. At this point I don't believe its possible because of inadequeses in the Python "C" API. The one traceset specific function that I do use is undocumented, and it just verifies that the python object is really a traceback object. If you, or anybody, have a different opinion, I'd like to know about it. My code, modified your example is: // Extra paranoia... if (!PyErr_Occurred()) { return; } // PyErr_Print(); PyObject *type = NULL, *value = NULL, *traceback = NULL, *pyString = NULL; PyErr_Fetch(&type, &value, &traceback); PyErr_Clear(); _M_msg = "Python error: "; if (type != NULL && (pyString=PyObject_Str(type))!=NULL && (PyString_Check(pyString))) _M_msg += PyString_AsString(pyString); else _M_msg += "<unknown exception type> "; Py_XDECREF(pyString); if (value != NULL && (pyString=PyObject_Str(value))!=NULL && (PyString_Check(pyString))) { _M_msg += ": "; _M_msg += PyString_AsString(value); } else _M_msg += "<unknown exception date> "; Py_XDECREF(pyString); if (traceback != NULL && PyTraceBack_Check(traceback)) { // what belongs here? TBD } Py_XDECREF(type); Py_XDECREF(value); Py_XDECREF(traceback); Chad Austin wrote:
Straight from my codebase:
std::string getPythonErrorString() { // Extra paranoia... if (!PyErr_Occurred()) { return "No Python error"; }
PyObject *type, *value, *traceback; PyErr_Fetch(&type, &value, &traceback); PyErr_Clear();
std::string message = "Python error: "; if (type) { type = PyObject_Str(type); message += PyString_AsString(type); } if (value) { value = PyObject_Str(value); message += ": "; message += PyString_AsString(value); } Py_XDECREF(type); Py_XDECREF(value); Py_XDECREF(traceback);
return message; }
void checkForPythonError() { if (PyErr_Occurred()) { throw PythonError(getPythonErrorString()); } }
void requirePythonError() { if (!PyErr_Occurred()) { throw PythonError("Boost.Python exception, " "but no Python error set."); } }
...
PYR_DEFINE_RUNTIME_ERROR(PythonError);
std::string getPythonErrorString(); void checkForPythonError(); void requirePythonError();
/** * Allows us to check for Python errors at the end of a Python * code block whether control left the end of the block or * 'return' was used. */ struct PythonCodeErrorSentry { ~PythonCodeErrorSentry() { if (!std::uncaught_exception()) { checkForPythonError(); } } };
#define PYR_NO_UNUSED_WARNING(x) ((void)&(x))
#define PYR_BEGIN_PYTHON_CODE() \ try { \ PythonCodeErrorSentry sentry__; \ PYR_NO_UNUSED_WARNING(sentry__); // silly gcc
#define PYR_END_PYTHON_CODE() \ } \ catch (const boost::python::error_already_set&) { \ requirePythonError(); \ throw PythonError(getPythonErrorString()); \ }
May have bugs, but it should give you an idea of what to do. Notice I don't do anything with the stacktrace object yet, but it wouldn't take much more work.
HTH, Chad
On Wed, 30 Jun 2004 02:39:46 -0400, Jeffrey Holle <jeff.holle@verizon.net> wrote:
Given what the name of this exception, I assume there is a source of information to obtain useful information about it. To this end, I've dug a bit into the Python C API and found the added the following to my code example:
catch (error_already_set& x) { PyObject *err_type,*err_value,*err_traceback; PyErr_Fetch(&err_type,&err_value,&err_traceback); cout << "something bad happened" << endl; }
While I see that err_type and error_value are not null pointers after the call to PyErr_Fetch, I don't know what to do with either to get at a useful message. Can anybody provide any hints?
Jeffrey Holle wrote:
I've explored whats actually going on some with my debugger and now see what is being thrown. It is "error_already_set". This is a trivial object (it has no attributes at all), and isn't much better than "catch (...)". However, its name implies something. Is there a meaningful error message set somewhere else that can be accessed within the catch clause of "error_already_set"?
Jeff Holle wrote:
An underlining concern I have is error handling. When PyImport_ImportModule fails, a NULL pointer is returned. Seems like the underlining machinery in "handle<>" is intolerant of this.
What makes you say that?
You are correct about the success in compiling your much cleaner example, and it works :-) , but has a flaw.
When I hide the needed python script, the call to PyImport_ImportModule returns NULL. When this occurres, both your example and what I had created, but commented out throws something because I can catch it with "catch(...)". I don't however know how to be more specific. Any hints? Whatever it is, it doesn't inherit from "exception".
If I was operating on windows, I would anticipate it was a "structured exception", or whatever Microsoft calls their propritary stuff. Don't know of the equivalent on linux though. Without the "catch (...)", the command line program exits with "Aborted".
_______________________________________________ C++-sig mailing list C++-sig@python.org http://mail.python.org/mailman/listinfo/c++-sig
Jeffrey Holle <jeff.holle@verizon.net> writes:
I've explored whats actually going on some with my debugger and now see what is being thrown. It is "error_already_set".
Yes; did you miss my posting? http://article.gmane.org/gmane.comp.python.c%2B%2B/6662 -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
Jeff Holle <jeff.holle@verizon.net> writes:
An underlining concern I have is error handling. When PyImport_ImportModule fails, a NULL pointer is returned. Seems like the underlining machinery in "handle<>" is intolerant of this.
What makes you say that?
You are correct about the success in compiling your much cleaner example, and it works :-) , but has a flaw.
When I hide the needed python script, the call to PyImport_ImportModule returns NULL. When this occurres, both your example and what I had created, but commented out throws something because I can catch it with "catch(...)". I don't however know how to be more specific. Any hints? Whatever it is, it doesn't inherit from "exception".
It's "boost::python::error_already_set". It means that Python knows what the error is already. If you're in an extension module, it's handled automatically. Otherwise, you can look at www.boost.org/libs/python/test/embedding.cpp for examples of how to get at this exception. This is all in the documentation, BTW: http://www.boost.org/libs/python/doc/v2/object.html#object-spec-ctors http://www.boost.org/libs/python/doc/v2/errors.html
If I was operating on windows, I would anticipate it was a "structured exception", or whatever Microsoft calls their propritary stuff.
Never!
Don't know of the equivalent on linux though. Without the "catch (...)", the command line program exits with "Aborted".
'course it does. That's C++. ;-) -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
participants (4)
-
Chad Austin -
David Abrahams -
Jeff Holle -
Jeffrey Holle