Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
dstufft committed Jan 21, 2016
2 parents ce4c356 + 9b13df0 commit 024cfe1
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 63 deletions.
4 changes: 4 additions & 0 deletions AUTHORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Craig Kerstiens <[email protected]>
Cristian Sorinel <[email protected]>
Dan Savilonis <[email protected]>
Dan Sully <[email protected]>
daniel <[email protected]>
Daniel Collins <[email protected]>
Daniel Hahler <[email protected]>
Daniel Holth <[email protected]>
Expand All @@ -67,6 +68,7 @@ Donald Stufft <[email protected]>
Dongweiming <[email protected]>
Douglas Thor <[email protected]>
Dwayne Bailey <[email protected]>
Emil Styrke <[email protected]>
Endoh Takanao <[email protected]>
enoch <[email protected]>
Eric Gillingham <[email protected]>
Expand Down Expand Up @@ -155,6 +157,7 @@ Matthew Iversen <[email protected]>
Matthew Trumbell <[email protected]>
Matthias Bussonnier <[email protected]>
Maxime Rouyrre <[email protected]>
Michael <[email protected]>
Michael E. Karpeles <[email protected]>
Michael Klich <[email protected]>
Michael Williamson <[email protected]>
Expand Down Expand Up @@ -240,6 +243,7 @@ Victor Stinner <[email protected]>
Ville Skyttä <[email protected]>
Vinay Sajip <[email protected]>
Vitaly Babiy <[email protected]>
Vladimir Rutsky <[email protected]>
W. Trevor King <[email protected]>
Wil Tan <[email protected]>
William ML Leslie <[email protected]>
Expand Down
21 changes: 21 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
**8.0.1 (2016-01-21)**

* Detect CAPaths in addition to CAFiles on platforms that provide them.

* Installing argparse or wsgiref will no longer warn or error - pip will allow
the installation even though it may be useless (since the installed thing
will be shadowed by the standard library).

* Upgrading a distutils installed item that is installed outside of a virtual
environment, while inside of a virtual environment will no longer warn or
error.

* Fix a bug where pre-releases were showing up in ``pip list --outdated``
without the ``--pre`` flag.

* Switch the SOABI emulation from using RuntimeWarnings to debug logging.

* Rollback the removal of the ability to uninstall distutils installed items
until a future date.


**8.0.0 (2016-01-19)**

