Index: src/org/python/core/PyString.java =================================================================== --- src/org/python/core/PyString.java (revision 3133) +++ src/org/python/core/PyString.java (working copy) @@ -2045,7 +2045,7 @@ public PyObject str___mod__(PyObject other){ StringFormatter fmt = new StringFormatter(string); - return createInstance(fmt.format(other)); + return fmt.format(other); } public PyObject __int__() { @@ -3535,7 +3535,7 @@ } - public String formatLong(PyString arg, char type, boolean altFlag) { + private String formatLong(PyString arg, char type, boolean altFlag) { checkPrecision("long"); String s = arg.toString(); int end = s.length(); @@ -3589,11 +3589,11 @@ return s; } - public String formatInteger(PyObject arg, int radix, boolean unsigned) { + private String formatInteger(PyObject arg, int radix, boolean unsigned) { return formatInteger(((PyInteger)arg.__int__()).getValue(), radix, unsigned); } - public String formatInteger(long v, int radix, boolean unsigned) { + private String formatInteger(long v, int radix, boolean unsigned) { checkPrecision("integer"); if (unsigned) { if (v < 0) @@ -3611,11 +3611,11 @@ return s; } - public String formatFloatDecimal(PyObject arg, boolean truncate) { + private String formatFloatDecimal(PyObject arg, boolean truncate) { return formatFloatDecimal(arg.__float__().getValue(), truncate); } - public String formatFloatDecimal(double v, boolean truncate) { + private String formatFloatDecimal(double v, boolean truncate) { checkPrecision("decimal"); java.text.NumberFormat format = java.text.NumberFormat.getInstance( java.util.Locale.US); @@ -3638,7 +3638,7 @@ return ret; } - public String formatFloatExponential(PyObject arg, char e, + private String formatFloatExponential(PyObject arg, char e, boolean truncate) { StringBuffer buf = new StringBuffer(); @@ -3681,9 +3681,10 @@ return buf.toString(); } - public String format(PyObject args) { + public PyString format(PyObject args) { PyObject dict = null; this.args = args; + boolean needUnicode = false; if (args instanceof PyTuple) { argIndex = 0; } else { @@ -3786,6 +3787,9 @@ if (precision >= 0 && string.length() > precision) { string = string.substring(0, precision); } + if (arg instanceof PyUnicode) { + needUnicode = true; + } break; case 'i': case 'd': @@ -3877,6 +3881,9 @@ string = ((PyString)arg).toString(); if (string.length() != 1) throw Py.TypeError("%c requires int or char"); + if (arg instanceof PyUnicode) { + needUnicode = true; + } break; } char tmp = (char)((PyInteger)arg.__int__()).getValue(); @@ -3949,7 +3956,10 @@ { throw Py.TypeError("not all arguments converted"); } - return buffer.toString(); + if (needUnicode) { + return new PyUnicode(buffer.toString()); + } + return new PyString(buffer.toString()); } } Index: src/org/python/core/PyUnicode.java =================================================================== --- src/org/python/core/PyUnicode.java (revision 3133) +++ src/org/python/core/PyUnicode.java (working copy) @@ -1483,6 +1483,10 @@ return new PyUnicode(str); } + public PyObject __mod__(PyObject other) { + return str___mod__(other).__unicode__(); + } + final PyUnicode unicode___unicode__() { return str___unicode__(); } Index: Lib/test/test_str2unicode.py =================================================================== --- Lib/test/test_str2unicode.py (revision 3133) +++ Lib/test/test_str2unicode.py (working copy) @@ -1,19 +1,90 @@ import unittest + class TestStrReturnsUnicode(unittest.TestCase): + def test_join(self): - self.assertEquals(unicode, type(''.join([u'blah']))) + self.assertEquals(unicode, type(''.join([u'blah']))) def test_replace(self): - self.assertEquals(unicode, type('hello'.replace('o', u'o'))) + self.assertEquals(unicode, type('hello'.replace('o', u'o'))) + def test_string_formatting_s(self): + self.assertEquals(unicode, type('%s' % u'x')) + self.assertEquals(unicode, type('%s %s' % (u'x', 'y'))) + self.assertEquals(unicode, type('%(x)s' % {'x' : u'x'})) + def test_string_formatting_r(self): + self.assertEquals(unicode, type('%r' % u'x')) + self.assertEquals(unicode, type('%r %r' % (u'x', 'y'))) + self.assertEquals(unicode, type('%(x)r' % {'x' : u'x'})) + + def test_string_formatting_c(self): + self.assertEquals(unicode, type('%c' % u'x')) + self.assertEquals(unicode, type('%c %c' % (u'x', 'y'))) + self.assertEquals(unicode, type('%(x)c' % {'x' : u'x'})) + + class TestStrReturnsStr(unittest.TestCase): + def test_join(self): - self.assertEquals(str, type(''.join(['blah']))) + self.assertEquals(str, type(''.join(['blah']))) def test_replace(self): - self.assertEquals(str, type('hello'.replace('o', 'oo'))) + self.assertEquals(str, type('hello'.replace('o', 'oo'))) + def test_string_formatting_s(self): + self.assertEquals(str, type('%s' % 'x')) + self.assertEquals(str, type('%s %s' % ('x', 'y'))) + self.assertEquals(str, type('%(x)s' % {'x' : 'xxx'})) + + def test_string_formatting_r(self): + self.assertEquals(str, type('%r' % 'x')) + self.assertEquals(str, type('%r %r' % ('x', 'y'))) + self.assertEquals(str, type('%(x)r' % {'x' : 'x'})) + + def test_string_formatting_c(self): + self.assertEquals(str, type('%c' % 'x')) + self.assertEquals(str, type('%c %c' % ('x', 'y'))) + self.assertEquals(str, type('%(x)c' % {'x' : 'x'})) + + +class TestUnicodeReturnsUnicode(unittest.TestCase): + + def test_join(self): + self.assertEquals(unicode, type(u''.join([u'blah']))) + self.assertEquals(unicode, type(u''.join(['blah']))) + + def test_replace(self): + self.assertEquals(unicode, type(u'hello'.replace('o', u'o'))) + self.assertEquals(unicode, type(u'hello'.replace(u'o', 'o'))) + self.assertEquals(unicode, type(u'hello'.replace(u'o', u'o'))) + self.assertEquals(unicode, type(u'hello'.replace('o', 'o'))) + + def test_string_formatting_s(self): + self.assertEquals(unicode, type(u'%s' % u'x')) + self.assertEquals(unicode, type(u'%s' % 'x')) + self.assertEquals(unicode, type(u'%s %s' % (u'x', 'y'))) + self.assertEquals(unicode, type(u'%s %s' % ('x', 'y'))) + self.assertEquals(unicode, type(u'%(x)s' % {'x' : u'x'})) + self.assertEquals(unicode, type(u'%(x)s' % {'x' : 'x'})) + + def test_string_formatting_r(self): + self.assertEquals(unicode, type(u'%r' % u'x')) + self.assertEquals(unicode, type(u'%r' % 'x')) + self.assertEquals(unicode, type(u'%r %r' % (u'x', 'y'))) + self.assertEquals(unicode, type(u'%r %r' % ('x', 'y'))) + self.assertEquals(unicode, type(u'%(x)r' % {'x' : u'x'})) + self.assertEquals(unicode, type(u'%(x)r' % {'x' : 'x'})) + + def test_string_formatting_c(self): + self.assertEquals(unicode, type(u'%c' % u'x')) + self.assertEquals(unicode, type(u'%c' % 'x')) + self.assertEquals(unicode, type(u'%c %c' % (u'x', 'y'))) + self.assertEquals(unicode, type(u'%c %c' % ('x', 'y'))) + self.assertEquals(unicode, type(u'%(x)c' % {'x' : u'x'})) + self.assertEquals(unicode, type(u'%(x)c' % {'x' : 'x'})) + + if __name__ == '__main__': unittest.main()