R-17-t.doc

(446 KB) Pobierz
Szablon dla tlumaczy

17. Wbudowywanie i rozszerzanie Pythona przy pomocy C/C++

Python zawiera liczne modułów, dzięki którym jest udostępniona ogromna różnorodność jego cech. Wiele niezależnych modułów jest także dostępnych, w tym zapewniających obsługę dla CORBA, XML i szerokiego zestawu funkcji serwera WWW. Może się zdarzyć, że Python i jego moduły mogą nie sprostać naszym wymaganiom, albo ponieważ niezbędna funkcja nie jest realizowana, albo też wydajność skryptu Pythona jest niezadowalająca.

Te dwa problemy mogą być jednocześnie rozwiązane poprzez utworzenie modułu „rozszerzenia” w C/C++, który można wywołać ze skryptu Pythona. W ten sposób dosłownie dokonujemy rozszerzenia Pythona, nadając programom dostęp do nowych możliwości i typów obiektów, które nie są włączone w jego podstawowym środowisku. Niekiedy rozszerzenie polega na poprawieniu wydajności istniejących funkcji. Co więcej, moduły mogą być użyte do zdefiniowania nowych typów obiektów, jak też i nowych funkcji.

Python udostępnia bogaty zestaw interfejsów programowania aplikacji (API), które ułatwiają opracowanie modułów rozszerzenia. Owe interfejsy API pozwalają także programom w C/C++ na wywołanie skryptów Pythona, poprzez wbudowanie interpretera Pythona do istniejącego programu w języku C/C++. Najczęściej programy, które mają wbudowany interpreter Pythona, także rozszerzają Pythona, umożliwiając skryptom na dostęp do funkcji zadeklarowanych w programie macierzystym (ang. host program).

W tym rozdziale, rozszerzymy Pythona poprzez utworzenie kilku modułów rozszerzenia w C/C++. Rozpoczniemy od użycia SWIG (Simplified Wrapper Interface Generator — uproszczony generator interfejsu otoczki) dla opracowania kilku prostych modułów. Utworzymy następnie bardziej zaawansowane moduły rozszerzenia poprzez bezpośrednie użycie interfejsów programowanie Pythona API C. Na zakończenie, wykorzystamy interfejs API do wbudowania Pythona do programu macierzystego w C/C++. Umożliwimy Pythonowi na wywołania zwrotne (ang. callback) do programu macierzystego poprzez przekształcenie programu macierzystego w moduł rozszerzenia.

Rozszerzenie Pythona za pomocą modułu rozszerzenia C/C++

 

Rys.1, str. 588

Opis:

                                 Funkcje wywołania

Biblioteka                                                             Biblioteka

Interpretera                                                            Modułu

Pythona                                                                Rozszerzenia

                                Użyj exportowane C API

                       Program wykonywalny w Pythonie

 

 

 

 

 

 

 

 

 

Typowy scenariusz rozszerzenia jest pokazany powyżej. Program wykonywalny w Pythonie ładuje bibliotekę interpretera Pythona, która z kolei wykonuje skrypt Pythona. W którymś momencie skrypt importuje moduł rozszerzenia. Interpreter Pythona dynamicznie ładuje bibliotekę modułu rozszerzenia oraz inicjalizuje ją. Następnie funkcje i typy obiektów zdefiniowane w module rozszerzenia stają się dostępne dla skryptu Pythona.

Należy pamiętać, że skrypt Pythona nie wie, że zaimportowany moduł jest modułem rozszerzenia, a nie zwykłym modułem Pythona. Z punktu widzenia skryptu nie ma to znaczenia.

Wbudowanie Pythona do programu macierzystego

Biblioteka interpretera Pythona może być również załadowana do programów C/C++, wzbogacając je potężnymi możliwościami skryptowymi Pythona.

 

 

Rys. 2, str. 588.

Opis:

                                 Funkcje wywołania    

Biblioteka                                                             Funkcje modułu

Interpretera                                                           rozszerzenia wbudowane

Pythona                                                              do programu macierzystego

                            Użyj eksportowane C API

                                Program macierzysty

 

 

 

 

 

 

 

 

 

 

 

