Управление журналами / предупреждениями в расширениях Python - PullRequest
2 голосов
/ 08 июня 2010

TL; версия DR: Что вы используете для настраиваемой (и предпочтительно захваченной) регистрации внутри ваших битов C ++ в проекте Python? Подробности следуют.

Допустим, у вас есть несколько скомпилированных .so модулей, которые, возможно, должны выполнить некоторую проверку ошибок и предупредить пользователя о (частично) неверных данных. В настоящее время у меня довольно упрощенная установка, где я использую logging фреймворк из кода Python и log4cxx библиотеку из C / C ++. log4cxx Уровень журнала определен в файле (log4cxx.properties) и в настоящее время исправлен, и я думаю, как сделать его более гибким. Пара вариантов, которые я вижу:

  1. Один из способов управления им - это вызов конфигурации для всего модуля.

    # foo/__init__.py
    import sys
    from _foo import import bar, baz, configure_log
    configure_log(sys.stdout, WARNING)
    
    # tests/test_foo.py
    def test_foo():
        # Maybe a custom context to change the logfile for 
        # the module and restore it at the end.
        with CaptureLog(foo) as log:
            assert foo.bar() == 5
            assert log.read() == "124.24 - foo - INFO - Bar returning 5"
    
  2. Пусть каждая скомпилированная функция, выполняющая ведение журнала, принимает необязательные параметры журнала.

    # foo.c
    int bar(PyObject* x, PyObject* logfile, PyObject* loglevel) {
        LoggerPtr logger = default_logger("foo");
        if (logfile != Py_None)
            logger = file_logger(logfile, loglevel);
        ...
    }
    
    # tests/test_foo.py
    def test_foo():
        with TemporaryFile() as logfile:
            assert foo.bar(logfile=logfile, loglevel=DEBUG) == 5
            assert logfile.read() == "124.24 - foo - INFO - Bar returning 5"
    
  3. Как-то иначе?

Второй выглядит несколько чище, но требует изменения сигнатуры функции (или использования kwargs и их анализа). Во-первых, это, вероятно, несколько неудобно, но настраивает весь модуль за один раз и удаляет логику из каждой отдельной функции.

Что вы думаете об этом? Я все уши к альтернативным решениям, а также.

Спасибо

Ответы [ 2 ]

2 голосов
/ 08 июня 2010

Я большой сторонник того, чтобы в Python происходило как можно больше работы, оставляя только ту работу, которая должна происходить в C в C. Так что мне нравится № 2 лучше, чем # 1, но вы правы, это беспорядок все подписи вашей функции.

Я бы создал объект уровня модуля для обработки журналов, что-то вроде обратного вызова. Код Python может создать объект любым удобным для него способом, а затем назначить его объекту модуля. Код C может просто использовать глобальный объект для ведения своей регистрации:

# Python:

import my_compiled_module

def log_it(level, msg):
    print "%s: Oh, look: %s" % (level, msg)

my_compiled_module.logger = log_it

# C

static void log_it(unsigned int level, char * msg)
{
    PyObject * args = Py_BuildValue("(Is)", level, msg);
    PyObject_Call(log_it, args, NULL);
    Py_DECREF(args);
}

Теперь вы можете просто вызывать функцию C log_it по всему коду и не беспокоиться о том, как это делает код Python. Конечно, ваша функция Python log_it будет богаче, чем эта, и она позволит вам объединить все ваши записи в один Python logger.

0 голосов
/ 11 ноября 2011

Спасибо за информацию, ребята.Я обнаружил, что PyObject_CallFunction проще в использовании.

// C++ code with logger passed as 2nd argument
static PyObject *lap_auction_assign(PyObject *self, PyObject *args)
{
  PyObject *cost_dict;  PyObject *pyLogger;
 /* the O! parses for a Python object (listObj) checked to be of type PyList_Type */
  if( !PyArg_ParseTuple( args, "O!O", &PyDict_Type, &cost_dict, &pyLogger)) 
    return NULL;
/*
....... then call the logger
*/

char astring[2048];

sprintf( astring, "%d out of %d rows un-assigned", no_new_list, num_rows );
PyObject_CallFunction( pyLogger, "s", astring );

/* python */
# where logging is the python logging module and lap_auction is your extension
cost_dict = {}
tmp_assign_dict = lap_auction.assign( cost_dict, logging.info )
...