extending python, what is the best option for me?

52
Extending Python Francisco Fernandez Castano Rushmore.fm [email protected] @fcofdezc November 29, 2014 http://kcy.me/1dykg Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 1 / 52

Upload: codemotion

Post on 05-Jul-2015

206 views

Category:

Technology


3 download

DESCRIPTION

by Francisco Fernández Castaño - Python is a great language, but there are occasions where we need access to low level operations or connect with some database driver written in C. With the FFI(Foreign function interface) we can connect Python with other languages like C, C++ and even the new Rust. There are some alternatives to achieve this goal, Native Extensions, Ctypes and CFFI. I'll compare this three ways of extending Python.

TRANSCRIPT

Page 1: Extending Python, what is the best option for me?

Extending Python

Francisco Fernandez Castano

Rushmore.fm

[email protected] @fcofdezc

November 29, 2014

http://kcy.me/1dykg

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 1 / 52

Page 2: Extending Python, what is the best option for me?

Overview

1 Motivations

2 Guidelines

3 Native extensions

4 CTypes

5 CFFI

6 Conclusions

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 2 / 52

Page 3: Extending Python, what is the best option for me?

Caution

Huge topic

Toy examples

Unix CPython centric

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 3 / 52

Page 4: Extending Python, what is the best option for me?

Motivation

Why write in C?

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 4 / 52

Page 5: Extending Python, what is the best option for me?

Motivation

Speed

Using legacy code

Integration

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 5 / 52

Page 6: Extending Python, what is the best option for me?

MotivationWhy is Python slow?

Interpretation overhead

Boxed arithmetic and automatic overflow handling

Dynamic dispatch of operations

Dynamic lookup of methods and attributes

Everything can change on runtime

Extreme introspective and reflective capabilities

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 6 / 52

Page 7: Extending Python, what is the best option for me?

Motivation

Speed

Using legacy code

Integration

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 7 / 52

Page 8: Extending Python, what is the best option for me?

Motivation

Speed

Using legacy code

Integration

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 8 / 52

Page 9: Extending Python, what is the best option for me?

Guidelines

Static libraries

Shared libraries

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 9 / 52

Page 10: Extending Python, what is the best option for me?

Static libraries

$ ar tv libdemo.a

rw -r--r-- 501/20 40 Jul 19 22:34 2014 __.SYMDEF

rw -r--r-- 501/20 2352 Jul 19 22:33 2014 a.o

rw -r--r-- 501/20 2352 Jul 19 22:33 2014 b.o

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 10 / 52

Page 11: Extending Python, what is the best option for me?

Shared libraries

Single copy in memory

Runtime load

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 11 / 52

Page 12: Extending Python, what is the best option for me?

Native extensions

C API

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 12 / 52

Page 13: Extending Python, what is the best option for me?

Native extensions

Hello world, Newton method

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 13 / 52

Page 14: Extending Python, what is the best option for me?

Native extensions

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 14 / 52

Page 15: Extending Python, what is the best option for me?

Native extensions

#include "Python.h"

static PyObject *

newton(PyObject *self , PyObject *args)

{

float guess;

float x;

if (! PyArg_ParseTuple(args , "ff", &guess , &x))

return NULL;

while (fabs(powf(guess , 2) - x) > 0.01)

{

guess = ((x / guess) + guess) / 2;

}

return Py_BuildValue("f", guess );

}

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 15 / 52

Page 16: Extending Python, what is the best option for me?

Native extensions

static PyMethodDef

module_functions [] = {

{"newton", newton , METH_VARARGS , "Newton method."},

{NULL}

};

PyMODINIT_FUNC

initnewton(void)

{

Py_InitModule3("newton", module_functions , "Newton");

}

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 16 / 52

Page 17: Extending Python, what is the best option for me?

Native extensions

from distutils.core import setup , Extension

setup(name=’codemotion ’,

version =1.0,

ext_modules =[

Extension(’newton ’, [’newton.c’])])

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 17 / 52

Page 18: Extending Python, what is the best option for me?

Native extensions

Python 2.7.5 (default , Nov 3 2014, 14:26:24)

[GCC 4.8.3 20140911 (Red Hat 4.8.3 -7)] on linux2

>>> import newton

>>> newton.newton(1, 2)

1.4166667461395264

>>>

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 18 / 52

Page 19: Extending Python, what is the best option for me?

How?

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 19 / 52

Page 20: Extending Python, what is the best option for me?

Native extensions

NAME

dlopen --load and link a dynamic library or bundle

SYNOPSIS

#include <dlfcn.h>

void*

dlopen(const char* path , int mode);

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 20 / 52

Page 21: Extending Python, what is the best option for me?

Native extensions

$ nm -g newton.so

00000000002010 a0 B __bss_start

w __cxa_finalize@@GLIBC_2 .2.5

00000000002010 a0 D _edata

00000000002010 a8 B _end

