In [4]:
%load_ext cython

Regular class vs extensions class

In [5]:
%%cython

cdef extern from "Python.h" nogil:
     ctypedef class __builtin__.Exception [object PyBaseExceptionObject]:
         pass

cdef class FUSEErrorExt(Exception):
    '''
    This exception may be raised by request handlers to indicate that
    the requested operation could not be carried out. The system call
    that resulted in the request (if any) will then fail with error
    code *errno_*.
    '''

    # If we call this variable "errno", we will get syntax errors
    # during C compilation (maybe something else declares errno as
    # a macro?)
    cdef int errno_

    property errno:
        '''Error code to return to client process'''
        def __get__(self):
            return self.errno_
        def __set__(self, val):
            self.errno_ = val

    def __init__(self, errno):
        self.errno_ = errno
In [6]:
class FUSEErrorInt(Exception):
    def __init__(self, errno):
        self.errno = errno
In [7]:
def test_ext():
    a = 0
    for i in range(100):
        try:
            raise FUSEErrorExt(i)
        except FUSEErrorExt as exc:
            a += exc.errno
        except:
            print('This should not happen')
    return a

def test_int():
    a = 0
    for i in range(100):
        try:
            raise FUSEErrorInt(i)
        except FUSEErrorInt as exc:
            a += exc.errno
        except:
            print('This should not happen')
    return a
In [8]:
assert test_ext() == test_int()
%timeit test_ext()
%timeit test_int()
10000 loops, best of 3: 35.3 µs per loop
10000 loops, best of 3: 55.6 µs per loop

Instantiation vs Factory Function with Cache

(Unfortunately we cannot use @cython.freelist for derived classes)

In [9]:
cache = dict()
def getError(errno):
    try:
        return cache[errno]
    except KeyError:
        cache[errno] = FUSEErrorExt(errno)
        return cache[errno]
    
def test_ext_cached():
    a = 0
    for i in range(100):
        try:
            raise getError(i)
        except FUSEErrorExt as exc:
            a += exc.errno
        except:
            print('This should not happen')
    return a
In [10]:
assert test_ext() == test_ext_cached()
%timeit test_ext()
%timeit test_ext_cached()
10000 loops, best of 3: 34.9 µs per loop
10000 loops, best of 3: 38 µs per loop

Catching Exception vs Ordinary Return

In [15]:
def handler(i):
    return getError(i)

def test_ext_direct():
    a = 0
    for i in range(100):
        res = handler(i)
        if isinstance(res, FUSEErrorExt):
            a += res.errno
    return a
In [16]:
assert test_ext_cached() == test_ext_direct()
%timeit test_ext_cached()
%timeit test_ext_direct()
10000 loops, best of 3: 38 µs per loop
10000 loops, best of 3: 29.1 µs per loop

In []: