<figure><img src="image_1.png" /></figure>

HAL
open science

# Python et l'agrégation d'outils

Laurent Pointal

► To cite this version:

Laurent Pointal. Python et l'agrégation d'outils. Linux Developer's Journal, 2007, 3 (4), pp.12-19.
hal-04469680

HAL Id: hal-04469680

https://hal.science/hal-04469680v1

Submitted on 10 Mar 2024

HAL is a multi-disciplinary open access
archive for the deposit and dissemination of sci-
entific research documents, whether they are pub-
lished or not. The documents may come from
teaching and research institutions in France or
abroad, or from public or private research centers.

L'archive ouverte pluridisciplinaire HAL, est
destinée au dépôt et à la diffusion de documents
scientifiques de niveau recherche, publiés ou non,
émanant des établissements d'enseignement et de
recherche français ou étrangers, des laboratoires
publics ou privés.

Public Domain
<<DZIAL>>

Python, Communication inter-processus, Contrôle de processus, Intégration de logiciels, CORBA, RPC,
SOAP, HTTP, XML.

<</DZIAL>>

<<TOP>>

Intégration en Python Majeur

<</TOP>>

<<STRONA_PISMA>>

www.linuxdevjournal.com

<</STRONA_PISMA>>

<<NUMER_WYDANIA>>

3/2007

<</NUMER_WYDANIA>>

<<POZIOM_TRUDNOSCI>>

<<POZIOM posx=1;c posy=c fit=W grow=H>>

<<graphic place=GRAFIKA file_name=poziom_1.eps posx=c posy=c fit=W grow=H/>>

<</POZIOM>>

<</POZIOM_TRUDNOSCI>>

<<TYTUL>>

## Agrégation d'outils existants avec Python

<</TYTUL>>

<<LEAD>>

Utilisé autant dans le cadre de petits scripts répondant à des besoins immédiats, que pour des logiciels plus pérennes de toutes envergures, le langage Python [PYTHON] excelle là où il faut interconnecter différentes technologies afin d'agréger des outils disparates utilisant des moyens de communication divers.

Lancer des exécutables tiers et les contrôler via des pipes, invoquer des méthodes distantes et/ou des objets distribués, manipuler directement des sockets réseau ou utiliser les protocoles de l'Internet, charger des librairies existantes, lire directement certains formats de fichiers... il existe de nombreux outils en Python pour se faciliter la vie en intégrant des logiciels existants.

Nous allons montrer ici l'utilisation de diverses techniques d'intégrations avec Python et donner des liens vers d'autres techniques et outils qu'il est possible d'utiliser.

<</LEAD>>

<<O_AUTORZE>>

Laurent Pointal

L'auteur est informaticien pour deux groupes de recherche au sein du laboratoire LIMSI/CNRS. Il y exerce depuis trois ans dans le domaine de la communication homme-machine. Il a travaillé précédemment pendant un peu plus de dix ans au pilotage d'expériences scientifiques sur les installations du très grand équipement du laboratoire LURE.

Ses activités l'ont amené à aborder certaines techniques permettant d'interfacer des logiciels issus de diverses sources, afin de réaliser des systèmes communiquants complexes.

<</O_AUTORZE>>

<<RAMKA posx=8;0r posy=t fit=W grow=H>>

## Notes aux lecteurs

Sauf indication particulière, les modules (librairies) indiquées font parties de la distribution standard de Python et sont donc directement disponibles lorsque celui-ci est installé. Les sites où trouver les librairies tiers sont indiqués à la fin de l'article. Les exemples de scripts ont été testés avec la version 2.4.3 de Python, sur un Linux Mandriva 2007.0.

Les exemples de scripts omettent les en-têtes habituels de fichiers sources Python afin de ne pas surcharger les listings, à savoir :
#!/bin/env python

# -*- coding: utf-8 -*-

Le shebang étant à adapter à votre distribution (`/usr/bin/env python`, `/bin/python`...), et l'encodage à adapter à celui utilisé par votre éditeur (on utilise souvent `latin1` et `ascii`).

Pour faciliter la compréhension on a préfixé explicitement le nom des modules lors de l'appel aux fonctions dans les exemples. Python permet des raccourcis pour éviter cette lourdeur d'écriture.

Pour l'installation de modules tiers, la première chose à regarder est s'ils ne sont pas disponibles dans le gestionnaire de packages de votre distribution. Si ce n'est pas le cas, la solution la plus simple est d'utiliser `easy_install` [EASYINSTALL], une application qui s'occupe de télécharger le module désiré (ainsi que les autres modules nécessaires s'il y a lieu) puis de l'installer, à condition que le développeur ait créé un fichier `.egg` correspondant au module et déposé le tout sur le "Python Package Index" [PYPI]. La façon suivante est de décompresser l'archive téléchargée, et si elle contient un fichier `setup.py`, d'ouvrir un shell dans le répertoire qui contient ce fichier et de faire simplement `python setup.py install`. S'il n'y a pas de `setup.py`, il faut généralement copier les fichiers dans un des répertoires du `PYTHONPATH` – on utilisera le répertoire site-packages du répertoire des librairies de Python. Et sinon… il faut lire la documentation du module pour connaître sa procédure d'installation (certains passent par les habituels `/configure`; `make`; `make install`).

<</RAMKA>>

## Contrôle de processus

C'est le b.a.-ba de la réutilisation de logiciels existant, être capable de démarrer un exécutable tiers, de lui fournir les données en entrée s'il y a lieu, et de récupérer les résultats en sortie s'il en produit. On peut aussi vouloir un contrôle plus fin, avec une adaptation des entrées fournies au logiciel suivant des éléments extérieurs ou encore suivant les résultats qu'il nous donne.

### Fonctions des librairies C

