Determining application path in a Python EXE generated by pyInstaller
Asked Answered
F

9

123

I have an application that resides in a single .py file. I've been able to get pyInstaller to bundle it successfully into an EXE for Windows. The problem is, the application requires a .cfg file that always sits directly beside the application in the same directory.

Normally, I build the path using the following code:

import os
config_name = 'myapp.cfg'
config_path = os.path.join(sys.path[0], config_name)

However, it seems the sys.path is blank when its called from an EXE generated by pyInstaller. This same behaviour occurs when you run the python interactive command line and try to fetch sys.path[0].

Is there a more concrete way of getting the path of the currently running application so that I can find files that are relative to it?

Forewarn answered 1/1, 2009 at 8:50 Comment(2)
possible duplicate of how can i get the executable's current directory in py2exe? -- This current question is older but the other question has more answers and is more documented.Arlenarlena
These have a lot in common but py2exe != pyinstallerForetopmast
F
182

I found a solution. You need to check if the application is running as a script or as a frozen exe:

import os
import sys

config_name = 'myapp.cfg'

# determine if application is a script file or frozen exe
if getattr(sys, 'frozen', False):
    application_path = os.path.dirname(sys.executable)
elif __file__:
    application_path = os.path.dirname(__file__)

config_path = os.path.join(application_path, config_name)
Forewarn answered 1/1, 2009 at 8:53 Comment(5)
FYI: For newer PyInstaller releases (3.0+), see @normanius answer.Decoy
Shouldn't there be only else: instead of elif __file__:?Intussusception
For users that like to use pathlib: bundle_dir = Path(sys.executable).parent also, you might want to check hasattr(sys, '_MEIPASS'). (should be True)Lolitaloll
This is the only solution that actually works, that is clean, and that it doesn't utilize that sys._MEIPASS stupidity.Monoplane
Also see @Max Tet comment on normanius answer.Diagnostics
E
108

According to the documentation of PyInstaller, the suggested method of recovering application path is as follows:

#!/usr/bin/python3
import sys, os
if getattr(sys, 'frozen', False):
    # If the application is run as a bundle, the PyInstaller bootloader
    # extends the sys module by a flag frozen=True and sets the app 
    # path into variable _MEIPASS'.
    application_path = sys._MEIPASS
else:
    application_path = os.path.dirname(os.path.abspath(__file__))

Tested for PyInstaller v3.2, but this certainly has been working for earlier versions as well.

Soviut's solution does not work, at least not in general for recent versions of pyInstaller (note that the OP is many years old). For instance, on MacOS, when bundling an application into a one-file-bundle, sys.executable points only to the location of the embedded archive, which is not the location where the application actually runs after the pyInstaller bootloader has created a temporary application environment. Only sys._MEIPASS correctly points to that location. Refer to this doc-page for further information on how PyInstaller works.

Earthly answered 6/3, 2017 at 0:0 Comment(6)
Can confirm that this works on Windows 10 and Windows 7 with PyInstaller 3.2 and Python 3.4 -- other answers no longer work.Decoy
just adding that according to the docs this works for both one-file and one-folder distributionsTowne
Using sys._MEIPASS does not work for one-file executables. From the docs: For a one-folder bundle, this is the path to that folder, wherever the user may have put it. For a one-file bundle, this is the path to the _MEIxxxxxx temporary folder created by the bootloader . Use sys.executable for one-file executables.Sure
for a --onefile executable, the path to the application is given by application_path = os.path.dirname(sys.executable)Fascista
Good. Now i can have the sqlite3 in other folder in production and other in the "release build (exe)".Loutitia
Just tested with pyinstaller version 5.0 and it worked like a charm. This is the only answer that worked for me. Also, using __file__ to get absolute path was the missing point for me.Drambuie
C
12

I shortened the code a bit.

import os, sys

if getattr(sys, 'frozen', False):
    application_path = os.path.dirname(sys.executable)
    os.chdir(application_path)

logging.debug('CWD: ' + os.getcwd())

But, sys._MEIPASS pointed to a wrong directory. I think it also needs sys._MEIPASS + \app_name

