Как получить доступ к sys.stderr после вызова PyErr_Print с Python C-API - PullRequest
0 голосов
/ 16 апреля 2019

Я пытаюсь использовать 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 #?

Ответы [ 2 ]

1 голос
/ 19 апреля 2019

Этот ответ дает код на C, потому что я понимаю C, а не C #, но я думаю, что он должен быть вполне переносимым.читать из негоТем не менее, вполне возможно заменить его, чтобы перенаправить вывод.Два разумных варианта включают запись в файл и запись в объект 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,&temp_result,&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 result;

}

При копировании строки потрачено немного усилий.Вы можете рассмотреть возможность работы непосредственно с Unicode и использовать PyUnicode_AsUCS4Copy.

Возможно, вы захотите посмотреть на очистку строки после ее написания, просто сделав замену sys.stderr насвежий StringIO объект.

0 голосов
/ 18 апреля 2019

Я не смог найти способ доступа 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')
...