57 votes

Distribuer mes scripts Python comme fichiers JAR avec Jython ?

Je suis programmeur Python depuis presque deux ans, et j'ai l'habitude d'écrire de petits scripts pour automatiser certaines tâches répétitives que j'avais à faire au bureau. Maintenant, apparemment mes collègues l'ont remarqué, et ils veulent ces scripts aussi.

Certains d'entre eux ont des Macs, d'autres des Windows ; j'ai réalisé ceux-ci sur Windows. J'ai étudié la possibilité d'utiliser py2exe ou même py2app pour faire des natifs de mon script, mais ils ne m'ont jamais satisfait...

J'ai appris qu'ils ont tous une JVM sur leurs systèmes, alors puis-je leur donner un seul fichier JAR exécutable de mon script en utilisant quelque chose comme Jython peut-être ?

A quel point c'est faisable... Je veux dire, je n'avais aucune idée de comment écrire des scripts pour Jython, et je ne m'en souciais pas non plus quand je les écrivais... quel genre de problèmes cela va-t-il donner ?

66voto

Frank Wierzbicki Points 1096

Les meilleures techniques actuelles pour distribuer vos fichiers Python dans un jar sont détaillées dans cet article sur le wiki de Jython : http://wiki.python.org/jython/JythonFaq/DistributingJythonScripts

Dans votre cas, je pense que vous devriez prendre le fichier jython.jar que vous obtenez lors de l'installation de Jython et y placer le répertoire Jython Lib, puis y placer vos fichiers .py, et enfin ajouter une balise __run__.py avec votre logique de démarrage (ce fichier est traité spécialement par Jython et sera le fichier exécuté lorsque vous appellerez le jar avec "java -jar").

