Skip to content

Commit a19749b

Browse files
committed
Thread-safety for linecache
1 parent 6020260 commit a19749b

File tree

1 file changed

+21
-24
lines changed

1 file changed

+21
-24
lines changed

Lib/linecache.py

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
is not found, it will look down the module search path for a file by
55
that name.
66
"""
7+
import threading
78

89
__all__ = ["getline", "clearcache", "checkcache", "lazycache"]
910

@@ -12,6 +13,7 @@
1213
# or a tuple (size, mtime, lines, fullname) once loaded.
1314
cache = {}
1415
_interactive_cache = {}
16+
mutex = threading.Lock()
1517

1618

1719
def clearcache():
@@ -33,10 +35,9 @@ def getlines(filename, module_globals=None):
3335
"""Get the lines for a Python source file from the cache.
3436
Update the cache if it doesn't contain an entry for this file already."""
3537

36-
if filename in cache:
37-
entry = cache[filename]
38-
if len(entry) != 1:
39-
return cache[filename][2]
38+
entry = cache.get(filename, None)
39+
if entry is not None and len(entry) != 1:
40+
return entry[2]
4041

4142
try:
4243
return updatecache(filename, module_globals)
@@ -56,10 +57,9 @@ def _make_key(code):
5657

5758
def _getlines_from_code(code):
5859
code_id = _make_key(code)
59-
if code_id in _interactive_cache:
60-
entry = _interactive_cache[code_id]
61-
if len(entry) != 1:
62-
return _interactive_cache[code_id][2]
60+
entry = _interactive_cache.get(code_id, None)
61+
if entry is not None and len(entry) != 1:
62+
return entry[2]
6363
return []
6464

6565

@@ -84,12 +84,8 @@ def checkcache(filename=None):
8484
filenames = [filename]
8585

8686
for filename in filenames:
87-
try:
88-
entry = cache[filename]
89-
except KeyError:
90-
continue
91-
92-
if len(entry) == 1:
87+
entry = cache.get(filename, None)
88+
if entry is None or len(entry) == 1:
9389
# lazy cache entry, leave it lazy.
9490
continue
9591
size, mtime, lines, fullname = entry
@@ -125,8 +121,9 @@ def updatecache(filename, module_globals=None):
125121
# These import can fail if the interpreter is shutting down
126122
return []
127123

128-
if filename in cache:
129-
if len(cache[filename]) != 1:
124+
with mutex:
125+
entry = cache.get(filename, None)
126+
if entry is not None and len(entry) != 1:
130127
cache.pop(filename, None)
131128
if _source_unavailable(filename):
132129
return []
@@ -156,13 +153,14 @@ def updatecache(filename, module_globals=None):
156153
# No luck, the PEP302 loader cannot find the source
157154
# for this module.
158155
return []
159-
cache[filename] = (
156+
entry = (
160157
len(data),
161158
None,
162159
[line + '\n' for line in data.splitlines()],
163160
fullname
164161
)
165-
return cache[filename][2]
162+
cache[filename] = entry
163+
return entry[2]
166164

167165
# Try looking through the module search path, which is only useful
168166
# when handling a relative filename.
@@ -211,11 +209,9 @@ def lazycache(filename, module_globals):
211209
get_source method must be found, the filename must be a cacheable
212210
filename, and the filename must not be already cached.
213211
"""
214-
if filename in cache:
215-
if len(cache[filename]) == 1:
216-
return True
217-
else:
218-
return False
212+
entry = cache.get(filename, None)
213+
if entry is not None:
214+
return len(entry) == 1
219215
if not filename or (filename.startswith('<') and filename.endswith('>')):
220216
return False
221217
# Try for a __loader__, if available
@@ -245,4 +241,5 @@ def _register_code(code, string, name):
245241
for const in code.co_consts:
246242
if isinstance(const, type(code)):
247243
stack.append(const)
248-
_interactive_cache[_make_key(code)] = entry
244+
key = _make_key(code)
245+
_interactive_cache[key] = entry

0 commit comments

Comments
 (0)