JĘZYK PYTHON - NARZĘDZIE DLA KAŻDEGO NAUKOWCA Marcin Lewandowski [ [email protected] ] 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 – – – – – • Initialization, Finalization, and Threads – – – • • Fundamental Objects Numeric Objects Sequence Objects Mapping Objects Other Objects 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 Funkcja Rezultaty do Pythona • Konwersja argumentów na typy C • Weryfikacja poprawności argumentów • Wykonanie funkcji na przekazanych argumentach • 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) Wartośd Pythonowa Py_BuildValue("“) None Py_BuildValue("i", 123) 123 Py_BuildValue("iii", 123, 456, 789) (123, 456, 789) Py_BuildValue("s", "hello") 'hello' Py_BuildValue("ss", "hello", "world") ('hello', 'world') Py_BuildValue("s#", "hello", 4) ‘hell’ Py_BuildValue("()") () 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(&ltime); return ctime(&ltime); } 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 extern extern extern double My_variable; int fact(int n); int my_mod(int x, int y); 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 intent(c) ! ! ! integer intent(hide), depend(x) foo is a C function all foo arguments are considered as C based :: 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) ! y is output array, ! see code in foo.c end subroutine foo end interface end python module m 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 1-character unicode string 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 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