[Zope3-checkins] SVN: Zope3/branches/ZopeX3-3.0/ Added a couple of useful scripts to the distribution.

Stephan Richter srichter at cosmos.phy.tufts.edu
Wed Sep 1 19:47:48 EDT 2004


Log message for revision 27399:
  Added a couple of useful scripts to the distribution.
  


Changed:
  U   Zope3/branches/ZopeX3-3.0/src/zope/app/locales/extract.py
  A   Zope3/branches/ZopeX3-3.0/zopeskel/bin/i18nextract.in
  A   Zope3/branches/ZopeX3-3.0/zopeskel/bin/i18nmergeall.in
  A   Zope3/branches/ZopeX3-3.0/zopeskel/bin/i18nstats.in
  A   Zope3/branches/ZopeX3-3.0/zopeskel/bin/importchecker.in
  A   Zope3/branches/ZopeX3-3.0/zopeskel/bin/pyskel.in


-=-
Modified: Zope3/branches/ZopeX3-3.0/src/zope/app/locales/extract.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/locales/extract.py	2004-09-01 21:13:44 UTC (rev 27398)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/locales/extract.py	2004-09-01 23:47:48 UTC (rev 27399)
@@ -306,14 +306,15 @@
     # strings have the domain the user specified.   
     return eater.getCatalog()
 
-def zcml_strings(dir, domain="zope"):
+def zcml_strings(dir, domain="zope", site_zcml=None):
     """Retrieve all ZCML messages from dir that are in the domain.
     """
     from zope.app.appsetup import config
     import zope
     dirname = os.path.dirname
-    site_zcml = os.path.join(dirname(dirname(dirname(zope.__file__))),
-                             "site.zcml")
+    if site_zcml is None:
+        site_zcml = os.path.join(dirname(dirname(dirname(zope.__file__))),
+                                 "site.zcml")
     context = config(site_zcml, execute=False)
     return context.i18n_strings.get(domain, {})
 

