10a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# Adapted from test_file.py by Daniel Stutzbach 20a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 30a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom __future__ import unicode_literals 40a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 50a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport sys 60a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport os 70a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport errno 80a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport unittest 90a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom array import array 100a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom weakref import proxy 110a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom functools import wraps 120a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom UserList import UserList 130a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport _testcapi 140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 150a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom test.test_support import TESTFN, check_warnings, run_unittest, make_bad_fd 160a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom test.test_support import py3k_bytes as bytes 170a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom test.script_helper import run_python 180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 190a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom _io import FileIO as _FileIO 200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 210a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass AutoFileTests(unittest.TestCase): 220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # file tests for which a test file is automatically set up 230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def setUp(self): 250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f = _FileIO(TESTFN, 'w') 260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def tearDown(self): 280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.f: 290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.close() 300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao os.remove(TESTFN) 310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testWeakRefs(self): 330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # verify weak references 340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao p = proxy(self.f) 350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao p.write(bytes(range(10))) 360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(self.f.tell(), p.tell()) 370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.close() 380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f = None 390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertRaises(ReferenceError, getattr, p, 'tell') 400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testSeekTell(self): 420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.write(bytes(range(20))) 430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(self.f.tell(), 20) 440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.seek(0) 450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(self.f.tell(), 0) 460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.seek(10) 470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(self.f.tell(), 10) 480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.seek(5, 1) 490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(self.f.tell(), 15) 500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.seek(-5, 1) 510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(self.f.tell(), 10) 520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.seek(-5, 2) 530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(self.f.tell(), 15) 540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testAttributes(self): 560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # verify expected attributes exist 570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = self.f 580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.mode, "wb") 600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.closed, False) 610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # verify the attributes are readonly 630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for attr in 'mode', 'closed': 640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertRaises((AttributeError, TypeError), 650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao setattr, f, attr, 'oops') 660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testReadinto(self): 680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # verify readinto 690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.write(b"\x01\x02") 700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.close() 710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao a = array(b'b', b'x'*10) 720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f = _FileIO(TESTFN, 'r') 730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao n = self.f.readinto(a) 740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(array(b'b', [1, 2]), a[:n]) 750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testWritelinesList(self): 770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao l = [b'123', b'456'] 780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.writelines(l) 790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.close() 800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f = _FileIO(TESTFN, 'rb') 810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao buf = self.f.read() 820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(buf, b'123456') 830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testWritelinesUserList(self): 850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao l = UserList([b'123', b'456']) 860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.writelines(l) 870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.close() 880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f = _FileIO(TESTFN, 'rb') 890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao buf = self.f.read() 900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(buf, b'123456') 910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testWritelinesError(self): 930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertRaises(TypeError, self.f.writelines, [1, 2, 3]) 940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertRaises(TypeError, self.f.writelines, None) 950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def test_none_args(self): 970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.write(b"hi\nbye\nabc") 980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.close() 990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f = _FileIO(TESTFN, 'r') 1000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(self.f.read(None), b"hi\nbye\nabc") 1010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.seek(0) 1020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(self.f.readline(None), b"hi\n") 1030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(self.f.readlines(None), [b"bye\n", b"abc"]) 1040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testRepr(self): 1060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(repr(self.f), "<_io.FileIO name=%r mode='%s'>" 1070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao % (self.f.name, self.f.mode)) 1080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao del self.f.name 1090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(repr(self.f), "<_io.FileIO fd=%r mode='%s'>" 1100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao % (self.f.fileno(), self.f.mode)) 1110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.close() 1120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(repr(self.f), "<_io.FileIO [closed]>") 1130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testErrors(self): 1150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = self.f 1160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertTrue(not f.isatty()) 1170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertTrue(not f.closed) 1180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao #self.assertEqual(f.name, TESTFN) 1190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertRaises(ValueError, f.read, 10) # Open for reading 1200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.close() 1210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertTrue(f.closed) 1220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = _FileIO(TESTFN, 'r') 1230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertRaises(TypeError, f.readinto, "") 1240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertTrue(not f.closed) 1250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.close() 1260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertTrue(f.closed) 1270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testMethods(self): 1290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao methods = ['fileno', 'isatty', 'read', 'readinto', 1300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 'seek', 'tell', 'truncate', 'write', 'seekable', 1310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 'readable', 'writable'] 1320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if sys.platform.startswith('atheos'): 1330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao methods.remove('truncate') 1340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.close() 1360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertTrue(self.f.closed) 1370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for methodname in methods: 1390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao method = getattr(self.f, methodname) 1400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # should raise on closed file 1410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertRaises(ValueError, method) 1420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testOpendir(self): 1440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Issue 3703: opening a directory should fill the errno 1450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Windows always returns "[Errno 13]: Permission denied 1460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Unix calls dircheck() and returns "[Errno 21]: Is a directory" 1470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 1480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao _FileIO('.', 'r') 1490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao except IOError as e: 1500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertNotEqual(e.errno, 0) 1510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(e.filename, ".") 1520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 1530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.fail("Should have raised IOError") 1540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao @unittest.skipIf(os.name == 'nt', "test only works on a POSIX-like system") 1560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testOpenDirFD(self): 1570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao fd = os.open('.', os.O_RDONLY) 1580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao with self.assertRaises(IOError) as cm: 1590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao _FileIO(fd, 'r') 1600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao os.close(fd) 1610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(cm.exception.errno, errno.EISDIR) 1620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao #A set of functions testing that we get expected behaviour if someone has 1640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao #manually closed the internal file descriptor. First, a decorator: 1650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def ClosedFD(func): 1660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao @wraps(func) 1670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def wrapper(self): 1680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao #forcibly close the fd before invoking the problem function 1690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = self.f 1700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao os.close(f.fileno()) 1710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 1720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao func(self, f) 1730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao finally: 1740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 1750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.close() 1760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao except IOError: 1770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao pass 1780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return wrapper 1790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def ClosedFDRaises(func): 1810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao @wraps(func) 1820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def wrapper(self): 1830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao #forcibly close the fd before invoking the problem function 1840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = self.f 1850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao os.close(f.fileno()) 1860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 1870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao func(self, f) 1880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao except IOError as e: 1890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(e.errno, errno.EBADF) 1900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 1910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.fail("Should have raised IOError") 1920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao finally: 1930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 1940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.close() 1950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao except IOError: 1960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao pass 1970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return wrapper 1980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao @ClosedFDRaises 2000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testErrnoOnClose(self, f): 2010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.close() 2020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao @ClosedFDRaises 2040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testErrnoOnClosedWrite(self, f): 2050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.write('a') 2060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao @ClosedFDRaises 2080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testErrnoOnClosedSeek(self, f): 2090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.seek(0) 2100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao @ClosedFDRaises 2120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testErrnoOnClosedTell(self, f): 2130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.tell() 2140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao @ClosedFDRaises 2160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testErrnoOnClosedTruncate(self, f): 2170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.truncate(0) 2180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao @ClosedFD 2200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testErrnoOnClosedSeekable(self, f): 2210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.seekable() 2220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao @ClosedFD 2240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testErrnoOnClosedReadable(self, f): 2250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.readable() 2260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao @ClosedFD 2280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testErrnoOnClosedWritable(self, f): 2290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.writable() 2300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao @ClosedFD 2320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testErrnoOnClosedFileno(self, f): 2330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.fileno() 2340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao @ClosedFD 2360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testErrnoOnClosedIsatty(self, f): 2370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.isatty(), False) 2380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def ReopenForRead(self): 2400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 2410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f.close() 2420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao except IOError: 2430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao pass 2440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.f = _FileIO(TESTFN, 'r') 2450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao os.close(self.f.fileno()) 2460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return self.f 2470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao @ClosedFDRaises 2490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testErrnoOnClosedRead(self, f): 2500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = self.ReopenForRead() 2510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.read(1) 2520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao @ClosedFDRaises 2540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testErrnoOnClosedReadall(self, f): 2550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = self.ReopenForRead() 2560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.readall() 2570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao @ClosedFDRaises 2590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testErrnoOnClosedReadinto(self, f): 2600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = self.ReopenForRead() 2610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao a = array(b'b', b'x'*10) 2620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.readinto(a) 2630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2640a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass OtherFileTests(unittest.TestCase): 2650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testAbles(self): 2670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 2680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = _FileIO(TESTFN, "w") 2690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.readable(), False) 2700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.writable(), True) 2710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.seekable(), True) 2720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.close() 2730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = _FileIO(TESTFN, "r") 2750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.readable(), True) 2760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.writable(), False) 2770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.seekable(), True) 2780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.close() 2790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = _FileIO(TESTFN, "a+") 2810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.readable(), True) 2820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.writable(), True) 2830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.seekable(), True) 2840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.isatty(), False) 2850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.close() 2860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if sys.platform != "win32": 2880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 2890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = _FileIO("/dev/tty", "a") 2900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao except EnvironmentError: 2910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # When run in a cron job there just aren't any 2920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # ttys, so skip the test. This also handles other 2930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # OS'es that don't support /dev/tty. 2940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao pass 2950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 2960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.readable(), False) 2970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.writable(), True) 2980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if sys.platform != "darwin" and \ 2990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 'bsd' not in sys.platform and \ 3000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao not sys.platform.startswith('sunos'): 3010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Somehow /dev/tty appears seekable on some BSDs 3020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.seekable(), False) 3030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.isatty(), True) 3040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.close() 3050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao finally: 3060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao os.unlink(TESTFN) 3070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testModeStrings(self): 3090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # check invalid mode strings 3100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for mode in ("", "aU", "wU+", "rw", "rt"): 3110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 3120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = _FileIO(TESTFN, mode) 3130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao except ValueError: 3140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao pass 3150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 3160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.close() 3170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.fail('%r is an invalid file mode' % mode) 3180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testUnicodeOpen(self): 3200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # verify repr works for unicode too 3210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = _FileIO(str(TESTFN), "w") 3220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.close() 3230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao os.unlink(TESTFN) 3240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testBytesOpen(self): 3260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Opening a bytes filename 3270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 3280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao fn = TESTFN.encode("ascii") 3290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao except UnicodeEncodeError: 3300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Skip test 3310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return 3320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = _FileIO(fn, "w") 3330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 3340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.write(b"abc") 3350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.close() 3360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao with open(TESTFN, "rb") as f: 3370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.read(), b"abc") 3380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao finally: 3390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao os.unlink(TESTFN) 3400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testInvalidFd(self): 3420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertRaises(ValueError, _FileIO, -10) 3430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertRaises(OSError, _FileIO, make_bad_fd()) 3440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if sys.platform == 'win32': 3450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao import msvcrt 3460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertRaises(IOError, msvcrt.get_osfhandle, make_bad_fd()) 3470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Issue 15989 3480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertRaises(TypeError, _FileIO, _testcapi.INT_MAX + 1) 3490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertRaises(TypeError, _FileIO, _testcapi.INT_MIN - 1) 3500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testBadModeArgument(self): 3520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # verify that we get a sensible error message for bad mode argument 3530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao bad_mode = "qwerty" 3540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 3550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = _FileIO(TESTFN, bad_mode) 3560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao except ValueError as msg: 3570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if msg.args[0] != 0: 3580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao s = str(msg) 3590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if TESTFN in s or bad_mode not in s: 3600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.fail("bad error message for invalid mode: %s" % s) 3610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # if msg.args[0] == 0, we're probably on Windows where there may be 3620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # no obvious way to discover why open() failed. 3630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 3640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.close() 3650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.fail("no error for invalid mode: %s" % bad_mode) 3660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testTruncate(self): 3680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = _FileIO(TESTFN, 'w') 3690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.write(bytes(bytearray(range(10)))) 3700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.tell(), 10) 3710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.truncate(5) 3720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.tell(), 10) 3730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.seek(0, os.SEEK_END), 5) 3740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.truncate(15) 3750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.tell(), 5) 3760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(f.seek(0, os.SEEK_END), 15) 3770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.close() 3780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testTruncateOnWindows(self): 3800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def bug801631(): 3810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # SF bug <http://www.python.org/sf/801631> 3820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # "file.truncate fault on windows" 3830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = _FileIO(TESTFN, 'w') 3840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.write(bytes(range(11))) 3850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.close() 3860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = _FileIO(TESTFN,'r+') 3880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao data = f.read(5) 3890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if data != bytes(range(5)): 3900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.fail("Read on file opened for update failed %r" % data) 3910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if f.tell() != 5: 3920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.fail("File pos after read wrong %d" % f.tell()) 3930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.truncate() 3950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if f.tell() != 5: 3960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.fail("File pos after ftruncate wrong %d" % f.tell()) 3970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.close() 3990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao size = os.path.getsize(TESTFN) 4000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if size != 5: 4010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.fail("File size after ftruncate wrong %d" % size) 4020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 4030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 4040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao bug801631() 4050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao finally: 4060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao os.unlink(TESTFN) 4070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 4080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testAppend(self): 4090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 4100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = open(TESTFN, 'wb') 4110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.write(b'spam') 4120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.close() 4130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = open(TESTFN, 'ab') 4140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.write(b'eggs') 4150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.close() 4160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f = open(TESTFN, 'rb') 4170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao d = f.read() 4180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao f.close() 4190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(d, b'spameggs') 4200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao finally: 4210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 4220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao os.unlink(TESTFN) 4230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao except: 4240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao pass 4250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 4260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testInvalidInit(self): 4270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertRaises(TypeError, _FileIO, "1", 0, 0) 4280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 4290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testWarnings(self): 4300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao with check_warnings(quiet=True) as w: 4310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(w.warnings, []) 4320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertRaises(TypeError, _FileIO, []) 4330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(w.warnings, []) 4340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertRaises(ValueError, _FileIO, "/some/invalid/name", "rt") 4350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertEqual(w.warnings, []) 4360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 4370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def test_surrogates(self): 4380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Issue #8438: try to open a filename containing surrogates. 4390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # It should either fail because the file doesn't exist or the filename 4400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # can't be represented using the filesystem encoding, but not because 4410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # of a LookupError for the error handler "surrogateescape". 4420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao filename = u'\udc80.txt' 4430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 4440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao with _FileIO(filename): 4450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao pass 4460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao except (UnicodeEncodeError, IOError): 4470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao pass 4480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Spawn a separate Python process with a different "file system 4490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # default encoding", to exercise this further. 4500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao env = dict(os.environ) 4510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao env[b'LC_CTYPE'] = b'C' 4520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao _, out = run_python('-c', 'import _io; _io.FileIO(%r)' % filename, env=env) 4530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if ('UnicodeEncodeError' not in out and not 4540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ( ('IOError: [Errno 2] No such file or directory' in out) or 4550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ('IOError: [Errno 22] Invalid argument' in out) ) ): 4560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.fail('Bad output: %r' % out) 4570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 4580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def testUnclosedFDOnException(self): 4590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao class MyException(Exception): pass 4600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao class MyFileIO(_FileIO): 4610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __setattr__(self, name, value): 4620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if name == "name": 4630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise MyException("blocked setting name") 4640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return super(MyFileIO, self).__setattr__(name, value) 4650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao fd = os.open(__file__, os.O_RDONLY) 4660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.assertRaises(MyException, MyFileIO, fd) 4670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao os.close(fd) # should not raise OSError(EBADF) 4680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 4690a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef test_main(): 4700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Historically, these tests have been sloppy about removing TESTFN. 4710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # So get rid of it no matter what. 4720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 4730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao run_unittest(AutoFileTests, OtherFileTests) 4740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao finally: 4750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if os.path.exists(TESTFN): 4760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao os.unlink(TESTFN) 4770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 4780a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoif __name__ == '__main__': 4790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao test_main() 480