[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