1
1
Fork 0
mirror of https://github.com/pypa/pip synced 2023-12-13 21:30:23 +01:00

merge with develop

This commit is contained in:
Marcus Smith 2013-02-16 14:17:22 -08:00
commit 22bf924b52
27 changed files with 137 additions and 126 deletions

View file

@ -63,23 +63,34 @@ This process includes virtualenv, since pip releases necessitate a virtualenv re
:<oldv>/<newv>: refers to the old and new versions of virtualenv.
1. Upgrade distribute, if needed:
#. Upgrade distribute in ``virtualenv:develop`` using the :ref:`Refresh virtualenv` process.
#. Create a pull request against ``pip:develop`` with a modified ``.travis.yml`` file that installs virtualenv from ``virtualenv:develop``, to confirm the travis builds are still passing.
3. Create Release branches:
2. Create Release branches:
#. Create ``pip:<newp>`` branch.
#. In ``pip:develop``, change ``pip.version`` to '<newp>.post1'.
#. Create ``virtualenv:<newv>`` branch.
#. In ``virtualenv:develop``, change ``virtualenv.version`` to '<newv>.post1'.
4. Prepare "rcX":
3. Prepare "rcX":
#. In ``pip:<newp>``, change ``pip.version`` to '<newp>rcX', and tag with '<newp>rcX'.
#. Build a pip sdist from ``pip:<newp>``, and build it into ``virtualenv:<newv>`` using the :ref:`Refresh virtualenv` process.
#. In ``virtualenv:<newv>``, change ``virtualenv.version`` to '<newv>rcX', and tag with '<newv>rcX'.
5. Announce ``pip-<newp>rcX`` and ``virtualenv-<newv>rcX`` with the :ref:`RC Install Instructions` and elicit feedback.
6. Apply fixes to 'rcX':
4. Announce ``pip-<newp>rcX`` and ``virtualenv-<newv>rcX`` with the :ref:`RC Install Instructions` and elicit feedback.
5. Apply fixes to 'rcX':
#. Apply fixes to ``pip:<newp>`` and ``virtualenv:<newv>``
#. Periodically merge fixes to ``pip:develop`` and ``virtualenv:develop``
7. Repeat #4 thru #6 if needed.
8. Final Release:
6. Repeat #4 thru #6 if needed.
7. Final Release:
#. In ``pip:<newp>``, change ``pip.version`` to '<newp>', and tag with '<newp>'.
#. Merge ``pip:<newp>`` to ``pip:master``.
#. Build a pip sdist from ``pip:<newp>``, and load it into ``virtualenv:<newv>`` using the :ref:`Refresh virtualenv` process.

View file

@ -1,8 +1,8 @@
.. _`pip logic`:
===================
================
Internal Details
===================
================
.. _`Requirements File Format`:
@ -141,9 +141,10 @@ Tags or revisions can be installed like this::
Finding Packages
================
pip searches for packages on `PyPI <http://pypi.python.org>`_ using the `http simple interface <http://pypi.python.org/simple>`_,
pip searches for packages on `PyPI <http://pypi.python.org>`_ using the
`http simple interface <http://pypi.python.org/simple>`_,
which is documented `here <http://packages.python.org/distribute/easy_install.html#package-index-api>`_
and `here <http://www.python.org/dev/peps/pep-0301/>`_
and `there <http://www.python.org/dev/peps/pep-0301/>`_
pip offers a set of :ref:`Package Index Options <Package Index Options>` for modifying how packages are found.

View file

