Attachment 'findtext.py'

Download

   1 #!/usr/bin/env python
   2 """ Find texts in python source tree using Python compiler package
   3     
   4     Usage: findtext.py path
   5     
   6     Find all calls to gettext function in the source tree and collect the 
   7     texts in a dict. Use compiler to create an abstract syntax tree from 
   8     each source file, then find the nodes for gettext function call, and 
   9     get the text from the call.
  10          
  11     Localized texts are used usually translated during runtime by gettext
  12     functions and apear in the source as _('text...'). TextFinder class
  13     finds calls to the '_' function in any namespace, or your prefered
  14     gettext function.
  15     
  16     Note that TextFinder will only retrive text from function calls with 
  17     a constant argument like _("text"). Calls like _("text" % locals()),
  18     _("text 1" + "text 2") are marked as bad call in the report, and the 
  19     text is not retrived into the dictionary.
  20     
  21     Note also that texts in source can appear several times in the same file
  22     or different files, but they will only apear once in the dictinary that
  23     this tool creates.
  24     
  25     What is missing from this tool is the rather simple machinary to create 
  26     a language file from the dictionary. This machinary exist allready in
  27     MoinMoin [http://moin.sf.net] which was tool was written for.
  28     
  29        
  30     findtext - Find texts in python source tree
  31     
  32     Copyright (C) 2003 Nir Soffer
  33     
  34     Based on code by Seo Sanghyeon and the python compiler package.
  35     
  36     This program is free software; you can redistribute it and/or modify
  37     it under the terms of the GNU General Public License as published by
  38     the Free Software Foundation; either version 2 of the License, or
  39     (at your option) any later version.
  40 
  41     This program is distributed in the hope that it will be useful, but
  42     WITHOUT ANY WARRANTY; without even the implied warranty of
  43     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  44     General Public License for more details:
  45     http://www.gnu.org/licenses/gpl.html
  46 """
  47 
  48 import sys
  49 import os
  50 
  51 import compiler
  52 from compiler.ast import Name, Const, CallFunc, Getattr
  53 
  54 
  55 class TextFinder:
  56     """ Walk through AST tree and collect text from gettext calls """
  57     
  58     def __init__(self, name='_'):
  59         """ Init with the gettext function name or '_' 
  60         
  61             Each time a text is found, we check if we allready have it in the 
  62             dictionary. If we have it, we count the item as duplicate.
  63         """
  64         self._name = name       # getText function name
  65         self._dictionary = {}   # Unique texts in the found texts
  66         self._found = 0         # All good calls including duplicates
  67         self._bad = 0           # Bad calls: _('%s' % var) or _('a' + 'b')
  68     
  69     def visitModule(self, node):
  70         """ Start the visit from the top level 
  71         
  72             Reset node cache. Node cache is used to prevent us from visiting
  73             the same node twice.
  74         """
  75         self._visited = {}
  76         self.walk(node)
  77 
  78     def parseNode(self, node):
  79         """ Parse function call nodes and collect text """
  80         if node.__class__ == CallFunc and node.args: 
  81             child = node.node
  82             klass = child.__class__
  83             
  84             if (# Stanard call _('text')
  85                 (klass == Name and child.name == self._name) or
  86                 # A call to an object attribue: object._('text') 
  87                 (klass == Getattr and child.attrname == self._name)):
  88                 if node.args[0].__class__ == Const:
  89                     # Good call with a constant _('text')
  90                     self.addText(node.args[0].value)
  91                 else:
  92                     # Bad call like _('a' + 'b')
  93                     self._bad = self._bad + 1
  94                 return 1
  95         return 0
  96             
  97     def walk(self, node):
  98         """ Walk thourgh all nodes """
  99         if self._visited.has_key(node):
 100             # We visited this node allready
 101             return
 102             
 103         self._visited[node] = True            
 104         if not self.parseNode(node):           
 105             for child in node.getChildNodes():
 106                 self.walk(child)
 107     
 108     def addText(self, text):
 109         """ Add text to dictionary and count found texts.
 110         
 111         Note that number of texts in dictionary could be different from
 112         the number of texts found, because some texts apear several
 113         times in the code.
 114 
 115         """
 116 
 117         self._found = self._found + 1
 118         self._dictionary[text] = text
 119         
 120     def dictionary(self):
 121         return self._dictionary
 122             
 123     def bad(self):
 124         return self._bad
 125         
 126     def found(self):
 127         return self._found
 128         
 129 
 130 def visit(path, visitor):
 131     tree = compiler.parseFile(path)
 132     compiler.walk(tree, visitor)
 133 
 134 
 135 if __name__ == '__main__':
 136     if not sys.argv[1:2]:
 137         print 'Usage %s path' % __file__
 138         sys.exit(1)
 139         
 140     textFinder = TextFinder()
 141     top = sys.argv[1]
 142     found = 0
 143     unique = 0   
 144     bad = 0
 145 
 146     print
 147     print 'Find texts in %(top)s:' % locals()
 148     print
 149     
 150     for root, dirs, files in os.walk(top):
 151         for name in files:
 152             if name.endswith('.py'):
 153                 path = os.path.join(root, name)
 154                 visit(path, textFinder)
 155                 
 156                 # Report each file results
 157                 new_unique = len(textFinder.dictionary()) - unique
 158                 new_found = textFinder.found() - found
 159                 print '%(path)s: %(new_unique)d (of %(new_found)d)' % locals()
 160                                 
 161                 # warn about bad calls - These should be fixed!
 162                 new_bad = textFinder.bad() - bad
 163                 if new_bad:
 164                     print '### Warning: %(new_bad)d bad call(s)' % locals()
 165                     print
 166                 
 167                 unique = unique + new_unique
 168                 bad = bad + new_bad
 169                 found = found + new_found
 170 
 171     print
 172     print ('%(unique)d unique texts in dictionary of '
 173            '%(found)d texts in source') % locals()
 174     if bad:
 175         print '### %(bad)d bad calls' % locals()
 176     

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2003-12-07 18:15:54, 2.7 KB) [[attachment:check_i18n.py]]
  • [get | view] (2003-12-07 18:15:54, 6.0 KB) [[attachment:findtext.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.