Added: Zope3/branches/ZopeX3-3.0/zopeskel/bin/i18nextract.in
===================================================================
--- Zope3/branches/ZopeX3-3.0/zopeskel/bin/i18nextract.in	2004-09-01 21:13:44 UTC (rev 27398)
+++ Zope3/branches/ZopeX3-3.0/zopeskel/bin/i18nextract.in	2004-09-01 23:47:48 UTC (rev 27399)
@@ -0,0 +1,158 @@
+#!<<PYTHON>>
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Program to extract internationalization markup from Python Code,
+Page Templates and ZCML.
+
+This tool will extract all findable message strings from all
+internationalizable files in your Zope 3 product. It only extracts message ids
+of the specified domain. It defaults to the 'zope' domain and the zope
+package.
+
+Note: The Python Code extraction tool does not support domain
+      registration, so that all message strings are returned for
+      Python code.
+
+Note: The script expects to be executed either from inside the Zope 3 source
+      tree or with the Zope 3 source tree on the Python path.  Execution from
+      a symlinked directory inside the Zope 3 source tree will not work.
+
+Usage: i18nextract.py [options]
+Options:
+    -h / --help
+        Print this message and exit.
+    -d / --domain <domain>
+        Specifies the domain that is supposed to be extracted (i.e. 'zope')
+    -p / --path <path>
+        Specifies the package that is supposed to be searched
+        (i.e. 'zope/app')
+    -o dir
+        Specifies a directory, relative to the package in which to put the
+        output translation template.
+
+$Id$
+"""
+import os, sys, getopt
+
+SOFTWARE_HOME = "<<SOFTWARE_HOME>>"
+INSTANCE_HOME = "<<INSTANCE_HOME>>"
+
+def usage(code, msg=''):
+    # Python 2.1 required
+    print >> sys.stderr, __doc__
+    if msg:
+        print >> sys.stderr, msg
+    sys.exit(code)
+
+def app_dir():
+    try:
+        import zope
+    except ImportError:
+        # Couldn't import zope, need to add something to the Python path
+
+        # Get the path of the src
+        path = os.path.abspath(os.getcwd())
+        while not path.endswith('src'):
+            parentdir = os.path.dirname(path)
+            if path == parentdir:
+                # root directory reached
+                break
+            path = parentdir
+        sys.path.insert(0, path)
+
+        try:
+            import zope
+        except ImportError:
+            usage(1, "Make sure the script has been executed "
+                     "inside Zope 3 source tree.")
+
+    return os.path.dirname(zope.__file__)
+
+def main(argv=sys.argv):
+    try:
+        opts, args = getopt.getopt(
+            sys.argv[1:],
+            'hd:p:o:',
+            ['help', 'domain=', 'path=', 'python-only'])
+    except getopt.error, msg:
+        usage(1, msg)
+
+    domain = 'zope'
+    path = app_dir()
+    include_default_domain = True
+    output_dir = None
+    python_only = None
+    for opt, arg in opts:
+        if opt in ('-h', '--help'):
+            usage(0)
+        elif opt in ('-d', '--domain'):
+            domain = arg
+            include_default_domain = False
+        elif opt in ('-o', ):
+            output_dir = arg
+        elif opt in ('--python-only',):
+            python_only = True
+        elif opt in ('-p', '--path'):
+            if not os.path.exists(arg):
+                usage(1, 'The specified path does not exist.')
+            path = arg
+            # We might not have an absolute path passed in.
+            if not path == os.path.abspath(path):
+                cwd = os.getcwd()
+                # This is for symlinks. Thanks to Fred for this trick.
+                if os.environ.has_key('PWD'):
+                    cwd = os.environ['PWD']
+                path = os.path.normpath(os.path.join(cwd, arg))
+
+    # When generating the comments, we will not need the base directory info,
+    # since it is specific to everyone's installation
+    src_start = path.rfind('lib/python')
+    base_dir = path[:src_start]
+
+    output_file = domain+'.pot'
+    if output_dir:
+        output_dir = os.path.join(path, output_dir)
+        if not os.path.exists(output_dir):
+            os.mkdir(output_dir)
+        output_file = os.path.join(output_dir, output_file)
+
+    print "base path: %r\nsearch path: %r\ndomain: %r\noutput file: %r" \
+        % (base_dir, path, domain, output_file)
+
+    from zope.app.locales.extract import POTMaker, \
+         py_strings, tal_strings, zcml_strings
+
+    maker = POTMaker(output_file, path)
+    maker.add(py_strings(path, domain), base_dir)
+    if not python_only:
+        site_zcml = os.path.join(INSTANCE_HOME, 'etc', 'site.zcml')
+        maker.add(zcml_strings(path, domain, site_zcml), base_dir)
+        maker.add(tal_strings(path, domain, include_default_domain), base_dir)
+    maker.write()
+
+
+def run():
+    # This removes the script directory from sys.path, which we do
+    # since there are no modules here.
+    #
+    basepath = filter(None, sys.path)
+
+    sys.path[:] = [os.path.join(INSTANCE_HOME, "lib", "python"),
+                   SOFTWARE_HOME] + basepath
+
+    main()
+
+
+if __name__ == '__main__':
+    run()


Property changes on: Zope3/branches/ZopeX3-3.0/zopeskel/bin/i18nextract.in
___________________________________________________________________
Name: svn:executable
   + *

Added: Zope3/branches/ZopeX3-3.0/zopeskel/bin/i18nmergeall.in
===================================================================
--- Zope3/branches/ZopeX3-3.0/zopeskel/bin/i18nmergeall.in	2004-09-01 21:13:44 UTC (rev 27398)
+++ Zope3/branches/ZopeX3-3.0/zopeskel/bin/i18nmergeall.in	2004-09-01 23:47:48 UTC (rev 27399)
@@ -0,0 +1,87 @@
+#!<<PYTHON>>
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Merge a POT file with all languages
+
+This utility requires the GNU gettext package to be installed. The command
+'msgmerge' will be executed for each language.
+
+Usage: i18mergeall.py [options]
+Options:
+
+    -h / --help
+        Print this message and exit.
+
+    -l / --locales-dir
+        Specify the 'locales' directory for which to generate the statistics.
+
+$Id: i18nmergeall.py 26446 2004-07-13 16:11:36Z philikon $
+"""
+import sys
+import os
+import getopt
+
+def usage(code, msg=''):
+    """Display help."""
+    print >> sys.stderr, '\n'.join(__doc__.split('\n')[:-2])
+    if msg:
+        print >> sys.stderr, '** Error: ' + str(msg) + ' **'
+    sys.exit(code)
+
+
+def main(path):
+    for language in os.listdir(path):
+        lc_messages_path = os.path.join(path, language, 'LC_MESSAGES')
+
+        # English is the default for Zope, so ignore it
+        if language == 'en':
+            continue
+
+        # Make sure we got a language directory
+        if not os.path.isdir(lc_messages_path):
+            continue
+
+        msgs = []
+        for domain_file in os.listdir(lc_messages_path):
+            if domain_file.endswith('.po'):
+                domain_path = os.path.join(lc_messages_path, domain_file)
+                pot_path = os.path.join(path, domain_file+'t')
+                domain = domain_file.split('.')[0]
+                print 'Merging language "%s", domain "%s"' %(language, domain)
+                os.system('msgmerge -U %s %s' %(domain_path, pot_path))
+
+
+if __name__ == '__main__':
+    try:
+        opts, args = getopt.getopt(
+            sys.argv[1:],
+            'l:h',
+            ['help', 'locals-dir='])
+    except getopt.error, msg:
+        usage(1, msg)
+
+    path = None
+    for opt, arg in opts:
+        if opt in ('-h', '--help'):
+            usage(0)
+        elif opt in ('-l', '--locales-dir'):
+            cwd = os.getcwd()
+            # This is for symlinks. Thanks to Fred for this trick.
+            if os.environ.has_key('PWD'):
+                cwd = os.environ['PWD']
+            path = os.path.normpath(os.path.join(cwd, arg))
+
+    if path is None:
+        usage(1, 'You must specify the path to the locales directory.')
+    main(path)


