[Distutils] The "dist" command

Greg Ward gward@cnri.reston.va.us
Thu, 23 Sep 1999 09:46:48 -0400


I realized last night that I can't honestly release Distutils 0.1 until
it can distribute itself, i.e. until there's a "dist" command to create
a source distribution.  So I sat down and did a brain dump on what's
needed to create source distributions.  If you enjoy reading braindumps, 
please read this one and post your reactions.

<braindump>
Possible modes of operation:
  - require an explicit manifest that lists every single file (presumably
    along with a way to auto-generate the manifest)
  - require an explicit manifest, but allow it to have globs or
    filename patterns of some kind (and also have auto-generation)
  - allow an explict manifest, but automatically augment it at runtime
    with the source files mentioned in 'packages', 'py_modules', and
    'ext_modules' (and any other such things that might come along)

I'm liking the third way.  Possible gotchas:
  - redundant specification: 'packages' includes 'foo' and manifest
    includes 'foo/*.py'
  - obvious conflict: 'packages' includes 'foo' and manifest
    includes '! foo/*.py' (can't imagine why you'd want this)
  - subtle conflict:  'packages' includes 'foo' and manifest
    includes '! foo/bar.py' (this could well be desired: eg. exclude
    an experimental module from distribution)

Syntax for the manifest file:
  - each line is by default a Unix-style glob specifying files to include
  - if the line starts with '!', then it's an exclude pattern
  - if the line just names a directory in the distribution, that directory
    is included recursively
  - if the first word of a line names a directory, then the remaining
    words are include/exclude patterns that are applied recursively
    under that directory

example (ignoring auto-augmentation!):
  distutils/*.py
  distutils/command/*.py
  ! distutils/bleeding_edge.py
  examples/*.py
  examples/README

smarter way (that *will* include distutils/command/bleeding_edge.py!)
  distutils *.py
  ! distutils/bleeding_edge.py
  examples !*~ !*.py[co]
  test !*~ !*.py[co]
  README
  setup.py

The actual Distutils manifest (don't need to mention source files,
README, setup.py -- they're automatically distributed!):
  examples !*~ !*.py[co]
  test !*~ !*.py[co]

The algorithm that will make it work:
  files = stuff from 'packages', 'py_modules', 'ext_modules',
    plus README, setup.py, ... ?
  foreach spec in manifest file:
    if simple-include-spec:         # "distutils/*.py"
      files.append (glob (spec))
    elif simple-exclude-spec:       # "! distutils/foo*"
      xfiles = glob (spec)
      remove all xfiles from files
    elif dir-spec:                  # "examples" (just a directory name)
      dir_specs = rest-of-words-on-line
      if dir_specs:
        for dspec in dir_specs:
          if include-spec:
            dir_files.append (recursive_glob (dir, dspec))
          elif exclude-spec:
            xfiles = recursive_glob (dir, dspec)
            remove all xfiles from dir_files
      else:
        dir_files = recursive_glob (dir, '*')


Other things we need to look for in creating a source distribution:
  - make sure there's a README
  - make sure the distribution meta-info is supplied and non-empty
    (*must* have name, version, ((author and author_email) or
    (maintainer and maintainer_email)), url

Frills:
  - make sure the setup script is called "setup.py"
  - make sure the README refers to "setup.py" (ie. has a line matching
    /^\s*python\s+setup\.py/)

A crazy idea that conflicts with having/requiring 'version' in setup.py:
  - make sure there's a version number in the "main file" (main file
    is __init__.py of first package, or the first module if no packages,
    or the first extension module if no pure Python modules)
  - XXX how do we look for __version__ in an extension module?
  - XXX do we import and look for __version__? or just scan source for
    /^__version__\s*=\s*"[^"]+"/ ?
  - what about 'version_from' as an alternative to 'version' -- then
    we know just where to search for the version -- no guessing about
    what the "main file" is
</braindump>

If you have comments, better get 'em in quick -- I've already started
implementing this.

        Greg

-- 
Greg Ward - software developer                    gward@cnri.reston.va.us
Corporation for National Research Initiatives    
1895 Preston White Drive                           voice: +1-703-620-8990
Reston, Virginia, USA  20191-5434                    fax: +1-703-620-0913