【问题标题】:How to integrate a standalone Python script into a Rails application?如何将独立的 Python 脚本集成到 Rails 应用程序中?
【发布时间】:2013-04-24 17:36:02
【问题描述】:

我有一个程序,它有一个小文件结构,然后运行使用

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 终端。但是,无论出于何种原因,它都不会使用参数。

【问题讨论】:

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


【解决方案1】:

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

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

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

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

【讨论】:

  • 另外,我似乎无法让示例在裸轨应用程序上运行
  • 为什么system() 会更安全?反引号只是system() 的别名。
  • 反引号对反引号中的任何内容进行字符串插值,因此如果它包含用户输入,则类似于 SQL 注入攻击的错误输入可能会溜进来。通过使用具有多个参数的系统,你会很更明确地说明命令是什么,参数是什么以及它们之间的界限,所以这种攻击也不起作用。不利的一面是,system() 返回退出状态而不是像反引号那样的输出,因此输出必须存储在某个地方才能呈现给用户。
【解决方案2】:

您的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 模块。

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

别忘了检查用户输入的数据。切勿将其直接传递给脚本。

【讨论】:

  • 你能评论一下如何将它与空灵的 /tmp/filesystem 联系起来,以便将文件保存在那里吗?
  • 您是指临时可写文件系统吗?看到这个问题:How to use Heroku's ephemeral filesystem?。您只需要在请求期间处理文件,因为在处理请求后它们可能会被删除。
【解决方案3】:

utapyngo 的答案几乎涵盖了您需要知道的所有内容。我会回答这部分:

顺便说一下,在 irb 中尝试这个: %x(蟒蛇) 在 irb 内部打开 python 终端。但是,无论出于何种原因,它都不会使用参数。

要将参数传递给您的 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。

【讨论】:

    【解决方案4】:

    这取决于您想要集成 Python 脚本的深度。有很多方法可以直接从 ruby​​ 调用 python 模块。

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

    这将使您受益于直接从 python 脚本获取输出,而不是通过 I/O 设备。

    【讨论】:

      【解决方案5】:

      我会做如下的事情。

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

      您可以通过使用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 将其作为后台任务运行。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-09-03
        • 2018-07-09
        • 1970-01-01
        • 2011-08-03
        • 1970-01-01
        • 2018-07-21
        • 2012-12-20
        • 1970-01-01
        相关资源
        最近更新 更多