[capi-sig] expy 0.5.2 released
Yingjie Lan
lanyjie at yahoo.com
Wed Feb 3 13:21:00 CET 2010
Hi, Thank you very much for making this fantastic
contrast with Cython. Admittedly, Cython has
a lot of good things, and it is improved a lot.
The task expy is targeting at is different from
that of Cython, and I hope expy provides more
flexibility when it comes to extend Python.
> >>> 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
>
>
Yes, this looks much easier, I need to modify the way
exceptions are defined in expy, after which the
same thing would be possible with expy. Thanks!
There are many good things to learn from Cython.
> -------------------------
> #'$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?
>
You are right, n*n < 0 would never happen.
>
> -------------------------
> @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.
First of all, thank you very much for pointing this out to me.
I'm not sure if this is something unavoidable by the
design of expy or if I can avoid this by improve
my implementation code of this particular example.
I will check this and see what I can do about it.
Meanwhile, I wonder if you can give me some suggestions,
especially on the memory leak issue.
>
> 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.
>
EXPY requires you to do things in pure C
when it comes to logic/functionality, the
bright side of this is that you have
full control over the implementation.
As I said, EXPY and Cython are different
in their objectives.
>
> -------------------------
> #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.
>
Well, in EXPY, you can also define this check_prime(.)
function and then use it, I will improve this the way
you did. Thanks again!
Yingjie
More information about the capi-sig
mailing list