@ -14,6 +14,7 @@ from pip.util import get_prog
class PipCommandUsage(rst.Directive):
required_arguments = 1
def run(self):
cmd = commands[self.arguments[0]]
prog = '%s %s' % (get_prog(), cmd.name)
@ -21,18 +22,21 @@ class PipCommandUsage(rst.Directive):
node = nodes.literal_block(usage, usage)
return [node]
class PipCommandDescription(rst.Directive):
required_arguments = 1
def run(self):
node = nodes.paragraph()
node.document = self.state.document
desc = ViewList()
description = dedent(commands[self.arguments[0]].description)
description = dedent(commands[self.arguments[0]].__doc__)
for line in description.split('\n'):
desc.append(line, "")
self.state.nested_parse(desc, 0, node)
return [node]
class PipOptions(rst.Directive):
def _format_option(self, option, cmd_name=None):
@ -72,20 +76,25 @@ class PipOptions(rst.Directive):
self.state.nested_parse(self.view_list, 0, node)
return [node]
class PipGeneralOptions(PipOptions):
def process_options(self):
self._format_options(standard_options)
class PipIndexOptions(PipOptions):
def process_options(self):
self._format_options(cmdoptions.index_group['options'])
class PipCommandOptions(PipOptions):
required_arguments = 1
def process_options(self):
cmd = commands[self.arguments[0]](create_main_parser())
self._format_options(cmd.parser.option_groups[0].option_list, cmd_name=cmd.name)
def setup(app):
app.add_directive('pip-command-usage', PipCommandUsage)
app.add_directive('pip-command-description', PipCommandDescription)

View file

@ -158,6 +158,7 @@ Examples
********
1) Uninstall a package.
::
$ pip uninstall simplejson

View file

@ -4,7 +4,6 @@ import optparse
import sys
import re
import difflib
from pip.exceptions import InstallationError, CommandError, PipError
from pip.log import logger

View file

@ -7,7 +7,7 @@ import site
__all__ = ['WindowsError']
uses_pycache = hasattr(imp,'cache_from_source')
uses_pycache = hasattr(imp, 'cache_from_source')
try:
WindowsError = WindowsError
@ -91,7 +91,8 @@ else:
from distutils.sysconfig import get_python_lib, get_python_version
#site.USER_SITE was created in py2.6
user_site = getattr(site,'USER_SITE',None)
user_site = getattr(site, 'USER_SITE', None)
def product(*args, **kwds):
# product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
@ -99,10 +100,11 @@ def product(*args, **kwds):
pools = list(map(tuple, args)) * kwds.get('repeat', 1)
result = [[]]
for pool in pools:
result = [x+[y] for x in result for y in pool]
result = [x + [y] for x in result for y in pool]
for prod in result:
yield tuple(prod)
def home_lib(home):
"""Return the lib dir under the 'home' installation scheme"""
if hasattr(sys, 'pypy_version_info'):

View file