Powyższy diagram pokazuje typowy scenariusz wbudowania Pythona. W tym przykładzie program macierzysty C/C++ załadował bibliotekę interpretera Pythona. Program macierzysty kieruje wywołanie do interpretera Pythona, używając interfejsu C API Pythona do wykonania skryptów Pythona. Te skrypty Pythona mogą skierować wywołanie zwrotne do programu macierzystego korzystając z modułu rozszerzenia.

W tym przypadku, program macierzysty jest także modułem rozszerzenia, jeśli nawet nie jest on dynamicznie załadowany. Możliwe jest również dynamiczne załadowanie innych modułów rozszerzenia, wtedy kiedy interpreter Pythona jest wbudowany do programu macierzystego C/C++.

Opracowanie modułów rozszerzenia w C/C++

Poniższy plan zarysowuje sposób tworzenia w C/C++ modułu rozszerzenia Pythona:

q       Określić funkcje, struktury i obiekty, które mają być dostępne dla Pythona.

q       Utworzyć funkcje interfejsowe (często funkcje owijające — wrapper functions), które mogą być wywołane przez interpreter Pythona.

q       Skompilować funkcje interfejsowe (moduł owijający).

q       Skonsolidować moduł owijający wraz z biblioteką funkcji docelowych (ang. target function library) i biblioteką Pythona.

q       Przetestować moduł.

Funkcje interfejsowe, zwane otoczkami (ang. wrappers), mogą być napisane ręcznie lub utworzone automatycznie przez SWIG. Zaczniemy od użycia SWIG dla utworzenia kodu otoczki dla kilku funkcji i struktur. W dalszym ciągu, będziemy tworzyć funkcje owijające poprzez bezpośrednie użycie C API Pythona.

Wymagane oprogramowanie narzędziowe

Dla rozszerzenia lub wbudowania Pythona, należy mieć zainstalowany podstawowy interpreter Pythona oraz biblioteki do opracowywania oprogramowania (ang. development libraries). Są one dostępne w formacie RPM lub formacie .tar pod adresem http://www.python.org. Dla opracowywania rozszerzeń przy użyciu SWIG, trzeba pobrać i zainstalować SWIG, który można znaleźć pod adresem http://www.swig.org.

Interpreter Pythona

Pakiet z Pythonem zawiera programy wykonywalne w Pythonie, interpreter Pythona oraz bibliotekę Pythona. Należy sprawdzić, czy te pliki są typowo zainstalowane (dla wersji przynajmniej 1.5.2) w katalogach, odpowiednio: /usr/bin/python, /usr/lib/python1.5 i /usr/lib/python1.5/config.

Biblioteki do opracowywania oprogramowania w Pythonie

Biblioteki Pythona są typowo zlokalizowane w katalogu /usr/lib/python1.5/config, gdzie można znaleźć libpython1.5.a, Makefile.pre.in oraz python.o. Jeśli brak tych plików, to trzeba będzie zainstalować pakiet oprogramowania (ang. development package) Pythona lub pobrać i zainstalować wersję źródłową z witryny WWW Pythona.

SWIG – uproszczony generator interfejsu otoczki

SWIG może utworzyć otoczki dla TCL, Perl i Pythona i działa w systemach UNIX i Windows. Użyjemy go do automatycznego wytworzenia funkcji owijających. Dostępnych jest kilka wersji SWIG — nasze przykłady były zrobione przy użyciu wersji 1.1 (łata Patch 5). Można pobrać najnowszą wersję pod adresem http://www.swig.org.

 

$ swig –version

 

SWIG Version 1.1 (Patch 5)

Copyright (c) 1995-98

University of Utah and the Regents of the University of California

 

Compiled with c++

 

W chwili pisania tego rozdziału, wersje 1.3 swig były nadal zawodne, tak więc powinno się pozostać przy ostatniej stabilnej wersji 1.1.

Rozszerzenie Pythona przy użyciu SWIG

