Python3 LaTex PDF生成器使用子进程,错误:memoryview:str对象没有缓冲区接口

时间:2015-06-08 19:40:36

标签: django python-3.x pdf-generation latex subprocess

我正在将python 2项目转换为python 3.4。项目的一部分使用LaTex和subprocess生成PDF文件。我有问题让代码工作通过subprocess.Popen.communicate()步骤。问题出在gen_pdf()中,我认为导致问题的是cmd.communicate(input = self._gen_latex())。如果我拿出试试并直接运行代码,它将产生错误" 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:
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

1 个答案:

答案 0 :(得分:0)

修复后#34; stdout = cmd.communicate(input =(self._gen_latex())。encode(' utf-8'))[0]",我能够打印出所有LaTex执行细节。我得到Popen.poll()= 1而不是0的原因是因为子进程因错误而终止。打印出stdout并深入研究错误后,有一个错误路径的徽标文件。纠正错误后,一切都运行良好。 希望这有助于那些碰巧在像我这样的类似事情上工作的人。