Подтвердить что ты не робот

Как интегрировать автономный Python script в приложение Rails?

У меня есть программа, которая имеет небольшую файловую структуру, и затем выполняется с помощью

python do_work.py foo bar

Я хочу, чтобы мои пользователи Rails нажали кнопку, и это произошло для них, результат был загружен где-то или просто добавлен к ним в качестве ссылки для загрузки или что-то в этом роде - вывод do_work.py (скажем, это result.txt)

Я также хочу пояснить, что script приводит к созданию в файловой системе трех отдельных файлов, которые не являются текстовыми файлами (что не имеет значения и на самом деле не проблема)

Каков наилучший способ сделать это? Может ли rake запустить exec Python? Что еще более важно, можно ли справиться с героикой?

У меня есть Python, установленный в моей системе, но предоставленный ответ sockmonk, похоже, не работает - он возвращает nil. Имейте в виду, что другие команды вроде ls, похоже, работают.

Может быть проблема с разрешениями?

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

Кстати, попробуйте это в irb:

%x(python)

Подключает терминал Python INSIDE от irb. Однако он не будет принимать параметры по какой-либо причине.

4b9b3361

Ответ 1

Ваш метод index не работает, потому что python --version выводит свою версию на STDERR, а не STDOUT. Если вам не нужно разделять эти потоки, вы можете просто перенаправить STDERR в STDOUT:

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

Этот вызов является синхронным, поэтому после запуска script (python do_work.py foo bar 2>&1) вы должны будете прочитать файлы, созданные им.

Если script не может создать файлы по какой-либо причине, теперь вы увидите исключение в переменной value, потому что сообщения об ошибках обычно отправляются в STDERR.

Если вы хотите отделить STDERR от STDOUT, используйте модуль Open3.

Помните, что для выполнения script требуется некоторое время, поэтому вызовы могут перекрываться. Я бы использовал здесь очередь для предотвращения этого.

И не забудьте проверить данные, которые вводит пользователь. Никогда не передавайте его непосредственно на script.

Ответ 2

Отчасти это зависит от формата данных. Если он не слишком длинный и может быть отображен непосредственно в браузере, вы можете просто сделать что-то вроде этого в контроллере rails:

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

И если предположить, что результатом является простой текст ASCII, результат будет передан прямо в их браузер. Если параметры do_work.py исходят от пользователя, вы должны сначала подтвердить их, но не заставляете себя создавать неприятную уязвимость для себя. В этом случае использование вызова system(), вероятно, будет более безопасным.

Если вы хотите отправить результаты обратно в файл, посмотрите на класс рубин TempFile для создания файла (таким образом, что не будет придерживаться вокруг навсегда), и send_file и send_data команды Rails, для некоторых различных вариантов для отправки верните результаты таким образом.

Ответ 3

Ответ от utapyngo охватывает почти все, что вам нужно знать. Я отвечу на эту часть:

кстати, попробовав это в irb: % х (Python) Выводит терминал python INSIDE из irb. Однако он не будет принимать параметры по какой-либо причине.

Чтобы передать параметры вашему python script, просто передайте его. Пример:

[[email protected] ~]$ python a.py 
args:
['a.py']
[[email protected] ~]$ 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 script. Существуют способы фактического вызова модулей python непосредственно из ruby.

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

Это даст вам преимущество получить результат непосредственно из вашего python script вместо перехода через устройство ввода/вывода.

Ответ 5

Я сделал бы что-то вроде следующего.

Асинхронно выполнять эту задачу в фоновом режиме. И как только результат будет готов сообщить об этом пользователю.

Один из способов добиться этого можно, используя Open3 и delayed_job gem.

Посмотрите popen3 метод в модуле Open3.

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 script

И затем используйте затухание delayed_job, чтобы запустить это как фоновое задание.

У вас также должен быть механизм опроса, который будет опросить систему, чтобы увидеть, установлен ли флаг, который будет означать, что результат готов, а затем обслуживает его для пользователя.