gh-132661: Add default value (of "") for Interpolation.expression (#136441)

This commit is contained in:
Dave Peck 2025-07-10 07:27:41 -07:00 committed by GitHub
parent f519918ec6
commit f1b8d01c80
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 58 additions and 27 deletions

View file

@ -2,33 +2,45 @@ from string.templatelib import Interpolation
class TStringBaseCase: class TStringBaseCase:
def assertInterpolationEqual(self, i, exp):
"""Test Interpolation equality.
The *i* argument must be an Interpolation instance.
The *exp* argument must be a tuple of the form
(value, expression, conversion, format_spec) where the final three
items may be omitted and are assumed to be '', None and '' respectively.
"""
if len(exp) == 4:
actual = (i.value, i.expression, i.conversion, i.format_spec)
self.assertEqual(actual, exp)
elif len(exp) == 3:
self.assertEqual((i.value, i.expression, i.conversion), exp)
self.assertEqual(i.format_spec, "")
elif len(exp) == 2:
self.assertEqual((i.value, i.expression), exp)
self.assertEqual(i.conversion, None)
self.assertEqual(i.format_spec, "")
elif len(exp) == 1:
self.assertEqual((i.value,), exp)
self.assertEqual(i.expression, "")
self.assertEqual(i.conversion, None)
self.assertEqual(i.format_spec, "")
def assertTStringEqual(self, t, strings, interpolations): def assertTStringEqual(self, t, strings, interpolations):
"""Test template string literal equality. """Test template string literal equality.
The *strings* argument must be a tuple of strings equal to *t.strings*. The *strings* argument must be a tuple of strings equal to *t.strings*.
The *interpolations* argument must be a sequence of tuples which are The *interpolations* argument must be a sequence of tuples which are
compared against *t.interpolations*. Each tuple consists of compared against *t.interpolations*. Each tuple must match the form
(value, expression, conversion, format_spec), though the final two described in the `assertInterpolationEqual` method.
items may be omitted, and are assumed to be None and '' respectively.
""" """
self.assertEqual(t.strings, strings) self.assertEqual(t.strings, strings)
self.assertEqual(len(t.interpolations), len(interpolations)) self.assertEqual(len(t.interpolations), len(interpolations))
for i, exp in zip(t.interpolations, interpolations, strict=True): for i, exp in zip(t.interpolations, interpolations, strict=True):
if len(exp) == 4: self.assertInterpolationEqual(i, exp)
actual = (i.value, i.expression, i.conversion, i.format_spec)
self.assertEqual(actual, exp)
continue
if len(exp) == 3:
self.assertEqual((i.value, i.expression, i.conversion), exp)
self.assertEqual(i.format_spec, '')
continue
self.assertEqual((i.value, i.expression), exp)
self.assertEqual(i.format_spec, '')
self.assertIsNone(i.conversion)
def convert(value, conversion): def convert(value, conversion):

View file

@ -45,6 +45,19 @@ world"""
self.assertEqual(len(t.interpolations), 0) self.assertEqual(len(t.interpolations), 0)
self.assertEqual(fstring(t), 'Hello,\nworld') self.assertEqual(fstring(t), 'Hello,\nworld')
def test_interpolation_creation(self):
i = Interpolation('Maria', 'name', 'a', 'fmt')
self.assertInterpolationEqual(i, ('Maria', 'name', 'a', 'fmt'))
i = Interpolation('Maria', 'name', 'a')
self.assertInterpolationEqual(i, ('Maria', 'name', 'a'))
i = Interpolation('Maria', 'name')
self.assertInterpolationEqual(i, ('Maria', 'name'))
i = Interpolation('Maria')
self.assertInterpolationEqual(i, ('Maria',))
def test_creation_interleaving(self): def test_creation_interleaving(self):
# Should add strings on either side # Should add strings on either side
t = Template(Interpolation('Maria', 'name', None, '')) t = Template(Interpolation('Maria', 'name', None, ''))

View file

@ -0,0 +1 @@
``Interpolation.expression`` now has a default, the empty string.

View file

@ -47,26 +47,31 @@ interpolation_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
PyObject *argsbuf[4]; PyObject *argsbuf[4];
PyObject * const *fastargs; PyObject * const *fastargs;
Py_ssize_t nargs = PyTuple_GET_SIZE(args); Py_ssize_t nargs = PyTuple_GET_SIZE(args);
Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 2; Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1;
PyObject *value; PyObject *value;
PyObject *expression; PyObject *expression = &_Py_STR(empty);
PyObject *conversion = Py_None; PyObject *conversion = Py_None;
PyObject *format_spec = &_Py_STR(empty); PyObject *format_spec = &_Py_STR(empty);
fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser,
/*minpos*/ 2, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); /*minpos*/ 1, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
if (!fastargs) { if (!fastargs) {
goto exit; goto exit;
} }
value = fastargs[0]; value = fastargs[0];
if (!noptargs) {
goto skip_optional_pos;
}
if (fastargs[1]) {
if (!PyUnicode_Check(fastargs[1])) { if (!PyUnicode_Check(fastargs[1])) {
_PyArg_BadArgument("Interpolation", "argument 'expression'", "str", fastargs[1]); _PyArg_BadArgument("Interpolation", "argument 'expression'", "str", fastargs[1]);
goto exit; goto exit;
} }
expression = fastargs[1]; expression = fastargs[1];
if (!noptargs) { if (!--noptargs) {
goto skip_optional_pos; goto skip_optional_pos;
} }
}
if (fastargs[2]) { if (fastargs[2]) {
if (!_conversion_converter(fastargs[2], &conversion)) { if (!_conversion_converter(fastargs[2], &conversion)) {
goto exit; goto exit;
@ -86,4 +91,4 @@ skip_optional_pos:
exit: exit:
return return_value; return return_value;
} }
/*[clinic end generated code: output=599742a5ccd6f060 input=a9049054013a1b77]*/ /*[clinic end generated code: output=2391391e2d7708c0 input=a9049054013a1b77]*/

View file

@ -54,7 +54,7 @@ typedef struct {
Interpolation.__new__ as interpolation_new Interpolation.__new__ as interpolation_new
value: object value: object
expression: object(subclass_of='&PyUnicode_Type') expression: object(subclass_of='&PyUnicode_Type', c_default='&_Py_STR(empty)') = ""
conversion: object(converter='_conversion_converter') = None conversion: object(converter='_conversion_converter') = None
format_spec: object(subclass_of='&PyUnicode_Type', c_default='&_Py_STR(empty)') = "" format_spec: object(subclass_of='&PyUnicode_Type', c_default='&_Py_STR(empty)') = ""
[clinic start generated code]*/ [clinic start generated code]*/
@ -63,7 +63,7 @@ static PyObject *
interpolation_new_impl(PyTypeObject *type, PyObject *value, interpolation_new_impl(PyTypeObject *type, PyObject *value,
PyObject *expression, PyObject *conversion, PyObject *expression, PyObject *conversion,
PyObject *format_spec) PyObject *format_spec)
/*[clinic end generated code: output=6488e288765bc1a9 input=d91711024068528c]*/ /*[clinic end generated code: output=6488e288765bc1a9 input=fc5c285c1dd23d36]*/
{ {
interpolationobject *self = PyObject_GC_New(interpolationobject, type); interpolationobject *self = PyObject_GC_New(interpolationobject, type);
if (!self) { if (!self) {