Pyinstaller ModuleNotFoundError on Mac OS - python

I'm trying to make my python program into an app using pyinstaller. This works flawlessly in Windows, but is not working on Mac OS (Big Sur). I've installed pyinstaller version 5.0dev0.
I started the virtual environment and ran pyinstaller in terminal with the following. (Ultimately, I want to run it as --onefile and without the debug stuff):
(klusterbox) thomasweeks#Thomass-MacBook-Pro kb_install % pyinstaller -w -D -i kb_sub/kb_images/kb_icon1.icns --log-level DEBUG > out.txt klusterbox.py
When I click on the app or open it in terminal, the icon appears in the dock for an instant and disappears. Nothing else happens. When I the Unix Executable File in the dist directory, the, terminal opens and give me this:
Last login: Fri Jan 15 22:24:13 on ttys002
thomasweeks#Thomass-MacBook-Pro ~ % /Users/thomasweeks/klusterbox/kb_install/dist/klusterbox/klusterbox ; exit;
Traceback (most recent call last):
File "klusterbox.py", line 58, in <module>
ModuleNotFoundError: No module named 'PIL'
[24871] Failed to execute script klusterbox
Saving session...
...copying shared history...
...saving history...truncating history files...
...completed.
[Process completed]
This is line 58 in the python program:
# Pillow Library
from PIL import ImageTk, Image # Pillow Library
The Pillow module is installed in the virtual environment:
(klusterbox) thomasweeks#Thomass-MacBook-Pro klusterbox % pip3 list
Package Version
------------------ --------
...
openpyxl 3.0.3
pdfminer.six 20181108
Pillow 8.0.1
pip 20.3.1
...
My .spec file looks like this:
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(['klusterbox.py'],
pathex=['/Users/thomasweeks/klusterbox/kb_install'],
binaries=[],
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,
[],
exclude_binaries=True,
name='klusterbox',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False , icon='kb_sub/kb_images/kb_icon1.icns')
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='klusterbox')
app = BUNDLE(coll,
name='klusterbox.app',
icon='kb_sub/kb_images/kb_icon1.icns',
bundle_identifier=None)
I've got more raw data if you need to see it.

Edit: I found out later that the below method was made irrelevant by running pyinstaller from inside the virtual environment. After I uninstalled pyinstaller using 'pip uninstall pyinstaller' outside of the virtual environment. I started the virtual environment then installed pyinstaller inside the virtual environment with 'pip install pyinstaller'. After this, pyinstaller found the needed modules. End of edit.
This is a partial answer, where I was able to solve part of the problem. First I went to terminal, navigated to the project folder and made sure the virtual environment was running:
pipenv shell
Next I looked for the missing module which is cited in ModuleNotFoundError: No module named 'PIL'.
pip show Pillow
This gives the following report:
(klusterbox) thomasweeks#Thomass-MacBook-Pro kb_install % pip show Pillow
Name: Pillow
Version: 8.0.1
Summary: Python Imaging Library (Fork)
Home-page: https://python-pillow.org
Author: Alex Clark (PIL Fork Author)
Author-email: aclark#python-pillow.org
License: HPND
Location: /Users/thomasweeks/.local/share/virtualenvs/klusterbox-K4ySO0c7/lib/python3.7/site-packages
Requires:
Required-by:
I copied the location from the terminal and opened up my klusterbox.spec file. You you it will be .spec in your project folder. I changed the value of pathex in the Analysis tuple in the .spec file.
...
a = Analysis(['klusterbox.py'],
pathex=['/Users/thomasweeks/.local/share/virtualenvs/klusterbox-K4ySO0c7/lib/python3.7/site-packages', '/Users/thomasweeks/klusterbox/kb_install'],
binaries=[],
...
So the pathex value of the Analysis tuple now contains two locations, the location of the project folder and the location of the site packages in the virtual environment. I didn't change anything else in the spec file.
I deleted the old dist, build and pycache folders in Finder.
In terminal I re-ran pyinstaller using the .spec file instead of the .py file. This runs pyinstaller so that it is configured by your revised spec file.
pyinstaller klusterbox.spec
Pyinstaller runs, creates the dist, build and pycache folders. Opening the dist folder there is a klusterbox file (Unix Executable File) and a klusterbox.app file. The klusterbox.app file still will not open, but the Unix Executable File does open and runs although there is a terminal window even though I specified no console in the spec file.

Related

Pyinstaller's .exe file closes immediately

I have a working python package that's a CLI tool and I wanted to convert it into a single .exe file to upload it to other package managers so I used Pyinstaller. After building the .exe file with this command:
pyinstaller -c --log-level=DEBUG main.py 2> build.txt --onefile --exclude-module=pytest --add-data "src;src"
I double-clicked the .exe file but it closed immediately but in that split second, I saw the expected output which is supposed to be the command-line interface so the .exe does work but not entirely.
main.py
from src.Categorize_CLI.__main__ import main
if __name__ == "__main__":
main()
.spec file
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['main.py'],
pathex=[],
binaries=[],
datas=[('src', 'src')],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=['pytest'],
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='main',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
Update
I got it working by dragging the main.exe file to a open command prompt and then pressed enter and it worked, and I got an error:
RuntimeError: 'package' is not installed. Try passing 'package_name' instead.
[15592] Failed to execute script 'main' due to unhandled exception!
It sounds like the script ran to end of file to fast for you to see. You can confirm this by opening your terminal (cmd/poweshell in windows) and running your program like any other CLI tool.
cd path\to\exe
./exe -arguments
Since you launched it from an allready opened terminal it won't close when the script ends.
If this is the problem you can solve it by adding
input("Continue...") # wait for user
Update
As #BokiX says pyinstaller can cause false positives with anti virus software. Try a diffrent one e.g. nuikta:
pip install Nuikta
python -m nuikta main.py
installing python programs vs tradtitional programs
A tradtitional program installer is usualy a fancy zip file with some additianal feautures to setup a program for the relevant system it's on (e.g. make registry changes, download additianal files).
A python program is just a python script that was "frozen" in state so it can run independently on a system without python or it's dependencies. once you have an exe it should just run without needing to be "installed".
using console programs
A console program is a program that is made to be exicuted from a terminal. In modern use these are usualy so they can be run by a script or someone who finds typing is faster than using a GUI.
Run the code in the cmd and not by clicking the exe
A pyinstaller exe "usually" closes when 1 - Theres an error in the code , 2 - The code execution finished , closes after displaying the output
Also as BokiX stated , pyinstaller exe's often get false flagged as malicious , so maybe add an exception to your anti virus.

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,
)

