Skip to content

Commit 886483e

Browse files
authored
bpo-34595: Add %T format to PyUnicode_FromFormatV() (GH-9080)
* Add %T format to PyUnicode_FromFormatV(), and so to PyUnicode_FromFormat() and PyErr_Format(), to format an object type name: equivalent to "%s" with Py_TYPE(obj)->tp_name. * Replace Py_TYPE(obj)->tp_name with %T format in unicodeobject.c. * Add unit test on %T format. * Rename unicode_fromformat_write_cstr() to unicode_fromformat_write_utf8(), to make the intent more explicit.
1 parent 254a466 commit 886483e

File tree

4 files changed

+66
-53
lines changed

4 files changed

+66
-53
lines changed

Doc/c-api/unicode.rst

+6
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,9 @@ APIs:
519519
| :attr:`%R` | PyObject\* | The result of calling |
520520
| | | :c:func:`PyObject_Repr`. |
521521
+-------------------+---------------------+--------------------------------+
522+
| :attr:`%T` | PyObject\* | Object type name, equivalent |
523+
| | | to ``Py_TYPE(op)->tp_name``. |
524+
+-------------------+---------------------+--------------------------------+
522525
523526
An unrecognized format character causes all the rest of the format string to be
524527
copied as-is to the result string, and any extra arguments discarded.
@@ -543,6 +546,9 @@ APIs:
543546
Support width and precision formatter for ``"%s"``, ``"%A"``, ``"%U"``,
544547
``"%V"``, ``"%S"``, ``"%R"`` added.
545548
549+
.. versionchanged:: 3.7
550+
Support for ``"%T"`` (object type name) added.
551+
546552
547553
.. c:function:: PyObject* PyUnicode_FromFormatV(const char *format, va_list vargs)
548554

Lib/test/test_unicode.py

+4
Original file line numberDiff line numberDiff line change
@@ -2655,6 +2655,10 @@ def check_format(expected, format, *args):
26552655
check_format(r"%A:'abc\xe9\uabcd\U0010ffff'",
26562656
b'%%A:%A', 'abc\xe9\uabcd\U0010ffff')
26572657

2658+
# test %T (object type name)
2659+
check_format(r"type name: str",
2660+
b'type name: %T', 'text')
2661+
26582662
# test %V
26592663
check_format('repr=abc',
26602664
b'repr=%V', 'abc', b'xyz')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:c:func:`PyUnicode_FromFormatV`: add ``%T`` format to
2+
:c:func:`PyUnicode_FromFormatV`, and so to :c:func:`PyUnicode_FromFormat`
3+
and :c:func:`PyErr_Format`, to format an object type name: equivalent to
4+
"%s" with ``Py_TYPE(obj)->tp_name``.

Objects/unicodeobject.c

+52-53
Original file line numberDiff line numberDiff line change
@@ -768,8 +768,7 @@ ensure_unicode(PyObject *obj)
768768
{
769769
if (!PyUnicode_Check(obj)) {
770770
PyErr_Format(PyExc_TypeError,
771-
"must be str, not %.100s",
772-
Py_TYPE(obj)->tp_name);
771+
"must be str, not %T", obj);
773772
return -1;
774773
}
775774
return PyUnicode_READY(obj);
@@ -2530,7 +2529,7 @@ unicode_fromformat_write_str(_PyUnicodeWriter *writer, PyObject *str,
25302529
}
25312530

25322531
static int
2533-
unicode_fromformat_write_cstr(_PyUnicodeWriter *writer, const char *str,
2532+
unicode_fromformat_write_utf8(_PyUnicodeWriter *writer, const char *str,
25342533
Py_ssize_t width, Py_ssize_t precision)
25352534
{
25362535
/* UTF-8 */
@@ -2747,7 +2746,7 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
27472746
{
27482747
/* UTF-8 */
27492748
const char *s = va_arg(*vargs, const char*);
2750-
if (unicode_fromformat_write_cstr(writer, s, width, precision) < 0)
2749+
if (unicode_fromformat_write_utf8(writer, s, width, precision) < 0)
27512750
return NULL;
27522751
break;
27532752
}
@@ -2773,7 +2772,7 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
27732772
}
27742773
else {
27752774
assert(str != NULL);
2776-
if (unicode_fromformat_write_cstr(writer, str, width, precision) < 0)
2775+
if (unicode_fromformat_write_utf8(writer, str, width, precision) < 0)
27772776
return NULL;
27782777
}
27792778
break;
@@ -2827,6 +2826,17 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
28272826
break;
28282827
}
28292828