Python udostępnia wszechstronny interfejs programowania C (C API), który jest używany zarówno do rozszerzania, jak i wbudowywania Pythona. Jednakże interfejs jest duży i nie w pełni udokumentowany, co utrudnia jego użycie. Często trzeba udostępnić skryptowi Pythona jedną, czy dwie proste funkcje lub struktury. Jest to , z pozoru, proste zadanie, ale dla osiągnięcia tego celu przy użyciu C API, konieczne jest napisanie obszernego kodu. Z myślą o tego typu prostych zadaniach został zaprojektowany SWIG.

SWIG ułatwia rozszerzenie Pythona — tworzy się prosty plik interfejsowy (ang. file interface), określający funkcje, które mają być dostępne z Pythona. Następnie SWIG automatycznie tworzy kod potrzebny do utworzenia interfejsu pomiędzy Pythonem, a podanymi funkcjami. Dyrektywy SWIG pozwalają na dodatkowy nadzór nad procesem budowy otoczki.

 

 

 

 

 

 

 

Rys., str. 590.

Opisy:

SWIG Interface File = Plik interfejsowy SWIG

SWIG = SWIG

C Wrapper File = Plik owijający w C

Compile and Link= Kompilacja i konsolidacja

Python Shadow File = Plik towarzyszący Pythona

Shared Library Ext. Module = Moduł zewnętrznej biblioteki współużytkowanej

Python Interpreter = Interpreter Pythona

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Powyższy diagram pokazuje proces wykorzystywany do utworzenia modułu rozszerzenia dla pliku interfejsowego SWIG. SWIG czyta plik interfejsowy i produkuje plik towarzyszący (ang. shadow file). Plik towarzyszący zawiera definicje klasy wspierającej (ang. helper class), które ułatwiają zadanie tworzenia interfejsu ze strukturami języka C. SWIG tworzy również plik źródłowy C, zawierający kod, oparty na zawartości pliku interfejsowego — ten plik zwany jest plikiem owijającym.

Proces tworzenia funkcji C dostępnych dla Pythona jest powszechnie określany przez programistów Pythona jako „owijanie” (ang. wrapping).

Plik owijający utworzony przez SWIG jest kompilowany i konsolidowany wraz z biblioteką interpretera Pythona, produkując współużytkowany moduł rozszerzenia. Plik owijający mógłby być także skonsolidowany bezpośrednio do wersji interpretera Pythona dostosowanego do indywidualnych potrzeb lub skonsolidowany do programu macierzystego, który będzie wbudowywał interpreter Pythona.

I w końcu, Python ładuje plik towarzyszący i moduł rozszerzenia, by zapewnić dostęp do zadeklarowanych w pliku interfejsowym SWIG funkcji i struktur.

Proste funkcje

Wykonamy pierwszy moduł rozszerzający używając SWIG do owinięcia funkcji i struktury sysinfo. Jeśli przeanalizuje się dokumentację systemową dla sysinfo, to można zauważyć, że opisano tam strukturę zwaną sysinfo, wraz z funkcją o tej samej nazwie. Struktura ta jest zdefiniowana w /usr/include/linux/kernel.h, ale sama funkcja nie jest zdefiniowana w żadnym z plików include.

Zgodnie z konwencją, nazwy plików interfejsowych SWIG używają rozszerzenia .i. Utwórzmy zatem nasz pierwszy plik interfejsowy SWIG pod nazwą sysinfo.i. Powinien powstać nowy katalog, zawierający pliki tworzące moduł rozszerzenia — kilka plików będzie użytych dla każdego z powstających modułów. Można zbudować więcej niż jeden moduł w pojedynczym katalogu, ale w tym przykładzie w nowym katalogu utworzymy tylko jeden moduł.

Plik interfejsowy sysinfo.i zawiera następujący tekst:

 

%module sysinfo

%{

#include <linux/kernel.h>

#include <linux/sys.h>

%}

 

%include "linux/kernel.h"

 

%name(getsysinfo) int sysinfo(struct sysinfo *info);

 

Należy zwrócić uwagę w tym pliku na kilka spraw:

q       Dyrektywy SWIG są oznaczone przez wiodący symbol procenta (%).

