Skip to content

Commit 67fdc0f

Browse files
committed
Add !R conversion to f-strings and str.format()
Add _PyObject_AltRepr()
1 parent 12fad0c commit 67fdc0f

15 files changed

Lines changed: 53 additions & 14 deletions

File tree

‎Include/ceval.h‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,9 @@ PyAPI_FUNC(void) PyEval_ReleaseThread(PyThreadState *tstate);
130130
#define FVC_STR 0x1
131131
#define FVC_REPR 0x2
132132
#define FVC_ASCII 0x3
133-
#define FVS_MASK 0x4
134-
#define FVS_HAVE_SPEC 0x4
133+
#define FVC_ALT_REPR 0x4
134+
#define FVS_MASK 0x8
135+
#define FVS_HAVE_SPEC 0x8
135136

136137
#ifndef Py_LIMITED_API
137138
# define Py_CPYTHON_CEVAL_H

‎Include/internal/pycore_object.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,8 @@ static inline Py_ALWAYS_INLINE void _Py_INCREF_MORTAL(PyObject *op)
10371037
* references. */
10381038
PyAPI_FUNC(int) _PyObject_VisitType(PyObject *op, visitproc visit, void *arg);
10391039

1040+
extern PyObject* _PyObject_AltRepr(PyObject *);
1041+
10401042
#ifdef __cplusplus
10411043
}
10421044
#endif

‎Lib/test/test_fstring.py‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,6 +1365,14 @@ def test_conversions(self):
13651365
self.assertEqual(f'{"a"!r}', "'a'")
13661366
self.assertEqual(f'{"a"!a}', "'a'")
13671367

1368+
# Test !R conversion
1369+
class OverrideRepr(str):
1370+
def __repr__(self):
1371+
return 'CUSTOM REPR'
1372+
abc = OverrideRepr('abc')
1373+
self.assertEqual(f'{abc!r}', 'CUSTOM REPR')
1374+
self.assertEqual(f'{abc!R}', "'abc'")
1375+
13681376
# Conversions can have trailing whitespace after them since it
13691377
# does not provide any significance
13701378
self.assertEqual(f"{3!s }", "3")
@@ -1390,7 +1398,7 @@ def test_conversions(self):
13901398
for conv_identifier in 'g', 'A', 'G', 'ä', 'ɐ':
13911399
self.assertAllRaise(SyntaxError,
13921400
"f-string: invalid conversion character %r: "
1393-
"expected 's', 'r', or 'a'" % conv_identifier,
1401+
"expected 's', 'r', 'R', or 'a'" % conv_identifier,
13941402
["f'{3!" + conv_identifier + "}'"])
13951403

13961404
for conv_non_identifier in '3', '!':
@@ -1406,7 +1414,7 @@ def test_conversions(self):
14061414

14071415
self.assertAllRaise(SyntaxError,
14081416
"f-string: invalid conversion character 'ss': "
1409-
"expected 's', 'r', or 'a'",
1417+
"expected 's', 'r', 'R', or 'a'",
14101418
["f'{3!ss}'",
14111419
"f'{3!ss:}'",
14121420
"f'{3!ss:s}'",

‎Lib/test/test_str.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,6 +1279,14 @@ def __repr__(self):
12791279
self.assertEqual('{0!a}'.format(F('Hello')), 'F(Hello)')
12801280
self.assertEqual('{0!a}'.format(F('\u0374')), 'F(\\u0374)')
12811281

1282+
# !R conversion
1283+
class OverrideRepr(str):
1284+
def __repr__(self):
1285+
return 'CUSTOM REPR'
1286+
abc = OverrideRepr('abc')
1287+
self.assertEqual('{0!r}'.format(abc), 'CUSTOM REPR')
1288+
self.assertEqual('{0!R}'.format(abc), "'abc'")
1289+
12821290
# test fallback to object.__format__
12831291
self.assertEqual('{0}'.format({}), '{}')
12841292
self.assertEqual('{0}'.format([]), '[]')

‎Modules/_testinternalcapi/test_cases.c.h‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Objects/interpolationobject.c‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ _PyInterpolation_Build(PyObject *value, PyObject *str, int conversion, PyObject
209209
case FVC_REPR:
210210
interpolation->conversion = _Py_LATIN1_CHR('r');
211211
break;
212+
case FVC_ALT_REPR:
213+
interpolation->conversion = _Py_LATIN1_CHR('R');
214+
break;
212215
case FVC_STR:
213216
interpolation->conversion = _Py_LATIN1_CHR('s');
214217
break;

‎Objects/object.c‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,19 @@ PyObject_Repr(PyObject *v)
797797
return res;
798798
}
799799

800+
PyObject *
801+
_PyObject_AltRepr(PyObject *v)
802+
{
803+
if (v != NULL && PyUnicode_Check(v)) {
804+
if (PyErr_CheckSignals()) {
805+
return NULL;
806+
}
807+
reprfunc repr_func = PyUnicode_Type.tp_repr;
808+
return repr_func(v);
809+
}
810+
return PyObject_Repr(v);
811+
}
812+
800813
PyObject *
801814
PyObject_Str(PyObject *v)
802815
{

‎Objects/stringlib/unicode_format.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,8 @@ do_conversion(PyObject *obj, Py_UCS4 conversion)
770770
switch (conversion) {
771771
case 'r':
772772
return PyObject_Repr(obj);
773+
case 'R':
774+
return _PyObject_AltRepr(obj);
773775
case 's':
774776
return PyObject_Str(obj);
775777
case 'a':

‎Objects/unicodeobject.c‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2974,8 +2974,7 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
29742974
assert(obj);
29752975
PyObject *repr;
29762976
if (flags & F_ALT) {
2977-
reprfunc repr_func = PyUnicode_Type.tp_repr;
2978-
repr = repr_func(obj);
2977+
repr = _PyObject_AltRepr(obj);
29792978
}
29802979
else {
29812980
repr = PyObject_Repr(obj);

‎Parser/action_helpers.c‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,9 +1047,9 @@ _PyPegen_check_fstring_conversion(Parser *p, Token* conv_token, expr_ty conv)
10471047

10481048
Py_UCS4 first = PyUnicode_READ_CHAR(conv->v.Name.id, 0);
10491049
if (PyUnicode_GET_LENGTH(conv->v.Name.id) > 1 ||
1050-
!(first == 's' || first == 'r' || first == 'a')) {
1050+
!(first == 's' || first == 'r' || first == 'R' || first == 'a')) {
10511051
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(conv,
1052-
"%c-string: invalid conversion character %R: expected 's', 'r', or 'a'",
1052+
"%c-string: invalid conversion character %R: expected 's', 'r', 'R', or 'a'",
10531053
formatted_string_prefix(p),
10541054
conv->v.Name.id);
10551055
return NULL;

0 commit comments

Comments
 (0)