+++ /dev/null
-"""Cache lines from files.\r
-\r
-This is intended to read lines from modules imported -- hence if a filename\r
-is not found, it will look down the module search path for a file by\r
-that name.\r
-"""\r
-\r
-import sys\r
-import os\r
-\r
-__all__ = ["getline", "clearcache", "checkcache"]\r
-\r
-def getline(filename, lineno, module_globals=None):\r
- lines = getlines(filename, module_globals)\r
- if 1 <= lineno <= len(lines):\r
- return lines[lineno-1]\r
- else:\r
- return ''\r
-\r
-\r
-# The cache\r
-\r
-cache = {} # The cache\r
-\r
-\r
-def clearcache():\r
- """Clear the cache entirely."""\r
-\r
- global cache\r
- cache = {}\r
-\r
-\r
-def getlines(filename, module_globals=None):\r
- """Get the lines for a file from the cache.\r
- Update the cache if it doesn't contain an entry for this file already."""\r
-\r
- if filename in cache:\r
- return cache[filename][2]\r
- else:\r
- return updatecache(filename, module_globals)\r
-\r
-\r
-def checkcache(filename=None):\r
- """Discard cache entries that are out of date.\r
- (This is not checked upon each call!)"""\r
-\r
- if filename is None:\r
- filenames = cache.keys()\r
- else:\r
- if filename in cache:\r
- filenames = [filename]\r
- else:\r
- return\r
-\r
- for filename in filenames:\r
- size, mtime, lines, fullname = cache[filename]\r
- if mtime is None:\r
- continue # no-op for files loaded via a __loader__\r
- try:\r
- stat = os.stat(fullname)\r
- except os.error:\r
- del cache[filename]\r
- continue\r
- if size != stat.st_size or mtime != stat.st_mtime:\r
- del cache[filename]\r
-\r
-\r
-def updatecache(filename, module_globals=None):\r
- """Update a cache entry and return its list of lines.\r
- If something's wrong, print a message, discard the cache entry,\r
- and return an empty list."""\r
-\r
- if filename in cache:\r
- del cache[filename]\r
- if not filename or (filename.startswith('<') and filename.endswith('>')):\r
- return []\r
-\r
- fullname = filename\r
- try:\r
- stat = os.stat(fullname)\r
- except OSError:\r
- basename = filename\r
-\r
- # Try for a __loader__, if available\r
- if module_globals and '__loader__' in module_globals:\r
- name = module_globals.get('__name__')\r
- loader = module_globals['__loader__']\r
- get_source = getattr(loader, 'get_source', None)\r
-\r
- if name and get_source:\r
- try:\r
- data = get_source(name)\r
- except (ImportError, IOError):\r
- pass\r
- else:\r
- if data is None:\r
- # No luck, the PEP302 loader cannot find the source\r
- # for this module.\r
- return []\r
- cache[filename] = (\r
- len(data), None,\r
- [line+'\n' for line in data.splitlines()], fullname\r
- )\r
- return cache[filename][2]\r
-\r
- # Try looking through the module search path, which is only useful\r
- # when handling a relative filename.\r
- if os.path.isabs(filename):\r
- return []\r
-\r
- for dirname in sys.path:\r
- # When using imputil, sys.path may contain things other than\r
- # strings; ignore them when it happens.\r
- try:\r
- fullname = os.path.join(dirname, basename)\r
- except (TypeError, AttributeError):\r
- # Not sufficiently string-like to do anything useful with.\r
- continue\r
- try:\r
- stat = os.stat(fullname)\r
- break\r
- except os.error:\r
- pass\r
- else:\r
- return []\r
- try:\r
- with open(fullname, 'rU') as fp:\r
- lines = fp.readlines()\r
- except IOError:\r
- return []\r
- if lines and not lines[-1].endswith('\n'):\r
- lines[-1] += '\n'\r
- size, mtime = stat.st_size, stat.st_mtime\r
- cache[filename] = size, mtime, lines, fullname\r
- return lines\r