Reputation: 229341
I have a Python app. It loads config files (and various other files) by doing stuff such as:
_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
CONFIG_DIR = os.path.join(_path, 'conf')
This works fine. However, when I package the app with py2exe, bad things happen:
File "proj\config.pyc", line 8, in <module>
WindowsError: [Error 3] The system cannot find the path specified: 'C:\\proj\
\dist\\library.zip\\conf'
Obviously that's an invalid path... What's a more robust way of doing this? I don't want to specify absolute paths in the program because it could be placed in different folders. Should I just say "if it says the folder name is 'library.zip', then go one more level down to the 'dist' folder"?
Note that I have pretty nested directory hierarchies... for example, I have a module gui.utils.images, stored in "gui/utils/images.py", and it uses its path to access "gui/images/ok.png", for example. Right now the py2exe version would try to access "proj/dist/library.zip/gui/images/ok.png", or something, which just won't work.
Upvotes: 4
Views: 5353
Reputation: 85603
I use the following trick in windows.
When the program is frozen in a py2exe 'executable' (i.e my_application.exe), dirname(__file__)
returns the folder where your main python script is running.
Oddly enough, that folder is not the folder containing my_application.exe but my_application.exe itself.
Note that my_application.exe is not a binary file but a compressed folder (you can unzip it) that contains python libs and your compiled scripts.
That's why the os.path.dirname
of your __file__
is in fact my_application.exe.
Thus, this code gives you the root directory for your configuration files or images (in case of a frozen application, the folder containing the py2exe executable, otherwise the folder from where your python script is running):
dirname = os.path.dirname(__file__)
if dirname.endswith('.exe'):
dirname = os.path.split(dirname)[0]
Obviously you can use more general, portable methods to detect if the file is frozen as other answers show instead of the endswith
method.
Upvotes: 0
Reputation: 31
Here's my solution (tested on Windows, Linux and Mac). It also works if the application or Python script is started via a symbolic link.
# Get if frozen
is_frozen = bool( getattr(sys, 'frozen', None) )
# Get path variable
path = sys.path if is_frozen else sys.argv
# Get nonempty first element or raise error
if path and path[0]:
apppath = path[0]
elif is_frozen():
raise RuntimeError('Cannot determine app path because sys.path[0] is empty.')
else:
raise RuntimeError('Cannot determine app path in interpreter mode.')
# Make absolute (eliminate symbolic links)
apppath = os.path.abspath( os.path.realpath(apppath) )
# Split and return
appdir, appname = os.path.split(apppath)
Upvotes: 3
Reputation: 1387
Use os.path.dirname(os.path.abspath(sys.argv[0])) from a py2exe app, it'll work the same way from the python script (so you can test without creating the exe every time) and from the exe.
This can be much better than using relative paths because you don't have to worry about where your app (.py or .exe) is running from.
Upvotes: 2
Reputation: 22047
The usual approach to doing this sort of thing (if I understand properly) is this:
Example:
frozen = getattr(sys, 'frozen', '')
if not frozen:
# not frozen: in regular python interpreter
approot = os.path.dirname(__file__)
elif frozen in ('dll', 'console_exe', 'windows_exe'):
# py2exe:
approot = os.path.dirname(sys.executable)
elif frozen in ('macosx_app',):
# py2app:
# Notes on how to find stuff on MAC, by an expert (Bob Ippolito):
# http://mail.python.org/pipermail/pythonmac-sig/2004-November/012121.html
approot = os.environ['RESOURCEPATH']
Or some variant thereof... the latter one handles the use of py2app. You could extend this for other builders, if needed.
Upvotes: 11
Reputation: 141
What do you think about using relative paths for all of the included files? I guess it should be possible to use sys.path.append(".." + os.path.sep + "images")
for your example about ok.png, then you could just open("ok.png", "rb")
. Using relative paths should fix the issues with the library.zip file that's generated by py2exe, at least that's what it does for me.
Upvotes: 2