如何在用于插件的Objective-C OS X应用程序中嵌入python?

我正在尝试在新的OS X应用程序中使用 python进行插件脚本编写.我希望将一些程序逻辑卸载到python中,以便进行简单的即时修改.我从查看 Big Nerd Ranch tutorial开始.这似乎有效,但它提出了一种较旧的链接Python的方法.看来,从Xcode 5开始,我们应该使用 this Mac Developer Library Tech Note来安装python.但是,这个过程似乎创建了一个链接的python实例,而不是嵌入式实例.我已经尝试遵循 answer to this question中的指导方针,但似乎打破了.

所以我的问题是:在Objective C Mac OS X应用程序中构建python用作插件运行时的当前最佳实践是什么?如何确保它与应用程序捆绑在一起,并且可以在构建最终的Objective C应用程序之前安装任何可能需要的其他库?

解决方法

我想出了一种似乎运作良好的方法.

首先,从official website下载要使用的python版本的源代码.在某处提取存档.我正在使用Python 3.4.2.根据您使用的特定版本调整系统上的命令.

创建一个将用于此开发python版本的构建目录.整个目录中应该没有空格,以确保bash正确解释了she-bang(#!)行.我使用了/Users/myaccount/development/python/devbuild/python3.4.2.

进入解压缩的Python目录并运行以下命令:

./configure --prefix="/Users/myaccount/development/python/devbuild/python3.4.2"
make
make install

这将在该开发构建目录中安装python.设置Python路径以使用正确的目录:

export PYTHONPATH="/Users/myaccount/development/python/devbuild/python3.4.2/lib/python3.4/site-packages/"

进入python bin目录(/Users/myaccount/development/python/devbuild/python3.4.2/bin)并使用pip3安装你需要的任何模块. $PYTHONPATH设置将确保将模块安装到正确的site-packages目录中.

找到一个方便的PyObjC存储库的家,并在那里克隆它.然后签出最新的版本标签并安装它,确保你的$PYTHONPATH仍然正确:

hg clone https://bitbucket.org/ronaldoussoren/pyobjc
cd pyobjc
hg tags
hg checkout [the id of the latest version from the tag list]
/Users/myaccount/development/python/devbuild/python3.4.2/bin/python3 ./install.py

每当你需要更新python模块时,只需确保使用正确的python bin和$PYTHONPATH.

现在将python添加到Xcode项目中.

将/Users/myaccount/development/python/devbuild/python3.4.2目录拖到Xcode项目中,将其设置为不根据需要复制项目,以及创建文件夹引用.

将/Users/myaccount/development/python/devbuild/python3.4.2/include/python3.4m添加到Xcode项目的Build Settings中的Header Search Paths设置.不确定是否有办法将此作为一个通用步骤来搜索我们刚刚添加文件夹引用目录.

将`/Users/myaccount/development/python/devbuild/python3.4.2/lib/libpython3.4m.a库拖到Xcode项目中,将其设置为引用而不复制.

现在可以使用Big Nerd Ranch scripting tutorial repository代码进行一些修改.

插件管理器代码需要一个Nsstring扩展来处理Python API似乎非常喜欢的wchar_t字符串:

@interface Nsstring (WcharEncodedString)

- (wchar_t*) getWideString;

@end

@implementation Nsstring (WcharEncodedString)

- (wchar_t*) getWideString {
    const char* tmp = [self cStringUsingEncoding:NSUTF8StringEncoding];
    unsigned long buflen = strlen(tmp) + 1;
    wchar_t* buffer = malloc(buflen * sizeof(wchar_t));
    mbstowcs(buffer,tmp,buflen);
    return buffer;
}

@end

Python标题应包含如下:

#include "Python.h"

调用Py_Initialize()之前,需要运行以下代码,以便按照Zorg在其他问题上的建议设置正确的python可执行文件,PYTHONPATH和PYTHONHOME.

Nsstring* executablePath = [[NSBundle mainBundle] pathForResource:@"python3.4" ofType:nil inDirectory:@"python3.4.2/bin"];
Py_SetProgramName([executablePath getWideString]);

Nsstring* pythonDirectory = [[NSBundle mainBundle] pathForResource:@"python3.4" ofType:nil inDirectory:@"python3.4.2/lib"];
Py_SetPath([pythonDirectory getWideString]);
Py_SetPythonHome([pythonDirectory getWideString]);

最后,需要在PluginExecutor.py文件中扩展python路径以包含高级lib路径的各个子目录.将以下代码添加到Plugin Executor文件的顶部:

import sys
from os import walk
path = sys.path.copy()
for p in path:
    for root,dirs,files in walk(p):
        if p is not root:
            sys.path.append(root)

如果事情开始崩溃,我会发布更新,但现在这似乎是一个有效的解决方案.

相关文章

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评...
水了一学期的院选修,万万没想到期末考试还有比较硬核的编程...
补充一下,先前文章末尾给出的下载链接的完整代码含有部分C&...
思路如标题所说采用模N取余法,难点是这个除法过程如何实现。...
本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码...
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊...