blob: 90b864634694f42e9fb86f78120066821be97c51 [file] [log] [blame]
Guido van Rossum54f22ed2000-02-04 15:10:34 +00001"""Cache lines from files.
2
3This is intended to read lines from modules imported -- hence if a filename
4is not found, it will look down the module search path for a file by
5that name.
6"""
Guido van Rossum921c8241992-01-10 14:54:42 +00007
Guido van Rossumc341c621992-03-27 15:12:43 +00008import sys
Guido van Rossum921c8241992-01-10 14:54:42 +00009import os
Guido van Rossum33f31242007-05-09 23:24:46 +000010import re
Guido van Rossum921c8241992-01-10 14:54:42 +000011
Jeremy Hylton97b2e842003-06-29 16:59:43 +000012__all__ = ["getline", "clearcache", "checkcache"]
Skip Montanaro17ab1232001-01-24 06:27:27 +000013
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000014def getline(filename, lineno, module_globals=None):
15 lines = getlines(filename, module_globals)
Guido van Rossum54f22ed2000-02-04 15:10:34 +000016 if 1 <= lineno <= len(lines):
17 return lines[lineno-1]
18 else:
19 return ''
Guido van Rossum921c8241992-01-10 14:54:42 +000020
21
22# The cache
23
24cache = {} # The cache
25
26
Guido van Rossum921c8241992-01-10 14:54:42 +000027def clearcache():
Guido van Rossum54f22ed2000-02-04 15:10:34 +000028 """Clear the cache entirely."""
Guido van Rossum921c8241992-01-10 14:54:42 +000029
Guido van Rossum54f22ed2000-02-04 15:10:34 +000030 global cache
31 cache = {}
Guido van Rossum921c8241992-01-10 14:54:42 +000032
Guido van Rossum921c8241992-01-10 14:54:42 +000033
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000034def getlines(filename, module_globals=None):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000035 """Get the lines for a file from the cache.
36 Update the cache if it doesn't contain an entry for this file already."""
Guido van Rossum921c8241992-01-10 14:54:42 +000037
Raymond Hettinger54f02222002-06-01 14:18:47 +000038 if filename in cache:
Guido van Rossum54f22ed2000-02-04 15:10:34 +000039 return cache[filename][2]
40 else:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000041 return updatecache(filename, module_globals)
Guido van Rossum921c8241992-01-10 14:54:42 +000042
Guido van Rossum921c8241992-01-10 14:54:42 +000043
Hye-Shik Chang182ac852004-10-26 09:16:42 +000044def checkcache(filename=None):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000045 """Discard cache entries that are out of date.
46 (This is not checked upon each call!)"""
Guido van Rossum921c8241992-01-10 14:54:42 +000047
Hye-Shik Chang182ac852004-10-26 09:16:42 +000048 if filename is None:
Guido van Rossumf5433482007-02-26 22:21:25 +000049 filenames = list(cache.keys())
Hye-Shik Chang182ac852004-10-26 09:16:42 +000050 else:
51 if filename in cache:
52 filenames = [filename]
53 else:
54 return
55
56 for filename in filenames:
Guido van Rossum54f22ed2000-02-04 15:10:34 +000057 size, mtime, lines, fullname = cache[filename]
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000058 if mtime is None:
59 continue # no-op for files loaded via a __loader__
Guido van Rossum54f22ed2000-02-04 15:10:34 +000060 try:
61 stat = os.stat(fullname)
62 except os.error:
63 del cache[filename]
64 continue
Raymond Hettinger32200ae2002-06-01 19:51:15 +000065 if size != stat.st_size or mtime != stat.st_mtime:
Guido van Rossum54f22ed2000-02-04 15:10:34 +000066 del cache[filename]
Guido van Rossum921c8241992-01-10 14:54:42 +000067
Guido van Rossum921c8241992-01-10 14:54:42 +000068
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000069def updatecache(filename, module_globals=None):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000070 """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."""
73
Raymond Hettinger54f02222002-06-01 14:18:47 +000074 if filename in cache:
Guido van Rossum54f22ed2000-02-04 15:10:34 +000075 del cache[filename]
76 if not filename or filename[0] + filename[-1] == '<>':
77 return []
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000078
Guido van Rossum54f22ed2000-02-04 15:10:34 +000079 fullname = filename
80 try:
81 stat = os.stat(fullname)
Guido van Rossumb940e112007-01-10 16:19:56 +000082 except os.error as msg:
Guido van Rossum54f22ed2000-02-04 15:10:34 +000083 basename = os.path.split(filename)[1]
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000084
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)
90
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:
Thomas Wouters0e3f5912006-08-11 14:57:12 +000098 if data is None:
99 # No luck, the PEP302 loader cannot find the source
100 # for this module.
101 return []
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000102 cache[filename] = (
103 len(data), None,
104 [line+'\n' for line in data.splitlines()], fullname
105 )
106 return cache[filename][2]
107
108 # Try looking through the module search path.
109
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000110 for dirname in sys.path:
Tim Peters12f21ae2001-05-29 04:27:01 +0000111 # When using imputil, sys.path may contain things other than
112 # strings; ignore them when it happens.
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000113 try:
Tim Peters12f21ae2001-05-29 04:27:01 +0000114 fullname = os.path.join(dirname, basename)
115 except (TypeError, AttributeError):
116 # Not sufficiently string-like to do anything useful with.
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000117 pass
Tim Peters12f21ae2001-05-29 04:27:01 +0000118 else:
119 try:
120 stat = os.stat(fullname)
121 break
122 except os.error:
123 pass
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000124 else:
125 # No luck
126## print '*** Cannot stat', filename, ':', msg
127 return []
Guido van Rossumd10b2dc2007-07-10 11:34:31 +0000128## print("Refreshing cache for %s..." % fullname)
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000129 try:
Jack Jansen7b8c7542002-04-14 20:12:41 +0000130 fp = open(fullname, 'rU')
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000131 lines = fp.readlines()
132 fp.close()
Guido van Rossumd10b2dc2007-07-10 11:34:31 +0000133 except Exception as msg:
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000134## print '*** Cannot open', fullname, ':', msg
135 return []
Guido van Rossum33f31242007-05-09 23:24:46 +0000136 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:
Guido van Rossum94ca1c62007-05-22 00:12:45 +0000143 lines = [line if isinstance(line, str) else str(line, coding)
144 for line in lines]
145 except:
Guido van Rossum33f31242007-05-09 23:24:46 +0000146 pass # Hope for the best
Raymond Hettinger32200ae2002-06-01 19:51:15 +0000147 size, mtime = stat.st_size, stat.st_mtime
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000148 cache[filename] = size, mtime, lines, fullname
149 return lines