0000000000000944 T _fini

w __gmon_start__

00000000000006 d0 T _init

0000000000000920 T initnewton

w _ITM_deregisterTMCloneTable

w _ITM_registerTMCloneTable

w _Jv_RegisterClasses

U PyArg_ParseTuple

U Py_BuildValue

U Py_InitModule4_64

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 21 / 52

Page 22: Extending Python, what is the best option for me?

Native extensions

cpython/Python/dynload shlib.c

handle = dlopen(pathname , dlopenflags );

if (handle == NULL) {

const char *error = dlerror ();

if (error == NULL)

error = "unknown dlopen () error";

PyErr_SetString(PyExc_ImportError , error );

return NULL;

}

if (fp != NULL && nhandles < 128)

handles[nhandles ++]. handle = handle;

p = (dl_funcptr) dlsym(handle , funcname );

return p;

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 22 / 52

Page 23: Extending Python, what is the best option for me?

Native extensionsMemory management

Manual memory management

Python GC - RC

Cycle detector

Py INCREF(x) Py DECREF(x)

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 23 / 52

Page 24: Extending Python, what is the best option for me?

Native extensionsError management

Return NULL as a convention

Register exceptions

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 24 / 52

Page 25: Extending Python, what is the best option for me?

Native extensionsError management

if (err < 0) {

PyErr_SetString(PyExc_Exception , "Err");

return NULL;

}

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 25 / 52

Page 26: Extending Python, what is the best option for me?

Native extensionsPython 3 differences

static struct PyModuleDef examplemodule = {

PyModuleDef_HEAD_INIT ,

"newton",

"newton module doc string",

-1,

module_functions ,

NULL ,

NULL ,

NULL ,

NULL};

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 26 / 52

Page 27: Extending Python, what is the best option for me?

Native extensionsPython 3 differences

PyMODINIT_FUNC

PyInit_sum(void)

{

PyModule_Create (& examplemodule );

}

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 27 / 52

Page 28: Extending Python, what is the best option for me?

CTypes

Advanced FFI for Python

Allows call functions from Shared libs

Create, access, manipulate C data types

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 28 / 52

Page 29: Extending Python, what is the best option for me?

CTypesTypes correspondence

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 29 / 52

Page 30: Extending Python, what is the best option for me?

CTypesStructs

from ctypes import *

class POINT(Structure ):

_fields_ = [("x", c_int), ("y", c_int )]

class RECT(Structure ):

_fields_ = [("upperleft", POINT),

("lowerright", POINT )]

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 30 / 52

Page 31: Extending Python, what is the best option for me?

CTypesExample

Implemented fibonacci as c function

Map as Python code

Measure differences between Python and C

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 31 / 52

Page 32: Extending Python, what is the best option for me?

CTypes

int fib(int n){

if (n < 2)

return n;

else

return fib(n - 1) + fib(n - 2);

}

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 32 / 52

Page 33: Extending Python, what is the best option for me?

CTypes

import ctypes

lib_fib = ctypes.CDLL("libfib.so")

def ctypes_fib(n):

return lib_fib.fib(ctypes.c_int(n))

def py_fib(n):

if n < 2:

return n

else:

return py_fib(n-1) + py_fib(n-2)

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 33 / 52

Page 34: Extending Python, what is the best option for me?

CTypes

In [3]: %timeit fib.ctypes_fib (20)

10000 loops , best of 3: 63.8 micro s per loop

In [4]: %timeit fib.py_fib (20)

100 loops , best of 3: 3.62 ms per loop

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 34 / 52

Page 35: Extending Python, what is the best option for me?

CTypesFortran example

Use of existing fortran code

Take random code at github

https://github.com/astrofrog/fortranlib

Wrap using ctypes

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 35 / 52

Page 36: Extending Python, what is the best option for me?

CTypesFortran example

real(dp) function mean_dp(x, mask)

implicit none

real(dp),intent(in) :: x(:)

logical ,intent(in),optional :: mask (:)

if(present(mask)) then

mean_dp = sum(x, mask=mask)/size(x)

else

mean_dp = sum(x)/size(x)

end if

end function mean_dp

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 36 / 52

Page 37: Extending Python, what is the best option for me?

CTypesFortran example

gfortran -fPIC -shared statistic.f90 -o lib_statistics.so

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 37 / 52

Page 38: Extending Python, what is the best option for me?

CTypesFortran example

bin git:( master) -> nm -g lib_statistics.so

001eab T ___lib_statistics_MOD_clipped_mean_dp

000afc T ___lib_statistics_MOD_clipped_mean_sp

00306c T ___lib_statistics_MOD_mean_dp

001c55 T ___lib_statistics_MOD_mean_sp

002db0 T ___lib_statistics_MOD_median_dp

0019b0 T ___lib_statistics_MOD_median_sp

002544 T ___lib_statistics_MOD_quantile_dp

