summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorTim Peters <tim.peters@gmail.com>2001-01-28 05:07:00 (GMT)
committerTim Peters <tim.peters@gmail.com>2001-01-28 05:07:00 (GMT)
commit0149e84af280523f74bc2bccb6505eb12cf5bf9c (patch)
treea1b07dd97142ee583aba1bb2358b551f570e2877 /Lib
parent64d42c5bb12a38e9a2c71ccc3570bcd7ba46a943 (diff)
downloadcpython-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.py77
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])