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