如何将独立的Python脚本集成到Rails应用程序中?

时间:2013-04-30 17:49:48

标签: python ruby-on-rails ruby heroku rake

我有一个程序,其中包含一个小文件结构,然后使用

运行
python do_work.py foo bar

我希望我的Rails用户按下按钮并为他们发生这种情况,结果要么上传到某个地方,要么只是作为下载链接或类似的东西抛出 - do_work.py的输出(比如说,它是result.txt

我还想澄清一下,脚本导致在文件系统上创建3个独立的文件,这些文件不是文本文件(这应该不重要,这里不是问题)

最好的方法是什么?可以rake运行exec Python吗?更重要的是,这在heroku上是可行的吗?

我在我的系统上安装了Python,但sockmonk提供的答案似乎不起作用 - 它返回nil。请注意,ls等其他命令似乎有效。

这可能是权限问题吗?

def index
    value = %x( python --version )
    render :text => value
end

顺便说一下,在irb中尝试此操作:

%x(python)

打开irb的Python终端INSIDE。无论出于什么原因,它都不会占用参数。

5 个答案:

答案 0 :(得分:26)

您的index方法不起作用,因为python --version将其版本输出到STDERR,而不是STDOUT。如果您不需要分离这些流,则可以将STDERR重定向到STDOUT:

value = %x(python --version 2>&1)

此调用是同步的,因此在运行脚本(python do_work.py foo bar 2>&1)之后,您应该能够读取它生成的文件。

如果脚本由于某种原因无法创建文件,您现在将在value变量中看到异常,因为错误消息通常会发送到STDERR。

如果要将STDERR与STDOUT分开,请使用Open3模块。

请注意脚本需要一些时间才能运行,因此调用可能会重叠。我会在这里使用一个队列来阻止这种情况。

不要忘记检查用户输入的数据。永远不要将它直接传递给脚本。

答案 1 :(得分:4)

部分取决于数据的格式。如果它不是太长并且可以直接在浏览器中呈现,您可以在rails控制器中执行类似的操作:

result = `python do_work.py foo bar`
render :text => result

假设结果是纯ASCII文本,结果将直接显示在浏览器中。如果do_work.py的参数来自用户,你必须首先验证它们,所以你最终不会为自己创建一个讨厌的漏洞。在这种情况下,使用system()调用可能会更安全。

如果要将结果作为文件发回,请查看ruby的Tempfile类以创建文件(以不会永远存在的方式),并使用rails的send_file和send_data命令发送一些不同的选项以这种方式支持结果。

答案 2 :(得分:3)

utapyngo的回答几乎涵盖了你需要知道的全部内容。我会回答这一部分:

  

顺便说一下,在irb中尝试这个:   %×(蟒)   打开irb的python终端INSIDE。无论出于什么原因,它都不会占用参数。

要将参数传递给python脚本,只需传递它即可。例如:

[fotanus@thing ~]$ python a.py 
args:
['a.py']
[fotanus@thing ~]$ irb
1.8.7 :001 > %x(python a.py foo bar)
 => "args:\n['a.py', 'foo', 'bar']\n" 

这适用于ruby 1.8,1.9和2.0。

答案 3 :(得分:1)

这取决于你想要整合python脚本的深度。有一些方法可以直接从ruby中调用python模块。

http://www.goto.info.waseda.ac.jp/~fukusima/ruby/python-e.html

这样可以直接从python脚本获取输出,而不是通过I / O设备。

答案 4 :(得分:1)

我会做以下事情。

在后台异步执行此任务。一旦结果准备就绪,报告给用户。

使用Open3delayed_job gem。

可以实现这一目标

在Open3模块中查看popen3方法。

Open3.popen3([env,] cmd... [, opts]) {|stdin, stdout, stderr, wait_thr|
  pid = wait_thr.pid # pid of the started process.
  ...
  exit_status = wait_thr.value # Process::Status object returned.
}

在您的情况下,您可以将Open3.popen3语句更改为以下内容

Open3.popen3("python do_work.py foo bar"){
  ...
  # mechanism for reporting like setting a flag in database system
  # or queue system
}

注意:您应该提供python脚本的完整路径

然后使用delayed_job gem将其作为后台任务运行。

您还应该有一个轮询机制,它将轮询系统以查看是否设置了标志,这意味着结果已准备就绪,然后将其提供给用户。