Edgewall Software

source: trunk/genshi/_speedups.c

Last change on this file was 1266, checked in by hodgestar, 10 years ago

Add support for iterator arguments to _speedups Markup.join implementation so that it matches the Python implementation (fixes #574).

  • Property svn:eol-style set to native
File size: 18.5 KB
RevLine 
[650]1/*
[835]2 * Copyright (C) 2006-2008 Edgewall Software
[650]3 * All rights reserved.
4 *
5 * This software is licensed as described in the file COPYING, which
6 * you should have received as part of this distribution. The terms
7 * are also available at http://genshi.edgewall.org/wiki/License.
8 *
9 * This software consists of voluntary contributions made by many
10 * individuals. For the exact contribution history, see the revision
11 * history and logs, available at http://genshi.edgewall.org/log/.
12 */
13
14#include <Python.h>
15#include <structmember.h>
16
[1155]17#if PY_MAJOR_VERSION > 2
18#   define IS_PY3K
19#elif PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
20    typedef int Py_ssize_t;
21#   define PY_SSIZE_T_MAX INT_MAX
22#   define PY_SSIZE_T_MIN INT_MIN
[847]23#endif
24
[1155]25/* We only use Unicode Strings in this module */
26#ifndef IS_PY3K
27#   define PyObject_Str PyObject_Unicode
28#endif
29
[650]30static PyObject *amp1, *amp2, *lt1, *lt2, *gt1, *gt2, *qt1, *qt2;
31static PyObject *stripentities, *striptags;
32
33static void
34init_constants(void)
35{
36    PyObject *util = PyImport_ImportModule("genshi.util");
37    stripentities = PyObject_GetAttrString(util, "stripentities");
38    striptags = PyObject_GetAttrString(util, "striptags");
39    Py_DECREF(util);
40
41    amp1 = PyUnicode_DecodeASCII("&", 1, NULL);
42    amp2 = PyUnicode_DecodeASCII("&amp;", 5, NULL);
43    lt1 = PyUnicode_DecodeASCII("<", 1, NULL);
44    lt2 = PyUnicode_DecodeASCII("&lt;", 4, NULL);
45    gt1 = PyUnicode_DecodeASCII(">", 1, NULL);
46    gt2 = PyUnicode_DecodeASCII("&gt;", 4, NULL);
47    qt1 = PyUnicode_DecodeASCII("\"", 1, NULL);
48    qt2 = PyUnicode_DecodeASCII("&#34;", 5, NULL);
49}
50
51/* Markup class */
52
[995]53PyTypeObject MarkupType; /* declared later */
[650]54
55PyDoc_STRVAR(Markup__doc__,
56"Marks a string as being safe for inclusion in HTML/XML output without\n\
57needing to be escaped.");
58
59static PyObject *
60escape(PyObject *text, int quotes)
61{
62    PyObject *args, *ret;
63    PyUnicodeObject *in, *out;
64    Py_UNICODE *inp, *outp;
65    int len, inn, outn;
66
67    if (PyObject_TypeCheck(text, &MarkupType)) {
68        Py_INCREF(text);
69        return text;
70    }
[861]71    if (PyObject_HasAttrString(text, "__html__")) {
72        ret = PyObject_CallMethod(text, "__html__", NULL);
73        args = PyTuple_New(1);
74        if (args == NULL) {
75            Py_DECREF(ret);
76            return NULL;
77        }
78        PyTuple_SET_ITEM(args, 0, ret);
79        ret = MarkupType.tp_new(&MarkupType, args, NULL);
80        Py_DECREF(args);
81        return ret;
82    }
[1155]83    in = (PyUnicodeObject *) PyObject_Str(text);
[650]84    if (in == NULL) {
85        return NULL;
86    }
87    /* First we need to figure out how long the escaped string will be */
88    len = inn = 0;
89    inp = in->str;
90    while (*(inp) || in->length > inp - in->str) {
91        switch (*inp++) {
92            case '&': len += 5; inn++;                                 break;
93            case '"': len += quotes ? 5 : 1; inn += quotes ? 1 : 0;    break;
94            case '<':
95            case '>': len += 4; inn++;                                 break;
96            default:  len++;
97        }
98    }
99
100    /* Do we need to escape anything at all? */
101    if (!inn) {
102        args = PyTuple_New(1);
103        if (args == NULL) {
104            Py_DECREF((PyObject *) in);
105            return NULL;
106        }
107        PyTuple_SET_ITEM(args, 0, (PyObject *) in);
108        ret = MarkupType.tp_new(&MarkupType, args, NULL);
109        Py_DECREF(args);
110        return ret;
111    }
112
113    out = (PyUnicodeObject*) PyUnicode_FromUnicode(NULL, len);
114    if (out == NULL) {
[781]115        Py_DECREF((PyObject *) in);
[650]116        return NULL;
117    }
118
119    outn = 0;
120    inp = in->str;
121    outp = out->str;
122    while (*(inp) || in->length > inp - in->str) {
123        if (outn == inn) {
124            /* copy rest of string if we have already replaced everything */
125            Py_UNICODE_COPY(outp, inp, in->length - (inp - in->str));
126            break;
127        }
128        switch (*inp) {
129            case '&':
130                Py_UNICODE_COPY(outp, ((PyUnicodeObject *) amp2)->str, 5);
131                outp += 5;
132                outn++;
133                break;
134            case '"':
135                if (quotes) {
136                    Py_UNICODE_COPY(outp, ((PyUnicodeObject *) qt2)->str, 5);
137                    outp += 5;
138                    outn++;
139                } else {
140                    *outp++ = *inp;
141                }
142                break;
143            case '<':
144                Py_UNICODE_COPY(outp, ((PyUnicodeObject *) lt2)->str, 4);
145                outp += 4;
146                outn++;
147                break;
148            case '>':
149                Py_UNICODE_COPY(outp, ((PyUnicodeObject *) gt2)->str, 4);
150                outp += 4;
151                outn++;
152                break;
153            default:
154                *outp++ = *inp;
155        }
156        inp++;
157    }
158
[781]159    Py_DECREF((PyObject *) in);
160
[650]161    args = PyTuple_New(1);
162    if (args == NULL) {
163        Py_DECREF((PyObject *) out);
164        return NULL;
165    }
166    PyTuple_SET_ITEM(args, 0, (PyObject *) out);
167    ret = MarkupType.tp_new(&MarkupType, args, NULL);
168    Py_DECREF(args);
169    return ret;
170}
171
172PyDoc_STRVAR(escape__doc__,
173"Create a Markup instance from a string and escape special characters\n\
174it may contain (<, >, & and \").\n\
175\n\
[656]176>>> escape('\"1 < 2\"')\n\
177<Markup u'&#34;1 &lt; 2&#34;'>\n\
178\n\
[650]179If the `quotes` parameter is set to `False`, the \" character is left\n\
180as is. Escaping quotes is generally only required for strings that are\n\
[656]181to be used in attribute values.\n\
182\n\
183>>> escape('\"1 < 2\"', quotes=False)\n\
184<Markup u'\"1 &lt; 2\"'>\n\
185\n\
186:param text: the text to escape\n\
187:param quotes: if ``True``, double quote characters are escaped in\n\
188               addition to the other special characters\n\
189:return: the escaped `Markup` string\n\
190:rtype: `Markup`\n\
191");
[650]192
193static PyObject *
194Markup_escape(PyTypeObject* type, PyObject *args, PyObject *kwds)
195{
196    static char *kwlist[] = {"text", "quotes", 0};
197    PyObject *text = NULL;
198    char quotes = 1;
199
200    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|b", kwlist, &text, &quotes)) {
201        return NULL;
202    }
203    if (PyObject_Not(text)) {
[1168]204        args = PyTuple_New(0);
205        if (args == NULL)
206            return NULL;
207        text = type->tp_new(type, args, NULL);
208        Py_DECREF(args);
209        return text;
[650]210    }
211    if (PyObject_TypeCheck(text, type)) {
212        Py_INCREF(text);
213        return text;
214    }
215    return escape(text, quotes);
216}
217
[861]218static PyObject *
219Markup_html(PyObject *self)
220{
221    Py_INCREF(self);
222    return self;
223}
224
[656]225PyDoc_STRVAR(join__doc__,
226"Return a `Markup` object which is the concatenation of the strings\n\
227in the given sequence, where this `Markup` object is the separator\n\
228between the joined elements.\n\
229\n\
230Any element in the sequence that is not a `Markup` instance is\n\
231automatically escaped.\n\
232\n\
233:param seq: the sequence of strings to join\n\
234:param escape_quotes: whether double quote characters in the elements\n\
235                      should be escaped\n\
236:return: the joined `Markup` object\n\
237:rtype: `Markup`\n\
238:see: `escape`\n\
239");
240
[650]241static PyObject *
242Markup_join(PyObject *self, PyObject *args, PyObject *kwds)
243{
244    static char *kwlist[] = {"seq", "escape_quotes", 0};
[1266]245    PyObject *seq = NULL, *seq2, *it, *tmp, *tmp2;
[650]246    char quotes = 1;
247
248    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|b", kwlist, &seq, &quotes)) {
249        return NULL;
250    }
[1266]251    it = PyObject_GetIter(seq);
252    if (it == NULL)
[650]253        return NULL;
[1266]254    seq2 = PyList_New(0);
[650]255    if (seq2 == NULL) {
[1266]256        Py_DECREF(it);
[650]257        return NULL;
258    }
[1266]259    while ((tmp = PyIter_Next(it))) {
[781]260        tmp2 = escape(tmp, quotes);
261        if (tmp2 == NULL) {
[650]262            Py_DECREF(seq2);
[1266]263            Py_DECREF(it);
[650]264            return NULL;
265        }
[1266]266        PyList_Append(seq2, tmp2);
[781]267        Py_DECREF(tmp);
[650]268    }
[1266]269    Py_DECREF(it);
270    if (PyErr_Occurred()) {
271        Py_DECREF(seq2);
272        return NULL;
273    }
[650]274    tmp = PyUnicode_Join(self, seq2);
275    Py_DECREF(seq2);
276    if (tmp == NULL)
277        return NULL;
278    args = PyTuple_New(1);
279    if (args == NULL) {
280        Py_DECREF(tmp);
281        return NULL;
282    }
283    PyTuple_SET_ITEM(args, 0, tmp);
284    tmp = MarkupType.tp_new(&MarkupType, args, NULL);
285    Py_DECREF(args);
286    return tmp;
287}
288
289static PyObject *
290Markup_add(PyObject *self, PyObject *other)
291{
292    PyObject *tmp, *tmp2, *args, *ret;
293    if (PyObject_TypeCheck(self, &MarkupType)) {
294        tmp = escape(other, 1);
295        if (tmp == NULL)
296            return NULL;
297        tmp2 = PyUnicode_Concat(self, tmp);
298    } else { // __radd__
299        tmp = escape(self, 1);
300        if (tmp == NULL)
301            return NULL;
302        tmp2 = PyUnicode_Concat(tmp, other);
303    }
[781]304    Py_DECREF(tmp);
305    if (tmp2 == NULL)
[650]306        return NULL;
307    args = PyTuple_New(1);
308    if (args == NULL) {
309        Py_DECREF(tmp2);
310        return NULL;
311    }
312    PyTuple_SET_ITEM(args, 0, tmp2);
313    ret = MarkupType.tp_new(&MarkupType, args, NULL);
314    Py_DECREF(args);
315    return ret;
316}
317
318static PyObject *
319Markup_mod(PyObject *self, PyObject *args)
320{
321    PyObject *tmp, *tmp2, *ret, *args2;
[995]322    int i;
323    Py_ssize_t nargs = 0;
[829]324    PyObject *kwds = NULL;
[650]325
[829]326    if (PyDict_Check(args)) {
327        kwds = args;
328    }
329    if (kwds && PyDict_Size(kwds)) {
330        PyObject *kwcopy, *key, *value;
331        Py_ssize_t pos = 0;
332
333        kwcopy = PyDict_Copy( kwds );
334        if (kwcopy == NULL) {
335            return NULL;
336        }
337        while (PyDict_Next(kwcopy, &pos, &key, &value)) {
338            tmp = escape(value, 1);
339            if (tmp == NULL) {
340                Py_DECREF(kwcopy);
341                return NULL;
342            }
343            if (PyDict_SetItem(kwcopy, key, tmp) < 0) {
344                Py_DECREF(tmp);
345                Py_DECREF(kwcopy);
346                return NULL;
347            }
348        }
349        tmp = PyUnicode_Format(self, kwcopy);
350        Py_DECREF(kwcopy);
351        if (tmp == NULL) {
352            return NULL;
353        }
354    } else if (PyTuple_Check(args)) {
[650]355        nargs = PyTuple_GET_SIZE(args);
356        args2 = PyTuple_New(nargs);
357        if (args2 == NULL) {
358            return NULL;
359        }
360        for (i = 0; i < nargs; i++) {
361            tmp = escape(PyTuple_GET_ITEM(args, i), 1);
362            if (tmp == NULL) {
363                Py_DECREF(args2);
364                return NULL;
365            }
366            PyTuple_SET_ITEM(args2, i, tmp);
367        }
368        tmp = PyUnicode_Format(self, args2);
369        Py_DECREF(args2);
370        if (tmp == NULL) {
371            return NULL;
372        }
373    } else {
374        tmp2 = escape(args, 1);
375        if (tmp2 == NULL) {
376            return NULL;
377        }
378        tmp = PyUnicode_Format(self, tmp2);
379        Py_DECREF(tmp2);
380        if (tmp == NULL) {
381            return NULL;
382        }
383    }
384    args = PyTuple_New(1);
385    if (args == NULL) {
386        Py_DECREF(tmp);
387        return NULL;
388    }
389    PyTuple_SET_ITEM(args, 0, tmp);
390    ret = PyUnicode_Type.tp_new(&MarkupType, args, NULL);
391    Py_DECREF(args);
392    return ret;
393}
394
395static PyObject *
396Markup_mul(PyObject *self, PyObject *num)
397{
398    PyObject *unicode, *result, *args;
399
400    if (PyObject_TypeCheck(self, &MarkupType)) {
[1155]401        unicode = PyObject_Str(self);
[650]402        if (unicode == NULL) return NULL;
403        result = PyNumber_Multiply(unicode, num);
404    } else { // __rmul__
[1155]405        unicode = PyObject_Str(num);
[650]406        if (unicode == NULL) return NULL;
407        result = PyNumber_Multiply(unicode, self);
408    }
[781]409    Py_DECREF(unicode);
[650]410
411    if (result == NULL) return NULL;
412    args = PyTuple_New(1);
413    if (args == NULL) {
414        Py_DECREF(result);
415        return NULL;
416    }
417    PyTuple_SET_ITEM(args, 0, result);
418    result = PyUnicode_Type.tp_new(&MarkupType, args, NULL);
419    Py_DECREF(args);
420
421    return result;
422}
423
424static PyObject *
425Markup_repr(PyObject *self)
426{
427    PyObject *format, *result, *args;
428
[1155]429#ifdef IS_PY3K
430    format = PyUnicode_FromString("<Markup %r>");
431#else
[650]432    format = PyString_FromString("<Markup %r>");
[1155]433#endif
[650]434    if (format == NULL) return NULL;
[1155]435    result = PyObject_Str(self);
[781]436    if (result == NULL) {
437        Py_DECREF(format);
438        return NULL;
439    }
[650]440    args = PyTuple_New(1);
441    if (args == NULL) {
[781]442        Py_DECREF(format);
[650]443        Py_DECREF(result);
444        return NULL;
445    }
446    PyTuple_SET_ITEM(args, 0, result);
[1155]447#ifdef IS_PY3K
448    result = PyUnicode_Format(format, args);
449#else
[650]450    result = PyString_Format(format, args);
[1155]451#endif
[797]452    Py_DECREF(format);
[650]453    Py_DECREF(args);
454    return result;
455}
456
457PyDoc_STRVAR(unescape__doc__,
[656]458"Reverse-escapes &, <, >, and \" and returns a `unicode` object.\n\
459\n\
460>>> Markup('1 &lt; 2').unescape()\n\
461u'1 < 2'\n\
462\n\
463:return: the unescaped string\n\
464:rtype: `unicode`\n\
465:see: `genshi.core.unescape`\n\
466");
[650]467
468static PyObject *
469Markup_unescape(PyObject* self)
470{
471    PyObject *tmp, *tmp2;
472
473    tmp = PyUnicode_Replace(self, qt2, qt1, -1);
474    if (tmp == NULL) return NULL;
475    tmp2 = PyUnicode_Replace(tmp, gt2, gt1, -1);
476    Py_DECREF(tmp);
477    if (tmp2 == NULL) return NULL;
478    tmp = PyUnicode_Replace(tmp2, lt2, lt1, -1);
479    Py_DECREF(tmp2);
480    if (tmp == NULL) return NULL;
481    tmp2 = PyUnicode_Replace(tmp, amp2, amp1, -1);
482    Py_DECREF(tmp);
483    return tmp2;
484}
485
486PyDoc_STRVAR(stripentities__doc__,
487"Return a copy of the text with any character or numeric entities\n\
488replaced by the equivalent UTF-8 characters.\n\
489\n\
490If the `keepxmlentities` parameter is provided and evaluates to `True`,\n\
[656]491the core XML entities (``&amp;``, ``&apos;``, ``&gt;``, ``&lt;`` and\n\
492``&quot;``) are not stripped.\n\
493\n\
494:return: a `Markup` instance with entities removed\n\
495:rtype: `Markup`\n\
496:see: `genshi.util.stripentities`\n\
497");
[650]498
499static PyObject *
500Markup_stripentities(PyObject* self, PyObject *args, PyObject *kwds)
501{
502    static char *kwlist[] = {"keepxmlentities", 0};
503    PyObject *result, *args2;
504    char keepxml = 0;
505
506    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|b", kwlist, &keepxml)) {
507        return NULL;
508    }
509
510    if (stripentities == NULL) return NULL;
511    result = PyObject_CallFunction(stripentities, "Ob", self, keepxml);
512    if (result == NULL) return NULL;
513    args2 = PyTuple_New(1);
514    if (args2 == NULL) {
515        Py_DECREF(result);
516        return NULL;
517    }
518    PyTuple_SET_ITEM(args2, 0, result);
519    result = MarkupType.tp_new(&MarkupType, args2, NULL);
520    Py_DECREF(args2);
521    return result;
522}
523
524PyDoc_STRVAR(striptags__doc__,
[656]525"""Return a copy of the text with all XML/HTML tags removed.\n\
526\n\
527:return: a `Markup` instance with all tags removed\n\
528:rtype: `Markup`\n\
529:see: `genshi.util.striptags`\n\
530");
[650]531
532static PyObject *
533Markup_striptags(PyObject* self)
534{
535    PyObject *result, *args;
536
537    if (striptags == NULL) return NULL;
538    result = PyObject_CallFunction(striptags, "O", self);
539    if (result == NULL) return NULL;
540    args = PyTuple_New(1);
541    if (args == NULL) {
542        Py_DECREF(result);
543        return NULL;
544    }
545    PyTuple_SET_ITEM(args, 0, result);
546    result = MarkupType.tp_new(&MarkupType, args, NULL);
547    Py_DECREF(args);
548    return result;
549}
550
551typedef struct {
552    PyUnicodeObject HEAD;
553} MarkupObject;
554
555static PyMethodDef Markup_methods[] = {
[861]556    {"__html__", (PyCFunction) Markup_html, METH_NOARGS, NULL},
[650]557    {"escape", (PyCFunction) Markup_escape,
[656]558     METH_VARARGS|METH_CLASS|METH_KEYWORDS, escape__doc__},
559    {"join", (PyCFunction)Markup_join, METH_VARARGS|METH_KEYWORDS, join__doc__},
[650]560    {"unescape", (PyCFunction)Markup_unescape, METH_NOARGS, unescape__doc__},
561    {"stripentities", (PyCFunction) Markup_stripentities,
562     METH_VARARGS|METH_KEYWORDS, stripentities__doc__},
563    {"striptags", (PyCFunction) Markup_striptags, METH_NOARGS,
564     striptags__doc__},
565    {NULL}  /* Sentinel */
566};
567
568static PyNumberMethods Markup_as_number = {
[656]569    Markup_add, /*nb_add*/
570    0, /*nb_subtract*/
571    Markup_mul, /*nb_multiply*/
[1155]572#ifndef IS_PY3K
[656]573    0, /*nb_divide*/
[1155]574#endif
[656]575    Markup_mod, /*nb_remainder*/
[650]576};
577
578PyTypeObject MarkupType = {
[1155]579#ifdef IS_PY3K
580    PyVarObject_HEAD_INIT(NULL, 0)
581#else
[650]582    PyObject_HEAD_INIT(NULL)
583    0,
[1155]584#endif
[650]585    "genshi._speedups.Markup",
586    sizeof(MarkupObject),
587    0,
588    0,          /*tp_dealloc*/
[846]589    0,          /*tp_print*/
[650]590    0,          /*tp_getattr*/
591    0,          /*tp_setattr*/
[1155]592#ifdef IS_PY3K
593    0,          /*tp_reserved*/
594#else
[650]595    0,          /*tp_compare*/
[1155]596#endif
[650]597    Markup_repr, /*tp_repr*/
598    &Markup_as_number, /*tp_as_number*/
599    0,          /*tp_as_sequence*/
600    0,          /*tp_as_mapping*/
601    0,          /*tp_hash */
602
603    0,          /*tp_call*/
604    0,          /*tp_str*/
605    0,          /*tp_getattro*/
606    0,          /*tp_setattro*/
607    0,          /*tp_as_buffer*/
608
[1155]609#ifdef IS_PY3K
610    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_UNICODE_SUBCLASS, /*tp_flags*/
611#elif defined(Py_TPFLAGS_UNICODE_SUBCLASS)
612    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_UNICODE_SUBCLASS, /*tp_flags*/
613#else
[650]614    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES, /*tp_flags*/
[1155]615#endif
616
[650]617    Markup__doc__,/*tp_doc*/
[846]618
[650]619    0,          /*tp_traverse*/
620    0,          /*tp_clear*/
621
622    0,          /*tp_richcompare*/
623    0,          /*tp_weaklistoffset*/
624
625    0,          /*tp_iter*/
626    0,          /*tp_iternext*/
627
628    /* Attribute descriptor and subclassing stuff */
629
630    Markup_methods,/*tp_methods*/
631    0,          /*tp_members*/
632    0,          /*tp_getset*/
633    0,          /*tp_base*/
634    0,          /*tp_dict*/
[846]635
[650]636    0,          /*tp_descr_get*/
637    0,          /*tp_descr_set*/
638    0,          /*tp_dictoffset*/
[846]639
[650]640    0,          /*tp_init*/
641    0,          /*tp_alloc  will be set to PyType_GenericAlloc in module init*/
[829]642    0,          /*tp_new*/
[650]643    0,          /*tp_free  Low-level free-memory routine */
644    0,          /*tp_is_gc For PyObject_IS_GC */
645    0,          /*tp_bases*/
646    0,          /*tp_mro method resolution order */
647    0,          /*tp_cache*/
648    0,          /*tp_subclasses*/
649    0           /*tp_weaklist*/
650};
651
[1155]652#ifdef IS_PY3K
653struct PyModuleDef module_def = {
654    PyModuleDef_HEAD_INIT, /*m_base*/
655    "_speedups",           /*m_name*/
656    NULL,                  /*m_doc*/
657    -1,                    /*m_size*/
658    NULL,                  /*m_methods*/
659    NULL,                  /*m_reload*/
660    NULL,                  /*m_traverse*/
661    NULL,                  /*m_clear*/
662    NULL                   /*m_free*/
663};
664
665PyObject *
666PyInit__speedups(void)
667#else
[650]668PyMODINIT_FUNC
669init_speedups(void)
[1155]670#endif
[650]671{
672    PyObject *module;
673
674    /* Workaround for quirk in Visual Studio, see
675        <http://www.python.it/faq/faq-3.html#3.24> */
676    MarkupType.tp_base = &PyUnicode_Type;
677
678    if (PyType_Ready(&MarkupType) < 0)
[1155]679#ifdef IS_PY3K
680        return NULL;
681#else
[650]682        return;
[1155]683#endif
[650]684
685    init_constants();
686
[1155]687#ifdef IS_PY3K
688    module = PyModule_Create(&module_def);
689#else
[650]690    module = Py_InitModule("_speedups", NULL);
[1155]691#endif
[650]692    Py_INCREF(&MarkupType);
693    PyModule_AddObject(module, "Markup", (PyObject *) &MarkupType);
[1155]694
695#ifdef IS_PY3K
696    return module;
697#endif
[650]698}
Note: See TracBrowser for help on using the repository browser.