Property changes on: Zope3/branches/ZopeX3-3.0/zopeskel/bin/i18nmergeall.in
___________________________________________________________________
Name: svn:executable
   + *

Added: Zope3/branches/ZopeX3-3.0/zopeskel/bin/i18nstats.in
===================================================================
--- Zope3/branches/ZopeX3-3.0/zopeskel/bin/i18nstats.in	2004-09-01 21:13:44 UTC (rev 27398)
+++ Zope3/branches/ZopeX3-3.0/zopeskel/bin/i18nstats.in	2004-09-01 23:47:48 UTC (rev 27399)
@@ -0,0 +1,167 @@
+#!<<PYTHON>>
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Translation Statistics Utility
+
+Utility to determine the status of the translations.
+
+Usage: i18nstats.py [options]
+Options:
+
+    -h / --help
+        Print this message and exit.
+
+    -l / --locales-dir
+        Specify the 'locales' directory for which to generate the statistics.
+
+$Id$
+"""
+import sys
+import os
+import getopt
+
+SEARCHING = 0
+COMMENT = 1
+MSGID = 2
+MSGSTR = 3
+MSGDONE = 4
+
+def usage(code, msg=''):
+    """Display help."""
+    print >> sys.stderr, '\n'.join(__doc__.split('\n')[:-2])
+    if msg:
+        print >> sys.stderr, '** Error: ' + str(msg) + ' **'
+    sys.exit(code)
+
+
+def getMessageDictionary(file):
+    """Simple state machine."""
+
+    msgs = []
+    comment = []
+    msgid = []
+    msgstr = []
+    fuzzy = False
+    line_counter = 0
+    status = SEARCHING
+
+    for line in file.readlines():
+        line = line.strip('\n')
+        line_counter += 1
+
+        # Handle Events
+        if line.startswith('#'):
+            status = COMMENT
+
+        elif line.startswith('msgid'):
+            line = line[6:] 
+            line_number = line_counter
+            status = MSGID
+
+        elif line.startswith('msgstr'):
+            line = line[7:] 
+            status = MSGSTR
+
+        elif line == '':
+            status = MSGDONE
+
+        # Actions based on status
+        if status == MSGID:
+            msgid.append(line.strip('"'))
+
+        elif status == MSGSTR:
+            msgstr.append(line.strip('"'))
+
+        elif status == COMMENT:
+            if line.startswith('#, fuzzy'):
+                fuzzy = True
+            comment.append(line[1:].strip())
+
+        elif status == MSGDONE:
+            status = SEARCHING
+            # Avoid getting the meta-data message string
+            if ''.join(msgid):
+                msgs.append( (''.join(msgid), ''.join(msgstr),
+                              line_number, '\n'.join(comment), fuzzy) )
+            comment = []
+            msgid = []
+            msgstr = []
+            fuzzy = False
+
+    return msgs
+
+
+def main(path):
+    print 'Language    Total    Done    Not Done    Fuzzy      Done %'
+    print '=========================================================='
+    languages = os.listdir(path)
+    languages.sort()
+    for language in languages:
+        lc_messages_path = os.path.join(path, language, 'LC_MESSAGES')
+
+        # Make sure we got a language directory
+        if not os.path.isdir(lc_messages_path):
+            continue
+
+        msgs = []
+        for domain_file in os.listdir(lc_messages_path):
+            if domain_file.endswith('.po'):
+                domain_path = os.path.join(lc_messages_path, domain_file)
+                file = open(domain_path, mode='r')
+                msgs += getMessageDictionary(file)
+
+        # We are dealing with the default language, which always has just one
+        # message string for the meta data (which is not recorded). 
+        if len(msgs) == 0:
+            continue
+
+        total = len(msgs)
+        not_done = len([msg for msg in msgs if msg[1] == ''])
+        fuzzy = len([msg for msg in msgs if msg[4] is True])
+        done = total - not_done - fuzzy
+        percent_done = 100.0 * done/total
+
+        line = language + ' '*(8-len(language))
+        line += ' '*(9-len(str(total))) + str(total)
+        line += ' '*(8-len(str(done))) + str(done)
+        line += ' '*(12-len(str(not_done))) + str(not_done)
+        line += ' '*(9-len(str(fuzzy))) + str(fuzzy)
+        pd_str = '%0.2f %%' %percent_done
+        line += ' '*(12-len(pd_str)) + pd_str
+        print line
+    
+
+if __name__ == '__main__':
+    try:
+        opts, args = getopt.getopt(
+            sys.argv[1:],
+            'l:h',
+            ['help', 'locals-dir='])
+    except getopt.error, msg:
+        usage(1, msg)
+
+    path = None
+    for opt, arg in opts:
+        if opt in ('-h', '--help'):
+            usage(0)
+        elif opt in ('-l', '--locales-dir'):
+            cwd = os.getcwd()
+            # This is for symlinks. Thanks to Fred for this trick.
+            if os.environ.has_key('PWD'):
+                cwd = os.environ['PWD']
+            path = os.path.normpath(os.path.join(cwd, arg))
+
+    if path is None:
+        usage(1, 'You must specify the path to the locales directory.')
+    main(path)


Property changes on: Zope3/branches/ZopeX3-3.0/zopeskel/bin/i18nstats.in
___________________________________________________________________
Name: svn:executable
   + *

Added: Zope3/branches/ZopeX3-3.0/zopeskel/bin/importchecker.in
===================================================================
--- Zope3/branches/ZopeX3-3.0/zopeskel/bin/importchecker.in	2004-09-01 21:13:44 UTC (rev 27398)
+++ Zope3/branches/ZopeX3-3.0/zopeskel/bin/importchecker.in	2004-09-01 23:47:48 UTC (rev 27399)
@@ -0,0 +1,313 @@
+#!<<PYTHON>>
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Import checker
+
+This utility finds unused imports in Python modules.  Its output is
+grep-like and thus emacs-friendly.
+
+$Id: importchecker.py 26446 2004-07-13 16:11:36Z philikon $
+"""
+import compiler
+import os, os.path
+import sys
+
+def _findDottedNamesHelper(node, result):
+    more_node = node
+    name = node.__class__.__name__
+    if name == 'Getattr':
+        dotted = []
+        while name == 'Getattr':
+            dotted.append(node.attrname)
+            node = node.expr
+            name = node.__class__.__name__
+        if name == 'Name':
+            dotted.append(node.name)
+            dotted.reverse()
+            for i in range(1, len(dotted)):
+                result.append('.'.join(dotted[:i]))
+            result.append('.'.join(dotted))
+            return
+    elif name == 'Name':
+        result.append(node.name)
+        return
+    elif name == 'AssAttr':
+        return 
+    for child in more_node.getChildNodes():
+        _findDottedNamesHelper(child, result)
+
+def findDottedNames(node):
+    """Find dotted names in an AST tree node
+    """
+    result = []
+    _findDottedNamesHelper(node, result)
+    return result
+
+class ImportFinder:
+    """An instance of this class will be used to walk over a compiler AST
+    tree (a module). During that operation, the appropriate methods of
+    this visitor will be called
+    """
+
+    def __init__(self):
+        self._map = {}
+
+    def visitFrom(self, stmt):
+        """Will be called for 'from foo import bar' statements
+        """
+        module_name, names = stmt.asList()
+        if module_name == '__future__':
+            # we don't care what's imported from the future
+            return
+        names_dict = {}
+        for orig_name, as_name in names:
+            # we don't care about from import *
+            if orig_name == '*':
+                continue
+            if as_name is None:
+                name = orig_name
+            else:
+                name = as_name
+            names_dict[name] = orig_name
+        self._map.setdefault(module_name, {'names': names_dict,
+                                           'lineno': stmt.lineno})
+
+    def visitImport(self, stmt):
+        """Will be called for 'import foo.bar' statements
+        """
+        for orig_name, as_name in stmt.names:
+            if as_name is None:
+                name = orig_name
+            else:
+                name = as_name
+            self._map.setdefault(orig_name, {'names': {name: orig_name},
+                                             'lineno': stmt.lineno})
+
+    def getMap(self):
+        return self._map
+
+def findImports(mod):
+    """Find import statements in module and put the result in a mapping.
+    """
+    visitor = ImportFinder()
+    compiler.walk(mod, visitor)
+    return visitor.getMap()
+
+class Module:
+    """This represents a python module.
+    """
+
+    def __init__(self, path):
+        mod = compiler.parseFile(path)
+        self._path = path
+        self._map = findImports(mod)
+        dottednames = {}
+        self._dottednames = findDottedNames(mod)
+           
+    def getPath(self):
+        """Return the path to this module's file.
+        """
+        return self._path
+
+    def getImportedModuleNames(self):
+        """Return the names of imported modules.
+        """
+        return self._map.keys()
+
+    def getImportNames(self):
+        """Return the names of imports; add dottednames as well.
+        """
+        result = []
+        map = self._map
+        for module_name in map.keys():
+            for usedname, originalname in map[module_name]['names'].items():
+                result.append((originalname, module_name))
+                # add any other name that we could be using
+                for dottedname in self._dottednames:
+                    usednamedot = usedname + '.'
+                    if dottedname.startswith(usednamedot):
+                        attrname = dottedname[len(usednamedot):].split('.')[0]
+                        result.append((attrname, module_name))
+        return result
+
+    def getUnusedImports(self):
+        """Get unused imports of this module (the whole import info).
+        """
+        result = []
+        for value in self._map.values():
+            for usedname, originalname in value['names'].items():
+                if usedname not in self._dottednames:
+                    result.append((originalname, value['lineno']))
+        return result
+    
+class ModuleFinder:
+
+    def __init__(self):
+        self._files = []
+        
+    def visit(self, arg, dirname, names):
+        """This method will be called when we walk the filesystem
+        tree. It looks for python modules and stored their filenames.
+        """
+        for name in names:
+            # get all .py files that aren't weirdo emacs droppings
+            if name.endswith('.py') and not name.startswith('.#'):
+                self._files.append(os.path.join(dirname, name))
+
+    def getModuleFilenames(self):
+        return self._files
+
+def findModules(path):
+    """Find python modules in the given path and return their absolute
+    filenames in a sequence.
+    """
+    finder = ModuleFinder()
+    os.path.walk(path, finder.visit, ())
+    return finder.getModuleFilenames()
+
+class ImportDatabase:
+    """This database keeps tracks of imports.
+
+    It allows to NOT report cases where a module imports something
+    just so that another module can import it (import dependencies).
+    """
+
+    def __init__(self, root_path):
+        self._root_path = root_path
+        self._modules = {}
+        self._names = {}
+        
+    def resolveDottedModuleName(self, dotted_name, module):
+        """Return path to file representing module, or None if no such
+        thing. Can do this relative from module.
+        """
+        dotted_path = dotted_name.replace('.', '/')
+        # try relative import first
+        path = os.path.join(os.path.dirname(module.getPath()), dotted_path)
+        path = self._resolveHelper(path)
+        if path is not None:
+            return path
+        # absolute import (assumed to be from this tree)
+        if os.path.isfile(os.path.join(self._root_path, '__init__.py')):
+            startpath, dummy = os.path.split(self._root_path)
+        else:
+            startpath = self._root_path
+        return self._resolveHelper(os.path.join(startpath, dotted_path))
+
+    def _resolveHelper(self, path):
+        if os.path.isfile(path + '.py'):
+            return path + '.py'
+        if os.path.isdir(path):
+            path = os.path.join(path, '__init__.py')
+            if os.path.isfile(path):
+                return path
+        return None
+
+    def findModules(self):
+        """Find modules in the given path.
+        """
+        for modulepath in findModules(self._root_path):
+            module = Module(modulepath)
+            self.addModule(module)
+
+    def addModule(self, module):
+        """Add information about a module to the database. A module in
+        this case is not a python module object, but an instance of
+        the above defined Module class.w
+        """
+        self_path = module.getPath()
+        # do nothing if we already know about it
+        if self._modules.has_key(self_path):
+            return
+
+        self._modules[self_path] = module
+
+        # add imported names to internal names mapping; this will
+        # allow us identify dependent imports later
+        names = self._names
+        for name, from_module_name in module.getImportNames():
+            path = self.resolveDottedModuleName(from_module_name, module)
+            t = (path, name)
+            modulepaths = names.get(t, {})
+            if not modulepaths.has_key(self_path):
+                modulepaths[self_path] = 1
+            names[t] = modulepaths
+        
+    def getUnusedImports(self):
+        """Get unused imports of all known modules.
+        """
+        result = {}
+        for path, module in self._modules.items():
+            result[path] = self.getUnusedImportsInModule(module)
+        return result
+
+    def getUnusedImportsInModule(self, module):
+        """Get all unused imports in a module.
+        """
+        result = []
+        for name, lineno in module.getUnusedImports():
+            if not self.isNameImportedFrom(name, module):
+                result.append((name, lineno))
+        return result
+
+    def isNameImportedFrom(self, name, module):
+        """Return true if name is imported from module by another module.
+        """
+        return self._names.has_key((module.getPath(), name))
+
+    def getModulesImportingNameFrom(self, name, module):
+        """Return list of known modules that import name from module.
+        """
+        result = []
+        for path in self._names.get((module.getPath(), name), {}).keys():
+            result.append(self._modules[path])
+        return result
+
+def main():
+    try:
+        path = sys.argv[1]
+    except IndexError:
+        print "No path supplied"
+        sys.exit(1)
+
+    path = os.path.abspath(path)
+    if not os.path.isdir(path):
+        print "Unknown path:", path
+        sys.exit(1)
+
+    l = len(path) + 1
+    db = ImportDatabase(path)
+    db.findModules()
+    unused_imports = db.getUnusedImports()
+    module_paths = unused_imports.keys()
+    module_paths.sort()
+    for path in module_paths:
+        info = unused_imports[path]
+        path = path[l:]
+        if not info:
+            continue
+        line2names = {}
+        for name, line in info:
+            names = line2names.get(line, [])
+            names.append(name)
+            line2names[line] = names
+        lines = line2names.keys()
+        lines.sort()
+        for line in lines:
+            names = ', '.join(line2names[line])
+            print "%s:%s: %s" % (path, line, names)
+
+if __name__ == '__main__':
+    main()
+