@ -1,7 +1,6 @@
"""Base Command class, and related routines"""
import os
from pkgutil import walk_packages
import socket
import sys
import tempfile
@ -33,14 +32,13 @@ class Command(object):
def __init__(self, main_parser):
parser_kw = {
'usage' : self.usage,
'prog' : '%s %s' % (get_prog(), self.name),
'formatter' : UpdatingDefaultsHelpFormatter(),
'add_help_option' : False,
'name' : self.name,
'description' : self.description
'usage': self.usage,
'prog': '%s %s' % (get_prog(), self.name),
'formatter': UpdatingDefaultsHelpFormatter(),
'add_help_option': False,
'name': self.name,
'description': self.__doc__,
}
self.main_parser = main_parser
self.parser = ConfigOptionParser(**parser_kw)
@ -91,10 +89,10 @@ class Command(object):
options, args = self.parser.parse_args(args)
self.merge_options(initial_options, options)
level = 1 # Notify
level = 1 # Notify
level += options.verbose
level -= options.quiet
level = logger.level_for_integer(4-level)
level = logger.level_for_integer(4 - level)
complete_log = []
logger.consumers.extend(
[(level, sys.stdout),
@ -172,11 +170,11 @@ class Command(object):
log_fn = options.log_file
text = '\n'.join(complete_log)
try:
log_fp = open_logfile(log_fn, 'w')
log_fp = open_logfile(log_fn, 'w')
except IOError:
temp = tempfile.NamedTemporaryFile(delete=False)
log_fn = temp.name
log_fp = open_logfile(log_fn, 'w')
temp = tempfile.NamedTemporaryFile(delete=False)
log_fn = temp.name
log_fp = open_logfile(log_fn, 'w')
logger.fatal('Storing complete log in %s' % log_fn)
log_fp.write(text)
log_fp.close()
@ -206,7 +204,6 @@ def open_logfile(filename, mode='a'):
log_fp = open(filename, mode)
if exists:
log_fp.write('%s\n' % ('-'*60))
log_fp.write('%s\n' % ('-' * 60))
log_fp.write('%s run on %s\n' % (sys.argv[0], time.strftime('%c')))
return log_fp

View file

@ -64,10 +64,16 @@ class PrettyHelpFormatter(optparse.IndentedHelpFormatter):
# leave full control over description to us
if description:
if hasattr(self.parser, 'main'):
label = 'Commands:'
label = 'Commands'
else:
label = 'Description:'
description = label + ' %s\n' % self.indent_lines(textwrap.dedent(description), " ")
label = 'Description'
#some doc strings have inital newlines, some don't
description = description.lstrip('\n')
#some doc strings have final newlines and spaces, some don't
description = description.rstrip()
#dedent, then reindent
description = self.indent_lines(textwrap.dedent(description), " ")
description = '%s:\n%s\n' % (label, description)
return description
else:
return ''
@ -222,11 +228,11 @@ except pkg_resources.DistributionNotFound:
def create_main_parser():
parser_kw = {
'usage' : '\n%prog <command> [options]',
'add_help_option' : False,
'formatter' : UpdatingDefaultsHelpFormatter(),
'name' : 'global',
'prog' : get_prog(),
'usage': '\n%prog <command> [options]',
'add_help_option': False,
'formatter': UpdatingDefaultsHelpFormatter(),
'name': 'global',
'prog': get_prog(),
}
parser = ConfigOptionParser(**parser_kw)

View file

@ -1,7 +1,7 @@
"""shared options and groups"""
from optparse import make_option, OptionGroup
def make_option_group(group, parser):
"""
Return an OptionGroup object

View file

@ -6,13 +6,11 @@ from pip.commands.install import InstallCommand
class BundleCommand(InstallCommand):
"""Create pybundles (archives containing multiple packages)."""
name = 'bundle'
usage = """
%prog [options] <bundle name>.pybundle <package>..."""
summary = 'Create pybundles.'
description = """
Create pybundles (archives containing multiple packages)."""
bundle = True
def __init__(self, *args, **kw):
@ -37,4 +35,3 @@ class BundleCommand(InstallCommand):
self.bundle_filename = args.pop(0)
requirement_set = super(BundleCommand, self).run(options, args)
return requirement_set

View file

@ -28,10 +28,9 @@ compctl -K _pip_completion pip
class CompletionCommand(Command):
"""A helper command to be used for command completion."""
name = 'completion'
summary = 'A helper command to be used for command completion'
description = """
A helper command to be used for command completion."""
hidden = True
def __init__(self, *args, **kw):
@ -52,7 +51,7 @@ class CompletionCommand(Command):
def run(self, options, args):
"""Prints the completion code of the given shell"""
shells = COMPLETION_SCRIPTS.keys()
shell_options = ['--'+shell for shell in sorted(shells)]
shell_options = ['--' + shell for shell in sorted(shells)]
if options.shell in shells:
script = COMPLETION_SCRIPTS.get(options.shell, '')
print(BASE_COMPLETION % {'script': script, 'shell': options.shell})

View file

@ -9,12 +9,11 @@ from pip.util import get_installed_distributions
class FreezeCommand(Command):
"""Output installed packages in requirements format."""
name = 'freeze'
usage = """
%prog [options]"""
summary = 'Output installed packages in requirements format.'
description = """
Output installed packages in requirements format."""
def __init__(self, *args, **kw):
super(FreezeCommand, self).__init__(*args, **kw)

View file

@ -1,14 +1,13 @@
from pip.basecommand import Command, SUCCESS, ERROR
from pip.basecommand import Command, SUCCESS
from pip.exceptions import CommandError
class HelpCommand(Command):
"""Show help for commands"""
name = 'help'
usage = """
%prog <command>"""
summary = 'Show help for commands.'
description = """
Show help for commands"""
def run(self, options, args):
from pip.commands import commands, get_similar_commands

View file

@ -2,9 +2,7 @@ import os
import sys
import tempfile
import shutil
import optparse
from pip.req import InstallRequirement, RequirementSet
from pip.req import parse_requirements
from pip.req import InstallRequirement, RequirementSet, parse_requirements
from pip.log import logger
from pip.locations import build_prefix, src_prefix, virtualenv_no_global
from pip.basecommand import Command
@ -15,6 +13,20 @@ from pip.cmdoptions import make_option_group, index_group
class InstallCommand(Command):
"""
Install packages from:
- PyPI (and other indexes) using requirement specifiers.
- VCS project urls.
- Local project directories.
- Local or remote source archives.
pip also supports installing from "requirements files", which provide
an easy way to specify a whole environment to be installed.
See http://www.pip-installer.org for details on VCS url formats and
requirements files.
"""
name = 'install'
usage = """
@ -24,20 +36,6 @@ class InstallCommand(Command):
%prog [options] [-e] <local project path> ...
%prog [options] <archive url/path> ..."""
description = """
Install packages from:
- PyPI (and other indexes) using requirement specifiers.
- VCS project urls.
- Local project directories.
- Local or remote source archives.
pip also supports installing from "requirements files", which provide
an easy way to specify a whole environment to be installed.
See http://www.pip-installer.org for details on VCS url formats and
requirements files."""
summary = 'Install packages.'
bundle = False

View file

@ -1,4 +1,3 @@
import pkg_resources
from pip.basecommand import Command
from pip.exceptions import DistributionNotFound, BestVersionAlreadyInstalled
from pip.index import PackageFinder
@ -9,17 +8,15 @@ from pip.cmdoptions import make_option_group, index_group
class ListCommand(Command):
"""List installed packages, including editables."""
name = 'list'
usage = """
%prog [options]"""
summary = 'List installed packages.'
description = """
List installed packages, including editables."""
def __init__(self, *args, **kw):
super(ListCommand, self).__init__(*args, **kw)
cmd_opts = self.cmd_opts
cmd_opts.add_option(
@ -48,7 +45,6 @@ class ListCommand(Command):
self.parser.insert_option_group(0, index_opts)
self.parser.insert_option_group(0, cmd_opts)
def _build_package_finder(self, options, index_urls):
"""
Create a package finder appropriate to this list command.
@ -134,4 +130,3 @@ class ListCommand(Command):
if dist.parsed_version == remote_version_parsed:
uptodate.append(dist)
self.output_package_listing(uptodate)

View file

@ -12,12 +12,11 @@ from distutils.version import StrictVersion, LooseVersion
class SearchCommand(Command):
"""Search for PyPI packages whose name or summary contains <query>."""
name = 'search'
usage = """
%prog [options] <query>"""
summary = 'Search PyPI for packages.'
description = """
Search for PyPI packages whose name or summary contains <query>."""
def __init__(self, *args, **kw):
super(SearchCommand, self).__init__(*args, **kw)

View file

@ -5,12 +5,11 @@ from pip.log import logger
class ShowCommand(Command):
"""Show information about one or more installed packages."""
name = 'show'
usage = """
%prog [options] <package> ..."""
summary = 'Show information about installed packages.'
description = """
Show information about one or more installed packages."""
def __init__(self, *args, **kw):
super(ShowCommand, self).__init__(*args, **kw)

View file

@ -4,19 +4,20 @@ from pip.exceptions import InstallationError
class UninstallCommand(Command):
"""
Uninstall packages.
pip is able to uninstall most installed packages. Known exceptions are:
- Pure distutils packages installed with ``python setup.py install``, which
leave behind no metadata to determine what files were installed.
- Script wrappers installed by ``python setup.py develop``.
"""
name = 'uninstall'
usage = """
%prog [options] <package> ...
%prog [options] -r <requirements file> ..."""
summary = 'Uninstall packages.'
description = """
Uninstall packages.
pip is able to uninstall most installed packages. Known exceptions are:
- Pure distutils packages installed with ``python setup.py install``, which
leave behind no metadata to determine what files were installed.
- Script wrappers installed by ``python setup.py develop``."""
def __init__(self, *args, **kw):
super(UninstallCommand, self).__init__(*args, **kw)

View file

@ -2,5 +2,6 @@ from pip.commands.zip import ZipCommand
class UnzipCommand(ZipCommand):
"""Unzip individual packages."""
name = 'unzip'
summary = 'Unzip individual packages.'

View file

@ -11,12 +11,11 @@ from pip.basecommand import Command
class ZipCommand(Command):
"""Zip individual packages."""
name = 'zip'
usage = """
%prog [options] <package> ..."""
summary = 'Zip individual packages.'
description = """
Zip individual packages."""
def __init__(self, *args, **kw):
super(ZipCommand, self).__init__(*args, **kw)
@ -71,7 +70,7 @@ class ZipCommand(Command):
for match in self.select_paths:
match = os.path.normcase(os.path.abspath(match))
if '*' in match:
if re.search(fnmatch.translate(match+'*'), path):
if re.search(fnmatch.translate(match + '*'), path):
result.append(path)
match_any.add(match)
break
@ -86,8 +85,8 @@ class ZipCommand(Command):
for match in self.select_paths:
if match not in match_any and '*' not in match:
result.append(match)
logger.debug("Adding path %s because it doesn't match anything already on sys.path"
% match)
logger.debug("Adding path %s because it doesn't match "
"anything already on sys.path" % match)
return result
def run(self, options, args):
@ -197,7 +196,7 @@ class ZipCommand(Command):
full = os.path.join(dirpath, fn)
dest = os.path.join(module_name, dirpath[len(filename):].lstrip(os.path.sep), fn)
if is_dir:
zip.writestr(dest+'/', '')
zip.writestr(dest + '/', '')
else:
zip.write(full, dest)
zip.close()
@ -246,7 +245,7 @@ class ZipCommand(Command):
f.close()
if lines and not lines[-1].endswith('\n'):
lines[-1] += '\n'
lines.append(filename+'\n')
lines.append(filename + '\n')
else:
lines = [filename + '\n']
f = open(dest, 'wb')

View file

@ -8,6 +8,7 @@ import shutil
import socket
import sys
import tempfile
from pip.backwardcompat import (xmlrpclib, urllib, urllib2, httplib,
urlparse, string_types, ssl)
if ssl:
@ -435,7 +436,7 @@ def _download_url(resp, link, temp_location):
except (ValueError, KeyError, TypeError):
total_length = 0
downloaded = 0
show_progress = total_length > 40*1000 or not total_length
show_progress = total_length > 40 * 1000 or not total_length
show_url = link.show_url
try:
if show_progress:
@ -457,7 +458,7 @@ def _download_url(resp, link, temp_location):
if not total_length:
logger.show_progress('%s' % format_size(downloaded))
else:
logger.show_progress('%3i%% %s' % (100*downloaded/total_length, format_size(downloaded)))
logger.show_progress('%3i%% %s' % (100 * downloaded / total_length, format_size(downloaded)))
if download_hash is not None:
download_hash.update(chunk)
fp.write(chunk)

View file

@ -5,27 +5,28 @@ import os
import re
import gzip
import mimetypes
try:
import threading
except ImportError:
import dummy_threading as threading
import posixpath
import pkg_resources
import random
import socket
import string
import zlib
try:
import threading
except ImportError:
import dummy_threading as threading
from pip.log import logger
from pip.util import Inf
from pip.util import normalize_name, splitext
from pip.util import Inf, normalize_name, splitext
from pip.exceptions import DistributionNotFound, BestVersionAlreadyInstalled
from pip.backwardcompat import (WindowsError, BytesIO,
Queue, urlparse,
URLError, HTTPError, u,
product, url2pathname, ssl)
product, url2pathname, ssl
Empty as QueueEmpty)
if ssl:
from pip.backwardcompat import CertificateError
from pip.backwardcompat import Empty as QueueEmpty
from pip.download import urlopen, path_to_url2, url_to_path, geturl, Urllib2HeadRequest
__all__ = ['PackageFinder']

View file

@ -8,16 +8,14 @@ from pip import backwardcompat
class Logger(object):
"""
Logging object for use in command-line script. Allows ranges of
levels, to avoid some redundancy of displayed information.
"""
VERBOSE_DEBUG = logging.DEBUG-1
VERBOSE_DEBUG = logging.DEBUG - 1
DEBUG = logging.DEBUG
INFO = logging.INFO
NOTIFY = (logging.INFO+logging.WARN)/2
NOTIFY = (logging.INFO + logging.WARN) / 2
WARN = WARNING = logging.WARN
ERROR = logging.ERROR
FATAL = logging.FATAL
@ -68,7 +66,7 @@ class Logger(object):
rendered = msg % args
else:
rendered = msg
rendered = ' '*self.indent + rendered
rendered = ' ' * self.indent + rendered
if self.explicit_levels:
## FIXME: should this be a name, not a level number?
rendered = '%02i %s' % (level, rendered)
@ -87,7 +85,7 @@ class Logger(object):
"Tried to start_progress(%r) while in_progress %r"
% (msg, self.in_progress))
if self._show_progress():
sys.stdout.write(' '*self.indent + msg)
sys.stdout.write(' ' * self.indent + msg)
sys.stdout.flush()
self.in_progress_hanging = True
else:
@ -121,10 +119,11 @@ class Logger(object):
sys.stdout.flush()
else:
if self.last_message:
padding = ' ' * max(0, len(self.last_message)-len(message))
padding = ' ' * max(0, len(self.last_message) - len(message))
else:
padding = ''
sys.stdout.write('\r%s%s%s%s' % (' '*self.indent, self.in_progress, message, padding))
sys.stdout.write('\r%s%s%s%s' %
(' ' * self.indent, self.in_progress, message, padding))
sys.stdout.flush()
self.last_message = message

