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