[Distutils] installing data files and headers

Alexandre Alexandre.Fayolle@logilab.fr
Fri Feb 28 11:17:02 2003

Content-Type: text/plain; charset=iso-8859-1
Content-Disposition: inline
Content-Transfer-Encoding: 8bit

On Fri, Feb 28, 2003 at 10:31:00AM -0500, Jeremy Hylton wrote:
> We often store data files in a package directory.  Zope components
> sometimes have configuration files, presentation files like html and
> images, and other data.  One common case is unit test packages, which
> often have test data in them.  In all these cases, we find it useful to
> access the data files by loading them from the package directory.  You
> get the package's __path__ attribute and look for data files in that
> directory.
> The problem is that distutils won't install these files for us.  It ends
> up being a lot of work to get the files installed.  You need to provide
> a custom distclass to copy the files at build and install time.  It
> would be a lot more convenient if distutils would just install the files
> by default.

Attached to this mail is a Distutils extension I coded to install
twisted tml file, but it works fine to install any other file in a
python package. Feel free to use, modify and distribute. 

Alexandre Fayolle
LOGILAB, Paris (France).
http://www.logilab.com   http://www.logilab.fr  http://www.logilab.org
Développement logiciel avancé - Intelligence Artificielle - Formations

Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="twisted_distutils.py"

"""Distutils extensions for twisted framework.

This module enables the installation of plugins.tml files using standard
distutils syntax. It adds the following commands to the standard
setup.py commands:
* build_twisted_plugins: build (i.e. copy) plugins
* install_twisted_plugins: install plugins

Additionally, the following commands have been modified to deal with
plugins files:
 * sdist
 * build
 * install

To use these extenstion, you should import the setup fonction from this
module, and use it normally. To list the plugins.tml files, use the
twisted_plugins keyword argument to the setup function:

from twisted_distutils import setup # you can also import Extension if needed

if __name__ == '__main__':
          twisted_plugins = ['my_package/plugins.tml'])

Note that you can use this to install files that are not twisted plugins in any package directory of your application.
# (c) 2002 Alexandre Fayolle <alexandre.fayolle@free.fr>
# This module is heavily based on code copied from the python distutils
# framework, especially distutils.command.build_script,
# distutils.command.install_script. Many thanks to the authors of these
# modules.
# This module is provided as is, I'm not responsible if anything bad
# happens to you or your python library while using this module. You may
# freely copy it, distribute it and use it in your library or to distribute
# your applications. I'd appreciate if you could drop me an email if you plan
# to do so <wink>.
# Happy twisting!

from distutils.core import Extension,Distribution,Command
from distutils.command.install import install
from distutils.command.build import build
from distutils.command.sdist import sdist
from distutils.dep_util import newer
from distutils.util import convert_path
import os

class twisted_sdist(sdist):
    def add_defaults(self):
        if self.distribution.has_twisted_plugins():
            build_twisted_plugins = self.get_finalized_command('build_twisted_plugins')

class twisted_install(install):
    def initialize_options (self):
        self.twisted_plugins = None
    def has_twisted_plugins(self):
        return self.distribution.has_twisted_plugins()
    sub_commands = []
    sub_commands.append(('install_twisted_plugins', has_twisted_plugins))

class twisted_build(build):
    def initialize_options (self):
        self.twisted_plugins = None
    def has_twisted_plugins(self):
        return self.distribution.has_twisted_plugins()
    sub_commands = []
    sub_commands.append(('build_twisted_plugins', has_twisted_plugins))

class build_twisted_plugins (Command):

    description = "\"build\" twisted plugins (copy)"

    user_options = [
        ('build-dir=', 'd', "directory to \"build\" (copy) to"),
        ('force', 'f', "forcibly build everything (ignore file timestamps"),

    boolean_options = ['force']

    def initialize_options (self):
        self.build_dir = None
        self.twisted_plugins = None
        self.force = None
        self.outfiles = None

    def get_source_files(self):
        return self.twisted_plugins

    def finalize_options (self):
                                   ('build_lib', 'build_dir'),
                                   ('force', 'force'))
        self.twisted_plugins = self.distribution.twisted_plugins

    def run (self):
        if not self.twisted_plugins:

    def copy_twisted_plugins (self):
        """Copy each plugin listed in 'self.twisted_plugins'.
        for plugin in self.twisted_plugins:
            adjust = 0
            plugin = convert_path(plugin)
            outfile = os.path.join(self.build_dir, plugin)
            if not self.force and not newer(plugin, outfile):
                self.announce("not copying %s (up-to-date)" % plugin)

            # Always open the file, but ignore failures in dry-run mode --
            # that way, we'll get accurate feedback if we can read the
            # plugin.
                f = open(plugin, "r")
            except IOError:
                if not self.dry_run:
                f = None
                self.copy_file(plugin, outfile)

class install_twisted_plugins(Command):

    description = "install twisted plugins"

    user_options = [
        ('install-dir=', 'd', "directory to install scripts to"),
        ('build-dir=','b', "build directory (where to install from)"),
        ('force', 'f', "force installation (overwrite existing files)"),
        ('skip-build', None, "skip the build steps"),

    boolean_options = ['force', 'skip-build']

    def initialize_options (self):
        self.install_dir = None
        self.force = 0
        self.build_dir = None
        self.skip_build = None

    def finalize_options (self):
        self.set_undefined_options('build', ('build_lib', 'build_dir'))
                                   ('install_lib', 'install_dir'),
                                   ('force', 'force'),
                                   ('skip_build', 'skip_build'),

    def run (self):
        if not self.skip_build:
        self.outfiles = self.copy_tree(self.build_dir, self.install_dir)

    def get_inputs (self):
        return self.distribution.twisted_plugins or []

    def get_outputs(self):
        return self.outfiles or []

class TwistedDistribution(Distribution):
    def __init__(self,attrs=None):
        self.twisted_plugins = None
        self.cmdclass = {'install':twisted_install,

    def has_twisted_plugins(self):
        return self.twisted_plugins and len(self.twisted_plugins) > 0

def setup(**attrs):
    from distutils.core import setup
    attrs['distclass'] = TwistedDistribution