使用 Jython 将我的 Python 脚本作为 JAR 文件分发?

Posted

技术标签:

【中文标题】使用 Jython 将我的 Python 脚本作为 JAR 文件分发?【英文标题】:Distributing my Python scripts as JAR files with Jython? 【发布时间】:2010-11-18 04:28:49 【问题描述】:

我已经做了将近两年的 Python 程序员,我习惯于编写小脚本来自动化一些我必须在办公室完成的重复性任务。现在,显然我的同事注意到了这一点,他们也想要这些脚本。

其中一些拥有 Mac,一些拥有 Windows;我在窗户上做的。我调查了使用 py2exe 甚至 py2app 来制作我的脚本的本地人的可能性,但他们从来没有让我满意......

我知道他们所有人的系统上都有 JVM,所以我可以使用 Jython 之类的东西给他们一个我的脚本的可执行 JAR 文件吗?

这有多可行...我的意思是,我不知道如何为 Jython 编写脚本,我在编写它们时也没有在意...它会带来什么样的问题?

【问题讨论】:

【参考方案1】:

在 Jython 的 wiki 上的这篇文章中详细介绍了在 jar 中分发 Python 文件的当前最佳技术:http://wiki.python.org/jython/JythonFaq/DistributingJythonScripts

对于您的情况,我认为您需要获取安装 Jython 时获得的 jython.jar 文件并将 Jython Lib 目录压缩到其中,然后将 .py 文件压缩到其中,然后添加 __run__.py包含您的启动逻辑的文件(此文件由 Jython 特殊处理,当您使用“java -jar”调用 jar 时将执行该文件)。

这个过程肯定比应该的要复杂得多,因此我们(Jython 开发人员)需要想出一个很好的工具来自动化这些任务,但目前这些是最好的方法。下面我将复制上述文章底部的配方(稍作修改以适合您的问题描述),让您了解解决方案。

创建基本jar:

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

将其他模块添加到 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

添加__run__.py 模块:

# 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

在 MS Windows 上,设置 CLASSPATH 环境变量的最后一行看起来像这样:

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

或者,再次在 MS Windows 上,使用控制面板和系统属性来设置 CLASSPATH 环境变量。

运行应用程序:

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

或者,如果您已将启动脚本添加到 jar 中,请使用以下方法之一:

$ 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

双罐有点烦人,所以如果你想避免这种情况并获得更令人愉悦的体验:

$ java -jar myapp.jar arg1

在我们将类似的东西引入未来的 Jython [更新:JarRunner 是 Jython 2.5.1 的一部分]之前,您必须做更多的工作。这是一些自动查找__run__.py 并运行它的Java 代码。请注意,这是我第一次尝试这门课。如果需要改进,请告诉我!

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

我将此代码放入 org.python.util 包中,因为如果我们决定将它包含在未来的 Jython 中,它会放在那里。要编译它,您需要将 jython.jar(或您的 myapp.jar)放入类路径中,例如:

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

然后你需要将 JarRunner.class 添加到你的 jar 中(类文件需要在 org/python/util/JarRunner.class 中)在“org”目录中调用 jar 会将整个路径放入你的罐子。

$ jar uf org

将此添加到您将用于更新清单的文件中,一个好名字是 manifest.txt:

Main-Class: org.python.util.JarRunner

然后更新 jar 的清单:

$ jar ufm myapp.jar manifest.txt

现在您应该可以像这样运行您的应用了:

$ java -jar myapp.jar

【讨论】:

嘿弗兰克,感谢您的洞察力,真的.. 有一件事,我在命令“zip myapp.jar Lib/showobjs.py”时遇到错误.. 它应该做什么? 显然,必须先调用 jython 解释器,然后我的脚本才能执行任何操作。这告诉我“java -jar MyScript.jar”无法运行我的 python 脚本。非常感谢弗兰克! 您应该能够进行设置,以便“java -jar MyScript.jar”可以正常工作。今天晚些时候,我将编辑我的步骤以制作一个更具体的示例。 好吧,我错了——你确实需要调用你的分布式 jar 两次,所以你最终得到:“java -jar myapp.jar -jar myapp.jar arg1”。这非常令人不满意,而且似乎不太难修复。我将把它放在我的待办事项列表中以检查 2.5.1。 顺便说一句,我会带着某种解决方案回到这里。我认为基于 org.python.util.jython 的自定义 Java 类和修改后的清单文件可以解决问题。我可能需要一两天才能找到时间,但请继续关注:)【参考方案2】:

