diff options
author | Tim Peters <tim.peters@gmail.com> | 2001-01-28 05:07:00 (GMT) |
---|---|---|
committer | Tim Peters <tim.peters@gmail.com> | 2001-01-28 05:07:00 (GMT) |
commit | 0149e84af280523f74bc2bccb6505eb12cf5bf9c (patch) | |
tree | a1b07dd97142ee583aba1bb2358b551f570e2877 /Lib | |
parent | 64d42c5bb12a38e9a2c71ccc3570bcd7ba46a943 (diff) | |
download | cpython-0149e84af280523f74bc2bccb6505eb12cf5bf9c.zip cpython-0149e84af280523f74bc2bccb6505eb12cf5bf9c.tar.gz cpython-0149e84af280523f74bc2bccb6505eb12cf5bf9c.tar.bz2 |
SF bug #130306: statcache.py full of thread problems.
Fixed the thread races. Function forget_dir was also utterly Unix-specific.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/statcache.py | 77 |
1 files changed, 38 insertions, 39 deletions
diff --git a/Lib/statcache.py b/Lib/statcache.py index 85a3e79..056ec40 100644 --- a/Lib/statcache.py +++ b/Lib/statcache.py @@ -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 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) - 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] - + # 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]) |