Werkzeuge zur Erweiterung von CPython Authors: Date: Version: Berthold Höllmann 2009-09-06 13:33:01 +0200 (So, 06 Sep 2009) 23 Inhalt 1 Einleitung 2 2 Libraries/Utilities: Einbinden vorhandener Bibliotheken 2 3 Libraries/Utilities: Entwicklung eigenes Codes 3 4 ctypes 3 5 ctypes: Beispiel 3 6 numpy/f2py 3 7 numpy/f2py: Beispiel 4 8 numpy/f2py: Beispiel (2) 4 9 SWIG 4 10 SWIG: Beispiel 4 11 SWIG: Beispiel (2) 5 12 SWIG: Beispiel (3) 5 13 SWIG: Beispiel (4) 5 14 sip 6 15 sip: Beispiel 6 16 sip: Beispiel (2) 7 17 sip: Beispiel (3) 7 18 sip: Beispiel (4) 7 19 Handgeschriebene Erweiterungen 7 20 Handgeschriebene Erweiterungen: Beispiel 8 1 21 Handgeschriebene Erweiterungen: Beispiel 8 22 CXX 8 23 CXX: Beispiel 8 24 CXX: Beispiel (2) 9 25 pyrex 9 26 pyrex: Beispiel 9 27 pyrex: Beispiel (2) 10 28 pyrex: Beispiel (3) 10 29 weave 10 30 weave: Beispiel 10 31 Boost.Python 11 32 Boost.Python: Beispiel 11 33 Boost.Python: Beispiel (2) 11 34 distutils 11 35 Diskussion 12 1 Einleitung Oft ist es aus verschieden Gründen nötig oder wünschenswert, Code aus anderen Programmiersprachen in CPython einzubinden. Oft ist dieser Code in C oder C++ geschrieben, bei wissenschaftlichen Anwendungen in FORTRAN. Die Gründe können u.a. sein • Beschleunigung von Anwendungen mit eigenem Code, • Beschleunigung von Anwendungen mit fremdem Code, • Zugriff auf spezielle Bibliotheken. 2 Libraries/Utilities: Einbinden vorhandener Bibliotheken Natürlich immer auch zum Entwickeln eigener Bibliotheken. • ctypes1 (Shared Libraries, Standard CPython Library seit 2.5) • numpy/f2py2 (FORTRAN/C) • SWIG3 (C/C++) • sip4 (C++) (Entwickelt für PyQt) • Handgeschriebene Erweiterung (Python/C API Reference Manual5, Extending and Embedding the Python Interpreter6) (C) ( aktuelles“ Beispiel: pysqlite7) ” 2 3 Libraries/Utilities: Entwicklung eigenes Codes Natürlich immer auch zum Einbinden von vorhandenem Code. 8 • CXX (C++) • pyrex9 (C) (Oder zum Wrappen vorhandenen Codes: pytables10) • weave11 (C/C++) (Einbetten von C Code in Python Code, Bestandteil von SciPy12) • Boost.Python13 (C++) 4 ctypes • Einbinden von dynamischen Bibliotheken. • Kein Compiler benötigt, Einbindung aus Python heraus • PyOpenGL wird auf ctypes umgestellt 5 ctypes: Beispiel >>> from ctypes import * >>> cdll.LoadLibrary("libc.so.6") #doctest: +ELLIPSIS <CDLL ’libc.so.6’, handle ... at ...> >>> libc = CDLL("libc.so.6") >>> libc.printf #doctest: +ELLIPSIS <_FuncPtr object at 0x...> >>> print libc.time(None) #doctest: +SKIP 1179046382 >>> res = libc.printf( ... "dies %d ist %s ein %f Test\n", ... 3, "kleiner", c_double(4.5)) dies 3 ist kleiner ein 4.500000 Test 6 numpy/f2py • Einbinden von FORTRAN und (auch) C Code 12 • Wird in SciPy benutzt um BLAS und Lapack Code einzubinden • Ggf. wird FORTRAN Compiler benötigt • Code Generator • Eingebautes Build System • Arbeitet Wahlweise direkt mit dem, durch spezielle Kommentare angereicherten, Quellcode oder einer Datei zur Beschreibung des Interfaces (.pyf). 3 7 numpy/f2py: Beispiel tst.f : SUBROUTINE addit(out, in_a, in_b) REAL*8 out INTEGER in_a REAL*8 in_b Cf2py intent(out) out Cf2py intent(in) in_a Cf2py intent(in) in_b out = in_a + in_b RETURN END 8 numpy/f2py: Beispiel (2) > f2py -c addit.f -m addit >>> import addit >>> print dir(addit) [’__doc__’, ’__file__’, ’__name__’, ’__package__’, ’__version__’, ’addit’] >>> print addit.addit.__doc__ #doctest: +NORMALIZE_WHITESPACE addit - Function signature: out = addit(in_a,in_b) Required arguments: in_a : input int in_b : input float Return objects: out : float <BLANKLINE> >>> addit.addit(2, 3.) 5.0 9 SWIG • Unterstützt eine Auswahl an verschiedenen Ziel (aufrufenden) Sprachen (von der SWIG3 Website): Python, Tcl, Perl, Guile, Java, Ruby, Mzscheme, PHP, Objective Caml (Ocaml), Pike, C#, the Chicken scheme compiler, Allegro CL, Modula-3, Lua, CLISP, Common Lisp with UFFI, Common Lisp with CFFI, und R • Code Generator • Benutzt von PyOpenGL2 • Interface ändert sich häufig und zwischen patch releases. (PyOpenGL2 2.0.1 festgelegt auf Version 1.3.13, 2.0.2 auf 1.3.23) 10 SWIG: Beispiel Aus Tutorial von Website (SWIG3) swig example.c: 4 #include <time.h> double My_variable = 3.0; int fact(int n) { if (n <= 1) return 1; else return n*fact(n-1); } int my_mod(int x, int y) { return (x%y); } char *get_time() { time_t ltime; time(&ltime); return ctime(&ltime); } 11 SWIG: Beispiel (2) swig example.i: /* example.i */ %module swig_example %{ /* Put header files here or function declarations like below */ extern double My_variable; extern int fact(int n); extern int my_mod(int x, int y); extern char *get_time(); %} extern extern extern extern 12 double My_variable; int fact(int n); int my_mod(int x, int y); char *get_time(); SWIG: Beispiel (3) > swig -python swig_example.i > gcc -c swig_example.c swig_example_wrap.c \ -I/usr/local/include/python2.5 > ld -shared swig_example.o swig_example_wrap.o \ -o _swig_example.so > ls *swig_example* _swig_example.so* swig_example.i swig_example.py swig_example_wrap.o swig_example.c swig_example.o swig_example_wrap.c 13 SWIG: Beispiel (4) >>> import swig_example >>> swig_example.fact(5) 120 >>> swig_example.my_mod(7,3) 1 5 >>> swig_example.get_time() ’Sun May 13 14:33:41 2007\n’ 14 #doctest: +SKIP sip • Einbinden von C++ Klassen in Python • Code Generator • Entwickelt für PyQt und PyKDE 15 sip: Beispiel sip example.h: // Define the interface to the word library. #include <string> class Word { const std::string the_word; public: Word(const char *w); char *reverse() const; }; sip_example.cpp im Handout sip example.cpp: // Define the word library. #include <cstring> #include <cstdlib> #include <iostream> #include "sip_example.h" Word::Word(const char *w) : the_word(w) { }; char *Word::reverse() const { size_t wlen, i; char *res; wlen = the_word.length(); res = (char*)calloc(wlen+1, sizeof(char)); for (i=0; i<wlen; i++) { res[wlen-i-1] = the_word[i]; } return res; }; 6 16 sip: Beispiel (2) sip example.sip: // Define the SIP wrapper to the word library. %Module sip_example 0 class Word { %TypeHeaderCode #include "sip_example.h" %End public: Word(const char *w); char *reverse() const; }; 17 sip: Beispiel (3) > g++ -c -o sip_example.o sip_example.cpp > sip -c . sip_example.sip > g++ -c -I/usr/local/include/python2.5 \ sipsip_examplecmodule.cpp > g++ -c -I/usr/local/include/python2.5 \ sipsip_exampleWord.cpp > g++ -shared sip_example.o \ sipsip_examplecmodule.o \ sipsip_exampleWord.o \ -o sip_example.so > ls *sip_e* sipAPIsip_example.h sip_example.cpp sip_example.h sip_example.sip 18 sip_example.so* sipsip_exampleWord.cpp sipsip_exampleWord.h sipsip_exampleWord.o sipsip_examplecmodule.cpp sipsip_examplecmodule.o sip: Beispiel (4) >>> import sip_example >>> w = sip_example.Word( ... "Dies ist ein Test"[::-1]) >>> print w #doctest: +ELLIPSIS <sip_example.Word object at 0x...> >>> print dir(w) #doctest: +ELLIPSIS [... ’reverse’] >>> w.reverse() ’Dies ist ein Test’ 19 Handgeschriebene Erweiterungen • Funktioniert mit jedem CPython port 7 • Entwickler verantwortlich für Reference counting, korrekte Übergabeparameter, Aufbau der Rückgabeparameter. • Code in C • Wird von allen Code Generatoren und Einbindungs Bibliotheken verwendet. 20 Handgeschriebene Erweiterungen: Beispiel manual example.c: (aus Python Docs) #include "Python.h" static PyObject * spam_system(PyObject *self, PyObject *args) { const char *command; int sts; if (!PyArg_ParseTuple(args, "s", &command)) return NULL; sts = system(command); return Py_BuildValue("i", sts); } static PyMethodDef ManualMethods[] = { {"system", spam_system, METH_VARARGS, "Execute a shell command."}, {NULL, NULL, 0, NULL}}; PyMODINIT_FUNC initmanual_example(void) { PyObject *m; m = Py_InitModule("manual_example", ManualMethods);} 21 Handgeschriebene Erweiterungen: Beispiel >>> import manual_example >>> status = manual_example.system("ls -l") 22 CXX • C++ Klassen zur Einbindung von C++ Code in Python. • Definiert z.B. C++ Klassen für Python Standard Typen. • Bietet Numeric Interface (NumPy) • C++ Ausnahmen werden zu Python Exceptions • Mit Standard distutils zu übersetzen, benötigt jedoch zusätzlich Quelldateien für Hilfsfunktionen. 23 CXX: Beispiel cxx example.cxx: class example_module : public ExtensionModule<example_module> { public: 8 example_module() : ExtensionModule<example_module>( "cxx_example" ) { add_varargs_method("sum", &example_module::ex_sum, "sum(arglist) = sum of arguments"); add_varargs_method("test", &example_module::ex_test, "test(arglist) runs a test suite"); initialize("documentation for the example module" ); } virtual ~example_module() {} private: Object ex_sum(const Tuple &a); Object ex_test(const Tuple &a);}; extern "C" void initcxx_example() { static example_module* example = new example_module; } 24 CXX: Beispiel (2) >>> import cxx_example >>> dir(cxx_example) [’__doc__’, ’__file__’, ’__name__’, ’__package__’, ’sum’, ’test’] >>> cxx_example.test() Module test ok. >>> print cxx_example.sum(1, 2, 3) 6.0 >>> print cxx_example.sum(*range(10)) 45.0 25 pyrex • Python ähnliche Sprache zum Erstellen von Python Erweiterungen • Zum Einbinden von C Code werden die .h Dateien in Pyrex neu geschrieben. • Vermittelt die Unterschiede im C und Python Typen System. 26 pyrex: Beispiel pyrex example.pyx: def primes(int kmax): cdef int n, k, i cdef int p[1000] result = [] if kmax > 1000: kmax = 1000 k = 0 n = 2 while k < kmax: i = 0 while i < k and n % p[i] <> 0: i = i + 1 if i == k: p[k] = n 9 k = k + 1 result.append(n) n = n + 1 return result 27 pyrex: Beispiel (2) pyrex example.pyx: cdef class Spam: cdef int amount def __new__(self): self.amount = 0 def get_amount(self): return self.amount def set_amount(self, new_amount): self.amount = new_amount def describe(self): print self.amount, "tons of spam!" 28 pyrex: Beispiel (3) >>> import pyrex_example >>> dir(pyrex_example) #doctest: +ELLIPSIS [’Spam’, ..., ’primes’] >>> pyrex_example.primes(4) [2, 3, 5, 7] >>> s = pyrex_example.Spam() >>> s.set_amount(33) >>> s.describe() 33 tons of spam! 29 weave • Übersetzen von Ausdrücken oder inline Code • Ergebnisse der Übersetzung werden gecached • Unterstützung für numpy arrays 30 weave: Beispiel >>> from scipy import weave >>> code = r""" ... int i; 10 ... py::tuple results(2); ... for (i=0; i<a.length(); i++) { ... a[i] = i; ... } ... results[0] = 3.0; ... results[1] = 4.0; ... return_val = results; ... """ >>> a = [None]*10 >>> print weave.inline(code, [’a’]) (3.0, 4.0) 31 Boost.Python • Nahtlose Integration von C++ und Python • boost: C++ Bibliothek von Template Fans • Wrapper generatoren unter C++/Python interfacing14 32 Boost.Python: Beispiel boost example.cpp: #include <boost/python.hpp> using namespace boost::python; struct World { void set(std::string msg) { this->msg = msg; } std::string greet() { return msg; } std::string msg; }; BOOST_PYTHON_MODULE(boost_example) { class_<World>("World") .def("greet", &World::greet) .def("set", &World::set); } 33 Boost.Python: Beispiel (2) >> import boost_example >> dir(boost_example) [’World’, ’__doc__’, ’__file__’, ’__name__’] >> planet = boost_example.World() >> planet.set(’howdy’) >> planet.greet() ’howdy’ 34 distutils 2 numpy/f2py -- Unterstützt von numpy.distutils SWIG3 -- Unterstützt von distutils sip4 -- Eigene Distutils Erweiterung (sipdistutils) 11 8 CXX -- Standard distutils, allerdings müssen Support Dateien gesucht werden. 9 pyrex -- Eigene Distutils Erweiterung (Pyrex.Distutils) 13 Boost.Python -- Ersteller bevorzugen bjam. Standard distutils möglich, allerdings müssen Header Dateien und Bibliotheken gesucht werden. Links 35 Diskussion Fragen / Anmerkungen? Hamburger Python User Group • 2007/05/16 View document source. Generated on: 2009-09-28 13:14 UTC. Generated by Docutils from reStructuredText source. 1 http://docs.python.org/lib/module-ctypes.html 2 http://cens.ioc.ee/projects/f2py2e/ 3 http://swig.sourceforge.net/ 4 http://www.riverbankcomputing.co.uk/sip/index.php 5 http://docs.python.org/api/api.html 6 http://docs.python.org/ext/ext.html 7 http://www.pysqlite.org 8 http://cxx.sourceforge.net/ 9 http://www.cosc.canterbury.ac.nz/greg.ewing/python/Pyrex/ 10 http://www.pytables.org/ 11 http://scipy.org/Weave 12 http://scipy.org/ 13 http://boost.org/libs/python/doc/index.html 14 http://www.language-binding.net/ 12