我遇到了类似的问题,我希望能够为我的 jython 应用程序创建简单的命令行调用,不需要用户完成 jython 安装过程,并且能够让 jython 脚本附加库依赖项运行时到 sys.path 以便包含核心 java 代码。

# 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"))

在命令行上显式运行“jython”启动器时,在 Unix 系统上,它只是运行一个大的 shell 脚本来正确地形成一个 java 命令行调用。这个 jython 启动器似乎依赖于返回到 jython 的核心安装,并且通过某种魔法允许在运行时从我的 .py 脚本中正确处理添加到 sys.path 的 .jar 文件。您可以通过以下方式查看调用是什么并阻止执行:

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

但它仍然只是启动 JVM 并运行类文件。所以我的目标是能够对我的发行版的 lib 目录中存在的独立 jython.jar 进行这个 java 调用,这样用户就不需要执行任何额外的安装步骤来开始使用我的 .py 脚本实用程序。

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

问题在于行为完全不同,以至于我会得到这样的响应:

  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

现在你可能会说我应该只将 jar 文件添加到 -classpath,实际上我尝试过,但我会得到相同的结果。

将所有 .class 文件捆绑在一个 jython.jar 中的建议对我来说根本没有吸引力。这将是一团糟,并且会将 Java/Python 混合应用程序过于紧密地绑定到 jython 发行版。所以这个想法是行不通的。最后,经过大量搜索,我在 jython.org 上遇到了 bug #1776,它已被列为关键一年半,但我没有看到 jython 的最新更新包含修复。不过,如果您在让 jython 包含单独的 jar 文件时遇到问题,您应该阅读这篇文章。

http://bugs.jython.org/issue1776

在那里,您会找到解决此问题的临时解决方法。在我的例子中,我将 Apache POI jar 文件解压缩到自己单独的 lib 目录中,然后修改 sys.path 条目以指向该目录而不是 jar:

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

现在,当我通过 java 运行 jython 并引用我的本地 jython.jar 时,该实用程序运行得非常好。现在,我可以创建简单的脚本或批处理文件,为我的 .py 实用程序提供无缝的命令行体验,用户无需任何额外的安装步骤即可运行。

【讨论】:

这篇文章很好,感谢您分享您的经验和知识。我也一直在使用 jython 并遇到与您相同的问题。 (+1)【参考方案3】:

“jythonc”命令应该能够将您的 .py 源代码编译为 JVM 字节码,这应该使其可移植到任何 Java 安装。或者我读到:http://hell.org.ua/Docs/oreilly/other2/python/0596001886_pythonian-chp-25-sect-3.html

【讨论】:

感谢链接..我不知道 jythonc,但我碰巧得知 jythonc 不再维护了...jython.org/archive/22/jythonc.html我找不到任何关于如何使用的帮助用于制作 jars 的 jython 命令......对此的任何帮助表示赞赏【参考方案4】:

要以不需要安装本机 Python 的方式分发您的 Python 脚本,您还可以尝试 Nuitka,它基本上将您的 Python 代码转换为 C++ 代码,然后将其编译为真正的本机二进制文件。

【讨论】:

以上是关于使用 Jython 将我的 Python 脚本作为 JAR 文件分发?的主要内容,如果未能解决你的问题,请参考以下文章

jython zxJDBC:如何从游标中获取字典?

java 调用 python脚本 ImportError: No module named os

Jython的简单使用

Java集成Jython及动态执行指定脚本

Java集成Jython及动态执行指定脚本

JMeter中的模块ImportError