Search code examples
pythonfilematplotlibiooperation

Pandas IO Operation Error From Windows EXE. No Error Running Python Script Directly From CLI


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:

  • python
  • pyinstaller
  • matplotlib
  • pypdf2
  • numpy

conda installed the above and all the dependencies it identified.

Operation

In Anaconda Prompt,

  • activate environment
  • Run pyhton script and GUI functions using python script.pyw
  • After successful run on CLI, invoke 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')

Solution

  • 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.