blob: 5a7e9aa2d958b9f6779e3dc1ead66fdfa3e0588f [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 *
10 * :copyright: 2008 by Armin Ronacher.
11 * :license: BSD.
12 */
13
14#include <Python.h>
15
16
Priit Laes4c81b162008-04-18 09:52:58 +020017static const char *samp = "&amp;", *slt = "&lt;", *sgt = "&gt;", *sqt = "&quot;";
Armin Ronacherbd33f112008-04-18 09:17:32 +020018static Py_UNICODE *amp, *lt, *gt, *qt;
19static PyObject* markup;
20
21
22static int
23init_constants(void)
24{
25 amp = ((PyUnicodeObject*)PyUnicode_DecodeASCII(samp, 5, NULL))->str;
26 lt = ((PyUnicodeObject*)PyUnicode_DecodeASCII(slt, 4, NULL))->str;
27 gt = ((PyUnicodeObject*)PyUnicode_DecodeASCII(sgt, 4, NULL))->str;
28 qt = ((PyUnicodeObject*)PyUnicode_DecodeASCII(sqt, 6, NULL))->str;
29
30 PyObject *module = PyImport_ImportModule("jinja2.utils");
31 if (!module)
32 return 0;
33 markup = PyObject_GetAttrString(module, "Markup");
34 Py_DECREF(module);
35
36 return 1;
37}
38
39static PyObject*
40escape_unicode(PyUnicodeObject *in)
41{
42 PyUnicodeObject *out;
43 Py_UNICODE *outp;
44
45 /* First we need to figure out how long the escaped string will be */
46 int len = 0, erepl = 0, repl = 0;
47 Py_UNICODE *inp = in->str;
48 while (*(inp) || in->length > inp - in->str)
49 switch (*inp++) {
50 case '&':
51 len += 5;
Armin Ronacherf59bac22008-04-20 13:11:43 +020052 ++erepl;
Armin Ronacherbd33f112008-04-18 09:17:32 +020053 break;
54 case '"':
55 len += 6;
Armin Ronacherf59bac22008-04-20 13:11:43 +020056 ++erepl;
Armin Ronacherbd33f112008-04-18 09:17:32 +020057 break;
58 case '<':
59 case '>':
60 len += 4;
Armin Ronacherf59bac22008-04-20 13:11:43 +020061 ++erepl;
Armin Ronacherbd33f112008-04-18 09:17:32 +020062 break;
63 default:
Armin Ronacherf59bac22008-04-20 13:11:43 +020064 ++len;
Armin Ronacherbd33f112008-04-18 09:17:32 +020065 }
66
67 /* Do we need to escape anything at all? */
68 if (!erepl) {
69 Py_INCREF(in);
70 return (PyObject*)in;
71 }
72
73 out = (PyUnicodeObject*)PyUnicode_FromUnicode(NULL, len);
74 if (!out)
75 return NULL;
76
77 outp = out->str;
78 inp = in->str;
79 while (*(inp) || in->length > inp - in->str) {
80 /* copy rest of string if we have replaced everything */
81 if (repl == erepl) {
82 Py_UNICODE_COPY(outp, inp, in->length - (inp - in->str));
83 break;
84 }
85 /* regular replacements */
86 switch (*inp) {
87 case '&':
88 Py_UNICODE_COPY(outp, amp, 5);
89 outp += 5;
Armin Ronacherf59bac22008-04-20 13:11:43 +020090 ++repl;
Armin Ronacherbd33f112008-04-18 09:17:32 +020091 break;
92 case '"':
93 Py_UNICODE_COPY(outp, qt, 6);
94 outp += 6;
Armin Ronacherf59bac22008-04-20 13:11:43 +020095 ++repl;
Armin Ronacherbd33f112008-04-18 09:17:32 +020096 break;
97 case '<':
98 Py_UNICODE_COPY(outp, lt, 4);
99 outp += 4;
Armin Ronacherf59bac22008-04-20 13:11:43 +0200100 ++repl;
Armin Ronacherbd33f112008-04-18 09:17:32 +0200101 break;
102 case '>':
103 Py_UNICODE_COPY(outp, gt, 4);
104 outp += 4;
Armin Ronacherf59bac22008-04-20 13:11:43 +0200105 ++repl;
Armin Ronacherbd33f112008-04-18 09:17:32 +0200106 break;
107 default:
108 *outp++ = *inp;
109 };
110 ++inp;
111 }
112
113 return (PyObject*)out;
114}
115
116
117static PyObject*
Armin Ronacherf59bac22008-04-20 13:11:43 +0200118soft_unicode(PyObject *self, PyObject *s)
Armin Ronacherbd33f112008-04-18 09:17:32 +0200119{
Armin Ronacherf59bac22008-04-20 13:11:43 +0200120 if (!PyUnicode_Check(s))
121 return PyObject_Unicode(s);
122 Py_INCREF(s);
123 return s;
124}
125
126
127static PyObject*
128escape(PyObject *self, PyObject *text)
129{
130 PyObject *s = NULL, *rv = NULL;
Armin Ronacherbd33f112008-04-18 09:17:32 +0200131
132 /* we don't have to escape integers, bools or floats */
133 if (PyInt_CheckExact(text) || PyLong_CheckExact(text) ||
134 PyFloat_CheckExact(text) || PyBool_Check(text) ||
Armin Ronacher7ceced52008-05-03 10:15:31 +0200135 text == Py_None)
136 return PyObject_CallFunctionObjArgs(markup, text, NULL);
Armin Ronacherbd33f112008-04-18 09:17:32 +0200137
138 /* if the object has an __html__ method that performs the escaping */
139 PyObject *html = PyObject_GetAttrString(text, "__html__");
140 if (html) {
141 rv = PyObject_CallObject(html, NULL);
142 Py_DECREF(html);
143 return rv;
144 }
145
146 /* otherwise make the object unicode if it isn't, then escape */
147 PyErr_Clear();
148 if (!PyUnicode_Check(text)) {
149 PyObject *unicode = PyObject_Unicode(text);
150 if (!unicode)
151 return NULL;
152 s = escape_unicode((PyUnicodeObject*)unicode);
153 Py_DECREF(unicode);
154 }
155 else
156 s = escape_unicode((PyUnicodeObject*)text);
157
158 /* convert the unicode string into a markup object. */
Armin Ronacher7ceced52008-05-03 10:15:31 +0200159 rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL);
160 Py_DECREF(s);
161 return rv;
Armin Ronacherbd33f112008-04-18 09:17:32 +0200162}
163
164
165static PyObject *
166tb_set_next(PyObject *self, PyObject *args)
167{
168 PyTracebackObject *tb, *old;
169 PyObject *next;
170
171 if (!PyArg_ParseTuple(args, "O!O:tb_set_next", &PyTraceBack_Type, &tb, &next))
172 return NULL;
173 if (next == Py_None)
174 next = NULL;
175 else if (!PyTraceBack_Check(next)) {
176 PyErr_SetString(PyExc_TypeError,
177 "tb_set_next arg 2 must be traceback or None");
178 return NULL;
179 }
180 else
181 Py_INCREF(next);
182
183 old = tb->tb_next;
184 tb->tb_next = (PyTracebackObject*)next;
185 Py_XDECREF(old);
186
187 Py_INCREF(Py_None);
188 return Py_None;
189}
190
191
192static PyMethodDef module_methods[] = {
Armin Ronacherf59bac22008-04-20 13:11:43 +0200193 {"escape", (PyCFunction)escape, METH_O,
Armin Ronacherbd33f112008-04-18 09:17:32 +0200194 "escape(s) -> string\n\n"
195 "Convert the characters &, <, >, and \" in string s to HTML-safe\n"
196 "sequences. Use this if you need to display text that might contain\n"
197 "such characters in HTML."},
Armin Ronacherf59bac22008-04-20 13:11:43 +0200198 {"soft_unicode", (PyCFunction)soft_unicode, METH_O,
199 "soft_unicode(object) -> string\n\n"
200 "Make a string unicode if it isn't already. That way a markup\n"
201 "string is not converted back to unicode."},
Armin Ronacherbd33f112008-04-18 09:17:32 +0200202 {"tb_set_next", (PyCFunction)tb_set_next, METH_VARARGS,
203 "Set the tb_next member of a traceback object."},
204 {NULL, NULL, 0, NULL} /* Sentinel */
205};
206
207
208#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
209#define PyMODINIT_FUNC void
210#endif
211PyMODINIT_FUNC
212init_speedups(void)
213{
214 if (!init_constants())
215 return;
216
217 Py_InitModule3("jinja2._speedups", module_methods, "");
218}