Le module `os` fournit les fonctions habituelles des librairies C, `system()`, les familles `exec...()` et `spawn...()` (avec leurs variantes permettant d'indiquer l'utilisation ou non du `PATH`, le passage de variables d'environnement, et les façons de transmettre les arguments), `fork()`.

<<LISTING lang=Python posx=6;01 posy=t fit=W grow=H>>

Listing 1. Fonctions de base des librairies C

import os

os.system('ls -l /')

exitcode = os spawnlp(os.P_WAIT,'ls','ls','-l','/' )

pid = os spawnlp(os.P_NOWAIT,'ls','ls','-l','/' )

<</LISTING>>

L'utilisation de l'option `P_NOWAIT` permet de lancer l'exécutable tiers en parallèle au script Python en cours, le `pid` récupéré pouvant être utilisé pour connaître l'état du processus lancé, attendre sa terminaison, le tuer - le module `os` fournit les fonctions ad-hoc (`wait()`, `waitpid()`, `kill()`)… On notera dans les exemples `spawn` la répétition du nom de l'exécutable en première position des paramètre.

Le module `os`, ainsi que le module `popen2`, fournissent des fonctions et des classes permettant de lancer des processus en maintenant des pipes de communication avec leurs entrées/sorties standard (attention à l'ordre des pipes retournés, qui diffèrent entre les fonctions de `os` et celles de `popen2`). Pour les cas simples, le module `commands` définit des fonctions évitant d'avoir à manipuler les pipes.

<<LISTING lang=Python posx=6;01 posy=t fit=W grow=H>>

Listing 2. Contrôle de base via des pipes

import popen2,commands

childout,childin = popen2.popen2('ls -l /')

res1 = childout.readline()
```
res2 = commands.getoutput('ls -l /')
```

```
<</LISTING>>
```

### Module subprocess

Les fonctions des librairies C fonctionnent bien, mais ne sont pas toujours pratiques à utiliser, entre autre lorsque l'on veut effectuer des pipes entre différents exécutables tiers, ni toujours très sécurisés (appel à /bin/sh de façon implicite). Depuis sa version 2.4, Python dispose d'un module subprocess qui remplace avantageusement ces outils dans la plupart des cas.

Il fonctionne via l'utilisation d'objets de la classe Popen, dont les nombreux paramètres de construction permettent de définir comment on veux les utiliser. Outre l'indication de l'exécutable et de ses paramètres, on peut spécifier le répertoire courant d'exécution, les variables d'environnement, indiquer une fonction à appeler avant de créer le processus fils... et bien sûr spécifier comment on désire utiliser les flux en entrée et en sortie. Voici un exemple réalisant l'équivalent d'une commande shell `ls -l / | sort -k 2 -n`.

```
<<LISTING lang=Python posx=6;0l posy=t fit=W grow=H>>
```

#### Listing 3. Utilisation de subprocess

```
import subprocess
```

```
pls = subprocess Popen(['ls', '-l', '/'],
```

```
__stdout = subprocess.Pipe)
```

```
psort = subprocess Popen(['sort', '-k', '2', '-n'],
```

```
__stdin = pls.stdout, stdout = subprocess.Pipe)
```

```
res = psort.communicate()[0]
```

```
print res
```

```
<</LISTING>>
```

La documentation de Python contient des indications sur l'utilisation de ce module pour réaliser diverses opérations habituelles et remplacer les appels aux fonctions des librairies C.

Que ça soit avec `os`, `popen2` ou `subprocess`, lorsque l'exécutable doit fonctionner en étant alimenté régulièrement en données et/ou doit fournir un flux de résultats, il faut alors directement manipuler les pipes et utiliser leurs méthodes `write()`, `flush()`, `readline()`... éventuellement à partir de deux threads séparées si les flux doivent être simultanés.

```
<<LISTING lang=Python posx=6;0l posy=t fit=W grow=H>>
```

#### Listing 4. Manipulation directe des pipes avec subprocess

```
import subprocess
```

```
s = subprocess Popen(['bc', '-q'], stdin = subprocess.Pipe,
```

```
__stdout = subprocess.Pipe, stderr = subprocess.STDOUT)
```

```
print dir(s.stdout)
```

```
while True :
```

```
__cmde = raw_input("Calcul? ")
```

```
__if not cmde : break
```

```
__s.stdin.write(cmde + '
')
```

```
__s.stdin.flush()
```

```
__if '=' not in cmde :
```

```
__res = s.stdout.readline()
```

```
__print "Resultat:",res
```

```
__s.stdin.close()
```

```
__s.stdout.close()
```
<<LISTING>>

## Module pexpect

Lors que l'on désire contrôler des exécutables interactifs qui n'utilisent pas les flux stdin/stdout/stderr et agissent directement avec le terminal, ou encore qui posent des problèmes lors de communications via des pipes (bufferisations gênantes...), il vaut mieux se tourner vers le module tiers `pexpect` [PEXPECT].

Il permet d'envoyer des commandes vers l'exécutable, de définir des patterns possibles attendus en sortie, de récupérer le contenu des sorties, de donner la main à l'utilisateur... Il existe par ailleurs des extensions spécifiques destinées à certains cas particuliers, par exemple `pxssh` pour le support de ssh.

## Invocation de Procédures Distantes

Cela consiste à appeler une fonction/méthode comme si l'on effectuait un appel local à l'application courante, mais avec un transfert transparent de l'appel vers une autre application en cours de fonctionnement. Une couche intermédiaire prend en charge l'acheminement des paramètres, des valeurs de retour, et éventuellement des retours d'exceptions suite à des erreurs.

## Sun Remote Procedure Call (Sun RPC)

Un grand standard, accessible avec l'installation du module tiers `pyrpc` [PYRPC] (installation via `make install`). Celui-ci nécessaire l'installation préalable de `Ply` [PLY] (une implémentation de Lex et Yacc pour Python - installation via `python setup.py install`).

La distribution de `pyrpc` viens avec un générateur de code, `pyrpcgen`, qui permet de générer le code client RPC à partir de la description au format XDR (eXternal Data Representation) de l'interface à laquelle on veut accéder.

En prenant un exemple simple [EX-RPC] (lancer `make` deux fois pour arriver à générer les binaires), qui fournit un service faisant une opération d'addition et une opération de soustraction. On commence par générer faire un pré-processing sur le fichier XDR, `cpp -P simp.x -o simp.xdr`, on crée ensuite le répertoire destiné à recevoir le package Python correspondant, `mkdir pysimp`, et on utilise finalement `pyrpcgen` sur le fichier XDR préprocessé pour générer ce package, `pyrpcgen simp.xdr pysimp`.

<<LISTING lang=XDR posx=6;0l posy=t fit=W grow=H>>

Listing 5. Définition XDR de l'exemple

#define VERSION_NUMBER 1

struct operands {

int x;

int y;

};

program SIMP_PROG {

version SIMP_VERSION {

int ADD( operands ) = 1;

int SUB( operands ) = 2;

} = VERSION_NUMBER;

} = 555555555;

<<LISTING>>

Il nous faut maintenant trouver le moyen de connaître le port sur lequel notre service RPC va être disponible. Démarrons ce service (exécutable server produit par les make sur l'exemple). Ce port est choisi au hasard, mais il est enregistré auprès du service standard port mapping, qui lui fonctionne sur le port connu et réservé 111 (voir `/etc/services`). On peut donc trouver à la main où notre service est accessible via une commande `/usr/sbin/rpcinfo -p`, mais ce qui est raisonnable pour quelques tests doit pouvoir se faire de façon automatique si l'on veut avoir un programme capable de rentrer en production. Qu'à cela ne tienne, le port mapping est simplement un service RPC, il suffit de trouver la définition de son interface XDR (une
piste, la RFC 1833), et de refaire les mêmes opérations que pour notre exemple. Note : on commentera la
définition du type `pmaplist` ainsi que celle de la fonction `PMAPPROC_DUMP`, car elles ne passent pas la
compilation par `pyrpcgen` à cause du symbole `*`.

<<RAMKA posx=8;0r posy=t fit=W grow=H>>

## Liste des services enregistrés auprès du portmapper

/usr/sbin/rpcinfo -p

program no_version protocole no_port

100000 2 tcp 111 portmapper

100000 2 udp 111 portmapper

100024 1 udp 32769 status

100024 1 tcp 38070 status

55555555 1 udp 1003

55555555 1 tcp 1005

<</RAMKA>>

<<LISTING lang=XDR posx=6;0l posy=t fit=W grow=H>>

Listing 6. Définition XDR du portmapper (partie nous intéressant)

const PMAP_PORT = 111;

struct mapping {

__unsigned int prog;

__unsigned int vers;

__unsigned int prot;

__unsigned int port;

};

const IPPROTO_TCP = 6;

const IPPROTO_TCP = 6;

program PMAP_PROG {

__version PMAP_VERS {

__void

__PMAPPROC_NULL(void) = 0;

__unsigned int

__PMAPPROC_GETPORT LATIPORT LATIPORT LATIPORT LATIPORT;

__} = 2;

__} = 100000;

<</LISTING>>

Il suffit ensuite d'écrire un script client RPC qui utilise le package `pysimp` généré (penser à ce qu'il soit accessible dans le PYTHONPATH lors de l'exécution du script client).

<<LISTING lang=Python posx=6;0l posy=t fit=W grow=H>>

Listing 7. Utilisation du module client RPC

import pysimp

import portmapper
cxpmap = portmapping.client.PMAP_PROG.PMAP_VERS("localhost",
                                                  portmappingigation)
__

infoparams = portmapping.types mappingprog=pysimp. const.SIMP_PROG,
vers=pysimp. const.SIMP_VERSION,
__

prot=portmapping. const.IPPROTO_TCP,
__port=0)

port = cxpmap. PMAPPROC_GETPORT(infoparams)

cxserver = pysimp. client.SIMP_PROG.SIMP_VERSION("localhost", port)

data = pysimp. types. operands(x=3, y=4)

res = cxserver. ADD (data)

print res

<< /LISTING >>

Objets Distribués

C'est l'étape suivante après les appels de procédures distantes, on ne s'adresse plus à une application dans
son ensemble, mais à un objet résident dans une application. On n'appelle plus des fonctions mais des
méthodes, et on accède à des attributs.

Module Pyro

Pyro [PYRO] est à Python ce que RMI (Remote Method Invocation) est à Java : une technologie d'appel de méthodes distantes dédié à un langage. Pyro n'est utilisable qu'avec un client et un serveur écrits en Python, mais il rend l'appel de procédure distante facile et quasi transparent.

Pour utiliser Pyro, nul besoin de définir une interface dans un langage tiers. Côté serveur, il suffit de créer
une classe Python et soit de la faire hériter d'une classe spécifique de Pyro, soit d'utiliser un mécanisme de
délégation. Une fois le démon de Pyro démarré, il suffit d'y enregistrer l'objet qui peut alors recevoir des
appels distants.

<<LISTING lang=Python posx=6;0l posy=t fit=W grow=H>>

Listing 8. Un serveur utilisant Pyro

import Pyro. core

class MonServeur (Pyro. core. ObjBase):

__def__init__ (self):

__Pyro. core. ObjBase. __init__ (self)

__def doubler(self, x):

__return x * 2

Pyro. core. initServer()

daemon = Pyro. core. Daemon (port=7766)

mon_server = MonServeur()

uri = daemon. connect(mon_server, "lenom")

daemon. requestLoop()

<< /LISTING >>

Côté client, il suffit de se connecter à l'objet serveur distant et de récupérer un objet proxy local permettant
d'effectuer les appels au serveur.

<<LISTING lang=Python posx=6;0l posy=t fit=W grow=H>>

Listing 9. Un client utilisant Pyro
import Pyro.core

serveur = Pyro.core.getProxyForURI("PYROLOC://localhost:7766/lenom")

print "Doublage chaine:",serveur.doubler("Une chaine")

print "Doublage entier:",serveur.doubler(21)

<<LISTING>>

Si vous n'avez que des programmes Python à faire communiquer entre eux, c'est une solution à envisager sérieusement. Il s'agit d'un outil très complet, avec de nombreuses options permettant d'adapter son fonctionnement à certains besoins : logs, mécanisme d'URI, service de nommage (via des objets distants Pyro), migration de code...

## CORBA

Défini et maintenu au sein de l'Object Management Group [OMG] qui rassemble des constructeurs et éditeurs majeurs, c'est l'architecture logicielle standard industriel pour les systèmes d'objets répartis multi-plateformes et multi-langages.

On utilise des définitions exprimées en Interface Definition Language (OMG/IDL) qui sont traitées pour produire des adaptateurs dans les langages de programmation cible – il existe des spécifications standards OMG/IDL [IDLSPEC] pour différents langages dont Python [IDLPYTHON].

Des middlewares dédiés, les Object Request Broker (ORB), se chargent d'implémenter les communications sur le "bus à objets" et d'adapter les requêtes pour que les objets puissent communiquer entre eux de façon transparente – ce sont ces implémentations qui fournissent les compilateurs d'OMG/IDL vers les langages cibles.

L'ORB préféré pour Python est OmniORBpy [OMNIORBPY], l'intégration dans Python de l'ORB OmniORB, il bénéficie d'une communauté très active d'utilisateurs et d'un développement suivi.

Avec cet ORB on génère les adaptateurs pour Python via la commande **omniidl -bpython monservice.idl**.

Elle traite la définition d'interface en OMG/IDL et crée les packages Python (des répertoires) *MonService*, *MonService POA*, ainsi que le module *monservice idl.py*. Le premier est utilisé pour créer des clients, le second pour créer des serveurs, et le troisième est utilisé par les deux autres.

<<LISTING lang=OMG/IDL posx=6;0l posy=t fit=W grow=H>>

Listing 10. Définition OMG/IDL monservice.idl

module MonService {

interface Service {

string doubler_str(in string s);

long doubler_long(in long l);

};

};

<<LISTING>>

Il ne reste plus qu'a coder le client et le serveur, et à les faire communiquer.

Côté serveur il faut écrire une classe héritant d'un squelette créé par le compilateur IDL à partir des définitions, et créer un objet "servant" de cette classe, qui sera chargé de traiter effectivement les requêtes. Il faut ensuite activer ce servant auprès d'un adaptateur d'objets (Portable Object Adapter ou POA) qui s'occupera de créer un objet CORBA lié à notre servant – dans l'exemple on a utilisé de façon implicite le RootPOA qui est l'adaptateur d'objets par défaut. C'est le POA qui régit le fonctionnement de l'objet via différentes politiques de gestion : durée de vie, activation/désactivation, support multithreading... plus de détails sur la page décrivant les principes de base d'un ORB sur le site de l'OMG [ORBBASICS].

<<LISTING lang=Python posx=6;0l posy=t fit=W grow=H>>

Listing 11. Un serveur utilisant CORBA

import sys

from omniORB import CORBA, PortableServer
import MonService, MonService__POA

class MonService_i (MonService__POA.Service):

def doubler_str(self, s):

return s * 2

def doubler_long(self, l):

return l * 2

orb = CORBA.ORB_init(sys.argv, CORBA.ORB_ID)

poa = orb.resolve_initial_references("RootPOA")

service_implementation = MonService_i()

service_corba = service_implementation._this()

poaManager = poa._get_the POAManager()

poaManager.Activate()

open("id_service.txt ","w").write(orb object_to_string(service_corba))

orb.run()

<< /LISTING >>

Pour notre exemple on passe au client une information permettant de se connecter au serveur par
l'intermédiaire d'un fichier texte contenant l'Interoperable Object Reference (IOR) du serveur.

Côté client, il suffit de trouver une référence vers l'objet – dans notre exemple un IOR – puis de demander
un accès à l'objet correspondant via un proxy, en s'assurant que l'on a bien un objet correspondant au service
attendu. Après, il n'y a plus qu'à utiliser le proxy comme s'il s'agissait d'un objet local.

<<LISTING lang=Python posx=6;0l posy=t fit=W grow=H>>

Listing 12. Un client utilisant CORBA

import sys

from omniORB import CORBA

import MonService

orb = CORBA.ORB_init(sys.argv, CORBA.ORB_ID)

ior = open("id_service.txt ").read()

obj = orb. string_to_object (ior)

proxy_serveur = obj._narrow(MonService.Service)

if proxy_serveur is None:

__print("L'objet serveur n'est pas un MonService.Service!")

__sys.exit(1)

s = proxy_serveur.doubler_str("Drole.")

print s

l = proxy_serveur.doubler_long(21)

print l

<< /LISTING >>

L'exemple est volontairement simple, CORBA offre d'autres façons de gérer les servants et de les activer, de
traiter le multithreading, de gérer la durée de vie des objets... et la norme définit de nombreux services
optionnels. La documentation d'OmniORB offre d'autres exemples plus complets avec par exemple
l'utilisation du service de nommage de CORBA, le Naming Service.

<<RAMKA posx=8;0r posy=t fit=W grow=H>>
# Transfert d'images par CORBA

Si vous avez à transférer de gros tableaux d'entiers – typiquement des images – il peut être préférable de les définir en tant que `sequence<octet>` plutôt que `sequence<short/long>`. Les spécifications OMG/IDL-Python prévoient en effet de très coûteuses conversions en listes d'entiers pour les séquences de short/long, alors que les séquences d'octets sont simplement transférés via des chaînes Python (qui peuvent contenir des valeurs 0). Il vous faut par contre gérer vous-même les problèmes d'ordre des octets suivant les plateformes, mais cela permet de manipuler directement des chaînes Python et de les utiliser par exemple avec la Python Imaging Library [PIL].

<</RAMKA>>

Pour Python il existe aussi l'ORB Fnorb [FNORB], antérieur à OmniORBpy, et dont le développement n'avance plus trop, mais qui a l'avantage d'être en pure Python.

Et pour les utilisateurs de Windows, le module d'extension pywin32 [PYWIN32] offre le support de la technologie d'objets distribués propriétaire COM/DCOM de Microsoft, ce qui permet de piloter la plupart des applications sur cette plateforme ainsi que de créer des serveurs.

## DBUS

DBUS [DBUS] est un est un projet FreeDesktop de technologie de communication inter-applications et système-applications, permettant de piloter les applications Gnome et KDE 4 (KDE 3 utilisant DCOP), ainsi que de s'informer sur le système ou d'être notifié de certains évènements via des signaux DBUS.

Il existe un module tiers `bus` [DBUSPYTHON] et un tutoriel pour son utilisation [DBUSPYTHONTUT].

# Programmation Sockets

Il s'agit là de la mise en œuvre de l'accès le plus simple au réseau, l'accès direct aux sockets et l'envoi/réception de paquets ou de flux.

## Accès UDP

Le User Datagram Protocol permet de transmettre des paquets sur un réseau IP en mode non connecté (pas de garantie de délivrance ni d'ordre d'arrivée) – en cours réseau on le compare généralement à l'envoi de courrier postal. Son utilisation est très simple et peut permettre la mise en place rapide d'échanges entre diverses applications.

Pour réaliser un serveur pouvant recevoir des paquets UDP, il suffit de créer un objet socket pour de l'UDP (`SOCK_DGRAM`) sur IP (`AF_INET`), de lui associer une adresse, puis de lire les paquets qui arrivent sur le socket.

<<LISTING lang=Python posx=6;0l posy=t fit=W grow=H>>

Listing 13. Un serveur UDP

import socket

PORT = 17834

HOTE = "localhost"

TAILLE_MAX_PAQUET = 10240

s = socket.socket(socket.AF_INET, socket.SOCKDSGRAM)

s.bind((HOTE, PORT))

while True :

paquet,source =s.recvfrom(TAILLE_MAX_PAQUET)

print "En provenance de",source,":"

print repr(paquet)

if paquet =="FIN" :

break
### s.close()

## <<//LISTING>>

La partie cliente est encore plus simple, il suffit simplement d'écrire sur le socket créé.

## <<LISTING lang=Python posx=6;0l posy=t fit=W grow=H>>

Listing 14. Un client UDP

### import socket

### PORT = 17834

### HOTE = "localhost"

### s = socket.socket(socket.AF_INET, socket.SOCKADDR)

### while True :

### paquet = raw_input("Message (FIN pour terminer):")

### s.sendto(paquet, (HOTE,PORT))

### if paquet == "FIN" : break

### s.close()

## <<//LISTING>>

## Accès TCP

Le Transmission Control Protocol est plus évolué, il assure un transport fiable en mode connecté – en cours réseau on le compare généralement à une communication téléphonique. C'est sur ce protocole qu'est réalisé la plupart des transmissions d'informations du Web.

Un serveur TCP écoute sur un socket A associé à un port spécifique connu. Lorsqu'une connexion est mise en place un autre socket B lui est dédié, qui sera utilisé pour toute la durée de cette connexion – le socket A peut alors recevoir d'autres demandes de connexion.

## <<LISTING lang=Python posx=6;0l posy=t fit=W grow=H>>

Listing 15. Un serveur TCP

### import socket

### PORT = 17834

### HOTE = "localhost"

### TAILLE_MAX_PAQUET = 10240

### TAILLE_FILE_ATTENTE = 10

### s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

### s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

### s.bind((HOTE,PORT))

### s.listen(TAILLE_FILE_ATTENTE)

### donnees = ""

### while True :

### connexion.source = s.accept()

### print "Connexion avec",source

### while True :

### print '-'*40

### donnees = connexion.recv(TAILLE_MAX_PAQUET)

### print "Recu:",repr(donnees)
```python
if "FIN" in donnees: break
connexion.send('Coucou'+donnees)
connexion.close()
if "QUIT" in donnees: break
```

### s.close()

```html
<</LISTING>>
```

Côté client, il suffit d'établir la connexion puis de lire/écrire sur le socket.

```html
<<LISTING lang=Python posx=6;01 posy=t fit=W grow=H>>
```

Listing 16. Un client TCP

```python
import socket
```

```python
PORT = 17834
```

```python
HOTE = "localhost"
```

```python
TAILLE_MAX_PAQUET = 10240
```

```python
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
```

```python
s.connect((HOTE,PORT))
```

```python
while True :
```

```python
paquet = raw_input("Message (FIN ou FIN,QUIT pour terminer):")
```

```python
s.send(paquet)
```

```python
if "FIN" in paquet: break
```

```python
retour = s.recv(TAILLE_MAX_PAQUET)
```

```python
print "Retour:",repr(retour)
```

### s.close()

```html
<</LISTING>>
```

Pour l'exemple de client/serveur TCP, saisir `FIN` côté client pour terminer le client en cours, et `FIN,QUIT` pour terminer le client et le serveur.

## Surcouches légères

Au dessus de TCP ou UDP de nombreuses personnes ont développé des couches logicielles pour répondre à des besoins spécifiques à un domaine en définissant des protocoles ad-hoc. Et dans bien des cas il existe un module tiers Python qui permet d'utiliser ces protocoles. En voici quelques exemples.

Open Sound Control (OSC) [OSC] est un protocole de communication utilisé dans le domaine de la musique, qui permet de transmettre des messages contenant des données typées, des ordres de contrôle pour des instruments, ou encore des échantillons numériques, le tout avec des contraintes de temps (rendu musical), et des contraintes de facilité d'accès par des personnes dont le métier de base n'est pas nécessairement l'informatique. Les paquets OSC peuvent être transportés de différentes façons, liaison série, bus USB, et liaisons réseau TCP ou UDP. Il existe une implémentation en Python [SIMPLEOSC] pour l'encapsulation et l'extraction de données dans des paquets OSC – il faut tout de même effectuer leur transport via TCP ou UDP.

Ivy [IVY] est un "bus" logiciel qui utilise la diffusion (broadcast) de paquets UDP pour transmettre des informations entre divers logiciels faiblement couplés. Chaque client met en place des règles de filtrage basées sur des expressions régulières afin de ne recevoir que ce qui l'intéresse. Il existe une implémentation en pur Python ainsi qu'un module d'extension basé sur la librairie C d'Ivy [IVYPYTHON].

## Protocoles de l'Internet

Par les protocoles de l'Internet, on entend les façons d'accéder aux informations qui sont arrivées avec l'Hypertext Transfer Protocol (HTTP), les Uniform Resource Locators (URLs), et les différentes couches
logicielles qui y ont été ajoutées.

## HTTP

Un grand nombre d'échanges sur l'Internet se font via HTTP, le protocole de base du Web. Python est très (trop) bien fourni au niveau des applications pour le Web [WEBPROG], que ça soit pour les serveurs, les frameworks applicatifs ou les systèmes de template – il dispose aussi de nombreux outils pour le développement côté client.

Les modules standards de Python fournissent le nécessaire à la mise en place de serveurs, ce qui peut être pratiques pour rendre facilement accessible des informations via un butineur Web. Voici un exemple très très simple.

```python
<<LISTING lang=Python posx=6;0l posy=t fit=W grow=H>>
```

Listing 17. Un serveur HTTP très simple

```python
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
```

```python
class MonHandler(BaseHTTPRequestHandler):
```

```python
def do_GET(self):
```

```python
    self.send_response(200)
```

```python
    self.send_header("Content-type", "text.html; charset=iso-8859-1")
```

```python
    self.end_headers()
```

```python
    self.wfile.write(self.contenu().encode("iso-8859-1"))
```

```python
def contenu(self):
```

```python
    return u"""<html><body>Mon contenu&hellip; qui peut être dynamique.</body></html>"""
```

```python
try:
```

```python
    server = HTTPServer((",8083"), MonHandler)
```

```python
    server.serve.forever()
```

```python
except KeyboardInterrupt:
```

```python
    server.socket.close()
```

```python
<</LISTING>>
```

Une fois ce script en fonctionnement, il suffit de saisir l'URL http://localhost:8083/ dans un browser Web pour récupérer le contenu de notre "page" servi par notre serveur.

A noter, le module `cgi`, prévu à la base pour l'utilisation de Python en tant que service Common Gateway Interface (CGI), comporte des fonctions permettant de manipuler des URLs et des en-têtes HTTP, de convertir les entités caractères (là dessus voir aussi le module `htmlentity definitions`)… qui sont tout à fait utilisables dans le cadre d'un serveur Web autonome. Ainsi que le module `Cookie` pour la gestion des cookies côté serveur, et le module `urlparse` pour l'analyse des URLs.

Côté client, le module `urllib2` fournit les fonctions nécessaires pour pouvoir ouvrir des URLs (se faire passer pour un client Web par exemple) et récupérer ou envoyer facilement des données via HTTP, FTP…

```python
<<LISTING lang=Python posx=6;0l posy=t fit=W grow=H>>
```

Listing 18. Un client HTTP simple

```python
import urllib2
```

```python
reponse = urllib2.urlopen('http://www.kernel.org/')
```

```python
xhtmldata = reponse.read()
```

```python
for num,ligne in enumerate(xhtmldata.splitlines()):
```

```python
    print "%04d - %s"%(num,ligne)
```

```python
<</LISTING>>
```
En plus de la section sur `urllib2` dans la documentation des librairies Python, il faut signaler le document "Urllib2 - The Missing Manual" [URLLIB2MM], qui contient de nombreux exemples décrivant l'utilisation avancée de ce module (transmission d'en-têtes HTML particuliers, simulation de formulaires, authentification...).

