Fix the tag.
[python/dscho.git] / Lib / linecache.py
blob90b864634694f42e9fb86f78120066821be97c51
1 """Cache lines from files.
3 This is intended to read lines from modules imported -- hence if a filename
4 is not found, it will look down the module search path for a file by
5 that name.
6 """
8 import sys
9 import os
10 import re
12 __all__ = ["getline", "clearcache", "checkcache"]
14 def getline(filename, lineno, module_globals=None):
15 lines = getlines(filename, module_globals)
16 if 1 <= lineno <= len(lines):
17 return lines[lineno-1]
18 else:
19 return ''
22 # The cache
24 cache = {} # The cache
27 def clearcache():
28 """Clear the cache entirely."""
30 global cache
31 cache = {}
34 def getlines(filename, module_globals=None):
35 """Get the lines for a file from the cache.
36 Update the cache if it doesn't contain an entry for this file already."""
38 if filename in cache:
39 return cache[filename][2]
40 else:
41 return updatecache(filename, module_globals)
44 def checkcache(filename=None):
45 """Discard cache entries that are out of date.
46 (This is not checked upon each call!)"""
48 if filename is None:
49 filenames = list(cache.keys())
50 else:
51 if filename in cache:
52 filenames = [filename]
53 else:
54 return
56 for filename in filenames:
57 size, mtime, lines, fullname = cache[filename]
58 if mtime is None:
59 continue # no-op for files loaded via a __loader__
60 try:
61 stat = os.stat(fullname)
62 except os.error:
63 del cache[filename]
64 continue
65 if size != stat.st_size or mtime != stat.st_mtime:
66 del cache[filename]
69 def updatecache(filename, module_globals=None):
70 """Update a cache entry and return its list of lines.
71 If something's wrong, print a message, discard the cache entry,
72 and return an empty list."""
74 if filename in cache:
75 del cache[filename]
76 if not filename or filename[0] + filename[-1] == '<>':
77 return []
79 fullname = filename
80 try:
81 stat = os.stat(fullname)
82 except os.error as msg:
83 basename = os.path.split(filename)[1]
85 # Try for a __loader__, if available
86 if module_globals and '__loader__' in module_globals:
87 name = module_globals.get('__name__')
88 loader = module_globals['__loader__']
89 get_source = getattr(loader, 'get_source', None)
91 if name and get_source:
92 if basename.startswith(name.split('.')[-1]+'.'):
93 try:
94 data = get_source(name)
95 except (ImportError, IOError):
96 pass
97 else:
98 if data is None:
99 # No luck, the PEP302 loader cannot find the source
100 # for this module.
101 return []
102 cache[filename] = (
103 len(data), None,
104 [line+'\n' for line in data.splitlines()], fullname
106 return cache[filename][2]
108 # Try looking through the module search path.
110 for dirname in sys.path:
111 # When using imputil, sys.path may contain things other than
112 # strings; ignore them when it happens.
113 try:
114 fullname = os.path.join(dirname, basename)
115 except (TypeError, AttributeError):
116 # Not sufficiently string-like to do anything useful with.
117 pass
118 else:
119 try:
120 stat = os.stat(fullname)
121 break
122 except os.error:
123 pass
124 else:
125 # No luck
126 ## print '*** Cannot stat', filename, ':', msg
127 return []
128 ## print("Refreshing cache for %s..." % fullname)
129 try:
130 fp = open(fullname, 'rU')
131 lines = fp.readlines()
132 fp.close()
133 except Exception as msg:
134 ## print '*** Cannot open', fullname, ':', msg
135 return []
136 coding = "utf-8"
137 for line in lines[:2]:
138 m = re.search(r"coding[:=]\s*([-\w.]+)", line)
139 if m:
140 coding = m.group(1)
141 break
142 try:
143 lines = [line if isinstance(line, str) else str(line, coding)
144 for line in lines]
145 except:
146 pass # Hope for the best
147 size, mtime = stat.st_size, stat.st_mtime
148 cache[filename] = size, mtime, lines, fullname
149 return lines