如何防止嵌入式Python中sys.exit(N)结束整个进程?
我在我的应用程序中使用了嵌入式的Python解释器(具体是iOS)。
有时候在解释的脚本中会调用sys.exit(1)
,这会导致整个应用程序都结束,而不是仅仅停止PyObject_callObject()
的调用。我尝试用PyErr_Occured
来检查错误,但没有什么帮助。
怎么才能防止在嵌入式Python中调用sys.exit(N)时结束整个进程呢?
NSString *outputFile = nil;
for (int i=0; i<args.count; i++) {
if ([@"-o" isEqualToString:args[i]]) {
outputFile = args[i + 1];
break;
}
}
PyEval_AcquireLock();
PyThreadState *subState = Py_NewInterpreter();
PyObject *pModuleName, *pModule, *pFunc;
// init python
NSString *pythonHome = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/"];
Py_SetProgramName("python");
char *cPythonHome = (char*)[pythonHome UTF8String];
setenv("PYTHONPATH", cPythonHome, 1); // VERY IMPORTANT !!!
Py_SetPythonHome(cPythonHome);
NSString *libsPath = [pythonHome stringByAppendingString:@"lib/python2.7"];
if (!Py_IsInitialized())
Py_Initialize();
// invoke
int result = 0;
NSString *scriptFilename = args[1];
NSString *moduleName = [[scriptFilename lastPathComponent] stringByDeletingPathExtension];
pModuleName = PyString_FromString([moduleName UTF8String]); // module (script) name
pModule = PyImport_Import(pModuleName);
if (PyErr_Occurred())
PyErr_Print();
if (pModule != NULL) {
pFunc = PyObject_GetAttrString(pModule, "main__"); // module must have "def main__(args)"
if (pFunc != NULL && PyCallable_Check(pFunc)) {
// prepare args
PyObject *pArgs = PyList_New(args.count-1);
for (int i=0; i<args.count-1; i++) {
NSString *arg_i = args[i + 1]; // skip first argument (it's program name)
PyObject *pEachArg = PyString_FromString([arg_i UTF8String]);
PyList_SetItem(pArgs, i, pEachArg);
// WARNING: don't Py_DECREF for each argument
}
// for some reason arguments should be passed as s Tuple
PyObject *pTuple = PyTuple_New(1);
PyTuple_SetItem(pTuple, 0, pArgs);
// call func
NSLog(@"Invoke %@ via main__(args)", scriptFilename);
PyObject *pyResult = PyObject_CallObject(pFunc, pTuple); // process killed here !
if (pyResult == NULL || PyErr_Occurred()) {
// print error
PyErr_Print();
// fix error
PyErr_Clear();
if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
result = 3; // error: system.exit() called
} else
result = 4; // error: unknown exception
}
if (pyResult != NULL)
Py_DECREF(pyResult);
Py_DECREF(pTuple);
Py_DECREF(pArgs);
} else
result = 2; // error: can't find "def main__()" in module
if (pFunc != NULL)
Py_XDECREF(pFunc);
} else
result = 1; // error: can't import module
if (pModule != NULL)
Py_DECREF(pModule);
Py_DECREF(pModuleName);
// restore parent interpreter
Py_EndInterpreter(subState);
PyEval_ReleaseLock();
1 个回答
1
我不得不对Python的源代码进行一些修改,创建了我自己的函数:
int MyPyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit, PyCompilerFlags *flags)
{
PyObject *m, *d, *v;
const char *ext;
int set_file_name = 0, ret, len;
m = PyImport_AddModule("__main__");
if (m == NULL)
return -1;
d = PyModule_GetDict(m);
if (PyDict_GetItemString(d, "__file__") == NULL) {
PyObject *f = PyString_FromString(filename);
if (f == NULL)
return -1;
if (PyDict_SetItemString(d, "__file__", f) < 0) {
Py_DECREF(f);
return -1;
}
set_file_name = 1;
Py_DECREF(f);
}
len = strlen(filename);
ext = filename + len - (len > 4 ? 4 : 0);
/*
if (maybe_pyc_file(fp, filename, ext, closeit)) {
// Try to run a pyc file. First, re-open in binary
if (closeit)
fclose(fp);
if ((fp = fopen(filename, "rb")) == NULL) {
fprintf(stderr, "python: Can't reopen .pyc file\n");
ret = -1;
goto done;
}
// Turn on optimization if a .pyo file is given
if (strcmp(ext, ".pyo") == 0)
Py_OptimizeFlag = 1;
v = run_pyc_file(fp, filename, d, d, flags);
} else { */
v = PyRun_FileExFlags(fp, filename, Py_file_input, d, d,
closeit, flags);
//}
if (v == NULL) {
//PyErr_Print(); // crashed here!
ret = -1;
goto done;
}
Py_DECREF(v);
if (Py_FlushLine())
PyErr_Clear();
ret = 0;
done:
if (set_file_name && PyDict_DelItemString(d, "__file__"))
PyErr_Clear();
return ret;
}
我使用了 _SimpleFile
而不是 _SimpleString
,但我相信你也可以用类似的方式修改 _SimpleString
。
这样做的原因是因为 PyErr_Print
会导致应用崩溃。唯一的缺点是我不得不注释掉pyc文件的检查和使用,因为 maybe_pyc_file
没有被导出,所以无法使用。
还有一点要注意:如果抛出了 SystemExit
(比如通过 sys.exit(1)
),就不要使用 PyErr_Print
。可以使用下面的检查:
if (PyErr_Occurred()) {
pythonSuccess = NO;
if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
NSLog(@"sys.exit() in python");
} else {
// print error
PyErr_Print();
}
// fix error
PyErr_Clear();
}
如果你有更好的解决方案,请告诉我们。