Indice

Estendere Python grazie a swig

Autore: Fabio Di Matteo
Ultima revisione: 13/07/2008

Dal sito di swig:www.swig.org

“SWIG is a software development tool that connects programs written in C and C++ with a variety of high-level programming languages. SWIG is used with different types of languages including common scripting languages such as Perl, PHP, Python, Tcl and Ruby. The list of supported languages also includes non-scripting languages such as C#, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), Java, Lua, Modula-3, OCAML, Octave and R. Also several interpreted and compiled Scheme implementations (Guile, MzScheme, Chicken) are supported. SWIG is most commonly used to create high-level interpreted or compiled programming environments, user interfaces, and as a tool for testing and prototyping C/C++ software. SWIG can also export its parse tree in the form of XML and Lisp s-expressions. SWIG may be freely used, distributed, and modified for commercial and non-commercial use.”

Istruire swig sul sorgente C da trattare

Il sorgente verrà trattato da swig grazie ad un file chiamato interfaccia dove sono specificati header e codice del nostro sorgente.
Consideriamo il seguente codice C Una libreria per leggere i file .desktop :

#include <stdio.h>
#include <string.h>
#define MAXKEY  50
#define MAXVALUE  350
#define MAXLINE 450
 
int wordcount(char* line);
char* keyread(char *path, char* key);
void keywrite(char *path, char* key, char* value);
int keydel(char *path, char* key);
 
 
 
 
char* keyread(char *path, char* key){
	FILE *f=fopen(path, "r");
	char valuetmp[MAXVALUE], line[MAXVALUE+MAXKEY], *value, key_now[MAXKEY] ;
	int i;
 
	while (feof(f)==0){
		fgets(line,MAXVALUE+MAXKEY,f);
		if (line[0]!='#'){
			sscanf(line, "%[^'=']=%[^'=\n']",key_now, valuetmp);
			if (strcmp(key_now, key)==0){
				value=malloc(sizeof(valuetmp));
				strcpy(value,valuetmp);
				fclose(f);
				return value;
			}
		}
	}
	fclose(f);
}
 
void keywrite(char *path, char* key, char* value){ 
	FILE *f=fopen(path, "r");
	int i, pos=0, idxline=0 ;
	char valuetmp[MAXVALUE],  *nline[MAXVALUE+MAXKEY], key_now[MAXKEY] ;
 
	//get key's position
	while (feof(f)==0){
	pos++;
		nline[pos-1]=malloc(MAXLINE);
		fgets(nline[pos-1],MAXLINE,f);
		if (nline[pos-1][0]!='#'){
			sscanf(nline[pos-1], "%[^'=']=%[^'=']",key_now, valuetmp);
			if (strcmp(key_now, key)==0 && idxline==0){
				idxline=pos-1;
			}
		}
	}
	fclose(f);
 
	//edit or append key
	nline[idxline]=malloc(MAXLINE+15);
	if (idxline!=0){
		//edit key
		sprintf(nline[idxline] ,"%s=%s\n",key,value);
		FILE *f=fopen(path, "w");
 
		for (i=0;i<=pos-1;i++)	fputs(nline[i],f);
		fclose(f);
 
	}else{
		//append new key
		FILE *f=fopen(path, "a");
		sprintf(nline[pos-1] ,"%s=%s",key,value);;
		fputs(nline[pos-1],f);
		fclose(f);	
	}
 
}
 
 
 
int keydel(char *path, char* key){ 
	FILE *f=fopen(path, "r");
	int i, pos=0, idxline=0 ;
	char valuetmp[MAXVALUE],  *nline[MAXVALUE+MAXKEY], key_now[MAXKEY] ;
 
	//get key's position
	while (feof(f)==0){
	pos++;
		nline[pos-1]=malloc(MAXLINE+15);
 
		fgets(nline[pos-1],MAXLINE,f);
		if (nline[pos-1][0]!='#'){
			sscanf(nline[pos-1], "%[^'=']=%[^'=']\n",key_now, valuetmp);
			if (strcmp(key_now, key)==0 && idxline==0){
				idxline=pos-1;
			}
		}
	}
	fclose(f);
 
	//delete key
	if (idxline!=0){
		FILE *f=fopen(path, "w");
		for (i=0;i<=pos-1;i++){
			if (i!=idxline)fputs(nline[i],f);
		}
		fclose(f);
 
	}else{
		return -1;
	}
}
 
 
 
 
int wordcount(char* line){
	int i,count=0;
	for(i=0; i<strlen(line);i++){
		if (isspace(line[i]) && !isspace(line[i+1]) ) count++;	
	}
	return count;
}

Creiamo il file di interfaccia

A partire dal codice sopra esposto ecco come potremmo scrivere il file di interfaccia keys.i a swig:

