Commit 2de648f4 authored by PJ Eby's avatar PJ Eby

Add option to allow specifying a download/extract/build directory, which

will be kept after installation completes.  Added an "installation report"
that tells you how to use 'require()' to activate a particular package
version.  Installer.install_eggs() now returns a list of Distribution
objects for the eggs it found and installed, so that the command-line tool
can print an installation report for each one.

--HG--
branch : setuptools
extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041031
parent a0453161
...@@ -166,13 +166,15 @@ class Installer: ...@@ -166,13 +166,15 @@ class Installer:
"""Manage a download/build/install process""" """Manage a download/build/install process"""
pth_file = None pth_file = None
cleanup = False
def __init__(self, instdir=None, zip_ok=False, multi=None, tmpdir=None): def __init__(self, instdir=None, zip_ok=False, multi=None, tmpdir=None):
if tmpdir is None: if tmpdir is None:
tmpdir = tempfile.mkdtemp(prefix="easy_install-") tmpdir = tempfile.mkdtemp(prefix="easy_install-")
self.cleanup = True
self.tmpdir = tmpdir elif not os.path.isdir(tmpdir):
os.makedirs(tmpdir)
self.tmpdir = os.path.realpath(tmpdir)
site_packages = get_python_lib() site_packages = get_python_lib()
if instdir is None or self.samefile(site_packages,instdir): if instdir is None or self.samefile(site_packages,instdir):
...@@ -194,15 +196,13 @@ class Installer: ...@@ -194,15 +196,13 @@ class Installer:
self.multi = multi self.multi = multi
def close(self): def close(self):
if os.path.isdir(self.tmpdir): if self.cleanup and os.path.isdir(self.tmpdir):
rmtree(self.tmpdir,True) rmtree(self.tmpdir,True)
def __del__(self): def __del__(self):
self.close() self.close()
def samefile(self,p1,p2): def samefile(self,p1,p2):
if hasattr(os.path,'samefile') and ( if hasattr(os.path,'samefile') and (
os.path.exists(p1) and os.path.exists(p2) os.path.exists(p1) and os.path.exists(p2)
...@@ -269,14 +269,14 @@ class Installer: ...@@ -269,14 +269,14 @@ class Installer:
setup_script = setups[0] setup_script = setups[0]
self._run_setup(setup_script) self._run_setup(setup_script)
eggs = []
for egg in glob( for egg in glob(
os.path.join(os.path.dirname(setup_script),'dist','*.egg') os.path.join(os.path.dirname(setup_script),'dist','*.egg')
): ):
self.install_egg(egg, self.zip_ok) eggs.append(self.install_egg(egg, self.zip_ok))
return eggs
...@@ -339,7 +339,7 @@ class Installer: ...@@ -339,7 +339,7 @@ class Installer:
sys.path.insert(0,os.getcwd()) sys.path.insert(0,os.getcwd())
DirectorySandbox(self.tmpdir).run( DirectorySandbox(self.tmpdir).run(
lambda: execfile( lambda: execfile(
setup_script, "setup.py",
{'__file__':setup_script, '__name__':'__main__'} {'__file__':setup_script, '__name__':'__main__'}
) )
) )
...@@ -391,7 +391,6 @@ class Installer: ...@@ -391,7 +391,6 @@ class Installer:
os.mkdir(destination) os.mkdir(destination)
self._extract_zip(egg_path, destination) self._extract_zip(egg_path, destination)
if self.pth_file is not None:
if os.path.isdir(destination): if os.path.isdir(destination):
dist = Distribution.from_filename( dist = Distribution.from_filename(
destination, metadata=PathMetadata( destination, metadata=PathMetadata(
...@@ -402,11 +401,12 @@ class Installer: ...@@ -402,11 +401,12 @@ class Installer:
metadata = EggMetadata(zipimport.zipimporter(destination)) metadata = EggMetadata(zipimport.zipimporter(destination))
dist = Distribution.from_filename(destination,metadata=metadata) dist = Distribution.from_filename(destination,metadata=metadata)
# remove old if self.pth_file is not None:
map(self.pth_file.remove, self.pth_file.get(dist.key,())) map(self.pth_file.remove, self.pth_file.get(dist.key,())) # drop old
if not self.multi: if not self.multi:
self.pth_file.add(dist) # add new self.pth_file.add(dist) # add new
self.pth_file.save() self.pth_file.save()
return dist
def _download_url(self, scheme, url): def _download_url(self, scheme, url):
...@@ -531,6 +531,47 @@ class Installer: ...@@ -531,6 +531,47 @@ class Installer:
def installation_report(self, dist):
"""Helpful installation message for display to package users"""
msg = "Installed %(eggloc)s to %(instdir)s"
if self.multi:
msg += """
Because this distribution was installed --multi-version or --install-dir,
before you can import modules from this package in an application, you
will need to 'import pkg_resources' and then use a 'require()' call
similar to one of these examples, in order to select the desired version:
pkg_resources.require("%(name)s") # latest installed version
pkg_resources.require("%(name)s==%(version)s") # this exact version
pkg_resources.require("%(name)s>=%(version)s") # this version or higher
"""
if not self.samefile(get_python_lib(),self.instdir):
msg += """
Note also that the installation directory must be on sys.path at runtime for
this to work. (e.g. by being the application's script directory, by being on
PYTHONPATH, or by being added to sys.path by your code.)
"""
eggloc = os.path.basename(dist.path)
instdir = os.path.realpath(self.instdir)
name = dist.name
version = dist.version
return msg % locals()
class AbstractSandbox: class AbstractSandbox:
"""Wrap 'os' module and 'open()' builtin for virtualizing setup scripts""" """Wrap 'os' module and 'open()' builtin for virtualizing setup scripts"""
...@@ -670,7 +711,7 @@ class DirectorySandbox(AbstractSandbox): ...@@ -670,7 +711,7 @@ class DirectorySandbox(AbstractSandbox):
def _remap_input(self,operation,path,*args,**kw): def _remap_input(self,operation,path,*args,**kw):
"""Called for path inputs""" """Called for path inputs"""
if operation in self.write_ops and not self._ok(path): if operation in self.write_ops and not self._ok(path):
self._violation(operation, path, *args, **kw) self._violation(operation, os.path.realpath(path), *args, **kw)
return path return path
def _remap_pair(self,operation,src,dst,*args,**kw): def _remap_pair(self,operation,src,dst,*args,**kw):
...@@ -740,7 +781,6 @@ URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):',re.I).match ...@@ -740,7 +781,6 @@ URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):',re.I).match
def main(argv, factory=Installer): def main(argv, factory=Installer):
from optparse import OptionParser from optparse import OptionParser
parser = OptionParser(usage = "usage: %prog [options] url [url...]") parser = OptionParser(usage = "usage: %prog [options] url [url...]")
parser.add_option("-d", "--install-dir", dest="instdir", default=None, parser.add_option("-d", "--install-dir", dest="instdir", default=None,
help="install package to DIR", metavar="DIR") help="install package to DIR", metavar="DIR")
...@@ -753,27 +793,69 @@ def main(argv, factory=Installer): ...@@ -753,27 +793,69 @@ def main(argv, factory=Installer):
action="store_true", dest="multi", default=None, action="store_true", dest="multi", default=None,
help="make apps have to require() a version") help="make apps have to require() a version")
parser.add_option("-b", "--build-directory", dest="tmpdir", metavar="DIR",
default=None,
help="download/extract/build in DIR; keep the results")
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
try: try:
if not args: if not args:
parser.error("No urls, filenames, or requirements specified") parser.error("No urls, filenames, or requirements specified")
for spec in args: for spec in args:
inst = factory(options.instdir, options.zip_ok, options.multi) inst = factory(
options.instdir, options.zip_ok, options.multi, options.tmpdir
)
try: try:
print "Downloading", spec print "Downloading", spec
downloaded = inst.download(spec) downloaded = inst.download(spec)
print "Installing", os.path.basename(downloaded) print "Installing", os.path.basename(downloaded)
inst.install_eggs(downloaded) for dist in inst.install_eggs(downloaded):
print inst.installation_report(dist)
finally: finally:
inst.close() inst.close()
except RuntimeError, v: except RuntimeError, v:
print >>sys.stderr,"error:",v print >>sys.stderr,"error:",v
sys.exit(1) sys.exit(1)
if __name__ == '__main__': if __name__ == '__main__':
main(sys.argv[1:]) main(sys.argv[1:])
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment