janeh
janeh

Reputation: 3804

NSTask is not launching Python with correct path

In my Mac OS app I am including a Python.framework (v 2.7), so I added it to 'Linked frameworks' as Required. Also in the app, I am launching a Python script with NSTask like:

//...
pythonEnv = [[[NSBundle mainBundle] privateFrameworksPath] stringByAppendingPathComponent:@"Python.framework/Versions/2.7/bin/python"];
task = [[NSTask alloc] init];
outPipe = [NSPipe pipe];
readHandle = [outPipe fileHandleForReading];
data = [[NSMutableData alloc] init];

args = [NSArray arrayWithObjects: scriptPath, kbt, server, port, username, password, nil];

[task setArguments:args];
[task setLaunchPath: pythonEnv];

readHandle = [outPipe fileHandleForReading];
[task setStandardInput:[NSPipe pipe]];
[task setStandardOutput:outPipe];
[task launch];
[task waitUntilExit];
//...

When I build the app, and in Python script check to see which version it uses with sys.path, it returns:

["/Users/...path to script.../Contents/Resources", "/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip", "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7", "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin", "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac", "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages", "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk", "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old", "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload", "/Users/tatiana/Library/Python/2.7/lib/python/site-packages", "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages", "/Library/Python/2.7/site-packages"]

... it's not using my included framework. Where else should I be setting the correct path? Is it something in Build Settings I am forgetting?

UPDATE 1 ------------------------

If I don't set task environment, and in Python test with sys.executable, I get:

/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python

... so it looks like it's not using my framework. If set task environment with:

// pythonEnv = [[[NSBundle mainBundle] privateFrameworksPath] stringByAppendingPathComponent:@"Python.framework/Versions/2.7/bin/python"];
NSDictionary *environment = [NSDictionary dictionaryWithObjectsAndKeys: pythonEnv, @"PYTHONPATH", nil];
[task setEnvironment:environment];

and test it, I still get the same result. BUT, if in Python I test with os.environ["PYTHONPATH"], I get:

/Users/tatiana/Desktop/MyApp.app/Contents/Frameworks/Python.framework/Versions/2.7/bin/python

That looks a bit more promissing, but I am confused why sys.executable is not giving me that.

UPDATE 2

My bin/python was not an executable, so I changed it to python2.7; sys.executable still shows that it defaults to Library/...

pythonEnv = [[[NSBundle mainBundle] privateFrameworksPath] stringByAppendingPathComponent:@"Python.framework/Versions/2.7/bin/python2.7"];
NSString *searchPath = [[[NSBundle mainBundle] privateFrameworksPath] stringByAppendingPathComponent:@"Python.framework/Versions/2.7/lib/python2.7"];
NSDictionary *environment = [NSDictionary dictionaryWithObjectsAndKeys: searchPath, @"PYTHONPATH", nil];
[task setEnvironment:environment];

Upvotes: 0

Views: 1326

Answers (2)

Phil Frost
Phil Frost

Reputation: 3966

This may be a known bug. See this old mailing list post and this much more recent bug on bugs.python.org.

There's this comment in Modules/getpath.c of Python 2.7.3:

 /* On Mac OS X, if a script uses an interpreter of the form
  * "#!/opt/python2.3/bin/python", the kernel only passes "python"
  * as argv[0], which falls through to the $PATH search below.
  * If /opt/python2.3/bin isn't in your path, or is near the end,
  * this algorithm may incorrectly find /usr/bin/python. To work
  * around this, we can use _NSGetExecutablePath to get a better
  * hint of what the intended interpreter was, although this
  * will fail if a relative path was used. but in that case,
  * absolutize() should help us out below
  */

While this may not apply directly to you, this is a hint that maybe argv[0] in the executed python process is just "python". Python will then look at $PATH and try to find the absolute path to the Python interpreter if the _NSGetExecutablePath hack mentioned in the comment fails or is not present in your version. Your framework, not being in $PATH, is ignored, so it finds the system Python and uses that.

You could verify this a couple ways. You could set a breakpoint in a debugger and look at the arguments that NSTask eventually passes to exec(). Or, you may be able to look at ps ax and find your Python process (it may help to have your script print its PID and then run forever) and see if the first argument is the full path to your framework. Or, you could check out sys.argv[0] in your script (probably -- maybe it's modified).

If this is indeed the problem, the fix is to get a proper argv[0] set. I don't see a way to do that with NSTask, so your next best bet is probably to include the path to your Python framework's bin/ in the PATH environment variable. At least then Python may infer the correct value for sys.executable.

Or, if nothing in your script really cares about sys.executable, you could just leave it as is after you have determined that your Python is in fact the one being executed, dispite sys.executable.

Upvotes: 0

paulmelnikow
paulmelnikow

Reputation: 17208

It sounds like you're confusing two related things:

  1. Launching the Python executable that's inside the app bundle
  2. Setting the Python search path to include the Python lib folder that's inside the app bundle

To set the path of the Python executable that NSTask runs, set its launchPath. In Python you can access the value using sys.executable.

To manipulate Python's search path, invoke -[NSTask setEnvironment:] with PYTHONPATH set to your framework's lib/python2.7 folder. In Python you can access that path with sys.path.

Upvotes: 0

Related Questions