%module keys
%{
/*Qua ci fanno soltanto gli header*/
#include <stdio.h>
#include <string.h>
#define MAXKEY  50
#define MAXVALUE  350
#define MAXLINE 450


extern wordcount(char* line);
extern keyread(char *path, char* key);
extern keywrite(char *path, char* key, char* value);
extern keydel(char *path, char* key);

%}
/*qua il sorgente*/
char* keyread(char *path, char* key){
	FILE *f=fopen(path, "r");
	char valuetmp[MAXVALUE], line[MAXVALUE+MAXKEY], *value, key_now[MAXKEY] ;
	int i;

	while (feof(f)==0){
		fgets(line,MAXVALUE+MAXKEY,f);
		if (line[0]!='#'){
			sscanf(line, "%[^'=']=%[^'=\n']",key_now, valuetmp);
			if (strcmp(key_now, key)==0){
				value=malloc(sizeof(valuetmp));
				strcpy(value,valuetmp);
				fclose(f);
				return value;
			}
		}
	}
	fclose(f);
}

void keywrite(char *path, char* key, char* value){ 
	FILE *f=fopen(path, "r");
	int i, pos=0, idxline=0 ;
	char valuetmp[MAXVALUE],  *nline[MAXVALUE+MAXKEY], key_now[MAXKEY] ;

	//get key's position
	while (feof(f)==0){
	pos++;
		nline[pos-1]=malloc(MAXLINE);
		fgets(nline[pos-1],MAXLINE,f);
		if (nline[pos-1][0]!='#'){
			sscanf(nline[pos-1], "%[^'=']=%[^'=']",key_now, valuetmp);
			if (strcmp(key_now, key)==0 && idxline==0){
				idxline=pos-1;
			}
		}
	}
	fclose(f);
	
	//edit or append key
	nline[idxline]=malloc(MAXLINE+15);
	if (idxline!=0){
		//edit key
		sprintf(nline[idxline] ,"%s=%s\n",key,value);
		FILE *f=fopen(path, "w");
		
		for (i=0;i<=pos-1;i++)	fputs(nline[i],f);
		fclose(f);

	}else{
		//append new key
		FILE *f=fopen(path, "a");
		sprintf(nline[pos-1] ,"%s=%s",key,value);;
		fputs(nline[pos-1],f);
		fclose(f);	
	}

}



int keydel(char *path, char* key){ 
	FILE *f=fopen(path, "r");
	int i, pos=0, idxline=0 ;
	char valuetmp[MAXVALUE],  *nline[MAXVALUE+MAXKEY], key_now[MAXKEY] ;

	//get key's position
	while (feof(f)==0){
	pos++;
		nline[pos-1]=malloc(MAXLINE+15);

		fgets(nline[pos-1],MAXLINE,f);
		if (nline[pos-1][0]!='#'){
			sscanf(nline[pos-1], "%[^'=']=%[^'=']\n",key_now, valuetmp);
			if (strcmp(key_now, key)==0 && idxline==0){
				idxline=pos-1;
			}
		}
	}
	fclose(f);
	
	//delete key
	if (idxline!=0){
		FILE *f=fopen(path, "w");
		for (i=0;i<=pos-1;i++){
			if (i!=idxline)fputs(nline[i],f);
		}
		fclose(f);

	}else{
		return -1;
	}
}




int wordcount(char* line){
	int i,count=0;
	for(i=0; i<strlen(line);i++){
		if (isspace(line[i]) && !isspace(line[i+1]) ) count++;	
	}
	return count;
}

Come vedete la direttiva %module specifica il nome del modulo python che verrà creato, subito sotto ci sono gli header, e in basso dopo l'ultima parentesi graffa c'è il corpo del codice sorgente con le nostre istruzioni.
Adesso non ci resta che creare il modulo python composto da libreria binaria e wrapper.

Makefile

Il seguente makefile creerà il modulo python:

all:
	swig -python keys.i
	gcc -c keys.c keys_wrap.c  -I/usr/include/python2.5
	ld -shared keys.o keys_wrap.o -o _keys.so 

Sono 3 le istruzini, e in ordine:

  1. viene creato il wrapper python per la nostra libreria;
  2. viene creato un file oggetto (di nome keys.o ) con i nostri sorgenti e il wrapper creato ;
  3. viene creata una libreria condivisa di nome _keys.so .

Proviamo il tutto in python

Creiamo nella stessa directory del modulo un file di nome test.py con il seguente codice:

import keys
 
icona=keys.keyread('audacious.desktop','Icon');
print keys;
 
keys.keywrite('audacious.desktop','Icon', 'fileicona.png' );
 
exit;

avviare il file con python test.py (dato che non abbiamo specificato l'interprete).

Come si vede si sta usando la libreria proprio come lo si faceva in C in questo articolo: Una libreria per leggere i file .desktop