JĘZYK PYTHON - NARZĘDZIE DLA KAŻDEGO NAUKOWCA Marcin Lewandowski [ mlew@ippt.gov.pl ]
ROZSZERZANIE I OSADZANIE PYTHONA (PYTHON EXTENDING & EMBEDDING) 2
Łączenie i Osadzanie Pythona Łączenie Pythona (Extending) Tworzenie wrapperów do bibliotek w C/C++ Osadzanie Pythona (Embedding) Wywoływanie Pythona z programu C/C++ 3
PO CO? Budowa skryptowalnych aplikacji C/C++ nie tylko bibliotekę C/C++, ale całą aplikację można zamienid na moduł Pythona. Zastępując main() programu C/C++ przez interpreter Pythona uzyskujemy aplikację kontrolowaną skryptem Pythona! To pozwala łatwą modyfikację aplikacji (bez konieczności jej rekompilacji). Szybkie prototypowanie i debugowanie biblioteka/aplikacja C/C++ pod kontrolą Pythona może byd łatwo testowana i debugowana. Integracja systemów/oprogramowania z pomocą Pythona i modułów rozszerzeo można łatwo sklejad aplikacje i biblioteki stworzone w różnych językach (nie tylko C/C++). Niezależne elementy aplikacje i biblioteki można integrowad w jedną nową aplikację. 4
Zagadnienia łączenia różnych języków programowania typy danych przekazywanie argumentów funkcji tworzenie nowych zmiennych zarządzanie pamięcią obsługa wyjątków 5
LINKI Extending and Embedding the Python Interpreter dokumentacja Pythona (http://docs.python.org/extending/) Python/C API interfejs C do Pythona (http://docs.python.org/c-api/) 6
ROZSZERZANIE PYTHONA 7
Kompilacja rozszerzenia do Pythona Rozszerzenie (moduł C) można łączyd z Pythonem przez: Ładowanie dynamiczne Rozszerzenie jest kompilowane do biblioteki współdzielonej (DLL). Polecenie import ładuje i inicjalizuje moduł w locie Łatwiejsza kompilacja i stosowane częściej niż łączenie statyczne Łączenie statyczne Rozszerzenie jest wkompilowane bezpośrednio w interpreter Pythona i staje się nowym modułem wbudowanym ( builtin ) Polecenie import tylko inicjalizuje moduł 8
Python/C API (plik nagłówkowy Python.h) The Very High Level Layer Reference Counting Exception Handling Utilities Operating System Utilities System Functions Process Control Importing Modules Data marshalling support Parsing arguments and building values String conversion and formatting Reflection Abstract Objects Layer Object Protocol Number Protocol Sequence Protocol Mapping Protocol Iterator Protocol Old Buffer Protocol Concrete Objects Layer Fundamental Objects Numeric Objects Sequence Objects Mapping Objects Other Objects Initialization, Finalization, and Threads Thread State and the Global Interpreter Lock Profiling and Tracing Advanced Debugger Support Memory Management Object Implementation Support Allocating Objects on the Heap Common Object Structures Type Objects Number Object Structures Mapping Object Structures Sequence Object Structures Buffer Object Structures Supporting Cyclic Garbage Collection 9
Funkcja opakowująca (wrapper) Argumenty z Pythona Konwersja argumentów na typy C Weryfikacja poprawności argumentów Funkcja Wykonanie funkcji na przekazanych argumentach Rezultaty do Pythona Konwersja rezultatów funkcji do Pythona Ustawienie Ref Count zwracanych obiektów 10
Konwersja typów C->Python PyObject *Py_BuildValue(char *format,...) Tworzy obiekt Pythona z listy zmiennych C na podstawie stringu określającego format (a la printf()) Typ PyObject jest uniwersalnym typem służącym do wymiany zmiennych/obiektów pomiędzy C i Pythonem 11
Konwersja typów Python->C int PyArg_ParseTuple(PyObject *args, char *format,...) Służy do konwersji zmiennych/obiektów Pythona do C na podstawie stringu określającego format (a la scanf()) PyObject *args jest obiektem interpretera Python zawierającym argumenty przekazane do funkcji int PyArg_ParseTupleAndKeywords(PyObject *args, PyObject *kw, const char *format, char *keywords[],...) j.w. ale z opcją interpretacji argumentów nazwanych (keyword) do funkcji 12
Tworzenie nowych zmiennych/obiektów Pythonowych Funkcja tworząca (zwraca obiekt typu PyObject* do Python/C) Py_BuildValue(" ) Wartośd Pythonowa None Py_BuildValue("i", 123) 123 Py_BuildValue("iii", 123, 456, 789) (123, 456, 789) Py_BuildValue("s", "hello") Py_BuildValue("ss", "hello", "world") Py_BuildValue("s#", "hello", 4) Py_BuildValue("()") () 'hello' ('hello', 'world') hell Py_BuildValue("(ii)", 123, 456) (123, 456) Py_BuildValue("[i,i]", 123, 456) [123, 456] Py_BuildValue("{s:i,s:i}", "abc", 123, "def", 456) {'abc': 123, 'def': 456} Py_BuildValue("((ii)(ii)) (ii)", 1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6)) 13
Mechanizm zarządzenia pamięcią Python/C Każdy obiekt/zmienna Pythona ma licznik odwołao (referencji) zwiększany przy każdym przypisaniu Gdy licznik jest równy zero obiekt jest automatycznie usuwany z pamięci (tzw. Automatic Garbage Collection) W kodzie C/C++ trzeba SAMEMU aktualizowad licznik! Zwiększenie licznika: void Py_INCREF(PyObject *o) Zmniejszanie licznika: void Py_DECREF(PyObject *o) Zwracanie nowo utworzonego w C obiektu do Pythona: res = Py_BuildValue("i", 1234); Py_INCREF(res); return res; 14
Reguły własności obiektów Python Każde wywołanie Py_INCREF() powinno ostatecznie byd sparowane z wywołaniem Py_DECREF() Py_INCREF() służy do zabezpieczenia życia obiektu na czas jego używania przez funkcję; czasami korzysta się z pożyczonych referencji Większośd (ALE NIE WSZYSTKIE!) funkcji Python/C API wykonuje INCREF na zwracanych/tworzonych obiektach; w takim wypadku funkcja wywołująca jest odpowiedzialna za wykonanie Py_DECREF() Nie ma konieczności INCREF dla każdej zmiennej lokalnej O ILE nie ma szansy, że w tym czasie ktoś inny wykona DECREF Większośd funkcji zakłada, że przekazane argumenty (obiekty) są już zabezpieczone, więc nie wykonuje dla nich INCREF WYJĄTKI: PyTuple_SetItem() i PyList_SetItem() przejmują z założenia argumenty niezabezpieczone PyTuple_GetItem() nie wykonuje INCREF na zwracanych obiektach i jeszcze inne patrz dokumentacja!!! 15
Obsługa i raportowanie błędów Moduły rozszerzeo zwracają błąd do Pythona przez zwrot wartości NULL Dodatkowo przed powrotem do interpretera funkcja C powinna ustawid typ błędu np.: void PyErr_NoMemory() wyjątek typu MemoryError void PyErr_SetFromErrno(PyObject *exc) wyjątek z błędem ustalonym na podstawie wartości errno biblioteki CRT void PyErr_SetObject(PyObject *exc, PyObject *val) wyjątek typu exc wraz z obiektem obiekt/wartością wyjątku val void PyErr_SetString(PyObject *exc, char *msg) wyjątek typu exc wraz ze stringiem msg komunikatu błędu 16
Szkielet modułu rozszerzającego #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 SpamMethods[] = { {"system", spam_system, METH_VARARGS, "Execute a shell command."}, {NULL, NULL, 0, NULL} /* Sentinel */ }; PyMODINIT_FUNC initspam(void) { (void) Py_InitModule("spam", SpamMethods); } 17
Konwencja C Nazywanie modułów C nazwą z podkreśleniem (np. _module.pyd/_module.so) Obudowanie modułu C modułem Pythona: # module.py from _module import * # Dodatkowy kod wspierający poniżej... 18
Ręczna kompilacja modułu C Niestety polecenia kompilacji są nieco różna dla różnych systemów operacyjnych np. pod LINUX: > gcc -fpic -c -I/usr/local/include/python \ -I/usr/local/lib/python/config \ example.c wrapper.c > gcc -shared example.o wrapper.o -o examplemodule.so 19
Kompilacja rozszerzeo za pomocą modułu distutils Plik setup.py zawiera: pythonowy moduł obudowujący example.py oraz kod źródłowy C: pyexample.c, example.c: # setup.py from distutils.core import setup, Extension setup(name="example", version="1.0", py_modules = ['example.py'], ext_modules = [ Extension("_example", ["pyexample.c","example.c"]) ] ) Kompilacja modułu za pomocą polecenia: % python setup.py build % python setup.py install 20
Generatory interfejsów SWIG (Simplified Wrapper Interface Generator) automatyczny generator wrapper ów C/C++ dla języków dynamicznych Guile, Java, Mzscheme, Ocaml, Perl, Pike, PHP, Python, Ruby, and Tcl (www.swig.org). SIP generator interfejsów Python/C++; koncepcyjnie podobny do SWIG; używany do budowy interfejsów PyQt i PyKDE. F2PY generator interfejsów Fortran/C/Python; moduły C są generowane na podstawie plików sygnatur (.pyf) z kodu Fortran/C (http://www.scipy.org/f2py). 21
Biblioteki Python/C/C++ PyCXX obiektowa biblioteka do budowy rozszerzeo do Pythona w C++ (http://sourceforge.net/projects/cxx) Boost.Python elegancka biblioteka obiektowa zapewniająca integrację klas, funkcji i obiektów C++ i Pythona (www.boost.org) ctypes moduł Pythona umożliwia bezpośrednie wołanie funkcji w bibliotekach dynamicznych; zapewnia obsługę typów danych C oraz konwencję wywołao. 22
Inne rozwiązania Weave element pakietu SciPy pozwala na umieszczanie kodu C/C++ bezpośrednio w kodzie Pythona w celu jego akceleracji (http://www.scipy.org/weave) Pyrex jest Pythonem z typami danych języka C (cdef); umożliwia kompilację takiego kodu do modułu C (coś jak Python2C) Psyco jest kompilatorem just-in-time (JIT) dla Pythona, co przyśpiesza wykonywanie 2-100x (typowo 4x) (http://psyco.sourceforge.net) 23
SWIG SWIG jest generatorem interfejsów do kodu C/C++ z języków skryptowych (Perl, Python, Ruby, Tcl, ). Interfejsy są generowane na podstawie deklaracji w kodzie C/C++ (np. pliku nagłówkowego) lub pliku interfejsu Szczegóły na 690 stronach dokumentacji 24
SWIG działanie 25
SWIG wykonanie 26
SWIG kod w C /* File : example.c */ #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(<ime); return ctime(<ime); } http://www.swig.org/tutorial.html 27
SWIG plik interfejsu /* example.i */ %module 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 double My_variable; extern int fact(int n); extern int my_mod(int x, int y); extern char *get_time(); 28
SWIG kompilacja i uruchomienie % swig -python example.i % gcc -c example.c example_wrap.c \ -I/usr/local/include/python2.1 % ld -shared example.o example_wrap.o -o _example.so % python >>> import example >>> example.fact(5) 120 >>> example.my_mod(7,3) 1 >>> example.get_time() 'Sun Feb 11 23:01:07 1996 29
F2PY dla C funkcja w C /* File foo.c */ void foo(int n, double *x, double *y) { int i; for (i=0;i<n;i++) { y[i] = x[i] + i; } } http://www.scipy.org/cookbook/f2py_and_numpy 30
F2PY dla C plik sygnatury! File m.pyf python module m interface subroutine foo(n,x,y) intent(c) foo! foo is a C function intent(c)! all foo arguments are! considered as C based integer intent(hide), depend(x) :: n=len(x)! n is the length! of input array x double precision intent(in) :: x(n)! x is input array! (or arbitrary sequence) double precision intent(out) :: y(n) end subroutine foo end interface end python module m! y is output array,! see code in foo.c 31
F2PY dla C plik setup.py # File setup.py def configuration(parent_package='',top_path=none): from numpy.distutils.misc_util import Configuration config = Configuration('',parent_package,top_path) config.add_extension('m', sources = ['m.pyf','foo.c']) return config if name == " main ": from numpy.distutils.core import setup setup(**configuration(top_path='').todict()) 32
F2PY dla C kompilacja i uruchomienie Kompilacja w wyniku której powstaje moduł C (m.so/m.pyd) : > f2py m.pyf foo.c -c >>> import m >>> print m. doc This module 'm' is auto-generated with f2py (version:2_2130). Functions: y = foo(x). >>> print m.foo. doc foo - Function signature: y = foo(x) Required arguments: x : input rank-1 array('d') with bounds (n) Return objects: y : rank-1 array('d') with bounds (n) >>> print m.foo([1,2,3,4,5]) [ 1. 3. 5. 7. 9.] 33
ctypes Moduł Pythona umożliwia bezpośrednie wołanie funkcji w bibliotekach dynamicznych Zapewnia obsługę typów danych C oraz konwencję wywołao. Dokumentacja: ctypes A foreign function library for Python (dokumentacja Pythona) http://starship.python.net/crew/theller/ctypes/tu torial.html 34
ctypes typy danych I Typ ctypes Typ C Typ Python c_char char 1-character string c_wchar wchar_t c_byte char int/long c_ubyte unsigned char int/long c_short short int/long c_ushort unsigned short int/long c_int int int/long c_uint unsigned int int/long c_long long int/long c_ulong unsigned long int/long 1-character unicode string 35
ctypes typy danych II Typ ctypes Typ C Typ Python c_longlong int64 or long long int/long c_ulonglong unsigned int64 or unsigned long long int/long c_float float float c_double double float c_longdouble long double float c_char_p char * (NUL terminated) string or None c_wchar_p wchar_t * (NUL terminated) unicode or None c_void_p void * int/long or None 36
ctypes ładowanie biblioteki # WINDOWS >>> from ctypes import * >>> print windll.kernel32 <WinDLL 'kernel32', handle... at...> >>> print cdll.msvcrt <CDLL 'msvcrt', handle... at...> >>> libc = cdll.msvcrt # LINUX >>> cdll.loadlibrary("libc.so.6") <CDLL 'libc.so.6', handle... at...> >>> libc = CDLL("libc.so.6") >>> libc <CDLL 'libc.so.6', handle... at...> 37
ctypes wywołanie funkcji # EX1 from ctypes import * libname = 'libc.so' # on a UNIX-based system libname = 'msvcrt.dll' # on Windows libc = CDLL(libName) libc.printf("hello, World!\n") # EX2 from ctypes import c_int, WINFUNCTYPE, windll from ctypes.wintypes import HWND, LPCSTR, UINT prototype = WINFUNCTYPE(c_int, HWND, LPCSTR, LPCSTR, UINT) paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", None), (1, "flags", 0) MessageBox = prototype(("messageboxa", windll.user32), paramflags) 38
OSADZANIE PYTHONA 39
Osadzanie template #include <Python.h> int main(int argc, char **argv) { Py_Initialize(); PyRun_SimpleString("print('Hello!')"); Py_Finalize(); return 0; } 40
Osadzanie funkcje Wykonanie z łaocucha znaków: Py_Initialize(); PyRun_SimpleString("i = 2"); PyRun_SimpleString("i = i*innprint i"); Py_Finalize(); Wykonanie z pliku: Py_Initialize(); FILE * f = fopen("test.py", "r"); PyRun_SimpleFile(f, "test.py"); Py_Finalize(); 41
Dostęp do Pythona z C Importowanie modułów Pythona (emulacja polecenia import) Dostęp do obiektów zdefiniowanych w modułach Wywoływanie funkcji Python (funkcje w klasach, modułach) Dostęp do zmiennych/atrybutów obiektów Konwersja typów Python/C Zarządzanie pamięcią 42
Wywołanie funkcji w module PyObject *pname, *pmodule, *pargs, *pfunc, *pvalue; # Import modułu Python Py_Initialize(); pname = PyString_FromString("mymod"); pmodule = PyImport_Import(pName); # Pobranie funkcji z modułu pfunc = PyObject_GetAttrString(pModule, "foo"); # Wywołanie funkcji pvalue = PyObject_CallObject(pFunc, pargs); 43