#c# #python #python-c-api
#c# #python #python-c-api
Вопрос:
Я пытаюсь использовать python c-api из проекта c # через импорт dll.
Я получаю ModuleNotFoundError
при импорте некоторые модули, которые, как я думал, являются встроенными. (Примечание: я сам скомпилировал python)
Сейчас я немного застрял, но я надеюсь получить дополнительную информацию при вызове PyErr_Print()
приведенного ниже кода.
Код:
IntPtr modulePtr = NativeInterface.PyImport_ExecCodeModuleEx(moduleName,compiledModule, path);
if (modulePtr == IntPtr.Zero)
{
NativeInterface.PyErr_Print();
PythonException exception = PythonException.Query();
throw exception;
}
В документах для PyErr_Print указано, что он будет sys.stderr
заполнен некоторой информацией об ошибке.
Каков был бы самый простой способ прочитать эту переменную из моего приложения на c #?
Комментарии:
1. Я не знаю достаточно C #, чтобы действительно дать на это ответ, но я бы сделал что-то вроде: 1) заменить
sys.stdout
наBytesIO
объект. 2) Получить содержимое этогоBytesIO
объекта сPyBytes_AsString
помощью . Оба шага немного сложнее, чем я понял, но вы, вероятно, можете написать этот код на Python.
Ответ №1:
Этот ответ дает код C, потому что я понимаю C, а не C #, но я думаю, что он должен быть довольно переносимым.
По умолчанию sys.stderr
выполняется запись в какую-то консоль, и поэтому вы не можете осмысленно пытаться читать из нее. Однако вполне возможно заменить его, чтобы перенаправить вывод. Два разумных варианта включают запись в файл и запись в StringIO
объект, который позже может быть запрошен.
Код C для запуска в основном эквивалентен:
import sys
from io import StringIO # Python 3
sys.stderr = StringIO()
или в C:
int setup_stderr() {
PyObject *io = NULL, *stringio = NULL, *stringioinstance = NULL;
int success = 0;
io = PyImport_ImportModule("io");
if (!io) goto done;
stringio = PyObject_GetAttrString(io,"StringIO");
if (!stringio) goto done;
stringioinstance = PyObject_CallFunctionObjArgs(stringio,NULL);
if (!stringioinstance) goto done;
if (PySys_SetObject("stderr",stringioinstance)==-1) goto done;
success = 1;
done:
Py_XDECREF(stringioinstance);
Py_XDECREF(stringio);
Py_XDECREF(io);
return success;
}
Вы запускаете это один раз в начале своей программы.
Чтобы запросить содержимое sys.stderr
, вы должны выполнить эквивалент:
value = sys.stderr.getvalue()
encoded_value = value.encode() # or you could just handle the unicode output
В C:
char* get_stderr_text() {
PyObject* stderr = PySys_GetObject("stderr"); // borrowed reference
PyObject *value = NULL, *encoded = NULL;
char* result = NULL;
char* temp_result = NULL;
Py_ssize_t size = 0;
value = PyObject_CallMethod(stderr,"getvalue",NULL);
if (!value) goto done;
// ideally should get the preferred encoding
encoded = PyUnicode_AsEncodedString(value,"utf-8","strict");
if (!encoded) goto done;
if (PyBytes_AsStringAndSize(encoded,amp;temp_result,amp;size) == -1) goto done;
size = 1;
// copy so we own the memory
result = malloc(sizeof(char)*size);
for (int i = 0; i<size; i) {
result[i] = temp_result[i];
}
done:
Py_XDECREF(encoded);
Py_XDECREF(value);
return resu<
}
Требуется немного усилий для копирования строки. Вы могли бы рассмотреть возможность прямой работы с unicode и использования PyUnicode_AsUCS4Copy
.
Вероятно, затем вы захотите посмотреть на очистку строки после ее написания, просто sys.stderr
заменив ее новым StringIO
объектом.
Комментарии:
1. Спасибо вам за этот приятный ответ. Я решил свою проблему, приняв скрипт python, который я вызвал через C-API. Однако мой первоначальный вопрос заключался в том, как решить эту проблему с помощью C-API. Поэтому я думаю, что предпочтительнее принять ваш ответ.
Ответ №2:
Я не смог найти способ доступа sys.stderr
через C-Api. Но я понял, что могу запускать скрипты python через c-api (см. PyRun_String в документации). Итак, сейчас я выполняю отладку, записывая sys.path в текстовый файл.
import sys
file = open('log.txt','w')
for path in sys.path:
file.write(i 'n')