test_linecache.py 9.6 KB

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