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.
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(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 @@
         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 @@
   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 *
-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 @@
 		    	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 @@
 	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 @@
 				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 @@
 #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);