View file

@ -15,12 +15,11 @@ from pip.exceptions import (InstallationError, UninstallationError,
DistributionNotFound)
from pip.vcs import vcs
from pip.log import logger
from pip.util import display_path, rmtree
from pip.util import ask, ask_path_exists, backup_dir
from pip.util import is_installable_dir, is_local, dist_is_local, dist_in_usersite, dist_in_site_packages
from pip.util import renames, normalize_path, egg_link_path
from pip.util import make_path_relative
from pip.util import call_subprocess
from pip.util import (display_path, rmtree, ask, ask_path_exists, backup_dir,
is_installable_dir, is_local, dist_is_local,
dist_in_usersite, dist_in_site_packages, renames,
normalize_path, egg_link_path, make_path_relative,
call_subprocess)
from pip.backwardcompat import (urlparse, urllib, uses_pycache,
ConfigParser, string_types, HTTPError,
get_python_version, b)

View file

@ -6,7 +6,7 @@ import shutil
from pip.backwardcompat import urlparse, urllib
from pip.log import logger
from pip.util import (display_path, backup_dir, find_command,
ask, rmtree, ask_path_exists)
rmtree, ask_path_exists)
__all__ = ['vcs', 'get_src_requirement']
@ -117,7 +117,7 @@ class VersionControl(object):
Returns the correct repository URL and revision by parsing the given
repository URL
"""
error_message= (
error_message = (
"Sorry, '%s' is a malformed VCS url. "
"The format is <vcs>+<protocol>://<url>, "
"e.g. svn+http://myrepo/svn/MyApp#egg=MyApp")

View file

@ -126,7 +126,7 @@ class Subversion(VersionControl):
dirurl, localrev = self._get_svn_url_rev(base)
if base == location:
base_url = dirurl+'/' # save the root url
base_url = dirurl + '/' # save the root url
elif not dirurl or not dirurl.startswith(base_url):
dirs[:] = []
continue # not part of the same svn tree, skip it
@ -163,13 +163,13 @@ class Subversion(VersionControl):
data = list(map(str.splitlines, data.split('\n\x0c\n')))
del data[0][0] # get rid of the '8'
url = data[0][3]
revs = [int(d[9]) for d in data if len(d)>9 and d[9]]+[0]
revs = [int(d[9]) for d in data if len(d) > 9 and d[9]] + [0]
elif data.startswith('<?xml'):
match = _svn_xml_url_re.search(data)
if not match:
raise ValueError('Badly formatted data: %r' % data)
url = match.group(1) # get repository URL
revs = [int(m.group(1)) for m in _svn_rev_re.finditer(data)]+[0]
revs = [int(m.group(1)) for m in _svn_rev_re.finditer(data)] + [0]
else:
try:
# subversion >= 1.7

View file

@ -1,8 +1,7 @@
from pip.exceptions import CommandError
from pip.baseparser import create_main_parser
from pip.commands.help import (HelpCommand,
SUCCESS,
ERROR,)
from pip.basecommand import ERROR, SUCCESS
from pip.commands.help import HelpCommand
from pip.commands import commands
from mock import Mock
from nose.tools import assert_raises