summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpje <pje@571e12c6-e1fa-0310-aee7-ff1267fa46bd>2004-03-20 21:00:15 +0000
committerpje <pje@571e12c6-e1fa-0310-aee7-ff1267fa46bd>2004-03-20 21:00:15 +0000
commitbb83e0944014f8c62b587cbee19b1889d5546d45 (patch)
treec8f058dab2e305217b9e2e8e99ae1e69561bb960
parenta002492cd2114b5353be438e82f05c26aaa20c69 (diff)
downloadwsgiref-bb83e0944014f8c62b587cbee19b1889d5546d45.tar.gz
Flesh out 'depends' command to display dependencies' status, and halt if
all requirements aren't met. (Also, check planned install location for the dependencies, as well as checking sys.path.) Also: * Allow 'Feature()' objects to include 'Require()' objects, so that dependencies can be optional * 'Require()' objects can set a homepage, whose URL will be displayed by the 'depends' command if the dependency needs to be installed. * Misc. fixes/refactoring of version validation to properly handle "unknown" versions, and to decouple version fetching from version checking. * Updated TODO to remove various completed items. git-svn-id: svn://svn.eby-sarna.com/svnroot/wsgiref@244 571e12c6-e1fa-0310-aee7-ff1267fa46bd
-rw-r--r--setuptools/command/depends.py67
-rw-r--r--setuptools/depends.py34
-rw-r--r--setuptools/dist.py16
-rw-r--r--setuptools/tests/__init__.py69
4 files changed, 140 insertions, 46 deletions
diff --git a/setuptools/command/depends.py b/setuptools/command/depends.py
index e149fac..8917c83 100644
--- a/setuptools/command/depends.py
+++ b/setuptools/command/depends.py
@@ -1,7 +1,9 @@
from distutils.cmd import Command
-import os
+import os, sys
+
class depends(Command):
+
"""Download and install dependencies, if needed"""
description = "download and install dependencies, if needed"
@@ -13,15 +15,68 @@ class depends(Command):
"ignore options that won't be passed to child setup scripts"),
]
+ path_attrs = [
+ # Note: these must be in *reverse* order, as they are pushed onto the
+ # *front* of a copy of sys.path.
+ ('install','install_libbase'), # installation base if extra_path
+ ('install_lib','install_dir'), # where modules are installed
+ ]
+
+
def initialize_options(self):
self.temp = None
- self.install_purelib = self.install_platlib = None
- self.install_lib = self.install_libbase = None
- self.install_scripts = self.install_data = self.install_headers = None
- self.compiler = self.debug = self.force = None
def finalize_options(self):
self.set_undefined_options('build',('build_temp', 'temp'))
+ self.set_search_path()
+
+ def set_search_path(self):
+ """Determine paths to check for installed dependencies"""
+ path = sys.path[:] # copy sys path
+ for cmd,attr in self.path_attrs:
+ dir = getattr(self.get_finalized_command(cmd),attr,None)
+ if dir and dir not in path:
+ path.insert(0,dir) # prepend
+ self.search_path = path
def run(self):
- self.announce("downloading and building here")
+ self.announce("checking for installed dependencies")
+ needed = [
+ dep for dep in self.distribution.requires if self.is_needed(dep)
+ ]
+ if not needed:
+ self.announce("all dependencies are present and up-to-date")
+ return
+
+ # Alert the user to missing items
+ fmt = "\t%s\t%s\n"
+ items = [fmt % (dep.full_name(),dep.homepage) for dep in needed]
+ items.insert(0,"Please install the following packages first:\n")
+ items.append('')
+ raise SystemExit('\n'.join(items)) # dump msg to stderr and exit
+
+
+ def is_needed(self,dep):
+ """Does the specified dependency need to be installed/updated?"""
+
+ self.announce("searching for "+dep.full_name())
+ version = dep.get_version(self.search_path)
+
+ if version is None:
+ self.announce(name+" not found!")
+ return True
+
+ if str(version)=="unknown":
+ status = dep.name+" is installed"
+ else:
+ status = dep.name+" version "+str(version)+" found"
+
+ if dep.version_ok(version):
+ self.announce(status+" (OK)")
+ return False
+ else:
+ self.announce(status+" (update needed)")
+ return True
+
+
+
diff --git a/setuptools/depends.py b/setuptools/depends.py
index c3bc333..20e5cec 100644
--- a/setuptools/depends.py
+++ b/setuptools/depends.py
@@ -10,7 +10,9 @@ __all__ = [
class Require:
"""A prerequisite to building or installing a distribution"""
- def __init__(self,name,requested_version,module,attribute=None,format=None):
+ def __init__(self,name,requested_version,module,homepage='',
+ attribute=None,format=None
+ ):
if format is None and requested_version is not None:
format = StrictVersion
@@ -20,23 +22,21 @@ class Require:
if attribute is None:
attribute = '__version__'
- self.name = name
- self.requested_version = requested_version
- self.module = module
- self.attribute = attribute
- self.format = format
-
-
-
-
-
-
-
-
+ self.__dict__.update(locals())
+ del self.self
+ def full_name(self):
+ """Return full package/distribution name, w/version"""
+ if self.requested_version is not None:
+ return '%s-%s' % (self.name,self.requested_version)
+ return self.name
+ def version_ok(self,version):
+ """Is 'version' sufficiently up-to-date?"""
+ return self.attribute is None or self.format is None or \
+ str(version)<>"unknown" and version >= self.requested_version
def get_version(self, paths=None, default="unknown"):
@@ -66,18 +66,18 @@ class Require:
return v
+
def is_present(self,paths=None):
"""Return true if dependency is present on 'paths'"""
return self.get_version(paths) is not None
+
def is_current(self,paths=None):
"""Return true if dependency is present and up-to-date on 'paths'"""
version = self.get_version(paths)
if version is None:
return False
- return self.attribute is None or self.format is None or \
- version >= self.requested_version
-
+ return self.version_ok(version)
def _iter_code(code):
diff --git a/setuptools/dist.py b/setuptools/dist.py
index 2941f26..cd3af26 100644
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -1,7 +1,7 @@
__all__ = ['Distribution', 'Feature']
-
from distutils.core import Distribution as _Distribution
from distutils.core import Extension
+from setuptools.depends import Require
from setuptools.command.build_py import build_py
from setuptools.command.build_ext import build_ext
from setuptools.command.install import install
@@ -11,7 +11,6 @@ from distutils.errors import DistutilsSetupError
sequence = tuple, list
class Distribution(_Distribution):
-
"""Distribution with support for features, tests, and package data
This is an enhanced version of 'distutils.dist.Distribution' that
@@ -67,7 +66,6 @@ class Distribution(_Distribution):
self.cmdclass.setdefault('build_ext',build_ext)
self.cmdclass.setdefault('install',install)
self.cmdclass.setdefault('install_lib',install_lib)
-
if self.features:
self._set_global_opts_from_features()
@@ -288,7 +286,6 @@ class Distribution(_Distribution):
class Feature:
-
"""A subset of the distribution that can be excluded if unneeded/wanted
Features are created using these keyword arguments:
@@ -312,6 +309,8 @@ class Feature:
'requires' -- a string or sequence of strings naming features that should
also be included if this feature is included. Defaults to empty list.
+ May also contain 'Require' objects that should be added/removed from
+ the distribution.
'remove' -- a string or list of strings naming packages to be removed
from the distribution if this feature is *not* included. If the
@@ -345,15 +344,15 @@ class Feature:
self.standard = standard
self.available = available
self.optional = optional
-
- if isinstance(requires,str):
+ if isinstance(requires,(str,Require)):
requires = requires,
- self.requires = requires
+ self.requires = [r for r in requires if isinstance(r,str)]
+ er = [r for r in requires if not isinstance(r,str)]
+ if er: extras['requires'] = er
if isinstance(remove,str):
remove = remove,
-
self.remove = remove
self.extras = extras
@@ -368,7 +367,6 @@ class Feature:
"""Should this feature be included by default?"""
return self.available and self.standard
-
def include_in(self,dist):
"""Ensure feature and its requirements are included in distribution
diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py
index 662d4ec..913c65b 100644
--- a/setuptools/tests/__init__.py
+++ b/setuptools/tests/__init__.py
@@ -10,9 +10,10 @@ from distutils.core import Extension
from setuptools.depends import extract_constant, get_module_constant
from setuptools.depends import find_module, Require
from distutils.version import StrictVersion, LooseVersion
-
+from distutils.util import convert_path
import sys, os.path
+
def makeSetup(**args):
"""Return distribution from 'setup(**args)', without executing commands"""
@@ -38,7 +39,6 @@ def makeSetup(**args):
-
class DependsTests(TestCase):
def testExtractConst(self):
@@ -80,24 +80,21 @@ class DependsTests(TestCase):
get_module_constant('setuptools.tests','__doc__'),__doc__
)
- def testDependsCmd(self):
- dist = makeSetup()
- cmd = dist.get_command_obj('depends')
- cmd.ensure_finalized()
- self.assertEqual(cmd.temp, dist.get_command_obj('build').build_temp)
- self.assertEqual(cmd.install_lib, dist.get_command_obj('install').install_lib)
-
-
def testRequire(self):
+
req = Require('Distutils','1.0.3','distutils')
self.assertEqual(req.name, 'Distutils')
self.assertEqual(req.module, 'distutils')
self.assertEqual(req.requested_version, '1.0.3')
self.assertEqual(req.attribute, '__version__')
+ self.assertEqual(req.full_name(), 'Distutils-1.0.3')
from distutils import __version__
self.assertEqual(req.get_version(), __version__)
+ self.failUnless(req.version_ok('1.0.9'))
+ self.failIf(req.version_ok('0.9.1'))
+ self.failIf(req.version_ok('unknown'))
self.failUnless(req.is_present())
self.failUnless(req.is_current())
@@ -105,15 +102,18 @@ class DependsTests(TestCase):
req = Require('Distutils 3000','03000','distutils',format=LooseVersion)
self.failUnless(req.is_present())
self.failIf(req.is_current())
+ self.failIf(req.version_ok('unknown'))
req = Require('Do-what-I-mean','1.0','d-w-i-m')
self.failIf(req.is_present())
self.failIf(req.is_current())
- req = Require('Tests', None, 'tests')
+ req = Require('Tests', None, 'tests', homepage="http://example.com")
self.assertEqual(req.format, None)
self.assertEqual(req.attribute, None)
self.assertEqual(req.requested_version, None)
+ self.assertEqual(req.full_name(), 'Tests')
+ self.assertEqual(req.homepage, 'http://example.com')
paths = [os.path.dirname(p) for p in __path__]
self.failUnless(req.is_present(paths))
@@ -121,6 +121,47 @@ class DependsTests(TestCase):
+ def testDependsCmd(self):
+ path1 = convert_path('foo/bar/baz')
+ path2 = convert_path('foo/bar/baz/spam')
+
+ dist = makeSetup(
+ extra_path='spam',
+ script_args=['install','--install-lib',path1]
+ )
+
+ cmd = dist.get_command_obj('depends')
+ cmd.ensure_finalized()
+
+ self.assertEqual(cmd.temp, dist.get_command_obj('build').build_temp)
+ self.assertEqual(cmd.search_path, [path2,path1]+sys.path)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
class DistroTests(TestCase):
def setUp(self):
@@ -247,9 +288,10 @@ class DistroTests(TestCase):
class FeatureTests(TestCase):
def setUp(self):
+ self.req = Require('Distutils','1.0.3','distutils')
self.dist = makeSetup(
features={
- 'foo': Feature("foo",standard=True,requires='baz'),
+ 'foo': Feature("foo",standard=True,requires=['baz',self.req]),
'bar': Feature("bar", standard=True, packages=['pkg.bar'],
py_modules=['bar_et'], remove=['bar.ext'],
),
@@ -284,7 +326,6 @@ class FeatureTests(TestCase):
self.dist.features['dwim'].include_in, self.dist
)
-
def testFeatureOptions(self):
dist = self.dist
self.failUnless(
@@ -309,13 +350,13 @@ class FeatureTests(TestCase):
self.assertEqual(dist.with_foo,1)
self.assertEqual(dist.with_bar,0)
self.assertEqual(dist.with_baz,1)
-
self.failIf('bar_et' in dist.py_modules)
self.failIf('pkg.bar' in dist.packages)
self.failUnless('pkg.baz' in dist.packages)
self.failUnless('scripts/baz_it' in dist.scripts)
self.failUnless(('libfoo','foo/foofoo.c') in dist.libraries)
self.assertEqual(dist.ext_modules,[])
+ self.assertEqual(dist.requires, [self.req])
# If we ask for bar, it should fail because we explicitly disabled
# it on the command line