Commit 6c1d47b1 authored by Stefan Behnel's avatar Stefan Behnel

Try to call "__rmod__()" during string %-formatting when the right side is a...

Try to call "__rmod__()" during string %-formatting when the right side is a string subtype that implements it.
See https://bugs.python.org/issue28598
parent 977c3326
...@@ -73,6 +73,10 @@ Bugs fixed ...@@ -73,6 +73,10 @@ Bugs fixed
* Multiplied string literals lost their factor when they are part of another * Multiplied string literals lost their factor when they are part of another
constant expression (e.g. 'x' * 10 + 'y' => 'xy'). constant expression (e.g. 'x' * 10 + 'y' => 'xy').
* String formatting with the '%' operator didn't call the special ``__rmod__()``
method if the right side is a string subclass that implements it.
(Python issue 28598)
* The directive ``language_level=3`` did not apply to the first token in the * The directive ``language_level=3`` did not apply to the first token in the
source file. (Github issue #2230) source file. (Github issue #2230)
......
...@@ -11616,13 +11616,19 @@ class ModNode(DivNode): ...@@ -11616,13 +11616,19 @@ class ModNode(DivNode):
self.operand2.result()) self.operand2.result())
def py_operation_function(self, code): def py_operation_function(self, code):
if self.operand1.type is unicode_type: type1, type2 = self.operand1.type, self.operand2.type
if self.operand1.may_be_none(): # ("..." % x) must call "x.__rmod__()" for string subtypes.
if type1 is unicode_type:
if self.operand1.may_be_none() or (
type2.is_extension_type and type2.subtype_of(type1) or
type2 is py_object_type and not isinstance(self.operand2, CoerceToPyTypeNode)):
return '__Pyx_PyUnicode_FormatSafe' return '__Pyx_PyUnicode_FormatSafe'
else: else:
return 'PyUnicode_Format' return 'PyUnicode_Format'
elif self.operand1.type is str_type: elif type1 is str_type:
if self.operand1.may_be_none(): if self.operand1.may_be_none() or (
type2.is_extension_type and type2.subtype_of(type1) or
type2 is py_object_type and not isinstance(self.operand2, CoerceToPyTypeNode)):
return '__Pyx_PyString_FormatSafe' return '__Pyx_PyString_FormatSafe'
else: else:
return '__Pyx_PyString_Format' return '__Pyx_PyString_Format'
......
...@@ -576,8 +576,9 @@ static CYTHON_INLINE void * PyThread_tss_get(Py_tss_t *key) { ...@@ -576,8 +576,9 @@ static CYTHON_INLINE void * PyThread_tss_get(Py_tss_t *key) {
#define PyObject_Format(obj, fmt) PyObject_CallMethod(obj, "__format__", "O", fmt) #define PyObject_Format(obj, fmt) PyObject_CallMethod(obj, "__format__", "O", fmt)
#endif #endif
#define __Pyx_PyString_FormatSafe(a, b) ((unlikely((a) == Py_None)) ? PyNumber_Remainder(a, b) : __Pyx_PyString_Format(a, b)) // ("..." % x) must call PyNumber_Remainder() if x is a string subclass that implements "__rmod__()".
#define __Pyx_PyUnicode_FormatSafe(a, b) ((unlikely((a) == Py_None)) ? PyNumber_Remainder(a, b) : PyUnicode_Format(a, b)) #define __Pyx_PyString_FormatSafe(a, b) ((unlikely((a) == Py_None || (PyString_Check(b) && !PyString_CheckExact(b)))) ? PyNumber_Remainder(a, b) : __Pyx_PyString_Format(a, b))
#define __Pyx_PyUnicode_FormatSafe(a, b) ((unlikely((a) == Py_None || (PyUnicode_Check(b) && !PyUnicode_CheckExact(b)))) ? PyNumber_Remainder(a, b) : PyUnicode_Format(a, b))
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
#define __Pyx_PyString_Format(a, b) PyUnicode_Format(a, b) #define __Pyx_PyString_Format(a, b) PyUnicode_Format(a, b)
......
...@@ -34,3 +34,27 @@ def mix_format(a, int b, list c): ...@@ -34,3 +34,27 @@ def mix_format(a, int b, list c):
-x-2-[1]- -x-2-[1]-
""" """
return '-%s-%r-%a-' % (a, b, c) return '-%s-%r-%a-' % (a, b, c)
class PySubtype(unicode):
def __rmod__(self, other):
return f'PyRMOD({other})'
cdef class ExtSubtype(unicode):
def __mod__(one, other):
return f'ExtMOD({one}, {other})'
def subtypes():
"""
>>> py, ext = subtypes()
>>> print(py)
PyRMOD(-%s-)
>>> print(ext)
ExtMOD(-%s-, ExtSub)
"""
return [
'-%s-' % PySubtype("PySub"),
'-%s-' % ExtSubtype("ExtSub"),
]
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment