I'm getting errors like this:
2010-07-13 20:43:15.131
Python[1527:60f] main: Caught
OC_PythonException: :
LoginMenuSet instance has no attribute
'play_sound'
That's with this code:
#try {
[section loop]; //Loop through section
} #catch (NSException *exception) {
NSLog(#"Caught %#: %#", [exception name], [exception reason]);
}
I want the python exception to be printed normally with the traceback and everything else.
Thank you.
One trick to see Python exceptions is to call objc.setVerbose(1). This makes PyObjC slightly more verbose and causes it to print Python stack traces when converting exceptions from Python to Objective-C.
Here's my own solution:
In Objective-C class:
#try {
[section loop]; //Loop through section
} #catch (NSException *exception) {
NSLog(#"main: Caught %#: %#", [exception name], [exception reason]);
[self exception: [[exception userInfo] valueForKey: #"__pyobjc_exc_traceback__"]];
}
In python pyobjc subclass:
def exception_(self,trace):
traceback.print_tb(trace)
NSApplication.sharedApplication().terminate_(None) #Accept no errors
I, of-course, imported the traceback module.
Related
I'm wrapping some classes using Swig directors in Python.
Each of my class methods returns a MyError class. What happens is, once I derive a python class from one of my C++ and I forget to return the MyError() object but I return None of "pass" or I forget to return anything, my software crashes in the default constructor of the MyError class through a read access violation, and I'm not able to track this exception through a try/catch block.
What is the correct way in Swig to handle such kind of situations ?
Thanks!
Section 36.5.4 Exception unrolling in the SWIG documentation:
With directors routing method calls to Python, and proxies routing them to C++, the handling of exceptions is an important concern. By default, the directors ignore exceptions that occur during method calls that are resolved in Python. To handle such exceptions correctly, it is necessary to temporarily translate them into C++ exceptions. This can be done with the %feature("director:except") directive. The following code should suffice in most cases:
%feature("director:except") {
if ($error != NULL) {
throw Swig::DirectorMethodException();
}
}
This code will check the Python error state after each method call from a director into Python, and throw a C++ exception if an error occurred. This exception can be caught in C++ to implement an error handler. Currently no information about the Python error is stored in the Swig::DirectorMethodException object, but this will likely change in the future.
It may be the case that a method call originates in Python, travels up to C++ through a proxy class, and then back into Python via a director method. If an exception occurs in Python at this point, it would be nice for that exception to find its way back to the original caller. This can be done by combining a normal %exception directive with the director:except handler shown above. Here is an example of a suitable exception handler:
%exception {
try { $action }
catch (Swig::DirectorException &e) { SWIG_fail; }
}
The class Swig::DirectorException used in this example is actually a base class of Swig::DirectorMethodException, so it will trap this exception. Because the Python error state is still set when Swig::DirectorMethodException is thrown, Python will register the exception as soon as the C wrapper function returns.
Example
Here's an example test.i that illustrates the technique:
%module test
%module(directors="1") test
%feature("director");
%feature("director:except") {
if ($error != NULL) {
throw Swig::DirectorMethodException();
}
}
%exception {
try { $action }
catch (Swig::DirectorException &e) { SWIG_fail; }
}
%inline %{
class MyError {
int m_n;
public:
MyError(int n = 0) : m_n(n) {}
~MyError() {}
int get() const { return m_n; }
};
class Demo {
public:
Demo() {}
virtual ~Demo() {}
virtual MyError test() { return MyError(5); }
};
int func(Demo* d) { return d->test().get(); }
%}
After swigging and compiling, a demo:
>>> import test
>>> d=test.Demo() # default class implementation
>>> test.func(d) # Call virtual method in a C++ test function.
5
The above worked normally. Below overrides incorrectly:
>>> class Demo2(test.Demo): # New class
... def test(self): # Override virtual function
... return 7 # But don't return a MyError object.
...
>>> d=Demo2()
>>> test.func(d)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: SWIG director type mismatch in output value of type 'MyError'
That caught the exception and returned a useful exception. Below overrides correctly:
>>> class Demo2(test.Demo):
... def test(self):
... return test.MyError(7)
...
>>> d=Demo2()
>>> test.func(d)
7
I'm trying to build a Flask server using Haxe, and I'm stumped on how to return a JSON. I got an example up and running using this gist that has Main.index() return a String, but when I try to return a String from haxe.Json.stringify() I get an error when I try to run the output Python.
import haxe.Constraints.Function;
#:pythonImport("flask", "Flask")
extern class Flask {
function new(module:String);
function route<T:Function>(path:String):T->T;
function run():Void;
}
class Main {
static function main() {
var app = new Flask(untyped __name__);
app.route("/")(index);
app.run();
}
static function index() {
return haxe.Json.stringify({msg:"hello"});
}
}
Python error:
$ python main.py
File "main.py", line 69
return haxe_format_JsonPrinter.print(_hx_AnonObject({'msg': "hello"}),None,None)
^
SyntaxError: invalid syntax
It doesn't seem to be well documented but Haxe's python target only supports Python 3. See https://github.com/HaxeFoundation/haxe/issues/4195
In this case, "print" was a keyword in Python 2, and the Haxe generated code is trying to generate a function named "print", hence the error.
Try:
python3 main.py
to have it run correctly.
Why doesn't this work?
try:
1/0
except ZeroDivisionError as e:
e.message += ', you fool!'
raise
The modified message is not used, even though it remains on the exception instance. Is there a working pattern for the above? Behaviour should be like my current workaround below:
try:
1/0
except ZeroDivisionError as e:
args = e.args
if not args:
arg0 = ''
else:
arg0 = args[0]
arg0 += ', you fool!'
e.args = (arg0,) + args[1:]
raise
I'm aware of exception chaining in python3, it looks nice but unfortunately doesn't work in python2. So what is the usual recipe for re-raising an exception in python2?
Note: Because of the warnings and caveats mentioned here, I don't want to dig up the traceback and create a new exception but rather re-raise the existing exception instance.
Changing e.args is the only way to do this. The implementation for BaseException.__str__ only considers the args tuple, it doesn't look at message at all:
static PyObject *
BaseException_str(PyBaseExceptionObject *self)
{
PyObject *out;
switch (PyTuple_GET_SIZE(self->args)) {
case 0:
out = PyString_FromString("");
break;
case 1:
out = PyObject_Str(PyTuple_GET_ITEM(self->args, 0));
break;
default:
out = PyObject_Str(self->args);
break;
}
return out;
}
This shouldn't be too unexpected, since BaseException.message is deprecated since Python 2.6.
I don't think there's going to be a better solution than your workaround. ZeroDivisionError doesn't use the message property to build its output, so modifying that will have no effect.
If you're specifically catching a ZeroDivisionError it should always have a single argument so the following more succinct version will work:
try:
1/0
except ZeroDivisionError as e:
e.args = (e.args[0] + ', you fool!',)
raise
If you check the properties of ZeroDivisionError, all it has are args and message and it doesn't use message so short of creating a new error, modifying args is pretty much the only possible solution.
I am creating my own Python extension (using SWIG, but I hope that is not relevant).
In the C++ side of it, I am using PyErr_NewException to create a custom exception object.
// C++ - create a custom Python Exception class.
m = Py_InitModule((char *) "MyModule", SwigMethods);
g_pyMyErr = PyErr_NewException( "MyModule.MyErr", 0, 0 );
Py_INCREF(g_pyMyErr);
int result = PyModule_AddObject(m, "MyErr", g_pyMyErr);
The above code returns success values and I can throw the above exception successfully and catch it in the Python client code.
The problem is this: When I refer to "MyErr" in Python code I get an error saying "MyErr" is not defined.
// Python client code - catch the exception
from MyModule import *
try:
causeException()
catch MyErr: # Error: MyErr is not defined.
pass
catch Exception:
pass
EDIT: My current thinking is that maybe SWIG is altering (mangling) the names of things.
You need to define the error then:
%{
class MyErr {};
%}
Also, I would suggest adding this, so that you could catch the exceptions as MyModule.MyErr and not as MyModule._MyModule.MyErr (since that is how they will be generated by SWIG):
%pythoncode %{
MyErr = _MyModule.MyErr
%}
I write a python wrapper for box2d, everything work perfect but a strange TypeError error occurs sometimes when calling method function exposed by boost python. It's a random behavior, not happened every time.
The problem python code:
try:
world = body.GetWorld() # world is b2World instance, body is b2Body instance
world.DestroyBody(body) # raise TypeError: 'NoneType' object is not callable
except TypeError: # catch it, and print some infomation
print "xxxxx", world # I got a b2World instance here
print "xxxxx", sys.getrefcount(world) # I got a value of 66 here
print "xxxxx", world.DestroyBody # I got a bound method object here
raise
It seems all okay. How does that happened?
And part of my wrapper code:
// [file]: https://github.com/layzerar/box2d-py/blob/master/python/world.cpp
// [project]: https://github.com/layzerar/box2d-py
template<class T>
inline void b2Func_ClearUserData(T& self)
{
using namespace boost::python;
xdecref((PyObject*)self.GetUserData());
self.SetUserData(NULL);
}
inline void b2World_DestroyBody(b2World& self, b2Body* body)
{
b2Assert(self.GetBodyCount() > 0);
b2Assert(self.IsLocked() == false);
b2Func_ClearUserData(*body);
self.DestroyBody(body);
}
class_<b2World, b2World_W , boost::noncopyable>("b2World")
//...
.def("CreateBody", b2World_CreateBody, return_internal_reference<>())
.def("DestroyBody", b2World_DestroyBody)
//...
;
Did I make an obvious mistake?
After a few days exploring I found the answer.
I make a mistake about the question, the exception is really not thrown by DestroyBody. It's thrown by a virtual function which is rewrite in python and called in DestroyBody function.