D'autres modules fournissent des services utiles, comme `cookieLib` pour la gestion des cookies côté client, ou `webbrowser` pour directement démarrer le browser Web de l'utilisateur.

## Manipulation du HTML/XHTML

Si vous avez à traiter des documents HTML/XHTML, il existe le module `HTMLParser`, mais celui-ci peut avoir du mal à procéder sur des documents vraiment mal formés. Le module `tiersBeautifulSoup` [BEAUTIFULSOUP] utilise dans ce cas des heuristiques permettant de récupérer tout de même une structure arborescente correspondant au document.

Si vous avez à traiter des documents XML (par exemple du XHTML), correctement formés, les outils dédiés à ce langage peuvent s'avérer plus adaptés. Il existe en standard des modules `xml.sax` implémentant la Simple API for XML (SAX) pour un traitement par flux, et des modules `xml.dom` implémentant le Document Object Model (DOM) permettant de manipuler une arborescence XML directement en mémoire. Ces interfaces de programmation sont issues du monde Java / C/C++, et leur utilisation est parfois un peu lourde par rapport à d'autres modules Python, d'autres boites à outil ont donc été naturellement développés pour "pythoniser" la manipulation de documents XML. Il faut citer principalement `ElementTree` [ELEMENTTREE] qui a été intégré en standard dans Python 2.5, mais il en existe d'autres, certaines ciblant l'adaptation de l'interface de programmation à des besoins ou à des dialectes XML spécifiques, d'autres ayant pour objectif d'intégrer des librairies externes (généralement en C/C++) pour pouvoir les utiliser directement à partir de scripts Python.

