Commit 886483e2 authored by Victor Stinner's avatar Victor Stinner Committed by GitHub

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.
parent 254a4663
...@@ -519,6 +519,9 @@ APIs: ...@@ -519,6 +519,9 @@ APIs:
| :attr:`%R` | PyObject\* | The result of calling | | :attr:`%R` | PyObject\* | The result of calling |
| | | :c:func:`PyObject_Repr`. | | | | :c:func:`PyObject_Repr`. |
+-------------------+---------------------+--------------------------------+ +-------------------+---------------------+--------------------------------+
| :attr:`%T` | PyObject\* | Object type name, equivalent |
| | | to ``Py_TYPE(op)->tp_name``. |
+-------------------+---------------------+--------------------------------+
An unrecognized format character causes all the rest of the format string to be An unrecognized format character causes all the rest of the format string to be
copied as-is to the result string, and any extra arguments discarded. copied as-is to the result string, and any extra arguments discarded.
...@@ -543,6 +546,9 @@ APIs: ...@@ -543,6 +546,9 @@ APIs:
Support width and precision formatter for ``"%s"``, ``"%A"``, ``"%U"``, Support width and precision formatter for ``"%s"``, ``"%A"``, ``"%U"``,
``"%V"``, ``"%S"``, ``"%R"`` added. ``"%V"``, ``"%S"``, ``"%R"`` added.
.. versionchanged:: 3.7
Support for ``"%T"`` (object type name) added.
.. c:function:: PyObject* PyUnicode_FromFormatV(const char *format, va_list vargs) .. c:function:: PyObject* PyUnicode_FromFormatV(const char *format, va_list vargs)
......
...@@ -2655,6 +2655,10 @@ class CAPITest(unittest.TestCase): ...@@ -2655,6 +2655,10 @@ class CAPITest(unittest.TestCase):
check_format(r"%A:'abc\xe9\uabcd\U0010ffff'", check_format(r"%A:'abc\xe9\uabcd\U0010ffff'",
b'%%A:%A', 'abc\xe9\uabcd\U0010ffff') b'%%A:%A', 'abc\xe9\uabcd\U0010ffff')
# test %T (object type name)
check_format(r"type name: str",
b'type name: %T', 'text')
# test %V # test %V
check_format('repr=abc', check_format('repr=abc',
b'repr=%V', 'abc', b'xyz') b'repr=%V', 'abc', b'xyz')
......
:c:func:`PyUnicode_FromFormatV`: add ``%T`` format to
:c:func:`PyUnicode_FromFormatV`, and so to :c:func:`PyUnicode_FromFormat`
and :c:func:`PyErr_Format`, to format an object type name: equivalent to
"%s" with ``Py_TYPE(obj)->tp_name``.
...@@ -768,8 +768,7 @@ ensure_unicode(PyObject *obj) ...@@ -768,8 +768,7 @@ ensure_unicode(PyObject *obj)
{ {
if (!PyUnicode_Check(obj)) { if (!PyUnicode_Check(obj)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"must be str, not %.100s", "must be str, not %T", obj);
Py_TYPE(obj)->tp_name);
return -1; return -1;
} }
return PyUnicode_READY(obj); return PyUnicode_READY(obj);
...@@ -2530,7 +2529,7 @@ unicode_fromformat_write_str(_PyUnicodeWriter *writer, PyObject *str, ...@@ -2530,7 +2529,7 @@ unicode_fromformat_write_str(_PyUnicodeWriter *writer, PyObject *str,
} }
static int static int
unicode_fromformat_write_cstr(_PyUnicodeWriter *writer, const char *str, unicode_fromformat_write_utf8(_PyUnicodeWriter *writer, const char *str,
Py_ssize_t width, Py_ssize_t precision) Py_ssize_t width, Py_ssize_t precision)
{ {
/* UTF-8 */ /* UTF-8 */
...@@ -2747,7 +2746,7 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer, ...@@ -2747,7 +2746,7 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
{ {
/* UTF-8 */ /* UTF-8 */
const char *s = va_arg(*vargs, const char*); const char *s = va_arg(*vargs, const char*);
if (unicode_fromformat_write_cstr(writer, s, width, precision) < 0) if (unicode_fromformat_write_utf8(writer, s, width, precision) < 0)
return NULL; return NULL;
break; break;
} }
...@@ -2773,7 +2772,7 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer, ...@@ -2773,7 +2772,7 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
} }
else { else {
assert(str != NULL); assert(str != NULL);
if (unicode_fromformat_write_cstr(writer, str, width, precision) < 0) if (unicode_fromformat_write_utf8(writer, str, width, precision) < 0)
return NULL; return NULL;
} }
break; break;
...@@ -2827,6 +2826,17 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer, ...@@ -2827,6 +2826,17 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
break; break;
} }
case 'T':
{
/* Object type name (tp_name) */
PyObject *obj = va_arg(*vargs, PyObject *);
PyTypeObject *type = Py_TYPE(obj);
const char *type_name = type->tp_name;
if (unicode_fromformat_write_utf8(writer, type_name, -1, -1) < 0) {
return NULL;
}
break;
}
case '%': case '%':
if (_PyUnicodeWriter_WriteCharInline(writer, '%') < 0) if (_PyUnicodeWriter_WriteCharInline(writer, '%') < 0)
return NULL; return NULL;
...@@ -3024,8 +3034,7 @@ PyUnicode_FromObject(PyObject *obj) ...@@ -3024,8 +3034,7 @@ PyUnicode_FromObject(PyObject *obj)
return _PyUnicode_Copy(obj); return _PyUnicode_Copy(obj);
} }
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"Can't convert '%.100s' object to str implicitly", "Can't convert '%T' object to str implicitly", obj);
Py_TYPE(obj)->tp_name);
return NULL; return NULL;
} }
...@@ -3061,8 +3070,8 @@ PyUnicode_FromEncodedObject(PyObject *obj, ...@@ -3061,8 +3070,8 @@ PyUnicode_FromEncodedObject(PyObject *obj,
/* Retrieve a bytes buffer view through the PEP 3118 buffer interface */ /* Retrieve a bytes buffer view through the PEP 3118 buffer interface */
if (PyObject_GetBuffer(obj, &buffer, PyBUF_SIMPLE) < 0) { if (PyObject_GetBuffer(obj, &buffer, PyBUF_SIMPLE) < 0) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"decoding to str: need a bytes-like object, %.80s found", "decoding to str: need a bytes-like object, %T found",
Py_TYPE(obj)->tp_name); obj);
return NULL; return NULL;
} }
...@@ -3192,10 +3201,9 @@ PyUnicode_Decode(const char *s, ...@@ -3192,10 +3201,9 @@ PyUnicode_Decode(const char *s,
goto onError; goto onError;
if (!PyUnicode_Check(unicode)) { if (!PyUnicode_Check(unicode)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"'%.400s' decoder returned '%.400s' instead of 'str'; " "'%.400s' decoder returned '%T' instead of 'str'; "
"use codecs.decode() to decode to arbitrary types", "use codecs.decode() to decode to arbitrary types",
encoding, encoding, unicode);
Py_TYPE(unicode)->tp_name);
Py_DECREF(unicode); Py_DECREF(unicode);
goto onError; goto onError;
} }
...@@ -3255,10 +3263,9 @@ PyUnicode_AsDecodedUnicode(PyObject *unicode, ...@@ -3255,10 +3263,9 @@ PyUnicode_AsDecodedUnicode(PyObject *unicode,
goto onError; goto onError;
if (!PyUnicode_Check(v)) { if (!PyUnicode_Check(v)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"'%.400s' decoder returned '%.400s' instead of 'str'; " "'%.400s' decoder returned '%T' instead of 'str'; "
"use codecs.decode() to decode to arbitrary types", "use codecs.decode() to decode to arbitrary types",
encoding, encoding, unicode);
Py_TYPE(unicode)->tp_name);
Py_DECREF(v); Py_DECREF(v);
goto onError; goto onError;
} }
...@@ -3489,10 +3496,9 @@ PyUnicode_AsEncodedString(PyObject *unicode, ...@@ -3489,10 +3496,9 @@ PyUnicode_AsEncodedString(PyObject *unicode,
} }
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"'%.400s' encoder returned '%.400s' instead of 'bytes'; " "'%.400s' encoder returned '%T' instead of 'bytes'; "
"use codecs.encode() to encode to arbitrary types", "use codecs.encode() to encode to arbitrary types",
encoding, encoding, v);
Py_TYPE(v)->tp_name);
Py_DECREF(v); Py_DECREF(v);
return NULL; return NULL;
} }
...@@ -3523,10 +3529,9 @@ PyUnicode_AsEncodedUnicode(PyObject *unicode, ...@@ -3523,10 +3529,9 @@ PyUnicode_AsEncodedUnicode(PyObject *unicode,
goto onError; goto onError;
if (!PyUnicode_Check(v)) { if (!PyUnicode_Check(v)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"'%.400s' encoder returned '%.400s' instead of 'str'; " "'%.400s' encoder returned '%T' instead of 'str'; "
"use codecs.encode() to encode to arbitrary types", "use codecs.encode() to encode to arbitrary types",
encoding, encoding, v);
Py_TYPE(v)->tp_name);
Py_DECREF(v); Py_DECREF(v);
goto onError; goto onError;
} }
...@@ -3698,8 +3703,10 @@ PyUnicode_FSDecoder(PyObject* arg, void* addr) ...@@ -3698,8 +3703,10 @@ PyUnicode_FSDecoder(PyObject* arg, void* addr)
if (!PyBytes_Check(path) && if (!PyBytes_Check(path) &&
PyErr_WarnFormat(PyExc_DeprecationWarning, 1, PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
"path should be string, bytes, or os.PathLike, not %.200s", "path should be string, bytes, "
Py_TYPE(arg)->tp_name)) { "or os.PathLike, not %T",
arg))
{
Py_DECREF(path); Py_DECREF(path);
return 0; return 0;
} }
...@@ -3717,8 +3724,8 @@ PyUnicode_FSDecoder(PyObject* arg, void* addr) ...@@ -3717,8 +3724,8 @@ PyUnicode_FSDecoder(PyObject* arg, void* addr)
} }
else { else {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"path should be string, bytes, or os.PathLike, not %.200s", "path should be string, bytes, or os.PathLike, not %T",
Py_TYPE(arg)->tp_name); arg);
Py_DECREF(path); Py_DECREF(path);
return 0; return 0;
} }
...@@ -9886,9 +9893,8 @@ _PyUnicode_JoinArray(PyObject *separator, PyObject *const *items, Py_ssize_t seq ...@@ -9886,9 +9893,8 @@ _PyUnicode_JoinArray(PyObject *separator, PyObject *const *items, Py_ssize_t seq
else { else {
if (!PyUnicode_Check(separator)) { if (!PyUnicode_Check(separator)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"separator: expected str instance," "separator: expected str instance, %T found",
" %.80s found", separator);
Py_TYPE(separator)->tp_name);
goto onError; goto onError;
} }
if (PyUnicode_READY(separator)) if (PyUnicode_READY(separator))
...@@ -9919,9 +9925,8 @@ _PyUnicode_JoinArray(PyObject *separator, PyObject *const *items, Py_ssize_t seq ...@@ -9919,9 +9925,8 @@ _PyUnicode_JoinArray(PyObject *separator, PyObject *const *items, Py_ssize_t seq
item = items[i]; item = items[i];
if (!PyUnicode_Check(item)) { if (!PyUnicode_Check(item)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"sequence item %zd: expected str instance," "sequence item %zd: expected str instance, %T found",
" %.80s found", i, item);
i, Py_TYPE(item)->tp_name);
goto onError; goto onError;
} }
if (PyUnicode_READY(item) == -1) if (PyUnicode_READY(item) == -1)
...@@ -10736,7 +10741,7 @@ convert_uc(PyObject *obj, void *addr) ...@@ -10736,7 +10741,7 @@ convert_uc(PyObject *obj, void *addr)
if (!PyUnicode_Check(obj)) { if (!PyUnicode_Check(obj)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"The fill character must be a unicode character, " "The fill character must be a unicode character, "
"not %.100s", Py_TYPE(obj)->tp_name); "not %T", obj);
return 0; return 0;
} }
if (PyUnicode_READY(obj) < 0) if (PyUnicode_READY(obj) < 0)
...@@ -11142,8 +11147,8 @@ PyUnicode_Contains(PyObject *str, PyObject *substr) ...@@ -11142,8 +11147,8 @@ PyUnicode_Contains(PyObject *str, PyObject *substr)
if (!PyUnicode_Check(substr)) { if (!PyUnicode_Check(substr)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"'in <string>' requires string as left operand, not %.100s", "'in <string>' requires string as left operand, not %T",
Py_TYPE(substr)->tp_name); substr);
return -1; return -1;
} }
if (PyUnicode_READY(substr) == -1) if (PyUnicode_READY(substr) == -1)
...@@ -12848,9 +12853,7 @@ unicode_split_impl(PyObject *self, PyObject *sep, Py_ssize_t maxsplit) ...@@ -12848,9 +12853,7 @@ unicode_split_impl(PyObject *self, PyObject *sep, Py_ssize_t maxsplit)
if (PyUnicode_Check(sep)) if (PyUnicode_Check(sep))
return split(self, sep, maxsplit); return split(self, sep, maxsplit);
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError, "must be str or None, not %T", sep);
"must be str or None, not %.100s",
Py_TYPE(sep)->tp_name);
return NULL; return NULL;
} }
...@@ -13036,9 +13039,7 @@ unicode_rsplit_impl(PyObject *self, PyObject *sep, Py_ssize_t maxsplit) ...@@ -13036,9 +13039,7 @@ unicode_rsplit_impl(PyObject *self, PyObject *sep, Py_ssize_t maxsplit)
if (PyUnicode_Check(sep)) if (PyUnicode_Check(sep))
return rsplit(self, sep, maxsplit); return rsplit(self, sep, maxsplit);
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError, "must be str or None, not %T", sep);
"must be str or None, not %.100s",
Py_TYPE(sep)->tp_name);
return NULL; return NULL;
} }
...@@ -13333,8 +13334,8 @@ unicode_startswith(PyObject *self, ...@@ -13333,8 +13334,8 @@ unicode_startswith(PyObject *self,
if (!PyUnicode_Check(substring)) { if (!PyUnicode_Check(substring)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"tuple for startswith must only contain str, " "tuple for startswith must only contain str, "
"not %.100s", "not %T",
Py_TYPE(substring)->tp_name); substring);
return NULL; return NULL;
} }
result = tailmatch(self, substring, start, end, -1); result = tailmatch(self, substring, start, end, -1);
...@@ -13350,7 +13351,7 @@ unicode_startswith(PyObject *self, ...@@ -13350,7 +13351,7 @@ unicode_startswith(PyObject *self,
if (!PyUnicode_Check(subobj)) { if (!PyUnicode_Check(subobj)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"startswith first arg must be str or " "startswith first arg must be str or "
"a tuple of str, not %.100s", Py_TYPE(subobj)->tp_name); "a tuple of str, not %T", subobj);
return NULL; return NULL;
} }
result = tailmatch(self, subobj, start, end, -1); result = tailmatch(self, subobj, start, end, -1);
...@@ -13387,8 +13388,8 @@ unicode_endswith(PyObject *self, ...@@ -13387,8 +13388,8 @@ unicode_endswith(PyObject *self,
if (!PyUnicode_Check(substring)) { if (!PyUnicode_Check(substring)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"tuple for endswith must only contain str, " "tuple for endswith must only contain str, "
"not %.100s", "not %T",
Py_TYPE(substring)->tp_name); substring);
return NULL; return NULL;
} }
result = tailmatch(self, substring, start, end, +1); result = tailmatch(self, substring, start, end, +1);
...@@ -13403,7 +13404,7 @@ unicode_endswith(PyObject *self, ...@@ -13403,7 +13404,7 @@ unicode_endswith(PyObject *self,
if (!PyUnicode_Check(subobj)) { if (!PyUnicode_Check(subobj)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"endswith first arg must be str or " "endswith first arg must be str or "
"a tuple of str, not %.100s", Py_TYPE(subobj)->tp_name); "a tuple of str, not %T", subobj);
return NULL; return NULL;
} }
result = tailmatch(self, subobj, start, end, +1); result = tailmatch(self, subobj, start, end, +1);
...@@ -14313,15 +14314,13 @@ wrongtype: ...@@ -14313,15 +14314,13 @@ wrongtype:
case 'x': case 'x':
case 'X': case 'X':
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"%%%c format: an integer is required, " "%%%c format: an integer is required, not %T",
"not %.200s", type, v);
type, Py_TYPE(v)->tp_name);
break; break;
default: default:
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"%%%c format: a number is required, " "%%%c format: a number is required, not %T",
"not %.200s", type, v);
type, Py_TYPE(v)->tp_name);
break; break;
} }
return -1; return -1;
......
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