[Checkins] SVN: zc.buildout/trunk/src/zc/buildout/ Added newest keyword parameter to the install and build functions to

Jim Fulton jim at zope.com
Mon Feb 5 18:03:46 EST 2007


Log message for revision 72383:
  Added newest keyword parameter to the install and build functions to
  allow for getting less than the newest but still getting what's
  necessary.
  

Changed:
  U   zc.buildout/trunk/src/zc/buildout/easy_install.py
  U   zc.buildout/trunk/src/zc/buildout/easy_install.txt
  U   zc.buildout/trunk/src/zc/buildout/tests.py

-=-
Modified: zc.buildout/trunk/src/zc/buildout/easy_install.py
===================================================================
--- zc.buildout/trunk/src/zc/buildout/easy_install.py	2007-02-05 22:03:49 UTC (rev 72382)
+++ zc.buildout/trunk/src/zc/buildout/easy_install.py	2007-02-05 23:03:44 UTC (rev 72383)
@@ -1,4 +1,4 @@
-##############################################################################
+#############################################################################
 #
 # Copyright (c) 2005 Zope Corporation and Contributors.
 # All Rights Reserved.
@@ -85,381 +85,399 @@
     _indexes[key] = index
     return index
 
-def _satisfied(req, env, dest, executable, index, links):
-    dists = [dist for dist in env[req.project_name] if dist in req]
-    if not dists:
-        logger.debug('We have no distributions for %s', req.project_name)
-        return None
+clear_index_cache = _indexes.clear
 
-    # Note that dists are sorted from best to worst, as promised by
-    # env.__getitem__
+if sys.platform == 'win32':
+    # work around spawn lamosity on windows
+    # XXX need safe quoting (see the subproces.list2cmdline) and test
+    def _safe_arg(arg):
+        return '"%s"' % arg
+else:
+    _safe_arg = str
 
-    for dist in dists:
-        if (dist.precedence == pkg_resources.DEVELOP_DIST):
-            logger.debug('We have a develop egg for %s', req)
-            return dist
+_easy_install_cmd = _safe_arg(
+    'from setuptools.command.easy_install import main; main()'
+    )
 
-    # Find an upprt limit in the specs, if there is one:
-    specs = [(pkg_resources.parse_version(v), op) for (op, v) in req.specs]
-    specs.sort()
-    maxv = None
-    greater = False
-    lastv = None
-    for v, op in specs:
-        if op == '==' and not greater:
-            maxv = v
-        elif op in ('>', '>=', '!='):
-            maxv = None
-            greater == True
-        elif op == '<':
-            maxv = None
-            greater == False
-        elif op == '<=':
-            maxv = v
-            greater == False
+class Installer:
 
-        if v == lastv:
-            # Repeated versions values are undefined, so
-            # all bets are off
-            maxv = None
-            greater = True
-        else:
-            lastv = v
+    def __init__(self,
+                 dest=None,
+                 links=(),
+                 index=None,
+                 executable=sys.executable,
+                 always_unzip=False,
+                 path=None,
+                 newest=True,
+                 ):
+        self._dest = dest
+        self._links = list(links)
+        self._index_url = index
+        self._executable = executable
+        self._always_unzip = always_unzip
+        path = (path and path[:] or []) + buildout_and_setuptools_path
+        if dest is not None and dest not in path:
+            path.insert(0, dest)
+        self._path = path
+        self._newest = newest
+        self._env = pkg_resources.Environment(path,
+                                              python=_get_version(executable))
+        self._index = _get_index(executable, index, links)
 
-    best_we_have = dists[0] # Because dists are sorted from best to worst
+    def _satisfied(self, req):
+        dists = [dist for dist in self._env[req.project_name] if dist in req]
+        if not dists:
+            logger.debug('We have no distributions for %s', req.project_name)
+            return None
 
-    # Check if we have the upper limit
-    if maxv is not None and best_we_have.version == maxv:
-        logger.debug('We have the best distribution that satisfies\n%s',
-                     req)
-        return best_we_have
+        # Note that dists are sorted from best to worst, as promised by
+        # env.__getitem__
 
