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
* Multiplied string literals lost their factor when they are part of another
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
source file. (Github issue #2230)
......
......@@ -11616,13 +11616,19 @@ class ModNode(DivNode):
self.operand2.result())
def py_operation_function(self, code):
if self.operand1.type is unicode_type:
if self.operand1.may_be_none():
type1, type2 = self.operand1.type, self.operand2.type
# ("..." % 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'
else:
return 'PyUnicode_Format'
elif self.operand1.type is str_type:
if self.operand1.may_be_none():
elif type1 is str_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_PyString_FormatSafe'
else:
return '__Pyx_PyString_Format'
......
......@@ -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)
#endif
#define __Pyx_PyString_FormatSafe(a, b) ((unlikely((a) == Py_None)) ? PyNumber_Remainder(a, b) : __Pyx_PyString_Format(a, b))
#define __Pyx_PyUnicode_FormatSafe(a, b) ((unlikely((a) == Py_None)) ? PyNumber_Remainder(a, b) : PyUnicode_Format(a, b))
// ("..." % x) must call PyNumber_Remainder() if x is a string subclass that implements "__rmod__()".
#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
#define __Pyx_PyString_Format(a, b) PyUnicode_Format(a, b)
......
......@@ -34,3 +34,27 @@ def mix_format(a, int b, list c):
-x-2-[1]-
"""
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