20

I use this bit of code in my script to pinpoint, in a cross-platform way, where exactly it's being run from:

SCRIPT_ROOT = os.path.dirname(os.path.realpath(__file__))

Pretty simple. I then go on to use SCRIPT_ROOT in other areas of my script to make sure everything is properly relative. My problem occurs when I run it through py2exe, because the generated executable doesn't set __file__, therefore my script breaks. Does anyone know how to fix or work around this?

Chris Lamberson
  • 201
  • 1
  • 2
  • 3
  • There is an older duplicated question but with less answers: [Determining application path in a Python EXE generated by pyInstaller](http://stackoverflow.com/questions/404744) – oHo Nov 12 '13 at 15:03

4 Answers4

27

Here is the py2exe documentation reference and here are the relevant items:

  • sys.executable is set to the full pathname of the exe-file.
  • The first item in sys.argv is the full pathname of the executable, the rest are the command line arguments.
  • sys.frozen only exists in the executable. It is set to "console_exe" for a console executable, to "windows_exe" for a console-less gui executable, and to "dll" for a inprocess dll server.
  • __file__ is not defined (you might want to use sys.argv[0] instead)

It is not apparent from those docs whether "the exe-file" and "the executable" are the same thing, and thus whether sys.executable and sys.argv[0] are the same thing. Looking at code that worked for both script.py and py2exe_executable.exe last time I had to do this, I find something like:

if hasattr(sys, 'frozen'):
    basis = sys.executable
else:
    basis = sys.argv[0]
required_folder = os.path.split(basis)[0]

As I say that worked, but I don't recall why I thought that was necessary instead of just using sys.argv[0].

Using only basis was adequate for the job in hand (read files in that directory). For a more permanent record, split something like os.path.realpath(basis).

Update Actually did a test; beats guesswork and armchair pontification :-)

Summary: Ignore sys.frozen, ignore sys.executable, go with sys.argv[0] unconditionally.

Evidence:

=== foo.py ===

# coding: ascii
import sys, os.path
print 'sys has frozen:', hasattr(sys, 'frozen')
print 'using sys.executable:', repr(os.path.dirname(os.path.realpath(sys.executable)))
print 'using sys.argv[0]:',    repr(os.path.dirname(os.path.realpath(sys.argv[0]   )))

=== setup.py ===

from distutils.core import setup
import py2exe
setup(console=['foo.py'])

=== results ===

C:\junk\so\py2exe>\python26\python foo.py
sys has frozen: False
using sys.executable: 'C:\\python26'
using sys.argv[0]: 'C:\\junk\\so\\py2exe' # where foo.py lives

C:\junk\so\py2exe>dist\foo
sys has frozen: True
using sys.executable: 'C:\\junk\\so\\py2exe\\dist'
using sys.argv[0]: 'C:\\junk\\so\\py2exe\\dist' # where foo.exe lives
John Machin
  • 81,303
  • 11
  • 141
  • 189
  • I believe that sys.executable usually points to the python interpreter itself, which is what you want under py2exe, but not otherwise. – prestomation Feb 19 '10 at 03:19
  • @prestomation: What is "the Python interpreter itself" in the py2exe case? What do you mean by "but not otherwise"? – John Machin Feb 19 '10 at 04:12
  • "otherwise" is when you are running an interpreted file, which is most environments. In the py2exe case, I would imagine the interpreter is your py2exe, because it's included. It looks like your most recent edit confirms that. – prestomation Feb 19 '10 at 14:27
  • What happens if you put the executable into a directory on the search path (PATH env. variable)? I find that `sys.argv[0]` does not have a directory component in this case. – Ber Jul 18 '13 at 13:05
  • @Ber yes I think that sys.argv[0] will capture the current working directory and may not be what you want... – Har Jan 28 '16 at 11:17
9

Py2exe does not define __file__: http://www.py2exe.org/index.cgi/Py2exeEnvironment

The OP requested a py2exe friendly version of:

SCRIPT_ROOT = os.path.dirname(os.path.realpath(__file__))

The best answer is to determine if python is frozen in an exe, py2exe has documentation on this: http://www.py2exe.org/index.cgi/HowToDetermineIfRunningFromExe

import imp, os, sys

def main_is_frozen():
   return (hasattr(sys, "frozen") or # new py2exe
           hasattr(sys, "importers") # old py2exe
           or imp.is_frozen("__main__")) # tools/freeze

def get_main_dir():
   if main_is_frozen():
       return os.path.dirname(sys.executable)
   return os.path.dirname(os.path.realpath(__file__))

SCRIPT_ROOT = get_main_dir()

Since, the python is EAFP, here's an EAFP version ...

try:
   if sys.frozen or sys.importers:
      SCRIPT_ROOT = os.path.dirname(sys.executable)
except AttributeError:
   SCRIPT_ROOT = os.path.dirname(os.path.realpath(__file__))

Cheers!

VertigoRay
  • 5,935
  • 6
  • 39
  • 48
3

sys.argv[0] is a reliable way to get the path, as it will give the same result irrespective of being run as a script or exe . To get the directory os.path.dirname(sys.argv[0])

comparison between file and exe

Siva Prakash
  • 4,626
  • 34
  • 26
2

Try this:

import os
import sys
os.path.realpath(os.path.dirname(sys.argv[0]))
prestomation
  • 7,225
  • 3
  • 39
  • 37
  • 1
    In my experience, using `sys.argv [0]` does not have a path to it when the program is found via the PATH variable. – Ber Jul 18 '13 at 13:03
  • @Ber, ...however, `py2exe` is a special case, and does set that to a fully qualified value in all cases. – Charles Duffy May 26 '15 at 19:32