## XML-RPC

De HTTP et XML, on arrive naturellement à XML-Remote Procedure Call (XML-RPC) [XMLRPC]. C'est un protocole d'appel de procédure distante utilisant HTTP pour le transport et XML pour la représentation des données, en essayant de conserver un fonctionnement simple pour transmettre et recevoir des informations.

Python supporte XML-RPC en standard avec le module `xmlrpcclib`, ainsi que des modules outils comme `SimpleXMLRPCServer` ou `DocXMLRPCServer`. L'association de procédures avec un serveur peut se faire soit en définissant un objet dont les méthodes seront les procédures (il est aussi possible de créer une méthode `dispatch()` de l'objet qui sera chargée de router les appels), soit en effectuant des associations nom/fonction individuellement.

```
<<LISTING lang=Python posx=6;0l posy=t fit=W grow=H>>
```

Listing 19. Un serveur XML-RPC

```python
import SimpleXMLRPCServer,time
```

```python
class MonService:
```

```python
def quelleHeure(self):
```

```python
    return time.asctime()
```

```python
def quellePrevisionMeteo(self, jours):
```

```python
    if int(jours)%2:
```

```python
        return u"Météo: pluie"
```

```python
    else:
```

```python
        return u"Météo: beau temps"
```

```python
serveur = SimpleXMLRPCServer.SimpleXMLRPCServer(("localhost",5123))
```