-    # We have some installed distros.  There might, theoretically, be
-    # newer ones.  Let's find out which ones are available and see if
-    # any are newer.  We only do this if we're willing to install
-    # something, which is only true if dest is not None:
+        for dist in dists:
+            if (dist.precedence == pkg_resources.DEVELOP_DIST):
+                logger.debug('We have a develop egg for %s', req)
+                return dist
 
-    if dest is not None:
-        best_available = _get_index(executable, index, links).obtain(req)
-    else:
-        best_available = None
+        if not self._newest:
+            # We don't need the newest, so we'll use the newest one we
+            # find, which is the first returned by
+            # Environment.__getitem__.
+            return dists[0]
 
-    if best_available is None:
-        # That's a bit odd.  There aren't any distros available.
-        # We should use the best one we have that meets the requirement.
-        logger.debug(
-            'There are no distros available that meet %s. Using our best.', req)
-        return best_we_have
-    else:
-        # Let's find out if we already have the best available:
-        if best_we_have.parsed_version >= best_available.parsed_version:
-            # Yup. Use it.
-            logger.debug('We have the best distribution that satisfies\n%s', req)
+        # Find an upprt limit in the specs, if there is one:
+        specs = [(pkg_resources.parse_version(v), op) for (op, v) in req.specs]
+        specs.sort()
+        maxv = None
+        greater = False
+        lastv = None
+        for v, op in specs:
+            if op == '==' and not greater:
+                maxv = v
+            elif op in ('>', '>=', '!='):
+                maxv = None
+                greater == True
+            elif op == '<':
+                maxv = None
+                greater == False
+            elif op == '<=':
+                maxv = v
+                greater == False
+
+            if v == lastv:
+                # Repeated versions values are undefined, so
+                # all bets are off
+                maxv = None
+                greater = True
+            else:
+                lastv = v
+
+        best_we_have = dists[0] # Because dists are sorted from best to worst
+
+        # Check if we have the upper limit
+        if maxv is not None and best_we_have.version == maxv:
+            logger.debug('We have the best distribution that satisfies\n%s',
+                         req)
             return best_we_have
 
-    return None
+        # We have some installed distros.  There might, theoretically, be
+        # newer ones.  Let's find out which ones are available and see if
+        # any are newer.  We only do this if we're willing to install
+        # something, which is only true if dest is not None:
 
+        
+        if self._dest is not None:
+            best_available = self._index.obtain(req)
+        else:
+            best_available = None
 
-if sys.platform == 'win32':
-    # work around spawn lamosity on windows
-    # XXX need safe quoting (see the subproces.list2cmdline) and test
-    def _safe_arg(arg):
-        return '"%s"' % arg
-else:
-    _safe_arg = str
+        if best_available is None:
+            # That's a bit odd.  There aren't any distros available.
+            # We should use the best one we have that meets the requirement.
+            logger.debug(
+                'There are no distros available that meet %s. Using our best.',
+                req)
+            return best_we_have
+        else:
+            # Let's find out if we already have the best available:
+            if best_we_have.parsed_version >= best_available.parsed_version:
+                # Yup. Use it.
+                logger.debug(
+                    'We have the best distribution that satisfies\n%s',
+                    req)
+                return best_we_have
 
-_easy_install_cmd = _safe_arg(
-    'from setuptools.command.easy_install import main; main()'
-    )
+        return None
 
-def _call_easy_install(spec, env, ws, dest, links, index,
-                       executable, always_unzip):
+    def _call_easy_install(self, spec, ws, dest):
 
-    path = _get_dist(pkg_resources.Requirement.parse('setuptools'),
-                     env, ws, dest, links, index, executable, False).location
- 
-    args = ('-c', _easy_install_cmd, '-mUNxd', _safe_arg(dest))
-    if always_unzip:
-        args += ('-Z', )
-    level = logger.getEffectiveLevel()
-    if level > logging.DEBUG:
-        args += ('-q', )
-    elif level < logging.DEBUG:
-        args += ('-v', )
-    
-    args += (spec, )
+        path = self._get_dist(pkg_resources.Requirement.parse('setuptools'),
+                              ws, False).location
 
