Guido van Rossum | 4b8c6ea | 2000-02-04 15:39:30 +0000 | [diff] [blame] | 1 | """Maintain a cache of stat() information on files. |
| 2 | |
Guido van Rossum | e7b146f | 2000-02-04 15:28:42 +0000 | [diff] [blame] | 3 | There are functions to reset the cache or to selectively remove items. |
| 4 | """ |
Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 5 | |
Tim Peters | 0149e84 | 2001-01-28 05:07:00 +0000 | [diff] [blame] | 6 | import os as _os |
Guido van Rossum | 40d9304 | 1990-10-21 16:17:34 +0000 | [diff] [blame] | 7 | from stat import * |
Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 8 | |
Tim Peters | 0149e84 | 2001-01-28 05:07:00 +0000 | [diff] [blame] | 9 | # The cache. Keys are pathnames, values are os.stat outcomes. |
| 10 | # Remember that multiple threads may be calling this! So, e.g., that |
| 11 | # cache.has_key(path) returns 1 doesn't mean the cache will still contain |
| 12 | # path on the next line. Code defensively. |
Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 13 | |
Tim Peters | 0149e84 | 2001-01-28 05:07:00 +0000 | [diff] [blame] | 14 | cache = {} |
Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 15 | |
Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 16 | def stat(path): |
Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 17 | """Stat a file, possibly out of the cache.""" |
Tim Peters | 0149e84 | 2001-01-28 05:07:00 +0000 | [diff] [blame] | 18 | ret = cache.get(path, None) |
| 19 | if ret is None: |
| 20 | cache[path] = ret = _os.stat(path) |
Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 21 | return ret |
Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 22 | |
Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 23 | def reset(): |
Tim Peters | 0149e84 | 2001-01-28 05:07:00 +0000 | [diff] [blame] | 24 | """Clear the cache.""" |
| 25 | cache.clear() |
Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 26 | |
Tim Peters | 0149e84 | 2001-01-28 05:07:00 +0000 | [diff] [blame] | 27 | # For thread saftey, always use forget() internally too. |
Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 28 | def forget(path): |
Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 29 | """Remove a given item from the cache, if it exists.""" |
Tim Peters | 0149e84 | 2001-01-28 05:07:00 +0000 | [diff] [blame] | 30 | try: |
Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 31 | del cache[path] |
Tim Peters | 0149e84 | 2001-01-28 05:07:00 +0000 | [diff] [blame] | 32 | except KeyError: |
| 33 | pass |
Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 34 | |
Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 35 | def forget_prefix(prefix): |
Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 36 | """Remove all pathnames with a given prefix.""" |
Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 37 | for path in cache.keys(): |
Tim Peters | 0149e84 | 2001-01-28 05:07:00 +0000 | [diff] [blame] | 38 | if path.startswith(prefix): |
| 39 | forget(path) |
Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 40 | |
Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 41 | def forget_dir(prefix): |
Tim Peters | 0149e84 | 2001-01-28 05:07:00 +0000 | [diff] [blame] | 42 | """Forget a directory and all entries except for entries in subdirs.""" |
Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 43 | |
Tim Peters | 0149e84 | 2001-01-28 05:07:00 +0000 | [diff] [blame] | 44 | # Remove trailing separator, if any. This is tricky to do in a |
| 45 | # x-platform way. For example, Windows accepts both / and \ as |
| 46 | # separators, and if there's nothing *but* a separator we want to |
| 47 | # preserve that this is the root. Only os.path has the platform |
| 48 | # knowledge we need. |
| 49 | from os.path import split, join |
| 50 | prefix = split(join(prefix, "xxx"))[0] |
| 51 | forget(prefix) |
| 52 | for path in cache.keys(): |
| 53 | # First check that the path at least starts with the prefix, so |
| 54 | # that when it doesn't we can avoid paying for split(). |
| 55 | if path.startswith(prefix) and split(path)[0] == prefix: |
| 56 | forget(path) |
Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 57 | |
Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 58 | def forget_except_prefix(prefix): |
Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 59 | """Remove all pathnames except with a given prefix. |
Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 60 | |
Tim Peters | 0149e84 | 2001-01-28 05:07:00 +0000 | [diff] [blame] | 61 | Normally used with prefix = '/' after a chdir(). |
| 62 | """ |
| 63 | |
| 64 | for path in cache.keys(): |
| 65 | if not path.startswith(prefix): |
| 66 | forget(path) |
Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 67 | |
Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 68 | def isdir(path): |
Tim Peters | 0149e84 | 2001-01-28 05:07:00 +0000 | [diff] [blame] | 69 | """Return 1 if directory, else 0.""" |
Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 70 | try: |
| 71 | st = stat(path) |
Tim Peters | 0149e84 | 2001-01-28 05:07:00 +0000 | [diff] [blame] | 72 | except _os.error: |
Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 73 | return 0 |
| 74 | return S_ISDIR(st[ST_MODE]) |