Basic dependency checking. setup() has two new optional arguments

requires and provides. requires is a sequence of strings, of the
form 'packagename-version'. The dependency checking so far merely
does an '__import__(packagename)' and checks for packagename.__version__
You can also leave off the version, and any version of the package
will be installed.
There's a special case for the package 'python' - sys.version_info
is used, so
requires= ( 'python-2.3', )
just works.

Provides is of the same format as requires - but if it's not supplied,
a provides is generated by adding the version to each entry in packages,
or modules if packages isn't there.
Provides is currently only used in the PKG-INFO file. Shortly, PyPI
will grow the ability to accept these lines, and register will be
updated to send them.

There's a new command 'checkdep' command that runs these checks.
For this version, only greater-than-or-equal checking is done. We'll
add the ability to specify an optional operator later.
This commit is contained in:
Anthony Baxter 2004-03-22 22:22:05 +00:00
parent a3837a0d63
commit 05f842bae2
5 changed files with 144 additions and 3 deletions

View file

@ -24,6 +24,7 @@ __all__ = ['build',
'bdist_dumb',
'bdist_rpm',
'bdist_wininst',
'checkdep',
# These two are reserved for future use:
#'bdist_sdux',
#'bdist_pkgtool',

View file

@ -0,0 +1,70 @@
"""distutils.command.x
Implements the Distutils 'x' command.
"""
# created 2000/mm/dd, John Doe
__revision__ = "$Id$"
from distutils.core import Command
class DependencyFailure(Exception): pass
class VersionTooOld(DependencyFailure): pass
class VersionNotKnown(DependencyFailure): pass
class checkdep (Command):
# Brief (40-50 characters) description of the command
description = "check package dependencies"
# List of option tuples: long name, short name (None if no short
# name), and help string.
# Later on, we might have auto-fetch and the like here. Feel free.
user_options = []
def initialize_options (self):
self.debug = None
# initialize_options()
def finalize_options (self):
pass
# finalize_options()
def run (self):
from distutils.version import LooseVersion
failed = []
for pkg, ver in self.distribution.metadata.requires:
if pkg == 'python':
if ver is not None:
# Special case the 'python' package
import sys
thisver = LooseVersion('%d.%d.%d'%sys.version_info[:3])
if thisver < ver:
failed.append(((pkg,ver), VersionTooOld(thisver)))
continue
# Kinda hacky - we should do more here
try:
mod = __import__(pkg)
except Exception, e:
failed.append(((pkg,ver), e))
continue
if ver is not None:
if hasattr(mod, '__version__'):
thisver = LooseVersion(mod.__version__)
if thisver < ver:
failed.append(((pkg,ver), VersionTooOld(thisver)))
else:
failed.append(((pkg,ver), VersionNotKnown()))
if failed:
raise DependencyFailure, failed
# run()
# class x

View file

@ -126,6 +126,8 @@ class install (Command):
"force installation (overwrite any existing files)"),
('skip-build', None,
"skip rebuilding everything (for testing/debugging)"),
('skip-checkdep', None,
"skip checking dependencies (use at own risk)"),
# Where to install documentation (eventually!)
#('doc-format=', None, "format of documentation to generate"),
@ -183,12 +185,15 @@ class install (Command):
# 'force' forces installation, even if target files are not
# out-of-date. 'skip_build' skips running the "build" command,
# handy if you know it's not necessary. 'warn_dir' (which is *not*
# handy if you know it's not necessary. 'skip_checkdep' skips
# the 'checkdep' command, if you are sure you can work around the
# dependency failure in another way. 'warn_dir' (which is *not*
# a user option, it's just there so the bdist_* commands can turn
# it off) determines whether we warn about installing to a
# directory not in sys.path.
self.force = 0
self.skip_build = 0
self.skip_checkdep = 0
self.warn_dir = 1
# These are only here as a conduit from the 'build' command to the
@ -500,6 +505,12 @@ class install (Command):
if not self.skip_build:
self.run_command('build')
# We check dependencies before we install
# For now, this is disabled. Before 2.4 is released, this will
# be turned on.
#if not self.skip_checkdep:
# self.run_command('checkdep')
# Run all sub-commands (at least those that need to be run)
for cmd_name in self.get_sub_commands():
self.run_command(cmd_name)