1""" Tests for the linecache module """ 2 3import linecache 4import unittest 5import os.path 6import tempfile 7import tokenize 8from test import support 9from test.support import os_helper 10 11 12FILENAME = linecache.__file__ 13NONEXISTENT_FILENAME = FILENAME + '.missing' 14INVALID_NAME = '!@$)(!@#_1' 15EMPTY = '' 16TEST_PATH = os.path.dirname(__file__) 17MODULES = "linecache abc".split() 18MODULE_PATH = os.path.dirname(FILENAME) 19 20SOURCE_1 = ''' 21" Docstring " 22 23def function(): 24 return result 25 26''' 27 28SOURCE_2 = ''' 29def f(): 30 return 1 + 1 31 32a = f() 33 34''' 35 36SOURCE_3 = ''' 37def f(): 38 return 3''' # No ending newline 39 40 41class TempFile: 42 43 def setUp(self): 44 super().setUp() 45 with tempfile.NamedTemporaryFile(delete=False) as fp: 46 self.file_name = fp.name 47 fp.write(self.file_byte_string) 48 self.addCleanup(os_helper.unlink, self.file_name) 49 50 51class GetLineTestsGoodData(TempFile): 52 # file_list = ['list\n', 'of\n', 'good\n', 'strings\n'] 53 54 def setUp(self): 55 self.file_byte_string = ''.join(self.file_list).encode('utf-8') 56 super().setUp() 57 58 def test_getline(self): 59 with tokenize.open(self.file_name) as fp: 60 for index, line in enumerate(fp): 61 if not line.endswith('\n'): 62 line += '\n' 63 64 cached_line = linecache.getline(self.file_name, index + 1) 65 self.assertEqual(line, cached_line) 66 67 def test_getlines(self): 68 lines = linecache.getlines(self.file_name) 69 self.assertEqual(lines, self.file_list) 70 71 72class GetLineTestsBadData(TempFile): 73 # file_byte_string = b'Bad data goes here' 74 75 def test_getline(self): 76 self.assertRaises((SyntaxError, UnicodeDecodeError), 77 linecache.getline, self.file_name, 1) 78 79 def test_getlines(self): 80 self.assertRaises((SyntaxError, UnicodeDecodeError), 81 linecache.getlines, self.file_name) 82 83 84class EmptyFile(GetLineTestsGoodData, unittest.TestCase): 85 file_list = [] 86 87 88class SingleEmptyLine(GetLineTestsGoodData, unittest.TestCase): 89 file_list = ['\n'] 90 91 92class GoodUnicode(GetLineTestsGoodData, unittest.TestCase): 93 file_list = ['á\n', 'b\n', 'abcdef\n', 'ááááá\n'] 94 95 96class BadUnicode(GetLineTestsBadData, unittest.TestCase): 97 file_byte_string = b'\x80abc' 98 99 100class LineCacheTests(unittest.TestCase): 101 102 def test_getline(self): 103 getline = linecache.getline 104 105 # Bad values for line number should return an empty string 106 self.assertEqual(getline(FILENAME, 2**15), EMPTY) 107 self.assertEqual(getline(FILENAME, -1), EMPTY) 108 109 # Float values currently raise TypeError, should it? 110 self.assertRaises(TypeError, getline, FILENAME, 1.1) 111 112 # Bad filenames should return an empty string 113 self.assertEqual(getline(EMPTY, 1), EMPTY) 114 self.assertEqual(getline(INVALID_NAME, 1), EMPTY) 115 116 # Check module loading 117 for entry in MODULES: 118 filename = os.path.join(MODULE_PATH, entry) + '.py' 119 with open(filename, encoding='utf-8') as file: 120 for index, line in enumerate(file): 121 self.assertEqual(line, getline(filename, index + 1)) 122 123 # Check that bogus data isn't returned (issue #1309567) 124 empty = linecache.getlines('a/b/c/__init__.py') 125 self.assertEqual(empty, []) 126 127 def test_no_ending_newline(self): 128 self.addCleanup(os_helper.unlink, os_helper.TESTFN) 129 with open(os_helper.TESTFN, "w", encoding='utf-8') as fp: 130 fp.write(SOURCE_3) 131 lines = linecache.getlines(os_helper.TESTFN) 132 self.assertEqual(lines, ["\n", "def f():\n", " return 3\n"]) 133 134 def test_clearcache(self): 135 cached = [] 136 for entry in MODULES: 137 filename = os.path.join(MODULE_PATH, entry) + '.py' 138 cached.append(filename) 139 linecache.getline(filename, 1) 140 141 # Are all files cached? 142 self.assertNotEqual(cached, []) 143 cached_empty = [fn for fn in cached if fn not in linecache.cache] 144 self.assertEqual(cached_empty, []) 145 146 # Can we clear the cache? 147 linecache.clearcache() 148 cached_empty = [fn for fn in cached if fn in linecache.cache] 149 self.assertEqual(cached_empty, []) 150 151 def test_checkcache(self): 152 getline = linecache.getline 153 # Create a source file and cache its contents 154 source_name = os_helper.TESTFN + '.py' 155 self.addCleanup(os_helper.unlink, source_name) 156 with open(source_name, 'w', encoding='utf-8') as source: 157 source.write(SOURCE_1) 158 getline(source_name, 1) 159 160 # Keep a copy of the old contents 161 source_list = [] 162 with open(source_name, encoding='utf-8') as source: 163 for index, line in enumerate(source): 164 self.assertEqual(line, getline(source_name, index + 1)) 165 source_list.append(line) 166 167 with open(source_name, 'w', encoding='utf-8') as source: 168 source.write(SOURCE_2) 169 170 # Try to update a bogus cache entry 171 linecache.checkcache('dummy') 172 173 # Check that the cache matches the old contents 174 for index, line in enumerate(source_list): 175 self.assertEqual(line, getline(source_name, index + 1)) 176 177 # Update the cache and check whether it matches the new source file 178 linecache.checkcache(source_name) 179 with open(source_name, encoding='utf-8') as source: 180 for index, line in enumerate(source): 181 self.assertEqual(line, getline(source_name, index + 1)) 182 source_list.append(line) 183 184 def test_lazycache_no_globals(self): 185 lines = linecache.getlines(FILENAME) 186 linecache.clearcache() 187 self.assertEqual(False, linecache.lazycache(FILENAME, None)) 188 self.assertEqual(lines, linecache.getlines(FILENAME)) 189 190 def test_lazycache_smoke(self): 191 lines = linecache.getlines(NONEXISTENT_FILENAME, globals()) 192 linecache.clearcache() 193 self.assertEqual( 194 True, linecache.lazycache(NONEXISTENT_FILENAME, globals())) 195 self.assertEqual(1, len(linecache.cache[NONEXISTENT_FILENAME])) 196 # Note here that we're looking up a nonexistent filename with no 197 # globals: this would error if the lazy value wasn't resolved. 198 self.assertEqual(lines, linecache.getlines(NONEXISTENT_FILENAME)) 199 200 def test_lazycache_provide_after_failed_lookup(self): 201 linecache.clearcache() 202 lines = linecache.getlines(NONEXISTENT_FILENAME, globals()) 203 linecache.clearcache() 204 linecache.getlines(NONEXISTENT_FILENAME) 205 linecache.lazycache(NONEXISTENT_FILENAME, globals()) 206 self.assertEqual(lines, linecache.updatecache(NONEXISTENT_FILENAME)) 207 208 def test_lazycache_check(self): 209 linecache.clearcache() 210 linecache.lazycache(NONEXISTENT_FILENAME, globals()) 211 linecache.checkcache() 212 213 def test_lazycache_bad_filename(self): 214 linecache.clearcache() 215 self.assertEqual(False, linecache.lazycache('', globals())) 216 self.assertEqual(False, linecache.lazycache('<foo>', globals())) 217 218 def test_lazycache_already_cached(self): 219 linecache.clearcache() 220 lines = linecache.getlines(NONEXISTENT_FILENAME, globals()) 221 self.assertEqual( 222 False, 223 linecache.lazycache(NONEXISTENT_FILENAME, globals())) 224 self.assertEqual(4, len(linecache.cache[NONEXISTENT_FILENAME])) 225 226 def test_memoryerror(self): 227 lines = linecache.getlines(FILENAME) 228 self.assertTrue(lines) 229 def raise_memoryerror(*args, **kwargs): 230 raise MemoryError 231 with support.swap_attr(linecache, 'updatecache', raise_memoryerror): 232 lines2 = linecache.getlines(FILENAME) 233 self.assertEqual(lines2, lines) 234 235 linecache.clearcache() 236 with support.swap_attr(linecache, 'updatecache', raise_memoryerror): 237 lines3 = linecache.getlines(FILENAME) 238 self.assertEqual(lines3, []) 239 self.assertEqual(linecache.getlines(FILENAME), lines) 240 241 242class LineCacheInvalidationTests(unittest.TestCase): 243 def setUp(self): 244 super().setUp() 245 linecache.clearcache() 246 self.deleted_file = os_helper.TESTFN + '.1' 247 self.modified_file = os_helper.TESTFN + '.2' 248 self.unchanged_file = os_helper.TESTFN + '.3' 249 250 for fname in (self.deleted_file, 251 self.modified_file, 252 self.unchanged_file): 253 self.addCleanup(os_helper.unlink, fname) 254 with open(fname, 'w', encoding='utf-8') as source: 255 source.write(f'print("I am {fname}")') 256 257 self.assertNotIn(fname, linecache.cache) 258 linecache.getlines(fname) 259 self.assertIn(fname, linecache.cache) 260 261 os.remove(self.deleted_file) 262 with open(self.modified_file, 'w', encoding='utf-8') as source: 263 source.write('print("was modified")') 264 265 def test_checkcache_for_deleted_file(self): 266 linecache.checkcache(self.deleted_file) 267 self.assertNotIn(self.deleted_file, linecache.cache) 268 self.assertIn(self.modified_file, linecache.cache) 269 self.assertIn(self.unchanged_file, linecache.cache) 270 271 def test_checkcache_for_modified_file(self): 272 linecache.checkcache(self.modified_file) 273 self.assertIn(self.deleted_file, linecache.cache) 274 self.assertNotIn(self.modified_file, linecache.cache) 275 self.assertIn(self.unchanged_file, linecache.cache) 276 277 def test_checkcache_with_no_parameter(self): 278 linecache.checkcache() 279 self.assertNotIn(self.deleted_file, linecache.cache) 280 self.assertNotIn(self.modified_file, linecache.cache) 281 self.assertIn(self.unchanged_file, linecache.cache) 282 283 284if __name__ == "__main__": 285 unittest.main() 286