-    if level <= logging.DEBUG:
-        logger.debug('Running easy_install:\n%s "%s"\npath=%s\n',
-                     executable, '" "'.join(args), path)
+        args = ('-c', _easy_install_cmd, '-mUNxd', _safe_arg(dest))
+        if self._always_unzip:
+            args += ('-Z', )
+        level = logger.getEffectiveLevel()
+        if level > logging.DEBUG:
+            args += ('-q', )
+        elif level < logging.DEBUG:
+            args += ('-v', )
 
-    args += (dict(os.environ, PYTHONPATH=path), )
-    sys.stdout.flush() # We want any pending output first
-    exit_code = os.spawnle(os.P_WAIT, executable, executable, *args)
-    assert exit_code == 0
+        args += (spec, )
 
+        if level <= logging.DEBUG:
+            logger.debug('Running easy_install:\n%s "%s"\npath=%s\n',
+                         executable, '" "'.join(args), path)
 
-def _get_dist(requirement, env, ws,
-              dest, links, index_url, executable, always_unzip):
-    
-    # Maybe an existing dist is already the best dist that satisfies the
-    # requirement
-    dist = _satisfied(requirement, env, dest, executable, index_url, links)
+        args += (dict(os.environ, PYTHONPATH=path), )
+        sys.stdout.flush() # We want any pending output first
+        exit_code = os.spawnle(os.P_WAIT, self._executable, self._executable,
+                               *args)
+        assert exit_code == 0
 
-    if dist is None:
-        if dest is not None:
-            logger.info("Getting new distribution for %s", requirement)
 
-            # Retrieve the dist:
-            index = _get_index(executable, index_url, links)
-            dist = index.obtain(requirement)
-            if dist is None:
-                raise zc.buildout.UserError(
-                    "Couldn't find a distribution for %s."
-                    % requirement)
+    def _get_dist(self, requirement, ws, always_unzip):
 
-            fname = dist.location
-            if url_match(fname):
-                fname = urlparse.urlparse(fname)[2]
-                
-            if fname.endswith('.egg'):
-                # It's already an egg, just fetch it into the dest
-                tmp = tempfile.mkdtemp('get_dist')
-                try:
-                    dist = index.fetch_distribution(requirement, tmp)
-                    if dist is None:
-                        raise zc.buildout.UserError(
-                            "Couln't download a distribution for %s."
-                            % requirement)
+        # Maybe an existing dist is already the best dist that satisfies the
+        # requirement
+        dist = self._satisfied(requirement)
 
-                    newloc = os.path.join(
-                        dest, os.path.basename(dist.location))
+        if dist is None:
+            if self._dest is not None:
+                logger.info("Getting new distribution for %s", requirement)
 
-                    if os.path.isdir(dist.location):
-                        # we got a directory. It must have been
-                        # obtained locally.  Jut copy it.
-                        shutil.copytree(dist.location, newloc)
-                    else:
+                # Retrieve the dist:
+                index = self._index
+                dist = index.obtain(requirement)
+                if dist is None:
+                    raise zc.buildout.UserError(
+                        "Couldn't find a distribution for %s."
+                        % requirement)
 
-                        if always_unzip:
-                            should_unzip = True
-                        else:
-                            metadata = pkg_resources.EggMetadata(
-                                zipimport.zipimporter(dist.location)
-                                )
-                            should_unzip = (
-                                metadata.has_metadata('not-zip-safe')
-                                or not metadata.has_metadata('zip-safe')
-                                )
+                fname = dist.location
+                if url_match(fname):
+                    fname = urlparse.urlparse(fname)[2]
 
-                        if should_unzip:
-                            setuptools.archive_util.unpack_archive(
-                                dist.location, newloc)
+                if fname.endswith('.egg'):
+                    # It's already an egg, just fetch it into the dest
+                    tmp = tempfile.mkdtemp('get_dist')
+                    try:
+                        dist = index.fetch_distribution(requirement, tmp)
+                        if dist is None:
+                            raise zc.buildout.UserError(
+                                "Couln't download a distribution for %s."
+                                % requirement)
+
+                        newloc = os.path.join(
+                            self._dest, os.path.basename(dist.location))
+
+                        if os.path.isdir(dist.location):
+                            # we got a directory. It must have been
+                            # obtained locally.  Jut copy it.
+                            shutil.copytree(dist.location, newloc)
                         else:
-                            shutil.copyfile(dist.location, newloc)
-                        
-                finally:
-                    shutil.rmtree(tmp)
 
