Jython : Java et Python

30/11/2006

JPython est né d’une volonté d’utiliser les technologies Java avec un langage simple et puissant : Python. L’interpréteur python a donc été réécrit en Java pour permettre le mélange entre les classes du jdk et l’intrépeteur python au sein de la même machine virtuelle Java. Suite à des problèmes de license déténue par le CNRI (qui a vu la naissance de Python et de JPython), un nouveau nom a été donné à ce projet : Jython.

jython
Jython 2 est une version plus mature, qui subit un processus de développement plus intense que celui de JPython.

Jython permet de :

La procédure d’installation de Jython 2 n’a plus rien à voir avec celle de son ancêtre JPython 1.1. Elle est en effet vraiment trés simple. Pour ceux qui désirent travailler avec JPython 1.1, il faut non seulement installer JPython mais aussi l’errata de Finn Bock permettant de fixer pas mal de petites défaillances (voir liens). Cela dit, il n’y a pas vraiment de raison d’utiliser cette dernière, d’autant plus que la compatibilité ascendante est assurée.

Expérimentation de Jython

Voici un programme simple qui, décliné en plusieurs étapes permet de parcourir les possibilités d’utilisation du couple java/python. L’objet du programme est de retrouver des fichiers dans une arborescence, en utilisant les expressions régulières pour spécifier les noms de fichier.

Le moteur de recherche de ces fichiers est écrit en python, et cela pour :

Par exemple, pour trouver les fichiers python contenant dans le nom ‘mllib’ et étant suffixés par ‘py’ ou ‘pyc’ contenus dans le répertoire lib de la distribution python, en utilisant l’interpréteur python :

