source: trunk/ext/_syckmodule.c @ 9

Revision 9, 14.0 KB checked in by xi, 8 years ago (diff)

Make it somewhat compatible with older versions of Python.

Line 
1
2#include <Python.h>
3#include <syck.h>
4
5/* Global objects. */
6
7static PyObject *PySyck_Error;
8
9static PyObject *PySyck_ScalarKind;
10static PyObject *PySyck_SeqKind;
11static PyObject *PySyck_MapKind;
12
13/* Node type. */
14
15typedef struct {
16    PyObject_HEAD
17    PyObject *kind;
18    PyObject *type_id;
19    PyObject *value;
20} PySyckNodeObject;
21
22static void
23PySyckNode_dealloc(PySyckNodeObject *self)
24{
25    Py_XDECREF(self->kind);
26    Py_XDECREF(self->type_id);
27    Py_XDECREF(self->value);
28    PyObject_Del(self);
29}
30
31static PyObject *
32PySyckNode_getattr(PySyckNodeObject *self, char *name)
33{
34    PyObject *value;
35
36    if (strcmp(name, "kind") == 0)
37        value = self->kind;
38    else if (strcmp(name, "type_id") == 0)
39        value = self->type_id;
40    else if (strcmp(name, "value") == 0)
41        value = self->value;
42    else {
43        PyErr_SetString(PyExc_AttributeError, name);
44        return NULL;
45    }
46
47    Py_INCREF(value);
48    return value;
49}
50
51static char PySyckNode_doc[] =
52    "Node object\n"
53    "\n"
54    "Attributes of the Node object:\n\n"
55    "kind -- 'scalar', 'seq', or 'map'.\n"
56    "type_id -- the tag of the node.\n"
57    "value -- the value of the node, a string, list or dict object.\n";
58
59static PyTypeObject PySyckNode_Type = {
60    PyObject_HEAD_INIT(NULL)
61    0,                                  /* ob_size */
62    "_syck.Node",                       /* tp_name */
63    sizeof(PySyckNodeObject),           /* tp_basicsize */
64    0,                                  /* tp_itemsize */
65    (destructor)PySyckNode_dealloc,     /* tp_dealloc */
66    0,                                  /* tp_print */
67    (getattrfunc)PySyckNode_getattr,    /* tp_getattr */
68    0,                                  /* tp_setattr */
69    0,                                  /* tp_compare */
70    0,                                  /* tp_repr */
71    0,                                  /* tp_as_number */
72    0,                                  /* tp_as_sequence */
73    0,                                  /* tp_as_mapping */
74    0,                                  /* tp_hash */
75    0,                                  /* tp_call */
76    0,                                  /* tp_str */
77    0,                                  /* tp_getattro */
78    0,                                  /* tp_setattro */
79    0,                                  /* tp_as_buffer */
80    Py_TPFLAGS_DEFAULT,                 /* tp_flags */
81    PySyckNode_doc,                     /* tp_doc */
82};
83
84static PyObject *
85PySyckNode_New(char *type_id, PyObject *value) /* Note: steals the reference to the value. */
86{
87    PySyckNodeObject *self;
88    PyObject *kind;
89
90    self = PyObject_NEW(PySyckNodeObject, &PySyckNode_Type);
91    if (!self) {
92        Py_XDECREF(value);
93        return NULL;
94    }
95
96    self->value = value;
97
98    if (PyList_Check(value))
99        kind = PySyck_SeqKind;
100    else if (PyDict_Check(value))
101        kind = PySyck_MapKind;
102    else
103        kind = PySyck_ScalarKind;
104    Py_INCREF(kind);
105    self->kind = kind;
106
107    if (type_id) {
108        self->type_id = PyString_FromString(type_id);
109        if (!self->type_id) {
110            Py_DECREF(self);
111            return NULL;
112        }
113    }
114    else {
115        Py_INCREF(Py_None);
116        self->type_id = Py_None;
117    }
118
119    return (PyObject *)self;
120}
121
122static PyObject *
123PySyck_Node(PyObject *self, PyObject *args)
124{
125    if (!PyArg_ParseTuple(args, ":_syck.Node"))
126        return NULL;
127
128    PyErr_SetString(PyExc_TypeError, "Node object cannot be created explicitly. Use _syck.Parser.parse() instead.");
129    return NULL;
130}
131
132static char PySyck_Node_doc[] =
133    "Node object cannot be created explicitly. Use _syck.Parser.parse() instead.";
134
135/* Parser type. */
136
137typedef struct {
138    PyObject_HEAD
139    PyObject *source;
140    PyObject *resolver;
141    PyObject *symbols;
142    SyckParser *syck;
143    int error;
144} PySyckParserObject;
145
146static void
147PySyckParser_free(PySyckParserObject *parser)
148{
149    Py_XDECREF(parser->source);
150    parser->source = NULL;
151    Py_XDECREF(parser->resolver);
152    parser->resolver = NULL;
153    Py_XDECREF(parser->symbols);
154    parser->symbols = NULL;
155    if (parser->syck) {
156        syck_free_parser(parser->syck);
157        parser->syck = NULL;
158    }
159}
160
161static PyObject *
162PySyckParser_parse(PySyckParserObject *parser, PyObject *args)
163{
164    SYMID index;
165    PyObject *value;
166
167    if (!PyArg_ParseTuple(args, ":parse"))
168        return NULL;
169
170    if (!parser->syck) {
171        Py_INCREF(Py_None);
172        return Py_None;
173    }
174
175    if (parser->symbols) {
176        PyErr_SetString(PyExc_RuntimeError, "do not call Parser.parse while it is running");
177        return NULL;
178    }
179
180    parser->symbols = PyList_New(0);
181    if (!parser->symbols) {
182        return NULL;
183    }
184
185    index = syck_parse(parser->syck);
186
187    if (parser->error) {
188        PySyckParser_free(parser);
189        return NULL;
190    }
191
192    if (parser->syck->eof) {
193        PySyckParser_free(parser);
194        Py_INCREF(Py_None);
195        return Py_None;
196    }
197
198    value = PyList_GetItem(parser->symbols, index);
199
200    Py_DECREF(parser->symbols);
201    parser->symbols = NULL;
202
203    return value;
204}
205
206static char PySyckParser_parse_doc[] =
207    "Parses the next document in the YAML stream, return the root Node object or None on EOF.";
208
209static PyObject *
210PySyckParser_eof(PySyckParserObject *parser, PyObject *args)
211{
212    PyObject *value;
213
214    if (!PyArg_ParseTuple(args, ":eof"))
215        return NULL;
216
217    value = parser->syck ? Py_False : Py_True;
218
219    Py_INCREF(value);
220    return value;
221}
222
223static char PySyckParser_eof_doc[] =
224    "Checks if the parser is stopped.";
225
226static PyMethodDef PySyckParser_methods[] = {
227    {"parse",  (PyCFunction)PySyckParser_parse, METH_VARARGS, PySyckParser_parse_doc},
228    {"eof",  (PyCFunction)PySyckParser_eof, METH_VARARGS, PySyckParser_eof_doc},
229    {NULL}  /* Sentinel */
230};
231
232static void
233PySyckParser_dealloc(PySyckParserObject *parser)
234{
235    PySyckParser_free(parser);
236    PyObject_Del(parser);
237}
238
239static PyObject *
240PySyckParser_getattr(PySyckParserObject *parser, char *name)
241{
242    return Py_FindMethod(PySyckParser_methods, (PyObject *)parser, name);
243}
244
245static char PySyckParser_doc[] =
246    "_syck.Parser(yaml_string_or_file, resolver=None, implicit_typing=True, taguri_expansion=True) -> Parser object\n"
247    "\n"
248    "Methods of the Parser object:\n\n"
249    "parse() -- Parses the next document in the YAML stream, return the root Node object or None on EOF.\n"
250    "eof() -- Checks if the parser is stopped.\n";
251
252static PyTypeObject PySyckParser_Type = {
253    PyObject_HEAD_INIT(NULL)
254    0,                                  /* ob_size */
255    "_syck.Parser",                     /* tp_name */
256    sizeof(PySyckParserObject),         /* tp_basicsize */
257    0,                                  /* tp_itemsize */
258    (destructor)PySyckParser_dealloc,   /* tp_dealloc */
259    0,                                  /* tp_print */
260    (getattrfunc)PySyckParser_getattr,  /* tp_getattr */
261    0,                                  /* tp_setattr */
262    0,                                  /* tp_compare */
263    0,                                  /* tp_repr */
264    0,                                  /* tp_as_number */
265    0,                                  /* tp_as_sequence */
266    0,                                  /* tp_as_mapping */
267    0,                                  /* tp_hash */
268    0,                                  /* tp_call */
269    0,                                  /* tp_str */
270    0,                                  /* tp_getattro */
271    0,                                  /* tp_setattro */
272    0,                                  /* tp_as_buffer */
273    Py_TPFLAGS_DEFAULT,                 /* tp_flags */
274    PySyckParser_doc,                   /* tp_doc */
275};
276
277static long
278PySyckParser_read_handler(char *buf, SyckIoFile *file, long max_size, long skip)
279{
280    PySyckParserObject *parser = (PySyckParserObject *)file->ptr;
281
282    PyObject *value;
283
284    char *str;
285    int length;
286
287    buf[skip] = '\0';
288
289    if (parser->error) {
290        return skip;
291    }
292   
293    max_size -= skip;
294
295    value = PyObject_CallMethod(parser->source, "read", "(i)", max_size);
296    if (!value) {
297        parser->error = 1;
298        return skip;
299    }
300
301    if (!PyString_Check(value)) {
302        Py_DECREF(value);
303        PyErr_SetString(PyExc_TypeError, "file-like object should return a string");
304        parser->error = 1;
305       
306        return skip;
307    }
308
309    str = PyString_AS_STRING(value);
310    length = PyString_GET_SIZE(value);
311    if (!length) {
312        Py_DECREF(value);
313        return skip;
314    }
315
316    if (length > max_size) {
317        Py_DECREF(value);
318        PyErr_SetString(PyExc_ValueError, "read returns an overly long string");
319        parser->error = 1;
320        return skip;
321    }
322
323    memcpy(buf+skip, str, length);
324    length += skip;
325    buf[length] = '\0';
326
327    Py_DECREF(value);
328
329    return length;
330}
331
332static SYMID
333PySyckParser_node_handler(SyckParser *syck, SyckNode *node)
334{
335    PySyckParserObject *parser = (PySyckParserObject *)syck->bonus;
336
337    SYMID index;
338    PyObject *object = NULL;
339
340    PyObject *key, *value, *item;
341    int k;
342
343    if (parser->error)
344        return -1;
345
346    switch (node->kind) {
347
348        case syck_str_kind:
349            object = PyString_FromStringAndSize(node->data.str->ptr,
350                    node->data.str->len);
351            if (!object) goto error;
352            break;
353
354        case syck_seq_kind:
355            object = PyList_New(node->data.list->idx);
356            if (!object) goto error;
357            for (k = 0; k < node->data.list->idx; k++) {
358                index = syck_seq_read(node, k);
359                item = PyList_GetItem(parser->symbols, index);
360                if (!item) goto error;
361                Py_INCREF(item);
362                PyList_SET_ITEM(object, k, item);
363            }
364            break;
365
366        case syck_map_kind:
367            object = PyDict_New();
368            if (!object) goto error;
369            for (k = 0; k < node->data.pairs->idx; k++)
370            {
371                index = syck_map_read(node, map_key, k);
372                key = PyList_GetItem(parser->symbols, index);
373                if (!key) goto error;
374                index = syck_map_read(node, map_value, k);
375                value = PyList_GetItem(parser->symbols, index);
376                if (!value) goto error;
377                if (PyDict_SetItem(object, key, value) < 0)
378                    goto error;
379            }
380            break;
381    }
382
383    object = PySyckNode_New(node->type_id, object);
384    if (!object) goto error;
385
386    if (parser->resolver) {
387        value = PyObject_CallFunction(parser->resolver, "(O)", object);
388        if (!value) goto error;
389        Py_DECREF(object);
390        object = value;
391    }
392
393    if (PyList_Append(parser->symbols, object) < 0)
394        goto error;
395
396    index = PyList_Size(parser->symbols)-1;
397    return index;
398
399error:
400    Py_XDECREF(object);
401    parser->error = 1;
402    return -1;
403}
404
405static void
406PySyckParser_error_handler(SyckParser *syck, char *str)
407{
408    PySyckParserObject *parser = (PySyckParserObject *)syck->bonus;
409    PyObject *value;
410
411    if (parser->error) return;
412
413    parser->error = 1;
414
415    value = Py_BuildValue("(sii)", str, syck->linect, syck->cursor-syck->lineptr);
416    if (value) {
417        PyErr_SetObject(PySyck_Error, value);
418    }
419}
420
421static PyObject *
422PySyck_Parser(PyObject *self, PyObject *args, PyObject *kwds)
423{
424    PySyckParserObject *parser;
425    PyObject *source;
426    PyObject *resolver = NULL;
427    int implicit_typing = 1;
428    int taguri_expansion = 1;
429
430    static char *kwdlist[] = {"source", "resolver", "implicit_typing", "taguri_expansion", NULL};
431
432    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|Oii", kwdlist,
433                &source, &resolver, &implicit_typing, &taguri_expansion))
434        return NULL;
435
436    parser = PyObject_NEW(PySyckParserObject, &PySyckParser_Type);
437    if (!parser)
438        return NULL;
439
440    parser->error = 0;
441    parser->symbols = NULL;
442
443    Py_INCREF(source);
444    parser->source = source;
445
446    if (resolver == Py_None)
447        resolver = NULL;
448    Py_XINCREF(resolver);
449    parser->resolver = resolver;
450
451    parser->syck = syck_new_parser();
452    parser->syck->bonus = parser;
453
454    if (PyString_Check(source)) {
455        syck_parser_str(parser->syck, PyString_AS_STRING(source), PyString_GET_SIZE(source), NULL);
456    }
457    else {
458        syck_parser_file(parser->syck, (FILE *)parser, PySyckParser_read_handler);
459    }
460    syck_parser_implicit_typing(parser->syck, implicit_typing);
461    syck_parser_taguri_expansion(parser->syck, taguri_expansion);
462
463    syck_parser_handler(parser->syck, PySyckParser_node_handler);
464    syck_parser_error_handler(parser->syck, PySyckParser_error_handler);
465    /*
466    syck_parser_bad_anchor_handler(parser, PySyckParser_bad_anchor_handler);
467    */
468
469    return (PyObject *)parser;
470}
471
472static char PySyck_Parser_doc[] =
473    "Creates a new Parser object.";
474
475/* The module definitions. */
476
477static PyMethodDef PySyck_methods[] = {
478    {"Node",  (PyCFunction)PySyck_Node, METH_VARARGS, PySyck_Node_doc},
479    {"Parser",  (PyCFunction)PySyck_Parser, METH_VARARGS|METH_KEYWORDS, PySyck_Parser_doc},
480    {NULL}  /* Sentinel */
481};
482
483static char PySyck_doc[] =
484    "This module provides low-level access to the Syck parser and emitter.\n"
485    "Do not use this module directly, use the package 'syck' instead.\n";
486
487/* PyMODINIT_FUNC - does not work with versions <2.3 */
488void
489init_syck(void)
490{
491    PyObject *m;
492
493    PySyckNode_Type.ob_type = &PyType_Type;
494    PySyckParser_Type.ob_type = &PyType_Type;
495
496    PySyck_Error = PyErr_NewException("_syck.error", NULL, NULL);
497    if (!PySyck_Error)
498        return;
499
500    PySyck_ScalarKind = PyString_FromString("scalar");
501    if (!PySyck_ScalarKind)
502        return;
503    PySyck_SeqKind = PyString_FromString("seq");
504    if (!PySyck_SeqKind)
505        return;
506    PySyck_MapKind = PyString_FromString("map");
507    if (!PySyck_MapKind)
508        return;
509
510    m = Py_InitModule3("_syck", PySyck_methods, PySyck_doc);
511
512    Py_INCREF(PySyck_Error);
513    if (!PyModule_AddObject(m, "error", PySyck_Error) < 0)
514        return;
515
516    Py_INCREF(&PySyckNode_Type);
517    if (PyModule_AddObject(m, "NodeType", (PyObject *)&PySyckNode_Type) < 0)
518        return;
519
520    Py_INCREF(&PySyckParser_Type);
521    if (PyModule_AddObject(m, "ParserType", (PyObject *)&PySyckParser_Type) < 0)
522        return;
523}
524
Note: See TracBrowser for help on using the repository browser.