diff -r 78320ba69644 Lib/test/test_StringIO_jy.py --- a/Lib/test/test_StringIO_jy.py Fri Feb 12 22:40:12 2016 -0700 +++ b/Lib/test/test_StringIO_jy.py Mon Feb 15 11:48:45 2016 -0700 @@ -2,6 +2,9 @@ import cStringIO from test import test_support +from array import array +from java.nio import ByteBuffer + class TestUnicodeInput(unittest.TestCase): def test_differences_handling_unicode(self): # Test for the "feature" described on #1089. @@ -53,10 +56,57 @@ else: self.fail("cStringIO.StringIO: getvalue() after close() should have raised ValueError") + +class TestBytearray(unittest.TestCase): + # Verify fix for http://bugs.jython.org/issue2429 + # + # cStringIO currently uses an older style of implementation that + # does not use exposed types. Until this is refactored in Jython, + # need to account for using string-like objects with it. + def test_bytearray(self): + f = cStringIO.StringIO(bytearray("initial-value")) + self.assertEqual(f.getvalue(), "initial-value") + f.write(bytearray("initial-write")) + self.assertEqual(f.getvalue(), "initial-write") # No memory of initial-value, due to pos semantics + f.write(bytearray("+more-data")) + self.assertEqual(f.getvalue(), "initial-write+more-data") + f.write("+regular-string") + self.assertEqual(f.getvalue(), "initial-write+more-data+regular-string") + + +class TestArray(unittest.TestCase): + + def test_array_from_bytebuffer(self): + """Verify byte[] arrays are properly written as bytes""" + def get_array(data): + buf = ByteBuffer.allocate(len(data)) + buf.put(array("b", data)) + buf.flip() + return buf.array() + + f = cStringIO.StringIO(get_array("initial-value\xFF")) + self.assertEqual(f.getvalue(), "initial-value\xFF") + f.write(get_array("initial-write\x00")) + self.assertEqual(f.getvalue(), "initial-write\x00") + f.write(get_array("+more-data")) + self.assertEqual(f.getvalue(), "initial-write\x00+more-data") + + def test_int_array(self): + """Verify array.array is converted to bytes using tostring method""" + f = cStringIO.StringIO(array("i", xrange(5))) + self.assertEqual(f.getvalue(), array("i", xrange(5)).tostring()) + f.write(array("i", xrange(0, 10, 2))) + self.assertEqual(f.getvalue(), array("i", xrange(0, 10, 2)).tostring()) + + def test_main(): - test_support.run_unittest(TestUnicodeInput) - test_support.run_unittest(TestWrite) - test_support.run_unittest(TestGetValueAfterClose) + test_support.run_unittest( + TestUnicodeInput, + TestWrite, + TestGetValueAfterClose, + TestBytearray, + TestArray + ) if __name__ == '__main__': test_main() diff -r 78320ba69644 src/org/python/modules/cStringIO.java --- a/src/org/python/modules/cStringIO.java Fri Feb 12 22:40:12 2016 -0700 +++ b/src/org/python/modules/cStringIO.java Mon Feb 15 11:48:45 2016 -0700 @@ -11,6 +11,8 @@ package org.python.modules; +import org.python.core.BaseBytes; +import org.python.core.BufferProtocol; import org.python.core.Py; import org.python.core.PyArray; import org.python.core.PyIterator; @@ -36,6 +38,12 @@ public static final int SEEK_END = 2; } + /*NOTE CPython implements a more restricted model of StringI and StringO, + * depending on how cStringIO.StringIO is constructed. This class should be a + * superset of such functionality. (In part this works the same because pos=0 + * regardless of initialization.) + */ + public static PyType InputType = PyType.fromClass(StringIO.class); public static PyType OutputType = PyType.fromClass(StringIO.class); @@ -43,6 +51,10 @@ return new StringIO(); } + public static StringIO StringIO(StringBuilder builder) { + return new StringIO(builder); + } + /** * Create a StringIO object, initialized by the value. * @param buffer The initial value. @@ -61,6 +73,7 @@ return new StringIO(array); } + public static StringIO StringIO(BaseBytes array) { return new StringIO(array); } /** * The StringIO object @@ -74,17 +87,33 @@ private final StringBuilder buf; + private static StringBuilder toStringBuilder(byte[] array) { + StringBuilder buf = new StringBuilder(); + for (byte b : array) { + buf.append(b); + } + System.err.println("Buffer:" + buf); + return buf; + } + public StringIO() { buf = new StringBuilder(); } + public StringIO(StringBuilder buf) { + this.buf = buf; + } public StringIO(String buffer) { - buf = new StringBuilder(buffer); + this(new StringBuilder(buffer)); } public StringIO(PyArray array) { - buf = new StringBuilder(array.tostring()); + this(array.tostring()); + } + + public StringIO(BaseBytes array) { + this(new StringBuilder(array.asString())); } private void _complain_ifclosed() { @@ -266,7 +295,7 @@ /** * Read and return a line without the trailing newline. - * Usind by cPickle as an optimization. + * Used by cPickle as an optimization. */ public synchronized PyString readlineNoNl() { _complain_ifclosed(); @@ -343,6 +372,16 @@ write(obj.toString()); } + public void write(BaseBytes obj) { + write(obj.asString()); + } + + public void write(PyArray obj) { + // NOTE the very important capitalization difference! - in Python 3, this is renamed to tobytes, + // In either case, we want bytes, not a __str__ representation of the array + write(obj.tostring()); + } + public synchronized void write(String s) { _complain_ifclosed();