2829+
case 'T':
2830+
{
2831+
/* Object type name (tp_name) */
2832+
PyObject *obj = va_arg(*vargs, PyObject *);
2833+
PyTypeObject *type = Py_TYPE(obj);
2834+
const char *type_name = type->tp_name;
2835+
if (unicode_fromformat_write_utf8(writer, type_name, -1, -1) < 0) {
2836+
return NULL;
2837+
}
2838+
break;
2839+
}
28302840
case '%':
28312841
if (_PyUnicodeWriter_WriteCharInline(writer, '%') < 0)
28322842
return NULL;
@@ -3024,8 +3034,7 @@ PyUnicode_FromObject(PyObject *obj)
30243034
return _PyUnicode_Copy(obj);
30253035
}
30263036
PyErr_Format(PyExc_TypeError,
3027-
"Can't convert '%.100s' object to str implicitly",
3028-
Py_TYPE(obj)->tp_name);
3037+
"Can't convert '%T' object to str implicitly", obj);
30293038
return NULL;
30303039
}
30313040

@@ -3061,8 +3070,8 @@ PyUnicode_FromEncodedObject(PyObject *obj,
30613070
/* Retrieve a bytes buffer view through the PEP 3118 buffer interface */
30623071
if (PyObject_GetBuffer(obj, &buffer, PyBUF_SIMPLE) < 0) {
30633072
PyErr_Format(PyExc_TypeError,
3064-
"decoding to str: need a bytes-like object, %.80s found",
3065-
Py_TYPE(obj)->tp_name);
3073+
"decoding to str: need a bytes-like object, %T found",
3074+
obj);
30663075
return NULL;
30673076
}
30683077

@@ -3192,10 +3201,9 @@ PyUnicode_Decode(const char *s,
31923201
goto onError;
31933202
if (!PyUnicode_Check(unicode)) {
31943203
PyErr_Format(PyExc_TypeError,
3195-
"'%.400s' decoder returned '%.400s' instead of 'str'; "
3204+
"'%.400s' decoder returned '%T' instead of 'str'; "
31963205
"use codecs.decode() to decode to arbitrary types",
3197-
encoding,
3198-
Py_TYPE(unicode)->tp_name);
3206+
encoding, unicode);
31993207
Py_DECREF(unicode);
32003208
goto onError;
32013209
}
@@ -3255,10 +3263,9 @@ PyUnicode_AsDecodedUnicode(PyObject *unicode,
32553263
goto onError;
32563264
if (!PyUnicode_Check(v)) {
32573265
PyErr_Format(PyExc_TypeError,
3258-
"'%.400s' decoder returned '%.400s' instead of 'str'; "
3266+
"'%.400s' decoder returned '%T' instead of 'str'; "
32593267
"use codecs.decode() to decode to arbitrary types",
3260-
encoding,
3261-
Py_TYPE(unicode)->tp_name);
3268+
encoding, unicode);
32623269
Py_DECREF(v);
32633270
goto onError;
32643271
}
@@ -3489,10 +3496,9 @@ PyUnicode_AsEncodedString(PyObject *unicode,
34893496
}
34903497