-            else:
-                # It's some other kind of dist.  We'll download it to
-                # a temporary directory and let easy_install have it's
-                # way with it:
-                tmp = tempfile.mkdtemp('get_dist')
-                try:
-                    dist = index.fetch_distribution(requirement, tmp)
+                            if self._always_unzip:
+                                should_unzip = True
+                            else:
+                                metadata = pkg_resources.EggMetadata(
+                                    zipimport.zipimporter(dist.location)
+                                    )
+                                should_unzip = (
+                                    metadata.has_metadata('not-zip-safe')
+                                    or not metadata.has_metadata('zip-safe')
+                                    )
 
-                    # May need a new one.  Call easy_install
-                    _call_easy_install(
-                        dist.location, env, ws, dest, links, index_url,
-                        executable, always_unzip)
-                finally:
-                    shutil.rmtree(tmp)
+                            if should_unzip:
+                                setuptools.archive_util.unpack_archive(
+                                    dist.location, newloc)
+                            else:
+                                shutil.copyfile(dist.location, newloc)
 
+                    finally:
+                        shutil.rmtree(tmp)
 
-            # Because we have added a new egg, we need to rescan
-            # the destination directory.
+                else:
+                    # It's some other kind of dist.  We'll download it to
+                    # a temporary directory and let easy_install have it's
+                    # way with it:
+                    tmp = tempfile.mkdtemp('get_dist')
+                    try:
+                        dist = index.fetch_distribution(requirement, tmp)
 
-            # We may overwrite distributions, so clear importer
-            # cache.
-            sys.path_importer_cache.clear()
+                        # May need a new one.  Call easy_install
+                        self._call_easy_install(dist.location, ws, self._dest)
+                    finally:
+                        shutil.rmtree(tmp)
 
-            env.scan([dest])
-            dist = env.best_match(requirement, ws)
-            logger.info("Got %s", dist)            
-        else:
-            dist = env.best_match(requirement, ws)
 
-    if dist is None:
-        raise ValueError("Couldn't find", requirement)
+                # Because we have added a new egg, we need to rescan
+                # the destination directory.
 
-    # XXX Need test for this
-    if dist.has_metadata('dependency_links.txt'):
-        for link in dist.get_metadata_lines('dependency_links.txt'):
-            link = link.strip()
-            if link not in links:
-                links.append(link)
-                
-    return dist
+                # We may overwrite distributions, so clear importer
+                # cache.
+                sys.path_importer_cache.clear()
 
-def _maybe_add_setuptools(ws, dist, env, dest, links, index, executable):
-    if dist.has_metadata('namespace_packages.txt'):
-        for r in dist.requires():
-            if r.project_name == 'setuptools':
-                break
-        else:
-            # We have a namespace package but no requirement for setuptools
-            if dist.precedence == pkg_resources.DEVELOP_DIST:
-                logger.warn(
-                    "Develop distribution for %s\n"
-                    "uses namespace packages but the distribution "
-                    "does not require setuptools.",
-                    dist)
-            requirement = pkg_resources.Requirement.parse('setuptools')
-            if ws.find(requirement) is None:
-                dist = _get_dist(requirement, env, ws,
-                                 dest, links, index, executable,
-                                 False)
-                ws.add(dist)
-    
-    
-def install(specs, dest,
-            links=(), index=None,
-            executable=sys.executable, always_unzip=False,
-            path=None, working_set=None):
+                self._env.scan([self._dest])
+                dist = self._env.best_match(requirement, ws)
+                logger.info("Got %s", dist)            
+            else:
+                dist = self._env.best_match(requirement, ws)
 
-    logger.debug('Installing %r', specs)
+        if dist is None:
+            raise ValueError("Couldn't find", requirement)
 
-    path = path and path[:] or []
-    if dest is not None and dest not in path:
-        path.insert(0, dest)
+        # XXX Need test for this
+        if dist.has_metadata('dependency_links.txt'):
+            for link in dist.get_metadata_lines('dependency_links.txt'):
+                link = link.strip()
+                if link not in self._links:
+                    self._links.append(link)
+                    self._index = _get_index(self._executable,
+                                             self._index_url, self._links)
 
-    path += buildout_and_setuptools_path
+        return dist
 
