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
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]
24
cache = {} # The cache
28
"""Clear the cache entirely."""
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."""
39
return cache[filename][2]
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!)"""
49
filenames = list(cache.keys())
52
filenames = [filename]
56
for filename in filenames:
57
size, mtime, lines, fullname = cache[filename]
59
continue # no-op for files loaded via a __loader__
61
stat = os.stat(fullname)
65
if size != stat.st_size or mtime != stat.st_mtime:
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."""
76
if not filename or (filename.startswith('<') and filename.endswith('>')):
81
stat = os.stat(fullname)
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:
93
data = get_source(name)
94
except (ImportError, OSError):
98
# No luck, the PEP302 loader cannot find the source
103
[line+'\n' for line in data.splitlines()], fullname
105
return cache[filename][2]
107
# Try looking through the module search path, which is only useful
108
# when handling a relative filename.
109
if os.path.isabs(filename):
112
for dirname in sys.path:
114
fullname = os.path.join(dirname, basename)
115
except (TypeError, AttributeError):
116
# Not sufficiently string-like to do anything useful with.
119
stat = os.stat(fullname)
126
with tokenize.open(fullname) as fp:
127
lines = fp.readlines()
130
if lines and not lines[-1].endswith('\n'):
132
size, mtime = stat.st_size, stat.st_mtime
133
cache[filename] = size, mtime, lines, fullname