```python
serveur.register_instance(MonService())
```

```python
serveur.serve.forever()
```

```xml
</LISTING>>
```
La mise en place d'un client est des plus triviales, il suffit de créer un proxy d'accès au serveur distant, via un appel à `xmlrpclib.ServerProxy()`, puis d'appeler les procédures distantes sur ce proxy.

```
<<LISTING lang=Python posx=6;0l posy=t fit=W grow=H>>
```

Listing 20. Un client XML-RPC

```python
import xmlrpclib
```

```python
proxy = xmlrpclib.ServerProxy("http://localhost:5123/")
```

```python
temps = proxy.quelleHeure()
```

```python
previsions = proxy.quellePrevisionMeteo(3)
```

```python
print "Heure:",temps
```

```python
print "Previsions metep:",previsions
```

```
<</LISTING>>
```

Si votre client est constitué d'un script JavaScript tournant dans un browser Web, il peut être intéressant de se tourner vers la représentation des données avec le JavaScript Object Notation (JSON) [JSON] et d'utiliser simplement les JSON-RPC [JSONRPC].

## SOAP

L'étape suivante est le Simple Object Access Protocol (SOAP). Normalisé par le W3C [SOAP-W3C], SOAP utilise un protocole (éventuellement HTTP) afin de transférer les données nécessaires à l'appel de procédure distante, et un représentation XML pour ces données et le résultat en retour. Il utilise abondamment les espaces de noms XML, permet de définir de nouveaux types de données construits ; c'est une version plus puissante de XML-RPC, mais aussi beaucoup plus lourde à mettre en œuvre si l'on n'a pas des outils automatiques.

