]> granicus.if.org Git - python/commitdiff
bpo-28598: Support __rmod__ for RHS subclasses of str in % string formatting operatio...
authorMartijn Pieters <github.com@zopatista.com>
Thu, 23 Feb 2017 13:38:04 +0000 (13:38 +0000)
committerSerhiy Storchaka <storchaka@gmail.com>
Thu, 23 Feb 2017 13:38:04 +0000 (15:38 +0200)
When you use `'%s' % SubClassOfStr()`, where `SubClassOfStr.__rmod__` exists, the reverse operation is ignored as normally such string formatting operations use the `PyUnicode_Format()` fast path. This patch tests for subclasses of `str` first and picks the slow path in that case.

Patch by Martijn Pieters.

Lib/test/test_unicode.py
Misc/NEWS
Python/ceval.c

index b1f7c8966aa11cbdf53023a40a9203edd88a70e3..f70504c11afdc0157e75d2a01ec110e01f694f06 100644 (file)
@@ -1448,6 +1448,15 @@ class UnicodeTest(string_tests.CommonTest,
         with self.assertRaises(ValueError):
             result = format_string % 2.34
 
+    def test_issue28598_strsubclass_rhs(self):
+        # A subclass of str with an __rmod__ method should be able to hook
+        # into the % operator
+        class SubclassedStr(str):
+            def __rmod__(self, other):
+                return 'Success, self.__rmod__({!r}) was called'.format(other)
+        self.assertEqual('lhs %% %r' % SubclassedStr('rhs'),
+                         "Success, self.__rmod__('lhs %% %r') was called")
+
     @support.cpython_only
     def test_formatting_huge_precision_c_limits(self):
         from _testcapi import INT_MAX
index e21a7235d03b06b0fc6faebadec83d7d386b591e..72d96e7ef5386918d3f32144313c4ff0761f4d89 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@ What's New in Python 3.7.0 alpha 1?
 Core and Builtins
 -----------------
 
+- bpo-28598: Support __rmod__ for subclasses of str being called before
+  str.__mod__.  Patch by Martijn Pieters.
+
 - bpo-29607: Fix stack_effect computation for CALL_FUNCTION_EX. 
   Patch by Matthieu Dartiailh.
 
@@ -19,6 +22,7 @@ Core and Builtins
 
 - bpo-29347: Fixed possibly dereferencing undefined pointers
   when creating weakref objects.
+
 - bpo-29463: Add ``docstring`` field to Module, ClassDef, FunctionDef,
   and AsyncFunctionDef ast nodes.  docstring is not first stmt in their body
   anymore.  It affects ``co_firstlineno`` and ``co_lnotab`` of code object
index 81c89dfadfaac02cee6b31fe76bca3495d802218..0a82965c51732081a6521ed65b00bb46eeda6056 100644 (file)
@@ -1354,9 +1354,15 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
         TARGET(BINARY_MODULO) {
             PyObject *divisor = POP();
             PyObject *dividend = TOP();
-            PyObject *res = PyUnicode_CheckExact(dividend) ?
-                PyUnicode_Format(dividend, divisor) :
-                PyNumber_Remainder(dividend, divisor);
+            PyObject *res;
+            if (PyUnicode_CheckExact(dividend) && (
+                  !PyUnicode_Check(divisor) || PyUnicode_CheckExact(divisor))) {
+              // fast path; string formatting, but not if the RHS is a str subclass
+              // (see issue28598)
+              res = PyUnicode_Format(dividend, divisor);
+            } else {
+              res = PyNumber_Remainder(dividend, divisor);
+            }
             Py_DECREF(divisor);
             Py_DECREF(dividend);
             SET_TOP(res);