summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Include/object.h1
-rw-r--r--Lib/test/test_unicode.py4
-rw-r--r--Misc/NEWS4
-rw-r--r--Objects/object.c38
-rw-r--r--Objects/stringobject.c12
5 files changed, 42 insertions, 17 deletions
diff --git a/Include/object.h b/Include/object.h
index fd7c235..15fee96 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -371,6 +371,7 @@ PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *);
PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int);
PyAPI_FUNC(void) _PyObject_Dump(PyObject *);
PyAPI_FUNC(PyObject *) PyObject_Repr(PyObject *);
+PyAPI_FUNC(PyObject *) _PyObject_Str(PyObject *);
PyAPI_FUNC(PyObject *) PyObject_Str(PyObject *);
#ifdef Py_USING_UNICODE
PyAPI_FUNC(PyObject *) PyObject_Unicode(PyObject *);
diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py
index 80242d5..d85f171 100644
--- a/Lib/test/test_unicode.py
+++ b/Lib/test/test_unicode.py
@@ -388,6 +388,10 @@ class UnicodeTest(
self.assertEqual('%i %*.*s' % (10, 5,3,u'abc',), u'10 abc')
self.assertEqual('%i%s %*.*s' % (10, 3, 5, 3, u'abc',), u'103 abc')
self.assertEqual('%c' % u'a', u'a')
+ class Wrapper:
+ def __str__(self):
+ return u'\u1234'
+ self.assertEqual('%s' % Wrapper(), u'\u1234')
def test_constructor(self):
# unicode(obj) tests (this maps to PyObject_Unicode() at C level)
diff --git a/Misc/NEWS b/Misc/NEWS
index a1f15ab..ecbbd97 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -118,6 +118,10 @@ Core and builtins
positions. It once again reports a syntax error if a future
statement occurs after anything other than a doc string.
+- Change the %s format specifier for str objects so that it returns a
+ unicode instance if the argument is not an instance of basestring and
+ calling __str__ on the argument returns a unicode instance.
+
Extension Modules
-----------------
diff --git a/Objects/object.c b/Objects/object.c
index 975c967..1895697 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -331,22 +331,48 @@ PyObject_Repr(PyObject *v)
}
PyObject *
-PyObject_Str(PyObject *v)
+_PyObject_Str(PyObject *v)
{
PyObject *res;
-
+ int type_ok;
if (v == NULL)
return PyString_FromString("<NULL>");
if (PyString_CheckExact(v)) {
Py_INCREF(v);
return v;
}
+#ifdef Py_USING_UNICODE
+ if (PyUnicode_CheckExact(v)) {
+ Py_INCREF(v);
+ return v;
+ }
+#endif
if (v->ob_type->tp_str == NULL)
return PyObject_Repr(v);
res = (*v->ob_type->tp_str)(v);
if (res == NULL)
return NULL;
+ type_ok = PyString_Check(res);
+#ifdef Py_USING_UNICODE
+ type_ok = type_ok || PyUnicode_Check(res);
+#endif
+ if (!type_ok) {
+ PyErr_Format(PyExc_TypeError,
+ "__str__ returned non-string (type %.200s)",
+ res->ob_type->tp_name);
+ Py_DECREF(res);
+ return NULL;
+ }
+ return res;
+}
+
+PyObject *
+PyObject_Str(PyObject *v)
+{
+ PyObject *res = _PyObject_Str(v);
+ if (res == NULL)
+ return NULL;
#ifdef Py_USING_UNICODE
if (PyUnicode_Check(res)) {
PyObject* str;
@@ -358,13 +384,7 @@ PyObject_Str(PyObject *v)
return NULL;
}
#endif
- if (!PyString_Check(res)) {
- PyErr_Format(PyExc_TypeError,
- "__str__ returned non-string (type %.200s)",
- res->ob_type->tp_name);
- Py_DECREF(res);
- return NULL;
- }
+ assert(PyString_Check(res));
return res;
}
diff --git a/Objects/stringobject.c b/Objects/stringobject.c
index 8a9dc52..9bcae0f 100644
--- a/Objects/stringobject.c
+++ b/Objects/stringobject.c
@@ -3853,7 +3853,6 @@ formatchar(char *buf, size_t buflen, PyObject *v)
return 1;
}
-
/* fmt%(v1,v2,...) is roughly equivalent to sprintf(fmt, v1, v2, ...)
FORMATBUFLEN is the length of the buffer in which the floats, ints, &
@@ -4079,7 +4078,9 @@ PyString_Format(PyObject *format, PyObject *args)
break;
case 's':
#ifdef Py_USING_UNICODE
- if (PyUnicode_Check(v)) {
+ temp = _PyObject_Str(v);
+ if (temp != NULL && PyUnicode_Check(temp)) {
+ Py_DECREF(temp);
fmt = fmt_start;
argidx = argidx_start;
goto unicode;
@@ -4087,16 +4088,11 @@ PyString_Format(PyObject *format, PyObject *args)
#endif
/* Fall through */
case 'r':
- if (c == 's')
- temp = PyObject_Str(v);
- else
+ if (c == 'r')
temp = PyObject_Repr(v);
if (temp == NULL)
goto error;
if (!PyString_Check(temp)) {
- /* XXX Note: this should never happen,
- since PyObject_Repr() and
- PyObject_Str() assure this */
PyErr_SetString(PyExc_TypeError,
"%s argument has non-string str()");
Py_DECREF(temp);