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.
Related
I've created a script to automatize creating new executable versions of my app. The thing is that when I run command pyinstaller myapp.spec from commandline on windows platform it creates different file than when I run in from my script (which doesn't work BTW). Below is a snippet of code I use to create .exe.
SNIPPET
SPEC_PATH = 'venv_python37'
file = 'myapp.spec'
os.chdir(SPEC_PATH)
command = r'{} {}'.format('pyinstaller', file)
os.system(command)
When I run that I see all prints of pyinstaller and at the end there is a:
55830 INFO: Building COLLECT COLLECT-00.toc completed successfully.
Which looks exacly like when I run this command directly from commandline.
DIFFERENCES
myapp.exe size created from my script is 27MB but from commandline it is 40MB
When I run the .exe created from myscript there is an error that says:
The 'gcloud' distribution was not found and it is required by the application
That looks like when I run it from myscrip it user another dependencies than from commandline and I think command from myscript doesn't use dependencies from my virtual enviroment. A'm I right?
MYAPP.SPEC
# -*- mode: python -*-
from kivy.deps import sdl2, glew
block_cipher = None
_excludes=("'tcl'", "'tk'", "'FixTk'", "'_tkinter'", "'tkinter'", "'Tkinter'")
a = Analysis(['C:\\Users\\Patryk\\PycharmProjects\\myapp\\myapp.py'],
pathex=['venv_python37', 'C:\\Users\\Patryk\\PycharmProjects\\myapp'],
binaries=None,
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=_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,
*[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
name='BajotWorkSpace',
debug=False,
strip=False,
upx=True,
console=True,
runtime_tmpdir=None,
icon='C:\\Users\\Patryk\\PycharmProjects\\myapp\\main_logo.ico' )
coll = COLLECT(exe,
Tree('C:\\Users\\Patryk\\PycharmProjects\\myapp\\Added_files'),
name='myapp')
I've found the reason. When I run .spec from my script it uses packages from my virtual enviroment but the same command run from commandline uses global packages. The issue with gcloud module was lack of hook file in Lib\site-packages\PyInstaller\hooks. Adding file named hook-gcloud.py with below code made my .exe work.
from PyInstaller.utils.hooks import copy_metadata
datas = copy_metadata('gcloud')
EDIT
The issue is fixed, but only if I run the produced executable from my desktop instead of the server location I've been developing and running from (VPN). No change to the code was necessary, but I would prefer to have something more robust. There's no reason this shouldn't work when running from a network location, especially considering it works fine if I run the script without creating the executable. It's seeming like the sluggishness that comes from running it there versus on my computer's desktop is what is somehow causing things to behave differently. I'd really like to know why that is, and if there's a way to avoid it.
Versions:
Windows 10
Python: 3.8.3
PyInstaller: 3.6
Paramiko: 2.7.1
Below is a minimal version of the code that exhibits the issue:
import paramiko
ip = "10.0.0.172"
username = "username"
password = "password"
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(ip, username=username, password=password)
stdin, stdout, stderr = client.exec_command("ls")
print(stdout.readlines())
client.close()
I am using pipenv to contain these packages in a virtual environment.
When running natively (e.g. pipenv run python main.py), there are no problems. Running the executable produced by Pyinstaller, using pipenv run pyinstaller Test.spec --distpath "./Release", results in the following runtime error when running the produced executable:
Traceback (most recent call last):
File "main.py", line 8, in <module>
File "lib\site-packages\paramiko\client.py", line 343, in connect
File "socket.py", line 231, in __init__
OSError: [WinError 10022] An invalid argument was supplied
Things I have tried:
Adding "_socket" and "socket" to hiddenimports in the .spec file
Installing different versions of Python, Pyinstaller, Paramiko
Searching for other people with this issue. The closest I found was This Github Issue which mentions "PuTTY Pageant" (Which I don't have and am not running it or anything similar). Here is a StackOverflow post with the same error, but from someone who is attempting to create a socket themselves and appears to be running it with Python directly instead of through an executable created with Pyinstaller. And Here is another post that suggests having multiple attempts to create a socket, which I implemented and still ran into the issue.
Restarting my computer and running the executable in a fresh boot state
And here is my .spec file:
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['main.py'],
binaries=[],
datas=[],
hiddenimports=["_socket"],
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='Test',
debug=True,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True
)
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='Test'
)
Your help is appreciated.
I am currently writing an application for a raspberry pi 3 using python3.7 and kivy1.11.1. I am trying to package a test application to ensure that it will work. I have created a simple grid with 6 buttons. When you click on a button it prints "button pressed" to the screen. I am using a separate kv file to lay this out as this will be more practical as the application develops. So I have two files, "test.py" and "Test.kv".
I have been using pyinstaller to package my app but it is not working. It runs and creates the executable file just fine with no errors. I then drag my executable file out to my main folder from the "dist" folder as recommended in this video.
My command is:
pyinstaller --onefile --add-data "Test.kv:." test.py
I have also tried:
pyinstaller --onedir --add-data "Test.kv:." test.py
which does the same: blank window no widgets, no errors.
When I try to execute it nothing happens. I don't know what is wrong because I'm not getting any errors when I try to execute it from the terminal. When I do the exact same process on windows it works so its not the code. On occasion, I think it does eventually run but I just get a blank screen and again no error. This would leave me to believe it's an issue with reading the .kv file.
Perhaps this process doesn't work with kivy and I need to use some other method to package my application. However I have also tried this kind of approach but it has left me with errors. I much prefer the pyinstaller method however I don't know if it is going to work as it doesn't seem to pick up the .kv file.
Any help/advice on this issue would be greatly appreciated?
My test.spec file:
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(['test.py'],
pathex=['/home/pi/Desktop/WinTest'],
binaries=[],
datas=[('Test.kv', '.')],
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='test',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False )
The following picture is my program folder (sorry for the poor quality). My library files are in venv/Lib/site-packages. This is where my kivy/kivy_deps folders are. Could the issue be that the program can't find my library files? But surely if that was the issue it would throw a "file not found" error? And as previously stated, I am not receiving any errors.
Found this in my warn-text.txt file
missing module named kivy.lib.vidcore_lite.egl - imported by kivy.lib.vidcore_lite (top-level), kivy.core.window.window_egl_rpi (top-level)
missing module named kivy.lib.vidcore_lite.bcm - imported by kivy.lib.vidcore_lite (top-level), kivy.core.window.window_egl_rpi (top-level)
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"
What is the best way to package python scripts using the pygame module as cross platform executables? I need to be able to compile from my Linux machine. I've tried using PyInstaller, however, when I run the compiled executable, it says it can't find the pygame module. When I running it as a python script through the Wing IDE debug it works fine, so I clearly have pygame installed. However, when I try running without the IDE debug it fails to create a windows. Is there something wrong with my script that it can only be run through Wing IDE? How can I package my game so that it work on any computer, Linux or Windows, without even needed python or pygame? Is this even possible?
My source: https://github.com/william1008/TrappedTux
PyInstaller works fine to build a Windows executable that embed both python and pygame.
Here is the spec file I'm using:
# -*- mode: python -*-
a = Analysis(['MyApplication.py'],
pathex=['.'],
hiddenimports=[],
hookspath=None,
runtime_hooks=None)
# Remove some weird error message
for d in a.datas:
if 'pyconfig' in d[0]:
a.datas.remove(d)
break
pyz = PYZ(a.pure)
exe = EXE(pyz,
Tree('resource', prefix='resource', excludes=[]),
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name='MyApplication.exe',
debug=False,
strip=None,
upx=True,
console=False,
icon="resource/image/MyApplication.ico")
This will bundle everything (resources as well) into a single executable. However, you'll need to use sys._MEIPATH to access those resources. I use this code to detect automatically which path to use:
def resource_path(relative):
if hasattr(sys, "_MEIPASS"):
return os.path.join(sys._MEIPASS, relative)
return relative
I suggest using cx_Freeze, it’s relatively easy to use and supports both windows and OS X