"Fossies" - the Fresh Open Source Software Archive

Member "cheetah3-3.2.6.post2/Cheetah/FileUtils.py" (20 Apr 2021, 10497 Bytes) of package /linux/www/cheetah3-3.2.6.post2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "FileUtils.py" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 3-3.2.1_vs_3-3.2.2.

    1 
    2 from glob import glob
    3 import os
    4 import os.path
    5 import re
    6 from tempfile import NamedTemporaryFile
    7 from Cheetah.compat import string_type
    8 
    9 
   10 def _escapeRegexChars(
   11         txt, escapeRE=re.compile(r'([\$\^\*\+\.\?\{\}\[\]\(\)\|\\])')):
   12     return escapeRE.sub(r'\\\1', txt)
   13 
   14 
   15 def findFiles(*args, **kw):
   16     """Recursively find all the files matching a glob pattern.
   17 
   18     This function is a wrapper around the FileFinder class.  See its docstring
   19     for details about the accepted arguments, etc."""
   20 
   21     return FileFinder(*args, **kw).files()
   22 
   23 
   24 def replaceStrInFiles(files, theStr, repl):
   25 
   26     """Replace all instances of 'theStr' with 'repl' for each file in the 'files'
   27     list. Returns a dictionary with data about the matches found.
   28 
   29     This is like string.replace() on a multi-file basis.
   30 
   31     This function is a wrapper around the FindAndReplace class. See its
   32     docstring for more details."""
   33 
   34     pattern = _escapeRegexChars(theStr)
   35     return FindAndReplace(files, pattern, repl).results()
   36 
   37 
   38 def replaceRegexInFiles(files, pattern, repl):
   39 
   40     """Replace all instances of regex 'pattern' with 'repl' for each file in the
   41     'files' list. Returns a dictionary with data about the matches found.
   42 
   43     This is like re.sub on a multi-file basis.
   44 
   45     This function is a wrapper around the FindAndReplace class. See its
   46     docstring for more details."""
   47 
   48     return FindAndReplace(files, pattern, repl).results()
   49 
   50 
   51 ##################################################
   52 # CLASSES
   53 
   54 class FileFinder:
   55 
   56     """Traverses a directory tree and finds all files in it that match one of
   57     the specified glob patterns."""
   58 
   59     def __init__(self, rootPath,
   60                  globPatterns=('*',),
   61                  ignoreBasenames=('CVS', '.svn'),
   62                  ignoreDirs=(),
   63                  ):
   64 
   65         self._rootPath = rootPath
   66         self._globPatterns = globPatterns
   67         self._ignoreBasenames = ignoreBasenames
   68         self._ignoreDirs = ignoreDirs
   69         self._files = []
   70 
   71         self.walkDirTree(rootPath)
   72 
   73     def walkDirTree(self, dir='.',
   74 
   75                     listdir=os.listdir,
   76                     isdir=os.path.isdir,
   77                     join=os.path.join,
   78                     ):
   79 
   80         """Recursively walk through a directory tree and find matching files"""
   81         processDir = self.processDir
   82         filterDir = self.filterDir
   83 
   84         pendingDirs = [dir]
   85         addDir = pendingDirs.append
   86         getDir = pendingDirs.pop
   87 
   88         while pendingDirs:
   89             dir = getDir()
   90             #  process this dir
   91             processDir(dir)
   92 
   93             # and add sub-dirs
   94             for baseName in listdir(dir):
   95                 fullPath = join(dir, baseName)
   96                 if isdir(fullPath):
   97                     if filterDir(baseName, fullPath):
   98                         addDir(fullPath)
   99 
  100     def filterDir(self, baseName, fullPath):
  101 
  102         """A hook for filtering out certain dirs. """
  103 
  104         return not (baseName in self._ignoreBasenames
  105                     or fullPath in self._ignoreDirs)
  106 
  107     def processDir(self, dir, glob=glob):
  108         extend = self._files.extend
  109         for pattern in self._globPatterns:
  110             extend(glob(os.path.join(dir, pattern)))
  111 
  112     def files(self):
  113         return self._files
  114 
  115 
  116 class _GenSubberFunc:
  117 
  118     """Converts a 'sub' string in the form that one feeds to re.sub (backrefs,
  119     groups, etc.) into a function that can be used to do the substitutions in
  120     the FindAndReplace class."""
  121 
  122     backrefRE = re.compile(r'\\([1-9][0-9]*)')
  123     groupRE = re.compile(r'\\g<([a-zA-Z_][a-zA-Z_]*)>')
  124 
  125     def __init__(self, replaceStr):
  126         self._src = replaceStr
  127         self._pos = 0
  128         self._codeChunks = []
  129         self.parse()
  130 
  131     def src(self):
  132         return self._src
  133 
  134     def pos(self):
  135         return self._pos
  136 
  137     def setPos(self, pos):
  138         self._pos = pos
  139 
  140     def atEnd(self):
  141         return self._pos >= len(self._src)
  142 
  143     def advance(self, offset=1):
  144         self._pos += offset
  145 
  146     def readTo(self, to, start=None):
  147         if start is None:
  148             start = self._pos
  149         self._pos = to
  150         if self.atEnd():
  151             return self._src[start:]
  152         else:
  153             return self._src[start:to]
  154 
  155     # match and get methods
  156 
  157     def matchBackref(self):
  158         return self.backrefRE.match(self.src(), self.pos())
  159 
  160     def getBackref(self):
  161         m = self.matchBackref()
  162         self.setPos(m.end())
  163         return m.group(1)
  164 
  165     def matchGroup(self):
  166         return self.groupRE.match(self.src(), self.pos())
  167 
  168     def getGroup(self):
  169         m = self.matchGroup()
  170         self.setPos(m.end())
  171         return m.group(1)
  172 
  173     # main parse loop and the eat methods
  174 
  175     def parse(self):
  176         while not self.atEnd():
  177             if self.matchBackref():
  178                 self.eatBackref()
  179             elif self.matchGroup():
  180                 self.eatGroup()
  181             else:
  182                 self.eatStrConst()
  183 
  184     def eatStrConst(self):
  185         startPos = self.pos()
  186         while not self.atEnd():
  187             if self.matchBackref() or self.matchGroup():
  188                 break
  189             else:
  190                 self.advance()
  191         strConst = self.readTo(self.pos(), start=startPos)
  192         self.addChunk(repr(strConst))
  193 
  194     def eatBackref(self):
  195         self.addChunk('m.group(' + self.getBackref() + ')')
  196 
  197     def eatGroup(self):
  198         self.addChunk('m.group("' + self.getGroup() + '")')
  199 
  200     def addChunk(self, chunk):
  201         self._codeChunks.append(chunk)
  202 
  203     # code wrapping methods
  204 
  205     def codeBody(self):
  206         return ', '.join(self._codeChunks)
  207 
  208     def code(self):
  209         return "def subber(m):\n\treturn ''.join([%s])\n" % (self.codeBody())
  210 
  211     def subberFunc(self):
  212         exec(self.code())
  213         return subber  # noqa: F821 undefined name 'subber'
  214 
  215 
  216 class FindAndReplace:
  217 
  218     """Find and replace all instances of 'patternOrRE' with 'replacement' for
  219     each file in the 'files' list. This is a multi-file version of re.sub().
  220 
  221     'patternOrRE' can be a raw regex pattern or
  222     a regex object as generated by the re module. 'replacement' can be any
  223     string that would work with patternOrRE.sub(replacement, fileContents).
  224     """
  225 
  226     def __init__(self, files, patternOrRE, replacement,
  227                  recordResults=True):
  228 
  229         if isinstance(patternOrRE, string_type):
  230             self._regex = re.compile(patternOrRE)
  231         else:
  232             self._regex = patternOrRE
  233         if isinstance(replacement, string_type):
  234             self._subber = _GenSubberFunc(replacement).subberFunc()
  235         else:
  236             self._subber = replacement
  237 
  238         self._pattern = pattern = self._regex.pattern
  239         self._files = files
  240         self._results = {}
  241         self._recordResults = recordResults
  242 
  243         # see if we should use pgrep to do the file matching
  244         self._usePgrep = False
  245         if (os.popen3('pgrep')[2].read()).startswith('Usage:'):
  246             # now check to make sure pgrep understands the pattern
  247             tmpFile = NamedTemporaryFile()
  248             tmpFile.write('#')
  249             if not (os.popen3(
  250                     'pgrep "' + pattern + '" ' + tmpFile.name)[2].read()):
  251                 # it didn't print an error msg so we're ok
  252                 self._usePgrep = True
  253             tmpFile.close()  # Will be automatically removed on close
  254 
  255         self._run()
  256 
  257     def results(self):
  258         return self._results
  259 
  260     def _run(self):
  261         regex = self._regex
  262         subber = self._subDispatcher
  263         usePgrep = self._usePgrep
  264         pattern = self._pattern
  265         for file in self._files:
  266             if not os.path.isfile(file):
  267                 continue  # skip dirs etc.
  268 
  269             self._currFile = file
  270             found = False
  271             if 'orig' in locals():
  272                 del orig
  273             if self._usePgrep:
  274                 if os.popen('pgrep "' + pattern + '" ' + file).read():
  275                     found = True
  276             else:
  277                 orig = open(file).read()
  278                 if regex.search(orig):
  279                     found = True
  280             if found:
  281                 if 'orig' not in locals():
  282                     orig = open(file).read()
  283                 new = regex.sub(subber, orig)
  284                 open(file, 'w').write(new)
  285 
  286     def _subDispatcher(self, match):
  287         if self._recordResults:
  288             if self._currFile not in self._results:
  289                 res = self._results[self._currFile] = {}
  290                 res['count'] = 0
  291                 res['matches'] = []
  292             else:
  293                 res = self._results[self._currFile]
  294             res['count'] += 1
  295             res['matches'].append({'contents': match.group(),
  296                                    'start': match.start(),
  297                                    'end': match.end(),
  298                                    }
  299                                   )
  300         return self._subber(match)
  301 
  302 
  303 class SourceFileStats:
  304 
  305     """
  306     """
  307 
  308     _fileStats = None
  309 
  310     def __init__(self, files):
  311         self._fileStats = stats = {}
  312         for file in files:
  313             stats[file] = self.getFileStats(file)
  314 
  315     def rawStats(self):
  316         return self._fileStats
  317 
  318     def summary(self):
  319         codeLines = 0
  320         blankLines = 0
  321         commentLines = 0
  322         totalLines = 0
  323         for fileStats in self.rawStats().values():
  324             codeLines += fileStats['codeLines']
  325             blankLines += fileStats['blankLines']
  326             commentLines += fileStats['commentLines']
  327             totalLines += fileStats['totalLines']
  328 
  329         stats = {'codeLines': codeLines,
  330                  'blankLines': blankLines,
  331                  'commentLines': commentLines,
  332                  'totalLines': totalLines,
  333                  }
  334         return stats
  335 
  336     def printStats(self):
  337         pass
  338 
  339     def getFileStats(self, fileName):
  340         codeLines = 0
  341         blankLines = 0
  342         commentLines = 0
  343         commentLineRe = re.compile(r'\s#.*$')
  344         blankLineRe = re.compile('\s$')
  345         lines = open(fileName).read().splitlines()
  346         totalLines = len(lines)
  347 
  348         for line in lines:
  349             if commentLineRe.match(line):
  350                 commentLines += 1
  351             elif blankLineRe.match(line):
  352                 blankLines += 1
  353             else:
  354                 codeLines += 1
  355 
  356         stats = {'codeLines': codeLines,
  357                  'blankLines': blankLines,
  358                  'commentLines': commentLines,
  359                  'totalLines': totalLines,
  360                  }
  361 
  362         return stats