[Python-checkins] distutils2: merged upstream
tarek.ziade
python-checkins at python.org
Sun Sep 19 10:20:22 CEST 2010
tarek.ziade pushed cc240818f9f9 to distutils2:
http://hg.python.org/distutils2/rev/cc240818f9f9
changeset: 621:cc240818f9f9
parent: 620:b81715424b1c
parent: 556:272a3087dd6e
user: Konrad Delong <konryd at gmail.com>
date: Fri Aug 13 18:19:28 2010 +0200
summary: merged upstream
files: src/distutils2/install_with_deps.py, src/distutils2/tests/test_install_with_deps.py
diff --git a/docs/source/library/distutils2.version.rst b/docs/source/library/distutils2.version.rst
--- a/docs/source/library/distutils2.version.rst
+++ b/docs/source/library/distutils2.version.rst
@@ -5,12 +5,12 @@
Distutils2 ships with a python package capable to work with version numbers.
It's an implementation of version specifiers `as defined in PEP 345
<http://www.python.org/dev/peps/pep-0345/#version-specifiers>`_ about
-Metadata.
+Metadatas.
-`distutils2.version.NormalizedVersion`
-======================================
+NormalizedVersion
+==================
-A Normalized version corresponds to a specific version of a distribution, as
+A Normalized version is a specific version of a distribution, as
described in the PEP 345. So, you can work with the `NormalizedVersion` like
this::
@@ -31,11 +31,12 @@
NormalizedVersion is used internally by `VersionPredicate` to do his stuff.
-`distutils2.version.suggest_normalized_version`
------------------------------------------------
+Suggest a normalized version
+----------------------------
-You also can let the normalized version be suggested to you, using the
-`suggest_normalized_version` function::
+Before this version scheme, there was existing others ones. Distutils2 provides
+a tool that suggests a normalized version: the `suggest_normalized_version`
+function::
>>> suggest_normalized_version('2.1-rc1')
2.1c1
@@ -46,8 +47,8 @@
>>> print suggest_normalized_version('not a version')
None
-`distutils2.version.VersionPredicate`
-=====================================
+Dealing with version predicates
+===============================
`VersionPredicate` knows how to parse stuff like "ProjectName (>=version)", the
class also provides a `match` method to test if a version number is the version
@@ -59,6 +60,11 @@
>>> version.match("1.1.1")
True
-`is_valid_predicate`
---------------------
+You could test if a predicate is a valid one, usng the `is_valid_predicate`
+function::
+ >>> is_valid_predicate('FooBar (1.1)')
+ True
+ >>> is_valid_predicate('FooBar (+1.1)')
+ False
+
diff --git a/src/distutils2/errors.py b/src/distutils2/errors.py
--- a/src/distutils2/errors.py
+++ b/src/distutils2/errors.py
@@ -80,6 +80,10 @@
"""Byte compile error."""
+class DistutilsIndexError(DistutilsError):
+ """Any problem occuring during using the indexes."""
+
+
# Exception classes used by the CCompiler implementation classes
class CCompilerError(Exception):
"""Some compile/link operation failed."""
diff --git a/src/distutils2/index/errors.py b/src/distutils2/index/errors.py
--- a/src/distutils2/index/errors.py
+++ b/src/distutils2/index/errors.py
@@ -2,30 +2,26 @@
All errors and exceptions raised by PyPiIndex classes.
"""
-from distutils2.errors import DistutilsError
+from distutils2.errors import DistutilsIndexError
-class IndexesError(DistutilsError):
- """The base class for errors of the index python package."""
-
-
-class ProjectNotFound(IndexesError):
+class ProjectNotFound(DistutilsIndexError):
"""Project has not been found"""
-class DistributionNotFound(IndexesError):
+class DistributionNotFound(DistutilsIndexError):
"""The release has not been found"""
-class ReleaseNotFound(IndexesError):
+class ReleaseNotFound(DistutilsIndexError):
"""The release has not been found"""
-class CantParseArchiveName(IndexesError):
+class CantParseArchiveName(DistutilsIndexError):
"""An archive name can't be parsed to find distribution name and version"""
-class DownloadError(IndexesError):
+class DownloadError(DistutilsIndexError):
"""An error has occurs while downloading"""
@@ -33,13 +29,13 @@
"""Compared hashes does not match"""
-class UnsupportedHashName(IndexesError):
+class UnsupportedHashName(DistutilsIndexError):
"""A unsupported hashname has been used"""
-class UnableToDownload(IndexesError):
+class UnableToDownload(DistutilsIndexError):
"""All mirrors have been tried, without success"""
-class InvalidSearchField(IndexesError):
+class InvalidSearchField(DistutilsIndexError):
"""An invalid search field has been used"""
diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py
--- a/src/distutils2/index/simple.py
+++ b/src/distutils2/index/simple.py
@@ -17,7 +17,7 @@
from distutils2.index.base import BaseClient
from distutils2.index.dist import (ReleasesList, EXTENSIONS,
get_infos_from_url, MD5_HASH)
-from distutils2.index.errors import (IndexesError, DownloadError,
+from distutils2.index.errors import (DistutilsIndexError, DownloadError,
UnableToDownload, CantParseArchiveName,
ReleaseNotFound, ProjectNotFound)
from distutils2.index.mirrors import get_mirrors
@@ -395,7 +395,7 @@
fp = urllib2.urlopen(request)
except (ValueError, httplib.InvalidURL), v:
msg = ' '.join([str(arg) for arg in v.args])
- raise IndexesError('%s %s' % (url, msg))
+ raise DistutilsIndexError('%s %s' % (url, msg))
except urllib2.HTTPError, v:
return v
except urllib2.URLError, v:
diff --git a/src/distutils2/install_with_deps.py b/src/distutils2/install_tools.py
rename from src/distutils2/install_with_deps.py
rename to src/distutils2/install_tools.py
--- a/src/distutils2/install_with_deps.py
+++ b/src/distutils2/install_tools.py
@@ -15,155 +15,79 @@
"""
-def get_deps(requirements, index):
- """Return the dependencies of a project, as a depgraph object.
-
- Build a :class depgraph.DependencyGraph: for the given requirements
-
- If the project does not uses Metadata < 1.1, dependencies can't be handled
- from here, so it returns an empty list of dependencies.
-
- :param requirements: is a string containing the version predicate to take
- the project name and version specifier from.
- :param index: the index to use for making searches.
- """
- deps = []
- release = index.get_release(requirements)
- requires = release.metadata['Requires-Dist'] + release.metadata['Requires']
- deps.append(release) # include the release we are computing deps.
- for req in requires:
- deps.extend(get_deps(req, index))
- return deps
-
-
-def install(requirements, index=None, interactive=True, upgrade=True,
- prefer_source=True, prefer_final=True):
- """Given a list of distributions to install, a list of distributions to
- remove, and a list of conflicts, proceed and do what's needed to be done.
-
- :param requirements: is a *string* containing the requirements for this
- project (for instance "FooBar 1.1" or "BarBaz (<1.2)
- :param index: If an index is specified, use this one, otherwise, use
- :class index.ClientWrapper: to get project metadatas.
- :param interactive: if set to True, will prompt the user for interactions
- of needed. If false, use the default values.
- :param upgrade: If a project exists in a newer version, does the script
- need to install the new one, or keep the already installed
- version.
- :param prefer_source: used to tell if the user prefer source distributions
- over built dists.
- :param prefer_final: if set to true, pick up the "final" versions (eg.
- stable) over the beta, alpha (not final) ones.
- """
- # get the default index if none is specified
- if not index:
- index = wrapper.WrapperClient()
-
- # check if the project is already installed.
- installed_release = get_installed_release(requirements)
-
- # if a version that satisfy the requirements is already installed
- if installed_release and (interactive or upgrade):
- new_releases = index.get_releases(requirements)
- if (new_releases.get_last(requirements).version >
- installed_release.version):
- if interactive:
- # prompt the user to install the last version of the package.
- # set upgrade here.
- print "You want to install a package already installed on your"
- "system. A new version exists, you could just use the version"
- "you have, or upgrade to the latest version"
-
- upgrade = raw_input("Do you want to install the most recent one ? (Y/n)") or "Y"
- if upgrade in ('Y', 'y'):
- upgrade = True
- else:
- upgrade = False
- if not upgrade:
- return
-
- # create the depgraph from the dependencies of the release we want to
- # install
- graph = generate_graph(get_deps(requirements, index))
- from ipdb import set_trace
- set_trace()
- installed = [] # to uninstall on errors
- try:
- for release in graph.adjacency_list:
- dist = release.get_distribution()
- install(dist)
- installed.append(dist)
- print "%s have been installed on your system" % requirements
- except:
- print "an error has occured, uninstalling"
- for dist in installed:
- uninstall_dist(dist)
-
class InstallationException(Exception):
pass
-def get_install_info(requirements, index=None, already_installed=None):
+
+def _update_infos(infos, new_infos):
+ """extends the lists contained in the `info` dict with those contained
+ in the `new_info` one
+ """
+ for key, value in infos.items():
+ if key in new_infos:
+ infos[key].extend(new_infos[key])
+
+
+def get_infos(requirements, index=None, installed=None,
+ prefer_final=True):
"""Return the informations on what's going to be installed and upgraded.
:param requirements: is a *string* containing the requirements for this
project (for instance "FooBar 1.1" or "BarBaz (<1.2)")
:param index: If an index is specified, use this one, otherwise, use
:class index.ClientWrapper: to get project metadatas.
- :param already_installed: a list of already installed distributions.
+ :param installed: a list of already installed distributions.
+ :param prefer_final: when picking up the releases, prefer a "final" one
+ over a beta/alpha/etc one.
- The results are returned in a dict. For instance::
+ The results are returned in a dict, containing all the operations
+ needed to install the given requirements::
>>> get_install_info("FooBar (<=1.2)")
{'install': [<FooBar 1.1>], 'remove': [], 'conflict': []}
Conflict contains all the conflicting distributions, if there is a
conflict.
-
"""
- def update_infos(new_infos, infos):
- for key, value in infos.items():
- if key in new_infos:
- infos[key].extend(new_infos[key])
- return new_infos
if not index:
index = wrapper.ClientWrapper()
- logging.info("searching releases for %s" % requirements)
- # 1. get all the releases that match the requirements
+ if not installed:
+ installed = get_distributions()
+
+ # Get all the releases that match the requirements
try:
releases = index.get_releases(requirements)
except (ReleaseNotFound, ProjectNotFound), e:
- raise InstallationException('Release not found: "%s"' % requirements)
+ raise InstallationException('Release not found: "%s"' % requirements)
- # 2. pick up a release, and try to get the dependency tree
- release = releases.get_last(requirements)
+ # Pick up a release, and try to get the dependency tree
+ release = releases.get_last(requirements, prefer_final=prefer_final)
+
+ # Iter since we found something without conflicts
metadata = release.fetch_metadata()
- # 3. get the distributions already_installed on the system
- # 4. and add the one we want to install
- if not already_installed:
- already_installed = get_distributions()
+ # Get the distributions already_installed on the system
+ # and add the one we want to install
- logging.info("fetching %s %s dependencies" % (
- release.name, release.version))
- distributions = already_installed + [release]
+ distributions = installed + [release]
depgraph = generate_graph(distributions)
- # store all the already_installed packages in a list, in case of rollback.
- infos = {'install':[], 'remove': [], 'conflict': []}
+ # Store all the already_installed packages in a list, in case of rollback.
+ infos = {'install': [], 'remove': [], 'conflict': []}
- # 5. get what the missing deps are
+ # Get what the missing deps are
for dists in depgraph.missing.values():
if dists:
logging.info("missing dependencies found, installing them")
# we have missing deps
for dist in dists:
- update_infos(get_install_info(dist, index, already_installed),
- infos)
+ _update_infos(infos,
+ get_infos(dist, index, installed))
- # 6. fill in the infos
- existing = [d for d in already_installed if d.name == release.name]
+ # Fill in the infos
+ existing = [d for d in installed if d.name == release.name]
if existing:
infos['remove'].append(existing[0])
infos['conflict'].extend(depgraph.reverse_list[existing[0]])
diff --git a/src/distutils2/tests/test_install_with_deps.py b/src/distutils2/tests/test_install_tools.py
rename from src/distutils2/tests/test_install_with_deps.py
rename to src/distutils2/tests/test_install_tools.py
--- a/src/distutils2/tests/test_install_with_deps.py
+++ b/src/distutils2/tests/test_install_tools.py
@@ -4,10 +4,11 @@
from distutils2.tests import run_unittest
from distutils2.tests.support import unittest
from distutils2.index.xmlrpc import Client
-from distutils2.install_with_deps import (get_install_info,
+from distutils2.install_tools import (get_infos,
InstallationException)
from distutils2.metadata import DistributionMetadata
+
class FakeDist(object):
"""A fake distribution object, for tests"""
def __init__(self, name, version, deps):
@@ -20,16 +21,25 @@
def __repr__(self):
return '<FakeDist %s>' % self.name
+
def get_fake_dists(dists):
objects = []
for (name, version, deps) in dists:
- objects.append(FakeDist(name, version, deps))
+ objects.append(FakeDist(name, version, deps))
return objects
+
class TestInstallWithDeps(unittest.TestCase):
def _get_client(self, server, *args, **kwargs):
return Client(server.full_address, *args, **kwargs)
+ def _get_results(self, output):
+ """return a list of results"""
+ installed = [(o.name, '%s' % o.version) for o in output['install']]
+ remove = [(o.name, '%s' % o.version) for o in output['remove']]
+ conflict = [(o.name, '%s' % o.version) for o in output['conflict']]
+ return (installed, remove, conflict)
+
@use_xmlrpc_server()
def test_existing_deps(self, server):
# Test that the installer get the dependencies from the metadatas
@@ -55,17 +65,17 @@
'url': archive_path},
])
installed = get_fake_dists([('bacon', '0.1', []),])
- output = get_install_info("choxie", index=client,
- already_installed=installed)
+ output = get_infos("choxie", index=client,
+ installed=installed)
# we dont have installed bacon as it's already installed on the system.
self.assertEqual(0, len(output['remove']))
self.assertEqual(2, len(output['install']))
- readable_output = [(o.name, '%s' % o.version)
+ readable_output = [(o.name, '%s' % o.version)
for o in output['install']]
self.assertIn(('towel-stuff', '0.1'), readable_output)
self.assertIn(('choxie', '2.0.0.9'), readable_output)
-
+
@use_xmlrpc_server()
def test_upgrade_existing_deps(self, server):
# Tests that the existing distributions can be upgraded if needed.
@@ -85,11 +95,11 @@
'requires_dist': [],
'url': archive_path},
])
-
- output = get_install_info("choxie", index=client, already_installed=
- get_fake_dists([('bacon', '0.1', []),]))
+
+ output = get_infos("choxie", index=client, installed=
+ get_fake_dists([('bacon', '0.1', []),]))
installed = [(o.name, '%s' % o.version) for o in output['install']]
-
+
# we need bacon 0.2, but 0.1 is installed.
# So we expect to remove 0.1 and to install 0.2 instead.
remove = [(o.name, '%s' % o.version) for o in output['remove']]
@@ -101,7 +111,7 @@
@use_xmlrpc_server()
def test_conflicts(self, server):
- # Tests that conflicts are detected
+ # Tests that conflicts are detected
client = self._get_client(server)
archive_path = '%s/distribution.tar.gz' % server.full_address
server.xmlrpc.set_distributions([
@@ -118,16 +128,14 @@
'requires_dist': [],
'url': archive_path},
])
- already_installed = [('bacon', '0.1', []),
- ('chicken', '1.1', ['bacon (0.1)'])]
- output = get_install_info("choxie", index=client, already_installed=
- get_fake_dists(already_installed))
-
+ already_installed = [('bacon', '0.1', []),
+ ('chicken', '1.1', ['bacon (0.1)'])]
+ output = get_infos("choxie", index=client, installed=
+ get_fake_dists(already_installed))
+
# we need bacon 0.2, but 0.1 is installed.
# So we expect to remove 0.1 and to install 0.2 instead.
- installed = [(o.name, '%s' % o.version) for o in output['install']]
- remove = [(o.name, '%s' % o.version) for o in output['remove']]
- conflict = [(o.name, '%s' % o.version) for o in output['conflict']]
+ installed, remove, conflict = self._get_results(output)
self.assertIn(('choxie', '2.0.0.9'), installed)
self.assertIn(('towel-stuff', '0.1'), installed)
self.assertIn(('bacon', '0.2'), installed)
@@ -139,7 +147,7 @@
# Test that the isntalled raises an exception if the project does not
# exists.
client = self._get_client(server)
- self.assertRaises(InstallationException, get_install_info,
+ self.assertRaises(InstallationException, get_infos,
'unexistant project', index=client)
--
Repository URL: http://hg.python.org/distutils2
More information about the Python-checkins
mailing list