PyPandoc in combination with PyInstaller
Asked Answered
D

1

2

I installed PyInstaller to create executables for my python scripts, and that works fine. I used PyPandoc to create .docx reports, which also run fine when the normal python files are run, but not from the PyInstaller generated executable. It gives the error:

Traceback (most recent call last):
  File "src\flexmodel_postcalc.py", line 295, in postcalculate_everything
  File "src\flexmodel_postcalc.py", line 281, in generate_report_docx
  File "src\flexmodel_report_docx.py", line 118, in generate_text_useages_docx
  File "pypandoc\__init__.py", line 50, in convert
  File "pypandoc\__init__.py", line 70, in _convert
  File "pypandoc\__init__.py", line 197, in get_pandoc_formats
  File "pypandoc\__init__.py", line 336, in _ensure_pandoc_path
OSError: No pandoc was found: either install pandoc and add it
to your PATH or install pypandoc wheels with included pandoc.

During the executable creation I see no strange issues about PyPandoc. How can I include Pandoc into my executable so others (without Python and/or Pandoc installation) can use the executable and create .docx reports?

edit: a working process included the following steps:

  1. Create a file including the following code:

    import pypandoc
    pypandoc.convert(sou‌​rce='# Sample title\nPlaceholder', to='docx', format='md', outputfile='test.doc‌​x')
    
  2. Save this file as pythonfile.py

  3. create an executable with PyInstaller:

    pyinstaller --onefile --clean pythonfile.py
    
  4. Now the executable should run on a computer without Pandoc (or PyPandoc) installed.

Declared answered 10/8, 2016 at 13:15 Comment(4)
Can you add a minimal working example of your python script to see which part of pandoc you are using? I tried with pypandoc only and seems to be working fine in my windows system. Most likely you need to add pypandoc as a hidden import plus some other dependecy.Karakul
@Karakul I can give a very short sample code, but not test it right now. import pypandoc; pypandoc.convert(source='# Sample title\nPlaceholder', to='docx', format='md', outputfile='test.docx'). This is saved in a .py file, which I convert with PyInstaller via the command pyinstaller --onefile --clean -p H:\AppData\Python\Python27\Lib\site-packages pythonfile.pyDeclared
Please post your sample code in the question to allow others to understand your issue. I'll post an answer below.Karakul
@Karakul added it. Thanks in advance!Declared
K
4

There are two issues here. The first one is that pypandoc needs pandoc.exe to work. This is not picked up by pyinstaller automatically, but you can specify it manually.

To do this you you have to create a .spec file. The one I generated and used looks like this:

block_cipher = None

a = Analysis(['pythonfile.py'],
             pathex=['CodeDIR'],
             binaries=[],
             datas=[],
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          name='EXEName',
          debug=False,
          strip=False,
          upx=True,
          console=True , 
          resources=['YourPandocLocationHere\\\\pandoc.exe'])

You can build the executable by using pyinstaller myspec.spec. Don't forget to change the paths and the name parameter.

If you were building that in a directory mode this should be enough. However, for the one-file mode, things are a bit more complicated due to the way the pyinstaller bootloader process works. The pandoc.exe file is unzipped during execution in a temporary folder, but the execution happens in your original .exe folder. According to this question, you have to add the following lines to your code before calling pypandoc to change your current folder, if you run the frozen code.

if hasattr(sys, '_MEIPASS'):
    os.chdir(sys._MEIPASS)
Karakul answered 15/8, 2016 at 14:54 Comment(3)
Thanks for posting this. I have not got the -p argument working for some reason, but at least I am on the right track. Thanks!Declared
You have to add a collect block at the end of the .spec file in order to compile as a one-dir.Karakul
Is it correct that the resources argument should not be a list? I get a TypeError with is, and without the [] it works. For future reference, I included the -p argument folder in the pathex argument (a new item in that list).Declared

© 2022 - 2024 — McMap. All rights reserved.