PyPandoc in combination with PyInstaller - python

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:
Create a file including the following code:
import pypandoc
pypandoc.convert(sou‌​rce='# Sample title\nPlaceholder', to='docx', format='md', outputfile='test.doc‌​x')
Save this file as pythonfile.py
create an executable with PyInstaller:
pyinstaller --onefile --clean pythonfile.py
Now the executable should run on a computer without Pandoc (or PyPandoc) installed.

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)

Related

Error when including git.exe in a Pyinstaller app to be used by clients

I'm designing a GUI application that will be distributed to others, written with Python, that includes the use of Kivy and GitPython.
I'm running into major issues when trying to package the program with Pyinstaller.
This is a sample of my python script where I'm defining the path to the the git exe in the 'dist' folder created by Pyinstaller.
base_path = os.getcwd()
splitString = base_path.split('\\')
if 'dist' in splitString:
end_index = splitString.index('dist')
splitString = splitString[:end_index]
save_path = ''
for string in splitString:
save_path += string + '\\'
save_path = save_path[:-1]
# Establish path to git.exe
git_path = base_path + '\\git\\'
cmd = f'SET PATH=%PATH%;{git_path}'
os.system(cmd)
import git
And this is a sample of my spec file generated by Pyinstaller.
a = Analysis(['main.py'],
pathex=[path\to\main.py],
binaries=[('C:\\Program Files\\Git\\cmd\\git.exe', '.\\git')],
datas=[],
hiddenimports=['win32timezone'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='sample_app',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True)
I'm getting this error over and over again when trying to import git:
Traceback (most recent call last):
File "main.py", line 162, in <module>
File "C:\Users\<name>\anaconda3\lib\site-packages\Pyinstaller\loader\pyimod03_importers.py", line 623, in exec_module
File "site-packages\git\__init__.py", line 89, in <module>
manage the module state. Use the 'ctx' variable dictionary. This
ImportError: Failed to initialize: Bad git executable.
The git executable must be specified in one of the following ways:
- be included in your $PATH
- be set via $GIT_PYTHON_GIT_EXECUTABLE
- explicitly set via git.refresh()
All git commands will error until this is rectified.
This initial warning can be silenced or aggravated in the future by setting the
$GIT_PYTHON_REFRESH environment variable. Use one of the following values:
- quiet|q|silence|s|none|n|0: for no warning or exception
- warn|w|warning|1: for a printed warning
- error|e|raise|r|2: for a raised exception
Example:
export GIT_PYTHON_REFRESH=quiet
When I run the exe on my personal computer (which has git installed here C:\\Program Files\\Git\\cmd\\git.exe ) everything runs totally normally. It's just went the exe is run on a computer that DOES NOT have git installed where it throws the error shown above.
It should be able to work since I'm including git as a binary file to Pyinstaller but it's just not working for me right now.
I've tried all of the suggestions made in the error message and nothing is working. Not sure if I'm missing a hidden import or something but I'm completely stumped so any assistance would be appreciated!!

Create an executable from Python code with gmsh import

I'm trying to package up my Python package into an executable using pyinstaller. The script name is called "run-jointbuilder.py" The package has a number of dependancies (such as numpy), but importantly gmsh.
When using pyinstaller to compile my code, it appears to be successful, but then when I try to run the executable I get the following errors:
import gmsh # PyInstaller PYZ\
import ctypes.util # PyInstaller PYZ\
import 'ctypes.util' # <pyimod03_importers.FrozenImporter object at 0x000001BD783FC910>\
Traceback (most recent call last):\
File "PyInstaller\loader\pyiboot01_bootstrap.py", line 144, in __init__
File "ctypes\__init__.py", line 381, in __init__\
FileNotFoundError: Could not find module 'C:\Users\willber\Anaconda3\Scripts\gmsh' (or one of its dependencies). Try using the full path with constructor syntax.
I then get this error:
__main__.PyInstallerImportError: Failed to load dynlib/dll
'C:\\Users\\willber\\Anaconda3\\Scripts\\gmsh'. Most probably this dynlib/dll was not found when the application was frozen.
[18612] Failed to execute script run-jointbuilder
Has anybody tried to compile some Python code that imports the gmsh package? I'd really appreciate an example .spec file, for use with pyinstaller if so!
The gmsh python package wraps a bunch of compiled libraries which contain the implementations of the methods you call from python. When you import gmsh.py into your script then gmsh loads these libraries in the background giving you access to their functionality through python methods. So it's essential that these libraries are embedded in the pyinstaller output for your code to function as it does when you run it directly through the python interpreter.
It's difficult for pyinstaller to consistently find these libraries since they're not accessed in the same way as normal python imports, they are loaded using the cytpes package. There's some description of how pyinstaller does this in the docs. Since you're seeing a dynlib/dll loading error when you run the compiled python script, this suggests that pyinstaller isn't finding the gmsh library during compile and hence it is missing from the executable.
If you look in the gmsh.py source, you can see that gmsh.py loads up a .dll library called gmsh-4.9.dll for Windows OS. You can use the binaries input of the pyinstaller .spec file to point the compiler to the gmsh-4.9.dll.
Here's an example .spec file which dynamically locates the gmsh-4.9.dll at the time of compile so it picks up the right .dll for your active environment. You could make this more generic by filtering all *.dll in the gmsh directory, but I've hardcoded for clarity:
# -*- mode: python ; coding: utf-8 -*-
from pathlib import Path
import gmsh
# get the location of the gmsh dll which sits next to the gmsh.py file
libname = 'gmsh-4.9.dll'
libpath = Path(gmsh.__file__).parent / libname
print('Adding {} to binaries'.format(libpath))
block_cipher = None
a = Analysis(['gmsh-test.py'],
pathex=['C:\\Users\\user\\dev\\gmsh'],
# tell pyinstaller to add the binary to the compiled path
binaries=[(str(libpath), '.')],
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name='gmsh-test',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True,
runtime_tmpdir=None,
)

How do I resolve a missing utility module preventing my executable from running when I already installed the parent module?

I've written a few Python scripts to create a tkinter GUI for a machine learning algorithm process. I originally coded everything in PyCharm, but I'd really like to put everything together into a stand-alone executable. I've moved my main script and its .py dependencies into their own directory and tested it out using the Command Prompt, and it works great. However, when I run pyinstaller, the executable is created but fails on startup.
The program is made up of three files, with GUI.py being the main script. As mentioned above, I moved the dependent files into a new directory and tested GUI.py in the Command Prompt, and it worked great. Executable is created (albeit with a lot of warnings about missing 'api-ms-win-crt' files) but can't be run.
I created the executable using the command:
pyinstaller --onefile GUI.py
When the executable is run from the command line after creation, I get a big long traceback ending in the following:
File "site-packages\sklearn\metrics\pairwise.py", line 32, in <module>
File "sklearn\metrics\pairwise_fast.pyx", line 1, in init
sklearn.metrics.pairwise_fast
ModuleNotFoundError: No module named 'sklearn.utils._cython_blas'
[3372] Failed to execute script GUI
I know I've already explicitly imported sklearn through the command prompt, but from the traceback, it seems I'm missing a utility module somewhere. I tried to import the missing module specifically, but I got an error that no distributed module was available.
I don't have much experience with pyinstaller, and I have no idea where to go from here. I'm using Windows 10 and Python 3.7.3.
It seems that Pyinstaller can't resolve sklearn import. So one easy way is to just bring the whole module directory which located in <path_to_python>/Lib/site-packages/sklearn/ with executable output. So use below spec file to generate your executable:
# -*- mode: python -*-
block_cipher = None
a = Analysis(['test.py'],
pathex=['<path to root of your project>'],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
a.datas += Tree('<path_to_sklearn_in_python_dir>', prefix='sklearn')
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='test',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False,
runtime_tmpdir=None,
console=True )
Finally generate your executable with
pyinstaller test.spec
This should resolve import errors for sklearn but if you face other NotFound imports add them like above to spec file.
Building up on M.R.'s answer, you can directly include the path to sklearn in your original pyinstaller command:
pyinstaller --onefile GUI.py --add-data "<path-to-python>\Lib\site-packages\sklearn;sklearn"
which results in the following line of code being added inside a = Analysis() in the automatically-generated GUI.spec file:
datas=[('<path-to-python>\\Lib\\site-packages\\sklearn', 'sklearn')]
Note that the --onefile option will result in an executable that is slower to start up than the default one-folder bundle (based on both the pyinstaller documentation and my own experience bundling up sklearn):
pyinstaller GUI.py --add-data "<path-to-python>\Lib\site-packages\sklearn;sklearn"

Windows- Pyinstaller Error “failed to execute script ” When App Clicked

I’m New to Python Coding and just finished my first python scripted
I’m trying to publish my programme so that I can install in on another device.
But as soon as I convert it from .py to .exe with pyinstaller and try to run my programme it gives me the error:
fatal error: failed to execute scrip
Code I used in to convert:
pyinstaller -w file_name.py
pyinstaller -F file_name.py
pyinstaller -i "c:\\icon_file path" file_name.py
am I just missing as step or is there something else I can try to resolve this problem?
I usually code on Visual studio and when I test run everything worked fine.
My .spec file:
block_cipher = None
a = Analysis(['file_name.py'],
pathex=['C:\\Users\\MainUser\\Desktop\\Publishing'],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
a.binaries = a.binaries +
[('libsha1.dll','/home/iot/lib/libsha1.dll','BINARY')]
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='file_name',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=True )
```
Usually, this is due to a lack of file when packaging.
When you use PyInstaller, you can use it like this:
python -m PyInstaller .\yourFile.py
then, a yourFile.spec file is generated under this folder.
you should edit this file, add all project file into datas,
a = Analysis(['yourFile.py'],
pathex=['D:\\projectPath\\project'],
binaries=[],
datas=[('D:\\projectPath\\project\\*.py', '.'),
('D:\\projectPath\\project\\UI\\*.ui', 'UI'),
('D:\\projectPath\\project\\other\\*.py', 'other'),
],
...
)
It's simulated up here, a project that contains the UI and other folders. It like a tuple, ('full path', 'folder name').
If you have *.dll on Windows or *.so on Linux, you must be write into binaries:
a.binaries = a.binaries + [('libsha1.so','/home/iot/lib/libsha1.so','BINARY')]
I am guessing you only have one script, so if you use:
Pyinstaller --onefile yourScript.py
Replacing yourScript.py with the name of your python file in the CMD/Terminal, you shouldn't have any problems.
If you are missing a binary, this should help. For example pyinstaller was missing the currency converter module, so I found and it, got the zip file and then ran this in CMD:
Pyinstaller --add-binary "C:\Users\myName\Downloads\eurofxref-hist.zip";currency_converter --onefile myScript.py
Where myScript.py is my Python script, and the link is to the folder with the binary zip file.

Build Cython-compiled modules and python code into executable binary using PyInstaller

I am trying to package my project code into a an executable binary using Cython and PyInstaller libraries.
My code directory looks like this:
The main.py is the main code which imports the logic from program_a.py and program_b.py.
I am successfully able to convert my program_a and program_b files into .so files which can be imported by any python code. I did this by executing the following script.
from distutils.core import setup
from Cython.Build import cythonize
sourcefiles = ['program_a.py', 'program_b.py']
setup(
name = "Hello World",
ext_modules = cythonize(sourcefiles),
)
By executing >python setup.py build_ext --inplace I get .so files as shown below
When I run python main.py it runs perfectly with .so files. Which shows that I can import them as a module.
Now, I want to package binaries (.so) files and main.py into single binary file. For that I used the following command provided by pyInstaller
pyinstaller "main.py" --onefile
It actually gives a binary in dist/ folder but I cannot able to import some modules and getting the following error:
Traceback (most recent call last):
File "main.py", line 1, in <module>
import program_a as lisence_checker
File "program_a.py", line 1, in init program_a
ModuleNotFoundError: No module named 'licensing'
[18032] Failed to execute script main
How can I link libraries with the pyinstaller or embed library information into my binaries?
What I found yet:
Building Cython-compiled python code with PyInstaller
https://riptutorial.com/cython/example/21982/bundling-a-cython-program-using-pyinstaller
But all of these above links are not using any external package inside there python code examples. I am able to compile the code without external modules
After getting familiar with PyInstaller package I am able to figure out the issue. I followed the following steps to make it work for me at the end.
Now, posting my answer to help others :)
## Build *.so files from python modules
1. Execute "setup.py" file
> python setup.py build
2. It will generate "*.so" modules inside "build/lib.linux-x86_64-3.6" dir.
## Created binary from cython modules
1. Copy the binaries (i.e. *.so) files into binary folder
2. Get inside the binary folder 'cd binary'
3. Run Pyinstaller command inside binary directory: `python -O -m PyInstaller --clean --onefile idps.spec`
4. Your binary will be inside dist folder 'binary/dist/'
5. Execute the binary in linux using './dist/sample_app'
6. Your app is ready :)
Here is spec file to make it work for me:
# -*- mode: python -*-
block_cipher = None
a = Analysis(['main.py'],
pathex=['cython_pyinstaller_sample/binary'],
binaries=[('program_a.cpython-36m-x86_64-linux-gnu.so', '.'),('program_b.cpython-36m-x86_64-linux-gnu.so', '.')],
datas=[('config_file.txt', '.')],
hiddenimports=['licensing', 'licensing.methods', 'pandas'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False) pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher) exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='sample_app',
debug=True,
bootloader_ignore_signals=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=True )
Just in case someone's looking for a quick fix.
I ran into the same situation and found a quick/dirty way to do the job. The issue is that pyinstaller is not adding the necessary libraries in the .exe file that are needed to run your program.
All you need to do is import all the libraries (and the .so files) needed into your main.py file (the file which calls program_a.py and program_b.py). For example, assume that program_a.py uses opencv library (cv2) and program_b.py uses matplotlib library. Now in your main.py file you need to import cv2 and matplotlib as well. Basically, whatever you import in program_a.py and program_b.py, you have to import that in main.py as well. This tells pyinstaller that the program needed these libraries and it includes those libraries in the exe file.

Categories

Resources