[Python-checkins] distutils2: savepoint
tarek.ziade
python-checkins at python.org
Sun Jan 30 10:43:57 CET 2011
tarek.ziade pushed 0a2b6ac0fed2 to distutils2:
http://hg.python.org/distutils2/rev/0a2b6ac0fed2
changeset: 922:0a2b6ac0fed2
parent: 856:0de7d5f4dae5
user: Tarek Ziade <tarek at ziade.org>
date: Mon Jan 03 18:09:24 2011 +0100
summary:
savepoint
files:
distutils2/index/dist.py
distutils2/index/simple.py
distutils2/index/xmlrpc.py
distutils2/install.py
distutils2/run.py
diff --git a/distutils2/index/dist.py b/distutils2/index/dist.py
--- a/distutils2/index/dist.py
+++ b/distutils2/index/dist.py
@@ -17,7 +17,6 @@
import urllib
import urlparse
import zipfile
-
try:
import hashlib
except ImportError:
@@ -206,6 +205,7 @@
__hash__ = object.__hash__
+
class DistInfo(IndexReference):
"""Represents a distribution retrieved from an index (sdist, bdist, ...)
"""
diff --git a/distutils2/index/simple.py b/distutils2/index/simple.py
--- a/distutils2/index/simple.py
+++ b/distutils2/index/simple.py
@@ -199,6 +199,7 @@
Currently, download one archive, extract it and use the PKG-INFO file.
"""
+ import pdb; pdb.set_trace()
release = self.get_distributions(project_name, version)
if not release._metadata:
location = release.get_distribution().unpack()
diff --git a/distutils2/index/xmlrpc.py b/distutils2/index/xmlrpc.py
--- a/distutils2/index/xmlrpc.py
+++ b/distutils2/index/xmlrpc.py
@@ -127,10 +127,17 @@
return release
def get_metadata(self, project_name, version):
- """Retreive project metadatas.
+ """Retrieve project metadata.
Return a ReleaseInfo object, with metadata informations filled in.
"""
+ # to be case-insensitive
+ projects = [d['name'] for d in
+ self.proxy.search({'name': project_name})
+ if d['name'].lower() == project_name]
+ if len(projects) > 0:
+ project_name = projects[0]
+
metadata = self.proxy.release_data(project_name, version)
project = self._get_project(project_name)
if version not in project.get_versions():
diff --git a/distutils2/install.py b/distutils2/install.py
--- a/distutils2/install.py
+++ b/distutils2/install.py
@@ -1,14 +1,18 @@
from tempfile import mkdtemp
-import logging
import shutil
import os
import errno
import itertools
+import sys
+import tarfile
+from distutils2 import logger
from distutils2._backport.pkgutil import get_distributions
+from distutils2._backport.sysconfig import get_config_var
from distutils2.depgraph import generate_graph
from distutils2.index import wrapper
from distutils2.index.errors import ProjectNotFound, ReleaseNotFound
+from distutils2.version import get_version_predicate
"""Provides installations scripts.
@@ -53,7 +57,129 @@
else:
raise e
os.rename(old, new)
- yield(old, new)
+ yield old, new
+
+
+
+# ripped from shutil
+def _ensure_directory(path):
+ """Ensure that the parent directory of `path` exists"""
+ dirname = os.path.dirname(path)
+ if not os.path.isdir(dirname):
+ os.makedirs(dirname)
+
+def _unpack_zipfile(filename, extract_dir):
+ try:
+ import zipfile
+ except ImportError:
+ raise ReadError('zlib not supported, cannot unpack this archive.')
+
+ if not zipfile.is_zipfile(filename):
+ raise ReadError("%s is not a zip file" % filename)
+
+ zip = zipfile.ZipFile(filename)
+ try:
+ for info in zip.infolist():
+ name = info.filename
+ if name.startswith('/') or '..' in name:
+ continue
+
+ target = os.path.join(extract_dir, *name.split('/'))
+ if not target:
+ continue
+
+ _ensure_directory(target)
+ if not name.endswith('/'):
+ # file
+ data = zip.read(info.filename)
+ f = open(target,'wb')
+ try:
+ f.write(data)
+ finally:
+ f.close()
+ del data
+ finally:
+ zip.close()
+
+def _unpack_tarfile(filename, extract_dir):
+ try:
+ tarobj = tarfile.open(filename)
+ except tarfile.TarError:
+ raise ReadError(
+ "%s is not a compressed or uncompressed tar file" % filename)
+ try:
+ tarobj.extractall(extract_dir)
+ finally:
+ tarobj.close()
+
+
+_UNPACKERS = (
+ (['.tar.gz', '.tgz', '.tar'], _unpack_tarfile),
+ (['.zip', '.egg'], _unpack_zipfile))
+
+
+def _unpack(filename, extract_dir=None):
+ if extract_dir is None:
+ extract_dir = os.path.dirname(filename)
+
+ for formats, func in _UNPACKERS:
+ for format in formats:
+ if filename.endswith(format):
+ func(filename, extract_dir)
+ return extract_dir
+
+ raise ValueError('Unknown archive format: %s' % filename)
+
+
+def _install_dist(dist, path):
+ """Install a distribution into a path"""
+ def _run_d1_install(archive_dir, path):
+ # backward compat: using setuptools or plain-distutils
+ cmd = '%s setup.py install --root=%s --record=%s'
+ setup_py = os.path.join(archive_dir, 'setup.py')
+ if 'setuptools' in open(setup_py).read():
+ cmd += ' --single-version-externally-managed'
+
+ # how to place this file in the egg-info dir
+ # for non-distutils2 projects ?
+ record_file = os.path.join(archive_dir, 'RECORD')
+ os.system(cmd % (sys.executable, path, record_file))
+ if not os.path.exists(record_file):
+ raise ValueError('Failed to install.')
+ return open(record_file).read().split('\n')
+
+ def _run_d2_install(archive_dir, path):
+ # using our own install command
+ raise NotImplementedError()
+
+ # download
+ archive = dist.download()
+
+ # unarchive
+ where = _unpack(archive)
+
+ # get into the dir
+ archive_dir = None
+ for item in os.listdir(where):
+ fullpath = os.path.join(where, item)
+ if os.path.isdir(fullpath):
+ archive_dir = fullpath
+ break
+
+ if archive_dir is None:
+ raise ValueError('Cannot locate the unpacked archive')
+
+ # install
+ old_dir = os.getcwd()
+ os.chdir(archive_dir)
+ try:
+ # distutils2 or distutils1 ?
+ if 'setup.py' in os.listdir(archive_dir):
+ return _run_d1_install(archive_dir, path)
+ else:
+ return _run_d2_install(archive_dir, path)
+ finally:
+ os.chdir(old_dir)
def install_dists(dists, path=None):
@@ -65,19 +191,23 @@
Return a list of installed files.
:param dists: distributions to install
- :param path: base path to install distribution on
+ :param path: base path to install distribution in
"""
if not path:
path = mkdtemp()
installed_dists, installed_files = [], []
for d in dists:
+ logger.info('Installing %s %s' % (d.name, d.version))
try:
- installed_files.extend(d.install(path))
+ installed_files.extend(_install_dist(d, path))
installed_dists.append(d)
except Exception, e :
+ logger.info('Failed. %s' % str(e))
+
+ # reverting
for d in installed_dists:
- d.uninstall()
+ _uninstall(d)
raise e
return installed_files
@@ -123,16 +253,26 @@
try:
if install:
installed_files = install_dists(install, install_path) # install to tmp first
- for files in temp_files.values():
- for old, new in files:
- os.remove(new)
- except Exception,e:
+ except Exception:
# if an error occurs, put back the files in the good place.
for files in temp_files.values():
for old, new in files:
shutil.move(new, old)
+ # now re-raising
+ raise
+
+ # we can emove them for good
+ for files in temp_files.values():
+ for old, new in files:
+ os.remove(new)
+
+
+def _get_setuptools_deps(release):
+ # NotImplementedError
+ pass
+
def get_infos(requirements, index=None, installed=None, prefer_final=True):
"""Return the informations on what's going to be installed and upgraded.
@@ -155,12 +295,34 @@
conflict.
"""
+
+ if not installed:
+ logger.info('Reading installed distributions')
+ installed = get_distributions(use_egg_info=True)
+
+ infos = {'install': [], 'remove': [], 'conflict': []}
+ # Is a compatible version of the project is already installed ?
+ predicate = get_version_predicate(requirements)
+ found = False
+ installed = list(installed)
+ for installed_project in installed:
+ # is it a compatible project ?
+ if predicate.name.lower() != installed_project.name.lower():
+ continue
+ found = True
+ logger.info('Found %s %s' % (installed_project.name,
+ installed_project.metadata.version))
+ if predicate.match(installed_project.metadata.version):
+ return infos
+
+ break
+
+ if not found:
+ logger.info('Project not installed.')
+
if not index:
index = wrapper.ClientWrapper()
- if not installed:
- installed = get_distributions(use_egg_info=True)
-
# Get all the releases that match the requirements
try:
releases = index.get_releases(requirements)
@@ -170,28 +332,36 @@
# 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
+ if release is None:
+ logger.info('Could not find a matching project')
+ return infos
+
+ import pdb; pdb.set_trace()
+
+ # this works for Metadata 1.2
metadata = release.fetch_metadata()
- # Get the distributions already_installed on the system
- # and add the one we want to install
+ # for earlier, we need to build setuptools deps if any
+ if 'requires_dist' not in metadata:
+ deps = _get_setuptools_deps(release)
+ else:
+ deps = metadata['requires_dist']
distributions = itertools.chain(installed, [release])
depgraph = generate_graph(distributions)
# Store all the already_installed packages in a list, in case of rollback.
- infos = {'install': [], 'remove': [], 'conflict': []}
-
# Get what the missing deps are
- for dists in depgraph.missing.values():
+ for dists in depgraph.missing[release]:
if dists:
- logging.info("missing dependencies found, installing them")
+ logger.info("missing dependencies found, installing them")
# we have missing deps
for dist in dists:
_update_infos(infos, get_infos(dist, index, installed))
# 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]])
@@ -214,5 +384,28 @@
attrs['requirements'] = sys.argv[1]
get_infos(**attrs)
+
+def install(project):
+ logger.info('Getting information about "%s".' % project)
+ try:
+ info = get_infos(project)
+ except InstallationException:
+ logger.info('Cound not find "%s".' % project)
+ return
+
+ if info['install'] == []:
+ logger.info('Nothing to install.')
+ return
+
+ install_path = get_config_var('base')
+ try:
+ install_from_infos(info['install'], info['remove'], info['conflict'],
+ install_path=install_path)
+
+ except InstallationConflict, e:
+ projects = ['%s %s' % (p.name, p.metadata.version) for p in e.args[0]]
+ logger.info('"%s" conflicts with "%s"' % (project, ','.join(projects)))
+
+
if __name__ == '__main__':
main()
diff --git a/distutils2/run.py b/distutils2/run.py
--- a/distutils2/run.py
+++ b/distutils2/run.py
@@ -1,7 +1,9 @@
import os
import sys
from optparse import OptionParser
+import logging
+from distutils2 import logger
from distutils2.util import grok_environment_error
from distutils2.errors import (DistutilsSetupError, DistutilsArgError,
DistutilsError, CCompilerError)
@@ -9,6 +11,7 @@
from distutils2 import __version__
from distutils2._backport.pkgutil import get_distributions, get_distribution
from distutils2.depgraph import generate_graph
+from distutils2.install import install
# This is a barebones help message generated displayed when the user
# runs the setup script with no arguments at all. More useful help
@@ -114,8 +117,17 @@
return dist
+def _set_logger():
+ logger.setLevel(logging.INFO)
+ sth = logging.StreamHandler(sys.stderr)
+ sth.setLevel(logging.INFO)
+ logger.addHandler(sth)
+ logger.propagate = 0
+
+
def main():
"""Main entry point for Distutils2"""
+ _set_logger()
parser = OptionParser()
parser.disable_interspersed_args()
parser.usage = '%prog [options] cmd1 cmd2 ..'
@@ -136,6 +148,10 @@
action="store_true", dest="fgraph", default=False,
help="Display the full graph for installed distributions.")
+ parser.add_option("-i", "--install",
+ action="store", dest="install",
+ help="Install a project.")
+
options, args = parser.parse_args()
if options.version:
print('Distutils2 %s' % __version__)
@@ -169,6 +185,10 @@
print(graph)
sys.exit(0)
+ if options.install is not None:
+ install(options.install)
+ sys.exit(0)
+
if len(args) == 0:
parser.print_help()
sys.exit(0)
--
Repository URL: http://hg.python.org/distutils2
More information about the Python-checkins
mailing list