SOAP est à la base des "Web Services" disponibles sur le Net, où des applications décrivent leurs interfaces dans des documents Web Services Description Language (WSDL) et communiquent effectivement via SOAP.

Pour faire court (voir le site du W3C pour faire plus long), avec SOAP on a des services et des ports, des messages échangés pour réaliser des opérations. Par analogie, on peut voir les services comme des objets et les ports comme des méthodes sur ces objets, les envois de messages correspondant à des appels de méthodes distantes.

Il existe divers modules tiers pour le support de SOAP dans Python, citons le Python Web Services [PYWEBSVCS] et son module `SOAPpy`, le package `SOAPy` [SOAPY] ainsi que l'utilisation possible de `ElementTree` [ELEMENTSOAP] – attention à certains modules comme "Python SOAP" dont le développement a été arrêté.

```
<<RAMKA posx=8;0r posy=t fit=W grow=H>>
```

## LA boite à outils : twisted

On ne peut aborder la programmation de services réseau en Python sans citer le framework de développement d'applications réseau twisted [TWISTED]. Il permet de mettre en place des serveurs pilotés par évènements en utilisant divers protocoles de transport de base : TCP et UDP, les connexions sécurisées SSL/TLS, la diffusion sur des groupes d'adresses en multicast, les sockets Unix ; ainsi que des protocoles de plus haut niveau : HTTP, NNTP, IMAP, SSH, IRC, FTP...

```
<</RAMKA>>
```

## Intégration de bibliothèques

A côté des exécutables tiers autonomes avec lesquels on veut communiquer il y a aussi des librairies de code compilé que l'on voudrait pouvoir utiliser directement à partir de Python.

### Codes en C/C++

Pour les librairies C/C++ il existe différentes solutions pour réaliser cette intégration, le choix dépendant en
partie du nombre de fonctions que l'on veut pouvoir accéder à partir de Python, et en partie du couplage que l'on veut si l'on a du C++ avec des classes.

