https://github.com/python/cpython/commit/f1b8d01c802165b9807172965b7097b6aaf6fcd3
commit: f1b8d01c802165b9807172965b7097b6aaf6fcd3
branch: main
author: Dave Peck <davep...@gmail.com>
committer: lysnikolaou <lisandros...@gmail.com>
date: 2025-07-10T16:27:41+02:00
summary:

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

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2025-07-08-23-53-51.gh-issue-132661.B84iYt.rst
M Lib/test/test_string/_support.py
M Lib/test/test_string/test_templatelib.py
M Objects/clinic/interpolationobject.c.h
M Objects/interpolationobject.c

diff --git a/Lib/test/test_string/_support.py b/Lib/test/test_string/_support.py
index abdddaf187b4fe..e1d7f6f6500aeb 100644
--- a/Lib/test/test_string/_support.py
+++ b/Lib/test/test_string/_support.py
@@ -2,33 +2,45 @@
 
 
 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):
         """Test template string literal equality.
 
         The *strings* argument must be a tuple of strings equal to *t.strings*.
 
         The *interpolations* argument must be a sequence of tuples which are
-        compared against *t.interpolations*. Each tuple consists of
-        (value, expression, conversion, format_spec), though the final two
-        items may be omitted, and are assumed to be None and '' respectively.
+        compared against *t.interpolations*. Each tuple must match the form
+        described in the `assertInterpolationEqual` method.
         """
         self.assertEqual(t.strings, strings)
         self.assertEqual(len(t.interpolations), len(interpolations))
 
         for i, exp in zip(t.interpolations, interpolations, strict=True):
-            if len(exp) == 4:
-                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)
+            self.assertInterpolationEqual(i, exp)
 
 
 def convert(value, conversion):
diff --git a/Lib/test/test_string/test_templatelib.py 
b/Lib/test/test_string/test_templatelib.py
index 85fcff486d6616..adaf590e64dad6 100644
--- a/Lib/test/test_string/test_templatelib.py
+++ b/Lib/test/test_string/test_templatelib.py
@@ -45,6 +45,19 @@ def test_basic_creation(self):
         self.assertEqual(len(t.interpolations), 0)
         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):
         # Should add strings on either side
         t = Template(Interpolation('Maria', 'name', None, ''))
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-08-23-53-51.gh-issue-132661.B84iYt.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-08-23-53-51.gh-issue-132661.B84iYt.rst
new file mode 100644
index 00000000000000..9930413b53c150
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-08-23-53-51.gh-issue-132661.B84iYt.rst
@@ -0,0 +1 @@
+``Interpolation.expression`` now has a default, the empty string.
diff --git a/Objects/clinic/interpolationobject.c.h 
b/Objects/clinic/interpolationobject.c.h
index 7a94dabafc92f2..2030e17e49e47a 100644
--- a/Objects/clinic/interpolationobject.c.h
+++ b/Objects/clinic/interpolationobject.c.h
@@ -47,26 +47,31 @@ interpolation_new(PyTypeObject *type, PyObject *args, 
PyObject *kwargs)
     PyObject *argsbuf[4];
     PyObject * const *fastargs;
     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 *expression;
+    PyObject *expression = &_Py_STR(empty);
     PyObject *conversion = Py_None;
     PyObject *format_spec = &_Py_STR(empty);
 
     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) {
         goto exit;
     }
     value = fastargs[0];
-    if (!PyUnicode_Check(fastargs[1])) {
-        _PyArg_BadArgument("Interpolation", "argument 'expression'", "str", 
fastargs[1]);
-        goto exit;
-    }
-    expression = fastargs[1];
     if (!noptargs) {
         goto skip_optional_pos;
     }
+    if (fastargs[1]) {
+        if (!PyUnicode_Check(fastargs[1])) {
+            _PyArg_BadArgument("Interpolation", "argument 'expression'", 
"str", fastargs[1]);
+            goto exit;
+        }
+        expression = fastargs[1];
+        if (!--noptargs) {
+            goto skip_optional_pos;
+        }
+    }
     if (fastargs[2]) {
         if (!_conversion_converter(fastargs[2], &conversion)) {
             goto exit;
@@ -86,4 +91,4 @@ interpolation_new(PyTypeObject *type, PyObject *args, 
PyObject *kwargs)
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=599742a5ccd6f060 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=2391391e2d7708c0 input=a9049054013a1b77]*/
diff --git a/Objects/interpolationobject.c b/Objects/interpolationobject.c
index a5d407a7b0e296..b58adb693f0cae 100644
--- a/Objects/interpolationobject.c
+++ b/Objects/interpolationobject.c
@@ -54,7 +54,7 @@ typedef struct {
 Interpolation.__new__ as interpolation_new
 
     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
     format_spec: object(subclass_of='&PyUnicode_Type', 
c_default='&_Py_STR(empty)') = ""
 [clinic start generated code]*/
@@ -63,7 +63,7 @@ static PyObject *
 interpolation_new_impl(PyTypeObject *type, PyObject *value,
                        PyObject *expression, PyObject *conversion,
                        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);
     if (!self) {

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: arch...@mail-archive.com

Reply via email to