diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 9bf801ad608c773..8b4ae8752c687d0 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -572,6 +572,11 @@ APIs: - :c:expr:`PyObject*` - The result of calling :c:func:`PyObject_Repr`. + * - ``#R`` + - :c:expr:`PyObject*` + - Similar to ``R`` format, but don't call :meth:`~object.__repr__` + method on :class:`str` subclasses. + * - ``T`` - :c:expr:`PyObject*` - Get the fully qualified name of an object type; @@ -630,6 +635,9 @@ APIs: .. versionchanged:: 3.13 Support for ``%T``, ``%#T``, ``%N`` and ``%#N`` formats added. + .. versionchanged:: next + Support for ``%#R`` format added. + .. c:function:: PyObject* PyUnicode_FromFormatV(const char *format, va_list vargs) diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index d24fed49d1f95e3..ff3ab77c888f056 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -1077,7 +1077,11 @@ C API changes New features ------------ -* TODO +* Add ``%#R`` format to :c:func:`PyUnicode_FromFormat`: similar to ``%R`` + format, but don't call :meth:`~object.__str__` method on :class:`str` + subclasses. + (Contributed by Serhiy Storchaka in :gh:`154610`.) + Porting to Python 3.16 ---------------------- diff --git a/Include/ceval.h b/Include/ceval.h index e9df8684996e23f..18429a8b37769b1 100644 --- a/Include/ceval.h +++ b/Include/ceval.h @@ -130,8 +130,9 @@ PyAPI_FUNC(void) PyEval_ReleaseThread(PyThreadState *tstate); #define FVC_STR 0x1 #define FVC_REPR 0x2 #define FVC_ASCII 0x3 -#define FVS_MASK 0x4 -#define FVS_HAVE_SPEC 0x4 +#define FVC_ALT_REPR 0x4 +#define FVS_MASK 0x8 +#define FVS_HAVE_SPEC 0x8 #ifndef Py_LIMITED_API # define Py_CPYTHON_CEVAL_H diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 8c8a2d9ea51aef9..bc26ae961fba073 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -298,6 +298,7 @@ _PyStaticObjects_CheckAll(PyInterpreterState *interp) { _PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(all_threads), "all_threads", 11); _PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(allow_code), "allow_code", 10); _PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(alphabet), "alphabet", 8); + _PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(alt), "alt", 3); _PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(any), "any", 3); _PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(append), "append", 6); _PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(arg), "arg", 3); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 5b35c53e0aa03b1..629b06775b4ed6a 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -310,6 +310,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(all_threads) STRUCT_FOR_ID(allow_code) STRUCT_FOR_ID(alphabet) + STRUCT_FOR_ID(alt) STRUCT_FOR_ID(any) STRUCT_FOR_ID(append) STRUCT_FOR_ID(arg) diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 41786cb267c2e96..82bc077710a8811 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -1037,6 +1037,8 @@ static inline Py_ALWAYS_INLINE void _Py_INCREF_MORTAL(PyObject *op) * references. */ PyAPI_FUNC(int) _PyObject_VisitType(PyObject *op, visitproc visit, void *arg); +extern PyObject* _PyObject_AltRepr(PyObject *); + #ifdef __cplusplus } #endif diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index c80925f020186ba..8a100fc8a6dc797 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -1585,6 +1585,7 @@ extern "C" { INIT_ID(all_threads), \ INIT_ID(allow_code), \ INIT_ID(alphabet), \ + INIT_ID(alt), \ INIT_ID(any), \ INIT_ID(append), \ INIT_ID(arg), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index b30cfc678de1cd7..9ff0a3d0c81742a 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -1020,6 +1020,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(alt); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(any); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); diff --git a/Lib/test/test_capi/test_unicode.py b/Lib/test/test_capi/test_unicode.py index 0dcd8a25ad0128d..880d759a0046aef 100644 --- a/Lib/test/test_capi/test_unicode.py +++ b/Lib/test/test_capi/test_unicode.py @@ -442,6 +442,7 @@ def check_format(expected, format, *args): check_format(' abc[\ufffd]', b'%10.7s', b'abc[\xe2\x82]') + # Test %A and %R check_format("'\\u20acABC'", b'%A', '\u20acABC') check_format("'\\u20", @@ -455,6 +456,16 @@ def check_format(expected, format, *args): check_format('\u20acAB', b'%.3U', '\u20acABCDEF') + # Test %#R: do not call __str__() of str subclasses + check_format("'abc'", + b'%#R', 'abc') + class StrSubclass(str): + def __repr__(self): + return 'StrSubclass' + check_format("'abc'", + b'%#R', StrSubclass('abc')) + self.assertEqual(repr(StrSubclass()), 'StrSubclass') + check_format('\u20acAB', b'%.3V', '\u20acABCDEF', None) check_format('abc[', diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 2d6320549b03f62..d6132e5a2fe48ed 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -1365,6 +1365,14 @@ def test_conversions(self): self.assertEqual(f'{"a"!r}', "'a'") self.assertEqual(f'{"a"!a}', "'a'") + # Test !R conversion + class OverrideRepr(str): + def __repr__(self): + return 'CUSTOM REPR' + abc = OverrideRepr('abc') + self.assertEqual(f'{abc!r}', 'CUSTOM REPR') + self.assertEqual(f'{abc!R}', "'abc'") + # Conversions can have trailing whitespace after them since it # does not provide any significance self.assertEqual(f"{3!s }", "3") @@ -1390,7 +1398,7 @@ def test_conversions(self): for conv_identifier in 'g', 'A', 'G', 'ä', 'ɐ': self.assertAllRaise(SyntaxError, "f-string: invalid conversion character %r: " - "expected 's', 'r', or 'a'" % conv_identifier, + "expected 's', 'r', 'R', or 'a'" % conv_identifier, ["f'{3!" + conv_identifier + "}'"]) for conv_non_identifier in '3', '!': @@ -1406,7 +1414,7 @@ def test_conversions(self): self.assertAllRaise(SyntaxError, "f-string: invalid conversion character 'ss': " - "expected 's', 'r', or 'a'", + "expected 's', 'r', 'R', or 'a'", ["f'{3!ss}'", "f'{3!ss:}'", "f'{3!ss:s}'", diff --git a/Lib/test/test_str.py b/Lib/test/test_str.py index 17163182be08c4c..c7917bc15d55e14 100644 --- a/Lib/test/test_str.py +++ b/Lib/test/test_str.py @@ -1279,6 +1279,14 @@ def __repr__(self): self.assertEqual('{0!a}'.format(F('Hello')), 'F(Hello)') self.assertEqual('{0!a}'.format(F('\u0374')), 'F(\\u0374)') + # !R conversion + class OverrideRepr(str): + def __repr__(self): + return 'CUSTOM REPR' + abc = OverrideRepr('abc') + self.assertEqual('{0!r}'.format(abc), 'CUSTOM REPR') + self.assertEqual('{0!R}'.format(abc), "'abc'") + # test fallback to object.__format__ self.assertEqual('{0}'.format({}), '{}') self.assertEqual('{0}'.format([]), '[]') diff --git a/Lib/test/test_string/_support.py b/Lib/test/test_string/_support.py index e1d7f6f6500aebb..aafc79d7518bf6c 100644 --- a/Lib/test/test_string/_support.py +++ b/Lib/test/test_string/_support.py @@ -48,6 +48,8 @@ def convert(value, conversion): return ascii(value) elif conversion == "r": return repr(value) + elif conversion == "R": + return repr(value, alt=True) elif conversion == "s": return str(value) return value diff --git a/Lib/test/test_tstring.py b/Lib/test/test_tstring.py index 67a8e0fc6bcffb1..0612ca0a49fe6c9 100644 --- a/Lib/test/test_tstring.py +++ b/Lib/test/test_tstring.py @@ -106,6 +106,15 @@ def test_conversions(self): self.assertTStringEqual(t, ("ASCII: ", ""), [(text, "text", "a")]) self.assertEqual(fstring(t), f"ASCII: {ascii(text)}") + # Test !R conversion (repr) + class StrSubclass(str): + def __repr__(self): + return '' + obj = StrSubclass('abc') + t = t"Data: {obj!R}" + self.assertTStringEqual(t, ("Data: ", ""), [(obj, "obj", "R")]) + self.assertEqual(fstring(t), f"Data: 'abc'") + # Test !z conversion (error) num = 1 with self.assertRaises(SyntaxError): @@ -305,11 +314,11 @@ def test_syntax_errors(self): ("t'{x!}'", "t-string: missing conversion character"), ("t'{x=!}'", "t-string: missing conversion character"), ("t'{x!z}'", "t-string: invalid conversion character 'z': " - "expected 's', 'r', or 'a'"), + "expected 's', 'r', 'R', or 'a'"), ("f\"{t'{x!z}'}\"", "t-string: invalid conversion character 'z': " - "expected 's', 'r', or 'a'"), + "expected 's', 'r', 'R', or 'a'"), ("t'{f\"{x!z}\"}'", "f-string: invalid conversion character 'z': " - "expected 's', 'r', or 'a'"), + "expected 's', 'r', 'R', or 'a'"), ("t'{lambda:1}'", "t-string: lambda expressions are not allowed " "without parentheses"), ("t'{x:{;}}'", "t-string: expecting a valid expression after '{'"), diff --git a/Misc/NEWS.d/next/C_API/2026-09-17-17-24-42.gh-issue-154610.fhmq2b.rst b/Misc/NEWS.d/next/C_API/2026-09-17-17-24-42.gh-issue-154610.fhmq2b.rst new file mode 100644 index 000000000000000..56711a3b11dff29 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-09-17-17-24-42.gh-issue-154610.fhmq2b.rst @@ -0,0 +1,3 @@ +Add ``%#R`` format to :c:func:`PyUnicode_FromFormat`: similar to ``%R`` +format, but don't call :meth:`~object.__str__` method on :class:`str` +subclasses. Patch by Victor Stinner. diff --git a/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h index 7a75e80298fcd82..3ebe94645c3356d 100644 --- a/Modules/_testinternalcapi/test_cases.c.h +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -5766,7 +5766,7 @@ _PyStackRef result; value = stack_pointer[-1]; conversion_func conv_fn; - assert(oparg >= FVC_STR && oparg <= FVC_ASCII); + assert(oparg >= FVC_STR && oparg <= FVC_ALT_REPR); conv_fn = _PyEval_ConversionFuncs[oparg]; _PyFrame_SetStackPointer(frame, stack_pointer); _PyFrame_StackPointerValidate(frame); diff --git a/Objects/interpolationobject.c b/Objects/interpolationobject.c index e37724fb7852a27..bcffdc24d54176f 100644 --- a/Objects/interpolationobject.c +++ b/Objects/interpolationobject.c @@ -209,6 +209,9 @@ _PyInterpolation_Build(PyObject *value, PyObject *str, int conversion, PyObject case FVC_REPR: interpolation->conversion = _Py_LATIN1_CHR('r'); break; + case FVC_ALT_REPR: + interpolation->conversion = _Py_LATIN1_CHR('R'); + break; case FVC_STR: interpolation->conversion = _Py_LATIN1_CHR('s'); break; diff --git a/Objects/object.c b/Objects/object.c index e3f29b71301695e..7916593a1f31f6b 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -797,6 +797,19 @@ PyObject_Repr(PyObject *v) return res; } +PyObject * +_PyObject_AltRepr(PyObject *v) +{ + if (v != NULL && PyUnicode_Check(v)) { + if (PyErr_CheckSignals()) { + return NULL; + } + reprfunc repr_func = PyUnicode_Type.tp_repr; + return repr_func(v); + } + return PyObject_Repr(v); +} + PyObject * PyObject_Str(PyObject *v) { diff --git a/Objects/stringlib/unicode_format.h b/Objects/stringlib/unicode_format.h index c9c46283840d188..a478a878b58019b 100644 --- a/Objects/stringlib/unicode_format.h +++ b/Objects/stringlib/unicode_format.h @@ -770,6 +770,8 @@ do_conversion(PyObject *obj, Py_UCS4 conversion) switch (conversion) { case 'r': return PyObject_Repr(obj); + case 'R': + return _PyObject_AltRepr(obj); case 's': return PyObject_Str(obj); case 'a': diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 86b9baadd0d8aa9..f1726fed58a46df 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -2971,9 +2971,14 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer, case 'R': { PyObject *obj = va_arg(*vargs, PyObject *); - PyObject *repr; assert(obj); - repr = PyObject_Repr(obj); + PyObject *repr; + if (flags & F_ALT) { + repr = _PyObject_AltRepr(obj); + } + else { + repr = PyObject_Repr(obj); + } if (!repr) return NULL; if (unicode_fromformat_write_str(writer, repr, width, precision, flags) == -1) { diff --git a/Parser/action_helpers.c b/Parser/action_helpers.c index 88a9df98aa5b62a..4da4a64361a9eff 100644 --- a/Parser/action_helpers.c +++ b/Parser/action_helpers.c @@ -1047,9 +1047,9 @@ _PyPegen_check_fstring_conversion(Parser *p, Token* conv_token, expr_ty conv) Py_UCS4 first = PyUnicode_READ_CHAR(conv->v.Name.id, 0); if (PyUnicode_GET_LENGTH(conv->v.Name.id) > 1 || - !(first == 's' || first == 'r' || first == 'a')) { + !(first == 's' || first == 'r' || first == 'R' || first == 'a')) { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(conv, - "%c-string: invalid conversion character %R: expected 's', 'r', or 'a'", + "%c-string: invalid conversion character %R: expected 's', 'r', 'R', or 'a'", formatted_string_prefix(p), conv->v.Name.id); return NULL; diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 965cf20fe617787..7e0ea9f857a2aa6 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2639,6 +2639,8 @@ repr as builtin_repr obj: object / + * + alt: bool = False Return the canonical string representation of the object. @@ -2646,10 +2648,15 @@ For many object types, including most builtins, eval(repr(obj)) == obj. [clinic start generated code]*/ static PyObject * -builtin_repr(PyObject *module, PyObject *obj) -/*[clinic end generated code: output=7ed3778c44fd0194 input=1c9e6d66d3e3be04]*/ +builtin_repr_impl(PyObject *module, PyObject *obj, int alt) +/*[clinic end generated code: output=fc5168bb1b4c8846 input=0b1b2969c594dc9a]*/ { - return PyObject_Repr(obj); + if (!alt) { + return PyObject_Repr(obj); + } + else { + return _PyObject_AltRepr(obj); + } } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index fb0cdf4d65e060d..16ea7ed815c30a6 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -5868,7 +5868,7 @@ dummy_func( inst(CONVERT_VALUE, (value -- result)) { conversion_func conv_fn; - assert(oparg >= FVC_STR && oparg <= FVC_ASCII); + assert(oparg >= FVC_STR && oparg <= FVC_ALT_REPR); conv_fn = _PyEval_ConversionFuncs[oparg]; PyObject *result_o = conv_fn(PyStackRef_AsPyObjectBorrow(value)); PyStackRef_CLOSE(value); diff --git a/Python/ceval.c b/Python/ceval.c index 8cf02651d9a408f..6d68cc09363957c 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -346,10 +346,11 @@ const binaryfunc _PyEval_BinaryOps[] = { [NB_SUBSCR] = PyObject_GetItem, }; -const conversion_func _PyEval_ConversionFuncs[4] = { +const conversion_func _PyEval_ConversionFuncs[5] = { [FVC_STR] = PyObject_Str, [FVC_REPR] = PyObject_Repr, - [FVC_ASCII] = PyObject_ASCII + [FVC_ASCII] = PyObject_ASCII, + [FVC_ALT_REPR] = _PyObject_AltRepr, }; const _Py_SpecialMethod _Py_SpecialMethods[] = { diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index 5858ca9ff88ec22..68b5c892c7741b3 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -1284,7 +1284,7 @@ builtin_input(PyObject *module, PyObject *const *args, Py_ssize_t nargs) } PyDoc_STRVAR(builtin_repr__doc__, -"repr($module, obj, /)\n" +"repr($module, obj, /, *, alt=False)\n" "--\n" "\n" "Return the canonical string representation of the object.\n" @@ -1292,7 +1292,66 @@ PyDoc_STRVAR(builtin_repr__doc__, "For many object types, including most builtins, eval(repr(obj)) == obj."); #define BUILTIN_REPR_METHODDEF \ - {"repr", (PyCFunction)builtin_repr, METH_O, builtin_repr__doc__}, + {"repr", _PyCFunction_CAST(builtin_repr), METH_FASTCALL|METH_KEYWORDS, builtin_repr__doc__}, + +static PyObject * +builtin_repr_impl(PyObject *module, PyObject *obj, int alt); + +static PyObject * +builtin_repr(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(alt), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "alt", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "repr", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *obj; + int alt = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + obj = args[0]; + if (!noptargs) { + goto skip_optional_kwonly; + } + alt = PyObject_IsTrue(args[1]); + if (alt < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = builtin_repr_impl(module, obj, alt); + +exit: + return return_value; +} PyDoc_STRVAR(builtin_round__doc__, "round($module, /, number, ndigits=None)\n" @@ -1501,4 +1560,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=b56739f2e13f616a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=d3c8cf28704c898b input=a9049054013a1b77]*/ diff --git a/Python/codegen.c b/Python/codegen.c index c12baf6b15a6dec..1d2965ccbb01c58 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -4338,6 +4338,7 @@ codegen_interpolation(compiler *c, expr_ty e) switch (conversion) { case 's': oparg |= FVC_STR << 2; break; case 'r': oparg |= FVC_REPR << 2; break; + case 'R': oparg |= FVC_ALT_REPR << 2; break; case 'a': oparg |= FVC_ASCII << 2; break; default: PyErr_Format(PyExc_SystemError, @@ -4365,6 +4366,7 @@ codegen_formatted_value(compiler *c, expr_ty e) switch (conversion) { case 's': oparg = FVC_STR; break; case 'r': oparg = FVC_REPR; break; + case 'R': oparg = FVC_ALT_REPR; break; case 'a': oparg = FVC_ASCII; break; default: PyErr_Format(PyExc_SystemError, diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 9aad9e003765cf8..cd4bb1426d3a030 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -20638,7 +20638,7 @@ oparg = CURRENT_OPARG(); value = _stack_item_0; conversion_func conv_fn; - assert(oparg >= FVC_STR && oparg <= FVC_ASCII); + assert(oparg >= FVC_STR && oparg <= FVC_ALT_REPR); conv_fn = _PyEval_ConversionFuncs[oparg]; stack_pointer[0] = value; stack_pointer += 1; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 77c18b3d61fefc7..60471e6a20a69a5 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -5766,7 +5766,7 @@ _PyStackRef result; value = stack_pointer[-1]; conversion_func conv_fn; - assert(oparg >= FVC_STR && oparg <= FVC_ASCII); + assert(oparg >= FVC_STR && oparg <= FVC_ALT_REPR); conv_fn = _PyEval_ConversionFuncs[oparg]; _PyFrame_SetStackPointer(frame, stack_pointer); _PyFrame_StackPointerValidate(frame);