Ce processus est définitivement plus compliqué qu'il ne devrait l'être, et donc nous (les développeurs de Jython) devons trouver un bon outil qui automatisera ces tâches, mais pour l'instant ce sont les meilleures méthodes. Je copie ci-dessous la recette en bas de l'article ci-dessus (légèrement modifiée pour s'adapter à la description de votre problème) pour vous donner une idée de la solution.

Créez le pot de base :

$ cd $JYTHON_HOME
$ cp jython.jar jythonlib.jar
$ zip -r jythonlib.jar Lib

Ajoutez d'autres modules au bocal :

$ cd $MY_APP_DIRECTORY
$ cp $JYTHON_HOME/jythonlib.jar myapp.jar
$ zip myapp.jar Lib/showobjs.py
# Add path to additional jar file.
$ jar ufm myapp.jar othermanifest.mf

Ajouter le __run__.py module :

# Copy or rename your start-up script, removing the "__name__  == '__main__'" check.
$ cp mymainscript.py __run__.py
# Add your start-up script (__run__.py) to the jar.
$ zip myapp.jar __run__.py
# Add path to main jar to the CLASSPATH environment variable.
$ export CLASSPATH=/path/to/my/app/myapp.jar:$CLASSPATH

Sous MS Windows, cette dernière ligne, qui définit la variable d'environnement CLASSPATH, ressemblerait à quelque chose comme ceci :

set CLASSPATH=C:\path\to\my\app\myapp.jar;%CLASSPATH%

Ou, toujours sous MS Windows, utilisez le Panneau de configuration et les propriétés du système pour définir la variable d'environnement CLASSPATH.

Exécutez l'application :

$ java -jar myapp.jar mymainscript.py arg1 arg2

Ou, si vous avez ajouté votre script de démarrage au jar, utilisez l'un des éléments suivants :

$ java org.python.util.jython -jar myapp.jar arg1 arg2
$ java -cp myapp.jar org.python.util.jython -jar myapp.jar arg1 arg2
$ java -jar myapp.jar -jar myapp.jar arg1 arg2

Le double -jar est un peu ennuyeux, donc si vous voulez éviter cela et obtenir le plus agréable :

$ java -jar myapp.jar arg1

Il vous faudra encore travailler un peu avant que quelque chose de ce genre ne soit intégré à un futur Jython [Mise à jour : JarRunner fait partie de Jython 2.5.1]. Voici un peu de code Java qui recherche le __run__.py automatiquement, et l'exécute. Notez que c'est mon premier essai pour cette classe. Faites-moi savoir s'il faut l'améliorer !

package org.python.util;

import org.python.core.imp;
import org.python.core.PySystemState;

public class JarRunner {

    public static void run(String[] args) {
        final String runner = "__run__";
        String[] argv = new String[args.length + 1];
        argv[0] = runner;
        System.arraycopy(args, 0, argv, 1, args.length);
        PySystemState.initialize(PySystemState.getBaseProperties(), null, argv);
        imp.load(runner);
    }

    public static void main(String[] args) {
        run(args);
    }
}

J'ai placé ce code dans le paquet org.python.util, puisque c'est là qu'il ira si nous décidons de l'inclure dans un futur Jython. Pour le compiler, vous devrez mettre jython.jar (ou votre myapp.jar) dans le classpath comme suit :

$ javac -classpath myapp.jar org/python/util/JarRunner.java

Ensuite, vous devrez ajouter JarRunner.class à votre jar (le fichier de classe devra se trouver dans org/python/util/JarRunner.class). Appeler jar sur le répertoire "org" mettra le chemin complet dans votre jar.

$ jar uf org

Ajoutez-le à un fichier que vous utiliserez pour mettre à jour le manifeste, un bon nom est manifest.txt :

Main-Class: org.python.util.JarRunner

Puis mettez à jour le manifeste du bocal :

$ jar ufm myapp.jar manifest.txt

Maintenant, vous devriez être en mesure d'exécuter votre application comme ceci :

$ java -jar myapp.jar

0 votes

Hé Frank, merci pour les informations, vraiment une chose, j'ai une erreur à la commande "zip myapp.jar Lib/showobjs.py" qu'est-ce que c'est censé faire ?

0 votes

Apparemment, l'interpréteur jython doit être invoqué avant que mon script puisse faire quoi que ce soit ce qui me dit que "java -jar MyScript.jar" pour exécuter mon script python n'est pas possible. Merci beaucoup Frank !

0 votes

Vous devriez être en mesure de configurer les choses de manière à ce que "java -jar MyScript.jar" fonctionne. Plus tard dans la journée, je modifierai mes étapes pour en faire un exemple plus concret.

2voto

Robert Casey Points 88

J'ai rencontré un problème similaire dans la mesure où je veux pouvoir créer des appels de ligne de commande simples pour mes applications jython, ne pas exiger que l'utilisateur passe par le processus d'installation de jython, et pouvoir faire en sorte que les scripts jython ajoutent les dépendances de bibliothèque au moment de l'exécution à sys.path afin d'inclure le code java de base.

# append Java library elements to path
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..", "lib", "poi-3.8-20120326.jar"))

Lorsque vous exécutez le lanceur 'jython' explicitement sur la ligne de commande, sur les systèmes Unix, il exécute simplement un gros shell script pour former correctement un appel de ligne de commande java. Ce lanceur jython semble dépendre d'une installation de base de jython et, par une sorte de magie, permet de gérer correctement les fichiers .jar ajoutés au sys.path au moment de l'exécution à partir de mes script .py. Vous pouvez voir quel est l'appel et bloquer l'exécution par ce qui suit :

jython --print run_form.py
java -Xmx512m -Xss1024k -Dfile.encoding=UTF-8 -classpath /Applications/jython2.5.2/jython.jar: -Dpython.home=/Applications/jython2.5.2 -Dpython.executable=/Applications/jython2.5.2/bin/jython org.python.util.jython run_form.py

Mais il s'agit toujours de lancer une JVM et d'exécuter un fichier de classe. Mon objectif était donc de pouvoir faire cet appel java à un jython.jar autonome présent dans le répertoire lib de ma distribution, afin que les utilisateurs n'aient pas à effectuer d'étapes d'installation supplémentaires pour commencer à utiliser mes utilitaires scriptés .py.

java -Xmx512m -Xss1024k -classpath ../../lib/jython.jar org.python.util.jython run_form.py

Le problème est que le comportement est suffisamment différent pour que je reçoive des réponses comme celle-ci :

  File "run_form.py", line 14, in <module>
    import xls_mgr
  File "/Users/test/Eclipse/workspace/test_code/py/test/xls_mgr.py", line 17, in <module>
    import org.apache.poi.hssf.extractor as xls_extractor
ImportError: No module named apache

Maintenant, vous pourriez dire que je devrais simplement ajouter les fichiers jar au -classpath, ce que j'ai essayé en fait, mais j'obtiendrais le même résultat.

La suggestion de regrouper tous vos fichiers .class dans un jython.jar ne m'a pas du tout semblé attrayante. Ce serait un désordre et cela lierait trop étroitement l'application hybride Java/Python à la distribution jython. Cette idée n'était donc pas prête de se concrétiser. Finalement, après de nombreuses recherches, je suis tombé sur le bug #1776 sur jython.org, qui a été répertorié comme critique depuis un an et demi, mais je ne vois pas si les dernières mises à jour de jython intègrent un correctif. Néanmoins, si vous avez des problèmes avec l'inclusion par jython de vos fichiers jar séparés, vous devriez lire ceci.

http://bugs.jython.org/issue1776

Vous y trouverez la solution de contournement temporaire pour ce problème. Dans mon cas, j'ai pris le fichier jar Apache POI et l'ai désarrimé dans son propre répertoire lib séparé, puis j'ai modifié l'entrée sys.path pour qu'elle pointe vers le répertoire au lieu du jar :

sys.path.append('/Users/test/Eclipse/workspace/test_code/lib/poi_lib')

Maintenant, lorsque je lance jython par le biais de java, en faisant référence à mon jython.jar local, l'utilitaire fonctionne parfaitement. Je peux maintenant créer de simples scripts ou des fichiers batch pour créer une expérience de ligne de commande transparente pour mes utilitaires .py, que l'utilisateur peut exécuter sans aucune étape d'installation supplémentaire.

0 votes

C'est un bon article, merci de partager votre expérience et vos connaissances. Je travaille également avec jython et je rencontre les mêmes problèmes que vous. (+1)

1voto

Lucas Points 13318

C'est une excellente idée. Bon à savoir qu'ils ont déjà la JVM (comme la plupart des gens (grâce à facebook)). Vous n'aurez même pas à modifier votre code. Il suffit de recompiler et vous avez terminé.

Mais prenez note :

There are a number of differences. First, Jython programs cannot use CPython 
extension modules written in C. These modules usually have files with the
extension .so, .pyd or .dll. If you want to use such a module, you should look 
for an equivalent written in pure Python or Java. Although it is technically 
feasible to support such extensions - IronPython does so - there are no plans 
to do so in Jython.Plus d'informations sur le site [FAQ Jython](http://wiki.python.org/jython/JythonFaq/GeneralInfo#IsJythonthesamelanguageasPython.3F) .

Bonne chance !

1voto

Yancy Points 149

La commande 'jythonc' devrait être capable de compiler votre source .py en bytecode JVM, ce qui devrait la rendre portable sur n'importe quelle installation Java. C'est du moins ce que j'ai lu : http://hell.org.ua/Docs/oreilly/other2/python/0596001886_pythonian-chp-25-sect-3.html

3 votes

Merci pour le lien... Je ne connaissais pas jythonc, mais j'ai appris par hasard que jythonc n'est plus maintenu... jython.org/archive/22/jythonc.html Je n'ai pas trouvé d'aide sur la façon d'utiliser la commande jython pour créer des pots... toute aide à ce sujet est appréciée.

1voto

Tobias Kienzler Points 3769

Pour distribuer vos scripts Python d'une manière qui ne nécessite pas une installation native de Python, vous pouvez également essayer Nuitka qui traduit essentiellement votre code Python en code C++, qui est ensuite compilé en un véritable binaire natif.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X