blob: dcf72483f1de3979fed2a7f0cdbf25f4352c37f4 [file] [log] [blame]
Armin Ronacherbd33f112008-04-18 09:17:32 +02001/**
2 * jinja2._speedups
3 * ~~~~~~~~~~~~~~~~
4 *
Armin Ronacher7ceced52008-05-03 10:15:31 +02005 * This module implements a few functions in C for better performance. It
6 * also defines a `tb_set_next` function that is used to patch the debug
7 * traceback. If the speedups module is not compiled a ctypes implementation
8 * is used.
Armin Ronacherbd33f112008-04-18 09:17:32 +02009 *
Armin Ronacherb2178862008-05-05 22:06:21 +020010 * :copyright: 2008 by Armin Ronacher, Mickaël Guérin.
Armin Ronacherbd33f112008-04-18 09:17:32 +020011 * :license: BSD.
12 */
13
14#include <Python.h>
15
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020016#define ESCAPED_CHARS_TABLE_SIZE 63
Armin Ronacher9a1e33c2008-05-05 22:00:46 +020017#define UNICHR(x) (((PyUnicodeObject*)PyUnicode_DecodeASCII(x, strlen(x), NULL))->str);
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020018
Armin Ronacher9a1e33c2008-05-05 22:00:46 +020019static PyObject* markup;
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020020static Py_ssize_t escaped_chars_delta_len[ESCAPED_CHARS_TABLE_SIZE];
21static Py_UNICODE *escaped_chars_repl[ESCAPED_CHARS_TABLE_SIZE];
Armin Ronacherbd33f112008-04-18 09:17:32 +020022
23static int
24init_constants(void)
25{
Armin Ronacherf35e2812008-05-06 16:04:10 +020026 /* happing of characters to replace */
27 escaped_chars_repl['"'] = UNICHR("&#34;");
28 escaped_chars_repl['\''] = UNICHR("&#39;");
Armin Ronacher9a1e33c2008-05-05 22:00:46 +020029 escaped_chars_repl['&'] = UNICHR("&amp;");
Armin Ronacher9a1e33c2008-05-05 22:00:46 +020030 escaped_chars_repl['<'] = UNICHR("&lt;");
Armin Ronacher9a1e33c2008-05-05 22:00:46 +020031 escaped_chars_repl['>'] = UNICHR("&gt;");
Armin Ronacherf35e2812008-05-06 16:04:10 +020032
33 /* lengths of those characters when replaced - 1 */
34 memset(escaped_chars_delta_len, 0, sizeof (escaped_chars_delta_len));
35 escaped_chars_delta_len['"'] = escaped_chars_delta_len['\''] = \
36 escaped_chars_delta_len['&'] = 4;
37 escaped_chars_delta_len['<'] = escaped_chars_delta_len['>'] = 3;
Armin Ronacherbd33f112008-04-18 09:17:32 +020038
Armin Ronacherf35e2812008-05-06 16:04:10 +020039 /* import markup type so that we can mark the return value */
Armin Ronacherbd33f112008-04-18 09:17:32 +020040 PyObject *module = PyImport_ImportModule("jinja2.utils");
41 if (!module)
42 return 0;
43 markup = PyObject_GetAttrString(module, "Markup");
44 Py_DECREF(module);
45
46 return 1;
47}
48
49static PyObject*
50escape_unicode(PyUnicodeObject *in)
51{
52 PyUnicodeObject *out;
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020053 Py_UNICODE *inp = in->str;
54 const Py_UNICODE *inp_end = in->str + in->length;
55 Py_UNICODE *next_escp;
Armin Ronacherbd33f112008-04-18 09:17:32 +020056 Py_UNICODE *outp;
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020057 Py_ssize_t delta=0, erepl=0, delta_len=0;
Armin Ronacherbd33f112008-04-18 09:17:32 +020058
59 /* First we need to figure out how long the escaped string will be */
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020060 while (*(inp) || inp < inp_end) {
61 if (*inp < ESCAPED_CHARS_TABLE_SIZE && escaped_chars_delta_len[*inp]) {
62 delta += escaped_chars_delta_len[*inp];
Armin Ronacherf59bac22008-04-20 13:11:43 +020063 ++erepl;
Armin Ronacherbd33f112008-04-18 09:17:32 +020064 }
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020065 ++inp;
66 }
Armin Ronacherbd33f112008-04-18 09:17:32 +020067
68 /* Do we need to escape anything at all? */
69 if (!erepl) {
70 Py_INCREF(in);
71 return (PyObject*)in;
72 }
73
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020074 out = (PyUnicodeObject*)PyUnicode_FromUnicode(NULL, in->length + delta);
Armin Ronacherbd33f112008-04-18 09:17:32 +020075 if (!out)
76 return NULL;
77
78 outp = out->str;
79 inp = in->str;
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020080 while (erepl-- > 0) {
Armin Ronacher9a1e33c2008-05-05 22:00:46 +020081 /* look for the next substitution */
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020082 next_escp = inp;
83 while (next_escp < inp_end) {
Armin Ronacher9a1e33c2008-05-05 22:00:46 +020084 if (*next_escp < ESCAPED_CHARS_TABLE_SIZE &&
85 (delta_len = escaped_chars_delta_len[*next_escp])) {
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020086 ++delta_len;
87 break;
88 }
89 ++next_escp;
Armin Ronacherbd33f112008-04-18 09:17:32 +020090 }
Mickaël Guérinc0d40d32008-05-05 17:08:51 +020091
92 if (next_escp > inp) {
93 /* copy unescaped chars between inp and next_escp */
94 Py_UNICODE_COPY(outp, inp, next_escp-inp);
95 outp += next_escp-inp;
96 }
97
98 /* escape 'next_escp' */
99 Py_UNICODE_COPY(outp, escaped_chars_repl[*next_escp], delta_len);
100 outp += delta_len;
101
102 inp = next_escp + 1;
103 }
Armin Ronacher9a1e33c2008-05-05 22:00:46 +0200104 if (inp < inp_end)
Mickaël Guérinc0d40d32008-05-05 17:08:51 +0200105 Py_UNICODE_COPY(outp, inp, in->length - (inp - in->str));
Armin Ronacherbd33f112008-04-18 09:17:32 +0200106
107 return (PyObject*)out;
108}
109
110
111static PyObject*
Armin Ronacherf59bac22008-04-20 13:11:43 +0200112escape(PyObject *self, PyObject *text)
113{
114 PyObject *s = NULL, *rv = NULL;
Armin Ronacherbd33f112008-04-18 09:17:32 +0200115
116 /* we don't have to escape integers, bools or floats */
117 if (PyInt_CheckExact(text) || PyLong_CheckExact(text) ||
118 PyFloat_CheckExact(text) || PyBool_Check(text) ||
Armin Ronacher7ceced52008-05-03 10:15:31 +0200119 text == Py_None)
120 return PyObject_CallFunctionObjArgs(markup, text, NULL);
Armin Ronacherbd33f112008-04-18 09:17:32 +0200121
122 /* if the object has an __html__ method that performs the escaping */
123 PyObject *html = PyObject_GetAttrString(text, "__html__");
124 if (html) {
125 rv = PyObject_CallObject(html, NULL);
126 Py_DECREF(html);
127 return rv;
128 }
129
130 /* otherwise make the object unicode if it isn't, then escape */
131 PyErr_Clear();
132 if (!PyUnicode_Check(text)) {
133 PyObject *unicode = PyObject_Unicode(text);
134 if (!unicode)
135 return NULL;
136 s = escape_unicode((PyUnicodeObject*)unicode);
137 Py_DECREF(unicode);
138 }
139 else
140 s = escape_unicode((PyUnicodeObject*)text);
141
142 /* convert the unicode string into a markup object. */
Armin Ronacher7ceced52008-05-03 10:15:31 +0200143 rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL);
144 Py_DECREF(s);
145 return rv;
Armin Ronacherbd33f112008-04-18 09:17:32 +0200146}
147
148
Armin Ronacherf35e2812008-05-06 16:04:10 +0200149static PyObject*
150soft_unicode(PyObject *self, PyObject *s)
151{
152 if (!PyUnicode_Check(s))
153 return PyObject_Unicode(s);
154 Py_INCREF(s);
155 return s;
156}
157
158
Armin Ronacherbd33f112008-04-18 09:17:32 +0200159static PyObject *
160tb_set_next(PyObject *self, PyObject *args)
161{
162 PyTracebackObject *tb, *old;
163 PyObject *next;
164
165 if (!PyArg_ParseTuple(args, "O!O:tb_set_next", &PyTraceBack_Type, &tb, &next))
166 return NULL;
167 if (next == Py_None)
168 next = NULL;
169 else if (!PyTraceBack_Check(next)) {
170 PyErr_SetString(PyExc_TypeError,
171 "tb_set_next arg 2 must be traceback or None");
172 return NULL;
173 }
174 else
175 Py_INCREF(next);
176
177 old = tb->tb_next;
178 tb->tb_next = (PyTracebackObject*)next;
179 Py_XDECREF(old);
180
181 Py_INCREF(Py_None);
182 return Py_None;
183}
184
185
186static PyMethodDef module_methods[] = {
Armin Ronacherf59bac22008-04-20 13:11:43 +0200187 {"escape", (PyCFunction)escape, METH_O,
Armin Ronacher9a1e33c2008-05-05 22:00:46 +0200188 "escape(s) -> markup\n\n"
Armin Ronacherbd33f112008-04-18 09:17:32 +0200189 "Convert the characters &, <, >, and \" in string s to HTML-safe\n"
Armin Ronacherf35e2812008-05-06 16:04:10 +0200190 "sequences. Use this if you need to display text that might contain\n"
Armin Ronacher9a1e33c2008-05-05 22:00:46 +0200191 "such characters in HTML. Marks return value as markup string."},
Armin Ronacherf59bac22008-04-20 13:11:43 +0200192 {"soft_unicode", (PyCFunction)soft_unicode, METH_O,
193 "soft_unicode(object) -> string\n\n"
194 "Make a string unicode if it isn't already. That way a markup\n"
195 "string is not converted back to unicode."},
Armin Ronacherbd33f112008-04-18 09:17:32 +0200196 {"tb_set_next", (PyCFunction)tb_set_next, METH_VARARGS,
197 "Set the tb_next member of a traceback object."},
198 {NULL, NULL, 0, NULL} /* Sentinel */
199};
200
201
202#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
203#define PyMODINIT_FUNC void
204#endif
205PyMODINIT_FUNC
206init_speedups(void)
207{
208 if (!init_constants())
209 return;
210
211 Py_InitModule3("jinja2._speedups", module_methods, "");
212}