6

I used pyinstaller to bundle a program into one .exe file, which as you know makes a temporary _MEIPASS folder on execution.

The program itself has an input method for exit via sys.exit() which removes the _MEIPASS folder, but if the user closes the terminal via the window close button (X) the folder remains, and by the next execution another _MEIPASS folder will be created which will lead to a lot of MEIPASS folders after a while.

I want to know:

  1. Is there any way to force the .exe file to create a specific folder with the same name each time it runs to avoid multiple folders?

  2. Or just make the 'X' button to act like sys.exit() and remove the _MEIPASS folder.

My .spec file:

exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          [],
          name='CookieVPN',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          upx_exclude=[],
          runtime_tmpdir='E:/CookieVPNtmp/',
          console=True , icon='cookievpn.ico')

I also tried:

  1. 'E:\CookieVPNtmp\'
  2. 'E://CookieVPNtmp//'
  3. 'E:\'
  4. '/CookieVPNtmp' <--- This make the _MEIPASS folder in the root directory which the program was executed
1
  • There is an open issue here. The other option is build without the --onefile option and use the one folder option instead (I am posting this b/c I did not know about the "one folder" alternative).
    – xinthose
    Commented May 14, 2020 at 14:22

3 Answers 3

7

I found a somewhat solution which I'm not sure if it is failproof or not but works for my scenario.

Add this function to your code and call it as the first line of your program to find any _MEI folder in %tmp% (or anywhere the program's _MEI folder will be located) and delete them. (It excludes the MEI for the current run)

#Imports
import glob
import sys
import os
from shutil import rmtree

def CLEANMEIFOLDERS():
    #Getting the current run _MEI path
    try:
        base_path = sys._MEIPASS

    #I don't know if this part is crucial or not since it only works if you don't use 
    #--onefile in pyinstaller, which makes the use of this function unnecessary
    except Exception:
        base_path = os.path.abspath(".")

    #Extracting the updir of current run path to search for _MEI folders for the previous runs,    
    #cave man style. Please update this part if you know a better way.
    base_path = base_path.split("\\") 
    base_path.pop(-1)                
    temp_path = ""                    
    for item in base_path:
        temp_path = temp_path + item + "\\"

    #Search the path for _MEI folders and delete them if they are not for the current run
    mei_folders = [f for f in glob.glob(temp_path + "**/", recursive=False)]
    for item in mei_folders:
        if item.find('_MEI') != -1 and item != sys._MEIPASS + "\\":
            rmtree(item)

The code works but I will appreciate any tweak to make the code lighter :)

1
  • Thank you! This help me a lot. Maybe there is something can be improved, but I don't care now, it can work. :)
    – Zhang Buzz
    Commented Nov 22, 2019 at 7:40
4

The solution from Sadra is good, but I wanted to keep a few "new" folders just in case a users runs the tool with multiple instances. Thus it wouldn't help to remove everything except the current run.

So I came up with the idea to check the creation date of a folder and delete everything older than X seconds. Here's the code just in case somebody needs it:

from shutil import rmtree
import time, os

def deleteOldPyinstallerFolders(time_threshold = 3600): # Default setting: Remove after 1 hour, time_threshold in seconds
    try:
        base_path = sys._MEIPASS
    except Exception:
        return  # Not being ran as OneFile Folder -> Return

    temp_path = os.path.abspath(os.path.join(base_path, '..')) # Go to parent folder of MEIPASS

    # Search all MEIPASS folders...
    mei_folders = glob.glob(os.path.join(temp_path, '_MEI*'))
    for item in mei_folders:
        if (time.time()-os.path.getctime(item)) > time_threshold:
            rmtree(item)
1
  • 1
    It's a brilliant idea. Thank you for improving this subject. Commented May 23, 2020 at 8:18
1

First, when you close the window manually, your script would be closed unexpectedly (It is not the same as when you press ctrl+c) and thus you can't do anything about that as OS would close it forcibly.

But you can use --runtime-tmpdir to create a fixed folder for the files, thus it won't generate the folder each time:

pyinstaller -F --runtime-tmpdir "Some/Path" test.py 
1
  • 3
    Thank you for your respond, I tried this but apparently --runtime-tmpdir just changes the location of _MEIPASS folder and not the name of the folder, so its the same problem in a different directory. Also, I can't make it to work with a full path like 'E:/Folder/Subfolder' and it always makes the _MEIPASS in the root directory E:/. I will update the question part with my .spec file. Commented Jul 30, 2019 at 9:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.