Commit 5bf51fa2 authored by PJ Eby's avatar PJ Eby

Add script installation support. Use distutils' exceptions for option

errors.  Include Python version in setuptools' egg name for compatibility
w/installs via easy_install.  Add isdir/listdir facilities for metadata,
along with support for running scripts from eggs.

--HG--
branch : setuptools
extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041053
parent 18b9ae1e
...@@ -65,10 +65,10 @@ version, and automatically downloading, building, and installing it:: ...@@ -65,10 +65,10 @@ version, and automatically downloading, building, and installing it::
easy_install SQLObject easy_install SQLObject
**Example 2**. Install a package by name and version from a given **Example 2**. Install or upgrade a package by name and version by finding
"download page":: links on a given "download page"::
easy_install -s http://peak.telecommunity.com/dist "setuptools>=0.4a1" easy_install -f http://peak.telecommunity.com/dist "setuptools>=0.4a1"
**Example 3**. Download a source distribution from a specified URL, **Example 3**. Download a source distribution from a specified URL,
automatically building and installing it:: automatically building and installing it::
...@@ -90,17 +90,26 @@ distributions as well. ...@@ -90,17 +90,26 @@ distributions as well.
By default, packages are installed to the running Python installation's By default, packages are installed to the running Python installation's
``site-packages`` directory, unless you provide the ``-d`` or ``--install-dir`` ``site-packages`` directory, unless you provide the ``-d`` or ``--install-dir``
option to specify an alternative directory. option to specify an alternative directory, or specify an alternate location
using distutils configuration files. (See `Configuration Files`_, below.)
By default, any scripts included with the package are installed to the running
Python installation's standard script installation location. However, if you
specify an installation directory via the command line or a config file, then
the default directory for installing scripts will be the same as the package
installation directory, to ensure that the script will have access to the
installed package. You can override this using the ``-s`` or ``--script-dir``
option.
Packages installed to ``site-packages`` are added to an ``easy-install.pth`` Packages installed to ``site-packages`` are added to an ``easy-install.pth``
file, so that Python will be able to import the package by default. If you do file, so that Python will always use the most-recently-installed version of
not want this to happen, you should use the ``-m`` or ``--multi`` option, which the package. If you would like to be able to select which version to use at
allows multiple versions of the same package to be selected at runtime. runtime, you should use the ``-m`` or ``--multi-version`` option.
Note that installing to a directory other than ``site-packages`` already Note, however, that installing to a directory other than ``site-packages``
implies the ``-m`` option, so if you cannot install to ``site-packages``, already implies the ``-m`` option, so if you cannot install to
please see the `Command-Line Options`_ section below (under ``--multi``) to ``site-packages``, please see the `Command-Line Options`_ section below (under
find out how to select packages at runtime. ``--multi-version``) to find out how to select packages at runtime.
Upgrading a Package Upgrading a Package
...@@ -133,6 +142,11 @@ package automatically replaces any previous version in the ``easy-install.pth`` ...@@ -133,6 +142,11 @@ package automatically replaces any previous version in the ``easy-install.pth``
file, so that Python will import the most-recently installed version by file, so that Python will import the most-recently installed version by
default. default.
If you haven't suppressed script installation (using ``--exclude-scripts`` or
``-x``), then the upgraded version's scripts will be installed, and they will
be automatically patched to ``require()`` the corresponding version of the
package, so that you can use them even if not installing to ``site-packages``.
``easy_install`` never actually deletes packages (unless you're installing a ``easy_install`` never actually deletes packages (unless you're installing a
package with the same name and version number as an existing package), so if package with the same name and version number as an existing package), so if
you want to get rid of older versions of a package, please see `Uninstalling you want to get rid of older versions of a package, please see `Uninstalling
...@@ -148,15 +162,22 @@ version, you can do so like this:: ...@@ -148,15 +162,22 @@ version, you can do so like this::
easy_install PackageName==1.2.3 easy_install PackageName==1.2.3
Where ``1.2.3`` is replaced by the exact version number you wish to switch to. Where ``1.2.3`` is replaced by the exact version number you wish to switch to.
Note that the named package and version must already have been installed to If a package matching the requested name and version is not already installed
``site-packages``. in a directory on ``sys.path``, it will be located via PyPI and installed.
If you'd like to switch to the latest version of ``PackageName``, you can do so If you'd like to switch to the latest installed version of ``PackageName``, you
like this:: can do so like this::
easy_install PackageName easy_install PackageName
This will activate the latest installed version. This will activate the latest installed version. (Note: if you have set any
``find_links`` via distutils configuration files, those download pages will be
checked for the latest available version of the package, and it will be
downloaded and installed if it is newer than your current version.)
Note that changing the active version of a package will install the newly
active version's scripts, unless the ``--exclude-scripts`` or ``-x`` option is
specified.
Uninstalling Packages Uninstalling Packages
...@@ -173,7 +194,45 @@ versions of a package), you should first run:: ...@@ -173,7 +194,45 @@ versions of a package), you should first run::
This will ensure that Python doesn't continue to search for a package you're This will ensure that Python doesn't continue to search for a package you're
planning to remove. After you've done this, you can safely delete the .egg planning to remove. After you've done this, you can safely delete the .egg
files or directories. files or directories, along with any scripts you wish to remove.
Managing Scripts
----------------
Whenever you install, upgrade, or change versions of a package, EasyInstall
automatically installs the scripts for the selected package version, unless
you tell it not to with ``-x`` or ``--exclude-scripts``. If any scripts in
the script directory have the same name, they are overwritten.
Thus, you do not normally need to manually delete scripts for older versions of
a package, unless the newer version of the package does not include a script
of the same name. However, if you are completely uninstalling a package, you
may wish to manually delete its scripts.
EasyInstall's default behavior means that you can normally only run scripts
from one version of a package at a time. If you want to keep multiple versions
of a script available, however, you can simply use the ``--multi-version`` or
``-m`` option, and rename the scripts that EasyInstall creates. This works
because EasyInstall installs scripts as short code stubs that ``require()`` the
matching version of the package the script came from, so renaming the script
has no effect on what it executes.
For example, suppose you want to use two versions of the ``rst2html`` tool
provided by the `docutils <http://docutils.sf.net/>`_ package. You might
first install one version::
easy_install -m docutils==0.3.9
then rename the ``rst2html.py`` to ``r2h_039``, and install another version::
easy_install -m docutils==0.3.10
This will create another ``rst2html.py`` script, this one using docutils
version 0.3.10 instead of 0.3.9. You now have two scripts, each using a
different version of the package. (Notice that we used ``-m`` for both
installations, so that Python won't lock us out of using anything but the most
recently-installed version of the package.)
Reference Manual Reference Manual
...@@ -267,8 +326,22 @@ Command-Line Options ...@@ -267,8 +326,22 @@ Command-Line Options
or in a distutils configuration file, the distutils default installation or in a distutils configuration file, the distutils default installation
location is used. Normally, this would be the ``site-packages`` directory, location is used. Normally, this would be the ``site-packages`` directory,
but if you are using distutils configuration files, setting things like but if you are using distutils configuration files, setting things like
``--prefix`` or ``--install-lib``, then those settings are taken into ``prefix`` or ``install_lib``, then those settings are taken into
account when computing the default directory. account when computing the default installation directory.
``--script-dir=DIR, -s DIR``
Set the script installation directory. If you don't supply this option
(via the command line or a configuration file), but you *have* supplied
an ``--install-dir`` (via command line or config file), then this option
defaults to the same directory, so that the scripts will be able to find
their associated package installation. Otherwise, this setting defaults
to the location where the distutils would normally install scripts, taking
any distutils configuration file settings into account.
``--exclude-scripts, -x``
Don't install scripts. This is useful if you need to install multiple
versions of a package, but do not want to reset the version that will be
run by scripts that are already installed.
``--find-links=URL, -f URL`` (Option renamed in 0.4a2) ``--find-links=URL, -f URL`` (Option renamed in 0.4a2)
Scan the specified "download pages" for direct links to downloadable eggs Scan the specified "download pages" for direct links to downloadable eggs
...@@ -319,6 +392,8 @@ Known Issues ...@@ -319,6 +392,8 @@ Known Issues
time out or be missing a file. time out or be missing a file.
0.4a2 0.4a2
* Added support for installing scripts
* Added support for setting options via distutils configuration files * Added support for setting options via distutils configuration files
* Renamed ``--scan-url/-s`` to ``--find-links/-f`` to free up ``-s`` for the * Renamed ``--scan-url/-s`` to ``--find-links/-f`` to free up ``-s`` for the
...@@ -405,9 +480,9 @@ Known Issues ...@@ -405,9 +480,9 @@ Known Issues
Future Plans Future Plans
============ ============
* Support packages that include scripts
* Log progress to a logger, with -v and -q options to control verbosity * Log progress to a logger, with -v and -q options to control verbosity
* Process the installed package's dependencies as well as the base package * Process the installed package's dependencies as well as the base package
* Additional utilities to list/remove/verify packages * Additional utilities to list/remove/verify packages
* Signature checking? SSL? Ability to suppress PyPI search? * Signature checking? SSL? Ability to suppress PyPI search?
* Support installation from bdist_wininst packages?
...@@ -17,7 +17,7 @@ import sys, os.path, zipimport, shutil, tempfile ...@@ -17,7 +17,7 @@ import sys, os.path, zipimport, shutil, tempfile
from setuptools import Command from setuptools import Command
from setuptools.sandbox import run_setup from setuptools.sandbox import run_setup
from distutils.sysconfig import get_python_lib from distutils.sysconfig import get_python_lib
from distutils.errors import DistutilsArgError
from setuptools.archive_util import unpack_archive from setuptools.archive_util import unpack_archive
from setuptools.package_index import PackageIndex from setuptools.package_index import PackageIndex
from pkg_resources import * from pkg_resources import *
...@@ -43,31 +43,31 @@ class easy_install(Command): ...@@ -43,31 +43,31 @@ class easy_install(Command):
"""Manage a download/build/install process""" """Manage a download/build/install process"""
description = "Find/get/install Python packages" description = "Find/get/install Python packages"
command_consumes_arguments = True command_consumes_arguments = True
user_options = [ user_options = [
("zip-ok", "z", "install package as a zipfile"), ("zip-ok", "z", "install package as a zipfile"),
("multi-version", "m", "make apps have to require() a version"), ("multi-version", "m", "make apps have to require() a version"),
("install-dir=", "d", "install package to DIR"), ("install-dir=", "d", "install package to DIR"),
("script-dir=", "s", "install scripts to DIR"),
("exclude-scripts", "x", "Don't install scripts"),
("index-url=", "i", "base URL of Python Package Index"), ("index-url=", "i", "base URL of Python Package Index"),
("find-links=", "f", "additional URL(s) to search for packages"), ("find-links=", "f", "additional URL(s) to search for packages"),
("build-directory=", "b", ("build-directory=", "b",
"download/extract/build in DIR; keep the results"), "download/extract/build in DIR; keep the results"),
] ]
boolean_options = [ 'zip-ok', 'multi-version' ] boolean_options = [ 'zip-ok', 'multi-version', 'exclude-scripts' ]
create_index = PackageIndex create_index = PackageIndex
def initialize_options(self): def initialize_options(self):
self.zip_ok = None self.zip_ok = None
self.multi_version = None self.multi_version = None
self.install_dir = None self.install_dir = self.script_dir = self.exclude_scripts = None
self.index_url = None self.index_url = None
self.find_links = None self.find_links = None
self.build_directory = None self.build_directory = None
self.args = None self.args = None
# Options not specifiable via command line # Options not specifiable via command line
self.package_index = None self.package_index = None
self.pth_file = None self.pth_file = None
...@@ -81,11 +81,22 @@ class easy_install(Command): ...@@ -81,11 +81,22 @@ class easy_install(Command):
return tmpdir return tmpdir
def finalize_options(self): def finalize_options(self):
# Let install_lib get set by install_lib command, which in turn # If a non-default installation directory was specified, default the
# script directory to match it.
if self.script_dir is None:
self.script_dir = self.install_dir
# Let install_dir get set by install_lib command, which in turn
# gets its info from the install command, and takes into account # gets its info from the install command, and takes into account
# --prefix and --home and all that other crud. # --prefix and --home and all that other crud.
# self.set_undefined_options('install_lib',
self.set_undefined_options('install_lib',('install_dir','install_dir')) ('install_dir','install_dir')
)
# Likewise, set default script_dir from 'install_scripts.install_dir'
self.set_undefined_options('install_scripts',
('install_dir', 'script_dir')
)
site_packages = get_python_lib() site_packages = get_python_lib()
instdir = self.install_dir instdir = self.install_dir
...@@ -102,10 +113,10 @@ class easy_install(Command): ...@@ -102,10 +113,10 @@ class easy_install(Command):
elif not self.multi_version: elif not self.multi_version:
# explicit false set from Python code; raise an error # explicit false set from Python code; raise an error
raise RuntimeError( raise DistutilsArgError(
"Can't do single-version installs outside site-packages" "Can't do single-version installs outside site-packages"
) )
self.index_url = self.index_url or "http://www.python.org/pypi" self.index_url = self.index_url or "http://www.python.org/pypi"
if self.package_index is None: if self.package_index is None:
self.package_index = self.create_index(self.index_url) self.package_index = self.create_index(self.index_url)
...@@ -117,10 +128,13 @@ class easy_install(Command): ...@@ -117,10 +128,13 @@ class easy_install(Command):
self.package_index.scan_url(link) self.package_index.scan_url(link)
if not self.args: if not self.args:
parser.error("No urls, filenames, or requirements specified") raise DistutilsArgError(
"No urls, filenames, or requirements specified (see --help)")
elif len(self.args)>1 and self.build_directory is not None: elif len(self.args)>1 and self.build_directory is not None:
parser.error("Build directory can only be set when using one URL") raise DistutilsArgError(
"Build directory can only be set when using one URL"
)
def run(self): def run(self):
for spec in self.args: for spec in self.args:
self.easy_install(spec) self.easy_install(spec)
...@@ -139,6 +153,7 @@ class easy_install(Command): ...@@ -139,6 +153,7 @@ class easy_install(Command):
print "Installing", os.path.basename(download) print "Installing", os.path.basename(download)
for dist in self.install_eggs(download, self.zip_ok, tmpdir): for dist in self.install_eggs(download, self.zip_ok, tmpdir):
self.package_index.add(dist) self.package_index.add(dist)
self.install_egg_scripts(dist)
print self.installation_report(dist) print self.installation_report(dist)
finally: finally:
...@@ -147,16 +162,42 @@ class easy_install(Command): ...@@ -147,16 +162,42 @@ class easy_install(Command):
def install_egg_scripts(self, dist):
metadata = dist.metadata
if self.exclude_scripts or not metadata.metadata_isdir('scripts'):
return
from distutils.command.build_scripts import first_line_re
for script_name in metadata.metadata_listdir('scripts'):
target = os.path.join(self.script_dir, script_name)
print "Installing", script_name, "to", target
script_text = metadata.get_metadata('scripts/'+script_name)
script_text = script_text.replace('\r','\n')
first, rest = script_text.split('\n',1)
match = first_line_re.match(first)
options = ''
if match:
options = match.group(1) or ''
if options:
options = ' '+options
spec = '%s==%s' % (dist.name,dist.version)
script_text = '\n'.join([
"#!%s%s" % (os.path.normpath(sys.executable),options),
"# EASY-INSTALL-SCRIPT: %r,%r" % (spec, script_name),
"import pkg_resources",
"pkg_resources.run_main(%r, %r)" % (spec, script_name)
])
f = open(target,"w")
f.write(script_text)
f.close()
...@@ -329,7 +370,7 @@ class PthDistributions(AvailableDistributions): ...@@ -329,7 +370,7 @@ class PthDistributions(AvailableDistributions):
def main(argv, cmds={'easy_install':easy_install}): def main(argv, cmds={'easy_install':easy_install}):
from setuptools import setup from setuptools import setup
try: try:
setup(cmdclass = cmds, script_args = ['easy_install']+argv) setup(cmdclass = cmds, script_args = ['-q','easy_install', '-v']+argv)
except RuntimeError, v: except RuntimeError, v:
print >>sys.stderr,"error:",v print >>sys.stderr,"error:",v
sys.exit(1) sys.exit(1)
......
This diff is collapsed.
...@@ -2,8 +2,9 @@ ...@@ -2,8 +2,9 @@
"""Distutils setup file, used to install or test 'setuptools'""" """Distutils setup file, used to install or test 'setuptools'"""
VERSION = "0.4a1" VERSION = "0.4a1"
import sys
from setuptools import setup, find_packages, Require from setuptools import setup, find_packages, Require
from distutils.version import LooseVersion PYVER = sys.version[:3]
setup( setup(
name="setuptools", name="setuptools",
...@@ -38,11 +39,10 @@ setup( ...@@ -38,11 +39,10 @@ setup(
Require('PyUnit', None, 'unittest', "http://pyunit.sf.net/"), Require('PyUnit', None, 'unittest', "http://pyunit.sf.net/"),
], ],
packages = find_packages(), packages = find_packages(),
py_modules = ['pkg_resources', 'easy_install'], py_modules = ['pkg_resources', 'easy_install'],
scripts = ['easy_install.py'], scripts = ['easy_install.py'],
extra_path = ('setuptools', 'setuptools-%s.egg' % VERSION), extra_path = ('setuptools', 'setuptools-%s-py%s.egg' % (VERSION,PYVER)),
classifiers = [f.strip() for f in """ classifiers = [f.strip() for f in """
Development Status :: 3 - Alpha Development Status :: 3 - Alpha
......
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