Python 2.0 (#2, Oct 30 2000, 14:29:42)
[GCC 2.8.1] on sunos5
Type "copyright", "credits" or "license" for more information.
>>> import pyFind
>>> finder=pyFind.FileFind()
>>> finder.setPath("/users/public_w3p/share/python/Python-2.0/lib")
>>> finder.setExprFile("[/].*?mllib[.](py|pyc)$")
>>> finder.process()
>>> for elem in finder.getFoundFiles():
...     print elem
...
/users/public_w3p/share/python/Python-2.0/lib/python2.0/htmllib.py
/users/public_w3p/share/python/Python-2.0/lib/python2.0/sgmllib.py
/users/public_w3p/share/python/Python-2.0/lib/python2.0/xmllib.py
/users/public_w3p/share/python/Python-2.0/lib/python2.0/htmllib.pyc
/users/public_w3p/share/python/Python-2.0/lib/python2.0/sgmllib.pyc
/users/public_w3p/share/python/Python-2.0/lib/python2.0/xmllib.pyc
/users/public_w3p/share/python/Python-2.0/lib/python2.0/test/test_xmllib.py
/users/public_w3p/share/python/Python-2.0/lib/python2.0/test/test_xmllib.pyc
>>>

Une couche graphique

Pour rendre le moteur de recherche disponible de façon plus conviviale, une interface graphique est rajoutée. Pour cela, l’utilisation de Jython permet d’utiliser

Jython sert alors de liant entre ces différentes technologies.

interface de FindGui

En plus de la présentation graphique, on peut rajouter la possibilité d’exécuter une action en double-cliquant sur un élément de la ul (classiquement une action d’édition ou de suppression).

Il y a trois (au moins) façons de réaliser ce programme. Les trois étapes décrites par la suite représentent une évolution du même programme depuis une approche 100% Java jusqu’à l’approche 100% Jython.

Pour chaque étape, le composant de recherche python décrit plus haut est utilisé, et :

  1. 100% du GUI en Java
  2. les composants graphiques adaptés de swing en Java, le modèle de données en Java, l’application graphique en Jython
  3. 100% du GUI en Jython

Etape 1 : Java utilise Python

Cet exemple illustre l’utilisation de l’interpréteur python au travers du code Java. Le but ici est d’utiliser le composant de recherche écrit en python dans du code Java. Le programme étant assez simple, voici un diagramme de classe :

Etape 1

Le package dont le rôle de contrôleur devrait faire l’interface entre les données, le graphique et l’interpréteur python, a été volontairement laissé de côté afin de réaliser un exemple court et fonctionnel. Il est à compléter.

Main
La classe Main représente juste le point de départ de l’application
FindGui
Le package contient la classe de l’interface graphique: une JFrame spécialisée. Elle recoit les évenements provenant de l’utilisateur, pilote l’interpréteur python, remplit le modèle de données, exécute ce qui doit l’être lors d’un double-clique (en somme GUI + contrôleur).
FindData
Le package contient la classe gérant les données issues de la recherche.
SwingAdapter
Le package contient les adaptations faites d’après Swing pour obtenir le rendu voulu. En particulier, la gestion du double-clique et la façon d’afficher chaque élément de la liste (en deux couleurs). Dans les autres exemples, tous ces services sont réalisés par du code Jython en utilisant la dérivation d’objets Java.

Le diagramme suivant représente l’organisation en terme de classes :

find class

Utilisation de l’interpréteur

Pour utiliser le composant de recherche python défini dans le fichier (module) pyFind.py, et plus précisement la classe FileFind, il faut récupérer une instance de celle-ci.

Ceci se réalise par plusieurs étapes simples :

importation du module
La classe org.python.core.imp est utilisée pour réaliser l’importation du modulePyObject
findMod = imp.importName("pyFind", true);
création d’une instance
Une instance est créée (par réflexion) de la classe FileFind. Elle est représentée par un PyObject (org.python.core). C’est sur cette instance qu’il faut invoquer des méthodes.
PyObject findClass = findMod.__getattr__("FileFind");
findInstance = findClass.__call__();
passage de paramètres, appel de méthodes
Il suffit d’invoquer une méthode (premier argument) sur l’instance précédemment créée en passant les bons paramètres. Les types de base sont définis dans le package org.python.core. Une chaine de caractères passée comme paramètre est un PyString. Un entier est un PyInteger, un entier long un PyLong, etc…
PyObject res;
// def setPath(self,p):
res = findInstance.invoke("setPath",new PyString(whereText.getText()));
// def setExprFile(self,r,simplified)
res = findInstance.invoke("setExprFile",new PyString(whatText.getText()));
// def process(self):
res = findInstance.invoke("process");
récupération du résultat
Pour retrouver les différentes valeurs résultant de la recherche et stocker dans un tableau python, il suffit d’itérer sur cette liste et les insérer dans le modèle de données Java. Chaque élément est récupéré dans un PyObject, qui fournit la méthode toString.
PyObject item;
for (int i = 0; (item = res.__finditem__(i)) != null; i++) {
dataModel.addElement(item.toString());
}

Distribution

Les fichiers sont regroupés dans des archives, format zip et tarball. Les sources sont sous licence GPL. Le makefile fournit permet de compiler le code java et de lancer l’execution. Les possibilites sont compile,clean, exec. Le ‘make’ sans argument permet de tout enchainer.

make compile
make exec
ou
make

Téléchargement :

Etape 2 : 50/50 Java/Jython

Cette étape permet d’écrire l’application graphique en Jython et non plus en Java. Il s’agit ici de modifier la classe FindGui sans pour autant changer tous les packages.

Le but est ainsi de démontrer qu’il est possible de créér une classe Python dérivant de classe(s) Java, soit par extension, soit par implémentation. Cette classe Python utilise alors des classes Java.

Modele de Find

Migration vers Jython

La syntaxe Python étant plus simple et non typée, le code s’en trouve allégé (un des nombreux avantages du langage Python). Les principales différences sont évoquées ci dessous.

Le déclaration de la fenêtre principale devient :

class FindGui(JFrame, ActionListener):

à la place de :

public class FindGui extends JFrame
          implements ActionListener

La déclaration du constructeur (nommé __init__)

def __init__(self,title):

à la place de :

public FindGui(String title) {

A chaque déclaration d’une méthode Python, il faut passer comme premier argument l’objet lui même, celui ci est représenté par “self” (équivalent au “this” Java). Pour appeler le constructeur de la super classe, il faut explicitement le nommer comme n’importe quel autre appel :

JFrame.__init__(self,title, visible=1)

à la place de :

super(title);

En python, et à fortiori en Jython, les variables membres n’ont pas besoin d’être déclarées. Néanmoins, il est possible de le faire, tout dépend de la façon de programmer.

Les variables membres, au même titre que les méthodes, sont stockées dans un dictionnaire interne de la classe. Donc à chaque fois que l’on fait référence à un self.toto, si dans le dictionnaire, la clé ‘toto’ n’existe pas et qu’il s’agit d’une affectation, une nouvelle clé est insérée ainsi que la valeur associée. Si la valeur n’existe pas dans le dictionnaire et que l’on veut un accès en consultation, une exception est levée.

En prenant deux variables membres au hasard, l’une est déclarée dans le constructeur et affecté à rien (None), l’autre n’est déclarée que lors de l’instanciation du JButton.

self.foundlist = None
self.searchButton = swing.JButton("Recherche")

En Java, il y a d’abord la déclaration, puis l’instanciation (bien que l’on puisse faire les temps en une seule étape) :

// déclaration private JList foundlist;
private JButton searchButton;
// création des objets
foundlist = new JList(dataModel);
searchButton = new JButton("Recherche");

Distribution

Les fichiers sont regroupés dans des archives au format tarball ou zip. Les sources sont sous licence GPL.
Le makefile fournit permet de compiler le code java, de créer un jar, et de lancer l’exécution. Les possibilités sont compile,jar,clean, et exec. Le ‘make’ sans argument permet de tout enchaîner.

Dans cette version, comme il y a du java à compiler, il faut mettre le bytecode dans un fichier jar pour que l’interpreteur jpython puisse trouver les classes.

make compile
make jar
make exec

ou

make

Téléchargement :

Etape 3 : Jython pilote java

Cette version est certainement une des plus intéresssantes, puisque l’ensemble du code écrit est du code Jython. Cela signifie que Jython est utilisé comme glue. Il permet de faire fonctionner les composants Java et/ou Python ensemble sans pour cela avoir besoin de créer un seul fichier à compiler. D’ailleurs, dans les sources, il n’existe plus de Makefile, seul l’interpréteur Jython est nécessaire.

Distribution

Les fichiers sont regroupés dans des archives au format tarball ou zip. Les sources sont sous licence GPL.
Pour lancer l’interface, il suffit de faire :

  cd step3
  jpython findgui.py

Téléchargement :