no module named error after pyinstaller python script

What I want: Turn a python file to executable file with all modules and start script.
Problem: Executable file gives no module named signalrcore when execute.
I already have module named 'signalrcore' but when I convert my python script to executable file with pyinstaller it wont work. The error is no module named signalrcore . It only appears with executable file. The python script is work fine. Script work with python2 python myscript.py -> work without any error. But python3 myscript.py -> have same error with executable file.
My python code:
from signalrcore.hub_connection_builder import HubConnectionBuilder
print('TEST')
My .spec file :
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(['service.py'],
pathex=['/home/pi/Desktop/agent'],
binaries=[],
datas=[],
hiddenimports=['signalrcore','signalrcore.hub_connection_builder'] ,
hookspath=['/usr/lib/python2.7/dist-packages'],
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,
[],
exclude_binaries=True,
name='service',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False)
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='service')
This errors pops while compile python code to executable ( sudo pyinstaller service.spec)
56429 INFO: Analyzing hidden import 'signalrcore'
56433 ERROR: Hidden import 'signalrcore' not found
56434 INFO: Analyzing hidden import 'signalrcore.hub_connection_builder'
56438 ERROR: Hidden import 'signalrcore.hub_connection_builder' not found
The problem seems to be, that you installed signalrcore only for the current user, but not for root.
Is there any reason, that you run pyinstaller as root?
This should not be necessary.
Call following command without sudo and look at the output.
python -c "import signalrcore ; print(signalrcore.__file__)"
It will show you where the signalrcore module is installed I assume, that this is a local path.
What is normally best is to use python virtualenv to have specific python setups for specific tasks. these virtualenvs can be shared amongst users as long as it is always one user (the one who created the virtualenv) who performs the pip installs and as long as the other is only using the virtualenv.
I suggest you try to read about virtualenvs. ( https://virtualenv.pypa.io/en/latest/ )
Very quick introduction
# install virtualenv
python -m pip install --user virtualenv
# create a virtualenv
python -m virtualenv /path/to/virtualenv # e.g /home/pi/virtualenv4service
# activate virtualenv
source /path/to/virtualenv/bin/activate
pip install signalrcore
pip install pyinstaller
pip install any_other_module_you_need
Then call pyinstaller with
pyinstaller service.spec

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"

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