q       Tekst zawarty pomiędzy %{ i }% jest umieszczony bez interpretacji w utworzonym w C pliku owijającym.

q       Pierwszy wiersz pliku interfejsowego, %module sysinfo, określa nazwę modułu Pythona, który ma być utworzony.

W tym przykładzie, pierwsze dwie instrukcje #include będą przepisane do utworzonego w C pliku owijającego. Dokumentacja systemowa dla sysinfo podaje, że oba te pliki powinny być dołączone.

q       Dyrektywy %include mówią SWIG, aby przetworzyć podany plik, tworząc kod owijający dla struktur i funkcji zdefiniowanych w pliku dołączanym.

W tym przykładzie, przetwarza się tylko linux/kernel.h, ponieważ jest to plik, który definiuje strukturę sysinfo. Należy zwrócić uwagę, że SWIG nie posiada pełnego preprocesora C, tak więc czasem zawodzi dla skomplikowanych plików .h.

Przy owijaniu wielu funkcji, opisanych przez istniejące pliki .h, czasami jest konieczne przepisanie plików .h tak, aby usunąć konstrukcje niezrozumiałe dla SWIG. W tym przypadku SWIG nie ma kłopotów z linux/kernel.h.

q       Wreszcie, podczas generowania pliku towarzyszącego (ang. shadow file) SWIG utworzy klasę zwaną sysinfo, reprezentującą strukturę C sysinfo.

Ponieważ Python używa pojedynczej przestrzeni nazw (ang. namespace) dla obiektów zadeklarowanych na poziomie modułu, nazwa klasy 'sysinfo' jest w konflikcie z nazwą funkcji 'sysinfo'. Dla usunięcia tego konfliktu nazw, używamy dyrektywy %name w celu zmiany nazwy funkcji sysinfo na getsysinfo w pliku towarzyszącym.

Mając utworzony plik interfejsowy sysinfo.i, można użyć SWIG do wytworzenia w C pliku owijającego sysinfo_wrap.c:

 

$ swig –python –I/usr/include –shadow –make_default sysinfo.i

Generating wrappers for Python

/usr/include/linux/kernel.h : Line 85. Warning. Array member will be read-only.

/usr/include/linux/kernel.h : Line 93. Warning. Array member will be read-only.

 

SWIG zgłasza dwa ostrzeżenia podczas przetwarzania pliku interfejsowego. Znaczenie tych ostrzeżeń omówimy dalej.

Kilka opcji wiersza poleceń może być użytych w C do nadzoru procesu tworzenia pliku owijającego:

 

Opcja

Znaczenie

-python

produkuje kod do rozszerzenia Pythona (zamiast TCL czy Perla),

-I/usr/include

określa lokalizację, w której należy szukać plików include,

-shadow

tworzy plik towarzyszący w Pythonie,

-make_default

tworzy domyślny konstruktor dla struktur,

sysinfo.i

nazwa pliku interfejsowego.

 

W odróżnieniu od kompilatora C/C++, SWIG domyślnie będzie szukał plików dołączanych w bieżącym katalogu roboczym i w katalogu swig_lib. Ponieważ dołączamy linux/kernel.h w sysinfo.i, użyliśmy tutaj opcji wiersza poleceń -I/usr/include. Informuje ona SWIG o tym, że należy szukać plików dołączanych również w katalogu /usr/include.

Opcja –shadow powoduje, że SWIG produkuje plik towarzyszący w Pythonie. Pliki towarzyszące ułatwiają dostęp z Pythona do struktur i funkcji języka C, poprzez zdefiniowanie struktur jako klas Pythona.

SWIG tworzy w C plik owijający sysinfo_wrap.c oraz w Pythonie plik towarzyszący sysinfo.py. Fragment pliku towarzyszącego, sysinfo.py, pokazano poniżej:

 

# This file was created automatically by SWIG.

import sysinfoc

class sysinfo:

    def __init__(self, *args):

        self.this = aply(sysinfoc.new_sysinfo,args)

...

Zgłoś jeśli naruszono regulamin