Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit690f5a8

Browse files
committed
Thread-safety for linecache
1 parent6020260 commit690f5a8

File tree

3 files changed

+74
-31
lines changed

3 files changed

+74
-31
lines changed

‎Lib/linecache.py

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,9 @@ def getlines(filename, module_globals=None):
3333
"""Get the lines for a Python source file from the cache.
3434
Update the cache if it doesn't contain an entry for this file already."""
3535

36-
iffilenameincache:
37-
entry=cache[filename]
38-
iflen(entry)!=1:
39-
returncache[filename][2]
36+
entry=cache.get(filename,None)
37+
ifentryisnotNoneandlen(entry)!=1:
38+
returnentry[2]
4039

4140
try:
4241
returnupdatecache(filename,module_globals)
@@ -56,10 +55,9 @@ def _make_key(code):
5655

5756
def_getlines_from_code(code):
5857
code_id=_make_key(code)
59-
ifcode_idin_interactive_cache:
60-
entry=_interactive_cache[code_id]
61-
iflen(entry)!=1:
62-
return_interactive_cache[code_id][2]
58+
entry=_interactive_cache.get(code_id,None)
59+
ifentryisnotNoneandlen(entry)!=1:
60+
returnentry[2]
6361
return []
6462

6563

@@ -84,12 +82,8 @@ def checkcache(filename=None):
8482
filenames= [filename]
8583

8684
forfilenameinfilenames:
87-
try:
88-
entry=cache[filename]
89-
exceptKeyError:
90-
continue
91-
92-
iflen(entry)==1:
85+
entry=cache.get(filename,None)
86+
ifentryisNoneorlen(entry)==1:
9387
# lazy cache entry, leave it lazy.
9488
continue
9589
size,mtime,lines,fullname=entry
@@ -125,9 +119,7 @@ def updatecache(filename, module_globals=None):
125119
# These import can fail if the interpreter is shutting down
126120
return []
127121

128-
iffilenameincache:
129-
iflen(cache[filename])!=1:
130-
cache.pop(filename,None)
122+
entry=cache.pop(filename,None)
131123
if_source_unavailable(filename):
132124
return []
133125

@@ -146,23 +138,27 @@ def updatecache(filename, module_globals=None):
146138

147139
# Realise a lazy loader based lookup if there is one
148140
# otherwise try to lookup right now.
149-
iflazycache(filename,module_globals):
141+
lazy_entry=entryifentryisnotNoneandlen(entry)==1elseNone
142+
iflazy_entryisNone:
143+
lazy_entry=_make_lazycache_entry(filename,module_globals)
144+
iflazy_entryisnotNone:
150145
try:
151-
data=cache[filename][0]()
146+
data=lazy_entry[0]()
152147
except (ImportError,OSError):
153148
pass
154149
else:
155150
ifdataisNone:
156151
# No luck, the PEP302 loader cannot find the source
157152
# for this module.
158153
return []
159-
cache[filename]= (
154+
entry= (
160155
len(data),
161156
None,
162157
[line+'\n'forlineindata.splitlines()],
163158
fullname
164159
)
165-
returncache[filename][2]
160+
cache[filename]=entry
161+
returnentry[2]
166162

167163
# Try looking through the module search path, which is only useful
168164
# when handling a relative filename.
@@ -211,13 +207,20 @@ def lazycache(filename, module_globals):
211207
get_source method must be found, the filename must be a cacheable
212208
filename, and the filename must not be already cached.
213209
"""
214-
iffilenameincache:
215-
iflen(cache[filename])==1:
216-
returnTrue
217-
else:
218-
returnFalse
210+
entry=cache.get(filename,None)
211+
ifentryisnotNone:
212+
returnlen(entry)==1
213+
214+
lazy_entry=_make_lazycache_entry(filename,module_globals)
215+
iflazy_entryisnotNone:
216+
cache[filename]=lazy_entry
217+
returnTrue
218+
returnFalse
219+
220+
221+
def_make_lazycache_entry(filename,module_globals):
219222
ifnotfilenameor (filename.startswith('<')andfilename.endswith('>')):
220-
returnFalse
223+
returnNone
221224
# Try for a __loader__, if available
222225
ifmodule_globalsand'__name__'inmodule_globals:
223226
spec=module_globals.get('__spec__')
@@ -230,9 +233,10 @@ def lazycache(filename, module_globals):
230233
ifnameandget_source:
231234
defget_lines(name=name,*args,**kwargs):
232235
returnget_source(name,*args,**kwargs)
233-
cache[filename]= (get_lines,)
234-
returnTrue
235-
returnFalse
236+
return (get_lines,)
237+
returnNone
238+
239+
236240

237241
def_register_code(code,string,name):
238242
entry= (len(string),
@@ -245,4 +249,5 @@ def _register_code(code, string, name):
245249
forconstincode.co_consts:
246250
ifisinstance(const,type(code)):
247251
stack.append(const)
248-
_interactive_cache[_make_key(code)]=entry
252+
key=_make_key(code)
253+
_interactive_cache[key]=entry

‎Lib/test/test_linecache.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
importunittest
55
importos.path
66
importtempfile
7+
importthreading
78
importtokenize
89
fromimportlib.machineryimportModuleSpec
910
fromtestimportsupport
1011
fromtest.supportimportos_helper
12+
fromtest.supportimportthreading_helper
1113
fromtest.support.script_helperimportassert_python_ok
1214

1315

@@ -374,5 +376,40 @@ def test_checkcache_with_no_parameter(self):
374376
self.assertIn(self.unchanged_file,linecache.cache)
375377

376378

379+
classFreeThreadingTest(unittest.TestCase):
380+
@threading_helper.reap_threads
381+
@threading_helper.requires_working_threading()
382+
deftest_free_threading_read_write_safety(self):
383+
384+
withtempfile.TemporaryDirectory()astmpdirname:
385+
filenames= []
386+
foriinrange(10):
387+
name=os.path.join(tmpdirname,f"test_{i}.py")
388+
withopen(name,"w")ash:
389+
h.write("import time\n")
390+
h.write("import system\n")
391+
filenames.append(name)
392+
393+
deflinecache_get_line(b):
394+
b.wait()
395+
for_inrange(100):
396+
fornameinfilenames:
397+
linecache.getline(name,1)
398+
399+
defcheck(funcs):
400+
barrier=threading.Barrier(len(funcs))
401+
threads= []
402+
403+
forfuncinfuncs:
404+
thread=threading.Thread(target=func,args=(barrier,))
405+
406+
threads.append(thread)
407+
408+
withthreading_helper.start_threads(threads):
409+
pass
410+
411+
check([linecache_get_line]*20)
412+
413+
377414
if__name__=="__main__":
378415
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix thread-safety issues in:mod:`linecache`.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp