views:

2217

answers:

3

Hi all, I have been a python programmer for almost 2 years and I am used to writing small scripts to automate some repetitive tasks I had to do at office. Now, apparently my colleagues noticed this and they want those scripts too.

Some of them have macs, some windows, I made these on windows. I investigated the possibility of using py2exe or even py2app to make natives of my script, but they never satisfied me..

I came to know that all of them have JVM on their systems, so can I give them one single executable jar file of my script using something like jython may be?

How feasible is this... I mean, I had no idea how to write scripts for jython, neither did I care about it when I wrote them... what kind of problems will it give?

+1  A: 

It's a great idea. Good to know that they already have the JVM (as most people do (thanks to facebook)). You won't even have to change your code. Just recompile and your done.

But take 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.
Read more on the Jython FAQ.

Good Luck!

Lucas McCoy
"Just recompile"so, i understand that giving a single jar file to run the script is possible...i have no experience with jython... could you give more details on this please...
Shrikant Sharat
This tell how to compile: http://wiki.python.org/jython/UserGuide#invoking-the-jython-interpreter
Lucas McCoy
It would also be a lot easier if you were on a Unix/Linux based system.
Lucas McCoy
But those guys don't have jython interpreter on their systems... and I dont want to make them have it.. I would've rather made them install cPython.. :)The stuff at the link explains to run the jar as "jython -jar", but I want to run as "java -jar"... so how do I make a jar that can be run like that...?
Shrikant Sharat
It is true that we have no immediate plans to support c-extensions, but I have talked to the implementer of IronClad (IronPython's support for c-extensions) and we plan to take a look at a Jython attempt once IronClad is stable.
Frank Wierzbicki
A: 

The 'jythonc' command should be able to compile your .py source into JVM bytecode, which should make it portable to any Java install. Or so I read at: http://hell.org.ua/Docs/oreilly/other2/python/0596001886_pythonian-chp-25-sect-3.html

Yancy
Thanks for the link.. I didn't know about jythonc, but I happened to learn that jythonc is not maintained anymore... http://www.jython.org/archive/22/jythonc.htmlI could not find any help as to how to use the jython command to make jars... any help on that appreciated
Shrikant Sharat
+12  A: 

The best current techniques for distributing your Python files in a jar are detailed in this article on Jython's wiki: http://wiki.python.org/jython/JythonFaq/DistributingJythonScripts

For your case, I think you would want to take the jython.jar file that you get when you install Jython and zip the Jython Lib directory into it, then zip your .py files in, and then add a __run__.py file with your startup logic (this file is treated specially by Jython and will be the file executed when you call the jar with "java -jar").

This process is definitely more complicated then in ought to be, and so we (the Jython developers) need to come up with a nice tool that will automate these tasks, but for now these are the best methods. Below I'm copying the recipe at the bottom of the above article (modified slightly to fit your problem description) to give you a sense of the solution.

Create the basic jar:

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

Add other modules to the jar:

$ 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

Add the 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

On MS Windows, that last line, setting the CLASSPATH environment variable, would look something like this:

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

Or, again on MS Windows, use the Control Panel and the System properties to set the CLASSPATH environment variable.

Run the application:

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

Or, if you have added your start-up script to the jar, use one of the following:

$ 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

The double -jar is kind of annoying, so if you want to avoid that and get the more pleasing:

$ java -jar myapp.jar arg1

You'll have to do a bit more work until we get something like this into a future Jython [Update: JarRunner is part of Jython 2.5.1]. Here is some Java code that looks for the __run__.py automatically, and runs it. Note that this is my first try at this class. Let me know if it needs improvement!

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);
    }
}

I put this code into the org.python.util package, since that's where it would go if we decide to include it in a future Jython. To compile it, you'll need to put jython.jar (or your myapp.jar) into the classpath like:

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

Then you'll need to add JarRunner.class to your jar (the class file will need to be in org/python/util/JarRunner.class) calling jar on the "org" directory will get the whole path into your jar.

$ jar uf org

Add this to a file that you will use to update the manifest, a good name is manifest.txt:

Main-Class: org.python.util.JarRunner

Then update the jar's manifest:

$ jar ufm myapp.jar manifest.txt

Now you should be able to run your app like this:

$ java -jar myapp.jar
Frank Wierzbicki
hey Frank, thanks for the insight, really.. one thing, I am getting an error at the command "zip myapp.jar Lib/showobjs.py".. what is it supposed to do?
Shrikant Sharat
apparently, the jython interpreter has to be invoked before my script can do anything.. which tells me that "java -jar MyScript.jar" to run my python script is not possible.Thank you so much Frank!
Shrikant Sharat
You should be able to set things up so that "java -jar MyScript.jar" should work. Later today I'll edit my steps to make a more concrete example.
Frank Wierzbicki
Okay, I was wrong -- you do need call your distributed jar twice, so you end up with: "java -jar myapp.jar -jar myapp.jar arg1". That's very unsatisfying, and doesn't seem like it would be too hard to fix. I'll put this on my todo list to check out for 2.5.1.
Frank Wierzbicki
BTW, I'll be back here with some kind of solution. I think a custom Java class based off of org.python.util.jython and an altered manifest file will do the trick. It may be a day or two before I can find the time, but stay tuned :)
Frank Wierzbicki
Just so I don't leave this as "stay tuned" I added a Java class to this post that will allow "java -jar myapp.jar" to work.
Frank Wierzbicki
Hey Frank, I am sorry that I am so late, thank you so much for your efforts. I tried the above method, and what I achieved was, my __run__.py is being run if i say 'java -classpath myapp.jar org.python.util.JarRunner' but not with 'java -jar myapp.jar', in the latter case, I get a jython prompt. I think it is a mistake in the manifest.. 'Main-Class: org.python.util.JarRunner' is all my manifest file has... should it contain anything else?
Shrikant Sharat
oops, my bad, I had to change the Main-Class attribute in the manifest.mf file that is already placed (by you?) in the META-INF directory inside the jar... and voila, it works now, thanks a lot Frank :)
Shrikant Sharat