Si l'on a juste quelques fonctions d'une librairie C, disponible sous forme de module chargeable, que l'on veut pouvoir appeler, le plus simple est d'utiliser le module `ctypes`. Voici un exemple d'accès direct aux fonctions `fopen`, `fputs`, `fclose` de la libC, où l'on spécifie explicitement les arguments en entrée et en sortie de ces fonctions. Il est possible d'utiliser les conversions automatiques que réalise `ctypes` entre les types C et certains types Python, mais le risque est grand de sortir alors en core-dump.

```c
<<LISTING lang=Python posx=6;0l posy=t fit=W grow=H>>
```

Listing 21. Utilisation dectypes

```c
importctypes
```

# Pré-chargement de la librairie.

```c
libc =ctypes.cdll LoadLibrary("libc.so.6")
```

# FILE* fopen(const char* filename, const char* mode);

```c
fopen =libc.fopen
```

```c
fopen.argtypes=[ctypes.c_char_p,ctypes.c_char_p]
```

```c
fopen.re种type =ctypes.cvoidp
```

# int fclose(FILE* stream);

```c
fclose =libc.fclose
```

```c
fclose.argtypes=[ctypes.cvoidp]
```

```c
fclose.re种type =ctypes.cint
```

# int fputs(const char* s, FILE* stream);

```c
fputs =libc.fputs
```

```c
fputs.argtypes=[ctypes.c_char_p,ctypes.cvoidp]
```

```c
fputs.re种type =ctypes.cint
```

# Utilisation

```c
f =fopen("test-ctypes.txt","w")
```

```c
fputs("Juste une chaine pour faire un essai",f)
```

```c
fflush(f)
```

```c
<</LISTING>>
```

Il est possible de spécifier d'autres genres de paramètres avec `ctypes`: indiquer un passage par référence, définir des structures ou des unions, définir des tableaux, gérer des zones mémoire alloués dynamiquement (et modifiables), définir des fonctions callback (voir le tutoriel), accéder à des variables exportées par la librairie...

C'est un outil très pratique lorsque l'on n'a qu'un nombre limité de fonctions à définir, ou lorsque l'on n'a pas nécessairement de compilateur C installé sur la machine. Mais lorsque l'on a des besoins plus poussés : interfaçage avec du C++, création d'adaptateur pour Python et pour d'autres langages... l'utilisation de swig [SWIG], de boost-python [BOOSTPYTHON] ou de sip [SIP] deviens incontournable. Ceux-ci permettent de réaliser des adaptateurs pour des librairies complètes, en spécifiant des façons de procéder pour les types définis, pour les traitements d'erreurs, pour les callbacks...

Il faut aussi citer Pyrex [PYREX], qui permet générer du code C à partir d'un sous-ensemble de code source Python (pouvant intégrer des appels à des codes C existant), et de créer des modules d'extension compilés. Cela peut être par ailleurs un bon moyen d'accélérer certains traitements sans avoir à écrire un module complet en C.

Attention, même si les outils cités facilitent le travail, la création d'adaptateurs Python pour des bibliothèques n'est pas toujours triviale, aussi avant d'essayer de le faire vous-même, vérifiez bien que quelqu'un n'a pas déjà fait le travail. L'article "Scripting C with Python" [SCRIPTINGC] peut vous permettre de faire un choix sur la technologie la plus adaptée à vos besoins.
## Communication avec d'autres langages

Il existe des outils pour lier Python avec d'autres langages que le C. Pour Java : Jython [JYTHON] (interpréteur Python écrit en Java), le Java to Python Integration [JPYPE] et le Java Embedded Python [JEPP] ainsi que le projet java2python [JAVA2PYTHON] permettant de traduire du code Java vers Python. Pour les amateurs de C# il existe IronPython [IRONPYTHON], un interpréteur Python basé sur le Common Language Runtime (CLR) de Microsoft. Pour Fortran il existe une présentation [FORTRAN] donnant un apperçu des trois solutions disponibles : Pyfort [PYFORT], f2py [F2PY] et Forthon [FORTHON]. Et il existe des outils d'interfaçage avec d'autres langages encore : Lisp, Ocaml, Delphi...

## Lecture/écriture de fichiers

Un dernier problème que nous aborderons dans cet article, la manipulation des différents formats des fichiers générés par les processus informatiques. Nous ne donnerons ici que des pistes vers des outils dédiés qui évitent d'avoir à ré-inventer la roue et l'engrenage.

Outre l'accès simple en mode texte brut, Python peut rendre transparent l'encodage des fichiers avec la fonction `codecs.open()` qui retourne un objet fichier manipulant des chaînes unicodes au niveau de l'utilisateur et les convertissant de/vers l'encodage désiré au niveau du fichier.

Pour les utilisateurs de données textuelles formatées en tableaux (avec séparateurs de colonnes et de lignes), le module `csv` permet d'automatiser l'extraction des données.

Si l'on désire accéder à des fichiers binaires spécifiques, il est possible d'ouvrir les fichiers en mode binaire ('b') et d'utiliser les modules `struct`, `array`, et `ctypes` [CTYPES] (ce dernier a été intégré dans Python 2.5) qui permettent de spécifier les structures de données et de manipuler les informations correspondantes.

Pour les fichiers d'images bitmap (et pour la manipulation de ces images), la Python Imaging Library [PIL] supporte la lecture/écriture d'un certain nombre de formats de fichiers et peut être étendue pour en gérer d'autres. Concernant les fichiers SVG, les outils XML sont normalement suffisant.

Pour les documents Open Office, ce sont des archives compressées dont il est possible d'extraire les fichier contenus avec le module `zipfile`. A partir de là on peut accéder aux données qu'ils contiennent (du XML pour les documents principaux). Il est possible de manipuler le XML directement avec les outils cités précédemment, d'utiliser les `py-odftools` [PYODFTOOLS] de Google, ou encore d'utiliser le module tiers `opy` [OOPY]. On peut aussi scripter directement Open Office en utilisant PyUNO [PYUNO].

Pour les documents Microsoft Excel, il existe divers projets : `pyExcelerator` [PYEXCELERATOR], `xlrd` [XLRD] et `pyXLWriter` [PYXLWRITER]. Et pour la génération de documents Rich Text Format (RTF), il y a le `PyRTF` [PYRTF]. Pour le scripting des applications Microsoft Office, cela passe par COM/DCOM avec le module tiers pywin32 [PYWIN32].

Pour de nombreux formats issus du monde scientifique il existe des modules tiers, par exemple pour le Hierarchical Data Format [HDF] il y a le module `pyhdf` [PYHDF] et pour [NETCDF], et certaines communautés ont développés des packages complets contenant entre autres les outils de manipulations de leurs formats de données habituels, par exemple biopython [BIOPYTHON], SciPy [SCIPY] et ScientificPython [SCIPYTHON].

## Conclusion