-    links = list(links) # make copy, because we may need to mutate
-    
+    def _maybe_add_setuptools(self, ws, dist):
+        if dist.has_metadata('namespace_packages.txt'):
+            for r in dist.requires():
+                if r.project_name == 'setuptools':
+                    break
+            else:
+                # We have a namespace package but no requirement for setuptools
+                if dist.precedence == pkg_resources.DEVELOP_DIST:
+                    logger.warn(
+                        "Develop distribution for %s\n"
+                        "uses namespace packages but the distribution "
+                        "does not require setuptools.",
+                        dist)
+                requirement = pkg_resources.Requirement.parse('setuptools')
+                if ws.find(requirement) is None:
+                    dist = self._get_dist(requirement, ws, False)
+                    ws.add(dist)
 
-    # For each spec, see if it is already installed.  We create a working
-    # set to keep track of what we've collected and to make sue than the
-    # distributions assembled are consistent.
-    env = pkg_resources.Environment(path, python=_get_version(executable))
-    requirements = [pkg_resources.Requirement.parse(spec) for spec in specs]
 
-    if working_set is None:
-        ws = pkg_resources.WorkingSet([])
-    else:
-        ws = working_set
+    def install(self, specs, working_set=None):
 
-    for requirement in requirements:
-        dist = _get_dist(requirement, env, ws,
-                         dest, links, index, executable, always_unzip)
-        ws.add(dist)
-        _maybe_add_setuptools(ws, dist,
-                              env, dest, links, index, executable)
+        logger.debug('Installing %r', specs)
 
-    # OK, we have the requested distributions and they're in the working
-    # set, but they may have unmet requirements.  We'll simply keep
-    # trying to resolve requirements, adding missing requirements as they
-    # are reported.
-    #
-    # Note that we don't pass in the environment, because we
-    # want to look for new eggs unless what we have is the best that matches
-    # the requirement.
-    while 1:
-        try:
-            ws.resolve(requirements)
-        except pkg_resources.DistributionNotFound, err:
-            [requirement] = err
-            if dest:
-                logger.debug('Getting required %s', requirement)
-            dist = _get_dist(requirement, env, ws,
-                             dest, links, index, executable, always_unzip)
-            ws.add(dist)
-            _maybe_add_setuptools(ws, dist,
-                                  env, dest, links, index, executable)
+        path = self._path
+        dest = self._dest
+        if dest is not None and dest not in path:
+            path.insert(0, dest)
+
+        requirements = [pkg_resources.Requirement.parse(spec)
+                        for spec in specs]
+
+        if working_set is None:
+            ws = pkg_resources.WorkingSet([])
         else:
-            break
-            
-    return ws
+            ws = working_set
 
-def build(spec, dest, build_ext,
-          links=(), index=None,
-          executable=sys.executable,
-          path=None):
+        for requirement in requirements:
+            dist = self._get_dist(requirement, ws, self._always_unzip)
+            ws.add(dist)
+            self._maybe_add_setuptools(ws, dist)
 
-    index_url = index
+        # OK, we have the requested distributions and they're in the working
+        # set, but they may have unmet requirements.  We'll simply keep
+        # trying to resolve requirements, adding missing requirements as they
+        # are reported.
+        #
+        # Note that we don't pass in the environment, because we
+        # want to look for new eggs unless what we have is the best that matches
+        # the requirement.
+        while 1:
+            try:
+                ws.resolve(requirements)
+            except pkg_resources.DistributionNotFound, err:
+                [requirement] = err
+                if dest:
+                    logger.debug('Getting required %s', requirement)
+                dist = self._get_dist(requirement, ws, self._always_unzip)
+                ws.add(dist)
+                self._maybe_add_setuptools(ws, dist)
+            else:
+                break
 
-    logger.debug('Building %r', spec)
+        return ws
 
-    path = path and path[:] or []
-    if dest is not None:
-        path.insert(0, dest)
+    def build(self, spec, build_ext):
+        logger.debug('Building %r', spec)
 
-    path += buildout_and_setuptools_path
+        requirement = pkg_resources.Requirement.parse(spec)
 
