Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Handling the _MEIPASS folder in temporary folder

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
like image 328
Sadra Heydari Avatar asked Dec 23 '22 22:12

Sadra Heydari


2 Answers

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

like image 96
Sadra Heydari Avatar answered Jan 15 '23 06:01

Sadra Heydari


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)
like image 38
tim Avatar answered Jan 15 '23 06:01

tim