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