summaryrefslogtreecommitdiff
path: root/distutils2/install.py
diff options
context:
space:
mode:
authorTarek Ziade <tarek@ziade.org>2011-01-03 18:09:24 +0100
committerTarek Ziade <tarek@ziade.org>2011-01-03 18:09:24 +0100
commit1b60c948f776068f7ac7ca8b8814b0dabe5d2e49 (patch)
treeac7e11442c10e997072b097966f5f8171f0f9359 /distutils2/install.py
parent03dae7f7be990bc07f76db4007d782f28fa91b1d (diff)
downloaddisutils2-1b60c948f776068f7ac7ca8b8814b0dabe5d2e49.tar.gz
savepoint
Diffstat (limited to 'distutils2/install.py')
-rw-r--r--distutils2/install.py229
1 files changed, 211 insertions, 18 deletions
diff --git a/distutils2/install.py b/distutils2/install.py
index c20538a..e42ecf6 100644
--- 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 @@ def move_files(files, destination=None):
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 @@ def install_dists(dists, path=None):
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 @@ def install_from_infos(install=[], remove=[], conflicts=[], install_path=None):
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 @@ def get_infos(requirements, index=None, installed=None, prefer_final=True):
conflict.
"""
- if not index:
- index = wrapper.ClientWrapper()
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()
+
# Get all the releases that match the requirements
try:
releases = index.get_releases(requirements)
@@ -170,28 +332,36 @@ def get_infos(requirements, index=None, installed=None, prefer_final=True):
# 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 @@ def main(**attrs):
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()