Property changes on: Zope3/branches/ZopeX3-3.0/zopeskel/bin/importchecker.in
___________________________________________________________________
Name: svn:executable
   + *

Added: Zope3/branches/ZopeX3-3.0/zopeskel/bin/pyskel.in
===================================================================
--- Zope3/branches/ZopeX3-3.0/zopeskel/bin/pyskel.in	2004-09-01 21:13:44 UTC (rev 27398)
+++ Zope3/branches/ZopeX3-3.0/zopeskel/bin/pyskel.in	2004-09-01 23:47:48 UTC (rev 27399)
@@ -0,0 +1,201 @@
+#!<<PYTHON>>
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Generate method skeletons for intefaces.
+
+Usage: python pyskel.py dotted_name
+
+Example:
+
+    cd lib/python
+    python zope/interface/pyskel.py zope.app.interfaces.pigs.IPigService
+
+The dotted name is the module name and interface object name connected
+with a dot.
+
+$Id: pyskel.py 26446 2004-07-13 16:11:36Z philikon $
+"""
+import sys, os, re
+
+SOFTWARE_HOME = "<<SOFTWARE_HOME>>"
+INSTANCE_HOME = "<<INSTANCE_HOME>>"
+
+sys.path.insert(0, os.getcwd())
+
+from types import ModuleType
+from zope.interface.interface import Method
+from zope.interface import Attribute
+
+class_re = re.compile(r'\s*class\s+([a-zA-Z_][a-zA-Z0-9_]*)')
+def_re = re.compile(r'\s*def\s+([a-zA-Z_][a-zA-Z0-9_]*)')
+attr_re = re.compile(r'\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*Attribute')
+
+
+def rskel(iface, top, print_iface=1):
+    name = "%s.%s" % (iface.__module__, iface.__name__)
+
+    file = resolve(iface.__module__).__file__
+    if file.endswith('pyc'):
+        file = file[:-1]
+    order = guessOrder(open(file))
+    namesAndDescriptions =  getAttributesInOrder(iface, order)
+
+    namesAndDescriptions = filter(lambda ades:
+                                  isinstance(ades[1], Method) or
+                                  isinstance(ades[1], Attribute),
+                                  namesAndDescriptions)
+
+    for aname, ades in namesAndDescriptions:
+        if isinstance(ades, Method):
+            sig = ades.getSignatureString()[1:-1]
+            if sig: sig = "self, %s" % sig
+            else:   sig = "self"
+            print
+            print "    def %s(%s):" % (aname, sig)
+            print '        "See %s"' % name
+
+        elif isinstance(ades, Attribute):
+            print
+            print "    # See %s" % name
+            print "    %s = None" %aname
+
+        else:
+            print
+            print 'Waaaa', ades
+
+    for base in iface.__bases__:
+        if base.__name__ not in ('Interface',):
+            rskel(base, top)
+
+def skel(name):
+    iface = resolve(name)
+    class_name = iface.__name__
+    if class_name.startswith('I'):
+        class_name = class_name[1:]
+    print "from zope.interface import implements"
+    print "from %s import %s" % (iface.__module__, iface.getName())
+    print
+    print "class %s:" %class_name
+    print "    __doc__ = %s.__doc__" % iface.getName()
+    print
+    print "    implements(%s)" %iface.getName()
+    print
+
+    rskel(iface, iface, 0)
+
+
+def resolve(name, _silly=('__doc__',), _globals={}):
+    # Support for file path syntax; this way I can use TAB to search for
+    # the module.
+    if '/' in name or name.endswith('.py'):
+        # We got a relative path. Let's try to get the full one and then
+        # make a package path out of it.
+        if not name.startswith('/'):
+            cwd = os.getcwd()
+            for path in sys.path[1:]: # Yeah, we need to exclude the cwd itself
+                path = str(path)
+                if path != '' and cwd.startswith(path):
+                    name = os.path.join(cwd[len(path)+1:], name)
+                    name = os.path.normpath(name)
+                    break
+
+        # get rid of the file ending :)
+        if name.endswith('.py'):
+            name = name[:-3]
+        name = name.replace('/', '.')
+
+    # Now to the regular lookup
+    if name[:1]=='.':
+        name = 'zopeproducts' + name
+
+    if name[-1:] == '.':
+        name = name[:-1]
+        repeat = 1
+    else:
+        repeat = 0
+
+    names=name.split('.')
+    last=names[-1]
+    mod='.'.join(names[:-1])
+
+    while 1:
+        m=__import__(mod, _globals, _globals, _silly)
+        try:
+            a=getattr(m, last)
+        except AttributeError:
+            pass
+        else:
+            if not repeat or (type(a) is not ModuleType):
+                return a
+        mod += '.' + last
+
+
+def guessOrder(source_file):
+    order = {}  # { class name -> list of methods }
+    lines = source_file.readlines()
+    class_name = None
+    for line in lines:
+        m = class_re.match(line)
+        if m and m.groups():
+            class_name = m.groups()[0]
+        else:
+            for m in (def_re.match(line),
+                      attr_re.match(line)):
+                if m and m.groups():
+                    def_name = m.groups()[0]
+                    name_order = order.get(class_name)
+                    if name_order is None:
+                        name_order = []
+                        order[class_name] = name_order
+                    name_order.append(def_name)
+
+    return order
+
+
+def getAttributesInOrder(interface, order):
+    # order is the dictionary returned from guessOrder().
+    # interface is a metaclass-based interface object.
+    name_order = order.get(interface.getName())
+
+    if name_order is None:
+        # Something's wrong.  Oh well.
+        items = interface.namesAndDescriptions()
+        items.sort()
+        return items
+    else:
+        items = []
+        for key, value in interface.namesAndDescriptions():
+            if key in name_order:
+                items.append((name_order.index(key), key, value))
+            else:
+                items.append((99999, key, value))  # Go to end.
+        items.sort()
+        return map(lambda item: item[1:], items)
+
+
+def run():
+    # This removes the script directory from sys.path, which we do
+    # since there are no modules here.
+    #
+    basepath = filter(None, sys.path)
+
+    sys.path[:] = [os.path.join(INSTANCE_HOME, "lib", "python"),
+                   SOFTWARE_HOME] + basepath
+
+    for a in sys.argv[1:]:
+        skel(a)
+
+
+if __name__ == '__main__':
+    run()


Property changes on: Zope3/branches/ZopeX3-3.0/zopeskel/bin/pyskel.in
___________________________________________________________________
Name: svn:executable
   + *



More information about the Zope3-Checkins mailing list