1 // Attempts to load a UCL structure from a string
2 #include <ucl.h>
3 #include <Python.h>
4
5 static PyObject *SchemaError;
6
7 static PyObject *
_basic_ucl_type(ucl_object_t const * obj)8 _basic_ucl_type (ucl_object_t const *obj)
9 {
10 switch (obj->type) {
11 case UCL_INT:
12 return Py_BuildValue ("L", (long long)ucl_object_toint (obj));
13 case UCL_FLOAT:
14 return Py_BuildValue ("d", ucl_object_todouble (obj));
15 case UCL_STRING:
16 return Py_BuildValue ("s", ucl_object_tostring (obj));
17 case UCL_BOOLEAN:
18 return PyBool_FromLong (ucl_object_toboolean (obj));
19 case UCL_TIME:
20 return Py_BuildValue ("d", ucl_object_todouble (obj));
21 case UCL_NULL:
22 Py_RETURN_NONE;
23 }
24 return NULL;
25 }
26
27 static PyObject *
_iterate_valid_ucl(ucl_object_t const * obj)28 _iterate_valid_ucl (ucl_object_t const *obj)
29 {
30 const ucl_object_t *tmp;
31 ucl_object_iter_t it = NULL;
32
33 tmp = obj;
34
35 while ((obj = ucl_object_iterate (tmp, &it, false))) {
36 PyObject *val;
37
38 val = _basic_ucl_type(obj);
39 if (!val) {
40 PyObject *key = NULL;
41
42 if (obj->key != NULL) {
43 key = Py_BuildValue("s", ucl_object_key(obj));
44 }
45
46 if (obj->type == UCL_OBJECT) {
47 const ucl_object_t *cur;
48 ucl_object_iter_t it_obj = NULL;
49
50 val = PyDict_New();
51
52 while ((cur = ucl_object_iterate (obj, &it_obj, true))) {
53 PyObject *keyobj = Py_BuildValue("s",ucl_object_key(cur));
54 PyDict_SetItem(val, keyobj, _iterate_valid_ucl(cur));
55 }
56 } else if (obj->type == UCL_ARRAY) {
57 const ucl_object_t *cur;
58 ucl_object_iter_t it_obj = NULL;
59
60 val = PyList_New(0);
61
62 while ((cur = ucl_object_iterate (obj, &it_obj, true))) {
63 PyList_Append(val, _iterate_valid_ucl(cur));
64 }
65 } else if (obj->type == UCL_USERDATA) {
66 // XXX: this should be
67 // PyBytes_FromStringAndSize; where is the
68 // length from?
69 val = PyBytes_FromString(obj->value.ud);
70 }
71 }
72 return val;
73 }
74
75 PyErr_SetString(PyExc_SystemError, "unhandled type");
76 return NULL;
77 }
78
79 static PyObject *
_internal_load_ucl(char * uclstr)80 _internal_load_ucl (char *uclstr)
81 {
82 PyObject *ret;
83 struct ucl_parser *parser = ucl_parser_new (UCL_PARSER_NO_TIME);
84 bool r = ucl_parser_add_string(parser, uclstr, 0);
85
86 if (r) {
87 if (ucl_parser_get_error (parser)) {
88 PyErr_SetString(PyExc_ValueError, ucl_parser_get_error(parser));
89 ucl_parser_free(parser);
90 ret = NULL;
91 goto return_with_parser;
92 } else {
93 ucl_object_t *uclobj = ucl_parser_get_object(parser);
94 ret = _iterate_valid_ucl(uclobj);
95 ucl_object_unref(uclobj);
96 goto return_with_parser;
97 }
98 }
99 else {
100 PyErr_SetString(PyExc_ValueError, ucl_parser_get_error (parser));
101 ret = NULL;
102 goto return_with_parser;
103 }
104
105 return_with_parser:
106 ucl_parser_free(parser);
107 return ret;
108 }
109
110 static PyObject*
ucl_load(PyObject * self,PyObject * args)111 ucl_load (PyObject *self, PyObject *args)
112 {
113 char *uclstr;
114
115 if (PyArg_ParseTuple(args, "z", &uclstr)) {
116 if (!uclstr) {
117 Py_RETURN_NONE;
118 }
119
120 return _internal_load_ucl(uclstr);
121 }
122
123 return NULL;
124 }
125
126 static ucl_object_t *
_iterate_python(PyObject * obj)127 _iterate_python (PyObject *obj)
128 {
129 if (obj == Py_None) {
130 return ucl_object_new();
131 }
132 else if (PyBool_Check (obj)) {
133 return ucl_object_frombool (obj == Py_True);
134 }
135 #if PY_MAJOR_VERSION < 3
136 else if (PyInt_Check (obj)) {
137 return ucl_object_fromint (PyInt_AsLong (obj));
138 }
139 #endif
140 else if (PyLong_Check (obj)) {
141 return ucl_object_fromint (PyLong_AsLong (obj));
142 }
143 else if (PyFloat_Check (obj)) {
144 return ucl_object_fromdouble (PyFloat_AsDouble (obj));
145 }
146 else if (PyUnicode_Check (obj)) {
147 ucl_object_t *ucl_str;
148 PyObject *str = PyUnicode_AsASCIIString(obj);
149 ucl_str = ucl_object_fromstring (PyBytes_AsString (str));
150 Py_DECREF(str);
151 return ucl_str;
152 }
153 #if PY_MAJOR_VERSION < 3
154 else if (PyString_Check (obj)) {
155 return ucl_object_fromstring (PyString_AsString (obj));
156 }
157 #endif
158 else if (PyDict_Check(obj)) {
159 PyObject *key, *value;
160 Py_ssize_t pos = 0;
161 ucl_object_t *top, *elm;
162 char *keystr = NULL;
163
164 top = ucl_object_typed_new (UCL_OBJECT);
165
166 while (PyDict_Next(obj, &pos, &key, &value)) {
167 elm = _iterate_python(value);
168
169 if (PyUnicode_Check(key)) {
170 PyObject *keyascii = PyUnicode_AsASCIIString(key);
171 keystr = PyBytes_AsString(keyascii);
172 Py_DECREF(keyascii);
173 }
174 #if PY_MAJOR_VERSION < 3
175 else if (PyString_Check(key)) {
176 keystr = PyString_AsString(key);
177 }
178 #endif
179 else {
180 PyErr_SetString(PyExc_TypeError, "Unknown key type");
181 return NULL;
182 }
183
184 ucl_object_insert_key (top, elm, keystr, 0, true);
185 }
186
187 return top;
188 }
189 else if (PySequence_Check(obj)) {
190 PyObject *value;
191 Py_ssize_t len, pos;
192 ucl_object_t *top, *elm;
193
194 len = PySequence_Length(obj);
195 top = ucl_object_typed_new (UCL_ARRAY);
196
197 for (pos = 0; pos < len; pos++) {
198 value = PySequence_GetItem(obj, pos);
199 elm = _iterate_python(value);
200 ucl_array_append(top, elm);
201 }
202
203 return top;
204 }
205 else {
206 PyErr_SetString(PyExc_TypeError, "Unhandled object type");
207 return NULL;
208 }
209
210 return NULL;
211 }
212
213 static PyObject *
ucl_dump(PyObject * self,PyObject * args)214 ucl_dump (PyObject *self, PyObject *args)
215 {
216 PyObject *obj;
217 ucl_emitter_t emitter;
218 ucl_object_t *root = NULL;
219
220 emitter = UCL_EMIT_CONFIG;
221
222 if (!PyArg_ParseTuple(args, "O|i", &obj, &emitter)) {
223 PyErr_SetString(PyExc_TypeError, "Unhandled object type");
224 return NULL;
225 }
226
227 if (emitter >= UCL_EMIT_MAX) {
228 PyErr_SetString(PyExc_TypeError, "Invalid emitter type");
229 return NULL;
230 }
231
232 if (obj == Py_None) {
233 Py_RETURN_NONE;
234 }
235
236 root = _iterate_python(obj);
237 if (root) {
238 PyObject *ret;
239 char *buf;
240
241 buf = (char *) ucl_object_emit (root, emitter);
242 ucl_object_unref (root);
243 #if PY_MAJOR_VERSION < 3
244 ret = PyString_FromString (buf);
245 #else
246 ret = PyUnicode_FromString (buf);
247 #endif
248 free(buf);
249
250 return ret;
251 }
252
253 return NULL;
254 }
255
256 static PyObject *
ucl_validate(PyObject * self,PyObject * args)257 ucl_validate (PyObject *self, PyObject *args)
258 {
259 PyObject *dataobj, *schemaobj;
260 ucl_object_t *data, *schema;
261 bool r;
262 struct ucl_schema_error err;
263
264 if (!PyArg_ParseTuple (args, "OO", &schemaobj, &dataobj)) {
265 PyErr_SetString (PyExc_TypeError, "Unhandled object type");
266 return NULL;
267 }
268
269 schema = _iterate_python(schemaobj);
270 if (!schema)
271 return NULL;
272
273 data = _iterate_python(dataobj);
274 if (!data)
275 return NULL;
276
277 // validation
278 r = ucl_object_validate (schema, data, &err);
279 ucl_object_unref (schema);
280 ucl_object_unref (data);
281
282 if (!r) {
283 PyErr_SetString (SchemaError, err.msg);
284 return NULL;
285 }
286
287 Py_RETURN_TRUE;
288 }
289
290 static PyMethodDef uclMethods[] = {
291 {"load", ucl_load, METH_VARARGS, "Load UCL from stream"},
292 {"dump", ucl_dump, METH_VARARGS, "Dump UCL to stream"},
293 {"validate", ucl_validate, METH_VARARGS, "Validate ucl stream against schema"},
294 {NULL, NULL, 0, NULL}
295 };
296
297 static void
init_macros(PyObject * mod)298 init_macros(PyObject *mod)
299 {
300 PyModule_AddIntMacro(mod, UCL_EMIT_JSON);
301 PyModule_AddIntMacro(mod, UCL_EMIT_JSON_COMPACT);
302 PyModule_AddIntMacro(mod, UCL_EMIT_CONFIG);
303 PyModule_AddIntMacro(mod, UCL_EMIT_YAML);
304 PyModule_AddIntMacro(mod, UCL_EMIT_MSGPACK);
305
306 SchemaError = PyErr_NewException("ucl.SchemaError", NULL, NULL);
307 Py_INCREF(SchemaError);
308 PyModule_AddObject(mod, "SchemaError", SchemaError);
309 }
310
311 #if PY_MAJOR_VERSION >= 3
312 static struct PyModuleDef uclmodule = {
313 PyModuleDef_HEAD_INIT,
314 "ucl",
315 NULL,
316 -1,
317 uclMethods
318 };
319
320 PyMODINIT_FUNC
PyInit_ucl(void)321 PyInit_ucl (void)
322 {
323 PyObject *mod = PyModule_Create (&uclmodule);
324 init_macros (mod);
325
326 return mod;
327 }
328 #else
initucl(void)329 void initucl (void)
330 {
331 PyObject *mod = Py_InitModule ("ucl", uclMethods);
332 init_macros (mod);
333 }
334 #endif
335