views:

341

answers:

3

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.

+1  A: 

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.

dertyp
hmm i'll give that a shot. im writing a function getAbsPath() which takes a relative path and just returns the right thing to open. it'll detect whether it's in the library.zip, and if it is, it'll go to a different dir instead, on the actual fs
Claudiu
You could also try to put your include files into a package directory, since the `__init__.py`'s `__path__` attribute will store the path of the `__init__.py` script.
dertyp
A: 

The usual approach to doing this sort of thing (if I understand properly) is this:

  1. check sys.frozen, which py2exe contrives to set, using something like getattr(sys, 'frozen', '')
  2. if that's not set, use the usual method since you're running from source
  3. if it's set, check sys.executable since that will point you to where the .exe is (instead of to where python.exe is, which is what it normally points to). Use something like os.path.dirname(sys.executable) and then join that with your relative paths to find subfolders etc

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.

Peter Hansen
A: 

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.

Plinio