Cupellation answered 21/12, 2017 at 6:47 Comment(0)
H
8

I'm surprised nobody has mentioned that getattr() has a built-in default argument which will be returned if the attribute doesn't exist. This can also be made a bit more readable with pathlib. This code works whether or not the code is bundled with PyInstaller.

from pathlib import Path
bundle_dir = Path(getattr(sys, '_MEIPASS', Path.cwd()))
config_path = bundle_dir / 'myapp.cfg'
Hyades answered 4/9, 2020 at 0:30 Comment(2)
Shouldn't that last line be config_path = bundle_dir + '/myapp.cfg'? Otherwise good answerQuigley
@EdwardSpencer, nope, pathlib.Path objects let you use the / operator to build a path.Boutte
M
7

__file__ works from command line with python executable. It also gives the script file name without actual path in frozen mode. However it gives error in interactive mode.

The following will work for all three modes:

import sys,os

config_name = 'myapp.cfg'

if getattr(sys, 'frozen', False):
    application_path = os.path.dirname(sys.executable)
    running_mode = 'Frozen/executable'
else:
    try:
        app_full_path = os.path.realpath(__file__)
        application_path = os.path.dirname(app_full_path)
        running_mode = "Non-interactive (e.g. 'python myapp.py')"
    except NameError:
        application_path = os.getcwd()
        running_mode = 'Interactive'

config_full_path = os.path.join(application_path, config_name)

print('Running mode:', running_mode)
print('  Appliction path  :', application_path)
print('  Config full path :', config_full_path)

Output in three different modes:

Running mode: Interactive
  Appliction path  : C:\Projects\MyAppDir
  Config full path : C:\Projects\MyAppDir\myapp.cfg

C:\Projects\MyAppDir>myapp.exe
Running mode: Frozen/executable
  Appliction path  : C:\Program Files\myapp
  Config full path : C:\Program Files\myapp\myapp.cfg

C:\Projects\MyAppDir>python myapp.py
Running mode: Non-interactive (e.g. 'python myapp.py')
  Appliction path  : C:\Projects\MyAppDir
  Config full path : C:\Projects\MyAppDir\myapp.cfg

C:\Projects\MyAppDir>
Meteorite answered 15/2, 2018 at 22:13 Comment(2)
I have tried all of the above with an executable created with pyinstaller and this is the only answer that worked. ThanxMicrogroove
I also tried all other solutions and this is the only answer that worked for me as well. Thank you soo much!!!Shrill
P
4
os.path.dirname(sys.argv[0])

That works for me.

Prolix answered 17/1, 2013 at 5:31 Comment(1)
This only works if you use the absolute path to the executable when calling it. Using just the exe's name and having found via the PATH returns an empty path.Cartan
H
3

Many answers here but I found this solution works in most situations:

import os
import sys
import os.path as op
try:
    this_file = __file__
except NameError:
    this_file = sys.argv[0]
this_file = op.abspath(this_file)
if getattr(sys, 'frozen', False):
    application_path = getattr(sys, '_MEIPASS', op.dirname(sys.executable))
else:
    application_path = op.dirname(this_file)
Headless answered 26/4, 2018 at 3:27 Comment(1)
For me this results in the runtime path, in AppData\Local\Temp_MEI... the question was about the folder where the EXE file resides.Geostrophic
A
0

My case is using a service that runs an executable .exe build with pyinstaller. I use os.path.dirname(**os.path.realpath(sys.executable)**)

import os
import sys
# determine if application is a script file or frozen exe
if getattr(sys, 'frozen', False):
    application_path = os.path.dirname(os.path.realpath(sys.executable))
elif __file__:
    application_path = os.path.dirname(__file__)
Accolade answered 11/3, 2022 at 14:42 Comment(0)
I
0

Here's how I handle my pyinstaller relative paths if I have a folder next to the .exe I want to use:

import os
import pathlib

os.environ['base_path'] = os.path.dirname(sys.argv[0])
files_to_process = glob.glob( str(pathlib.Path(os.environ.get('base_path')) / 'to_process/*.pdf') )

This works whether you're executing as a script or exe.

Interstellar answered 8/6, 2023 at 15:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.