/src/cpython/Objects/templateobject.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* t-string Template object implementation */ |
2 | | |
3 | | #include "Python.h" |
4 | | #include "pycore_interpolation.h" // _PyInterpolation_CheckExact() |
5 | | #include "pycore_runtime.h" // _Py_STR() |
6 | | #include "pycore_template.h" |
7 | | |
8 | | typedef struct { |
9 | | PyObject_HEAD |
10 | | PyObject *stringsiter; |
11 | | PyObject *interpolationsiter; |
12 | | int from_strings; |
13 | | } templateiterobject; |
14 | | |
15 | | #define templateiterobject_CAST(op) \ |
16 | 0 | (assert(_PyTemplateIter_CheckExact(op)), _Py_CAST(templateiterobject*, (op))) |
17 | | |
18 | | static PyObject * |
19 | | templateiter_next(PyObject *op) |
20 | 0 | { |
21 | 0 | templateiterobject *self = templateiterobject_CAST(op); |
22 | 0 | PyObject *item; |
23 | 0 | if (self->from_strings) { |
24 | 0 | item = PyIter_Next(self->stringsiter); |
25 | 0 | self->from_strings = 0; |
26 | 0 | if (item == NULL) { |
27 | 0 | return NULL; |
28 | 0 | } |
29 | 0 | if (PyUnicode_GET_LENGTH(item) == 0) { |
30 | 0 | Py_SETREF(item, PyIter_Next(self->interpolationsiter)); |
31 | 0 | self->from_strings = 1; |
32 | 0 | } |
33 | 0 | } else { |
34 | 0 | item = PyIter_Next(self->interpolationsiter); |
35 | 0 | self->from_strings = 1; |
36 | 0 | } |
37 | 0 | return item; |
38 | 0 | } |
39 | | |
40 | | static void |
41 | | templateiter_dealloc(PyObject *op) |
42 | 0 | { |
43 | 0 | PyObject_GC_UnTrack(op); |
44 | 0 | Py_TYPE(op)->tp_clear(op); |
45 | 0 | Py_TYPE(op)->tp_free(op); |
46 | 0 | } |
47 | | |
48 | | static int |
49 | | templateiter_clear(PyObject *op) |
50 | 0 | { |
51 | 0 | templateiterobject *self = templateiterobject_CAST(op); |
52 | 0 | Py_CLEAR(self->stringsiter); |
53 | 0 | Py_CLEAR(self->interpolationsiter); |
54 | 0 | return 0; |
55 | 0 | } |
56 | | |
57 | | static int |
58 | | templateiter_traverse(PyObject *op, visitproc visit, void *arg) |
59 | 0 | { |
60 | 0 | templateiterobject *self = templateiterobject_CAST(op); |
61 | 0 | Py_VISIT(self->stringsiter); |
62 | 0 | Py_VISIT(self->interpolationsiter); |
63 | 0 | return 0; |
64 | 0 | } |
65 | | |
66 | | PyTypeObject _PyTemplateIter_Type = { |
67 | | PyVarObject_HEAD_INIT(NULL, 0) |
68 | | .tp_name = "string.templatelib.TemplateIter", |
69 | | .tp_doc = PyDoc_STR("Template iterator object"), |
70 | | .tp_basicsize = sizeof(templateiterobject), |
71 | | .tp_itemsize = 0, |
72 | | .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, |
73 | | .tp_alloc = PyType_GenericAlloc, |
74 | | .tp_dealloc = templateiter_dealloc, |
75 | | .tp_clear = templateiter_clear, |
76 | | .tp_free = PyObject_GC_Del, |
77 | | .tp_traverse = templateiter_traverse, |
78 | | .tp_iter = PyObject_SelfIter, |
79 | | .tp_iternext = templateiter_next, |
80 | | }; |
81 | | |
82 | | typedef struct { |
83 | | PyObject_HEAD |
84 | | PyObject *strings; |
85 | | PyObject *interpolations; |
86 | | } templateobject; |
87 | | |
88 | | #define templateobject_CAST(op) \ |
89 | 0 | (assert(_PyTemplate_CheckExact(op)), _Py_CAST(templateobject*, (op))) |
90 | | |
91 | | static PyObject * |
92 | | template_new(PyTypeObject *type, PyObject *args, PyObject *kwds) |
93 | 0 | { |
94 | 0 | if (kwds != NULL) { |
95 | 0 | PyErr_SetString(PyExc_TypeError, "Template.__new__ only accepts *args arguments"); |
96 | 0 | return NULL; |
97 | 0 | } |
98 | | |
99 | 0 | Py_ssize_t argslen = PyTuple_GET_SIZE(args); |
100 | 0 | Py_ssize_t stringslen = 0; |
101 | 0 | Py_ssize_t interpolationslen = 0; |
102 | 0 | int last_was_str = 0; |
103 | |
|
104 | 0 | for (Py_ssize_t i = 0; i < argslen; i++) { |
105 | 0 | PyObject *item = PyTuple_GET_ITEM(args, i); |
106 | 0 | if (PyUnicode_Check(item)) { |
107 | 0 | if (!last_was_str) { |
108 | 0 | stringslen++; |
109 | 0 | } |
110 | 0 | last_was_str = 1; |
111 | 0 | } |
112 | 0 | else if (_PyInterpolation_CheckExact(item)) { |
113 | 0 | if (!last_was_str) { |
114 | 0 | stringslen++; |
115 | 0 | } |
116 | 0 | interpolationslen++; |
117 | 0 | last_was_str = 0; |
118 | 0 | } |
119 | 0 | else { |
120 | 0 | PyErr_Format( |
121 | 0 | PyExc_TypeError, |
122 | 0 | "Template.__new__ *args need to be of type 'str' or 'Interpolation', got %T", |
123 | 0 | item); |
124 | 0 | return NULL; |
125 | 0 | } |
126 | 0 | } |
127 | 0 | if (!last_was_str) { |
128 | 0 | stringslen++; |
129 | 0 | } |
130 | |
|
131 | 0 | PyObject *strings = PyTuple_New(stringslen); |
132 | 0 | if (!strings) { |
133 | 0 | return NULL; |
134 | 0 | } |
135 | | |
136 | 0 | PyObject *interpolations = PyTuple_New(interpolationslen); |
137 | 0 | if (!interpolations) { |
138 | 0 | Py_DECREF(strings); |
139 | 0 | return NULL; |
140 | 0 | } |
141 | | |
142 | 0 | last_was_str = 0; |
143 | 0 | Py_ssize_t stringsidx = 0, interpolationsidx = 0; |
144 | 0 | for (Py_ssize_t i = 0; i < argslen; i++) { |
145 | 0 | PyObject *item = PyTuple_GET_ITEM(args, i); |
146 | 0 | if (PyUnicode_Check(item)) { |
147 | 0 | if (last_was_str) { |
148 | 0 | PyObject *laststring = PyTuple_GET_ITEM(strings, stringsidx - 1); |
149 | 0 | PyObject *concat = PyUnicode_Concat(laststring, item); |
150 | 0 | Py_DECREF(laststring); |
151 | 0 | if (!concat) { |
152 | 0 | Py_DECREF(strings); |
153 | 0 | Py_DECREF(interpolations); |
154 | 0 | return NULL; |
155 | 0 | } |
156 | 0 | PyTuple_SET_ITEM(strings, stringsidx - 1, concat); |
157 | 0 | } |
158 | 0 | else { |
159 | 0 | PyTuple_SET_ITEM(strings, stringsidx++, Py_NewRef(item)); |
160 | 0 | } |
161 | 0 | last_was_str = 1; |
162 | 0 | } |
163 | 0 | else if (_PyInterpolation_CheckExact(item)) { |
164 | 0 | if (!last_was_str) { |
165 | 0 | _Py_DECLARE_STR(empty, ""); |
166 | 0 | PyTuple_SET_ITEM(strings, stringsidx++, &_Py_STR(empty)); |
167 | 0 | } |
168 | 0 | PyTuple_SET_ITEM(interpolations, interpolationsidx++, Py_NewRef(item)); |
169 | 0 | last_was_str = 0; |
170 | 0 | } |
171 | 0 | } |
172 | 0 | if (!last_was_str) { |
173 | 0 | _Py_DECLARE_STR(empty, ""); |
174 | 0 | PyTuple_SET_ITEM(strings, stringsidx++, &_Py_STR(empty)); |
175 | 0 | } |
176 | |
|
177 | 0 | PyObject *template = _PyTemplate_Build(strings, interpolations); |
178 | 0 | Py_DECREF(strings); |
179 | 0 | Py_DECREF(interpolations); |
180 | 0 | return template; |
181 | 0 | } |
182 | | |
183 | | static void |
184 | | template_dealloc(PyObject *op) |
185 | 0 | { |
186 | 0 | PyObject_GC_UnTrack(op); |
187 | 0 | Py_TYPE(op)->tp_clear(op); |
188 | 0 | Py_TYPE(op)->tp_free(op); |
189 | 0 | } |
190 | | |
191 | | static int |
192 | | template_clear(PyObject *op) |
193 | 0 | { |
194 | 0 | templateobject *self = templateobject_CAST(op); |
195 | 0 | Py_CLEAR(self->strings); |
196 | 0 | Py_CLEAR(self->interpolations); |
197 | 0 | return 0; |
198 | 0 | } |
199 | | |
200 | | static int |
201 | | template_traverse(PyObject *op, visitproc visit, void *arg) |
202 | 0 | { |
203 | 0 | templateobject *self = templateobject_CAST(op); |
204 | 0 | Py_VISIT(self->strings); |
205 | 0 | Py_VISIT(self->interpolations); |
206 | 0 | return 0; |
207 | 0 | } |
208 | | |
209 | | static PyObject * |
210 | | template_repr(PyObject *op) |
211 | 0 | { |
212 | 0 | templateobject *self = templateobject_CAST(op); |
213 | 0 | return PyUnicode_FromFormat("%s(strings=%R, interpolations=%R)", |
214 | 0 | _PyType_Name(Py_TYPE(self)), |
215 | 0 | self->strings, |
216 | 0 | self->interpolations); |
217 | 0 | } |
218 | | |
219 | | static PyObject * |
220 | | template_iter(PyObject *op) |
221 | 0 | { |
222 | 0 | templateobject *self = templateobject_CAST(op); |
223 | 0 | templateiterobject *iter = PyObject_GC_New(templateiterobject, &_PyTemplateIter_Type); |
224 | 0 | if (iter == NULL) { |
225 | 0 | return NULL; |
226 | 0 | } |
227 | | |
228 | 0 | PyObject *stringsiter = PyObject_GetIter(self->strings); |
229 | 0 | if (stringsiter == NULL) { |
230 | 0 | Py_DECREF(iter); |
231 | 0 | return NULL; |
232 | 0 | } |
233 | | |
234 | 0 | PyObject *interpolationsiter = PyObject_GetIter(self->interpolations); |
235 | 0 | if (interpolationsiter == NULL) { |
236 | 0 | Py_DECREF(iter); |
237 | 0 | Py_DECREF(stringsiter); |
238 | 0 | return NULL; |
239 | 0 | } |
240 | | |
241 | 0 | iter->stringsiter = stringsiter; |
242 | 0 | iter->interpolationsiter = interpolationsiter; |
243 | 0 | iter->from_strings = 1; |
244 | 0 | PyObject_GC_Track(iter); |
245 | 0 | return (PyObject *)iter; |
246 | 0 | } |
247 | | |
248 | | static PyObject * |
249 | | template_strings_append_str(PyObject *strings, PyObject *str) |
250 | 0 | { |
251 | 0 | Py_ssize_t stringslen = PyTuple_GET_SIZE(strings); |
252 | 0 | PyObject *string = PyTuple_GET_ITEM(strings, stringslen - 1); |
253 | 0 | PyObject *concat = PyUnicode_Concat(string, str); |
254 | 0 | if (concat == NULL) { |
255 | 0 | return NULL; |
256 | 0 | } |
257 | | |
258 | 0 | PyObject *newstrings = PyTuple_New(stringslen); |
259 | 0 | if (newstrings == NULL) { |
260 | 0 | Py_DECREF(concat); |
261 | 0 | return NULL; |
262 | 0 | } |
263 | | |
264 | 0 | for (Py_ssize_t i = 0; i < stringslen - 1; i++) { |
265 | 0 | PyTuple_SET_ITEM(newstrings, i, Py_NewRef(PyTuple_GET_ITEM(strings, i))); |
266 | 0 | } |
267 | 0 | PyTuple_SET_ITEM(newstrings, stringslen - 1, concat); |
268 | |
|
269 | 0 | return newstrings; |
270 | 0 | } |
271 | | |
272 | | static PyObject * |
273 | | template_strings_prepend_str(PyObject *strings, PyObject *str) |
274 | 0 | { |
275 | 0 | Py_ssize_t stringslen = PyTuple_GET_SIZE(strings); |
276 | 0 | PyObject *string = PyTuple_GET_ITEM(strings, 0); |
277 | 0 | PyObject *concat = PyUnicode_Concat(str, string); |
278 | 0 | if (concat == NULL) { |
279 | 0 | return NULL; |
280 | 0 | } |
281 | | |
282 | 0 | PyObject *newstrings = PyTuple_New(stringslen); |
283 | 0 | if (newstrings == NULL) { |
284 | 0 | Py_DECREF(concat); |
285 | 0 | return NULL; |
286 | 0 | } |
287 | | |
288 | 0 | PyTuple_SET_ITEM(newstrings, 0, concat); |
289 | 0 | for (Py_ssize_t i = 1; i < stringslen; i++) { |
290 | 0 | PyTuple_SET_ITEM(newstrings, i, Py_NewRef(PyTuple_GET_ITEM(strings, i))); |
291 | 0 | } |
292 | |
|
293 | 0 | return newstrings; |
294 | 0 | } |
295 | | |
296 | | static PyObject * |
297 | | template_strings_concat(PyObject *left, PyObject *right) |
298 | 0 | { |
299 | 0 | Py_ssize_t left_stringslen = PyTuple_GET_SIZE(left); |
300 | 0 | PyObject *left_laststring = PyTuple_GET_ITEM(left, left_stringslen - 1); |
301 | 0 | Py_ssize_t right_stringslen = PyTuple_GET_SIZE(right); |
302 | 0 | PyObject *right_firststring = PyTuple_GET_ITEM(right, 0); |
303 | |
|
304 | 0 | PyObject *concat = PyUnicode_Concat(left_laststring, right_firststring); |
305 | 0 | if (concat == NULL) { |
306 | 0 | return NULL; |
307 | 0 | } |
308 | | |
309 | 0 | PyObject *newstrings = PyTuple_New(left_stringslen + right_stringslen - 1); |
310 | 0 | if (newstrings == NULL) { |
311 | 0 | Py_DECREF(concat); |
312 | 0 | return NULL; |
313 | 0 | } |
314 | | |
315 | 0 | Py_ssize_t index = 0; |
316 | 0 | for (Py_ssize_t i = 0; i < left_stringslen - 1; i++) { |
317 | 0 | PyTuple_SET_ITEM(newstrings, index++, Py_NewRef(PyTuple_GET_ITEM(left, i))); |
318 | 0 | } |
319 | 0 | PyTuple_SET_ITEM(newstrings, index++, concat); |
320 | 0 | for (Py_ssize_t i = 1; i < right_stringslen; i++) { |
321 | 0 | PyTuple_SET_ITEM(newstrings, index++, Py_NewRef(PyTuple_GET_ITEM(right, i))); |
322 | 0 | } |
323 | |
|
324 | 0 | return newstrings; |
325 | 0 | } |
326 | | |
327 | | static PyObject * |
328 | | template_concat_templates(templateobject *self, templateobject *other) |
329 | 0 | { |
330 | 0 | PyObject *newstrings = template_strings_concat(self->strings, other->strings); |
331 | 0 | if (newstrings == NULL) { |
332 | 0 | return NULL; |
333 | 0 | } |
334 | | |
335 | 0 | PyObject *newinterpolations = PySequence_Concat(self->interpolations, other->interpolations); |
336 | 0 | if (newinterpolations == NULL) { |
337 | 0 | Py_DECREF(newstrings); |
338 | 0 | return NULL; |
339 | 0 | } |
340 | | |
341 | 0 | PyObject *newtemplate = _PyTemplate_Build(newstrings, newinterpolations); |
342 | 0 | Py_DECREF(newstrings); |
343 | 0 | Py_DECREF(newinterpolations); |
344 | 0 | return newtemplate; |
345 | 0 | } |
346 | | |
347 | | static PyObject * |
348 | | template_concat_template_str(templateobject *self, PyObject *other) |
349 | 0 | { |
350 | 0 | PyObject *newstrings = template_strings_append_str(self->strings, other); |
351 | 0 | if (newstrings == NULL) { |
352 | 0 | return NULL; |
353 | 0 | } |
354 | | |
355 | 0 | PyObject *newtemplate = _PyTemplate_Build(newstrings, self->interpolations); |
356 | 0 | Py_DECREF(newstrings); |
357 | 0 | return newtemplate; |
358 | 0 | } |
359 | | |
360 | | static PyObject * |
361 | | template_concat_str_template(templateobject *self, PyObject *other) |
362 | 0 | { |
363 | 0 | PyObject *newstrings = template_strings_prepend_str(self->strings, other); |
364 | 0 | if (newstrings == NULL) { |
365 | 0 | return NULL; |
366 | 0 | } |
367 | | |
368 | 0 | PyObject *newtemplate = _PyTemplate_Build(newstrings, self->interpolations); |
369 | 0 | Py_DECREF(newstrings); |
370 | 0 | return newtemplate; |
371 | 0 | } |
372 | | |
373 | | PyObject * |
374 | | _PyTemplate_Concat(PyObject *self, PyObject *other) |
375 | 0 | { |
376 | 0 | if (_PyTemplate_CheckExact(self) && _PyTemplate_CheckExact(other)) { |
377 | 0 | return template_concat_templates((templateobject *) self, (templateobject *) other); |
378 | 0 | } |
379 | 0 | else if ((_PyTemplate_CheckExact(self)) && PyUnicode_Check(other)) { |
380 | 0 | return template_concat_template_str((templateobject *) self, other); |
381 | 0 | } |
382 | 0 | else if (PyUnicode_Check(self) && (_PyTemplate_CheckExact(other))) { |
383 | 0 | return template_concat_str_template((templateobject *) other, self); |
384 | 0 | } |
385 | 0 | else { |
386 | 0 | Py_RETURN_NOTIMPLEMENTED; |
387 | 0 | } |
388 | 0 | } |
389 | | |
390 | | static PyObject * |
391 | | template_values_get(PyObject *op, void *Py_UNUSED(data)) |
392 | 0 | { |
393 | 0 | templateobject *self = templateobject_CAST(op); |
394 | |
|
395 | 0 | Py_ssize_t len = PyTuple_GET_SIZE(self->interpolations); |
396 | 0 | PyObject *values = PyTuple_New(PyTuple_GET_SIZE(self->interpolations)); |
397 | 0 | if (values == NULL) { |
398 | 0 | return NULL; |
399 | 0 | } |
400 | | |
401 | 0 | for (Py_ssize_t i = 0; i < len; i++) { |
402 | 0 | PyObject *item = PyTuple_GET_ITEM(self->interpolations, i); |
403 | 0 | PyTuple_SET_ITEM(values, i, _PyInterpolation_GetValueRef(item)); |
404 | 0 | } |
405 | |
|
406 | 0 | return values; |
407 | 0 | } |
408 | | |
409 | | static PyMemberDef template_members[] = { |
410 | | {"strings", Py_T_OBJECT_EX, offsetof(templateobject, strings), Py_READONLY, "Strings"}, |
411 | | {"interpolations", Py_T_OBJECT_EX, offsetof(templateobject, interpolations), Py_READONLY, "Interpolations"}, |
412 | | {NULL}, |
413 | | }; |
414 | | |
415 | | static PyGetSetDef template_getset[] = { |
416 | | {"values", template_values_get, NULL, |
417 | | PyDoc_STR("Values of interpolations"), NULL}, |
418 | | {NULL}, |
419 | | }; |
420 | | |
421 | | static PySequenceMethods template_as_sequence = { |
422 | | .sq_concat = _PyTemplate_Concat, |
423 | | }; |
424 | | |
425 | | static PyObject* |
426 | | template_reduce(PyObject *op, PyObject *Py_UNUSED(dummy)) |
427 | 0 | { |
428 | 0 | PyObject *mod = PyImport_ImportModule("string.templatelib"); |
429 | 0 | if (mod == NULL) { |
430 | 0 | return NULL; |
431 | 0 | } |
432 | 0 | PyObject *func = PyObject_GetAttrString(mod, "_template_unpickle"); |
433 | 0 | Py_DECREF(mod); |
434 | 0 | if (func == NULL) { |
435 | 0 | return NULL; |
436 | 0 | } |
437 | | |
438 | 0 | templateobject *self = templateobject_CAST(op); |
439 | 0 | PyObject *result = Py_BuildValue("O(OO)", |
440 | 0 | func, |
441 | 0 | self->strings, |
442 | 0 | self->interpolations); |
443 | |
|
444 | 0 | Py_DECREF(func); |
445 | 0 | return result; |
446 | 0 | } |
447 | | |
448 | | static PyMethodDef template_methods[] = { |
449 | | {"__reduce__", template_reduce, METH_NOARGS, NULL}, |
450 | | {"__class_getitem__", Py_GenericAlias, |
451 | | METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, |
452 | | {NULL, NULL}, |
453 | | }; |
454 | | |
455 | | PyTypeObject _PyTemplate_Type = { |
456 | | PyVarObject_HEAD_INIT(NULL, 0) |
457 | | .tp_name = "string.templatelib.Template", |
458 | | .tp_doc = PyDoc_STR("Template object"), |
459 | | .tp_basicsize = sizeof(templateobject), |
460 | | .tp_itemsize = 0, |
461 | | .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, |
462 | | .tp_as_sequence = &template_as_sequence, |
463 | | .tp_new = template_new, |
464 | | .tp_alloc = PyType_GenericAlloc, |
465 | | .tp_dealloc = template_dealloc, |
466 | | .tp_clear = template_clear, |
467 | | .tp_free = PyObject_GC_Del, |
468 | | .tp_repr = template_repr, |
469 | | .tp_members = template_members, |
470 | | .tp_methods = template_methods, |
471 | | .tp_getset = template_getset, |
472 | | .tp_iter = template_iter, |
473 | | .tp_traverse = template_traverse, |
474 | | }; |
475 | | |
476 | | PyObject * |
477 | | _PyTemplate_Build(PyObject *strings, PyObject *interpolations) |
478 | 0 | { |
479 | 0 | templateobject *template = PyObject_GC_New(templateobject, &_PyTemplate_Type); |
480 | 0 | if (template == NULL) { |
481 | 0 | return NULL; |
482 | 0 | } |
483 | | |
484 | 0 | template->strings = Py_NewRef(strings); |
485 | 0 | template->interpolations = Py_NewRef(interpolations); |
486 | 0 | PyObject_GC_Track(template); |
487 | 0 | return (PyObject *) template; |
488 | 0 | } |