Notebook

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 []: