Accueil > .NET, Divers, Java EE > Intégration de code Python dans des applications Java ou .Net

Intégration de code Python dans des applications Java ou .Net

Une particularité de Python est qu’il peut être exécuté sur plusieurs environnements techniques, grâce à ses différents interpréteurs. Ainsi, on pourra importer des librairies et exécuter le code nativement, ou à travers des machines virtuelles Java ou .Net, entre autres…

Avec des librairies système

CPython est l’interpréteur par défaut. Son nom vient du fait qu’il est écrit en C, et il permet d’utiliser toute librairie compilée pour le système (dont les librairies C).

pythonGreeter.py
    class PythonGreeter:
        __defaultHello = "World"
     
        @staticmethod
        def sayHello(helloWho=__defaultHello):
            print("Hello", helloWho, "!")
     
    PythonGreeter.sayHello()
    PythonGreeter.sayHello("Jack")
$ python pythonGreeter.py
Hello World !
Hello Jack !

Avec Java

Jython est un interpréteur pour inclure des librairies Java. Il peut également être inclus à un projet Java pour exécuter du code Python, bien qu’il existe quelques limitations.

Appeler du code Java depuis Python

L’appel de code Java depuis Python est on ne peut plus simple: à partir du moment où les classes Java ont été compilées, elles peuvent être instanciées dans le code Python comme n’importe quelle autre classe Python.

JavaGreeter.java
    public class JavaGreeter {
    	private final static String _defaultHello = "World";
     
    	public static void sayHello (String helloWho) {
    		if (helloWho == null) helloWho = _defaultHello;
    		System.out.println("Hello " + helloWho + " !");
    	}
    }
javaCaller.py
    # coding=UTF-8
    # On peut importer la classe java comme s'il s'agissait d'une classe python, à partir du moment où elle a été compilée
    import JavaGreeter
     
    JavaGreeter.sayHello(None)
    JavaGreeter.sayHello("Francis")
$ javac JavaGreeter.java 
$ jython javaCaller.py 
Hello World !
Hello Francis !

Appeler du code Python depuis Java

L’inclusion de code Python dans une application Java est nettement plus compliqué, à cause de typage statique du langage. Il existe plusieurs façons d’intégrer du code Python à Java, celle que je propose ici permet de conserver le paradigme objet du langage et de l’utiliser de façon presque transparente pour Java.
On utilise ici une interface JythonGreeterInterface entre la classe Python à appeler (qui va donc l’implémenter) et le code Java appelant, afin que le second puisse connaitre, à la compilation, les méthodes du premier. Pour véritablement instancier la classe Python, on passe par un factory JythonObjectFactory pour créer un PyObject; ce PyObject est en fait notre objet Python, qui peut alors être casté en JythonGreeterInterface.
Ne pas oublier d’ajouter la librairie jython.jar au classpath de l’application Java.

jythonGreeter.py
    # coding=UTF-8
     
    import JythonGreeterInterface
     
    # On définit ici la classe Python comme implémentant une interface définie en Java
    # afin qu'elle soit visible du code Java
    class JythonGreeter(JythonGreeterInterface):
        __defaultHello = "World"
     
        @staticmethod
        def sayHello(helloWho=__defaultHello):
            # Pour faire le parallèle avec le println de Java
            # on doit passer une String en paramètre
            print("Hello " + helloWho + " !")
JythonGreeterInterface.java
    // On définit une interface afin que Java connaisse les méthodes de la classe Python
    public interface JythonGreeterInterface {
     
    	// Notez qu'on définit les deux variantes, avec et sans paramètre, de la méthode Python
    	// alors que côté Python, on a une seule méthode, capable d'initialiser le paramètre par une valeur par défaut
    	public void sayHello();
    	public void sayHello(String helloWho);
     
    }
JythonObjectFactory.java
    import org.python.core.Py;
    import org.python.core.PyObject;
    import org.python.core.PySystemState;
     
    /**
     * Jython Object Factory utilisant PySystemState
     * Ce factory permet d'instancier des objets Python pour les rendre utilisables par Java
     * http://www.jython.org/jythonbook/en/1.0/JythonAndJavaIntegration.html#more-efficient-version-of-loosely-coupled-object-factory
     */
    public class JythonObjectFactory {
     
     private final Class interfaceType;
     private final PyObject klass;
     
     // Constructor obtains a reference to the importer, module, and the class name
     public JythonObjectFactory(PySystemState state, Class interfaceType, String moduleName, String className) {
         this.interfaceType = interfaceType;
         PyObject importer = state.getBuiltins().__getitem__(Py.newString("__import__"));
         PyObject module = importer.__call__(Py.newString(moduleName));
         klass = module.__getattr__(className);
         System.err.println("module=" + module + ",class=" + klass);
     }
     
     public JythonObjectFactory(Class interfaceType, String moduleName, String className) {
         this(new PySystemState(), interfaceType, moduleName, className);
     }
     
    /** Instancie un objet Python sans être limité par le nombre de paramètres **/
     public Object createObject(Object args[], String keywords[]) {
         PyObject convertedArgs[] = new PyObject[args.length];
         for (int i = 0; i < args.length; i++) {
             convertedArgs[i] = Py.java2py(args[i]);
         }
     
         return klass.__call__(convertedArgs, keywords).__tojava__(interfaceType);
     }
     
     public Object createObject(Object... args) {
         return createObject(args, Py.NoKeywords);
     }
     
    }
