2014-01-03 4 views
5

В настоящее время я работаю с Freeswitch и его event socket library (через mod event socket). Например:TypeError: аргумент типа 'char const *'

from ESL import ESLconnection 

cmd = 'uuid_kill %s' % active_call # active_call comes from a Django db and is unicode 
con = ESLconnection(config.HOST, config.PORT, config.PWD) 
if con.connected(): 
    e = con.api(str(cmd)) 
else: 
    logging.error('Couldn\'t connect to Freeswitch Mod Event Socket') 

Как вы можете видеть, я должен был явно привести con.api() «s аргумент с str(). Без этого, вызов заканчивается в следующем трассировки стека:

Traceback (most recent call last): 
    [...] 
    e = con.api(cmd) 
    File "/usr/lib64/python2.7/site-packages/ESL.py", line 87, in api 
    def api(*args): return apply(_ESL.ESLconnection_api, args) 
TypeError: in method 'ESLconnection_api', argument 2 of type 'char const *' 

Я не понимаю этого TypeError: что это значит? cmd содержит строку, так что она исправляет ее, когда я бросаю ее с str(cmd)?
Может ли это быть связано с API-интерфейсом Freeswitch python, созданным через SWIG?

+0

Какой тип 'active_call'? Если это unicode, то 'cmd' будет строкой Unicode, а' str (cmd) 'преобразует ее в строку. Вы можете вставить 'import pdb; pdb.set_trace() 'перед вызовом' con.api' и проверьте 'cmd'. – user4815162342

+0

'active_call', скорее всего, unicode, действительно (выходит из базы данных Django), но в этом сообщении об ошибке нет ничего, что заставило меня задуматься о unicode: как вы думаете, это может быть связано? – Anto

+2

Это, безусловно, связано: API, скорее всего, экспортируется через инфраструктуру построения интерфейса, которая знает, как преобразовать строковые объекты Python в 'char const *' (путем вызова 'PyString_AsString' объекта). Строки Unicode Python 2 не состоят из символов C - они состоят из 'wchar_t', что означает, что они не могут быть тривиально« отлиты »от' const char * ', их нужно преобразовать в новый буфер, который должен быть выделен, и т. Д. . Предварительно преобразуя строку Unicode в строку в Python, вы выполняете сложную часть до того, как объект когда-либо достигнет C. – user4815162342

ответ

9

Короткий ответ: cmd, вероятно, содержит строку Юникода, которая не может быть преобразована тривиально в const char *. Сообщение об ошибке, скорее всего, происходит из фреймворка-оболочки, который автоматизирует запись привязок Python для библиотек C, таких как SWIG или ctypes. Структура знает, что делать с байтовой строкой, но называет строки Unicode. Передача str(cmd) помогает, потому что она преобразует строку Unicode в байтовую строку, из которой значение const char *, ожидаемое кодом C, может быть тривиально извлечено.

Длинный ответ:

Тип C char const *, более обычно пишется const char *, может быть прочитана как «только для чтения массива char», char быть путь C, чтобы записать «байт». Когда функция C принимает const char *, она ожидает «C-строку», то есть массив значений char, заканчивающийся нулевым символом. Удобно, что строки Python внутренне представлены как строки C с некоторой дополнительной информацией, такой как тип, счетчик ссылок и длина строки (поэтому длину строки можно получить с помощью сложности O (1), а также чтобы строка могла содержать нулевые символы).

Строки Unicode в Python 2 представлены в виде массивов Py_UNICODE, которые имеют ширину 16 или 32 бит в зависимости от операционной системы и флагов времени сборки. Такой массив не может быть передан в код, который ожидает массив из 8-разрядных символов - он должен быть преобразован, как правило, во временный буфер, и этот буфер должен быть освобожден, когда он больше не нужен.

Например, простодушный (и совсем необязательно) обертка для функции C strlen может выглядеть следующим образом:

PyObject *strlen(PyObject *ignore, PyObject *obj) 
{ 
    const char *c_string; 
    size_t len; 
    if (!PyString_Check(obj)) { 
    PyErr_Format(PyExc_TypeError, "string expected, got %s", Py_TYPE(obj)->tp_name); 
    return NULL; 
    } 
    c_string = PyString_AsString(obj); 
    len = strlen(c_string); 
    return PyInt_FromLong((long) len); 
} 

код просто вызывает PyString_AsString, чтобы получить внутреннюю строку C сохраненную каждой строки Python и ожидается на strlen. Для этого кода также поддерживает Unicode объектов (при условии, что даже имеет смысл называть strlen на объекты Unicode), он должен обращаться с ними в явном виде:

PyObject *strlen(PyObject *ignore, PyObject *obj) 
{ 
    const char *c_string; 
    size_t len; 
    PyObject *tmp = NULL; 
    if (PyString_Check(obj)) 
    c_string = PyString_AsString(obj); 
    else if (PyUnicode_Check(obj)) { 
    if (!(tmp = PyUnicode_AsUTF8String(obj))) 
     return NULL; 
    c_string = PyString_AsString(tmp); 
    } 
    else { 
    PyErr_Format(PyExc_TypeError, "string or unicode expected, got %s", 
       Py_TYPE(obj)->tp_name); 
    return NULL; 
    } 
    len = strlen(c_string); 
    Py_XDECREF(tmp); 
    return PyInt_FromLong((long) len); 
} 

Обратите внимание на дополнительные сложности, а не только в линиях шаблонного кода, но в различные пути кода, которые требуют различного управления временным объектом, который содержит байтовое представление строки Unicode. Также обратите внимание, что код, необходимый для принятия решения о кодировке при преобразовании строки Unicode в байтовую строку. UTF-8 гарантированно сможет кодировать любую строку Unicode, но передача последовательности, кодированной UTF-8, функции, ожидающей строку C, может не иметь смысла для некоторых целей.Функция str использует кодек ASCII для кодирования строки Unicode, поэтому, если строка Unicode содержит любые символы, отличные от ASCII, вы получите исключение.

Там было requests to include this functionality in SWIG, но неясно, из связанного отчета, если они сделали это в

1

У меня была аналогичная проблема, и я решил ее сделать это:. cmd = 'uuid_kill %s'.encode('utf-8')

Смежные вопросы