-    links = list(links) # make copy, because we may need to mutate
-    
-    # For each spec, see if it is already installed.  We create a working
-    # set to keep track of what we've collected and to make sue than the
-    # distributions assembled are consistent.
-    env = pkg_resources.Environment(path, python=_get_version(executable))
-    requirement = pkg_resources.Requirement.parse(spec)
+        dist = self._satisfied(requirement)
+        if dist is not None:
+            return dist.location
 
-    dist = _satisfied(requirement, env, dest, executable, index_url, links)
-    if dist is not None:
-        return [dist.location]
+        undo = []
+        try:
+            tmp = tempfile.mkdtemp('build')
+            undo.append(lambda : shutil.rmtree(tmp)) 
+            tmp2 = tempfile.mkdtemp('build')
+            undo.append(lambda : shutil.rmtree(tmp2))
 
-    undo = []
-    try:
-        tmp = tempfile.mkdtemp('build')
-        undo.append(lambda : shutil.rmtree(tmp)) 
-        tmp2 = tempfile.mkdtemp('build')
-        undo.append(lambda : shutil.rmtree(tmp2))
+            dist = self._index.fetch_distribution(
+                requirement, tmp2, False, True)
+            if dist is None:
+                raise zc.buildout.UserError(
+                    "Couldn't find a source distribution for %s."
+                    % requirement)
+            setuptools.archive_util.unpack_archive(dist.location, tmp)
 
-        index = _get_index(executable, index_url, links)
-        dist = index.fetch_distribution(requirement, tmp2, False, True)
-        if dist is None:
-            raise zc.buildout.UserError(
-                "Couldn't find a source distribution for %s."
-                % requirement)
-        setuptools.archive_util.unpack_archive(dist.location, tmp)
+            if os.path.exists(os.path.join(tmp, 'setup.py')):
+                base = tmp
+            else:
+                setups = glob.glob(os.path.join(tmp, '*', 'setup.py'))
+                if not setups:
+                    raise distutils.errors.DistutilsError(
+                        "Couldn't find a setup script in %s"
+                        % os.path.basename(dist.location)
+                        )
+                if len(setups) > 1:
+                    raise distutils.errors.DistutilsError(
+                        "Multiple setup scripts in %s"
+                        % os.path.basename(dist.location)
+                        )
+                base = os.path.dirname(setups[0])
 
-        if os.path.exists(os.path.join(tmp, 'setup.py')):
-            base = tmp
-        else:
-            setups = glob.glob(os.path.join(tmp, '*', 'setup.py'))
-            if not setups:
-                raise distutils.errors.DistutilsError(
-                    "Couldn't find a setup script in %s"
-                    % os.path.basename(dist.location)
-                    )
-            if len(setups) > 1:
-                raise distutils.errors.DistutilsError(
-                    "Multiple setup scripts in %s"
-                    % os.path.basename(dist.location)
-                    )
-            base = os.path.dirname(setups[0])
 
+            setup_cfg = os.path.join(base, 'setup.cfg')
+            if not os.path.exists(setup_cfg):
+                f = open(setup_cfg, 'w')
+                f.close()
+            setuptools.command.setopt.edit_config(
+                setup_cfg, dict(build_ext=build_ext))
 
-        setup_cfg = os.path.join(base, 'setup.cfg')
-        if not os.path.exists(setup_cfg):
-            f = open(setup_cfg, 'w')
-            f.close()
-        setuptools.command.setopt.edit_config(
-            setup_cfg, dict(build_ext=build_ext))
+            tmp3 = tempfile.mkdtemp('build', dir=self._dest)
+            undo.append(lambda : shutil.rmtree(tmp3)) 
 
-        tmp3 = tempfile.mkdtemp('build', dir=dest)
-        undo.append(lambda : shutil.rmtree(tmp3)) 
+            self._call_easy_install(base, pkg_resources.WorkingSet(), tmp3)
 
-        _call_easy_install(base, env, pkg_resources.WorkingSet(),
-                           tmp3, links, index_url, executable, True)
+            return _copyeggs(tmp3, self._dest, '.egg', undo)
 
