"""Disutils packaging for the pipelines package. """ import os from distutils import dir_util from distutils.command.build import build as buildcmd from distutils.command.install_lib import install_lib as installcmd from distutils.command.sdist import sdist as sdistcmd from distutils.core import setup from distutils.dist import Distribution # We have to snoop for file types that distutils doesn't copy correctly when # doing a non-build-in-place. EXTS = ['.conf', '.css', '.dtd', '.gif', '.jpg', '.html', '.js', '.mo', '.png', '.pt', '.stx', '.ref', '.txt', '.xml', '.zcml', '.mar', '.in', '.sample', '.rdf', ] # This class serves multiple purposes. It walks the file system looking for # auxiliary files that distutils doesn't install properly, and it actually # copies those files (when hooked into by distutils). It also walks the file # system looking for candidate packages for distutils to install as normal. # The key here is that the package must have an __init__.py file. class Finder: def __init__(self, exts, prefix): self._files = [] self._pkgs = {} self._exts = exts # We're finding packages in lib/python in the source dir, but we're # copying them directly under build/lib.. So we need to lop off # the prefix when calculating the package names from the file names. self._plen = len(prefix) def visit(self, ignore, dir, files): for file in files: # First see if this is one of the packages we want to add, or if # we're really skipping this package. if '__init__.py' in files: aspkg = dir[self._plen:].replace(os.sep, '.') self._pkgs[aspkg] = True # Add any extra files we're interested in base, ext = os.path.splitext(file) if ext in self._exts: self._files.append(os.path.join(dir, file)) def copy_files(self, cmd, outputbase): for file in self._files: dest = os.path.join(outputbase, file[self._plen:]) # Make sure the destination directory exists dir = os.path.dirname(dest) if not os.path.exists(dir): dir_util.mkpath(dir) cmd.copy_file(file, dest) def get_packages(self): return self._pkgs.keys() def remove_stale_bytecode(arg, dirname, names): names = map(os.path.normcase, names) for name in names: if name.endswith(".pyc") or name.endswith(".pyo"): srcname = name[:-1] if srcname not in names: fullname = os.path.join(dirname, name) print "Removing stale bytecode file", fullname os.unlink(fullname) # Create the finder instance, which will be used in lots of places. `finder' # is the global we're most interested in. basedir = 'src/' finder = Finder(EXTS, basedir) os.path.walk(basedir, finder.visit, None) packages = finder.get_packages() # Distutils hook classes class MyBuilder(buildcmd): def run(self): os.path.walk(os.curdir, remove_stale_bytecode, None) buildcmd.run(self) finder.copy_files(self, self.build_lib) class MyLibInstaller(installcmd): def run(self): installcmd.run(self) finder.copy_files(self, self.install_dir) class MyDistribution(Distribution): # To control the selection of MyLibInstaller and MyPyBuilder, we # have to set it into the cmdclass instance variable, set in # Distribution.__init__(). def __init__(self, *attrs): Distribution.__init__(self, *attrs) self.cmdclass['build'] = MyBuilder self.cmdclass['install_lib'] = MyLibInstaller setup(name="pipelines", version="0.1", maintainer="Tres Seaver", maintainer_email="tseaver@palladion.com", platforms = ["any"], packages = packages, package_dir = {'': 'src'}, distclass = MyDistribution, description = "Zope3 pipeline component", long_description = """\ Pipelines are configurable chains of callables, each of which will be called sequentially to process an argument. In Zope3, pipelines may be configured through ZCML and registered as utilities or event subscribers.""", license = "Zope Public License (ZPL) 2.1", url = "http://codespeak.net/z3", )