2013-04-05 2 views
4

Я работаю над системой, которая встраивает интерпретатор Python, и мне нужно построить PyObject*, задав строку из C API.Создать объект из представления строки из C API

У меня есть const char*, представляющий словарь, в правильном формате для eval() для правильной работы с Python, то есть: "{'bar': 42, 'baz': 50}".

В настоящее время это передается в Python как PyObject* с использованием Py_Unicode_ API (представляющий строку), так что в моем питона переводчика, я могу успешно написать:

foo = eval(myObject.value) 
print(foo['bar']) # prints 42 

Я хотел бы изменить это автоматически «eval» const char* на стороне C и вернуть PyObject*, представляющий законченный словарь. Как мне перейти на преобразование этой строки в словарь в C API?

+1

Вы не хотите использовать 'ast.literal_eval' для этого вместо' eval'? (Это не влияет на ответ, но об этом стоит подумать.) – abarnert

+0

@abarnert Строки, которые обрабатываются, все под моим контролем, поэтому я не думаю, что мне нужно это сделать - это всегда будет словарь, содержащий различные значения строковых значений float, int, bool или (правильно экранированные). –

+1

Если это всегда словарь, содержащий float, int, bool и строки, почему _not_ использовать 'literal_eval'? Это немного проще, и вы получаете бесплатную проверку. – abarnert

ответ

3

Существует два основных способа сделать это.

Первый - это просто позвонить eval так же, как и в Python. Единственный трюк в том, что вам нужен дескриптор модуля builtins, потому что вы не получите его бесплатно в C API. Есть несколько способов сделать это, но один очень простой способ, это просто импортировать:

/* or PyEval_GetBuiltins() if you know you're at the interpreter's top level */ 
PyObject *builtins = PyImport_ImportModule("builtins"); 
PyObject *eval = PyObject_GetAttrString(builtins, "eval"); 
PyObject *args = Py_BuildValue("(s)", expression_as_c_string); 
PyObject *result = PyObject_Call(eval, args); 

(Это непроверенный код, и это по крайней мере утечки ссылки, и не проверяет NULL возврата, если вы хотите обрабатывать исключения на стороне C ... Но этого должно быть достаточно, чтобы передумать.)

Одна хорошая вещь в том, что вы можете использовать ast.literal_eval точно так же, как eval (это означает, что вы получаете некоторые бесплатная проверка); просто измените "builtins" на "ast" и "eval" на "literal_eval". Но реальная победа в том, что вы делаете именно то, что делает eval в Python, что вы уже знаете, именно то, что вы хотели.

Альтернативой является использование API-интерфейсов компиляции. На действительно высоком уровне вы можете просто создать оператор Python из "foo = eval(%s)" и PyRun_SimpleString. Ниже, используйте Py_CompileString для синтаксического анализа и компиляции выражения (вы также можете анализировать и компилировать отдельные шаги, но это не полезно здесь), затем PyEval_EvalCode, чтобы оценить его в соответствующих глобальных и локальных. (Если вы не отслеживаете глобальные переменные самостоятельно, используйте interpreter-reflection APIsPyEval_GetLocals и PyEval_GetGlobals.) Обратите внимание, что я предоставляю супер-упрощенную версию каждой функции; часто вы хотите использовать одну из функций брата и сестры. Но вы можете легко найти их в документах.

+1

У этого почти есть это - похоже, что 'eval' ожидает кортеж, поэтому мне нужно обернуть args в' PyTuple_Pack', но в остальном это работает (+ добавление обработки ссылок) , –

+0

На стороне примечания - любая причина, по которой вы использовали 'Py_BuildValue (" s ",' vs 'PyUnicode_FromString'? –

+1

@ReedCopsey: Извините, это глупая опечатка. Я хотел сделать' '(s)" ', чтобы построить' str', а затем выстроить arg tuple из этого за один шаг вместо того, чтобы сначала построить 'str', а затем' tuple'. Хороший catch. – abarnert

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