[capi-sig] expy 0.5.2 released
Stefan Behnel
python_capi at behnel.de
Wed Feb 3 12:18:53 CET 2010
Yingjie Lan, 03.02.2010 10:46:
>>> in release 0.5.2, expy now supports custom exceptions,
>>> besides all built-in ones, and exception handling is made
>>> easy.
>>
>> How does that work? I.e., how do you handle exceptions that
>> occur inside of
>> the plain string that expy uses to integrate C code?
>
> There are several ways to throw exceptions with expy. First of all, you
> can use @throws(...) to throw exceptions based on input
> and output of a function. Another possibility is to throw exceptions inside
> your function implementation, and upon return of your code, the wrapper
> function would return NULL after all necessary reference count management,
> if any exception is thrown by your function implementation code. Please
> also refer to the documentation of expy on exception handling here (it also
> includes how to create a custom exception, and how to manage exception
> inheritance):
>
> http://expy.sourceforge.net/tutorial.html#exception-for-arguments-returns
Hmm, there's a lot of code in that example. Let's contrast that to Cython.
-------------------------
MyError = gfield(exception('module1f.MyError', ValueError))
NegError = gfield(exception('module1f.NegEror', MyError))
-------------------------
Cython:
class MyError(ValueError): pass
class NegError(MyError): pass
-------------------------
#'$return': returned value
@function(int)
@throws('$return<0', NegError, 'No negatives!')
@throws('$return>100', ValueError, 'Out of range: %i', '$return')
def range_check(n = int):
return "n*n"
-------------------------
Cython:
def range_check(int n):
cdef int retval = n*n
if retval < 0:
raise NegError('No negatives!')
if retval > 100:
raise ValueError('Out of range: %d' % retval)
return retval
Note that the C compiler will likely discard the "retval < 0" block here. I
assume you meant to check the input value instead?
-------------------------
@function(str)
@throws('n<0', ValueError, 'No nagatives: %s=%i!','desc, n')
@throws('$return==NULL',
code="""
//you can also do sth else here
PyErr_NoMemory();
""")
def stringify(n=int, desc = str):
return """{
char* s = malloc(sizeof(char)*20);
if (s) sprintf(s, "%i", n);
return s;
}"""
-------------------------
This example will actually leak memory. Also, it won't work correctly on a
64-bit system.
Anyway, here's some Cython code for this:
from stdio cimport sprintf
from stdlib cimport malloc, free
from python_exc cimport PyErr_NoMemory
def stringify(int n, desc):
cdef char* s
if n < 0:
raise ValueError('No negatives: %s = %d' % (desc,i))
s = malloc(sizeof(char)*20)
if s is NULL:
PyErr_NoMemory()
sprintf(s, "%i", n)
try:
return s
finally:
free(s)
Personally, I'd rather write this:
def stringify(int n, str desc):
if n < 0:
raise ValueError('No negatives: %s = %d' % (desc,i))
return str(n)
or even this:
def stringify(unsigned int n):
return str(n)
which will also do the right thing.
-------------------------
#throws(True/False, ...):
#special case when you provide all code
#for exception checking and handling
#True for arguement, False for returned values
@function(int) #argument checking
@throws('den > 500', MyError, "Can't handle it!")
@throws(True, code="""
int i = 2;
while(i < den && (den % i)) i++;
if (i != den){ //not a prime!
PyErr_SetString(PyExc_Exception,
"argument 'den' must be a prime!");
//usually don't return here
//unless no argument is of object type,
//as you need to DEC_REF it, which
//is automatically handled later.
}
""")
@throws(False, code="""
int i = 2;
while(i < $return && ($return % i)) i++;
if (i != $return){ //not a prime!
PyErr_SetString(PyExc_Exception,
"must return a prime!");
return NULL;
}
""")
def prime_friend(den = int):
return "den+2"
-------------------------
Cython:
cdef check_prime(int p, error_message):
cdef int i
for i in range(2, p): # I'd add at least a sqrt() here
if not (p % i):
raise Exception(error_message)
def prime_friend(int den):
cdef int i, retval
if den > 500:
raise MyError("Can't handle it!")
check_prime(den)
check_prime(den+2)
return den+2
Looking at the two implementations, I actually wonder what the term
"express way" means to you exactly.
Stefan
More information about the capi-sig
mailing list