34913498
PyErr_Format(PyExc_TypeError,
3492-
"'%.400s' encoder returned '%.400s' instead of 'bytes'; "
3499+
"'%.400s' encoder returned '%T' instead of 'bytes'; "
34933500
"use codecs.encode() to encode to arbitrary types",
3494-
encoding,
3495-
Py_TYPE(v)->tp_name);
3501+
encoding, v);
34963502
Py_DECREF(v);
34973503
return NULL;
34983504
}
@@ -3523,10 +3529,9 @@ PyUnicode_AsEncodedUnicode(PyObject *unicode,
35233529
goto onError;
35243530
if (!PyUnicode_Check(v)) {
35253531
PyErr_Format(PyExc_TypeError,
3526-
"'%.400s' encoder returned '%.400s' instead of 'str'; "
3532+
"'%.400s' encoder returned '%T' instead of 'str'; "
35273533
"use codecs.encode() to encode to arbitrary types",
3528-
encoding,
3529-
Py_TYPE(v)->tp_name);
3534+
encoding, v);
35303535
Py_DECREF(v);
35313536
goto onError;
35323537
}
@@ -3698,9 +3703,11 @@ PyUnicode_FSDecoder(PyObject* arg, void* addr)
36983703

36993704
if (!PyBytes_Check(path) &&
37003705
PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
3701-
"path should be string, bytes, or os.PathLike, not %.200s",
3702-
Py_TYPE(arg)->tp_name)) {
3703-
Py_DECREF(path);
3706+
"path should be string, bytes, "
3707+
"or os.PathLike, not %T",
3708+
arg))
3709+
{
3710+
Py_DECREF(path);
37043711
return 0;
37053712
}
37063713
path_bytes = PyBytes_FromObject(path);
@@ -3717,8 +3724,8 @@ PyUnicode_FSDecoder(PyObject* arg, void* addr)
37173724
}
37183725
else {
37193726
PyErr_Format(PyExc_TypeError,
3720-
"path should be string, bytes, or os.PathLike, not %.200s",
3721-
Py_TYPE(arg)->tp_name);
3727+
"path should be string, bytes, or os.PathLike, not %T",
3728+
arg);
37223729
Py_DECREF(path);
37233730
return 0;
37243731
}
@@ -9886,9 +9893,8 @@ _PyUnicode_JoinArray(PyObject *separator, PyObject *const *items, Py_ssize_t seq
98869893
else {
98879894
if (!PyUnicode_Check(separator)) {
98889895
PyErr_Format(PyExc_TypeError,
9889-
"separator: expected str instance,"
9890-
" %.80s found",
9891-
Py_TYPE(separator)->tp_name);
9896+
"separator: expected str instance, %T found",
9897+
separator);
98929898
goto onError;
98939899
}
98949900
if (PyUnicode_READY(separator))
@@ -9919,9 +9925,8 @@ _PyUnicode_JoinArray(PyObject *separator, PyObject *const *items, Py_ssize_t seq
99199925
item = items[i];
99209926
if (!PyUnicode_Check(item)) {
99219927
PyErr_Format(PyExc_TypeError,
9922-
"sequence item %zd: expected str instance,"
9923-
" %.80s found",
9924-
i, Py_TYPE(item)->tp_name);
9928+
"sequence item %zd: expected str instance, %T found",
9929+
i, item);
99259930
goto onError;
99269931
}
99279932
if (PyUnicode_READY(item) == -1)
@@ -10736,7 +10741,7 @@ convert_uc(PyObject *obj, void *addr)
1073610741
if (!PyUnicode_Check(obj)) {
1073710742
PyErr_Format(PyExc_TypeError,
1073810743
"The fill character must be a unicode character, "
10739-
"not %.100s", Py_TYPE(obj)->tp_name);
10744+
"not %T", obj);
1074010745
return 0;
1074110746
}
1074210747
if (PyUnicode_READY(obj) < 0)
@@ -11142,8 +11147,8 @@ PyUnicode_Contains(PyObject *str, PyObject *substr)
1114211147

1114311148
if (!PyUnicode_Check(substr)) {
1114411149
PyErr_Format(PyExc_TypeError,
11145-
"'in <string>' requires string as left operand, not %.100s",
11146-
Py_TYPE(substr)->tp_name);
11150+
"'in <string>' requires string as left operand, not %T",
11151+
substr);
1114711152
return -1;
1114811153
}
1114911154
if (PyUnicode_READY(substr) == -1)
@@ -12848,9 +12853,7 @@ unicode_split_impl(PyObject *self, PyObject *sep, Py_ssize_t maxsplit)
1284812853
if (PyUnicode_Check(sep))
1284912854
return split(self, sep, maxsplit);
1285012855