00115a T ___lib_statistics_MOD_quantile_sp

002299 T ___lib_statistics_MOD_variance_dp

000ec3 T ___lib_statistics_MOD_variance_sp

U __gfortran_arandom_r4

U __gfortran_arandom_r8

U __gfortran_os_error

U __gfortran_pack

U __gfortran_pow_i4_i4

U __gfortran_runtime_error

U __gfortran_runtime_error_at

U __gfortran_st_write

U __gfortran_st_write_done

U __gfortran_stop_string

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 38 / 52

Page 39: Extending Python, what is the best option for me?

CTypesFortran example

from ctypes import *

# Statistics fortran lib

st_lib = CDLL(’lib_statistics.so’)

mean = st_lib.__lib_statistics_MOD_mean_dp

mean.argtypes = [POINTER(c_float *2)]

mean.restype = c_float

vals = (c_float *2)(2.0 , 3.0)

print mean(vals)

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 39 / 52

Page 40: Extending Python, what is the best option for me?

CTypesCTypes source

cpython/Modules/ ctypes/callproc.c

static PyObject *py_dl_open(PyObject *self , PyObject *args)

{

char *name;

void * handle;

#ifdef RTLD_LOCAL

int mode = RTLD_NOW | RTLD_LOCAL;

#else

/* cygwin doesn ’t define RTLD_LOCAL */

int mode = RTLD_NOW;

#endif

if (! PyArg_ParseTuple(args , "z|i:dlopen", &name , &mode))

return NULL;

mode |= RTLD_NOW;

handle = ctypes_dlopen(name , mode);

.

return PyLong_FromVoidPtr(handle );

}Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 40 / 52

Page 41: Extending Python, what is the best option for me?

CFFI

Advanced FFI for Python

Allows call functions from Shared libs

Create, access, manipulate C data types

Both API and ABI access

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 41 / 52

Page 42: Extending Python, what is the best option for me?

CFFI

Mostly the same as CTypes

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 42 / 52

Page 43: Extending Python, what is the best option for me?

CFFI

Recommended way to extend PyPy

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 43 / 52

Page 44: Extending Python, what is the best option for me?

CFFIABI

from cffi import FFI

ffi = FFI()

ffi.cdef(""" int printf(const char *format , ...); """)

C = ffi.dlopen(None)

arg = ffi.new("char[]", "world")

C.printf("hi there , %s!\n", arg)

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 44 / 52

Page 45: Extending Python, what is the best option for me?

CFFIABI- Fibonacci

from cffi import FFI

ffi = FFI()

ffi.cdef(""" int fib(int n);""")

libfib = ffi.dlopen(’libfib.so’)

libfib.fib (10)

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 45 / 52

Page 46: Extending Python, what is the best option for me?

CFFIAPI Level

import cffi

ffi = cffi.FFI()

ffi.cdef(""" int fib(int n);""")

lib = ffi.verify(r’’’

int fib(int n){

if ( n < 2 )

return n;

else

return fib(n-1) + fib(n-2);

}’’’)

print lib.fib (10)

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 46 / 52

Page 47: Extending Python, what is the best option for me?

CFFIAPI Level

import cffi

ffi = cffi.FFI()

ffi.cdef(""" int fib(int n);""")

lib = ffi.verify(r’’’

int fib(int n){

if ( n < 2 )

return n;

else

return fib(n-1) + fib(n-2);

}’’’)

print lib.fib(’asd’)

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 47 / 52

Page 48: Extending Python, what is the best option for me?

CFFIAPI Level

Traceback (most recent call last):

File "fib.py", line 16, in <module >

print lib.fib("asd")

TypeError: an integer is required

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 48 / 52

Page 49: Extending Python, what is the best option for me?

CFFIAPI Level

from cffi import FFI

ffi = FFI()

ffi.cdef(""" typedef struct { float x, y; } point;""")

point = ffi.new("point *")

point.x = 2.0

point.y = 3.0

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 49 / 52

Page 50: Extending Python, what is the best option for me?

CFFIInternals

cffi/c/ cffi backend.c

static PyObject *

b_load_library(PyObject *self , PyObject *args)

{

void *handle;

DynLibObject *dlobj;

if ((flags & (RTLD_NOW | RTLD_LAZY )) == 0)

flags |= RTLD_NOW;

printable_filename = filename_or_null ? filename_or_null : "<None >";

handle = dlopen(filename_or_null , flags );

dlobj = PyObject_New(DynLibObject , &dl_type );

dlobj ->dl_handle = handle;

dlobj ->dl_name = strdup(printable_filename );

return (PyObject *)dlobj;

}Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 50 / 52

Page 51: Extending Python, what is the best option for me?

Conclusions

Three different ways

Same principles

Less portable - More portable

Harder - Easier

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 51 / 52

Page 52: Extending Python, what is the best option for me?

Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 52 / 52