[Zope3-checkins] SVN: Zope3/trunk/ move the implementation of finddeps.py to a package; this allows the script

Fred L. Drake, Jr. fred at zope.com
Wed May 19 14:40:07 EDT 2004


Log message for revision 24831:
move the implementation of finddeps.py to a package; this allows the script
to become a set of utility objects and tools covered by the unit testing
framework



-=-
Added: Zope3/trunk/src/zope/dependencytool/__init__.py
===================================================================
--- Zope3/trunk/src/zope/dependencytool/__init__.py	2004-05-19 18:30:07 UTC (rev 24830)
+++ Zope3/trunk/src/zope/dependencytool/__init__.py	2004-05-19 18:40:01 UTC (rev 24831)
@@ -0,0 +1 @@
+# This directory is a Python package.


Property changes on: Zope3/trunk/src/zope/dependencytool/__init__.py
___________________________________________________________________
Name: svn:mime-type
   + text/x-python
Name: svn:eol-style
   + native

Copied: Zope3/trunk/src/zope/dependencytool/finddeps.py (from rev 24830, Zope3/trunk/utilities/finddeps.py)

Deleted: Zope3/trunk/utilities/finddeps.py
===================================================================
--- Zope3/trunk/utilities/finddeps.py	2004-05-19 18:30:07 UTC (rev 24830)
+++ Zope3/trunk/utilities/finddeps.py	2004-05-19 18:40:01 UTC (rev 24831)
@@ -1,539 +0,0 @@
-#! /usr/bin/env python2.3
-##############################################################################
-#
-# Copyright (c) 2004 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (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.
-#
-##############################################################################
-"""Script to determine the dependencies of a package or module
-
-This script walks through the modules of a package or only observes a
-file-based module to determine its dependencies.
-
-Usage: finddeps.py [options]
-Options:
-    -a / --all
-        Find all dependencies. This means that the program will also scan the
-        dependencies originally found in the module.
-
-    -h / --help
-        Print this message and exit.
-
-    -l / --long
-        If long is specified, the file and line where the dependency occurs is
-        reported.
-
-    -d / --dir
-        Specify the path of the module that is to be inspected.
-
-    -m / --module
-        Specify the dotted name of the module that is to be inspected.
-
-    -z / --zcml
-        Also look through ZCML files for dependencies.
-
-Important: Make sure that the PYTHONPATH is set to or includes 'ZOPE3/src'.
-
-$Id$
-"""
-import sys
-import getopt
-import os
-import re
-import token
-import tokenize
-
-# Get the Zope base path
-import zope
-ZOPESRC = os.path.dirname(os.path.dirname(zope.__file__))
-ZOPESRCPREFIX = os.path.join(ZOPESRC, "")
-
-# Matching expression for python files.
-pythonfile = re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*\.py$')
-zcmlfile = re.compile(r'[a-zA-Z][a-zA-Z0-9_]*\.zcml$')
-
-# Matching expressions of dotted paths in XML
-dottedName = re.compile(r'"[a-zA-Z\.][a-zA-Z0-9_\.]*"')
-
-
-def stripZopePrefix(path):
-    """Remove the '.../src/' prefix from path, if present."""
-    if path.startswith(ZOPESRCPREFIX):
-        return path[len(ZOPESRCPREFIX):]
-    else:
-        return path
-
-
-class Dependency(object):
-    """Object representing a dependency."""
-
-    def __init__(self, path, file, lineno):
-        """Initialize a Dependency instance.
-
-        path -- dotted name of the module
-
-        file -- full path of a source file that depends on the module
-        named by path
-
-        lineno -- line number within file where the dependency was
-        identified (import or ZCML reference)
-
-        """
-        self.path = path
-        self.occurences = [(file, lineno)]
-
-    def addOccurence(self, file, lineno):
-        """Add occurenace of the dependency in the code."""
-        self.occurences.append((file, lineno))
-
-    def isSubPackageOf(self, dep):
-        """Return True if this dependency's path is a sub-package of dep."""
-        path = self.path.split('.')
-        deppath = dep.path.split('.')
-        for i in range(len(path)):
-            if i >= len(deppath):
-                return True
-            if path[i] != deppath[i]:
-                break
-        return False
-
-    def __cmp__(self, other):
-        """Compare dependecies by module name."""
-        return cmp(self.path, other.path)
-
-
-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 makeDottedName(path):
-    """Convert a path to a dotted module name, using sys.path."""
-    syspaths = sys.path[1:]
-    syspaths.append(os.getcwd())
-    for syspath in syspaths:
-        syspath = os.path.join(syspath, '')
-        if path.startswith(syspath):
-            return path[len(syspath):].replace(os.sep, ".")
-
-    raise ValueError, 'Cannot create dotted name.'
-
-
-START = "<start>"
-FROM = "<from>"
-FROM_IMPORT = "<from-import>"
-IMPORT = "<import>"
-COLLECTING = "<collecting>"
-SWALLOWING = "<swallowing>"  # used to swallow "as foo"
-
-TOK_COMMA = (token.OP, ",")
-TOK_IMPORT = (token.NAME, "import")
-TOK_FROM = (token.NAME, "from")
-TOK_NEWLINE = (token.NEWLINE, "\n")
-TOK_ENDMARK = (token.ENDMARKER, "")
-
-dotjoin = ".".join
-
-
-class ImportFinder:
-
-    def __init__(self):
-        self.module_checks = {}
-        self.deps = []
-        self.imported_names = {}
-
-    def get_imports(self):
-        return self.deps
-
-    def find_imports(self, f, path):
-        self.path = path
-        self.state = START
-        self.post_name_state = None
-        prevline = None
-        try:
-            for t in tokenize.generate_tokens(f.readline):
-                type, string, start, end, line = t
-                self.transition(type, string, start[0])
-        except:
-            print >>sys.stderr, "error finding imports in", path
-            raise
-
-    def add_import(self, name, lineno):
-        if name not in self.module_checks:
-            self.check_module_name(name)
-            if not self.module_checks[name] and "." in name:
-                # if "." isn't in name, I'd be very surprised!
-                # i'd also be surprised if the result *isn't* a module
-                name = dotjoin(name.split(".")[:-1])
-                self.check_module_name(name)
-        # A few oddball cases import __main__ (support for
-        # command-line scripts), so we need to filter that out.
-        if self.module_checks[name] and name != "__main__":
-            self.deps.append(Dependency(name, self.path, lineno))
-
-    def check_module_name(self, name):
-        """Check whether 'name' is a module name.  Update module_checks."""
-        try:
-            __import__(name)
-        except ImportError:
-            self.module_checks[name] = False
-        else:
-            self.module_checks[name] = name in sys.modules
-
-    def transition(self, type, string, lineno):
-        if type == tokenize.COMMENT:
-            return
-        entry = self.state_table.get((self.state, (type, string)))
-        if entry is not None:
-            self.state = entry[0]
-            for action in entry[2:]:
-                meth = getattr(self, "action_" + action)
-                meth(type, string, lineno)
-            if entry[1] is not None:
-                self.post_name_state = entry[1]
-
-        # gotta figure out what to do:
-        elif self.state == COLLECTING:
-            # watch for "as" used as syntax
-            name = self.name
-            if type == token.NAME and name and not name.endswith("."):
-                self.state = SWALLOWING
-                if self.prefix:
-                    self.name = "%s.%s" % (self.prefix, name)
-            else:
-                self.name += string
-
-        elif self.state in (START, SWALLOWING):
-            pass
-
-        else:
-            raise RuntimeError(
-                "invalid transition: %s %r" % (self.state, (type, string)))
-
-    state_table = {
-        # (state,     token):       (new_state,  saved_state, action...),
-        (START,       TOK_IMPORT):  (COLLECTING, IMPORT, "reset"),
-        (START,       TOK_FROM):    (COLLECTING, FROM,   "reset"),
-
-        (FROM,        TOK_IMPORT):  (COLLECTING, FROM_IMPORT, "setprefix"),
-        (FROM_IMPORT, TOK_COMMA):   (COLLECTING, FROM_IMPORT),
-        (IMPORT,      TOK_COMMA):   (COLLECTING, IMPORT),
-
-        (COLLECTING,  TOK_COMMA):   (COLLECTING, None,   "save", "poststate"),
-        (COLLECTING,  TOK_IMPORT):  (COLLECTING, FROM_IMPORT, "setprefix"),
-
-        (SWALLOWING,  TOK_COMMA):   (None,       None,   "save", "poststate"),
-
-        # Commented-out transitions are syntax errors, so shouldn't
-        # ever be seen in working code.
-
-        # end of line:
-        #(START,       TOK_NEWLINE): (START,      None,   "save", "reset"),
-        #(FROM,        TOK_NEWLINE): (START,      None,   "save", "reset"),
-        (FROM_IMPORT, TOK_NEWLINE): (START,      None,   "save", "reset"),
-        #(IMPORT,      TOK_NEWLINE): (START,      None,   "save", "reset"),
-        (COLLECTING,  TOK_NEWLINE): (START,      None,   "save", "reset"),
-        (SWALLOWING,  TOK_NEWLINE): (START,      None,   "save", "reset"),
-
-        # end of input:
-        #(START,       TOK_ENDMARK): (START,      None,   "save", "reset"),
-        #(FROM,        TOK_ENDMARK): (START,      None,   "save", "reset"),
-        (FROM_IMPORT, TOK_ENDMARK): (START,      None,   "save", "reset"),
-        #(IMPORT,      TOK_ENDMARK): (START,      None,   "save", "reset"),
-        (COLLECTING,  TOK_ENDMARK): (START,      None,   "save", "reset"),
-        (SWALLOWING,  TOK_ENDMARK): (START,      None,   "save", "reset"),
-        }
-
-    def action_reset(self, type, string, lineno):
-        self.name = ''
-        self.prefix = None
-
-    def action_save(self, type, string, lineno):
-        if self.name:
-            assert not self.name.endswith("."), repr(self.name)
-            name = self.name
-            if self.prefix:
-                name = "%s.%s" % (self.prefix, name)
-            self.add_import(name, lineno)
-            self.name = ""
-
-    def action_setprefix(self, type, string, lineno):
-        assert self.name, repr(self.name)
-        assert not self.name.endswith("."), repr(self.name)
-        self.prefix = self.name
-        self.name = ""
-
-    def action_collect(self, type, string, lineno):
-        self.name += string
-
-    def action_poststate(self, type, string, lineno):
-        self.state = self.post_name_state
-        self.post_name_state = None
-        self.transition(type, string, lineno)
-
-
-def getDependenciesOfPythonFile(path):
-    finder = ImportFinder()
-    finder.find_imports(open(path, 'rU'), path)
-    return finder.get_imports()
-
-
-def getDependenciesOfZCMLFile(path):
-    """Get dependencies from ZCML file."""
-    localModule = stripZopePrefix(os.path.dirname(path))
-    localModule = localModule.replace(os.sep, '.')
-    deps = []
-    lineno = 0
-    for line in open(path, 'r'):
-        lineno += 1
-        match = dottedName.findall(line)
-        if match:
-            match[0] = match[0][1:-1]
-            match.append('.'.join(match[0].split('.')[:-1]))
-
-            for name in match:
-                if name.startswith('.'):
-                    name = localModule + name
-                try:
-                    __import__(name)
-                except:
-                    continue
-                deps.append(Dependency(name, path, lineno))
-    return deps
-
-
-def filterStandardModules(deps):
-    """Try to remove modules from the standard Python library.
-
-    Modules are considered part of the standard library if their
-    __file__ is located in the tree rooted at the parent of the
-    site-packages directory, but not in the sub-tree in site-packages.
-    """
-    from distutils import sysconfig
-    site_packages = sysconfig.get_python_lib()
-    standard_lib = os.path.dirname(site_packages)
-    site_packages = os.path.join(site_packages, "")
-    standard_lib = os.path.join(standard_lib, "")
-    filteredDeps = []
-    for dep in deps:
-        try:
-            module = __import__(dep.path)
-        except ImportError:
-            continue
-        # built-ins (like sys) do not have a file associated
-        if not hasattr(module, '__file__'):
-            continue
-        starts = module.__file__.startswith
-        if starts(standard_lib) and not starts(site_packages):
-            continue
-        filteredDeps.append(dep)
-    return filteredDeps
-
-
-def filterLocalModules(deps, path):
-    """Filter out local module imports."""
-    # File-based modules cannot have relative imports
-    if os.path.isfile(path):
-        return deps
-
-    # Filter relative imports
-    filteredDeps = []
-    for dep in deps:
-        module = dep.path.split('.')[0]
-        modulePath = os.path.join(path, module)
-        if not (os.path.exists(modulePath)
-                or os.path.exists(modulePath+'.py')):
-            filteredDeps.append(dep)
-    deps = filteredDeps
-
-    # Filter absolute imports
-    dottedName = makeDottedName(path)
-    filteredDeps = []
-    for dep in deps:
-        if not dep.path.startswith(dottedName):
-            filteredDeps.append(dep)
-
-    return filteredDeps
-
-
-def filterMostGeneral(deps):
-    """Return only the parent module and no children.
-
-    for example (foo, foo.bar) --> (foo,)
-    """
-    newdeps = []
-    for dep in deps:
-        subpackage = False
-        for parentdep in deps:
-            if parentdep is not dep and dep.isSubPackageOf(parentdep):
-                subpackage = True
-                break
-        if not subpackage:
-            newdeps.append(dep)
-    return newdeps
-
-
-def makeUnique(deps):
-    """Remove entries that appear multiple times"""
-    uniqueDeps = {}
-    for dep in deps:
-        if not dep.path in uniqueDeps.keys():
-            uniqueDeps[dep.path] = dep
-        else:
-            uniqueDeps[dep.path].addOccurence(*dep.occurences[0])
-
-    return uniqueDeps.values()
-
-
-def getDependencies(path, zcml=False):
-    """Get all dependencies of a package or module.
-
-    If the path is a package, all Python source files are searched inside it.
-    """
-    if os.path.isdir(path):
-        deps = []
-        for file in os.listdir(path):
-            filePath = os.path.join(path, file)
-            if pythonfile.match(file):
-                deps += getDependenciesOfPythonFile(filePath)
-            elif zcml and zcmlfile.match(file):
-                deps += getDependenciesOfZCMLFile(filePath)
-            elif os.path.isdir(filePath):
-                filenames = os.listdir(filePath)
-                if (  'PUBLICATION.cfg' not in filenames
-                      and 'SETUP.cfg' not in filenames
-                      and 'DEPENDENCIES.cfg' not in filenames
-                      and '__init__.py' in filenames):
-                    deps += getDependencies(filePath)
-
-    elif os.path.isfile(path):
-        ext = os.path.splitext(path)[1]
-        if ext == ".py":
-            deps = getDependenciesOfPythonFile(path)
-        elif ext == ".zcml":
-            deps = getDependenciesOfZCMLFile(path)
-        else:
-            print >>sys.stderr, ("dependencies can only be"
-                                 " extracted from Python and ZCML files")
-            sys.exit(1)
-
-    else:
-        print >>sys.stderr, path, "does not exist"
-        sys.exit(1)
-
-    return deps
-
-
-def getCleanedDependencies(path, zcml=False):
-    """Return clean dependency list."""
-    deps = getDependencies(path, zcml)
-    deps = filterStandardModules(deps)
-    deps = filterLocalModules(deps, path)
-    deps = filterMostGeneral(deps)
-    deps = makeUnique(deps)
-    deps.sort()
-    return deps
-
-
-def getAllCleanedDependencies(path, zcml=False, deps=None, paths=None):
-    """Return a list of all cleaned dependencies in a path."""
-    # zope and zope/app are too general to be considered.
-    if path.endswith('src/zope/') or path.endswith('src/zope/app/'):
-        return deps
-
-    if deps is None:
-        deps = []
-        paths = []
-
-    newdeps = getCleanedDependencies(path)
-    for dep in newdeps:
-        if dep.path not in paths:
-            deps.append(dep)
-            paths.append(dep.path)
-            modulePath = __import__(dep.path).__file__
-            dirname, basename = os.path.split(modulePath)
-            if basename in ('__init__.py', '__init__.pyc', '__init__.pyo'):
-                modulePath = os.path.join(dirname, '')
-            getAllCleanedDependencies(modulePath, zcml, deps, paths)
-    deps = filterMostGeneral(deps)
-    deps.sort()
-    return deps
-
-
-def showDependencies(path, zcml=False, long=False, all=False):
-    """Show the dependencies of a module on the screen."""
-    if all:
-        deps = getAllCleanedDependencies(path, zcml)
-    else:
-        deps = getCleanedDependencies(path, zcml)
-
-    if long:
-        print '='*(8+len(path))
-        print "Module: " + path
-        print '='*(8+len(path))
-    for dep in deps:
-        print dep.path
-        if long:
-            print '-'*len(dep.path)
-            for file, lineno in dep.occurences:
-                file = stripZopePrefix(file)
-                if len(file) >= 69:
-                    file = '...' + file[:69-3]
-                print '  %s, Line %s' %(file, lineno)
-            print
-
-
-def main(argv=None):
-    if argv is None:
-        argv = sys.argv
-    try:
-        opts, args = getopt.getopt(
-            argv[1:],
-            'd:m:ahlz',
-            ['all', 'help', 'dir=', 'module=', 'long', 'zcml'])
-    except getopt.error, msg:
-        usage(1, msg)
-
-    all = False
-    long = False
-    path = None
-    zcml = False
-    for opt, arg in opts:
-        if opt in ('-a', '--all'):
-            all = True
-        elif opt in ('-h', '--help'):
-            usage(0)
-        elif opt in ('-l', '--long'):
-            long = True
-        elif opt in ('-d', '--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))
-        elif opt in ('-m', '--module'):
-            try:
-                module = __import__(arg, globals(), locals(), ('something',))
-                path = os.path.dirname(module.__file__)
-            except ImportError:
-                usage(1, "Could not import module %s" % module)
-        elif opt in ('-z', '--zcml'):
-            zcml = True
-    if path is None:
-        usage(1, 'The module must be specified either by path, '
-              'dotted name or ZCML file.')
-    showDependencies(path, zcml, long, all)
-
-
-if __name__ == '__main__':
-    main()

Added: Zope3/trunk/utilities/finddeps.py
===================================================================
--- Zope3/trunk/utilities/finddeps.py	2004-05-19 18:30:07 UTC (rev 24830)
+++ Zope3/trunk/utilities/finddeps.py	2004-05-19 18:40:01 UTC (rev 24831)
@@ -0,0 +1,40 @@
+#!/usr/bin/env python2.3
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+#
+##############################################################################
+"""Script to search for package dependencies.
+
+$Id$
+"""
+
+import os
+import sys
+
+here = os.path.dirname(os.path.realpath(__file__))
+swhome = os.path.dirname(here)
+
+for parts in [("src",), ("lib", "python"), ("Lib", "site-packages")]:
+    d = os.path.join(swhome, *(parts + ("zope", "app", "process")))
+    if os.path.isdir(d):
+        d = os.path.join(swhome, *parts)
+        sys.path.insert(0, d)
+        break
+else:
+    print >>sys.stderr, "Could not locate Zope software installation!"
+    sys.exit(1)
+
+
+from zope.dependencytool.finddeps import main
+
+
+main()


Property changes on: Zope3/trunk/utilities/finddeps.py
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + text/x-python
Name: svn:eol-style
   + native




More information about the Zope3-Checkins mailing list