mirror of https://github.com/python/cpython
SF bug #130306: statcache.py full of thread problems.
Fixed the thread races. Function forget_dir was also utterly Unix-specific.
This commit is contained in:
parent
64d42c5bb1
commit
0149e84af2
|
@ -3,73 +3,72 @@
|
|||
There are functions to reset the cache or to selectively remove items.
|
||||
"""
|
||||
|
||||
import os
|
||||
import os as _os
|
||||
from stat import *
|
||||
|
||||
# The cache.
|
||||
# Keys are pathnames, values are `os.stat' outcomes.
|
||||
#
|
||||
cache = {}
|
||||
# The cache. Keys are pathnames, values are os.stat outcomes.
|
||||
# Remember that multiple threads may be calling this! So, e.g., that
|
||||
# cache.has_key(path) returns 1 doesn't mean the cache will still contain
|
||||
# path on the next line. Code defensively.
|
||||
|
||||
cache = {}
|
||||
|
||||
def stat(path):
|
||||
"""Stat a file, possibly out of the cache."""
|
||||
if cache.has_key(path):
|
||||
return cache[path]
|
||||
cache[path] = ret = os.stat(path)
|
||||
ret = cache.get(path, None)
|
||||
if ret is None:
|
||||
cache[path] = ret = _os.stat(path)
|
||||
return ret
|
||||
|
||||
|
||||
def reset():
|
||||
"""Reset the cache completely."""
|
||||
global cache
|
||||
cache = {}
|
||||
|
||||
"""Clear the cache."""
|
||||
cache.clear()
|
||||
|
||||
# For thread saftey, always use forget() internally too.
|
||||
def forget(path):
|
||||
"""Remove a given item from the cache, if it exists."""
|
||||
if cache.has_key(path):
|
||||
try:
|
||||
del cache[path]
|
||||
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def forget_prefix(prefix):
|
||||
"""Remove all pathnames with a given prefix."""
|
||||
n = len(prefix)
|
||||
for path in cache.keys():
|
||||
if path[:n] == prefix:
|
||||
del cache[path]
|
||||
|
||||
if path.startswith(prefix):
|
||||
forget(path)
|
||||
|
||||
def forget_dir(prefix):
|
||||
"""Forget about a directory and all entries in it, but not about
|
||||
entries in subdirectories."""
|
||||
if prefix[-1:] == '/' and prefix != '/':
|
||||
prefix = prefix[:-1]
|
||||
forget(prefix)
|
||||
if prefix[-1:] != '/':
|
||||
prefix = prefix + '/'
|
||||
n = len(prefix)
|
||||
for path in cache.keys():
|
||||
if path[:n] == prefix:
|
||||
rest = path[n:]
|
||||
if rest[-1:] == '/': rest = rest[:-1]
|
||||
if '/' not in rest:
|
||||
del cache[path]
|
||||
"""Forget a directory and all entries except for entries in subdirs."""
|
||||
|
||||
# Remove trailing separator, if any. This is tricky to do in a
|
||||
# x-platform way. For example, Windows accepts both / and \ as
|
||||
# separators, and if there's nothing *but* a separator we want to
|
||||
# preserve that this is the root. Only os.path has the platform
|
||||
# knowledge we need.
|
||||
from os.path import split, join
|
||||
prefix = split(join(prefix, "xxx"))[0]
|
||||
forget(prefix)
|
||||
for path in cache.keys():
|
||||
# First check that the path at least starts with the prefix, so
|
||||
# that when it doesn't we can avoid paying for split().
|
||||
if path.startswith(prefix) and split(path)[0] == prefix:
|
||||
forget(path)
|
||||
|
||||
def forget_except_prefix(prefix):
|
||||
"""Remove all pathnames except with a given prefix.
|
||||
Normally used with prefix = '/' after a chdir()."""
|
||||
n = len(prefix)
|
||||
for path in cache.keys():
|
||||
if path[:n] != prefix:
|
||||
del cache[path]
|
||||
|
||||
Normally used with prefix = '/' after a chdir().
|
||||
"""
|
||||
|
||||
for path in cache.keys():
|
||||
if not path.startswith(prefix):
|
||||
forget(path)
|
||||
|
||||
def isdir(path):
|
||||
"""Check for directory."""
|
||||
"""Return 1 if directory, else 0."""
|
||||
try:
|
||||
st = stat(path)
|
||||
except os.error:
|
||||
except _os.error:
|
||||
return 0
|
||||
return S_ISDIR(st[ST_MODE])
|
||||
|
|
Loading…
Reference in New Issue