2

I have a python script which reads a text file in it's current working directory called "data.txt" then converts the data inside of it into a json format for another separate program to handle.

The problem i'm having is that i'm not sure how to read the .txt file (and write a new one) which is in the same directory as the .app when the python script is all bundled up. The current method i'm using doesn't work because of something to do with it using the fact that it's ran from the terminal instead of executed as a .app?

Any help is appreciated!

Richard Bamford
  • 1,835
  • 3
  • 15
  • 19
  • How are you bundling it? – Blender Sep 20 '12 at 19:31
  • I'm using this: http://svn.pythonmac.org/py2app/py2app/trunk/doc/index.html – Richard Bamford Sep 20 '12 at 19:34
  • I guess this isn't a very common problem? – Richard Bamford Sep 20 '12 at 20:14
  • I've never worked with py2app so I can't give you much help. From what I remember, those bundlers give you some environment variable that contains the current execution path – Blender Sep 20 '12 at 20:15
  • oh snap, cool i'll look into it. cheers. Which one would you recommend? – Richard Bamford Sep 20 '12 at 20:27
  • I don't do anything with Mac, so I don't really know. I've used PyInstaller for Windows and it works pretty well. – Blender Sep 20 '12 at 20:27
  • The reason this isn't a very common problem is that it's something you usually don't want to do. If you're trying to package a .app/.exe, you usually want people to select files by double-click, drag&drop, an Open dialog, etc., not by placing them into the Program Files or Applications directory (which your program probably won't even have write access to). – abarnert Sep 21 '12 at 17:08

2 Answers2

2

A .app on the Mac doesn't have any reasonable current working directory when launched.

Of course it has some working directory, and you can easily find out what it is at runtime by os.getcwd(), and you can test on a variety of different ways of launching on different versions of OS X to figure out all of the patterns, but what good does that do you?

The good news is, you apparently don't actually want the current working directory; you need the directory of the .app bundle or .exe.

In other words, if someone does this:

C:\Users\foo> C:\Stuff\MyProgram.exe

You want C:\Stuff (the executable's directory), not C:\Users\foo (the working directory).

On Windows, this is easy. An .exe is just a file, and its path will be the __path__ you get in Python, so:

import os
pathToApp = os.path.dirname(__path__)

On Mac, it's harder. A .app is a bundle—a directory containing other files and directories. Somewhere in there is an executable interpreter and a copy of your script, and __path__ is going to give you the latter, not the path to the .app.

The correct way to get that is to use Cocoa (or CoreFoundation):

import Cocoa
pathToApp = Cocoa.NSBundle.mainBundle().bundlePath()

If you don't want to do that, you pretty much have to rely on some information that the documentation says you can't rely on and could change some day. But the following code should be safe:

import os
pathToApp = __file__
while not pathToApp.endswith('.app'):
  path = os.path.dirname(path)

In order for this to stop working, either the script would have to be outside the .app bundle, or inside another .app inside the one you're looking for, or bundles would have to stop being named .app, or they'd have to stop being structured as normal directories; none of this seems likely to change in OS X 10.*, or even OS Y 11.

As a side issue: what you're trying to do is most likely a bad idea in the first place. A Mac application shouldn't be working with files alongside it. Conversely, if users are going to expect to work on files alongside it, you probably want a simple Unix executable (or just a plain Python script with chmod +x), not an application.

abarnert
  • 354,177
  • 51
  • 601
  • 671
0

Use the __file__ variable. This will give you the filename of your module. Using the functions in os.path you can determine the full path of the parent directory of your module. The os.path module is in the standard python documentation, you should be able to find that.

Then you can combine the module path with your filename to open it, using os.path.join.

Hans Then
  • 10,935
  • 3
  • 32
  • 51
  • I've been using os.getcwd() to get the cwd. Does the method you provide produce a different output? i'll tell you in a minute if it works. – Richard Bamford Sep 20 '12 at 20:51
  • `os.getcwd()` will return the current working directory, so this will fail if your application is called from a different directory. E.g. when your application is called like this `python ../myapp.py`. – Hans Then Sep 20 '12 at 20:55
  • ah i see, thanks for the insight i'm working on implementing this now – Richard Bamford Sep 20 '12 at 21:13
  • This is fine for Windows, but not for Mac. If the app is `/Applications/MyProgram.app`, `__path__` is likely to be something like `/Applications/MyProgram.app/Contents/Resources/MyProgram.py`. Unless you actually want to write to that directory deep in the middle of the bundle, you need the path to the bundle, not the script. – abarnert Sep 20 '12 at 21:45