#!/usr/bin/env python
# pkgsearch.py - Print information about installed Python packages
# and modules.
import sys, os.path
import compiler
import site
from imp import *
__usage__="""Usage: %s [optional paths] % sys.argv[0])"""
class PkgUtil(object):
""" Utility class for querying information about
installed packages and modules """
def __init__(self, paths=None):
self.paths = sys.path
if paths:
self.paths = paths + self.paths
def find_standard_package(self, pkgname):
"""Search in standard paths for a package/module """
try:
result = find_module(pkgname)
return result
except ImportError, e:
return ()
def get_package_init_path(self, pkgname, pkgdir):
""" Return the init file path for the package.
This has to be called only for directory packages """
pkgdir = os.path.abspath(pkgdir)
# Try __init__.py
pkginitfile = os.path.join(pkgdir, '__init__.py')
# If it does not exist, try <pkgname>.py
if not os.path.isfile(pkginitfile):
pkginitfile = os.path.join(pkgdir,pkgname + '.py')
if os.path.isfile(pkginitfile):
return pkginitfile
else:
# Everything failed, return pkgdir itself!
return pkgdir
def load_comments(self, pkgfile):
""" Open the package and load comments if any.
Return the loaded comments """
# Note: This has to be called with a Python
# source file (.py) only!
if not os.path.exists(pkgfile):
return ""
comment = ""
try:
of = open(pkgfile,'rb')
data = of.read()
if data:
# Create code object
try:
c = compiler.compile(data,pkgfile,'exec')
# Get the position of first line of code
if c:
lno = c.co_firstlineno
lnum = 0
# Read file till this line number
of.seek(0)
for line in of:
comment = "".join((comment, line))
lnum += 1
if lnum==lno or line=="\n": break
except SyntaxError, e:
pass
except Exception, e:
pass
of.close()
except (OSError, IOError, TypeError), e:
pass
return comment
def find_package(self, pkgname):
# Query for package/module and return a dictionary
# with the following fields
# 'name': Package/module name,
# 'path' : Full path of the package/module,
# 'type' : What kind of a package/module is it
# This has the following values
# 'doc' : Package documentation
#
# PY_SOURCE: The module was found as a source file.
# PY_COMPILED: The module was found as a compiled code
# object file.
# C_EXTENSION: The module was found as dynamically
# loadable shared library.
# PY_RESOURCE: The module was found as a Macintosh resource.
# This value can only be returned on a Macintosh.
# PKG_DIRECTORY: The module was found as a package directory.
# C_BUILTIN: The module was found as a built-in module.
# PY_FROZEN: The module was found as a frozen module.
#
# If no module/package is found, returns a null dictionary.
d = {}
packages = pkgname.split('.')
top_level = packages[0]
try:
# First look for built-in modules
result = self.find_standard_package(pkgname)
if not result and self.paths:
result = find_module(pkgname, self.paths)
if result:
of, pathname, desc = result
# Last or only component of package
if len(packages)==1:
# Load module
try:
M = load_module(pkgname, of, pathname, desc)
except Exception, e:
return d
d['name'] = pkgname
d['type'] = desc[2]
d['doc']=''
if os.path.dirname(pathname):
d['path'] = self.get_package_init_path(pkgname, pathname)
else:
# For built-in modules
d['path']=pathname
if M:
if M.__doc__:
# Set doc string
d['doc'] = M.__doc__
else:
pkgfile = ''
# Load comments from the package file
# if any.
if d['type'] == PY_SOURCE:
pkgfile = d['path']
elif d['type'] == PKG_DIRECTORY:
if os.path.isfile(d['path']):
pkgfile = d['path']
if pkgfile:
d['doc'] = self.load_comments(pkgfile)
return d
except ImportError, e:
if len(packages)>1:
try:
result = find_module(top_level, self.paths)
if result:
of, pathname, desc = result
try:
M = load_module(top_level, of, pathname, desc)
# Remove the top_level package from the name
pkgname = reduce(lambda x,y: x+'.'+y, packages[1:])
# Call this recursively
if hasattr(M, '__path__'):
return self.find_package(pkgname, M.__path__)
except ImportError, e:
pass
except Exception, e:
pass
except ImportError, e:
pass
else:
pass
return d
def pkgTypeInfo(self, pkg_typ):
""" Return information on the package - Version 2"""
if pkg_typ is PY_SOURCE:
return "PYTHON SOURCE FILE MODULES"
elif pkg_typ is PY_COMPILED:
return "PYTHON COMPILED CODE OBJECT MODULES "
elif pkg_typ is C_EXTENSION:
return "DYNAMICALLY LOADABLE SHARED LIBRARY (C-EXTENSION) MODULES"
elif pkg_typ is PY_RESOURCE:
return "MACINTOSH RESOURCE MODULES"
elif pkg_typ is PKG_DIRECTORY:
return "PYTHON PACKAGE DIRECTORY MODULES"
elif pkg_typ is C_BUILTIN:
return "BUILT-IN MODULES"
elif pkg_typ is PY_FROZEN:
return "FROZEN PYTHON MODULES"
else:
return "UNKNOWN MODULES"
def list_packages(self):
""" An ambitious function which attempts to list all Python packages
in your system, according to the configuration """
# First extract loaded module names from sys.modules
sys_modules = sys.modules.keys()
packages = {}
# First add moduels in sys.modules (built-ins,
# preloads and already loaded ones)
for name in sys_modules:
d = self.find_package(name)
if not d: continue
try:
pkginfo = packages[d['type']]
pkginfo[d['name']] = d['path']
except Exception, e:
packages[d['type']] = { d['name'] : d['path'] }
import site
# Loop through all directories in sys.path and check for modules
# Dont iterate through <prefix>/lib directory
libdir = os.path.join(sys.prefix, 'lib')
walked = []
for top_level in self.paths:
if not os.path.isdir(top_level):
continue
# Dont iterate through libdir
if os.path.abspath(top_level) == os.path.abspath(libdir):
continue
walked.append(top_level)
for item in os.listdir(top_level):
fullpath = os.path.join(top_level, item)
if fullpath in walked: continue
walked.append(fullpath)
# Remove the extension
idx = item.find('.')
if idx != -1: item = item[:idx]
d = self.find_package(item)
if not d: continue
try:
pkginfo = packages[d['type']]
pkginfo[d['name']] = d['path']
except Exception, e:
packages[d['type']] = { d['name'] : d['path'] }
for key,item in packages.items():
print
print self.pkgTypeInfo(key)
print
# Print sorted
listofitems = item.keys()
listofitems.sort()
for key2 in listofitems:
print key2,':',item[key2]
if __name__=="__main__":
u = PkgUtil(sys.argv)
# List information about standard packages
u.list_packages()