github.com/grumpyhome/grumpy@v0.3.1-0.20201208125205-7b775405bdf1/grumpy-runtime-src/third_party/stdlib/linecache.py (about)

     1  """Cache lines from files.
     2  
     3  This is intended to read lines from modules imported -- hence if a filename
     4  is not found, it will look down the module search path for a file by
     5  that name.
     6  """
     7  
     8  import sys
     9  import os
    10  
    11  __all__ = ["getline", "clearcache", "checkcache"]
    12  
    13  def getline(filename, lineno, module_globals=None):
    14      lines = getlines(filename, module_globals)
    15      if 1 <= lineno <= len(lines):
    16          return lines[lineno-1]
    17      else:
    18          return ''
    19  
    20  
    21  # The cache
    22  
    23  cache = {} # The cache
    24  
    25  
    26  def clearcache():
    27      """Clear the cache entirely."""
    28  
    29      global cache
    30      cache = {}
    31  
    32  
    33  def getlines(filename, module_globals=None):
    34      """Get the lines for a file from the cache.
    35      Update the cache if it doesn't contain an entry for this file already."""
    36  
    37      if filename in cache:
    38          return cache[filename][2]
    39  
    40      try:
    41          return updatecache(filename, module_globals)
    42      except MemoryError:
    43          clearcache()
    44          return []
    45  
    46  
    47  def checkcache(filename=None):
    48      """Discard cache entries that are out of date.
    49      (This is not checked upon each call!)"""
    50  
    51      if filename is None:
    52          filenames = cache.keys()
    53      else:
    54          if filename in cache:
    55              filenames = [filename]
    56          else:
    57              return
    58  
    59      for filename in filenames:
    60          size, mtime, lines, fullname = cache[filename]
    61          if mtime is None:
    62              continue   # no-op for files loaded via a __loader__
    63          try:
    64              stat = os.stat(fullname)
    65          except os.error:
    66              del cache[filename]
    67              continue
    68          if size != stat.st_size or mtime != stat.st_mtime:
    69              del cache[filename]
    70  
    71  
    72  def updatecache(filename, module_globals=None):
    73      """Update a cache entry and return its list of lines.
    74      If something's wrong, print a message, discard the cache entry,
    75      and return an empty list."""
    76  
    77      if filename in cache:
    78          del cache[filename]
    79      if not filename or (filename.startswith('<') and filename.endswith('>')):
    80          return []
    81  
    82      fullname = filename
    83      try:
    84          stat = os.stat(fullname)
    85      except OSError:
    86          basename = filename
    87  
    88          # Try for a __loader__, if available
    89          if module_globals and '__loader__' in module_globals:
    90              name = module_globals.get('__name__')
    91              loader = module_globals['__loader__']
    92              get_source = getattr(loader, 'get_source', None)
    93  
    94              if name and get_source:
    95                  try:
    96                      data = get_source(name)
    97                  except (ImportError, IOError):
    98                      pass
    99                  else:
   100                      if data is None:
   101                          # No luck, the PEP302 loader cannot find the source
   102                          # for this module.
   103                          return []
   104                      cache[filename] = (
   105                          len(data), None,
   106                          [line+'\n' for line in data.splitlines()], fullname
   107                      )
   108                      return cache[filename][2]
   109  
   110          # Try looking through the module search path, which is only useful
   111          # when handling a relative filename.
   112          if os.path.isabs(filename):
   113              return []
   114  
   115          for dirname in sys.path:
   116              # When using imputil, sys.path may contain things other than
   117              # strings; ignore them when it happens.
   118              try:
   119                  fullname = os.path.join(dirname, basename)
   120              except (TypeError, AttributeError):
   121                  # Not sufficiently string-like to do anything useful with.
   122                  continue
   123              try:
   124                  stat = os.stat(fullname)
   125                  break
   126              except os.error:
   127                  pass
   128          else:
   129              return []
   130      try:
   131          with open(fullname, 'rU') as fp:
   132              lines = fp.readlines()
   133      except IOError:
   134          return []
   135      if lines and not lines[-1].endswith('\n'):
   136          lines[-1] += '\n'
   137      size, mtime = stat.st_size, stat.st_mtime
   138      cache[filename] = size, mtime, lines, fullname
   139      return lines