-        return _copyeggs(tmp3, dest, '.egg', undo)
+        finally:
+            undo.reverse()
+            [f() for f in undo]
+
+
+def install(specs, dest,
+            links=(), index=None,
+            executable=sys.executable, always_unzip=False,
+            path=None, working_set=None, newest=True):
+    installer = Installer(dest, links, index, executable, always_unzip, path,
+                          newest)
+    return installer.install(specs, working_set)
+
+
+def build(spec, dest, build_ext,
+          links=(), index=None,
+          executable=sys.executable,
+          path=None, newest=True):
+    installer = Installer(dest, links, index, executable, True, path, newest)
+    return installer.build(spec, build_ext)
+
         
-    finally:
-        undo.reverse()
-        [f() for f in undo]
-        
 
 def _rm(*paths):
     for path in paths:
@@ -477,8 +495,8 @@
             _rm(new)
             os.rename(os.path.join(src, name), new)
             result.append(new)
-
-    assert len(result) == 1
+        
+    assert len(result) == 1, str(result)
     undo.pop()
     
     return result[0]

Modified: zc.buildout/trunk/src/zc/buildout/easy_install.txt
===================================================================
--- zc.buildout/trunk/src/zc/buildout/easy_install.txt	2007-02-05 22:03:49 UTC (rev 72382)
+++ zc.buildout/trunk/src/zc/buildout/easy_install.txt	2007-02-05 23:03:44 UTC (rev 72383)
@@ -21,7 +21,10 @@
 
 - Distutils options for building extensions can be passed.
 
-The easy_install module provides a method, install, for installing one
+Distribution installation
+-------------------------
+
+The easy_install module provides a function, install, for installing one
 or more packages and their dependencies.  The install function takes 2
 positional arguments:
 
@@ -68,6 +71,13 @@
    you to call install multiple times, if necessary, to gather
    multiple sets of requirements.
 
+newest
+   A boolian value indicating whether to search for new distributions
+   when already-installed distributions meet the requirement.  When
+   this is true, the default, and when the destination directory is
+   not None, then the install function will search for the newest
+   distributions that satisfy the requirements.
+
 The install method returns a working set containing the distributions
 needed to meet the given requirements.
 
@@ -111,10 +121,19 @@
     -  demo-0.2-py2.4.egg
     -  demoneeded-1.1-py2.4.egg
 
-If we ask for the demo distribution without a version restriction,
-we'll get the newer version:
+If we remove the version restriction on demo, but specify a false
+value for newest, no new didstributions will be installed:
 
     >>> ws = zc.buildout.easy_install.install(
+    ...     ['demo'], dest, links=[link_server], index=link_server+'index/',
+    ...     newest=False)
+    >>> ls(dest)
+    -  demo-0.2-py2.4.egg
+    -  demoneeded-1.1-py2.4.egg
+
+If we leave off the newst option, we'll get an update for demo:
+
+    >>> ws = zc.buildout.easy_install.install(
     ...     ['demo'], dest, links=[link_server], index=link_server+'index/')
     >>> ls(dest)
     -  demo-0.2-py2.4.egg
@@ -480,10 +499,14 @@
    A list of additional directories to search for locally-installed
    distributions.
 
-always_unzip
-   A flag indicating that newly-downloaded distributions should be
-   directories even if they could be installed as zip files.
+newest
+   A boolian value indicating whether to search for new distributions
+   when already-installed distributions meet the requirement.  When
+   this is true, the default, and when the destination directory is
+   not None, then the install function will search for the newest
+   distributions that satisfy the requirements.
 
+
 Our link server included a source distribution that includes a simple
 extension, extdemo.c::
 
@@ -534,6 +557,57 @@
     d  demoneeded-1.1-py2.4.egg
     d  extdemo-1.4-py2.4-unix-i686.egg
 
