How do I properly use Python's C API and exceptions? - python

if I do something like
>>> x = int(1,2,3,4,5)
I immediately get a fatal error (one that would end program execution if it was in a pre-written script)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: int() takes at most 2 arguments (5 given)
and x remains undefined:
>>> x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
How would I go about implementing that in Python's C API? I found some documentation for it, but I am not sure that I know how to use it correctly.
Here is what I have been trying:
Print:
if(something) {
PyErr_SetString(PyExc_TypeError, "Oh no!");
PyErr_Print();
}
This, unfortunately, only prints the exception and the program continues. Additionally,—if I understand it correctly—PyErr_Print() removes the exception from some sort of queue so Python thinks that it is handled. This is what it looks like:
>>> import awesomemod
>>> x = awesomemod.thing()
TypeError: Oh no!
>>> x # x is defined because the function returns None eventually
>>>
PyErr_Occurred():
if(something) {
PyErr_SetString(PyExc_TypeError, "Oh no!");
PyErr_Occurred();
}
Behavior:
>>> import awesomemod
>>> awesomemod.thing()
>>>
TypeError: Oh no!
>>>
So it does it kind of late...
return PyErr_Occurred():
if(something) {
PyErr_SetString(PyExc_TypeError, "Oh no!");
return PyErr_Occurred();
}
Behavior:
>>> import awesomemod
>>> awesomemod.thing()
<type 'exceptions.TypeError'>
>>>
TypeError: Oh no!
This one is just really weird.
What do I need to do to get the behavior of built-in functions?
Edit: I tried what #user2864740 suggested in a comment and it worked perfectly!
if(something) {
PyErr_SetString(PyExc_TypeError, "Oh no!");
return (PyObject *) NULL;
}

Raising an exception in C is done by setting the exception object or string and then returning NULL from the function.

As Ignacio Vazquez-Abrams said:
Raising an exception in C is done by setting the exception object or string and then returning NULL from the function.
There are convenience functions which make this easy to do for common exception types. For example, PyErr_NoMemory can be used like this:
PyObject *my_function(void)
{
return PyErr_NoMemory(); // Sets the exception and returns NULL
}

Related

Is everything in Python castable to a string?

I'm trying to find an example of something in Python that can't be cast to a string.
>>> str(None)
'None'
>>> str(False)
'False'
>>> str(5)
'5'
>>> str(object)
"<class 'object'>"
>>> class Test:
... pass
...
>>> str(Test)
"<class '__main__.Test'>"
>>> str(Test())
'<__main__.Test object at 0x7f7e88a13630>'
Is there anything the entire Python universe that cannot be cast to str?
From the __str__ documentation:
The default implementation defined by the built-in type object
calls object.__repr__().
and object.__repr__ prints object name and address (at least in cpython). That's where your output '<__main__.Test object at 0x7f7e88a13630>' comes from. A class would have to override __str__ and raise an exception (or have a bug) to fail. There is little reason to do this and you'd be hard-pressed to find one that wasn't built to purpose.
Is everything in Python castable to a string?
Nope!
>>> class MyObject():
... def __str__(self):
... raise NotImplementedError("You can't string me!")
...
>>> str(MyObject())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __str__
NotImplementedError: You can't string me!

Swig exception in Python director class

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

implementing a deferred exception in Python

I would like to implement a deferred exception in Python that is OK to store somewhere but as soon as it is used in any way, it raises the exception that was deferred. Something like this:
# this doesn't work but it's a start
class DeferredException(object):
def __init__(self, exc):
self.exc = exc
def __getattr__(self, key):
raise self.exc
# example:
mydict = {'foo': 3}
try:
myval = obtain_some_number()
except Exception as e:
myval = DeferredException(e)
mydict['myval'] = myval
def plus_two(x):
print x+2
# later on...
plus_two(mydict['foo']) # prints 5
we_dont_use_this_val = mydict['myval'] # Always ok to store this value if not used
plus_two(mydict['myval']) # If obtain_some_number() failed earlier,
# re-raises the exception, otherwise prints the value + 2.
The use case is that I want to write code to analyze some values from incoming data; if this code fails but the results are never used, I want it to fail quietly; if it fails but the results are used later, then I'd like the failure to propagate.
Any suggestions on how to do this? If I use my DeferredException class I get this result:
>>> ke = KeyError('something')
>>> de = DeferredException(ke)
>>> de.bang # yay, this works
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __getattr__
KeyError: 'something'
>>> de+2 # boo, this doesn't
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'DeferredException' and 'int'
Read section 3.4.12 of the docs, "Special method lookup for new-style classes." It explains exactly the problem you have encountered. The normal attribute lookup is bypassed by the interpreter for certain operators, such as addition (as you found out the hard way). Thus the statement de+2 in your code never calls your getattr function.
The only solution, according to that section, is to insure that "the special method must be set on the class object itself in order to be consistently invoked by the interpreter."
Perhaps you'd be better off storing all your deferred exceptions in a global list, wrapping your entire program in a try:finally: statement, and printing out the whole list in the finally block.

Is it possible to mock the string module from Python?

For instance, if I have a call to the split method (i.e. some_string.split(":") )
Is is possible to mock this. I wanted to assert that the split function is called using assert_called_once_with
I confirm you can't do that because split() is a built-in attribute of str object and you can't set attributes of built-in or extension because they are readonly.
Below some inconclusive tests after trying into a Python 2.7.10 interpreter
>>> __builtins__.str.split
<method 'split' of 'str' objects>
>>> type(__builtins__.str.split)
<type 'method_descriptor'>
Trying to override it using a function
>>> type(lambda f:f)
<type 'function'>
>>> __builtins__.str.split = lambda f: f
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'str'
Trying to override it using a callable (function or method)
>>> type(callable)
<type 'builtin_function_or_method'>
>>> __builtins__.str.split = callable
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'str'
After having a look more deeply into the CPython source code here [1]. It's a limitation in Objects/typeobject.c introduce by the function list below. This function check if we try to set a readonly attribute and raise TypeError.
type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
{
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
PyErr_Format(
PyExc_TypeError,
"can't set attributes of built-in/extension type '%s'",
type->tp_name);
return -1;
}
if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0)
return -1;
return update_slot(type, name);
}
[1] https://hg.python.org/cpython/file/tip/Objects/typeobject.c#l3022
Yes it is with a couple of caviats.
In my case I have successfully mocked str in python3 so I can assert that split is being called with a specific input
There are two caviats
With patch, I replaced the original str class with a new class that inherits from str
In the code that I was testing, I had to do a redundant string casting like str(str_val).split
Here's how one can do it:
class MyStr(str):
def split(self, sep=None, maxsplit=-1)):
expected_str = "some_input_mutated_inside_fn_before_split_called"
self.assertEqual(self, expected_str)
return super().split(sep=sep, maxsplit=maxsplit)
with patch('mymodule.str', new=MyStr):
output = mymodule.function_that_calls_string_split(
"some_input"
)

Strange TypeError occured when calling method function exposed by boost python

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.

Categories