12851-
PyErr_Format(PyExc_TypeError,
12852-
"must be str or None, not %.100s",
12853-
Py_TYPE(sep)->tp_name);
12856+
PyErr_Format(PyExc_TypeError, "must be str or None, not %T", sep);
1285412857
return NULL;
1285512858
}
1285612859

@@ -13036,9 +13039,7 @@ unicode_rsplit_impl(PyObject *self, PyObject *sep, Py_ssize_t maxsplit)
1303613039
if (PyUnicode_Check(sep))
1303713040
return rsplit(self, sep, maxsplit);
1303813041

13039-
PyErr_Format(PyExc_TypeError,
13040-
"must be str or None, not %.100s",
13041-
Py_TYPE(sep)->tp_name);
13042+
PyErr_Format(PyExc_TypeError, "must be str or None, not %T", sep);
1304213043
return NULL;
1304313044
}
1304413045

@@ -13333,8 +13334,8 @@ unicode_startswith(PyObject *self,
1333313334
if (!PyUnicode_Check(substring)) {
1333413335
PyErr_Format(PyExc_TypeError,
1333513336
"tuple for startswith must only contain str, "
13336-
"not %.100s",
13337-
Py_TYPE(substring)->tp_name);
13337+
"not %T",
13338+
substring);
1333813339
return NULL;
1333913340
}
1334013341
result = tailmatch(self, substring, start, end, -1);
@@ -13350,7 +13351,7 @@ unicode_startswith(PyObject *self,
1335013351
if (!PyUnicode_Check(subobj)) {
1335113352
PyErr_Format(PyExc_TypeError,
1335213353
"startswith first arg must be str or "
13353-
"a tuple of str, not %.100s", Py_TYPE(subobj)->tp_name);
13354+
"a tuple of str, not %T", subobj);
1335413355
return NULL;
1335513356
}
1335613357
result = tailmatch(self, subobj, start, end, -1);
@@ -13387,8 +13388,8 @@ unicode_endswith(PyObject *self,
1338713388
if (!PyUnicode_Check(substring)) {
1338813389
PyErr_Format(PyExc_TypeError,
1338913390
"tuple for endswith must only contain str, "
13390-
"not %.100s",
13391-
Py_TYPE(substring)->tp_name);
13391+
"not %T",
13392+
substring);
1339213393
return NULL;
1339313394
}
1339413395
result = tailmatch(self, substring, start, end, +1);
@@ -13403,7 +13404,7 @@ unicode_endswith(PyObject *self,
1340313404
if (!PyUnicode_Check(subobj)) {
1340413405
PyErr_Format(PyExc_TypeError,
1340513406
"endswith first arg must be str or "
13406-
"a tuple of str, not %.100s", Py_TYPE(subobj)->tp_name);
13407+
"a tuple of str, not %T", subobj);
1340713408
return NULL;
1340813409
}
1340913410
result = tailmatch(self, subobj, start, end, +1);
@@ -14313,15 +14314,13 @@ mainformatlong(PyObject *v,
1431314314
case 'x':
1431414315
case 'X':
1431514316
PyErr_Format(PyExc_TypeError,
14316-
"%%%c format: an integer is required, "
14317-
"not %.200s",
14318-
type, Py_TYPE(v)->tp_name);
14317+
"%%%c format: an integer is required, not %T",
14318+
type, v);
1431914319
break;
1432014320
default:
1432114321
PyErr_Format(PyExc_TypeError,
14322-
"%%%c format: a number is required, "
14323-
"not %.200s",
14324-
type, Py_TYPE(v)->tp_name);
14322+
"%%%c format: a number is required, not %T",
14323+
type, v);
1432514324
break;
1432614325
}
1432714326
return -1;

0 commit comments

Comments
 (0)