Как получить доступ к sys.stderr после вызова PyErr_Print с помощью python c-api

#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')