Differences between revisions 5 and 6
Revision 5 as of 2009-01-31 15:44:37
Size: 1881
Editor: 61
Comment:
Revision 6 as of 2009-04-22 03:52:05
Size: 1901
Editor: eth595
Comment: use PEP 8 for code examples
Deletions are marked like this. Additions are marked like this.
Line 12: Line 12:
def isPackage( filename ):
def is_package(path):
Line 14: Line 15:
        os.path.isdir(filename) and
        os.path.isfile(
os.path.join(filename,'__init__.py')
    )
def
packagesFor( filename, basePackage="" ):
    """Find all
packages in filename"""
    set
= {}
    for item in os.listdir(filename):
        dir = os.path.join(filename, item)
        if isPackage( dir ):
            if basePackage:
                moduleName = basePackage+'.'+item
        os.path.isdir(path) and
        os.path.isfile(
os.path.join(path, '__init__.py')
        )

def find_
packages(path, base="" ):
    """
Find all packages in path """
   
packages = {}
    for item in os.listdir(path):
        dir = os.path.join(path, item)
        if is_package( dir ):
            if base:
                module_name = "%(base)s.%(item)s" % vars()
Line 26: Line 28:
                moduleName = item
            set[ moduleName] = dir
            set.update( packagesFor( dir, moduleName))
    return set
                module_name = item
            packages[module_name] = dir
            packages.update(find_packages(dir, module_name))
    return packages
Line 32: Line 34:
Then call packagesFor to get the set of packages to be included (note that this call assumes that the packages are sub-directories of the directory where setup.py resides). Then call {{{find_packages}}} to get the set of packages to be included (note that this call assumes that the packages are sub-directories of the directory where setup.py resides).
Line 35: Line 37:
packages = packagesFor( "." ) packages = find_packages(".")
Line 38: Line 40:
Then use packages as the source within your call to setup: Then use {{{packages}}} as the source within your call to setup:
Line 44: Line 46:
... #...
Line 46: Line 48:
    **extraArguments
)
    **extra_arguments
    )

Problem

Distutils requires that you manually specify each package to be included in the distribution. For packages with large and deep sub-package hierarchies it can be a pain to keep this list in sync with the code, particularly as forgetting an entry is not noticable until a user happens to report that an entire sub-package is missing.

Solution

Use an automatic sub-package scanning mechanism to generate the package_dir and packages parameters for setup:

import os

def is_package(path):
    return (
        os.path.isdir(path) and
        os.path.isfile(os.path.join(path, '__init__.py')
        )

def find_packages(path, base="" ):
    """ Find all packages in path """
    packages = {}
    for item in os.listdir(path):
        dir = os.path.join(path, item)
        if is_package( dir ):
            if base:
                module_name = "%(base)s.%(item)s" % vars()
            else:
                module_name = item
            packages[module_name] = dir
            packages.update(find_packages(dir, module_name))
    return packages

Then call find_packages to get the set of packages to be included (note that this call assumes that the packages are sub-directories of the directory where setup.py resides).

packages = find_packages(".")

Then use packages as the source within your call to setup:

setup (
    name = "pytable",
    package_dir = packages,
#...
    packages = packages.keys(),
    **extra_arguments
    )

You can see a real-world usage example in the PyTable setup script

Discussion

There should be some way to do this with distutils own machinery, I just don't know what it would be.


CategoryDistutilsCookbook

Distutils/Cookbook/AutoPackageDiscovery (last edited 2010-04-11 01:25:10 by 101)

Unable to edit the page? See the FrontPage for instructions.