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 =
84 ucl_parser_new (UCL_PARSER_NO_TIME|UCL_PARSER_NO_IMPLICIT_ARRAYS);
85 bool r = ucl_parser_add_string(parser, uclstr, 0);
86
87 if (r) {
88 if (ucl_parser_get_error (parser)) {
89 PyErr_SetString(PyExc_ValueError, ucl_parser_get_error(parser));
90 ucl_parser_free(parser);
91 ret = NULL;
92 goto return_with_parser;
93 } else {
94 ucl_object_t *uclobj = ucl_parser_get_object(parser);
95 ret = _iterate_valid_ucl(uclobj);
96 ucl_object_unref(uclobj);
97 goto return_with_parser;
98 }
99 }
100 else {
101 PyErr_SetString(PyExc_ValueError, ucl_parser_get_error (parser));
102 ret = NULL;
103 goto return_with_parser;
104 }
105
106 return_with_parser:
107 ucl_parser_free(parser);
108 return ret;
109 }
110
111 static PyObject*
ucl_load(PyObject * self,PyObject * args)112 ucl_load (PyObject *self, PyObject *args)
113 {
114 char *uclstr;
115
116 if (PyArg_ParseTuple(args, "z", &uclstr)) {
117 if (!uclstr) {
118 Py_RETURN_NONE;
119 }
120
121 return _internal_load_ucl(uclstr);
122 }
123
124 return NULL;
125 }
126
127 static ucl_object_t *
_iterate_python(PyObject * obj)128 _iterate_python (PyObject *obj)
129 {
130 if (obj == Py_None) {
131 return ucl_object_new();
132 }
133 else if (PyBool_Check (obj)) {
134 return ucl_object_frombool (obj == Py_True);
135 }
136 #if PY_MAJOR_VERSION < 3
137 else if (PyInt_Check (obj)) {
138 return ucl_object_fromint (PyInt_AsLong (obj));
139 }
140 #endif
141 else if (PyLong_Check (obj)) {
142 return ucl_object_fromint (PyLong_AsLong (obj));
143 }
144 else if (PyFloat_Check (obj)) {
145 return ucl_object_fromdouble (PyFloat_AsDouble (obj));
146 }
147 else if (PyUnicode_Check (obj)) {
148 ucl_object_t *ucl_str;
149 PyObject *str = PyUnicode_AsASCIIString(obj);
150 ucl_str = ucl_object_fromstring (PyBytes_AsString (str));
151 Py_DECREF(str);
152 return ucl_str;
153 }
154 #if PY_MAJOR_VERSION < 3
155 else if (PyString_Check (obj)) {
156 return ucl_object_fromstring (PyString_AsString (obj));
157 }
158 #endif
159 else if (PyDict_Check(obj)) {
160 PyObject *key, *value;
161 Py_ssize_t pos = 0;
162 ucl_object_t *top, *elm;
163 char *keystr = NULL;
164
165 top = ucl_object_typed_new (UCL_OBJECT);
166
167 while (PyDict_Next(obj, &pos, &key, &value)) {
168 elm = _iterate_python(value);
169
170 if (PyUnicode_Check(key)) {
171 PyObject *keyascii = PyUnicode_AsASCIIString(key);
172 keystr = PyBytes_AsString(keyascii);
173 Py_DECREF(keyascii);
174 }
175 #if PY_MAJOR_VERSION < 3
176 else if (PyString_Check(key)) {
177 keystr = PyString_AsString(key);
178 }
179 #endif
180 else {
181 PyErr_SetString(PyExc_TypeError, "Unknown key type");
182 return NULL;
183 }
184
185 ucl_object_insert_key (top, elm, keystr, 0, true);
186 }
187
188 return top;
189 }
190 else if (PySequence_Check(obj)) {
191 PyObject *value;
192 Py_ssize_t len, pos;
193 ucl_object_t *top, *elm;
194
195 len = PySequence_Length(obj);
196 top = ucl_object_typed_new (UCL_ARRAY);
197
198 for (pos = 0; pos < len; pos++) {
199 value = PySequence_GetItem(obj, pos);
200 elm = _iterate_python(value);
201 ucl_array_append(top, elm);
202 }
203
204 return top;
205 }
206 else {
207 PyErr_SetString(PyExc_TypeError, "Unhandled object type");
208 return NULL;
209 }
210
211 return NULL;
212 }
213
214 static PyObject *
ucl_dump(PyObject * self,PyObject * args)215 ucl_dump (PyObject *self, PyObject *args)
216 {
217 PyObject *obj;
218 ucl_emitter_t emitter;
219 ucl_object_t *root = NULL;
220
221 emitter = UCL_EMIT_CONFIG;
222
223 if (!PyArg_ParseTuple(args, "O|i", &obj, &emitter)) {
224 PyErr_SetString(PyExc_TypeError, "Unhandled object type");
225 return NULL;
226 }
227
228 if (emitter >= UCL_EMIT_MAX) {
229 PyErr_SetString(PyExc_TypeError, "Invalid emitter type");
230 return NULL;
231 }
232
233 if (obj == Py_None) {
234 Py_RETURN_NONE;
235 }
236
237 root = _iterate_python(obj);
238 if (root) {
239 PyObject *ret;
240 char *buf;
241
242 buf = (char *) ucl_object_emit (root, emitter);
243 ucl_object_unref (root);
244 #if PY_MAJOR_VERSION < 3
245 ret = PyString_FromString (buf);
246 #else
247 ret = PyUnicode_FromString (buf);
248 #endif
249 free(buf);
250
251 return ret;
252 }
253
254 return NULL;
255 }
256
257 static PyObject *
ucl_validate(PyObject * self,PyObject * args)258 ucl_validate (PyObject *self, PyObject *args)
259 {
260 PyObject *dataobj, *schemaobj;
261 ucl_object_t *data, *schema;
262 bool r;
263 struct ucl_schema_error err;
264
265 if (!PyArg_ParseTuple (args, "OO", &schemaobj, &dataobj)) {
266 PyErr_SetString (PyExc_TypeError, "Unhandled object type");
267 return NULL;
268 }
269
270 schema = _iterate_python(schemaobj);
271 if (!schema)
272 return NULL;
273
274 data = _iterate_python(dataobj);
275 if (!data)
276 return NULL;
277
278 // validation
279 r = ucl_object_validate (schema, data, &err);
280 ucl_object_unref (schema);
281 ucl_object_unref (data);
282
283 if (!r) {
284 PyErr_SetString (SchemaError, err.msg);
285 return NULL;
286 }
287
288 Py_RETURN_TRUE;
289 }
290
291 static PyMethodDef uclMethods[] = {
292 {"load", ucl_load, METH_VARARGS, "Load UCL from stream"},
293 {"dump", ucl_dump, METH_VARARGS, "Dump UCL to stream"},
294 {"validate", ucl_validate, METH_VARARGS, "Validate ucl stream against schema"},
295 {NULL, NULL, 0, NULL}
296 };
297
298 static void
init_macros(PyObject * mod)299 init_macros(PyObject *mod)
300 {
301 PyModule_AddIntMacro(mod, UCL_EMIT_JSON);
302 PyModule_AddIntMacro(mod, UCL_EMIT_JSON_COMPACT);
303 PyModule_AddIntMacro(mod, UCL_EMIT_CONFIG);
304 PyModule_AddIntMacro(mod, UCL_EMIT_YAML);
305 PyModule_AddIntMacro(mod, UCL_EMIT_MSGPACK);
306
307 SchemaError = PyErr_NewException("ucl.SchemaError", NULL, NULL);
308 Py_INCREF(SchemaError);
309 PyModule_AddObject(mod, "SchemaError", SchemaError);
310 }
311
312 #if PY_MAJOR_VERSION >= 3
313 static struct PyModuleDef uclmodule = {
314 PyModuleDef_HEAD_INIT,
315 "ucl",
316 NULL,
317 -1,
318 uclMethods
319 };
320
321 PyMODINIT_FUNC
PyInit_ucl(void)322 PyInit_ucl (void)
323 {
324 PyObject *mod = PyModule_Create (&uclmodule);
325 init_macros (mod);
326
327 return mod;
328 }
329 #else
initucl(void)330 void initucl (void)
331 {
332 PyObject *mod = Py_InitModule ("ucl", uclMethods);
333 init_macros (mod);
334 }
335 #endif
336