你如何使用python3 c api为命令行驱动的应用程序?

我已经使用自定义构建作为virtualenv的替代品已经有一段时间了,而且它很棒。 它需要更长的时间才能构建,但实际上它可以工作,并且它永远不会搞砸。

部分内容在一个简单的python包装器中,它将一些特定的文件夹添加到库路径中,我发现它非常有用。 它的代码是微不足道的:

#include  #include  #include  int main(int argc, char *argv[]) { /* Setup */ Py_SetProgramName(argv[0]); Py_Initialize(); PySys_SetArgv(argc, argv); /* Add local path */ PyObject *sys = PyImport_ImportModule("sys"); PyObject *path = PyObject_GetAttrString(sys, "path"); /* Custom path */ char *cwd = nrealpath(argv[0]); char *libdir = nstrpath(cwd, "python_lib", NULL); PyList_Append(path, PyString_FromString(libdir)); free(cwd); free(libdir); /* Run the 'main' module */ int rtn = Py_Main(argc, argv); // <-- Notice the command line arguments. Py_Finalize(); return rtn; } 

那么,转向python3是对的吗? 所以…

我尽职尽责地用PyByte_FromString()替换了对PyString_FromString()的调用并尝试重新编译,但它引发了错误:

 /Users/doug/env/src/main.c:8:21: error: incompatible pointer types passing 'char *' to parameter of type 'wchar_t *' (aka 'int *') [-Werror,-Wincompatible-pointer-types] Py_SetProgramName(argv[0]); ^~~~~~~ /Users/doug/projects/py-sdl2/py3/include/python3.3m/pythonrun.h:25:45: note: passing argument to parameter here PyAPI_FUNC(void) Py_SetProgramName(wchar_t *); ^ /Users/doug/env/src/main.c:10:23: error: incompatible pointer types passing 'char **' to parameter of type 'wchar_t **' (aka 'int **') [-Werror,-Wincompatible-pointer-types] PySys_SetArgv(argc, argv); ^~~~ /Users/doug/projects/py-sdl2/py3/include/python3.3m/sysmodule.h:12:47: note: passing argument to parameter here PyAPI_FUNC(void) PySys_SetArgv(int, wchar_t **); ^ /Users/doug/env/src/main.c:24:27: error: incompatible pointer types passing 'char **' to parameter of type 'wchar_t **' (aka 'int **') [-Werror,-Wincompatible-pointer-types] int rtn = Py_Main(argc, argv); ^~~~ /Users/doug/projects/py-sdl2/py3/include/python3.3m/pythonrun.h:148:45: note: passing argument to parameter 'argv' here PyAPI_FUNC(int) Py_Main(int argc, wchar_t **argv); ^ 3 errors generated. make[2]: *** [CMakeFiles/python.dir/src/main.co] Error 1 make[1]: *** [CMakeFiles/python.dir/all] Error 2 make: *** [all] Error 2 

从错误中可以看出,使用wchar_t而不是char *。

你怎么用这个api?

我看到有一些例子可以做到这一点,例如: http : //svn.python.org/projects/python/tags/r32rc2/Python/frozenmain.c

当真?

我的29线程序必须成为一个充满#ifdefs的110线怪物?

我误解了,还是python3 c api真的变得非常难以使用?

当然,我错过了一些明显的便利function,它以简单,便携和跨平台的方式为您做到这一点?

官方推荐的从char转换为wchar_t是使用Py_DecodeLocale 。 像这样:

 wchar_t *program = Py_DecodeLocale(argv[0], NULL); Py_SetProgramName(program); 

似乎没有简单的方法来做到这一点。

我最接近下面。 我会在模糊的希望中打开这个问题,有人会出现并向我展示超级简单易行的方法。

 #include  #include  #include  int main(int argc, char *argv[]) { /* These have to be wchar_t */ char *str_program_name = argv[0]; char **str_argv = argv; /* For ever stupid reason, these don't need to be wchar_t * */ char *_sys = "sys"; char *_libdir = "lib"; char *_path = "path"; char *_dot = "."; #if PY_MAJOR_VERSION >= 3 wchar_t **_argv = nstrws_array(argc, str_argv); wchar_t *_program_name = nstrws_convert(str_program_name); #else char **_argv = str_argv; char *_program_name = str_program_name; #endif /* Setup */ Py_SetProgramName(_program_name); Py_Initialize(); /* Add local path */ #if PY_MAJOR_VERSION >= 3 PyObject *sys = PyImport_ImportModule(_sys); PyObject *path = PyObject_GetAttrString(sys, _path); PyList_Append(path, PyBytes_FromString(_dot)); PyList_Append(path, PyBytes_FromString(_libdir)); #else PyObject *sys = PyImport_ImportModule(_sys); PyObject *path = PyObject_GetAttrString(sys, _path); PyList_Append(path, PyString_FromString(_dot)); PyList_Append(path, PyString_FromString(_libdir)); #endif /* Run the 'main' module */ int rtn = Py_Main(argc, _argv); Py_Finalize(); #if PY_MAJOR_VERSION >= 3 nstrws_dispose(argc, _argv); free(_program_name); #endif return rtn; } 

使用:

 /** Unix-like platform char * to wchar_t conversion. */ wchar_t *nstrws_convert(char *raw) { wchar_t *rtn = (wchar_t *) calloc(1, (sizeof(wchar_t) * (strlen(raw) + 1))); setlocale(LC_ALL,"en_US.UTF-8"); // Unless you do this python 3 crashes. mbstowcs(rtn, raw, strlen(raw)); return rtn; } /** Dispose of an array of wchar_t * */ void nstrws_dispose(int count, wchar_t ** values) { for (int i = 0; i < count; i++) { free(values[i]); } free(values); } /** Convert an array of strings to wchar_t * all at once. */ wchar_t **nstrws_array(int argc, char *argv[]) { wchar_t **rtn = (wchar_t **) calloc(argc, sizeof(wchar_t *)); for (int i = 0; i < argc; i++) { rtn[i] = nstrws_convert(argv[i]); } return rtn; } 

对于Windows用户,如果需要:

 #include  /** Windows char * to wchar_t conversion. */ wchar_t *nstrws_convert(char *raw) { int size_needed = MultiByteToWideChar(CP_UTF8, 0, raw, -1, NULL, 0); wchar_t *rtn = (wchar_t *) calloc(1, size_needed * sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, raw, -1, rtn, size_needed); return rtn; } 

这可能是错误的做法,从来没有:

 Py_SetProgramName((wchar_t*)argv[0]); 

这个修复程序阻止我的代码抱怨,没有测试它知道它如何处理args,但至少它编译..

我发现这适用于在main函数中将char *转换为wchar_t *:

 wchar_t progname[FILENAME_MAX + 1]; mbstowcs(progname, argv[0], strlen(argv[0]) + 1); Py_SetProgramName(progname); 

如果您使用的是unix:

 #include "sys/param.h"