JythonCaller.java
    public class JythonCaller {
     
    	public static void main (String args[]) {
    		JythonObjectFactory factory = new JythonObjectFactory(JythonGreeterInterface.class, "jythonGreeter", "JythonGreeter");
    		JythonGreeterInterface jythonGreeter = (JythonGreeterInterface) factory.createObject();
     
    		jythonGreeter.sayHello();
    		jythonGreeter.sayHello("Lea");
    	}
     
    }
$ javac -cp ./jython.jar *.java
$ java -cp ./jython.jar:. JythonCaller
Hello World !
Hello Lea !

Si on souhaite conserver le caractère duck-typing et appeler le code Python avec des instances de différentes classes, on peut modifier l’interface afin de:

  • soit surcharger la méthode Python avec chaque type attendu en paramètre
  • soit utiliser un objet Object en paramètre

Avec C#/Mono

IronPython est un interpréteur pour inclure cette fois des librairies .Net. Ecrit en C#, il peut également exécuter du code Python dans une application C#/Mono.

Appeler du code C#/Mono depuis Python

Contrairement à l’import de code Java qui ne nécessite pas de lignes Python supplémentaires, il est ici nécessaire d’importer explicitement les librairies .Net compilées.

MonoGreeter.cs
    using System;
     
    public class MonoGreeter
    {
    	private readonly static string _defaultHello = "World";
     
    	static public void SayHello (String helloWho)
        {
    		if (helloWho == null) helloWho = _defaultHello;
    		Console.WriteLine("Hello " + helloWho + " !");
    	}
     
    	static public void Main () {}
    }
monoCaller.py
    # coding=UTF-8
    # Pour importer une classe Mono, elle doit d'abord être compilée vers une dll
    # puis cette dll doit être ajoutée explicitement au programe Python
    import clr
    clr.AddReference("MonoGreeter.dll")
    import MonoGreeter
     
    MonoGreeter.SayHello(None)
    MonoGreeter.SayHello("Eve")
$ mcs MonoGreeter.cs -out:MonoGreeter.dll
$ ipy monoCaller.py
Hello World !
Hello Eve !

Appeler du code Python depuis C#/Mono

C# supportant les types dynamiques, il est bien plus facile d’y inclure du code Python qu’en Java. Un piège cependant est qu’il faut commencer par récupérer le contenu des fichiers Python, avant d’en déduire les classes qu’ils contiennent, à la façon Python (noter l’absence de new).
Ne pas oublier d’ajouter les librairies IronPython et .Net nécessaires à l’application.

ironPythonGreeter.py
    # coding=UTF-8
     
    class IronPythonGreeter:
        __defaultHello = "World"
     
        @staticmethod
        def sayHello(helloWho=__defaultHello):
            # Pour faire le parallèle avec le WriteLine de .Net
            # on doit passer une String en paramètre
            print("Hello " + helloWho + " !")
IronPythonCaller.cs
    // Nécessite les librairies suivantes pour pouvoir appeler le code Python :
    // IronPython.dll
    // IronPython.Modules.dll
    // Microsoft.Dynamic.dll
    // Microsoft.Scripting.dll
    using System;
    using IronPython.Hosting;
    using Microsoft.Scripting.Hosting;
     
    public class IronPythonCaller
    {
    	static void Main()
        {
    		var ipy = Python.CreateRuntime();
     
    		// On récupère d'abord le contenu du fichier
    		dynamic pyGreeter = ipy.UseFile("ironPythonGreeter.py");
     
    		// Puis on récupère une instance de la classe Python
    		dynamic ironPythonGreeter = pyGreeter.IronPythonGreeter();
    		ironPythonGreeter.sayHello();
    		ironPythonGreeter.sayHello("Marcel");
    	}
    }
$ mcs IronPythonCaller.cs -r:IronPython.dll,IronPython.Modules.dll,Microsoft.Scripting.dll,Microsoft.Dynamic.dll
$ mono IronPythonCaller.exe 
Hello World !
Hello Marcel !

Et plus encore…

Il existe également de nombreux autres interpréteurs et compilateurs, permettant de faire tourner du code Python sur encore d’autres environnements (Lisp, Javascript, PHP, etc.). Toutefois, la plupart n’ont pas la même maturité, ni les mêmes fonctionnalités, et ne supportent pas l’intégralité du langage, nécessitant des concessions dans quelques cas.

Documentation

  1. Pas encore de commentaire
  1. Pas encore de trackbacks


× un = 5