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
(
)
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 (“built-
in”)
• 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)
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 (
).
• 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 (
).
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 (
)
• 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
(
)
• 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) (
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) ! 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