Cet article a essayé de faire un tour non exhaustif des différentes façons par lesquelles Python peut s'interfacer avec d'autres outils. Il existe bien d'autres protocoles, d'autres façons de faire communiquer des applications, d'autres modules dédiés disponibles sur l'Internet. Nous n'avons pas traité par exemple des accès aux bases de données, du pilotage d'interfaces graphiques par du scripting (DBUS, COM/DCOM, pywinauto [PYWINAUTO]...), du pilotage de liaisons série [PYSERIAL].

J'espère que cette lecture vous aura fait découvrir les possibilités de Python, la facilité et la rapidité avec laquelle on peut réaliser des développements à-façon, et vous aura donné l'envie d'essayer ce langage.

Et si vous achoppez sur certains problèmes, vous pouvez compter sur la communauté des Pythoneurs toujours prête à répondre aux questions pertinentes (usenet anglophone [CLP] et francophone [FCLP], liste de diffusion python-fr [PYTHONFR], Association Francophone Python [AFPY]...), sans oublier les ressources que l'on peut trouver sur le Net avec les moteurs de recherche.

```
<<W_SIECI posx=18;0r posy=t fit=W grow=H>>
```

Les liens pour les modules et documents cités dans l'article :

* [AFPY] http://afpy.org/
* [BEAUTIFULSOUP] http://www.crummy.com/software/BeautifulSoup/

* [BIOPYTHON] http://biopython.org/

* [BOOSTPYTHON] http://www.boost.org/libs/python/doc/

* [CLP] http://groups.google.fr/group/comp.lang.python/

* [CTYPES] http://python.net/crew/theller/ctypes/

* [DBUS] http://www.freedesktop.org/wiki/Software_2fibus

* [DBUSPYTHON] http://www.freedesktop.org/wiki/Software_2fDBusBindings

* [DBUSPYHONTUT] http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html

* [EASYINSTALL] http://peak.telecommunity.com/DevCenter/EasyInstall

* [ELEMENTSOAP] http://effbot.org/zone/elementsoap-1.htm

* [ELEMENTTREE] http://effbot.org/zone/element-index.htm

* [EX-RPC] http://www.cs.rpi.edu/~hollingd/netprog/code/rpc/simple/

* [F2PY] http://cens.ioc.ee/projects/f2py2e/

* [FCLP] http://groups.google.fr/group/fr.comp.lang.python/

* [FNORB] http://fnorb.sourceforge.net/

* [FORTHON] http://hifweb.lbl.gov/Forthon/

* [FORTRAN] http://calcul.math.cnrs.fr/IMG/pdf/python-fortran.pdf

* [HDF] http://hdf.ncsa.uiuc.edu/

* [IDLPYTHON] http://www.omg.org/technology/documents/formal/python.htm

* [IDLSPEC] http://www.omg.org/technology/documents/idl2x_spec;catalog.htm

* [IRONPYTHON] http://www.codeplex.com/Wiki/View.aspx?ProjectName=IronPython

* [IVY] http://www.tls.cena.fr/products/ivy/

* [IVYPYTHON] http://www.tls.cena.fr/products/ivy/download/source.html

* [JAVA2PYTHON] http://code.google.com/p/java2python/

* [JEPP] http://jepp.sourceforge.net/

* [JPYPE] http://jpype.sourceforge.net/

* [JSON] http://www.json.org/jsonfr.html

* [JSONRPC] http://json-rpc.org/wiki/python-json-rpc

* [JYTHON] http://www.jython.org/

* [OMG] http://www.omg.org/

* [OMNIORBPY] http://omniorb.sourceforge.net/

* [OOPY] http://ooopy.sourceforge.net/

* [ORBBASICS] http://www.omg.org/gettingstarted/orb_basics.htm

* [OSC] http://www.cnmat.berkeley.edu/OpenSoundControl/ et http://opensoundcontrol.org/

* [PEXPECT] http://pexpect.sourceforge.net/

* [PIL] http://www.pythonware.com/products/pil/

* [PLY] http://www.dabeaz.com/ply/

* [PYEXCELERATOR] http://sourceforge.net/projects/pyexcelerator/

* [PYFORT] http://pyfortran.sourceforge.net/

* [PYHDF] http://pysclint.sourceforge.net/pyhdf
* [PYODFTOOLS] http://code.google.com/p/py-odftools/

* [PYPI] http://www.python.org/pypi

* [PYREX] http://www.cosc.canterbury.ac.nz/greg.ewing/python/Pyrex/

* [PYRO] http://pyro.sourceforge.net/

* [PYRPC] http://thomas.enix.org/Blog-20051221211433-Libre et http://thomas.enix.org/pub/pyrpc/

* [PYRTF] http://pyrtf.sourceforge.net/

* [PYSERIAL] http://pyserial.sourceforge.net/

* [PYTHON] http://www.python.org/

* [PYTHONFR] https://www.aful.org/wws/info/python

* [PYUNO] http://udk.openoffice.org/python/python-bridge.html

* [PYWEBSVCS] http://pywebsvcs.sourceforge.net/

* [PYWIN32] https://sourceforge.net/projects/pywin32/

* [PYWINAUTO] http://www.openqa.org/pywinauto/

* [PYXLWRITER] http://sourceforge.net/projects/pyxlwriter/

* [SCIPY] http://www.scipy.org/

* [SCIPYTHON] http://dirac.cnrs-orleans.fr/plone/software/scientificpython/

* [SCRIPTINGC] http://www.suttoncourtenay.org.uk/duncan/accu/ScriptingC.html

* [SIP] http://www.riverbankcomputing.co.uk/sip/

* [SIMPLEOSC] http://www.ixi-software.net/content/body_backyard_code.html

* [SOAP-W3C] http://www.w3.org/2002/07/soap-translation/soap12-part0.html

* [SOAPY] http://soapy.sourceforge.net/

* [SWIG] http://www.swig.org/

* [TWISTED] http://twistedmatrix.com/trac/

* [URllib2MM] http://www.voidspace.org.uk/python/articles/urllib2.shtml et en VF
http://www.voidspace.org.uk/python/articles/urllib2_francais.shtml

* [WEBPROG] http://wiki.python.org/moin/WebProgramming

* [XLRD] http://www.lexicon.net/sjmachin/xlrd.htm

* [XMLRPC] http://www.xmlrpc.com/spec

Et pour aller plus loin :

* http://www.python.org/ – le site de Python (téléchargement, documentations, wiki, dépôt de modules...),

* http://divintopython.org/ – un livre en ligne pour apprendre Python à partir d'exemples (traduit dans différentes langues),

* http://www.limsi.fr/Individu/pointal/python.html – la page de liens sur Python de l'auteur.

<</W_SIECI>>