* **BACKWARD INCOMPATIBLE** Drop support for Python 3.2.
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/pip_install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ Since version 6.0, pip also supports specifers containing `environment markers
::

SomeProject ==5.4 ; python_version < '2.7'
SomeProject; sys.platform == 'win32'
SomeProject; sys_platform == 'win32'

Environment markers are supported in the command line and in requirements files.

Expand Down
2 changes: 1 addition & 1 deletion pip/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
cmdoptions = pip.cmdoptions

# The version as used in the setup.py and the docs conf.py
__version__ = "8.0.0"
__version__ = "8.0.1"


logger = logging.getLogger(__name__)
Expand Down
5 changes: 5 additions & 0 deletions pip/commands/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ def find_packages_latest_versions(self, options):
for dist in installed_packages:
typ = 'unknown'
all_candidates = finder.find_all_candidates(dist.key)
if not options.pre:
# Remove prereleases
all_candidates = [candidate for candidate in all_candidates
if not candidate.version.is_prerelease]

if not all_candidates:
continue
best_candidate = max(all_candidates,
Expand Down
20 changes: 20 additions & 0 deletions pip/compat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,26 @@
ipaddress.ip_network = ipaddress.IPNetwork


try:
import sysconfig

def get_stdlib():
paths = [
sysconfig.get_path("stdlib"),
sysconfig.get_path("platstdlib"),
]
return set(filter(bool, paths))
except ImportError:
from distutils import sysconfig

def get_stdlib():
paths = [
sysconfig.get_python_lib(standard_lib=True),
sysconfig.get_python_lib(standard_lib=True, plat_specific=True),
]
return set(filter(bool, paths))


__all__ = [
"logging_dictConfig", "ipaddress", "uses_pycache", "console_to_str",
"native_str", "get_path_uid", "stdlib_pkgs", "WINDOWS", "samefile"
Expand Down
37 changes: 36 additions & 1 deletion pip/locations.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,44 @@
# doesn't exist or we cannot resolve the path to an existing file, then we will
# simply set this to None. Setting this to None will have requests fall back
# and use it's default CA Bundle logic.
# Ever since requests 2.9.0, requests has supported a CAPath in addition to a
# CAFile, because some systems (such as Debian) have a broken CAFile currently
# we'll go ahead and support both, prefering CAPath over CAfile.
if getattr(ssl, "get_default_verify_paths", None):
CA_BUNDLE_PATH = ssl.get_default_verify_paths().cafile
_ssl_paths = ssl.get_default_verify_paths()

# Ok, this is a little hairy because system trust stores are randomly
# broken in different and exciting ways and this should help fix that.
# Ideally we'd just not trust the system store, however that leads end
# users to be confused on systems like Debian that patch python-pip, even
# inside of a virtual environment, to support the system store. This would
# lead to pip trusting the system store when creating the virtual
# environment, but then switching to not doing that when upgraded to a
# version from PyPI. However, we can't *only* rely on the system store
# because not all systems actually have one, so at the end of the day we
# still need to fall back to trusting the bundled copy.
#
# Resolution Method:
#
# 1. We prefer a CAPath, however we will *only* prefer a CAPath if the
# directory exists and it is not empty. This works around systems like
# Homebrew which have an empty CAPath but a populated CAFile.
# 2. Failing that, we prefer a CAFile, however again we will *only* prefer
# it if it exists on disk and if it is not empty. This will work around
# systems that have an empty CAFile sitting around for no good reason.
# 3. Finally, we'll just fall back to letting requests use it's bundled
# CAFile, which can of course be overriden by the end user installing
# certifi.
if _ssl_paths.capath is not None and os.listdir(_ssl_paths.capath):
CA_BUNDLE_PATH = _ssl_paths.capath
elif _ssl_paths.cafile is not None and os.path.getsize(_ssl_paths.cafile):
CA_BUNDLE_PATH = _ssl_paths.cafile
else:
CA_BUNDLE_PATH = None
else:
# If we aren't running on a copy of Python that is new enough to be able
# to query OpenSSL for it's default locations, then we'll only support
# using the built in CA Bundle by default.
CA_BUNDLE_PATH = None


Expand Down
13 changes: 9 additions & 4 deletions pip/pep425tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import sys
import warnings
import platform
import logging

try:
import sysconfig
Expand All @@ -13,6 +14,10 @@
import distutils.sysconfig as sysconfig
import distutils.util


logger = logging.getLogger(__name__)


_osx_arch_pat = re.compile(r'(.+)_(\d+)_(\d+)_(.+)')


Expand Down Expand Up @@ -69,8 +74,8 @@ def get_flag(var, fallback, expected=True, warn=True):
val = get_config_var(var)
if val is None:
if warn:
warnings.warn("Config variable '{0}' is unset, Python ABI tag may "
"be incorrect".format(var), RuntimeWarning, 2)
logger.debug("Config variable '%s' is unset, Python ABI tag may "
"be incorrect", var)
return fallback()
return val == expected

Expand Down Expand Up @@ -116,8 +121,8 @@ def get_platform():
# of MACOSX_DEPLOYMENT_TARGET on which Python was built, which may
# be signficantly older than the user's current machine.
release, _, machine = platform.mac_ver()
major, minor, micro = release.split('.')
return 'macosx_{0}_{1}_{2}'.format(major, minor, machine)
split_ver = release.split('.')
return 'macosx_{0}_{1}_{2}'.format(split_ver[0], split_ver[1], machine)
# XXX remove distutils dependency
return distutils.util.get_platform().replace('.', '_').replace('-', '_')

Expand Down
50 changes: 39 additions & 11 deletions pip/req/req_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
import sys
import tempfile
import traceback
import warnings
import zipfile

from distutils.util import change_root
from distutils import sysconfig
from distutils.util import change_root
from email.parser import FeedParser

from pip._vendor import pkg_resources, six
Expand All @@ -20,7 +21,7 @@

import pip.wheel

from pip.compat import native_str, WINDOWS
from pip.compat import native_str, get_stdlib, WINDOWS
from pip.download import is_url, url_to_path, path_to_url, is_archive_file
from pip.exceptions import (
InstallationError, UninstallationError, UnsupportedWheel,
Expand All @@ -32,9 +33,11 @@
display_path, rmtree, ask_path_exists, backup_dir, is_installable_dir,
dist_in_usersite, dist_in_site_packages, egg_link_path,
call_subprocess, read_text_file, FakeFile, _make_build_dir, ensure_dir,
get_installed_version, canonicalize_name
get_installed_version, canonicalize_name, normalize_path, dist_is_local,
)

from pip.utils.hashes import Hashes
from pip.utils.deprecation import RemovedInPip10Warning
from pip.utils.logging import indent_log
from pip.utils.setuptools_build import SETUPTOOLS_SHIM
from pip.utils.ui import open_spinner
Expand Down Expand Up @@ -114,6 +117,9 @@ def __init__(self, req, comes_from, source_dir=None, editable=False,
self.install_succeeded = None
# UninstallPathSet of uninstalled distribution (for possible rollback)
self.uninstalled = None
# Set True if a legitimate do-nothing-on-uninstall has happened - e.g.
# system site packages, stdlib packages.
self.nothing_to_uninstall = False
self.use_user_site = False
self.target_dir = None
self.options = options if options else {}
Expand Down Expand Up @@ -606,6 +612,26 @@ def uninstall(self, auto_confirm=False):
)
dist = self.satisfied_by or self.conflicts_with

dist_path = normalize_path(dist.location)
if not dist_is_local(dist):
logger.info(
"Not uninstalling %s at %s, outside environment %s",
dist.key,
dist_path,
sys.prefix,
)
self.nothing_to_uninstall = True
return

if dist_path in get_stdlib():
logger.info(
"Not uninstalling %s at %s, as it is in the standard library.",
dist.key,
dist_path,
)
self.nothing_to_uninstall = True
return

paths_to_remove = UninstallPathSet(dist)
develop_egg_link = egg_link_path(dist)
develop_egg_link_egg_info = '{0}.egg-info'.format(
Expand Down Expand Up @@ -647,12 +673,14 @@ def uninstall(self, auto_confirm=False):
paths_to_remove.add(path + '.pyo')

elif distutils_egg_info:
raise UninstallationError(
"Detected a distutils installed project ({0!r}) which we "
"cannot uninstall. The metadata provided by distutils does "
"not contain a list of files which have been installed, so "
"pip does not know which files to uninstall.".format(self.name)
warnings.warn(
"Uninstalling a distutils installed project ({0}) has been "
"deprecated and will be removed in a future version. This is "
"due to the fact that uninstalling a distutils project will "
"only partially uninstall the project.".format(self.name),
RemovedInPip10Warning,
)
paths_to_remove.add(distutils_egg_info)

elif dist.location.endswith('.egg'):
# package installed by easy_install
Expand Down Expand Up @@ -729,15 +757,15 @@ def rollback_uninstall(self):
self.uninstalled.rollback()
else:
logger.error(
"Can't rollback %s, nothing uninstalled.", self.project_name,
"Can't rollback %s, nothing uninstalled.", self.name,
)

def commit_uninstall(self):
if self.uninstalled:
self.uninstalled.commit()
else:
elif not self.nothing_to_uninstall:
logger.error(
"Can't commit %s, nothing uninstalled.", self.project_name,
"Can't commit %s, nothing uninstalled.", self.name,
)

def archive(self, build_dir):
Expand Down
17 changes: 1 addition & 16 deletions pip/req/req_uninstall.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@

import logging
import os
import sys
import tempfile

from pip.compat import uses_pycache, WINDOWS, cache_from_source
from pip.exceptions import UninstallationError
from pip.utils import (rmtree, ask, is_local, dist_is_local, renames,
normalize_path)
from pip.utils import rmtree, ask, is_local, renames, normalize_path
from pip.utils.logging import indent_log


Expand All @@ -34,17 +32,6 @@ def _permitted(self, path):
"""
return is_local(path)

def _can_uninstall(self):
if not dist_is_local(self.dist):
logger.info(
"Not uninstalling %s at %s, outside environment %s",
self.dist.project_name,
normalize_path(self.dist.location),
sys.prefix,
)
return False
return True

def add(self, path):
head, tail = os.path.split(path)

Expand Down Expand Up @@ -94,8 +81,6 @@ def _stash(self, path):
def remove(self, auto_confirm=False):
"""Remove paths in ``self.paths`` with confirmation (unless
``auto_confirm`` is True)."""
if not self._can_uninstall():
return
if not self.paths:
logger.info(
"Can't uninstall '%s'. No files were found to uninstall.",
Expand Down
23 changes: 23 additions & 0 deletions tests/functional/test_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,29 @@ def test_editable_install_from_local_directory_with_no_setup_py(script, data):
assert "is not installable. File 'setup.py' not found." in result.stderr


@pytest.mark.skipif("sys.version_info < (2,7) or sys.version_info >= (3,4)")
@pytest.mark.xfail
def test_install_argparse_shadowed(script, data):
# When argparse is in the stdlib, we support installing it
# even though thats pretty useless because older packages did need to
# depend on it, and not having its metadata will cause pkg_resources
# requirements checks to fail // trigger easy-install, both of which are
# bad.
# XXX: Note, this test hits the outside-environment check, not the
# in-stdlib check, because our tests run in virtualenvs...
result = script.pip('install', 'argparse>=1.4')
assert "Not uninstalling argparse" in result.stdout


@pytest.mark.skipif("sys.version_info < (3,4)")
def test_upgrade_argparse_shadowed(script, data):
# If argparse is installed - even if shadowed for imported - we support
# upgrading it and properly remove the older versions files.
script.pip('install', 'argparse==1.3')
result = script.pip('install', 'argparse>=1.4')
assert "Not uninstalling argparse" not in result.stdout


def test_install_as_egg(script, data):
"""
Test installing as egg, instead of flat install.
Expand Down
Loading

0 comments on commit 024cfe1

Please sign in to comment.