+Let's update our link server with a new version of extdemo:
+
+    >>> update_extdemo()
+    >>> print get(link_server),
+    <html><body>
+    <a href="demo-0.1-py2.4.egg">demo-0.1-py2.4.egg</a><br>
+    <a href="demo-0.2-py2.4.egg">demo-0.2-py2.4.egg</a><br>
+    <a href="demo-0.3-py2.4.egg">demo-0.3-py2.4.egg</a><br>
+    <a href="demoneeded-1.0.zip">demoneeded-1.0.zip</a><br>
+    <a href="demoneeded-1.1.zip">demoneeded-1.1.zip</a><br>
+    <a href="extdemo-1.4.zip">extdemo-1.4.zip</a><br>
+    <a href="extdemo-1.5.zip">extdemo-1.5.zip</a><br>
+    <a href="index/">index/</a><br>
+    <a href="other-1.0-py2.4.egg">other-1.0-py2.4.egg</a><br>
+    </body></html>
+
+The easy_install caches information about servers to reduce network
+access. To see the update, we have to call the clear_index_cache
+function to clear the index cache:
+
+    >>> zc.buildout.easy_install.clear_index_cache()
+
+If we run build with newest set to False, we won't get an update:
+
+    >>> zc.buildout.easy_install.build(
+    ...   'extdemo', dest, 
+    ...   {'include-dirs': os.path.join(sample_buildout, 'include')},
+    ...   links=[link_server], index=link_server+'index/',
+    ...   newest=False)
+    '/sample-install/extdemo-1.4-py2.4-linux-i686.egg'
+
+    >>> ls(dest)
+    d  demo-0.3-py2.4.egg
+    d  demoneeded-1.1-py2.4.egg
+    d  extdemo-1.4-py2.4-unix-i686.egg
+
+But if we run it with the default True setting for newest, then we'll
+get an updated egg:
+
+    >>> zc.buildout.easy_install.build(
+    ...   'extdemo', dest, 
+    ...   {'include-dirs': os.path.join(sample_buildout, 'include')},
+    ...   links=[link_server], index=link_server+'index/')
+    '/sample-install/extdemo-1.5-py2.4-unix-i686.egg'
+
+    d  demo-0.3-py2.4.egg
+    d  demoneeded-1.1-py2.4.egg
+    d  extdemo-1.4-py2.4-unix-i686.egg
+    d  extdemo-1.5-py2.4-unix-i686.egg
+
+
 Handling custom build options for extensions in develop eggs
 ------------------------------------------------------------
 
@@ -586,6 +660,7 @@
     d  demo-0.3-py2.4.egg
     d  demoneeded-1.1-py2.4.egg
     d  extdemo-1.4-py2.4-linux-i686.egg
+    d  extdemo-1.5-py2.4-linux-i686.egg
     -  extdemo.egg-link
 
 And that the source directory contains the compiled extension:

Modified: zc.buildout/trunk/src/zc/buildout/tests.py
===================================================================
--- zc.buildout/trunk/src/zc/buildout/tests.py	2007-02-05 22:03:49 UTC (rev 72382)
+++ zc.buildout/trunk/src/zc/buildout/tests.py	2007-02-05 23:03:44 UTC (rev 72383)
@@ -1047,19 +1047,22 @@
 extdemo_setup_py = """
 from distutils.core import setup, Extension
 
-setup(name = "extdemo", version = "1.4", url="http://www.zope.org",
+setup(name = "extdemo", version = "%s", url="http://www.zope.org",
       author="Demo", author_email="demo at demo.com",
       ext_modules = [Extension('extdemo', ['extdemo.c'])],
       )
 """
 
-def add_source_dist(test):
-    
-    tmp = test.globs['extdemo'] = test.globs['tmpdir']('extdemo')
+def add_source_dist(test, version=1.4):
+
+    if 'extdemo' not in test.globs:
+        test.globs['extdemo'] = test.globs['tmpdir']('extdemo')
+
+    tmp = test.globs['extdemo']
     write = test.globs['write']
     try:
         write(tmp, 'extdemo.c', extdemo_c);
-        write(tmp, 'setup.py', extdemo_setup_py);
+        write(tmp, 'setup.py', extdemo_setup_py % version);
         write(tmp, 'README', "");
         write(tmp, 'MANIFEST.in', "include *.c\n");
         test.globs['sdist'](tmp, test.globs['sample_eggs'])
@@ -1075,7 +1078,9 @@
     add_source_dist(test)
     test.globs['link_server'] = test.globs['start_server'](
         test.globs['sample_eggs'])
+    test.globs['update_extdemo'] = lambda : add_source_dist(test, 1.5)
 
+        
 egg_parse = re.compile('([0-9a-zA-Z_.]+)-([0-9a-zA-Z_.]+)-py(\d[.]\d).egg$'
                        ).match
 def makeNewRelease(project, ws, dest):



More information about the Checkins mailing list