Browse files

Merge branch 'release/1.0.1'

  • Loading branch information...
jezdez committed Apr 30, 2011
2 parents 6b6f494 + 7431240 commit b1f2a9fddaa407cfbee6f249a52129e370800bd1
@@ -47,7 +47,7 @@
# built documents.
# The short X.Y version.
release = "1.0"
release = "1.0.1"
version = '.'.join(release.split('.')[:2])
# The language for content autogenerated by Sphinx. Refer to documentation
@@ -7,6 +7,14 @@ All kinds of contributions are welcome - code, tests, documentation,
bug reports, ideas, etc.
Release Schedule
Minor releases of pip (e.g. 1.1, 1.2, 1.3...) occur every four months
(beginning with the release of pip 1.0 on April 4, 2011). Two weeks before a
scheduled release, a new branch ``release/X.Y`` is created for release testing
and preparation. This branch is only open to bugfixes.
.. _contributing-with-code:
Contributing with Code
@@ -23,27 +31,44 @@ Log in to Github, go to the `pip repository page
to copy the repository and then clone your fork, like::
$ git clone
Now you can change whatever you want, commit, push to your fork and when your
contribution is done, follow the **pull request** link and send us a request
explaining what you did and why.
Pip uses the `git-flow`_ branching model. The default branch on GitHub is
``develop``, and all development work (new features and bugfixes) should happen
in that branch. The ``master`` branch is stable, and reflects the last released
.. _git-flow:
All tests should pass
Almost all changes to pip should be accompanied by automated tests -
especially ones adding new behavior.
`Nose`_ is used to find and run all tests. Take a look at :doc:`running-tests`
to see what you need and how you should run the tests.
Before sending us a pull request, please, be sure all tests pass.
Supported Python versions
Pip supports Python versions 2.4, 2.5, 2.6, 2.7, 3.1, and 3.2, from a single
codebase (without use of 2to3 translation). Untested contributions frequently
break Python 2.4 or 3.x compatibility. Please run the tests on at least 2.4 and
3.2 and report your results when sending a pull request.
Using a Continuous Integration server
Continuous Integration server
We have a continuous integration server running all pip related tests at
We have a continuous integration server running all pip related tests at But if you want to have your own,
you can learn how to set up a Hudson CI server like that in the
:doc:`ci-server-step-by-step` page.
@@ -1,8 +1,24 @@
News / Changelog
Next release (1.1) schedule
Beta release mid-July 2011, final release early August.
1.0.1 (2011-04-30)
* Start to use git-flow.
* Fixed issue #274 - `find_command` should not raise AttributeError
* Fixed issue #273 - respect Content-Disposition header. Thanks Bradley Ayers.
* Fixed issue #233 - pathext handling on Windows.
* Fixed issue #252 - svn+svn protocol.
* Fixed issue #44 - multiple CLI searches.
* Fixed issue #266 - current working directory when running clean.
1.0 (2011-04-04)
* Added Python 3 support! Huge thanks to Vinay Sajip, Vitaly Babiy, Kelsey
Hightower, and Alex Gronholm, among others.
@@ -16,7 +32,7 @@ News / Changelog
python-setuptools package (workaround until fixed in Debian and Ubuntu).
* Added ` <>`_
installer. Simple download and execute it, using the Python interpreter of
installer. Simply download and execute it, using the Python interpreter of
your choice::
$ curl -O
@@ -24,6 +24,15 @@ The ``#egg=MyProject`` part is important, because while you can
install simply given the svn location, the project name is useful in
other places.
You can also specify the egg name for a non-editable url. This is useful to
point to HEAD locations on the local filesystem:
or relative paths:
If you need to give pip (and by association easy_install) hints
about where to find a package, you can use the ``-f``
(``--find-links``) option, like::
@@ -48,9 +57,10 @@ Right now pip knows of the following major version control systems:
Pip supports the URL schemes ``svn``, ``svn+http``, ``svn+https``, ``svn+ssh``.
Pip supports the URL schemes ``svn``, ``svn+svn``, ``svn+http``, ``svn+https``, ``svn+ssh``.
You can also give specific revisions to an SVN URL, like::
-e svn+svn://
-e svn+
which will check out revision 2019. ``@{20080101}`` would also check
@@ -134,6 +134,10 @@ def main(self, complete_args, args, initial_options):
logger.fatal(str(e))'Exception information:\n%s' % format_exc())
exit = 1
except KeyboardInterrupt:
logger.fatal('Operation cancelled by user')'Exception information:\n%s' % format_exc())
exit = 1
logger.fatal('Exception:\n%s' % format_exc())
exit = 2
@@ -27,7 +27,7 @@ def run(self, options, args):
if not args:
logger.warn('ERROR: Missing required argument (search query).')
query = ' '.join(args)
query = args
index_url = options.index
pypi_hits =, index_url)
@@ -1,9 +1,10 @@
import re
import cgi
import getpass
import sys
import os
import mimetypes
import os
import re
import shutil
import sys
import tempfile
from pip.backwardcompat import (md5, copytree, xmlrpclib, urllib, urllib2,
urlparse, string_types, HTTPError)
@@ -330,7 +331,7 @@ def _check_md5(download_hash, link):
def _get_md5_from_file(target_file, link):
download_hash = md5()
fp = open(target_file, 'rb')
while 1:
while True:
chunk =
if not chunk:
@@ -362,7 +363,7 @@ def _download_url(resp, link, temp_location):
logger.notify('Downloading %s' % show_url)
logger.debug('Downloading from URL %s' % link)
while 1:
while True:
chunk =
if not chunk:
@@ -416,7 +417,7 @@ def unpack_http_url(link, location, download_cache, only_download):
if (target_file
and os.path.exists(target_file)
and os.path.exists(target_file+'.content-type')):
and os.path.exists(target_file + '.content-type')):
fp = open(target_file+'.content-type')
content_type =
@@ -427,7 +428,14 @@ def unpack_http_url(link, location, download_cache, only_download):
resp = _get_response_from_url(target_url, link)
content_type =['content-type']
filename = link.filename
filename = link.filename # fallback
# Have a look at the Content-Disposition header for a better guess
content_disposition ='content-disposition')
if content_disposition:
type, params = cgi.parse_header(content_disposition)
# We use ``or`` here because we don't want to use an "empty" value
# from the filename param.
filename = params.get('filename') or filename
ext = splitext(filename)[1]
if not ext:
ext = mimetypes.guess_extension(content_type)
@@ -466,6 +474,7 @@ def _get_response_from_url(target_url, link):
return resp
class Urllib2HeadRequest(urllib2.Request):
def get_method(self):
return "HEAD"
@@ -578,13 +578,9 @@ def __hash__(self):
def filename(self):
url = self.url
url = url.split('#', 1)[0]
url = url.split('?', 1)[0]
url = url.rstrip('/')
url = self.url_fragment
name = posixpath.basename(url)
assert name, (
'URL %r produced no filename' % url)
assert name, ('URL %r produced no filename' % url)
return name
@@ -598,6 +594,14 @@ def path(self):
def splitext(self):
return splitext(posixpath.basename(self.path.rstrip('/')))
def url_fragment(self):
url = self.url
url = url.split('#', 1)[0]
url = url.split('?', 1)[0]
url = url.rstrip('/')
return url
_egg_fragment_re = re.compile(r'#egg=([^&]*)')
@@ -73,28 +73,34 @@ def from_line(cls, name, comes_from=None):
url = None
name = name.strip()
req = name
req = None
path = os.path.normpath(os.path.abspath(name))
link = None
if is_url(name):
url = name
## FIXME: I think getting the requirement here is a bad idea:
#req = get_requirement_from_url(url)
req = None
link = Link(name)
elif os.path.isdir(path) and (os.path.sep in name or name.startswith('.')):
if not is_installable_dir(path):
raise InstallationError("Directory %r is not installable. File '' not found."
% name)
url = path_to_url(name)
#req = get_requirement_from_url(url)
req = None
raise InstallationError("Directory %r is not installable. File '' not found.", name)
link = Link(path_to_url(name))
elif is_archive_file(path):
if not os.path.isfile(path):
logger.warn('Requirement %r looks like a filename, but the file does not exist'
% name)
url = path_to_url(name)
#req = get_requirement_from_url(url)
req = None
logger.warn('Requirement %r looks like a filename, but the file does not exist', name)
link = Link(path_to_url(name))
# If the line has an egg= definition, but isn't editable, pull the requirement out.
# Otherwise, assume the name is the req for the non URL/path/archive case.
if link and req is None:
url = link.url_fragment
req = link.egg_fragment
# Handle relative file URLs
if link.scheme == 'file' and'\.\./', url):
url = path_to_url(os.path.normpath(os.path.abspath(link.path)))
req = name
return cls(req, comes_from, url=url)
def __str__(self):
@@ -752,12 +758,12 @@ def values(self):
def __contains__(self, item):
return item in self._keys
def __setitem__(self, key, value):
if key not in self._keys:
self._dict[key] = value
def __getitem__(self, key):
return self._dict[key]
@@ -1054,10 +1060,11 @@ def _pip_has_created_build_dir(self):
def copy_to_build_dir(self, req_to_install):
target_dir = req_to_install.editable and self.src_dir or self.build_dir"Copying %s to %s" %(, target_dir))"Copying %s to %s" % (, target_dir))
dest = os.path.join(target_dir,
copytree(req_to_install.source_dir, dest)
call_subprocess(["python", "%s/"%dest, "clean"])
call_subprocess(["python", "%s/" % dest, "clean"], cwd=dest,
command_desc='python clean')
def unpack_url(self, link, location, only_download=False):
if only_download:
@@ -1077,7 +1084,7 @@ def install(self, install_options, global_options=()):
if self.upgrade or not r.satisfied_by]
if to_install:
logger.notify('Installing collected packages: %s' % (', '.join([ for req in to_install])))
logger.notify('Installing collected packages: %s' % ', '.join([ for req in to_install]))
logger.indent += 2
for requirement in to_install:
@@ -7,7 +7,7 @@
import pkg_resources
import zipfile
import tarfile
from pip.exceptions import InstallationError
from pip.exceptions import InstallationError, BadCommand
from pip.backwardcompat import WindowsError, string_types, raw_input
from pip.locations import site_packages, running_under_virtualenv
from pip.log import logger
@@ -71,12 +71,12 @@ def backup_dir(dir, ext='.bak'):
def find_command(cmd, paths=None, pathext=None):
"""Searches the PATH for the given command and returns its path"""
if paths is None:
paths = os.environ.get('PATH', []).split(os.pathsep)
paths = os.environ.get('PATH', '').split(os.pathsep)
if isinstance(paths, string_types):
paths = [paths]
# check if there are funny path extensions for executables, e.g. Windows
if pathext is None:
pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
pathext = get_pathext()
pathext = [ext for ext in pathext.lower().split(os.pathsep)]
# don't use extensions if the command ends with one of them
if os.path.splitext(cmd)[1].lower() in pathext:
@@ -92,9 +92,16 @@ def find_command(cmd, paths=None, pathext=None):
return cmd_path_ext
if os.path.isfile(cmd_path):
return cmd_path
return None
raise BadCommand('Cannot find command %r' % cmd)
def get_pathext(default_pathext=None):
"""Returns the path extensions from environment or a default"""
if default_pathext is None:
default_pathext = os.pathsep.join([ '.COM', '.EXE', '.BAT', '.CMD' ])
pathext = os.environ.get('PATHEXT', default_pathext)
return pathext
def ask(message, options):
"""Ask the message interactively, with the given possible responses"""
while 1:
Oops, something went wrong.

0 comments on commit b1f2a9f

Please sign in to comment.