web-dev-qa-db-ja.com

スタンドアロンのPythonスクリプトをRailsアプリケーションに統合する方法は?

小さなファイル構造が実行されているプログラムがあり、それを使用して実行されます

python do_work.py foo bar

Railsユーザーにボタンを押して、これを実行してもらいたいのですが、結果はどこかにアップロードされるか、ダウンロードリンクなどとしてユーザーにスローされます-do_work.pyの出力(たとえば、それはresult.txtです)

また、スクリプトによって、テキストファイルではない3つの別々のファイルがファイルシステム上に作成されることを明確にしたい(これは問題ではなく、ここでは実際には問題ではない)。

それについて行くための最良の方法は何ですか? rakeはexecPythonを実行できますか?さらに重要なことに、これはherokuで実行できますか?

私のシステムにはPythonがインストールされていますが、sockmonkによって提供された回答が機能しないようです-nilを返します。lsのような他のコマンドは機能するようです。

権限の問題でしょうか?

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

ちなみに、これをirbで試してみてください。

%x(python)

Irbの内部でPythonターミナルを起動します。ただし、何らかの理由でparamsを使用しません。

30
dsp_099

indexメソッドは、python --versionバージョンをSTDOUTではなくSTDERRに出力します。これらのストリームを分離する必要がない場合は、STDERRをSTDOUTにリダイレクトするだけです。

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

この呼び出しは同期的であるため、スクリプトを実行した後(python do_work.py foo bar 2>&1)、それによって生成されたファイルを読み取ることができるはずです。

スクリプトが何らかの理由でファイルを作成できない場合、エラーメッセージは通常STDERRに送信されるため、value変数に例外が表示されます。

STDERRをSTDOUTから分離する場合は、 Open モジュールを使用します。

スクリプトの実行には時間がかかるため、呼び出しが重複する可能性があることに注意してください。これを防ぐために、ここでキューを使用します。

また、ユーザーが入力したデータを確認することを忘れないでください。スクリプトに直接渡さないでください。

26
utapyngo

それは部分的にデータのフォーマットに依存します。長すぎず、ブラウザーで直接レンダリングできる場合は、Railsコントローラー:

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

そして、結果がプレーンASCII textであると仮定すると、結果はブラウザに直接送信されます。do_work.pyのパラメータがユーザーからのものである場合は、最初に検証する必要があるため、検証しないでください。その場合、system()呼び出しを使用する方がおそらく安全です。

結果をファイルとして送り返したい場合は、ファイルを作成するためのRubyのTempfileクラス(永遠に固執しない方法で)、および結果を送り返すためのいくつかの異なるオプションについてのRailsのsend_fileおよびsend_dataコマンドを調べてください。そのように。

4
sockmonk

Utapyngoからの答えはあなたが知る必要があるほとんどすべてをカバーすることです。私はこの部分に答えます:

ちなみに、これをirbで試してみると:%x(python)irbの内部にpythonターミナルが表示されます。ただし、何らかの理由でparamsは使用されません。

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
fotanus

私は次のようなことをします。

このタスクをバックグラウンドで非同期的に実行します。そして、結果の準備ができたら、それをユーザーに報告します。

これを実現する1つの方法は、 Open および delayed_job gemを使用することです。

Open3モジュールの popen メソッドを見てください。

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を使用して、これをバックグラウンドタスクとして実行します。

また、システムをポーリングして、結果の準備ができていることを意味するフラグが設定されているかどうかを確認し、それをユーザーに提供するポーリングメカニズムも必要です。

1
Josnidhin

pythonスクリプトをどの程度深く統合するかによって異なります。Rubyから直接pythonモジュールを呼び出す方法があります。

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

これにより、I/Oデバイスを経由する代わりに、pythonスクリプトから直接出力を取得できるという利点があります。

1
Bjoern Rennhak