Ошибка сегментации wkhtmltopdf при запуске из python

Мне нужно запустить wkhtmltopdf из python с помощью subprocess.call(...). Из командной строки я могу без проблем сгенерировать pdf, но когда он запускается из python, он терпит неудачу с segfault.

Я понятия не имею, что вызывает wkhtmltopdf segfault.

Я даже пытался отправить атрибуты env своего терминала, но он все равно дает сбои. Я отправил stderr, stdin, stdout, но ничего не работает. Что меня беспокоит, так это то, что он запускается с терминала, а не с python.

Кроме того, вызов процесса из другого процесса в python также приводит к segfault. Например, я добавил промежуточный скрипт для вызова этого приложения, а скрипт, написанный на python, также получает segfault от wkhtmltopdf.

#!/bin/env python
import subprocess
import sys
import pdb
import os


sys.argv[0] = "/usr/local/bin/wkhtmltopdf.b"

sys.argv.remove('--quiet')

status = subprocess.call(sys.argv,
    env=env,
    stdin=sys.stdin,
    stdout=open("/tmp/stdout.w", "w"),
    stderr=open("/tmp/stderr.w", "w"))

cmd = " ".join(sys.argv)

pdb.set_trace()

Сейчас я делаю это, чтобы получить время для выполнения команды во внешнем терминале. OpenErp проверяет содержимое pdf-файла. wkhtmltopdf.b — исходный двоичный файл. Я удалил тихий параметр, так как хотел посмотреть, что происходит.

По-видимому, в этот момент он ошибается:

Loading pages (1/6)
[======>                                                     ] 10%

И ничего больше

Моя версия wkhtmltopdf amd64 static с сайта wkhtmltopdf.org

$ wkhtmltopdf -V
wkhtmltopdf 0.12.1 (with patched qt)

Я запускаю один из бинарных пакетов ubuntu amd64 на моем gentoo box. Довольно сложно/долго компилировать wkhtmltopdf с исправленным qt в gentoo, похоже, он не поддерживается по умолчанию. Тем не менее, поскольку он запускается из командной строки, он также должен запускаться из python.

Я запускаю его из zsh, но даже если внутри моей программы на Python я бы вместо этого назвал что-то вроде этого:

'/bin/sh -c "%s"' % command

Это также будет segfault.


person Loïc Faure-Lacroix    schedule 05.12.2014    source источник
comment
Подпроцесс Python запускает новую оболочку, которая, вероятно, не наследует некоторые параметры оболочки и переменные среды. Например, у вас может быть LD_LIBRARY_PATH в вашей командной строке, указывающий на более новую библиотеку, которую использует wkhtmltopdf, в то время как этот путь отсутствует при запуске через subprocess, и, таким образом, wkhtmltopdf возвращается к более старой библиотеке, что вызывает segfault. Попробуйте найти одну или несколько переменных оболочки/среды, которые заставляют wkhtmltopdf работать правильно в командной строке.   -  person    schedule 05.12.2014
comment
@Evert Я думал об этом. Я обновил словарь env результатом env с моего терминала. Я вырезал его, потому что он довольно большой. Но env такой же, как в моем терминале.   -  person Loïc Faure-Lacroix    schedule 06.12.2014
comment
Просто для двойной проверки распечатайте свою среду при запуске из командной строки или при запуске из подпроцесса.   -  person    schedule 06.12.2014
comment
Можете ли вы указать свою версию Python, ОС, тип и версию оболочки и версию wkhtmltopdf?   -  person    schedule 06.12.2014


Ответы (2)


У меня была точно такая же проблема, как и у вас, но я работал с другим стеком (Apache и PHP), но я не уверен на 100%, как вы на самом деле запускаете свой python. В любом случае, он разбился точно в том же месте, что и ваш, и отлично работал из командной строки, поэтому я думаю, что стоит поделиться, если это кому-то поможет;)

Я обнаружил, что моя проблема заключалась в том, что ulimit был установлен по-разному при запуске через apache, а затем через оболочку. В частности, моя «виртуальная память» ulimit -v была довольно низкой. В итоге я делаю $cmd = "ulimit -v 1073741824; {$this->wkhtmltopdf_path}...." и это решает мою проблему! (Вы можете проверить, запустив ulimit -a и сравнив значения той же команды в оболочке!)

person ROunofF    schedule 24.03.2015
comment
Я должен попробовать это. - person Loïc Faure-Lacroix; 24.03.2015
comment
Хорошо, не могу сказать, что это на 100% правильно в моем случае, потому что ulimit не будет иметь никакого эффекта в odoo ... но я изменил параметры, которые контролируют использование памяти, и установил ulimit на более 1 ГБ оперативной памяти, чтобы решить проблему. - person Loïc Faure-Lacroix; 12.04.2016

Попробуйте передать строку HTML через стандартный ввод. Вот пример, за которым следует ответ на загрузку.

from subprocess import Popen, PIPE, STDOUT
from django.core.files.temp import NamedTemporaryFile
from django.template.loader import render_to_string
from django.http import HttpResponse

tmp = NamedTemporaryFile()
html = render_to_string('your-template.html', context)
p = Popen(['wkhtmltopdf', '-', tmp.name], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
out, err = p.communicate(input=(html + u'\n').encode('utf-8'))
# check for errors in 'out' and 'err' -- print out, err
with open(tmp.name, 'r') as pdf:
    pdfcontent = pdf.read()
response = HttpResponse(pdfcontent, content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename=print.pdf'
response['Content-Length'] = len(pdfcontent)
return response

Вам придется использовать полные статические URL-адреса в своих шаблонах, чтобы wkhtmltopdf не смог найти статические файлы CSS и JS.

person vmassuchetto    schedule 23.04.2015