117

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?

Mathias711
  • 6,568
  • 4
  • 41
  • 58
Soviut
  • 88,194
  • 49
  • 192
  • 260
  • possible duplicate of [how can i get the executable's current directory in py2exe?](http://stackoverflow.com/questions/2292703/how-can-i-get-the-executables-current-directory-in-py2exe) -- This current question is older but the other question has more answers and is more documented. – oHo Nov 12 '13 at 15:07
  • 12
    These have a lot in common but py2exe != pyinstaller – demux Oct 26 '15 at 00:18

9 Answers9

170

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)
Georg Schölly
  • 124,188
  • 49
  • 220
  • 267
Soviut
  • 88,194
  • 49
  • 192
  • 260
99

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.

phoenix
  • 7,988
  • 6
  • 39
  • 45
normanius
  • 8,629
  • 7
  • 53
  • 83
  • 1
    Can confirm that this works on Windows 10 and Windows 7 with PyInstaller 3.2 and Python 3.4 -- other answers no longer work. –  Jun 05 '17 at 19:31
  • 1
    just adding that according to the docs this works for both one-file and one-folder distributions – mac Mar 29 '18 at 12:55
  • 56
    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. – Max Tet Jul 12 '18 at 10:26
  • 14
    for a --onefile executable, the path to the application is given by `application_path = os.path.dirname(sys.executable)` – masher Apr 30 '21 at 09:22
  • Good. Now i can have the sqlite3 in other folder in production and other in the "release build (exe)". – Chris P Sep 27 '21 at 23:04
  • 1
    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. – Dhara Vihol Apr 25 '22 at 19:26
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

Polv
  • 1,918
  • 1
  • 20
  • 31
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>
Rafiq
  • 1,380
  • 4
  • 16
  • 31
  • I have tried all of the above with an executable created with pyinstaller and this is the only answer that worked. Thanx – opperman.eric Jun 17 '22 at 23:40
  • I also tried all other solutions and this is the only answer that worked for me as well. Thank you soo much!!! – Paul D. Oct 10 '22 at 22:12
7

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'
Elliott B
  • 980
  • 9
  • 32
4
os.path.dirname(sys.argv[0])

That works for me.

  • 6
    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. – Ber Jul 18 '13 at 12:59
2

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)
dashesy
  • 2,596
  • 3
  • 45
  • 61
  • For me this results in the runtime path, in AppData\Local\Temp\_MEI... the question was about the folder where the EXE file resides. – cslotty Mar 23 '20 at 17:22
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__)
Soviut
  • 88,194
  • 49
  • 192
  • 260
Igor Micev
  • 1,514
  • 1
  • 17
  • 23
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.

grantr
  • 878
  • 8
  • 16