A tkinter GUI program runs fine with file IO operation, when run from CLI with python script.pyw
. But when this code is converted to a Windows exe using pyinstaller, it fails while doing a df.to_csv
operation.
This program reads in a CSV, after some calculations. The exception happens when the program tries to write the dataframe with all the calculations just performed to disk. Specifically the error reads:
Unhandled exception in script
Traceback (most recent call last):
File "tkinter\__init__.py", line 1948, in __call__
File "script.pyw", line 2385, in update_current_file
File "script.pyw", line 1390, in calcs
ValueError: I/O operation on closed file.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "script.pyw", line 3085, in <module>
File "tkinter\__init__.py", line 1485, in mainloop
File "tkinter\__init__.py", line 1952, in __call__
File "tkinter\__init__.py", line 1668, in _report_exception
File "tkinter\__init__.py", line 2402, in report_callback_exception
ValueError: I/O operation on closed file.
Setup
Created a new environment for the project using conda. Packages requested while creating the environment:
conda installed the above and all the dependencies it identified.
Operation
In Anaconda Prompt,
python script.pyw
pyinstaller script.spec
Contents of script.spec:
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(['script.pyw'],
pathex=[],
binaries=[],
datas=[('subprocess_perl_script.plx', '.'), ('icon_file.ico', '.')],
hiddenimports=[],
hookspath=[],
hooksconfig={},
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)
splash = Splash('splash_image.png',
binaries=a.binaries,
datas=a.datas,
text_pos=None,
text_size=12,
minify_script=True)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
splash,
splash.binaries,
[],
name='script',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None , icon='icon_file.ico')
I solved this problem by fixing some print statements that were being used to write a log file.
Next I ran into a Matplotlib error. This too only showed up after running the pyinstaller executable. I was able to solve this by rolling back the matplotlib version number in the environment. To the best of my understanding, not all hooks are provided/listed in some newer versions. Pyinstaller uses hooks information to collect the packages for the final executable.
Also, a side note, changing the environment from conda to python venv resulted in the final exe at 40 MB, vs 400+ MB.