xref: /freebsd/contrib/libucl/python/src/uclmodule.c (revision 5dae51da3da0cc94d17bd67b308fad304ebec7e0)
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 *
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 *
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 *
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*
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 *
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 *
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 *
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
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
321 PyInit_ucl (void)
322 {
323 	PyObject *mod = PyModule_Create (&uclmodule);
324 	init_macros (mod);
325 
326 	return mod;
327 }
328 #else
329 void initucl (void)
330 {
331 	PyObject *mod = Py_InitModule ("ucl", uclMethods);
332 	init_macros (mod);
333 }
334 #endif
335