From: <pj...@us...> - 2007-11-16 21:41:21
|
Revision: 3682 http://jython.svn.sourceforge.net/jython/?rev=3682&view=rev Author: pjenvey Date: 2007-11-16 13:41:19 -0800 (Fri, 16 Nov 2007) Log Message: ----------- Merged revisions 3649-3651,3653-3681 via svnmerge from https://jython.svn.sourceforge.net/svnroot/jython/branches/pyfile-nio rewrite of PyFile. most of its innards now live under the new org.python.core.io package, which is loosely based off of py3k's new io (PEP 3116): consits of 3 layers: o raw io (RawIOBase) - deals in nio Channels (mostly) and ByteBuffers o buffered io (BufferedIOBase) - buffers bytes to/from raw ios o text io (TextIOBase) - deals in Strings, via buffered ios features: o improved performance for larger files, especially for readline and 'r' (on CRLF platforms) and 'U' modes o full CPython-like buffering support (variable sized buffering, line buffering) o line buffered stdout, and support for jython -u to disable o thread safe PyFile o support for readinto, and isatty (only returns true for stdin/out/err) o fixes file.softspace o a modified 2.5.1 test_file now passes. test_dumbdbm also now passes and I don't know why also: o kill PythonInterpreter.setOut/Err(Writer) and the associated Writer backed PyFile support Modified Paths: -------------- trunk/jython/Lib/test/regrtest.py trunk/jython/Lib/test/test_file_newlines.py trunk/jython/src/org/python/core/Options.java trunk/jython/src/org/python/core/Py.java trunk/jython/src/org/python/core/PyArray.java trunk/jython/src/org/python/core/PyFile.java trunk/jython/src/org/python/core/PyString.java trunk/jython/src/org/python/core/PySystemState.java trunk/jython/src/org/python/core/StdoutWrapper.java trunk/jython/src/org/python/modules/cPickle.java trunk/jython/src/org/python/modules/imp.java trunk/jython/src/org/python/util/PythonInterpreter.java trunk/jython/src/org/python/util/jython.java trunk/jython/src/templates/file.expose Added Paths: ----------- trunk/jython/Lib/test/test_file.py trunk/jython/src/org/python/core/io/ trunk/jython/src/org/python/core/io/BinaryIOWrapper.java trunk/jython/src/org/python/core/io/BufferedIOBase.java trunk/jython/src/org/python/core/io/BufferedIOMixin.java trunk/jython/src/org/python/core/io/BufferedRandom.java trunk/jython/src/org/python/core/io/BufferedReader.java trunk/jython/src/org/python/core/io/BufferedWriter.java trunk/jython/src/org/python/core/io/DatagramSocketIO.java trunk/jython/src/org/python/core/io/FileIO.java trunk/jython/src/org/python/core/io/IOBase.java trunk/jython/src/org/python/core/io/LineBufferedRandom.java trunk/jython/src/org/python/core/io/LineBufferedWriter.java trunk/jython/src/org/python/core/io/RawIOBase.java trunk/jython/src/org/python/core/io/ServerSocketIO.java trunk/jython/src/org/python/core/io/SocketIO.java trunk/jython/src/org/python/core/io/SocketIOBase.java trunk/jython/src/org/python/core/io/StreamIO.java trunk/jython/src/org/python/core/io/TextIOBase.java trunk/jython/src/org/python/core/io/TextIOWrapper.java trunk/jython/src/org/python/core/io/UniversalIOWrapper.java Removed Paths: ------------- trunk/jython/src/org/python/core/io/BinaryIOWrapper.java trunk/jython/src/org/python/core/io/BufferedIOBase.java trunk/jython/src/org/python/core/io/BufferedIOMixin.java trunk/jython/src/org/python/core/io/BufferedRandom.java trunk/jython/src/org/python/core/io/BufferedReader.java trunk/jython/src/org/python/core/io/BufferedWriter.java trunk/jython/src/org/python/core/io/FileIO.java trunk/jython/src/org/python/core/io/IOBase.java trunk/jython/src/org/python/core/io/LineBufferedRandom.java trunk/jython/src/org/python/core/io/LineBufferedWriter.java trunk/jython/src/org/python/core/io/RawIOBase.java trunk/jython/src/org/python/core/io/SocketIO.java trunk/jython/src/org/python/core/io/StreamIO.java trunk/jython/src/org/python/core/io/TextIOBase.java trunk/jython/src/org/python/core/io/TextIOWrapper.java trunk/jython/src/org/python/core/io/UniversalIOWrapper.java Property Changed: ---------------- trunk/jython/ Property changes on: trunk/jython ___________________________________________________________________ Name: svnmerge-integrated - /branches/pyfile-nio:1-3648,3652 + /branches/pyfile-nio:1-3681 Modified: trunk/jython/Lib/test/regrtest.py =================================================================== --- trunk/jython/Lib/test/regrtest.py 2007-11-16 19:49:34 UTC (rev 3681) +++ trunk/jython/Lib/test/regrtest.py 2007-11-16 21:41:19 UTC (rev 3682) @@ -1035,9 +1035,7 @@ test_dis test_descr test_descrtut - test_dumbdbm test_eof - test_file test_frozen test_hexoct test_inspect Copied: trunk/jython/Lib/test/test_file.py (from rev 3651, branches/pyfile-nio/Lib/test/test_file.py) =================================================================== --- trunk/jython/Lib/test/test_file.py (rev 0) +++ trunk/jython/Lib/test/test_file.py 2007-11-16 21:41:19 UTC (rev 3682) @@ -0,0 +1,358 @@ +# From CPython 2.5.1 +import sys +import os +import unittest +from array import array +from weakref import proxy + +from test.test_support import TESTFN, findfile, run_unittest +from UserList import UserList + +class AutoFileTests(unittest.TestCase): + # file tests for which a test file is automatically set up + + def setUp(self): + self.f = open(TESTFN, 'wb') + + def tearDown(self): + if self.f: + self.f.close() + os.remove(TESTFN) + + # XXX: assumes CPython style garbage collection + def _testWeakRefs(self): + # verify weak references + p = proxy(self.f) + p.write('teststring') + self.assertEquals(self.f.tell(), p.tell()) + self.f.close() + self.f = None + self.assertRaises(ReferenceError, getattr, p, 'tell') + + def testAttributes(self): + # verify expected attributes exist + f = self.f + softspace = f.softspace + f.name # merely shouldn't blow up + f.mode # ditto + f.closed # ditto + + # verify softspace is writable + f.softspace = softspace # merely shouldn't blow up + + # verify the others aren't + for attr in 'name', 'mode', 'closed': + self.assertRaises((AttributeError, TypeError), setattr, f, attr, 'oops') + + def testReadinto(self): + # verify readinto + self.f.write('12') + self.f.close() + a = array('c', 'x'*10) + self.f = open(TESTFN, 'rb') + n = self.f.readinto(a) + self.assertEquals('12', a.tostring()[:n]) + + def testWritelinesUserList(self): + # verify writelines with instance sequence + l = UserList(['1', '2']) + self.f.writelines(l) + self.f.close() + self.f = open(TESTFN, 'rb') + buf = self.f.read() + self.assertEquals(buf, '12') + + def testWritelinesIntegers(self): + # verify writelines with integers + self.assertRaises(TypeError, self.f.writelines, [1, 2, 3]) + + def testWritelinesIntegersUserList(self): + # verify writelines with integers in UserList + l = UserList([1,2,3]) + self.assertRaises(TypeError, self.f.writelines, l) + + def testWritelinesNonString(self): + # verify writelines with non-string object + class NonString: + pass + + self.assertRaises(TypeError, self.f.writelines, + [NonString(), NonString()]) + + def testRepr(self): + # verify repr works + self.assert_(repr(self.f).startswith("<open file '" + TESTFN)) + + def testErrors(self): + f = self.f + self.assertEquals(f.name, TESTFN) + # XXX: Jython doesn't support isatty + #self.assert_(not f.isatty()) + self.assert_(not f.closed) + + self.assertRaises(TypeError, f.readinto, "") + f.close() + self.assert_(f.closed) + + def testMethods(self): + # XXX: Jython file methods require valid arguments: closed file + # checks are done before parsing the arguments in CPython + #methods = ['next', 'read', 'readinto', + # 'readline', 'readlines', 'seek', 'tell', 'truncate', + # 'write', 'xreadlines', '__iter__'] + noarg = object() + # XXX: Jython doesn't support fileno or isatty + #methods = dict(fileno=noarg, flush=noarg, isatty=noarg, next=noarg, + methods = dict(flush=noarg, next=noarg, + read=-1, readinto=array('c', 'x'), readline=-1, + readlines=noarg, seek=0, tell=noarg, truncate=0, + write='x', xreadlines=noarg, __iter__=noarg) + if sys.platform.startswith('atheos'): + methods.remove('truncate') + + # __exit__ should close the file + # XXX: __exit__ is 2.5 specific + #self.f.__exit__(None, None, None) + self.f.close() + self.assert_(self.f.closed) + + for methodname, arg in methods.iteritems(): + method = getattr(self.f, methodname) + # should raise on closed file + if arg is noarg: + self.assertRaises(ValueError, method) + else: + self.assertRaises(ValueError, method, arg) + self.assertRaises(ValueError, self.f.writelines, []) + + # XXX: __exit__ is 2.5 specific + return + # file is closed, __exit__ shouldn't do anything + self.assertEquals(self.f.__exit__(None, None, None), None) + # it must also return None if an exception was given + try: + 1/0 + except: + self.assertEquals(self.f.__exit__(*sys.exc_info()), None) + + +class OtherFileTests(unittest.TestCase): + + def testModeStrings(self): + # check invalid mode strings + for mode in ("", "aU", "wU+"): + try: + f = open(TESTFN, mode) + except ValueError: + pass + else: + f.close() + self.fail('%r is an invalid file mode' % mode) + + # XXX: Jython's stdin can't seek, it's not backed by a + # RandomAccessFile + def _testStdin(self): + # This causes the interpreter to exit on OSF1 v5.1. + if sys.platform != 'osf1V5': + self.assertRaises(IOError, sys.stdin.seek, -1) + else: + print >>sys.__stdout__, ( + ' Skipping sys.stdin.seek(-1), it may crash the interpreter.' + ' Test manually.') + self.assertRaises(IOError, sys.stdin.truncate) + + def testUnicodeOpen(self): + # verify repr works for unicode too + f = open(unicode(TESTFN), "w") + self.assert_(repr(f).startswith("<open file u'" + TESTFN)) + f.close() + os.unlink(TESTFN) + + def testBadModeArgument(self): + # verify that we get a sensible error message for bad mode argument + bad_mode = "qwerty" + try: + f = open(TESTFN, bad_mode) + except ValueError, msg: + if msg[0] != 0: + s = str(msg) + if s.find(TESTFN) != -1 or s.find(bad_mode) == -1: + self.fail("bad error message for invalid mode: %s" % s) + # if msg[0] == 0, we're probably on Windows where there may be + # no obvious way to discover why open() failed. + else: + f.close() + self.fail("no error for invalid mode: %s" % bad_mode) + + def testSetBufferSize(self): + # make sure that explicitly setting the buffer size doesn't cause + # misbehaviour especially with repeated close() calls + for s in (-1, 0, 1, 512): + try: + f = open(TESTFN, 'w', s) + f.write(str(s)) + f.close() + f.close() + f = open(TESTFN, 'r', s) + d = int(f.read()) + f.close() + f.close() + except IOError, msg: + self.fail('error setting buffer size %d: %s' % (s, str(msg))) + self.assertEquals(d, s) + + def testTruncateOnWindows(self): + os.unlink(TESTFN) + + def bug801631(): + # SF bug <http://www.python.org/sf/801631> + # "file.truncate fault on windows" + f = open(TESTFN, 'wb') + f.write('12345678901') # 11 bytes + f.close() + + f = open(TESTFN,'rb+') + data = f.read(5) + if data != '12345': + self.fail("Read on file opened for update failed %r" % data) + if f.tell() != 5: + self.fail("File pos after read wrong %d" % f.tell()) + + f.truncate() + if f.tell() != 5: + self.fail("File pos after ftruncate wrong %d" % f.tell()) + + f.close() + size = os.path.getsize(TESTFN) + if size != 5: + self.fail("File size after ftruncate wrong %d" % size) + + try: + bug801631() + finally: + os.unlink(TESTFN) + + # XXX: Jython allows mixing reads with iteration + def _testIteration(self): + # Test the complex interaction when mixing file-iteration and the + # various read* methods. Ostensibly, the mixture could just be tested + # to work when it should work according to the Python language, + # instead of fail when it should fail according to the current CPython + # implementation. People don't always program Python the way they + # should, though, and the implemenation might change in subtle ways, + # so we explicitly test for errors, too; the test will just have to + # be updated when the implementation changes. + dataoffset = 16384 + filler = "ham\n" + assert not dataoffset % len(filler), \ + "dataoffset must be multiple of len(filler)" + nchunks = dataoffset // len(filler) + testlines = [ + "spam, spam and eggs\n", + "eggs, spam, ham and spam\n", + "saussages, spam, spam and eggs\n", + "spam, ham, spam and eggs\n", + "spam, spam, spam, spam, spam, ham, spam\n", + "wonderful spaaaaaam.\n" + ] + methods = [("readline", ()), ("read", ()), ("readlines", ()), + ("readinto", (array("c", " "*100),))] + + try: + # Prepare the testfile + bag = open(TESTFN, "w") + bag.write(filler * nchunks) + bag.writelines(testlines) + bag.close() + # Test for appropriate errors mixing read* and iteration + for methodname, args in methods: + f = open(TESTFN) + if f.next() != filler: + self.fail, "Broken testfile" + meth = getattr(f, methodname) + try: + meth(*args) + except ValueError: + pass + else: + self.fail("%s%r after next() didn't raise ValueError" % + (methodname, args)) + f.close() + + # Test to see if harmless (by accident) mixing of read* and + # iteration still works. This depends on the size of the internal + # iteration buffer (currently 8192,) but we can test it in a + # flexible manner. Each line in the bag o' ham is 4 bytes + # ("h", "a", "m", "\n"), so 4096 lines of that should get us + # exactly on the buffer boundary for any power-of-2 buffersize + # between 4 and 16384 (inclusive). + f = open(TESTFN) + for i in range(nchunks): + f.next() + testline = testlines.pop(0) + try: + line = f.readline() + except ValueError: + self.fail("readline() after next() with supposedly empty " + "iteration-buffer failed anyway") + if line != testline: + self.fail("readline() after next() with empty buffer " + "failed. Got %r, expected %r" % (line, testline)) + testline = testlines.pop(0) + buf = array("c", "\x00" * len(testline)) + try: + f.readinto(buf) + except ValueError: + self.fail("readinto() after next() with supposedly empty " + "iteration-buffer failed anyway") + line = buf.tostring() + if line != testline: + self.fail("readinto() after next() with empty buffer " + "failed. Got %r, expected %r" % (line, testline)) + + testline = testlines.pop(0) + try: + line = f.read(len(testline)) + except ValueError: + self.fail("read() after next() with supposedly empty " + "iteration-buffer failed anyway") + if line != testline: + self.fail("read() after next() with empty buffer " + "failed. Got %r, expected %r" % (line, testline)) + try: + lines = f.readlines() + except ValueError: + self.fail("readlines() after next() with supposedly empty " + "iteration-buffer failed anyway") + if lines != testlines: + self.fail("readlines() after next() with empty buffer " + "failed. Got %r, expected %r" % (line, testline)) + # Reading after iteration hit EOF shouldn't hurt either + f = open(TESTFN) + try: + for line in f: + pass + try: + f.readline() + f.readinto(buf) + f.read() + f.readlines() + except ValueError: + self.fail("read* failed after next() consumed file") + finally: + f.close() + finally: + os.unlink(TESTFN) + + +def test_main(): + # Historically, these tests have been sloppy about removing TESTFN. + # So get rid of it no matter what. + try: + run_unittest(AutoFileTests, OtherFileTests) + finally: + if os.path.exists(TESTFN): + os.unlink(TESTFN) + +if __name__ == '__main__': + test_main() Modified: trunk/jython/Lib/test/test_file_newlines.py =================================================================== --- trunk/jython/Lib/test/test_file_newlines.py 2007-11-16 19:49:34 UTC (rev 3681) +++ trunk/jython/Lib/test/test_file_newlines.py 2007-11-16 21:41:19 UTC (rev 3682) @@ -3,6 +3,7 @@ Made for Jython. """ import os +import sys import tempfile import test.test_support as test_support import unittest @@ -15,6 +16,12 @@ CRLF_TEST = 'CR\rLF\nCRLF\r\nEOF' +if sys.platform.startswith('java'): + from org.python.core.io import TextIOBase + READAHEAD_SIZE = TextIOBase.CHUNK_SIZE +else: + READAHEAD_SIZE = 300 + class BaseTestCase(unittest.TestCase): data = CRLF_TEST @@ -23,18 +30,20 @@ def setUp(self): self.filename = tempfile.mktemp() - fp = open(self.filename, self.write_mode) - fp.write(self.data) - fp.close() + self.write_fp = open(self.filename, self.write_mode) + self.write_fp.write(self.data) + self.write_fp.flush() self.fp = open(self.filename, self.mode) def tearDown(self): + if self.write_fp: + self.write_fp.close() if self.fp: self.fp.close() os.remove(self.filename) -class BinaryNewlinesTestCase(BaseTestCase): +class ReadBinaryNewlinesTestCase(BaseTestCase): mode = 'rb' @@ -44,62 +53,212 @@ self.fp.seek(0) read(self.fp, CRLF_TEST, len(CRLF_TEST)) + self.fp.seek(0) + read(self.fp, 'CR\r', 3) + read(self.fp, 'LF\n', 3) + read(self.fp, 'CRLF\r\n', 6) + read(self.fp, 'EOF', 3) + def test_binary_readline(self): readline(self.fp, 'CR\rLF\n') readline(self.fp, 'CRLF\r\n') readline(self.fp, 'EOF') + self.fp.seek(0) + readline(self.fp, 'CR\rLF\n', 6) + readline(self.fp, 'CRLF\r\n', 6) + readline(self.fp, 'EOF', 3) -class ReadTextNewlinesTestCase(BaseTestCase): - def test_text_read(self): - if LF: - read(self.fp, 'CR\rLF\nCRLF\r\nEOF') - elif CRLF: +if LF: + class ReadTextNewlinesTestCase(ReadBinaryNewlinesTestCase): + + mode = 'r' + +else: + class ReadTextNewlinesTestCase(BaseTestCase): + + def test_text_read(self): read(self.fp, 'CR\rLF\nCRLF\nEOF') - self.fp.seek(0) - read(self.fp, 'CR\r', 3) - read(self.fp, 'LF\n', 3) - if LF: - read(self.fp, 'CRLF\r\n', 6) - elif CRLF: + self.fp.seek(0) + read(self.fp, 'CR\rLF\nCRLF\nEOF', len('CR\rLF\nCRLF\nEOF')) + + self.fp.seek(0) + read(self.fp, 'CR\r', 3) + read(self.fp, 'LF\n', 3) read(self.fp, 'CRLF\n', 5) - read(self.fp, 'EOF', 3) + read(self.fp, 'EOF', 3) - def _test_text_readline(self): - readline(self.fp, 'CR\rLF\n') - if LF: - readline(self.fp, 'CRLF\r\n') - elif CRLF: + def test_text_readline(self): + readline(self.fp, 'CR\rLF\n') readline(self.fp, 'CRLF\n') - readline(self.fp, 'EOF') + readline(self.fp, 'EOF') - self.fp.seek(0) - readline(self.fp, 'CR\rLF\n') - if LF: - readline(self.fp, 'CRLF\r', 5) - readline(self.fp, '\n') - elif CRLF: + self.fp.seek(0) + readline(self.fp, 'CR\rLF\n', 6) readline(self.fp, 'CRLF\n', 5) - readline(self.fp, 'EOF') + readline(self.fp, 'EOF', 3) -class ReadTextBoundaryTestCase(BaseTestCase): +class ReadTextBasicBoundaryTestCase(BaseTestCase): data = 'CR\r' + read_data = 'CR\r' - def test_read_boundary(self): - read(self.fp, 'CR\r') + def test_read_basic_boundary(self): + read(self.fp, self.read_data) self.fp.seek(0) - read(self.fp, 'CR\r', 3) + read(self.fp, self.read_data, 3) + def test_readline_basic_boundary(self): + readline(self.fp, self.read_data) + self.fp.seek(0) + readline(self.fp, self.read_data, 3) + + +class ReadUniversalBasicBoundaryTestCase(ReadTextBasicBoundaryTestCase): + + mode = 'U' + read_data = 'CR\n' + + +class BinaryReadaheadBoundaryTestCase(BaseTestCase): + + mode = 'rb' + data = 'foo\n' + ('x' * READAHEAD_SIZE) + + def test_read_boundary(self): + readline(self.fp, 'foo\n') + read(self.fp, 'x' * READAHEAD_SIZE, READAHEAD_SIZE) + def test_readline_boundary(self): - readline(self.fp, 'CR\r') + readline(self.fp, 'foo\n') + readline(self.fp, 'x' * READAHEAD_SIZE, READAHEAD_SIZE) + + +class TextReadaheadBoundaryTestCase(BinaryReadaheadBoundaryTestCase): + + mode = 'r' + + +class UniversalReadaheadBoundaryTestCase(BinaryReadaheadBoundaryTestCase): + + mode = 'U' + + +class TextReadaheadBoundary2TestCase(BaseTestCase): + """For CRLF platforms only""" + + data = ('x' * (READAHEAD_SIZE + 1)) + '\r\n' + + def test_read_boundary2(self): + read(self.fp, 'x' * (READAHEAD_SIZE + 1), READAHEAD_SIZE + 1) + read(self.fp, '\n') + + +class UniversalReadaheadBoundary2TestCase(TextReadaheadBoundary2TestCase): + + mode = 'U' + + +class TextReadaheadBoundary3TestCase(BaseTestCase): + """For CRLF platforms only""" + + mode = 'r' + data = ('x' * (READAHEAD_SIZE - 1)) + '\r\n' + + def test_read_boundary3(self): + size = READAHEAD_SIZE - 1 + read(self.fp, 'x' * size, size) + read(self.fp, '\n') self.fp.seek(0) - readline(self.fp, 'CR\r', 3) + read(self.fp, ('x' * size) + '\n', READAHEAD_SIZE) + def test_readline_boundary3(self): + size = READAHEAD_SIZE - 1 + readline(self.fp, 'x' * size, size) + readline(self.fp, '\n') + def test_read_boundary3_with_extra(self): + self.write_fp.seek(0, 2) + self.write_fp.write('foo') + self.write_fp.flush() + self.write_fp.seek(0) + + size = READAHEAD_SIZE - 1 + read(self.fp, ('x' * size) + '\nfoo', READAHEAD_SIZE + 3) + + +class UniversalReadaheadBoundary3TestCase(TextReadaheadBoundary3TestCase): + + mode = 'U' + + +class TextReadaheadBoundary4TestCase(BaseTestCase): + """For CRLF platforms only""" + + mode = 'r' + data = ('x' * (READAHEAD_SIZE - 2)) + '\n\r' + + def test_read_boundary4(self): + readline(self.fp, 'x' * (READAHEAD_SIZE - 2) + '\n') + + self.write_fp.write('\n') + self.write_fp.flush() + + read(self.fp, '\n') + + def test_readline_boundary4(self): + readline(self.fp, 'x' * (READAHEAD_SIZE - 2) + '\n') + + self.write_fp.write('\n') + self.write_fp.flush() + + readline(self.fp, '\n') + + +class UniversalReadaheadBoundary4TestCase(TextReadaheadBoundary4TestCase): + + mode = 'U' + data = ('x' * (READAHEAD_SIZE - 2)) + '\n\r' + + +class TextReadaheadBoundary5TestCase(BaseTestCase): + """For CRLF platforms only""" + + mode = 'r' + data = 'foobar\n' + ('x' * (READAHEAD_SIZE + 1)) + + def test_boundary5(self): + readline(self.fp, 'foobar\n') + read(self.fp, 'x' * (READAHEAD_SIZE + 1), READAHEAD_SIZE + 1) + + +class UniversalReadaheadBoundary5TestCase(TextReadaheadBoundary5TestCase): + + mode = 'U' + + +class TextCRAtReadheadBoundaryTestCase(BaseTestCase): + """For CRLF platforms only""" + + data = ('x' * (READAHEAD_SIZE - 1)) + '\rfoo' + read_data = data + + def test_readline_cr_at_boundary(self): + readline(self.fp, self.read_data, len(self.read_data)) + self.fp.seek(0) + readline(self.fp, self.read_data) + + +class TextCRAtReadheadBoundary2TestCase(TextCRAtReadheadBoundaryTestCase): + """For CRLF platforms only""" + + data = ('x' * (READAHEAD_SIZE - 1)) + '\r' + ('x' * 300) + read_data = data + + class WriteTextNewlinesTestCase(BaseTestCase): write_mode = 'w' @@ -161,8 +320,8 @@ else: raise AssertionError("file mode 'wU' did not raise a " "ValueError") - + def read(fp, data, size=-1): line = fp.read(size) assert line == data, 'read: %r expected: %r' % (line, data) @@ -174,12 +333,28 @@ def test_main(): - test_support.run_unittest(BinaryNewlinesTestCase, - ReadTextNewlinesTestCase, - ReadTextBoundaryTestCase, - WriteTextNewlinesTestCase, - ReadUniversalNewlinesTestCase, - WriteUniversalNewlinesTestCase) + tests = [ReadBinaryNewlinesTestCase, + ReadTextNewlinesTestCase, + ReadTextBasicBoundaryTestCase, + ReadUniversalBasicBoundaryTestCase, + BinaryReadaheadBoundaryTestCase, + TextReadaheadBoundaryTestCase, + UniversalReadaheadBoundaryTestCase, + UniversalReadaheadBoundary2TestCase, + UniversalReadaheadBoundary3TestCase, + UniversalReadaheadBoundary4TestCase, + UniversalReadaheadBoundary5TestCase, + WriteTextNewlinesTestCase, + ReadUniversalNewlinesTestCase, + WriteUniversalNewlinesTestCase] + if CRLF: + tests.extend([TextReadaheadBoundary2TestCase, + TextReadaheadBoundary3TestCase, + TextReadaheadBoundary4TestCase, + TextReadaheadBoundary5TestCase, + TextCRAtReadheadBoundaryTestCase, + TextCRAtReadheadBoundary2TestCase]) + test_support.run_unittest(*tests) if __name__ == '__main__': test_main() Modified: trunk/jython/src/org/python/core/Options.java =================================================================== --- trunk/jython/src/org/python/core/Options.java 2007-11-16 19:49:34 UTC (rev 3681) +++ trunk/jython/src/org/python/core/Options.java 2007-11-16 21:41:19 UTC (rev 3682) @@ -71,6 +71,10 @@ */ public static boolean Qnew = false; + /** Force stdin, stdout and stderr to be unbuffered, and opened in + * binary mode */ + public static boolean unbuffered = false; + /** * Enable division warning. The value maps to the registry values of * <ul> Modified: trunk/jython/src/org/python/core/Py.java =================================================================== --- trunk/jython/src/org/python/core/Py.java 2007-11-16 19:49:34 UTC (rev 3681) +++ trunk/jython/src/org/python/core/Py.java 2007-11-16 21:41:19 UTC (rev 3682) @@ -11,7 +11,6 @@ import java.io.PrintStream; import java.io.Serializable; import java.io.StreamCorruptedException; -import java.io.Writer; import java.lang.reflect.InvocationTargetException; import org.python.compiler.Module; @@ -151,6 +150,12 @@ return new PyException(Py.IOError, message); } + public static PyException IOError(int errno, String message) { + PyTuple args = new PyTuple(new PyObject[] {new PyInteger(errno), + new PyString(message)}); + return new PyException(Py.IOError, args); + } + public static PyObject KeyError; public static PyException KeyError(String message) { return new PyException(Py.KeyError, message); @@ -1304,7 +1309,7 @@ contents = o.toString(); else if (o instanceof PyFile) { PyFile fp = (PyFile)o; - if (fp.closed) + if (fp.getClosed()) return; contents = fp.read().toString(); } else @@ -2047,16 +2052,9 @@ this.file = file; if (file instanceof PyJavaInstance) { - Object tmp = file.__tojava__(OutputStream.class); - if ((tmp != Py.NoConversion) && (tmp != null)) { - OutputStream os = (OutputStream)tmp; - this.file = new PyFile(os, "<java OutputStream>"); - } else { - tmp = file.__tojava__(Writer.class); - if ((tmp != Py.NoConversion) && (tmp != null)) { - Writer w = (Writer)tmp; - this.file = new PyFile(w, "<java Writer>"); - } + Object tojava = file.__tojava__(OutputStream.class); + if (tojava != null && tojava != Py.NoConversion) { + this.file = new PyFile((OutputStream)tojava, "<java OutputStream>"); } } } Modified: trunk/jython/src/org/python/core/PyArray.java =================================================================== --- trunk/jython/src/org/python/core/PyArray.java 2007-11-16 19:49:34 UTC (rev 3681) +++ trunk/jython/src/org/python/core/PyArray.java 2007-11-16 21:41:19 UTC (rev 3682) @@ -1581,7 +1581,7 @@ * @param value * value to set the element to */ - protected void set(int i, PyObject value) { + public void set(int i, PyObject value) { // check for overflow of the integral types if(type == Byte.TYPE) { long val; Modified: trunk/jython/src/org/python/core/PyFile.java =================================================================== --- trunk/jython/src/org/python/core/PyFile.java 2007-11-16 19:49:34 UTC (rev 3681) +++ trunk/jython/src/org/python/core/PyFile.java 2007-11-16 21:41:19 UTC (rev 3682) @@ -1,26 +1,25 @@ // Copyright (c) Corporation for National Research Initiatives package org.python.core; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.PushbackInputStream; import java.io.RandomAccessFile; -import java.io.Writer; - -import java.util.EnumSet; -import java.util.Iterator; import java.util.LinkedList; -// To do: -// - readinto(array) -// - modes w, a should disallow reading -// - what to do about buffer size? -// - isatty() -// - fileno() (defined, but always raises an exception, for urllib) +import org.python.core.io.BinaryIOWrapper; +import org.python.core.io.BufferedIOBase; +import org.python.core.io.BufferedRandom; +import org.python.core.io.BufferedReader; +import org.python.core.io.BufferedWriter; +import org.python.core.io.FileIO; +import org.python.core.io.IOBase; +import org.python.core.io.LineBufferedRandom; +import org.python.core.io.LineBufferedWriter; +import org.python.core.io.RawIOBase; +import org.python.core.io.StreamIO; +import org.python.core.io.TextIOBase; +import org.python.core.io.TextIOWrapper; +import org.python.core.io.UniversalIOWrapper; /** * A python file wrapper around a java stream, reader/writer or file. @@ -39,6 +38,7 @@ dict.__setitem__("name",new PyGetSetDescr("name",PyFile.class,"getName",null,null)); dict.__setitem__("closed",new PyGetSetDescr("closed",PyFile.class,"getClosed",null,null)); dict.__setitem__("newlines",new PyGetSetDescr("newlines",PyFile.class,"getNewlines",null,null)); + dict.__setitem__("softspace",new PyGetSetDescr("softspace",PyFile.class,"getSoftspace","setSoftspace","delSoftspace")); class exposed___cmp__ extends PyBuiltinMethodNarrow { exposed___cmp__(PyObject self,PyBuiltinFunction.Info info) { @@ -205,6 +205,22 @@ } dict.__setitem__("read",new PyMethodDescr("read",PyFile.class,0,1,new exposed_read(null,null))); + class exposed_readinto extends PyBuiltinMethodNarrow { + + exposed_readinto(PyObject self,PyBuiltinFunction.Info info) { + super(self,info); + } + + public PyBuiltinFunction bind(PyObject self) { + return new exposed_readinto(self,info); + } + + public PyObject __call__(PyObject arg0) { + return Py.newInteger(((PyFile)self).file_readinto(arg0)); + } + + } + dict.__setitem__("readinto",new PyMethodDescr("readinto",PyFile.class,1,1,new exposed_readinto(null,null))); class exposed_readline extends PyBuiltinMethodNarrow { exposed_readline(PyObject self,PyBuiltinFunction.Info info) { @@ -395,8 +411,8 @@ } public PyObject __call__(PyObject arg0) { - ((PyFile)self).file_write(arg0); - return Py.None; + ((PyFile)self).file_write(arg0); + return Py.None; } } @@ -491,74 +507,77 @@ } //~ END GENERATED REGION -- DO NOT EDIT SEE gexpose.py - public String name; + /** The filename */ + private PyObject name; + + /** The mode string */ public String mode; - public boolean softspace; - public boolean closed; - private FileWrapper file; + /** Indicator dictating whether a space should be written to this + * file on the next print statement (not currently implemented in + * print ) */ + public boolean softspace = false; - private boolean universal = false; + /** Whether this file is opened for reading */ + private boolean reading = false; + + /** Whether this file is opened for writing */ + private boolean writing = false; + + /** Whether this file is opened in appending mode */ + private boolean appending = false; + + /** Whether this file is opened for updating */ + private boolean updating = false; + + /** Whether this file is opened in binary mode */ private boolean binary = false; + /** Whether this file is opened in universal newlines mode */ + private boolean universal = false; + + /** The underlying IO object */ + private TextIOBase file; + + /** The file's closer object; ensures the file is closed at + * shutdown */ private Closer closer; + + /** All PyFiles' closers */ private static LinkedList closers = new LinkedList(); + static { - try { - Runtime.getRuntime().addShutdownHook(new PyFileCloser()); - } catch(SecurityException e) { - Py.writeDebug("PyFile", "Can't register file closer hook"); - } + initCloser(); } - private static InputStream _pb(InputStream s, String mode) - { - if (mode.contains("b")) { - if (s instanceof PushbackInputStream) { - return s; - } - return new PushbackInputStream(s); - } - return s; + public PyFile() { } - final void file_init(PyObject[] args,String[] kwds) { - - ArgParser ap = new ArgParser("file", args, kwds, - new String[] { "name", "mode", "bufsize" }, 1); - String nameArg = ap.getString(0, null); - String modeArg = ap.getString(1, "r"); - int buffArg = ap.getInt(2, 0); - file_init(_setup(nameArg, modeArg, buffArg), nameArg, modeArg); + public PyFile(PyType subType) { + super(subType); } - public PyFile() { - // xxx: this constructor should only be used in conjunction with file_init + public PyFile(RawIOBase raw, String name, String mode, int bufsize) { + parseMode(mode); + file_init(raw, name, mode, bufsize); } - public PyFile(PyType subType) { - super(subType); + public PyFile(InputStream istream, OutputStream ostream, String name, + String mode, int bufsize, boolean closefd) { + parseMode(mode); + file_init(new StreamIO(istream, ostream, closefd), name, mode, bufsize); } - private void file_init(FileWrapper file, String name, String mode) { - file.setMode(mode); - this.name = name; - this.mode = mode; - this.softspace = false; - this.closed = false; - if (universal) { - this.file = new UniversalWrapper(file); - } else if (!binary) { - this.file = new TextWrapper(file); - } else { - this.file = file; - } + public PyFile(InputStream istream, OutputStream ostream, String name, + String mode, int bufsize) + { + this(istream, ostream, name, mode, -1, true); } public PyFile(InputStream istream, OutputStream ostream, String name, String mode) { - file_init(new IOStreamWrapper(_pb(istream, mode), ostream), name, mode); + this(istream, ostream, name, mode, -1); } public PyFile(InputStream istream, OutputStream ostream, String name) @@ -570,8 +589,18 @@ this(istream, ostream, "<???>", "r+"); } + public PyFile(InputStream istream, String name, String mode, int bufsize, + boolean closefd) { + parseMode(mode); + file_init(new StreamIO(istream, closefd), name, mode, bufsize); + } + + public PyFile(InputStream istream, String name, String mode, int bufsize) { + this(istream, name, mode, -1, true); + } + public PyFile(InputStream istream, String name, String mode) { - file_init(new InputStreamWrapper(_pb(istream, mode)), name, mode); + this(istream, name, mode, -1); } public PyFile(InputStream istream, String name) { @@ -582,8 +611,18 @@ this(istream, "<???>", "r"); } + public PyFile(OutputStream ostream, String name, String mode, int bufsize, + boolean closefd) { + parseMode(mode); + file_init(new StreamIO(ostream, closefd), name, mode, bufsize); + } + + public PyFile(OutputStream ostream, String name, String mode, int bufsize) { + this(ostream, name, mode, -1, true); + } + public PyFile(OutputStream ostream, String name, String mode) { - file_init(new OutputStreamWrapper(ostream), name, mode); + this(ostream, name, mode, -1); } public PyFile(OutputStream ostream, String name) { @@ -594,20 +633,12 @@ this(ostream, "<???>", "w"); } - public PyFile(Writer ostream, String name, String mode) { - file_init(new WriterWrapper(ostream), name, mode); + public PyFile(RandomAccessFile file, String name, String mode, int bufsize) { + file_init(new FileIO(file.getChannel(), parseMode(mode)), name, mode, bufsize); } - public PyFile(Writer ostream, String name) { - this(ostream, name, "w"); - } - - public PyFile(Writer ostream) { - this(ostream, "<???>", "w"); - } - public PyFile(RandomAccessFile file, String name, String mode) { - file_init(new RFileWrapper(file), name, mode); + this(file, name, mode, -1); } public PyFile(RandomAccessFile file, String name) { @@ -619,67 +650,82 @@ } public PyFile(String name, String mode, int bufsize) { - file_init(_setup(name, mode, bufsize), name, mode); + file_init(new FileIO(name, parseMode(mode)), name, mode, bufsize); } - public void __setattr__(String name, PyObject value) { - // softspace is the only writeable file object attribute - if (name == "softspace") - softspace = value.__nonzero__(); - else if (name == "mode" || name == "closed" || name == "name") - throw Py.TypeError("readonly attribute: " + name); - else - throw Py.AttributeError(name); + final void file_init(PyObject[] args,String[] kwds) { + ArgParser ap = new ArgParser("file", args, kwds, + new String[] { "name", "mode", "bufsize" }, 1); + PyObject name = ap.getPyObject(0); + if (!(name instanceof PyString)) { + throw Py.TypeError("coercing to Unicode: need string, '" + + name.getType().getFullName() + "'type found"); + } + String mode = ap.getString(1, "r"); + int bufsize = ap.getInt(2, -1); + file_init(new FileIO(name.toString(), parseMode(mode)), name, mode, bufsize); } - public Object __tojava__(Class cls) { - Object o = null; - try { - o = file.__tojava__(cls); - } catch (IOException exc) { } - if (o == null) - o = super.__tojava__(cls); - return o; + private void file_init(RawIOBase raw, String name, String mode, int bufsize) { + file_init(raw, new PyString(name), mode, bufsize); } - private FileWrapper _setup(String name, String mode, int bufsize) { - String jmode = sanitizeMode(mode); - char c1 = mode.charAt(0); - try { - File f = new File(name); - if (c1 == 'r') { - if (!f.exists()) { - throw Py.IOError("No such file or directory: '" + name + "'"); - } - } - if (c1 == 'w') { - // Hack to truncate the file without deleting it: - // create a FileOutputStream for it and close it again. - FileOutputStream fo = new FileOutputStream(f); - fo.close(); - fo = null; - } - // What about bufsize? - RandomAccessFile rfile = new RandomAccessFile(f, jmode); - RFileWrapper iofile = new RFileWrapper(rfile); - if (c1 == 'a') - iofile.seek(0, 2); - return iofile; - } catch (IOException e) { - throw Py.IOError(e); + private void file_init(RawIOBase raw, PyObject name, String mode, int bufsize) { + this.name = name; + this.mode = mode; + + BufferedIOBase buffer = createBuffer(raw, bufsize); + if (universal) { + this.file = new UniversalIOWrapper(buffer); + } else if (!binary) { + this.file = new TextIOWrapper(buffer); + } else { + this.file = new BinaryIOWrapper(buffer); } } /** + * Wrap the given RawIOBase with a BufferedIOBase according to the + * mode and given bufsize. + * + * @param raw a RawIOBase value + * @param bufsize an int size of the buffer + * @return a BufferedIOBase wrapper + */ + private BufferedIOBase createBuffer(RawIOBase raw, int bufsize) { + if (bufsize < 0) { + bufsize = IOBase.DEFAULT_BUFFER_SIZE; + } + boolean lineBuffered = bufsize == 1; + BufferedIOBase buffer; + if (updating) { + buffer = lineBuffered ? + new LineBufferedRandom(raw) : + new BufferedRandom(raw, bufsize); + } else if (writing || appending) { + buffer = lineBuffered ? + new LineBufferedWriter(raw) : + new BufferedWriter(raw, bufsize); + } else if (reading) { + // Line buffering is for output only + buffer = new BufferedReader(raw, lineBuffered ? 0 : bufsize); + } else { + // Should never happen + throw Py.ValueError("unknown mode: '" + mode + "'"); + } + return buffer; + } + + /** * Parse and validate the python file mode, returning a cleaned - * file mode suitable for RandomAccessFile + * file mode suitable for FileIO. * * @param mode a python file mode String * @return a RandomAccessFile mode String */ - private String sanitizeMode(String mode) { + private String parseMode(String mode) { if (mode.length() == 0) { - throw Py.IOError("invalid mode: "); + throw Py.ValueError("empty mode string"); } String origMode = mode; @@ -699,37 +745,18 @@ } binary = mode.contains("b"); + reading = mode.contains("r"); + writing = mode.contains("w"); + appending = mode.contains("a"); + updating = mode.contains("+"); - int maxSearch = mode.length() < 3 ? mode.length() : 3; - if ("wa".indexOf(mode.charAt(0)) > -1 || - mode.substring(0, maxSearch).contains("+")) { - return "rw"; - } else { - return "r"; - } + return (reading ? "r" : "") + (writing ? "w" : "") + + (appending ? "a" : "") + (updating ? "+" : ""); } - final String file_read(int n) { - if (closed) - err_closed(); - StringBuffer data = new StringBuffer(); - try { - while (n != 0) { - String s = file.read(n); - int len = s.length(); - if (len == 0) - break; - data.append(s); - if (n > 0) { - n -= len; - if (n <= 0) - break; - } - } - } catch (IOException e) { - throw Py.IOError(e); - } - return data.toString(); + final synchronized String file_read(int n) { + checkClosed(); + return file.read(n); } public String read(int n) { @@ -744,26 +771,20 @@ return file_read(); } - final String file_readline(int max) { - if (closed) - err_closed(); - StringBuffer s = new StringBuffer(); - while (max < 0 || s.length() < max) { - int c; - try { - c = file.read(); - } catch (IOException e) { - throw Py.IOError(e); - } - if (c < 0) - break; - s.append((char)c); - if ((char)c == '\n') - break; - } - return s.toString(); + final synchronized int file_readinto(PyObject buf) { + checkClosed(); + return file.readinto(buf); } + public int readinto(PyObject buf) { + return file_readinto(buf); + } + + final synchronized String file_readline(int max) { + checkClosed(); + return file.readline(max); + } + public String readline(int max) { return file_readline(max); } @@ -776,22 +797,20 @@ return file_readline(-1); } - final PyObject file_readlines(int sizehint) { - if (closed) - err_closed(); + final synchronized PyObject file_readlines(int sizehint) { + checkClosed(); PyList list = new PyList(); - int bytesread = 0; - for (;;) { - String s = readline(); - int len = s.length(); - if (len == 0) + int count = 0; + do { + String line = file.readline(-1); + int len = line.length(); + if (len == 0) { // EOF break; - bytesread += len; - list.append(new PyString(s)); - if (sizehint > 0 && bytesread > sizehint) - break; - } + } + count += len; + list.append(new PyString(line)); + } while (sizehint <= 0 || count < sizehint); return list; } @@ -812,6 +831,7 @@ } final PyObject file___iter__() { + checkClosed(); return this; } @@ -819,11 +839,13 @@ return file___iternext__(); } - final PyObject file___iternext__() { - PyString s = new PyString(readline()); - if (s.__len__() == 0) + final synchronized PyObject file___iternext__() { + checkClosed(); + String next = file.readline(-1); + if (next.length() == 0) { return null; - return s; + } + return new PyString(next); } final PyObject file_next() { @@ -838,6 +860,7 @@ } final PyObject file_xreadlines() { + checkClosed(); return this; } @@ -846,40 +869,37 @@ } final void file_write(PyObject o) { - if(o instanceof PyUnicode) { + if (o instanceof PyUnicode) { // Call __str__ on unicode objects to encode them before writing file_write(o.__str__().string); - } else if(o instanceof PyString) { + } else if (o instanceof PyString) { file_write(((PyString)o).string); } else { throw Py.TypeError("write requires a string as its argument"); } } - final void file_write(String s) { - if (closed) - err_closed(); - try { - file.write(s); - softspace = false; - } catch (IOException e) { - throw Py.IOError(e); - } + final synchronized void file_write(String s) { + checkClosed(); + softspace = false; + file.write(s); } public void write(String s) { file_write(s); } - final void file_writelines(PyObject a) { + final synchronized void file_writelines(PyObject a) { + checkClosed(); PyObject iter = Py.iter(a, "writelines() requires an iterable argument"); PyObject item = null; - while((item = iter.__iternext__()) != null) { - if (!(item instanceof PyString)) + while ((item = iter.__iternext__()) != null) { + if (!(item instanceof PyString)) { throw Py.TypeError("writelines() argument must be a " + "sequence of strings"); - write(item.toString()); + } + file.write(item.toString()); } } @@ -887,28 +907,18 @@ file_writelines(a); } - final long file_tell() { - if (closed) - err_closed(); - try { - return file.tell(); - } catch (IOException e) { - throw Py.IOError(e); - } + final synchronized long file_tell() { + checkClosed(); + return file.tell(); } public long tell() { return file_tell(); } - final void file_seek(long pos, int how) { - if (closed) - err_closed(); - try { - file.seek(pos, how); - } catch (IOException e) { - throw Py.IOError(e); - } + final synchronized void file_seek(long pos, int how) { + checkClosed(); + file.seek(pos, how); } public void seek(long pos, int how) { @@ -923,79 +933,70 @@ file_seek(pos); } - final void file_flush() { - if (closed) - err_closed(); - try { - file.flush(); - } catch (IOException e) { - throw Py.IOError(e); - } + final synchronized void file_flush() { + checkClosed(); + file.flush(); } public void flush() { file_flush(); } - final void file_close() { + final synchronized void file_close() { if (closer != null) { closer.close(); closer = null; } else { - try { - file.close(); - } catch (IOException e) { - throw Py.IOError(e); - } + file.close(); } - closed = true; - file = new FileWrapper(); } public void close() { file_close(); } - final void file_truncate() { - try { - file.truncate(file.tell()); - } catch (IOException e) { - throw Py.IOError(e); - } + final synchronized void file_truncate() { + file.truncate(file.tell()); } public void truncate() { file_truncate(); } - final void file_truncate(long position) { - try { - file.truncate(position); - } catch (IOException e) { - throw Py.IOError(e); - } + final synchronized void file_truncate(long position) { + file.truncate(position); } public void truncate(long position) { file_truncate(position); } - // TBD: should this be removed? I think it's better to raise an - // AttributeError than an IOError here. - public PyObject fileno() { - throw Py.IOError("fileno() is not supported in jython"); + public boolean isatty() { + return file_isatty(); } + final boolean file_isatty() { + return file.isatty(); + } + + public int fileno() { + return file_fileno(); + } + + final int file_fileno() { + return file.fileno(); + } + final String file_toString() { StringBuffer s = new StringBuffer("<"); - if (closed) { + if (file.closed()) { s.append("closed "); } else { s.append("open "); } - s.append("file '"); - s.append(name); - s.append("', mode '"); + s.append("file "); + s.append(name.__repr__()); + s.append(", mode '"); s.append(mode); s.append("' "); s.append(Py.idstr(this)); @@ -1015,734 +1016,62 @@ return super.__nonzero__(); } - private void err_closed() { - throw Py.ValueError("I/O operation on closed file"); + private void checkClosed() { + file.checkClosed(); } public String getMode() { return mode; } - public String getName() { + public PyObject getName() { return name; } public boolean getClosed() { - return closed; + return file.closed(); } public PyObject getNewlines() { - if (!universal) { - return Py.None; - } - EnumSet newlineTypes = ((UniversalWrapper)file).getNewlineTypes(); - int size = newlineTypes.size(); - if (size == 0) { - return Py.None; - } else if (size == 1) { - String newline = ((Newline)newlineTypes.iterator().next()).getValue(); - return new PyString(newline); - } - - PyObject[] newlines = new PyObject[size]; - int i = 0; - for (Iterator newlineIter = newlineTypes.iterator(); newlineIter.hasNext();) { - String newline = ((Newline)newlineIter.next()).getValue(); - newlines[i] = new PyString(newline); - i++; - } - return new PyTuple(newlines); + return file.getNewlines(); } - protected void finalize() throws Throwable { - super.finalize(); - if (closer != null) { - closer.close(); - } + public PyObject getSoftspace() { + return softspace ? new PyInteger(1) : new PyInteger(0); } - private static class FileWrapper { - protected boolean reading; - protected boolean writing; - protected boolean binary; - - void setMode(String mode) { - reading = mode.indexOf('r') >= 0; - writing = mode.indexOf('w') >= 0 || mode.indexOf("+") >= 0 || - mode.indexOf('a') >= 0; - binary = mode.indexOf('b') >= 0; - } - - public String read(int n) throws IOException { - throw new IOException("file not open for reading"); - } - - public int read() throws IOException { - throw new IOException("file not open for reading"); - } - - public int available() throws IOException { - throw new IOException("file not open for reading"); - } - - public void unread(int c) throws IOException { - throw new IOException("file doesn't support unread"); - } - - public void write(String s) throws IOException { - throw new IOException("file not open for writing"); - } - - public long tell() throws IOException { - throw new IOException("file doesn't support tell/seek"); - } - - public void seek(long pos, int how) throws IOException { - throw new IOException("file doesn't support tell/seek"); - } - - public void flush() throws IOException { - } - - public void close() throws IOException { - } - - public void truncate(long position) throws IOException { - throw new IOException("file doesn't support truncate"); - } - - public Object __tojava__(Class cls) throws IOException { - return null; - } + public void setSoftspace(PyObject obj) { + softspace = obj.__nonzero__(); } - private static class InputStreamWrapper extends FileWrapper { - InputStream istream; - - public InputStreamWrapper(InputStream s) { - istream = s; - } - - public String read(int n) throws IOException { - if (n == 0) - // nothing to do - return ""; - if (n < 0) { - // read until we hit EOF - byte buf[] = new byte[1024]; - StringBuffer sbuf = new StringBuffer(); - for (int read=0; read >= 0; read=istream.read(buf)) - sbuf.append(PyString.from_bytes(buf, 0, read)); - return sbuf.toString(); - } - // read the next chunk available, but make sure it's at least - // one byte so as not to trip the `empty string' return value - // test done by the caller - //int avail = istream.available(); - //n = (n > avail) ? n : avail; - byte buf[] = new byte[n]; - int read = istream.read(buf); - if (read < 0) - // EOF encountered - return ""; - return PyString.from_bytes(buf, 0, read); - } - - public int read() throws IOException { - return istream.read(); - } - - public int available() throws IOException { - return istream.available(); - } - - public void unread(int c) throws IOException { - ((PushbackInputStream)istream).unread(c); - } - - public void close() throws IOException { - istream.close(); - } - - public Object __tojava__(Class cls) throws IOException { - if (InputStream.class.isAssignableFrom(cls)) - return is... [truncated message content] |