2015-06-08 1 views
0

Я работаю над преобразованием проекта python 2 в python 3.4. Одна часть проекта использует LaTex и подпроцесс для создания PDF-файлов. У меня возникла проблема с получением кода, проходящего через шаг subprocess.Popen.communicate(). Проблема в gen_pdf(), и я думаю, что это cmd.communicate (input = self._gen_latex()), который вызывает проблему. Если я выберу попытку и запустим код напрямую, он будет генерировать ошибку «memoryview: str объект не имеет интерфейса буфера». Но я не мог найти решение, чтобы обойти эту проблему.Python3 LaTex PDF-генератор с использованием подпроцесса, Ошибка: memoryview: объект str не имеет интерфейса буфера

Любая помощь очень ценится. Благодаря!

import django.conf 
import subprocess 
import os 
import tempfile 
import shutil 


class PDFLatexWriter(object): 
    """ 
    Handles creating Latex documents and building them into PDFs. 
    """ 

    def gen_pdf(self): 
     """ 
     Generates the Latex document and writes to tmpfile. 
     Returns the pdf file handle. 
     """ 
     try: 
      args=['/usr/bin/pdflatex', '-jobname', 'dp', '-output-directory', self.tmpd, '-halt-on-error'] 
      cmd = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
      stderr, stdout = cmd.communicate(input=self._gen_latex()) 
      if cmd.poll() != 0: 
       print('Error running cmd.') 
       raise IOError 
      else: 
       return self._cp_pdf_tmp() 
     except Exception: 
      pass 
     finally: 
      self._clean_tmp() 

    def __init__(self, get_pdf_form, parent_dir=os.path.join(django.conf.settings.BASE_DIR+'/media', 'pdfs', 'tmp')): 
     """ 
     get_pdf_form: A validated pdfs.forms.GetPDFForm. 
     parent_dir: Directory where the temporary directory will be created. 
     """ 
     self.form = get_pdf_form 
     self.parent = parent_dir 
     self.tmpd = tempfile.mkdtemp(dir=self.parent) 

    def __del__(self): 
     self._clean_tmp() 

    def _gen_latex(self): 
     """ 
     Generates the latex markup and returns a string of the markup. 
     """ 
     header = r""" 
     \documentclass[a4paper,16pt]{article} 

     \usepackage{graphicx} 
     \usepackage{pdfpages} 
     \usepackage{hyperref} 
     \usepackage{fancyhdr} 

     \begin{document} 

     \pagestyle{fancy} 

     \fancyhead[C]{\includegraphics[width=9mm]{%s}\huge{ Student Book}} 

     """ % os.path.join(django.conf.settings.BASE_DIR, 'static', 'images', 'logo.png') 

     footer = '\n\n\end{document}' 
     links = '' 
     docs = '' 
     hyperlink = 2 
     for x, i in enumerate(self.form.iter_pdf()): 
      docs += r"\includepdf[pages=%s,link,linkname=%s]{%s}" % (i[1], i[0].pdf_display_name, i[0].pdf_path) 
      docs += '\n' 
      if i[1] == '-': 
       # Complete PDF. 
       links += r"\noindent\hyperlink{page.%s}{%s}\newline" % (hyperlink, 
                     i[0].pdf_display_name) 
       hyperlink += i[0].pages 
      else: 
       links += r"\noindent\hyperlink{page.%s}{%s (Page %s)}\newline" % (hyperlink, 
                        i[0].pdf_display_name, i[1]) 
       hyperlink += 1 
      links += '\n' 
     return header + '\n\n' + links + '\n\n' + docs + '\n\n' + footer 

    def _cp_pdf_tmp(self): 
     """ 
     gen_pdf() creates a temp directory that includes latex build files and the PDF. Unfortunately, 
     a temp directory will not automatically delete when the last reference is closed. Therefore, 
     it's necessary to manually delete this temp dir before returning from the view. However, 
     we can't send the PDF to the user if we've already deleted its containing dir. This function 
     copies the PDF to a true temp file that will delete on close, allowing us to have the desired 
     behavior where the temp dir is manually deleted, and the PDF is deleted upon close. 
     Returns a file handle to the PDF. 
     """ 
     if os.path.isfile(os.path.join(self.tmpd, 'dp.pdf')): 
      tmp = tempfile.TemporaryFile(dir=self.parent, mode='r+b') 
      shutil.copyfileobj(open(os.path.join(self.tmpd, 'dp.pdf'), 'rb'), tmp) 
      tmp.seek(0) 
      return tmp 
     else: 
      print('No source file.') 
      raise IOError 

    def _clean_tmp(self): 
     """ 
     Cleans up temp directory. 
     """ 
     try: 
      shutil.rmtree(self.tmpd) 
     except OSError: 
      print('Unable to clean temporary files.') 

Добавлено Traceback

Traceback: 
File "/usr/lib/python3/dist-packages/django/core/handlers/base.py" in get_response 
    112.      response = wrapped_callback(request, *callback_args, **callback_kwargs) 
File "/usr/lib/python3/dist-packages/django/contrib/auth/decorators.py" in _wrapped_view 
    22.     return view_func(request, *args, **kwargs) 
File "/var/django/project1/project1/pdfs/views.py" in pdf_share 
    132.     pdf_fb = tex.gen_pdf() 
File "/var/django/project1/project1/pdfs/latex.py" in gen_pdf 
    125.   stdout = cmd.communicate(input=self._gen_latex())[0] 
File "/usr/lib/python3.4/subprocess.py" in communicate 
    960.     stdout, stderr = self._communicate(input, endtime, timeout) 
File "/usr/lib/python3.4/subprocess.py" in _communicate 
    1602.     input_view = memoryview(self._input) 

Exception Type: TypeError at /app1/share/pdf/ 
Exception Value: memoryview: str object does not have the buffer interface 
+0

предоставить полный обзор. Кстати, если вы портируете скрипт из Python 2 в Python 3, вы должны быть очень знакомы с разницей между текстом (строки Unicode) и двоичными данными (байтами). – jfs

+0

Я попытался использовать stdout = cmd.communicate (input = (self._gen_latex()). Encode ('utf-8')) [0] здесь, он прошел через эту строку, но затем получил cmd.poll() = 1 и не мог попасть в процесс копирования. – fd8323

+0

1. Если 'pdflatex' ожидает ввода в кодировке utf-8, тогда' .encode ('utf-8') 'is fine 2. Он исправляет эту проблему. Это не значит, что он исправит все проблемы в вашем скрипте. Вы должны понимать, какие процессы, функции, методы ожидают, какие данные и в какой форме. – jfs

ответ

0

После фиксации "стандартный вывод = cmd.communicate (вход = (self._gen_latex()). Кодировать ('UTF-8')) [0]", я смог распечатать все детали выполнения LaTex. Причина, по которой я получил Popen.poll() = 1 вместо 0, состояла в том, что подпроцесс был прерван с ошибкой. После распечатки stdout и выкапывания ошибки, был файл логотипа, который имел неправильный путь. После исправления этой ошибки все работает отлично. Надеюсь, что это поможет тому, кто бы ни работал на подобном мне материале.

Смежные вопросы