diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000000..7a2f4b47c48 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,4 @@ +[run] +branch = True +omit = + pip/_vendor/* diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000000..75716e6ea4e --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,13 @@ +* Pip version: +* Python version: +* Operating System: + +### Description: + +// REPLACE ME: What are you trying to get done, what has happened, what went wrong, and what did you expect? + +### What I've run: + +``` +// REPLACE ME: Paste a log of command(s) you ran and pip's output, tracebacks, etc, here +``` diff --git a/.gitignore b/.gitignore index 7efeda1189b..06499a6d032 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,11 @@ +build/ +dist/ +docs/_build/ +pip.egg-info/ MANIFEST -pip.egg-info/* -ScriptTest-*.egg -virtualenv-*.egg -mock-*egg -dist/* -docs/_build/* -build/* -*.pyc -*.pyo -*.~ .tox - +*.egg +*.py[cod] +*~ +.coverage +coverage.xml diff --git a/.landscape.yml b/.landscape.yml new file mode 100644 index 00000000000..6da153837ba --- /dev/null +++ b/.landscape.yml @@ -0,0 +1,2 @@ +ignore-paths: + - pip/_vendor/ diff --git a/.mailmap b/.mailmap new file mode 100644 index 00000000000..e54cab6f92c --- /dev/null +++ b/.mailmap @@ -0,0 +1,36 @@ +Adam Wentz +Alex Grönholm +Alex Grönholm +Anatoly Techtonik +Andrey Bulgakov + +Andrei Geacar unknown +Ben Rosser + + +Daniel Holth +David Black + +Dongweiming +Endoh Takanao +Erik M. Bray +Gabriel de Perthuis +Geoffrey Lehée +Hsiaoming Yang +Ilya Baryshev +Jakub Stasiak +John-Scott Atlakson +Jorge Niedbalski + +Ludovic Gasc +Markus Hametner +Masklinn +Matthew Iversen + +Preston Holmes +Przemek Wrzos + +Romuald Brunet +Thomas Johansson prencher +Yoval P +Zhiping Deng diff --git a/.travis.yml b/.travis.yml index cbcdbbea365..ff3558cf637 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,37 +1,49 @@ language: python -env: - - TOXENV=docs - - TOXENV=py26 - - TOXENV=py27 - - TOXENV=py32 - - TOXENV=py33 - - TOXENV=py34 - - TOXENV=pypy - -before_install: - - "[ -d ~/.distlib ] && sudo chown -R travis:travis ~/.distlib || true" - -install: - - sudo apt-get install subversion bzr mercurial - - echo -e "[web]\ncacerts = /etc/ssl/certs/ca-certificates.crt" >> ~/.hgrc - - git config --global user.email "python-virtualenv@googlegroups.com" - - git config --global user.name "Pip" - - pip install --upgrade setuptools - - pip install tox - -script: tox - -branches: - only: - - develop - - 1.3.X - - 1.4.X - - 1.5.X + +matrix: + include: + - env: TOXENV=docs + - env: TOXENV=pep8 + - env: TOXENV=py3pep8 + - env: TOXENV=packaging + - python: 2.6 # these are just to make travis's UI a bit prettier + env: TOXENV=py26 + - python: 2.7 + env: TOXENV=py27 + - python: 3.3 + env: TOXENV=py33 + - python: 3.4 + env: TOXENV=py34 + - python: 3.5 + env: TOXENV=py35 + - python: nightly + env: TOXENV=py36 + - python: pypy + env: TOXENV=pypy + - python: 2.7 + env: TOXENV=py27 VENDOR=no + - python: 3.5 + env: TOXENV=py35 VENDOR=no + - python: 2.7 + env: TOXENV=py27 VENDOR=no WHEELS=yes + - python: 3.5 + env: TOXENV=py35 VENDOR=no WHEELS=yes + + +install: .travis/install.sh + + +script: .travis/run.sh + notifications: irc: channels: - - "irc.freenode.org#pypa-dev" + # This is set to a secure variable to prevent forks from notifying the + # IRC channel whenever they fail a build. This can be removed when travis + # implements https://github.com/travis-ci/travis-ci/issues/1094. + # The actual value here is: irc.freenode.org#pypa-dev + - secure: "zAlwcmrDThlRsZz7CPDGpj4ABTzf7bc/zQXYtvIuqmSj0yJMAwsO5Vx/+qdTGYBvmW/oHw2s/uUgtkZzntSQiVQToKMag2fs0d3wV5bLJQUE2Si2jnH2JOQo3JZWSo9HOqL6WYmlKGI8lH9FVTdVLgpeJmIpLy1bN4zx4/TiJjc=" use_notice: true skip_join: true diff --git a/.travis/install.sh b/.travis/install.sh new file mode 100755 index 00000000000..8b700ba3227 --- /dev/null +++ b/.travis/install.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -e +set -x + +git config --global user.email "pypa-dev@googlegroups.com" +git config --global user.name "pip" + +pip install --upgrade setuptools +pip install --upgrade tox diff --git a/.travis/run.sh b/.travis/run.sh new file mode 100755 index 00000000000..965a1cfc45c --- /dev/null +++ b/.travis/run.sh @@ -0,0 +1,47 @@ +#!/bin/bash +set -e +set -x + +# We want to create the virtual environment here, but not actually run anything +tox --notest + +# If we have a VENDOR=no then we want to reinstall pip into the virtual +# environment without the vendor directory included as well as install the +# dependencies we need installed. +if [[ $VENDOR = "no" ]]; then + # Install our dependencies if we're not installing from wheels + if [[ $WHEELS != "yes" ]]; then + .tox/$TOXENV/bin/pip install -r pip/_vendor/vendor.txt --no-deps + fi + + # Install our dependencies if we're installing from wheels + if [[ $WHEELS = "yes" ]]; then + mkdir -p /tmp/wheels + pip wheel --wheel-dir /tmp/wheels --no-deps -r pip/_vendor/vendor.txt + cp /tmp/wheels/* `echo .tox/$TOXENV/lib/python*/site-packages/pip/_vendor/` + fi + + # Remove the vendored dependencies from within the installed pip inside of + # our installed copy of pip. + find .tox/$TOXENV/lib/python*/site-packages/pip/_vendor -d \ + -not -regex '.*/pip/_vendor/__init__\.py$' \ + -not -regex '.*/pip/_vendor$' \ + -exec rm -rf {} \; + + # Patch our installed pip/_vendor/__init__.py so that it knows to look for + # the vendored dependencies instead of only looking for the vendored. + sed -i 's/DEBUNDLED = False/DEBUNDLED = True/' \ + .tox/$TOXENV/lib/python*/site-packages/pip/_vendor/__init__.py + + # Test to make sure that we successfully installed without vendoring + if [ -f .tox/$TOXENV/lib/python*/site-packages/pip/_vendor/six.py ]; then + echo "Did not successfully unvendor" + exit 1 + fi +fi + +# Run the unit tests +tox -- -m unit + +# Run our integration tests +tox -- -m integration -n 8 diff --git a/AUTHORS.txt b/AUTHORS.txt index 3d62f0190b3..47f309aa13c 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -1,120 +1,289 @@ -Alex Gaynor -Alex Grönholm -Alex Morega -Alexandre Conrad -Andrey Bulgakov -Anrs Hu -Anton Patrushev -Antti Kaihola -Armin Ronacher -Aziz Köksal -Ben Rosser -Bernardo B. Marques -Bradley Ayers -Brian Rosner -Carl Meyer -Chris McDonough -Christian Oudard -Clay McClure -Cody Soyland -Craig Kerstiens -Cristian Sorinel -Dan Sully -Daniel Holth -Dave Abrahams -David (d1b) -David Aguilar -David Evans -David Pursehouse -dengzhp -Dmitry Gladkov -Donald Stufft -Endoh Takanao -enoch -Erik M. Bray -Francesco -Gabriel de Perthuis -Garry Polley -Geoffrey Lehée -George Song -Georgi Valkov -Herbert Pfennig -hetmankp -Hugo Lopes Tavares -Hynek Schlawack -Ian Bicking -Igor Sobreira -Ionel Maries Cristian -Jakub Vysoky -James Cleveland -Jannis Leidel -Jakub Stasiak -Jay Graves -Jeff Dairiki -John-Scott Atlakson -Jon Parise -Jonas Nockert -Jon Parise -Jorge Niedbalski -Josh Bronson -Josh Hansen -Kamal Bin Mustafa -Kelsey Hightower -Kenneth Belitzky -Kenneth Reitz -Kevin Frommelt -Kumar McMillan -Lev Givon -Lincoln de Sousa -Luke Macken -Masklinn -Marc Abramowitz -Marc Tamlyn -Marcus Smith -Markus Hametner -Matt Maker -Maxime Rouyrre -Michael Williamson -Miguel Araujo Perez -Monty Taylor -Nick Stenning -Nowell Strite -Oliver Tonnhofer -Olivier Girardot -Ollie Rutherfurd -Oren Held -Patrick Jenkins -Patrick Dubroy -Paul Moore -Paul Nasrat -Paul Oswald -Paul van der Linden -Peter Waller -Phil Freo -Phil Whelan -Piet Delport -Preston Holmes -Przemek Wrzos -Qiangning Hong -Rafael Caricio -Rene Dudfield -Roey Berman -Ronny Pfannschmidt -Rory McCann -Ross Brattain -Sergey Vasilyev -Seth Woodworth -Simon Cross -Stavros Korokithakis -Stéphane Klein -Steven Myint -Takayuki SHIMIZUKAWA -Thomas Fenzl -Thomas Johansson -Toshio Kuratomi -Travis Swicegood -Vinay Sajip -Vitaly Babiy -W. Trevor King -Wil Tan -Hsiaoming Yang +Adam Wentz +Aleks Bunin +Alex Gaynor +Alex Grönholm +Alex Morega +Alexandre Conrad +Alli +Anatoly Techtonik +Andrei Geacar +Andrey Bulgakov +Andy Freeland +Anrs Hu +Antoine Musso +Anton Ovchinnikov +Anton Patrushev +Antonio Alvarado Hernandez +Antti Kaihola +AQNOUCH Mohammed +Arindam Choudhury +Armin Ronacher +Ashley Manton +Baptiste Mispelon +Ben Darnell +Ben Rosser +Bence Nagy +Berker Peksag +Bernardo B. Marques +Bogdan Opanchuk +Brad Erickson +Bradley Ayers +Brian Rosner +Bruno Renié +Buck Golemon +burrows +Bussonnier Matthias +Carl Meyer +Carlos Liam +Carol Willing +Cass +Chris Brinker +Chris Jerdonek +Chris McDonough +Chris Wolfe +Christian Oudard +Christopher Snyder +Clark Boylan +Clay McClure +Cody Soyland +Colin Watson +Connor Osborn +Cory Benfield +Cory Wright +Craig Kerstiens +Cristian Sorinel +Dan Savilonis +Dan Sully +daniel +Daniel Collins +Daniel Hahler +Daniel Holth +Daniel Jost +Daniele Procida +Dav Clark +Dave Abrahams +David Aguilar +David Black +David Evans +David Linke +David Pursehouse +David Wales +Davidovich +derwolfe +Dmitry Gladkov +Domen Kožar +Donald Stufft +Dongweiming +Douglas Thor +Dwayne Bailey +Ed Morley +Emil Styrke +Endoh Takanao +enoch +Eric Gillingham +Eric Hanchrow +Erik M. Bray +Erik Rose +Eugene Vereshchagin +Florian Briand +Francesco +Francesco Montesano +Gabriel de Perthuis +Garry Polley +Geoffrey Lehée +Geoffrey Sneddon +George Song +Georgi Valkov +gizmoguy1 +Guilherme Espada +Guy Rozendorn +Herbert Pfennig +Hsiaoming Yang +Hugo Lopes Tavares +Hynek Schlawack +Ian Bicking +Ian Cordasco +Ian Lee +Ian Wienand +Ian Wienand +Igor Sobreira +Ilya Baryshev +INADA Naoki +Ionel Cristian Mărieș +Ionel Maries Cristian +Jakub Stasiak +Jakub Vysoky +James Cleveland +James Cleveland +James Firth +James Polley +Jan Pokorný +Jannis Leidel +jarondl +Jay Graves +Jeff Barber +Jeff Dairiki +Jeremy Stanley +Jim Garrison +John-Scott Atlakson +Jon Banafato +Jon Parise +Jon Wayne Parrott +Jonas Nockert +Joost Molenaar +Jorge Niedbalski +Joseph Long +Josh Bronson +Josh Hansen +Josh Schneier +Julien Demoor +jwg4 +Jyrki Pulliainen +Kamal Bin Mustafa +kaustav haldar +Kelsey Hightower +Kenneth Belitzky +Kenneth Reitz +Kenneth Reitz +Kevin Burke +Kevin Carter +Kevin Frommelt +Kit Randel +Kumar McMillan +Kyle Persohn +Laurent Bristiel +Leon Sasson +Lev Givon +Lincoln de Sousa +Ludovic Gasc +Luke Macken +Marc Abramowitz +Marc Tamlyn +Marcus Smith +Mark Kohler +Markus Hametner +Masklinn +Matej Stuchlik +Mathew Jennings +Matt Good +Matt Maker +Matt Robenolt +matthew +Matthew Einhorn +Matthew Gilliard +Matthew Iversen +Matthew Trumbell +Matthias Bussonnier +Maxime Rouyrre +Michael +Michael E. Karpeles +Michael Klich +Michael Williamson +Miguel Araujo Perez +Mihir Singh +Min RK +MinRK +montefra +Monty Taylor +Nate Coraor +Nathaniel J. Smith +Nick Coghlan +Nick Stenning +Nowell Strite +nvdv +Oliver Tonnhofer +Olivier Girardot +Olivier Grisel +Ollie Rutherfurd +Oren Held +Oscar Benjamin +Patrick Dubroy +Patrick Jenkins +Patrick Lawson +patricktokeeffe +Paul Moore +Paul Nasrat +Paul Oswald +Paul van der Linden +Paulus Schoutsen +Pawel Jasinski +Peter Waller +Phaneendra Chiruvella +Phil Freo +Phil Whelan +Philippe Ombredanne +Pierre-Yves Rofes +Piet Delport +Preston Holmes +Przemek Wrzos +Qiangning Hong +Rafael Caricio +Ralf Schmitt +Razzi Abuissa +Remi Rampin +Rene Dudfield +Richard Jones +RobberPhex +Robert Collins +Robert McGibbon +Robert T. McGibbon +Roey Berman +Rohan Jain +Rohan Jain +Rohan Jain +Roman Bogorodskiy +Romuald Brunet +Ronny Pfannschmidt +Rory McCann +Ross Brattain +Roy Wellington Ⅳ +Roy Wellington Ⅳ +Ryan Wooden +Sachi King +Salvatore Rinchiera +schlamar +Scott Kitterman +seanj +Sergey Vasilyev +Seth Woodworth +Simeon Visser +Simon Cross +Stavros Korokithakis +Stefan Scherfke +Stephan Erb +Steve Kowalik +Steven Myint +stonebig +Stéphane Bidoul (ACSONE) +Stéphane Bidoul +Stéphane Klein +Takayuki SHIMIZUKAWA +Thomas Fenzl +Thomas Grainger +Thomas Guettler +Thomas Johansson +Thomas Kluyver +Thomas Smith +Tim Harder +tim smith +tinruufu +Tomer Chachamu +Tony Zhaocheng Tan +Toshio Kuratomi +Travis Swicegood +Valentin Haenel +Victor Stinner +Ville Skyttä +Vinay Sajip +Vitaly Babiy +Vladimir Rutsky +W. Trevor King +Wil Tan +William ML Leslie +Xavier Fernandez +Xavier Fernandez +Yen Chi Hsuan +Yoval P +Yu Jian +Zearin +Zhiping Deng diff --git a/CHANGES.txt b/CHANGES.txt index 2405601c299..aebfd1bfe10 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,15 +1,745 @@ -**1.5.6 (2014-05-16)** +**8.2.0 (unreleased)** + +* Pip show is less verbose by default. `--verbose` prints multiline fields + +* Added optional column formatting to ``pip list`` (:issue:`3651`). + +* Fix the build on systems with symlinked /tmp directory for custom + builds such as numpy. + +* Fix regression in pip freeze: when there is more than one git remote, + priority is given to the remote named origin (:issue:`3616`) + +* Pip wheel now works on editable packages too (it was only working on + editable dependencies before); this allows running pip wheel on the result + of pip freeze in presence of editable requirements (:issue:`3291`) + + +**8.1.2 (2016-05-10)** + +* Fix a regression on systems with uninitialized locale (:issue:`3575`). + +* Use environment markers to filter packages before determining if a + required wheel is supported. Solves (:issue:`3254`). + +* Make glibc parsing for `manylinux1` support more robust for the variety of + glibc versions found in the wild (:issue:`3588`). + +* Update environment marker support to fully support PEP 508 and legacy + environment markers (:issue:`3624`). + +* Always use debug logging to the ``--log`` file (:issue:`3351`). + +* Don't attempt to wrap search results for extremely narrow terminal windows + (:issue:`3655`). + + +**8.1.1 (2016-03-17)** + +* Fix regression with non-ascii requirement files on Python 2 and add support + for encoding headers in requirement files (:issue:`3548`, :pull:`3547`). + + +**8.1.0 (2016-03-05)** + +* Implement PEP 513, which adds support for the manylinux1 platform tag, + allowing carefully compiled binary wheels to be installed on compatible Linux + platforms. + +* Allow wheels which are not specific to a particular Python interpreter but + which are specific to a particular platform (:issue:`3202`). + +* Fixed an issue where ``call_subprocess`` would crash trying to print debug + data on child process failure (:issue:`3521`, :pull:`3522`). + +* Exclude the wheel package from the `pip freeze` output (like pip and setuptools). + :issue:`2989`. + +* Allow installing modules from a subdirectory of a vcs repository + in non-editable mode (:issue:`3217`, :pull:`3466`). + +* Make pip wheel and pip download work with vcs urls with subdirectory option + (:pull:`3466`). + +* Show classifiers in ``pip show``. + +* Show PEP376 Installer in ``pip show`` (:issue:`3517`). + +* Unhide completion command (:pull:`1810`). + +* Show latest version number in ``pip search`` results (:pull:`1415`). + +* Decode requirement files according to their BOM if present (:pull:`3485`, + :issue:`2865`). + +* Fix and deprecate package name detection from url path (:issue:`3523` and + :pull:`3495`). + +* Correct the behavior where interpreter specific tags (such as cp34) were + being used on later versions of the same interpreter instead of only for that + specific interpreter (:issue:`3472`). + +* Fix an issue where pip would erroneously install a 64 bit wheel on a 32 bit + Python running on a 64 bit OS X machine. + +* Do not assume that all git repositories have an origin remote. + +* Correctly display the line to add to a requirements.txt for an URL based + dependency when ``--require-hashes`` is enabled. + + +**8.0.3 (2016-02-25)** + +* Make ``install --quiet`` really quiet. See :issue:`3418`. + +* Fix a bug when removing packages in python 3: disable INI-style parsing of the + entry_point.txt file to allow entry point names with colons (:pull:`3434`) + +* Normalize generated script files path in RECORD files. (:pull:`3448`) + +* Fix bug introduced in 8.0.0 where subcommand output was not shown, + even when the user specified ``-v`` / ``--verbose``. :issue:`3486`. + +* Enable python -W with respect to PipDeprecationWarning. (:pull:`3455`) + +* Upgrade distlib to 0.2.2 (fix :issue:`3467`): + + * Improved support for Jython when quoting executables in output scripts. + +* Add a `--all` option to `pip freeze` to include usually skipped package + (like pip, setuptools and wheel) to the freeze output. :issue:`1610`. + + +**8.0.2 (2016-01-21)** + +* Stop attempting to trust the system CA trust store because it's extremely + common for them to be broken, often in incompatible ways. See :pull:`3416`. + + +**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. + +* **BACKWARD INCOMPATIBLE** Remove the ability to find any files other than the + ones directly linked from the index or find-links pages. + +* **BACKWARD INCOMPATIBLE** Remove the ``--download-cache`` which had been + deprecated and no-op'd in 6.0. + +* **BACKWARD INCOMPATIBLE** Remove the ``--log-explicit-levels`` which had been + deprecated in 6.0. + +* **BACKWARD INCOMPATIBLE** Change pip wheel --wheel-dir default path from + /wheelhouse to . + +* Deprecate and no-op the ``--allow-external``, ``--allow-all-external``, and + ``--allow-unverified`` functionality that was added as part of PEP 438. With + changes made to the repository protocol made in PEP 470, these options are no + longer functional. + +* Allow ``--trusted-host`` within a requirements file. :issue:`2822`. + +* Allow ``--process-dependency-links`` within a requirements file. :issue:`1274`. + +* Allow ``--pre`` within a requirements file. :issue:`1273`. + +* Allow repository URLs with secure transports to count as trusted. (E.g., + "git+ssh" is okay.) :issue:`2811`. + +* Implement a top-level ``pip download`` command and deprecate + ``pip install --download``. + +* Fixed :issue:`3141`, when uninstalling, look for the case of paths containing + symlinked directories (:pull:`3154`) + +* When installing, if building a wheel fails, clear up the build directory + before falling back to a source install. :issue:`3047`. + +* Fix user directory expansion when ``HOME=/``. Workaround for Python bug + http://bugs.python.org/issue14768, reported in :issue:`2996`. + +* Fixed :issue:`3009`, correct reporting of requirements file line numbers + (:pull:`3125`) + +* Fixed :issue:`1062`, Exception(IOError) for ``pip freeze`` and ``pip list`` + commands with subversion >= 1.7. (:pull:`3346`) + +* Provide a spinner showing that progress is happening when installing or + building a package via ``setup.py``. This will alleviate concerns that + projects with unusually long build times have with pip appearing to stall. + +* Include the functionality of ``peep`` into pip, allowing hashes to be baked + into a requirements file and ensuring that the packages being downloaded + match one of those hashes. This is an additional, opt-in security measure + that, when used, removes the need to trust the repository. + +* Fix a bug causing pip to not select a wheel compiled against an OSX SDK later + than what Python itself was compiled against when running on a newer version + of OSX. + +* Add a new ``--prefix`` option for ``pip install`` that supports wheels and + sdists. (:pull:`3252`) + +* Fixed :issue:`2042` regarding wheel building with setup.py using a different + encoding than the system. + +* Drop PasteScript specific egg_info hack. (:pull:`3270`) + +* Allow combination of pip list options --editable with --outdated/--uptodate. + (:issue:`933`) + +* Gives VCS implementations control over saying whether a project + is under their control (:pull:`3258`) + +* Git detection now works when ``setup.py`` is not at the Git repo root + and when ``package_dir`` is used, so ``pip freeze`` works in more + cases (:pull:`3258`) + +* Correctly freeze Git develop packages in presence of the &subdirectory + option (:pull:`3258`) + +* The detection of editable packages now relies on the presence of ``.egg-link`` + instead of looking for a VCS, so ``pip list -e`` is more reliable + (:pull:`3258`) + +* Add the ``--prefix`` flag to ``pip install`` which allows specifying a root + prefix to use instead of ``sys.prefix`` (:pull:`3252`). + +* Allow duplicate specifications in the case that only the extras differ, and + union all specified extras together (:pull:`3198`). + +* Fix the detection of the user's current platform on OSX when determining the + OSX SDK version (:pull:`3232`). + +* Prevent the automatically built wheels from mistakenly being used across + multiple versions of Python when they may not be correctly configured for + that by making the wheel specific to a specific version of Python and + specific interpreter (:pull:`3225`). + +* Emulate the SOABI support in wheels from Python 2.x on Python 2.x as closely + as we can with the information available within the interpreter + (:pull:`3075`). + +* Don't roundtrip to the network when git is pinned to a specific commit hash + and that hash already exists locally (:pull:`3066`). + +* Prefer wheels built against a newer SDK to wheels built against an older SDK + on OSX (:pull:`3163`). + +* Show entry points for projects installed via wheel (:pull:`3122`). + +* Improve message when an unexisting path is passed to --find-links option + (:issue:`2968`). + +* pip freeze does not add the VCS branch/tag name in the #egg=... fragment anymore + (:pull:`3312`). + +* Warn on installation of editable if the provided #egg=name part does not + match the metadata produced by `setup.py egg_info`. :issue:`3143`. + +* Add support for .xz files for python versions supporting them (>= 3.3). + :issue:`722`. + + +**7.1.2 (2015-08-22)** + +* Don't raise an error if pip is not installed when checking for the latest pip + version. + + +**7.1.1 (2015-08-20)** + +* Check that the wheel cache directory is writable before we attempt to write + cached files to them. + +* Move the pip version check until *after* any installs have been performed, + thus removing the extraneous warning when upgrading pip. + +* Added debug logging when using a cached wheel. + +* Respect platlib by default on platforms that have it separated from purelib. + +* Upgrade packaging to 15.3. + + * Normalize post-release spellings for rev/r prefixes. + +* Upgrade distlib to 0.2.1. + + * Updated launchers to decode shebangs using UTF-8. This allows non-ASCII + pathnames to be correctly handled. + + * Ensured that the executable written to shebangs is normcased. + + * Changed ScriptMaker to work better under Jython. + +* Upgrade ipaddress to 1.0.13. + + +**7.1.0 (2015-06-30)** + +* Allow constraining versions globally without having to know exactly what will + be installed by the pip command. :issue:`2731`. + +* Accept --no-binary and --only-binary via pip.conf. :issue:`2867`. + +* Allow ``--allow-all-external`` within a requirements file. + +* Fixed an issue where ``--user`` could not be used when ``--prefix`` was used + in a distutils configuration file. + +* Fixed an issue where the SOABI tags were not correctly being generated on + Python 3.5. + +* Fixed an issue where we were advising windows users to upgrade by directly + executing pip, when that would always fail on Windows. + +* Allow ``~`` to be expanded within a cache directory in all situations. + + +**7.0.3 (2015-06-01)** + +* Fixed a regression where ``--no-cache-dir`` would raise an exception, fixes + :issue:`2855`. + + +**7.0.2 (2015-06-01)** + +* **BACKWARD INCOMPATIBLE** Revert the change (released in v7.0.0) that + required quoting in requirements files around specifiers containing + environment markers. (:pull:`2841`) + +* **BACKWARD INCOMPATIBLE** Revert the accidental introduction of support for + options interleaved with requirements, version specifiers etc in + ``requirements`` files. (:pull:`2841`) + +* Expand ``~`` in the cache directory when caching wheels, fixes :issue:`2816`. + +* Use ``python -m pip`` instead of ``pip`` when recommending an upgrade command + to Windows users. + + +**7.0.1 (2015-05-22)** + +* Don't build and cache wheels for non-editable installations from VCSs. + +* Allow ``--allow-all-external`` inside of a requirements.txt file, fixing a + regression in 7.0. + + +**7.0.0 (2015-05-21)** + +* **BACKWARD INCOMPATIBLE** Removed the deprecated ``--mirror``, + ``--use-mirrors``, and ``-M`` options. +* **BACKWARD INCOMPATIBLE** Removed the deprecated ``zip`` and ``unzip`` + commands. + +* **BACKWARD INCOMPATIBLE** Removed the deprecated ``--no-install`` and + ``--no-download`` options. + +* **BACKWARD INCOMPATIBLE** No longer implicitly support an insecure origin + origin, and instead require insecure origins be explicitly trusted with the + ``--trusted-host`` option. + +* **BACKWARD INCOMPATIBLE** Removed the deprecated link scraping that attempted + to parse HTML comments for a specially formatted comment. + +* **BACKWARD INCOMPATIBLE** Requirements in requirements files containing + markers must now be quoted due to parser changes from (:pull:`2697`) and + (:pull:`2725`). For example, use ``"SomeProject; python_version < '2.7'"``, + not simply ``SomeProject; python_version < '2.7'`` + +* `get-pip.py` now installs the "wheel" package, when it's not already installed + (:pull:`2800`). + +* Ignores bz2 archives if Python wasn't compiled with bz2 support. + Fixes :issue:`497` + +* Support ``--install-option`` and ``--global-option`` per requirement in + requirement files (:pull:`2537`) + +* Build Wheels prior to installing from sdist, caching them in the pip cache + directory to speed up subsequent installs. (:pull:`2618`) + +* Allow fine grained control over the use of wheels and source builds. + (:pull:`2699`) + +* ``--no-use-wheel`` and ``--use-wheel`` are deprecated in favour of new + options ``--no-binary`` and ``--only-binary``. The equivalent of + ``--no-use-wheel`` is ``--no-binary=:all:``. (:pull:`2699`) + +* The use of ``--install-option``, ``--global-option`` or ``--build-option`` + disable the use of wheels, and the autobuilding of wheels. (:pull:`2711`) + Fixes :issue:`2677` + +* Improve logging when a requirement marker doesn't match your environment + (:pull:`2735`) + +* Removed the temporary modifications (that began in pip v1.4 when distribute + and setuptools merged) that allowed distribute to be considered a conflict to + setuptools. ``pip install -U setuptools`` will no longer upgrade "distribute" + to "setuptools". Instead, use ``pip install -U distribute`` (:pull:`2767`). + +* Only display a warning to upgrade pip when the newest version is a final + release and it is not a post release of the version we already have + installed (:pull:`2766`). + +* Display a warning when attempting to access a repository that uses HTTPS when + we don't have Python compiled with SSL support (:pull:`2761`). + +* Allowing using extras when installing from a file path without requiring the + use of an editable (:pull:`2785`). + +* Fix an infinite loop when the cache directory is stored on a file system + which does not support hard links (:pull:`2796`). + +* Remove the implicit debug log that was written on every invocation, instead + users will need to use ``--log`` if they wish to have one (:pull:`2798`). + + +**6.1.1 (2015-04-07)** + +* No longer ignore dependencies which have been added to the standard library, + instead continue to install them. + + +**6.1.0 (2015-04-07)** + +* Fixes :issue:`2502`. Upgrades were failing when no potential links were found + for dependencies other than the current installation. (:pull:`2538`) + +* Use a smoother progress bar when the terminal is capable of handling it, + otherwise fallback to the original ASCII based progress bar. + +* Display much less output when `pip install` succeeds, because on success, + users probably don't care about all the nitty gritty details of compiling and + installing. When `pip install` fails, display the failed install output once + instead of twice, because once is enough. (:pull:`2487`) + +* Upgrade the bundled copy of requests to 2.6.0, fixing CVE-2015-2296. + +* Display format of latest package when using ``pip list --outdated``. + (:pull:`2475`) + +* Don't use pywin32 as ctypes should always be available on Windows, using + pywin32 prevented uninstallation of pywin32 on Windows. (:pull:`2467`) + +* Normalize the ``--wheel-dir`` option, expanding out constructs such as ``~`` + when used (:pull:`2441`). + +* Display a warning when an undefined extra has been requested. (:pull:`2142`) + +* Speed up installing a directory in certain cases by creating a sdist instead + of copying the entire directory. (:pull:`2535`) + +* Don't follow symlinks when uninstalling files (:pull:`2552`) + +* Upgrade the bundled copy of cachecontrol from 0.11.1 to 0.11.2. + Fixes :issue:`2481` (:pull:`2595`) + +* Attempt to more smartly choose the order of installation to try and install + dependencies before the projects that depend on them. (:pull:`2616`) + +* Skip trying to install libraries which are part of the standard library. + (:pull:`2636`, :pull:`2602`) + +* Support arch specific wheels that are not tied to a specific Python ABI. + (:pull:`2561`) + +* Output warnings and errors to stderr instead of stdout. (:pull:`2543`) + +* Adjust the cache dir file checks to only check ownership if the effective + user is root. (:pull:`2396`) + +* Install headers into a per project name directory instead of all of them into + the root directory when inside of a virtual environment. (:pull:`2421`) + + +**6.0.8 (2015-02-04)** + +* Fix an issue where the ``--download`` flag would cause pip to no longer use + randomized build directories. + +* Fix an issue where pip did not properly unquote quoted URLs which contain + characters like PEP 440's epoch separator (``!``). + +* Fix an issue where distutils installed projects were not actually uninstalled + and deprecate attempting to uninstall them altogether. + +* Retry deleting directories in case a process like an antivirus is holding the + directory open temporarily. + +* Fix an issue where pip would hide the cursor on Windows but would not reshow + it. + + +**6.0.7 (2015-01-28)** + +* Fix a regression where Numpy requires a build path without symlinks to + properly build. + +* Fix a broken log message when running ``pip wheel`` without a requirement. + +* Don't mask network errors while downloading the file as a hash failure. + +* Properly create the state file for the pip version check so it only happens + once a week. + +* Fix an issue where switching between Python 3 and Python 2 would evict cached + items. + +* Fix a regression where pip would be unable to successfully uninstall a + project without a normalized version. + + +**6.0.6 (2015-01-03)** + +* Continue the regression fix from 6.0.5 which was not a complete fix. + + +**6.0.5 (2015-01-03)** + +* Fix a regression with 6.0.4 under Windows where most commands would raise an + exception due to Windows not having the ``os.geteuid()`` function. + + +**6.0.4 (2015-01-03)** + +* Fix an issue where ANSI escape codes would be used on Windows even though the + Windows shell does not support them, causing odd characters to appear with + the progress bar. + +* Fix an issue where using -v would cause an exception saying + ``TypeError: not all arguments converted during string formatting``. + +* Fix an issue where using -v with dependency links would cause an exception + saying ``TypeError: 'InstallationCandidate' object is not iterable``. + +* Fix an issue where upgrading distribute would cause an exception saying + ``TypeError: expected string or buffer``. + +* Show a warning and disable the use of the cache directory when the cache + directory is not owned by the current user, commonly caused by using ``sudo`` + without the ``-H`` flag. + +* Update PEP 440 support to handle the latest changes to PEP 440, particularly + the changes to ``>V`` and `` when the given + specifier doesn't match anything. + +* Fix an issue where installing from a directory would not copy over certain + directories which were being excluded, however some build systems rely on + them. + + +**6.0 (2014-12-22)** + +* **PROCESS** Version numbers are now simply ``X.Y`` where the leading ``1`` + has been dropped. + +* **BACKWARD INCOMPATIBLE** Dropped support for Python 3.1. + +* **BACKWARD INCOMPATIBLE** Removed the bundle support which was deprecated in + 1.4. (:pull:`1806`) + +* **BACKWARD INCOMPATIBLE** File lists generated by `pip show -f` are now + rooted at the location reported by show, rather than one (unstated) + directory lower. (:pull:`1933`) + +* **BACKWARD INCOMPATIBLE** The ability to install files over the FTP protocol + was accidentally lost in pip 1.5 and it has now been decided to not restore + that ability. + +* **BACKWARD INCOMPATIBLE** PEP 440 is now fully implemented, this means that + in some cases versions will sort differently or version specifiers will be + interpreted differently than previously. The common cases should all function + similarly to before. + +* **DEPRECATION** ``pip install --download-cache`` and + ``pip wheel --download-cache`` command line flags have been deprecated and + the functionality removed. Since pip now automatically configures and uses + it's internal HTTP cache which supplants the ``--download-cache`` the + existing options have been made non functional but will still be accepted + until their removal in pip v8.0. For more information please see + https://pip.pypa.io/en/stable/reference/pip_install.html#caching + +* **DEPRECATION** ``pip install --build`` and ``pip install --no-clean`` are now + *NOT* deprecated. This reverses the deprecation that occurred in v1.5.3. See + :issue:`906` for discussion. + +* **DEPRECATION** Implicitly accessing URLs which point to an origin which is + not a secure origin, instead requiring an opt-in for each host using the new + ``--trusted-host`` flag (``pip install --trusted-host example.com foo``). + +* Allow the new ``--trusted-host`` flag to also disable TLS verification for + a particular hostname. + +* Added a ``--user`` flag to ``pip freeze`` and ``pip list`` to check the + user site directory only. + +* Fixed :issue:`1873`. Silence byte compile errors when installation succeed. + +* Added a virtualenv-specific configuration file. (:pull:`1364`) + +* Added site-wide configuration files. (:pull:`1978`) + +* Added an automatic check to warn if there is an updated version of pip + available (:pull:`2049`). + +* `wsgiref` and `argparse` (for >py26) are now excluded from `pip list` and `pip + freeze` (:pull:`1606`, :pull:`1369`) + +* Fixed :issue:`1424`. Add ``--client-cert`` option for SSL client certificates. + +* Fixed :issue:`1484`. `pip show --files` was broken for wheel installs. (:pull:`1635`) + +* Fixed :issue:`1641`. install_lib should take precedence when reading distutils config. + (:pull:`1642`) + +* Send `Accept-Encoding: identity` when downloading files in an attempt to + convince some servers who double compress the downloaded file to stop doing + so. (:pull:`1688`) + +* Fixed :issue:`1559`. Stop breaking when given pip commands in uppercase (:pull:`1725`) + +* Fixed :issue:`1618`. Pip no longer adds duplicate logging consumers, so it + won't create duplicate output when being called multiple times. (:pull:`1723`) + +* Fixed :issue:`1769`. `pip wheel` now returns an error code if any wheels + fail to build. + +* Fixed :issue:`1775`. `pip wheel` wasn't building wheels for dependencies of + editable requirements. + +* Allow the use of ``--no-use-wheel`` within a requirements file. (:pull:`1859`) + +* Fixed :issue:`1680`. Attempt to locate system TLS certificates to use instead + of the included CA Bundle if possible. (:pull:`1866`) + +* Fixed :issue:`1319`. Allow use of Zip64 extension in Wheels and other zip + files. (:pull:`1868`) + +* Fixed :issue:`1101`. Properly handle an index or --find-links target which + has a without a href attribute. (:pull:`1869`) + +* Fixed :issue:`1885`. Properly handle extras when a project is installed + via Wheel. (:pull:`1896`) + +* Fixed :issue:`1180`. Added support to respect proxies in ``pip search``. It + also fixes :issue:`932` and :issue:`1104`. (:pull:`1902`) + +* Fixed :issue:`798` and :issue:`1060`. `pip install --download` works with vcs links. + (:pull:`1926`) + +* Fixed :issue:`1456`. Disabled warning about insecure index host when using localhost. + Based off of Guy Rozendorn's work in :pull:`1718`. (:pull:`1967`) + +* Allow the use of OS standard user configuration files instead of ones simply + based around ``$HOME``. (:pull:`2021`) + +* Fixed :issue:`1825`. When installing directly from wheel paths or urls, + previous versions were not uninstalled. This also fixes :issue:`804` + specifically for the case of wheel archives. (:pull:`1838`) + +* Fixed :issue:`2075`, detect the location of the ``.egg-info`` directory by + looking for any file located inside of it instead of relying on the record + file listing a directory. (:pull:`2076`) + +* Fixed :issue:`1964`, :issue:`1935`, :issue:`676`, Use a randomized and secure + default build directory when possible. (:pull:`2122`, CVE-2014-8991) + +* Fixed :issue:`1433`. Support environment markers in requirements.txt files. + (:pull:`2134`) + +* Automatically retry failed HTTP requests by default. (:pull:`1444`, :pull:`2147`) + +* Fixed :issue:`1100` - Handle HTML Encoding better using a method that is more + similar to how browsers handle it. (:pull:`1874`) + +* Reduce the verbosity of the pip command by default. (:pull:`2175`, + :pull:`2177`, :pull:`2178`) + +* Fixed :issue:`2031` - Respect sys.executable on OSX when installing from + Wheels. + +* Display the entire URL of the file that is being downloaded when downloading + from a non PyPI repository (:pull:`2183`). + +* Support setuptools style environment markers in a source distribution + (:pull:`2153`). + + +**1.5.6 (2014-05-16)** * Upgrade requests to 2.3.0 to fix an issue with proxies on Python 3.4.1 - (PR #1821). + (:pull:`1821`). **1.5.5 (2014-05-03)** -* Fixes #1632. Uninstall issues on debianized pypy, specifically issues with - setuptools upgrades. (PR #1743) +* Fixes :issue:`1632`. Uninstall issues on debianized pypy, specifically issues with + setuptools upgrades. (:pull:`1743`) * Update documentation to point at https://bootstrap.pypa.io/get-pip.py for bootstrapping pip. @@ -31,23 +761,23 @@ * **DEPRECATION** ``pip install --build`` and ``pip install --no-clean`` are now - deprecated. See Issue #906 for discussion. + deprecated. See :issue:`906` for discussion. -* Fixed #1112. Couldn't download directly from wheel paths/urls, and when wheel +* Fixed :issue:`1112`. Couldn't download directly from wheel paths/urls, and when wheel downloads did occur using requirement specifiers, dependencies weren't - downloaded (PR #1527) + downloaded (:pull:`1527`) -* Fixed #1320. ``pip wheel`` was not downloading wheels that already existed (PR - #1524) +* Fixed :issue:`1320`. ``pip wheel`` was not downloading wheels that already existed (PR + :issue:`1524`) -* Fixed #1111. ``pip install --download`` was failing using local - ``--find-links`` (PR #1524) +* Fixed :issue:`1111`. ``pip install --download`` was failing using local + ``--find-links`` (:pull:`1524`) -* Workaround for Python bug http://bugs.python.org/issue20053 (PR #1544) +* Workaround for Python bug http://bugs.python.org/issue20053 (:pull:`1544`) -* Don't pass a unicode __file__ to setup.py on Python 2.x (PR #1583) +* Don't pass a unicode __file__ to setup.py on Python 2.x (:pull:`1583`) -* Verify that the Wheel version is compatible with this pip (PR #1569) +* Verify that the Wheel version is compatible with this pip (:pull:`1569`) **1.5.2 (2014-01-26)** @@ -66,19 +796,19 @@ * pip now only requires setuptools (any setuptools, not a certain version) when - installing distributions from src (i.e. not from wheel). (Pull #1434). + installing distributions from src (i.e. not from wheel). (:pull:`1434`). -* `get-pip.py` now installs setuptools, when it's not already installed (Pull - #1475) +* `get-pip.py` now installs setuptools, when it's not already installed + (:pull:`1475`) -* Don't decode downloaded files that have a ``Content-Encoding`` header. (Pull - #1435) +* Don't decode downloaded files that have a ``Content-Encoding`` header. + (:pull:`1435`) -* Fix to correctly parse wheel filenames with single digit versions. (Pull - #1445) +* Fix to correctly parse wheel filenames with single digit versions. + (:pull:`1445`) -* If `--allow-unverified` is used assume it also means `--allow-external`. (Pull - #1457) +* If `--allow-unverified` is used assume it also means `--allow-external`. + (:pull:`1457`) **1.5 (2014-01-01)** @@ -87,75 +817,75 @@ * **BACKWARD INCOMPATIBLE** pip no longer supports the ``--use-mirrors``, ``-M``, and ``--mirrors`` flags. The mirroring support has been removed. In order to use a mirror specify it as the primary index with ``-i`` or - ``--index-url``, or as an additional index with ``--extra-index-url``. (Pull #1098, CVE-2013-5123) + ``--index-url``, or as an additional index with ``--extra-index-url``. (:pull:`1098`, CVE-2013-5123) * **BACKWARD INCOMPATIBLE** pip no longer will scrape insecure external urls by default nor will it install externally hosted files by default. Users may opt into installing externally hosted or insecure files or urls using - ``--allow-external PROJECT`` and ``--allow-unverified PROJECT``. (Pull #1055) + ``--allow-external PROJECT`` and ``--allow-unverified PROJECT``. (:pull:`1055`) * **BACKWARD INCOMPATIBLE** pip no longer respects dependency links by default. Users may opt into respecting them again using ``--process-dependency-links``. * **DEPRECATION** ``pip install --no-install`` and ``pip install - --no-download`` are now formally deprecated. See Issue #906 for discussion on + --no-download`` are now formally deprecated. See :issue:`906` for discussion on possible alternatives, or lack thereof, in future releases. * **DEPRECATION** ``pip zip`` and ``pip unzip`` are now formally deprecated. -* pip will now install Mac OSX platform wheels from PyPI. (Pull #1278) +* pip will now install Mac OSX platform wheels from PyPI. (:pull:`1278`) * pip now generates the appropriate platform-specific console scripts when - installing wheels. (Pull #1251) + installing wheels. (:pull:`1251`) * Pip now confirms a wheel is supported when installing directly from a path or - url. (Pull #1315) + url. (:pull:`1315`) -* Fixed #1097, ``--ignore-installed`` now behaves again as designed, after it was - unintentionally broke in v0.8.3 when fixing Issue #14 (Pull #1352). +* Fixed :issue:`1097`, ``--ignore-installed`` now behaves again as designed, after it was + unintentionally broke in v0.8.3 when fixing :issue:`14` (:pull:`1352`). * Fixed a bug where global scripts were being removed when uninstalling --user - installed packages (Pull #1353). + installed packages (:pull:`1353`). -* Fixed #1163, --user wasn't being respected when installing scripts from wheels (Pull #1176). +* Fixed :issue:`1163`, --user wasn't being respected when installing scripts from wheels (:pull:`1176`). -* Fixed #1150, we now assume '_' means '-' in versions from wheel filenames (Pull #1158). +* Fixed :issue:`1150`, we now assume '_' means '-' in versions from wheel filenames (:pull:`1158`). -* Fixed #219, error when using --log with a failed install (Pull #1205). +* Fixed :issue:`219`, error when using --log with a failed install (:pull:`1205`). -* Fixed #1131, logging was buffered and choppy in Python 3. +* Fixed :issue:`1131`, logging was buffered and choppy in Python 3. -* Fixed #70, --timeout was being ignored (Pull #1202). +* Fixed :issue:`70`, --timeout was being ignored (:pull:`1202`). -* Fixed #772, error when setting PIP_EXISTS_ACTION (Pull #1201). +* Fixed :issue:`772`, error when setting PIP_EXISTS_ACTION (:pull:`1201`). * Added colors to the logging output in order to draw attention to important - warnings and errors. (Pull #1109) + warnings and errors. (:pull:`1109`) -* Added warnings when using an insecure index, find-link, or dependency link. (Pull #1121) +* Added warnings when using an insecure index, find-link, or dependency link. (:pull:`1121`) * Added support for installing packages from a subdirectory using the ``subdirectory`` - editable option. ( Pull #1082 ) + editable option. ( :pull:`1082` ) -* Fixed #1192. "TypeError: bad operand type for unary" in some cases when - installing wheels using --find-links (Pull #1218). +* Fixed :issue:`1192`. "TypeError: bad operand type for unary" in some cases when + installing wheels using --find-links (:pull:`1218`). -* Fixed #1133 and #317. Archive contents are now written based on system +* Fixed :issue:`1133` and :issue:`317`. Archive contents are now written based on system defaults and umask (i.e. permissions are not preserved), except that regular files with any execute permissions have the equivalent of "chmod +x" applied - after being written (Pull #1146). + after being written (:pull:`1146`). * PreviousBuildDirError now returns a non-zero exit code and prevents the - previous build dir from being cleaned in all cases (Pull #1162). + previous build dir from being cleaned in all cases (:pull:`1162`). * Renamed --allow-insecure to --allow-unverified, however the old name will - continue to work for a period of time (Pull #1257). + continue to work for a period of time (:pull:`1257`). -* Fixed #1006, error when installing local projects with symlinks in - Python 3. (Pull #1311) +* Fixed :issue:`1006`, error when installing local projects with symlinks in + Python 3. (:pull:`1311`) -* The previously hidden ``--log-file`` otion, is now shown as a general option. - (Pull #1316) +* The previously hidden ``--log-file`` option, is now shown as a general option. + (:pull:`1316`) **1.4.1 (2013-08-07)** @@ -163,12 +893,12 @@ * **New Signing Key** Release 1.4.1 is using a different key than normal with fingerprint: 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA -* Fixed issues with installing from pybundle files (Pull #1116). -* Fixed error when sysconfig module throws an exception (Pull #1095). -* Don't ignore already installed pre-releases (Pull #1076). -* Fixes related to upgrading setuptools (Pull #1092). -* Fixes so that --download works with wheel archives (Pull #1113). -* Fixes related to recognizing and cleaning global build dirs (Pull #1080). +* Fixed issues with installing from pybundle files (:pull:`1116`). +* Fixed error when sysconfig module throws an exception (:pull:`1095`). +* Don't ignore already installed pre-releases (:pull:`1076`). +* Fixes related to upgrading setuptools (:pull:`1092`). +* Fixes so that --download works with wheel archives (:pull:`1113`). +* Fixes related to recognizing and cleaning global build dirs (:pull:`1080`). **1.4 (2013-07-23)** @@ -176,102 +906,102 @@ * **BACKWARD INCOMPATIBLE** pip now only installs stable versions by default, and offers a new ``--pre`` option to also find pre-release and development - versions. (Pull #834) + versions. (:pull:`834`) * **BACKWARD INCOMPATIBLE** Dropped support for Python 2.5. The minimum supported Python version for pip 1.4 is Python 2.6. * Added support for installing and building wheel archives. Thanks Daniel Holth, Marcus Smith, Paul Moore, and Michele Lacchia - (Pull #845) + (:pull:`845`) * Applied security patch to pip's ssl support related to certificate DNS wildcard matching (http://bugs.python.org/issue17980). * To satisfy pip's setuptools requirement, pip now recommends setuptools>=0.8, not distribute. setuptools and distribute are now merged into one project - called 'setuptools'. (Pull #1003) + called 'setuptools'. (:pull:`1003`) * pip will now warn when installing a file that is either hosted externally to the index or cannot be verified with a hash. In the future pip will default to not installing them and will require the flags --allow-external NAME, and - --allow-insecure NAME respectively. (Pull #985) + --allow-insecure NAME respectively. (:pull:`985`) * If an already-downloaded or cached file has a bad hash, re-download it rather - than erroring out. (Issue #963). + than erroring out. (:issue:`963`). * ``pip bundle`` and support for installing from pybundle files is now considered deprecated and will be removed in pip v1.5. -* Fixed a number of issues (#413, #709, #634, #602, and #939) related to - cleaning up and not reusing build directories. (Pull #865, #948) +* Fixed a number of issues (:issue:`413`, :issue:`709`, :issue:`634`, :issue:`602`, and :issue:`939`) related to + cleaning up and not reusing build directories. (:pull:`865`, :issue:`948`) -* Added a User Agent so that pip is identifiable in logs. (Pull #901) +* Added a User Agent so that pip is identifiable in logs. (:pull:`901`) * Added ssl and --user support to get-pip.py. Thanks Gabriel de Perthuis. - (Pull #895) + (:pull:`895`) -* Fixed the proxy support, which was broken in pip 1.3.x (Pull #840) +* Fixed the proxy support, which was broken in pip 1.3.x (:pull:`840`) -* Fixed issue #32 - pip fails when server does not send content-type header. - Thanks Hugo Lopes Tavares and Kelsey Hightower (Pull #872). +* Fixed :issue:`32` - pip fails when server does not send content-type header. + Thanks Hugo Lopes Tavares and Kelsey Hightower (:pull:`872`). -* "Vendorized" distlib as pip.vendor.distlib (https://distlib.readthedocs.org/). +* "Vendorized" distlib as pip.vendor.distlib (https://distlib.readthedocs.io/). -* Fixed git VCS backend with git 1.8.3. (Pull #967) +* Fixed git VCS backend with git 1.8.3. (:pull:`967`) **1.3.1 (2013-03-08)** * Fixed a major backward incompatible change of parsing URLs to externally - hosted packages that got accidentily included in 1.3. + hosted packages that got accidentally included in 1.3. **1.3 (2013-03-07)** * SSL Cert Verification; Make https the default for PyPI access. - Thanks James Cleveland, Giovanni Bajo, Marcus Smith and many others (Pull #791, CVE-2013-1629). + Thanks James Cleveland, Giovanni Bajo, Marcus Smith and many others (:pull:`791`, CVE-2013-1629). * Added "pip list" for listing installed packages and the latest version - available. Thanks Rafael Caricio, Miguel Araujo, Dmitry Gladkov (Pull #752) + available. Thanks Rafael Caricio, Miguel Araujo, Dmitry Gladkov (:pull:`752`) * Fixed security issues with pip's use of temp build directories. - Thanks David (d1b) and Thomas Guttler. (Pull #780, CVE-2013-1888) + Thanks David (d1b) and Thomas Guttler. (:pull:`780`, CVE-2013-1888) -* Improvements to sphinx docs and cli help. (Pull #773) +* Improvements to sphinx docs and cli help. (:pull:`773`) -* Fixed issue #707, dealing with OS X temp dir handling, which was causing - global NumPy installs to fail. (Pull #768) +* Fixed :issue:`707`, dealing with OS X temp dir handling, which was causing + global NumPy installs to fail. (:pull:`768`) * Split help output into general vs command-specific option groups. - Thanks Georgi Valkov. (Pull #744; Pull #721 contains preceding refactor) + Thanks Georgi Valkov. (:pull:`744`; :pull:`721` contains preceding refactor) * Fixed dependency resolution when installing from archives with uppercase - project names. (Pull #724) + project names. (:pull:`724`) * Fixed problem where re-installs always occurred when using file:// find-links. - (Pulls #683/#702) + (Pulls :issue:`683`/:issue:`702`) * "pip install -v" now shows the full download url, not just the archive name. - Thanks Marc Abramowitz (Pull #687) + Thanks Marc Abramowitz (:pull:`687`) -* Fix to prevent unnecessary PyPI redirects. Thanks Alex Gronholm (Pull #695) +* Fix to prevent unnecessary PyPI redirects. Thanks Alex Gronholm (:pull:`695`) -* Fixed issue #670 - install failure under Python 3 when the same version - of a package is found under 2 different URLs. Thanks Paul Moore (Pull #671) +* Fixed :issue:`670` - install failure under Python 3 when the same version + of a package is found under 2 different URLs. Thanks Paul Moore (:pull:`671`) -* Fix git submodule recursive updates. Thanks Roey Berman. (Pulls #674) +* Fix git submodule recursive updates. Thanks Roey Berman. (Pulls :issue:`674`) * Explicitly ignore rel='download' links while looking for html pages. - Thanks Maxime R. (Pull #677) + Thanks Maxime R. (:pull:`677`) * --user/--upgrade install options now work together. Thanks 'eevee' for - discovering the problem. (Pull #705) + discovering the problem. (:pull:`705`) * Added check in ``install --download`` to prevent re-downloading if the target - file already exists. Thanks Andrey Bulgakov. (Pull #669) + file already exists. Thanks Andrey Bulgakov. (:pull:`669`) * Added support for bare paths (including relative paths) as argument to `--find-links`. Thanks Paul Moore for draft patch. @@ -279,11 +1009,11 @@ * Added support for --no-index in requirements files. * Added "pip show" command to get information about an installed package. - Fixes #131. Thanks Kelsey Hightower and Rafael Caricio. + Fixes :issue:`131`. Thanks Kelsey Hightower and Rafael Caricio. * Added `--root` option for "pip install" to specify root directory. Behaves like the same option in distutils but also plays nice with pip's egg-info. - Thanks Przemek Wrzos. (Issue #253 / Pull #693) + Thanks Przemek Wrzos. (:issue:`253` / :pull:`693`) **1.2.1 (2012-09-06)** @@ -300,63 +1030,63 @@ * **Dropped support for Python 2.4** The minimum supported Python version is now Python 2.5. -* Fixed issue #605 - pypi mirror support broken on some DNS responses. Thanks +* Fixed :issue:`605` - pypi mirror support broken on some DNS responses. Thanks philwhin. -* Fixed issue #355 - pip uninstall removes files it didn't install. Thanks +* Fixed :issue:`355` - pip uninstall removes files it didn't install. Thanks pjdelport. -* Fixed issues #493, #494, #440, and #573 related to improving support for the +* Fixed issues :issue:`493`, :issue:`494`, :issue:`440`, and :issue:`573` related to improving support for the user installation scheme. Thanks Marcus Smith. * Write failure log to temp file if default location is not writable. Thanks andreigc. -* Pull in submodules for git editable checkouts. Fixes #289 and #421. Thanks +* Pull in submodules for git editable checkouts. Fixes :issue:`289` and :issue:`421`. Thanks Hsiaoming Yang and Markus Hametner. * Use a temporary directory as the default build location outside of a - virtualenv. Fixes issues #339 and #381. Thanks Ben Rosser. + virtualenv. Fixes issues :issue:`339` and :issue:`381`. Thanks Ben Rosser. * Added support for specifying extras with local editables. Thanks Nick Stenning. * Added ``--egg`` flag to request egg-style rather than flat installation. Refs - issue #3. Thanks Kamal Bin Mustafa. + :issue:`3`. Thanks Kamal Bin Mustafa. -* Fixed issue #510 - prevent e.g. ``gmpy2-2.0.tar.gz`` from matching a request +* Fixed :issue:`510` - prevent e.g. ``gmpy2-2.0.tar.gz`` from matching a request to ``pip install gmpy``; sdist filename must begin with full project name followed by a dash. Thanks casevh for the report. -* Fixed issue #504 - allow package URLS to have querystrings. Thanks W. +* Fixed :issue:`504` - allow package URLS to have querystrings. Thanks W. Trevor King. -* Fixed issue #58 - pip freeze now falls back to non-editable format rather +* Fixed :issue:`58` - pip freeze now falls back to non-editable format rather than blowing up if it can't determine the origin repository of an editable. Thanks Rory McCann. * Added a `__main__.py` file to enable `python -m pip` on Python versions that support it. Thanks Alexey Luchko. -* Fixed issue #487 - upgrade from VCS url of project that does exist on +* Fixed :issue:`487` - upgrade from VCS url of project that does exist on index. Thanks Andrew Knapp for the report. -* Fixed issue #486 - fix upgrade from VCS url of project with no distribution +* Fixed :issue:`486` - fix upgrade from VCS url of project with no distribution on index. Thanks Andrew Knapp for the report. -* Fixed issue #427 - clearer error message on a malformed VCS url. Thanks +* Fixed :issue:`427` - clearer error message on a malformed VCS url. Thanks Thomas Fenzl. * Added support for using any of the built in guaranteed algorithms in ``hashlib`` as a checksum hash. -* Fixed issue #321 - Raise an exception if current working directory can't be +* Fixed :issue:`321` - Raise an exception if current working directory can't be found or accessed. -* Fixed issue #82 - Removed special casing of the user directory and use the +* Fixed :issue:`82` - Removed special casing of the user directory and use the Python default instead. -* Fixed #436 - Only warn about version conflicts if there is actually one. +* Fixed :issue:`436` - Only warn about version conflicts if there is actually one. This re-enables using ``==dev`` in requirements files. * Moved tests to be run on Travis CI: http://travis-ci.org/pypa/pip @@ -367,7 +1097,7 @@ **1.1 (2012-02-16)** -* Fixed issue #326 - don't crash when a package's setup.py emits UTF-8 and +* Fixed :issue:`326` - don't crash when a package's setup.py emits UTF-8 and then fails. Thanks Marc Abramowitz. * Added ``--target`` option for installing directly to arbitrary directory. @@ -376,37 +1106,37 @@ * Added support for authentication with Subversion repositories. Thanks Qiangning Hong. -* Fixed issue #315 - ``--download`` now downloads dependencies as well. +* Fixed :issue:`315` - ``--download`` now downloads dependencies as well. Thanks Qiangning Hong. * Errors from subprocesses will display the current working directory. Thanks Antti Kaihola. -* Fixed issue #369 - compatibility with Subversion 1.7. Thanks Qiangning +* Fixed :issue:`369` - compatibility with Subversion 1.7. Thanks Qiangning Hong. Note that setuptools remains incompatible with Subversion 1.7; to get the benefits of pip's support you must use Distribute rather than setuptools. -* Fixed issue #57 - ignore py2app-generated OS X mpkg zip files in finder. +* Fixed :issue:`57` - ignore py2app-generated OS X mpkg zip files in finder. Thanks Rene Dudfield. -* Fixed issue #182 - log to ~/Library/Logs/ by default on OS X framework +* Fixed :issue:`182` - log to ~/Library/Logs/ by default on OS X framework installs. Thanks Dan Callahan for report and patch. -* Fixed issue #310 - understand version tags without minor version ("py3") +* Fixed :issue:`310` - understand version tags without minor version ("py3") in sdist filenames. Thanks Stuart Andrews for report and Olivier Girardot for patch. -* Fixed issue #7 - Pip now supports optionally installing setuptools +* Fixed :issue:`7` - Pip now supports optionally installing setuptools "extras" dependencies; e.g. "pip install Paste[openid]". Thanks Matt Maker and Olivier Girardot. -* Fixed issue #391 - freeze no longer borks on requirements files with +* Fixed :issue:`391` - freeze no longer borks on requirements files with --index-url or --find-links. Thanks Herbert Pfennig. -* Fixed issue #288 - handle symlinks properly. Thanks lebedov for the patch. +* Fixed :issue:`288` - handle symlinks properly. Thanks lebedov for the patch. -* Fixed issue #49 - pip install -U no longer reinstalls the same versions of +* Fixed :issue:`49` - pip install -U no longer reinstalls the same versions of packages. Thanks iguananaut for the pull request. * Removed ``-E``/``--environment`` option and ``PIP_RESPECT_VIRTUALENV``; @@ -415,11 +1145,11 @@ path/to/venv install Foo`` with ``virtualenv path/to/venv && path/to/venv/pip install Foo``. -* Fixed issue #366 - pip throws IndexError when it calls `scraped_rel_links` +* Fixed :issue:`366` - pip throws IndexError when it calls `scraped_rel_links` -* Fixed issue #22 - pip search should set and return a userful shell status code +* Fixed :issue:`22` - pip search should set and return a useful shell status code -* Fixed issue #351 and #365 - added global ``--exists-action`` command line +* Fixed :issue:`351` and :issue:`365` - added global ``--exists-action`` command line option to easier script file exists conflicts, e.g. from editable requirements from VCS that have a changed repo URL. @@ -428,23 +1158,23 @@ * Fixed docs issues. -* Fixed issue #295 - Reinstall a package when using the ``install -I`` option -* Fixed issue #283 - Finds a Git tag pointing to same commit as origin/master -* Fixed issue #279 - Use absolute path for path to docs in setup.py -* Fixed issue #314 - Correctly handle exceptions on Python3. -* Fixed issue #320 - Correctly parse ``--editable`` lines in requirements files +* Fixed :issue:`295` - Reinstall a package when using the ``install -I`` option +* Fixed :issue:`283` - Finds a Git tag pointing to same commit as origin/master +* Fixed :issue:`279` - Use absolute path for path to docs in setup.py +* Fixed :issue:`314` - Correctly handle exceptions on Python3. +* Fixed :issue:`320` - Correctly parse ``--editable`` lines in requirements files **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 setup.py clean. +* 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 setup.py clean. **1.0 (2011-04-04)** @@ -483,15 +1213,15 @@ * Transferred primary maintenance from Ian to Jannis Leidel, Carl Meyer, Brian Rosner -* Fixed issue #14 - No uninstall-on-upgrade with URL package. Thanks Oliver Tonnhofer +* Fixed :issue:`14` - No uninstall-on-upgrade with URL package. Thanks Oliver Tonnhofer -* Fixed issue #163 - Egg name not properly resolved. Thanks Igor Sobreira +* Fixed :issue:`163` - Egg name not properly resolved. Thanks Igor Sobreira -* Fixed issue #178 - Non-alphabetical installation of requirements. Thanks Igor Sobreira +* Fixed :issue:`178` - Non-alphabetical installation of requirements. Thanks Igor Sobreira -* Fixed issue #199 - Documentation mentions --index instead of --index-url. Thanks Kelsey Hightower +* Fixed :issue:`199` - Documentation mentions --index instead of --index-url. Thanks Kelsey Hightower -* Fixed issue #204 - rmtree undefined in mercurial.py. Thanks Kelsey Hightower +* Fixed :issue:`204` - rmtree undefined in mercurial.py. Thanks Kelsey Hightower * Fixed bug in Git vcs backend that would break during reinstallation. @@ -505,15 +1235,15 @@ * Avoid redundant unpacking of bundles (from pwaller) -* Fixed issue #32, #150, #161 - Fixed checking out the correct +* Fixed :issue:`32`, :issue:`150`, :issue:`161` - Fixed checking out the correct tag/branch/commit when updating an editable Git requirement. -* Fixed issue #49 - Added ability to install version control requirements +* Fixed :issue:`49` - Added ability to install version control requirements without making them editable, e.g.:: pip install git+https://github.com/pypa/pip/ -* Fixed issue #175 - Correctly locate build and source directory on Mac OS X. +* Fixed :issue:`175` - Correctly locate build and source directory on Mac OS X. * Added ``git+https://`` scheme to Git VCS backend. @@ -528,13 +1258,13 @@ defined in `PEP 381 `_, from Jannis Leidel. -* Fixed issue #138 - Git revisions ignored. Thanks John-Scott Atlakson. +* Fixed :issue:`138` - Git revisions ignored. Thanks John-Scott Atlakson. -* Fixed issue #95 - Initial editable install of github package from a tag fails. Thanks John-Scott Atlakson. +* Fixed :issue:`95` - Initial editable install of github package from a tag fails. Thanks John-Scott Atlakson. -* Fixed issue #107 - Can't install if a directory in cwd has the same name as the package you're installing. +* Fixed :issue:`107` - Can't install if a directory in cwd has the same name as the package you're installing. -* Fixed issue #39 - --install-option="--prefix=~/.local" ignored with -e. +* Fixed :issue:`39` - --install-option="--prefix=~/.local" ignored with -e. Thanks Ronny Pfannschmidt and Wil Tan. @@ -603,7 +1333,7 @@ override this location with the ``$PIP_LOG_FILE`` environment variable. For a complete (appended) logfile use the separate ``'--log'`` command line option. -* Fixed an issue with Git that left an editable packge as a checkout of a +* Fixed an issue with Git that left an editable package as a checkout of a remote branch, even if the default behaviour would have been fine, too. * Fixed installing from a Git tag with older versions of Git. * Expand "~" in logfile and download cache paths. diff --git a/LICENSE.txt b/LICENSE.txt index 40d8cdaa302..f63eac3d667 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2008-2014 The pip developers (see AUTHORS.txt file) +Copyright (c) 2008-2016 The pip developers (see AUTHORS.txt file) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/MANIFEST.in b/MANIFEST.in index 7f84811a9dc..3dac128eab5 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,9 +1,25 @@ include AUTHORS.txt include LICENSE.txt include CHANGES.txt -include PROJECT.txt -include pip/cacert.pem -recursive-include docs *.rst -recursive-include docs *.html -recursive-exclude docs/_build *.rst -prune docs/_build/_sources +include README.rst +include pip/_vendor/README.rst +include pip/_vendor/vendor.txt + +exclude .coveragerc +exclude .mailmap +exclude .travis.yml +exclude .landscape.yml +exclude pip/_vendor/Makefile +exclude tox.ini +exclude dev-requirements.txt +exclude appveyor.yml + +recursive-include pip/_vendor *.pem +recursive-include docs Makefile *.rst *.py *.bat + +prune .github +prune .travis +prune docs/_build +prune contrib +prune tasks +prune tests diff --git a/PROJECT.txt b/PROJECT.txt deleted file mode 100644 index 60f6c1ede4c..00000000000 --- a/PROJECT.txt +++ /dev/null @@ -1,11 +0,0 @@ -Project Info -============ - -* Project Page: https://github.com/pypa/pip -* Install howto: https://pip.pypa.io/en/latest/installing.html -* Changelog: https://pip.pypa.io/en/latest/news.html -* Bug Tracking: https://github.com/pypa/pip/issues -* Mailing list: http://groups.google.com/group/python-virtualenv -* Docs: https://pip.pypa.io/ -* User IRC: #pypa on Freenode. -* Dev IRC: #pypa-dev on Freenode. diff --git a/README.rst b/README.rst index 5707824d6f4..a10c06dd221 100644 --- a/README.rst +++ b/README.rst @@ -1,10 +1,34 @@ pip === -.. image:: https://pypip.in/v/pip/badge.png - :target: https://pypi.python.org/pypi/pip +The `PyPA recommended +`_ +tool for installing Python packages. -.. image:: https://secure.travis-ci.org/pypa/pip.png?branch=develop +* `Installation `_ +* `Documentation `_ +* `Changelog `_ +* `Github Page `_ +* `Issue Tracking `_ +* `User mailing list `_ +* `Dev mailing list `_ +* User IRC: #pypa on Freenode. +* Dev IRC: #pypa-dev on Freenode. + + +.. image:: https://img.shields.io/pypi/v/pip.svg + :target: https://pypi.python.org/pypi/pip + +.. image:: https://img.shields.io/travis/pypa/pip/master.svg :target: http://travis-ci.org/pypa/pip -For documentation, see https://pip.pypa.io/ +.. image:: https://readthedocs.org/projects/pip/badge/?version=stable + :target: https://pip.pypa.io/en/stable + +Code of Conduct +--------------- + +Everyone interacting in the pip project's codebases, issue trackers, chat +rooms, and mailing lists is expected to follow the `PyPA Code of Conduct`_. + +.. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/ diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000000..c87d253c2cd --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,16 @@ +environment: + matrix: + - PYTHON: "C:\\Python27" + - PYTHON: "C:\\Python33" + - PYTHON: "C:\\Python34" + - PYTHON: "C:\\Python35" + - PYTHON: "C:\\Python27-x64" + - PYTHON: "C:\\Python33-x64" + - PYTHON: "C:\\Python34-x64" + - PYTHON: "C:\\Python35-x64" + +install: + cmd: "%PYTHON%\\python.exe -m pip install tox" +build: off +test_script: + - "%PYTHON%\\Scripts\\tox.exe -e py -- -m unit -n 8" diff --git a/contrib/build-installer b/contrib/build-installer deleted file mode 100755 index 3c1acb1f67d..00000000000 --- a/contrib/build-installer +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env python -import base64 -import os -import sys -import zipfile - -WRAPPER_SCRIPT = b""" -#!/usr/bin/env python -# -# Hi There! -# You may be wondering what this giant blob of binary data here is, you might -# even be worried that we're up to something nefarious (good for you for being -# paranoid!). This is a base64 encoding of a zip file, this zip file contains -# an entire copy of pip. -# -# Pip is a thing that installs packages, pip itself is a package that someone -# might want to install, especially if they're looking to run this get-pip.py -# script. Pip has a lot of code to deal with the security of installing -# packages, various edge cases on various platforms, and other such sort of -# "tribal knowledge" that has been encoded in it's code base. Because of this -# we basically include an entire copy of pip inside this blob. We do this -# because the alternatives are attempt to implement a "minipip" that probably -# doesn't do things correctly and has weird edge cases, or compress pip itself -# down into a single file. -# -# If you're wondering how this is created, the secret is -# "contrib/build-installer" from the pip repository. - -ZIPFILE = b\"\"\" -{zipfile} -\"\"\" - -import base64 -import os.path -import pkgutil -import shutil -import sys -import tempfile - - -def bootstrap(tmpdir=None): - # Import pip so we can use it to install pip and maybe setuptools too - import pip - - # We always want to install pip - packages = ["pip"] - - # Check if the user has requested us not to install setuptools - if "--no-setuptools" in sys.argv or os.environ.get("PIP_NO_SETUPTOOLS"): - args = [x for x in sys.argv[1:] if x != "--no-setuptools"] - else: - args = sys.argv[1:] - - # We want to see if setuptools is available before attempting to - # install it - try: - import setuptools - except ImportError: - packages += ["setuptools"] - - delete_tmpdir = False - try: - # Create a temporary directory to act as a working directory if we were - # not given one. - if tmpdir is None: - tmpdir = tempfile.mkdtemp() - delete_tmpdir = True - - # We need to extract the SSL certificates from requests so that they - # can be passed to --cert - cert_path = os.path.join(tmpdir, "cacert.pem") - with open(cert_path, "wb") as cert: - cert.write(pkgutil.get_data("pip._vendor.requests", "cacert.pem")) - - # Use an environment variable here so that users can still pass - # --cert via sys.argv - os.environ.setdefault("PIP_CERT", cert_path) - - # Execute the included pip and use it to install the latest pip and - # setuptools from PyPI - sys.exit(pip.main(["install", "--upgrade"] + packages + args)) - finally: - # Remove our temporary directory - if delete_tmpdir and tmpdir: - shutil.rmtree(tmpdir, ignore_errors=True) - - -def main(): - tmpdir = None - try: - # Create a temporary working directory - tmpdir = tempfile.mkdtemp() - - # Unpack the zipfile into the temporary directory - pip_zip = os.path.join(tmpdir, "pip.zip") - with open(pip_zip, "wb") as fp: - fp.write(base64.decodestring(ZIPFILE)) - - # Add the zipfile to sys.path so that we can import it - sys.path = [pip_zip] + sys.path - - # Run the bootstrap - bootstrap(tmpdir=tmpdir) - finally: - # Clean up our temporary working directory - if tmpdir: - shutil.rmtree(tmpdir, ignore_errors=True) - - -if __name__ == "__main__": - main() -""".lstrip() - - -def getmodname(rootpath, path): - parts = path.split(os.sep)[len(rootpath.split(os.sep)):] - return '/'.join(parts) - - -def main(): - sys.stdout.write("Creating pip bootstrapper...") - - here = os.path.dirname(os.path.abspath(__file__)) - toplevel = os.path.dirname(here) - get_pip = os.path.join(here, "get-pip.py") - - # Get all of the files we want to add to the zip file - all_files = [] - for root, dirs, files in os.walk(os.path.join(toplevel, "pip")): - for pyfile in files: - if os.path.splitext(pyfile)[1] in {".py", ".pem", ".cfg", ".exe"}: - all_files.append( - getmodname(toplevel, os.path.join(root, pyfile)) - ) - - with zipfile.ZipFile(get_pip, "w", compression=zipfile.ZIP_DEFLATED) as z: - # Write the pip files to the zip archive - for filename in all_files: - z.write(os.path.join(toplevel, filename), filename) - - # Get the binary data that compromises our zip file - with open(get_pip, "rb") as fp: - data = fp.read() - - # Write out the wrapper script that will take the place of the zip script - # The reason we need to do this instead of just directly executing the - # zip script is that while Python will happily execute a zip script if - # passed it on the file system, it will not however allow this to work if - # passed it via stdin. This means that this wrapper script is required to - # make ``curl https://...../get-pip.py | python`` continue to work. - with open(get_pip, "wb") as fp: - fp.write(WRAPPER_SCRIPT.format(zipfile=base64.encodestring(data))) - - # Ensure the permissions on the newly created file - if hasattr(os, "chmod"): - oldmode = os.stat(get_pip).st_mode & 0o7777 - newmode = (oldmode | 0o555) & 0o7777 - os.chmod(get_pip, newmode) - - sys.stdout.write("done.\n") - - -if __name__ == "__main__": - main() diff --git a/contrib/get-pip.py b/contrib/get-pip.py index 2b733f4b267..9e48c87e8cc 100755 --- a/contrib/get-pip.py +++ b/contrib/get-pip.py @@ -1,17473 +1,12 @@ #!/usr/bin/env python -# -# Hi There! -# You may be wondering what this giant blob of binary data here is, you might -# even be worried that we're up to something nefarious (good for you for being -# paranoid!). This is a base64 encoding of a zip file, this zip file contains -# an entire copy of pip. -# -# Pip is a thing that installs packages, pip itself is a package that someone -# might want to install, especially if they're looking to run this get-pip.py -# script. Pip has a lot of code to deal with the security of installing -# packages, various edge cases on various platforms, and other such sort of -# "tribal knowledge" that has been encoded in it's code base. Because of this -# we basically include an entire copy of pip inside this blob. We do this -# because the alternatives are attempt to implement a "minipip" that probably -# doesn't do things correctly and has weird edge cases, or compress pip itself -# down into a single file. -# -# If you're wondering how this is created, the secret is -# "contrib/build-installer" from the pip repository. - -ZIPFILE = b""" -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 -knQgF5UiAADPjwAAHwAAAHBpcC9fdmVuZG9yL2Rpc3RsaWIvbWV0YWRhdGEucHntPf1328aRv+uv -2Fr1A+iQrKWmd1e2ulax6VQXW/aT5MSposIQsZJQkQADgJLVxP/7zcx+f4CU7Pa113d6bSwBu7Oz -s7Oz87nYZqMnIzari7K6nLBVdzH6L3yytb21zZ7Vy7umvLzqWPpswHaf7uyykyvO3tx1V3XFjuuL -7jZvOHtRr6oi78q6GkOnY87Zy4Nn08Pj6bj70LG8Ktiz14cnRwdfvT15fXSMD6Hd1qNHjw4Wyzlf -8Kqjzqy+YB2Af8W7HMDl7KJu1FjLfHadX/KWvZm+acdbW8er5bJuupbl8zlbWB0WOTxMd8ZPh2xn -vIP/2R0SCrvjp4x/WPKmpAHngzFisHXR1AuWZRerbtXwLGPlAuGyVVUCSXg2Lzve5PN2a0u+wKez -VnTji7ycqx4L3raAYYZvsotyzlWPv7Z1pX6f15eXQGf1Z8O3tgSosQLzvGy7eXk+/TDjSyTKEJC7 -4U0Lv2aZbDurF8u8Uz2OuwZAHrwespZ+y7q7JW+HrOMfOvpd9lrkzTUAUt3KCma2bHgnX686Mxfo -2eSzLju/y6753ZBd8i6jZ3LiY4mRao/v29kVLOUQF+jL3f/Ivp0eHR+8PsyOpltbOGvesD01/TG0 -f0nP0iyr8gXQfQCEmM3zttWr/6psW2g7bZq6SX2qDCZbDH5gAfeBiD+uyoYXhg3Kli1Eb1xhH/Cz -urqYl7NuI+Su44tlx7oahsgLBsx42wA7WOxW8nnRAs/CYuA+mEnIq3w+jo38tmr4rL6syr/x4ltB -wU1IvK2uq/q2MmMqylerxTlvosMcVDf5vCw2U84AzecrjmQrRVeCus2Wq3OYDtt/cyC2JtK1LlbA -2lkG+w52yx47TdSwyZAlb775Ojs4fPE6mx4+e/384PBr5+Gbo+mL6dHR9LnijuQMh5lWQvqwVQur -iJsepQD0GmEvhnup3Qogw9gJSauEUG34BW+QCySBxuxP9ZJfrObzO3ZbgpA4h/W5yqtLXkBzWFMQ -DKyuZhwZlv36y9/g9FshVQAKBzB3t1ccdmg/+ogCCBlAIHt5cDilBgfv4GnDaY8C4mnyQ8XEzw8/ -J4OtbPfLnezFwfTl82Nol2rijSQ/IL0OYUfgv9ajN/O8Q+GWDLeY+5Mcrxaws++w1XPezpqSljnS -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 -vc2MPKPnGONHr7ORovQrU8U6+o7Pw7YuOEEtS82jovEy8mDSJ6x9gMVoSG6YSujSjhTCWSr/cp2e -lpZVfdPLWKcU4y7uBnl5esaC+pa+llx+rn1Y2llLAs6+KpVqmJovBFZYR263fCh4v1/CNpaLwgvq -4kF7Sq8PYVs27P//vLuhsExu9szyolb9A/Tlm4opbtwaFrrTO8z6oEohxhj+AFBLAwQUAAAACAAA -cLBEr4ovZc4BAADLAwAAJQAAAHBpcC9fdmVuZG9yL2Rpc3RsaWIvX2JhY2twb3J0L21pc2MucHl9 -U8Fu2zAMvesriASFnS0xtp6GAjmsQQYEGNKhSU/DIMgynWhTJEOit/jvJ9lR66bYdBFN8j09PslT -WLxbgLSVMoc7aKlefIoZNmVTWNmmc+pwJMhXM7j98PEW9keEbx0drYGdremPcAhfbGsqQcqaIoB2 -iPB1s1pvd+uCzgTCVLB62O4fN/dP+4fHXUyGPjaZTO6F/NVYRx5q60CZSv1WVSs0SC28R99j69bI -yO2LgGBMnSICrE+R7zxjnAutOYclfM+kkEfktbMn7m3rJGZzCEmtRan7uPZowsCY/WCMkevuGIQV -ARA44cL7hobhWWJDsOnra+esG5AV1m+786bjtdI4D+WyPSw573fOZwMoLgz2LId6P2omMwhGZDZ7 -bnFIrTNwIYP3ETNWneYKPClMOrfihCOV/XzSao2DnWnOVYKNZhkyuS1/jtRepCivjCdhZF+fP+Nn -r8y8WBxkWV+kr6TsM5FTZUt4ZWLqy+OsJsgfna7q8cmpYQ5lR+hHfSOlqenFb/0vFk/u/xzFRVl4 -bcUBKaZDSHjq8+HfyWez0Tker9iE8gj7rhlGzid4bsI9DOrjnQcBczCW4MZP4OYV9npRYHlxqOA8 -7uFdsb9QSwMEFAAAAAgAAHCwRDSp0cpIHAAAMmQAACcAAABwaXAvX3ZlbmRvci9kaXN0bGliL19i -YWNrcG9ydC9zaHV0aWwucHnVPGtvG8mR3/UrOhQMDjf0xNYdFnde6A6O10aMJGvDj+xeBIEakk1q -ouEMMw/L9N7+96tnP2aGktYJcIg+2CSnu7qqut5dPafm8TePzapa5+X2menazeP/wF9OTk9OzYtq -f6jz7XVrkhczc/bk6Zn5cG3N20N7XZXmfbVpb7PamldVV66zNq/KFCa9t9b86fWLlz+8f5m2n1uT -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 -P3z4cP7BP19v3r6eu677Gq68/hXBqLcW6m2CNGBn+nUxHbxkGt9gqDCH9fjYNVWTzPaXulzi0Pu0 -hf4FdscNMNptZUxHEubJPbcHJl4NT87OJgozZevwdwB7gJuNPcLdrFcDl8q8fSfjWg3SSn8rPppC -iEkKqJNFfphXR8oX9SLK0a+ittBRggdomK38PCBPngC7AeMtpsJio14ZT+Fas35QL5fljZhPZrOP -AvSteBz2wfnulUzo/GOYHMUD00pEnC08Xjh/kfm9ceR9fqaS3L4m/xTIcHAdR0t1/0Lv5MiPnd+9 -6DeCh2XEebdYtXy5nO7ZO5o/0gf11lA5//2XXzj3ytTHd6epItTrRrWib4UsKAzXLOiU3jfu47oT -FXX2XlduTTF7aj8dXAzqFN6yqGCYTl5OZup3Hu6zOJD+Xp70sO/8l5rgU7LluaikG6fPI6UcPbrJ -yJLNG3si2cNsa5W3f1Km2vrHLXrsa2GHvAs7doff6Rz/8mr8oZQxvbMkd78sw5SRj6FpJjKQPgvd -Eb8OL4tDm4ZzcgDQjoxibC2MLXzh1zA98obcbEGtv7W025t329r/PYy/aZ8+CcWn79cxifeRf7Vn -yqb2U95RosPHncvE/Cpiv9zbrxMOcNgWxu9xrp9JnSP69CsR5eEedPpHO1bFhqd+WfHs3t3+2Y7K -rG1X7TPbTnLt34Hjj1ZRjOolUibH0+dhteU5fqt3dLmsz1viR1dhF5vJd5+54ffq1zjqt4C7yDyB -u30gAFifguNOENYr9dOpwyoOvxxa0e8IlYQ69PB7n9nJ/wJQSwMEFAAAAAgAAHCwRJhwNWiPHQAA -rHcAACMAAABwaXAvX3ZlbmRvci9odG1sNWxpYi9pbnB1dHN0cmVhbS5wee09a3PbyJHf9StgqrZE -xiRDyW+V6ZT1YM513rVrbd/lStapQHAoYQUCDB56bJL/ft09D/Q8QNHeTd1VXVhZRxzM9PT09Ht6 -wGVZrKKLi2VTN6W4uIjS1boo6yieV0XW1OJCfh9Gi/QmrdIiH0ZNnibFQlxkaS3KOKt2lghina7H -FzciXxTluErvNJxa3NUX9f1a7OyoFhybVPpbCQ9o/Dgp8qqO87rSQ08/zIZRtY4TcXwVl3ECs1XD -KK6SNH0vavbty3otyiSuRBckkcOkaX4JA34W67isxOldItY1LEcN0T2bOoUFyca00K2f6hJGv/uw -s1OX94c7EXycHkf3taiggyC40TtqPS3LopTd1fNoug2sZrkUpVi8+3CEa+oCmWRxVTmd+8X8F5HU -A9kDP2votLOzG/1U5CO1cdENkA6WXkXFMmpptSzKqKlElOZRfSWidSlGRKpyx9kDWgusBND+VeSV -qPtnwAmrMVFZ9Hu0J70BAcQHCNEBcT7Y4fv4PRD5eA3OMMJ3AzQQACThXL3NLzNxBHhfixpBBonx -dz7TvPemN4zmvdc9ALKT5jdxli4utNSUAoCUAph0tU4zwOXsazOZTPZH9H8v6d8j+veUmvZn+O+L -GX15BV9OXk4m8OVkNoMvs5MT/DI7OcUvs9kp/Tv7+gVB0nf9l2w7MG0Hpu2JaXti2p6atqem7Zlp -e2banpu256bthWl7YdpemraXpu2VaXtl2t6atrem7ci0HZm2Y9N2bNpOTNuJaTs1baembWbaZrpt -f6Lb6K/ZeQ92Li/yi/lqfaF3ELdvXaS5ZAPc6ckd0XgYyT9m+MeBbjnQLUTfoZHI7o/sSmOeaihP -dcsz3ULE3w7ccz3muYbyQre80C0vt8fupR7zSkN5pVve6pa322N3pMccaSjHuuVYt5xsj92JHnOq -oZzqlplumW2PnWSJof5rhrJMKuJi3eRJ3cRoPbqk+ZWU5hP892BCXw5Ijp+8pS9PJ/jvsyP68py+ -vJBfXpwS7+1Gx3ECWhgVVALKpvqSg2XqD3baLz+Ly9M7mP1v/9jZ2bGNAdgYEa9sY9Dr9eRjsD4E -tqJOFSj7uI4WRZQXdXQV34hobrqBfQBTkJZRcZvvEJTPV/p5lJJ5zcRK5LVYgPqM4ihLq5qsylWT -X4N9kaYEUGtWZG1pMgL0CwgSTrGK83tEBa1zdJtmWTQXUZUVt1GV5glYoxon+tDP//CHg4FeiMRl -IZbguACY+uKiX4lsOVRrYuYPm8eyFYWW/rAfqtVMo7Nz+8G6qFLCGR6N9oERzqNoV64sypvVXJRD -WOoSFEGLTi2yjFDhFrhAfTEx3+WWIhS0iy0KZ4fWtGeT8/NDi1MR0ONplIm8T+MHO84Te/x+u55S -gHOXY7cW1UqIa0U1aGf4wm4J8EIQ5msF82KuGIusXb+dWK4floegdWNqLff2CuSCkOZrTc8H0Ws1 -3F6lAjmahsZYPVNc8373pqV6f87bVcP+L9Sq57gWtu50SSLAZrQRU0SUBEE4SsokHNNVZACo725l -NPXWM4jifOGoIncHA8OAF88Hg+9ErRIPDJyBKyrVhB7ciprNBA6Xa0jNqn+GGKNX5biA7RrOOViG -b3hjFnEdR1MuymPaRmd1DP44BjcuX/Rx5CDMILgn3exDpCfK2yDUKrHRWQEjXHgVpVjFpPK0b0od -2sc3tgqSC3mXL8Rd5Er2xO32QYthpwaQUsiBvg7yo4vmIxBlm2WUenD6vWES3+IlFids7xT/MiSU -YOpPunTBvpabwKENopG16kNHhCJJ2M/Fz7ArZJw5RK+zpzUYekObvI855HMLki9ZPiIPLeQbUfPA -nfvzs+22eB0/5Y2WEg7lzCLthuUPHD1ibdtoyvvuBDjDcOykfept/2EHwp6es4cNBjuuwM57vTF6 -HP3yBh7uoNT+2+cf37/L102tdU/RlIkYmlzBFCJm+Eoh8I+ijqefywa+o/+1EDV9U9INeF/FVVzX -pQHSQ+R63LiAzyaD7yn8nVLMnQjVX6qzyWDYZkskeW222gjDGqvRMiMsK6dXiI4VWjxcqEPqOK1E -9BlgUb6h3zuOc+wp7tZZmqR1do/xD+iLFtZtWl+BA6hTDNKj6/k7gWRXSPnUD62ajTtK87i837Rp -bL/MVg2Me9wxt+smfyyLm3QhKns56EGSZ6sj/6guyLtFqJ+La5Gnv4Ll0W4y0FZOWsfXACqJIVLg -w1vKKa0Lc6LHXcLf6yxO4IvcRehWloAfSdSoEn9tYCQiB8PirCqitUY3KbJmldODLM1FVGPSAuAo -nIzffAHsHzdZfYxO5CfAGvhpf3LwdNLtVsvdaTcFYL2DDinExZWoDBkYVcftzndJ2pkmAfiCozfA -huUK4P0KsYQiN+XFZG/Le8Ys1fw+uqpX2bMsnbOpZGegdo5hhACeBErH0RKM30huMyjOIokzasrj -lUCKx4pdGSCMcwoKWaCv2SpgLhiCu7dqINCBKfRQGUWl+SJNYm5mkDJ6+DiK3oHfvRZJukzFYijH -MAmSwQ+sbjFk7H8JXJyJirJ1GCwdffgRkc5ixGMhgMtKikXB6WySK4zDMJkVAZ4x8/woTGPiaOQE -rND7orgmwsbRaxz2RvfH7GAtNSvTGvkSd4qSp5wh2i8QvJbxr8CCVcX1DlqsXptj6Q3Qu923NQ+p -dpCAoqxNjo10UKW9iMRu/nL86WkQQmal6SBYBuXlx+osmYZxd0sv155/D2IH34tYn2F2BJj1//QI -W45bXAd/7//p9SOn28DtxPXvbvReBee3IBQiysUtqQngqyRpStsPhofv6Rl4HRPmpZmFnmpumEb9 -XlMvRy97YPUS8AyBXXqOz43u8icTg2NLAXbc0fw7Dp0wwTbggRs2OEGHQgdj6SmwYKBdqbdJ4BFz -QayHQu/q2Tmnngz5kX593KcM1MBAERDkbV2Km7RoKpX5cKIKePhTs9IUnQShSu1d6fQ72I5aqnGZ -hHFmCE5wXGSV7VLtRicCFBgZ5+Ofo/czsg5VA0u8RD0F2ihLgSluhM5JzIsmX8RlKpw1mOjPsC7M -hJ5Du0V8UzvNBhjXRUP2i1RwJLUyV/XbqfPom9R5jzHHbvQ2Q4fr3obENZXrzu1h/z0n6G6zSraF -CugO3VOf/fhMr8NnmaBq7bB2/xVFZT6DIaKZv2Vr8yjX/EbNoFyavO7vfc33htHEQOIpJJoIB5lA -kvPtYwXQjEAOxZaPRTtJuQQT2D0JGgE+ahqN9r0cF6FxLH2Zqc/fj3XSrZveHhCdU4r6fPrH0b4X -2fc5HYYOJKaOrH2xOfxnglTBXAQC5HqgZRj0bIlm1QR1SthVVoNzqRmsaWCzAldh/iJIb8Dy5Owt -1qi5AxhHFKGCLDP3lASyxQ3lCjzPRuAexjdxmsXzTIwjuViL+qcfZmhicvoD3GAYnlyJxbhDGGnu -mMyRZGYzc4o+o54fs3MC9EYFEQDnJ0+fv5k66t/mDZ7lQ6kmJ7g/8IN3RUtYxI4tbXayhTWxbnFp -PT9jvZwssw2Sf4P98/QDQraTmRJ/qR3Mkil2tVObrTWELfEjPm4sJbu5QYJjoLluGIasUCfTIjDX -3P8ONjyYLmw9DxljhxB40EbOS4zuojgpi8oz8JoFfftoE5hjFbClj2VOkY94wOzqbpRvRpZGAPac -u9F/CnmkkxfRqgCnD/tgalKM4D/3OITx2QwiS7bjynmnXGj0xvXZUadi9rIoZcaVMtSu0KlOsFN3 -kxPUJpM7dF0pw0ePXuMjdGN9UeymhJ7OG6LITc8PsYPrXgYceZXrtXQTeeuROgFmwT/r9FNRi+jL -4wl8ULYWwEy15Cal3WudHAiyaEdEMK6aOURLsOblAlxrBzW2PA0AOpdfc/TCv3IHvKOn6ReWQ4sZ -PUls8+KufsKsmG1unFBNaSoa3W40HRMguco4vxR9Oj/wyibG6FvEWSando9AmOuu04Q9BWNkDu57 -g03IHYSR240+FSuB1nGdJtdiQXt6WxYQ/KiATWUK2Ij/KhpwTKtWSqvrFIM8KVd82RBIJ8QpHQvG -Gqu+gxF+UO8ATF9YMGRP80bs2I2KAkpMadbxZVk06/7AllV5XCk7VHVc1v2Bo1Q+wmbX96ghBdIC -/q2uiiZbkI8OGMc5Rv4gC1UD2hM7uJhTldU4rT5pJfsxTuUqz2D6QzrOjA7OA1a5VWlxq6KjNQx/ -5FMCdvgCiArLkRNWfLrPxbEmSmBmD1i6bOHBbnXXhvg442db9vTGSb4hueLt8pSx3dY3U61Q0Xi1 -7VKrzmazwGGj2W0m0eAj7wfI/j3oh89EgoLwrdNYQqxKI7QL1FYKFmvyPsSUJvNcXumlmwSendt1 -3d9mjeneOfijYGpbu5gnWaOSuPfMeQYG2WuB7aG5Az9ybDeqFKIBFutsGwCgzGDVrNFKyfTqXprv -YU7vqljQFlPtJcUQFLyndWuS2klsn5vppy8VClBCJSYFOpJociijjTqHJizFX5sUzG3I6JnKRT6l -jAKt6pSzfmg/2EmVqm38d3HPChsNdTEPvRDz5vLiwmckKqNASrdThEVPHlj2Ue0lWHSwf/DSPby6 -pENWfUZ01vv69e6HycFdL/ohUsMCs/k+DnphepGhgEJN898/VAiZvv8WKrrZzB+q88cGMLfNN04y -Sx4Go05x/cWZ3v0MzJsA/iQ7QKnvUixTG92VRnVMvZiLP/RcdI9Wq3AcItF4R7S8kvVBNUaPQ8X0 -0W1c5XsoN0mMxwC3An2GqGjasiO/vGsXl4TJlWId0uteOPFocwipP3PQDNdbqDxQY2hRx6jOfA2v -VyvdiqsiEyoONitXvpUAOb8nogRgSP1EecIYq66ppitdIKkITmjdiNeWS3XOXmVQ69LtEJ4HLCd+ -AhGbCNqjEFWJQi115GHvQmZNKatikcsZShXNdQR+ozpoiyWD5+KudlKp2y80LPoP5xQo24FYUloh -vPJWbJlKwmNr0y7ZgeUBGpDVmhlAy3n9kGf3Tm4HJA/oUdyiL1vQkVN+WdQ1xrggRlh1N0KaoYFi -gKAfFos3Kyz2u4QtgJYlBpVo+5ZNScnZBAkNQAklO/8Ql90HziEpnHqlJ2pHETSCwrkAFzBTNWqB -UmT3mHMG1PfQrl8WYCpTCMcQn0URgEQh8W1RXpOTSgfmsIvgXOLweSoPS/CxCuSWJZ2+1tl9AJjC -htZJPphR4uNA70/FMPoFHQDQq8huBN9sQ7tV4BEUPHUYmhhp1uGy6XiOsHocyhL73SnI88pFOrw5 -d9NG7jj8qJKhDRJFhW4yucXO6v3z/vAJ/v+fo/vOs/nvqVtRkP91kP+vg/yuk7Kf49tIndyO5I4p -qZJ7gAyTqrJtebWGdD8eB8skquAGJKQ52EkyFpCvQSLWZQoUcfJ18e32B8hhJTF25caC6yfC7DNu -urT2E/BMX5NvYJ13s2WaYe8YfdvHP5kTXyqRI4KhI4seagb7p8vz5cabDcSEIoMS3ERrBXmzomo4 -Yo1p9Gz/YEskmgrhgq6ADbb4RT5QiiQ82bF8SAVFkxBN1ETkMUUFSTVbgZb3JR5C2zOo4wi2J71b -kNLithrtHzw76Nn5fMSd0WhJ0+nqtagH6jWv6K5dBl511rNK4jDszkCsuffS95gC64dVBBPKRDr8 -o5aAaOnWfrBSrV3EMXpSgIsoyRi5+eutyyOs4gt593IMDhT6qapW2V3XoG/Lxjb3ZR747KnU897g -ISllC/lXZcH2lQXqfqmvDL1UjTprltc/5N90a4WlgWVWpmsm+45R5arPjkoGh/nVxZPtnBTMS5RY -DtOaN7Cd7PFnY4XIraIgb40FNGi10cwC5dfIWcyBFkHphM4sRgeruQT/DCOiaavt2cQyMC2FjGxo -qlzIsMogy9V4JQ+G9NwMUkiPO7W6qGvIrBqy2VsUXpEmOfZ30g/28iDwqGH+G8EX+OcGHRZCWm3O -UFUAZClWADyIqxp1+G0TBzm3sxE/JObkoi/EGLwUvOMcZ5IEeLokr1Z/0Q9O1AM/WdNx27pzPpji -t8wnTzUr+/KH/hhwUx9SIJUk83p0Fqw6jRfBDFs7sTmF1Mp+bG5IuUY9nNlRsR2rR5eA9QWY4CCV -MAndsrJQ9FJBDtXsGwzhuQwpliCYnT1NrwSP7QK0ZcJl+oJGAZfkbE8/2/N30CEuad2Jc06yBcet -uQdAage9A7QH0TJOs4qcKlR2yk3yNUxAPr9DJn0VY3ll3H/51MyrOq2bWl5ixdMEUBroypq3MbQI -6CYYhDdae2lVjF6+fPZqtN87dBy9f+yE1jTGdBbIBIZMDFingmR9zlwY554xa68XtFSyDqHyS+GY -tlzc6hb/bqXvde2fYyrWNzEMjHbfKArh4Dk9eH8ghazN3X+Ox/3yr9FcsC+Z6Dm+hz2hKu3tnCHE -SKVdEibrUzjUaWD9k/OHneigsxqsOu4q394oh5Zj/SAubEVBFIgSdI3GfeNIv2fgSM5ZSGPyA4Vh -8myoY6l81weub4Wui1/b97auxWpdE2zZD5O86KjEtTrgxAMDlUpXDiEomJZl2SUf5S4jnHKV5hpz -7WUpaaH0OnnMBNT3uCjuu0XaqBHIRNyRnherkzTBMPJvjo6i8AXmuvjyefbyMNoj/twbbui1//zi -/anqSRy/N/R7HLEec7ER3pODFt6TgxA86HHEegA8A+4fXEP+uZDp98s0z9UJNEUN5pwCBPipc01U -JanCVvvpwFU0/JoYjTRmmXvP5T0L9/Vmymhfpgf4aTg3Kkyhqi0b00kE9To7fHI+iNppgDKjlyw2 -FZiYfmKplqI1Du7J5E/KrVY8jMCeHOgTCPy2/7xL1/uouWg9cW9qEG72vZKN+G1BjAMkxm4IVTbl -gWVBRa1O4jGc0SXDblyDjvZtXMmcybCVLQYIE/NprZPeAYHvSLZJNWlluwlPrN7zIz7jcnTEfBSA -hMql5XuiWH5VJUNhw+f39ICHUMHIe3tnFrHYJCYd3qt8ExJMoFfzkRo8f/IBO8N4REJEFjGewyDo -2nyfHRe+Ee/eMHXmohGRF/z5BXagtby7MMrSa5NakXc+8c1JVZFg0paxKTLLTVzSjRVxV5exKl6R -qkwd6LZ18BUdb0eXQLVa1r/kTOFgfdIlzEXH+GiQtC2FgQSPLO1Cc4Q8JQFjaZK94HlaGQV/36mH -t+36+jC2jgMgtcs46DyecWe2y6IjvAFhjYXVq7GBlytA6461wrva77z2iq/9hsfslA5zI1g/pl8H -4L5bgpyYT3WxfqdLjmzfbh29dg9qnfvDoXWcrQ/XiAZ7Nwcux8VgN/p4f0B1lnEdAjM2VOCXM9RV -qYcp879BBG9v1vTfiG3JZjqBOv9oXwvSwOySf2ei71udj60mncHn0sHnn4BFEI5Lb84V3gZ3vvuk -vcPGF1kWa1AQ9322tiEn/MBa/rE8nEe9uUF05am3nsV+2w/b4qQF5mDCpmHzY0UlK/6opqE31Nk2 -9xMWYZIjwV/ZpAdww2pExiBqf2SBjUaSIn2y5Mjxo+RK0Cm2gSYTVGv27hGHBxI1Xcv1/CmWkZAT -purwAgV/Prt6XfRVGuvBOvAymDAYj230HrhlqFVI83h8+XuQ5P8cOagWq31NkP8yHGAxdlatKyPa -80g3NjUHL9pzsKMirNAg/5ffQ5BlFosbNO+2t6EcYYQRxUvpb7T+six9jz64QSpVEcsSDYEF6BbI -OHNC2O4dZ5c/1J7Sq1yIQuyVXTf61gYRoUJfy33jEb4w5SaQ52iVypTBdresvGk37Jdmtf5cbLFZ -pnTO3zNTKhpHl+kN1luqLnY6Qd94UBHLxh2iS9C0TSpcobGczOCRfbS9HEfLntMtigDl+Mg33j3U -3egvf/nLoTxYgv81l1hbhuXf7zAJsofrx5qRPE2Ae27je1k6fUf1EGNXQl3x8a+9BoVs4u/sBd/a -Pl8C5yIq4g/ZOusaQcAiBiywGxuouMd9Z8qP4PTqIAn5pM0nBMqmKJ1gHYttKnByLr9AVxUUjGTG -G8UJqE+FekAISja3gSShUeQ992ahkkI75Am8L4zFUbaO44Gbo7ZlpHOSgiFGVoedssjcn/dePxqN -eqoGBUKdRSaOixUV9gy9rkgouy/FsH7HP9q9gDWqdJ6J03zxOb4M9H9k9yeFF+j2p+26hSf/hMoL -p2/JihtEMq3fCedfXLwWYo2MJunuXX5BCNcCBFLdhwBANsl94dJSSBqVmScAE6jTxU/nSaOPoJw8 -cGikP90HWerwx5K5bacN36HpnlNlsBiQwCvLnBpk5lP7+R2LcwOpHXIz5bslZB/LteSwaVuUEZr3 -RqM3/JJPy/Rd4QUN536zchJDnrCr5wHGrbpSRqJGY5eFqo+eYy0HgcHKYqrYRfpFl4Vbhx9Ssfy2 -mrxBatd0YeYup8RcJeISVAX5QzWoNzA1zL+5iquPZXy5ir1dx+NPwOTU0VL6cfdND0z68us+ZNTN -1K1b5L2zwIKDA7SjAwrxrR7f9wrkqWfnlY8Q8fATrjpW4NRrNee9q7pej+iAsReWHU4/GrmvRmJZ -JCxuhK9N6wWHypodNZqKLxyKd9a0849rShwgneOs69juQ0o8WISgKEDUHVQwZ7qMWxQ1gv3pVMM6 -dfQgdB7vy7HfRRsa+rtRRG5wB0XU0486uXssvyMbKz/H9g8UtQbhZYfoa80wJgepw06kywCArQj4 -3fukpv2GvVIjjERs7omfb9td/dm4y/wTVhDux9eSEg3HxLhOy4ZcjuPmYGd5y7QLpvTCXIgm5TkO -vme2YyYqj+uaSM+CNwLI8Wtnc9+Mwe1olwn1fqbBNSQqICf7Id06yUixGgtxSU3ev6yxTC/zgu6N -p5UDaIkchZYG38FDS1Hxfx3jyTPQFg+e8FyFTrPiJClKVVPsQGKOqmuGJE18jqHlm7xx4B624wD3 -u+Mr8yAxEXybHfJ/UsIKShOptV73XCrro3UZFy+pHLKqwRFRV5PQy8KSoF59W9CDXhRnl0UJRF95 -oNZlgW/S0ae9r2nfrF5d5PDFTb/AB2svfccFP1v6CNJV0W5CtyraAlzwZRR88zbIte2JWn6oNVvo -IJOgxFT7MKRzH3oxgZ03aV0s691PQ84FeKSbtie6pVcf0SnIuxBJAFPsR326Zy/TkGGG7G/zQyZ/ -7HFjp07NElPgibdX8G308k2OLhYHNmfjMab8aZRA0TxPIzpg2hIBpB3aN7tYElv/g6htNavRTxnF -cWMedI1bKZz2ZApRzdoVLPEWckCS7UIPhuTzwMsrrL3yFdKmufu4cfgLNG/cU2H8uK9E1usb4Ihe -B8jAz+uERRMh6bLMpD0eDUF9IBiweEGOC9l6b1L3dSVE4WdWIxKXTK9tdVXfFzbfPpK8YM+8yV5s -SWA1W1sS043Rqw4R9jruT0Iyt4fMsNdzrxnsQvexfVvzr01RE9eiixRQz764GEAHQRYOrMka9cRr -1vJnUAm7dxsA42fzHrRtpDg6cHvqNX+jPODHTPKAUJhZnwVm7fJxfeDO71AoTfamF9S2D3HoNyx3 -m2VukvuQzPvr3rReLQEtQ3ex7Aa+tLS37ad9qyLdyGPfoVi3YaLfX7O61NbHAX6kbJ0IPJzG9ytw -9Lvp/B/1YO8wY0UeFEs7bpiXtN2NjvEQmqLrK+VeUm0qXvKW2RKnv1ucaj0OuIg65xKoGZZGgp3Y -7Hf0Cdh4/k4JLzabhkySRN+5kQROIkRcMmdp3S4FQvg/htHFH795NbvsajKrJ56L+laIvD06JKUP -X8vryqVGmBJk4MC0ocjthbwdgvgjAOT+MgcRDvUeWCx+imzhHD3ao0KWzeMfg17HKYQbmpyxWQ/9 -Of1EXrfx+DZlsBt9yQnZhSxp++3k6DxgsTmJBc5uTcs/iWJbH8nsqreIeS+nQVYOU+lBDL13Y23A -w6q9oB81CVxTN0e0DNf1fX2Ft60p8UfqkN5UUa2LXN/S5mKqIlClRAmeOoJdFIJex9QOp7HqsMNc -rNGxK/0kiVH47a0at+LAY43AzZ0x4M5/y9Olm7pbfEK9AteqXPZnFbdtxyTOC4ATZyrsDP0AoHxT -Z699A8dAG2c3wWAuPlFhuAVbBcU0Ivj7J4Tn/wBQSwMEFAAAAAgAAHCwRD39/KslEwAALEAAACEA -AABwaXAvX3ZlbmRvci9odG1sNWxpYi9zYW5pdGl6ZXIucHmlW3t32zaW/9+fgqM0Iyk21aQ7u3PW -SZqTST2dnk3anNidzqykaCESklCTBAOQstX1fve9D4APSRTdXZ+EBEDgXuDiPn64pFZGp8FisSqL -0sjFIlBprk0RiKXVSVnIBdcvglhtlVU6uwjKTEU6lotEFdKIxJ6duTFGnq2Q2n2aTKy4x/9loRLr -aUobiVwiAS6dcfdJoW9lpn6Txnf8282H9ze+0XWKdGYLkRUVNRp1s8slTOAsSoS1NO5aZKrAcR/U -vcpGevmrjIrx5VkAf4PBILD8XBSwlkCvgn/goPMPotjA7frv3wcii7FdZYnKZGCLXSIDURRGLUEc -dgI0zoiYiCKZF2KZyIVMZCpxaq+D6VAML4KhWC4N3SOjs11KxTg20loqGgm9iErHH3QpVJRI6m1V -zIUyVhoLS7qoNd0SHd1+KXVBXZgrzLTQWQ+HSGRbQdOBvUBpUBGWIYlGpJgibjXfE3dbG13mfcR1 -moIgcUQsCrE2qionyhZU5haZ8K0QoClUXPXNPFYi0bT4WBm+benGpIi6JJnLLawntLo0ES1ipWQS -W1n0MFipdUMmUAPToJLWTjornRV8N8RoI0XMTzYveohvvqFu/0LXP9H1X+n6b3QlIoouKa1RZXlZ -cMH2kL6Vu7WkOd8uSbqJWLJ8EwkPuIlo06RTkdNNZiXfaXEnOaRlAnrJqpAJknom7wveXW7WZeEm -rPPC6QqVVa9KUs+cZZ0bvfb28gUvVLIipU4WjNptjwXri/p21KYiodmBLrAcaqWwuYh476DEJMHa -b6Ur6WzdR7wkg7S8UnIJVFjqeEcFYliAnNjwoaxS7oIa1UO92FBP1DAq0EwLEjBtW0nr2gpq34Kr -0MM5e6gUnFqatL1TKirB4WPed2M0jU5XRkRUUN2TGqakBDYyKi9oU1Imp/m6ZVmmOTg8GXNxA45b -p6dowq43KRqSCxXu6E6bxKUvhh+Ra+YSbkA3bXjs9ib192qTUt6cFHeHCzx70JJTxuA6+MVmOpNe -6na7PhYQMgXylo3iO52w1F39gz5hIb7TjRGZ9U4nSlT+0W1ipIwLF7FcsR8FgZIPTKCblV2E0ZOF -KyfcqhJmIt1rsYbIkU9aJ7uc2G5upemcMwZQYb43IlaS3SW2sOqZW6cm4PgxKrDWWauydeioHycK -esUrru462a1Zo7HoWSBXkTSZm243MaSYgOau2b1saZn2ThXRxhsvG27hjNv7itJWO99ABDVgYBWo -4AD2qEthtBHG8cYWa8GFn/YHqAuVCUMwXbsC0ygLDaE3T6RTNaivdFQy6rhXPQEEaC9FdItOm53k -UiQiY9VYriOvsMs1OOdcAkKRRHipzUlj8aSpW02lrsagDntNsLBNwW2AZciZqD5HjLgDtL3qy1X0 -HL7KprKBWCCztXQV4+96tepnsKFePMDtXLSR0S27ugo1IR7lgnQMeN195DWDILw7BcP9FBEzAmNE -gBaryn9hk6kGgfz6t9ibG95XSYXLcrGWFoCzrzt7h6L04QpciyiTnoAVEKgTFPkqfGZxvszKiPXa -zz7eZY6NzKICsDwX414OtcNycIw9IsQvnukat9jsHIRaAx526IxwWr+MNtLr3wYiamVCUIkZYG3g -Od2NXPk72Aqx27hg1cMDcBSRYvBEMC91YFtZh83AHXixNLDcqqiUuoeHn5IH3QmgGR8YEs3ODu8R -WHzhK4/ZAOwH5zHjBt25m9vNVNy7GxqahxngkJ3L3+h+DqliWIFgI2d18UEp017smbYbEbvGOyMc -1pQ9SDNgQJqWqYskBYUxKDpPkWuFBwdnDrm2Tn/yL/zYyESL/jWAn0zZ32Mc0hUaBhgY6yzZcTnh -Wy5FETrJ+ZrqXwh0TZw1GPmlVIbtzEhC5wb1uOECAU9ZfycH00e9TNjJ20izHjLeZiYgfNfoJFUh -aNYDpyE9PEC4HHU9oLMlnB7Njot57o8B4PQUoK57Lpt13zkOwbOEYOhwVx27dd6IEd6+yoxX5n0T -BHYywh4OJSvOtgrFUColF4zyxLY6KVl3txWS3Zo06SV/p2I2H6/c92lySVbdBvn7gIMggl9aNbcI -p5Edr3ZP5PgwBFq2rjbDrG/IOGjkRReaY/IQHvIqYFCx0gRQaJE4elXd6/BKZqfdLKHXihZW7iq/ -XoWK2tXjioqNim4zp3BJryOnQ1QbM2FLBXKwQnrA3vWges+Gc4oBHPudcWk4BBpnvNVmnCi7HTpB -nL1A0ze4TTOVlvKpLJFbdlNsIz1HeeiVCyMKlgGcoSXAaNrfWp194R5meXvp/TnX7AYDygn63M2r -N5gEawcVLulh8zR2YBuY5QrrfYeGEsKMP56BZyjU1hkOHF2XgH+izukMYaFLFYUegggbufNGxfZH -p2lVww1NvIvgUlj5EcCoYrVdLjV5vKVcc0RckiQjsIsPPjcn8mo5XVQbiFTloT88VYpKhdBIPNVW -Zkxok4r3Jwiz4dKFJspJD0QZbnTTvkvi5nJRsMRuD0hPQ43qWex87xDjkTdmOJemioMoVU/akuvC -ulgPaTqHsGGaVG96ixPpi+GK1r2imaxf0JWSfHSQrY7Sa3cW/TlTnOXYgBs/BeKaKFQb9Vso4m14 -X1fhAuO5xYHIWALIEPlGUfi97SYNwPIjYhyaCFSu88qfQ+0GAD97QIcf+cAenkKGvk+q4saICiNy -/W/VirjuZHGa5C/eV6D7hKAM5igS5z/pxnbh5QwHtFMp3mFDpUCCTkdZllTaSoOyCAH1qZOeru7a -ihrgQrWV4Qsu18mK9xUUzp3ku+gi4pFmK9/aHJztJ3xXQU6Zcd3qH+7+T74jUnzH+L2LIHf6rnQU -GCVe3YN54ysd22z9K3QsjXRtpwEcRIzCeU3DsJUxW+KhIsCvjS9suaDz8PRRmPs0NomzscUGIux6 -09iV/SeNTegmbXSd2L2VYSwAwhojdnttXoeqRtzm6AQcbPb7VausMTTF92SJglujsVpfD70qTtod -CvE92GMpOHXhITBnp0KRRRv25YXuJls0E4gV9CVdLclnUV7zkfpf920ZgHtB2CiGwHXtGwob5uhL -TjhUwtBECyzMut3eKnn3F46F+CpyqRKnIG1QwUigm/Z9I/Lf08Lvv6mhBwDn0qm0azCR0cmJuHIM -wPCINpypUIs/hjRBTCdxQPuIBxy6ufT+GMsVRDuKf6B6Qrt2tPAdLfw3rdO3WfwRkJ9PZAJQWcAe -LJRdlEYRavILdCc7n+iqc5HN1II/MHemz/eE5le5B9poDiLBvMICutI8DuFLmNdgKSqNdSmhU9ii -ngh0c0f7Rk76aOjaC2yPSJJQALO3tdW31ofLWiQaQtli4xcnkuJ7n1rvfGNwkun+24SONwe1mOQP -qfMnXhSn6R8m9Rvpk8OkO7omv1sFbfdp8s7rHs+qR9Yu6tQzS+w3lZZMvj6G9USYJj9OOYecZQ73 -ktNYT0TO5lc3/T7SmK47IExpmYPWRnCsMsePYFQPqbY1VqZ+Q9rA33B624rqxXaiRQV2H8WpG3X/ -Ljz9CE6NU7lE9Qob51SKOHUHRGErl4LMRdn5qqvNgHqGYuUTe1RdSrAQTvn5Nz9UqOOXUdHmJMZo -8gD4Jtj8sRDWHwlwPStT/ICmbsjLjMJP75vymr6kyRUe0/hMGQECnwqgWiwjbaqtpyZMpj1y430c -X6pYubBMKLxmstUqkg3d8BmvRxC/20AwCatwxuH8uPXD2eQOX3Kw7ZcF4WLxpaTE8jIBD8AF7Qvl -I6YA5sevQcALbNydXlfj9zT6bv/DmIZPQKgoHxMHYqDIKdNVGW0sJ8LXDneujeSXCn/gT5ucySj8 -VoYz9eBCHsEkca9pIEJp94pfxopz3PSG+oI+2thx3aSseo28eUJpj14+dHxxZlMal5v3aWdvlRao -cY5YJe4teSGZI71k7eVCSDUXxgWSCmuSimwc/NhJDKNHlAVD7F6oYERwMq/wGJvbP0LUCH3vpHDk -TPBo+tX8DlcGq8KXvAkvSsbfkKavCuK4Keo7eQJlulNXjmMqIPCTIWWSE6trnbtMY5YxvUImGUfm -O7mk0/dpmvdpTuOgq6O9kqwgLl8uFKmlsbuMVLwQfS+zhtaymN1STWHpLlbWS+lJYMslJbpgy1Ox -C2K5wo/4CggWJgBLDtxxwuIXftBqZVB9V8hyRnWScfMDjmPf+Z0ffFtz3vruo0WrlX48/o3A+ZE0 -/vle7rJF8wAJdaKkg2ENF9rhXFtDDgyp08haw5paekx5/Yb5zzVxN4LzTZEm5xf8oSgEe6QWVCLO -dBGoLHj7/v1Pv1x9t7h6f/Xh6seb6wv8YtNvPwgrp5G6LGj0k8a3m/sU3t7cfPrhLz/fXF1PgmsE -Lo5KYwS4nyDHF/0xsQlEgDkRAAHgzQPAqhfINJeRWiloWO4cBc/h3fX14uOnnz5efbr54eqaKDQf -/cfVP3/56dN3uATg40QXuHzG5HA2OHeY8+Lvb98vfrhe/PzpBxoI0soyP0N8rRjgExttQHC2np6j -1xAATO3mp3c/vb9u8nd8Xe/AfzArF7g5o+ErfiHwbRDrRSYA7y1sUa5Wo3Hw6mv3aDiuBgevvw3+ -mBQvOwfhQz+uk6UI8IT0evCr2AruewmWHsExbDR+Ofj2HYTJ2wA8lgwAvgVfvXj+/NXXYn8ar0RX -R+oGrqLmS98Wj6xMVhf8nfH48qxyTk9QoXWaavw0IuBTkw0gJEWbAEAkD1jguT6I1WolMYIlu2p4 -4/FrrkwHWBvMqy5q1ewFG4av70f1B88TsFQ7GruvmrsJU+dp3T4/62bRID8dXOPR90asB/OLJqXB -VRZz6wlP3eqf5sWORuzN1bOfDjBrO5jjDFDck30HfHnAycgCYki7N+8XXS8a6xq3BsvEytPk8DuV -x1KUSVuEr1syH7wDBYH5D+ZtjjnEpgaJ/Qm5yRChs0ot23NqaGVrZpfNvR3gFzwDlCr1aHNBj4J+ -OQY3NpqOcAcugq1IxmQVVRVG923zlPnMp5eX4Yv5id4wJaR7sM21c5u3Nwtngg/rEe2c1OFGAgsa -4Jw8LfKwF/7hiy2VlfLgIZL3P06IQUBGTgBQjAbT/5o9f/48nD3/0/PZiz//OZx9AwU7Px8A/HgM -qOM/T3lEU5vidT4eT1AQ4McOqDwB9vg1beA+8EB1CvCrMxGRw6GfRdSzrZ/0LqtVnzjyo8EMPPIq -hjUNBoezAemOQBwAU6LNaPB5KsLfnof/Pp+G5xNXfHY5uGiTHleRef9v1J6CzRNVjIaXw/H0+dxt -4HGptpSnAhP7ntD/xTIJGsI+rWAdScduRTutZA2+tSYZgL/JzD6bjeAy/fwElGj6eTw/fzMbP16P -AAoHv6f3cb3bd8ej4/74aKry2LY2U7qVaAiR4NqlMKA3w8/VsifPfs8SGn+8iCa3+bHtr7e+1XV/ -1e6boxN76YhwP9xKFotHCoCbR+0ubcm23CSe2KaVf50fuFuO80RuAgfcFGN8PeXjweEgZj0qPnQH -Lh/ij2CLxjIGr75+ar8dBE/bYfxIdPSDjoeg4XCCJ+PRdBg8ta8HT+0ACsHo1p0B5Gg75qgELdsq -nHmaPbIevHpq3Szb2n3B/JvBfD8SH6N1Yr1+tZO1LEYD3IJ3icaP2QfHQFqD7l4YhSgKB7/B198O -DqGah4m/DxBWo2AN76oYMehfejWsBWtqCg0wicZ2XC7HdbZlPayvZDyN+T+pFDsAl1nHNP49HjlU -/DBaJXLU9qkzS/4U6mO4DMfkd9FhOhZNHL8WJUBzWTRFjdGnDnSDwefR9PLi5ZOnk5mFQPc2/E+I -dX+YP8zuwtndw3A6s7O7+fnwYeBKg4fZaDqLLxAezMbjZ18BicPVNUQzHHZzR3c5moZIF0qX08+X -L+fPRi+h/PAV0e6jXDXjy4QM3U+9NWhVeHgn51MSNAPOK5XFIPbRwPFFIV7SNIj5uIOlmzpROvSh -RwEXDEH2Hv8cQMN2GuMIUVzSROT4ufIIO4LhDC8hNp67BUH15XD/COC5OsgREuRozmG6/xMI9+MG -SquuH5fE2/sb+q9N54erIN/GeRfkTjN3k+sANU7UjUEsto5EOUbg2ckp7+nc6MkUVFyEq/n5g1kv -wazi86dvLmbxM7i+odts/OZhFv/384tv/mc2eeNKoyh9kPDv/kFlD2n6kEcPefGQ3z88fbh4AFN4 -M0Z9dfPqWBv+LY0UtwdPjx/e8O//pwdd2tdObf3ftW/fE4In4ohHBMbHf7o8av0A+uLI75qd/NCZ -LhYKniwWlSMF+aUQP7NIo9K9/lFnADAoifVBFuL1jSnxN9hWoi+PZcENh6IlyUTCyis+iuNXjK// -KmAfLupnbyGONh8QG0M8W878HX7rJgMcEpCiYY7O/ZwEdNQGd5IzV/zr0ZpBgCkgUOLdndg16N1s -lA3oF+r4M+5YS5sNC9hBmQYqliKZTCZV75YwJz3iakiqKaReoz8mrSNyqiTEt/FZcxshrLp5NWSH -DoLCJ6rowVKODsE/HrKPVhtZjYMDwJFEAf7t8CfTLoD/L1BLAwQUAAAACAAAcLBEUidz7AsdAACB -LAEAIQAAAHBpcC9fdmVuZG9yL2h0bWw1bGliL3Rva2VuaXplci5wee1de3PbNrb/P58CpWdqaWup -Tne3j7ROJ4ntaWabNNu4d7PX9fjSJCSxpkiVIO2obb77PQcgSJAERVKiXjE5nUaWgAPgPH7n4AAE -RoE/JdfXoyiMAnp9TZzpzA9CYt4w341Cei3+PiK2c+cwx/eOSOQ5lm/Ta9cJaWC67NGjMJg/eUTg -sSYBOeEF4MMBGbnmLf36CfH8381H9L1FZyF5bU7pWRD4gagxMxkQGGEnLN91qRVCG0z2wqa/RzT+ -eWjBD6HphcmvbGZa9MXEDEwLesLKilEvdEKHlv5uMstxfqQh0jgSf/0ym9Hgix/9exqU1bKdsRNC -+Ql9fxp/PPvpvKx06N9S72I+o1AsNMcXyZ9lFQI6c2F0U+i8MsK4tOPNopCFATWnsvwPF69+fIlf -v+Vfy5Jh4FBZ5AI+P3okuYF/gazwn578rv/o0SPLBYlwcryTzh806Pk3v4Fg+kJihmGQi4nDiCgZ -gojhsxlQ4o/EOJ0/HG/MSQwf8Sp/I4y6o6EVBQE0xeny7wn5wXdtoDGhoiZ8MkOCtEVRd05uKBKb -Bb5FGaN2liLwLKQZUiZwbkShroUkOeUpDSe+jX/dAC+8O2jIHg6H5N27dzlinHOC2hvfQWlApRxn -iWDGUPJCkLDpCIzI8Zzw+rqH1I6IIHcE6gfWAmM4ee179AgUPmD0FQ3Nk4sggr8jxlXYpqH4Im5f -eVxUQ8tk9Mzl+oAWFFdOfnoWhoHyPW8k4C2C1BKSyjBB9LmB9fIdVvqqdrOfJSeaAnLiQ9rYAXlD -g5EfgGFDB8G6vTtQYbTu77MUdOMDerqvSyrK0au15Hdqh97SMJpxnUBJOaYr9RUGoKpSTJ8yy5zR -c9ccA+FzQLp8+yYLz/0oQMYwKHJ5lec0kITv+R+2GZpvy9pI6CudvYBuxmYQG4cwBQukFFI7S0Y1 -LSCGgk8LRABmvYxFH/Fa/WGisv2MGgPWxGoc23zG7uG/+wmYmDAuc+xYZGICYHpsmPb/P5TYPgHm -gOZEpguGHJswDiGcBH40nnACnElgt56NVD1yT4HaHQVDDhWcIGiJAYgv4CXmDnVtBTbuJ+BxQANB -S5naUATA5vJyHn0f5ijCOAL0LyxEWFGGmWUtr/TviEYoJe6QepdXfVWrQhPQNW11SP6DAwFvIJow -rQm1VY24d1xXDiarVgegmNAd00YsRVPmfMH+u74/ExVBOFPHAzppn2H4LlVa6CliK/yORj6k6IHZ -kwLYCMb+aYTgm4wnitu6NN6gfXPPbVwdEQP1GUoUiQ5n/qx33P9Q1oOUn2XN54ohQZeOQlVJ0V9G -U/o6mt7Q4Azd1zyGXYf9QN9n1ZZr7SjyeGwRM54R6gBfA/LLZ+fn56fEDzibLelryQ1gCIgBXVIq -H5tazhRgA0qD3zfln+CsA8rA/kxsYUheQlDhMh9iBLDuALyS8a1BnBGJS6WCezmC2CiU3xcGzq3K -7lWK40MfNU26towqJ3+ADQI02qjDPFxJfghM23kPXz8+Ti1jJNiYFVBKIYl5Mr8nhL5MW0V+gn1Y -twIeFUV/ISSIVLOcZyIIwIDCAWmZ3pjG6jM1b9GoGQSqAAIKLdv3DkMycaAWt7uUBZaE31hFsZVe -P2c4FjYkR4cGZyE7UTBAK8uDZDxSNlY/+7u+vey4wQ+GAvtoiIauDD3Wax6twFgcVVmw2DP2EvTk -BH/oGcbwN4hSekmf+kdCBtn2oDETxqc0MqZJfAmgC2ZAfDQFpso/bQyFoAtGi4yBfmlLXibErjJ1 -llX3BH2KsZLmMQA06dh0BzhpmWFYN4CoZOABmwPHGvDYd25o4i4dLWz3DroCvfvTSIYFfyWfP3xI -NYK6wMle7/j96dfHx+S7E4Wr8Ad8DdDTBzDJtd1Liz2FUo+Pz7FcX8tw49cI8ct4aIxlOe8Bbnji -R+g8ZhS0mod5OI+YUMAe5k/pxL8nPToE7zz172hifmhnRM6+srYcy+74+PixRnbw9dca2YH0+G9n -2iqPdeKOq3x1rq3yTWmV89NTnVKdn57pq2RMGqaHf0DERsPeJe/v8yOsen5+Fv97jv8+5l/UEiA+ -ogKv+YUk9YX85u9Naf1d1vyHpPUP+c0/m9L6p6z5paT1pfzmq6a0vpI1v5a0vpbffNOU1jey5jNJ -65n85nlTWs9lzReS1gv5zWlTWqey5pmkdSa/OW9K6zzRpmNJTCDaVR7S8Fkaumr3qAnE8fItwlzS -fn2owyfJr6kPzBCD+edxVs0D0MPghZFfXrwdfEHezMMJxK43kePa7FBk5MBf+B5MxO794JZpyGEk -4PB4lzx/9WaoxQ+CcBOkDirbz7gz/2O6kZrjU587ogLWgCsDPIsai93nX6R3R54+hTi13yefyV9e -yF8+FZDRzwQ/pyIA52P6Vo3AyU8Y8dw7DJMlESBiCAE/eAoR75Pf+XQPQsHMzAzDa5Hm4IPDwiLp -kfIKIyfyyQkG/E+24ZKzWjlAkfpROGB06li+63tGTrPUWDWCSDvEgDadGog5KsqiMPXKTLri4BlD -vjjNhelHTME4N1FIT/gsV7H2A/JSJGBAADJNB7TNyIWAOApRImBjIiHg+SKDO8egfGqGOJtOCMWF -geGfGiWzjmI4fqWKKw2fL4+v0DPmsso418sX6cG8AATznXGE7fYLooEqPYUlcjaBnOHzC/W3k5MM -+TwmagSklk7HzGPNTEeBsnFg5MOknzG7kGRE0vkuSIFRyqd+4SHDKR4OI57hZmjgT/l8WIbrUrmL -nC8EWGmHB48Fa433yNV3hsY3iIYxLaKFi8q2c5zIts1E+HgjQsORE7A4x1+ICbEbfJaY73syLe6T -X7XminqBilBKQUzNNUM/IM9C4lITOoVKxMuBiUSefQTRrbRJ3vX7iQ9zWo9nRvTutUSdoA8aVZYG -JtKMmrQLDCbnBQrBuRjBaz8en+j49lw/Mej7GQWtt/Ne/EORAQs4xjNd/QU8A3AAT1Wcp6tWW5zI -PAvF3IXHGiT2yHF6kUyd8SQUGVLPxESB6PmQnMlVriwxTKOw0A+gZExp7Po3pksgAnHMG9AUQ67+ -GFmXf5CjJBM2SiKB67A/nWEjAsUFnJuERTfAM8zW4KLQhOZoxYCOI2CyX+ALQiVhm1IABwCeawwY -JdCfZfspcji9nC0l+RuNMTki66auhA0nJru+pXN2jS7zGsKEkfNek2HRUMPnBtTjthVQygo/TYbH -vOCpMYYJZyYyNzi1VUSSo8aLT8254BxmlATnc8UgkESxjZwk2QzsZqEUk0jT8/Z5BloQQ4GbEBDh -0l+OHsj8U+Cww524w+fWFs1KTRvSivbilZyMeOIelQrm8gmiV9EUBcUfqTcGXp0Agnq9tBFt8Pov -Oi8JXTO94yssedeglFBcvlYB06JcX3Uho3w2EzrKx+CwUjuAjIfT046HA0QmEszE1OqjiFKVGfeK -6gK9LrHBnwX1Y6/TtCbGTydGmckv4RfwqfYN+Rp6d5qhJU3lMpXC1Ro6/dlJsb8Znj25qhMJbE6f -FT+vKPXmvTwYSMYKNCG+uoB7KcZ5hZZ0CcYEfBeNLQgboIm4Q8UpTJFhCYux+2+zpY0aIszUL6va -QM7JR2VlUQzng7LoF8dAIu586SXsLE5CNet/8S6QeIGCxVMgCIvQR4kYSKXJcxh8vd5YsDacnQur -s2Dlc34yjBMYZVSArOELRfqadXd1AA7ucRlTD8NWCA4824XACMeAdEIRI1DcY8TSdUhGlaW6ZNEd -PbhgNsx6LFw75/HCIS7v839kUmmE88N4PR6aZFBcWUbn7ZatoIfxhoSy/T885LFtufvHV7IvYr8O -XxFWJvzoa0JhJFyNOMBndlT1C4ZRusukRLEvuQ8E0ifZv4dhYHrMxQX2/C6xwsQ228eTkwyqnXn2 -hTk2rrSRQahCQMsRQbNIQM6WTKm6bOB4AKL2ABheEgjEvccevnB93A6xI4PA5gaW6NJg5Jrjga8d -S0m3+De5UiX7e3ihA/Kcgsrx+RfqNE63/IjlNxwxno3FjQZRvNKLkJDQymMB/rB4RdsZxYW4X9Lm -kpROC9g7LWxN4smkhM53lXSAhz8BmxYR+TXCPG9bWdF62VApe8e7M13HTjP42jRozX4o7q5pP2Ie -5FeIOY8A1wu7DWBmJtSF7yuBMM/OzX21m4gUmhWhACbIAwiQ3HkM8okPkWg/9yPCYBIA8z2eH48R -mo8oR4oJZylmsDDtFDPYfDaVb+vg+1FN/JWTm5PiFDL2KiCZGdQNqTsfLi2xfIDTMJLkrPysYHTs -F8xV9HIDPOL5yX5Ov3jyi0cbwMBoZqPpZHcP4oa6IwLgZKkJSE6cT7pz5ERWwIXO2HPujolggGgh -S5pvrcN5O690E2TcryBnenPy3SeDAWYoB4OnMFbwwdAVpihVPg604l2PJXzpAfzIHHms9/2WjG4Z -6fGeKVKJLYcnk9NQLAuHefTVhHz1PMLCRgNrY3Cf5Il+lnukX3o/Wxlbbor8ovc/Qlh+AcHoW2e8 -yAm0DnCdY5GOhW8C0juWzgl0TqBzArWdQDlIrugPFKCv8AjmfUjfhy24hBr4LdqqCeAd2FaAbZtO -rrGt7ZWZMStwZmFprNW2nqfNdapevx+dqpc9TVR95pogwlYQvYugN6jpea3cqGaVqH6i9FVKp6bk -VgPXTyrBdWoGt9HslFouhumgh1XJwM+rp6quz+jF4rRibmm4YlUNKOsnBribgqfja6iMWAl4wttP -l6oua1VVc+JPhEE2rPbMuvX8e5faY2pLEh8qGAl6gEsdi6TxtLBJ8N27d+Ql35bjB3PcHGvJzXG4 -3wS3CY/hD+bY+OYmqhrfyjMkz6MwR8n2cRMLqBi+AopbRb5vB2WabYtN1oGhq3wteHAThYOxHw4C -3E81uAFbvaXtYU+KK989LdlyuzBRlJXP9w9YPvz9XECUAWJM9e5lbLSK3Tf+OGIv/CmuAubZXtyN -h6cE7AJH1qGb7fCz5qy2gOjLe6V2gF+uwh5lUb0GJKewr0X1NUDyprVOLpSi9tWHytqoplvs2oUh -Un+0Djv7fDluacFIMNAy8TgIcn14PRwunxNdFxvRbJd68TPmGP6TfweqbawvC5oTy1wJnhZm3DXd -pCM/oMkWoaXQobCPqO4q86Y0xh/h9pGF/qxRWFQ9iVCwWUb5+53xye4SjHcpfaZ5C7xk8lpWX7OS -0zvlhyrgrrB0+hmvUCTbxeLAkmnWbu4oRKZswg/Y8mzxUtHIZPGrRrg32sYjDuYiWB2ZnjWvmNGW -LDWuNrnVKlFIcXHKDObPo9GIH25kaLY2apL7IqTQzVlbzyW0Fr7VX5bQDXGdQVxeDDpFXSwJHZCu -QxJlDn4Toih1WDCQwJ8FjppVUcNiNMtSVBD7JXt9NBKdMOTvrbhH8VJk2t1VA/r6eRzd0DYwA2jo -7z83HhyHGrrupw+CQwtCPD0XF87BVsbfFrG0WbAEeCsz5Lk+bhiGSxbPdyQkEb3by5hE2QFRSwS7 -H5Wo/dy3sGQ5aXSByV44lS4w6QKTVTnUBSabCUxqA/GC7U67EZykHSyPTyo3IazoHj+pzkEq3eQH -NXOT3584KrfJrq7O7Hw0le/qngVUK4mlC6v2wiV2YVUXVq3KoS6s2khYtSwc5yKC1QKrQfsBzmDJ -+ObUZJNa3nQLnE769jFw28bR7Ci37Y+Ly4ucSZO3R2KS3Usk9fvR9CWSem5tuVdGBvv03ki5In80 -ZtmZ5o6ZZk0mL2BsS/a8ulLiP6uOrL5d7oltbsy8VtnBXDKQGq9SlIU8nenn+9GZfiumv/vpddHP -6ix7jfzAiiltiHvKBFMcYqMs8Kkf3bh0z1P0K2jhFhP2TfP1aof3Nm2/sqi6JP5eJGC7JH6XxF+V -Q10Sf8NJ/GXAWR9ArORH84eCkb+US/d6AA9HaAF97YU/bQe/8hDkEn/ArVEwQ5PGqht45TiuV7al -hLexILVueFrTqLaoxB9D+j4znqUyheuJ8Ks72+Ug6/djqeWBjb2uKgScfmrnTe7WkaoJMuxJplLP -3QVj6hDiwSBEI1Y3Tlh24NIsfbqA201xaU+w6SMAlm59JvN0oNeB3jZAbyPrRytZtT55vtKalMqB -M6+gq23PpdsRlexolxSq4neL6aBamPZgkkJNFblsxWLtR1vVux9hKVHqLkqUQr3EP0FwxlUVT831 -H7XV+hFV/Hb0Q0CIQ+PwCK8m5TcrrIAUK/jyJEqSMkV/njC1/ICvDUtv9wPJhQyRQWB7TNmFgxez -alJ59uJyoeDaFU0LuMXyzaHWpeYdoAHe+fk27gtvJBkwgI9cDc2e6q5q/kkl8uScQ3rhaWuwzO+v -PU4OmCOf/Vrm+/P+Qm0s7yxKeLTogPuSA65HePQxspMfa8dv3EmvuuSXFFm+d0eDUFyoYxLbsfLn -J8cXZIOrxWPsrAm18DZHftvOhIqL7REA4He8DTKIPMYbRQvKUZKH6kk7Fc3xk/JM4jos27KqBVn1 -qH3xk0bd8f6nBhscWnRu+4vWipYXjmHEp4Gu5jx8e869CS82HwDkcGJp7rXp25ppT5zaqDH+9rzZ -UlxTfUW+UB4jn2WxkB/ZGfjAQMA3vN4N+A2jGdMhucCr3iactyzBvRw1hgDATwzlPEdpJde7+eQO -vQ/jxxqzEK8pw6PszRHFS+niO81y9BCQkw4eEfgHgRWPs8c75eDXgOIFcxyBZyhZAbr8umhnSrOH -Jhcuicax62+IrpTHCelplaeq4qLbpbOKhLd9o4IdCbZxoC8l/gSoF0chB7xwICe8FT0gbCZolI9h -RzPXsYA3qY3pLqDG5waCiVuNuz933nPOmTf+HS0c6++MUpdaIvQFsz59MKj3prs7+W4/blzxsOpd -SwZ0Ec/a56frioBWjW64Me9YhmMXJvNAfADBT51LFPZpIl8KcruN3r8alQhlZsYkMsr/jvxw4ZJh -jbuls3R/8XQ088PU5cCz7R42bPctAG+d8Wz7fpUUSHgI1+COldq55/30LTz4fFw+m15O8XIO5iS5 -lPj/tuRg6O8RzMdwzhh5v/MO51ViGY4V5oErc2sHPEyZqazfzbTP2cVJYz0ir7YhoI5HyMxTKuN5 -rSuYBb5FGRO3YL/0Emo9iOMeDkDthuHkc1HCbGyuVwMONpu1mwZ5/x7q6xERt8YXj2mpYT6FAGA1 -86kRf6zTeqD9znpWHFor1sO4Wu289RyuZjwZp7XD9581NKKnpUa0Um6qJ3IUnOfbjygjL72ST12T -WVt82cHR9uDI8wUUsU1jURn0FEaj7mr8VOxqlEm9gsGocEX+ymNHoyz7BhM1y8PXLu0p255qK4AF -XSgkWMtBqoULilfbP942Sq9x6JWaqd8AXaJAq8XSWrXPoo7SsHGV39mS1Nn8TadN/NEK2s98cGMR -i69JfUjKv46RL6f7hRuD80p/QF6Zt5SYxKP3xBIF4z0NuO1r7NxR4oTEZPHqvOm6YhtY4k4UUhG/ -VRV/HjkBC8lT4geot6SnXLrKd5UxvniNP5lR6E/N0LGAsnJn6gFvnm9nc8JhlY3GfjsTG8dF8Z9h -QGcu+MCe9MxHSYCUli9Rm4xY9PvsBdc0m+yV0ZyZYZZvxHYC0B7cEjLi18jCj1xaiRTuJ4414TtL -HPgxIKZCDk+bwdV/D3k4zI5Bj111bEmrQlMzuI1mp9Ryoeeh43ulB3Nhi/CDdQstXBa7cqXiaFIW -gzLtW45JCSmJIsXCGyDVVBNW1DmJRyNZw/igp5dhrZVYXP7wNh2r5Xcc7rND4BMzG+LMw9PD3EwM -jAZMydb5FbQtCU6cRO/QRxo/HfaPgJ6Fn1+IzyF+voDPi0GvdzjHgv8VlWb4+Y34TPHz2WH+HZ+l -JKiVIu7UgjHI8ejROWVGdj+d+hS30jgjWXEFJTn1Lf5tTb+anNlk1LxTnhiz6MZ1rJc2VHrtexTP -apqzkE7Tb2oSsvwAMQdqocrUUWRbDK6hDveKdnjJT8d6pGmPb2cLEOdQ0jicxSWHoD906INCnbkU -rYw1LY+dGvKbvnFiRD45KRYH4DMjN3wtCy1ve4Ai6G74tpJn+L+L5NOlodnL1o7RwJi2YjBZFOQe -BjoBTqNag6occYvxW7ocZ5tsQtnADwaxphuq2wYP7NKUs1Wvv6Wim/kzVUyaaC4fly32wQV/soYD -BUodWNVJJPuZLKvIkm1xW4XjxTg9iKVQ8539wlBXOppv61nCLQy+diax5iK6tZSBr/PUEF33NO+s -fyTmPejsu7PvZe0bDCg+4nsNVr5J436Q/nv7r0/tun7HS2AN1uNLrhtapOqq+nXurG13tpAPO4/8 -fLc9TsB21kTW5wJWP36nQQDTauCw7zbUjhGteol3O+tON6Y3jpec4g2RaE5VzqcENJ+b3sK3u1Y5 -hbCd4fLBNR5urfnbLsKjMsZtIuRB4e3S7SywLitnMPk1onhiNfuJ5FqjXsDJT2ri5sc97QA+fISh -GPcmvPs7HJCBBrZtzOoa1+b32cXLh7v+ZmzMpHqHXGXFJ1cdr7SLTRvXrfUzzaPAMK4MSbSSrC1p -BrHURqBStVnwAnC+zvqUfWYyplHkXXhXVavI1S+q7opK77MHFde6kdrvwtodNG7T7SbiquFtS0W1 -yONu6hCZkmGV/rbo7KIKPnC8r+HSa+4gbruvHSiVsbj+G/o7jkoirlcxqR0c2kvFrI9y+YRQ+ds5 -+xRFtWrdpVrdWJfqxLybNpVtznkzx3nMcJXtTf69S3wW7DrEp7jr14iQ1i8GbtQ1bvDzc/HZxc8/ -GlW7fvHpGQ4WfikqWvj5haHb7YtPte7rhl1rh2+WAeWbFvEpblyMmyvdvIjPYm/+hm/C/Red3/uB -5iAg+ej2xOKTO7OFIR/ftiPkOdL6r5CPpIufQ7HbtJ6QKRY+ExWn+PnVQxTyW76vupGQMwUOyLPC -+zFQ2rTjGbj4SaAHOUS+HJJ7cUBojs6lOfjj2eB/AdOYj7Xmh1B7bAY35pgfkZm+LhLjF39fBk8c -LZCyHWaZgU3tb4nvuXNeFf0yC5W3UaY4A8ZzSg+fHsZv7eTIIH3MbDA8jhQKcvQOcyeZ7hK8y/mZ -yMT4QXaWr0J/TPkOGqtJ/c/Mmz6tBFjF/cGn6ub7+pFJEam2ltV8E7+5AIxwRg4NSkML5aDEnThw -oCowWC1rV5stuzKP2L9UxjoFVJ1i1dbfykRBf2JWVnTJ+0U8N1dz/psfYt2zF7VnEK2hPzt+dqKC -N/Gho/tgah0wNeHWbryY3jq3KgIVWXZR5rcSPDZzTGBDd7yP6U0FS3f2hacOCx8oFi5S1epsbF3H -v5EzEzso6aCkg5KNcGsNUFJpwpvNoIT3lHqZ7jzz7LfxmQ+yX6yFWU3bq7+HxuG2zb55yJscptFk -upmXxkrT34fAoDrz8Q7/umnlUtPKRpD5YBbsSwF51/BvS3AD/engpoOb5nCzeMl4a6ttBRvoVtsa -saWLPraz2rZYQNWrbdr6O7va9pDd7Q6khdbiTLq80A5wqwtUSl9trEKPjS631fXH+5gjV8C0y5F3 -WLhbWLhIVWsvt1V6/k0ut3VQ0kFJoVYHJbsOJZUm/GBStJ0KfjyR/UoLGPni+3eckE6V+dlR5OzV -y4vlckYb0qcMKOgPfOJklKPsS+RzmV5yIg6PRzLZxrBk2dn+8h6bKyN3yH+dSk/zlfDH6he14rsD -tKsh+BTfsyoyEB/gIQ3ChBZ0p1AkVhV+D9/giyfiZoirqxJ7lEXjEwh4rSeDL660hfVvg+l7KolL -hmKXlVP/Y1EaxvA3CNVyqnlATqnpknsnnBAvcl0yoQElgclv6AknpidfzhK3SST1sOwLP/LCeDRD -C/9ILiTKmHFa9ik5znYfX/9zsInABLvpJSU1r+qtN7TFp354G7OUD7zOXUyxoiyP+qn7bxqr4z9K -7+sAiooY/w9QSwMEFAAAAAgAAHCwRBBkUDo0BAAA8QkAAB0AAABwaXAvX3ZlbmRvci9odG1sNWxp -Yi91dGlscy5weY1V32/bNhB+119xUFFMShXDCfowBPWALYmxDuhWIOlTEAiUdLLZUKRAUnHcov/7 -7ijZlpykqF4o8u4+3q/vWFvTQJ7Xne8s5jnIpjXWgyicUZ3HvN9nUMlH6aTRGXRalqbCXEmPVigX -RTVj+G2Lbmf+yVSdwls6iiJvtxcR0DfInho1Q28RZ+W1wga1v6UNCAcV1qJTPg/SCJ9KbD18DFbX -1hr7CsxPUaIoz4VSFNkC7uKJLM4g/oR+baor6VrhyzVaPpPupqPrVsLjZyHpKFw7fLEby27NJaWi -NVJ7tmxC2EtRemO3wxLfkw+lEs7B8WVJJUuf9lHFcXxFO9hIv4ZzcC2WUihorWnReonuIgp6/2mQ -WnopfCjGA24d+LWgilkEJZ13GTj0DowF37WKasKS0uhHwsEKvAk4DaVBkrhHcAZEWaJzUq9A6C0Y -jWBqQkagMjdUWB02xsqV1EIFDL7uVMkHOi6+IjlvkbpIu6DZcIwM9yhUh73zTUVVeJaF70lcG8P5 -K4SN0wtavsU/0sHiLgjvYbGAIOiR/tyVuYeHzVqWayiFhgI5fnLBmm61Dq7sVIX3VhbU1bNdynsw -khMFOK15njhUddYHvUjSoTr8vYEvIT05V+1aExT3u3YeRcW5qqSlHKgt9aCTK82q3gDDgaQiFKbz -Iyy/kWVo11o4P4PPCoUjTw3FYLHuqPJoa2MboUnNo/OMVyAdUTHXQlMVViM4qpkP2aaEDuHxN/GV -CHC/lxAQaNFgNiSQ6htivhg3O8g68DphzZR1Eq551ndWBkT8b6gp3aHn0qnt7haGZVPGeK5x7OVM -tC3qKknYbHAuTSdmqNwLQC+CjCIcgbDm7KjgY/ODJstmu/ZZwL9Ei3HLrNCzm3sQ4tIoCT0b+ttI -86CTTXBTmg9v4MY0CJ2XNFW3UHe6ZH477qCKxkCYChuUttJEUmK06XQFXy5vzuHR8fqeIFpqAUME -6KSqaCqzi0ejLKmEF4OHg3eJQt0fM8POqZGqSWqNrYL4bn6fwh8LmD9d/T6fs9pU9CGI/louX0c4 -2yNcHiOc7RGWyyUnhJ1/bdKOoyAq2JzqS8VJ5k9nc/rgHSQT104Hp1M4ob/3rPGse5KJK6eDk0PP -DKna3cXl6h84GGb8fk20IXKsie/S6d88/CMeRQYf4UGbTUpmfxM7w1Twpu31CmTWVsQnWQqez/RY -gaFZHSZXq0RJdO7z8dLrktT9OqSjV7kUNFcpI99/HJp1YpwUNGz6EDI4EXZFj8bJycOG/0YNLLmB -eMLxEEoObzpxh4mV51k/HeI4PeI+i8mBOH/r8sHDGN7C4do9xN7sOasHkOJXUMY+BzsaOKNkTIGH -eo7kd2xz/xNfSJefrn0K+ok47fPiK8/Y+hdSfAxNcfCUyPNZ11ITYsJYz7SmztJVdPZKXFE03R9K -H/0PUEsDBBQAAAAIAABwsEQAAAAAAgAAAAAAAAAoAAAAcGlwL192ZW5kb3IvaHRtbDVsaWIvZmls -dGVycy9fX2luaXRfXy5weQMAUEsDBBQAAAAIAABwsEQigPvElQAAAB4BAAAlAAAAcGlwL192ZW5k -b3IvaHRtbDVsaWIvZmlsdGVycy9fYmFzZS5weWWOQQrDMAwE736FjgmEPqDQa78hHEcOKo5VLLnv -r0NMMXRv2tWOFIscgBir1UKIwMdbioFfVVI1wmteYOMPK0teoGYOshEmNio+qXMuJK8KT07NmWR9 -UbD57qBpo9jonNkQJ6UUF1CpJVDPT5327XLh0WM3thu1t4dWofZxhjOcBsI8Nncyb1Z+p7M/6B/R -t0ZKX3VfUEsDBBQAAAAIAABwsER/DpBOIAEAAHACAAA2AAAAcGlwL192ZW5kb3IvaHRtbDVsaWIv -ZmlsdGVycy9hbHBoYWJldGljYWxhdHRyaWJ1dGVzLnB5dY9Na8MwDIbv/hUipwRC2LnQ2zrYaYfu -VoJRYmWYOnGQldL8+9lJO9q11ckf7yM96tj3oHU3ycSkNdh+9CyATfBuEtLrvQRjTzZYP5QwDbb1 -hrSzQowuKNWlHtUV1Q0GUkp43iiItfy23jlqJTYI19wXG2Iy77YVReeWRoHP5WfH7PmG9WvQxOAz -VqnWYQjwYV0Uypfx1Xop1i6Gurhi0tU6D+S6y/sywTOIP9IAdoBbtnpFpLLdCh0ymUfK6gTn2V6Q -5Rt/shKyXT/KnM7/yFQowgG2t1vkxUMqmQ3YUwkndBOlESHuTia/jDYomNVVtOxDXpQPDV7Xkeat -w74xCOcNnA9v9RPLP9NDsqij7+LxkLuziamFuUvNlpxZc+oXUEsDBBQAAAAIAABwsEQVd3k6uAIA -ALoKAAAzAAAAcGlwL192ZW5kb3IvaHRtbDVsaWIvZmlsdGVycy9pbmplY3RfbWV0YV9jaGFyc2V0 -LnB5pVZNb5wwEL3zKyyqaEEitOetckyOvTS3aGU5MAQrxia2SbqK8t/rMV4WsiwhyZz4mHnz8Z4H -Kq0aQmnV2U4DpYQ3rdKWsHujRGeB9vcZKfkzN1zJjHSSF6oEKrgFzYSJogox8kMovWcGoigqBDOG -3HDh3BL/MO9v0m1EnJVQucRccktpYkBUGTGq0wVkBKTLwOVD8EQbA+SzYengi0/zAwa5GuCicV6H -EwBGWYxlFlxE3Lph1MDKeHjVgGW0Up0s3ftkmoIb8kfJUQUtyJD7bhcNTyuliVWPIAmXJw3N1oNm -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 -+z/9x0z9koH87h7I45fvu3tn9+8fb97233n2v7/58H3/5nF+dnx62t3jq0ev9Y9ff2T8vZrf0fDv -etB/fXzbq/HhU8vP/uYU/MebfvN4+85u/9Rz3z90h/2if2mxevzhzeOH79/88YdvX3UP/dnHf/3i -q//ym99/9fj81//y+K+ff/nl57/+6l/+vj/bM91/7fXbPb3983ctgt88+q0+vH738a898O7gn3/x -5c//S7f4/Gdf/OqLr/6F0f/yi69+/Yvf/e7xy998+fj88dvPv/zqi5///leff/n47e+//O1vfveL -zx6P371582mKmcy/Ncn/zxT/sbv88/ueyW/efHz99tvv96v/Sy/v9z3Ab795/On1v7/pZf76zduW -qMfr3iLf/fX/1xp++/7dv/m+3eQ/JvTvH2//+Hj3/uOrx18+vG3Zaen7n1a3e/mP9X3VQv/1Z68e -6+gPvX73377tJfhdN//l2z9217/89v37D68eP3v//Uc++s+fd9vneRzPnxzjeTwev/9d/+Z/9b/H -L379j/8flcqPXrrjn739t8WG+O8/vHn3dUvl6z98y1hbQL96/fYvr9/9761keiO8/tBD/O2H939+ -z/h7H/7w7uu33/YH/48/ffz43X/66U//8pe/fPbmmx8++/iX/1THT//cTT789D87/8dZj8dPfvKf -H8/P5nnG0b86V3z61cpa/God56df5dy/Op7nvH9V16hJw+es+1dXrho+4Itv3qA+Pkk04/uSaX78 -w6fOfvrj4yf3j3/3+IfzM3XPlz3G93/+G+3edcMezk9/vObz+En/9Hf/8PzseK69kf76XevDv/m4 -139gF5zrf0Ok9qDev+tt+v1HNtOff/j6T48/tVJTgbS6+fIfv3x5+dkX/7T+9at/+e0XP//8V//6 -j1/87qsvv/jZ77/64je//tcvP+//26+weqF+/qfXHxCsFqr//psPaiGXanfw+c9+9Yt//d0X/9cv -GPrI6I57XWnz1fv/aPEPjx+/tMI8Xh317H/WM171cNerY47jVavketV/vfqfOvkpXq3ns393ZPbn -nuerEdXNevFenXXS4ni+eqCCj3gZdcxuNMarcWb/1Avd/5n9hMecr1p6+evRLa/Z/xyVr46Dv8a6 -Xo0r+fAZr8Zcx6sz1tj9jvPlYFj9gX76WM9Xk3E8Zj/4PI/16jH4z3X1OEY/8LiCxzyK8fLTqlfd -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 -nO7ezOIDO1nEx2T2muk102um10yvmV4zPVZ6jfQa6TUamZbQmW/e6ede0zI6LaPTMjoto9MyOi2j -0zI6LaPTMjotodMSOi2h0xI6LaHTEjrzzTv93GtaRqdldFpGpz102kOnPXTaQ6c9dNo/p/1z2j+n -/XPaP6f9c9o/5/uy6k6kf+M2Ne2h0x467aHTHjrtodMeOnyDbieEtIROS+i0hE5L6LR6TqvnfN8H -0on0b9xGpxV0WkGfTu5pD5320GkPnfbQaf+c9s8ZjtDctH9O++e0f85sj9g59G/cRqc9dFpBpxV0 -WkGnFXRaQafVc1o9p9VzWj1nOBN/+ZFerZGZ57u9p89bo9N38enpYPitbAWdVtBpBZ1Wz2n1nOHe -MFoj0+o5rZ7T6jnfl0125vwbt9FpBZ1W0GkFnVbQaQWdVtDpi2JaPZ++8KX9c9o/p/1z2j+n/XO0 -Pt9J81/aQ6c9dNpDpz10+jqQ9tBpD5320Gn/nPbPaf+c9s/Rdj79kYZH+m7v33lJWkanZXRaRqdl -dFpGp2V0WkanZXRaRqcldFpCpyV0WkKnJXRaQkfr850v/8btlNMyOi2j0zI6AjHCP/TqDVEb6bSR -TpvotIlOm+i0iU6b6GjnnU6Rf+M2P+wU1XOFtJFOG+m0kU7fXxy+orXnD2k3ndbSaR2d1tERj0Nj -pF3n0ynyb9zGqbV0WkuntXTaSJ/DjzZTraXTWjptpNMmOm2i0yY6baLTJjraz9t3ivwbt3ESXxDb -cLaWTmvptJZOa+mIFPCwcJeaKXHzm6U20WkTHeW7vU+ft2aqjXTaSKeNdNpIp4102kinjXTaSKcl -dFpCpyV0WkKnJXTaP8f7+bNOjP/SHjrtodMeOu2h0x467aHTHjpsCt9Ts7SETkvotIROS+i0f077 -53g/b9+J8W/cxqk9dNpDpz102kOnPXTaQ6c9dNo/p/1z2j+n/XPaP6f9c9o/x/t5+06Mf+M2Se2h -Txu9tIxOy+i0jE7L6LSMTsvotIROS+i0hE5L6LSETkvoeL8PrxPj37idjlpGp2V0WkGnFXRaQacV -dMwBm5tWz2nrnLbOYRPanrbGfKvv/T0Of+claQ+d9tBpD5320GELsJ7hpmV0WkanZXRaQqcldFpC -pyV0WkKnJXT8fuP2vKRldFpGp2V0WkanZXTME8WN4QidkFpCpyV0WkKnJXRaQqcldM7ve3x7XrKb -BTz8aL+4+OLn3170G8TZWKiPbwvqtJtOu+m0m0476bSTTjvpnH1960z4N26D1W467aZz+LhHb1sL -6rSbTrvptJtOu+m0m0676bBZYk9W02I63+erOwn+jdt0taBOu+m0m0676dOGNy2o04I6LajTgjot -qNOCOi2o02I6LaZz9ry689/fuJ2sWlCnBXXYA5qwHR4RHoJGrC112lKnLXXaUqctdQ63t5PU9/nq -Tn1/4zZi7ZTS8iZ9LxGmauYdMt38ZJ8C7gNr8HkGO6O0qk6r6nyfr+5c9zduv5o+PMWNWKvqtKpO -q+q0qk6r6rSqTqvqtKpOq+qwFTK/RO2r8/XVneb+xm3E2lenfXXaV6d9ddpXp311Dk9jb2D76rSv -TvvqtK9O++q0r87XV3eG+5f21WlfnfbVaV+d9tVpX5321WlfnfbVaV+d9tVpX5321WlffcKxdpPq -znB/4zZOLa3T0jotrdPS+oRb1Ey1uU6b67S5TpvrtLlOm+u0uU6b61y7nqyT29+4TVIb7LS5Tpvr -tLlOm+u0uU6b67S5TpvrtLlOm+u0uU6b67S5zrfpXSe3v3EbojbYaXOdNtdpc50212lznTbXaXOd -Ntdpc50212lpnZbWaWmd63t96+de0/I6La3T0jotrdPSOu2r07467avTvjrtq9O+Ou2r07467avT -vjpfX91Z7J9fnsmXi169+WlpnZbWaWmdltZpaZ2W1mlpnZbWaWmdltZpaZ1rP3/WCexfWl6npXVa -Wp++s8+9F6cXVy/uXpBz/u7txd9R21ynzXXaXOfbdKnT1t+4XW8frj5cdIwmqaV1WlqnpXVaWqd9 -ddpXp3112lenfXXaV+frqztP/Y3b/LSvTvvq8PnqcAMbopbWaWmdltZpaZ2W1mlfnfbVaV+d9tX5 -+urOTv9O64u0tE5L67S0TkvrtLROS+u0tE5L67S0TkvrtLROS+u0tE776nx9dSemv9vbJLWlTlvq -tKVOW+q0pU5b6rSlTlvqtKVOW+q0pU5b6vBVIJwx98SGcXte0leEtKpOq+rcOw316g8XHaOZoq8O -z1aDdTNQ56L21Wlfna+v7nT0N24z1b467atPeIobrJbWaWntPWrT1eY6ba4VHrRGrPV1Wl/nWRfp -TPQvrbHTGjutsdP6Oq2vTzuitMNOO+y0w0477LS+zsO/Nl2tr9P6Ot/3gXQm+hu3wWqNndbYaX2d -1tdpfZ3W12l9ndbXaX2d1tdpfZ3W12l9ndbX+b4PpPPP37idmFpjp8112lx3147+X6/eYLW+Tuvr -tL4+4QY2Xe2w0w477bDzfOdR/dxr2mWnXXbaZadddtphh1f8dthph5122GmHnXbYaYcd9pEKD0FT -9X1Jfuefv3GbrhbaaaGddtlph5122GmHnXbYaYeddthph5122GmHnXbYaYed53t96+de0y477bLT -LjvtstMOO+2wT8u8tMhOi+y0yE6L7LTITovstMNO6+t83wfSKedv3MaJb+cPz2Dj1Po6ra/T+jqt -r9P6Oq2v0/o6ba7T5jptrsOs+H0fSOeYf2mDnTbYaXOdNtdpc50212lznTbXaXOdltZpaZ2W1mlp -nZbWaWmddz22c8zfuA1RS+u0tE5L67S0TkvrtLROS+u0tE5L67S0TkvrtLROS+u0tM73PQ2dWf7G -bX5aWqeldVpap6V1WlqnpXVaWqelddpXp3112lenfXXaV6d9db6+ujPL37iNTvvqtK9O++q0r077 -6rSvTvvqtK9O++q0r0776rSvTvvqEx6b77y6n3tNy+u0tE5L67S0TkvrtLROS+u0tE5L67S0Tkvr -tLROS+u0kE1rzXw9YieQv3GbmjZUodKgUuBtO+9j+x70HA7YlzbedPLOiLP/ntK7KQhntt/nqztt -/AunZpyucCLC6xsvY7w28crB1M/cyTzJlMesxO87v9bh1WydrNPG37jND9HtY3TaXKc/kP2/hihb -Q/ZYnYZaZKdFdtphpx122mEn3+tbP/eadtlpl5122WmXndbXaX2d1tdpfZ3W12l9ndbXaX2d1tdp -fZ3W18l+D1Hnib9xmyS+7GNvW+PU+jqtr9P6Oq2v0/o6ra/T+jpsS0ZL0w477bCT7/Wtn3sNbw/a -Zadddtplpx122mGHDruPQ9phpx12wr4nDz/S6zZQLbLzfX91p4a/cfmBBquFdlpop4X2Oft3veVN -V9vstM1O2+y0zU54zLnRTVW+17d+7jVttdNWO22101Y7bbXTIjststMiOy2y0yI7LbLTInt+bbJ7 -OVyKy31N7mdf+6fD311csv9593n4tdnuJWOwATjbzf3aavfSXHLd4brDdYfrDtfdNUWdKXocMUbz -FXa+bs992OLs9/suGU2MJkYTo4nR2NOuZXcvuSXslPptq9B5o0cxY7AJ3c+7mztjmDHMGIcxDmMc -xjgc/3Ddw3UP1z1ct/sRfl/Y3hmkhzkMdDHQxUAXA10MdDEQG0u2C+8lA13ciIvr3lz35rrsr9NK -vJffcbp74K+9eC8Z42aMmzFuxngY42GMhzHY8rideC+57sN12SC3vXgvue4Cb+eVHudljJcxXsZ4 -GYON0NqP95Ik718w0MtA7EvfpryXDBAGCANk58t+rrZ/YqAw0P4vAxHUIahDUIegtjrvJVsdEtAh -oGx7/xsCOgT0+zh3p5nuOdPtodkI9jeklc1pfjP7NwxEWoe0Dmltn94937gR4kYQU/aG/Q3Z/D7d -3QmnhyGjQ0aHjO6+i0NGh4zO7uZrxiCbbBb42933hmwO2Ryy2ZLdPR/gMH8nSadN8wm/FENk5+wG -wQxJZOfsvzIkdVcnnbbT/TODk9chr+xZ+ZvvjWU/kNs/8dkHfpTwfruiEt4hvHPtvzIQ4WUPveyD -QIKHBM9uTUly28v/x38yCfUoJHhI8JDgIcFDgtnE9Dck+Hvynt2alSOQ4CHBQ4Jb0PeS6/LxTCYh -9hxiDBI8u5UfCf4ekb2bhHcI7xDelvbWRi57yQBhAJK732rCzNPDEN4hvG3wz3l2V+zhUlyyBXm/ -sus8+3MXlzeXD5d7Jf6VebZNPkc5nQLYOurHrqA/kWCRYJFgkWCRYJFgkWCRYJFgkWCRYBEk8aTt -d38z5fQ4JFgkmN2+fyLBIsEiwe34Dzsv/kSMRYzl/RsGIMZiihV51fmem+53+BO5Zee6n8ityK2Y -akVa2Xf4J9Iq0ipmVzG7ioCKgIqAioCKl3gmmR6HoLK/+0/MsiKjIqO7farIqJhdRTZFNkU2RTZF -NkU2t/Nn4mRm+de9C/uA7I8zJJEVkRWR1YZin30iKyIrJlu9u0c91yWqIqp697np54L7J8YgsiKy -IrJivhVpFWkVaRVpLQz0kuuSUjPFmoCagJpzASaSf/3T4e8uLtkflIyajJqMmoyacwGTTZNNk02T -TZNNk02TTXMuwMzR45BRk1GTUZNRk1GTUZNREzY2AP2Zx9lk02TTZNNk02TT35PzNAMmo96NTsmo -yajJqMmoyWgV4WTHJKAmoCagJqAmoCag+63jTBk9DEE1QTVBNUE1QS0peF8wvV8CzGhE1tf+NTeH -hcjv/i9D3ft7048Y908MSW7ZkOxncmtya3Jrcmtya3JrcmvmVBPQGsPJPm6k1M8mTZwNmrSatJq0 -mrSatJq0mrSatHrvDROr907uXSClJqVFhxNv0MTJoImsiWwR4sT7v7snLaORW5PbQ24PuT3k9jCx -HvJ6yOshr+f33RvOBg+5PeT2kNtDbtmP9vC0HsLLnty/Q3jP7N8/XDIAE+shtOw/+Tv6nhvOBisS -J7vxMQk+2v9nIBLMbqW/Q4ILEyek8hC5w9PBFnG/wwNxdjDvq6c4Fzwk+JDgQ4LZEux3SPDhrKBK -cbI/SIIPCf7uKqcG7/40ozDPnmt/Z8yZ4CHGhxgfYswejr/DfHuYbw/hPYT3EN7DPHvI7WGePeT1 -kNdDXs93LmBOBQ+5PeT2kNtDbg+5PeT2kNtDbvfEc88aDxPrvtruy+ohqt99+84FzLngIbKHyB4i -e4jsIbKHyB4ie4jsIbKHifWQ0kNKDxPrIaCHgJ7s6405GTwE9SKoF0G9COpFUC+CetEd8gRepPVi -dr1++zcMwJ5xPPoXUb3muzd3M3CR1ou0XqSVXckPc9M1e8loRPYisheRZfO138U8e5HSi5RepPT6 -zgXM6eBFWi/SejHfXgT1IqgXQb0I6m73zdbzv4vcXXv/COhFQC8CehFQtoL8T36j/53z7l8xce/P -MxppvfY+EdSLoF7MsxcB/R4iAnoR0IuAXgS0KMJROB28COpFUC+CehHUi6BeBPUiqBdBvZhYyyTm -26o3QhdRvYjqRVSvZ8/TDmeDVRPv79VFbi9ye5Hbi9xe5PZ69mcYiNxe5PbaR2MPRl4v8nq9+3tz -OB28yO1Fbi9ye5Hbi9xe5PYitxe5vcjtRW4vcnsxsV7k9SavN3m9f5u0w+ngTW5vcstm3b+byN5E -9iay92+3mWcM0npzQnCT0puU3qT0Zk69CSgbG/8nv7M9DkG9CepNUG+CehPUm6DeBPUmqNWVs7/O -Nym9d697UnqT0vWVs7Pa4WzwJrI3kb2J7E1kbyJ7E9mbCN5E9iayN5G9OSG4mU5vAnoT0JuA3mfv -TT+A3T8xBkG9CepNUNmm/ncT1Jugsl4geximUzYS/5Ve9D03RPUmqvd3LnBxOngT2ZvIslfpjw1H -9/34zcR6k9abtLJp++9mYr2ZWG8CehPQm4DeBPT+zgUuzgZvgnoTVLYozz4epPV+9/8ZaJ8O0noz -u96k9Cal994sUnqT0jvfveFkkO24fzdpvUnrTVr5pvadsm8ie/OwPESW/a9/D1PsQ1SrMydn/5eP -1Hz3hVPBh/A+hPchvA/hfQgve1L/SjbdNZz/ZZzZ/7+4ZAAS/DDFPiT30ebs4lTwIcFsrvx7SPBD -gtn4+PeQ4IcTgod3Dmf/moFI8EOCH+bZh9A+hPbhewb4xethCO9DeB/C+xDeh/A+pOrhyXr2Adk7 -tUcjvA/hfZiuHkL7ENrn2jnt5mzwIbwP4X0I70N42f3295Dbh9w+zK4PkX3oBfYwTLEPeX3I60Ne -n+9c4OZssNJz2u6e7A2695IhCe9DeB/Cyx6zv4fwPoSXTWV/D7PrQ2gfQvt8b9VuTgYfcvuQ24fc -smn37yFie1r+kNuH3D7k9iG37M/6e5hdH/L6kFc2nv49+e4NZ4MPsyx7Gf8eIvsSWTY0/b1E9iWy -L7Pry+z6ElC22P2xIe7vJaAvAX0J6PudC9ycD75k9CWjLxl9yehLRl8y+pLRl9n1JZsv2WTj8B8b -j/9esvmSzZds7mal/Er1OMTzJZ4v8Xy9+4EzBvF8mVNfYvkSy5dYvsTyJZYvsXyJ5Ussd/NSfod6 -HOL5kpuXeL6k4SWeL/F8mVNfYvkSy5dYvsTyJZYvsXyJ5UssdzNTfm16HGbUd/c0J5kvyXxJ5suL -/8skykbGv5ccvuTwJYcvOXzJ4UsOX3K4m5vya9PjkMea0tlTzpdQvrzkv4TxJYwvYXwJY33pZO/x -XolEviTyJZFvvnvDGeFLMl+S+ZJM9hX/vSTzZSZ9SeRLIl8SGRIZEhkSGRIZEhkSmd93bzglDMkM -yQzJDMkMyQyv+yGRIZEhkSGRIZEhkaWmsyffIZbRzjgvFeHSwKpAiOdWn1szbvUUXve3dQmx3Hfu -4XX/2r9gALK5b8zyvZd6OSXct0h7Kr6nniGj+4K8r3P7WrKvBztR75wWsrkzVZ+ps28ivgN/8+fL -OWEIaghqCGoIaghqCGquvXa3QGGuyb2X3BJSGlIaUhpSmm/+fDknDGkNaQ1pDWkNaQ1pDWkNaQ1p -ZUfzX0hpmDVDQENAQ0DzvZd6OScMQQ1BDbNmyGj26dyHgYxm7wnZDNkM2QzZDNkM2QzZzPde6uWc -ELga4GqAqwGu2rtyebi8uOy+RPf+4MPlyyUDDH8/DDAMMPvuo592758YaBioQR30ij3re8kYYgwx -hhhD3AhxXXFdcV1xXXFdbT/UT7/PYFaDWfXdbzO6/8tAZiAzkBnIDGQGMjfiMMDhWocbcbjuN3/2 -w/D9E2Mcxti3+ntlBjr8/8VA/RA+0Zzfd8loF7fkYpSLUS5uyder9qPxJ9+PMto+7jc362agm3+9 -GehmoJuBbga6uSU313247sMt4cefTVo/KN8/MdCz/8JADwM9DPQw0MNALwO93IiXG/Fy3Zfrvlz3 -5bov132/e/MQgX2IwhhhjDBGGCOMEcYIY4QxwvFJKWo1qNWgVoNa9RHf4/S8cNCrQa8GvRr0atCr -/odLxiCogNUAVlOw8rs/TkpRq0GtZrZXbe57GNKKXg16NejVoFczpBW1GtRqUKtBrQa1GtRqUKtB -rWboVfmYPbHvYUgrhDUQ1kBYA2HNkFasarCqwaoGqxqsarCqqVX1kusS0Nkuqonvcfg9GTI6BA+c -GnBqwKkBpwacGnBqwKlZnNofIZvg1IBTM/d3d3piOHPvTzEQGQWnBpwacGrAqQGnBpwacGrAqQGn -BpwacGqG90tbQzTbPQwZHTKKUA1CNQjVLPPt3SCoMNVQCA8vDANTzRBQhGoQqpl89+YlAwR1CCpC -NQjVVKjMW7pBqAahGoRqxJwKTk1x6uyxEKpBqGZXnBDsf41Yb5GILEI1CNUgVINQDUI1CNUgVINQ -DUI1CNUgVINQDUI10t6bfoK/f2IM0opQDUI1CNUgVINQDUI1YjoFpwacGnBqwKkBpwacGvGNWsS4 -xyGo4NSAUwNODTg14NSAUwNODTg12pl0/4IBSClCNQjViI3TSXEPQ1rF06y9HmlFqAahGoRqEKpB -qAahGoRqEKpBqAahGoRqtPNnA9zjEFRIaiCpgaQGkhptCggqJDWQ1EBSA0kNJDWQ1EBSI2KpnT+b -3R6HeEJSo40M8YSkBpIaSGogqYGkBpIaSGogqYGkBpIak0jv/NnY/uufLv7u5vLh8uWSMQgjJDWQ -1EBSU5Iy/cXgUoNLDS41bHe7H18nqj0K8QSnBpwacGrAqQGnBpwacGrAqQGnBpwacGrAqQGnBpya -3f6WqPY4xBOXGlxqcKnBpQaXGlxqTCwhqYGkBpIaSGpY2rJnP7jUfC7VpPYwZBSXGlxqcKnBpcbE -08yQaNSgUYNGjXdUYmliCUANADX+5s+uJuifGIN4AlEDRA0QNUDUAFEDRA0QNSaWGNSY7U6ZsfeI -BNTPd29eIkBQ0ahBowaNGjRq0KhBowaNGjRq0KhBowaNGhNQAGoAqPE3f4qzQgxqalBnvh9lINIK -RA0QNUDUAFEDRA0QNUDUHFIKQA0ANWc2a+aksBB1OL2fM3vJQMyfQNQAUQNEDRA1QNQAUQNEzSGg -h4BiT3O++dOcFGJQg0ENBjUY1ByCCj8N/DTw08BPAz/NIaCw08BOAzsN7DTnmz+7MqF/YgyCijwN -8jTI0+ATQzk+oNMcsgk2Ddg0YNOATQM2Ddg053sRNSeGoNOATgM6Deg0Zx9JMgo6Deg0h2yCTQM2 -TbHpZH+QgFaczu4TQB57FNIKPw38NAc23YGJLAY1GNRgUINBDQY1yMYAB0NZPRSZc7bLbwh7GILK -28fZk3UMajCoObDp3gvSCkQNEDVA1ABRcwgoADUA1Fy/vTeHs0IgaoCoAaLmIqgY1GBQg0ENBjUX -0yn8NBfZhJ0GdhrYaS6yec0+N4fTQuRpkKdBngZ5GuRpkKdBngZ5GuRpLrKJOM3F4i2eSthpYKfZ -TXwJXg9DUOGngZ8Gfhr4aeCngZ8Gfhr4aeCnuQjoRUDBpgGbBmya3dSX4PU4BBV0GtBpQKcBnQZ0 -GtBpQKe5mETBpgGbBmwasGmuvVfk7rr31aCLIk7P5M6eyMFPAz/NRWRBpwGdBnSai+n0IqA40+BM -gzMNzjQ401zPd284LcSbBm8avGnwpsGbBm8avGkuJlGcaXCmwZkGZ5o602n9cPhg1Fzb5TdYPQpB -vfbefP/OaAQVeRp0Y5CnQZ7mJqCI01ScvG/IYKeBneZe12+u/vVPL3/HQP32OxqQwaAGgxoMajCo -waAGgxoMam6iij0N9jTY09z67g0nhRjUYFCDQQ0GNTeRhZ8Gfhr4aeCnuZlOEadBnAZxGsRpEKe5 -vc/NxVkh8jTI0yBPgzwN8jTI0yBPgzwN8jQ30yniNIjTIE6DOA3iNPe1Sbs4K0Se5iatoNOATnOT -Vrxp8Ka5SSvONDjT4Exzk817H22yCTHN/exr28VZIdQ0UNNATVNqOtkrEFS8aW6Cum9K9jztJqAQ -00BMAzENxDT3u69tNyeFUNNATQM1DdQ096aHSfTmJZwlQO/GhWziTIMzzU02IaaBmGYXAZGYf/3T -8Hfi0lweLi8uby4fLl8uGYNJFFiah0RiSoMpDaY0z+yrwc1ZIbY0tSXvFAMwzUM8gaUBlgZYGmBp -gKUBlgZYmodYYkrz8EmTe0+jbk4KAaYBmAZgGoBpHjIKLA2wNMDSAEvDYiE+FDHo0qBLgy4NujS7 -Yoi49DAEFWUalGlQpkGZ5iGobNZAUznP3mHmVIhpIKaBmAZiGohpdgERQelhSCvKNCjToEyDMs1D -Wh/SiikNpjTPHpOUYkqDKQ2mNJjS7IIiMtLjEFRsabCleQgqpjSY0mBKgykNpjSY0mBKgykNpjQP -q9T28N95x8NJ4bOPMJMowDQA0zwEtbB0sreXtKJLgy4NujTo0qBLgy4NujS73Ihw/OufHv7u5ZIx -mER5EKCmeYksxDQQ00BMAzENxDQQ00BMAzHNq+/ecE4INQ3UNFDTQE0DNQ3UNC9phZgGYhqIaSCm -gZimxORODAbDZvcmJgo9Shdw02sP8jTI0yBPgzwN8jTI0yBP8xJZxGkQp0GcBnEaxGner4t6OCVE -ngZ5GuRpkKdBngZ5GuRpkKdBnuZlTkWcBnEaxGkQp0Gc5r2/54ZzQuRpkKdBngZ5GuRpkKd594na -+0xaX6ZTxGleUgo2Ddg0YNO82+X3ee9xSGvR6ezZ5UtkQacBnQZ0GtBpXuZUsGnApnn3WqQUbBqw -ad79jPSwumRApwGdBnQa0GlApwGdBnQa0GnCdAo2Ddg0YNOATQM2Ddg0u3MET/G//okxpmfpPCLI -0yBPgzxNCCroNGE6BZsGbBqwacCmAZsGbJpd6MQz3MMQVNBpQKfho5SDNw3eNHjT8Pm+wZkGZxqc -aXCmwZkGZxqcaXbhE89wj0My8abBmwZvGrwJuBm8aZYStszfbn7rdJxptv/EmWa7xd1ngue2hyGe -+557T7v3jdG+0dn3HmES3ROsfQWHmGYn7p3FdlLa+WN/FXffCZ7bHmdvMfGEmgZqGqhpoKYJkyjE -NBDTQEwDMU2J6Wy1iTMNzjT5+huWlwzeNHzf13csMgo6Deg02SGy/8pAzabAJoFNApsENgls0u5K -wZP6r3+6+buHy5dLxhjGGMYYxhjGGMbo/CmcSTiTcCbhTMKZ9PvmT9aXCG8S3iS8SXiT8CbhTcKb -xNoo4UzCmcTaKEFMgpgEMQli0s/7O9oFJv0TY5gxDmMcxjiMcRjjMMZhjMMYh+Mfrnu47sV1L657 -cd1rf0dZZCKASQCTACYBTGJZlDAlYUr6UQU9/PXNQDc34maAmwFuBrgZgC8z4EnsYW4Gehjo4cY8 -jPEwxsMYD2M8jPEwxh7t4bov13257st1X67LlxvwJPY4L2O8jPEyxssYL2OEMcIYYYwwRjh+uG64 -brhuuC5hnD3/FMtMhC0JWxK2JFZECVMSpiRMSZiSMCVhSsKUhCkJUxKmJBZBafb8Uyw0EawkWEks -hhKcJDhJcJKGTzo/+48MRCIxJWFKqikd+EHAksbfvemJoQAmAUwCmMSyKGFKwpSEKQlTEqYkTEmY -kjAlYUrClIQpac53b3pmKGxJ2JKwJbH6SZiSMCVhSqopHWYiAUsClgQsCVgSsKTC0jn5MtDzQlWZ -esloZLTA5HcfdoKKLgldErqk4cvrSQDEpNkGip8jqsO3JvDE9SBEFm9SvcnvPp/kFnTS7FOyd4Lc -4kzCmYQzCWcSK6EEMWm2yxeLTAQ1CWoS/CP6T0FNoqHQ7K3em7rPMbmFmCQmUXRJ6JLQJWm7fLHI -RCiTWAIlgEkAk1gCJWBJwJKAJQFLfK9lL7kukyiwJGBJwJK0n8UTi0wklpryYKBMQpmEMol1UEKX -hC4JXRK6JHRJ6JLQJaFLQpekPf/sc/Hv8LIlqElQk6AmQU2CmsQ6KEFMgpgEMYnvPOMVTziTcCbh -TNqdOHgSehR+ngSzIkp4k/Am4U3Cm8SKKOFMwpmEMwln4juWe8l1yau2i+pz0MMwRBdAUy0KeRLy -JORJyJOQJyFPYm2UECchTkKchDgJcZK2ixKLTIQ8CXmS9gnaR3Efir2ve1NIa+Xp5PtfbgRRhZ0E -Owl20u4yzcPewxBZ+EnlJ529jXvPyC0GJQxKGJQwKGFQYjmUsCdhT8KetJtO81j/658Yg9xiUMKg -hEGJZVGCnwQ/CX4Sy6JUcTLnXIKdBDsJdtLuQc0D3MOQW/hJ8JPgJ8FPgp8EPwl+kvmkCb/nGJRM -eLEnYU/CnmR/96anhMKghEEJgxIGJQxKGJQwKGFQYlmU4CeZeRZ2Euwk2Emwk3aLah7VHoe0wk+C -nwQ/qfxkzj+FQQmDEgYl7yP1/SsDEFDsSdiTdsdqHs8e5i+ozv4oaQWiBEQJiBIQJSBKJq3wk1gE -JdhJsJNgJ8FO8ncuwCoTwU+Cn+R9zAkq/CT4SfCT4CfBT4KfZAIKO4mlT0KchDjpfOcCrDIR8iTk -SciTkCchT0KehDyp8nQ4VRb8JNY/ifVPQpyEOAlx0u5vzWPYwxBU5EnIk5AnIU9CnoQ8CXkS8iRW -PQlxEuIkxEmIkxAn7fex8fD1OMytyJOQJyFPQp6EPKnyZNp2wU+Cn8SqJ8FOgp0EOwl20vnOBQ5n -hPCT4CfBT4KfBD8JfhL8pLPHIZvwk1jrJNhJsJNgJ8FOOvu5fLHQRPCTyk+HtyfCoIRBCYMSBiUW -PAl+Egue2J11vgkKcRILngQ26exn2cVKE4FOAp10+JwJ+UKehDwJeRLyJORJfARViJPOxoJtj4ja -2QG2ixILTUTbJ7oBIU/aU5Y9T9mXh/9/7d4S5EksfRLiJMRJiJMQJyFOun7fveGEEHkS8iTkSciT -kCchT0KehDwJeRJrnYQ4CXES4iTESYiTrm/+ZKGJkCchT0KehDwJeRJrnQQ6CXQSa50ENglsEtgk -sElgk8AmXd4Zh6UmAp0EOgl0Eugk0Emgk0AngU5ihZPAJoFNuvhUCbMF4iTqYF3f/MlKEyFPQp6E -PAl5EvIk5EnIk5AnIU9imZNwJuFMwpmEMwln0rVdvlhrIrxJeJPwJuFNwpuENwlvUr3J+/4SbBLY -JLBJYJOKTc4e95s/WWoi5EnIk5AnIU9CnoQ86dpHhFjiTMKZhDMJZxLOxNaks6todH/zJ2tNhDcJ -bxLeJFY4CWoS1CRWOAliEsQkVjgJXRK6JHRJ6JLQJd3feymWmwhlEsoklEkok1AmscJJ6JLQJaFL -QpeELgldErokdEnoku61ULHeRCiTUCahTEKZhDKJdU1Cl4QuCV0SuiR0SeiS0CVVl87+NpeYehjW -mwhqEtQkqElQk6AmQU1icZMgJkFMgpgEMQliEsQkiEkQk+57f0dZbyKoSVCToCZBTYKaBDWJ1UyC -mAQxCWISq5mELgldErokdEn3fpZELDgRyiSUSSiTUCahTGIhk9AloUtCl4QuCV0SuiR0SSxgErCk -ez9LIpacCGASwKQC08l3hQ7EQiahS0KXhC4JXRK6JHRJ6JLQJaFLevazJGLFiVAmoUxCmYQyCWUS -K5eELomVSwKWBCwJWBKwJGBJwJKAJRWWOA7nhgCTACYBTGLRkjAlYUrClIQpCVMSpiRMSZiSMCWx -WElwkp79jgix4kSwkmAlsWhJcJLgJMFJgpMEJ/HFs73k+IQRSRKSJCRJSJKe/SyzWHEiREksWhKS -JCRJSJKQJCFJQpKEJAlJEpIkJElIkpAkIUl6np3dWHEiREmsUhKSpErSaTv5va3HlIQpCVMSpiRM -SZiSMCW+PuDsa+2z3xEhlpsIYBLAJBYtiUVLwpSEKQlTEqYkTEksVhKcJDhJcJLgJMFJevc7IsSC -E8FKgpXEoiXBSYKTBCcJp9G2IFtWIElCkoQkCUkSkiQkSe9+llksOBGiJBYtCUkSkiQkSUiSKkmn -DYTpp4UpCVMSpiRMSZiSMCW9+0UEYr2JWL4kli+J5UsCkQQiCUQSiCQQSSCSQCSBSAKRBCIJRBKI -pPd7/86CE7F8SSCSQCSBSAKRBCIJRBKIJBBJIJJAJBWRzvYsSJKQJL3f+3cWnLDPVS8ZaB8S0ook -CUkSkiQkSUiSkCQhSUKShCQJSRKSpHfX0okVJ0KUxPIlIUli+ZJAJIFIKiKdPR9EkoQkCUkSkiQk -SUiSkCS93/zJghMhSmL5kpAkIUlCkoQkCUkSkiQkSUiSkCQhSUKShCQJSVL2O3bEghOFlZ/cANYw -CU4Sa5iEJAlJEpIkJElIkpAkIUlCkoQkCUlSvvmTBSdClMTyJSFJQpKEJAlJEpIkJElIkpAkIUlC -koQkCUkSkqR88ycLToQoiZVLfMlpLxmDeCJJYo2ZQCSBSAKRBCIJRBKIJBBpJ7fvA+1iwYkQJfHx -ZyFJQpKEJGlbjX0nv2/A98Rvf1ORJCFJQpKEJCl7nK//DCeHiJJgPSFJQne0QLBt9Pa32/ZtJbe9 -2hZn22ttg7SVyL4zzru/oyw60b6N2hPvfaHGj/QFg1kTP1L96OybnCLS2Xc6SJKRJCNJRpL8++bP -Ljk5vB3y77u8uLy5fLh8uWSgYaBhoGGgZtNwkuEkw0mGk/zbdfVixYlhJcNKZvmS4STDSYaTDCcZ -TjKcZDjJ5STzRtmYkjElY0re79vjpvYwZiAzkBnIDGQGMmMcxjiMcRjjcCMO1z1c93Ddw3UP1935 -0yw5MbZkbMnYklm0ZEzJmJIxJWNKxpSMKRlTMuuUDCcZTjKc5N9+ltksOTGsZFjJrFcynGQ4yXCS -4STDSYaTDCcZTjKcZDjJcJLhJP/YWY5b2eO8jPEyxssYL2O8jPEyxssYL2OEMcLxw3XDdcN1w3XD -ddffzaITw0qGlcxSJcNJhpMMJxlOMpxkOMlwkuEkw0mGkwwnGU7yzJeDnhoaVjKsZFYpGU5yOcn8 -srumdPgtMrBkYMnAklmnZEzJmJIxJc9+L0lvY3939h8YTdws4okpGVMypuRdtBROlRiGgKJLRpeM -Lrm6dPI9Yqw4MdRkqMlQk6EmQ02GmszyJUNMhpgMMRliMsRkiMkQk1m25N1rnZvR45BWlMkok6tM -B5Q2a5iMLhldMrpkli8ZWDKwZGDJs48wUd2t17kBPQyRhZXM8iXDSea80+Wks/MWnGQ4yXCS4STD -SYaTDCcZTvLuxM6hexhyCyuZlUuGkwwnGU4ynGQ4yXCSZ9NAbuEkz959bhGc5N2ZnaP+GzaNm/2W -PrNoyXCSy0nX77vCw+XLJQMRXkzJmJIxJWNKxpSsff9u1psYWzK2ZGzJLFoyi5YMJxlOMpxkOMlw -kstJp7f92hcBYMnAkrUWatabGGAywGSAyQCTWb5kTMmYkjElY0quKZlXTgNLBpYMLLmw5HwJYLWJ -WchkgMkAkwEms5DJmJIxJWNKxpSMKRlTMqZkTMmYkjEl6/6O03NCY0vGlowtmTVMZg2TkSQjSa4k -+eyB934wu8JJhpMMJxlOsvazJGa1ictK176wYktmNZMxJWNKxpSMKRlTMqZkTMmYklnNZDjJcJKV -7968JIDIwkpmNZPhJMNJZjWTkSQjSUaSjCQZSXIlybzvMZxkOMn+ffemp4SGlQwrmSVNhpMMJxlO -Mpxk70hEFk4ynGQ4yXCSWcBkJMnez5KY5SZGlMxCJiNJRpKMJBlJciXpXHtTSCucZDjJrGYykmQk -yUiSvevqzXITI0pmNZORJCNJriRdv30USCucZDjJcJLhJMNJbJ4y7DLStVIMcH33pmeEhpUMK5nV -TIaTDCcZTjKcZFYzGUkykmQkyUiSkSQjSUaSvPvHc41/156lwUpmSZNZ0mQkyUiSkSQjSUaSjCQZ -STKSZCTJSJKRJO928vxs7w4ZZSGTkSQjSUaSjCQZSTKSZCTJSJKRJCNJRpKMJBlJ8m4vz8/2OGSU -NUxGkowkGUkykmQkyUiSkSSzhskgkkEkg0gGkQwi+bBNFD/7r39iDOIJIhlEMohkEMkgkkEkg0gG -kQwiGUQyiGQQySCSzzd/suDELF9yEelceyOJJ5LkStLR3ktmUjjJLGQykmQkyUiSkSQjST7nuzec -EiJK+22B+0Vr+y1f3wnyTmNIkpEkI0lGkowkGUlyJenaU344ybs3Pf/cw5BRWMmsZjKcZFYzGUky -kmQkyUiSkSRXks61zxDZhJMMJ/k8373hhBBWMqxk1jAZTjKcZNYwGUkykmQkyUiSkSQjSUaSjCSZ -tUv+1lKa9SZmDZNBJINIBpEMIhlEMohkEMnUzqbYMqWBeX9l3lp53zXt+d9uDcTf/evlw9+9XDIG -GQWRDCIZRDKIZBDJIJJBJINIBpEMIhlE8m5tz9/1OGQURDKIZBDJIJJBJINIBpEMIhlEMohkEMkg -kkEkg0jere75ux6HZIJIBpEMIhlEMohkEMkgkkEkg0gGkQwimRVLbL81u5zAnx+ZNSfGj4wfGT8y -fmT8yPiR8SPjR8aPjB8ZPzJ+ZPzI+JHxI39+ZNacGD8yfmT8yPiR8SPjR8aPzPfiGToydGToyNCR -Wadk1ikZLfL3vXhmzYlRI6NGRo2MGhk1Mmpk1MiokVEjo0ZGjYwaGTUyq5OMFvn7Xjyz6MSokVEj -o0ZGjYwaGTUyamTUyKiRUSOjRkaNjBqZNUlGi/x9L55ZdWLUyKiRUSOjRkaNjBoZNTJqZNTIqJFR -I6NGRo3MmiSjRb53LbJZdWLUyKiRUSOjRkaNjBoZNXLV6NqyAjoydGToyCxMMguTjBb5/t6/s+zE -qJGrRtc2HdCRoSNDR4aODB0ZOjJ0ZOjI0JGhI0NHZnWS7/u7N5wcQkeGjgwdGToydGToyNCRoSP2 -XRx2qhu2CBq+8XH48rjZb0vYBeSsP+A4nB1CR4aODB0ZOjJ0ZOjI0JGhI0NHho4MHRk6MnRk6Mis -SfK93yVm1p0YOjJrk4waGTUyamTUyKiRUSOjRkaNjBoZNTJqZNYkGS3ys98lZhaeGDUyamTUyKiR -USOjRkaNjBoZNTJqZNTIqJFRI7McyWiRn/38vFl5YtTIqJFRI6NGRo2MGhk1Mmpk1MiokVEjo0ZG -jcwiJKNFfr7XbJaeGDUyamTUyKiRUSOjRkaNjBoZNTJqZNTIqJFRI7P+yGiRv/VHZvGJUSOjRkaN -jBoZNTJqZNTIqJFRI6NGRo1MsWrUyFWj753P873nYfGJoSNDR2YRkgEjA0YGjAwYGTAyYGTAyCxC -MouQjBEZIzJG5G/9kVl8YqzIWJGxImNFxoqMFRkrMlZkrMhYkbEis/TIGBHbvw173My3pUH/2xhg -Rbsgb9f07Cdm94Ot33smrMhYkbEiY0XGisyqI2NExoiMEfndz3yY1SfGiowVmVVHRoiMEBkhMkJk -hMgIkVl1ZGTIyJCRISNDRob87mc+zOoTI0RGiIwQGSEyQmSEyAiRESIjRGaZkZEhI0NGhowMGRny -+3VGLD8xQmSEyAiRESIjRGaZkcEhg0NmmZFBIYNCBoUMChkUMijkd7/L1iw/MThkcMjgkMEhg0MG -hwwOGRwyy4wMChkUMihkUMigkEEh737e/S85II+4kHEh40LGhYwLGRcyK4yMBxkPMh5kPMh4kPEg -40HOdu5m/YlxIeNCxoWMCxkXMi5kXMisMDIrjAwFGQoyFGQoyFCQoSBnzdIsQDEkZEjIkJAhIUNC -hoTM4iJDQYaCDAUZCjIUZCjIUJChIGc/c2xWoBgSMiRkSMiQkCEhQ0JmcZGhIENBhoIMBRkKMhRk -KMhQkL/vrzMrUAwJGRIyJGRIyJCQWVfEZuDDjquz38TGF3nMLuPfz7bu5zn3Y5j78R+8luNwkrgF -8XZaaJDRoL9L/Rd+6vG/f//5H//jf/23//nf3//6H//7//zf//bv/wFQSwMEFAAAAAgAb3CwRP6A -GQQbAwAAlAYAADMAAABwaXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdlcy9jaGFyZGV0L2JpZzVw -cm9iZXIucHmVVE1v2zgQvftXDOqLA7jauIsC2w16kBU7NdayDclGkZNBSyObKEUaJJVA/fV99Fey -jQPs6mQO5z3OezOebvf6R8PRw2RG00kymuUjGk7nyT/0Tm6306Xljmlu5VZqoSgxJZN0lJqfUimB -c103WhbCG0uFkqw9FciJOmfoREsvgbznJ1Zmz5ZMRf4KJ/Jn7F0h9vyKVhrtcLR7Yw8HENPC2GO8 -sCw8l7RpD4xvnxI2cO1bPLXz1EtuaPDly1+guJoeUawUZSHXUcaO7ROXRyWJ0d7KTQOZPXfzNyJE -qbA/aCHV1sqaPhIq9OQNLVq/M/qkH1YpubHCtsG1yjKTM5V/RmF31JqGCqHJcindkR5GeBK6/MNY -4GtTyqoNoUaXkBOq9mxrd/bwYbaiKTtUSg+s2ULNotkoWQA8lQVrxyQc7UPM7V6cGodC8lMhNDag -P7h7RyxxH95+YusQoU/R4PzaibFP6HVP+FA/urkPwBsU3ZJCN+wZGV214EVpSVIfaHfwHj9ACKHP -GCvaMDWOq0b1wYBc+j5ZfpuvlhTPHul7nGXxbPl4h1w4jVv078gk6z1GsCSoskL7FoWDIB1lyTcg -4uFkOlk+hurHk+VslOc0nmcU0yLOlpNkNY0zWqyyxTwfRUQ589niYOY1ky8WV6CsDZws2Qup3FH6 -I9rrUKAqaSeeGG0uWGKiSOAvsm//Vw+V0duDXkBeDL0jWZE2vk/PVmJ2MH1vuguWl/72MfRF1KfP -AyQJ/UOhBTngY1mBeqyMsX0aGudDahoDe/tpMLj9OPjzdkC0yhF576PR7P4/LpVup1NZU1NUb4qd -sI793poN9KN/4U+UNsrLYes5wW3OfnG4PWGwXaTeOo9Rq0Wxk5rPsORwk4eb9HhzhoDmMndhpk+A -odx+vn8Vj7GNWodNdKnOufp1cp6m2FWq0+kUSjh3iB2L612vGZsCiwJjUdF6LbFv1uueY1Wd4uG7 -Doz+nX7JDqdoXZ+kpvT1iureq1LfIN/o/Qnfv75rRe83Aout6BG8yNqyX5+auNai5t/lWfaN1fQh -PPCh8wtQSwMEFAAAAAgAb3CwRJWuRhcCAgAAdQQAADMAAABwaXAvX3ZlbmRvci9yZXF1ZXN0cy9w -YWNrYWdlcy9jaGFyZGV0L2NoYXJkZXRlY3QucHmdU7GO2zAM3fUVbIrANuA6lwNuMZqtHbq26HQ4 -GIpNnYXakiHJSe/vS0p2UjQZDtUki+R7fI/0xw+72bvdUZsdmhNMb6G3Rmw2G/GjdXoKcO5120OQ -v9CDNQjWwWgdgtIDwiRD70GaDhxO1gVOgdCjdtBhwDZgJ9C0ttPm1Qvx9bccpwHrWgigs4W2ly4l -grcjRky+WMJw/BXz1lANZ206e/af9o9Pj/QRemitUbojDoSH6umSfgGoQfpW65vkffUgxDcFxq4i -SNPk7IniXQk6LJI1adJmmgMoZ0fwgaRUIhoUH7QFPbJ0sBOa9Obf/Poo3eupTFUiBRfJ1Wz0CZ2X -Q9JPti4lP9fAlyUghOhQkaE+TkRb01iVs7gSjBzxkEX8rKijfGrtO4bZGZBE7Mj5pfTIV/KFZR7l -kaxeRwNWUTIjViyMUWY43HaSFzGmqNlB0y5oE4sSb6yqFGKXczClzlU7WI9LoUM/D4GQ5ypd46tW -S+A5WxvKXq6YLonJtr6Grb8Z5NZntEg5G1Feat53bln/F+HaT/aSpOLg8b4G2rhUxn1z28t8R6lN -vsyQLBnQ5Lw9BXw+wP4KNdFEQ/7PMsQFKO4x86x4wXlWDPe8r//ylk80lJc357wSMnfMCvprQNU3 -btwlV2VkIHohqPGmYVFNA4cDZE3DspomS1hJo/gDUEsDBBQAAAAIAG9wsET4kMIADgkAAAokAAA5 -AAAAcGlwL192ZW5kb3IvcmVxdWVzdHMvcGFja2FnZXMvY2hhcmRldC9jaGFyZGlzdHJpYnV0aW9u -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 -+f3IJFOmGtm2Ipvl8uykjYqVQpE1zM56lTGKyW2k9okZyECHTBt7n792HihSOorthx85lO/07iYe -gLWK5Oub/oNt96twGOqapv0s3ezG0Wdm55zTxHUefjfhKuDLpr18dlmUN+muTe1dm9JFRxEf9sbU -8t1NznZLszRr12jQMzRG5+Vt45wgbC3NEoWM15slffYwx36c+uXqDhaIFAH3UXg29t8HoWe3d9N5 -bzFfzm+nffozvG42/891K78N5YJLj8s+ndLTQhrzbHrB4f0qVV4qZ+eaf9sc+taOxkmAPPWIPTtF -Gq09arPxH1BLAwQUAAAACABvcLBEKuQuka9TAACaswAAMgAAAHBpcC9fdmVuZG9yL3JlcXVlc3Rz -L3BhY2thZ2VzL2NoYXJkZXQvZXVja3JmcmVxLnB5lN1tr2xJdSDo7/dXpMQXWyrTZ0esl8ixeiSM -cXepMaAC1PJoJKsoLuaqi7rMrcJu5tfPflaCPZLd0gyS03VOZsTeO2LF2xNx8n7ve//x/x5/86P/ -8vlPHj/+/Ic/+snPf/T4mx//9If/7fG/+Oz33n3v8Yvfvn/89NOHf/rwzZdfP3748dfvHx++ffz9 -x//7w9dff3n//Lvf/eGbD199+d3HT4+vvv7w/pvvHl/dn/n+uz8n/fybD999uFP+7ft/fv/1x9+/ -//T4+JvHd/9Bnvfnf/L+u2+/+vL37/9f2X74+M2394+ffv/x0/xwZ/z42cdPr99/9en9l9+9//Xj -V3+cHP/9pb78JK/f//G+1G+/e/zFD//ycT2f587iP/z49x8/+Prrxxc+++3ji/ffvv/0z+9//XqS -H3785rtPH371h/sx/+Lbv/zf7t88Hn//5af/8fjZh6//6dOH3z3+6nHf4XeP7z4+fvbH73778Zs/ -Pf9dVF9/+NWnLz/9Uan95tP7949vP/7mu3+5b+yvH3/8+IfHV19+8/j0/tcfvn1lfxfEd48vv/n1 -f/r46U7/u4+//vCbP/rVH7759f047vq7959+9+2fy/C//OSXjx+///a+08d/ef/N+0/30/zsD7/6 -+sNXd+Iff/jq/Tffvn98+e3j93737W//raT+zo38/E838vi7j3f2U7p//Xj/4X7ftf/5/adv7988 -1vevP1/tTzl+9rjr+i++/M7937X5ewn/8r7pPz6+vmvj059Tqqn/oAz+7VF//fjwzeT727vw7/+4 -c7yf9F/uuHr86v3jD9++/80fvv7szuH+7OO/f/6L//rTX/7i8YOf/MPjv//giy9+8JNf/MNf35+9 -i/p+967AV04ffvf7OwZ//bgf69OX33z3x/vO7wz+/kdf/PC/3il+8Def//jzX/yD2/+7z3/xkx/9 -/OePv/vpF48fPH72gy9+8fkPf/njH3zx+Nkvv/jZT3/+o+8/Hj9///7PZaw0/6NS/tcy/s2d5e8+ -3kX56/ffffnh62//9Oz/cFfwt/cdfv3rx2+//Of3d0V/9f7DHVOPL+9G8vs//v+qxa8/fvNP88B3 -kn8r0b9+fPjN45uP3332+JdPH+7ouePv39Xvncu/1fBnd9h/9f3PHnndH/rym//x9V0HP7+T/92H -39xZ/93XHz9++uzxNx+//c5H//4Hd9q3dV1vf3Xtt+vx+OXP79/8r/73+NFP/vb/Y7fyvXd3xj// -Um3dT/WbTx9/9/jyV2pyvf39HeH/87vH74TS3Tq/vYPkq6//cPcQX9/Pd/cBf7hL+W4gdwH+7vd/ -EG7fvf/qt998/PrjP/1Rptc6j8df/dX//nj7fj/vn1fWn39+rvvnvNa//nxKgrcVf/7F8xkhzVuc -f/vV8zlt+fNfv//y8bd/Dl4t4wsF+vjPr5xyP/7T4y+uv/rTD395/77395fsvrhv937Af5/2m/tD -7udOuHa+/dX93385+a1+/rn1/PH3dy/49X904ce7dz/65Q//2xf/+It/+NnnP/zBj//xbz//+S++ -+PxvfvmLz3/6k3/84gf3/7szq++//evnfvA3P/7RP/788//jR/fv7wsu5fXD3375SdTcEfN//fTT -dDJf/urr94/PHq9U3v/Fx3979z8//uLxf757PK792f1/b/fLevvs2s/67BHn+uzq63m/3L977HU+ -e9Tb87PHupbfXZ89+q3vz73d767nfG599nic/uzdnUP7Wa5vcYfnrs+u67p/t962N+L+r7oT10n/ -dWedb8cb6aW8TAbHzZzJcC7g5b7N++W+/BX3S7mRLclb3hnuks2+b2Tyj3Nn/ViTYrvSZBCfvXs8 -476Ue79/zvszcX/6+ea+en7nHvb9u+77IVZMrsfT3iWwFVcfhfS6o+ed4XXdPz/vp3+c++07azd3 -7g8+2jMqhss1O+QfCjcUZPiItMct1JTKXZDv7tJ10Qgvk07+Va7sQSfDubx0d9XcOaypwTvDavc/ -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 -aLbjYttxse2w0WUtsR1m2U52bLvB237cfpt5Uc7vLGK3/C8ZLBngpO3c13buazv2tR372o59bSe+ -thNf2xmlbZd62/ndb5PNlmJLsaXYUtwhcWe4JdmShCQhSUgSkoQkIUlIEi4SUoQUOW3euymZEyfb -8unKYY/06Xme12fk8PqdHFIO5ZolcblmuWZJUVLccXFnOCPJJKl5ka6la+laupaupWvpZqEz+bfL -tWTtxztC7gyPdEe6WfVNfRyJj8THbR7pjnRHuqfLPSV7SvaU4ikFr98OjGxb+9vW9ba/uG0tbntL -2+x9o8hNoDZs2lY220pjX2OuOR+RjAts66DtvM2ek2gWhhtzbWdutjM3e/6q8E+fO14kFidO1WyH -arZDNduhjjtDceLIzHZkZtt32Y7MbEdmtnMy28J5Ox2zycW2N7kdcNl2lrdthW1XdNu2vzMUJzZl -t42DzZA34934bVsLb8vgbQ21rZe2VdJ+PagCd1RkI6Bty+DOUGDYtd5OhOxrHllg2OrdKG/b5d1g -d9sH2Q6DbDPu7RjItlu07eBuRx/uDMWE0x/bOmjP6Y89jycmbFpv+9Xb/uK2QbLJ97a03xZq2zp6 -c7I9242bcO0XG8x9iQknNq495SUwLOG2Yxob526asS3AtsMTG4BsxwL2nJbYlqDbQYnNVjZL2/a+ -t/MQ23mIbTm7bfVtO2LbWYcNVTd33zaot83n7ZjDnaFIwGPbMYcNoLddme2Yw3bMYds82o45bMcc -tsMN29bitkm6HW7YNm72HG7Ya1ZENT9LJxws47fN6I34t+25bedp28DYqHZjug2RNsDZWGVbKd8Z -CgeL3205uA0Be9qf0wDbLsO2z7bt/m/4vu1Vbpv12+7gXnMzosEG352hcLDhvu21b9vs2zb7to++ -baFvfrRtkm/b4xs5bLtRe7bCc/ISDbbC7wyFg23wbQN8c6FtR3rbAN/0cmOCbcd72/Hedrw3Ud72 -tbcN6m1req/pHGxGb7vP2+7ztrO87SxvO8vbtsW2gbxtG28bxtuG8bbttu26bntv2x7Upm/vtq2a -baNl2z/Zdk223Yltk2AD7c2oN3/ewHmz5s1EN0TY+HATs01j7gwFgXnrNtPaphN7Bp9pLhPddk63 -PdNta3Tb7Nz2dbbNzm3HZdvs3DY77wzVv43ObaNz2+jcNjq3jc5to3Pb4tw2MbdNzI1lN23fIGaD -121bctuRvDNU/3xq86ptf3Ez9ut1XzUv0okBW4Tb7uAm2NvG4LYxuG0JbhuBd4aCwN7Ptqmy7fVt -e33bBt/GPdsG37att23rbTt6247etqN3vcpCDMxm3raZt3n3tpm37eNt+3jbPt62j7ft423yve3e -bbt32+7dJuTblt22Zbdt2b3byGbbqdt4bNuK27biNgHe9sa2vbFtb+yaSrH5tW10bXtc2x7Xnn1h -tXVnKByA0X5RdE3TlkRg2NnaNrU2cdy2ebatrG0ra9vE2vOHNtNO7WTdGYoOuwDXRIL9rG0/a8fc -jRCxJXXhyW1fauPQbV9q25fa9qU2RtuI7M5QnNiT2jaitu2nbftp22radpl2vCRJkteLrAULGtxs -6wKhl375znDWJHbvcOi2b7TtG+3ZsaCj277RjimGuU0BFJOi58OSzed6MhRGc/oqX/8lsVCyc7Pt -2Wx7NtuezbZns+3UbPsz2/7Mtj+z7crs2ZW5pvnYldn2Yra9mG0vZttw2LNZ/zZVJp7su2xbLBu+ -b9sie7Y7SO622yHD+9P2PLb9jW1rY9vV2Mh926zYNiu2zYpts2LbrNi2I3bO3oLqQVLbbsS7TaG3 -nYids99kVaaVb9sR2ybEnp0tUrxnZwsXb3sQ2x7Etgex7UFs2w93hmvellh8IfgNpTdN3sx4w98N -bDeb3eB188LNAjfy25huz0GlnXPHc0sCil9sBLHpw6YD24J1W5xuS9JtpbNnGj6z7ZnBmrFuM9Y7 -Q7E008eZeM1oP4PgjBUazp6GM4Et3LYY2xNjE1QTOxMhaubOUJy8CmjKRpyg9I3S94vS53eCZU5h -vt4VMSR9A/MNzHfOVIR8b6y9ifZm1Bs9b/S8qfPmyhskb468OfJmxpsUb0i8efBmoO82C77mboDw -BsIbCG8MvDHwxsCb+27su7Hvxr6b+G6wu8Huxrl3hkYgkrtJ7ia5G91uaruB7Qa2m9VuVrtR7aUs -Nq/dvPaaAqnpbQjtprEbvG7wusHrBq+buW60utHqRqubiW4mupnoZqKbie6aqQgU3VB0Q9ENRTcU -3VB0Q9Fds4o584Z04gKMbjC6wegGoxuM3hkKDDK6yegmo5uMbjK6yegmo5uMbjK6yegGoxuMbjC6 -weiumYrUFMYUwdyDcCCjm4xuMrrJ6Cajm4xuMrrB6AajG4xuMLprpiJkdJPRTUY3Gd089JrJKxTd -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 -2NSwqWFTw6aGTQ2bGjY1bGrY1LCpYUPDnoOSTQ2bGjY1bGrY1LCpYVPDpoZNDZsaNjVsatjUsKlh -Q8Oeg5JNDZsaNjVsatjUsKlhA8MGhg0MGxg2MGxg2MCwUWGjwkaFd4aqnhU2K2xW2KywWWGzwmaF -zQqbFTYrbFbYqLBRYaPCnq/jbEzYmLAxYWPCxoSNCRsTNiZsTNiYsDFhA8IGhA0IGxD2fB1nE8Im -hE0ImxA2IWxC2ISwCWETwiaEDQgbEDYgbEDYgLDn6zibEDYhbELYhLAJYRPCJoRNCJsQNiFsQNiA -sAFhg7hGbT0Q1LSgIUBbwPasG2elNpO/mQEQwiaETQgbEDYgbEDYgLDZYPcM9HCw4WDDwYaDDQcb -DjYcbDjYcLCJYBPBJoJNBJsINhHsnoGeBjYNbBrYILBBYHPA5oDNAZsDNgdsDtgcsDlgc8BGgN0z -0DPAZoDNAJsBNgJsBNgIsBFgI8BGgI0AGwE2AmwE2AiwewZ6BtgMsBlgM8BGgE3/mv41/Wv61/Sv -6V/Tv6Z/Tf+a/nXPQI//Gv81/mv61/Sv6V/Tv6Z/Tf+a/jX9a/rX9K/pX9O/PjPQ47/Gfw3+Gvw1 -+Gvw1+CvwV+DvwZ/Df4a/DX4a/DX4K/PDPTkr8lfg78Gfw3+Gvw1+Gvw1+CvwV+DvwZ/Df6a+TXz -6zMDPfRr5tfMr5lfM79mfs38mvk182vm18yvmV8zv2Z+zfz6zEAP/Zr5NfNr5tfMr5lfM79mfs38 -mvk182vm18yvmV8zvz4z0EO/Zn7N/Jr5NfNr5tfMr5lfM79mfs38mvk182vm18yvzwz00K+ZXzO/ -Zn7N/Jr5NfNr5tfMr5lfM79mfs38mvk18+vhvsZ9jfsa9zXua9zXuK9xX5O+Jn1N+pr0Nelr0tek -r0lfP2egJ31N+pr0Nelr0tekr0lfk74mfU36mvQ16WvS16SvSV8/Z6AnfU36mvQ16WvS16SvSV+T -viZ9Tfqa9DXpa9LXpK9JX88xxCZ9Tfqa9DXpa9LXpK9JX5O+Jn1N+pr0Nelr0tekr0lfP2egJ31N -+pr0Nelr0tekr0lfQ76GfA35GvI15GvI15CvGV8/Z6CHfA35GvI15GvI13yv+V7zveZ7zfea7zXf -a77XeK/xXs9fbh++d9DeQXsH7R20d9DeQXsH7R20d9DeQXsH7R2yd8jeIXuH7N0ZXpJcklySXJIs -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 -7k8efrJ9/s3j9senF3998er5y+2D1189bi/ebH94/b9fvHz5PP/+9tvvX7348vnb10/bly9fPL56 -u32Z33nv4d2lH7168fZFXvnbx789vnz93ePT9vrr7e0/aDO///Hj2zdfPv/u8f9q9sXrV2/yz6fv -Xj/5Rza8ffL6qd7/8unx+dvHr7YvfrTF/3yr50+09d2Peatv3m4//eBnW7uuyCb+4dff295/+XL7 -lO++2T59fPP49LfHr+pJPnj96u3Tiy++z8f86Zuf/TLf2bY/PH/6t+2TFy//+vTi2+3nW/bw7fb2 -9fbJj2+/ef3qfv4cqpcvvnh6/vQjo/b10+Pj9ub1129/yI79avvx9ffbl89fbU+PX714U83nQLzd -nr/66hevn/L6b19/9eLrH3nr+1df5ePQ67ePT9++eTeG//zxn7bfP77Jnm7//Pjq8Smf5pPvv3j5 -4su8+Pcvvnx89eZxe/5m+4733nzz95H6HR357O7I9rvX2byj+6vt8UV+zr3/9vj0Jt/Z+nvt3d3u -Fp9tOdc/ff6W/udsfseFP8tO/7i9zNl4enfle/9wCP7+pF9tL17Z7Dc59vmPbDAf9Ic0q+2Lx+37 -N49ff//yWbaQ393+9aPP/+WPf/p8e//jP2//+v6nn77/8ed//lV+N0c6P835q5ZefPtdmuBXWz7V -0/NXb3/MjmcDf/jw0w/+Ja94/zcf/f6jz/9M73/30ecff/jZZ9vv/vjp9v72yfuffv7RB3/6/fuf -bp/86dNP/vjZh+9t22ePj++GmMH8R4P8H0P8dTb57escya8e3z5/8fJNPfqfc3rfZAdffrV98/xv -jznNXz6+SIvanucS+e7H/685fPn61V993rzk7wP6q+3F19ur12+fbT88vUjbSev7T7Obrfx9fp+l -0X/53rPtaPml56/+7WVOwWd5+e9efJ1N/+7l69dPz7bfvH7zlq/+4f28du+t7T9vY2/b9qfP8p3/ -6r/tw49/+//oVH7ykA1/+KcPPv9XVsS/f//46ss0y+dfvHyslZYGhHl8/fT62+2LF389th9eP/0b -lvDj9vnzFz88f/Xf0wHlInn+lN3/JL/1mmfLK79/9eWLl/nF//HN27ff/fInP/zww3uPX33/3tsf -fhntF9/mFU+/+Cfu3Xps289//k/b/t7s/Wz5Vj/Od28dKw7eOlp/99aa9Vbb+7zfimvE5MJ9xv3W -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 -X7m+Kn2l+Aes//q2G50/ECx2o0fwLGzLft00cq1FyX8KtOz3VtM73HC9fHjX+glQSwMEFAAAAAgA -b3CwRDmN9A3rQgAAq4wAADMAAABwaXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdlcy9jaGFyZGV0 -L2diMjMxMmZyZXEucHmVnV2PHceRpu/PryhAWMAG2nJlZkRW1RizgCTLHmJkSaBkDLw3BkW2rMZQ -bE2TsqH59RvPk9R6gfEAu7qgyD7n1MmP+HjjjTezP/jgH/+3ffzp7599vn327JNPP//q0+3jz774 -5F+3/+a9H9w+2L7+7n774unhLw9vXrzePnl8db89vN3+8PifD69fv6h/f//9j28eXr549/i0vXz9 -cP/m3fay3vPh7eePPnvz8O6hPvnb+7/ev3784f5pe/x2e/cPnlnv//z+3duXL364/78e+/D45m39 -8+mHxyf/UQ/evnx8Wj9/+XT/4t39q+2bn3zif/2qF08864ef6qu+e7f94pNfbu26znrEP3z7h9tH -r19vz3nv2+35/dv7p7/ev1oz+eTxzbunh29+rGn+4u0v/6l+sm1/ePH079uXD6//8vTw/farrUb4 -bnv3uH3507vvHt+8n38t1euHb55ePP3Eqn37dH+/vX389t3famC/2X56/HF7+eLN9nT/6uHtenwt -xLvtxZtXv358qs9///jq4duf+NGPb17VdBj1u/un79/+vIa///yP22f3b2uk2+/v39w/1Wy+/PGb -1w8v68OfPby8f/P2fnvxdvuBn7397u8r9TsG8tX7gWy/e6zHu7q/2e4f6nW++6/3T2/rJ1v/sP38 -be+feLfVXv/ixTvGX7v5Ax/8ZQ36p+117cbTz5/88B8uwd9n+mp7eONjv6u1r7/UA2uifyuz2r65 -3358e//tj6/v6gn13u3fnn39L1/88evto8//tP3bR8+ff/T513/6Tb23Vrperf1bT3r4/ocywVdb -zerpxZt3P9XA6wF/+PT5J/9Sn/jo42efPfv6T4z+d8++/vzTr77afvfF8+2j7cuPnn/97JM/fvbR -8+3LPz7/8ouvPv1w2766v/95iVnMf7TI/2eJv61Hfv9YK/nq/t2Lh9dv19T/VNv7tgb4+tX23Yu/ -3tc2v7x/KIvaXpSL/PDT/9cevn588xfnWx/5+4L+Znv4dnvz+O5u+9vTQ9lOWd9/2d16yt/3966M -/uWHd1u2etOLN//+urbgq/r47x6+rUf/7vXj49Pd9vHj23e89Q8f1Wf33tr+qzb2tm1//Kp+8t/9 -t336+W//H4PKB7d68O8/7qP1Wra37/CL//ixQsfrn9j3V9vL7148vXiJLb178c3r++WB9TMmWJP7 -jy+eXv382nZXn378fvvuP+cxBw/Oeur2q1/9z23/8Lj4m3+pF9re4/0LV3//Qhv1Qt/j/PmF8/0L -+6wX6pHrhfbhvv/8Qnc4z17dEzp+tmY85TlLvP2zX9dG/voX7Vfv//rL+ulYg3hezl3D/a8ffFPv -Yei/3n4xjswKKPWvX/q4lsfypZ9+qJD4D7/1xTc4Qs//gVWtsT2+KU99+w5/+v7Hl99t31VcM4aU -oz3/7fPbbe3An7/+05fPPvnosz//9tlXXz9/9vEfv372xed/fv5R/c8vv/7+xo8+/uzTP3/17H99 -ynSOuf/8Clvz9ePfN+aft1/c2jza3XbEddcixl0fM+7G1euPcz/qn2Pnn+2uHXv9LGa9ZV71z2z+ -kWWjR78brX7W+553t3pCPbDXE3rmebfFUY8+6t3bVR8ebfb6Wex3sc/9rl31kX7NWX/EcTfy3Ov5 -/O2qp4wzej2wtv1uO+d1F72GtF0H7znOu5zdz9U3XTlr/PVNUSaC29RY82Jw53nXath3/ew1k9iv -u9vWgzfyylGvtPOoSe31pe04go/UzM427rYazl2r9/CsGuteT21Z44qT7+xRz581mFrDMWtmrWZ2 -njXbfs4aZtYTssbVR6/5ONtejxktai0mz29n1pCO+uIxa2Jn1qsM81brVcsS/WQxej1hrxEeo9Z5 -rw/3mjgvnDWuWrlxMYlRc9pGzWnsV31sZ49GfWfNoTZla9nv2jzrq3LUG49RM8usv23JgOvR/er1 -nbWw9TmWhkWquddjZq1r7dFeA042vb7zVpPNemM9NToGhMVEvYkxJLs87yJ4/mjMu5aqM71tq/Ua -9deaTr9qcIPFPTSbGvbo/BH14cb31WhqiXur5cMmygYCi6nR7Gd9Xey1FkdtXu0MTy0LSeZZCznu -bmOvwZbRN76ewTH2E5vAxFtcNb0sOyzHqCe0Mrwx0xXAgLDStQnNzWu1hlmP2QZbXeGmPlcfaQcr -sNcEar9reh37ra+6i9pINph9q2UoE+h8drBI5Va19pjNyWM03Xp022tngiHV9kwMr76Zbe3vHbPe -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 -+6IpmN04zx2oTG9mdGN1hD2WvmPvYslYb0b7PYNGxb+Du17rsuq4/QS5z//Xgcyw6Tsm+caKviMr -+hTduk7pz0737L6Z+gZ1d3KEgLGd5HBj+CNTulN95P+tgj/K9/fG8313bgzIaXlHRU/74w4MlBLx -r1CLvac8cnfzXu075Lf9tM2X/wVQSwMEFAAAAAgAb3CwRJevmg6+VgAA07gAADAAAABwaXAvX3Zl -bmRvci9yZXF1ZXN0cy9wYWNrYWdlcy9jaGFyZGV0L2ppc2ZyZXEucHmVvV2vJbeVpnl/fkUAwgA2 -Kq3e/FqL7EI3IKvkatX4C5KMhvumkJaOyolKZaoyU67W/Prh8zA0HqDcQLcuQnn23mQwyJeLa73r -JeOjj/72f9cvP/vHz397/frzTz/77ZefXb/89e8+/b+v/8VvP3r66Prqz8/X7969+pdXb16+vj59 -+83z9er99Zu3/8+r169f7r+/++6HN6++fvnh7bvr69evnt98uL7ev/n46aein7959eHVLvkPz395 -fv32++d319tvrw9/o879+98+f3j/9cvvn/9/1b56++b9/vPd92/f+ceu+Pr923fn86/fPb/88PzN -9acfrfE/3urlO+r6/sd9qz9/uH726c+vstbcVfzNn398ffL69fUFv31/ffH8/vndX56/OU/y6ds3 -H969+tMP+zF/9v7n/3l/cl2/efnuX6/fv3r9L+9efXf94tot/HB9eHv9/scPf3775n7+3VWvX/3p -3ct3P9Jr3757fr7ev/32w7/vhv399ePbH66vX7653j1/8+r9qX53xIfr5Ztv/tPbd7v8d2+/efXt -j3z0w5tv9uPQ6g/P7757/1Mf/uNv/3D9+vn9bun1j89vnt/tp/n9D396/errXfjXr75+fvP++Xr5 -/vqez97/+a899Ssa8uXdkOtXb3f19u7fX8+v9vfc+y/P797vT676cfnpbneNL6491j97+YH279H8 -noI/343+8Xq9R+PdTyU//ptd8Ncn/eZ69cZq/7z7fv9jV7gf9N83rK4/PV8/vH/+9ofXL3YN+7fX -f//8q//2uz98dX3y2z9e//2TL7745Ldf/fHv9293T+9v9/idml599/2G4DfXfqp3L998+HE3fFfw -m8+++PS/7RKf/PLzX3/+1R9p/a8+/+q3n3355fWr331xfXL9/pMvvvr80z/8+pMvrt//4Yvf/+7L -zz6+ri+fn3/qYjrzb3Xy/9fF3+4qv3u7e/Kb5w8vX71+fx79j3t43+8Gvv7m+vPLvzzvYf76+dVG -1PVyT5Hvf/w/GsPXb9/8i8+7i/y1Q//+evXt9ebthxfXv797tbGz0fcfRnfX8tfxfbFB//XHL65R -9o9evvnX13sIvtzFf/Xq2131r16/ffvuxfXLt+8/8NPffLLLPmopj1+U9ijX9Ycv9yf/q/+uz377 -D/+bRuWjp13xly8ZrP1U3757+9318k8MZH38ZuP7f364vgNIe26+3xj5+vUP2z683s+3LcAPu5P3 -9Ngd+N33PwC2D89f//nN29dv/+VH+/yfXn7/8s0zQ/Lu+d9+eH7z9cb7yz+93ph9+f3Bxu6jP73d -D/vlL/7p8y+t67M/fPqLf/r9MVc/ajLe76l88Pn23Z53H9PeUud1/eIX//V6fJz5WH1/VEf89NEc -WR77o1HqTx+tGm1Q8FH7Tx/l7kgKPvr86aPVW7Hpn3/zjDH6aX4w975g0K7/8lNl13+6flZ+cf/x -8/15qR8PrNkX+zF2J/7Hsm/2j2jSLlhXjL+L+nez/d2MX+wPf27FZZ27f/Xj99vc/q37v9jP+X8B -1s//4Yunp91p//zVH3//+aef/Pqf/+HzL7/64vNf/uGrz3/323/+4pP9v11l+/hBd33655fv6OuN -xX/73TuNF+NwvTg1fPLLX3/2z19+/j8+2yV6i2nFlPnq7V9L/JfrZ0/X1R8vtrEtXOLFVWbdl8Fl -7i/qGi9qqbn/NceLq83yovFZb4/+oj1ivujxiBetxHjRRtklsNwlnvYf5cWurL6ouUvuX+WLUlZ7 -UWuNF+ORDz6b+zZZXtS14kWpfd9m35bPFj/ZbZpjfxFRTr2tPpUale92A6Os/dPdtraHmLvX3bb2 -2Hdd60XJB81v+3e1zl356lwmD9K57JY8+l1vn0/7j/qiPPad9mUX2p9xWVw6l/30vTUuu6LH7rRi -cVp+5UyeYT9hHfbNOPVGf6rlYR/tFsW+cYld725+49K5DC72SHKZL/Zo+a+1i+0OHY89Dm3xmGs/ -iPXOx9P+2KEInp6HXI/dtbunS6dDi3XssaxjF6+z7i/aYz9zoeXZdnP6fqQaa9fS8m7viqfiJ3U/ -y36M4JK0Yz9p36DYP93jAUbqnmo0dYKCxe94rslzTZ5r8lwzrLeUSnttkb9idBePtu+1LwzoouSi -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 -9pN4bLdiXjWiU4u6d91rQQyiahZ/bDvd/r5d1MtHU7RtFno5ZtaD6u57yPDN+Qdxpno9U/FGNarT -q3m/vV3Xc934rJ6rplei6sXGlPUrktSJmciVn4g4aXX3Vro/CVXr62bsr6rrdYnIDiRG8z0mQut6 -rxrM/LU2N6bhvp70o1hrbXRoeTApAlrpQtSN7XalZa8/6A71Qh80rMStEtteLbfrRPeg64pfTq/f -Xny4FofnH8Uvh5eXh+fXH3/SdbWk9VWtP9dTfb/REFwIvaquaoZHPXHdwbvjy6O3usXhq9Oz0+uP -ZvYnp9fnx1dX4uTiUhyK94eX16dHH84OL8X7D5fvL66OD4S4UgoiNsKcEnIQ8VJ3ed9qSS7UUNXr -3i39o1Zvrye4XohV9VVpNc9VrRElKm0im8ffpMN129zZ9eomJNCfRL0UTTsk4qGrNXY0+na0q3sh -/SYa9PODRJRSV6qaL2utgivd/KRe6q5P1m3bJeJV2w+m6rtD3TbNpEyfyzyVQny40iVP/RPH56+/ -k1SezWbLrr0XB/P2fmPUfm9t56GrNjdtt5jNzj+8u7k4uTk6vD5+c3H5UbwU/zl7fXF+ffOX84tf -9LfncnZ8fvHhzduby+Ozm+u3l8dXby/OXusrMk1n7w7/a6pcXzg9P32nu359eH0YXS1mQKr+v6q7 -6q5qKpE9n6+qTvTqb1vVzLV0q9u1Br+RcLO9v9X60tJT1Xwl5kqDtlObTnNGo7mjHqyt23aP2sQH -ddd2j7O/brIj3aPhEvXroMfdm+2liXllSfoPecnf2W66vdxPZntZUuiP5icPvwv7Mldy+y3Xn90n -9zu316S/LpPSfsJPaXvKbWlmP0n7nod3aUsKVlJ4YZlW7uVm5VoWZqK/Rw6/V47f+8rcRM3cS//j -1lFaiTrZlFZikBekBtmhXhlkgW+F/y2t/J2eUi9j8+56zWx9kqjrsbQ/rtS8B4n+n3+5iRI28bn0 -UtuVHMm08K+SSbr0sigtfkuGQhmwWISS0o9QJtxGSGcOo5Jj9Pe+/tkYTQmjJM00SCizSMrsmmSQ -sFuv9LLAys079EByd0iTFn3uPfOfU1vfjYDxZZAodJGBHf7xGP0nyZYwSpYnA4dK/xt8mieEXMk4 -1bWQ9lV4lk19K2fvTl8yLAN6yGwfMY8T6l2JWbzFqAwVqQE3GiiUjAnXsAAQfhaIhUjG9Zf5RaUB -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 -lQVegW2ffUndSOJTUuicD3j5ciG+5Q51iTfM/pZpfJ46q4PhF99kuhvf8/+b98Y8ado8n2CeTfi3 -J8TPc+f5rp/3xObHUn3SWdJjzgt6/uFjVObxd2h1H36oJO+vHpbxPErOsyKmBycu6DFdiAc7swk9 -klEJfdF6eZje0uPrJ7osdQ6BoygptG8AFro8je411n86vjx7O52OZ2/HP3n/8bzfW4+rbY7tkcd2 -/9+xAd4i+1a8JYxVdPTAShJ90dM9kWZ/RN5/AVBLAwQUAAAACABvcLBEnJ63Y/0LAAA9RQAAOgAA -AHBpcC9fdmVuZG9yL3JlcXVlc3RzL3BhY2thZ2VzL2NoYXJkZXQvbGFuZ2N5cmlsbGljbW9kZWwu -cHntW21P3DoW/p5fYV20aiulrO2QeWm1HygXelF500BV9dMqzASIOiRzk9BedrX/fZ+TxLGdeAam -zFWrCiJD4hwfn5fnPHaGydaW+4e9239/eMKODvf2T8732buj070PbInslrfFLm5idpon10kazdle -NotZUrDj7D/JfB7h+vb2Lk2mUZnlbDpP4rRkU8hse2roYZqUCUb+Hn+N59kizll2xUqHTsifxGUx -jRaxoTbJ0gKX+SLLqwsoZmdZXvdP8zgq4xm7vK809qeKctK1uMdUNyV7ufeKifF4BBVO8W22O5+z -CckWbBIXcf41ntWe7GVpmSeXd3DzZfHqDXoYO47yL+wsmV/nyS17zWBhycqMnd2XN1na+I9QzZPL -PMrvKWpXeRyzIrsqv8Gwt+w+u2PTKGV5PEuKWj0CUbIonf0zyzH+NpslV/fUdZfO4A5ZXcb5baFi -+P7kIzuKC1jK3sdpnMObs7vLeTLF4KNkGqdFzKKCLaivuNGROiBDzhtD2EEG9VV037I4wX2a+2uc -F+hhcluo2RqNPkOuX0Yl2Y9sLmjgKxh9z+bIRq5GbjtDoD2dsSSt1N4g9jiBQjj6DbBilzG7K+Kr -u7kPDZBlnw4v/jj9eMF2Tz6zT7uTye7Jxee3kEWkcRf5qzUltwtAcMbgVR6l5T0Mh4Lj/cneHxix -++7w6PDiM1l/cHhxsn9+zg5OJ2yXne1OLg73Ph7tTtjZx8nZ6fn+NmPncaxCTMF0BbkN8RVU3maI -5Cwuo2Re1K5/RnoLGDifsZvoa4w0T+MEiGIRSmRxv1YO51l6XfmLITqgb1lyxdKs9Nm3PAF2gL5e -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 -nCy4XHfTxXtP2/2Brxa22x90isndh9j898HbZ7v41dYnzcXQz4SNJvDtAbuKe6YW/FJxtelu+xwE -vjWhu6D6EbQhdFPj/t3Yna+vlcAXK3PO+rJei8CewfdnUd+9oAvfRmC7XougH1VfixuF5K40fg42 -Ilj3U4I5Z3Y23Pu7+guGr30w79XGLHR1YDjpytty8Dpo3uxs30/Q7yb8KyD6Zeq8l/XLIW1+DXSg -xszVG78z9f7Dp7gqku8Y1X/5YWX5eLOcJ9M4vczLRL04caXeWIU5258M1SsZK6OvUs5Psnv1JveZ -rCpZwOCmWMiBxlfK6jx+lLj90+n1xZsoOpi8ET8F/w6C3ztvmW0ObfM7aX91aEA3y5/KNwriKrg9 -ukvjrzI6pCz/Iw7+A1BLAwQUAAAACABvcLBESz1/XeUKAAALLAAANgAAAHBpcC9fdmVuZG9yL3Jl -cXVlc3RzL3BhY2thZ2VzL2NoYXJkZXQvbGFuZ3RoYWltb2RlbC5wecVabW+jSBL+zq8o7ei0icR6 -6cax44zuQyabzEaXNyUereYjsdsJGgJewDPjPd1/v6eBpqsxTpzMaW8QBvql6qmnqquLDO/e9f+j -D6cfz6/o4vzk9OrulD5cXJ/8i7aMfee9o+mjous8fojTKKGTbK4oLugy+ytOkgjPT0+rNJ5FZZbT -LIlVWtIMYwaemXqexmWMmb+pryrJliqnbEFlj0yMv1JlMYuWiomNs7TAY77M8uoBgukmy+v2Wa6i -Us3pfl1J3FQV5VrWcg1VjyXtneyTmEwOIaJ3+ICOk4Ru9diCblWh8q9qXltykqVlHt+vYOZesX+E -FqLLKP9CN3HykMdP9AsBYUllRjfr8jFLG/tBVRLf51G+1qwtcqWoyBblNwB7T+tsRbMopVzN46IW -DyJKitL5r1mO+U/ZPF6sddMqncMcjbpU+VNhOPx49YkuVAGk9FGlKoc1N6v7JJ5h8kU8U2mhKCpo -qduKR8vUmQZy1wChswziK3bfk4rRr3V/VXmBFpIDYbQ1En2Cr/eiUuOHN5d64j5ArymBN3Izc9BL -gbV0TnFaiX0E97iBQBj6DWFF94pWhVqsEh8SMJb+OJ/+fv1pSsdXn+mP49vb46vp5/cYC6bRC//V -kuKnJUJwTrAqj9JyDeAQcHl6e/I7Zhx/OL84n37W6M/Op1end3d0dn1Lx3RzfDs9P/l0cXxLN59u -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 -siGQKc2AMlW/2GHgUko+NARqQOrOSxaX/gNbi9u4gyrSbuldas42LVQaQInIad/pY8vmyIvKR1tA -cQWTDO9oTlC3TUVqGMnp67U7AnxvILZ45wOL30faoB2wf/273foPUEsDBBQAAAAIADizsEQQsGjl -3QAAAMwBAABNAAAAcGlwL192ZW5kb3IvcmVxdWVzdHMvcGFja2FnZXMvdXJsbGliMy9wYWNrYWdl -cy9zc2xfbWF0Y2hfaG9zdG5hbWUvX19pbml0X18ucHmNjsFKxDAQhu99ih/2UMXSg94EL8oevKhv -ELLJ1AaTTEgnu/bt7aYqVBA2x8nM932S5/sGy9vhbZaRI+7625s6GTIHTJOHC4mz4ImyuMEZLbTP -mXOHoMWMauRJog7U0KehJHiu63VlRcuPY/U8avNRgTxARsJQohG3mKtQI83JIbAtnn6v6tfh+3Dq -lyq1lV8ceab9F7r2vZaMI0XLmSwMp3lb0atF5SlQFF2zLzU3O7ywIBJZsh0ORWDZlDPIxXecRi04 -EVLmo7PUN0pp75XCA67av/C2Q7vFt9fNF1BLAwQUAAAACABvcLBErdScGwQGAADCDgAAVAAAAHBp -cC9fdmVuZG9yL3JlcXVlc3RzL3BhY2thZ2VzL3VybGxpYjMvcGFja2FnZXMvc3NsX21hdGNoX2hv -c3RuYW1lL19pbXBsZW1lbnRhdGlvbi5wea1X/W8TRxD93X/FyFHkO+NcCGkptZRKUYAWiSYRBooK -1Frf7cVbzrun3XWM+9f3ze6dfY4DVG2DRJz9mI83b96O+/3+67mkhfD5fDo3zmuxkElK5VLnXhlN -pTULul77OT6fZvg3Iumc1F6JilZzqWnplL6hyeRl1u/3e70DujRejun1XDkqVSUJv5e6kJY8PF1P -nlOlcqmdJOHCUm4K/m8hXfTGa3XwCGPOF5WaZUQ0974eHx8XJndZ3M6MvTk+PW7MZXO/qHo9taiN -9WRlrzed3krrkMV0Smc0OM2+yx5mjwa9Xl4J5+hCWq9KlQsvn1lrbPJWVMv4MR334JFqHOv1eoUs -aVpox9hMA1RJoUfUwjUCfJ+nK1UVubCFOztpbgOOX/kwwyPy3NiCP3lDr55f0OOTR9+PyMmI8mOE -dtoLt5o0vTGVy5T0ZciSczu2Zc7XDppbR/FW46oJ2Duk+v5j+EuVpI2nQsd4+MdKv7SanovKyejv -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 -b3IvX21hcmtlcmxpYi9tYXJrZXJzLnB5UEsBAhQDFAAAAAgAAHCwRODGB4GiAAAA2QAAACAAAAAA -AAAAAAAAAKSBNZUBAHBpcC9fdmVuZG9yL2NvbG9yYW1hL19faW5pdF9fLnB5UEsBAhQDFAAAAAgA -AHCwRB/HOuT2AQAADwQAABwAAAAAAAAAAAAAAKSBFZYBAHBpcC9fdmVuZG9yL2NvbG9yYW1hL2Fu -c2kucHlQSwECFAMUAAAACAAAcLBEK69H1NYHAAAIGgAAIwAAAAAAAAAAAAAApIFFmAEAcGlwL192 -ZW5kb3IvY29sb3JhbWEvYW5zaXRvd2luMzIucHlQSwECFAMUAAAACAAAcLBE9VOze90BAAARBQAA -IgAAAAAAAAAAAAAApIFcoAEAcGlwL192ZW5kb3IvY29sb3JhbWEvaW5pdGlhbGlzZS5weVBLAQIU -AxQAAAAIAABwsETSt5et/AQAAC8TAAAdAAAAAAAAAAAAAACkgXmiAQBwaXAvX3ZlbmRvci9jb2xv -cmFtYS93aW4zMi5weVBLAQIUAxQAAAAIAABwsETsL7e+9gQAAG4QAAAfAAAAAAAAAAAAAACkgbCn -AQBwaXAvX3ZlbmRvci9jb2xvcmFtYS93aW50ZXJtLnB5UEsBAhQDFAAAAAgAAHCwRC29881dAQAA -RQIAAB8AAAAAAAAAAAAAAKSB46wBAHBpcC9fdmVuZG9yL2Rpc3RsaWIvX19pbml0X18ucHlQSwEC -FAMUAAAACAAAcLBEK/pMBwwoAADblwAAHQAAAAAAAAAAAAAApIF9rgEAcGlwL192ZW5kb3IvZGlz -dGxpYi9jb21wYXQucHlQSwECFAMUAAAACAAAcLBEdIegQ1cuAADyvwAAHwAAAAAAAAAAAAAApIHE -1gEAcGlwL192ZW5kb3IvZGlzdGxpYi9kYXRhYmFzZS5weVBLAQIUAxQAAAAIAABwsEQcmvpilxQA -ABRMAAAcAAAAAAAAAAAAAACkgVgFAgBwaXAvX3ZlbmRvci9kaXN0bGliL2luZGV4LnB5UEsBAhQD -FAAAAAgAAHCwRA7wYvyGLwAAYrcAAB8AAAAAAAAAAAAAAKSBKRoCAHBpcC9fdmVuZG9yL2Rpc3Rs -aWIvbG9jYXRvcnMucHlQSwECFAMUAAAACAAAcLBEzkTrbjAPAAC5NAAAHwAAAAAAAAAAAAAApIHs -SQIAcGlwL192ZW5kb3IvZGlzdGxpYi9tYW5pZmVzdC5weVBLAQIUAxQAAAAIAABwsEQ9Ea4CjQcA -AIoYAAAeAAAAAAAAAAAAAACkgVlZAgBwaXAvX3ZlbmRvci9kaXN0bGliL21hcmtlcnMucHlQSwEC -FAMUAAAACAAAcLBEknQgF5UiAADPjwAAHwAAAAAAAAAAAAAApIEiYQIAcGlwL192ZW5kb3IvZGlz -dGxpYi9tZXRhZGF0YS5weVBLAQIUAxQAAAAIAABwsERCHKoBRAoAANgkAAAgAAAAAAAAAAAAAACk -gfSDAgBwaXAvX3ZlbmRvci9kaXN0bGliL3Jlc291cmNlcy5weVBLAQIUAxQAAAAIAABwsETIkdXI -MQ8AABMwAAAeAAAAAAAAAAAAAACkgXaOAgBwaXAvX3ZlbmRvci9kaXN0bGliL3NjcmlwdHMucHlQ -SwECFAMUAAAACAAAcLBEKIMNd26zAAAAZAEAGwAAAAAAAAAAAAAApIHjnQIAcGlwL192ZW5kb3Iv -ZGlzdGxpYi90MzIuZXhlUEsBAhQDFAAAAAgAAHCwRASX3krkswAAAHIBABsAAAAAAAAAAAAAAKSB -ilEDAHBpcC9fdmVuZG9yL2Rpc3RsaWIvdDY0LmV4ZVBLAQIUAxQAAAAIAABwsETfH6N9tjgAAB7I -AAAbAAAAAAAAAAAAAACkgacFBABwaXAvX3ZlbmRvci9kaXN0bGliL3V0aWwucHlQSwECFAMUAAAA -CAAAcLBE+Bs9LtIYAADUWQAAHgAAAAAAAAAAAAAApIGWPgQAcGlwL192ZW5kb3IvZGlzdGxpYi92 -ZXJzaW9uLnB5UEsBAhQDFAAAAAgAAHCwREPI3+18rgAAAFQBABsAAAAAAAAAAAAAAKSBpFcEAHBp -cC9fdmVuZG9yL2Rpc3RsaWIvdzMyLmV4ZVBLAQIUAxQAAAAIAABwsESQ5nUseLAAAABmAQAbAAAA -AAAAAAAAAACkgVkGBQBwaXAvX3ZlbmRvci9kaXN0bGliL3c2NC5leGVQSwECFAMUAAAACAAAcLBE -HhE/ByIkAABzlQAAHAAAAAAAAAAAAAAApIEKtwUAcGlwL192ZW5kb3IvZGlzdGxpYi93aGVlbC5w -eVBLAQIUAxQAAAAIAABwsEQj72f8wwAAABIBAAApAAAAAAAAAAAAAACkgWbbBQBwaXAvX3ZlbmRv -ci9kaXN0bGliL19iYWNrcG9ydC9fX2luaXRfXy5weVBLAQIUAxQAAAAIAABwsESvii9lzgEAAMsD -AAAlAAAAAAAAAAAAAACkgXDcBQBwaXAvX3ZlbmRvci9kaXN0bGliL19iYWNrcG9ydC9taXNjLnB5 -UEsBAhQDFAAAAAgAAHCwRDSp0cpIHAAAMmQAACcAAAAAAAAAAAAAAKSBgd4FAHBpcC9fdmVuZG9y -L2Rpc3RsaWIvX2JhY2twb3J0L3NodXRpbC5weVBLAQIUAxQAAAAIAABwsERvFgCt9gEAADkKAAAr -AAAAAAAAAAAAAACkgQ77BQBwaXAvX3ZlbmRvci9kaXN0bGliL19iYWNrcG9ydC9zeXNjb25maWcu -Y2ZnUEsBAhQDFAAAAAgAAHCwRBE/SCDoHgAATmkAACoAAAAAAAAAAAAAAKSBTf0FAHBpcC9fdmVu -ZG9yL2Rpc3RsaWIvX2JhY2twb3J0L3N5c2NvbmZpZy5weVBLAQIUAxQAAAAIAABwsEShQzhbY1kA -ANNpAQAoAAAAAAAAAAAAAACkgX0cBgBwaXAvX3ZlbmRvci9kaXN0bGliL19iYWNrcG9ydC90YXJm -aWxlLnB5UEsBAhQDFAAAAAgAAHCwRBTmdiiKAQAAygIAACAAAAAAAAAAAAAAAKSBJnYGAHBpcC9f -dmVuZG9yL2h0bWw1bGliL19faW5pdF9fLnB5UEsBAhQDFAAAAAgAAHCwRMAcJ8T9SQAAMlUBACEA -AAAAAAAAAAAAAKSB7ncGAHBpcC9fdmVuZG9yL2h0bWw1bGliL2NvbnN0YW50cy5weVBLAQIUAxQA -AAAIAABwsESrhnzWj0QAACXJAQAjAAAAAAAAAAAAAACkgSrCBgBwaXAvX3ZlbmRvci9odG1sNWxp -Yi9odG1sNXBhcnNlci5weVBLAQIUAxQAAAAIAABwsEQ6zykMBRQAAMVAAAAgAAAAAAAAAAAAAACk -gfoGBwBwaXAvX3ZlbmRvci9odG1sNWxpYi9paGF0ZXhtbC5weVBLAQIUAxQAAAAIAABwsESYcDVo -jx0AAKx3AAAjAAAAAAAAAAAAAACkgT0bBwBwaXAvX3ZlbmRvci9odG1sNWxpYi9pbnB1dHN0cmVh -bS5weVBLAQIUAxQAAAAIAABwsEQ9/fyrJRMAACxAAAAhAAAAAAAAAAAAAACkgQ05BwBwaXAvX3Zl -bmRvci9odG1sNWxpYi9zYW5pdGl6ZXIucHlQSwECFAMUAAAACAAAcLBEUidz7AsdAACBLAEAIQAA -AAAAAAAAAAAApIFxTAcAcGlwL192ZW5kb3IvaHRtbDVsaWIvdG9rZW5pemVyLnB5UEsBAhQDFAAA -AAgAAHCwRBBkUDo0BAAA8QkAAB0AAAAAAAAAAAAAAKSBu2kHAHBpcC9fdmVuZG9yL2h0bWw1bGli -L3V0aWxzLnB5UEsBAhQDFAAAAAgAAHCwRAAAAAACAAAAAAAAACgAAAAAAAAAAAAAAKSBKm4HAHBp -cC9fdmVuZG9yL2h0bWw1bGliL2ZpbHRlcnMvX19pbml0X18ucHlQSwECFAMUAAAACAAAcLBEIoD7 -xJUAAAAeAQAAJQAAAAAAAAAAAAAApIFybgcAcGlwL192ZW5kb3IvaHRtbDVsaWIvZmlsdGVycy9f -YmFzZS5weVBLAQIUAxQAAAAIAABwsER/DpBOIAEAAHACAAA2AAAAAAAAAAAAAACkgUpvBwBwaXAv -X3ZlbmRvci9odG1sNWxpYi9maWx0ZXJzL2FscGhhYmV0aWNhbGF0dHJpYnV0ZXMucHlQSwECFAMU -AAAACAAAcLBEFXd5OrgCAAC6CgAAMwAAAAAAAAAAAAAApIG+cAcAcGlwL192ZW5kb3IvaHRtbDVs -aWIvZmlsdGVycy9pbmplY3RfbWV0YV9jaGFyc2V0LnB5UEsBAhQDFAAAAAgAAHCwROmk8iyyAwAA -0hAAACQAAAAAAAAAAAAAAKSBx3MHAHBpcC9fdmVuZG9yL2h0bWw1bGliL2ZpbHRlcnMvbGludC5w -eVBLAQIUAxQAAAAIAABwsETRk28IyQYAAAQpAAAsAAAAAAAAAAAAAACkgbt3BwBwaXAvX3ZlbmRv -ci9odG1sNWxpYi9maWx0ZXJzL29wdGlvbmFsdGFncy5weVBLAQIUAxQAAAAIAABwsEQ4r+ShsQAA -AGABAAApAAAAAAAAAAAAAACkgc5+BwBwaXAvX3ZlbmRvci9odG1sNWxpYi9maWx0ZXJzL3Nhbml0 -aXplci5weVBLAQIUAxQAAAAIAABwsERjtVoV0AEAAHYEAAAqAAAAAAAAAAAAAACkgcZ/BwBwaXAv -X3ZlbmRvci9odG1sNWxpYi9maWx0ZXJzL3doaXRlc3BhY2UucHlQSwECFAMUAAAACAAAcLBEAjHD -5BABAADqAQAAKwAAAAAAAAAAAAAApIHegQcAcGlwL192ZW5kb3IvaHRtbDVsaWIvc2VyaWFsaXpl -ci9fX2luaXRfXy5weVBLAQIUAxQAAAAIAABwsEQu98tAjg0AAG0yAAAxAAAAAAAAAAAAAACkgTeD -BwBwaXAvX3ZlbmRvci9odG1sNWxpYi9zZXJpYWxpemVyL2h0bWxzZXJpYWxpemVyLnB5UEsBAhQD -FAAAAAgAAHCwRAAAAAACAAAAAAAAAC0AAAAAAAAAAAAAAKSBFJEHAHBpcC9fdmVuZG9yL2h0bWw1 -bGliL3RyZWVhZGFwdGVycy9fX2luaXRfXy5weVBLAQIUAxQAAAAIAABwsETkXF8QGgIAAH0GAAAo -AAAAAAAAAAAAAACkgWGRBwBwaXAvX3ZlbmRvci9odG1sNWxpYi90cmVlYWRhcHRlcnMvc2F4LnB5 -UEsBAhQDFAAAAAgAAHCwRKBMh+XiBAAATQ0AAC0AAAAAAAAAAAAAAKSBwZMHAHBpcC9fdmVuZG9y -L2h0bWw1bGliL3RyZWVidWlsZGVycy9fX2luaXRfXy5weVBLAQIUAxQAAAAIAABwsES7DKmyQw4A -AI81AAAqAAAAAAAAAAAAAACkge6YBwBwaXAvX3ZlbmRvci9odG1sNWxpYi90cmVlYnVpbGRlcnMv -X2Jhc2UucHlQSwECFAMUAAAACAAAcLBE/E1f75oHAAAVIQAAKAAAAAAAAAAAAAAApIF5pwcAcGlw -L192ZW5kb3IvaHRtbDVsaWIvdHJlZWJ1aWxkZXJzL2RvbS5weVBLAQIUAxQAAAAIAABwsERgz3sn -BAoAAE0xAAAqAAAAAAAAAAAAAACkgVmvBwBwaXAvX3ZlbmRvci9odG1sNWxpYi90cmVlYnVpbGRl -cnMvZXRyZWUucHlQSwECFAMUAAAACAAAcLBEk58l+yUNAADPNgAALwAAAAAAAAAAAAAApIGluQcA -cGlwL192ZW5kb3IvaHRtbDVsaWIvdHJlZWJ1aWxkZXJzL2V0cmVlX2x4bWwucHlQSwECFAMUAAAA -CAAAcLBE2/SxkXgDAAATCQAALAAAAAAAAAAAAAAApIEXxwcAcGlwL192ZW5kb3IvaHRtbDVsaWIv -dHJlZXdhbGtlcnMvX19pbml0X18ucHlQSwECFAMUAAAACAAAcLBE+5/JcuIFAAAHGwAAKQAAAAAA -AAAAAAAApIHZygcAcGlwL192ZW5kb3IvaHRtbDVsaWIvdHJlZXdhbGtlcnMvX2Jhc2UucHlQSwEC -FAMUAAAACAAAcLBEnCvsjAcCAACxBQAAJwAAAAAAAAAAAAAApIEC0QcAcGlwL192ZW5kb3IvaHRt -bDVsaWIvdHJlZXdhbGtlcnMvZG9tLnB5UEsBAhQDFAAAAAgAAHCwRAHKG1TxBAAAERIAACkAAAAA -AAAAAAAAAKSBTtMHAHBpcC9fdmVuZG9yL2h0bWw1bGliL3RyZWV3YWxrZXJzL2V0cmVlLnB5UEsB -AhQDFAAAAAgAAHCwRC0YsNfBAgAA5ggAADAAAAAAAAAAAAAAAKSBhtgHAHBpcC9fdmVuZG9yL2h0 -bWw1bGliL3RyZWV3YWxrZXJzL2dlbnNoaXN0cmVhbS5weVBLAQIUAxQAAAAIAABwsETTdaw6ZwYA -AEcYAAAtAAAAAAAAAAAAAACkgZXbBwBwaXAvX3ZlbmRvci9odG1sNWxpYi90cmVld2Fsa2Vycy9s -eG1sZXRyZWUucHlQSwECFAMUAAAACAAAcLBEdTlHULwCAAD+CAAAKwAAAAAAAAAAAAAApIFH4gcA -cGlwL192ZW5kb3IvaHRtbDVsaWIvdHJlZXdhbGtlcnMvcHVsbGRvbS5weVBLAQIUAxQAAAAIAABw -sESpVd2ohgAAANQAAAAlAAAAAAAAAAAAAACkgUzlBwBwaXAvX3ZlbmRvci9odG1sNWxpYi90cmll -L19faW5pdF9fLnB5UEsBAhQDFAAAAAgAAHCwRPRihZN9AQAAnwMAACIAAAAAAAAAAAAAAKSBFeYH -AHBpcC9fdmVuZG9yL2h0bWw1bGliL3RyaWUvX2Jhc2UucHlQSwECFAMUAAAACAAAcLBEJFWdqaYB -AACaBAAAIwAAAAAAAAAAAAAApIHS5wcAcGlwL192ZW5kb3IvaHRtbDVsaWIvdHJpZS9kYXRyaWUu -cHlQSwECFAMUAAAACAAAcLBES09uzh0CAADvBgAAHwAAAAAAAAAAAAAApIG56QcAcGlwL192ZW5k -b3IvaHRtbDVsaWIvdHJpZS9weS5weVBLAQIUAxQAAAAIADizsEQyWlAKnwMAAEAHAAAgAAAAAAAA -AAAAAACkgRPsBwBwaXAvX3ZlbmRvci9yZXF1ZXN0cy9fX2luaXRfXy5weVBLAQIUAxQAAAAIADiz -sESf+iR0LA8AABA5AAAgAAAAAAAAAAAAAACkgfDvBwBwaXAvX3ZlbmRvci9yZXF1ZXN0cy9hZGFw -dGVycy5weVBLAQIUAxQAAAAIADizsETHhQp9OQQAAPgQAAAbAAAAAAAAAAAAAACkgVr/BwBwaXAv -X3ZlbmRvci9yZXF1ZXN0cy9hcGkucHlQSwECFAMUAAAACAA4s7BE1oYQ7KUHAADrFwAAHAAAAAAA -AAAAAAAApIHMAwgAcGlwL192ZW5kb3IvcmVxdWVzdHMvYXV0aC5weVBLAQIUAxQAAAAIAG9wsETT -UFp+THkCANK0BAAfAAAAAAAAAAAAAACkgasLCABwaXAvX3ZlbmRvci9yZXF1ZXN0cy9jYWNlcnQu -cGVtUEsBAhQDFAAAAAgAb3CwREd/m5JKAQAAIAIAAB0AAAAAAAAAAAAAAKSBNIUKAHBpcC9fdmVu -ZG9yL3JlcXVlc3RzL2NlcnRzLnB5UEsBAhQDFAAAAAgAb3CwRLWyidhLAwAA/AkAAB4AAAAAAAAA -AAAAAKSBuYYKAHBpcC9fdmVuZG9yL3JlcXVlc3RzL2NvbXBhdC5weVBLAQIUAxQAAAAIAG9wsESS -jl9VJhIAAC5BAAAfAAAAAAAAAAAAAACkgUCKCgBwaXAvX3ZlbmRvci9yZXF1ZXN0cy9jb29raWVz -LnB5UEsBAhQDFAAAAAgAOLOwRKDehYmaAgAAVQcAACIAAAAAAAAAAAAAAKSBo5wKAHBpcC9fdmVu -ZG9yL3JlcXVlc3RzL2V4Y2VwdGlvbnMucHlQSwECFAMUAAAACABvcLBEF3b9vIsBAAA0AwAAHQAA -AAAAAAAAAAAApIF9nwoAcGlwL192ZW5kb3IvcmVxdWVzdHMvaG9va3MucHlQSwECFAMUAAAACAA4 -s7BE/EiWu7odAABEZwAAHgAAAAAAAAAAAAAApIFDoQoAcGlwL192ZW5kb3IvcmVxdWVzdHMvbW9k -ZWxzLnB5UEsBAhQDFAAAAAgAOLOwRNnM8+dFGAAAElcAACAAAAAAAAAAAAAAAKSBOb8KAHBpcC9f -dmVuZG9yL3JlcXVlc3RzL3Nlc3Npb25zLnB5UEsBAhQDFAAAAAgAb3CwRLM1G4bDBAAAQAwAACQA -AAAAAAAAAAAAAKSBvNcKAHBpcC9fdmVuZG9yL3JlcXVlc3RzL3N0YXR1c19jb2Rlcy5weVBLAQIU -AxQAAAAIADizsETv87/SCgUAANUNAAAiAAAAAAAAAAAAAACkgcHcCgBwaXAvX3ZlbmRvci9yZXF1 -ZXN0cy9zdHJ1Y3R1cmVzLnB5UEsBAhQDFAAAAAgAOLOwRM8SF3UbGgAABU4AAB0AAAAAAAAAAAAA -AKSBC+IKAHBpcC9fdmVuZG9yL3JlcXVlc3RzL3V0aWxzLnB5UEsBAhQDFAAAAAgAb3CwRI8p6uUz -AAAAPgAAACkAAAAAAAAAAAAAAKSBYfwKAHBpcC9fdmVuZG9yL3JlcXVlc3RzL3BhY2thZ2VzL19f -aW5pdF9fLnB5UEsBAhQDFAAAAAgAb3CwREzU1hONAgAADwUAADEAAAAAAAAAAAAAAKSB2/wKAHBp -cC9fdmVuZG9yL3JlcXVlc3RzL3BhY2thZ2VzL2NoYXJkZXQvX19pbml0X18ucHlQSwECFAMUAAAA -CABvcLBEcZy/MXeRAACiQgEAMQAAAAAAAAAAAAAApIG3/woAcGlwL192ZW5kb3IvcmVxdWVzdHMv -cGFja2FnZXMvY2hhcmRldC9iaWc1ZnJlcS5weVBLAQIUAxQAAAAIAG9wsET+gBkEGwMAAJQGAAAz -AAAAAAAAAAAAAACkgX2RCwBwaXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdlcy9jaGFyZGV0L2Jp -ZzVwcm9iZXIucHlQSwECFAMUAAAACABvcLBEla5GFwICAAB1BAAAMwAAAAAAAAAAAAAApIHplAsA -cGlwL192ZW5kb3IvcmVxdWVzdHMvcGFja2FnZXMvY2hhcmRldC9jaGFyZGV0ZWN0LnB5UEsBAhQD -FAAAAAgAb3CwRPiQwgAOCQAACiQAADkAAAAAAAAAAAAAAKSBPJcLAHBpcC9fdmVuZG9yL3JlcXVl -c3RzL3BhY2thZ2VzL2NoYXJkZXQvY2hhcmRpc3RyaWJ1dGlvbi5weVBLAQIUAxQAAAAIAG9wsEQ5 -iaWfdAQAAM8OAAA7AAAAAAAAAAAAAACkgaGgCwBwaXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdl -cy9jaGFyZGV0L2NoYXJzZXRncm91cHByb2Jlci5weVBLAQIUAxQAAAAIAG9wsET4GeiDWwMAAG4H -AAA2AAAAAAAAAAAAAACkgW6lCwBwaXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdlcy9jaGFyZGV0 -L2NoYXJzZXRwcm9iZXIucHlQSwECFAMUAAAACABvcLBEwNkQNOADAAAOCQAAOwAAAAAAAAAAAAAA -pIEdqQsAcGlwL192ZW5kb3IvcmVxdWVzdHMvcGFja2FnZXMvY2hhcmRldC9jb2RpbmdzdGF0ZW1h -Y2hpbmUucHlQSwECFAMUAAAACABvcLBEIPvdOVECAACFBAAALwAAAAAAAAAAAAAApIFWrQsAcGlw -L192ZW5kb3IvcmVxdWVzdHMvcGFja2FnZXMvY2hhcmRldC9jb21wYXQucHlQSwECFAMUAAAACABv -cLBE/FXAz7YCAAA3BQAAMgAAAAAAAAAAAAAApIH0rwsAcGlwL192ZW5kb3IvcmVxdWVzdHMvcGFj -a2FnZXMvY2hhcmRldC9jb25zdGFudHMucHlQSwECFAMUAAAACABvcLBEqwYmxkwDAAD2BgAANAAA -AAAAAAAAAAAApIH6sgsAcGlwL192ZW5kb3IvcmVxdWVzdHMvcGFja2FnZXMvY2hhcmRldC9jcDk0 -OXByb2Jlci5weVBLAQIUAxQAAAAIAG9wsESmLdI5cQQAAHMMAAAyAAAAAAAAAAAAAACkgZi2CwBw -aXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdlcy9jaGFyZGV0L2VzY3Byb2Jlci5weVBLAQIUAxQA -AAAIAG9wsETWSP3MyQUAAJ8eAAAuAAAAAAAAAAAAAACkgVm7CwBwaXAvX3ZlbmRvci9yZXF1ZXN0 -cy9wYWNrYWdlcy9jaGFyZGV0L2VzY3NtLnB5UEsBAhQDFAAAAAgAb3CwRKx8vEULBQAAXg4AADQA -AAAAAAAAAAAAAKSBbsELAHBpcC9fdmVuZG9yL3JlcXVlc3RzL3BhY2thZ2VzL2NoYXJkZXQvZXVj -anBwcm9iZXIucHlQSwECFAMUAAAACABvcLBEKuQuka9TAACaswAAMgAAAAAAAAAAAAAApIHLxgsA -cGlwL192ZW5kb3IvcmVxdWVzdHMvcGFja2FnZXMvY2hhcmRldC9ldWNrcmZyZXEucHlQSwECFAMU -AAAACABvcLBEBbPG5BkDAACLBgAANAAAAAAAAAAAAAAApIHKGgwAcGlwL192ZW5kb3IvcmVxdWVz -dHMvcGFja2FnZXMvY2hhcmRldC9ldWNrcnByb2Jlci5weVBLAQIUAxQAAAAIAG9wsEQlT6MU30AA -ADiIAAAyAAAAAAAAAAAAAACkgTUeDABwaXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdlcy9jaGFy -ZGV0L2V1Y3R3ZnJlcS5weVBLAQIUAxQAAAAIAG9wsEQtojhUGQMAAIwGAAA0AAAAAAAAAAAAAACk -gWRfDABwaXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdlcy9jaGFyZGV0L2V1Y3R3cHJvYmVyLnB5 -UEsBAhQDFAAAAAgAb3CwRDmN9A3rQgAAq4wAADMAAAAAAAAAAAAAAKSBz2IMAHBpcC9fdmVuZG9y -L3JlcXVlc3RzL3BhY2thZ2VzL2NoYXJkZXQvZ2IyMzEyZnJlcS5weVBLAQIUAxQAAAAIAG9wsEQf -tdv2GgMAAJEGAAA1AAAAAAAAAAAAAACkgQumDABwaXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdl -cy9jaGFyZGV0L2diMjMxMnByb2Jlci5weVBLAQIUAxQAAAAIAG9wsETjkq0maxIAAC80AAA1AAAA -AAAAAAAAAACkgXipDABwaXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdlcy9jaGFyZGV0L2hlYnJl -d3Byb2Jlci5weVBLAQIUAxQAAAAIAG9wsESXr5oOvlYAANO4AAAwAAAAAAAAAAAAAACkgTa8DABw -aXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdlcy9jaGFyZGV0L2ppc2ZyZXEucHlQSwECFAMUAAAA -CABvcLBE/nlR3RMTAACgSgAALwAAAAAAAAAAAAAApIFCEw0AcGlwL192ZW5kb3IvcmVxdWVzdHMv -cGFja2FnZXMvY2hhcmRldC9qcGNudHgucHlQSwECFAMUAAAACABvcLBESFIJjAwLAADwMQAAOwAA -AAAAAAAAAAAApIGiJg0AcGlwL192ZW5kb3IvcmVxdWVzdHMvcGFja2FnZXMvY2hhcmRldC9sYW5n -YnVsZ2FyaWFubW9kZWwucHlQSwECFAMUAAAACABvcLBEnJ63Y/0LAAA9RQAAOgAAAAAAAAAAAAAA -pIEHMg0AcGlwL192ZW5kb3IvcmVxdWVzdHMvcGFja2FnZXMvY2hhcmRldC9sYW5nY3lyaWxsaWNt -b2RlbC5weVBLAQIUAxQAAAAIAG9wsEQRIUMdZQkAAFQxAAA3AAAAAAAAAAAAAACkgVw+DQBwaXAv -X3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdlcy9jaGFyZGV0L2xhbmdncmVla21vZGVsLnB5UEsBAhQD -FAAAAAgAb3CwRB+iewqeCQAANiwAADgAAAAAAAAAAAAAAKSBFkgNAHBpcC9fdmVuZG9yL3JlcXVl -c3RzL3BhY2thZ2VzL2NoYXJkZXQvbGFuZ2hlYnJld21vZGVsLnB5UEsBAhQDFAAAAAgAb3CwRFC3 -mn7HCgAA+DAAADsAAAAAAAAAAAAAAKSBClINAHBpcC9fdmVuZG9yL3JlcXVlc3RzL3BhY2thZ2Vz -L2NoYXJkZXQvbGFuZ2h1bmdhcmlhbm1vZGVsLnB5UEsBAhQDFAAAAAgAb3CwREs9f13lCgAACywA -ADYAAAAAAAAAAAAAAKSBKl0NAHBpcC9fdmVuZG9yL3JlcXVlc3RzL3BhY2thZ2VzL2NoYXJkZXQv -bGFuZ3RoYWltb2RlbC5weVBLAQIUAxQAAAAIAG9wsEQWCld5fAYAAHkUAAA1AAAAAAAAAAAAAACk -gWNoDQBwaXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdlcy9jaGFyZGV0L2xhdGluMXByb2Jlci5w -eVBLAQIUAxQAAAAIAG9wsESlnHFznQQAAMQMAAA4AAAAAAAAAAAAAACkgTJvDQBwaXAvX3ZlbmRv -ci9yZXF1ZXN0cy9wYWNrYWdlcy9jaGFyZGV0L21iY2hhcnNldHByb2Jlci5weVBLAQIUAxQAAAAI -AG9wsETOjI87WQMAAK8HAAA4AAAAAAAAAAAAAACkgSV0DQBwaXAvX3ZlbmRvci9yZXF1ZXN0cy9w -YWNrYWdlcy9jaGFyZGV0L21iY3Nncm91cHByb2Jlci5weVBLAQIUAxQAAAAIAG9wsESPW1KXWAwA -AJhMAAAvAAAAAAAAAAAAAACkgdR3DQBwaXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdlcy9jaGFy -ZGV0L21iY3NzbS5weVBLAQIUAxQAAAAIAG9wsET6GhW4egYAALkSAAA4AAAAAAAAAAAAAACkgXmE -DQBwaXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdlcy9jaGFyZGV0L3NiY2hhcnNldHByb2Jlci5w -eVBLAQIUAxQAAAAIAG9wsESk3OW/JAQAANsMAAA4AAAAAAAAAAAAAACkgUmLDQBwaXAvX3ZlbmRv -ci9yZXF1ZXN0cy9wYWNrYWdlcy9jaGFyZGV0L3NiY3Nncm91cHByb2Jlci5weVBLAQIUAxQAAAAI -AG9wsET2XU4VBAUAAJYOAAAzAAAAAAAAAAAAAACkgcOPDQBwaXAvX3ZlbmRvci9yZXF1ZXN0cy9w -YWNrYWdlcy9jaGFyZGV0L3NqaXNwcm9iZXIucHlQSwECFAMUAAAACABvcLBEnb2s71QHAACvGgAA -OgAAAAAAAAAAAAAApIEYlQ0AcGlwL192ZW5kb3IvcmVxdWVzdHMvcGFja2FnZXMvY2hhcmRldC91 -bml2ZXJzYWxkZXRlY3Rvci5weVBLAQIUAxQAAAAIAG9wsERf+yBrHgQAAFwKAAAzAAAAAAAAAAAA -AACkgcScDQBwaXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdlcy9jaGFyZGV0L3V0Zjhwcm9iZXIu -cHlQSwECFAMUAAAACABvcLBEBlNsVEADAAClBgAAMQAAAAAAAAAAAAAApIEzoQ0AcGlwL192ZW5k -b3IvcmVxdWVzdHMvcGFja2FnZXMvdXJsbGliMy9fX2luaXRfXy5weVBLAQIUAxQAAAAIADizsER6 -THyG8QgAAJ0ZAAA1AAAAAAAAAAAAAACkgcKkDQBwaXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdl -cy91cmxsaWIzL19jb2xsZWN0aW9ucy5weVBLAQIUAxQAAAAIADizsEQN07ZsKwgAAIUZAAAzAAAA -AAAAAAAAAACkgQauDQBwaXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdlcy91cmxsaWIzL2Nvbm5l -Y3Rpb24ucHlQSwECFAMUAAAACAA4s7BEEY6N6yQeAAAYaQAANwAAAAAAAAAAAAAApIGCtg0AcGlw -L192ZW5kb3IvcmVxdWVzdHMvcGFja2FnZXMvdXJsbGliMy9jb25uZWN0aW9ucG9vbC5weVBLAQIU -AxQAAAAIADizsETIZvGsYgQAACQNAAAzAAAAAAAAAAAAAACkgfvUDQBwaXAvX3ZlbmRvci9yZXF1 -ZXN0cy9wYWNrYWdlcy91cmxsaWIzL2V4Y2VwdGlvbnMucHlQSwECFAMUAAAACAA4s7BEdci2ro0H -AABYFwAALwAAAAAAAAAAAAAApIGu2Q0AcGlwL192ZW5kb3IvcmVxdWVzdHMvcGFja2FnZXMvdXJs -bGliMy9maWVsZHMucHlQSwECFAMUAAAACABvcLBE/rsY4NcDAADQCQAAMQAAAAAAAAAAAAAApIGI -4Q0AcGlwL192ZW5kb3IvcmVxdWVzdHMvcGFja2FnZXMvdXJsbGliMy9maWxlcG9zdC5weVBLAQIU -AxQAAAAIAG9wsETDaxKCCgsAABEjAAA0AAAAAAAAAAAAAACkga7lDQBwaXAvX3ZlbmRvci9yZXF1 -ZXN0cy9wYWNrYWdlcy91cmxsaWIzL3Bvb2xtYW5hZ2VyLnB5UEsBAhQDFAAAAAgAb3CwRLDTBYPA -BwAAsBYAADAAAAAAAAAAAAAAAKSBCvENAHBpcC9fdmVuZG9yL3JlcXVlc3RzL3BhY2thZ2VzL3Vy -bGxpYjMvcmVxdWVzdC5weVBLAQIUAxQAAAAIADizsEQaw1aiqAwAAGsoAAAxAAAAAAAAAAAAAACk -gRj5DQBwaXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdlcy91cmxsaWIzL3Jlc3BvbnNlLnB5UEsB -AhQDFAAAAAgAb3CwRAAAAAACAAAAAAAAADkAAAAAAAAAAAAAAKSBDwYOAHBpcC9fdmVuZG9yL3Jl -cXVlc3RzL3BhY2thZ2VzL3VybGxpYjMvY29udHJpYi9fX2luaXRfXy5weVBLAQIUAxQAAAAIAG9w -sERTZ5urIQYAAIUSAAA5AAAAAAAAAAAAAACkgWgGDgBwaXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNr -YWdlcy91cmxsaWIzL2NvbnRyaWIvbnRsbXBvb2wucHlQSwECFAMUAAAACAA4s7BE9CcpqHURAADu -OgAAOgAAAAAAAAAAAAAApIHgDA4AcGlwL192ZW5kb3IvcmVxdWVzdHMvcGFja2FnZXMvdXJsbGli -My9jb250cmliL3B5b3BlbnNzbC5weVBLAQIUAxQAAAAIAG9wsES/1A0gPwAAAEoAAAA6AAAAAAAA -AAAAAACkga0eDgBwaXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdlcy91cmxsaWIzL3BhY2thZ2Vz -L19faW5pdF9fLnB5UEsBAhQDFAAAAAgAb3CwRIKG6QbxCgAA6CIAAD4AAAAAAAAAAAAAAKSBRB8O -AHBpcC9fdmVuZG9yL3JlcXVlc3RzL3BhY2thZ2VzL3VybGxpYjMvcGFja2FnZXMvb3JkZXJlZF9k -aWN0LnB5UEsBAhQDFAAAAAgAb3CwRNNYHrFcDgAAbC0AADUAAAAAAAAAAAAAAKSBkSoOAHBpcC9f -dmVuZG9yL3JlcXVlc3RzL3BhY2thZ2VzL3VybGxpYjMvcGFja2FnZXMvc2l4LnB5UEsBAhQDFAAA -AAgAOLOwRBCwaOXdAAAAzAEAAE0AAAAAAAAAAAAAAKSBQDkOAHBpcC9fdmVuZG9yL3JlcXVlc3Rz -L3BhY2thZ2VzL3VybGxpYjMvcGFja2FnZXMvc3NsX21hdGNoX2hvc3RuYW1lL19faW5pdF9fLnB5 -UEsBAhQDFAAAAAgAb3CwRK3UnBsEBgAAwg4AAFQAAAAAAAAAAAAAAKSBiDoOAHBpcC9fdmVuZG9y -L3JlcXVlc3RzL3BhY2thZ2VzL3VybGxpYjMvcGFja2FnZXMvc3NsX21hdGNoX2hvc3RuYW1lL19p -bXBsZW1lbnRhdGlvbi5weVBLAQIUAxQAAAAIADizsERbPXAsawEAAG4CAAA2AAAAAAAAAAAAAACk -gf5ADgBwaXAvX3ZlbmRvci9yZXF1ZXN0cy9wYWNrYWdlcy91cmxsaWIzL3V0aWwvX19pbml0X18u -cHlQSwECFAMUAAAACAA4s7BEyvO3eUQCAABEBQAAOAAAAAAAAAAAAAAApIG9Qg4AcGlwL192ZW5k -b3IvcmVxdWVzdHMvcGFja2FnZXMvdXJsbGliMy91dGlsL2Nvbm5lY3Rpb24ucHlQSwECFAMUAAAA -CAA4s7BExIOf9aACAACEBwAANQAAAAAAAAAAAAAApIFXRQ4AcGlwL192ZW5kb3IvcmVxdWVzdHMv -cGFja2FnZXMvdXJsbGliMy91dGlsL3JlcXVlc3QucHlQSwECFAMUAAAACAA4s7BEWK8zpssAAABi -AQAANgAAAAAAAAAAAAAApIFKSA4AcGlwL192ZW5kb3IvcmVxdWVzdHMvcGFja2FnZXMvdXJsbGli -My91dGlsL3Jlc3BvbnNlLnB5UEsBAhQDFAAAAAgAOLOwRFqx0uLdBQAAixAAADIAAAAAAAAAAAAA -AKSBaUkOAHBpcC9fdmVuZG9yL3JlcXVlc3RzL3BhY2thZ2VzL3VybGxpYjMvdXRpbC9zc2xfLnB5 -UEsBAhQDFAAAAAgAOLOwRNs0Nj//CgAAFCQAADUAAAAAAAAAAAAAAKSBlk8OAHBpcC9fdmVuZG9y -L3JlcXVlc3RzL3BhY2thZ2VzL3VybGxpYjMvdXRpbC90aW1lb3V0LnB5UEsBAhQDFAAAAAgAOLOw -RKLqtXpHBgAAsRAAADEAAAAAAAAAAAAAAKSB6FoOAHBpcC9fdmVuZG9yL3JlcXVlc3RzL3BhY2th -Z2VzL3VybGxpYjMvdXRpbC91cmwucHlQSwECFAMUAAAACAB3g8RE2T4ie7cFAACsDgAAHgAAAAAA -AAAAAAAApIF+YQ4AcGlwL2JhY2t3YXJkY29tcGF0L19faW5pdF9fLnB5UEsBAhQDFAAAAAgAd4PE -RCPPCpCjAgAAvAgAABgAAAAAAAAAAAAAAKSBcWcOAHBpcC9jb21tYW5kcy9fX2luaXRfXy5weVBL -AQIUAxQAAAAIAHeDxESTevD34QIAAPsGAAAWAAAAAAAAAAAAAACkgUpqDgBwaXAvY29tbWFuZHMv -YnVuZGxlLnB5UEsBAhQDFAAAAAgAd4PERJHJw/W+AgAALgcAABoAAAAAAAAAAAAAAKSBX20OAHBp -cC9jb21tYW5kcy9jb21wbGV0aW9uLnB5UEsBAhQDFAAAAAgAd4PEROKZwWZ0BQAAOBIAABYAAAAA -AAAAAAAAAKSBVXAOAHBpcC9jb21tYW5kcy9mcmVlemUucHlQSwECFAMUAAAACAAAcLBEG2x//ZIB -AACfAwAAFAAAAAAAAAAAAAAApIH9dQ4AcGlwL2NvbW1hbmRzL2hlbHAucHlQSwECFAMUAAAACAB3 -g8REyYA+A6IMAACWMQAAFwAAAAAAAAAAAAAApIHBdw4AcGlwL2NvbW1hbmRzL2luc3RhbGwucHlQ -SwECFAMUAAAACAB3g8REVy3HGr8GAACeGgAAFAAAAAAAAAAAAAAApIGYhA4AcGlwL2NvbW1hbmRz -L2xpc3QucHlQSwECFAMUAAAACAB3g8REGxs4FZ4GAACAEgAAFgAAAAAAAAAAAAAApIGJiw4AcGlw -L2NvbW1hbmRzL3NlYXJjaC5weVBLAQIUAxQAAAAIAHeDxERbTyrz0AMAAM8KAAAUAAAAAAAAAAAA -AACkgVuSDgBwaXAvY29tbWFuZHMvc2hvdy5weVBLAQIUAxQAAAAIAHeDxESiDCG+NQMAAJsIAAAZ -AAAAAAAAAAAAAACkgV2WDgBwaXAvY29tbWFuZHMvdW5pbnN0YWxsLnB5UEsBAhQDFAAAAAgAAHCw -RKO124N+AAAAuQAAABUAAAAAAAAAAAAAAKSByZkOAHBpcC9jb21tYW5kcy91bnppcC5weVBLAQIU -AxQAAAAIAHeDxES64m82HQgAAJgcAAAVAAAAAAAAAAAAAACkgXqaDgBwaXAvY29tbWFuZHMvd2hl -ZWwucHlQSwECFAMUAAAACAB3g8REEhf9dEENAADlOQAAEwAAAAAAAAAAAAAApIHKog4AcGlwL2Nv -bW1hbmRzL3ppcC5weVBLAQIUAxQAAAAIAHeDxESq/0uPSAoAACwiAAATAAAAAAAAAAAAAACkgTyw -DgBwaXAvdmNzL19faW5pdF9fLnB5UEsBAhQDFAAAAAgAd4PERKV+QEFTBgAATxMAABEAAAAAAAAA -AAAAAKSBtboOAHBpcC92Y3MvYmF6YWFyLnB5UEsBAhQDFAAAAAgAd4PERC+aeQ29CQAA2h4AAA4A -AAAAAAAAAAAAAKSBN8EOAHBpcC92Y3MvZ2l0LnB5UEsBAhQDFAAAAAgAd4PERN5SR1BfBgAAvBYA -ABQAAAAAAAAAAAAAAKSBIMsOAHBpcC92Y3MvbWVyY3VyaWFsLnB5UEsBAhQDFAAAAAgAd4PERPES -jDSWDAAAkCkAABUAAAAAAAAAAAAAAKSBsdEOAHBpcC92Y3Mvc3VidmVyc2lvbi5weVBLBQYAAAAA -uQC5AM88AAB63g4AAAA= - -""" - -import base64 -import os.path -import pkgutil -import shutil import sys -import tempfile - - -def bootstrap(tmpdir=None): - # Import pip so we can use it to install pip and maybe setuptools too - import pip - - # We always want to install pip - packages = ["pip"] - - # Check if the user has requested us not to install setuptools - if "--no-setuptools" in sys.argv or os.environ.get("PIP_NO_SETUPTOOLS"): - args = [x for x in sys.argv[1:] if x != "--no-setuptools"] - else: - args = sys.argv[1:] - - # We want to see if setuptools is available before attempting to - # install it - try: - import setuptools - except ImportError: - packages += ["setuptools"] - - delete_tmpdir = False - try: - # Create a temporary directory to act as a working directory if we were - # not given one. - if tmpdir is None: - tmpdir = tempfile.mkdtemp() - delete_tmpdir = True - - # We need to extract the SSL certificates from requests so that they - # can be passed to --cert - cert_path = os.path.join(tmpdir, "cacert.pem") - with open(cert_path, "wb") as cert: - cert.write(pkgutil.get_data("pip._vendor.requests", "cacert.pem")) - - # Use an environment variable here so that users can still pass - # --cert via sys.argv - os.environ.setdefault("PIP_CERT", cert_path) - - # Execute the included pip and use it to install the latest pip and - # setuptools from PyPI - sys.exit(pip.main(["install", "--upgrade"] + packages + args)) - finally: - # Remove our temporary directory - if delete_tmpdir and tmpdir: - shutil.rmtree(tmpdir, ignore_errors=True) def main(): - tmpdir = None - try: - # Create a temporary working directory - tmpdir = tempfile.mkdtemp() - - # Unpack the zipfile into the temporary directory - pip_zip = os.path.join(tmpdir, "pip.zip") - with open(pip_zip, "wb") as fp: - fp.write(base64.decodestring(ZIPFILE)) - - # Add the zipfile to sys.path so that we can import it - sys.path = [pip_zip] + sys.path - - # Run the bootstrap - bootstrap(tmpdir=tmpdir) - finally: - # Clean up our temporary working directory - if tmpdir: - shutil.rmtree(tmpdir, ignore_errors=True) + sys.exit( + "You're using an outdated location for the get-pip.py script, please " + "use the one available from https://bootstrap.pypa.io/get-pip.py" + ) if __name__ == "__main__": diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 00000000000..e128741fefa --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1,9 @@ +freezegun +pretend +pytest +pytest-catchlog +pytest-timeout +pytest-xdist +mock<1.1 +scripttest>=1.3 +https://github.com/pypa/virtualenv/archive/master.zip#egg=virtualenv diff --git a/docs/Makefile b/docs/Makefile index e4de9f847c4..9f08edea7e7 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -7,12 +7,19 @@ SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @@ -27,14 +34,20 @@ help: @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: - -rm -rf $(BUILDDIR)/* + rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @@ -72,17 +85,17 @@ qthelp: @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-compressor.qhcp" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pip-installer.qhcp" @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-compressor.qhc" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pip-installer.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/django-compressor" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-compressor" + @echo "# mkdir -p $$HOME/.local/share/devhelp/pip-installer" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pip-installer" @echo "# devhelp" epub: @@ -100,7 +113,13 @@ latex: latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." - make -C $(BUILDDIR)/latex all-pdf + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: @@ -113,6 +132,24 @@ man: @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @@ -128,3 +165,13 @@ doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/__init__.py b/docs/__init__.py index 5c26cb21551..b430650996d 100644 --- a/docs/__init__.py +++ b/docs/__init__.py @@ -1 +1 @@ -#docs module +# docs module diff --git a/docs/conf.py b/docs/conf.py index 768e5dafd99..3a55481b642 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -3,7 +3,7 @@ # pip documentation build configuration file, created by # sphinx-quickstart on Tue Apr 22 22:08:49 2008 # -# This file is execfile()d with the current directory set to its containing dir. +# This file is execfile()d with the current directory set to its containing dir # # Note that not all possible configuration values are present in this # autogenerated file. @@ -20,14 +20,22 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath(os.pardir)) -#sys.path.append(os.path.join(os.path.dirname(__file__), '../')) +# sys.path.append(os.path.join(os.path.dirname(__file__), '../')) -# -- General configuration ----------------------------------------------------- +# -- General configuration ---------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +# extensions = ['sphinx.ext.autodoc'] +extensions = ['sphinx.ext.extlinks', 'docs.pipext', 'sphinx.ext.intersphinx'] + +# intersphinx +intersphinx_cache_limit = 0 +intersphinx_mapping = { + 'pypug': ('https://packaging.python.org/en/latest/', None), + 'pypa': ('https://www.pypa.io/en/latest/', None), +} -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -#extensions = ['sphinx.ext.autodoc'] -extensions = ['docs.pipext'] # Add any paths that contain templates here, relative to this directory. templates_path = [] @@ -36,14 +44,14 @@ source_suffix = '.rst' # The encoding of source files. -#source_encoding = 'utf-8' +# source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = 'pip' -copyright = '2008-2014, PyPA' +copyright = '2008-2016, PyPA' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -61,76 +69,81 @@ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. -#unused_docs = [] +# unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = ['build'] -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +# The reST default role (used for this markup: `text`) to use for all documents +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] +extlinks = { + 'issue': ('https://github.com/pypa/pip/issues/%s', '#'), + 'pull': ('https://github.com/pypa/pip/pull/%s', 'PR #'), +} -# -- Options for HTML output --------------------------------------------------- +# -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. -if os.environ.get('DOCS_LOCAL'): - import sphinx_rtd_theme - html_theme = "sphinx_rtd_theme" - html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] -else: - # on RTD - html_theme = 'default' +html_theme = 'default' +if not on_rtd: + try: + import sphinx_rtd_theme + html_theme = 'sphinx_rtd_theme' + html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] + except ImportError: + pass # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = '_static/piplogo.png' +# html_logo = '_static/piplogo.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = 'favicon.png' +# html_favicon = 'favicon.png' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -143,14 +156,14 @@ # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -html_use_smartypants = True +html_use_smartypants = False # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. html_use_modindex = False @@ -159,7 +172,7 @@ html_use_index = False # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. html_show_sourcelink = False @@ -167,43 +180,48 @@ # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' +# html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'pipdocs' -# -- Options for LaTeX output -------------------------------------------------- +# -- Options for LaTeX output ------------------------------------------------- # The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' +# latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' +# latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). +# (source start file, target name, title, author, documentclass [howto/manual]) latex_documents = [ - ('index', 'pip.tex', u'pip Documentation', - u'The pip developers', 'manual'), + ( + 'index', + 'pip.tex', + u'pip Documentation', + u'The pip developers', + 'manual', + ), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # Additional stuff for the LaTeX preamble. -#latex_preamble = '' +# latex_preamble = '' # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_use_modindex = True +# latex_use_modindex = True diff --git a/docs/configuration.rst b/docs/configuration.rst index e1d0746b894..3af24de3b7a 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -4,5 +4,3 @@ Configuration ============= This content is now covered in the :doc:`User Guide ` - - diff --git a/docs/cookbook.rst b/docs/cookbook.rst index 3da030c500d..83d42a608f4 100644 --- a/docs/cookbook.rst +++ b/docs/cookbook.rst @@ -5,4 +5,3 @@ Cookbook ============ This content is now covered in the :doc:`User Guide ` - diff --git a/docs/development.rst b/docs/development.rst index a0cc8d6a163..52daef6ab18 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -5,17 +5,40 @@ Development Pull Requests ============= -Submit Pull Requests against the `develop` branch. +- Submit Pull Requests against the `master` branch. +- Provide a good description of what you're doing and why. +- Provide tests that cover your changes and try to run the tests locally first. -Provide a good description of what you're doing and why. +**Example**. Assuming you set up GitHub account, forked pip repository from +https://github.com/pypa/pip to your own page via web interface, and your +fork is located at https://github.com/yourname/pip + +:: + + $ git clone git@github.com:pypa/pip.git + $ cd pip + # ... + $ git diff + $ git add ... + $ git status + $ git commit + +You may reference relevant issues in commit messages (like #1259) to +make GitHub link issues and commits together, and with phrase like +"fixes #1259" you can even close relevant issues automatically. Now +push the changes to your fork:: + + $ git push git@github.com:yourname/pip.git + +Open Pull Requests page at https://github.com/yourname/pip/pulls and +click "New pull request". That's it. -Provide tests that cover your changes and try to run the tests locally first. Automated Testing ================= -All pull requests and merges to 'develop' branch are tested in `Travis `_ -based on our `.travis.yml file `_. +All pull requests and merges to 'master' branch are tested in `Travis `_ +based on our `.travis.yml file `_. Usually, a link to your specific travis build appears in pull requests, but if not, you can find it on our `travis pull requests page `_ @@ -42,6 +65,13 @@ Ways to run the tests locally: $ py.test # Using py.test directly $ tox # Using tox against pip's tox.ini +If you are missing one of the VCS tools, you can tell ``py.test`` to skip it: + +:: + + $ py.test -k 'not bzr' + $ py.test -k 'not svn' + Getting Involved ================ @@ -57,70 +87,41 @@ If you want to become an official maintainer, start by helping out. Later, when you think you're ready, get in touch with one of the maintainers, and they will initiate a vote. + Release Process =============== -This process includes virtualenv, since pip releases necessitate a virtualenv release. - -As an example, the instructions assume we're releasing pip-1.4, and virtualenv-1.10. - -1. Upgrade setuptools, if needed: - - #. Upgrade setuptools 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. - -2. Create Release branches: - - #. Create ``pip/release-1.4`` branch. - #. In ``pip/develop``, change ``pip.version`` to '1.5.dev1'. - #. Create ``virtualenv/release-1.10`` branch. - #. In ``virtualenv/develop``, change ``virtualenv.version`` to '1.11.dev1'. - -3. Prepare "rcX": - - #. In ``pip/release-1.4``, change ``pip.version`` to '1.4rcX', and tag with '1.4rcX'. - #. Build a pip sdist from ``pip/release-1.4``, and build it into ``virtualenv/release-1.10`` using the :ref:`Refresh virtualenv` process. - #. In ``virtualenv/release-1.10``, change ``virtualenv.version`` to '1.10rcX', and tag with '1.10rcX'. - -4. Announce ``pip-1.4rcX`` and ``virtualenv-1.10rcX`` with the :ref:`RC Install Instructions` and elicit feedback. - -5. Apply fixes to 'rcX': - - #. Apply fixes to ``pip/release-1.4`` and ``virtualenv/release-1.10`` - #. Periodically merge fixes to ``pip/develop`` and ``virtualenv/develop`` - -6. Repeat #4 thru #6 if needed. - -7. Final Release: - - #. In ``pip/release-1.4``, change ``pip.version`` to '1.4', and tag with '1.4'. - #. Merge ``pip/release-1.4`` to ``pip/master``. - #. Build a pip sdist from ``pip/release-1.4``, and load it into ``virtualenv/release-1.10`` using the :ref:`Refresh virtualenv` process. - #. Merge ``vitualenv/release-1.10`` to ``virtualenv/develop``. - #. In ``virtualenv/release-1.10``, change ``virtualenv.version`` to '1.10', and tag with '1.10'. - #. Merge ``virtualenv/release-1.10`` to ``virtualenv/master`` - #. Build and upload pip and virtualenv sdists to PyPI. - -.. _`Refresh virtualenv`: - -Refresh virtualenv -++++++++++++++++++ - -#. Update the embedded versions of pip and setuptools in ``virtualenv_support``. -#. Run ``bin/rebuild-script.py`` to rebuild virtualenv based on the latest versions. - - -.. _`RC Install Instructions`: - -RC Install Instructions -+++++++++++++++++++++++ - -:: - - $ curl -L -O https://github.com/pypa/virtualenv/archive/1.10rc1.tar.gz - $ echo " 1.10rc1.tar.gz" | md5sum -c - 1.10rc1.tar.gz: OK - $ tar zxf 1.10rc1.tar.gz - $ python virtualenv-1.10rc1/virtualenv.py myVE - $ myVE/bin/pip install SomePackage - +#. On the current pip ``master`` branch, generate a new ``AUTHORS.txt`` by + running ``invoke generate.authors`` and commit the results. +#. On the current pip ``master`` branch, make a new commit which bumps the + version in ``pip/__init__.py`` to the release version and adjust the + ``CHANGES.txt`` file to reflect the current date. +#. Create a signed tag of the ``master`` branch of the form ``X.Y.Z`` using the + command ``git tag -s X.Y.Z``. +#. Checkout the tag using ``git checkout X.Y.Z`` and create the distribution + files using ``python setup.py sdist bdist_wheel``. +#. Upload the distribution files to PyPI using twine + (``twine upload -s dist/*``). The upload should include GPG signatures of + the distribution files. +#. Push all of the changes. +#. Regenerate the ``get-pip.py`` script by running + ``invoke generate.installer`` in the get-pip repository, and committing the + results. + + +Creating a Bugfix Release +========================= + +Sometimes we need to release a bugfix release of the form ``X.Y.Z+1``. In order +to create one of these the changes should already be merged into the +``master`` branch. + +#. Create a new ``release/X.Y.Z+1`` branch off of the ``X.Y.Z`` tag using the + command ``git checkout -b release/X.Y.Z+1 X.Y.Z``. +#. Cherry pick the fixed commits off of the ``master`` branch, fixing any + conflicts and moving any changelog entries from the development version's + changelog section to the ``X.Y.Z+1`` section. +#. Push the ``release/X.Y.Z+1`` branch to github and submit a PR for it against + the ``master`` branch and wait for the tests to run. +#. Once tests run, merge the ``release/X.Y.Z+1`` branch into master, and follow + the above release process starting with step 4. diff --git a/docs/distribute_setuptools.rst b/docs/distribute_setuptools.rst deleted file mode 100644 index f47dbf8d500..00000000000 --- a/docs/distribute_setuptools.rst +++ /dev/null @@ -1,69 +0,0 @@ -:orphan: - -"ImportError: No module named setuptools" -+++++++++++++++++++++++++++++++++++++++++ - -Although using ``pip install --upgrade setuptools`` to upgrade from distribute -to setuptools works in isolation, it's possible to get "ImportError: No module -named setuptools" when using pip<1.4 to upgrade a package that depends on -setuptools or distribute. - -e.g. when running a command like this: `pip install --upgrade pyramid` - -Solution -~~~~~~~~ - -To prevent the problem in *new* environments (that aren't broken yet): - -* Option 1: - - * *First* run `pip install -U setuptools`, - * *Then* run the command to upgrade your package (e.g. `pip install --upgrade pyramid`) - -* Option 2: - - * Upgrade pip using :ref:`get-pip ` - * *Then* run the command to upgrade your package (e.g. `pip install --upgrade pyramid`) - -To fix the problem once it's occurred, you'll need to manually install the new -setuptools, then rerun the upgrade that failed. - -1. Download `ez_setup.py` (https://bitbucket.org/pypa/setuptools/downloads/ez_setup.py) -2. Run `python ez_setup.py` -3. Then rerun your upgrade (e.g. `pip install --upgrade pyramid`) - - -Cause -~~~~~ - -distribute-0.7.3 is just an empty wrapper that only serves to require the new -setuptools (setuptools>=0.7) so that it will be installed. (If you don't know -yet, the "new setuptools" is a merge of distribute and setuptools back into one -project). - -distribute-0.7.3 does its job well, when the upgrade is done in isolation. -E.g. if you're currently on distribute-0.6.X, then running `pip install -U -setuptools` works fine to upgrade you to setuptools>=0.7. - -The problem occurs when: - -1. you are currently using an older distribute (i.e. 0.6.X) -2. and you try to use pip to upgrade a package that *depends* on setuptools or - distribute. - -As part of the upgrade process, pip builds an install list that ends up -including distribute-0.7.3 and setuptools>=0.7 , but they can end up being -separated by other dependencies in the list, so what can happen is this: - -1. pip uninstalls the existing distribute -2. pip installs distribute-0.7.3 (which has no importable setuptools, that pip - *needs* internally to function) -3. pip moves on to install another dependency (before setuptools>=0.7) and is - unable to proceed without the setuptools package - -Note that pip v1.4 has fixes to prevent this. distribute-0.7.3 (or -setuptools>=0.7) by themselves cannot prevent this kind of problem. - - -.. _setuptools: https://pypi.python.org/pypi/setuptools -.. _distribute: https://pypi.python.org/pypi/distribute diff --git a/docs/index.rst b/docs/index.rst index e17f9e82734..ce08561fd47 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -8,9 +8,8 @@ pip User IRC: #pypa | Dev IRC: #pypa-dev -The `PyPA recommended -`_ -tool for installing and managing Python packages. +The `PyPA recommended `_ tool +for installing Python packages. .. toctree:: :maxdepth: 2 @@ -21,5 +20,3 @@ tool for installing and managing Python packages. reference/index development news - - diff --git a/docs/installing.rst b/docs/installing.rst index 0bfd60afa88..7a806c9e39d 100644 --- a/docs/installing.rst +++ b/docs/installing.rst @@ -3,38 +3,64 @@ Installation ============ -Python & OS Support -------------------- +Do I need to install pip? +------------------------- -pip works with CPython versions 2.6, 2.7, 3.1, 3.2, 3.3, 3.4 and also pypy. +pip is already installed if you're using Python 2 >=2.7.9 or Python 3 >=3.4 +binaries downloaded from `python.org `_, but you'll +need to :ref:`upgrade pip `. -pip works on Unix/Linux, OS X, and Windows. - -.. note:: - - Python 2.5 was supported through v1.3.1, and Python 2.4 was supported through v1.1. +Additionally, pip will already be installed if you're working in a :ref:`Virtual +Environment ` created by +:ref:`pypug:virtualenv` or :ref:`pyvenv `. .. _`get-pip`: -Install pip ------------ +Installing with get-pip.py +-------------------------- -To install or upgrade pip, securely download `get-pip.py -`_. [1]_ +To install pip, securely download `get-pip.py +`_. [2]_ -Then run the following (which may require administrator access):: +Then run the following: + +:: python get-pip.py -If `setuptools`_ (or `distribute`_) is not already installed, ``get-pip.py`` will -install `setuptools`_ for you. [2]_ -To upgrade an existing `setuptools`_ (or `distribute`_), run ``pip install -U -setuptools``. [3]_ +.. warning:: + + Be cautious if you're using a Python install that's managed by your operating + system or another package manager. get-pip.py does not coordinate with + those tools, and may leave your system in an inconsistent state. + +get-pip.py will also install :ref:`pypug:setuptools` [3]_ and :ref:`pypug:wheel`, +if they're not already. :ref:`pypug:setuptools` is required to install +:term:`source distributions `. Both are +required to be able to build a :ref:`Wheel cache` (which improves installation +speed), although neither are required to install pre-built :term:`wheels +`. + +.. note:: + + The get-pip.py script is supported on the same python version as pip. + For the now unsupported Python 3.2, an alternate script is available + `here `_. + + +get-pip.py options +~~~~~~~~~~~~~~~~~~~ + +.. option:: --no-setuptools + + If set, don't attempt to install :ref:`pypug:setuptools` + +.. option:: --no-wheel + + If set, don't attempt to install :ref:`pypug:wheel` -To enable the use of pip from the command line, ensure the ``Scripts`` subdirectory of -your Python installation is available on the system PATH. (This is not done automatically.) Additionally, ``get-pip.py`` supports using the :ref:`pip install options ` and the :ref:`general options `. Below are @@ -53,8 +79,17 @@ Install behind a proxy:: python get-pip.py --proxy="[user:passwd@]proxy.server:port" -Upgrade pip ------------ +Using Linux Package Managers +---------------------------- + +See :ref:`pypug:Installing pip/setuptools/wheel with Linux Package Managers` in +the `Python Packaging User Guide +`_. + +.. _`Upgrading pip`: + +Upgrading pip +------------- On Linux or OS X: @@ -70,38 +105,30 @@ On Windows [5]_: python -m pip install -U pip +Python and OS Compatibility +--------------------------- -Using Package Managers ----------------------- - -On Linux, pip will generally be available for the system install of python using -the system package manager, although often the latest version will be -unavailable. +pip works with CPython versions 2.6, 2.7, 3.3, 3.4, 3.5 and also pypy. -On Debian and Ubuntu:: +This means pip works on the latest patch version of each of these minor versions +(i.e. 2.6.9 for 2.6, etc). +Previous patch versions are supported on a best effort approach. - sudo apt-get install python-pip - -On Fedora:: - - sudo yum install python-pip +pip works on Unix/Linux, OS X, and Windows. ---- -.. [1] "Secure" in this context means using a modern browser or a +.. [1] For Python 2, see https://docs.python.org/2/installing, and for Python3, + see https://docs.python.org/3/installing. + +.. [2] "Secure" in this context means using a modern browser or a tool like `curl` that verifies SSL certificates when downloading from https URLs. -.. [2] Beginning with pip v1.5.1, ``get-pip.py`` stopped requiring setuptools to +.. [3] Beginning with pip v1.5.1, ``get-pip.py`` stopped requiring setuptools to be installed first. -.. [3] Although using ``pip install --upgrade setuptools`` to upgrade from - distribute to setuptools works in isolation, it's possible to get - "ImportError: No module named setuptools" when using pip<1.4 to upgrade a - package that depends on setuptools or distribute. See :doc:`here for - details `. - .. [4] The pip developers are considering making ``--user`` the default for all installs, including ``get-pip.py`` installs of pip, but at this time, ``--user`` installs for pip itself, should not be considered to be fully @@ -109,6 +136,3 @@ On Fedora:: `_. .. [5] https://github.com/pypa/pip/issues/1299 - -.. _setuptools: https://pypi.python.org/pypi/setuptools -.. _distribute: https://pypi.python.org/pypi/distribute diff --git a/docs/make.bat b/docs/make.bat index eebb21057d4..65726733056 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -7,8 +7,10 @@ if "%SPHINXBUILD%" == "" ( ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help @@ -21,14 +23,18 @@ if "%1" == "help" ( echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files - echo. htmlhelp to make HTML files and an HTML help project + echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end @@ -40,6 +46,31 @@ if "%1" == "clean" ( goto end ) + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 2> nul +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +:sphinx_ok + + if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 @@ -95,9 +126,9 @@ if "%1" == "qthelp" ( echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-compressor.qhcp + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pip-installer.qhcp echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-compressor.ghc + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pip-installer.ghc goto end ) @@ -125,6 +156,26 @@ if "%1" == "latex" ( goto end ) +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 @@ -141,6 +192,22 @@ if "%1" == "man" ( goto end ) +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 @@ -167,4 +234,20 @@ results in %BUILDDIR%/doctest/output.txt. goto end ) +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + :end diff --git a/docs/pipext.py b/docs/pipext.py index d11cc77912d..2bcd244e0dc 100644 --- a/docs/pipext.py +++ b/docs/pipext.py @@ -6,10 +6,9 @@ from docutils.parsers import rst from docutils.statemachine import ViewList from textwrap import dedent -from pip import commands +from pip.commands import commands_dict as commands from pip import cmdoptions -from pip.locations import default_log_file -from pip.util import get_prog +from pip.utils import get_prog class PipCommandUsage(rst.Directive): @@ -54,11 +53,10 @@ def _format_option(self, option, cmd_name=None): if option.takes_value(): metavar = option.metavar or option.dest.lower() line += " <%s>" % metavar.lower() - #fix defaults + # fix defaults opt_help = option.help.replace('%default', str(option.default)) - #fix paths with sys.prefix + # fix paths with sys.prefix opt_help = opt_help.replace(sys.prefix, "") - opt_help = opt_help.replace(default_log_file, "") return [bookmark_line, "", line, "", " %s" % opt_help, ""] def _format_options(self, options, cmd_name=None): @@ -79,12 +77,16 @@ def run(self): class PipGeneralOptions(PipOptions): def process_options(self): - self._format_options([o.make() for o in cmdoptions.general_group['options']]) + self._format_options( + [o() for o in cmdoptions.general_group['options']] + ) class PipIndexOptions(PipOptions): def process_options(self): - self._format_options([o.make() for o in cmdoptions.index_group['options']]) + self._format_options( + [o() for o in cmdoptions.index_group['options']] + ) class PipCommandOptions(PipOptions): @@ -92,7 +94,10 @@ class PipCommandOptions(PipOptions): def process_options(self): cmd = commands[self.arguments[0]]() - self._format_options(cmd.parser.option_groups[0].option_list, cmd_name=cmd.name) + self._format_options( + cmd.parser.option_groups[0].option_list, + cmd_name=cmd.name, + ) def setup(app): diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 84a9bb83b0e..efd6d348630 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -11,6 +11,15 @@ Install a package from `PyPI`_: [...] Successfully installed SomePackage +Install a package already downloaded from `PyPI`_ or got elsewhere. +This is useful if the target machine does not have a network connection: + +:: + + $ pip install SomePackage-1.0-py2.py3-none-any.whl + [...] + Successfully installed SomePackage + Show what files were installed: :: diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 8322f8f1c00..cb83554b30e 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -7,11 +7,11 @@ Reference Guide pip pip_install + pip_download pip_uninstall pip_freeze pip_list pip_show pip_search pip_wheel - - + pip_hash diff --git a/docs/reference/pip.rst b/docs/reference/pip.rst index 3becdfd2dad..3bb1b2d049d 100644 --- a/docs/reference/pip.rst +++ b/docs/reference/pip.rst @@ -1,4 +1,3 @@ - pip --- @@ -25,26 +24,7 @@ Console logging ~~~~~~~~~~~~~~~ pip offers :ref:`-v, --verbose <--verbose>` and :ref:`-q, --quiet <--quiet>` -to control the console log level. Each option can be used multiple times and -used together. One ``-v`` increases the verbosity by one, whereas one ``-q`` decreases it by -one. - -The series of log levels, in order, are as follows:: - - VERBOSE_DEBUG, DEBUG, INFO, NOTIFY, WARN, ERROR, FATAL - -``NOTIFY`` is the default level. - -A few examples on how the parameters work to affect the level: - -* specifying nothing results in ``NOTIFY`` -* ``-v`` results in ``INFO`` -* ``-vv`` results in ``DEBUG`` -* ``-q`` results in ``WARN`` -* ``-vq`` results in ``NOTIFY`` - -The most practical use case for users is either ``-v`` or ``-vv`` to see -additional logging to help troubleshoot an issue. +to control the console log level. .. _`FileLogging`: @@ -56,20 +36,88 @@ pip offers the :ref:`--log <--log>` option for specifying a file where a maximum verbosity log will be kept. This option is empty by default. This log appends to previous logging. -Additionally, when commands fail (i.e. return a non-zero exit code), pip writes -a "failure log" for the failed command. This log overwrites previous -logging. The default location is as follows: +Like all pip options, ``--log`` can also be set as an environment variable, or +placed into the pip config file. See the :ref:`Configuration` section. + +.. _`exists-action`: + +--exists-action option +====================== + +This option specifies default behavior when path already exists. +Possible cases: downloading files or checking out repositories for installation, +creating archives. If ``--exists-action`` is not defined, pip will prompt +when decision is needed. + +*(s)witch* + Only relevant to VCS checkout. Attempt to switch the checkout + to the appropriate url and/or revision. +*(i)gnore* + Abort current operation (e.g. don't copy file, don't create archive, + don't modify a checkout). +*(w)ipe* + Delete the file or VCS checkout before trying to create, download, or checkout a new one. +*(b)ackup* + Rename the file or checkout to ``{name}{'.bak' * n}``, where n is some number + of ``.bak`` extensions, such that the file didn't exist at some point. + So the most recent backup will be the one with the largest number after ``.bak``. + +.. _`build-interface`: + +Build System Interface +====================== + +Pip builds packages by invoking the build system. Presently, the only supported +build system is ``setuptools``, but future developments to the Python packaging +infrastructure are expected to include support for other build systems. As +well as package building, the build system is also invoked to install packages +direct from source. + +The interface to the build system is via the ``setup.py`` command line script - +all build actions are defined in terms of the specific ``setup.py`` command +line that will be run to invoke the required action. + +Setuptools Injection +~~~~~~~~~~~~~~~~~~~~ + +As noted above, the supported build system is ``setuptools``. However, not all +packages use ``setuptools`` in their build scripts. To support projects that +use "pure ``distutils``", pip injects ``setuptools`` into ``sys.modules`` +before invoking ``setup.py``. The injection should be transparent to +``distutils``-based projects, but 3rd party build tools wishing to provide a +``setup.py`` emulating the commands pip requires may need to be aware that it +takes place. + +Future Developments +~~~~~~~~~~~~~~~~~~~ + +`PEP426`_ notes that the intention is to add hooks to project metadata in +version 2.1 of the metadata spec, to explicitly define how to build a project +from its source. Once this version of the metadata spec is final, pip will +migrate to using that interface. At that point, the ``setup.py`` interface +documented here will be retained solely for legacy purposes, until projects +have migrated. + +Specifically, applications should *not* expect to rely on there being any form +of backward compatibility guarantees around the ``setup.py`` interface. + +.. _PEP426: http://www.python.org/dev/peps/pep-0426/#metabuild-system + +Build Options +~~~~~~~~~~~~~ -* On Unix and Mac OS X: :file:`$HOME/.pip/pip.log` -* On Windows, the configuration file is: :file:`%HOME%\\pip\\pip.log` +The ``--global-option`` and ``--build-option`` arguments to the ``pip install`` +and ``pip wheel`` inject additional arguments into the ``setup.py`` command +(``--build-option`` is only available in ``pip wheel``). These arguments are +included in the command as follows:: -The option for the failure log, is :ref:`--log-file <--log-file>`. + python setup.py BUILD COMMAND -Both logs add a line per execution to specify the date and what pip executable wrote the log. +The options are passed unmodified, and presently offer direct access to the +distutils command line. Use of ``--global-option`` and ``--build-option`` +should be considered as build system dependent, and may not be supported in the +current form if support for alternative build systems is added to pip. -Like all pip options, ``--log`` and ``log-file``, can also be set as an environment -variable, or placed into the pip config file. See the :ref:`Configuration` -section. .. _`General Options`: diff --git a/docs/reference/pip_download.rst b/docs/reference/pip_download.rst new file mode 100644 index 00000000000..b1f805fea20 --- /dev/null +++ b/docs/reference/pip_download.rst @@ -0,0 +1,51 @@ + +.. _`pip download`: + +pip download +------------ + +.. contents:: + +Usage +***** + +.. pip-command-usage:: download + + +Description +*********** + +.. pip-command-description:: download + + +Overview +++++++++ +``pip download`` replaces the ``--download`` option to ``pip install``, +which is now deprecated and will be removed in pip 10. + +``pip download`` does the same resolution and downloading as ``pip install``, +but instead of installing the dependencies, it collects the downloaded +distributions into the directory provided (defaulting to the current +directory). This directory can later be passed as the value to +``pip install --find-links`` to facilitate offline or locked down package +installation. + + +Options +******* + +.. pip-command-options:: download + +.. pip-index-options:: + + +Examples +******** + +#. Download a package and all of its dependencies + + :: + + $ pip download SomePackage + $ pip download -d . SomePackage # equivalent to above + $ pip download --no-index --find-links=/tmp/wheelhouse -d /tmp/otherwheelhouse SomePackage diff --git a/docs/reference/pip_freeze.rst b/docs/reference/pip_freeze.rst index 360b69dfc5f..c13bc00f3b6 100644 --- a/docs/reference/pip_freeze.rst +++ b/docs/reference/pip_freeze.rst @@ -27,20 +27,21 @@ Options Examples ******** -1) Generate output suitable for a requirements file. +#. Generate output suitable for a requirements file. - :: + :: - $ pip freeze - Jinja2==2.6 - Pygments==1.5 - Sphinx==1.1.3 - docutils==0.9.1 + $ pip freeze + docutils==0.11 + Jinja2==2.7.2 + MarkupSafe==0.19 + Pygments==1.6 + Sphinx==1.2.2 -2) Generate a requirements file and then install from it in another environment. +#. Generate a requirements file and then install from it in another environment. - :: + :: - $ env1/bin/pip freeze > requirements.txt - $ env2/bin/pip install -r requirements.txt + $ env1/bin/pip freeze > requirements.txt + $ env2/bin/pip install -r requirements.txt diff --git a/docs/reference/pip_hash.rst b/docs/reference/pip_hash.rst new file mode 100644 index 00000000000..72052bc22dc --- /dev/null +++ b/docs/reference/pip_hash.rst @@ -0,0 +1,49 @@ +.. _`pip hash`: + +pip hash +------------ + +.. contents:: + +Usage +***** + +.. pip-command-usage:: hash + + +Description +*********** + +.. pip-command-description:: hash + + +Overview +++++++++ +``pip hash`` is a convenient way to get a hash digest for use with +:ref:`hash-checking mode`, especially for packages with multiple archives. The +error message from ``pip install --require-hashes ...`` will give you one +hash, but, if there are multiple archives (like source and binary ones), you +will need to manually download and compute a hash for the others. Otherwise, a +spurious hash mismatch could occur when :ref:`pip install` is passed a +different set of options, like :ref:`--no-binary `. + + +Options +******* + +.. pip-command-options:: hash + + +Example +******** + +Compute the hash of a downloaded archive:: + + $ pip download SomePackage + Collecting SomePackage + Downloading SomePackage-2.2.tar.gz + Saved ./pip_downloads/SomePackage-2.2.tar.gz + Successfully downloaded SomePackage + $ pip hash ./pip_downloads/SomePackage-2.2.tar.gz + ./pip_downloads/SomePackage-2.2.tar.gz: + --hash=sha256:93e62e05c7ad3da1a233def6731e8285156701e3419a5fe279017c429ec67ce0 diff --git a/docs/reference/pip_install.rst b/docs/reference/pip_install.rst index 06c003ae9c4..1724a3f2746 100644 --- a/docs/reference/pip_install.rst +++ b/docs/reference/pip_install.rst @@ -1,4 +1,3 @@ - .. _`pip install`: pip install @@ -17,6 +16,107 @@ Description .. pip-command-description:: install +Overview +++++++++ + +Pip install has several stages: + +1. Identify the base requirements. The user supplied arguments are processed + here. +2. Resolve dependencies. What will be installed is determined here. +3. Build wheels. All the dependencies that can be are built into wheels. +4. Install the packages (and uninstall anything being upgraded/replaced). + +Argument Handling ++++++++++++++++++ + +When looking at the items to be installed, pip checks what type of item +each is, in the following order: + +1. Project or archive URL. +2. Local directory (which must contain a ``setup.py``, or pip will report + an error). +3. Local file (a sdist or wheel format archive, following the naming + conventions for those formats). +4. A requirement, as specified in PEP 440. + +Each item identified is added to the set of requirements to be satisfied by +the install. + +Working Out the Name and Version +++++++++++++++++++++++++++++++++ + +For each candidate item, pip needs to know the project name and version. For +wheels (identified by the ``.whl`` file extension) this can be obtained from +the filename, as per the Wheel spec. For local directories, or explicitly +specified sdist files, the ``setup.py egg_info`` command is used to determine +the project metadata. For sdists located via an index, the filename is parsed +for the name and project version (this is in theory slightly less reliable +than using the ``egg_info`` command, but avoids downloading and processing +unnecessary numbers of files). + +Any URL may use the ``#egg=name`` syntax (see :ref:`VCS Support`) to +explicitly state the project name. + +Satisfying Requirements ++++++++++++++++++++++++ + +Once pip has the set of requirements to satisfy, it chooses which version of +each requirement to install using the simple rule that the latest version that +satisfies the given constraints will be installed (but see :ref:`here
`
+for an exception regarding pre-release versions). Where more than one source of
+the chosen version is available, it is assumed that any source is acceptable
+(as otherwise the versions would differ).
+
+Installation Order
+++++++++++++++++++
+
+As of v6.1.0, pip installs dependencies before their dependents, i.e. in
+"topological order".  This is the only commitment pip currently makes related
+to order.  While it may be coincidentally true that pip will install things in
+the order of the install arguments or in the order of the items in a
+requirements file, this is not a promise.
+
+In the event of a dependency cycle (aka "circular dependency"), the current
+implementation (which might possibly change later) has it such that the first
+encountered member of the cycle is installed last.
+
+For instance, if quux depends on foo which depends on bar which depends on baz,
+which depends on foo::
+
+    pip install quux
+    ...
+    Installing collected packages baz, bar, foo, quux
+
+    pip install bar
+    ...
+    Installing collected packages foo, baz, bar
+
+
+Prior to v6.1.0, pip made no commitments about install order.
+
+The decision to install topologically is based on the principle that
+installations should proceed in a way that leaves the environment usable at each
+step. This has two main practical benefits:
+
+1. Concurrent use of the environment during the install is more likely to work.
+2. A failed install is less likely to leave a broken environment.  Although pip
+   would like to support failure rollbacks eventually, in the mean time, this is
+   an improvement.
+
+Although the new install order is not intended to replace (and does not replace)
+the use of ``setup_requires`` to declare build dependencies, it may help certain
+projects install from sdist (that might previously fail) that fit the following
+profile:
+
+1. They have build dependencies that are also declared as install dependencies
+   using ``install_requires``.
+2. ``python setup.py egg_info`` works without their build dependencies being
+   installed.
+3. For whatever reason, they don't or won't declare their build dependencies using
+   ``setup_requires``.
+
+
 .. _`Requirements File Format`:
 
 Requirements File Format
@@ -25,24 +125,34 @@ Requirements File Format
 Each line of the requirements file indicates something to be installed,
 and like arguments to :ref:`pip install`, the following forms are supported::
 
-    
+    [[--option]...]
+     [; markers] [[--option]...]
     
     [-e] 
     [-e] 
 
+For details on requirement specifiers, see :ref:`Requirement Specifiers`.
+
 See the :ref:`pip install Examples` for examples of all these forms.
 
-A line beginning with ``#`` is treated as a comment and ignored.
+A line that begins with ``#`` is treated as a comment and ignored. Whitespace
+followed by a ``#`` causes the ``#`` and the remainder of the line to be
+treated as a comment.
+
+A line ending in an unescaped ``\`` is treated as a line continuation
+and the newline following it is effectively ignored.
+
+Comments are stripped *before* line continuations are processed.
 
-Additionally, the following Package Index Options are supported:
+The following options are supported:
 
   *  :ref:`-i, --index-url <--index-url>`
   *  :ref:`--extra-index-url <--extra-index-url>`
   *  :ref:`--no-index <--no-index>`
   *  :ref:`-f, --find-links <--find-links>`
-  *  :ref:`--allow-external <--allow-external>`
-  *  :ref:`--allow-all-external <--allow-external>`
-  *  :ref:`--allow-unverified <--allow-unverified>`
+  *  :ref:`--no-binary `
+  *  :ref:`--only-binary `
+  *  :ref:`--require-hashes <--require-hashes>`
 
 For example, to specify :ref:`--no-index <--no-index>` and 2 :ref:`--find-links <--find-links>` locations:
 
@@ -53,33 +163,124 @@ For example, to specify :ref:`--no-index <--no-index>` and 2 :ref:`--find-links
 --find-links http://some.archives.com/archives
 
 
-Lastly, if you wish, you can refer to other requirements files, like this::
+If you wish, you can refer to other requirements files, like this::
 
     -r more_requirements.txt
 
+You can also refer to :ref:`constraints files `, like this::
+
+    -c some_constraints.txt
+
+.. _`Example Requirements File`:
+
+Example Requirements File
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Use ``pip install -r example-requirements.txt`` to install::
+
+    #
+    ####### example-requirements.txt #######
+    #
+    ###### Requirements without Version Specifiers ######
+    nose
+    nose-cov
+    beautifulsoup4
+    #
+    ###### Requirements with Version Specifiers ######
+    #   See https://www.python.org/dev/peps/pep-0440/#version-specifiers
+    docopt == 0.6.1             # Version Matching. Must be version 0.6.1
+    keyring >= 4.1.1            # Minimum version 4.1.1
+    coverage != 3.5             # Version Exclusion. Anything except version 3.5
+    Mopidy-Dirble ~= 1.1        # Compatible release. Same as >= 1.1, == 1.*
+    #
+    ###### Refer to other requirements files ######
+    -r other-requirements.txt
+    #
+    #
+    ###### A particular file ######
+    ./downloads/numpy-1.9.2-cp34-none-win32.whl
+    http://wxpython.org/Phoenix/snapshot-builds/wxPython_Phoenix-3.0.3.dev1820+49a8884-cp34-none-win_amd64.whl
+    #
+    ###### Additional Requirements without Version Specifiers ######
+    #   Same as 1st section, just here to show that you can put things in any order.
+    rejected
+    green
+    #
+
 .. _`Requirement Specifiers`:
 
 Requirement Specifiers
 ++++++++++++++++++++++
 
-pip supports installing from "requirement specifiers" as implemented in
-`pkg_resources Requirements `_
+pip supports installing from a package index using a :term:`requirement
+specifier `. Generally speaking, a requirement
+specifier is composed of a project name followed by optional :term:`version
+specifiers `.  `PEP508`_ contains a full specification
+of the format of a requirement (``pip`` does not support the ``url_req`` form
+of specifier at this time).
+
+Some examples:
+
+ ::
+
+  SomeProject
+  SomeProject == 1.3
+  SomeProject >=1.2,<.2.0
+  SomeProject[foo, bar]
+  SomeProject~=1.4.2
 
-Some Examples:
+Since version 6.0, pip also supports specifiers containing `environment markers
+`_ like so:
 
  ::
 
-  'FooProject >= 1.2'
-  Fizzy [foo, bar]
-  'PickyThing<1.6,>1.9,!=1.9.6,<2.0a0,==2.4c1'
-  SomethingWhoseVersionIDontCareAbout
+  SomeProject ==5.4 ; python_version < '2.7'
+  SomeProject; sys_platform == 'win32'
+
+Environment markers are supported in the command line and in requirements files.
 
 .. note::
 
-  Use single or double quotes around specifiers to avoid ``>`` and ``<`` being
-  interpreted as shell redirects. e.g. ``pip install 'FooProject>=1.2'``.
+   Use quotes around specifiers in the shell when using ``>``, ``<``, or when
+   using environment markers. Don't use quotes in requirement files. [1]_
 
 
+.. _`Per-requirement Overrides`:
+
+Per-requirement Overrides
++++++++++++++++++++++++++
+
+Since version 7.0 pip supports controlling the command line options given to
+``setup.py`` via requirements files. This disables the use of wheels (cached or
+otherwise) for that package, as ``setup.py`` does not exist for wheels.
+
+The ``--global-option`` and ``--install-option`` options are used to pass
+options to ``setup.py``. For example:
+
+ ::
+
+    FooProject >= 1.2 --global-option="--no-user-cfg" \
+                      --install-option="--prefix='/usr/local'" \
+                      --install-option="--no-compile"
+
+The above translates roughly into running FooProject's ``setup.py``
+script as:
+
+ ::
+
+   python setup.py --no-user-cfg install --prefix='/usr/local' --no-compile
+
+Note that the only way of giving more than one option to ``setup.py``
+is through multiple ``--global-option`` and ``--install-option``
+options, as shown in the example above. The value of each option is
+passed as a single argument to the ``setup.py`` script. Therefore, a
+line such as the following is invalid and would result in an
+installation error.
+
+::
+
+   # Invalid. Please use '--install-option' twice as shown above.
+   FooProject >= 1.2 --install-option="--prefix=/usr/local --no-compile"
 
 
 .. _`Pre Release Versions`:
@@ -101,37 +302,6 @@ that will enable installing pre-releases and development releases.
 
 .. _PEP426: http://www.python.org/dev/peps/pep-0426
 
-.. _`Externally Hosted Files`:
-
-Externally Hosted Files
-+++++++++++++++++++++++
-
-Starting with v1.4, pip will warn about installing any file that does not come
-from the primary index. As of version 1.5, pip defaults to ignoring these files
-unless asked to consider them.
-
-The ``pip install`` command supports a
-:ref:`--allow-external PROJECT <--allow-external>` option that will enable
-installing links that are linked directly from the simple index but to an
-external host that also have a supported hash fragment. Externally hosted
-files for all projects may be enabled using the
-:ref:`--allow-all-external <--allow-all-external>` flag to the ``pip install``
-command.
-
-The ``pip install`` command also supports a
-:ref:`--allow-unverified PROJECT <--allow-unverified>` option that will enable
-installing insecurely linked files. These are either directly linked (as above)
-files without a hash, or files that are linked from either the home page or the
-download url of a package.
-
-These options can be used in a requirements file.  Assuming some fictional
-`ExternalPackage` that is hosted external and unverified, then your requirements
-file would be like so::
-
-    --allow-external ExternalPackage
-    --allow-unverified ExternalPackage
-    ExternalPackage
-
 
 .. _`VCS Support`:
 
@@ -153,8 +323,28 @@ the :ref:`--editable ` option) or not.
 * For non-editable installs, the project is built locally in a temp dir and then
   installed normally.
 
-The url suffix "egg=" is used by pip in it's dependency logic to
-identify the project prior to pip downloading and analyzing the metadata.
+The "project name" component of the url suffix "egg=-"
+is used by pip in its dependency logic to identify the project prior
+to pip downloading and analyzing the metadata.  The optional "version"
+component of the egg name is not functionally important.  It merely
+provides a human-readable clue as to what version is in use. For projects
+where setup.py is not in the root of project, "subdirectory" component
+is used. Value of "subdirectory" component should be a path starting from root
+of the project to where setup.py is located.
+
+So if your repository layout is:
+
+    - pkg_dir/
+
+      - setup.py  # setup.py for package ``pkg``
+      - some_module.py
+    - other_dir/
+
+      - some_file
+    - some_other_file
+
+You'll need to use ``pip install -e vcs+protocol://repo_url/#egg=pkg&subdirectory=pkg_dir``.
+
 
 Git
 ~~~
@@ -233,11 +423,18 @@ Finding Packages
 
 pip searches for packages on `PyPI`_ using the
 `http simple interface `_,
-which is documented `here `_
+which is documented `here `_
 and `there `_
 
 pip offers a number of Package Index Options for modifying how packages are found.
 
+pip looks for packages in a number of places, on PyPI (if not disabled via
+```--no-index```), in the local filesystem, and in any additional repositories
+specified via ```--find-links``` or ```--index-url```. There is no ordering in
+the locations that are searched, rather they are all checked, and the "best"
+match for the requirements (in terms of version number - see `PEP440`_ for
+details) is selected.
+
 See the :ref:`pip install Examples`.
 
 
@@ -246,43 +443,188 @@ See the :ref:`pip install Examples`.
 SSL Certificate Verification
 ++++++++++++++++++++++++++++
 
-Starting with v1.3, pip provides SSL certificate verification over https, for the purpose
-of providing secure, certified downloads from PyPI.
-
-
-Hash Verification
-+++++++++++++++++
-
-PyPI provides md5 hashes in the hash fragment of package download urls.
-
-pip supports checking this, as well as any of the
-guaranteed hashlib algorithms (sha1, sha224, sha384, sha256, sha512, md5).
-
-The hash fragment is case sensitive (i.e. sha1 not SHA1).
-
-This check is only intended to provide basic download corruption protection.
-It is not intended to provide security against tampering. For that,
-see :ref:`SSL Certificate Verification`
+Starting with v1.3, pip provides SSL certificate verification over https, to
+prevent man-in-the-middle attacks against PyPI downloads.
 
 
-Download Cache
-++++++++++++++
+.. _`Caching`:
 
-pip offers a :ref:`--download-cache ` option for
-installs to prevent redundant downloads of archives from PyPI.
+Caching
++++++++
 
-The point of this cache is *not* to circumvent the index crawling process, but
-to *just* prevent redundant downloads.
+Starting with v6.0, pip provides an on-by-default cache which functions
+similarly to that of a web browser. While the cache is on by default and is
+designed do the right thing by default you can disable the cache and always
+access PyPI by utilizing the ``--no-cache-dir`` option.
 
-Items are stored in this cache based on the url the archive was found at, not
-simply the archive name.
+When making any HTTP request pip will first check its local cache to determine
+if it has a suitable response stored for that request which has not expired. If
+it does then it simply returns that response and doesn't make the request.
 
-If you want a fast/local install solution that circumvents crawling PyPI, see
-the :ref:`Fast & Local Installs`.
-
-Like all options, :ref:`--download-cache `, can also
-be set as an environment variable, or placed into the pip config file.  See the
-:ref:`Configuration` section.
+If it has a response stored, but it has expired, then it will attempt to make a
+conditional request to refresh the cache which will either return an empty
+response telling pip to simply use the cached item (and refresh the expiration
+timer) or it will return a whole new response which pip can then store in the
+cache.
+
+When storing items in the cache, pip will respect the ``CacheControl`` header
+if it exists, or it will fall back to the ``Expires`` header if that exists.
+This allows pip to function as a browser would, and allows the index server
+to communicate to pip how long it is reasonable to cache any particular item.
+
+While this cache attempts to minimize network activity, it does not prevent
+network access altogether. If you want a local install solution that
+circumvents accessing PyPI, see :ref:`Installing from local packages`.
+
+The default location for the cache directory depends on the Operating System:
+
+Unix
+  :file:`~/.cache/pip` and it respects the ``XDG_CACHE_HOME`` directory.
+OS X
+  :file:`~/Library/Caches/pip`.
+Windows
+  :file:`\\pip\\Cache`
+
+
+.. _`Wheel cache`:
+
+Wheel Cache
+~~~~~~~~~~~
+
+Pip will read from the subdirectory ``wheels`` within the pip cache directory
+and use any packages found there. This is disabled via the same
+``--no-cache-dir`` option that disables the HTTP cache. The internal structure
+of that is not part of the pip API. As of 7.0, pip makes a subdirectory for
+each sdist that wheels are built from and places the resulting wheels inside.
+
+Pip attempts to choose the best wheels from those built in preference to
+building a new wheel. Note that this means when a package has both optional
+C extensions and builds `py` tagged wheels when the C extension can't be built
+that pip will not attempt to build a better wheel for Pythons that would have
+supported it, once any generic wheel is built. To correct this, make sure that
+the wheels are built with Python specific tags - e.g. pp on Pypy.
+
+When no wheels are found for an sdist, pip will attempt to build a wheel
+automatically and insert it into the wheel cache.
+
+
+.. _`hash-checking mode`:
+
+Hash-Checking Mode
+++++++++++++++++++
+
+Since version 8.0, pip can check downloaded package archives against local
+hashes to protect against remote tampering. To verify a package against one or
+more hashes, add them to the end of the line::
+
+    FooProject == 1.2 --hash=sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 \
+                      --hash=sha256:486ea46224d1bb4fb680f34f7c9ad96a8f24ec88be73ea8e5a6c65260e9cb8a7
+
+(The ability to use multiple hashes is important when a package has both
+binary and source distributions or when it offers binary distributions for a
+variety of platforms.)
+
+The recommended hash algorithm at the moment is sha256, but stronger ones are
+allowed, including all those supported by ``hashlib``. However, weaker ones
+such as md5, sha1, and sha224 are excluded to avoid giving a false sense of
+security.
+
+Hash verification is an all-or-nothing proposition. Specifying a ``--hash``
+against any requirement not only checks that hash but also activates a global
+*hash-checking mode*, which imposes several other security restrictions:
+
+* Hashes are required for all requirements. This is because a partially-hashed
+  requirements file is of little use and thus likely an error: a malicious
+  actor could slip bad code into the installation via one of the unhashed
+  requirements. Note that hashes embedded in URL-style requirements via the
+  ``#md5=...`` syntax suffice to satisfy this rule (regardless of hash
+  strength, for legacy reasons), though you should use a stronger
+  hash like sha256 whenever possible.
+* Hashes are required for all dependencies. An error results if there is a
+  dependency that is not spelled out and hashed in the requirements file.
+* Requirements that take the form of project names (rather than URLs or local
+  filesystem paths) must be pinned to a specific version using ``==``. This
+  prevents a surprising hash mismatch upon the release of a new version
+  that matches the requirement specifier.
+* ``--egg`` is disallowed, because it delegates installation of dependencies
+  to setuptools, giving up pip's ability to enforce any of the above.
+
+.. _`--require-hashes`:
+
+Hash-checking mode can be forced on with the ``--require-hashes`` command-line
+option::
+
+    $ pip install --require-hashes -r requirements.txt
+        ...
+        Hashes are required in --require-hashes mode (implicitly on when a hash is
+        specified for any package). These requirements were missing hashes,
+        leaving them open to tampering. These are the hashes the downloaded
+        archives actually had. You can add lines like these to your requirements
+        files to prevent tampering.
+            pyelasticsearch==1.0 --hash=sha256:44ddfb1225054d7d6b1d02e9338e7d4809be94edbe9929a2ec0807d38df993fa
+            more-itertools==2.2 --hash=sha256:93e62e05c7ad3da1a233def6731e8285156701e3419a5fe279017c429ec67ce0
+
+This can be useful in deploy scripts, to ensure that the author of the
+requirements file provided hashes. It is also a convenient way to bootstrap
+your list of hashes, since it shows the hashes of the packages it fetched. It
+fetches only the preferred archive for each package, so you may still need to
+add hashes for alternatives archives using :ref:`pip hash`: for instance if
+there is both a binary and a source distribution.
+
+The :ref:`wheel cache ` is disabled in hash-checking mode to
+prevent spurious hash mismatch errors. These would otherwise occur while
+installing sdists that had already been automatically built into cached wheels:
+those wheels would be selected for installation, but their hashes would not
+match the sdist ones from the requirements file. A further complication is that
+locally built wheels are nondeterministic: contemporary modification times make
+their way into the archive, making hashes unpredictable across machines and
+cache flushes. Compilation of C code adds further nondeterminism, as many
+compilers include random-seeded values in their output. However, wheels fetched
+from index servers are the same every time. They land in pip's HTTP cache, not
+its wheel cache, and are used normally in hash-checking mode. The only downside
+of having the wheel cache disabled is thus extra build time for sdists, and
+this can be solved by making sure pre-built wheels are available from the index
+server.
+
+Hash-checking mode also works with :ref:`pip download` and :ref:`pip wheel`. A
+:ref:`comparison of hash-checking mode with other repeatability strategies
+` is available in the User Guide.
+
+.. warning::
+    Beware of the ``setup_requires`` keyword arg in :file:`setup.py`. The
+    (rare) packages that use it will cause those dependencies to be downloaded
+    by setuptools directly, skipping pip's hash-checking. If you need to use
+    such a package, see :ref:`Controlling
+    setup_requires`.
+
+.. warning::
+    Be careful not to nullify all your security work when you install your
+    actual project by using setuptools directly: for example, by calling
+    ``python setup.py install``, ``python setup.py develop``, or
+    ``easy_install``. Setuptools will happily go out and download, unchecked,
+    anything you missed in your requirements file—and it’s easy to miss things
+    as your project evolves. To be safe, install your project using pip and
+    :ref:`--no-deps `.
+
+    Instead of ``python setup.py develop``, use... ::
+
+        pip install --no-deps -e .
+
+    Instead of ``python setup.py install``, use... ::
+
+        pip install --no-deps .
+
+
+Hashes from PyPI
+~~~~~~~~~~~~~~~~
+
+PyPI provides an MD5 hash in the fragment portion of each package download URL,
+like ``#md5=123...``, which pip checks as a protection against download
+corruption. Other hash algorithms that have guaranteed support from ``hashlib``
+are also supported here: sha1, sha224, sha384, sha256, and sha512. Since this
+hash originates remotely, it is not a useful guard against tampering and thus
+does not satisfy the ``--require-hashes`` demand that every package have a
+local hash.
 
 
 .. _`editable-installs`:
@@ -291,7 +633,7 @@ be set as an environment variable, or placed into the pip config file.  See the
 +++++++++++++++++++
 
 "Editable" installs are fundamentally `"setuptools develop mode"
-`_
+`_
 installs.
 
 You can install local projects or VCS projects in "editable" mode::
@@ -299,17 +641,20 @@ You can install local projects or VCS projects in "editable" mode::
 $ pip install -e path/to/SomeProject
 $ pip install -e git+http://repo/my_project.git#egg=SomeProject
 
+(See the :ref:`VCS Support` section above for more information on VCS-related syntax.)
+
 For local projects, the "SomeProject.egg-info" directory is created relative to
 the project path.  This is one advantage over just using ``setup.py develop``,
 which creates the "egg-info" directly relative the current working directory.
 
 
+.. _`controlling-setup-requires`:
 
 Controlling setup_requires
 ++++++++++++++++++++++++++
 
 Setuptools offers the ``setup_requires`` `setup() keyword
-`_
+`_
 for specifying dependencies that need to be present in order for the `setup.py`
 script to run.  Internally, Setuptools uses ``easy_install`` to fulfill these
 dependencies.
@@ -335,7 +680,51 @@ To have the dependency located from a local directory and not crawl PyPI, add th
 
   [easy_install]
   allow_hosts = ''
-  find_links = file:///path/to/local/archives
+  find_links = file:///path/to/local/archives/
+
+
+Build System Interface
+++++++++++++++++++++++
+
+In order for pip to install a package from source, ``setup.py`` must implement
+the following commands::
+
+    setup.py egg_info [--egg-base XXX]
+    setup.py install --record XXX [--single-version-externally-managed] [--root XXX] [--compile|--no-compile] [--install-headers XXX]
+
+The ``egg_info`` command should create egg metadata for the package, as
+described in the setuptools documentation at
+https://setuptools.readthedocs.io/en/latest/setuptools.html#egg-info-create-egg-metadata-and-set-build-tags
+
+The ``install`` command should implement the complete process of installing the
+package to the target directory XXX.
+
+To install a package in "editable" mode (``pip install -e``), ``setup.py`` must
+implement the following command::
+
+    setup.py develop --no-deps
+
+This should implement the complete process of installing the package in
+"editable" mode.
+
+All packages will be attempted to built into wheels::
+
+    setup.py bdist_wheel -d XXX
+
+One further ``setup.py`` command is invoked by ``pip install``::
+
+    setup.py clean
+
+This command is invoked to clean up temporary commands from the build. (TODO:
+Investigate in more detail when this command is required).
+
+No other build system commands are invoked by the ``pip install`` command.
+
+Installing a package from a wheel does not invoke the build system at all.
+
+.. _PyPI: http://pypi.python.org/pypi/
+.. _setuptools extras: https://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-extras-optional-features-with-their-own-dependencies
+
 
 
 .. _`pip install Options`:
@@ -353,88 +742,91 @@ Options
 Examples
 ********
 
-1) Install `SomePackage` and it's dependencies from `PyPI`_ using :ref:`Requirement Specifiers`
+#. Install `SomePackage` and its dependencies from `PyPI`_ using :ref:`Requirement Specifiers`
 
-  ::
+    ::
 
-  $ pip install SomePackage            # latest version
-  $ pip install SomePackage==1.0.4     # specific version
-  $ pip install 'SomePackage>=1.0.4'     # minimum version
+      $ pip install SomePackage            # latest version
+      $ pip install SomePackage==1.0.4     # specific version
+      $ pip install 'SomePackage>=1.0.4'     # minimum version
 
 
-2) Install a list of requirements specified in a file.  See the :ref:`Requirements files `.
+#. Install a list of requirements specified in a file.  See the :ref:`Requirements files `.
 
-  ::
+    ::
 
-  $ pip install -r requirements.txt
+      $ pip install -r requirements.txt
 
 
-3) Upgrade an already installed `SomePackage` to the latest from PyPI.
+#. Upgrade an already installed `SomePackage` to the latest from PyPI.
 
-  ::
+    ::
 
-  $ pip install --upgrade SomePackage
+      $ pip install --upgrade SomePackage
 
 
-4) Install a local project in "editable" mode. See the section on :ref:`Editable Installs `.
+#. Install a local project in "editable" mode. See the section on :ref:`Editable Installs `.
 
-  ::
+    ::
 
-  $ pip install -e .                     # project in current directory
-  $ pip install -e path/to/project       # project in another directory
+      $ pip install -e .                     # project in current directory
+      $ pip install -e path/to/project       # project in another directory
 
 
-5) Install a project from VCS in "editable" mode. See the sections on :ref:`VCS Support ` and :ref:`Editable Installs `.
+#. Install a project from VCS in "editable" mode. See the sections on :ref:`VCS Support ` and :ref:`Editable Installs `.
 
-  ::
+    ::
 
-  $ pip install -e git+https://git.repo/some_pkg.git#egg=SomePackage          # from git
-  $ pip install -e hg+https://hg.repo/some_pkg.git#egg=SomePackage            # from mercurial
-  $ pip install -e svn+svn://svn.repo/some_pkg/trunk/#egg=SomePackage         # from svn
-  $ pip install -e git+https://git.repo/some_pkg.git@feature#egg=SomePackage  # from 'feature' branch
-  $ pip install -e git+https://git.repo/some_repo.git@egg=subdir&subdirectory=subdir_path # install a python package from a repo subdirectory
+      $ pip install -e git+https://git.repo/some_pkg.git#egg=SomePackage          # from git
+      $ pip install -e hg+https://hg.repo/some_pkg.git#egg=SomePackage            # from mercurial
+      $ pip install -e svn+svn://svn.repo/some_pkg/trunk/#egg=SomePackage         # from svn
+      $ pip install -e git+https://git.repo/some_pkg.git@feature#egg=SomePackage  # from 'feature' branch
+      $ pip install -e "git+https://git.repo/some_repo.git#egg=subdir&subdirectory=subdir_path" # install a python package from a repo subdirectory
 
-6) Install a package with `setuptools extras`_.
+#. Install a package with `setuptools extras`_.
 
-  ::
+    ::
 
-  $ pip install SomePackage[PDF]
-  $ pip install SomePackage[PDF]==3.0
-  $ pip install -e .[PDF]==3.0  # editable project in current directory
+      $ pip install SomePackage[PDF]
+      $ pip install SomePackage[PDF]==3.0
+      $ pip install -e .[PDF]==3.0  # editable project in current directory
 
 
-7) Install a particular source archive file.
+#. Install a particular source archive file.
 
-  ::
+    ::
 
-  $ pip install ./downloads/SomePackage-1.0.4.tar.gz
-  $ pip install http://my.package.repo/SomePackage-1.0.4.zip
+      $ pip install ./downloads/SomePackage-1.0.4.tar.gz
+      $ pip install http://my.package.repo/SomePackage-1.0.4.zip
 
 
-8) Install from alternative package repositories.
+#. Install from alternative package repositories.
 
-  Install from a different index, and not `PyPI`_ ::
+   Install from a different index, and not `PyPI`_ ::
 
-  $ pip install --index-url http://my.package.repo/simple/ SomePackage
+     $ pip install --index-url http://my.package.repo/simple/ SomePackage
 
-  Search an additional index during install, in addition to `PyPI`_ ::
+   Search an additional index during install, in addition to `PyPI`_ ::
 
-  $ pip install --extra-index-url http://my.package.repo/simple SomePackage
+     $ pip install --extra-index-url http://my.package.repo/simple SomePackage
 
-  Install from a local flat directory containing archives (and don't scan indexes)::
+   Install from a local flat directory containing archives (and don't scan indexes)::
 
-  $ pip install --no-index --find-links=file:///local/dir/ SomePackage
-  $ pip install --no-index --find-links=/local/dir/ SomePackage
-  $ pip install --no-index --find-links=relative/dir/ SomePackage
+     $ pip install --no-index --find-links=file:///local/dir/ SomePackage
+     $ pip install --no-index --find-links=/local/dir/ SomePackage
+     $ pip install --no-index --find-links=relative/dir/ SomePackage
 
 
-9) Find pre-release and development versions, in addition to stable versions.  By default, pip only finds stable versions.
+#. Find pre-release and development versions, in addition to stable versions.  By default, pip only finds stable versions.
 
- ::
+    ::
 
-  $ pip install --pre SomePackage
+      $ pip install --pre SomePackage
 
+----
 
+.. [1] This is true with the exception that pip v7.0 and v7.0.1 required quotes
+       around specifiers containing environment markers in requirement files.
 
-.. _PyPI: http://pypi.python.org/pypi/
-.. _setuptools extras: http://packages.python.org/setuptools/setuptools.html#declaring-extras-optional-features-with-their-own-dependencies
+.. _PEP440: http://www.python.org/dev/peps/pep-0440
+.. _PEP508: http://www.python.org/dev/peps/pep-0508
diff --git a/docs/reference/pip_list.rst b/docs/reference/pip_list.rst
index feb5f212c48..b24e6019c77 100644
--- a/docs/reference/pip_list.rst
+++ b/docs/reference/pip_list.rst
@@ -26,20 +26,69 @@ Options
 Examples
 ********
 
-1) List installed packages.
+#. List installed packages.
 
- ::
+    ::
 
-  $ pip list
-  Pygments (1.5)
-  docutils (0.9.1)
-  Sphinx (1.1.2)
-  Jinja2 (2.6)
+     $ pip list
+     docutils (0.10)
+     Jinja2 (2.7.2)
+     MarkupSafe (0.18)
+     Pygments (1.6)
+     Sphinx (1.2.1)
 
-2) List outdated packages (excluding editables), and the latest version available
+#. List outdated packages (excluding editables), and the latest version available.
 
- ::
+    ::
 
-  $ pip list --outdated
-  docutils (Current: 0.9.1 Latest: 0.10)
-  Sphinx (Current: 1.1.2 Latest: 1.1.3)
+     $ pip list --outdated
+     docutils (Current: 0.10 Latest: 0.11)
+     Sphinx (Current: 1.2.1 Latest: 1.2.2)
+
+#. List installed packages with column formatting.
+
+    ::
+
+     $ pip list --columns
+     Package Version
+     ------- -------
+     docopt  0.6.2
+     idlex   1.13
+     jedi    0.9.0
+
+#. List outdated packages with column formatting.
+
+    ::
+
+     $ pip list -o --columns
+     Package    Version Latest Type
+     ---------- ------- ------ -----
+     retry      0.8.1   0.9.1  wheel
+     setuptools 20.6.7  21.0.0 wheel
+
+#. Use legacy formatting
+
+    ::
+
+     $ pip list --format=legacy
+     colorama (0.3.7)
+     docopt (0.6.2)
+     idlex (1.13)
+     jedi (0.9.0)
+
+#. Use json formatting
+
+    ::
+
+     $ pip list --format=json
+     [{'name': 'colorama', 'version': '0.3.7'}, {'name': 'docopt', 'version': '0.6.2'}, ...
+
+#. Use freeze formatting
+
+    ::
+
+     $ pip list --format=freeze
+     colorama==0.3.7
+     docopt==0.6.2
+     idlex==1.13
+     jedi==0.9.0
diff --git a/docs/reference/pip_search.rst b/docs/reference/pip_search.rst
index 4a2a2dd78d2..1332c99f781 100644
--- a/docs/reference/pip_search.rst
+++ b/docs/reference/pip_search.rst
@@ -25,12 +25,10 @@ Options
 Examples
 ********
 
-1. Search for "peppercorn"
+#. Search for "peppercorn"
 
- ::
+    ::
 
-  $ pip search peppercorn
-  pepperedform    - Helpers for using peppercorn with formprocess.
-  peppercorn      - A library for converting a token stream into [...]
-
-.. _`pip wheel`:
+     $ pip search peppercorn
+     pepperedform    - Helpers for using peppercorn with formprocess.
+     peppercorn      - A library for converting a token stream into [...]
diff --git a/docs/reference/pip_show.rst b/docs/reference/pip_show.rst
index 0fc505810c9..6c9aa84aae4 100644
--- a/docs/reference/pip_show.rst
+++ b/docs/reference/pip_show.rst
@@ -5,11 +5,13 @@ pip show
 
 .. contents::
 
+
 Usage
 *****
 
 .. pip-command-usage:: show
 
+
 Description
 ***********
 
@@ -25,13 +27,60 @@ Options
 Examples
 ********
 
-1. Show information about a package:
+#. Show information about a package:
+
+    ::
+
+      $ pip show sphinx
+      Name: Sphinx
+      Version: 1.4.5
+      Summary: Python documentation generator
+      Home-page: http://sphinx-doc.org/
+      Author: Georg Brandl
+      Author-email: georg@python.org
+      License: BSD
+      Location: /my/env/lib/python2.7/site-packages
+      Requires: docutils, snowballstemmer, alabaster, Pygments, imagesize, Jinja2, babel, six
+
+#. Show all information about a package
 
-  ::
+    ::
 
-    $ pip show sphinx
-    ---
-    Name: Sphinx
-    Version: 1.1.3
-    Location: /my/env/lib/pythonx.x/site-packages
-    Requires: Pygments, Jinja2, docutils
+      $ pip show --verbose sphinx
+      Name: Sphinx
+      Version: 1.4.5
+      Summary: Python documentation generator
+      Home-page: http://sphinx-doc.org/
+      Author: Georg Brandl
+      Author-email: georg@python.org
+      License: BSD
+      Location: /my/env/lib/python2.7/site-packages
+      Requires: docutils, snowballstemmer, alabaster, Pygments, imagesize, Jinja2, babel, six
+      Metadata-Version: 2.0
+      Installer:
+      Classifiers:
+        Development Status :: 5 - Production/Stable
+        Environment :: Console
+        Environment :: Web Environment
+        Intended Audience :: Developers
+        Intended Audience :: Education
+        License :: OSI Approved :: BSD License
+        Operating System :: OS Independent
+        Programming Language :: Python
+        Programming Language :: Python :: 2
+        Programming Language :: Python :: 3
+        Framework :: Sphinx
+        Framework :: Sphinx :: Extension
+        Framework :: Sphinx :: Theme
+        Topic :: Documentation
+        Topic :: Documentation :: Sphinx
+        Topic :: Text Processing
+        Topic :: Utilities
+      Entry-points:
+        [console_scripts]
+        sphinx-apidoc = sphinx.apidoc:main
+        sphinx-autogen = sphinx.ext.autosummary.generate:main
+        sphinx-build = sphinx:main
+        sphinx-quickstart = sphinx.quickstart:main
+        [distutils.commands]
+        build_sphinx = sphinx.setup_command:BuildDoc
diff --git a/docs/reference/pip_uninstall.rst b/docs/reference/pip_uninstall.rst
index 7025981f8ed..f9a97589eb5 100644
--- a/docs/reference/pip_uninstall.rst
+++ b/docs/reference/pip_uninstall.rst
@@ -24,7 +24,7 @@ Options
 Examples
 ********
 
-1) Uninstall a package.
+#. Uninstall a package.
 
   ::
 
diff --git a/docs/reference/pip_wheel.rst b/docs/reference/pip_wheel.rst
index e64f383c15c..75ae51a21fb 100644
--- a/docs/reference/pip_wheel.rst
+++ b/docs/reference/pip_wheel.rst
@@ -18,6 +18,42 @@ Description
 .. pip-command-description:: wheel
 
 
+Build System Interface
+++++++++++++++++++++++
+
+In order for pip to build a wheel, ``setup.py`` must implement the
+``bdist_wheel`` command with the following syntax::
+
+    python setup.py bdist_wheel -d TARGET
+
+This command must create a wheel compatible with the invoking Python
+interpreter, and save that wheel in the directory TARGET.
+
+No other build system commands are invoked by the ``pip wheel`` command.
+
+Customising the build
+~~~~~~~~~~~~~~~~~~~~~
+
+It is possible using ``--global-option`` to include additional build commands
+with their arguments in the ``setup.py`` command. This is currently the only
+way to influence the building of C extensions from the command line. For
+example::
+
+    pip wheel --global-option bdist_ext --global-option -DFOO wheel
+
+will result in a build command of
+
+::
+
+    setup.py bdist_ext -DFOO bdist_wheel -d TARGET
+
+which passes a preprocessor symbol to the extension build.
+
+Such usage is considered highly build-system specific and more an accident of
+the current implementation than a supported interface.
+
+
+
 Options
 *******
 
@@ -29,9 +65,9 @@ Options
 Examples
 ********
 
-1. Build wheels for a requirement (and all its dependencies), and then install
+#. Build wheels for a requirement (and all its dependencies), and then install
 
-  ::
+    ::
 
-    $ pip wheel --wheel-dir=/tmp/wheelhouse SomePackage
-    $ pip install --no-index --find-links=/tmp/wheelhouse SomePackage
+      $ pip wheel --wheel-dir=/tmp/wheelhouse SomePackage
+      $ pip install --no-index --find-links=/tmp/wheelhouse SomePackage
diff --git a/docs/usage.rst b/docs/usage.rst
index 5108803d625..20c2b29ec39 100644
--- a/docs/usage.rst
+++ b/docs/usage.rst
@@ -5,4 +5,3 @@ Usage
 ==========
 
 The "Usage" section is now covered in the :doc:`Reference Guide `
-
diff --git a/docs/user_guide.rst b/docs/user_guide.rst
index cd0146f53ab..2b9c51dc4f5 100644
--- a/docs/user_guide.rst
+++ b/docs/user_guide.rst
@@ -23,6 +23,8 @@ Specifiers`
 
 For more information and examples, see the :ref:`pip install` reference.
 
+.. _PyPI: http://pypi.python.org/pypi
+
 
 .. _`Requirements Files`:
 
@@ -40,7 +42,8 @@ installed using :ref:`pip install` like so:
 Details on the format of the files are here: :ref:`Requirements File Format`.
 
 Logically, a Requirements file is just a list of :ref:`pip install` arguments
-placed in a file.
+placed in a file. Note that you should not rely on the items in the file being
+installed by pip in any particular order.
 
 In practice, there are 4 common uses of Requirements files:
 
@@ -71,7 +74,6 @@ In practice, there are 4 common uses of Requirements files:
      pkg2
      pkg3>=1.0,<=2.0
 
-
 3. Requirements files are used to force pip to install an alternate version of a
    sub-dependency.  For example, suppose `ProjectA` in your requirements file
    requires `ProjectB`, but the latest version (v1.3) has a bug, you can force
@@ -85,7 +87,7 @@ In practice, there are 4 common uses of Requirements files:
 4. Requirements files are used to override a dependency with a local patch that
    lives in version control.  For example, suppose a dependency,
    `SomeDependency` from PyPI has a bug, and you can't wait for an upstream fix.
-   You could clone/copy the src, make the fix, and place it in vcs with the tag
+   You could clone/copy the src, make the fix, and place it in VCS with the tag
    `sometag`.  You'd reference it in your requirements file with a line like so:
 
    ::
@@ -99,7 +101,7 @@ In practice, there are 4 common uses of Requirements files:
 
 It's important to be clear that pip determines package dependencies using
 `install_requires metadata
-`_,
+`_,
 not by discovering `requirements.txt` files embedded in projects.
 
 See also:
@@ -107,9 +109,42 @@ See also:
 * :ref:`Requirements File Format`
 * :ref:`pip freeze`
 * `"setup.py vs requirements.txt" (an article by Donald Stufft)
-  `_
+  `_
+
+
+.. _`Constraints Files`:
+
+Constraints Files
+*****************
+
+Constraints files are requirements files that only control which version of a
+requirement is installed, not whether it is installed or not. Their syntax and
+contents is nearly identical to :ref:`Requirements Files`. There is one key
+difference: Including a package in a constraints file does not trigger
+installation of the package.
+
+Use a constraints file like so:
 
+ ::
+
+   pip install -c constraints.txt
+
+Constraints files are used for exactly the same reason as requirements files
+when you don't know exactly what things you want to install. For instance, say
+that the "helloworld" package doesn't work in your environment, so you have a
+local patched version. Some things you install depend on "helloworld", and some
+don't.
+
+One way to ensure that the patched version is used consistently is to
+manually audit the dependencies of everything you install, and if "helloworld"
+is present, write a requirements file to use when installing that thing.
 
+Constraints files offer a better way: write a single constraints file for your
+organisation and use that everywhere. If the thing being installed requires
+"helloworld" to be installed, your fixed version specified in your constraints
+file will be used.
+
+Constraints file support was added in pip 7.1.
 
 .. _`Installing from Wheels`:
 
@@ -118,12 +153,12 @@ Installing from Wheels
 
 "Wheel" is a built, archive format that can greatly speed installation compared
 to building and installing from source archives. For more information, see the
-`Wheel docs `_ ,
+`Wheel docs `_ ,
 `PEP427 `_, and
 `PEP425 `_
 
 Pip prefers Wheels where they are available. To disable this, use the
-:ref:`--no-use-wheel ` flag for :ref:`pip install`.
+:ref:`--no-binary ` flag for :ref:`pip install`.
 
 If no satisfactory wheels are found, pip will default to finding source archives.
 
@@ -180,10 +215,10 @@ To list installed packages:
 ::
 
   $ pip list
-  Pygments (1.5)
   docutils (0.9.1)
-  Sphinx (1.1.2)
   Jinja2 (2.6)
+  Pygments (1.5)
+  Sphinx (1.1.2)
 
 To list outdated packages, and show the latest version available:
 
@@ -237,12 +272,54 @@ pip allows you to set all command line option defaults in a standard ini
 style config file.
 
 The names and locations of the configuration files vary slightly across
-platforms.
+platforms. You may have per-user, per-virtualenv or site-wide (shared amongst
+all users) configuration:
+
+**Per-user**:
+
+* On Unix the default configuration file is: :file:`$HOME/.config/pip/pip.conf`
+  which respects the ``XDG_CONFIG_HOME`` environment variable.
+* On Mac OS X the configuration file is
+  :file:`$HOME/Library/Application Support/pip/pip.conf`.
+* On Windows the configuration file is :file:`%APPDATA%\\pip\\pip.ini`.
+
+There are also a legacy per-user configuration file which is also respected,
+these are located at:
 
 * On Unix and Mac OS X the configuration file is: :file:`$HOME/.pip/pip.conf`
-* On Windows, the configuration file is: :file:`%HOME%\\pip\\pip.ini`
+* On Windows the configuration file is: :file:`%HOME%\\pip\\pip.ini`
+
+You can set a custom path location for this config file using the environment
+variable ``PIP_CONFIG_FILE``.
+
+**Inside a virtualenv**:
 
-You can set a custom path location for the config file using the environment variable ``PIP_CONFIG_FILE``.
+* On Unix and Mac OS X the file is :file:`$VIRTUAL_ENV/pip.conf`
+* On Windows the file is: :file:`%VIRTUAL_ENV%\\pip.ini`
+
+**Site-wide**:
+
+* On Unix the file may be located in :file:`/etc/pip.conf`. Alternatively
+  it may be in a "pip" subdirectory of any of the paths set in the
+  environment variable ``XDG_CONFIG_DIRS`` (if it exists), for example
+  :file:`/etc/xdg/pip/pip.conf`.
+* On Mac OS X the file is: :file:`/Library/Application Support/pip/pip.conf`
+* On Windows XP the file is:
+  :file:`C:\\Documents and Settings\\All Users\\Application Data\\pip\\pip.ini`
+* On Windows 7 and later the file is hidden, but writeable at
+  :file:`C:\\ProgramData\\pip\\pip.ini`
+* Site-wide configuration is not supported on Windows Vista
+
+If multiple configuration files are found by pip then they are combined in
+the following order:
+
+1. Firstly the site-wide file is read, then
+2. The per-user file is read, and finally
+3. The virtualenv-specific file is read.
+
+Each file read overrides any values read from previous files, so if the
+global timeout is specified in both the site-wide file and the per-user file
+then the latter value is the one that will be used.
 
 The names of the settings are derived from the long command line option, e.g.
 if you want to use a different package index (``--index-url``) and set the
@@ -279,6 +356,17 @@ set like this:
     ignore-installed = true
     no-dependencies = yes
 
+To enable the boolean options ``--no-compile`` and ``--no-cache-dir``, falsy
+values have to be used:
+
+.. code-block:: ini
+
+    [global]
+    no-cache-dir = false
+
+    [install]
+    no-compile = no
+
 Appending options like ``--find-links`` can be written on multiple lines:
 
 .. code-block:: ini
@@ -347,49 +435,69 @@ To setup for zsh::
     $ pip completion --zsh >> ~/.zprofile
 
 Alternatively, you can use the result of the ``completion`` command
-directly with the eval function of you shell, e.g. by adding the following to your startup file::
+directly with the eval function of your shell, e.g. by adding the following to your startup file::
 
     eval "`pip completion --bash`"
 
 
 
-.. _`Fast & Local Installs`:
+.. _`Installing from local packages`:
 
-Fast & Local Installs
-*********************
+Installing from local packages
+******************************
 
-Often, you will want a fast install from local archives, without probing PyPI.
+In some cases, you may want to install from local packages only, with no traffic
+to PyPI.
 
 First, download the archives that fulfill your requirements::
 
-$ pip install --download  -r requirements.txt
+$ pip install --download DIR -r requirements.txt
+
 
-Then, install using  :ref:`--find-links <--find-links>` and :ref:`--no-index <--no-index>`::
+Note that ``pip install --download`` will look in your wheel cache first, before
+trying to download from PyPI.  If you've never installed your requirements
+before, you won't have a wheel cache for those items.  In that case, if some of
+your requirements don't come as wheels from PyPI, and you want wheels, then run
+this instead::
 
-$ pip install --no-index --find-links=[file://] -r requirements.txt
+$ pip wheel --wheel-dir DIR -r requirements.txt
 
 
-Non-recursive upgrades
-************************
+Then, to install from local only, you'll be using :ref:`--find-links
+<--find-links>` and :ref:`--no-index <--no-index>` like so::
 
-``pip install --upgrade`` is currently written to perform a recursive upgrade.
+$ pip install --no-index --find-links=DIR -r requirements.txt
+
+
+"Only if needed" Recursive Upgrade
+**********************************
+
+``pip install --upgrade`` is currently written to perform an eager recursive
+upgrade, i.e. it upgrades all dependencies regardless of whether they still
+satisfy the new parent requirements.
 
 E.g. supposing:
 
 * `SomePackage-1.0` requires `AnotherPackage>=1.0`
-* `SomePackage-2.0` requires `AnotherPackage>=1.0` and `OneMorePoject==1.0`
+* `SomePackage-2.0` requires `AnotherPackage>=1.0` and `OneMorePackage==1.0`
 * `SomePackage-1.0` and `AnotherPackage-1.0` are currently installed
 * `SomePackage-2.0` and `AnotherPackage-2.0` are the latest versions available on PyPI.
 
-Running ``pip install --upgrade SomePackage`` would upgrade `SomePackage` *and* `AnotherPackage`
-despite `AnotherPackage` already being satisifed.
+Running ``pip install --upgrade SomePackage`` would upgrade `SomePackage` *and*
+`AnotherPackage` despite `AnotherPackage` already being satisfied.
 
-If you would like to perform a non-recursive upgrade perform these 2 steps::
+pip doesn't currently have an option to do an "only if needed" recursive
+upgrade, but you can achieve it using these 2 steps::
 
   pip install --upgrade --no-deps SomePackage
   pip install SomePackage
 
-The first line will upgrade `SomePackage`, but not dependencies like `AnotherPackage`.  The 2nd line will fill in new dependencies like `OneMorePackage`.
+The first line will upgrade `SomePackage`, but not dependencies like
+`AnotherPackage`.  The 2nd line will fill in new dependencies like
+`OneMorePackage`.
+
+See :issue:`59` for a plan of making "only if needed" recursive the default
+behavior for a new ``pip upgrade`` command.
 
 
 User Installs
@@ -485,25 +593,81 @@ From within a real python, where ``SomePackage`` *is* installed globally, and is
 Ensuring Repeatability
 **********************
 
-Three things are required to fully guarantee a repeatable installation using requirements files.
+pip can achieve various levels of repeatability:
+
+Pinned Version Numbers
+----------------------
+
+Pinning the versions of your dependencies in the requirements file
+protects you from bugs or incompatibilities in newly released versions::
+
+    SomePackage == 1.2.3
+    DependencyOfSomePackage == 4.5.6
+
+Using :ref:`pip freeze` to generate the requirements file will ensure that not
+only the top-level dependencies are included but their sub-dependencies as
+well, and so on. Perform the installation using :ref:`--no-deps
+` for an extra dose of insurance against installing
+anything not explicitly listed.
+
+This strategy is easy to implement and works across OSes and architectures.
+However, it trusts PyPI and the certificate authority chain. It
+also relies on indices and find-links locations not allowing
+packages to change without a version increase. (PyPI does protect
+against this.)
+
+Hash-checking Mode
+------------------
+
+Beyond pinning version numbers, you can add hashes against which to verify
+downloaded packages::
+
+    FooProject == 1.2 --hash=sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
+
+This protects against a compromise of PyPI or the HTTPS
+certificate chain. It also guards against a package changing
+without its version number changing (on indexes that allow this).
+This approach is a good fit for automated server deployments.
+
+Hash-checking mode is a labor-saving alternative to running a private index
+server containing approved packages: it removes the need to upload packages,
+maintain ACLs, and keep an audit trail (which a VCS gives you on the
+requirements file for free). It can also substitute for a vendor library,
+providing easier upgrades and less VCS noise. It does not, of course,
+provide the availability benefits of a private index or a vendor library.
+
+For more, see :ref:`pip install\'s discussion of hash-checking mode `.
+
+.. _`Installation Bundle`:
+
+Installation Bundles
+--------------------
+
+Using :ref:`pip wheel`, you can bundle up all of a project's dependencies, with
+any compilation done, into a single archive. This allows installation when
+index servers are unavailable and avoids time-consuming recompilation. Create
+an archive like this::
+
+    $ tempdir=$(mktemp -d /tmp/wheelhouse-XXXXX)
+    $ pip wheel -r requirements.txt --wheel-dir=$tempdir
+    $ cwd=`pwd`
+    $ (cd "$tempdir"; tar -cjvf "$cwd/bundled.tar.bz2" *)
 
-1. The requirements file was generated by ``pip freeze`` or you're sure it only
-   contains requirements that specify a specific version.
+You can then install from the archive like this::
 
-2. The installation is performed using :ref:`--no-deps `.
-   This guarantees that only what is explicitly listed in the requirements file is
-   installed.
+    $ tempdir=$(mktemp -d /tmp/wheelhouse-XXXXX)
+    $ (cd $tempdir; tar -xvf /path/to/bundled.tar.bz2)
+    $ pip install --force-reinstall --ignore-installed --upgrade --no-index --no-deps $tempdir/*
 
-3. The installation is performed against an index or find-links location that is
-   guaranteed to *not* allow archives to be changed and updated without a
-   version increase.  Unfortunately, this is *not* true on PyPI. It is possible
-   for the same pypi distribution to have a different hash over time. Project
-   authors are allowed to delete a distribution, and then upload a new one with
-   the same name and version, but a different hash. See `Issue #1175
-   `_ for plans to add hash
-   confirmation to pip, or a new "lock file" notion, but for now, know that the `peep
-   project `_ offers this feature on top of pip
-   using requirements file comments.
+Note that compiled packages are typically OS- and architecture-specific, so
+these archives are not necessarily portable across machines.
 
+Hash-checking mode can be used along with this method to ensure that future
+archives are built with identical packages.
 
-.. _PyPI: http://pypi.python.org/pypi/
+.. warning::
+    Finally, beware of the ``setup_requires`` keyword arg in :file:`setup.py`.
+    The (rare) packages that use it will cause those dependencies to be
+    downloaded by setuptools directly, skipping pip's protections. If you need
+    to use such a package, see :ref:`Controlling
+    setup_requires`.
diff --git a/pip/__init__.py b/pip/__init__.py
index bb4a588a690..385ca7fab19 100755
--- a/pip/__init__.py
+++ b/pip/__init__.py
@@ -1,16 +1,28 @@
 #!/usr/bin/env python
+from __future__ import absolute_import
+
+import locale
+import logging
 import os
 import optparse
+import warnings
 
 import sys
 import re
 
 from pip.exceptions import InstallationError, CommandError, PipError
-from pip.log import logger
-from pip.util import get_installed_distributions, get_prog
+from pip.utils import get_installed_distributions, get_prog
+from pip.utils import deprecation, dist_is_editable
 from pip.vcs import git, mercurial, subversion, bazaar  # noqa
 from pip.baseparser import ConfigOptionParser, UpdatingDefaultsHelpFormatter
-from pip.commands import commands, get_summaries, get_similar_commands
+from pip.commands import get_summaries, get_similar_commands
+from pip.commands import commands_dict
+from pip._vendor.requests.packages.urllib3.exceptions import (
+    InsecureRequestWarning,
+)
+
+
+# assignment for flake8 to be happy
 
 # This fixes a peculiarity when importing via __import__ - as we are
 # initialising the pip module, "from pip import cmdoptions" is recursive
@@ -19,7 +31,13 @@
 cmdoptions = pip.cmdoptions
 
 # The version as used in the setup.py and the docs conf.py
-__version__ = "1.5.6"
+__version__ = "8.2.0.dev0"
+
+
+logger = logging.getLogger(__name__)
+
+# Hide the InsecureRequestWarning from urllib3
+warnings.filterwarnings("ignore", category=InsecureRequestWarning)
 
 
 def autocomplete():
@@ -65,7 +83,7 @@ def autocomplete():
                     print(dist)
                 sys.exit(1)
 
-        subcommand = commands[subcommand_name]()
+        subcommand = commands_dict[subcommand_name]()
         options += [(opt.get_opt_string(), opt.nargs)
                     for opt in subcommand.parser.option_list_all
                     if opt.help != optparse.SUPPRESS_HELP]
@@ -109,13 +127,13 @@ def create_main_parser():
 
     pip_pkg_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
     parser.version = 'pip %s from %s (python %s)' % (
-        __version__,  pip_pkg_dir, sys.version[:3])
+        __version__, pip_pkg_dir, sys.version[:3])
 
     # add the general options
     gen_opts = cmdoptions.make_option_group(cmdoptions.general_group, parser)
     parser.add_option_group(gen_opts)
 
-    parser.main = True # so the help formatter knows
+    parser.main = True  # so the help formatter knows
 
     # create command listing for description
     command_summaries = get_summaries()
@@ -128,8 +146,8 @@ def create_main_parser():
 def parseopts(args):
     parser = create_main_parser()
 
-    # Note: parser calls disable_interspersed_args(), so the result of this call
-    # is to split the initial args into the general options before the
+    # Note: parser calls disable_interspersed_args(), so the result of this
+    # call is to split the initial args into the general options before the
     # subcommand and everything else.
     # For example:
     #  args: ['--timeout=5', 'install', '--user', 'INITools']
@@ -149,13 +167,9 @@ def parseopts(args):
         sys.exit()
 
     # the subcommand name
-    cmd_name = args_else[0].lower()
+    cmd_name = args_else[0]
 
-    #all the args without the subcommand
-    cmd_args = args[:]
-    cmd_args.remove(args_else[0].lower())
-
-    if cmd_name not in commands:
+    if cmd_name not in commands_dict:
         guess = get_similar_commands(cmd_name)
 
         msg = ['unknown command "%s"' % cmd_name]
@@ -164,41 +178,51 @@ def parseopts(args):
 
         raise CommandError(' - '.join(msg))
 
+    # all the args without the subcommand
+    cmd_args = args[:]
+    cmd_args.remove(cmd_name)
+
     return cmd_name, cmd_args
 
 
-def main(initial_args=None):
-    if initial_args is None:
-        initial_args = sys.argv[1:]
+def check_isolated(args):
+    isolated = False
+
+    if "--isolated" in args:
+        isolated = True
+
+    return isolated
+
+
+def main(args=None):
+    if args is None:
+        args = sys.argv[1:]
+
+    # Configure our deprecation warnings to be sent through loggers
+    deprecation.install_warning_logger()
 
     autocomplete()
 
     try:
-        cmd_name, cmd_args = parseopts(initial_args)
-    except PipError:
-        e = sys.exc_info()[1]
-        sys.stderr.write("ERROR: %s" % e)
+        cmd_name, cmd_args = parseopts(args)
+    except PipError as exc:
+        sys.stderr.write("ERROR: %s" % exc)
         sys.stderr.write(os.linesep)
         sys.exit(1)
 
-    command = commands[cmd_name]()
-    return command.main(cmd_args)
-
-
-def bootstrap():
-    """
-    Bootstrapping function to be called from install-pip.py script.
-    """
-    pkgs = ['pip']
+    # Needed for locale.getpreferredencoding(False) to work
+    # in pip.utils.encoding.auto_decode
     try:
-        import setuptools
-    except ImportError:
-        pkgs.append('setuptools')
-    return main(['install', '--upgrade'] + pkgs + sys.argv[1:])
+        locale.setlocale(locale.LC_ALL, '')
+    except locale.Error as e:
+        # setlocale can apparently crash if locale are uninitialized
+        logger.debug("Ignoring error %s when setting locale", e)
+    command = commands_dict[cmd_name](isolated=check_isolated(cmd_args))
+    return command.main(cmd_args)
 
-############################################################
-## Writing freeze files
 
+# ###########################################################
+# # Writing freeze files
 
 class FrozenRequirement(object):
 
@@ -212,48 +236,68 @@ def __init__(self, name, req, editable, comments=()):
     _date_re = re.compile(r'-(20\d\d\d\d\d\d)$')
 
     @classmethod
-    def from_dist(cls, dist, dependency_links, find_tags=False):
+    def from_dist(cls, dist, dependency_links):
         location = os.path.normcase(os.path.abspath(dist.location))
         comments = []
         from pip.vcs import vcs, get_src_requirement
-        if vcs.get_backend_name(location):
+        if dist_is_editable(dist) and vcs.get_backend_name(location):
             editable = True
             try:
-                req = get_src_requirement(dist, location, find_tags)
-            except InstallationError:
-                ex = sys.exc_info()[1]
-                logger.warn("Error when trying to get requirement for VCS system %s, falling back to uneditable format" % ex)
+                req = get_src_requirement(dist, location)
+            except InstallationError as exc:
+                logger.warning(
+                    "Error when trying to get requirement for VCS system %s, "
+                    "falling back to uneditable format", exc
+                )
                 req = None
             if req is None:
-                logger.warn('Could not determine repository location of %s' % location)
-                comments.append('## !! Could not determine repository location')
+                logger.warning(
+                    'Could not determine repository location of %s', location
+                )
+                comments.append(
+                    '## !! Could not determine repository location'
+                )
                 req = dist.as_requirement()
                 editable = False
         else:
             editable = False
             req = dist.as_requirement()
             specs = req.specs
-            assert len(specs) == 1 and specs[0][0] == '=='
+            assert len(specs) == 1 and specs[0][0] in ["==", "==="], \
+                'Expected 1 spec with == or ===; specs = %r; dist = %r' % \
+                (specs, dist)
             version = specs[0][1]
             ver_match = cls._rev_re.search(version)
             date_match = cls._date_re.search(version)
             if ver_match or date_match:
                 svn_backend = vcs.get_backend('svn')
                 if svn_backend:
-                    svn_location = svn_backend(
-                        ).get_location(dist, dependency_links)
+                    svn_location = svn_backend().get_location(
+                        dist,
+                        dependency_links,
+                    )
                 if not svn_location:
-                    logger.warn(
-                        'Warning: cannot find svn location for %s' % req)
-                    comments.append('## FIXME: could not find svn URL in dependency_links for this package:')
+                    logger.warning(
+                        'Warning: cannot find svn location for %s', req)
+                    comments.append(
+                        '## FIXME: could not find svn URL in dependency_links '
+                        'for this package:'
+                    )
                 else:
-                    comments.append('# Installing as editable to satisfy requirement %s:' % req)
+                    comments.append(
+                        '# Installing as editable to satisfy requirement %s:' %
+                        req
+                    )
                     if ver_match:
                         rev = ver_match.group(1)
                     else:
                         rev = '{%s}' % date_match.group(1)
                     editable = True
-                    req = '%s@%s#egg=%s' % (svn_location, rev, cls.egg_name(dist))
+                    req = '%s@%s#egg=%s' % (
+                        svn_location,
+                        rev,
+                        cls.egg_name(dist)
+                    )
         return cls(dist.project_name, req, editable, comments)
 
     @staticmethod
@@ -272,6 +316,4 @@ def __str__(self):
 
 
 if __name__ == '__main__':
-    exit = main()
-    if exit:
-        sys.exit(exit)
+    sys.exit(main())
diff --git a/pip/__main__.py b/pip/__main__.py
index 5ca3746342c..5556539cb71 100644
--- a/pip/__main__.py
+++ b/pip/__main__.py
@@ -1,7 +1,19 @@
+from __future__ import absolute_import
+
+import os
 import sys
-from .runner import run
+
+# If we are running from a wheel, add the wheel to sys.path
+# This allows the usage python pip-*.whl/pip install pip-*.whl
+if __package__ == '':
+    # __file__ is pip-*.whl/pip/__main__.py
+    # first dirname call strips of '/__main__.py', second strips off '/pip'
+    # Resulting path is the name of the wheel itself
+    # Add that to sys.path so we can import pip
+    path = os.path.dirname(os.path.dirname(__file__))
+    sys.path.insert(0, path)
+
+import pip  # noqa
 
 if __name__ == '__main__':
-    exit = run()
-    if exit:
-        sys.exit(exit)
+    sys.exit(pip.main())
diff --git a/pip/_vendor/Makefile b/pip/_vendor/Makefile
index 416249aca76..175e4166768 100644
--- a/pip/_vendor/Makefile
+++ b/pip/_vendor/Makefile
@@ -10,3 +10,4 @@ vendor:
 
 	@# Cleanup .egg-info directories
 	rm -rf *.egg-info
+	rm -rf *.dist-info
diff --git a/pip/_vendor/README.rst b/pip/_vendor/README.rst
index f2c9ee9b4ca..a9cddb114c5 100644
--- a/pip/_vendor/README.rst
+++ b/pip/_vendor/README.rst
@@ -1,32 +1,132 @@
 Policy
 ======
 
-Vendored libraries should not be modified except as required to actually
-successfully vendor them.
+* Vendored libraries **MUST** not be modified except as required to
+  successfully vendor them.
+
+* Vendored libraries **MUST** be released copies of libraries available on
+  PyPI.
+
+* The versions of libraries vendored in pip **MUST** be reflected in
+  ``pip/_vendor/vendor.txt``.
+
+* Vendored libraries **MUST** function without any build steps such as 2to3 or
+  compilation of C code, pratically this limits to single source 2.x/3.x and
+  pure Python.
+
+* Any modifications made to libraries **MUST** be noted in
+  ``pip/_vendor/README.rst``.
+
+
+Rationale
+---------
+
+Historically pip has not had any dependencies except for setuptools itself,
+choosing instead to implement any functionality it needed to prevent needing
+a dependency. However, starting with pip 1.5 we began to replace code that was
+implemented inside of pip with reusable libraries from PyPI. This brought the
+typical benefits of reusing libraries instead of reinventing the wheel like
+higher quality and more battle tested code, centralization of bug fixes
+(particularly security sensitive ones), and better/more features for less work.
+
+However, there is several issues with having dependencies in the traditional
+way (via ``install_requires``) for pip. These issues are:
+
+* Fragility. When pip depends on another library to function then if for
+  whatever reason that library either isn't installed or an incompatible
+  version is installed then pip ceases to function. This is of course true for
+  all Python applications, however for every application *except* for pip the
+  way you fix it is by re-running pip. Obviously when pip can't run you can't
+  use pip to fix pip so you're left having to manually resolve dependencies and
+  installing them by hand.
+
+* Making other libraries uninstallable. One of pip's current dependencies is
+  the requests library, for which pip requires a fairly recent version to run.
+  If pip dependended on requests in the traditional manner then we'd end up
+  needing to either maintain compatibility with every version of requests that
+  has ever existed (and will ever exist) or some subset of the versions of
+  requests available will simply become uninstallable depending on what version
+  of pip you're using. This is again a problem that is technically true for all
+  Python applications, however the nature of pip is that you're likely to have
+  pip installed in every single environment since it is installed by default
+  in Python, in pyvenv, and in virtualenv.
+
+* Security. On the surface this is oxymoronic since traditionally vendoring
+  tends to make it harder to update a dependent library for security updates
+  and that holds true for pip. However given the *other* reasons that exist for
+  pip to avoid dependencies the alternative (and what was done historically) is
+  for pip to reinvent the wheel itself. This led to pip having implemented
+  its own HTTPS verification routines to work around the lack of ssl
+  validation in the Python standard library which ended up having similar bugs
+  to validation routine in requests/urllib3 but which had to be discovered and
+  fixed independently. By reusing the libraries, even though we're vendoring,
+  we make it easier to keep pip secure by relying on the great work of our
+  dependencies *and* making it easier to actually fix security issues by simply
+  pulling in a newer version of the dependencies.
+
+* Bootstrapping. Currently most of the popular methods of installing pip rely
+  on the fact that pip is self contained to install pip itself. These tools
+  work by bundling a copy of pip, adding it to the sys.path and then executing
+  that copy of pip. This is done instead of implementing a "mini" installer to
+  again reduce duplication, pip already knows how to install a Python package
+  and is going to be vastly more battle tested than any sort of mini installer
+  could ever possibly be.
+
+Many downstream redistributors have policies against this kind of bundling and
+instead opt to patch the software they distribute to debundle it and make it
+rely on the global versions of the software that they already have packaged
+(which may have its own patches applied to it). We (the pip team) would prefer
+it if pip was *not* debundled in this manner due to the above reasons and
+instead we would prefer it if pip would be left intact as it is now. The one
+exception to this, is it is acceptable to remove the
+``pip/_vendor/requests/cacert.pem`` file provided you ensure that the
+``ssl.get_default_verify_paths().cafile`` API returns the correct CA bundle for
+your system. This will ensure that pip will use your system provided CA bundle
+instead of the copy bundled with pip.
+
+In the longer term, if someone has a *portable* solution to the above problems,
+other than the bundling method we currently use, that doesn't add additional
+problems that are unreasonable then we would be happy to consider, and possibly
+switch to said method. This solution must function correctly across all of the
+situation that we expect pip to be used and not mandate some external mechanism
+such as OS packages.
+
+
+pkg_resources
+-------------
+
+pkg_resources has been pulled in from setuptools 21.0.0
 
 
 Modifications
-=============
+-------------
 
 * html5lib has been modified to import six from pip._vendor
-* pkg_resources has been modified to import _markerlib from pip._vendor
-* markerlib has been modified to import it's API from pip._vendor
+* pkg_resources has been modified to import its externs from pip._vendor
+* CacheControl has been modified to import its dependencies from pip._vendor
+* packaging has been modified to import its dependencies from pip._vendor
 
 
-Markerlib and pkg_resources
-===========================
+Debundling
+----------
 
-Markerlib and pkg_resources has been pulled in from setuptools 3.4.4
+As mentioned in the rationale we, the pip team, would prefer it if pip was not
+debundled (other than optionally ``pip/_vendor/requests/cacert.pem``) and that
+pip was left intact. However, if you insist on doing so we have a
+semi-supported method that we do test in our CI but which requires a bit of
+extra work on your end to make it still solve the problems from above.
 
+1. Delete everything in ``pip/_vendor/`` **except** for
+   ``pip/_vendor/__init__.py``.
 
-Note to Downstream Distributors
-===============================
+2. Generate wheels for each of pip's dependencies (and any of their
+   dependencies) using your patched copies of these libraries. These must be
+   placed somewhere on the filesystem that pip can access, by default we will
+   assume you've placed them in ``pip/_vendor``.
 
-Libraries are vendored/bundled inside of this directory in order to prevent
-end users from needing to manually install packages if they accidently remove
-something that pip depends on.
+3. Modify ``pip/_vendor/__init__.py`` so that the ``DEBUNDLED`` variable is
+   ``True``.
 
-All bundled packages exist in the ``pip._vendor`` namespace, and the versions
-(fetched from PyPI) that we use are located in vendor.txt. If you remove
-``pip._vendor.*`` you'll also need to update the import statements that import
-these packages.
+4. *(Optional)* If you've placed the wheels in a location other than
+   ``pip/_vendor/`` then modify ``pip/_vendor/__init__.py`` so that the
+   ``WHEEL_DIR`` variable points to the location you've placed them.
diff --git a/pip/_vendor/__init__.py b/pip/_vendor/__init__.py
index f233ca0d3a2..d0e7b3407c8 100644
--- a/pip/_vendor/__init__.py
+++ b/pip/_vendor/__init__.py
@@ -6,3 +6,101 @@
 updated to versions from upstream.
 """
 from __future__ import absolute_import
+
+import glob
+import os.path
+import sys
+
+# Downstream redistributors which have debundled our dependencies should also
+# patch this value to be true. This will trigger the additional patching
+# to cause things like "six" to be available as pip.
+DEBUNDLED = False
+
+# By default, look in this directory for a bunch of .whl files which we will
+# add to the beginning of sys.path before attempting to import anything. This
+# is done to support downstream re-distributors like Debian and Fedora who
+# wish to create their own Wheels for our dependencies to aid in debundling.
+WHEEL_DIR = os.path.abspath(os.path.dirname(__file__))
+
+
+# Define a small helper function to alias our vendored modules to the real ones
+# if the vendored ones do not exist. This idea of this was taken from
+# https://github.com/kennethreitz/requests/pull/2567.
+def vendored(modulename):
+    vendored_name = "{0}.{1}".format(__name__, modulename)
+
+    try:
+        __import__(vendored_name, globals(), locals(), level=0)
+    except ImportError:
+        try:
+            __import__(modulename, globals(), locals(), level=0)
+        except ImportError:
+            # We can just silently allow import failures to pass here. If we
+            # got to this point it means that ``import pip._vendor.whatever``
+            # failed and so did ``import whatever``. Since we're importing this
+            # upfront in an attempt to alias imports, not erroring here will
+            # just mean we get a regular import error whenever pip *actually*
+            # tries to import one of these modules to use it, which actually
+            # gives us a better error message than we would have otherwise
+            # gotten.
+            pass
+        else:
+            sys.modules[vendored_name] = sys.modules[modulename]
+            base, head = vendored_name.rsplit(".", 1)
+            setattr(sys.modules[base], head, sys.modules[modulename])
+
+
+# If we're operating in a debundled setup, then we want to go ahead and trigger
+# the aliasing of our vendored libraries as well as looking for wheels to add
+# to our sys.path. This will cause all of this code to be a no-op typically
+# however downstream redistributors can enable it in a consistent way across
+# all platforms.
+if DEBUNDLED:
+    # Actually look inside of WHEEL_DIR to find .whl files and add them to the
+    # front of our sys.path.
+    sys.path[:] = glob.glob(os.path.join(WHEEL_DIR, "*.whl")) + sys.path
+
+    # Actually alias all of our vendored dependencies.
+    vendored("cachecontrol")
+    vendored("colorama")
+    vendored("distlib")
+    vendored("html5lib")
+    vendored("lockfile")
+    vendored("six")
+    vendored("six.moves")
+    vendored("six.moves.urllib")
+    vendored("packaging")
+    vendored("packaging.version")
+    vendored("packaging.specifiers")
+    vendored("pkg_resources")
+    vendored("progress")
+    vendored("retrying")
+    vendored("requests")
+    vendored("requests.packages")
+    vendored("requests.packages.urllib3")
+    vendored("requests.packages.urllib3._collections")
+    vendored("requests.packages.urllib3.connection")
+    vendored("requests.packages.urllib3.connectionpool")
+    vendored("requests.packages.urllib3.contrib")
+    vendored("requests.packages.urllib3.contrib.ntlmpool")
+    vendored("requests.packages.urllib3.contrib.pyopenssl")
+    vendored("requests.packages.urllib3.exceptions")
+    vendored("requests.packages.urllib3.fields")
+    vendored("requests.packages.urllib3.filepost")
+    vendored("requests.packages.urllib3.packages")
+    vendored("requests.packages.urllib3.packages.ordered_dict")
+    vendored("requests.packages.urllib3.packages.six")
+    vendored("requests.packages.urllib3.packages.ssl_match_hostname")
+    vendored("requests.packages.urllib3.packages.ssl_match_hostname."
+             "_implementation")
+    vendored("requests.packages.urllib3.poolmanager")
+    vendored("requests.packages.urllib3.request")
+    vendored("requests.packages.urllib3.response")
+    vendored("requests.packages.urllib3.util")
+    vendored("requests.packages.urllib3.util.connection")
+    vendored("requests.packages.urllib3.util.request")
+    vendored("requests.packages.urllib3.util.response")
+    vendored("requests.packages.urllib3.util.retry")
+    vendored("requests.packages.urllib3.util.ssl_")
+    vendored("requests.packages.urllib3.util.timeout")
+    vendored("requests.packages.urllib3.util.url")
diff --git a/pip/_vendor/_markerlib/__init__.py b/pip/_vendor/_markerlib/__init__.py
deleted file mode 100644
index 197781a0f2c..00000000000
--- a/pip/_vendor/_markerlib/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-try:
-    import ast
-    from pip._vendor._markerlib.markers import default_environment, compile, interpret
-except ImportError:
-    if 'ast' in globals():
-        raise
-    def default_environment():
-        return {}
-    def compile(marker):
-        def marker_fn(environment=None, override=None):
-            # 'empty markers are True' heuristic won't install extra deps.
-            return not marker.strip()
-        marker_fn.__doc__ = marker
-        return marker_fn
-    def interpret(marker, environment=None, override=None):
-        return compile(marker)()
diff --git a/pip/_vendor/_markerlib/markers.py b/pip/_vendor/_markerlib/markers.py
deleted file mode 100644
index fa837061e02..00000000000
--- a/pip/_vendor/_markerlib/markers.py
+++ /dev/null
@@ -1,119 +0,0 @@
-# -*- coding: utf-8 -*-
-"""Interpret PEP 345 environment markers.
-
-EXPR [in|==|!=|not in] EXPR [or|and] ...
-
-where EXPR belongs to any of those:
-
-    python_version = '%s.%s' % (sys.version_info[0], sys.version_info[1])
-    python_full_version = sys.version.split()[0]
-    os.name = os.name
-    sys.platform = sys.platform
-    platform.version = platform.version()
-    platform.machine = platform.machine()
-    platform.python_implementation = platform.python_implementation()
-    a free string, like '2.6', or 'win32'
-"""
-
-__all__ = ['default_environment', 'compile', 'interpret']
-
-import ast
-import os
-import platform
-import sys
-import weakref
-
-_builtin_compile = compile
-
-try:
-    from platform import python_implementation
-except ImportError:
-    if os.name == "java":
-        # Jython 2.5 has ast module, but not platform.python_implementation() function.
-        def python_implementation():
-            return "Jython"
-    else:
-        raise
-
-
-# restricted set of variables
-_VARS = {'sys.platform': sys.platform,
-         'python_version': '%s.%s' % sys.version_info[:2],
-         # FIXME parsing sys.platform is not reliable, but there is no other
-         # way to get e.g. 2.7.2+, and the PEP is defined with sys.version
-         'python_full_version': sys.version.split(' ', 1)[0],
-         'os.name': os.name,
-         'platform.version': platform.version(),
-         'platform.machine': platform.machine(),
-         'platform.python_implementation': python_implementation(),
-         'extra': None # wheel extension
-        }
-
-for var in list(_VARS.keys()):
-    if '.' in var:
-        _VARS[var.replace('.', '_')] = _VARS[var]
-
-def default_environment():
-    """Return copy of default PEP 385 globals dictionary."""
-    return dict(_VARS)
-
-class ASTWhitelist(ast.NodeTransformer):
-    def __init__(self, statement):
-        self.statement = statement # for error messages
-
-    ALLOWED = (ast.Compare, ast.BoolOp, ast.Attribute, ast.Name, ast.Load, ast.Str)
-    # Bool operations
-    ALLOWED += (ast.And, ast.Or)
-    # Comparison operations
-    ALLOWED += (ast.Eq, ast.Gt, ast.GtE, ast.In, ast.Is, ast.IsNot, ast.Lt, ast.LtE, ast.NotEq, ast.NotIn)
-
-    def visit(self, node):
-        """Ensure statement only contains allowed nodes."""
-        if not isinstance(node, self.ALLOWED):
-            raise SyntaxError('Not allowed in environment markers.\n%s\n%s' %
-                               (self.statement,
-                               (' ' * node.col_offset) + '^'))
-        return ast.NodeTransformer.visit(self, node)
-
-    def visit_Attribute(self, node):
-        """Flatten one level of attribute access."""
-        new_node = ast.Name("%s.%s" % (node.value.id, node.attr), node.ctx)
-        return ast.copy_location(new_node, node)
-
-def parse_marker(marker):
-    tree = ast.parse(marker, mode='eval')
-    new_tree = ASTWhitelist(marker).generic_visit(tree)
-    return new_tree
-
-def compile_marker(parsed_marker):
-    return _builtin_compile(parsed_marker, '', 'eval',
-                   dont_inherit=True)
-
-_cache = weakref.WeakValueDictionary()
-
-def compile(marker):
-    """Return compiled marker as a function accepting an environment dict."""
-    try:
-        return _cache[marker]
-    except KeyError:
-        pass
-    if not marker.strip():
-        def marker_fn(environment=None, override=None):
-            """"""
-            return True
-    else:
-        compiled_marker = compile_marker(parse_marker(marker))
-        def marker_fn(environment=None, override=None):
-            """override updates environment"""
-            if override is None:
-                override = {}
-            if environment is None:
-                environment = default_environment()
-            environment.update(override)
-            return eval(compiled_marker, environment)
-    marker_fn.__doc__ = marker
-    _cache[marker] = marker_fn
-    return _cache[marker]
-
-def interpret(marker, environment=None):
-    return compile(marker)(environment)
diff --git a/pip/_vendor/cachecontrol/__init__.py b/pip/_vendor/cachecontrol/__init__.py
new file mode 100644
index 00000000000..724e220dcf6
--- /dev/null
+++ b/pip/_vendor/cachecontrol/__init__.py
@@ -0,0 +1,11 @@
+"""CacheControl import Interface.
+
+Make it easy to import from cachecontrol without long namespaces.
+"""
+__author__ = 'Eric Larson'
+__email__ = 'eric@ionrock.org'
+__version__ = '0.11.6'
+
+from .wrapper import CacheControl
+from .adapter import CacheControlAdapter
+from .controller import CacheController
diff --git a/pip/_vendor/cachecontrol/_cmd.py b/pip/_vendor/cachecontrol/_cmd.py
new file mode 100644
index 00000000000..afdcc88c27e
--- /dev/null
+++ b/pip/_vendor/cachecontrol/_cmd.py
@@ -0,0 +1,60 @@
+import logging
+
+from pip._vendor import requests
+
+from pip._vendor.cachecontrol.adapter import CacheControlAdapter
+from pip._vendor.cachecontrol.cache import DictCache
+from pip._vendor.cachecontrol.controller import logger
+
+from argparse import ArgumentParser
+
+
+def setup_logging():
+    logger.setLevel(logging.DEBUG)
+    handler = logging.StreamHandler()
+    logger.addHandler(handler)
+
+
+def get_session():
+    adapter = CacheControlAdapter(
+        DictCache(),
+        cache_etags=True,
+        serializer=None,
+        heuristic=None,
+    )
+    sess = requests.Session()
+    sess.mount('http://', adapter)
+    sess.mount('https://', adapter)
+
+    sess.cache_controller = adapter.controller
+    return sess
+
+
+def get_args():
+    parser = ArgumentParser()
+    parser.add_argument('url', help='The URL to try and cache')
+    return parser.parse_args()
+
+
+def main(args=None):
+    args = get_args()
+    sess = get_session()
+
+    # Make a request to get a response
+    resp = sess.get(args.url)
+
+    # Turn on logging
+    setup_logging()
+
+    # try setting the cache
+    sess.cache_controller.cache_response(resp.request, resp.raw)
+
+    # Now try to get it
+    if sess.cache_controller.cached_request(resp.request):
+        print('Cached!')
+    else:
+        print('Not cached :(')
+
+
+if __name__ == '__main__':
+    main()
diff --git a/pip/_vendor/cachecontrol/adapter.py b/pip/_vendor/cachecontrol/adapter.py
new file mode 100644
index 00000000000..74589e00cbe
--- /dev/null
+++ b/pip/_vendor/cachecontrol/adapter.py
@@ -0,0 +1,117 @@
+import functools
+
+from pip._vendor.requests.adapters import HTTPAdapter
+
+from .controller import CacheController
+from .cache import DictCache
+from .filewrapper import CallbackFileWrapper
+
+
+class CacheControlAdapter(HTTPAdapter):
+    invalidating_methods = set(['PUT', 'DELETE'])
+
+    def __init__(self, cache=None,
+                 cache_etags=True,
+                 controller_class=None,
+                 serializer=None,
+                 heuristic=None,
+                 *args, **kw):
+        super(CacheControlAdapter, self).__init__(*args, **kw)
+        self.cache = cache or DictCache()
+        self.heuristic = heuristic
+
+        controller_factory = controller_class or CacheController
+        self.controller = controller_factory(
+            self.cache,
+            cache_etags=cache_etags,
+            serializer=serializer,
+        )
+
+    def send(self, request, **kw):
+        """
+        Send a request. Use the request information to see if it
+        exists in the cache and cache the response if we need to and can.
+        """
+        if request.method == 'GET':
+            cached_response = self.controller.cached_request(request)
+            if cached_response:
+                return self.build_response(request, cached_response,
+                                           from_cache=True)
+
+            # check for etags and add headers if appropriate
+            request.headers.update(
+                self.controller.conditional_headers(request)
+            )
+
+        resp = super(CacheControlAdapter, self).send(request, **kw)
+
+        return resp
+
+    def build_response(self, request, response, from_cache=False):
+        """
+        Build a response by making a request or using the cache.
+
+        This will end up calling send and returning a potentially
+        cached response
+        """
+        if not from_cache and request.method == 'GET':
+
+            # apply any expiration heuristics
+            if response.status == 304:
+                # We must have sent an ETag request. This could mean
+                # that we've been expired already or that we simply
+                # have an etag. In either case, we want to try and
+                # update the cache if that is the case.
+                cached_response = self.controller.update_cached_response(
+                    request, response
+                )
+
+                if cached_response is not response:
+                    from_cache = True
+
+                # We are done with the server response, read a
+                # possible response body (compliant servers will
+                # not return one, but we cannot be 100% sure) and
+                # release the connection back to the pool.
+                response.read(decode_content=False)
+                response.release_conn()
+
+                response = cached_response
+
+            # We always cache the 301 responses
+            elif response.status == 301:
+                self.controller.cache_response(request, response)
+            else:
+                # Check for any heuristics that might update headers
+                # before trying to cache.
+                if self.heuristic:
+                    response = self.heuristic.apply(response)
+
+                # Wrap the response file with a wrapper that will cache the
+                #   response when the stream has been consumed.
+                response._fp = CallbackFileWrapper(
+                    response._fp,
+                    functools.partial(
+                        self.controller.cache_response,
+                        request,
+                        response,
+                    )
+                )
+
+        resp = super(CacheControlAdapter, self).build_response(
+            request, response
+        )
+
+        # See if we should invalidate the cache.
+        if request.method in self.invalidating_methods and resp.ok:
+            cache_url = self.controller.cache_url(request.url)
+            self.cache.delete(cache_url)
+
+        # Give the request a from_cache attr to let people use it
+        resp.from_cache = from_cache
+
+        return resp
+
+    def close(self):
+        self.cache.close()
+        super(CacheControlAdapter, self).close()
diff --git a/pip/_vendor/cachecontrol/cache.py b/pip/_vendor/cachecontrol/cache.py
new file mode 100644
index 00000000000..7389a73f8c5
--- /dev/null
+++ b/pip/_vendor/cachecontrol/cache.py
@@ -0,0 +1,39 @@
+"""
+The cache object API for implementing caches. The default is a thread
+safe in-memory dictionary.
+"""
+from threading import Lock
+
+
+class BaseCache(object):
+
+    def get(self, key):
+        raise NotImplemented()
+
+    def set(self, key, value):
+        raise NotImplemented()
+
+    def delete(self, key):
+        raise NotImplemented()
+
+    def close(self):
+        pass
+
+
+class DictCache(BaseCache):
+
+    def __init__(self, init_dict=None):
+        self.lock = Lock()
+        self.data = init_dict or {}
+
+    def get(self, key):
+        return self.data.get(key, None)
+
+    def set(self, key, value):
+        with self.lock:
+            self.data.update({key: value})
+
+    def delete(self, key):
+        with self.lock:
+            if key in self.data:
+                self.data.pop(key)
diff --git a/pip/_vendor/cachecontrol/caches/__init__.py b/pip/_vendor/cachecontrol/caches/__init__.py
new file mode 100644
index 00000000000..f9e66a1ff7f
--- /dev/null
+++ b/pip/_vendor/cachecontrol/caches/__init__.py
@@ -0,0 +1,18 @@
+from textwrap import dedent
+
+try:
+    from .file_cache import FileCache
+except ImportError:
+    notice = dedent('''
+    NOTE: In order to use the FileCache you must have
+    lockfile installed. You can install it via pip:
+      pip install lockfile
+    ''')
+    print(notice)
+
+
+try:
+    import redis
+    from .redis_cache import RedisCache
+except ImportError:
+    pass
diff --git a/pip/_vendor/cachecontrol/caches/file_cache.py b/pip/_vendor/cachecontrol/caches/file_cache.py
new file mode 100644
index 00000000000..b77728f0faf
--- /dev/null
+++ b/pip/_vendor/cachecontrol/caches/file_cache.py
@@ -0,0 +1,116 @@
+import hashlib
+import os
+
+from pip._vendor.lockfile import LockFile
+from pip._vendor.lockfile.mkdirlockfile import MkdirLockFile
+
+from ..cache import BaseCache
+from ..controller import CacheController
+
+
+def _secure_open_write(filename, fmode):
+    # We only want to write to this file, so open it in write only mode
+    flags = os.O_WRONLY
+
+    # os.O_CREAT | os.O_EXCL will fail if the file already exists, so we only
+    #  will open *new* files.
+    # We specify this because we want to ensure that the mode we pass is the
+    # mode of the file.
+    flags |= os.O_CREAT | os.O_EXCL
+
+    # Do not follow symlinks to prevent someone from making a symlink that
+    # we follow and insecurely open a cache file.
+    if hasattr(os, "O_NOFOLLOW"):
+        flags |= os.O_NOFOLLOW
+
+    # On Windows we'll mark this file as binary
+    if hasattr(os, "O_BINARY"):
+        flags |= os.O_BINARY
+
+    # Before we open our file, we want to delete any existing file that is
+    # there
+    try:
+        os.remove(filename)
+    except (IOError, OSError):
+        # The file must not exist already, so we can just skip ahead to opening
+        pass
+
+    # Open our file, the use of os.O_CREAT | os.O_EXCL will ensure that if a
+    # race condition happens between the os.remove and this line, that an
+    # error will be raised. Because we utilize a lockfile this should only
+    # happen if someone is attempting to attack us.
+    fd = os.open(filename, flags, fmode)
+    try:
+        return os.fdopen(fd, "wb")
+    except:
+        # An error occurred wrapping our FD in a file object
+        os.close(fd)
+        raise
+
+
+class FileCache(BaseCache):
+    def __init__(self, directory, forever=False, filemode=0o0600,
+                 dirmode=0o0700, use_dir_lock=None, lock_class=None):
+
+        if use_dir_lock is not None and lock_class is not None:
+            raise ValueError("Cannot use use_dir_lock and lock_class together")
+
+        if use_dir_lock:
+            lock_class = MkdirLockFile
+
+        if lock_class is None:
+            lock_class = LockFile
+
+        self.directory = directory
+        self.forever = forever
+        self.filemode = filemode
+        self.dirmode = dirmode
+        self.lock_class = lock_class
+
+
+    @staticmethod
+    def encode(x):
+        return hashlib.sha224(x.encode()).hexdigest()
+
+    def _fn(self, name):
+        # NOTE: This method should not change as some may depend on it.
+        #       See: https://github.com/ionrock/cachecontrol/issues/63
+        hashed = self.encode(name)
+        parts = list(hashed[:5]) + [hashed]
+        return os.path.join(self.directory, *parts)
+
+    def get(self, key):
+        name = self._fn(key)
+        if not os.path.exists(name):
+            return None
+
+        with open(name, 'rb') as fh:
+            return fh.read()
+
+    def set(self, key, value):
+        name = self._fn(key)
+
+        # Make sure the directory exists
+        try:
+            os.makedirs(os.path.dirname(name), self.dirmode)
+        except (IOError, OSError):
+            pass
+
+        with self.lock_class(name) as lock:
+            # Write our actual file
+            with _secure_open_write(lock.path, self.filemode) as fh:
+                fh.write(value)
+
+    def delete(self, key):
+        name = self._fn(key)
+        if not self.forever:
+            os.remove(name)
+
+
+def url_to_file_path(url, filecache):
+    """Return the file cache path based on the URL.
+
+    This does not ensure the file exists!
+    """
+    key = CacheController.cache_url(url)
+    return filecache._fn(key)
diff --git a/pip/_vendor/cachecontrol/caches/redis_cache.py b/pip/_vendor/cachecontrol/caches/redis_cache.py
new file mode 100644
index 00000000000..9f5d55fd98c
--- /dev/null
+++ b/pip/_vendor/cachecontrol/caches/redis_cache.py
@@ -0,0 +1,41 @@
+from __future__ import division
+
+from datetime import datetime
+
+
+def total_seconds(td):
+    """Python 2.6 compatability"""
+    if hasattr(td, 'total_seconds'):
+        return td.total_seconds()
+
+    ms = td.microseconds
+    secs = (td.seconds + td.days * 24 * 3600)
+    return (ms + secs * 10**6) / 10**6
+
+
+class RedisCache(object):
+
+    def __init__(self, conn):
+        self.conn = conn
+
+    def get(self, key):
+        return self.conn.get(key)
+
+    def set(self, key, value, expires=None):
+        if not expires:
+            self.conn.set(key, value)
+        else:
+            expires = expires - datetime.now()
+            self.conn.setex(key, total_seconds(expires), value)
+
+    def delete(self, key):
+        self.conn.delete(key)
+
+    def clear(self):
+        """Helper for clearing all the keys in a database. Use with
+        caution!"""
+        for key in self.conn.keys():
+            self.conn.delete(key)
+
+    def close(self):
+        self.conn.disconnect()
diff --git a/pip/_vendor/cachecontrol/compat.py b/pip/_vendor/cachecontrol/compat.py
new file mode 100644
index 00000000000..018e6ac5a05
--- /dev/null
+++ b/pip/_vendor/cachecontrol/compat.py
@@ -0,0 +1,20 @@
+try:
+    from urllib.parse import urljoin
+except ImportError:
+    from urlparse import urljoin
+
+
+try:
+    import cPickle as pickle
+except ImportError:
+    import pickle
+
+
+from pip._vendor.requests.packages.urllib3.response import HTTPResponse
+from pip._vendor.requests.packages.urllib3.util import is_fp_closed
+
+# Replicate some six behaviour
+try:
+    text_type = (unicode,)
+except NameError:
+    text_type = (str,)
diff --git a/pip/_vendor/cachecontrol/controller.py b/pip/_vendor/cachecontrol/controller.py
new file mode 100644
index 00000000000..6e591f8b034
--- /dev/null
+++ b/pip/_vendor/cachecontrol/controller.py
@@ -0,0 +1,353 @@
+"""
+The httplib2 algorithms ported for use with requests.
+"""
+import logging
+import re
+import calendar
+import time
+from email.utils import parsedate_tz
+
+from pip._vendor.requests.structures import CaseInsensitiveDict
+
+from .cache import DictCache
+from .serialize import Serializer
+
+
+logger = logging.getLogger(__name__)
+
+URI = re.compile(r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?")
+
+
+def parse_uri(uri):
+    """Parses a URI using the regex given in Appendix B of RFC 3986.
+
+        (scheme, authority, path, query, fragment) = parse_uri(uri)
+    """
+    groups = URI.match(uri).groups()
+    return (groups[1], groups[3], groups[4], groups[6], groups[8])
+
+
+class CacheController(object):
+    """An interface to see if request should cached or not.
+    """
+    def __init__(self, cache=None, cache_etags=True, serializer=None):
+        self.cache = cache or DictCache()
+        self.cache_etags = cache_etags
+        self.serializer = serializer or Serializer()
+
+    @classmethod
+    def _urlnorm(cls, uri):
+        """Normalize the URL to create a safe key for the cache"""
+        (scheme, authority, path, query, fragment) = parse_uri(uri)
+        if not scheme or not authority:
+            raise Exception("Only absolute URIs are allowed. uri = %s" % uri)
+
+        scheme = scheme.lower()
+        authority = authority.lower()
+
+        if not path:
+            path = "/"
+
+        # Could do syntax based normalization of the URI before
+        # computing the digest. See Section 6.2.2 of Std 66.
+        request_uri = query and "?".join([path, query]) or path
+        defrag_uri = scheme + "://" + authority + request_uri
+
+        return defrag_uri
+
+    @classmethod
+    def cache_url(cls, uri):
+        return cls._urlnorm(uri)
+
+    def parse_cache_control(self, headers):
+        """
+        Parse the cache control headers returning a dictionary with values
+        for the different directives.
+        """
+        retval = {}
+
+        cc_header = 'cache-control'
+        if 'Cache-Control' in headers:
+            cc_header = 'Cache-Control'
+
+        if cc_header in headers:
+            parts = headers[cc_header].split(',')
+            parts_with_args = [
+                tuple([x.strip().lower() for x in part.split("=", 1)])
+                for part in parts if -1 != part.find("=")
+            ]
+            parts_wo_args = [
+                (name.strip().lower(), 1)
+                for name in parts if -1 == name.find("=")
+            ]
+            retval = dict(parts_with_args + parts_wo_args)
+        return retval
+
+    def cached_request(self, request):
+        """
+        Return a cached response if it exists in the cache, otherwise
+        return False.
+        """
+        cache_url = self.cache_url(request.url)
+        logger.debug('Looking up "%s" in the cache', cache_url)
+        cc = self.parse_cache_control(request.headers)
+
+        # Bail out if the request insists on fresh data
+        if 'no-cache' in cc:
+            logger.debug('Request header has "no-cache", cache bypassed')
+            return False
+
+        if 'max-age' in cc and cc['max-age'] == 0:
+            logger.debug('Request header has "max_age" as 0, cache bypassed')
+            return False
+
+        # Request allows serving from the cache, let's see if we find something
+        cache_data = self.cache.get(cache_url)
+        if cache_data is None:
+            logger.debug('No cache entry available')
+            return False
+
+        # Check whether it can be deserialized
+        resp = self.serializer.loads(request, cache_data)
+        if not resp:
+            logger.warning('Cache entry deserialization failed, entry ignored')
+            return False
+
+        # If we have a cached 301, return it immediately. We don't
+        # need to test our response for other headers b/c it is
+        # intrinsically "cacheable" as it is Permanent.
+        # See:
+        #   https://tools.ietf.org/html/rfc7231#section-6.4.2
+        #
+        # Client can try to refresh the value by repeating the request
+        # with cache busting headers as usual (ie no-cache).
+        if resp.status == 301:
+            msg = ('Returning cached "301 Moved Permanently" response '
+                   '(ignoring date and etag information)')
+            logger.debug(msg)
+            return resp
+
+        headers = CaseInsensitiveDict(resp.headers)
+        if not headers or 'date' not in headers:
+            if 'etag' not in headers:
+                # Without date or etag, the cached response can never be used
+                # and should be deleted.
+                logger.debug('Purging cached response: no date or etag')
+                self.cache.delete(cache_url)
+            logger.debug('Ignoring cached response: no date')
+            return False
+
+        now = time.time()
+        date = calendar.timegm(
+            parsedate_tz(headers['date'])
+        )
+        current_age = max(0, now - date)
+        logger.debug('Current age based on date: %i', current_age)
+
+        # TODO: There is an assumption that the result will be a
+        #       urllib3 response object. This may not be best since we
+        #       could probably avoid instantiating or constructing the
+        #       response until we know we need it.
+        resp_cc = self.parse_cache_control(headers)
+
+        # determine freshness
+        freshness_lifetime = 0
+
+        # Check the max-age pragma in the cache control header
+        if 'max-age' in resp_cc and resp_cc['max-age'].isdigit():
+            freshness_lifetime = int(resp_cc['max-age'])
+            logger.debug('Freshness lifetime from max-age: %i',
+                         freshness_lifetime)
+
+        # If there isn't a max-age, check for an expires header
+        elif 'expires' in headers:
+            expires = parsedate_tz(headers['expires'])
+            if expires is not None:
+                expire_time = calendar.timegm(expires) - date
+                freshness_lifetime = max(0, expire_time)
+                logger.debug("Freshness lifetime from expires: %i",
+                             freshness_lifetime)
+
+        # Determine if we are setting freshness limit in the
+        # request. Note, this overrides what was in the response.
+        if 'max-age' in cc:
+            try:
+                freshness_lifetime = int(cc['max-age'])
+                logger.debug('Freshness lifetime from request max-age: %i',
+                             freshness_lifetime)
+            except ValueError:
+                freshness_lifetime = 0
+
+        if 'min-fresh' in cc:
+            try:
+                min_fresh = int(cc['min-fresh'])
+            except ValueError:
+                min_fresh = 0
+            # adjust our current age by our min fresh
+            current_age += min_fresh
+            logger.debug('Adjusted current age from min-fresh: %i',
+                         current_age)
+
+        # Return entry if it is fresh enough
+        if freshness_lifetime > current_age:
+            logger.debug('The response is "fresh", returning cached response')
+            logger.debug('%i > %i', freshness_lifetime, current_age)
+            return resp
+
+        # we're not fresh. If we don't have an Etag, clear it out
+        if 'etag' not in headers:
+            logger.debug(
+                'The cached response is "stale" with no etag, purging'
+            )
+            self.cache.delete(cache_url)
+
+        # return the original handler
+        return False
+
+    def conditional_headers(self, request):
+        cache_url = self.cache_url(request.url)
+        resp = self.serializer.loads(request, self.cache.get(cache_url))
+        new_headers = {}
+
+        if resp:
+            headers = CaseInsensitiveDict(resp.headers)
+
+            if 'etag' in headers:
+                new_headers['If-None-Match'] = headers['ETag']
+
+            if 'last-modified' in headers:
+                new_headers['If-Modified-Since'] = headers['Last-Modified']
+
+        return new_headers
+
+    def cache_response(self, request, response, body=None):
+        """
+        Algorithm for caching requests.
+
+        This assumes a requests Response object.
+        """
+        # From httplib2: Don't cache 206's since we aren't going to
+        #                handle byte range requests
+        cacheable_status_codes = [200, 203, 300, 301]
+        if response.status not in cacheable_status_codes:
+            logger.debug(
+                'Status code %s not in %s',
+                response.status,
+                cacheable_status_codes
+            )
+            return
+
+        response_headers = CaseInsensitiveDict(response.headers)
+
+        # If we've been given a body, our response has a Content-Length, that
+        # Content-Length is valid then we can check to see if the body we've
+        # been given matches the expected size, and if it doesn't we'll just
+        # skip trying to cache it.
+        if (body is not None and
+                "content-length" in response_headers and
+                response_headers["content-length"].isdigit() and
+                int(response_headers["content-length"]) != len(body)):
+            return
+
+        cc_req = self.parse_cache_control(request.headers)
+        cc = self.parse_cache_control(response_headers)
+
+        cache_url = self.cache_url(request.url)
+        logger.debug('Updating cache with response from "%s"', cache_url)
+
+        # Delete it from the cache if we happen to have it stored there
+        no_store = False
+        if cc.get('no-store'):
+            no_store = True
+            logger.debug('Response header has "no-store"')
+        if cc_req.get('no-store'):
+            no_store = True
+            logger.debug('Request header has "no-store"')
+        if no_store and self.cache.get(cache_url):
+            logger.debug('Purging existing cache entry to honor "no-store"')
+            self.cache.delete(cache_url)
+
+        # If we've been given an etag, then keep the response
+        if self.cache_etags and 'etag' in response_headers:
+            logger.debug('Caching due to etag')
+            self.cache.set(
+                cache_url,
+                self.serializer.dumps(request, response, body=body),
+            )
+
+        # Add to the cache any 301s. We do this before looking that
+        # the Date headers.
+        elif response.status == 301:
+            logger.debug('Caching permanant redirect')
+            self.cache.set(
+                cache_url,
+                self.serializer.dumps(request, response)
+            )
+
+        # Add to the cache if the response headers demand it. If there
+        # is no date header then we can't do anything about expiring
+        # the cache.
+        elif 'date' in response_headers:
+            # cache when there is a max-age > 0
+            if cc and cc.get('max-age'):
+                if int(cc['max-age']) > 0:
+                    logger.debug('Caching b/c date exists and max-age > 0')
+                    self.cache.set(
+                        cache_url,
+                        self.serializer.dumps(request, response, body=body),
+                    )
+
+            # If the request can expire, it means we should cache it
+            # in the meantime.
+            elif 'expires' in response_headers:
+                if response_headers['expires']:
+                    logger.debug('Caching b/c of expires header')
+                    self.cache.set(
+                        cache_url,
+                        self.serializer.dumps(request, response, body=body),
+                    )
+
+    def update_cached_response(self, request, response):
+        """On a 304 we will get a new set of headers that we want to
+        update our cached value with, assuming we have one.
+
+        This should only ever be called when we've sent an ETag and
+        gotten a 304 as the response.
+        """
+        cache_url = self.cache_url(request.url)
+
+        cached_response = self.serializer.loads(
+            request,
+            self.cache.get(cache_url)
+        )
+
+        if not cached_response:
+            # we didn't have a cached response
+            return response
+
+        # Lets update our headers with the headers from the new request:
+        # http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26#section-4.1
+        #
+        # The server isn't supposed to send headers that would make
+        # the cached body invalid. But... just in case, we'll be sure
+        # to strip out ones we know that might be problmatic due to
+        # typical assumptions.
+        excluded_headers = [
+            "content-length",
+        ]
+
+        cached_response.headers.update(
+            dict((k, v) for k, v in response.headers.items()
+                 if k.lower() not in excluded_headers)
+        )
+
+        # we want a 200 b/c we have content via the cache
+        cached_response.status = 200
+
+        # update our cache
+        self.cache.set(
+            cache_url,
+            self.serializer.dumps(request, cached_response),
+        )
+
+        return cached_response
diff --git a/pip/_vendor/cachecontrol/filewrapper.py b/pip/_vendor/cachecontrol/filewrapper.py
new file mode 100644
index 00000000000..4b91bce04b0
--- /dev/null
+++ b/pip/_vendor/cachecontrol/filewrapper.py
@@ -0,0 +1,63 @@
+from io import BytesIO
+
+
+class CallbackFileWrapper(object):
+    """
+    Small wrapper around a fp object which will tee everything read into a
+    buffer, and when that file is closed it will execute a callback with the
+    contents of that buffer.
+
+    All attributes are proxied to the underlying file object.
+
+    This class uses members with a double underscore (__) leading prefix so as
+    not to accidentally shadow an attribute.
+    """
+
+    def __init__(self, fp, callback):
+        self.__buf = BytesIO()
+        self.__fp = fp
+        self.__callback = callback
+
+    def __getattr__(self, name):
+        # The vaguaries of garbage collection means that self.__fp is
+        # not always set.  By using __getattribute__ and the private
+        # name[0] allows looking up the attribute value and raising an
+        # AttributeError when it doesn't exist. This stop thigns from
+        # infinitely recursing calls to getattr in the case where
+        # self.__fp hasn't been set.
+        #
+        # [0] https://docs.python.org/2/reference/expressions.html#atom-identifiers
+        fp = self.__getattribute__('_CallbackFileWrapper__fp')
+        return getattr(fp, name)
+
+    def __is_fp_closed(self):
+        try:
+            return self.__fp.fp is None
+        except AttributeError:
+            pass
+
+        try:
+            return self.__fp.closed
+        except AttributeError:
+            pass
+
+        # We just don't cache it then.
+        # TODO: Add some logging here...
+        return False
+
+    def read(self, amt=None):
+        data = self.__fp.read(amt)
+        self.__buf.write(data)
+
+        if self.__is_fp_closed():
+            if self.__callback:
+                self.__callback(self.__buf.getvalue())
+
+            # We assign this to None here, because otherwise we can get into
+            # really tricky problems where the CPython interpreter dead locks
+            # because the callback is holding a reference to something which
+            # has a __del__ method. Setting this to None breaks the cycle
+            # and allows the garbage collector to do it's thing normally.
+            self.__callback = None
+
+        return data
diff --git a/pip/_vendor/cachecontrol/heuristics.py b/pip/_vendor/cachecontrol/heuristics.py
new file mode 100644
index 00000000000..94715a4e7e3
--- /dev/null
+++ b/pip/_vendor/cachecontrol/heuristics.py
@@ -0,0 +1,138 @@
+import calendar
+import time
+
+from email.utils import formatdate, parsedate, parsedate_tz
+
+from datetime import datetime, timedelta
+
+TIME_FMT = "%a, %d %b %Y %H:%M:%S GMT"
+
+
+def expire_after(delta, date=None):
+    date = date or datetime.now()
+    return date + delta
+
+
+def datetime_to_header(dt):
+    return formatdate(calendar.timegm(dt.timetuple()))
+
+
+class BaseHeuristic(object):
+
+    def warning(self, response):
+        """
+        Return a valid 1xx warning header value describing the cache
+        adjustments.
+
+        The response is provided too allow warnings like 113
+        http://tools.ietf.org/html/rfc7234#section-5.5.4 where we need
+        to explicitly say response is over 24 hours old.
+        """
+        return '110 - "Response is Stale"'
+
+    def update_headers(self, response):
+        """Update the response headers with any new headers.
+
+        NOTE: This SHOULD always include some Warning header to
+              signify that the response was cached by the client, not
+              by way of the provided headers.
+        """
+        return {}
+
+    def apply(self, response):
+        updated_headers = self.update_headers(response)
+
+        if updated_headers:
+            response.headers.update(updated_headers)
+            warning_header_value = self.warning(response)
+            if warning_header_value is not None:
+                response.headers.update({'Warning': warning_header_value})
+
+        return response
+
+
+class OneDayCache(BaseHeuristic):
+    """
+    Cache the response by providing an expires 1 day in the
+    future.
+    """
+    def update_headers(self, response):
+        headers = {}
+
+        if 'expires' not in response.headers:
+            date = parsedate(response.headers['date'])
+            expires = expire_after(timedelta(days=1),
+                                   date=datetime(*date[:6]))
+            headers['expires'] = datetime_to_header(expires)
+            headers['cache-control'] = 'public'
+        return headers
+
+
+class ExpiresAfter(BaseHeuristic):
+    """
+    Cache **all** requests for a defined time period.
+    """
+
+    def __init__(self, **kw):
+        self.delta = timedelta(**kw)
+
+    def update_headers(self, response):
+        expires = expire_after(self.delta)
+        return {
+            'expires': datetime_to_header(expires),
+            'cache-control': 'public',
+        }
+
+    def warning(self, response):
+        tmpl = '110 - Automatically cached for %s. Response might be stale'
+        return tmpl % self.delta
+
+
+class LastModified(BaseHeuristic):
+    """
+    If there is no Expires header already, fall back on Last-Modified
+    using the heuristic from
+    http://tools.ietf.org/html/rfc7234#section-4.2.2
+    to calculate a reasonable value.
+
+    Firefox also does something like this per
+    https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching_FAQ
+    http://lxr.mozilla.org/mozilla-release/source/netwerk/protocol/http/nsHttpResponseHead.cpp#397
+    Unlike mozilla we limit this to 24-hr.
+    """
+    cacheable_by_default_statuses = set([
+        200, 203, 204, 206, 300, 301, 404, 405, 410, 414, 501
+    ])
+
+    def update_headers(self, resp):
+        headers = resp.headers
+
+        if 'expires' in headers:
+            return {}
+
+        if 'cache-control' in headers and headers['cache-control'] != 'public':
+            return {}
+
+        if resp.status not in self.cacheable_by_default_statuses:
+            return {}
+
+        if 'date' not in headers or 'last-modified' not in headers:
+            return {}
+
+        date = calendar.timegm(parsedate_tz(headers['date']))
+        last_modified = parsedate(headers['last-modified'])
+        if date is None or last_modified is None:
+            return {}
+
+        now = time.time()
+        current_age = max(0, now - date)
+        delta = date - calendar.timegm(last_modified)
+        freshness_lifetime = max(0, min(delta / 10, 24 * 3600))
+        if freshness_lifetime <= current_age:
+            return {}
+
+        expires = date + freshness_lifetime
+        return {'expires': time.strftime(TIME_FMT, time.gmtime(expires))}
+
+    def warning(self, resp):
+        return None
diff --git a/pip/_vendor/cachecontrol/serialize.py b/pip/_vendor/cachecontrol/serialize.py
new file mode 100644
index 00000000000..ffbfbf6f84e
--- /dev/null
+++ b/pip/_vendor/cachecontrol/serialize.py
@@ -0,0 +1,190 @@
+import base64
+import io
+import json
+import zlib
+
+from pip._vendor.requests.structures import CaseInsensitiveDict
+
+from .compat import HTTPResponse, pickle, text_type
+
+
+def _b64_encode_bytes(b):
+    return base64.b64encode(b).decode("ascii")
+
+
+def _b64_encode_str(s):
+    return _b64_encode_bytes(s.encode("utf8"))
+
+
+def _b64_encode(s):
+    if isinstance(s, text_type):
+        return _b64_encode_str(s)
+    return _b64_encode_bytes(s)
+
+
+def _b64_decode_bytes(b):
+    return base64.b64decode(b.encode("ascii"))
+
+
+def _b64_decode_str(s):
+    return _b64_decode_bytes(s).decode("utf8")
+
+
+class Serializer(object):
+
+    def dumps(self, request, response, body=None):
+        response_headers = CaseInsensitiveDict(response.headers)
+
+        if body is None:
+            body = response.read(decode_content=False)
+
+            # NOTE: 99% sure this is dead code. I'm only leaving it
+            #       here b/c I don't have a test yet to prove
+            #       it. Basically, before using
+            #       `cachecontrol.filewrapper.CallbackFileWrapper`,
+            #       this made an effort to reset the file handle. The
+            #       `CallbackFileWrapper` short circuits this code by
+            #       setting the body as the content is consumed, the
+            #       result being a `body` argument is *always* passed
+            #       into cache_response, and in turn,
+            #       `Serializer.dump`.
+            response._fp = io.BytesIO(body)
+
+        data = {
+            "response": {
+                "body": _b64_encode_bytes(body),
+                "headers": dict(
+                    (_b64_encode(k), _b64_encode(v))
+                    for k, v in response.headers.items()
+                ),
+                "status": response.status,
+                "version": response.version,
+                "reason": _b64_encode_str(response.reason),
+                "strict": response.strict,
+                "decode_content": response.decode_content,
+            },
+        }
+
+        # Construct our vary headers
+        data["vary"] = {}
+        if "vary" in response_headers:
+            varied_headers = response_headers['vary'].split(',')
+            for header in varied_headers:
+                header = header.strip()
+                data["vary"][header] = request.headers.get(header, None)
+
+        # Encode our Vary headers to ensure they can be serialized as JSON
+        data["vary"] = dict(
+            (_b64_encode(k), _b64_encode(v) if v is not None else v)
+            for k, v in data["vary"].items()
+        )
+
+        return b",".join([
+            b"cc=2",
+            zlib.compress(
+                json.dumps(
+                    data, separators=(",", ":"), sort_keys=True,
+                ).encode("utf8"),
+            ),
+        ])
+
+    def loads(self, request, data):
+        # Short circuit if we've been given an empty set of data
+        if not data:
+            return
+
+        # Determine what version of the serializer the data was serialized
+        # with
+        try:
+            ver, data = data.split(b",", 1)
+        except ValueError:
+            ver = b"cc=0"
+
+        # Make sure that our "ver" is actually a version and isn't a false
+        # positive from a , being in the data stream.
+        if ver[:3] != b"cc=":
+            data = ver + data
+            ver = b"cc=0"
+
+        # Get the version number out of the cc=N
+        ver = ver.split(b"=", 1)[-1].decode("ascii")
+
+        # Dispatch to the actual load method for the given version
+        try:
+            return getattr(self, "_loads_v{0}".format(ver))(request, data)
+        except AttributeError:
+            # This is a version we don't have a loads function for, so we'll
+            # just treat it as a miss and return None
+            return
+
+    def prepare_response(self, request, cached):
+        """Verify our vary headers match and construct a real urllib3
+        HTTPResponse object.
+        """
+        # Special case the '*' Vary value as it means we cannot actually
+        # determine if the cached response is suitable for this request.
+        if "*" in cached.get("vary", {}):
+            return
+
+        # Ensure that the Vary headers for the cached response match our
+        # request
+        for header, value in cached.get("vary", {}).items():
+            if request.headers.get(header, None) != value:
+                return
+
+        body_raw = cached["response"].pop("body")
+
+        try:
+            body = io.BytesIO(body_raw)
+        except TypeError:
+            # This can happen if cachecontrol serialized to v1 format (pickle)
+            # using Python 2. A Python 2 str(byte string) will be unpickled as
+            # a Python 3 str (unicode string), which will cause the above to
+            # fail with:
+            #
+            #     TypeError: 'str' does not support the buffer interface
+            body = io.BytesIO(body_raw.encode('utf8'))
+
+        return HTTPResponse(
+            body=body,
+            preload_content=False,
+            **cached["response"]
+        )
+
+    def _loads_v0(self, request, data):
+        # The original legacy cache data. This doesn't contain enough
+        # information to construct everything we need, so we'll treat this as
+        # a miss.
+        return
+
+    def _loads_v1(self, request, data):
+        try:
+            cached = pickle.loads(data)
+        except ValueError:
+            return
+
+        return self.prepare_response(request, cached)
+
+    def _loads_v2(self, request, data):
+        try:
+            cached = json.loads(zlib.decompress(data).decode("utf8"))
+        except ValueError:
+            return
+
+        # We need to decode the items that we've base64 encoded
+        cached["response"]["body"] = _b64_decode_bytes(
+            cached["response"]["body"]
+        )
+        cached["response"]["headers"] = dict(
+            (_b64_decode_str(k), _b64_decode_str(v))
+            for k, v in cached["response"]["headers"].items()
+        )
+        cached["response"]["reason"] = _b64_decode_str(
+            cached["response"]["reason"],
+        )
+        cached["vary"] = dict(
+            (_b64_decode_str(k), _b64_decode_str(v) if v is not None else v)
+            for k, v in cached["vary"].items()
+        )
+
+        return self.prepare_response(request, cached)
diff --git a/pip/_vendor/cachecontrol/wrapper.py b/pip/_vendor/cachecontrol/wrapper.py
new file mode 100644
index 00000000000..ea421aa7e71
--- /dev/null
+++ b/pip/_vendor/cachecontrol/wrapper.py
@@ -0,0 +1,21 @@
+from .adapter import CacheControlAdapter
+from .cache import DictCache
+
+
+def CacheControl(sess,
+                 cache=None,
+                 cache_etags=True,
+                 serializer=None,
+                 heuristic=None):
+
+    cache = cache or DictCache()
+    adapter = CacheControlAdapter(
+        cache,
+        cache_etags=cache_etags,
+        serializer=serializer,
+        heuristic=heuristic,
+    )
+    sess.mount('http://', adapter)
+    sess.mount('https://', adapter)
+
+    return sess
diff --git a/pip/_vendor/colorama/__init__.py b/pip/_vendor/colorama/__init__.py
index 0856986fab5..670e6b3970b 100644
--- a/pip/_vendor/colorama/__init__.py
+++ b/pip/_vendor/colorama/__init__.py
@@ -1,7 +1,7 @@
 # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
-from .initialise import init, deinit, reinit
-from .ansi import Fore, Back, Style
+from .initialise import init, deinit, reinit, colorama_text
+from .ansi import Fore, Back, Style, Cursor
 from .ansitowin32 import AnsiToWin32
 
-__version__ = '0.3.1'
+__version__ = '0.3.7'
 
diff --git a/pip/_vendor/colorama/ansi.py b/pip/_vendor/colorama/ansi.py
index 5dfe374ceb5..78776588db9 100644
--- a/pip/_vendor/colorama/ansi.py
+++ b/pip/_vendor/colorama/ansi.py
@@ -5,46 +5,98 @@
 '''
 
 CSI = '\033['
+OSC = '\033]'
+BEL = '\007'
+
 
 def code_to_chars(code):
     return CSI + str(code) + 'm'
 
+def set_title(title):
+    return OSC + '2;' + title + BEL
+
+def clear_screen(mode=2):
+    return CSI + str(mode) + 'J'
+
+def clear_line(mode=2):
+    return CSI + str(mode) + 'K'
+
+
 class AnsiCodes(object):
-    def __init__(self, codes):
-        for name in dir(codes):
+    def __init__(self):
+        # the subclasses declare class attributes which are numbers.
+        # Upon instantiation we define instance attributes, which are the same
+        # as the class attributes but wrapped with the ANSI escape sequence
+        for name in dir(self):
             if not name.startswith('_'):
-                value = getattr(codes, name)
+                value = getattr(self, name)
                 setattr(self, name, code_to_chars(value))
 
-class AnsiFore:
-    BLACK   = 30
-    RED     = 31
-    GREEN   = 32
-    YELLOW  = 33
-    BLUE    = 34
-    MAGENTA = 35
-    CYAN    = 36
-    WHITE   = 37
-    RESET   = 39
-
-class AnsiBack:
-    BLACK   = 40
-    RED     = 41
-    GREEN   = 42
-    YELLOW  = 43
-    BLUE    = 44
-    MAGENTA = 45
-    CYAN    = 46
-    WHITE   = 47
-    RESET   = 49
-
-class AnsiStyle:
+
+class AnsiCursor(object):
+    def UP(self, n=1):
+        return CSI + str(n) + 'A'
+    def DOWN(self, n=1):
+        return CSI + str(n) + 'B'
+    def FORWARD(self, n=1):
+        return CSI + str(n) + 'C'
+    def BACK(self, n=1):
+        return CSI + str(n) + 'D'
+    def POS(self, x=1, y=1):
+        return CSI + str(y) + ';' + str(x) + 'H'
+
+
+class AnsiFore(AnsiCodes):
+    BLACK           = 30
+    RED             = 31
+    GREEN           = 32
+    YELLOW          = 33
+    BLUE            = 34
+    MAGENTA         = 35
+    CYAN            = 36
+    WHITE           = 37
+    RESET           = 39
+
+    # These are fairly well supported, but not part of the standard.
+    LIGHTBLACK_EX   = 90
+    LIGHTRED_EX     = 91
+    LIGHTGREEN_EX   = 92
+    LIGHTYELLOW_EX  = 93
+    LIGHTBLUE_EX    = 94
+    LIGHTMAGENTA_EX = 95
+    LIGHTCYAN_EX    = 96
+    LIGHTWHITE_EX   = 97
+
+
+class AnsiBack(AnsiCodes):
+    BLACK           = 40
+    RED             = 41
+    GREEN           = 42
+    YELLOW          = 43
+    BLUE            = 44
+    MAGENTA         = 45
+    CYAN            = 46
+    WHITE           = 47
+    RESET           = 49
+
+    # These are fairly well supported, but not part of the standard.
+    LIGHTBLACK_EX   = 100
+    LIGHTRED_EX     = 101
+    LIGHTGREEN_EX   = 102
+    LIGHTYELLOW_EX  = 103
+    LIGHTBLUE_EX    = 104
+    LIGHTMAGENTA_EX = 105
+    LIGHTCYAN_EX    = 106
+    LIGHTWHITE_EX   = 107
+
+
+class AnsiStyle(AnsiCodes):
     BRIGHT    = 1
     DIM       = 2
     NORMAL    = 22
     RESET_ALL = 0
 
-Fore = AnsiCodes( AnsiFore )
-Back = AnsiCodes( AnsiBack )
-Style = AnsiCodes( AnsiStyle )
-
+Fore   = AnsiFore()
+Back   = AnsiBack()
+Style  = AnsiStyle()
+Cursor = AnsiCursor()
diff --git a/pip/_vendor/colorama/ansitowin32.py b/pip/_vendor/colorama/ansitowin32.py
index 0053a775822..b7ff6f2136e 100644
--- a/pip/_vendor/colorama/ansitowin32.py
+++ b/pip/_vendor/colorama/ansitowin32.py
@@ -1,10 +1,11 @@
 # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
 import re
 import sys
+import os
 
 from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style
 from .winterm import WinTerm, WinColor, WinStyle
-from .win32 import windll
+from .win32 import windll, winapi_test
 
 
 winterm = None
@@ -12,6 +13,10 @@
     winterm = WinTerm()
 
 
+def is_stream_closed(stream):
+    return not hasattr(stream, 'closed') or stream.closed
+
+
 def is_a_tty(stream):
     return hasattr(stream, 'isatty') and stream.isatty()
 
@@ -41,7 +46,8 @@ class AnsiToWin32(object):
     sequences from the text, and if outputting to a tty, will convert them into
     win32 function calls.
     '''
-    ANSI_RE = re.compile('\033\[((?:\d|;)*)([a-zA-Z])')
+    ANSI_CSI_RE = re.compile('\001?\033\[((?:\d|;)*)([a-zA-Z])\002?')     # Control Sequence Introducer
+    ANSI_OSC_RE = re.compile('\001?\033\]((?:.|;)*?)(\x07)\002?')         # Operating System Command
 
     def __init__(self, wrapped, convert=None, strip=None, autoreset=False):
         # The wrapped stream (normally sys.stdout or sys.stderr)
@@ -53,16 +59,21 @@ def __init__(self, wrapped, convert=None, strip=None, autoreset=False):
         # create the proxy wrapping our output stream
         self.stream = StreamWrapper(wrapped, self)
 
-        on_windows = sys.platform.startswith('win')
+        on_windows = os.name == 'nt'
+        # We test if the WinAPI works, because even if we are on Windows
+        # we may be using a terminal that doesn't support the WinAPI
+        # (e.g. Cygwin Terminal). In this case it's up to the terminal
+        # to support the ANSI codes.
+        conversion_supported = on_windows and winapi_test()
 
         # should we strip ANSI sequences from our output?
         if strip is None:
-            strip = on_windows
+            strip = conversion_supported or (not is_stream_closed(wrapped) and not is_a_tty(wrapped))
         self.strip = strip
 
         # should we should convert ANSI sequences into win32 calls?
         if convert is None:
-            convert = on_windows and is_a_tty(wrapped)
+            convert = conversion_supported and not is_stream_closed(wrapped) and is_a_tty(wrapped)
         self.convert = convert
 
         # dict of ansi codes to win32 functions and parameters
@@ -71,7 +82,6 @@ def __init__(self, wrapped, convert=None, strip=None, autoreset=False):
         # are we wrapping stderr?
         self.on_stderr = self.wrapped is sys.stderr
 
-
     def should_wrap(self):
         '''
         True if this class is actually needed. If false, then the output
@@ -82,7 +92,6 @@ def should_wrap(self):
         '''
         return self.convert or self.strip or self.autoreset
 
-
     def get_win32_calls(self):
         if self.convert and winterm:
             return {
@@ -99,6 +108,14 @@ def get_win32_calls(self):
                 AnsiFore.CYAN: (winterm.fore, WinColor.CYAN),
                 AnsiFore.WHITE: (winterm.fore, WinColor.GREY),
                 AnsiFore.RESET: (winterm.fore, ),
+                AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True),
+                AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True),
+                AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True),
+                AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True),
+                AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True),
+                AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True),
+                AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True),
+                AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True),
                 AnsiBack.BLACK: (winterm.back, WinColor.BLACK),
                 AnsiBack.RED: (winterm.back, WinColor.RED),
                 AnsiBack.GREEN: (winterm.back, WinColor.GREEN),
@@ -108,8 +125,16 @@ def get_win32_calls(self):
                 AnsiBack.CYAN: (winterm.back, WinColor.CYAN),
                 AnsiBack.WHITE: (winterm.back, WinColor.GREY),
                 AnsiBack.RESET: (winterm.back, ),
+                AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True),
+                AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True),
+                AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True),
+                AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True),
+                AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True),
+                AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True),
+                AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True),
+                AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True),
             }
-
+        return dict()
 
     def write(self, text):
         if self.strip or self.convert:
@@ -124,7 +149,7 @@ def write(self, text):
     def reset_all(self):
         if self.convert:
             self.call_win32('m', (0,))
-        elif not self.wrapped.closed and is_a_tty(self.wrapped):
+        elif not self.strip and not is_stream_closed(self.wrapped):
             self.wrapped.write(Style.RESET_ALL)
 
 
@@ -135,7 +160,8 @@ def write_and_convert(self, text):
         calls.
         '''
         cursor = 0
-        for match in self.ANSI_RE.finditer(text):
+        text = self.convert_osc(text)
+        for match in self.ANSI_CSI_RE.finditer(text):
             start, end = match.span()
             self.write_plain_text(text, cursor, start)
             self.convert_ansi(*match.groups())
@@ -151,21 +177,29 @@ def write_plain_text(self, text, start, end):
 
     def convert_ansi(self, paramstring, command):
         if self.convert:
-            params = self.extract_params(paramstring)
+            params = self.extract_params(command, paramstring)
             self.call_win32(command, params)
 
 
-    def extract_params(self, paramstring):
-        def split(paramstring):
-            for p in paramstring.split(';'):
-                if p != '':
-                    yield int(p)
-        return tuple(split(paramstring))
+    def extract_params(self, command, paramstring):
+        if command in 'Hf':
+            params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';'))
+            while len(params) < 2:
+                # defaults:
+                params = params + (1,)
+        else:
+            params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0)
+            if len(params) == 0:
+                # defaults:
+                if command in 'JKm':
+                    params = (0,)
+                elif command in 'ABCD':
+                    params = (1,)
+
+        return params
 
 
     def call_win32(self, command, params):
-        if params == []:
-            params = [0]
         if command == 'm':
             for param in params:
                 if param in self.win32_calls:
@@ -174,17 +208,29 @@ def call_win32(self, command, params):
                     args = func_args[1:]
                     kwargs = dict(on_stderr=self.on_stderr)
                     func(*args, **kwargs)
-        elif command in ('H', 'f'): # set cursor position
-            func = winterm.set_cursor_position
-            func(params, on_stderr=self.on_stderr)
-        elif command in ('J'):
-            func = winterm.erase_data
-            func(params, on_stderr=self.on_stderr)
-        elif command == 'A':
-            if params == () or params == None:
-                num_rows = 1
-            else:
-                num_rows = params[0]
-            func = winterm.cursor_up
-            func(num_rows, on_stderr=self.on_stderr)
-
+        elif command in 'J':
+            winterm.erase_screen(params[0], on_stderr=self.on_stderr)
+        elif command in 'K':
+            winterm.erase_line(params[0], on_stderr=self.on_stderr)
+        elif command in 'Hf':     # cursor position - absolute
+            winterm.set_cursor_position(params, on_stderr=self.on_stderr)
+        elif command in 'ABCD':   # cursor position - relative
+            n = params[0]
+            # A - up, B - down, C - forward, D - back
+            x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command]
+            winterm.cursor_adjust(x, y, on_stderr=self.on_stderr)
+
+
+    def convert_osc(self, text):
+        for match in self.ANSI_OSC_RE.finditer(text):
+            start, end = match.span()
+            text = text[:start] + text[end:]
+            paramstring, command = match.groups()
+            if command in '\x07':       # \x07 = BEL
+                params = paramstring.split(";")
+                # 0 - change title and icon (we will only change title)
+                # 1 - change icon (we don't support this)
+                # 2 - change title
+                if params[0] in '02':
+                    winterm.set_title(params[1])
+        return text
diff --git a/pip/_vendor/colorama/initialise.py b/pip/_vendor/colorama/initialise.py
index cba3676dd9c..834962a35f7 100644
--- a/pip/_vendor/colorama/initialise.py
+++ b/pip/_vendor/colorama/initialise.py
@@ -1,21 +1,23 @@
 # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
 import atexit
+import contextlib
 import sys
 
 from .ansitowin32 import AnsiToWin32
 
 
-orig_stdout = sys.stdout
-orig_stderr = sys.stderr
+orig_stdout = None
+orig_stderr = None
 
-wrapped_stdout = sys.stdout
-wrapped_stderr = sys.stderr
+wrapped_stdout = None
+wrapped_stderr = None
 
 atexit_done = False
 
 
 def reset_all():
-    AnsiToWin32(orig_stdout).reset_all()
+    if AnsiToWin32 is not None:    # Issue #74: objects might become None at exit
+        AnsiToWin32(orig_stdout).reset_all()
 
 
 def init(autoreset=False, convert=None, strip=None, wrap=True):
@@ -24,10 +26,21 @@ def init(autoreset=False, convert=None, strip=None, wrap=True):
         raise ValueError('wrap=False conflicts with any other arg=True')
 
     global wrapped_stdout, wrapped_stderr
-    sys.stdout = wrapped_stdout = \
-        wrap_stream(orig_stdout, convert, strip, autoreset, wrap)
-    sys.stderr = wrapped_stderr = \
-        wrap_stream(orig_stderr, convert, strip, autoreset, wrap)
+    global orig_stdout, orig_stderr
+
+    orig_stdout = sys.stdout
+    orig_stderr = sys.stderr
+
+    if sys.stdout is None:
+        wrapped_stdout = None
+    else:
+        sys.stdout = wrapped_stdout = \
+            wrap_stream(orig_stdout, convert, strip, autoreset, wrap)
+    if sys.stderr is None:
+        wrapped_stderr = None
+    else:
+        sys.stderr = wrapped_stderr = \
+            wrap_stream(orig_stderr, convert, strip, autoreset, wrap)
 
     global atexit_done
     if not atexit_done:
@@ -36,13 +49,26 @@ def init(autoreset=False, convert=None, strip=None, wrap=True):
 
 
 def deinit():
-    sys.stdout = orig_stdout
-    sys.stderr = orig_stderr
+    if orig_stdout is not None:
+        sys.stdout = orig_stdout
+    if orig_stderr is not None:
+        sys.stderr = orig_stderr
+
+
+@contextlib.contextmanager
+def colorama_text(*args, **kwargs):
+    init(*args, **kwargs)
+    try:
+        yield
+    finally:
+        deinit()
 
 
 def reinit():
-    sys.stdout = wrapped_stdout
-    sys.stderr = wrapped_stdout
+    if wrapped_stdout is not None:
+        sys.stdout = wrapped_stdout
+    if wrapped_stderr is not None:
+        sys.stderr = wrapped_stderr
 
 
 def wrap_stream(stream, convert, strip, autoreset, wrap):
diff --git a/pip/_vendor/colorama/win32.py b/pip/_vendor/colorama/win32.py
index 5203e791d27..3d1d2f2d918 100644
--- a/pip/_vendor/colorama/win32.py
+++ b/pip/_vendor/colorama/win32.py
@@ -4,28 +4,28 @@
 STDOUT = -11
 STDERR = -12
 
-import ctypes
-from ctypes import LibraryLoader
-
 try:
+    import ctypes
+    from ctypes import LibraryLoader
     windll = LibraryLoader(ctypes.WinDLL)
     from ctypes import wintypes
 except (AttributeError, ImportError):
     windll = None
     SetConsoleTextAttribute = lambda *_: None
+    winapi_test = lambda *_: None
 else:
-    from ctypes import (
-        byref, Structure, c_char, c_short, c_uint32, c_ushort, POINTER
-    )
+    from ctypes import byref, Structure, c_char, POINTER
+
+    COORD = wintypes._COORD
 
     class CONSOLE_SCREEN_BUFFER_INFO(Structure):
         """struct in wincon.h."""
         _fields_ = [
-            ("dwSize", wintypes._COORD),
-            ("dwCursorPosition", wintypes._COORD),
+            ("dwSize", COORD),
+            ("dwCursorPosition", COORD),
             ("wAttributes", wintypes.WORD),
             ("srWindow", wintypes.SMALL_RECT),
-            ("dwMaximumWindowSize", wintypes._COORD),
+            ("dwMaximumWindowSize", COORD),
         ]
         def __str__(self):
             return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % (
@@ -59,7 +59,7 @@ def __str__(self):
     _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition
     _SetConsoleCursorPosition.argtypes = [
         wintypes.HANDLE,
-        wintypes._COORD,
+        COORD,
     ]
     _SetConsoleCursorPosition.restype = wintypes.BOOL
 
@@ -68,7 +68,7 @@ def __str__(self):
         wintypes.HANDLE,
         c_char,
         wintypes.DWORD,
-        wintypes._COORD,
+        COORD,
         POINTER(wintypes.DWORD),
     ]
     _FillConsoleOutputCharacterA.restype = wintypes.BOOL
@@ -78,16 +78,29 @@ def __str__(self):
         wintypes.HANDLE,
         wintypes.WORD,
         wintypes.DWORD,
-        wintypes._COORD,
+        COORD,
         POINTER(wintypes.DWORD),
     ]
     _FillConsoleOutputAttribute.restype = wintypes.BOOL
 
+    _SetConsoleTitleW = windll.kernel32.SetConsoleTitleA
+    _SetConsoleTitleW.argtypes = [
+        wintypes.LPCSTR
+    ]
+    _SetConsoleTitleW.restype = wintypes.BOOL
+
     handles = {
         STDOUT: _GetStdHandle(STDOUT),
         STDERR: _GetStdHandle(STDERR),
     }
 
+    def winapi_test():
+        handle = handles[STDOUT]
+        csbi = CONSOLE_SCREEN_BUFFER_INFO()
+        success = _GetConsoleScreenBufferInfo(
+            handle, byref(csbi))
+        return bool(success)
+
     def GetConsoleScreenBufferInfo(stream_id=STDOUT):
         handle = handles[stream_id]
         csbi = CONSOLE_SCREEN_BUFFER_INFO()
@@ -99,26 +112,27 @@ def SetConsoleTextAttribute(stream_id, attrs):
         handle = handles[stream_id]
         return _SetConsoleTextAttribute(handle, attrs)
 
-    def SetConsoleCursorPosition(stream_id, position):
-        position = wintypes._COORD(*position)
+    def SetConsoleCursorPosition(stream_id, position, adjust=True):
+        position = COORD(*position)
         # If the position is out of range, do nothing.
         if position.Y <= 0 or position.X <= 0:
             return
         # Adjust for Windows' SetConsoleCursorPosition:
         #    1. being 0-based, while ANSI is 1-based.
         #    2. expecting (x,y), while ANSI uses (y,x).
-        adjusted_position = wintypes._COORD(position.Y - 1, position.X - 1)
-        # Adjust for viewport's scroll position
-        sr = GetConsoleScreenBufferInfo(STDOUT).srWindow
-        adjusted_position.Y += sr.Top
-        adjusted_position.X += sr.Left
+        adjusted_position = COORD(position.Y - 1, position.X - 1)
+        if adjust:
+            # Adjust for viewport's scroll position
+            sr = GetConsoleScreenBufferInfo(STDOUT).srWindow
+            adjusted_position.Y += sr.Top
+            adjusted_position.X += sr.Left
         # Resume normal processing
         handle = handles[stream_id]
         return _SetConsoleCursorPosition(handle, adjusted_position)
 
     def FillConsoleOutputCharacter(stream_id, char, length, start):
         handle = handles[stream_id]
-        char = c_char(char)
+        char = c_char(char.encode())
         length = wintypes.DWORD(length)
         num_written = wintypes.DWORD(0)
         # Note that this is hard-coded for ANSI (vs wide) bytes.
@@ -135,3 +149,6 @@ def FillConsoleOutputAttribute(stream_id, attr, length, start):
         # Note that this is hard-coded for ANSI (vs wide) bytes.
         return _FillConsoleOutputAttribute(
             handle, attribute, length, start, byref(num_written))
+
+    def SetConsoleTitle(title):
+        return _SetConsoleTitleW(title)
diff --git a/pip/_vendor/colorama/winterm.py b/pip/_vendor/colorama/winterm.py
index 2708811541f..60309d3c07a 100644
--- a/pip/_vendor/colorama/winterm.py
+++ b/pip/_vendor/colorama/winterm.py
@@ -15,9 +15,9 @@ class WinColor(object):
 
 # from wincon.h
 class WinStyle(object):
-    NORMAL = 0x00 # dim text, dim background
-    BRIGHT = 0x08 # bright text, dim background
-
+    NORMAL              = 0x00 # dim text, dim background
+    BRIGHT              = 0x08 # bright text, dim background
+    BRIGHT_BACKGROUND   = 0x80 # dim text, bright background
 
 class WinTerm(object):
 
@@ -27,29 +27,44 @@ def __init__(self):
         self._default_fore = self._fore
         self._default_back = self._back
         self._default_style = self._style
+        # In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style.
+        # So that LIGHT_EX colors and BRIGHT style do not clobber each other,
+        # we track them separately, since LIGHT_EX is overwritten by Fore/Back
+        # and BRIGHT is overwritten by Style codes.
+        self._light = 0
 
     def get_attrs(self):
-        return self._fore + self._back * 16 + self._style
+        return self._fore + self._back * 16 + (self._style | self._light)
 
     def set_attrs(self, value):
         self._fore = value & 7
         self._back = (value >> 4) & 7
-        self._style = value & WinStyle.BRIGHT
+        self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND)
 
     def reset_all(self, on_stderr=None):
         self.set_attrs(self._default)
         self.set_console(attrs=self._default)
 
-    def fore(self, fore=None, on_stderr=False):
+    def fore(self, fore=None, light=False, on_stderr=False):
         if fore is None:
             fore = self._default_fore
         self._fore = fore
+        # Emulate LIGHT_EX with BRIGHT Style
+        if light:
+            self._light |= WinStyle.BRIGHT
+        else:
+            self._light &= ~WinStyle.BRIGHT
         self.set_console(on_stderr=on_stderr)
 
-    def back(self, back=None, on_stderr=False):
+    def back(self, back=None, light=False, on_stderr=False):
         if back is None:
             back = self._default_back
         self._back = back
+        # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style
+        if light:
+            self._light |= WinStyle.BRIGHT_BACKGROUND
+        else:
+            self._light &= ~WinStyle.BRIGHT_BACKGROUND
         self.set_console(on_stderr=on_stderr)
 
     def style(self, style=None, on_stderr=False):
@@ -73,48 +88,75 @@ def get_position(self, handle):
         position.X += 1
         position.Y += 1
         return position
-    
+
     def set_cursor_position(self, position=None, on_stderr=False):
         if position is None:
-            #I'm not currently tracking the position, so there is no default.
-            #position = self.get_position()
+            # I'm not currently tracking the position, so there is no default.
+            # position = self.get_position()
             return
         handle = win32.STDOUT
         if on_stderr:
             handle = win32.STDERR
         win32.SetConsoleCursorPosition(handle, position)
 
-    def cursor_up(self, num_rows=0, on_stderr=False):
-        if num_rows == 0:
-            return
+    def cursor_adjust(self, x, y, on_stderr=False):
         handle = win32.STDOUT
         if on_stderr:
             handle = win32.STDERR
         position = self.get_position(handle)
-        adjusted_position = (position.Y - num_rows, position.X)
-        self.set_cursor_position(adjusted_position, on_stderr)
+        adjusted_position = (position.Y + y, position.X + x)
+        win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False)
 
-    def erase_data(self, mode=0, on_stderr=False):
-        # 0 (or None) should clear from the cursor to the end of the screen.
+    def erase_screen(self, mode=0, on_stderr=False):
+        # 0 should clear from the cursor to the end of the screen.
         # 1 should clear from the cursor to the beginning of the screen.
-        # 2 should clear the entire screen. (And maybe move cursor to (1,1)?)
-        #
-        # At the moment, I only support mode 2. From looking at the API, it
-        #    should be possible to calculate a different number of bytes to clear,
-        #    and to do so relative to the cursor position.
-        if mode[0] not in (2,):
-            return
+        # 2 should clear the entire screen, and move cursor to (1,1)
         handle = win32.STDOUT
         if on_stderr:
             handle = win32.STDERR
-        # here's where we'll home the cursor
-        coord_screen = win32.COORD(0,0)
         csbi = win32.GetConsoleScreenBufferInfo(handle)
         # get the number of character cells in the current buffer
-        dw_con_size = csbi.dwSize.X * csbi.dwSize.Y
+        cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y
+        # get number of character cells before current cursor position
+        cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X
+        if mode == 0:
+            from_coord = csbi.dwCursorPosition
+            cells_to_erase = cells_in_screen - cells_before_cursor
+        if mode == 1:
+            from_coord = win32.COORD(0, 0)
+            cells_to_erase = cells_before_cursor
+        elif mode == 2:
+            from_coord = win32.COORD(0, 0)
+            cells_to_erase = cells_in_screen
         # fill the entire screen with blanks
-        win32.FillConsoleOutputCharacter(handle, ' ', dw_con_size, coord_screen)
+        win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
         # now set the buffer's attributes accordingly
-        win32.FillConsoleOutputAttribute(handle, self.get_attrs(), dw_con_size, coord_screen );
-        # put the cursor at (0, 0)
-        win32.SetConsoleCursorPosition(handle, (coord_screen.X, coord_screen.Y))
+        win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord)
+        if mode == 2:
+            # put the cursor where needed
+            win32.SetConsoleCursorPosition(handle, (1, 1))
+
+    def erase_line(self, mode=0, on_stderr=False):
+        # 0 should clear from the cursor to the end of the line.
+        # 1 should clear from the cursor to the beginning of the line.
+        # 2 should clear the entire line.
+        handle = win32.STDOUT
+        if on_stderr:
+            handle = win32.STDERR
+        csbi = win32.GetConsoleScreenBufferInfo(handle)
+        if mode == 0:
+            from_coord = csbi.dwCursorPosition
+            cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X
+        if mode == 1:
+            from_coord = win32.COORD(0, csbi.dwCursorPosition.Y)
+            cells_to_erase = csbi.dwCursorPosition.X
+        elif mode == 2:
+            from_coord = win32.COORD(0, csbi.dwCursorPosition.Y)
+            cells_to_erase = csbi.dwSize.X
+        # fill the entire screen with blanks
+        win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
+        # now set the buffer's attributes accordingly
+        win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord)
+
+    def set_title(self, title):
+        win32.SetConsoleTitle(title)
diff --git a/pip/_vendor/distlib/__init__.py b/pip/_vendor/distlib/__init__.py
index f9081bb84f4..7026860f13a 100644
--- a/pip/_vendor/distlib/__init__.py
+++ b/pip/_vendor/distlib/__init__.py
@@ -1,12 +1,12 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2012-2014 Vinay Sajip.
+# Copyright (C) 2012-2016 Vinay Sajip.
 # Licensed to the Python Software Foundation under a contributor agreement.
 # See LICENSE.txt and CONTRIBUTORS.txt.
 #
 import logging
 
-__version__ = '0.1.8'
+__version__ = '0.2.3'
 
 class DistlibException(Exception):
     pass
diff --git a/pip/_vendor/distlib/compat.py b/pip/_vendor/distlib/compat.py
index 63af3742de6..069ec7796db 100644
--- a/pip/_vendor/distlib/compat.py
+++ b/pip/_vendor/distlib/compat.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2013 Vinay Sajip.
+# Copyright (C) 2013-2016 Vinay Sajip.
 # Licensed to the Python Software Foundation under a contributor agreement.
 # See LICENSE.txt and CONTRIBUTORS.txt.
 #
@@ -10,7 +10,7 @@
 import re
 import sys
 
-if sys.version_info[0] < 3:
+if sys.version_info[0] < 3:  # pragma: no cover
     from StringIO import StringIO
     string_types = basestring,
     text_type = unicode
@@ -53,7 +53,7 @@ def splituser(host):
         if match: return match.group(1, 2)
         return None, host
 
-else:
+else:  # pragma: no cover
     from io import StringIO
     string_types = str,
     text_type = str
@@ -81,40 +81,78 @@ def splituser(host):
 
 try:
     from ssl import match_hostname, CertificateError
-except ImportError:
+except ImportError: # pragma: no cover
     class CertificateError(ValueError):
         pass
 
 
-    def _dnsname_to_pat(dn):
+    def _dnsname_match(dn, hostname, max_wildcards=1):
+        """Matching according to RFC 6125, section 6.4.3
+
+        http://tools.ietf.org/html/rfc6125#section-6.4.3
+        """
         pats = []
-        for frag in dn.split(r'.'):
-            if frag == '*':
-                # When '*' is a fragment by itself, it matches a non-empty
-                # dotless fragment.
-                pats.append('[^.]+')
-            else:
-                # Otherwise, '*' matches any dotless fragment.
-                frag = re.escape(frag)
-                pats.append(frag.replace(r'\*', '[^.]*'))
-        return re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
+        if not dn:
+            return False
+
+        parts = dn.split('.')
+        leftmost, remainder = parts[0], parts[1:]
+
+        wildcards = leftmost.count('*')
+        if wildcards > max_wildcards:
+            # Issue #17980: avoid denials of service by refusing more
+            # than one wildcard per fragment.  A survery of established
+            # policy among SSL implementations showed it to be a
+            # reasonable choice.
+            raise CertificateError(
+                "too many wildcards in certificate DNS name: " + repr(dn))
+
+        # speed up common case w/o wildcards
+        if not wildcards:
+            return dn.lower() == hostname.lower()
+
+        # RFC 6125, section 6.4.3, subitem 1.
+        # The client SHOULD NOT attempt to match a presented identifier in which
+        # the wildcard character comprises a label other than the left-most label.
+        if leftmost == '*':
+            # When '*' is a fragment by itself, it matches a non-empty dotless
+            # fragment.
+            pats.append('[^.]+')
+        elif leftmost.startswith('xn--') or hostname.startswith('xn--'):
+            # RFC 6125, section 6.4.3, subitem 3.
+            # The client SHOULD NOT attempt to match a presented identifier
+            # where the wildcard character is embedded within an A-label or
+            # U-label of an internationalized domain name.
+            pats.append(re.escape(leftmost))
+        else:
+            # Otherwise, '*' matches any dotless string, e.g. www*
+            pats.append(re.escape(leftmost).replace(r'\*', '[^.]*'))
+
+        # add the remaining fragments, ignore any wildcards
+        for frag in remainder:
+            pats.append(re.escape(frag))
+
+        pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
+        return pat.match(hostname)
 
 
     def match_hostname(cert, hostname):
         """Verify that *cert* (in decoded format as returned by
-        SSLSocket.getpeercert()) matches the *hostname*.  RFC 2818 rules
-        are mostly followed, but IP addresses are not accepted for *hostname*.
+        SSLSocket.getpeercert()) matches the *hostname*.  RFC 2818 and RFC 6125
+        rules are followed, but IP addresses are not accepted for *hostname*.
 
         CertificateError is raised on failure. On success, the function
         returns nothing.
         """
         if not cert:
-            raise ValueError("empty or no certificate")
+            raise ValueError("empty or no certificate, match_hostname needs a "
+                             "SSL socket or SSL context with either "
+                             "CERT_OPTIONAL or CERT_REQUIRED")
         dnsnames = []
         san = cert.get('subjectAltName', ())
         for key, value in san:
             if key == 'DNS':
-                if _dnsname_to_pat(value).match(hostname):
+                if _dnsname_match(value, hostname):
                     return
                 dnsnames.append(value)
         if not dnsnames:
@@ -125,7 +163,7 @@ def match_hostname(cert, hostname):
                     # XXX according to RFC 2818, the most specific Common Name
                     # must be used.
                     if key == 'commonName':
-                        if _dnsname_to_pat(value).match(hostname):
+                        if _dnsname_match(value, hostname):
                             return
                         dnsnames.append(value)
         if len(dnsnames) > 1:
@@ -143,7 +181,7 @@ def match_hostname(cert, hostname):
 
 try:
     from types import SimpleNamespace as Container
-except ImportError:
+except ImportError:  # pragma: no cover
     class Container(object):
         """
         A generic container for when multiple values need to be returned
@@ -154,7 +192,7 @@ def __init__(self, **kwargs):
 
 try:
     from shutil import which
-except ImportError:
+except ImportError:  # pragma: no cover
     # Implementation from Python 3.3
     def which(cmd, mode=os.F_OK | os.X_OK, path=None):
         """Given a command, mode, and a PATH string, return the path which
@@ -223,7 +261,7 @@ def _access_check(fn, mode):
 
 from zipfile import ZipFile as BaseZipFile
 
-if hasattr(BaseZipFile, '__enter__'):
+if hasattr(BaseZipFile, '__enter__'):  # pragma: no cover
     ZipFile = BaseZipFile
 else:
     from zipfile import ZipExtFile as BaseZipExtFile
diff --git a/pip/_vendor/distlib/database.py b/pip/_vendor/distlib/database.py
index 0f013ec5496..7bc191447a0 100644
--- a/pip/_vendor/distlib/database.py
+++ b/pip/_vendor/distlib/database.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2012-2013 The Python Software Foundation.
+# Copyright (C) 2012-2016 The Python Software Foundation.
 # See LICENSE.txt and CONTRIBUTORS.txt.
 #
 """PEP 376 implementation."""
@@ -20,7 +20,7 @@
 from . import DistlibException, resources
 from .compat import StringIO
 from .version import get_scheme, UnsupportedVersionError
-from .metadata import Metadata, METADATA_FILENAME
+from .metadata import Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME
 from .util import (parse_requirement, cached_property, parse_name_and_version,
                    read_exports, write_exports, CSVReader, CSVWriter)
 
@@ -132,13 +132,17 @@ def _yield_distributions(self):
                 if not r or r.path in seen:
                     continue
                 if self._include_dist and entry.endswith(DISTINFO_EXT):
-                    metadata_path = posixpath.join(entry, METADATA_FILENAME)
-                    pydist = finder.find(metadata_path)
-                    if not pydist:
+                    possible_filenames = [METADATA_FILENAME, WHEEL_METADATA_FILENAME]
+                    for metadata_filename in possible_filenames:
+                        metadata_path = posixpath.join(entry, metadata_filename)
+                        pydist = finder.find(metadata_path)
+                        if pydist:
+                            break
+                    else:
                         continue
 
-                    metadata = Metadata(fileobj=pydist.as_stream(),
-                                        scheme='legacy')
+                    with contextlib.closing(pydist.as_stream()) as stream:
+                        metadata = Metadata(fileobj=stream, scheme='legacy')
                     logger.debug('Found %s', r.path)
                     seen.add(r.path)
                     yield new_dist_class(r.path, metadata=metadata,
@@ -334,6 +338,8 @@ def __init__(self, metadata):
         self.digest = None
         self.extras = None      # additional features requested
         self.context = None     # environment marker overrides
+        self.download_urls = set()
+        self.digests = {}
 
     @property
     def source_url(self):
@@ -364,9 +370,11 @@ def provides(self):
         return plist
 
     def _get_requirements(self, req_attr):
-        reqts = getattr(self.metadata, req_attr)
-        return set(self.metadata.get_requirements(reqts, extras=self.extras,
-                                                  env=self.context))
+        md = self.metadata
+        logger.debug('Getting requirements from metadata %r', md.todict())
+        reqts = getattr(md, req_attr)
+        return set(md.get_requirements(reqts, extras=self.extras,
+                                       env=self.context))
 
     @property
     def run_requires(self):
@@ -528,6 +536,9 @@ def __init__(self, path, metadata=None, env=None):
             metadata = env._cache.path[path].metadata
         elif metadata is None:
             r = finder.find(METADATA_FILENAME)
+            # Temporary - for Wheel 0.23 support
+            if r is None:
+                r = finder.find(WHEEL_METADATA_FILENAME)
             # Temporary - for legacy support
             if r is None:
                 r = finder.find('METADATA')
@@ -925,9 +936,9 @@ def parse_requires_path(req_path):
                     requires = None
         elif path.endswith('.egg-info'):
             if os.path.isdir(path):
-                path = os.path.join(path, 'PKG-INFO')
                 req_path = os.path.join(path, 'requires.txt')
                 requires = parse_requires_path(req_path)
+                path = os.path.join(path, 'PKG-INFO')
             metadata = Metadata(path=path, scheme='legacy')
         else:
             raise DistlibException('path must end with .egg-info or .egg, '
diff --git a/pip/_vendor/distlib/index.py b/pip/_vendor/distlib/index.py
index 83004b13f70..73037c97b4c 100644
--- a/pip/_vendor/distlib/index.py
+++ b/pip/_vendor/distlib/index.py
@@ -148,7 +148,8 @@ def _reader(self, name, stream, outbuf):
             logger.debug('%s: %s' % (name, s))
         stream.close()
 
-    def get_sign_command(self, filename, signer, sign_password):
+    def get_sign_command(self, filename, signer, sign_password,
+                         keystore=None):
         """
         Return a suitable command for signing a file.
 
@@ -156,12 +157,17 @@ def get_sign_command(self, filename, signer, sign_password):
         :param signer: The identifier of the signer of the file.
         :param sign_password: The passphrase for the signer's
                               private key used for signing.
+        :param keystore: The path to a directory which contains the keys
+                         used in verification. If not specified, the
+                         instance's ``gpg_home`` attribute is used instead.
         :return: The signing command as a list suitable to be
                  passed to :class:`subprocess.Popen`.
         """
         cmd = [self.gpg, '--status-fd', '2', '--no-tty']
-        if self.gpg_home:
-            cmd.extend(['--homedir', self.gpg_home])
+        if keystore is None:
+            keystore = self.gpg_home
+        if keystore:
+            cmd.extend(['--homedir', keystore])
         if sign_password is not None:
             cmd.extend(['--batch', '--passphrase-fd', '0'])
         td = tempfile.mkdtemp()
@@ -206,7 +212,7 @@ def run_command(self, cmd, input_data=None):
         t2.join()
         return p.returncode, stdout, stderr
 
-    def sign_file(self, filename, signer, sign_password):
+    def sign_file(self, filename, signer, sign_password, keystore=None):
         """
         Sign a file.
 
@@ -214,10 +220,14 @@ def sign_file(self, filename, signer, sign_password):
         :param signer: The identifier of the signer of the file.
         :param sign_password: The passphrase for the signer's
                               private key used for signing.
+        :param keystore: The path to a directory which contains the keys
+                         used in signing. If not specified, the instance's
+                         ``gpg_home`` attribute is used instead.
         :return: The absolute pathname of the file where the signature is
                  stored.
         """
-        cmd, sig_file = self.get_sign_command(filename, signer, sign_password)
+        cmd, sig_file = self.get_sign_command(filename, signer, sign_password,
+                                              keystore)
         rc, stdout, stderr = self.run_command(cmd,
                                               sign_password.encode('utf-8'))
         if rc != 0:
@@ -226,7 +236,7 @@ def sign_file(self, filename, signer, sign_password):
         return sig_file
 
     def upload_file(self, metadata, filename, signer=None, sign_password=None,
-                    filetype='sdist', pyversion='source'):
+                    filetype='sdist', pyversion='source', keystore=None):
         """
         Upload a release file to the index.
 
@@ -242,6 +252,9 @@ def upload_file(self, metadata, filename, signer=None, sign_password=None,
         :param pyversion: The version of Python which the release relates
                           to. For code compatible with any Python, this would
                           be ``source``, otherwise it would be e.g. ``3.2``.
+        :param keystore: The path to a directory which contains the keys
+                         used in signing. If not specified, the instance's
+                         ``gpg_home`` attribute is used instead.
         :return: The HTTP response received from PyPI upon submission of the
                 request.
         """
@@ -255,7 +268,8 @@ def upload_file(self, metadata, filename, signer=None, sign_password=None,
             if not self.gpg:
                 logger.warning('no signing program available - not signed')
             else:
-                sig_file = self.sign_file(filename, signer, sign_password)
+                sig_file = self.sign_file(filename, signer, sign_password,
+                                          keystore)
         with open(filename, 'rb') as f:
             file_data = f.read()
         md5_digest = hashlib.md5(file_data).hexdigest()
@@ -306,7 +320,8 @@ def upload_documentation(self, metadata, doc_dir):
         request = self.encode_request(fields, files)
         return self.send_request(request)
 
-    def get_verify_command(self, signature_filename, data_filename):
+    def get_verify_command(self, signature_filename, data_filename,
+                           keystore=None):
         """
         Return a suitable command for verifying a file.
 
@@ -314,17 +329,23 @@ def get_verify_command(self, signature_filename, data_filename):
                                    signature.
         :param data_filename: The pathname to the file containing the
                               signed data.
+        :param keystore: The path to a directory which contains the keys
+                         used in verification. If not specified, the
+                         instance's ``gpg_home`` attribute is used instead.
         :return: The verifying command as a list suitable to be
                  passed to :class:`subprocess.Popen`.
         """
         cmd = [self.gpg, '--status-fd', '2', '--no-tty']
-        if self.gpg_home:
-            cmd.extend(['--homedir', self.gpg_home])
+        if keystore is None:
+            keystore = self.gpg_home
+        if keystore:
+            cmd.extend(['--homedir', keystore])
         cmd.extend(['--verify', signature_filename, data_filename])
         logger.debug('invoking: %s', ' '.join(cmd))
         return cmd
 
-    def verify_signature(self, signature_filename, data_filename):
+    def verify_signature(self, signature_filename, data_filename,
+                         keystore=None):
         """
         Verify a signature for a file.
 
@@ -332,12 +353,16 @@ def verify_signature(self, signature_filename, data_filename):
                                    signature.
         :param data_filename: The pathname to the file containing the
                               signed data.
+        :param keystore: The path to a directory which contains the keys
+                         used in verification. If not specified, the
+                         instance's ``gpg_home`` attribute is used instead.
         :return: True if the signature was verified, else False.
         """
         if not self.gpg:
             raise DistlibException('verification unavailable because gpg '
                                    'unavailable')
-        cmd = self.get_verify_command(signature_filename, data_filename)
+        cmd = self.get_verify_command(signature_filename, data_filename,
+                                      keystore)
         rc, stdout, stderr = self.run_command(cmd)
         if rc not in (0, 1):
             raise DistlibException('verify command failed with error '
diff --git a/pip/_vendor/distlib/locators.py b/pip/_vendor/distlib/locators.py
index 07bc1fd434c..1e146382045 100644
--- a/pip/_vendor/distlib/locators.py
+++ b/pip/_vendor/distlib/locators.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2012-2013 Vinay Sajip.
+# Copyright (C) 2012-2015 Vinay Sajip.
 # Licensed to the Python Software Foundation under a contributor agreement.
 # See LICENSE.txt and CONTRIBUTORS.txt.
 #
@@ -14,7 +14,7 @@
 import re
 try:
     import threading
-except ImportError:
+except ImportError:  # pragma: no cover
     import dummy_threading as threading
 import zlib
 
@@ -36,7 +36,7 @@
 HASHER_HASH = re.compile('^(\w+)=([a-f0-9]+)')
 CHARSET = re.compile(r';\s*charset\s*=\s*(.*)\s*$', re.I)
 HTML_CONTENT_TYPE = re.compile('text/html|application/x(ht)?ml')
-DEFAULT_INDEX = 'http://python.org/pypi'
+DEFAULT_INDEX = 'https://pypi.python.org/pypi'
 
 def get_all_distribution_names(url=None):
     """
@@ -165,8 +165,13 @@ def score_url(self, url):
         for a given project release.
         """
         t = urlparse(url)
+        basename = posixpath.basename(t.path)
+        compatible = True
+        is_wheel = basename.endswith('.whl')
+        if is_wheel:
+            compatible = is_compatible(Wheel(basename), self.wheel_tags)
         return (t.scheme != 'https', 'pypi.python.org' in t.netloc,
-                posixpath.basename(t.path))
+                is_wheel, compatible, basename)
 
     def prefer_url(self, url1, url2):
         """
@@ -174,8 +179,9 @@ def prefer_url(self, url1, url2):
         archives for the same version of a distribution (for example,
         .tar.gz vs. zip).
 
-        The current implement favours http:// URLs over https://, archives
-        from PyPI over those from other locations and then the archive name.
+        The current implementation favours https:// URLs over http://, archives
+        from PyPI over those from other locations, wheel compatibility (if a
+        wheel) and then the archive name.
         """
         result = url2
         if url1:
@@ -290,9 +296,9 @@ def _get_digest(self, info):
 
     def _update_version_data(self, result, info):
         """
-        Update a result dictionary (the final result from _get_project) with a dictionary for a
-        specific version, whih typically holds information gleaned from a filename or URL for an
-        archive for the distribution.
+        Update a result dictionary (the final result from _get_project) with a
+        dictionary for a specific version, which typically holds information
+        gleaned from a filename or URL for an archive for the distribution.
         """
         name = info.pop('name')
         version = info.pop('version')
@@ -302,9 +308,12 @@ def _update_version_data(self, result, info):
         else:
             dist = make_dist(name, version, scheme=self.scheme)
             md = dist.metadata
-        dist.digest = self._get_digest(info)
+        dist.digest = digest = self._get_digest(info)
+        url = info['url']
+        result['digests'][url] = digest
         if md.source_url != info['url']:
-            md.source_url = self.prefer_url(md.source_url, info['url'])
+            md.source_url = self.prefer_url(md.source_url, url)
+            result['urls'].setdefault(version, set()).add(url)
         dist.locator = self
         result[version] = dist
 
@@ -329,11 +338,13 @@ def locate(self, requirement, prereleases=False):
         self.matcher = matcher = scheme.matcher(r.requirement)
         logger.debug('matcher: %s (%s)', matcher, type(matcher).__name__)
         versions = self.get_project(r.name)
-        if versions:
+        if len(versions) > 2:   # urls and digests keys are present
             # sometimes, versions are invalid
             slist = []
             vcls = matcher.version_class
             for k in versions:
+                if k in ('urls', 'digests'):
+                    continue
                 try:
                     if not matcher.match(k):
                         logger.debug('%s did not match %r', matcher, k)
@@ -343,16 +354,25 @@ def locate(self, requirement, prereleases=False):
                         else:
                             logger.debug('skipping pre-release '
                                          'version %s of %s', k, matcher.name)
-                except Exception:
+                except Exception:  # pragma: no cover
                     logger.warning('error matching %s with %r', matcher, k)
                     pass # slist.append(k)
             if len(slist) > 1:
                 slist = sorted(slist, key=scheme.key)
             if slist:
                 logger.debug('sorted list: %s', slist)
-                result = versions[slist[-1]]
-        if result and r.extras:
-            result.extras = r.extras
+                version = slist[-1]
+                result = versions[version]
+        if result:
+            if r.extras:
+                result.extras = r.extras
+            result.download_urls = versions.get('urls', {}).get(version, set())
+            d = {}
+            sd = versions.get('digests', {})
+            for url in result.download_urls:
+                if url in sd:
+                    d[url] = sd[url]
+            result.digests = d
         self.matcher = None
         return result
 
@@ -380,7 +400,7 @@ def get_distribution_names(self):
         return set(self.client.list_packages())
 
     def _get_project(self, name):
-        result = {}
+        result = {'urls': {}, 'digests': {}}
         versions = self.client.package_releases(name, True)
         for v in versions:
             urls = self.client.release_urls(name, v)
@@ -398,12 +418,17 @@ def _get_project(self, name):
                 dist.digest = self._get_digest(info)
                 dist.locator = self
                 result[v] = dist
+                for info in urls:
+                    url = info['url']
+                    digest = self._get_digest(info)
+                    result['urls'].setdefault(v, set()).add(url)
+                    result['digests'][url] = digest
         return result
 
 class PyPIJSONLocator(Locator):
     """
     This locator uses PyPI's JSON interface. It's very limited in functionality
-    nad probably not worth using.
+    and probably not worth using.
     """
     def __init__(self, url, **kwargs):
         super(PyPIJSONLocator, self).__init__(**kwargs)
@@ -416,7 +441,7 @@ def get_distribution_names(self):
         raise NotImplementedError('Not available from this locator')
 
     def _get_project(self, name):
-        result = {}
+        result = {'urls': {}, 'digests': {}}
         url = urljoin(self.base_url, '%s/json' % quote(name))
         try:
             resp = self.opener.open(url)
@@ -430,13 +455,39 @@ def _get_project(self, name):
             md.keywords = data.get('keywords', [])
             md.summary = data.get('summary')
             dist = Distribution(md)
+            dist.locator = self
             urls = d['urls']
-            if urls:
-                info = urls[0]
-                md.source_url = info['url']
-                dist.digest = self._get_digest(info)
-                dist.locator = self
-                result[md.version] = dist
+            result[md.version] = dist
+            for info in d['urls']:
+                url = info['url']
+                dist.download_urls.add(url)
+                dist.digests[url] = self._get_digest(info)
+                result['urls'].setdefault(md.version, set()).add(url)
+                result['digests'][url] = self._get_digest(info)
+            # Now get other releases
+            for version, infos in d['releases'].items():
+                if version == md.version:
+                    continue    # already done
+                omd = Metadata(scheme=self.scheme)
+                omd.name = md.name
+                omd.version = version
+                odist = Distribution(omd)
+                odist.locator = self
+                result[version] = odist
+                for info in infos:
+                    url = info['url']
+                    odist.download_urls.add(url)
+                    odist.digests[url] = self._get_digest(info)
+                    result['urls'].setdefault(version, set()).add(url)
+                    result['digests'][url] = self._get_digest(info)
+#            for info in urls:
+#                md.source_url = info['url']
+#                dist.digest = self._get_digest(info)
+#                dist.locator = self
+#                for info in urls:
+#                    url = info['url']
+#                    result['urls'].setdefault(md.version, set()).add(url)
+#                    result['digests'][url] = self._get_digest(info)
         except Exception as e:
             logger.exception('JSON fetch failed: %s', e)
         return result
@@ -567,7 +618,7 @@ def _wait_threads(self):
         self._threads = []
 
     def _get_project(self, name):
-        result = {}
+        result = {'urls': {}, 'digests': {}}
         with self._gplock:
             self.result = result
             self.project_name = name
@@ -712,18 +763,18 @@ def get_page(self, url):
                             encoding = m.group(1)
                         try:
                             data = data.decode(encoding)
-                        except UnicodeError:
+                        except UnicodeError:  # pragma: no cover
                             data = data.decode('latin-1')    # fallback
                         result = Page(data, final_url)
                         self._page_cache[final_url] = result
                 except HTTPError as e:
                     if e.code != 404:
                         logger.exception('Fetch failed: %s: %s', url, e)
-                except URLError as e:
+                except URLError as e:  # pragma: no cover
                     logger.exception('Fetch failed: %s: %s', url, e)
                     with self._lock:
                         self._bad_hosts.add(host)
-                except Exception as e:
+                except Exception as e:  # pragma: no cover
                     logger.exception('Fetch failed: %s: %s', url, e)
                 finally:
                     self._page_cache[url] = result   # even if None (failure)
@@ -761,7 +812,7 @@ def __init__(self, path, **kwargs):
         self.recursive = kwargs.pop('recursive', True)
         super(DirectoryLocator, self).__init__(**kwargs)
         path = os.path.abspath(path)
-        if not os.path.isdir(path):
+        if not os.path.isdir(path):  # pragma: no cover
             raise DistlibException('Not a directory: %r' % path)
         self.base_dir = path
 
@@ -774,7 +825,7 @@ def should_include(self, filename, parent):
         return filename.endswith(self.downloadable_extensions)
 
     def _get_project(self, name):
-        result = {}
+        result = {'urls': {}, 'digests': {}}
         for root, dirs, files in os.walk(self.base_dir):
             for fn in files:
                 if self.should_include(fn, root):
@@ -822,7 +873,7 @@ def get_distribution_names(self):
         raise NotImplementedError('Not available from this locator')
 
     def _get_project(self, name):
-        result = {}
+        result = {'urls': {}, 'digests': {}}
         data = get_project_data(name)
         if data:
             for info in data.get('files', []):
@@ -843,6 +894,7 @@ def _get_project(self, name):
                 md.dependencies = info.get('requirements', {})
                 dist.exports = info.get('exports', {})
                 result[dist.version] = dist
+                result['urls'].setdefault(dist.version, set()).add(info['url'])
         return result
 
 class DistPathLocator(Locator):
@@ -863,9 +915,13 @@ def __init__(self, distpath, **kwargs):
     def _get_project(self, name):
         dist = self.distpath.get_distribution(name)
         if dist is None:
-            result = {}
+            result = {'urls': {}, 'digests': {}}
         else:
-            result = { dist.version: dist }
+            result = {
+                dist.version: dist,
+                'urls': {dist.version: set([dist.source_url])},
+                'digests': {dist.version: set([None])}
+            }
         return result
 
 
@@ -907,7 +963,20 @@ def _get_project(self, name):
             d = locator.get_project(name)
             if d:
                 if self.merge:
+                    files = result.get('urls', {})
+                    digests = result.get('digests', {})
+                    # next line could overwrite result['urls'], result['digests']
                     result.update(d)
+                    df = result.get('urls')
+                    if files and df:
+                        for k, v in files.items():
+                            if k in df:
+                                df[k] |= v
+                            else:
+                                df[k] = v
+                    dd = result.get('digests')
+                    if digests and dd:
+                        dd.update(digests)
                 else:
                     # See issue #18. If any dists are found and we're looking
                     # for specific constraints, we only return something if
@@ -1014,7 +1083,7 @@ def get_matcher(self, reqt):
         """
         try:
             matcher = self.scheme.matcher(reqt)
-        except UnsupportedVersionError:
+        except UnsupportedVersionError:  # pragma: no cover
             # XXX compat-mode if cannot read the version
             name = reqt.split()[0]
             matcher = self.scheme.matcher(name)
@@ -1071,7 +1140,8 @@ def try_to_replace(self, provider, other, problems):
                 unmatched.add(s)
         if unmatched:
             # can't replace other with provider
-            problems.add(('cantreplace', provider, other, unmatched))
+            problems.add(('cantreplace', provider, other,
+                          frozenset(unmatched)))
             result = False
         else:
             # can replace other with provider
diff --git a/pip/_vendor/distlib/manifest.py b/pip/_vendor/distlib/manifest.py
index c6b98c59a37..21cff45e35c 100644
--- a/pip/_vendor/distlib/manifest.py
+++ b/pip/_vendor/distlib/manifest.py
@@ -147,9 +147,10 @@ def process_directive(self, directive):
 
         elif action == 'exclude':
             for pattern in patterns:
-                if not self._exclude_pattern(pattern, anchor=True):
-                    logger.warning('no previously-included files '
-                                   'found matching %r', pattern)
+                found = self._exclude_pattern(pattern, anchor=True)
+                #if not found:
+                #    logger.warning('no previously-included files '
+                #                   'found matching %r', pattern)
 
         elif action == 'global-include':
             for pattern in patterns:
@@ -159,10 +160,11 @@ def process_directive(self, directive):
 
         elif action == 'global-exclude':
             for pattern in patterns:
-                if not self._exclude_pattern(pattern, anchor=False):
-                    logger.warning('no previously-included files '
-                                   'matching %r found anywhere in '
-                                   'distribution', pattern)
+                found = self._exclude_pattern(pattern, anchor=False)
+                #if not found:
+                #    logger.warning('no previously-included files '
+                #                   'matching %r found anywhere in '
+                #                   'distribution', pattern)
 
         elif action == 'recursive-include':
             for pattern in patterns:
@@ -172,10 +174,11 @@ def process_directive(self, directive):
 
         elif action == 'recursive-exclude':
             for pattern in patterns:
-                if not self._exclude_pattern(pattern, prefix=thedir):
-                    logger.warning('no previously-included files '
-                                   'matching %r found under directory %r',
-                                   pattern, thedir)
+                found = self._exclude_pattern(pattern, prefix=thedir)
+                #if not found:
+                #    logger.warning('no previously-included files '
+                #                   'matching %r found under directory %r',
+                #                   pattern, thedir)
 
         elif action == 'graft':
             if not self._include_pattern(None, prefix=dirpattern):
diff --git a/pip/_vendor/distlib/metadata.py b/pip/_vendor/distlib/metadata.py
index 8441d8fefe4..71525dd9ec0 100644
--- a/pip/_vendor/distlib/metadata.py
+++ b/pip/_vendor/distlib/metadata.py
@@ -20,7 +20,7 @@
 from .compat import StringIO, string_types, text_type
 from .markers import interpret
 from .util import extract_by_key, get_extras
-from .version import get_scheme, PEP426_VERSION_RE
+from .version import get_scheme, PEP440_VERSION_RE
 
 logger = logging.getLogger(__name__)
 
@@ -50,7 +50,8 @@ class MetadataInvalidError(DistlibException):
 # to 1.2 once PEP 345 is supported everywhere
 PKG_INFO_PREFERRED_VERSION = '1.1'
 
-_LINE_PREFIX = re.compile('\n       \|')
+_LINE_PREFIX_1_2 = re.compile('\n       \|')
+_LINE_PREFIX_PRE_1_2 = re.compile('\n        ')
 _241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform',
                'Summary', 'Description',
                'Keywords', 'Home-page', 'Author', 'Author-email',
@@ -295,7 +296,10 @@ def _default_value(self, name):
         return 'UNKNOWN'
 
     def _remove_line_prefix(self, value):
-        return _LINE_PREFIX.sub('\n', value)
+        if self.metadata_version in ('1.0', '1.1'):
+            return _LINE_PREFIX_PRE_1_2.sub('\n', value)
+        else:
+            return _LINE_PREFIX_1_2.sub('\n', value)
 
     def __getattr__(self, name):
         if name in _ATTR2FIELD:
@@ -374,7 +378,10 @@ def write_file(self, fileobject, skip_unknown=False):
                 continue
             if field not in _LISTFIELDS:
                 if field == 'Description':
-                    values = values.replace('\n', '\n       |')
+                    if self.metadata_version in ('1.0', '1.1'):
+                        values = values.replace('\n', '\n        ')
+                    else:
+                        values = values.replace('\n', '\n       |')
                 values = [values]
 
             if field in _LISTTUPLEFIELDS:
@@ -548,7 +555,7 @@ def todict(self, skip_missing=False):
             ('description', 'Description'),
             ('keywords', 'Keywords'),
             ('platform', 'Platform'),
-            ('classifier', 'Classifier'),
+            ('classifiers', 'Classifier'),
             ('download_url', 'Download-URL'),
         )
 
@@ -617,6 +624,7 @@ def __repr__(self):
 
 
 METADATA_FILENAME = 'pydist.json'
+WHEEL_METADATA_FILENAME = 'metadata.json'
 
 
 class Metadata(object):
@@ -630,7 +638,7 @@ class Metadata(object):
 
     NAME_MATCHER = re.compile('^[0-9A-Z]([0-9A-Z_.-]*[0-9A-Z])?$', re.I)
 
-    VERSION_MATCHER = PEP426_VERSION_RE
+    VERSION_MATCHER = PEP440_VERSION_RE
 
     SUMMARY_MATCHER = re.compile('.{1,2047}')
 
@@ -741,7 +749,27 @@ def __getattribute__(self, key):
                     result = self._legacy.get(lk)
             else:
                 value = None if maker is None else maker()
-                result = self._data.get(key, value)
+                if key not in ('commands', 'exports', 'modules', 'namespaces',
+                               'classifiers'):
+                    result = self._data.get(key, value)
+                else:
+                    # special cases for PEP 459
+                    sentinel = object()
+                    result = sentinel
+                    d = self._data.get('extensions')
+                    if d:
+                        if key == 'commands':
+                            result = d.get('python.commands', value)
+                        elif key == 'classifiers':
+                            d = d.get('python.details')
+                            if d:
+                                result = d.get(key, value)
+                        else:
+                            d = d.get('python.exports')
+                            if d:
+                                result = d.get(key, value)
+                    if result is sentinel:
+                        result = value
         elif key not in common:
             result = object.__getattribute__(self, key)
         elif self._legacy:
@@ -770,8 +798,20 @@ def __setattr__(self, key, value):
                 if lk is None:
                     raise NotImplementedError
                 self._legacy[lk] = value
-            else:
+            elif key not in ('commands', 'exports', 'modules', 'namespaces',
+                             'classifiers'):
                 self._data[key] = value
+            else:
+                # special cases for PEP 459
+                d = self._data.setdefault('extensions', {})
+                if key == 'commands':
+                    d['python.commands'] = value
+                elif key == 'classifiers':
+                    d = d.setdefault('python.details', {})
+                    d[key] = value
+                else:
+                    d = d.setdefault('python.exports', {})
+                    d[key] = value
         elif key not in common:
             object.__setattr__(self, key, value)
         else:
diff --git a/pip/_vendor/distlib/resources.py b/pip/_vendor/distlib/resources.py
index 567840e7b02..9dd8ca016b1 100644
--- a/pip/_vendor/distlib/resources.py
+++ b/pip/_vendor/distlib/resources.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2013 Vinay Sajip.
+# Copyright (C) 2013-2016 Vinay Sajip.
 # Licensed to the Python Software Foundation under a contributor agreement.
 # See LICENSE.txt and CONTRIBUTORS.txt.
 #
@@ -120,6 +120,12 @@ class ResourceFinder(object):
     """
     Resource finder for file system resources.
     """
+
+    if sys.platform.startswith('java'):
+        skipped_extensions = ('.pyc', '.pyo', '.class')
+    else:
+        skipped_extensions = ('.pyc', '.pyo')
+
     def __init__(self, module):
         self.module = module
         self.loader = getattr(module, '__loader__', None)
@@ -129,7 +135,13 @@ def _adjust_path(self, path):
         return os.path.realpath(path)
 
     def _make_path(self, resource_name):
-        parts = resource_name.split('/')
+        # Issue #50: need to preserve type of path on Python 2.x
+        # like os.path._get_sep
+        if isinstance(resource_name, bytes):    # should only happen on 2.x
+            sep = b'/'
+        else:
+            sep = '/'
+        parts = resource_name.split(sep)
         parts.insert(0, self.base)
         result = os.path.join(*parts)
         return self._adjust_path(result)
@@ -164,7 +176,8 @@ def get_size(self, resource):
 
     def get_resources(self, resource):
         def allowed(f):
-            return f != '__pycache__' and not f.endswith(('.pyc', '.pyo'))
+            return (f != '__pycache__' and not
+                    f.endswith(self.skipped_extensions))
         return set([f for f in os.listdir(resource.path) if allowed(f)])
 
     def is_container(self, resource):
@@ -172,6 +185,26 @@ def is_container(self, resource):
 
     _is_directory = staticmethod(os.path.isdir)
 
+    def iterator(self, resource_name):
+        resource = self.find(resource_name)
+        if resource is not None:
+            todo = [resource]
+            while todo:
+                resource = todo.pop(0)
+                yield resource
+                if resource.is_container:
+                    rname = resource.name
+                    for name in resource.resources:
+                        if not rname:
+                            new_name = name
+                        else:
+                            new_name = '/'.join([rname, name])
+                        child = self.find(new_name)
+                        if child.is_container:
+                            todo.append(child)
+                        else:
+                            yield child
+
 
 class ZipResourceFinder(ResourceFinder):
     """
diff --git a/pip/_vendor/distlib/scripts.py b/pip/_vendor/distlib/scripts.py
index 36850b2a3f5..c9996d59890 100644
--- a/pip/_vendor/distlib/scripts.py
+++ b/pip/_vendor/distlib/scripts.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2013 Vinay Sajip.
+# Copyright (C) 2013-2015 Vinay Sajip.
 # Licensed to the Python Software Foundation under a contributor agreement.
 # See LICENSE.txt and CONTRIBUTORS.txt.
 #
@@ -11,7 +11,7 @@
 import struct
 import sys
 
-from .compat import sysconfig, fsencode, detect_encoding, ZipFile
+from .compat import sysconfig, detect_encoding, ZipFile
 from .resources import finder
 from .util import (FileOperator, get_export_entry, convert_path,
                    get_executable, in_venv)
@@ -63,6 +63,22 @@ def _resolve(module, func):
 '''
 
 
+def _enquote_executable(executable):
+    if ' ' in executable:
+        # make sure we quote only the executable in case of env
+        # for example /usr/bin/env "/dir with spaces/bin/jython"
+        # instead of "/usr/bin/env /dir with spaces/bin/jython"
+        # otherwise whole
+        if executable.startswith('/usr/bin/env '):
+            env, _executable = executable.split(' ', 1)
+            if ' ' in _executable and not _executable.startswith('"'):
+                executable = '%s "%s"' % (env, _executable)
+        else:
+            if not executable.startswith('"'):
+                executable = '"%s"' % executable
+    return executable
+
+
 class ScriptMaker(object):
     """
     A class to copy or create scripts from source scripts or callable
@@ -80,17 +96,46 @@ def __init__(self, source_dir, target_dir, add_launchers=True,
         self.force = False
         self.clobber = False
         # It only makes sense to set mode bits on POSIX.
-        self.set_mode = (os.name == 'posix')
+        self.set_mode = (os.name == 'posix') or (os.name == 'java' and
+                                                 os._name == 'posix')
         self.variants = set(('', 'X.Y'))
         self._fileop = fileop or FileOperator(dry_run)
 
+        self._is_nt = os.name == 'nt' or (
+            os.name == 'java' and os._name == 'nt')
+
     def _get_alternate_executable(self, executable, options):
-        if options.get('gui', False) and os.name == 'nt':
+        if options.get('gui', False) and self._is_nt:  # pragma: no cover
             dn, fn = os.path.split(executable)
             fn = fn.replace('python', 'pythonw')
             executable = os.path.join(dn, fn)
         return executable
 
+    if sys.platform.startswith('java'):  # pragma: no cover
+        def _is_shell(self, executable):
+            """
+            Determine if the specified executable is a script
+            (contains a #! line)
+            """
+            try:
+                with open(executable) as fp:
+                    return fp.read(2) == '#!'
+            except (OSError, IOError):
+                logger.warning('Failed to open %s', executable)
+                return False
+
+        def _fix_jython_executable(self, executable):
+            if self._is_shell(executable):
+                # Workaround for Jython is not needed on Linux systems.
+                import java
+
+                if java.lang.System.getProperty('os.name') == 'Linux':
+                    return executable
+            elif executable.lower().endswith('jython.exe'):
+                # Use wrapper exe for Jython on Windows
+                return executable
+            return '/usr/bin/env %s' % executable
+
     def _get_shebang(self, encoding, post_interp=b'', options=None):
         enquote = True
         if self.executable:
@@ -98,10 +143,10 @@ def _get_shebang(self, encoding, post_interp=b'', options=None):
             enquote = False     # assume this will be taken care of
         elif not sysconfig.is_python_build():
             executable = get_executable()
-        elif in_venv():
+        elif in_venv():  # pragma: no cover
             executable = os.path.join(sysconfig.get_path('scripts'),
                             'python%s' % sysconfig.get_config_var('EXE'))
-        else:
+        else:  # pragma: no cover
             executable = os.path.join(
                 sysconfig.get_config_var('BINDIR'),
                'python%s%s' % (sysconfig.get_config_var('VERSION'),
@@ -109,11 +154,21 @@ def _get_shebang(self, encoding, post_interp=b'', options=None):
         if options:
             executable = self._get_alternate_executable(executable, options)
 
+        if sys.platform.startswith('java'):  # pragma: no cover
+            executable = self._fix_jython_executable(executable)
+        # Normalise case for Windows
+        executable = os.path.normcase(executable)
         # If the user didn't specify an executable, it may be necessary to
         # cater for executable paths with spaces (not uncommon on Windows)
-        if enquote and ' ' in executable:
-            executable = '"%s"' % executable
-        executable = fsencode(executable)
+        if enquote:
+            executable = _enquote_executable(executable)
+        # Issue #51: don't use fsencode, since we later try to
+        # check that the shebang is decodable using utf-8.
+        executable = executable.encode('utf-8')
+        # in case of IronPython, play safe and enable frames support
+        if (sys.platform == 'cli' and '-X:Frames' not in post_interp
+            and '-X:FullFrames' not in post_interp):  # pragma: no cover
+            post_interp += b' -X:Frames'
         shebang = b'#!' + executable + post_interp + b'\n'
         # Python parser starts to read a script using UTF-8 until
         # it gets a #coding:xxx cookie. The shebang has to be the
@@ -122,7 +177,7 @@ def _get_shebang(self, encoding, post_interp=b'', options=None):
         # UTF-8.
         try:
             shebang.decode('utf-8')
-        except UnicodeDecodeError:
+        except UnicodeDecodeError:  # pragma: no cover
             raise ValueError(
                 'The shebang (%r) is not decodable from utf-8' % shebang)
         # If the script is encoded to a custom encoding (use a
@@ -131,7 +186,7 @@ def _get_shebang(self, encoding, post_interp=b'', options=None):
         if encoding != 'utf-8':
             try:
                 shebang.decode(encoding)
-            except UnicodeDecodeError:
+            except UnicodeDecodeError:  # pragma: no cover
                 raise ValueError(
                     'The shebang (%r) is not decodable '
                     'from the script encoding (%r)' % (shebang, encoding))
@@ -148,11 +203,11 @@ def get_manifest(self, exename):
         return self.manifest % base
 
     def _write_script(self, names, shebang, script_bytes, filenames, ext):
-        use_launcher = self.add_launchers and os.name == 'nt'
+        use_launcher = self.add_launchers and self._is_nt
         linesep = os.linesep.encode('utf-8')
         if not use_launcher:
             script_bytes = shebang + linesep + script_bytes
-        else:
+        else:  # pragma: no cover
             if ext == 'py':
                 launcher = self._get_launcher('t')
             else:
@@ -164,7 +219,7 @@ def _write_script(self, names, shebang, script_bytes, filenames, ext):
             script_bytes = launcher + shebang + linesep + zip_data
         for name in names:
             outname = os.path.join(self.target_dir, name)
-            if use_launcher:
+            if use_launcher:  # pragma: no cover
                 n, e = os.path.splitext(outname)
                 if e.startswith('.py'):
                     outname = n
@@ -187,7 +242,7 @@ def _write_script(self, names, shebang, script_bytes, filenames, ext):
                     except Exception:
                         pass    # still in use - ignore error
             else:
-                if os.name == 'nt' and not outname.endswith('.' + ext):
+                if self._is_nt and not outname.endswith('.' + ext):  # pragma: no cover
                     outname = '%s.%s' % (outname, ext)
                 if os.path.exists(outname) and not self.clobber:
                     logger.warning('Skipping existing file %s', outname)
@@ -198,7 +253,13 @@ def _write_script(self, names, shebang, script_bytes, filenames, ext):
             filenames.append(outname)
 
     def _make_script(self, entry, filenames, options=None):
-        shebang = self._get_shebang('utf-8', options=options)
+        post_interp = b''
+        if options:
+            args = options.get('interpreter_args', [])
+            if args:
+                args = ' %s' % ' '.join(args)
+                post_interp = args.encode('utf-8')
+        shebang = self._get_shebang('utf-8', post_interp, options=options)
         script = self._get_script_text(entry).encode('utf-8')
         name = entry.name
         scriptnames = set()
@@ -227,15 +288,13 @@ def _copy_script(self, script, filenames):
         # script.
         try:
             f = open(script, 'rb')
-        except IOError:
+        except IOError:  # pragma: no cover
             if not self.dry_run:
                 raise
             f = None
         else:
-            encoding, lines = detect_encoding(f.readline)
-            f.seek(0)
             first_line = f.readline()
-            if not first_line:
+            if not first_line:  # pragma: no cover
                 logger.warning('%s: %s is an empty file (skipping)',
                                self.get_command_name(),  script)
                 return
@@ -256,8 +315,10 @@ def _copy_script(self, script, filenames):
             logger.info('copying and adjusting %s -> %s', script,
                         self.target_dir)
             if not self._fileop.dry_run:
+                encoding, lines = detect_encoding(f.readline)
+                f.seek(0)
                 shebang = self._get_shebang(encoding, post_interp)
-                if b'pythonw' in first_line:
+                if b'pythonw' in first_line:  # pragma: no cover
                     ext = 'pyw'
                 else:
                     ext = 'py'
@@ -274,7 +335,7 @@ def dry_run(self):
     def dry_run(self, value):
         self._fileop.dry_run = value
 
-    if os.name == 'nt':
+    if os.name == 'nt' or (os.name == 'java' and os._name == 'nt'):  # pragma: no cover
         # Executable launcher support.
         # Launchers are from https://bitbucket.org/vinay.sajip/simple_launcher/
 
diff --git a/pip/_vendor/distlib/t32.exe b/pip/_vendor/distlib/t32.exe
index 43f39f31e98..e0168c22c57 100644
Binary files a/pip/_vendor/distlib/t32.exe and b/pip/_vendor/distlib/t32.exe differ
diff --git a/pip/_vendor/distlib/t64.exe b/pip/_vendor/distlib/t64.exe
index 73e2f40522d..29a1fd6cc06 100644
Binary files a/pip/_vendor/distlib/t64.exe and b/pip/_vendor/distlib/t64.exe differ
diff --git a/pip/_vendor/distlib/util.py b/pip/_vendor/distlib/util.py
index 29ec519ab3a..7e209ec2da4 100644
--- a/pip/_vendor/distlib/util.py
+++ b/pip/_vendor/distlib/util.py
@@ -1,5 +1,5 @@
 #
-# Copyright (C) 2012-2013 The Python Software Foundation.
+# Copyright (C) 2012-2016 The Python Software Foundation.
 # See LICENSE.txt and CONTRIBUTORS.txt.
 #
 import codecs
@@ -20,6 +20,8 @@
 import sys
 import tarfile
 import tempfile
+import textwrap
+
 try:
     import threading
 except ImportError:
@@ -28,10 +30,11 @@
 
 from . import DistlibException
 from .compat import (string_types, text_type, shutil, raw_input, StringIO,
-                     cache_from_source, urlopen, httplib, xmlrpclib, splittype,
-                     HTTPHandler, HTTPSHandler as BaseHTTPSHandler,
+                     cache_from_source, urlopen, urljoin, httplib, xmlrpclib,
+                     splittype, HTTPHandler, HTTPSHandler as BaseHTTPSHandler,
                      BaseConfigurator, valid_ident, Container, configparser,
-                     URLError, match_hostname, CertificateError, ZipFile)
+                     URLError, match_hostname, CertificateError, ZipFile,
+                     fsdecode)
 
 logger = logging.getLogger(__name__)
 
@@ -154,11 +157,18 @@ def in_venv():
 
 
 def get_executable():
-    if sys.platform == 'darwin' and ('__PYVENV_LAUNCHER__'
-                                     in os.environ):
-        result =  os.environ['__PYVENV_LAUNCHER__']
-    else:
-        result = sys.executable
+# The __PYVENV_LAUNCHER__ dance is apparently no longer needed, as
+# changes to the stub launcher mean that sys.executable always points
+# to the stub on OS X
+#    if sys.platform == 'darwin' and ('__PYVENV_LAUNCHER__'
+#                                     in os.environ):
+#        result =  os.environ['__PYVENV_LAUNCHER__']
+#    else:
+#        result = sys.executable
+#    return result
+    result = os.path.normcase(sys.executable)
+    if not isinstance(result, text_type):
+        result = fsdecode(result)
     return result
 
 
@@ -195,8 +205,8 @@ def read_exports(stream):
     data = stream.read()
     stream = StringIO(data)
     try:
-        data = json.load(stream)
-        result = data['exports']
+        jdata = json.load(stream)
+        result = jdata['extensions']['python.exports']['exports']
         for group, entries in result.items():
             for k, v in entries.items():
                 s = '%s = %s' % (k, v)
@@ -206,11 +216,22 @@ def read_exports(stream):
         return result
     except Exception:
         stream.seek(0, 0)
+
+    def read_stream(cp, stream):
+        if hasattr(cp, 'read_file'):
+            cp.read_file(stream)
+        else:
+            cp.readfp(stream)
+
     cp = configparser.ConfigParser()
-    if hasattr(cp, 'read_file'):
-        cp.read_file(stream)
-    else:
-        cp.readfp(stream)
+    try:
+        read_stream(cp, stream)
+    except configparser.MissingSectionHeaderError:
+        stream.close()
+        data = textwrap.dedent(data)
+        stream = StringIO(data)
+        read_stream(cp, stream)
+
     result = {}
     for key in cp.sections():
         result[key] = entries = {}
@@ -393,7 +414,7 @@ def write_text_file(self, path, data, encoding):
         self.record_as_written(path)
 
     def set_mode(self, bits, mask, files):
-        if os.name == 'posix':
+        if os.name == 'posix' or (os.name == 'java' and os._name == 'posix'):
             # Set the executable bits (owner, group, and world) on
             # all the files specified.
             for f in files:
@@ -536,12 +557,11 @@ def __eq__(self, other):
     __hash__ = object.__hash__
 
 
-ENTRY_RE = re.compile(r'''(?P(\w|[-.])+)
+ENTRY_RE = re.compile(r'''(?P(\w|[-.+])+)
                       \s*=\s*(?P(\w+)([:\.]\w+)*)
                       \s*(\[\s*(?P\w+(=\w+)?(,\s*\w+(=\w+)?)*)\s*\])?
                       ''', re.VERBOSE)
 
-
 def get_export_entry(specification):
     m = ENTRY_RE.search(specification)
     if not m:
@@ -743,8 +763,9 @@ def _get_external_data(url):
         # using a custom redirect handler.
         resp = urlopen(url)
         headers = resp.info()
-        if headers.get('Content-Type') != 'application/json':
-            logger.debug('Unexpected response for JSON request')
+        ct = headers.get('Content-Type')
+        if not ct.startswith('application/json'):
+            logger.debug('Unexpected response for JSON request: %s', ct)
         else:
             reader = codecs.getreader('utf-8')(resp)
             #data = reader.read().decode('utf-8')
@@ -754,16 +775,17 @@ def _get_external_data(url):
         logger.exception('Failed to get external data for %s: %s', url, e)
     return result
 
+_external_data_base_url = 'https://www.red-dove.com/pypi/projects/'
 
 def get_project_data(name):
-    url = ('https://www.red-dove.com/pypi/projects/'
-           '%s/%s/project.json' % (name[0].upper(), name))
+    url = '%s/%s/project.json' % (name[0].upper(), name)
+    url = urljoin(_external_data_base_url, url)
     result = _get_external_data(url)
     return result
 
 def get_package_data(name, version):
-    url = ('https://www.red-dove.com/pypi/projects/'
-           '%s/%s/package-%s.json' % (name[0].upper(), name, version))
+    url = '%s/%s/package-%s.json' % (name[0].upper(), name, version)
+    url = urljoin(_external_data_base_url, url)
     return _get_external_data(url)
 
 
diff --git a/pip/_vendor/distlib/version.py b/pip/_vendor/distlib/version.py
index f0e62c4ee50..d3dcfa006d5 100644
--- a/pip/_vendor/distlib/version.py
+++ b/pip/_vendor/distlib/version.py
@@ -1,11 +1,11 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2012-2013 The Python Software Foundation.
+# Copyright (C) 2012-2016 The Python Software Foundation.
 # See LICENSE.txt and CONTRIBUTORS.txt.
 #
 """
-Implementation of a flexible versioning scheme providing support for PEP-386,
-distribute-compatible and semantic versioning.
+Implementation of a flexible versioning scheme providing support for PEP-440,
+setuptools-compatible and semantic versioning.
 """
 
 import logging
@@ -79,7 +79,7 @@ class Matcher(object):
     version_class = None
 
     dist_re = re.compile(r"^(\w[\s\w'.-]*)(\((.*)\))?")
-    comp_re = re.compile(r'^(<=|>=|<|>|!=|==|~=)?\s*([^\s,]+)$')
+    comp_re = re.compile(r'^(<=|>=|<|>|!=|={2,3}|~=)?\s*([^\s,]+)$')
     num_re = re.compile(r'^\d+(\.\d+)*$')
 
     # value is either a callable or the name of a method
@@ -89,6 +89,7 @@ class Matcher(object):
         '<=': lambda v, c, p: v == c or v < c,
         '>=': lambda v, c, p: v == c or v > c,
         '==': lambda v, c, p: v == c,
+        '===': lambda v, c, p: v == c,
         # by default, compatible => >=.
         '~=': lambda v, c, p: v == c or v > c,
         '!=': lambda v, c, p: v != c,
@@ -155,7 +156,7 @@ def match(self, version):
     @property
     def exact_version(self):
         result = None
-        if len(self._parts) == 1 and self._parts[0][0] == '==':
+        if len(self._parts) == 1 and self._parts[0][0] in ('==', '==='):
             result = self._parts[0][1]
         return result
 
@@ -181,25 +182,29 @@ def __str__(self):
         return self._string
 
 
-PEP426_VERSION_RE = re.compile(r'^(\d+(\.\d+)*)((a|b|c|rc)(\d+))?'
+PEP440_VERSION_RE = re.compile(r'^v?(\d+!)?(\d+(\.\d+)*)((a|b|c|rc)(\d+))?'
                                r'(\.(post)(\d+))?(\.(dev)(\d+))?'
-                               r'(-(\d+(\.\d+)?))?$')
+                               r'(\+([a-zA-Z\d]+(\.[a-zA-Z\d]+)?))?$')
 
 
-def _pep426_key(s):
+def _pep_440_key(s):
     s = s.strip()
-    m = PEP426_VERSION_RE.match(s)
+    m = PEP440_VERSION_RE.match(s)
     if not m:
         raise UnsupportedVersionError('Not a valid version: %s' % s)
     groups = m.groups()
-    nums = tuple(int(v) for v in groups[0].split('.'))
+    nums = tuple(int(v) for v in groups[1].split('.'))
     while len(nums) > 1 and nums[-1] == 0:
         nums = nums[:-1]
 
-    pre = groups[3:5]
-    post = groups[6:8]
-    dev = groups[9:11]
-    local = groups[12]
+    if not groups[0]:
+        epoch = 0
+    else:
+        epoch = int(groups[0])
+    pre = groups[4:6]
+    post = groups[7:9]
+    dev = groups[10:12]
+    local = groups[13]
     if pre == (None, None):
         pre = ()
     else:
@@ -215,7 +220,17 @@ def _pep426_key(s):
     if local is None:
         local = ()
     else:
-        local = tuple([int(s) for s in local.split('.')])
+        parts = []
+        for part in local.split('.'):
+            # to ensure that numeric compares as > lexicographic, avoid
+            # comparing them directly, but encode a tuple which ensures
+            # correct sorting
+            if part.isdigit():
+                part = (1, int(part))
+            else:
+                part = (0, part)
+            parts.append(part)
+        local = tuple(parts)
     if not pre:
         # either before pre-release, or final release and after
         if not post and dev:
@@ -230,10 +245,10 @@ def _pep426_key(s):
         dev = ('final',)
 
     #print('%s -> %s' % (s, m.groups()))
-    return nums, pre, post, dev, local
+    return epoch, nums, pre, post, dev, local
 
 
-_normalized_key = _pep426_key
+_normalized_key = _pep_440_key
 
 
 class NormalizedVersion(Version):
@@ -260,9 +275,9 @@ def parse(self, s):
         # clause, since that's needed to ensure that X.Y == X.Y.0 == X.Y.0.0
         # However, PEP 440 prefix matching needs it: for example,
         # (~= 1.4.5.0) matches differently to (~= 1.4.5.0.0).
-        m = PEP426_VERSION_RE.match(s)      # must succeed
+        m = PEP440_VERSION_RE.match(s)      # must succeed
         groups = m.groups()
-        self._release_clause = tuple(int(v) for v in groups[0].split('.'))
+        self._release_clause = tuple(int(v) for v in groups[1].split('.'))
         return result
 
     PREREL_TAGS = set(['a', 'b', 'c', 'rc', 'dev'])
@@ -294,12 +309,13 @@ class NormalizedMatcher(Matcher):
         '<=': '_match_le',
         '>=': '_match_ge',
         '==': '_match_eq',
+        '===': '_match_arbitrary',
         '!=': '_match_ne',
     }
 
     def _adjust_local(self, version, constraint, prefix):
         if prefix:
-            strip_local = '-' not in constraint and version._parts[-1]
+            strip_local = '+' not in constraint and version._parts[-1]
         else:
             # both constraint and version are
             # NormalizedVersion instances.
@@ -307,7 +323,7 @@ def _adjust_local(self, version, constraint, prefix):
             # ensure the version doesn't, either.
             strip_local = not constraint._parts[-1] and version._parts[-1]
         if strip_local:
-            s = version._string.split('-', 1)[0]
+            s = version._string.split('+', 1)[0]
             version = self.version_class(s)
         return version, constraint
 
@@ -343,6 +359,9 @@ def _match_eq(self, version, constraint, prefix):
             result = _match_prefix(version, constraint)
         return result
 
+    def _match_arbitrary(self, version, constraint, prefix):
+        return str(version) == str(constraint)
+
     def _match_ne(self, version, constraint, prefix):
         version, constraint = self._adjust_local(version, constraint, prefix)
         if not prefix:
@@ -357,6 +376,8 @@ def _match_compatible(self, version, constraint, prefix):
             return True
         if version < constraint:
             return False
+#        if not prefix:
+#            return True
         release_clause = constraint._release_clause
         if len(release_clause) > 1:
             release_clause = release_clause[:-1]
diff --git a/pip/_vendor/distlib/w32.exe b/pip/_vendor/distlib/w32.exe
index 09e76354f7b..f27573a1eac 100644
Binary files a/pip/_vendor/distlib/w32.exe and b/pip/_vendor/distlib/w32.exe differ
diff --git a/pip/_vendor/distlib/w64.exe b/pip/_vendor/distlib/w64.exe
index 29e44e1f8a4..fd37d9ee468 100644
Binary files a/pip/_vendor/distlib/w64.exe and b/pip/_vendor/distlib/w64.exe differ
diff --git a/pip/_vendor/distlib/wheel.py b/pip/_vendor/distlib/wheel.py
index d67d4bc5d24..2952b8e0b31 100644
--- a/pip/_vendor/distlib/wheel.py
+++ b/pip/_vendor/distlib/wheel.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2013-2014 Vinay Sajip.
+# Copyright (C) 2013-2016 Vinay Sajip.
 # Licensed to the Python Software Foundation under a contributor agreement.
 # See LICENSE.txt and CONTRIBUTORS.txt.
 #
@@ -74,7 +74,7 @@ def _derive_abi():
 (-(?P\d+[^-]*))?
 -(?P\w+\d+(\.\w+\d+)*)
 -(?P\w+)
--(?P\w+)
+-(?P\w+(\.\w+)*)
 \.whl$
 ''', re.IGNORECASE | re.VERBOSE)
 
@@ -85,6 +85,9 @@ def _derive_abi():
 ''', re.IGNORECASE | re.VERBOSE)
 
 SHEBANG_RE = re.compile(br'\s*#![^\r\n]*')
+SHEBANG_DETAIL_RE = re.compile(br'^(\s*#!("[^"]+"|\S+))\s+(.*)$')
+SHEBANG_PYTHON = b'#!python'
+SHEBANG_PYTHONW = b'#!pythonw'
 
 if os.sep == '/':
     to_posix = lambda o: o
@@ -251,7 +254,20 @@ def info(self):
     def process_shebang(self, data):
         m = SHEBANG_RE.match(data)
         if m:
-            data = b'#!python' + data[m.end():]
+            end = m.end()
+            shebang, data_after_shebang = data[:end], data[end:]
+            # Preserve any arguments after the interpreter
+            if b'pythonw' in shebang.lower():
+                shebang_python = SHEBANG_PYTHONW
+            else:
+                shebang_python = SHEBANG_PYTHON
+            m = SHEBANG_DETAIL_RE.match(shebang)
+            if m:
+                args = b' ' + m.groups()[-1]
+            else:
+                args = b''
+            shebang = shebang_python + args
+            data = shebang + data_after_shebang
         else:
             cr = data.find(b'\r')
             lf = data.find(b'\n')
@@ -262,7 +278,7 @@ def process_shebang(self, data):
                     term = b'\r\n'
                 else:
                     term = b'\r'
-            data = b'#!python' + term + data
+            data = SHEBANG_PYTHON + term + data
         return data
 
     def get_hash(self, data, hash_kind=None):
@@ -277,11 +293,13 @@ def get_hash(self, data, hash_kind=None):
         return hash_kind, result
 
     def write_record(self, records, record_path, base):
+        records = list(records) # make a copy for sorting
+        p = to_posix(os.path.relpath(record_path, base))
+        records.append((p, '', ''))
+        records.sort()
         with CSVWriter(record_path) as writer:
             for row in records:
                 writer.writerow(row)
-            p = to_posix(os.path.relpath(record_path, base))
-            writer.writerow((p, '', ''))
 
     def write_records(self, info, libdir, archive_paths):
         records = []
@@ -383,7 +401,7 @@ def build(self, paths, tags=None, wheel_version=None):
         # Now distinfo. Assumed to be flat, i.e. os.listdir is enough.
         files = os.listdir(distinfo)
         for fn in files:
-            if fn not in ('RECORD', 'INSTALLER', 'SHARED'):
+            if fn not in ('RECORD', 'INSTALLER', 'SHARED', 'WHEEL'):
                 p = fsdecode(os.path.join(distinfo, fn))
                 ap = to_posix(os.path.join(info_dir, fn))
                 archive_paths.append((ap, p))
@@ -587,7 +605,9 @@ def install(self, paths, maker, **kwargs):
                         try:
                             with zf.open(metadata_name) as bwf:
                                 wf = wrapper(bwf)
-                                commands = json.load(wf).get('commands')
+                                commands = json.load(wf).get('extensions')
+                                if commands:
+                                    commands = commands.get('python.commands')
                         except Exception:
                             logger.warning('Unable to read JSON metadata, so '
                                            'cannot generate scripts')
@@ -810,11 +830,11 @@ def update_version(version, path):
                 v = NormalizedVersion(version)
                 i = version.find('-')
                 if i < 0:
-                    updated = '%s-1' % version
+                    updated = '%s+1' % version
                 else:
                     parts = [int(s) for s in version[i + 1:].split('.')]
                     parts[-1] += 1
-                    updated = '%s-%s' % (version[:i],
+                    updated = '%s+%s' % (version[:i],
                                          '.'.join(str(i) for i in parts))
             except UnsupportedVersionError:
                 logger.debug('Cannot update non-compliant (PEP-440) '
diff --git a/pip/_vendor/html5lib/__init__.py b/pip/_vendor/html5lib/__init__.py
index ff5c775513e..9484fdc9b7b 100644
--- a/pip/_vendor/html5lib/__init__.py
+++ b/pip/_vendor/html5lib/__init__.py
@@ -20,4 +20,6 @@
 
 __all__ = ["HTMLParser", "parse", "parseFragment", "getTreeBuilder",
            "getTreeWalker", "serialize"]
-__version__ = "1.0b3"
+
+# this has to be at the top level, see how setup.py parses this
+__version__ = "1.0b8"
diff --git a/pip/_vendor/html5lib/constants.py b/pip/_vendor/html5lib/constants.py
index e7089846d59..d938e0ae666 100644
--- a/pip/_vendor/html5lib/constants.py
+++ b/pip/_vendor/html5lib/constants.py
@@ -1,292 +1,290 @@
 from __future__ import absolute_import, division, unicode_literals
 
 import string
-import gettext
-_ = gettext.gettext
 
 EOF = None
 
 E = {
     "null-character":
-        _("Null character in input stream, replaced with U+FFFD."),
+        "Null character in input stream, replaced with U+FFFD.",
     "invalid-codepoint":
-        _("Invalid codepoint in stream."),
+        "Invalid codepoint in stream.",
     "incorrectly-placed-solidus":
-        _("Solidus (/) incorrectly placed in tag."),
+        "Solidus (/) incorrectly placed in tag.",
     "incorrect-cr-newline-entity":
-        _("Incorrect CR newline entity, replaced with LF."),
+        "Incorrect CR newline entity, replaced with LF.",
     "illegal-windows-1252-entity":
-        _("Entity used with illegal number (windows-1252 reference)."),
+        "Entity used with illegal number (windows-1252 reference).",
     "cant-convert-numeric-entity":
-        _("Numeric entity couldn't be converted to character "
-          "(codepoint U+%(charAsInt)08x)."),
+        "Numeric entity couldn't be converted to character "
+        "(codepoint U+%(charAsInt)08x).",
     "illegal-codepoint-for-numeric-entity":
-        _("Numeric entity represents an illegal codepoint: "
-          "U+%(charAsInt)08x."),
+        "Numeric entity represents an illegal codepoint: "
+        "U+%(charAsInt)08x.",
     "numeric-entity-without-semicolon":
-        _("Numeric entity didn't end with ';'."),
+        "Numeric entity didn't end with ';'.",
     "expected-numeric-entity-but-got-eof":
-        _("Numeric entity expected. Got end of file instead."),
+        "Numeric entity expected. Got end of file instead.",
     "expected-numeric-entity":
-        _("Numeric entity expected but none found."),
+        "Numeric entity expected but none found.",
     "named-entity-without-semicolon":
-        _("Named entity didn't end with ';'."),
+        "Named entity didn't end with ';'.",
     "expected-named-entity":
-        _("Named entity expected. Got none."),
+        "Named entity expected. Got none.",
     "attributes-in-end-tag":
-        _("End tag contains unexpected attributes."),
+        "End tag contains unexpected attributes.",
     'self-closing-flag-on-end-tag':
-        _("End tag contains unexpected self-closing flag."),
+        "End tag contains unexpected self-closing flag.",
     "expected-tag-name-but-got-right-bracket":
-        _("Expected tag name. Got '>' instead."),
+        "Expected tag name. Got '>' instead.",
     "expected-tag-name-but-got-question-mark":
-        _("Expected tag name. Got '?' instead. (HTML doesn't "
-          "support processing instructions.)"),
+        "Expected tag name. Got '?' instead. (HTML doesn't "
+        "support processing instructions.)",
     "expected-tag-name":
-        _("Expected tag name. Got something else instead"),
+        "Expected tag name. Got something else instead",
     "expected-closing-tag-but-got-right-bracket":
-        _("Expected closing tag. Got '>' instead. Ignoring ''."),
+        "Expected closing tag. Got '>' instead. Ignoring ''.",
     "expected-closing-tag-but-got-eof":
-        _("Expected closing tag. Unexpected end of file."),
+        "Expected closing tag. Unexpected end of file.",
     "expected-closing-tag-but-got-char":
-        _("Expected closing tag. Unexpected character '%(data)s' found."),
+        "Expected closing tag. Unexpected character '%(data)s' found.",
     "eof-in-tag-name":
-        _("Unexpected end of file in the tag name."),
+        "Unexpected end of file in the tag name.",
     "expected-attribute-name-but-got-eof":
-        _("Unexpected end of file. Expected attribute name instead."),
+        "Unexpected end of file. Expected attribute name instead.",
     "eof-in-attribute-name":
-        _("Unexpected end of file in attribute name."),
+        "Unexpected end of file in attribute name.",
     "invalid-character-in-attribute-name":
-        _("Invalid character in attribute name"),
+        "Invalid character in attribute name",
     "duplicate-attribute":
-        _("Dropped duplicate attribute on tag."),
+        "Dropped duplicate attribute on tag.",
     "expected-end-of-tag-name-but-got-eof":
-        _("Unexpected end of file. Expected = or end of tag."),
+        "Unexpected end of file. Expected = or end of tag.",
     "expected-attribute-value-but-got-eof":
-        _("Unexpected end of file. Expected attribute value."),
+        "Unexpected end of file. Expected attribute value.",
     "expected-attribute-value-but-got-right-bracket":
-        _("Expected attribute value. Got '>' instead."),
+        "Expected attribute value. Got '>' instead.",
     'equals-in-unquoted-attribute-value':
-        _("Unexpected = in unquoted attribute"),
+        "Unexpected = in unquoted attribute",
     'unexpected-character-in-unquoted-attribute-value':
-        _("Unexpected character in unquoted attribute"),
+        "Unexpected character in unquoted attribute",
     "invalid-character-after-attribute-name":
-        _("Unexpected character after attribute name."),
+        "Unexpected character after attribute name.",
     "unexpected-character-after-attribute-value":
-        _("Unexpected character after attribute value."),
+        "Unexpected character after attribute value.",
     "eof-in-attribute-value-double-quote":
-        _("Unexpected end of file in attribute value (\")."),
+        "Unexpected end of file in attribute value (\").",
     "eof-in-attribute-value-single-quote":
-        _("Unexpected end of file in attribute value (')."),
+        "Unexpected end of file in attribute value (').",
     "eof-in-attribute-value-no-quotes":
-        _("Unexpected end of file in attribute value."),
+        "Unexpected end of file in attribute value.",
     "unexpected-EOF-after-solidus-in-tag":
-        _("Unexpected end of file in tag. Expected >"),
+        "Unexpected end of file in tag. Expected >",
     "unexpected-character-after-solidus-in-tag":
-        _("Unexpected character after / in tag. Expected >"),
+        "Unexpected character after / in tag. Expected >",
     "expected-dashes-or-doctype":
-        _("Expected '--' or 'DOCTYPE'. Not found."),
+        "Expected '--' or 'DOCTYPE'. Not found.",
     "unexpected-bang-after-double-dash-in-comment":
-        _("Unexpected ! after -- in comment"),
+        "Unexpected ! after -- in comment",
     "unexpected-space-after-double-dash-in-comment":
-        _("Unexpected space after -- in comment"),
+        "Unexpected space after -- in comment",
     "incorrect-comment":
-        _("Incorrect comment."),
+        "Incorrect comment.",
     "eof-in-comment":
-        _("Unexpected end of file in comment."),
+        "Unexpected end of file in comment.",
     "eof-in-comment-end-dash":
-        _("Unexpected end of file in comment (-)"),
+        "Unexpected end of file in comment (-)",
     "unexpected-dash-after-double-dash-in-comment":
-        _("Unexpected '-' after '--' found in comment."),
+        "Unexpected '-' after '--' found in comment.",
     "eof-in-comment-double-dash":
-        _("Unexpected end of file in comment (--)."),
+        "Unexpected end of file in comment (--).",
     "eof-in-comment-end-space-state":
-        _("Unexpected end of file in comment."),
+        "Unexpected end of file in comment.",
     "eof-in-comment-end-bang-state":
-        _("Unexpected end of file in comment."),
+        "Unexpected end of file in comment.",
     "unexpected-char-in-comment":
-        _("Unexpected character in comment found."),
+        "Unexpected character in comment found.",
     "need-space-after-doctype":
-        _("No space after literal string 'DOCTYPE'."),
+        "No space after literal string 'DOCTYPE'.",
     "expected-doctype-name-but-got-right-bracket":
-        _("Unexpected > character. Expected DOCTYPE name."),
+        "Unexpected > character. Expected DOCTYPE name.",
     "expected-doctype-name-but-got-eof":
-        _("Unexpected end of file. Expected DOCTYPE name."),
+        "Unexpected end of file. Expected DOCTYPE name.",
     "eof-in-doctype-name":
-        _("Unexpected end of file in DOCTYPE name."),
+        "Unexpected end of file in DOCTYPE name.",
     "eof-in-doctype":
-        _("Unexpected end of file in DOCTYPE."),
+        "Unexpected end of file in DOCTYPE.",
     "expected-space-or-right-bracket-in-doctype":
-        _("Expected space or '>'. Got '%(data)s'"),
+        "Expected space or '>'. Got '%(data)s'",
     "unexpected-end-of-doctype":
-        _("Unexpected end of DOCTYPE."),
+        "Unexpected end of DOCTYPE.",
     "unexpected-char-in-doctype":
-        _("Unexpected character in DOCTYPE."),
+        "Unexpected character in DOCTYPE.",
     "eof-in-innerhtml":
-        _("XXX innerHTML EOF"),
+        "XXX innerHTML EOF",
     "unexpected-doctype":
-        _("Unexpected DOCTYPE. Ignored."),
+        "Unexpected DOCTYPE. Ignored.",
     "non-html-root":
-        _("html needs to be the first start tag."),
+        "html needs to be the first start tag.",
     "expected-doctype-but-got-eof":
-        _("Unexpected End of file. Expected DOCTYPE."),
+        "Unexpected End of file. Expected DOCTYPE.",
     "unknown-doctype":
-        _("Erroneous DOCTYPE."),
+        "Erroneous DOCTYPE.",
     "expected-doctype-but-got-chars":
-        _("Unexpected non-space characters. Expected DOCTYPE."),
+        "Unexpected non-space characters. Expected DOCTYPE.",
     "expected-doctype-but-got-start-tag":
-        _("Unexpected start tag (%(name)s). Expected DOCTYPE."),
+        "Unexpected start tag (%(name)s). Expected DOCTYPE.",
     "expected-doctype-but-got-end-tag":
-        _("Unexpected end tag (%(name)s). Expected DOCTYPE."),
+        "Unexpected end tag (%(name)s). Expected DOCTYPE.",
     "end-tag-after-implied-root":
-        _("Unexpected end tag (%(name)s) after the (implied) root element."),
+        "Unexpected end tag (%(name)s) after the (implied) root element.",
     "expected-named-closing-tag-but-got-eof":
-        _("Unexpected end of file. Expected end tag (%(name)s)."),
+        "Unexpected end of file. Expected end tag (%(name)s).",
     "two-heads-are-not-better-than-one":
-        _("Unexpected start tag head in existing head. Ignored."),
+        "Unexpected start tag head in existing head. Ignored.",
     "unexpected-end-tag":
-        _("Unexpected end tag (%(name)s). Ignored."),
+        "Unexpected end tag (%(name)s). Ignored.",
     "unexpected-start-tag-out-of-my-head":
-        _("Unexpected start tag (%(name)s) that can be in head. Moved."),
+        "Unexpected start tag (%(name)s) that can be in head. Moved.",
     "unexpected-start-tag":
-        _("Unexpected start tag (%(name)s)."),
+        "Unexpected start tag (%(name)s).",
     "missing-end-tag":
-        _("Missing end tag (%(name)s)."),
+        "Missing end tag (%(name)s).",
     "missing-end-tags":
-        _("Missing end tags (%(name)s)."),
+        "Missing end tags (%(name)s).",
     "unexpected-start-tag-implies-end-tag":
-        _("Unexpected start tag (%(startName)s) "
-          "implies end tag (%(endName)s)."),
+        "Unexpected start tag (%(startName)s) "
+        "implies end tag (%(endName)s).",
     "unexpected-start-tag-treated-as":
-        _("Unexpected start tag (%(originalName)s). Treated as %(newName)s."),
+        "Unexpected start tag (%(originalName)s). Treated as %(newName)s.",
     "deprecated-tag":
-        _("Unexpected start tag %(name)s. Don't use it!"),
+        "Unexpected start tag %(name)s. Don't use it!",
     "unexpected-start-tag-ignored":
-        _("Unexpected start tag %(name)s. Ignored."),
+        "Unexpected start tag %(name)s. Ignored.",
     "expected-one-end-tag-but-got-another":
-        _("Unexpected end tag (%(gotName)s). "
-          "Missing end tag (%(expectedName)s)."),
+        "Unexpected end tag (%(gotName)s). "
+        "Missing end tag (%(expectedName)s).",
     "end-tag-too-early":
-        _("End tag (%(name)s) seen too early. Expected other end tag."),
+        "End tag (%(name)s) seen too early. Expected other end tag.",
     "end-tag-too-early-named":
-        _("Unexpected end tag (%(gotName)s). Expected end tag (%(expectedName)s)."),
+        "Unexpected end tag (%(gotName)s). Expected end tag (%(expectedName)s).",
     "end-tag-too-early-ignored":
-        _("End tag (%(name)s) seen too early. Ignored."),
+        "End tag (%(name)s) seen too early. Ignored.",
     "adoption-agency-1.1":
-        _("End tag (%(name)s) violates step 1, "
-          "paragraph 1 of the adoption agency algorithm."),
+        "End tag (%(name)s) violates step 1, "
+        "paragraph 1 of the adoption agency algorithm.",
     "adoption-agency-1.2":
-        _("End tag (%(name)s) violates step 1, "
-          "paragraph 2 of the adoption agency algorithm."),
+        "End tag (%(name)s) violates step 1, "
+        "paragraph 2 of the adoption agency algorithm.",
     "adoption-agency-1.3":
-        _("End tag (%(name)s) violates step 1, "
-          "paragraph 3 of the adoption agency algorithm."),
+        "End tag (%(name)s) violates step 1, "
+        "paragraph 3 of the adoption agency algorithm.",
     "adoption-agency-4.4":
-        _("End tag (%(name)s) violates step 4, "
-          "paragraph 4 of the adoption agency algorithm."),
+        "End tag (%(name)s) violates step 4, "
+        "paragraph 4 of the adoption agency algorithm.",
     "unexpected-end-tag-treated-as":
-        _("Unexpected end tag (%(originalName)s). Treated as %(newName)s."),
+        "Unexpected end tag (%(originalName)s). Treated as %(newName)s.",
     "no-end-tag":
-        _("This element (%(name)s) has no end tag."),
+        "This element (%(name)s) has no end tag.",
     "unexpected-implied-end-tag-in-table":
-        _("Unexpected implied end tag (%(name)s) in the table phase."),
+        "Unexpected implied end tag (%(name)s) in the table phase.",
     "unexpected-implied-end-tag-in-table-body":
-        _("Unexpected implied end tag (%(name)s) in the table body phase."),
+        "Unexpected implied end tag (%(name)s) in the table body phase.",
     "unexpected-char-implies-table-voodoo":
-        _("Unexpected non-space characters in "
-          "table context caused voodoo mode."),
+        "Unexpected non-space characters in "
+        "table context caused voodoo mode.",
     "unexpected-hidden-input-in-table":
-        _("Unexpected input with type hidden in table context."),
+        "Unexpected input with type hidden in table context.",
     "unexpected-form-in-table":
-        _("Unexpected form in table context."),
+        "Unexpected form in table context.",
     "unexpected-start-tag-implies-table-voodoo":
-        _("Unexpected start tag (%(name)s) in "
-          "table context caused voodoo mode."),
+        "Unexpected start tag (%(name)s) in "
+        "table context caused voodoo mode.",
     "unexpected-end-tag-implies-table-voodoo":
-        _("Unexpected end tag (%(name)s) in "
-          "table context caused voodoo mode."),
+        "Unexpected end tag (%(name)s) in "
+        "table context caused voodoo mode.",
     "unexpected-cell-in-table-body":
-        _("Unexpected table cell start tag (%(name)s) "
-          "in the table body phase."),
+        "Unexpected table cell start tag (%(name)s) "
+        "in the table body phase.",
     "unexpected-cell-end-tag":
-        _("Got table cell end tag (%(name)s) "
-          "while required end tags are missing."),
+        "Got table cell end tag (%(name)s) "
+        "while required end tags are missing.",
     "unexpected-end-tag-in-table-body":
-        _("Unexpected end tag (%(name)s) in the table body phase. Ignored."),
+        "Unexpected end tag (%(name)s) in the table body phase. Ignored.",
     "unexpected-implied-end-tag-in-table-row":
-        _("Unexpected implied end tag (%(name)s) in the table row phase."),
+        "Unexpected implied end tag (%(name)s) in the table row phase.",
     "unexpected-end-tag-in-table-row":
-        _("Unexpected end tag (%(name)s) in the table row phase. Ignored."),
+        "Unexpected end tag (%(name)s) in the table row phase. Ignored.",
     "unexpected-select-in-select":
-        _("Unexpected select start tag in the select phase "
-          "treated as select end tag."),
+        "Unexpected select start tag in the select phase "
+        "treated as select end tag.",
     "unexpected-input-in-select":
-        _("Unexpected input start tag in the select phase."),
+        "Unexpected input start tag in the select phase.",
     "unexpected-start-tag-in-select":
-        _("Unexpected start tag token (%(name)s in the select phase. "
-          "Ignored."),
+        "Unexpected start tag token (%(name)s in the select phase. "
+        "Ignored.",
     "unexpected-end-tag-in-select":
-        _("Unexpected end tag (%(name)s) in the select phase. Ignored."),
+        "Unexpected end tag (%(name)s) in the select phase. Ignored.",
     "unexpected-table-element-start-tag-in-select-in-table":
-        _("Unexpected table element start tag (%(name)s) in the select in table phase."),
+        "Unexpected table element start tag (%(name)s) in the select in table phase.",
     "unexpected-table-element-end-tag-in-select-in-table":
-        _("Unexpected table element end tag (%(name)s) in the select in table phase."),
+        "Unexpected table element end tag (%(name)s) in the select in table phase.",
     "unexpected-char-after-body":
-        _("Unexpected non-space characters in the after body phase."),
+        "Unexpected non-space characters in the after body phase.",
     "unexpected-start-tag-after-body":
-        _("Unexpected start tag token (%(name)s)"
-          " in the after body phase."),
+        "Unexpected start tag token (%(name)s)"
+        " in the after body phase.",
     "unexpected-end-tag-after-body":
-        _("Unexpected end tag token (%(name)s)"
-          " in the after body phase."),
+        "Unexpected end tag token (%(name)s)"
+        " in the after body phase.",
     "unexpected-char-in-frameset":
-        _("Unexpected characters in the frameset phase. Characters ignored."),
+        "Unexpected characters in the frameset phase. Characters ignored.",
     "unexpected-start-tag-in-frameset":
-        _("Unexpected start tag token (%(name)s)"
-          " in the frameset phase. Ignored."),
+        "Unexpected start tag token (%(name)s)"
+        " in the frameset phase. Ignored.",
     "unexpected-frameset-in-frameset-innerhtml":
-        _("Unexpected end tag token (frameset) "
-          "in the frameset phase (innerHTML)."),
+        "Unexpected end tag token (frameset) "
+        "in the frameset phase (innerHTML).",
     "unexpected-end-tag-in-frameset":
-        _("Unexpected end tag token (%(name)s)"
-          " in the frameset phase. Ignored."),
+        "Unexpected end tag token (%(name)s)"
+        " in the frameset phase. Ignored.",
     "unexpected-char-after-frameset":
-        _("Unexpected non-space characters in the "
-          "after frameset phase. Ignored."),
+        "Unexpected non-space characters in the "
+        "after frameset phase. Ignored.",
     "unexpected-start-tag-after-frameset":
-        _("Unexpected start tag (%(name)s)"
-          " in the after frameset phase. Ignored."),
+        "Unexpected start tag (%(name)s)"
+        " in the after frameset phase. Ignored.",
     "unexpected-end-tag-after-frameset":
-        _("Unexpected end tag (%(name)s)"
-          " in the after frameset phase. Ignored."),
+        "Unexpected end tag (%(name)s)"
+        " in the after frameset phase. Ignored.",
     "unexpected-end-tag-after-body-innerhtml":
-        _("Unexpected end tag after body(innerHtml)"),
+        "Unexpected end tag after body(innerHtml)",
     "expected-eof-but-got-char":
-        _("Unexpected non-space characters. Expected end of file."),
+        "Unexpected non-space characters. Expected end of file.",
     "expected-eof-but-got-start-tag":
-        _("Unexpected start tag (%(name)s)"
-          ". Expected end of file."),
+        "Unexpected start tag (%(name)s)"
+        ". Expected end of file.",
     "expected-eof-but-got-end-tag":
-        _("Unexpected end tag (%(name)s)"
-          ". Expected end of file."),
+        "Unexpected end tag (%(name)s)"
+        ". Expected end of file.",
     "eof-in-table":
-        _("Unexpected end of file. Expected table content."),
+        "Unexpected end of file. Expected table content.",
     "eof-in-select":
-        _("Unexpected end of file. Expected select content."),
+        "Unexpected end of file. Expected select content.",
     "eof-in-frameset":
-        _("Unexpected end of file. Expected frameset content."),
+        "Unexpected end of file. Expected frameset content.",
     "eof-in-script-in-script":
-        _("Unexpected end of file. Expected script content."),
+        "Unexpected end of file. Expected script content.",
     "eof-in-foreign-lands":
-        _("Unexpected end of file. Expected foreign content"),
+        "Unexpected end of file. Expected foreign content",
     "non-void-element-with-trailing-solidus":
-        _("Trailing solidus not allowed on element %(name)s"),
+        "Trailing solidus not allowed on element %(name)s",
     "unexpected-html-element-in-foreign-content":
-        _("Element %(name)s not allowed in a non-html context"),
+        "Element %(name)s not allowed in a non-html context",
     "unexpected-end-tag-before-html":
-        _("Unexpected end tag (%(name)s) before html."),
+        "Unexpected end tag (%(name)s) before html.",
     "XXX-undefined-error":
-        _("Undefined error (this sucks and should be fixed)"),
+        "Undefined error (this sucks and should be fixed)",
 }
 
 namespaces = {
@@ -298,7 +296,7 @@
     "xmlns": "http://www.w3.org/2000/xmlns/"
 }
 
-scopingElements = frozenset((
+scopingElements = frozenset([
     (namespaces["html"], "applet"),
     (namespaces["html"], "caption"),
     (namespaces["html"], "html"),
@@ -316,9 +314,9 @@
     (namespaces["svg"], "foreignObject"),
     (namespaces["svg"], "desc"),
     (namespaces["svg"], "title"),
-))
+])
 
-formattingElements = frozenset((
+formattingElements = frozenset([
     (namespaces["html"], "a"),
     (namespaces["html"], "b"),
     (namespaces["html"], "big"),
@@ -333,9 +331,9 @@
     (namespaces["html"], "strong"),
     (namespaces["html"], "tt"),
     (namespaces["html"], "u")
-))
+])
 
-specialElements = frozenset((
+specialElements = frozenset([
     (namespaces["html"], "address"),
     (namespaces["html"], "applet"),
     (namespaces["html"], "area"),
@@ -416,22 +414,22 @@
     (namespaces["html"], "wbr"),
     (namespaces["html"], "xmp"),
     (namespaces["svg"], "foreignObject")
-))
+])
 
-htmlIntegrationPointElements = frozenset((
+htmlIntegrationPointElements = frozenset([
     (namespaces["mathml"], "annotaion-xml"),
     (namespaces["svg"], "foreignObject"),
     (namespaces["svg"], "desc"),
     (namespaces["svg"], "title")
-))
+])
 
-mathmlTextIntegrationPointElements = frozenset((
+mathmlTextIntegrationPointElements = frozenset([
     (namespaces["mathml"], "mi"),
     (namespaces["mathml"], "mo"),
     (namespaces["mathml"], "mn"),
     (namespaces["mathml"], "ms"),
     (namespaces["mathml"], "mtext")
-))
+])
 
 adjustForeignAttributes = {
     "xlink:actuate": ("xlink", "actuate", namespaces["xlink"]),
@@ -451,21 +449,21 @@
 unadjustForeignAttributes = dict([((ns, local), qname) for qname, (prefix, local, ns) in
                                   adjustForeignAttributes.items()])
 
-spaceCharacters = frozenset((
+spaceCharacters = frozenset([
     "\t",
     "\n",
     "\u000C",
     " ",
     "\r"
-))
+])
 
-tableInsertModeElements = frozenset((
+tableInsertModeElements = frozenset([
     "table",
     "tbody",
     "tfoot",
     "thead",
     "tr"
-))
+])
 
 asciiLowercase = frozenset(string.ascii_lowercase)
 asciiUppercase = frozenset(string.ascii_uppercase)
@@ -486,7 +484,7 @@
     "h6"
 )
 
-voidElements = frozenset((
+voidElements = frozenset([
     "base",
     "command",
     "event-source",
@@ -502,11 +500,11 @@
     "input",
     "source",
     "track"
-))
+])
 
-cdataElements = frozenset(('title', 'textarea'))
+cdataElements = frozenset(['title', 'textarea'])
 
-rcdataElements = frozenset((
+rcdataElements = frozenset([
     'style',
     'script',
     'xmp',
@@ -514,27 +512,27 @@
     'noembed',
     'noframes',
     'noscript'
-))
+])
 
 booleanAttributes = {
-    "": frozenset(("irrelevant",)),
-    "style": frozenset(("scoped",)),
-    "img": frozenset(("ismap",)),
-    "audio": frozenset(("autoplay", "controls")),
-    "video": frozenset(("autoplay", "controls")),
-    "script": frozenset(("defer", "async")),
-    "details": frozenset(("open",)),
-    "datagrid": frozenset(("multiple", "disabled")),
-    "command": frozenset(("hidden", "disabled", "checked", "default")),
-    "hr": frozenset(("noshade")),
-    "menu": frozenset(("autosubmit",)),
-    "fieldset": frozenset(("disabled", "readonly")),
-    "option": frozenset(("disabled", "readonly", "selected")),
-    "optgroup": frozenset(("disabled", "readonly")),
-    "button": frozenset(("disabled", "autofocus")),
-    "input": frozenset(("disabled", "readonly", "required", "autofocus", "checked", "ismap")),
-    "select": frozenset(("disabled", "readonly", "autofocus", "multiple")),
-    "output": frozenset(("disabled", "readonly")),
+    "": frozenset(["irrelevant"]),
+    "style": frozenset(["scoped"]),
+    "img": frozenset(["ismap"]),
+    "audio": frozenset(["autoplay", "controls"]),
+    "video": frozenset(["autoplay", "controls"]),
+    "script": frozenset(["defer", "async"]),
+    "details": frozenset(["open"]),
+    "datagrid": frozenset(["multiple", "disabled"]),
+    "command": frozenset(["hidden", "disabled", "checked", "default"]),
+    "hr": frozenset(["noshade"]),
+    "menu": frozenset(["autosubmit"]),
+    "fieldset": frozenset(["disabled", "readonly"]),
+    "option": frozenset(["disabled", "readonly", "selected"]),
+    "optgroup": frozenset(["disabled", "readonly"]),
+    "button": frozenset(["disabled", "autofocus"]),
+    "input": frozenset(["disabled", "readonly", "required", "autofocus", "checked", "ismap"]),
+    "select": frozenset(["disabled", "readonly", "autofocus", "multiple"]),
+    "output": frozenset(["disabled", "readonly"]),
 }
 
 # entitiesWindows1252 has to be _ordered_ and needs to have an index. It
@@ -574,7 +572,7 @@
     376     # 0x9F  0x0178  LATIN CAPITAL LETTER Y WITH DIAERESIS
 )
 
-xmlEntities = frozenset(('lt;', 'gt;', 'amp;', 'apos;', 'quot;'))
+xmlEntities = frozenset(['lt;', 'gt;', 'amp;', 'apos;', 'quot;'])
 
 entities = {
     "AElig": "\xc6",
@@ -3088,8 +3086,8 @@
     "ParseError": 7
 }
 
-tagTokenTypes = frozenset((tokenTypes["StartTag"], tokenTypes["EndTag"],
-                           tokenTypes["EmptyTag"]))
+tagTokenTypes = frozenset([tokenTypes["StartTag"], tokenTypes["EndTag"],
+                           tokenTypes["EmptyTag"]])
 
 
 prefixes = dict([(v, k) for k, v in namespaces.items()])
diff --git a/pip/_vendor/html5lib/filters/lint.py b/pip/_vendor/html5lib/filters/lint.py
index 7cc99a4ba7c..8884696dc51 100644
--- a/pip/_vendor/html5lib/filters/lint.py
+++ b/pip/_vendor/html5lib/filters/lint.py
@@ -1,8 +1,5 @@
 from __future__ import absolute_import, division, unicode_literals
 
-from gettext import gettext
-_ = gettext
-
 from . import _base
 from ..constants import cdataElements, rcdataElements, voidElements
 
@@ -23,24 +20,24 @@ def __iter__(self):
             if type in ("StartTag", "EmptyTag"):
                 name = token["name"]
                 if contentModelFlag != "PCDATA":
-                    raise LintError(_("StartTag not in PCDATA content model flag: %(tag)s") % {"tag": name})
+                    raise LintError("StartTag not in PCDATA content model flag: %(tag)s" % {"tag": name})
                 if not isinstance(name, str):
-                    raise LintError(_("Tag name is not a string: %(tag)r") % {"tag": name})
+                    raise LintError("Tag name is not a string: %(tag)r" % {"tag": name})
                 if not name:
-                    raise LintError(_("Empty tag name"))
+                    raise LintError("Empty tag name")
                 if type == "StartTag" and name in voidElements:
-                    raise LintError(_("Void element reported as StartTag token: %(tag)s") % {"tag": name})
+                    raise LintError("Void element reported as StartTag token: %(tag)s" % {"tag": name})
                 elif type == "EmptyTag" and name not in voidElements:
-                    raise LintError(_("Non-void element reported as EmptyTag token: %(tag)s") % {"tag": token["name"]})
+                    raise LintError("Non-void element reported as EmptyTag token: %(tag)s" % {"tag": token["name"]})
                 if type == "StartTag":
                     open_elements.append(name)
                 for name, value in token["data"]:
                     if not isinstance(name, str):
-                        raise LintError(_("Attribute name is not a string: %(name)r") % {"name": name})
+                        raise LintError("Attribute name is not a string: %(name)r" % {"name": name})
                     if not name:
-                        raise LintError(_("Empty attribute name"))
+                        raise LintError("Empty attribute name")
                     if not isinstance(value, str):
-                        raise LintError(_("Attribute value is not a string: %(value)r") % {"value": value})
+                        raise LintError("Attribute value is not a string: %(value)r" % {"value": value})
                 if name in cdataElements:
                     contentModelFlag = "CDATA"
                 elif name in rcdataElements:
@@ -51,43 +48,43 @@ def __iter__(self):
             elif type == "EndTag":
                 name = token["name"]
                 if not isinstance(name, str):
-                    raise LintError(_("Tag name is not a string: %(tag)r") % {"tag": name})
+                    raise LintError("Tag name is not a string: %(tag)r" % {"tag": name})
                 if not name:
-                    raise LintError(_("Empty tag name"))
+                    raise LintError("Empty tag name")
                 if name in voidElements:
-                    raise LintError(_("Void element reported as EndTag token: %(tag)s") % {"tag": name})
+                    raise LintError("Void element reported as EndTag token: %(tag)s" % {"tag": name})
                 start_name = open_elements.pop()
                 if start_name != name:
-                    raise LintError(_("EndTag (%(end)s) does not match StartTag (%(start)s)") % {"end": name, "start": start_name})
+                    raise LintError("EndTag (%(end)s) does not match StartTag (%(start)s)" % {"end": name, "start": start_name})
                 contentModelFlag = "PCDATA"
 
             elif type == "Comment":
                 if contentModelFlag != "PCDATA":
-                    raise LintError(_("Comment not in PCDATA content model flag"))
+                    raise LintError("Comment not in PCDATA content model flag")
 
             elif type in ("Characters", "SpaceCharacters"):
                 data = token["data"]
                 if not isinstance(data, str):
-                    raise LintError(_("Attribute name is not a string: %(name)r") % {"name": data})
+                    raise LintError("Attribute name is not a string: %(name)r" % {"name": data})
                 if not data:
-                    raise LintError(_("%(type)s token with empty data") % {"type": type})
+                    raise LintError("%(type)s token with empty data" % {"type": type})
                 if type == "SpaceCharacters":
                     data = data.strip(spaceCharacters)
                     if data:
-                        raise LintError(_("Non-space character(s) found in SpaceCharacters token: %(token)r") % {"token": data})
+                        raise LintError("Non-space character(s) found in SpaceCharacters token: %(token)r" % {"token": data})
 
             elif type == "Doctype":
                 name = token["name"]
                 if contentModelFlag != "PCDATA":
-                    raise LintError(_("Doctype not in PCDATA content model flag: %(name)s") % {"name": name})
+                    raise LintError("Doctype not in PCDATA content model flag: %(name)s" % {"name": name})
                 if not isinstance(name, str):
-                    raise LintError(_("Tag name is not a string: %(tag)r") % {"tag": name})
+                    raise LintError("Tag name is not a string: %(tag)r" % {"tag": name})
                 # XXX: what to do with token["data"] ?
 
             elif type in ("ParseError", "SerializeError"):
                 pass
 
             else:
-                raise LintError(_("Unknown token type: %(type)s") % {"type": type})
+                raise LintError("Unknown token type: %(type)s" % {"type": type})
 
             yield token
diff --git a/pip/_vendor/html5lib/html5parser.py b/pip/_vendor/html5lib/html5parser.py
index b28f46f2a72..40f3d093e8a 100644
--- a/pip/_vendor/html5lib/html5parser.py
+++ b/pip/_vendor/html5lib/html5parser.py
@@ -18,6 +18,7 @@
 from .constants import tokenTypes, ReparseException, namespaces
 from .constants import htmlIntegrationPointElements, mathmlTextIntegrationPointElements
 from .constants import adjustForeignAttributes as adjustForeignAttributesMap
+from .constants import E
 
 
 def parse(doc, treebuilder="etree", encoding=None,
@@ -129,6 +130,17 @@ def reset(self):
 
         self.framesetOK = True
 
+    @property
+    def documentEncoding(self):
+        """The name of the character encoding
+        that was used to decode the input stream,
+        or :obj:`None` if that is not determined yet.
+
+        """
+        if not hasattr(self, 'tokenizer'):
+            return None
+        return self.tokenizer.stream.charEncoding[0]
+
     def isHTMLIntegrationPoint(self, element):
         if (element.name == "annotation-xml" and
                 element.namespace == namespaces["mathml"]):
@@ -245,7 +257,7 @@ def parseError(self, errorcode="XXX-undefined-error", datavars={}):
         # XXX The idea is to make errorcode mandatory.
         self.errors.append((self.tokenizer.stream.position(), errorcode, datavars))
         if self.strict:
-            raise ParseError
+            raise ParseError(E[errorcode] % datavars)
 
     def normalizeToken(self, token):
         """ HTML5 specific normalizations to the token stream """
@@ -868,7 +880,7 @@ def __init__(self, parser, tree):
             self.startTagHandler = utils.MethodDispatcher([
                 ("html", self.startTagHtml),
                 (("base", "basefont", "bgsound", "command", "link", "meta",
-                  "noframes", "script", "style", "title"),
+                  "script", "style", "title"),
                  self.startTagProcessInHead),
                 ("body", self.startTagBody),
                 ("frameset", self.startTagFrameset),
@@ -1205,8 +1217,7 @@ def startTagIsIndex(self, token):
             attributes["name"] = "isindex"
             self.processStartTag(impliedTagToken("input", "StartTag",
                                                  attributes=attributes,
-                                                 selfClosing=
-                                                 token["selfClosing"]))
+                                                 selfClosing=token["selfClosing"]))
             self.processEndTag(impliedTagToken("label"))
             self.processStartTag(impliedTagToken("hr", "StartTag"))
             self.processEndTag(impliedTagToken("form"))
diff --git a/pip/_vendor/html5lib/inputstream.py b/pip/_vendor/html5lib/inputstream.py
index f3dfd7f3d65..ac0c5e53a96 100644
--- a/pip/_vendor/html5lib/inputstream.py
+++ b/pip/_vendor/html5lib/inputstream.py
@@ -1,5 +1,6 @@
 from __future__ import absolute_import, division, unicode_literals
 from pip._vendor.six import text_type
+from pip._vendor.six.moves import http_client
 
 import codecs
 import re
@@ -27,7 +28,18 @@ class BufferedIOBase(object):
 asciiUppercaseBytes = frozenset([item.encode("ascii") for item in asciiUppercase])
 spacesAngleBrackets = spaceCharactersBytes | frozenset([b">", b"<"])
 
-invalid_unicode_re = re.compile("[\u0001-\u0008\u000B\u000E-\u001F\u007F-\u009F\uD800-\uDFFF\uFDD0-\uFDEF\uFFFE\uFFFF\U0001FFFE\U0001FFFF\U0002FFFE\U0002FFFF\U0003FFFE\U0003FFFF\U0004FFFE\U0004FFFF\U0005FFFE\U0005FFFF\U0006FFFE\U0006FFFF\U0007FFFE\U0007FFFF\U0008FFFE\U0008FFFF\U0009FFFE\U0009FFFF\U000AFFFE\U000AFFFF\U000BFFFE\U000BFFFF\U000CFFFE\U000CFFFF\U000DFFFE\U000DFFFF\U000EFFFE\U000EFFFF\U000FFFFE\U000FFFFF\U0010FFFE\U0010FFFF]")
+
+invalid_unicode_no_surrogate = "[\u0001-\u0008\u000B\u000E-\u001F\u007F-\u009F\uFDD0-\uFDEF\uFFFE\uFFFF\U0001FFFE\U0001FFFF\U0002FFFE\U0002FFFF\U0003FFFE\U0003FFFF\U0004FFFE\U0004FFFF\U0005FFFE\U0005FFFF\U0006FFFE\U0006FFFF\U0007FFFE\U0007FFFF\U0008FFFE\U0008FFFF\U0009FFFE\U0009FFFF\U000AFFFE\U000AFFFF\U000BFFFE\U000BFFFF\U000CFFFE\U000CFFFF\U000DFFFE\U000DFFFF\U000EFFFE\U000EFFFF\U000FFFFE\U000FFFFF\U0010FFFE\U0010FFFF]"
+
+if utils.supports_lone_surrogates:
+    # Use one extra step of indirection and create surrogates with
+    # unichr. Not using this indirection would introduce an illegal
+    # unicode literal on platforms not supporting such lone
+    # surrogates.
+    invalid_unicode_re = re.compile(invalid_unicode_no_surrogate +
+                                    eval('"\\uD800-\\uDFFF"'))
+else:
+    invalid_unicode_re = re.compile(invalid_unicode_no_surrogate)
 
 non_bmp_invalid_codepoints = set([0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE,
                                   0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF,
@@ -118,7 +130,11 @@ def _readFromBuffer(self, bytes):
 
 
 def HTMLInputStream(source, encoding=None, parseMeta=True, chardet=True):
-    if hasattr(source, "read"):
+    if isinstance(source, http_client.HTTPResponse):
+        # Work around Python bug #20007: read(0) closes the connection.
+        # http://bugs.python.org/issue20007
+        isUnicode = False
+    elif hasattr(source, "read"):
         isUnicode = isinstance(source.read(0), text_type)
     else:
         isUnicode = isinstance(source, text_type)
@@ -159,13 +175,18 @@ def __init__(self, source):
 
         """
 
-        # Craziness
-        if len("\U0010FFFF") == 1:
+        if not utils.supports_lone_surrogates:
+            # Such platforms will have already checked for such
+            # surrogate errors, so no need to do this checking.
+            self.reportCharacterErrors = None
+            self.replaceCharactersRegexp = None
+        elif len("\U0010FFFF") == 1:
             self.reportCharacterErrors = self.characterErrorsUCS4
-            self.replaceCharactersRegexp = re.compile("[\uD800-\uDFFF]")
+            self.replaceCharactersRegexp = re.compile(eval('"[\\uD800-\\uDFFF]"'))
         else:
             self.reportCharacterErrors = self.characterErrorsUCS2
-            self.replaceCharactersRegexp = re.compile("([\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?/
+                               (?P[-a-zA-Z0-9.]+/[-a-zA-Z0-9.]+)
+                               # Match any character set and encoding
+                               (?:(?:;charset=(?:[-a-zA-Z0-9]+)(?:;(?:base64))?)
+                                 |(?:;(?:base64))?(?:;charset=(?:[-a-zA-Z0-9]+))?)
+                               # Assume the rest is data
+                               ,.*
+                               $
+                               ''',
+                              re.VERBOSE)
+
+
 class HTMLSanitizerMixin(object):
     """ sanitization of XHTML+MathML+SVG and of inline style attributes."""
 
@@ -100,8 +115,8 @@ class HTMLSanitizerMixin(object):
                       'xml:base', 'xml:lang', 'xml:space', 'xmlns', 'xmlns:xlink', 'y',
                       'y1', 'y2', 'zoomAndPan']
 
-    attr_val_is_uri = ['href', 'src', 'cite', 'action', 'longdesc', 'poster',
-                       'xlink:href', 'xml:base']
+    attr_val_is_uri = ['href', 'src', 'cite', 'action', 'longdesc', 'poster', 'background', 'datasrc',
+                       'dynsrc', 'lowsrc', 'ping', 'poster', 'xlink:href', 'xml:base']
 
     svg_attr_val_allows_ref = ['clip-path', 'color-profile', 'cursor', 'fill',
                                'filter', 'marker', 'marker-start', 'marker-mid', 'marker-end',
@@ -138,7 +153,9 @@ class HTMLSanitizerMixin(object):
     acceptable_protocols = ['ed2k', 'ftp', 'http', 'https', 'irc',
                             'mailto', 'news', 'gopher', 'nntp', 'telnet', 'webcal',
                             'xmpp', 'callto', 'feed', 'urn', 'aim', 'rsync', 'tag',
-                            'ssh', 'sftp', 'rtsp', 'afs']
+                            'ssh', 'sftp', 'rtsp', 'afs', 'data']
+
+    acceptable_content_types = ['image/png', 'image/jpeg', 'image/gif', 'image/webp', 'image/bmp', 'text/plain']
 
     # subclasses may define their own versions of these constants
     allowed_elements = acceptable_elements + mathml_elements + svg_elements
@@ -147,6 +164,7 @@ class HTMLSanitizerMixin(object):
     allowed_css_keywords = acceptable_css_keywords
     allowed_svg_properties = acceptable_svg_properties
     allowed_protocols = acceptable_protocols
+    allowed_content_types = acceptable_content_types
 
     # Sanitize the +html+, escaping all elements not in ALLOWED_ELEMENTS, and
     # stripping out all # attributes not in ALLOWED_ATTRIBUTES. Style
@@ -189,10 +207,21 @@ def allowed_token(self, token, token_type):
                                        unescape(attrs[attr])).lower()
                 # remove replacement characters from unescaped characters
                 val_unescaped = val_unescaped.replace("\ufffd", "")
-                if (re.match("^[a-z0-9][-+.a-z0-9]*:", val_unescaped) and
-                    (val_unescaped.split(':')[0] not in
-                     self.allowed_protocols)):
+                try:
+                    uri = urlparse.urlparse(val_unescaped)
+                except ValueError:
+                    uri = None
                     del attrs[attr]
+                if uri and uri.scheme:
+                    if uri.scheme not in self.allowed_protocols:
+                        del attrs[attr]
+                    if uri.scheme == 'data':
+                        m = content_type_rgx.match(uri.path)
+                        if not m:
+                            del attrs[attr]
+                        elif m.group('content_type') not in self.allowed_content_types:
+                            del attrs[attr]
+
             for attr in self.svg_attr_val_allows_ref:
                 if attr in attrs:
                     attrs[attr] = re.sub(r'url\s*\(\s*[^#\s][^)]+?\)',
@@ -245,7 +274,7 @@ def sanitize_css(self, style):
             elif prop.split('-')[0].lower() in ['background', 'border', 'margin',
                                                 'padding']:
                 for keyword in value.split():
-                    if not keyword in self.acceptable_css_keywords and \
+                    if keyword not in self.acceptable_css_keywords and \
                             not re.match("^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$", keyword):
                         break
                 else:
diff --git a/pip/_vendor/html5lib/serializer/htmlserializer.py b/pip/_vendor/html5lib/serializer/htmlserializer.py
index 157840a057b..01d9c5ff687 100644
--- a/pip/_vendor/html5lib/serializer/htmlserializer.py
+++ b/pip/_vendor/html5lib/serializer/htmlserializer.py
@@ -1,9 +1,6 @@
 from __future__ import absolute_import, division, unicode_literals
 from pip._vendor.six import text_type
 
-import gettext
-_ = gettext.gettext
-
 try:
     from functools import reduce
 except ImportError:
@@ -35,7 +32,7 @@
                 v = utils.surrogatePairToCodepoint(v)
             else:
                 v = ord(v)
-            if not v in encode_entity_map or k.islower():
+            if v not in encode_entity_map or k.islower():
                 # prefer < over < and similarly for &, >, etc.
                 encode_entity_map[v] = k
 
@@ -208,7 +205,7 @@ def serialize(self, treewalker, encoding=None):
                 if token["systemId"]:
                     if token["systemId"].find('"') >= 0:
                         if token["systemId"].find("'") >= 0:
-                            self.serializeError(_("System identifer contains both single and double quote characters"))
+                            self.serializeError("System identifer contains both single and double quote characters")
                         quote_char = "'"
                     else:
                         quote_char = '"'
@@ -220,7 +217,7 @@ def serialize(self, treewalker, encoding=None):
             elif type in ("Characters", "SpaceCharacters"):
                 if type == "SpaceCharacters" or in_cdata:
                     if in_cdata and token["data"].find("= 0:
-                        self.serializeError(_("Unexpected " % name)
 
             elif type == "Comment":
                 data = token["data"]
                 if data.find("--") >= 0:
-                    self.serializeError(_("Comment contains --"))
+                    self.serializeError("Comment contains --")
                 yield self.encodeStrict("" % token["data"])
 
             elif type == "Entity":
                 name = token["name"]
                 key = name + ";"
-                if not key in entities:
-                    self.serializeError(_("Entity %s not recognized" % name))
+                if key not in entities:
+                    self.serializeError("Entity %s not recognized" % name)
                 if self.resolve_entities and key not in xmlEntities:
                     data = entities[key]
                 else:
diff --git a/pip/_vendor/html5lib/treebuilders/dom.py b/pip/_vendor/html5lib/treebuilders/dom.py
index 61e5ed79edc..234233b7932 100644
--- a/pip/_vendor/html5lib/treebuilders/dom.py
+++ b/pip/_vendor/html5lib/treebuilders/dom.py
@@ -158,7 +158,7 @@ def insertText(self, data, parent=None):
             else:
                 # HACK: allow text nodes as children of the document node
                 if hasattr(self.dom, '_child_node_types'):
-                    if not Node.TEXT_NODE in self.dom._child_node_types:
+                    if Node.TEXT_NODE not in self.dom._child_node_types:
                         self.dom._child_node_types = list(self.dom._child_node_types)
                         self.dom._child_node_types.append(Node.TEXT_NODE)
                 self.dom.appendChild(self.dom.createTextNode(data))
diff --git a/pip/_vendor/html5lib/treewalkers/__init__.py b/pip/_vendor/html5lib/treewalkers/__init__.py
index 18124e75f3b..20b91b114ab 100644
--- a/pip/_vendor/html5lib/treewalkers/__init__.py
+++ b/pip/_vendor/html5lib/treewalkers/__init__.py
@@ -10,8 +10,12 @@
 
 from __future__ import absolute_import, division, unicode_literals
 
+__all__ = ["getTreeWalker", "pprint", "dom", "etree", "genshistream", "lxmletree",
+           "pulldom"]
+
 import sys
 
+from .. import constants
 from ..utils import default_etree
 
 treeWalkerCache = {}
@@ -55,3 +59,89 @@ def getTreeWalker(treeType, implementation=None, **kwargs):
             # XXX: NEVER cache here, caching is done in the etree submodule
             return etree.getETreeModule(implementation, **kwargs).TreeWalker
     return treeWalkerCache.get(treeType)
+
+
+def concatenateCharacterTokens(tokens):
+    pendingCharacters = []
+    for token in tokens:
+        type = token["type"]
+        if type in ("Characters", "SpaceCharacters"):
+            pendingCharacters.append(token["data"])
+        else:
+            if pendingCharacters:
+                yield {"type": "Characters", "data": "".join(pendingCharacters)}
+                pendingCharacters = []
+            yield token
+    if pendingCharacters:
+        yield {"type": "Characters", "data": "".join(pendingCharacters)}
+
+
+def pprint(walker):
+    """Pretty printer for tree walkers"""
+    output = []
+    indent = 0
+    for token in concatenateCharacterTokens(walker):
+        type = token["type"]
+        if type in ("StartTag", "EmptyTag"):
+            # tag name
+            if token["namespace"] and token["namespace"] != constants.namespaces["html"]:
+                if token["namespace"] in constants.prefixes:
+                    ns = constants.prefixes[token["namespace"]]
+                else:
+                    ns = token["namespace"]
+                name = "%s %s" % (ns, token["name"])
+            else:
+                name = token["name"]
+            output.append("%s<%s>" % (" " * indent, name))
+            indent += 2
+            # attributes (sorted for consistent ordering)
+            attrs = token["data"]
+            for (namespace, localname), value in sorted(attrs.items()):
+                if namespace:
+                    if namespace in constants.prefixes:
+                        ns = constants.prefixes[namespace]
+                    else:
+                        ns = namespace
+                    name = "%s %s" % (ns, localname)
+                else:
+                    name = localname
+                output.append("%s%s=\"%s\"" % (" " * indent, name, value))
+            # self-closing
+            if type == "EmptyTag":
+                indent -= 2
+
+        elif type == "EndTag":
+            indent -= 2
+
+        elif type == "Comment":
+            output.append("%s" % (" " * indent, token["data"]))
+
+        elif type == "Doctype":
+            if token["name"]:
+                if token["publicId"]:
+                    output.append("""%s""" %
+                                  (" " * indent,
+                                   token["name"],
+                                   token["publicId"],
+                                   token["systemId"] if token["systemId"] else ""))
+                elif token["systemId"]:
+                    output.append("""%s""" %
+                                  (" " * indent,
+                                   token["name"],
+                                   token["systemId"]))
+                else:
+                    output.append("%s" % (" " * indent,
+                                                       token["name"]))
+            else:
+                output.append("%s" % (" " * indent,))
+
+        elif type == "Characters":
+            output.append("%s\"%s\"" % (" " * indent, token["data"]))
+
+        elif type == "SpaceCharacters":
+            assert False, "concatenateCharacterTokens should have got rid of all Space tokens"
+
+        else:
+            raise ValueError("Unknown token type, %s" % type)
+
+    return "\n".join(output)
diff --git a/pip/_vendor/html5lib/treewalkers/_base.py b/pip/_vendor/html5lib/treewalkers/_base.py
index dda3cd74e30..42a59a4bfbb 100644
--- a/pip/_vendor/html5lib/treewalkers/_base.py
+++ b/pip/_vendor/html5lib/treewalkers/_base.py
@@ -1,8 +1,8 @@
 from __future__ import absolute_import, division, unicode_literals
 from pip._vendor.six import text_type, string_types
 
-import gettext
-_ = gettext.gettext
+__all__ = ["DOCUMENT", "DOCTYPE", "TEXT", "ELEMENT", "COMMENT", "ENTITY", "UNKNOWN",
+           "TreeWalker", "NonRecursiveTreeWalker"]
 
 from xml.dom import Node
 
@@ -58,7 +58,7 @@ def emptyTag(self, namespace, name, attrs, hasChildren=False):
                "namespace": to_text(namespace),
                "data": attrs}
         if hasChildren:
-            yield self.error(_("Void element has children"))
+            yield self.error("Void element has children")
 
     def startTag(self, namespace, name, attrs):
         assert namespace is None or isinstance(namespace, string_types), type(namespace)
@@ -122,7 +122,7 @@ def entity(self, name):
         return {"type": "Entity", "name": text_type(name)}
 
     def unknown(self, nodeType):
-        return self.error(_("Unknown node type: ") + nodeType)
+        return self.error("Unknown node type: " + nodeType)
 
 
 class NonRecursiveTreeWalker(TreeWalker):
diff --git a/pip/_vendor/html5lib/treewalkers/dom.py b/pip/_vendor/html5lib/treewalkers/dom.py
index a01287a9448..ac4dcf31bf6 100644
--- a/pip/_vendor/html5lib/treewalkers/dom.py
+++ b/pip/_vendor/html5lib/treewalkers/dom.py
@@ -2,9 +2,6 @@
 
 from xml.dom import Node
 
-import gettext
-_ = gettext.gettext
-
 from . import _base
 
 
diff --git a/pip/_vendor/html5lib/treewalkers/etree.py b/pip/_vendor/html5lib/treewalkers/etree.py
index f5615f50a7e..71634255143 100644
--- a/pip/_vendor/html5lib/treewalkers/etree.py
+++ b/pip/_vendor/html5lib/treewalkers/etree.py
@@ -7,8 +7,6 @@
         from ordereddict import OrderedDict
     except ImportError:
         OrderedDict = dict
-import gettext
-_ = gettext.gettext
 
 import re
 
@@ -60,7 +58,7 @@ def getNodeDetails(self, node):
                 return _base.COMMENT, node.text
 
             else:
-                assert type(node.tag) == text_type, type(node.tag)
+                assert isinstance(node.tag, string_types), type(node.tag)
                 # This is assumed to be an ordinary element
                 match = tag_regexp.match(node.tag)
                 if match:
diff --git a/pip/_vendor/html5lib/treewalkers/lxmletree.py b/pip/_vendor/html5lib/treewalkers/lxmletree.py
index 4373383c574..a4854869dc8 100644
--- a/pip/_vendor/html5lib/treewalkers/lxmletree.py
+++ b/pip/_vendor/html5lib/treewalkers/lxmletree.py
@@ -4,9 +4,6 @@
 from lxml import etree
 from ..treebuilders.etree import tag_regexp
 
-from gettext import gettext
-_ = gettext
-
 from . import _base
 
 from .. import ihatexml
@@ -87,10 +84,6 @@ def __init__(self, fragment_root, obj):
             self.tail = ensure_str(self.obj.tail)
         else:
             self.tail = None
-        self.isstring = isinstance(obj, str) or isinstance(obj, bytes)
-        # Support for bytes here is Py2
-        if self.isstring:
-            self.obj = ensure_str(self.obj)
 
     def __getattr__(self, name):
         return getattr(self.obj, name)
@@ -134,7 +127,7 @@ def __init__(self, tree):
     def getNodeDetails(self, node):
         if isinstance(node, tuple):  # Text node
             node, key = node
-            assert key in ("text", "tail"), _("Text nodes are text or tail, found %s") % key
+            assert key in ("text", "tail"), "Text nodes are text or tail, found %s" % key
             return _base.TEXT, ensure_str(getattr(node, key))
 
         elif isinstance(node, Root):
@@ -143,7 +136,7 @@ def getNodeDetails(self, node):
         elif isinstance(node, Doctype):
             return _base.DOCTYPE, node.name, node.public_id, node.system_id
 
-        elif isinstance(node, FragmentWrapper) and node.isstring:
+        elif isinstance(node, FragmentWrapper) and not hasattr(node, "tag"):
             return _base.TEXT, node.obj
 
         elif node.tag == etree.Comment:
@@ -173,7 +166,7 @@ def getNodeDetails(self, node):
                     attrs, len(node) > 0 or node.text)
 
     def getFirstChild(self, node):
-        assert not isinstance(node, tuple), _("Text nodes have no children")
+        assert not isinstance(node, tuple), "Text nodes have no children"
 
         assert len(node) or node.text, "Node has no children"
         if node.text:
@@ -184,7 +177,7 @@ def getFirstChild(self, node):
     def getNextSibling(self, node):
         if isinstance(node, tuple):  # Text node
             node, key = node
-            assert key in ("text", "tail"), _("Text nodes are text or tail, found %s") % key
+            assert key in ("text", "tail"), "Text nodes are text or tail, found %s" % key
             if key == "text":
                 # XXX: we cannot use a "bool(node) and node[0] or None" construct here
                 # because node[0] might evaluate to False if it has no child element
@@ -200,7 +193,7 @@ def getNextSibling(self, node):
     def getParentNode(self, node):
         if isinstance(node, tuple):  # Text node
             node, key = node
-            assert key in ("text", "tail"), _("Text nodes are text or tail, found %s") % key
+            assert key in ("text", "tail"), "Text nodes are text or tail, found %s" % key
             if key == "text":
                 return node
             # else: fallback to "normal" processing
diff --git a/pip/_vendor/html5lib/utils.py b/pip/_vendor/html5lib/utils.py
index 2f41f4dfa60..85f1459d003 100644
--- a/pip/_vendor/html5lib/utils.py
+++ b/pip/_vendor/html5lib/utils.py
@@ -2,6 +2,8 @@
 
 from types import ModuleType
 
+from pip._vendor.six import text_type
+
 try:
     import xml.etree.cElementTree as default_etree
 except ImportError:
@@ -9,7 +11,26 @@
 
 
 __all__ = ["default_etree", "MethodDispatcher", "isSurrogatePair",
-           "surrogatePairToCodepoint", "moduleFactoryFactory"]
+           "surrogatePairToCodepoint", "moduleFactoryFactory",
+           "supports_lone_surrogates"]
+
+
+# Platforms not supporting lone surrogates (\uD800-\uDFFF) should be
+# caught by the below test. In general this would be any platform
+# using UTF-16 as its encoding of unicode strings, such as
+# Jython. This is because UTF-16 itself is based on the use of such
+# surrogates, and there is no mechanism to further escape such
+# escapes.
+try:
+    _x = eval('"\\uD800"')
+    if not isinstance(_x, text_type):
+        # We need this with u"" because of http://bugs.jython.org/issue2039
+        _x = eval('u"\\uD800"')
+        assert isinstance(_x, text_type)
+except:
+    supports_lone_surrogates = False
+else:
+    supports_lone_surrogates = True
 
 
 class MethodDispatcher(dict):
diff --git a/pip/_vendor/ipaddress.py b/pip/_vendor/ipaddress.py
new file mode 100644
index 00000000000..7657fc8fbb7
--- /dev/null
+++ b/pip/_vendor/ipaddress.py
@@ -0,0 +1,2417 @@
+# Copyright 2007 Google Inc.
+#  Licensed to PSF under a Contributor Agreement.
+
+"""A fast, lightweight IPv4/IPv6 manipulation library in Python.
+
+This library is used to create/poke/manipulate IPv4 and IPv6 addresses
+and networks.
+
+"""
+
+from __future__ import unicode_literals
+
+
+import itertools
+import struct
+
+__version__ = '1.0.16'
+
+# Compatibility functions
+_compat_int_types = (int,)
+try:
+    _compat_int_types = (int, long)
+except NameError:
+    pass
+try:
+    _compat_str = unicode
+except NameError:
+    _compat_str = str
+    assert bytes != str
+if b'\0'[0] == 0:  # Python 3 semantics
+    def _compat_bytes_to_byte_vals(byt):
+        return byt
+else:
+    def _compat_bytes_to_byte_vals(byt):
+        return [struct.unpack(b'!B', b)[0] for b in byt]
+try:
+    _compat_int_from_byte_vals = int.from_bytes
+except AttributeError:
+    def _compat_int_from_byte_vals(bytvals, endianess):
+        assert endianess == 'big'
+        res = 0
+        for bv in bytvals:
+            assert isinstance(bv, _compat_int_types)
+            res = (res << 8) + bv
+        return res
+
+
+def _compat_to_bytes(intval, length, endianess):
+    assert isinstance(intval, _compat_int_types)
+    assert endianess == 'big'
+    if length == 4:
+        if intval < 0 or intval >= 2 ** 32:
+            raise struct.error("integer out of range for 'I' format code")
+        return struct.pack(b'!I', intval)
+    elif length == 16:
+        if intval < 0 or intval >= 2 ** 128:
+            raise struct.error("integer out of range for 'QQ' format code")
+        return struct.pack(b'!QQ', intval >> 64, intval & 0xffffffffffffffff)
+    else:
+        raise NotImplementedError()
+if hasattr(int, 'bit_length'):
+    # Not int.bit_length , since that won't work in 2.7 where long exists
+    def _compat_bit_length(i):
+        return i.bit_length()
+else:
+    def _compat_bit_length(i):
+        for res in itertools.count():
+            if i >> res == 0:
+                return res
+
+
+def _compat_range(start, end, step=1):
+    assert step > 0
+    i = start
+    while i < end:
+        yield i
+        i += step
+
+
+class _TotalOrderingMixin(object):
+    __slots__ = ()
+
+    # Helper that derives the other comparison operations from
+    # __lt__ and __eq__
+    # We avoid functools.total_ordering because it doesn't handle
+    # NotImplemented correctly yet (http://bugs.python.org/issue10042)
+    def __eq__(self, other):
+        raise NotImplementedError
+
+    def __ne__(self, other):
+        equal = self.__eq__(other)
+        if equal is NotImplemented:
+            return NotImplemented
+        return not equal
+
+    def __lt__(self, other):
+        raise NotImplementedError
+
+    def __le__(self, other):
+        less = self.__lt__(other)
+        if less is NotImplemented or not less:
+            return self.__eq__(other)
+        return less
+
+    def __gt__(self, other):
+        less = self.__lt__(other)
+        if less is NotImplemented:
+            return NotImplemented
+        equal = self.__eq__(other)
+        if equal is NotImplemented:
+            return NotImplemented
+        return not (less or equal)
+
+    def __ge__(self, other):
+        less = self.__lt__(other)
+        if less is NotImplemented:
+            return NotImplemented
+        return not less
+
+
+IPV4LENGTH = 32
+IPV6LENGTH = 128
+
+
+class AddressValueError(ValueError):
+    """A Value Error related to the address."""
+
+
+class NetmaskValueError(ValueError):
+    """A Value Error related to the netmask."""
+
+
+def ip_address(address):
+    """Take an IP string/int and return an object of the correct type.
+
+    Args:
+        address: A string or integer, the IP address.  Either IPv4 or
+          IPv6 addresses may be supplied; integers less than 2**32 will
+          be considered to be IPv4 by default.
+
+    Returns:
+        An IPv4Address or IPv6Address object.
+
+    Raises:
+        ValueError: if the *address* passed isn't either a v4 or a v6
+          address
+
+    """
+    try:
+        return IPv4Address(address)
+    except (AddressValueError, NetmaskValueError):
+        pass
+
+    try:
+        return IPv6Address(address)
+    except (AddressValueError, NetmaskValueError):
+        pass
+
+    if isinstance(address, bytes):
+        raise AddressValueError(
+            '%r does not appear to be an IPv4 or IPv6 address. '
+            'Did you pass in a bytes (str in Python 2) instead of'
+            ' a unicode object?' % address)
+
+    raise ValueError('%r does not appear to be an IPv4 or IPv6 address' %
+                     address)
+
+
+def ip_network(address, strict=True):
+    """Take an IP string/int and return an object of the correct type.
+
+    Args:
+        address: A string or integer, the IP network.  Either IPv4 or
+          IPv6 networks may be supplied; integers less than 2**32 will
+          be considered to be IPv4 by default.
+
+    Returns:
+        An IPv4Network or IPv6Network object.
+
+    Raises:
+        ValueError: if the string passed isn't either a v4 or a v6
+          address. Or if the network has host bits set.
+
+    """
+    try:
+        return IPv4Network(address, strict)
+    except (AddressValueError, NetmaskValueError):
+        pass
+
+    try:
+        return IPv6Network(address, strict)
+    except (AddressValueError, NetmaskValueError):
+        pass
+
+    if isinstance(address, bytes):
+        raise AddressValueError(
+            '%r does not appear to be an IPv4 or IPv6 network. '
+            'Did you pass in a bytes (str in Python 2) instead of'
+            ' a unicode object?' % address)
+
+    raise ValueError('%r does not appear to be an IPv4 or IPv6 network' %
+                     address)
+
+
+def ip_interface(address):
+    """Take an IP string/int and return an object of the correct type.
+
+    Args:
+        address: A string or integer, the IP address.  Either IPv4 or
+          IPv6 addresses may be supplied; integers less than 2**32 will
+          be considered to be IPv4 by default.
+
+    Returns:
+        An IPv4Interface or IPv6Interface object.
+
+    Raises:
+        ValueError: if the string passed isn't either a v4 or a v6
+          address.
+
+    Notes:
+        The IPv?Interface classes describe an Address on a particular
+        Network, so they're basically a combination of both the Address
+        and Network classes.
+
+    """
+    try:
+        return IPv4Interface(address)
+    except (AddressValueError, NetmaskValueError):
+        pass
+
+    try:
+        return IPv6Interface(address)
+    except (AddressValueError, NetmaskValueError):
+        pass
+
+    raise ValueError('%r does not appear to be an IPv4 or IPv6 interface' %
+                     address)
+
+
+def v4_int_to_packed(address):
+    """Represent an address as 4 packed bytes in network (big-endian) order.
+
+    Args:
+        address: An integer representation of an IPv4 IP address.
+
+    Returns:
+        The integer address packed as 4 bytes in network (big-endian) order.
+
+    Raises:
+        ValueError: If the integer is negative or too large to be an
+          IPv4 IP address.
+
+    """
+    try:
+        return _compat_to_bytes(address, 4, 'big')
+    except (struct.error, OverflowError):
+        raise ValueError("Address negative or too large for IPv4")
+
+
+def v6_int_to_packed(address):
+    """Represent an address as 16 packed bytes in network (big-endian) order.
+
+    Args:
+        address: An integer representation of an IPv6 IP address.
+
+    Returns:
+        The integer address packed as 16 bytes in network (big-endian) order.
+
+    """
+    try:
+        return _compat_to_bytes(address, 16, 'big')
+    except (struct.error, OverflowError):
+        raise ValueError("Address negative or too large for IPv6")
+
+
+def _split_optional_netmask(address):
+    """Helper to split the netmask and raise AddressValueError if needed"""
+    addr = _compat_str(address).split('/')
+    if len(addr) > 2:
+        raise AddressValueError("Only one '/' permitted in %r" % address)
+    return addr
+
+
+def _find_address_range(addresses):
+    """Find a sequence of sorted deduplicated IPv#Address.
+
+    Args:
+        addresses: a list of IPv#Address objects.
+
+    Yields:
+        A tuple containing the first and last IP addresses in the sequence.
+
+    """
+    it = iter(addresses)
+    first = last = next(it)
+    for ip in it:
+        if ip._ip != last._ip + 1:
+            yield first, last
+            first = ip
+        last = ip
+    yield first, last
+
+
+def _count_righthand_zero_bits(number, bits):
+    """Count the number of zero bits on the right hand side.
+
+    Args:
+        number: an integer.
+        bits: maximum number of bits to count.
+
+    Returns:
+        The number of zero bits on the right hand side of the number.
+
+    """
+    if number == 0:
+        return bits
+    return min(bits, _compat_bit_length(~number & (number - 1)))
+
+
+def summarize_address_range(first, last):
+    """Summarize a network range given the first and last IP addresses.
+
+    Example:
+        >>> list(summarize_address_range(IPv4Address('192.0.2.0'),
+        ...                              IPv4Address('192.0.2.130')))
+        ...                                #doctest: +NORMALIZE_WHITESPACE
+        [IPv4Network('192.0.2.0/25'), IPv4Network('192.0.2.128/31'),
+         IPv4Network('192.0.2.130/32')]
+
+    Args:
+        first: the first IPv4Address or IPv6Address in the range.
+        last: the last IPv4Address or IPv6Address in the range.
+
+    Returns:
+        An iterator of the summarized IPv(4|6) network objects.
+
+    Raise:
+        TypeError:
+            If the first and last objects are not IP addresses.
+            If the first and last objects are not the same version.
+        ValueError:
+            If the last object is not greater than the first.
+            If the version of the first address is not 4 or 6.
+
+    """
+    if (not (isinstance(first, _BaseAddress) and
+             isinstance(last, _BaseAddress))):
+        raise TypeError('first and last must be IP addresses, not networks')
+    if first.version != last.version:
+        raise TypeError("%s and %s are not of the same version" % (
+                        first, last))
+    if first > last:
+        raise ValueError('last IP address must be greater than first')
+
+    if first.version == 4:
+        ip = IPv4Network
+    elif first.version == 6:
+        ip = IPv6Network
+    else:
+        raise ValueError('unknown IP version')
+
+    ip_bits = first._max_prefixlen
+    first_int = first._ip
+    last_int = last._ip
+    while first_int <= last_int:
+        nbits = min(_count_righthand_zero_bits(first_int, ip_bits),
+                    _compat_bit_length(last_int - first_int + 1) - 1)
+        net = ip((first_int, ip_bits - nbits))
+        yield net
+        first_int += 1 << nbits
+        if first_int - 1 == ip._ALL_ONES:
+            break
+
+
+def _collapse_addresses_internal(addresses):
+    """Loops through the addresses, collapsing concurrent netblocks.
+
+    Example:
+
+        ip1 = IPv4Network('192.0.2.0/26')
+        ip2 = IPv4Network('192.0.2.64/26')
+        ip3 = IPv4Network('192.0.2.128/26')
+        ip4 = IPv4Network('192.0.2.192/26')
+
+        _collapse_addresses_internal([ip1, ip2, ip3, ip4]) ->
+          [IPv4Network('192.0.2.0/24')]
+
+        This shouldn't be called directly; it is called via
+          collapse_addresses([]).
+
+    Args:
+        addresses: A list of IPv4Network's or IPv6Network's
+
+    Returns:
+        A list of IPv4Network's or IPv6Network's depending on what we were
+        passed.
+
+    """
+    # First merge
+    to_merge = list(addresses)
+    subnets = {}
+    while to_merge:
+        net = to_merge.pop()
+        supernet = net.supernet()
+        existing = subnets.get(supernet)
+        if existing is None:
+            subnets[supernet] = net
+        elif existing != net:
+            # Merge consecutive subnets
+            del subnets[supernet]
+            to_merge.append(supernet)
+    # Then iterate over resulting networks, skipping subsumed subnets
+    last = None
+    for net in sorted(subnets.values()):
+        if last is not None:
+            # Since they are sorted,
+            # last.network_address <= net.network_address is a given.
+            if last.broadcast_address >= net.broadcast_address:
+                continue
+        yield net
+        last = net
+
+
+def collapse_addresses(addresses):
+    """Collapse a list of IP objects.
+
+    Example:
+        collapse_addresses([IPv4Network('192.0.2.0/25'),
+                            IPv4Network('192.0.2.128/25')]) ->
+                           [IPv4Network('192.0.2.0/24')]
+
+    Args:
+        addresses: An iterator of IPv4Network or IPv6Network objects.
+
+    Returns:
+        An iterator of the collapsed IPv(4|6)Network objects.
+
+    Raises:
+        TypeError: If passed a list of mixed version objects.
+
+    """
+    addrs = []
+    ips = []
+    nets = []
+
+    # split IP addresses and networks
+    for ip in addresses:
+        if isinstance(ip, _BaseAddress):
+            if ips and ips[-1]._version != ip._version:
+                raise TypeError("%s and %s are not of the same version" % (
+                                ip, ips[-1]))
+            ips.append(ip)
+        elif ip._prefixlen == ip._max_prefixlen:
+            if ips and ips[-1]._version != ip._version:
+                raise TypeError("%s and %s are not of the same version" % (
+                                ip, ips[-1]))
+            try:
+                ips.append(ip.ip)
+            except AttributeError:
+                ips.append(ip.network_address)
+        else:
+            if nets and nets[-1]._version != ip._version:
+                raise TypeError("%s and %s are not of the same version" % (
+                                ip, nets[-1]))
+            nets.append(ip)
+
+    # sort and dedup
+    ips = sorted(set(ips))
+
+    # find consecutive address ranges in the sorted sequence and summarize them
+    if ips:
+        for first, last in _find_address_range(ips):
+            addrs.extend(summarize_address_range(first, last))
+
+    return _collapse_addresses_internal(addrs + nets)
+
+
+def get_mixed_type_key(obj):
+    """Return a key suitable for sorting between networks and addresses.
+
+    Address and Network objects are not sortable by default; they're
+    fundamentally different so the expression
+
+        IPv4Address('192.0.2.0') <= IPv4Network('192.0.2.0/24')
+
+    doesn't make any sense.  There are some times however, where you may wish
+    to have ipaddress sort these for you anyway. If you need to do this, you
+    can use this function as the key= argument to sorted().
+
+    Args:
+      obj: either a Network or Address object.
+    Returns:
+      appropriate key.
+
+    """
+    if isinstance(obj, _BaseNetwork):
+        return obj._get_networks_key()
+    elif isinstance(obj, _BaseAddress):
+        return obj._get_address_key()
+    return NotImplemented
+
+
+class _IPAddressBase(_TotalOrderingMixin):
+
+    """The mother class."""
+
+    __slots__ = ()
+
+    @property
+    def exploded(self):
+        """Return the longhand version of the IP address as a string."""
+        return self._explode_shorthand_ip_string()
+
+    @property
+    def compressed(self):
+        """Return the shorthand version of the IP address as a string."""
+        return _compat_str(self)
+
+    @property
+    def reverse_pointer(self):
+        """The name of the reverse DNS pointer for the IP address, e.g.:
+            >>> ipaddress.ip_address("127.0.0.1").reverse_pointer
+            '1.0.0.127.in-addr.arpa'
+            >>> ipaddress.ip_address("2001:db8::1").reverse_pointer
+            '1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa'
+
+        """
+        return self._reverse_pointer()
+
+    @property
+    def version(self):
+        msg = '%200s has no version specified' % (type(self),)
+        raise NotImplementedError(msg)
+
+    def _check_int_address(self, address):
+        if address < 0:
+            msg = "%d (< 0) is not permitted as an IPv%d address"
+            raise AddressValueError(msg % (address, self._version))
+        if address > self._ALL_ONES:
+            msg = "%d (>= 2**%d) is not permitted as an IPv%d address"
+            raise AddressValueError(msg % (address, self._max_prefixlen,
+                                           self._version))
+
+    def _check_packed_address(self, address, expected_len):
+        address_len = len(address)
+        if address_len != expected_len:
+            msg = (
+                '%r (len %d != %d) is not permitted as an IPv%d address. '
+                'Did you pass in a bytes (str in Python 2) instead of'
+                ' a unicode object?'
+            )
+            raise AddressValueError(msg % (address, address_len,
+                                           expected_len, self._version))
+
+    @classmethod
+    def _ip_int_from_prefix(cls, prefixlen):
+        """Turn the prefix length into a bitwise netmask
+
+        Args:
+            prefixlen: An integer, the prefix length.
+
+        Returns:
+            An integer.
+
+        """
+        return cls._ALL_ONES ^ (cls._ALL_ONES >> prefixlen)
+
+    @classmethod
+    def _prefix_from_ip_int(cls, ip_int):
+        """Return prefix length from the bitwise netmask.
+
+        Args:
+            ip_int: An integer, the netmask in expanded bitwise format
+
+        Returns:
+            An integer, the prefix length.
+
+        Raises:
+            ValueError: If the input intermingles zeroes & ones
+        """
+        trailing_zeroes = _count_righthand_zero_bits(ip_int,
+                                                     cls._max_prefixlen)
+        prefixlen = cls._max_prefixlen - trailing_zeroes
+        leading_ones = ip_int >> trailing_zeroes
+        all_ones = (1 << prefixlen) - 1
+        if leading_ones != all_ones:
+            byteslen = cls._max_prefixlen // 8
+            details = _compat_to_bytes(ip_int, byteslen, 'big')
+            msg = 'Netmask pattern %r mixes zeroes & ones'
+            raise ValueError(msg % details)
+        return prefixlen
+
+    @classmethod
+    def _report_invalid_netmask(cls, netmask_str):
+        msg = '%r is not a valid netmask' % netmask_str
+        raise NetmaskValueError(msg)
+
+    @classmethod
+    def _prefix_from_prefix_string(cls, prefixlen_str):
+        """Return prefix length from a numeric string
+
+        Args:
+            prefixlen_str: The string to be converted
+
+        Returns:
+            An integer, the prefix length.
+
+        Raises:
+            NetmaskValueError: If the input is not a valid netmask
+        """
+        # int allows a leading +/- as well as surrounding whitespace,
+        # so we ensure that isn't the case
+        if not _BaseV4._DECIMAL_DIGITS.issuperset(prefixlen_str):
+            cls._report_invalid_netmask(prefixlen_str)
+        try:
+            prefixlen = int(prefixlen_str)
+        except ValueError:
+            cls._report_invalid_netmask(prefixlen_str)
+        if not (0 <= prefixlen <= cls._max_prefixlen):
+            cls._report_invalid_netmask(prefixlen_str)
+        return prefixlen
+
+    @classmethod
+    def _prefix_from_ip_string(cls, ip_str):
+        """Turn a netmask/hostmask string into a prefix length
+
+        Args:
+            ip_str: The netmask/hostmask to be converted
+
+        Returns:
+            An integer, the prefix length.
+
+        Raises:
+            NetmaskValueError: If the input is not a valid netmask/hostmask
+        """
+        # Parse the netmask/hostmask like an IP address.
+        try:
+            ip_int = cls._ip_int_from_string(ip_str)
+        except AddressValueError:
+            cls._report_invalid_netmask(ip_str)
+
+        # Try matching a netmask (this would be /1*0*/ as a bitwise regexp).
+        # Note that the two ambiguous cases (all-ones and all-zeroes) are
+        # treated as netmasks.
+        try:
+            return cls._prefix_from_ip_int(ip_int)
+        except ValueError:
+            pass
+
+        # Invert the bits, and try matching a /0+1+/ hostmask instead.
+        ip_int ^= cls._ALL_ONES
+        try:
+            return cls._prefix_from_ip_int(ip_int)
+        except ValueError:
+            cls._report_invalid_netmask(ip_str)
+
+    def __reduce__(self):
+        return self.__class__, (_compat_str(self),)
+
+
+class _BaseAddress(_IPAddressBase):
+
+    """A generic IP object.
+
+    This IP class contains the version independent methods which are
+    used by single IP addresses.
+    """
+
+    __slots__ = ()
+
+    def __int__(self):
+        return self._ip
+
+    def __eq__(self, other):
+        try:
+            return (self._ip == other._ip and
+                    self._version == other._version)
+        except AttributeError:
+            return NotImplemented
+
+    def __lt__(self, other):
+        if not isinstance(other, _IPAddressBase):
+            return NotImplemented
+        if not isinstance(other, _BaseAddress):
+            raise TypeError('%s and %s are not of the same type' % (
+                self, other))
+        if self._version != other._version:
+            raise TypeError('%s and %s are not of the same version' % (
+                self, other))
+        if self._ip != other._ip:
+            return self._ip < other._ip
+        return False
+
+    # Shorthand for Integer addition and subtraction. This is not
+    # meant to ever support addition/subtraction of addresses.
+    def __add__(self, other):
+        if not isinstance(other, _compat_int_types):
+            return NotImplemented
+        return self.__class__(int(self) + other)
+
+    def __sub__(self, other):
+        if not isinstance(other, _compat_int_types):
+            return NotImplemented
+        return self.__class__(int(self) - other)
+
+    def __repr__(self):
+        return '%s(%r)' % (self.__class__.__name__, _compat_str(self))
+
+    def __str__(self):
+        return _compat_str(self._string_from_ip_int(self._ip))
+
+    def __hash__(self):
+        return hash(hex(int(self._ip)))
+
+    def _get_address_key(self):
+        return (self._version, self)
+
+    def __reduce__(self):
+        return self.__class__, (self._ip,)
+
+
+class _BaseNetwork(_IPAddressBase):
+
+    """A generic IP network object.
+
+    This IP class contains the version independent methods which are
+    used by networks.
+
+    """
+    def __init__(self, address):
+        self._cache = {}
+
+    def __repr__(self):
+        return '%s(%r)' % (self.__class__.__name__, _compat_str(self))
+
+    def __str__(self):
+        return '%s/%d' % (self.network_address, self.prefixlen)
+
+    def hosts(self):
+        """Generate Iterator over usable hosts in a network.
+
+        This is like __iter__ except it doesn't return the network
+        or broadcast addresses.
+
+        """
+        network = int(self.network_address)
+        broadcast = int(self.broadcast_address)
+        for x in _compat_range(network + 1, broadcast):
+            yield self._address_class(x)
+
+    def __iter__(self):
+        network = int(self.network_address)
+        broadcast = int(self.broadcast_address)
+        for x in _compat_range(network, broadcast + 1):
+            yield self._address_class(x)
+
+    def __getitem__(self, n):
+        network = int(self.network_address)
+        broadcast = int(self.broadcast_address)
+        if n >= 0:
+            if network + n > broadcast:
+                raise IndexError
+            return self._address_class(network + n)
+        else:
+            n += 1
+            if broadcast + n < network:
+                raise IndexError
+            return self._address_class(broadcast + n)
+
+    def __lt__(self, other):
+        if not isinstance(other, _IPAddressBase):
+            return NotImplemented
+        if not isinstance(other, _BaseNetwork):
+            raise TypeError('%s and %s are not of the same type' % (
+                            self, other))
+        if self._version != other._version:
+            raise TypeError('%s and %s are not of the same version' % (
+                            self, other))
+        if self.network_address != other.network_address:
+            return self.network_address < other.network_address
+        if self.netmask != other.netmask:
+            return self.netmask < other.netmask
+        return False
+
+    def __eq__(self, other):
+        try:
+            return (self._version == other._version and
+                    self.network_address == other.network_address and
+                    int(self.netmask) == int(other.netmask))
+        except AttributeError:
+            return NotImplemented
+
+    def __hash__(self):
+        return hash(int(self.network_address) ^ int(self.netmask))
+
+    def __contains__(self, other):
+        # always false if one is v4 and the other is v6.
+        if self._version != other._version:
+            return False
+        # dealing with another network.
+        if isinstance(other, _BaseNetwork):
+            return False
+        # dealing with another address
+        else:
+            # address
+            return (int(self.network_address) <= int(other._ip) <=
+                    int(self.broadcast_address))
+
+    def overlaps(self, other):
+        """Tell if self is partly contained in other."""
+        return self.network_address in other or (
+            self.broadcast_address in other or (
+                other.network_address in self or (
+                    other.broadcast_address in self)))
+
+    @property
+    def broadcast_address(self):
+        x = self._cache.get('broadcast_address')
+        if x is None:
+            x = self._address_class(int(self.network_address) |
+                                    int(self.hostmask))
+            self._cache['broadcast_address'] = x
+        return x
+
+    @property
+    def hostmask(self):
+        x = self._cache.get('hostmask')
+        if x is None:
+            x = self._address_class(int(self.netmask) ^ self._ALL_ONES)
+            self._cache['hostmask'] = x
+        return x
+
+    @property
+    def with_prefixlen(self):
+        return '%s/%d' % (self.network_address, self._prefixlen)
+
+    @property
+    def with_netmask(self):
+        return '%s/%s' % (self.network_address, self.netmask)
+
+    @property
+    def with_hostmask(self):
+        return '%s/%s' % (self.network_address, self.hostmask)
+
+    @property
+    def num_addresses(self):
+        """Number of hosts in the current subnet."""
+        return int(self.broadcast_address) - int(self.network_address) + 1
+
+    @property
+    def _address_class(self):
+        # Returning bare address objects (rather than interfaces) allows for
+        # more consistent behaviour across the network address, broadcast
+        # address and individual host addresses.
+        msg = '%200s has no associated address class' % (type(self),)
+        raise NotImplementedError(msg)
+
+    @property
+    def prefixlen(self):
+        return self._prefixlen
+
+    def address_exclude(self, other):
+        """Remove an address from a larger block.
+
+        For example:
+
+            addr1 = ip_network('192.0.2.0/28')
+            addr2 = ip_network('192.0.2.1/32')
+            addr1.address_exclude(addr2) =
+                [IPv4Network('192.0.2.0/32'), IPv4Network('192.0.2.2/31'),
+                IPv4Network('192.0.2.4/30'), IPv4Network('192.0.2.8/29')]
+
+        or IPv6:
+
+            addr1 = ip_network('2001:db8::1/32')
+            addr2 = ip_network('2001:db8::1/128')
+            addr1.address_exclude(addr2) =
+                [ip_network('2001:db8::1/128'),
+                ip_network('2001:db8::2/127'),
+                ip_network('2001:db8::4/126'),
+                ip_network('2001:db8::8/125'),
+                ...
+                ip_network('2001:db8:8000::/33')]
+
+        Args:
+            other: An IPv4Network or IPv6Network object of the same type.
+
+        Returns:
+            An iterator of the IPv(4|6)Network objects which is self
+            minus other.
+
+        Raises:
+            TypeError: If self and other are of differing address
+              versions, or if other is not a network object.
+            ValueError: If other is not completely contained by self.
+
+        """
+        if not self._version == other._version:
+            raise TypeError("%s and %s are not of the same version" % (
+                            self, other))
+
+        if not isinstance(other, _BaseNetwork):
+            raise TypeError("%s is not a network object" % other)
+
+        if not other.subnet_of(self):
+            raise ValueError('%s not contained in %s' % (other, self))
+        if other == self:
+            return
+
+        # Make sure we're comparing the network of other.
+        other = other.__class__('%s/%s' % (other.network_address,
+                                           other.prefixlen))
+
+        s1, s2 = self.subnets()
+        while s1 != other and s2 != other:
+            if other.subnet_of(s1):
+                yield s2
+                s1, s2 = s1.subnets()
+            elif other.subnet_of(s2):
+                yield s1
+                s1, s2 = s2.subnets()
+            else:
+                # If we got here, there's a bug somewhere.
+                raise AssertionError('Error performing exclusion: '
+                                     's1: %s s2: %s other: %s' %
+                                     (s1, s2, other))
+        if s1 == other:
+            yield s2
+        elif s2 == other:
+            yield s1
+        else:
+            # If we got here, there's a bug somewhere.
+            raise AssertionError('Error performing exclusion: '
+                                 's1: %s s2: %s other: %s' %
+                                 (s1, s2, other))
+
+    def compare_networks(self, other):
+        """Compare two IP objects.
+
+        This is only concerned about the comparison of the integer
+        representation of the network addresses.  This means that the
+        host bits aren't considered at all in this method.  If you want
+        to compare host bits, you can easily enough do a
+        'HostA._ip < HostB._ip'
+
+        Args:
+            other: An IP object.
+
+        Returns:
+            If the IP versions of self and other are the same, returns:
+
+            -1 if self < other:
+              eg: IPv4Network('192.0.2.0/25') < IPv4Network('192.0.2.128/25')
+              IPv6Network('2001:db8::1000/124') <
+                  IPv6Network('2001:db8::2000/124')
+            0 if self == other
+              eg: IPv4Network('192.0.2.0/24') == IPv4Network('192.0.2.0/24')
+              IPv6Network('2001:db8::1000/124') ==
+                  IPv6Network('2001:db8::1000/124')
+            1 if self > other
+              eg: IPv4Network('192.0.2.128/25') > IPv4Network('192.0.2.0/25')
+                  IPv6Network('2001:db8::2000/124') >
+                      IPv6Network('2001:db8::1000/124')
+
+          Raises:
+              TypeError if the IP versions are different.
+
+        """
+        # does this need to raise a ValueError?
+        if self._version != other._version:
+            raise TypeError('%s and %s are not of the same type' % (
+                            self, other))
+        # self._version == other._version below here:
+        if self.network_address < other.network_address:
+            return -1
+        if self.network_address > other.network_address:
+            return 1
+        # self.network_address == other.network_address below here:
+        if self.netmask < other.netmask:
+            return -1
+        if self.netmask > other.netmask:
+            return 1
+        return 0
+
+    def _get_networks_key(self):
+        """Network-only key function.
+
+        Returns an object that identifies this address' network and
+        netmask. This function is a suitable "key" argument for sorted()
+        and list.sort().
+
+        """
+        return (self._version, self.network_address, self.netmask)
+
+    def subnets(self, prefixlen_diff=1, new_prefix=None):
+        """The subnets which join to make the current subnet.
+
+        In the case that self contains only one IP
+        (self._prefixlen == 32 for IPv4 or self._prefixlen == 128
+        for IPv6), yield an iterator with just ourself.
+
+        Args:
+            prefixlen_diff: An integer, the amount the prefix length
+              should be increased by. This should not be set if
+              new_prefix is also set.
+            new_prefix: The desired new prefix length. This must be a
+              larger number (smaller prefix) than the existing prefix.
+              This should not be set if prefixlen_diff is also set.
+
+        Returns:
+            An iterator of IPv(4|6) objects.
+
+        Raises:
+            ValueError: The prefixlen_diff is too small or too large.
+                OR
+            prefixlen_diff and new_prefix are both set or new_prefix
+              is a smaller number than the current prefix (smaller
+              number means a larger network)
+
+        """
+        if self._prefixlen == self._max_prefixlen:
+            yield self
+            return
+
+        if new_prefix is not None:
+            if new_prefix < self._prefixlen:
+                raise ValueError('new prefix must be longer')
+            if prefixlen_diff != 1:
+                raise ValueError('cannot set prefixlen_diff and new_prefix')
+            prefixlen_diff = new_prefix - self._prefixlen
+
+        if prefixlen_diff < 0:
+            raise ValueError('prefix length diff must be > 0')
+        new_prefixlen = self._prefixlen + prefixlen_diff
+
+        if new_prefixlen > self._max_prefixlen:
+            raise ValueError(
+                'prefix length diff %d is invalid for netblock %s' % (
+                    new_prefixlen, self))
+
+        start = int(self.network_address)
+        end = int(self.broadcast_address)
+        step = (int(self.hostmask) + 1) >> prefixlen_diff
+        for new_addr in _compat_range(start, end, step):
+            current = self.__class__((new_addr, new_prefixlen))
+            yield current
+
+    def supernet(self, prefixlen_diff=1, new_prefix=None):
+        """The supernet containing the current network.
+
+        Args:
+            prefixlen_diff: An integer, the amount the prefix length of
+              the network should be decreased by.  For example, given a
+              /24 network and a prefixlen_diff of 3, a supernet with a
+              /21 netmask is returned.
+
+        Returns:
+            An IPv4 network object.
+
+        Raises:
+            ValueError: If self.prefixlen - prefixlen_diff < 0. I.e., you have
+              a negative prefix length.
+                OR
+            If prefixlen_diff and new_prefix are both set or new_prefix is a
+              larger number than the current prefix (larger number means a
+              smaller network)
+
+        """
+        if self._prefixlen == 0:
+            return self
+
+        if new_prefix is not None:
+            if new_prefix > self._prefixlen:
+                raise ValueError('new prefix must be shorter')
+            if prefixlen_diff != 1:
+                raise ValueError('cannot set prefixlen_diff and new_prefix')
+            prefixlen_diff = self._prefixlen - new_prefix
+
+        new_prefixlen = self.prefixlen - prefixlen_diff
+        if new_prefixlen < 0:
+            raise ValueError(
+                'current prefixlen is %d, cannot have a prefixlen_diff of %d' %
+                (self.prefixlen, prefixlen_diff))
+        return self.__class__((
+            int(self.network_address) & (int(self.netmask) << prefixlen_diff),
+            new_prefixlen
+        ))
+
+    @property
+    def is_multicast(self):
+        """Test if the address is reserved for multicast use.
+
+        Returns:
+            A boolean, True if the address is a multicast address.
+            See RFC 2373 2.7 for details.
+
+        """
+        return (self.network_address.is_multicast and
+                self.broadcast_address.is_multicast)
+
+    def subnet_of(self, other):
+        # always false if one is v4 and the other is v6.
+        if self._version != other._version:
+            return False
+        # dealing with another network.
+        if (hasattr(other, 'network_address') and
+                hasattr(other, 'broadcast_address')):
+            return (other.network_address <= self.network_address and
+                    other.broadcast_address >= self.broadcast_address)
+        # dealing with another address
+        else:
+            raise TypeError('Unable to test subnet containment with element '
+                            'of type %s' % type(other))
+
+    def supernet_of(self, other):
+        # always false if one is v4 and the other is v6.
+        if self._version != other._version:
+            return False
+        # dealing with another network.
+        if (hasattr(other, 'network_address') and
+                hasattr(other, 'broadcast_address')):
+            return (other.network_address >= self.network_address and
+                    other.broadcast_address <= self.broadcast_address)
+        # dealing with another address
+        else:
+            raise TypeError('Unable to test subnet containment with element '
+                            'of type %s' % type(other))
+
+    @property
+    def is_reserved(self):
+        """Test if the address is otherwise IETF reserved.
+
+        Returns:
+            A boolean, True if the address is within one of the
+            reserved IPv6 Network ranges.
+
+        """
+        return (self.network_address.is_reserved and
+                self.broadcast_address.is_reserved)
+
+    @property
+    def is_link_local(self):
+        """Test if the address is reserved for link-local.
+
+        Returns:
+            A boolean, True if the address is reserved per RFC 4291.
+
+        """
+        return (self.network_address.is_link_local and
+                self.broadcast_address.is_link_local)
+
+    @property
+    def is_private(self):
+        """Test if this address is allocated for private networks.
+
+        Returns:
+            A boolean, True if the address is reserved per
+            iana-ipv4-special-registry or iana-ipv6-special-registry.
+
+        """
+        return (self.network_address.is_private and
+                self.broadcast_address.is_private)
+
+    @property
+    def is_global(self):
+        """Test if this address is allocated for public networks.
+
+        Returns:
+            A boolean, True if the address is not reserved per
+            iana-ipv4-special-registry or iana-ipv6-special-registry.
+
+        """
+        return not self.is_private
+
+    @property
+    def is_unspecified(self):
+        """Test if the address is unspecified.
+
+        Returns:
+            A boolean, True if this is the unspecified address as defined in
+            RFC 2373 2.5.2.
+
+        """
+        return (self.network_address.is_unspecified and
+                self.broadcast_address.is_unspecified)
+
+    @property
+    def is_loopback(self):
+        """Test if the address is a loopback address.
+
+        Returns:
+            A boolean, True if the address is a loopback address as defined in
+            RFC 2373 2.5.3.
+
+        """
+        return (self.network_address.is_loopback and
+                self.broadcast_address.is_loopback)
+
+
+class _BaseV4(object):
+
+    """Base IPv4 object.
+
+    The following methods are used by IPv4 objects in both single IP
+    addresses and networks.
+
+    """
+
+    __slots__ = ()
+    _version = 4
+    # Equivalent to 255.255.255.255 or 32 bits of 1's.
+    _ALL_ONES = (2 ** IPV4LENGTH) - 1
+    _DECIMAL_DIGITS = frozenset('0123456789')
+
+    # the valid octets for host and netmasks. only useful for IPv4.
+    _valid_mask_octets = frozenset([255, 254, 252, 248, 240, 224, 192, 128, 0])
+
+    _max_prefixlen = IPV4LENGTH
+    # There are only a handful of valid v4 netmasks, so we cache them all
+    # when constructed (see _make_netmask()).
+    _netmask_cache = {}
+
+    def _explode_shorthand_ip_string(self):
+        return _compat_str(self)
+
+    @classmethod
+    def _make_netmask(cls, arg):
+        """Make a (netmask, prefix_len) tuple from the given argument.
+
+        Argument can be:
+        - an integer (the prefix length)
+        - a string representing the prefix length (e.g. "24")
+        - a string representing the prefix netmask (e.g. "255.255.255.0")
+        """
+        if arg not in cls._netmask_cache:
+            if isinstance(arg, _compat_int_types):
+                prefixlen = arg
+            else:
+                try:
+                    # Check for a netmask in prefix length form
+                    prefixlen = cls._prefix_from_prefix_string(arg)
+                except NetmaskValueError:
+                    # Check for a netmask or hostmask in dotted-quad form.
+                    # This may raise NetmaskValueError.
+                    prefixlen = cls._prefix_from_ip_string(arg)
+            netmask = IPv4Address(cls._ip_int_from_prefix(prefixlen))
+            cls._netmask_cache[arg] = netmask, prefixlen
+        return cls._netmask_cache[arg]
+
+    @classmethod
+    def _ip_int_from_string(cls, ip_str):
+        """Turn the given IP string into an integer for comparison.
+
+        Args:
+            ip_str: A string, the IP ip_str.
+
+        Returns:
+            The IP ip_str as an integer.
+
+        Raises:
+            AddressValueError: if ip_str isn't a valid IPv4 Address.
+
+        """
+        if not ip_str:
+            raise AddressValueError('Address cannot be empty')
+
+        octets = ip_str.split('.')
+        if len(octets) != 4:
+            raise AddressValueError("Expected 4 octets in %r" % ip_str)
+
+        try:
+            return _compat_int_from_byte_vals(
+                map(cls._parse_octet, octets), 'big')
+        except ValueError as exc:
+            raise AddressValueError("%s in %r" % (exc, ip_str))
+
+    @classmethod
+    def _parse_octet(cls, octet_str):
+        """Convert a decimal octet into an integer.
+
+        Args:
+            octet_str: A string, the number to parse.
+
+        Returns:
+            The octet as an integer.
+
+        Raises:
+            ValueError: if the octet isn't strictly a decimal from [0..255].
+
+        """
+        if not octet_str:
+            raise ValueError("Empty octet not permitted")
+        # Whitelist the characters, since int() allows a lot of bizarre stuff.
+        if not cls._DECIMAL_DIGITS.issuperset(octet_str):
+            msg = "Only decimal digits permitted in %r"
+            raise ValueError(msg % octet_str)
+        # We do the length check second, since the invalid character error
+        # is likely to be more informative for the user
+        if len(octet_str) > 3:
+            msg = "At most 3 characters permitted in %r"
+            raise ValueError(msg % octet_str)
+        # Convert to integer (we know digits are legal)
+        octet_int = int(octet_str, 10)
+        # Any octets that look like they *might* be written in octal,
+        # and which don't look exactly the same in both octal and
+        # decimal are rejected as ambiguous
+        if octet_int > 7 and octet_str[0] == '0':
+            msg = "Ambiguous (octal/decimal) value in %r not permitted"
+            raise ValueError(msg % octet_str)
+        if octet_int > 255:
+            raise ValueError("Octet %d (> 255) not permitted" % octet_int)
+        return octet_int
+
+    @classmethod
+    def _string_from_ip_int(cls, ip_int):
+        """Turns a 32-bit integer into dotted decimal notation.
+
+        Args:
+            ip_int: An integer, the IP address.
+
+        Returns:
+            The IP address as a string in dotted decimal notation.
+
+        """
+        return '.'.join(_compat_str(struct.unpack(b'!B', b)[0]
+                                    if isinstance(b, bytes)
+                                    else b)
+                        for b in _compat_to_bytes(ip_int, 4, 'big'))
+
+    def _is_hostmask(self, ip_str):
+        """Test if the IP string is a hostmask (rather than a netmask).
+
+        Args:
+            ip_str: A string, the potential hostmask.
+
+        Returns:
+            A boolean, True if the IP string is a hostmask.
+
+        """
+        bits = ip_str.split('.')
+        try:
+            parts = [x for x in map(int, bits) if x in self._valid_mask_octets]
+        except ValueError:
+            return False
+        if len(parts) != len(bits):
+            return False
+        if parts[0] < parts[-1]:
+            return True
+        return False
+
+    def _reverse_pointer(self):
+        """Return the reverse DNS pointer name for the IPv4 address.
+
+        This implements the method described in RFC1035 3.5.
+
+        """
+        reverse_octets = _compat_str(self).split('.')[::-1]
+        return '.'.join(reverse_octets) + '.in-addr.arpa'
+
+    @property
+    def max_prefixlen(self):
+        return self._max_prefixlen
+
+    @property
+    def version(self):
+        return self._version
+
+
+class IPv4Address(_BaseV4, _BaseAddress):
+
+    """Represent and manipulate single IPv4 Addresses."""
+
+    __slots__ = ('_ip', '__weakref__')
+
+    def __init__(self, address):
+
+        """
+        Args:
+            address: A string or integer representing the IP
+
+              Additionally, an integer can be passed, so
+              IPv4Address('192.0.2.1') == IPv4Address(3221225985).
+              or, more generally
+              IPv4Address(int(IPv4Address('192.0.2.1'))) ==
+                IPv4Address('192.0.2.1')
+
+        Raises:
+            AddressValueError: If ipaddress isn't a valid IPv4 address.
+
+        """
+        # Efficient constructor from integer.
+        if isinstance(address, _compat_int_types):
+            self._check_int_address(address)
+            self._ip = address
+            return
+
+        # Constructing from a packed address
+        if isinstance(address, bytes):
+            self._check_packed_address(address, 4)
+            bvs = _compat_bytes_to_byte_vals(address)
+            self._ip = _compat_int_from_byte_vals(bvs, 'big')
+            return
+
+        # Assume input argument to be string or any object representation
+        # which converts into a formatted IP string.
+        addr_str = _compat_str(address)
+        if '/' in addr_str:
+            raise AddressValueError("Unexpected '/' in %r" % address)
+        self._ip = self._ip_int_from_string(addr_str)
+
+    @property
+    def packed(self):
+        """The binary representation of this address."""
+        return v4_int_to_packed(self._ip)
+
+    @property
+    def is_reserved(self):
+        """Test if the address is otherwise IETF reserved.
+
+         Returns:
+             A boolean, True if the address is within the
+             reserved IPv4 Network range.
+
+        """
+        return self in self._constants._reserved_network
+
+    @property
+    def is_private(self):
+        """Test if this address is allocated for private networks.
+
+        Returns:
+            A boolean, True if the address is reserved per
+            iana-ipv4-special-registry.
+
+        """
+        return any(self in net for net in self._constants._private_networks)
+
+    @property
+    def is_multicast(self):
+        """Test if the address is reserved for multicast use.
+
+        Returns:
+            A boolean, True if the address is multicast.
+            See RFC 3171 for details.
+
+        """
+        return self in self._constants._multicast_network
+
+    @property
+    def is_unspecified(self):
+        """Test if the address is unspecified.
+
+        Returns:
+            A boolean, True if this is the unspecified address as defined in
+            RFC 5735 3.
+
+        """
+        return self == self._constants._unspecified_address
+
+    @property
+    def is_loopback(self):
+        """Test if the address is a loopback address.
+
+        Returns:
+            A boolean, True if the address is a loopback per RFC 3330.
+
+        """
+        return self in self._constants._loopback_network
+
+    @property
+    def is_link_local(self):
+        """Test if the address is reserved for link-local.
+
+        Returns:
+            A boolean, True if the address is link-local per RFC 3927.
+
+        """
+        return self in self._constants._linklocal_network
+
+
+class IPv4Interface(IPv4Address):
+
+    def __init__(self, address):
+        if isinstance(address, (bytes, _compat_int_types)):
+            IPv4Address.__init__(self, address)
+            self.network = IPv4Network(self._ip)
+            self._prefixlen = self._max_prefixlen
+            return
+
+        if isinstance(address, tuple):
+            IPv4Address.__init__(self, address[0])
+            if len(address) > 1:
+                self._prefixlen = int(address[1])
+            else:
+                self._prefixlen = self._max_prefixlen
+
+            self.network = IPv4Network(address, strict=False)
+            self.netmask = self.network.netmask
+            self.hostmask = self.network.hostmask
+            return
+
+        addr = _split_optional_netmask(address)
+        IPv4Address.__init__(self, addr[0])
+
+        self.network = IPv4Network(address, strict=False)
+        self._prefixlen = self.network._prefixlen
+
+        self.netmask = self.network.netmask
+        self.hostmask = self.network.hostmask
+
+    def __str__(self):
+        return '%s/%d' % (self._string_from_ip_int(self._ip),
+                          self.network.prefixlen)
+
+    def __eq__(self, other):
+        address_equal = IPv4Address.__eq__(self, other)
+        if not address_equal or address_equal is NotImplemented:
+            return address_equal
+        try:
+            return self.network == other.network
+        except AttributeError:
+            # An interface with an associated network is NOT the
+            # same as an unassociated address. That's why the hash
+            # takes the extra info into account.
+            return False
+
+    def __lt__(self, other):
+        address_less = IPv4Address.__lt__(self, other)
+        if address_less is NotImplemented:
+            return NotImplemented
+        try:
+            return self.network < other.network
+        except AttributeError:
+            # We *do* allow addresses and interfaces to be sorted. The
+            # unassociated address is considered less than all interfaces.
+            return False
+
+    def __hash__(self):
+        return self._ip ^ self._prefixlen ^ int(self.network.network_address)
+
+    __reduce__ = _IPAddressBase.__reduce__
+
+    @property
+    def ip(self):
+        return IPv4Address(self._ip)
+
+    @property
+    def with_prefixlen(self):
+        return '%s/%s' % (self._string_from_ip_int(self._ip),
+                          self._prefixlen)
+
+    @property
+    def with_netmask(self):
+        return '%s/%s' % (self._string_from_ip_int(self._ip),
+                          self.netmask)
+
+    @property
+    def with_hostmask(self):
+        return '%s/%s' % (self._string_from_ip_int(self._ip),
+                          self.hostmask)
+
+
+class IPv4Network(_BaseV4, _BaseNetwork):
+
+    """This class represents and manipulates 32-bit IPv4 network + addresses..
+
+    Attributes: [examples for IPv4Network('192.0.2.0/27')]
+        .network_address: IPv4Address('192.0.2.0')
+        .hostmask: IPv4Address('0.0.0.31')
+        .broadcast_address: IPv4Address('192.0.2.32')
+        .netmask: IPv4Address('255.255.255.224')
+        .prefixlen: 27
+
+    """
+    # Class to use when creating address objects
+    _address_class = IPv4Address
+
+    def __init__(self, address, strict=True):
+
+        """Instantiate a new IPv4 network object.
+
+        Args:
+            address: A string or integer representing the IP [& network].
+              '192.0.2.0/24'
+              '192.0.2.0/255.255.255.0'
+              '192.0.0.2/0.0.0.255'
+              are all functionally the same in IPv4. Similarly,
+              '192.0.2.1'
+              '192.0.2.1/255.255.255.255'
+              '192.0.2.1/32'
+              are also functionally equivalent. That is to say, failing to
+              provide a subnetmask will create an object with a mask of /32.
+
+              If the mask (portion after the / in the argument) is given in
+              dotted quad form, it is treated as a netmask if it starts with a
+              non-zero field (e.g. /255.0.0.0 == /8) and as a hostmask if it
+              starts with a zero field (e.g. 0.255.255.255 == /8), with the
+              single exception of an all-zero mask which is treated as a
+              netmask == /0. If no mask is given, a default of /32 is used.
+
+              Additionally, an integer can be passed, so
+              IPv4Network('192.0.2.1') == IPv4Network(3221225985)
+              or, more generally
+              IPv4Interface(int(IPv4Interface('192.0.2.1'))) ==
+                IPv4Interface('192.0.2.1')
+
+        Raises:
+            AddressValueError: If ipaddress isn't a valid IPv4 address.
+            NetmaskValueError: If the netmask isn't valid for
+              an IPv4 address.
+            ValueError: If strict is True and a network address is not
+              supplied.
+
+        """
+        _BaseNetwork.__init__(self, address)
+
+        # Constructing from a packed address or integer
+        if isinstance(address, (_compat_int_types, bytes)):
+            self.network_address = IPv4Address(address)
+            self.netmask, self._prefixlen = self._make_netmask(
+                self._max_prefixlen)
+            # fixme: address/network test here.
+            return
+
+        if isinstance(address, tuple):
+            if len(address) > 1:
+                arg = address[1]
+            else:
+                # We weren't given an address[1]
+                arg = self._max_prefixlen
+            self.network_address = IPv4Address(address[0])
+            self.netmask, self._prefixlen = self._make_netmask(arg)
+            packed = int(self.network_address)
+            if packed & int(self.netmask) != packed:
+                if strict:
+                    raise ValueError('%s has host bits set' % self)
+                else:
+                    self.network_address = IPv4Address(packed &
+                                                       int(self.netmask))
+            return
+
+        # Assume input argument to be string or any object representation
+        # which converts into a formatted IP prefix string.
+        addr = _split_optional_netmask(address)
+        self.network_address = IPv4Address(self._ip_int_from_string(addr[0]))
+
+        if len(addr) == 2:
+            arg = addr[1]
+        else:
+            arg = self._max_prefixlen
+        self.netmask, self._prefixlen = self._make_netmask(arg)
+
+        if strict:
+            if (IPv4Address(int(self.network_address) & int(self.netmask)) !=
+                    self.network_address):
+                raise ValueError('%s has host bits set' % self)
+        self.network_address = IPv4Address(int(self.network_address) &
+                                           int(self.netmask))
+
+        if self._prefixlen == (self._max_prefixlen - 1):
+            self.hosts = self.__iter__
+
+    @property
+    def is_global(self):
+        """Test if this address is allocated for public networks.
+
+        Returns:
+            A boolean, True if the address is not reserved per
+            iana-ipv4-special-registry.
+
+        """
+        return (not (self.network_address in IPv4Network('100.64.0.0/10') and
+                self.broadcast_address in IPv4Network('100.64.0.0/10')) and
+                not self.is_private)
+
+
+class _IPv4Constants(object):
+
+    _linklocal_network = IPv4Network('169.254.0.0/16')
+
+    _loopback_network = IPv4Network('127.0.0.0/8')
+
+    _multicast_network = IPv4Network('224.0.0.0/4')
+
+    _private_networks = [
+        IPv4Network('0.0.0.0/8'),
+        IPv4Network('10.0.0.0/8'),
+        IPv4Network('127.0.0.0/8'),
+        IPv4Network('169.254.0.0/16'),
+        IPv4Network('172.16.0.0/12'),
+        IPv4Network('192.0.0.0/29'),
+        IPv4Network('192.0.0.170/31'),
+        IPv4Network('192.0.2.0/24'),
+        IPv4Network('192.168.0.0/16'),
+        IPv4Network('198.18.0.0/15'),
+        IPv4Network('198.51.100.0/24'),
+        IPv4Network('203.0.113.0/24'),
+        IPv4Network('240.0.0.0/4'),
+        IPv4Network('255.255.255.255/32'),
+    ]
+
+    _reserved_network = IPv4Network('240.0.0.0/4')
+
+    _unspecified_address = IPv4Address('0.0.0.0')
+
+
+IPv4Address._constants = _IPv4Constants
+
+
+class _BaseV6(object):
+
+    """Base IPv6 object.
+
+    The following methods are used by IPv6 objects in both single IP
+    addresses and networks.
+
+    """
+
+    __slots__ = ()
+    _version = 6
+    _ALL_ONES = (2 ** IPV6LENGTH) - 1
+    _HEXTET_COUNT = 8
+    _HEX_DIGITS = frozenset('0123456789ABCDEFabcdef')
+    _max_prefixlen = IPV6LENGTH
+
+    # There are only a bunch of valid v6 netmasks, so we cache them all
+    # when constructed (see _make_netmask()).
+    _netmask_cache = {}
+
+    @classmethod
+    def _make_netmask(cls, arg):
+        """Make a (netmask, prefix_len) tuple from the given argument.
+
+        Argument can be:
+        - an integer (the prefix length)
+        - a string representing the prefix length (e.g. "24")
+        - a string representing the prefix netmask (e.g. "255.255.255.0")
+        """
+        if arg not in cls._netmask_cache:
+            if isinstance(arg, _compat_int_types):
+                prefixlen = arg
+            else:
+                prefixlen = cls._prefix_from_prefix_string(arg)
+            netmask = IPv6Address(cls._ip_int_from_prefix(prefixlen))
+            cls._netmask_cache[arg] = netmask, prefixlen
+        return cls._netmask_cache[arg]
+
+    @classmethod
+    def _ip_int_from_string(cls, ip_str):
+        """Turn an IPv6 ip_str into an integer.
+
+        Args:
+            ip_str: A string, the IPv6 ip_str.
+
+        Returns:
+            An int, the IPv6 address
+
+        Raises:
+            AddressValueError: if ip_str isn't a valid IPv6 Address.
+
+        """
+        if not ip_str:
+            raise AddressValueError('Address cannot be empty')
+
+        parts = ip_str.split(':')
+
+        # An IPv6 address needs at least 2 colons (3 parts).
+        _min_parts = 3
+        if len(parts) < _min_parts:
+            msg = "At least %d parts expected in %r" % (_min_parts, ip_str)
+            raise AddressValueError(msg)
+
+        # If the address has an IPv4-style suffix, convert it to hexadecimal.
+        if '.' in parts[-1]:
+            try:
+                ipv4_int = IPv4Address(parts.pop())._ip
+            except AddressValueError as exc:
+                raise AddressValueError("%s in %r" % (exc, ip_str))
+            parts.append('%x' % ((ipv4_int >> 16) & 0xFFFF))
+            parts.append('%x' % (ipv4_int & 0xFFFF))
+
+        # An IPv6 address can't have more than 8 colons (9 parts).
+        # The extra colon comes from using the "::" notation for a single
+        # leading or trailing zero part.
+        _max_parts = cls._HEXTET_COUNT + 1
+        if len(parts) > _max_parts:
+            msg = "At most %d colons permitted in %r" % (
+                _max_parts - 1, ip_str)
+            raise AddressValueError(msg)
+
+        # Disregarding the endpoints, find '::' with nothing in between.
+        # This indicates that a run of zeroes has been skipped.
+        skip_index = None
+        for i in _compat_range(1, len(parts) - 1):
+            if not parts[i]:
+                if skip_index is not None:
+                    # Can't have more than one '::'
+                    msg = "At most one '::' permitted in %r" % ip_str
+                    raise AddressValueError(msg)
+                skip_index = i
+
+        # parts_hi is the number of parts to copy from above/before the '::'
+        # parts_lo is the number of parts to copy from below/after the '::'
+        if skip_index is not None:
+            # If we found a '::', then check if it also covers the endpoints.
+            parts_hi = skip_index
+            parts_lo = len(parts) - skip_index - 1
+            if not parts[0]:
+                parts_hi -= 1
+                if parts_hi:
+                    msg = "Leading ':' only permitted as part of '::' in %r"
+                    raise AddressValueError(msg % ip_str)  # ^: requires ^::
+            if not parts[-1]:
+                parts_lo -= 1
+                if parts_lo:
+                    msg = "Trailing ':' only permitted as part of '::' in %r"
+                    raise AddressValueError(msg % ip_str)  # :$ requires ::$
+            parts_skipped = cls._HEXTET_COUNT - (parts_hi + parts_lo)
+            if parts_skipped < 1:
+                msg = "Expected at most %d other parts with '::' in %r"
+                raise AddressValueError(msg % (cls._HEXTET_COUNT - 1, ip_str))
+        else:
+            # Otherwise, allocate the entire address to parts_hi.  The
+            # endpoints could still be empty, but _parse_hextet() will check
+            # for that.
+            if len(parts) != cls._HEXTET_COUNT:
+                msg = "Exactly %d parts expected without '::' in %r"
+                raise AddressValueError(msg % (cls._HEXTET_COUNT, ip_str))
+            if not parts[0]:
+                msg = "Leading ':' only permitted as part of '::' in %r"
+                raise AddressValueError(msg % ip_str)  # ^: requires ^::
+            if not parts[-1]:
+                msg = "Trailing ':' only permitted as part of '::' in %r"
+                raise AddressValueError(msg % ip_str)  # :$ requires ::$
+            parts_hi = len(parts)
+            parts_lo = 0
+            parts_skipped = 0
+
+        try:
+            # Now, parse the hextets into a 128-bit integer.
+            ip_int = 0
+            for i in range(parts_hi):
+                ip_int <<= 16
+                ip_int |= cls._parse_hextet(parts[i])
+            ip_int <<= 16 * parts_skipped
+            for i in range(-parts_lo, 0):
+                ip_int <<= 16
+                ip_int |= cls._parse_hextet(parts[i])
+            return ip_int
+        except ValueError as exc:
+            raise AddressValueError("%s in %r" % (exc, ip_str))
+
+    @classmethod
+    def _parse_hextet(cls, hextet_str):
+        """Convert an IPv6 hextet string into an integer.
+
+        Args:
+            hextet_str: A string, the number to parse.
+
+        Returns:
+            The hextet as an integer.
+
+        Raises:
+            ValueError: if the input isn't strictly a hex number from
+              [0..FFFF].
+
+        """
+        # Whitelist the characters, since int() allows a lot of bizarre stuff.
+        if not cls._HEX_DIGITS.issuperset(hextet_str):
+            raise ValueError("Only hex digits permitted in %r" % hextet_str)
+        # We do the length check second, since the invalid character error
+        # is likely to be more informative for the user
+        if len(hextet_str) > 4:
+            msg = "At most 4 characters permitted in %r"
+            raise ValueError(msg % hextet_str)
+        # Length check means we can skip checking the integer value
+        return int(hextet_str, 16)
+
+    @classmethod
+    def _compress_hextets(cls, hextets):
+        """Compresses a list of hextets.
+
+        Compresses a list of strings, replacing the longest continuous
+        sequence of "0" in the list with "" and adding empty strings at
+        the beginning or at the end of the string such that subsequently
+        calling ":".join(hextets) will produce the compressed version of
+        the IPv6 address.
+
+        Args:
+            hextets: A list of strings, the hextets to compress.
+
+        Returns:
+            A list of strings.
+
+        """
+        best_doublecolon_start = -1
+        best_doublecolon_len = 0
+        doublecolon_start = -1
+        doublecolon_len = 0
+        for index, hextet in enumerate(hextets):
+            if hextet == '0':
+                doublecolon_len += 1
+                if doublecolon_start == -1:
+                    # Start of a sequence of zeros.
+                    doublecolon_start = index
+                if doublecolon_len > best_doublecolon_len:
+                    # This is the longest sequence of zeros so far.
+                    best_doublecolon_len = doublecolon_len
+                    best_doublecolon_start = doublecolon_start
+            else:
+                doublecolon_len = 0
+                doublecolon_start = -1
+
+        if best_doublecolon_len > 1:
+            best_doublecolon_end = (best_doublecolon_start +
+                                    best_doublecolon_len)
+            # For zeros at the end of the address.
+            if best_doublecolon_end == len(hextets):
+                hextets += ['']
+            hextets[best_doublecolon_start:best_doublecolon_end] = ['']
+            # For zeros at the beginning of the address.
+            if best_doublecolon_start == 0:
+                hextets = [''] + hextets
+
+        return hextets
+
+    @classmethod
+    def _string_from_ip_int(cls, ip_int=None):
+        """Turns a 128-bit integer into hexadecimal notation.
+
+        Args:
+            ip_int: An integer, the IP address.
+
+        Returns:
+            A string, the hexadecimal representation of the address.
+
+        Raises:
+            ValueError: The address is bigger than 128 bits of all ones.
+
+        """
+        if ip_int is None:
+            ip_int = int(cls._ip)
+
+        if ip_int > cls._ALL_ONES:
+            raise ValueError('IPv6 address is too large')
+
+        hex_str = '%032x' % ip_int
+        hextets = ['%x' % int(hex_str[x:x + 4], 16) for x in range(0, 32, 4)]
+
+        hextets = cls._compress_hextets(hextets)
+        return ':'.join(hextets)
+
+    def _explode_shorthand_ip_string(self):
+        """Expand a shortened IPv6 address.
+
+        Args:
+            ip_str: A string, the IPv6 address.
+
+        Returns:
+            A string, the expanded IPv6 address.
+
+        """
+        if isinstance(self, IPv6Network):
+            ip_str = _compat_str(self.network_address)
+        elif isinstance(self, IPv6Interface):
+            ip_str = _compat_str(self.ip)
+        else:
+            ip_str = _compat_str(self)
+
+        ip_int = self._ip_int_from_string(ip_str)
+        hex_str = '%032x' % ip_int
+        parts = [hex_str[x:x + 4] for x in range(0, 32, 4)]
+        if isinstance(self, (_BaseNetwork, IPv6Interface)):
+            return '%s/%d' % (':'.join(parts), self._prefixlen)
+        return ':'.join(parts)
+
+    def _reverse_pointer(self):
+        """Return the reverse DNS pointer name for the IPv6 address.
+
+        This implements the method described in RFC3596 2.5.
+
+        """
+        reverse_chars = self.exploded[::-1].replace(':', '')
+        return '.'.join(reverse_chars) + '.ip6.arpa'
+
+    @property
+    def max_prefixlen(self):
+        return self._max_prefixlen
+
+    @property
+    def version(self):
+        return self._version
+
+
+class IPv6Address(_BaseV6, _BaseAddress):
+
+    """Represent and manipulate single IPv6 Addresses."""
+
+    __slots__ = ('_ip', '__weakref__')
+
+    def __init__(self, address):
+        """Instantiate a new IPv6 address object.
+
+        Args:
+            address: A string or integer representing the IP
+
+              Additionally, an integer can be passed, so
+              IPv6Address('2001:db8::') ==
+                IPv6Address(42540766411282592856903984951653826560)
+              or, more generally
+              IPv6Address(int(IPv6Address('2001:db8::'))) ==
+                IPv6Address('2001:db8::')
+
+        Raises:
+            AddressValueError: If address isn't a valid IPv6 address.
+
+        """
+        # Efficient constructor from integer.
+        if isinstance(address, _compat_int_types):
+            self._check_int_address(address)
+            self._ip = address
+            return
+
+        # Constructing from a packed address
+        if isinstance(address, bytes):
+            self._check_packed_address(address, 16)
+            bvs = _compat_bytes_to_byte_vals(address)
+            self._ip = _compat_int_from_byte_vals(bvs, 'big')
+            return
+
+        # Assume input argument to be string or any object representation
+        # which converts into a formatted IP string.
+        addr_str = _compat_str(address)
+        if '/' in addr_str:
+            raise AddressValueError("Unexpected '/' in %r" % address)
+        self._ip = self._ip_int_from_string(addr_str)
+
+    @property
+    def packed(self):
+        """The binary representation of this address."""
+        return v6_int_to_packed(self._ip)
+
+    @property
+    def is_multicast(self):
+        """Test if the address is reserved for multicast use.
+
+        Returns:
+            A boolean, True if the address is a multicast address.
+            See RFC 2373 2.7 for details.
+
+        """
+        return self in self._constants._multicast_network
+
+    @property
+    def is_reserved(self):
+        """Test if the address is otherwise IETF reserved.
+
+        Returns:
+            A boolean, True if the address is within one of the
+            reserved IPv6 Network ranges.
+
+        """
+        return any(self in x for x in self._constants._reserved_networks)
+
+    @property
+    def is_link_local(self):
+        """Test if the address is reserved for link-local.
+
+        Returns:
+            A boolean, True if the address is reserved per RFC 4291.
+
+        """
+        return self in self._constants._linklocal_network
+
+    @property
+    def is_site_local(self):
+        """Test if the address is reserved for site-local.
+
+        Note that the site-local address space has been deprecated by RFC 3879.
+        Use is_private to test if this address is in the space of unique local
+        addresses as defined by RFC 4193.
+
+        Returns:
+            A boolean, True if the address is reserved per RFC 3513 2.5.6.
+
+        """
+        return self in self._constants._sitelocal_network
+
+    @property
+    def is_private(self):
+        """Test if this address is allocated for private networks.
+
+        Returns:
+            A boolean, True if the address is reserved per
+            iana-ipv6-special-registry.
+
+        """
+        return any(self in net for net in self._constants._private_networks)
+
+    @property
+    def is_global(self):
+        """Test if this address is allocated for public networks.
+
+        Returns:
+            A boolean, true if the address is not reserved per
+            iana-ipv6-special-registry.
+
+        """
+        return not self.is_private
+
+    @property
+    def is_unspecified(self):
+        """Test if the address is unspecified.
+
+        Returns:
+            A boolean, True if this is the unspecified address as defined in
+            RFC 2373 2.5.2.
+
+        """
+        return self._ip == 0
+
+    @property
+    def is_loopback(self):
+        """Test if the address is a loopback address.
+
+        Returns:
+            A boolean, True if the address is a loopback address as defined in
+            RFC 2373 2.5.3.
+
+        """
+        return self._ip == 1
+
+    @property
+    def ipv4_mapped(self):
+        """Return the IPv4 mapped address.
+
+        Returns:
+            If the IPv6 address is a v4 mapped address, return the
+            IPv4 mapped address. Return None otherwise.
+
+        """
+        if (self._ip >> 32) != 0xFFFF:
+            return None
+        return IPv4Address(self._ip & 0xFFFFFFFF)
+
+    @property
+    def teredo(self):
+        """Tuple of embedded teredo IPs.
+
+        Returns:
+            Tuple of the (server, client) IPs or None if the address
+            doesn't appear to be a teredo address (doesn't start with
+            2001::/32)
+
+        """
+        if (self._ip >> 96) != 0x20010000:
+            return None
+        return (IPv4Address((self._ip >> 64) & 0xFFFFFFFF),
+                IPv4Address(~self._ip & 0xFFFFFFFF))
+
+    @property
+    def sixtofour(self):
+        """Return the IPv4 6to4 embedded address.
+
+        Returns:
+            The IPv4 6to4-embedded address if present or None if the
+            address doesn't appear to contain a 6to4 embedded address.
+
+        """
+        if (self._ip >> 112) != 0x2002:
+            return None
+        return IPv4Address((self._ip >> 80) & 0xFFFFFFFF)
+
+
+class IPv6Interface(IPv6Address):
+
+    def __init__(self, address):
+        if isinstance(address, (bytes, _compat_int_types)):
+            IPv6Address.__init__(self, address)
+            self.network = IPv6Network(self._ip)
+            self._prefixlen = self._max_prefixlen
+            return
+        if isinstance(address, tuple):
+            IPv6Address.__init__(self, address[0])
+            if len(address) > 1:
+                self._prefixlen = int(address[1])
+            else:
+                self._prefixlen = self._max_prefixlen
+            self.network = IPv6Network(address, strict=False)
+            self.netmask = self.network.netmask
+            self.hostmask = self.network.hostmask
+            return
+
+        addr = _split_optional_netmask(address)
+        IPv6Address.__init__(self, addr[0])
+        self.network = IPv6Network(address, strict=False)
+        self.netmask = self.network.netmask
+        self._prefixlen = self.network._prefixlen
+        self.hostmask = self.network.hostmask
+
+    def __str__(self):
+        return '%s/%d' % (self._string_from_ip_int(self._ip),
+                          self.network.prefixlen)
+
+    def __eq__(self, other):
+        address_equal = IPv6Address.__eq__(self, other)
+        if not address_equal or address_equal is NotImplemented:
+            return address_equal
+        try:
+            return self.network == other.network
+        except AttributeError:
+            # An interface with an associated network is NOT the
+            # same as an unassociated address. That's why the hash
+            # takes the extra info into account.
+            return False
+
+    def __lt__(self, other):
+        address_less = IPv6Address.__lt__(self, other)
+        if address_less is NotImplemented:
+            return NotImplemented
+        try:
+            return self.network < other.network
+        except AttributeError:
+            # We *do* allow addresses and interfaces to be sorted. The
+            # unassociated address is considered less than all interfaces.
+            return False
+
+    def __hash__(self):
+        return self._ip ^ self._prefixlen ^ int(self.network.network_address)
+
+    __reduce__ = _IPAddressBase.__reduce__
+
+    @property
+    def ip(self):
+        return IPv6Address(self._ip)
+
+    @property
+    def with_prefixlen(self):
+        return '%s/%s' % (self._string_from_ip_int(self._ip),
+                          self._prefixlen)
+
+    @property
+    def with_netmask(self):
+        return '%s/%s' % (self._string_from_ip_int(self._ip),
+                          self.netmask)
+
+    @property
+    def with_hostmask(self):
+        return '%s/%s' % (self._string_from_ip_int(self._ip),
+                          self.hostmask)
+
+    @property
+    def is_unspecified(self):
+        return self._ip == 0 and self.network.is_unspecified
+
+    @property
+    def is_loopback(self):
+        return self._ip == 1 and self.network.is_loopback
+
+
+class IPv6Network(_BaseV6, _BaseNetwork):
+
+    """This class represents and manipulates 128-bit IPv6 networks.
+
+    Attributes: [examples for IPv6('2001:db8::1000/124')]
+        .network_address: IPv6Address('2001:db8::1000')
+        .hostmask: IPv6Address('::f')
+        .broadcast_address: IPv6Address('2001:db8::100f')
+        .netmask: IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0')
+        .prefixlen: 124
+
+    """
+
+    # Class to use when creating address objects
+    _address_class = IPv6Address
+
+    def __init__(self, address, strict=True):
+        """Instantiate a new IPv6 Network object.
+
+        Args:
+            address: A string or integer representing the IPv6 network or the
+              IP and prefix/netmask.
+              '2001:db8::/128'
+              '2001:db8:0000:0000:0000:0000:0000:0000/128'
+              '2001:db8::'
+              are all functionally the same in IPv6.  That is to say,
+              failing to provide a subnetmask will create an object with
+              a mask of /128.
+
+              Additionally, an integer can be passed, so
+              IPv6Network('2001:db8::') ==
+                IPv6Network(42540766411282592856903984951653826560)
+              or, more generally
+              IPv6Network(int(IPv6Network('2001:db8::'))) ==
+                IPv6Network('2001:db8::')
+
+            strict: A boolean. If true, ensure that we have been passed
+              A true network address, eg, 2001:db8::1000/124 and not an
+              IP address on a network, eg, 2001:db8::1/124.
+
+        Raises:
+            AddressValueError: If address isn't a valid IPv6 address.
+            NetmaskValueError: If the netmask isn't valid for
+              an IPv6 address.
+            ValueError: If strict was True and a network address was not
+              supplied.
+
+        """
+        _BaseNetwork.__init__(self, address)
+
+        # Efficient constructor from integer or packed address
+        if isinstance(address, (bytes, _compat_int_types)):
+            self.network_address = IPv6Address(address)
+            self.netmask, self._prefixlen = self._make_netmask(
+                self._max_prefixlen)
+            return
+
+        if isinstance(address, tuple):
+            if len(address) > 1:
+                arg = address[1]
+            else:
+                arg = self._max_prefixlen
+            self.netmask, self._prefixlen = self._make_netmask(arg)
+            self.network_address = IPv6Address(address[0])
+            packed = int(self.network_address)
+            if packed & int(self.netmask) != packed:
+                if strict:
+                    raise ValueError('%s has host bits set' % self)
+                else:
+                    self.network_address = IPv6Address(packed &
+                                                       int(self.netmask))
+            return
+
+        # Assume input argument to be string or any object representation
+        # which converts into a formatted IP prefix string.
+        addr = _split_optional_netmask(address)
+
+        self.network_address = IPv6Address(self._ip_int_from_string(addr[0]))
+
+        if len(addr) == 2:
+            arg = addr[1]
+        else:
+            arg = self._max_prefixlen
+        self.netmask, self._prefixlen = self._make_netmask(arg)
+
+        if strict:
+            if (IPv6Address(int(self.network_address) & int(self.netmask)) !=
+                    self.network_address):
+                raise ValueError('%s has host bits set' % self)
+        self.network_address = IPv6Address(int(self.network_address) &
+                                           int(self.netmask))
+
+        if self._prefixlen == (self._max_prefixlen - 1):
+            self.hosts = self.__iter__
+
+    def hosts(self):
+        """Generate Iterator over usable hosts in a network.
+
+          This is like __iter__ except it doesn't return the
+          Subnet-Router anycast address.
+
+        """
+        network = int(self.network_address)
+        broadcast = int(self.broadcast_address)
+        for x in _compat_range(network + 1, broadcast + 1):
+            yield self._address_class(x)
+
+    @property
+    def is_site_local(self):
+        """Test if the address is reserved for site-local.
+
+        Note that the site-local address space has been deprecated by RFC 3879.
+        Use is_private to test if this address is in the space of unique local
+        addresses as defined by RFC 4193.
+
+        Returns:
+            A boolean, True if the address is reserved per RFC 3513 2.5.6.
+
+        """
+        return (self.network_address.is_site_local and
+                self.broadcast_address.is_site_local)
+
+
+class _IPv6Constants(object):
+
+    _linklocal_network = IPv6Network('fe80::/10')
+
+    _multicast_network = IPv6Network('ff00::/8')
+
+    _private_networks = [
+        IPv6Network('::1/128'),
+        IPv6Network('::/128'),
+        IPv6Network('::ffff:0:0/96'),
+        IPv6Network('100::/64'),
+        IPv6Network('2001::/23'),
+        IPv6Network('2001:2::/48'),
+        IPv6Network('2001:db8::/32'),
+        IPv6Network('2001:10::/28'),
+        IPv6Network('fc00::/7'),
+        IPv6Network('fe80::/10'),
+    ]
+
+    _reserved_networks = [
+        IPv6Network('::/8'), IPv6Network('100::/8'),
+        IPv6Network('200::/7'), IPv6Network('400::/6'),
+        IPv6Network('800::/5'), IPv6Network('1000::/4'),
+        IPv6Network('4000::/3'), IPv6Network('6000::/3'),
+        IPv6Network('8000::/3'), IPv6Network('A000::/3'),
+        IPv6Network('C000::/3'), IPv6Network('E000::/4'),
+        IPv6Network('F000::/5'), IPv6Network('F800::/6'),
+        IPv6Network('FE00::/9'),
+    ]
+
+    _sitelocal_network = IPv6Network('fec0::/10')
+
+
+IPv6Address._constants = _IPv6Constants
diff --git a/pip/_vendor/lockfile/__init__.py b/pip/_vendor/lockfile/__init__.py
new file mode 100644
index 00000000000..a6f44a55c63
--- /dev/null
+++ b/pip/_vendor/lockfile/__init__.py
@@ -0,0 +1,347 @@
+# -*- coding: utf-8 -*-
+
+"""
+lockfile.py - Platform-independent advisory file locks.
+
+Requires Python 2.5 unless you apply 2.4.diff
+Locking is done on a per-thread basis instead of a per-process basis.
+
+Usage:
+
+>>> lock = LockFile('somefile')
+>>> try:
+...     lock.acquire()
+... except AlreadyLocked:
+...     print 'somefile', 'is locked already.'
+... except LockFailed:
+...     print 'somefile', 'can\\'t be locked.'
+... else:
+...     print 'got lock'
+got lock
+>>> print lock.is_locked()
+True
+>>> lock.release()
+
+>>> lock = LockFile('somefile')
+>>> print lock.is_locked()
+False
+>>> with lock:
+...    print lock.is_locked()
+True
+>>> print lock.is_locked()
+False
+
+>>> lock = LockFile('somefile')
+>>> # It is okay to lock twice from the same thread...
+>>> with lock:
+...     lock.acquire()
+...
+>>> # Though no counter is kept, so you can't unlock multiple times...
+>>> print lock.is_locked()
+False
+
+Exceptions:
+
+    Error - base class for other exceptions
+        LockError - base class for all locking exceptions
+            AlreadyLocked - Another thread or process already holds the lock
+            LockFailed - Lock failed for some other reason
+        UnlockError - base class for all unlocking exceptions
+            AlreadyUnlocked - File was not locked.
+            NotMyLock - File was locked but not by the current thread/process
+"""
+
+from __future__ import absolute_import
+
+import functools
+import os
+import socket
+import threading
+import warnings
+
+# Work with PEP8 and non-PEP8 versions of threading module.
+if not hasattr(threading, "current_thread"):
+    threading.current_thread = threading.currentThread
+if not hasattr(threading.Thread, "get_name"):
+    threading.Thread.get_name = threading.Thread.getName
+
+__all__ = ['Error', 'LockError', 'LockTimeout', 'AlreadyLocked',
+           'LockFailed', 'UnlockError', 'NotLocked', 'NotMyLock',
+           'LinkFileLock', 'MkdirFileLock', 'SQLiteFileLock',
+           'LockBase', 'locked']
+
+
+class Error(Exception):
+    """
+    Base class for other exceptions.
+
+    >>> try:
+    ...   raise Error
+    ... except Exception:
+    ...   pass
+    """
+    pass
+
+
+class LockError(Error):
+    """
+    Base class for error arising from attempts to acquire the lock.
+
+    >>> try:
+    ...   raise LockError
+    ... except Error:
+    ...   pass
+    """
+    pass
+
+
+class LockTimeout(LockError):
+    """Raised when lock creation fails within a user-defined period of time.
+
+    >>> try:
+    ...   raise LockTimeout
+    ... except LockError:
+    ...   pass
+    """
+    pass
+
+
+class AlreadyLocked(LockError):
+    """Some other thread/process is locking the file.
+
+    >>> try:
+    ...   raise AlreadyLocked
+    ... except LockError:
+    ...   pass
+    """
+    pass
+
+
+class LockFailed(LockError):
+    """Lock file creation failed for some other reason.
+
+    >>> try:
+    ...   raise LockFailed
+    ... except LockError:
+    ...   pass
+    """
+    pass
+
+
+class UnlockError(Error):
+    """
+    Base class for errors arising from attempts to release the lock.
+
+    >>> try:
+    ...   raise UnlockError
+    ... except Error:
+    ...   pass
+    """
+    pass
+
+
+class NotLocked(UnlockError):
+    """Raised when an attempt is made to unlock an unlocked file.
+
+    >>> try:
+    ...   raise NotLocked
+    ... except UnlockError:
+    ...   pass
+    """
+    pass
+
+
+class NotMyLock(UnlockError):
+    """Raised when an attempt is made to unlock a file someone else locked.
+
+    >>> try:
+    ...   raise NotMyLock
+    ... except UnlockError:
+    ...   pass
+    """
+    pass
+
+
+class _SharedBase(object):
+    def __init__(self, path):
+        self.path = path
+
+    def acquire(self, timeout=None):
+        """
+        Acquire the lock.
+
+        * If timeout is omitted (or None), wait forever trying to lock the
+          file.
+
+        * If timeout > 0, try to acquire the lock for that many seconds.  If
+          the lock period expires and the file is still locked, raise
+          LockTimeout.
+
+        * If timeout <= 0, raise AlreadyLocked immediately if the file is
+          already locked.
+        """
+        raise NotImplemented("implement in subclass")
+
+    def release(self):
+        """
+        Release the lock.
+
+        If the file is not locked, raise NotLocked.
+        """
+        raise NotImplemented("implement in subclass")
+
+    def __enter__(self):
+        """
+        Context manager support.
+        """
+        self.acquire()
+        return self
+
+    def __exit__(self, *_exc):
+        """
+        Context manager support.
+        """
+        self.release()
+
+    def __repr__(self):
+        return "<%s: %r>" % (self.__class__.__name__, self.path)
+
+
+class LockBase(_SharedBase):
+    """Base class for platform-specific lock classes."""
+    def __init__(self, path, threaded=True, timeout=None):
+        """
+        >>> lock = LockBase('somefile')
+        >>> lock = LockBase('somefile', threaded=False)
+        """
+        super(LockBase, self).__init__(path)
+        self.lock_file = os.path.abspath(path) + ".lock"
+        self.hostname = socket.gethostname()
+        self.pid = os.getpid()
+        if threaded:
+            t = threading.current_thread()
+            # Thread objects in Python 2.4 and earlier do not have ident
+            # attrs.  Worm around that.
+            ident = getattr(t, "ident", hash(t))
+            self.tname = "-%x" % (ident & 0xffffffff)
+        else:
+            self.tname = ""
+        dirname = os.path.dirname(self.lock_file)
+
+        # unique name is mostly about the current process, but must
+        # also contain the path -- otherwise, two adjacent locked
+        # files conflict (one file gets locked, creating lock-file and
+        # unique file, the other one gets locked, creating lock-file
+        # and overwriting the already existing lock-file, then one
+        # gets unlocked, deleting both lock-file and unique file,
+        # finally the last lock errors out upon releasing.
+        self.unique_name = os.path.join(dirname,
+                                        "%s%s.%s%s" % (self.hostname,
+                                                       self.tname,
+                                                       self.pid,
+                                                       hash(self.path)))
+        self.timeout = timeout
+
+    def is_locked(self):
+        """
+        Tell whether or not the file is locked.
+        """
+        raise NotImplemented("implement in subclass")
+
+    def i_am_locking(self):
+        """
+        Return True if this object is locking the file.
+        """
+        raise NotImplemented("implement in subclass")
+
+    def break_lock(self):
+        """
+        Remove a lock.  Useful if a locking thread failed to unlock.
+        """
+        raise NotImplemented("implement in subclass")
+
+    def __repr__(self):
+        return "<%s: %r -- %r>" % (self.__class__.__name__, self.unique_name,
+                                   self.path)
+
+
+def _fl_helper(cls, mod, *args, **kwds):
+    warnings.warn("Import from %s module instead of lockfile package" % mod,
+                  DeprecationWarning, stacklevel=2)
+    # This is a bit funky, but it's only for awhile.  The way the unit tests
+    # are constructed this function winds up as an unbound method, so it
+    # actually takes three args, not two.  We want to toss out self.
+    if not isinstance(args[0], str):
+        # We are testing, avoid the first arg
+        args = args[1:]
+    if len(args) == 1 and not kwds:
+        kwds["threaded"] = True
+    return cls(*args, **kwds)
+
+
+def LinkFileLock(*args, **kwds):
+    """Factory function provided for backwards compatibility.
+
+    Do not use in new code.  Instead, import LinkLockFile from the
+    lockfile.linklockfile module.
+    """
+    from . import linklockfile
+    return _fl_helper(linklockfile.LinkLockFile, "lockfile.linklockfile",
+                      *args, **kwds)
+
+
+def MkdirFileLock(*args, **kwds):
+    """Factory function provided for backwards compatibility.
+
+    Do not use in new code.  Instead, import MkdirLockFile from the
+    lockfile.mkdirlockfile module.
+    """
+    from . import mkdirlockfile
+    return _fl_helper(mkdirlockfile.MkdirLockFile, "lockfile.mkdirlockfile",
+                      *args, **kwds)
+
+
+def SQLiteFileLock(*args, **kwds):
+    """Factory function provided for backwards compatibility.
+
+    Do not use in new code.  Instead, import SQLiteLockFile from the
+    lockfile.mkdirlockfile module.
+    """
+    from . import sqlitelockfile
+    return _fl_helper(sqlitelockfile.SQLiteLockFile, "lockfile.sqlitelockfile",
+                      *args, **kwds)
+
+
+def locked(path, timeout=None):
+    """Decorator which enables locks for decorated function.
+
+    Arguments:
+     - path: path for lockfile.
+     - timeout (optional): Timeout for acquiring lock.
+
+     Usage:
+         @locked('/var/run/myname', timeout=0)
+         def myname(...):
+             ...
+    """
+    def decor(func):
+        @functools.wraps(func)
+        def wrapper(*args, **kwargs):
+            lock = FileLock(path, timeout=timeout)
+            lock.acquire()
+            try:
+                return func(*args, **kwargs)
+            finally:
+                lock.release()
+        return wrapper
+    return decor
+
+
+if hasattr(os, "link"):
+    from . import linklockfile as _llf
+    LockFile = _llf.LinkLockFile
+else:
+    from . import mkdirlockfile as _mlf
+    LockFile = _mlf.MkdirLockFile
+
+FileLock = LockFile
diff --git a/pip/_vendor/lockfile/linklockfile.py b/pip/_vendor/lockfile/linklockfile.py
new file mode 100644
index 00000000000..2ca9be04235
--- /dev/null
+++ b/pip/_vendor/lockfile/linklockfile.py
@@ -0,0 +1,73 @@
+from __future__ import absolute_import
+
+import time
+import os
+
+from . import (LockBase, LockFailed, NotLocked, NotMyLock, LockTimeout,
+               AlreadyLocked)
+
+
+class LinkLockFile(LockBase):
+    """Lock access to a file using atomic property of link(2).
+
+    >>> lock = LinkLockFile('somefile')
+    >>> lock = LinkLockFile('somefile', threaded=False)
+    """
+
+    def acquire(self, timeout=None):
+        try:
+            open(self.unique_name, "wb").close()
+        except IOError:
+            raise LockFailed("failed to create %s" % self.unique_name)
+
+        timeout = timeout if timeout is not None else self.timeout
+        end_time = time.time()
+        if timeout is not None and timeout > 0:
+            end_time += timeout
+
+        while True:
+            # Try and create a hard link to it.
+            try:
+                os.link(self.unique_name, self.lock_file)
+            except OSError:
+                # Link creation failed.  Maybe we've double-locked?
+                nlinks = os.stat(self.unique_name).st_nlink
+                if nlinks == 2:
+                    # The original link plus the one I created == 2.  We're
+                    # good to go.
+                    return
+                else:
+                    # Otherwise the lock creation failed.
+                    if timeout is not None and time.time() > end_time:
+                        os.unlink(self.unique_name)
+                        if timeout > 0:
+                            raise LockTimeout("Timeout waiting to acquire"
+                                              " lock for %s" %
+                                              self.path)
+                        else:
+                            raise AlreadyLocked("%s is already locked" %
+                                                self.path)
+                    time.sleep(timeout is not None and timeout / 10 or 0.1)
+            else:
+                # Link creation succeeded.  We're good to go.
+                return
+
+    def release(self):
+        if not self.is_locked():
+            raise NotLocked("%s is not locked" % self.path)
+        elif not os.path.exists(self.unique_name):
+            raise NotMyLock("%s is locked, but not by me" % self.path)
+        os.unlink(self.unique_name)
+        os.unlink(self.lock_file)
+
+    def is_locked(self):
+        return os.path.exists(self.lock_file)
+
+    def i_am_locking(self):
+        return (self.is_locked() and
+                os.path.exists(self.unique_name) and
+                os.stat(self.unique_name).st_nlink == 2)
+
+    def break_lock(self):
+        if os.path.exists(self.lock_file):
+            os.unlink(self.lock_file)
diff --git a/pip/_vendor/lockfile/mkdirlockfile.py b/pip/_vendor/lockfile/mkdirlockfile.py
new file mode 100644
index 00000000000..05a8c96ca51
--- /dev/null
+++ b/pip/_vendor/lockfile/mkdirlockfile.py
@@ -0,0 +1,84 @@
+from __future__ import absolute_import, division
+
+import time
+import os
+import sys
+import errno
+
+from . import (LockBase, LockFailed, NotLocked, NotMyLock, LockTimeout,
+               AlreadyLocked)
+
+
+class MkdirLockFile(LockBase):
+    """Lock file by creating a directory."""
+    def __init__(self, path, threaded=True, timeout=None):
+        """
+        >>> lock = MkdirLockFile('somefile')
+        >>> lock = MkdirLockFile('somefile', threaded=False)
+        """
+        LockBase.__init__(self, path, threaded, timeout)
+        # Lock file itself is a directory.  Place the unique file name into
+        # it.
+        self.unique_name = os.path.join(self.lock_file,
+                                        "%s.%s%s" % (self.hostname,
+                                                     self.tname,
+                                                     self.pid))
+
+    def acquire(self, timeout=None):
+        timeout = timeout if timeout is not None else self.timeout
+        end_time = time.time()
+        if timeout is not None and timeout > 0:
+            end_time += timeout
+
+        if timeout is None:
+            wait = 0.1
+        else:
+            wait = max(0, timeout / 10)
+
+        while True:
+            try:
+                os.mkdir(self.lock_file)
+            except OSError:
+                err = sys.exc_info()[1]
+                if err.errno == errno.EEXIST:
+                    # Already locked.
+                    if os.path.exists(self.unique_name):
+                        # Already locked by me.
+                        return
+                    if timeout is not None and time.time() > end_time:
+                        if timeout > 0:
+                            raise LockTimeout("Timeout waiting to acquire"
+                                              " lock for %s" %
+                                              self.path)
+                        else:
+                            # Someone else has the lock.
+                            raise AlreadyLocked("%s is already locked" %
+                                                self.path)
+                    time.sleep(wait)
+                else:
+                    # Couldn't create the lock for some other reason
+                    raise LockFailed("failed to create %s" % self.lock_file)
+            else:
+                open(self.unique_name, "wb").close()
+                return
+
+    def release(self):
+        if not self.is_locked():
+            raise NotLocked("%s is not locked" % self.path)
+        elif not os.path.exists(self.unique_name):
+            raise NotMyLock("%s is locked, but not by me" % self.path)
+        os.unlink(self.unique_name)
+        os.rmdir(self.lock_file)
+
+    def is_locked(self):
+        return os.path.exists(self.lock_file)
+
+    def i_am_locking(self):
+        return (self.is_locked() and
+                os.path.exists(self.unique_name))
+
+    def break_lock(self):
+        if os.path.exists(self.lock_file):
+            for name in os.listdir(self.lock_file):
+                os.unlink(os.path.join(self.lock_file, name))
+            os.rmdir(self.lock_file)
diff --git a/pip/_vendor/lockfile/pidlockfile.py b/pip/_vendor/lockfile/pidlockfile.py
new file mode 100644
index 00000000000..069e85b15bd
--- /dev/null
+++ b/pip/_vendor/lockfile/pidlockfile.py
@@ -0,0 +1,190 @@
+# -*- coding: utf-8 -*-
+
+# pidlockfile.py
+#
+# Copyright © 2008–2009 Ben Finney 
+#
+# This is free software: you may copy, modify, and/or distribute this work
+# under the terms of the Python Software Foundation License, version 2 or
+# later as published by the Python Software Foundation.
+# No warranty expressed or implied. See the file LICENSE.PSF-2 for details.
+
+""" Lockfile behaviour implemented via Unix PID files.
+    """
+
+from __future__ import absolute_import
+
+import errno
+import os
+import time
+
+from . import (LockBase, AlreadyLocked, LockFailed, NotLocked, NotMyLock,
+               LockTimeout)
+
+
+class PIDLockFile(LockBase):
+    """ Lockfile implemented as a Unix PID file.
+
+    The lock file is a normal file named by the attribute `path`.
+    A lock's PID file contains a single line of text, containing
+    the process ID (PID) of the process that acquired the lock.
+
+    >>> lock = PIDLockFile('somefile')
+    >>> lock = PIDLockFile('somefile')
+    """
+
+    def __init__(self, path, threaded=False, timeout=None):
+        # pid lockfiles don't support threaded operation, so always force
+        # False as the threaded arg.
+        LockBase.__init__(self, path, False, timeout)
+        self.unique_name = self.path
+
+    def read_pid(self):
+        """ Get the PID from the lock file.
+            """
+        return read_pid_from_pidfile(self.path)
+
+    def is_locked(self):
+        """ Test if the lock is currently held.
+
+            The lock is held if the PID file for this lock exists.
+
+            """
+        return os.path.exists(self.path)
+
+    def i_am_locking(self):
+        """ Test if the lock is held by the current process.
+
+        Returns ``True`` if the current process ID matches the
+        number stored in the PID file.
+        """
+        return self.is_locked() and os.getpid() == self.read_pid()
+
+    def acquire(self, timeout=None):
+        """ Acquire the lock.
+
+        Creates the PID file for this lock, or raises an error if
+        the lock could not be acquired.
+        """
+
+        timeout = timeout if timeout is not None else self.timeout
+        end_time = time.time()
+        if timeout is not None and timeout > 0:
+            end_time += timeout
+
+        while True:
+            try:
+                write_pid_to_pidfile(self.path)
+            except OSError as exc:
+                if exc.errno == errno.EEXIST:
+                    # The lock creation failed.  Maybe sleep a bit.
+                    if time.time() > end_time:
+                        if timeout is not None and timeout > 0:
+                            raise LockTimeout("Timeout waiting to acquire"
+                                              " lock for %s" %
+                                              self.path)
+                        else:
+                            raise AlreadyLocked("%s is already locked" %
+                                                self.path)
+                    time.sleep(timeout is not None and timeout / 10 or 0.1)
+                else:
+                    raise LockFailed("failed to create %s" % self.path)
+            else:
+                return
+
+    def release(self):
+        """ Release the lock.
+
+            Removes the PID file to release the lock, or raises an
+            error if the current process does not hold the lock.
+
+            """
+        if not self.is_locked():
+            raise NotLocked("%s is not locked" % self.path)
+        if not self.i_am_locking():
+            raise NotMyLock("%s is locked, but not by me" % self.path)
+        remove_existing_pidfile(self.path)
+
+    def break_lock(self):
+        """ Break an existing lock.
+
+            Removes the PID file if it already exists, otherwise does
+            nothing.
+
+            """
+        remove_existing_pidfile(self.path)
+
+
+def read_pid_from_pidfile(pidfile_path):
+    """ Read the PID recorded in the named PID file.
+
+        Read and return the numeric PID recorded as text in the named
+        PID file. If the PID file cannot be read, or if the content is
+        not a valid PID, return ``None``.
+
+        """
+    pid = None
+    try:
+        pidfile = open(pidfile_path, 'r')
+    except IOError:
+        pass
+    else:
+        # According to the FHS 2.3 section on PID files in /var/run:
+        #
+        #   The file must consist of the process identifier in
+        #   ASCII-encoded decimal, followed by a newline character.
+        #
+        #   Programs that read PID files should be somewhat flexible
+        #   in what they accept; i.e., they should ignore extra
+        #   whitespace, leading zeroes, absence of the trailing
+        #   newline, or additional lines in the PID file.
+
+        line = pidfile.readline().strip()
+        try:
+            pid = int(line)
+        except ValueError:
+            pass
+        pidfile.close()
+
+    return pid
+
+
+def write_pid_to_pidfile(pidfile_path):
+    """ Write the PID in the named PID file.
+
+        Get the numeric process ID (“PID”) of the current process
+        and write it to the named file as a line of text.
+
+        """
+    open_flags = (os.O_CREAT | os.O_EXCL | os.O_WRONLY)
+    open_mode = 0o644
+    pidfile_fd = os.open(pidfile_path, open_flags, open_mode)
+    pidfile = os.fdopen(pidfile_fd, 'w')
+
+    # According to the FHS 2.3 section on PID files in /var/run:
+    #
+    #   The file must consist of the process identifier in
+    #   ASCII-encoded decimal, followed by a newline character. For
+    #   example, if crond was process number 25, /var/run/crond.pid
+    #   would contain three characters: two, five, and newline.
+
+    pid = os.getpid()
+    pidfile.write("%s\n" % pid)
+    pidfile.close()
+
+
+def remove_existing_pidfile(pidfile_path):
+    """ Remove the named PID file if it exists.
+
+        Removing a PID file that doesn't already exist puts us in the
+        desired state, so we ignore the condition if the file does not
+        exist.
+
+        """
+    try:
+        os.remove(pidfile_path)
+    except OSError as exc:
+        if exc.errno == errno.ENOENT:
+            pass
+        else:
+            raise
diff --git a/pip/_vendor/lockfile/sqlitelockfile.py b/pip/_vendor/lockfile/sqlitelockfile.py
new file mode 100644
index 00000000000..f997e2444e7
--- /dev/null
+++ b/pip/_vendor/lockfile/sqlitelockfile.py
@@ -0,0 +1,156 @@
+from __future__ import absolute_import, division
+
+import time
+import os
+
+try:
+    unicode
+except NameError:
+    unicode = str
+
+from . import LockBase, NotLocked, NotMyLock, LockTimeout, AlreadyLocked
+
+
+class SQLiteLockFile(LockBase):
+    "Demonstrate SQL-based locking."
+
+    testdb = None
+
+    def __init__(self, path, threaded=True, timeout=None):
+        """
+        >>> lock = SQLiteLockFile('somefile')
+        >>> lock = SQLiteLockFile('somefile', threaded=False)
+        """
+        LockBase.__init__(self, path, threaded, timeout)
+        self.lock_file = unicode(self.lock_file)
+        self.unique_name = unicode(self.unique_name)
+
+        if SQLiteLockFile.testdb is None:
+            import tempfile
+            _fd, testdb = tempfile.mkstemp()
+            os.close(_fd)
+            os.unlink(testdb)
+            del _fd, tempfile
+            SQLiteLockFile.testdb = testdb
+
+        import sqlite3
+        self.connection = sqlite3.connect(SQLiteLockFile.testdb)
+
+        c = self.connection.cursor()
+        try:
+            c.execute("create table locks"
+                      "("
+                      "   lock_file varchar(32),"
+                      "   unique_name varchar(32)"
+                      ")")
+        except sqlite3.OperationalError:
+            pass
+        else:
+            self.connection.commit()
+            import atexit
+            atexit.register(os.unlink, SQLiteLockFile.testdb)
+
+    def acquire(self, timeout=None):
+        timeout = timeout if timeout is not None else self.timeout
+        end_time = time.time()
+        if timeout is not None and timeout > 0:
+            end_time += timeout
+
+        if timeout is None:
+            wait = 0.1
+        elif timeout <= 0:
+            wait = 0
+        else:
+            wait = timeout / 10
+
+        cursor = self.connection.cursor()
+
+        while True:
+            if not self.is_locked():
+                # Not locked.  Try to lock it.
+                cursor.execute("insert into locks"
+                               "  (lock_file, unique_name)"
+                               "  values"
+                               "  (?, ?)",
+                               (self.lock_file, self.unique_name))
+                self.connection.commit()
+
+                # Check to see if we are the only lock holder.
+                cursor.execute("select * from locks"
+                               "  where unique_name = ?",
+                               (self.unique_name,))
+                rows = cursor.fetchall()
+                if len(rows) > 1:
+                    # Nope.  Someone else got there.  Remove our lock.
+                    cursor.execute("delete from locks"
+                                   "  where unique_name = ?",
+                                   (self.unique_name,))
+                    self.connection.commit()
+                else:
+                    # Yup.  We're done, so go home.
+                    return
+            else:
+                # Check to see if we are the only lock holder.
+                cursor.execute("select * from locks"
+                               "  where unique_name = ?",
+                               (self.unique_name,))
+                rows = cursor.fetchall()
+                if len(rows) == 1:
+                    # We're the locker, so go home.
+                    return
+
+            # Maybe we should wait a bit longer.
+            if timeout is not None and time.time() > end_time:
+                if timeout > 0:
+                    # No more waiting.
+                    raise LockTimeout("Timeout waiting to acquire"
+                                      " lock for %s" %
+                                      self.path)
+                else:
+                    # Someone else has the lock and we are impatient..
+                    raise AlreadyLocked("%s is already locked" % self.path)
+
+            # Well, okay.  We'll give it a bit longer.
+            time.sleep(wait)
+
+    def release(self):
+        if not self.is_locked():
+            raise NotLocked("%s is not locked" % self.path)
+        if not self.i_am_locking():
+            raise NotMyLock("%s is locked, but not by me (by %s)" %
+                            (self.unique_name, self._who_is_locking()))
+        cursor = self.connection.cursor()
+        cursor.execute("delete from locks"
+                       "  where unique_name = ?",
+                       (self.unique_name,))
+        self.connection.commit()
+
+    def _who_is_locking(self):
+        cursor = self.connection.cursor()
+        cursor.execute("select unique_name from locks"
+                       "  where lock_file = ?",
+                       (self.lock_file,))
+        return cursor.fetchone()[0]
+
+    def is_locked(self):
+        cursor = self.connection.cursor()
+        cursor.execute("select * from locks"
+                       "  where lock_file = ?",
+                       (self.lock_file,))
+        rows = cursor.fetchall()
+        return not not rows
+
+    def i_am_locking(self):
+        cursor = self.connection.cursor()
+        cursor.execute("select * from locks"
+                       "  where lock_file = ?"
+                       "    and unique_name = ?",
+                       (self.lock_file, self.unique_name))
+        return not not cursor.fetchall()
+
+    def break_lock(self):
+        cursor = self.connection.cursor()
+        cursor.execute("delete from locks"
+                       "  where lock_file = ?",
+                       (self.lock_file,))
+        self.connection.commit()
diff --git a/pip/_vendor/lockfile/symlinklockfile.py b/pip/_vendor/lockfile/symlinklockfile.py
new file mode 100644
index 00000000000..23b41f582b9
--- /dev/null
+++ b/pip/_vendor/lockfile/symlinklockfile.py
@@ -0,0 +1,70 @@
+from __future__ import absolute_import
+
+import os
+import time
+
+from . import (LockBase, NotLocked, NotMyLock, LockTimeout,
+               AlreadyLocked)
+
+
+class SymlinkLockFile(LockBase):
+    """Lock access to a file using symlink(2)."""
+
+    def __init__(self, path, threaded=True, timeout=None):
+        # super(SymlinkLockFile).__init(...)
+        LockBase.__init__(self, path, threaded, timeout)
+        # split it back!
+        self.unique_name = os.path.split(self.unique_name)[1]
+
+    def acquire(self, timeout=None):
+        # Hopefully unnecessary for symlink.
+        # try:
+        #     open(self.unique_name, "wb").close()
+        # except IOError:
+        #     raise LockFailed("failed to create %s" % self.unique_name)
+        timeout = timeout if timeout is not None else self.timeout
+        end_time = time.time()
+        if timeout is not None and timeout > 0:
+            end_time += timeout
+
+        while True:
+            # Try and create a symbolic link to it.
+            try:
+                os.symlink(self.unique_name, self.lock_file)
+            except OSError:
+                # Link creation failed.  Maybe we've double-locked?
+                if self.i_am_locking():
+                    # Linked to out unique name. Proceed.
+                    return
+                else:
+                    # Otherwise the lock creation failed.
+                    if timeout is not None and time.time() > end_time:
+                        if timeout > 0:
+                            raise LockTimeout("Timeout waiting to acquire"
+                                              " lock for %s" %
+                                              self.path)
+                        else:
+                            raise AlreadyLocked("%s is already locked" %
+                                                self.path)
+                    time.sleep(timeout / 10 if timeout is not None else 0.1)
+            else:
+                # Link creation succeeded.  We're good to go.
+                return
+
+    def release(self):
+        if not self.is_locked():
+            raise NotLocked("%s is not locked" % self.path)
+        elif not self.i_am_locking():
+            raise NotMyLock("%s is locked, but not by me" % self.path)
+        os.unlink(self.lock_file)
+
+    def is_locked(self):
+        return os.path.islink(self.lock_file)
+
+    def i_am_locking(self):
+        return (os.path.islink(self.lock_file)
+                and os.readlink(self.lock_file) == self.unique_name)
+
+    def break_lock(self):
+        if os.path.islink(self.lock_file):  # exists && link
+            os.unlink(self.lock_file)
diff --git a/pip/_vendor/packaging/__about__.py b/pip/_vendor/packaging/__about__.py
new file mode 100644
index 00000000000..c21a758b82d
--- /dev/null
+++ b/pip/_vendor/packaging/__about__.py
@@ -0,0 +1,21 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+from __future__ import absolute_import, division, print_function
+
+__all__ = [
+    "__title__", "__summary__", "__uri__", "__version__", "__author__",
+    "__email__", "__license__", "__copyright__",
+]
+
+__title__ = "packaging"
+__summary__ = "Core utilities for Python packages"
+__uri__ = "https://github.com/pypa/packaging"
+
+__version__ = "16.7"
+
+__author__ = "Donald Stufft and individual contributors"
+__email__ = "donald@stufft.io"
+
+__license__ = "BSD or Apache License, Version 2.0"
+__copyright__ = "Copyright 2014-2016 %s" % __author__
diff --git a/pip/_vendor/packaging/__init__.py b/pip/_vendor/packaging/__init__.py
new file mode 100644
index 00000000000..5ee6220203e
--- /dev/null
+++ b/pip/_vendor/packaging/__init__.py
@@ -0,0 +1,14 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+from __future__ import absolute_import, division, print_function
+
+from .__about__ import (
+    __author__, __copyright__, __email__, __license__, __summary__, __title__,
+    __uri__, __version__
+)
+
+__all__ = [
+    "__title__", "__summary__", "__uri__", "__version__", "__author__",
+    "__email__", "__license__", "__copyright__",
+]
diff --git a/pip/_vendor/packaging/_compat.py b/pip/_vendor/packaging/_compat.py
new file mode 100644
index 00000000000..210bb80b7e7
--- /dev/null
+++ b/pip/_vendor/packaging/_compat.py
@@ -0,0 +1,30 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+from __future__ import absolute_import, division, print_function
+
+import sys
+
+
+PY2 = sys.version_info[0] == 2
+PY3 = sys.version_info[0] == 3
+
+# flake8: noqa
+
+if PY3:
+    string_types = str,
+else:
+    string_types = basestring,
+
+
+def with_metaclass(meta, *bases):
+    """
+    Create a base class with a metaclass.
+    """
+    # This requires a bit of explanation: the basic idea is to make a dummy
+    # metaclass for one level of class instantiation that replaces itself with
+    # the actual metaclass.
+    class metaclass(meta):
+        def __new__(cls, name, this_bases, d):
+            return meta(name, bases, d)
+    return type.__new__(metaclass, 'temporary_class', (), {})
diff --git a/pip/_vendor/packaging/_structures.py b/pip/_vendor/packaging/_structures.py
new file mode 100644
index 00000000000..ccc27861c3a
--- /dev/null
+++ b/pip/_vendor/packaging/_structures.py
@@ -0,0 +1,68 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+from __future__ import absolute_import, division, print_function
+
+
+class Infinity(object):
+
+    def __repr__(self):
+        return "Infinity"
+
+    def __hash__(self):
+        return hash(repr(self))
+
+    def __lt__(self, other):
+        return False
+
+    def __le__(self, other):
+        return False
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__)
+
+    def __ne__(self, other):
+        return not isinstance(other, self.__class__)
+
+    def __gt__(self, other):
+        return True
+
+    def __ge__(self, other):
+        return True
+
+    def __neg__(self):
+        return NegativeInfinity
+
+Infinity = Infinity()
+
+
+class NegativeInfinity(object):
+
+    def __repr__(self):
+        return "-Infinity"
+
+    def __hash__(self):
+        return hash(repr(self))
+
+    def __lt__(self, other):
+        return True
+
+    def __le__(self, other):
+        return True
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__)
+
+    def __ne__(self, other):
+        return not isinstance(other, self.__class__)
+
+    def __gt__(self, other):
+        return False
+
+    def __ge__(self, other):
+        return False
+
+    def __neg__(self):
+        return Infinity
+
+NegativeInfinity = NegativeInfinity()
diff --git a/pip/_vendor/packaging/markers.py b/pip/_vendor/packaging/markers.py
new file mode 100644
index 00000000000..9195243533c
--- /dev/null
+++ b/pip/_vendor/packaging/markers.py
@@ -0,0 +1,289 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+from __future__ import absolute_import, division, print_function
+
+import operator
+import os
+import platform
+import sys
+
+from pip._vendor.pyparsing import (
+    ParseException, ParseResults, stringStart, stringEnd,
+)
+from pip._vendor.pyparsing import ZeroOrMore, Group, Forward, QuotedString
+from pip._vendor.pyparsing import Literal as L  # noqa
+
+from ._compat import string_types
+from .specifiers import Specifier, InvalidSpecifier
+
+
+__all__ = [
+    "InvalidMarker", "UndefinedComparison", "UndefinedEnvironmentName",
+    "Marker", "default_environment",
+]
+
+
+class InvalidMarker(ValueError):
+    """
+    An invalid marker was found, users should refer to PEP 508.
+    """
+
+
+class UndefinedComparison(ValueError):
+    """
+    An invalid operation was attempted on a value that doesn't support it.
+    """
+
+
+class UndefinedEnvironmentName(ValueError):
+    """
+    A name was attempted to be used that does not exist inside of the
+    environment.
+    """
+
+
+class Node(object):
+
+    def __init__(self, value):
+        self.value = value
+
+    def __str__(self):
+        return str(self.value)
+
+    def __repr__(self):
+        return "<{0}({1!r})>".format(self.__class__.__name__, str(self))
+
+
+class Variable(Node):
+    pass
+
+
+class Value(Node):
+    pass
+
+
+VARIABLE = (
+    L("implementation_version") |
+    L("platform_python_implementation") |
+    L("implementation_name") |
+    L("python_full_version") |
+    L("platform_release") |
+    L("platform_version") |
+    L("platform_machine") |
+    L("platform_system") |
+    L("python_version") |
+    L("sys_platform") |
+    L("os_name") |
+    L("os.name") |  # PEP-345
+    L("sys.platform") |  # PEP-345
+    L("platform.version") |  # PEP-345
+    L("platform.machine") |  # PEP-345
+    L("platform.python_implementation") |  # PEP-345
+    L("python_implementation") |  # undocumented setuptools legacy
+    L("extra")
+)
+ALIASES = {
+    'os.name': 'os_name',
+    'sys.platform': 'sys_platform',
+    'platform.version': 'platform_version',
+    'platform.machine': 'platform_machine',
+    'platform.python_implementation': 'platform_python_implementation',
+    'python_implementation': 'platform_python_implementation'
+}
+VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0])))
+
+VERSION_CMP = (
+    L("===") |
+    L("==") |
+    L(">=") |
+    L("<=") |
+    L("!=") |
+    L("~=") |
+    L(">") |
+    L("<")
+)
+
+MARKER_OP = VERSION_CMP | L("not in") | L("in")
+
+MARKER_VALUE = QuotedString("'") | QuotedString('"')
+MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0]))
+
+BOOLOP = L("and") | L("or")
+
+MARKER_VAR = VARIABLE | MARKER_VALUE
+
+MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR)
+MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0]))
+
+LPAREN = L("(").suppress()
+RPAREN = L(")").suppress()
+
+MARKER_EXPR = Forward()
+MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN)
+MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR)
+
+MARKER = stringStart + MARKER_EXPR + stringEnd
+
+
+def _coerce_parse_result(results):
+    if isinstance(results, ParseResults):
+        return [_coerce_parse_result(i) for i in results]
+    else:
+        return results
+
+
+def _format_marker(marker, first=True):
+    assert isinstance(marker, (list, tuple, string_types))
+
+    # Sometimes we have a structure like [[...]] which is a single item list
+    # where the single item is itself it's own list. In that case we want skip
+    # the rest of this function so that we don't get extraneous () on the
+    # outside.
+    if (isinstance(marker, list) and len(marker) == 1 and
+            isinstance(marker[0], (list, tuple))):
+        return _format_marker(marker[0])
+
+    if isinstance(marker, list):
+        inner = (_format_marker(m, first=False) for m in marker)
+        if first:
+            return " ".join(inner)
+        else:
+            return "(" + " ".join(inner) + ")"
+    elif isinstance(marker, tuple):
+        return '{0} {1} "{2}"'.format(*marker)
+    else:
+        return marker
+
+
+_operators = {
+    "in": lambda lhs, rhs: lhs in rhs,
+    "not in": lambda lhs, rhs: lhs not in rhs,
+    "<": operator.lt,
+    "<=": operator.le,
+    "==": operator.eq,
+    "!=": operator.ne,
+    ">=": operator.ge,
+    ">": operator.gt,
+}
+
+
+def _eval_op(lhs, op, rhs):
+    try:
+        spec = Specifier("".join([op, rhs]))
+    except InvalidSpecifier:
+        pass
+    else:
+        return spec.contains(lhs)
+
+    oper = _operators.get(op)
+    if oper is None:
+        raise UndefinedComparison(
+            "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs)
+        )
+
+    return oper(lhs, rhs)
+
+
+_undefined = object()
+
+
+def _get_env(environment, name):
+    value = environment.get(name, _undefined)
+
+    if value is _undefined:
+        raise UndefinedEnvironmentName(
+            "{0!r} does not exist in evaluation environment.".format(name)
+        )
+
+    return value
+
+
+def _evaluate_markers(markers, environment):
+    groups = [[]]
+
+    for marker in markers:
+        assert isinstance(marker, (list, tuple, string_types))
+
+        if isinstance(marker, list):
+            groups[-1].append(_evaluate_markers(marker, environment))
+        elif isinstance(marker, tuple):
+            lhs, op, rhs = marker
+
+            if isinstance(lhs, Variable):
+                lhs_value = _get_env(environment, lhs.value)
+                rhs_value = rhs.value
+            else:
+                lhs_value = lhs.value
+                rhs_value = _get_env(environment, rhs.value)
+
+            groups[-1].append(_eval_op(lhs_value, op, rhs_value))
+        else:
+            assert marker in ["and", "or"]
+            if marker == "or":
+                groups.append([])
+
+    return any(all(item) for item in groups)
+
+
+def format_full_version(info):
+    version = '{0.major}.{0.minor}.{0.micro}'.format(info)
+    kind = info.releaselevel
+    if kind != 'final':
+        version += kind[0] + str(info.serial)
+    return version
+
+
+def default_environment():
+    if hasattr(sys, 'implementation'):
+        iver = format_full_version(sys.implementation.version)
+        implementation_name = sys.implementation.name
+    else:
+        iver = '0'
+        implementation_name = ''
+
+    return {
+        "implementation_name": implementation_name,
+        "implementation_version": iver,
+        "os_name": os.name,
+        "platform_machine": platform.machine(),
+        "platform_release": platform.release(),
+        "platform_system": platform.system(),
+        "platform_version": platform.version(),
+        "python_full_version": platform.python_version(),
+        "platform_python_implementation": platform.python_implementation(),
+        "python_version": platform.python_version()[:3],
+        "sys_platform": sys.platform,
+    }
+
+
+class Marker(object):
+
+    def __init__(self, marker):
+        try:
+            self._markers = _coerce_parse_result(MARKER.parseString(marker))
+        except ParseException as e:
+            err_str = "Invalid marker: {0!r}, parse error at {1!r}".format(
+                marker, marker[e.loc:e.loc + 8])
+            raise InvalidMarker(err_str)
+
+    def __str__(self):
+        return _format_marker(self._markers)
+
+    def __repr__(self):
+        return "".format(str(self))
+
+    def evaluate(self, environment=None):
+        """Evaluate a marker.
+
+        Return the boolean from evaluating the given marker against the
+        environment. environment is an optional argument to override all or
+        part of the determined environment.
+
+        The environment is determined from the current Python process.
+        """
+        current_environment = default_environment()
+        if environment is not None:
+            current_environment.update(environment)
+
+        return _evaluate_markers(self._markers, current_environment)
diff --git a/pip/_vendor/packaging/requirements.py b/pip/_vendor/packaging/requirements.py
new file mode 100644
index 00000000000..49a4385be7e
--- /dev/null
+++ b/pip/_vendor/packaging/requirements.py
@@ -0,0 +1,129 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+from __future__ import absolute_import, division, print_function
+
+import string
+import re
+
+from pip._vendor.pyparsing import (
+    stringStart, stringEnd, originalTextFor, ParseException
+)
+from pip._vendor.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine
+from pip._vendor.pyparsing import Literal as L  # noqa
+from pip._vendor.six.moves.urllib import parse as urlparse
+
+from .markers import MARKER_EXPR, Marker
+from .specifiers import LegacySpecifier, Specifier, SpecifierSet
+
+
+class InvalidRequirement(ValueError):
+    """
+    An invalid requirement was found, users should refer to PEP 508.
+    """
+
+
+ALPHANUM = Word(string.ascii_letters + string.digits)
+
+LBRACKET = L("[").suppress()
+RBRACKET = L("]").suppress()
+LPAREN = L("(").suppress()
+RPAREN = L(")").suppress()
+COMMA = L(",").suppress()
+SEMICOLON = L(";").suppress()
+AT = L("@").suppress()
+
+PUNCTUATION = Word("-_.")
+IDENTIFIER_END = ALPHANUM | (ZeroOrMore(PUNCTUATION) + ALPHANUM)
+IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END))
+
+NAME = IDENTIFIER("name")
+EXTRA = IDENTIFIER
+
+URI = Regex(r'[^ ]+')("url")
+URL = (AT + URI)
+
+EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA)
+EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras")
+
+VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE)
+VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE)
+
+VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY
+VERSION_MANY = Combine(VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE),
+                       joinString=",", adjacent=False)("_raw_spec")
+_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY))
+_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or '')
+
+VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier")
+VERSION_SPEC.setParseAction(lambda s, l, t: t[1])
+
+MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker")
+MARKER_EXPR.setParseAction(
+    lambda s, l, t: Marker(s[t._original_start:t._original_end])
+)
+MARKER_SEPERATOR = SEMICOLON
+MARKER = MARKER_SEPERATOR + MARKER_EXPR
+
+VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER)
+URL_AND_MARKER = URL + Optional(MARKER)
+
+NAMED_REQUIREMENT = \
+    NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER)
+
+REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd
+
+
+class Requirement(object):
+    """Parse a requirement.
+
+    Parse a given requirement string into its parts, such as name, specifier,
+    URL, and extras. Raises InvalidRequirement on a badly-formed requirement
+    string.
+    """
+
+    # TODO: Can we test whether something is contained within a requirement?
+    #       If so how do we do that? Do we need to test against the _name_ of
+    #       the thing as well as the version? What about the markers?
+    # TODO: Can we normalize the name and extra name?
+
+    def __init__(self, requirement_string):
+        try:
+            req = REQUIREMENT.parseString(requirement_string)
+        except ParseException as e:
+            raise InvalidRequirement(
+                "Invalid requirement, parse error at \"{0!r}\"".format(
+                    requirement_string[e.loc:e.loc + 8]))
+
+        self.name = req.name
+        if req.url:
+            parsed_url = urlparse.urlparse(req.url)
+            if not (parsed_url.scheme and parsed_url.netloc) or (
+                    not parsed_url.scheme and not parsed_url.netloc):
+                raise InvalidRequirement("Invalid URL given")
+            self.url = req.url
+        else:
+            self.url = None
+        self.extras = set(req.extras.asList() if req.extras else [])
+        self.specifier = SpecifierSet(req.specifier)
+        self.marker = req.marker if req.marker else None
+
+    def __str__(self):
+        parts = [self.name]
+
+        if self.extras:
+            parts.append("[{0}]".format(",".join(sorted(self.extras))))
+
+        if self.specifier:
+            parts.append(str(self.specifier))
+
+        if self.url:
+            parts.append("@ {0}".format(self.url))
+
+        if self.marker:
+            parts.append("; {0}".format(self.marker))
+
+        return "".join(parts)
+
+    def __repr__(self):
+        return "".format(str(self))
diff --git a/pip/_vendor/packaging/specifiers.py b/pip/_vendor/packaging/specifiers.py
new file mode 100644
index 00000000000..7f5a76cfd63
--- /dev/null
+++ b/pip/_vendor/packaging/specifiers.py
@@ -0,0 +1,774 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+from __future__ import absolute_import, division, print_function
+
+import abc
+import functools
+import itertools
+import re
+
+from ._compat import string_types, with_metaclass
+from .version import Version, LegacyVersion, parse
+
+
+class InvalidSpecifier(ValueError):
+    """
+    An invalid specifier was found, users should refer to PEP 440.
+    """
+
+
+class BaseSpecifier(with_metaclass(abc.ABCMeta, object)):
+
+    @abc.abstractmethod
+    def __str__(self):
+        """
+        Returns the str representation of this Specifier like object. This
+        should be representative of the Specifier itself.
+        """
+
+    @abc.abstractmethod
+    def __hash__(self):
+        """
+        Returns a hash value for this Specifier like object.
+        """
+
+    @abc.abstractmethod
+    def __eq__(self, other):
+        """
+        Returns a boolean representing whether or not the two Specifier like
+        objects are equal.
+        """
+
+    @abc.abstractmethod
+    def __ne__(self, other):
+        """
+        Returns a boolean representing whether or not the two Specifier like
+        objects are not equal.
+        """
+
+    @abc.abstractproperty
+    def prereleases(self):
+        """
+        Returns whether or not pre-releases as a whole are allowed by this
+        specifier.
+        """
+
+    @prereleases.setter
+    def prereleases(self, value):
+        """
+        Sets whether or not pre-releases as a whole are allowed by this
+        specifier.
+        """
+
+    @abc.abstractmethod
+    def contains(self, item, prereleases=None):
+        """
+        Determines if the given item is contained within this specifier.
+        """
+
+    @abc.abstractmethod
+    def filter(self, iterable, prereleases=None):
+        """
+        Takes an iterable of items and filters them so that only items which
+        are contained within this specifier are allowed in it.
+        """
+
+
+class _IndividualSpecifier(BaseSpecifier):
+
+    _operators = {}
+
+    def __init__(self, spec="", prereleases=None):
+        match = self._regex.search(spec)
+        if not match:
+            raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec))
+
+        self._spec = (
+            match.group("operator").strip(),
+            match.group("version").strip(),
+        )
+
+        # Store whether or not this Specifier should accept prereleases
+        self._prereleases = prereleases
+
+    def __repr__(self):
+        pre = (
+            ", prereleases={0!r}".format(self.prereleases)
+            if self._prereleases is not None
+            else ""
+        )
+
+        return "<{0}({1!r}{2})>".format(
+            self.__class__.__name__,
+            str(self),
+            pre,
+        )
+
+    def __str__(self):
+        return "{0}{1}".format(*self._spec)
+
+    def __hash__(self):
+        return hash(self._spec)
+
+    def __eq__(self, other):
+        if isinstance(other, string_types):
+            try:
+                other = self.__class__(other)
+            except InvalidSpecifier:
+                return NotImplemented
+        elif not isinstance(other, self.__class__):
+            return NotImplemented
+
+        return self._spec == other._spec
+
+    def __ne__(self, other):
+        if isinstance(other, string_types):
+            try:
+                other = self.__class__(other)
+            except InvalidSpecifier:
+                return NotImplemented
+        elif not isinstance(other, self.__class__):
+            return NotImplemented
+
+        return self._spec != other._spec
+
+    def _get_operator(self, op):
+        return getattr(self, "_compare_{0}".format(self._operators[op]))
+
+    def _coerce_version(self, version):
+        if not isinstance(version, (LegacyVersion, Version)):
+            version = parse(version)
+        return version
+
+    @property
+    def operator(self):
+        return self._spec[0]
+
+    @property
+    def version(self):
+        return self._spec[1]
+
+    @property
+    def prereleases(self):
+        return self._prereleases
+
+    @prereleases.setter
+    def prereleases(self, value):
+        self._prereleases = value
+
+    def __contains__(self, item):
+        return self.contains(item)
+
+    def contains(self, item, prereleases=None):
+        # Determine if prereleases are to be allowed or not.
+        if prereleases is None:
+            prereleases = self.prereleases
+
+        # Normalize item to a Version or LegacyVersion, this allows us to have
+        # a shortcut for ``"2.0" in Specifier(">=2")
+        item = self._coerce_version(item)
+
+        # Determine if we should be supporting prereleases in this specifier
+        # or not, if we do not support prereleases than we can short circuit
+        # logic if this version is a prereleases.
+        if item.is_prerelease and not prereleases:
+            return False
+
+        # Actually do the comparison to determine if this item is contained
+        # within this Specifier or not.
+        return self._get_operator(self.operator)(item, self.version)
+
+    def filter(self, iterable, prereleases=None):
+        yielded = False
+        found_prereleases = []
+
+        kw = {"prereleases": prereleases if prereleases is not None else True}
+
+        # Attempt to iterate over all the values in the iterable and if any of
+        # them match, yield them.
+        for version in iterable:
+            parsed_version = self._coerce_version(version)
+
+            if self.contains(parsed_version, **kw):
+                # If our version is a prerelease, and we were not set to allow
+                # prereleases, then we'll store it for later incase nothing
+                # else matches this specifier.
+                if (parsed_version.is_prerelease and not
+                        (prereleases or self.prereleases)):
+                    found_prereleases.append(version)
+                # Either this is not a prerelease, or we should have been
+                # accepting prereleases from the begining.
+                else:
+                    yielded = True
+                    yield version
+
+        # Now that we've iterated over everything, determine if we've yielded
+        # any values, and if we have not and we have any prereleases stored up
+        # then we will go ahead and yield the prereleases.
+        if not yielded and found_prereleases:
+            for version in found_prereleases:
+                yield version
+
+
+class LegacySpecifier(_IndividualSpecifier):
+
+    _regex_str = (
+        r"""
+        (?P(==|!=|<=|>=|<|>))
+        \s*
+        (?P
+            [^,;\s)]* # Since this is a "legacy" specifier, and the version
+                      # string can be just about anything, we match everything
+                      # except for whitespace, a semi-colon for marker support,
+                      # a closing paren since versions can be enclosed in
+                      # them, and a comma since it's a version separator.
+        )
+        """
+    )
+
+    _regex = re.compile(
+        r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
+
+    _operators = {
+        "==": "equal",
+        "!=": "not_equal",
+        "<=": "less_than_equal",
+        ">=": "greater_than_equal",
+        "<": "less_than",
+        ">": "greater_than",
+    }
+
+    def _coerce_version(self, version):
+        if not isinstance(version, LegacyVersion):
+            version = LegacyVersion(str(version))
+        return version
+
+    def _compare_equal(self, prospective, spec):
+        return prospective == self._coerce_version(spec)
+
+    def _compare_not_equal(self, prospective, spec):
+        return prospective != self._coerce_version(spec)
+
+    def _compare_less_than_equal(self, prospective, spec):
+        return prospective <= self._coerce_version(spec)
+
+    def _compare_greater_than_equal(self, prospective, spec):
+        return prospective >= self._coerce_version(spec)
+
+    def _compare_less_than(self, prospective, spec):
+        return prospective < self._coerce_version(spec)
+
+    def _compare_greater_than(self, prospective, spec):
+        return prospective > self._coerce_version(spec)
+
+
+def _require_version_compare(fn):
+    @functools.wraps(fn)
+    def wrapped(self, prospective, spec):
+        if not isinstance(prospective, Version):
+            return False
+        return fn(self, prospective, spec)
+    return wrapped
+
+
+class Specifier(_IndividualSpecifier):
+
+    _regex_str = (
+        r"""
+        (?P(~=|==|!=|<=|>=|<|>|===))
+        (?P
+            (?:
+                # The identity operators allow for an escape hatch that will
+                # do an exact string match of the version you wish to install.
+                # This will not be parsed by PEP 440 and we cannot determine
+                # any semantic meaning from it. This operator is discouraged
+                # but included entirely as an escape hatch.
+                (?<====)  # Only match for the identity operator
+                \s*
+                [^\s]*    # We just match everything, except for whitespace
+                          # since we are only testing for strict identity.
+            )
+            |
+            (?:
+                # The (non)equality operators allow for wild card and local
+                # versions to be specified so we have to define these two
+                # operators separately to enable that.
+                (?<===|!=)            # Only match for equals and not equals
+
+                \s*
+                v?
+                (?:[0-9]+!)?          # epoch
+                [0-9]+(?:\.[0-9]+)*   # release
+                (?:                   # pre release
+                    [-_\.]?
+                    (a|b|c|rc|alpha|beta|pre|preview)
+                    [-_\.]?
+                    [0-9]*
+                )?
+                (?:                   # post release
+                    (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
+                )?
+
+                # You cannot use a wild card and a dev or local version
+                # together so group them with a | and make them optional.
+                (?:
+                    (?:[-_\.]?dev[-_\.]?[0-9]*)?         # dev release
+                    (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local
+                    |
+                    \.\*  # Wild card syntax of .*
+                )?
+            )
+            |
+            (?:
+                # The compatible operator requires at least two digits in the
+                # release segment.
+                (?<=~=)               # Only match for the compatible operator
+
+                \s*
+                v?
+                (?:[0-9]+!)?          # epoch
+                [0-9]+(?:\.[0-9]+)+   # release  (We have a + instead of a *)
+                (?:                   # pre release
+                    [-_\.]?
+                    (a|b|c|rc|alpha|beta|pre|preview)
+                    [-_\.]?
+                    [0-9]*
+                )?
+                (?:                                   # post release
+                    (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
+                )?
+                (?:[-_\.]?dev[-_\.]?[0-9]*)?          # dev release
+            )
+            |
+            (?:
+                # All other operators only allow a sub set of what the
+                # (non)equality operators do. Specifically they do not allow
+                # local versions to be specified nor do they allow the prefix
+                # matching wild cards.
+                (?=": "greater_than_equal",
+        "<": "less_than",
+        ">": "greater_than",
+        "===": "arbitrary",
+    }
+
+    @_require_version_compare
+    def _compare_compatible(self, prospective, spec):
+        # Compatible releases have an equivalent combination of >= and ==. That
+        # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to
+        # implement this in terms of the other specifiers instead of
+        # implementing it ourselves. The only thing we need to do is construct
+        # the other specifiers.
+
+        # We want everything but the last item in the version, but we want to
+        # ignore post and dev releases and we want to treat the pre-release as
+        # it's own separate segment.
+        prefix = ".".join(
+            list(
+                itertools.takewhile(
+                    lambda x: (not x.startswith("post") and not
+                               x.startswith("dev")),
+                    _version_split(spec),
+                )
+            )[:-1]
+        )
+
+        # Add the prefix notation to the end of our string
+        prefix += ".*"
+
+        return (self._get_operator(">=")(prospective, spec) and
+                self._get_operator("==")(prospective, prefix))
+
+    @_require_version_compare
+    def _compare_equal(self, prospective, spec):
+        # We need special logic to handle prefix matching
+        if spec.endswith(".*"):
+            # In the case of prefix matching we want to ignore local segment.
+            prospective = Version(prospective.public)
+            # Split the spec out by dots, and pretend that there is an implicit
+            # dot in between a release segment and a pre-release segment.
+            spec = _version_split(spec[:-2])  # Remove the trailing .*
+
+            # Split the prospective version out by dots, and pretend that there
+            # is an implicit dot in between a release segment and a pre-release
+            # segment.
+            prospective = _version_split(str(prospective))
+
+            # Shorten the prospective version to be the same length as the spec
+            # so that we can determine if the specifier is a prefix of the
+            # prospective version or not.
+            prospective = prospective[:len(spec)]
+
+            # Pad out our two sides with zeros so that they both equal the same
+            # length.
+            spec, prospective = _pad_version(spec, prospective)
+        else:
+            # Convert our spec string into a Version
+            spec = Version(spec)
+
+            # If the specifier does not have a local segment, then we want to
+            # act as if the prospective version also does not have a local
+            # segment.
+            if not spec.local:
+                prospective = Version(prospective.public)
+
+        return prospective == spec
+
+    @_require_version_compare
+    def _compare_not_equal(self, prospective, spec):
+        return not self._compare_equal(prospective, spec)
+
+    @_require_version_compare
+    def _compare_less_than_equal(self, prospective, spec):
+        return prospective <= Version(spec)
+
+    @_require_version_compare
+    def _compare_greater_than_equal(self, prospective, spec):
+        return prospective >= Version(spec)
+
+    @_require_version_compare
+    def _compare_less_than(self, prospective, spec):
+        # Convert our spec to a Version instance, since we'll want to work with
+        # it as a version.
+        spec = Version(spec)
+
+        # Check to see if the prospective version is less than the spec
+        # version. If it's not we can short circuit and just return False now
+        # instead of doing extra unneeded work.
+        if not prospective < spec:
+            return False
+
+        # This special case is here so that, unless the specifier itself
+        # includes is a pre-release version, that we do not accept pre-release
+        # versions for the version mentioned in the specifier (e.g. <3.1 should
+        # not match 3.1.dev0, but should match 3.0.dev0).
+        if not spec.is_prerelease and prospective.is_prerelease:
+            if Version(prospective.base_version) == Version(spec.base_version):
+                return False
+
+        # If we've gotten to here, it means that prospective version is both
+        # less than the spec version *and* it's not a pre-release of the same
+        # version in the spec.
+        return True
+
+    @_require_version_compare
+    def _compare_greater_than(self, prospective, spec):
+        # Convert our spec to a Version instance, since we'll want to work with
+        # it as a version.
+        spec = Version(spec)
+
+        # Check to see if the prospective version is greater than the spec
+        # version. If it's not we can short circuit and just return False now
+        # instead of doing extra unneeded work.
+        if not prospective > spec:
+            return False
+
+        # This special case is here so that, unless the specifier itself
+        # includes is a post-release version, that we do not accept
+        # post-release versions for the version mentioned in the specifier
+        # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0).
+        if not spec.is_postrelease and prospective.is_postrelease:
+            if Version(prospective.base_version) == Version(spec.base_version):
+                return False
+
+        # Ensure that we do not allow a local version of the version mentioned
+        # in the specifier, which is techincally greater than, to match.
+        if prospective.local is not None:
+            if Version(prospective.base_version) == Version(spec.base_version):
+                return False
+
+        # If we've gotten to here, it means that prospective version is both
+        # greater than the spec version *and* it's not a pre-release of the
+        # same version in the spec.
+        return True
+
+    def _compare_arbitrary(self, prospective, spec):
+        return str(prospective).lower() == str(spec).lower()
+
+    @property
+    def prereleases(self):
+        # If there is an explicit prereleases set for this, then we'll just
+        # blindly use that.
+        if self._prereleases is not None:
+            return self._prereleases
+
+        # Look at all of our specifiers and determine if they are inclusive
+        # operators, and if they are if they are including an explicit
+        # prerelease.
+        operator, version = self._spec
+        if operator in ["==", ">=", "<=", "~=", "==="]:
+            # The == specifier can include a trailing .*, if it does we
+            # want to remove before parsing.
+            if operator == "==" and version.endswith(".*"):
+                version = version[:-2]
+
+            # Parse the version, and if it is a pre-release than this
+            # specifier allows pre-releases.
+            if parse(version).is_prerelease:
+                return True
+
+        return False
+
+    @prereleases.setter
+    def prereleases(self, value):
+        self._prereleases = value
+
+
+_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$")
+
+
+def _version_split(version):
+    result = []
+    for item in version.split("."):
+        match = _prefix_regex.search(item)
+        if match:
+            result.extend(match.groups())
+        else:
+            result.append(item)
+    return result
+
+
+def _pad_version(left, right):
+    left_split, right_split = [], []
+
+    # Get the release segment of our versions
+    left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left)))
+    right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right)))
+
+    # Get the rest of our versions
+    left_split.append(left[len(left_split[0]):])
+    right_split.append(right[len(right_split[0]):])
+
+    # Insert our padding
+    left_split.insert(
+        1,
+        ["0"] * max(0, len(right_split[0]) - len(left_split[0])),
+    )
+    right_split.insert(
+        1,
+        ["0"] * max(0, len(left_split[0]) - len(right_split[0])),
+    )
+
+    return (
+        list(itertools.chain(*left_split)),
+        list(itertools.chain(*right_split)),
+    )
+
+
+class SpecifierSet(BaseSpecifier):
+
+    def __init__(self, specifiers="", prereleases=None):
+        # Split on , to break each indidivual specifier into it's own item, and
+        # strip each item to remove leading/trailing whitespace.
+        specifiers = [s.strip() for s in specifiers.split(",") if s.strip()]
+
+        # Parsed each individual specifier, attempting first to make it a
+        # Specifier and falling back to a LegacySpecifier.
+        parsed = set()
+        for specifier in specifiers:
+            try:
+                parsed.add(Specifier(specifier))
+            except InvalidSpecifier:
+                parsed.add(LegacySpecifier(specifier))
+
+        # Turn our parsed specifiers into a frozen set and save them for later.
+        self._specs = frozenset(parsed)
+
+        # Store our prereleases value so we can use it later to determine if
+        # we accept prereleases or not.
+        self._prereleases = prereleases
+
+    def __repr__(self):
+        pre = (
+            ", prereleases={0!r}".format(self.prereleases)
+            if self._prereleases is not None
+            else ""
+        )
+
+        return "".format(str(self), pre)
+
+    def __str__(self):
+        return ",".join(sorted(str(s) for s in self._specs))
+
+    def __hash__(self):
+        return hash(self._specs)
+
+    def __and__(self, other):
+        if isinstance(other, string_types):
+            other = SpecifierSet(other)
+        elif not isinstance(other, SpecifierSet):
+            return NotImplemented
+
+        specifier = SpecifierSet()
+        specifier._specs = frozenset(self._specs | other._specs)
+
+        if self._prereleases is None and other._prereleases is not None:
+            specifier._prereleases = other._prereleases
+        elif self._prereleases is not None and other._prereleases is None:
+            specifier._prereleases = self._prereleases
+        elif self._prereleases == other._prereleases:
+            specifier._prereleases = self._prereleases
+        else:
+            raise ValueError(
+                "Cannot combine SpecifierSets with True and False prerelease "
+                "overrides."
+            )
+
+        return specifier
+
+    def __eq__(self, other):
+        if isinstance(other, string_types):
+            other = SpecifierSet(other)
+        elif isinstance(other, _IndividualSpecifier):
+            other = SpecifierSet(str(other))
+        elif not isinstance(other, SpecifierSet):
+            return NotImplemented
+
+        return self._specs == other._specs
+
+    def __ne__(self, other):
+        if isinstance(other, string_types):
+            other = SpecifierSet(other)
+        elif isinstance(other, _IndividualSpecifier):
+            other = SpecifierSet(str(other))
+        elif not isinstance(other, SpecifierSet):
+            return NotImplemented
+
+        return self._specs != other._specs
+
+    def __len__(self):
+        return len(self._specs)
+
+    def __iter__(self):
+        return iter(self._specs)
+
+    @property
+    def prereleases(self):
+        # If we have been given an explicit prerelease modifier, then we'll
+        # pass that through here.
+        if self._prereleases is not None:
+            return self._prereleases
+
+        # If we don't have any specifiers, and we don't have a forced value,
+        # then we'll just return None since we don't know if this should have
+        # pre-releases or not.
+        if not self._specs:
+            return None
+
+        # Otherwise we'll see if any of the given specifiers accept
+        # prereleases, if any of them do we'll return True, otherwise False.
+        return any(s.prereleases for s in self._specs)
+
+    @prereleases.setter
+    def prereleases(self, value):
+        self._prereleases = value
+
+    def __contains__(self, item):
+        return self.contains(item)
+
+    def contains(self, item, prereleases=None):
+        # Ensure that our item is a Version or LegacyVersion instance.
+        if not isinstance(item, (LegacyVersion, Version)):
+            item = parse(item)
+
+        # Determine if we're forcing a prerelease or not, if we're not forcing
+        # one for this particular filter call, then we'll use whatever the
+        # SpecifierSet thinks for whether or not we should support prereleases.
+        if prereleases is None:
+            prereleases = self.prereleases
+
+        # We can determine if we're going to allow pre-releases by looking to
+        # see if any of the underlying items supports them. If none of them do
+        # and this item is a pre-release then we do not allow it and we can
+        # short circuit that here.
+        # Note: This means that 1.0.dev1 would not be contained in something
+        #       like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0
+        if not prereleases and item.is_prerelease:
+            return False
+
+        # We simply dispatch to the underlying specs here to make sure that the
+        # given version is contained within all of them.
+        # Note: This use of all() here means that an empty set of specifiers
+        #       will always return True, this is an explicit design decision.
+        return all(
+            s.contains(item, prereleases=prereleases)
+            for s in self._specs
+        )
+
+    def filter(self, iterable, prereleases=None):
+        # Determine if we're forcing a prerelease or not, if we're not forcing
+        # one for this particular filter call, then we'll use whatever the
+        # SpecifierSet thinks for whether or not we should support prereleases.
+        if prereleases is None:
+            prereleases = self.prereleases
+
+        # If we have any specifiers, then we want to wrap our iterable in the
+        # filter method for each one, this will act as a logical AND amongst
+        # each specifier.
+        if self._specs:
+            for spec in self._specs:
+                iterable = spec.filter(iterable, prereleases=bool(prereleases))
+            return iterable
+        # If we do not have any specifiers, then we need to have a rough filter
+        # which will filter out any pre-releases, unless there are no final
+        # releases, and which will filter out LegacyVersion in general.
+        else:
+            filtered = []
+            found_prereleases = []
+
+            for item in iterable:
+                # Ensure that we some kind of Version class for this item.
+                if not isinstance(item, (LegacyVersion, Version)):
+                    parsed_version = parse(item)
+                else:
+                    parsed_version = item
+
+                # Filter out any item which is parsed as a LegacyVersion
+                if isinstance(parsed_version, LegacyVersion):
+                    continue
+
+                # Store any item which is a pre-release for later unless we've
+                # already found a final version or we are accepting prereleases
+                if parsed_version.is_prerelease and not prereleases:
+                    if not filtered:
+                        found_prereleases.append(item)
+                else:
+                    filtered.append(item)
+
+            # If we've found no items except for pre-releases, then we'll go
+            # ahead and use the pre-releases
+            if not filtered and found_prereleases and prereleases is None:
+                return found_prereleases
+
+            return filtered
diff --git a/pip/_vendor/packaging/utils.py b/pip/_vendor/packaging/utils.py
new file mode 100644
index 00000000000..942387cef5d
--- /dev/null
+++ b/pip/_vendor/packaging/utils.py
@@ -0,0 +1,14 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+from __future__ import absolute_import, division, print_function
+
+import re
+
+
+_canonicalize_regex = re.compile(r"[-_.]+")
+
+
+def canonicalize_name(name):
+    # This is taken from PEP 503.
+    return _canonicalize_regex.sub("-", name).lower()
diff --git a/pip/_vendor/packaging/version.py b/pip/_vendor/packaging/version.py
new file mode 100644
index 00000000000..83b5ee8c5ef
--- /dev/null
+++ b/pip/_vendor/packaging/version.py
@@ -0,0 +1,393 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+from __future__ import absolute_import, division, print_function
+
+import collections
+import itertools
+import re
+
+from ._structures import Infinity
+
+
+__all__ = [
+    "parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"
+]
+
+
+_Version = collections.namedtuple(
+    "_Version",
+    ["epoch", "release", "dev", "pre", "post", "local"],
+)
+
+
+def parse(version):
+    """
+    Parse the given version string and return either a :class:`Version` object
+    or a :class:`LegacyVersion` object depending on if the given version is
+    a valid PEP 440 version or a legacy version.
+    """
+    try:
+        return Version(version)
+    except InvalidVersion:
+        return LegacyVersion(version)
+
+
+class InvalidVersion(ValueError):
+    """
+    An invalid version was found, users should refer to PEP 440.
+    """
+
+
+class _BaseVersion(object):
+
+    def __hash__(self):
+        return hash(self._key)
+
+    def __lt__(self, other):
+        return self._compare(other, lambda s, o: s < o)
+
+    def __le__(self, other):
+        return self._compare(other, lambda s, o: s <= o)
+
+    def __eq__(self, other):
+        return self._compare(other, lambda s, o: s == o)
+
+    def __ge__(self, other):
+        return self._compare(other, lambda s, o: s >= o)
+
+    def __gt__(self, other):
+        return self._compare(other, lambda s, o: s > o)
+
+    def __ne__(self, other):
+        return self._compare(other, lambda s, o: s != o)
+
+    def _compare(self, other, method):
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return method(self._key, other._key)
+
+
+class LegacyVersion(_BaseVersion):
+
+    def __init__(self, version):
+        self._version = str(version)
+        self._key = _legacy_cmpkey(self._version)
+
+    def __str__(self):
+        return self._version
+
+    def __repr__(self):
+        return "".format(repr(str(self)))
+
+    @property
+    def public(self):
+        return self._version
+
+    @property
+    def base_version(self):
+        return self._version
+
+    @property
+    def local(self):
+        return None
+
+    @property
+    def is_prerelease(self):
+        return False
+
+    @property
+    def is_postrelease(self):
+        return False
+
+
+_legacy_version_component_re = re.compile(
+    r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE,
+)
+
+_legacy_version_replacement_map = {
+    "pre": "c", "preview": "c", "-": "final-", "rc": "c", "dev": "@",
+}
+
+
+def _parse_version_parts(s):
+    for part in _legacy_version_component_re.split(s):
+        part = _legacy_version_replacement_map.get(part, part)
+
+        if not part or part == ".":
+            continue
+
+        if part[:1] in "0123456789":
+            # pad for numeric comparison
+            yield part.zfill(8)
+        else:
+            yield "*" + part
+
+    # ensure that alpha/beta/candidate are before final
+    yield "*final"
+
+
+def _legacy_cmpkey(version):
+    # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch
+    # greater than or equal to 0. This will effectively put the LegacyVersion,
+    # which uses the defacto standard originally implemented by setuptools,
+    # as before all PEP 440 versions.
+    epoch = -1
+
+    # This scheme is taken from pkg_resources.parse_version setuptools prior to
+    # it's adoption of the packaging library.
+    parts = []
+    for part in _parse_version_parts(version.lower()):
+        if part.startswith("*"):
+            # remove "-" before a prerelease tag
+            if part < "*final":
+                while parts and parts[-1] == "*final-":
+                    parts.pop()
+
+            # remove trailing zeros from each series of numeric parts
+            while parts and parts[-1] == "00000000":
+                parts.pop()
+
+        parts.append(part)
+    parts = tuple(parts)
+
+    return epoch, parts
+
+# Deliberately not anchored to the start and end of the string, to make it
+# easier for 3rd party code to reuse
+VERSION_PATTERN = r"""
+    v?
+    (?:
+        (?:(?P[0-9]+)!)?                           # epoch
+        (?P[0-9]+(?:\.[0-9]+)*)                  # release segment
+        (?P
                                          # pre-release
+            [-_\.]?
+            (?P(a|b|c|rc|alpha|beta|pre|preview))
+            [-_\.]?
+            (?P[0-9]+)?
+        )?
+        (?P                                         # post release
+            (?:-(?P[0-9]+))
+            |
+            (?:
+                [-_\.]?
+                (?Ppost|rev|r)
+                [-_\.]?
+                (?P[0-9]+)?
+            )
+        )?
+        (?P                                          # dev release
+            [-_\.]?
+            (?Pdev)
+            [-_\.]?
+            (?P[0-9]+)?
+        )?
+    )
+    (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
+"""
+
+
+class Version(_BaseVersion):
+
+    _regex = re.compile(
+        r"^\s*" + VERSION_PATTERN + r"\s*$",
+        re.VERBOSE | re.IGNORECASE,
+    )
+
+    def __init__(self, version):
+        # Validate the version and parse it into pieces
+        match = self._regex.search(version)
+        if not match:
+            raise InvalidVersion("Invalid version: '{0}'".format(version))
+
+        # Store the parsed out pieces of the version
+        self._version = _Version(
+            epoch=int(match.group("epoch")) if match.group("epoch") else 0,
+            release=tuple(int(i) for i in match.group("release").split(".")),
+            pre=_parse_letter_version(
+                match.group("pre_l"),
+                match.group("pre_n"),
+            ),
+            post=_parse_letter_version(
+                match.group("post_l"),
+                match.group("post_n1") or match.group("post_n2"),
+            ),
+            dev=_parse_letter_version(
+                match.group("dev_l"),
+                match.group("dev_n"),
+            ),
+            local=_parse_local_version(match.group("local")),
+        )
+
+        # Generate a key which will be used for sorting
+        self._key = _cmpkey(
+            self._version.epoch,
+            self._version.release,
+            self._version.pre,
+            self._version.post,
+            self._version.dev,
+            self._version.local,
+        )
+
+    def __repr__(self):
+        return "".format(repr(str(self)))
+
+    def __str__(self):
+        parts = []
+
+        # Epoch
+        if self._version.epoch != 0:
+            parts.append("{0}!".format(self._version.epoch))
+
+        # Release segment
+        parts.append(".".join(str(x) for x in self._version.release))
+
+        # Pre-release
+        if self._version.pre is not None:
+            parts.append("".join(str(x) for x in self._version.pre))
+
+        # Post-release
+        if self._version.post is not None:
+            parts.append(".post{0}".format(self._version.post[1]))
+
+        # Development release
+        if self._version.dev is not None:
+            parts.append(".dev{0}".format(self._version.dev[1]))
+
+        # Local version segment
+        if self._version.local is not None:
+            parts.append(
+                "+{0}".format(".".join(str(x) for x in self._version.local))
+            )
+
+        return "".join(parts)
+
+    @property
+    def public(self):
+        return str(self).split("+", 1)[0]
+
+    @property
+    def base_version(self):
+        parts = []
+
+        # Epoch
+        if self._version.epoch != 0:
+            parts.append("{0}!".format(self._version.epoch))
+
+        # Release segment
+        parts.append(".".join(str(x) for x in self._version.release))
+
+        return "".join(parts)
+
+    @property
+    def local(self):
+        version_string = str(self)
+        if "+" in version_string:
+            return version_string.split("+", 1)[1]
+
+    @property
+    def is_prerelease(self):
+        return bool(self._version.dev or self._version.pre)
+
+    @property
+    def is_postrelease(self):
+        return bool(self._version.post)
+
+
+def _parse_letter_version(letter, number):
+    if letter:
+        # We consider there to be an implicit 0 in a pre-release if there is
+        # not a numeral associated with it.
+        if number is None:
+            number = 0
+
+        # We normalize any letters to their lower case form
+        letter = letter.lower()
+
+        # We consider some words to be alternate spellings of other words and
+        # in those cases we want to normalize the spellings to our preferred
+        # spelling.
+        if letter == "alpha":
+            letter = "a"
+        elif letter == "beta":
+            letter = "b"
+        elif letter in ["c", "pre", "preview"]:
+            letter = "rc"
+        elif letter in ["rev", "r"]:
+            letter = "post"
+
+        return letter, int(number)
+    if not letter and number:
+        # We assume if we are given a number, but we are not given a letter
+        # then this is using the implicit post release syntax (e.g. 1.0-1)
+        letter = "post"
+
+        return letter, int(number)
+
+
+_local_version_seperators = re.compile(r"[\._-]")
+
+
+def _parse_local_version(local):
+    """
+    Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
+    """
+    if local is not None:
+        return tuple(
+            part.lower() if not part.isdigit() else int(part)
+            for part in _local_version_seperators.split(local)
+        )
+
+
+def _cmpkey(epoch, release, pre, post, dev, local):
+    # When we compare a release version, we want to compare it with all of the
+    # trailing zeros removed. So we'll use a reverse the list, drop all the now
+    # leading zeros until we come to something non zero, then take the rest
+    # re-reverse it back into the correct order and make it a tuple and use
+    # that for our sorting key.
+    release = tuple(
+        reversed(list(
+            itertools.dropwhile(
+                lambda x: x == 0,
+                reversed(release),
+            )
+        ))
+    )
+
+    # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
+    # We'll do this by abusing the pre segment, but we _only_ want to do this
+    # if there is not a pre or a post segment. If we have one of those then
+    # the normal sorting rules will handle this case correctly.
+    if pre is None and post is None and dev is not None:
+        pre = -Infinity
+    # Versions without a pre-release (except as noted above) should sort after
+    # those with one.
+    elif pre is None:
+        pre = Infinity
+
+    # Versions without a post segment should sort before those with one.
+    if post is None:
+        post = -Infinity
+
+    # Versions without a development segment should sort after those with one.
+    if dev is None:
+        dev = Infinity
+
+    if local is None:
+        # Versions without a local segment should sort before those with one.
+        local = -Infinity
+    else:
+        # Versions with a local segment need that segment parsed to implement
+        # the sorting rules in PEP440.
+        # - Alpha numeric segments sort before numeric segments
+        # - Alpha numeric segments sort lexicographically
+        # - Numeric segments sort numerically
+        # - Shorter versions sort before longer versions when the prefixes
+        #   match exactly
+        local = tuple(
+            (i, "") if isinstance(i, int) else (-Infinity, i)
+            for i in local
+        )
+
+    return epoch, release, pre, post, dev, local
diff --git a/pip/_vendor/pkg_resources.py b/pip/_vendor/pkg_resources/__init__.py
similarity index 68%
rename from pip/_vendor/pkg_resources.py
rename to pip/_vendor/pkg_resources/__init__.py
index ee8a4823b63..91e1a9d2415 100644
--- a/pip/_vendor/pkg_resources.py
+++ b/pip/_vendor/pkg_resources/__init__.py
@@ -14,45 +14,37 @@
 method.
 """
 
+from __future__ import absolute_import
+
 import sys
 import os
+import io
 import time
 import re
-import imp
+import types
 import zipfile
 import zipimport
 import warnings
 import stat
 import functools
 import pkgutil
-import token
-import symbol
 import operator
 import platform
+import collections
+import plistlib
+import email.parser
+import tempfile
+import textwrap
 from pkgutil import get_importer
 
 try:
-    from urlparse import urlparse, urlunparse
+    import _imp
 except ImportError:
-    from urllib.parse import urlparse, urlunparse
+    # Python 3.2 compatibility
+    import imp as _imp
 
-try:
-    frozenset
-except NameError:
-    from sets import ImmutableSet as frozenset
-try:
-    basestring
-    next = lambda o: o.next()
-    from cStringIO import StringIO as BytesIO
-except NameError:
-    basestring = str
-    from io import BytesIO
-    def execfile(fn, globs=None, locs=None):
-        if globs is None:
-            globs = globals()
-        if locs is None:
-            locs = globs
-        exec(compile(open(fn).read(), fn, 'exec'), globs, locs)
+from pip._vendor import six
+from pip._vendor.six.moves import urllib, map, filter
 
 # capture these to bypass sandboxing
 from os import utime
@@ -66,25 +58,153 @@ def execfile(fn, globs=None, locs=None):
 from os import open as os_open
 from os.path import isdir, split
 
-# Avoid try/except due to potential problems with delayed import mechanisms.
-if sys.version_info >= (3, 3) and sys.implementation.name == "cpython":
-    import importlib._bootstrap as importlib_bootstrap
-else:
-    importlib_bootstrap = None
-
 try:
-    import parser
+    import importlib.machinery as importlib_machinery
+    # access attribute to force import under delayed import mechanisms.
+    importlib_machinery.__name__
 except ImportError:
+    importlib_machinery = None
+
+from pip._vendor import packaging
+__import__('pip._vendor.packaging.version')
+__import__('pip._vendor.packaging.specifiers')
+__import__('pip._vendor.packaging.requirements')
+__import__('pip._vendor.packaging.markers')
+
+
+if (3, 0) < sys.version_info < (3, 3):
+    msg = (
+        "Support for Python 3.0-3.2 has been dropped. Future versions "
+        "will fail here."
+    )
+    warnings.warn(msg)
+
+# declare some globals that will be defined later to
+# satisfy the linters.
+require = None
+working_set = None
+
+
+class PEP440Warning(RuntimeWarning):
+    """
+    Used when there is an issue with a version or specifier not complying with
+    PEP 440.
+    """
+
+
+class _SetuptoolsVersionMixin(object):
+
+    def __hash__(self):
+        return super(_SetuptoolsVersionMixin, self).__hash__()
+
+    def __lt__(self, other):
+        if isinstance(other, tuple):
+            return tuple(self) < other
+        else:
+            return super(_SetuptoolsVersionMixin, self).__lt__(other)
+
+    def __le__(self, other):
+        if isinstance(other, tuple):
+            return tuple(self) <= other
+        else:
+            return super(_SetuptoolsVersionMixin, self).__le__(other)
+
+    def __eq__(self, other):
+        if isinstance(other, tuple):
+            return tuple(self) == other
+        else:
+            return super(_SetuptoolsVersionMixin, self).__eq__(other)
+
+    def __ge__(self, other):
+        if isinstance(other, tuple):
+            return tuple(self) >= other
+        else:
+            return super(_SetuptoolsVersionMixin, self).__ge__(other)
+
+    def __gt__(self, other):
+        if isinstance(other, tuple):
+            return tuple(self) > other
+        else:
+            return super(_SetuptoolsVersionMixin, self).__gt__(other)
+
+    def __ne__(self, other):
+        if isinstance(other, tuple):
+            return tuple(self) != other
+        else:
+            return super(_SetuptoolsVersionMixin, self).__ne__(other)
+
+    def __getitem__(self, key):
+        return tuple(self)[key]
+
+    def __iter__(self):
+        component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE)
+        replace = {
+            'pre': 'c',
+            'preview': 'c',
+            '-': 'final-',
+            'rc': 'c',
+            'dev': '@',
+        }.get
+
+        def _parse_version_parts(s):
+            for part in component_re.split(s):
+                part = replace(part, part)
+                if not part or part == '.':
+                    continue
+                if part[:1] in '0123456789':
+                    # pad for numeric comparison
+                    yield part.zfill(8)
+                else:
+                    yield '*'+part
+
+            # ensure that alpha/beta/candidate are before final
+            yield '*final'
+
+        def old_parse_version(s):
+            parts = []
+            for part in _parse_version_parts(s.lower()):
+                if part.startswith('*'):
+                    # remove '-' before a prerelease tag
+                    if part < '*final':
+                        while parts and parts[-1] == '*final-':
+                            parts.pop()
+                    # remove trailing zeros from each series of numeric parts
+                    while parts and parts[-1] == '00000000':
+                        parts.pop()
+                parts.append(part)
+            return tuple(parts)
+
+        # Warn for use of this function
+        warnings.warn(
+            "You have iterated over the result of "
+            "pkg_resources.parse_version. This is a legacy behavior which is "
+            "inconsistent with the new version class introduced in setuptools "
+            "8.0. In most cases, conversion to a tuple is unnecessary. For "
+            "comparison of versions, sort the Version instances directly. If "
+            "you have another use case requiring the tuple, please file a "
+            "bug with the setuptools project describing that need.",
+            RuntimeWarning,
+            stacklevel=1,
+        )
+
+        for part in old_parse_version(str(self)):
+            yield part
+
+
+class SetuptoolsVersion(_SetuptoolsVersionMixin, packaging.version.Version):
     pass
 
-def _bypass_ensure_directory(name, mode=0x1FF):  # 0777
-    # Sandbox-bypassing version of ensure_directory()
-    if not WRITE_SUPPORT:
-        raise IOError('"os.mkdir" not supported on this platform.')
-    dirname, filename = split(name)
-    if dirname and filename and not isdir(dirname):
-        _bypass_ensure_directory(dirname)
-        mkdir(dirname, mode)
+
+class SetuptoolsLegacyVersion(_SetuptoolsVersionMixin,
+                              packaging.version.LegacyVersion):
+    pass
+
+
+def parse_version(v):
+    try:
+        return SetuptoolsVersion(v)
+    except packaging.version.InvalidVersion:
+        return SetuptoolsLegacyVersion(v)
 
 
 _state_vars = {}
@@ -141,13 +261,15 @@ def get_supported_platform():
         try:
             plat = 'macosx-%s-%s' % ('.'.join(_macosx_vers()[:2]), m.group(3))
         except ValueError:
-            pass    # not Mac OS X
+            # not Mac OS X
+            pass
     return plat
 
 __all__ = [
     # Basic resource access and distribution/entry point discovery
     'require', 'run_script', 'get_provider',  'get_distribution',
-    'load_entry_point', 'get_entry_map', 'get_entry_info', 'iter_entry_points',
+    'load_entry_point', 'get_entry_map', 'get_entry_info',
+    'iter_entry_points',
     'resource_string', 'resource_stream', 'resource_filename',
     'resource_listdir', 'resource_exists', 'resource_isdir',
 
@@ -161,8 +283,11 @@ def get_supported_platform():
     'Distribution', 'Requirement', 'EntryPoint',
 
     # Exceptions
-    'ResolutionError','VersionConflict','DistributionNotFound','UnknownExtra',
-    'ExtractionError',
+    'ResolutionError', 'VersionConflict', 'DistributionNotFound',
+    'UnknownExtra', 'ExtractionError',
+
+    # Warnings
+    'PEP440Warning',
 
     # Parsing functions and string utilities
     'parse_requirements', 'parse_version', 'safe_name', 'safe_version',
@@ -191,12 +316,79 @@ class ResolutionError(Exception):
     def __repr__(self):
         return self.__class__.__name__+repr(self.args)
 
+
 class VersionConflict(ResolutionError):
-    """An already-installed version conflicts with the requested version"""
+    """
+    An already-installed version conflicts with the requested version.
+
+    Should be initialized with the installed Distribution and the requested
+    Requirement.
+    """
+
+    _template = "{self.dist} is installed but {self.req} is required"
+
+    @property
+    def dist(self):
+        return self.args[0]
+
+    @property
+    def req(self):
+        return self.args[1]
+
+    def report(self):
+        return self._template.format(**locals())
+
+    def with_context(self, required_by):
+        """
+        If required_by is non-empty, return a version of self that is a
+        ContextualVersionConflict.
+        """
+        if not required_by:
+            return self
+        args = self.args + (required_by,)
+        return ContextualVersionConflict(*args)
+
+
+class ContextualVersionConflict(VersionConflict):
+    """
+    A VersionConflict that accepts a third parameter, the set of the
+    requirements that required the installed Distribution.
+    """
+
+    _template = VersionConflict._template + ' by {self.required_by}'
+
+    @property
+    def required_by(self):
+        return self.args[2]
+
 
 class DistributionNotFound(ResolutionError):
     """A requested distribution was not found"""
 
+    _template = ("The '{self.req}' distribution was not found "
+                 "and is required by {self.requirers_str}")
+
+    @property
+    def req(self):
+        return self.args[0]
+
+    @property
+    def requirers(self):
+        return self.args[1]
+
+    @property
+    def requirers_str(self):
+        if not self.requirers:
+            return 'the application'
+        return ', '.join(self.requirers)
+
+    def report(self):
+        return self._template.format(**locals())
+
+    def __str__(self):
+        return self.report()
+
+
 class UnknownExtra(ResolutionError):
     """Distribution doesn't have an "extra feature" of the given name"""
 _provider_factories = {}
@@ -219,7 +411,7 @@ def register_loader_type(loader_type, provider_factory):
 
 def get_provider(moduleOrReq):
     """Return an IResourceProvider for the named module or requirement"""
-    if isinstance(moduleOrReq,Requirement):
+    if isinstance(moduleOrReq, Requirement):
         return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0]
     try:
         module = sys.modules[moduleOrReq]
@@ -231,11 +423,9 @@ def get_provider(moduleOrReq):
 
 def _macosx_vers(_cache=[]):
     if not _cache:
-        import platform
         version = platform.mac_ver()[0]
         # fallback for MacPorts
         if version == '':
-            import plistlib
             plist = '/System/Library/CoreServices/SystemVersion.plist'
             if os.path.exists(plist):
                 if hasattr(plistlib, 'readPlist'):
@@ -247,7 +437,7 @@ def _macosx_vers(_cache=[]):
     return _cache[0]
 
 def _macosx_arch(machine):
-    return {'PowerPC':'ppc', 'Power_Macintosh':'ppc'}.get(machine,machine)
+    return {'PowerPC': 'ppc', 'Power_Macintosh': 'ppc'}.get(machine, machine)
 
 def get_build_platform():
     """Return this platform's string for platform-specific distributions
@@ -276,10 +466,11 @@ def get_build_platform():
 
 macosVersionString = re.compile(r"macosx-(\d+)\.(\d+)-(.*)")
 darwinVersionString = re.compile(r"darwin-(\d+)\.(\d+)\.(\d+)-(.*)")
-get_platform = get_build_platform   # XXX backward compat
+# XXX backward compat
+get_platform = get_build_platform
 
 
-def compatible_platforms(provided,required):
+def compatible_platforms(provided, required):
     """Can code for the `provided` platform run on the `required` platform?
 
     Returns true if either platform is ``None``, or the platforms are equal.
@@ -287,7 +478,8 @@ def compatible_platforms(provided,required):
     XXX Needs compatibility checks for Linux and other unixy OSes.
     """
     if provided is None or required is None or provided==required:
-        return True     # easy case
+        # easy case
+        return True
 
     # Mac OS X special cases
     reqMac = macosVersionString.match(required)
@@ -305,13 +497,9 @@ def compatible_platforms(provided,required):
                 macosversion = "%s.%s" % (reqMac.group(1), reqMac.group(2))
                 if dversion == 7 and macosversion >= "10.3" or \
                         dversion == 8 and macosversion >= "10.4":
-
-                    #import warnings
-                    #warnings.warn("Mac eggs should be rebuilt to "
-                    #    "use the macosx designation instead of darwin.",
-                    #    category=DeprecationWarning)
                     return True
-            return False    # egg isn't macosx or legacy darwin
+            # egg isn't macosx or legacy darwin
+            return False
 
         # are they the same major version and machine type?
         if provMac.group(1) != reqMac.group(1) or \
@@ -336,13 +524,16 @@ def run_script(dist_spec, script_name):
     ns['__name__'] = name
     require(dist_spec)[0].run_script(script_name, ns)
 
-run_main = run_script   # backward compatibility
+# backward compatibility
+run_main = run_script
 
 def get_distribution(dist):
     """Return a current distribution object for a Requirement or string"""
-    if isinstance(dist,basestring): dist = Requirement.parse(dist)
-    if isinstance(dist,Requirement): dist = get_provider(dist)
-    if not isinstance(dist,Distribution):
+    if isinstance(dist, six.string_types):
+        dist = Requirement.parse(dist)
+    if isinstance(dist, Requirement):
+        dist = get_provider(dist)
+    if not isinstance(dist, Distribution):
         raise TypeError("Expected string, Requirement, or Distribution", dist)
     return dist
 
@@ -484,7 +675,7 @@ def add_entry(self, entry):
         for dist in find_distributions(entry, True):
             self.add(dist, entry, False)
 
-    def __contains__(self,dist):
+    def __contains__(self, dist):
         """True if `dist` is the active distribution for its project"""
         return self.by_key.get(dist.key) == dist
 
@@ -500,9 +691,9 @@ def find(self, req):
         """
         dist = self.by_key.get(req.key)
         if dist is not None and dist not in req:
-            raise VersionConflict(dist,req)     # XXX add more info
-        else:
-            return dist
+            # XXX add more info
+            raise VersionConflict(dist, req)
+        return dist
 
     def iter_entry_points(self, group, name=None):
         """Yield entry point objects from `group` matching `name`
@@ -557,14 +748,15 @@ def add(self, dist, entry=None, insert=True, replace=False):
         will be called.
         """
         if insert:
-            dist.insert_on(self.entries, entry)
+            dist.insert_on(self.entries, entry, replace=replace)
 
         if entry is None:
             entry = dist.location
         keys = self.entry_keys.setdefault(entry,[])
         keys2 = self.entry_keys.setdefault(dist.location,[])
         if not replace and dist.key in self.by_key:
-            return      # ignore hidden distros
+            # ignore hidden distros
+            return
 
         self.by_key[dist.key] = dist
         if dist.key not in keys:
@@ -592,16 +784,30 @@ def resolve(self, requirements, env=None, installer=None,
         it.
         """
 
-        requirements = list(requirements)[::-1]  # set up the stack
-        processed = {}  # set of processed requirements
-        best = {}  # key -> dist
+        # set up the stack
+        requirements = list(requirements)[::-1]
+        # set of processed requirements
+        processed = {}
+        # key -> dist
+        best = {}
         to_activate = []
 
+        req_extras = _ReqExtras()
+
+        # Mapping of requirement to set of distributions that required it;
+        # useful for reporting info about conflicts.
+        required_by = collections.defaultdict(set)
+
         while requirements:
-            req = requirements.pop(0)   # process dependencies breadth-first
+            # process dependencies breadth-first
+            req = requirements.pop(0)
             if req in processed:
                 # Ignore cyclic or redundant dependencies
                 continue
+
+            if not req_extras.markers_pass(req):
+                continue
+
             dist = best.get(req.key)
             if dist is None:
                 # Find the best distribution and add it to the map
@@ -619,21 +825,27 @@ def resolve(self, requirements, env=None, installer=None,
                             ws = WorkingSet([])
                     dist = best[req.key] = env.best_match(req, ws, installer)
                     if dist is None:
-                        #msg = ("The '%s' distribution was not found on this "
-                        #       "system, and is required by this application.")
-                        #raise DistributionNotFound(msg % req)
-
-                        # unfortunately, zc.buildout uses a str(err)
-                        # to get the name of the distribution here..
-                        raise DistributionNotFound(req)
+                        requirers = required_by.get(req, None)
+                        raise DistributionNotFound(req, requirers)
                 to_activate.append(dist)
             if dist not in req:
                 # Oops, the "best" so far conflicts with a dependency
-                raise VersionConflict(dist,req) # XXX put more info here
-            requirements.extend(dist.requires(req.extras)[::-1])
+                dependent_req = required_by[req]
+                raise VersionConflict(dist, req).with_context(dependent_req)
+
+            # push the new requirements onto the stack
+            new_requirements = dist.requires(req.extras)[::-1]
+            requirements.extend(new_requirements)
+
+            # Register the new requirements needed by req
+            for new_requirement in new_requirements:
+                required_by[new_requirement].add(req.project_name)
+                req_extras[new_requirement] = req.extras
+
             processed[req] = True
 
-        return to_activate    # return list of distros to activate
+        # return list of distros to activate
+        return to_activate
 
     def find_plugins(self, plugin_env, full_env=None, installer=None,
             fallback=True):
@@ -644,8 +856,10 @@ def find_plugins(self, plugin_env, full_env=None, installer=None,
             distributions, errors = working_set.find_plugins(
                 Environment(plugin_dirlist)
             )
-            map(working_set.add, distributions)  # add plugins+libs to sys.path
-            print 'Could not load', errors        # display errors
+            # add plugins+libs to sys.path
+            map(working_set.add, distributions)
+            # display errors
+            print('Could not load', errors)
 
         The `plugin_env` should be an ``Environment`` instance that contains
         only distributions that are in the project's "plugin directory" or
@@ -670,7 +884,8 @@ def find_plugins(self, plugin_env, full_env=None, installer=None,
         """
 
         plugin_projects = list(plugin_env)
-        plugin_projects.sort()  # scan project names in alphabetic order
+        # scan project names in alphabetic order
+        plugin_projects.sort()
 
         error_info = {}
         distributions = {}
@@ -682,7 +897,8 @@ def find_plugins(self, plugin_env, full_env=None, installer=None,
             env = full_env + plugin_env
 
         shadow_set = self.__class__([])
-        list(map(shadow_set.add, self))   # put all our entries in shadow_set
+        # put all our entries in shadow_set
+        list(map(shadow_set.add, self))
 
         for project_name in plugin_projects:
 
@@ -693,13 +909,15 @@ def find_plugins(self, plugin_env, full_env=None, installer=None,
                 try:
                     resolvees = shadow_set.resolve(req, env, installer)
 
-                except ResolutionError:
-                    v = sys.exc_info()[1]
-                    error_info[dist] = v    # save error info
+                except ResolutionError as v:
+                    # save error info
+                    error_info[dist] = v
                     if fallback:
-                        continue    # try the next older version of project
+                        # try the next older version of project
+                        continue
                     else:
-                        break       # give up on this project, keep going
+                        # give up on this project, keep going
+                        break
 
                 else:
                     list(map(shadow_set.add, resolvees))
@@ -755,10 +973,31 @@ def __setstate__(self, e_k_b_c):
         self.callbacks = callbacks[:]
 
 
+class _ReqExtras(dict):
+    """
+    Map each requirement to the extras that demanded it.
+    """
+
+    def markers_pass(self, req):
+        """
+        Evaluate markers for req against each extra that
+        demanded it.
+
+        Return False if the req has a marker and fails
+        evaluation. Otherwise, return True.
+        """
+        extra_evals = (
+            req.marker.evaluate({'extra': extra})
+            for extra in self.get(req, ()) + (None,)
+        )
+        return not req.marker or any(extra_evals)
+
+
 class Environment(object):
     """Searchable snapshot of distributions on a search path"""
 
-    def __init__(self, search_path=None, platform=get_supported_platform(), python=PY_MAJOR):
+    def __init__(self, search_path=None, platform=get_supported_platform(),
+            python=PY_MAJOR):
         """Snapshot distributions available on a search path
 
         Any distributions found on `search_path` are added to the environment.
@@ -776,7 +1015,6 @@ def __init__(self, search_path=None, platform=get_supported_platform(), python=P
         running platform or Python version.
         """
         self._distmap = {}
-        self._cache = {}
         self.platform = platform
         self.python = python
         self.scan(search_path)
@@ -790,7 +1028,7 @@ def can_add(self, dist):
         """
         return (self.python is None or dist.py_version is None
             or dist.py_version==self.python) \
-            and compatible_platforms(dist.platform,self.platform)
+            and compatible_platforms(dist.platform, self.platform)
 
     def remove(self, dist):
         """Remove `dist` from the environment"""
@@ -811,30 +1049,25 @@ def scan(self, search_path=None):
             for dist in find_distributions(item):
                 self.add(dist)
 
-    def __getitem__(self,project_name):
+    def __getitem__(self, project_name):
         """Return a newest-to-oldest list of distributions for `project_name`
-        """
-        try:
-            return self._cache[project_name]
-        except KeyError:
-            project_name = project_name.lower()
-            if project_name not in self._distmap:
-                return []
 
-        if project_name not in self._cache:
-            dists = self._cache[project_name] = self._distmap[project_name]
-            _sort_dists(dists)
+        Uses case-insensitive `project_name` comparison, assuming all the
+        project's distributions use their project's name converted to all
+        lowercase as their key.
 
-        return self._cache[project_name]
+        """
+        distribution_key = project_name.lower()
+        return self._distmap.get(distribution_key, [])
 
-    def add(self,dist):
-        """Add `dist` if we ``can_add()`` it and it isn't already added"""
+    def add(self, dist):
+        """Add `dist` if we ``can_add()`` it and it has not already been added
+        """
         if self.can_add(dist) and dist.has_version():
-            dists = self._distmap.setdefault(dist.key,[])
+            dists = self._distmap.setdefault(dist.key, [])
             if dist not in dists:
                 dists.append(dist)
-                if dist.key in self._cache:
-                    _sort_dists(self._cache[dist.key])
+                dists.sort(key=operator.attrgetter('hashcmp'), reverse=True)
 
     def best_match(self, req, working_set, installer=None):
         """Find distribution best matching `req` and usable on `working_set`
@@ -855,7 +1088,8 @@ def best_match(self, req, working_set, installer=None):
         for dist in self[req.key]:
             if dist in req:
                 return dist
-        return self.obtain(req, installer) # try and download/install
+        # try to download/install
+        return self.obtain(req, installer)
 
     def obtain(self, requirement, installer=None):
         """Obtain a distribution matching `requirement` (e.g. via download)
@@ -872,13 +1106,14 @@ def obtain(self, requirement, installer=None):
     def __iter__(self):
         """Yield the unique project names of the available distributions"""
         for key in self._distmap.keys():
-            if self[key]: yield key
+            if self[key]:
+                yield key
 
     def __iadd__(self, other):
         """In-place addition of a distribution or environment"""
-        if isinstance(other,Distribution):
+        if isinstance(other, Distribution):
             self.add(other)
-        elif isinstance(other,Environment):
+        elif isinstance(other, Environment):
             for project in other:
                 for dist in other[project]:
                     self.add(dist)
@@ -894,7 +1129,8 @@ def __add__(self, other):
         return new
 
 
-AvailableDistributions = Environment    # XXX backward compatibility
+# XXX backward compatibility
+AvailableDistributions = Environment
 
 
 class ExtractionError(RuntimeError):
@@ -960,22 +1196,23 @@ def extraction_error(self):
         old_exc = sys.exc_info()[1]
         cache_path = self.extraction_path or get_default_cache()
 
-        err = ExtractionError("""Can't extract file(s) to egg cache
+        tmpl = textwrap.dedent("""
+            Can't extract file(s) to egg cache
 
-The following error occurred while trying to extract file(s) to the Python egg
-cache:
+            The following error occurred while trying to extract file(s) to the Python egg
+            cache:
 
-  %s
+              {old_exc}
 
-The Python egg cache directory is currently set to:
+            The Python egg cache directory is currently set to:
 
-  %s
+              {cache_path}
 
-Perhaps your account does not have write access to this directory?  You can
-change the cache directory by setting the PYTHON_EGG_CACHE environment
-variable to point to an accessible directory.
-""" % (old_exc, cache_path)
-        )
+            Perhaps your account does not have write access to this directory?  You can
+            change the cache directory by setting the PYTHON_EGG_CACHE environment
+            variable to point to an accessible directory.
+            """).lstrip()
+        err = ExtractionError(tmpl.format(**locals()))
         err.manager = self
         err.cache_path = cache_path
         err.original_error = old_exc
@@ -1047,7 +1284,7 @@ def postprocess(self, tempname, filename):
 
         if os.name == 'posix':
             # Make the resource executable
-            mode = ((os.stat(tempname).st_mode) | 0x16D) & 0xFFF # 0555, 07777
+            mode = ((os.stat(tempname).st_mode) | 0o555) & 0o7777
             os.chmod(tempname, mode)
 
     def set_extraction_path(self, path):
@@ -1104,14 +1341,17 @@ def get_default_cache():
     if os.name!='nt':
         return os.path.expanduser('~/.python-eggs')
 
-    app_data = 'Application Data'   # XXX this may be locale-specific!
+    # XXX this may be locale-specific!
+    app_data = 'Application Data'
     app_homes = [
-        (('APPDATA',), None),       # best option, should be locale-safe
+        # best option, should be locale-safe
+        (('APPDATA',), None),
         (('USERPROFILE',), app_data),
         (('HOMEDRIVE','HOMEPATH'), app_data),
         (('HOMEPATH',), app_data),
         (('HOME',), None),
-        (('WINDIR',), app_data),    # 95/98/ME
+        # 95/98/ME
+        (('WINDIR',), app_data),
     ]
 
     for keys, subdir in app_homes:
@@ -1123,7 +1363,7 @@ def get_default_cache():
                 break
         else:
             if subdir:
-                dirname = os.path.join(dirname,subdir)
+                dirname = os.path.join(dirname, subdir)
             return os.path.join(dirname, 'Python-Eggs')
     else:
         raise RuntimeError(
@@ -1139,13 +1379,15 @@ def safe_name(name):
 
 
 def safe_version(version):
-    """Convert an arbitrary string to a standard version string
-
-    Spaces become dots, and all other non-alphanumeric characters become
-    dashes, with runs of multiple dashes condensed to a single dash.
     """
-    version = version.replace(' ','.')
-    return re.sub('[^A-Za-z0-9.]+', '-', version)
+    Convert an arbitrary string to a standard version string
+    """
+    try:
+        # normalize the version
+        return str(packaging.version.Version(version))
+    except packaging.version.InvalidVersion:
+        version = version.replace(' ','.')
+        return re.sub('[^A-Za-z0-9.]+', '-', version)
 
 
 def safe_extra(extra):
@@ -1165,168 +1407,34 @@ def to_filename(name):
     return name.replace('-','_')
 
 
-class MarkerEvaluation(object):
-    values = {
-        'os_name': lambda: os.name,
-        'sys_platform': lambda: sys.platform,
-        'python_full_version': lambda: sys.version.split()[0],
-        'python_version': lambda:'%s.%s' % (sys.version_info[0], sys.version_info[1]),
-        'platform_version': platform.version,
-        'platform_machine': platform.machine,
-        'python_implementation': platform.python_implementation,
-    }
-
-    @classmethod
-    def is_invalid_marker(cls, text):
-        """
-        Validate text as a PEP 426 environment marker; return an exception
-        if invalid or False otherwise.
-        """
-        try:
-            cls.evaluate_marker(text)
-        except SyntaxError:
-            return cls.normalize_exception(sys.exc_info()[1])
-        return False
-
-    @staticmethod
-    def normalize_exception(exc):
-        """
-        Given a SyntaxError from a marker evaluation, normalize the error message:
-         - Remove indications of filename and line number.
-         - Replace platform-specific error messages with standard error messages.
-        """
-        subs = {
-            'unexpected EOF while parsing': 'invalid syntax',
-            'parenthesis is never closed': 'invalid syntax',
-        }
-        exc.filename = None
-        exc.lineno = None
-        exc.msg = subs.get(exc.msg, exc.msg)
-        return exc
-
-    @classmethod
-    def and_test(cls, nodelist):
-        # MUST NOT short-circuit evaluation, or invalid syntax can be skipped!
-        return functools.reduce(operator.and_, [cls.interpret(nodelist[i]) for i in range(1,len(nodelist),2)])
-
-    @classmethod
-    def test(cls, nodelist):
-        # MUST NOT short-circuit evaluation, or invalid syntax can be skipped!
-        return functools.reduce(operator.or_, [cls.interpret(nodelist[i]) for i in range(1,len(nodelist),2)])
-
-    @classmethod
-    def atom(cls, nodelist):
-        t = nodelist[1][0]
-        if t == token.LPAR:
-            if nodelist[2][0] == token.RPAR:
-                raise SyntaxError("Empty parentheses")
-            return cls.interpret(nodelist[2])
-        raise SyntaxError("Language feature not supported in environment markers")
-
-    @classmethod
-    def comparison(cls, nodelist):
-        if len(nodelist)>4:
-            raise SyntaxError("Chained comparison not allowed in environment markers")
-        comp = nodelist[2][1]
-        cop = comp[1]
-        if comp[0] == token.NAME:
-            if len(nodelist[2]) == 3:
-                if cop == 'not':
-                    cop = 'not in'
-                else:
-                    cop = 'is not'
-        try:
-            cop = cls.get_op(cop)
-        except KeyError:
-            raise SyntaxError(repr(cop)+" operator not allowed in environment markers")
-        return cop(cls.evaluate(nodelist[1]), cls.evaluate(nodelist[3]))
-
-    @classmethod
-    def get_op(cls, op):
-        ops = {
-            symbol.test: cls.test,
-            symbol.and_test: cls.and_test,
-            symbol.atom: cls.atom,
-            symbol.comparison: cls.comparison,
-            'not in': lambda x, y: x not in y,
-            'in': lambda x, y: x in y,
-            '==': operator.eq,
-            '!=': operator.ne,
-        }
-        if hasattr(symbol, 'or_test'):
-            ops[symbol.or_test] = cls.test
-        return ops[op]
-
-    @classmethod
-    def evaluate_marker(cls, text, extra=None):
-        """
-        Evaluate a PEP 426 environment marker on CPython 2.4+.
-        Return a boolean indicating the marker result in this environment.
-        Raise SyntaxError if marker is invalid.
-
-        This implementation uses the 'parser' module, which is not implemented on
-        Jython and has been superseded by the 'ast' module in Python 2.6 and
-        later.
-        """
-        return cls.interpret(parser.expr(text).totuple(1)[1])
+def invalid_marker(text):
+    """
+    Validate text as a PEP 508 environment marker; return an exception
+    if invalid or False otherwise.
+    """
+    try:
+        evaluate_marker(text)
+    except SyntaxError as e:
+        e.filename = None
+        e.lineno = None
+        return e
+    return False
 
-    @classmethod
-    def _markerlib_evaluate(cls, text):
-        """
-        Evaluate a PEP 426 environment marker using markerlib.
-        Return a boolean indicating the marker result in this environment.
-        Raise SyntaxError if marker is invalid.
-        """
-        from pip._vendor import _markerlib
-        # markerlib implements Metadata 1.2 (PEP 345) environment markers.
-        # Translate the variables to Metadata 2.0 (PEP 426).
-        env = _markerlib.default_environment()
-        for key in env.keys():
-            new_key = key.replace('.', '_')
-            env[new_key] = env.pop(key)
-        try:
-            result = _markerlib.interpret(text, env)
-        except NameError:
-            e = sys.exc_info()[1]
-            raise SyntaxError(e.args[0])
-        return result
 
-    if 'parser' not in globals():
-        # Fall back to less-complete _markerlib implementation if 'parser' module
-        # is not available.
-        evaluate_marker = _markerlib_evaluate
+def evaluate_marker(text, extra=None):
+    """
+    Evaluate a PEP 508 environment marker.
+    Return a boolean indicating the marker result in this environment.
+    Raise SyntaxError if marker is invalid.
 
-    @classmethod
-    def interpret(cls, nodelist):
-        while len(nodelist)==2: nodelist = nodelist[1]
-        try:
-            op = cls.get_op(nodelist[0])
-        except KeyError:
-            raise SyntaxError("Comparison or logical expression expected")
-        return op(nodelist)
+    This implementation uses the 'pyparsing' module.
+    """
+    try:
+        marker = packaging.markers.Marker(text)
+        return marker.evaluate()
+    except packaging.markers.InvalidMarker as e:
+        raise SyntaxError(e)
 
-    @classmethod
-    def evaluate(cls, nodelist):
-        while len(nodelist)==2: nodelist = nodelist[1]
-        kind = nodelist[0]
-        name = nodelist[1]
-        if kind==token.NAME:
-            try:
-                op = cls.values[name]
-            except KeyError:
-                raise SyntaxError("Unknown name %r" % name)
-            return op()
-        if kind==token.STRING:
-            s = nodelist[1]
-            if s[:1] not in "'\"" or s.startswith('"""') or s.startswith("'''") \
-                    or '\\' in s:
-                raise SyntaxError(
-                    "Only plain strings allowed in environment markers")
-            return s[1:-1]
-        raise SyntaxError("Language feature not supported in environment markers")
-
-invalid_marker = MarkerEvaluation.is_invalid_marker
-evaluate_marker = MarkerEvaluation.evaluate_marker
 
 class NullProvider:
     """Try to implement resources and metadata for arbitrary PEP 302 loaders"""
@@ -1343,7 +1451,7 @@ def get_resource_filename(self, manager, resource_name):
         return self._fn(self.module_path, resource_name)
 
     def get_resource_stream(self, manager, resource_name):
-        return BytesIO(self.get_resource_string(manager, resource_name))
+        return io.BytesIO(self.get_resource_string(manager, resource_name))
 
     def get_resource_string(self, manager, resource_name):
         return self._get(self._fn(self.module_path, resource_name))
@@ -1352,52 +1460,54 @@ def has_resource(self, resource_name):
         return self._has(self._fn(self.module_path, resource_name))
 
     def has_metadata(self, name):
-        return self.egg_info and self._has(self._fn(self.egg_info,name))
+        return self.egg_info and self._has(self._fn(self.egg_info, name))
 
     if sys.version_info <= (3,):
         def get_metadata(self, name):
             if not self.egg_info:
                 return ""
-            return self._get(self._fn(self.egg_info,name))
+            return self._get(self._fn(self.egg_info, name))
     else:
         def get_metadata(self, name):
             if not self.egg_info:
                 return ""
-            return self._get(self._fn(self.egg_info,name)).decode("utf-8")
+            return self._get(self._fn(self.egg_info, name)).decode("utf-8")
 
     def get_metadata_lines(self, name):
         return yield_lines(self.get_metadata(name))
 
-    def resource_isdir(self,resource_name):
+    def resource_isdir(self, resource_name):
         return self._isdir(self._fn(self.module_path, resource_name))
 
-    def metadata_isdir(self,name):
-        return self.egg_info and self._isdir(self._fn(self.egg_info,name))
+    def metadata_isdir(self, name):
+        return self.egg_info and self._isdir(self._fn(self.egg_info, name))
 
-    def resource_listdir(self,resource_name):
-        return self._listdir(self._fn(self.module_path,resource_name))
+    def resource_listdir(self, resource_name):
+        return self._listdir(self._fn(self.module_path, resource_name))
 
-    def metadata_listdir(self,name):
+    def metadata_listdir(self, name):
         if self.egg_info:
-            return self._listdir(self._fn(self.egg_info,name))
+            return self._listdir(self._fn(self.egg_info, name))
         return []
 
-    def run_script(self,script_name,namespace):
+    def run_script(self, script_name, namespace):
         script = 'scripts/'+script_name
         if not self.has_metadata(script):
             raise ResolutionError("No script named %r" % script_name)
-        script_text = self.get_metadata(script).replace('\r\n','\n')
-        script_text = script_text.replace('\r','\n')
-        script_filename = self._fn(self.egg_info,script)
+        script_text = self.get_metadata(script).replace('\r\n', '\n')
+        script_text = script_text.replace('\r', '\n')
+        script_filename = self._fn(self.egg_info, script)
         namespace['__file__'] = script_filename
         if os.path.exists(script_filename):
-            execfile(script_filename, namespace, namespace)
+            source = open(script_filename).read()
+            code = compile(source, script_filename, 'exec')
+            exec(code, namespace, namespace)
         else:
             from linecache import cache
             cache[script_filename] = (
                 len(script_text), 0, script_text.split('\n'), script_filename
             )
-            script_code = compile(script_text,script_filename,'exec')
+            script_code = compile(script_text, script_filename,'exec')
             exec(script_code, namespace, namespace)
 
     def _has(self, path):
@@ -1433,8 +1543,8 @@ def _get(self, path):
 class EggProvider(NullProvider):
     """Provider based on a virtual filesystem"""
 
-    def __init__(self,module):
-        NullProvider.__init__(self,module)
+    def __init__(self, module):
+        NullProvider.__init__(self, module)
         self._setup_prefix()
 
     def _setup_prefix(self):
@@ -1443,7 +1553,7 @@ def _setup_prefix(self):
         path = self.module_path
         old = None
         while path!=old:
-            if path.lower().endswith('.egg'):
+            if _is_unpacked_egg(path):
                 self.egg_name = os.path.basename(path)
                 self.egg_info = os.path.join(path, 'EGG-INFO')
                 self.egg_root = path
@@ -1457,34 +1567,34 @@ class DefaultProvider(EggProvider):
     def _has(self, path):
         return os.path.exists(path)
 
-    def _isdir(self,path):
+    def _isdir(self, path):
         return os.path.isdir(path)
 
-    def _listdir(self,path):
+    def _listdir(self, path):
         return os.listdir(path)
 
     def get_resource_stream(self, manager, resource_name):
         return open(self._fn(self.module_path, resource_name), 'rb')
 
     def _get(self, path):
-        stream = open(path, 'rb')
-        try:
+        with open(path, 'rb') as stream:
             return stream.read()
-        finally:
-            stream.close()
 
-register_loader_type(type(None), DefaultProvider)
+    @classmethod
+    def _register(cls):
+        loader_cls = getattr(importlib_machinery, 'SourceFileLoader',
+            type(None))
+        register_loader_type(loader_cls, cls)
 
-if importlib_bootstrap is not None:
-    register_loader_type(importlib_bootstrap.SourceFileLoader, DefaultProvider)
+DefaultProvider._register()
 
 
 class EmptyProvider(NullProvider):
     """Provider that returns nothing for all requests"""
 
-    _isdir = _has = lambda self,path: False
-    _get = lambda self,path: ''
-    _listdir = lambda self,path: []
+    _isdir = _has = lambda self, path: False
+    _get = lambda self, path: ''
+    _listdir = lambda self, path: []
     module_path = None
 
     def __init__(self):
@@ -1493,47 +1603,81 @@ def __init__(self):
 empty_provider = EmptyProvider()
 
 
-def build_zipmanifest(path):
+class ZipManifests(dict):
     """
-    This builds a similar dictionary to the zipimport directory
-    caches.  However instead of tuples, ZipInfo objects are stored.
-
-    The translation of the tuple is as follows:
-      * [0] - zipinfo.filename on stock pythons this needs "/" --> os.sep
-              on pypy it is the same (one reason why distribute did work
-              in some cases on pypy and win32).
-      * [1] - zipinfo.compress_type
-      * [2] - zipinfo.compress_size
-      * [3] - zipinfo.file_size
-      * [4] - len(utf-8 encoding of filename) if zipinfo & 0x800
-              len(ascii encoding of filename) otherwise
-      * [5] - (zipinfo.date_time[0] - 1980) << 9 |
-               zipinfo.date_time[1] << 5 | zipinfo.date_time[2]
-      * [6] - (zipinfo.date_time[3] - 1980) << 11 |
-               zipinfo.date_time[4] << 5 | (zipinfo.date_time[5] // 2)
-      * [7] - zipinfo.CRC
+    zip manifest builder
     """
-    zipinfo = dict()
-    zfile = zipfile.ZipFile(path)
-    #Got ZipFile has not __exit__ on python 3.1
-    try:
-        for zitem in zfile.namelist():
-            zpath = zitem.replace('/', os.sep)
-            zipinfo[zpath] = zfile.getinfo(zitem)
-            assert zipinfo[zpath] is not None
-    finally:
-        zfile.close()
-    return zipinfo
+
+    @classmethod
+    def build(cls, path):
+        """
+        Build a dictionary similar to the zipimport directory
+        caches, except instead of tuples, store ZipInfo objects.
+
+        Use a platform-specific path separator (os.sep) for the path keys
+        for compatibility with pypy on Windows.
+        """
+        with ContextualZipFile(path) as zfile:
+            items = (
+                (
+                    name.replace('/', os.sep),
+                    zfile.getinfo(name),
+                )
+                for name in zfile.namelist()
+            )
+            return dict(items)
+
+    load = build
+
+
+class MemoizedZipManifests(ZipManifests):
+    """
+    Memoized zipfile manifests.
+    """
+    manifest_mod = collections.namedtuple('manifest_mod', 'manifest mtime')
+
+    def load(self, path):
+        """
+        Load a manifest at path or return a suitable manifest already loaded.
+        """
+        path = os.path.normpath(path)
+        mtime = os.stat(path).st_mtime
+
+        if path not in self or self[path].mtime != mtime:
+            manifest = self.build(path)
+            self[path] = self.manifest_mod(manifest, mtime)
+
+        return self[path].manifest
+
+
+class ContextualZipFile(zipfile.ZipFile):
+    """
+    Supplement ZipFile class to support context manager for Python 2.6
+    """
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, type, value, traceback):
+        self.close()
+
+    def __new__(cls, *args, **kwargs):
+        """
+        Construct a ZipFile or ContextualZipFile as appropriate
+        """
+        if hasattr(zipfile.ZipFile, '__exit__'):
+            return zipfile.ZipFile(*args, **kwargs)
+        return super(ContextualZipFile, cls).__new__(cls)
 
 
 class ZipProvider(EggProvider):
     """Resource support for zips and eggs"""
 
     eagers = None
+    _zip_manifests = MemoizedZipManifests()
 
     def __init__(self, module):
-        EggProvider.__init__(self,module)
-        self.zipinfo = build_zipmanifest(self.loader.archive)
+        EggProvider.__init__(self, module)
         self.zip_pre = self.loader.archive+os.sep
 
     def _zipinfo_name(self, fspath):
@@ -1542,18 +1686,23 @@ def _zipinfo_name(self, fspath):
         if fspath.startswith(self.zip_pre):
             return fspath[len(self.zip_pre):]
         raise AssertionError(
-            "%s is not a subpath of %s" % (fspath,self.zip_pre)
+            "%s is not a subpath of %s" % (fspath, self.zip_pre)
         )
 
-    def _parts(self,zip_path):
-        # Convert a zipfile subpath into an egg-relative path part list
-        fspath = self.zip_pre+zip_path  # pseudo-fs path
+    def _parts(self, zip_path):
+        # Convert a zipfile subpath into an egg-relative path part list.
+        # pseudo-fs path
+        fspath = self.zip_pre+zip_path
         if fspath.startswith(self.egg_root+os.sep):
             return fspath[len(self.egg_root)+1:].split(os.sep)
         raise AssertionError(
-            "%s is not a subpath of %s" % (fspath,self.egg_root)
+            "%s is not a subpath of %s" % (fspath, self.egg_root)
         )
 
+    @property
+    def zipinfo(self):
+        return self._zip_manifests.load(self.loader.archive)
+
     def get_resource_filename(self, manager, resource_name):
         if not self.egg_name:
             raise NotImplementedError(
@@ -1570,8 +1719,9 @@ def get_resource_filename(self, manager, resource_name):
     @staticmethod
     def _get_date_and_size(zip_stat):
         size = zip_stat.file_size
-        date_time = zip_stat.date_time + (0, 0, -1)  # ymdhms+wday, yday, dst
-        #1980 offset already done
+        # ymdhms+wday, yday, dst
+        date_time = zip_stat.date_time + (0, 0, -1)
+        # 1980 offset already done
         timestamp = time.mktime(date_time)
         return timestamp, size
 
@@ -1582,7 +1732,8 @@ def _extract_resource(self, manager, zip_path):
                 last = self._extract_resource(
                     manager, os.path.join(zip_path, name)
                 )
-            return os.path.dirname(last)  # return the extracted directory name
+            # return the extracted directory name
+            return os.path.dirname(last)
 
         timestamp, size = self._get_date_and_size(self.zipinfo[zip_path])
 
@@ -1601,7 +1752,7 @@ def _extract_resource(self, manager, zip_path):
             outf, tmpnam = _mkstemp(".$extract", dir=os.path.dirname(real_path))
             os.write(outf, self.loader.get_data(zip_path))
             os.close(outf)
-            utime(tmpnam, (timestamp,timestamp))
+            utime(tmpnam, (timestamp, timestamp))
             manager.postprocess(tmpnam, real_path)
 
             try:
@@ -1613,14 +1764,16 @@ def _extract_resource(self, manager, zip_path):
                         # the file became current since it was checked above,
                         #  so proceed.
                         return real_path
-                    elif os.name=='nt':     # Windows, del old file and retry
+                    # Windows, del old file and retry
+                    elif os.name=='nt':
                         unlink(real_path)
                         rename(tmpnam, real_path)
                         return real_path
                 raise
 
         except os.error:
-            manager.extraction_error()  # report a user-friendly error
+            # report a user-friendly error
+            manager.extraction_error()
 
         return real_path
 
@@ -1636,9 +1789,8 @@ def _is_current(self, file_path, zip_path):
             return False
         # check that the contents match
         zip_contents = self.loader.get_data(zip_path)
-        f = open(file_path, 'rb')
-        file_contents = f.read()
-        f.close()
+        with open(file_path, 'rb') as f:
+            file_contents = f.read()
         return zip_contents == file_contents
 
     def _get_eager_resources(self):
@@ -1671,17 +1823,17 @@ def _has(self, fspath):
         zip_path = self._zipinfo_name(fspath)
         return zip_path in self.zipinfo or zip_path in self._index()
 
-    def _isdir(self,fspath):
+    def _isdir(self, fspath):
         return self._zipinfo_name(fspath) in self._index()
 
-    def _listdir(self,fspath):
+    def _listdir(self, fspath):
         return list(self._index().get(self._zipinfo_name(fspath), ()))
 
-    def _eager_to_zip(self,resource_name):
-        return self._zipinfo_name(self._fn(self.egg_root,resource_name))
+    def _eager_to_zip(self, resource_name):
+        return self._zipinfo_name(self._fn(self.egg_root, resource_name))
 
-    def _resource_to_zip(self,resource_name):
-        return self._zipinfo_name(self._fn(self.module_path,resource_name))
+    def _resource_to_zip(self, resource_name):
+        return self._zipinfo_name(self._fn(self.module_path, resource_name))
 
 register_loader_type(zipimport.zipimporter, ZipProvider)
 
@@ -1698,21 +1850,26 @@ class FileMetadata(EmptyProvider):
     the provided location.
     """
 
-    def __init__(self,path):
+    def __init__(self, path):
         self.path = path
 
-    def has_metadata(self,name):
-        return name=='PKG-INFO'
+    def has_metadata(self, name):
+        return name=='PKG-INFO' and os.path.isfile(self.path)
 
-    def get_metadata(self,name):
+    def get_metadata(self, name):
         if name=='PKG-INFO':
-            f = open(self.path,'rU')
-            metadata = f.read()
-            f.close()
+            with io.open(self.path, encoding='utf-8') as f:
+                try:
+                    metadata = f.read()
+                except UnicodeDecodeError as exc:
+                    # add path context to error message
+                    tmpl = " in {self.path}"
+                    exc.reason += tmpl.format(self=self)
+                    raise
             return metadata
         raise KeyError("No metadata except PKG-INFO is available")
 
-    def get_metadata_lines(self,name):
+    def get_metadata_lines(self, name):
         return yield_lines(self.get_metadata(name))
 
 
@@ -1727,7 +1884,7 @@ class PathMetadata(DefaultProvider):
         base_dir = os.path.dirname(egg_info)
         metadata = PathMetadata(base_dir, egg_info)
         dist_name = os.path.splitext(os.path.basename(egg_info))[0]
-        dist = Distribution(basedir,project_name=dist_name,metadata=metadata)
+        dist = Distribution(basedir, project_name=dist_name, metadata=metadata)
 
         # Unpacked egg directories:
 
@@ -1747,7 +1904,6 @@ class EggMetadata(ZipProvider):
     def __init__(self, importer):
         """Create a metadata provider from a zipimporter"""
 
-        self.zipinfo = build_zipmanifest(importer.archive)
         self.zip_pre = importer.archive+os.sep
         self.loader = importer
         if importer.prefix:
@@ -1786,9 +1942,10 @@ def find_eggs_in_zip(importer, path_item, only=False):
     if metadata.has_metadata('PKG-INFO'):
         yield Distribution.from_filename(path_item, metadata=metadata)
     if only:
-        return  # don't yield nested distros
+        # don't yield nested distros
+        return
     for subitem in metadata.resource_listdir('/'):
-        if subitem.endswith('.egg'):
+        if _is_unpacked_egg(subitem):
             subpath = os.path.join(path_item, subitem)
             for dist in find_eggs_in_zip(zipimport.zipimporter(subpath), subpath):
                 yield dist
@@ -1797,15 +1954,14 @@ def find_eggs_in_zip(importer, path_item, only=False):
 
 def find_nothing(importer, path_item, only=False):
     return ()
-register_finder(object,find_nothing)
+register_finder(object, find_nothing)
 
 def find_on_path(importer, path_item, only=False):
     """Yield distributions accessible on a sys.path directory"""
     path_item = _normalize_cached(path_item)
 
     if os.path.isdir(path_item) and os.access(path_item, os.R_OK):
-        if path_item.lower().endswith('.egg'):
-            # unpacked egg
+        if _is_unpacked_egg(path_item):
             yield Distribution.from_filename(
                 path_item, metadata=PathMetadata(
                     path_item, os.path.join(path_item,'EGG-INFO')
@@ -1823,26 +1979,27 @@ def find_on_path(importer, path_item, only=False):
                     else:
                         metadata = FileMetadata(fullpath)
                     yield Distribution.from_location(
-                        path_item,entry,metadata,precedence=DEVELOP_DIST
+                        path_item, entry, metadata, precedence=DEVELOP_DIST
                     )
-                elif not only and lower.endswith('.egg'):
-                    for dist in find_distributions(os.path.join(path_item, entry)):
+                elif not only and _is_unpacked_egg(entry):
+                    dists = find_distributions(os.path.join(path_item, entry))
+                    for dist in dists:
                         yield dist
                 elif not only and lower.endswith('.egg-link'):
-                    entry_file = open(os.path.join(path_item, entry))
-                    try:
+                    with open(os.path.join(path_item, entry)) as entry_file:
                         entry_lines = entry_file.readlines()
-                    finally:
-                        entry_file.close()
                     for line in entry_lines:
-                        if not line.strip(): continue
-                        for item in find_distributions(os.path.join(path_item,line.rstrip())):
+                        if not line.strip():
+                            continue
+                        path = os.path.join(path_item, line.rstrip())
+                        dists = find_distributions(path)
+                        for item in dists:
                             yield item
                         break
-register_finder(pkgutil.ImpImporter,find_on_path)
+register_finder(pkgutil.ImpImporter, find_on_path)
 
-if importlib_bootstrap is not None:
-    register_finder(importlib_bootstrap.FileFinder, find_on_path)
+if hasattr(importlib_machinery, 'FileFinder'):
+    register_finder(importlib_machinery.FileFinder, find_on_path)
 
 _declare_state('dict', _namespace_handlers={})
 _declare_state('dict', _namespace_packages={})
@@ -1854,7 +2011,7 @@ def register_namespace_handler(importer_type, namespace_handler):
     `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item
     handler), and `namespace_handler` is a callable like this::
 
-        def namespace_handler(importer,path_entry,moduleName,module):
+        def namespace_handler(importer, path_entry, moduleName, module):
             # return a path_entry to use for child packages
 
     Namespace handlers are only called if the importer object has already
@@ -1876,7 +2033,7 @@ def _handle_ns(packageName, path_item):
         return None
     module = sys.modules.get(packageName)
     if module is None:
-        module = sys.modules[packageName] = imp.new_module(packageName)
+        module = sys.modules[packageName] = types.ModuleType(packageName)
         module.__path__ = []
         _set_parent_ns(packageName)
     elif not hasattr(module,'__path__'):
@@ -1887,15 +2044,33 @@ def _handle_ns(packageName, path_item):
         path = module.__path__
         path.append(subpath)
         loader.load_module(packageName)
-        for path_item in path:
-            if path_item not in module.__path__:
-                module.__path__.append(path_item)
+        _rebuild_mod_path(path, packageName, module)
     return subpath
 
+
+def _rebuild_mod_path(orig_path, package_name, module):
+    """
+    Rebuild module.__path__ ensuring that all entries are ordered
+    corresponding to their sys.path order
+    """
+    sys_path = [_normalize_cached(p) for p in sys.path]
+    def position_in_sys_path(path):
+        """
+        Return the ordinal of the path based on its position in sys.path
+        """
+        path_parts = path.split(os.sep)
+        module_parts = package_name.count('.') + 1
+        parts = path_parts[:-module_parts]
+        return sys_path.index(_normalize_cached(os.sep.join(parts)))
+
+    orig_path.sort(key=position_in_sys_path)
+    module.__path__[:] = [_normalize_cached(p) for p in orig_path]
+
+
 def declare_namespace(packageName):
     """Declare that package 'packageName' is a namespace package"""
 
-    imp.acquire_lock()
+    _imp.acquire_lock()
     try:
         if packageName in _namespace_packages:
             return
@@ -1922,17 +2097,18 @@ def declare_namespace(packageName):
             _handle_ns(packageName, path_item)
 
     finally:
-        imp.release_lock()
+        _imp.release_lock()
 
 def fixup_namespace_packages(path_item, parent=None):
     """Ensure that previously-declared namespace packages include path_item"""
-    imp.acquire_lock()
+    _imp.acquire_lock()
     try:
         for package in _namespace_packages.get(parent,()):
             subpath = _handle_ns(package, path_item)
-            if subpath: fixup_namespace_packages(subpath,package)
+            if subpath:
+                fixup_namespace_packages(subpath, package)
     finally:
-        imp.release_lock()
+        _imp.release_lock()
 
 def file_ns_handler(importer, path_item, packageName, module):
     """Compute an ns-package subpath for a filesystem or zipfile importer"""
@@ -1946,30 +2122,38 @@ def file_ns_handler(importer, path_item, packageName, module):
         # Only return the path if it's not already there
         return subpath
 
-register_namespace_handler(pkgutil.ImpImporter,file_ns_handler)
-register_namespace_handler(zipimport.zipimporter,file_ns_handler)
+register_namespace_handler(pkgutil.ImpImporter, file_ns_handler)
+register_namespace_handler(zipimport.zipimporter, file_ns_handler)
 
-if importlib_bootstrap is not None:
-    register_namespace_handler(importlib_bootstrap.FileFinder, file_ns_handler)
+if hasattr(importlib_machinery, 'FileFinder'):
+    register_namespace_handler(importlib_machinery.FileFinder, file_ns_handler)
 
 
 def null_ns_handler(importer, path_item, packageName, module):
     return None
 
-register_namespace_handler(object,null_ns_handler)
+register_namespace_handler(object, null_ns_handler)
 
 
 def normalize_path(filename):
     """Normalize a file/dir name for comparison purposes"""
     return os.path.normcase(os.path.realpath(filename))
 
-def _normalize_cached(filename,_cache={}):
+def _normalize_cached(filename, _cache={}):
     try:
         return _cache[filename]
     except KeyError:
         _cache[filename] = result = normalize_path(filename)
         return result
 
+def _is_unpacked_egg(path):
+    """
+    Determine if given path appears to be an unpacked egg.
+    """
+    return (
+        path.lower().endswith('.egg')
+    )
+
 def _set_parent_ns(packageName):
     parts = packageName.split('.')
     name = parts.pop()
@@ -1979,87 +2163,33 @@ def _set_parent_ns(packageName):
 
 
 def yield_lines(strs):
-    """Yield non-empty/non-comment lines of a ``basestring`` or sequence"""
-    if isinstance(strs,basestring):
+    """Yield non-empty/non-comment lines of a string or sequence"""
+    if isinstance(strs, six.string_types):
         for s in strs.splitlines():
             s = s.strip()
-            if s and not s.startswith('#'):     # skip blank lines/comments
+            # skip blank lines/comments
+            if s and not s.startswith('#'):
                 yield s
     else:
         for ss in strs:
             for s in yield_lines(ss):
                 yield s
 
-LINE_END = re.compile(r"\s*(#.*)?$").match         # whitespace and comment
-CONTINUE = re.compile(r"\s*\\\s*(#.*)?$").match    # line continuation
-DISTRO = re.compile(r"\s*((\w|[-.])+)").match    # Distribution or extra
-VERSION = re.compile(r"\s*(<=?|>=?|==|!=)\s*((\w|[-.])+)").match  # ver. info
-COMMA = re.compile(r"\s*,").match               # comma between items
-OBRACKET = re.compile(r"\s*\[").match
-CBRACKET = re.compile(r"\s*\]").match
 MODULE = re.compile(r"\w+(\.\w+)*$").match
 EGG_NAME = re.compile(
-    r"(?P[^-]+)"
-    r"( -(?P[^-]+) (-py(?P[^-]+) (-(?P.+))? )? )?",
-    re.VERBOSE | re.IGNORECASE
+    r"""
+    (?P[^-]+) (
+        -(?P[^-]+) (
+            -py(?P[^-]+) (
+                -(?P.+)
+            )?
+        )?
+    )?
+    """,
+    re.VERBOSE | re.IGNORECASE,
 ).match
 
-component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE)
-replace = {'pre':'c', 'preview':'c','-':'final-','rc':'c','dev':'@'}.get
 
-def _parse_version_parts(s):
-    for part in component_re.split(s):
-        part = replace(part,part)
-        if not part or part=='.':
-            continue
-        if part[:1] in '0123456789':
-            yield part.zfill(8)    # pad for numeric comparison
-        else:
-            yield '*'+part
-
-    yield '*final'  # ensure that alpha/beta/candidate are before final
-
-def parse_version(s):
-    """Convert a version string to a chronologically-sortable key
-
-    This is a rough cross between distutils' StrictVersion and LooseVersion;
-    if you give it versions that would work with StrictVersion, then it behaves
-    the same; otherwise it acts like a slightly-smarter LooseVersion. It is
-    *possible* to create pathological version coding schemes that will fool
-    this parser, but they should be very rare in practice.
-
-    The returned value will be a tuple of strings.  Numeric portions of the
-    version are padded to 8 digits so they will compare numerically, but
-    without relying on how numbers compare relative to strings.  Dots are
-    dropped, but dashes are retained.  Trailing zeros between alpha segments
-    or dashes are suppressed, so that e.g. "2.4.0" is considered the same as
-    "2.4". Alphanumeric parts are lower-cased.
-
-    The algorithm assumes that strings like "-" and any alpha string that
-    alphabetically follows "final"  represents a "patch level".  So, "2.4-1"
-    is assumed to be a branch or patch of "2.4", and therefore "2.4.1" is
-    considered newer than "2.4-1", which in turn is newer than "2.4".
-
-    Strings like "a", "b", "c", "alpha", "beta", "candidate" and so on (that
-    come before "final" alphabetically) are assumed to be pre-release versions,
-    so that the version "2.4" is considered newer than "2.4a1".
-
-    Finally, to handle miscellaneous cases, the strings "pre", "preview", and
-    "rc" are treated as if they were "c", i.e. as though they were release
-    candidates, and therefore are not as new as a version string that does not
-    contain them, and "dev" is replaced with an '@' so that it sorts lower than
-    than any other pre-release tag.
-    """
-    parts = []
-    for part in _parse_version_parts(s.lower()):
-        if part.startswith('*'):
-            if part<'*final':   # remove '-' before a prerelease tag
-                while parts and parts[-1]=='*final-': parts.pop()
-            # remove trailing zeros from each series of numeric parts
-            while parts and parts[-1]=='00000000':
-                parts.pop()
-        parts.append(part)
-    return tuple(parts)
 class EntryPoint(object):
     """Object representing an advertised importable object"""
 
@@ -2083,21 +2213,46 @@ def __str__(self):
     def __repr__(self):
         return "EntryPoint.parse(%r)" % str(self)
 
-    def load(self, require=True, env=None, installer=None):
-        if require: self.require(env, installer)
-        entry = __import__(self.module_name, globals(),globals(), ['__name__'])
-        for attr in self.attrs:
-            try:
-                entry = getattr(entry,attr)
-            except AttributeError:
-                raise ImportError("%r has no %r attribute" % (entry,attr))
-        return entry
+    def load(self, require=True, *args, **kwargs):
+        """
+        Require packages for this EntryPoint, then resolve it.
+        """
+        if not require or args or kwargs:
+            warnings.warn(
+                "Parameters to load are deprecated.  Call .resolve and "
+                ".require separately.",
+                DeprecationWarning,
+                stacklevel=2,
+            )
+        if require:
+            self.require(*args, **kwargs)
+        return self.resolve()
+
+    def resolve(self):
+        """
+        Resolve the entry point from its module and attrs.
+        """
+        module = __import__(self.module_name, fromlist=['__name__'], level=0)
+        try:
+            return functools.reduce(getattr, self.attrs, module)
+        except AttributeError as exc:
+            raise ImportError(str(exc))
 
     def require(self, env=None, installer=None):
         if self.extras and not self.dist:
             raise UnknownExtra("Can't require() without a distribution", self)
-        list(map(working_set.add,
-            working_set.resolve(self.dist.requires(self.extras),env,installer)))
+        reqs = self.dist.requires(self.extras)
+        items = working_set.resolve(reqs, env, installer)
+        list(map(working_set.add, items))
+
+    pattern = re.compile(
+        r'\s*'
+        r'(?P.+?)\s*'
+        r'=\s*'
+        r'(?P[\w.]+)\s*'
+        r'(:\s*(?P[\w.]+))?\s*'
+        r'(?P\[.*\])?\s*$'
+    )
 
     @classmethod
     def parse(cls, src, dist=None):
@@ -2105,31 +2260,28 @@ def parse(cls, src, dist=None):
 
         Entry point syntax follows the form::
 
-            name = some.module:some.attr [extra1,extra2]
+            name = some.module:some.attr [extra1, extra2]
 
         The entry name and module name are required, but the ``:attrs`` and
         ``[extras]`` parts are optional
         """
-        try:
-            attrs = extras = ()
-            name,value = src.split('=',1)
-            if '[' in value:
-                value,extras = value.split('[',1)
-                req = Requirement.parse("x["+extras)
-                if req.specs: raise ValueError
-                extras = req.extras
-            if ':' in value:
-                value,attrs = value.split(':',1)
-                if not MODULE(attrs.rstrip()):
-                    raise ValueError
-                attrs = attrs.rstrip().split('.')
-        except ValueError:
-            raise ValueError(
-                "EntryPoint must be in 'name=module:attrs [extras]' format",
-                src
-            )
-        else:
-            return cls(name.strip(), value.strip(), attrs, extras, dist)
+        m = cls.pattern.match(src)
+        if not m:
+            msg = "EntryPoint must be in 'name=module:attrs [extras]' format"
+            raise ValueError(msg, src)
+        res = m.groupdict()
+        extras = cls._parse_extras(res['extras'])
+        attrs = res['attr'].split('.') if res['attr'] else ()
+        return cls(res['name'], res['module'], attrs, extras, dist)
+
+    @classmethod
+    def _parse_extras(cls, extras_spec):
+        if not extras_spec:
+            return ()
+        req = Requirement.parse('x' + extras_spec)
+        if req.specs:
+            raise ValueError()
+        return req.extras
 
     @classmethod
     def parse_group(cls, group, lines, dist=None):
@@ -2147,7 +2299,7 @@ def parse_group(cls, group, lines, dist=None):
     @classmethod
     def parse_map(cls, data, dist=None):
         """Parse a map of entry point groups"""
-        if isinstance(data,dict):
+        if isinstance(data, dict):
             data = data.items()
         else:
             data = split_sections(data)
@@ -2167,12 +2319,24 @@ def parse_map(cls, data, dist=None):
 def _remove_md5_fragment(location):
     if not location:
         return ''
-    parsed = urlparse(location)
+    parsed = urllib.parse.urlparse(location)
     if parsed[-1].startswith('md5='):
-        return urlunparse(parsed[:-1] + ('',))
+        return urllib.parse.urlunparse(parsed[:-1] + ('',))
     return location
 
 
+def _version_from_file(lines):
+    """
+    Given an iterable of lines from a Metadata file, return
+    the value of the Version field, if present, or None otherwise.
+    """
+    is_version_line = lambda line: line.lower().startswith('version:')
+    version_lines = filter(is_version_line, lines)
+    line = next(iter(version_lines), '')
+    _, _, value = line.partition(':')
+    return safe_version(value.strip()) or None
+
+
 class Distribution(object):
     """Wrap an actual or potential sys.path entry w/metadata"""
     PKG_INFO = 'PKG-INFO'
@@ -2190,46 +2354,57 @@ def __init__(self, location=None, metadata=None, project_name=None,
         self._provider = metadata or empty_provider
 
     @classmethod
-    def from_location(cls,location,basename,metadata=None,**kw):
+    def from_location(cls, location, basename, metadata=None, **kw):
         project_name, version, py_version, platform = [None]*4
         basename, ext = os.path.splitext(basename)
         if ext.lower() in _distributionImpl:
-            # .dist-info gets much metadata differently
+            cls = _distributionImpl[ext.lower()]
+
             match = EGG_NAME(basename)
             if match:
                 project_name, version, py_version, platform = match.group(
-                    'name','ver','pyver','plat'
+                    'name', 'ver', 'pyver', 'plat'
                 )
-            cls = _distributionImpl[ext.lower()]
         return cls(
             location, metadata, project_name=project_name, version=version,
             py_version=py_version, platform=platform, **kw
-        )
+        )._reload_version()
 
-    hashcmp = property(
-        lambda self: (
-            getattr(self,'parsed_version',()),
+    def _reload_version(self):
+        return self
+
+    @property
+    def hashcmp(self):
+        return (
+            self.parsed_version,
             self.precedence,
             self.key,
             _remove_md5_fragment(self.location),
-            self.py_version,
-            self.platform
+            self.py_version or '',
+            self.platform or '',
         )
-    )
-    def __hash__(self): return hash(self.hashcmp)
+
+    def __hash__(self):
+        return hash(self.hashcmp)
+
     def __lt__(self, other):
         return self.hashcmp < other.hashcmp
+
     def __le__(self, other):
         return self.hashcmp <= other.hashcmp
+
     def __gt__(self, other):
         return self.hashcmp > other.hashcmp
+
     def __ge__(self, other):
         return self.hashcmp >= other.hashcmp
+
     def __eq__(self, other):
         if not isinstance(other, self.__class__):
             # It's not a Distribution, so they are not equal
             return False
         return self.hashcmp == other.hashcmp
+
     def __ne__(self, other):
         return not self == other
 
@@ -2247,25 +2422,47 @@ def key(self):
 
     @property
     def parsed_version(self):
-        try:
-            return self._parsed_version
-        except AttributeError:
-            self._parsed_version = pv = parse_version(self.version)
-            return pv
+        if not hasattr(self, "_parsed_version"):
+            self._parsed_version = parse_version(self.version)
+
+        return self._parsed_version
+
+    def _warn_legacy_version(self):
+        LV = packaging.version.LegacyVersion
+        is_legacy = isinstance(self._parsed_version, LV)
+        if not is_legacy:
+            return
+
+        # While an empty version is technically a legacy version and
+        # is not a valid PEP 440 version, it's also unlikely to
+        # actually come from someone and instead it is more likely that
+        # it comes from setuptools attempting to parse a filename and
+        # including it in the list. So for that we'll gate this warning
+        # on if the version is anything at all or not.
+        if not self.version:
+            return
+
+        tmpl = textwrap.dedent("""
+            '{project_name} ({version})' is being parsed as a legacy,
+            non PEP 440,
+            version. You may find odd behavior and sort order.
+            In particular it will be sorted as less than 0.0. It
+            is recommended to migrate to PEP 440 compatible
+            versions.
+            """).strip().replace('\n', ' ')
+
+        warnings.warn(tmpl.format(**vars(self)), PEP440Warning)
 
     @property
     def version(self):
         try:
             return self._version
         except AttributeError:
-            for line in self._get_metadata(self.PKG_INFO):
-                if line.lower().startswith('version:'):
-                    self._version = safe_version(line.split(':',1)[1].strip())
-                    return self._version
-            else:
-                raise ValueError(
-                    "Missing 'Version:' header and/or %s file" % self.PKG_INFO, self
-                )
+            version = _version_from_file(self._get_metadata(self.PKG_INFO))
+            if version is None:
+                tmpl = "Missing 'Version:' header and/or %s file"
+                raise ValueError(tmpl % self.PKG_INFO, self)
+            return version
 
     @property
     def _dep_map(self):
@@ -2274,23 +2471,24 @@ def _dep_map(self):
         except AttributeError:
             dm = self.__dep_map = {None: []}
             for name in 'requires.txt', 'depends.txt':
-                for extra,reqs in split_sections(self._get_metadata(name)):
+                for extra, reqs in split_sections(self._get_metadata(name)):
                     if extra:
                         if ':' in extra:
-                            extra, marker = extra.split(':',1)
+                            extra, marker = extra.split(':', 1)
                             if invalid_marker(marker):
-                                reqs=[] # XXX warn
+                                # XXX warn
+                                reqs=[]
                             elif not evaluate_marker(marker):
                                 reqs=[]
                         extra = safe_extra(extra) or None
                     dm.setdefault(extra,[]).extend(parse_requirements(reqs))
             return dm
 
-    def requires(self,extras=()):
+    def requires(self, extras=()):
         """List of Requirements needed for this distro if `extras` are used"""
         dm = self._dep_map
         deps = []
-        deps.extend(dm.get(None,()))
+        deps.extend(dm.get(None, ()))
         for ext in extras:
             try:
                 deps.extend(dm[safe_extra(ext)])
@@ -2300,15 +2498,16 @@ def requires(self,extras=()):
                 )
         return deps
 
-    def _get_metadata(self,name):
+    def _get_metadata(self, name):
         if self.has_metadata(name):
             for line in self.get_metadata_lines(name):
                 yield line
 
-    def activate(self,path=None):
+    def activate(self, path=None):
         """Ensure distribution is importable on `path` (default=sys.path)"""
-        if path is None: path = sys.path
-        self.insert_on(path)
+        if path is None:
+            path = sys.path
+        self.insert_on(path, replace=True)
         if path is sys.path:
             fixup_namespace_packages(self.location)
             for pkg in self._get_metadata('namespace_packages.txt'):
@@ -2323,29 +2522,31 @@ def egg_name(self):
         )
 
         if self.platform:
-            filename += '-'+self.platform
+            filename += '-' + self.platform
         return filename
 
     def __repr__(self):
         if self.location:
-            return "%s (%s)" % (self,self.location)
+            return "%s (%s)" % (self, self.location)
         else:
             return str(self)
 
     def __str__(self):
-        try: version = getattr(self,'version',None)
-        except ValueError: version = None
+        try:
+            version = getattr(self, 'version', None)
+        except ValueError:
+            version = None
         version = version or "[unknown version]"
-        return "%s %s" % (self.project_name,version)
+        return "%s %s" % (self.project_name, version)
 
-    def __getattr__(self,attr):
+    def __getattr__(self, attr):
         """Delegate all unrecognized public attributes to .metadata provider"""
         if attr.startswith('_'):
             raise AttributeError(attr)
         return getattr(self._provider, attr)
 
     @classmethod
-    def from_filename(cls,filename,metadata=None, **kw):
+    def from_filename(cls, filename, metadata=None, **kw):
         return cls.from_location(
             _normalize_cached(filename), os.path.basename(filename), metadata,
             **kw
@@ -2353,13 +2554,18 @@ def from_filename(cls,filename,metadata=None, **kw):
 
     def as_requirement(self):
         """Return a ``Requirement`` that matches this distribution exactly"""
-        return Requirement.parse('%s==%s' % (self.project_name, self.version))
+        if isinstance(self.parsed_version, packaging.version.Version):
+            spec = "%s==%s" % (self.project_name, self.parsed_version)
+        else:
+            spec = "%s===%s" % (self.project_name, self.parsed_version)
+
+        return Requirement.parse(spec)
 
     def load_entry_point(self, group, name):
         """Return the `name` entry point of `group` or raise ImportError"""
-        ep = self.get_entry_info(group,name)
+        ep = self.get_entry_info(group, name)
         if ep is None:
-            raise ImportError("Entry point %r not found" % ((group,name),))
+            raise ImportError("Entry point %r not found" % ((group, name),))
         return ep.load()
 
     def get_entry_map(self, group=None):
@@ -2378,7 +2584,7 @@ def get_entry_info(self, group, name):
         """Return the EntryPoint object for `group`+`name`, or ``None``"""
         return self.get_entry_map(group).get(name)
 
-    def insert_on(self, path, loc = None):
+    def insert_on(self, path, loc=None, replace=False):
         """Insert self.location in path before its nearest parent directory"""
 
         loc = loc or self.location
@@ -2390,9 +2596,9 @@ def insert_on(self, path, loc = None):
         npath= [(p and _normalize_cached(p) or p) for p in path]
 
         for p, item in enumerate(npath):
-            if item==nloc:
+            if item == nloc:
                 break
-            elif item==bdir and self.precedence==EGG_DIST:
+            elif item == bdir and self.precedence == EGG_DIST:
                 # if it's an .egg, give it precedence over its directory
                 if path is sys.path:
                     self.check_version_conflict()
@@ -2402,24 +2608,29 @@ def insert_on(self, path, loc = None):
         else:
             if path is sys.path:
                 self.check_version_conflict()
-            path.append(loc)
+            if replace:
+                path.insert(0, loc)
+            else:
+                path.append(loc)
             return
 
         # p is the spot where we found or inserted loc; now remove duplicates
-        while 1:
+        while True:
             try:
                 np = npath.index(nloc, p+1)
             except ValueError:
                 break
             else:
                 del npath[np], path[np]
-                p = np  # ha!
+                # ha!
+                p = np
 
         return
 
     def check_version_conflict(self):
-        if self.key=='setuptools':
-            return      # ignore the inevitable setuptools self-conflicts  :(
+        if self.key == 'setuptools':
+            # ignore the inevitable setuptools self-conflicts  :(
+            return
 
         nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt'))
         loc = normalize_path(self.location)
@@ -2442,17 +2653,15 @@ def has_version(self):
         try:
             self.version
         except ValueError:
-            issue_warning("Unbuilt egg for "+repr(self))
+            issue_warning("Unbuilt egg for " + repr(self))
             return False
         return True
 
     def clone(self,**kw):
         """Copy this distribution, substituting in any changed keyword args"""
-        for attr in (
-            'project_name', 'version', 'py_version', 'platform', 'location',
-            'precedence'
-        ):
-            kw.setdefault(attr, getattr(self,attr,None))
+        names = 'project_name version py_version platform location precedence'
+        for attr in names.split():
+            kw.setdefault(attr, getattr(self, attr, None))
         kw.setdefault('metadata', self._provider)
         return self.__class__(**kw)
 
@@ -2461,6 +2670,26 @@ def extras(self):
         return [dep for dep in self._dep_map if dep]
 
 
+class EggInfoDistribution(Distribution):
+
+    def _reload_version(self):
+        """
+        Packages installed by distutils (e.g. numpy or scipy),
+        which uses an old safe_version, and so
+        their version numbers can get mangled when
+        converted to filenames (e.g., 1.11.0.dev0+2329eae to
+        1.11.0.dev0_2329eae). These distributions will not be
+        parsed properly
+        downstream by Distribution and safe_version, so
+        take an extra step and try to get the version number from
+        the metadata file itself instead of the filename.
+        """
+        md_version = _version_from_file(self._get_metadata(self.PKG_INFO))
+        if md_version:
+            self._version = md_version
+        return self
+
+
 class DistInfoDistribution(Distribution):
     """Wrap an actual or potential sys.path entry w/metadata, .dist-info style"""
     PKG_INFO = 'METADATA'
@@ -2472,8 +2701,8 @@ def _parsed_pkg_info(self):
         try:
             return self._pkg_info
         except AttributeError:
-            from email.parser import Parser
-            self._pkg_info = Parser().parsestr(self.get_metadata(self.PKG_INFO))
+            metadata = self.get_metadata(self.PKG_INFO)
+            self._pkg_info = email.parser.Parser().parsestr(metadata)
             return self._pkg_info
 
     @property
@@ -2484,34 +2713,18 @@ def _dep_map(self):
             self.__dep_map = self._compute_dependencies()
             return self.__dep_map
 
-    def _preparse_requirement(self, requires_dist):
-        """Convert 'Foobar (1); baz' to ('Foobar ==1', 'baz')
-        Split environment marker, add == prefix to version specifiers as
-        necessary, and remove parenthesis.
-        """
-        parts = requires_dist.split(';', 1) + ['']
-        distvers = parts[0].strip()
-        mark = parts[1].strip()
-        distvers = re.sub(self.EQEQ, r"\1==\2\3", distvers)
-        distvers = distvers.replace('(', '').replace(')', '')
-        return (distvers, mark)
-
     def _compute_dependencies(self):
         """Recompute this distribution's dependencies."""
-        from pip._vendor._markerlib import compile as compile_marker
         dm = self.__dep_map = {None: []}
 
         reqs = []
         # Including any condition expressions
         for req in self._parsed_pkg_info.get_all('Requires-Dist') or []:
-            distvers, mark = self._preparse_requirement(req)
-            parsed = next(parse_requirements(distvers))
-            parsed.marker_fn = compile_marker(mark)
-            reqs.append(parsed)
+            reqs.extend(parse_requirements(req))
 
         def reqs_for_extra(extra):
             for req in reqs:
-                if req.marker_fn(override={'extra':extra}):
+                if not req.marker or req.marker.evaluate({'extra': extra}):
                     yield req
 
         common = frozenset(reqs_for_extra(None))
@@ -2526,7 +2739,7 @@ def reqs_for_extra(extra):
 
 _distributionImpl = {
     '.egg': Distribution,
-    '.egg-info': Distribution,
+    '.egg-info': EggInfoDistribution,
     '.dist-info': DistInfoDistribution,
     }
 
@@ -2541,121 +2754,74 @@ def issue_warning(*args,**kw):
             level += 1
     except ValueError:
         pass
-    from warnings import warn
-    warn(stacklevel = level+1, *args, **kw)
+    warnings.warn(stacklevel=level + 1, *args, **kw)
+
+
+class RequirementParseError(ValueError):
+    def __str__(self):
+        return ' '.join(self.args)
 
 
 def parse_requirements(strs):
     """Yield ``Requirement`` objects for each specification in `strs`
 
-    `strs` must be an instance of ``basestring``, or a (possibly-nested)
-    iterable thereof.
+    `strs` must be a string, or a (possibly-nested) iterable thereof.
     """
     # create a steppable iterator, so we can handle \-continuations
     lines = iter(yield_lines(strs))
 
-    def scan_list(ITEM,TERMINATOR,line,p,groups,item_name):
-
-        items = []
-
-        while not TERMINATOR(line,p):
-            if CONTINUE(line,p):
-                try:
-                    line = next(lines)
-                    p = 0
-                except StopIteration:
-                    raise ValueError(
-                        "\\ must not appear on the last nonblank line"
-                    )
-
-            match = ITEM(line,p)
-            if not match:
-                raise ValueError("Expected "+item_name+" in",line,"at",line[p:])
-
-            items.append(match.group(*groups))
-            p = match.end()
-
-            match = COMMA(line,p)
-            if match:
-                p = match.end() # skip the comma
-            elif not TERMINATOR(line,p):
-                raise ValueError(
-                    "Expected ',' or end-of-list in",line,"at",line[p:]
-                )
-
-        match = TERMINATOR(line,p)
-        if match: p = match.end()   # skip the terminator, if any
-        return line, p, items
-
     for line in lines:
-        match = DISTRO(line)
-        if not match:
-            raise ValueError("Missing distribution spec", line)
-        project_name = match.group(1)
-        p = match.end()
-        extras = []
-
-        match = OBRACKET(line,p)
-        if match:
-            p = match.end()
-            line, p, extras = scan_list(
-                DISTRO, CBRACKET, line, p, (1,), "'extra' name"
-            )
-
-        line, p, specs = scan_list(VERSION,LINE_END,line,p,(1,2),"version spec")
-        specs = [(op,safe_version(val)) for op,val in specs]
-        yield Requirement(project_name, specs, extras)
-
-
-def _sort_dists(dists):
-    tmp = [(dist.hashcmp,dist) for dist in dists]
-    tmp.sort()
-    dists[::-1] = [d for hc,d in tmp]
-
-
-class Requirement:
-    def __init__(self, project_name, specs, extras):
+        # Drop comments -- a hash without a space may be in a URL.
+        if ' #' in line:
+            line = line[:line.find(' #')]
+        # If there is a line continuation, drop it, and append the next line.
+        if line.endswith('\\'):
+            line = line[:-2].strip()
+            line += next(lines)
+        yield Requirement(line)
+
+
+class Requirement(packaging.requirements.Requirement):
+    def __init__(self, requirement_string):
         """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!"""
-        self.unsafe_name, project_name = project_name, safe_name(project_name)
+        try:
+            super(Requirement, self).__init__(requirement_string)
+        except packaging.requirements.InvalidRequirement as e:
+            raise RequirementParseError(str(e))
+        self.unsafe_name = self.name
+        project_name = safe_name(self.name)
         self.project_name, self.key = project_name, project_name.lower()
-        index = [(parse_version(v),state_machine[op],op,v) for op,v in specs]
-        index.sort()
-        self.specs = [(op,ver) for parsed,trans,op,ver in index]
-        self.index, self.extras = index, tuple(map(safe_extra,extras))
+        self.specs = [
+            (spec.operator, spec.version) for spec in self.specifier]
+        self.extras = tuple(map(safe_extra, self.extras))
         self.hashCmp = (
-            self.key, tuple([(op,parsed) for parsed,trans,op,ver in index]),
-            frozenset(self.extras)
+            self.key,
+            self.specifier,
+            frozenset(self.extras),
+            str(self.marker) if self.marker else None,
         )
         self.__hash = hash(self.hashCmp)
 
-    def __str__(self):
-        specs = ','.join([''.join(s) for s in self.specs])
-        extras = ','.join(self.extras)
-        if extras: extras = '[%s]' % extras
-        return '%s%s%s' % (self.project_name, extras, specs)
-
-    def __eq__(self,other):
-        return isinstance(other,Requirement) and self.hashCmp==other.hashCmp
-
-    def __contains__(self,item):
-        if isinstance(item,Distribution):
-            if item.key != self.key: return False
-            if self.index: item = item.parsed_version  # only get if we need it
-        elif isinstance(item,basestring):
-            item = parse_version(item)
-        last = None
-        compare = lambda a, b: (a > b) - (a < b) # -1, 0, 1
-        for parsed,trans,op,ver in self.index:
-            action = trans[compare(item,parsed)] # Indexing: 0, 1, -1
-            if action=='F':
+    def __eq__(self, other):
+        return (
+            isinstance(other, Requirement) and
+            self.hashCmp == other.hashCmp
+        )
+
+    def __ne__(self, other):
+        return not self == other
+
+    def __contains__(self, item):
+        if isinstance(item, Distribution):
+            if item.key != self.key:
                 return False
-            elif action=='T':
-                return True
-            elif action=='+':
-                last = True
-            elif action=='-' or last is None:   last = False
-        if last is None: last = True    # no rules encountered
-        return last
+
+            item = item.version
+
+        # Allow prereleases always in order to match the previous behavior of
+        # this method. In the future this should be smarter and follow PEP 440
+        # more accurately.
+        return self.specifier.contains(item, prereleases=True)
 
     def __hash__(self):
         return self.__hash
@@ -2664,28 +2830,14 @@ def __repr__(self): return "Requirement.parse(%r)" % str(self)
 
     @staticmethod
     def parse(s):
-        reqs = list(parse_requirements(s))
-        if reqs:
-            if len(reqs)==1:
-                return reqs[0]
-            raise ValueError("Expected only one requirement", s)
-        raise ValueError("No requirements found", s)
-
-state_machine = {
-    #       =><
-    '<': '--T',
-    '<=': 'T-T',
-    '>': 'F+F',
-    '>=': 'T+F',
-    '==': 'T..',
-    '!=': 'F++',
-}
+        req, = parse_requirements(s)
+        return req
 
 
 def _get_mro(cls):
     """Get an mro for a type or classic class"""
-    if not isinstance(cls,type):
-        class cls(cls,object): pass
+    if not isinstance(cls, type):
+        class cls(cls, object): pass
         return cls.__mro__[1:]
     return cls.__mro__
 
@@ -2702,8 +2854,19 @@ def ensure_directory(path):
     if not os.path.isdir(dirname):
         os.makedirs(dirname)
 
+
+def _bypass_ensure_directory(path):
+    """Sandbox-bypassing version of ensure_directory()"""
+    if not WRITE_SUPPORT:
+        raise IOError('"os.mkdir" not supported on this platform.')
+    dirname, filename = split(path)
+    if dirname and filename and not isdir(dirname):
+        _bypass_ensure_directory(dirname)
+        mkdir(dirname, 0o755)
+
+
 def split_sections(s):
-    """Split a string or iterable thereof into (section,content) pairs
+    """Split a string or iterable thereof into (section, content) pairs
 
     Each ``section`` is a stripped version of the section header ("[section]")
     and each ``content`` is a list of stripped lines excluding blank lines and
@@ -2728,35 +2891,66 @@ def split_sections(s):
     yield section, content
 
 def _mkstemp(*args,**kw):
-    from tempfile import mkstemp
     old_open = os.open
     try:
-        os.open = os_open   # temporarily bypass sandboxing
-        return mkstemp(*args,**kw)
+        # temporarily bypass sandboxing
+        os.open = os_open
+        return tempfile.mkstemp(*args,**kw)
     finally:
-        os.open = old_open  # and then put it back
+        # and then put it back
+        os.open = old_open
 
 
-# Set up global resource manager (deliberately not state-saved)
-_manager = ResourceManager()
-def _initialize(g):
-    for name in dir(_manager):
+# Silence the PEP440Warning by default, so that end users don't get hit by it
+# randomly just because they use pkg_resources. We want to append the rule
+# because we want earlier uses of filterwarnings to take precedence over this
+# one.
+warnings.filterwarnings("ignore", category=PEP440Warning, append=True)
+
+
+# from jaraco.functools 1.3
+def _call_aside(f, *args, **kwargs):
+    f(*args, **kwargs)
+    return f
+
+
+@_call_aside
+def _initialize(g=globals()):
+    "Set up global resource manager (deliberately not state-saved)"
+    manager = ResourceManager()
+    g['_manager'] = manager
+    for name in dir(manager):
         if not name.startswith('_'):
-            g[name] = getattr(_manager, name)
-_initialize(globals())
-
-# Prepare the master working set and make the ``require()`` API available
-working_set = WorkingSet._build_master()
-_declare_state('object', working_set=working_set)
-
-require = working_set.require
-iter_entry_points = working_set.iter_entry_points
-add_activation_listener = working_set.subscribe
-run_script = working_set.run_script
-run_main = run_script   # backward compatibility
-# Activate all distributions already on sys.path, and ensure that
-# all distributions added to the working set in the future (e.g. by
-# calling ``require()``) will get activated as well.
-add_activation_listener(lambda dist: dist.activate())
-working_set.entries=[]
-list(map(working_set.add_entry,sys.path)) # match order
+            g[name] = getattr(manager, name)
+
+
+@_call_aside
+def _initialize_master_working_set():
+    """
+    Prepare the master working set and make the ``require()``
+    API available.
+
+    This function has explicit effects on the global state
+    of pkg_resources. It is intended to be invoked once at
+    the initialization of this module.
+
+    Invocation by other packages is unsupported and done
+    at their own risk.
+    """
+    working_set = WorkingSet._build_master()
+    _declare_state('object', working_set=working_set)
+
+    require = working_set.require
+    iter_entry_points = working_set.iter_entry_points
+    add_activation_listener = working_set.subscribe
+    run_script = working_set.run_script
+    # backward compatibility
+    run_main = run_script
+    # Activate all distributions already on sys.path, and ensure that
+    # all distributions added to the working set in the future (e.g. by
+    # calling ``require()``) will get activated as well.
+    add_activation_listener(lambda dist: dist.activate())
+    working_set.entries=[]
+    # match order
+    list(map(working_set.add_entry, sys.path))
+    globals().update(locals())
diff --git a/pip/_vendor/progress/__init__.py b/pip/_vendor/progress/__init__.py
new file mode 100644
index 00000000000..5107bc0a796
--- /dev/null
+++ b/pip/_vendor/progress/__init__.py
@@ -0,0 +1,123 @@
+# Copyright (c) 2012 Giorgos Verigakis 
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from __future__ import division
+
+from collections import deque
+from datetime import timedelta
+from math import ceil
+from sys import stderr
+from time import time
+
+
+__version__ = '1.2'
+
+
+class Infinite(object):
+    file = stderr
+    sma_window = 10
+
+    def __init__(self, *args, **kwargs):
+        self.index = 0
+        self.start_ts = time()
+        self._ts = self.start_ts
+        self._dt = deque(maxlen=self.sma_window)
+        for key, val in kwargs.items():
+            setattr(self, key, val)
+
+    def __getitem__(self, key):
+        if key.startswith('_'):
+            return None
+        return getattr(self, key, None)
+
+    @property
+    def avg(self):
+        return sum(self._dt) / len(self._dt) if self._dt else 0
+
+    @property
+    def elapsed(self):
+        return int(time() - self.start_ts)
+
+    @property
+    def elapsed_td(self):
+        return timedelta(seconds=self.elapsed)
+
+    def update(self):
+        pass
+
+    def start(self):
+        pass
+
+    def finish(self):
+        pass
+
+    def next(self, n=1):
+        if n > 0:
+            now = time()
+            dt = (now - self._ts) / n
+            self._dt.append(dt)
+            self._ts = now
+
+        self.index = self.index + n
+        self.update()
+
+    def iter(self, it):
+        for x in it:
+            yield x
+            self.next()
+        self.finish()
+
+
+class Progress(Infinite):
+    def __init__(self, *args, **kwargs):
+        super(Progress, self).__init__(*args, **kwargs)
+        self.max = kwargs.get('max', 100)
+
+    @property
+    def eta(self):
+        return int(ceil(self.avg * self.remaining))
+
+    @property
+    def eta_td(self):
+        return timedelta(seconds=self.eta)
+
+    @property
+    def percent(self):
+        return self.progress * 100
+
+    @property
+    def progress(self):
+        return min(1, self.index / self.max)
+
+    @property
+    def remaining(self):
+        return max(self.max - self.index, 0)
+
+    def start(self):
+        self.update()
+
+    def goto(self, index):
+        incr = index - self.index
+        self.next(incr)
+
+    def iter(self, it):
+        try:
+            self.max = len(it)
+        except TypeError:
+            pass
+
+        for x in it:
+            yield x
+            self.next()
+        self.finish()
diff --git a/pip/_vendor/progress/bar.py b/pip/_vendor/progress/bar.py
new file mode 100644
index 00000000000..8ce14610046
--- /dev/null
+++ b/pip/_vendor/progress/bar.py
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 Giorgos Verigakis 
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from . import Progress
+from .helpers import WritelnMixin
+
+
+class Bar(WritelnMixin, Progress):
+    width = 32
+    message = ''
+    suffix = '%(index)d/%(max)d'
+    bar_prefix = ' |'
+    bar_suffix = '| '
+    empty_fill = ' '
+    fill = '#'
+    hide_cursor = True
+
+    def update(self):
+        filled_length = int(self.width * self.progress)
+        empty_length = self.width - filled_length
+
+        message = self.message % self
+        bar = self.fill * filled_length
+        empty = self.empty_fill * empty_length
+        suffix = self.suffix % self
+        line = ''.join([message, self.bar_prefix, bar, empty, self.bar_suffix,
+                        suffix])
+        self.writeln(line)
+
+
+class ChargingBar(Bar):
+    suffix = '%(percent)d%%'
+    bar_prefix = ' '
+    bar_suffix = ' '
+    empty_fill = u'∙'
+    fill = u'█'
+
+
+class FillingSquaresBar(ChargingBar):
+    empty_fill = u'▢'
+    fill = u'▣'
+
+
+class FillingCirclesBar(ChargingBar):
+    empty_fill = u'◯'
+    fill = u'◉'
+
+
+class IncrementalBar(Bar):
+    phases = (u' ', u'▏', u'▎', u'▍', u'▌', u'▋', u'▊', u'▉', u'█')
+
+    def update(self):
+        nphases = len(self.phases)
+        expanded_length = int(nphases * self.width * self.progress)
+        filled_length = int(self.width * self.progress)
+        empty_length = self.width - filled_length
+        phase = expanded_length - (filled_length * nphases)
+
+        message = self.message % self
+        bar = self.phases[-1] * filled_length
+        current = self.phases[phase] if phase > 0 else ''
+        empty = self.empty_fill * max(0, empty_length - len(current))
+        suffix = self.suffix % self
+        line = ''.join([message, self.bar_prefix, bar, current, empty,
+                        self.bar_suffix, suffix])
+        self.writeln(line)
+
+
+class ShadyBar(IncrementalBar):
+    phases = (u' ', u'░', u'▒', u'▓', u'█')
diff --git a/pip/_vendor/progress/counter.py b/pip/_vendor/progress/counter.py
new file mode 100644
index 00000000000..caaddc680d3
--- /dev/null
+++ b/pip/_vendor/progress/counter.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 Giorgos Verigakis 
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from . import Infinite, Progress
+from .helpers import WriteMixin
+
+
+class Counter(WriteMixin, Infinite):
+    message = ''
+    hide_cursor = True
+
+    def update(self):
+        self.write(str(self.index))
+
+
+class Countdown(WriteMixin, Progress):
+    hide_cursor = True
+
+    def update(self):
+        self.write(str(self.remaining))
+
+
+class Stack(WriteMixin, Progress):
+    phases = (u' ', u'▁', u'▂', u'▃', u'▄', u'▅', u'▆', u'▇', u'█')
+    hide_cursor = True
+
+    def update(self):
+        nphases = len(self.phases)
+        i = min(nphases - 1, int(self.progress * nphases))
+        self.write(self.phases[i])
+
+
+class Pie(Stack):
+    phases = (u'○', u'◔', u'◑', u'◕', u'●')
diff --git a/pip/_vendor/progress/helpers.py b/pip/_vendor/progress/helpers.py
new file mode 100644
index 00000000000..9ed90b2bc4c
--- /dev/null
+++ b/pip/_vendor/progress/helpers.py
@@ -0,0 +1,91 @@
+# Copyright (c) 2012 Giorgos Verigakis 
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from __future__ import print_function
+
+
+HIDE_CURSOR = '\x1b[?25l'
+SHOW_CURSOR = '\x1b[?25h'
+
+
+class WriteMixin(object):
+    hide_cursor = False
+
+    def __init__(self, message=None, **kwargs):
+        super(WriteMixin, self).__init__(**kwargs)
+        self._width = 0
+        if message:
+            self.message = message
+
+        if self.file.isatty():
+            if self.hide_cursor:
+                print(HIDE_CURSOR, end='', file=self.file)
+            print(self.message, end='', file=self.file)
+            self.file.flush()
+
+    def write(self, s):
+        if self.file.isatty():
+            b = '\b' * self._width
+            c = s.ljust(self._width)
+            print(b + c, end='', file=self.file)
+            self._width = max(self._width, len(s))
+            self.file.flush()
+
+    def finish(self):
+        if self.file.isatty() and self.hide_cursor:
+            print(SHOW_CURSOR, end='', file=self.file)
+
+
+class WritelnMixin(object):
+    hide_cursor = False
+
+    def __init__(self, message=None, **kwargs):
+        super(WritelnMixin, self).__init__(**kwargs)
+        if message:
+            self.message = message
+
+        if self.file.isatty() and self.hide_cursor:
+            print(HIDE_CURSOR, end='', file=self.file)
+
+    def clearln(self):
+        if self.file.isatty():
+            print('\r\x1b[K', end='', file=self.file)
+
+    def writeln(self, line):
+        if self.file.isatty():
+            self.clearln()
+            print(line, end='', file=self.file)
+            self.file.flush()
+
+    def finish(self):
+        if self.file.isatty():
+            print(file=self.file)
+            if self.hide_cursor:
+                print(SHOW_CURSOR, end='', file=self.file)
+
+
+from signal import signal, SIGINT
+from sys import exit
+
+
+class SigIntMixin(object):
+    """Registers a signal handler that calls finish on SIGINT"""
+
+    def __init__(self, *args, **kwargs):
+        super(SigIntMixin, self).__init__(*args, **kwargs)
+        signal(SIGINT, self._sigint_handler)
+
+    def _sigint_handler(self, signum, frame):
+        self.finish()
+        exit(0)
diff --git a/pip/_vendor/progress/spinner.py b/pip/_vendor/progress/spinner.py
new file mode 100644
index 00000000000..969bfbb5c80
--- /dev/null
+++ b/pip/_vendor/progress/spinner.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 Giorgos Verigakis 
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from . import Infinite
+from .helpers import WriteMixin
+
+
+class Spinner(WriteMixin, Infinite):
+    message = ''
+    phases = ('-', '\\', '|', '/')
+    hide_cursor = True
+
+    def update(self):
+        i = self.index % len(self.phases)
+        self.write(self.phases[i])
+
+
+class PieSpinner(Spinner):
+    phases = [u'◷', u'◶', u'◵', u'◴']
+
+
+class MoonSpinner(Spinner):
+    phases = [u'◑', u'◒', u'◐', u'◓']
+
+
+class LineSpinner(Spinner):
+    phases = [u'⎺', u'⎻', u'⎼', u'⎽', u'⎼', u'⎻']
diff --git a/pip/_vendor/pyparsing.py b/pip/_vendor/pyparsing.py
new file mode 100644
index 00000000000..56f19663755
--- /dev/null
+++ b/pip/_vendor/pyparsing.py
@@ -0,0 +1,3837 @@
+# module pyparsing.py
+#
+# Copyright (c) 2003-2015  Paul T. McGuire
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__doc__ = \
+"""
+pyparsing module - Classes and methods to define and execute parsing grammars
+
+The pyparsing module is an alternative approach to creating and executing simple grammars,
+vs. the traditional lex/yacc approach, or the use of regular expressions.  With pyparsing, you
+don't need to learn a new syntax for defining grammars or matching expressions - the parsing module
+provides a library of classes that you use to construct the grammar directly in Python.
+
+Here is a program to parse "Hello, World!" (or any greeting of the form C{", !"})::
+
+    from pyparsing import Word, alphas
+
+    # define grammar of a greeting
+    greet = Word( alphas ) + "," + Word( alphas ) + "!"
+
+    hello = "Hello, World!"
+    print (hello, "->", greet.parseString( hello ))
+
+The program outputs the following::
+
+    Hello, World! -> ['Hello', ',', 'World', '!']
+
+The Python representation of the grammar is quite readable, owing to the self-explanatory
+class names, and the use of '+', '|' and '^' operators.
+
+The parsed results returned from C{parseString()} can be accessed as a nested list, a dictionary, or an
+object with named attributes.
+
+The pyparsing module handles some of the problems that are typically vexing when writing text parsers:
+ - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello  ,  World  !", etc.)
+ - quoted strings
+ - embedded comments
+"""
+
+__version__ = "2.1.1"
+__versionTime__ = "21 Mar 2016 05:04 UTC"
+__author__ = "Paul McGuire "
+
+import string
+from weakref import ref as wkref
+import copy
+import sys
+import warnings
+import re
+import sre_constants
+import collections
+import pprint
+import functools
+import itertools
+import traceback
+
+#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) )
+
+__all__ = [
+'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty',
+'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal',
+'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or',
+'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException',
+'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException',
+'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', 
+'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore',
+'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col',
+'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString',
+'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'hexnums',
+'htmlComment', 'javaStyleComment', 'line', 'lineEnd', 'lineStart', 'lineno',
+'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral',
+'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables',
+'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', 
+'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd',
+'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute',
+'indentedBlock', 'originalTextFor', 'ungroup', 'infixNotation','locatedExpr', 'withClass',
+]
+
+PY_3 = sys.version.startswith('3')
+if PY_3:
+    _MAX_INT = sys.maxsize
+    basestring = str
+    unichr = chr
+    _ustr = str
+
+    # build list of single arg builtins, that can be used as parse actions
+    singleArgBuiltins = [sum, len, sorted, reversed, list, tuple, set, any, all, min, max]
+
+else:
+    _MAX_INT = sys.maxint
+    range = xrange
+
+    def _ustr(obj):
+        """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries
+           str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It
+           then < returns the unicode object | encodes it with the default encoding | ... >.
+        """
+        if isinstance(obj,unicode):
+            return obj
+
+        try:
+            # If this works, then _ustr(obj) has the same behaviour as str(obj), so
+            # it won't break any existing code.
+            return str(obj)
+
+        except UnicodeEncodeError:
+            # Else encode it
+            ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace')
+            xmlcharref = Regex('&#\d+;')
+            xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:])
+            return xmlcharref.transformString(ret)
+
+    # build list of single arg builtins, tolerant of Python version, that can be used as parse actions
+    singleArgBuiltins = []
+    import __builtin__
+    for fname in "sum len sorted reversed list tuple set any all min max".split():
+        try:
+            singleArgBuiltins.append(getattr(__builtin__,fname))
+        except AttributeError:
+            continue
+            
+_generatorType = type((y for y in range(1)))
+ 
+def _xml_escape(data):
+    """Escape &, <, >, ", ', etc. in a string of data."""
+
+    # ampersand must be replaced first
+    from_symbols = '&><"\''
+    to_symbols = ('&'+s+';' for s in "amp gt lt quot apos".split())
+    for from_,to_ in zip(from_symbols, to_symbols):
+        data = data.replace(from_, to_)
+    return data
+
+class _Constants(object):
+    pass
+
+alphas     = string.ascii_uppercase + string.ascii_lowercase
+nums       = "0123456789"
+hexnums    = nums + "ABCDEFabcdef"
+alphanums  = alphas + nums
+_bslash    = chr(92)
+printables = "".join(c for c in string.printable if c not in string.whitespace)
+
+class ParseBaseException(Exception):
+    """base exception class for all parsing runtime exceptions"""
+    # Performance tuning: we construct a *lot* of these, so keep this
+    # constructor as small and fast as possible
+    def __init__( self, pstr, loc=0, msg=None, elem=None ):
+        self.loc = loc
+        if msg is None:
+            self.msg = pstr
+            self.pstr = ""
+        else:
+            self.msg = msg
+            self.pstr = pstr
+        self.parserElement = elem
+
+    def __getattr__( self, aname ):
+        """supported attributes by name are:
+            - lineno - returns the line number of the exception text
+            - col - returns the column number of the exception text
+            - line - returns the line containing the exception text
+        """
+        if( aname == "lineno" ):
+            return lineno( self.loc, self.pstr )
+        elif( aname in ("col", "column") ):
+            return col( self.loc, self.pstr )
+        elif( aname == "line" ):
+            return line( self.loc, self.pstr )
+        else:
+            raise AttributeError(aname)
+
+    def __str__( self ):
+        return "%s (at char %d), (line:%d, col:%d)" % \
+                ( self.msg, self.loc, self.lineno, self.column )
+    def __repr__( self ):
+        return _ustr(self)
+    def markInputline( self, markerString = ">!<" ):
+        """Extracts the exception line from the input string, and marks
+           the location of the exception with a special symbol.
+        """
+        line_str = self.line
+        line_column = self.column - 1
+        if markerString:
+            line_str = "".join((line_str[:line_column],
+                                markerString, line_str[line_column:]))
+        return line_str.strip()
+    def __dir__(self):
+        return "lineno col line".split() + dir(type(self))
+
+class ParseException(ParseBaseException):
+    """exception thrown when parse expressions don't match class;
+       supported attributes by name are:
+        - lineno - returns the line number of the exception text
+        - col - returns the column number of the exception text
+        - line - returns the line containing the exception text
+    """
+    pass
+
+class ParseFatalException(ParseBaseException):
+    """user-throwable exception thrown when inconsistent parse content
+       is found; stops all parsing immediately"""
+    pass
+
+class ParseSyntaxException(ParseFatalException):
+    """just like C{L{ParseFatalException}}, but thrown internally when an
+       C{L{ErrorStop}} ('-' operator) indicates that parsing is to stop immediately because
+       an unbacktrackable syntax error has been found"""
+    def __init__(self, pe):
+        super(ParseSyntaxException, self).__init__(
+                                    pe.pstr, pe.loc, pe.msg, pe.parserElement)
+
+#~ class ReparseException(ParseBaseException):
+    #~ """Experimental class - parse actions can raise this exception to cause
+       #~ pyparsing to reparse the input string:
+        #~ - with a modified input string, and/or
+        #~ - with a modified start location
+       #~ Set the values of the ReparseException in the constructor, and raise the
+       #~ exception in a parse action to cause pyparsing to use the new string/location.
+       #~ Setting the values as None causes no change to be made.
+       #~ """
+    #~ def __init_( self, newstring, restartLoc ):
+        #~ self.newParseText = newstring
+        #~ self.reparseLoc = restartLoc
+
+class RecursiveGrammarException(Exception):
+    """exception thrown by C{validate()} if the grammar could be improperly recursive"""
+    def __init__( self, parseElementList ):
+        self.parseElementTrace = parseElementList
+
+    def __str__( self ):
+        return "RecursiveGrammarException: %s" % self.parseElementTrace
+
+class _ParseResultsWithOffset(object):
+    def __init__(self,p1,p2):
+        self.tup = (p1,p2)
+    def __getitem__(self,i):
+        return self.tup[i]
+    def __repr__(self):
+        return repr(self.tup)
+    def setOffset(self,i):
+        self.tup = (self.tup[0],i)
+
+class ParseResults(object):
+    """Structured parse results, to provide multiple means of access to the parsed data:
+       - as a list (C{len(results)})
+       - by list index (C{results[0], results[1]}, etc.)
+       - by attribute (C{results.})
+       """
+    def __new__(cls, toklist=None, name=None, asList=True, modal=True ):
+        if isinstance(toklist, cls):
+            return toklist
+        retobj = object.__new__(cls)
+        retobj.__doinit = True
+        return retobj
+
+    # Performance tuning: we construct a *lot* of these, so keep this
+    # constructor as small and fast as possible
+    def __init__( self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance ):
+        if self.__doinit:
+            self.__doinit = False
+            self.__name = None
+            self.__parent = None
+            self.__accumNames = {}
+            self.__asList = asList
+            self.__modal = modal
+            if toklist is None:
+                toklist = []
+            if isinstance(toklist, list):
+                self.__toklist = toklist[:]
+            elif isinstance(toklist, _generatorType):
+                self.__toklist = list(toklist)
+            else:
+                self.__toklist = [toklist]
+            self.__tokdict = dict()
+
+        if name is not None and name:
+            if not modal:
+                self.__accumNames[name] = 0
+            if isinstance(name,int):
+                name = _ustr(name) # will always return a str, but use _ustr for consistency
+            self.__name = name
+            if not (isinstance(toklist, (type(None), basestring, list)) and toklist in (None,'',[])):
+                if isinstance(toklist,basestring):
+                    toklist = [ toklist ]
+                if asList:
+                    if isinstance(toklist,ParseResults):
+                        self[name] = _ParseResultsWithOffset(toklist.copy(),0)
+                    else:
+                        self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0)
+                    self[name].__name = name
+                else:
+                    try:
+                        self[name] = toklist[0]
+                    except (KeyError,TypeError,IndexError):
+                        self[name] = toklist
+
+    def __getitem__( self, i ):
+        if isinstance( i, (int,slice) ):
+            return self.__toklist[i]
+        else:
+            if i not in self.__accumNames:
+                return self.__tokdict[i][-1][0]
+            else:
+                return ParseResults([ v[0] for v in self.__tokdict[i] ])
+
+    def __setitem__( self, k, v, isinstance=isinstance ):
+        if isinstance(v,_ParseResultsWithOffset):
+            self.__tokdict[k] = self.__tokdict.get(k,list()) + [v]
+            sub = v[0]
+        elif isinstance(k,(int,slice)):
+            self.__toklist[k] = v
+            sub = v
+        else:
+            self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)]
+            sub = v
+        if isinstance(sub,ParseResults):
+            sub.__parent = wkref(self)
+
+    def __delitem__( self, i ):
+        if isinstance(i,(int,slice)):
+            mylen = len( self.__toklist )
+            del self.__toklist[i]
+
+            # convert int to slice
+            if isinstance(i, int):
+                if i < 0:
+                    i += mylen
+                i = slice(i, i+1)
+            # get removed indices
+            removed = list(range(*i.indices(mylen)))
+            removed.reverse()
+            # fixup indices in token dictionary
+            #~ for name in self.__tokdict:
+                #~ occurrences = self.__tokdict[name]
+                #~ for j in removed:
+                    #~ for k, (value, position) in enumerate(occurrences):
+                        #~ occurrences[k] = _ParseResultsWithOffset(value, position - (position > j))
+            for name,occurrences in self.__tokdict.items():
+                for j in removed:
+                    for k, (value, position) in enumerate(occurrences):
+                        occurrences[k] = _ParseResultsWithOffset(value, position - (position > j))
+        else:
+            del self.__tokdict[i]
+
+    def __contains__( self, k ):
+        return k in self.__tokdict
+
+    def __len__( self ): return len( self.__toklist )
+    def __bool__(self): return ( not not self.__toklist )
+    __nonzero__ = __bool__
+    def __iter__( self ): return iter( self.__toklist )
+    def __reversed__( self ): return iter( self.__toklist[::-1] )
+    def iterkeys( self ):
+        """Returns all named result keys."""
+        if hasattr(self.__tokdict, "iterkeys"):
+            return self.__tokdict.iterkeys()
+        else:
+            return iter(self.__tokdict)
+
+    def itervalues( self ):
+        """Returns all named result values."""
+        return (self[k] for k in self.iterkeys())
+            
+    def iteritems( self ):
+        return ((k, self[k]) for k in self.iterkeys())
+
+    if PY_3:
+        keys = iterkeys
+        values = itervalues
+        items = iteritems
+    else:
+        def keys( self ):
+            """Returns all named result keys."""
+            return list(self.iterkeys())
+
+        def values( self ):
+            """Returns all named result values."""
+            return list(self.itervalues())
+                
+        def items( self ):
+            """Returns all named result keys and values as a list of tuples."""
+            return list(self.iteritems())
+
+    def haskeys( self ):
+        """Since keys() returns an iterator, this method is helpful in bypassing
+           code that looks for the existence of any defined results names."""
+        return bool(self.__tokdict)
+        
+    def pop( self, *args, **kwargs):
+        """Removes and returns item at specified index (default=last).
+           Supports both list and dict semantics for pop(). If passed no
+           argument or an integer argument, it will use list semantics
+           and pop tokens from the list of parsed tokens. If passed a 
+           non-integer argument (most likely a string), it will use dict
+           semantics and pop the corresponding value from any defined 
+           results names. A second default return value argument is 
+           supported, just as in dict.pop()."""
+        if not args:
+            args = [-1]
+        for k,v in kwargs.items():
+            if k == 'default':
+                args = (args[0], v)
+            else:
+                raise TypeError("pop() got an unexpected keyword argument '%s'" % k)
+        if (isinstance(args[0], int) or 
+                        len(args) == 1 or 
+                        args[0] in self):
+            index = args[0]
+            ret = self[index]
+            del self[index]
+            return ret
+        else:
+            defaultvalue = args[1]
+            return defaultvalue
+
+    def get(self, key, defaultValue=None):
+        """Returns named result matching the given key, or if there is no
+           such name, then returns the given C{defaultValue} or C{None} if no
+           C{defaultValue} is specified."""
+        if key in self:
+            return self[key]
+        else:
+            return defaultValue
+
+    def insert( self, index, insStr ):
+        """Inserts new element at location index in the list of parsed tokens."""
+        self.__toklist.insert(index, insStr)
+        # fixup indices in token dictionary
+        #~ for name in self.__tokdict:
+            #~ occurrences = self.__tokdict[name]
+            #~ for k, (value, position) in enumerate(occurrences):
+                #~ occurrences[k] = _ParseResultsWithOffset(value, position + (position > index))
+        for name,occurrences in self.__tokdict.items():
+            for k, (value, position) in enumerate(occurrences):
+                occurrences[k] = _ParseResultsWithOffset(value, position + (position > index))
+
+    def append( self, item ):
+        """Add single element to end of ParseResults list of elements."""
+        self.__toklist.append(item)
+
+    def extend( self, itemseq ):
+        """Add sequence of elements to end of ParseResults list of elements."""
+        if isinstance(itemseq, ParseResults):
+            self += itemseq
+        else:
+            self.__toklist.extend(itemseq)
+
+    def clear( self ):
+        """Clear all elements and results names."""
+        del self.__toklist[:]
+        self.__tokdict.clear()
+
+    def __getattr__( self, name ):
+        try:
+            return self[name]
+        except KeyError:
+            return ""
+            
+        if name in self.__tokdict:
+            if name not in self.__accumNames:
+                return self.__tokdict[name][-1][0]
+            else:
+                return ParseResults([ v[0] for v in self.__tokdict[name] ])
+        else:
+            return ""
+
+    def __add__( self, other ):
+        ret = self.copy()
+        ret += other
+        return ret
+
+    def __iadd__( self, other ):
+        if other.__tokdict:
+            offset = len(self.__toklist)
+            addoffset = lambda a: offset if a<0 else a+offset
+            otheritems = other.__tokdict.items()
+            otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) )
+                                for (k,vlist) in otheritems for v in vlist]
+            for k,v in otherdictitems:
+                self[k] = v
+                if isinstance(v[0],ParseResults):
+                    v[0].__parent = wkref(self)
+            
+        self.__toklist += other.__toklist
+        self.__accumNames.update( other.__accumNames )
+        return self
+
+    def __radd__(self, other):
+        if isinstance(other,int) and other == 0:
+            # useful for merging many ParseResults using sum() builtin
+            return self.copy()
+        else:
+            # this may raise a TypeError - so be it
+            return other + self
+        
+    def __repr__( self ):
+        return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) )
+
+    def __str__( self ):
+        return '[' + ', '.join(_ustr(i) if isinstance(i, ParseResults) else repr(i) for i in self.__toklist) + ']'
+
+    def _asStringList( self, sep='' ):
+        out = []
+        for item in self.__toklist:
+            if out and sep:
+                out.append(sep)
+            if isinstance( item, ParseResults ):
+                out += item._asStringList()
+            else:
+                out.append( _ustr(item) )
+        return out
+
+    def asList( self ):
+        """Returns the parse results as a nested list of matching tokens, all converted to strings."""
+        return [res.asList() if isinstance(res,ParseResults) else res for res in self.__toklist]
+
+    def asDict( self ):
+        """Returns the named parse results as a nested dictionary."""
+        if PY_3:
+            item_fn = self.items
+        else:
+            item_fn = self.iteritems
+            
+        def toItem(obj):
+            if isinstance(obj, ParseResults):
+                if obj.haskeys():
+                    return obj.asDict()
+                else:
+                    return [toItem(v) for v in obj]
+            else:
+                return obj
+                
+        return dict((k,toItem(v)) for k,v in item_fn())
+
+    def copy( self ):
+        """Returns a new copy of a C{ParseResults} object."""
+        ret = ParseResults( self.__toklist )
+        ret.__tokdict = self.__tokdict.copy()
+        ret.__parent = self.__parent
+        ret.__accumNames.update( self.__accumNames )
+        ret.__name = self.__name
+        return ret
+
+    def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ):
+        """Returns the parse results as XML. Tags are created for tokens and lists that have defined results names."""
+        nl = "\n"
+        out = []
+        namedItems = dict((v[1],k) for (k,vlist) in self.__tokdict.items()
+                                                            for v in vlist)
+        nextLevelIndent = indent + "  "
+
+        # collapse out indents if formatting is not desired
+        if not formatted:
+            indent = ""
+            nextLevelIndent = ""
+            nl = ""
+
+        selfTag = None
+        if doctag is not None:
+            selfTag = doctag
+        else:
+            if self.__name:
+                selfTag = self.__name
+
+        if not selfTag:
+            if namedItemsOnly:
+                return ""
+            else:
+                selfTag = "ITEM"
+
+        out += [ nl, indent, "<", selfTag, ">" ]
+
+        for i,res in enumerate(self.__toklist):
+            if isinstance(res,ParseResults):
+                if i in namedItems:
+                    out += [ res.asXML(namedItems[i],
+                                        namedItemsOnly and doctag is None,
+                                        nextLevelIndent,
+                                        formatted)]
+                else:
+                    out += [ res.asXML(None,
+                                        namedItemsOnly and doctag is None,
+                                        nextLevelIndent,
+                                        formatted)]
+            else:
+                # individual token, see if there is a name for it
+                resTag = None
+                if i in namedItems:
+                    resTag = namedItems[i]
+                if not resTag:
+                    if namedItemsOnly:
+                        continue
+                    else:
+                        resTag = "ITEM"
+                xmlBodyText = _xml_escape(_ustr(res))
+                out += [ nl, nextLevelIndent, "<", resTag, ">",
+                                                xmlBodyText,
+                                                "" ]
+
+        out += [ nl, indent, "" ]
+        return "".join(out)
+
+    def __lookup(self,sub):
+        for k,vlist in self.__tokdict.items():
+            for v,loc in vlist:
+                if sub is v:
+                    return k
+        return None
+
+    def getName(self):
+        """Returns the results name for this token expression."""
+        if self.__name:
+            return self.__name
+        elif self.__parent:
+            par = self.__parent()
+            if par:
+                return par.__lookup(self)
+            else:
+                return None
+        elif (len(self) == 1 and
+               len(self.__tokdict) == 1 and
+               self.__tokdict.values()[0][0][1] in (0,-1)):
+            return self.__tokdict.keys()[0]
+        else:
+            return None
+
+    def dump(self,indent='',depth=0):
+        """Diagnostic method for listing out the contents of a C{ParseResults}.
+           Accepts an optional C{indent} argument so that this string can be embedded
+           in a nested display of other data."""
+        out = []
+        NL = '\n'
+        out.append( indent+_ustr(self.asList()) )
+        if self.haskeys():
+            items = sorted(self.items())
+            for k,v in items:
+                if out:
+                    out.append(NL)
+                out.append( "%s%s- %s: " % (indent,('  '*depth), k) )
+                if isinstance(v,ParseResults):
+                    if v:
+                        out.append( v.dump(indent,depth+1) )
+                    else:
+                        out.append(_ustr(v))
+                else:
+                    out.append(_ustr(v))
+        elif any(isinstance(vv,ParseResults) for vv in self):
+            v = self
+            for i,vv in enumerate(v):
+                if isinstance(vv,ParseResults):
+                    out.append("\n%s%s[%d]:\n%s%s%s" % (indent,('  '*(depth)),i,indent,('  '*(depth+1)),vv.dump(indent,depth+1) ))
+                else:
+                    out.append("\n%s%s[%d]:\n%s%s%s" % (indent,('  '*(depth)),i,indent,('  '*(depth+1)),_ustr(vv)))
+            
+        return "".join(out)
+
+    def pprint(self, *args, **kwargs):
+        """Pretty-printer for parsed results as a list, using the C{pprint} module.
+           Accepts additional positional or keyword args as defined for the 
+           C{pprint.pprint} method. (U{http://docs.python.org/3/library/pprint.html#pprint.pprint})"""
+        pprint.pprint(self.asList(), *args, **kwargs)
+
+    # add support for pickle protocol
+    def __getstate__(self):
+        return ( self.__toklist,
+                 ( self.__tokdict.copy(),
+                   self.__parent is not None and self.__parent() or None,
+                   self.__accumNames,
+                   self.__name ) )
+
+    def __setstate__(self,state):
+        self.__toklist = state[0]
+        (self.__tokdict,
+         par,
+         inAccumNames,
+         self.__name) = state[1]
+        self.__accumNames = {}
+        self.__accumNames.update(inAccumNames)
+        if par is not None:
+            self.__parent = wkref(par)
+        else:
+            self.__parent = None
+
+    def __getnewargs__(self):
+        return self.__toklist, self.__name, self.__asList, self.__modal
+
+    def __dir__(self):
+        return (dir(type(self)) + list(self.keys()))
+
+collections.MutableMapping.register(ParseResults)
+
+def col (loc,strg):
+    """Returns current column within a string, counting newlines as line separators.
+   The first column is number 1.
+
+   Note: the default parsing behavior is to expand tabs in the input string
+   before starting the parsing process.  See L{I{ParserElement.parseString}} for more information
+   on parsing strings containing C{}s, and suggested methods to maintain a
+   consistent view of the parsed string, the parse location, and line and column
+   positions within the parsed string.
+   """
+    s = strg
+    return 1 if loc} for more information
+   on parsing strings containing C{}s, and suggested methods to maintain a
+   consistent view of the parsed string, the parse location, and line and column
+   positions within the parsed string.
+   """
+    return strg.count("\n",0,loc) + 1
+
+def line( loc, strg ):
+    """Returns the line of text containing loc within a string, counting newlines as line separators.
+       """
+    lastCR = strg.rfind("\n", 0, loc)
+    nextCR = strg.find("\n", loc)
+    if nextCR >= 0:
+        return strg[lastCR+1:nextCR]
+    else:
+        return strg[lastCR+1:]
+
+def _defaultStartDebugAction( instring, loc, expr ):
+    print (("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )))
+
+def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ):
+    print ("Matched " + _ustr(expr) + " -> " + str(toks.asList()))
+
+def _defaultExceptionDebugAction( instring, loc, expr, exc ):
+    print ("Exception raised:" + _ustr(exc))
+
+def nullDebugAction(*args):
+    """'Do-nothing' debug action, to suppress debugging output during parsing."""
+    pass
+
+# Only works on Python 3.x - nonlocal is toxic to Python 2 installs
+#~ 'decorator to trim function calls to match the arity of the target'
+#~ def _trim_arity(func, maxargs=3):
+    #~ if func in singleArgBuiltins:
+        #~ return lambda s,l,t: func(t)
+    #~ limit = 0
+    #~ foundArity = False
+    #~ def wrapper(*args):
+        #~ nonlocal limit,foundArity
+        #~ while 1:
+            #~ try:
+                #~ ret = func(*args[limit:])
+                #~ foundArity = True
+                #~ return ret
+            #~ except TypeError:
+                #~ if limit == maxargs or foundArity:
+                    #~ raise
+                #~ limit += 1
+                #~ continue
+    #~ return wrapper
+
+# this version is Python 2.x-3.x cross-compatible
+'decorator to trim function calls to match the arity of the target'
+def _trim_arity(func, maxargs=2):
+    if func in singleArgBuiltins:
+        return lambda s,l,t: func(t)
+    limit = [0]
+    foundArity = [False]
+    def wrapper(*args):
+        while 1:
+            try:
+                ret = func(*args[limit[0]:]) #~@$^*)+_(&%#!=-`~;:"[]{}
+                foundArity[0] = True
+                return ret
+            except TypeError:
+                # re-raise TypeErrors if they did not come from our arity testing
+                if foundArity[0]:
+                    raise
+                else:
+                    try:
+                        tb = sys.exc_info()[-1]
+                        exc_source_line = traceback.extract_tb(tb)[-1][-1]
+                        if not exc_source_line.endswith('#~@$^*)+_(&%#!=-`~;:"[]{}'):
+                            raise
+                    finally:
+                        del tb
+
+                if limit[0] <= maxargs:
+                    limit[0] += 1
+                    continue
+                raise
+    return wrapper
+
+class ParserElement(object):
+    """Abstract base level parser element class."""
+    DEFAULT_WHITE_CHARS = " \n\t\r"
+    verbose_stacktrace = False
+
+    @staticmethod
+    def setDefaultWhitespaceChars( chars ):
+        """Overrides the default whitespace chars
+        """
+        ParserElement.DEFAULT_WHITE_CHARS = chars
+
+    @staticmethod
+    def inlineLiteralsUsing(cls):
+        """
+        Set class to be used for inclusion of string literals into a parser.
+        """
+        ParserElement.literalStringClass = cls
+
+    def __init__( self, savelist=False ):
+        self.parseAction = list()
+        self.failAction = None
+        #~ self.name = ""  # don't define self.name, let subclasses try/except upcall
+        self.strRepr = None
+        self.resultsName = None
+        self.saveAsList = savelist
+        self.skipWhitespace = True
+        self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
+        self.copyDefaultWhiteChars = True
+        self.mayReturnEmpty = False # used when checking for left-recursion
+        self.keepTabs = False
+        self.ignoreExprs = list()
+        self.debug = False
+        self.streamlined = False
+        self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index
+        self.errmsg = ""
+        self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all)
+        self.debugActions = ( None, None, None ) #custom debug actions
+        self.re = None
+        self.callPreparse = True # used to avoid redundant calls to preParse
+        self.callDuringTry = False
+
+    def copy( self ):
+        """Make a copy of this C{ParserElement}.  Useful for defining different parse actions
+           for the same parsing pattern, using copies of the original parse element."""
+        cpy = copy.copy( self )
+        cpy.parseAction = self.parseAction[:]
+        cpy.ignoreExprs = self.ignoreExprs[:]
+        if self.copyDefaultWhiteChars:
+            cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
+        return cpy
+
+    def setName( self, name ):
+        """Define name for this expression, for use in debugging."""
+        self.name = name
+        self.errmsg = "Expected " + self.name
+        if hasattr(self,"exception"):
+            self.exception.msg = self.errmsg
+        return self
+
+    def setResultsName( self, name, listAllMatches=False ):
+        """Define name for referencing matching tokens as a nested attribute
+           of the returned parse results.
+           NOTE: this returns a *copy* of the original C{ParserElement} object;
+           this is so that the client can define a basic element, such as an
+           integer, and reference it in multiple places with different names.
+           
+           You can also set results names using the abbreviated syntax,
+           C{expr("name")} in place of C{expr.setResultsName("name")} - 
+           see L{I{__call__}<__call__>}.
+        """
+        newself = self.copy()
+        if name.endswith("*"):
+            name = name[:-1]
+            listAllMatches=True
+        newself.resultsName = name
+        newself.modalResults = not listAllMatches
+        return newself
+
+    def setBreak(self,breakFlag = True):
+        """Method to invoke the Python pdb debugger when this element is
+           about to be parsed. Set C{breakFlag} to True to enable, False to
+           disable.
+        """
+        if breakFlag:
+            _parseMethod = self._parse
+            def breaker(instring, loc, doActions=True, callPreParse=True):
+                import pdb
+                pdb.set_trace()
+                return _parseMethod( instring, loc, doActions, callPreParse )
+            breaker._originalParseMethod = _parseMethod
+            self._parse = breaker
+        else:
+            if hasattr(self._parse,"_originalParseMethod"):
+                self._parse = self._parse._originalParseMethod
+        return self
+
+    def setParseAction( self, *fns, **kwargs ):
+        """Define action to perform when successfully matching parse element definition.
+           Parse action fn is a callable method with 0-3 arguments, called as C{fn(s,loc,toks)},
+           C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where:
+            - s   = the original string being parsed (see note below)
+            - loc = the location of the matching substring
+            - toks = a list of the matched tokens, packaged as a C{L{ParseResults}} object
+           If the functions in fns modify the tokens, they can return them as the return
+           value from fn, and the modified list of tokens will replace the original.
+           Otherwise, fn does not need to return any value.
+
+           Note: the default parsing behavior is to expand tabs in the input string
+           before starting the parsing process.  See L{I{parseString}} for more information
+           on parsing strings containing C{}s, and suggested methods to maintain a
+           consistent view of the parsed string, the parse location, and line and column
+           positions within the parsed string.
+           """
+        self.parseAction = list(map(_trim_arity, list(fns)))
+        self.callDuringTry = kwargs.get("callDuringTry", False)
+        return self
+
+    def addParseAction( self, *fns, **kwargs ):
+        """Add parse action to expression's list of parse actions. See L{I{setParseAction}}."""
+        self.parseAction += list(map(_trim_arity, list(fns)))
+        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
+        return self
+
+    def addCondition(self, *fns, **kwargs):
+        """Add a boolean predicate function to expression's list of parse actions. See 
+        L{I{setParseAction}}. Optional keyword argument C{message} can
+        be used to define a custom message to be used in the raised exception."""
+        msg = kwargs.get("message") or "failed user-defined condition"
+        for fn in fns:
+            def pa(s,l,t):
+                if not bool(_trim_arity(fn)(s,l,t)):
+                    raise ParseException(s,l,msg)
+                return t
+            self.parseAction.append(pa)
+        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
+        return self
+
+    def setFailAction( self, fn ):
+        """Define action to perform if parsing fails at this expression.
+           Fail acton fn is a callable function that takes the arguments
+           C{fn(s,loc,expr,err)} where:
+            - s = string being parsed
+            - loc = location where expression match was attempted and failed
+            - expr = the parse expression that failed
+            - err = the exception thrown
+           The function returns no value.  It may throw C{L{ParseFatalException}}
+           if it is desired to stop parsing immediately."""
+        self.failAction = fn
+        return self
+
+    def _skipIgnorables( self, instring, loc ):
+        exprsFound = True
+        while exprsFound:
+            exprsFound = False
+            for e in self.ignoreExprs:
+                try:
+                    while 1:
+                        loc,dummy = e._parse( instring, loc )
+                        exprsFound = True
+                except ParseException:
+                    pass
+        return loc
+
+    def preParse( self, instring, loc ):
+        if self.ignoreExprs:
+            loc = self._skipIgnorables( instring, loc )
+
+        if self.skipWhitespace:
+            wt = self.whiteChars
+            instrlen = len(instring)
+            while loc < instrlen and instring[loc] in wt:
+                loc += 1
+
+        return loc
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        return loc, []
+
+    def postParse( self, instring, loc, tokenlist ):
+        return tokenlist
+
+    #~ @profile
+    def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ):
+        debugging = ( self.debug ) #and doActions )
+
+        if debugging or self.failAction:
+            #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) ))
+            if (self.debugActions[0] ):
+                self.debugActions[0]( instring, loc, self )
+            if callPreParse and self.callPreparse:
+                preloc = self.preParse( instring, loc )
+            else:
+                preloc = loc
+            tokensStart = preloc
+            try:
+                try:
+                    loc,tokens = self.parseImpl( instring, preloc, doActions )
+                except IndexError:
+                    raise ParseException( instring, len(instring), self.errmsg, self )
+            except ParseBaseException as err:
+                #~ print ("Exception raised:", err)
+                if self.debugActions[2]:
+                    self.debugActions[2]( instring, tokensStart, self, err )
+                if self.failAction:
+                    self.failAction( instring, tokensStart, self, err )
+                raise
+        else:
+            if callPreParse and self.callPreparse:
+                preloc = self.preParse( instring, loc )
+            else:
+                preloc = loc
+            tokensStart = preloc
+            if self.mayIndexError or loc >= len(instring):
+                try:
+                    loc,tokens = self.parseImpl( instring, preloc, doActions )
+                except IndexError:
+                    raise ParseException( instring, len(instring), self.errmsg, self )
+            else:
+                loc,tokens = self.parseImpl( instring, preloc, doActions )
+
+        tokens = self.postParse( instring, loc, tokens )
+
+        retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults )
+        if self.parseAction and (doActions or self.callDuringTry):
+            if debugging:
+                try:
+                    for fn in self.parseAction:
+                        tokens = fn( instring, tokensStart, retTokens )
+                        if tokens is not None:
+                            retTokens = ParseResults( tokens,
+                                                      self.resultsName,
+                                                      asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
+                                                      modal=self.modalResults )
+                except ParseBaseException as err:
+                    #~ print "Exception raised in user parse action:", err
+                    if (self.debugActions[2] ):
+                        self.debugActions[2]( instring, tokensStart, self, err )
+                    raise
+            else:
+                for fn in self.parseAction:
+                    tokens = fn( instring, tokensStart, retTokens )
+                    if tokens is not None:
+                        retTokens = ParseResults( tokens,
+                                                  self.resultsName,
+                                                  asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
+                                                  modal=self.modalResults )
+
+        if debugging:
+            #~ print ("Matched",self,"->",retTokens.asList())
+            if (self.debugActions[1] ):
+                self.debugActions[1]( instring, tokensStart, loc, self, retTokens )
+
+        return loc, retTokens
+
+    def tryParse( self, instring, loc ):
+        try:
+            return self._parse( instring, loc, doActions=False )[0]
+        except ParseFatalException:
+            raise ParseException( instring, loc, self.errmsg, self)
+    
+    def canParseNext(self, instring, loc):
+        try:
+            self.tryParse(instring, loc)
+        except (ParseException, IndexError):
+            return False
+        else:
+            return True
+
+    # this method gets repeatedly called during backtracking with the same arguments -
+    # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression
+    def _parseCache( self, instring, loc, doActions=True, callPreParse=True ):
+        lookup = (self,instring,loc,callPreParse,doActions)
+        if lookup in ParserElement._exprArgCache:
+            value = ParserElement._exprArgCache[ lookup ]
+            if isinstance(value, Exception):
+                raise value
+            return (value[0],value[1].copy())
+        else:
+            try:
+                value = self._parseNoCache( instring, loc, doActions, callPreParse )
+                ParserElement._exprArgCache[ lookup ] = (value[0],value[1].copy())
+                return value
+            except ParseBaseException as pe:
+                pe.__traceback__ = None
+                ParserElement._exprArgCache[ lookup ] = pe
+                raise
+
+    _parse = _parseNoCache
+
+    # argument cache for optimizing repeated calls when backtracking through recursive expressions
+    _exprArgCache = {}
+    @staticmethod
+    def resetCache():
+        ParserElement._exprArgCache.clear()
+
+    _packratEnabled = False
+    @staticmethod
+    def enablePackrat():
+        """Enables "packrat" parsing, which adds memoizing to the parsing logic.
+           Repeated parse attempts at the same string location (which happens
+           often in many complex grammars) can immediately return a cached value,
+           instead of re-executing parsing/validating code.  Memoizing is done of
+           both valid results and parsing exceptions.
+
+           This speedup may break existing programs that use parse actions that
+           have side-effects.  For this reason, packrat parsing is disabled when
+           you first import pyparsing.  To activate the packrat feature, your
+           program must call the class method C{ParserElement.enablePackrat()}.  If
+           your program uses C{psyco} to "compile as you go", you must call
+           C{enablePackrat} before calling C{psyco.full()}.  If you do not do this,
+           Python will crash.  For best results, call C{enablePackrat()} immediately
+           after importing pyparsing.
+        """
+        if not ParserElement._packratEnabled:
+            ParserElement._packratEnabled = True
+            ParserElement._parse = ParserElement._parseCache
+
+    def parseString( self, instring, parseAll=False ):
+        """Execute the parse expression with the given string.
+           This is the main interface to the client code, once the complete
+           expression has been built.
+
+           If you want the grammar to require that the entire input string be
+           successfully parsed, then set C{parseAll} to True (equivalent to ending
+           the grammar with C{L{StringEnd()}}).
+
+           Note: C{parseString} implicitly calls C{expandtabs()} on the input string,
+           in order to report proper column numbers in parse actions.
+           If the input string contains tabs and
+           the grammar uses parse actions that use the C{loc} argument to index into the
+           string being parsed, you can ensure you have a consistent view of the input
+           string by:
+            - calling C{parseWithTabs} on your grammar before calling C{parseString}
+              (see L{I{parseWithTabs}})
+            - define your parse action using the full C{(s,loc,toks)} signature, and
+              reference the input string using the parse action's C{s} argument
+            - explictly expand the tabs in your input string before calling
+              C{parseString}
+        """
+        ParserElement.resetCache()
+        if not self.streamlined:
+            self.streamline()
+            #~ self.saveAsList = True
+        for e in self.ignoreExprs:
+            e.streamline()
+        if not self.keepTabs:
+            instring = instring.expandtabs()
+        try:
+            loc, tokens = self._parse( instring, 0 )
+            if parseAll:
+                loc = self.preParse( instring, loc )
+                se = Empty() + StringEnd()
+                se._parse( instring, loc )
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+        else:
+            return tokens
+
+    def scanString( self, instring, maxMatches=_MAX_INT, overlap=False ):
+        """Scan the input string for expression matches.  Each match will return the
+           matching tokens, start location, and end location.  May be called with optional
+           C{maxMatches} argument, to clip scanning after 'n' matches are found.  If
+           C{overlap} is specified, then overlapping matches will be reported.
+
+           Note that the start and end locations are reported relative to the string
+           being parsed.  See L{I{parseString}} for more information on parsing
+           strings with embedded tabs."""
+        if not self.streamlined:
+            self.streamline()
+        for e in self.ignoreExprs:
+            e.streamline()
+
+        if not self.keepTabs:
+            instring = _ustr(instring).expandtabs()
+        instrlen = len(instring)
+        loc = 0
+        preparseFn = self.preParse
+        parseFn = self._parse
+        ParserElement.resetCache()
+        matches = 0
+        try:
+            while loc <= instrlen and matches < maxMatches:
+                try:
+                    preloc = preparseFn( instring, loc )
+                    nextLoc,tokens = parseFn( instring, preloc, callPreParse=False )
+                except ParseException:
+                    loc = preloc+1
+                else:
+                    if nextLoc > loc:
+                        matches += 1
+                        yield tokens, preloc, nextLoc
+                        if overlap:
+                            nextloc = preparseFn( instring, loc )
+                            if nextloc > loc:
+                                loc = nextLoc
+                            else:
+                                loc += 1
+                        else:
+                            loc = nextLoc
+                    else:
+                        loc = preloc+1
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def transformString( self, instring ):
+        """Extension to C{L{scanString}}, to modify matching text with modified tokens that may
+           be returned from a parse action.  To use C{transformString}, define a grammar and
+           attach a parse action to it that modifies the returned token list.
+           Invoking C{transformString()} on a target string will then scan for matches,
+           and replace the matched text patterns according to the logic in the parse
+           action.  C{transformString()} returns the resulting transformed string."""
+        out = []
+        lastE = 0
+        # force preservation of s, to minimize unwanted transformation of string, and to
+        # keep string locs straight between transformString and scanString
+        self.keepTabs = True
+        try:
+            for t,s,e in self.scanString( instring ):
+                out.append( instring[lastE:s] )
+                if t:
+                    if isinstance(t,ParseResults):
+                        out += t.asList()
+                    elif isinstance(t,list):
+                        out += t
+                    else:
+                        out.append(t)
+                lastE = e
+            out.append(instring[lastE:])
+            out = [o for o in out if o]
+            return "".join(map(_ustr,_flatten(out)))
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def searchString( self, instring, maxMatches=_MAX_INT ):
+        """Another extension to C{L{scanString}}, simplifying the access to the tokens found
+           to match the given parse expression.  May be called with optional
+           C{maxMatches} argument, to clip searching after 'n' matches are found.
+        """
+        try:
+            return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ])
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def __add__(self, other ):
+        """Implementation of + operator - returns C{L{And}}"""
+        if isinstance( other, basestring ):
+            other = ParserElement.literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return And( [ self, other ] )
+
+    def __radd__(self, other ):
+        """Implementation of + operator when left operand is not a C{L{ParserElement}}"""
+        if isinstance( other, basestring ):
+            other = ParserElement.literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other + self
+
+    def __sub__(self, other):
+        """Implementation of - operator, returns C{L{And}} with error stop"""
+        if isinstance( other, basestring ):
+            other = ParserElement.literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return And( [ self, And._ErrorStop(), other ] )
+
+    def __rsub__(self, other ):
+        """Implementation of - operator when left operand is not a C{L{ParserElement}}"""
+        if isinstance( other, basestring ):
+            other = ParserElement.literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other - self
+
+    def __mul__(self,other):
+        """Implementation of * operator, allows use of C{expr * 3} in place of
+           C{expr + expr + expr}.  Expressions may also me multiplied by a 2-integer
+           tuple, similar to C{{min,max}} multipliers in regular expressions.  Tuples
+           may also include C{None} as in:
+            - C{expr*(n,None)} or C{expr*(n,)} is equivalent
+              to C{expr*n + L{ZeroOrMore}(expr)}
+              (read as "at least n instances of C{expr}")
+            - C{expr*(None,n)} is equivalent to C{expr*(0,n)}
+              (read as "0 to n instances of C{expr}")
+            - C{expr*(None,None)} is equivalent to C{L{ZeroOrMore}(expr)}
+            - C{expr*(1,None)} is equivalent to C{L{OneOrMore}(expr)}
+
+           Note that C{expr*(None,n)} does not raise an exception if
+           more than n exprs exist in the input stream; that is,
+           C{expr*(None,n)} does not enforce a maximum number of expr
+           occurrences.  If this behavior is desired, then write
+           C{expr*(None,n) + ~expr}
+
+        """
+        if isinstance(other,int):
+            minElements, optElements = other,0
+        elif isinstance(other,tuple):
+            other = (other + (None, None))[:2]
+            if other[0] is None:
+                other = (0, other[1])
+            if isinstance(other[0],int) and other[1] is None:
+                if other[0] == 0:
+                    return ZeroOrMore(self)
+                if other[0] == 1:
+                    return OneOrMore(self)
+                else:
+                    return self*other[0] + ZeroOrMore(self)
+            elif isinstance(other[0],int) and isinstance(other[1],int):
+                minElements, optElements = other
+                optElements -= minElements
+            else:
+                raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1]))
+        else:
+            raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other))
+
+        if minElements < 0:
+            raise ValueError("cannot multiply ParserElement by negative value")
+        if optElements < 0:
+            raise ValueError("second tuple value must be greater or equal to first tuple value")
+        if minElements == optElements == 0:
+            raise ValueError("cannot multiply ParserElement by 0 or (0,0)")
+
+        if (optElements):
+            def makeOptionalList(n):
+                if n>1:
+                    return Optional(self + makeOptionalList(n-1))
+                else:
+                    return Optional(self)
+            if minElements:
+                if minElements == 1:
+                    ret = self + makeOptionalList(optElements)
+                else:
+                    ret = And([self]*minElements) + makeOptionalList(optElements)
+            else:
+                ret = makeOptionalList(optElements)
+        else:
+            if minElements == 1:
+                ret = self
+            else:
+                ret = And([self]*minElements)
+        return ret
+
+    def __rmul__(self, other):
+        return self.__mul__(other)
+
+    def __or__(self, other ):
+        """Implementation of | operator - returns C{L{MatchFirst}}"""
+        if isinstance( other, basestring ):
+            other = ParserElement.literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return MatchFirst( [ self, other ] )
+
+    def __ror__(self, other ):
+        """Implementation of | operator when left operand is not a C{L{ParserElement}}"""
+        if isinstance( other, basestring ):
+            other = ParserElement.literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other | self
+
+    def __xor__(self, other ):
+        """Implementation of ^ operator - returns C{L{Or}}"""
+        if isinstance( other, basestring ):
+            other = ParserElement.literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return Or( [ self, other ] )
+
+    def __rxor__(self, other ):
+        """Implementation of ^ operator when left operand is not a C{L{ParserElement}}"""
+        if isinstance( other, basestring ):
+            other = ParserElement.literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other ^ self
+
+    def __and__(self, other ):
+        """Implementation of & operator - returns C{L{Each}}"""
+        if isinstance( other, basestring ):
+            other = ParserElement.literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return Each( [ self, other ] )
+
+    def __rand__(self, other ):
+        """Implementation of & operator when left operand is not a C{L{ParserElement}}"""
+        if isinstance( other, basestring ):
+            other = ParserElement.literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other & self
+
+    def __invert__( self ):
+        """Implementation of ~ operator - returns C{L{NotAny}}"""
+        return NotAny( self )
+
+    def __call__(self, name=None):
+        """Shortcut for C{L{setResultsName}}, with C{listAllMatches=default}::
+             userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno")
+           could be written as::
+             userdata = Word(alphas)("name") + Word(nums+"-")("socsecno")
+             
+           If C{name} is given with a trailing C{'*'} character, then C{listAllMatches} will be
+           passed as C{True}.
+           
+           If C{name} is omitted, same as calling C{L{copy}}.
+           """
+        if name is not None:
+            return self.setResultsName(name)
+        else:
+            return self.copy()
+
+    def suppress( self ):
+        """Suppresses the output of this C{ParserElement}; useful to keep punctuation from
+           cluttering up returned output.
+        """
+        return Suppress( self )
+
+    def leaveWhitespace( self ):
+        """Disables the skipping of whitespace before matching the characters in the
+           C{ParserElement}'s defined pattern.  This is normally only used internally by
+           the pyparsing module, but may be needed in some whitespace-sensitive grammars.
+        """
+        self.skipWhitespace = False
+        return self
+
+    def setWhitespaceChars( self, chars ):
+        """Overrides the default whitespace chars
+        """
+        self.skipWhitespace = True
+        self.whiteChars = chars
+        self.copyDefaultWhiteChars = False
+        return self
+
+    def parseWithTabs( self ):
+        """Overrides default behavior to expand C{}s to spaces before parsing the input string.
+           Must be called before C{parseString} when the input grammar contains elements that
+           match C{} characters."""
+        self.keepTabs = True
+        return self
+
+    def ignore( self, other ):
+        """Define expression to be ignored (e.g., comments) while doing pattern
+           matching; may be called repeatedly, to define multiple comment or other
+           ignorable patterns.
+        """
+        if isinstance(other, basestring):
+            other = Suppress(other)
+
+        if isinstance( other, Suppress ):
+            if other not in self.ignoreExprs:
+                self.ignoreExprs.append(other)
+        else:
+            self.ignoreExprs.append( Suppress( other.copy() ) )
+        return self
+
+    def setDebugActions( self, startAction, successAction, exceptionAction ):
+        """Enable display of debugging messages while doing pattern matching."""
+        self.debugActions = (startAction or _defaultStartDebugAction,
+                             successAction or _defaultSuccessDebugAction,
+                             exceptionAction or _defaultExceptionDebugAction)
+        self.debug = True
+        return self
+
+    def setDebug( self, flag=True ):
+        """Enable display of debugging messages while doing pattern matching.
+           Set C{flag} to True to enable, False to disable."""
+        if flag:
+            self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction )
+        else:
+            self.debug = False
+        return self
+
+    def __str__( self ):
+        return self.name
+
+    def __repr__( self ):
+        return _ustr(self)
+
+    def streamline( self ):
+        self.streamlined = True
+        self.strRepr = None
+        return self
+
+    def checkRecursion( self, parseElementList ):
+        pass
+
+    def validate( self, validateTrace=[] ):
+        """Check defined expressions for valid structure, check for infinite recursive definitions."""
+        self.checkRecursion( [] )
+
+    def parseFile( self, file_or_filename, parseAll=False ):
+        """Execute the parse expression on the given file or filename.
+           If a filename is specified (instead of a file object),
+           the entire file is opened, read, and closed before parsing.
+        """
+        try:
+            file_contents = file_or_filename.read()
+        except AttributeError:
+            f = open(file_or_filename, "r")
+            file_contents = f.read()
+            f.close()
+        try:
+            return self.parseString(file_contents, parseAll)
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def __eq__(self,other):
+        if isinstance(other, ParserElement):
+            return self is other or vars(self) == vars(other)
+        elif isinstance(other, basestring):
+            try:
+                self.parseString(_ustr(other), parseAll=True)
+                return True
+            except ParseBaseException:
+                return False
+        else:
+            return super(ParserElement,self)==other
+
+    def __ne__(self,other):
+        return not (self == other)
+
+    def __hash__(self):
+        return hash(id(self))
+
+    def __req__(self,other):
+        return self == other
+
+    def __rne__(self,other):
+        return not (self == other)
+
+    def runTests(self, tests, parseAll=False):
+        """Execute the parse expression on a series of test strings, showing each
+           test, the parsed results or where the parse failed. Quick and easy way to
+           run a parse expression against a list of sample strings.
+           
+           Parameters:
+            - tests - a list of separate test strings, or a multiline string of test strings
+            - parseAll - (default=False) - flag to pass to C{L{parseString}} when running tests           
+        """
+        if isinstance(tests, basestring):
+            tests = map(str.strip, tests.splitlines())
+        for t in tests:
+            out = [t]
+            try:
+                out.append(self.parseString(t, parseAll=parseAll).dump())
+            except ParseException as pe:
+                if '\n' in t:
+                    out.append(line(pe.loc, t))
+                    out.append(' '*(col(pe.loc,t)-1) + '^')
+                else:
+                    out.append(' '*pe.loc + '^')
+                out.append(str(pe))
+            out.append('')
+            print('\n'.join(out))
+
+        
+class Token(ParserElement):
+    """Abstract C{ParserElement} subclass, for defining atomic matching patterns."""
+    def __init__( self ):
+        super(Token,self).__init__( savelist=False )
+
+
+class Empty(Token):
+    """An empty token, will always match."""
+    def __init__( self ):
+        super(Empty,self).__init__()
+        self.name = "Empty"
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+
+
+class NoMatch(Token):
+    """A token that will never match."""
+    def __init__( self ):
+        super(NoMatch,self).__init__()
+        self.name = "NoMatch"
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+        self.errmsg = "Unmatchable token"
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        raise ParseException(instring, loc, self.errmsg, self)
+
+
+class Literal(Token):
+    """Token to exactly match a specified string."""
+    def __init__( self, matchString ):
+        super(Literal,self).__init__()
+        self.match = matchString
+        self.matchLen = len(matchString)
+        try:
+            self.firstMatchChar = matchString[0]
+        except IndexError:
+            warnings.warn("null string passed to Literal; use Empty() instead",
+                            SyntaxWarning, stacklevel=2)
+            self.__class__ = Empty
+        self.name = '"%s"' % _ustr(self.match)
+        self.errmsg = "Expected " + self.name
+        self.mayReturnEmpty = False
+        self.mayIndexError = False
+
+    # Performance tuning: this routine gets called a *lot*
+    # if this is a single character match string  and the first character matches,
+    # short-circuit as quickly as possible, and avoid calling startswith
+    #~ @profile
+    def parseImpl( self, instring, loc, doActions=True ):
+        if (instring[loc] == self.firstMatchChar and
+            (self.matchLen==1 or instring.startswith(self.match,loc)) ):
+            return loc+self.matchLen, self.match
+        raise ParseException(instring, loc, self.errmsg, self)
+_L = Literal
+ParserElement.literalStringClass = Literal
+
+class Keyword(Token):
+    """Token to exactly match a specified string as a keyword, that is, it must be
+       immediately followed by a non-keyword character.  Compare with C{L{Literal}}::
+         Literal("if") will match the leading C{'if'} in C{'ifAndOnlyIf'}.
+         Keyword("if") will not; it will only match the leading C{'if'} in C{'if x=1'}, or C{'if(y==2)'}
+       Accepts two optional constructor arguments in addition to the keyword string:
+       C{identChars} is a string of characters that would be valid identifier characters,
+       defaulting to all alphanumerics + "_" and "$"; C{caseless} allows case-insensitive
+       matching, default is C{False}.
+    """
+    DEFAULT_KEYWORD_CHARS = alphanums+"_$"
+
+    def __init__( self, matchString, identChars=DEFAULT_KEYWORD_CHARS, caseless=False ):
+        super(Keyword,self).__init__()
+        self.match = matchString
+        self.matchLen = len(matchString)
+        try:
+            self.firstMatchChar = matchString[0]
+        except IndexError:
+            warnings.warn("null string passed to Keyword; use Empty() instead",
+                            SyntaxWarning, stacklevel=2)
+        self.name = '"%s"' % self.match
+        self.errmsg = "Expected " + self.name
+        self.mayReturnEmpty = False
+        self.mayIndexError = False
+        self.caseless = caseless
+        if caseless:
+            self.caselessmatch = matchString.upper()
+            identChars = identChars.upper()
+        self.identChars = set(identChars)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.caseless:
+            if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
+                 (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and
+                 (loc == 0 or instring[loc-1].upper() not in self.identChars) ):
+                return loc+self.matchLen, self.match
+        else:
+            if (instring[loc] == self.firstMatchChar and
+                (self.matchLen==1 or instring.startswith(self.match,loc)) and
+                (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and
+                (loc == 0 or instring[loc-1] not in self.identChars) ):
+                return loc+self.matchLen, self.match
+        raise ParseException(instring, loc, self.errmsg, self)
+
+    def copy(self):
+        c = super(Keyword,self).copy()
+        c.identChars = Keyword.DEFAULT_KEYWORD_CHARS
+        return c
+
+    @staticmethod
+    def setDefaultKeywordChars( chars ):
+        """Overrides the default Keyword chars
+        """
+        Keyword.DEFAULT_KEYWORD_CHARS = chars
+
+class CaselessLiteral(Literal):
+    """Token to match a specified string, ignoring case of letters.
+       Note: the matched results will always be in the case of the given
+       match string, NOT the case of the input text.
+    """
+    def __init__( self, matchString ):
+        super(CaselessLiteral,self).__init__( matchString.upper() )
+        # Preserve the defining literal.
+        self.returnString = matchString
+        self.name = "'%s'" % self.returnString
+        self.errmsg = "Expected " + self.name
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if instring[ loc:loc+self.matchLen ].upper() == self.match:
+            return loc+self.matchLen, self.returnString
+        raise ParseException(instring, loc, self.errmsg, self)
+
+class CaselessKeyword(Keyword):
+    def __init__( self, matchString, identChars=Keyword.DEFAULT_KEYWORD_CHARS ):
+        super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True )
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
+             (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ):
+            return loc+self.matchLen, self.match
+        raise ParseException(instring, loc, self.errmsg, self)
+
+class Word(Token):
+    """Token for matching words composed of allowed character sets.
+       Defined with string containing all allowed initial characters,
+       an optional string containing allowed body characters (if omitted,
+       defaults to the initial character set), and an optional minimum,
+       maximum, and/or exact length.  The default value for C{min} is 1 (a
+       minimum value < 1 is not valid); the default values for C{max} and C{exact}
+       are 0, meaning no maximum or exact length restriction. An optional
+       C{excludeChars} parameter can list characters that might be found in 
+       the input C{bodyChars} string; useful to define a word of all printables
+       except for one or two characters, for instance.
+    """
+    def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False, excludeChars=None ):
+        super(Word,self).__init__()
+        if excludeChars:
+            initChars = ''.join(c for c in initChars if c not in excludeChars)
+            if bodyChars:
+                bodyChars = ''.join(c for c in bodyChars if c not in excludeChars)
+        self.initCharsOrig = initChars
+        self.initChars = set(initChars)
+        if bodyChars :
+            self.bodyCharsOrig = bodyChars
+            self.bodyChars = set(bodyChars)
+        else:
+            self.bodyCharsOrig = initChars
+            self.bodyChars = set(initChars)
+
+        self.maxSpecified = max > 0
+
+        if min < 1:
+            raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted")
+
+        self.minLen = min
+
+        if max > 0:
+            self.maxLen = max
+        else:
+            self.maxLen = _MAX_INT
+
+        if exact > 0:
+            self.maxLen = exact
+            self.minLen = exact
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayIndexError = False
+        self.asKeyword = asKeyword
+
+        if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0):
+            if self.bodyCharsOrig == self.initCharsOrig:
+                self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig)
+            elif len(self.initCharsOrig) == 1:
+                self.reString = "%s[%s]*" % \
+                                      (re.escape(self.initCharsOrig),
+                                      _escapeRegexRangeChars(self.bodyCharsOrig),)
+            else:
+                self.reString = "[%s][%s]*" % \
+                                      (_escapeRegexRangeChars(self.initCharsOrig),
+                                      _escapeRegexRangeChars(self.bodyCharsOrig),)
+            if self.asKeyword:
+                self.reString = r"\b"+self.reString+r"\b"
+            try:
+                self.re = re.compile( self.reString )
+            except:
+                self.re = None
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.re:
+            result = self.re.match(instring,loc)
+            if not result:
+                raise ParseException(instring, loc, self.errmsg, self)
+
+            loc = result.end()
+            return loc, result.group()
+
+        if not(instring[ loc ] in self.initChars):
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        start = loc
+        loc += 1
+        instrlen = len(instring)
+        bodychars = self.bodyChars
+        maxloc = start + self.maxLen
+        maxloc = min( maxloc, instrlen )
+        while loc < maxloc and instring[loc] in bodychars:
+            loc += 1
+
+        throwException = False
+        if loc - start < self.minLen:
+            throwException = True
+        if self.maxSpecified and loc < instrlen and instring[loc] in bodychars:
+            throwException = True
+        if self.asKeyword:
+            if (start>0 and instring[start-1] in bodychars) or (loc4:
+                    return s[:4]+"..."
+                else:
+                    return s
+
+            if ( self.initCharsOrig != self.bodyCharsOrig ):
+                self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) )
+            else:
+                self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig)
+
+        return self.strRepr
+
+
+class Regex(Token):
+    """Token for matching strings that match a given regular expression.
+       Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module.
+    """
+    compiledREtype = type(re.compile("[A-Z]"))
+    def __init__( self, pattern, flags=0):
+        """The parameters C{pattern} and C{flags} are passed to the C{re.compile()} function as-is. See the Python C{re} module for an explanation of the acceptable patterns and flags."""
+        super(Regex,self).__init__()
+
+        if isinstance(pattern, basestring):
+            if not pattern:
+                warnings.warn("null string passed to Regex; use Empty() instead",
+                        SyntaxWarning, stacklevel=2)
+
+            self.pattern = pattern
+            self.flags = flags
+
+            try:
+                self.re = re.compile(self.pattern, self.flags)
+                self.reString = self.pattern
+            except sre_constants.error:
+                warnings.warn("invalid pattern (%s) passed to Regex" % pattern,
+                    SyntaxWarning, stacklevel=2)
+                raise
+
+        elif isinstance(pattern, Regex.compiledREtype):
+            self.re = pattern
+            self.pattern = \
+            self.reString = str(pattern)
+            self.flags = flags
+            
+        else:
+            raise ValueError("Regex may only be constructed with a string or a compiled RE object")
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayIndexError = False
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        result = self.re.match(instring,loc)
+        if not result:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        loc = result.end()
+        d = result.groupdict()
+        ret = ParseResults(result.group())
+        if d:
+            for k in d:
+                ret[k] = d[k]
+        return loc,ret
+
+    def __str__( self ):
+        try:
+            return super(Regex,self).__str__()
+        except:
+            pass
+
+        if self.strRepr is None:
+            self.strRepr = "Re:(%s)" % repr(self.pattern)
+
+        return self.strRepr
+
+
+class QuotedString(Token):
+    """Token for matching strings that are delimited by quoting characters.
+    """
+    def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None, convertWhitespaceEscapes=True):
+        r"""Defined with the following parameters:
+            - quoteChar - string of one or more characters defining the quote delimiting string
+            - escChar - character to escape quotes, typically backslash (default=None)
+            - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=None)
+            - multiline - boolean indicating whether quotes can span multiple lines (default=C{False})
+            - unquoteResults - boolean indicating whether the matched text should be unquoted (default=C{True})
+            - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=C{None} => same as quoteChar)
+            - convertWhitespaceEscapes - convert escaped whitespace (C{'\t'}, C{'\n'}, etc.) to actual whitespace (default=C{True})
+        """
+        super(QuotedString,self).__init__()
+
+        # remove white space from quote chars - wont work anyway
+        quoteChar = quoteChar.strip()
+        if not quoteChar:
+            warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
+            raise SyntaxError()
+
+        if endQuoteChar is None:
+            endQuoteChar = quoteChar
+        else:
+            endQuoteChar = endQuoteChar.strip()
+            if not endQuoteChar:
+                warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
+                raise SyntaxError()
+
+        self.quoteChar = quoteChar
+        self.quoteCharLen = len(quoteChar)
+        self.firstQuoteChar = quoteChar[0]
+        self.endQuoteChar = endQuoteChar
+        self.endQuoteCharLen = len(endQuoteChar)
+        self.escChar = escChar
+        self.escQuote = escQuote
+        self.unquoteResults = unquoteResults
+        self.convertWhitespaceEscapes = convertWhitespaceEscapes
+
+        if multiline:
+            self.flags = re.MULTILINE | re.DOTALL
+            self.pattern = r'%s(?:[^%s%s]' % \
+                ( re.escape(self.quoteChar),
+                  _escapeRegexRangeChars(self.endQuoteChar[0]),
+                  (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
+        else:
+            self.flags = 0
+            self.pattern = r'%s(?:[^%s\n\r%s]' % \
+                ( re.escape(self.quoteChar),
+                  _escapeRegexRangeChars(self.endQuoteChar[0]),
+                  (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
+        if len(self.endQuoteChar) > 1:
+            self.pattern += (
+                '|(?:' + ')|(?:'.join("%s[^%s]" % (re.escape(self.endQuoteChar[:i]),
+                                               _escapeRegexRangeChars(self.endQuoteChar[i]))
+                                    for i in range(len(self.endQuoteChar)-1,0,-1)) + ')'
+                )
+        if escQuote:
+            self.pattern += (r'|(?:%s)' % re.escape(escQuote))
+        if escChar:
+            self.pattern += (r'|(?:%s.)' % re.escape(escChar))
+            self.escCharReplacePattern = re.escape(self.escChar)+"(.)"
+        self.pattern += (r')*%s' % re.escape(self.endQuoteChar))
+
+        try:
+            self.re = re.compile(self.pattern, self.flags)
+            self.reString = self.pattern
+        except sre_constants.error:
+            warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern,
+                SyntaxWarning, stacklevel=2)
+            raise
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayIndexError = False
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None
+        if not result:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        loc = result.end()
+        ret = result.group()
+
+        if self.unquoteResults:
+
+            # strip off quotes
+            ret = ret[self.quoteCharLen:-self.endQuoteCharLen]
+
+            if isinstance(ret,basestring):
+                # replace escaped whitespace
+                if '\\' in ret and self.convertWhitespaceEscapes:
+                    ws_map = {
+                        r'\t' : '\t',
+                        r'\n' : '\n',
+                        r'\f' : '\f',
+                        r'\r' : '\r',
+                    }
+                    for wslit,wschar in ws_map.items():
+                        ret = ret.replace(wslit, wschar)
+
+                # replace escaped characters
+                if self.escChar:
+                    ret = re.sub(self.escCharReplacePattern,"\g<1>",ret)
+
+                # replace escaped quotes
+                if self.escQuote:
+                    ret = ret.replace(self.escQuote, self.endQuoteChar)
+
+        return loc, ret
+
+    def __str__( self ):
+        try:
+            return super(QuotedString,self).__str__()
+        except:
+            pass
+
+        if self.strRepr is None:
+            self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar)
+
+        return self.strRepr
+
+
+class CharsNotIn(Token):
+    """Token for matching words composed of characters *not* in a given set.
+       Defined with string containing all disallowed characters, and an optional
+       minimum, maximum, and/or exact length.  The default value for C{min} is 1 (a
+       minimum value < 1 is not valid); the default values for C{max} and C{exact}
+       are 0, meaning no maximum or exact length restriction.
+    """
+    def __init__( self, notChars, min=1, max=0, exact=0 ):
+        super(CharsNotIn,self).__init__()
+        self.skipWhitespace = False
+        self.notChars = notChars
+
+        if min < 1:
+            raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted")
+
+        self.minLen = min
+
+        if max > 0:
+            self.maxLen = max
+        else:
+            self.maxLen = _MAX_INT
+
+        if exact > 0:
+            self.maxLen = exact
+            self.minLen = exact
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayReturnEmpty = ( self.minLen == 0 )
+        self.mayIndexError = False
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if instring[loc] in self.notChars:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        start = loc
+        loc += 1
+        notchars = self.notChars
+        maxlen = min( start+self.maxLen, len(instring) )
+        while loc < maxlen and \
+              (instring[loc] not in notchars):
+            loc += 1
+
+        if loc - start < self.minLen:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        return loc, instring[start:loc]
+
+    def __str__( self ):
+        try:
+            return super(CharsNotIn, self).__str__()
+        except:
+            pass
+
+        if self.strRepr is None:
+            if len(self.notChars) > 4:
+                self.strRepr = "!W:(%s...)" % self.notChars[:4]
+            else:
+                self.strRepr = "!W:(%s)" % self.notChars
+
+        return self.strRepr
+
+class White(Token):
+    """Special matching class for matching whitespace.  Normally, whitespace is ignored
+       by pyparsing grammars.  This class is included when some whitespace structures
+       are significant.  Define with a string containing the whitespace characters to be
+       matched; default is C{" \\t\\r\\n"}.  Also takes optional C{min}, C{max}, and C{exact} arguments,
+       as defined for the C{L{Word}} class."""
+    whiteStrs = {
+        " " : "",
+        "\t": "",
+        "\n": "",
+        "\r": "",
+        "\f": "",
+        }
+    def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0):
+        super(White,self).__init__()
+        self.matchWhite = ws
+        self.setWhitespaceChars( "".join(c for c in self.whiteChars if c not in self.matchWhite) )
+        #~ self.leaveWhitespace()
+        self.name = ("".join(White.whiteStrs[c] for c in self.matchWhite))
+        self.mayReturnEmpty = True
+        self.errmsg = "Expected " + self.name
+
+        self.minLen = min
+
+        if max > 0:
+            self.maxLen = max
+        else:
+            self.maxLen = _MAX_INT
+
+        if exact > 0:
+            self.maxLen = exact
+            self.minLen = exact
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if not(instring[ loc ] in self.matchWhite):
+            raise ParseException(instring, loc, self.errmsg, self)
+        start = loc
+        loc += 1
+        maxloc = start + self.maxLen
+        maxloc = min( maxloc, len(instring) )
+        while loc < maxloc and instring[loc] in self.matchWhite:
+            loc += 1
+
+        if loc - start < self.minLen:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        return loc, instring[start:loc]
+
+
+class _PositionToken(Token):
+    def __init__( self ):
+        super(_PositionToken,self).__init__()
+        self.name=self.__class__.__name__
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+
+class GoToColumn(_PositionToken):
+    """Token to advance to a specific column of input text; useful for tabular report scraping."""
+    def __init__( self, colno ):
+        super(GoToColumn,self).__init__()
+        self.col = colno
+
+    def preParse( self, instring, loc ):
+        if col(loc,instring) != self.col:
+            instrlen = len(instring)
+            if self.ignoreExprs:
+                loc = self._skipIgnorables( instring, loc )
+            while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col :
+                loc += 1
+        return loc
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        thiscol = col( loc, instring )
+        if thiscol > self.col:
+            raise ParseException( instring, loc, "Text not in expected column", self )
+        newloc = loc + self.col - thiscol
+        ret = instring[ loc: newloc ]
+        return newloc, ret
+
+class LineStart(_PositionToken):
+    """Matches if current position is at the beginning of a line within the parse string"""
+    def __init__( self ):
+        super(LineStart,self).__init__()
+        self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") )
+        self.errmsg = "Expected start of line"
+
+    def preParse( self, instring, loc ):
+        preloc = super(LineStart,self).preParse(instring,loc)
+        if instring[preloc] == "\n":
+            loc += 1
+        return loc
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if not( loc==0 or
+            (loc == self.preParse( instring, 0 )) or
+            (instring[loc-1] == "\n") ): #col(loc, instring) != 1:
+            raise ParseException(instring, loc, self.errmsg, self)
+        return loc, []
+
+class LineEnd(_PositionToken):
+    """Matches if current position is at the end of a line within the parse string"""
+    def __init__( self ):
+        super(LineEnd,self).__init__()
+        self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") )
+        self.errmsg = "Expected end of line"
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if loc len(instring):
+            return loc, []
+        else:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+class WordStart(_PositionToken):
+    """Matches if the current position is at the beginning of a Word, and
+       is not preceded by any character in a given set of C{wordChars}
+       (default=C{printables}). To emulate the C{\b} behavior of regular expressions,
+       use C{WordStart(alphanums)}. C{WordStart} will also match at the beginning of
+       the string being parsed, or at the beginning of a line.
+    """
+    def __init__(self, wordChars = printables):
+        super(WordStart,self).__init__()
+        self.wordChars = set(wordChars)
+        self.errmsg = "Not at the start of a word"
+
+    def parseImpl(self, instring, loc, doActions=True ):
+        if loc != 0:
+            if (instring[loc-1] in self.wordChars or
+                instring[loc] not in self.wordChars):
+                raise ParseException(instring, loc, self.errmsg, self)
+        return loc, []
+
+class WordEnd(_PositionToken):
+    """Matches if the current position is at the end of a Word, and
+       is not followed by any character in a given set of C{wordChars}
+       (default=C{printables}). To emulate the C{\b} behavior of regular expressions,
+       use C{WordEnd(alphanums)}. C{WordEnd} will also match at the end of
+       the string being parsed, or at the end of a line.
+    """
+    def __init__(self, wordChars = printables):
+        super(WordEnd,self).__init__()
+        self.wordChars = set(wordChars)
+        self.skipWhitespace = False
+        self.errmsg = "Not at the end of a word"
+
+    def parseImpl(self, instring, loc, doActions=True ):
+        instrlen = len(instring)
+        if instrlen>0 and loc maxExcLoc:
+                    maxException = err
+                    maxExcLoc = err.loc
+            except IndexError:
+                if len(instring) > maxExcLoc:
+                    maxException = ParseException(instring,len(instring),e.errmsg,self)
+                    maxExcLoc = len(instring)
+            else:
+                # save match among all matches, to retry longest to shortest
+                matches.append((loc2, e))
+
+        if matches:
+            matches.sort(key=lambda x: -x[0])
+            for _,e in matches:
+                try:
+                    return e._parse( instring, loc, doActions )
+                except ParseException as err:
+                    err.__traceback__ = None
+                    if err.loc > maxExcLoc:
+                        maxException = err
+                        maxExcLoc = err.loc
+
+        if maxException is not None:
+            maxException.msg = self.errmsg
+            raise maxException
+        else:
+            raise ParseException(instring, loc, "no defined alternatives to match", self)
+
+
+    def __ixor__(self, other ):
+        if isinstance( other, basestring ):
+            other = ParserElement.literalStringClass( other )
+        return self.append( other ) #Or( [ self, other ] )
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + " ^ ".join(_ustr(e) for e in self.exprs) + "}"
+
+        return self.strRepr
+
+    def checkRecursion( self, parseElementList ):
+        subRecCheckList = parseElementList[:] + [ self ]
+        for e in self.exprs:
+            e.checkRecursion( subRecCheckList )
+
+
+class MatchFirst(ParseExpression):
+    """Requires that at least one C{ParseExpression} is found.
+       If two expressions match, the first one listed is the one that will match.
+       May be constructed using the C{'|'} operator.
+    """
+    def __init__( self, exprs, savelist = False ):
+        super(MatchFirst,self).__init__(exprs, savelist)
+        if self.exprs:
+            self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs)
+        else:
+            self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        maxExcLoc = -1
+        maxException = None
+        for e in self.exprs:
+            try:
+                ret = e._parse( instring, loc, doActions )
+                return ret
+            except ParseException as err:
+                if err.loc > maxExcLoc:
+                    maxException = err
+                    maxExcLoc = err.loc
+            except IndexError:
+                if len(instring) > maxExcLoc:
+                    maxException = ParseException(instring,len(instring),e.errmsg,self)
+                    maxExcLoc = len(instring)
+
+        # only got here if no expression matched, raise exception for match that made it the furthest
+        else:
+            if maxException is not None:
+                maxException.msg = self.errmsg
+                raise maxException
+            else:
+                raise ParseException(instring, loc, "no defined alternatives to match", self)
+
+    def __ior__(self, other ):
+        if isinstance( other, basestring ):
+            other = ParserElement.literalStringClass( other )
+        return self.append( other ) #MatchFirst( [ self, other ] )
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + " | ".join(_ustr(e) for e in self.exprs) + "}"
+
+        return self.strRepr
+
+    def checkRecursion( self, parseElementList ):
+        subRecCheckList = parseElementList[:] + [ self ]
+        for e in self.exprs:
+            e.checkRecursion( subRecCheckList )
+
+
+class Each(ParseExpression):
+    """Requires all given C{ParseExpression}s to be found, but in any order.
+       Expressions may be separated by whitespace.
+       May be constructed using the C{'&'} operator.
+    """
+    def __init__( self, exprs, savelist = True ):
+        super(Each,self).__init__(exprs, savelist)
+        self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs)
+        self.skipWhitespace = True
+        self.initExprGroups = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.initExprGroups:
+            self.opt1map = dict((id(e.expr),e) for e in self.exprs if isinstance(e,Optional))
+            opt1 = [ e.expr for e in self.exprs if isinstance(e,Optional) ]
+            opt2 = [ e for e in self.exprs if e.mayReturnEmpty and not isinstance(e,Optional)]
+            self.optionals = opt1 + opt2
+            self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ]
+            self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ]
+            self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ]
+            self.required += self.multirequired
+            self.initExprGroups = False
+        tmpLoc = loc
+        tmpReqd = self.required[:]
+        tmpOpt  = self.optionals[:]
+        matchOrder = []
+
+        keepMatching = True
+        while keepMatching:
+            tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired
+            failed = []
+            for e in tmpExprs:
+                try:
+                    tmpLoc = e.tryParse( instring, tmpLoc )
+                except ParseException:
+                    failed.append(e)
+                else:
+                    matchOrder.append(self.opt1map.get(id(e),e))
+                    if e in tmpReqd:
+                        tmpReqd.remove(e)
+                    elif e in tmpOpt:
+                        tmpOpt.remove(e)
+            if len(failed) == len(tmpExprs):
+                keepMatching = False
+
+        if tmpReqd:
+            missing = ", ".join(_ustr(e) for e in tmpReqd)
+            raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing )
+
+        # add any unmatched Optionals, in case they have default values defined
+        matchOrder += [e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt]
+
+        resultlist = []
+        for e in matchOrder:
+            loc,results = e._parse(instring,loc,doActions)
+            resultlist.append(results)
+
+        finalResults = ParseResults()
+        for r in resultlist:
+            dups = {}
+            for k in r.keys():
+                if k in finalResults:
+                    tmp = ParseResults(finalResults[k])
+                    tmp += ParseResults(r[k])
+                    dups[k] = tmp
+            finalResults += ParseResults(r)
+            for k,v in dups.items():
+                finalResults[k] = v
+        return loc, finalResults
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + " & ".join(_ustr(e) for e in self.exprs) + "}"
+
+        return self.strRepr
+
+    def checkRecursion( self, parseElementList ):
+        subRecCheckList = parseElementList[:] + [ self ]
+        for e in self.exprs:
+            e.checkRecursion( subRecCheckList )
+
+
+class ParseElementEnhance(ParserElement):
+    """Abstract subclass of C{ParserElement}, for combining and post-processing parsed tokens."""
+    def __init__( self, expr, savelist=False ):
+        super(ParseElementEnhance,self).__init__(savelist)
+        if isinstance( expr, basestring ):
+            expr = Literal(expr)
+        self.expr = expr
+        self.strRepr = None
+        if expr is not None:
+            self.mayIndexError = expr.mayIndexError
+            self.mayReturnEmpty = expr.mayReturnEmpty
+            self.setWhitespaceChars( expr.whiteChars )
+            self.skipWhitespace = expr.skipWhitespace
+            self.saveAsList = expr.saveAsList
+            self.callPreparse = expr.callPreparse
+            self.ignoreExprs.extend(expr.ignoreExprs)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.expr is not None:
+            return self.expr._parse( instring, loc, doActions, callPreParse=False )
+        else:
+            raise ParseException("",loc,self.errmsg,self)
+
+    def leaveWhitespace( self ):
+        self.skipWhitespace = False
+        self.expr = self.expr.copy()
+        if self.expr is not None:
+            self.expr.leaveWhitespace()
+        return self
+
+    def ignore( self, other ):
+        if isinstance( other, Suppress ):
+            if other not in self.ignoreExprs:
+                super( ParseElementEnhance, self).ignore( other )
+                if self.expr is not None:
+                    self.expr.ignore( self.ignoreExprs[-1] )
+        else:
+            super( ParseElementEnhance, self).ignore( other )
+            if self.expr is not None:
+                self.expr.ignore( self.ignoreExprs[-1] )
+        return self
+
+    def streamline( self ):
+        super(ParseElementEnhance,self).streamline()
+        if self.expr is not None:
+            self.expr.streamline()
+        return self
+
+    def checkRecursion( self, parseElementList ):
+        if self in parseElementList:
+            raise RecursiveGrammarException( parseElementList+[self] )
+        subRecCheckList = parseElementList[:] + [ self ]
+        if self.expr is not None:
+            self.expr.checkRecursion( subRecCheckList )
+
+    def validate( self, validateTrace=[] ):
+        tmp = validateTrace[:]+[self]
+        if self.expr is not None:
+            self.expr.validate(tmp)
+        self.checkRecursion( [] )
+
+    def __str__( self ):
+        try:
+            return super(ParseElementEnhance,self).__str__()
+        except:
+            pass
+
+        if self.strRepr is None and self.expr is not None:
+            self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) )
+        return self.strRepr
+
+
+class FollowedBy(ParseElementEnhance):
+    """Lookahead matching of the given parse expression.  C{FollowedBy}
+    does *not* advance the parsing position within the input string, it only
+    verifies that the specified parse expression matches at the current
+    position.  C{FollowedBy} always returns a null token list."""
+    def __init__( self, expr ):
+        super(FollowedBy,self).__init__(expr)
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        self.expr.tryParse( instring, loc )
+        return loc, []
+
+
+class NotAny(ParseElementEnhance):
+    """Lookahead to disallow matching with the given parse expression.  C{NotAny}
+    does *not* advance the parsing position within the input string, it only
+    verifies that the specified parse expression does *not* match at the current
+    position.  Also, C{NotAny} does *not* skip over leading whitespace. C{NotAny}
+    always returns a null token list.  May be constructed using the '~' operator."""
+    def __init__( self, expr ):
+        super(NotAny,self).__init__(expr)
+        #~ self.leaveWhitespace()
+        self.skipWhitespace = False  # do NOT use self.leaveWhitespace(), don't want to propagate to exprs
+        self.mayReturnEmpty = True
+        self.errmsg = "Found unwanted token, "+_ustr(self.expr)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.expr.canParseNext(instring, loc):
+            raise ParseException(instring, loc, self.errmsg, self)
+        return loc, []
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "~{" + _ustr(self.expr) + "}"
+
+        return self.strRepr
+
+
+class OneOrMore(ParseElementEnhance):
+    """Repetition of one or more of the given expression.
+    
+       Parameters:
+        - expr - expression that must match one or more times
+        - stopOn - (default=None) - expression for a terminating sentinel
+          (only required if the sentinel would ordinarily match the repetition 
+          expression)          
+    """
+    def __init__( self, expr, stopOn=None):
+        super(OneOrMore, self).__init__(expr)
+        ender = stopOn
+        if isinstance(ender, basestring):
+            ender = Literal(ender)
+        self.not_ender = ~ender if ender is not None else None
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        self_expr_parse = self.expr._parse
+        self_skip_ignorables = self._skipIgnorables
+        check_ender = self.not_ender is not None
+        if check_ender:
+            try_not_ender = self.not_ender.tryParse
+        
+        # must be at least one (but first see if we are the stopOn sentinel;
+        # if so, fail)
+        if check_ender:
+            try_not_ender(instring, loc)
+        loc, tokens = self_expr_parse( instring, loc, doActions, callPreParse=False )
+        try:
+            hasIgnoreExprs = (not not self.ignoreExprs)
+            while 1:
+                if check_ender:
+                    try_not_ender(instring, loc)
+                if hasIgnoreExprs:
+                    preloc = self_skip_ignorables( instring, loc )
+                else:
+                    preloc = loc
+                loc, tmptokens = self_expr_parse( instring, preloc, doActions )
+                if tmptokens or tmptokens.haskeys():
+                    tokens += tmptokens
+        except (ParseException,IndexError):
+            pass
+
+        return loc, tokens
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + _ustr(self.expr) + "}..."
+
+        return self.strRepr
+
+    def setResultsName( self, name, listAllMatches=False ):
+        ret = super(OneOrMore,self).setResultsName(name,listAllMatches)
+        ret.saveAsList = True
+        return ret
+
+class ZeroOrMore(OneOrMore):
+    """Optional repetition of zero or more of the given expression.
+    
+       Parameters:
+        - expr - expression that must match zero or more times
+        - stopOn - (default=None) - expression for a terminating sentinel
+          (only required if the sentinel would ordinarily match the repetition 
+          expression)          
+    """
+    def __init__( self, expr, stopOn=None):
+        super(ZeroOrMore,self).__init__(expr, stopOn=stopOn)
+        self.mayReturnEmpty = True
+        
+    def parseImpl( self, instring, loc, doActions=True ):
+        try:
+            return super(ZeroOrMore, self).parseImpl(instring, loc, doActions)
+        except (ParseException,IndexError):
+            return loc, []
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "[" + _ustr(self.expr) + "]..."
+
+        return self.strRepr
+
+class _NullToken(object):
+    def __bool__(self):
+        return False
+    __nonzero__ = __bool__
+    def __str__(self):
+        return ""
+
+_optionalNotMatched = _NullToken()
+class Optional(ParseElementEnhance):
+    """Optional matching of the given expression.
+
+       Parameters:
+        - expr - expression that must match zero or more times
+        - default (optional) - value to be returned if the optional expression
+          is not found.
+    """
+    def __init__( self, expr, default=_optionalNotMatched ):
+        super(Optional,self).__init__( expr, savelist=False )
+        self.defaultValue = default
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        try:
+            loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False )
+        except (ParseException,IndexError):
+            if self.defaultValue is not _optionalNotMatched:
+                if self.expr.resultsName:
+                    tokens = ParseResults([ self.defaultValue ])
+                    tokens[self.expr.resultsName] = self.defaultValue
+                else:
+                    tokens = [ self.defaultValue ]
+            else:
+                tokens = []
+        return loc, tokens
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "[" + _ustr(self.expr) + "]"
+
+        return self.strRepr
+
+class SkipTo(ParseElementEnhance):
+    """Token for skipping over all undefined text until the matched expression is found.
+
+       Parameters:
+        - expr - target expression marking the end of the data to be skipped
+        - include - (default=False) if True, the target expression is also parsed 
+          (the skipped text and target expression are returned as a 2-element list).
+        - ignore - (default=None) used to define grammars (typically quoted strings and 
+          comments) that might contain false matches to the target expression
+        - failOn - (default=None) define expressions that are not allowed to be 
+          included in the skipped test; if found before the target expression is found, 
+          the SkipTo is not a match
+    """
+    def __init__( self, other, include=False, ignore=None, failOn=None ):
+        super( SkipTo, self ).__init__( other )
+        self.ignoreExpr = ignore
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+        self.includeMatch = include
+        self.asList = False
+        if isinstance(failOn, basestring):
+            self.failOn = Literal(failOn)
+        else:
+            self.failOn = failOn
+        self.errmsg = "No match found for "+_ustr(self.expr)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        startloc = loc
+        instrlen = len(instring)
+        expr = self.expr
+        expr_parse = self.expr._parse
+        self_failOn_canParseNext = self.failOn.canParseNext if self.failOn is not None else None
+        self_ignoreExpr_tryParse = self.ignoreExpr.tryParse if self.ignoreExpr is not None else None
+        
+        tmploc = loc
+        while tmploc <= instrlen:
+            if self_failOn_canParseNext is not None:
+                # break if failOn expression matches
+                if self_failOn_canParseNext(instring, tmploc):
+                    break
+                    
+            if self_ignoreExpr_tryParse is not None:
+                # advance past ignore expressions
+                while 1:
+                    try:
+                        tmploc = self_ignoreExpr_tryParse(instring, tmploc)
+                    except ParseBaseException:
+                        break
+            
+            try:
+                expr_parse(instring, tmploc, doActions=False, callPreParse=False)
+            except (ParseException, IndexError):
+                # no match, advance loc in string
+                tmploc += 1
+            else:
+                # matched skipto expr, done
+                break
+
+        else:
+            # ran off the end of the input string without matching skipto expr, fail
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        # build up return values
+        loc = tmploc
+        skiptext = instring[startloc:loc]
+        skipresult = ParseResults(skiptext)
+        
+        if self.includeMatch:
+            loc, mat = expr_parse(instring,loc,doActions,callPreParse=False)
+            skipresult += mat
+
+        return loc, skipresult
+
+class Forward(ParseElementEnhance):
+    """Forward declaration of an expression to be defined later -
+       used for recursive grammars, such as algebraic infix notation.
+       When the expression is known, it is assigned to the C{Forward} variable using the '<<' operator.
+
+       Note: take care when assigning to C{Forward} not to overlook precedence of operators.
+       Specifically, '|' has a lower precedence than '<<', so that::
+          fwdExpr << a | b | c
+       will actually be evaluated as::
+          (fwdExpr << a) | b | c
+       thereby leaving b and c out as parseable alternatives.  It is recommended that you
+       explicitly group the values inserted into the C{Forward}::
+          fwdExpr << (a | b | c)
+       Converting to use the '<<=' operator instead will avoid this problem.
+    """
+    def __init__( self, other=None ):
+        super(Forward,self).__init__( other, savelist=False )
+
+    def __lshift__( self, other ):
+        if isinstance( other, basestring ):
+            other = ParserElement.literalStringClass(other)
+        self.expr = other
+        self.strRepr = None
+        self.mayIndexError = self.expr.mayIndexError
+        self.mayReturnEmpty = self.expr.mayReturnEmpty
+        self.setWhitespaceChars( self.expr.whiteChars )
+        self.skipWhitespace = self.expr.skipWhitespace
+        self.saveAsList = self.expr.saveAsList
+        self.ignoreExprs.extend(self.expr.ignoreExprs)
+        return self
+        
+    def __ilshift__(self, other):
+        return self << other
+    
+    def leaveWhitespace( self ):
+        self.skipWhitespace = False
+        return self
+
+    def streamline( self ):
+        if not self.streamlined:
+            self.streamlined = True
+            if self.expr is not None:
+                self.expr.streamline()
+        return self
+
+    def validate( self, validateTrace=[] ):
+        if self not in validateTrace:
+            tmp = validateTrace[:]+[self]
+            if self.expr is not None:
+                self.expr.validate(tmp)
+        self.checkRecursion([])
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+        return self.__class__.__name__ + ": ..."
+
+        # stubbed out for now - creates awful memory and perf issues
+        self._revertClass = self.__class__
+        self.__class__ = _ForwardNoRecurse
+        try:
+            if self.expr is not None:
+                retString = _ustr(self.expr)
+            else:
+                retString = "None"
+        finally:
+            self.__class__ = self._revertClass
+        return self.__class__.__name__ + ": " + retString
+
+    def copy(self):
+        if self.expr is not None:
+            return super(Forward,self).copy()
+        else:
+            ret = Forward()
+            ret <<= self
+            return ret
+
+class _ForwardNoRecurse(Forward):
+    def __str__( self ):
+        return "..."
+
+class TokenConverter(ParseElementEnhance):
+    """Abstract subclass of C{ParseExpression}, for converting parsed results."""
+    def __init__( self, expr, savelist=False ):
+        super(TokenConverter,self).__init__( expr )#, savelist )
+        self.saveAsList = False
+
+class Combine(TokenConverter):
+    """Converter to concatenate all matching tokens to a single string.
+       By default, the matching patterns must also be contiguous in the input string;
+       this can be disabled by specifying C{'adjacent=False'} in the constructor.
+    """
+    def __init__( self, expr, joinString="", adjacent=True ):
+        super(Combine,self).__init__( expr )
+        # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself
+        if adjacent:
+            self.leaveWhitespace()
+        self.adjacent = adjacent
+        self.skipWhitespace = True
+        self.joinString = joinString
+        self.callPreparse = True
+
+    def ignore( self, other ):
+        if self.adjacent:
+            ParserElement.ignore(self, other)
+        else:
+            super( Combine, self).ignore( other )
+        return self
+
+    def postParse( self, instring, loc, tokenlist ):
+        retToks = tokenlist.copy()
+        del retToks[:]
+        retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults)
+
+        if self.resultsName and retToks.haskeys():
+            return [ retToks ]
+        else:
+            return retToks
+
+class Group(TokenConverter):
+    """Converter to return the matched tokens as a list - useful for returning tokens of C{L{ZeroOrMore}} and C{L{OneOrMore}} expressions."""
+    def __init__( self, expr ):
+        super(Group,self).__init__( expr )
+        self.saveAsList = True
+
+    def postParse( self, instring, loc, tokenlist ):
+        return [ tokenlist ]
+
+class Dict(TokenConverter):
+    """Converter to return a repetitive expression as a list, but also as a dictionary.
+       Each element can also be referenced using the first token in the expression as its key.
+       Useful for tabular report scraping when the first column can be used as a item key.
+    """
+    def __init__( self, expr ):
+        super(Dict,self).__init__( expr )
+        self.saveAsList = True
+
+    def postParse( self, instring, loc, tokenlist ):
+        for i,tok in enumerate(tokenlist):
+            if len(tok) == 0:
+                continue
+            ikey = tok[0]
+            if isinstance(ikey,int):
+                ikey = _ustr(tok[0]).strip()
+            if len(tok)==1:
+                tokenlist[ikey] = _ParseResultsWithOffset("",i)
+            elif len(tok)==2 and not isinstance(tok[1],ParseResults):
+                tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i)
+            else:
+                dictvalue = tok.copy() #ParseResults(i)
+                del dictvalue[0]
+                if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.haskeys()):
+                    tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i)
+                else:
+                    tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i)
+
+        if self.resultsName:
+            return [ tokenlist ]
+        else:
+            return tokenlist
+
+
+class Suppress(TokenConverter):
+    """Converter for ignoring the results of a parsed expression."""
+    def postParse( self, instring, loc, tokenlist ):
+        return []
+
+    def suppress( self ):
+        return self
+
+
+class OnlyOnce(object):
+    """Wrapper for parse actions, to ensure they are only called once."""
+    def __init__(self, methodCall):
+        self.callable = _trim_arity(methodCall)
+        self.called = False
+    def __call__(self,s,l,t):
+        if not self.called:
+            results = self.callable(s,l,t)
+            self.called = True
+            return results
+        raise ParseException(s,l,"")
+    def reset(self):
+        self.called = False
+
+def traceParseAction(f):
+    """Decorator for debugging parse actions."""
+    f = _trim_arity(f)
+    def z(*paArgs):
+        thisFunc = f.func_name
+        s,l,t = paArgs[-3:]
+        if len(paArgs)>3:
+            thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc
+        sys.stderr.write( ">>entering %s(line: '%s', %d, %s)\n" % (thisFunc,line(l,s),l,t) )
+        try:
+            ret = f(*paArgs)
+        except Exception as exc:
+            sys.stderr.write( "<", "|".join( [ _escapeRegexChars(sym) for sym in symbols] ))
+        try:
+            if len(symbols)==len("".join(symbols)):
+                return Regex( "[%s]" % "".join(_escapeRegexRangeChars(sym) for sym in symbols) ).setName(' | '.join(symbols))
+            else:
+                return Regex( "|".join(re.escape(sym) for sym in symbols) ).setName(' | '.join(symbols))
+        except:
+            warnings.warn("Exception creating Regex for oneOf, building MatchFirst",
+                    SyntaxWarning, stacklevel=2)
+
+
+    # last resort, just use MatchFirst
+    return MatchFirst(parseElementClass(sym) for sym in symbols).setName(' | '.join(symbols))
+
+def dictOf( key, value ):
+    """Helper to easily and clearly define a dictionary by specifying the respective patterns
+       for the key and value.  Takes care of defining the C{L{Dict}}, C{L{ZeroOrMore}}, and C{L{Group}} tokens
+       in the proper order.  The key pattern can include delimiting markers or punctuation,
+       as long as they are suppressed, thereby leaving the significant key text.  The value
+       pattern can include named results, so that the C{Dict} results can include named token
+       fields.
+    """
+    return Dict( ZeroOrMore( Group ( key + value ) ) )
+
+def originalTextFor(expr, asString=True):
+    """Helper to return the original, untokenized text for a given expression.  Useful to
+       restore the parsed fields of an HTML start tag into the raw tag text itself, or to
+       revert separate tokens with intervening whitespace back to the original matching
+       input text. By default, returns astring containing the original parsed text.  
+       
+       If the optional C{asString} argument is passed as C{False}, then the return value is a 
+       C{L{ParseResults}} containing any results names that were originally matched, and a 
+       single token containing the original matched text from the input string.  So if 
+       the expression passed to C{L{originalTextFor}} contains expressions with defined
+       results names, you must set C{asString} to C{False} if you want to preserve those
+       results name values."""
+    locMarker = Empty().setParseAction(lambda s,loc,t: loc)
+    endlocMarker = locMarker.copy()
+    endlocMarker.callPreparse = False
+    matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end")
+    if asString:
+        extractText = lambda s,l,t: s[t._original_start:t._original_end]
+    else:
+        def extractText(s,l,t):
+            t[:] = [s[t.pop('_original_start'):t.pop('_original_end')]]
+    matchExpr.setParseAction(extractText)
+    return matchExpr
+
+def ungroup(expr): 
+    """Helper to undo pyparsing's default grouping of And expressions, even
+       if all but one are non-empty."""
+    return TokenConverter(expr).setParseAction(lambda t:t[0])
+
+def locatedExpr(expr):
+    """Helper to decorate a returned token with its starting and ending locations in the input string.
+       This helper adds the following results names:
+        - locn_start = location where matched expression begins
+        - locn_end = location where matched expression ends
+        - value = the actual parsed results
+
+       Be careful if the input text contains C{} characters, you may want to call
+       C{L{ParserElement.parseWithTabs}}
+    """
+    locator = Empty().setParseAction(lambda s,l,t: l)
+    return Group(locator("locn_start") + expr("value") + locator.copy().leaveWhitespace()("locn_end"))
+
+
+# convenience constants for positional expressions
+empty       = Empty().setName("empty")
+lineStart   = LineStart().setName("lineStart")
+lineEnd     = LineEnd().setName("lineEnd")
+stringStart = StringStart().setName("stringStart")
+stringEnd   = StringEnd().setName("stringEnd")
+
+_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1])
+_escapedHexChar = Regex(r"\\0?[xX][0-9a-fA-F]+").setParseAction(lambda s,l,t:unichr(int(t[0].lstrip(r'\0x'),16)))
+_escapedOctChar = Regex(r"\\0[0-7]+").setParseAction(lambda s,l,t:unichr(int(t[0][1:],8)))
+_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | Word(printables, excludeChars=r'\]', exact=1) | Regex(r"\w", re.UNICODE)
+_charRange = Group(_singleChar + Suppress("-") + _singleChar)
+_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]"
+
+def srange(s):
+    r"""Helper to easily define string ranges for use in Word construction.  Borrows
+       syntax from regexp '[]' string range definitions::
+          srange("[0-9]")   -> "0123456789"
+          srange("[a-z]")   -> "abcdefghijklmnopqrstuvwxyz"
+          srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_"
+       The input string must be enclosed in []'s, and the returned string is the expanded
+       character set joined into a single string.
+       The values enclosed in the []'s may be::
+          a single character
+          an escaped character with a leading backslash (such as \- or \])
+          an escaped hex character with a leading '\x' (\x21, which is a '!' character) 
+            (\0x## is also supported for backwards compatibility) 
+          an escaped octal character with a leading '\0' (\041, which is a '!' character)
+          a range of any of the above, separated by a dash ('a-z', etc.)
+          any combination of the above ('aeiouy', 'a-zA-Z0-9_$', etc.)
+    """
+    _expanded = lambda p: p if not isinstance(p,ParseResults) else ''.join(unichr(c) for c in range(ord(p[0]),ord(p[1])+1))
+    try:
+        return "".join(_expanded(part) for part in _reBracketExpr.parseString(s).body)
+    except:
+        return ""
+
+def matchOnlyAtCol(n):
+    """Helper method for defining parse actions that require matching at a specific
+       column in the input text.
+    """
+    def verifyCol(strg,locn,toks):
+        if col(locn,strg) != n:
+            raise ParseException(strg,locn,"matched token not at column %d" % n)
+    return verifyCol
+
+def replaceWith(replStr):
+    """Helper method for common parse actions that simply return a literal value.  Especially
+       useful when used with C{L{transformString}()}.
+    """
+    return lambda s,l,t: [replStr]
+
+def removeQuotes(s,l,t):
+    """Helper parse action for removing quotation marks from parsed quoted strings.
+       To use, add this parse action to quoted string using::
+         quotedString.setParseAction( removeQuotes )
+    """
+    return t[0][1:-1]
+
+def upcaseTokens(s,l,t):
+    """Helper parse action to convert tokens to upper case."""
+    return [ tt.upper() for tt in map(_ustr,t) ]
+
+def downcaseTokens(s,l,t):
+    """Helper parse action to convert tokens to lower case."""
+    return [ tt.lower() for tt in map(_ustr,t) ]
+
+def _makeTags(tagStr, xml):
+    """Internal helper to construct opening and closing tag expressions, given a tag name"""
+    if isinstance(tagStr,basestring):
+        resname = tagStr
+        tagStr = Keyword(tagStr, caseless=not xml)
+    else:
+        resname = tagStr.name
+
+    tagAttrName = Word(alphas,alphanums+"_-:")
+    if (xml):
+        tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes )
+        openTag = Suppress("<") + tagStr("tag") + \
+                Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \
+                Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
+    else:
+        printablesLessRAbrack = "".join(c for c in printables if c not in ">")
+        tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack)
+        openTag = Suppress("<") + tagStr("tag") + \
+                Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \
+                Optional( Suppress("=") + tagAttrValue ) ))) + \
+                Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
+    closeTag = Combine(_L("")
+
+    openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % resname)
+    closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("" % resname)
+    openTag.tag = resname
+    closeTag.tag = resname
+    return openTag, closeTag
+
+def makeHTMLTags(tagStr):
+    """Helper to construct opening and closing tag expressions for HTML, given a tag name"""
+    return _makeTags( tagStr, False )
+
+def makeXMLTags(tagStr):
+    """Helper to construct opening and closing tag expressions for XML, given a tag name"""
+    return _makeTags( tagStr, True )
+
+def withAttribute(*args,**attrDict):
+    """Helper to create a validating parse action to be used with start tags created
+       with C{L{makeXMLTags}} or C{L{makeHTMLTags}}. Use C{withAttribute} to qualify a starting tag
+       with a required attribute value, to avoid false matches on common tags such as
+       C{} or C{
}. + + Call C{withAttribute} with a series of attribute names and values. Specify the list + of filter attributes names and values as: + - keyword arguments, as in C{(align="right")}, or + - as an explicit dict with C{**} operator, when an attribute name is also a Python + reserved word, as in C{**{"class":"Customer", "align":"right"}} + - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) + For attribute names with a namespace prefix, you must use the second form. Attribute + names are matched insensitive to upper/lower case. + + If just testing for C{class} (with or without a namespace), use C{L{withClass}}. + + To verify that the attribute exists, but without specifying a value, pass + C{withAttribute.ANY_VALUE} as the value. + """ + if args: + attrs = args[:] + else: + attrs = attrDict.items() + attrs = [(k,v) for k,v in attrs] + def pa(s,l,tokens): + for attrName,attrValue in attrs: + if attrName not in tokens: + raise ParseException(s,l,"no matching attribute " + attrName) + if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue: + raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" % + (attrName, tokens[attrName], attrValue)) + return pa +withAttribute.ANY_VALUE = object() + +def withClass(classname, namespace=''): + """Simplified version of C{L{withAttribute}} when matching on a div class - made + difficult because C{class} is a reserved word in Python. + """ + classattr = "%s:class" % namespace if namespace else "class" + return withAttribute(**{classattr : classname}) + +opAssoc = _Constants() +opAssoc.LEFT = object() +opAssoc.RIGHT = object() + +def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ): + """Helper method for constructing grammars of expressions made up of + operators working in a precedence hierarchy. Operators may be unary or + binary, left- or right-associative. Parse actions can also be attached + to operator expressions. + + Parameters: + - baseExpr - expression representing the most basic element for the nested + - opList - list of tuples, one for each operator precedence level in the + expression grammar; each tuple is of the form + (opExpr, numTerms, rightLeftAssoc, parseAction), where: + - opExpr is the pyparsing expression for the operator; + may also be a string, which will be converted to a Literal; + if numTerms is 3, opExpr is a tuple of two expressions, for the + two operators separating the 3 terms + - numTerms is the number of terms for this operator (must + be 1, 2, or 3) + - rightLeftAssoc is the indicator whether the operator is + right or left associative, using the pyparsing-defined + constants C{opAssoc.RIGHT} and C{opAssoc.LEFT}. + - parseAction is the parse action to be associated with + expressions matching this operator expression (the + parse action tuple member may be omitted) + - lpar - expression for matching left-parentheses (default=Suppress('(')) + - rpar - expression for matching right-parentheses (default=Suppress(')')) + """ + ret = Forward() + lastExpr = baseExpr | ( lpar + ret + rpar ) + for i,operDef in enumerate(opList): + opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] + termName = "%s term" % opExpr if arity < 3 else "%s%s term" % opExpr + if arity == 3: + if opExpr is None or len(opExpr) != 2: + raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions") + opExpr1, opExpr2 = opExpr + thisExpr = Forward().setName(termName) + if rightLeftAssoc == opAssoc.LEFT: + if arity == 1: + matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) ) + else: + matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \ + Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + elif rightLeftAssoc == opAssoc.RIGHT: + if arity == 1: + # try to avoid LR with this extra test + if not isinstance(opExpr, Optional): + opExpr = Optional(opExpr) + matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) ) + else: + matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \ + Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + else: + raise ValueError("operator must indicate right or left associativity") + if pa: + matchExpr.setParseAction( pa ) + thisExpr <<= ( matchExpr.setName(termName) | lastExpr ) + lastExpr = thisExpr + ret <<= lastExpr + return ret +operatorPrecedence = infixNotation + +dblQuotedString = Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\x[0-9a-fA-F]+)|(?:\\.))*"').setName("string enclosed in double quotes") +sglQuotedString = Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\x[0-9a-fA-F]+)|(?:\\.))*'").setName("string enclosed in single quotes") +quotedString = Regex(r'''(?:"(?:[^"\n\r\\]|(?:"")|(?:\\x[0-9a-fA-F]+)|(?:\\.))*")|(?:'(?:[^'\n\r\\]|(?:'')|(?:\\x[0-9a-fA-F]+)|(?:\\.))*')''').setName("quotedString using single or double quotes") +unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal") + +def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString.copy()): + """Helper method for defining nested lists enclosed in opening and closing + delimiters ("(" and ")" are the default). + + Parameters: + - opener - opening character for a nested list (default="("); can also be a pyparsing expression + - closer - closing character for a nested list (default=")"); can also be a pyparsing expression + - content - expression for items within the nested lists (default=None) + - ignoreExpr - expression for ignoring opening and closing delimiters (default=quotedString) + + If an expression is not provided for the content argument, the nested + expression will capture all whitespace-delimited content between delimiters + as a list of separate values. + + Use the C{ignoreExpr} argument to define expressions that may contain + opening or closing characters that should not be treated as opening + or closing characters for nesting, such as quotedString or a comment + expression. Specify multiple expressions using an C{L{Or}} or C{L{MatchFirst}}. + The default is L{quotedString}, but if no expressions are to be ignored, + then pass C{None} for this argument. + """ + if opener == closer: + raise ValueError("opening and closing strings cannot be the same") + if content is None: + if isinstance(opener,basestring) and isinstance(closer,basestring): + if len(opener) == 1 and len(closer)==1: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (empty.copy()+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS + ).setParseAction(lambda t:t[0].strip())) + else: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + ~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + raise ValueError("opening and closing arguments must be strings if no content expression is given") + ret = Forward() + if ignoreExpr is not None: + ret <<= Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) + else: + ret <<= Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) + ret.setName('nested %s%s expression' % (opener,closer)) + return ret + +def indentedBlock(blockStatementExpr, indentStack, indent=True): + """Helper method for defining space-delimited indentation blocks, such as + those used to define block statements in Python source code. + + Parameters: + - blockStatementExpr - expression defining syntax of statement that + is repeated within the indented block + - indentStack - list created by caller to manage indentation stack + (multiple statementWithIndentedBlock expressions within a single grammar + should share a common indentStack) + - indent - boolean indicating whether block must be indented beyond the + the current level; set to False for block of left-most statements + (default=True) + + A valid block must contain at least one C{blockStatement}. + """ + def checkPeerIndent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if curCol != indentStack[-1]: + if curCol > indentStack[-1]: + raise ParseFatalException(s,l,"illegal nesting") + raise ParseException(s,l,"not a peer entry") + + def checkSubIndent(s,l,t): + curCol = col(l,s) + if curCol > indentStack[-1]: + indentStack.append( curCol ) + else: + raise ParseException(s,l,"not a subentry") + + def checkUnindent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]): + raise ParseException(s,l,"not an unindent") + indentStack.pop() + + NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress()) + INDENT = (Empty() + Empty().setParseAction(checkSubIndent)).setName('INDENT') + PEER = Empty().setParseAction(checkPeerIndent).setName('') + UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT') + if indent: + smExpr = Group( Optional(NL) + + #~ FollowedBy(blockStatementExpr) + + INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT) + else: + smExpr = Group( Optional(NL) + + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) ) + blockStatementExpr.ignore(_bslash + LineEnd()) + return smExpr.setName('indented block') + +alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") +punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") + +anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:").setName('any tag')) +_htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(),'><& "\'')) +commonHTMLEntity = Regex('&(?P' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity") +def replaceHTMLEntity(t): + """Helper parser action to replace common HTML entities with their special characters""" + return _htmlEntityMap.get(t.entity) + +# it's easy to get these comment structures wrong - they're very common, so may as well make them available +cStyleComment = Regex(r"/\*(?:[^*]*\*+)+?/").setName("C style comment") + +htmlComment = Regex(r"").setName("HTML comment") +restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line") +dblSlashComment = Regex(r"\/\/(\\\n|.)*").setName("// comment") +cppStyleComment = Regex(r"/(?:\*(?:[^*]*\*+)+?/|/[^\n]*(?:\n[^\n]*)*?(?:(?>> import requests - >>> r = requests.get('http://python.org') + >>> r = requests.get('https://www.python.org') >>> r.status_code 200 >>> 'Python is a programming language' in r.content @@ -22,7 +22,7 @@ ... or POST: >>> payload = dict(key1='value1', key2='value2') - >>> r = requests.post("http://httpbin.org/post", data=payload) + >>> r = requests.post('http://httpbin.org/post', data=payload) >>> print(r.text) { ... @@ -36,17 +36,17 @@ The other HTTP methods are supported - see `requests.api`. Full documentation is at . -:copyright: (c) 2014 by Kenneth Reitz. +:copyright: (c) 2016 by Kenneth Reitz. :license: Apache 2.0, see LICENSE for more details. """ __title__ = 'requests' -__version__ = '2.3.0' -__build__ = 0x020300 +__version__ = '2.10.0' +__build__ = 0x021000 __author__ = 'Kenneth Reitz' __license__ = 'Apache 2.0' -__copyright__ = 'Copyright 2014 Kenneth Reitz' +__copyright__ = 'Copyright 2016 Kenneth Reitz' # Attempt to enable urllib3's SNI support, if possible try: @@ -55,6 +55,12 @@ except ImportError: pass +import warnings + +# urllib3's DependencyWarnings should be silenced. +from .packages.urllib3.exceptions import DependencyWarning +warnings.simplefilter('ignore', DependencyWarning) + from . import utils from .models import Request, Response, PreparedRequest from .api import request, get, head, post, patch, put, delete, options @@ -62,7 +68,8 @@ from .status_codes import codes from .exceptions import ( RequestException, Timeout, URLRequired, - TooManyRedirects, HTTPError, ConnectionError + TooManyRedirects, HTTPError, ConnectionError, + FileModeWarning, ConnectTimeout, ReadTimeout ) # Set default logging handler to avoid "No handler found" warnings. @@ -75,3 +82,8 @@ def emit(self, record): pass logging.getLogger(__name__).addHandler(NullHandler()) + +import warnings + +# FileModeWarnings go off per the default. +warnings.simplefilter('default', FileModeWarning, append=True) diff --git a/pip/_vendor/requests/adapters.py b/pip/_vendor/requests/adapters.py index eb7a2d282f9..23e448f42e9 100644 --- a/pip/_vendor/requests/adapters.py +++ b/pip/_vendor/requests/adapters.py @@ -8,28 +8,44 @@ and maintain connections. """ +import os.path import socket from .models import Response from .packages.urllib3.poolmanager import PoolManager, proxy_from_url from .packages.urllib3.response import HTTPResponse from .packages.urllib3.util import Timeout as TimeoutSauce -from .compat import urlparse, basestring, urldefrag, unquote +from .packages.urllib3.util.retry import Retry +from .compat import urlparse, basestring from .utils import (DEFAULT_CA_BUNDLE_PATH, get_encoding_from_headers, - prepend_scheme_if_needed, get_auth_from_url) + prepend_scheme_if_needed, get_auth_from_url, urldefragauth, + select_proxy, to_native_string) from .structures import CaseInsensitiveDict -from .packages.urllib3.exceptions import MaxRetryError -from .packages.urllib3.exceptions import TimeoutError -from .packages.urllib3.exceptions import SSLError as _SSLError +from .packages.urllib3.exceptions import ClosedPoolError +from .packages.urllib3.exceptions import ConnectTimeoutError from .packages.urllib3.exceptions import HTTPError as _HTTPError +from .packages.urllib3.exceptions import MaxRetryError +from .packages.urllib3.exceptions import NewConnectionError from .packages.urllib3.exceptions import ProxyError as _ProxyError +from .packages.urllib3.exceptions import ProtocolError +from .packages.urllib3.exceptions import ReadTimeoutError +from .packages.urllib3.exceptions import SSLError as _SSLError +from .packages.urllib3.exceptions import ResponseError from .cookies import extract_cookies_to_jar -from .exceptions import ConnectionError, Timeout, SSLError, ProxyError +from .exceptions import (ConnectionError, ConnectTimeout, ReadTimeout, SSLError, + ProxyError, RetryError, InvalidSchema) from .auth import _basic_auth_str +try: + from .packages.urllib3.contrib.socks import SOCKSProxyManager +except ImportError: + def SOCKSProxyManager(*args, **kwargs): + raise InvalidSchema("Missing dependencies for SOCKS support.") + DEFAULT_POOLBLOCK = False DEFAULT_POOLSIZE = 10 DEFAULT_RETRIES = 0 +DEFAULT_POOL_TIMEOUT = None class BaseAdapter(object): @@ -55,9 +71,13 @@ class HTTPAdapter(BaseAdapter): :param pool_connections: The number of urllib3 connection pools to cache. :param pool_maxsize: The maximum number of connections to save in the pool. - :param int max_retries: The maximum number of retries each connection - should attempt. Note, this applies only to failed connections and - timeouts, never to requests where the server returns a response. + :param max_retries: The maximum number of retries each connection + should attempt. Note, this applies only to failed DNS lookups, socket + connections and connection timeouts, never to requests where data has + made it to the server. By default, Requests does not retry failed + connections. If you need granular control over the conditions under + which we retry a request, import urllib3's ``Retry`` class and pass + that instead. :param pool_block: Whether the connection pool should block for connections. Usage:: @@ -73,7 +93,10 @@ class HTTPAdapter(BaseAdapter): def __init__(self, pool_connections=DEFAULT_POOLSIZE, pool_maxsize=DEFAULT_POOLSIZE, max_retries=DEFAULT_RETRIES, pool_block=DEFAULT_POOLBLOCK): - self.max_retries = max_retries + if max_retries == DEFAULT_RETRIES: + self.max_retries = Retry(0, read=False) + else: + self.max_retries = Retry.from_int(max_retries) self.config = {} self.proxy_manager = {} @@ -91,7 +114,7 @@ def __getstate__(self): def __setstate__(self, state): # Can't handle by adding 'proxy_manager' to self.__attrs__ because - # because self.poolmanager uses a lambda function, which isn't pickleable. + # self.poolmanager uses a lambda function, which isn't pickleable. self.proxy_manager = {} self.config = {} @@ -101,14 +124,17 @@ def __setstate__(self, state): self.init_poolmanager(self._pool_connections, self._pool_maxsize, block=self._pool_block) - def init_poolmanager(self, connections, maxsize, block=DEFAULT_POOLBLOCK): - """Initializes a urllib3 PoolManager. This method should not be called - from user code, and is only exposed for use when subclassing the + def init_poolmanager(self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs): + """Initializes a urllib3 PoolManager. + + This method should not be called from user code, and is only + exposed for use when subclassing the :class:`HTTPAdapter `. :param connections: The number of urllib3 connection pools to cache. :param maxsize: The maximum number of connections to save in the pool. :param block: Block when no free connections are available. + :param pool_kwargs: Extra keyword arguments used to initialize the Pool Manager. """ # save these values for pickling self._pool_connections = connections @@ -116,7 +142,43 @@ def init_poolmanager(self, connections, maxsize, block=DEFAULT_POOLBLOCK): self._pool_block = block self.poolmanager = PoolManager(num_pools=connections, maxsize=maxsize, - block=block) + block=block, strict=True, **pool_kwargs) + + def proxy_manager_for(self, proxy, **proxy_kwargs): + """Return urllib3 ProxyManager for the given proxy. + + This method should not be called from user code, and is only + exposed for use when subclassing the + :class:`HTTPAdapter `. + + :param proxy: The proxy to return a urllib3 ProxyManager for. + :param proxy_kwargs: Extra keyword arguments used to configure the Proxy Manager. + :returns: ProxyManager + """ + if proxy in self.proxy_manager: + manager = self.proxy_manager[proxy] + elif proxy.lower().startswith('socks'): + username, password = get_auth_from_url(proxy) + manager = self.proxy_manager[proxy] = SOCKSProxyManager( + proxy, + username=username, + password=password, + num_pools=self._pool_connections, + maxsize=self._pool_maxsize, + block=self._pool_block, + **proxy_kwargs + ) + else: + proxy_headers = self.proxy_headers(proxy) + manager = self.proxy_manager[proxy] = proxy_from_url( + proxy, + proxy_headers=proxy_headers, + num_pools=self._pool_connections, + maxsize=self._pool_maxsize, + block=self._pool_block, + **proxy_kwargs) + + return manager def cert_verify(self, conn, url, verify, cert): """Verify a SSL certificate. This method should not be called from user @@ -143,10 +205,15 @@ def cert_verify(self, conn, url, verify, cert): raise Exception("Could not find a suitable SSL CA certificate bundle.") conn.cert_reqs = 'CERT_REQUIRED' - conn.ca_certs = cert_loc + + if not os.path.isdir(cert_loc): + conn.ca_certs = cert_loc + else: + conn.ca_cert_dir = cert_loc else: conn.cert_reqs = 'CERT_NONE' conn.ca_certs = None + conn.ca_cert_dir = None if cert: if not isinstance(cert, basestring): @@ -199,22 +266,12 @@ def get_connection(self, url, proxies=None): :param url: The URL to connect to. :param proxies: (optional) A Requests-style dictionary of proxies used on this request. """ - proxies = proxies or {} - proxy = proxies.get(urlparse(url.lower()).scheme) + proxy = select_proxy(url, proxies) if proxy: proxy = prepend_scheme_if_needed(proxy, 'http') - proxy_headers = self.proxy_headers(proxy) - - if not proxy in self.proxy_manager: - self.proxy_manager[proxy] = proxy_from_url( - proxy, - proxy_headers=proxy_headers, - num_pools=self._pool_connections, - maxsize=self._pool_maxsize, - block=self._pool_block) - - conn = self.proxy_manager[proxy].connection_from_url(url) + proxy_manager = self.proxy_manager_for(proxy) + conn = proxy_manager.connection_from_url(url) else: # Only scheme should be lower case parsed = urlparse(url) @@ -226,10 +283,12 @@ def get_connection(self, url, proxies=None): def close(self): """Disposes of any internal state. - Currently, this just closes the PoolManager, which closes pooled - connections. + Currently, this closes the PoolManager and any active ProxyManager, + which closes any pooled connections. """ self.poolmanager.clear() + for proxy in self.proxy_manager.values(): + proxy.clear() def request_url(self, request, proxies): """Obtain the url to use when making the final request. @@ -242,16 +301,20 @@ def request_url(self, request, proxies): :class:`HTTPAdapter `. :param request: The :class:`PreparedRequest ` being sent. - :param proxies: A dictionary of schemes to proxy URLs. + :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs. """ - proxies = proxies or {} + proxy = select_proxy(request.url, proxies) scheme = urlparse(request.url).scheme - proxy = proxies.get(scheme) - if proxy and scheme != 'https': - url, _ = urldefrag(request.url) - else: - url = request.path_url + is_proxied_http_request = (proxy and scheme != 'https') + using_socks_proxy = False + if proxy: + proxy_scheme = urlparse(proxy).scheme.lower() + using_socks_proxy = proxy_scheme.startswith('socks') + + url = request.path_url + if is_proxied_http_request and not using_socks_proxy: + url = urldefragauth(request.url) return url @@ -280,7 +343,6 @@ def proxy_headers(self, proxy): :class:`HTTPAdapter `. :param proxies: The url of the proxy being used for this request. - :param kwargs: Optional additional keyword arguments. """ headers = {} username, password = get_auth_from_url(proxy) @@ -296,7 +358,10 @@ def send(self, request, stream=False, timeout=None, verify=True, cert=None, prox :param request: The :class:`PreparedRequest ` being sent. :param stream: (optional) Whether to stream the request content. - :param timeout: (optional) The timeout on the request. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple :param verify: (optional) Whether to verify SSL certificates. :param cert: (optional) Any user-provided SSL certificate to be trusted. :param proxies: (optional) The proxies dictionary to apply to the request. @@ -310,7 +375,18 @@ def send(self, request, stream=False, timeout=None, verify=True, cert=None, prox chunked = not (request.body is None or 'Content-Length' in request.headers) - timeout = TimeoutSauce(connect=timeout, read=timeout) + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError as e: + # this may raise a string formatting error. + err = ("Invalid timeout {0}. Pass a (connect, read) " + "timeout tuple, or a single float to set " + "both timeouts to the same value".format(timeout)) + raise ValueError(err) + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) try: if not chunked: @@ -332,7 +408,7 @@ def send(self, request, stream=False, timeout=None, verify=True, cert=None, prox if hasattr(conn, 'proxy_pool'): conn = conn.proxy_pool - low_conn = conn._get_conn(timeout=timeout) + low_conn = conn._get_conn(timeout=DEFAULT_POOL_TIMEOUT) try: low_conn.putrequest(request.method, @@ -351,7 +427,15 @@ def send(self, request, stream=False, timeout=None, verify=True, cert=None, prox low_conn.send(b'\r\n') low_conn.send(b'0\r\n\r\n') - r = low_conn.getresponse() + # Receive the response from the server + try: + # For Python 2.7+ versions, use buffering of HTTP + # responses + r = low_conn.getresponse(buffering=True) + except TypeError: + # For compatibility with Python 2.6 versions and back + r = low_conn.getresponse() + resp = HTTPResponse.from_httplib( r, pool=conn, @@ -364,14 +448,25 @@ def send(self, request, stream=False, timeout=None, verify=True, cert=None, prox # Then, reraise so that we can handle the actual exception. low_conn.close() raise - else: - # All is well, return the connection to the pool. - conn._put_conn(low_conn) - except socket.error as sockerr: - raise ConnectionError(sockerr, request=request) + except (ProtocolError, socket.error) as err: + raise ConnectionError(err, request=request) except MaxRetryError as e: + if isinstance(e.reason, ConnectTimeoutError): + # TODO: Remove this in 3.0.0: see #2811 + if not isinstance(e.reason, NewConnectionError): + raise ConnectTimeout(e, request=request) + + if isinstance(e.reason, ResponseError): + raise RetryError(e, request=request) + + if isinstance(e.reason, _ProxyError): + raise ProxyError(e, request=request) + + raise ConnectionError(e, request=request) + + except ClosedPoolError as e: raise ConnectionError(e, request=request) except _ProxyError as e: @@ -380,8 +475,8 @@ def send(self, request, stream=False, timeout=None, verify=True, cert=None, prox except (_SSLError, _HTTPError) as e: if isinstance(e, _SSLError): raise SSLError(e, request=request) - elif isinstance(e, TimeoutError): - raise Timeout(e, request=request) + elif isinstance(e, ReadTimeoutError): + raise ReadTimeout(e, request=request) else: raise diff --git a/pip/_vendor/requests/api.py b/pip/_vendor/requests/api.py index 01d853d5cae..c2068d0eda9 100644 --- a/pip/_vendor/requests/api.py +++ b/pip/_vendor/requests/api.py @@ -16,22 +16,32 @@ def request(method, url, **kwargs): """Constructs and sends a :class:`Request `. - Returns :class:`Response ` object. :param method: method for the new :class:`Request` object. :param url: URL for the new :class:`Request` object. :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`. :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. + :param json: (optional) json data to send in the body of the :class:`Request`. :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`. - :param files: (optional) Dictionary of 'name': file-like-objects (or {'name': ('filename', fileobj)}) for multipart encoding upload. + :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload. + ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')`` + or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string + defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers + to add for the file. :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth. - :param timeout: (optional) Float describing the timeout of the request in seconds. + :param timeout: (optional) How long to wait for the server to send data + before giving up, as a float, or a :ref:`(connect timeout, read + timeout) ` tuple. + :type timeout: float or tuple :param allow_redirects: (optional) Boolean. Set to True if POST/PUT/DELETE redirect following is allowed. + :type allow_redirects: bool :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. - :param verify: (optional) if ``True``, the SSL cert will be verified. A CA_BUNDLE path can also be provided. + :param verify: (optional) whether the SSL cert will be verified. A CA_BUNDLE path can also be provided. Defaults to ``True``. :param stream: (optional) if ``False``, the response content will be immediately downloaded. :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. + :return: :class:`Response ` object + :rtype: requests.Response Usage:: @@ -40,26 +50,34 @@ def request(method, url, **kwargs): """ - session = sessions.Session() - return session.request(method=method, url=url, **kwargs) + # By using the 'with' statement we are sure the session is closed, thus we + # avoid leaving sockets open which can trigger a ResourceWarning in some + # cases, and look like a memory leak in others. + with sessions.Session() as session: + return session.request(method=method, url=url, **kwargs) -def get(url, **kwargs): - """Sends a GET request. Returns :class:`Response` object. +def get(url, params=None, **kwargs): + """Sends a GET request. :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`. :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response """ kwargs.setdefault('allow_redirects', True) - return request('get', url, **kwargs) + return request('get', url, params=params, **kwargs) def options(url, **kwargs): - """Sends a OPTIONS request. Returns :class:`Response` object. + """Sends a OPTIONS request. :param url: URL for the new :class:`Request` object. :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response """ kwargs.setdefault('allow_redirects', True) @@ -67,54 +85,65 @@ def options(url, **kwargs): def head(url, **kwargs): - """Sends a HEAD request. Returns :class:`Response` object. + """Sends a HEAD request. :param url: URL for the new :class:`Request` object. :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response """ kwargs.setdefault('allow_redirects', False) return request('head', url, **kwargs) -def post(url, data=None, **kwargs): - """Sends a POST request. Returns :class:`Response` object. +def post(url, data=None, json=None, **kwargs): + """Sends a POST request. :param url: URL for the new :class:`Request` object. :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. + :param json: (optional) json data to send in the body of the :class:`Request`. :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response """ - return request('post', url, data=data, **kwargs) + return request('post', url, data=data, json=json, **kwargs) def put(url, data=None, **kwargs): - """Sends a PUT request. Returns :class:`Response` object. + """Sends a PUT request. :param url: URL for the new :class:`Request` object. :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response """ return request('put', url, data=data, **kwargs) def patch(url, data=None, **kwargs): - """Sends a PATCH request. Returns :class:`Response` object. + """Sends a PATCH request. :param url: URL for the new :class:`Request` object. :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response """ return request('patch', url, data=data, **kwargs) def delete(url, **kwargs): - """Sends a DELETE request. Returns :class:`Response` object. + """Sends a DELETE request. :param url: URL for the new :class:`Request` object. :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response """ return request('delete', url, **kwargs) diff --git a/pip/_vendor/requests/auth.py b/pip/_vendor/requests/auth.py index 9f831b7ad03..73f8e9da8da 100644 --- a/pip/_vendor/requests/auth.py +++ b/pip/_vendor/requests/auth.py @@ -11,12 +11,14 @@ import re import time import hashlib +import threading from base64 import b64encode from .compat import urlparse, str from .cookies import extract_cookies_to_jar -from .utils import parse_dict_header +from .utils import parse_dict_header, to_native_string +from .status_codes import codes CONTENT_TYPE_FORM_URLENCODED = 'application/x-www-form-urlencoded' CONTENT_TYPE_MULTI_PART = 'multipart/form-data' @@ -25,7 +27,11 @@ def _basic_auth_str(username, password): """Returns a Basic Auth string.""" - return 'Basic ' + b64encode(('%s:%s' % (username, password)).encode('latin1')).strip().decode('latin1') + authstr = 'Basic ' + to_native_string( + b64encode(('%s:%s' % (username, password)).encode('latin1')).strip() + ) + + return authstr class AuthBase(object): @@ -41,6 +47,15 @@ def __init__(self, username, password): self.username = username self.password = password + def __eq__(self, other): + return all([ + self.username == getattr(other, 'username', None), + self.password == getattr(other, 'password', None) + ]) + + def __ne__(self, other): + return not self == other + def __call__(self, r): r.headers['Authorization'] = _basic_auth_str(self.username, self.password) return r @@ -58,18 +73,27 @@ class HTTPDigestAuth(AuthBase): def __init__(self, username, password): self.username = username self.password = password - self.last_nonce = '' - self.nonce_count = 0 - self.chal = {} - self.pos = None + # Keep state in per-thread local storage + self._thread_local = threading.local() + + def init_per_thread_state(self): + # Ensure state is initialized just once per-thread + if not hasattr(self._thread_local, 'init'): + self._thread_local.init = True + self._thread_local.last_nonce = '' + self._thread_local.nonce_count = 0 + self._thread_local.chal = {} + self._thread_local.pos = None + self._thread_local.num_401_calls = None def build_digest_header(self, method, url): - realm = self.chal['realm'] - nonce = self.chal['nonce'] - qop = self.chal.get('qop') - algorithm = self.chal.get('algorithm') - opaque = self.chal.get('opaque') + realm = self._thread_local.chal['realm'] + nonce = self._thread_local.chal['nonce'] + qop = self._thread_local.chal.get('qop') + algorithm = self._thread_local.chal.get('algorithm') + opaque = self._thread_local.chal.get('opaque') + hash_utf8 = None if algorithm is None: _algorithm = 'MD5' @@ -97,7 +121,8 @@ def sha_utf8(x): # XXX not implemented yet entdig = None p_parsed = urlparse(url) - path = p_parsed.path + #: path is request-uri defined in RFC 2616 which should not be empty + path = p_parsed.path or "/" if p_parsed.query: path += '?' + p_parsed.query @@ -107,30 +132,32 @@ def sha_utf8(x): HA1 = hash_utf8(A1) HA2 = hash_utf8(A2) - if nonce == self.last_nonce: - self.nonce_count += 1 + if nonce == self._thread_local.last_nonce: + self._thread_local.nonce_count += 1 else: - self.nonce_count = 1 - ncvalue = '%08x' % self.nonce_count - s = str(self.nonce_count).encode('utf-8') + self._thread_local.nonce_count = 1 + ncvalue = '%08x' % self._thread_local.nonce_count + s = str(self._thread_local.nonce_count).encode('utf-8') s += nonce.encode('utf-8') s += time.ctime().encode('utf-8') s += os.urandom(8) cnonce = (hashlib.sha1(s).hexdigest()[:16]) - noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, HA2) if _algorithm == 'MD5-SESS': HA1 = hash_utf8('%s:%s:%s' % (HA1, nonce, cnonce)) - if qop is None: + if not qop: respdig = KD(HA1, "%s:%s" % (nonce, HA2)) elif qop == 'auth' or 'auth' in qop.split(','): + noncebit = "%s:%s:%s:%s:%s" % ( + nonce, ncvalue, cnonce, 'auth', HA2 + ) respdig = KD(HA1, noncebit) else: # XXX handle auth-int. return None - self.last_nonce = nonce + self._thread_local.last_nonce = nonce # XXX should the partial digests be encoded too? base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \ @@ -146,26 +173,30 @@ def sha_utf8(x): return 'Digest %s' % (base) + def handle_redirect(self, r, **kwargs): + """Reset num_401_calls counter on redirects.""" + if r.is_redirect: + self._thread_local.num_401_calls = 1 + def handle_401(self, r, **kwargs): """Takes the given response and tries digest-auth, if needed.""" - if self.pos is not None: + if self._thread_local.pos is not None: # Rewind the file position indicator of the body to where # it was to resend the request. - r.request.body.seek(self.pos) - num_401_calls = getattr(self, 'num_401_calls', 1) + r.request.body.seek(self._thread_local.pos) s_auth = r.headers.get('www-authenticate', '') - if 'digest' in s_auth.lower() and num_401_calls < 2: + if 'digest' in s_auth.lower() and self._thread_local.num_401_calls < 2: - setattr(self, 'num_401_calls', num_401_calls + 1) + self._thread_local.num_401_calls += 1 pat = re.compile(r'digest ', flags=re.IGNORECASE) - self.chal = parse_dict_header(pat.sub('', s_auth, count=1)) + self._thread_local.chal = parse_dict_header(pat.sub('', s_auth, count=1)) # Consume content and release the original connection # to allow our new request to reuse the same one. r.content - r.raw.release_conn() + r.close() prep = r.request.copy() extract_cookies_to_jar(prep._cookies, r.request, r.raw) prep.prepare_cookies(prep._cookies) @@ -178,16 +209,34 @@ def handle_401(self, r, **kwargs): return _r - setattr(self, 'num_401_calls', 1) + self._thread_local.num_401_calls = 1 return r def __call__(self, r): + # Initialize per-thread state, if needed + self.init_per_thread_state() # If we have a saved nonce, skip the 401 - if self.last_nonce: + if self._thread_local.last_nonce: r.headers['Authorization'] = self.build_digest_header(r.method, r.url) try: - self.pos = r.body.tell() + self._thread_local.pos = r.body.tell() except AttributeError: - pass + # In the case of HTTPDigestAuth being reused and the body of + # the previous request was a file-like object, pos has the + # file position of the previous body. Ensure it's set to + # None. + self._thread_local.pos = None r.register_hook('response', self.handle_401) + r.register_hook('response', self.handle_redirect) + self._thread_local.num_401_calls = 1 + return r + + def __eq__(self, other): + return all([ + self.username == getattr(other, 'username', None), + self.password == getattr(other, 'password', None) + ]) + + def __ne__(self, other): + return not self == other diff --git a/pip/_vendor/requests/cacert.pem b/pip/_vendor/requests/cacert.pem index 729fe15d400..6a66daa9979 100644 --- a/pip/_vendor/requests/cacert.pem +++ b/pip/_vendor/requests/cacert.pem @@ -1,83 +1,3 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -# Issuer: CN=GTE CyberTrust Global Root O=GTE Corporation OU=GTE CyberTrust Solutions, Inc. -# Subject: CN=GTE CyberTrust Global Root O=GTE Corporation OU=GTE CyberTrust Solutions, Inc. -# Label: "GTE CyberTrust Global Root" -# Serial: 421 -# MD5 Fingerprint: ca:3d:d3:68:f1:03:5c:d0:32:fa:b8:2b:59:e8:5a:db -# SHA1 Fingerprint: 97:81:79:50:d8:1c:96:70:cc:34:d8:09:cf:79:44:31:36:7e:f4:74 -# SHA256 Fingerprint: a5:31:25:18:8d:21:10:aa:96:4b:02:c7:b7:c6:da:32:03:17:08:94:e5:fb:71:ff:fb:66:67:d5:e6:81:0a:36 ------BEGIN CERTIFICATE----- -MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD -VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv -bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv -b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV -UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU -cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds -b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH -iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS -r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4 -04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r -GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9 -3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P -lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ ------END CERTIFICATE----- - -# Issuer: CN=Thawte Server CA O=Thawte Consulting cc OU=Certification Services Division -# Subject: CN=Thawte Server CA O=Thawte Consulting cc OU=Certification Services Division -# Label: "Thawte Server CA" -# Serial: 1 -# MD5 Fingerprint: c5:70:c4:a2:ed:53:78:0c:c8:10:53:81:64:cb:d0:1d -# SHA1 Fingerprint: 23:e5:94:94:51:95:f2:41:48:03:b4:d5:64:d2:a3:a3:f5:d8:8b:8c -# SHA256 Fingerprint: b4:41:0b:73:e2:e6:ea:ca:47:fb:c4:2f:8f:a4:01:8a:f4:38:1d:c5:4c:fa:a8:44:50:46:1e:ed:09:45:4d:e9 ------BEGIN CERTIFICATE----- -MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkEx -FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD -VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv -biBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEm -MCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wHhcNOTYwODAx -MDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT -DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3 -dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNl -cyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3 -DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD -gY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91 -yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCX -L+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGj -EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG -7oWDTSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e -QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZ -qdq5snUb9kLy78fyGPmJvKP/iiMucEc= ------END CERTIFICATE----- - -# Issuer: CN=Thawte Premium Server CA O=Thawte Consulting cc OU=Certification Services Division -# Subject: CN=Thawte Premium Server CA O=Thawte Consulting cc OU=Certification Services Division -# Label: "Thawte Premium Server CA" -# Serial: 1 -# MD5 Fingerprint: 06:9f:69:79:16:66:90:02:1b:8c:8c:a2:c3:07:6f:3a -# SHA1 Fingerprint: 62:7f:8d:78:27:65:63:99:d2:7d:7f:90:44:c9:fe:b3:f3:3e:fa:9a -# SHA256 Fingerprint: ab:70:36:36:5c:71:54:aa:29:c2:c2:9f:5d:41:91:16:3b:16:2a:22:25:01:13:57:d5:6d:07:ff:a7:bc:1f:72 ------BEGIN CERTIFICATE----- -MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkEx -FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD -VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv -biBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2Vy -dmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29t -MB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpB -MRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsG -A1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRp -b24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNl -cnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNv -bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkE -VdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQ -ug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMR -uHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG -9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI -hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JM -pAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg== ------END CERTIFICATE----- # Issuer: O=Equifax OU=Equifax Secure Certificate Authority # Subject: O=Equifax OU=Equifax Secure Certificate Authority @@ -106,55 +26,6 @@ A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y 1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 -----END CERTIFICATE----- -# Issuer: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority -# Subject: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority -# Label: "Verisign Class 3 Public Primary Certification Authority" -# Serial: 149843929435818692848040365716851702463 -# MD5 Fingerprint: 10:fc:63:5d:f6:26:3e:0d:f3:25:be:5f:79:cd:67:67 -# SHA1 Fingerprint: 74:2c:31:92:e6:07:e4:24:eb:45:49:54:2b:e1:bb:c5:3e:61:74:e2 -# SHA256 Fingerprint: e7:68:56:34:ef:ac:f6:9a:ce:93:9a:6b:25:5b:7b:4f:ab:ef:42:93:5b:50:a2:65:ac:b5:cb:60:27:e4:4e:70 ------BEGIN CERTIFICATE----- -MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG -A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz -cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 -MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV -BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt -YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN -ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE -BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is -I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G -CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do -lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc -AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k ------END CERTIFICATE----- - -# Issuer: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority - G2/(c) 1998 VeriSign, Inc. - For authorized use only/VeriSign Trust Network -# Subject: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority - G2/(c) 1998 VeriSign, Inc. - For authorized use only/VeriSign Trust Network -# Label: "Verisign Class 3 Public Primary Certification Authority - G2" -# Serial: 167285380242319648451154478808036881606 -# MD5 Fingerprint: a2:33:9b:4c:74:78:73:d4:6c:e7:c1:f3:8d:cb:5c:e9 -# SHA1 Fingerprint: 85:37:1c:a6:e5:50:14:3d:ce:28:03:47:1b:de:3a:09:e8:f8:77:0f -# SHA256 Fingerprint: 83:ce:3c:12:29:68:8a:59:3d:48:5f:81:97:3c:0f:91:95:43:1e:da:37:cc:5e:36:43:0e:79:c7:a8:88:63:8b ------BEGIN CERTIFICATE----- -MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ -BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh -c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy -MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp -emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X -DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw -FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg -UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo -YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5 -MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB -AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4 -pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0 -13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID -AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk -U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i -F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY -oJ2daZH9 ------END CERTIFICATE----- - # Issuer: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA # Subject: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA # Label: "GlobalSign Root CA" @@ -214,84 +85,6 @@ AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== -----END CERTIFICATE----- -# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 1 Policy Validation Authority -# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 1 Policy Validation Authority -# Label: "ValiCert Class 1 VA" -# Serial: 1 -# MD5 Fingerprint: 65:58:ab:15:ad:57:6c:1e:a8:a7:b5:69:ac:bf:ff:eb -# SHA1 Fingerprint: e5:df:74:3c:b6:01:c4:9b:98:43:dc:ab:8c:e8:6a:81:10:9f:e4:8e -# SHA256 Fingerprint: f4:c1:49:55:1a:30:13:a3:5b:c7:bf:fe:17:a7:f3:44:9b:c1:ab:5b:5a:0a:e7:4b:06:c2:3b:90:00:4c:01:04 ------BEGIN CERTIFICATE----- -MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 -IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz -BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y -aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG -9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIyMjM0OFoXDTE5MDYy -NTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y -azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs -YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw -Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl -cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9Y -LqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+ -TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8Y -TfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0 -LBwGlN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLW -I8sogTLDAHkY7FkXicnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPw -nXS3qT6gpf+2SQMT2iLM7XGCK5nPOrf1LXLI ------END CERTIFICATE----- - -# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 2 Policy Validation Authority -# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 2 Policy Validation Authority -# Label: "ValiCert Class 2 VA" -# Serial: 1 -# MD5 Fingerprint: a9:23:75:9b:ba:49:36:6e:31:c2:db:f2:e7:66:ba:87 -# SHA1 Fingerprint: 31:7a:2a:d0:7f:2b:33:5e:f5:a1:c3:4e:4b:57:e8:b7:d8:f1:fc:a6 -# SHA256 Fingerprint: 58:d0:17:27:9c:d4:dc:63:ab:dd:b1:96:a6:c9:90:6c:30:c4:e0:87:83:ea:e8:c1:60:99:54:d6:93:55:59:6b ------BEGIN CERTIFICATE----- -MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 -IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz -BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y -aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG -9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy -NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y -azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs -YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw -Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl -cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY -dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9 -WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS -v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v -UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu -IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC -W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd ------END CERTIFICATE----- - -# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 3 Policy Validation Authority -# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 3 Policy Validation Authority -# Label: "RSA Root Certificate 1" -# Serial: 1 -# MD5 Fingerprint: a2:6f:53:b7:ee:40:db:4a:68:e7:fa:18:d9:10:4b:72 -# SHA1 Fingerprint: 69:bd:8c:f4:9c:d3:00:fb:59:2e:17:93:ca:55:6a:f3:ec:aa:35:fb -# SHA256 Fingerprint: bc:23:f9:8a:31:3c:b9:2d:e3:bb:fc:3a:5a:9f:44:61:ac:39:49:4c:4a:e1:5a:9e:9d:f1:31:e9:9b:73:01:9a ------BEGIN CERTIFICATE----- -MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 -IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz -BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y -aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG -9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMjIzM1oXDTE5MDYy -NjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y -azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs -YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw -Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl -cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfD -cnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs -2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqY -JJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliE -Zwgs3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJ -n0WuPIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A -PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu ------END CERTIFICATE----- - # Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only # Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only # Label: "Verisign Class 3 Public Primary Certification Authority - G3" @@ -356,42 +149,6 @@ fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c bLvSHgCwIe34QWKCudiyxLtGUPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg== -----END CERTIFICATE----- -# Issuer: CN=Entrust.net Secure Server Certification Authority O=Entrust.net OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited -# Subject: CN=Entrust.net Secure Server Certification Authority O=Entrust.net OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited -# Label: "Entrust.net Secure Server CA" -# Serial: 927650371 -# MD5 Fingerprint: df:f2:80:73:cc:f1:e6:61:73:fc:f5:42:e9:c5:7c:ee -# SHA1 Fingerprint: 99:a6:9b:e6:1a:fe:88:6b:4d:2b:82:00:7c:b8:54:fc:31:7e:15:39 -# SHA256 Fingerprint: 62:f2:40:27:8c:56:4c:4d:d8:bf:7d:9d:4f:6f:36:6e:a8:94:d2:2f:5f:34:d9:89:a9:83:ac:ec:2f:ff:ed:50 ------BEGIN CERTIFICATE----- -MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC -VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u -ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc -KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u -ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1 -MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE -ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j -b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF -bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg -U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA -A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/ -I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3 -wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC -AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb -oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5 -BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p -dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk -MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp -b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu -dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0 -MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi -E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa -MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI -hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN -95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd -2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= ------END CERTIFICATE----- - # Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited # Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited # Label: "Entrust.net Premium 2048 Secure Server CA" @@ -454,61 +211,13 @@ ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp -----END CERTIFICATE----- -# Issuer: CN=Equifax Secure Global eBusiness CA-1 O=Equifax Secure Inc. -# Subject: CN=Equifax Secure Global eBusiness CA-1 O=Equifax Secure Inc. -# Label: "Equifax Secure Global eBusiness CA" +# Issuer: CN=AddTrust Class 1 CA Root O=AddTrust AB OU=AddTrust TTP Network +# Subject: CN=AddTrust Class 1 CA Root O=AddTrust AB OU=AddTrust TTP Network +# Label: "AddTrust Low-Value Services Root" # Serial: 1 -# MD5 Fingerprint: 8f:5d:77:06:27:c4:98:3c:5b:93:78:e7:d7:7d:9b:cc -# SHA1 Fingerprint: 7e:78:4a:10:1c:82:65:cc:2d:e1:f1:6d:47:b4:40:ca:d9:0a:19:45 -# SHA256 Fingerprint: 5f:0b:62:ea:b5:e3:53:ea:65:21:65:16:58:fb:b6:53:59:f4:43:28:0a:4a:fb:d1:04:d7:7d:10:f9:f0:4c:07 ------BEGIN CERTIFICATE----- -MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEc -MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBT -ZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIw -MDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0VxdWlmYXggU2Vj -dXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEdsb2JhbCBlQnVzaW5l -c3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRVPEnC -UdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc -58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/ -o5brhTMhHD4ePmBudpxnhcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAH -MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1dr -aGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUA -A4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkA -Z70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv -8qIYNMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV ------END CERTIFICATE----- - -# Issuer: CN=Equifax Secure eBusiness CA-1 O=Equifax Secure Inc. -# Subject: CN=Equifax Secure eBusiness CA-1 O=Equifax Secure Inc. -# Label: "Equifax Secure eBusiness CA 1" -# Serial: 4 -# MD5 Fingerprint: 64:9c:ef:2e:44:fc:c6:8f:52:07:d0:51:73:8f:cb:3d -# SHA1 Fingerprint: da:40:18:8b:91:89:a3:ed:ee:ae:da:97:fe:2f:9d:f5:b7:d1:8a:41 -# SHA256 Fingerprint: cf:56:ff:46:a4:a1:86:10:9d:d9:65:84:b5:ee:b5:8a:51:0c:42:75:b0:e5:f9:4f:40:bb:ae:86:5e:19:f6:73 ------BEGIN CERTIFICATE----- -MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEc -MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBT -ZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQw -MDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5j -LjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwgZ8wDQYJ -KoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ1MRo -RvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBu -WqDZQu4aIZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKw -Env+j6YDAgMBAAGjZjBkMBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTAD -AQH/MB8GA1UdIwQYMBaAFEp4MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRK -eDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZM -zfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+ -WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN -/Bf+KpYrtWKmpj29f5JZzVoqgrI3eQ== ------END CERTIFICATE----- - -# Issuer: CN=AddTrust Class 1 CA Root O=AddTrust AB OU=AddTrust TTP Network -# Subject: CN=AddTrust Class 1 CA Root O=AddTrust AB OU=AddTrust TTP Network -# Label: "AddTrust Low-Value Services Root" -# Serial: 1 -# MD5 Fingerprint: 1e:42:95:02:33:92:6b:b9:5f:c0:7f:da:d6:b2:4b:fc -# SHA1 Fingerprint: cc:ab:0e:a0:4c:23:01:d6:69:7b:dd:37:9f:cd:12:eb:24:e3:94:9d -# SHA256 Fingerprint: 8c:72:09:27:9a:c0:4e:27:5e:16:d0:7f:d3:b7:75:e8:01:54:b5:96:80:46:e3:1f:52:dd:25:76:63:24:e9:a7 +# MD5 Fingerprint: 1e:42:95:02:33:92:6b:b9:5f:c0:7f:da:d6:b2:4b:fc +# SHA1 Fingerprint: cc:ab:0e:a0:4c:23:01:d6:69:7b:dd:37:9f:cd:12:eb:24:e3:94:9d +# SHA256 Fingerprint: 8c:72:09:27:9a:c0:4e:27:5e:16:d0:7f:d3:b7:75:e8:01:54:b5:96:80:46:e3:1f:52:dd:25:76:63:24:e9:a7 -----BEGIN CERTIFICATE----- MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEU MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 @@ -831,77 +540,6 @@ OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS -----END CERTIFICATE----- -# Issuer: CN=America Online Root Certification Authority 1 O=America Online Inc. -# Subject: CN=America Online Root Certification Authority 1 O=America Online Inc. -# Label: "America Online Root Certification Authority 1" -# Serial: 1 -# MD5 Fingerprint: 14:f1:08:ad:9d:fa:64:e2:89:e7:1c:cf:a8:ad:7d:5e -# SHA1 Fingerprint: 39:21:c1:15:c1:5d:0e:ca:5c:cb:5b:c4:f0:7d:21:d8:05:0b:56:6a -# SHA256 Fingerprint: 77:40:73:12:c6:3a:15:3d:5b:c0:0b:4e:51:75:9c:df:da:c2:37:dc:2a:33:b6:79:46:e9:8e:9b:fa:68:0a:e3 ------BEGIN CERTIFICATE----- -MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc -MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP -bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2 -MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft -ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg -Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lk -hsmj76CGv2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym -1BW32J/X3HGrfpq/m44zDyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsW -OqMFf6Dch9Wc/HKpoH145LcxVR5lu9RhsCFg7RAycsWSJR74kEoYeEfffjA3PlAb -2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP8c9GsEsPPt2IYriMqQko -O3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0TAQH/BAUw -AwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAU -AK3Zo/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB -BQUAA4IBAQB8itEfGDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkF -Zu90821fnZmv9ov761KyBZiibyrFVL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAb -LjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft3OJvx8Fi8eNy1gTIdGcL+oir -oQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43gKd8hdIaC2y+C -MMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds -sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7 ------END CERTIFICATE----- - -# Issuer: CN=America Online Root Certification Authority 2 O=America Online Inc. -# Subject: CN=America Online Root Certification Authority 2 O=America Online Inc. -# Label: "America Online Root Certification Authority 2" -# Serial: 1 -# MD5 Fingerprint: d6:ed:3c:ca:e2:66:0f:af:10:43:0d:77:9b:04:09:bf -# SHA1 Fingerprint: 85:b5:ff:67:9b:0c:79:96:1f:c8:6e:44:22:00:46:13:db:17:92:84 -# SHA256 Fingerprint: 7d:3b:46:5a:60:14:e5:26:c0:af:fc:ee:21:27:d2:31:17:27:ad:81:1c:26:84:2d:00:6a:f3:73:06:cc:80:bd ------BEGIN CERTIFICATE----- -MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc -MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP -bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2 -MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft -ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg -Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIP -ADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC -206B89enfHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFci -KtZHgVdEglZTvYYUAQv8f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2 -JxhP7JsowtS013wMPgwr38oE18aO6lhOqKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9 -BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JNRvCAOVIyD+OEsnpD8l7e -Xz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0gBe4lL8B -PeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67 -Xnfn6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEq -Z8A9W6Wa6897GqidFEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZ -o2C7HK2JNDJiuEMhBnIMoVxtRsX6Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3 -+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnjB453cMor9H124HhnAgMBAAGj -YzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3OpaaEg5+31IqEj -FNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE -AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmn -xPBUlgtk87FYT15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2 -LHo1YGwRgJfMqZJS5ivmae2p+DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzccc -obGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXgJXUjhx5c3LqdsKyzadsXg8n33gy8 -CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//ZoyzH1kUQ7rVyZ2OuMe -IjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgOZtMA -DjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2F -AjgQ5ANh1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUX -Om/9riW99XJZZLF0KjhfGEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPb -AZO1XB4Y3WRayhgoPmMEEf0cjQAPuDffZ4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQl -Zvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuPcX/9XhmgD0uRuMRUvAaw -RY8mkaKO/qk= ------END CERTIFICATE----- - # Issuer: CN=Visa eCommerce Root O=VISA OU=Visa International Service Association # Subject: CN=Visa eCommerce Root O=VISA OU=Visa International Service Association # Label: "Visa eCommerce Root" @@ -1272,39 +910,6 @@ u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3ynGQI0DvDKcWy iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw== -----END CERTIFICATE----- -# Issuer: O=TDC Internet OU=TDC Internet Root CA -# Subject: O=TDC Internet OU=TDC Internet Root CA -# Label: "TDC Internet Root CA" -# Serial: 986490188 -# MD5 Fingerprint: 91:f4:03:55:20:a1:f8:63:2c:62:de:ac:fb:61:1c:8e -# SHA1 Fingerprint: 21:fc:bd:8e:7f:6c:af:05:1b:d1:b3:43:ec:a8:e7:61:47:f2:0f:8a -# SHA256 Fingerprint: 48:98:c6:88:8c:0c:ff:b0:d3:e3:1a:ca:8a:37:d4:e3:51:5f:f7:46:d0:26:35:d8:66:46:cf:a0:a3:18:5a:e7 ------BEGIN CERTIFICATE----- -MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJE -SzEVMBMGA1UEChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQg -Um9vdCBDQTAeFw0wMTA0MDUxNjMzMTdaFw0yMTA0MDUxNzAzMTdaMEMxCzAJBgNV -BAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJuZXQxHTAbBgNVBAsTFFREQyBJbnRl -cm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxLhA -vJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4NrXceO+YQwzho7+vvOi20jxsNu -Zp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaqHF1j4QeGDmUApy6mcca8uYGoOn0a -0vnRrEvLznWv3Hv6gXPU/Lq9QYjUdLP5Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc1 -4izbSysseLlJ28TQx5yc5IogCSEWVmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGN -eGlVRGn1ypYcNIUXJXfi9i8nmHj9eQY6otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcD -R0G2l8ktCkEiu7vmpwIDAQABo4IBJTCCASEwEQYJYIZIAYb4QgEBBAQDAgAHMGUG -A1UdHwReMFwwWqBYoFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMMVERDIElu -dGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxME -Q1JMMTArBgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3 -WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAw -HQYDVR0OBBYEFGxkAcf9hW2syNqeUAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8wHQYJ -KoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4IBAQBO -Q8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540mgwV5dOy0uaOX -wTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKsLtB9KOy282A4aW8+ -2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7TmHnaCB4Mb7j4Fifvwm89 -9qNLPg7kbWzbO0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0 -jUNAE4z9mQNUecYu6oah9jrUCbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38 -aQNiuJkFBT1reBK9sG9l ------END CERTIFICATE----- - # Issuer: CN=UTN - DATACorp SGC O=The USERTRUST Network OU=http://www.usertrust.com # Subject: CN=UTN - DATACorp SGC O=The USERTRUST Network OU=http://www.usertrust.com # Label: "UTN DATACorp SGC Root CA" @@ -1490,84 +1095,6 @@ f1qbFFgBJ34TUMdrKuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK 8CtmdWOMovsEPoMOmzbwGOQmIMOM8CgHrTwXZoi1/baI -----END CERTIFICATE----- -# Issuer: CN=NetLock Uzleti (Class B) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok -# Subject: CN=NetLock Uzleti (Class B) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok -# Label: "NetLock Business (Class B) Root" -# Serial: 105 -# MD5 Fingerprint: 39:16:aa:b9:6a:41:e1:14:69:df:9e:6c:3b:72:dc:b6 -# SHA1 Fingerprint: 87:9f:4b:ee:05:df:98:58:3b:e3:60:d6:33:e7:0d:3f:fe:98:71:af -# SHA256 Fingerprint: 39:df:7b:68:2b:7b:93:8f:84:71:54:81:cc:de:8d:60:d8:f2:2e:c5:98:87:7d:0a:aa:c1:2b:59:18:2b:03:12 ------BEGIN CERTIFICATE----- -MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUx -ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 -b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQD -EylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikgVGFudXNpdHZhbnlraWFkbzAeFw05 -OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYDVQQGEwJIVTERMA8G -A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh -Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5l -dExvY2sgVXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqG -SIb3DQEBAQUAA4GNADCBiQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xK -gZjupNTKihe5In+DCnVMm8Bp2GQ5o+2So/1bXHQawEfKOml2mrriRBf8TKPV/riX -iK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr1nGTLbO/CVRY7QbrqHvc -Q7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8E -BAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1G -SUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFu -b3MgU3pvbGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBh -bGFwamFuIGtlc3p1bHQuIEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExv -Y2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGln -aXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0 -IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh -c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGph -biBhIGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJo -ZXRvIGF6IGVsbGVub3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBP -UlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmlj -YXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBo -dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNA -bmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06 -sPgzTEdM43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXa -n3BukxowOR0w2y7jfLKRstE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKS -NitjrFgBazMpUIaD8QFI ------END CERTIFICATE----- - -# Issuer: CN=NetLock Expressz (Class C) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok -# Subject: CN=NetLock Expressz (Class C) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok -# Label: "NetLock Express (Class C) Root" -# Serial: 104 -# MD5 Fingerprint: 4f:eb:f1:f0:70:c2:80:63:5d:58:9f:da:12:3c:a9:c4 -# SHA1 Fingerprint: e3:92:51:2f:0a:cf:f5:05:df:f6:de:06:7f:75:37:e1:65:ea:57:4b -# SHA256 Fingerprint: 0b:5e:ed:4e:84:64:03:cf:55:e0:65:84:84:40:ed:2a:82:75:8b:f5:b9:aa:1f:25:3d:46:13:cf:a0:80:ff:3f ------BEGIN CERTIFICATE----- -MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUx -ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 -b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQD -EytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBDKSBUYW51c2l0dmFueWtpYWRvMB4X -DTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJBgNVBAYTAkhVMREw -DwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9u -c2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMr -TmV0TG9jayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzAN -BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNA -OoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3ZW3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC -2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63euyucYT2BDMIJTLrdKwW -RMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQwDgYDVR0P -AQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEW -ggJNRklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0 -YWxhbm9zIFN6b2xnYWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFz -b2sgYWxhcGphbiBrZXN6dWx0LiBBIGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBO -ZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1iaXp0b3NpdGFzYSB2ZWRpLiBB -IGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0ZWxlIGF6IGVs -b2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs -ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25s -YXBqYW4gYSBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kg -a2VyaGV0byBheiBlbGxlbm9yemVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4g -SU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5kIHRoZSB1c2Ugb2YgdGhpcyBjZXJ0 -aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQUyBhdmFpbGFibGUg -YXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwgYXQg -Y3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmY -ta3UzbM2xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2g -pO0u9f38vf5NNwgMvOOWgyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4 -Fp1hBWeAyNDYpQcCNJgEjTME1A== ------END CERTIFICATE----- - # Issuer: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com # Subject: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com # Label: "XRamp Global CA Root" @@ -1757,40 +1284,6 @@ LMDDav7v3Aun+kbfYNucpllQdSNpc5Oy+fwC00fmcc4QAu4njIT/rEUNE1yDMuAl pYYsfPQS -----END CERTIFICATE----- -# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 -# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 -# Label: "Firmaprofesional Root CA" -# Serial: 1 -# MD5 Fingerprint: 11:92:79:40:3c:b1:83:40:e5:ab:66:4a:67:92:80:df -# SHA1 Fingerprint: a9:62:8f:4b:98:a9:1b:48:35:ba:d2:c1:46:32:86:bb:66:64:6a:8c -# SHA256 Fingerprint: c1:cf:0b:52:09:64:35:e3:f1:b7:1d:aa:ec:45:5a:23:11:c8:40:4f:55:83:a9:e2:13:c6:9d:85:7d:94:33:05 ------BEGIN CERTIFICATE----- -MIIEVzCCAz+gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnTELMAkGA1UEBhMCRVMx -IjAgBgNVBAcTGUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMTOUF1 -dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 -MjYzNDA2ODEmMCQGCSqGSIb3DQEJARYXY2FAZmlybWFwcm9mZXNpb25hbC5jb20w -HhcNMDExMDI0MjIwMDAwWhcNMTMxMDI0MjIwMDAwWjCBnTELMAkGA1UEBhMCRVMx -IjAgBgNVBAcTGUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMTOUF1 -dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 -MjYzNDA2ODEmMCQGCSqGSIb3DQEJARYXY2FAZmlybWFwcm9mZXNpb25hbC5jb20w -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDnIwNvbyOlXnjOlSztlB5u -Cp4Bx+ow0Syd3Tfom5h5VtP8c9/Qit5Vj1H5WuretXDE7aTt/6MNbg9kUDGvASdY -rv5sp0ovFy3Tc9UTHI9ZpTQsHVQERc1ouKDAA6XPhUJHlShbz++AbOCQl4oBPB3z -hxAwJkh91/zpnZFx/0GaqUC1N5wpIE8fUuOgfRNtVLcK3ulqTgesrBlf3H5idPay -BQC6haD9HThuy1q7hryUZzM1gywfI834yJFxzJeL764P3CkDG8A563DtwW4O2GcL -iam8NeTvtjS0pbbELaW+0MOUJEjb35bTALVmGotmBQ/dPz/LP6pemkr4tErvlTcb -AgMBAAGjgZ8wgZwwKgYDVR0RBCMwIYYfaHR0cDovL3d3dy5maXJtYXByb2Zlc2lv -bmFsLmNvbTASBgNVHRMBAf8ECDAGAQH/AgEBMCsGA1UdEAQkMCKADzIwMDExMDI0 -MjIwMDAwWoEPMjAxMzEwMjQyMjAwMDBaMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E -FgQUMwugZtHq2s7eYpMEKFK1FH84aLcwDQYJKoZIhvcNAQEFBQADggEBAEdz/o0n -VPD11HecJ3lXV7cVVuzH2Fi3AQL0M+2TUIiefEaxvT8Ub/GzR0iLjJcG1+p+o1wq -u00vR+L4OQbJnC4xGgN49Lw4xiKLMzHwFgQEffl25EvXwOaD7FnMP97/T2u3Z36m -hoEyIwOdyPdfwUpgpZKpsaSgYMN4h7Mi8yrrW6ntBas3D7Hi05V2Y1Z0jFhyGzfl -ZKG+TQyTmAyX9odtsz/ny4Cm7YjHX1BiAuiZdBbQ5rQ58SfLyEDW44YQqSMSkuBp -QWOnryULwMWSyx6Yo1q6xTMPoJcB3X/ge9YGVM+h4k0460tQtcsm9MracEpqoeJ5 -quGnM/b9Sh/22WA= ------END CERTIFICATE----- - # Issuer: CN=Swisscom Root CA 1 O=Swisscom OU=Digital Certificate Services # Subject: CN=Swisscom Root CA 1 O=Swisscom OU=Digital Certificate Services # Label: "Swisscom Root CA 1" @@ -2014,38 +1507,6 @@ rscL9yuwNwXsvFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf2 9w4LTJxoeHtxMcfrHuBnQfO3oKfN5XozNmr6mis= -----END CERTIFICATE----- -# Issuer: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=(c) 2005 TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. -# Subject: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=(c) 2005 TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. -# Label: "TURKTRUST Certificate Services Provider Root 1" -# Serial: 1 -# MD5 Fingerprint: f1:6a:22:18:c9:cd:df:ce:82:1d:1d:b7:78:5c:a9:a5 -# SHA1 Fingerprint: 79:98:a3:08:e1:4d:65:85:e6:c2:1e:15:3a:71:9f:ba:5a:d3:4a:d9 -# SHA256 Fingerprint: 44:04:e3:3b:5e:14:0d:cf:99:80:51:fd:fc:80:28:c7:c8:16:15:c5:ee:73:7b:11:1b:58:82:33:a9:b5:35:a0 ------BEGIN CERTIFICATE----- -MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOc -UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx -c8SxMQswCQYDVQQGDAJUUjEPMA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykg -MjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8 -dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMxMDI3MTdaFw0xNTAz -MjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsgU2Vy -dGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYD -VQQHDAZBTktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kg -xLBsZXRpxZ9pbSB2ZSBCaWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEu -xZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7 -XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GXyGl8hMW0kWxsE2qkVa2k -heiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8iSi9BB35J -YbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5C -urKZ8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1 -JuTm5Rh8i27fbMx4W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51 -b0dewQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV -9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46sWrv7/hg0Uw2ZkUd82YCdAR7 -kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxEq8Sn5RTOPEFh -fEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy -B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdA -aLX/7KfS0zgYnNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKS -RGQDJereW26fyfJOrN3H ------END CERTIFICATE----- - # Issuer: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Kasım 2005 # Subject: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Kasım 2005 # Label: "TURKTRUST Certificate Services Provider Root 2" @@ -2617,152 +2078,6 @@ t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== -----END CERTIFICATE----- -# Issuer: CN=AC Raíz Certicámara S.A. O=Sociedad Cameral de Certificación Digital - Certicámara S.A. -# Subject: CN=AC Raíz Certicámara S.A. O=Sociedad Cameral de Certificación Digital - Certicámara S.A. -# Label: "AC Ra\xC3\xADz Certic\xC3\xA1mara S.A." -# Serial: 38908203973182606954752843738508300 -# MD5 Fingerprint: 93:2a:3e:f6:fd:23:69:0d:71:20:d4:2b:47:99:2b:a6 -# SHA1 Fingerprint: cb:a1:c5:f8:b0:e3:5e:b8:b9:45:12:d3:f9:34:a2:e9:06:10:d3:36 -# SHA256 Fingerprint: a6:c5:1e:0d:a5:ca:0a:93:09:d2:e4:c0:e4:0c:2a:f9:10:7a:ae:82:03:85:7f:e1:98:e3:e7:69:e3:43:08:5c ------BEGIN CERTIFICATE----- -MIIGZjCCBE6gAwIBAgIPB35Sk3vgFeNX8GmMy+wMMA0GCSqGSIb3DQEBBQUAMHsx -CzAJBgNVBAYTAkNPMUcwRQYDVQQKDD5Tb2NpZWRhZCBDYW1lcmFsIGRlIENlcnRp -ZmljYWNpw7NuIERpZ2l0YWwgLSBDZXJ0aWPDoW1hcmEgUy5BLjEjMCEGA1UEAwwa -QUMgUmHDrXogQ2VydGljw6FtYXJhIFMuQS4wHhcNMDYxMTI3MjA0NjI5WhcNMzAw -NDAyMjE0MjAyWjB7MQswCQYDVQQGEwJDTzFHMEUGA1UECgw+U29jaWVkYWQgQ2Ft -ZXJhbCBkZSBDZXJ0aWZpY2FjacOzbiBEaWdpdGFsIC0gQ2VydGljw6FtYXJhIFMu -QS4xIzAhBgNVBAMMGkFDIFJhw616IENlcnRpY8OhbWFyYSBTLkEuMIICIjANBgkq -hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq2uJo1PMSCMI+8PPUZYILrgIem08kBeG -qentLhM0R7LQcNzJPNCNyu5LF6vQhbCnIwTLqKL85XXbQMpiiY9QngE9JlsYhBzL -fDe3fezTf3MZsGqy2IiKLUV0qPezuMDU2s0iiXRNWhU5cxh0T7XrmafBHoi0wpOQ -Y5fzp6cSsgkiBzPZkc0OnB8OIMfuuzONj8LSWKdf/WU34ojC2I+GdV75LaeHM/J4 -Ny+LvB2GNzmxlPLYvEqcgxhaBvzz1NS6jBUJJfD5to0EfhcSM2tXSExP2yYe68yQ -54v5aHxwD6Mq0Do43zeX4lvegGHTgNiRg0JaTASJaBE8rF9ogEHMYELODVoqDA+b -MMCm8Ibbq0nXl21Ii/kDwFJnmxL3wvIumGVC2daa49AZMQyth9VXAnow6IYm+48j -ilSH5L887uvDdUhfHjlvgWJsxS3EF1QZtzeNnDeRyPYL1epjb4OsOMLzP96a++Ej -YfDIJss2yKHzMI+ko6Kh3VOz3vCaMh+DkXkwwakfU5tTohVTP92dsxA7SH2JD/zt -A/X7JWR1DhcZDY8AFmd5ekD8LVkH2ZD6mq093ICK5lw1omdMEWux+IBkAC1vImHF -rEsm5VoQgpukg3s0956JkSCXjrdCx2bD0Omk1vUgjcTDlaxECp1bczwmPS9KvqfJ -pxAe+59QafMCAwEAAaOB5jCB4zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE -AwIBBjAdBgNVHQ4EFgQU0QnQ6dfOeXRU+Tows/RtLAMDG2gwgaAGA1UdIASBmDCB -lTCBkgYEVR0gADCBiTArBggrBgEFBQcCARYfaHR0cDovL3d3dy5jZXJ0aWNhbWFy -YS5jb20vZHBjLzBaBggrBgEFBQcCAjBOGkxMaW1pdGFjaW9uZXMgZGUgZ2FyYW50 -7WFzIGRlIGVzdGUgY2VydGlmaWNhZG8gc2UgcHVlZGVuIGVuY29udHJhciBlbiBs -YSBEUEMuMA0GCSqGSIb3DQEBBQUAA4ICAQBclLW4RZFNjmEfAygPU3zmpFmps4p6 -xbD/CHwso3EcIRNnoZUSQDWDg4902zNc8El2CoFS3UnUmjIz75uny3XlesuXEpBc -unvFm9+7OSPI/5jOCk0iAUgHforA1SBClETvv3eiiWdIG0ADBaGJ7M9i4z0ldma/ -Jre7Ir5v/zlXdLp6yQGVwZVR6Kss+LGGIOk/yzVb0hfpKv6DExdA7ohiZVvVO2Dp -ezy4ydV/NgIlqmjCMRW3MGXrfx1IebHPOeJCgBbT9ZMj/EyXyVo3bHwi2ErN0o42 -gzmRkBDI8ck1fj+404HGIGQatlDCIaR43NAvO2STdPCWkPHv+wlaNECW8DYSwaN0 -jJN+Qd53i+yG2dIPPy3RzECiiWZIHiCznCNZc6lEc7wkeZBWN7PGKX6jD/EpOe9+ -XCgycDWs2rjIdWb8m0w5R44bb5tNAlQiM+9hup4phO9OSzNHdpdqy35f/RWmnkJD -W2ZaiogN9xa5P1FlK2Zqi9E4UqLWRhH6/JocdJ6PlwsCT2TG9WjTSy3/pDceiz+/ -RL5hRqGEPQgnTIEgd4kI6mdAXmwIUV80WoyWaM3X94nCHNMyAK9Sy9NgWyo6R35r -MDOhYil/SrnhLecUIw4OGEfhefwVVdCx/CVxY3UzHCMrr1zZ7Ud3YA47Dx7SwNxk -BYn8eNZcLCZDqQ== ------END CERTIFICATE----- - -# Issuer: CN=TC TrustCenter Class 2 CA II O=TC TrustCenter GmbH OU=TC TrustCenter Class 2 CA -# Subject: CN=TC TrustCenter Class 2 CA II O=TC TrustCenter GmbH OU=TC TrustCenter Class 2 CA -# Label: "TC TrustCenter Class 2 CA II" -# Serial: 941389028203453866782103406992443 -# MD5 Fingerprint: ce:78:33:5c:59:78:01:6e:18:ea:b9:36:a0:b9:2e:23 -# SHA1 Fingerprint: ae:50:83:ed:7c:f4:5c:bc:8f:61:c6:21:fe:68:5d:79:42:21:15:6e -# SHA256 Fingerprint: e6:b8:f8:76:64:85:f8:07:ae:7f:8d:ac:16:70:46:1f:07:c0:a1:3e:ef:3a:1f:f7:17:53:8d:7a:ba:d3:91:b4 ------BEGIN CERTIFICATE----- -MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjEL -MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV -BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0 -Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYwMTEyMTQzODQzWhcNMjUxMjMxMjI1 -OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i -SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UEAxMc -VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jf -tMjWQ+nEdVl//OEd+DFwIxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKg -uNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2J -XjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQXa7pIXSSTYtZgo+U4+lK -8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7uSNQZu+99 -5OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1Ud -EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3 -kUrL84J6E1wIqzCB7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRy -dXN0Y2VudGVyLmRlL2NybC92Mi90Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6 -Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBUcnVzdENlbnRlciUyMENsYXNz -JTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21iSCxPVT1yb290 -Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u -TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iS -GNn3Bzn1LL4GdXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprt -ZjluS5TmVfwLG4t3wVMTZonZKNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8 -au0WOB9/WIFaGusyiC2y8zl3gK9etmF1KdsjTYjKUCjLhdLTEKJZbtOTVAB6okaV -hgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kPJOzHdiEoZa5X6AeI -dUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfkvQ== ------END CERTIFICATE----- - -# Issuer: CN=TC TrustCenter Class 3 CA II O=TC TrustCenter GmbH OU=TC TrustCenter Class 3 CA -# Subject: CN=TC TrustCenter Class 3 CA II O=TC TrustCenter GmbH OU=TC TrustCenter Class 3 CA -# Label: "TC TrustCenter Class 3 CA II" -# Serial: 1506523511417715638772220530020799 -# MD5 Fingerprint: 56:5f:aa:80:61:12:17:f6:67:21:e6:2b:6d:61:56:8e -# SHA1 Fingerprint: 80:25:ef:f4:6e:70:c8:d4:72:24:65:84:fe:40:3b:8a:8d:6a:db:f5 -# SHA256 Fingerprint: 8d:a0:84:fc:f9:9c:e0:77:22:f8:9b:32:05:93:98:06:fa:5c:b8:11:e1:c8:13:f6:a1:08:c7:d3:36:b3:40:8e ------BEGIN CERTIFICATE----- -MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjEL -MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV -BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0 -Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYwMTEyMTQ0MTU3WhcNMjUxMjMxMjI1 -OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i -SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UEAxMc -VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJW -Ht4bNwcwIi9v8Qbxq63WyKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+Q -Vl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo6SI7dYnWRBpl8huXJh0obazovVkdKyT2 -1oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZuV3bOx4a+9P/FRQI2Alq -ukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk2ZyqBwi1 -Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1Ud -EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NX -XAek0CSnwPIA1DCB7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRy -dXN0Y2VudGVyLmRlL2NybC92Mi90Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6 -Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBUcnVzdENlbnRlciUyMENsYXNz -JTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21iSCxPVT1yb290 -Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u -TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlN -irTzwppVMXzEO2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8 -TtXqluJucsG7Kv5sbviRmEb8yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6 -g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9IJqDnxrcOfHFcqMRA/07QlIp2+gB -95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal092Y+tTmBvTwtiBj -S+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc5A== ------END CERTIFICATE----- - -# Issuer: CN=TC TrustCenter Universal CA I O=TC TrustCenter GmbH OU=TC TrustCenter Universal CA -# Subject: CN=TC TrustCenter Universal CA I O=TC TrustCenter GmbH OU=TC TrustCenter Universal CA -# Label: "TC TrustCenter Universal CA I" -# Serial: 601024842042189035295619584734726 -# MD5 Fingerprint: 45:e1:a5:72:c5:a9:36:64:40:9e:f5:e4:58:84:67:8c -# SHA1 Fingerprint: 6b:2f:34:ad:89:58:be:62:fd:b0:6b:5c:ce:bb:9d:d9:4f:4e:39:f3 -# SHA256 Fingerprint: eb:f3:c0:2a:87:89:b1:fb:7d:51:19:95:d6:63:b7:29:06:d9:13:ce:0d:5e:10:56:8a:8a:77:e2:58:61:67:e7 ------BEGIN CERTIFICATE----- -MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTEL -MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNV -BAsTG1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1 -c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcNMDYwMzIyMTU1NDI4WhcNMjUxMjMx -MjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIg -R21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYwJAYD -VQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcN -AQEBBQADggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSR -JJZ4Hgmgm5qVSkr1YnwCqMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3T -fCZdzHd55yx4Oagmcw6iXSVphU9VDprvxrlE4Vc93x9UIuVvZaozhDrzznq+VZeu -jRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtwag+1m7Z3W0hZneTvWq3z -wZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9OgdwZu5GQ -fezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYD -VR0jBBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAO -BgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0G -CSqGSIb3DQEBBQUAA4IBAQAo0uCG1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X1 -7caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/CyvwbZ71q+s2IhtNerNXxTPqYn -8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3ghUJGooWMNjs -ydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT -ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/ -2TYcuiUaUj0a7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY ------END CERTIFICATE----- - # Issuer: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center # Subject: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center # Label: "Deutsche Telekom Root CA 2" @@ -2793,36 +2108,6 @@ xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU Cm26OWMohpLzGITY+9HPBVZkVw== -----END CERTIFICATE----- -# Issuer: CN=ComSign Secured CA O=ComSign -# Subject: CN=ComSign Secured CA O=ComSign -# Label: "ComSign Secured CA" -# Serial: 264725503855295744117309814499492384489 -# MD5 Fingerprint: 40:01:25:06:8d:21:43:6a:0e:43:00:9c:e7:43:f3:d5 -# SHA1 Fingerprint: f9:cd:0e:2c:da:76:24:c1:8f:bd:f0:f0:ab:b6:45:b8:f7:fe:d5:7a -# SHA256 Fingerprint: 50:79:41:c7:44:60:a0:b4:70:86:22:0d:4e:99:32:57:2a:b5:d1:b5:bb:cb:89:80:ab:1c:b1:76:51:a8:44:d2 ------BEGIN CERTIFICATE----- -MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAw -PDEbMBkGA1UEAxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWdu -MQswCQYDVQQGEwJJTDAeFw0wNDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwx -GzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBDQTEQMA4GA1UEChMHQ29tU2lnbjEL -MAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGtWhf -HZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs49oh -gHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sW -v+bznkqH7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ue -Mv5WJDmyVIRD9YTC2LxBkMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr -9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d19guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt -6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUwAwEB/zBEBgNVHR8EPTA7 -MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29tU2lnblNl -Y3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58 -ADsAj8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkq -hkiG9w0BAQUFAAOCAQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7p -iL1DRYHjZiM/EoZNGeQFsOY3wo3aBijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtC -dsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtpFhpFfTMDZflScZAmlaxMDPWL -kz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP51qJThRv4zdL -hfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz -OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw== ------END CERTIFICATE----- - # Issuer: CN=Cybertrust Global Root O=Cybertrust, Inc # Subject: CN=Cybertrust Global Root O=Cybertrust, Inc # Label: "Cybertrust Global Root" @@ -2960,34 +2245,6 @@ h7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5wwDX3OaJdZtB7WZ+oRxKaJyOk LY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho -----END CERTIFICATE----- -# Issuer: CN=Buypass Class 3 CA 1 O=Buypass AS-983163327 -# Subject: CN=Buypass Class 3 CA 1 O=Buypass AS-983163327 -# Label: "Buypass Class 3 CA 1" -# Serial: 2 -# MD5 Fingerprint: df:3c:73:59:81:e7:39:50:81:04:4c:34:a2:cb:b3:7b -# SHA1 Fingerprint: 61:57:3a:11:df:0e:d8:7e:d5:92:65:22:ea:d0:56:d7:44:b3:23:71 -# SHA256 Fingerprint: b7:b1:2b:17:1f:82:1d:aa:99:0c:d0:fe:50:87:b1:28:44:8b:a8:e5:18:4f:84:c5:1e:02:b5:c8:fb:96:2b:24 ------BEGIN CERTIFICATE----- -MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEd -MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3Mg -Q2xhc3MgMyBDQSAxMB4XDTA1MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzEL -MAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MR0wGwYD -VQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKxifZg -isRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//z -NIqeKNc0n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI -+MkcVyzwPX6UvCWThOiaAJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2R -hzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+ -mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNCMEAwDwYDVR0TAQH/BAUw -AwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0PAQH/BAQD -AgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFP -Bdy7pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27s -EzNxZy5p+qksP2bAEllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2 -mSlf56oBzKwzqBwKu5HEA6BvtjT5htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yC -e/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQjel/wroQk5PMr+4okoyeYZdow -dXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915 ------END CERTIFICATE----- - # Issuer: CN=EBG Elektronik Sertifika Hizmet Sağlayıcısı O=EBG Bilişim Teknolojileri ve Hizmetleri A.Ş. # Subject: CN=EBG Elektronik Sertifika Hizmet Sağlayıcısı O=EBG Bilişim Teknolojileri ve Hizmetleri A.Ş. # Label: "EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1" @@ -3535,28 +2792,6 @@ r0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3otkYNbn5XOmeUwssfnHdK Z05phkOTOPu220+DkdRgfks+KzgHVZhepA== -----END CERTIFICATE----- -# Issuer: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority -# Subject: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority -# Label: "Verisign Class 3 Public Primary Certification Authority" -# Serial: 80507572722862485515306429940691309246 -# MD5 Fingerprint: ef:5a:f1:33:ef:f1:cd:bb:51:02:ee:12:14:4b:96:c4 -# SHA1 Fingerprint: a1:db:63:93:91:6f:17:e4:18:55:09:40:04:15:c7:02:40:b0:ae:6b -# SHA256 Fingerprint: a4:b6:b3:99:6f:c2:f3:06:b3:fd:86:81:bd:63:41:3d:8c:50:09:cc:4f:a3:29:c2:cc:f0:e2:fa:1b:14:03:05 ------BEGIN CERTIFICATE----- -MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkG -A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz -cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 -MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV -BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt -YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN -ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE -BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is -I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G -CSqGSIb3DQEBBQUAA4GBABByUqkFFBkyCEHwxWsKzH4PIRnN5GfcX6kb5sroc50i -2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWXbj9T/UWZYB2oK0z5XqcJ -2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/D/xwzoiQ ------END CERTIFICATE----- - # Issuer: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. # Subject: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. # Label: "Microsec e-Szigno Root CA 2009" @@ -3589,36 +2824,6 @@ tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW -----END CERTIFICATE----- -# Issuer: CN=e-Guven Kok Elektronik Sertifika Hizmet Saglayicisi O=Elektronik Bilgi Guvenligi A.S. -# Subject: CN=e-Guven Kok Elektronik Sertifika Hizmet Saglayicisi O=Elektronik Bilgi Guvenligi A.S. -# Label: "E-Guven Kok Elektronik Sertifika Hizmet Saglayicisi" -# Serial: 91184789765598910059173000485363494069 -# MD5 Fingerprint: 3d:41:29:cb:1e:aa:11:74:cd:5d:b0:62:af:b0:43:5b -# SHA1 Fingerprint: dd:e1:d2:a9:01:80:2e:1d:87:5e:84:b3:80:7e:4b:b1:fd:99:41:34 -# SHA256 Fingerprint: e6:09:07:84:65:a4:19:78:0c:b6:ac:4c:1c:0b:fb:46:53:d9:d9:cc:6e:b3:94:6e:b7:f3:d6:99:97:ba:d5:98 ------BEGIN CERTIFICATE----- -MIIDtjCCAp6gAwIBAgIQRJmNPMADJ72cdpW56tustTANBgkqhkiG9w0BAQUFADB1 -MQswCQYDVQQGEwJUUjEoMCYGA1UEChMfRWxla3Ryb25payBCaWxnaSBHdXZlbmxp -Z2kgQS5TLjE8MDoGA1UEAxMzZS1HdXZlbiBLb2sgRWxla3Ryb25payBTZXJ0aWZp -a2EgSGl6bWV0IFNhZ2xheWljaXNpMB4XDTA3MDEwNDExMzI0OFoXDTE3MDEwNDEx -MzI0OFowdTELMAkGA1UEBhMCVFIxKDAmBgNVBAoTH0VsZWt0cm9uaWsgQmlsZ2kg -R3V2ZW5saWdpIEEuUy4xPDA6BgNVBAMTM2UtR3V2ZW4gS29rIEVsZWt0cm9uaWsg -U2VydGlmaWthIEhpem1ldCBTYWdsYXlpY2lzaTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAMMSIJ6wXgBljU5Gu4Bc6SwGl9XzcslwuedLZYDBS75+PNdU -MZTe1RK6UxYC6lhj71vY8+0qGqpxSKPcEC1fX+tcS5yWCEIlKBHMilpiAVDV6wlT -L/jDj/6z/P2douNffb7tC+Bg62nsM+3YjfsSSYMAyYuXjDtzKjKzEve5TfL0TW3H -5tYmNwjy2f1rXKPlSFxYvEK+A1qBuhw1DADT9SN+cTAIJjjcJRFHLfO6IxClv7wC -90Nex/6wN1CZew+TzuZDLMN+DfIcQ2Zgy2ExR4ejT669VmxMvLz4Bcpk9Ok0oSy1 -c+HCPujIyTQlCFzz7abHlJ+tiEMl1+E5YP6sOVkCAwEAAaNCMEAwDgYDVR0PAQH/ -BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJ/uRLOU1fqRTy7ZVZoE -VtstxNulMA0GCSqGSIb3DQEBBQUAA4IBAQB/X7lTW2M9dTLn+sR0GstG30ZpHFLP -qk/CaOv/gKlR6D1id4k9CnU58W5dF4dvaAXBlGzZXd/aslnLpRCKysw5zZ/rTt5S -/wzw9JKp8mxTq5vSR6AfdPebmvEvFZ96ZDAYBzwqD2fK/A+JYZ1lpTzlvBNbCNvj -/+27BrtqBrF6T2XGgv0enIu1De5Iu7i9qgi0+6N8y5/NkHZchpZ4Vwpm+Vganf2X -KWDeEaaQHBkc7gGWIjQ0LpH5t8Qn0Xvmv/uARFoW5evg1Ao4vOSR49XrXMGs3xtq -fJ7lddK2l4fbzIcrQzqECK+rPNv3PGYxhrCdU3nt+CPeQuMtgvEP5fqX ------END CERTIFICATE----- - # Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 # Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 # Label: "GlobalSign Root CA - R3" @@ -5024,3 +4229,1388 @@ wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy KwbQBM0= -----END CERTIFICATE----- + +# Issuer: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Subject: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Label: "TeliaSonera Root CA v1" +# Serial: 199041966741090107964904287217786801558 +# MD5 Fingerprint: 37:41:49:1b:18:56:9a:26:f5:ad:c2:66:fb:40:a5:4c +# SHA1 Fingerprint: 43:13:bb:96:f1:d5:86:9b:c1:4e:6a:92:f6:cf:f6:34:69:87:82:37 +# SHA256 Fingerprint: dd:69:36:fe:21:f8:f0:77:c1:23:a1:a5:21:c1:22:24:f7:22:55:b7:3e:03:a7:26:06:93:e8:a2:4b:0f:a3:89 +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw +NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv +b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD +VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2 +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F +VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1 +7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X +Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+ +/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs +81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm +dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe +Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu +sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4 +pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs +slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ +arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD +VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG +9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl +dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj +TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed +Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7 +Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI +OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7 +vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW +t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn +HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx +SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +# Issuer: CN=E-Tugra Certification Authority O=E-Tuğra EBG Bilişim Teknolojileri ve Hizmetleri A.Ş. OU=E-Tugra Sertifikasyon Merkezi +# Subject: CN=E-Tugra Certification Authority O=E-Tuğra EBG Bilişim Teknolojileri ve Hizmetleri A.Ş. OU=E-Tugra Sertifikasyon Merkezi +# Label: "E-Tugra Certification Authority" +# Serial: 7667447206703254355 +# MD5 Fingerprint: b8:a1:03:63:b0:bd:21:71:70:8a:6f:13:3a:bb:79:49 +# SHA1 Fingerprint: 51:c6:e7:08:49:06:6e:f3:92:d4:5c:a0:0d:6d:a3:62:8f:c3:52:39 +# SHA256 Fingerprint: b0:bf:d5:2b:b0:d7:d9:bd:92:bf:5d:4d:c1:3d:a2:55:c0:2c:54:2f:37:83:65:ea:89:39:11:f5:5e:55:f2:3c +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV +BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC +aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV +BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1 +Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz +MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+ +BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp +em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY +B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH +D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF +Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo +q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D +k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH +fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut +dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM +ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8 +zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX +U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6 +Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5 +XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF +Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR +HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY +GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c +77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3 ++GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK +vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6 +FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl +yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P +AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD +y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d +NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 2" +# Serial: 1 +# MD5 Fingerprint: 2b:9b:9e:e4:7b:6c:1f:00:72:1a:cc:c1:77:79:df:6a +# SHA1 Fingerprint: 59:0d:2d:7d:88:4f:40:2e:61:7e:a5:62:32:17:65:cf:17:d8:94:e9 +# SHA256 Fingerprint: 91:e2:f5:78:8d:58:10:eb:a7:ba:58:73:7d:e1:54:8a:8e:ca:cd:01:45:98:bc:0b:14:3e:04:1b:17:05:25:52 +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd +AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC +FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi +1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq +jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ +wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ +WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy +NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC +uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw +IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 +g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP +BSeOE6Fuwg== +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot 2011 O=Atos +# Subject: CN=Atos TrustedRoot 2011 O=Atos +# Label: "Atos TrustedRoot 2011" +# Serial: 6643877497813316402 +# MD5 Fingerprint: ae:b9:c4:32:4b:ac:7f:5d:66:cc:77:94:bb:2a:77:56 +# SHA1 Fingerprint: 2b:b1:f5:3e:55:0c:1d:c5:f1:d4:e6:b7:6a:46:4b:55:06:02:ac:21 +# SHA256 Fingerprint: f3:56:be:a2:44:b7:a9:1e:b3:5d:53:ca:9a:d7:86:4a:ce:01:8e:2d:35:d5:f8:f9:6d:df:68:a6:f4:1a:a4:74 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE +AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG +EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM +FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC +REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp +Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM +VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ +SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ +4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L +cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi +eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG +A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 +DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j +vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP +DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc +maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D +lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv +KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 1 G3" +# Serial: 687049649626669250736271037606554624078720034195 +# MD5 Fingerprint: a4:bc:5b:3f:fe:37:9a:fa:64:f0:e2:fa:05:3d:0b:ab +# SHA1 Fingerprint: 1b:8e:ea:57:96:29:1a:c9:39:ea:b8:0a:81:1a:73:73:c0:93:79:67 +# SHA256 Fingerprint: 8a:86:6f:d1:b2:76:b5:7e:57:8e:92:1c:65:82:8a:2b:ed:58:e9:f2:f2:88:05:41:34:b7:f1:f4:bf:c9:cc:74 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 +MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV +wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe +rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 +68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh +4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp +UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o +abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc +3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G +KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt +hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO +Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt +zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD +ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 +cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN +qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 +YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv +b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 +8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k +NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj +ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp +q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt +nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2 G3" +# Serial: 390156079458959257446133169266079962026824725800 +# MD5 Fingerprint: af:0c:86:6e:bf:40:2d:7f:0b:3e:12:50:ba:12:3d:06 +# SHA1 Fingerprint: 09:3c:61:f3:8b:8b:dc:7d:55:df:75:38:02:05:00:e1:25:f5:c8:36 +# SHA256 Fingerprint: 8f:e4:fb:0a:f9:3a:4d:0d:67:db:0b:eb:b2:3e:37:c7:1b:f3:25:dc:bc:dd:24:0e:a0:4d:af:58:b4:7e:18:40 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3 G3" +# Serial: 268090761170461462463995952157327242137089239581 +# MD5 Fingerprint: df:7d:b9:ad:54:6f:68:a1:df:89:57:03:97:43:b0:d7 +# SHA1 Fingerprint: 48:12:bd:92:3c:a8:c4:39:06:e7:30:6d:27:96:e6:a4:cf:22:2e:7d +# SHA256 Fingerprint: 88:ef:81:de:20:2e:b0:18:45:2e:43:f8:64:72:5c:ea:5f:bd:1f:c2:d9:d2:05:73:07:09:c5:d8:b8:69:0f:46 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 +MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR +/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu +FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR +U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c +ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR +FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k +A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw +eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl +sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp +VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q +A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ +ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD +ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI +FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv +oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg +u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP +0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf +3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl +8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ +DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN +PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ +ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G2" +# Serial: 15385348160840213938643033620894905419 +# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d +# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f +# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85 +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G3" +# Serial: 15459312981008553731928384953135426796 +# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb +# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89 +# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2 +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G2" +# Serial: 4293743540046975378534879503202253541 +# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44 +# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4 +# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G3" +# Serial: 7089244469030293291760083333884364146 +# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca +# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e +# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0 +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Trusted Root G4" +# Serial: 7451500558977370777930084869016614236 +# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49 +# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4 +# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88 +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- + +# Issuer: CN=Certification Authority of WoSign O=WoSign CA Limited +# Subject: CN=Certification Authority of WoSign O=WoSign CA Limited +# Label: "WoSign" +# Serial: 125491772294754854453622855443212256657 +# MD5 Fingerprint: a1:f2:f9:b5:d2:c8:7a:74:b8:f3:05:f1:d7:e1:84:8d +# SHA1 Fingerprint: b9:42:94:bf:91:ea:8f:b6:4b:e6:10:97:c7:fb:00:13:59:b6:76:cb +# SHA256 Fingerprint: 4b:22:d5:a6:ae:c9:9f:3c:db:79:aa:5e:c0:68:38:47:9c:d5:ec:ba:71:64:f7:f2:2d:c1:d6:5f:63:d8:57:08 +-----BEGIN CERTIFICATE----- +MIIFdjCCA16gAwIBAgIQXmjWEXGUY1BWAGjzPsnFkTANBgkqhkiG9w0BAQUFADBV +MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxKjAoBgNV +BAMTIUNlcnRpZmljYXRpb24gQXV0aG9yaXR5IG9mIFdvU2lnbjAeFw0wOTA4MDgw +MTAwMDFaFw0zOTA4MDgwMTAwMDFaMFUxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFX +b1NpZ24gQ0EgTGltaXRlZDEqMCgGA1UEAxMhQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgb2YgV29TaWduMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvcqN +rLiRFVaXe2tcesLea9mhsMMQI/qnobLMMfo+2aYpbxY94Gv4uEBf2zmoAHqLoE1U +fcIiePyOCbiohdfMlZdLdNiefvAA5A6JrkkoRBoQmTIPJYhTpA2zDxIIFgsDcScc +f+Hb0v1naMQFXQoOXXDX2JegvFNBmpGN9J42Znp+VsGQX+axaCA2pIwkLCxHC1l2 +ZjC1vt7tj/id07sBMOby8w7gLJKA84X5KIq0VC6a7fd2/BVoFutKbOsuEo/Uz/4M +x1wdC34FMr5esAkqQtXJTpCzWQ27en7N1QhatH/YHGkR+ScPewavVIMYe+HdVHpR +aG53/Ma/UkpmRqGyZxq7o093oL5d//xWC0Nyd5DKnvnyOfUNqfTq1+ezEC8wQjch +zDBwyYaYD8xYTYO7feUapTeNtqwylwA6Y3EkHp43xP901DfA4v6IRmAR3Qg/UDar +uHqklWJqbrDKaiFaafPz+x1wOZXzp26mgYmhiMU7ccqjUu6Du/2gd/Tkb+dC221K +mYo0SLwX3OSACCK28jHAPwQ+658geda4BmRkAjHXqc1S+4RFaQkAKtxVi8QGRkvA +Sh0JWzko/amrzgD5LkhLJuYwTKVYyrREgk/nkR4zw7CT/xH8gdLKH3Ep3XZPkiWv +HYG3Dy+MwwbMLyejSuQOmbp8HkUff6oZRZb9/D0CAwEAAaNCMEAwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOFmzw7R8bNLtwYgFP6H +EtX2/vs+MA0GCSqGSIb3DQEBBQUAA4ICAQCoy3JAsnbBfnv8rWTjMnvMPLZdRtP1 +LOJwXcgu2AZ9mNELIaCJWSQBnfmvCX0KI4I01fx8cpm5o9dU9OpScA7F9dY74ToJ +MuYhOZO9sxXqT2r09Ys/L3yNWC7F4TmgPsc9SnOeQHrAK2GpZ8nzJLmzbVUsWh2e +JXLOC62qx1ViC777Y7NhRCOjy+EaDveaBk3e1CNOIZZbOVtXHS9dCF4Jef98l7VN +g64N1uajeeAz0JmWAjCnPv/So0M/BVoG6kQC2nz4SNAzqfkHx5Xh9T71XXG68pWp +dIhhWeO/yloTunK0jF02h+mmxTwTv97QRCbut+wucPrXnbes5cVAWubXbHssw1ab +R80LzvobtCHXt2a49CUwi1wNuepnsvRtrtWhnk/Yn+knArAdBtaP4/tIEp9/EaEQ +PkxROpaw0RPxx9gmrjrKkcRpnd8BKWRRb2jaFOwIQZeQjdCygPLPwj2/kWjFgGce +xGATVdVhmVd8upUPYUk6ynW8yQqTP2cOEvIo4jEbwFcW3wh8GcF+Dx+FHgo2fFt+ +J7x6v+Db9NpSvd4MVHAxkUOVyLzwPt0JfjBkUO1/AaQzZ01oT74V77D2AhGiGxMl +OtzCWfHjXEa7ZywCRuoeSKbmW9m1vFGikpbbqsY3Iqb+zCB0oy2pLmvLwIIRIbWT +ee5Ehr7XHuQe+w== +-----END CERTIFICATE----- + +# Issuer: CN=CA 沃通根证书 O=WoSign CA Limited +# Subject: CN=CA 沃通根证书 O=WoSign CA Limited +# Label: "WoSign China" +# Serial: 106921963437422998931660691310149453965 +# MD5 Fingerprint: 78:83:5b:52:16:76:c4:24:3b:83:78:e8:ac:da:9a:93 +# SHA1 Fingerprint: 16:32:47:8d:89:f9:21:3a:92:00:85:63:f5:a4:a7:d3:12:40:8a:d6 +# SHA256 Fingerprint: d6:f0:34:bd:94:aa:23:3f:02:97:ec:a4:24:5b:28:39:73:e4:47:aa:59:0f:31:0c:77:f4:8f:df:83:11:22:54 +-----BEGIN CERTIFICATE----- +MIIFWDCCA0CgAwIBAgIQUHBrzdgT/BtOOzNy0hFIjTANBgkqhkiG9w0BAQsFADBG +MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxGzAZBgNV +BAMMEkNBIOayg+mAmuagueivgeS5pjAeFw0wOTA4MDgwMTAwMDFaFw0zOTA4MDgw +MTAwMDFaMEYxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRl +ZDEbMBkGA1UEAwwSQ0Eg5rKD6YCa5qC56K+B5LmmMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA0EkhHiX8h8EqwqzbdoYGTufQdDTc7WU1/FDWiD+k8H/r +D195L4mx/bxjWDeTmzj4t1up+thxx7S8gJeNbEvxUNUqKaqoGXqW5pWOdO2XCld1 +9AXbbQs5uQF/qvbW2mzmBeCkTVL829B0txGMe41P/4eDrv8FAxNXUDf+jJZSEExf +v5RxadmWPgxDT74wwJ85dE8GRV2j1lY5aAfMh09Qd5Nx2UQIsYo06Yms25tO4dnk +UkWMLhQfkWsZHWgpLFbE4h4TV2TwYeO5Ed+w4VegG63XX9Gv2ystP9Bojg/qnw+L +NVgbExz03jWhCl3W6t8Sb8D7aQdGctyB9gQjF+BNdeFyb7Ao65vh4YOhn0pdr8yb ++gIgthhid5E7o9Vlrdx8kHccREGkSovrlXLp9glk3Kgtn3R46MGiCWOc76DbT52V +qyBPt7D3h1ymoOQ3OMdc4zUPLK2jgKLsLl3Az+2LBcLmc272idX10kaO6m1jGx6K +yX2m+Jzr5dVjhU1zZmkR/sgO9MHHZklTfuQZa/HpelmjbX7FF+Ynxu8b22/8DU0G +AbQOXDBGVWCvOGU6yke6rCzMRh+yRpY/8+0mBe53oWprfi1tWFxK1I5nuPHa1UaK +J/kR8slC/k7e3x9cxKSGhxYzoacXGKUN5AXlK8IrC6KVkLn9YDxOiT7nnO4fuwEC +AwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFOBNv9ybQV0T6GTwp+kVpOGBwboxMA0GCSqGSIb3DQEBCwUAA4ICAQBqinA4 +WbbaixjIvirTthnVZil6Xc1bL3McJk6jfW+rtylNpumlEYOnOXOvEESS5iVdT2H6 +yAa+Tkvv/vMx/sZ8cApBWNromUuWyXi8mHwCKe0JgOYKOoICKuLJL8hWGSbueBwj +/feTZU7n85iYr83d2Z5AiDEoOqsuC7CsDCT6eiaY8xJhEPRdF/d+4niXVOKM6Cm6 +jBAyvd0zaziGfjk9DgNyp115j0WKWa5bIW4xRtVZjc8VX90xJc/bYNaBRHIpAlf2 +ltTW/+op2znFuCyKGo3Oy+dCMYYFaA6eFN0AkLppRQjbbpCBhqcqBT/mhDn4t/lX +X0ykeVoQDF7Va/81XwVRHmyjdanPUIPTfPRm94KNPQx96N97qA4bLJyuQHCH2u2n +FoJavjVsIE4iYdm8UXrNemHcSxH5/mc0zy4EZmFcV5cjjPOGG0jfKq+nwf/Yjj4D +u9gqsPoUJbJRa4ZDhS4HIxaAjUz7tGM7zMN07RujHv41D198HRaG9Q7DlfEvr10l +O1Hm13ZBONFLAzkopR6RctR9q5czxNM+4Gm2KHmgCY0c0f9BckgG/Jou5yD5m6Le +ie2uPAmvylezkolwQOQvT8Jwg0DXJCxr5wkf09XHwQj02w47HAcLQxGEIYbpgNR1 +2KvxAmLBsX5VYc8T1yaw15zLKYs4SgsOkI26oQ== +-----END CERTIFICATE----- + +# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Label: "COMODO RSA Certification Authority" +# Serial: 101909084537582093308941363524873193117 +# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18 +# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4 +# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34 +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Label: "USERTrust RSA Certification Authority" +# Serial: 2645093764781058787591871645665788717 +# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 +# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e +# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Label: "USERTrust ECC Certification Authority" +# Serial: 123013823720199481456569720443997572134 +# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1 +# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0 +# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Label: "GlobalSign ECC Root CA - R4" +# Serial: 14367148294922964480859022125800977897474 +# MD5 Fingerprint: 20:f0:27:68:d1:7e:a0:9d:0e:e6:2a:ca:df:5c:89:8e +# SHA1 Fingerprint: 69:69:56:2e:40:80:f4:24:a1:e7:19:9f:14:ba:f3:ee:58:ab:6a:bb +# SHA256 Fingerprint: be:c9:49:11:c2:95:56:76:db:6c:0a:55:09:86:d7:6e:3b:a0:05:66:7c:44:2c:97:62:b4:fb:b7:73:de:22:8c +-----BEGIN CERTIFICATE----- +MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ +FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F +uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX +kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs +ewv4n4Q= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Label: "GlobalSign ECC Root CA - R5" +# Serial: 32785792099990507226680698011560947931244 +# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08 +# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa +# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24 +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden Root CA - G3 O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden Root CA - G3 O=Staat der Nederlanden +# Label: "Staat der Nederlanden Root CA - G3" +# Serial: 10003001 +# MD5 Fingerprint: 0b:46:67:07:db:10:2f:19:8c:35:50:60:d1:0b:f4:37 +# SHA1 Fingerprint: d8:eb:6b:41:51:92:59:e0:f3:e7:85:00:c0:3d:b6:88:97:c9:ee:fc +# SHA256 Fingerprint: 3c:4f:b0:b9:5a:b8:b3:00:32:f4:32:b8:6f:53:5f:e1:72:c1:85:d0:fd:39:86:58:37:cf:36:18:7f:a6:f4:28 +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloX +DTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4yolQP +cPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WW +IkYFsO2tx1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqX +xz8ecAgwoNzFs21v0IJyEavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFy +KJLZWyNtZrVtB0LrpjPOktvA9mxjeM3KTj215VKb8b475lRgsGYeCasH/lSJEULR +9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUurmkVLoR9BvUhTFXFkC4az +5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU51nus6+N8 +6U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7 +Ngzp07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHP +bMk7ccHViLVlvMDoFxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXt +BznaqB16nzaeErAMZRKQFWDZJkBE41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTt +XUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleuyjWcLhL75Lpd +INyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD +U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwp +LiniyMMB8jPqKqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8 +Ipf3YF3qKS9Ysr1YvY2WTxB1v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixp +gZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA8KCWAg8zxXHzniN9lLf9OtMJgwYh +/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b8KKaa8MFSu1BYBQw +0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0rmj1A +fsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq +4BZ+Extq1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR +1VmiiXTTn74eS9fGbbeIJG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/ +QFH1T/U67cjF68IeHRaVesd+QnGTbksVtzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM +94B7IWcnMFk= +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden +# Label: "Staat der Nederlanden EV Root CA" +# Serial: 10000013 +# MD5 Fingerprint: fc:06:af:7b:e8:1a:f1:9a:b4:e8:d2:70:1f:c0:f5:ba +# SHA1 Fingerprint: 76:e2:7e:c1:4f:db:82:c1:c0:a6:75:b5:05:be:3d:29:b4:ed:db:bb +# SHA256 Fingerprint: 4d:24:91:41:4c:fe:95:67:46:ec:4c:ef:a6:cf:6f:72:e2:8a:13:29:43:2f:9d:8a:90:7a:c4:cb:5d:ad:c1:5a +-----BEGIN CERTIFICATE----- +MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gRVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0y +MjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIg +TmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBS +b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkkSzrS +M4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nC +UiY4iKTWO0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3d +Z//BYY1jTw+bbRcwJu+r0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46p +rfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13l +pJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gVXJrm0w912fxBmJc+qiXb +j5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr08C+eKxC +KFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS +/ZbV0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0X +cgOPvZuM5l5Tnrmd74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH +1vI4gnPah1vlPNOePqc7nvQDs/nxfRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrP +px9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwaivsnuL8wbqg7 +MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI +eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u +2dfOWBfoqSmuc0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHS +v4ilf0X8rLiltTMMgsT7B/Zq5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTC +wPTxGfARKbalGAKb12NMcIxHowNDXLldRqANb/9Zjr7dn3LDWyvfjFvO5QxGbJKy +CqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tNf1zuacpzEPuKqf2e +vTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi5Dp6 +Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIa +Gl6I6lD4WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeL +eG9QgkRQP2YGiqtDhFZKDyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8 +FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGyeUN51q1veieQA6TqJIc/2b3Z6fJfUEkc +7uzXLg== +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Label: "IdenTrust Commercial Root CA 1" +# Serial: 13298821034946342390520003877796839426 +# MD5 Fingerprint: b3:3e:77:73:75:ee:a0:d3:e3:7e:49:63:49:59:bb:c7 +# SHA1 Fingerprint: df:71:7e:aa:4a:d9:4e:c9:55:84:99:60:2d:48:de:5f:bc:f0:3a:25 +# SHA256 Fingerprint: 5d:56:49:9b:e4:d2:e0:8b:cf:ca:d0:8a:3e:38:72:3d:50:50:3b:de:70:69:48:e4:2f:55:60:30:19:e5:28:ae +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu +VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw +MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw +JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT +3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU ++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp +S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 +bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi +T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL +vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK +Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK +dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT +c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv +l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N +iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD +ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt +LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 +nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 ++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK +W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT +AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq +l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG +4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ +mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A +7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Label: "IdenTrust Public Sector Root CA 1" +# Serial: 13298821034946342390521976156843933698 +# MD5 Fingerprint: 37:06:a5:b0:fc:89:9d:ba:f4:6b:8c:1a:64:cd:d5:ba +# SHA1 Fingerprint: ba:29:41:60:77:98:3f:f4:f3:ef:f2:31:05:3b:2e:ea:6d:4d:45:fd +# SHA256 Fingerprint: 30:d0:89:5a:9a:44:8a:26:20:91:63:55:22:d1:f5:20:10:b5:86:7a:ca:e1:2c:78:ef:95:8f:d4:f4:38:9f:2f +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu +VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN +MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 +MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 +ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy +RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS +bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF +/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R +3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw +EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy +9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V +GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ +2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV +WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD +W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN +AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV +DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 +TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G +lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW +mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df +WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 ++bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ +tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA +GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv +8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - G2" +# Serial: 1246989352 +# MD5 Fingerprint: 4b:e2:c9:91:96:65:0c:f4:0e:5a:93:92:a0:0a:fe:b2 +# SHA1 Fingerprint: 8c:f4:27:fd:79:0c:3a:d1:66:06:8d:e8:1e:57:ef:bb:93:22:72:d4 +# SHA256 Fingerprint: 43:df:57:74:b0:3e:7f:ef:5f:e4:0d:93:1a:7b:ed:f1:bb:2e:6b:42:73:8c:4e:6d:38:41:10:3d:3a:a7:f3:39 +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 +cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs +IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz +dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy +NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu +dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt +dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 +aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T +RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN +cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW +wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 +U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 +jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN +BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ +jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v +1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R +nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH +VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - EC1" +# Serial: 51543124481930649114116133369 +# MD5 Fingerprint: b6:7e:1d:f0:58:c5:49:6c:24:3b:3d:ed:98:18:ed:bc +# SHA1 Fingerprint: 20:d8:06:40:df:9b:25:f5:12:25:3a:11:ea:f7:59:8a:eb:14:b5:47 +# SHA256 Fingerprint: 02:ed:0e:b2:8c:14:da:45:16:5c:56:67:91:70:0d:64:51:d7:fb:56:f0:b2:ab:1d:3b:8e:b0:70:e5:6e:df:f5 +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 +d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu +dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq +RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy +MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD +VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g +Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi +A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt +ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH +Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC +R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX +hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- + +# Issuer: CN=CFCA EV ROOT O=China Financial Certification Authority +# Subject: CN=CFCA EV ROOT O=China Financial Certification Authority +# Label: "CFCA EV ROOT" +# Serial: 407555286 +# MD5 Fingerprint: 74:e1:b6:ed:26:7a:7a:44:30:33:94:ab:7b:27:81:30 +# SHA1 Fingerprint: e2:b8:29:4b:55:84:ab:6b:58:c2:90:46:6c:ac:3f:b8:39:8f:84:83 +# SHA256 Fingerprint: 5c:c3:d7:8e:4e:1d:5e:45:54:7a:04:e6:87:3e:64:f9:0c:f9:53:6d:1c:cc:2e:f8:00:f3:55:c4:c5:fd:70:fd +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx +MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP +T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 +sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL +TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 +/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp +7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz +EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt +hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP +a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot +aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg +TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV +PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv +cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL +tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd +BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT +ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL +jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS +ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy +P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 +xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d +Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN +5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe +/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z +AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ +5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +# Issuer: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H5 O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. +# Subject: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H5 O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. +# Label: "TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H5" +# Serial: 156233699172481 +# MD5 Fingerprint: da:70:8e:f0:22:df:93:26:f6:5f:9f:d3:15:06:52:4e +# SHA1 Fingerprint: c4:18:f6:4d:46:d1:df:00:3d:27:30:13:72:43:a9:12:11:c6:75:fb +# SHA256 Fingerprint: 49:35:1b:90:34:44:c1:85:cc:dc:5c:69:3d:24:d8:55:5c:b2:08:d6:a8:14:13:07:69:9f:4a:f0:63:19:9d:78 +-----BEGIN CERTIFICATE----- +MIIEJzCCAw+gAwIBAgIHAI4X/iQggTANBgkqhkiG9w0BAQsFADCBsTELMAkGA1UE +BhMCVFIxDzANBgNVBAcMBkFua2FyYTFNMEsGA1UECgxEVMOcUktUUlVTVCBCaWxn +aSDEsGxldGnFn2ltIHZlIEJpbGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkg +QS7Fni4xQjBABgNVBAMMOVTDnFJLVFJVU1QgRWxla3Ryb25payBTZXJ0aWZpa2Eg +SGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSBINTAeFw0xMzA0MzAwODA3MDFaFw0yMzA0 +MjgwODA3MDFaMIGxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMU0wSwYD +VQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8 +dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg1MIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApCUZ4WWe60ghUEoI5RHwWrom +/4NZzkQqL/7hzmAD/I0Dpe3/a6i6zDQGn1k19uwsu537jVJp45wnEFPzpALFp/kR +Gml1bsMdi9GYjZOHp3GXDSHHmflS0yxjXVW86B8BSLlg/kJK9siArs1mep5Fimh3 +4khon6La8eHBEJ/rPCmBp+EyCNSgBbGM+42WAA4+Jd9ThiI7/PS98wl+d+yG6w8z +5UNP9FR1bSmZLmZaQ9/LXMrI5Tjxfjs1nQ/0xVqhzPMggCTTV+wVunUlm+hkS7M0 +hO8EuPbJbKoCPrZV4jI3X/xml1/N1p7HIL9Nxqw/dV8c7TKcfGkAaZHjIxhT6QID +AQABo0IwQDAdBgNVHQ4EFgQUVpkHHtOsDGlktAxQR95DLL4gwPswDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJ5FdnsX +SDLyOIspve6WSk6BGLFRRyDN0GSxDsnZAdkJzsiZ3GglE9Rc8qPoBP5yCccLqh0l +VX6Wmle3usURehnmp349hQ71+S4pL+f5bFgWV1Al9j4uPqrtd3GqqpmWRgqujuwq +URawXs3qZwQcWDD1YIq9pr1N5Za0/EKJAWv2cMhQOQwt1WbZyNKzMrcbGW3LM/nf +peYVhDfwwvJllpKQd/Ct9JDpEXjXk4nAPQu6KfTomZ1yju2dL+6SfaHx/126M2CF +Yv4HAqGEVka+lgqaE9chTLd8B59OTj+RdPsnnRHM3eaxynFNExc5JsUpISuTKWqW ++qtB4Uu2NQvAmxU= +-----END CERTIFICATE----- + +# Issuer: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H6 O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. +# Subject: CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H6 O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. +# Label: "TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H6" +# Serial: 138134509972618 +# MD5 Fingerprint: f8:c5:ee:2a:6b:be:95:8d:08:f7:25:4a:ea:71:3e:46 +# SHA1 Fingerprint: 8a:5c:8c:ee:a5:03:e6:05:56:ba:d8:1b:d4:f6:c9:b0:ed:e5:2f:e0 +# SHA256 Fingerprint: 8d:e7:86:55:e1:be:7f:78:47:80:0b:93:f6:94:d2:1d:36:8c:c0:6e:03:3e:7f:ab:04:bb:5e:b9:9d:a6:b7:00 +-----BEGIN CERTIFICATE----- +MIIEJjCCAw6gAwIBAgIGfaHyZeyKMA0GCSqGSIb3DQEBCwUAMIGxMQswCQYDVQQG +EwJUUjEPMA0GA1UEBwwGQW5rYXJhMU0wSwYDVQQKDERUw5xSS1RSVVNUIEJpbGdp +IMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBB +LsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBI +aXptZXQgU2HEn2xhecSxY8Sxc8SxIEg2MB4XDTEzMTIxODA5MDQxMFoXDTIzMTIx +NjA5MDQxMFowgbExCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExTTBLBgNV +BAoMRFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBCaWxpxZ9pbSBHw7x2 +ZW5sacSfaSBIaXptZXRsZXJpIEEuxZ4uMUIwQAYDVQQDDDlUw5xSS1RSVVNUIEVs +ZWt0cm9uaWsgU2VydGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLEgSDYwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCdsGjW6L0UlqMACprx9MfMkU1x +eHe59yEmFXNRFpQJRwXiM/VomjX/3EsvMsew7eKC5W/a2uqsxgbPJQ1BgfbBOCK9 ++bGlprMBvD9QFyv26WZV1DOzXPhDIHiTVRZwGTLmiddk671IUP320EEDwnS3/faA +z1vFq6TWlRKb55cTMgPp1KtDWxbtMyJkKbbSk60vbNg9tvYdDjTu0n2pVQ8g9P0p +u5FbHH3GQjhtQiht1AH7zYiXSX6484P4tZgvsycLSF5W506jM7NE1qXyGJTtHB6p +lVxiSvgNZ1GpryHV+DKdeboaX+UEVU0TRv/yz3THGmNtwx8XEsMeED5gCLMxAgMB +AAGjQjBAMB0GA1UdDgQWBBTdVRcT9qzoSCHK77Wv0QAy7Z6MtTAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAb1gNl0Oq +FlQ+v6nfkkU/hQu7VtMMUszIv3ZnXuaqs6fvuay0EBQNdH49ba3RfdCaqaXKGDsC +QC4qnFAUi/5XfldcEQlLNkVS9z2sFP1E34uXI9TDwe7UU5X+LEr+DXCqu4svLcsy +o4LyVN/Y8t3XSHLuSqMplsNEzm61kod2pLv0kmzOLBQJZo6NrRa1xxsJYTvjIKID +gI6tflEATseWhvtDmHd9KMeP2Cpu54Rvl0EpABZeTeIT6lnAY2c6RPuY/ATTMHKm +9ocJV612ph1jmv3XZch4gyt1O6VbuA1df74jrlZVlFjvH4GMKrLN5ptjnhi85WsG +tAuYSyher4hYyw== +-----END CERTIFICATE----- + +# Issuer: CN=Certinomis - Root CA O=Certinomis OU=0002 433998903 +# Subject: CN=Certinomis - Root CA O=Certinomis OU=0002 433998903 +# Label: "Certinomis - Root CA" +# Serial: 1 +# MD5 Fingerprint: 14:0a:fd:8d:a8:28:b5:38:69:db:56:7e:61:22:03:3f +# SHA1 Fingerprint: 9d:70:bb:01:a5:a4:a0:18:11:2e:f7:1c:01:b9:32:c5:34:e7:88:a8 +# SHA256 Fingerprint: 2a:99:f5:bc:11:74:b7:3c:bb:1d:62:08:84:e0:1c:34:e5:1c:cb:39:78:da:12:5f:0e:33:26:88:83:bf:41:58 +-----BEGIN CERTIFICATE----- +MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjET +MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAb +BgNVBAMTFENlcnRpbm9taXMgLSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMz +MTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMx +FzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRDZXJ0aW5vbWlzIC0g +Um9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQosP5L2 +fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJfl +LieY6pOod5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQV +WZUKxkd8aRi5pwP5ynapz8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDF +TKWrteoB4owuZH9kb/2jJZOLyKIOSY008B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb +5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09xRLWtwHkziOC/7aOgFLSc +CbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE6OXWk6Ri +wsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJ +wx3tFvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SG +m/lg0h9tkQPTYKbVPZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4 +F2iw4lNVYC2vPsKD2NkJK/DAZNuHi5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZng +WVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I6tNxIqSSaHh0 +2TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF +AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/ +0KGRHCwPT5iVWVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWw +F6YSjNRieOpWauwK0kDDPAUwPk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZS +g081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAXlCOotQqSD7J6wWAsOMwaplv/8gzj +qh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJy29SWwNyhlCVCNSN +h4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9Iff/ +ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8V +btaw5BngDwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwj +Y/M50n92Uaf0yKHxDHYiI0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ +8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nMcyrDflOR1m749fPH0FFNjkulW+YZFzvW +gQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVrhkIGuUE= +-----END CERTIFICATE----- +# Issuer: CN=Entrust.net Secure Server Certification Authority O=Entrust.net OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Subject: CN=Entrust.net Secure Server Certification Authority O=Entrust.net OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Label: "Entrust.net Secure Server CA" +# Serial: 927650371 +# MD5 Fingerprint: df:f2:80:73:cc:f1:e6:61:73:fc:f5:42:e9:c5:7c:ee +# SHA1 Fingerprint: 99:a6:9b:e6:1a:fe:88:6b:4d:2b:82:00:7c:b8:54:fc:31:7e:15:39 +# SHA256 Fingerprint: 62:f2:40:27:8c:56:4c:4d:d8:bf:7d:9d:4f:6f:36:6e:a8:94:d2:2f:5f:34:d9:89:a9:83:ac:ec:2f:ff:ed:50 +-----BEGIN CERTIFICATE----- +MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC +VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u +ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc +KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u +ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1 +MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE +ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j +b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF +bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg +U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA +A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/ +I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3 +wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC +AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb +oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5 +BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p +dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk +MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp +b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu +dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0 +MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi +E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa +MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI +hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN +95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd +2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= +-----END CERTIFICATE----- + +# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 2 Policy Validation Authority +# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 2 Policy Validation Authority +# Label: "ValiCert Class 2 VA" +# Serial: 1 +# MD5 Fingerprint: a9:23:75:9b:ba:49:36:6e:31:c2:db:f2:e7:66:ba:87 +# SHA1 Fingerprint: 31:7a:2a:d0:7f:2b:33:5e:f5:a1:c3:4e:4b:57:e8:b7:d8:f1:fc:a6 +# SHA256 Fingerprint: 58:d0:17:27:9c:d4:dc:63:ab:dd:b1:96:a6:c9:90:6c:30:c4:e0:87:83:ea:e8:c1:60:99:54:d6:93:55:59:6b +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz +BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y +aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG +9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy +NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y +azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw +Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl +cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY +dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9 +WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS +v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v +UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu +IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC +W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Expressz (Class C) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok +# Subject: CN=NetLock Expressz (Class C) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok +# Label: "NetLock Express (Class C) Root" +# Serial: 104 +# MD5 Fingerprint: 4f:eb:f1:f0:70:c2:80:63:5d:58:9f:da:12:3c:a9:c4 +# SHA1 Fingerprint: e3:92:51:2f:0a:cf:f5:05:df:f6:de:06:7f:75:37:e1:65:ea:57:4b +# SHA256 Fingerprint: 0b:5e:ed:4e:84:64:03:cf:55:e0:65:84:84:40:ed:2a:82:75:8b:f5:b9:aa:1f:25:3d:46:13:cf:a0:80:ff:3f +-----BEGIN CERTIFICATE----- +MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUx +ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 +b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQD +EytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBDKSBUYW51c2l0dmFueWtpYWRvMB4X +DTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJBgNVBAYTAkhVMREw +DwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9u +c2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMr +TmV0TG9jayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzAN +BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNA +OoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3ZW3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC +2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63euyucYT2BDMIJTLrdKwW +RMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQwDgYDVR0P +AQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEW +ggJNRklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0 +YWxhbm9zIFN6b2xnYWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFz +b2sgYWxhcGphbiBrZXN6dWx0LiBBIGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBO +ZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1iaXp0b3NpdGFzYSB2ZWRpLiBB +IGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0ZWxlIGF6IGVs +b2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs +ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25s +YXBqYW4gYSBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kg +a2VyaGV0byBheiBlbGxlbm9yemVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4g +SU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5kIHRoZSB1c2Ugb2YgdGhpcyBjZXJ0 +aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQUyBhdmFpbGFibGUg +YXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwgYXQg +Y3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmY +ta3UzbM2xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2g +pO0u9f38vf5NNwgMvOOWgyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4 +Fp1hBWeAyNDYpQcCNJgEjTME1A== +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Uzleti (Class B) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok +# Subject: CN=NetLock Uzleti (Class B) Tanusitvanykiado O=NetLock Halozatbiztonsagi Kft. OU=Tanusitvanykiadok +# Label: "NetLock Business (Class B) Root" +# Serial: 105 +# MD5 Fingerprint: 39:16:aa:b9:6a:41:e1:14:69:df:9e:6c:3b:72:dc:b6 +# SHA1 Fingerprint: 87:9f:4b:ee:05:df:98:58:3b:e3:60:d6:33:e7:0d:3f:fe:98:71:af +# SHA256 Fingerprint: 39:df:7b:68:2b:7b:93:8f:84:71:54:81:cc:de:8d:60:d8:f2:2e:c5:98:87:7d:0a:aa:c1:2b:59:18:2b:03:12 +-----BEGIN CERTIFICATE----- +MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUx +ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 +b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQD +EylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikgVGFudXNpdHZhbnlraWFkbzAeFw05 +OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYDVQQGEwJIVTERMA8G +A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh +Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5l +dExvY2sgVXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqG +SIb3DQEBAQUAA4GNADCBiQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xK +gZjupNTKihe5In+DCnVMm8Bp2GQ5o+2So/1bXHQawEfKOml2mrriRBf8TKPV/riX +iK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr1nGTLbO/CVRY7QbrqHvc +Q7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8E +BAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1G +SUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFu +b3MgU3pvbGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBh +bGFwamFuIGtlc3p1bHQuIEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExv +Y2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGln +aXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0 +IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh +c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGph +biBhIGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJo +ZXRvIGF6IGVsbGVub3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBP +UlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmlj +YXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBo +dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNA +bmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06 +sPgzTEdM43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXa +n3BukxowOR0w2y7jfLKRstE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKS +NitjrFgBazMpUIaD8QFI +-----END CERTIFICATE----- + +# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 3 Policy Validation Authority +# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 3 Policy Validation Authority +# Label: "RSA Root Certificate 1" +# Serial: 1 +# MD5 Fingerprint: a2:6f:53:b7:ee:40:db:4a:68:e7:fa:18:d9:10:4b:72 +# SHA1 Fingerprint: 69:bd:8c:f4:9c:d3:00:fb:59:2e:17:93:ca:55:6a:f3:ec:aa:35:fb +# SHA256 Fingerprint: bc:23:f9:8a:31:3c:b9:2d:e3:bb:fc:3a:5a:9f:44:61:ac:39:49:4c:4a:e1:5a:9e:9d:f1:31:e9:9b:73:01:9a +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz +BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y +aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG +9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMjIzM1oXDTE5MDYy +NjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y +azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw +Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl +cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfD +cnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs +2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqY +JJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliE +Zwgs3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJ +n0WuPIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A +PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu +-----END CERTIFICATE----- + +# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 1 Policy Validation Authority +# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 1 Policy Validation Authority +# Label: "ValiCert Class 1 VA" +# Serial: 1 +# MD5 Fingerprint: 65:58:ab:15:ad:57:6c:1e:a8:a7:b5:69:ac:bf:ff:eb +# SHA1 Fingerprint: e5:df:74:3c:b6:01:c4:9b:98:43:dc:ab:8c:e8:6a:81:10:9f:e4:8e +# SHA256 Fingerprint: f4:c1:49:55:1a:30:13:a3:5b:c7:bf:fe:17:a7:f3:44:9b:c1:ab:5b:5a:0a:e7:4b:06:c2:3b:90:00:4c:01:04 +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz +BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y +aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG +9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIyMjM0OFoXDTE5MDYy +NTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y +azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw +Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl +cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9Y +LqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+ +TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8Y +TfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0 +LBwGlN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLW +I8sogTLDAHkY7FkXicnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPw +nXS3qT6gpf+2SQMT2iLM7XGCK5nPOrf1LXLI +-----END CERTIFICATE----- + +# Issuer: CN=Equifax Secure eBusiness CA-1 O=Equifax Secure Inc. +# Subject: CN=Equifax Secure eBusiness CA-1 O=Equifax Secure Inc. +# Label: "Equifax Secure eBusiness CA 1" +# Serial: 4 +# MD5 Fingerprint: 64:9c:ef:2e:44:fc:c6:8f:52:07:d0:51:73:8f:cb:3d +# SHA1 Fingerprint: da:40:18:8b:91:89:a3:ed:ee:ae:da:97:fe:2f:9d:f5:b7:d1:8a:41 +# SHA256 Fingerprint: cf:56:ff:46:a4:a1:86:10:9d:d9:65:84:b5:ee:b5:8a:51:0c:42:75:b0:e5:f9:4f:40:bb:ae:86:5e:19:f6:73 +-----BEGIN CERTIFICATE----- +MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEc +MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBT +ZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQw +MDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5j +LjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwgZ8wDQYJ +KoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ1MRo +RvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBu +WqDZQu4aIZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKw +Env+j6YDAgMBAAGjZjBkMBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTAD +AQH/MB8GA1UdIwQYMBaAFEp4MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRK +eDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZM +zfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+ +WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN +/Bf+KpYrtWKmpj29f5JZzVoqgrI3eQ== +-----END CERTIFICATE----- + +# Issuer: CN=Equifax Secure Global eBusiness CA-1 O=Equifax Secure Inc. +# Subject: CN=Equifax Secure Global eBusiness CA-1 O=Equifax Secure Inc. +# Label: "Equifax Secure Global eBusiness CA" +# Serial: 1 +# MD5 Fingerprint: 8f:5d:77:06:27:c4:98:3c:5b:93:78:e7:d7:7d:9b:cc +# SHA1 Fingerprint: 7e:78:4a:10:1c:82:65:cc:2d:e1:f1:6d:47:b4:40:ca:d9:0a:19:45 +# SHA256 Fingerprint: 5f:0b:62:ea:b5:e3:53:ea:65:21:65:16:58:fb:b6:53:59:f4:43:28:0a:4a:fb:d1:04:d7:7d:10:f9:f0:4c:07 +-----BEGIN CERTIFICATE----- +MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEc +MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBT +ZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIw +MDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0VxdWlmYXggU2Vj +dXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEdsb2JhbCBlQnVzaW5l +c3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRVPEnC +UdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc +58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/ +o5brhTMhHD4ePmBudpxnhcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAH +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1dr +aGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUA +A4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkA +Z70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv +8qIYNMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV +-----END CERTIFICATE----- + +# Issuer: CN=Thawte Premium Server CA O=Thawte Consulting cc OU=Certification Services Division +# Subject: CN=Thawte Premium Server CA O=Thawte Consulting cc OU=Certification Services Division +# Label: "Thawte Premium Server CA" +# Serial: 1 +# MD5 Fingerprint: 06:9f:69:79:16:66:90:02:1b:8c:8c:a2:c3:07:6f:3a +# SHA1 Fingerprint: 62:7f:8d:78:27:65:63:99:d2:7d:7f:90:44:c9:fe:b3:f3:3e:fa:9a +# SHA256 Fingerprint: ab:70:36:36:5c:71:54:aa:29:c2:c2:9f:5d:41:91:16:3b:16:2a:22:25:01:13:57:d5:6d:07:ff:a7:bc:1f:72 +-----BEGIN CERTIFICATE----- +MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkEx +FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD +VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2Vy +dmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29t +MB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpB +MRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsG +A1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRp +b24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNl +cnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNv +bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkE +VdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQ +ug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMR +uHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG +9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI +hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JM +pAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg== +-----END CERTIFICATE----- + +# Issuer: CN=Thawte Server CA O=Thawte Consulting cc OU=Certification Services Division +# Subject: CN=Thawte Server CA O=Thawte Consulting cc OU=Certification Services Division +# Label: "Thawte Server CA" +# Serial: 1 +# MD5 Fingerprint: c5:70:c4:a2:ed:53:78:0c:c8:10:53:81:64:cb:d0:1d +# SHA1 Fingerprint: 23:e5:94:94:51:95:f2:41:48:03:b4:d5:64:d2:a3:a3:f5:d8:8b:8c +# SHA256 Fingerprint: b4:41:0b:73:e2:e6:ea:ca:47:fb:c4:2f:8f:a4:01:8a:f4:38:1d:c5:4c:fa:a8:44:50:46:1e:ed:09:45:4d:e9 +-----BEGIN CERTIFICATE----- +MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkEx +FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD +VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEm +MCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wHhcNOTYwODAx +MDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3 +dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNl +cyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3 +DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD +gY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91 +yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCX +L+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGj +EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG +7oWDTSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e +QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZ +qdq5snUb9kLy78fyGPmJvKP/iiMucEc= +-----END CERTIFICATE----- + +# Issuer: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority +# Subject: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority +# Label: "Verisign Class 3 Public Primary Certification Authority" +# Serial: 149843929435818692848040365716851702463 +# MD5 Fingerprint: 10:fc:63:5d:f6:26:3e:0d:f3:25:be:5f:79:cd:67:67 +# SHA1 Fingerprint: 74:2c:31:92:e6:07:e4:24:eb:45:49:54:2b:e1:bb:c5:3e:61:74:e2 +# SHA256 Fingerprint: e7:68:56:34:ef:ac:f6:9a:ce:93:9a:6b:25:5b:7b:4f:ab:ef:42:93:5b:50:a2:65:ac:b5:cb:60:27:e4:4e:70 +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz +cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 +MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV +BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN +ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE +BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is +I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G +CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do +lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc +AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k +-----END CERTIFICATE----- + +# Issuer: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority +# Subject: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority +# Label: "Verisign Class 3 Public Primary Certification Authority" +# Serial: 80507572722862485515306429940691309246 +# MD5 Fingerprint: ef:5a:f1:33:ef:f1:cd:bb:51:02:ee:12:14:4b:96:c4 +# SHA1 Fingerprint: a1:db:63:93:91:6f:17:e4:18:55:09:40:04:15:c7:02:40:b0:ae:6b +# SHA256 Fingerprint: a4:b6:b3:99:6f:c2:f3:06:b3:fd:86:81:bd:63:41:3d:8c:50:09:cc:4f:a3:29:c2:cc:f0:e2:fa:1b:14:03:05 +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz +cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 +MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV +BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN +ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE +BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is +I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G +CSqGSIb3DQEBBQUAA4GBABByUqkFFBkyCEHwxWsKzH4PIRnN5GfcX6kb5sroc50i +2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWXbj9T/UWZYB2oK0z5XqcJ +2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/D/xwzoiQ +-----END CERTIFICATE----- + +# Issuer: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority - G2/(c) 1998 VeriSign, Inc. - For authorized use only/VeriSign Trust Network +# Subject: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority - G2/(c) 1998 VeriSign, Inc. - For authorized use only/VeriSign Trust Network +# Label: "Verisign Class 3 Public Primary Certification Authority - G2" +# Serial: 167285380242319648451154478808036881606 +# MD5 Fingerprint: a2:33:9b:4c:74:78:73:d4:6c:e7:c1:f3:8d:cb:5c:e9 +# SHA1 Fingerprint: 85:37:1c:a6:e5:50:14:3d:ce:28:03:47:1b:de:3a:09:e8:f8:77:0f +# SHA256 Fingerprint: 83:ce:3c:12:29:68:8a:59:3d:48:5f:81:97:3c:0f:91:95:43:1e:da:37:cc:5e:36:43:0e:79:c7:a8:88:63:8b +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ +BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh +c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy +MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp +emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X +DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw +FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg +UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo +YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5 +MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4 +pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0 +13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID +AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk +U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i +F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY +oJ2daZH9 +-----END CERTIFICATE----- + +# Issuer: CN=GTE CyberTrust Global Root O=GTE Corporation OU=GTE CyberTrust Solutions, Inc. +# Subject: CN=GTE CyberTrust Global Root O=GTE Corporation OU=GTE CyberTrust Solutions, Inc. +# Label: "GTE CyberTrust Global Root" +# Serial: 421 +# MD5 Fingerprint: ca:3d:d3:68:f1:03:5c:d0:32:fa:b8:2b:59:e8:5a:db +# SHA1 Fingerprint: 97:81:79:50:d8:1c:96:70:cc:34:d8:09:cf:79:44:31:36:7e:f4:74 +# SHA256 Fingerprint: a5:31:25:18:8d:21:10:aa:96:4b:02:c7:b7:c6:da:32:03:17:08:94:e5:fb:71:ff:fb:66:67:d5:e6:81:0a:36 +-----BEGIN CERTIFICATE----- +MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD +VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv +bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv +b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV +UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU +cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds +b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH +iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS +r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4 +04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r +GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9 +3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P +lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ +-----END CERTIFICATE----- diff --git a/pip/_vendor/requests/certs.py b/pip/_vendor/requests/certs.py index bc00826191d..07e64750703 100644 --- a/pip/_vendor/requests/certs.py +++ b/pip/_vendor/requests/certs.py @@ -11,14 +11,15 @@ environment, you can change the definition of where() to return a separately packaged CA bundle. """ - import os.path - -def where(): - """Return the preferred certificate bundle.""" - # vendored bundle inside Requests - return os.path.join(os.path.dirname(__file__), 'cacert.pem') +try: + from certifi import where +except ImportError: + def where(): + """Return the preferred certificate bundle.""" + # vendored bundle inside Requests + return os.path.join(os.path.dirname(__file__), 'cacert.pem') if __name__ == '__main__': print(where()) diff --git a/pip/_vendor/requests/compat.py b/pip/_vendor/requests/compat.py index bdf10d6a9f9..70edff78495 100644 --- a/pip/_vendor/requests/compat.py +++ b/pip/_vendor/requests/compat.py @@ -21,61 +21,11 @@ #: Python 3.x? is_py3 = (_ver[0] == 3) -#: Python 3.0.x -is_py30 = (is_py3 and _ver[1] == 0) - -#: Python 3.1.x -is_py31 = (is_py3 and _ver[1] == 1) - -#: Python 3.2.x -is_py32 = (is_py3 and _ver[1] == 2) - -#: Python 3.3.x -is_py33 = (is_py3 and _ver[1] == 3) - -#: Python 3.4.x -is_py34 = (is_py3 and _ver[1] == 4) - -#: Python 2.7.x -is_py27 = (is_py2 and _ver[1] == 7) - -#: Python 2.6.x -is_py26 = (is_py2 and _ver[1] == 6) - -#: Python 2.5.x -is_py25 = (is_py2 and _ver[1] == 5) - -#: Python 2.4.x -is_py24 = (is_py2 and _ver[1] == 4) # I'm assuming this is not by choice. - - -# --------- -# Platforms -# --------- - - -# Syntax sugar. -_ver = sys.version.lower() - -is_pypy = ('pypy' in _ver) -is_jython = ('jython' in _ver) -is_ironpython = ('iron' in _ver) - -# Assume CPython, if nothing else. -is_cpython = not any((is_pypy, is_jython, is_ironpython)) - -# Windows-based system. -is_windows = 'win32' in str(sys.platform).lower() - -# Standard Linux 2+ system. -is_linux = ('linux' in str(sys.platform).lower()) -is_osx = ('darwin' in str(sys.platform).lower()) -is_hpux = ('hpux' in str(sys.platform).lower()) # Complete guess. -is_solaris = ('solar==' in str(sys.platform).lower()) # Complete guess. - try: import simplejson as json -except ImportError: +except (ImportError, SyntaxError): + # simplejson does not support Python 3.2, it throws a SyntaxError + # because of u'...' Unicode literals. import json # --------- @@ -90,7 +40,6 @@ from Cookie import Morsel from StringIO import StringIO from .packages.urllib3.packages.ordered_dict import OrderedDict - from httplib import IncompleteRead builtin_str = str bytes = str @@ -98,7 +47,6 @@ basestring = basestring numeric_types = (int, long, float) - elif is_py3: from urllib.parse import urlparse, urlunparse, urljoin, urlsplit, urlencode, quote, unquote, quote_plus, unquote_plus, urldefrag from urllib.request import parse_http_list, getproxies, proxy_bypass @@ -106,7 +54,6 @@ from http.cookies import Morsel from io import StringIO from collections import OrderedDict - from http.client import IncompleteRead builtin_str = str str = str diff --git a/pip/_vendor/requests/cookies.py b/pip/_vendor/requests/cookies.py index 831c49c6d27..eee5168f2c5 100644 --- a/pip/_vendor/requests/cookies.py +++ b/pip/_vendor/requests/cookies.py @@ -6,7 +6,9 @@ requests.utils imports from here, so be careful with imports. """ +import copy import time +import calendar import collections from .compat import cookielib, urlparse, urlunparse, Morsel @@ -142,10 +144,13 @@ def remove_cookie_by_name(cookiejar, name, domain=None, path=None): """ clearables = [] for cookie in cookiejar: - if cookie.name == name: - if domain is None or domain == cookie.domain: - if path is None or path == cookie.path: - clearables.append((cookie.domain, cookie.path, cookie.name)) + if cookie.name != name: + continue + if domain is not None and domain != cookie.domain: + continue + if path is not None and path != cookie.path: + continue + clearables.append((cookie.domain, cookie.path, cookie.name)) for domain, path, name in clearables: cookiejar.clear(domain, path, name) @@ -157,26 +162,28 @@ class CookieConflictError(RuntimeError): class RequestsCookieJar(cookielib.CookieJar, collections.MutableMapping): - """Compatibility class; is a cookielib.CookieJar, but exposes a dict interface. + """Compatibility class; is a cookielib.CookieJar, but exposes a dict + interface. This is the CookieJar we create by default for requests and sessions that don't specify one, since some clients may expect response.cookies and session.cookies to support dict operations. - Don't use the dict interface internally; it's just for compatibility with - with external client code. All `requests` code should work out of the box - with externally provided instances of CookieJar, e.g., LWPCookieJar and - FileCookieJar. - - Caution: dictionary operations that are normally O(1) may be O(n). + Requests does not use the dict interface internally; it's just for + compatibility with external client code. All requests code should work + out of the box with externally provided instances of ``CookieJar``, e.g. + ``LWPCookieJar`` and ``FileCookieJar``. Unlike a regular CookieJar, this class is pickleable. - """ + .. warning:: dictionary operations that are normally O(1) may be O(n). + """ def get(self, name, default=None, domain=None, path=None): """Dict-like get() that also supports optional domain and path args in order to resolve naming collisions from using one cookie jar over - multiple domains. Caution: operation is O(n), not O(1).""" + multiple domains. + + .. warning:: operation is O(n), not O(1).""" try: return self._find_no_duplicates(name, domain, path) except KeyError: @@ -199,37 +206,38 @@ def set(self, name, value, **kwargs): return c def iterkeys(self): - """Dict-like iterkeys() that returns an iterator of names of cookies from the jar. - See itervalues() and iteritems().""" + """Dict-like iterkeys() that returns an iterator of names of cookies + from the jar. See itervalues() and iteritems().""" for cookie in iter(self): yield cookie.name def keys(self): - """Dict-like keys() that returns a list of names of cookies from the jar. - See values() and items().""" + """Dict-like keys() that returns a list of names of cookies from the + jar. See values() and items().""" return list(self.iterkeys()) def itervalues(self): - """Dict-like itervalues() that returns an iterator of values of cookies from the jar. - See iterkeys() and iteritems().""" + """Dict-like itervalues() that returns an iterator of values of cookies + from the jar. See iterkeys() and iteritems().""" for cookie in iter(self): yield cookie.value def values(self): - """Dict-like values() that returns a list of values of cookies from the jar. - See keys() and items().""" + """Dict-like values() that returns a list of values of cookies from the + jar. See keys() and items().""" return list(self.itervalues()) def iteritems(self): - """Dict-like iteritems() that returns an iterator of name-value tuples from the jar. - See iterkeys() and itervalues().""" + """Dict-like iteritems() that returns an iterator of name-value tuples + from the jar. See iterkeys() and itervalues().""" for cookie in iter(self): yield cookie.name, cookie.value def items(self): - """Dict-like items() that returns a list of name-value tuples from the jar. - See keys() and values(). Allows client-code to call "dict(RequestsCookieJar) - and get a vanilla python dict of key value pairs.""" + """Dict-like items() that returns a list of name-value tuples from the + jar. See keys() and values(). Allows client-code to call + ``dict(RequestsCookieJar)`` and get a vanilla python dict of key value + pairs.""" return list(self.iteritems()) def list_domains(self): @@ -259,8 +267,9 @@ def multiple_domains(self): return False # there is only one domain in jar def get_dict(self, domain=None, path=None): - """Takes as an argument an optional domain and path and returns a plain old - Python dict of name-value pairs of cookies that meet the requirements.""" + """Takes as an argument an optional domain and path and returns a plain + old Python dict of name-value pairs of cookies that meet the + requirements.""" dictionary = {} for cookie in iter(self): if (domain is None or cookie.domain == domain) and (path is None @@ -268,22 +277,31 @@ def get_dict(self, domain=None, path=None): dictionary[cookie.name] = cookie.value return dictionary + def __contains__(self, name): + try: + return super(RequestsCookieJar, self).__contains__(name) + except CookieConflictError: + return True + def __getitem__(self, name): - """Dict-like __getitem__() for compatibility with client code. Throws exception - if there are more than one cookie with name. In that case, use the more - explicit get() method instead. Caution: operation is O(n), not O(1).""" + """Dict-like __getitem__() for compatibility with client code. Throws + exception if there are more than one cookie with name. In that case, + use the more explicit get() method instead. + + .. warning:: operation is O(n), not O(1).""" return self._find_no_duplicates(name) def __setitem__(self, name, value): - """Dict-like __setitem__ for compatibility with client code. Throws exception - if there is already a cookie of that name in the jar. In that case, use the more - explicit set() method instead.""" + """Dict-like __setitem__ for compatibility with client code. Throws + exception if there is already a cookie of that name in the jar. In that + case, use the more explicit set() method instead.""" self.set(name, value) def __delitem__(self, name): - """Deletes a cookie given a name. Wraps cookielib.CookieJar's remove_cookie_by_name().""" + """Deletes a cookie given a name. Wraps ``cookielib.CookieJar``'s + ``remove_cookie_by_name()``.""" remove_cookie_by_name(self, name) def set_cookie(self, cookie, *args, **kwargs): @@ -295,15 +313,16 @@ def update(self, other): """Updates this jar with cookies from another CookieJar or dict-like""" if isinstance(other, cookielib.CookieJar): for cookie in other: - self.set_cookie(cookie) + self.set_cookie(copy.copy(cookie)) else: super(RequestsCookieJar, self).update(other) def _find(self, name, domain=None, path=None): - """Requests uses this method internally to get cookie values. Takes as args name - and optional domain and path. Returns a cookie.value. If there are conflicting cookies, - _find arbitrarily chooses one. See _find_no_duplicates if you want an exception thrown - if there are conflicting cookies.""" + """Requests uses this method internally to get cookie values. Takes as + args name and optional domain and path. Returns a cookie.value. If + there are conflicting cookies, _find arbitrarily chooses one. See + _find_no_duplicates if you want an exception thrown if there are + conflicting cookies.""" for cookie in iter(self): if cookie.name == name: if domain is None or cookie.domain == domain: @@ -313,10 +332,11 @@ def _find(self, name, domain=None, path=None): raise KeyError('name=%r, domain=%r, path=%r' % (name, domain, path)) def _find_no_duplicates(self, name, domain=None, path=None): - """__get_item__ and get call _find_no_duplicates -- never used in Requests internally. - Takes as args name and optional domain and path. Returns a cookie.value. - Throws KeyError if cookie is not found and CookieConflictError if there are - multiple cookies that match name and optionally domain and path.""" + """Both ``__get_item__`` and ``get`` call this function: it's never + used elsewhere in Requests. Takes as args name and optional domain and + path. Returns a cookie.value. Throws KeyError if cookie is not found + and CookieConflictError if there are multiple cookies that match name + and optionally domain and path.""" toReturn = None for cookie in iter(self): if cookie.name == name: @@ -350,6 +370,21 @@ def copy(self): return new_cj +def _copy_cookie_jar(jar): + if jar is None: + return None + + if hasattr(jar, 'copy'): + # We're dealing with an instance of RequestsCookieJar + return jar.copy() + # We're dealing with a generic CookieJar instance + new_jar = copy.copy(jar) + new_jar.clear() + for cookie in jar: + new_jar.set_cookie(copy.copy(cookie)) + return new_jar + + def create_cookie(name, value, **kwargs): """Make a cookie from underspecified parameters. @@ -390,11 +425,15 @@ def morsel_to_cookie(morsel): expires = None if morsel['max-age']: - expires = time.time() + morsel['max-age'] + try: + expires = int(time.time() + int(morsel['max-age'])) + except ValueError: + raise TypeError('max-age: %s must be integer' % morsel['max-age']) elif morsel['expires']: time_template = '%a, %d-%b-%Y %H:%M:%S GMT' - expires = time.mktime( - time.strptime(morsel['expires'], time_template)) - time.timezone + expires = calendar.timegm( + time.strptime(morsel['expires'], time_template) + ) return create_cookie( comment=morsel['comment'], comment_url=bool(morsel['comment']), @@ -440,7 +479,7 @@ def merge_cookies(cookiejar, cookies): """ if not isinstance(cookiejar, cookielib.CookieJar): raise ValueError('You can only merge into CookieJar') - + if isinstance(cookies, dict): cookiejar = cookiejar_from_dict( cookies, cookiejar=cookiejar, overwrite=False) diff --git a/pip/_vendor/requests/exceptions.py b/pip/_vendor/requests/exceptions.py index a4ee9d630c2..ba0b910e316 100644 --- a/pip/_vendor/requests/exceptions.py +++ b/pip/_vendor/requests/exceptions.py @@ -44,7 +44,23 @@ class SSLError(ConnectionError): class Timeout(RequestException): - """The request timed out.""" + """The request timed out. + + Catching this error will catch both + :exc:`~requests.exceptions.ConnectTimeout` and + :exc:`~requests.exceptions.ReadTimeout` errors. + """ + + +class ConnectTimeout(ConnectionError, Timeout): + """The request timed out while trying to connect to the remote server. + + Requests that produced this error are safe to retry. + """ + + +class ReadTimeout(Timeout): + """The server did not send any data in the allotted amount of time.""" class URLRequired(RequestException): @@ -73,3 +89,26 @@ class ChunkedEncodingError(RequestException): class ContentDecodingError(RequestException, BaseHTTPError): """Failed to decode response content""" + + +class StreamConsumedError(RequestException, TypeError): + """The content for this response was already consumed""" + + +class RetryError(RequestException): + """Custom retries logic failed""" + + +# Warnings + + +class RequestsWarning(Warning): + """Base warning for Requests.""" + pass + + +class FileModeWarning(RequestsWarning, DeprecationWarning): + """ + A file was opened in text mode, but Requests determined its binary length. + """ + pass diff --git a/pip/_vendor/requests/hooks.py b/pip/_vendor/requests/hooks.py index 5dfaf6b6801..9da94366d7e 100644 --- a/pip/_vendor/requests/hooks.py +++ b/pip/_vendor/requests/hooks.py @@ -12,34 +12,23 @@ The response generated from a Request. """ - - HOOKS = ['response'] - def default_hooks(): - hooks = {} - for event in HOOKS: - hooks[event] = [] - return hooks + return dict((event, []) for event in HOOKS) # TODO: response is the only one def dispatch_hook(key, hooks, hook_data, **kwargs): """Dispatches a hook dictionary on a given piece of data.""" - hooks = hooks or dict() - - if key in hooks: - hooks = hooks.get(key) - + hooks = hooks.get(key) + if hooks: if hasattr(hooks, '__call__'): hooks = [hooks] - for hook in hooks: _hook_data = hook(hook_data, **kwargs) if _hook_data is not None: hook_data = _hook_data - return hook_data diff --git a/pip/_vendor/requests/models.py b/pip/_vendor/requests/models.py index 120968ff511..fe4bec1bd33 100644 --- a/pip/_vendor/requests/models.py +++ b/pip/_vendor/requests/models.py @@ -15,31 +15,35 @@ from .structures import CaseInsensitiveDict from .auth import HTTPBasicAuth -from .cookies import cookiejar_from_dict, get_cookie_header +from .cookies import cookiejar_from_dict, get_cookie_header, _copy_cookie_jar from .packages.urllib3.fields import RequestField from .packages.urllib3.filepost import encode_multipart_formdata from .packages.urllib3.util import parse_url -from .packages.urllib3.exceptions import DecodeError +from .packages.urllib3.exceptions import ( + DecodeError, ReadTimeoutError, ProtocolError, LocationParseError) from .exceptions import ( - HTTPError, RequestException, MissingSchema, InvalidURL, - ChunkedEncodingError, ContentDecodingError) + HTTPError, MissingSchema, InvalidURL, ChunkedEncodingError, + ContentDecodingError, ConnectionError, StreamConsumedError) from .utils import ( guess_filename, get_auth_from_url, requote_uri, stream_decode_response_unicode, to_key_val_list, parse_header_links, iter_slices, guess_json_utf, super_len, to_native_string) from .compat import ( cookielib, urlunparse, urlsplit, urlencode, str, bytes, StringIO, - is_py2, chardet, json, builtin_str, basestring, IncompleteRead) + is_py2, chardet, builtin_str, basestring) +from .compat import json as complexjson from .status_codes import codes #: The set of HTTP status codes that indicate an automatically #: processable redirect. REDIRECT_STATI = ( - codes.moved, # 301 - codes.found, # 302 - codes.other, # 303 - codes.temporary_moved, # 307 + codes.moved, # 301 + codes.found, # 302 + codes.other, # 303 + codes.temporary_redirect, # 307 + codes.permanent_redirect, # 308 ) + DEFAULT_REDIRECT_LIMIT = 30 CONTENT_CHUNK_SIZE = 10 * 1024 ITER_CHUNK_SIZE = 512 @@ -99,8 +103,10 @@ def _encode_files(files, data): """Build the body for a multipart/form-data request. Will successfully encode files when passed as a dict or a list of - 2-tuples. Order is retained if data is a list of 2-tuples but arbitrary + tuples. Order is retained if data is a list of tuples but arbitrary if parameters are supplied as a dict. + The tuples may be 2-tuples (filename, fileobj), 3-tuples (filename, fileobj, contentype) + or 4-tuples (filename, fileobj, contentype, custom_headers). """ if (not files): @@ -139,13 +145,13 @@ def _encode_files(files, data): else: fn = guess_filename(v) or k fp = v - if isinstance(fp, str): - fp = StringIO(fp) - if isinstance(fp, bytes): - fp = BytesIO(fp) - rf = RequestField(name=k, data=fp.read(), - filename=fn, headers=fh) + if isinstance(fp, (str, bytes, bytearray)): + fdata = fp + else: + fdata = fp.read() + + rf = RequestField(name=k, data=fdata, filename=fn, headers=fh) rf.make_multipart(content_type=ft) new_fields.append(rf) @@ -187,7 +193,8 @@ class Request(RequestHooksMixin): :param url: URL to send. :param headers: dictionary of headers to send. :param files: dictionary of {filename: fileobject} files to multipart upload. - :param data: the body to attach the request. If a dictionary is provided, form-encoding will take place. + :param data: the body to attach to the request. If a dictionary is provided, form-encoding will take place. + :param json: json for the body to attach to the request (if files or data is not specified). :param params: dictionary of URL parameters to append to the URL. :param auth: Auth handler or (user, pass) tuple. :param cookies: dictionary or CookieJar of cookies to attach to this request. @@ -201,16 +208,8 @@ class Request(RequestHooksMixin): """ - def __init__(self, - method=None, - url=None, - headers=None, - files=None, - data=None, - params=None, - auth=None, - cookies=None, - hooks=None): + def __init__(self, method=None, url=None, headers=None, files=None, + data=None, params=None, auth=None, cookies=None, hooks=None, json=None): # Default empty dicts for dict params. data = [] if data is None else data @@ -228,6 +227,7 @@ def __init__(self, self.headers = headers self.files = files self.data = data + self.json = json self.params = params self.auth = auth self.cookies = cookies @@ -244,6 +244,7 @@ def prepare(self): headers=self.headers, files=self.files, data=self.data, + json=self.json, params=self.params, auth=self.auth, cookies=self.cookies, @@ -287,15 +288,16 @@ def __init__(self): self.hooks = default_hooks() def prepare(self, method=None, url=None, headers=None, files=None, - data=None, params=None, auth=None, cookies=None, hooks=None): + data=None, params=None, auth=None, cookies=None, hooks=None, json=None): """Prepares the entire request with the given parameters.""" self.prepare_method(method) self.prepare_url(url, params) self.prepare_headers(headers) self.prepare_cookies(cookies) - self.prepare_body(data, files) + self.prepare_body(data, files, json) self.prepare_auth(auth, url) + # Note that prepare_auth must be last to enable authentication schemes # such as OAuth to work on a fully prepared request. @@ -309,8 +311,8 @@ def copy(self): p = PreparedRequest() p.method = self.method p.url = self.url - p.headers = self.headers.copy() - p._cookies = self._cookies.copy() + p.headers = self.headers.copy() if self.headers is not None else None + p._cookies = _copy_cookie_jar(self._cookies) p.body = self.body p.hooks = self.hooks return p @@ -319,30 +321,38 @@ def prepare_method(self, method): """Prepares the given HTTP method.""" self.method = method if self.method is not None: - self.method = self.method.upper() + self.method = to_native_string(self.method.upper()) def prepare_url(self, url, params): """Prepares the given HTTP URL.""" #: Accept objects that have string representations. - try: - url = unicode(url) - except NameError: - # We're on Python 3. - url = str(url) - except UnicodeDecodeError: - pass - - # Don't do any URL preparation for oddball schemes + #: We're unable to blindly call unicode/str functions + #: as this will include the bytestring indicator (b'') + #: on python 3.x. + #: https://github.com/kennethreitz/requests/pull/2238 + if isinstance(url, bytes): + url = url.decode('utf8') + else: + url = unicode(url) if is_py2 else str(url) + + # Don't do any URL preparation for non-HTTP schemes like `mailto`, + # `data` etc to work around exceptions from `url_parse`, which + # handles RFC 3986 only. if ':' in url and not url.lower().startswith('http'): self.url = url return # Support for unicode domain names and paths. - scheme, auth, host, port, path, query, fragment = parse_url(url) + try: + scheme, auth, host, port, path, query, fragment = parse_url(url) + except LocationParseError as e: + raise InvalidURL(*e.args) if not scheme: - raise MissingSchema("Invalid URL {0!r}: No schema supplied. " - "Perhaps you meant http://{0}?".format(url)) + error = ("Invalid URL {0!r}: No schema supplied. Perhaps you meant http://{0}?") + error = error.format(to_native_string(url, 'utf8')) + + raise MissingSchema(error) if not host: raise InvalidURL("Invalid URL %r: No host supplied" % url) @@ -377,6 +387,9 @@ def prepare_url(self, url, params): if isinstance(fragment, str): fragment = fragment.encode('utf-8') + if isinstance(params, (str, bytes)): + params = to_native_string(params) + enc_params = self._encode_params(params) if enc_params: if query: @@ -395,7 +408,7 @@ def prepare_headers(self, headers): else: self.headers = CaseInsensitiveDict() - def prepare_body(self, data, files): + def prepare_body(self, data, files, json=None): """Prepares the given HTTP body data.""" # Check if file, fo, generator, iterator. @@ -406,6 +419,10 @@ def prepare_body(self, data, files): content_type = None length = None + if not data and json is not None: + content_type = 'application/json' + body = complexjson.dumps(json) + is_stream = all([ hasattr(data, '__iter__'), not isinstance(data, (basestring, list, tuple, dict)) @@ -422,7 +439,7 @@ def prepare_body(self, data, files): if files: raise NotImplementedError('Streamed bodies and files are mutually exclusive.') - if length is not None: + if length: self.headers['Content-Length'] = builtin_str(length) else: self.headers['Transfer-Encoding'] = 'chunked' @@ -433,7 +450,7 @@ def prepare_body(self, data, files): else: if data: body = self._encode_params(data) - if isinstance(data, str) or isinstance(data, builtin_str) or hasattr(data, 'read'): + if isinstance(data, basestring) or hasattr(data, 'read'): content_type = None else: content_type = 'application/x-www-form-urlencoded' @@ -441,21 +458,23 @@ def prepare_body(self, data, files): self.prepare_content_length(body) # Add content-type if it wasn't explicitly provided. - if (content_type) and (not 'content-type' in self.headers): + if content_type and ('content-type' not in self.headers): self.headers['Content-Type'] = content_type self.body = body def prepare_content_length(self, body): if hasattr(body, 'seek') and hasattr(body, 'tell'): + curr_pos = body.tell() body.seek(0, 2) - self.headers['Content-Length'] = builtin_str(body.tell()) - body.seek(0, 0) + end_pos = body.tell() + self.headers['Content-Length'] = builtin_str(max(0, end_pos - curr_pos)) + body.seek(curr_pos, 0) elif body is not None: l = super_len(body) if l: self.headers['Content-Length'] = builtin_str(l) - elif self.method not in ('GET', 'HEAD'): + elif (self.method not in ('GET', 'HEAD')) and (self.headers.get('Content-Length') is None): self.headers['Content-Length'] = '0' def prepare_auth(self, auth, url=''): @@ -481,7 +500,15 @@ def prepare_auth(self, auth, url=''): self.prepare_content_length(self.body) def prepare_cookies(self, cookies): - """Prepares the given HTTP cookie data.""" + """Prepares the given HTTP cookie data. + + This function eventually generates a ``Cookie`` header from the + given cookies using cookielib. Due to cookielib's design, the header + will not be regenerated if it already exists, meaning this function + can only be called once for the life of the + :class:`PreparedRequest ` object. Any subsequent calls + to ``prepare_cookies`` will have no actual effect, unless the "Cookie" + header is removed beforehand.""" if isinstance(cookies, cookielib.CookieJar): self._cookies = cookies @@ -494,6 +521,10 @@ def prepare_cookies(self, cookies): def prepare_hooks(self, hooks): """Prepares the given hooks.""" + # hooks can be passed as None to the prepare method and to this + # method. To prevent iterating over None, simply use an empty list + # if hooks is False-y + hooks = hooks or [] for event in hooks: self.register_hook(event, hooks[event]) @@ -504,16 +535,8 @@ class Response(object): """ __attrs__ = [ - '_content', - 'status_code', - 'headers', - 'url', - 'history', - 'encoding', - 'reason', - 'cookies', - 'elapsed', - 'request', + '_content', 'status_code', 'headers', 'url', 'history', + 'encoding', 'reason', 'cookies', 'elapsed', 'request' ] def __init__(self): @@ -553,9 +576,17 @@ def __init__(self): self.cookies = cookiejar_from_dict({}) #: The amount of time elapsed between sending the request - #: and the arrival of the response (as a timedelta) + #: and the arrival of the response (as a timedelta). + #: This property specifically measures the time taken between sending + #: the first byte of the request and finishing parsing the headers. It + #: is therefore unaffected by consuming the response content or the + #: value of the ``stream`` keyword argument. self.elapsed = datetime.timedelta(0) + #: The :class:`PreparedRequest ` object to which this + #: is a response. + self.request = None + def __getstate__(self): # Consume everything; accessing the content attribute makes # sure the content has been fully read. @@ -594,7 +625,7 @@ def __iter__(self): def ok(self): try: self.raise_for_status() - except RequestException: + except HTTPError: return False return True @@ -605,6 +636,11 @@ def is_redirect(self): """ return ('location' in self.headers and self.status_code in REDIRECT_STATI) + @property + def is_permanent_redirect(self): + """True if this Response one of the permanent versions of redirect""" + return ('location' in self.headers and self.status_code in (codes.moved_permanently, codes.permanent_redirect)) + @property def apparent_encoding(self): """The apparent encoding, provided by the chardet library""" @@ -620,17 +656,20 @@ def iter_content(self, chunk_size=1, decode_unicode=False): If decode_unicode is True, content will be decoded using the best available encoding based on the response. """ + def generate(): - try: - # Special case for urllib3. + # Special case for urllib3. + if hasattr(self.raw, 'stream'): try: for chunk in self.raw.stream(chunk_size, decode_content=True): yield chunk - except IncompleteRead as e: + except ProtocolError as e: raise ChunkedEncodingError(e) except DecodeError as e: raise ContentDecodingError(e) - except AttributeError: + except ReadTimeoutError as e: + raise ConnectionError(e) + else: # Standard file-like object. while True: chunk = self.raw.read(chunk_size) @@ -640,6 +679,8 @@ def generate(): self._content_consumed = True + if self._content_consumed and isinstance(self._content, bool): + raise StreamConsumedError() # simulate reading small chunks of the content reused_chunks = iter_slices(self._content, chunk_size) @@ -652,10 +693,12 @@ def generate(): return chunks - def iter_lines(self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=None): + def iter_lines(self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=None, delimiter=None): """Iterates over the response data, one line at a time. When stream=True is set on the request, this avoids reading the content at once into memory for large responses. + + .. note:: This method is not reentrant safe. """ pending = None @@ -664,7 +707,11 @@ def iter_lines(self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=None): if pending is not None: chunk = pending + chunk - lines = chunk.splitlines() + + if delimiter: + lines = chunk.split(delimiter) + else: + lines = chunk.splitlines() if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]: pending = lines.pop() @@ -753,14 +800,16 @@ def json(self, **kwargs): encoding = guess_json_utf(self.content) if encoding is not None: try: - return json.loads(self.content.decode(encoding), **kwargs) + return complexjson.loads( + self.content.decode(encoding), **kwargs + ) except UnicodeDecodeError: # Wrong UTF codec detected; usually because it's not UTF-8 # but some other 8-bit codec. This is an RFC violation, # and the server didn't bother to tell us what codec *was* # used. pass - return json.loads(self.text, **kwargs) + return complexjson.loads(self.text, **kwargs) @property def links(self): @@ -786,10 +835,10 @@ def raise_for_status(self): http_error_msg = '' if 400 <= self.status_code < 500: - http_error_msg = '%s Client Error: %s' % (self.status_code, self.reason) + http_error_msg = '%s Client Error: %s for url: %s' % (self.status_code, self.reason, self.url) elif 500 <= self.status_code < 600: - http_error_msg = '%s Server Error: %s' % (self.status_code, self.reason) + http_error_msg = '%s Server Error: %s for url: %s' % (self.status_code, self.reason, self.url) if http_error_msg: raise HTTPError(http_error_msg, response=self) @@ -800,4 +849,7 @@ def close(self): *Note: Should not normally need to be called explicitly.* """ + if not self._content_consumed: + return self.raw.close() + return self.raw.release_conn() diff --git a/pip/_vendor/requests/packages/__init__.py b/pip/_vendor/requests/packages/__init__.py index d62c4b7111b..971c2ad024d 100644 --- a/pip/_vendor/requests/packages/__init__.py +++ b/pip/_vendor/requests/packages/__init__.py @@ -1,3 +1,36 @@ +''' +Debian and other distributions "unbundle" requests' vendored dependencies, and +rewrite all imports to use the global versions of ``urllib3`` and ``chardet``. +The problem with this is that not only requests itself imports those +dependencies, but third-party code outside of the distros' control too. + +In reaction to these problems, the distro maintainers replaced +``requests.packages`` with a magical "stub module" that imports the correct +modules. The implementations were varying in quality and all had severe +problems. For example, a symlink (or hardlink) that links the correct modules +into place introduces problems regarding object identity, since you now have +two modules in `sys.modules` with the same API, but different identities:: + + requests.packages.urllib3 is not urllib3 + +With version ``2.5.2``, requests started to maintain its own stub, so that +distro-specific breakage would be reduced to a minimum, even though the whole +issue is not requests' fault in the first place. See +https://github.com/kennethreitz/requests/pull/2375 for the corresponding pull +request. +''' + from __future__ import absolute_import +import sys + +try: + from . import urllib3 +except ImportError: + import urllib3 + sys.modules['%s.urllib3' % __name__] = urllib3 -from . import urllib3 +try: + from . import chardet +except ImportError: + import chardet + sys.modules['%s.chardet' % __name__] = chardet diff --git a/pip/_vendor/requests/packages/chardet/__init__.py b/pip/_vendor/requests/packages/chardet/__init__.py index e4f0799d621..82c2a48d290 100644 --- a/pip/_vendor/requests/packages/chardet/__init__.py +++ b/pip/_vendor/requests/packages/chardet/__init__.py @@ -15,7 +15,7 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### -__version__ = "2.2.1" +__version__ = "2.3.0" from sys import version_info diff --git a/pip/_vendor/requests/packages/chardet/chardetect.py b/pip/_vendor/requests/packages/chardet/chardetect.py index ecd0163be72..ffe892f25db 100644 --- a/pip/_vendor/requests/packages/chardet/chardetect.py +++ b/pip/_vendor/requests/packages/chardet/chardetect.py @@ -12,34 +12,68 @@ If no paths are provided, it takes its input from stdin. """ + +from __future__ import absolute_import, print_function, unicode_literals + +import argparse +import sys from io import open -from sys import argv, stdin +from chardet import __version__ from chardet.universaldetector import UniversalDetector -def description_of(file, name='stdin'): - """Return a string describing the probable encoding of a file.""" +def description_of(lines, name='stdin'): + """ + Return a string describing the probable encoding of a file or + list of strings. + + :param lines: The lines to get the encoding of. + :type lines: Iterable of bytes + :param name: Name of file or collection of lines + :type name: str + """ u = UniversalDetector() - for line in file: + for line in lines: u.feed(line) u.close() result = u.result if result['encoding']: - return '%s: %s with confidence %s' % (name, - result['encoding'], - result['confidence']) + return '{0}: {1} with confidence {2}'.format(name, result['encoding'], + result['confidence']) else: - return '%s: no result' % name + return '{0}: no result'.format(name) -def main(): - if len(argv) <= 1: - print(description_of(stdin)) - else: - for path in argv[1:]: - with open(path, 'rb') as f: - print(description_of(f, path)) +def main(argv=None): + ''' + Handles command line arguments and gets things started. + + :param argv: List of arguments, as if specified on the command-line. + If None, ``sys.argv[1:]`` is used instead. + :type argv: list of str + ''' + # Get command line arguments + parser = argparse.ArgumentParser( + description="Takes one or more file paths and reports their detected \ + encodings", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + conflict_handler='resolve') + parser.add_argument('input', + help='File whose encoding we would like to determine.', + type=argparse.FileType('rb'), nargs='*', + default=[sys.stdin]) + parser.add_argument('--version', action='version', + version='%(prog)s {0}'.format(__version__)) + args = parser.parse_args(argv) + + for f in args.input: + if f.isatty(): + print("You are running chardetect interactively. Press " + + "CTRL-D twice at the start of a blank line to signal the " + + "end of your input. If you want help, run chardetect " + + "--help\n", file=sys.stderr) + print(description_of(f, f.name)) if __name__ == '__main__': diff --git a/pip/_vendor/requests/packages/chardet/jpcntx.py b/pip/_vendor/requests/packages/chardet/jpcntx.py index f7f69ba4cda..59aeb6a8789 100644 --- a/pip/_vendor/requests/packages/chardet/jpcntx.py +++ b/pip/_vendor/requests/packages/chardet/jpcntx.py @@ -177,6 +177,12 @@ def get_order(self, aBuf): return -1, 1 class SJISContextAnalysis(JapaneseContextAnalysis): + def __init__(self): + self.charset_name = "SHIFT_JIS" + + def get_charset_name(self): + return self.charset_name + def get_order(self, aBuf): if not aBuf: return -1, 1 @@ -184,6 +190,8 @@ def get_order(self, aBuf): first_char = wrap_ord(aBuf[0]) if ((0x81 <= first_char <= 0x9F) or (0xE0 <= first_char <= 0xFC)): charLen = 2 + if (first_char == 0x87) or (0xFA <= first_char <= 0xFC): + self.charset_name = "CP932" else: charLen = 1 diff --git a/pip/_vendor/requests/packages/chardet/latin1prober.py b/pip/_vendor/requests/packages/chardet/latin1prober.py index ad695f57a72..eef3573543c 100644 --- a/pip/_vendor/requests/packages/chardet/latin1prober.py +++ b/pip/_vendor/requests/packages/chardet/latin1prober.py @@ -129,11 +129,11 @@ def get_confidence(self): if total < 0.01: confidence = 0.0 else: - confidence = ((self._mFreqCounter[3] / total) - - (self._mFreqCounter[1] * 20.0 / total)) + confidence = ((self._mFreqCounter[3] - self._mFreqCounter[1] * 20.0) + / total) if confidence < 0.0: confidence = 0.0 # lower the confidence of latin1 so that other more accurate # detector can take priority. - confidence = confidence * 0.5 + confidence = confidence * 0.73 return confidence diff --git a/pip/_vendor/requests/packages/chardet/mbcssm.py b/pip/_vendor/requests/packages/chardet/mbcssm.py index 3f93cfb045c..efe678ca039 100644 --- a/pip/_vendor/requests/packages/chardet/mbcssm.py +++ b/pip/_vendor/requests/packages/chardet/mbcssm.py @@ -353,7 +353,7 @@ 2,2,2,2,2,2,2,2, # 68 - 6f 2,2,2,2,2,2,2,2, # 70 - 77 2,2,2,2,2,2,2,1, # 78 - 7f - 3,3,3,3,3,3,3,3, # 80 - 87 + 3,3,3,3,3,2,2,3, # 80 - 87 3,3,3,3,3,3,3,3, # 88 - 8f 3,3,3,3,3,3,3,3, # 90 - 97 3,3,3,3,3,3,3,3, # 98 - 9f @@ -369,9 +369,8 @@ 2,2,2,2,2,2,2,2, # d8 - df 3,3,3,3,3,3,3,3, # e0 - e7 3,3,3,3,3,4,4,4, # e8 - ef - 4,4,4,4,4,4,4,4, # f0 - f7 - 4,4,4,4,4,0,0,0 # f8 - ff -) + 3,3,3,3,3,3,3,3, # f0 - f7 + 3,3,3,3,3,0,0,0) # f8 - ff SJIS_st = ( @@ -571,5 +570,3 @@ 'stateTable': UTF8_st, 'charLenTable': UTF8CharLenTable, 'name': 'UTF-8'} - -# flake8: noqa diff --git a/pip/_vendor/requests/packages/chardet/sjisprober.py b/pip/_vendor/requests/packages/chardet/sjisprober.py index b173614e682..cd0e9e7078b 100644 --- a/pip/_vendor/requests/packages/chardet/sjisprober.py +++ b/pip/_vendor/requests/packages/chardet/sjisprober.py @@ -47,7 +47,7 @@ def reset(self): self._mContextAnalyzer.reset() def get_charset_name(self): - return "SHIFT_JIS" + return self._mContextAnalyzer.get_charset_name() def feed(self, aBuf): aLen = len(aBuf) diff --git a/pip/_vendor/requests/packages/chardet/universaldetector.py b/pip/_vendor/requests/packages/chardet/universaldetector.py index 9a03ad3d89a..476522b9996 100644 --- a/pip/_vendor/requests/packages/chardet/universaldetector.py +++ b/pip/_vendor/requests/packages/chardet/universaldetector.py @@ -71,9 +71,9 @@ def feed(self, aBuf): if not self._mGotData: # If the data starts with BOM, we know it is UTF - if aBuf[:3] == codecs.BOM: + if aBuf[:3] == codecs.BOM_UTF8: # EF BB BF UTF-8 with BOM - self.result = {'encoding': "UTF-8", 'confidence': 1.0} + self.result = {'encoding': "UTF-8-SIG", 'confidence': 1.0} elif aBuf[:4] == codecs.BOM_UTF32_LE: # FF FE 00 00 UTF-32, little-endian BOM self.result = {'encoding': "UTF-32LE", 'confidence': 1.0} diff --git a/pip/_vendor/requests/packages/urllib3/__init__.py b/pip/_vendor/requests/packages/urllib3/__init__.py index 73071f7001b..73668991fd9 100644 --- a/pip/_vendor/requests/packages/urllib3/__init__.py +++ b/pip/_vendor/requests/packages/urllib3/__init__.py @@ -1,17 +1,9 @@ -# urllib3/__init__.py -# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt) -# -# This module is part of urllib3 and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - """ urllib3 - Thread-safe connection pooling and re-using. """ -__author__ = 'Andrey Petrov (andrey.petrov@shazow.net)' -__license__ = 'MIT' -__version__ = 'dev' - +from __future__ import absolute_import +import warnings from .connectionpool import ( HTTPConnectionPool, @@ -23,7 +15,10 @@ from .filepost import encode_multipart_formdata from .poolmanager import PoolManager, ProxyManager, proxy_from_url from .response import HTTPResponse -from .util import make_headers, get_host, Timeout +from .util.request import make_headers +from .util.url import get_host +from .util.timeout import Timeout +from .util.retry import Retry # Set default logging handler to avoid "No handler found" warnings. @@ -35,8 +30,30 @@ class NullHandler(logging.Handler): def emit(self, record): pass +__author__ = 'Andrey Petrov (andrey.petrov@shazow.net)' +__license__ = 'MIT' +__version__ = '1.15.1' + +__all__ = ( + 'HTTPConnectionPool', + 'HTTPSConnectionPool', + 'PoolManager', + 'ProxyManager', + 'HTTPResponse', + 'Retry', + 'Timeout', + 'add_stderr_logger', + 'connection_from_url', + 'disable_warnings', + 'encode_multipart_formdata', + 'get_host', + 'make_headers', + 'proxy_from_url', +) + logging.getLogger(__name__).addHandler(NullHandler()) + def add_stderr_logger(level=logging.DEBUG): """ Helper for quickly adding a StreamHandler to the logger. Useful for @@ -51,8 +68,29 @@ def add_stderr_logger(level=logging.DEBUG): handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s')) logger.addHandler(handler) logger.setLevel(level) - logger.debug('Added an stderr logging handler to logger: %s' % __name__) + logger.debug('Added a stderr logging handler to logger: %s', __name__) return handler # ... Clean up. del NullHandler + + +# All warning filters *must* be appended unless you're really certain that they +# shouldn't be: otherwise, it's very hard for users to use most Python +# mechanisms to silence them. +# SecurityWarning's always go off by default. +warnings.simplefilter('always', exceptions.SecurityWarning, append=True) +# SubjectAltNameWarning's should go off once per host +warnings.simplefilter('default', exceptions.SubjectAltNameWarning, append=True) +# InsecurePlatformWarning's don't vary between requests, so we keep it default. +warnings.simplefilter('default', exceptions.InsecurePlatformWarning, + append=True) +# SNIMissingWarnings should go off only once. +warnings.simplefilter('default', exceptions.SNIMissingWarning, append=True) + + +def disable_warnings(category=exceptions.HTTPWarning): + """ + Helper for quickly disabling all urllib3 warnings. + """ + warnings.simplefilter('ignore', category) diff --git a/pip/_vendor/requests/packages/urllib3/_collections.py b/pip/_vendor/requests/packages/urllib3/_collections.py index 9cea3a44c42..77cee017048 100644 --- a/pip/_vendor/requests/packages/urllib3/_collections.py +++ b/pip/_vendor/requests/packages/urllib3/_collections.py @@ -1,13 +1,8 @@ -# urllib3/_collections.py -# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt) -# -# This module is part of urllib3 and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - +from __future__ import absolute_import from collections import Mapping, MutableMapping try: from threading import RLock -except ImportError: # Platform-specific: No threads available +except ImportError: # Platform-specific: No threads available class RLock: def __enter__(self): pass @@ -16,11 +11,11 @@ def __exit__(self, exc_type, exc_value, traceback): pass -try: # Python 2.7+ +try: # Python 2.7+ from collections import OrderedDict except ImportError: from .packages.ordered_dict import OrderedDict -from .packages.six import itervalues +from .packages.six import iterkeys, itervalues, PY3 __all__ = ['RecentlyUsedContainer', 'HTTPHeaderDict'] @@ -91,8 +86,7 @@ def __iter__(self): def clear(self): with self.lock: # Copy pointers to all values, then wipe the mapping - # under Python 2, this copies the list of values twice :-| - values = list(self._container.values()) + values = list(itervalues(self._container)) self._container.clear() if self.dispose_func: @@ -101,7 +95,7 @@ def clear(self): def keys(self): with self.lock: - return self._container.keys() + return list(iterkeys(self._container)) class HTTPHeaderDict(MutableMapping): @@ -116,7 +110,7 @@ class HTTPHeaderDict(MutableMapping): A ``dict`` like container for storing HTTP Headers. Field names are stored and compared case-insensitively in compliance with - RFC 2616. Iteration provides the first case-sensitive key seen for each + RFC 7230. Iteration provides the first case-sensitive key seen for each case-insensitive pair. Using ``__setitem__`` syntax overwrites fields that compare equal @@ -136,25 +130,82 @@ class HTTPHeaderDict(MutableMapping): 'foo=bar, baz=quxx' >>> headers['Content-Length'] '7' - - If you want to access the raw headers with their original casing - for debugging purposes you can access the private ``._data`` attribute - which is a normal python ``dict`` that maps the case-insensitive key to a - list of tuples stored as (case-sensitive-original-name, value). Using the - structure from above as our example: - - >>> headers._data - {'set-cookie': [('Set-Cookie', 'foo=bar'), ('set-cookie', 'baz=quxx')], - 'content-length': [('content-length', '7')]} """ def __init__(self, headers=None, **kwargs): - self._data = {} - if headers is None: - headers = {} - self.update(headers, **kwargs) + super(HTTPHeaderDict, self).__init__() + self._container = OrderedDict() + if headers is not None: + if isinstance(headers, HTTPHeaderDict): + self._copy_from(headers) + else: + self.extend(headers) + if kwargs: + self.extend(kwargs) + + def __setitem__(self, key, val): + self._container[key.lower()] = (key, val) + return self._container[key.lower()] + + def __getitem__(self, key): + val = self._container[key.lower()] + return ', '.join(val[1:]) + + def __delitem__(self, key): + del self._container[key.lower()] + + def __contains__(self, key): + return key.lower() in self._container + + def __eq__(self, other): + if not isinstance(other, Mapping) and not hasattr(other, 'keys'): + return False + if not isinstance(other, type(self)): + other = type(self)(other) + return (dict((k.lower(), v) for k, v in self.itermerged()) == + dict((k.lower(), v) for k, v in other.itermerged())) + + def __ne__(self, other): + return not self.__eq__(other) + + if not PY3: # Python 2 + iterkeys = MutableMapping.iterkeys + itervalues = MutableMapping.itervalues + + __marker = object() + + def __len__(self): + return len(self._container) + + def __iter__(self): + # Only provide the originally cased names + for vals in self._container.values(): + yield vals[0] + + def pop(self, key, default=__marker): + '''D.pop(k[,d]) -> v, remove specified key and return the corresponding value. + If key is not found, d is returned if given, otherwise KeyError is raised. + ''' + # Using the MutableMapping function directly fails due to the private marker. + # Using ordinary dict.pop would expose the internal structures. + # So let's reinvent the wheel. + try: + value = self[key] + except KeyError: + if default is self.__marker: + raise + return default + else: + del self[key] + return value + + def discard(self, key): + try: + del self[key] + except KeyError: + pass - def add(self, key, value): + def add(self, key, val): """Adds a (name, value) pair, doesn't overwrite the value if it already exists. @@ -163,43 +214,111 @@ def add(self, key, value): >>> headers['foo'] 'bar, baz' """ - self._data.setdefault(key.lower(), []).append((key, value)) + key_lower = key.lower() + new_vals = key, val + # Keep the common case aka no item present as fast as possible + vals = self._container.setdefault(key_lower, new_vals) + if new_vals is not vals: + # new_vals was not inserted, as there was a previous one + if isinstance(vals, list): + # If already several items got inserted, we have a list + vals.append(val) + else: + # vals should be a tuple then, i.e. only one item so far + # Need to convert the tuple to list for further extension + self._container[key_lower] = [vals[0], vals[1], val] + + def extend(self, *args, **kwargs): + """Generic import function for any type of header-like object. + Adapted version of MutableMapping.update in order to insert items + with self.add instead of self.__setitem__ + """ + if len(args) > 1: + raise TypeError("extend() takes at most 1 positional " + "arguments ({0} given)".format(len(args))) + other = args[0] if len(args) >= 1 else () + + if isinstance(other, HTTPHeaderDict): + for key, val in other.iteritems(): + self.add(key, val) + elif isinstance(other, Mapping): + for key in other: + self.add(key, other[key]) + elif hasattr(other, "keys"): + for key in other.keys(): + self.add(key, other[key]) + else: + for key, value in other: + self.add(key, value) + + for key, value in kwargs.items(): + self.add(key, value) def getlist(self, key): """Returns a list of all the values for the named field. Returns an empty list if the key doesn't exist.""" - return self[key].split(', ') if key in self else [] - - def copy(self): - h = HTTPHeaderDict() - for key in self._data: - for rawkey, value in self._data[key]: - h.add(rawkey, value) - return h - - def __eq__(self, other): - if not isinstance(other, Mapping): - return False - other = HTTPHeaderDict(other) - return dict((k1, self[k1]) for k1 in self._data) == \ - dict((k2, other[k2]) for k2 in other._data) - - def __getitem__(self, key): - values = self._data[key.lower()] - return ', '.join(value[1] for value in values) + try: + vals = self._container[key.lower()] + except KeyError: + return [] + else: + if isinstance(vals, tuple): + return [vals[1]] + else: + return vals[1:] + + # Backwards compatibility for httplib + getheaders = getlist + getallmatchingheaders = getlist + iget = getlist - def __setitem__(self, key, value): - self._data[key.lower()] = [(key, value)] - - def __delitem__(self, key): - del self._data[key.lower()] - - def __len__(self): - return len(self._data) + def __repr__(self): + return "%s(%s)" % (type(self).__name__, dict(self.itermerged())) - def __iter__(self): - for headers in itervalues(self._data): - yield headers[0][0] + def _copy_from(self, other): + for key in other: + val = other.getlist(key) + if isinstance(val, list): + # Don't need to convert tuples + val = list(val) + self._container[key.lower()] = [key] + val - def __repr__(self): - return '%s(%r)' % (self.__class__.__name__, dict(self.items())) + def copy(self): + clone = type(self)() + clone._copy_from(self) + return clone + + def iteritems(self): + """Iterate over all header lines, including duplicate ones.""" + for key in self: + vals = self._container[key.lower()] + for val in vals[1:]: + yield vals[0], val + + def itermerged(self): + """Iterate over all headers, merging duplicate ones together.""" + for key in self: + val = self._container[key.lower()] + yield val[0], ', '.join(val[1:]) + + def items(self): + return list(self.iteritems()) + + @classmethod + def from_httplib(cls, message): # Python 2 + """Read headers from a Python 2 httplib message object.""" + # python2.7 does not expose a proper API for exporting multiheaders + # efficiently. This function re-reads raw lines from the message + # object and extracts the multiheaders properly. + headers = [] + + for line in message.headers: + if line.startswith((' ', '\t')): + key, value = headers[-1] + headers[-1] = (key, value + '\r\n' + line.rstrip()) + continue + + key, value = line.split(':', 1) + headers.append((key, value.strip())) + + return cls(headers) diff --git a/pip/_vendor/requests/packages/urllib3/connection.py b/pip/_vendor/requests/packages/urllib3/connection.py index 5feb3322ee6..5ce0080480a 100644 --- a/pip/_vendor/requests/packages/urllib3/connection.py +++ b/pip/_vendor/requests/packages/urllib3/connection.py @@ -1,95 +1,154 @@ -# urllib3/connection.py -# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt) -# -# This module is part of urllib3 and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - +from __future__ import absolute_import +import datetime +import logging +import os import sys import socket -from socket import timeout as SocketTimeout +from socket import error as SocketError, timeout as SocketTimeout +import warnings +from .packages import six -try: # Python 3 - from http.client import HTTPConnection as _HTTPConnection, HTTPException +try: # Python 3 + from http.client import HTTPConnection as _HTTPConnection + from http.client import HTTPException # noqa: unused in this module except ImportError: - from httplib import HTTPConnection as _HTTPConnection, HTTPException + from httplib import HTTPConnection as _HTTPConnection + from httplib import HTTPException # noqa: unused in this module -class DummyConnection(object): - "Used to detect a failed ConnectionCls import." - pass - -try: # Compiled with SSL? +try: # Compiled with SSL? + import ssl + BaseSSLError = ssl.SSLError +except (ImportError, AttributeError): # Platform-specific: No SSL. ssl = None - HTTPSConnection = DummyConnection class BaseSSLError(BaseException): pass - try: # Python 3 - from http.client import HTTPSConnection as _HTTPSConnection - except ImportError: - from httplib import HTTPSConnection as _HTTPSConnection - import ssl - BaseSSLError = ssl.SSLError +try: # Python 3: + # Not a no-op, we're adding this to the namespace so it can be imported. + ConnectionError = ConnectionError +except NameError: # Python 2: + class ConnectionError(Exception): + pass -except (ImportError, AttributeError): # Platform-specific: No SSL. - pass from .exceptions import ( + NewConnectionError, ConnectTimeoutError, + SubjectAltNameWarning, + SystemTimeWarning, ) -from .packages.ssl_match_hostname import match_hostname -from .packages import six -from .util import ( - assert_fingerprint, +from .packages.ssl_match_hostname import match_hostname, CertificateError + +from .util.ssl_ import ( resolve_cert_reqs, resolve_ssl_version, ssl_wrap_socket, + assert_fingerprint, ) +from .util import connection + +from ._collections import HTTPHeaderDict + +log = logging.getLogger(__name__) + port_by_scheme = { 'http': 80, 'https': 443, } +RECENT_DATE = datetime.date(2014, 1, 1) + + +class DummyConnection(object): + """Used to detect a failed ConnectionCls import.""" + pass + class HTTPConnection(_HTTPConnection, object): """ Based on httplib.HTTPConnection but provides an extra constructor backwards-compatibility layer between older and newer Pythons. + + Additional keyword parameters are used to configure attributes of the connection. + Accepted parameters include: + + - ``strict``: See the documentation on :class:`urllib3.connectionpool.HTTPConnectionPool` + - ``source_address``: Set the source address for the current connection. + + .. note:: This is ignored for Python 2.6. It is only applied for 2.7 and 3.x + + - ``socket_options``: Set specific options on the underlying socket. If not specified, then + defaults are loaded from ``HTTPConnection.default_socket_options`` which includes disabling + Nagle's algorithm (sets TCP_NODELAY to 1) unless the connection is behind a proxy. + + For example, if you wish to enable TCP Keep Alive in addition to the defaults, + you might pass:: + + HTTPConnection.default_socket_options + [ + (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1), + ] + + Or you may want to disable the defaults by passing an empty list (e.g., ``[]``). """ default_port = port_by_scheme['http'] - # By default, disable Nagle's Algorithm. - tcp_nodelay = 1 + #: Disable Nagle's algorithm by default. + #: ``[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]`` + default_socket_options = [(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)] + + #: Whether this connection verifies the host's certificate. + is_verified = False def __init__(self, *args, **kw): if six.PY3: # Python 3 kw.pop('strict', None) - if sys.version_info < (2, 7): # Python 2.6 and older - kw.pop('source_address', None) # Pre-set source_address in case we have an older Python like 2.6. self.source_address = kw.get('source_address') + if sys.version_info < (2, 7): # Python 2.6 + # _HTTPConnection on Python 2.6 will balk at this keyword arg, but + # not newer versions. We can still use it when creating a + # connection though, so we pop it *after* we have saved it as + # self.source_address. + kw.pop('source_address', None) + + #: The socket options provided by the user. If no options are + #: provided, we use the default options. + self.socket_options = kw.pop('socket_options', self.default_socket_options) + # Superclass also sets self.source_address in Python 2.7+. - _HTTPConnection.__init__(self, *args, **kw) + _HTTPConnection.__init__(self, *args, **kw) def _new_conn(self): """ Establish a socket connection and set nodelay settings on it. - :return: a new socket connection + :return: New socket connection. """ - extra_args = [] - if self.source_address: # Python 2.7+ - extra_args.append(self.source_address) + extra_kw = {} + if self.source_address: + extra_kw['source_address'] = self.source_address + + if self.socket_options: + extra_kw['socket_options'] = self.socket_options + + try: + conn = connection.create_connection( + (self.host, self.port), self.timeout, **extra_kw) + + except SocketTimeout as e: + raise ConnectTimeoutError( + self, "Connection to %s timed out. (connect timeout=%s)" % + (self.host, self.timeout)) - conn = socket.create_connection( - (self.host, self.port), self.timeout, *extra_args) - conn.setsockopt( - socket.IPPROTO_TCP, socket.TCP_NODELAY, self.tcp_nodelay) + except SocketError as e: + raise NewConnectionError( + self, "Failed to establish a new connection: %s" % e) return conn @@ -101,11 +160,45 @@ def _prepare_conn(self, conn): if getattr(self, '_tunnel_host', None): # TODO: Fix tunnel so it doesn't depend on self.sock state. self._tunnel() + # Mark this connection as not reusable + self.auto_open = 0 def connect(self): conn = self._new_conn() self._prepare_conn(conn) + def request_chunked(self, method, url, body=None, headers=None): + """ + Alternative to the common request method, which sends the + body with chunked encoding and not as one block + """ + headers = HTTPHeaderDict(headers if headers is not None else {}) + skip_accept_encoding = 'accept-encoding' in headers + self.putrequest(method, url, skip_accept_encoding=skip_accept_encoding) + for header, value in headers.items(): + self.putheader(header, value) + if 'transfer-encoding' not in headers: + self.putheader('Transfer-Encoding', 'chunked') + self.endheaders() + + if body is not None: + stringish_types = six.string_types + (six.binary_type,) + if isinstance(body, stringish_types): + body = (body,) + for chunk in body: + if not chunk: + continue + if not isinstance(chunk, six.binary_type): + chunk = chunk.encode('utf8') + len_str = hex(len(chunk))[2:] + self.send(len_str.encode('utf-8')) + self.send(b'\r\n') + self.send(chunk) + self.send(b'\r\n') + + # After the if clause, to always have a closed body + self.send(b'0\r\n\r\n') + class HTTPSConnection(HTTPConnection): default_port = port_by_scheme['https'] @@ -136,34 +229,29 @@ class VerifiedHTTPSConnection(HTTPSConnection): """ cert_reqs = None ca_certs = None + ca_cert_dir = None ssl_version = None - conn_kw = {} + assert_fingerprint = None def set_cert(self, key_file=None, cert_file=None, cert_reqs=None, ca_certs=None, - assert_hostname=None, assert_fingerprint=None): + assert_hostname=None, assert_fingerprint=None, + ca_cert_dir=None): + + if (ca_certs or ca_cert_dir) and cert_reqs is None: + cert_reqs = 'CERT_REQUIRED' self.key_file = key_file self.cert_file = cert_file self.cert_reqs = cert_reqs - self.ca_certs = ca_certs self.assert_hostname = assert_hostname self.assert_fingerprint = assert_fingerprint + self.ca_certs = ca_certs and os.path.expanduser(ca_certs) + self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir) def connect(self): # Add certificate verification - - try: - sock = socket.create_connection( - address=(self.host, self.port), timeout=self.timeout, - **self.conn_kw) - except SocketTimeout: - raise ConnectTimeoutError( - self, "Connection to %s timed out. (connect timeout=%s)" % - (self.host, self.timeout)) - - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, - self.tcp_nodelay) + conn = self._new_conn() resolved_cert_reqs = resolve_cert_reqs(self.cert_reqs) resolved_ssl_version = resolve_ssl_version(self.ssl_version) @@ -173,32 +261,70 @@ def connect(self): # _tunnel_host was added in Python 2.6.3 # (See: http://hg.python.org/cpython/rev/0f57b30a152f) - self.sock = sock + self.sock = conn # Calls self._set_hostport(), so self.host is # self._tunnel_host below. self._tunnel() + # Mark this connection as not reusable + self.auto_open = 0 # Override the host with the one we're requesting data from. hostname = self._tunnel_host + is_time_off = datetime.date.today() < RECENT_DATE + if is_time_off: + warnings.warn(( + 'System time is way off (before {0}). This will probably ' + 'lead to SSL verification errors').format(RECENT_DATE), + SystemTimeWarning + ) + # Wrap socket using verification with the root certs in # trusted_root_certs - self.sock = ssl_wrap_socket(sock, self.key_file, self.cert_file, + self.sock = ssl_wrap_socket(conn, self.key_file, self.cert_file, cert_reqs=resolved_cert_reqs, ca_certs=self.ca_certs, + ca_cert_dir=self.ca_cert_dir, server_hostname=hostname, ssl_version=resolved_ssl_version) - if resolved_cert_reqs != ssl.CERT_NONE: - if self.assert_fingerprint: - assert_fingerprint(self.sock.getpeercert(binary_form=True), - self.assert_fingerprint) - elif self.assert_hostname is not False: - match_hostname(self.sock.getpeercert(), - self.assert_hostname or hostname) + if self.assert_fingerprint: + assert_fingerprint(self.sock.getpeercert(binary_form=True), + self.assert_fingerprint) + elif resolved_cert_reqs != ssl.CERT_NONE \ + and self.assert_hostname is not False: + cert = self.sock.getpeercert() + if not cert.get('subjectAltName', ()): + warnings.warn(( + 'Certificate for {0} has no `subjectAltName`, falling back to check for a ' + '`commonName` for now. This feature is being removed by major browsers and ' + 'deprecated by RFC 2818. (See https://github.com/shazow/urllib3/issues/497 ' + 'for details.)'.format(hostname)), + SubjectAltNameWarning + ) + _match_hostname(cert, self.assert_hostname or hostname) + + self.is_verified = (resolved_cert_reqs == ssl.CERT_REQUIRED or + self.assert_fingerprint is not None) + + +def _match_hostname(cert, asserted_hostname): + try: + match_hostname(cert, asserted_hostname) + except CertificateError as e: + log.error( + 'Certificate did not match expected hostname: %s. ' + 'Certificate: %s', asserted_hostname, cert + ) + # Add cert to exception and reraise so client code can inspect + # the cert when catching the exception, if they want to + e._peer_cert = cert + raise if ssl: # Make a copy for testing. UnverifiedHTTPSConnection = HTTPSConnection HTTPSConnection = VerifiedHTTPSConnection +else: + HTTPSConnection = DummyConnection diff --git a/pip/_vendor/requests/packages/urllib3/connectionpool.py b/pip/_vendor/requests/packages/urllib3/connectionpool.py index 95a53a7df6c..3fcfb12012b 100644 --- a/pip/_vendor/requests/packages/urllib3/connectionpool.py +++ b/pip/_vendor/requests/packages/urllib3/connectionpool.py @@ -1,35 +1,34 @@ -# urllib3/connectionpool.py -# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt) -# -# This module is part of urllib3 and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - -import sys +from __future__ import absolute_import import errno import logging +import sys +import warnings from socket import error as SocketError, timeout as SocketTimeout import socket -try: # Python 3 +try: # Python 3 from queue import LifoQueue, Empty, Full except ImportError: from Queue import LifoQueue, Empty, Full - import Queue as _ # Platform-specific: Windows + # Queue is imported for side effects on MS Windows + import Queue as _unused_module_Queue # noqa: unused from .exceptions import ( ClosedPoolError, - ConnectionError, - ConnectTimeoutError, + ProtocolError, EmptyPoolError, + HeaderParsingError, HostChangedError, - LocationParseError, + LocationValueError, MaxRetryError, + ProxyError, + ReadTimeoutError, SSLError, TimeoutError, - ReadTimeoutError, - ProxyError, + InsecureRequestWarning, + NewConnectionError, ) from .packages.ssl_match_hostname import CertificateError from .packages import six @@ -41,11 +40,12 @@ ) from .request import RequestMethods from .response import HTTPResponse -from .util import ( - get_host, - is_connection_dropped, - Timeout, -) + +from .util.connection import is_connection_dropped +from .util.response import assert_header_parsing +from .util.retry import Retry +from .util.timeout import Timeout +from .util.url import get_host, Url xrange = six.moves.xrange @@ -54,8 +54,8 @@ _Default = object() -## Pool objects +# Pool objects class ConnectionPool(object): """ Base class for all connection pools, such as @@ -66,22 +66,41 @@ class ConnectionPool(object): QueueCls = LifoQueue def __init__(self, host, port=None): - if host is None: - raise LocationParseError(host) + if not host: + raise LocationValueError("No host specified.") # httplib doesn't like it when we include brackets in ipv6 addresses - host = host.strip('[]') - - self.host = host + # Specifically, if we include brackets but also pass the port then + # httplib crazily doubles up the square brackets on the Host header. + # Instead, we need to make sure we never pass ``None`` as the port. + # However, for backward compatibility reasons we can't actually + # *assert* that. + self.host = host.strip('[]') self.port = port def __str__(self): return '%s(host=%r, port=%r)' % (type(self).__name__, self.host, self.port) + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + # Return False to re-raise any potential exceptions + return False + + def close(): + """ + Close all pooled connections and disable the pool. + """ + pass + + # This is taken from http://hg.python.org/cpython/file/7aaba721ebc0/Lib/socket.py#l252 _blocking_errnos = set([errno.EAGAIN, errno.EWOULDBLOCK]) + class HTTPConnectionPool(ConnectionPool, RequestMethods): """ Thread-safe connection pool for one host. @@ -111,7 +130,7 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): :param maxsize: Number of connections to save that can be reused. More than 1 is useful - in multithreaded situations. If ``block`` is set to false, more + in multithreaded situations. If ``block`` is set to False, more connections will be created but they will not be saved once they've been used. @@ -126,6 +145,9 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): Headers to include with all requests, unless other headers are given explicitly. + :param retries: + Retry configuration to use by default with requests in this pool. + :param _proxy: Parsed proxy URL, should not be used directly, instead, see :class:`urllib3.connectionpool.ProxyManager`" @@ -133,6 +155,10 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): :param _proxy_headers: A dictionary with proxy headers, should not be used directly, instead, see :class:`urllib3.connectionpool.ProxyManager`" + + :param \**conn_kw: + Additional parameters are used to create fresh :class:`urllib3.connection.HTTPConnection`, + :class:`urllib3.connection.HTTPSConnection` instances. """ scheme = 'http' @@ -140,18 +166,22 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): def __init__(self, host, port=None, strict=False, timeout=Timeout.DEFAULT_TIMEOUT, maxsize=1, block=False, - headers=None, _proxy=None, _proxy_headers=None, **conn_kw): + headers=None, retries=None, + _proxy=None, _proxy_headers=None, + **conn_kw): ConnectionPool.__init__(self, host, port) RequestMethods.__init__(self, headers) self.strict = strict - # This is for backwards compatibility and can be removed once a timeout - # can only be set to a Timeout object if not isinstance(timeout, Timeout): timeout = Timeout.from_float(timeout) + if retries is None: + retries = Retry.DEFAULT + self.timeout = timeout + self.retries = retries self.pool = self.QueueCls(maxsize) self.block = block @@ -166,26 +196,25 @@ def __init__(self, host, port=None, strict=False, # These are mostly for testing and debugging purposes. self.num_connections = 0 self.num_requests = 0 - - if sys.version_info < (2, 7): # Python 2.6 and older - conn_kw.pop('source_address', None) self.conn_kw = conn_kw + if self.proxy: + # Enable Nagle's algorithm for proxies, to avoid packet fragmentation. + # We cannot know if the user has added default socket options, so we cannot replace the + # list. + self.conn_kw.setdefault('socket_options', []) + def _new_conn(self): """ Return a fresh :class:`HTTPConnection`. """ self.num_connections += 1 - log.info("Starting new HTTP connection (%d): %s" % - (self.num_connections, self.host)) + log.info("Starting new HTTP connection (%d): %s", + self.num_connections, self.host) conn = self.ConnectionCls(host=self.host, port=self.port, timeout=self.timeout.connect_timeout, strict=self.strict, **self.conn_kw) - if self.proxy is not None: - # Enable Nagle's algorithm for proxies, to avoid packet - # fragmentation. - conn.tcp_nodelay = 0 return conn def _get_conn(self, timeout=None): @@ -204,7 +233,7 @@ def _get_conn(self, timeout=None): try: conn = self.pool.get(block=self.block, timeout=timeout) - except AttributeError: # self.pool is None + except AttributeError: # self.pool is None raise ClosedPoolError(self, "Pool is closed.") except Empty: @@ -216,8 +245,13 @@ def _get_conn(self, timeout=None): # If this is a persistent connection, check if it got disconnected if conn and is_connection_dropped(conn): - log.info("Resetting dropped connection: %s" % self.host) + log.info("Resetting dropped connection: %s", self.host) conn.close() + if getattr(conn, 'auto_open', 1) == 0: + # This is a proxied connection that has been mutated by + # httplib._tunnel() and cannot be reused (since it would + # attempt to bypass the proxy) + conn = None return conn or self._new_conn() @@ -237,20 +271,30 @@ def _put_conn(self, conn): """ try: self.pool.put(conn, block=False) - return # Everything is dandy, done. + return # Everything is dandy, done. except AttributeError: # self.pool is None. pass except Full: # This should never happen if self.block == True log.warning( - "Connection pool is full, discarding connection: %s" % + "Connection pool is full, discarding connection: %s", self.host) # Connection never got put back into the pool, close it. if conn: conn.close() + def _validate_conn(self, conn): + """ + Called right before a request is made, after the socket is created. + """ + pass + + def _prepare_proxy(self, conn): + # Nothing to do for HTTP connections. + pass + def _get_timeout(self, timeout): """ Helper that always returns a :class:`urllib3.util.Timeout` """ if timeout is _Default: @@ -263,7 +307,24 @@ def _get_timeout(self, timeout): # can be removed later return Timeout.from_float(timeout) - def _make_request(self, conn, method, url, timeout=_Default, + def _raise_timeout(self, err, url, timeout_value): + """Is the error actually a timeout? Will raise a ReadTimeout or pass""" + + if isinstance(err, SocketTimeout): + raise ReadTimeoutError(self, url, "Read timed out. (read timeout=%s)" % timeout_value) + + # See the above comment about EAGAIN in Python 3. In Python 2 we have + # to specifically catch it and throw the timeout error + if hasattr(err, 'errno') and err.errno in _blocking_errnos: + raise ReadTimeoutError(self, url, "Read timed out. (read timeout=%s)" % timeout_value) + + # Catch possible read timeouts thrown as SSL errors. If not the + # case, rethrow the original. We need to do this because of: + # http://bugs.python.org/issue10272 + if 'timed out' in str(err) or 'did not complete (read)' in str(err): # Python 2.6 + raise ReadTimeoutError(self, url, "Read timed out. (read timeout=%s)" % timeout_value) + + def _make_request(self, conn, method, url, timeout=_Default, chunked=False, **httplib_request_kw): """ Perform a request on a given urllib connection object taken from our @@ -282,23 +343,29 @@ def _make_request(self, conn, method, url, timeout=_Default, self.num_requests += 1 timeout_obj = self._get_timeout(timeout) + timeout_obj.start_connect() + conn.timeout = timeout_obj.connect_timeout + # Trigger any extra validation we need to do. try: - timeout_obj.start_connect() - conn.timeout = timeout_obj.connect_timeout - # conn.request() calls httplib.*.request, not the method in - # urllib3.request. It also calls makefile (recv) on the socket. + self._validate_conn(conn) + except (SocketTimeout, BaseSSLError) as e: + # Py2 raises this as a BaseSSLError, Py3 raises it as socket timeout. + self._raise_timeout(err=e, url=url, timeout_value=conn.timeout) + raise + + # conn.request() calls httplib.*.request, not the method in + # urllib3.request. It also calls makefile (recv) on the socket. + if chunked: + conn.request_chunked(method, url, **httplib_request_kw) + else: conn.request(method, url, **httplib_request_kw) - except SocketTimeout: - raise ConnectTimeoutError( - self, "Connection to %s timed out. (connect timeout=%s)" % - (self.host, timeout_obj.connect_timeout)) # Reset the timeout for the recv() on the socket read_timeout = timeout_obj.read_timeout # App Engine doesn't have a sock attr - if hasattr(conn, 'sock'): + if getattr(conn, 'sock', None): # In Python 3 socket.py will catch EAGAIN and return None when you # try and read into the file pointer created by http.client, which # instead raises a BadStatusLine exception. Instead of catching @@ -306,50 +373,39 @@ def _make_request(self, conn, method, url, timeout=_Default, # timeouts, check for a zero timeout before making the request. if read_timeout == 0: raise ReadTimeoutError( - self, url, - "Read timed out. (read timeout=%s)" % read_timeout) + self, url, "Read timed out. (read timeout=%s)" % read_timeout) if read_timeout is Timeout.DEFAULT_TIMEOUT: conn.sock.settimeout(socket.getdefaulttimeout()) - else: # None or a value + else: # None or a value conn.sock.settimeout(read_timeout) # Receive the response from the server try: - try: # Python 2.7+, use buffering of HTTP responses + try: # Python 2.7, use buffering of HTTP responses httplib_response = conn.getresponse(buffering=True) - except TypeError: # Python 2.6 and older + except TypeError: # Python 2.6 and older httplib_response = conn.getresponse() - except SocketTimeout: - raise ReadTimeoutError( - self, url, "Read timed out. (read timeout=%s)" % read_timeout) - - except BaseSSLError as e: - # Catch possible read timeouts thrown as SSL errors. If not the - # case, rethrow the original. We need to do this because of: - # http://bugs.python.org/issue10272 - if 'timed out' in str(e) or \ - 'did not complete (read)' in str(e): # Python 2.6 - raise ReadTimeoutError(self, url, "Read timed out.") - - raise - - except SocketError as e: # Platform-specific: Python 2 - # See the above comment about EAGAIN in Python 3. In Python 2 we - # have to specifically catch it and throw the timeout error - if e.errno in _blocking_errnos: - raise ReadTimeoutError( - self, url, - "Read timed out. (read timeout=%s)" % read_timeout) - + except (SocketTimeout, BaseSSLError, SocketError) as e: + self._raise_timeout(err=e, url=url, timeout_value=read_timeout) raise # AppEngine doesn't have a version attr. http_version = getattr(conn, '_http_vsn_str', 'HTTP/?') - log.debug("\"%s %s %s\" %s %s" % (method, url, http_version, - httplib_response.status, - httplib_response.length)) + log.debug("\"%s %s %s\" %s %s", method, url, http_version, + httplib_response.status, httplib_response.length) + + try: + assert_header_parsing(httplib_response.msg) + except HeaderParsingError as hpe: # Platform-specific: Python 3 + log.warning( + 'Failed to parse headers (url=%s): %s', + self._absolute_url(url), hpe, exc_info=True) + return httplib_response + def _absolute_url(self, path): + return Url(scheme=self.scheme, host=self.host, port=self.port, path=path).url + def close(self): """ Close all pooled connections and disable the pool. @@ -364,7 +420,7 @@ def close(self): conn.close() except Empty: - pass # Done. + pass # Done. def is_same_host(self, url): """ @@ -385,9 +441,10 @@ def is_same_host(self, url): return (scheme, host, port) == (self.scheme, self.host, self.port) - def urlopen(self, method, url, body=None, headers=None, retries=3, + def urlopen(self, method, url, body=None, headers=None, retries=None, redirect=True, assert_same_host=True, timeout=_Default, - pool_timeout=None, release_conn=None, **response_kw): + pool_timeout=None, release_conn=None, chunked=False, + **response_kw): """ Get a connection from the pool and perform an HTTP request. This is the lowest level call for making a request, so you'll need to specify all @@ -419,9 +476,20 @@ def urlopen(self, method, url, body=None, headers=None, retries=3, these headers completely replace any pool-specific headers. :param retries: - Number of retries to allow before raising a MaxRetryError exception. - If `False`, then retries are disabled and any exception is raised - immediately. + Configure the number of retries to allow before raising a + :class:`~urllib3.exceptions.MaxRetryError` exception. + + Pass ``None`` to retry until you receive a response. Pass a + :class:`~urllib3.util.retry.Retry` object for fine-grained control + over different types of retries. + Pass an integer number to retry connection errors that many times, + but no other types of errors. Pass zero to never retry. + + If ``False``, then retries are disabled and any exception is raised + immediately. Also, instead of raising a MaxRetryError on redirects, + the redirect response will be returned. + + :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. :param redirect: If True, automatically handle redirects (status codes 301, 302, @@ -453,6 +521,11 @@ def urlopen(self, method, url, body=None, headers=None, retries=3, back into the pool. If None, it takes the value of ``response_kw.get('preload_content', True)``. + :param chunked: + If True, urllib3 will send the body using chunked transfer + encoding. Otherwise, urllib3 will send the body using the standard + content-length form. Defaults to False. + :param \**response_kw: Additional parameters are passed to :meth:`urllib3.response.HTTPResponse.from_httplib` @@ -460,15 +533,15 @@ def urlopen(self, method, url, body=None, headers=None, retries=3, if headers is None: headers = self.headers - if retries < 0 and retries is not False: - raise MaxRetryError(self, url) + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect, default=self.retries) if release_conn is None: release_conn = response_kw.get('preload_content', True) # Check host if assert_same_host and not self.is_same_host(url): - raise HostChangedError(self, url, retries - 1) + raise HostChangedError(self, url, retries) conn = None @@ -483,20 +556,32 @@ def urlopen(self, method, url, body=None, headers=None, retries=3, # complains about UnboundLocalError. err = None + # Keep track of whether we cleanly exited the except block. This + # ensures we do proper cleanup in finally. + clean_exit = False + try: - # Request a connection from the queue + # Request a connection from the queue. + timeout_obj = self._get_timeout(timeout) conn = self._get_conn(timeout=pool_timeout) - # Make the request on the httplib connection object + conn.timeout = timeout_obj.connect_timeout + + is_new_proxy_conn = self.proxy is not None and not getattr(conn, 'sock', None) + if is_new_proxy_conn: + self._prepare_proxy(conn) + + # Make the request on the httplib connection object. httplib_response = self._make_request(conn, method, url, - timeout=timeout, - body=body, headers=headers) + timeout=timeout_obj, + body=body, headers=headers, + chunked=chunked) # If we're going to release the connection in ``finally:``, then - # the request doesn't need to know about the connection. Otherwise + # the response doesn't need to know about the connection. Otherwise # it will also try to release it and we'll have a double-release # mess. - response_conn = not release_conn and conn + response_conn = conn if not release_conn else None # Import httplib's response into our own wrapper object response = HTTPResponse.from_httplib(httplib_response, @@ -504,48 +589,52 @@ def urlopen(self, method, url, body=None, headers=None, retries=3, connection=response_conn, **response_kw) - # else: - # The connection will be put back into the pool when - # ``response.release_conn()`` is called (implicitly by - # ``response.read()``) + # Everything went great! + clean_exit = True except Empty: # Timed out by queue. raise EmptyPoolError(self, "No pool connections are available.") except (BaseSSLError, CertificateError) as e: - # Release connection unconditionally because there is no way to - # close it externally in case of exception. - release_conn = True + # Close the connection. If a connection is reused on which there + # was a Certificate error, the next request will certainly raise + # another Certificate error. + clean_exit = False raise SSLError(e) - except (TimeoutError, HTTPException, SocketError) as e: - if conn: - # Discard the connection for these exceptions. It will be - # be replaced during the next _get_conn() call. - conn.close() - conn = None - - if not retries: - if isinstance(e, TimeoutError): - # TimeoutError is exempt from MaxRetryError-wrapping. - # FIXME: ... Not sure why. Add a reason here. - raise + except SSLError: + # Treat SSLError separately from BaseSSLError to preserve + # traceback. + clean_exit = False + raise - # Wrap unexpected exceptions with the most appropriate - # module-level exception and re-raise. - if isinstance(e, SocketError) and self.proxy: - raise ProxyError('Cannot connect to proxy.', e) + except (TimeoutError, HTTPException, SocketError, ProtocolError) as e: + # Discard the connection for these exceptions. It will be + # be replaced during the next _get_conn() call. + clean_exit = False - if retries is False: - raise ConnectionError('Connection failed.', e) + if isinstance(e, (SocketError, NewConnectionError)) and self.proxy: + e = ProxyError('Cannot connect to proxy.', e) + elif isinstance(e, (SocketError, HTTPException)): + e = ProtocolError('Connection aborted.', e) - raise MaxRetryError(self, url, e) + retries = retries.increment(method, url, error=e, _pool=self, + _stacktrace=sys.exc_info()[2]) + retries.sleep() # Keep track of the error for the retry warning. err = e finally: + if not clean_exit: + # We hit some kind of exception, handled or otherwise. We need + # to throw the connection away unless explicitly told not to. + # Close the connection, set the variable to None, and make sure + # we put the None back in the pool to avoid leaking it. + conn = conn and conn.close() + release_conn = True + if release_conn: # Put the connection back to be reused. If the connection is # expired then it will be None, which will get replaced with a @@ -554,23 +643,56 @@ def urlopen(self, method, url, body=None, headers=None, retries=3, if not conn: # Try again - log.warning("Retrying (%d attempts remain) after connection " - "broken by '%r': %s" % (retries, err, url)) - return self.urlopen(method, url, body, headers, retries - 1, + log.warning("Retrying (%r) after connection " + "broken by '%r': %s", retries, err, url) + return self.urlopen(method, url, body, headers, retries, redirect, assert_same_host, timeout=timeout, pool_timeout=pool_timeout, release_conn=release_conn, **response_kw) # Handle redirect? redirect_location = redirect and response.get_redirect_location() - if redirect_location and retries is not False: + if redirect_location: if response.status == 303: method = 'GET' - log.info("Redirecting %s -> %s" % (url, redirect_location)) - return self.urlopen(method, redirect_location, body, headers, - retries - 1, redirect, assert_same_host, - timeout=timeout, pool_timeout=pool_timeout, - release_conn=release_conn, **response_kw) + + try: + retries = retries.increment(method, url, response=response, _pool=self) + except MaxRetryError: + if retries.raise_on_redirect: + # Release the connection for this response, since we're not + # returning it to be released manually. + response.release_conn() + raise + return response + + log.info("Redirecting %s -> %s", url, redirect_location) + return self.urlopen( + method, redirect_location, body, headers, + retries=retries, redirect=redirect, + assert_same_host=assert_same_host, + timeout=timeout, pool_timeout=pool_timeout, + release_conn=release_conn, **response_kw) + + # Check if we should retry the HTTP response. + if retries.is_forced_retry(method, status_code=response.status): + try: + retries = retries.increment(method, url, response=response, _pool=self) + except MaxRetryError: + if retries.raise_on_status: + # Release the connection for this response, since we're not + # returning it to be released manually. + response.release_conn() + raise + return response + retries.sleep() + log.info("Forced retry: %s", url) + return self.urlopen( + method, url, body, headers, + retries=retries, redirect=redirect, + assert_same_host=assert_same_host, + timeout=timeout, pool_timeout=pool_timeout, + release_conn=release_conn, **response_kw) return response @@ -587,37 +709,39 @@ class HTTPSConnectionPool(HTTPConnectionPool): ``assert_hostname`` and ``host`` in this order to verify connections. If ``assert_hostname`` is False, no verification is done. - The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs`` and - ``ssl_version`` are only used if :mod:`ssl` is available and are fed into - :meth:`urllib3.util.ssl_wrap_socket` to upgrade the connection socket - into an SSL socket. + The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs``, + ``ca_cert_dir``, and ``ssl_version`` are only used if :mod:`ssl` is + available and are fed into :meth:`urllib3.util.ssl_wrap_socket` to upgrade + the connection socket into an SSL socket. """ scheme = 'https' ConnectionCls = HTTPSConnection def __init__(self, host, port=None, - strict=False, timeout=None, maxsize=1, - block=False, headers=None, + strict=False, timeout=Timeout.DEFAULT_TIMEOUT, maxsize=1, + block=False, headers=None, retries=None, _proxy=None, _proxy_headers=None, key_file=None, cert_file=None, cert_reqs=None, ca_certs=None, ssl_version=None, assert_hostname=None, assert_fingerprint=None, - **conn_kw): - - if sys.version_info < (2, 7): # Python 2.6 or older - conn_kw.pop('source_address', None) + ca_cert_dir=None, **conn_kw): HTTPConnectionPool.__init__(self, host, port, strict, timeout, maxsize, - block, headers, _proxy, _proxy_headers, **conn_kw) + block, headers, retries, _proxy, _proxy_headers, + **conn_kw) + + if ca_certs and cert_reqs is None: + cert_reqs = 'CERT_REQUIRED' + self.key_file = key_file self.cert_file = cert_file self.cert_reqs = cert_reqs self.ca_certs = ca_certs + self.ca_cert_dir = ca_cert_dir self.ssl_version = ssl_version self.assert_hostname = assert_hostname self.assert_fingerprint = assert_fingerprint - self.conn_kw = conn_kw def _prepare_conn(self, conn): """ @@ -630,34 +754,40 @@ def _prepare_conn(self, conn): cert_file=self.cert_file, cert_reqs=self.cert_reqs, ca_certs=self.ca_certs, + ca_cert_dir=self.ca_cert_dir, assert_hostname=self.assert_hostname, assert_fingerprint=self.assert_fingerprint) conn.ssl_version = self.ssl_version - conn.conn_kw = self.conn_kw - if self.proxy is not None: - # Python 2.7+ - try: - set_tunnel = conn.set_tunnel - except AttributeError: # Platform-specific: Python 2.6 - set_tunnel = conn._set_tunnel + return conn + + def _prepare_proxy(self, conn): + """ + Establish tunnel connection early, because otherwise httplib + would improperly set Host: header to proxy's IP:port. + """ + # Python 2.7+ + try: + set_tunnel = conn.set_tunnel + except AttributeError: # Platform-specific: Python 2.6 + set_tunnel = conn._set_tunnel + + if sys.version_info <= (2, 6, 4) and not self.proxy_headers: # Python 2.6.4 and older + set_tunnel(self.host, self.port) + else: set_tunnel(self.host, self.port, self.proxy_headers) - # Establish tunnel connection early, because otherwise httplib - # would improperly set Host: header to proxy's IP:port. - conn.connect() - return conn + conn.connect() def _new_conn(self): """ Return a fresh :class:`httplib.HTTPSConnection`. """ self.num_connections += 1 - log.info("Starting new HTTPS connection (%d): %s" - % (self.num_connections, self.host)) + log.info("Starting new HTTPS connection (%d): %s", + self.num_connections, self.host) if not self.ConnectionCls or self.ConnectionCls is DummyConnection: - # Platform-specific: Python without ssl raise SSLError("Can't connect to HTTPS URL because the SSL " "module is not available.") @@ -667,21 +797,29 @@ def _new_conn(self): actual_host = self.proxy.host actual_port = self.proxy.port - extra_params = {} - if not six.PY3: # Python 2 - extra_params['strict'] = self.strict - extra_params.update(self.conn_kw) - conn = self.ConnectionCls(host=actual_host, port=actual_port, timeout=self.timeout.connect_timeout, - **extra_params) - if self.proxy is not None: - # Enable Nagle's algorithm for proxies, to avoid packet - # fragmentation. - conn.tcp_nodelay = 0 + strict=self.strict, **self.conn_kw) return self._prepare_conn(conn) + def _validate_conn(self, conn): + """ + Called right before a request is made, after the socket is created. + """ + super(HTTPSConnectionPool, self)._validate_conn(conn) + + # Force connect early to allow us to validate the connection. + if not getattr(conn, 'sock', None): # AppEngine might not have `.sock` + conn.connect() + + if not conn.is_verified: + warnings.warn(( + 'Unverified HTTPS request is being made. ' + 'Adding certificate verification is strongly advised. See: ' + 'https://urllib3.readthedocs.org/en/latest/security.html'), + InsecureRequestWarning) + def connection_from_url(url, **kw): """ @@ -698,12 +836,13 @@ def connection_from_url(url, **kw): :class:`.ConnectionPool`. Useful for specifying things like timeout, maxsize, headers, etc. - Example: :: + Example:: >>> conn = connection_from_url('http://google.com/') >>> r = conn.request('GET', '/') """ scheme, host, port = get_host(url) + port = port or port_by_scheme.get(scheme, 80) if scheme == 'https': return HTTPSConnectionPool(host, port=port, **kw) else: diff --git a/pip/_vendor/requests/packages/urllib3/contrib/appengine.py b/pip/_vendor/requests/packages/urllib3/contrib/appengine.py new file mode 100644 index 00000000000..f4289c0ff8d --- /dev/null +++ b/pip/_vendor/requests/packages/urllib3/contrib/appengine.py @@ -0,0 +1,231 @@ +from __future__ import absolute_import +import logging +import os +import warnings + +from ..exceptions import ( + HTTPError, + HTTPWarning, + MaxRetryError, + ProtocolError, + TimeoutError, + SSLError +) + +from ..packages.six import BytesIO +from ..request import RequestMethods +from ..response import HTTPResponse +from ..util.timeout import Timeout +from ..util.retry import Retry + +try: + from google.appengine.api import urlfetch +except ImportError: + urlfetch = None + + +log = logging.getLogger(__name__) + + +class AppEnginePlatformWarning(HTTPWarning): + pass + + +class AppEnginePlatformError(HTTPError): + pass + + +class AppEngineManager(RequestMethods): + """ + Connection manager for Google App Engine sandbox applications. + + This manager uses the URLFetch service directly instead of using the + emulated httplib, and is subject to URLFetch limitations as described in + the App Engine documentation here: + + https://cloud.google.com/appengine/docs/python/urlfetch + + Notably it will raise an AppEnginePlatformError if: + * URLFetch is not available. + * If you attempt to use this on GAEv2 (Managed VMs), as full socket + support is available. + * If a request size is more than 10 megabytes. + * If a response size is more than 32 megabtyes. + * If you use an unsupported request method such as OPTIONS. + + Beyond those cases, it will raise normal urllib3 errors. + """ + + def __init__(self, headers=None, retries=None, validate_certificate=True): + if not urlfetch: + raise AppEnginePlatformError( + "URLFetch is not available in this environment.") + + if is_prod_appengine_mvms(): + raise AppEnginePlatformError( + "Use normal urllib3.PoolManager instead of AppEngineManager" + "on Managed VMs, as using URLFetch is not necessary in " + "this environment.") + + warnings.warn( + "urllib3 is using URLFetch on Google App Engine sandbox instead " + "of sockets. To use sockets directly instead of URLFetch see " + "https://urllib3.readthedocs.org/en/latest/contrib.html.", + AppEnginePlatformWarning) + + RequestMethods.__init__(self, headers) + self.validate_certificate = validate_certificate + + self.retries = retries or Retry.DEFAULT + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + # Return False to re-raise any potential exceptions + return False + + def urlopen(self, method, url, body=None, headers=None, + retries=None, redirect=True, timeout=Timeout.DEFAULT_TIMEOUT, + **response_kw): + + retries = self._get_retries(retries, redirect) + + try: + response = urlfetch.fetch( + url, + payload=body, + method=method, + headers=headers or {}, + allow_truncated=False, + follow_redirects=( + redirect and + retries.redirect != 0 and + retries.total), + deadline=self._get_absolute_timeout(timeout), + validate_certificate=self.validate_certificate, + ) + except urlfetch.DeadlineExceededError as e: + raise TimeoutError(self, e) + + except urlfetch.InvalidURLError as e: + if 'too large' in str(e): + raise AppEnginePlatformError( + "URLFetch request too large, URLFetch only " + "supports requests up to 10mb in size.", e) + raise ProtocolError(e) + + except urlfetch.DownloadError as e: + if 'Too many redirects' in str(e): + raise MaxRetryError(self, url, reason=e) + raise ProtocolError(e) + + except urlfetch.ResponseTooLargeError as e: + raise AppEnginePlatformError( + "URLFetch response too large, URLFetch only supports" + "responses up to 32mb in size.", e) + + except urlfetch.SSLCertificateError as e: + raise SSLError(e) + + except urlfetch.InvalidMethodError as e: + raise AppEnginePlatformError( + "URLFetch does not support method: %s" % method, e) + + http_response = self._urlfetch_response_to_http_response( + response, **response_kw) + + # Check for redirect response + if (http_response.get_redirect_location() and + retries.raise_on_redirect and redirect): + raise MaxRetryError(self, url, "too many redirects") + + # Check if we should retry the HTTP response. + if retries.is_forced_retry(method, status_code=http_response.status): + retries = retries.increment( + method, url, response=http_response, _pool=self) + log.info("Forced retry: %s", url) + retries.sleep() + return self.urlopen( + method, url, + body=body, headers=headers, + retries=retries, redirect=redirect, + timeout=timeout, **response_kw) + + return http_response + + def _urlfetch_response_to_http_response(self, urlfetch_resp, **response_kw): + + if is_prod_appengine(): + # Production GAE handles deflate encoding automatically, but does + # not remove the encoding header. + content_encoding = urlfetch_resp.headers.get('content-encoding') + + if content_encoding == 'deflate': + del urlfetch_resp.headers['content-encoding'] + + transfer_encoding = urlfetch_resp.headers.get('transfer-encoding') + # We have a full response's content, + # so let's make sure we don't report ourselves as chunked data. + if transfer_encoding == 'chunked': + encodings = transfer_encoding.split(",") + encodings.remove('chunked') + urlfetch_resp.headers['transfer-encoding'] = ','.join(encodings) + + return HTTPResponse( + # In order for decoding to work, we must present the content as + # a file-like object. + body=BytesIO(urlfetch_resp.content), + headers=urlfetch_resp.headers, + status=urlfetch_resp.status_code, + **response_kw + ) + + def _get_absolute_timeout(self, timeout): + if timeout is Timeout.DEFAULT_TIMEOUT: + return 5 # 5s is the default timeout for URLFetch. + if isinstance(timeout, Timeout): + if timeout._read is not timeout._connect: + warnings.warn( + "URLFetch does not support granular timeout settings, " + "reverting to total timeout.", AppEnginePlatformWarning) + return timeout.total + return timeout + + def _get_retries(self, retries, redirect): + if not isinstance(retries, Retry): + retries = Retry.from_int( + retries, redirect=redirect, default=self.retries) + + if retries.connect or retries.read or retries.redirect: + warnings.warn( + "URLFetch only supports total retries and does not " + "recognize connect, read, or redirect retry parameters.", + AppEnginePlatformWarning) + + return retries + + +def is_appengine(): + return (is_local_appengine() or + is_prod_appengine() or + is_prod_appengine_mvms()) + + +def is_appengine_sandbox(): + return is_appengine() and not is_prod_appengine_mvms() + + +def is_local_appengine(): + return ('APPENGINE_RUNTIME' in os.environ and + 'Development/' in os.environ['SERVER_SOFTWARE']) + + +def is_prod_appengine(): + return ('APPENGINE_RUNTIME' in os.environ and + 'Google App Engine/' in os.environ['SERVER_SOFTWARE'] and + not is_prod_appengine_mvms()) + + +def is_prod_appengine_mvms(): + return os.environ.get('GAE_VM', False) == 'true' diff --git a/pip/_vendor/requests/packages/urllib3/contrib/ntlmpool.py b/pip/_vendor/requests/packages/urllib3/contrib/ntlmpool.py index b8cd933034c..11d0b5c34d6 100644 --- a/pip/_vendor/requests/packages/urllib3/contrib/ntlmpool.py +++ b/pip/_vendor/requests/packages/urllib3/contrib/ntlmpool.py @@ -1,14 +1,9 @@ -# urllib3/contrib/ntlmpool.py -# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt) -# -# This module is part of urllib3 and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - """ NTLM authenticating pool, contributed by erikcederstran Issue #10, see: http://code.google.com/p/urllib3/issues/detail?id=10 """ +from __future__ import absolute_import try: from http.client import HTTPSConnection @@ -48,8 +43,8 @@ def _new_conn(self): # Performs the NTLM handshake that secures the connection. The socket # must be kept open while requests are performed. self.num_connections += 1 - log.debug('Starting NTLM HTTPS connection no. %d: https://%s%s' % - (self.num_connections, self.host, self.authurl)) + log.debug('Starting NTLM HTTPS connection no. %d: https://%s%s', + self.num_connections, self.host, self.authurl) headers = {} headers['Connection'] = 'Keep-Alive' @@ -61,13 +56,13 @@ def _new_conn(self): # Send negotiation message headers[req_header] = ( 'NTLM %s' % ntlm.create_NTLM_NEGOTIATE_MESSAGE(self.rawuser)) - log.debug('Request headers: %s' % headers) + log.debug('Request headers: %s', headers) conn.request('GET', self.authurl, None, headers) res = conn.getresponse() reshdr = dict(res.getheaders()) - log.debug('Response status: %s %s' % (res.status, res.reason)) - log.debug('Response headers: %s' % reshdr) - log.debug('Response data: %s [...]' % res.read(100)) + log.debug('Response status: %s %s', res.status, res.reason) + log.debug('Response headers: %s', reshdr) + log.debug('Response data: %s [...]', res.read(100)) # Remove the reference to the socket, so that it can not be closed by # the response object (we want to keep the socket open) @@ -92,12 +87,12 @@ def _new_conn(self): self.pw, NegotiateFlags) headers[req_header] = 'NTLM %s' % auth_msg - log.debug('Request headers: %s' % headers) + log.debug('Request headers: %s', headers) conn.request('GET', self.authurl, None, headers) res = conn.getresponse() - log.debug('Response status: %s %s' % (res.status, res.reason)) - log.debug('Response headers: %s' % dict(res.getheaders())) - log.debug('Response data: %s [...]' % res.read()[:100]) + log.debug('Response status: %s %s', res.status, res.reason) + log.debug('Response headers: %s', dict(res.getheaders())) + log.debug('Response data: %s [...]', res.read()[:100]) if res.status != 200: if res.status == 401: raise Exception('Server rejected request: wrong ' diff --git a/pip/_vendor/requests/packages/urllib3/contrib/pyopenssl.py b/pip/_vendor/requests/packages/urllib3/contrib/pyopenssl.py index 21a12c68adb..ed3b9cc3423 100644 --- a/pip/_vendor/requests/packages/urllib3/contrib/pyopenssl.py +++ b/pip/_vendor/requests/packages/urllib3/contrib/pyopenssl.py @@ -29,7 +29,7 @@ when the required modules are installed. Activating this module also has the positive side effect of disabling SSL/TLS -encryption in Python 2 (see `CRIME attack`_). +compression in Python 2 (see `CRIME attack`_). If you want to configure the default list of supported cipher suites, you can set the ``urllib3.contrib.pyopenssl.DEFAULT_SSL_CIPHER_LIST`` variable. @@ -38,23 +38,33 @@ ---------------- :var DEFAULT_SSL_CIPHER_LIST: The list of supported SSL/TLS cipher suites. - Default: ``ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES: - ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS`` .. _sni: https://en.wikipedia.org/wiki/Server_Name_Indication .. _crime attack: https://en.wikipedia.org/wiki/CRIME_(security_exploit) ''' +from __future__ import absolute_import + +try: + from ndg.httpsclient.ssl_peer_verification import SUBJ_ALT_NAME_SUPPORT + from ndg.httpsclient.subj_alt_name import SubjectAltName as BaseSubjectAltName +except SyntaxError as e: + raise ImportError(e) -from ndg.httpsclient.ssl_peer_verification import SUBJ_ALT_NAME_SUPPORT -from ndg.httpsclient.subj_alt_name import SubjectAltName as BaseSubjectAltName import OpenSSL.SSL from pyasn1.codec.der import decoder as der_decoder from pyasn1.type import univ, constraint -from socket import _fileobject, timeout +from socket import timeout, error as SocketError + +try: # Platform-specific: Python 2 + from socket import _fileobject +except ImportError: # Platform-specific: Python 3 + _fileobject = None + from urllib3.packages.backports.makefile import backport_makefile + import ssl import select -from cStringIO import StringIO +import six from .. import connection from .. import util @@ -67,33 +77,31 @@ # Map from urllib3 to PyOpenSSL compatible parameter-values. _openssl_versions = { ssl.PROTOCOL_SSLv23: OpenSSL.SSL.SSLv23_METHOD, - ssl.PROTOCOL_SSLv3: OpenSSL.SSL.SSLv3_METHOD, ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD, } + +if hasattr(ssl, 'PROTOCOL_TLSv1_1') and hasattr(OpenSSL.SSL, 'TLSv1_1_METHOD'): + _openssl_versions[ssl.PROTOCOL_TLSv1_1] = OpenSSL.SSL.TLSv1_1_METHOD + +if hasattr(ssl, 'PROTOCOL_TLSv1_2') and hasattr(OpenSSL.SSL, 'TLSv1_2_METHOD'): + _openssl_versions[ssl.PROTOCOL_TLSv1_2] = OpenSSL.SSL.TLSv1_2_METHOD + +try: + _openssl_versions.update({ssl.PROTOCOL_SSLv3: OpenSSL.SSL.SSLv3_METHOD}) +except AttributeError: + pass + _openssl_verify = { ssl.CERT_NONE: OpenSSL.SSL.VERIFY_NONE, ssl.CERT_OPTIONAL: OpenSSL.SSL.VERIFY_PEER, - ssl.CERT_REQUIRED: OpenSSL.SSL.VERIFY_PEER - + OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT, + ssl.CERT_REQUIRED: + OpenSSL.SSL.VERIFY_PEER + OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT, } -# A secure default. -# Sources for more information on TLS ciphers: -# -# - https://wiki.mozilla.org/Security/Server_Side_TLS -# - https://www.ssllabs.com/projects/best-practices/index.html -# - https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/ -# -# The general intent is: -# - Prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE), -# - prefer ECDHE over DHE for better performance, -# - prefer any AES-GCM over any AES-CBC for better performance and security, -# - use 3DES as fallback which is secure but slow, -# - disable NULL authentication, MD5 MACs and DSS for security reasons. -DEFAULT_SSL_CIPHER_LIST = "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:" + \ - "ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:" + \ - "!aNULL:!MD5:!DSS" +DEFAULT_SSL_CIPHER_LIST = util.ssl_.DEFAULT_CIPHERS.encode('ascii') +# OpenSSL will only write 16K at a time +SSL_WRITE_BLOCKSIZE = 16384 orig_util_HAS_SNI = util.HAS_SNI orig_connection_ssl_wrap_socket = connection.ssl_wrap_socket @@ -104,6 +112,7 @@ def inject_into_urllib3(): connection.ssl_wrap_socket = ssl_wrap_socket util.HAS_SNI = HAS_SNI + util.IS_PYOPENSSL = True def extract_from_urllib3(): @@ -111,9 +120,10 @@ def extract_from_urllib3(): connection.ssl_wrap_socket = orig_connection_ssl_wrap_socket util.HAS_SNI = orig_util_HAS_SNI + util.IS_PYOPENSSL = False -### Note: This is a slightly bug-fixed version of same from ndg-httpsclient. +# Note: This is a slightly bug-fixed version of same from ndg-httpsclient. class SubjectAltName(BaseSubjectAltName): '''ASN.1 implementation for subjectAltNames support''' @@ -124,7 +134,7 @@ class SubjectAltName(BaseSubjectAltName): constraint.ValueSizeConstraint(1, 1024) -### Note: This is a slightly bug-fixed version of same from ndg-httpsclient. +# Note: This is a slightly bug-fixed version of same from ndg-httpsclient. def get_subj_alt_name(peer_cert): # Search through extensions dns_name = [] @@ -135,7 +145,7 @@ def get_subj_alt_name(peer_cert): for i in range(peer_cert.get_extension_count()): ext = peer_cert.get_extension(i) ext_name = ext.get_short_name() - if ext_name != 'subjectAltName': + if ext_name != b'subjectAltName': continue # PyOpenSSL returns extension data in ASN.1 encoded form @@ -155,205 +165,107 @@ def get_subj_alt_name(peer_cert): return dns_name -class fileobject(_fileobject): - - def _wait_for_sock(self): - rd, wd, ed = select.select([self._sock], [], [], - self._sock.gettimeout()) - if not rd: - raise timeout() - - - def read(self, size=-1): - # Use max, disallow tiny reads in a loop as they are very inefficient. - # We never leave read() with any leftover data from a new recv() call - # in our internal buffer. - rbufsize = max(self._rbufsize, self.default_bufsize) - # Our use of StringIO rather than lists of string objects returned by - # recv() minimizes memory usage and fragmentation that occurs when - # rbufsize is large compared to the typical return value of recv(). - buf = self._rbuf - buf.seek(0, 2) # seek end - if size < 0: - # Read until EOF - self._rbuf = StringIO() # reset _rbuf. we consume it via buf. - while True: - try: - data = self._sock.recv(rbufsize) - except OpenSSL.SSL.WantReadError: - self._wait_for_sock() - continue - if not data: - break - buf.write(data) - return buf.getvalue() - else: - # Read until size bytes or EOF seen, whichever comes first - buf_len = buf.tell() - if buf_len >= size: - # Already have size bytes in our buffer? Extract and return. - buf.seek(0) - rv = buf.read(size) - self._rbuf = StringIO() - self._rbuf.write(buf.read()) - return rv - - self._rbuf = StringIO() # reset _rbuf. we consume it via buf. - while True: - left = size - buf_len - # recv() will malloc the amount of memory given as its - # parameter even though it often returns much less data - # than that. The returned data string is short lived - # as we copy it into a StringIO and free it. This avoids - # fragmentation issues on many platforms. - try: - data = self._sock.recv(left) - except OpenSSL.SSL.WantReadError: - self._wait_for_sock() - continue - if not data: - break - n = len(data) - if n == size and not buf_len: - # Shortcut. Avoid buffer data copies when: - # - We have no data in our buffer. - # AND - # - Our call to recv returned exactly the - # number of bytes we were asked to read. - return data - if n == left: - buf.write(data) - del data # explicit free - break - assert n <= left, "recv(%d) returned %d bytes" % (left, n) - buf.write(data) - buf_len += n - del data # explicit free - #assert buf_len == buf.tell() - return buf.getvalue() - - def readline(self, size=-1): - buf = self._rbuf - buf.seek(0, 2) # seek end - if buf.tell() > 0: - # check if we already have it in our buffer - buf.seek(0) - bline = buf.readline(size) - if bline.endswith('\n') or len(bline) == size: - self._rbuf = StringIO() - self._rbuf.write(buf.read()) - return bline - del bline - if size < 0: - # Read until \n or EOF, whichever comes first - if self._rbufsize <= 1: - # Speed up unbuffered case - buf.seek(0) - buffers = [buf.read()] - self._rbuf = StringIO() # reset _rbuf. we consume it via buf. - data = None - recv = self._sock.recv - while True: - try: - while data != "\n": - data = recv(1) - if not data: - break - buffers.append(data) - except OpenSSL.SSL.WantReadError: - self._wait_for_sock() - continue - break - return "".join(buffers) - - buf.seek(0, 2) # seek end - self._rbuf = StringIO() # reset _rbuf. we consume it via buf. - while True: - try: - data = self._sock.recv(self._rbufsize) - except OpenSSL.SSL.WantReadError: - self._wait_for_sock() - continue - if not data: - break - nl = data.find('\n') - if nl >= 0: - nl += 1 - buf.write(data[:nl]) - self._rbuf.write(data[nl:]) - del data - break - buf.write(data) - return buf.getvalue() - else: - # Read until size bytes or \n or EOF seen, whichever comes first - buf.seek(0, 2) # seek end - buf_len = buf.tell() - if buf_len >= size: - buf.seek(0) - rv = buf.read(size) - self._rbuf = StringIO() - self._rbuf.write(buf.read()) - return rv - self._rbuf = StringIO() # reset _rbuf. we consume it via buf. - while True: - try: - data = self._sock.recv(self._rbufsize) - except OpenSSL.SSL.WantReadError: - self._wait_for_sock() - continue - if not data: - break - left = size - buf_len - # did we just receive a newline? - nl = data.find('\n', 0, left) - if nl >= 0: - nl += 1 - # save the excess data to _rbuf - self._rbuf.write(data[nl:]) - if buf_len: - buf.write(data[:nl]) - break - else: - # Shortcut. Avoid data copy through buf when returning - # a substring of our first recv(). - return data[:nl] - n = len(data) - if n == size and not buf_len: - # Shortcut. Avoid data copy through buf when - # returning exactly all of our first recv(). - return data - if n >= left: - buf.write(data[:left]) - self._rbuf.write(data[left:]) - break - buf.write(data) - buf_len += n - #assert buf_len == buf.tell() - return buf.getvalue() - - class WrappedSocket(object): - '''API-compatibility wrapper for Python OpenSSL's Connection-class.''' + '''API-compatibility wrapper for Python OpenSSL's Connection-class. + + Note: _makefile_refs, _drop() and _reuse() are needed for the garbage + collector of pypy. + ''' - def __init__(self, connection, socket): + def __init__(self, connection, socket, suppress_ragged_eofs=True): self.connection = connection self.socket = socket + self.suppress_ragged_eofs = suppress_ragged_eofs + self._makefile_refs = 0 + self._closed = False def fileno(self): return self.socket.fileno() - def makefile(self, mode, bufsize=-1): - return fileobject(self.connection, mode, bufsize) + # Copy-pasted from Python 3.5 source code + def _decref_socketios(self): + if self._makefile_refs > 0: + self._makefile_refs -= 1 + if self._closed: + self.close() + + def recv(self, *args, **kwargs): + try: + data = self.connection.recv(*args, **kwargs) + except OpenSSL.SSL.SysCallError as e: + if self.suppress_ragged_eofs and e.args == (-1, 'Unexpected EOF'): + return b'' + else: + raise SocketError(str(e)) + except OpenSSL.SSL.ZeroReturnError as e: + if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: + return b'' + else: + raise + except OpenSSL.SSL.WantReadError: + rd, wd, ed = select.select( + [self.socket], [], [], self.socket.gettimeout()) + if not rd: + raise timeout('The read operation timed out') + else: + return self.recv(*args, **kwargs) + else: + return data + + def recv_into(self, *args, **kwargs): + try: + return self.connection.recv_into(*args, **kwargs) + except OpenSSL.SSL.SysCallError as e: + if self.suppress_ragged_eofs and e.args == (-1, 'Unexpected EOF'): + return 0 + else: + raise SocketError(str(e)) + except OpenSSL.SSL.ZeroReturnError as e: + if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: + return 0 + else: + raise + except OpenSSL.SSL.WantReadError: + rd, wd, ed = select.select( + [self.socket], [], [], self.socket.gettimeout()) + if not rd: + raise timeout('The read operation timed out') + else: + return self.recv_into(*args, **kwargs) def settimeout(self, timeout): return self.socket.settimeout(timeout) + def _send_until_done(self, data): + while True: + try: + return self.connection.send(data) + except OpenSSL.SSL.WantWriteError: + _, wlist, _ = select.select([], [self.socket], [], + self.socket.gettimeout()) + if not wlist: + raise timeout() + continue + def sendall(self, data): - return self.connection.sendall(data) + total_sent = 0 + while total_sent < len(data): + sent = self._send_until_done(data[total_sent:total_sent + SSL_WRITE_BLOCKSIZE]) + total_sent += sent + + def shutdown(self): + # FIXME rethrow compatible exceptions should we ever use this + self.connection.shutdown() def close(self): - return self.connection.shutdown() + if self._makefile_refs < 1: + try: + self._closed = True + return self.connection.close() + except OpenSSL.SSL.Error: + return + else: + self._makefile_refs -= 1 def getpeercert(self, binary_form=False): x509 = self.connection.get_peer_certificate() @@ -376,6 +288,25 @@ def getpeercert(self, binary_form=False): ] } + def _reuse(self): + self._makefile_refs += 1 + + def _drop(self): + if self._makefile_refs < 1: + self.close() + else: + self._makefile_refs -= 1 + + +if _fileobject: # Platform-specific: Python 2 + def makefile(self, mode, bufsize=-1): + self._makefile_refs += 1 + return _fileobject(self, mode, bufsize, close=True) +else: # Platform-specific: Python 3 + makefile = backport_makefile + +WrappedSocket.makefile = makefile + def _verify_callback(cnx, x509, err_no, err_depth, return_code): return err_no == 0 @@ -383,23 +314,24 @@ def _verify_callback(cnx, x509, err_no, err_depth, return_code): def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, ca_certs=None, server_hostname=None, - ssl_version=None): + ssl_version=None, ca_cert_dir=None): ctx = OpenSSL.SSL.Context(_openssl_versions[ssl_version]) if certfile: + keyfile = keyfile or certfile # Match behaviour of the normal python ssl library ctx.use_certificate_file(certfile) if keyfile: ctx.use_privatekey_file(keyfile) if cert_reqs != ssl.CERT_NONE: ctx.set_verify(_openssl_verify[cert_reqs], _verify_callback) - if ca_certs: + if ca_certs or ca_cert_dir: try: - ctx.load_verify_locations(ca_certs, None) + ctx.load_verify_locations(ca_certs, ca_cert_dir) except OpenSSL.SSL.Error as e: raise ssl.SSLError('bad ca_certs: %r' % ca_certs, e) else: ctx.set_default_verify_paths() - # Disable TLS compression to migitate CRIME attack (issue #309) + # Disable TLS compression to mitigate CRIME attack (issue #309) OP_NO_COMPRESSION = 0x20000 ctx.set_options(OP_NO_COMPRESSION) @@ -407,16 +339,20 @@ def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, ctx.set_cipher_list(DEFAULT_SSL_CIPHER_LIST) cnx = OpenSSL.SSL.Connection(ctx, sock) + if isinstance(server_hostname, six.text_type): # Platform-specific: Python 3 + server_hostname = server_hostname.encode('utf-8') cnx.set_tlsext_host_name(server_hostname) cnx.set_connect_state() while True: try: cnx.do_handshake() except OpenSSL.SSL.WantReadError: - select.select([sock], [], []) + rd, _, _ = select.select([sock], [], [], sock.gettimeout()) + if not rd: + raise timeout('select timed out') continue except OpenSSL.SSL.Error as e: - raise ssl.SSLError('bad handshake', e) + raise ssl.SSLError('bad handshake: %r' % e) break return WrappedSocket(cnx, sock) diff --git a/pip/_vendor/requests/packages/urllib3/contrib/socks.py b/pip/_vendor/requests/packages/urllib3/contrib/socks.py new file mode 100644 index 00000000000..3748fee5333 --- /dev/null +++ b/pip/_vendor/requests/packages/urllib3/contrib/socks.py @@ -0,0 +1,172 @@ +# -*- coding: utf-8 -*- +""" +SOCKS support for urllib3 +~~~~~~~~~~~~~~~~~~~~~~~~~ + +This contrib module contains provisional support for SOCKS proxies from within +urllib3. This module supports SOCKS4 (specifically the SOCKS4A variant) and +SOCKS5. To enable its functionality, either install PySocks or install this +module with the ``socks`` extra. + +Known Limitations: + +- Currently PySocks does not support contacting remote websites via literal + IPv6 addresses. Any such connection attempt will fail. +- Currently PySocks does not support IPv6 connections to the SOCKS proxy. Any + such connection attempt will fail. +""" +from __future__ import absolute_import + +try: + import socks +except ImportError: + import warnings + from ..exceptions import DependencyWarning + + warnings.warn(( + 'SOCKS support in urllib3 requires the installation of optional ' + 'dependencies: specifically, PySocks. For more information, see ' + 'https://urllib3.readthedocs.org/en/latest/contrib.html#socks-proxies' + ), + DependencyWarning + ) + raise + +from socket import error as SocketError, timeout as SocketTimeout + +from ..connection import ( + HTTPConnection, HTTPSConnection +) +from ..connectionpool import ( + HTTPConnectionPool, HTTPSConnectionPool +) +from ..exceptions import ConnectTimeoutError, NewConnectionError +from ..poolmanager import PoolManager +from ..util.url import parse_url + +try: + import ssl +except ImportError: + ssl = None + + +class SOCKSConnection(HTTPConnection): + """ + A plain-text HTTP connection that connects via a SOCKS proxy. + """ + def __init__(self, *args, **kwargs): + self._socks_options = kwargs.pop('_socks_options') + super(SOCKSConnection, self).__init__(*args, **kwargs) + + def _new_conn(self): + """ + Establish a new connection via the SOCKS proxy. + """ + extra_kw = {} + if self.source_address: + extra_kw['source_address'] = self.source_address + + if self.socket_options: + extra_kw['socket_options'] = self.socket_options + + try: + conn = socks.create_connection( + (self.host, self.port), + proxy_type=self._socks_options['socks_version'], + proxy_addr=self._socks_options['proxy_host'], + proxy_port=self._socks_options['proxy_port'], + proxy_username=self._socks_options['username'], + proxy_password=self._socks_options['password'], + timeout=self.timeout, + **extra_kw + ) + + except SocketTimeout as e: + raise ConnectTimeoutError( + self, "Connection to %s timed out. (connect timeout=%s)" % + (self.host, self.timeout)) + + except socks.ProxyError as e: + # This is fragile as hell, but it seems to be the only way to raise + # useful errors here. + if e.socket_err: + error = e.socket_err + if isinstance(error, SocketTimeout): + raise ConnectTimeoutError( + self, + "Connection to %s timed out. (connect timeout=%s)" % + (self.host, self.timeout) + ) + else: + raise NewConnectionError( + self, + "Failed to establish a new connection: %s" % error + ) + else: + raise NewConnectionError( + self, + "Failed to establish a new connection: %s" % e + ) + + except SocketError as e: # Defensive: PySocks should catch all these. + raise NewConnectionError( + self, "Failed to establish a new connection: %s" % e) + + return conn + + +# We don't need to duplicate the Verified/Unverified distinction from +# urllib3/connection.py here because the HTTPSConnection will already have been +# correctly set to either the Verified or Unverified form by that module. This +# means the SOCKSHTTPSConnection will automatically be the correct type. +class SOCKSHTTPSConnection(SOCKSConnection, HTTPSConnection): + pass + + +class SOCKSHTTPConnectionPool(HTTPConnectionPool): + ConnectionCls = SOCKSConnection + + +class SOCKSHTTPSConnectionPool(HTTPSConnectionPool): + ConnectionCls = SOCKSHTTPSConnection + + +class SOCKSProxyManager(PoolManager): + """ + A version of the urllib3 ProxyManager that routes connections via the + defined SOCKS proxy. + """ + pool_classes_by_scheme = { + 'http': SOCKSHTTPConnectionPool, + 'https': SOCKSHTTPSConnectionPool, + } + + def __init__(self, proxy_url, username=None, password=None, + num_pools=10, headers=None, **connection_pool_kw): + parsed = parse_url(proxy_url) + + if parsed.scheme == 'socks5': + socks_version = socks.PROXY_TYPE_SOCKS5 + elif parsed.scheme == 'socks4': + socks_version = socks.PROXY_TYPE_SOCKS4 + else: + raise ValueError( + "Unable to determine SOCKS version from %s" % proxy_url + ) + + self.proxy_url = proxy_url + + socks_options = { + 'socks_version': socks_version, + 'proxy_host': parsed.host, + 'proxy_port': parsed.port, + 'username': username, + 'password': password, + } + connection_pool_kw['_socks_options'] = socks_options + + super(SOCKSProxyManager, self).__init__( + num_pools, headers, **connection_pool_kw + ) + + self.pool_classes_by_scheme = SOCKSProxyManager.pool_classes_by_scheme diff --git a/pip/_vendor/requests/packages/urllib3/exceptions.py b/pip/_vendor/requests/packages/urllib3/exceptions.py index b4df831fec3..f2e65917b53 100644 --- a/pip/_vendor/requests/packages/urllib3/exceptions.py +++ b/pip/_vendor/requests/packages/urllib3/exceptions.py @@ -1,17 +1,17 @@ -# urllib3/exceptions.py -# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt) -# -# This module is part of urllib3 and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php +from __future__ import absolute_import +# Base Exceptions -## Base Exceptions - class HTTPError(Exception): "Base exception used by this module." pass +class HTTPWarning(Warning): + "Base warning used by this module." + pass + + class PoolError(HTTPError): "Base exception for errors caused within a pool." def __init__(self, pool, message): @@ -44,29 +44,37 @@ class ProxyError(HTTPError): pass -class ConnectionError(HTTPError): - "Raised when a normal connection fails." +class DecodeError(HTTPError): + "Raised when automatic decoding based on Content-Type fails." pass -class DecodeError(HTTPError): - "Raised when automatic decoding based on Content-Type fails." +class ProtocolError(HTTPError): + "Raised when something unexpected happens mid-request/response." pass -## Leaf Exceptions +#: Renamed to ProtocolError but aliased for backwards compatibility. +ConnectionError = ProtocolError + + +# Leaf Exceptions class MaxRetryError(RequestError): - "Raised when the maximum number of retries is exceeded." + """Raised when the maximum number of retries is exceeded. + + :param pool: The connection pool + :type pool: :class:`~urllib3.connectionpool.HTTPConnectionPool` + :param string url: The requested Url + :param exceptions.Exception reason: The underlying error + + """ def __init__(self, pool, url, reason=None): self.reason = reason - message = "Max retries exceeded with url: %s" % url - if reason: - message += " (Caused by %s: %s)" % (type(reason), reason) - else: - message += " (Caused by redirect)" + message = "Max retries exceeded with url: %s (Caused by %r)" % ( + url, reason) RequestError.__init__(self, pool, url, message) @@ -106,6 +114,11 @@ class ConnectTimeoutError(TimeoutError): pass +class NewConnectionError(ConnectTimeoutError, PoolError): + "Raised when we fail to establish a new connection. Usually ECONNREFUSED." + pass + + class EmptyPoolError(PoolError): "Raised when a pool runs out of connections and no more are allowed." pass @@ -116,7 +129,12 @@ class ClosedPoolError(PoolError): pass -class LocationParseError(ValueError, HTTPError): +class LocationValueError(ValueError, HTTPError): + "Raised when there is something wrong with a given URL input." + pass + + +class LocationParseError(LocationValueError): "Raised when get_host or similar fails to parse the URL input." def __init__(self, location): @@ -124,3 +142,68 @@ def __init__(self, location): HTTPError.__init__(self, message) self.location = location + + +class ResponseError(HTTPError): + "Used as a container for an error reason supplied in a MaxRetryError." + GENERIC_ERROR = 'too many error responses' + SPECIFIC_ERROR = 'too many {status_code} error responses' + + +class SecurityWarning(HTTPWarning): + "Warned when perfoming security reducing actions" + pass + + +class SubjectAltNameWarning(SecurityWarning): + "Warned when connecting to a host with a certificate missing a SAN." + pass + + +class InsecureRequestWarning(SecurityWarning): + "Warned when making an unverified HTTPS request." + pass + + +class SystemTimeWarning(SecurityWarning): + "Warned when system time is suspected to be wrong" + pass + + +class InsecurePlatformWarning(SecurityWarning): + "Warned when certain SSL configuration is not available on a platform." + pass + + +class SNIMissingWarning(HTTPWarning): + "Warned when making a HTTPS request without SNI available." + pass + + +class DependencyWarning(HTTPWarning): + """ + Warned when an attempt is made to import a module with missing optional + dependencies. + """ + pass + + +class ResponseNotChunked(ProtocolError, ValueError): + "Response needs to be chunked in order to read it as chunks." + pass + + +class ProxySchemeUnknown(AssertionError, ValueError): + "ProxyManager does not support the supplied scheme" + # TODO(t-8ch): Stop inheriting from AssertionError in v2.0. + + def __init__(self, scheme): + message = "Not supported proxy scheme %s" % scheme + super(ProxySchemeUnknown, self).__init__(message) + + +class HeaderParsingError(HTTPError): + "Raised by assert_header_parsing, but we convert it to a log.warning statement." + def __init__(self, defects, unparsed_data): + message = '%s, unparsed data: %r' % (defects or 'Unknown', unparsed_data) + super(HeaderParsingError, self).__init__(message) diff --git a/pip/_vendor/requests/packages/urllib3/fields.py b/pip/_vendor/requests/packages/urllib3/fields.py index da79e929be6..8fa2a127677 100644 --- a/pip/_vendor/requests/packages/urllib3/fields.py +++ b/pip/_vendor/requests/packages/urllib3/fields.py @@ -1,9 +1,4 @@ -# urllib3/fields.py -# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt) -# -# This module is part of urllib3 and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - +from __future__ import absolute_import import email.utils import mimetypes @@ -41,11 +36,11 @@ def format_header_param(name, value): result = '%s="%s"' % (name, value) try: result.encode('ascii') - except UnicodeEncodeError: + except (UnicodeEncodeError, UnicodeDecodeError): pass else: return result - if not six.PY3: # Python 2: + if not six.PY3 and isinstance(value, six.text_type): # Python 2: value = value.encode('utf-8') value = email.utils.encode_rfc2231(value, 'utf-8') value = '%s*=%s' % (name, value) @@ -78,9 +73,10 @@ def from_tuples(cls, fieldname, value): """ A :class:`~urllib3.fields.RequestField` factory from old-style tuple parameters. - Supports constructing :class:`~urllib3.fields.RequestField` from parameter - of key/value strings AND key/filetuple. A filetuple is a (filename, data, MIME type) - tuple where the MIME type is optional. For example: :: + Supports constructing :class:`~urllib3.fields.RequestField` from + parameter of key/value strings AND key/filetuple. A filetuple is a + (filename, data, MIME type) tuple where the MIME type is optional. + For example:: 'foo': 'bar', 'fakefile': ('foofile.txt', 'contents of foofile'), @@ -125,8 +121,8 @@ def _render_parts(self, header_parts): 'Content-Disposition' fields. :param header_parts: - A sequence of (k, v) typles or a :class:`dict` of (k, v) to format as - `k1="v1"; k2="v2"; ...`. + A sequence of (k, v) typles or a :class:`dict` of (k, v) to format + as `k1="v1"; k2="v2"; ...`. """ parts = [] iterable = header_parts @@ -158,7 +154,8 @@ def render_headers(self): lines.append('\r\n') return '\r\n'.join(lines) - def make_multipart(self, content_disposition=None, content_type=None, content_location=None): + def make_multipart(self, content_disposition=None, content_type=None, + content_location=None): """ Makes this request field into a multipart request field. @@ -172,6 +169,10 @@ def make_multipart(self, content_disposition=None, content_type=None, content_lo """ self.headers['Content-Disposition'] = content_disposition or 'form-data' - self.headers['Content-Disposition'] += '; '.join(['', self._render_parts((('name', self._name), ('filename', self._filename)))]) + self.headers['Content-Disposition'] += '; '.join([ + '', self._render_parts( + (('name', self._name), ('filename', self._filename)) + ) + ]) self.headers['Content-Type'] = content_type self.headers['Content-Location'] = content_location diff --git a/pip/_vendor/requests/packages/urllib3/filepost.py b/pip/_vendor/requests/packages/urllib3/filepost.py index e8b30bddf2e..97a2843ca47 100644 --- a/pip/_vendor/requests/packages/urllib3/filepost.py +++ b/pip/_vendor/requests/packages/urllib3/filepost.py @@ -1,11 +1,5 @@ -# urllib3/filepost.py -# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt) -# -# This module is part of urllib3 and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - +from __future__ import absolute_import import codecs -import mimetypes from uuid import uuid4 from io import BytesIO @@ -38,10 +32,10 @@ def iter_field_objects(fields): i = iter(fields) for field in i: - if isinstance(field, RequestField): - yield field - else: - yield RequestField.from_tuples(*field) + if isinstance(field, RequestField): + yield field + else: + yield RequestField.from_tuples(*field) def iter_fields(fields): diff --git a/pip/_vendor/requests/packages/urllib3/packages/__init__.py b/pip/_vendor/requests/packages/urllib3/packages/__init__.py index 37e83515776..170e974c157 100644 --- a/pip/_vendor/requests/packages/urllib3/packages/__init__.py +++ b/pip/_vendor/requests/packages/urllib3/packages/__init__.py @@ -2,3 +2,4 @@ from . import ssl_match_hostname +__all__ = ('ssl_match_hostname', ) diff --git a/pip/_vendor/requests/packages/urllib3/packages/ordered_dict.py b/pip/_vendor/requests/packages/urllib3/packages/ordered_dict.py index 7f8ee154360..4479363cc45 100644 --- a/pip/_vendor/requests/packages/urllib3/packages/ordered_dict.py +++ b/pip/_vendor/requests/packages/urllib3/packages/ordered_dict.py @@ -2,7 +2,6 @@ # Passes Python2.7's test suite and incorporates all the latest updates. # Copyright 2009 Raymond Hettinger, released under the MIT License. # http://code.activestate.com/recipes/576693/ - try: from thread import get_ident as _get_ident except ImportError: diff --git a/pip/_vendor/requests/packages/urllib3/poolmanager.py b/pip/_vendor/requests/packages/urllib3/poolmanager.py index f18ff2bb7ef..1023dcba388 100644 --- a/pip/_vendor/requests/packages/urllib3/poolmanager.py +++ b/pip/_vendor/requests/packages/urllib3/poolmanager.py @@ -1,9 +1,4 @@ -# urllib3/poolmanager.py -# Copyright 2008-2014 Andrey Petrov and contributors (see CONTRIBUTORS.txt) -# -# This module is part of urllib3 and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - +from __future__ import absolute_import import logging try: # Python 3 @@ -14,23 +9,25 @@ from ._collections import RecentlyUsedContainer from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool from .connectionpool import port_by_scheme +from .exceptions import LocationValueError, MaxRetryError, ProxySchemeUnknown from .request import RequestMethods -from .util import parse_url +from .util.url import parse_url +from .util.retry import Retry __all__ = ['PoolManager', 'ProxyManager', 'proxy_from_url'] +log = logging.getLogger(__name__) + +SSL_KEYWORDS = ('key_file', 'cert_file', 'cert_reqs', 'ca_certs', + 'ssl_version', 'ca_cert_dir') + pool_classes_by_scheme = { 'http': HTTPConnectionPool, 'https': HTTPSConnectionPool, } -log = logging.getLogger(__name__) - -SSL_KEYWORDS = ('key_file', 'cert_file', 'cert_reqs', 'ca_certs', - 'ssl_version') - class PoolManager(RequestMethods): """ @@ -49,7 +46,7 @@ class PoolManager(RequestMethods): Additional parameters are used to create fresh :class:`urllib3.connectionpool.ConnectionPool` instances. - Example: :: + Example:: >>> manager = PoolManager(num_pools=2) >>> r = manager.request('GET', 'http://google.com/') @@ -68,6 +65,17 @@ def __init__(self, num_pools=10, headers=None, **connection_pool_kw): self.pools = RecentlyUsedContainer(num_pools, dispose_func=lambda p: p.close()) + # Locally set the pool classes so other PoolManagers can override them. + self.pool_classes_by_scheme = pool_classes_by_scheme + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.clear() + # Return False to re-raise any potential exceptions + return False + def _new_pool(self, scheme, host, port): """ Create a new :class:`ConnectionPool` based on host, port and scheme. @@ -76,7 +84,7 @@ def _new_pool(self, scheme, host, port): by :meth:`connection_from_url` and companion methods. It is intended to be overridden for customization. """ - pool_cls = pool_classes_by_scheme[scheme] + pool_cls = self.pool_classes_by_scheme[scheme] kwargs = self.connection_pool_kw if scheme == 'http': kwargs = self.connection_pool_kw.copy() @@ -102,10 +110,11 @@ def connection_from_host(self, host, port=None, scheme='http'): ``urllib3.connectionpool.port_by_scheme``. """ - scheme = scheme or 'http' + if not host: + raise LocationValueError("No host specified.") + scheme = scheme or 'http' port = port or port_by_scheme.get(scheme, 80) - pool_key = (scheme, host, port) with self.pools.lock: @@ -118,6 +127,7 @@ def connection_from_host(self, host, port=None, scheme='http'): # Make a fresh ConnectionPool of the desired type pool = self._new_pool(scheme, host, port) self.pools[pool_key] = pool + return pool def connection_from_url(self, url): @@ -161,13 +171,25 @@ def urlopen(self, method, url, redirect=True, **kw): # Support relative URLs for redirecting. redirect_location = urljoin(url, redirect_location) - # RFC 2616, Section 10.3.4 + # RFC 7231, Section 6.4.4 if response.status == 303: method = 'GET' - log.info("Redirecting %s -> %s" % (url, redirect_location)) - kw['retries'] = kw.get('retries', 3) - 1 # Persist retries countdown + retries = kw.get('retries') + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect) + + try: + retries = retries.increment(method, url, response=response, _pool=conn) + except MaxRetryError: + if retries.raise_on_redirect: + raise + return response + + kw['retries'] = retries kw['redirect'] = redirect + + log.info("Redirecting %s -> %s", url, redirect_location) return self.urlopen(method, redirect_location, **kw) @@ -208,12 +230,16 @@ def __init__(self, proxy_url, num_pools=10, headers=None, if not proxy.port: port = port_by_scheme.get(proxy.scheme, 80) proxy = proxy._replace(port=port) + + if proxy.scheme not in ("http", "https"): + raise ProxySchemeUnknown(proxy.scheme) + self.proxy = proxy self.proxy_headers = proxy_headers or {} - assert self.proxy.scheme in ("http", "https"), \ - 'Not supported proxy scheme %s' % self.proxy.scheme + connection_pool_kw['_proxy'] = self.proxy connection_pool_kw['_proxy_headers'] = self.proxy_headers + super(ProxyManager, self).__init__( num_pools, headers, **connection_pool_kw) @@ -248,10 +274,10 @@ def urlopen(self, method, url, redirect=True, **kw): # For proxied HTTPS requests, httplib sets the necessary headers # on the CONNECT to the proxy. For HTTP, we'll definitely # need to set 'Host' at the very least. - kw['headers'] = self._set_proxy_headers(url, kw.get('headers', - self.headers)) + headers = kw.get('headers', self.headers) + kw['headers'] = self._set_proxy_headers(url, headers) - return super(ProxyManager, self).urlopen(method, url, redirect, **kw) + return super(ProxyManager, self).urlopen(method, url, redirect=redirect, **kw) def proxy_from_url(url, **kw): diff --git a/pip/_vendor/requests/packages/urllib3/request.py b/pip/_vendor/requests/packages/urllib3/request.py index 2a92cc20834..d5aa62d887f 100644 --- a/pip/_vendor/requests/packages/urllib3/request.py +++ b/pip/_vendor/requests/packages/urllib3/request.py @@ -1,9 +1,4 @@ -# urllib3/request.py -# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt) -# -# This module is part of urllib3 and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - +from __future__ import absolute_import try: from urllib.parse import urlencode except ImportError: @@ -26,8 +21,8 @@ class RequestMethods(object): Specifically, - :meth:`.request_encode_url` is for sending requests whose fields are encoded - in the URL (such as GET, HEAD, DELETE). + :meth:`.request_encode_url` is for sending requests whose fields are + encoded in the URL (such as GET, HEAD, DELETE). :meth:`.request_encode_body` is for sending requests whose fields are encoded in the *body* of the request using multipart or www-form-urlencoded @@ -51,7 +46,7 @@ def __init__(self, headers=None): def urlopen(self, method, url, body=None, headers=None, encode_multipart=True, multipart_boundary=None, - **kw): # Abstract + **kw): # Abstract raise NotImplemented("Classes extending RequestMethods must implement " "their own ``urlopen`` method.") @@ -61,8 +56,8 @@ def request(self, method, url, fields=None, headers=None, **urlopen_kw): ``fields`` based on the ``method`` used. This is a convenience method that requires the least amount of manual - effort. It can be used in most situations, while still having the option - to drop down to more specific methods when necessary, such as + effort. It can be used in most situations, while still having the + option to drop down to more specific methods when necessary, such as :meth:`request_encode_url`, :meth:`request_encode_body`, or even the lowest level :meth:`urlopen`. """ @@ -70,21 +65,29 @@ def request(self, method, url, fields=None, headers=None, **urlopen_kw): if method in self._encode_url_methods: return self.request_encode_url(method, url, fields=fields, - headers=headers, - **urlopen_kw) + headers=headers, + **urlopen_kw) else: return self.request_encode_body(method, url, fields=fields, - headers=headers, - **urlopen_kw) + headers=headers, + **urlopen_kw) - def request_encode_url(self, method, url, fields=None, **urlopen_kw): + def request_encode_url(self, method, url, fields=None, headers=None, + **urlopen_kw): """ Make a request using :meth:`urlopen` with the ``fields`` encoded in the url. This is useful for request methods like GET, HEAD, DELETE, etc. """ + if headers is None: + headers = self.headers + + extra_kw = {'headers': headers} + extra_kw.update(urlopen_kw) + if fields: url += '?' + urlencode(fields) - return self.urlopen(method, url, **urlopen_kw) + + return self.urlopen(method, url, **extra_kw) def request_encode_body(self, method, url, fields=None, headers=None, encode_multipart=True, multipart_boundary=None, @@ -94,18 +97,18 @@ def request_encode_body(self, method, url, fields=None, headers=None, the body. This is useful for request methods like POST, PUT, PATCH, etc. When ``encode_multipart=True`` (default), then - :meth:`urllib3.filepost.encode_multipart_formdata` is used to encode the - payload with the appropriate content type. Otherwise + :meth:`urllib3.filepost.encode_multipart_formdata` is used to encode + the payload with the appropriate content type. Otherwise :meth:`urllib.urlencode` is used with the 'application/x-www-form-urlencoded' content type. Multipart encoding must be used when posting files, and it's reasonably - safe to use it in other times too. However, it may break request signing, - such as with OAuth. + safe to use it in other times too. However, it may break request + signing, such as with OAuth. Supports an optional ``fields`` parameter of key/value strings AND key/filetuple. A filetuple is a (filename, data, MIME type) tuple where - the MIME type is optional. For example: :: + the MIME type is optional. For example:: fields = { 'foo': 'bar', @@ -119,23 +122,30 @@ def request_encode_body(self, method, url, fields=None, headers=None, When uploading a file, providing a filename (the first parameter of the tuple) is optional but recommended to best mimick behavior of browsers. - Note that if ``headers`` are supplied, the 'Content-Type' header will be - overwritten because it depends on the dynamic random boundary string + Note that if ``headers`` are supplied, the 'Content-Type' header will + be overwritten because it depends on the dynamic random boundary string which is used to compose the body of the request. The random boundary string can be explicitly set with the ``multipart_boundary`` parameter. """ - if encode_multipart: - body, content_type = encode_multipart_formdata(fields or {}, - boundary=multipart_boundary) - else: - body, content_type = (urlencode(fields or {}), - 'application/x-www-form-urlencoded') - if headers is None: headers = self.headers - headers_ = {'Content-Type': content_type} - headers_.update(headers) + extra_kw = {'headers': {}} + + if fields: + if 'body' in urlopen_kw: + raise TypeError( + "request got values for both 'fields' and 'body', can only specify one.") + + if encode_multipart: + body, content_type = encode_multipart_formdata(fields, boundary=multipart_boundary) + else: + body, content_type = urlencode(fields), 'application/x-www-form-urlencoded' + + extra_kw['body'] = body + extra_kw['headers'] = {'Content-Type': content_type} + + extra_kw['headers'].update(headers) + extra_kw.update(urlopen_kw) - return self.urlopen(method, url, body=body, headers=headers_, - **urlopen_kw) + return self.urlopen(method, url, **extra_kw) diff --git a/pip/_vendor/requests/packages/urllib3/response.py b/pip/_vendor/requests/packages/urllib3/response.py index db441828aad..ac1b2f19e3d 100644 --- a/pip/_vendor/requests/packages/urllib3/response.py +++ b/pip/_vendor/requests/packages/urllib3/response.py @@ -1,21 +1,18 @@ -# urllib3/response.py -# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt) -# -# This module is part of urllib3 and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - - -import logging +from __future__ import absolute_import +from contextlib import contextmanager import zlib import io +from socket import timeout as SocketTimeout +from socket import error as SocketError from ._collections import HTTPHeaderDict -from .exceptions import DecodeError -from .packages.six import string_types as basestring, binary_type -from .util import is_fp_closed - - -log = logging.getLogger(__name__) +from .exceptions import ( + ProtocolError, DecodeError, ReadTimeoutError, ResponseNotChunked +) +from .packages.six import string_types as basestring, binary_type, PY3 +from .packages.six.moves import http_client as httplib +from .connection import HTTPException, BaseSSLError +from .util.response import is_fp_closed, is_response_to_head class DeflateDecoder(object): @@ -29,6 +26,9 @@ def __getattr__(self, name): return getattr(self._obj, name) def decompress(self, data): + if not data: + return data + if not self._first_try: return self._obj.decompress(data) @@ -44,9 +44,23 @@ def decompress(self, data): self._data = None +class GzipDecoder(object): + + def __init__(self): + self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS) + + def __getattr__(self, name): + return getattr(self._obj, name) + + def decompress(self, data): + if not data: + return data + return self._obj.decompress(data) + + def _get_decoder(mode): if mode == 'gzip': - return zlib.decompressobj(16 + zlib.MAX_WBITS) + return GzipDecoder() return DeflateDecoder() @@ -56,7 +70,10 @@ class HTTPResponse(io.IOBase): HTTP Response container. Backwards-compatible to httplib's HTTPResponse but the response ``body`` is - loaded and decoded on-demand when the ``data`` property is accessed. + loaded and decoded on-demand when the ``data`` property is accessed. This + class is also compatible with the Python standard library's :mod:`io` + module, and can hence be treated as a readable object in the context of that + framework. Extra parameters for behaviour not present in httplib.HTTPResponse: @@ -81,9 +98,10 @@ def __init__(self, body='', headers=None, status=0, version=0, reason=None, strict=0, preload_content=True, decode_content=True, original_response=None, pool=None, connection=None): - self.headers = HTTPHeaderDict() - if headers: - self.headers.update(headers) + if isinstance(headers, HTTPHeaderDict): + self.headers = headers + else: + self.headers = HTTPHeaderDict(headers) self.status = status self.version = version self.reason = reason @@ -91,17 +109,30 @@ def __init__(self, body='', headers=None, status=0, version=0, reason=None, self.decode_content = decode_content self._decoder = None - self._body = body if body and isinstance(body, basestring) else None + self._body = None self._fp = None self._original_response = original_response self._fp_bytes_read = 0 + if body and isinstance(body, (basestring, binary_type)): + self._body = body + self._pool = pool self._connection = connection if hasattr(body, 'read'): self._fp = body + # Are we using the chunked-style of transfer encoding? + self.chunked = False + self.chunk_left = None + tr_enc = self.headers.get('transfer-encoding', '').lower() + # Don't incur the penalty of creating a list and then discarding it + encodings = (enc.strip() for enc in tr_enc.split(",")) + if "chunked" in encodings: + self.chunked = True + + # If requested, preload the body. if preload_content and not self._body: self._body = self.read(decode_content=decode_content) @@ -142,6 +173,102 @@ def tell(self): """ return self._fp_bytes_read + def _init_decoder(self): + """ + Set-up the _decoder attribute if necessar. + """ + # Note: content-encoding value should be case-insensitive, per RFC 7230 + # Section 3.2 + content_encoding = self.headers.get('content-encoding', '').lower() + if self._decoder is None and content_encoding in self.CONTENT_DECODERS: + self._decoder = _get_decoder(content_encoding) + + def _decode(self, data, decode_content, flush_decoder): + """ + Decode the data passed in and potentially flush the decoder. + """ + try: + if decode_content and self._decoder: + data = self._decoder.decompress(data) + except (IOError, zlib.error) as e: + content_encoding = self.headers.get('content-encoding', '').lower() + raise DecodeError( + "Received response with content-encoding: %s, but " + "failed to decode it." % content_encoding, e) + + if flush_decoder and decode_content: + data += self._flush_decoder() + + return data + + def _flush_decoder(self): + """ + Flushes the decoder. Should only be called if the decoder is actually + being used. + """ + if self._decoder: + buf = self._decoder.decompress(b'') + return buf + self._decoder.flush() + + return b'' + + @contextmanager + def _error_catcher(self): + """ + Catch low-level python exceptions, instead re-raising urllib3 + variants, so that low-level exceptions are not leaked in the + high-level api. + + On exit, release the connection back to the pool. + """ + clean_exit = False + + try: + try: + yield + + except SocketTimeout: + # FIXME: Ideally we'd like to include the url in the ReadTimeoutError but + # there is yet no clean way to get at it from this context. + raise ReadTimeoutError(self._pool, None, 'Read timed out.') + + except BaseSSLError as e: + # FIXME: Is there a better way to differentiate between SSLErrors? + if 'read operation timed out' not in str(e): # Defensive: + # This shouldn't happen but just in case we're missing an edge + # case, let's avoid swallowing SSL errors. + raise + + raise ReadTimeoutError(self._pool, None, 'Read timed out.') + + except (HTTPException, SocketError) as e: + # This includes IncompleteRead. + raise ProtocolError('Connection broken: %r' % e, e) + + # If no exception is thrown, we should avoid cleaning up + # unnecessarily. + clean_exit = True + finally: + # If we didn't terminate cleanly, we need to throw away our + # connection. + if not clean_exit: + # The response may not be closed but we're not going to use it + # anymore so close it now to ensure that the connection is + # released back to the pool. + if self._original_response: + self._original_response.close() + + # Closing the response may not actually be sufficient to close + # everything, so if we have a hold of the connection close that + # too. + if self._connection: + self._connection.close() + + # If we hold the original response but it's closed now, we should + # return the connection back to the pool. + if self._original_response and self._original_response.isclosed(): + self.release_conn() + def read(self, amt=None, decode_content=None, cache_content=False): """ Similar to :meth:`httplib.HTTPResponse.read`, but with two additional @@ -163,12 +290,7 @@ def read(self, amt=None, decode_content=None, cache_content=False): after having ``.read()`` the file object. (Overridden if ``amt`` is set.) """ - # Note: content-encoding value should be case-insensitive, per RFC 2616 - # Section 3.5 - content_encoding = self.headers.get('content-encoding', '').lower() - if self._decoder is None: - if content_encoding in self.CONTENT_DECODERS: - self._decoder = _get_decoder(content_encoding) + self._init_decoder() if decode_content is None: decode_content = self.decode_content @@ -176,8 +298,9 @@ def read(self, amt=None, decode_content=None, cache_content=False): return flush_decoder = False + data = None - try: + with self._error_catcher(): if amt is None: # cStringIO doesn't like amt=None data = self._fp.read() @@ -190,35 +313,21 @@ def read(self, amt=None, decode_content=None, cache_content=False): # # This is redundant to what httplib/http.client _should_ # already do. However, versions of python released before - # December 15, 2012 (http://bugs.python.org/issue16298) do not - # properly close the connection in all cases. There is no harm - # in redundantly calling close. + # December 15, 2012 (http://bugs.python.org/issue16298) do + # not properly close the connection in all cases. There is + # no harm in redundantly calling close. self._fp.close() flush_decoder = True + if data: self._fp_bytes_read += len(data) - try: - if decode_content and self._decoder: - data = self._decoder.decompress(data) - except (IOError, zlib.error) as e: - raise DecodeError( - "Received response with content-encoding: %s, but " - "failed to decode it." % content_encoding, - e) - - if flush_decoder and decode_content and self._decoder: - buf = self._decoder.decompress(binary_type()) - data += buf + self._decoder.flush() + data = self._decode(data, decode_content, flush_decoder) if cache_content: self._body = data - return data - - finally: - if self._original_response and self._original_response.isclosed(): - self.release_conn() + return data def stream(self, amt=2**16, decode_content=None): """ @@ -236,12 +345,15 @@ def stream(self, amt=2**16, decode_content=None): If True, will attempt to decode the body based on the 'content-encoding' header. """ - while not is_fp_closed(self._fp): - data = self.read(amt=amt, decode_content=decode_content) - - if data: - yield data + if self.chunked: + for line in self.read_chunked(amt, decode_content=decode_content): + yield line + else: + while not is_fp_closed(self._fp): + data = self.read(amt=amt, decode_content=decode_content) + if data: + yield data @classmethod def from_httplib(ResponseCls, r, **response_kw): @@ -252,14 +364,17 @@ def from_httplib(ResponseCls, r, **response_kw): Remaining parameters are passed to the HTTPResponse constructor, along with ``original_response=r``. """ + headers = r.msg - headers = HTTPHeaderDict() - for k, v in r.getheaders(): - headers.add(k, v) + if not isinstance(headers, HTTPHeaderDict): + if PY3: # Python 3 + headers = HTTPHeaderDict(headers.items()) + else: # Python 2 + headers = HTTPHeaderDict.from_httplib(headers) # HTTPResponse objects in Python 3 don't have a .strict attribute strict = getattr(r, 'strict', 0) - return ResponseCls(body=r, + resp = ResponseCls(body=r, headers=headers, status=r.status, version=r.version, @@ -267,6 +382,7 @@ def from_httplib(ResponseCls, r, **response_kw): strict=strict, original_response=r, **response_kw) + return resp # Backwards-compatibility methods for httplib.HTTPResponse def getheaders(self): @@ -280,6 +396,9 @@ def close(self): if not self.closed: self._fp.close() + if self._connection: + self._connection.close() + @property def closed(self): if self._fp is None: @@ -297,7 +416,7 @@ def fileno(self): elif hasattr(self._fp, "fileno"): return self._fp.fileno() else: - raise IOError("The file-like object this HTTPResponse is wrapped " + raise IOError("The file-like object this HTTPResponse is wrapped " "around has no file descriptor") def flush(self): @@ -305,4 +424,103 @@ def flush(self): return self._fp.flush() def readable(self): + # This method is required for `io` module compatibility. return True + + def readinto(self, b): + # This method is required for `io` module compatibility. + temp = self.read(len(b)) + if len(temp) == 0: + return 0 + else: + b[:len(temp)] = temp + return len(temp) + + def _update_chunk_length(self): + # First, we'll figure out length of a chunk and then + # we'll try to read it from socket. + if self.chunk_left is not None: + return + line = self._fp.fp.readline() + line = line.split(b';', 1)[0] + try: + self.chunk_left = int(line, 16) + except ValueError: + # Invalid chunked protocol response, abort. + self.close() + raise httplib.IncompleteRead(line) + + def _handle_chunk(self, amt): + returned_chunk = None + if amt is None: + chunk = self._fp._safe_read(self.chunk_left) + returned_chunk = chunk + self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. + self.chunk_left = None + elif amt < self.chunk_left: + value = self._fp._safe_read(amt) + self.chunk_left = self.chunk_left - amt + returned_chunk = value + elif amt == self.chunk_left: + value = self._fp._safe_read(amt) + self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. + self.chunk_left = None + returned_chunk = value + else: # amt > self.chunk_left + returned_chunk = self._fp._safe_read(self.chunk_left) + self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. + self.chunk_left = None + return returned_chunk + + def read_chunked(self, amt=None, decode_content=None): + """ + Similar to :meth:`HTTPResponse.read`, but with an additional + parameter: ``decode_content``. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + """ + self._init_decoder() + # FIXME: Rewrite this method and make it a class with a better structured logic. + if not self.chunked: + raise ResponseNotChunked( + "Response is not chunked. " + "Header 'transfer-encoding: chunked' is missing.") + + # Don't bother reading the body of a HEAD request. + if self._original_response and is_response_to_head(self._original_response): + self._original_response.close() + return + + with self._error_catcher(): + while True: + self._update_chunk_length() + if self.chunk_left == 0: + break + chunk = self._handle_chunk(amt) + decoded = self._decode(chunk, decode_content=decode_content, + flush_decoder=False) + if decoded: + yield decoded + + if decode_content: + # On CPython and PyPy, we should never need to flush the + # decoder. However, on Jython we *might* need to, so + # lets defensively do it anyway. + decoded = self._flush_decoder() + if decoded: # Platform-specific: Jython. + yield decoded + + # Chunk content ends with \r\n: discard it. + while True: + line = self._fp.fp.readline() + if not line: + # Some sites may not end with '\r\n'. + break + if line == b'\r\n': + break + + # We read everything; close the "file". + if self._original_response: + self._original_response.close() diff --git a/pip/_vendor/requests/packages/urllib3/util/__init__.py b/pip/_vendor/requests/packages/urllib3/util/__init__.py index a40185eeaf3..4778cf9962b 100644 --- a/pip/_vendor/requests/packages/urllib3/util/__init__.py +++ b/pip/_vendor/requests/packages/urllib3/util/__init__.py @@ -1,15 +1,12 @@ -# urllib3/util/__init__.py -# Copyright 2008-2014 Andrey Petrov and contributors (see CONTRIBUTORS.txt) -# -# This module is part of urllib3 and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - +from __future__ import absolute_import +# For backwards compatibility, provide imports that used to be here. from .connection import is_connection_dropped from .request import make_headers from .response import is_fp_closed from .ssl_ import ( SSLContext, HAS_SNI, + IS_PYOPENSSL, assert_fingerprint, resolve_cert_reqs, resolve_ssl_version, @@ -19,9 +16,31 @@ current_time, Timeout, ) + +from .retry import Retry from .url import ( get_host, parse_url, split_first, Url, ) + +__all__ = ( + 'HAS_SNI', + 'IS_PYOPENSSL', + 'SSLContext', + 'Retry', + 'Timeout', + 'Url', + 'assert_fingerprint', + 'current_time', + 'is_connection_dropped', + 'is_fp_closed', + 'get_host', + 'parse_url', + 'make_headers', + 'resolve_cert_reqs', + 'resolve_ssl_version', + 'split_first', + 'ssl_wrap_socket', +) diff --git a/pip/_vendor/requests/packages/urllib3/util/connection.py b/pip/_vendor/requests/packages/urllib3/util/connection.py index 8deeab5cc03..01a4812f21b 100644 --- a/pip/_vendor/requests/packages/urllib3/util/connection.py +++ b/pip/_vendor/requests/packages/urllib3/util/connection.py @@ -1,4 +1,5 @@ -from socket import error as SocketError +from __future__ import absolute_import +import socket try: from select import poll, POLLIN except ImportError: # `poll` doesn't exist on OSX and other platforms @@ -8,6 +9,7 @@ except ImportError: # `select` doesn't exist on AppEngine. select = False + def is_connection_dropped(conn): # Platform-specific """ Returns True if the connection is dropped and should be closed. @@ -22,7 +24,7 @@ def is_connection_dropped(conn): # Platform-specific if sock is False: # Platform-specific: AppEngine return False if sock is None: # Connection already closed (such as by httplib). - return False + return True if not poll: if not select: # Platform-specific: AppEngine @@ -30,7 +32,7 @@ def is_connection_dropped(conn): # Platform-specific try: return select([sock], [], [], 0.0)[0] - except SocketError: + except socket.error: return True # This version is better on platforms that support it. @@ -42,4 +44,58 @@ def is_connection_dropped(conn): # Platform-specific return True +# This function is copied from socket.py in the Python 2.7 standard +# library test suite. Added to its signature is only `socket_options`. +def create_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, + source_address=None, socket_options=None): + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`getdefaulttimeout` + is used. If *source_address* is set it must be a tuple of (host, port) + for the socket to bind as a source address before making the connection. + An host of '' or port 0 tells the OS to use the default. + """ + + host, port = address + if host.startswith('['): + host = host.strip('[]') + err = None + for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket.socket(af, socktype, proto) + + # If provided, set socket level options before connecting. + # This is the only addition urllib3 makes to this function. + _set_socket_options(sock, socket_options) + + if timeout is not socket._GLOBAL_DEFAULT_TIMEOUT: + sock.settimeout(timeout) + if source_address: + sock.bind(source_address) + sock.connect(sa) + return sock + + except socket.error as e: + err = e + if sock is not None: + sock.close() + sock = None + + if err is not None: + raise err + + raise socket.error("getaddrinfo returns an empty list") + + +def _set_socket_options(sock, options): + if options is None: + return + for opt in options: + sock.setsockopt(*opt) diff --git a/pip/_vendor/requests/packages/urllib3/util/request.py b/pip/_vendor/requests/packages/urllib3/util/request.py index d48d6513b16..73779315f4b 100644 --- a/pip/_vendor/requests/packages/urllib3/util/request.py +++ b/pip/_vendor/requests/packages/urllib3/util/request.py @@ -1,13 +1,13 @@ +from __future__ import absolute_import from base64 import b64encode -from ..packages import six - +from ..packages.six import b ACCEPT_ENCODING = 'gzip,deflate' def make_headers(keep_alive=None, accept_encoding=None, user_agent=None, - basic_auth=None, proxy_basic_auth=None): + basic_auth=None, proxy_basic_auth=None, disable_cache=None): """ Shortcuts for generating request headers. @@ -32,7 +32,10 @@ def make_headers(keep_alive=None, accept_encoding=None, user_agent=None, Colon-separated username:password string for 'proxy-authorization: basic ...' auth header. - Example: :: + :param disable_cache: + If ``True``, adds 'cache-control: no-cache' header. + + Example:: >>> make_headers(keep_alive=True, user_agent="Batman/1.0") {'connection': 'keep-alive', 'user-agent': 'Batman/1.0'} @@ -57,12 +60,13 @@ def make_headers(keep_alive=None, accept_encoding=None, user_agent=None, if basic_auth: headers['authorization'] = 'Basic ' + \ - b64encode(six.b(basic_auth)).decode('utf-8') + b64encode(b(basic_auth)).decode('utf-8') if proxy_basic_auth: headers['proxy-authorization'] = 'Basic ' + \ - b64encode(six.b(proxy_basic_auth)).decode('utf-8') - - return headers + b64encode(b(proxy_basic_auth)).decode('utf-8') + if disable_cache: + headers['cache-control'] = 'no-cache' + return headers diff --git a/pip/_vendor/requests/packages/urllib3/util/response.py b/pip/_vendor/requests/packages/urllib3/util/response.py index d0325bc6b5c..0b5c75c13c8 100644 --- a/pip/_vendor/requests/packages/urllib3/util/response.py +++ b/pip/_vendor/requests/packages/urllib3/util/response.py @@ -1,3 +1,9 @@ +from __future__ import absolute_import +from ..packages.six.moves import http_client as httplib + +from ..exceptions import HeaderParsingError + + def is_fp_closed(obj): """ Checks whether a given file-like object is closed. @@ -5,9 +11,64 @@ def is_fp_closed(obj): :param obj: The file-like object to check. """ - if hasattr(obj, 'fp'): - # Object is a container for another file-like object that gets released - # on exhaustion (e.g. HTTPResponse) + + try: + # Check via the official file-like-object way. + return obj.closed + except AttributeError: + pass + + try: + # Check if the object is a container for another file-like object that + # gets released on exhaustion (e.g. HTTPResponse). return obj.fp is None + except AttributeError: + pass + + raise ValueError("Unable to determine whether fp is closed.") + + +def assert_header_parsing(headers): + """ + Asserts whether all headers have been successfully parsed. + Extracts encountered errors from the result of parsing headers. + + Only works on Python 3. + + :param headers: Headers to verify. + :type headers: `httplib.HTTPMessage`. - return obj.closed + :raises urllib3.exceptions.HeaderParsingError: + If parsing errors are found. + """ + + # This will fail silently if we pass in the wrong kind of parameter. + # To make debugging easier add an explicit check. + if not isinstance(headers, httplib.HTTPMessage): + raise TypeError('expected httplib.Message, got {0}.'.format( + type(headers))) + + defects = getattr(headers, 'defects', None) + get_payload = getattr(headers, 'get_payload', None) + + unparsed_data = None + if get_payload: # Platform-specific: Python 3. + unparsed_data = get_payload() + + if defects or unparsed_data: + raise HeaderParsingError(defects=defects, unparsed_data=unparsed_data) + + +def is_response_to_head(response): + """ + Checks whether the request of a response has been a HEAD-request. + Handles the quirks of AppEngine. + + :param conn: + :type conn: :class:`httplib.HTTPResponse` + """ + # FIXME: Can we do this somehow without accessing private httplib _method? + method = response._method + if isinstance(method, int): # Platform-specific: Appengine + return method == 3 + return method.upper() == 'HEAD' diff --git a/pip/_vendor/requests/packages/urllib3/util/retry.py b/pip/_vendor/requests/packages/urllib3/util/retry.py new file mode 100644 index 00000000000..2d3aa20d0ae --- /dev/null +++ b/pip/_vendor/requests/packages/urllib3/util/retry.py @@ -0,0 +1,294 @@ +from __future__ import absolute_import +import time +import logging + +from ..exceptions import ( + ConnectTimeoutError, + MaxRetryError, + ProtocolError, + ReadTimeoutError, + ResponseError, +) +from ..packages import six + + +log = logging.getLogger(__name__) + + +class Retry(object): + """ Retry configuration. + + Each retry attempt will create a new Retry object with updated values, so + they can be safely reused. + + Retries can be defined as a default for a pool:: + + retries = Retry(connect=5, read=2, redirect=5) + http = PoolManager(retries=retries) + response = http.request('GET', 'http://example.com/') + + Or per-request (which overrides the default for the pool):: + + response = http.request('GET', 'http://example.com/', retries=Retry(10)) + + Retries can be disabled by passing ``False``:: + + response = http.request('GET', 'http://example.com/', retries=False) + + Errors will be wrapped in :class:`~urllib3.exceptions.MaxRetryError` unless + retries are disabled, in which case the causing exception will be raised. + + :param int total: + Total number of retries to allow. Takes precedence over other counts. + + Set to ``None`` to remove this constraint and fall back on other + counts. It's a good idea to set this to some sensibly-high value to + account for unexpected edge cases and avoid infinite retry loops. + + Set to ``0`` to fail on the first retry. + + Set to ``False`` to disable and imply ``raise_on_redirect=False``. + + :param int connect: + How many connection-related errors to retry on. + + These are errors raised before the request is sent to the remote server, + which we assume has not triggered the server to process the request. + + Set to ``0`` to fail on the first retry of this type. + + :param int read: + How many times to retry on read errors. + + These errors are raised after the request was sent to the server, so the + request may have side-effects. + + Set to ``0`` to fail on the first retry of this type. + + :param int redirect: + How many redirects to perform. Limit this to avoid infinite redirect + loops. + + A redirect is a HTTP response with a status code 301, 302, 303, 307 or + 308. + + Set to ``0`` to fail on the first retry of this type. + + Set to ``False`` to disable and imply ``raise_on_redirect=False``. + + :param iterable method_whitelist: + Set of uppercased HTTP method verbs that we should retry on. + + By default, we only retry on methods which are considered to be + indempotent (multiple requests with the same parameters end with the + same state). See :attr:`Retry.DEFAULT_METHOD_WHITELIST`. + + :param iterable status_forcelist: + A set of HTTP status codes that we should force a retry on. + + By default, this is disabled with ``None``. + + :param float backoff_factor: + A backoff factor to apply between attempts. urllib3 will sleep for:: + + {backoff factor} * (2 ^ ({number of total retries} - 1)) + + seconds. If the backoff_factor is 0.1, then :func:`.sleep` will sleep + for [0.1s, 0.2s, 0.4s, ...] between retries. It will never be longer + than :attr:`Retry.BACKOFF_MAX`. + + By default, backoff is disabled (set to 0). + + :param bool raise_on_redirect: Whether, if the number of redirects is + exhausted, to raise a MaxRetryError, or to return a response with a + response code in the 3xx range. + + :param bool raise_on_status: Similar meaning to ``raise_on_redirect``: + whether we should raise an exception, or return a response, + if status falls in ``status_forcelist`` range and retries have + been exhausted. + """ + + DEFAULT_METHOD_WHITELIST = frozenset([ + 'HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE']) + + #: Maximum backoff time. + BACKOFF_MAX = 120 + + def __init__(self, total=10, connect=None, read=None, redirect=None, + method_whitelist=DEFAULT_METHOD_WHITELIST, status_forcelist=None, + backoff_factor=0, raise_on_redirect=True, raise_on_status=True, + _observed_errors=0): + + self.total = total + self.connect = connect + self.read = read + + if redirect is False or total is False: + redirect = 0 + raise_on_redirect = False + + self.redirect = redirect + self.status_forcelist = status_forcelist or set() + self.method_whitelist = method_whitelist + self.backoff_factor = backoff_factor + self.raise_on_redirect = raise_on_redirect + self.raise_on_status = raise_on_status + self._observed_errors = _observed_errors # TODO: use .history instead? + + def new(self, **kw): + params = dict( + total=self.total, + connect=self.connect, read=self.read, redirect=self.redirect, + method_whitelist=self.method_whitelist, + status_forcelist=self.status_forcelist, + backoff_factor=self.backoff_factor, + raise_on_redirect=self.raise_on_redirect, + raise_on_status=self.raise_on_status, + _observed_errors=self._observed_errors, + ) + params.update(kw) + return type(self)(**params) + + @classmethod + def from_int(cls, retries, redirect=True, default=None): + """ Backwards-compatibility for the old retries format.""" + if retries is None: + retries = default if default is not None else cls.DEFAULT + + if isinstance(retries, Retry): + return retries + + redirect = bool(redirect) and None + new_retries = cls(retries, redirect=redirect) + log.debug("Converted retries value: %r -> %r", retries, new_retries) + return new_retries + + def get_backoff_time(self): + """ Formula for computing the current backoff + + :rtype: float + """ + if self._observed_errors <= 1: + return 0 + + backoff_value = self.backoff_factor * (2 ** (self._observed_errors - 1)) + return min(self.BACKOFF_MAX, backoff_value) + + def sleep(self): + """ Sleep between retry attempts using an exponential backoff. + + By default, the backoff factor is 0 and this method will return + immediately. + """ + backoff = self.get_backoff_time() + if backoff <= 0: + return + time.sleep(backoff) + + def _is_connection_error(self, err): + """ Errors when we're fairly sure that the server did not receive the + request, so it should be safe to retry. + """ + return isinstance(err, ConnectTimeoutError) + + def _is_read_error(self, err): + """ Errors that occur after the request has been started, so we should + assume that the server began processing it. + """ + return isinstance(err, (ReadTimeoutError, ProtocolError)) + + def is_forced_retry(self, method, status_code): + """ Is this method/status code retryable? (Based on method/codes whitelists) + """ + if self.method_whitelist and method.upper() not in self.method_whitelist: + return False + + return self.status_forcelist and status_code in self.status_forcelist + + def is_exhausted(self): + """ Are we out of retries? """ + retry_counts = (self.total, self.connect, self.read, self.redirect) + retry_counts = list(filter(None, retry_counts)) + if not retry_counts: + return False + + return min(retry_counts) < 0 + + def increment(self, method=None, url=None, response=None, error=None, + _pool=None, _stacktrace=None): + """ Return a new Retry object with incremented retry counters. + + :param response: A response object, or None, if the server did not + return a response. + :type response: :class:`~urllib3.response.HTTPResponse` + :param Exception error: An error encountered during the request, or + None if the response was received successfully. + + :return: A new ``Retry`` object. + """ + if self.total is False and error: + # Disabled, indicate to re-raise the error. + raise six.reraise(type(error), error, _stacktrace) + + total = self.total + if total is not None: + total -= 1 + + _observed_errors = self._observed_errors + connect = self.connect + read = self.read + redirect = self.redirect + cause = 'unknown' + + if error and self._is_connection_error(error): + # Connect retry? + if connect is False: + raise six.reraise(type(error), error, _stacktrace) + elif connect is not None: + connect -= 1 + _observed_errors += 1 + + elif error and self._is_read_error(error): + # Read retry? + if read is False: + raise six.reraise(type(error), error, _stacktrace) + elif read is not None: + read -= 1 + _observed_errors += 1 + + elif response and response.get_redirect_location(): + # Redirect retry? + if redirect is not None: + redirect -= 1 + cause = 'too many redirects' + + else: + # Incrementing because of a server error like a 500 in + # status_forcelist and a the given method is in the whitelist + _observed_errors += 1 + cause = ResponseError.GENERIC_ERROR + if response and response.status: + cause = ResponseError.SPECIFIC_ERROR.format( + status_code=response.status) + + new_retry = self.new( + total=total, + connect=connect, read=read, redirect=redirect, + _observed_errors=_observed_errors) + + if new_retry.is_exhausted(): + raise MaxRetryError(_pool, url, error or ResponseError(cause)) + + log.debug("Incremented Retry for (url='%s'): %r", url, new_retry) + + return new_retry + + def __repr__(self): + return ('{cls.__name__}(total={self.total}, connect={self.connect}, ' + 'read={self.read}, redirect={self.redirect})').format( + cls=type(self), self=self) + + +# For backwards compatibility (equivalent to pre-v1.9): +Retry.DEFAULT = Retry(3) diff --git a/pip/_vendor/requests/packages/urllib3/util/ssl_.py b/pip/_vendor/requests/packages/urllib3/util/ssl_.py index dee4b876297..e8d9e7d292a 100644 --- a/pip/_vendor/requests/packages/urllib3/util/ssl_.py +++ b/pip/_vendor/requests/packages/urllib3/util/ssl_.py @@ -1,21 +1,140 @@ +from __future__ import absolute_import +import errno +import warnings +import hmac + from binascii import hexlify, unhexlify -from hashlib import md5, sha1 +from hashlib import md5, sha1, sha256 -from ..exceptions import SSLError +from ..exceptions import SSLError, InsecurePlatformWarning, SNIMissingWarning -try: # Test for SSL features - SSLContext = None - HAS_SNI = False +SSLContext = None +HAS_SNI = False +create_default_context = None +IS_PYOPENSSL = False + +# Maps the length of a digest to a possible hash function producing this digest +HASHFUNC_MAP = { + 32: md5, + 40: sha1, + 64: sha256, +} + +def _const_compare_digest_backport(a, b): + """ + Compare two digests of equal length in constant time. + + The digests must be of type str/bytes. + Returns True if the digests match, and False otherwise. + """ + result = abs(len(a) - len(b)) + for l, r in zip(bytearray(a), bytearray(b)): + result |= l ^ r + return result == 0 + + +_const_compare_digest = getattr(hmac, 'compare_digest', + _const_compare_digest_backport) + + +try: # Test for SSL features import ssl from ssl import wrap_socket, CERT_NONE, PROTOCOL_SSLv23 - from ssl import SSLContext # Modern SSL? from ssl import HAS_SNI # Has SNI? except ImportError: pass +try: + from ssl import OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_COMPRESSION +except ImportError: + OP_NO_SSLv2, OP_NO_SSLv3 = 0x1000000, 0x2000000 + OP_NO_COMPRESSION = 0x20000 + +# A secure default. +# Sources for more information on TLS ciphers: +# +# - https://wiki.mozilla.org/Security/Server_Side_TLS +# - https://www.ssllabs.com/projects/best-practices/index.html +# - https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/ +# +# The general intent is: +# - Prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE), +# - prefer ECDHE over DHE for better performance, +# - prefer any AES-GCM over any AES-CBC for better performance and security, +# - use 3DES as fallback which is secure but slow, +# - disable NULL authentication, MD5 MACs and DSS for security reasons. +DEFAULT_CIPHERS = ( + 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:' + 'DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:!aNULL:' + '!eNULL:!MD5' +) + +try: + from ssl import SSLContext # Modern SSL? +except ImportError: + import sys + + class SSLContext(object): # Platform-specific: Python 2 & 3.1 + supports_set_ciphers = ((2, 7) <= sys.version_info < (3,) or + (3, 2) <= sys.version_info) + + def __init__(self, protocol_version): + self.protocol = protocol_version + # Use default values from a real SSLContext + self.check_hostname = False + self.verify_mode = ssl.CERT_NONE + self.ca_certs = None + self.options = 0 + self.certfile = None + self.keyfile = None + self.ciphers = None + + def load_cert_chain(self, certfile, keyfile): + self.certfile = certfile + self.keyfile = keyfile + + def load_verify_locations(self, cafile=None, capath=None): + self.ca_certs = cafile + + if capath is not None: + raise SSLError("CA directories not supported in older Pythons") + + def set_ciphers(self, cipher_suite): + if not self.supports_set_ciphers: + raise TypeError( + 'Your version of Python does not support setting ' + 'a custom cipher suite. Please upgrade to Python ' + '2.7, 3.2, or later if you need this functionality.' + ) + self.ciphers = cipher_suite + + def wrap_socket(self, socket, server_hostname=None, server_side=False): + warnings.warn( + 'A true SSLContext object is not available. This prevents ' + 'urllib3 from configuring SSL appropriately and may cause ' + 'certain SSL connections to fail. You can upgrade to a newer ' + 'version of Python to solve this. For more information, see ' + 'https://urllib3.readthedocs.org/en/latest/security.html' + '#insecureplatformwarning.', + InsecurePlatformWarning + ) + kwargs = { + 'keyfile': self.keyfile, + 'certfile': self.certfile, + 'ca_certs': self.ca_certs, + 'cert_reqs': self.verify_mode, + 'ssl_version': self.protocol, + 'server_side': server_side, + } + if self.supports_set_ciphers: # Platform-specific: Python 2.7+ + return wrap_socket(socket, ciphers=self.ciphers, **kwargs) + else: # Platform-specific: Python 2.6 + return wrap_socket(socket, **kwargs) + + def assert_fingerprint(cert, fingerprint): """ Checks if given fingerprint matches the supplied certificate. @@ -26,31 +145,21 @@ def assert_fingerprint(cert, fingerprint): Fingerprint as string of hexdigits, can be interspersed by colons. """ - # Maps the length of a digest to a possible hash function producing - # this digest. - hashfunc_map = { - 16: md5, - 20: sha1 - } - fingerprint = fingerprint.replace(':', '').lower() - - digest_length, rest = divmod(len(fingerprint), 2) - - if rest or digest_length not in hashfunc_map: - raise SSLError('Fingerprint is of invalid length.') + digest_length = len(fingerprint) + hashfunc = HASHFUNC_MAP.get(digest_length) + if not hashfunc: + raise SSLError( + 'Fingerprint of invalid length: {0}'.format(fingerprint)) # We need encode() here for py32; works on py2 and p33. fingerprint_bytes = unhexlify(fingerprint.encode()) - hashfunc = hashfunc_map[digest_length] - cert_digest = hashfunc(cert).digest() - if not cert_digest == fingerprint_bytes: + if not _const_compare_digest(cert_digest, fingerprint_bytes): raise SSLError('Fingerprints did not match. Expected "{0}", got "{1}".' - .format(hexlify(fingerprint_bytes), - hexlify(cert_digest))) + .format(fingerprint, hexlify(cert_digest))) def resolve_cert_reqs(candidate): @@ -92,42 +201,120 @@ def resolve_ssl_version(candidate): return candidate -if SSLContext is not None: # Python 3.2+ - def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, - ca_certs=None, server_hostname=None, - ssl_version=None): - """ - All arguments except `server_hostname` have the same meaning as for - :func:`ssl.wrap_socket` - - :param server_hostname: - Hostname of the expected certificate - """ - context = SSLContext(ssl_version) - context.verify_mode = cert_reqs - - # Disable TLS compression to migitate CRIME attack (issue #309) - OP_NO_COMPRESSION = 0x20000 - context.options |= OP_NO_COMPRESSION - - if ca_certs: - try: - context.load_verify_locations(ca_certs) - # Py32 raises IOError - # Py33 raises FileNotFoundError - except Exception as e: # Reraise as SSLError +def create_urllib3_context(ssl_version=None, cert_reqs=None, + options=None, ciphers=None): + """All arguments have the same meaning as ``ssl_wrap_socket``. + + By default, this function does a lot of the same work that + ``ssl.create_default_context`` does on Python 3.4+. It: + + - Disables SSLv2, SSLv3, and compression + - Sets a restricted set of server ciphers + + If you wish to enable SSLv3, you can do:: + + from urllib3.util import ssl_ + context = ssl_.create_urllib3_context() + context.options &= ~ssl_.OP_NO_SSLv3 + + You can do the same to enable compression (substituting ``COMPRESSION`` + for ``SSLv3`` in the last line above). + + :param ssl_version: + The desired protocol version to use. This will default to + PROTOCOL_SSLv23 which will negotiate the highest protocol that both + the server and your installation of OpenSSL support. + :param cert_reqs: + Whether to require the certificate verification. This defaults to + ``ssl.CERT_REQUIRED``. + :param options: + Specific OpenSSL options. These default to ``ssl.OP_NO_SSLv2``, + ``ssl.OP_NO_SSLv3``, ``ssl.OP_NO_COMPRESSION``. + :param ciphers: + Which cipher suites to allow the server to select. + :returns: + Constructed SSLContext object with specified options + :rtype: SSLContext + """ + context = SSLContext(ssl_version or ssl.PROTOCOL_SSLv23) + + # Setting the default here, as we may have no ssl module on import + cert_reqs = ssl.CERT_REQUIRED if cert_reqs is None else cert_reqs + + if options is None: + options = 0 + # SSLv2 is easily broken and is considered harmful and dangerous + options |= OP_NO_SSLv2 + # SSLv3 has several problems and is now dangerous + options |= OP_NO_SSLv3 + # Disable compression to prevent CRIME attacks for OpenSSL 1.0+ + # (issue #309) + options |= OP_NO_COMPRESSION + + context.options |= options + + if getattr(context, 'supports_set_ciphers', True): # Platform-specific: Python 2.6 + context.set_ciphers(ciphers or DEFAULT_CIPHERS) + + context.verify_mode = cert_reqs + if getattr(context, 'check_hostname', None) is not None: # Platform-specific: Python 3.2 + # We do our own verification, including fingerprints and alternative + # hostnames. So disable it here + context.check_hostname = False + return context + + +def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, + ca_certs=None, server_hostname=None, + ssl_version=None, ciphers=None, ssl_context=None, + ca_cert_dir=None): + """ + All arguments except for server_hostname, ssl_context, and ca_cert_dir have + the same meaning as they do when using :func:`ssl.wrap_socket`. + + :param server_hostname: + When SNI is supported, the expected hostname of the certificate + :param ssl_context: + A pre-made :class:`SSLContext` object. If none is provided, one will + be created using :func:`create_urllib3_context`. + :param ciphers: + A string of ciphers we wish the client to support. This is not + supported on Python 2.6 as the ssl module does not support it. + :param ca_cert_dir: + A directory containing CA certificates in multiple separate files, as + supported by OpenSSL's -CApath flag or the capath argument to + SSLContext.load_verify_locations(). + """ + context = ssl_context + if context is None: + context = create_urllib3_context(ssl_version, cert_reqs, + ciphers=ciphers) + + if ca_certs or ca_cert_dir: + try: + context.load_verify_locations(ca_certs, ca_cert_dir) + except IOError as e: # Platform-specific: Python 2.6, 2.7, 3.2 + raise SSLError(e) + # Py33 raises FileNotFoundError which subclasses OSError + # These are not equivalent unless we check the errno attribute + except OSError as e: # Platform-specific: Python 3.3 and beyond + if e.errno == errno.ENOENT: raise SSLError(e) - if certfile: - # FIXME: This block needs a test. - context.load_cert_chain(certfile, keyfile) - if HAS_SNI: # Platform-specific: OpenSSL with enabled SNI - return context.wrap_socket(sock, server_hostname=server_hostname) - return context.wrap_socket(sock) - -else: # Python 3.1 and earlier - def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, - ca_certs=None, server_hostname=None, - ssl_version=None): - return wrap_socket(sock, keyfile=keyfile, certfile=certfile, - ca_certs=ca_certs, cert_reqs=cert_reqs, - ssl_version=ssl_version) + raise + + if certfile: + context.load_cert_chain(certfile, keyfile) + if HAS_SNI: # Platform-specific: OpenSSL with enabled SNI + return context.wrap_socket(sock, server_hostname=server_hostname) + + warnings.warn( + 'An HTTPS request has been made, but the SNI (Subject Name ' + 'Indication) extension to TLS is not available on this platform. ' + 'This may cause the server to present an incorrect TLS ' + 'certificate, which can cause validation failures. You can upgrade to ' + 'a newer version of Python to solve this. For more information, see ' + 'https://urllib3.readthedocs.org/en/latest/security.html' + '#snimissingwarning.', + SNIMissingWarning + ) + return context.wrap_socket(sock) diff --git a/pip/_vendor/requests/packages/urllib3/util/timeout.py b/pip/_vendor/requests/packages/urllib3/util/timeout.py index 4f947cb249f..ff62f4764db 100644 --- a/pip/_vendor/requests/packages/urllib3/util/timeout.py +++ b/pip/_vendor/requests/packages/urllib3/util/timeout.py @@ -1,32 +1,51 @@ +from __future__ import absolute_import +# The default socket timeout, used by httplib to indicate that no timeout was +# specified by the user from socket import _GLOBAL_DEFAULT_TIMEOUT import time from ..exceptions import TimeoutStateError +# A sentinel value to indicate that no timeout was specified by the user in +# urllib3 +_Default = object() + def current_time(): """ - Retrieve the current time, this function is mocked out in unit testing. + Retrieve the current time. This function is mocked out in unit testing. """ return time.time() -_Default = object() -# The default timeout to use for socket connections. This is the attribute used -# by httplib to define the default timeout +class Timeout(object): + """ Timeout configuration. + Timeouts can be defined as a default for a pool:: -class Timeout(object): - """ - Utility object for storing timeout values. + timeout = Timeout(connect=2.0, read=7.0) + http = PoolManager(timeout=timeout) + response = http.request('GET', 'http://example.com/') + + Or per-request (which overrides the default for the pool):: + + response = http.request('GET', 'http://example.com/', timeout=Timeout(10)) - Example usage: + Timeouts can be disabled by setting all the parameters to ``None``:: + + no_timeout = Timeout(connect=None, read=None) + response = http.request('GET', 'http://example.com/, timeout=no_timeout) + + + :param total: + This combines the connect and read timeouts into one; the read timeout + will be set to the time leftover from the connect attempt. In the + event that both a connect timeout and a total are specified, or a read + timeout and a total are specified, the shorter timeout will be applied. - .. code-block:: python + Defaults to None. - timeout = urllib3.util.Timeout(connect=2.0, read=7.0) - pool = HTTPConnectionPool('www.google.com', 80, timeout=timeout) - pool.request(...) # Etc, etc + :type total: integer, float, or None :param connect: The maximum amount of time to wait for a connection attempt to a server @@ -47,25 +66,15 @@ class Timeout(object): :type read: integer, float, or None - :param total: - This combines the connect and read timeouts into one; the read timeout - will be set to the time leftover from the connect attempt. In the - event that both a connect timeout and a total are specified, or a read - timeout and a total are specified, the shorter timeout will be applied. - - Defaults to None. - - :type total: integer, float, or None - .. note:: Many factors can affect the total amount of time for urllib3 to return - an HTTP response. Specifically, Python's DNS resolver does not obey the - timeout specified on the socket. Other factors that can affect total - request time include high CPU load, high swap, the program running at a - low priority level, or other behaviors. The observed running time for - urllib3 to return a response may be greater than the value passed to - `total`. + an HTTP response. + + For example, Python's DNS resolver does not obey the timeout specified + on the socket. Other factors that can affect total request time include + high CPU load, high swap, the program running at a low priority level, + or other behaviors. In addition, the read and total timeouts only measure the time between read operations on the socket connecting the client and the server, @@ -73,8 +82,8 @@ class Timeout(object): response. For most requests, the timeout is raised because the server has not sent the first byte in the specified time. This is not always the case; if a server streams one byte every fifteen seconds, a timeout - of 20 seconds will not ever trigger, even though the request will - take several minutes to complete. + of 20 seconds will not trigger, even though the request will take + several minutes to complete. If your goal is to cut off any request after a set amount of wall clock time, consider having a second "watcher" thread to cut off a slow @@ -94,17 +103,16 @@ def __str__(self): return '%s(connect=%r, read=%r, total=%r)' % ( type(self).__name__, self._connect, self._read, self.total) - @classmethod def _validate_timeout(cls, value, name): - """ Check that a timeout attribute is valid + """ Check that a timeout attribute is valid. :param value: The timeout value to validate - :param name: The name of the timeout attribute to validate. This is used - for clear error messages - :return: the value - :raises ValueError: if the type is not an integer or a float, or if it - is a numeric value less than zero + :param name: The name of the timeout attribute to validate. This is + used to specify in error messages. + :return: The validated and casted version of the given value. + :raises ValueError: If the type is not an integer or a float, or if it + is a numeric value less than zero. """ if value is _Default: return cls.DEFAULT_TIMEOUT @@ -123,7 +131,7 @@ def _validate_timeout(cls, value, name): raise ValueError("Attempted to set %s timeout to %s, but the " "timeout cannot be set to a value less " "than 0." % (name, value)) - except TypeError: # Python 3 + except TypeError: # Python 3 raise ValueError("Timeout value %s was %s, but it must be an " "int or float." % (name, value)) @@ -135,12 +143,12 @@ def from_float(cls, timeout): The timeout value used by httplib.py sets the same timeout on the connect(), and recv() socket requests. This creates a :class:`Timeout` - object that sets the individual timeouts to the ``timeout`` value passed - to this function. + object that sets the individual timeouts to the ``timeout`` value + passed to this function. - :param timeout: The legacy timeout value + :param timeout: The legacy timeout value. :type timeout: integer, float, sentinel default object, or None - :return: a Timeout object + :return: Timeout object :rtype: :class:`Timeout` """ return Timeout(read=timeout, connect=timeout) @@ -174,7 +182,7 @@ def start_connect(self): def get_connect_duration(self): """ Gets the time elapsed since the call to :meth:`start_connect`. - :return: the elapsed time + :return: Elapsed time. :rtype: float :raises urllib3.exceptions.TimeoutStateError: if you attempt to get duration for a timer that hasn't been started. @@ -191,7 +199,7 @@ def connect_timeout(self): This will be a positive float or integer, the value None (never timeout), or the default system timeout. - :return: the connect timeout + :return: Connect timeout. :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None """ if self.total is None: @@ -214,16 +222,16 @@ def read_timeout(self): established, a :exc:`~urllib3.exceptions.TimeoutStateError` will be raised. - :return: the value to use for the read timeout + :return: Value to use for the read timeout. :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None :raises urllib3.exceptions.TimeoutStateError: If :meth:`start_connect` has not yet been called on this object. """ if (self.total is not None and - self.total is not self.DEFAULT_TIMEOUT and - self._read is not None and - self._read is not self.DEFAULT_TIMEOUT): - # in case the connect timeout has not yet been established. + self.total is not self.DEFAULT_TIMEOUT and + self._read is not None and + self._read is not self.DEFAULT_TIMEOUT): + # In case the connect timeout has not yet been established. if self._start_connect is None: return self._read return max(0, min(self.total - self.get_connect_duration(), diff --git a/pip/_vendor/requests/packages/urllib3/util/url.py b/pip/_vendor/requests/packages/urllib3/util/url.py index 362d2160894..e996204a07a 100644 --- a/pip/_vendor/requests/packages/urllib3/util/url.py +++ b/pip/_vendor/requests/packages/urllib3/util/url.py @@ -1,17 +1,25 @@ +from __future__ import absolute_import from collections import namedtuple from ..exceptions import LocationParseError -class Url(namedtuple('Url', ['scheme', 'auth', 'host', 'port', 'path', 'query', 'fragment'])): +url_attrs = ['scheme', 'auth', 'host', 'port', 'path', 'query', 'fragment'] + + +class Url(namedtuple('Url', url_attrs)): """ Datastructure for representing an HTTP URL. Used as a return value for :func:`parse_url`. """ slots = () - def __new__(cls, scheme=None, auth=None, host=None, port=None, path=None, query=None, fragment=None): - return super(Url, cls).__new__(cls, scheme, auth, host, port, path, query, fragment) + def __new__(cls, scheme=None, auth=None, host=None, port=None, path=None, + query=None, fragment=None): + if path and not path.startswith('/'): + path = '/' + path + return super(Url, cls).__new__(cls, scheme, auth, host, port, path, + query, fragment) @property def hostname(self): @@ -35,6 +43,49 @@ def netloc(self): return '%s:%d' % (self.host, self.port) return self.host + @property + def url(self): + """ + Convert self into a url + + This function should more or less round-trip with :func:`.parse_url`. The + returned url may not be exactly the same as the url inputted to + :func:`.parse_url`, but it should be equivalent by the RFC (e.g., urls + with a blank port will have : removed). + + Example: :: + + >>> U = parse_url('http://google.com/mail/') + >>> U.url + 'http://google.com/mail/' + >>> Url('http', 'username:password', 'host.com', 80, + ... '/path', 'query', 'fragment').url + 'http://username:password@host.com:80/path?query#fragment' + """ + scheme, auth, host, port, path, query, fragment = self + url = '' + + # We use "is not None" we want things to happen with empty strings (or 0 port) + if scheme is not None: + url += scheme + '://' + if auth is not None: + url += auth + '@' + if host is not None: + url += host + if port is not None: + url += ':' + str(port) + if path is not None: + url += path + if query is not None: + url += '?' + query + if fragment is not None: + url += '#' + fragment + + return url + + def __str__(self): + return self.url + def split_first(s, delims): """ @@ -43,7 +94,7 @@ def split_first(s, delims): If not found, then the first part is the full input string. - Example: :: + Example:: >>> split_first('foo/bar?baz', '?/=') ('foo', 'bar?baz', '/') @@ -66,7 +117,7 @@ def split_first(s, delims): if min_idx is None or min_idx < 0: return s, '', None - return s[:min_idx], s[min_idx+1:], min_delim + return s[:min_idx], s[min_idx + 1:], min_delim def parse_url(url): @@ -76,10 +127,10 @@ def parse_url(url): Partly backwards-compatible with :mod:`urlparse`. - Example: :: + Example:: >>> parse_url('http://google.com/mail/') - Url(scheme='http', host='google.com', port=None, path='/', ...) + Url(scheme='http', host='google.com', port=None, path='/mail/', ...) >>> parse_url('google.com:80') Url(scheme=None, host='google.com', port=80, path=None, ...) >>> parse_url('/foo?bar') @@ -91,6 +142,10 @@ def parse_url(url): # Additionally, this implementations does silly things to be optimal # on CPython. + if not url: + # Empty + return Url() + scheme = None auth = None host = None diff --git a/pip/_vendor/requests/sessions.py b/pip/_vendor/requests/sessions.py index df85a25c118..45be9733e56 100644 --- a/pip/_vendor/requests/sessions.py +++ b/pip/_vendor/requests/sessions.py @@ -13,7 +13,7 @@ from datetime import datetime from .auth import _basic_auth_str -from .compat import cookielib, OrderedDict, urljoin, urlparse, builtin_str +from .compat import cookielib, OrderedDict, urljoin, urlparse from .cookies import ( cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar, merge_cookies) from .models import Request, PreparedRequest, DEFAULT_REDIRECT_LIMIT @@ -21,6 +21,7 @@ from .utils import to_key_val_list, default_headers, to_native_string from .exceptions import ( TooManyRedirects, InvalidSchema, ChunkedEncodingError, ContentDecodingError) +from .packages.urllib3._collections import RecentlyUsedContainer from .structures import CaseInsensitiveDict from .adapters import HTTPAdapter @@ -35,6 +36,8 @@ # formerly defined here, reexposed here for backward compatibility from .models import REDIRECT_STATI +REDIRECT_CACHE_SIZE = 1000 + def merge_setting(request_setting, session_setting, dict_class=OrderedDict): """ @@ -59,12 +62,11 @@ def merge_setting(request_setting, session_setting, dict_class=OrderedDict): merged_setting = dict_class(to_key_val_list(session_setting)) merged_setting.update(to_key_val_list(request_setting)) - # Remove keys that are set to None. - for (k, v) in request_setting.items(): - if v is None: - del merged_setting[k] - - merged_setting = dict((k, v) for (k, v) in merged_setting.items() if v is not None) + # Remove keys that are set to None. Extract keys first to avoid altering + # the dictionary during iteration. + none_keys = [k for (k, v) in merged_setting.items() if v is None] + for key in none_keys: + del merged_setting[key] return merged_setting @@ -87,27 +89,33 @@ def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict): class SessionRedirectMixin(object): def resolve_redirects(self, resp, req, stream=False, timeout=None, - verify=True, cert=None, proxies=None): + verify=True, cert=None, proxies=None, **adapter_kwargs): """Receives a Response. Returns a generator of Responses.""" i = 0 + hist = [] # keep track of history while resp.is_redirect: prepared_request = req.copy() + if i > 0: + # Update history and keep track of redirects. + hist.append(resp) + new_hist = list(hist) + resp.history = new_hist + try: resp.content # Consume socket so it can be released except (ChunkedEncodingError, ContentDecodingError, RuntimeError): resp.raw.read(decode_content=False) if i >= self.max_redirects: - raise TooManyRedirects('Exceeded %s redirects.' % self.max_redirects) + raise TooManyRedirects('Exceeded %s redirects.' % self.max_redirects, response=resp) # Release the connection back into the pool. resp.close() url = resp.headers['location'] - method = req.method # Handle redirection without scheme (see: RFC 1808 Section 4) if url.startswith('//'): @@ -118,35 +126,23 @@ def resolve_redirects(self, resp, req, stream=False, timeout=None, parsed = urlparse(url) url = parsed.geturl() - # Facilitate non-RFC2616-compliant 'location' headers + # Facilitate relative 'location' headers, as allowed by RFC 7231. # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') # Compliant with RFC3986, we percent encode the url. - if not urlparse(url).netloc: + if not parsed.netloc: url = urljoin(resp.url, requote_uri(url)) else: url = requote_uri(url) prepared_request.url = to_native_string(url) + # Cache the url, unless it redirects to itself. + if resp.is_permanent_redirect and req.url != prepared_request.url: + self.redirect_cache[req.url] = prepared_request.url - # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4 - if (resp.status_code == codes.see_other and - method != 'HEAD'): - method = 'GET' - - # Do what the browsers do, despite standards... - # First, turn 302s into GETs. - if resp.status_code == codes.found and method != 'HEAD': - method = 'GET' - - # Second, if a POST is responded to with a 301, turn it into a GET. - # This bizarre behaviour is explained in Issue 1704. - if resp.status_code == codes.moved and method == 'POST': - method = 'GET' - - prepared_request.method = method + self.rebuild_method(prepared_request, resp) # https://github.com/kennethreitz/requests/issues/1084 - if resp.status_code not in (codes.temporary, codes.resume): + if resp.status_code not in (codes.temporary_redirect, codes.permanent_redirect): if 'Content-Length' in prepared_request.headers: del prepared_request.headers['Content-Length'] @@ -158,7 +154,10 @@ def resolve_redirects(self, resp, req, stream=False, timeout=None, except KeyError: pass - extract_cookies_to_jar(prepared_request._cookies, prepared_request, resp.raw) + # Extract any cookies sent on the response to the cookiejar + # in the new request. Because we've mutated our copied prepared + # request, use the old one that we haven't yet touched. + extract_cookies_to_jar(prepared_request._cookies, req, resp.raw) prepared_request._cookies.update(self.cookies) prepared_request.prepare_cookies(prepared_request._cookies) @@ -177,6 +176,7 @@ def resolve_redirects(self, resp, req, stream=False, timeout=None, cert=cert, proxies=proxies, allow_redirects=False, + **adapter_kwargs ) extract_cookies_to_jar(self.cookies, prepared_request, resp.raw) @@ -246,6 +246,28 @@ def rebuild_proxies(self, prepared_request, proxies): return new_proxies + def rebuild_method(self, prepared_request, response): + """When being redirected we may want to change the method of the request + based on certain specs or browser behavior. + """ + method = prepared_request.method + + # http://tools.ietf.org/html/rfc7231#section-6.4.4 + if response.status_code == codes.see_other and method != 'HEAD': + method = 'GET' + + # Do what the browsers do, despite standards... + # First, turn 302s into GETs. + if response.status_code == codes.found and method != 'HEAD': + method = 'GET' + + # Second, if a POST is responded to with a 301, turn it into a GET. + # This bizarre behaviour is explained in Issue 1704. + if response.status_code == codes.moved and method == 'POST': + method = 'GET' + + prepared_request.method = method + class Session(SessionRedirectMixin): """A Requests session. @@ -257,13 +279,20 @@ class Session(SessionRedirectMixin): >>> import requests >>> s = requests.Session() >>> s.get('http://httpbin.org/get') - 200 + + + Or as a context manager:: + + >>> with requests.Session() as s: + >>> s.get('http://httpbin.org/get') + """ __attrs__ = [ - 'headers', 'cookies', 'auth', 'timeout', 'proxies', 'hooks', - 'params', 'verify', 'cert', 'prefetch', 'adapters', 'stream', - 'trust_env', 'max_redirects'] + 'headers', 'cookies', 'auth', 'proxies', 'hooks', 'params', 'verify', + 'cert', 'prefetch', 'adapters', 'stream', 'trust_env', + 'max_redirects', + ] def __init__(self): @@ -276,9 +305,9 @@ def __init__(self): #: :class:`Request `. self.auth = None - #: Dictionary mapping protocol to the URL of the proxy (e.g. - #: {'http': 'foo.bar:3128'}) to be used on each - #: :class:`Request `. + #: Dictionary mapping protocol or protocol and host to the URL of the proxy + #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to + #: be used on each :class:`Request `. self.proxies = {} #: Event-handling hooks. @@ -302,7 +331,8 @@ def __init__(self): #: limit, a :class:`TooManyRedirects` exception is raised. self.max_redirects = DEFAULT_REDIRECT_LIMIT - #: Should we trust the environment? + #: Trust environment settings for proxy configuration, default + #: authentication and similar. self.trust_env = True #: A CookieJar containing all currently outstanding cookies set on this @@ -316,6 +346,9 @@ def __init__(self): self.mount('https://', HTTPAdapter()) self.mount('http://', HTTPAdapter()) + # Only store 1000 redirects to prevent using infinite memory + self.redirect_cache = RecentlyUsedContainer(REDIRECT_CACHE_SIZE) + def __enter__(self): return self @@ -353,6 +386,7 @@ def prepare_request(self, request): url=request.url, files=request.files, data=request.data, + json=request.json, headers=merge_setting(request.headers, self.headers, dict_class=CaseInsensitiveDict), params=merge_setting(request.params, self.params), auth=merge_setting(auth, self.auth), @@ -374,7 +408,8 @@ def request(self, method, url, hooks=None, stream=None, verify=None, - cert=None): + cert=None, + json=None): """Constructs a :class:`Request `, prepares it and sends it. Returns :class:`Response ` object. @@ -382,31 +417,34 @@ def request(self, method, url, :param url: URL for the new :class:`Request` object. :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`. - :param data: (optional) Dictionary or bytes to send in the body of the + :param data: (optional) Dictionary, bytes, or file-like object to send + in the body of the :class:`Request`. + :param json: (optional) json to send in the body of the :class:`Request`. :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`. - :param files: (optional) Dictionary of 'filename': file-like-objects + :param files: (optional) Dictionary of ``'filename': file-like-objects`` for multipart encoding upload. :param auth: (optional) Auth tuple or callable to enable Basic/Digest/Custom HTTP Auth. - :param timeout: (optional) Float describing the timeout of the - request in seconds. - :param allow_redirects: (optional) Boolean. Set to True by default. - :param proxies: (optional) Dictionary mapping protocol to the URL of - the proxy. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple + :param allow_redirects: (optional) Set to True by default. + :type allow_redirects: bool + :param proxies: (optional) Dictionary mapping protocol or protocol and + hostname to the URL of the proxy. :param stream: (optional) whether to immediately download the response content. Defaults to ``False``. - :param verify: (optional) if ``True``, the SSL cert will be verified. - A CA_BUNDLE path can also be provided. + :param verify: (optional) whether the SSL cert will be verified. + A CA_BUNDLE path can also be provided. Defaults to ``True``. :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. - """ - - method = builtin_str(method) - + :rtype: requests.Response + """ # Create the Request. req = Request( method = method.upper(), @@ -414,6 +452,7 @@ def request(self, method, url, headers = headers, files = files, data = data or {}, + json = json, params = params or {}, auth = auth, cookies = cookies, @@ -423,36 +462,16 @@ def request(self, method, url, proxies = proxies or {} - # Gather clues from the surrounding environment. - if self.trust_env: - # Set environment's proxies. - env_proxies = get_environ_proxies(url) or {} - for (k, v) in env_proxies.items(): - proxies.setdefault(k, v) - - # Look for configuration. - if not verify and verify is not False: - verify = os.environ.get('REQUESTS_CA_BUNDLE') - - # Curl compatibility. - if not verify and verify is not False: - verify = os.environ.get('CURL_CA_BUNDLE') - - # Merge all the kwargs. - proxies = merge_setting(proxies, self.proxies) - stream = merge_setting(stream, self.stream) - verify = merge_setting(verify, self.verify) - cert = merge_setting(cert, self.cert) + settings = self.merge_environment_settings( + prep.url, proxies, stream, verify, cert + ) # Send the request. send_kwargs = { - 'stream': stream, 'timeout': timeout, - 'verify': verify, - 'cert': cert, - 'proxies': proxies, 'allow_redirects': allow_redirects, } + send_kwargs.update(settings) resp = self.send(prep, **send_kwargs) return resp @@ -487,15 +506,16 @@ def head(self, url, **kwargs): kwargs.setdefault('allow_redirects', False) return self.request('HEAD', url, **kwargs) - def post(self, url, data=None, **kwargs): + def post(self, url, data=None, json=None, **kwargs): """Sends a POST request. Returns :class:`Response` object. :param url: URL for the new :class:`Request` object. :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. + :param json: (optional) json to send in the body of the :class:`Request`. :param \*\*kwargs: Optional arguments that ``request`` takes. """ - return self.request('POST', url, data=data, **kwargs) + return self.request('POST', url, data=data, json=json, **kwargs) def put(self, url, data=None, **kwargs): """Sends a PUT request. Returns :class:`Response` object. @@ -537,18 +557,24 @@ def send(self, request, **kwargs): # It's possible that users might accidentally send a Request object. # Guard against that specific failure case. - if not isinstance(request, PreparedRequest): + if isinstance(request, Request): raise ValueError('You can only send PreparedRequests.') # Set up variables needed for resolve_redirects and dispatching of hooks allow_redirects = kwargs.pop('allow_redirects', True) stream = kwargs.get('stream') - timeout = kwargs.get('timeout') - verify = kwargs.get('verify') - cert = kwargs.get('cert') - proxies = kwargs.get('proxies') hooks = request.hooks + # Resolve URL in redirect cache, if available. + if allow_redirects: + checked_urls = set() + while request.url in self.redirect_cache: + checked_urls.add(request.url) + new_url = self.redirect_cache.get(request.url) + if new_url in checked_urls: + break + request.url = new_url + # Get the appropriate adapter to use adapter = self.get_adapter(url=request.url) @@ -574,12 +600,7 @@ def send(self, request, **kwargs): extract_cookies_to_jar(self.cookies, request, r.raw) # Redirect resolving generator. - gen = self.resolve_redirects(r, request, - stream=stream, - timeout=timeout, - verify=verify, - cert=cert, - proxies=proxies) + gen = self.resolve_redirects(r, request, **kwargs) # Resolve redirects if allowed. history = [resp for resp in gen] if allow_redirects else [] @@ -597,8 +618,32 @@ def send(self, request, **kwargs): return r + def merge_environment_settings(self, url, proxies, stream, verify, cert): + """Check the environment and merge it with some settings.""" + # Gather clues from the surrounding environment. + if self.trust_env: + # Set environment's proxies. + env_proxies = get_environ_proxies(url) or {} + for (k, v) in env_proxies.items(): + proxies.setdefault(k, v) + + # Look for requests environment configuration and be compatible + # with cURL. + if verify is True or verify is None: + verify = (os.environ.get('REQUESTS_CA_BUNDLE') or + os.environ.get('CURL_CA_BUNDLE')) + + # Merge all the kwargs. + proxies = merge_setting(proxies, self.proxies) + stream = merge_setting(stream, self.stream) + verify = merge_setting(verify, self.verify) + cert = merge_setting(cert, self.cert) + + return {'verify': verify, 'proxies': proxies, 'stream': stream, + 'cert': cert} + def get_adapter(self, url): - """Returns the appropriate connnection adapter for the given URL.""" + """Returns the appropriate connection adapter for the given URL.""" for (prefix, adapter) in self.adapters.items(): if url.lower().startswith(prefix): @@ -624,12 +669,19 @@ def mount(self, prefix, adapter): self.adapters[key] = self.adapters.pop(key) def __getstate__(self): - return dict((attr, getattr(self, attr, None)) for attr in self.__attrs__) + state = dict((attr, getattr(self, attr, None)) for attr in self.__attrs__) + state['redirect_cache'] = dict(self.redirect_cache) + return state def __setstate__(self, state): + redirect_cache = state.pop('redirect_cache', {}) for attr, value in state.items(): setattr(self, attr, value) + self.redirect_cache = RecentlyUsedContainer(REDIRECT_CACHE_SIZE) + for redirect, to in redirect_cache.items(): + self.redirect_cache[redirect] = to + def session(): """Returns a :class:`Session` for context-management.""" diff --git a/pip/_vendor/requests/status_codes.py b/pip/_vendor/requests/status_codes.py index ed7a8660a6f..0137c91d961 100644 --- a/pip/_vendor/requests/status_codes.py +++ b/pip/_vendor/requests/status_codes.py @@ -30,7 +30,8 @@ 305: ('use_proxy',), 306: ('switch_proxy',), 307: ('temporary_redirect', 'temporary_moved', 'temporary'), - 308: ('resume_incomplete', 'resume'), + 308: ('permanent_redirect', + 'resume_incomplete', 'resume',), # These 2 to be removed in 3.0 # Client Error. 400: ('bad_request', 'bad'), @@ -52,6 +53,7 @@ 416: ('requested_range_not_satisfiable', 'requested_range', 'range_not_satisfiable'), 417: ('expectation_failed',), 418: ('im_a_teapot', 'teapot', 'i_am_a_teapot'), + 421: ('misdirected_request',), 422: ('unprocessable_entity', 'unprocessable'), 423: ('locked',), 424: ('failed_dependency', 'dependency'), @@ -77,11 +79,12 @@ 507: ('insufficient_storage',), 509: ('bandwidth_limit_exceeded', 'bandwidth'), 510: ('not_extended',), + 511: ('network_authentication_required', 'network_auth', 'network_authentication'), } codes = LookupDict(name='status_codes') -for (code, titles) in list(_codes.items()): +for code, titles in _codes.items(): for title in titles: setattr(codes, title, code) if not title.startswith('\\'): diff --git a/pip/_vendor/requests/structures.py b/pip/_vendor/requests/structures.py index 9fd78187f2b..991056e476e 100644 --- a/pip/_vendor/requests/structures.py +++ b/pip/_vendor/requests/structures.py @@ -8,30 +8,9 @@ """ -import os import collections -from itertools import islice - -class IteratorProxy(object): - """docstring for IteratorProxy""" - def __init__(self, i): - self.i = i - # self.i = chain.from_iterable(i) - - def __iter__(self): - return self.i - - def __len__(self): - if hasattr(self.i, '__len__'): - return len(self.i) - if hasattr(self.i, 'len'): - return self.i.len - if hasattr(self.i, 'fileno'): - return os.fstat(self.i.fileno()).st_size - - def read(self, n): - return "".join(islice(self.i, None, n)) +from .compat import OrderedDict class CaseInsensitiveDict(collections.MutableMapping): @@ -46,7 +25,7 @@ class CaseInsensitiveDict(collections.MutableMapping): case of the last key to be set, and ``iter(instance)``, ``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()`` will contain case-sensitive keys. However, querying and contains - testing is case insensitive: + testing is case insensitive:: cid = CaseInsensitiveDict() cid['Accept'] = 'application/json' @@ -63,7 +42,7 @@ class CaseInsensitiveDict(collections.MutableMapping): """ def __init__(self, data=None, **kwargs): - self._store = dict() + self._store = OrderedDict() if data is None: data = {} self.update(data, **kwargs) diff --git a/pip/_vendor/requests/utils.py b/pip/_vendor/requests/utils.py index 68e50cf0a96..c08448ccb1b 100644 --- a/pip/_vendor/requests/utils.py +++ b/pip/_vendor/requests/utils.py @@ -14,20 +14,20 @@ import collections import io import os -import platform import re -import sys import socket import struct +import warnings from . import __version__ from . import certs from .compat import parse_http_list as _parse_list_header from .compat import (quote, urlparse, bytes, str, OrderedDict, unquote, is_py2, - builtin_str, getproxies, proxy_bypass, urlunparse) + builtin_str, getproxies, proxy_bypass, urlunparse, + basestring) from .cookies import RequestsCookieJar, cookiejar_from_dict from .structures import CaseInsensitiveDict -from .exceptions import InvalidURL +from .exceptions import InvalidURL, FileModeWarning _hush_pyflakes = (RequestsCookieJar,) @@ -46,26 +46,54 @@ def dict_to_sequence(d): def super_len(o): + total_length = 0 + current_position = 0 + if hasattr(o, '__len__'): - return len(o) + total_length = len(o) + + elif hasattr(o, 'len'): + total_length = o.len - if hasattr(o, 'len'): - return o.len + elif hasattr(o, 'getvalue'): + # e.g. BytesIO, cStringIO.StringIO + total_length = len(o.getvalue()) - if hasattr(o, 'fileno'): + elif hasattr(o, 'fileno'): try: fileno = o.fileno() except io.UnsupportedOperation: pass else: - return os.fstat(fileno).st_size + total_length = os.fstat(fileno).st_size + + # Having used fstat to determine the file length, we need to + # confirm that this file was opened up in binary mode. + if 'b' not in o.mode: + warnings.warn(( + "Requests has determined the content-length for this " + "request using the binary size of the file: however, the " + "file has been opened in text mode (i.e. without the 'b' " + "flag in the mode). This may lead to an incorrect " + "content-length. In Requests 3.0, support will be removed " + "for files in text mode."), + FileModeWarning + ) + + if hasattr(o, 'tell'): + try: + current_position = o.tell() + except (OSError, IOError): + # This can happen in some weird situations, such as when the file + # is actually a special file descriptor like stdin. In this + # instance, we don't know what the length is, so set it to zero and + # let requests chunk it instead. + current_position = total_length - if hasattr(o, 'getvalue'): - # e.g. BytesIO, cStringIO.StringIO - return len(o.getvalue()) + return max(0, total_length - current_position) -def get_netrc_auth(url): +def get_netrc_auth(url, raise_errors=False): """Returns the Requests tuple auth for a given url from netrc.""" try: @@ -92,8 +120,12 @@ def get_netrc_auth(url): ri = urlparse(url) - # Strip port numbers from netloc - host = ri.netloc.split(':')[0] + # Strip port numbers from netloc. This weird `if...encode`` dance is + # used for Python 3.2, which doesn't support unicode literals. + splitstr = b':' + if isinstance(url, str): + splitstr = splitstr.decode('ascii') + host = ri.netloc.split(splitstr)[0] try: _netrc = netrc(netrc_path).authenticators(host) @@ -103,8 +135,9 @@ def get_netrc_auth(url): return (_netrc[login_i], _netrc[2]) except (NetrcParseError, IOError): # If there was a parsing error or a permissions issue reading the file, - # we'll just skip netrc auth - pass + # we'll just skip netrc auth unless explicitly asked to raise errors. + if raise_errors: + raise # AppEngine hackiness. except (ImportError, AttributeError): @@ -114,7 +147,8 @@ def get_netrc_auth(url): def guess_filename(obj): """Tries to guess the filename of the given object.""" name = getattr(obj, 'name', None) - if name and name[0] != '<' and name[-1] != '>': + if (name and isinstance(name, basestring) and name[0] != '<' and + name[-1] != '>'): return os.path.basename(name) @@ -287,6 +321,11 @@ def get_encodings_from_content(content): :param content: bytestring to extract encodings from. """ + warnings.warn(( + 'In requests 3.0, get_encodings_from_content will be removed. For ' + 'more information, please see the discussion on issue #2266. (This' + ' warning should only appear once.)'), + DeprecationWarning) charset_re = re.compile(r']', flags=re.I) pragma_re = re.compile(r']', flags=re.I) @@ -351,12 +390,14 @@ def get_unicode_from_response(r): Tried: 1. charset from content-type - - 2. every encodings from ```` - - 3. fall back and replace all unicode characters + 2. fall back and replace all unicode characters """ + warnings.warn(( + 'In requests 3.0, get_unicode_from_response will be removed. For ' + 'more information, please see the discussion on issue #2266. (This' + ' warning should only appear once.)'), + DeprecationWarning) tried_encodings = [] @@ -410,10 +451,18 @@ def requote_uri(uri): This function passes the given URI through an unquote/quote cycle to ensure that it is fully and consistently quoted. """ - # Unquote only the unreserved characters - # Then quote only illegal characters (do not quote reserved, unreserved, - # or '%') - return quote(unquote_unreserved(uri), safe="!#$%&'()*+,/:;=?@[]~") + safe_with_percent = "!#$%&'()*+,/:;=?@[]~" + safe_without_percent = "!#$&'()*+,/:;=?@[]~" + try: + # Unquote only the unreserved characters + # Then quote only illegal characters (do not quote reserved, + # unreserved, or '%') + return quote(unquote_unreserved(uri), safe=safe_with_percent) + except InvalidURL: + # We couldn't unquote the given URI, so let's try quoting it, but + # there may be unquoted '%'s in the URI. We need to make sure they're + # properly quoted so they do not cause issues elsewhere. + return quote(uri, safe=safe_without_percent) def address_in_network(ip, net): @@ -480,7 +529,9 @@ def should_bypass_proxies(url): if no_proxy: # We need to check whether we match here. We need to see if we match # the end of the netloc, both with and without the port. - no_proxy = no_proxy.replace(' ', '').split(',') + no_proxy = ( + host for host in no_proxy.replace(' ', '').split(',') if host + ) ip = netloc.split(':')[0] if is_ipv4_address(ip): @@ -511,6 +562,7 @@ def should_bypass_proxies(url): return False + def get_environ_proxies(url): """Return a dict of environment proxies.""" if should_bypass_proxies(url): @@ -519,42 +571,34 @@ def get_environ_proxies(url): return getproxies() -def default_user_agent(name="python-requests"): - """Return a string representing the default user agent.""" - _implementation = platform.python_implementation() - - if _implementation == 'CPython': - _implementation_version = platform.python_version() - elif _implementation == 'PyPy': - _implementation_version = '%s.%s.%s' % (sys.pypy_version_info.major, - sys.pypy_version_info.minor, - sys.pypy_version_info.micro) - if sys.pypy_version_info.releaselevel != 'final': - _implementation_version = ''.join([_implementation_version, sys.pypy_version_info.releaselevel]) - elif _implementation == 'Jython': - _implementation_version = platform.python_version() # Complete Guess - elif _implementation == 'IronPython': - _implementation_version = platform.python_version() # Complete Guess +def select_proxy(url, proxies): + """Select a proxy for the url, if applicable. + + :param url: The url being for the request + :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs + """ + proxies = proxies or {} + urlparts = urlparse(url) + if urlparts.hostname is None: + proxy = None else: - _implementation_version = 'Unknown' + proxy = proxies.get(urlparts.scheme+'://'+urlparts.hostname) + if proxy is None: + proxy = proxies.get(urlparts.scheme) + return proxy - try: - p_system = platform.system() - p_release = platform.release() - except IOError: - p_system = 'Unknown' - p_release = 'Unknown' - return " ".join(['%s/%s' % (name, __version__), - '%s/%s' % (_implementation, _implementation_version), - '%s/%s' % (p_system, p_release)]) +def default_user_agent(name="python-requests"): + """Return a string representing the default user agent.""" + return '%s/%s' % (name, __version__) def default_headers(): return CaseInsensitiveDict({ 'User-Agent': default_user_agent(), 'Accept-Encoding': ', '.join(('gzip', 'deflate')), - 'Accept': '*/*' + 'Accept': '*/*', + 'Connection': 'keep-alive', }) @@ -567,21 +611,19 @@ def parse_header_links(value): links = [] - replace_chars = " '\"" + replace_chars = ' \'"' - for val in value.split(","): + for val in re.split(', *<', value): try: - url, params = val.split(";", 1) + url, params = val.split(';', 1) except ValueError: url, params = val, '' - link = {} - - link["url"] = url.strip("<> '\"") + link = {'url': url.strip('<> \'"')} - for param in params.split(";"): + for param in params.split(';'): try: - key, value = param.split("=") + key, value = param.split('=') except ValueError: break @@ -628,8 +670,8 @@ def guess_json_utf(data): def prepend_scheme_if_needed(url, new_scheme): - '''Given a URL that may or may not have a scheme, prepend the given scheme. - Does not replace a present scheme with the one provided as an argument.''' + """Given a URL that may or may not have a scheme, prepend the given scheme. + Does not replace a present scheme with the one provided as an argument.""" scheme, netloc, path, params, query, fragment = urlparse(url, new_scheme) # urlparse is a finicky beast, and sometimes decides that there isn't a @@ -660,8 +702,6 @@ def to_native_string(string, encoding='ascii'): string in the native string type, encoding and decoding where necessary. This assumes ASCII unless told otherwise. """ - out = None - if isinstance(string, builtin_str): out = string else: @@ -671,3 +711,18 @@ def to_native_string(string, encoding='ascii'): out = string.decode(encoding) return out + + +def urldefragauth(url): + """ + Given a url remove the fragment and the authentication part + """ + scheme, netloc, path, params, query, fragment = urlparse(url) + + # see func:`prepend_scheme_if_needed` + if not netloc: + netloc, path = path, netloc + + netloc = netloc.rsplit('@', 1)[-1] + + return urlunparse((scheme, netloc, path, params, query, '')) diff --git a/pip/_vendor/retrying.py b/pip/_vendor/retrying.py new file mode 100644 index 00000000000..6d1e627aae8 --- /dev/null +++ b/pip/_vendor/retrying.py @@ -0,0 +1,267 @@ +## Copyright 2013-2014 Ray Holder +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. + +import random +from pip._vendor import six +import sys +import time +import traceback + + +# sys.maxint / 2, since Python 3.2 doesn't have a sys.maxint... +MAX_WAIT = 1073741823 + + +def retry(*dargs, **dkw): + """ + Decorator function that instantiates the Retrying object + @param *dargs: positional arguments passed to Retrying object + @param **dkw: keyword arguments passed to the Retrying object + """ + # support both @retry and @retry() as valid syntax + if len(dargs) == 1 and callable(dargs[0]): + def wrap_simple(f): + + @six.wraps(f) + def wrapped_f(*args, **kw): + return Retrying().call(f, *args, **kw) + + return wrapped_f + + return wrap_simple(dargs[0]) + + else: + def wrap(f): + + @six.wraps(f) + def wrapped_f(*args, **kw): + return Retrying(*dargs, **dkw).call(f, *args, **kw) + + return wrapped_f + + return wrap + + +class Retrying(object): + + def __init__(self, + stop=None, wait=None, + stop_max_attempt_number=None, + stop_max_delay=None, + wait_fixed=None, + wait_random_min=None, wait_random_max=None, + wait_incrementing_start=None, wait_incrementing_increment=None, + wait_exponential_multiplier=None, wait_exponential_max=None, + retry_on_exception=None, + retry_on_result=None, + wrap_exception=False, + stop_func=None, + wait_func=None, + wait_jitter_max=None): + + self._stop_max_attempt_number = 5 if stop_max_attempt_number is None else stop_max_attempt_number + self._stop_max_delay = 100 if stop_max_delay is None else stop_max_delay + self._wait_fixed = 1000 if wait_fixed is None else wait_fixed + self._wait_random_min = 0 if wait_random_min is None else wait_random_min + self._wait_random_max = 1000 if wait_random_max is None else wait_random_max + self._wait_incrementing_start = 0 if wait_incrementing_start is None else wait_incrementing_start + self._wait_incrementing_increment = 100 if wait_incrementing_increment is None else wait_incrementing_increment + self._wait_exponential_multiplier = 1 if wait_exponential_multiplier is None else wait_exponential_multiplier + self._wait_exponential_max = MAX_WAIT if wait_exponential_max is None else wait_exponential_max + self._wait_jitter_max = 0 if wait_jitter_max is None else wait_jitter_max + + # TODO add chaining of stop behaviors + # stop behavior + stop_funcs = [] + if stop_max_attempt_number is not None: + stop_funcs.append(self.stop_after_attempt) + + if stop_max_delay is not None: + stop_funcs.append(self.stop_after_delay) + + if stop_func is not None: + self.stop = stop_func + + elif stop is None: + self.stop = lambda attempts, delay: any(f(attempts, delay) for f in stop_funcs) + + else: + self.stop = getattr(self, stop) + + # TODO add chaining of wait behaviors + # wait behavior + wait_funcs = [lambda *args, **kwargs: 0] + if wait_fixed is not None: + wait_funcs.append(self.fixed_sleep) + + if wait_random_min is not None or wait_random_max is not None: + wait_funcs.append(self.random_sleep) + + if wait_incrementing_start is not None or wait_incrementing_increment is not None: + wait_funcs.append(self.incrementing_sleep) + + if wait_exponential_multiplier is not None or wait_exponential_max is not None: + wait_funcs.append(self.exponential_sleep) + + if wait_func is not None: + self.wait = wait_func + + elif wait is None: + self.wait = lambda attempts, delay: max(f(attempts, delay) for f in wait_funcs) + + else: + self.wait = getattr(self, wait) + + # retry on exception filter + if retry_on_exception is None: + self._retry_on_exception = self.always_reject + else: + self._retry_on_exception = retry_on_exception + + # TODO simplify retrying by Exception types + # retry on result filter + if retry_on_result is None: + self._retry_on_result = self.never_reject + else: + self._retry_on_result = retry_on_result + + self._wrap_exception = wrap_exception + + def stop_after_attempt(self, previous_attempt_number, delay_since_first_attempt_ms): + """Stop after the previous attempt >= stop_max_attempt_number.""" + return previous_attempt_number >= self._stop_max_attempt_number + + def stop_after_delay(self, previous_attempt_number, delay_since_first_attempt_ms): + """Stop after the time from the first attempt >= stop_max_delay.""" + return delay_since_first_attempt_ms >= self._stop_max_delay + + def no_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + """Don't sleep at all before retrying.""" + return 0 + + def fixed_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + """Sleep a fixed amount of time between each retry.""" + return self._wait_fixed + + def random_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + """Sleep a random amount of time between wait_random_min and wait_random_max""" + return random.randint(self._wait_random_min, self._wait_random_max) + + def incrementing_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + """ + Sleep an incremental amount of time after each attempt, starting at + wait_incrementing_start and incrementing by wait_incrementing_increment + """ + result = self._wait_incrementing_start + (self._wait_incrementing_increment * (previous_attempt_number - 1)) + if result < 0: + result = 0 + return result + + def exponential_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + exp = 2 ** previous_attempt_number + result = self._wait_exponential_multiplier * exp + if result > self._wait_exponential_max: + result = self._wait_exponential_max + if result < 0: + result = 0 + return result + + def never_reject(self, result): + return False + + def always_reject(self, result): + return True + + def should_reject(self, attempt): + reject = False + if attempt.has_exception: + reject |= self._retry_on_exception(attempt.value[1]) + else: + reject |= self._retry_on_result(attempt.value) + + return reject + + def call(self, fn, *args, **kwargs): + start_time = int(round(time.time() * 1000)) + attempt_number = 1 + while True: + try: + attempt = Attempt(fn(*args, **kwargs), attempt_number, False) + except: + tb = sys.exc_info() + attempt = Attempt(tb, attempt_number, True) + + if not self.should_reject(attempt): + return attempt.get(self._wrap_exception) + + delay_since_first_attempt_ms = int(round(time.time() * 1000)) - start_time + if self.stop(attempt_number, delay_since_first_attempt_ms): + if not self._wrap_exception and attempt.has_exception: + # get() on an attempt with an exception should cause it to be raised, but raise just in case + raise attempt.get() + else: + raise RetryError(attempt) + else: + sleep = self.wait(attempt_number, delay_since_first_attempt_ms) + if self._wait_jitter_max: + jitter = random.random() * self._wait_jitter_max + sleep = sleep + max(0, jitter) + time.sleep(sleep / 1000.0) + + attempt_number += 1 + + +class Attempt(object): + """ + An Attempt encapsulates a call to a target function that may end as a + normal return value from the function or an Exception depending on what + occurred during the execution. + """ + + def __init__(self, value, attempt_number, has_exception): + self.value = value + self.attempt_number = attempt_number + self.has_exception = has_exception + + def get(self, wrap_exception=False): + """ + Return the return value of this Attempt instance or raise an Exception. + If wrap_exception is true, this Attempt is wrapped inside of a + RetryError before being raised. + """ + if self.has_exception: + if wrap_exception: + raise RetryError(self) + else: + six.reraise(self.value[0], self.value[1], self.value[2]) + else: + return self.value + + def __repr__(self): + if self.has_exception: + return "Attempts: {0}, Error:\n{1}".format(self.attempt_number, "".join(traceback.format_tb(self.value[2]))) + else: + return "Attempts: {0}, Value: {1}".format(self.attempt_number, self.value) + + +class RetryError(Exception): + """ + A RetryError encapsulates the last Attempt instance right before giving up. + """ + + def __init__(self, last_attempt): + self.last_attempt = last_attempt + + def __str__(self): + return "RetryError[{0}]".format(self.last_attempt) diff --git a/pip/_vendor/six.py b/pip/_vendor/six.py index 019130f7ba0..190c0239cd7 100644 --- a/pip/_vendor/six.py +++ b/pip/_vendor/six.py @@ -1,6 +1,6 @@ """Utilities for writing code that runs on Python 2 and 3""" -# Copyright (c) 2010-2014 Benjamin Peterson +# Copyright (c) 2010-2015 Benjamin Peterson # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,17 +20,22 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import + +import functools +import itertools import operator import sys import types __author__ = "Benjamin Peterson " -__version__ = "1.6.1" +__version__ = "1.10.0" # Useful for very coarse version differentiation. PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) if PY3: string_types = str, @@ -53,6 +58,7 @@ else: # It's possible to have sizeof(long) != sizeof(Py_ssize_t). class X(object): + def __len__(self): return 1 << 31 try: @@ -83,14 +89,14 @@ def __init__(self, name): self.name = name def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. try: - result = self._resolve() - except ImportError: - # See the nice big comment in MovedModule.__getattr__. - raise AttributeError("%s could not be imported " % self.name) - setattr(obj, self.name, result) # Invokes __set__. - # This is a bit ugly, but it avoids running this again. - delattr(obj.__class__, self.name) + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass return result @@ -109,22 +115,7 @@ def _resolve(self): return _import_module(self.mod) def __getattr__(self, attr): - # It turns out many Python frameworks like to traverse sys.modules and - # try to load various attributes. This causes problems if this is a - # platform-specific module on the wrong platform, like _winreg on - # Unixes. Therefore, we silently pretend unimportable modules do not - # have any attributes. See issues #51, #53, #56, and #63 for the full - # tales of woe. - # - # First, if possible, avoid loading the module just to look at __file__, - # __name__, or __path__. - if (attr in ("__file__", "__name__", "__path__") and - self.mod not in sys.modules): - raise AttributeError(attr) - try: - _module = self._resolve() - except ImportError: - raise AttributeError(attr) + _module = self._resolve() value = getattr(_module, attr) setattr(self, attr, value) return value @@ -170,9 +161,75 @@ def _resolve(self): return getattr(module, self.attr) +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + +_importer = _SixMetaPathImporter(__name__) + class _MovedItems(_LazyModule): + """Lazy loading of moved objects""" + __path__ = [] # mark as package _moved_attributes = [ @@ -180,26 +237,33 @@ class _MovedItems(_LazyModule): MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "imp", "reload"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), MovedAttribute("UserString", "UserString", "collections"), MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - MovedModule("builtins", "__builtin__"), MovedModule("configparser", "ConfigParser"), MovedModule("copyreg", "copy_reg"), MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), MovedModule("http_cookies", "Cookie", "http.cookies"), MovedModule("html_entities", "htmlentitydefs", "html.entities"), MovedModule("html_parser", "HTMLParser", "html.parser"), MovedModule("http_client", "httplib", "http.client"), MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), @@ -233,21 +297,28 @@ class _MovedItems(_LazyModule): MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), - MovedModule("xmlrpc_server", "xmlrpclib", "xmlrpc.server"), - MovedModule("winreg", "_winreg"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), ] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + for attr in _moved_attributes: setattr(_MovedItems, attr.name, attr) if isinstance(attr, MovedModule): - sys.modules[__name__ + ".moves." + attr.name] = attr + _importer._add_module(attr, "moves." + attr.name) del attr _MovedItems._moved_attributes = _moved_attributes -moves = sys.modules[__name__ + ".moves"] = _MovedItems(__name__ + ".moves") +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") class Module_six_moves_urllib_parse(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_parse""" @@ -268,6 +339,13 @@ class Module_six_moves_urllib_parse(_LazyModule): MovedAttribute("unquote_plus", "urllib", "urllib.parse"), MovedAttribute("urlencode", "urllib", "urllib.parse"), MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), ] for attr in _urllib_parse_moved_attributes: setattr(Module_six_moves_urllib_parse, attr.name, attr) @@ -275,10 +353,12 @@ class Module_six_moves_urllib_parse(_LazyModule): Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes -sys.modules[__name__ + ".moves.urllib_parse"] = sys.modules[__name__ + ".moves.urllib.parse"] = Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse") +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") class Module_six_moves_urllib_error(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_error""" @@ -293,10 +373,12 @@ class Module_six_moves_urllib_error(_LazyModule): Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes -sys.modules[__name__ + ".moves.urllib_error"] = sys.modules[__name__ + ".moves.urllib.error"] = Module_six_moves_urllib_error(__name__ + ".moves.urllib.error") +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") class Module_six_moves_urllib_request(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_request""" @@ -341,10 +423,12 @@ class Module_six_moves_urllib_request(_LazyModule): Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes -sys.modules[__name__ + ".moves.urllib_request"] = sys.modules[__name__ + ".moves.urllib.request"] = Module_six_moves_urllib_request(__name__ + ".moves.urllib.request") +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") class Module_six_moves_urllib_response(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_response""" @@ -360,10 +444,12 @@ class Module_six_moves_urllib_response(_LazyModule): Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes -sys.modules[__name__ + ".moves.urllib_response"] = sys.modules[__name__ + ".moves.urllib.response"] = Module_six_moves_urllib_response(__name__ + ".moves.urllib.response") +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") class Module_six_moves_urllib_robotparser(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_robotparser""" @@ -376,22 +462,25 @@ class Module_six_moves_urllib_robotparser(_LazyModule): Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes -sys.modules[__name__ + ".moves.urllib_robotparser"] = sys.modules[__name__ + ".moves.urllib.robotparser"] = Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser") +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") class Module_six_moves_urllib(types.ModuleType): + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - parse = sys.modules[__name__ + ".moves.urllib_parse"] - error = sys.modules[__name__ + ".moves.urllib_error"] - request = sys.modules[__name__ + ".moves.urllib_request"] - response = sys.modules[__name__ + ".moves.urllib_response"] - robotparser = sys.modules[__name__ + ".moves.urllib_robotparser"] + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") def __dir__(self): return ['parse', 'error', 'request', 'response', 'robotparser'] - -sys.modules[__name__ + ".moves.urllib"] = Module_six_moves_urllib(__name__ + ".moves.urllib") +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") def add_move(move): @@ -418,11 +507,6 @@ def remove_move(name): _func_code = "__code__" _func_defaults = "__defaults__" _func_globals = "__globals__" - - _iterkeys = "keys" - _itervalues = "values" - _iteritems = "items" - _iterlists = "lists" else: _meth_func = "im_func" _meth_self = "im_self" @@ -432,11 +516,6 @@ def remove_move(name): _func_defaults = "func_defaults" _func_globals = "func_globals" - _iterkeys = "iterkeys" - _itervalues = "itervalues" - _iteritems = "iteritems" - _iterlists = "iterlists" - try: advance_iterator = next @@ -459,6 +538,9 @@ def get_unbound_function(unbound): create_bound_method = types.MethodType + def create_unbound_method(func, cls): + return func + Iterator = object else: def get_unbound_function(unbound): @@ -467,6 +549,9 @@ def get_unbound_function(unbound): def create_bound_method(func, obj): return types.MethodType(func, obj, obj.__class__) + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + class Iterator(object): def next(self): @@ -485,66 +570,117 @@ def next(self): get_function_globals = operator.attrgetter(_func_globals) -def iterkeys(d, **kw): - """Return an iterator over the keys of a dictionary.""" - return iter(getattr(d, _iterkeys)(**kw)) +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") -def itervalues(d, **kw): - """Return an iterator over the values of a dictionary.""" - return iter(getattr(d, _itervalues)(**kw)) + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") -def iteritems(d, **kw): - """Return an iterator over the (key, value) pairs of a dictionary.""" - return iter(getattr(d, _iteritems)(**kw)) + viewitems = operator.methodcaller("viewitems") -def iterlists(d, **kw): - """Return an iterator over the (key, [values]) pairs of a dictionary.""" - return iter(getattr(d, _iterlists)(**kw)) +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") if PY3: def b(s): return s.encode("latin-1") + def u(s): return s unichr = chr - if sys.version_info[1] <= 1: - def int2byte(i): - return bytes((i,)) - else: - # This is about 2x faster than the implementation above on 3.2+ - int2byte = operator.methodcaller("to_bytes", 1, "big") + import struct + int2byte = struct.Struct(">B").pack + del struct byte2int = operator.itemgetter(0) indexbytes = operator.getitem iterbytes = iter import io StringIO = io.StringIO BytesIO = io.BytesIO + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" else: def b(s): return s # Workaround for standalone backslash + def u(s): return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") unichr = unichr int2byte = chr + def byte2int(bs): return ord(bs[0]) + def indexbytes(buf, i): return ord(buf[i]) - def iterbytes(buf): - return (ord(byte) for byte in buf) + iterbytes = functools.partial(itertools.imap, ord) import StringIO StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" _add_doc(b, """Byte literal""") _add_doc(u, """Text literal""") +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + if PY3: exec_ = getattr(moves.builtins, "exec") - def reraise(tp, value, tb=None): + if value is None: + value = tp() if value.__traceback__ is not tb: raise value.with_traceback(tb) raise value @@ -562,12 +698,26 @@ def exec_(_code_, _globs_=None, _locs_=None): _locs_ = _globs_ exec("""exec _code_ in _globs_, _locs_""") - exec_("""def reraise(tp, value, tb=None): raise tp, value, tb """) +if sys.version_info[:2] == (3, 2): + exec_("""def raise_from(value, from_value): + if from_value is None: + raise value + raise value from from_value +""") +elif sys.version_info[:2] > (3, 2): + exec_("""def raise_from(value, from_value): + raise value from from_value +""") +else: + def raise_from(value, from_value): + raise value + + print_ = getattr(moves.builtins, "print", None) if print_ is None: def print_(*args, **kwargs): @@ -575,13 +725,14 @@ def print_(*args, **kwargs): fp = kwargs.pop("file", sys.stdout) if fp is None: return + def write(data): if not isinstance(data, basestring): data = str(data) # If the file has an encoding, encode unicode with it. if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): + isinstance(data, unicode) and + fp.encoding is not None): errors = getattr(fp, "errors", None) if errors is None: errors = "strict" @@ -622,25 +773,96 @@ def write(data): write(sep) write(arg) write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() _add_doc(reraise, """Reraise an exception.""") +if sys.version_info[0:2] < (3, 4): + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + def wrapper(f): + f = functools.wraps(wrapped, assigned, updated)(f) + f.__wrapped__ = wrapped + return f + return wrapper +else: + wraps = functools.wraps + def with_metaclass(meta, *bases): """Create a base class with a metaclass.""" - return meta("NewBase", bases, {}) + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) + def add_metaclass(metaclass): """Class decorator for creating a class with a metaclass.""" def wrapper(cls): orig_vars = cls.__dict__.copy() - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) slots = orig_vars.get('__slots__') if slots is not None: if isinstance(slots, str): slots = [slots] for slots_var in slots: orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) return metaclass(cls.__name__, cls.__bases__, orig_vars) return wrapper + + +def python_2_unicode_compatible(klass): + """ + A decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/pip/_vendor/vendor.txt b/pip/_vendor/vendor.txt index 9bd96cc1998..c2917c28889 100644 --- a/pip/_vendor/vendor.txt +++ b/pip/_vendor/vendor.txt @@ -1,5 +1,12 @@ -distlib==0.1.8 -html5lib==1.0b3 -six==1.6.1 -colorama==0.3.1 -requests==2.3.0 +distlib==0.2.3 +html5lib==1.0b8 +six==1.10.0 +colorama==0.3.7 +requests==2.10.0 +CacheControl==0.11.6 +lockfile==0.12.2 +progress==1.2 +ipaddress==1.0.16 # Only needed on 2.6 and 2.7 +packaging==16.7 +pyparsing==2.1.1 +retrying==1.3.3 diff --git a/pip/backwardcompat/__init__.py b/pip/backwardcompat/__init__.py deleted file mode 100644 index c327bbeec19..00000000000 --- a/pip/backwardcompat/__init__.py +++ /dev/null @@ -1,138 +0,0 @@ -"""Stuff that differs in different Python versions and platform -distributions.""" - -import os -import imp -import sys -import site - -__all__ = ['WindowsError'] - -uses_pycache = hasattr(imp, 'cache_from_source') - -class NeverUsedException(Exception): - """this exception should never be raised""" - -try: - WindowsError = WindowsError -except NameError: - WindowsError = NeverUsedException - -try: - #new in Python 3.3 - PermissionError = PermissionError -except NameError: - PermissionError = NeverUsedException - -console_encoding = sys.__stdout__.encoding - -if sys.version_info >= (3,): - from io import StringIO, BytesIO - from functools import reduce - from urllib.error import URLError, HTTPError - from queue import Queue, Empty - from urllib.request import url2pathname, urlretrieve, pathname2url - from email import message as emailmessage - import urllib.parse as urllib - import urllib.request as urllib2 - import configparser as ConfigParser - import xmlrpc.client as xmlrpclib - import urllib.parse as urlparse - import http.client as httplib - - def cmp(a, b): - return (a > b) - (a < b) - - def b(s): - return s.encode('utf-8') - - def u(s): - return s.decode('utf-8') - - def console_to_str(s): - try: - return s.decode(console_encoding) - except UnicodeDecodeError: - return s.decode('utf_8') - - def get_http_message_param(http_message, param, default_value): - return http_message.get_param(param, default_value) - - bytes = bytes - string_types = (str,) - raw_input = input -else: - from cStringIO import StringIO - from urllib2 import URLError, HTTPError - from Queue import Queue, Empty - from urllib import url2pathname, urlretrieve, pathname2url - from email import Message as emailmessage - import urllib - import urllib2 - import urlparse - import ConfigParser - import xmlrpclib - import httplib - - def b(s): - return s - - def u(s): - return s - - def console_to_str(s): - return s - - def get_http_message_param(http_message, param, default_value): - result = http_message.getparam(param) - return result or default_value - - bytes = str - string_types = (basestring,) - reduce = reduce - cmp = cmp - raw_input = raw_input - BytesIO = StringIO - - -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) - - -def product(*args, **kwds): - # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy - # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111 - 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] - for prod in result: - yield tuple(prod) - - -def get_path_uid(path): - """ - Return path's uid. - - Does not follow symlinks: https://github.com/pypa/pip/pull/935#discussion_r5307003 - - Placed this function in backwardcompat due to differences on AIX and Jython, - that should eventually go away. - - :raises OSError: When path is a symlink or can't be read. - """ - if hasattr(os, 'O_NOFOLLOW'): - fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW) - file_uid = os.fstat(fd).st_uid - os.close(fd) - else: # AIX and Jython - # WARNING: time of check vulnerabity, but best we can do w/o NOFOLLOW - if not os.path.islink(path): - # older versions of Jython don't have `os.fstat` - file_uid = os.stat(path).st_uid - else: - # raise OSError for parity with os.O_NOFOLLOW above - raise OSError("%s is a symlink; Will not return uid for symlinks" % path) - return file_uid diff --git a/pip/basecommand.py b/pip/basecommand.py index e4670191293..261d3999a54 100644 --- a/pip/basecommand.py +++ b/pip/basecommand.py @@ -1,34 +1,44 @@ """Base Command class, and related routines""" +from __future__ import absolute_import +import logging import os import sys -import tempfile -import traceback -import time import optparse +import warnings from pip import cmdoptions +from pip.index import PackageFinder from pip.locations import running_under_virtualenv -from pip.log import logger from pip.download import PipSession from pip.exceptions import (BadCommand, InstallationError, UninstallationError, CommandError, PreviousBuildDirError) -from pip.backwardcompat import StringIO + +from pip.compat import logging_dictConfig from pip.baseparser import ConfigOptionParser, UpdatingDefaultsHelpFormatter -from pip.status_codes import (SUCCESS, ERROR, UNKNOWN_ERROR, VIRTUALENV_NOT_FOUND, - PREVIOUS_BUILD_DIR_ERROR) -from pip.util import get_prog +from pip.req import InstallRequirement, parse_requirements +from pip.status_codes import ( + SUCCESS, ERROR, UNKNOWN_ERROR, VIRTUALENV_NOT_FOUND, + PREVIOUS_BUILD_DIR_ERROR, +) +from pip.utils import deprecation, get_prog, normalize_path +from pip.utils.logging import IndentingFormatter +from pip.utils.outdated import pip_version_check __all__ = ['Command'] +logger = logging.getLogger(__name__) + + class Command(object): name = None usage = None hidden = False + log_streams = ("ext://sys.stdout", "ext://sys.stderr") - def __init__(self): + def __init__(self, isolated=False): parser_kw = { 'usage': self.usage, 'prog': '%s %s' % (get_prog(), self.name), @@ -36,6 +46,7 @@ def __init__(self): 'add_help_option': False, 'name': self.name, 'description': self.__doc__, + 'isolated': isolated, } self.parser = ConfigOptionParser(**parser_kw) @@ -45,19 +56,35 @@ def __init__(self): self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name) # Add the general options - gen_opts = cmdoptions.make_option_group(cmdoptions.general_group, self.parser) + gen_opts = cmdoptions.make_option_group( + cmdoptions.general_group, + self.parser, + ) self.parser.add_option_group(gen_opts) - def _build_session(self, options): - session = PipSession() + def _build_session(self, options, retries=None, timeout=None): + session = PipSession( + cache=( + normalize_path(os.path.join(options.cache_dir, "http")) + if options.cache_dir else None + ), + retries=retries if retries is not None else options.retries, + insecure_hosts=options.trusted_hosts, + ) # Handle custom ca-bundles from the user if options.cert: session.verify = options.cert + # Handle SSL client certificate + if options.client_cert: + session.cert = options.client_cert + # Handle timeouts - if options.timeout: - session.timeout = options.timeout + if options.timeout or timeout: + session.timeout = ( + timeout if timeout is not None else options.timeout + ) # Handle configured proxies if options.proxy: @@ -71,9 +98,6 @@ def _build_session(self, options): return session - def setup_logging(self): - pass - def parse_args(self, args): # factored out for testability return self.parser.parse_args(args) @@ -81,21 +105,96 @@ def parse_args(self, args): def main(self, args): options, args = self.parse_args(args) - level = 1 # Notify - level += options.verbose - level -= options.quiet - level = logger.level_for_integer(4 - level) - complete_log = [] - logger.add_consumers( - (level, sys.stdout), - (logger.DEBUG, complete_log.append), - ) - if options.log_explicit_levels: - logger.explicit_levels = True - - self.setup_logging() + if options.quiet: + if options.quiet == 1: + level = "WARNING" + if options.quiet == 2: + level = "ERROR" + else: + level = "CRITICAL" + elif options.verbose: + level = "DEBUG" + else: + level = "INFO" - #TODO: try to get these passing down from the command? + # The root logger should match the "console" level *unless* we + # specified "--log" to send debug logs to a file. + root_level = level + if options.log: + root_level = "DEBUG" + + logging_dictConfig({ + "version": 1, + "disable_existing_loggers": False, + "filters": { + "exclude_warnings": { + "()": "pip.utils.logging.MaxLevelFilter", + "level": logging.WARNING, + }, + }, + "formatters": { + "indent": { + "()": IndentingFormatter, + "format": "%(message)s", + }, + }, + "handlers": { + "console": { + "level": level, + "class": "pip.utils.logging.ColorizedStreamHandler", + "stream": self.log_streams[0], + "filters": ["exclude_warnings"], + "formatter": "indent", + }, + "console_errors": { + "level": "WARNING", + "class": "pip.utils.logging.ColorizedStreamHandler", + "stream": self.log_streams[1], + "formatter": "indent", + }, + "user_log": { + "level": "DEBUG", + "class": "pip.utils.logging.BetterRotatingFileHandler", + "filename": options.log or "/dev/null", + "delay": True, + "formatter": "indent", + }, + }, + "root": { + "level": root_level, + "handlers": list(filter(None, [ + "console", + "console_errors", + "user_log" if options.log else None, + ])), + }, + # Disable any logging besides WARNING unless we have DEBUG level + # logging enabled. These use both pip._vendor and the bare names + # for the case where someone unbundles our libraries. + "loggers": dict( + ( + name, + { + "level": ( + "WARNING" + if level in ["INFO", "ERROR"] + else "DEBUG" + ), + }, + ) + for name in ["pip._vendor", "distlib", "requests", "urllib3"] + ), + }) + + if sys.version_info[:2] == (2, 6): + warnings.warn( + "Python 2.6 is no longer supported by the Python core team, " + "please upgrade your Python. A future version of pip will " + "drop support for Python 2.6", + deprecation.Python26DeprecationWarning + ) + + # TODO: try to get these passing down from the command? # without resorting to os.environ to hold these. if options.no_input: @@ -107,95 +206,126 @@ def main(self, args): if options.require_venv: # If a venv is required check if it can really be found if not running_under_virtualenv(): - logger.fatal('Could not find an activated virtualenv (required).') + logger.critical( + 'Could not find an activated virtualenv (required).' + ) sys.exit(VIRTUALENV_NOT_FOUND) - if options.log: - log_fp = open_logfile(options.log, 'a') - logger.add_consumers((logger.DEBUG, log_fp)) - else: - log_fp = None - - exit = SUCCESS - store_log = False try: status = self.run(options, args) # FIXME: all commands should return an exit status # and when it is done, isinstance is not needed anymore if isinstance(status, int): - exit = status - except PreviousBuildDirError: - e = sys.exc_info()[1] - logger.fatal(str(e)) - logger.info('Exception information:\n%s' % format_exc()) - store_log = True - exit = PREVIOUS_BUILD_DIR_ERROR - except (InstallationError, UninstallationError): - e = sys.exc_info()[1] - logger.fatal(str(e)) - logger.info('Exception information:\n%s' % format_exc()) - store_log = True - exit = ERROR - except BadCommand: - e = sys.exc_info()[1] - logger.fatal(str(e)) - logger.info('Exception information:\n%s' % format_exc()) - store_log = True - exit = ERROR - except CommandError: - e = sys.exc_info()[1] - logger.fatal('ERROR: %s' % e) - logger.info('Exception information:\n%s' % format_exc()) - exit = ERROR + return status + except PreviousBuildDirError as exc: + logger.critical(str(exc)) + logger.debug('Exception information:', exc_info=True) + + return PREVIOUS_BUILD_DIR_ERROR + except (InstallationError, UninstallationError, BadCommand) as exc: + logger.critical(str(exc)) + logger.debug('Exception information:', exc_info=True) + + return ERROR + except CommandError as exc: + logger.critical('ERROR: %s', exc) + logger.debug('Exception information:', exc_info=True) + + return ERROR except KeyboardInterrupt: - logger.fatal('Operation cancelled by user') - logger.info('Exception information:\n%s' % format_exc()) - store_log = True - exit = ERROR + logger.critical('Operation cancelled by user') + logger.debug('Exception information:', exc_info=True) + + return ERROR except: - logger.fatal('Exception:\n%s' % format_exc()) - store_log = True - exit = UNKNOWN_ERROR - if store_log: - log_file_fn = options.log_file - text = '\n'.join(complete_log) - try: - log_file_fp = open_logfile(log_file_fn, 'w') - except IOError: - temp = tempfile.NamedTemporaryFile(delete=False) - log_file_fn = temp.name - log_file_fp = open_logfile(log_file_fn, 'w') - logger.fatal('Storing debug log for failure in %s' % log_file_fn) - log_file_fp.write(text) - log_file_fp.close() - if log_fp is not None: - log_fp.close() - return exit - - -def format_exc(exc_info=None): - if exc_info is None: - exc_info = sys.exc_info() - out = StringIO() - traceback.print_exception(*exc_info, **dict(file=out)) - return out.getvalue() - - -def open_logfile(filename, mode='a'): - """Open the named log file in append mode. - - If the file already exists, a separator will also be printed to - the file to separate past activity from current activity. - """ - filename = os.path.expanduser(filename) - filename = os.path.abspath(filename) - dirname = os.path.dirname(filename) - if not os.path.exists(dirname): - os.makedirs(dirname) - exists = os.path.exists(filename) - - log_fp = open(filename, mode) - if exists: - log_fp.write('%s\n' % ('-' * 60)) - log_fp.write('%s run on %s\n' % (sys.argv[0], time.strftime('%c'))) - return log_fp + logger.critical('Exception:', exc_info=True) + + return UNKNOWN_ERROR + finally: + # Check if we're using the latest version of pip available + if (not options.disable_pip_version_check and not + getattr(options, "no_index", False)): + with self._build_session( + options, + retries=0, + timeout=min(5, options.timeout)) as session: + pip_version_check(session) + + return SUCCESS + + +class RequirementCommand(Command): + + @staticmethod + def populate_requirement_set(requirement_set, args, options, finder, + session, name, wheel_cache): + """ + Marshal cmd line args into a requirement set. + """ + for filename in options.constraints: + for req in parse_requirements( + filename, + constraint=True, finder=finder, options=options, + session=session, wheel_cache=wheel_cache): + requirement_set.add_requirement(req) + + for req in args: + requirement_set.add_requirement( + InstallRequirement.from_line( + req, None, isolated=options.isolated_mode, + wheel_cache=wheel_cache + ) + ) + + for req in options.editables: + requirement_set.add_requirement( + InstallRequirement.from_editable( + req, + default_vcs=options.default_vcs, + isolated=options.isolated_mode, + wheel_cache=wheel_cache + ) + ) + + found_req_in_file = False + for filename in options.requirements: + for req in parse_requirements( + filename, + finder=finder, options=options, session=session, + wheel_cache=wheel_cache): + found_req_in_file = True + requirement_set.add_requirement(req) + # If --require-hashes was a line in a requirements file, tell + # RequirementSet about it: + requirement_set.require_hashes = options.require_hashes + + if not (args or options.editables or found_req_in_file): + opts = {'name': name} + if options.find_links: + msg = ('You must give at least one requirement to ' + '%(name)s (maybe you meant "pip %(name)s ' + '%(links)s"?)' % + dict(opts, links=' '.join(options.find_links))) + else: + msg = ('You must give at least one requirement ' + 'to %(name)s (see "pip help %(name)s")' % opts) + logger.warning(msg) + + def _build_package_finder(self, options, session): + """ + Create a package finder appropriate to this requirement command. + """ + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index: + logger.debug('Ignoring indexes: %s', ','.join(index_urls)) + index_urls = [] + + return PackageFinder( + find_links=options.find_links, + format_control=options.format_control, + index_urls=index_urls, + trusted_hosts=options.trusted_hosts, + allow_all_prereleases=options.pre, + process_dependency_links=options.process_dependency_links, + session=session, + ) diff --git a/pip/baseparser.py b/pip/baseparser.py index dd39ed63206..9b53aca160a 100644 --- a/pip/baseparser.py +++ b/pip/baseparser.py @@ -1,15 +1,23 @@ """Base option parser setup""" +from __future__ import absolute_import import sys import optparse import os +import re import textwrap from distutils.util import strtobool -from pip.backwardcompat import ConfigParser, string_types -from pip.locations import default_config_file -from pip.util import get_terminal_size, get_prog -from pip._vendor import pkg_resources +from pip._vendor.six import string_types +from pip._vendor.six.moves import configparser +from pip.locations import ( + legacy_config_file, config_basename, running_under_virtualenv, + site_config_files +) +from pip.utils import appdirs, get_terminal_size + + +_environ_prefix_re = re.compile(r"^PIP_", re.I) class PrettyHelpFormatter(optparse.IndentedHelpFormatter): @@ -68,11 +76,11 @@ def format_description(self, description): label = 'Commands' else: label = 'Description' - #some doc strings have inital newlines, some don't + # some doc strings have initial newlines, some don't description = description.lstrip('\n') - #some doc strings have final newlines and spaces, some don't + # some doc strings have final newlines and spaces, some don't description = description.rstrip() - #dedent, then reindent + # dedent, then reindent description = self.indent_lines(textwrap.dedent(description), " ") description = '%s:\n%s\n' % (label, description) return description @@ -92,13 +100,15 @@ def indent_lines(self, text, indent): class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter): - """Custom help formatter for use in ConfigOptionParser that updates - the defaults before expanding them, allowing them to show up correctly - in the help listing""" + """Custom help formatter for use in ConfigOptionParser. + + This is updates the defaults before expanding them, allowing + them to show up correctly in the help listing. + """ def expand_default(self, option): if self.parser is not None: - self.parser.update_defaults(self.parser.defaults) + self.parser._update_defaults(self.parser.defaults) return optparse.IndentedHelpFormatter.expand_default(self, option) @@ -126,9 +136,12 @@ class ConfigOptionParser(CustomOptionParser): """Custom option parser which updates its defaults by checking the configuration files and environmental variables""" + isolated = False + def __init__(self, *args, **kwargs): - self.config = ConfigParser.RawConfigParser() + self.config = configparser.RawConfigParser() self.name = kwargs.pop('name') + self.isolated = kwargs.pop("isolated", False) self.files = self.get_config_files() if self.files: self.config.read(self.files) @@ -136,22 +149,53 @@ def __init__(self, *args, **kwargs): optparse.OptionParser.__init__(self, *args, **kwargs) def get_config_files(self): + # the files returned by this method will be parsed in order with the + # first files listed being overridden by later files in standard + # ConfigParser fashion config_file = os.environ.get('PIP_CONFIG_FILE', False) if config_file == os.devnull: return [] - if config_file and os.path.exists(config_file): - return [config_file] - return [default_config_file] + + # at the base we have any site-wide configuration + files = list(site_config_files) + + # per-user configuration next + if not self.isolated: + if config_file and os.path.exists(config_file): + files.append(config_file) + else: + # This is the legacy config file, we consider it to be a lower + # priority than the new file location. + files.append(legacy_config_file) + + # This is the new config file, we consider it to be a higher + # priority than the legacy file. + files.append( + os.path.join( + appdirs.user_config_dir("pip"), + config_basename, + ) + ) + + # finally virtualenv configuration first trumping others + if running_under_virtualenv(): + venv_config_file = os.path.join( + sys.prefix, + config_basename, + ) + if os.path.exists(venv_config_file): + files.append(venv_config_file) + + return files def check_default(self, option, key, val): try: return option.check_value(key, val) - except optparse.OptionValueError: - e = sys.exc_info()[1] - print("An error occurred during configuration: %s" % e) + except optparse.OptionValueError as exc: + print("An error occurred during configuration: %s" % exc) sys.exit(3) - def update_defaults(self, defaults): + def _update_defaults(self, defaults): """Updates the given defaults with values from the config files and the environ. Does a little special handling for certain types of options (lists).""" @@ -159,25 +203,49 @@ def update_defaults(self, defaults): config = {} # 1. config files for section in ('global', self.name): - config.update(self.normalize_keys(self.get_config_section(section))) + config.update( + self.normalize_keys(self.get_config_section(section)) + ) # 2. environmental variables - config.update(self.normalize_keys(self.get_environ_vars())) + if not self.isolated: + config.update(self.normalize_keys(self.get_environ_vars())) + # Accumulate complex default state. + self.values = optparse.Values(self.defaults) + late_eval = set() # Then set the options with those values for key, val in config.items(): + # ignore empty values + if not val: + continue + option = self.get_option(key) - if option is not None: - # ignore empty values - if not val: - continue - if option.action in ('store_true', 'store_false', 'count'): - val = strtobool(val) - if option.action == 'append': - val = val.split() - val = [self.check_default(option, key, v) for v in val] - else: - val = self.check_default(option, key, val) - - defaults[option.dest] = val + # Ignore options not present in this parser. E.g. non-globals put + # in [global] by users that want them to apply to all applicable + # commands. + if option is None: + continue + + if option.action in ('store_true', 'store_false', 'count'): + val = strtobool(val) + elif option.action == 'append': + val = val.split() + val = [self.check_default(option, key, v) for v in val] + elif option.action == 'callback': + late_eval.add(option.dest) + opt_str = option.get_opt_string() + val = option.convert_value(opt_str, val) + # From take_action + args = option.callback_args or () + kwargs = option.callback_kwargs or {} + option.callback(option, opt_str, val, self, *args, **kwargs) + else: + val = self.check_default(option, key, val) + + defaults[option.dest] = val + + for key in late_eval: + defaults[key] = getattr(self.values, key) + self.values = None return defaults def normalize_keys(self, items): @@ -198,20 +266,20 @@ def get_config_section(self, name): return self.config.items(name) return [] - def get_environ_vars(self, prefix='PIP_'): + def get_environ_vars(self): """Returns a generator with all environmental vars with prefix PIP_""" for key, val in os.environ.items(): - if key.startswith(prefix): - yield (key.replace(prefix, '').lower(), val) + if _environ_prefix_re.search(key): + yield (_environ_prefix_re.sub("", key).lower(), val) def get_default_values(self): - """Overridding to make updating the defaults after instantiation of - the option parser possible, update_defaults() does the dirty work.""" + """Overriding to make updating the defaults after instantiation of + the option parser possible, _update_defaults() does the dirty work.""" if not self.process_default_values: # Old, pre-Optik 1.5 behaviour. return optparse.Values(self.defaults) - defaults = self.update_defaults(self.defaults.copy()) # ours + defaults = self._update_defaults(self.defaults.copy()) # ours for option in self._get_all_options(): default = defaults.get(option.dest) if isinstance(default, string_types): diff --git a/pip/cmdoptions.py b/pip/cmdoptions.py index 8ed3d91fc6e..ecf13cf92fd 100644 --- a/pip/cmdoptions.py +++ b/pip/cmdoptions.py @@ -1,15 +1,24 @@ """ shared options and groups -The principle here is to define options once, but *not* instantiate them globally. -One reason being that options with action='append' can carry state between parses. -pip parse's general options twice internally, and shouldn't pass on state. -To be consistent, all options will follow this design. +The principle here is to define options once, but *not* instantiate them +globally. One reason being that options with action='append' can carry state +between parses. pip parses general options twice internally, and shouldn't +pass on state. To be consistent, all options will follow this design. """ -import copy +from __future__ import absolute_import + +from functools import partial from optparse import OptionGroup, SUPPRESS_HELP, Option -from pip.locations import build_prefix, default_log_file +import warnings + +from pip.index import ( + FormatControl, fmt_ctl_handle_mutual_exclude, fmt_ctl_no_binary, + fmt_ctl_no_use_wheel) +from pip.models import PyPI +from pip.locations import USER_CACHE_DIR, src_prefix +from pip.utils.hashes import STRONG_HASHES def make_option_group(group, parser): @@ -20,31 +29,62 @@ def make_option_group(group, parser): """ option_group = OptionGroup(parser, group['name']) for option in group['options']: - option_group.add_option(option.make()) + option_group.add_option(option()) return option_group -class OptionMaker(object): - """Class that stores the args/kwargs that would be used to make an Option, - for making them later, and uses deepcopy's to reset state.""" - def __init__(self, *args, **kwargs): - self.args = args - self.kwargs = kwargs - def make(self): - args_copy = copy.deepcopy(self.args) - kwargs_copy = copy.deepcopy(self.kwargs) - return Option(*args_copy, **kwargs_copy) + +def resolve_wheel_no_use_binary(options): + if not options.use_wheel: + control = options.format_control + fmt_ctl_no_use_wheel(control) + + +def check_install_build_global(options, check_options=None): + """Disable wheels if per-setup.py call options are set. + + :param options: The OptionParser options to update. + :param check_options: The options to check, if not supplied defaults to + options. + """ + if check_options is None: + check_options = options + + def getname(n): + return getattr(check_options, n, None) + names = ["build_options", "global_options", "install_options"] + if any(map(getname, names)): + control = options.format_control + fmt_ctl_no_binary(control) + warnings.warn( + 'Disabling all use of wheels due to the use of --build-options ' + '/ --global-options / --install-options.', stacklevel=2) + ########### # options # ########### -help_ = OptionMaker( +help_ = partial( + Option, '-h', '--help', dest='help', action='help', help='Show help.') -require_virtualenv = OptionMaker( +isolated_mode = partial( + Option, + "--isolated", + dest="isolated_mode", + action="store_true", + default=False, + help=( + "Run pip in an isolated mode, ignoring environment variables and user " + "configuration." + ), +) + +require_virtualenv = partial( + Option, # Run only if inside a virtualenv, bail if not. '--require-virtualenv', '--require-venv', dest='require_venv', @@ -52,49 +92,40 @@ def make(self): default=False, help=SUPPRESS_HELP) -verbose = OptionMaker( +verbose = partial( + Option, '-v', '--verbose', dest='verbose', action='count', default=0, - help='Give more output. Option is additive, and can be used up to 3 times.') + help='Give more output. Option is additive, and can be used up to 3 times.' +) -version = OptionMaker( +version = partial( + Option, '-V', '--version', dest='version', action='store_true', help='Show version and exit.') -quiet = OptionMaker( +quiet = partial( + Option, '-q', '--quiet', dest='quiet', action='count', default=0, help='Give less output.') -log = OptionMaker( - '--log', - dest='log', - metavar='path', - help='Path to a verbose appending log. This log is inactive by default.') - -log_explicit_levels = OptionMaker( - # Writes the log levels explicitely to the log' - '--log-explicit-levels', - dest='log_explicit_levels', - action='store_true', - default=False, - help=SUPPRESS_HELP) - -log_file = OptionMaker( - # The default log file - '--log-file', '--local-log', - dest='log_file', - metavar='path', - default=default_log_file, - help='Path to a verbose non-appending log, that only logs failures. This log is active by default at %default.') +log = partial( + Option, + "--log", "--log-file", "--local-log", + dest="log", + metavar="path", + help="Path to a verbose appending log." +) -no_input = OptionMaker( +no_input = partial( + Option, # Don't ask for input '--no-input', dest='no_input', @@ -102,14 +133,25 @@ def make(self): default=False, help=SUPPRESS_HELP) -proxy = OptionMaker( +proxy = partial( + Option, '--proxy', dest='proxy', type='str', default='', help="Specify a proxy in the form [user:passwd@]proxy.server:port.") -timeout = OptionMaker( +retries = partial( + Option, + '--retries', + dest='retries', + type='int', + default=5, + help="Maximum number of retries each connection should attempt " + "(default %default times).") + +timeout = partial( + Option, '--timeout', '--default-timeout', metavar='sec', dest='timeout', @@ -117,7 +159,8 @@ def make(self): default=15, help='Set the socket timeout (default %default seconds).') -default_vcs = OptionMaker( +default_vcs = partial( + Option, # The default version control system for editables, e.g. 'svn' '--default-vcs', dest='default_vcs', @@ -125,7 +168,8 @@ def make(self): default='', help=SUPPRESS_HELP) -skip_requirements_regex = OptionMaker( +skip_requirements_regex = partial( + Option, # A regex to be used to skip requirements '--skip-requirements-regex', dest='skip_requirements_regex', @@ -133,92 +177,121 @@ def make(self): default='', help=SUPPRESS_HELP) -exists_action = OptionMaker( - # Option when path already exist - '--exists-action', - dest='exists_action', - type='choice', - choices=['s', 'i', 'w', 'b'], - default=[], - action='append', - metavar='action', - help="Default action when a path already exists: " - "(s)witch, (i)gnore, (w)ipe, (b)ackup.") -cert = OptionMaker( +def exists_action(): + return Option( + # Option when path already exist + '--exists-action', + dest='exists_action', + type='choice', + choices=['s', 'i', 'w', 'b'], + default=[], + action='append', + metavar='action', + help="Default action when a path already exists: " + "(s)witch, (i)gnore, (w)ipe, (b)ackup.") + + +cert = partial( + Option, '--cert', dest='cert', type='str', - default='', metavar='path', - help = "Path to alternate CA bundle.") + help="Path to alternate CA bundle.") -index_url = OptionMaker( +client_cert = partial( + Option, + '--client-cert', + dest='client_cert', + type='str', + default=None, + metavar='path', + help="Path to SSL client certificate, a single file containing the " + "private key and the certificate in PEM format.") + +index_url = partial( + Option, '-i', '--index-url', '--pypi-url', dest='index_url', metavar='URL', - default='https://pypi.python.org/simple/', - help='Base URL of Python Package Index (default %default).') - -extra_index_url = OptionMaker( - '--extra-index-url', - dest='extra_index_urls', - metavar='URL', - action='append', - default=[], - help='Extra URLs of package indexes to use in addition to --index-url.') - -no_index = OptionMaker( + default=PyPI.simple_url, + help="Base URL of Python Package Index (default %default). " + "This should point to a repository compliant with PEP 503 " + "(the simple repository API) or a local directory laid out " + "in the same format.") + + +def extra_index_url(): + return Option( + '--extra-index-url', + dest='extra_index_urls', + metavar='URL', + action='append', + default=[], + help="Extra URLs of package indexes to use in addition to " + "--index-url. Should follow the same rules as " + "--index-url." + ) + + +no_index = partial( + Option, '--no-index', dest='no_index', action='store_true', default=False, help='Ignore package index (only looking at --find-links URLs instead).') -find_links = OptionMaker( - '-f', '--find-links', - dest='find_links', - action='append', - default=[], - metavar='url', - help="If a url or path to an html file, then parse for links to archives. If a local path or file:// url that's a directory, then look for archives in the directory listing.") - -# TODO: Remove after 1.6 -use_mirrors = OptionMaker( - '-M', '--use-mirrors', - dest='use_mirrors', - action='store_true', - default=False, - help=SUPPRESS_HELP) -# TODO: Remove after 1.6 -mirrors = OptionMaker( - '--mirrors', - dest='mirrors', - metavar='URL', - action='append', - default=[], - help=SUPPRESS_HELP) +def find_links(): + return Option( + '-f', '--find-links', + dest='find_links', + action='append', + default=[], + metavar='url', + help="If a url or path to an html file, then parse for links to " + "archives. If a local path or file:// url that's a directory, " + "then look for archives in the directory listing.") + + +def allow_external(): + return Option( + "--allow-external", + dest="allow_external", + action="append", + default=[], + metavar="PACKAGE", + help=SUPPRESS_HELP, + ) -allow_external = OptionMaker( - "--allow-external", - dest="allow_external", - action="append", - default=[], - metavar="PACKAGE", - help="Allow the installation of externally hosted files", -) -allow_all_external = OptionMaker( +allow_all_external = partial( + Option, "--allow-all-external", dest="allow_all_external", action="store_true", default=False, - help="Allow the installation of all externally hosted files", + help=SUPPRESS_HELP, ) -# Remove after 1.7 -no_allow_external = OptionMaker( + +def trusted_host(): + return Option( + "--trusted-host", + dest="trusted_hosts", + action="append", + metavar="HOSTNAME", + default=[], + help="Mark this host as trusted, even though it does not have valid " + "or any HTTPS.", + ) + + +# Remove after 7.0 +no_allow_external = partial( + Option, "--no-allow-external", dest="allow_all_external", action="store_false", @@ -226,18 +299,21 @@ def make(self): help=SUPPRESS_HELP, ) -# Remove --allow-insecure after 1.7 -allow_unsafe = OptionMaker( - "--allow-unverified", "--allow-insecure", - dest="allow_unverified", - action="append", - default=[], - metavar="PACKAGE", - help="Allow the installation of insecure and unverifiable files", -) -# Remove after 1.7 -no_allow_unsafe = OptionMaker( +# Remove --allow-insecure after 7.0 +def allow_unsafe(): + return Option( + "--allow-unverified", "--allow-insecure", + dest="allow_unverified", + action="append", + default=[], + metavar="PACKAGE", + help=SUPPRESS_HELP, + ) + +# Remove after 7.0 +no_allow_unsafe = partial( + Option, "--no-allow-insecure", dest="allow_all_insecure", action="store_false", @@ -246,7 +322,8 @@ def make(self): ) # Remove after 1.5 -process_dependency_links = OptionMaker( +process_dependency_links = partial( + Option, "--process-dependency-links", dest="process_dependency_links", action="store_true", @@ -254,78 +331,244 @@ def make(self): help="Enable the processing of dependency links.", ) -requirements = OptionMaker( - '-r', '--requirement', - dest='requirements', - action='append', - default=[], - metavar='file', - help='Install from the given requirements file. ' - 'This option can be used multiple times.') -use_wheel = OptionMaker( +def constraints(): + return Option( + '-c', '--constraint', + dest='constraints', + action='append', + default=[], + metavar='file', + help='Constrain versions using the given constraints file. ' + 'This option can be used multiple times.') + + +def requirements(): + return Option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='file', + help='Install from the given requirements file. ' + 'This option can be used multiple times.') + + +def editable(): + return Option( + '-e', '--editable', + dest='editables', + action='append', + default=[], + metavar='path/url', + help=('Install a project in editable mode (i.e. setuptools ' + '"develop mode") from a local project path or a VCS url.'), + ) + +src = partial( + Option, + '--src', '--source', '--source-dir', '--source-directory', + dest='src_dir', + metavar='dir', + default=src_prefix, + help='Directory to check out editable projects into. ' + 'The default in a virtualenv is "/src". ' + 'The default for global installs is "/src".' +) + +# XXX: deprecated, remove in 9.0 +use_wheel = partial( + Option, '--use-wheel', dest='use_wheel', action='store_true', + default=True, help=SUPPRESS_HELP, ) -no_use_wheel = OptionMaker( +# XXX: deprecated, remove in 9.0 +no_use_wheel = partial( + Option, '--no-use-wheel', dest='use_wheel', action='store_false', default=True, help=('Do not Find and prefer wheel archives when searching indexes and ' - 'find-links locations.'), + 'find-links locations. DEPRECATED in favour of --no-binary.'), ) -download_cache = OptionMaker( - '--download-cache', - dest='download_cache', - metavar='dir', - default=None, - help='Cache downloaded packages in .') -no_deps = OptionMaker( +def _get_format_control(values, option): + """Get a format_control object.""" + return getattr(values, option.dest) + + +def _handle_no_binary(option, opt_str, value, parser): + existing = getattr(parser.values, option.dest) + fmt_ctl_handle_mutual_exclude( + value, existing.no_binary, existing.only_binary) + + +def _handle_only_binary(option, opt_str, value, parser): + existing = getattr(parser.values, option.dest) + fmt_ctl_handle_mutual_exclude( + value, existing.only_binary, existing.no_binary) + + +def no_binary(): + return Option( + "--no-binary", dest="format_control", action="callback", + callback=_handle_no_binary, type="str", + default=FormatControl(set(), set()), + help="Do not use binary packages. Can be supplied multiple times, and " + "each time adds to the existing value. Accepts either :all: to " + "disable all binary packages, :none: to empty the set, or one or " + "more package names with commas between them. Note that some " + "packages are tricky to compile and may fail to install when " + "this option is used on them.") + + +def only_binary(): + return Option( + "--only-binary", dest="format_control", action="callback", + callback=_handle_only_binary, type="str", + default=FormatControl(set(), set()), + help="Do not use source packages. Can be supplied multiple times, and " + "each time adds to the existing value. Accepts either :all: to " + "disable all source packages, :none: to empty the set, or one or " + "more package names with commas between them. Packages without " + "binary distributions will fail to install when this option is " + "used on them.") + + +cache_dir = partial( + Option, + "--cache-dir", + dest="cache_dir", + default=USER_CACHE_DIR, + metavar="dir", + help="Store the cache data in ." +) + +no_cache = partial( + Option, + "--no-cache-dir", + dest="cache_dir", + action="store_false", + help="Disable the cache.", +) + +no_deps = partial( + Option, '--no-deps', '--no-dependencies', dest='ignore_dependencies', action='store_true', default=False, help="Don't install package dependencies.") -build_dir = OptionMaker( +build_dir = partial( + Option, '-b', '--build', '--build-dir', '--build-directory', dest='build_dir', metavar='dir', - default=build_prefix, - help='Directory to unpack packages into and build in. ' - 'The default in a virtualenv is "/build". ' - 'The default for global installs is "/pip_build_".') + help='Directory to unpack packages into and build in.' +) -install_options = OptionMaker( +install_options = partial( + Option, '--install-option', dest='install_options', action='append', metavar='options', help="Extra arguments to be supplied to the setup.py install " - "command (use like --install-option=\"--install-scripts=/usr/local/bin\"). " - "Use multiple --install-option options to pass multiple options to setup.py install. " - "If you are using an option with a directory path, be sure to use absolute path.") + "command (use like --install-option=\"--install-scripts=/usr/local/" + "bin\"). Use multiple --install-option options to pass multiple " + "options to setup.py install. If you are using an option with a " + "directory path, be sure to use absolute path.") -global_options = OptionMaker( +global_options = partial( + Option, '--global-option', dest='global_options', action='append', metavar='options', help="Extra global options to be supplied to the setup.py " - "call before the install command.") + "call before the install command.") -no_clean = OptionMaker( +no_clean = partial( + Option, '--no-clean', action='store_true', default=False, help="Don't clean up build directories.") +pre = partial( + Option, + '--pre', + action='store_true', + default=False, + help="Include pre-release and development versions. By default, " + "pip only finds stable versions.") + +disable_pip_version_check = partial( + Option, + "--disable-pip-version-check", + dest="disable_pip_version_check", + action="store_true", + default=False, + help="Don't periodically check PyPI to determine whether a new version " + "of pip is available for download. Implied with --no-index.") + +# Deprecated, Remove later +always_unzip = partial( + Option, + '-Z', '--always-unzip', + dest='always_unzip', + action='store_true', + help=SUPPRESS_HELP, +) + + +def _merge_hash(option, opt_str, value, parser): + """Given a value spelled "algo:digest", append the digest to a list + pointed to in a dict by the algo name.""" + if not parser.values.hashes: + parser.values.hashes = {} + try: + algo, digest = value.split(':', 1) + except ValueError: + parser.error('Arguments to %s must be a hash name ' + 'followed by a value, like --hash=sha256:abcde...' % + opt_str) + if algo not in STRONG_HASHES: + parser.error('Allowed hash algorithms for %s are %s.' % + (opt_str, ', '.join(STRONG_HASHES))) + parser.values.hashes.setdefault(algo, []).append(digest) + + +hash = partial( + Option, + '--hash', + # Hash values eventually end up in InstallRequirement.hashes due to + # __dict__ copying in process_line(). + dest='hashes', + action='callback', + callback=_merge_hash, + type='string', + help="Verify that the package's archive matches this " + 'hash before installing. Example: --hash=sha256:abcdef...') + + +require_hashes = partial( + Option, + '--require-hashes', + dest='require_hashes', + action='store_true', + default=False, + help='Require a hash to check each requirement against, for ' + 'repeatable installs. This option is implied when any package in a ' + 'requirements file has a --hash option.') + ########## # groups # @@ -335,37 +578,46 @@ def make(self): 'name': 'General Options', 'options': [ help_, + isolated_mode, require_virtualenv, verbose, version, quiet, - log_file, log, - log_explicit_levels, no_input, proxy, + retries, timeout, default_vcs, skip_requirements_regex, exists_action, + trusted_host, cert, - ] - } - -index_group = { + client_cert, + cache_dir, + no_cache, + disable_pip_version_check, + ] +} + +non_deprecated_index_group = { 'name': 'Package Index Options', 'options': [ index_url, extra_index_url, no_index, find_links, - use_mirrors, - mirrors, + process_dependency_links, + ] +} + +index_group = { + 'name': 'Package Index Options (including deprecated options)', + 'options': non_deprecated_index_group['options'] + [ allow_external, allow_all_external, no_allow_external, allow_unsafe, no_allow_unsafe, - process_dependency_links, - ] - } + ] +} diff --git a/pip/commands/__init__.py b/pip/commands/__init__.py index e0702d2700b..62c64ebed27 100644 --- a/pip/commands/__init__.py +++ b/pip/commands/__init__.py @@ -1,65 +1,63 @@ """ Package containing all pip commands """ +from __future__ import absolute_import - -from pip.commands.bundle import BundleCommand from pip.commands.completion import CompletionCommand +from pip.commands.download import DownloadCommand from pip.commands.freeze import FreezeCommand +from pip.commands.hash import HashCommand from pip.commands.help import HelpCommand from pip.commands.list import ListCommand +from pip.commands.check import CheckCommand from pip.commands.search import SearchCommand from pip.commands.show import ShowCommand from pip.commands.install import InstallCommand from pip.commands.uninstall import UninstallCommand -from pip.commands.unzip import UnzipCommand -from pip.commands.zip import ZipCommand from pip.commands.wheel import WheelCommand -commands = { - BundleCommand.name: BundleCommand, +commands_dict = { CompletionCommand.name: CompletionCommand, FreezeCommand.name: FreezeCommand, + HashCommand.name: HashCommand, HelpCommand.name: HelpCommand, SearchCommand.name: SearchCommand, ShowCommand.name: ShowCommand, InstallCommand.name: InstallCommand, UninstallCommand.name: UninstallCommand, - UnzipCommand.name: UnzipCommand, - ZipCommand.name: ZipCommand, + DownloadCommand.name: DownloadCommand, ListCommand.name: ListCommand, + CheckCommand.name: CheckCommand, WheelCommand.name: WheelCommand, } commands_order = [ InstallCommand, + DownloadCommand, UninstallCommand, FreezeCommand, ListCommand, ShowCommand, + CheckCommand, SearchCommand, WheelCommand, - ZipCommand, - UnzipCommand, - BundleCommand, + HashCommand, + CompletionCommand, HelpCommand, ] -def get_summaries(ignore_hidden=True, ordered=True): +def get_summaries(ordered=True): """Yields sorted (command name, command summary) tuples.""" if ordered: - cmditems = _sort_commands(commands, commands_order) + cmditems = _sort_commands(commands_dict, commands_order) else: - cmditems = commands.items() + cmditems = commands_dict.items() for name, command_class in cmditems: - if ignore_hidden and command_class.hidden: - continue - yield (name, command_class.summary) @@ -67,14 +65,14 @@ def get_similar_commands(name): """Command name auto-correct.""" from difflib import get_close_matches - close_commands = get_close_matches(name, commands.keys()) + name = name.lower() + + close_commands = get_close_matches(name, commands_dict.keys()) if close_commands: - guess = close_commands[0] + return close_commands[0] else: - guess = False - - return guess + return False def _sort_commands(cmddict, order): diff --git a/pip/commands/bundle.py b/pip/commands/bundle.py deleted file mode 100644 index 69967fe5148..00000000000 --- a/pip/commands/bundle.py +++ /dev/null @@ -1,42 +0,0 @@ -import textwrap -from pip.locations import build_prefix, src_prefix -from pip.util import display_path, backup_dir -from pip.log import logger -from pip.exceptions import InstallationError -from pip.commands.install import InstallCommand - - -class BundleCommand(InstallCommand): - """Create pybundles (archives containing multiple packages).""" - name = 'bundle' - usage = """ - %prog [options] .pybundle ...""" - summary = 'DEPRECATED. Create pybundles.' - bundle = True - - def __init__(self, *args, **kw): - super(BundleCommand, self).__init__(*args, **kw) - # bundle uses different default source and build dirs - build_opt = self.parser.get_option("--build") - build_opt.default = backup_dir(build_prefix, '-bundle') - src_opt = self.parser.get_option("--src") - src_opt.default = backup_dir(src_prefix, '-bundle') - self.parser.set_defaults(**{ - src_opt.dest: src_opt.default, - build_opt.dest: build_opt.default, - }) - - def run(self, options, args): - - logger.deprecated('1.6', "DEPRECATION: 'pip bundle' and support for installing from *.pybundle files is deprecated. " - "See https://github.com/pypa/pip/pull/1046") - - if not args: - raise InstallationError('You must give a bundle filename') - # We have to get everything when creating a bundle: - options.ignore_installed = True - logger.notify('Putting temporary build files in %s and source/develop files in %s' - % (display_path(options.build_dir), display_path(options.src_dir))) - self.bundle_filename = args.pop(0) - requirement_set = super(BundleCommand, self).run(options, args) - return requirement_set diff --git a/pip/commands/check.py b/pip/commands/check.py new file mode 100644 index 00000000000..eb7b1aa506f --- /dev/null +++ b/pip/commands/check.py @@ -0,0 +1,39 @@ +import logging + +from pip.basecommand import Command +from pip.operations.check import ( + check_requirements, get_installed_distributions) + + +logger = logging.getLogger(__name__) + + +class CheckCommand(Command): + """Verify installed packages have compatible dependencies.""" + name = 'check' + usage = """ + %prog [options]""" + summary = 'Verify installed packages have compatible dependencies.' + + def run(self, options, args): + installed = get_installed_distributions(skip=()) + missing_reqs_dict, incompatible_reqs_dict = check_requirements() + + for dist in installed: + key = '%s==%s' % (dist.project_name, dist.version) + + for requirement in missing_reqs_dict.get(key, []): + logger.info( + "%s %s requires %s, which is not installed.", + dist.project_name, dist.version, requirement.project_name) + + for requirement, actual in incompatible_reqs_dict.get(key, []): + logger.info( + "%s %s has requirement %s, but you have %s %s.", + dist.project_name, dist.version, requirement, + actual.project_name, actual.version) + + if missing_reqs_dict or incompatible_reqs_dict: + return 1 + else: + logger.info("No broken requirements found.") diff --git a/pip/commands/completion.py b/pip/commands/completion.py index 5fa23762064..dc80af33a88 100644 --- a/pip/commands/completion.py +++ b/pip/commands/completion.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + import sys from pip.basecommand import Command @@ -30,24 +32,28 @@ class CompletionCommand(Command): """A helper command to be used for command completion.""" name = 'completion' - summary = 'A helper command to be used for command completion' - hidden = True + summary = 'A helper command used for command completion' def __init__(self, *args, **kw): super(CompletionCommand, self).__init__(*args, **kw) - self.parser.add_option( + + cmd_opts = self.cmd_opts + + cmd_opts.add_option( '--bash', '-b', action='store_const', const='bash', dest='shell', help='Emit completion code for bash') - self.parser.add_option( + cmd_opts.add_option( '--zsh', '-z', action='store_const', const='zsh', dest='shell', help='Emit completion code for zsh') + self.parser.insert_option_group(0, cmd_opts) + def run(self, options, args): """Prints the completion code of the given shell""" shells = COMPLETION_SCRIPTS.keys() @@ -56,4 +62,6 @@ def run(self, options, args): script = COMPLETION_SCRIPTS.get(options.shell, '') print(BASE_COMPLETION % {'script': script, 'shell': options.shell}) else: - sys.stderr.write('ERROR: You must pass %s\n' % ' or '.join(shell_options)) + sys.stderr.write( + 'ERROR: You must pass %s\n' % ' or '.join(shell_options) + ) diff --git a/pip/commands/download.py b/pip/commands/download.py new file mode 100644 index 00000000000..4155e052c5a --- /dev/null +++ b/pip/commands/download.py @@ -0,0 +1,136 @@ +from __future__ import absolute_import + +import logging +import os + +from pip.req import RequirementSet +from pip.basecommand import RequirementCommand +from pip import cmdoptions +from pip.utils import ensure_dir, normalize_path +from pip.utils.build import BuildDirectory +from pip.utils.filesystem import check_path_owner + + +logger = logging.getLogger(__name__) + + +class DownloadCommand(RequirementCommand): + """ + Download packages from: + + - PyPI (and other indexes) using requirement specifiers. + - VCS project urls. + - Local project directories. + - Local or remote source archives. + + pip also supports downloading from "requirements files", which provide + an easy way to specify a whole environment to be downloaded. + """ + name = 'download' + + usage = """ + %prog [options] [package-index-options] ... + %prog [options] -r [package-index-options] ... + %prog [options] [-e] ... + %prog [options] [-e] ... + %prog [options] ...""" + + summary = 'Download packages.' + + def __init__(self, *args, **kw): + super(DownloadCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option(cmdoptions.constraints()) + cmd_opts.add_option(cmdoptions.editable()) + cmd_opts.add_option(cmdoptions.requirements()) + cmd_opts.add_option(cmdoptions.build_dir()) + cmd_opts.add_option(cmdoptions.no_deps()) + cmd_opts.add_option(cmdoptions.global_options()) + cmd_opts.add_option(cmdoptions.no_binary()) + cmd_opts.add_option(cmdoptions.only_binary()) + cmd_opts.add_option(cmdoptions.src()) + cmd_opts.add_option(cmdoptions.pre()) + cmd_opts.add_option(cmdoptions.no_clean()) + cmd_opts.add_option(cmdoptions.require_hashes()) + + cmd_opts.add_option( + '-d', '--dest', '--destination-dir', '--destination-directory', + dest='download_dir', + metavar='dir', + default=os.curdir, + help=("Download packages into ."), + ) + + index_opts = cmdoptions.make_option_group( + cmdoptions.non_deprecated_index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, cmd_opts) + + def run(self, options, args): + options.ignore_installed = True + options.src_dir = os.path.abspath(options.src_dir) + options.download_dir = normalize_path(options.download_dir) + + ensure_dir(options.download_dir) + + with self._build_session(options) as session: + + finder = self._build_package_finder(options, session) + build_delete = (not (options.no_clean or options.build_dir)) + if options.cache_dir and not check_path_owner(options.cache_dir): + logger.warning( + "The directory '%s' or its parent directory is not owned " + "by the current user and caching wheels has been " + "disabled. check the permissions and owner of that " + "directory. If executing pip with sudo, you may want " + "sudo's -H flag.", + options.cache_dir, + ) + options.cache_dir = None + + with BuildDirectory(options.build_dir, + delete=build_delete) as build_dir: + + requirement_set = RequirementSet( + build_dir=build_dir, + src_dir=options.src_dir, + download_dir=options.download_dir, + ignore_installed=True, + ignore_dependencies=options.ignore_dependencies, + session=session, + isolated=options.isolated_mode, + require_hashes=options.require_hashes + ) + self.populate_requirement_set( + requirement_set, + args, + options, + finder, + session, + self.name, + None + ) + + if not requirement_set.has_requirements: + return + + requirement_set.prepare_files(finder) + + downloaded = ' '.join([ + req.name for req in requirement_set.successfully_downloaded + ]) + if downloaded: + logger.info( + 'Successfully downloaded %s', downloaded + ) + + # Clean up + if not options.no_clean: + requirement_set.cleanup_files() + + return requirement_set diff --git a/pip/commands/freeze.py b/pip/commands/freeze.py index 930de62a547..c1987961985 100644 --- a/pip/commands/freeze.py +++ b/pip/commands/freeze.py @@ -1,114 +1,87 @@ -import re +from __future__ import absolute_import + import sys -import pip -from pip.req import InstallRequirement -from pip.log import logger +import pip +from pip.compat import stdlib_pkgs from pip.basecommand import Command -from pip.util import get_installed_distributions -from pip._vendor import pkg_resources +from pip.operations.freeze import freeze +from pip.wheel import WheelCache + + +DEV_PKGS = ('pip', 'setuptools', 'distribute', 'wheel') class FreezeCommand(Command): - """Output installed packages in requirements format.""" + """ + Output installed packages in requirements format. + + packages are listed in a case-insensitive sorted order. + """ name = 'freeze' usage = """ %prog [options]""" summary = 'Output installed packages in requirements format.' + log_streams = ("ext://sys.stderr", "ext://sys.stderr") def __init__(self, *args, **kw): super(FreezeCommand, self).__init__(*args, **kw) self.cmd_opts.add_option( '-r', '--requirement', - dest='requirement', - action='store', - default=None, + dest='requirements', + action='append', + default=[], metavar='file', - help="Use the order in the given requirements file and it's comments when generating output.") + help="Use the order in the given requirements file and its " + "comments when generating output. This option can be " + "used multiple times.") self.cmd_opts.add_option( '-f', '--find-links', dest='find_links', action='append', default=[], metavar='URL', - help='URL for finding packages, which will be added to the output.') + help='URL for finding packages, which will be added to the ' + 'output.') self.cmd_opts.add_option( '-l', '--local', dest='local', action='store_true', default=False, - help='If in a virtualenv that has global access, do not output globally-installed packages.') + help='If in a virtualenv that has global access, do not output ' + 'globally-installed packages.') + self.cmd_opts.add_option( + '--user', + dest='user', + action='store_true', + default=False, + help='Only output packages installed in user-site.') + self.cmd_opts.add_option( + '--all', + dest='freeze_all', + action='store_true', + help='Do not skip these packages in the output:' + ' %s' % ', '.join(DEV_PKGS)) self.parser.insert_option_group(0, self.cmd_opts) - def setup_logging(self): - logger.move_stdout_to_stderr() - def run(self, options, args): - requirement = options.requirement - find_links = options.find_links or [] - local_only = options.local - ## FIXME: Obviously this should be settable: - find_tags = False - skip_match = None - - skip_regex = options.skip_requirements_regex - if skip_regex: - skip_match = re.compile(skip_regex) - - dependency_links = [] + format_control = pip.index.FormatControl(set(), set()) + wheel_cache = WheelCache(options.cache_dir, format_control) + skip = set(stdlib_pkgs) + if not options.freeze_all: + skip.update(DEV_PKGS) - f = sys.stdout + freeze_kwargs = dict( + requirement=options.requirements, + find_links=options.find_links, + local_only=options.local, + user_only=options.user, + skip_regex=options.skip_requirements_regex, + isolated=options.isolated_mode, + wheel_cache=wheel_cache, + skip=skip) - for dist in pkg_resources.working_set: - if dist.has_metadata('dependency_links.txt'): - dependency_links.extend(dist.get_metadata_lines('dependency_links.txt')) - for link in find_links: - if '#egg=' in link: - dependency_links.append(link) - for link in find_links: - f.write('-f %s\n' % link) - installations = {} - for dist in get_installed_distributions(local_only=local_only): - req = pip.FrozenRequirement.from_dist(dist, dependency_links, find_tags=find_tags) - installations[req.name] = req - if requirement: - req_f = open(requirement) - for line in req_f: - if not line.strip() or line.strip().startswith('#'): - f.write(line) - continue - if skip_match and skip_match.search(line): - f.write(line) - continue - elif line.startswith('-e') or line.startswith('--editable'): - if line.startswith('-e'): - line = line[2:].strip() - else: - line = line[len('--editable'):].strip().lstrip('=') - line_req = InstallRequirement.from_editable(line, default_vcs=options.default_vcs) - elif (line.startswith('-r') or line.startswith('--requirement') - or line.startswith('-Z') or line.startswith('--always-unzip') - or line.startswith('-f') or line.startswith('-i') - or line.startswith('--extra-index-url') - or line.startswith('--find-links') - or line.startswith('--index-url')): - f.write(line) - continue - else: - line_req = InstallRequirement.from_line(line) - if not line_req.name: - logger.notify("Skipping line because it's not clear what it would install: %s" - % line.strip()) - logger.notify(" (add #egg=PackageName to the URL to avoid this warning)") - continue - if line_req.name not in installations: - logger.warn("Requirement file contains %s, but that package is not installed" - % line.strip()) - continue - f.write(str(installations[line_req.name])) - del installations[line_req.name] - f.write('## The following requirements were added by pip --freeze:\n') - for installation in sorted(installations.values(), key=lambda x: x.name): - f.write(str(installation)) + for line in freeze(**freeze_kwargs): + sys.stdout.write(line + '\n') diff --git a/pip/commands/hash.py b/pip/commands/hash.py new file mode 100644 index 00000000000..27cca0bfa40 --- /dev/null +++ b/pip/commands/hash.py @@ -0,0 +1,57 @@ +from __future__ import absolute_import + +import hashlib +import logging +import sys + +from pip.basecommand import Command +from pip.status_codes import ERROR +from pip.utils import read_chunks +from pip.utils.hashes import FAVORITE_HASH, STRONG_HASHES + + +logger = logging.getLogger(__name__) + + +class HashCommand(Command): + """ + Compute a hash of a local package archive. + + These can be used with --hash in a requirements file to do repeatable + installs. + + """ + name = 'hash' + usage = '%prog [options] ...' + summary = 'Compute hashes of package archives.' + + def __init__(self, *args, **kw): + super(HashCommand, self).__init__(*args, **kw) + self.cmd_opts.add_option( + '-a', '--algorithm', + dest='algorithm', + choices=STRONG_HASHES, + action='store', + default=FAVORITE_HASH, + help='The hash algorithm to use: one of %s' % + ', '.join(STRONG_HASHES)) + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + if not args: + self.parser.print_usage(sys.stderr) + return ERROR + + algorithm = options.algorithm + for path in args: + logger.info('%s:\n--hash=%s:%s', + path, algorithm, _hash_of_file(path, algorithm)) + + +def _hash_of_file(path, algorithm): + """Return the hash digest of a file.""" + with open(path, 'rb') as archive: + hash = hashlib.new(algorithm) + for chunk in read_chunks(archive): + hash.update(chunk) + return hash.hexdigest() diff --git a/pip/commands/help.py b/pip/commands/help.py index 22533873224..11722f1e067 100644 --- a/pip/commands/help.py +++ b/pip/commands/help.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + from pip.basecommand import Command, SUCCESS from pip.exceptions import CommandError @@ -10,7 +12,7 @@ class HelpCommand(Command): summary = 'Show help for commands.' def run(self, options, args): - from pip.commands import commands, get_similar_commands + from pip.commands import commands_dict, get_similar_commands try: # 'pip help' with no args is handled by pip.__init__.parseopt() @@ -18,7 +20,7 @@ def run(self, options, args): except IndexError: return SUCCESS - if cmd_name not in commands: + if cmd_name not in commands_dict: guess = get_similar_commands(cmd_name) msg = ['unknown command "%s"' % cmd_name] @@ -27,7 +29,7 @@ def run(self, options, args): raise CommandError(' - '.join(msg)) - command = commands[cmd_name]() + command = commands_dict[cmd_name]() command.parser.print_help() return SUCCESS diff --git a/pip/commands/install.py b/pip/commands/install.py index cbf22a0860c..28d30c59f7b 100644 --- a/pip/commands/install.py +++ b/pip/commands/install.py @@ -1,18 +1,34 @@ +from __future__ import absolute_import + +import logging +import operator import os -import sys import tempfile import shutil -from pip.req import InstallRequirement, RequirementSet, parse_requirements -from pip.log import logger -from pip.locations import (src_prefix, virtualenv_no_global, distutils_scheme, - build_prefix) -from pip.basecommand import Command -from pip.index import PackageFinder -from pip.exceptions import InstallationError, CommandError, PreviousBuildDirError +import warnings +try: + import wheel +except ImportError: + wheel = None + +from pip.req import RequirementSet +from pip.basecommand import RequirementCommand +from pip.locations import virtualenv_no_global, distutils_scheme +from pip.exceptions import ( + InstallationError, CommandError, PreviousBuildDirError, +) from pip import cmdoptions +from pip.utils import ensure_dir +from pip.utils.build import BuildDirectory +from pip.utils.deprecation import RemovedInPip10Warning +from pip.utils.filesystem import check_path_owner +from pip.wheel import WheelCache, WheelBuilder + + +logger = logging.getLogger(__name__) -class InstallCommand(Command): +class InstallCommand(RequirementCommand): """ Install packages from: @@ -27,62 +43,54 @@ class InstallCommand(Command): name = 'install' usage = """ - %prog [options] ... - %prog [options] -r ... + %prog [options] [package-index-options] ... + %prog [options] -r [package-index-options] ... %prog [options] [-e] ... %prog [options] [-e] ... %prog [options] ...""" summary = 'Install packages.' - bundle = False def __init__(self, *args, **kw): super(InstallCommand, self).__init__(*args, **kw) cmd_opts = self.cmd_opts - cmd_opts.add_option( - '-e', '--editable', - dest='editables', - action='append', - default=[], - metavar='path/url', - help='Install a project in editable mode (i.e. setuptools "develop mode") from a local project path or a VCS url.') - - cmd_opts.add_option(cmdoptions.requirements.make()) - cmd_opts.add_option(cmdoptions.build_dir.make()) + cmd_opts.add_option(cmdoptions.constraints()) + cmd_opts.add_option(cmdoptions.editable()) + cmd_opts.add_option(cmdoptions.requirements()) + cmd_opts.add_option(cmdoptions.build_dir()) cmd_opts.add_option( '-t', '--target', dest='target_dir', metavar='dir', default=None, - help='Install packages into .') + help='Install packages into . ' + 'By default this will not replace existing files/folders in ' + '. Use --upgrade to replace existing packages in ' + 'with new versions.' + ) cmd_opts.add_option( '-d', '--download', '--download-dir', '--download-directory', dest='download_dir', metavar='dir', default=None, - help="Download packages into instead of installing them, regardless of what's already installed.") - - cmd_opts.add_option(cmdoptions.download_cache.make()) + help=("Download packages into instead of installing them, " + "regardless of what's already installed."), + ) - cmd_opts.add_option( - '--src', '--source', '--source-dir', '--source-directory', - dest='src_dir', - metavar='dir', - default=src_prefix, - help='Directory to check out editable projects into. ' - 'The default in a virtualenv is "/src". ' - 'The default for global installs is "/src".') + cmd_opts.add_option(cmdoptions.src()) cmd_opts.add_option( '-U', '--upgrade', dest='upgrade', action='store_true', - help='Upgrade all packages to the newest available version. ' - 'This process is recursive regardless of whether a dependency is already satisfied.') + help='Upgrade all specified packages to the newest available ' + 'version. This process is recursive regardless of whether ' + 'a dependency is already satisfied.' + ) cmd_opts.add_option( '--force-reinstall', @@ -97,42 +105,44 @@ def __init__(self, *args, **kw): action='store_true', help='Ignore the installed packages (reinstalling instead).') - cmd_opts.add_option(cmdoptions.no_deps.make()) + cmd_opts.add_option(cmdoptions.no_deps()) - cmd_opts.add_option( - '--no-install', - dest='no_install', - action='store_true', - help="DEPRECATED. Download and unpack all packages, but don't actually install them.") - - cmd_opts.add_option( - '--no-download', - dest='no_download', - action="store_true", - help="DEPRECATED. Don't download any packages, just install the ones already downloaded " - "(completes an install run with --no-install).") - - cmd_opts.add_option(cmdoptions.install_options.make()) - cmd_opts.add_option(cmdoptions.global_options.make()) + cmd_opts.add_option(cmdoptions.install_options()) + cmd_opts.add_option(cmdoptions.global_options()) cmd_opts.add_option( '--user', dest='use_user_site', action='store_true', - help='Install using the user scheme.') + help="Install to the Python user install directory for your " + "platform. Typically ~/.local/, or %APPDATA%\Python on " + "Windows. (See the Python documentation for site.USER_BASE " + "for full details.)") cmd_opts.add_option( '--egg', dest='as_egg', action='store_true', - help="Install packages as eggs, not 'flat', like pip normally does. This option is not about installing *from* eggs. (WARNING: Because this option overrides pip's normal install logic, requirements files may not behave as expected.)") + help="Install packages as eggs, not 'flat', like pip normally " + "does. This option is not about installing *from* eggs. " + "(WARNING: Because this option overrides pip's normal install" + " logic, requirements files may not behave as expected.)") cmd_opts.add_option( '--root', dest='root_path', metavar='dir', default=None, - help="Install everything relative to this alternate root directory.") + help="Install everything relative to this alternate root " + "directory.") + + cmd_opts.add_option( + '--prefix', + dest='prefix_path', + metavar='dir', + default=None, + help="Installation prefix where lib, bin and other top-level " + "folders are placed") cmd_opts.add_option( "--compile", @@ -149,166 +159,239 @@ def __init__(self, *args, **kw): help="Do not compile py files to pyc", ) - cmd_opts.add_option(cmdoptions.use_wheel.make()) - cmd_opts.add_option(cmdoptions.no_use_wheel.make()) - - cmd_opts.add_option( - '--pre', - action='store_true', - default=False, - help="Include pre-release and development versions. By default, pip only finds stable versions.") - - cmd_opts.add_option(cmdoptions.no_clean.make()) - - index_opts = cmdoptions.make_option_group(cmdoptions.index_group, self.parser) + cmd_opts.add_option(cmdoptions.use_wheel()) + cmd_opts.add_option(cmdoptions.no_use_wheel()) + cmd_opts.add_option(cmdoptions.no_binary()) + cmd_opts.add_option(cmdoptions.only_binary()) + cmd_opts.add_option(cmdoptions.pre()) + cmd_opts.add_option(cmdoptions.no_clean()) + cmd_opts.add_option(cmdoptions.require_hashes()) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) self.parser.insert_option_group(0, index_opts) self.parser.insert_option_group(0, cmd_opts) - def _build_package_finder(self, options, index_urls, session): - """ - Create a package finder appropriate to this install command. - This method is meant to be overridden by subclasses, not - called directly. - """ - return PackageFinder(find_links=options.find_links, - index_urls=index_urls, - use_wheel=options.use_wheel, - allow_external=options.allow_external, - allow_unverified=options.allow_unverified, - allow_all_external=options.allow_all_external, - allow_all_prereleases=options.pre, - process_dependency_links= - options.process_dependency_links, - session=session, - ) - def run(self, options, args): - - if ( - options.no_install or - options.no_download or - (options.build_dir != build_prefix) or - options.no_clean - ): - logger.deprecated('1.7', 'DEPRECATION: --no-install, --no-download, --build, ' - 'and --no-clean are deprecated. See https://github.com/pypa/pip/issues/906.') + cmdoptions.resolve_wheel_no_use_binary(options) + cmdoptions.check_install_build_global(options) + + if options.allow_external: + warnings.warn( + "--allow-external has been deprecated and will be removed in " + "the future. Due to changes in the repository protocol, it no " + "longer has any effect.", + RemovedInPip10Warning, + ) + + if options.allow_all_external: + warnings.warn( + "--allow-all-external has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + if options.allow_unverified: + warnings.warn( + "--allow-unverified has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) if options.download_dir: - options.no_install = True + warnings.warn( + "pip install --download has been deprecated and will be " + "removed in the future. Pip now has a download command that " + "should be used instead.", + RemovedInPip10Warning, + ) options.ignore_installed = True - options.build_dir = os.path.abspath(options.build_dir) + + if options.build_dir: + options.build_dir = os.path.abspath(options.build_dir) + options.src_dir = os.path.abspath(options.src_dir) install_options = options.install_options or [] if options.use_user_site: + if options.prefix_path: + raise CommandError( + "Can not combine '--user' and '--prefix' as they imply " + "different installation locations" + ) if virtualenv_no_global(): - raise InstallationError("Can not perform a '--user' install. User site-packages are not visible in this virtualenv.") + raise InstallationError( + "Can not perform a '--user' install. User site-packages " + "are not visible in this virtualenv." + ) install_options.append('--user') + install_options.append('--prefix=') temp_target_dir = None if options.target_dir: options.ignore_installed = True temp_target_dir = tempfile.mkdtemp() options.target_dir = os.path.abspath(options.target_dir) - if os.path.exists(options.target_dir) and not os.path.isdir(options.target_dir): - raise CommandError("Target path exists but is not a directory, will not continue.") + if (os.path.exists(options.target_dir) and not + os.path.isdir(options.target_dir)): + raise CommandError( + "Target path exists but is not a directory, will not " + "continue." + ) install_options.append('--home=' + temp_target_dir) global_options = options.global_options or [] - index_urls = [options.index_url] + options.extra_index_urls - if options.no_index: - logger.notify('Ignoring indexes: %s' % ','.join(index_urls)) - index_urls = [] - - if options.use_mirrors: - logger.deprecated("1.7", - "--use-mirrors has been deprecated and will be removed" - " in the future. Explicit uses of --index-url and/or " - "--extra-index-url is suggested.") - - if options.mirrors: - logger.deprecated("1.7", - "--mirrors has been deprecated and will be removed in " - " the future. Explicit uses of --index-url and/or " - "--extra-index-url is suggested.") - index_urls += options.mirrors - - session = self._build_session(options) - - finder = self._build_package_finder(options, index_urls, session) - - requirement_set = RequirementSet( - build_dir=options.build_dir, - src_dir=options.src_dir, - download_dir=options.download_dir, - download_cache=options.download_cache, - upgrade=options.upgrade, - as_egg=options.as_egg, - ignore_installed=options.ignore_installed, - ignore_dependencies=options.ignore_dependencies, - force_reinstall=options.force_reinstall, - use_user_site=options.use_user_site, - target_dir=temp_target_dir, - session=session, - pycompile=options.compile, - ) - for name in args: - requirement_set.add_requirement( - InstallRequirement.from_line(name, None)) - for name in options.editables: - requirement_set.add_requirement( - InstallRequirement.from_editable(name, default_vcs=options.default_vcs)) - for filename in options.requirements: - for req in parse_requirements(filename, finder=finder, options=options, session=session): - requirement_set.add_requirement(req) - if not requirement_set.has_requirements: - opts = {'name': self.name} - if options.find_links: - msg = ('You must give at least one requirement to %(name)s ' - '(maybe you meant "pip %(name)s %(links)s"?)' % - dict(opts, links=' '.join(options.find_links))) - else: - msg = ('You must give at least one requirement ' - 'to %(name)s (see "pip help %(name)s")' % opts) - logger.warn(msg) - return - - try: - if not options.no_download: - requirement_set.prepare_files(finder, force_root_egg_info=self.bundle, bundle=self.bundle) - else: - requirement_set.locate_files() - - if not options.no_install and not self.bundle: - requirement_set.install(install_options, global_options, root=options.root_path) - installed = ' '.join([req.name for req in - requirement_set.successfully_installed]) - if installed: - logger.notify('Successfully installed %s' % installed) - elif not self.bundle: - downloaded = ' '.join([req.name for req in - requirement_set.successfully_downloaded]) - if downloaded: - logger.notify('Successfully downloaded %s' % downloaded) - elif self.bundle: - requirement_set.create_bundle(self.bundle_filename) - logger.notify('Created bundle in %s' % self.bundle_filename) - except PreviousBuildDirError: - options.no_clean = True - raise - finally: - # Clean up - if (not options.no_clean) and ((not options.no_install) or options.download_dir): - requirement_set.cleanup_files(bundle=self.bundle) + + with self._build_session(options) as session: + + finder = self._build_package_finder(options, session) + build_delete = (not (options.no_clean or options.build_dir)) + wheel_cache = WheelCache(options.cache_dir, options.format_control) + if options.cache_dir and not check_path_owner(options.cache_dir): + logger.warning( + "The directory '%s' or its parent directory is not owned " + "by the current user and caching wheels has been " + "disabled. check the permissions and owner of that " + "directory. If executing pip with sudo, you may want " + "sudo's -H flag.", + options.cache_dir, + ) + options.cache_dir = None + + with BuildDirectory(options.build_dir, + delete=build_delete) as build_dir: + requirement_set = RequirementSet( + build_dir=build_dir, + src_dir=options.src_dir, + download_dir=options.download_dir, + upgrade=options.upgrade, + as_egg=options.as_egg, + ignore_installed=options.ignore_installed, + ignore_dependencies=options.ignore_dependencies, + force_reinstall=options.force_reinstall, + use_user_site=options.use_user_site, + target_dir=temp_target_dir, + session=session, + pycompile=options.compile, + isolated=options.isolated_mode, + wheel_cache=wheel_cache, + require_hashes=options.require_hashes, + ) + + self.populate_requirement_set( + requirement_set, args, options, finder, session, self.name, + wheel_cache + ) + + if not requirement_set.has_requirements: + return + + try: + if (options.download_dir or not wheel or not + options.cache_dir): + # on -d don't do complex things like building + # wheels, and don't try to build wheels when wheel is + # not installed. + requirement_set.prepare_files(finder) + else: + # build wheels before install. + wb = WheelBuilder( + requirement_set, + finder, + build_options=[], + global_options=[], + ) + # Ignore the result: a failed wheel will be + # installed from the sdist/vcs whatever. + wb.build(autobuilding=True) + + if not options.download_dir: + requirement_set.install( + install_options, + global_options, + root=options.root_path, + prefix=options.prefix_path, + ) + reqs = sorted( + requirement_set.successfully_installed, + key=operator.attrgetter('name')) + items = [] + for req in reqs: + item = req.name + try: + if hasattr(req, 'installed_version'): + if req.installed_version: + item += '-' + req.installed_version + except Exception: + pass + items.append(item) + installed = ' '.join(items) + if installed: + logger.info('Successfully installed %s', installed) + else: + downloaded = ' '.join([ + req.name + for req in requirement_set.successfully_downloaded + ]) + if downloaded: + logger.info( + 'Successfully downloaded %s', downloaded + ) + except PreviousBuildDirError: + options.no_clean = True + raise + finally: + # Clean up + if not options.no_clean: + requirement_set.cleanup_files() if options.target_dir: - if not os.path.exists(options.target_dir): - os.makedirs(options.target_dir) - lib_dir = distutils_scheme('', home=temp_target_dir)['purelib'] - for item in os.listdir(lib_dir): - shutil.move( - os.path.join(lib_dir, item), - os.path.join(options.target_dir, item) + ensure_dir(options.target_dir) + + # Checking both purelib and platlib directories for installed + # packages to be moved to target directory + lib_dir_list = [] + + purelib_dir = distutils_scheme('', home=temp_target_dir)['purelib'] + platlib_dir = distutils_scheme('', home=temp_target_dir)['platlib'] + + if os.path.exists(purelib_dir): + lib_dir_list.append(purelib_dir) + if os.path.exists(platlib_dir) and platlib_dir != purelib_dir: + lib_dir_list.append(platlib_dir) + + for lib_dir in lib_dir_list: + for item in os.listdir(lib_dir): + target_item_dir = os.path.join(options.target_dir, item) + if os.path.exists(target_item_dir): + if not options.upgrade: + logger.warning( + 'Target directory %s already exists. Specify ' + '--upgrade to force replacement.', + target_item_dir + ) + continue + if os.path.islink(target_item_dir): + logger.warning( + 'Target directory %s already exists and is ' + 'a link. Pip will not automatically replace ' + 'links, please remove if replacement is ' + 'desired.', + target_item_dir + ) + continue + if os.path.isdir(target_item_dir): + shutil.rmtree(target_item_dir) + else: + os.remove(target_item_dir) + + shutil.move( + os.path.join(lib_dir, item), + target_item_dir ) shutil.rmtree(temp_target_dir) return requirement_set diff --git a/pip/commands/list.py b/pip/commands/list.py index 207f06885ca..0310a3eb493 100644 --- a/pip/commands/list.py +++ b/pip/commands/list.py @@ -1,22 +1,37 @@ +from __future__ import absolute_import + +import json +import logging +import warnings +try: + from itertools import zip_longest +except ImportError: + from itertools import izip_longest as zip_longest + +from pip._vendor import six + from pip.basecommand import Command -from pip.exceptions import DistributionNotFound, BestVersionAlreadyInstalled +from pip.exceptions import CommandError from pip.index import PackageFinder -from pip.log import logger -from pip.req import InstallRequirement -from pip.util import get_installed_distributions, dist_is_editable +from pip.utils import ( + get_installed_distributions, dist_is_editable) +from pip.utils.deprecation import RemovedInPip10Warning from pip.cmdoptions import make_option_group, index_group +logger = logging.getLogger(__name__) + class ListCommand(Command): - """List installed packages, including editables.""" + """ + List installed packages, including editables. + + Packages are listed in a case-insensitive sorted order. + """ name = 'list' usage = """ %prog [options]""" summary = 'List installed packages.' - # distributions to skip (python itself is reported by pkg_resources.working_set) - skip = ['python'] - def __init__(self, *args, **kw): super(ListCommand, self).__init__(*args, **kw) @@ -26,12 +41,12 @@ def __init__(self, *args, **kw): '-o', '--outdated', action='store_true', default=False, - help='List outdated packages (excluding editables)') + help='List outdated packages') cmd_opts.add_option( '-u', '--uptodate', action='store_true', default=False, - help='List uptodate packages (excluding editables)') + help='List uptodate packages') cmd_opts.add_option( '-e', '--editable', action='store_true', @@ -41,13 +56,40 @@ def __init__(self, *args, **kw): '-l', '--local', action='store_true', default=False, - help='If in a virtualenv that has global access, do not list globally-installed packages.') + help=('If in a virtualenv that has global access, do not list ' + 'globally-installed packages.'), + ) + self.cmd_opts.add_option( + '--user', + dest='user', + action='store_true', + default=False, + help='Only output packages installed in user-site.') cmd_opts.add_option( '--pre', action='store_true', default=False, - help="Include pre-release and development versions. By default, pip only finds stable versions.") + help=("Include pre-release and development versions. By default, " + "pip only finds stable versions."), + ) + + cmd_opts.add_option( + '--format', + action='store', + dest='list_format', + choices=('legacy', 'columns', 'freeze', 'json'), + help="Select the output format among: legacy (default), columns, " + "freeze or json.", + ) + + cmd_opts.add_option( + '--columns', + action='store_const', + const='columns', + dest='list_format', + help="Align package names and versions into vertical columns.", + ) index_opts = make_option_group(index_group, self.parser) @@ -58,105 +100,226 @@ def _build_package_finder(self, options, index_urls, session): """ Create a package finder appropriate to this list command. """ - return PackageFinder(find_links=options.find_links, - index_urls=index_urls, - allow_external=options.allow_external, - allow_unverified=options.allow_unverified, - allow_all_external=options.allow_all_external, - allow_all_prereleases=options.pre, - process_dependency_links= - options.process_dependency_links, - session=session, - ) + return PackageFinder( + find_links=options.find_links, + index_urls=index_urls, + allow_all_prereleases=options.pre, + trusted_hosts=options.trusted_hosts, + process_dependency_links=options.process_dependency_links, + session=session, + ) def run(self, options, args): + if options.allow_external: + warnings.warn( + "--allow-external has been deprecated and will be removed in " + "the future. Due to changes in the repository protocol, it no " + "longer has any effect.", + RemovedInPip10Warning, + ) + + if options.allow_all_external: + warnings.warn( + "--allow-all-external has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + if options.allow_unverified: + warnings.warn( + "--allow-unverified has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + if options.list_format is None: + warnings.warn( + "The default format will switch to columns in the future. " + "You can use --format=legacy (or define a list_format " + "in your pip.conf) to disable this warning.", + RemovedInPip10Warning, + ) + + if options.outdated and options.uptodate: + raise CommandError( + "Options --outdated and --uptodate cannot be combined.") + + packages = get_installed_distributions( + local_only=options.local, + user_only=options.user, + editables_only=options.editable, + ) if options.outdated: - self.run_outdated(options) + packages = self.get_outdated(packages, options) elif options.uptodate: - self.run_uptodate(options) - elif options.editable: - self.run_editables(options) - else: - self.run_listing(options) + packages = self.get_uptodate(packages, options) + self.output_package_listing(packages, options) - def run_outdated(self, options): - for dist, remote_version_raw, remote_version_parsed in self.find_packages_latests_versions(options): - if remote_version_parsed > dist.parsed_version: - logger.notify('%s (Current: %s Latest: %s)' % (dist.project_name, - dist.version, remote_version_raw)) + def get_outdated(self, packages, options): + return [ + dist for dist in self.iter_packages_latest_infos(packages, options) + if dist.latest_version > dist.parsed_version + ] - def find_packages_latests_versions(self, options): + def get_uptodate(self, packages, options): + return [ + dist for dist in self.iter_packages_latest_infos(packages, options) + if dist.latest_version == dist.parsed_version + ] + + def iter_packages_latest_infos(self, packages, options): index_urls = [options.index_url] + options.extra_index_urls if options.no_index: - logger.notify('Ignoring indexes: %s' % ','.join(index_urls)) + logger.debug('Ignoring indexes: %s', ','.join(index_urls)) index_urls = [] - if options.use_mirrors: - logger.deprecated("1.7", - "--use-mirrors has been deprecated and will be removed" - " in the future. Explicit uses of --index-url and/or " - "--extra-index-url is suggested.") - - if options.mirrors: - logger.deprecated("1.7", - "--mirrors has been deprecated and will be removed in " - " the future. Explicit uses of --index-url and/or " - "--extra-index-url is suggested.") - index_urls += options.mirrors - dependency_links = [] - for dist in get_installed_distributions(local_only=options.local, skip=self.skip): + for dist in packages: if dist.has_metadata('dependency_links.txt'): dependency_links.extend( dist.get_metadata_lines('dependency_links.txt'), ) - session = self._build_session(options) + with self._build_session(options) as session: + finder = self._build_package_finder(options, index_urls, session) + finder.add_dependency_links(dependency_links) - finder = self._build_package_finder(options, index_urls, session) - finder.add_dependency_links(dependency_links) + for dist in 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] - installed_packages = get_installed_distributions(local_only=options.local, include_editables=False, skip=self.skip) - for dist in installed_packages: - req = InstallRequirement.from_line(dist.key, None) - try: - link = finder.find_requirement(req, True) - - # If link is None, means installed version is most up-to-date - if link is None: + if not all_candidates: continue - except DistributionNotFound: - continue - except BestVersionAlreadyInstalled: - remote_version = req.installed_version - else: - # It might be a good idea that link or finder had a public method - # that returned version - remote_version = finder._link_package_versions(link, req.name)[0] - remote_version_raw = remote_version[2] - remote_version_parsed = remote_version[0] - yield dist, remote_version_raw, remote_version_parsed - - def run_listing(self, options): - installed_packages = get_installed_distributions(local_only=options.local, skip=self.skip) - self.output_package_listing(installed_packages) - - def run_editables(self, options): - installed_packages = get_installed_distributions(local_only=options.local, editables_only=True) - self.output_package_listing(installed_packages) - - def output_package_listing(self, installed_packages): - installed_packages = sorted(installed_packages, key=lambda dist: dist.project_name.lower()) - for dist in installed_packages: - if dist_is_editable(dist): - line = '%s (%s, %s)' % (dist.project_name, dist.version, dist.location) - else: - line = '%s (%s)' % (dist.project_name, dist.version) - logger.notify(line) - - def run_uptodate(self, options): - uptodate = [] - for dist, remote_version_raw, remote_version_parsed in self.find_packages_latests_versions(options): - if dist.parsed_version == remote_version_parsed: - uptodate.append(dist) - self.output_package_listing(uptodate) + best_candidate = max(all_candidates, + key=finder._candidate_sort_key) + remote_version = best_candidate.version + if best_candidate.location.is_wheel: + typ = 'wheel' + else: + typ = 'sdist' + # This is dirty but makes the rest of the code much cleaner + dist.latest_version = remote_version + dist.latest_filetype = typ + yield dist + + def output_legacy(self, dist): + if dist_is_editable(dist): + return '%s (%s, %s)' % ( + dist.project_name, + dist.version, + dist.location, + ) + else: + return '%s (%s)' % (dist.project_name, dist.version) + + def output_legacy_latest(self, dist): + return '%s - Latest: %s [%s]' % ( + self.output_legacy(dist), + dist.latest_version, + dist.latest_filetype, + ) + + def output_package_listing(self, packages, options): + packages = sorted( + packages, + key=lambda dist: dist.project_name.lower(), + ) + if options.list_format == 'columns' and packages: + data, header = format_for_columns(packages, options) + self.output_package_listing_columns(data, header) + elif options.list_format == 'freeze': + for dist in packages: + logger.info("%s==%s", dist.project_name, dist.version) + elif options.list_format == 'json': + logger.info(format_for_json(packages, options)) + else: # legacy + for dist in packages: + if options.outdated: + logger.info(self.output_legacy_latest(dist)) + else: + logger.info(self.output_legacy(dist)) + + def output_package_listing_columns(self, data, header): + # insert the header first: we need to know the size of column names + if len(data) > 0: + data.insert(0, header) + + pkg_strings, sizes = tabulate(data) + + # Create and add a separator. + if len(data) > 0: + pkg_strings.insert(1, " ".join(map(lambda x: '-' * x, sizes))) + + for val in pkg_strings: + logger.info(val) + + +def tabulate(vals): + # From pfmoore on GitHub: + # https://github.com/pypa/pip/issues/3651#issuecomment-216932564 + assert len(vals) > 0 + + sizes = [0] * max(len(x) for x in vals) + for row in vals: + sizes = [max(s, len(str(c))) for s, c in zip_longest(sizes, row)] + + result = [] + for row in vals: + display = " ".join([str(c).ljust(s) if c is not None else '' + for s, c in zip_longest(sizes, row)]) + result.append(display) + + return result, sizes + + +def format_for_columns(pkgs, options): + """ + Convert the package data into something usable + by output_package_listing_columns. + """ + running_outdated = options.outdated + # Adjust the header for the `pip list --outdated` case. + if running_outdated: + header = ["Package", "Version", "Latest", "Type"] + else: + header = ["Package", "Version"] + + data = [] + if any(dist_is_editable(x) for x in pkgs): + header.append("Location") + + for proj in pkgs: + # if we're working on the 'outdated' list, separate out the + # latest_version and type + row = [proj.project_name, proj.version] + + if running_outdated: + row.append(proj.latest_version) + row.append(proj.latest_filetype) + + if dist_is_editable(proj): + row.append(proj.location) + + data.append(row) + + return data, header + + +def format_for_json(packages, options): + data = [] + for dist in packages: + info = { + 'name': dist.project_name, + 'version': six.text_type(dist.version), + } + if options.outdated: + info['latest_version'] = six.text_type(dist.latest_version) + info['latest_filetype'] = dist.latest_filetype + data.append(info) + return json.dumps(data) diff --git a/pip/commands/search.py b/pip/commands/search.py index 94c4d12eaa7..2aa024c900f 100644 --- a/pip/commands/search.py +++ b/pip/commands/search.py @@ -1,16 +1,22 @@ +from __future__ import absolute_import + +import logging import sys import textwrap -import pip.download - from pip.basecommand import Command, SUCCESS -from pip.util import get_terminal_size -from pip.log import logger -from pip.backwardcompat import xmlrpclib, reduce, cmp +from pip.download import PipXmlrpcTransport +from pip.models import PyPI +from pip.utils import get_terminal_size +from pip.utils.logging import indent_log from pip.exceptions import CommandError from pip.status_codes import NO_MATCHES_FOUND +from pip._vendor.packaging.version import parse as parse_version from pip._vendor import pkg_resources -from distutils.version import StrictVersion, LooseVersion +from pip._vendor.six.moves import xmlrpc_client + + +logger = logging.getLogger(__name__) class SearchCommand(Command): @@ -26,7 +32,7 @@ def __init__(self, *args, **kw): '--index', dest='index', metavar='URL', - default='https://pypi.python.org/pypi', + default=PyPI.pypi_url, help='Base URL of Python Package Index (default %default)') self.parser.insert_option_group(0, self.cmd_opts) @@ -35,9 +41,7 @@ def run(self, options, args): if not args: raise CommandError('Missing required argument (search query).') query = args - index_url = options.index - - pypi_hits = self.search(query, index_url) + pypi_hits = self.search(query, options) hits = transform_hits(pypi_hits) terminal_width = None @@ -49,10 +53,13 @@ def run(self, options, args): return SUCCESS return NO_MATCHES_FOUND - def search(self, query, index_url): - pypi = xmlrpclib.ServerProxy(index_url) - hits = pypi.search({'name': query, 'summary': query}, 'or') - return hits + def search(self, query, options): + index_url = options.index + with self._build_session(options) as session: + transport = PipXmlrpcTransport(index_url, session) + pypi = xmlrpc_client.ServerProxy(index_url, transport) + hits = pypi.search({'name': query, 'summary': query}, 'or') + return hits def transform_hits(hits): @@ -71,7 +78,12 @@ def transform_hits(hits): score = 0 if name not in packages.keys(): - packages[name] = {'name': name, 'summary': summary, 'versions': [version], 'score': score} + packages[name] = { + 'name': name, + 'summary': summary, + 'versions': [version], + 'score': score, + } else: packages[name]['versions'].append(version) @@ -80,53 +92,53 @@ def transform_hits(hits): packages[name]['summary'] = summary packages[name]['score'] = score - # each record has a unique name now, so we will convert the dict into a list sorted by score - package_list = sorted(packages.values(), key=lambda x: x['score'], reverse=True) + # each record has a unique name now, so we will convert the dict into a + # list sorted by score + package_list = sorted( + packages.values(), + key=lambda x: x['score'], + reverse=True, + ) return package_list -def print_results(hits, name_column_width=25, terminal_width=None): +def print_results(hits, name_column_width=None, terminal_width=None): + if not hits: + return + if name_column_width is None: + name_column_width = max([ + len(hit['name']) + len(hit.get('versions', ['-'])[-1]) + for hit in hits + ]) + 4 + installed_packages = [p.project_name for p in pkg_resources.working_set] for hit in hits: name = hit['name'] summary = hit['summary'] or '' + version = hit.get('versions', ['-'])[-1] if terminal_width is not None: - # wrap and indent summary to fit terminal - summary = textwrap.wrap(summary, terminal_width - name_column_width - 5) - summary = ('\n' + ' ' * (name_column_width + 3)).join(summary) - line = '%s - %s' % (name.ljust(name_column_width), summary) + target_width = terminal_width - name_column_width - 5 + if target_width > 10: + # wrap and indent summary to fit terminal + summary = textwrap.wrap(summary, target_width) + summary = ('\n' + ' ' * (name_column_width + 3)).join(summary) + + line = '%-*s - %s' % (name_column_width, + '%s (%s)' % (name, version), summary) try: - logger.notify(line) + logger.info(line) if name in installed_packages: dist = pkg_resources.get_distribution(name) - logger.indent += 2 - try: + with indent_log(): latest = highest_version(hit['versions']) if dist.version == latest: - logger.notify('INSTALLED: %s (latest)' % dist.version) + logger.info('INSTALLED: %s (latest)', dist.version) else: - logger.notify('INSTALLED: %s' % dist.version) - logger.notify('LATEST: %s' % latest) - finally: - logger.indent -= 2 + logger.info('INSTALLED: %s', dist.version) + logger.info('LATEST: %s', latest) except UnicodeEncodeError: pass -def compare_versions(version1, version2): - try: - return cmp(StrictVersion(version1), StrictVersion(version2)) - # in case of abnormal version number, fall back to LooseVersion - except ValueError: - pass - try: - return cmp(LooseVersion(version1), LooseVersion(version2)) - except TypeError: - # certain LooseVersion comparions raise due to unorderable types, - # fallback to string comparison - return cmp([str(v) for v in LooseVersion(version1).version], - [str(v) for v in LooseVersion(version2).version]) - - def highest_version(versions): - return reduce((lambda v1, v2: compare_versions(v1, v2) == 1 and v1 or v2), versions) + return max(versions, key=parse_version) diff --git a/pip/commands/show.py b/pip/commands/show.py index 02b473a974f..1ed2db93960 100644 --- a/pip/commands/show.py +++ b/pip/commands/show.py @@ -1,10 +1,17 @@ +from __future__ import absolute_import + +from email.parser import FeedParser +import logging import os from pip.basecommand import Command -from pip.log import logger +from pip.status_codes import SUCCESS, ERROR from pip._vendor import pkg_resources +logger = logging.getLogger(__name__) + + class ShowCommand(Command): """Show information about one or more installed packages.""" name = 'show' @@ -25,12 +32,15 @@ def __init__(self, *args, **kw): def run(self, options, args): if not args: - logger.warn('ERROR: Please provide a package name or names.') - return + logger.warning('ERROR: Please provide a package name or names.') + return ERROR query = args results = search_packages_info(query) - print_results(results, options.files) + if not print_results( + results, list_files=options.files, verbose=options.verbose): + return ERROR + return SUCCESS def search_packages_info(query): @@ -40,41 +50,104 @@ def search_packages_info(query): pip generated 'installed-files.txt' in the distributions '.egg-info' directory. """ - installed_packages = dict( + installed = dict( [(p.project_name.lower(), p) for p in pkg_resources.working_set]) - for name in query: - normalized_name = name.lower() - if normalized_name in installed_packages: - dist = installed_packages[normalized_name] - package = { - 'name': dist.project_name, - 'version': dist.version, - 'location': dist.location, - 'requires': [dep.project_name for dep in dist.requires()], - } - filelist = os.path.join( - dist.location, - dist.egg_name() + '.egg-info', - 'installed-files.txt') - if os.path.isfile(filelist): - package['files'] = filelist - yield package - - -def print_results(distributions, list_all_files): + query_names = [name.lower() for name in query] + for dist in [installed[pkg] for pkg in query_names if pkg in installed]: + package = { + 'name': dist.project_name, + 'version': dist.version, + 'location': dist.location, + 'requires': [dep.project_name for dep in dist.requires()], + } + file_list = None + metadata = None + if isinstance(dist, pkg_resources.DistInfoDistribution): + # RECORDs should be part of .dist-info metadatas + if dist.has_metadata('RECORD'): + lines = dist.get_metadata_lines('RECORD') + paths = [l.split(',')[0] for l in lines] + paths = [os.path.join(dist.location, p) for p in paths] + file_list = [os.path.relpath(p, dist.location) for p in paths] + + if dist.has_metadata('METADATA'): + metadata = dist.get_metadata('METADATA') + else: + # Otherwise use pip's log for .egg-info's + if dist.has_metadata('installed-files.txt'): + paths = dist.get_metadata_lines('installed-files.txt') + paths = [os.path.join(dist.egg_info, p) for p in paths] + file_list = [os.path.relpath(p, dist.location) for p in paths] + + if dist.has_metadata('PKG-INFO'): + metadata = dist.get_metadata('PKG-INFO') + + if dist.has_metadata('entry_points.txt'): + entry_points = dist.get_metadata_lines('entry_points.txt') + package['entry_points'] = entry_points + + if dist.has_metadata('INSTALLER'): + for line in dist.get_metadata_lines('INSTALLER'): + if line.strip(): + package['installer'] = line.strip() + break + + # @todo: Should pkg_resources.Distribution have a + # `get_pkg_info` method? + feed_parser = FeedParser() + feed_parser.feed(metadata) + pkg_info_dict = feed_parser.close() + for key in ('metadata-version', 'summary', + 'home-page', 'author', 'author-email', 'license'): + package[key] = pkg_info_dict.get(key) + + # It looks like FeedParser cannot deal with repeated headers + classifiers = [] + for line in metadata.splitlines(): + if not line: + break + # Classifier: License :: OSI Approved :: MIT License + if line.startswith('Classifier: '): + classifiers.append(line[len('Classifier: '):]) + package['classifiers'] = classifiers + + if file_list: + package['files'] = sorted(file_list) + yield package + + +def print_results(distributions, list_files=False, verbose=False): """ Print the informations from installed distributions found. """ - for dist in distributions: - logger.notify("---") - logger.notify("Name: %s" % dist['name']) - logger.notify("Version: %s" % dist['version']) - logger.notify("Location: %s" % dist['location']) - logger.notify("Requires: %s" % ', '.join(dist['requires'])) - if list_all_files: - logger.notify("Files:") - if 'files' in dist: - for line in open(dist['files']): - logger.notify(" %s" % line.strip()) - else: - logger.notify("Cannot locate installed-files.txt") + results_printed = False + for i, dist in enumerate(distributions): + results_printed = True + if i > 0: + logger.info("---") + logger.info("Name: %s", dist.get('name', '')) + logger.info("Version: %s", dist.get('version', '')) + logger.info("Summary: %s", dist.get('summary', '')) + logger.info("Home-page: %s", dist.get('home-page', '')) + logger.info("Author: %s", dist.get('author', '')) + logger.info("Author-email: %s", dist.get('author-email', '')) + logger.info("License: %s", dist.get('license', '')) + logger.info("Location: %s", dist.get('location', '')) + logger.info("Requires: %s", ', '.join(dist.get('requires', []))) + if verbose: + logger.info("Metadata-Version: %s", + dist.get('metadata-version', '')) + logger.info("Installer: %s", dist.get('installer', '')) + logger.info("Classifiers:") + for classifier in dist.get('classifiers', []): + logger.info(" %s", classifier) + logger.info("Entry-points:") + for entry in dist.get('entry_points', []): + logger.info(" %s", entry.strip()) + if list_files: + logger.info("Files:") + for line in dist.get('files', []): + logger.info(" %s", line.strip()) + if "files" not in dist: + logger.info("Cannot locate installed-files.txt") + return results_printed diff --git a/pip/commands/uninstall.py b/pip/commands/uninstall.py index b7099cf8ce7..8ba1a7c65d9 100644 --- a/pip/commands/uninstall.py +++ b/pip/commands/uninstall.py @@ -1,3 +1,7 @@ +from __future__ import absolute_import + +import pip +from pip.wheel import WheelCache from pip.req import InstallRequirement, RequirementSet, parse_requirements from pip.basecommand import Command from pip.exceptions import InstallationError @@ -27,8 +31,9 @@ def __init__(self, *args, **kw): action='append', default=[], metavar='file', - help='Uninstall all the packages listed in the given requirements file. ' - 'This option can be used multiple times.') + help='Uninstall all the packages listed in the given requirements ' + 'file. This option can be used multiple times.', + ) self.cmd_opts.add_option( '-y', '--yes', dest='yes', @@ -38,22 +43,34 @@ def __init__(self, *args, **kw): self.parser.insert_option_group(0, self.cmd_opts) def run(self, options, args): - session = self._build_session(options) - - requirement_set = RequirementSet( - build_dir=None, - src_dir=None, - download_dir=None, - session=session, - ) - for name in args: - requirement_set.add_requirement( - InstallRequirement.from_line(name)) - for filename in options.requirements: - for req in parse_requirements(filename, - options=options, session=session): - requirement_set.add_requirement(req) - if not requirement_set.has_requirements: - raise InstallationError('You must give at least one requirement ' - 'to %(name)s (see "pip help %(name)s")' % dict(name=self.name)) - requirement_set.uninstall(auto_confirm=options.yes) + with self._build_session(options) as session: + format_control = pip.index.FormatControl(set(), set()) + wheel_cache = WheelCache(options.cache_dir, format_control) + requirement_set = RequirementSet( + build_dir=None, + src_dir=None, + download_dir=None, + isolated=options.isolated_mode, + session=session, + wheel_cache=wheel_cache, + ) + for name in args: + requirement_set.add_requirement( + InstallRequirement.from_line( + name, isolated=options.isolated_mode, + wheel_cache=wheel_cache + ) + ) + for filename in options.requirements: + for req in parse_requirements( + filename, + options=options, + session=session, + wheel_cache=wheel_cache): + requirement_set.add_requirement(req) + if not requirement_set.has_requirements: + raise InstallationError( + 'You must give at least one requirement to %(name)s (see ' + '"pip help %(name)s")' % dict(name=self.name) + ) + requirement_set.uninstall(auto_confirm=options.yes) diff --git a/pip/commands/unzip.py b/pip/commands/unzip.py deleted file mode 100644 index ed66ab9c84c..00000000000 --- a/pip/commands/unzip.py +++ /dev/null @@ -1,7 +0,0 @@ -from pip.commands.zip import ZipCommand - - -class UnzipCommand(ZipCommand): - """Unzip individual packages.""" - name = 'unzip' - summary = 'DEPRECATED. Unzip individual packages.' diff --git a/pip/commands/wheel.py b/pip/commands/wheel.py index 6527063ca3f..3ad32459c76 100644 --- a/pip/commands/wheel.py +++ b/pip/commands/wheel.py @@ -1,29 +1,35 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import +import logging import os -import sys -from pip.basecommand import Command -from pip.index import PackageFinder -from pip.log import logger +import warnings + +from pip.basecommand import RequirementCommand from pip.exceptions import CommandError, PreviousBuildDirError -from pip.req import InstallRequirement, RequirementSet, parse_requirements -from pip.util import normalize_path -from pip.wheel import WheelBuilder +from pip.req import RequirementSet +from pip.utils import import_or_raise +from pip.utils.build import BuildDirectory +from pip.utils.deprecation import RemovedInPip10Warning +from pip.wheel import WheelCache, WheelBuilder from pip import cmdoptions -DEFAULT_WHEEL_DIR = os.path.join(normalize_path(os.curdir), 'wheelhouse') -class WheelCommand(Command): +logger = logging.getLogger(__name__) + + +class WheelCommand(RequirementCommand): """ Build Wheel archives for your requirements and dependencies. - Wheel is a built-package format, and offers the advantage of not recompiling your software during every install. - For more details, see the wheel docs: http://wheel.readthedocs.org/en/latest. + Wheel is a built-package format, and offers the advantage of not + recompiling your software during every install. For more details, see the + wheel docs: https://wheel.readthedocs.io/en/latest/ Requirements: setuptools>=0.8, and wheel. - 'pip wheel' uses the bdist_wheel setuptools extension from the wheel package to build individual wheels. + 'pip wheel' uses the bdist_wheel setuptools extension from the wheel + package to build individual wheels. """ @@ -31,8 +37,8 @@ class WheelCommand(Command): usage = """ %prog [options] ... %prog [options] -r ... - %prog [options] ... - %prog [options] ... + %prog [options] [-e] ... + %prog [options] [-e] ... %prog [options] ...""" summary = 'Build wheels from your requirements.' @@ -46,20 +52,26 @@ def __init__(self, *args, **kw): '-w', '--wheel-dir', dest='wheel_dir', metavar='dir', - default=DEFAULT_WHEEL_DIR, - help="Build wheels into , where the default is '/wheelhouse'.") - cmd_opts.add_option(cmdoptions.use_wheel.make()) - cmd_opts.add_option(cmdoptions.no_use_wheel.make()) + default=os.curdir, + help=("Build wheels into , where the default is the " + "current working directory."), + ) + cmd_opts.add_option(cmdoptions.use_wheel()) + cmd_opts.add_option(cmdoptions.no_use_wheel()) + cmd_opts.add_option(cmdoptions.no_binary()) + cmd_opts.add_option(cmdoptions.only_binary()) cmd_opts.add_option( '--build-option', dest='build_options', metavar='options', action='append', help="Extra arguments to be supplied to 'setup.py bdist_wheel'.") - cmd_opts.add_option(cmdoptions.requirements.make()) - cmd_opts.add_option(cmdoptions.download_cache.make()) - cmd_opts.add_option(cmdoptions.no_deps.make()) - cmd_opts.add_option(cmdoptions.build_dir.make()) + cmd_opts.add_option(cmdoptions.constraints()) + cmd_opts.add_option(cmdoptions.editable()) + cmd_opts.add_option(cmdoptions.requirements()) + cmd_opts.add_option(cmdoptions.src()) + cmd_opts.add_option(cmdoptions.no_deps()) + cmd_opts.add_option(cmdoptions.build_dir()) cmd_opts.add_option( '--global-option', @@ -73,123 +85,122 @@ def __init__(self, *args, **kw): '--pre', action='store_true', default=False, - help="Include pre-release and development versions. By default, pip only finds stable versions.") + help=("Include pre-release and development versions. By default, " + "pip only finds stable versions."), + ) - cmd_opts.add_option(cmdoptions.no_clean.make()) + cmd_opts.add_option(cmdoptions.no_clean()) + cmd_opts.add_option(cmdoptions.require_hashes()) - index_opts = cmdoptions.make_option_group(cmdoptions.index_group, self.parser) + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) self.parser.insert_option_group(0, index_opts) self.parser.insert_option_group(0, cmd_opts) + def check_required_packages(self): + import_or_raise( + 'wheel.bdist_wheel', + CommandError, + "'pip wheel' requires the 'wheel' package. To fix this, run: " + "pip install wheel" + ) + pkg_resources = import_or_raise( + 'pkg_resources', + CommandError, + "'pip wheel' requires setuptools >= 0.8 for dist-info support." + " To fix this, run: pip install --upgrade setuptools" + ) + if not hasattr(pkg_resources, 'DistInfoDistribution'): + raise CommandError( + "'pip wheel' requires setuptools >= 0.8 for dist-info " + "support. To fix this, run: pip install --upgrade " + "setuptools" + ) + def run(self, options, args): + self.check_required_packages() + cmdoptions.resolve_wheel_no_use_binary(options) + cmdoptions.check_install_build_global(options) + + if options.allow_external: + warnings.warn( + "--allow-external has been deprecated and will be removed in " + "the future. Due to changes in the repository protocol, it no " + "longer has any effect.", + RemovedInPip10Warning, + ) - # confirm requirements - try: - import wheel.bdist_wheel - except ImportError: - raise CommandError("'pip wheel' requires the 'wheel' package. To fix this, run: pip install wheel") + if options.allow_all_external: + warnings.warn( + "--allow-all-external has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) - try: - import pkg_resources - except ImportError: - raise CommandError( - "'pip wheel' requires setuptools >= 0.8 for dist-info support." - " To fix this, run: pip install --upgrade setuptools" + if options.allow_unverified: + warnings.warn( + "--allow-unverified has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, ) - else: - if not hasattr(pkg_resources, 'DistInfoDistribution'): - raise CommandError( - "'pip wheel' requires setuptools >= 0.8 for dist-info " - "support. To fix this, run: pip install --upgrade " - "setuptools" - ) index_urls = [options.index_url] + options.extra_index_urls if options.no_index: - logger.notify('Ignoring indexes: %s' % ','.join(index_urls)) + logger.debug('Ignoring indexes: %s', ','.join(index_urls)) index_urls = [] - if options.use_mirrors: - logger.deprecated("1.7", - "--use-mirrors has been deprecated and will be removed" - " in the future. Explicit uses of --index-url and/or " - "--extra-index-url is suggested.") - - if options.mirrors: - logger.deprecated("1.7", - "--mirrors has been deprecated and will be removed in " - " the future. Explicit uses of --index-url and/or " - "--extra-index-url is suggested.") - index_urls += options.mirrors - - session = self._build_session(options) - - finder = PackageFinder(find_links=options.find_links, - index_urls=index_urls, - use_wheel=options.use_wheel, - allow_external=options.allow_external, - allow_unverified=options.allow_unverified, - allow_all_external=options.allow_all_external, - allow_all_prereleases=options.pre, - process_dependency_links= - options.process_dependency_links, - session=session, - ) - - options.build_dir = os.path.abspath(options.build_dir) - requirement_set = RequirementSet( - build_dir=options.build_dir, - src_dir=None, - download_dir=None, - download_cache=options.download_cache, - ignore_dependencies=options.ignore_dependencies, - ignore_installed=True, - session=session, - wheel_download_dir=options.wheel_dir - ) + if options.build_dir: + options.build_dir = os.path.abspath(options.build_dir) + + options.src_dir = os.path.abspath(options.src_dir) + + with self._build_session(options) as session: + finder = self._build_package_finder(options, session) + build_delete = (not (options.no_clean or options.build_dir)) + wheel_cache = WheelCache(options.cache_dir, options.format_control) + with BuildDirectory(options.build_dir, + delete=build_delete) as build_dir: + requirement_set = RequirementSet( + build_dir=build_dir, + src_dir=options.src_dir, + download_dir=None, + ignore_dependencies=options.ignore_dependencies, + ignore_installed=True, + isolated=options.isolated_mode, + session=session, + wheel_cache=wheel_cache, + wheel_download_dir=options.wheel_dir, + require_hashes=options.require_hashes + ) - # make the wheelhouse - if not os.path.exists(options.wheel_dir): - os.makedirs(options.wheel_dir) - - #parse args and/or requirements files - for name in args: - requirement_set.add_requirement( - InstallRequirement.from_line(name, None)) - - for filename in options.requirements: - for req in parse_requirements( - filename, - finder=finder, - options=options, - session=session): - if req.editable: - logger.notify("ignoring %s" % req.url) - continue - requirement_set.add_requirement(req) - - #fail if no requirements - if not requirement_set.has_requirements: - opts = {'name': self.name} - msg = ('You must give at least one requirement ' - 'to %(name)s (see "pip help %(name)s")' % opts) - logger.error(msg) - return - - try: - #build wheels - wb = WheelBuilder( - requirement_set, - finder, - options.wheel_dir, - build_options = options.build_options or [], - global_options = options.global_options or [] + self.populate_requirement_set( + requirement_set, args, options, finder, session, self.name, + wheel_cache ) - wb.build() - except PreviousBuildDirError: - options.no_clean = True - raise - finally: - if not options.no_clean: - requirement_set.cleanup_files() + + if not requirement_set.has_requirements: + return + + try: + # build wheels + wb = WheelBuilder( + requirement_set, + finder, + build_options=options.build_options or [], + global_options=options.global_options or [], + ) + if not wb.build(): + raise CommandError( + "Failed to build one or more wheels" + ) + except PreviousBuildDirError: + options.no_clean = True + raise + finally: + if not options.no_clean: + requirement_set.cleanup_files() diff --git a/pip/commands/zip.py b/pip/commands/zip.py deleted file mode 100644 index c8013594d13..00000000000 --- a/pip/commands/zip.py +++ /dev/null @@ -1,351 +0,0 @@ -import sys -import re -import fnmatch -import os -import shutil -import zipfile -from pip.util import display_path, backup_dir, rmtree -from pip.log import logger -from pip.exceptions import InstallationError -from pip.basecommand import Command - - -class ZipCommand(Command): - """Zip individual packages.""" - name = 'zip' - usage = """ - %prog [options] ...""" - summary = 'DEPRECATED. Zip individual packages.' - - def __init__(self, *args, **kw): - super(ZipCommand, self).__init__(*args, **kw) - if self.name == 'zip': - self.cmd_opts.add_option( - '--unzip', - action='store_true', - dest='unzip', - help='Unzip (rather than zip) a package.') - else: - self.cmd_opts.add_option( - '--zip', - action='store_false', - dest='unzip', - default=True, - help='Zip (rather than unzip) a package.') - self.cmd_opts.add_option( - '--no-pyc', - action='store_true', - dest='no_pyc', - help='Do not include .pyc files in zip files (useful on Google App Engine).') - self.cmd_opts.add_option( - '-l', '--list', - action='store_true', - dest='list', - help='List the packages available, and their zip status.') - self.cmd_opts.add_option( - '--sort-files', - action='store_true', - dest='sort_files', - help='With --list, sort packages according to how many files they contain.') - self.cmd_opts.add_option( - '--path', - action='append', - dest='paths', - help='Restrict operations to the given paths (may include wildcards).') - self.cmd_opts.add_option( - '-n', '--simulate', - action='store_true', - help='Do not actually perform the zip/unzip operation.') - - self.parser.insert_option_group(0, self.cmd_opts) - - def paths(self): - """All the entries of sys.path, possibly restricted by --path""" - if not self.select_paths: - return sys.path - result = [] - match_any = set() - for path in sys.path: - path = os.path.normcase(os.path.abspath(path)) - for match in self.select_paths: - match = os.path.normcase(os.path.abspath(match)) - if '*' in match: - if re.search(fnmatch.translate(match + '*'), path): - result.append(path) - match_any.add(match) - break - else: - if path.startswith(match): - result.append(path) - match_any.add(match) - break - else: - logger.debug("Skipping path %s because it doesn't match %s" - % (path, ', '.join(self.select_paths))) - 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) - return result - - def run(self, options, args): - - logger.deprecated('1.7', "DEPRECATION: 'pip zip' and 'pip unzip` are deprecated, and will be removed in a future release.") - - self.select_paths = options.paths - self.simulate = options.simulate - if options.list: - return self.list(options, args) - if not args: - raise InstallationError( - 'You must give at least one package to zip or unzip') - packages = [] - for arg in args: - module_name, filename = self.find_package(arg) - if options.unzip and os.path.isdir(filename): - raise InstallationError( - 'The module %s (in %s) is not a zip file; cannot be unzipped' - % (module_name, filename)) - elif not options.unzip and not os.path.isdir(filename): - raise InstallationError( - 'The module %s (in %s) is not a directory; cannot be zipped' - % (module_name, filename)) - packages.append((module_name, filename)) - last_status = None - for module_name, filename in packages: - if options.unzip: - last_status = self.unzip_package(module_name, filename) - else: - last_status = self.zip_package(module_name, filename, options.no_pyc) - return last_status - - def unzip_package(self, module_name, filename): - zip_filename = os.path.dirname(filename) - if not os.path.isfile(zip_filename) and zipfile.is_zipfile(zip_filename): - raise InstallationError( - 'Module %s (in %s) isn\'t located in a zip file in %s' - % (module_name, filename, zip_filename)) - package_path = os.path.dirname(zip_filename) - if not package_path in self.paths(): - logger.warn( - 'Unpacking %s into %s, but %s is not on sys.path' - % (display_path(zip_filename), display_path(package_path), - display_path(package_path))) - logger.notify('Unzipping %s (in %s)' % (module_name, display_path(zip_filename))) - if self.simulate: - logger.notify('Skipping remaining operations because of --simulate') - return - logger.indent += 2 - try: - ## FIXME: this should be undoable: - zip = zipfile.ZipFile(zip_filename) - to_save = [] - for info in zip.infolist(): - name = info.filename - if name.startswith(module_name + os.path.sep): - content = zip.read(name) - dest = os.path.join(package_path, name) - if not os.path.exists(os.path.dirname(dest)): - os.makedirs(os.path.dirname(dest)) - if not content and dest.endswith(os.path.sep): - if not os.path.exists(dest): - os.makedirs(dest) - else: - f = open(dest, 'wb') - f.write(content) - f.close() - else: - to_save.append((name, zip.read(name))) - zip.close() - if not to_save: - logger.info('Removing now-empty zip file %s' % display_path(zip_filename)) - os.unlink(zip_filename) - self.remove_filename_from_pth(zip_filename) - else: - logger.info('Removing entries in %s/ from zip file %s' % (module_name, display_path(zip_filename))) - zip = zipfile.ZipFile(zip_filename, 'w') - for name, content in to_save: - zip.writestr(name, content) - zip.close() - finally: - logger.indent -= 2 - - def zip_package(self, module_name, filename, no_pyc): - orig_filename = filename - logger.notify('Zip %s (in %s)' % (module_name, display_path(filename))) - logger.indent += 2 - if filename.endswith('.egg'): - dest_filename = filename - else: - dest_filename = filename + '.zip' - try: - ## FIXME: I think this needs to be undoable: - if filename == dest_filename: - filename = backup_dir(orig_filename) - logger.notify('Moving %s aside to %s' % (orig_filename, filename)) - if not self.simulate: - shutil.move(orig_filename, filename) - try: - logger.info('Creating zip file in %s' % display_path(dest_filename)) - if not self.simulate: - zip = zipfile.ZipFile(dest_filename, 'w') - zip.writestr(module_name + '/', '') - for dirpath, dirnames, filenames in os.walk(filename): - if no_pyc: - filenames = [f for f in filenames - if not f.lower().endswith('.pyc')] - for fns, is_dir in [(dirnames, True), (filenames, False)]: - for fn in fns: - 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 + '/', '') - else: - zip.write(full, dest) - zip.close() - logger.info('Removing old directory %s' % display_path(filename)) - if not self.simulate: - rmtree(filename) - except: - ## FIXME: need to do an undo here - raise - ## FIXME: should also be undone: - self.add_filename_to_pth(dest_filename) - finally: - logger.indent -= 2 - - def remove_filename_from_pth(self, filename): - for pth in self.pth_files(): - f = open(pth, 'r') - lines = f.readlines() - f.close() - new_lines = [ - l for l in lines if l.strip() != filename] - if lines != new_lines: - logger.info('Removing reference to %s from .pth file %s' - % (display_path(filename), display_path(pth))) - if not [line for line in new_lines if line]: - logger.info('%s file would be empty: deleting' % display_path(pth)) - if not self.simulate: - os.unlink(pth) - else: - if not self.simulate: - f = open(pth, 'wb') - f.writelines(new_lines) - f.close() - return - logger.warn('Cannot find a reference to %s in any .pth file' % display_path(filename)) - - def add_filename_to_pth(self, filename): - path = os.path.dirname(filename) - dest = filename + '.pth' - if path not in self.paths(): - logger.warn('Adding .pth file %s, but it is not on sys.path' % display_path(dest)) - if not self.simulate: - if os.path.exists(dest): - f = open(dest) - lines = f.readlines() - f.close() - if lines and not lines[-1].endswith('\n'): - lines[-1] += '\n' - lines.append(filename + '\n') - else: - lines = [filename + '\n'] - f = open(dest, 'wb') - f.writelines(lines) - f.close() - - def pth_files(self): - for path in self.paths(): - if not os.path.exists(path) or not os.path.isdir(path): - continue - for filename in os.listdir(path): - if filename.endswith('.pth'): - yield os.path.join(path, filename) - - def find_package(self, package): - for path in self.paths(): - full = os.path.join(path, package) - if os.path.exists(full): - return package, full - if not os.path.isdir(path) and zipfile.is_zipfile(path): - zip = zipfile.ZipFile(path, 'r') - try: - zip.read(os.path.join(package, '__init__.py')) - except KeyError: - pass - else: - zip.close() - return package, full - zip.close() - ## FIXME: need special error for package.py case: - raise InstallationError( - 'No package with the name %s found' % package) - - def list(self, options, args): - if args: - raise InstallationError( - 'You cannot give an argument with --list') - for path in sorted(self.paths()): - if not os.path.exists(path): - continue - basename = os.path.basename(path.rstrip(os.path.sep)) - if os.path.isfile(path) and zipfile.is_zipfile(path): - if os.path.dirname(path) not in self.paths(): - logger.notify('Zipped egg: %s' % display_path(path)) - continue - if (basename != 'site-packages' and basename != 'dist-packages' - and not path.replace('\\', '/').endswith('lib/python')): - continue - logger.notify('In %s:' % display_path(path)) - logger.indent += 2 - zipped = [] - unzipped = [] - try: - for filename in sorted(os.listdir(path)): - ext = os.path.splitext(filename)[1].lower() - if ext in ('.pth', '.egg-info', '.egg-link'): - continue - if ext == '.py': - logger.info('Not displaying %s: not a package' % display_path(filename)) - continue - full = os.path.join(path, filename) - if os.path.isdir(full): - unzipped.append((filename, self.count_package(full))) - elif zipfile.is_zipfile(full): - zipped.append(filename) - else: - logger.info('Unknown file: %s' % display_path(filename)) - if zipped: - logger.notify('Zipped packages:') - logger.indent += 2 - try: - for filename in zipped: - logger.notify(filename) - finally: - logger.indent -= 2 - else: - logger.notify('No zipped packages.') - if unzipped: - if options.sort_files: - unzipped.sort(key=lambda x: -x[1]) - logger.notify('Unzipped packages:') - logger.indent += 2 - try: - for filename, count in unzipped: - logger.notify('%s (%i files)' % (filename, count)) - finally: - logger.indent -= 2 - else: - logger.notify('No unzipped packages.') - finally: - logger.indent -= 2 - - def count_package(self, path): - total = 0 - for dirpath, dirnames, filenames in os.walk(path): - filenames = [f for f in filenames - if not f.lower().endswith('.pyc')] - total += len(filenames) - return total diff --git a/pip/compat/__init__.py b/pip/compat/__init__.py new file mode 100644 index 00000000000..f229f59a068 --- /dev/null +++ b/pip/compat/__init__.py @@ -0,0 +1,164 @@ +"""Stuff that differs in different Python versions and platform +distributions.""" +from __future__ import absolute_import, division + +import os +import sys + +from pip._vendor.six import text_type + +try: + from logging.config import dictConfig as logging_dictConfig +except ImportError: + from pip.compat.dictconfig import dictConfig as logging_dictConfig + +try: + from collections import OrderedDict +except ImportError: + from pip.compat.ordereddict import OrderedDict + +try: + import ipaddress +except ImportError: + try: + from pip._vendor import ipaddress + except ImportError: + import ipaddr as ipaddress + ipaddress.ip_address = ipaddress.IPAddress + 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", + "OrderedDict", +] + + +if sys.version_info >= (3, 4): + uses_pycache = True + from importlib.util import cache_from_source +else: + import imp + uses_pycache = hasattr(imp, 'cache_from_source') + if uses_pycache: + cache_from_source = imp.cache_from_source + else: + cache_from_source = None + + +if sys.version_info >= (3,): + def console_to_str(s): + try: + return s.decode(sys.__stdout__.encoding) + except UnicodeDecodeError: + return s.decode('utf_8') + + def native_str(s, replace=False): + if isinstance(s, bytes): + return s.decode('utf-8', 'replace' if replace else 'strict') + return s + +else: + def console_to_str(s): + return s + + def native_str(s, replace=False): + # Replace is ignored -- unicode to UTF-8 can't fail + if isinstance(s, text_type): + return s.encode('utf-8') + return s + + +def total_seconds(td): + if hasattr(td, "total_seconds"): + return td.total_seconds() + else: + val = td.microseconds + (td.seconds + td.days * 24 * 3600) * 10 ** 6 + return val / 10 ** 6 + + +def get_path_uid(path): + """ + Return path's uid. + + Does not follow symlinks: + https://github.com/pypa/pip/pull/935#discussion_r5307003 + + Placed this function in compat due to differences on AIX and + Jython, that should eventually go away. + + :raises OSError: When path is a symlink or can't be read. + """ + if hasattr(os, 'O_NOFOLLOW'): + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW) + file_uid = os.fstat(fd).st_uid + os.close(fd) + else: # AIX and Jython + # WARNING: time of check vulnerability, but best we can do w/o NOFOLLOW + if not os.path.islink(path): + # older versions of Jython don't have `os.fstat` + file_uid = os.stat(path).st_uid + else: + # raise OSError for parity with os.O_NOFOLLOW above + raise OSError( + "%s is a symlink; Will not return uid for symlinks" % path + ) + return file_uid + + +def expanduser(path): + """ + Expand ~ and ~user constructions. + + Includes a workaround for http://bugs.python.org/issue14768 + """ + expanded = os.path.expanduser(path) + if path.startswith('~/') and expanded.startswith('//'): + expanded = expanded[1:] + return expanded + + +# packages in the stdlib that may have installation metadata, but should not be +# considered 'installed'. this theoretically could be determined based on +# dist.location (py27:`sysconfig.get_paths()['stdlib']`, +# py26:sysconfig.get_config_vars('LIBDEST')), but fear platform variation may +# make this ineffective, so hard-coding +stdlib_pkgs = ('python', 'wsgiref') +if sys.version_info >= (2, 7): + stdlib_pkgs += ('argparse',) + + +# windows detection, covers cpython and ironpython +WINDOWS = (sys.platform.startswith("win") or + (sys.platform == 'cli' and os.name == 'nt')) + + +def samefile(file1, file2): + """Provide an alternative for os.path.samefile on Windows/Python2""" + if hasattr(os.path, 'samefile'): + return os.path.samefile(file1, file2) + else: + path1 = os.path.normcase(os.path.abspath(file1)) + path2 = os.path.normcase(os.path.abspath(file2)) + return path1 == path2 diff --git a/pip/compat/dictconfig.py b/pip/compat/dictconfig.py new file mode 100644 index 00000000000..ec684aac203 --- /dev/null +++ b/pip/compat/dictconfig.py @@ -0,0 +1,565 @@ +# This is a copy of the Python logging.config.dictconfig module, +# reproduced with permission. It is provided here for backwards +# compatibility for Python versions prior to 2.7. +# +# Copyright 2009-2010 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +from __future__ import absolute_import + +import logging.handlers +import re +import sys +import types + +from pip._vendor import six + +# flake8: noqa + +IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I) + + +def valid_ident(s): + m = IDENTIFIER.match(s) + if not m: + raise ValueError('Not a valid Python identifier: %r' % s) + return True + +# +# This function is defined in logging only in recent versions of Python +# +try: + from logging import _checkLevel +except ImportError: + def _checkLevel(level): + if isinstance(level, int): + rv = level + elif str(level) == level: + if level not in logging._levelNames: + raise ValueError('Unknown level: %r' % level) + rv = logging._levelNames[level] + else: + raise TypeError('Level not an integer or a ' + 'valid string: %r' % level) + return rv + +# The ConvertingXXX classes are wrappers around standard Python containers, +# and they serve to convert any suitable values in the container. The +# conversion converts base dicts, lists and tuples to their wrapped +# equivalents, whereas strings which match a conversion format are converted +# appropriately. +# +# Each wrapper should have a configurator attribute holding the actual +# configurator to use for conversion. + + +class ConvertingDict(dict): + """A converting dictionary wrapper.""" + + def __getitem__(self, key): + value = dict.__getitem__(self, key) + result = self.configurator.convert(value) + # If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def get(self, key, default=None): + value = dict.get(self, key, default) + result = self.configurator.convert(value) + # If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def pop(self, key, default=None): + value = dict.pop(self, key, default) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + +class ConvertingList(list): + """A converting list wrapper.""" + def __getitem__(self, key): + value = list.__getitem__(self, key) + result = self.configurator.convert(value) + # If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def pop(self, idx=-1): + value = list.pop(self, idx) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + return result + + +class ConvertingTuple(tuple): + """A converting tuple wrapper.""" + def __getitem__(self, key): + value = tuple.__getitem__(self, key) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + +class BaseConfigurator(object): + """ + The configurator base class which defines some useful defaults. + """ + + CONVERT_PATTERN = re.compile(r'^(?P[a-z]+)://(?P.*)$') + + WORD_PATTERN = re.compile(r'^\s*(\w+)\s*') + DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*') + INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*') + DIGIT_PATTERN = re.compile(r'^\d+$') + + value_converters = { + 'ext' : 'ext_convert', + 'cfg' : 'cfg_convert', + } + + # We might want to use a different one, e.g. importlib + importer = __import__ + + def __init__(self, config): + self.config = ConvertingDict(config) + self.config.configurator = self + + def resolve(self, s): + """ + Resolve strings to objects using standard import and attribute + syntax. + """ + name = s.split('.') + used = name.pop(0) + try: + found = self.importer(used) + for frag in name: + used += '.' + frag + try: + found = getattr(found, frag) + except AttributeError: + self.importer(used) + found = getattr(found, frag) + return found + except ImportError: + e, tb = sys.exc_info()[1:] + v = ValueError('Cannot resolve %r: %s' % (s, e)) + v.__cause__, v.__traceback__ = e, tb + raise v + + def ext_convert(self, value): + """Default converter for the ext:// protocol.""" + return self.resolve(value) + + def cfg_convert(self, value): + """Default converter for the cfg:// protocol.""" + rest = value + m = self.WORD_PATTERN.match(rest) + if m is None: + raise ValueError("Unable to convert %r" % value) + else: + rest = rest[m.end():] + d = self.config[m.groups()[0]] + # print d, rest + while rest: + m = self.DOT_PATTERN.match(rest) + if m: + d = d[m.groups()[0]] + else: + m = self.INDEX_PATTERN.match(rest) + if m: + idx = m.groups()[0] + if not self.DIGIT_PATTERN.match(idx): + d = d[idx] + else: + try: + n = int(idx) # try as number first (most likely) + d = d[n] + except TypeError: + d = d[idx] + if m: + rest = rest[m.end():] + else: + raise ValueError('Unable to convert ' + '%r at %r' % (value, rest)) + # rest should be empty + return d + + def convert(self, value): + """ + Convert values to an appropriate type. dicts, lists and tuples are + replaced by their converting alternatives. Strings are checked to + see if they have a conversion format and are converted if they do. + """ + if not isinstance(value, ConvertingDict) and isinstance(value, dict): + value = ConvertingDict(value) + value.configurator = self + elif not isinstance(value, ConvertingList) and isinstance(value, list): + value = ConvertingList(value) + value.configurator = self + elif not isinstance(value, ConvertingTuple) and\ + isinstance(value, tuple): + value = ConvertingTuple(value) + value.configurator = self + elif isinstance(value, six.string_types): # str for py3k + m = self.CONVERT_PATTERN.match(value) + if m: + d = m.groupdict() + prefix = d['prefix'] + converter = self.value_converters.get(prefix, None) + if converter: + suffix = d['suffix'] + converter = getattr(self, converter) + value = converter(suffix) + return value + + def configure_custom(self, config): + """Configure an object with a user-supplied factory.""" + c = config.pop('()') + if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType: + c = self.resolve(c) + props = config.pop('.', None) + # Check for valid identifiers + kwargs = dict((k, config[k]) for k in config if valid_ident(k)) + result = c(**kwargs) + if props: + for name, value in props.items(): + setattr(result, name, value) + return result + + def as_tuple(self, value): + """Utility function which converts lists to tuples.""" + if isinstance(value, list): + value = tuple(value) + return value + + +class DictConfigurator(BaseConfigurator): + """ + Configure logging using a dictionary-like object to describe the + configuration. + """ + + def configure(self): + """Do the configuration.""" + + config = self.config + if 'version' not in config: + raise ValueError("dictionary doesn't specify a version") + if config['version'] != 1: + raise ValueError("Unsupported version: %s" % config['version']) + incremental = config.pop('incremental', False) + EMPTY_DICT = {} + logging._acquireLock() + try: + if incremental: + handlers = config.get('handlers', EMPTY_DICT) + # incremental handler config only if handler name + # ties in to logging._handlers (Python 2.7) + if sys.version_info[:2] == (2, 7): + for name in handlers: + if name not in logging._handlers: + raise ValueError('No handler found with ' + 'name %r' % name) + else: + try: + handler = logging._handlers[name] + handler_config = handlers[name] + level = handler_config.get('level', None) + if level: + handler.setLevel(_checkLevel(level)) + except StandardError as e: + raise ValueError('Unable to configure handler ' + '%r: %s' % (name, e)) + loggers = config.get('loggers', EMPTY_DICT) + for name in loggers: + try: + self.configure_logger(name, loggers[name], True) + except StandardError as e: + raise ValueError('Unable to configure logger ' + '%r: %s' % (name, e)) + root = config.get('root', None) + if root: + try: + self.configure_root(root, True) + except StandardError as e: + raise ValueError('Unable to configure root ' + 'logger: %s' % e) + else: + disable_existing = config.pop('disable_existing_loggers', True) + + logging._handlers.clear() + del logging._handlerList[:] + + # Do formatters first - they don't refer to anything else + formatters = config.get('formatters', EMPTY_DICT) + for name in formatters: + try: + formatters[name] = self.configure_formatter( + formatters[name]) + except StandardError as e: + raise ValueError('Unable to configure ' + 'formatter %r: %s' % (name, e)) + # Next, do filters - they don't refer to anything else, either + filters = config.get('filters', EMPTY_DICT) + for name in filters: + try: + filters[name] = self.configure_filter(filters[name]) + except StandardError as e: + raise ValueError('Unable to configure ' + 'filter %r: %s' % (name, e)) + + # Next, do handlers - they refer to formatters and filters + # As handlers can refer to other handlers, sort the keys + # to allow a deterministic order of configuration + handlers = config.get('handlers', EMPTY_DICT) + for name in sorted(handlers): + try: + handler = self.configure_handler(handlers[name]) + handler.name = name + handlers[name] = handler + except StandardError as e: + raise ValueError('Unable to configure handler ' + '%r: %s' % (name, e)) + # Next, do loggers - they refer to handlers and filters + + # we don't want to lose the existing loggers, + # since other threads may have pointers to them. + # existing is set to contain all existing loggers, + # and as we go through the new configuration we + # remove any which are configured. At the end, + # what's left in existing is the set of loggers + # which were in the previous configuration but + # which are not in the new configuration. + root = logging.root + existing = list(root.manager.loggerDict) + # The list needs to be sorted so that we can + # avoid disabling child loggers of explicitly + # named loggers. With a sorted list it is easier + # to find the child loggers. + existing.sort() + # We'll keep the list of existing loggers + # which are children of named loggers here... + child_loggers = [] + # now set up the new ones... + loggers = config.get('loggers', EMPTY_DICT) + for name in loggers: + if name in existing: + i = existing.index(name) + prefixed = name + "." + pflen = len(prefixed) + num_existing = len(existing) + i = i + 1 # look at the entry after name + while (i < num_existing) and\ + (existing[i][:pflen] == prefixed): + child_loggers.append(existing[i]) + i = i + 1 + existing.remove(name) + try: + self.configure_logger(name, loggers[name]) + except StandardError as e: + raise ValueError('Unable to configure logger ' + '%r: %s' % (name, e)) + + # Disable any old loggers. There's no point deleting + # them as other threads may continue to hold references + # and by disabling them, you stop them doing any logging. + # However, don't disable children of named loggers, as that's + # probably not what was intended by the user. + for log in existing: + logger = root.manager.loggerDict[log] + if log in child_loggers: + logger.level = logging.NOTSET + logger.handlers = [] + logger.propagate = True + elif disable_existing: + logger.disabled = True + + # And finally, do the root logger + root = config.get('root', None) + if root: + try: + self.configure_root(root) + except StandardError as e: + raise ValueError('Unable to configure root ' + 'logger: %s' % e) + finally: + logging._releaseLock() + + def configure_formatter(self, config): + """Configure a formatter from a dictionary.""" + if '()' in config: + factory = config['()'] # for use in exception handler + try: + result = self.configure_custom(config) + except TypeError as te: + if "'format'" not in str(te): + raise + # Name of parameter changed from fmt to format. + # Retry with old name. + # This is so that code can be used with older Python versions + #(e.g. by Django) + config['fmt'] = config.pop('format') + config['()'] = factory + result = self.configure_custom(config) + else: + fmt = config.get('format', None) + dfmt = config.get('datefmt', None) + result = logging.Formatter(fmt, dfmt) + return result + + def configure_filter(self, config): + """Configure a filter from a dictionary.""" + if '()' in config: + result = self.configure_custom(config) + else: + name = config.get('name', '') + result = logging.Filter(name) + return result + + def add_filters(self, filterer, filters): + """Add filters to a filterer from a list of names.""" + for f in filters: + try: + filterer.addFilter(self.config['filters'][f]) + except StandardError as e: + raise ValueError('Unable to add filter %r: %s' % (f, e)) + + def configure_handler(self, config): + """Configure a handler from a dictionary.""" + formatter = config.pop('formatter', None) + if formatter: + try: + formatter = self.config['formatters'][formatter] + except StandardError as e: + raise ValueError('Unable to set formatter ' + '%r: %s' % (formatter, e)) + level = config.pop('level', None) + filters = config.pop('filters', None) + if '()' in config: + c = config.pop('()') + if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType: + c = self.resolve(c) + factory = c + else: + klass = self.resolve(config.pop('class')) + # Special case for handler which refers to another handler + if issubclass(klass, logging.handlers.MemoryHandler) and\ + 'target' in config: + try: + config['target'] = self.config['handlers'][config['target']] + except StandardError as e: + raise ValueError('Unable to set target handler ' + '%r: %s' % (config['target'], e)) + elif issubclass(klass, logging.handlers.SMTPHandler) and\ + 'mailhost' in config: + config['mailhost'] = self.as_tuple(config['mailhost']) + elif issubclass(klass, logging.handlers.SysLogHandler) and\ + 'address' in config: + config['address'] = self.as_tuple(config['address']) + factory = klass + kwargs = dict((k, config[k]) for k in config if valid_ident(k)) + try: + result = factory(**kwargs) + except TypeError as te: + if "'stream'" not in str(te): + raise + # The argument name changed from strm to stream + # Retry with old name. + # This is so that code can be used with older Python versions + #(e.g. by Django) + kwargs['strm'] = kwargs.pop('stream') + result = factory(**kwargs) + if formatter: + result.setFormatter(formatter) + if level is not None: + result.setLevel(_checkLevel(level)) + if filters: + self.add_filters(result, filters) + return result + + def add_handlers(self, logger, handlers): + """Add handlers to a logger from a list of names.""" + for h in handlers: + try: + logger.addHandler(self.config['handlers'][h]) + except StandardError as e: + raise ValueError('Unable to add handler %r: %s' % (h, e)) + + def common_logger_config(self, logger, config, incremental=False): + """ + Perform configuration which is common to root and non-root loggers. + """ + level = config.get('level', None) + if level is not None: + logger.setLevel(_checkLevel(level)) + if not incremental: + # Remove any existing handlers + for h in logger.handlers[:]: + logger.removeHandler(h) + handlers = config.get('handlers', None) + if handlers: + self.add_handlers(logger, handlers) + filters = config.get('filters', None) + if filters: + self.add_filters(logger, filters) + + def configure_logger(self, name, config, incremental=False): + """Configure a non-root logger from a dictionary.""" + logger = logging.getLogger(name) + self.common_logger_config(logger, config, incremental) + propagate = config.get('propagate', None) + if propagate is not None: + logger.propagate = propagate + + def configure_root(self, config, incremental=False): + """Configure a root logger from a dictionary.""" + root = logging.getLogger() + self.common_logger_config(root, config, incremental) + +dictConfigClass = DictConfigurator + + +def dictConfig(config): + """Configure logging using a dictionary.""" + dictConfigClass(config).configure() diff --git a/pip/compat/ordereddict.py b/pip/compat/ordereddict.py new file mode 100644 index 00000000000..6eb3ba47a43 --- /dev/null +++ b/pip/compat/ordereddict.py @@ -0,0 +1,129 @@ +# Copyright (c) 2009 Raymond Hettinger +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +# flake8: noqa + +from UserDict import DictMixin + +class OrderedDict(dict, DictMixin): + + def __init__(self, *args, **kwds): + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__end + except AttributeError: + self.clear() + self.update(*args, **kwds) + + def clear(self): + self.__end = end = [] + end += [None, end, end] # sentinel node for doubly linked list + self.__map = {} # key --> [key, prev, next] + dict.clear(self) + + def __setitem__(self, key, value): + if key not in self: + end = self.__end + curr = end[1] + curr[2] = end[1] = self.__map[key] = [key, curr, end] + dict.__setitem__(self, key, value) + + def __delitem__(self, key): + dict.__delitem__(self, key) + key, prev, next = self.__map.pop(key) + prev[2] = next + next[1] = prev + + def __iter__(self): + end = self.__end + curr = end[2] + while curr is not end: + yield curr[0] + curr = curr[2] + + def __reversed__(self): + end = self.__end + curr = end[1] + while curr is not end: + yield curr[0] + curr = curr[1] + + def popitem(self, last=True): + if not self: + raise KeyError('dictionary is empty') + if last: + key = reversed(self).next() + else: + key = iter(self).next() + value = self.pop(key) + return key, value + + def __reduce__(self): + items = [[k, self[k]] for k in self] + tmp = self.__map, self.__end + del self.__map, self.__end + inst_dict = vars(self).copy() + self.__map, self.__end = tmp + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def keys(self): + return list(self) + + setdefault = DictMixin.setdefault + update = DictMixin.update + pop = DictMixin.pop + values = DictMixin.values + items = DictMixin.items + iterkeys = DictMixin.iterkeys + itervalues = DictMixin.itervalues + iteritems = DictMixin.iteritems + + def __repr__(self): + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + + def copy(self): + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + d = cls() + for key in iterable: + d[key] = value + return d + + def __eq__(self, other): + if isinstance(other, OrderedDict): + if len(self) != len(other): + return False + for p, q in zip(self.items(), other.items()): + if p != q: + return False + return True + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self == other diff --git a/pip/download.py b/pip/download.py index b8cfb791ad2..f2728b92971 100644 --- a/pip/download.py +++ b/pip/download.py @@ -1,7 +1,10 @@ +from __future__ import absolute_import + import cgi import email.utils -import hashlib import getpass +import json +import logging import mimetypes import os import platform @@ -10,61 +13,115 @@ import sys import tempfile +try: + import ssl # noqa + HAS_TLS = True +except ImportError: + HAS_TLS = False + +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + import pip -from pip.backwardcompat import urllib, urlparse, raw_input from pip.exceptions import InstallationError, HashMismatch -from pip.util import (splitext, rmtree, format_size, display_path, - backup_dir, ask_path_exists, unpack_file, - create_download_cache_folder, cache_download) +from pip.models import PyPI +from pip.utils import (splitext, rmtree, format_size, display_path, + backup_dir, ask_path_exists, unpack_file, + ARCHIVE_EXTENSIONS, consume, call_subprocess) +from pip.utils.encoding import auto_decode +from pip.utils.filesystem import check_path_owner +from pip.utils.logging import indent_log +from pip.utils.setuptools_build import SETUPTOOLS_SHIM +from pip.utils.glibc import libc_ver +from pip.utils.ui import DownloadProgressBar, DownloadProgressSpinner +from pip.locations import write_delete_marker_file from pip.vcs import vcs -from pip.log import logger from pip._vendor import requests, six -from pip._vendor.requests.adapters import BaseAdapter +from pip._vendor.requests.adapters import BaseAdapter, HTTPAdapter from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth -from pip._vendor.requests.compat import IncompleteRead -from pip._vendor.requests.exceptions import InvalidURL, ChunkedEncodingError -from pip._vendor.requests.models import Response +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response from pip._vendor.requests.structures import CaseInsensitiveDict +from pip._vendor.requests.packages import urllib3 +from pip._vendor.cachecontrol import CacheControlAdapter +from pip._vendor.cachecontrol.caches import FileCache +from pip._vendor.lockfile import LockError +from pip._vendor.six.moves import xmlrpc_client + __all__ = ['get_file_content', 'is_url', 'url_to_path', 'path_to_url', 'is_archive_file', 'unpack_vcs_link', - 'unpack_file_url', 'is_vcs_url', 'is_file_url', 'unpack_http_url'] + 'unpack_file_url', 'is_vcs_url', 'is_file_url', + 'unpack_http_url', 'unpack_url'] -def user_agent(): - """Return a string representing the user agent.""" - _implementation = platform.python_implementation() - - if _implementation == 'CPython': - _implementation_version = platform.python_version() - elif _implementation == 'PyPy': - _implementation_version = '%s.%s.%s' % (sys.pypy_version_info.major, - sys.pypy_version_info.minor, - sys.pypy_version_info.micro) - if sys.pypy_version_info.releaselevel != 'final': - _implementation_version = ''.join([ - _implementation_version, - sys.pypy_version_info.releaselevel, - ]) - elif _implementation == 'Jython': - _implementation_version = platform.python_version() # Complete Guess - elif _implementation == 'IronPython': - _implementation_version = platform.python_version() # Complete Guess - else: - _implementation_version = 'Unknown' +logger = logging.getLogger(__name__) - try: - p_system = platform.system() - p_release = platform.release() - except IOError: - p_system = 'Unknown' - p_release = 'Unknown' - return " ".join(['pip/%s' % pip.__version__, - '%s/%s' % (_implementation, _implementation_version), - '%s/%s' % (p_system, p_release)]) +def user_agent(): + """ + Return a string representing the user agent. + """ + data = { + "installer": {"name": "pip", "version": pip.__version__}, + "python": platform.python_version(), + "implementation": { + "name": platform.python_implementation(), + }, + } + + if data["implementation"]["name"] == 'CPython': + data["implementation"]["version"] = platform.python_version() + elif data["implementation"]["name"] == 'PyPy': + if sys.pypy_version_info.releaselevel == 'final': + pypy_version_info = sys.pypy_version_info[:3] + else: + pypy_version_info = sys.pypy_version_info + data["implementation"]["version"] = ".".join( + [str(x) for x in pypy_version_info] + ) + elif data["implementation"]["name"] == 'Jython': + # Complete Guess + data["implementation"]["version"] = platform.python_version() + elif data["implementation"]["name"] == 'IronPython': + # Complete Guess + data["implementation"]["version"] = platform.python_version() + + if sys.platform.startswith("linux"): + distro = dict(filter( + lambda x: x[1], + zip(["name", "version", "id"], platform.linux_distribution()), + )) + libc = dict(filter( + lambda x: x[1], + zip(["lib", "version"], libc_ver()), + )) + if libc: + distro["libc"] = libc + if distro: + data["distro"] = distro + + if sys.platform.startswith("darwin") and platform.mac_ver()[0]: + data["distro"] = {"name": "OS X", "version": platform.mac_ver()[0]} + + if platform.system(): + data.setdefault("system", {})["name"] = platform.system() + + if platform.release(): + data.setdefault("system", {})["release"] = platform.release() + + if platform.machine(): + data["cpu"] = platform.machine() + + # Python 2.6 doesn't have ssl.OPENSSL_VERSION. + if HAS_TLS and sys.version_info[:2] > (2, 6): + data["openssl_version"] = ssl.OPENSSL_VERSION + + return "{data[installer][name]}/{data[installer][version]} {json}".format( + data=data, + json=json.dumps(data, separators=(",", ":"), sort_keys=True), + ) class MultiDomainBasicAuth(AuthBase): @@ -74,13 +131,13 @@ def __init__(self, prompting=True): self.passwords = {} def __call__(self, req): - parsed = urlparse.urlparse(req.url) + parsed = urllib_parse.urlparse(req.url) # Get the netloc without any embedded credentials - netloc = parsed.netloc.split("@", 1)[-1] + netloc = parsed.netloc.rsplit("@", 1)[-1] # Set the url of the request to the url without any credentials - req.url = urlparse.urlunparse(parsed[:1] + (netloc,) + parsed[2:]) + req.url = urllib_parse.urlunparse(parsed[:1] + (netloc,) + parsed[2:]) # Use any stored credentials that we have for this netloc username, password = self.passwords.get(netloc, (None, None)) @@ -107,14 +164,14 @@ def handle_401(self, resp, **kwargs): if resp.status_code != 401: return resp - # We are not able to prompt the user so simple return the response + # We are not able to prompt the user so simply return the response if not self.prompting: return resp - parsed = urlparse.urlparse(resp.url) + parsed = urllib_parse.urlparse(resp.url) # Prompt the user for a new username and password - username = raw_input("User for %s: " % parsed.netloc) + username = six.moves.input("User for %s: " % parsed.netloc) password = getpass.getpass("Password: ") # Store the new username and password to use for future requests @@ -143,69 +200,119 @@ def parse_credentials(self, netloc): return userinfo, None return None, None + def __nonzero__(self): + # needed in order to evaluate authentication object to False when we + # have no credentials, prevents failure to load .netrc files + return bool(self.passwords) + + def __bool__(self): + return self.__nonzero__() -class LocalFSResponse(object): - def __init__(self, fileobj): - self.fileobj = fileobj +class LocalFSAdapter(BaseAdapter): - def __getattr__(self, name): - return getattr(self.fileobj, name) + def send(self, request, stream=None, timeout=None, verify=None, cert=None, + proxies=None): + pathname = url_to_path(request.url) - def read(self, amt=None, decode_content=None, cache_content=False): - return self.fileobj.read(amt) + resp = Response() + resp.status_code = 200 + resp.url = request.url - # Insert Hacks to Make Cookie Jar work w/ Requests - @property - def _original_response(self): - class FakeMessage(object): - def getheaders(self, header): - return [] + try: + stats = os.stat(pathname) + except OSError as exc: + resp.status_code = 404 + resp.raw = exc + else: + modified = email.utils.formatdate(stats.st_mtime, usegmt=True) + content_type = mimetypes.guess_type(pathname)[0] or "text/plain" + resp.headers = CaseInsensitiveDict({ + "Content-Type": content_type, + "Content-Length": stats.st_size, + "Last-Modified": modified, + }) - def get_all(self, header, default): - return [] + resp.raw = open(pathname, "rb") + resp.close = resp.raw.close - class FakeResponse(object): - @property - def msg(self): - return FakeMessage() + return resp - return FakeResponse() + def close(self): + pass -class LocalFSAdapter(BaseAdapter): +class SafeFileCache(FileCache): + """ + A file based cache which is safe to use even when the target directory may + not be accessible or writable. + """ - def send(self, request, stream=None, timeout=None, verify=None, cert=None, - proxies=None): - parsed_url = urlparse.urlparse(request.url) + def __init__(self, *args, **kwargs): + super(SafeFileCache, self).__init__(*args, **kwargs) + + # Check to ensure that the directory containing our cache directory + # is owned by the user current executing pip. If it does not exist + # we will check the parent directory until we find one that does exist. + # If it is not owned by the user executing pip then we will disable + # the cache and log a warning. + if not check_path_owner(self.directory): + logger.warning( + "The directory '%s' or its parent directory is not owned by " + "the current user and the cache has been disabled. Please " + "check the permissions and owner of that directory. If " + "executing pip with sudo, you may want sudo's -H flag.", + self.directory, + ) - # We only work for requests with a host of localhost - if parsed_url.netloc.lower() != "localhost": - raise InvalidURL("Invalid URL %r: Only localhost is allowed" % - request.url) + # Set our directory to None to disable the Cache + self.directory = None - real_url = urlparse.urlunparse(parsed_url[:1] + ("",) + parsed_url[2:]) - pathname = url_to_path(real_url) + def get(self, *args, **kwargs): + # If we don't have a directory, then the cache should be a no-op. + if self.directory is None: + return - resp = Response() - resp.status_code = 200 - resp.url = real_url + try: + return super(SafeFileCache, self).get(*args, **kwargs) + except (LockError, OSError, IOError): + # We intentionally silence this error, if we can't access the cache + # then we can just skip caching and process the request as if + # caching wasn't enabled. + pass + + def set(self, *args, **kwargs): + # If we don't have a directory, then the cache should be a no-op. + if self.directory is None: + return - stats = os.stat(pathname) - modified = email.utils.formatdate(stats.st_mtime, usegmt=True) - resp.headers = CaseInsensitiveDict({ - "Content-Type": mimetypes.guess_type(pathname)[0] or "text/plain", - "Content-Length": stats.st_size, - "Last-Modified": modified, - }) + try: + return super(SafeFileCache, self).set(*args, **kwargs) + except (LockError, OSError, IOError): + # We intentionally silence this error, if we can't access the cache + # then we can just skip caching and process the request as if + # caching wasn't enabled. + pass + + def delete(self, *args, **kwargs): + # If we don't have a directory, then the cache should be a no-op. + if self.directory is None: + return - resp.raw = LocalFSResponse(open(pathname, "rb")) - resp.close = resp.raw.close + try: + return super(SafeFileCache, self).delete(*args, **kwargs) + except (LockError, OSError, IOError): + # We intentionally silence this error, if we can't access the cache + # then we can just skip caching and process the request as if + # caching wasn't enabled. + pass - return resp - def close(self): - pass +class InsecureHTTPAdapter(HTTPAdapter): + + def cert_verify(self, conn, url, verify, cert): + conn.cert_reqs = 'CERT_NONE' + conn.ca_certs = None class PipSession(requests.Session): @@ -213,6 +320,10 @@ class PipSession(requests.Session): timeout = None def __init__(self, *args, **kwargs): + retries = kwargs.pop("retries", 0) + cache = kwargs.pop("cache", None) + insecure_hosts = kwargs.pop("insecure_hosts", []) + super(PipSession, self).__init__(*args, **kwargs) # Attach our User Agent to the request @@ -221,15 +332,54 @@ def __init__(self, *args, **kwargs): # Attach our Authentication handler to the session self.auth = MultiDomainBasicAuth() + # Create our urllib3.Retry instance which will allow us to customize + # how we handle retries. + retries = urllib3.Retry( + # Set the total number of retries that a particular request can + # have. + total=retries, + + # A 503 error from PyPI typically means that the Fastly -> Origin + # connection got interrupted in some way. A 503 error in general + # is typically considered a transient error so we'll go ahead and + # retry it. + status_forcelist=[503], + + # Add a small amount of back off between failed requests in + # order to prevent hammering the service. + backoff_factor=0.25, + ) + + # We want to _only_ cache responses on securely fetched origins. We do + # this because we can't validate the response of an insecurely fetched + # origin, and we don't want someone to be able to poison the cache and + # require manual eviction from the cache to fix it. + if cache: + secure_adapter = CacheControlAdapter( + cache=SafeFileCache(cache, use_dir_lock=True), + max_retries=retries, + ) + else: + secure_adapter = HTTPAdapter(max_retries=retries) + + # Our Insecure HTTPAdapter disables HTTPS validation. It does not + # support caching (see above) so we'll use it for all http:// URLs as + # well as any https:// host that we've marked as ignoring TLS errors + # for. + insecure_adapter = InsecureHTTPAdapter(max_retries=retries) + + self.mount("https://", secure_adapter) + self.mount("http://", insecure_adapter) + # Enable file:// urls self.mount("file://", LocalFSAdapter()) - def request(self, method, url, *args, **kwargs): - # Make file:// urls not fail due to lack of a hostname - parsed = urlparse.urlparse(url) - if parsed.scheme == "file": - url = urlparse.urlunparse(parsed[:1] + ("localhost",) + parsed[2:]) + # We want to use a non-validating adapter for any requests which are + # deemed insecure. + for host in insecure_hosts: + self.mount("https://{0}/".format(host), insecure_adapter) + def request(self, method, url, *args, **kwargs): # Allow setting a default timeout on a session kwargs.setdefault("timeout", self.timeout) @@ -241,13 +391,15 @@ def get_file_content(url, comes_from=None, session=None): """Gets the content of a file; it may be a filename, file: URL, or http: URL. Returns (location, content). Content is unicode.""" if session is None: - session = PipSession() + raise TypeError( + "get_file_content() missing 1 required keyword argument: 'session'" + ) match = _scheme_re.search(url) if match: scheme = match.group(1).lower() - if (scheme == 'file' and comes_from - and comes_from.startswith('http')): + if (scheme == 'file' and comes_from and + comes_from.startswith('http')): raise InstallationError( 'Requirements file %s references URL %s, which is local' % (comes_from, url)) @@ -257,27 +409,22 @@ def get_file_content(url, comes_from=None, session=None): match = _url_slash_drive_re.match(path) if match: path = match.group(1) + ':' + path.split('|', 1)[1] - path = urllib.unquote(path) + path = urllib_parse.unquote(path) if path.startswith('/'): path = '/' + path.lstrip('/') url = path else: - ## FIXME: catch some errors + # FIXME: catch some errors resp = session.get(url) resp.raise_for_status() - - if six.PY3: - return resp.url, resp.text - else: - return resp.url, resp.content + return resp.url, resp.text try: - f = open(url) - content = f.read() - except IOError: - e = sys.exc_info()[1] - raise InstallationError('Could not open requirements file: %s' % str(e)) - else: - f.close() + with open(url, 'rb') as f: + content = auto_decode(f.read()) + except IOError as exc: + raise InstallationError( + 'Could not open requirements file: %s' % str(exc) + ) return url, content @@ -299,17 +446,15 @@ def url_to_path(url): """ assert url.startswith('file:'), ( "You can only turn file: urls into filenames (not %r)" % url) - path = url[len('file:'):].lstrip('/') - path = urllib.unquote(path) - if _url_drive_re.match(path): - path = path[0] + ':' + path[2:] - else: - path = '/' + path - return path + _, netloc, path, _, _ = urllib_parse.urlsplit(url) -_drive_re = re.compile('^([a-z]):', re.I) -_url_drive_re = re.compile('^([a-z])[:|]', re.I) + # if we have a UNC path, prepend UNC share notation + if netloc: + netloc = '\\\\' + netloc + + path = urllib_request.url2pathname(netloc + path) + return path def path_to_url(path): @@ -318,30 +463,21 @@ def path_to_url(path): quoted path parts. """ path = os.path.normpath(os.path.abspath(path)) - drive, path = os.path.splitdrive(path) - filepath = path.split(os.path.sep) - url = '/'.join([urllib.quote(part) for part in filepath]) - if not drive: - url = url.lstrip('/') - return 'file:///' + drive + url + url = urllib_parse.urljoin('file:', urllib_request.pathname2url(path)) + return url def is_archive_file(name): """Return True if `name` is a considered as an archive file.""" - archives = ('.zip', '.tar.gz', '.tar.bz2', '.tgz', '.tar', '.pybundle', - '.whl') ext = splitext(name)[1].lower() - if ext in archives: + if ext in ARCHIVE_EXTENSIONS: return True return False -def unpack_vcs_link(link, location, only_download=False): +def unpack_vcs_link(link, location): vcs_backend = _get_used_vcs_backend(link) - if only_download: - vcs_backend.export(location) - else: - vcs_backend.unpack(location) + vcs_backend.unpack(location) def _get_used_vcs_backend(link): @@ -359,95 +495,118 @@ def is_file_url(link): return link.url.lower().startswith('file:') -def _check_hash(download_hash, link): - if download_hash.digest_size != hashlib.new(link.hash_name).digest_size: - logger.fatal("Hash digest size of the package %d (%s) doesn't match the expected hash name %s!" - % (download_hash.digest_size, link, link.hash_name)) - raise HashMismatch('Hash name mismatch for package %s' % link) - if download_hash.hexdigest() != link.hash: - logger.fatal("Hash of the package %s (%s) doesn't match the expected hash %s!" - % (link, download_hash.hexdigest(), link.hash)) - raise HashMismatch('Bad %s hash for package %s' % (link.hash_name, link)) +def is_dir_url(link): + """Return whether a file:// Link points to a directory. + ``link`` must not have any other scheme but file://. Call is_file_url() + first. -def _get_hash_from_file(target_file, link): - try: - download_hash = hashlib.new(link.hash_name) - except (ValueError, TypeError): - logger.warn("Unsupported hash name %s for package %s" % (link.hash_name, link)) - return None - - fp = open(target_file, 'rb') - while True: - chunk = fp.read(4096) - if not chunk: - break - download_hash.update(chunk) - fp.close() - return download_hash - - -def _download_url(resp, link, temp_location): - fp = open(temp_location, 'wb') - download_hash = None - if link.hash and link.hash_name: - try: - download_hash = hashlib.new(link.hash_name) - except ValueError: - logger.warn("Unsupported hash name %s for package %s" % (link.hash_name, link)) + """ + link_path = url_to_path(link.url_without_fragment) + return os.path.isdir(link_path) + + +def _progress_indicator(iterable, *args, **kwargs): + return iterable + + +def _download_url(resp, link, content_file, hashes): try: total_length = int(resp.headers['content-length']) except (ValueError, KeyError, TypeError): total_length = 0 - downloaded = 0 - show_progress = total_length > 40 * 1000 or not total_length + + cached_resp = getattr(resp, "from_cache", False) + + if logger.getEffectiveLevel() > logging.INFO: + show_progress = False + elif cached_resp: + show_progress = False + elif total_length > (40 * 1000): + show_progress = True + elif not total_length: + show_progress = True + else: + show_progress = False + show_url = link.show_url - try: - if show_progress: - ## FIXME: the URL can get really long in this message: - if total_length: - logger.start_progress('Downloading %s (%s): ' % (show_url, format_size(total_length))) - else: - logger.start_progress('Downloading %s (unknown size): ' % show_url) + + def resp_read(chunk_size): + try: + # Special case for urllib3. + for chunk in resp.raw.stream( + chunk_size, + # We use decode_content=False here because we don't + # want urllib3 to mess with the raw bytes we get + # from the server. If we decompress inside of + # urllib3 then we cannot verify the checksum + # because the checksum will be of the compressed + # file. This breakage will only occur if the + # server adds a Content-Encoding header, which + # depends on how the server was configured: + # - Some servers will notice that the file isn't a + # compressible file and will leave the file alone + # and with an empty Content-Encoding + # - Some servers will notice that the file is + # already compressed and will leave the file + # alone and will add a Content-Encoding: gzip + # header + # - Some servers won't notice anything at all and + # will take a file that's already been compressed + # and compress it again and set the + # Content-Encoding: gzip header + # + # By setting this not to decode automatically we + # hope to eliminate problems with the second case. + decode_content=False): + yield chunk + except AttributeError: + # Standard file-like object. + while True: + chunk = resp.raw.read(chunk_size) + if not chunk: + break + yield chunk + + def written_chunks(chunks): + for chunk in chunks: + content_file.write(chunk) + yield chunk + + progress_indicator = _progress_indicator + + if link.netloc == PyPI.netloc: + url = show_url + else: + url = link.url_without_fragment + + if show_progress: # We don't show progress on cached responses + if total_length: + logger.info("Downloading %s (%s)", url, format_size(total_length)) + progress_indicator = DownloadProgressBar(max=total_length).iter else: - logger.notify('Downloading %s' % show_url) - logger.info('Downloading from URL %s' % link) + logger.info("Downloading %s", url) + progress_indicator = DownloadProgressSpinner().iter + elif cached_resp: + logger.info("Using cached %s", url) + else: + logger.info("Downloading %s", url) + + logger.debug('Downloading from URL %s', link) + + downloaded_chunks = written_chunks( + progress_indicator( + resp_read(CONTENT_CHUNK_SIZE), + CONTENT_CHUNK_SIZE + ) + ) + if hashes: + hashes.check_against_chunks(downloaded_chunks) + else: + consume(downloaded_chunks) - def resp_read(chunk_size): - try: - # Special case for urllib3. - try: - for chunk in resp.raw.stream( - chunk_size, decode_content=False): - yield chunk - except IncompleteRead as e: - raise ChunkedEncodingError(e) - except AttributeError: - # Standard file-like object. - while True: - chunk = resp.raw.read(chunk_size) - if not chunk: - break - yield chunk - - for chunk in resp_read(4096): - downloaded += len(chunk) - if show_progress: - if not total_length: - logger.show_progress('%s' % format_size(downloaded)) - else: - 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) - fp.close() - finally: - if show_progress: - logger.end_progress('%s downloaded' % format_size(downloaded)) - return download_hash - - -def _copy_file(filename, location, content_type, link): + +def _copy_file(filename, location, link): copy = True download_location = os.path.join(location, link.filename) if os.path.exists(download_location): @@ -457,179 +616,94 @@ def _copy_file(filename, location, content_type, link): if response == 'i': copy = False elif response == 'w': - logger.warn('Deleting %s' % display_path(download_location)) + logger.warning('Deleting %s', display_path(download_location)) os.remove(download_location) elif response == 'b': dest_file = backup_dir(download_location) - logger.warn('Backing up %s to %s' - % (display_path(download_location), display_path(dest_file))) + logger.warning( + 'Backing up %s to %s', + display_path(download_location), + display_path(dest_file), + ) shutil.move(download_location, dest_file) if copy: shutil.copy(filename, download_location) - logger.notify('Saved %s' % display_path(download_location)) + logger.info('Saved %s', display_path(download_location)) -def unpack_http_url(link, location, download_cache, download_dir=None, - session=None): +def unpack_http_url(link, location, download_dir=None, + session=None, hashes=None): if session is None: - session = PipSession() + raise TypeError( + "unpack_http_url() missing 1 required keyword argument: 'session'" + ) temp_dir = tempfile.mkdtemp('-unpack', 'pip-') - temp_location = None - target_url = link.url.split('#', 1)[0] - already_cached = False - cache_file = None - cache_content_type_file = None - download_hash = None - - # If a download cache is specified, is the file cached there? - if download_cache: - cache_file = os.path.join(download_cache, - urllib.quote(target_url, '')) - cache_content_type_file = cache_file + '.content-type' - already_cached = ( - os.path.exists(cache_file) and - os.path.exists(cache_content_type_file) - ) - if not os.path.isdir(download_cache): - create_download_cache_folder(download_cache) # If a download dir is specified, is the file already downloaded there? - already_downloaded = None + already_downloaded_path = None if download_dir: - already_downloaded = os.path.join(download_dir, link.filename) - if not os.path.exists(already_downloaded): - already_downloaded = None - - # If already downloaded, does it's hash match? - if already_downloaded: - temp_location = already_downloaded - content_type = mimetypes.guess_type(already_downloaded)[0] - logger.notify('File was already downloaded %s' % already_downloaded) - if link.hash: - download_hash = _get_hash_from_file(temp_location, link) - try: - _check_hash(download_hash, link) - except HashMismatch: - logger.warn( - 'Previously-downloaded file %s has bad hash, ' - 're-downloading.' % temp_location - ) - temp_location = None - os.unlink(already_downloaded) - already_downloaded = None - - # If not a valid download, let's confirm the cached file is valid - if already_cached and not temp_location: - with open(cache_content_type_file) as fp: - content_type = fp.read().strip() - temp_location = cache_file - logger.notify('Using download cache from %s' % cache_file) - if link.hash and link.hash_name: - download_hash = _get_hash_from_file(cache_file, link) - try: - _check_hash(download_hash, link) - except HashMismatch: - logger.warn( - 'Cached file %s has bad hash, ' - 're-downloading.' % temp_location - ) - temp_location = None - os.unlink(cache_file) - os.unlink(cache_content_type_file) - already_cached = False - - # We don't have either a cached or a downloaded copy - # let's download to a tmp dir - if not temp_location: - try: - resp = session.get(target_url, stream=True) - resp.raise_for_status() - except requests.HTTPError as exc: - logger.fatal("HTTP error %s while getting %s" % - (exc.response.status_code, link)) - raise + already_downloaded_path = _check_download_dir(link, + download_dir, + hashes) - content_type = resp.headers.get('content-type', '') - filename = link.filename # fallback - # Have a look at the Content-Disposition header for a better guess - content_disposition = resp.headers.get('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) - if ext: - filename += ext - if not ext and link.url != resp.url: - ext = os.path.splitext(resp.url)[1] - if ext: - filename += ext - temp_location = os.path.join(temp_dir, filename) - download_hash = _download_url(resp, link, temp_location) - if link.hash and link.hash_name: - _check_hash(download_hash, link) - - # a download dir is specified; let's copy the archive there - if download_dir and not already_downloaded: - _copy_file(temp_location, download_dir, content_type, link) + if already_downloaded_path: + from_path = already_downloaded_path + content_type = mimetypes.guess_type(from_path)[0] + else: + # let's download to a tmp dir + from_path, content_type = _download_http_url(link, + session, + temp_dir, + hashes) # unpack the archive to the build dir location. even when only downloading # archives, they have to be unpacked to parse dependencies - unpack_file(temp_location, location, content_type, link) - - # if using a download cache, cache it, if needed - if cache_file and not already_cached: - cache_download(cache_file, temp_location, content_type) + unpack_file(from_path, location, content_type, link) - if not (already_cached or already_downloaded): - os.unlink(temp_location) + # a download dir is specified; let's copy the archive there + if download_dir and not already_downloaded_path: + _copy_file(from_path, download_dir, link) - os.rmdir(temp_dir) + if not already_downloaded_path: + os.unlink(from_path) + rmtree(temp_dir) -def unpack_file_url(link, location, download_dir=None): +def unpack_file_url(link, location, download_dir=None, hashes=None): + """Unpack link into location. + If download_dir is provided and link points to a file, make a copy + of the link file inside download_dir. + """ link_path = url_to_path(link.url_without_fragment) - already_downloaded = False # If it's a url to a local directory - if os.path.isdir(link_path): + if is_dir_url(link): if os.path.isdir(location): rmtree(location) shutil.copytree(link_path, location, symlinks=True) + if download_dir: + logger.info('Link is a directory, ignoring download_dir') return - # if link has a hash, let's confirm it matches - if link.hash: - link_path_hash = _get_hash_from_file(link_path, link) - _check_hash(link_path_hash, link) + # If --require-hashes is off, `hashes` is either empty, the + # link's embedded hash, or MissingHashes; it is required to + # match. If --require-hashes is on, we are satisfied by any + # hash in `hashes` matching: a URL-based or an option-based + # one; no internet-sourced hash will be in `hashes`. + if hashes: + hashes.check_against_path(link_path) # If a download dir is specified, is the file already there and valid? + already_downloaded_path = None if download_dir: - download_path = os.path.join(download_dir, link.filename) - if os.path.exists(download_path): - content_type = mimetypes.guess_type(download_path)[0] - logger.notify('File was already downloaded %s' % download_path) - if link.hash: - download_hash = _get_hash_from_file(download_path, link) - try: - _check_hash(download_hash, link) - already_downloaded = True - except HashMismatch: - logger.warn( - 'Previously-downloaded file %s has bad hash, ' - 're-downloading.' % link_path - ) - os.unlink(download_path) - else: - already_downloaded = True - - if already_downloaded: - from_path = download_path + already_downloaded_path = _check_download_dir(link, + download_dir, + hashes) + + if already_downloaded_path: + from_path = already_downloaded_path else: from_path = link_path @@ -640,5 +714,192 @@ def unpack_file_url(link, location, download_dir=None): unpack_file(from_path, location, content_type, link) # a download dir is specified and not already downloaded - if download_dir and not already_downloaded: - _copy_file(from_path, download_dir, content_type, link) + if download_dir and not already_downloaded_path: + _copy_file(from_path, download_dir, link) + + +def _copy_dist_from_dir(link_path, location): + """Copy distribution files in `link_path` to `location`. + + Invoked when user requests to install a local directory. E.g.: + + pip install . + pip install ~/dev/git-repos/python-prompt-toolkit + + """ + + # Note: This is currently VERY SLOW if you have a lot of data in the + # directory, because it copies everything with `shutil.copytree`. + # What it should really do is build an sdist and install that. + # See https://github.com/pypa/pip/issues/2195 + + if os.path.isdir(location): + rmtree(location) + + # build an sdist + setup_py = 'setup.py' + sdist_args = [sys.executable] + sdist_args.append('-c') + sdist_args.append(SETUPTOOLS_SHIM % setup_py) + sdist_args.append('sdist') + sdist_args += ['--dist-dir', location] + logger.info('Running setup.py sdist for %s', link_path) + + with indent_log(): + call_subprocess(sdist_args, cwd=link_path, show_stdout=False) + + # unpack sdist into `location` + sdist = os.path.join(location, os.listdir(location)[0]) + logger.info('Unpacking sdist %s into %s', sdist, location) + unpack_file(sdist, location, content_type=None, link=None) + + +class PipXmlrpcTransport(xmlrpc_client.Transport): + """Provide a `xmlrpclib.Transport` implementation via a `PipSession` + object. + """ + + def __init__(self, index_url, session, use_datetime=False): + xmlrpc_client.Transport.__init__(self, use_datetime) + index_parts = urllib_parse.urlparse(index_url) + self._scheme = index_parts.scheme + self._session = session + + def request(self, host, handler, request_body, verbose=False): + parts = (self._scheme, host, handler, None, None, None) + url = urllib_parse.urlunparse(parts) + try: + headers = {'Content-Type': 'text/xml'} + response = self._session.post(url, data=request_body, + headers=headers, stream=True) + response.raise_for_status() + self.verbose = verbose + return self.parse_response(response.raw) + except requests.HTTPError as exc: + logger.critical( + "HTTP error %s while getting %s", + exc.response.status_code, url, + ) + raise + + +def unpack_url(link, location, download_dir=None, + only_download=False, session=None, hashes=None): + """Unpack link. + If link is a VCS link: + if only_download, export into download_dir and ignore location + else unpack into location + for other types of link: + - unpack into location + - if download_dir, copy the file into download_dir + - if only_download, mark location for deletion + + :param hashes: A Hashes object, one of whose embedded hashes must match, + or HashMismatch will be raised. If the Hashes is empty, no matches are + required, and unhashable types of requirements (like VCS ones, which + would ordinarily raise HashUnsupported) are allowed. + """ + # non-editable vcs urls + if is_vcs_url(link): + unpack_vcs_link(link, location) + + # file urls + elif is_file_url(link): + unpack_file_url(link, location, download_dir, hashes=hashes) + + # http urls + else: + if session is None: + session = PipSession() + + unpack_http_url( + link, + location, + download_dir, + session, + hashes=hashes + ) + if only_download: + write_delete_marker_file(location) + + +def _download_http_url(link, session, temp_dir, hashes): + """Download link url into temp_dir using provided session""" + target_url = link.url.split('#', 1)[0] + try: + resp = session.get( + target_url, + # We use Accept-Encoding: identity here because requests + # defaults to accepting compressed responses. This breaks in + # a variety of ways depending on how the server is configured. + # - Some servers will notice that the file isn't a compressible + # file and will leave the file alone and with an empty + # Content-Encoding + # - Some servers will notice that the file is already + # compressed and will leave the file alone and will add a + # Content-Encoding: gzip header + # - Some servers won't notice anything at all and will take + # a file that's already been compressed and compress it again + # and set the Content-Encoding: gzip header + # By setting this to request only the identity encoding We're + # hoping to eliminate the third case. Hopefully there does not + # exist a server which when given a file will notice it is + # already compressed and that you're not asking for a + # compressed file and will then decompress it before sending + # because if that's the case I don't think it'll ever be + # possible to make this work. + headers={"Accept-Encoding": "identity"}, + stream=True, + ) + resp.raise_for_status() + except requests.HTTPError as exc: + logger.critical( + "HTTP error %s while getting %s", exc.response.status_code, link, + ) + raise + + content_type = resp.headers.get('content-type', '') + filename = link.filename # fallback + # Have a look at the Content-Disposition header for a better guess + content_disposition = resp.headers.get('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) + if ext: + filename += ext + if not ext and link.url != resp.url: + ext = os.path.splitext(resp.url)[1] + if ext: + filename += ext + file_path = os.path.join(temp_dir, filename) + with open(file_path, 'wb') as content_file: + _download_url(resp, link, content_file, hashes) + return file_path, content_type + + +def _check_download_dir(link, download_dir, hashes): + """ Check download_dir for previously downloaded file with correct hash + If a correct file is found return its path else None + """ + download_path = os.path.join(download_dir, link.filename) + if os.path.exists(download_path): + # If already downloaded, does its hash match? + logger.info('File was already downloaded %s', download_path) + if hashes: + try: + hashes.check_against_path(download_path) + except HashMismatch: + logger.warning( + 'Previously-downloaded file %s has bad hash. ' + 'Re-downloading.', + download_path + ) + os.unlink(download_path) + return None + return download_path + return None diff --git a/pip/exceptions.py b/pip/exceptions.py index febebfb2903..a529e40a89b 100644 --- a/pip/exceptions.py +++ b/pip/exceptions.py @@ -1,4 +1,9 @@ """Exceptions used throughout package""" +from __future__ import absolute_import + +from itertools import chain, groupby, repeat + +from pip._vendor.six import iteritems class PipError(Exception): @@ -17,9 +22,13 @@ class DistributionNotFound(InstallationError): """Raised when a distribution cannot be found to satisfy a requirement""" +class RequirementsFileParseError(InstallationError): + """Raised when a general error occurs parsing a requirements file line.""" + + class BestVersionAlreadyInstalled(PipError): """Raised when the most up-to-date version of a package is already - installed. """ + installed.""" class BadCommand(PipError): @@ -34,13 +43,197 @@ class PreviousBuildDirError(PipError): """Raised when there's a previous conflicting build directory""" -class HashMismatch(InstallationError): - """Distribution file hash values don't match.""" - - class InvalidWheelFilename(InstallationError): """Invalid wheel filename.""" class UnsupportedWheel(InstallationError): """Unsupported wheel.""" + + +class HashErrors(InstallationError): + """Multiple HashError instances rolled into one for reporting""" + + def __init__(self): + self.errors = [] + + def append(self, error): + self.errors.append(error) + + def __str__(self): + lines = [] + self.errors.sort(key=lambda e: e.order) + for cls, errors_of_cls in groupby(self.errors, lambda e: e.__class__): + lines.append(cls.head) + lines.extend(e.body() for e in errors_of_cls) + if lines: + return '\n'.join(lines) + + def __nonzero__(self): + return bool(self.errors) + + def __bool__(self): + return self.__nonzero__() + + +class HashError(InstallationError): + """ + A failure to verify a package against known-good hashes + + :cvar order: An int sorting hash exception classes by difficulty of + recovery (lower being harder), so the user doesn't bother fretting + about unpinned packages when he has deeper issues, like VCS + dependencies, to deal with. Also keeps error reports in a + deterministic order. + :cvar head: A section heading for display above potentially many + exceptions of this kind + :ivar req: The InstallRequirement that triggered this error. This is + pasted on after the exception is instantiated, because it's not + typically available earlier. + + """ + req = None + head = '' + + def body(self): + """Return a summary of me for display under the heading. + + This default implementation simply prints a description of the + triggering requirement. + + :param req: The InstallRequirement that provoked this error, with + populate_link() having already been called + + """ + return ' %s' % self._requirement_name() + + def __str__(self): + return '%s\n%s' % (self.head, self.body()) + + def _requirement_name(self): + """Return a description of the requirement that triggered me. + + This default implementation returns long description of the req, with + line numbers + + """ + return str(self.req) if self.req else 'unknown package' + + +class VcsHashUnsupported(HashError): + """A hash was provided for a version-control-system-based requirement, but + we don't have a method for hashing those.""" + + order = 0 + head = ("Can't verify hashes for these requirements because we don't " + "have a way to hash version control repositories:") + + +class DirectoryUrlHashUnsupported(HashError): + """A hash was provided for a version-control-system-based requirement, but + we don't have a method for hashing those.""" + + order = 1 + head = ("Can't verify hashes for these file:// requirements because they " + "point to directories:") + + +class HashMissing(HashError): + """A hash was needed for a requirement but is absent.""" + + order = 2 + head = ('Hashes are required in --require-hashes mode, but they are ' + 'missing from some requirements. Here is a list of those ' + 'requirements along with the hashes their downloaded archives ' + 'actually had. Add lines like these to your requirements files to ' + 'prevent tampering. (If you did not enable --require-hashes ' + 'manually, note that it turns on automatically when any package ' + 'has a hash.)') + + def __init__(self, gotten_hash): + """ + :param gotten_hash: The hash of the (possibly malicious) archive we + just downloaded + """ + self.gotten_hash = gotten_hash + + def body(self): + from pip.utils.hashes import FAVORITE_HASH # Dodge circular import. + + package = None + if self.req: + # In the case of URL-based requirements, display the original URL + # seen in the requirements file rather than the package name, + # so the output can be directly copied into the requirements file. + package = (self.req.original_link if self.req.original_link + # In case someone feeds something downright stupid + # to InstallRequirement's constructor. + else getattr(self.req, 'req', None)) + return ' %s --hash=%s:%s' % (package or 'unknown package', + FAVORITE_HASH, + self.gotten_hash) + + +class HashUnpinned(HashError): + """A requirement had a hash specified but was not pinned to a specific + version.""" + + order = 3 + head = ('In --require-hashes mode, all requirements must have their ' + 'versions pinned with ==. These do not:') + + +class HashMismatch(HashError): + """ + Distribution file hash values don't match. + + :ivar package_name: The name of the package that triggered the hash + mismatch. Feel free to write to this after the exception is raise to + improve its error message. + + """ + order = 4 + head = ('THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS ' + 'FILE. If you have updated the package versions, please update ' + 'the hashes. Otherwise, examine the package contents carefully; ' + 'someone may have tampered with them.') + + def __init__(self, allowed, gots): + """ + :param allowed: A dict of algorithm names pointing to lists of allowed + hex digests + :param gots: A dict of algorithm names pointing to hashes we + actually got from the files under suspicion + """ + self.allowed = allowed + self.gots = gots + + def body(self): + return ' %s:\n%s' % (self._requirement_name(), + self._hash_comparison()) + + def _hash_comparison(self): + """ + Return a comparison of actual and expected hash values. + + Example:: + + Expected sha256 abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde + or 123451234512345123451234512345123451234512345 + Got bcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdef + + """ + def hash_then_or(hash_name): + # For now, all the decent hashes have 6-char names, so we can get + # away with hard-coding space literals. + return chain([hash_name], repeat(' or')) + + lines = [] + for hash_name, expecteds in iteritems(self.allowed): + prefix = hash_then_or(hash_name) + lines.extend((' Expected %s %s' % (next(prefix), e)) + for e in expecteds) + lines.append(' Got %s\n' % + self.gots[hash_name].hexdigest()) + prefix = ' or' + return '\n'.join(lines) diff --git a/pip/index.py b/pip/index.py index 46916c19cf9..f8a298e9f0d 100644 --- a/pip/index.py +++ b/pip/index.py @@ -1,101 +1,186 @@ """Routines related to PyPI, indexes""" +from __future__ import absolute_import +import logging +import cgi +from collections import namedtuple +import itertools import sys import os import re import mimetypes import posixpath - -from pip.log import logger -from pip.util import Inf, normalize_name, splitext, is_prerelease -from pip.exceptions import (DistributionNotFound, BestVersionAlreadyInstalled, - InstallationError, InvalidWheelFilename, UnsupportedWheel) -from pip.backwardcompat import urlparse, url2pathname -from pip.download import PipSession, url_to_path, path_to_url +import warnings + +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + +from pip.compat import ipaddress +from pip.utils import ( + cached_property, splitext, normalize_path, + ARCHIVE_EXTENSIONS, SUPPORTED_EXTENSIONS, +) +from pip.utils.deprecation import RemovedInPip9Warning, RemovedInPip10Warning +from pip.utils.logging import indent_log +from pip.exceptions import ( + DistributionNotFound, BestVersionAlreadyInstalled, InvalidWheelFilename, + UnsupportedWheel, +) +from pip.download import HAS_TLS, is_url, path_to_url, url_to_path from pip.wheel import Wheel, wheel_ext -from pip.pep425tags import supported_tags, supported_tags_noarch, get_platform -from pip._vendor import html5lib, requests, pkg_resources +from pip.pep425tags import supported_tags +from pip._vendor import html5lib, requests, six +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.packaging.utils import canonicalize_name from pip._vendor.requests.exceptions import SSLError -__all__ = ['PackageFinder'] +__all__ = ['FormatControl', 'fmt_ctl_handle_mutual_exclude', 'PackageFinder'] + + +SECURE_ORIGINS = [ + # protocol, hostname, port + # Taken from Chrome's list of secure origins (See: http://bit.ly/1qrySKC) + ("https", "*", "*"), + ("*", "localhost", "*"), + ("*", "127.0.0.0/8", "*"), + ("*", "::1/128", "*"), + ("file", "*", None), + # ssh is always secure. + ("ssh", "*", "*"), +] + +logger = logging.getLogger(__name__) -DEFAULT_MIRROR_HOSTNAME = "last.pypi.python.org" -INSECURE_SCHEMES = { - "http": ["https"], -} +class InstallationCandidate(object): + + def __init__(self, project, version, location): + self.project = project + self.version = parse_version(version) + self.location = location + self._key = (self.project, self.version, self.location) + + def __repr__(self): + return "".format( + self.project, self.version, self.location, + ) + + def __hash__(self): + return hash(self._key) + + def __lt__(self, other): + return self._compare(other, lambda s, o: s < o) + + def __le__(self, other): + return self._compare(other, lambda s, o: s <= o) + + def __eq__(self, other): + return self._compare(other, lambda s, o: s == o) + + def __ge__(self, other): + return self._compare(other, lambda s, o: s >= o) + + def __gt__(self, other): + return self._compare(other, lambda s, o: s > o) + + def __ne__(self, other): + return self._compare(other, lambda s, o: s != o) + + def _compare(self, other, method): + if not isinstance(other, InstallationCandidate): + return NotImplemented + + return method(self._key, other._key) class PackageFinder(object): """This finds packages. This is meant to match easy_install's technique for looking for - packages, by reading pages and looking for appropriate links + packages, by reading pages and looking for appropriate links. """ - def __init__(self, find_links, index_urls, - use_wheel=True, allow_external=[], allow_unverified=[], - allow_all_external=False, allow_all_prereleases=False, - process_dependency_links=False, session=None): - self.find_links = find_links - self.index_urls = index_urls - self.dependency_links = [] - self.cache = PageCache() - # These are boring links that have already been logged somehow: - self.logged_links = set() - - self.use_wheel = use_wheel + def __init__(self, find_links, index_urls, allow_all_prereleases=False, + trusted_hosts=None, process_dependency_links=False, + session=None, format_control=None): + """Create a PackageFinder. - # Do we allow (safe and verifiable) externally hosted files? - self.allow_external = set(normalize_name(n) for n in allow_external) + :param format_control: A FormatControl object or None. Used to control + the selection of source packages / binary packages when consulting + the index and links. + """ + if session is None: + raise TypeError( + "PackageFinder() missing 1 required keyword argument: " + "'session'" + ) - # Which names are allowed to install insecure and unverifiable files? - self.allow_unverified = set( - normalize_name(n) for n in allow_unverified - ) + # Build find_links. If an argument starts with ~, it may be + # a local file relative to a home directory. So try normalizing + # it and if it exists, use the normalized version. + # This is deliberately conservative - it might be fine just to + # blindly normalize anything starting with a ~... + self.find_links = [] + for link in find_links: + if link.startswith('~'): + new_link = normalize_path(link) + if os.path.exists(new_link): + link = new_link + self.find_links.append(link) - # Anything that is allowed unverified is also allowed external - self.allow_external |= self.allow_unverified + self.index_urls = index_urls + self.dependency_links = [] - # Do we allow all (safe and verifiable) externally hosted files? - self.allow_all_external = allow_all_external + # These are boring links that have already been logged somehow: + self.logged_links = set() - # Stores if we ignored any external links so that we can instruct - # end users how to install them if no distributions are available - self.need_warn_external = False + self.format_control = format_control or FormatControl(set(), set()) - # Stores if we ignored any unsafe links so that we can instruct - # end users how to install them if no distributions are available - self.need_warn_unverified = False + # Domains that we won't emit warnings for when not using HTTPS + self.secure_origins = [ + ("*", host, "*") + for host in (trusted_hosts if trusted_hosts else []) + ] # Do we want to allow _all_ pre-releases? self.allow_all_prereleases = allow_all_prereleases # Do we process dependency links? self.process_dependency_links = process_dependency_links - self._have_warned_dependency_links = False # The Session we'll use to make requests - self.session = session or PipSession() + self.session = session + + # If we don't have TLS enabled, then WARN if anyplace we're looking + # relies on TLS. + if not HAS_TLS: + for link in itertools.chain(self.index_urls, self.find_links): + parsed = urllib_parse.urlparse(link) + if parsed.scheme == "https": + logger.warning( + "pip is configured with locations that require " + "TLS/SSL, however the ssl module in Python is not " + "available." + ) + break def add_dependency_links(self, links): - ## FIXME: this shouldn't be global list this, it should only - ## apply to requirements of the package that specifies the - ## dependency_links value - ## FIXME: also, we should track comes_from (i.e., use Link) + # # FIXME: this shouldn't be global list this, it should only + # # apply to requirements of the package that specifies the + # # dependency_links value + # # FIXME: also, we should track comes_from (i.e., use Link) if self.process_dependency_links: - if not self._have_warned_dependency_links: - logger.deprecated( - "1.6", - "Dependency Links processing has been deprecated with an " - "accelerated time schedule and will be removed in pip 1.6", - ) - self._have_warned_dependency_links = True + warnings.warn( + "Dependency Links processing has been deprecated and will be " + "removed in a future release.", + RemovedInPip9Warning, + ) self.dependency_links.extend(links) - def _sort_locations(self, locations): + @staticmethod + def _sort_locations(locations, expand_dir=False): """ Sort locations into "files" (archives) and "urls", and return a pair of lists (files,urls) @@ -115,27 +200,36 @@ def sort_path(path): is_local_path = os.path.exists(url) is_file_url = url.startswith('file:') - is_find_link = url in self.find_links if is_local_path or is_file_url: if is_local_path: path = url else: path = url_to_path(url) - if is_find_link and os.path.isdir(path): - path = os.path.realpath(path) - for item in os.listdir(path): - sort_path(os.path.join(path, item)) - elif is_file_url and os.path.isdir(path): - urls.append(url) + if os.path.isdir(path): + if expand_dir: + path = os.path.realpath(path) + for item in os.listdir(path): + sort_path(os.path.join(path, item)) + elif is_file_url: + urls.append(url) elif os.path.isfile(path): sort_path(path) - else: + else: + logger.warning( + "Url '%s' is ignored: it is neither a file " + "nor a directory.", url) + elif is_url(url): + # Only add url with clear scheme urls.append(url) + else: + logger.warning( + "Url '%s' is ignored. It is either a non-existing " + "path or lacks a specific scheme.", url) return files, urls - def _link_sort_key(self, link_tuple): + def _candidate_sort_key(self, candidate): """ Function used to generate link sort key for link tuples. The greater the return value, the more preferred it is. @@ -148,275 +242,319 @@ def _link_sort_key(self, link_tuple): comparison operators, but then different sdist links with the same version, would have to be considered equal """ - parsed_version, link, _ = link_tuple - if self.use_wheel: - support_num = len(supported_tags) - if link == INSTALLED_VERSION: - pri = 1 - elif link.ext == wheel_ext: - wheel = Wheel(link.filename) # can raise InvalidWheelFilename - if not wheel.supported(): - raise UnsupportedWheel("%s is not a supported wheel for this platform. It can't be sorted." % wheel.filename) - pri = -(wheel.support_index_min()) - else: # sdist - pri = -(support_num) - return (parsed_version, pri) - else: - return parsed_version + support_num = len(supported_tags) + if candidate.location.is_wheel: + # can raise InvalidWheelFilename + wheel = Wheel(candidate.location.filename) + if not wheel.supported(): + raise UnsupportedWheel( + "%s is not a supported wheel for this platform. It " + "can't be sorted." % wheel.filename + ) + pri = -(wheel.support_index_min()) + else: # sdist + pri = -(support_num) + return (candidate.version, pri) + + def _validate_secure_origin(self, logger, location): + # Determine if this url used a secure transport mechanism + parsed = urllib_parse.urlparse(str(location)) + origin = (parsed.scheme, parsed.hostname, parsed.port) + + # The protocol to use to see if the protocol matches. + # Don't count the repository type as part of the protocol: in + # cases such as "git+ssh", only use "ssh". (I.e., Only verify against + # the last scheme.) + protocol = origin[0].rsplit('+', 1)[-1] + + # Determine if our origin is a secure origin by looking through our + # hardcoded list of secure origins, as well as any additional ones + # configured on this PackageFinder instance. + for secure_origin in (SECURE_ORIGINS + self.secure_origins): + if protocol != secure_origin[0] and secure_origin[0] != "*": + continue - def _sort_versions(self, applicable_versions): - """ - Bring the latest version (and wheels) to the front, but maintain the existing ordering as secondary. - See the docstring for `_link_sort_key` for details. - This function is isolated for easier unit testing. - """ - return sorted(applicable_versions, key=self._link_sort_key, reverse=True) + try: + # We need to do this decode dance to ensure that we have a + # unicode object, even on Python 2.x. + addr = ipaddress.ip_address( + origin[1] + if ( + isinstance(origin[1], six.text_type) or + origin[1] is None + ) + else origin[1].decode("utf8") + ) + network = ipaddress.ip_network( + secure_origin[1] + if isinstance(secure_origin[1], six.text_type) + else secure_origin[1].decode("utf8") + ) + except ValueError: + # We don't have both a valid address or a valid network, so + # we'll check this origin against hostnames. + if (origin[1] and + origin[1].lower() != secure_origin[1].lower() and + secure_origin[1] != "*"): + continue + else: + # We have a valid address and network, so see if the address + # is contained within the network. + if addr not in network: + continue - def find_requirement(self, req, upgrade): + # Check to see if the port patches + if (origin[2] != secure_origin[2] and + secure_origin[2] != "*" and + secure_origin[2] is not None): + continue + + # If we've gotten here, then this origin matches the current + # secure origin and we should return True + return True + + # If we've gotten to this point, then the origin isn't secure and we + # will not accept it as a valid location to search. We will however + # log a warning that we are ignoring it. + logger.warning( + "The repository located at %s is not a trusted or secure host and " + "is being ignored. If this repository is available via HTTPS it " + "is recommended to use HTTPS instead, otherwise you may silence " + "this warning and allow it anyways with '--trusted-host %s'.", + parsed.hostname, + parsed.hostname, + ) + + return False + + def _get_index_urls_locations(self, project_name): + """Returns the locations found via self.index_urls + + Checks the url_name on the main (first in the list) index and + use this url_name to produce all locations + """ def mkurl_pypi_url(url): - loc = posixpath.join(url, url_name) + loc = posixpath.join( + url, + urllib_parse.quote(canonicalize_name(project_name))) # For maximum compatibility with easy_install, ensure the path # ends in a trailing slash. Although this isn't in the spec # (and PyPI can handle it without the slash) some other index - # implementations might break if they relied on easy_install's behavior. + # implementations might break if they relied on easy_install's + # behavior. if not loc.endswith('/'): loc = loc + '/' return loc - url_name = req.url_name - # Only check main index if index URL is given: - main_index_url = None - if self.index_urls: - # Check that we have the url_name correctly spelled: - main_index_url = Link(mkurl_pypi_url(self.index_urls[0]), trusted=True) - # This will also cache the page, so it's okay that we get it again later: - page = self._get_page(main_index_url, req) - if page is None: - url_name = self._find_url_name(Link(self.index_urls[0], trusted=True), url_name, req) or req.url_name + return [mkurl_pypi_url(url) for url in self.index_urls] - if url_name is not None: - locations = [ - mkurl_pypi_url(url) - for url in self.index_urls] + self.find_links - else: - locations = list(self.find_links) - for version in req.absolute_versions: - if url_name is not None and main_index_url is not None: - locations = [ - posixpath.join(main_index_url.url, version)] + locations + def find_all_candidates(self, project_name): + """Find all available InstallationCandidate for project_name + + This checks index_urls, find_links and dependency_links. + All versions found are returned as an InstallationCandidate list. - file_locations, url_locations = self._sort_locations(locations) - _flocations, _ulocations = self._sort_locations(self.dependency_links) - file_locations.extend(_flocations) + See _link_package_versions for details on which files are accepted + """ + index_locations = self._get_index_urls_locations(project_name) + index_file_loc, index_url_loc = self._sort_locations(index_locations) + fl_file_loc, fl_url_loc = self._sort_locations( + self.find_links, expand_dir=True) + dep_file_loc, dep_url_loc = self._sort_locations(self.dependency_links) + + file_locations = ( + Link(url) for url in itertools.chain( + index_file_loc, fl_file_loc, dep_file_loc) + ) # We trust every url that the user has given us whether it was given # via --index-url or --find-links - locations = [Link(url, trusted=True) for url in url_locations] - # We explicitly do not trust links that came from dependency_links - locations.extend([Link(url) for url in _ulocations]) + # We want to filter out any thing which does not have a secure origin. + url_locations = [ + link for link in itertools.chain( + (Link(url) for url in index_url_loc), + (Link(url) for url in fl_url_loc), + (Link(url) for url in dep_url_loc), + ) + if self._validate_secure_origin(logger, link) + ] + + logger.debug('%d location(s) to search for versions of %s:', + len(url_locations), project_name) + + for location in url_locations: + logger.debug('* %s', location) + + canonical_name = canonicalize_name(project_name) + formats = fmt_ctl_formats(self.format_control, canonical_name) + search = Search(project_name, canonical_name, formats) + find_links_versions = self._package_versions( + # We trust every directly linked archive in find_links + (Link(url, '-f') for url in self.find_links), + search + ) - logger.debug('URLs to search for versions for %s:' % req) - for location in locations: - logger.debug('* %s' % location) - - # Determine if this url used a secure transport mechanism - parsed = urlparse.urlparse(str(location)) - if parsed.scheme in INSECURE_SCHEMES: - secure_schemes = INSECURE_SCHEMES[parsed.scheme] - - if len(secure_schemes) == 1: - ctx = (location, parsed.scheme, secure_schemes[0], - parsed.netloc) - logger.warn("%s uses an insecure transport scheme (%s). " - "Consider using %s if %s has it available" % - ctx) - elif len(secure_schemes) > 1: - ctx = (location, parsed.scheme, ", ".join(secure_schemes), - parsed.netloc) - logger.warn("%s uses an insecure transport scheme (%s). " - "Consider using one of %s if %s has any of " - "them available" % ctx) - else: - ctx = (location, parsed.scheme) - logger.warn("%s uses an insecure transport scheme (%s)." % - ctx) - - found_versions = [] - found_versions.extend( - self._package_versions( - # We trust every directly linked archive in find_links - [Link(url, '-f', trusted=True) for url in self.find_links], req.name.lower())) page_versions = [] - for page in self._get_pages(locations, req): - logger.debug('Analyzing links from page %s' % page.url) - logger.indent += 2 - try: - page_versions.extend(self._package_versions(page.links, req.name.lower())) - finally: - logger.indent -= 2 - dependency_versions = list(self._package_versions( - [Link(url) for url in self.dependency_links], req.name.lower())) + for page in self._get_pages(url_locations, project_name): + logger.debug('Analyzing links from page %s', page.url) + with indent_log(): + page_versions.extend( + self._package_versions(page.links, search) + ) + + dependency_versions = self._package_versions( + (Link(url) for url in self.dependency_links), search + ) if dependency_versions: - logger.info('dependency_links found: %s' % ', '.join([link.url for parsed, link, version in dependency_versions])) - file_versions = list(self._package_versions( - [Link(url) for url in file_locations], req.name.lower())) - if not found_versions and not page_versions and not dependency_versions and not file_versions: - logger.fatal('Could not find any downloads that satisfy the requirement %s' % req) - - if self.need_warn_external: - logger.warn("Some externally hosted files were ignored (use " - "--allow-external %s to allow)." % req.name) - - if self.need_warn_unverified: - logger.warn("Some insecure and unverifiable files were ignored" - " (use --allow-unverified %s to allow)." % - req.name) - - raise DistributionNotFound('No distributions at all found for %s' % req) - installed_version = [] - if req.satisfied_by is not None: - installed_version = [(req.satisfied_by.parsed_version, INSTALLED_VERSION, req.satisfied_by.version)] + logger.debug( + 'dependency_links found: %s', + ', '.join([ + version.location.url for version in dependency_versions + ]) + ) + + file_versions = self._package_versions(file_locations, search) if file_versions: file_versions.sort(reverse=True) - logger.info('Local files found: %s' % ', '.join([url_to_path(link.url) for parsed, link, version in file_versions])) - #this is an intentional priority ordering - all_versions = installed_version + file_versions + found_versions + page_versions + dependency_versions - applicable_versions = [] - for (parsed_version, link, version) in all_versions: - if version not in req.req: - logger.info("Ignoring link %s, version %s doesn't match %s" - % (link, version, ','.join([''.join(s) for s in req.req.specs]))) - continue - elif is_prerelease(version) and not (self.allow_all_prereleases or req.prereleases): - # If this version isn't the already installed one, then - # ignore it if it's a pre-release. - if link is not INSTALLED_VERSION: - logger.info("Ignoring link %s, version %s is a pre-release (use --pre to allow)." % (link, version)) - continue - applicable_versions.append((parsed_version, link, version)) - applicable_versions = self._sort_versions(applicable_versions) - existing_applicable = bool([link for parsed_version, link, version in applicable_versions if link is INSTALLED_VERSION]) - if not upgrade and existing_applicable: - if applicable_versions[0][1] is INSTALLED_VERSION: - logger.info('Existing installed version (%s) is most up-to-date and satisfies requirement' - % req.satisfied_by.version) - else: - logger.info('Existing installed version (%s) satisfies requirement (most up-to-date version is %s)' - % (req.satisfied_by.version, applicable_versions[0][2])) - return None - if not applicable_versions: - logger.fatal('Could not find a version that satisfies the requirement %s (from versions: %s)' - % (req, ', '.join([version for parsed_version, link, version in all_versions]))) + logger.debug( + 'Local files found: %s', + ', '.join([ + url_to_path(candidate.location.url) + for candidate in file_versions + ]) + ) - if self.need_warn_external: - logger.warn("Some externally hosted files were ignored (use " - "--allow-external to allow).") + # This is an intentional priority ordering + return ( + file_versions + find_links_versions + page_versions + + dependency_versions + ) - if self.need_warn_unverified: - logger.warn("Some insecure and unverifiable files were ignored" - " (use --allow-unverified %s to allow)." % - req.name) + def find_requirement(self, req, upgrade): + """Try to find a Link matching req - raise DistributionNotFound('No distributions matching the version for %s' % req) - if applicable_versions[0][1] is INSTALLED_VERSION: - # We have an existing version, and its the best version - logger.info('Installed version (%s) is most up-to-date (past versions: %s)' - % (req.satisfied_by.version, ', '.join([version for parsed_version, link, version in applicable_versions[1:]]) or 'none')) - raise BestVersionAlreadyInstalled - if len(applicable_versions) > 1: - logger.info('Using version %s (newest of versions: %s)' % - (applicable_versions[0][2], ', '.join([version for parsed_version, link, version in applicable_versions]))) - - selected_version = applicable_versions[0][1] - - if (selected_version.internal is not None - and not selected_version.internal): - logger.warn("%s an externally hosted file and may be " - "unreliable" % req.name) - - if (selected_version.verifiable is not None - and not selected_version.verifiable): - logger.warn("%s is potentially insecure and " - "unverifiable." % req.name) - - if selected_version._deprecated_regex: - logger.deprecated( - "1.7", - "%s discovered using a deprecated method of parsing, " - "in the future it will no longer be discovered" % req.name + Expects req, an InstallRequirement and upgrade, a boolean + Returns a Link if found, + Raises DistributionNotFound or BestVersionAlreadyInstalled otherwise + """ + all_candidates = self.find_all_candidates(req.name) + + # Filter out anything which doesn't match our specifier + compatible_versions = set( + req.specifier.filter( + # We turn the version object into a str here because otherwise + # when we're debundled but setuptools isn't, Python will see + # packaging.version.Version and + # pkg_resources._vendor.packaging.version.Version as different + # types. This way we'll use a str as a common data interchange + # format. If we stop using the pkg_resources provided specifier + # and start using our own, we can drop the cast to str(). + [str(c.version) for c in all_candidates], + prereleases=( + self.allow_all_prereleases + if self.allow_all_prereleases else None + ), ) + ) + applicable_candidates = [ + # Again, converting to str to deal with debundling. + c for c in all_candidates if str(c.version) in compatible_versions + ] + + if applicable_candidates: + best_candidate = max(applicable_candidates, + key=self._candidate_sort_key) + else: + best_candidate = None - return selected_version + if req.satisfied_by is not None: + installed_version = parse_version(req.satisfied_by.version) + else: + installed_version = None + + if installed_version is None and best_candidate is None: + logger.critical( + 'Could not find a version that satisfies the requirement %s ' + '(from versions: %s)', + req, + ', '.join( + sorted( + set(str(c.version) for c in all_candidates), + key=parse_version, + ) + ) + ) + raise DistributionNotFound( + 'No matching distribution found for %s' % req + ) - def _find_url_name(self, index_url, url_name, req): - """Finds the true URL name of a package, when the given name isn't quite correct. - This is usually used to implement case-insensitivity.""" - if not index_url.url.endswith('/'): - # Vaguely part of the PyPI API... weird but true. - ## FIXME: bad to modify this? - index_url.url += '/' - page = self._get_page(index_url, req) - if page is None: - logger.fatal('Cannot fetch index base URL %s' % index_url) - return - norm_name = normalize_name(req.url_name) - for link in page.links: - base = posixpath.basename(link.path.rstrip('/')) - if norm_name == normalize_name(base): - logger.notify('Real name of requirement %s is %s' % (url_name, base)) - return base - return None + best_installed = False + if installed_version and ( + best_candidate is None or + best_candidate.version <= installed_version): + best_installed = True + + if not upgrade and installed_version is not None: + if best_installed: + logger.debug( + 'Existing installed version (%s) is most up-to-date and ' + 'satisfies requirement', + installed_version, + ) + else: + logger.debug( + 'Existing installed version (%s) satisfies requirement ' + '(most up-to-date version is %s)', + installed_version, + best_candidate.version, + ) + return None + + if best_installed: + # We have an existing version, and its the best version + logger.debug( + 'Installed version (%s) is most up-to-date (past versions: ' + '%s)', + installed_version, + ', '.join(sorted(compatible_versions, key=parse_version)) or + "none", + ) + raise BestVersionAlreadyInstalled - def _get_pages(self, locations, req): + logger.debug( + 'Using version %s (newest of versions: %s)', + best_candidate.version, + ', '.join(sorted(compatible_versions, key=parse_version)) + ) + return best_candidate.location + + def _get_pages(self, locations, project_name): """ Yields (page, page_url) from the given locations, skipping - locations that have errors, and adding download/homepage links + locations that have errors. """ - all_locations = list(locations) seen = set() - - while all_locations: - location = all_locations.pop(0) + for location in locations: if location in seen: continue seen.add(location) - page = self._get_page(location, req) + page = self._get_page(location) if page is None: continue yield page - for link in page.rel_links(): - normalized = normalize_name(req.name).lower() - - if (not normalized in self.allow_external - and not self.allow_all_external): - self.need_warn_external = True - logger.debug("Not searching %s for files because external " - "urls are disallowed." % link) - continue - - if (link.trusted is not None - and not link.trusted - and not normalized in self.allow_unverified): - logger.debug("Not searching %s for urls, it is an " - "untrusted link and cannot produce safe or " - "verifiable files." % link) - self.need_warn_unverified = True - continue - - all_locations.append(link) - - _egg_fragment_re = re.compile(r'#egg=([^&]*)') - _egg_info_re = re.compile(r'([a-z0-9_.]+)-([a-z0-9_.-]+)', re.I) _py_version_re = re.compile(r'-py([123]\.?[0-9]?)$') def _sort_links(self, links): - "Returns elements of links in order, non-egg links first, egg links second, while eliminating duplicates" + """ + Returns elements of links in order, non-egg links first, egg links + second, while eliminating duplicates + """ eggs, no_eggs = [], [] seen = set() for link in links: @@ -428,309 +566,241 @@ def _sort_links(self, links): no_eggs.append(link) return no_eggs + eggs - def _package_versions(self, links, search_name): + def _package_versions(self, links, search): + result = [] for link in self._sort_links(links): - for v in self._link_package_versions(link, search_name): - yield v + v = self._link_package_versions(link, search) + if v is not None: + result.append(v) + return result - def _known_extensions(self): - extensions = ('.tar.gz', '.tar.bz2', '.tar', '.tgz', '.zip') - if self.use_wheel: - return extensions + (wheel_ext,) - return extensions + def _log_skipped_link(self, link, reason): + if link not in self.logged_links: + logger.debug('Skipping link %s; %s', link, reason) + self.logged_links.add(link) - def _link_package_versions(self, link, search_name): - """ - Return an iterable of triples (pkg_resources_version_key, - link, python_version) that can be extracted from the given - link. - - Meant to be overridden by subclasses, not called by clients. - """ - platform = get_platform() + def _link_package_versions(self, link, search): + """Return an InstallationCandidate or None""" version = None if link.egg_fragment: egg_info = link.egg_fragment + ext = link.ext else: egg_info, ext = link.splitext() if not ext: - if link not in self.logged_links: - logger.debug('Skipping link %s; not a file' % link) - self.logged_links.add(link) - return [] - if egg_info.endswith('.tar'): - # Special double-extension case: - egg_info = egg_info[:-4] - ext = '.tar' + ext - if ext not in self._known_extensions(): - if link not in self.logged_links: - logger.debug('Skipping link %s; unknown archive format: %s' % (link, ext)) - self.logged_links.add(link) - return [] + self._log_skipped_link(link, 'not a file') + return + if ext not in SUPPORTED_EXTENSIONS: + self._log_skipped_link( + link, 'unsupported archive format: %s' % ext) + return + if "binary" not in search.formats and ext == wheel_ext: + self._log_skipped_link( + link, 'No binaries permitted for %s' % search.supplied) + return if "macosx10" in link.path and ext == '.zip': - if link not in self.logged_links: - logger.debug('Skipping link %s; macosx10 one' % (link)) - self.logged_links.add(link) - return [] + self._log_skipped_link(link, 'macosx10 one') + return if ext == wheel_ext: try: wheel = Wheel(link.filename) except InvalidWheelFilename: - logger.debug('Skipping %s because the wheel filename is invalid' % link) - return [] - if wheel.name.lower() != search_name.lower(): - logger.debug('Skipping link %s; wrong project name (not %s)' % (link, search_name)) - return [] + self._log_skipped_link(link, 'invalid wheel filename') + return + if canonicalize_name(wheel.name) != search.canonical: + self._log_skipped_link( + link, 'wrong project name (not %s)' % search.supplied) + return if not wheel.supported(): - logger.debug('Skipping %s because it is not compatible with this Python' % link) - return [] - # This is a dirty hack to prevent installing Binary Wheels from - # PyPI unless it is a Windows or Mac Binary Wheel. This is - # paired with a change to PyPI disabling uploads for the - # same. Once we have a mechanism for enabling support for binary - # wheels on linux that deals with the inherent problems of - # binary distribution this can be removed. - comes_from = getattr(link, "comes_from", None) - if (( - not platform.startswith('win') - and not platform.startswith('macosx') - ) - and comes_from is not None - and urlparse.urlparse(comes_from.url).netloc.endswith( - "pypi.python.org")): - if not wheel.supported(tags=supported_tags_noarch): - logger.debug( - "Skipping %s because it is a pypi-hosted binary " - "Wheel on an unsupported platform" % link - ) - return [] + self._log_skipped_link( + link, 'it is not compatible with this Python') + return + version = wheel.version + # This should be up by the search.ok_binary check, but see issue 2700. + if "source" not in search.formats and ext != wheel_ext: + self._log_skipped_link( + link, 'No sources permitted for %s' % search.supplied) + return + if not version: - version = self._egg_info_matches(egg_info, search_name, link) + version = egg_info_matches(egg_info, search.supplied, link) if version is None: - logger.debug('Skipping link %s; wrong project name (not %s)' % (link, search_name)) - return [] - - if (link.internal is not None - and not link.internal - and not normalize_name(search_name).lower() in self.allow_external - and not self.allow_all_external): - # We have a link that we are sure is external, so we should skip - # it unless we are allowing externals - logger.debug("Skipping %s because it is externally hosted." % link) - self.need_warn_external = True - return [] - - if (link.verifiable is not None - and not link.verifiable - and not (normalize_name(search_name).lower() - in self.allow_unverified)): - # We have a link that we are sure we cannot verify it's integrity, - # so we should skip it unless we are allowing unsafe installs - # for this requirement. - logger.debug("Skipping %s because it is an insecure and " - "unverifiable file." % link) - self.need_warn_unverified = True - return [] + self._log_skipped_link( + link, 'wrong project name (not %s)' % search.supplied) + return match = self._py_version_re.search(version) if match: version = version[:match.start()] py_version = match.group(1) if py_version != sys.version[:3]: - logger.debug('Skipping %s because Python version is incorrect' % link) - return [] - logger.debug('Found link %s, version: %s' % (link, version)) - return [(pkg_resources.parse_version(version), - link, - version)] - - def _egg_info_matches(self, egg_info, search_name, link): - match = self._egg_info_re.search(egg_info) - if not match: - logger.debug('Could not parse version from link: %s' % link) - return None - name = match.group(0).lower() - # To match the "safe" name that pkg_resources creates: - name = name.replace('_', '-') - # project name and version must be separated by a dash - look_for = search_name.lower() + "-" - if name.startswith(look_for): - return match.group(0)[len(look_for):] - else: - return None - - def _get_page(self, link, req): - return HTMLPage.get_page(link, req, - cache=self.cache, - session=self.session, - ) - - -class PageCache(object): - """Cache of HTML pages""" - - failure_limit = 3 - - def __init__(self): - self._failures = {} - self._pages = {} - self._archives = {} - - def too_many_failures(self, url): - return self._failures.get(url, 0) >= self.failure_limit + self._log_skipped_link( + link, 'Python version is incorrect') + return + logger.debug('Found link %s, version: %s', link, version) - def get_page(self, url): - return self._pages.get(url) + return InstallationCandidate(search.supplied, version, link) - def is_archive(self, url): - return self._archives.get(url, False) + def _get_page(self, link): + return HTMLPage.get_page(link, session=self.session) - def set_is_archive(self, url, value=True): - self._archives[url] = value - def add_page_failure(self, url, level): - self._failures[url] = self._failures.get(url, 0)+level +def egg_info_matches( + egg_info, search_name, link, + _egg_info_re=re.compile(r'([a-z0-9_.]+)-([a-z0-9_.!+-]+)', re.I)): + """Pull the version part out of a string. - def add_page(self, urls, page): - for url in urls: - self._pages[url] = page + :param egg_info: The string to parse. E.g. foo-2.1 + :param search_name: The name of the package this belongs to. None to + infer the name. Note that this cannot unambiguously parse strings + like foo-2-2 which might be foo, 2-2 or foo-2, 2. + :param link: The link the string came from, for logging on failure. + """ + match = _egg_info_re.search(egg_info) + if not match: + logger.debug('Could not parse version from link: %s', link) + return None + if search_name is None: + full_match = match.group(0) + return full_match[full_match.index('-'):] + name = match.group(0).lower() + # To match the "safe" name that pkg_resources creates: + name = name.replace('_', '-') + # project name and version must be separated by a dash + look_for = search_name.lower() + "-" + if name.startswith(look_for): + return match.group(0)[len(look_for):] + else: + return None class HTMLPage(object): """Represents one page, along with its URL""" - ## FIXME: these regexes are horrible hacks: - _homepage_re = re.compile(r'\s*home\s*page', re.I) - _download_re = re.compile(r'\s*download\s+url', re.I) - _href_re = re.compile('href=(?:"([^"]*)"|\'([^\']*)\'|([^>\\s\\n]*))', re.I|re.S) + def __init__(self, content, url, headers=None): + # Determine if we have any encoding information in our headers + encoding = None + if headers and "Content-Type" in headers: + content_type, params = cgi.parse_header(headers["Content-Type"]) + + if "charset" in params: + encoding = params['charset'] - def __init__(self, content, url, headers=None, trusted=None): self.content = content - self.parsed = html5lib.parse(self.content, namespaceHTMLElements=False) + self.parsed = html5lib.parse( + self.content, + encoding=encoding, + namespaceHTMLElements=False, + ) self.url = url self.headers = headers - self.trusted = trusted def __str__(self): return self.url @classmethod - def get_page(cls, link, req, cache=None, skip_archives=True, session=None): + def get_page(cls, link, skip_archives=True, session=None): if session is None: - session = PipSession() + raise TypeError( + "get_page() missing 1 required keyword argument: 'session'" + ) url = link.url url = url.split('#', 1)[0] - if cache.too_many_failures(url): - return None # Check for VCS schemes that do not support lookup as web pages. from pip.vcs import VcsSupport for scheme in VcsSupport.schemes: if url.lower().startswith(scheme) and url[len(scheme)] in '+:': - logger.debug('Cannot look at %(scheme)s URL %(link)s' % locals()) + logger.debug('Cannot look at %s URL %s', scheme, link) return None - if cache is not None: - inst = cache.get_page(url) - if inst is not None: - return inst try: if skip_archives: - if cache is not None: - if cache.is_archive(url): - return None filename = link.filename - for bad_ext in ['.tar', '.tar.gz', '.tar.bz2', '.tgz', '.zip']: + for bad_ext in ARCHIVE_EXTENSIONS: if filename.endswith(bad_ext): - content_type = cls._get_content_type(url, - session=session, + content_type = cls._get_content_type( + url, session=session, ) if content_type.lower().startswith('text/html'): break else: - logger.debug('Skipping page %s because of Content-Type: %s' % (link, content_type)) - if cache is not None: - cache.set_is_archive(url) - return None - logger.debug('Getting page %s' % url) + logger.debug( + 'Skipping page %s because of Content-Type: %s', + link, + content_type, + ) + return + + logger.debug('Getting page %s', url) # Tack index.html onto file:// URLs that point to directories - (scheme, netloc, path, params, query, fragment) = urlparse.urlparse(url) - if scheme == 'file' and os.path.isdir(url2pathname(path)): - # add trailing slash if not present so urljoin doesn't trim final segment + (scheme, netloc, path, params, query, fragment) = \ + urllib_parse.urlparse(url) + if (scheme == 'file' and + os.path.isdir(urllib_request.url2pathname(path))): + # add trailing slash if not present so urljoin doesn't trim + # final segment if not url.endswith('/'): url += '/' - url = urlparse.urljoin(url, 'index.html') - logger.debug(' file: URL is directory, getting %s' % url) - - resp = session.get(url, headers={"Accept": "text/html"}) + url = urllib_parse.urljoin(url, 'index.html') + logger.debug(' file: URL is directory, getting %s', url) + + resp = session.get( + url, + headers={ + "Accept": "text/html", + "Cache-Control": "max-age=600", + }, + ) resp.raise_for_status() # The check for archives above only works if the url ends with - # something that looks like an archive. However that is not a - # requirement. For instance http://sourceforge.net/projects/docutils/files/docutils/0.8.1/docutils-0.8.1.tar.gz/download - # redirects to http://superb-dca3.dl.sourceforge.net/project/docutils/docutils/0.8.1/docutils-0.8.1.tar.gz - # Unless we issue a HEAD request on every url we cannot know - # ahead of time for sure if something is HTML or not. However we - # can check after we've downloaded it. + # something that looks like an archive. However that is not a + # requirement of an url. Unless we issue a HEAD request on every + # url we cannot know ahead of time for sure if something is HTML + # or not. However we can check after we've downloaded it. content_type = resp.headers.get('Content-Type', 'unknown') if not content_type.lower().startswith("text/html"): - logger.debug('Skipping page %s because of Content-Type: %s' % - (link, content_type)) - if cache is not None: - cache.set_is_archive(url) - return None + logger.debug( + 'Skipping page %s because of Content-Type: %s', + link, + content_type, + ) + return - inst = cls(resp.text, resp.url, resp.headers, trusted=link.trusted) + inst = cls(resp.content, resp.url, resp.headers) except requests.HTTPError as exc: - level = 2 if exc.response.status_code == 404 else 1 - cls._handle_fail(req, link, exc, url, cache=cache, level=level) - except requests.ConnectionError as exc: - cls._handle_fail( - req, link, "connection error: %s" % exc, url, - cache=cache, - ) - except requests.Timeout: - cls._handle_fail(req, link, "timed out", url, cache=cache) + cls._handle_fail(link, exc, url) except SSLError as exc: reason = ("There was a problem confirming the ssl certificate: " "%s" % exc) - cls._handle_fail(req, link, reason, url, - cache=cache, - level=2, - meth=logger.notify, - ) + cls._handle_fail(link, reason, url, meth=logger.info) + except requests.ConnectionError as exc: + cls._handle_fail(link, "connection error: %s" % exc, url) + except requests.Timeout: + cls._handle_fail(link, "timed out", url) else: - if cache is not None: - cache.add_page([url, resp.url], inst) return inst @staticmethod - def _handle_fail(req, link, reason, url, cache=None, level=1, meth=None): + def _handle_fail(link, reason, url, meth=None): if meth is None: - meth = logger.info - - meth("Could not fetch URL %s: %s", link, reason) - meth("Will skip URL %s when looking for download links for %s" % - (link.url, req)) + meth = logger.debug - if cache is not None: - cache.add_page_failure(url, level) + meth("Could not fetch URL %s: %s - skipping", link, reason) @staticmethod - def _get_content_type(url, session=None): + def _get_content_type(url, session): """Get the Content-Type of the given url, using a HEAD request""" - if session is None: - session = PipSession() - - scheme, netloc, path, query, fragment = urlparse.urlsplit(url) - if not scheme in ('http', 'https', 'ftp', 'ftps'): - ## FIXME: some warning or something? - ## assertion error? + scheme, netloc, path, query, fragment = urllib_parse.urlsplit(url) + if scheme not in ('http', 'https'): + # FIXME: some warning or something? + # assertion error? return '' resp = session.head(url, allow_redirects=True) @@ -738,30 +808,16 @@ def _get_content_type(url, session=None): return resp.headers.get("Content-Type", "") - @property - def api_version(self): - if not hasattr(self, "_api_version"): - _api_version = None - - metas = [x for x in self.parsed.findall(".//meta") - if x.get("name", "").lower() == "api-version"] - if metas: - try: - _api_version = int(metas[0].get("value", None)) - except (TypeError, ValueError): - _api_version = None - self._api_version = _api_version - return self._api_version - - @property + @cached_property def base_url(self): - if not hasattr(self, "_base_url"): - base = self.parsed.find(".//base") - if base is not None and base.get("href"): - self._base_url = base.get("href") - else: - self._base_url = self.url - return self._base_url + bases = [ + x for x in self.parsed.findall(".//base") + if x.get("href") is not None + ] + if bases and bases[0].get("href"): + return bases[0].get("href") + else: + return self.url @property def links(self): @@ -769,54 +825,10 @@ def links(self): for anchor in self.parsed.findall(".//a"): if anchor.get("href"): href = anchor.get("href") - url = self.clean_link(urlparse.urljoin(self.base_url, href)) - - # Determine if this link is internal. If that distinction - # doesn't make sense in this context, then we don't make - # any distinction. - internal = None - if self.api_version and self.api_version >= 2: - # Only api_versions >= 2 have a distinction between - # external and internal links - internal = bool(anchor.get("rel") - and "internal" in anchor.get("rel").split()) - - yield Link(url, self, internal=internal) - - def rel_links(self): - for url in self.explicit_rel_links(): - yield url - for url in self.scraped_rel_links(): - yield url - - def explicit_rel_links(self, rels=('homepage', 'download')): - """Yields all links with the given relations""" - rels = set(rels) - - for anchor in self.parsed.findall(".//a"): - if anchor.get("rel") and anchor.get("href"): - found_rels = set(anchor.get("rel").split()) - # Determine the intersection between what rels were found and - # what rels were being looked for - if found_rels & rels: - href = anchor.get("href") - url = self.clean_link(urlparse.urljoin(self.base_url, href)) - yield Link(url, self, trusted=False) - - def scraped_rel_links(self): - # Can we get rid of this horrible horrible method? - for regex in (self._homepage_re, self._download_re): - match = regex.search(self.content) - if not match: - continue - href_match = self._href_re.search(self.content, pos=match.end()) - if not href_match: - continue - url = href_match.group(1) or href_match.group(2) or href_match.group(3) - if not url: - continue - url = self.clean_link(urlparse.urljoin(self.base_url, url)) - yield Link(url, self, trusted=False, _deprecated_regex=True) + url = self.clean_link( + urllib_parse.urljoin(self.base_url, href) + ) + yield Link(url, self) _clean_re = re.compile(r'[^a-z0-9$&+,/:;=?@.#%_\\|-]', re.I) @@ -830,13 +842,14 @@ def clean_link(self, url): class Link(object): - def __init__(self, url, comes_from=None, internal=None, trusted=None, - _deprecated_regex=False): + def __init__(self, url, comes_from=None): + + # url can be a UNC windows share + if url.startswith('\\\\'): + url = path_to_url(url) + self.url = url self.comes_from = comes_from - self.internal = internal - self.trusted = trusted - self._deprecated_regex = _deprecated_regex def __str__(self): if self.comes_from: @@ -848,21 +861,33 @@ def __repr__(self): return '' % self def __eq__(self, other): + if not isinstance(other, Link): + return NotImplemented return self.url == other.url def __ne__(self, other): + if not isinstance(other, Link): + return NotImplemented return self.url != other.url def __lt__(self, other): + if not isinstance(other, Link): + return NotImplemented return self.url < other.url def __le__(self, other): + if not isinstance(other, Link): + return NotImplemented return self.url <= other.url def __gt__(self, other): + if not isinstance(other, Link): + return NotImplemented return self.url > other.url def __ge__(self, other): + if not isinstance(other, Link): + return NotImplemented return self.url >= other.url def __hash__(self): @@ -870,18 +895,23 @@ def __hash__(self): @property def filename(self): - _, netloc, path, _, _ = urlparse.urlsplit(self.url) + _, netloc, path, _, _ = urllib_parse.urlsplit(self.url) name = posixpath.basename(path.rstrip('/')) or netloc + name = urllib_parse.unquote(name) assert name, ('URL %r produced no filename' % self.url) return name @property def scheme(self): - return urlparse.urlsplit(self.url)[0] + return urllib_parse.urlsplit(self.url)[0] + + @property + def netloc(self): + return urllib_parse.urlsplit(self.url)[1] @property def path(self): - return urlparse.urlsplit(self.url)[2] + return urllib_parse.unquote(urllib_parse.urlsplit(self.url)[2]) def splitext(self): return splitext(posixpath.basename(self.path.rstrip('/'))) @@ -892,10 +922,10 @@ def ext(self): @property def url_without_fragment(self): - scheme, netloc, path, query, fragment = urlparse.urlsplit(self.url) - return urlparse.urlunsplit((scheme, netloc, path, query, None)) + scheme, netloc, path, query, fragment = urllib_parse.urlsplit(self.url) + return urllib_parse.urlunsplit((scheme, netloc, path, query, None)) - _egg_fragment_re = re.compile(r'#egg=([^&]*)') + _egg_fragment_re = re.compile(r'[#&]egg=([^&]*)') @property def egg_fragment(self): @@ -904,7 +934,18 @@ def egg_fragment(self): return None return match.group(1) - _hash_re = re.compile(r'(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)') + _subdirectory_fragment_re = re.compile(r'[#&]subdirectory=([^&]*)') + + @property + def subdirectory_fragment(self): + match = self._subdirectory_fragment_re.search(self.url) + if not match: + return None + return match.group(1) + + _hash_re = re.compile( + r'(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)' + ) @property def hash(self): @@ -925,66 +966,83 @@ def show_url(self): return posixpath.basename(self.url.split('#', 1)[0].split('?', 1)[0]) @property - def verifiable(self): + def is_wheel(self): + return self.ext == wheel_ext + + @property + def is_artifact(self): """ - Returns True if this link can be verified after download, False if it - cannot, and None if we cannot determine. + Determines if this points to an actual artifact (e.g. a tarball) or if + it points to an "abstract" thing like a path or a VCS location. """ - trusted = self.trusted or getattr(self.comes_from, "trusted", None) - if trusted is not None and trusted: - # This link came from a trusted source. It *may* be verifiable but - # first we need to see if this page is operating under the new - # API version. - try: - api_version = getattr(self.comes_from, "api_version", None) - api_version = int(api_version) - except (ValueError, TypeError): - api_version = None - - if api_version is None or api_version <= 1: - # This link is either trusted, or it came from a trusted, - # however it is not operating under the API version 2 so - # we can't make any claims about if it's safe or not - return + from pip.vcs import vcs - if self.hash: - # This link came from a trusted source and it has a hash, so we - # can consider it safe. - return True - else: - # This link came from a trusted source, using the new API - # version, and it does not have a hash. It is NOT verifiable - return False - elif trusted is not None: - # This link came from an untrusted source and we cannot trust it + if self.scheme in vcs.all_schemes: return False + return True -# An object to represent the "link" for the installed version of a requirement. -# Using Inf as the url makes it sort higher. -INSTALLED_VERSION = Link(Inf) +FormatControl = namedtuple('FormatControl', 'no_binary only_binary') +"""This object has two fields, no_binary and only_binary. -def get_requirement_from_url(url): - """Get a requirement from the URL, if possible. This looks for #egg - in the URL""" - link = Link(url) - egg_info = link.egg_fragment - if not egg_info: - egg_info = splitext(link.filename)[0] - return package_to_requirement(egg_info) +If a field is falsy, it isn't set. If it is {':all:'}, it should match all +packages except those listed in the other field. Only one field can be set +to {':all:'} at a time. The rest of the time exact package name matches +are listed, with any given package only showing up in one field at a time. +""" -def package_to_requirement(package_name): - """Translate a name like Foo-1.2 to Foo==1.3""" - match = re.search(r'^(.*?)-(dev|\d.*)', package_name) - if match: - name = match.group(1) - version = match.group(2) - else: - name = package_name - version = '' - if version: - return '%s==%s' % (name, version) - else: - return name +def fmt_ctl_handle_mutual_exclude(value, target, other): + new = value.split(',') + while ':all:' in new: + other.clear() + target.clear() + target.add(':all:') + del new[:new.index(':all:') + 1] + if ':none:' not in new: + # Without a none, we want to discard everything as :all: covers it + return + for name in new: + if name == ':none:': + target.clear() + continue + name = canonicalize_name(name) + other.discard(name) + target.add(name) + + +def fmt_ctl_formats(fmt_ctl, canonical_name): + result = set(["binary", "source"]) + if canonical_name in fmt_ctl.only_binary: + result.discard('source') + elif canonical_name in fmt_ctl.no_binary: + result.discard('binary') + elif ':all:' in fmt_ctl.only_binary: + result.discard('source') + elif ':all:' in fmt_ctl.no_binary: + result.discard('binary') + return frozenset(result) + + +def fmt_ctl_no_binary(fmt_ctl): + fmt_ctl_handle_mutual_exclude( + ':all:', fmt_ctl.no_binary, fmt_ctl.only_binary) + + +def fmt_ctl_no_use_wheel(fmt_ctl): + fmt_ctl_no_binary(fmt_ctl) + warnings.warn( + '--no-use-wheel is deprecated and will be removed in the future. ' + ' Please use --no-binary :all: instead.', RemovedInPip10Warning, + stacklevel=2) + + +Search = namedtuple('Search', 'supplied canonical formats') +"""Capture key aspects of a search. + +:attribute supplied: The user supplied package. +:attribute canonical: The canonical package name. +:attribute formats: The formats allowed for this package. Should be a set + with 'binary' or 'source' or both in it. +""" diff --git a/pip/locations.py b/pip/locations.py index 1d402651689..1bd0faed044 100644 --- a/pip/locations.py +++ b/pip/locations.py @@ -1,13 +1,20 @@ """Locations where we look for configs, install stuff, etc""" +from __future__ import absolute_import -import sys -import site import os -import tempfile -from distutils.command.install import install, SCHEME_KEYS -import getpass -from pip.backwardcompat import get_python_lib, get_path_uid, user_site -import pip.exceptions +import os.path +import site +import sys + +from distutils import sysconfig +from distutils.command.install import install, SCHEME_KEYS # noqa + +from pip.compat import WINDOWS, expanduser +from pip.utils import appdirs + + +# Application Directories +USER_CACHE_DIR = appdirs.user_cache_dir("pip") DELETE_MARKER_MESSAGE = '''\ @@ -19,14 +26,14 @@ ''' PIP_DELETE_MARKER_FILENAME = 'pip-delete-this-directory.txt' + def write_delete_marker_file(directory): """ Write the pip delete marker file into this directory. """ filepath = os.path.join(directory, PIP_DELETE_MARKER_FILENAME) - marker_fp = open(filepath, 'w') - marker_fp.write(DELETE_MARKER_MESSAGE) - marker_fp.close() + with open(filepath, 'w') as marker_fp: + marker_fp.write(DELETE_MARKER_MESSAGE) def running_under_virtualenv(): @@ -46,127 +53,130 @@ def virtualenv_no_global(): """ Return True if in a venv and no system site packages. """ - #this mirrors the logic in virtualenv.py for locating the no-global-site-packages.txt file + # this mirrors the logic in virtualenv.py for locating the + # no-global-site-packages.txt file site_mod_dir = os.path.dirname(os.path.abspath(site.__file__)) no_global_file = os.path.join(site_mod_dir, 'no-global-site-packages.txt') if running_under_virtualenv() and os.path.isfile(no_global_file): return True -def __get_username(): - """ Returns the effective username of the current process. """ - if sys.platform == 'win32': - return getpass.getuser() - import pwd - return pwd.getpwuid(os.geteuid()).pw_name - -def _get_build_prefix(): - """ Returns a safe build_prefix """ - path = os.path.join(tempfile.gettempdir(), 'pip_build_%s' % - __get_username()) - if sys.platform == 'win32': - """ on windows(tested on 7) temp dirs are isolated """ - return path - try: - os.mkdir(path) - write_delete_marker_file(path) - except OSError: - file_uid = None - try: - # raises OSError for symlinks - # https://github.com/pypa/pip/pull/935#discussion_r5307003 - file_uid = get_path_uid(path) - except OSError: - file_uid = None - - if file_uid != os.geteuid(): - msg = "The temporary folder for building (%s) is either not owned by you, or is a symlink." \ - % path - print (msg) - print("pip will not work until the temporary folder is " + \ - "either deleted or is a real directory owned by your user account.") - raise pip.exceptions.InstallationError(msg) - return path if running_under_virtualenv(): - build_prefix = os.path.join(sys.prefix, 'build') src_prefix = os.path.join(sys.prefix, 'src') else: - # Note: intentionally NOT using mkdtemp - # See https://github.com/pypa/pip/issues/906 for plan to move to mkdtemp - build_prefix = _get_build_prefix() - - ## FIXME: keep src in cwd for now (it is not a temporary folder) + # FIXME: keep src in cwd for now (it is not a temporary folder) try: src_prefix = os.path.join(os.getcwd(), 'src') except OSError: # In case the current working directory has been renamed or deleted - sys.exit("The folder you are executing pip from can no longer be found.") + sys.exit( + "The folder you are executing pip from can no longer be found." + ) # under Mac OS X + virtualenv sys.prefix is not properly resolved # it is something like /path/to/python/bin/.. # Note: using realpath due to tmp dirs on OSX being symlinks -build_prefix = os.path.abspath(os.path.realpath(build_prefix)) src_prefix = os.path.abspath(src_prefix) # FIXME doesn't account for venv linked to global site-packages -site_packages = get_python_lib() -user_dir = os.path.expanduser('~') -if sys.platform == 'win32': +site_packages = sysconfig.get_python_lib() +user_site = site.USER_SITE +user_dir = expanduser('~') +if WINDOWS: bin_py = os.path.join(sys.prefix, 'Scripts') - bin_user = os.path.join(user_site, 'Scripts') if user_site else None + bin_user = os.path.join(user_site, 'Scripts') # buildout uses 'bin' on Windows too? if not os.path.exists(bin_py): bin_py = os.path.join(sys.prefix, 'bin') - bin_user = os.path.join(user_site, 'bin') if user_site else None - default_storage_dir = os.path.join(user_dir, 'pip') - default_config_file = os.path.join(default_storage_dir, 'pip.ini') - default_log_file = os.path.join(default_storage_dir, 'pip.log') + bin_user = os.path.join(user_site, 'bin') + + config_basename = 'pip.ini' + + legacy_storage_dir = os.path.join(user_dir, 'pip') + legacy_config_file = os.path.join( + legacy_storage_dir, + config_basename, + ) else: bin_py = os.path.join(sys.prefix, 'bin') - bin_user = os.path.join(user_site, 'bin') if user_site else None - default_storage_dir = os.path.join(user_dir, '.pip') - default_config_file = os.path.join(default_storage_dir, 'pip.conf') - default_log_file = os.path.join(default_storage_dir, 'pip.log') + bin_user = os.path.join(user_site, 'bin') + + config_basename = 'pip.conf' + + legacy_storage_dir = os.path.join(user_dir, '.pip') + legacy_config_file = os.path.join( + legacy_storage_dir, + config_basename, + ) # Forcing to use /usr/local/bin for standard Mac OS X framework installs # Also log to ~/Library/Logs/ for use with the Console.app log viewer if sys.platform[:6] == 'darwin' and sys.prefix[:16] == '/System/Library/': bin_py = '/usr/local/bin' - default_log_file = os.path.join(user_dir, 'Library/Logs/pip.log') + +site_config_files = [ + os.path.join(path, config_basename) + for path in appdirs.site_config_dirs('pip') +] -def distutils_scheme(dist_name, user=False, home=None, root=None): +def distutils_scheme(dist_name, user=False, home=None, root=None, + isolated=False, prefix=None): """ Return a distutils install scheme """ from distutils.dist import Distribution scheme = {} - d = Distribution({'name': dist_name}) + + if isolated: + extra_dist_args = {"script_args": ["--no-user-cfg"]} + else: + extra_dist_args = {} + dist_args = {'name': dist_name} + dist_args.update(extra_dist_args) + + d = Distribution(dist_args) d.parse_config_files() i = d.get_command_obj('install', create=True) - # NOTE: setting user or home has the side-effect of creating the home dir or - # user base for installations during finalize_options() + # NOTE: setting user or home has the side-effect of creating the home dir + # or user base for installations during finalize_options() # ideally, we'd prefer a scheme class that has no side-effects. + assert not (user and prefix), "user={0} prefix={1}".format(user, prefix) i.user = user or i.user + if user: + i.prefix = "" + i.prefix = prefix or i.prefix i.home = home or i.home i.root = root or i.root i.finalize_options() for key in SCHEME_KEYS: - scheme[key] = getattr(i, 'install_'+key) + scheme[key] = getattr(i, 'install_' + key) + + # install_lib specified in setup.cfg should install *everything* + # into there (i.e. it takes precedence over both purelib and + # platlib). Note, i.install_lib is *always* set after + # finalize_options(); we only want to override here if the user + # has explicitly requested it hence going back to the config + if 'install_lib' in d.get_option_dict('install'): + scheme.update(dict(purelib=i.install_lib, platlib=i.install_lib)) if running_under_virtualenv(): - scheme['headers'] = os.path.join(sys.prefix, - 'include', - 'site', - 'python' + sys.version[:3], - dist_name) + scheme['headers'] = os.path.join( + sys.prefix, + 'include', + 'site', + 'python' + sys.version[:3], + dist_name, + ) if root is not None: + path_no_drive = os.path.splitdrive( + os.path.abspath(scheme["headers"]))[1] scheme["headers"] = os.path.join( root, - os.path.abspath(scheme["headers"])[1:], + path_no_drive[1:], ) return scheme diff --git a/pip/log.py b/pip/log.py deleted file mode 100644 index 4e3177383c8..00000000000 --- a/pip/log.py +++ /dev/null @@ -1,276 +0,0 @@ -"""Logging -""" - -import sys -import os -import logging - -from pip import backwardcompat -from pip._vendor import colorama, pkg_resources - - -def _color_wrap(*colors): - def wrapped(inp): - return "".join(list(colors) + [inp, colorama.Style.RESET_ALL]) - return wrapped - - -def should_color(consumer, environ, std=(sys.stdout, sys.stderr)): - real_consumer = (consumer if not isinstance(consumer, colorama.AnsiToWin32) - else consumer.wrapped) - - # If consumer isn't stdout or stderr we shouldn't colorize it - if real_consumer not in std: - return False - - # If consumer is a tty we should color it - if hasattr(real_consumer, "isatty") and real_consumer.isatty(): - return True - - # If we have an ASNI term we should color it - if environ.get("TERM") == "ANSI": - return True - - # If anything else we should not color it - return False - - -def should_warn(current_version, removal_version): - # Our Significant digits on versions is 2, so remove everything but the - # first two places. - current_version = ".".join(current_version.split(".")[:2]) - removal_version = ".".join(removal_version.split(".")[:2]) - - # Our warning threshold is one minor version before removal, so we - # decrement the minor version by one - major, minor = removal_version.split(".") - minor = str(int(minor) - 1) - warn_version = ".".join([major, minor]) - - # Test if our current_version should be a warn - return (pkg_resources.parse_version(current_version) - < pkg_resources.parse_version(warn_version)) - - -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 - DEBUG = logging.DEBUG - INFO = logging.INFO - NOTIFY = (logging.INFO + logging.WARN) / 2 - WARN = WARNING = logging.WARN - ERROR = logging.ERROR - FATAL = logging.FATAL - - LEVELS = [VERBOSE_DEBUG, DEBUG, INFO, NOTIFY, WARN, ERROR, FATAL] - - COLORS = { - WARN: _color_wrap(colorama.Fore.YELLOW), - ERROR: _color_wrap(colorama.Fore.RED), - FATAL: _color_wrap(colorama.Fore.RED), - } - - def __init__(self): - self.consumers = [] - self.indent = 0 - self.explicit_levels = False - self.in_progress = None - self.in_progress_hanging = False - - def add_consumers(self, *consumers): - if sys.platform.startswith("win"): - for level, consumer in consumers: - if hasattr(consumer, "write"): - self.consumers.append( - (level, colorama.AnsiToWin32(consumer)), - ) - else: - self.consumers.append((level, consumer)) - else: - self.consumers.extend(consumers) - - def debug(self, msg, *args, **kw): - self.log(self.DEBUG, msg, *args, **kw) - - def info(self, msg, *args, **kw): - self.log(self.INFO, msg, *args, **kw) - - def notify(self, msg, *args, **kw): - self.log(self.NOTIFY, msg, *args, **kw) - - def warn(self, msg, *args, **kw): - self.log(self.WARN, msg, *args, **kw) - - def error(self, msg, *args, **kw): - self.log(self.ERROR, msg, *args, **kw) - - def fatal(self, msg, *args, **kw): - self.log(self.FATAL, msg, *args, **kw) - - def deprecated(self, removal_version, msg, *args, **kwargs): - """ - Logs deprecation message which is log level WARN if the - ``removal_version`` is > 1 minor release away and log level ERROR - otherwise. - - removal_version should be the version that the deprecated feature is - expected to be removed in, so something that will not exist in - version 1.7, but will in 1.6 would have a removal_version of 1.7. - """ - from pip import __version__ - - if should_warn(__version__, removal_version): - self.warn(msg, *args, **kwargs) - else: - self.error(msg, *args, **kwargs) - - def log(self, level, msg, *args, **kw): - if args: - if kw: - raise TypeError( - "You may give positional or keyword arguments, not both") - args = args or kw - - # render - if args: - rendered = msg % args - else: - rendered = msg - rendered = ' ' * self.indent + rendered - if self.explicit_levels: - ## FIXME: should this be a name, not a level number? - rendered = '%02i %s' % (level, rendered) - - for consumer_level, consumer in self.consumers: - if self.level_matches(level, consumer_level): - if (self.in_progress_hanging - and consumer in (sys.stdout, sys.stderr)): - self.in_progress_hanging = False - sys.stdout.write('\n') - sys.stdout.flush() - if hasattr(consumer, 'write'): - write_content = rendered + '\n' - if should_color(consumer, os.environ): - # We are printing to stdout or stderr and it supports - # colors so render our text colored - colorizer = self.COLORS.get(level, lambda x: x) - write_content = colorizer(write_content) - - consumer.write(write_content) - if hasattr(consumer, 'flush'): - consumer.flush() - else: - consumer(rendered) - - def _show_progress(self): - """Should we display download progress?""" - return (self.stdout_level_matches(self.NOTIFY) and sys.stdout.isatty()) - - def start_progress(self, msg): - assert not self.in_progress, ( - "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.flush() - self.in_progress_hanging = True - else: - self.in_progress_hanging = False - self.in_progress = msg - self.last_message = None - - def end_progress(self, msg='done.'): - assert self.in_progress, ( - "Tried to end_progress without start_progress") - if self._show_progress(): - if not self.in_progress_hanging: - # Some message has been printed out since start_progress - sys.stdout.write('...' + self.in_progress + msg + '\n') - sys.stdout.flush() - else: - # These erase any messages shown with show_progress (besides .'s) - logger.show_progress('') - logger.show_progress('') - sys.stdout.write(msg + '\n') - sys.stdout.flush() - self.in_progress = None - self.in_progress_hanging = False - - def show_progress(self, message=None): - """If we are in a progress scope, and no log messages have been - shown, write out another '.'""" - if self.in_progress_hanging: - if message is None: - sys.stdout.write('.') - sys.stdout.flush() - else: - if self.last_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.flush() - self.last_message = message - - def stdout_level_matches(self, level): - """Returns true if a message at this level will go to stdout""" - return self.level_matches(level, self._stdout_level()) - - def _stdout_level(self): - """Returns the level that stdout runs at""" - for level, consumer in self.consumers: - if consumer is sys.stdout: - return level - return self.FATAL - - def level_matches(self, level, consumer_level): - """ - >>> l = Logger() - >>> l.level_matches(3, 4) - False - >>> l.level_matches(3, 2) - True - >>> l.level_matches(slice(None, 3), 3) - False - >>> l.level_matches(slice(None, 3), 2) - True - >>> l.level_matches(slice(1, 3), 1) - True - >>> l.level_matches(slice(2, 3), 1) - False - """ - if isinstance(level, slice): - start, stop = level.start, level.stop - if start is not None and start > consumer_level: - return False - if stop is not None or stop <= consumer_level: - return False - return True - else: - return level >= consumer_level - - @classmethod - def level_for_integer(cls, level): - levels = cls.LEVELS - if level < 0: - return levels[0] - if level >= len(levels): - return levels[-1] - return levels[level] - - def move_stdout_to_stderr(self): - to_remove = [] - to_add = [] - for consumer_level, consumer in self.consumers: - if consumer == sys.stdout: - to_remove.append((consumer_level, consumer)) - to_add.append((consumer_level, sys.stderr)) - for item in to_remove: - self.consumers.remove(item) - self.consumers.extend(to_add) - -logger = Logger() diff --git a/pip/models/__init__.py b/pip/models/__init__.py new file mode 100644 index 00000000000..1d727d7eea7 --- /dev/null +++ b/pip/models/__init__.py @@ -0,0 +1,4 @@ +from pip.models.index import Index, PyPI + + +__all__ = ["Index", "PyPI"] diff --git a/pip/models/index.py b/pip/models/index.py new file mode 100644 index 00000000000..be9911988cf --- /dev/null +++ b/pip/models/index.py @@ -0,0 +1,16 @@ +from pip._vendor.six.moves.urllib import parse as urllib_parse + + +class Index(object): + def __init__(self, url): + self.url = url + self.netloc = urllib_parse.urlsplit(url).netloc + self.simple_url = self.url_to_path('simple') + self.pypi_url = self.url_to_path('pypi') + self.pip_json_url = self.url_to_path('pypi/pip/json') + + def url_to_path(self, path): + return urllib_parse.urljoin(self.url, path) + + +PyPI = Index('https://pypi.python.org/') diff --git a/pip/operations/__init__.py b/pip/operations/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/pip/operations/check.py b/pip/operations/check.py new file mode 100644 index 00000000000..38712eb5433 --- /dev/null +++ b/pip/operations/check.py @@ -0,0 +1,52 @@ +from pip.utils import get_installed_distributions + + +def check_requirements(): + installed = get_installed_distributions(skip=()) + missing_reqs_dict = {} + incompatible_reqs_dict = {} + + for dist in installed: + key = '%s==%s' % (dist.project_name, dist.version) + + missing_reqs = list(get_missing_reqs(dist, installed)) + if missing_reqs: + missing_reqs_dict[key] = missing_reqs + + incompatible_reqs = list(get_incompatible_reqs(dist, installed)) + if incompatible_reqs: + incompatible_reqs_dict[key] = incompatible_reqs + + return (missing_reqs_dict, incompatible_reqs_dict) + + +def get_missing_reqs(dist, installed_dists): + """Return all of the requirements of `dist` that aren't present in + `installed_dists`. + + """ + installed_names = set(d.project_name.lower() for d in installed_dists) + missing_requirements = set() + + for requirement in dist.requires(): + if requirement.project_name.lower() not in installed_names: + missing_requirements.add(requirement) + yield requirement + + +def get_incompatible_reqs(dist, installed_dists): + """Return all of the requirements of `dist` that are present in + `installed_dists`, but have incompatible versions. + + """ + installed_dists_by_name = {} + for installed_dist in installed_dists: + installed_dists_by_name[installed_dist.project_name] = installed_dist + + incompatible_requirements = set() + for requirement in dist.requires(): + present_dist = installed_dists_by_name.get(requirement.project_name) + + if present_dist and present_dist not in requirement: + incompatible_requirements.add((requirement, present_dist)) + yield (requirement, present_dist) diff --git a/pip/operations/freeze.py b/pip/operations/freeze.py new file mode 100644 index 00000000000..c55760b75ae --- /dev/null +++ b/pip/operations/freeze.py @@ -0,0 +1,131 @@ +from __future__ import absolute_import + +import logging +import re + +import pip +from pip.req import InstallRequirement +from pip.utils import get_installed_distributions +from pip._vendor import pkg_resources +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.pkg_resources import RequirementParseError + + +logger = logging.getLogger(__name__) + + +def freeze( + requirement=None, + find_links=None, local_only=None, user_only=None, skip_regex=None, + default_vcs=None, + isolated=False, + wheel_cache=None, + skip=()): + find_links = find_links or [] + skip_match = None + + if skip_regex: + skip_match = re.compile(skip_regex).search + + dependency_links = [] + + for dist in pkg_resources.working_set: + if dist.has_metadata('dependency_links.txt'): + dependency_links.extend( + dist.get_metadata_lines('dependency_links.txt') + ) + for link in find_links: + if '#egg=' in link: + dependency_links.append(link) + for link in find_links: + yield '-f %s' % link + installations = {} + for dist in get_installed_distributions(local_only=local_only, + skip=(), + user_only=user_only): + try: + req = pip.FrozenRequirement.from_dist( + dist, + dependency_links + ) + except RequirementParseError: + logger.warning( + "Could not parse requirement: %s", + dist.project_name + ) + continue + installations[req.name] = req + + if requirement: + # the options that don't get turned into an InstallRequirement + # should only be emitted once, even if the same option is in multiple + # requirements files, so we need to keep track of what has been emitted + # so that we don't emit it again if it's seen again + emitted_options = set() + for req_file_path in requirement: + with open(req_file_path) as req_file: + for line in req_file: + if (not line.strip() or + line.strip().startswith('#') or + (skip_match and skip_match(line)) or + line.startswith(( + '-r', '--requirement', + '-Z', '--always-unzip', + '-f', '--find-links', + '-i', '--index-url', + '--pre', + '--trusted-host', + '--process-dependency-links', + '--extra-index-url'))): + line = line.rstrip() + if line not in emitted_options: + emitted_options.add(line) + yield line + continue + + if line.startswith('-e') or line.startswith('--editable'): + if line.startswith('-e'): + line = line[2:].strip() + else: + line = line[len('--editable'):].strip().lstrip('=') + line_req = InstallRequirement.from_editable( + line, + default_vcs=default_vcs, + isolated=isolated, + wheel_cache=wheel_cache, + ) + else: + line_req = InstallRequirement.from_line( + line, + isolated=isolated, + wheel_cache=wheel_cache, + ) + + if not line_req.name: + logger.info( + "Skipping line in requirement file [%s] because " + "it's not clear what it would install: %s", + req_file_path, line.strip(), + ) + logger.info( + " (add #egg=PackageName to the URL to avoid" + " this warning)" + ) + elif line_req.name not in installations: + logger.warning( + "Requirement file [%s] contains %s, but that " + "package is not installed", + req_file_path, line.strip(), + ) + else: + yield str(installations[line_req.name]).rstrip() + del installations[line_req.name] + + yield( + '## The following requirements were added by ' + 'pip freeze:' + ) + for installation in sorted( + installations.values(), key=lambda x: x.name.lower()): + if canonicalize_name(installation.name) not in skip: + yield str(installation).rstrip() diff --git a/pip/pep425tags.py b/pip/pep425tags.py index 95d37539510..dd0ca73999d 100644 --- a/pip/pep425tags.py +++ b/pip/pep425tags.py @@ -1,7 +1,11 @@ """Generate and work with PEP 425 Compatibility Tags.""" +from __future__ import absolute_import +import re import sys import warnings +import platform +import logging try: import sysconfig @@ -10,6 +14,21 @@ import distutils.sysconfig as sysconfig import distutils.util +from pip.compat import OrderedDict +import pip.utils.glibc + +logger = logging.getLogger(__name__) + +_osx_arch_pat = re.compile(r'(.+)_(\d+)_(\d+)_(.+)') + + +def get_config_var(var): + try: + return sysconfig.get_config_var(var) + except IOError as e: # Issue #1074 + warnings.warn("{0}".format(e), RuntimeWarning) + return None + def get_abbr_impl(): """Return abbreviated implementation name.""" @@ -26,13 +45,182 @@ def get_abbr_impl(): def get_impl_ver(): """Return implementation version.""" - return ''.join(map(str, sys.version_info[:2])) + impl_ver = get_config_var("py_version_nodot") + if not impl_ver or get_abbr_impl() == 'pp': + impl_ver = ''.join(map(str, get_impl_version_info())) + return impl_ver + + +def get_impl_version_info(): + """Return sys.version_info-like tuple for use in decrementing the minor + version.""" + if get_abbr_impl() == 'pp': + # as per https://github.com/pypa/pip/issues/2882 + return (sys.version_info[0], sys.pypy_version_info.major, + sys.pypy_version_info.minor) + else: + return sys.version_info[0], sys.version_info[1] + + +def get_impl_tag(): + """ + Returns the Tag for this specific implementation. + """ + return "{0}{1}".format(get_abbr_impl(), get_impl_ver()) + + +def get_flag(var, fallback, expected=True, warn=True): + """Use a fallback method for determining SOABI flags if the needed config + var is unset or unavailable.""" + val = get_config_var(var) + if val is None: + if warn: + logger.debug("Config variable '%s' is unset, Python ABI tag may " + "be incorrect", var) + return fallback() + return val == expected + + +def get_abi_tag(): + """Return the ABI tag based on SOABI (if available) or emulate SOABI + (CPython 2, PyPy).""" + soabi = get_config_var('SOABI') + impl = get_abbr_impl() + if not soabi and impl in ('cp', 'pp') and hasattr(sys, 'maxunicode'): + d = '' + m = '' + u = '' + if get_flag('Py_DEBUG', + lambda: hasattr(sys, 'gettotalrefcount'), + warn=(impl == 'cp')): + d = 'd' + if get_flag('WITH_PYMALLOC', + lambda: impl == 'cp', + warn=(impl == 'cp')): + m = 'm' + if get_flag('Py_UNICODE_SIZE', + lambda: sys.maxunicode == 0x10ffff, + expected=4, + warn=(impl == 'cp' and + sys.version_info < (3, 3))) \ + and sys.version_info < (3, 3): + u = 'u' + abi = '%s%s%s%s%s' % (impl, get_impl_ver(), d, m, u) + elif soabi and soabi.startswith('cpython-'): + abi = 'cp' + soabi.split('-')[1] + elif soabi: + abi = soabi.replace('.', '_').replace('-', '_') + else: + abi = None + return abi + + +def _is_running_32bit(): + return sys.maxsize == 2147483647 def get_platform(): """Return our platform name 'win32', 'linux_x86_64'""" + if sys.platform == 'darwin': + # distutils.util.get_platform() returns the release based on the value + # of MACOSX_DEPLOYMENT_TARGET on which Python was built, which may + # be significantly older than the user's current machine. + release, _, machine = platform.mac_ver() + split_ver = release.split('.') + + if machine == "x86_64" and _is_running_32bit(): + machine = "i386" + elif machine == "ppc64" and _is_running_32bit(): + machine = "ppc" + + return 'macosx_{0}_{1}_{2}'.format(split_ver[0], split_ver[1], machine) + # XXX remove distutils dependency - return distutils.util.get_platform().replace('.', '_').replace('-', '_') + result = distutils.util.get_platform().replace('.', '_').replace('-', '_') + if result == "linux_x86_64" and _is_running_32bit(): + # 32 bit Python program (running on a 64 bit Linux): pip should only + # install and run 32 bit compiled extensions in that case. + result = "linux_i686" + + return result + + +def is_manylinux1_compatible(): + # Only Linux, and only x86-64 / i686 + if get_platform() not in ("linux_x86_64", "linux_i686"): + return False + + # Check for presence of _manylinux module + try: + import _manylinux + return bool(_manylinux.manylinux1_compatible) + except (ImportError, AttributeError): + # Fall through to heuristic check below + pass + + # Check glibc version. CentOS 5 uses glibc 2.5. + return pip.utils.glibc.have_compatible_glibc(2, 5) + + +def get_darwin_arches(major, minor, machine): + """Return a list of supported arches (including group arches) for + the given major, minor and machine architecture of an OS X machine. + """ + arches = [] + + def _supports_arch(major, minor, arch): + # Looking at the application support for OS X versions in the chart + # provided by https://en.wikipedia.org/wiki/OS_X#Versions it appears + # our timeline looks roughly like: + # + # 10.0 - Introduces ppc support. + # 10.4 - Introduces ppc64, i386, and x86_64 support, however the ppc64 + # and x86_64 support is CLI only, and cannot be used for GUI + # applications. + # 10.5 - Extends ppc64 and x86_64 support to cover GUI applications. + # 10.6 - Drops support for ppc64 + # 10.7 - Drops support for ppc + # + # Given that we do not know if we're installing a CLI or a GUI + # application, we must be conservative and assume it might be a GUI + # application and behave as if ppc64 and x86_64 support did not occur + # until 10.5. + # + # Note: The above information is taken from the "Application support" + # column in the chart not the "Processor support" since I believe + # that we care about what instruction sets an application can use + # not which processors the OS supports. + if arch == 'ppc': + return (major, minor) <= (10, 5) + if arch == 'ppc64': + return (major, minor) == (10, 5) + if arch == 'i386': + return (major, minor) >= (10, 4) + if arch == 'x86_64': + return (major, minor) >= (10, 5) + if arch in groups: + for garch in groups[arch]: + if _supports_arch(major, minor, garch): + return True + return False + + groups = OrderedDict([ + ("fat", ("i386", "ppc")), + ("intel", ("x86_64", "i386")), + ("fat64", ("x86_64", "ppc64")), + ("fat32", ("x86_64", "i386", "ppc")), + ]) + + if _supports_arch(major, minor, machine): + arches.append(machine) + + for garch in groups: + if machine in groups[garch] and _supports_arch(major, minor, garch): + arches.append(garch) + + arches.append('universal') + + return arches def get_supported(versions=None, noarch=False): @@ -47,23 +235,19 @@ def get_supported(versions=None, noarch=False): # Versions must be given with respect to the preference if versions is None: versions = [] - major = sys.version_info[0] + version_info = get_impl_version_info() + major = version_info[:-1] # Support all previous minor Python versions. - for minor in range(sys.version_info[1], -1, -1): - versions.append(''.join(map(str, (major, minor)))) + for minor in range(version_info[-1], -1, -1): + versions.append(''.join(map(str, major + (minor,)))) impl = get_abbr_impl() abis = [] - try: - soabi = sysconfig.get_config_var('SOABI') - except IOError as e: # Issue #1074 - warnings.warn("{0}".format(e), RuntimeWarning) - soabi = None - - if soabi and soabi.startswith('cpython-'): - abis[0:0] = ['cp' + soabi.split('-', 1)[-1]] + abi = get_abi_tag() + if abi: + abis[0:0] = [abi] abi3s = set() import imp @@ -77,18 +261,38 @@ def get_supported(versions=None, noarch=False): if not noarch: arch = get_platform() + if sys.platform == 'darwin': + # support macosx-10.6-intel on macosx-10.9-x86_64 + match = _osx_arch_pat.match(arch) + if match: + name, major, minor, actual_arch = match.groups() + tpl = '{0}_{1}_%i_%s'.format(name, major) + arches = [] + for m in reversed(range(int(minor) + 1)): + for a in get_darwin_arches(int(major), m, actual_arch): + arches.append(tpl % (m, a)) + else: + # arch pattern didn't match (?!) + arches = [arch] + elif is_manylinux1_compatible(): + arches = [arch.replace('linux', 'manylinux1'), arch] + else: + arches = [arch] # Current version, current API (built specifically for our Python): for abi in abis: - supported.append(('%s%s' % (impl, versions[0]), abi, arch)) + for arch in arches: + supported.append(('%s%s' % (impl, versions[0]), abi, arch)) + + # Has binaries, does not use the Python API: + for arch in arches: + supported.append(('py%s' % (versions[0][0]), 'none', arch)) # No abi / arch, but requires our implementation: - for i, version in enumerate(versions): - supported.append(('%s%s' % (impl, version), 'none', 'any')) - if i == 0: - # Tagged specifically as being cross-version compatible - # (with just the major version specified) - supported.append(('%s%s' % (impl, versions[0][0]), 'none', 'any')) + supported.append(('%s%s' % (impl, versions[0]), 'none', 'any')) + # Tagged specifically as being cross-version compatible + # (with just the major version specified) + supported.append(('%s%s' % (impl, versions[0][0]), 'none', 'any')) # No abi / arch, generic Python for i, version in enumerate(versions): @@ -100,3 +304,5 @@ def get_supported(versions=None, noarch=False): supported_tags = get_supported() supported_tags_noarch = get_supported(noarch=True) + +implementation_tag = get_impl_tag() diff --git a/pip/req.py b/pip/req.py deleted file mode 100644 index e9ea20f3b07..00000000000 --- a/pip/req.py +++ /dev/null @@ -1,1931 +0,0 @@ -from email.parser import FeedParser -import os -import imp -import locale -import re -import sys -import shutil -import tempfile -import textwrap -import zipfile - -from distutils.util import change_root -from pip.locations import (bin_py, running_under_virtualenv,PIP_DELETE_MARKER_FILENAME, - write_delete_marker_file, bin_user) -from pip.exceptions import (InstallationError, UninstallationError, UnsupportedWheel, - BestVersionAlreadyInstalled, InvalidWheelFilename, - DistributionNotFound, PreviousBuildDirError) -from pip.vcs import vcs -from pip.log import logger -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, is_prerelease, normalize_name) -from pip.backwardcompat import (urlparse, urllib, uses_pycache, - ConfigParser, string_types, HTTPError, - get_python_version, b) -from pip.index import Link -from pip.locations import build_prefix -from pip.download import (PipSession, get_file_content, is_url, url_to_path, - path_to_url, is_archive_file, - unpack_vcs_link, is_vcs_url, is_file_url, - unpack_file_url, unpack_http_url) -import pip.wheel -from pip.wheel import move_wheel_files, Wheel, wheel_ext -from pip._vendor import pkg_resources, six - - -def read_text_file(filename): - """Return the contents of *filename*. - - Try to decode the file contents with utf-8, the preffered system encoding - (e.g., cp1252 on some Windows machines) and latin1, in that order. Decoding - a byte string with latin1 will never raise an error. In the worst case, the - returned string will contain some garbage characters. - - """ - with open(filename, 'rb') as fp: - data = fp.read() - - encodings = ['utf-8', locale.getpreferredencoding(False), 'latin1'] - for enc in encodings: - try: - data = data.decode(enc) - except UnicodeDecodeError: - continue - break - - assert type(data) != bytes # Latin1 should have worked. - return data - - -class InstallRequirement(object): - - def __init__(self, req, comes_from, source_dir=None, editable=False, - url=None, as_egg=False, update=True, prereleases=None, - editable_options=None, from_bundle=False, pycompile=True): - self.extras = () - if isinstance(req, string_types): - req = pkg_resources.Requirement.parse(req) - self.extras = req.extras - self.req = req - self.comes_from = comes_from - self.source_dir = source_dir - self.editable = editable - - if editable_options is None: - editable_options = {} - - self.editable_options = editable_options - self.url = url - self.as_egg = as_egg - self._egg_info_path = None - # This holds the pkg_resources.Distribution object if this requirement - # is already available: - self.satisfied_by = None - # This hold the pkg_resources.Distribution object if this requirement - # conflicts with another installed distribution: - self.conflicts_with = None - self._temp_build_dir = None - self._is_bundle = None - # True if the editable should be updated: - self.update = update - # Set to True after successful installation - self.install_succeeded = None - # UninstallPathSet of uninstalled distribution (for possible rollback) - self.uninstalled = None - self.use_user_site = False - self.target_dir = None - self.from_bundle = from_bundle - - self.pycompile = pycompile - - # True if pre-releases are acceptable - if prereleases: - self.prereleases = True - elif self.req is not None: - self.prereleases = any([is_prerelease(x[1]) and x[0] != "!=" for x in self.req.specs]) - else: - self.prereleases = False - - @classmethod - def from_editable(cls, editable_req, comes_from=None, default_vcs=None): - name, url, extras_override = parse_editable(editable_req, default_vcs) - if url.startswith('file:'): - source_dir = url_to_path(url) - else: - source_dir = None - - res = cls(name, comes_from, source_dir=source_dir, - editable=True, - url=url, - editable_options=extras_override, - prereleases=True) - - if extras_override is not None: - res.extras = extras_override - - return res - - @classmethod - def from_line(cls, name, comes_from=None, prereleases=None): - """Creates an InstallRequirement from a name, which might be a - requirement, directory containing 'setup.py', filename, or URL. - """ - url = None - name = name.strip() - req = None - path = os.path.normpath(os.path.abspath(name)) - link = None - - if is_url(name): - 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 'setup.py' 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) - 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_without_fragment - req = link.egg_fragment #when fragment is None, this will become an 'unnamed' requirement - - # Handle relative file URLs - if link.scheme == 'file' and re.search(r'\.\./', url): - url = path_to_url(os.path.normpath(os.path.abspath(link.path))) - - # fail early for invalid or unsupported wheels - if link.ext == wheel_ext: - wheel = Wheel(link.filename) # can raise InvalidWheelFilename - if not wheel.supported(): - raise UnsupportedWheel("%s is not a supported wheel on this platform." % wheel.filename) - - else: - req = name - - return cls(req, comes_from, url=url, prereleases=prereleases) - - def __str__(self): - if self.req: - s = str(self.req) - if self.url: - s += ' from %s' % self.url - else: - s = self.url - if self.satisfied_by is not None: - s += ' in %s' % display_path(self.satisfied_by.location) - if self.comes_from: - if isinstance(self.comes_from, string_types): - comes_from = self.comes_from - else: - comes_from = self.comes_from.from_path() - if comes_from: - s += ' (from %s)' % comes_from - return s - - def from_path(self): - if self.req is None: - return None - s = str(self.req) - if self.comes_from: - if isinstance(self.comes_from, string_types): - comes_from = self.comes_from - else: - comes_from = self.comes_from.from_path() - if comes_from: - s += '->' + comes_from - return s - - def build_location(self, build_dir, unpack=True): - if self._temp_build_dir is not None: - return self._temp_build_dir - if self.req is None: - self._temp_build_dir = tempfile.mkdtemp('-build', 'pip-') - self._ideal_build_dir = build_dir - return self._temp_build_dir - if self.editable: - name = self.name.lower() - else: - name = self.name - # FIXME: Is there a better place to create the build_dir? (hg and bzr need this) - if not os.path.exists(build_dir): - _make_build_dir(build_dir) - return os.path.join(build_dir, name) - - def correct_build_location(self): - """If the build location was a temporary directory, this will move it - to a new more permanent location""" - if self.source_dir is not None: - return - assert self.req is not None - assert self._temp_build_dir - old_location = self._temp_build_dir - new_build_dir = self._ideal_build_dir - del self._ideal_build_dir - if self.editable: - name = self.name.lower() - else: - name = self.name - new_location = os.path.join(new_build_dir, name) - if not os.path.exists(new_build_dir): - logger.debug('Creating directory %s' % new_build_dir) - _make_build_dir(new_build_dir) - if os.path.exists(new_location): - raise InstallationError( - 'A package already exists in %s; please remove it to continue' - % display_path(new_location)) - logger.debug('Moving package %s from %s to new location %s' - % (self, display_path(old_location), display_path(new_location))) - shutil.move(old_location, new_location) - self._temp_build_dir = new_location - self.source_dir = new_location - self._egg_info_path = None - - @property - def name(self): - if self.req is None: - return None - return self.req.project_name - - @property - def url_name(self): - if self.req is None: - return None - return urllib.quote(self.req.unsafe_name) - - @property - def setup_py(self): - try: - import setuptools - except ImportError: - # Setuptools is not available - raise InstallationError( - "setuptools must be installed to install from a source " - "distribution" - ) - - setup_file = 'setup.py' - - if self.editable_options and 'subdirectory' in self.editable_options: - setup_py = os.path.join(self.source_dir, - self.editable_options['subdirectory'], - setup_file) - - else: - setup_py = os.path.join(self.source_dir, setup_file) - - # Python2 __file__ should not be unicode - if six.PY2 and isinstance(setup_py, six.text_type): - setup_py = setup_py.encode(sys.getfilesystemencoding()) - - return setup_py - - def run_egg_info(self, force_root_egg_info=False): - assert self.source_dir - if self.name: - logger.notify('Running setup.py (path:%s) egg_info for package %s' % (self.setup_py, self.name)) - else: - logger.notify('Running setup.py (path:%s) egg_info for package from %s' % (self.setup_py, self.url)) - logger.indent += 2 - try: - - # if it's distribute>=0.7, it won't contain an importable - # setuptools, and having an egg-info dir blocks the ability of - # setup.py to find setuptools plugins, so delete the egg-info dir if - # no setuptools. it will get recreated by the run of egg_info - # NOTE: this self.name check only works when installing from a specifier - # (not archive path/urls) - # TODO: take this out later - if self.name == 'distribute' and not os.path.isdir(os.path.join(self.source_dir, 'setuptools')): - rmtree(os.path.join(self.source_dir, 'distribute.egg-info')) - - script = self._run_setup_py - script = script.replace('__SETUP_PY__', repr(self.setup_py)) - script = script.replace('__PKG_NAME__', repr(self.name)) - egg_info_cmd = [sys.executable, '-c', script, 'egg_info'] - # We can't put the .egg-info files at the root, because then the source code will be mistaken - # for an installed egg, causing problems - if self.editable or force_root_egg_info: - egg_base_option = [] - else: - egg_info_dir = os.path.join(self.source_dir, 'pip-egg-info') - if not os.path.exists(egg_info_dir): - os.makedirs(egg_info_dir) - egg_base_option = ['--egg-base', 'pip-egg-info'] - call_subprocess( - egg_info_cmd + egg_base_option, - cwd=self.source_dir, filter_stdout=self._filter_install, show_stdout=False, - command_level=logger.VERBOSE_DEBUG, - command_desc='python setup.py egg_info') - finally: - logger.indent -= 2 - if not self.req: - self.req = pkg_resources.Requirement.parse( - "%(Name)s==%(Version)s" % self.pkg_info()) - self.correct_build_location() - - ## FIXME: This is a lame hack, entirely for PasteScript which has - ## a self-provided entry point that causes this awkwardness - _run_setup_py = """ -__file__ = __SETUP_PY__ -from setuptools.command import egg_info -import pkg_resources -import os -import tokenize -def replacement_run(self): - self.mkpath(self.egg_info) - installer = self.distribution.fetch_build_egg - for ep in pkg_resources.iter_entry_points('egg_info.writers'): - # require=False is the change we're making: - writer = ep.load(require=False) - if writer: - writer(self, ep.name, os.path.join(self.egg_info,ep.name)) - self.find_sources() -egg_info.egg_info.run = replacement_run -exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec')) -""" - - def egg_info_data(self, filename): - if self.satisfied_by is not None: - if not self.satisfied_by.has_metadata(filename): - return None - return self.satisfied_by.get_metadata(filename) - assert self.source_dir - filename = self.egg_info_path(filename) - if not os.path.exists(filename): - return None - data = read_text_file(filename) - return data - - def egg_info_path(self, filename): - if self._egg_info_path is None: - if self.editable: - base = self.source_dir - else: - base = os.path.join(self.source_dir, 'pip-egg-info') - filenames = os.listdir(base) - if self.editable: - filenames = [] - for root, dirs, files in os.walk(base): - for dir in vcs.dirnames: - if dir in dirs: - dirs.remove(dir) - # Iterate over a copy of ``dirs``, since mutating - # a list while iterating over it can cause trouble. - # (See https://github.com/pypa/pip/pull/462.) - for dir in list(dirs): - # Don't search in anything that looks like a virtualenv environment - if (os.path.exists(os.path.join(root, dir, 'bin', 'python')) - or os.path.exists(os.path.join(root, dir, 'Scripts', 'Python.exe'))): - dirs.remove(dir) - # Also don't search through tests - if dir == 'test' or dir == 'tests': - dirs.remove(dir) - filenames.extend([os.path.join(root, dir) - for dir in dirs]) - filenames = [f for f in filenames if f.endswith('.egg-info')] - - if not filenames: - raise InstallationError('No files/directories in %s (from %s)' % (base, filename)) - assert filenames, "No files/directories in %s (from %s)" % (base, filename) - - # if we have more than one match, we pick the toplevel one. This can - # easily be the case if there is a dist folder which contains an - # extracted tarball for testing purposes. - if len(filenames) > 1: - filenames.sort(key=lambda x: x.count(os.path.sep) + - (os.path.altsep and - x.count(os.path.altsep) or 0)) - self._egg_info_path = os.path.join(base, filenames[0]) - return os.path.join(self._egg_info_path, filename) - - def egg_info_lines(self, filename): - data = self.egg_info_data(filename) - if not data: - return [] - result = [] - for line in data.splitlines(): - line = line.strip() - if not line or line.startswith('#'): - continue - result.append(line) - return result - - def pkg_info(self): - p = FeedParser() - data = self.egg_info_data('PKG-INFO') - if not data: - logger.warn('No PKG-INFO file found in %s' % display_path(self.egg_info_path('PKG-INFO'))) - p.feed(data or '') - return p.close() - - @property - def dependency_links(self): - return self.egg_info_lines('dependency_links.txt') - - _requirements_section_re = re.compile(r'\[(.*?)\]') - - def requirements(self, extras=()): - in_extra = None - for line in self.egg_info_lines('requires.txt'): - match = self._requirements_section_re.match(line.lower()) - if match: - in_extra = match.group(1) - continue - if in_extra and in_extra not in extras: - logger.debug('skipping extra %s' % in_extra) - # Skip requirement for an extra we aren't requiring - continue - yield line - - @property - def absolute_versions(self): - for qualifier, version in self.req.specs: - if qualifier == '==': - yield version - - @property - def installed_version(self): - return self.pkg_info()['version'] - - def assert_source_matches_version(self): - assert self.source_dir - version = self.installed_version - if version not in self.req: - logger.warn('Requested %s, but installing version %s' % (self, self.installed_version)) - else: - logger.debug('Source in %s has version %s, which satisfies requirement %s' - % (display_path(self.source_dir), version, self)) - - def update_editable(self, obtain=True): - if not self.url: - logger.info("Cannot update repository at %s; repository location is unknown" % self.source_dir) - return - assert self.editable - assert self.source_dir - if self.url.startswith('file:'): - # Static paths don't get updated - return - assert '+' in self.url, "bad url: %r" % self.url - if not self.update: - return - vc_type, url = self.url.split('+', 1) - backend = vcs.get_backend(vc_type) - if backend: - vcs_backend = backend(self.url) - if obtain: - vcs_backend.obtain(self.source_dir) - else: - vcs_backend.export(self.source_dir) - else: - assert 0, ( - 'Unexpected version control type (in %s): %s' - % (self.url, vc_type)) - - def uninstall(self, auto_confirm=False): - """ - Uninstall the distribution currently satisfying this requirement. - - Prompts before removing or modifying files unless - ``auto_confirm`` is True. - - Refuses to delete or modify files outside of ``sys.prefix`` - - thus uninstallation within a virtual environment can only - modify that virtual environment, even if the virtualenv is - linked to global site-packages. - - """ - if not self.check_if_exists(): - raise UninstallationError("Cannot uninstall requirement %s, not installed" % (self.name,)) - dist = self.satisfied_by or self.conflicts_with - - paths_to_remove = UninstallPathSet(dist) - - pip_egg_info_path = os.path.join(dist.location, - dist.egg_name()) + '.egg-info' - dist_info_path = os.path.join(dist.location, - '-'.join(dist.egg_name().split('-')[:2]) - ) + '.dist-info' - # workaround for http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=618367 - debian_egg_info_path = pip_egg_info_path.replace( - '-py%s' % pkg_resources.PY_MAJOR, '') - easy_install_egg = dist.egg_name() + '.egg' - develop_egg_link = egg_link_path(dist) - - pip_egg_info_exists = os.path.exists(pip_egg_info_path) - debian_egg_info_exists = os.path.exists(debian_egg_info_path) - dist_info_exists = os.path.exists(dist_info_path) - if pip_egg_info_exists or debian_egg_info_exists: - # package installed by pip - if pip_egg_info_exists: - egg_info_path = pip_egg_info_path - else: - egg_info_path = debian_egg_info_path - paths_to_remove.add(egg_info_path) - if dist.has_metadata('installed-files.txt'): - for installed_file in dist.get_metadata('installed-files.txt').splitlines(): - path = os.path.normpath(os.path.join(egg_info_path, installed_file)) - paths_to_remove.add(path) - #FIXME: need a test for this elif block - #occurs with --single-version-externally-managed/--record outside of pip - elif dist.has_metadata('top_level.txt'): - if dist.has_metadata('namespace_packages.txt'): - namespaces = dist.get_metadata('namespace_packages.txt') - else: - namespaces = [] - for top_level_pkg in [p for p - in dist.get_metadata('top_level.txt').splitlines() - if p and p not in namespaces]: - path = os.path.join(dist.location, top_level_pkg) - paths_to_remove.add(path) - paths_to_remove.add(path + '.py') - paths_to_remove.add(path + '.pyc') - - elif dist.location.endswith(easy_install_egg): - # package installed by easy_install - paths_to_remove.add(dist.location) - easy_install_pth = os.path.join(os.path.dirname(dist.location), - 'easy-install.pth') - paths_to_remove.add_pth(easy_install_pth, './' + easy_install_egg) - - elif develop_egg_link: - # develop egg - fh = open(develop_egg_link, 'r') - link_pointer = os.path.normcase(fh.readline().strip()) - fh.close() - assert (link_pointer == dist.location), 'Egg-link %s does not match installed location of %s (at %s)' % (link_pointer, self.name, dist.location) - paths_to_remove.add(develop_egg_link) - easy_install_pth = os.path.join(os.path.dirname(develop_egg_link), - 'easy-install.pth') - paths_to_remove.add_pth(easy_install_pth, dist.location) - elif dist_info_exists: - for path in pip.wheel.uninstallation_paths(dist): - paths_to_remove.add(path) - - # find distutils scripts= scripts - if dist.has_metadata('scripts') and dist.metadata_isdir('scripts'): - for script in dist.metadata_listdir('scripts'): - if dist_in_usersite(dist): - bin_dir = bin_user - else: - bin_dir = bin_py - paths_to_remove.add(os.path.join(bin_dir, script)) - if sys.platform == 'win32': - paths_to_remove.add(os.path.join(bin_dir, script) + '.bat') - - # find console_scripts - if dist.has_metadata('entry_points.txt'): - config = ConfigParser.SafeConfigParser() - config.readfp(FakeFile(dist.get_metadata_lines('entry_points.txt'))) - if config.has_section('console_scripts'): - for name, value in config.items('console_scripts'): - if dist_in_usersite(dist): - bin_dir = bin_user - else: - bin_dir = bin_py - paths_to_remove.add(os.path.join(bin_dir, name)) - if sys.platform == 'win32': - paths_to_remove.add(os.path.join(bin_dir, name) + '.exe') - paths_to_remove.add(os.path.join(bin_dir, name) + '.exe.manifest') - paths_to_remove.add(os.path.join(bin_dir, name) + '-script.py') - - paths_to_remove.remove(auto_confirm) - self.uninstalled = paths_to_remove - - def rollback_uninstall(self): - if self.uninstalled: - self.uninstalled.rollback() - else: - logger.error("Can't rollback %s, nothing uninstalled." - % (self.project_name,)) - - def commit_uninstall(self): - if self.uninstalled: - self.uninstalled.commit() - else: - logger.error("Can't commit %s, nothing uninstalled." - % (self.project_name,)) - - def archive(self, build_dir): - assert self.source_dir - create_archive = True - archive_name = '%s-%s.zip' % (self.name, self.installed_version) - archive_path = os.path.join(build_dir, archive_name) - if os.path.exists(archive_path): - response = ask_path_exists( - 'The file %s exists. (i)gnore, (w)ipe, (b)ackup ' % - display_path(archive_path), ('i', 'w', 'b')) - if response == 'i': - create_archive = False - elif response == 'w': - logger.warn('Deleting %s' % display_path(archive_path)) - os.remove(archive_path) - elif response == 'b': - dest_file = backup_dir(archive_path) - logger.warn('Backing up %s to %s' - % (display_path(archive_path), display_path(dest_file))) - shutil.move(archive_path, dest_file) - if create_archive: - zip = zipfile.ZipFile(archive_path, 'w', zipfile.ZIP_DEFLATED) - dir = os.path.normcase(os.path.abspath(self.source_dir)) - for dirpath, dirnames, filenames in os.walk(dir): - if 'pip-egg-info' in dirnames: - dirnames.remove('pip-egg-info') - for dirname in dirnames: - dirname = os.path.join(dirpath, dirname) - name = self._clean_zip_name(dirname, dir) - zipdir = zipfile.ZipInfo(self.name + '/' + name + '/') - zipdir.external_attr = 0x1ED << 16 # 0o755 - zip.writestr(zipdir, '') - for filename in filenames: - if filename == PIP_DELETE_MARKER_FILENAME: - continue - filename = os.path.join(dirpath, filename) - name = self._clean_zip_name(filename, dir) - zip.write(filename, self.name + '/' + name) - zip.close() - logger.indent -= 2 - logger.notify('Saved %s' % display_path(archive_path)) - - def _clean_zip_name(self, name, prefix): - assert name.startswith(prefix+os.path.sep), ( - "name %r doesn't start with prefix %r" % (name, prefix)) - name = name[len(prefix)+1:] - name = name.replace(os.path.sep, '/') - return name - - def install(self, install_options, global_options=(), root=None): - if self.editable: - self.install_editable(install_options, global_options) - return - if self.is_wheel: - version = pip.wheel.wheel_version(self.source_dir) - pip.wheel.check_compatibility(version, self.name) - - self.move_wheel_files(self.source_dir, root=root) - self.install_succeeded = True - return - - temp_location = tempfile.mkdtemp('-record', 'pip-') - record_filename = os.path.join(temp_location, 'install-record.txt') - try: - install_args = [sys.executable] - install_args.append('-c') - install_args.append( - "import setuptools, tokenize;__file__=%r;"\ - "exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))" % self.setup_py) - install_args += list(global_options) + ['install','--record', record_filename] - - if not self.as_egg: - install_args += ['--single-version-externally-managed'] - - if root is not None: - install_args += ['--root', root] - - if self.pycompile: - install_args += ["--compile"] - else: - install_args += ["--no-compile"] - - if running_under_virtualenv(): - ## FIXME: I'm not sure if this is a reasonable location; probably not - ## but we can't put it in the default location, as that is a virtualenv symlink that isn't writable - install_args += ['--install-headers', - os.path.join(sys.prefix, 'include', 'site', - 'python' + get_python_version())] - logger.notify('Running setup.py install for %s' % self.name) - logger.indent += 2 - try: - call_subprocess(install_args + install_options, - cwd=self.source_dir, filter_stdout=self._filter_install, show_stdout=False) - finally: - logger.indent -= 2 - if not os.path.exists(record_filename): - logger.notify('Record file %s not found' % record_filename) - return - self.install_succeeded = True - if self.as_egg: - # there's no --always-unzip option we can pass to install command - # so we unable to save the installed-files.txt - return - - def prepend_root(path): - if root is None or not os.path.isabs(path): - return path - else: - return change_root(root, path) - - f = open(record_filename) - for line in f: - line = line.strip() - if line.endswith('.egg-info'): - egg_info_dir = prepend_root(line) - break - else: - logger.warn('Could not find .egg-info directory in install record for %s' % self) - ## FIXME: put the record somewhere - ## FIXME: should this be an error? - return - f.close() - new_lines = [] - f = open(record_filename) - for line in f: - filename = line.strip() - if os.path.isdir(filename): - filename += os.path.sep - new_lines.append(make_path_relative(prepend_root(filename), egg_info_dir)) - f.close() - f = open(os.path.join(egg_info_dir, 'installed-files.txt'), 'w') - f.write('\n'.join(new_lines)+'\n') - f.close() - finally: - if os.path.exists(record_filename): - os.remove(record_filename) - os.rmdir(temp_location) - - def remove_temporary_source(self): - """Remove the source files from this requirement, if they are marked - for deletion""" - if self.is_bundle or os.path.exists(self.delete_marker_filename): - logger.info('Removing source in %s' % self.source_dir) - if self.source_dir: - rmtree(self.source_dir) - self.source_dir = None - if self._temp_build_dir and os.path.exists(self._temp_build_dir): - rmtree(self._temp_build_dir) - self._temp_build_dir = None - - def install_editable(self, install_options, global_options=()): - logger.notify('Running setup.py develop for %s' % self.name) - logger.indent += 2 - try: - ## FIXME: should we do --install-headers here too? - call_subprocess( - [sys.executable, '-c', - "import setuptools, tokenize; __file__=%r; exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))" % self.setup_py] - + list(global_options) + ['develop', '--no-deps'] + list(install_options), - - cwd=self.source_dir, filter_stdout=self._filter_install, - show_stdout=False) - finally: - logger.indent -= 2 - self.install_succeeded = True - - def _filter_install(self, line): - level = logger.NOTIFY - for regex in [r'^running .*', r'^writing .*', '^creating .*', '^[Cc]opying .*', - r'^reading .*', r"^removing .*\.egg-info' \(and everything under it\)$", - r'^byte-compiling ', - # Not sure what this warning is, but it seems harmless: - r"^warning: manifest_maker: standard file '-c' not found$"]: - if re.search(regex, line.strip()): - level = logger.INFO - break - return (level, line) - - def check_if_exists(self): - """Find an installed distribution that satisfies or conflicts - with this requirement, and set self.satisfied_by or - self.conflicts_with appropriately.""" - - if self.req is None: - return False - try: - # DISTRIBUTE TO SETUPTOOLS UPGRADE HACK (1 of 3 parts) - # if we've already set distribute as a conflict to setuptools - # then this check has already run before. we don't want it to - # run again, and return False, since it would block the uninstall - # TODO: remove this later - if (self.req.project_name == 'setuptools' - and self.conflicts_with - and self.conflicts_with.project_name == 'distribute'): - return True - else: - self.satisfied_by = pkg_resources.get_distribution(self.req) - except pkg_resources.DistributionNotFound: - return False - except pkg_resources.VersionConflict: - existing_dist = pkg_resources.get_distribution(self.req.project_name) - if self.use_user_site: - if dist_in_usersite(existing_dist): - self.conflicts_with = existing_dist - elif running_under_virtualenv() and dist_in_site_packages(existing_dist): - raise InstallationError("Will not install to the user site because it will lack sys.path precedence to %s in %s" - %(existing_dist.project_name, existing_dist.location)) - else: - self.conflicts_with = existing_dist - return True - - @property - def is_wheel(self): - return self.url and '.whl' in self.url - - @property - def is_bundle(self): - if self._is_bundle is not None: - return self._is_bundle - base = self._temp_build_dir - if not base: - ## FIXME: this doesn't seem right: - return False - self._is_bundle = (os.path.exists(os.path.join(base, 'pip-manifest.txt')) - or os.path.exists(os.path.join(base, 'pyinstall-manifest.txt'))) - return self._is_bundle - - def bundle_requirements(self): - for dest_dir in self._bundle_editable_dirs: - package = os.path.basename(dest_dir) - ## FIXME: svnism: - for vcs_backend in vcs.backends: - url = rev = None - vcs_bundle_file = os.path.join( - dest_dir, vcs_backend.bundle_file) - if os.path.exists(vcs_bundle_file): - vc_type = vcs_backend.name - fp = open(vcs_bundle_file) - content = fp.read() - fp.close() - url, rev = vcs_backend().parse_vcs_bundle_file(content) - break - if url: - url = '%s+%s@%s' % (vc_type, url, rev) - else: - url = None - yield InstallRequirement( - package, self, editable=True, url=url, - update=False, source_dir=dest_dir, from_bundle=True) - for dest_dir in self._bundle_build_dirs: - package = os.path.basename(dest_dir) - yield InstallRequirement(package, self,source_dir=dest_dir, from_bundle=True) - - def move_bundle_files(self, dest_build_dir, dest_src_dir): - base = self._temp_build_dir - assert base - src_dir = os.path.join(base, 'src') - build_dir = os.path.join(base, 'build') - bundle_build_dirs = [] - bundle_editable_dirs = [] - for source_dir, dest_dir, dir_collection in [ - (src_dir, dest_src_dir, bundle_editable_dirs), - (build_dir, dest_build_dir, bundle_build_dirs)]: - if os.path.exists(source_dir): - for dirname in os.listdir(source_dir): - dest = os.path.join(dest_dir, dirname) - dir_collection.append(dest) - if os.path.exists(dest): - logger.warn('The directory %s (containing package %s) already exists; cannot move source from bundle %s' - % (dest, dirname, self)) - continue - if not os.path.exists(dest_dir): - logger.info('Creating directory %s' % dest_dir) - os.makedirs(dest_dir) - shutil.move(os.path.join(source_dir, dirname), dest) - if not os.listdir(source_dir): - os.rmdir(source_dir) - self._temp_build_dir = None - self._bundle_build_dirs = bundle_build_dirs - self._bundle_editable_dirs = bundle_editable_dirs - - def move_wheel_files(self, wheeldir, root=None): - move_wheel_files( - self.name, self.req, wheeldir, - user=self.use_user_site, - home=self.target_dir, - root=root, - pycompile=self.pycompile, - ) - - @property - def delete_marker_filename(self): - assert self.source_dir - return os.path.join(self.source_dir, PIP_DELETE_MARKER_FILENAME) - - -class Requirements(object): - - def __init__(self): - self._keys = [] - self._dict = {} - - def keys(self): - return self._keys - - def values(self): - return [self._dict[key] for key in self._keys] - - def __contains__(self, item): - return item in self._keys - - def __setitem__(self, key, value): - if key not in self._keys: - self._keys.append(key) - self._dict[key] = value - - def __getitem__(self, key): - return self._dict[key] - - def __repr__(self): - values = ['%s: %s' % (repr(k), repr(self[k])) for k in self.keys()] - return 'Requirements({%s})' % ', '.join(values) - - -class RequirementSet(object): - - def __init__(self, build_dir, src_dir, download_dir, download_cache=None, - upgrade=False, ignore_installed=False, as_egg=False, - target_dir=None, ignore_dependencies=False, - force_reinstall=False, use_user_site=False, session=None, - pycompile=True, wheel_download_dir=None): - self.build_dir = build_dir - self.src_dir = src_dir - self.download_dir = download_dir - if download_cache: - download_cache = os.path.expanduser(download_cache) - self.download_cache = download_cache - self.upgrade = upgrade - self.ignore_installed = ignore_installed - self.force_reinstall = force_reinstall - self.requirements = Requirements() - # Mapping of alias: real_name - self.requirement_aliases = {} - self.unnamed_requirements = [] - self.ignore_dependencies = ignore_dependencies - self.successfully_downloaded = [] - self.successfully_installed = [] - self.reqs_to_cleanup = [] - self.as_egg = as_egg - self.use_user_site = use_user_site - self.target_dir = target_dir #set from --target option - self.session = session or PipSession() - self.pycompile = pycompile - self.wheel_download_dir = wheel_download_dir - - def __str__(self): - reqs = [req for req in self.requirements.values() - if not req.comes_from] - reqs.sort(key=lambda req: req.name.lower()) - return ' '.join([str(req.req) for req in reqs]) - - def add_requirement(self, install_req): - name = install_req.name - install_req.as_egg = self.as_egg - install_req.use_user_site = self.use_user_site - install_req.target_dir = self.target_dir - install_req.pycompile = self.pycompile - if not name: - #url or path requirement w/o an egg fragment - self.unnamed_requirements.append(install_req) - else: - if self.has_requirement(name): - raise InstallationError( - 'Double requirement given: %s (already in %s, name=%r)' - % (install_req, self.get_requirement(name), name)) - self.requirements[name] = install_req - ## FIXME: what about other normalizations? E.g., _ vs. -? - if name.lower() != name: - self.requirement_aliases[name.lower()] = name - - def has_requirement(self, project_name): - for name in project_name, project_name.lower(): - if name in self.requirements or name in self.requirement_aliases: - return True - return False - - @property - def has_requirements(self): - return list(self.requirements.values()) or self.unnamed_requirements - - @property - def has_editables(self): - if any(req.editable for req in self.requirements.values()): - return True - if any(req.editable for req in self.unnamed_requirements): - return True - return False - - @property - def is_download(self): - if self.download_dir: - self.download_dir = os.path.expanduser(self.download_dir) - if os.path.exists(self.download_dir): - return True - else: - logger.fatal('Could not find download directory') - raise InstallationError( - "Could not find or access download directory '%s'" - % display_path(self.download_dir)) - return False - - def get_requirement(self, project_name): - for name in project_name, project_name.lower(): - if name in self.requirements: - return self.requirements[name] - if name in self.requirement_aliases: - return self.requirements[self.requirement_aliases[name]] - raise KeyError("No project with the name %r" % project_name) - - def uninstall(self, auto_confirm=False): - for req in self.requirements.values(): - req.uninstall(auto_confirm=auto_confirm) - req.commit_uninstall() - - def locate_files(self): - ## FIXME: duplicates code from prepare_files; relevant code should - ## probably be factored out into a separate method - unnamed = list(self.unnamed_requirements) - reqs = list(self.requirements.values()) - while reqs or unnamed: - if unnamed: - req_to_install = unnamed.pop(0) - else: - req_to_install = reqs.pop(0) - install_needed = True - if not self.ignore_installed and not req_to_install.editable: - req_to_install.check_if_exists() - if req_to_install.satisfied_by: - if self.upgrade: - #don't uninstall conflict if user install and and conflict is not user install - if not (self.use_user_site and not dist_in_usersite(req_to_install.satisfied_by)): - req_to_install.conflicts_with = req_to_install.satisfied_by - req_to_install.satisfied_by = None - else: - install_needed = False - if req_to_install.satisfied_by: - logger.notify('Requirement already satisfied ' - '(use --upgrade to upgrade): %s' - % req_to_install) - - if req_to_install.editable: - if req_to_install.source_dir is None: - req_to_install.source_dir = req_to_install.build_location(self.src_dir) - elif install_needed: - req_to_install.source_dir = req_to_install.build_location(self.build_dir, not self.is_download) - - if req_to_install.source_dir is not None and not os.path.isdir(req_to_install.source_dir): - raise InstallationError('Could not install requirement %s ' - 'because source folder %s does not exist ' - '(perhaps --no-download was used without first running ' - 'an equivalent install with --no-install?)' - % (req_to_install, req_to_install.source_dir)) - - def prepare_files(self, finder, force_root_egg_info=False, bundle=False): - """Prepare process. Create temp directories, download and/or unpack files.""" - unnamed = list(self.unnamed_requirements) - reqs = list(self.requirements.values()) - while reqs or unnamed: - if unnamed: - req_to_install = unnamed.pop(0) - else: - req_to_install = reqs.pop(0) - install = True - best_installed = False - not_found = None - if not self.ignore_installed and not req_to_install.editable: - req_to_install.check_if_exists() - if req_to_install.satisfied_by: - if self.upgrade: - if not self.force_reinstall and not req_to_install.url: - try: - url = finder.find_requirement( - req_to_install, self.upgrade) - except BestVersionAlreadyInstalled: - best_installed = True - install = False - except DistributionNotFound: - not_found = sys.exc_info()[1] - else: - # Avoid the need to call find_requirement again - req_to_install.url = url.url - - if not best_installed: - #don't uninstall conflict if user install and conflict is not user install - if not (self.use_user_site and not dist_in_usersite(req_to_install.satisfied_by)): - req_to_install.conflicts_with = req_to_install.satisfied_by - req_to_install.satisfied_by = None - else: - install = False - if req_to_install.satisfied_by: - if best_installed: - logger.notify('Requirement already up-to-date: %s' - % req_to_install) - else: - logger.notify('Requirement already satisfied ' - '(use --upgrade to upgrade): %s' - % req_to_install) - if req_to_install.editable: - logger.notify('Obtaining %s' % req_to_install) - elif install: - if req_to_install.url and req_to_install.url.lower().startswith('file:'): - logger.notify('Unpacking %s' % display_path(url_to_path(req_to_install.url))) - else: - logger.notify('Downloading/unpacking %s' % req_to_install) - logger.indent += 2 - try: - is_bundle = False - is_wheel = False - if req_to_install.editable: - if req_to_install.source_dir is None: - location = req_to_install.build_location(self.src_dir) - req_to_install.source_dir = location - else: - location = req_to_install.source_dir - if not os.path.exists(self.build_dir): - _make_build_dir(self.build_dir) - req_to_install.update_editable(not self.is_download) - if self.is_download: - req_to_install.run_egg_info() - req_to_install.archive(self.download_dir) - else: - req_to_install.run_egg_info() - elif install: - ##@@ if filesystem packages are not marked - ##editable in a req, a non deterministic error - ##occurs when the script attempts to unpack the - ##build directory - - # NB: This call can result in the creation of a temporary build directory - location = req_to_install.build_location(self.build_dir, not self.is_download) - unpack = True - url = None - - # In the case where the req comes from a bundle, we should - # assume a build dir exists and move on - if req_to_install.from_bundle: - pass - # If a checkout exists, it's unwise to keep going. version - # inconsistencies are logged later, but do not fail the - # installation. - elif os.path.exists(os.path.join(location, 'setup.py')): - raise PreviousBuildDirError(textwrap.dedent(""" - pip can't proceed with requirement '%s' due to a pre-existing build directory. - location: %s - This is likely due to a previous installation that failed. - pip is being responsible and not assuming it can delete this. - Please delete it and try again. - """ % (req_to_install, location))) - else: - ## FIXME: this won't upgrade when there's an existing package unpacked in `location` - if req_to_install.url is None: - if not_found: - raise not_found - url = finder.find_requirement(req_to_install, upgrade=self.upgrade) - else: - ## FIXME: should req_to_install.url already be a link? - url = Link(req_to_install.url) - assert url - if url: - try: - - if ( - url.filename.endswith(wheel_ext) - and self.wheel_download_dir - ): - # when doing 'pip wheel` - download_dir = self.wheel_download_dir - do_download = True - else: - download_dir = self.download_dir - do_download = self.is_download - self.unpack_url( - url, location, download_dir, - do_download, - ) - except HTTPError as exc: - logger.fatal( - 'Could not install requirement %s because ' - 'of error %s' % (req_to_install, exc) - ) - raise InstallationError( - 'Could not install requirement %s because of HTTP error %s for URL %s' - % (req_to_install, e, url)) - else: - unpack = False - if unpack: - is_bundle = req_to_install.is_bundle - is_wheel = url and url.filename.endswith(wheel_ext) - if is_bundle: - req_to_install.move_bundle_files(self.build_dir, self.src_dir) - for subreq in req_to_install.bundle_requirements(): - reqs.append(subreq) - self.add_requirement(subreq) - elif self.is_download: - req_to_install.source_dir = location - if not is_wheel: - # FIXME: see https://github.com/pypa/pip/issues/1112 - req_to_install.run_egg_info() - if url and url.scheme in vcs.all_schemes: - req_to_install.archive(self.download_dir) - elif is_wheel: - req_to_install.source_dir = location - req_to_install.url = url.url - else: - req_to_install.source_dir = location - req_to_install.run_egg_info() - if force_root_egg_info: - # We need to run this to make sure that the .egg-info/ - # directory is created for packing in the bundle - req_to_install.run_egg_info(force_root_egg_info=True) - req_to_install.assert_source_matches_version() - #@@ sketchy way of identifying packages not grabbed from an index - if bundle and req_to_install.url: - self.copy_to_build_dir(req_to_install) - install = False - # req_to_install.req is only avail after unpack for URL pkgs - # repeat check_if_exists to uninstall-on-upgrade (#14) - if not self.ignore_installed: - req_to_install.check_if_exists() - if req_to_install.satisfied_by: - if self.upgrade or self.ignore_installed: - #don't uninstall conflict if user install and and conflict is not user install - if not (self.use_user_site and not dist_in_usersite(req_to_install.satisfied_by)): - req_to_install.conflicts_with = req_to_install.satisfied_by - req_to_install.satisfied_by = None - else: - logger.notify( - 'Requirement already satisfied (use ' - '--upgrade to upgrade): %s' % - req_to_install - ) - install = False - if is_wheel: - dist = list( - pkg_resources.find_distributions(location) - )[0] - if not req_to_install.req: - req_to_install.req = dist.as_requirement() - self.add_requirement(req_to_install) - if not self.ignore_dependencies: - for subreq in dist.requires( - req_to_install.extras): - if self.has_requirement( - subreq.project_name): - continue - subreq = InstallRequirement(str(subreq), - req_to_install) - reqs.append(subreq) - self.add_requirement(subreq) - - # sdists - elif not is_bundle: - ## FIXME: shouldn't be globally added: - finder.add_dependency_links(req_to_install.dependency_links) - if (req_to_install.extras): - logger.notify("Installing extra requirements: %r" % ','.join(req_to_install.extras)) - if not self.ignore_dependencies: - for req in req_to_install.requirements(req_to_install.extras): - try: - name = pkg_resources.Requirement.parse(req).project_name - except ValueError: - e = sys.exc_info()[1] - ## FIXME: proper warning - logger.error('Invalid requirement: %r (%s) in requirement %s' % (req, e, req_to_install)) - continue - if self.has_requirement(name): - ## FIXME: check for conflict - continue - subreq = InstallRequirement(req, req_to_install) - reqs.append(subreq) - self.add_requirement(subreq) - if not self.has_requirement(req_to_install.name): - #'unnamed' requirements will get added here - self.add_requirement(req_to_install) - - # cleanup tmp src - if not is_bundle: - if ( - self.is_download or - req_to_install._temp_build_dir is not None - ): - self.reqs_to_cleanup.append(req_to_install) - - if install: - self.successfully_downloaded.append(req_to_install) - if bundle and (req_to_install.url and req_to_install.url.startswith('file:///')): - self.copy_to_build_dir(req_to_install) - finally: - logger.indent -= 2 - - def cleanup_files(self, bundle=False): - """Clean up files, remove builds.""" - logger.notify('Cleaning up...') - logger.indent += 2 - for req in self.reqs_to_cleanup: - req.remove_temporary_source() - - remove_dir = [] - if self._pip_has_created_build_dir(): - remove_dir.append(self.build_dir) - - # The source dir of a bundle can always be removed. - # FIXME: not if it pre-existed the bundle! - if bundle: - remove_dir.append(self.src_dir) - - for dir in remove_dir: - if os.path.exists(dir): - logger.info('Removing temporary dir %s...' % dir) - rmtree(dir) - - logger.indent -= 2 - - def _pip_has_created_build_dir(self): - return (self.build_dir == build_prefix and - os.path.exists(os.path.join(self.build_dir, PIP_DELETE_MARKER_FILENAME))) - - def copy_to_build_dir(self, req_to_install): - target_dir = req_to_install.editable and self.src_dir or self.build_dir - logger.info("Copying %s to %s" % (req_to_install.name, target_dir)) - dest = os.path.join(target_dir, req_to_install.name) - shutil.copytree(req_to_install.source_dir, dest) - call_subprocess(["python", "%s/setup.py" % dest, "clean"], cwd=dest, - command_desc='python setup.py clean') - - def unpack_url(self, link, location, download_dir=None, - only_download=False): - if download_dir is None: - download_dir = self.download_dir - - # non-editable vcs urls - if is_vcs_url(link): - if only_download: - loc = download_dir - else: - loc = location - unpack_vcs_link(link, loc, only_download) - - # file urls - elif is_file_url(link): - unpack_file_url(link, location, download_dir) - if only_download: - write_delete_marker_file(location) - - # http urls - else: - unpack_http_url( - link, - location, - self.download_cache, - download_dir, - self.session, - ) - if only_download: - write_delete_marker_file(location) - - def install(self, install_options, global_options=(), *args, **kwargs): - """Install everything in this set (after having downloaded and unpacked the packages)""" - to_install = [r for r in self.requirements.values() - if not r.satisfied_by] - - # DISTRIBUTE TO SETUPTOOLS UPGRADE HACK (1 of 3 parts) - # move the distribute-0.7.X wrapper to the end because it does not - # install a setuptools package. by moving it to the end, we ensure it's - # setuptools dependency is handled first, which will provide the - # setuptools package - # TODO: take this out later - distribute_req = pkg_resources.Requirement.parse("distribute>=0.7") - for req in to_install: - if req.name == 'distribute' and req.installed_version in distribute_req: - to_install.remove(req) - to_install.append(req) - - if to_install: - logger.notify('Installing collected packages: %s' % ', '.join([req.name for req in to_install])) - logger.indent += 2 - try: - for requirement in to_install: - - # DISTRIBUTE TO SETUPTOOLS UPGRADE HACK (1 of 3 parts) - # when upgrading from distribute-0.6.X to the new merged - # setuptools in py2, we need to force setuptools to uninstall - # distribute. In py3, which is always using distribute, this - # conversion is already happening in distribute's pkg_resources. - # It's ok *not* to check if setuptools>=0.7 because if someone - # were actually trying to ugrade from distribute to setuptools - # 0.6.X, then all this could do is actually help, although that - # upgade path was certainly never "supported" - # TODO: remove this later - if requirement.name == 'setuptools': - try: - # only uninstall distribute<0.7. For >=0.7, setuptools - # will also be present, and that's what we need to - # uninstall - distribute_requirement = pkg_resources.Requirement.parse("distribute<0.7") - existing_distribute = pkg_resources.get_distribution("distribute") - if existing_distribute in distribute_requirement: - requirement.conflicts_with = existing_distribute - except pkg_resources.DistributionNotFound: - # distribute wasn't installed, so nothing to do - pass - - if requirement.conflicts_with: - logger.notify('Found existing installation: %s' - % requirement.conflicts_with) - logger.indent += 2 - try: - requirement.uninstall(auto_confirm=True) - finally: - logger.indent -= 2 - try: - requirement.install(install_options, global_options, *args, **kwargs) - except: - # if install did not succeed, rollback previous uninstall - if requirement.conflicts_with and not requirement.install_succeeded: - requirement.rollback_uninstall() - raise - else: - if requirement.conflicts_with and requirement.install_succeeded: - requirement.commit_uninstall() - requirement.remove_temporary_source() - finally: - logger.indent -= 2 - self.successfully_installed = to_install - - def create_bundle(self, bundle_filename): - ## FIXME: can't decide which is better; zip is easier to read - ## random files from, but tar.bz2 is smaller and not as lame a - ## format. - - ## FIXME: this file should really include a manifest of the - ## packages, maybe some other metadata files. It would make - ## it easier to detect as well. - zip = zipfile.ZipFile(bundle_filename, 'w', zipfile.ZIP_DEFLATED) - vcs_dirs = [] - for dir, basename in (self.build_dir, 'build'), (self.src_dir, 'src'): - dir = os.path.normcase(os.path.abspath(dir)) - for dirpath, dirnames, filenames in os.walk(dir): - for backend in vcs.backends: - vcs_backend = backend() - vcs_url = vcs_rev = None - if vcs_backend.dirname in dirnames: - for vcs_dir in vcs_dirs: - if dirpath.startswith(vcs_dir): - # vcs bundle file already in parent directory - break - else: - vcs_url, vcs_rev = vcs_backend.get_info( - os.path.join(dir, dirpath)) - vcs_dirs.append(dirpath) - vcs_bundle_file = vcs_backend.bundle_file - vcs_guide = vcs_backend.guide % {'url': vcs_url, - 'rev': vcs_rev} - dirnames.remove(vcs_backend.dirname) - break - if 'pip-egg-info' in dirnames: - dirnames.remove('pip-egg-info') - for dirname in dirnames: - dirname = os.path.join(dirpath, dirname) - name = self._clean_zip_name(dirname, dir) - zip.writestr(basename + '/' + name + '/', '') - for filename in filenames: - if filename == PIP_DELETE_MARKER_FILENAME: - continue - filename = os.path.join(dirpath, filename) - name = self._clean_zip_name(filename, dir) - zip.write(filename, basename + '/' + name) - if vcs_url: - name = os.path.join(dirpath, vcs_bundle_file) - name = self._clean_zip_name(name, dir) - zip.writestr(basename + '/' + name, vcs_guide) - - zip.writestr('pip-manifest.txt', self.bundle_requirements()) - zip.close() - - BUNDLE_HEADER = '''\ -# This is a pip bundle file, that contains many source packages -# that can be installed as a group. You can install this like: -# pip this_file.zip -# The rest of the file contains a list of all the packages included: -''' - - def bundle_requirements(self): - parts = [self.BUNDLE_HEADER] - for req in [req for req in self.requirements.values() - if not req.comes_from]: - parts.append('%s==%s\n' % (req.name, req.installed_version)) - parts.append('# These packages were installed to satisfy the above requirements:\n') - for req in [req for req in self.requirements.values() - if req.comes_from]: - parts.append('%s==%s\n' % (req.name, req.installed_version)) - ## FIXME: should we do something with self.unnamed_requirements? - return ''.join(parts) - - def _clean_zip_name(self, name, prefix): - assert name.startswith(prefix+os.path.sep), ( - "name %r doesn't start with prefix %r" % (name, prefix)) - name = name[len(prefix)+1:] - name = name.replace(os.path.sep, '/') - return name - - -def _make_build_dir(build_dir): - os.makedirs(build_dir) - write_delete_marker_file(build_dir) - - -_scheme_re = re.compile(r'^(http|https|file):', re.I) - - -def parse_requirements(filename, finder=None, comes_from=None, options=None, - session=None): - if session is None: - session = PipSession() - - skip_match = None - skip_regex = options.skip_requirements_regex if options else None - if skip_regex: - skip_match = re.compile(skip_regex) - reqs_file_dir = os.path.dirname(os.path.abspath(filename)) - filename, content = get_file_content(filename, - comes_from=comes_from, - session=session, - ) - for line_number, line in enumerate(content.splitlines()): - line_number += 1 - line = line.strip() - - # Remove comments from file - line = re.sub(r"(^|\s)#.*$", "", line) - - if not line or line.startswith('#'): - continue - if skip_match and skip_match.search(line): - continue - if line.startswith('-r') or line.startswith('--requirement'): - if line.startswith('-r'): - req_url = line[2:].strip() - else: - req_url = line[len('--requirement'):].strip().strip('=') - if _scheme_re.search(filename): - # Relative to a URL - req_url = urlparse.urljoin(filename, req_url) - elif not _scheme_re.search(req_url): - req_url = os.path.join(os.path.dirname(filename), req_url) - for item in parse_requirements(req_url, finder, comes_from=filename, options=options, session=session): - yield item - elif line.startswith('-Z') or line.startswith('--always-unzip'): - # No longer used, but previously these were used in - # requirement files, so we'll ignore. - pass - elif line.startswith('-f') or line.startswith('--find-links'): - if line.startswith('-f'): - line = line[2:].strip() - else: - line = line[len('--find-links'):].strip().lstrip('=') - ## FIXME: it would be nice to keep track of the source of - ## the find_links: - # support a find-links local path relative to a requirements file - relative_to_reqs_file = os.path.join(reqs_file_dir, line) - if os.path.exists(relative_to_reqs_file): - line = relative_to_reqs_file - if finder: - finder.find_links.append(line) - elif line.startswith('-i') or line.startswith('--index-url'): - if line.startswith('-i'): - line = line[2:].strip() - else: - line = line[len('--index-url'):].strip().lstrip('=') - if finder: - finder.index_urls = [line] - elif line.startswith('--extra-index-url'): - line = line[len('--extra-index-url'):].strip().lstrip('=') - if finder: - finder.index_urls.append(line) - elif line.startswith('--use-wheel'): - finder.use_wheel = True - elif line.startswith('--no-index'): - finder.index_urls = [] - elif line.startswith("--allow-external"): - line = line[len("--allow-external"):].strip().lstrip("=") - finder.allow_external |= set([normalize_name(line).lower()]) - elif line.startswith("--allow-all-external"): - finder.allow_all_external = True - # Remove in 1.7 - elif line.startswith("--no-allow-external"): - pass - # Remove in 1.7 - elif line.startswith("--no-allow-insecure"): - pass - # Remove after 1.7 - elif line.startswith("--allow-insecure"): - line = line[len("--allow-insecure"):].strip().lstrip("=") - finder.allow_unverified |= set([normalize_name(line).lower()]) - elif line.startswith("--allow-unverified"): - line = line[len("--allow-unverified"):].strip().lstrip("=") - finder.allow_unverified |= set([normalize_name(line).lower()]) - else: - comes_from = '-r %s (line %s)' % (filename, line_number) - if line.startswith('-e') or line.startswith('--editable'): - if line.startswith('-e'): - line = line[2:].strip() - else: - line = line[len('--editable'):].strip().lstrip('=') - req = InstallRequirement.from_editable( - line, comes_from=comes_from, default_vcs=options.default_vcs if options else None) - else: - req = InstallRequirement.from_line(line, comes_from, prereleases=getattr(options, "pre", None)) - yield req - -def _strip_postfix(req): - """ - Strip req postfix ( -dev, 0.2, etc ) - """ - ## FIXME: use package_to_requirement? - match = re.search(r'^(.*?)(?:-dev|-\d.*)$', req) - if match: - # Strip off -dev, -0.2, etc. - req = match.group(1) - return req - -def _build_req_from_url(url): - - parts = [p for p in url.split('#', 1)[0].split('/') if p] - - req = None - if parts[-2] in ('tags', 'branches', 'tag', 'branch'): - req = parts[-3] - elif parts[-1] == 'trunk': - req = parts[-2] - return req - -def _build_editable_options(req): - - """ - This method generates a dictionary of the query string - parameters contained in a given editable URL. - """ - regexp = re.compile(r"[\?#&](?P[^&=]+)=(?P[^&=]+)") - matched = regexp.findall(req) - - if matched: - ret = dict() - for option in matched: - (name, value) = option - if name in ret: - raise Exception("%s option already defined" % name) - ret[name] = value - return ret - return None - - -def parse_editable(editable_req, default_vcs=None): - """Parses svn+http://blahblah@rev#egg=Foobar into a requirement - (Foobar) and a URL""" - - url = editable_req - extras = None - - # If a file path is specified with extras, strip off the extras. - m = re.match(r'^(.+)(\[[^\]]+\])$', url) - if m: - url_no_extras = m.group(1) - extras = m.group(2) - else: - url_no_extras = url - - if os.path.isdir(url_no_extras): - if not os.path.exists(os.path.join(url_no_extras, 'setup.py')): - raise InstallationError("Directory %r is not installable. File 'setup.py' not found." % url_no_extras) - # Treating it as code that has already been checked out - url_no_extras = path_to_url(url_no_extras) - - if url_no_extras.lower().startswith('file:'): - if extras: - return None, url_no_extras, pkg_resources.Requirement.parse('__placeholder__' + extras).extras - else: - return None, url_no_extras, None - - for version_control in vcs: - if url.lower().startswith('%s:' % version_control): - url = '%s+%s' % (version_control, url) - break - - if '+' not in url: - if default_vcs: - url = default_vcs + '+' + url - else: - raise InstallationError( - '%s should either be a path to a local project or a VCS url beginning with svn+, git+, hg+, or bzr+' % editable_req) - - vc_type = url.split('+', 1)[0].lower() - - if not vcs.get_backend(vc_type): - error_message = 'For --editable=%s only ' % editable_req + \ - ', '.join([backend.name + '+URL' for backend in vcs.backends]) + \ - ' is currently supported' - raise InstallationError(error_message) - - try: - options = _build_editable_options(editable_req) - except Exception: - message = sys.exc_info()[1] - raise InstallationError( - '--editable=%s error in editable options:%s' % (editable_req, message)) - - if not options or 'egg' not in options: - req = _build_req_from_url(editable_req) - if not req: - raise InstallationError('--editable=%s is not the right format; it must have #egg=Package' % editable_req) - else: - req = options['egg'] - - package = _strip_postfix(req) - return package, url, options - - -class UninstallPathSet(object): - """A set of file paths to be removed in the uninstallation of a - requirement.""" - def __init__(self, dist): - self.paths = set() - self._refuse = set() - self.pth = {} - self.dist = dist - self.save_dir = None - self._moved_paths = [] - - def _permitted(self, path): - """ - Return True if the given path is one we are permitted to - remove/modify, False otherwise. - - """ - return is_local(path) - - def _can_uninstall(self): - if not dist_is_local(self.dist): - logger.notify("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): - path = normalize_path(path) - if not os.path.exists(path): - return - if self._permitted(path): - self.paths.add(path) - else: - self._refuse.add(path) - - # __pycache__ files can show up after 'installed-files.txt' is created, due to imports - if os.path.splitext(path)[1] == '.py' and uses_pycache: - self.add(imp.cache_from_source(path)) - - - def add_pth(self, pth_file, entry): - pth_file = normalize_path(pth_file) - if self._permitted(pth_file): - if pth_file not in self.pth: - self.pth[pth_file] = UninstallPthEntries(pth_file) - self.pth[pth_file].add(entry) - else: - self._refuse.add(pth_file) - - def compact(self, paths): - """Compact a path set to contain the minimal number of paths - necessary to contain all paths in the set. If /a/path/ and - /a/path/to/a/file.txt are both in the set, leave only the - shorter path.""" - short_paths = set() - for path in sorted(paths, key=len): - if not any([(path.startswith(shortpath) and - path[len(shortpath.rstrip(os.path.sep))] == os.path.sep) - for shortpath in short_paths]): - short_paths.add(path) - return short_paths - - def _stash(self, path): - return os.path.join( - self.save_dir, os.path.splitdrive(path)[1].lstrip(os.path.sep)) - - 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.notify("Can't uninstall '%s'. No files were found to uninstall." % self.dist.project_name) - return - logger.notify('Uninstalling %s:' % self.dist.project_name) - logger.indent += 2 - paths = sorted(self.compact(self.paths)) - try: - if auto_confirm: - response = 'y' - else: - for path in paths: - logger.notify(path) - response = ask('Proceed (y/n)? ', ('y', 'n')) - if self._refuse: - logger.notify('Not removing or modifying (outside of prefix):') - for path in self.compact(self._refuse): - logger.notify(path) - if response == 'y': - self.save_dir = tempfile.mkdtemp(suffix='-uninstall', - prefix='pip-') - for path in paths: - new_path = self._stash(path) - logger.info('Removing file or directory %s' % path) - self._moved_paths.append(path) - renames(path, new_path) - for pth in self.pth.values(): - pth.remove() - logger.notify('Successfully uninstalled %s' % self.dist.project_name) - - finally: - logger.indent -= 2 - - def rollback(self): - """Rollback the changes previously made by remove().""" - if self.save_dir is None: - logger.error("Can't roll back %s; was not uninstalled" % self.dist.project_name) - return False - logger.notify('Rolling back uninstall of %s' % self.dist.project_name) - for path in self._moved_paths: - tmp_path = self._stash(path) - logger.info('Replacing %s' % path) - renames(tmp_path, path) - for pth in self.pth: - pth.rollback() - - def commit(self): - """Remove temporary save dir: rollback will no longer be possible.""" - if self.save_dir is not None: - rmtree(self.save_dir) - self.save_dir = None - self._moved_paths = [] - - -class UninstallPthEntries(object): - def __init__(self, pth_file): - if not os.path.isfile(pth_file): - raise UninstallationError("Cannot remove entries from nonexistent file %s" % pth_file) - self.file = pth_file - self.entries = set() - self._saved_lines = None - - def add(self, entry): - entry = os.path.normcase(entry) - # On Windows, os.path.normcase converts the entry to use - # backslashes. This is correct for entries that describe absolute - # paths outside of site-packages, but all the others use forward - # slashes. - if sys.platform == 'win32' and not os.path.splitdrive(entry)[0]: - entry = entry.replace('\\', '/') - self.entries.add(entry) - - def remove(self): - logger.info('Removing pth entries from %s:' % self.file) - fh = open(self.file, 'rb') - # windows uses '\r\n' with py3k, but uses '\n' with py2.x - lines = fh.readlines() - self._saved_lines = lines - fh.close() - if any(b('\r\n') in line for line in lines): - endline = '\r\n' - else: - endline = '\n' - for entry in self.entries: - try: - logger.info('Removing entry: %s' % entry) - lines.remove(b(entry + endline)) - except ValueError: - pass - fh = open(self.file, 'wb') - fh.writelines(lines) - fh.close() - - def rollback(self): - if self._saved_lines is None: - logger.error('Cannot roll back changes to %s, none were made' % self.file) - return False - logger.info('Rolling %s back to previous state' % self.file) - fh = open(self.file, 'wb') - fh.writelines(self._saved_lines) - fh.close() - return True - - -class FakeFile(object): - """Wrap a list of lines in an object with readline() to make - ConfigParser happy.""" - def __init__(self, lines): - self._gen = (l for l in lines) - - def readline(self): - try: - try: - return next(self._gen) - except NameError: - return self._gen.next() - except StopIteration: - return '' - - def __iter__(self): - return self._gen diff --git a/pip/req/__init__.py b/pip/req/__init__.py new file mode 100644 index 00000000000..00185a43083 --- /dev/null +++ b/pip/req/__init__.py @@ -0,0 +1,10 @@ +from __future__ import absolute_import + +from .req_install import InstallRequirement +from .req_set import RequirementSet, Requirements +from .req_file import parse_requirements + +__all__ = [ + "RequirementSet", "Requirements", "InstallRequirement", + "parse_requirements", +] diff --git a/pip/req/req_file.py b/pip/req/req_file.py new file mode 100644 index 00000000000..821df2271db --- /dev/null +++ b/pip/req/req_file.py @@ -0,0 +1,342 @@ +""" +Requirements file parsing +""" + +from __future__ import absolute_import + +import os +import re +import shlex +import sys +import optparse +import warnings + +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves import filterfalse + +import pip +from pip.download import get_file_content +from pip.req.req_install import InstallRequirement +from pip.exceptions import (RequirementsFileParseError) +from pip.utils.deprecation import RemovedInPip10Warning +from pip import cmdoptions + +__all__ = ['parse_requirements'] + +SCHEME_RE = re.compile(r'^(http|https|file):', re.I) +COMMENT_RE = re.compile(r'(^|\s)+#.*$') + +SUPPORTED_OPTIONS = [ + cmdoptions.constraints, + cmdoptions.editable, + cmdoptions.requirements, + cmdoptions.no_index, + cmdoptions.index_url, + cmdoptions.find_links, + cmdoptions.extra_index_url, + cmdoptions.allow_external, + cmdoptions.allow_all_external, + cmdoptions.no_allow_external, + cmdoptions.allow_unsafe, + cmdoptions.no_allow_unsafe, + cmdoptions.use_wheel, + cmdoptions.no_use_wheel, + cmdoptions.always_unzip, + cmdoptions.no_binary, + cmdoptions.only_binary, + cmdoptions.pre, + cmdoptions.process_dependency_links, + cmdoptions.trusted_host, + cmdoptions.require_hashes, +] + +# options to be passed to requirements +SUPPORTED_OPTIONS_REQ = [ + cmdoptions.install_options, + cmdoptions.global_options, + cmdoptions.hash, +] + +# the 'dest' string values +SUPPORTED_OPTIONS_REQ_DEST = [o().dest for o in SUPPORTED_OPTIONS_REQ] + + +def parse_requirements(filename, finder=None, comes_from=None, options=None, + session=None, constraint=False, wheel_cache=None): + """Parse a requirements file and yield InstallRequirement instances. + + :param filename: Path or url of requirements file. + :param finder: Instance of pip.index.PackageFinder. + :param comes_from: Origin description of requirements. + :param options: cli options. + :param session: Instance of pip.download.PipSession. + :param constraint: If true, parsing a constraint file rather than + requirements file. + :param wheel_cache: Instance of pip.wheel.WheelCache + """ + if session is None: + raise TypeError( + "parse_requirements() missing 1 required keyword argument: " + "'session'" + ) + + _, content = get_file_content( + filename, comes_from=comes_from, session=session + ) + + lines_enum = preprocess(content, options) + + for line_number, line in lines_enum: + req_iter = process_line(line, filename, line_number, finder, + comes_from, options, session, wheel_cache, + constraint=constraint) + for req in req_iter: + yield req + + +def preprocess(content, options): + """Split, filter, and join lines, and return a line iterator + + :param content: the content of the requirements file + :param options: cli options + """ + lines_enum = enumerate(content.splitlines(), start=1) + lines_enum = join_lines(lines_enum) + lines_enum = ignore_comments(lines_enum) + lines_enum = skip_regex(lines_enum, options) + return lines_enum + + +def process_line(line, filename, line_number, finder=None, comes_from=None, + options=None, session=None, wheel_cache=None, + constraint=False): + """Process a single requirements line; This can result in creating/yielding + requirements, or updating the finder. + + For lines that contain requirements, the only options that have an effect + are from SUPPORTED_OPTIONS_REQ, and they are scoped to the + requirement. Other options from SUPPORTED_OPTIONS may be present, but are + ignored. + + For lines that do not contain requirements, the only options that have an + effect are from SUPPORTED_OPTIONS. Options from SUPPORTED_OPTIONS_REQ may + be present, but are ignored. These lines may contain multiple options + (although our docs imply only one is supported), and all our parsed and + affect the finder. + + :param constraint: If True, parsing a constraints file. + :param options: OptionParser options that we may update + """ + parser = build_parser() + defaults = parser.get_default_values() + defaults.index_url = None + if finder: + # `finder.format_control` will be updated during parsing + defaults.format_control = finder.format_control + args_str, options_str = break_args_options(line) + if sys.version_info < (2, 7, 3): + # Prior to 2.7.3, shlex cannot deal with unicode entries + options_str = options_str.encode('utf8') + opts, _ = parser.parse_args(shlex.split(options_str), defaults) + + # preserve for the nested code path + line_comes_from = '%s %s (line %s)' % ( + '-c' if constraint else '-r', filename, line_number) + + # yield a line requirement + if args_str: + isolated = options.isolated_mode if options else False + if options: + cmdoptions.check_install_build_global(options, opts) + # get the options that apply to requirements + req_options = {} + for dest in SUPPORTED_OPTIONS_REQ_DEST: + if dest in opts.__dict__ and opts.__dict__[dest]: + req_options[dest] = opts.__dict__[dest] + yield InstallRequirement.from_line( + args_str, line_comes_from, constraint=constraint, + isolated=isolated, options=req_options, wheel_cache=wheel_cache + ) + + # yield an editable requirement + elif opts.editables: + isolated = options.isolated_mode if options else False + default_vcs = options.default_vcs if options else None + yield InstallRequirement.from_editable( + opts.editables[0], comes_from=line_comes_from, + constraint=constraint, default_vcs=default_vcs, isolated=isolated, + wheel_cache=wheel_cache + ) + + # parse a nested requirements file + elif opts.requirements or opts.constraints: + if opts.requirements: + req_path = opts.requirements[0] + nested_constraint = False + else: + req_path = opts.constraints[0] + nested_constraint = True + # original file is over http + if SCHEME_RE.search(filename): + # do a url join so relative paths work + req_path = urllib_parse.urljoin(filename, req_path) + # original file and nested file are paths + elif not SCHEME_RE.search(req_path): + # do a join so relative paths work + req_path = os.path.join(os.path.dirname(filename), req_path) + # TODO: Why not use `comes_from='-r {} (line {})'` here as well? + parser = parse_requirements( + req_path, finder, comes_from, options, session, + constraint=nested_constraint, wheel_cache=wheel_cache + ) + for req in parser: + yield req + + # percolate hash-checking option upward + elif opts.require_hashes: + options.require_hashes = opts.require_hashes + + # set finder options + elif finder: + if opts.allow_external: + warnings.warn( + "--allow-external has been deprecated and will be removed in " + "the future. Due to changes in the repository protocol, it no " + "longer has any effect.", + RemovedInPip10Warning, + ) + + if opts.allow_all_external: + warnings.warn( + "--allow-all-external has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + if opts.allow_unverified: + warnings.warn( + "--allow-unverified has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + if opts.index_url: + finder.index_urls = [opts.index_url] + if opts.use_wheel is False: + finder.use_wheel = False + pip.index.fmt_ctl_no_use_wheel(finder.format_control) + if opts.no_index is True: + finder.index_urls = [] + if opts.extra_index_urls: + finder.index_urls.extend(opts.extra_index_urls) + if opts.find_links: + # FIXME: it would be nice to keep track of the source + # of the find_links: support a find-links local path + # relative to a requirements file. + value = opts.find_links[0] + req_dir = os.path.dirname(os.path.abspath(filename)) + relative_to_reqs_file = os.path.join(req_dir, value) + if os.path.exists(relative_to_reqs_file): + value = relative_to_reqs_file + finder.find_links.append(value) + if opts.pre: + finder.allow_all_prereleases = True + if opts.process_dependency_links: + finder.process_dependency_links = True + if opts.trusted_hosts: + finder.secure_origins.extend( + ("*", host, "*") for host in opts.trusted_hosts) + + +def break_args_options(line): + """Break up the line into an args and options string. We only want to shlex + (and then optparse) the options, not the args. args can contain markers + which are corrupted by shlex. + """ + tokens = line.split(' ') + args = [] + options = tokens[:] + for token in tokens: + if token.startswith('-') or token.startswith('--'): + break + else: + args.append(token) + options.pop(0) + return ' '.join(args), ' '.join(options) + + +def build_parser(): + """ + Return a parser for parsing requirement lines + """ + parser = optparse.OptionParser(add_help_option=False) + + option_factories = SUPPORTED_OPTIONS + SUPPORTED_OPTIONS_REQ + for option_factory in option_factories: + option = option_factory() + parser.add_option(option) + + # By default optparse sys.exits on parsing errors. We want to wrap + # that in our own exception. + def parser_exit(self, msg): + raise RequirementsFileParseError(msg) + parser.exit = parser_exit + + return parser + + +def join_lines(lines_enum): + """Joins a line ending in '\' with the previous line (except when following + comments). The joined line takes on the index of the first line. + """ + primary_line_number = None + new_line = [] + for line_number, line in lines_enum: + if not line.endswith('\\') or COMMENT_RE.match(line): + if COMMENT_RE.match(line): + # this ensures comments are always matched later + line = ' ' + line + if new_line: + new_line.append(line) + yield primary_line_number, ''.join(new_line) + new_line = [] + else: + yield line_number, line + else: + if not new_line: + primary_line_number = line_number + new_line.append(line.strip('\\')) + + # last line contains \ + if new_line: + yield primary_line_number, ''.join(new_line) + + # TODO: handle space after '\'. + + +def ignore_comments(lines_enum): + """ + Strips comments and filter empty lines. + """ + for line_number, line in lines_enum: + line = COMMENT_RE.sub('', line) + line = line.strip() + if line: + yield line_number, line + + +def skip_regex(lines_enum, options): + """ + Skip lines that match '--skip-requirements-regex' pattern + + Note: the regex pattern is only built once + """ + skip_regex = options.skip_requirements_regex if options else None + if skip_regex: + pattern = re.compile(skip_regex) + lines_enum = filterfalse( + lambda e: pattern.search(e[1]), + lines_enum) + return lines_enum diff --git a/pip/req/req_install.py b/pip/req/req_install.py new file mode 100644 index 00000000000..e99e8757dae --- /dev/null +++ b/pip/req/req_install.py @@ -0,0 +1,1186 @@ +from __future__ import absolute_import + +import logging +import os +import re +import shutil +import sys +import tempfile +import traceback +import warnings +import zipfile + +from distutils import sysconfig +from distutils.util import change_root +from email.parser import FeedParser + +from pip._vendor import pkg_resources, six +from pip._vendor.packaging import specifiers +from pip._vendor.packaging.markers import Marker +from pip._vendor.packaging.requirements import InvalidRequirement, Requirement +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import Version, parse as parse_version +from pip._vendor.six.moves import configparser + +import pip.wheel + +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, +) +from pip.locations import ( + bin_py, running_under_virtualenv, PIP_DELETE_MARKER_FILENAME, bin_user, +) +from pip.utils import ( + 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, normalize_path, dist_is_local, +) + +from pip.utils.hashes import Hashes +from pip.utils.deprecation import RemovedInPip9Warning, RemovedInPip10Warning +from pip.utils.logging import indent_log +from pip.utils.setuptools_build import SETUPTOOLS_SHIM +from pip.utils.ui import open_spinner +from pip.req.req_uninstall import UninstallPathSet +from pip.vcs import vcs +from pip.wheel import move_wheel_files, Wheel + + +logger = logging.getLogger(__name__) + +operators = specifiers.Specifier._operators.keys() + + +def _strip_extras(path): + m = re.match(r'^(.+)(\[[^\]]+\])$', path) + extras = None + if m: + path_no_extras = m.group(1) + extras = m.group(2) + else: + path_no_extras = path + + return path_no_extras, extras + + +class InstallRequirement(object): + + def __init__(self, req, comes_from, source_dir=None, editable=False, + link=None, as_egg=False, update=True, + pycompile=True, markers=None, isolated=False, options=None, + wheel_cache=None, constraint=False): + self.extras = () + if isinstance(req, six.string_types): + try: + req = Requirement(req) + except InvalidRequirement: + if os.path.sep in req: + add_msg = "It looks like a path. Does it exist ?" + elif '=' in req and not any(op in req for op in operators): + add_msg = "= is not a valid operator. Did you mean == ?" + else: + add_msg = traceback.format_exc() + raise InstallationError( + "Invalid requirement: '%s'\n%s" % (req, add_msg)) + self.extras = req.extras + + self.req = req + self.comes_from = comes_from + self.constraint = constraint + self.source_dir = source_dir + self.editable = editable + + self._wheel_cache = wheel_cache + self.link = self.original_link = link + self.as_egg = as_egg + self.markers = markers + self._egg_info_path = None + # This holds the pkg_resources.Distribution object if this requirement + # is already available: + self.satisfied_by = None + # This hold the pkg_resources.Distribution object if this requirement + # conflicts with another installed distribution: + self.conflicts_with = None + # Temporary build location + self._temp_build_dir = None + # Used to store the global directory where the _temp_build_dir should + # have been created. Cf _correct_build_location method. + self._ideal_build_dir = None + # True if the editable should be updated: + self.update = update + # Set to True after successful installation + 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 {} + self.pycompile = pycompile + # Set to True after successful preparation of this requirement + self.prepared = False + + self.isolated = isolated + + @classmethod + def from_editable(cls, editable_req, comes_from=None, default_vcs=None, + isolated=False, options=None, wheel_cache=None, + constraint=False): + from pip.index import Link + + name, url, extras_override = parse_editable( + editable_req, default_vcs) + if url.startswith('file:'): + source_dir = url_to_path(url) + else: + source_dir = None + + res = cls(name, comes_from, source_dir=source_dir, + editable=True, + link=Link(url), + constraint=constraint, + isolated=isolated, + options=options if options else {}, + wheel_cache=wheel_cache) + + if extras_override is not None: + res.extras = extras_override + + return res + + @classmethod + def from_line( + cls, name, comes_from=None, isolated=False, options=None, + wheel_cache=None, constraint=False): + """Creates an InstallRequirement from a name, which might be a + requirement, directory containing 'setup.py', filename, or URL. + """ + from pip.index import Link + + if is_url(name): + marker_sep = '; ' + else: + marker_sep = ';' + if marker_sep in name: + name, markers = name.split(marker_sep, 1) + markers = markers.strip() + if not markers: + markers = None + else: + markers = None + name = name.strip() + req = None + path = os.path.normpath(os.path.abspath(name)) + link = None + extras = None + + if is_url(name): + link = Link(name) + else: + p, extras = _strip_extras(path) + if (os.path.isdir(p) and + (os.path.sep in name or name.startswith('.'))): + + if not is_installable_dir(p): + raise InstallationError( + "Directory %r is not installable. File 'setup.py' " + "not found." % name + ) + link = Link(path_to_url(p)) + elif is_archive_file(p): + if not os.path.isfile(p): + logger.warning( + 'Requirement %r looks like a filename, but the ' + 'file does not exist', + name + ) + link = Link(path_to_url(p)) + + # it's a local file, dir, or url + if link: + # Handle relative file URLs + if link.scheme == 'file' and re.search(r'\.\./', link.url): + link = Link( + path_to_url(os.path.normpath(os.path.abspath(link.path)))) + # wheel file + if link.is_wheel: + wheel = Wheel(link.filename) # can raise InvalidWheelFilename + req = "%s==%s" % (wheel.name, wheel.version) + else: + # set the req to the egg fragment. when it's not there, this + # will become an 'unnamed' requirement + req = link.egg_fragment + + # a requirement specifier + else: + req = name + + options = options if options else {} + res = cls(req, comes_from, link=link, markers=markers, + isolated=isolated, options=options, + wheel_cache=wheel_cache, constraint=constraint) + + if extras: + res.extras = Requirement('placeholder' + extras).extras + + return res + + def __str__(self): + if self.req: + s = str(self.req) + if self.link: + s += ' from %s' % self.link.url + else: + s = self.link.url if self.link else None + if self.satisfied_by is not None: + s += ' in %s' % display_path(self.satisfied_by.location) + if self.comes_from: + if isinstance(self.comes_from, six.string_types): + comes_from = self.comes_from + else: + comes_from = self.comes_from.from_path() + if comes_from: + s += ' (from %s)' % comes_from + return s + + def __repr__(self): + return '<%s object: %s editable=%r>' % ( + self.__class__.__name__, str(self), self.editable) + + def populate_link(self, finder, upgrade, require_hashes): + """Ensure that if a link can be found for this, that it is found. + + Note that self.link may still be None - if Upgrade is False and the + requirement is already installed. + + If require_hashes is True, don't use the wheel cache, because cached + wheels, always built locally, have different hashes than the files + downloaded from the index server and thus throw false hash mismatches. + Furthermore, cached wheels at present have undeterministic contents due + to file modification times. + """ + if self.link is None: + self.link = finder.find_requirement(self, upgrade) + if self._wheel_cache is not None and not require_hashes: + old_link = self.link + self.link = self._wheel_cache.cached_wheel(self.link, self.name) + if old_link != self.link: + logger.debug('Using cached wheel link: %s', self.link) + + @property + def specifier(self): + return self.req.specifier + + @property + def is_pinned(self): + """Return whether I am pinned to an exact version. + + For example, some-package==1.2 is pinned; some-package>1.2 is not. + """ + specifiers = self.specifier + return (len(specifiers) == 1 and + next(iter(specifiers)).operator in ('==', '===')) + + def from_path(self): + if self.req is None: + return None + s = str(self.req) + if self.comes_from: + if isinstance(self.comes_from, six.string_types): + comes_from = self.comes_from + else: + comes_from = self.comes_from.from_path() + if comes_from: + s += '->' + comes_from + return s + + def build_location(self, build_dir): + if self._temp_build_dir is not None: + return self._temp_build_dir + if self.req is None: + # for requirement via a path to a directory: the name of the + # package is not available yet so we create a temp directory + # Once run_egg_info will have run, we'll be able + # to fix it via _correct_build_location + # Some systems have /tmp as a symlink which confuses custom + # builds (such as numpy). Thus, we ensure that the real path + # is returned. + self._temp_build_dir = os.path.realpath( + tempfile.mkdtemp('-build', 'pip-') + ) + self._ideal_build_dir = build_dir + return self._temp_build_dir + if self.editable: + name = self.name.lower() + else: + name = self.name + # FIXME: Is there a better place to create the build_dir? (hg and bzr + # need this) + if not os.path.exists(build_dir): + logger.debug('Creating directory %s', build_dir) + _make_build_dir(build_dir) + return os.path.join(build_dir, name) + + def _correct_build_location(self): + """Move self._temp_build_dir to self._ideal_build_dir/self.req.name + + For some requirements (e.g. a path to a directory), the name of the + package is not available until we run egg_info, so the build_location + will return a temporary directory and store the _ideal_build_dir. + + This is only called by self.egg_info_path to fix the temporary build + directory. + """ + if self.source_dir is not None: + return + assert self.req is not None + assert self._temp_build_dir + assert self._ideal_build_dir + old_location = self._temp_build_dir + self._temp_build_dir = None + new_location = self.build_location(self._ideal_build_dir) + if os.path.exists(new_location): + raise InstallationError( + 'A package already exists in %s; please remove it to continue' + % display_path(new_location)) + logger.debug( + 'Moving package %s from %s to new location %s', + self, display_path(old_location), display_path(new_location), + ) + shutil.move(old_location, new_location) + self._temp_build_dir = new_location + self._ideal_build_dir = None + self.source_dir = new_location + self._egg_info_path = None + + @property + def name(self): + if self.req is None: + return None + return native_str(pkg_resources.safe_name(self.req.name)) + + @property + def setup_py_dir(self): + return os.path.join( + self.source_dir, + self.link and self.link.subdirectory_fragment or '') + + @property + def setup_py(self): + assert self.source_dir, "No source dir for %s" % self + try: + import setuptools # noqa + except ImportError: + if get_installed_version('setuptools') is None: + add_msg = "Please install setuptools." + else: + add_msg = traceback.format_exc() + # Setuptools is not available + raise InstallationError( + "Could not import setuptools which is required to " + "install from a source distribution.\n%s" % add_msg + ) + + setup_py = os.path.join(self.setup_py_dir, 'setup.py') + + # Python2 __file__ should not be unicode + if six.PY2 and isinstance(setup_py, six.text_type): + setup_py = setup_py.encode(sys.getfilesystemencoding()) + + return setup_py + + def run_egg_info(self): + assert self.source_dir + if self.name: + logger.debug( + 'Running setup.py (path:%s) egg_info for package %s', + self.setup_py, self.name, + ) + else: + logger.debug( + 'Running setup.py (path:%s) egg_info for package from %s', + self.setup_py, self.link, + ) + + with indent_log(): + script = SETUPTOOLS_SHIM % self.setup_py + base_cmd = [sys.executable, '-c', script] + if self.isolated: + base_cmd += ["--no-user-cfg"] + egg_info_cmd = base_cmd + ['egg_info'] + # We can't put the .egg-info files at the root, because then the + # source code will be mistaken for an installed egg, causing + # problems + if self.editable: + egg_base_option = [] + else: + egg_info_dir = os.path.join(self.setup_py_dir, 'pip-egg-info') + ensure_dir(egg_info_dir) + egg_base_option = ['--egg-base', 'pip-egg-info'] + call_subprocess( + egg_info_cmd + egg_base_option, + cwd=self.setup_py_dir, + show_stdout=False, + command_level=logging.DEBUG, + command_desc='python setup.py egg_info') + + if not self.req: + if isinstance(parse_version(self.pkg_info()["Version"]), Version): + op = "==" + else: + op = "===" + self.req = Requirement( + "".join([ + self.pkg_info()["Name"], + op, + self.pkg_info()["Version"], + ]) + ) + self._correct_build_location() + else: + metadata_name = canonicalize_name(self.pkg_info()["Name"]) + if canonicalize_name(self.req.name) != metadata_name: + logger.warning( + 'Running setup.py (path:%s) egg_info for package %s ' + 'produced metadata for project name %s. Fix your ' + '#egg=%s fragments.', + self.setup_py, self.name, metadata_name, self.name + ) + self.req = Requirement(metadata_name) + + def egg_info_data(self, filename): + if self.satisfied_by is not None: + if not self.satisfied_by.has_metadata(filename): + return None + return self.satisfied_by.get_metadata(filename) + assert self.source_dir + filename = self.egg_info_path(filename) + if not os.path.exists(filename): + return None + data = read_text_file(filename) + return data + + def egg_info_path(self, filename): + if self._egg_info_path is None: + if self.editable: + base = self.source_dir + else: + base = os.path.join(self.setup_py_dir, 'pip-egg-info') + filenames = os.listdir(base) + if self.editable: + filenames = [] + for root, dirs, files in os.walk(base): + for dir in vcs.dirnames: + if dir in dirs: + dirs.remove(dir) + # Iterate over a copy of ``dirs``, since mutating + # a list while iterating over it can cause trouble. + # (See https://github.com/pypa/pip/pull/462.) + for dir in list(dirs): + # Don't search in anything that looks like a virtualenv + # environment + if ( + os.path.exists( + os.path.join(root, dir, 'bin', 'python') + ) or + os.path.exists( + os.path.join( + root, dir, 'Scripts', 'Python.exe' + ) + )): + dirs.remove(dir) + # Also don't search through tests + elif dir == 'test' or dir == 'tests': + dirs.remove(dir) + filenames.extend([os.path.join(root, dir) + for dir in dirs]) + filenames = [f for f in filenames if f.endswith('.egg-info')] + + if not filenames: + raise InstallationError( + 'No files/directories in %s (from %s)' % (base, filename) + ) + assert filenames, \ + "No files/directories in %s (from %s)" % (base, filename) + + # if we have more than one match, we pick the toplevel one. This + # can easily be the case if there is a dist folder which contains + # an extracted tarball for testing purposes. + if len(filenames) > 1: + filenames.sort( + key=lambda x: x.count(os.path.sep) + + (os.path.altsep and x.count(os.path.altsep) or 0) + ) + self._egg_info_path = os.path.join(base, filenames[0]) + return os.path.join(self._egg_info_path, filename) + + def pkg_info(self): + p = FeedParser() + data = self.egg_info_data('PKG-INFO') + if not data: + logger.warning( + 'No PKG-INFO file found in %s', + display_path(self.egg_info_path('PKG-INFO')), + ) + p.feed(data or '') + return p.close() + + _requirements_section_re = re.compile(r'\[(.*?)\]') + + @property + def installed_version(self): + return get_installed_version(self.name) + + def assert_source_matches_version(self): + assert self.source_dir + version = self.pkg_info()['version'] + if self.req.specifier and version not in self.req.specifier: + logger.warning( + 'Requested %s, but installing version %s', + self, + self.installed_version, + ) + else: + logger.debug( + 'Source in %s has version %s, which satisfies requirement %s', + display_path(self.source_dir), + version, + self, + ) + + def update_editable(self, obtain=True): + if not self.link: + logger.debug( + "Cannot update repository at %s; repository location is " + "unknown", + self.source_dir, + ) + return + assert self.editable + assert self.source_dir + if self.link.scheme == 'file': + # Static paths don't get updated + return + assert '+' in self.link.url, "bad url: %r" % self.link.url + if not self.update: + return + vc_type, url = self.link.url.split('+', 1) + backend = vcs.get_backend(vc_type) + if backend: + vcs_backend = backend(self.link.url) + if obtain: + vcs_backend.obtain(self.source_dir) + else: + vcs_backend.export(self.source_dir) + else: + assert 0, ( + 'Unexpected version control type (in %s): %s' + % (self.link, vc_type)) + + def uninstall(self, auto_confirm=False): + """ + Uninstall the distribution currently satisfying this requirement. + + Prompts before removing or modifying files unless + ``auto_confirm`` is True. + + Refuses to delete or modify files outside of ``sys.prefix`` - + thus uninstallation within a virtual environment can only + modify that virtual environment, even if the virtualenv is + linked to global site-packages. + + """ + if not self.check_if_exists(): + raise UninstallationError( + "Cannot uninstall requirement %s, not installed" % (self.name,) + ) + 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( + pkg_resources.to_filename(dist.project_name)) + egg_info_exists = dist.egg_info and os.path.exists(dist.egg_info) + # Special case for distutils installed package + distutils_egg_info = getattr(dist._provider, 'path', None) + + # Uninstall cases order do matter as in the case of 2 installs of the + # same package, pip needs to uninstall the currently detected version + if (egg_info_exists and dist.egg_info.endswith('.egg-info') and + not dist.egg_info.endswith(develop_egg_link_egg_info)): + # if dist.egg_info.endswith(develop_egg_link_egg_info), we + # are in fact in the develop_egg_link case + paths_to_remove.add(dist.egg_info) + if dist.has_metadata('installed-files.txt'): + for installed_file in dist.get_metadata( + 'installed-files.txt').splitlines(): + path = os.path.normpath( + os.path.join(dist.egg_info, installed_file) + ) + paths_to_remove.add(path) + # FIXME: need a test for this elif block + # occurs with --single-version-externally-managed/--record outside + # of pip + elif dist.has_metadata('top_level.txt'): + if dist.has_metadata('namespace_packages.txt'): + namespaces = dist.get_metadata('namespace_packages.txt') + else: + namespaces = [] + for top_level_pkg in [ + p for p + in dist.get_metadata('top_level.txt').splitlines() + if p and p not in namespaces]: + path = os.path.join(dist.location, top_level_pkg) + paths_to_remove.add(path) + paths_to_remove.add(path + '.py') + paths_to_remove.add(path + '.pyc') + paths_to_remove.add(path + '.pyo') + + elif distutils_egg_info: + 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 + # We cannot match on dist.egg_name because it can slightly vary + # i.e. setuptools-0.6c11-py2.6.egg vs setuptools-0.6rc11-py2.6.egg + paths_to_remove.add(dist.location) + easy_install_egg = os.path.split(dist.location)[1] + easy_install_pth = os.path.join(os.path.dirname(dist.location), + 'easy-install.pth') + paths_to_remove.add_pth(easy_install_pth, './' + easy_install_egg) + + elif develop_egg_link: + # develop egg + with open(develop_egg_link, 'r') as fh: + link_pointer = os.path.normcase(fh.readline().strip()) + assert (link_pointer == dist.location), ( + 'Egg-link %s does not match installed location of %s ' + '(at %s)' % (link_pointer, self.name, dist.location) + ) + paths_to_remove.add(develop_egg_link) + easy_install_pth = os.path.join(os.path.dirname(develop_egg_link), + 'easy-install.pth') + paths_to_remove.add_pth(easy_install_pth, dist.location) + + elif egg_info_exists and dist.egg_info.endswith('.dist-info'): + for path in pip.wheel.uninstallation_paths(dist): + paths_to_remove.add(path) + + else: + logger.debug( + 'Not sure how to uninstall: %s - Check: %s', + dist, dist.location) + + # find distutils scripts= scripts + if dist.has_metadata('scripts') and dist.metadata_isdir('scripts'): + for script in dist.metadata_listdir('scripts'): + if dist_in_usersite(dist): + bin_dir = bin_user + else: + bin_dir = bin_py + paths_to_remove.add(os.path.join(bin_dir, script)) + if WINDOWS: + paths_to_remove.add(os.path.join(bin_dir, script) + '.bat') + + # find console_scripts + if dist.has_metadata('entry_points.txt'): + if six.PY2: + options = {} + else: + options = {"delimiters": ('=', )} + config = configparser.SafeConfigParser(**options) + config.readfp( + FakeFile(dist.get_metadata_lines('entry_points.txt')) + ) + if config.has_section('console_scripts'): + for name, value in config.items('console_scripts'): + if dist_in_usersite(dist): + bin_dir = bin_user + else: + bin_dir = bin_py + paths_to_remove.add(os.path.join(bin_dir, name)) + if WINDOWS: + paths_to_remove.add( + os.path.join(bin_dir, name) + '.exe' + ) + paths_to_remove.add( + os.path.join(bin_dir, name) + '.exe.manifest' + ) + paths_to_remove.add( + os.path.join(bin_dir, name) + '-script.py' + ) + + paths_to_remove.remove(auto_confirm) + self.uninstalled = paths_to_remove + + def rollback_uninstall(self): + if self.uninstalled: + self.uninstalled.rollback() + else: + logger.error( + "Can't rollback %s, nothing uninstalled.", self.name, + ) + + def commit_uninstall(self): + if self.uninstalled: + self.uninstalled.commit() + elif not self.nothing_to_uninstall: + logger.error( + "Can't commit %s, nothing uninstalled.", self.name, + ) + + def archive(self, build_dir): + assert self.source_dir + create_archive = True + archive_name = '%s-%s.zip' % (self.name, self.pkg_info()["version"]) + archive_path = os.path.join(build_dir, archive_name) + if os.path.exists(archive_path): + response = ask_path_exists( + 'The file %s exists. (i)gnore, (w)ipe, (b)ackup ' % + display_path(archive_path), ('i', 'w', 'b')) + if response == 'i': + create_archive = False + elif response == 'w': + logger.warning('Deleting %s', display_path(archive_path)) + os.remove(archive_path) + elif response == 'b': + dest_file = backup_dir(archive_path) + logger.warning( + 'Backing up %s to %s', + display_path(archive_path), + display_path(dest_file), + ) + shutil.move(archive_path, dest_file) + if create_archive: + zip = zipfile.ZipFile( + archive_path, 'w', zipfile.ZIP_DEFLATED, + allowZip64=True + ) + dir = os.path.normcase(os.path.abspath(self.setup_py_dir)) + for dirpath, dirnames, filenames in os.walk(dir): + if 'pip-egg-info' in dirnames: + dirnames.remove('pip-egg-info') + for dirname in dirnames: + dirname = os.path.join(dirpath, dirname) + name = self._clean_zip_name(dirname, dir) + zipdir = zipfile.ZipInfo(self.name + '/' + name + '/') + zipdir.external_attr = 0x1ED << 16 # 0o755 + zip.writestr(zipdir, '') + for filename in filenames: + if filename == PIP_DELETE_MARKER_FILENAME: + continue + filename = os.path.join(dirpath, filename) + name = self._clean_zip_name(filename, dir) + zip.write(filename, self.name + '/' + name) + zip.close() + logger.info('Saved %s', display_path(archive_path)) + + def _clean_zip_name(self, name, prefix): + assert name.startswith(prefix + os.path.sep), ( + "name %r doesn't start with prefix %r" % (name, prefix) + ) + name = name[len(prefix) + 1:] + name = name.replace(os.path.sep, '/') + return name + + def match_markers(self): + if self.markers is not None: + return Marker(self.markers).evaluate() + else: + return True + + def install(self, install_options, global_options=[], root=None, + prefix=None): + if self.editable: + self.install_editable( + install_options, global_options, prefix=prefix) + return + if self.is_wheel: + version = pip.wheel.wheel_version(self.source_dir) + pip.wheel.check_compatibility(version, self.name) + + self.move_wheel_files(self.source_dir, root=root, prefix=prefix) + self.install_succeeded = True + return + + # Extend the list of global and install options passed on to + # the setup.py call with the ones from the requirements file. + # Options specified in requirements file override those + # specified on the command line, since the last option given + # to setup.py is the one that is used. + global_options += self.options.get('global_options', []) + install_options += self.options.get('install_options', []) + + if self.isolated: + global_options = list(global_options) + ["--no-user-cfg"] + + temp_location = tempfile.mkdtemp('-record', 'pip-') + record_filename = os.path.join(temp_location, 'install-record.txt') + try: + install_args = [sys.executable, "-u"] + install_args.append('-c') + install_args.append(SETUPTOOLS_SHIM % self.setup_py) + install_args += list(global_options) + \ + ['install', '--record', record_filename] + + if not self.as_egg: + install_args += ['--single-version-externally-managed'] + + if root is not None: + install_args += ['--root', root] + if prefix is not None: + install_args += ['--prefix', prefix] + + if self.pycompile: + install_args += ["--compile"] + else: + install_args += ["--no-compile"] + + if running_under_virtualenv(): + py_ver_str = 'python' + sysconfig.get_python_version() + install_args += ['--install-headers', + os.path.join(sys.prefix, 'include', 'site', + py_ver_str, self.name)] + msg = 'Running setup.py install for %s' % (self.name,) + with open_spinner(msg) as spinner: + with indent_log(): + call_subprocess( + install_args + install_options, + cwd=self.setup_py_dir, + show_stdout=False, + spinner=spinner, + ) + + if not os.path.exists(record_filename): + logger.debug('Record file %s not found', record_filename) + return + self.install_succeeded = True + if self.as_egg: + # there's no --always-unzip option we can pass to install + # command so we unable to save the installed-files.txt + return + + def prepend_root(path): + if root is None or not os.path.isabs(path): + return path + else: + return change_root(root, path) + + with open(record_filename) as f: + for line in f: + directory = os.path.dirname(line) + if directory.endswith('.egg-info'): + egg_info_dir = prepend_root(directory) + break + else: + logger.warning( + 'Could not find .egg-info directory in install record' + ' for %s', + self, + ) + # FIXME: put the record somewhere + # FIXME: should this be an error? + return + new_lines = [] + with open(record_filename) as f: + for line in f: + filename = line.strip() + if os.path.isdir(filename): + filename += os.path.sep + new_lines.append( + os.path.relpath( + prepend_root(filename), egg_info_dir) + ) + inst_files_path = os.path.join(egg_info_dir, 'installed-files.txt') + with open(inst_files_path, 'w') as f: + f.write('\n'.join(new_lines) + '\n') + finally: + if os.path.exists(record_filename): + os.remove(record_filename) + rmtree(temp_location) + + def ensure_has_source_dir(self, parent_dir): + """Ensure that a source_dir is set. + + This will create a temporary build dir if the name of the requirement + isn't known yet. + + :param parent_dir: The ideal pip parent_dir for the source_dir. + Generally src_dir for editables and build_dir for sdists. + :return: self.source_dir + """ + if self.source_dir is None: + self.source_dir = self.build_location(parent_dir) + return self.source_dir + + def remove_temporary_source(self): + """Remove the source files from this requirement, if they are marked + for deletion""" + if self.source_dir and os.path.exists( + os.path.join(self.source_dir, PIP_DELETE_MARKER_FILENAME)): + logger.debug('Removing source in %s', self.source_dir) + rmtree(self.source_dir) + self.source_dir = None + if self._temp_build_dir and os.path.exists(self._temp_build_dir): + rmtree(self._temp_build_dir) + self._temp_build_dir = None + + def install_editable(self, install_options, + global_options=(), prefix=None): + logger.info('Running setup.py develop for %s', self.name) + + if self.isolated: + global_options = list(global_options) + ["--no-user-cfg"] + + if prefix: + prefix_param = ['--prefix={0}'.format(prefix)] + install_options = list(install_options) + prefix_param + + with indent_log(): + # FIXME: should we do --install-headers here too? + call_subprocess( + [ + sys.executable, + '-c', + SETUPTOOLS_SHIM % self.setup_py + ] + + list(global_options) + + ['develop', '--no-deps'] + + list(install_options), + + cwd=self.setup_py_dir, + show_stdout=False) + + self.install_succeeded = True + + def check_if_exists(self): + """Find an installed distribution that satisfies or conflicts + with this requirement, and set self.satisfied_by or + self.conflicts_with appropriately. + """ + if self.req is None: + return False + try: + # get_distribution() will resolve the entire list of requirements + # anyway, and we've already determined that we need the requirement + # in question, so strip the marker so that we don't try to + # evaluate it. + no_marker = Requirement(str(self.req)) + no_marker.marker = None + self.satisfied_by = pkg_resources.get_distribution(str(no_marker)) + except pkg_resources.DistributionNotFound: + return False + except pkg_resources.VersionConflict: + existing_dist = pkg_resources.get_distribution( + self.req.name + ) + if self.use_user_site: + if dist_in_usersite(existing_dist): + self.conflicts_with = existing_dist + elif (running_under_virtualenv() and + dist_in_site_packages(existing_dist)): + raise InstallationError( + "Will not install to the user site because it will " + "lack sys.path precedence to %s in %s" % + (existing_dist.project_name, existing_dist.location) + ) + else: + self.conflicts_with = existing_dist + return True + + @property + def is_wheel(self): + return self.link and self.link.is_wheel + + def move_wheel_files(self, wheeldir, root=None, prefix=None): + move_wheel_files( + self.name, self.req, wheeldir, + user=self.use_user_site, + home=self.target_dir, + root=root, + prefix=prefix, + pycompile=self.pycompile, + isolated=self.isolated, + ) + + def get_dist(self): + """Return a pkg_resources.Distribution built from self.egg_info_path""" + egg_info = self.egg_info_path('').rstrip('/') + base_dir = os.path.dirname(egg_info) + metadata = pkg_resources.PathMetadata(base_dir, egg_info) + dist_name = os.path.splitext(os.path.basename(egg_info))[0] + return pkg_resources.Distribution( + os.path.dirname(egg_info), + project_name=dist_name, + metadata=metadata) + + @property + def has_hash_options(self): + """Return whether any known-good hashes are specified as options. + + These activate --require-hashes mode; hashes specified as part of a + URL do not. + + """ + return bool(self.options.get('hashes', {})) + + def hashes(self, trust_internet=True): + """Return a hash-comparer that considers my option- and URL-based + hashes to be known-good. + + Hashes in URLs--ones embedded in the requirements file, not ones + downloaded from an index server--are almost peers with ones from + flags. They satisfy --require-hashes (whether it was implicitly or + explicitly activated) but do not activate it. md5 and sha224 are not + allowed in flags, which should nudge people toward good algos. We + always OR all hashes together, even ones from URLs. + + :param trust_internet: Whether to trust URL-based (#md5=...) hashes + downloaded from the internet, as by populate_link() + + """ + good_hashes = self.options.get('hashes', {}).copy() + link = self.link if trust_internet else self.original_link + if link and link.hash: + good_hashes.setdefault(link.hash_name, []).append(link.hash) + return Hashes(good_hashes) + + +def _strip_postfix(req): + """ + Strip req postfix ( -dev, 0.2, etc ) + """ + # FIXME: use package_to_requirement? + match = re.search(r'^(.*?)(?:-dev|-\d.*)$', req) + if match: + # Strip off -dev, -0.2, etc. + req = match.group(1) + return req + + +def _build_req_from_url(url): + + parts = [p for p in url.split('#', 1)[0].split('/') if p] + + req = None + if len(parts) > 2 and parts[-2] in ('tags', 'branches', 'tag', 'branch'): + req = parts[-3] + elif len(parts) > 1 and parts[-1] == 'trunk': + req = parts[-2] + if req: + warnings.warn( + 'Sniffing the requirement name from the url is deprecated and ' + 'will be removed in the future. Please specify an #egg segment ' + 'instead.', RemovedInPip9Warning, + stacklevel=2) + return req + + +def parse_editable(editable_req, default_vcs=None): + """Parses an editable requirement into: + - a requirement name + - an URL + - extras + - editable options + Accepted requirements: + svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir + .[some_extra] + """ + + from pip.index import Link + + url = editable_req + extras = None + + # If a file path is specified with extras, strip off the extras. + m = re.match(r'^(.+)(\[[^\]]+\])$', url) + if m: + url_no_extras = m.group(1) + extras = m.group(2) + else: + url_no_extras = url + + if os.path.isdir(url_no_extras): + if not os.path.exists(os.path.join(url_no_extras, 'setup.py')): + raise InstallationError( + "Directory %r is not installable. File 'setup.py' not found." % + url_no_extras + ) + # Treating it as code that has already been checked out + url_no_extras = path_to_url(url_no_extras) + + if url_no_extras.lower().startswith('file:'): + package_name = Link(url_no_extras).egg_fragment + if extras: + return ( + package_name, + url_no_extras, + Requirement("placeholder" + extras).extras, + ) + else: + return package_name, url_no_extras, None + + for version_control in vcs: + if url.lower().startswith('%s:' % version_control): + url = '%s+%s' % (version_control, url) + break + + if '+' not in url: + if default_vcs: + url = default_vcs + '+' + url + else: + raise InstallationError( + '%s should either be a path to a local project or a VCS url ' + 'beginning with svn+, git+, hg+, or bzr+' % + editable_req + ) + + vc_type = url.split('+', 1)[0].lower() + + if not vcs.get_backend(vc_type): + error_message = 'For --editable=%s only ' % editable_req + \ + ', '.join([backend.name + '+URL' for backend in vcs.backends]) + \ + ' is currently supported' + raise InstallationError(error_message) + + package_name = Link(url).egg_fragment + if not package_name: + package_name = _build_req_from_url(editable_req) + if not package_name: + raise InstallationError( + '--editable=%s is not the right format; it must have ' + '#egg=Package' % editable_req + ) + return _strip_postfix(package_name), url, None diff --git a/pip/req/req_set.py b/pip/req/req_set.py new file mode 100644 index 00000000000..a4e6b0e161b --- /dev/null +++ b/pip/req/req_set.py @@ -0,0 +1,756 @@ +from __future__ import absolute_import + +from collections import defaultdict +from itertools import chain +import logging +import os + +from pip._vendor import pkg_resources +from pip._vendor import requests + +from pip.compat import expanduser +from pip.download import (is_file_url, is_dir_url, is_vcs_url, url_to_path, + unpack_url) +from pip.exceptions import (InstallationError, BestVersionAlreadyInstalled, + DistributionNotFound, PreviousBuildDirError, + HashError, HashErrors, HashUnpinned, + DirectoryUrlHashUnsupported, VcsHashUnsupported) +from pip.req.req_install import InstallRequirement +from pip.utils import ( + display_path, dist_in_usersite, ensure_dir, normalize_path) +from pip.utils.hashes import MissingHashes +from pip.utils.logging import indent_log +from pip.vcs import vcs +from pip.wheel import Wheel + +logger = logging.getLogger(__name__) + + +class Requirements(object): + + def __init__(self): + self._keys = [] + self._dict = {} + + def keys(self): + return self._keys + + def values(self): + return [self._dict[key] for key in self._keys] + + def __contains__(self, item): + return item in self._keys + + def __setitem__(self, key, value): + if key not in self._keys: + self._keys.append(key) + self._dict[key] = value + + def __getitem__(self, key): + return self._dict[key] + + def __repr__(self): + values = ['%s: %s' % (repr(k), repr(self[k])) for k in self.keys()] + return 'Requirements({%s})' % ', '.join(values) + + +class DistAbstraction(object): + """Abstracts out the wheel vs non-wheel prepare_files logic. + + The requirements for anything installable are as follows: + - we must be able to determine the requirement name + (or we can't correctly handle the non-upgrade case). + - we must be able to generate a list of run-time dependencies + without installing any additional packages (or we would + have to either burn time by doing temporary isolated installs + or alternatively violate pips 'don't start installing unless + all requirements are available' rule - neither of which are + desirable). + - for packages with setup requirements, we must also be able + to determine their requirements without installing additional + packages (for the same reason as run-time dependencies) + - we must be able to create a Distribution object exposing the + above metadata. + """ + + def __init__(self, req_to_install): + self.req_to_install = req_to_install + + def dist(self, finder): + """Return a setuptools Dist object.""" + raise NotImplementedError(self.dist) + + def prep_for_dist(self): + """Ensure that we can get a Dist for this requirement.""" + raise NotImplementedError(self.dist) + + +def make_abstract_dist(req_to_install): + """Factory to make an abstract dist object. + + Preconditions: Either an editable req with a source_dir, or satisfied_by or + a wheel link, or a non-editable req with a source_dir. + + :return: A concrete DistAbstraction. + """ + if req_to_install.editable: + return IsSDist(req_to_install) + elif req_to_install.link and req_to_install.link.is_wheel: + return IsWheel(req_to_install) + else: + return IsSDist(req_to_install) + + +class IsWheel(DistAbstraction): + + def dist(self, finder): + return list(pkg_resources.find_distributions( + self.req_to_install.source_dir))[0] + + def prep_for_dist(self): + # FIXME:https://github.com/pypa/pip/issues/1112 + pass + + +class IsSDist(DistAbstraction): + + def dist(self, finder): + dist = self.req_to_install.get_dist() + # FIXME: shouldn't be globally added: + if dist.has_metadata('dependency_links.txt'): + finder.add_dependency_links( + dist.get_metadata_lines('dependency_links.txt') + ) + return dist + + def prep_for_dist(self): + self.req_to_install.run_egg_info() + self.req_to_install.assert_source_matches_version() + + +class Installed(DistAbstraction): + + def dist(self, finder): + return self.req_to_install.satisfied_by + + def prep_for_dist(self): + pass + + +class RequirementSet(object): + + def __init__(self, build_dir, src_dir, download_dir, upgrade=False, + ignore_installed=False, as_egg=False, target_dir=None, + ignore_dependencies=False, force_reinstall=False, + use_user_site=False, session=None, pycompile=True, + isolated=False, wheel_download_dir=None, + wheel_cache=None, require_hashes=False): + """Create a RequirementSet. + + :param wheel_download_dir: Where still-packed .whl files should be + written to. If None they are written to the download_dir parameter. + Separate to download_dir to permit only keeping wheel archives for + pip wheel. + :param download_dir: Where still packed archives should be written to. + If None they are not saved, and are deleted immediately after + unpacking. + :param wheel_cache: The pip wheel cache, for passing to + InstallRequirement. + """ + if session is None: + raise TypeError( + "RequirementSet() missing 1 required keyword argument: " + "'session'" + ) + + self.build_dir = build_dir + self.src_dir = src_dir + # XXX: download_dir and wheel_download_dir overlap semantically and may + # be combined if we're willing to have non-wheel archives present in + # the wheelhouse output by 'pip wheel'. + self.download_dir = download_dir + self.upgrade = upgrade + self.ignore_installed = ignore_installed + self.force_reinstall = force_reinstall + self.requirements = Requirements() + # Mapping of alias: real_name + self.requirement_aliases = {} + self.unnamed_requirements = [] + self.ignore_dependencies = ignore_dependencies + self.successfully_downloaded = [] + self.successfully_installed = [] + self.reqs_to_cleanup = [] + self.as_egg = as_egg + self.use_user_site = use_user_site + self.target_dir = target_dir # set from --target option + self.session = session + self.pycompile = pycompile + self.isolated = isolated + if wheel_download_dir: + wheel_download_dir = normalize_path(wheel_download_dir) + self.wheel_download_dir = wheel_download_dir + self._wheel_cache = wheel_cache + self.require_hashes = require_hashes + # Maps from install_req -> dependencies_of_install_req + self._dependencies = defaultdict(list) + + def __str__(self): + reqs = [req for req in self.requirements.values() + if not req.comes_from] + reqs.sort(key=lambda req: req.name.lower()) + return ' '.join([str(req.req) for req in reqs]) + + def __repr__(self): + reqs = [req for req in self.requirements.values()] + reqs.sort(key=lambda req: req.name.lower()) + reqs_str = ', '.join([str(req.req) for req in reqs]) + return ('<%s object; %d requirement(s): %s>' + % (self.__class__.__name__, len(reqs), reqs_str)) + + def add_requirement(self, install_req, parent_req_name=None): + """Add install_req as a requirement to install. + + :param parent_req_name: The name of the requirement that needed this + added. The name is used because when multiple unnamed requirements + resolve to the same name, we could otherwise end up with dependency + links that point outside the Requirements set. parent_req must + already be added. Note that None implies that this is a user + supplied requirement, vs an inferred one. + :return: Additional requirements to scan. That is either [] if + the requirement is not applicable, or [install_req] if the + requirement is applicable and has just been added. + """ + name = install_req.name + if not install_req.match_markers(): + logger.warning("Ignoring %s: markers %r don't match your " + "environment", install_req.name, + install_req.markers) + return [] + + # This check has to come after we filter requirements with the + # environment markers. + if install_req.link and install_req.link.is_wheel: + wheel = Wheel(install_req.link.filename) + if not wheel.supported(): + raise InstallationError( + "%s is not a supported wheel on this platform." % + wheel.filename + ) + + install_req.as_egg = self.as_egg + install_req.use_user_site = self.use_user_site + install_req.target_dir = self.target_dir + install_req.pycompile = self.pycompile + if not name: + # url or path requirement w/o an egg fragment + self.unnamed_requirements.append(install_req) + return [install_req] + else: + try: + existing_req = self.get_requirement(name) + except KeyError: + existing_req = None + if (parent_req_name is None and existing_req and not + existing_req.constraint and + existing_req.extras == install_req.extras and not + existing_req.req.specifier == install_req.req.specifier): + raise InstallationError( + 'Double requirement given: %s (already in %s, name=%r)' + % (install_req, existing_req, name)) + if not existing_req: + # Add requirement + self.requirements[name] = install_req + # FIXME: what about other normalizations? E.g., _ vs. -? + if name.lower() != name: + self.requirement_aliases[name.lower()] = name + result = [install_req] + else: + # Assume there's no need to scan, and that we've already + # encountered this for scanning. + result = [] + if not install_req.constraint and existing_req.constraint: + if (install_req.link and not (existing_req.link and + install_req.link.path == existing_req.link.path)): + self.reqs_to_cleanup.append(install_req) + raise InstallationError( + "Could not satisfy constraints for '%s': " + "installation from path or url cannot be " + "constrained to a version" % name) + # If we're now installing a constraint, mark the existing + # object for real installation. + existing_req.constraint = False + existing_req.extras = tuple( + sorted(set(existing_req.extras).union( + set(install_req.extras)))) + logger.debug("Setting %s extras to: %s", + existing_req, existing_req.extras) + # And now we need to scan this. + result = [existing_req] + # Canonicalise to the already-added object for the backref + # check below. + install_req = existing_req + if parent_req_name: + parent_req = self.get_requirement(parent_req_name) + self._dependencies[parent_req].append(install_req) + return result + + def has_requirement(self, project_name): + name = project_name.lower() + if (name in self.requirements and + not self.requirements[name].constraint or + name in self.requirement_aliases and + not self.requirements[self.requirement_aliases[name]].constraint): + return True + return False + + @property + def has_requirements(self): + return list(req for req in self.requirements.values() if not + req.constraint) or self.unnamed_requirements + + @property + def is_download(self): + if self.download_dir: + self.download_dir = expanduser(self.download_dir) + if os.path.exists(self.download_dir): + return True + else: + logger.critical('Could not find download directory') + raise InstallationError( + "Could not find or access download directory '%s'" + % display_path(self.download_dir)) + return False + + def get_requirement(self, project_name): + for name in project_name, project_name.lower(): + if name in self.requirements: + return self.requirements[name] + if name in self.requirement_aliases: + return self.requirements[self.requirement_aliases[name]] + raise KeyError("No project with the name %r" % project_name) + + def uninstall(self, auto_confirm=False): + for req in self.requirements.values(): + if req.constraint: + continue + req.uninstall(auto_confirm=auto_confirm) + req.commit_uninstall() + + def prepare_files(self, finder): + """ + Prepare process. Create temp directories, download and/or unpack files. + """ + # make the wheelhouse + if self.wheel_download_dir: + ensure_dir(self.wheel_download_dir) + + # If any top-level requirement has a hash specified, enter + # hash-checking mode, which requires hashes from all. + root_reqs = self.unnamed_requirements + self.requirements.values() + require_hashes = (self.require_hashes or + any(req.has_hash_options for req in root_reqs)) + if require_hashes and self.as_egg: + raise InstallationError( + '--egg is not allowed with --require-hashes mode, since it ' + 'delegates dependency resolution to setuptools and could thus ' + 'result in installation of unhashed packages.') + + # Actually prepare the files, and collect any exceptions. Most hash + # exceptions cannot be checked ahead of time, because + # req.populate_link() needs to be called before we can make decisions + # based on link type. + discovered_reqs = [] + hash_errors = HashErrors() + for req in chain(root_reqs, discovered_reqs): + try: + discovered_reqs.extend(self._prepare_file( + finder, + req, + require_hashes=require_hashes, + ignore_dependencies=self.ignore_dependencies)) + except HashError as exc: + exc.req = req + hash_errors.append(exc) + + if hash_errors: + raise hash_errors + + def _check_skip_installed(self, req_to_install, finder): + """Check if req_to_install should be skipped. + + This will check if the req is installed, and whether we should upgrade + or reinstall it, taking into account all the relevant user options. + + After calling this req_to_install will only have satisfied_by set to + None if the req_to_install is to be upgraded/reinstalled etc. Any + other value will be a dist recording the current thing installed that + satisfies the requirement. + + Note that for vcs urls and the like we can't assess skipping in this + routine - we simply identify that we need to pull the thing down, + then later on it is pulled down and introspected to assess upgrade/ + reinstalls etc. + + :return: A text reason for why it was skipped, or None. + """ + # Check whether to upgrade/reinstall this req or not. + req_to_install.check_if_exists() + if req_to_install.satisfied_by: + skip_reason = 'satisfied (use --upgrade to upgrade)' + if self.upgrade: + best_installed = False + # For link based requirements we have to pull the + # tree down and inspect to assess the version #, so + # its handled way down. + if not (self.force_reinstall or req_to_install.link): + try: + finder.find_requirement(req_to_install, self.upgrade) + except BestVersionAlreadyInstalled: + skip_reason = 'up-to-date' + best_installed = True + except DistributionNotFound: + # No distribution found, so we squash the + # error - it will be raised later when we + # re-try later to do the install. + # Why don't we just raise here? + pass + + if not best_installed: + # don't uninstall conflict if user install and + # conflict is not user install + if not (self.use_user_site and not + dist_in_usersite(req_to_install.satisfied_by)): + req_to_install.conflicts_with = \ + req_to_install.satisfied_by + req_to_install.satisfied_by = None + return skip_reason + else: + return None + + def _prepare_file(self, + finder, + req_to_install, + require_hashes=False, + ignore_dependencies=False): + """Prepare a single requirements file. + + :return: A list of additional InstallRequirements to also install. + """ + # Tell user what we are doing for this requirement: + # obtain (editable), skipping, processing (local url), collecting + # (remote url or package name) + if req_to_install.constraint or req_to_install.prepared: + return [] + + req_to_install.prepared = True + + # ###################### # + # # print log messages # # + # ###################### # + if req_to_install.editable: + logger.info('Obtaining %s', req_to_install) + else: + # satisfied_by is only evaluated by calling _check_skip_installed, + # so it must be None here. + assert req_to_install.satisfied_by is None + if not self.ignore_installed: + skip_reason = self._check_skip_installed( + req_to_install, finder) + + if req_to_install.satisfied_by: + assert skip_reason is not None, ( + '_check_skip_installed returned None but ' + 'req_to_install.satisfied_by is set to %r' + % (req_to_install.satisfied_by,)) + logger.info( + 'Requirement already %s: %s', skip_reason, + req_to_install) + else: + if (req_to_install.link and + req_to_install.link.scheme == 'file'): + path = url_to_path(req_to_install.link.url) + logger.info('Processing %s', display_path(path)) + else: + logger.info('Collecting %s', req_to_install) + + with indent_log(): + # ################################ # + # # vcs update or unpack archive # # + # ################################ # + if req_to_install.editable: + if require_hashes: + raise InstallationError( + 'The editable requirement %s cannot be installed when ' + 'requiring hashes, because there is no single file to ' + 'hash.' % req_to_install) + req_to_install.ensure_has_source_dir(self.src_dir) + req_to_install.update_editable(not self.is_download) + abstract_dist = make_abstract_dist(req_to_install) + abstract_dist.prep_for_dist() + if self.is_download: + req_to_install.archive(self.download_dir) + elif req_to_install.satisfied_by: + if require_hashes: + logger.debug( + 'Since it is already installed, we are trusting this ' + 'package without checking its hash. To ensure a ' + 'completely repeatable environment, install into an ' + 'empty virtualenv.') + abstract_dist = Installed(req_to_install) + else: + # @@ if filesystem packages are not marked + # editable in a req, a non deterministic error + # occurs when the script attempts to unpack the + # build directory + req_to_install.ensure_has_source_dir(self.build_dir) + # If a checkout exists, it's unwise to keep going. version + # inconsistencies are logged later, but do not fail the + # installation. + # FIXME: this won't upgrade when there's an existing + # package unpacked in `req_to_install.source_dir` + if os.path.exists( + os.path.join(req_to_install.source_dir, 'setup.py')): + raise PreviousBuildDirError( + "pip can't proceed with requirements '%s' due to a" + " pre-existing build directory (%s). This is " + "likely due to a previous installation that failed" + ". pip is being responsible and not assuming it " + "can delete this. Please delete it and try again." + % (req_to_install, req_to_install.source_dir) + ) + req_to_install.populate_link( + finder, self.upgrade, require_hashes) + # We can't hit this spot and have populate_link return None. + # req_to_install.satisfied_by is None here (because we're + # guarded) and upgrade has no impact except when satisfied_by + # is not None. + # Then inside find_requirement existing_applicable -> False + # If no new versions are found, DistributionNotFound is raised, + # otherwise a result is guaranteed. + assert req_to_install.link + link = req_to_install.link + + # Now that we have the real link, we can tell what kind of + # requirements we have and raise some more informative errors + # than otherwise. (For example, we can raise VcsHashUnsupported + # for a VCS URL rather than HashMissing.) + if require_hashes: + # We could check these first 2 conditions inside + # unpack_url and save repetition of conditions, but then + # we would report less-useful error messages for + # unhashable requirements, complaining that there's no + # hash provided. + if is_vcs_url(link): + raise VcsHashUnsupported() + elif is_file_url(link) and is_dir_url(link): + raise DirectoryUrlHashUnsupported() + if (not req_to_install.original_link and + not req_to_install.is_pinned): + # Unpinned packages are asking for trouble when a new + # version is uploaded. This isn't a security check, but + # it saves users a surprising hash mismatch in the + # future. + # + # file:/// URLs aren't pinnable, so don't complain + # about them not being pinned. + raise HashUnpinned() + hashes = req_to_install.hashes( + trust_internet=not require_hashes) + if require_hashes and not hashes: + # Known-good hashes are missing for this requirement, so + # shim it with a facade object that will provoke hash + # computation and then raise a HashMissing exception + # showing the user what the hash should be. + hashes = MissingHashes() + + try: + download_dir = self.download_dir + # We always delete unpacked sdists after pip ran. + autodelete_unpacked = True + if req_to_install.link.is_wheel \ + and self.wheel_download_dir: + # when doing 'pip wheel` we download wheels to a + # dedicated dir. + download_dir = self.wheel_download_dir + if req_to_install.link.is_wheel: + if download_dir: + # When downloading, we only unpack wheels to get + # metadata. + autodelete_unpacked = True + else: + # When installing a wheel, we use the unpacked + # wheel. + autodelete_unpacked = False + unpack_url( + req_to_install.link, req_to_install.source_dir, + download_dir, autodelete_unpacked, + session=self.session, hashes=hashes) + except requests.HTTPError as exc: + logger.critical( + 'Could not install requirement %s because ' + 'of error %s', + req_to_install, + exc, + ) + raise InstallationError( + 'Could not install requirement %s because ' + 'of HTTP error %s for URL %s' % + (req_to_install, exc, req_to_install.link) + ) + abstract_dist = make_abstract_dist(req_to_install) + abstract_dist.prep_for_dist() + if self.is_download: + # Make a .zip of the source_dir we already created. + if req_to_install.link.scheme in vcs.all_schemes: + req_to_install.archive(self.download_dir) + # req_to_install.req is only avail after unpack for URL + # pkgs repeat check_if_exists to uninstall-on-upgrade + # (#14) + if not self.ignore_installed: + req_to_install.check_if_exists() + if req_to_install.satisfied_by: + if self.upgrade or self.ignore_installed: + # don't uninstall conflict if user install and + # conflict is not user install + if not (self.use_user_site and not + dist_in_usersite( + req_to_install.satisfied_by)): + req_to_install.conflicts_with = \ + req_to_install.satisfied_by + req_to_install.satisfied_by = None + else: + logger.info( + 'Requirement already satisfied (use ' + '--upgrade to upgrade): %s', + req_to_install, + ) + + # ###################### # + # # parse dependencies # # + # ###################### # + dist = abstract_dist.dist(finder) + more_reqs = [] + + def add_req(subreq): + sub_install_req = InstallRequirement( + str(subreq), + req_to_install, + isolated=self.isolated, + wheel_cache=self._wheel_cache, + ) + more_reqs.extend(self.add_requirement( + sub_install_req, req_to_install.name)) + + # We add req_to_install before its dependencies, so that we + # can refer to it when adding dependencies. + if not self.has_requirement(req_to_install.name): + # 'unnamed' requirements will get added here + self.add_requirement(req_to_install, None) + + if not ignore_dependencies: + if (req_to_install.extras): + logger.debug( + "Installing extra requirements: %r", + ','.join(req_to_install.extras), + ) + missing_requested = sorted( + set(req_to_install.extras) - set(dist.extras) + ) + for missing in missing_requested: + logger.warning( + '%s does not provide the extra \'%s\'', + dist, missing + ) + + available_requested = sorted( + set(dist.extras) & set(req_to_install.extras) + ) + for subreq in dist.requires(available_requested): + add_req(subreq) + + # cleanup tmp src + self.reqs_to_cleanup.append(req_to_install) + + if not req_to_install.editable and not req_to_install.satisfied_by: + # XXX: --no-install leads this to report 'Successfully + # downloaded' for only non-editable reqs, even though we took + # action on them. + self.successfully_downloaded.append(req_to_install) + + return more_reqs + + def cleanup_files(self): + """Clean up files, remove builds.""" + logger.debug('Cleaning up...') + with indent_log(): + for req in self.reqs_to_cleanup: + req.remove_temporary_source() + + def _to_install(self): + """Create the installation order. + + The installation order is topological - requirements are installed + before the requiring thing. We break cycles at an arbitrary point, + and make no other guarantees. + """ + # The current implementation, which we may change at any point + # installs the user specified things in the order given, except when + # dependencies must come earlier to achieve topological order. + order = [] + ordered_reqs = set() + + def schedule(req): + if req.satisfied_by or req in ordered_reqs: + return + if req.constraint: + return + ordered_reqs.add(req) + for dep in self._dependencies[req]: + schedule(dep) + order.append(req) + for install_req in self.requirements.values(): + schedule(install_req) + return order + + def install(self, install_options, global_options=(), *args, **kwargs): + """ + Install everything in this set (after having downloaded and unpacked + the packages) + """ + to_install = self._to_install() + + if to_install: + logger.info( + 'Installing collected packages: %s', + ', '.join([req.name for req in to_install]), + ) + + with indent_log(): + for requirement in to_install: + if requirement.conflicts_with: + logger.info( + 'Found existing installation: %s', + requirement.conflicts_with, + ) + with indent_log(): + requirement.uninstall(auto_confirm=True) + try: + requirement.install( + install_options, + global_options, + *args, + **kwargs + ) + except: + # if install did not succeed, rollback previous uninstall + if (requirement.conflicts_with and not + requirement.install_succeeded): + requirement.rollback_uninstall() + raise + else: + if (requirement.conflicts_with and + requirement.install_succeeded): + requirement.commit_uninstall() + requirement.remove_temporary_source() + + self.successfully_installed = to_install diff --git a/pip/req/req_uninstall.py b/pip/req/req_uninstall.py new file mode 100644 index 00000000000..5248430a9b1 --- /dev/null +++ b/pip/req/req_uninstall.py @@ -0,0 +1,195 @@ +from __future__ import absolute_import + +import logging +import os +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, renames, normalize_path +from pip.utils.logging import indent_log + + +logger = logging.getLogger(__name__) + + +class UninstallPathSet(object): + """A set of file paths to be removed in the uninstallation of a + requirement.""" + def __init__(self, dist): + self.paths = set() + self._refuse = set() + self.pth = {} + self.dist = dist + self.save_dir = None + self._moved_paths = [] + + def _permitted(self, path): + """ + Return True if the given path is one we are permitted to + remove/modify, False otherwise. + + """ + return is_local(path) + + def add(self, path): + head, tail = os.path.split(path) + + # we normalize the head to resolve parent directory symlinks, but not + # the tail, since we only want to uninstall symlinks, not their targets + path = os.path.join(normalize_path(head), os.path.normcase(tail)) + + if not os.path.exists(path): + return + if self._permitted(path): + self.paths.add(path) + else: + self._refuse.add(path) + + # __pycache__ files can show up after 'installed-files.txt' is created, + # due to imports + if os.path.splitext(path)[1] == '.py' and uses_pycache: + self.add(cache_from_source(path)) + + def add_pth(self, pth_file, entry): + pth_file = normalize_path(pth_file) + if self._permitted(pth_file): + if pth_file not in self.pth: + self.pth[pth_file] = UninstallPthEntries(pth_file) + self.pth[pth_file].add(entry) + else: + self._refuse.add(pth_file) + + def compact(self, paths): + """Compact a path set to contain the minimal number of paths + necessary to contain all paths in the set. If /a/path/ and + /a/path/to/a/file.txt are both in the set, leave only the + shorter path.""" + short_paths = set() + for path in sorted(paths, key=len): + if not any([ + (path.startswith(shortpath) and + path[len(shortpath.rstrip(os.path.sep))] == os.path.sep) + for shortpath in short_paths]): + short_paths.add(path) + return short_paths + + def _stash(self, path): + return os.path.join( + self.save_dir, os.path.splitdrive(path)[1].lstrip(os.path.sep)) + + def remove(self, auto_confirm=False): + """Remove paths in ``self.paths`` with confirmation (unless + ``auto_confirm`` is True).""" + if not self.paths: + logger.info( + "Can't uninstall '%s'. No files were found to uninstall.", + self.dist.project_name, + ) + return + logger.info( + 'Uninstalling %s-%s:', + self.dist.project_name, self.dist.version + ) + + with indent_log(): + paths = sorted(self.compact(self.paths)) + + if auto_confirm: + response = 'y' + else: + for path in paths: + logger.info(path) + response = ask('Proceed (y/n)? ', ('y', 'n')) + if self._refuse: + logger.info('Not removing or modifying (outside of prefix):') + for path in self.compact(self._refuse): + logger.info(path) + if response == 'y': + self.save_dir = tempfile.mkdtemp(suffix='-uninstall', + prefix='pip-') + for path in paths: + new_path = self._stash(path) + logger.debug('Removing file or directory %s', path) + self._moved_paths.append(path) + renames(path, new_path) + for pth in self.pth.values(): + pth.remove() + logger.info( + 'Successfully uninstalled %s-%s', + self.dist.project_name, self.dist.version + ) + + def rollback(self): + """Rollback the changes previously made by remove().""" + if self.save_dir is None: + logger.error( + "Can't roll back %s; was not uninstalled", + self.dist.project_name, + ) + return False + logger.info('Rolling back uninstall of %s', self.dist.project_name) + for path in self._moved_paths: + tmp_path = self._stash(path) + logger.debug('Replacing %s', path) + renames(tmp_path, path) + for pth in self.pth.values(): + pth.rollback() + + def commit(self): + """Remove temporary save dir: rollback will no longer be possible.""" + if self.save_dir is not None: + rmtree(self.save_dir) + self.save_dir = None + self._moved_paths = [] + + +class UninstallPthEntries(object): + def __init__(self, pth_file): + if not os.path.isfile(pth_file): + raise UninstallationError( + "Cannot remove entries from nonexistent file %s" % pth_file + ) + self.file = pth_file + self.entries = set() + self._saved_lines = None + + def add(self, entry): + entry = os.path.normcase(entry) + # On Windows, os.path.normcase converts the entry to use + # backslashes. This is correct for entries that describe absolute + # paths outside of site-packages, but all the others use forward + # slashes. + if WINDOWS and not os.path.splitdrive(entry)[0]: + entry = entry.replace('\\', '/') + self.entries.add(entry) + + def remove(self): + logger.debug('Removing pth entries from %s:', self.file) + with open(self.file, 'rb') as fh: + # windows uses '\r\n' with py3k, but uses '\n' with py2.x + lines = fh.readlines() + self._saved_lines = lines + if any(b'\r\n' in line for line in lines): + endline = '\r\n' + else: + endline = '\n' + for entry in self.entries: + try: + logger.debug('Removing entry: %s', entry) + lines.remove((entry + endline).encode("utf-8")) + except ValueError: + pass + with open(self.file, 'wb') as fh: + fh.writelines(lines) + + def rollback(self): + if self._saved_lines is None: + logger.error( + 'Cannot roll back changes to %s, none were made', self.file + ) + return False + logger.debug('Rolling %s back to previous state', self.file) + with open(self.file, 'wb') as fh: + fh.writelines(self._saved_lines) + return True diff --git a/pip/runner.py b/pip/runner.py deleted file mode 100644 index be830ad9a95..00000000000 --- a/pip/runner.py +++ /dev/null @@ -1,18 +0,0 @@ -import sys -import os - - -def run(): - base = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - ## FIXME: this is kind of crude; if we could create a fake pip - ## module, then exec into it and update pip.__path__ properly, we - ## wouldn't have to update sys.path: - sys.path.insert(0, base) - import pip - return pip.main() - - -if __name__ == '__main__': - exit = run() - if exit: - sys.exit(exit) diff --git a/pip/status_codes.py b/pip/status_codes.py index 5e29502cddf..275360a3175 100644 --- a/pip/status_codes.py +++ b/pip/status_codes.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + SUCCESS = 0 ERROR = 1 UNKNOWN_ERROR = 2 diff --git a/pip/util.py b/pip/util.py deleted file mode 100644 index f459bb27ef4..00000000000 --- a/pip/util.py +++ /dev/null @@ -1,720 +0,0 @@ -import sys -import shutil -import os -import stat -import re -import posixpath -import zipfile -import tarfile -import subprocess -import textwrap - -from pip.exceptions import InstallationError, BadCommand, PipError -from pip.backwardcompat import(WindowsError, string_types, raw_input, - console_to_str, user_site, PermissionError) -from pip.locations import site_packages, running_under_virtualenv, virtualenv_no_global -from pip.log import logger -from pip._vendor import pkg_resources -from pip._vendor.distlib import version - -__all__ = ['rmtree', 'display_path', 'backup_dir', - 'find_command', 'ask', 'Inf', - 'normalize_name', 'splitext', - 'format_size', 'is_installable_dir', - 'is_svn_page', 'file_contents', - 'split_leading_dir', 'has_leading_dir', - 'make_path_relative', 'normalize_path', - 'renames', 'get_terminal_size', 'get_prog', - 'unzip_file', 'untar_file', 'create_download_cache_folder', - 'cache_download', 'unpack_file', 'call_subprocess'] - - -def get_prog(): - try: - if os.path.basename(sys.argv[0]) in ('__main__.py', '-c'): - return "%s -m pip" % sys.executable - except (AttributeError, TypeError, IndexError): - pass - return 'pip' - - -def rmtree(dir, ignore_errors=False): - shutil.rmtree(dir, ignore_errors=ignore_errors, - onerror=rmtree_errorhandler) - - -def rmtree_errorhandler(func, path, exc_info): - """On Windows, the files in .svn are read-only, so when rmtree() tries to - remove them, an exception is thrown. We catch that here, remove the - read-only attribute, and hopefully continue without problems.""" - exctype, value = exc_info[:2] - if not ((exctype is WindowsError and value.args[0] == 5) or #others - (exctype is OSError and value.args[0] == 13) or #python2.4 - (exctype is PermissionError and value.args[3] == 5) #python3.3 - ): - raise - # file type should currently be read only - if ((os.stat(path).st_mode & stat.S_IREAD) != stat.S_IREAD): - raise - # convert to read/write - os.chmod(path, stat.S_IWRITE) - # use the original function to repeat the operation - func(path) - - -def display_path(path): - """Gives the display value for a given path, making it relative to cwd - if possible.""" - path = os.path.normcase(os.path.abspath(path)) - if path.startswith(os.getcwd() + os.path.sep): - path = '.' + path[len(os.getcwd()):] - return path - - -def backup_dir(dir, ext='.bak'): - """Figure out the name of a directory to back up the given dir to - (adding .bak, .bak2, etc)""" - n = 1 - extension = ext - while os.path.exists(dir + extension): - n += 1 - extension = ext + str(n) - return dir + extension - - -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) - if isinstance(paths, string_types): - paths = [paths] - # check if there are funny path extensions for executables, e.g. Windows - if pathext is None: - pathext = get_pathext() - pathext = [ext for ext in pathext.lower().split(os.pathsep) if len(ext)] - # don't use extensions if the command ends with one of them - if os.path.splitext(cmd)[1].lower() in pathext: - pathext = [''] - # check if we find the command on PATH - for path in paths: - # try without extension first - cmd_path = os.path.join(path, cmd) - for ext in pathext: - # then including the extension - cmd_path_ext = cmd_path + ext - if os.path.isfile(cmd_path_ext): - return cmd_path_ext - if os.path.isfile(cmd_path): - return cmd_path - 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_path_exists(message, options): - for action in os.environ.get('PIP_EXISTS_ACTION', '').split(): - if action in options: - return action - return ask(message, options) - - -def ask(message, options): - """Ask the message interactively, with the given possible responses""" - while 1: - if os.environ.get('PIP_NO_INPUT'): - raise Exception('No input was expected ($PIP_NO_INPUT set); question: %s' % message) - response = raw_input(message) - response = response.strip().lower() - if response not in options: - print('Your response (%r) was not one of the expected responses: %s' % ( - response, ', '.join(options))) - else: - return response - - -class _Inf(object): - """I am bigger than everything!""" - - def __eq__(self, other): - if self is other: - return True - else: - return False - - def __ne__(self, other): - return not self.__eq__(other) - - def __lt__(self, other): - return False - - def __le__(self, other): - return False - - def __gt__(self, other): - return True - - def __ge__(self, other): - return True - - def __repr__(self): - return 'Inf' - - -Inf = _Inf() #this object is not currently used as a sortable in our code -del _Inf - - -_normalize_re = re.compile(r'[^a-z]', re.I) - - -def normalize_name(name): - return _normalize_re.sub('-', name.lower()) - - -def format_size(bytes): - if bytes > 1000*1000: - return '%.1fMB' % (bytes/1000.0/1000) - elif bytes > 10*1000: - return '%ikB' % (bytes/1000) - elif bytes > 1000: - return '%.1fkB' % (bytes/1000.0) - else: - return '%ibytes' % bytes - - -def is_installable_dir(path): - """Return True if `path` is a directory containing a setup.py file.""" - if not os.path.isdir(path): - return False - setup_py = os.path.join(path, 'setup.py') - if os.path.isfile(setup_py): - return True - return False - - -def is_svn_page(html): - """Returns true if the page appears to be the index page of an svn repository""" - return (re.search(r'[^<]*Revision \d+:', html) - and re.search(r'Powered by (?:<a[^>]*?>)?Subversion', html, re.I)) - - -def file_contents(filename): - fp = open(filename, 'rb') - try: - return fp.read().decode('utf-8') - finally: - fp.close() - - -def split_leading_dir(path): - path = str(path) - path = path.lstrip('/').lstrip('\\') - if '/' in path and (('\\' in path and path.find('/') < path.find('\\')) - or '\\' not in path): - return path.split('/', 1) - elif '\\' in path: - return path.split('\\', 1) - else: - return path, '' - - -def has_leading_dir(paths): - """Returns true if all the paths have the same leading path name - (i.e., everything is in one subdirectory in an archive)""" - common_prefix = None - for path in paths: - prefix, rest = split_leading_dir(path) - if not prefix: - return False - elif common_prefix is None: - common_prefix = prefix - elif prefix != common_prefix: - return False - return True - - -def make_path_relative(path, rel_to): - """ - Make a filename relative, where the filename path, and it is - relative to rel_to - - >>> make_relative_path('/usr/share/something/a-file.pth', - ... '/usr/share/another-place/src/Directory') - '../../../something/a-file.pth' - >>> make_relative_path('/usr/share/something/a-file.pth', - ... '/home/user/src/Directory') - '../../../usr/share/something/a-file.pth' - >>> make_relative_path('/usr/share/a-file.pth', '/usr/share/') - 'a-file.pth' - """ - path_filename = os.path.basename(path) - path = os.path.dirname(path) - path = os.path.normpath(os.path.abspath(path)) - rel_to = os.path.normpath(os.path.abspath(rel_to)) - path_parts = path.strip(os.path.sep).split(os.path.sep) - rel_to_parts = rel_to.strip(os.path.sep).split(os.path.sep) - while path_parts and rel_to_parts and path_parts[0] == rel_to_parts[0]: - path_parts.pop(0) - rel_to_parts.pop(0) - full_parts = ['..']*len(rel_to_parts) + path_parts + [path_filename] - if full_parts == ['']: - return '.' + os.path.sep - return os.path.sep.join(full_parts) - - -def normalize_path(path): - """ - Convert a path to its canonical, case-normalized, absolute version. - - """ - return os.path.normcase(os.path.realpath(os.path.expanduser(path))) - - -def splitext(path): - """Like os.path.splitext, but take off .tar too""" - base, ext = posixpath.splitext(path) - if base.lower().endswith('.tar'): - ext = base[-4:] + ext - base = base[:-4] - return base, ext - - -def renames(old, new): - """Like os.renames(), but handles renaming across devices.""" - # Implementation borrowed from os.renames(). - head, tail = os.path.split(new) - if head and tail and not os.path.exists(head): - os.makedirs(head) - - shutil.move(old, new) - - head, tail = os.path.split(old) - if head and tail: - try: - os.removedirs(head) - except OSError: - pass - - -def is_local(path): - """ - Return True if path is within sys.prefix, if we're running in a virtualenv. - - If we're not in a virtualenv, all paths are considered "local." - - """ - if not running_under_virtualenv(): - return True - return normalize_path(path).startswith(normalize_path(sys.prefix)) - - -def dist_is_local(dist): - """ - Return True if given Distribution object is installed locally - (i.e. within current virtualenv). - - Always True if we're not in a virtualenv. - - """ - return is_local(dist_location(dist)) - - -def dist_in_usersite(dist): - """ - Return True if given Distribution is installed in user site. - """ - if user_site: - return normalize_path(dist_location(dist)).startswith(normalize_path(user_site)) - else: - return False - -def dist_in_site_packages(dist): - """ - Return True if given Distribution is installed in distutils.sysconfig.get_python_lib(). - """ - return normalize_path(dist_location(dist)).startswith(normalize_path(site_packages)) - - -def dist_is_editable(dist): - """Is distribution an editable install?""" - #TODO: factor out determining editableness out of FrozenRequirement - from pip import FrozenRequirement - req = FrozenRequirement.from_dist(dist, []) - return req.editable - -def get_installed_distributions(local_only=True, - skip=('setuptools', 'pip', 'python', 'distribute'), - include_editables=True, - editables_only=False): - """ - Return a list of installed Distribution objects. - - If ``local_only`` is True (default), only return installations - local to the current virtualenv, if in a virtualenv. - - ``skip`` argument is an iterable of lower-case project names to - ignore; defaults to ('setuptools', 'pip', 'python'). [FIXME also - skip virtualenv?] - - If ``editables`` is False, don't report editables. - - If ``editables_only`` is True , only report editables. - - """ - if local_only: - local_test = dist_is_local - else: - local_test = lambda d: True - - if include_editables: - editable_test = lambda d: True - else: - editable_test = lambda d: not dist_is_editable(d) - - if editables_only: - editables_only_test = lambda d: dist_is_editable(d) - else: - editables_only_test = lambda d: True - - return [d for d in pkg_resources.working_set - if local_test(d) - and d.key not in skip - and editable_test(d) - and editables_only_test(d) - ] - - -def egg_link_path(dist): - """ - Return the path for the .egg-link file if it exists, otherwise, None. - - There's 3 scenarios: - 1) not in a virtualenv - try to find in site.USER_SITE, then site_packages - 2) in a no-global virtualenv - try to find in site_packages - 3) in a yes-global virtualenv - try to find in site_packages, then site.USER_SITE (don't look in global location) - - For #1 and #3, there could be odd cases, where there's an egg-link in 2 locations. - This method will just return the first one found. - """ - sites = [] - if running_under_virtualenv(): - if virtualenv_no_global(): - sites.append(site_packages) - else: - sites.append(site_packages) - if user_site: - sites.append(user_site) - else: - if user_site: - sites.append(user_site) - sites.append(site_packages) - - for site in sites: - egglink = os.path.join(site, dist.project_name) + '.egg-link' - if os.path.isfile(egglink): - return egglink - - -def dist_location(dist): - """ - Get the site-packages location of this distribution. Generally - this is dist.location, except in the case of develop-installed - packages, where dist.location is the source code location, and we - want to know where the egg-link file is. - - """ - egg_link = egg_link_path(dist) - if egg_link: - return egg_link - return dist.location - - -def get_terminal_size(): - """Returns a tuple (x, y) representing the width(x) and the height(x) - in characters of the terminal window.""" - def ioctl_GWINSZ(fd): - try: - import fcntl - import termios - import struct - cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, - '1234')) - except: - return None - if cr == (0, 0): - return None - if cr == (0, 0): - return None - return cr - cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) - if not cr: - try: - fd = os.open(os.ctermid(), os.O_RDONLY) - cr = ioctl_GWINSZ(fd) - os.close(fd) - except: - pass - if not cr: - cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) - return int(cr[1]), int(cr[0]) - - -def current_umask(): - """Get the current umask which involves having to set it temporarily.""" - mask = os.umask(0) - os.umask(mask) - return mask - - -def unzip_file(filename, location, flatten=True): - """ - Unzip the file (with path `filename`) to the destination `location`. All - files are written based on system defaults and umask (i.e. permissions are - not preserved), except that regular file members with any execute - permissions (user, group, or world) have "chmod +x" applied after being - written. Note that for windows, any execute changes using os.chmod are - no-ops per the python docs. - """ - if not os.path.exists(location): - os.makedirs(location) - zipfp = open(filename, 'rb') - try: - zip = zipfile.ZipFile(zipfp) - leading = has_leading_dir(zip.namelist()) and flatten - for info in zip.infolist(): - name = info.filename - data = zip.read(name) - fn = name - if leading: - fn = split_leading_dir(name)[1] - fn = os.path.join(location, fn) - dir = os.path.dirname(fn) - if not os.path.exists(dir): - os.makedirs(dir) - if fn.endswith('/') or fn.endswith('\\'): - # A directory - if not os.path.exists(fn): - os.makedirs(fn) - else: - fp = open(fn, 'wb') - try: - fp.write(data) - finally: - fp.close() - mode = info.external_attr >> 16 - # if mode and regular file and any execute permissions for user/group/world? - if mode and stat.S_ISREG(mode) and mode & 0o111: - # make dest file have execute for user/group/world (chmod +x) - # no-op on windows per python docs - os.chmod(fn, (0o777-current_umask() | 0o111)) - finally: - zipfp.close() - - -def untar_file(filename, location): - """ - Untar the file (with path `filename`) to the destination `location`. - All files are written based on system defaults and umask (i.e. permissions - are not preserved), except that regular file members with any execute - permissions (user, group, or world) have "chmod +x" applied after being - written. Note that for windows, any execute changes using os.chmod are - no-ops per the python docs. - """ - if not os.path.exists(location): - os.makedirs(location) - if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'): - mode = 'r:gz' - elif filename.lower().endswith('.bz2') or filename.lower().endswith('.tbz'): - mode = 'r:bz2' - elif filename.lower().endswith('.tar'): - mode = 'r' - else: - logger.warn('Cannot determine compression type for file %s' % filename) - mode = 'r:*' - tar = tarfile.open(filename, mode) - try: - # note: python<=2.5 doesnt seem to know about pax headers, filter them - leading = has_leading_dir([ - member.name for member in tar.getmembers() - if member.name != 'pax_global_header' - ]) - for member in tar.getmembers(): - fn = member.name - if fn == 'pax_global_header': - continue - if leading: - fn = split_leading_dir(fn)[1] - path = os.path.join(location, fn) - if member.isdir(): - if not os.path.exists(path): - os.makedirs(path) - elif member.issym(): - try: - tar._extract_member(member, path) - except: - e = sys.exc_info()[1] - # Some corrupt tar files seem to produce this - # (specifically bad symlinks) - logger.warn( - 'In the tar file %s the member %s is invalid: %s' - % (filename, member.name, e)) - continue - else: - try: - fp = tar.extractfile(member) - except (KeyError, AttributeError): - e = sys.exc_info()[1] - # Some corrupt tar files seem to produce this - # (specifically bad symlinks) - logger.warn( - 'In the tar file %s the member %s is invalid: %s' - % (filename, member.name, e)) - continue - if not os.path.exists(os.path.dirname(path)): - os.makedirs(os.path.dirname(path)) - destfp = open(path, 'wb') - try: - shutil.copyfileobj(fp, destfp) - finally: - destfp.close() - fp.close() - # member have any execute permissions for user/group/world? - if member.mode & 0o111: - # make dest file have execute for user/group/world - # no-op on windows per python docs - os.chmod(path, (0o777-current_umask() | 0o111)) - finally: - tar.close() - - -def create_download_cache_folder(folder): - logger.indent -= 2 - logger.notify('Creating supposed download cache at %s' % folder) - logger.indent += 2 - os.makedirs(folder) - - -def cache_download(target_file, temp_location, content_type): - logger.notify('Storing download in cache at %s' % display_path(target_file)) - shutil.copyfile(temp_location, target_file) - fp = open(target_file+'.content-type', 'w') - fp.write(content_type) - fp.close() - - -def unpack_file(filename, location, content_type, link): - filename = os.path.realpath(filename) - if (content_type == 'application/zip' - or filename.endswith('.zip') - or filename.endswith('.pybundle') - or filename.endswith('.whl') - or zipfile.is_zipfile(filename)): - unzip_file(filename, location, flatten=not filename.endswith(('.pybundle', '.whl'))) - elif (content_type == 'application/x-gzip' - or tarfile.is_tarfile(filename) - or splitext(filename)[1].lower() in ('.tar', '.tar.gz', '.tar.bz2', '.tgz', '.tbz')): - untar_file(filename, location) - elif (content_type and content_type.startswith('text/html') - and is_svn_page(file_contents(filename))): - # We don't really care about this - from pip.vcs.subversion import Subversion - Subversion('svn+' + link.url).unpack(location) - else: - ## FIXME: handle? - ## FIXME: magic signatures? - logger.fatal('Cannot unpack file %s (downloaded from %s, content-type: %s); cannot detect archive format' - % (filename, location, content_type)) - raise InstallationError('Cannot determine archive format of %s' % location) - - -def call_subprocess(cmd, show_stdout=True, - filter_stdout=None, cwd=None, - raise_on_returncode=True, - command_level=logger.DEBUG, command_desc=None, - extra_environ=None): - if command_desc is None: - cmd_parts = [] - for part in cmd: - if ' ' in part or '\n' in part or '"' in part or "'" in part: - part = '"%s"' % part.replace('"', '\\"') - cmd_parts.append(part) - command_desc = ' '.join(cmd_parts) - if show_stdout: - stdout = None - else: - stdout = subprocess.PIPE - logger.log(command_level, "Running command %s" % command_desc) - env = os.environ.copy() - if extra_environ: - env.update(extra_environ) - try: - proc = subprocess.Popen( - cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout, - cwd=cwd, env=env) - except Exception: - e = sys.exc_info()[1] - logger.fatal( - "Error %s while executing command %s" % (e, command_desc)) - raise - all_output = [] - if stdout is not None: - stdout = proc.stdout - while 1: - line = console_to_str(stdout.readline()) - if not line: - break - line = line.rstrip() - all_output.append(line + '\n') - if filter_stdout: - level = filter_stdout(line) - if isinstance(level, tuple): - level, line = level - logger.log(level, line) - if not logger.stdout_level_matches(level): - logger.show_progress() - else: - logger.info(line) - else: - returned_stdout, returned_stderr = proc.communicate() - all_output = [returned_stdout or ''] - proc.wait() - if proc.returncode: - if raise_on_returncode: - if all_output: - logger.notify('Complete output from command %s:' % command_desc) - logger.notify('\n'.join(all_output) + '\n----------------------------------------') - raise InstallationError( - "Command %s failed with error code %s in %s" - % (command_desc, proc.returncode, cwd)) - else: - logger.warn( - "Command %s had error code %s in %s" - % (command_desc, proc.returncode, cwd)) - if stdout is not None: - return ''.join(all_output) - - -def is_prerelease(vers): - """ - Attempt to determine if this is a pre-release using PEP386/PEP426 rules. - - Will return True if it is a pre-release and False if not. Versions are - assumed to be a pre-release if they cannot be parsed. - """ - normalized = version._suggest_normalized_version(vers) - - if normalized is None: - # Cannot normalize, assume it is a pre-release - return True - - parsed = version._normalized_key(normalized) - return any([any([y in set(["a", "b", "c", "rc", "dev"]) for y in x]) for x in parsed]) diff --git a/pip/utils/__init__.py b/pip/utils/__init__.py new file mode 100644 index 00000000000..b5d01cde61f --- /dev/null +++ b/pip/utils/__init__.py @@ -0,0 +1,860 @@ +from __future__ import absolute_import + +from collections import deque +import contextlib +import errno +import io +import locale +# we have a submodule named 'logging' which would shadow this if we used the +# regular name: +import logging as std_logging +import re +import os +import posixpath +import shutil +import stat +import subprocess +import sys +import tarfile +import zipfile + +from pip.exceptions import InstallationError +from pip.compat import console_to_str, expanduser, stdlib_pkgs +from pip.locations import ( + site_packages, user_site, running_under_virtualenv, virtualenv_no_global, + write_delete_marker_file, +) +from pip._vendor import pkg_resources +from pip._vendor.six.moves import input +from pip._vendor.six import PY2 +from pip._vendor.retrying import retry + +if PY2: + from io import BytesIO as StringIO +else: + from io import StringIO + +__all__ = ['rmtree', 'display_path', 'backup_dir', + 'ask', 'splitext', + 'format_size', 'is_installable_dir', + 'is_svn_page', 'file_contents', + 'split_leading_dir', 'has_leading_dir', + 'normalize_path', + 'renames', 'get_terminal_size', 'get_prog', + 'unzip_file', 'untar_file', 'unpack_file', 'call_subprocess', + 'captured_stdout', 'remove_tracebacks', 'ensure_dir', + 'ARCHIVE_EXTENSIONS', 'SUPPORTED_EXTENSIONS', + 'get_installed_version'] + + +logger = std_logging.getLogger(__name__) + +BZ2_EXTENSIONS = ('.tar.bz2', '.tbz') +XZ_EXTENSIONS = ('.tar.xz', '.txz', '.tlz', '.tar.lz', '.tar.lzma') +ZIP_EXTENSIONS = ('.zip', '.whl') +TAR_EXTENSIONS = ('.tar.gz', '.tgz', '.tar') +ARCHIVE_EXTENSIONS = ( + ZIP_EXTENSIONS + BZ2_EXTENSIONS + TAR_EXTENSIONS + XZ_EXTENSIONS) +SUPPORTED_EXTENSIONS = ZIP_EXTENSIONS + TAR_EXTENSIONS +try: + import bz2 # noqa + SUPPORTED_EXTENSIONS += BZ2_EXTENSIONS +except ImportError: + logger.debug('bz2 module is not available') + +try: + # Only for Python 3.3+ + import lzma # noqa + SUPPORTED_EXTENSIONS += XZ_EXTENSIONS +except ImportError: + logger.debug('lzma module is not available') + + +def import_or_raise(pkg_or_module_string, ExceptionType, *args, **kwargs): + try: + return __import__(pkg_or_module_string) + except ImportError: + raise ExceptionType(*args, **kwargs) + + +def ensure_dir(path): + """os.path.makedirs without EEXIST.""" + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + +def get_prog(): + try: + if os.path.basename(sys.argv[0]) in ('__main__.py', '-c'): + return "%s -m pip" % sys.executable + except (AttributeError, TypeError, IndexError): + pass + return 'pip' + + +# Retry every half second for up to 3 seconds +@retry(stop_max_delay=3000, wait_fixed=500) +def rmtree(dir, ignore_errors=False): + shutil.rmtree(dir, ignore_errors=ignore_errors, + onerror=rmtree_errorhandler) + + +def rmtree_errorhandler(func, path, exc_info): + """On Windows, the files in .svn are read-only, so when rmtree() tries to + remove them, an exception is thrown. We catch that here, remove the + read-only attribute, and hopefully continue without problems.""" + # if file type currently read only + if os.stat(path).st_mode & stat.S_IREAD: + # convert to read/write + os.chmod(path, stat.S_IWRITE) + # use the original function to repeat the operation + func(path) + return + else: + raise + + +def display_path(path): + """Gives the display value for a given path, making it relative to cwd + if possible.""" + path = os.path.normcase(os.path.abspath(path)) + if sys.version_info[0] == 2: + path = path.decode(sys.getfilesystemencoding(), 'replace') + path = path.encode(sys.getdefaultencoding(), 'replace') + if path.startswith(os.getcwd() + os.path.sep): + path = '.' + path[len(os.getcwd()):] + return path + + +def backup_dir(dir, ext='.bak'): + """Figure out the name of a directory to back up the given dir to + (adding .bak, .bak2, etc)""" + n = 1 + extension = ext + while os.path.exists(dir + extension): + n += 1 + extension = ext + str(n) + return dir + extension + + +def ask_path_exists(message, options): + for action in os.environ.get('PIP_EXISTS_ACTION', '').split(): + if action in options: + return action + return ask(message, options) + + +def ask(message, options): + """Ask the message interactively, with the given possible responses""" + while 1: + if os.environ.get('PIP_NO_INPUT'): + raise Exception( + 'No input was expected ($PIP_NO_INPUT set); question: %s' % + message + ) + response = input(message) + response = response.strip().lower() + if response not in options: + print( + 'Your response (%r) was not one of the expected responses: ' + '%s' % (response, ', '.join(options)) + ) + else: + return response + + +def format_size(bytes): + if bytes > 1000 * 1000: + return '%.1fMB' % (bytes / 1000.0 / 1000) + elif bytes > 10 * 1000: + return '%ikB' % (bytes / 1000) + elif bytes > 1000: + return '%.1fkB' % (bytes / 1000.0) + else: + return '%ibytes' % bytes + + +def is_installable_dir(path): + """Return True if `path` is a directory containing a setup.py file.""" + if not os.path.isdir(path): + return False + setup_py = os.path.join(path, 'setup.py') + if os.path.isfile(setup_py): + return True + return False + + +def is_svn_page(html): + """ + Returns true if the page appears to be the index page of an svn repository + """ + return (re.search(r'<title>[^<]*Revision \d+:', html) and + re.search(r'Powered by (?:<a[^>]*?>)?Subversion', html, re.I)) + + +def file_contents(filename): + with open(filename, 'rb') as fp: + return fp.read().decode('utf-8') + + +def read_chunks(file, size=io.DEFAULT_BUFFER_SIZE): + """Yield pieces of data from a file-like object until EOF.""" + while True: + chunk = file.read(size) + if not chunk: + break + yield chunk + + +def split_leading_dir(path): + path = path.lstrip('/').lstrip('\\') + if '/' in path and (('\\' in path and path.find('/') < path.find('\\')) or + '\\' not in path): + return path.split('/', 1) + elif '\\' in path: + return path.split('\\', 1) + else: + return path, '' + + +def has_leading_dir(paths): + """Returns true if all the paths have the same leading path name + (i.e., everything is in one subdirectory in an archive)""" + common_prefix = None + for path in paths: + prefix, rest = split_leading_dir(path) + if not prefix: + return False + elif common_prefix is None: + common_prefix = prefix + elif prefix != common_prefix: + return False + return True + + +def normalize_path(path, resolve_symlinks=True): + """ + Convert a path to its canonical, case-normalized, absolute version. + + """ + path = expanduser(path) + if resolve_symlinks: + path = os.path.realpath(path) + else: + path = os.path.abspath(path) + return os.path.normcase(path) + + +def splitext(path): + """Like os.path.splitext, but take off .tar too""" + base, ext = posixpath.splitext(path) + if base.lower().endswith('.tar'): + ext = base[-4:] + ext + base = base[:-4] + return base, ext + + +def renames(old, new): + """Like os.renames(), but handles renaming across devices.""" + # Implementation borrowed from os.renames(). + head, tail = os.path.split(new) + if head and tail and not os.path.exists(head): + os.makedirs(head) + + shutil.move(old, new) + + head, tail = os.path.split(old) + if head and tail: + try: + os.removedirs(head) + except OSError: + pass + + +def is_local(path): + """ + Return True if path is within sys.prefix, if we're running in a virtualenv. + + If we're not in a virtualenv, all paths are considered "local." + + """ + if not running_under_virtualenv(): + return True + return normalize_path(path).startswith(normalize_path(sys.prefix)) + + +def dist_is_local(dist): + """ + Return True if given Distribution object is installed locally + (i.e. within current virtualenv). + + Always True if we're not in a virtualenv. + + """ + return is_local(dist_location(dist)) + + +def dist_in_usersite(dist): + """ + Return True if given Distribution is installed in user site. + """ + norm_path = normalize_path(dist_location(dist)) + return norm_path.startswith(normalize_path(user_site)) + + +def dist_in_site_packages(dist): + """ + Return True if given Distribution is installed in + distutils.sysconfig.get_python_lib(). + """ + return normalize_path( + dist_location(dist) + ).startswith(normalize_path(site_packages)) + + +def dist_is_editable(dist): + """Is distribution an editable install?""" + for path_item in sys.path: + egg_link = os.path.join(path_item, dist.project_name + '.egg-link') + if os.path.isfile(egg_link): + return True + return False + + +def get_installed_distributions(local_only=True, + skip=stdlib_pkgs, + include_editables=True, + editables_only=False, + user_only=False): + """ + Return a list of installed Distribution objects. + + If ``local_only`` is True (default), only return installations + local to the current virtualenv, if in a virtualenv. + + ``skip`` argument is an iterable of lower-case project names to + ignore; defaults to stdlib_pkgs + + If ``editables`` is False, don't report editables. + + If ``editables_only`` is True , only report editables. + + If ``user_only`` is True , only report installations in the user + site directory. + + """ + if local_only: + local_test = dist_is_local + else: + def local_test(d): + return True + + if include_editables: + def editable_test(d): + return True + else: + def editable_test(d): + return not dist_is_editable(d) + + if editables_only: + def editables_only_test(d): + return dist_is_editable(d) + else: + def editables_only_test(d): + return True + + if user_only: + user_test = dist_in_usersite + else: + def user_test(d): + return True + + return [d for d in pkg_resources.working_set + if local_test(d) and + d.key not in skip and + editable_test(d) and + editables_only_test(d) and + user_test(d) + ] + + +def egg_link_path(dist): + """ + Return the path for the .egg-link file if it exists, otherwise, None. + + There's 3 scenarios: + 1) not in a virtualenv + try to find in site.USER_SITE, then site_packages + 2) in a no-global virtualenv + try to find in site_packages + 3) in a yes-global virtualenv + try to find in site_packages, then site.USER_SITE + (don't look in global location) + + For #1 and #3, there could be odd cases, where there's an egg-link in 2 + locations. + + This method will just return the first one found. + """ + sites = [] + if running_under_virtualenv(): + if virtualenv_no_global(): + sites.append(site_packages) + else: + sites.append(site_packages) + if user_site: + sites.append(user_site) + else: + if user_site: + sites.append(user_site) + sites.append(site_packages) + + for site in sites: + egglink = os.path.join(site, dist.project_name) + '.egg-link' + if os.path.isfile(egglink): + return egglink + + +def dist_location(dist): + """ + Get the site-packages location of this distribution. Generally + this is dist.location, except in the case of develop-installed + packages, where dist.location is the source code location, and we + want to know where the egg-link file is. + + """ + egg_link = egg_link_path(dist) + if egg_link: + return egg_link + return dist.location + + +def get_terminal_size(): + """Returns a tuple (x, y) representing the width(x) and the height(x) + in characters of the terminal window.""" + def ioctl_GWINSZ(fd): + try: + import fcntl + import termios + import struct + cr = struct.unpack( + 'hh', + fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234') + ) + except: + return None + if cr == (0, 0): + return None + return cr + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) + if not cr: + try: + fd = os.open(os.ctermid(), os.O_RDONLY) + cr = ioctl_GWINSZ(fd) + os.close(fd) + except: + pass + if not cr: + cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) + return int(cr[1]), int(cr[0]) + + +def current_umask(): + """Get the current umask which involves having to set it temporarily.""" + mask = os.umask(0) + os.umask(mask) + return mask + + +def unzip_file(filename, location, flatten=True): + """ + Unzip the file (with path `filename`) to the destination `location`. All + files are written based on system defaults and umask (i.e. permissions are + not preserved), except that regular file members with any execute + permissions (user, group, or world) have "chmod +x" applied after being + written. Note that for windows, any execute changes using os.chmod are + no-ops per the python docs. + """ + ensure_dir(location) + zipfp = open(filename, 'rb') + try: + zip = zipfile.ZipFile(zipfp, allowZip64=True) + leading = has_leading_dir(zip.namelist()) and flatten + for info in zip.infolist(): + name = info.filename + data = zip.read(name) + fn = name + if leading: + fn = split_leading_dir(name)[1] + fn = os.path.join(location, fn) + dir = os.path.dirname(fn) + if fn.endswith('/') or fn.endswith('\\'): + # A directory + ensure_dir(fn) + else: + ensure_dir(dir) + fp = open(fn, 'wb') + try: + fp.write(data) + finally: + fp.close() + mode = info.external_attr >> 16 + # if mode and regular file and any execute permissions for + # user/group/world? + if mode and stat.S_ISREG(mode) and mode & 0o111: + # make dest file have execute for user/group/world + # (chmod +x) no-op on windows per python docs + os.chmod(fn, (0o777 - current_umask() | 0o111)) + finally: + zipfp.close() + + +def untar_file(filename, location): + """ + Untar the file (with path `filename`) to the destination `location`. + All files are written based on system defaults and umask (i.e. permissions + are not preserved), except that regular file members with any execute + permissions (user, group, or world) have "chmod +x" applied after being + written. Note that for windows, any execute changes using os.chmod are + no-ops per the python docs. + """ + ensure_dir(location) + if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'): + mode = 'r:gz' + elif filename.lower().endswith(BZ2_EXTENSIONS): + mode = 'r:bz2' + elif filename.lower().endswith(XZ_EXTENSIONS): + mode = 'r:xz' + elif filename.lower().endswith('.tar'): + mode = 'r' + else: + logger.warning( + 'Cannot determine compression type for file %s', filename, + ) + mode = 'r:*' + tar = tarfile.open(filename, mode) + try: + # note: python<=2.5 doesn't seem to know about pax headers, filter them + leading = has_leading_dir([ + member.name for member in tar.getmembers() + if member.name != 'pax_global_header' + ]) + for member in tar.getmembers(): + fn = member.name + if fn == 'pax_global_header': + continue + if leading: + fn = split_leading_dir(fn)[1] + path = os.path.join(location, fn) + if member.isdir(): + ensure_dir(path) + elif member.issym(): + try: + tar._extract_member(member, path) + except Exception as exc: + # Some corrupt tar files seem to produce this + # (specifically bad symlinks) + logger.warning( + 'In the tar file %s the member %s is invalid: %s', + filename, member.name, exc, + ) + continue + else: + try: + fp = tar.extractfile(member) + except (KeyError, AttributeError) as exc: + # Some corrupt tar files seem to produce this + # (specifically bad symlinks) + logger.warning( + 'In the tar file %s the member %s is invalid: %s', + filename, member.name, exc, + ) + continue + ensure_dir(os.path.dirname(path)) + with open(path, 'wb') as destfp: + shutil.copyfileobj(fp, destfp) + fp.close() + # Update the timestamp (useful for cython compiled files) + tar.utime(member, path) + # member have any execute permissions for user/group/world? + if member.mode & 0o111: + # make dest file have execute for user/group/world + # no-op on windows per python docs + os.chmod(path, (0o777 - current_umask() | 0o111)) + finally: + tar.close() + + +def unpack_file(filename, location, content_type, link): + filename = os.path.realpath(filename) + if (content_type == 'application/zip' or + filename.lower().endswith(ZIP_EXTENSIONS) or + zipfile.is_zipfile(filename)): + unzip_file( + filename, + location, + flatten=not filename.endswith('.whl') + ) + elif (content_type == 'application/x-gzip' or + tarfile.is_tarfile(filename) or + filename.lower().endswith( + TAR_EXTENSIONS + BZ2_EXTENSIONS + XZ_EXTENSIONS)): + untar_file(filename, location) + elif (content_type and content_type.startswith('text/html') and + is_svn_page(file_contents(filename))): + # We don't really care about this + from pip.vcs.subversion import Subversion + Subversion('svn+' + link.url).unpack(location) + else: + # FIXME: handle? + # FIXME: magic signatures? + logger.critical( + 'Cannot unpack file %s (downloaded from %s, content-type: %s); ' + 'cannot detect archive format', + filename, location, content_type, + ) + raise InstallationError( + 'Cannot determine archive format of %s' % location + ) + + +def remove_tracebacks(output): + pattern = (r'(?:\W+File "(?:.*)", line (?:.*)\W+(?:.*)\W+\^\W+)?' + r'Syntax(?:Error|Warning): (?:.*)') + output = re.sub(pattern, '', output) + if PY2: + return output + # compileall.compile_dir() prints different messages to stdout + # in Python 3 + return re.sub(r"\*\*\* Error compiling (?:.*)", '', output) + + +def call_subprocess(cmd, show_stdout=True, cwd=None, + on_returncode='raise', + command_level=std_logging.DEBUG, command_desc=None, + extra_environ=None, spinner=None): + # This function's handling of subprocess output is confusing and I + # previously broke it terribly, so as penance I will write a long comment + # explaining things. + # + # The obvious thing that affects output is the show_stdout= + # kwarg. show_stdout=True means, let the subprocess write directly to our + # stdout. Even though it is nominally the default, it is almost never used + # inside pip (and should not be used in new code without a very good + # reason); as of 2016-02-22 it is only used in a few places inside the VCS + # wrapper code. Ideally we should get rid of it entirely, because it + # creates a lot of complexity here for a rarely used feature. + # + # Most places in pip set show_stdout=False. What this means is: + # - We connect the child stdout to a pipe, which we read. + # - By default, we hide the output but show a spinner -- unless the + # subprocess exits with an error, in which case we show the output. + # - If the --verbose option was passed (= loglevel is DEBUG), then we show + # the output unconditionally. (But in this case we don't want to show + # the output a second time if it turns out that there was an error.) + # + # stderr is always merged with stdout (even if show_stdout=True). + if show_stdout: + stdout = None + else: + stdout = subprocess.PIPE + if command_desc is None: + cmd_parts = [] + for part in cmd: + if ' ' in part or '\n' in part or '"' in part or "'" in part: + part = '"%s"' % part.replace('"', '\\"') + cmd_parts.append(part) + command_desc = ' '.join(cmd_parts) + logger.log(command_level, "Running command %s", command_desc) + env = os.environ.copy() + if extra_environ: + env.update(extra_environ) + try: + proc = subprocess.Popen( + cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout, + cwd=cwd, env=env) + except Exception as exc: + logger.critical( + "Error %s while executing command %s", exc, command_desc, + ) + raise + if stdout is not None: + all_output = [] + while True: + line = console_to_str(proc.stdout.readline()) + if not line: + break + line = line.rstrip() + all_output.append(line + '\n') + if logger.getEffectiveLevel() <= std_logging.DEBUG: + # Show the line immediately + logger.debug(line) + else: + # Update the spinner + if spinner is not None: + spinner.spin() + proc.wait() + if spinner is not None: + if proc.returncode: + spinner.finish("error") + else: + spinner.finish("done") + if proc.returncode: + if on_returncode == 'raise': + if (logger.getEffectiveLevel() > std_logging.DEBUG and + not show_stdout): + logger.info( + 'Complete output from command %s:', command_desc, + ) + logger.info( + ''.join(all_output) + + '\n----------------------------------------' + ) + raise InstallationError( + 'Command "%s" failed with error code %s in %s' + % (command_desc, proc.returncode, cwd)) + elif on_returncode == 'warn': + logger.warning( + 'Command "%s" had error code %s in %s', + command_desc, proc.returncode, cwd, + ) + elif on_returncode == 'ignore': + pass + else: + raise ValueError('Invalid value: on_returncode=%s' % + repr(on_returncode)) + if not show_stdout: + return remove_tracebacks(''.join(all_output)) + + +def read_text_file(filename): + """Return the contents of *filename*. + + Try to decode the file contents with utf-8, the preferred system encoding + (e.g., cp1252 on some Windows machines), and latin1, in that order. + Decoding a byte string with latin1 will never raise an error. In the worst + case, the returned string will contain some garbage characters. + + """ + with open(filename, 'rb') as fp: + data = fp.read() + + encodings = ['utf-8', locale.getpreferredencoding(False), 'latin1'] + for enc in encodings: + try: + data = data.decode(enc) + except UnicodeDecodeError: + continue + break + + assert type(data) != bytes # Latin1 should have worked. + return data + + +def _make_build_dir(build_dir): + os.makedirs(build_dir) + write_delete_marker_file(build_dir) + + +class FakeFile(object): + """Wrap a list of lines in an object with readline() to make + ConfigParser happy.""" + def __init__(self, lines): + self._gen = (l for l in lines) + + def readline(self): + try: + try: + return next(self._gen) + except NameError: + return self._gen.next() + except StopIteration: + return '' + + def __iter__(self): + return self._gen + + +class StreamWrapper(StringIO): + + @classmethod + def from_stream(cls, orig_stream): + cls.orig_stream = orig_stream + return cls() + + # compileall.compile_dir() needs stdout.encoding to print to stdout + @property + def encoding(self): + return self.orig_stream.encoding + + +@contextlib.contextmanager +def captured_output(stream_name): + """Return a context manager used by captured_stdout/stdin/stderr + that temporarily replaces the sys stream *stream_name* with a StringIO. + + Taken from Lib/support/__init__.py in the CPython repo. + """ + orig_stdout = getattr(sys, stream_name) + setattr(sys, stream_name, StreamWrapper.from_stream(orig_stdout)) + try: + yield getattr(sys, stream_name) + finally: + setattr(sys, stream_name, orig_stdout) + + +def captured_stdout(): + """Capture the output of sys.stdout: + + with captured_stdout() as stdout: + print('hello') + self.assertEqual(stdout.getvalue(), 'hello\n') + + Taken from Lib/support/__init__.py in the CPython repo. + """ + return captured_output('stdout') + + +class cached_property(object): + """A property that is only computed once per instance and then replaces + itself with an ordinary attribute. Deleting the attribute resets the + property. + + Source: https://github.com/bottlepy/bottle/blob/0.11.5/bottle.py#L175 + """ + + def __init__(self, func): + self.__doc__ = getattr(func, '__doc__') + self.func = func + + def __get__(self, obj, cls): + if obj is None: + # We're being accessed from the class itself, not from an object + return self + value = obj.__dict__[self.func.__name__] = self.func(obj) + return value + + +def get_installed_version(dist_name): + """Get the installed version of dist_name avoiding pkg_resources cache""" + # Create a requirement that we'll look for inside of setuptools. + req = pkg_resources.Requirement.parse(dist_name) + + # We want to avoid having this cached, so we need to construct a new + # working set each time. + working_set = pkg_resources.WorkingSet() + + # Get the installed distribution from our working set + dist = working_set.find(req) + + # Check to see if we got an installed distribution or not, if we did + # we want to return it's version. + return dist.version if dist else None + + +def consume(iterator): + """Consume an iterable at C speed.""" + deque(iterator, maxlen=0) diff --git a/pip/utils/appdirs.py b/pip/utils/appdirs.py new file mode 100644 index 00000000000..15ca3cd03e8 --- /dev/null +++ b/pip/utils/appdirs.py @@ -0,0 +1,224 @@ +""" +This code was taken from https://github.com/ActiveState/appdirs and modified +to suit our purposes. +""" +from __future__ import absolute_import + +import os +import sys + +from pip.compat import WINDOWS, expanduser + + +def user_cache_dir(appname): + r""" + Return full path to the user-specific cache dir for this application. + + "appname" is the name of application. + + Typical user cache directories are: + Mac OS X: ~/Library/Caches/<AppName> + Unix: ~/.cache/<AppName> (XDG default) + Windows: C:\Users\<username>\AppData\Local\<AppName>\Cache + + On Windows the only suggestion in the MSDN docs is that local settings go + in the `CSIDL_LOCAL_APPDATA` directory. This is identical to the + non-roaming app data dir (the default returned by `user_data_dir`). Apps + typically put cache data somewhere *under* the given dir here. Some + examples: + ...\Mozilla\Firefox\Profiles\<ProfileName>\Cache + ...\Acme\SuperApp\Cache\1.0 + + OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. + """ + if WINDOWS: + # Get the base path + path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) + + # Add our app name and Cache directory to it + path = os.path.join(path, appname, "Cache") + elif sys.platform == "darwin": + # Get the base path + path = expanduser("~/Library/Caches") + + # Add our app name to it + path = os.path.join(path, appname) + else: + # Get the base path + path = os.getenv("XDG_CACHE_HOME", expanduser("~/.cache")) + + # Add our app name to it + path = os.path.join(path, appname) + + return path + + +def user_data_dir(appname, roaming=False): + """ + Return full path to the user-specific data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> + for a discussion of issues. + + Typical user data directories are: + Mac OS X: ~/Library/Application Support/<AppName> + Unix: ~/.local/share/<AppName> # or in + $XDG_DATA_HOME, if defined + Win XP (not roaming): C:\Documents and Settings\<username>\ ... + ...Application Data\<AppName> + Win XP (roaming): C:\Documents and Settings\<username>\Local ... + ...Settings\Application Data\<AppName> + Win 7 (not roaming): C:\\Users\<username>\AppData\Local\<AppName> + Win 7 (roaming): C:\\Users\<username>\AppData\Roaming\<AppName> + + For Unix, we follow the XDG spec and support $XDG_DATA_HOME. + That means, by default "~/.local/share/<AppName>". + """ + if WINDOWS: + const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" + path = os.path.join(os.path.normpath(_get_win_folder(const)), appname) + elif sys.platform == "darwin": + path = os.path.join( + expanduser('~/Library/Application Support/'), + appname, + ) + else: + path = os.path.join( + os.getenv('XDG_DATA_HOME', expanduser("~/.local/share")), + appname, + ) + + return path + + +def user_config_dir(appname, roaming=True): + """Return full path to the user-specific config dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "roaming" (boolean, default True) can be set False to not use the + Windows roaming appdata directory. That means that for users on a + Windows network setup for roaming profiles, this user data will be + sync'd on login. See + <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> + for a discussion of issues. + + Typical user data directories are: + Mac OS X: same as user_data_dir + Unix: ~/.config/<AppName> + Win *: same as user_data_dir + + For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. + That means, by default "~/.config/<AppName>". + """ + if WINDOWS: + path = user_data_dir(appname, roaming=roaming) + elif sys.platform == "darwin": + path = user_data_dir(appname) + else: + path = os.getenv('XDG_CONFIG_HOME', expanduser("~/.config")) + path = os.path.join(path, appname) + + return path + + +# for the discussion regarding site_config_dirs locations +# see <https://github.com/pypa/pip/issues/1733> +def site_config_dirs(appname): + """Return a list of potential user-shared config dirs for this application. + + "appname" is the name of application. + + Typical user config directories are: + Mac OS X: /Library/Application Support/<AppName>/ + Unix: /etc or $XDG_CONFIG_DIRS[i]/<AppName>/ for each value in + $XDG_CONFIG_DIRS + Win XP: C:\Documents and Settings\All Users\Application ... + ...Data\<AppName>\ + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory + on Vista.) + Win 7: Hidden, but writeable on Win 7: + C:\ProgramData\<AppName>\ + """ + if WINDOWS: + path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) + pathlist = [os.path.join(path, appname)] + elif sys.platform == 'darwin': + pathlist = [os.path.join('/Library/Application Support', appname)] + else: + # try looking in $XDG_CONFIG_DIRS + xdg_config_dirs = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') + if xdg_config_dirs: + pathlist = [ + os.path.join(expanduser(x), appname) + for x in xdg_config_dirs.split(os.pathsep) + ] + else: + pathlist = [] + + # always look in /etc directly as well + pathlist.append('/etc') + + return pathlist + + +# -- Windows support functions -- + +def _get_win_folder_from_registry(csidl_name): + """ + This is a fallback technique at best. I'm not sure if using the + registry for this guarantees us the correct answer for all CSIDL_* + names. + """ + import _winreg + + shell_folder_name = { + "CSIDL_APPDATA": "AppData", + "CSIDL_COMMON_APPDATA": "Common AppData", + "CSIDL_LOCAL_APPDATA": "Local AppData", + }[csidl_name] + + key = _winreg.OpenKey( + _winreg.HKEY_CURRENT_USER, + r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" + ) + directory, _type = _winreg.QueryValueEx(key, shell_folder_name) + return directory + + +def _get_win_folder_with_ctypes(csidl_name): + csidl_const = { + "CSIDL_APPDATA": 26, + "CSIDL_COMMON_APPDATA": 35, + "CSIDL_LOCAL_APPDATA": 28, + }[csidl_name] + + buf = ctypes.create_unicode_buffer(1024) + ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + + # Downgrade to short path name if have highbit chars. See + # <http://bugs.activestate.com/show_bug.cgi?id=85099>. + has_high_char = False + for c in buf: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf2 = ctypes.create_unicode_buffer(1024) + if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + buf = buf2 + + return buf.value + +if WINDOWS: + try: + import ctypes + _get_win_folder = _get_win_folder_with_ctypes + except ImportError: + _get_win_folder = _get_win_folder_from_registry diff --git a/pip/utils/build.py b/pip/utils/build.py new file mode 100644 index 00000000000..fc65cfab3e0 --- /dev/null +++ b/pip/utils/build.py @@ -0,0 +1,42 @@ +from __future__ import absolute_import + +import os.path +import tempfile + +from pip.utils import rmtree + + +class BuildDirectory(object): + + def __init__(self, name=None, delete=None): + # If we were not given an explicit directory, and we were not given an + # explicit delete option, then we'll default to deleting. + if name is None and delete is None: + delete = True + + if name is None: + # We realpath here because some systems have their default tmpdir + # symlinked to another directory. This tends to confuse build + # scripts, so we canonicalize the path by traversing potential + # symlinks here. + name = os.path.realpath(tempfile.mkdtemp(prefix="pip-build-")) + # If we were not given an explicit directory, and we were not given + # an explicit delete option, then we'll default to deleting. + if delete is None: + delete = True + + self.name = name + self.delete = delete + + def __repr__(self): + return "<{} {!r}>".format(self.__class__.__name__, self.name) + + def __enter__(self): + return self.name + + def __exit__(self, exc, value, tb): + self.cleanup() + + def cleanup(self): + if self.delete: + rmtree(self.name) diff --git a/pip/utils/deprecation.py b/pip/utils/deprecation.py new file mode 100644 index 00000000000..74f621e0136 --- /dev/null +++ b/pip/utils/deprecation.py @@ -0,0 +1,76 @@ +""" +A module that implements tooling to enable easy warnings about deprecations. +""" +from __future__ import absolute_import + +import logging +import warnings + + +class PipDeprecationWarning(Warning): + pass + + +class Pending(object): + pass + + +class RemovedInPip9Warning(PipDeprecationWarning): + pass + + +class RemovedInPip10Warning(PipDeprecationWarning, Pending): + pass + + +class Python26DeprecationWarning(PipDeprecationWarning, Pending): + pass + + +# Warnings <-> Logging Integration + + +_warnings_showwarning = None + + +def _showwarning(message, category, filename, lineno, file=None, line=None): + if file is not None: + if _warnings_showwarning is not None: + _warnings_showwarning( + message, category, filename, lineno, file, line, + ) + else: + if issubclass(category, PipDeprecationWarning): + # We use a specially named logger which will handle all of the + # deprecation messages for pip. + logger = logging.getLogger("pip.deprecations") + + # This is purposely using the % formatter here instead of letting + # the logging module handle the interpolation. This is because we + # want it to appear as if someone typed this entire message out. + log_message = "DEPRECATION: %s" % message + + # PipDeprecationWarnings that are Pending still have at least 2 + # versions to go until they are removed so they can just be + # warnings. Otherwise, they will be removed in the very next + # version of pip. We want these to be more obvious so we use the + # ERROR logging level. + if issubclass(category, Pending): + logger.warning(log_message) + else: + logger.error(log_message) + else: + _warnings_showwarning( + message, category, filename, lineno, file, line, + ) + + +def install_warning_logger(): + # Enable our Deprecation Warnings + warnings.simplefilter("default", PipDeprecationWarning, append=True) + + global _warnings_showwarning + + if _warnings_showwarning is None: + _warnings_showwarning = warnings.showwarning + warnings.showwarning = _showwarning diff --git a/pip/utils/encoding.py b/pip/utils/encoding.py new file mode 100644 index 00000000000..24831686cf4 --- /dev/null +++ b/pip/utils/encoding.py @@ -0,0 +1,31 @@ +import codecs +import locale +import re + + +BOMS = [ + (codecs.BOM_UTF8, 'utf8'), + (codecs.BOM_UTF16, 'utf16'), + (codecs.BOM_UTF16_BE, 'utf16-be'), + (codecs.BOM_UTF16_LE, 'utf16-le'), + (codecs.BOM_UTF32, 'utf32'), + (codecs.BOM_UTF32_BE, 'utf32-be'), + (codecs.BOM_UTF32_LE, 'utf32-le'), +] + +ENCODING_RE = re.compile(b'coding[:=]\s*([-\w.]+)') + + +def auto_decode(data): + """Check a bytes string for a BOM to correctly detect the encoding + + Fallback to locale.getpreferredencoding(False) like open() on Python3""" + for bom, encoding in BOMS: + if data.startswith(bom): + return data[len(bom):].decode(encoding) + # Lets check the first two lines as in PEP263 + for line in data.split(b'\n')[:2]: + if line[0:1] == b'#' and ENCODING_RE.search(line): + encoding = ENCODING_RE.search(line).groups()[0].decode('ascii') + return data.decode(encoding) + return data.decode(locale.getpreferredencoding(False)) diff --git a/pip/utils/filesystem.py b/pip/utils/filesystem.py new file mode 100644 index 00000000000..25ad51660d4 --- /dev/null +++ b/pip/utils/filesystem.py @@ -0,0 +1,28 @@ +import os +import os.path + +from pip.compat import get_path_uid + + +def check_path_owner(path): + # If we don't have a way to check the effective uid of this process, then + # we'll just assume that we own the directory. + if not hasattr(os, "geteuid"): + return True + + previous = None + while path != previous: + if os.path.lexists(path): + # Check if path is writable by current user. + if os.geteuid() == 0: + # Special handling for root user in order to handle properly + # cases where users use sudo without -H flag. + try: + path_uid = get_path_uid(path) + except OSError: + return False + return path_uid == 0 + else: + return os.access(path, os.W_OK) + else: + previous, path = path, os.path.dirname(path) diff --git a/pip/utils/glibc.py b/pip/utils/glibc.py new file mode 100644 index 00000000000..7847885c4f9 --- /dev/null +++ b/pip/utils/glibc.py @@ -0,0 +1,81 @@ +from __future__ import absolute_import + +import re +import ctypes +import platform +import warnings + + +def glibc_version_string(): + "Returns glibc version string, or None if not using glibc." + + # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen + # manpage says, "If filename is NULL, then the returned handle is for the + # main program". This way we can let the linker do the work to figure out + # which libc our process is actually using. + process_namespace = ctypes.CDLL(None) + try: + gnu_get_libc_version = process_namespace.gnu_get_libc_version + except AttributeError: + # Symbol doesn't exist -> therefore, we are not linked to + # glibc. + return None + + # Call gnu_get_libc_version, which returns a string like "2.5" + gnu_get_libc_version.restype = ctypes.c_char_p + version_str = gnu_get_libc_version() + # py2 / py3 compatibility: + if not isinstance(version_str, str): + version_str = version_str.decode("ascii") + + return version_str + + +# Separated out from have_compatible_glibc for easier unit testing +def check_glibc_version(version_str, required_major, minimum_minor): + # Parse string and check against requested version. + # + # We use a regexp instead of str.split because we want to discard any + # random junk that might come after the minor version -- this might happen + # in patched/forked versions of glibc (e.g. Linaro's version of glibc + # uses version strings like "2.20-2014.11"). See gh-3588. + m = re.match(r"(?P<major>[0-9]+)\.(?P<minor>[0-9]+)", version_str) + if not m: + warnings.warn("Expected glibc version with 2 components major.minor," + " got: %s" % version_str, RuntimeWarning) + return False + return (int(m.group("major")) == required_major and + int(m.group("minor")) >= minimum_minor) + + +def have_compatible_glibc(required_major, minimum_minor): + version_str = glibc_version_string() + if version_str is None: + return False + return check_glibc_version(version_str, required_major, minimum_minor) + + +# platform.libc_ver regularly returns completely nonsensical glibc +# versions. E.g. on my computer, platform says: +# +# ~$ python2.7 -c 'import platform; print(platform.libc_ver())' +# ('glibc', '2.7') +# ~$ python3.5 -c 'import platform; print(platform.libc_ver())' +# ('glibc', '2.9') +# +# But the truth is: +# +# ~$ ldd --version +# ldd (Debian GLIBC 2.22-11) 2.22 +# +# This is unfortunate, because it means that the linehaul data on libc +# versions that was generated by pip 8.1.2 and earlier is useless and +# misleading. Solution: instead of using platform, use our code that actually +# works. +def libc_ver(): + glibc_version = glibc_version_string() + if glibc_version is None: + # For non-glibc platforms, fall back on platform.libc_ver + return platform.libc_ver() + else: + return ("glibc", glibc_version) diff --git a/pip/utils/hashes.py b/pip/utils/hashes.py new file mode 100644 index 00000000000..960297007ae --- /dev/null +++ b/pip/utils/hashes.py @@ -0,0 +1,92 @@ +from __future__ import absolute_import + +import hashlib + +from pip.exceptions import HashMismatch, HashMissing, InstallationError +from pip.utils import read_chunks +from pip._vendor.six import iteritems, iterkeys, itervalues + + +# The recommended hash algo of the moment. Change this whenever the state of +# the art changes; it won't hurt backward compatibility. +FAVORITE_HASH = 'sha256' + + +# Names of hashlib algorithms allowed by the --hash option and ``pip hash`` +# Currently, those are the ones at least as collision-resistant as sha256. +STRONG_HASHES = ['sha256', 'sha384', 'sha512'] + + +class Hashes(object): + """A wrapper that builds multiple hashes at once and checks them against + known-good values + + """ + def __init__(self, hashes=None): + """ + :param hashes: A dict of algorithm names pointing to lists of allowed + hex digests + """ + self._allowed = {} if hashes is None else hashes + + def check_against_chunks(self, chunks): + """Check good hashes against ones built from iterable of chunks of + data. + + Raise HashMismatch if none match. + + """ + gots = {} + for hash_name in iterkeys(self._allowed): + try: + gots[hash_name] = hashlib.new(hash_name) + except (ValueError, TypeError): + raise InstallationError('Unknown hash name: %s' % hash_name) + + for chunk in chunks: + for hash in itervalues(gots): + hash.update(chunk) + + for hash_name, got in iteritems(gots): + if got.hexdigest() in self._allowed[hash_name]: + return + self._raise(gots) + + def _raise(self, gots): + raise HashMismatch(self._allowed, gots) + + def check_against_file(self, file): + """Check good hashes against a file-like object + + Raise HashMismatch if none match. + + """ + return self.check_against_chunks(read_chunks(file)) + + def check_against_path(self, path): + with open(path, 'rb') as file: + return self.check_against_file(file) + + def __nonzero__(self): + """Return whether I know any known-good hashes.""" + return bool(self._allowed) + + def __bool__(self): + return self.__nonzero__() + + +class MissingHashes(Hashes): + """A workalike for Hashes used when we're missing a hash for a requirement + + It computes the actual hash of the requirement and raises a HashMissing + exception showing it to the user. + + """ + def __init__(self): + """Don't offer the ``hashes`` kwarg.""" + # Pass our favorite hash in to generate a "gotten hash". With the + # empty list, it will never match, so an error will always raise. + super(MissingHashes, self).__init__(hashes={FAVORITE_HASH: []}) + + def _raise(self, gots): + raise HashMissing(gots[FAVORITE_HASH].hexdigest()) diff --git a/pip/utils/logging.py b/pip/utils/logging.py new file mode 100644 index 00000000000..1c1053abfb4 --- /dev/null +++ b/pip/utils/logging.py @@ -0,0 +1,130 @@ +from __future__ import absolute_import + +import contextlib +import logging +import logging.handlers +import os + +try: + import threading +except ImportError: + import dummy_threading as threading + +from pip.compat import WINDOWS +from pip.utils import ensure_dir + +try: + from pip._vendor import colorama +# Lots of different errors can come from this, including SystemError and +# ImportError. +except Exception: + colorama = None + + +_log_state = threading.local() +_log_state.indentation = 0 + + +@contextlib.contextmanager +def indent_log(num=2): + """ + A context manager which will cause the log output to be indented for any + log messages emitted inside it. + """ + _log_state.indentation += num + try: + yield + finally: + _log_state.indentation -= num + + +def get_indentation(): + return getattr(_log_state, 'indentation', 0) + + +class IndentingFormatter(logging.Formatter): + + def format(self, record): + """ + Calls the standard formatter, but will indent all of the log messages + by our current indentation level. + """ + formatted = logging.Formatter.format(self, record) + formatted = "".join([ + (" " * get_indentation()) + line + for line in formatted.splitlines(True) + ]) + return formatted + + +def _color_wrap(*colors): + def wrapped(inp): + return "".join(list(colors) + [inp, colorama.Style.RESET_ALL]) + return wrapped + + +class ColorizedStreamHandler(logging.StreamHandler): + + # Don't build up a list of colors if we don't have colorama + if colorama: + COLORS = [ + # This needs to be in order from highest logging level to lowest. + (logging.ERROR, _color_wrap(colorama.Fore.RED)), + (logging.WARNING, _color_wrap(colorama.Fore.YELLOW)), + ] + else: + COLORS = [] + + def __init__(self, stream=None): + logging.StreamHandler.__init__(self, stream) + + if WINDOWS and colorama: + self.stream = colorama.AnsiToWin32(self.stream) + + def should_color(self): + # Don't colorize things if we do not have colorama + if not colorama: + return False + + real_stream = ( + self.stream if not isinstance(self.stream, colorama.AnsiToWin32) + else self.stream.wrapped + ) + + # If the stream is a tty we should color it + if hasattr(real_stream, "isatty") and real_stream.isatty(): + return True + + # If we have an ASNI term we should color it + if os.environ.get("TERM") == "ANSI": + return True + + # If anything else we should not color it + return False + + def format(self, record): + msg = logging.StreamHandler.format(self, record) + + if self.should_color(): + for level, color in self.COLORS: + if record.levelno >= level: + msg = color(msg) + break + + return msg + + +class BetterRotatingFileHandler(logging.handlers.RotatingFileHandler): + + def _open(self): + ensure_dir(os.path.dirname(self.baseFilename)) + return logging.handlers.RotatingFileHandler._open(self) + + +class MaxLevelFilter(logging.Filter): + + def __init__(self, level): + self.level = level + + def filter(self, record): + return record.levelno < self.level diff --git a/pip/utils/outdated.py b/pip/utils/outdated.py new file mode 100644 index 00000000000..2164cc3cc2f --- /dev/null +++ b/pip/utils/outdated.py @@ -0,0 +1,162 @@ +from __future__ import absolute_import + +import datetime +import json +import logging +import os.path +import sys + +from pip._vendor import lockfile +from pip._vendor.packaging import version as packaging_version + +from pip.compat import total_seconds, WINDOWS +from pip.models import PyPI +from pip.locations import USER_CACHE_DIR, running_under_virtualenv +from pip.utils import ensure_dir, get_installed_version +from pip.utils.filesystem import check_path_owner + + +SELFCHECK_DATE_FMT = "%Y-%m-%dT%H:%M:%SZ" + + +logger = logging.getLogger(__name__) + + +class VirtualenvSelfCheckState(object): + def __init__(self): + self.statefile_path = os.path.join(sys.prefix, "pip-selfcheck.json") + + # Load the existing state + try: + with open(self.statefile_path) as statefile: + self.state = json.load(statefile) + except (IOError, ValueError): + self.state = {} + + def save(self, pypi_version, current_time): + # Attempt to write out our version check file + with open(self.statefile_path, "w") as statefile: + json.dump( + { + "last_check": current_time.strftime(SELFCHECK_DATE_FMT), + "pypi_version": pypi_version, + }, + statefile, + sort_keys=True, + separators=(",", ":") + ) + + +class GlobalSelfCheckState(object): + def __init__(self): + self.statefile_path = os.path.join(USER_CACHE_DIR, "selfcheck.json") + + # Load the existing state + try: + with open(self.statefile_path) as statefile: + self.state = json.load(statefile)[sys.prefix] + except (IOError, ValueError, KeyError): + self.state = {} + + def save(self, pypi_version, current_time): + # Check to make sure that we own the directory + if not check_path_owner(os.path.dirname(self.statefile_path)): + return + + # Now that we've ensured the directory is owned by this user, we'll go + # ahead and make sure that all our directories are created. + ensure_dir(os.path.dirname(self.statefile_path)) + + # Attempt to write out our version check file + with lockfile.LockFile(self.statefile_path): + if os.path.exists(self.statefile_path): + with open(self.statefile_path) as statefile: + state = json.load(statefile) + else: + state = {} + + state[sys.prefix] = { + "last_check": current_time.strftime(SELFCHECK_DATE_FMT), + "pypi_version": pypi_version, + } + + with open(self.statefile_path, "w") as statefile: + json.dump(state, statefile, sort_keys=True, + separators=(",", ":")) + + +def load_selfcheck_statefile(): + if running_under_virtualenv(): + return VirtualenvSelfCheckState() + else: + return GlobalSelfCheckState() + + +def pip_version_check(session): + """Check for an update for pip. + + Limit the frequency of checks to once per week. State is stored either in + the active virtualenv or in the user's USER_CACHE_DIR keyed off the prefix + of the pip script path. + """ + installed_version = get_installed_version("pip") + if installed_version is None: + return + + pip_version = packaging_version.parse(installed_version) + pypi_version = None + + try: + state = load_selfcheck_statefile() + + current_time = datetime.datetime.utcnow() + # Determine if we need to refresh the state + if "last_check" in state.state and "pypi_version" in state.state: + last_check = datetime.datetime.strptime( + state.state["last_check"], + SELFCHECK_DATE_FMT + ) + if total_seconds(current_time - last_check) < 7 * 24 * 60 * 60: + pypi_version = state.state["pypi_version"] + + # Refresh the version if we need to or just see if we need to warn + if pypi_version is None: + resp = session.get( + PyPI.pip_json_url, + headers={"Accept": "application/json"}, + ) + resp.raise_for_status() + pypi_version = [ + v for v in sorted( + list(resp.json()["releases"]), + key=packaging_version.parse, + ) + if not packaging_version.parse(v).is_prerelease + ][-1] + + # save that we've performed a check + state.save(pypi_version, current_time) + + remote_version = packaging_version.parse(pypi_version) + + # Determine if our pypi_version is older + if (pip_version < remote_version and + pip_version.base_version != remote_version.base_version): + # Advise "python -m pip" on Windows to avoid issues + # with overwriting pip.exe. + if WINDOWS: + pip_cmd = "python -m pip" + else: + pip_cmd = "pip" + logger.warning( + "You are using pip version %s, however version %s is " + "available.\nYou should consider upgrading via the " + "'%s install --upgrade pip' command.", + pip_version, pypi_version, pip_cmd + ) + + except Exception: + logger.debug( + "There was an error checking the latest version of pip", + exc_info=True, + ) diff --git a/pip/utils/setuptools_build.py b/pip/utils/setuptools_build.py new file mode 100644 index 00000000000..03973e976ca --- /dev/null +++ b/pip/utils/setuptools_build.py @@ -0,0 +1,8 @@ +# Shim to wrap setup.py invocation with setuptools +SETUPTOOLS_SHIM = ( + "import setuptools, tokenize;__file__=%r;" + "f=getattr(tokenize, 'open', open)(__file__);" + "code=f.read().replace('\\r\\n', '\\n');" + "f.close();" + "exec(compile(code, __file__, 'exec'))" +) diff --git a/pip/utils/ui.py b/pip/utils/ui.py new file mode 100644 index 00000000000..bba73e3b133 --- /dev/null +++ b/pip/utils/ui.py @@ -0,0 +1,344 @@ +from __future__ import absolute_import +from __future__ import division + +import itertools +import sys +from signal import signal, SIGINT, default_int_handler +import time +import contextlib +import logging + +from pip.compat import WINDOWS +from pip.utils import format_size +from pip.utils.logging import get_indentation +from pip._vendor import six +from pip._vendor.progress.bar import Bar, IncrementalBar +from pip._vendor.progress.helpers import (WritelnMixin, + HIDE_CURSOR, SHOW_CURSOR) +from pip._vendor.progress.spinner import Spinner + +try: + from pip._vendor import colorama +# Lots of different errors can come from this, including SystemError and +# ImportError. +except Exception: + colorama = None + +logger = logging.getLogger(__name__) + + +def _select_progress_class(preferred, fallback): + encoding = getattr(preferred.file, "encoding", None) + + # If we don't know what encoding this file is in, then we'll just assume + # that it doesn't support unicode and use the ASCII bar. + if not encoding: + return fallback + + # Collect all of the possible characters we want to use with the preferred + # bar. + characters = [ + getattr(preferred, "empty_fill", six.text_type()), + getattr(preferred, "fill", six.text_type()), + ] + characters += list(getattr(preferred, "phases", [])) + + # Try to decode the characters we're using for the bar using the encoding + # of the given file, if this works then we'll assume that we can use the + # fancier bar and if not we'll fall back to the plaintext bar. + try: + six.text_type().join(characters).encode(encoding) + except UnicodeEncodeError: + return fallback + else: + return preferred + + +_BaseBar = _select_progress_class(IncrementalBar, Bar) + + +class InterruptibleMixin(object): + """ + Helper to ensure that self.finish() gets called on keyboard interrupt. + + This allows downloads to be interrupted without leaving temporary state + (like hidden cursors) behind. + + This class is similar to the progress library's existing SigIntMixin + helper, but as of version 1.2, that helper has the following problems: + + 1. It calls sys.exit(). + 2. It discards the existing SIGINT handler completely. + 3. It leaves its own handler in place even after an uninterrupted finish, + which will have unexpected delayed effects if the user triggers an + unrelated keyboard interrupt some time after a progress-displaying + download has already completed, for example. + """ + + def __init__(self, *args, **kwargs): + """ + Save the original SIGINT handler for later. + """ + super(InterruptibleMixin, self).__init__(*args, **kwargs) + + self.original_handler = signal(SIGINT, self.handle_sigint) + + # If signal() returns None, the previous handler was not installed from + # Python, and we cannot restore it. This probably should not happen, + # but if it does, we must restore something sensible instead, at least. + # The least bad option should be Python's default SIGINT handler, which + # just raises KeyboardInterrupt. + if self.original_handler is None: + self.original_handler = default_int_handler + + def finish(self): + """ + Restore the original SIGINT handler after finishing. + + This should happen regardless of whether the progress display finishes + normally, or gets interrupted. + """ + super(InterruptibleMixin, self).finish() + signal(SIGINT, self.original_handler) + + def handle_sigint(self, signum, frame): + """ + Call self.finish() before delegating to the original SIGINT handler. + + This handler should only be in place while the progress display is + active. + """ + self.finish() + self.original_handler(signum, frame) + + +class DownloadProgressMixin(object): + + def __init__(self, *args, **kwargs): + super(DownloadProgressMixin, self).__init__(*args, **kwargs) + self.message = (" " * (get_indentation() + 2)) + self.message + + @property + def downloaded(self): + return format_size(self.index) + + @property + def download_speed(self): + # Avoid zero division errors... + if self.avg == 0.0: + return "..." + return format_size(1 / self.avg) + "/s" + + @property + def pretty_eta(self): + if self.eta: + return "eta %s" % self.eta_td + return "" + + def iter(self, it, n=1): + for x in it: + yield x + self.next(n) + self.finish() + + +class WindowsMixin(object): + + def __init__(self, *args, **kwargs): + # The Windows terminal does not support the hide/show cursor ANSI codes + # even with colorama. So we'll ensure that hide_cursor is False on + # Windows. + # This call neds to go before the super() call, so that hide_cursor + # is set in time. The base progress bar class writes the "hide cursor" + # code to the terminal in its init, so if we don't set this soon + # enough, we get a "hide" with no corresponding "show"... + if WINDOWS and self.hide_cursor: + self.hide_cursor = False + + super(WindowsMixin, self).__init__(*args, **kwargs) + + # Check if we are running on Windows and we have the colorama module, + # if we do then wrap our file with it. + if WINDOWS and colorama: + self.file = colorama.AnsiToWin32(self.file) + # The progress code expects to be able to call self.file.isatty() + # but the colorama.AnsiToWin32() object doesn't have that, so we'll + # add it. + self.file.isatty = lambda: self.file.wrapped.isatty() + # The progress code expects to be able to call self.file.flush() + # but the colorama.AnsiToWin32() object doesn't have that, so we'll + # add it. + self.file.flush = lambda: self.file.wrapped.flush() + + +class DownloadProgressBar(WindowsMixin, InterruptibleMixin, + DownloadProgressMixin, _BaseBar): + + file = sys.stdout + message = "%(percent)d%%" + suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s" + + +class DownloadProgressSpinner(WindowsMixin, InterruptibleMixin, + DownloadProgressMixin, WritelnMixin, Spinner): + + file = sys.stdout + suffix = "%(downloaded)s %(download_speed)s" + + def next_phase(self): + if not hasattr(self, "_phaser"): + self._phaser = itertools.cycle(self.phases) + return next(self._phaser) + + def update(self): + message = self.message % self + phase = self.next_phase() + suffix = self.suffix % self + line = ''.join([ + message, + " " if message else "", + phase, + " " if suffix else "", + suffix, + ]) + + self.writeln(line) + + +################################################################ +# Generic "something is happening" spinners +# +# We don't even try using progress.spinner.Spinner here because it's actually +# simpler to reimplement from scratch than to coerce their code into doing +# what we need. +################################################################ + +@contextlib.contextmanager +def hidden_cursor(file): + # The Windows terminal does not support the hide/show cursor ANSI codes, + # even via colorama. So don't even try. + if WINDOWS: + yield + # We don't want to clutter the output with control characters if we're + # writing to a file, or if the user is running with --quiet. + # See https://github.com/pypa/pip/issues/3418 + elif not file.isatty() or logger.getEffectiveLevel() > logging.INFO: + yield + else: + file.write(HIDE_CURSOR) + try: + yield + finally: + file.write(SHOW_CURSOR) + + +class RateLimiter(object): + def __init__(self, min_update_interval_seconds): + self._min_update_interval_seconds = min_update_interval_seconds + self._last_update = 0 + + def ready(self): + now = time.time() + delta = now - self._last_update + return delta >= self._min_update_interval_seconds + + def reset(self): + self._last_update = time.time() + + +class InteractiveSpinner(object): + def __init__(self, message, file=None, spin_chars="-\\|/", + # Empirically, 8 updates/second looks nice + min_update_interval_seconds=0.125): + self._message = message + if file is None: + file = sys.stdout + self._file = file + self._rate_limiter = RateLimiter(min_update_interval_seconds) + self._finished = False + + self._spin_cycle = itertools.cycle(spin_chars) + + self._file.write(" " * get_indentation() + self._message + " ... ") + self._width = 0 + + def _write(self, status): + assert not self._finished + # Erase what we wrote before by backspacing to the beginning, writing + # spaces to overwrite the old text, and then backspacing again + backup = "\b" * self._width + self._file.write(backup + " " * self._width + backup) + # Now we have a blank slate to add our status + self._file.write(status) + self._width = len(status) + self._file.flush() + self._rate_limiter.reset() + + def spin(self): + if self._finished: + return + if not self._rate_limiter.ready(): + return + self._write(next(self._spin_cycle)) + + def finish(self, final_status): + if self._finished: + return + self._write(final_status) + self._file.write("\n") + self._file.flush() + self._finished = True + + +# Used for dumb terminals, non-interactive installs (no tty), etc. +# We still print updates occasionally (once every 60 seconds by default) to +# act as a keep-alive for systems like Travis-CI that take lack-of-output as +# an indication that a task has frozen. +class NonInteractiveSpinner(object): + def __init__(self, message, min_update_interval_seconds=60): + self._message = message + self._finished = False + self._rate_limiter = RateLimiter(min_update_interval_seconds) + self._update("started") + + def _update(self, status): + assert not self._finished + self._rate_limiter.reset() + logger.info("%s: %s", self._message, status) + + def spin(self): + if self._finished: + return + if not self._rate_limiter.ready(): + return + self._update("still running...") + + def finish(self, final_status): + if self._finished: + return + self._update("finished with status '%s'" % (final_status,)) + self._finished = True + + +@contextlib.contextmanager +def open_spinner(message): + # Interactive spinner goes directly to sys.stdout rather than being routed + # through the logging system, but it acts like it has level INFO, + # i.e. it's only displayed if we're at level INFO or better. + # Non-interactive spinner goes through the logging system, so it is always + # in sync with logging configuration. + if sys.stdout.isatty() and logger.getEffectiveLevel() <= logging.INFO: + spinner = InteractiveSpinner(message) + else: + spinner = NonInteractiveSpinner(message) + try: + with hidden_cursor(sys.stdout): + yield spinner + except KeyboardInterrupt: + spinner.finish("canceled") + raise + except Exception: + spinner.finish("error") + raise + else: + spinner.finish("done") diff --git a/pip/vcs/__init__.py b/pip/vcs/__init__.py index a56dd202bc0..e0da09dc289 100644 --- a/pip/vcs/__init__.py +++ b/pip/vcs/__init__.py @@ -1,27 +1,35 @@ """Handles all VCS (version control) support""" +from __future__ import absolute_import +import errno +import logging import os import shutil -from pip.backwardcompat import urlparse, urllib -from pip.log import logger -from pip.util import (display_path, backup_dir, find_command, - rmtree, ask_path_exists) +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip.exceptions import BadCommand +from pip.utils import (display_path, backup_dir, call_subprocess, + rmtree, ask_path_exists) __all__ = ['vcs', 'get_src_requirement'] +logger = logging.getLogger(__name__) + + class VcsSupport(object): _registry = {} schemes = ['ssh', 'git', 'hg', 'bzr', 'sftp', 'svn'] def __init__(self): - # Register more schemes with urlparse for various version control systems - urlparse.uses_netloc.extend(self.schemes) + # Register more schemes with urlparse for various version control + # systems + urllib_parse.uses_netloc.extend(self.schemes) # Python >= 2.7.4, 3.3 doesn't have uses_fragment - if getattr(urlparse, 'uses_fragment', None): - urlparse.uses_fragment.extend(self.schemes) + if getattr(urllib_parse, 'uses_fragment', None): + urllib_parse.uses_fragment.extend(self.schemes) super(VcsSupport, self).__init__() def __iter__(self): @@ -44,10 +52,11 @@ def all_schemes(self): def register(self, cls): if not hasattr(cls, 'name'): - logger.warn('Cannot register VCS %s' % cls.__name__) + logger.warning('Cannot register VCS %s', cls.__name__) return if cls.name not in self._registry: self._registry[cls.name] = cls + logger.debug('Registered VCS backend: %s', cls.name) def unregister(self, cls=None, name=None): if name in self._registry: @@ -55,7 +64,7 @@ def unregister(self, cls=None, name=None): elif cls in self._registry.values(): del self._registry[cls.name] else: - logger.warn('Cannot unregister because no class or name given') + logger.warning('Cannot unregister because no class or name given') def get_backend_name(self, location): """ @@ -63,8 +72,9 @@ def get_backend_name(self, location): location, e.g. vcs.get_backend_name('/path/to/vcs/checkout') """ for vc_type in self._registry.values(): - path = os.path.join(location, vc_type.dirname) - if os.path.exists(path): + if vc_type.controls_location(location): + logger.debug('Determine that %s uses VCS: %s', + location, vc_type.name) return vc_type.name return None @@ -86,31 +96,33 @@ def get_backend_from_location(self, location): class VersionControl(object): name = '' dirname = '' + # List of supported schemes for this Version Control + schemes = () def __init__(self, url=None, *args, **kwargs): self.url = url - self._cmd = None super(VersionControl, self).__init__(*args, **kwargs) - def _filter(self, line): - return (logger.INFO, line) - def _is_local_repository(self, repo): """ posix absolute paths start with os.path.sep, - win32 ones ones start with drive (like c:\\folder) + win32 ones start with drive (like c:\\folder) """ drive, tail = os.path.splitdrive(repo) return repo.startswith(os.path.sep) or drive - @property - def cmd(self): - if self._cmd is not None: - return self._cmd - command = find_command(self.name) - logger.info('Found command %r at %r' % (self.name, command)) - self._cmd = command - return command + # See issue #1083 for why this method was introduced: + # https://github.com/pypa/pip/issues/1083 + def translate_egg_surname(self, surname): + # For example, Django has branches of the form "stable/1.7.x". + return surname.replace('/', '_') + + def export(self, location): + """ + Export the repository at the url to the destination location + i.e. only download the files, without vcs informations + """ + raise NotImplementedError def get_url_rev(self): """ @@ -118,30 +130,33 @@ def get_url_rev(self): repository URL """ error_message = ( - "Sorry, '%s' is a malformed VCS url. " - "The format is <vcs>+<protocol>://<url>, " - "e.g. svn+http://myrepo/svn/MyApp#egg=MyApp") + "Sorry, '%s' is a malformed VCS url. " + "The format is <vcs>+<protocol>://<url>, " + "e.g. svn+http://myrepo/svn/MyApp#egg=MyApp" + ) assert '+' in self.url, error_message % self.url url = self.url.split('+', 1)[1] - scheme, netloc, path, query, frag = urlparse.urlsplit(url) + scheme, netloc, path, query, frag = urllib_parse.urlsplit(url) rev = None if '@' in path: path, rev = path.rsplit('@', 1) - url = urlparse.urlunsplit((scheme, netloc, path, query, '')) + url = urllib_parse.urlunsplit((scheme, netloc, path, query, '')) return url, rev def get_info(self, location): """ Returns (url, revision), where both are strings """ - assert not location.rstrip('/').endswith(self.dirname), 'Bad directory: %s' % location + assert not location.rstrip('/').endswith(self.dirname), \ + 'Bad directory: %s' % location return self.get_url(location), self.get_revision(location) def normalize_url(self, url): """ - Normalize a URL for comparison by unquoting it and removing any trailing slash. + Normalize a URL for comparison by unquoting it and removing any + trailing slash. """ - return urllib.unquote(url).rstrip('/') + return urllib_parse.unquote(url).rstrip('/') def compare_urls(self, url1, url2): """ @@ -149,14 +164,6 @@ def compare_urls(self, url1, url2): """ return (self.normalize_url(url1) == self.normalize_url(url2)) - def parse_vcs_bundle_file(self, content): - """ - Takes the contents of the bundled text file that explains how to revert - the stripped off version control data of the given package and returns - the URL and revision of it. - """ - raise NotImplementedError - def obtain(self, dest): """ Called when installing or updating an editable package, takes the @@ -168,7 +175,7 @@ def switch(self, dest, url, rev_options): """ Switch the repo at ``dest`` to point to ``URL``. """ - raise NotImplemented + raise NotImplementedError def update(self, dest, rev_options): """ @@ -176,6 +183,13 @@ def update(self, dest, rev_options): """ raise NotImplementedError + def check_version(self, dest, rev_options): + """ + Return True if the version is identical to what exists and + doesn't need to be updated. + """ + raise NotImplementedError + def check_destination(self, dest, url, rev_options, rev_display): """ Prepare a location to receive a checkout/clone. @@ -190,62 +204,160 @@ def check_destination(self, dest, url, rev_options, rev_display): if os.path.exists(os.path.join(dest, self.dirname)): existing_url = self.get_url(dest) if self.compare_urls(existing_url, url): - logger.info('%s in %s exists, and has correct URL (%s)' % - (self.repo_name.title(), display_path(dest), - url)) - logger.notify('Updating %s %s%s' % - (display_path(dest), self.repo_name, - rev_display)) - self.update(dest, rev_options) + logger.debug( + '%s in %s exists, and has correct URL (%s)', + self.repo_name.title(), + display_path(dest), + url, + ) + if not self.check_version(dest, rev_options): + logger.info( + 'Updating %s %s%s', + display_path(dest), + self.repo_name, + rev_display, + ) + self.update(dest, rev_options) + else: + logger.info( + 'Skipping because already up-to-date.') else: - logger.warn('%s %s in %s exists with URL %s' % - (self.name, self.repo_name, - display_path(dest), existing_url)) + logger.warning( + '%s %s in %s exists with URL %s', + self.name, + self.repo_name, + display_path(dest), + existing_url, + ) prompt = ('(s)witch, (i)gnore, (w)ipe, (b)ackup ', ('s', 'i', 'w', 'b')) else: - logger.warn('Directory %s already exists, ' - 'and is not a %s %s.' % - (dest, self.name, self.repo_name)) + logger.warning( + 'Directory %s already exists, and is not a %s %s.', + dest, + self.name, + self.repo_name, + ) prompt = ('(i)gnore, (w)ipe, (b)ackup ', ('i', 'w', 'b')) if prompt: - logger.warn('The plan is to install the %s repository %s' % - (self.name, url)) + logger.warning( + 'The plan is to install the %s repository %s', + self.name, + url, + ) response = ask_path_exists('What to do? %s' % prompt[0], prompt[1]) if response == 's': - logger.notify('Switching %s %s to %s%s' % - (self.repo_name, display_path(dest), url, - rev_display)) + logger.info( + 'Switching %s %s to %s%s', + self.repo_name, + display_path(dest), + url, + rev_display, + ) self.switch(dest, url, rev_options) elif response == 'i': # do nothing pass elif response == 'w': - logger.warn('Deleting %s' % display_path(dest)) + logger.warning('Deleting %s', display_path(dest)) rmtree(dest) checkout = True elif response == 'b': dest_dir = backup_dir(dest) - logger.warn('Backing up %s to %s' - % (display_path(dest), dest_dir)) + logger.warning( + 'Backing up %s to %s', display_path(dest), dest_dir, + ) shutil.move(dest, dest_dir) checkout = True return checkout def unpack(self, location): + """ + Clean up current location and download the url repository + (and vcs infos) into location + """ if os.path.exists(location): rmtree(location) self.obtain(location) - def get_src_requirement(self, dist, location, find_tags=False): + def get_src_requirement(self, dist, location): + """ + Return a string representing the requirement needed to + redownload the files currently present in location, something + like: + {repository_url}@{revision}#egg={project_name}-{version_identifier} + """ + raise NotImplementedError + + def get_url(self, location): + """ + Return the url used at location + Used in get_info or check_destination + """ + raise NotImplementedError + + def get_revision(self, location): + """ + Return the current revision of the files at location + Used in get_info + """ raise NotImplementedError + def run_command(self, cmd, show_stdout=True, cwd=None, + on_returncode='raise', + command_level=logging.DEBUG, command_desc=None, + extra_environ=None, spinner=None): + """ + Run a VCS subcommand + This is simply a wrapper around call_subprocess that adds the VCS + command name, and checks that the VCS is available + """ + cmd = [self.name] + cmd + try: + return call_subprocess(cmd, show_stdout, cwd, + on_returncode, command_level, + command_desc, extra_environ, + spinner) + except OSError as e: + # errno.ENOENT = no such file or directory + # In other words, the VCS executable isn't available + if e.errno == errno.ENOENT: + raise BadCommand('Cannot find command %r' % self.name) + else: + raise # re-raise exception if a different error occurred + + @classmethod + def controls_location(cls, location): + """ + Check if a location is controlled by the vcs. + It is meant to be overridden to implement smarter detection + mechanisms for specific vcs. + """ + logger.debug('Checking in %s for %s (%s)...', + location, cls.dirname, cls.name) + path = os.path.join(location, cls.dirname) + return os.path.exists(path) + -def get_src_requirement(dist, location, find_tags): +def get_src_requirement(dist, location): version_control = vcs.get_backend_from_location(location) if version_control: - return version_control().get_src_requirement(dist, location, find_tags) - logger.warn('cannot determine version of editable source in %s (is not SVN checkout, Git clone, Mercurial clone or Bazaar branch)' % location) + try: + return version_control().get_src_requirement(dist, + location) + except BadCommand: + logger.warning( + 'cannot determine version of editable source in %s ' + '(%s command not found in path)', + location, + version_control.name, + ) + return dist.as_requirement() + logger.warning( + 'cannot determine version of editable source in %s (is not SVN ' + 'checkout, Git clone, Mercurial clone or Bazaar branch)', + location, + ) return dist.as_requirement() diff --git a/pip/vcs/bazaar.py b/pip/vcs/bazaar.py index c62c9c85a53..0f095841d36 100644 --- a/pip/vcs/bazaar.py +++ b/pip/vcs/bazaar.py @@ -1,62 +1,60 @@ +from __future__ import absolute_import + +import logging import os import tempfile -import re -from pip.backwardcompat import urlparse -from pip.log import logger -from pip.util import rmtree, display_path, call_subprocess + +# TODO: Get this into six.moves.urllib.parse +try: + from urllib import parse as urllib_parse +except ImportError: + import urlparse as urllib_parse + +from pip.utils import rmtree, display_path from pip.vcs import vcs, VersionControl from pip.download import path_to_url +logger = logging.getLogger(__name__) + + class Bazaar(VersionControl): name = 'bzr' dirname = '.bzr' repo_name = 'branch' - bundle_file = 'bzr-branch.txt' - schemes = ('bzr', 'bzr+http', 'bzr+https', 'bzr+ssh', 'bzr+sftp', 'bzr+ftp', 'bzr+lp') - guide = ('# This was a Bazaar branch; to make it a branch again run:\n' - 'bzr branch -r %(rev)s %(url)s .\n') + schemes = ( + 'bzr', 'bzr+http', 'bzr+https', 'bzr+ssh', 'bzr+sftp', 'bzr+ftp', + 'bzr+lp', + ) def __init__(self, url=None, *args, **kwargs): super(Bazaar, self).__init__(url, *args, **kwargs) # Python >= 2.7.4, 3.3 doesn't have uses_fragment or non_hierarchical # Register lp but do not expose as a scheme to support bzr+lp. - if getattr(urlparse, 'uses_fragment', None): - urlparse.uses_fragment.extend(['lp']) - urlparse.non_hierarchical.extend(['lp']) - - def parse_vcs_bundle_file(self, content): - url = rev = None - for line in content.splitlines(): - if not line.strip() or line.strip().startswith('#'): - continue - match = re.search(r'^bzr\s*branch\s*-r\s*(\d*)', line) - if match: - rev = match.group(1).strip() - url = line[match.end():].strip().split(None, 1)[0] - if url and rev: - return url, rev - return None, None + if getattr(urllib_parse, 'uses_fragment', None): + urllib_parse.uses_fragment.extend(['lp']) + urllib_parse.non_hierarchical.extend(['lp']) def export(self, location): - """Export the Bazaar repository at the url to the destination location""" + """ + Export the Bazaar repository at the url to the destination location + """ temp_dir = tempfile.mkdtemp('-export', 'pip-') self.unpack(temp_dir) if os.path.exists(location): # Remove the location to make sure Bazaar can export it correctly rmtree(location) try: - call_subprocess([self.cmd, 'export', location], cwd=temp_dir, - filter_stdout=self._filter, show_stdout=False) + self.run_command(['export', location], cwd=temp_dir, + show_stdout=False) finally: rmtree(temp_dir) def switch(self, dest, url, rev_options): - call_subprocess([self.cmd, 'switch', url], cwd=dest) + self.run_command(['switch', url], cwd=dest) def update(self, dest, rev_options): - call_subprocess( - [self.cmd, 'pull', '-q'] + rev_options, cwd=dest) + self.run_command(['pull', '-q'] + rev_options, cwd=dest) def obtain(self, dest): url, rev = self.get_url_rev() @@ -67,10 +65,13 @@ def obtain(self, dest): rev_options = [] rev_display = '' if self.check_destination(dest, url, rev_options, rev_display): - logger.notify('Checking out %s%s to %s' - % (url, rev_display, display_path(dest))) - call_subprocess( - [self.cmd, 'branch', '-q'] + rev_options + [url, dest]) + logger.info( + 'Checking out %s%s to %s', + url, + rev_display, + display_path(dest), + ) + self.run_command(['branch', '-q'] + rev_options + [url, dest]) def get_url_rev(self): # hotfix the URL scheme after removing bzr+ from bzr+ssh:// readd it @@ -80,8 +81,7 @@ def get_url_rev(self): return url, rev def get_url(self, location): - urls = call_subprocess( - [self.cmd, 'info'], show_stdout=False, cwd=location) + urls = self.run_command(['info'], show_stdout=False, cwd=location) for line in urls.splitlines(): line = line.strip() for x in ('checkout of branch: ', @@ -94,38 +94,23 @@ def get_url(self, location): return None def get_revision(self, location): - revision = call_subprocess( - [self.cmd, 'revno'], show_stdout=False, cwd=location) + revision = self.run_command( + ['revno'], show_stdout=False, cwd=location) return revision.splitlines()[-1] - def get_tag_revs(self, location): - tags = call_subprocess( - [self.cmd, 'tags'], show_stdout=False, cwd=location) - tag_revs = [] - for line in tags.splitlines(): - tags_match = re.search(r'([.\w-]+)\s*(.*)$', line) - if tags_match: - tag = tags_match.group(1) - rev = tags_match.group(2) - tag_revs.append((rev.strip(), tag.strip())) - return dict(tag_revs) - - def get_src_requirement(self, dist, location, find_tags): + def get_src_requirement(self, dist, location): repo = self.get_url(location) + if not repo: + return None if not repo.lower().startswith('bzr:'): repo = 'bzr+' + repo egg_project_name = dist.egg_name().split('-', 1)[0] - if not repo: - return None current_rev = self.get_revision(location) - tag_revs = self.get_tag_revs(location) + return '%s@%s#egg=%s' % (repo, current_rev, egg_project_name) - if current_rev in tag_revs: - # It's a tag - full_egg_name = '%s-%s' % (egg_project_name, tag_revs[current_rev]) - else: - full_egg_name = '%s-dev_r%s' % (dist.egg_name(), current_rev) - return '%s@%s#egg=%s' % (repo, current_rev, full_egg_name) + def check_version(self, dest, rev_options): + """Always assume the versions don't match""" + return False vcs.register(Bazaar) diff --git a/pip/vcs/git.py b/pip/vcs/git.py index 16acebdc4ae..eaa1072e20d 100644 --- a/pip/vcs/git.py +++ b/pip/vcs/git.py @@ -1,23 +1,32 @@ +from __future__ import absolute_import + +import logging import tempfile -import re import os.path -from pip.util import call_subprocess -from pip.util import display_path, rmtree + +from pip.compat import samefile +from pip.exceptions import BadCommand +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + +from pip.utils import display_path, rmtree from pip.vcs import vcs, VersionControl -from pip.log import logger -from pip.backwardcompat import url2pathname, urlparse -urlsplit = urlparse.urlsplit -urlunsplit = urlparse.urlunsplit + + +urlsplit = urllib_parse.urlsplit +urlunsplit = urllib_parse.urlunsplit + + +logger = logging.getLogger(__name__) class Git(VersionControl): name = 'git' dirname = '.git' repo_name = 'clone' - schemes = ('git', 'git+http', 'git+https', 'git+ssh', 'git+git', 'git+file') - bundle_file = 'git-clone.txt' - guide = ('# This was a Git repo; to make it a repo again run:\n' - 'git init\ngit remote add origin %(url)s -f\ngit checkout %(rev)s\n') + schemes = ( + 'git', 'git+http', 'git+https', 'git+ssh', 'git+git', 'git+file', + ) def __init__(self, url=None, *args, **kwargs): @@ -27,28 +36,19 @@ def __init__(self, url=None, *args, **kwargs): scheme, netloc, path, query, fragment = urlsplit(url) if scheme.endswith('file'): initial_slashes = path[:-len(path.lstrip('/'))] - newpath = initial_slashes + url2pathname(path).replace('\\', '/').lstrip('/') + newpath = ( + initial_slashes + + urllib_request.url2pathname(path) + .replace('\\', '/').lstrip('/') + ) url = urlunsplit((scheme, netloc, newpath, query, fragment)) after_plus = scheme.find('+') + 1 - url = scheme[:after_plus] + urlunsplit((scheme[after_plus:], netloc, newpath, query, fragment)) + url = scheme[:after_plus] + urlunsplit( + (scheme[after_plus:], netloc, newpath, query, fragment), + ) super(Git, self).__init__(url, *args, **kwargs) - def parse_vcs_bundle_file(self, content): - url = rev = None - for line in content.splitlines(): - if not line.strip() or line.strip().startswith('#'): - continue - url_match = re.search(r'git\s*remote\s*add\s*origin(.*)\s*-f', line) - if url_match: - url = url_match.group(1).strip() - rev_match = re.search(r'^git\s*checkout\s*-q\s*(.*)\s*', line) - if rev_match: - rev = rev_match.group(1).strip() - if url and rev: - return url, rev - return None, None - def export(self, location): """Export the Git repository at the url to the destination location""" temp_dir = tempfile.mkdtemp('-export', 'pip-') @@ -56,9 +56,9 @@ def export(self, location): try: if not location.endswith('/'): location = location + '/' - call_subprocess( - [self.cmd, 'checkout-index', '-a', '-f', '--prefix', location], - filter_stdout=self._filter, show_stdout=False, cwd=temp_dir) + self.run_command( + ['checkout-index', '-a', '-f', '--prefix', location], + show_stdout=False, cwd=temp_dir) finally: rmtree(temp_dir) @@ -67,7 +67,7 @@ def check_rev_options(self, rev, dest, rev_options): and branches may need origin/ as a prefix. Returns the SHA1 of the branch or tag if found. """ - revisions = self.get_refs(dest) + revisions = self.get_short_refs(dest) origin_rev = 'origin/%s' % rev if origin_rev in revisions: @@ -77,24 +77,35 @@ def check_rev_options(self, rev, dest, rev_options): # a local tag or branch name return [revisions[rev]] else: - logger.warn("Could not find a tag or branch '%s', assuming commit." % rev) + logger.warning( + "Could not find a tag or branch '%s', assuming commit.", rev, + ) return rev_options + def check_version(self, dest, rev_options): + """ + Compare the current sha to the ref. ref may be a branch or tag name, + but current rev will always point to a sha. This means that a branch + or tag will never compare as True. So this ultimately only matches + against exact shas. + """ + return self.get_revision(dest).startswith(rev_options[0]) + def switch(self, dest, url, rev_options): - call_subprocess( - [self.cmd, 'config', 'remote.origin.url', url], cwd=dest) - call_subprocess( - [self.cmd, 'checkout', '-q'] + rev_options, cwd=dest) + self.run_command(['config', 'remote.origin.url', url], cwd=dest) + self.run_command(['checkout', '-q'] + rev_options, cwd=dest) self.update_submodules(dest) def update(self, dest, rev_options): # First fetch changes from the default remote - call_subprocess([self.cmd, 'fetch', '-q'], cwd=dest) - # Then reset to wanted revision (maby even origin/master) + self.run_command(['fetch', '-q'], cwd=dest) + # Then reset to wanted revision (maybe even origin/master) if rev_options: - rev_options = self.check_rev_options(rev_options[0], dest, rev_options) - call_subprocess([self.cmd, 'reset', '--hard', '-q'] + rev_options, cwd=dest) + rev_options = self.check_rev_options( + rev_options[0], dest, rev_options, + ) + self.run_command(['reset', '--hard', '-q'] + rev_options, cwd=dest) #: update submodules self.update_submodules(dest) @@ -107,47 +118,113 @@ def obtain(self, dest): rev_options = ['origin/master'] rev_display = '' if self.check_destination(dest, url, rev_options, rev_display): - logger.notify('Cloning %s%s to %s' % (url, rev_display, display_path(dest))) - call_subprocess([self.cmd, 'clone', '-q', url, dest]) - #: repo may contain submodules - self.update_submodules(dest) + logger.info( + 'Cloning %s%s to %s', url, rev_display, display_path(dest), + ) + self.run_command(['clone', '-q', url, dest]) + if rev: rev_options = self.check_rev_options(rev, dest, rev_options) # Only do a checkout if rev_options differs from HEAD - if not self.get_revision(dest).startswith(rev_options[0]): - call_subprocess([self.cmd, 'checkout', '-q'] + rev_options, cwd=dest) + if not self.check_version(dest, rev_options): + self.run_command( + ['checkout', '-q'] + rev_options, + cwd=dest, + ) + #: repo may contain submodules + self.update_submodules(dest) def get_url(self, location): - url = call_subprocess( - [self.cmd, 'config', 'remote.origin.url'], + """Return URL of the first remote encountered.""" + remotes = self.run_command( + ['config', '--get-regexp', 'remote\..*\.url'], show_stdout=False, cwd=location) + remotes = remotes.splitlines() + found_remote = remotes[0] + for remote in remotes: + if remote.startswith('remote.origin.url '): + found_remote = remote + break + url = found_remote.split(' ')[1] return url.strip() def get_revision(self, location): - current_rev = call_subprocess( - [self.cmd, 'rev-parse', 'HEAD'], show_stdout=False, cwd=location) + current_rev = self.run_command( + ['rev-parse', 'HEAD'], show_stdout=False, cwd=location) return current_rev.strip() + def get_full_refs(self, location): + """Yields tuples of (commit, ref) for branches and tags""" + output = self.run_command(['show-ref'], + show_stdout=False, cwd=location) + for line in output.strip().splitlines(): + commit, ref = line.split(' ', 1) + yield commit.strip(), ref.strip() + + def is_ref_remote(self, ref): + return ref.startswith('refs/remotes/') + + def is_ref_branch(self, ref): + return ref.startswith('refs/heads/') + + def is_ref_tag(self, ref): + return ref.startswith('refs/tags/') + + def is_ref_commit(self, ref): + """A ref is a commit sha if it is not anything else""" + return not any(( + self.is_ref_remote(ref), + self.is_ref_branch(ref), + self.is_ref_tag(ref), + )) + + # Should deprecate `get_refs` since it's ambiguous def get_refs(self, location): + return self.get_short_refs(location) + + def get_short_refs(self, location): """Return map of named refs (branches or tags) to commit hashes.""" - output = call_subprocess([self.cmd, 'show-ref'], - show_stdout=False, cwd=location) rv = {} - for line in output.strip().splitlines(): - commit, ref = line.split(' ', 1) - ref = ref.strip() + for commit, ref in self.get_full_refs(location): ref_name = None - if ref.startswith('refs/remotes/'): + if self.is_ref_remote(ref): ref_name = ref[len('refs/remotes/'):] - elif ref.startswith('refs/heads/'): + elif self.is_ref_branch(ref): ref_name = ref[len('refs/heads/'):] - elif ref.startswith('refs/tags/'): + elif self.is_ref_tag(ref): ref_name = ref[len('refs/tags/'):] if ref_name is not None: - rv[ref_name] = commit.strip() + rv[ref_name] = commit return rv - def get_src_requirement(self, dist, location, find_tags): + def _get_subdirectory(self, location): + """Return the relative path of setup.py to the git repo root.""" + # find the repo root + git_dir = self.run_command(['rev-parse', '--git-dir'], + show_stdout=False, cwd=location).strip() + if not os.path.isabs(git_dir): + git_dir = os.path.join(location, git_dir) + root_dir = os.path.join(git_dir, '..') + # find setup.py + orig_location = location + while not os.path.exists(os.path.join(location, 'setup.py')): + last_location = location + location = os.path.dirname(location) + if location == last_location: + # We've traversed up to the root of the filesystem without + # finding setup.py + logger.warning( + "Could not find setup.py for directory %s (tried all " + "parent directories)", + orig_location, + ) + return None + # relative path of setup.py to repo root + if samefile(root_dir, location): + return None + return os.path.relpath(location, root_dir) + + def get_src_requirement(self, dist, location): repo = self.get_url(location) if not repo.lower().startswith('git:'): repo = 'git+' + repo @@ -155,18 +232,11 @@ def get_src_requirement(self, dist, location, find_tags): if not repo: return None current_rev = self.get_revision(location) - refs = self.get_refs(location) - # refs maps names to commit hashes; we need the inverse - # if multiple names map to a single commit, this arbitrarily picks one - names_by_commit = dict((commit, ref) for ref, commit in refs.items()) - - if current_rev in names_by_commit: - # It's a tag - full_egg_name = '%s-%s' % (egg_project_name, names_by_commit[current_rev]) - else: - full_egg_name = '%s-dev' % egg_project_name - - return '%s@%s#egg=%s' % (repo, current_rev, full_egg_name) + req = '%s@%s#egg=%s' % (repo, current_rev, egg_project_name) + subdirectory = self._get_subdirectory(location) + if subdirectory: + req += '&subdirectory=' + subdirectory + return req def get_url_rev(self): """ @@ -175,8 +245,8 @@ def get_url_rev(self): work with a ssh:// scheme (e.g. Github). But we need a scheme for parsing. Hence we remove it again afterwards and return it as a stub. """ - if not '://' in self.url: - assert not 'file:' in self.url + if '://' not in self.url: + assert 'file:' not in self.url self.url = self.url.replace('git+', 'git+ssh://') url, rev = super(Git, self).get_url_rev() url = url.replace('ssh://', '') @@ -188,7 +258,25 @@ def get_url_rev(self): def update_submodules(self, location): if not os.path.exists(os.path.join(location, '.gitmodules')): return - call_subprocess([self.cmd, 'submodule', 'update', '--init', '--recursive', '-q'], - cwd=location) + self.run_command( + ['submodule', 'update', '--init', '--recursive', '-q'], + cwd=location, + ) + + @classmethod + def controls_location(cls, location): + if super(Git, cls).controls_location(location): + return True + try: + r = cls().run_command(['rev-parse'], + cwd=location, + show_stdout=False, + on_returncode='ignore') + return not r + except BadCommand: + logger.debug("could not determine if %s is under git control " + "because git is not available", location) + return False + vcs.register(Git) diff --git a/pip/vcs/mercurial.py b/pip/vcs/mercurial.py index 2dbe3fc001b..1aa83b91452 100644 --- a/pip/vcs/mercurial.py +++ b/pip/vcs/mercurial.py @@ -1,13 +1,16 @@ +from __future__ import absolute_import + +import logging import os import tempfile -import re -import sys -from pip.util import call_subprocess -from pip.util import display_path, rmtree -from pip.log import logger + +from pip.utils import display_path, rmtree from pip.vcs import vcs, VersionControl from pip.download import path_to_url -from pip.backwardcompat import ConfigParser +from pip._vendor.six.moves import configparser + + +logger = logging.getLogger(__name__) class Mercurial(VersionControl): @@ -15,57 +18,35 @@ class Mercurial(VersionControl): dirname = '.hg' repo_name = 'clone' schemes = ('hg', 'hg+http', 'hg+https', 'hg+ssh', 'hg+static-http') - bundle_file = 'hg-clone.txt' - guide = ('# This was a Mercurial repo; to make it a repo again run:\n' - 'hg init\nhg pull %(url)s\nhg update -r %(rev)s\n') - - def parse_vcs_bundle_file(self, content): - url = rev = None - for line in content.splitlines(): - if not line.strip() or line.strip().startswith('#'): - continue - url_match = re.search(r'hg\s*pull\s*(.*)\s*', line) - if url_match: - url = url_match.group(1).strip() - rev_match = re.search(r'^hg\s*update\s*-r\s*(.*)\s*', line) - if rev_match: - rev = rev_match.group(1).strip() - if url and rev: - return url, rev - return None, None def export(self, location): """Export the Hg repository at the url to the destination location""" temp_dir = tempfile.mkdtemp('-export', 'pip-') self.unpack(temp_dir) try: - call_subprocess( - [self.cmd, 'archive', location], - filter_stdout=self._filter, show_stdout=False, cwd=temp_dir) + self.run_command( + ['archive', location], show_stdout=False, cwd=temp_dir) finally: rmtree(temp_dir) def switch(self, dest, url, rev_options): repo_config = os.path.join(dest, self.dirname, 'hgrc') - config = ConfigParser.SafeConfigParser() + config = configparser.SafeConfigParser() try: config.read(repo_config) config.set('paths', 'default', url) - config_file = open(repo_config, 'w') - config.write(config_file) - config_file.close() - except (OSError, ConfigParser.NoSectionError): - e = sys.exc_info()[1] - logger.warn( - 'Could not switch Mercurial repository to %s: %s' - % (url, e)) + with open(repo_config, 'w') as config_file: + config.write(config_file) + except (OSError, configparser.NoSectionError) as exc: + logger.warning( + 'Could not switch Mercurial repository to %s: %s', url, exc, + ) else: - call_subprocess([self.cmd, 'update', '-q'] + rev_options, cwd=dest) + self.run_command(['update', '-q'] + rev_options, cwd=dest) def update(self, dest, rev_options): - call_subprocess([self.cmd, 'pull', '-q'], cwd=dest) - call_subprocess( - [self.cmd, 'update', '-q'] + rev_options, cwd=dest) + self.run_command(['pull', '-q'], cwd=dest) + self.run_command(['update', '-q'] + rev_options, cwd=dest) def obtain(self, dest): url, rev = self.get_url_rev() @@ -76,76 +57,47 @@ def obtain(self, dest): rev_options = [] rev_display = '' if self.check_destination(dest, url, rev_options, rev_display): - logger.notify('Cloning hg %s%s to %s' - % (url, rev_display, display_path(dest))) - call_subprocess([self.cmd, 'clone', '--noupdate', '-q', url, dest]) - call_subprocess([self.cmd, 'update', '-q'] + rev_options, cwd=dest) + logger.info( + 'Cloning hg %s%s to %s', + url, + rev_display, + display_path(dest), + ) + self.run_command(['clone', '--noupdate', '-q', url, dest]) + self.run_command(['update', '-q'] + rev_options, cwd=dest) def get_url(self, location): - url = call_subprocess( - [self.cmd, 'showconfig', 'paths.default'], + url = self.run_command( + ['showconfig', 'paths.default'], show_stdout=False, cwd=location).strip() if self._is_local_repository(url): url = path_to_url(url) return url.strip() - def get_tag_revs(self, location): - tags = call_subprocess( - [self.cmd, 'tags'], show_stdout=False, cwd=location) - tag_revs = [] - for line in tags.splitlines(): - tags_match = re.search(r'([\w\d\.-]+)\s*([\d]+):.*$', line) - if tags_match: - tag = tags_match.group(1) - rev = tags_match.group(2) - if "tip" != tag: - tag_revs.append((rev.strip(), tag.strip())) - return dict(tag_revs) - - def get_branch_revs(self, location): - branches = call_subprocess( - [self.cmd, 'branches'], show_stdout=False, cwd=location) - branch_revs = [] - for line in branches.splitlines(): - branches_match = re.search(r'([\w\d\.-]+)\s*([\d]+):.*$', line) - if branches_match: - branch = branches_match.group(1) - rev = branches_match.group(2) - if "default" != branch: - branch_revs.append((rev.strip(), branch.strip())) - return dict(branch_revs) - def get_revision(self, location): - current_revision = call_subprocess( - [self.cmd, 'parents', '--template={rev}'], + current_revision = self.run_command( + ['parents', '--template={rev}'], show_stdout=False, cwd=location).strip() return current_revision def get_revision_hash(self, location): - current_rev_hash = call_subprocess( - [self.cmd, 'parents', '--template={node}'], + current_rev_hash = self.run_command( + ['parents', '--template={node}'], show_stdout=False, cwd=location).strip() return current_rev_hash - def get_src_requirement(self, dist, location, find_tags): + def get_src_requirement(self, dist, location): repo = self.get_url(location) if not repo.lower().startswith('hg:'): repo = 'hg+' + repo egg_project_name = dist.egg_name().split('-', 1)[0] if not repo: return None - current_rev = self.get_revision(location) current_rev_hash = self.get_revision_hash(location) - tag_revs = self.get_tag_revs(location) - branch_revs = self.get_branch_revs(location) - if current_rev in tag_revs: - # It's a tag - full_egg_name = '%s-%s' % (egg_project_name, tag_revs[current_rev]) - elif current_rev in branch_revs: - # It's the tip of a branch - full_egg_name = '%s-%s' % (egg_project_name, branch_revs[current_rev]) - else: - full_egg_name = '%s-dev' % egg_project_name - return '%s@%s#egg=%s' % (repo, current_rev_hash, full_egg_name) + return '%s@%s#egg=%s' % (repo, current_rev_hash, egg_project_name) + + def check_version(self, dest, rev_options): + """Always assume the versions don't match""" + return False vcs.register(Mercurial) diff --git a/pip/vcs/subversion.py b/pip/vcs/subversion.py index 88163ff73f5..4b2315667af 100644 --- a/pip/vcs/subversion.py +++ b/pip/vcs/subversion.py @@ -1,9 +1,14 @@ +from __future__ import absolute_import + +import logging import os import re -from pip.backwardcompat import urlparse + +from pip._vendor.six.moves.urllib import parse as urllib_parse + from pip.index import Link -from pip.util import rmtree, display_path, call_subprocess -from pip.log import logger +from pip.utils import rmtree, display_path +from pip.utils.logging import indent_log from pip.vcs import vcs, VersionControl _svn_xml_url_re = re.compile('url="([^"]+)"') @@ -14,82 +19,80 @@ _svn_info_xml_url_re = re.compile(r'<url>(.*)</url>') +logger = logging.getLogger(__name__) + + class Subversion(VersionControl): name = 'svn' dirname = '.svn' repo_name = 'checkout' schemes = ('svn', 'svn+ssh', 'svn+http', 'svn+https', 'svn+svn') - bundle_file = 'svn-checkout.txt' - guide = ('# This was an svn checkout; to make it a checkout again run:\n' - 'svn checkout --force -r %(rev)s %(url)s .\n') def get_info(self, location): """Returns (url, revision), where both are strings""" - assert not location.rstrip('/').endswith(self.dirname), 'Bad directory: %s' % location - output = call_subprocess( - [self.cmd, 'info', location], show_stdout=False, extra_environ={'LANG': 'C'}) + assert not location.rstrip('/').endswith(self.dirname), \ + 'Bad directory: %s' % location + output = self.run_command( + ['info', location], + show_stdout=False, + extra_environ={'LANG': 'C'}, + ) match = _svn_url_re.search(output) if not match: - logger.warn('Cannot determine URL of svn checkout %s' % display_path(location)) - logger.info('Output that cannot be parsed: \n%s' % output) + logger.warning( + 'Cannot determine URL of svn checkout %s', + display_path(location), + ) + logger.debug('Output that cannot be parsed: \n%s', output) return None, None url = match.group(1).strip() match = _svn_revision_re.search(output) if not match: - logger.warn('Cannot determine revision of svn checkout %s' % display_path(location)) - logger.info('Output that cannot be parsed: \n%s' % output) + logger.warning( + 'Cannot determine revision of svn checkout %s', + display_path(location), + ) + logger.debug('Output that cannot be parsed: \n%s', output) return url, None return url, match.group(1) - def parse_vcs_bundle_file(self, content): - for line in content.splitlines(): - if not line.strip() or line.strip().startswith('#'): - continue - match = re.search(r'^-r\s*([^ ])?', line) - if not match: - return None, None - rev = match.group(1) - rest = line[match.end():].strip().split(None, 1)[0] - return rest, rev - return None, None - def export(self, location): """Export the svn repository at the url to the destination location""" url, rev = self.get_url_rev() rev_options = get_rev_options(url, rev) - logger.notify('Exporting svn repository %s to %s' % (url, location)) - logger.indent += 2 - try: + url = self.remove_auth_from_url(url) + logger.info('Exporting svn repository %s to %s', url, location) + with indent_log(): if os.path.exists(location): - # Subversion doesn't like to check out over an existing directory - # --force fixes this, but was only added in svn 1.5 + # Subversion doesn't like to check out over an existing + # directory --force fixes this, but was only added in svn 1.5 rmtree(location) - call_subprocess( - [self.cmd, 'export'] + rev_options + [url, location], - filter_stdout=self._filter, show_stdout=False) - finally: - logger.indent -= 2 + self.run_command( + ['export'] + rev_options + [url, location], + show_stdout=False) def switch(self, dest, url, rev_options): - call_subprocess( - [self.cmd, 'switch'] + rev_options + [url, dest]) + self.run_command(['switch'] + rev_options + [url, dest]) def update(self, dest, rev_options): - call_subprocess( - [self.cmd, 'update'] + rev_options + [dest]) + self.run_command(['update'] + rev_options + [dest]) def obtain(self, dest): url, rev = self.get_url_rev() rev_options = get_rev_options(url, rev) + url = self.remove_auth_from_url(url) if rev: rev_display = ' (to revision %s)' % rev else: rev_display = '' if self.check_destination(dest, url, rev_options, rev_display): - logger.notify('Checking out %s%s to %s' - % (url, rev_display, display_path(dest))) - call_subprocess( - [self.cmd, 'checkout', '-q'] + rev_options + [url, dest]) + logger.info( + 'Checking out %s%s to %s', + url, + rev_display, + display_path(dest), + ) + self.run_command(['checkout', '-q'] + rev_options + [url, dest]) def get_location(self, dist, dependency_links): for url in dependency_links: @@ -97,7 +100,7 @@ def get_location(self, dist, dependency_links): if not egg_fragment: continue if '-' in egg_fragment: - ## FIXME: will this work when a package has - in the name? + # FIXME: will this work when a package has - in the name? key = '-'.join(egg_fragment.split('-')[:-1]).lower() else: key = egg_fragment @@ -119,7 +122,7 @@ def get_revision(self, location): dirs.remove(self.dirname) entries_fn = os.path.join(base, self.dirname, 'entries') if not os.path.exists(entries_fn): - ## FIXME: should we warn? + # FIXME: should we warn? continue dirurl, localrev = self._get_svn_url_rev(base) @@ -140,16 +143,21 @@ def get_url_rev(self): return url, rev def get_url(self, location): - # In cases where the source is in a subdirectory, not alongside setup.py - # we have to look up in the location until we find a real setup.py + # In cases where the source is in a subdirectory, not alongside + # setup.py we have to look up in the location until we find a real + # setup.py orig_location = location while not os.path.exists(os.path.join(location, 'setup.py')): last_location = location location = os.path.dirname(location) if location == last_location: - # We've traversed up to the root of the filesystem without finding setup.py - logger.warn("Could not find setup.py for directory %s (tried all parent directories)" - % orig_location) + # We've traversed up to the root of the filesystem without + # finding setup.py + logger.warning( + "Could not find setup.py for directory %s (tried all " + "parent directories)", + orig_location, + ) return None return self._get_svn_url_rev(location)[0] @@ -157,10 +165,16 @@ def get_url(self, location): def _get_svn_url_rev(self, location): from pip.exceptions import InstallationError - f = open(os.path.join(location, self.dirname, 'entries')) - data = f.read() - f.close() - if data.startswith('8') or data.startswith('9') or data.startswith('10'): + entries_path = os.path.join(location, self.dirname, 'entries') + if os.path.exists(entries_path): + with open(entries_path) as f: + data = f.read() + else: # subversion >= 1.7 does not have the 'entries' file + data = '' + + if (data.startswith('8') or + data.startswith('9') or + data.startswith('10')): data = list(map(str.splitlines, data.split('\n\x0c\n'))) del data[0][0] # get rid of the '8' url = data[0][3] @@ -174,9 +188,14 @@ def _get_svn_url_rev(self, location): else: try: # subversion >= 1.7 - xml = call_subprocess([self.cmd, 'info', '--xml', location], show_stdout=False) + xml = self.run_command( + ['info', '--xml', location], + show_stdout=False, + ) url = _svn_info_xml_url_re.search(xml).group(1) - revs = [int(m.group(1)) for m in _svn_info_xml_rev_re.finditer(xml)] + revs = [ + int(m.group(1)) for m in _svn_info_xml_rev_re.finditer(xml) + ] except InstallationError: url, revs = None, [] @@ -187,59 +206,36 @@ def _get_svn_url_rev(self, location): return url, rev - def get_tag_revs(self, svn_tag_url): - stdout = call_subprocess( - [self.cmd, 'ls', '-v', svn_tag_url], show_stdout=False) - results = [] - for line in stdout.splitlines(): - parts = line.split() - rev = int(parts[0]) - tag = parts[-1].strip('/') - results.append((tag, rev)) - return results - - def find_tag_match(self, rev, tag_revs): - best_match_rev = None - best_tag = None - for tag, tag_rev in tag_revs: - if (tag_rev > rev and - (best_match_rev is None or best_match_rev > tag_rev)): - # FIXME: Is best_match > tag_rev really possible? - # or is it a sign something is wacky? - best_match_rev = tag_rev - best_tag = tag - return best_tag - - def get_src_requirement(self, dist, location, find_tags=False): + def get_src_requirement(self, dist, location): repo = self.get_url(location) if repo is None: return None - parts = repo.split('/') - ## FIXME: why not project name? + # FIXME: why not project name? egg_project_name = dist.egg_name().split('-', 1)[0] rev = self.get_revision(location) - if parts[-2] in ('tags', 'tag'): - # It's a tag, perfect! - full_egg_name = '%s-%s' % (egg_project_name, parts[-1]) - elif parts[-2] in ('branches', 'branch'): - # It's a branch :( - full_egg_name = '%s-%s-r%s' % (dist.egg_name(), parts[-1], rev) - elif parts[-1] == 'trunk': - # Trunk :-/ - full_egg_name = '%s-dev_r%s' % (dist.egg_name(), rev) - if find_tags: - tag_url = '/'.join(parts[:-1]) + '/tags' - tag_revs = self.get_tag_revs(tag_url) - match = self.find_tag_match(rev, tag_revs) - if match: - logger.notify('trunk checkout %s seems to be equivalent to tag %s' % match) - repo = '%s/%s' % (tag_url, match) - full_egg_name = '%s-%s' % (egg_project_name, match) - else: - # Don't know what it is - logger.warn('svn URL does not fit normal structure (tags/branches/trunk): %s' % repo) - full_egg_name = '%s-dev_r%s' % (egg_project_name, rev) - return 'svn+%s@%s#egg=%s' % (repo, rev, full_egg_name) + return 'svn+%s@%s#egg=%s' % (repo, rev, egg_project_name) + + def check_version(self, dest, rev_options): + """Always assume the versions don't match""" + return False + + @staticmethod + def remove_auth_from_url(url): + # Return a copy of url with 'username:password@' removed. + # username/pass params are passed to subversion through flags + # and are not recognized in the url. + + # parsed url + purl = urllib_parse.urlsplit(url) + stripped_netloc = \ + purl.netloc.split('@')[-1] + + # stripped url + url_pieces = ( + purl.scheme, stripped_netloc, purl.path, purl.query, purl.fragment + ) + surl = urllib_parse.urlunsplit(url_pieces) + return surl def get_rev_options(url, rev): @@ -248,7 +244,7 @@ def get_rev_options(url, rev): else: rev_options = [] - r = urlparse.urlsplit(url) + r = urllib_parse.urlsplit(url) if hasattr(r, 'username'): # >= Python-2.5 username, password = r.username, r.password diff --git a/pip/wheel.py b/pip/wheel.py index 4e9803f2354..3df512dcc90 100644 --- a/pip/wheel.py +++ b/pip/wheel.py @@ -1,29 +1,45 @@ """ Support for installing and building the "wheel" binary package format. """ -from __future__ import with_statement +from __future__ import absolute_import import compileall import csv +import errno import functools import hashlib +import logging import os +import os.path import re import shutil +import stat import sys +import tempfile +import warnings from base64 import urlsafe_b64encode from email.parser import Parser -from pip.backwardcompat import ConfigParser, StringIO -from pip.exceptions import InvalidWheelFilename, UnsupportedWheel -from pip.locations import distutils_scheme -from pip.log import logger +from pip._vendor.six import StringIO + +import pip +from pip.compat import expanduser +from pip.download import path_to_url, unpack_url +from pip.exceptions import ( + InstallationError, InvalidWheelFilename, UnsupportedWheel) +from pip.locations import distutils_scheme, PIP_DELETE_MARKER_FILENAME from pip import pep425tags -from pip.util import call_subprocess, normalize_path, make_path_relative -from pip._vendor import pkg_resources +from pip.utils import ( + call_subprocess, ensure_dir, captured_stdout, rmtree, read_chunks, +) +from pip.utils.ui import open_spinner +from pip.utils.logging import indent_log +from pip.utils.setuptools_build import SETUPTOOLS_SHIM from pip._vendor.distlib.scripts import ScriptMaker from pip._vendor import pkg_resources +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.six.moves import configparser wheel_ext = '.whl' @@ -31,65 +47,152 @@ VERSION_COMPATIBLE = (1, 0) -def rehash(path, algo='sha256', blocksize=1<<20): +logger = logging.getLogger(__name__) + + +class WheelCache(object): + """A cache of wheels for future installs.""" + + def __init__(self, cache_dir, format_control): + """Create a wheel cache. + + :param cache_dir: The root of the cache. + :param format_control: A pip.index.FormatControl object to limit + binaries being read from the cache. + """ + self._cache_dir = expanduser(cache_dir) if cache_dir else None + self._format_control = format_control + + def cached_wheel(self, link, package_name): + return cached_wheel( + self._cache_dir, link, self._format_control, package_name) + + +def _cache_for_link(cache_dir, link): + """ + Return a directory to store cached wheels in for link. + + Because there are M wheels for any one sdist, we provide a directory + to cache them in, and then consult that directory when looking up + cache hits. + + We only insert things into the cache if they have plausible version + numbers, so that we don't contaminate the cache with things that were not + unique. E.g. ./package might have dozens of installs done for it and build + a version of 0.0...and if we built and cached a wheel, we'd end up using + the same wheel even if the source has been edited. + + :param cache_dir: The cache_dir being used by pip. + :param link: The link of the sdist for which this will cache wheels. + """ + + # We want to generate an url to use as our cache key, we don't want to just + # re-use the URL because it might have other items in the fragment and we + # don't care about those. + key_parts = [link.url_without_fragment] + if link.hash_name is not None and link.hash is not None: + key_parts.append("=".join([link.hash_name, link.hash])) + key_url = "#".join(key_parts) + + # Encode our key url with sha224, we'll use this because it has similar + # security properties to sha256, but with a shorter total output (and thus + # less secure). However the differences don't make a lot of difference for + # our use case here. + hashed = hashlib.sha224(key_url.encode()).hexdigest() + + # We want to nest the directories some to prevent having a ton of top level + # directories where we might run out of sub directories on some FS. + parts = [hashed[:2], hashed[2:4], hashed[4:6], hashed[6:]] + + # Inside of the base location for cached wheels, expand our parts and join + # them all together. + return os.path.join(cache_dir, "wheels", *parts) + + +def cached_wheel(cache_dir, link, format_control, package_name): + if not cache_dir: + return link + if not link: + return link + if link.is_wheel: + return link + if not link.is_artifact: + return link + if not package_name: + return link + canonical_name = canonicalize_name(package_name) + formats = pip.index.fmt_ctl_formats(format_control, canonical_name) + if "binary" not in formats: + return link + root = _cache_for_link(cache_dir, link) + try: + wheel_names = os.listdir(root) + except OSError as e: + if e.errno in (errno.ENOENT, errno.ENOTDIR): + return link + raise + candidates = [] + for wheel_name in wheel_names: + try: + wheel = Wheel(wheel_name) + except InvalidWheelFilename: + continue + if not wheel.supported(): + # Built for a different python/arch/etc + continue + candidates.append((wheel.support_index_min(), wheel_name)) + if not candidates: + return link + candidates.sort() + path = os.path.join(root, candidates[0][1]) + return pip.index.Link(path_to_url(path)) + + +def rehash(path, algo='sha256', blocksize=1 << 20): """Return (hash, length) for path using hashlib.new(algo)""" h = hashlib.new(algo) length = 0 with open(path, 'rb') as f: - block = f.read(blocksize) - while block: + for block in read_chunks(f, size=blocksize): length += len(block) h.update(block) - block = f.read(blocksize) - digest = 'sha256='+urlsafe_b64encode(h.digest()).decode('latin1').rstrip('=') + digest = 'sha256=' + urlsafe_b64encode( + h.digest() + ).decode('latin1').rstrip('=') return (digest, length) -try: - unicode - def binary(s): - if isinstance(s, unicode): - return s.encode('ascii') - return s -except NameError: - def binary(s): - if isinstance(s, str): - return s.encode('ascii') def open_for_csv(name, mode): if sys.version_info[0] < 3: nl = {} bin = 'b' else: - nl = { 'newline': '' } + nl = {'newline': ''} bin = '' return open(name, mode + bin, **nl) + def fix_script(path): """Replace #!python with #!/path/to/python Return True if file was changed.""" # XXX RECORD hashes will need to be updated if os.path.isfile(path): - script = open(path, 'rb') - try: + with open(path, 'rb') as script: firstline = script.readline() - if not firstline.startswith(binary('#!python')): + if not firstline.startswith(b'#!python'): return False exename = sys.executable.encode(sys.getfilesystemencoding()) - firstline = binary('#!') + exename + binary(os.linesep) + firstline = b'#!' + exename + os.linesep.encode("ascii") rest = script.read() - finally: - script.close() - script = open(path, 'wb') - try: + with open(path, 'wb') as script: script.write(firstline) script.write(rest) - finally: - script.close() return True dist_info_re = re.compile(r"""^(?P<namever>(?P<name>.+?)(-(?P<ver>\d.+?))?) \.dist-info$""", re.VERBOSE) + def root_is_purelib(name, wheeldir): """ Return True if the extracted wheel in wheeldir should go into purelib. @@ -121,7 +224,8 @@ def get_entrypoints(filename): data.write("\n") data.seek(0) - cp = ConfigParser.RawConfigParser() + cp = configparser.RawConfigParser() + cp.optionxform = lambda option: option cp.readfp(data) console = {} @@ -134,11 +238,14 @@ def get_entrypoints(filename): def move_wheel_files(name, req, wheeldir, user=False, home=None, root=None, - pycompile=True, scheme=None): + pycompile=True, scheme=None, isolated=False, prefix=None): """Install a wheel""" if not scheme: - scheme = distutils_scheme(name, user=user, home=home, root=root) + scheme = distutils_scheme( + name, user=user, home=home, root=root, isolated=isolated, + prefix=prefix, + ) if root_is_purelib(name, wheeldir): lib_dir = scheme['purelib'] @@ -159,10 +266,14 @@ def move_wheel_files(name, req, wheeldir, user=False, home=None, root=None, # Compile all of the pyc files that we're going to be installing if pycompile: - compileall.compile_dir(source, force=True, quiet=True) + with captured_stdout() as stdout: + with warnings.catch_warnings(): + warnings.filterwarnings('ignore') + compileall.compile_dir(source, force=True, quiet=True) + logger.debug(stdout.getvalue()) def normpath(src, p): - return make_path_relative(src, p).replace(os.path.sep, '/') + return os.path.relpath(src, p).replace(os.path.sep, '/') def record_installed(srcfile, destfile, modified=False): """Map archive RECORD paths to installation RECORD paths.""" @@ -173,8 +284,7 @@ def record_installed(srcfile, destfile, modified=False): changed.add(destfile) def clobber(source, dest, is_base, fixer=None, filter=None): - if not os.path.exists(dest): # common for the 'include' path - os.makedirs(dest) + ensure_dir(dest) # common for the 'include' path for dir, subdirs, files in os.walk(source): basedir = dir[len(source):].lstrip(os.path.sep) @@ -186,11 +296,14 @@ def clobber(source, dest, is_base, fixer=None, filter=None): if is_base and basedir == '' and destsubdir.endswith('.data'): data_dirs.append(s) continue - elif (is_base - and s.endswith('.dist-info') - # is self.req.project_name case preserving? - and s.lower().startswith(req.project_name.replace('-', '_').lower())): - assert not info_dir, 'Multiple .dist-info directories' + elif (is_base and + s.endswith('.dist-info') and + # is self.req.project_name case preserving? + s.lower().startswith( + req.name.replace('-', '_').lower())): + assert not info_dir, ('Multiple .dist-info directories: ' + + destsubdir + ', ' + + ', '.join(info_dir)) info_dir.append(destsubdir) for f in files: # Skip unwanted files @@ -201,12 +314,30 @@ def clobber(source, dest, is_base, fixer=None, filter=None): # directory creation is lazy and after the file filtering above # to ensure we don't install empty dirs; empty dirs can't be # uninstalled. - if not os.path.exists(destdir): - os.makedirs(destdir) - # use copy2 (not move) to be extra sure we're not moving - # directories over; copy2 fails for directories. this would - # fail tests (not during released/user execution) - shutil.copy2(srcfile, destfile) + ensure_dir(destdir) + + # We use copyfile (not move, copy, or copy2) to be extra sure + # that we are not moving directories over (copyfile fails for + # directories) as well as to ensure that we are not copying + # over any metadata because we want more control over what + # metadata we actually copy over. + shutil.copyfile(srcfile, destfile) + + # Copy over the metadata for the file, currently this only + # includes the atime and mtime. + st = os.stat(srcfile) + if hasattr(os, "utime"): + os.utime(destfile, (st.st_atime, st.st_mtime)) + + # If our file is executable, then make our destination file + # executable. + if os.access(srcfile, os.X_OK): + st = os.stat(srcfile) + permissions = ( + st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH + ) + os.chmod(destfile, permissions) + changed = False if fixer: changed = fixer(destfile) @@ -248,6 +379,10 @@ def is_entrypoint_wrapper(name): maker = ScriptMaker(None, scheme['scripts']) + # Ensure old scripts are overwritten. + # See https://github.com/pypa/pip/issues/1800 + maker.clobber = True + # Ensure we don't generate any variants for scripts because this is almost # never what somebody wants. # See https://bitbucket.org/pypa/distlib/issue/35/ @@ -263,6 +398,13 @@ def is_entrypoint_wrapper(name): # See https://bitbucket.org/pypa/distlib/issue/34/ # See https://bitbucket.org/pypa/distlib/issue/33/ def _get_script_text(entry): + if entry.suffix is None: + raise InstallationError( + "Invalid script entry point: %s for req: %s - A callable " + "suffix is required. Cf https://packaging.python.org/en/" + "latest/distributing.html#console-scripts for more " + "information." % (entry, req) + ) return maker.script_template % { "module": entry.prefix, "import_name": entry.suffix.split(".")[0], @@ -298,7 +440,7 @@ def _get_script_text(entry): # Because setuptools and pip are bundled with _ensurepip and virtualenv, # we need to use universal wheels. So, as a stopgap until Metadata 2.0, we # override the versioned entry points in the wheel and generate the - # correct ones. This code is purely a short-term measure until Metadat 2.0 + # correct ones. This code is purely a short-term measure until Metadata 2.0 # is available. # # To add the level of hack in this section of code, in order to support @@ -339,17 +481,34 @@ def _get_script_text(entry): spec = 'easy_install-%s = %s' % (sys.version[:3], easy_install_script) generated.extend(maker.make(spec)) # Delete any other versioned easy_install entry points - easy_install_ep = [k for k in console - if re.match(r'easy_install(-\d\.\d)?$', k)] + easy_install_ep = [ + k for k in console if re.match(r'easy_install(-\d\.\d)?$', k) + ] for k in easy_install_ep: del console[k] # Generate the console and GUI entry points specified in the wheel if len(console) > 0: - generated.extend(maker.make_multiple(['%s = %s' % kv for kv in console.items()])) + generated.extend( + maker.make_multiple(['%s = %s' % kv for kv in console.items()]) + ) if len(gui) > 0: - generated.extend(maker.make_multiple(['%s = %s' % kv for kv in gui.items()], {'gui': True})) + generated.extend( + maker.make_multiple( + ['%s = %s' % kv for kv in gui.items()], + {'gui': True} + ) + ) + + # Record pip as the installer + installer = os.path.join(info_dir[0], 'INSTALLER') + temp_installer = os.path.join(info_dir[0], 'INSTALLER.pip') + with open(temp_installer, 'wb') as installer_file: + installer_file.write(b'pip\n') + shutil.move(temp_installer, installer) + generated.append(installer) + # Record details of all files installed record = os.path.join(info_dir[0], 'RECORD') temp_record = os.path.join(info_dir[0], 'RECORD.pip') with open_for_csv(record, 'r') as record_in: @@ -363,11 +522,12 @@ def _get_script_text(entry): writer.writerow(row) for f in generated: h, l = rehash(f) - writer.writerow((f, h, l)) + writer.writerow((normpath(f, lib_dir), h, l)) for f in installed: writer.writerow((installed[f], '', '')) shutil.move(temp_record, record) + def _unique(fn): @functools.wraps(fn) def unique(*args, **kw): @@ -378,6 +538,7 @@ def unique(*args, **kw): yield item return unique + # TODO: this goes somewhere besides the wheel module @_unique def uninstallation_paths(dist): @@ -389,7 +550,7 @@ def uninstallation_paths(dist): UninstallPathSet.add() takes care of the __pycache__ .pyc. """ - from pip.req import FakeFile # circular import + from pip.utils import FakeFile # circular import r = csv.reader(FakeFile(dist.get_metadata_lines('RECORD'))) for row in r: path = os.path.join(dist.location, row[0]) @@ -397,7 +558,7 @@ def uninstallation_paths(dist): if path.endswith('.py'): dn, fn = os.path.split(path) base = fn[:-3] - path = os.path.join(dn, base+'.pyc') + path = os.path.join(dn, base + '.pyc') yield path @@ -443,8 +604,10 @@ def check_compatibility(version, name): "of pip" % (name, '.'.join(map(str, version))) ) elif version > VERSION_COMPATIBLE: - logger.warn('Installing from a newer Wheel-Version (%s)' - % '.'.join(map(str, version))) + logger.warning( + 'Installing from a newer Wheel-Version (%s)', + '.'.join(map(str, version)), + ) class Wheel(object): @@ -453,10 +616,11 @@ class Wheel(object): # TODO: maybe move the install code into this class wheel_file_re = re.compile( - r"""^(?P<namever>(?P<name>.+?)-(?P<ver>\d.*?)) - ((-(?P<build>\d.*?))?-(?P<pyver>.+?)-(?P<abi>.+?)-(?P<plat>.+?) - \.whl|\.dist-info)$""", - re.VERBOSE) + r"""^(?P<namever>(?P<name>.+?)-(?P<ver>\d.*?)) + ((-(?P<build>\d.*?))?-(?P<pyver>.+?)-(?P<abi>.+?)-(?P<plat>.+?) + \.whl|\.dist-info)$""", + re.VERBOSE + ) def __init__(self, filename): """ @@ -464,7 +628,9 @@ def __init__(self, filename): """ wheel_info = self.wheel_file_re.match(filename) if not wheel_info: - raise InvalidWheelFilename("%s is not a valid wheel filename." % filename) + raise InvalidWheelFilename( + "%s is not a valid wheel filename." % filename + ) self.filename = filename self.name = wheel_info.group('name').replace('_', '-') # we'll assume "_" means "-" due to wheel naming scheme @@ -475,8 +641,10 @@ def __init__(self, filename): self.plats = wheel_info.group('plat').split('.') # All the tag combinations from this file - self.file_tags = set((x, y, z) for x in self.pyversions for y - in self.abis for z in self.plats) + self.file_tags = set( + (x, y, z) for x in self.pyversions + for y in self.abis for z in self.plats + ) def support_index_min(self, tags=None): """ @@ -485,14 +653,14 @@ def support_index_min(self, tags=None): and one of the file tags is first in the list, then return 0. Returns None is the wheel is not supported. """ - if tags is None: # for mock + if tags is None: # for mock tags = pep425tags.supported_tags indexes = [tags.index(c) for c in self.file_tags if c in tags] return min(indexes) if indexes else None def supported(self, tags=None): """Is this wheel supported on this system?""" - if tags is None: # for mock + if tags is None: # for mock tags = pep425tags.supported_tags return bool(set(tags).intersection(self.file_tags)) @@ -500,61 +668,187 @@ def supported(self, tags=None): class WheelBuilder(object): """Build wheels from a RequirementSet.""" - def __init__(self, requirement_set, finder, wheel_dir, build_options=[], global_options=[]): + def __init__(self, requirement_set, finder, build_options=None, + global_options=None): self.requirement_set = requirement_set self.finder = finder - self.wheel_dir = normalize_path(wheel_dir) - self.build_options = build_options - self.global_options = global_options - - def _build_one(self, req): - """Build one wheel.""" - - base_args = [ - sys.executable, '-c', - "import setuptools;__file__=%r;"\ - "exec(compile(open(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))" % req.setup_py] + \ - list(self.global_options) - - logger.notify('Running setup.py bdist_wheel for %s' % req.name) - logger.notify('Destination directory: %s' % self.wheel_dir) - wheel_args = base_args + ['bdist_wheel', '-d', self.wheel_dir] + self.build_options + self._cache_root = requirement_set._wheel_cache._cache_dir + self._wheel_dir = requirement_set.wheel_download_dir + self.build_options = build_options or [] + self.global_options = global_options or [] + + def _build_one(self, req, output_dir, python_tag=None): + """Build one wheel. + + :return: The filename of the built wheel, or None if the build failed. + """ + tempd = tempfile.mkdtemp('pip-wheel-') + try: + if self.__build_one(req, tempd, python_tag=python_tag): + try: + wheel_name = os.listdir(tempd)[0] + wheel_path = os.path.join(output_dir, wheel_name) + shutil.move(os.path.join(tempd, wheel_name), wheel_path) + logger.info('Stored in directory: %s', output_dir) + return wheel_path + except: + pass + # Ignore return, we can't do anything else useful. + self._clean_one(req) + return None + finally: + rmtree(tempd) + + def _base_setup_args(self, req): + return [ + sys.executable, "-u", '-c', + SETUPTOOLS_SHIM % req.setup_py + ] + list(self.global_options) + + def __build_one(self, req, tempd, python_tag=None): + base_args = self._base_setup_args(req) + + spin_message = 'Running setup.py bdist_wheel for %s' % (req.name,) + with open_spinner(spin_message) as spinner: + logger.debug('Destination directory: %s', tempd) + wheel_args = base_args + ['bdist_wheel', '-d', tempd] \ + + self.build_options + + if python_tag is not None: + wheel_args += ["--python-tag", python_tag] + + try: + call_subprocess(wheel_args, cwd=req.setup_py_dir, + show_stdout=False, spinner=spinner) + return True + except: + spinner.finish("error") + logger.error('Failed building wheel for %s', req.name) + return False + + def _clean_one(self, req): + base_args = self._base_setup_args(req) + + logger.info('Running setup.py clean for %s', req.name) + clean_args = base_args + ['clean', '--all'] try: - call_subprocess(wheel_args, cwd=req.source_dir, show_stdout=False) + call_subprocess(clean_args, cwd=req.source_dir, show_stdout=False) return True except: - logger.error('Failed building wheel for %s' % req.name) + logger.error('Failed cleaning build dir for %s', req.name) return False - def build(self): - """Build wheels.""" + def build(self, autobuilding=False): + """Build wheels. - #unpack and constructs req set + :param unpack: If True, replace the sdist we built from with the + newly built wheel, in preparation for installation. + :return: True if all the wheels built correctly. + """ + assert self._wheel_dir or (autobuilding and self._cache_root) + # unpack sdists and constructs req set self.requirement_set.prepare_files(self.finder) reqset = self.requirement_set.requirements.values() - buildset = [req for req in reqset if not req.is_wheel] + buildset = [] + for req in reqset: + if req.constraint: + continue + if req.is_wheel: + if not autobuilding: + logger.info( + 'Skipping %s, due to already being wheel.', req.name) + elif autobuilding and req.editable: + pass + elif autobuilding and req.link and not req.link.is_artifact: + pass + elif autobuilding and not req.source_dir: + pass + else: + if autobuilding: + link = req.link + base, ext = link.splitext() + if pip.index.egg_info_matches(base, None, link) is None: + # Doesn't look like a package - don't autobuild a wheel + # because we'll have no way to lookup the result sanely + continue + if "binary" not in pip.index.fmt_ctl_formats( + self.finder.format_control, + canonicalize_name(req.name)): + logger.info( + "Skipping bdist_wheel for %s, due to binaries " + "being disabled for it.", req.name) + continue + buildset.append(req) if not buildset: - return + return True - #build the wheels - logger.notify( - 'Building wheels for collected packages: %s' % - ','.join([req.name for req in buildset]) + # Build the wheels. + logger.info( + 'Building wheels for collected packages: %s', + ', '.join([req.name for req in buildset]), ) - logger.indent += 2 - build_success, build_failure = [], [] - for req in buildset: - if self._build_one(req): - build_success.append(req) - else: - build_failure.append(req) - logger.indent -= 2 - - #notify sucess/failure + with indent_log(): + build_success, build_failure = [], [] + for req in buildset: + python_tag = None + if autobuilding: + python_tag = pep425tags.implementation_tag + output_dir = _cache_for_link(self._cache_root, req.link) + try: + ensure_dir(output_dir) + except OSError as e: + logger.warning("Building wheel for %s failed: %s", + req.name, e) + build_failure.append(req) + continue + else: + output_dir = self._wheel_dir + wheel_file = self._build_one( + req, output_dir, + python_tag=python_tag, + ) + if wheel_file: + build_success.append(req) + if autobuilding: + # XXX: This is mildly duplicative with prepare_files, + # but not close enough to pull out to a single common + # method. + # The code below assumes temporary source dirs - + # prevent it doing bad things. + if req.source_dir and not os.path.exists(os.path.join( + req.source_dir, PIP_DELETE_MARKER_FILENAME)): + raise AssertionError( + "bad source dir - missing marker") + # Delete the source we built the wheel from + req.remove_temporary_source() + # set the build directory again - name is known from + # the work prepare_files did. + req.source_dir = req.build_location( + self.requirement_set.build_dir) + # Update the link for this. + req.link = pip.index.Link( + path_to_url(wheel_file)) + assert req.link.is_wheel + # extract the wheel into the dir + unpack_url( + req.link, req.source_dir, None, False, + session=self.requirement_set.session) + else: + build_failure.append(req) + + # notify success/failure if build_success: - logger.notify('Successfully built %s' % ' '.join([req.name for req in build_success])) + logger.info( + 'Successfully built %s', + ' '.join([req.name for req in build_success]), + ) if build_failure: - logger.notify('Failed to build %s' % ' '.join([req.name for req in build_failure])) + logger.info( + 'Failed to build %s', + ' '.join([req.name for req in build_failure]), + ) + # Return True if all builds were successful + return len(build_failure) == 0 diff --git a/setup.cfg b/setup.cfg index a6b1293d85b..cdc1dfe00f7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [pytest] addopts = --ignore pip/_vendor --ignore tests/tests_cache -[wheel] +[bdist_wheel] universal=1 diff --git a/setup.py b/setup.py index 7ad4e4faa3b..6de49987ec9 100644 --- a/setup.py +++ b/setup.py @@ -6,6 +6,7 @@ from setuptools import setup, find_packages from setuptools.command.test import test as TestCommand + here = os.path.abspath(os.path.dirname(__file__)) @@ -18,15 +19,15 @@ def finalize_options(self): self.test_suite = True def run_tests(self): - #import here, cause outside the eggs aren't loaded + # import here, cause outside the eggs aren't loaded import pytest sys.exit(pytest.main(self.test_args)) def read(*parts): - # intentionally *not* adding an encoding option to open - # see here: https://github.com/pypa/virtualenv/issues/201#issuecomment-3145690 + # intentionally *not* adding an encoding option to open, See: + # https://github.com/pypa/virtualenv/issues/201#issuecomment-3145690 return codecs.open(os.path.join(here, *parts), 'r').read() @@ -38,45 +39,55 @@ def find_version(*file_paths): return version_match.group(1) raise RuntimeError("Unable to find version string.") -long_description = "\n" + "\n".join([read('PROJECT.txt'), - read('docs', 'quickstart.rst')]) - -tests_require = ['pytest', 'virtualenv>=1.10', 'scripttest>=1.3', 'mock'] - -setup(name="pip", - version=find_version('pip', '__init__.py'), - description="A tool for installing and managing Python packages.", - long_description=long_description, - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Topic :: Software Development :: Build Tools', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.6', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.1', - 'Programming Language :: Python :: 3.2', - 'Programming Language :: Python :: 3.3', - ], - keywords='easy_install distutils setuptools egg virtualenv', - author='The pip developers', - author_email='python-virtualenv@groups.google.com', - url='https://pip.pypa.io/', - license='MIT', - packages=find_packages(exclude=["contrib", "docs", "tests*"]), - package_data={ - 'pip._vendor.requests': ['*.pem'], - 'pip._vendor.distlib._backport': ['sysconfig.cfg'], - 'pip._vendor.distlib': ['t32.exe', 't64.exe', 'w32.exe', 'w64.exe'], - }, - entry_points=dict(console_scripts=['pip=pip:main', 'pip%s=pip:main' % sys.version[:1], - 'pip%s=pip:main' % sys.version[:3]]), - tests_require=tests_require, - zip_safe=False, - extras_require={ - 'testing': tests_require, - }, - cmdclass = {'test': PyTest}, +long_description = read('README.rst') + +tests_require = ['pytest', 'virtualenv>=1.10', 'scripttest>=1.3', 'mock', + 'pretend'] + + +setup( + name="pip", + version=find_version("pip", "__init__.py"), + description="The PyPA recommended tool for installing Python packages.", + long_description=long_description, + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Topic :: Software Development :: Build Tools", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.6", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: Implementation :: PyPy" + ], + keywords='easy_install distutils setuptools egg virtualenv', + author='The pip developers', + author_email='python-virtualenv@groups.google.com', + url='https://pip.pypa.io/', + license='MIT', + packages=find_packages(exclude=["contrib", "docs", "tests*", "tasks"]), + package_data={ + "pip._vendor.certifi": ["*.pem"], + "pip._vendor.requests": ["*.pem"], + "pip._vendor.distlib._backport": ["sysconfig.cfg"], + "pip._vendor.distlib": ["t32.exe", "t64.exe", "w32.exe", "w64.exe"], + }, + entry_points={ + "console_scripts": [ + "pip=pip:main", + "pip%s=pip:main" % sys.version[:1], + "pip%s=pip:main" % sys.version[:3], + ], + }, + tests_require=tests_require, + zip_safe=False, + python_requires='>=2.6,!=3.0.*,!=3.1.*,!=3.2.*', + extras_require={ + 'testing': tests_require, + }, + cmdclass={'test': PyTest}, ) diff --git a/tasks/__init__.py b/tasks/__init__.py new file mode 100644 index 00000000000..250fe9c4b9b --- /dev/null +++ b/tasks/__init__.py @@ -0,0 +1,5 @@ +import invoke + +from . import generate + +ns = invoke.Collection(generate) diff --git a/tasks/generate.py b/tasks/generate.py new file mode 100644 index 00000000000..7a68cd0c18a --- /dev/null +++ b/tasks/generate.py @@ -0,0 +1,28 @@ +import io + +import invoke + + +@invoke.task +def authors(): + print("[generate.authors] Generating AUTHORS") + + # Get our list of authors + print("[generate.authors] Collecting author names") + r = invoke.run("git log --use-mailmap --format'=%aN <%aE>'", hide=True) + authors = [] + seen_authors = set() + for author in r.stdout.splitlines(): + author = author.strip() + if author.lower() not in seen_authors: + seen_authors.add(author.lower()) + authors.append(author) + + # Sort our list of Authors by their case insensitive name + authors = sorted(authors, key=lambda x: x.lower()) + + # Write our authors to the AUTHORS file + print("[generate.authors] Writing AUTHORS") + with io.open("AUTHORS.txt", "w", encoding="utf8") as fp: + fp.write(u"\n".join(authors)) + fp.write(u"\n") diff --git a/tests/conftest.py b/tests/conftest.py index ea4d014ee70..eb8de802a6a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,32 +1,122 @@ +import os import shutil -import py import pytest +from pip.utils import appdirs + from tests.lib import SRC_DIR, TestData from tests.lib.path import Path from tests.lib.scripttest import PipTestEnvironment from tests.lib.venv import VirtualEnvironment -@pytest.fixture -def tmpdir(request): +def pytest_collection_modifyitems(items): + for item in items: + if not hasattr(item, 'module'): # e.g.: DoctestTextfile + continue + module_path = os.path.relpath( + item.module.__file__, + os.path.commonprefix([__file__, item.module.__file__]), + ) + + module_root_dir = module_path.split(os.pathsep)[0] + if (module_root_dir.startswith("functional") or + module_root_dir.startswith("integration") or + module_root_dir.startswith("lib")): + item.add_marker(pytest.mark.integration) + elif module_root_dir.startswith("unit"): + item.add_marker(pytest.mark.unit) + + # We don't want to allow using the script resource if this is a + # unit test, as unit tests should not need all that heavy lifting + if set(getattr(item, "funcargnames", [])) & set(["script"]): + raise RuntimeError( + "Cannot use the ``script`` funcarg in a unit test: " + "(filename = {0}, item = {1})".format(module_path, item) + ) + else: + raise RuntimeError( + "Unknown test type (filename = {0})".format(module_path) + ) + + +@pytest.yield_fixture +def tmpdir(tmpdir): """ Return a temporary directory path object which is unique to each test function invocation, created as a sub directory of the base temporary directory. The returned object is a ``tests.lib.path.Path`` object. - This is taken from pytest itself but modified to return our typical - path object instead of py.path.local. + This uses the built-in tmpdir fixture from pytest itself but modified + to return our typical path object instead of py.path.local as well as + deleting the temporary directories at the end of each test case. + """ + assert tmpdir.isdir() + yield Path(str(tmpdir)) + # Clear out the temporary directory after the test has finished using it. + # This should prevent us from needing a multiple gigabyte temporary + # directory while running the tests. + tmpdir.remove(ignore_errors=True) + + +@pytest.fixture(autouse=True) +def isolate(tmpdir): + """ + Isolate our tests so that things like global configuration files and the + like do not affect our test results. + + We use an autouse function scoped fixture because we want to ensure that + every test has it's own isolated home directory. """ - name = request.node.name - name = py.std.re.sub("[\W]", "_", name) - tmp = request.config._tmpdirhandler.mktemp(name, numbered=True) - return Path(tmp) + # TODO: Ensure Windows will respect $HOME, including for the cache + # directory + + # TODO: Figure out how to isolate from *system* level configuration files + # as well as user level configuration files. + + # Create a directory to use as our home location. + home_dir = os.path.join(str(tmpdir), "home") + os.makedirs(home_dir) + + # Create a directory to use as a fake root + fake_root = os.path.join(str(tmpdir), "fake-root") + os.makedirs(fake_root) + + # Set our home directory to our temporary directory, this should force all + # of our relative configuration files to be read from here instead of the + # user's actual $HOME directory. + os.environ["HOME"] = home_dir + + # Isolate ourselves from XDG directories + os.environ["XDG_DATA_HOME"] = os.path.join(home_dir, ".local", "share") + os.environ["XDG_CONFIG_HOME"] = os.path.join(home_dir, ".config") + os.environ["XDG_CACHE_HOME"] = os.path.join(home_dir, ".cache") + os.environ["XDG_RUNTIME_DIR"] = os.path.join(home_dir, ".runtime") + os.environ["XDG_DATA_DIRS"] = ":".join([ + os.path.join(fake_root, "usr", "local", "share"), + os.path.join(fake_root, "usr", "share"), + ]) + os.environ["XDG_CONFIG_DIRS"] = os.path.join(fake_root, "etc", "xdg") + + # Configure git, because without an author name/email git will complain + # and cause test failures. + os.environ["GIT_CONFIG_NOSYSTEM"] = "1" + os.environ["GIT_AUTHOR_NAME"] = "pip" + os.environ["GIT_AUTHOR_EMAIL"] = "pypa-dev@googlegroups.com" + + # We want to disable the version check from running in the tests + os.environ["PIP_DISABLE_PIP_VERSION_CHECK"] = "true" + + os.makedirs(os.path.join(home_dir, ".config", "git")) + with open(os.path.join(home_dir, ".config", "git", "config"), "wb") as fp: + fp.write( + b"[user]\n\tname = pip\n\temail = pypa-dev@googlegroups.com\n" + ) @pytest.fixture -def virtualenv(tmpdir, monkeypatch): +def virtualenv(tmpdir, monkeypatch, isolate): """ Return a virtual environment which is unique to each test function invocation created inside of a sub directory of the test function's @@ -40,9 +130,12 @@ def virtualenv(tmpdir, monkeypatch): # Copy over our source tree so that each virtual environment is self # contained pip_src = tmpdir.join("pip_src").abspath - shutil.copytree(SRC_DIR, pip_src, + shutil.copytree( + SRC_DIR, + pip_src, ignore=shutil.ignore_patterns( - "*.pyc", "tests", "pip.egg-info", "build", "dist", ".tox", + "*.pyc", "__pycache__", "contrib", "docs", "tasks", "*.txt", + "tests", "pip.egg-info", "build", "dist", ".tox", ".git", ), ) @@ -52,6 +145,10 @@ def virtualenv(tmpdir, monkeypatch): pip_source_dir=pip_src, ) + # Clean out our cache: creating the venv injects wheels into it. + if os.path.exists(appdirs.user_cache_dir("pip")): + shutil.rmtree(appdirs.user_cache_dir("pip")) + # Undo our monkeypatching of shutil monkeypatch.undo() diff --git a/tests/data/indexes/externals/bar/index.html b/tests/data/indexes/externals/bar/index.html deleted file mode 100644 index 6a8cf05f181..00000000000 --- a/tests/data/indexes/externals/bar/index.html +++ /dev/null @@ -1,11 +0,0 @@ -<html> - <head> - <meta name="api-version" value="2" /> - </head> - <body> - <a rel="internal" href="bar-1.0.tar.gz#md5=7b8a4e19dfd3be354046b97f62cefc09">bar-1.0.tar.gz</a> - <a href="../foo/bar-2.0.tar.gz#md5=d41d8cd98f00b204e9800998ecf8427e">bar-2.0.tar.gz</a> - <a href="../foo/bar-3.0.tar.gz">bar-3.0.tar.gz</a> - <a rel="download" href="../bar2/"> - </body> -</html> diff --git a/tests/data/indexes/externals/bar2/index.html b/tests/data/indexes/externals/bar2/index.html deleted file mode 100644 index db09bab643d..00000000000 --- a/tests/data/indexes/externals/bar2/index.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> - <body> - <a href="bar-4.0.tar.gz">bar-4.0.tar.gz</a> - </body> -</html> diff --git a/tests/data/packages/BrokenEmitsUTF8/setup.py b/tests/data/packages/BrokenEmitsUTF8/setup.py index 989cc2a0bb5..067d580fb7e 100644 --- a/tests/data/packages/BrokenEmitsUTF8/setup.py +++ b/tests/data/packages/BrokenEmitsUTF8/setup.py @@ -20,6 +20,6 @@ class FakeError(Exception): raise FakeError('this package designed to fail on install') setup(name='broken', - version='0.2broken', + version='0.2', py_modules=['broken'], ) diff --git a/tests/data/packages/FSPkg/setup.cfg b/tests/data/packages/FSPkg/setup.cfg deleted file mode 100644 index 01bb954499e..00000000000 --- a/tests/data/packages/FSPkg/setup.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[egg_info] -tag_build = dev -tag_svn_revision = true diff --git a/tests/data/packages/FSPkg/setup.py b/tests/data/packages/FSPkg/setup.py index c94ead942fb..1215408c877 100644 --- a/tests/data/packages/FSPkg/setup.py +++ b/tests/data/packages/FSPkg/setup.py @@ -1,6 +1,6 @@ from setuptools import setup, find_packages -version = '0.1' +version = '0.1dev' setup(name='FSPkg', version=version, diff --git a/tests/data/packages/INITools-0.2.tar.gz b/tests/data/packages/INITools-0.2.tar.gz new file mode 100644 index 00000000000..bda2eb28500 Binary files /dev/null and b/tests/data/packages/INITools-0.2.tar.gz differ diff --git a/tests/data/packages/LocalEnvironMarker/.gitignore b/tests/data/packages/LocalEnvironMarker/.gitignore new file mode 100644 index 00000000000..02fa6ede3a2 --- /dev/null +++ b/tests/data/packages/LocalEnvironMarker/.gitignore @@ -0,0 +1 @@ +/LocalEnvironMarker.egg-info diff --git a/tests/data/packages/LocalEnvironMarker/localenvironmarker/__init__.py b/tests/data/packages/LocalEnvironMarker/localenvironmarker/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/data/packages/LocalEnvironMarker/setup.py b/tests/data/packages/LocalEnvironMarker/setup.py new file mode 100644 index 00000000000..3ba33bf358e --- /dev/null +++ b/tests/data/packages/LocalEnvironMarker/setup.py @@ -0,0 +1,29 @@ +import os +from setuptools import setup, find_packages + + +def path_to_url(path): + """ + Convert a path to URI. The path will be made absolute and + will not have quoted path parts. + """ + path = os.path.normpath(os.path.abspath(path)) + drive, path = os.path.splitdrive(path) + filepath = path.split(os.path.sep) + url = '/'.join(filepath) + if drive: + return 'file:///' + drive + url + return 'file://' +url + + +HERE = os.path.dirname(__file__) +DEP_PATH = os.path.join(HERE, '..', '..', 'indexes', 'simple', 'simple') +DEP_URL = path_to_url(DEP_PATH) + +setup( + name='LocalEnvironMarker', + version='0.0.1', + packages=find_packages(), + extras_require={":python_version == '2.7' or python_version == '3.4'": ['simple'] }, + dependency_links=[DEP_URL] +) diff --git a/tests/data/packages/LocalExtras-0.0.2/.gitignore b/tests/data/packages/LocalExtras-0.0.2/.gitignore new file mode 100644 index 00000000000..bc186a3f1cf --- /dev/null +++ b/tests/data/packages/LocalExtras-0.0.2/.gitignore @@ -0,0 +1 @@ +/LocalExtras-0.0.2.egg-info diff --git a/tests/data/packages/LocalExtras-0.0.2/localextras/__init__.py b/tests/data/packages/LocalExtras-0.0.2/localextras/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/data/packages/LocalExtras-0.0.2/setup.py b/tests/data/packages/LocalExtras-0.0.2/setup.py new file mode 100644 index 00000000000..a32e344f5f0 --- /dev/null +++ b/tests/data/packages/LocalExtras-0.0.2/setup.py @@ -0,0 +1,30 @@ +import os +from setuptools import setup, find_packages + + +def path_to_url(path): + """ + Convert a path to URI. The path will be made absolute and + will not have quoted path parts. + """ + path = os.path.normpath(os.path.abspath(path)) + drive, path = os.path.splitdrive(path) + filepath = path.split(os.path.sep) + url = '/'.join(filepath) + if drive: + return 'file:///' + drive + url + return 'file://' +url + + +HERE = os.path.dirname(__file__) +DEP_PATH = os.path.join(HERE, '..', '..', 'indexes', 'simple', 'simple') +DEP_URL = path_to_url(DEP_PATH) + +setup( + name='LocalExtras', + version='0.0.2', + packages=find_packages(), + install_requires=['simple==1.0'], + extras_require={ 'bar': ['simple==2.0'], 'baz': ['singlemodule'] }, + dependency_links=[DEP_URL] +) diff --git a/tests/data/packages/LocalExtras/setup.py b/tests/data/packages/LocalExtras/setup.py index 7c2839819a8..fea6e16039a 100644 --- a/tests/data/packages/LocalExtras/setup.py +++ b/tests/data/packages/LocalExtras/setup.py @@ -24,6 +24,6 @@ def path_to_url(path): name='LocalExtras', version='0.0.1', packages=find_packages(), - extras_require={ 'bar': ['simple'] }, + extras_require={ 'bar': ['simple'], 'baz': ['singlemodule'] }, dependency_links=[DEP_URL] ) diff --git a/tests/data/packages/README.txt b/tests/data/packages/README.txt index 3fd0c69bd50..c36d77f1b0e 100644 --- a/tests/data/packages/README.txt +++ b/tests/data/packages/README.txt @@ -44,7 +44,7 @@ for testing finder logic when name *contains* the name of the package specified HackedEggInfo ------------- -has it's own egg_info class +has its own egg_info class LineEndings ----------- @@ -71,6 +71,18 @@ priority-* ---------- used for testing wheel priority over sdists +TopoRequires[1234][-0.0.1.tar.gz] +-------------------------------- + +These are used for testing topological handling of requirements: we have +TopoRequires, which is install-required by TopoRequires2 and TopoRequires3 +and finally TopoRequires4 which install-requires both TopoRequires2 and 3 +and also install-Requires TopoRequires. +This creates a diamond where no matter which way we walk without topological +awareness we'll end up attempting to install TopoRequires after one of +TopoRequires2, TopoRequires3 or TopoRequires4. (prefix iteration works as its +topological, suffix iteration likewise, infix breaks). + simple[2]-[123].0.tar.gz ------------------------ contains "simple[2]" package; good for basic testing and version logic. @@ -88,6 +100,11 @@ meta-1.0-py2.py3-none-any.whl -------------------------------------------------- Is an empty package which install_requires the simple and simple2 packages. +requires_simple_extra-0.1-py2.py3-none-any.whl +---------------------------------------------- +requires_simple_extra[extra] requires simple==1.0 - - +requires_wheelbroken_upper +-------------------------- +Requires wheelbroken and upper - used for testing implicit wheel building +during install. diff --git a/tests/data/packages/SetupPyLatin1/setup.py b/tests/data/packages/SetupPyLatin1/setup.py index 89b87b37fdb..3ca77c00c96 100644 --- a/tests/data/packages/SetupPyLatin1/setup.py +++ b/tests/data/packages/SetupPyLatin1/setup.py @@ -3,5 +3,5 @@ from distutils.core import setup setup(name="SetupPyUTF8", - author="Sal Ibarra Corretg", + author=u"Sal Ibarra Corretg", ) diff --git a/tests/data/packages/TopoRequires-0.0.1.tar.gz b/tests/data/packages/TopoRequires-0.0.1.tar.gz new file mode 100644 index 00000000000..58182befe11 Binary files /dev/null and b/tests/data/packages/TopoRequires-0.0.1.tar.gz differ diff --git a/tests/data/packages/TopoRequires2-0.0.1.tar.gz b/tests/data/packages/TopoRequires2-0.0.1.tar.gz new file mode 100644 index 00000000000..1712f5ba1f0 Binary files /dev/null and b/tests/data/packages/TopoRequires2-0.0.1.tar.gz differ diff --git a/tests/data/packages/TopoRequires3-0.0.1.tar.gz b/tests/data/packages/TopoRequires3-0.0.1.tar.gz new file mode 100644 index 00000000000..165ad757f8f Binary files /dev/null and b/tests/data/packages/TopoRequires3-0.0.1.tar.gz differ diff --git a/tests/data/packages/TopoRequires4-0.0.1.tar.gz b/tests/data/packages/TopoRequires4-0.0.1.tar.gz new file mode 100644 index 00000000000..3b71b86f239 Binary files /dev/null and b/tests/data/packages/TopoRequires4-0.0.1.tar.gz differ diff --git a/tests/data/packages/compilewheel-1.0-py2.py3-none-any.whl b/tests/data/packages/compilewheel-1.0-py2.py3-none-any.whl new file mode 100644 index 00000000000..8d82bb573bb Binary files /dev/null and b/tests/data/packages/compilewheel-1.0-py2.py3-none-any.whl differ diff --git a/tests/data/packages/console_scripts_uppercase-1.0-py2.py3-none-any.whl b/tests/data/packages/console_scripts_uppercase-1.0-py2.py3-none-any.whl new file mode 100644 index 00000000000..f1937b172d4 Binary files /dev/null and b/tests/data/packages/console_scripts_uppercase-1.0-py2.py3-none-any.whl differ diff --git a/tests/data/packages/requires_simple_extra-0.1-py2.py3-none-any.whl b/tests/data/packages/requires_simple_extra-0.1-py2.py3-none-any.whl new file mode 100644 index 00000000000..c618eaa06d8 Binary files /dev/null and b/tests/data/packages/requires_simple_extra-0.1-py2.py3-none-any.whl differ diff --git a/tests/data/packages/requires_wheelbroken_upper/requires_wheelbroken_upper/__init__.py b/tests/data/packages/requires_wheelbroken_upper/requires_wheelbroken_upper/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/data/packages/requires_wheelbroken_upper/setup.py b/tests/data/packages/requires_wheelbroken_upper/setup.py new file mode 100644 index 00000000000..255cf2219eb --- /dev/null +++ b/tests/data/packages/requires_wheelbroken_upper/setup.py @@ -0,0 +1,5 @@ +import setuptools +setuptools.setup( + name="requires_wheelbroken_upper", + version="0", + install_requires=['wheelbroken', 'upper']) diff --git a/tests/data/packages/simplebundle.pybundle b/tests/data/packages/simplebundle.pybundle deleted file mode 100644 index e8bb889971e..00000000000 Binary files a/tests/data/packages/simplebundle.pybundle and /dev/null differ diff --git a/tests/data/packages/simplewheel-1.0-py2.py3-none-any.whl b/tests/data/packages/simplewheel-1.0-py2.py3-none-any.whl new file mode 100644 index 00000000000..0e6b91eea85 Binary files /dev/null and b/tests/data/packages/simplewheel-1.0-py2.py3-none-any.whl differ diff --git a/tests/data/packages/simplewheel-2.0-py2.py3-none-any.whl b/tests/data/packages/simplewheel-2.0-py2.py3-none-any.whl new file mode 100644 index 00000000000..acf0c56cfd9 Binary files /dev/null and b/tests/data/packages/simplewheel-2.0-py2.py3-none-any.whl differ diff --git a/tests/data/packages/singlemodule-0.0.0.tar.gz b/tests/data/packages/singlemodule-0.0.0.tar.gz new file mode 100644 index 00000000000..766d168d8f8 Binary files /dev/null and b/tests/data/packages/singlemodule-0.0.0.tar.gz differ diff --git a/tests/data/packages/singlemodule-0.0.1.tar.gz b/tests/data/packages/singlemodule-0.0.1.tar.gz new file mode 100644 index 00000000000..811c35c32a9 Binary files /dev/null and b/tests/data/packages/singlemodule-0.0.1.tar.gz differ diff --git a/tests/data/packages/singlemodule-0.0.1.tar.lzma b/tests/data/packages/singlemodule-0.0.1.tar.lzma new file mode 100644 index 00000000000..c5e954b5ee2 Binary files /dev/null and b/tests/data/packages/singlemodule-0.0.1.tar.lzma differ diff --git a/tests/data/packages/singlemodule-0.0.1.tar.xz b/tests/data/packages/singlemodule-0.0.1.tar.xz new file mode 100644 index 00000000000..c21c69ff6c5 Binary files /dev/null and b/tests/data/packages/singlemodule-0.0.1.tar.xz differ diff --git a/tests/data/packages/symlinks/setup.cfg b/tests/data/packages/symlinks/setup.cfg index 01bb954499e..3c2fdf51144 100644 --- a/tests/data/packages/symlinks/setup.cfg +++ b/tests/data/packages/symlinks/setup.cfg @@ -1,3 +1,2 @@ [egg_info] tag_build = dev -tag_svn_revision = true diff --git a/tests/data/packages/wheelbrokenafter-0.1.tar.gz b/tests/data/packages/wheelbrokenafter-0.1.tar.gz new file mode 100644 index 00000000000..f227ae13c07 Binary files /dev/null and b/tests/data/packages/wheelbrokenafter-0.1.tar.gz differ diff --git a/tests/data/packages3/dinner/Dinner-1.0.tar.gz b/tests/data/packages3/dinner/Dinner-1.0.tar.gz new file mode 100644 index 00000000000..1060b1bfbb2 Binary files /dev/null and b/tests/data/packages3/dinner/Dinner-1.0.tar.gz differ diff --git a/tests/data/packages3/dinner/Dinner-2.0.tar.gz b/tests/data/packages3/dinner/Dinner-2.0.tar.gz new file mode 100644 index 00000000000..6da050bd0fa Binary files /dev/null and b/tests/data/packages3/dinner/Dinner-2.0.tar.gz differ diff --git a/tests/data/packages3/dinner/index.html b/tests/data/packages3/dinner/index.html new file mode 100644 index 00000000000..6afabea66f1 --- /dev/null +++ b/tests/data/packages3/dinner/index.html @@ -0,0 +1,8 @@ +<html><head><title>PyPi Mirror + +

PyPi Mirror

+

For testing --index-url with a file:// url for the index

+ Dinner-1.0.tar.gz
+ Dinner-2.0.tar.gz
+ + diff --git a/tests/data/packages3/index.html b/tests/data/packages3/index.html new file mode 100644 index 00000000000..a59811ba05d --- /dev/null +++ b/tests/data/packages3/index.html @@ -0,0 +1,8 @@ +PyPi Mirror + +

PyPi Mirror

+

For testing --index-url with a file:// url for the index

+ requiredinner
+ Dinner
+ + diff --git a/tests/data/packages3/requiredinner/index.html b/tests/data/packages3/requiredinner/index.html new file mode 100644 index 00000000000..52701cd5c8d --- /dev/null +++ b/tests/data/packages3/requiredinner/index.html @@ -0,0 +1,7 @@ +PyPi Mirror + +

PyPi Mirror

+

For testing --index-url with a file:// url for the index

+ requiredinner=1.0.tar.gz
+ + diff --git a/tests/data/packages3/requiredinner/requiredinner-1.0.tar.gz b/tests/data/packages3/requiredinner/requiredinner-1.0.tar.gz new file mode 100644 index 00000000000..13c4adaab09 Binary files /dev/null and b/tests/data/packages3/requiredinner/requiredinner-1.0.tar.gz differ diff --git a/tests/data/reqfiles/README.txt b/tests/data/reqfiles/README.txt new file mode 100644 index 00000000000..be4f3830fd7 --- /dev/null +++ b/tests/data/reqfiles/README.txt @@ -0,0 +1,9 @@ +supported_options.txt +--------------------- + +Contains --no-use-wheel. + +supported_options2.txt +---------------------- + +Contains --no-binary and --only-binary options. diff --git a/tests/data/reqfiles/supported_options.txt b/tests/data/reqfiles/supported_options.txt deleted file mode 100644 index 0e9989f499c..00000000000 --- a/tests/data/reqfiles/supported_options.txt +++ /dev/null @@ -1,2 +0,0 @@ -# requirement file containing various supported options ---use-wheel diff --git a/tests/data/reqfiles/supported_options2.txt b/tests/data/reqfiles/supported_options2.txt new file mode 100644 index 00000000000..25fa4afdba4 --- /dev/null +++ b/tests/data/reqfiles/supported_options2.txt @@ -0,0 +1,5 @@ +# default is no constraints +# We're not testing the format control logic here, just that the options are +# accepted +--no-binary fred +--only-binary wilma diff --git a/tests/data/src/TopoRequires/setup.py b/tests/data/src/TopoRequires/setup.py new file mode 100644 index 00000000000..6cd29a7b656 --- /dev/null +++ b/tests/data/src/TopoRequires/setup.py @@ -0,0 +1,7 @@ +from setuptools import setup + +setup( + name='TopoRequires', + version='0.0.1', + packages=['toporequires'], +) diff --git a/tests/data/src/TopoRequires/toporequires/__init__.py b/tests/data/src/TopoRequires/toporequires/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/data/src/TopoRequires2/setup.cfg.pending b/tests/data/src/TopoRequires2/setup.cfg.pending new file mode 100644 index 00000000000..d42f01a5303 --- /dev/null +++ b/tests/data/src/TopoRequires2/setup.cfg.pending @@ -0,0 +1,4 @@ +[metadata] +name = TopoRequires2 +install-requires = + TopoRequires diff --git a/tests/data/src/TopoRequires2/setup.py b/tests/data/src/TopoRequires2/setup.py new file mode 100644 index 00000000000..019f43cb231 --- /dev/null +++ b/tests/data/src/TopoRequires2/setup.py @@ -0,0 +1,8 @@ +from setuptools import setup + +setup( + name='TopoRequires2', + version='0.0.1', + packages=['toporequires2'], + install_requires=['TopoRequires'], +) diff --git a/tests/data/src/TopoRequires2/toporequires2/__init__.py b/tests/data/src/TopoRequires2/toporequires2/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/data/src/TopoRequires3/setup.cfg.pending b/tests/data/src/TopoRequires3/setup.cfg.pending new file mode 100644 index 00000000000..cdd67122b3d --- /dev/null +++ b/tests/data/src/TopoRequires3/setup.cfg.pending @@ -0,0 +1,4 @@ +[metadata] +name = TopoRequires3 +install-requires = + TopoRequires diff --git a/tests/data/src/TopoRequires3/setup.py b/tests/data/src/TopoRequires3/setup.py new file mode 100644 index 00000000000..772ed618e3c --- /dev/null +++ b/tests/data/src/TopoRequires3/setup.py @@ -0,0 +1,8 @@ +from setuptools import setup + +setup( + name='TopoRequires3', + version='0.0.1', + packages=['toporequires3'], + install_requires=['TopoRequires'], +) diff --git a/tests/data/src/TopoRequires3/toporequires3/__init__.py b/tests/data/src/TopoRequires3/toporequires3/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/data/src/TopoRequires4/setup.cfg.pending b/tests/data/src/TopoRequires4/setup.cfg.pending new file mode 100644 index 00000000000..2fc2c551f89 --- /dev/null +++ b/tests/data/src/TopoRequires4/setup.cfg.pending @@ -0,0 +1,6 @@ +[metadata] +name = TopoRequires4 +install-requires = + TopoRequires2 + TopoRequires + TopoRequires3 diff --git a/tests/data/src/TopoRequires4/setup.py b/tests/data/src/TopoRequires4/setup.py new file mode 100644 index 00000000000..e276f55a240 --- /dev/null +++ b/tests/data/src/TopoRequires4/setup.py @@ -0,0 +1,8 @@ +from setuptools import setup + +setup( + name='TopoRequires4', + version='0.0.1', + packages=['toporequires4'], + install_requires=['TopoRequires2', 'TopoRequires', 'TopoRequires3'], +) diff --git a/tests/data/src/TopoRequires4/toporequires4/__init__.py b/tests/data/src/TopoRequires4/toporequires4/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/data/src/chattymodule/chattymodule.py b/tests/data/src/chattymodule/chattymodule.py new file mode 100644 index 00000000000..c8f10649946 --- /dev/null +++ b/tests/data/src/chattymodule/chattymodule.py @@ -0,0 +1,3 @@ +def main(): + """Entry point for the application script""" + print("Call your main application code here") diff --git a/tests/data/src/chattymodule/setup.cfg b/tests/data/src/chattymodule/setup.cfg new file mode 100644 index 00000000000..79bc67848ff --- /dev/null +++ b/tests/data/src/chattymodule/setup.cfg @@ -0,0 +1,5 @@ +[bdist_wheel] +# This flag says that the code is written to work on both Python 2 and Python +# 3. If at all possible, it is good practice to do this. If you cannot, you +# will need to generate wheels for each Python version that you support. +universal=1 diff --git a/tests/data/src/chattymodule/setup.py b/tests/data/src/chattymodule/setup.py new file mode 100644 index 00000000000..abc56b16119 --- /dev/null +++ b/tests/data/src/chattymodule/setup.py @@ -0,0 +1,19 @@ +# A chatty setup.py for testing pip subprocess output handling + +from setuptools import setup +import os +import sys + +print("HELLO FROM CHATTYMODULE %s" % (sys.argv[1],)) +print(os.environ) +print(sys.argv) +if "--fail" in sys.argv: + print("I DIE, I DIE") + sys.exit(1) + +setup( + name="chattymodule", + version='0.0.1', + description="A sample Python project with a single module", + py_modules=['chattymodule'], +) diff --git a/tests/data/src/compilewheel/setup.cfg b/tests/data/src/compilewheel/setup.cfg new file mode 100644 index 00000000000..79bc67848ff --- /dev/null +++ b/tests/data/src/compilewheel/setup.cfg @@ -0,0 +1,5 @@ +[bdist_wheel] +# This flag says that the code is written to work on both Python 2 and Python +# 3. If at all possible, it is good practice to do this. If you cannot, you +# will need to generate wheels for each Python version that you support. +universal=1 diff --git a/tests/data/src/compilewheel/setup.py b/tests/data/src/compilewheel/setup.py new file mode 100644 index 00000000000..46305d226b6 --- /dev/null +++ b/tests/data/src/compilewheel/setup.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python +from setuptools import setup, find_packages + +setup(name='compilewheel', + version='1.0', + packages=find_packages() + ) diff --git a/tests/data/src/compilewheel/simple/__init__.py b/tests/data/src/compilewheel/simple/__init__.py new file mode 100644 index 00000000000..a5ec68c7132 --- /dev/null +++ b/tests/data/src/compilewheel/simple/__init__.py @@ -0,0 +1,2 @@ +def spam(gen): + yield from gen diff --git a/tests/data/src/requires_simple/requires_simple/__init__.py b/tests/data/src/requires_simple/requires_simple/__init__.py new file mode 100644 index 00000000000..792d6005489 --- /dev/null +++ b/tests/data/src/requires_simple/requires_simple/__init__.py @@ -0,0 +1 @@ +# diff --git a/tests/data/src/requires_simple/setup.py b/tests/data/src/requires_simple/setup.py new file mode 100644 index 00000000000..3f7983b82c7 --- /dev/null +++ b/tests/data/src/requires_simple/setup.py @@ -0,0 +1,6 @@ +from setuptools import setup, find_packages + +setup(name='requires_simple', + version='0.1', + install_requires=['simple==1.0'] + ) diff --git a/tests/data/src/requires_simple_extra/requires_simple_extra.py b/tests/data/src/requires_simple_extra/requires_simple_extra.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/data/src/requires_simple_extra/setup.cfg b/tests/data/src/requires_simple_extra/setup.cfg new file mode 100644 index 00000000000..7c964b49e2d --- /dev/null +++ b/tests/data/src/requires_simple_extra/setup.cfg @@ -0,0 +1,2 @@ +[wheel] +universal=1 diff --git a/tests/data/src/requires_simple_extra/setup.py b/tests/data/src/requires_simple_extra/setup.py new file mode 100644 index 00000000000..b4f8042214e --- /dev/null +++ b/tests/data/src/requires_simple_extra/setup.py @@ -0,0 +1,9 @@ +from setuptools import setup + +setup(name='requires_simple_extra', + version='0.1', + py_modules = ['requires_simple_extra'], + extras_require = { + 'extra' : ['simple==1.0'] + } +) \ No newline at end of file diff --git a/tests/data/src/simplewheel-1.0/setup.py b/tests/data/src/simplewheel-1.0/setup.py new file mode 100644 index 00000000000..97a2210f0a1 --- /dev/null +++ b/tests/data/src/simplewheel-1.0/setup.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python +from setuptools import setup, find_packages + +setup(name='simplewheel', + version='1.0', + packages=find_packages() + ) diff --git a/tests/data/src/simplewheel-1.0/simple/__init__.py b/tests/data/src/simplewheel-1.0/simple/__init__.py new file mode 100644 index 00000000000..792d6005489 --- /dev/null +++ b/tests/data/src/simplewheel-1.0/simple/__init__.py @@ -0,0 +1 @@ +# diff --git a/tests/data/src/simplewheel-2.0/setup.py b/tests/data/src/simplewheel-2.0/setup.py new file mode 100644 index 00000000000..f2b31f6af93 --- /dev/null +++ b/tests/data/src/simplewheel-2.0/setup.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python +from setuptools import setup, find_packages + +setup(name='simplewheel', + version='2.0', + packages=find_packages() + ) diff --git a/tests/data/src/simplewheel-2.0/simple/__init__.py b/tests/data/src/simplewheel-2.0/simple/__init__.py new file mode 100644 index 00000000000..792d6005489 --- /dev/null +++ b/tests/data/src/simplewheel-2.0/simple/__init__.py @@ -0,0 +1 @@ +# diff --git a/tests/data/src/singlemodule/setup.cfg b/tests/data/src/singlemodule/setup.cfg new file mode 100644 index 00000000000..79bc67848ff --- /dev/null +++ b/tests/data/src/singlemodule/setup.cfg @@ -0,0 +1,5 @@ +[bdist_wheel] +# This flag says that the code is written to work on both Python 2 and Python +# 3. If at all possible, it is good practice to do this. If you cannot, you +# will need to generate wheels for each Python version that you support. +universal=1 diff --git a/tests/data/src/singlemodule/setup.py b/tests/data/src/singlemodule/setup.py new file mode 100644 index 00000000000..67ca9faefa3 --- /dev/null +++ b/tests/data/src/singlemodule/setup.py @@ -0,0 +1,9 @@ +from setuptools import setup + + +setup( + name="singlemodule", + version='0.0.1', + description="A sample Python project with a single module", + py_modules=['singlemodule'], +) diff --git a/tests/data/src/singlemodule/singlemodule.py b/tests/data/src/singlemodule/singlemodule.py new file mode 100644 index 00000000000..c8f10649946 --- /dev/null +++ b/tests/data/src/singlemodule/singlemodule.py @@ -0,0 +1,3 @@ +def main(): + """Entry point for the application script""" + print("Call your main application code here") diff --git a/tests/functional/test_bundle.py b/tests/functional/test_bundle.py deleted file mode 100644 index 42c875360f6..00000000000 --- a/tests/functional/test_bundle.py +++ /dev/null @@ -1,64 +0,0 @@ -import zipfile -import textwrap -from os.path import abspath, exists, join -from pip.download import path_to_url -from tests.lib.local_repos import local_checkout - - -def test_create_bundle(script, tmpdir, data): - """ - Test making a bundle. We'll grab one package from the filesystem - (the FSPkg dummy package), one from vcs (initools) and one from an - index (pip itself). - - """ - fspkg = path_to_url(data.packages/'FSPkg') - script.pip('install', '-e', fspkg) - pkg_lines = textwrap.dedent('''\ - -e %s - -e %s#egg=initools-dev - pip''' % (fspkg, local_checkout('svn+http://svn.colorstudy.com/INITools/trunk', tmpdir.join("cache")))) - script.scratch_path.join("bundle-req.txt").write(pkg_lines) - # Create a bundle in env.scratch_path/ test.pybundle - result = script.pip('bundle', '--no-use-wheel', '-r', script.scratch_path/ 'bundle-req.txt', script.scratch_path/ 'test.pybundle') - bundle = result.files_after.get(join('scratch', 'test.pybundle'), None) - assert bundle is not None - - files = zipfile.ZipFile(bundle.full).namelist() - assert 'src/FSPkg/' in files - assert 'src/initools/' in files - assert 'build/pip/' in files - - -def test_cleanup_after_create_bundle(script, tmpdir, data): - """ - Test clean up after making a bundle. Make sure (build|src)-bundle/ dirs are removed but not src/. - - """ - # Install an editable to create a src/ dir. - args = ['install'] - args.extend(['-e', - '%s#egg=pip-test-package' % - local_checkout('git+http://github.com/pypa/pip-test-package.git', tmpdir.join("cache"))]) - script.pip(*args) - build = script.venv_path/"build" - src = script.venv_path/"src" - assert not exists(build), "build/ dir still exists: %s" % build - assert exists(src), "expected src/ dir doesn't exist: %s" % src - - # Make the bundle. - fspkg = path_to_url(data.packages/'FSPkg') - pkg_lines = textwrap.dedent('''\ - -e %s - -e %s#egg=initools-dev - pip''' % (fspkg, local_checkout('svn+http://svn.colorstudy.com/INITools/trunk', tmpdir.join("cache")))) - script.scratch_path.join("bundle-req.txt").write(pkg_lines) - script.pip('bundle', '--no-use-wheel', '-r', 'bundle-req.txt', 'test.pybundle') - build_bundle = script.scratch_path/"build-bundle" - src_bundle = script.scratch_path/"src-bundle" - assert not exists(build_bundle), "build-bundle/ dir still exists: %s" % build_bundle - assert not exists(src_bundle), "src-bundle/ dir still exists: %s" % src_bundle - script.assert_no_temp() - - # Make sure previously created src/ from editable still exists - assert exists(src), "expected src dir doesn't exist: %s" % src diff --git a/tests/functional/test_check.py b/tests/functional/test_check.py new file mode 100644 index 00000000000..9eaaf30e9a3 --- /dev/null +++ b/tests/functional/test_check.py @@ -0,0 +1,97 @@ +import pytest + + +def matches_expected_lines(string, expected_lines): + # Ignore empty lines + output_lines = set(filter(None, string.splitlines())) + # Match regardless of order + return set(output_lines) == set(expected_lines) + + +def test_check_clean(script): + """On a clean environment, check should print a helpful message. + + """ + result = script.pip('check') + + expected_lines = ( + "No broken requirements found.", + ) + assert matches_expected_lines(result.stdout, expected_lines) + + +@pytest.mark.network +def test_check_missing_dependency(script): + # this will also install ipython, a dependency + script.pip('install', 'ipdb==0.7') + + # deliberately remove the dependency + script.pip('uninstall', 'ipython', '--yes') + + result = script.pip('check', expect_error=True) + + expected_lines = ( + "ipdb 0.7 requires ipython, which is not installed.", + ) + assert matches_expected_lines(result.stdout, expected_lines) + assert result.returncode == 1 + + +@pytest.mark.network +def test_check_missing_dependency_normalize_case(script): + # Install some things + script.pip('install', 'devpi-web==2.2.2') + script.pip('install', 'pyramid==1.5.2') + + # deliberately remove some dependencies + script.pip('uninstall', 'pygments', '--yes') + script.pip('uninstall', 'zope.deprecation', '--yes') + + result = script.pip('check', expect_error=True) + + expected_lines = ( + "devpi-web 2.2.2 requires pygments, which is not installed.", + "pyramid 1.5.2 requires zope.deprecation, which is not installed.", + ) + assert matches_expected_lines(result.stdout, expected_lines) + assert result.returncode == 1 + + +@pytest.mark.network +def test_check_broken_dependency(script): + # this will also install a compatible version of jinja2 + script.pip('install', 'flask==0.10.1') + + # deliberately change dependency to a version that is too old + script.pip('install', 'jinja2==2.3') + + result = script.pip('check', expect_error=True) + + expected_lines = ( + "Flask 0.10.1 has requirement Jinja2>=2.4, but you have Jinja2 2.3.", + ) + assert matches_expected_lines(result.stdout, expected_lines) + assert result.returncode == 1 + + +@pytest.mark.network +def test_check_broken_dependency_and_missing_dependency(script): + # this will also install a compatible version of jinja2 + script.pip('install', 'flask==0.10.1') + script.pip('install', 'pyramid==1.5.2') + + # deliberately remove a dependency + script.pip('uninstall', 'zope.deprecation', '--yes') + + # deliberately change dependency to a version that is too old + script.pip('install', 'jinja2==2.3') + + result = script.pip('check', expect_error=True) + + expected_lines = ( + "pyramid 1.5.2 requires zope.deprecation, which is not installed.", + "Flask 0.10.1 has requirement Jinja2>=2.4, but you have Jinja2 2.3." + ) + + assert matches_expected_lines(result.stdout, expected_lines) + assert result.returncode == 1 diff --git a/tests/functional/test_completion.py b/tests/functional/test_completion.py index 681c2cf5f8b..5680f9f02bc 100644 --- a/tests/functional/test_completion.py +++ b/tests/functional/test_completion.py @@ -62,20 +62,21 @@ def setup_completion(script, words, cword): script.environ['COMP_CWORD'] = cword # expect_error is True because autocomplete exists with 1 status code - result = script.run('python', '-c', 'import pip;pip.autocomplete()', - expect_error=True) + result = script.run( + 'python', '-c', 'import pip;pip.autocomplete()', + expect_error=True, + ) return result, script def test_completion_for_un_snippet(script): """ - Test getting completion for ``un`` should return - uninstall and unzip + Test getting completion for ``un`` should return uninstall """ res, env = setup_completion(script, 'pip un', '1') - assert res.stdout.strip().split() == ['uninstall', 'unzip'], res.stdout + assert res.stdout.strip().split() == ['uninstall'], res.stdout def test_completion_for_default_parameters(script): diff --git a/tests/functional/test_download.py b/tests/functional/test_download.py new file mode 100644 index 00000000000..54132261fbe --- /dev/null +++ b/tests/functional/test_download.py @@ -0,0 +1,161 @@ +import os +import textwrap +import pytest + +from tests.lib.path import Path + + +@pytest.mark.network +def test_download_if_requested(script): + """ + It should download (in the scratch path) and not install if requested. + """ + result = script.pip( + 'download', '-d', 'pip_downloads', 'INITools==0.1', expect_error=True + ) + assert Path('scratch') / 'pip_downloads' / 'INITools-0.1.tar.gz' \ + in result.files_created + assert script.site_packages / 'initools' not in result.files_created + + +@pytest.mark.network +def test_download_setuptools(script): + """ + It should download (in the scratch path) and not install if requested. + """ + result = script.pip('download', 'setuptools') + setuptools_prefix = str(Path('scratch') / 'setuptools') + assert any( + path.startswith(setuptools_prefix) for path in result.files_created + ) + + +@pytest.mark.network +def test_download_wheel(script): + """ + Test using "pip download" to download a *.whl archive. + FIXME: this test could use a local --find-links dir, but -d with local + --find-links has a bug https://github.com/pypa/pip/issues/1111 + """ + result = script.pip( + 'download', + '-f', 'https://bitbucket.org/pypa/pip-test-package/downloads', + '-d', '.', 'pip-test-package' + ) + assert ( + Path('scratch') / 'pip_test_package-0.1.1-py2.py3-none-any.whl' + in result.files_created + ) + assert script.site_packages / 'piptestpackage' not in result.files_created + + +@pytest.mark.network +def test_single_download_from_requirements_file(script): + """ + It should support download (in the scratch path) from PyPi from a + requirements file + """ + script.scratch_path.join("test-req.txt").write(textwrap.dedent(""" + INITools==0.1 + """)) + result = script.pip( + 'download', '-r', script.scratch_path / 'test-req.txt', '-d', '.', + expect_error=True, + ) + assert Path('scratch') / 'INITools-0.1.tar.gz' in result.files_created + assert script.site_packages / 'initools' not in result.files_created + + +@pytest.mark.network +def test_download_should_download_dependencies(script): + """ + It should download dependencies (in the scratch path) + """ + result = script.pip( + 'download', 'Paste[openid]==1.7.5.1', '-d', '.', expect_error=True, + ) + assert Path('scratch') / 'Paste-1.7.5.1.tar.gz' in result.files_created + openid_tarball_prefix = str(Path('scratch') / 'python-openid-') + assert any( + path.startswith(openid_tarball_prefix) for path in result.files_created + ) + assert script.site_packages / 'openid' not in result.files_created + + +def test_download_wheel_archive(script, data): + """ + It should download a wheel archive path + """ + wheel_filename = 'colander-0.9.9-py2.py3-none-any.whl' + wheel_path = os.path.join(data.find_links, wheel_filename) + result = script.pip( + 'download', wheel_path, + '-d', '.', '--no-deps' + ) + assert Path('scratch') / wheel_filename in result.files_created + + +def test_download_should_download_wheel_deps(script, data): + """ + It should download dependencies for wheels(in the scratch path) + """ + wheel_filename = 'colander-0.9.9-py2.py3-none-any.whl' + dep_filename = 'translationstring-1.1.tar.gz' + wheel_path = os.path.join(data.find_links, wheel_filename) + result = script.pip( + 'download', wheel_path, + '-d', '.', '--find-links', data.find_links, '--no-index' + ) + assert Path('scratch') / wheel_filename in result.files_created + assert Path('scratch') / dep_filename in result.files_created + + +@pytest.mark.network +def test_download_should_skip_existing_files(script): + """ + It should not download files already existing in the scratch dir + """ + script.scratch_path.join("test-req.txt").write(textwrap.dedent(""" + INITools==0.1 + """)) + + result = script.pip( + 'download', '-r', script.scratch_path / 'test-req.txt', '-d', '.', + expect_error=True, + ) + assert Path('scratch') / 'INITools-0.1.tar.gz' in result.files_created + assert script.site_packages / 'initools' not in result.files_created + + # adding second package to test-req.txt + script.scratch_path.join("test-req.txt").write(textwrap.dedent(""" + INITools==0.1 + python-openid==2.2.5 + """)) + + # only the second package should be downloaded + result = script.pip( + 'download', '-r', script.scratch_path / 'test-req.txt', '-d', '.', + expect_error=True, + ) + openid_tarball_prefix = str(Path('scratch') / 'python-openid-') + assert any( + path.startswith(openid_tarball_prefix) for path in result.files_created + ) + assert Path('scratch') / 'INITools-0.1.tar.gz' not in result.files_created + assert script.site_packages / 'initools' not in result.files_created + assert script.site_packages / 'openid' not in result.files_created + + +@pytest.mark.network +def test_download_vcs_link(script): + """ + It should allow -d flag for vcs links, regression test for issue #798. + """ + result = script.pip( + 'download', '-d', '.', 'git+git://github.com/pypa/pip-test-package.git' + ) + assert ( + Path('scratch') / 'pip-test-package-0.1.1.zip' + in result.files_created + ) + assert script.site_packages / 'piptestpackage' not in result.files_created diff --git a/tests/functional/test_freeze.py b/tests/functional/test_freeze.py index 1b76cad8b12..6e76365e162 100644 --- a/tests/functional/test_freeze.py +++ b/tests/functional/test_freeze.py @@ -1,9 +1,11 @@ import sys +import os import re import textwrap +import pytest from doctest import OutputChecker, ELLIPSIS -from tests.lib.local_repos import local_checkout, local_repo +from tests.lib import _create_test_package, _create_test_package_with_srcdir distribute_re = re.compile('^distribute==[0-9.]+\n', re.MULTILINE) @@ -13,14 +15,14 @@ def _check_output(result, expected): checker = OutputChecker() actual = str(result) - ## FIXME! The following is a TOTAL hack. For some reason the - ## __str__ result for pkg_resources.Requirement gets downcased on - ## Windows. Since INITools is the only package we're installing - ## in this file with funky case requirements, I'm forcibly - ## upcasing it. You can also normalize everything to lowercase, - ## but then you have to remember to upcase . The right - ## thing to do in the end is probably to find out how to report - ## the proper fully-cased package name in our error message. + # FIXME! The following is a TOTAL hack. For some reason the + # __str__ result for pkg_resources.Requirement gets downcased on + # Windows. Since INITools is the only package we're installing + # in this file with funky case requirements, I'm forcibly + # upcasing it. You can also normalize everything to lowercase, + # but then you have to remember to upcase . The right + # thing to do in the end is probably to find out how to report + # the proper fully-cased package name in our error message. if sys.platform == 'win32': actual = actual.replace('initools', 'INITools') @@ -30,7 +32,11 @@ def _check_output(result, expected): def banner(msg): return '\n========== %s ==========\n' % msg - assert checker.check_output(expected, actual, ELLIPSIS), banner('EXPECTED')+expected+banner('ACTUAL')+actual+banner(6*'=') + + assert checker.check_output(expected, actual, ELLIPSIS), ( + banner('EXPECTED') + expected + banner('ACTUAL') + actual + + banner(6 * '=') + ) def test_freeze_basic(script): @@ -47,144 +53,330 @@ def test_freeze_basic(script): # and something else to test out: simple2<=3.0 """)) - result = script.pip_install_local('-r', script.scratch_path/'initools-req.txt') + script.pip_install_local( + '-r', script.scratch_path / 'initools-req.txt', + ) result = script.pip('freeze', expect_stderr=True) expected = textwrap.dedent("""\ - Script result: pip freeze - -- stdout: -------------------- ...simple==2.0 simple2==3.0... """) - _check_output(result, expected) + _check_output(result.stdout, expected) + + +def test_freeze_with_pip(script): + """Test pip shows itself""" + result = script.pip('freeze', '--all') + assert 'pip==' in result.stdout +def test_freeze_with_invalid_names(script): + """ + Test that invalid names produce warnings and are passed over gracefully. + """ + + def fake_install(pkgname, dest): + egg_info_path = os.path.join( + dest, '{0}-1.0-py{1}.{2}.egg-info'.format( + pkgname.replace('-', '_'), + sys.version_info[0], + sys.version_info[1] + ) + ) + with open(egg_info_path, 'w') as egg_info_file: + egg_info_file.write(textwrap.dedent("""\ + Metadata-Version: 1.0 + Name: {0} + Version: 1.0 + """.format(pkgname) + )) + + valid_pkgnames = ('middle-dash', 'middle_underscore', 'middle.dot') + invalid_pkgnames = ( + '-leadingdash', '_leadingunderscore', '.leadingdot', + 'trailingdash-', 'trailingunderscore_', 'trailingdot.' + ) + for pkgname in valid_pkgnames + invalid_pkgnames: + fake_install(pkgname, script.site_packages_path) + result = script.pip('freeze', expect_stderr=True) + for pkgname in valid_pkgnames: + _check_output( + result.stdout, + '...{0}==1.0...'.format(pkgname.replace('_', '-')) + ) + for pkgname in invalid_pkgnames: + _check_output( + result.stderr, + '...Could not parse requirement: {0}\n...'.format( + pkgname.replace('_', '-') + ) + ) + + +@pytest.mark.svn def test_freeze_svn(script, tmpdir): """Test freezing a svn checkout""" - checkout_path = local_checkout('svn+http://svn.colorstudy.com/INITools/trunk', tmpdir.join("cache")) - #svn internally stores windows drives as uppercase; we'll match that. - checkout_path = checkout_path.replace('c:', 'C:') + checkout_path = _create_test_package(script, vcs='svn') - result = script.run('svn', 'co', '-r10', - local_repo('svn+http://svn.colorstudy.com/INITools/trunk', tmpdir.join("cache")), - 'initools-trunk') - result = script.run('python', 'setup.py', 'develop', - cwd=script.scratch_path/ 'initools-trunk', expect_stderr=True) + # Install with develop + script.run( + 'python', 'setup.py', 'develop', + cwd=checkout_path, expect_stderr=True + ) result = script.pip('freeze', expect_stderr=True) expected = textwrap.dedent("""\ - Script result: ...pip freeze - -- stdout: -------------------- - -e %s@10#egg=INITools-0.3.1dev...-dev_r10 - ...""" % checkout_path) - _check_output(result, expected) + ...-e svn+...#egg=version_pkg + ...""") + _check_output(result.stdout, expected) +@pytest.mark.git def test_freeze_git_clone(script, tmpdir): """ Test freezing a Git clone. + """ + # Returns path to a generated package called "version_pkg" + pkg_version = _create_test_package(script) + + result = script.run( + 'git', 'clone', pkg_version, 'pip-test-package', + expect_stderr=True, + ) + repo_dir = script.scratch_path / 'pip-test-package' + result = script.run( + 'python', 'setup.py', 'develop', + cwd=repo_dir, + expect_stderr=True, + ) + result = script.pip('freeze', expect_stderr=True) + expected = textwrap.dedent( + """ + ...-e git+...#egg=version_pkg + ... + """ + ).strip() + _check_output(result.stdout, expected) + + result = script.pip( + 'freeze', '-f', '%s#egg=pip_test_package' % repo_dir, + expect_stderr=True, + ) + expected = textwrap.dedent( + """ + -f %(repo)s#egg=pip_test_package... + -e git+...#egg=version_pkg + ... + """ % {'repo': repo_dir}, + ).strip() + _check_output(result.stdout, expected) + + # Check that slashes in branch or tag names are translated. + # See also issue #1083: https://github.com/pypa/pip/issues/1083 + script.run( + 'git', 'checkout', '-b', 'branch/name/with/slash', + cwd=repo_dir, + expect_stderr=True, + ) + # Create a new commit to ensure that the commit has only one branch + # or tag name associated to it (to avoid the non-determinism reported + # in issue #1867). + script.run('touch', 'newfile', cwd=repo_dir) + script.run('git', 'add', 'newfile', cwd=repo_dir) + script.run('git', 'commit', '-m', '...', cwd=repo_dir) + result = script.pip('freeze', expect_stderr=True) + expected = textwrap.dedent( + """ + ...-e ...@...#egg=version_pkg + ... + """ + ).strip() + _check_output(result.stdout, expected) + +@pytest.mark.git +def test_freeze_git_clone_srcdir(script, tmpdir): + """ + Test freezing a Git clone where setup.py is in a subdirectory + relative the repo root and the source code is in a subdirectory + relative to setup.py. """ - result = script.run('git', 'clone', local_repo('git+http://github.com/pypa/pip-test-package.git', tmpdir.join("cache")), 'pip-test-package', expect_stderr=True) - result = script.run('git', 'checkout', '7d654e66c8fa7149c165ddeffa5b56bc06619458', - cwd=script.scratch_path / 'pip-test-package', expect_stderr=True) - result = script.run('python', 'setup.py', 'develop', - cwd=script.scratch_path / 'pip-test-package') + # Returns path to a generated package called "version_pkg" + pkg_version = _create_test_package_with_srcdir(script) + + result = script.run( + 'git', 'clone', pkg_version, 'pip-test-package', + expect_stderr=True, + ) + repo_dir = script.scratch_path / 'pip-test-package' + result = script.run( + 'python', 'setup.py', 'develop', + cwd=repo_dir / 'subdir', + expect_stderr=True, + ) result = script.pip('freeze', expect_stderr=True) - expected = textwrap.dedent("""\ - Script result: ...pip freeze - -- stdout: -------------------- - ...-e %s@...#egg=pip_test_package-... - ...""" % local_checkout('git+http://github.com/pypa/pip-test-package.git', tmpdir.join("cache"))) - _check_output(result, expected) - - result = script.pip('freeze', '-f', - '%s#egg=pip_test_package' % local_checkout('git+http://github.com/pypa/pip-test-package.git', tmpdir.join("cache")), - expect_stderr=True) - expected = textwrap.dedent("""\ - Script result: pip freeze -f %(repo)s#egg=pip_test_package - -- stdout: -------------------- - -f %(repo)s#egg=pip_test_package... - -e %(repo)s@...#egg=pip_test_package-0.1.1 - ...""" % {'repo': local_checkout('git+http://github.com/pypa/pip-test-package.git', tmpdir.join("cache"))}) - _check_output(result, expected) + expected = textwrap.dedent( + """ + ...-e git+...#egg=version_pkg&subdirectory=subdir + ... + """ + ).strip() + _check_output(result.stdout, expected) + + result = script.pip( + 'freeze', '-f', '%s#egg=pip_test_package' % repo_dir, + expect_stderr=True, + ) + expected = textwrap.dedent( + """ + -f %(repo)s#egg=pip_test_package... + -e git+...#egg=version_pkg&subdirectory=subdir + ... + """ % {'repo': repo_dir}, + ).strip() + _check_output(result.stdout, expected) + + +@pytest.mark.git +def test_freeze_git_remote(script, tmpdir): + """ + Test freezing a Git clone. + """ + # Returns path to a generated package called "version_pkg" + pkg_version = _create_test_package(script) + + result = script.run( + 'git', 'clone', pkg_version, 'pip-test-package', + expect_stderr=True, + ) + repo_dir = script.scratch_path / 'pip-test-package' + result = script.run( + 'python', 'setup.py', 'develop', + cwd=repo_dir, + expect_stderr=True, + ) + origin_remote = pkg_version + other_remote = pkg_version + '-other' + # check frozen remote after clone + result = script.pip('freeze', expect_stderr=True) + expected = textwrap.dedent( + """ + ...-e git+{remote}@...#egg=version_pkg + ... + """ + ).format(remote=origin_remote).strip() + _check_output(result.stdout, expected) + # check frozen remote when there is no remote named origin + script.run('git', 'remote', 'remove', 'origin', cwd=repo_dir) + script.run('git', 'remote', 'add', 'other', other_remote, cwd=repo_dir) + result = script.pip('freeze', expect_stderr=True) + expected = textwrap.dedent( + """ + ...-e git+{remote}@...#egg=version_pkg + ... + """ + ).format(remote=other_remote).strip() + _check_output(result.stdout, expected) + # when there are more than one origin, priority is given to the + # remote named origin + script.run('git', 'remote', 'add', 'origin', origin_remote, cwd=repo_dir) + result = script.pip('freeze', expect_stderr=True) + expected = textwrap.dedent( + """ + ...-e git+{remote}@...#egg=version_pkg + ... + """ + ).format(remote=origin_remote).strip() + _check_output(result.stdout, expected) +@pytest.mark.mercurial def test_freeze_mercurial_clone(script, tmpdir): """ Test freezing a Mercurial clone. """ - result = script.run('hg', 'clone', - '-r', 'c9963c111e7c', - local_repo('hg+http://bitbucket.org/pypa/pip-test-package', tmpdir.join("cache")), - 'pip-test-package') - result = script.run('python', 'setup.py', 'develop', - cwd=script.scratch_path/'pip-test-package', expect_stderr=True) + # Returns path to a generated package called "version_pkg" + pkg_version = _create_test_package(script, vcs='hg') + + result = script.run( + 'hg', 'clone', pkg_version, 'pip-test-package', + expect_stderr=True, + ) + repo_dir = script.scratch_path / 'pip-test-package' + result = script.run( + 'python', 'setup.py', 'develop', + cwd=repo_dir, + expect_stderr=True, + ) result = script.pip('freeze', expect_stderr=True) - expected = textwrap.dedent("""\ - Script result: ...pip freeze - -- stdout: -------------------- - ...-e %s@...#egg=pip_test_package-... - ...""" % local_checkout('hg+http://bitbucket.org/pypa/pip-test-package', tmpdir.join("cache"))) - _check_output(result, expected) - - result = script.pip('freeze', '-f', - '%s#egg=pip_test_package' % local_checkout('hg+http://bitbucket.org/pypa/pip-test-package', tmpdir.join("cache")), - expect_stderr=True) - expected = textwrap.dedent("""\ - Script result: ...pip freeze -f %(repo)s#egg=pip_test_package - -- stdout: -------------------- - -f %(repo)s#egg=pip_test_package - ...-e %(repo)s@...#egg=pip_test_package-dev - ...""" % {'repo': local_checkout('hg+http://bitbucket.org/pypa/pip-test-package', tmpdir.join("cache"))}) - _check_output(result, expected) - - + expected = textwrap.dedent( + """ + ...-e hg+...#egg=version_pkg + ... + """ + ).strip() + _check_output(result.stdout, expected) + + result = script.pip( + 'freeze', '-f', '%s#egg=pip_test_package' % repo_dir, + expect_stderr=True, + ) + expected = textwrap.dedent( + """ + -f %(repo)s#egg=pip_test_package... + ...-e hg+...#egg=version_pkg + ... + """ % {'repo': repo_dir}, + ).strip() + _check_output(result.stdout, expected) + + +@pytest.mark.bzr def test_freeze_bazaar_clone(script, tmpdir): """ Test freezing a Bazaar clone. """ - - checkout_path = local_checkout('bzr+http://bazaar.launchpad.net/%7Edjango-wikiapp/django-wikiapp/release-0.1', tmpdir.join("cache")) - #bzr internally stores windows drives as uppercase; we'll match that. - checkout_pathC = checkout_path.replace('c:', 'C:') - - result = script.run('bzr', 'checkout', '-r', '174', - local_repo('bzr+http://bazaar.launchpad.net/%7Edjango-wikiapp/django-wikiapp/release-0.1', tmpdir.join("cache")), - 'django-wikiapp') - result = script.run('python', 'setup.py', 'develop', - cwd=script.scratch_path/'django-wikiapp') + try: + checkout_path = _create_test_package(script, vcs='bazaar') + except OSError as e: + pytest.fail('Invoking `bzr` failed: %s' % e) + + result = script.run( + 'bzr', 'checkout', checkout_path, 'bzr-package' + ) + result = script.run( + 'python', 'setup.py', 'develop', + cwd=script.scratch_path / 'bzr-package', + expect_stderr=True, + ) result = script.pip('freeze', expect_stderr=True) expected = textwrap.dedent("""\ - Script result: ...pip freeze - -- stdout: -------------------- - ...-e %s@...#egg=django_wikiapp-... - ...""" % checkout_pathC) - _check_output(result, expected) - - result = script.pip('freeze', '-f', - '%s/#egg=django-wikiapp' % checkout_path, - expect_stderr=True) + ...-e bzr+file://...@1#egg=version_pkg + ...""") + _check_output(result.stdout, expected) + + result = script.pip( + 'freeze', '-f', + '%s/#egg=django-wikiapp' % checkout_path, + expect_stderr=True, + ) expected = textwrap.dedent("""\ - Script result: ...pip freeze -f %(repo)s/#egg=django-wikiapp - -- stdout: -------------------- -f %(repo)s/#egg=django-wikiapp - ...-e %(repoC)s@...#egg=django_wikiapp-... - ...""" % {'repoC': checkout_pathC, 'repo': checkout_path}) - _check_output(result, expected) + ...-e bzr+file://...@...#egg=version_pkg + ...""" % {'repo': checkout_path}) + _check_output(result.stdout, expected) def test_freeze_with_local_option(script): """ - Test that wsgiref (from global site-packages) is reported normally, but not with --local. - + Test that wsgiref (from global site-packages) is reported normally, but not + with --local. """ - result = script.pip('install', 'initools==0.2') + result = script.pip_install_local('initools==0.2') result = script.pip('freeze', expect_stderr=True) expected = textwrap.dedent("""\ - Script result: ...pip freeze - -- stdout: -------------------- INITools==0.2 wsgiref==... """) @@ -198,11 +390,27 @@ def test_freeze_with_local_option(script): result = script.pip('freeze', '--local', expect_stderr=True) expected = textwrap.dedent("""\ - Script result: ...pip freeze --local - -- stdout: -------------------- INITools==0.2 """) - _check_output(result, expected) + _check_output(result.stdout, expected) + + +# used by the test_freeze_with_requirement_* tests below +_freeze_req_opts = textwrap.dedent("""\ + # Unchanged requirements below this line + -r ignore.txt + --requirement ignore.txt + -Z ignore + --always-unzip ignore + -f http://ignore + -i http://ignore + --pre + --trusted-host url + --process-dependency-links + --extra-index-url http://ignore + --find-links http://ignore + --index-url http://ignore +""") def test_freeze_with_requirement_option(script): @@ -210,31 +418,90 @@ def test_freeze_with_requirement_option(script): Test that new requirements are created correctly with --requirement hints """ - ignores = textwrap.dedent("""\ - # Unchanged requirements below this line - -r ignore.txt - --requirement ignore.txt - -Z ignore - --always-unzip ignore - -f http://ignore - -i http://ignore - --extra-index-url http://ignore - --find-links http://ignore - --index-url http://ignore - """) + script.scratch_path.join("hint.txt").write(textwrap.dedent("""\ INITools==0.1 NoExist==4.2 - """) + ignores) - result = script.pip('install', 'initools==0.2') + simple==3.0; python_version > '1.0' + """) + _freeze_req_opts) + result = script.pip_install_local('initools==0.2') result = script.pip_install_local('simple') - result = script.pip('freeze', '--requirement', 'hint.txt', expect_stderr=True) + result = script.pip( + 'freeze', '--requirement', 'hint.txt', + expect_stderr=True, + ) expected = textwrap.dedent("""\ - Script result: pip freeze --requirement hint.txt - -- stderr: -------------------- - Requirement file contains NoExist==4.2, but that package is not installed + INITools==0.2 + simple==3.0 + """) + expected += _freeze_req_opts + expected += "## The following requirements were added by pip freeze:..." + _check_output(result.stdout, expected) + assert ( + "Requirement file [hint.txt] contains NoExist==4.2, but that package " + "is not installed" + ) in result.stderr + + +def test_freeze_with_requirement_option_multiple(script): + """ + Test that new requirements are created correctly with multiple + --requirement hints - -- stdout: -------------------- + """ + script.scratch_path.join('hint1.txt').write(textwrap.dedent("""\ + INITools==0.1 + NoExist==4.2 + simple==3.0; python_version > '1.0' + """) + _freeze_req_opts) + script.scratch_path.join('hint2.txt').write(textwrap.dedent("""\ + NoExist2==2.0 + simple2==1.0 + """) + _freeze_req_opts) + result = script.pip_install_local('initools==0.2') + result = script.pip_install_local('simple') + result = script.pip_install_local('simple2==1.0') + result = script.pip_install_local('meta') + result = script.pip( + 'freeze', '--requirement', 'hint1.txt', '--requirement', 'hint2.txt', + expect_stderr=True, + ) + expected = textwrap.dedent("""\ INITools==0.2 - """) + ignores + "## The following requirements were added by pip --freeze:..." - _check_output(result, expected) + simple==1.0 + """) + expected += _freeze_req_opts + expected += textwrap.dedent("""\ + simple2==1.0 + """) + expected += "## The following requirements were added by pip freeze:" + expected += os.linesep + textwrap.dedent("""\ + ...meta==1.0... + """) + _check_output(result.stdout, expected) + assert ( + "Requirement file [hint1.txt] contains NoExist==4.2, but that " + "package is not installed" + ) in result.stderr + assert ( + "Requirement file [hint2.txt] contains NoExist2==2.0, but that " + "package is not installed" + ) in result.stderr + # any options like '--index-url http://ignore' should only be emitted once + # even if they are listed in multiple requirements files + assert result.stdout.count("--index-url http://ignore") == 1 + + +def test_freeze_user(script, virtualenv): + """ + Testing freeze with --user, first we have to install some stuff. + """ + virtualenv.system_site_packages = True + script.pip_install_local('--user', 'simple==2.0') + script.pip_install_local('simple2==3.0') + result = script.pip('freeze', '--user', expect_stderr=True) + expected = textwrap.dedent("""\ + simple==2.0 + """) + _check_output(result.stdout, expected) + assert 'simple2' not in result.stdout diff --git a/tests/functional/test_hash.py b/tests/functional/test_hash.py new file mode 100644 index 00000000000..9fc0d6e9108 --- /dev/null +++ b/tests/functional/test_hash.py @@ -0,0 +1,32 @@ +"""Tests for the ``pip hash`` command""" + + +def test_basic(script, tmpdir): + """Run 'pip hash' through its default behavior.""" + expected = ('--hash=sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425' + 'e73043362938b9824') + result = script.pip('hash', _hello_file(tmpdir)) + assert expected in str(result) + + +def test_good_algo_option(script, tmpdir): + """Make sure the -a option works.""" + expected = ('--hash=sha512:9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caad' + 'ae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e' + '5c3adef46f73bcdec043') + result = script.pip('hash', '-a', 'sha512', _hello_file(tmpdir)) + assert expected in str(result) + + +def test_bad_algo_option(script, tmpdir): + """Make sure the -a option raises an error when given a bad operand.""" + result = script.pip('hash', '-a', 'poppycock', _hello_file(tmpdir), + expect_error=True) + assert "invalid choice: 'poppycock'" in str(result) + + +def _hello_file(tmpdir): + """Return a temp file to hash containing "hello".""" + file = tmpdir / 'hashable' + file.write('hello') + return file diff --git a/tests/functional/test_help.py b/tests/functional/test_help.py index 65c67954998..ef143e2fe88 100644 --- a/tests/functional/test_help.py +++ b/tests/functional/test_help.py @@ -3,11 +3,11 @@ from pip.exceptions import CommandError from pip.basecommand import ERROR, SUCCESS from pip.commands.help import HelpCommand -from pip.commands import commands +from pip.commands import commands_dict as commands from mock import Mock -def test_run_method_should_return_sucess_when_finds_command_name(): +def test_run_method_should_return_success_when_finds_command_name(): """ Test HelpCommand.run for existing command """ @@ -18,7 +18,7 @@ def test_run_method_should_return_sucess_when_finds_command_name(): assert status == SUCCESS -def test_run_method_should_return_sucess_when_command_name_not_specified(): +def test_run_method_should_return_success_when_command_name_not_specified(): """ Test HelpCommand.run when there are no args """ @@ -80,6 +80,10 @@ def test_help_commands_equally_functional(script): assert sum(ret) == 0, 'exit codes of: ' + msg for name, cls in commands.items(): - if cls.hidden: continue - assert script.pip('help', name).stdout == \ - script.pip(name, '--help').stdout + if cls.hidden: + continue + + assert ( + script.pip('help', name).stdout == + script.pip(name, '--help').stdout + ) diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index ad72c4e1a4c..1440f3335f9 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -3,48 +3,94 @@ import textwrap import glob -from os.path import abspath, join, curdir, pardir +from os.path import join, curdir, pardir import pytest -from pip.util import rmtree -from tests.lib import pyversion +from pip import pep425tags +from pip.utils import appdirs, rmtree +from tests.lib import (pyversion, pyversion_tuple, + _create_test_package, _create_svn_repo, path_to_url, + requirements_file) from tests.lib.local_repos import local_checkout from tests.lib.path import Path -def test_without_setuptools(script): - script.run("pip", "uninstall", "setuptools", "-y") +def test_without_setuptools(script, data): + script.pip("uninstall", "setuptools", "-y") result = script.run( "python", "-c", - "import pip; pip.main(['install', 'INITools==0.2', '--no-use-wheel'])", + "import pip; pip.main([" + "'install', " + "'INITools==0.2', " + "'-f', '%s', " + "'--no-binary=:all:'])" % data.packages, expect_error=True, ) assert ( - "setuptools must be installed to install from a source distribution" - in result.stdout + "Could not import setuptools which is required to install from a " + "source distribution." + in result.stderr ) + assert "Please install setuptools" in result.stderr -def test_pip_second_command_line_interface_works(script): +def test_with_setuptools_and_import_error(script, data): + # Make sure we get an ImportError while importing setuptools + setuptools_init_path = script.site_packages_path.join( + "setuptools", "__init__.py") + with open(setuptools_init_path, 'a') as f: + f.write('\nraise ImportError("toto")') + + result = script.run( + "python", "-c", + "import pip; pip.main([" + "'install', " + "'INITools==0.2', " + "'-f', '%s', " + "'--no-binary=:all:'])" % data.packages, + expect_error=True, + ) + assert ( + "Could not import setuptools which is required to install from a " + "source distribution." + in result.stderr + ) + assert "Traceback " in result.stderr + assert "ImportError: toto" in result.stderr + + +def test_pip_second_command_line_interface_works(script, data): """ Check if ``pip`` commands behaves equally """ + # On old versions of Python, urllib3/requests will raise a warning about + # the lack of an SSLContext. + kwargs = {} + if pyversion_tuple < (2, 7, 9): + kwargs['expect_stderr'] = True + args = ['pip%s' % pyversion] args.extend(['install', 'INITools==0.2']) - result = script.run(*args) - egg_info_folder = script.site_packages / 'INITools-0.2-py%s.egg-info' % pyversion + args.extend(['-f', data.packages]) + result = script.run(*args, **kwargs) + egg_info_folder = ( + script.site_packages / 'INITools-0.2-py%s.egg-info' % pyversion + ) initools_folder = script.site_packages / 'initools' assert egg_info_folder in result.files_created, str(result) assert initools_folder in result.files_created, str(result) +@pytest.mark.network def test_install_from_pypi(script): """ Test installing a package from PyPI. """ result = script.pip('install', '-vvv', 'INITools==0.2') - egg_info_folder = script.site_packages / 'INITools-0.2-py%s.egg-info' % pyversion + egg_info_folder = ( + script.site_packages / 'INITools-0.2-py%s.egg-info' % pyversion + ) initools_folder = script.site_packages / 'initools' assert egg_info_folder in result.files_created, str(result) assert initools_folder in result.files_created, str(result) @@ -55,159 +101,124 @@ def test_editable_install(script): Test editable installation. """ result = script.pip('install', '-e', 'INITools==0.2', expect_error=True) - assert "INITools==0.2 should either be a path to a local project or a VCS url" in result.stdout - assert len(result.files_created) == 1, result.files_created - assert not result.files_updated, result.files_updated + assert ( + "INITools==0.2 should either be a path to a local project or a VCS url" + in result.stderr + ) + assert not result.files_created + assert not result.files_updated -def test_install_editable_from_svn(script, tmpdir): +def test_install_editable_from_svn(script): """ Test checking out from svn. """ - result = script.pip('install', - '-e', - '%s#egg=initools-dev' % - local_checkout('svn+http://svn.colorstudy.com/INITools/trunk', tmpdir.join("cache"))) - result.assert_installed('INITools', with_files=['.svn']) + checkout_path = _create_test_package(script) + repo_url = _create_svn_repo(script, checkout_path) + result = script.pip( + 'install', + '-e', 'svn+' + repo_url + '#egg=version-pkg' + ) + result.assert_installed('version-pkg', with_files=['.svn']) +@pytest.mark.network def test_download_editable_to_custom_path(script, tmpdir): """ Test downloading an editable using a relative custom src folder. """ script.scratch_path.join("customdl").mkdir() - result = script.pip('install', - '-e', - '%s#egg=initools-dev' % - local_checkout('svn+http://svn.colorstudy.com/INITools/trunk', tmpdir.join("cache")), - '--src', - 'customsrc', - '--download', - 'customdl') - customsrc = Path('scratch')/'customsrc'/'initools' - assert customsrc in result.files_created, sorted(result.files_created.keys()) - assert customsrc/'setup.py' in result.files_created, sorted(result.files_created.keys()) - - customdl = Path('scratch')/'customdl'/'initools' - customdl_files_created = [filename for filename in result.files_created - if filename.startswith(customdl)] - assert customdl_files_created - - -def test_editable_no_install_followed_by_no_download(script, tmpdir): - """ - Test installing an editable in two steps (first with --no-install, then with --no-download). - """ - result = script.pip('install', - '-e', - '%s#egg=initools-dev' % - local_checkout('svn+http://svn.colorstudy.com/INITools/trunk', tmpdir.join("cache")), - '--no-install', expect_error=True) - result.assert_installed('INITools', without_egg_link=True, with_files=['.svn']) - - result = script.pip('install', - '-e', - '%s#egg=initools-dev' % - local_checkout('svn+http://svn.colorstudy.com/INITools/trunk', tmpdir.join("cache")), - '--no-download', expect_error=True) - result.assert_installed('INITools', without_files=[curdir, '.svn']) - - -def test_no_install_followed_by_no_download(script): - """ - Test installing in two steps (first with --no-install, then with --no-download). - """ - egg_info_folder = script.site_packages/'INITools-0.2-py%s.egg-info' % pyversion - initools_folder = script.site_packages/'initools' - build_dir = script.venv/'build'/'INITools' - - result1 = script.pip('install', 'INITools==0.2', '--no-install', expect_error=True) - assert egg_info_folder not in result1.files_created, str(result1) - assert initools_folder not in result1.files_created, sorted(result1.files_created) - assert build_dir in result1.files_created, result1.files_created - assert build_dir/'INITools.egg-info' in result1.files_created + result = script.pip( + 'install', + '-e', + '%s#egg=initools-dev' % + local_checkout( + 'svn+http://svn.colorstudy.com/INITools/trunk', + tmpdir.join("cache") + ), + '--src', + 'customsrc', + '--download', + 'customdl', + expect_stderr=True + ) + customsrc = Path('scratch') / 'customsrc' / 'initools' + assert customsrc in result.files_created, ( + sorted(result.files_created.keys()) + ) + assert customsrc / 'setup.py' in result.files_created, ( + sorted(result.files_created.keys()) + ) - result2 = script.pip('install', 'INITools==0.2', '--no-download', expect_error=True) - assert egg_info_folder in result2.files_created, str(result2) - assert initools_folder in result2.files_created, sorted(result2.files_created) - assert build_dir not in result2.files_created - assert build_dir/'INITools.egg-info' not in result2.files_created + customdl = Path('scratch') / 'customdl' / 'initools' + customdl_files_created = [ + filename for filename in result.files_created + if filename.startswith(customdl) + ] + assert customdl_files_created + assert ('DEPRECATION: pip install --download has been deprecated and will ' + 'be removed in the future. Pip now has a download command that ' + 'should be used instead.') in result.stderr -def test_bad_install_with_no_download(script): - """ - Test that --no-download behaves sensibly if the package source can't be found. - """ - result = script.pip('install', 'INITools==0.2', '--no-download', expect_error=True) - assert "perhaps --no-download was used without first running "\ - "an equivalent install with --no-install?" in result.stdout +def _test_install_editable_from_git(script, tmpdir, wheel): + """Test cloning from Git.""" + if wheel: + script.pip('install', 'wheel') + pkg_path = _create_test_package(script, name='testpackage', vcs='git') + args = ['install', '-e', 'git+%s#egg=testpackage' % path_to_url(pkg_path)] + result = script.pip(*args, **{"expect_error": True}) + result.assert_installed('testpackage', with_files=['.git']) -def test_install_dev_version_from_pypi(script): - """ - Test using package==dev. - """ - result = script.pip('install', 'INITools==dev', - '--allow-external', 'INITools', - '--allow-unverified', 'INITools', - expect_error=True) - assert (script.site_packages / 'initools') in result.files_created, str(result.stdout) +def test_install_editable_from_git(script, tmpdir): + _test_install_editable_from_git(script, tmpdir, False) -def test_install_editable_from_git(script, tmpdir): - """ - Test cloning from Git. - """ - args = ['install'] - args.extend(['-e', - '%s#egg=pip-test-package' % - local_checkout('git+http://github.com/pypa/pip-test-package.git', tmpdir.join("cache"))]) - result = script.pip(*args, **{"expect_error": True}) - result.assert_installed('pip-test-package', with_files=['.git']) +def test_install_editable_from_git_autobuild_wheel(script, tmpdir): + _test_install_editable_from_git(script, tmpdir, True) def test_install_editable_from_hg(script, tmpdir): - """ - Test cloning from Mercurial. - """ - result = script.pip('install', '-e', - '%s#egg=ScriptTest' % - local_checkout('hg+https://bitbucket.org/ianb/scripttest', tmpdir.join("cache")), - expect_error=True) - result.assert_installed('ScriptTest', with_files=['.hg']) + """Test cloning from Mercurial.""" + pkg_path = _create_test_package(script, name='testpackage', vcs='hg') + args = ['install', '-e', 'hg+%s#egg=testpackage' % path_to_url(pkg_path)] + result = script.pip(*args, **{"expect_error": True}) + result.assert_installed('testpackage', with_files=['.hg']) def test_vcs_url_final_slash_normalization(script, tmpdir): """ Test that presence or absence of final slash in VCS URL is normalized. """ - result = script.pip('install', '-e', - '%s/#egg=ScriptTest' % - local_checkout('hg+https://bitbucket.org/ianb/scripttest', tmpdir.join("cache")), - expect_error=True) - assert 'pip-log.txt' not in result.files_created, result.files_created['pip-log.txt'].bytes + pkg_path = _create_test_package(script, name='testpackage', vcs='hg') + args = ['install', '-e', 'hg+%s/#egg=testpackage' % path_to_url(pkg_path)] + result = script.pip(*args, **{"expect_error": True}) + result.assert_installed('testpackage', with_files=['.hg']) def test_install_editable_from_bazaar(script, tmpdir): - """ - Test checking out from Bazaar. - """ - result = script.pip('install', '-e', - '%s/@174#egg=django-wikiapp' % - local_checkout('bzr+http://bazaar.launchpad.net/%7Edjango-wikiapp/django-wikiapp/release-0.1', tmpdir.join("cache")), - expect_error=True) - result.assert_installed('django-wikiapp', with_files=['.bzr']) + """Test checking out from Bazaar.""" + pkg_path = _create_test_package(script, name='testpackage', vcs='bazaar') + args = ['install', '-e', 'bzr+%s/#egg=testpackage' % path_to_url(pkg_path)] + result = script.pip(*args, **{"expect_error": True}) + result.assert_installed('testpackage', with_files=['.bzr']) +@pytest.mark.network def test_vcs_url_urlquote_normalization(script, tmpdir): """ Test that urlquoted characters are normalized for repo URL comparison. """ - result = script.pip('install', '-e', - '%s/#egg=django-wikiapp' % - local_checkout('bzr+http://bazaar.launchpad.net/%7Edjango-wikiapp/django-wikiapp/release-0.1', tmpdir.join("cache")), - expect_error=True) - assert 'pip-log.txt' not in result.files_created, result.files_created['pip-log.txt'].bytes + script.pip( + 'install', '-e', + '%s/#egg=django-wikiapp' % + local_checkout( + 'bzr+http://bazaar.launchpad.net/%7Edjango-wikiapp/django-wikiapp' + '/release-0.1', + tmpdir.join("cache"), + ), + ) def test_install_from_local_directory(script, data): @@ -216,20 +227,76 @@ def test_install_from_local_directory(script, data): """ to_install = data.packages.join("FSPkg") result = script.pip('install', to_install, expect_error=False) - fspkg_folder = script.site_packages/'fspkg' - egg_info_folder = script.site_packages/'FSPkg-0.1dev-py%s.egg-info' % pyversion + fspkg_folder = script.site_packages / 'fspkg' + egg_info_folder = ( + script.site_packages / 'FSPkg-0.1.dev0-py%s.egg-info' % pyversion + ) assert fspkg_folder in result.files_created, str(result.stdout) assert egg_info_folder in result.files_created, str(result) -def test_install_from_local_directory_with_symlinks_to_directories(script, data): +def test_install_quiet(script, data): + """ + Test that install -q is actually quiet. + """ + # Apparently if pip install -q is not actually quiet, then it breaks + # everything. See: + # https://github.com/pypa/pip/issues/3418 + # https://github.com/docker-library/python/issues/83 + to_install = data.packages.join("FSPkg") + result = script.pip('install', '-q', to_install, expect_error=False) + assert result.stdout == "" + assert result.stderr == "" + + +def test_hashed_install_success(script, data, tmpdir): + """ + Test that installing various sorts of requirements with correct hashes + works. + + Test file URLs and index packages (which become HTTP URLs behind the + scenes). + + """ + file_url = path_to_url( + (data.packages / 'simple-1.0.tar.gz').abspath) + with requirements_file( + 'simple2==1.0 --hash=sha256:9336af72ca661e6336eb87bc7de3e8844d853e' + '3848c2b9bbd2e8bf01db88c2c7\n' + '{simple} --hash=sha256:393043e672415891885c9a2a0929b1af95fb866d6c' + 'a016b42d2e6ce53619b653'.format(simple=file_url), + tmpdir) as reqs_file: + script.pip_install_local('-r', reqs_file.abspath, expect_error=False) + + +def test_hashed_install_failure(script, data, tmpdir): + """Test that wrong hashes stop installation. + + This makes sure prepare_files() is called in the course of installation + and so has the opportunity to halt if hashes are wrong. Checks on various + kinds of hashes are in test_req.py. + + """ + with requirements_file('simple2==1.0 --hash=sha256:9336af72ca661e6336eb87b' + 'c7de3e8844d853e3848c2b9bbd2e8bf01db88c2c\n', + tmpdir) as reqs_file: + result = script.pip_install_local('-r', + reqs_file.abspath, + expect_error=True) + assert len(result.files_created) == 0 + + +def test_install_from_local_directory_with_symlinks_to_directories( + script, data): """ Test installing from a local directory containing symlinks to directories. """ to_install = data.packages.join("symlinks") result = script.pip('install', to_install, expect_error=False) - pkg_folder = script.site_packages/'symlinks' - egg_info_folder = script.site_packages/'symlinks-0.1dev-py%s.egg-info' % pyversion + pkg_folder = script.site_packages / 'symlinks' + egg_info_folder = ( + script.site_packages / 'symlinks-0.1.dev0-py%s.egg-info' % pyversion + ) assert pkg_folder in result.files_created, str(result.stdout) assert egg_info_folder in result.files_created, str(result) @@ -239,9 +306,8 @@ def test_install_from_local_directory_with_no_setup_py(script, data): Test installing from a local directory with no 'setup.py'. """ result = script.pip('install', data.root, expect_error=True) - assert len(result.files_created) == 1, result.files_created - assert 'pip-log.txt' in result.files_created, result.files_created - assert "is not installable. File 'setup.py' not found." in result.stdout + assert not result.files_created + assert "is not installable. File 'setup.py' not found." in result.stderr def test_editable_install_from_local_directory_with_no_setup_py(script, data): @@ -249,9 +315,31 @@ def test_editable_install_from_local_directory_with_no_setup_py(script, data): Test installing from a local directory with no 'setup.py'. """ result = script.pip('install', '-e', data.root, expect_error=True) - assert len(result.files_created) == 1, result.files_created - assert 'pip-log.txt' in result.files_created, result.files_created - assert "is not installable. File 'setup.py' not found." in result.stdout + assert not result.files_created + 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 that's 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): @@ -260,8 +348,8 @@ def test_install_as_egg(script, data): """ to_install = data.packages.join("FSPkg") result = script.pip('install', to_install, '--egg', expect_error=False) - fspkg_folder = script.site_packages/'fspkg' - egg_folder = script.site_packages/'FSPkg-0.1dev-py%s.egg' % pyversion + fspkg_folder = script.site_packages / 'fspkg' + egg_folder = script.site_packages / 'FSPkg-0.1.dev0-py%s.egg' % pyversion assert fspkg_folder not in result.files_created, str(result.stdout) assert egg_folder in result.files_created, str(result) assert join(egg_folder, 'fspkg') in result.files_created, str(result) @@ -277,8 +365,10 @@ def test_install_curdir(script, data): if os.path.isdir(egg_info): rmtree(egg_info) result = script.pip('install', curdir, cwd=run_from, expect_error=False) - fspkg_folder = script.site_packages/'fspkg' - egg_info_folder = script.site_packages/'FSPkg-0.1dev-py%s.egg-info' % pyversion + fspkg_folder = script.site_packages / 'fspkg' + egg_info_folder = ( + script.site_packages / 'FSPkg-0.1.dev0-py%s.egg-info' % pyversion + ) assert fspkg_folder in result.files_created, str(result.stdout) assert egg_info_folder in result.files_created, str(result) @@ -289,18 +379,23 @@ def test_install_pardir(script, data): """ run_from = data.packages.join("FSPkg", "fspkg") result = script.pip('install', pardir, cwd=run_from, expect_error=False) - fspkg_folder = script.site_packages/'fspkg' - egg_info_folder = script.site_packages/'FSPkg-0.1dev-py%s.egg-info' % pyversion + fspkg_folder = script.site_packages / 'fspkg' + egg_info_folder = ( + script.site_packages / 'FSPkg-0.1.dev0-py%s.egg-info' % pyversion + ) assert fspkg_folder in result.files_created, str(result.stdout) assert egg_info_folder in result.files_created, str(result) +@pytest.mark.network def test_install_global_option(script): """ Test using global distutils options. (In particular those that disable the actual install action) """ - result = script.pip('install', '--global-option=--version', "INITools==0.1") + result = script.pip( + 'install', '--global-option=--version', "INITools==0.1", + expect_stderr=True) assert '0.1\n' in result.stdout @@ -317,34 +412,43 @@ def test_install_with_hacked_egg_info(script, data): """ run_from = data.packages.join("HackedEggInfo") result = script.pip('install', '.', cwd=run_from) - assert 'Successfully installed hackedegginfo\n' in result.stdout + assert 'Successfully installed hackedegginfo-0.0.0\n' in result.stdout +@pytest.mark.network def test_install_using_install_option_and_editable(script, tmpdir): """ Test installing a tool using -e and --install-option """ folder = 'script_folder' script.scratch_path.join(folder).mkdir() - url = 'git+git://github.com/pypa/virtualenv' - result = script.pip('install', '-e', '%s#egg=virtualenv' % - local_checkout(url, tmpdir.join("cache")), - '--install-option=--script-dir=%s' % folder) - virtualenv_bin = script.venv/'src'/'virtualenv'/folder/'virtualenv'+script.exe - assert virtualenv_bin in result.files_created + url = 'git+git://github.com/pypa/pip-test-package' + result = script.pip( + 'install', '-e', '%s#egg=pip-test-package' % + local_checkout(url, tmpdir.join("cache")), + '--install-option=--script-dir=%s' % folder, + expect_stderr=True) + script_file = ( + script.venv / 'src' / 'pip-test-package' / + folder / 'pip-test-package' + script.exe + ) + assert script_file in result.files_created +@pytest.mark.network def test_install_global_option_using_editable(script, tmpdir): """ Test using global distutils options, but in an editable installation """ url = 'hg+http://bitbucket.org/runeh/anyjson' - result = script.pip('install', '--global-option=--version', - '-e', '%s@0.2.5#egg=anyjson' % - local_checkout(url, tmpdir.join("cache"))) - assert '0.2.5\n' in result.stdout + result = script.pip( + 'install', '--global-option=--version', '-e', + '%s@0.2.5#egg=anyjson' % local_checkout(url, tmpdir.join("cache")), + expect_stderr=True) + assert 'Successfully installed anyjson' in result.stdout +@pytest.mark.network def test_install_package_with_same_name_in_curdir(script): """ Test installing a package with the same name of a local folder @@ -366,7 +470,7 @@ def test_install_folder_using_dot_slash(script): Test installing a folder using pip install ./foldername """ script.scratch_path.join("mock").mkdir() - pkg_path = script.scratch_path/'mock' + pkg_path = script.scratch_path / 'mock' pkg_path.join("setup.py").write(mock100_setup_py) result = script.pip('install', './mock') egg_folder = script.site_packages / 'mock-100.1-py%s.egg-info' % pyversion @@ -378,7 +482,7 @@ def test_install_folder_using_slash_in_the_end(script): Test installing a folder using pip install foldername/ or foldername\ """ script.scratch_path.join("mock").mkdir() - pkg_path = script.scratch_path/'mock' + pkg_path = script.scratch_path / 'mock' pkg_path.join("setup.py").write(mock100_setup_py) result = script.pip('install', 'mock' + os.path.sep) egg_folder = script.site_packages / 'mock-100.1-py%s.egg-info' % pyversion @@ -391,20 +495,24 @@ def test_install_folder_using_relative_path(script): """ script.scratch_path.join("initools").mkdir() script.scratch_path.join("initools", "mock").mkdir() - pkg_path = script.scratch_path/'initools'/'mock' + pkg_path = script.scratch_path / 'initools' / 'mock' pkg_path.join("setup.py").write(mock100_setup_py) - result = script.pip('install', Path('initools')/'mock') + result = script.pip('install', Path('initools') / 'mock') egg_folder = script.site_packages / 'mock-100.1-py%s.egg-info' % pyversion assert egg_folder in result.files_created, str(result) +@pytest.mark.network def test_install_package_which_contains_dev_in_name(script): """ Test installing package from pypi which contains 'dev' in name """ result = script.pip('install', 'django-devserver==0.0.4') - devserver_folder = script.site_packages/'devserver' - egg_info_folder = script.site_packages/'django_devserver-0.0.4-py%s.egg-info' % pyversion + devserver_folder = script.site_packages / 'devserver' + egg_info_folder = ( + script.site_packages / 'django_devserver-0.0.4-py%s.egg-info' % + pyversion + ) assert devserver_folder in result.files_created, str(result.stdout) assert egg_info_folder in result.files_created, str(result) @@ -413,109 +521,508 @@ def test_install_package_with_target(script): """ Test installing a package using pip install --target """ - target_dir = script.scratch_path/'target' - result = script.pip('install', '-t', target_dir, "initools==0.1") - assert Path('scratch')/'target'/'initools' in result.files_created, str(result) + target_dir = script.scratch_path / 'target' + result = script.pip_install_local('-t', target_dir, "simple==1.0") + assert Path('scratch') / 'target' / 'simple' in result.files_created, ( + str(result) + ) + + # Test repeated call without --upgrade, no files should have changed + result = script.pip_install_local( + '-t', target_dir, "simple==1.0", expect_stderr=True, + ) + assert not Path('scratch') / 'target' / 'simple' in result.files_updated + + # Test upgrade call, check that new version is installed + result = script.pip_install_local('--upgrade', '-t', + target_dir, "simple==2.0") + assert Path('scratch') / 'target' / 'simple' in result.files_updated, ( + str(result) + ) + egg_folder = ( + Path('scratch') / 'target' / 'simple-2.0-py%s.egg-info' % pyversion) + assert egg_folder in result.files_created, ( + str(result) + ) + + # Test install and upgrade of single-module package + result = script.pip_install_local('-t', target_dir, 'singlemodule==0.0.0') + singlemodule_py = Path('scratch') / 'target' / 'singlemodule.py' + assert singlemodule_py in result.files_created, str(result) + + result = script.pip_install_local('-t', target_dir, 'singlemodule==0.0.1', + '--upgrade') + assert singlemodule_py in result.files_updated, str(result) def test_install_package_with_root(script, data): """ Test installing a package using pip install --root """ - root_dir = script.scratch_path/'root' - result = script.pip('install', '--root', root_dir, '-f', data.find_links, '--no-index', 'simple==1.0') - normal_install_path = script.base_path / script.site_packages / 'simple-1.0-py%s.egg-info' % pyversion - #use distutils to change the root exactly how the --root option does it + root_dir = script.scratch_path / 'root' + result = script.pip( + 'install', '--root', root_dir, '-f', data.find_links, '--no-index', + 'simple==1.0', + ) + normal_install_path = ( + script.base_path / script.site_packages / 'simple-1.0-py%s.egg-info' % + pyversion + ) + # use distutils to change the root exactly how the --root option does it from distutils.util import change_root - root_path = change_root(os.path.join(script.scratch, 'root'), normal_install_path) + root_path = change_root( + os.path.join(script.scratch, 'root'), + normal_install_path + ) assert root_path in result.files_created, str(result) +def test_install_package_with_prefix(script, data): + """ + Test installing a package using pip install --prefix + """ + prefix_path = script.scratch_path / 'prefix' + result = script.pip( + 'install', '--prefix', prefix_path, '-f', data.find_links, + '--no-binary', 'simple', '--no-index', 'simple==1.0', + ) + + if hasattr(sys, "pypy_version_info"): + path = script.scratch / 'prefix' + else: + path = script.scratch / 'prefix' / 'lib' / 'python{0}'.format(pyversion) # noqa + install_path = ( + path / 'site-packages' / 'simple-1.0-py{0}.egg-info'.format(pyversion) + ) + assert install_path in result.files_created, str(result) + + +def test_install_editable_with_prefix(script): + # make a dummy project + pkga_path = script.scratch_path / 'pkga' + pkga_path.mkdir() + pkga_path.join("setup.py").write(textwrap.dedent(""" + from setuptools import setup + setup(name='pkga', + version='0.1') + """)) + + site_packages = os.path.join( + 'prefix', 'lib', 'python{0}'.format(pyversion), 'site-packages') + + # make sure target path is in PYTHONPATH + pythonpath = script.scratch_path / site_packages + pythonpath.makedirs() + script.environ["PYTHONPATH"] = pythonpath + + # install pkga package into the absolute prefix directory + prefix_path = script.scratch_path / 'prefix' + result = script.pip( + 'install', '--editable', pkga_path, '--prefix', prefix_path) + + # assert pkga is installed at correct location + install_path = script.scratch / site_packages / 'pkga.egg-link' + assert install_path in result.files_created, str(result) + + +def test_install_package_conflict_prefix_and_user(script, data): + """ + Test installing a package using pip install --prefix --user errors out + """ + prefix_path = script.scratch_path / 'prefix' + result = script.pip( + 'install', '-f', data.find_links, '--no-index', '--user', + '--prefix', prefix_path, 'simple==1.0', + expect_error=True, quiet=True, + ) + assert ( + "Can not combine '--user' and '--prefix'" in result.stderr + ) + + # skip on win/py3 for now, see issue #782 @pytest.mark.skipif("sys.platform == 'win32' and sys.version_info >= (3,)") def test_install_package_that_emits_unicode(script, data): """ Install a package with a setup.py that emits UTF-8 output and then fails. - This works fine in Python 2, but fails in Python 3 with: - - Traceback (most recent call last): - ... - File "/Users/marc/python/virtualenvs/py3.1-phpserialize/lib/python3.2/site-packages/pip-1.0.2-py3.2.egg/pip/__init__.py", line 230, in call_subprocess - line = console_to_str(stdout.readline()) - File "/Users/marc/python/virtualenvs/py3.1-phpserialize/lib/python3.2/site-packages/pip-1.0.2-py3.2.egg/pip/backwardcompat.py", line 60, in console_to_str - return s.decode(console_encoding) - UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 17: ordinal not in range(128) Refs https://github.com/pypa/pip/issues/326 """ to_install = data.packages.join("BrokenEmitsUTF8") - result = script.pip('install', to_install, expect_error=True, expect_temp=True, quiet=True) - assert 'FakeError: this package designed to fail on install' in result.stdout + result = script.pip( + 'install', to_install, expect_error=True, expect_temp=True, quiet=True, + ) + assert ( + 'FakeError: this package designed to fail on install' in result.stdout + ) assert 'UnicodeDecodeError' not in result.stdout + def test_install_package_with_utf8_setup(script, data): """Install a package with a setup.py that declares a utf-8 encoding.""" to_install = data.packages.join("SetupPyUTF8") script.pip('install', to_install) + def test_install_package_with_latin1_setup(script, data): """Install a package with a setup.py that declares a latin-1 encoding.""" to_install = data.packages.join("SetupPyLatin1") script.pip('install', to_install) -def test_url_req_case_mismatch(script, data): + +def test_url_req_case_mismatch_no_index(script, data): """ - tar ball url requirements (with no egg fragment), that happen to have upper case project names, - should be considered equal to later requirements that reference the project name using lower case. + tar ball url requirements (with no egg fragment), that happen to have upper + case project names, should be considered equal to later requirements that + reference the project name using lower case. - tests/packages contains Upper-1.0.tar.gz and Upper-2.0.tar.gz + tests/data/packages contains Upper-1.0.tar.gz and Upper-2.0.tar.gz 'requiresupper' has install_requires = ['upper'] """ Upper = os.path.join(data.find_links, 'Upper-1.0.tar.gz') - result = script.pip('install', '--no-index', '-f', data.find_links, Upper, 'requiresupper') + result = script.pip( + 'install', '--no-index', '-f', data.find_links, Upper, 'requiresupper' + ) - #only Upper-1.0.tar.gz should get installed. + # only Upper-1.0.tar.gz should get installed. egg_folder = script.site_packages / 'Upper-1.0-py%s.egg-info' % pyversion assert egg_folder in result.files_created, str(result) egg_folder = script.site_packages / 'Upper-2.0-py%s.egg-info' % pyversion assert egg_folder not in result.files_created, str(result) +def test_url_req_case_mismatch_file_index(script, data): + """ + tar ball url requirements (with no egg fragment), that happen to have upper + case project names, should be considered equal to later requirements that + reference the project name using lower case. + + tests/data/packages3 contains Dinner-1.0.tar.gz and Dinner-2.0.tar.gz + 'requiredinner' has install_requires = ['dinner'] + + This test is similar to test_url_req_case_mismatch_no_index; that test + tests behaviour when using "--no-index -f", while this one does the same + test when using "--index-url". Unfortunately this requires a different + set of packages as it requires a prepared index.html file and + subdirectory-per-package structure. + """ + Dinner = os.path.join(data.find_links3, 'dinner', 'Dinner-1.0.tar.gz') + result = script.pip( + 'install', '--index-url', data.find_links3, Dinner, 'requiredinner' + ) + + # only Upper-1.0.tar.gz should get installed. + egg_folder = script.site_packages / 'Dinner-1.0-py%s.egg-info' % pyversion + assert egg_folder in result.files_created, str(result) + egg_folder = script.site_packages / 'Dinner-2.0-py%s.egg-info' % pyversion + assert egg_folder not in result.files_created, str(result) + + +def test_url_incorrect_case_no_index(script, data): + """ + Same as test_url_req_case_mismatch_no_index, except testing for the case + where the incorrect case is given in the name of the package to install + rather than in a requirements file. + """ + result = script.pip( + 'install', '--no-index', '-f', data.find_links, "upper", + ) + + # only Upper-2.0.tar.gz should get installed. + egg_folder = script.site_packages / 'Upper-1.0-py%s.egg-info' % pyversion + assert egg_folder not in result.files_created, str(result) + egg_folder = script.site_packages / 'Upper-2.0-py%s.egg-info' % pyversion + assert egg_folder in result.files_created, str(result) + + +def test_url_incorrect_case_file_index(script, data): + """ + Same as test_url_req_case_mismatch_file_index, except testing for the case + where the incorrect case is given in the name of the package to install + rather than in a requirements file. + """ + result = script.pip( + 'install', '--index-url', data.find_links3, "dinner", + expect_stderr=True, + ) + + # only Upper-2.0.tar.gz should get installed. + egg_folder = script.site_packages / 'Dinner-1.0-py%s.egg-info' % pyversion + assert egg_folder not in result.files_created, str(result) + egg_folder = script.site_packages / 'Dinner-2.0-py%s.egg-info' % pyversion + assert egg_folder in result.files_created, str(result) + + +@pytest.mark.network def test_compiles_pyc(script): """ Test installing with --compile on """ del script.environ["PYTHONDONTWRITEBYTECODE"] - script.pip("install", "--compile", "--no-use-wheel", "INITools==0.2") + script.pip("install", "--compile", "--no-binary=:all:", "INITools==0.2") # There are many locations for the __init__.pyc file so attempt to find # any of them exists = [ - os.path.exists(script.site_packages_path/"initools/__init__.pyc"), + os.path.exists(script.site_packages_path / "initools/__init__.pyc"), ] exists += glob.glob( - script.site_packages_path/"initools/__pycache__/__init__*.pyc" + script.site_packages_path / "initools/__pycache__/__init__*.pyc" ) assert any(exists) +@pytest.mark.network def test_no_compiles_pyc(script, data): """ Test installing from wheel with --compile on """ del script.environ["PYTHONDONTWRITEBYTECODE"] - script.pip("install", "--no-compile", "--no-use-wheel", "INITools==0.2") + script.pip("install", "--no-compile", "--no-binary=:all:", "INITools==0.2") # There are many locations for the __init__.pyc file so attempt to find # any of them exists = [ - os.path.exists(script.site_packages_path/"initools/__init__.pyc"), + os.path.exists(script.site_packages_path / "initools/__init__.pyc"), ] exists += glob.glob( - script.site_packages_path/"initools/__pycache__/__init__*.pyc" + script.site_packages_path / "initools/__pycache__/__init__*.pyc" ) assert not any(exists) + + +def test_install_upgrade_editable_depending_on_other_editable(script): + script.scratch_path.join("pkga").mkdir() + pkga_path = script.scratch_path / 'pkga' + pkga_path.join("setup.py").write(textwrap.dedent(""" + from setuptools import setup + setup(name='pkga', + version='0.1') + """)) + script.pip('install', '--editable', pkga_path) + result = script.pip('list', '--format=freeze') + assert "pkga==0.1" in result.stdout + + script.scratch_path.join("pkgb").mkdir() + pkgb_path = script.scratch_path / 'pkgb' + pkgb_path.join("setup.py").write(textwrap.dedent(""" + from setuptools import setup + setup(name='pkgb', + version='0.1', + install_requires=['pkga']) + """)) + script.pip('install', '--upgrade', '--editable', pkgb_path, '--no-index') + result = script.pip('list', '--format=freeze') + assert "pkgb==0.1" in result.stdout + + +def test_install_subprocess_output_handling(script, data): + args = ['install', data.src.join('chattymodule')] + + # Regular install should not show output from the chatty setup.py + result = script.pip(*args) + assert 0 == result.stdout.count("HELLO FROM CHATTYMODULE") + script.pip("uninstall", "-y", "chattymodule") + + # With --verbose we should show the output. + # Only count examples with sys.argv[1] == egg_info, because we call + # setup.py multiple times, which should not count as duplicate output. + result = script.pip(*(args + ["--verbose"])) + assert 1 == result.stdout.count("HELLO FROM CHATTYMODULE egg_info") + script.pip("uninstall", "-y", "chattymodule") + + # If the install fails, then we *should* show the output... but only once, + # even if --verbose is given. + result = script.pip(*(args + ["--global-option=--fail"]), + expect_error=True) + assert 1 == result.stdout.count("I DIE, I DIE") + + result = script.pip(*(args + ["--global-option=--fail", "--verbose"]), + expect_error=True) + assert 1 == result.stdout.count("I DIE, I DIE") + + +def test_install_log(script, data, tmpdir): + # test that verbose logs go to "--log" file + f = tmpdir.join("log.txt") + args = ['--log=%s' % f, + 'install', data.src.join('chattymodule')] + result = script.pip(*args) + assert 0 == result.stdout.count("HELLO FROM CHATTYMODULE") + with open(f, 'r') as fp: + # one from egg_info, one from install + assert 2 == fp.read().count("HELLO FROM CHATTYMODULE") + + +def test_install_topological_sort(script, data): + args = ['install', 'TopoRequires4', '-f', data.packages] + res = str(script.pip(*args, expect_error=False)) + order1 = 'TopoRequires, TopoRequires2, TopoRequires3, TopoRequires4' + order2 = 'TopoRequires, TopoRequires3, TopoRequires2, TopoRequires4' + assert order1 in res or order2 in res, res + + +def test_install_wheel_broken(script, data): + script.pip('install', 'wheel') + res = script.pip( + 'install', '--no-index', '-f', data.find_links, 'wheelbroken', + expect_stderr=True) + assert "Successfully installed wheelbroken-0.1" in str(res), str(res) + + +def test_cleanup_after_failed_wheel(script, data): + script.pip('install', 'wheel') + res = script.pip( + 'install', '--no-index', '-f', data.find_links, 'wheelbrokenafter', + expect_stderr=True) + # One of the effects of not cleaning up is broken scripts: + script_py = script.bin_path / "script.py" + assert script_py.exists, script_py + shebang = open(script_py, 'r').readline().strip() + assert shebang != '#!python', shebang + # OK, assert that we *said* we were cleaning up: + assert "Running setup.py clean for wheelbrokenafter" in str(res), str(res) + + +def test_install_builds_wheels(script, data): + # NB This incidentally tests a local tree + tarball inputs + # see test_install_editable_from_git_autobuild_wheel for editable + # vcs coverage. + script.pip('install', 'wheel') + to_install = data.packages.join('requires_wheelbroken_upper') + res = script.pip( + 'install', '--no-index', '-f', data.find_links, + to_install, expect_stderr=True) + expected = ("Successfully installed requires-wheelbroken-upper-0" + " upper-2.0 wheelbroken-0.1") + # Must have installed it all + assert expected in str(res), str(res) + root = appdirs.user_cache_dir('pip') + wheels = [] + for top, dirs, files in os.walk(os.path.join(root, "wheels")): + wheels.extend(files) + # and built wheels for upper and wheelbroken + assert "Running setup.py bdist_wheel for upper" in str(res), str(res) + assert "Running setup.py bdist_wheel for wheelb" in str(res), str(res) + # But not requires_wheel... which is a local dir and thus uncachable. + assert "Running setup.py bdist_wheel for requir" not in str(res), str(res) + # wheelbroken has to run install + # into the cache + assert wheels != [], str(res) + # and installed from the wheel + assert "Running setup.py install for upper" not in str(res), str(res) + # the local tree can't build a wheel (because we can't assume that every + # build will have a suitable unique key to cache on). + assert "Running setup.py install for requires-wheel" in str(res), str(res) + # wheelbroken has to run install + assert "Running setup.py install for wheelb" in str(res), str(res) + # We want to make sure we used the correct implementation tag + assert wheels == [ + "Upper-2.0-{0}-none-any.whl".format(pep425tags.implementation_tag), + ] + + +def test_install_no_binary_disables_building_wheels(script, data): + script.pip('install', 'wheel') + to_install = data.packages.join('requires_wheelbroken_upper') + res = script.pip( + 'install', '--no-index', '--no-binary=upper', '-f', data.find_links, + to_install, expect_stderr=True) + expected = ("Successfully installed requires-wheelbroken-upper-0" + " upper-2.0 wheelbroken-0.1") + # Must have installed it all + assert expected in str(res), str(res) + root = appdirs.user_cache_dir('pip') + wheels = [] + for top, dirs, files in os.walk(root): + wheels.extend(files) + # and built wheels for wheelbroken only + assert "Running setup.py bdist_wheel for wheelb" in str(res), str(res) + # But not requires_wheel... which is a local dir and thus uncachable. + assert "Running setup.py bdist_wheel for requir" not in str(res), str(res) + # Nor upper, which was blacklisted + assert "Running setup.py bdist_wheel for upper" not in str(res), str(res) + # wheelbroken has to run install + # into the cache + assert wheels != [], str(res) + # the local tree can't build a wheel (because we can't assume that every + # build will have a suitable unique key to cache on). + assert "Running setup.py install for requires-wheel" in str(res), str(res) + # And these two fell back to sdist based installed. + assert "Running setup.py install for wheelb" in str(res), str(res) + assert "Running setup.py install for upper" in str(res), str(res) + + +def test_install_no_binary_disables_cached_wheels(script, data): + script.pip('install', 'wheel') + # Seed the cache + script.pip( + 'install', '--no-index', '-f', data.find_links, + 'upper') + script.pip('uninstall', 'upper', '-y') + res = script.pip( + 'install', '--no-index', '--no-binary=:all:', '-f', data.find_links, + 'upper', expect_stderr=True) + assert "Successfully installed upper-2.0" in str(res), str(res) + # No wheel building for upper, which was blacklisted + assert "Running setup.py bdist_wheel for upper" not in str(res), str(res) + # Must have used source, not a cached wheel to install upper. + assert "Running setup.py install for upper" in str(res), str(res) + + +def test_install_editable_with_wrong_egg_name(script): + script.scratch_path.join("pkga").mkdir() + pkga_path = script.scratch_path / 'pkga' + pkga_path.join("setup.py").write(textwrap.dedent(""" + from setuptools import setup + setup(name='pkga', + version='0.1') + """)) + result = script.pip( + 'install', '--editable', 'file://%s#egg=pkgb' % pkga_path, + expect_error=True) + assert ("egg_info for package pkgb produced metadata " + "for project name pkga. Fix your #egg=pkgb " + "fragments.") in result.stderr + assert "Successfully installed pkga" in str(result), str(result) + + +def test_install_tar_xz(script, data): + try: + import lzma # noqa + except ImportError: + pytest.skip("No lzma support") + res = script.pip('install', data.packages / 'singlemodule-0.0.1.tar.xz') + assert "Successfully installed singlemodule-0.0.1" in res.stdout, res + + +def test_install_tar_lzma(script, data): + try: + import lzma # noqa + except ImportError: + pytest.skip("No lzma support") + res = script.pip('install', data.packages / 'singlemodule-0.0.1.tar.lzma') + assert "Successfully installed singlemodule-0.0.1" in res.stdout, res + + +def test_double_install(script, data): + """ + Test double install passing with two same version requirements + """ + result = script.pip('install', 'pip', 'pip', expect_error=False) + msg = "Double requirement given: pip (already in pip, name='pip')" + assert msg not in result.stderr + + +def test_double_install_fail(script, data): + """ + Test double install failing with two different version requirements + """ + result = script.pip('install', 'pip==*', 'pip==7.1.2', expect_error=True) + msg = ("Double requirement given: pip==7.1.2 (already in pip==*, " + "name='pip')") + assert msg in result.stderr diff --git a/tests/functional/test_install_bundle.py b/tests/functional/test_install_bundle.py deleted file mode 100644 index 073d20c68bf..00000000000 --- a/tests/functional/test_install_bundle.py +++ /dev/null @@ -1,10 +0,0 @@ -def test_install_pybundle(script, data): - """ - Test intalling a *.pybundle file - """ - result = script.pip_install_local( - data.packages.join("simplebundle.pybundle"), - expect_temp=True, - ) - result.assert_installed('simple', editable=False) - result.assert_installed('simple2', editable=False) diff --git a/tests/functional/test_install_cleanup.py b/tests/functional/test_install_cleanup.py index d8513a67b4e..49fc5a0abe5 100644 --- a/tests/functional/test_install_cleanup.py +++ b/tests/functional/test_install_cleanup.py @@ -1,9 +1,9 @@ import os +import pytest -from os.path import abspath, exists, join +from os.path import exists from tests.lib.local_repos import local_checkout -from tests.lib.path import Path from pip.locations import write_delete_marker_file from pip.status_codes import PREVIOUS_BUILD_DIR_ERROR @@ -12,35 +12,47 @@ def test_cleanup_after_install(script, data): """ Test clean up after installing a package. """ - script.pip('install', '--no-index', '--find-links=%s' % data.find_links, 'simple') - build = script.venv_path/"build" - src = script.venv_path/"src" + script.pip( + 'install', '--no-index', '--find-links=%s' % data.find_links, 'simple' + ) + build = script.venv_path / "build" + src = script.venv_path / "src" assert not exists(build), "build/ dir still exists: %s" % build assert not exists(src), "unexpected src/ dir exists: %s" % src script.assert_no_temp() +@pytest.mark.network def test_no_clean_option_blocks_cleaning_after_install(script, data): """ Test --no-clean option blocks cleaning after install """ - result = script.pip('install', '--no-clean', '--no-index', '--find-links=%s' % data.find_links, 'simple') - build = script.venv_path/'build'/'simple' - assert exists(build), "build/simple should still exist %s" % str(result) + build = script.base_path / 'pip-build' + script.pip( + 'install', '--no-clean', '--no-index', '--build', build, + '--find-links=%s' % data.find_links, 'simple', + ) + assert exists(build) +@pytest.mark.network def test_cleanup_after_install_editable_from_hg(script, tmpdir): """ Test clean up after cloning from Mercurial. """ - script.pip('install', - '-e', - '%s#egg=ScriptTest' % - local_checkout('hg+https://bitbucket.org/ianb/scripttest', tmpdir.join("cache")), - expect_error=True) - build = script.venv_path/'build' - src = script.venv_path/'src' + script.pip( + 'install', + '-e', + '%s#egg=ScriptTest' % + local_checkout( + 'hg+https://bitbucket.org/ianb/scripttest', + tmpdir.join("cache"), + ), + expect_error=True, + ) + build = script.venv_path / 'build' + src = script.venv_path / 'src' assert not exists(build), "build/ dir still exists: %s" % build assert exists(src), "expected src/ dir doesn't exist: %s" % src script.assert_no_temp() @@ -52,62 +64,43 @@ def test_cleanup_after_install_from_local_directory(script, data): """ to_install = data.packages.join("FSPkg") script.pip('install', to_install, expect_error=False) - build = script.venv_path/'build' - src = script.venv_path/'src' + build = script.venv_path / 'build' + src = script.venv_path / 'src' assert not exists(build), "unexpected build/ dir exists: %s" % build assert not exists(src), "unexpected src/ dir exist: %s" % src script.assert_no_temp() -def test_no_install_and_download_should_not_leave_build_dir(script): - """ - It should remove build/ dir if it was pip that created - """ - script.scratch_path.join("downloaded_packages").mkdir() - assert not os.path.exists(script.venv_path/'/build') - result = script.pip('install', '--no-install', 'INITools==0.2', '-d', 'downloaded_packages') - assert Path('scratch')/'downloaded_packages/build' not in result.files_created, 'pip should not leave build/ dir' - assert not os.path.exists(script.venv_path/'/build'), "build/ dir should be deleted" - - def test_cleanup_req_satisifed_no_name(script, data): """ Test cleanup when req is already satisfied, and req has no 'name' """ - #this test confirms Issue #420 is fixed - #reqs with no 'name' that were already satisfied were leaving behind tmp build dirs - #2 examples of reqs that would do this + # this test confirms Issue #420 is fixed + # reqs with no 'name' that were already satisfied were leaving behind tmp + # build dirs + # 2 examples of reqs that would do this # 1) https://bitbucket.org/ianb/initools/get/tip.zip # 2) parent-0.1.tar.gz dist = data.packages.join("parent-0.1.tar.gz") - result = script.pip('install', dist) - result = script.pip('install', dist) - build = script.venv_path/'build' - assert not exists(build), "unexpected build/ dir exists: %s" % build - script.assert_no_temp() + script.pip('install', dist) + script.pip('install', dist) -def test_download_should_not_delete_existing_build_dir(script): - """ - It should not delete build/ if existing before run the command - """ - script.venv_path.join("build").mkdir() - script.venv_path.join("build", "somefile.txt").write("I am not empty!") - script.pip('install', '--no-install', 'INITools==0.2', '-d', '.') - with open(script.venv_path/'build'/'somefile.txt') as fp: - content = fp.read() - assert os.path.exists(script.venv_path/'build'), "build/ should be left if it exists before pip run" - assert content == 'I am not empty!', "it should not affect build/ and its content" - assert ['somefile.txt'] == os.listdir(script.venv_path/'build') + build = script.venv_path / 'build' + assert not exists(build), "unexpected build/ dir exists: %s" % build + script.assert_no_temp() def test_cleanup_after_install_exception(script, data): """ Test clean up after a 'setup.py install' exception. """ - #broken==0.2broken fails during install; see packages readme file - result = script.pip('install', '-f', data.find_links, '--no-index', 'broken==0.2broken', expect_error=True) - build = script.venv_path/'build' + # broken==0.2broken fails during install; see packages readme file + result = script.pip( + 'install', '-f', data.find_links, '--no-index', 'broken==0.2broken', + expect_error=True, + ) + build = script.venv_path / 'build' assert not exists(build), "build/ dir still exists: %s" % result.stdout script.assert_no_temp() @@ -116,23 +109,32 @@ def test_cleanup_after_egg_info_exception(script, data): """ Test clean up after a 'setup.py egg_info' exception. """ - #brokenegginfo fails during egg_info; see packages readme file - result = script.pip('install', '-f', data.find_links, '--no-index', 'brokenegginfo==0.1', expect_error=True) - build = script.venv_path/'build' + # brokenegginfo fails during egg_info; see packages readme file + result = script.pip( + 'install', '-f', data.find_links, '--no-index', 'brokenegginfo==0.1', + expect_error=True, + ) + build = script.venv_path / 'build' assert not exists(build), "build/ dir still exists: %s" % result.stdout script.assert_no_temp() +@pytest.mark.network def test_cleanup_prevented_upon_build_dir_exception(script, data): """ Test no cleanup occurs after a PreviousBuildDirError """ - build = script.venv_path/'build'/'simple' - os.makedirs(build) - write_delete_marker_file(script.venv_path/'build') - build.join("setup.py").write("#") - result = script.pip('install', '-f', data.find_links, '--no-index', 'simple', expect_error=True) + build = script.venv_path / 'build' + build_simple = build / 'simple' + os.makedirs(build_simple) + write_delete_marker_file(build) + build_simple.join("setup.py").write("#") + result = script.pip( + 'install', '-f', data.find_links, '--no-index', 'simple', + '--build', build, + expect_error=True, + ) assert result.returncode == PREVIOUS_BUILD_DIR_ERROR - assert "pip can't proceed" in result.stdout, result.stdout - assert exists(build) + assert "pip can't proceed" in result.stderr + assert exists(build_simple) diff --git a/tests/functional/test_install_compat.py b/tests/functional/test_install_compat.py index cbb6f0f4c75..d656005242e 100644 --- a/tests/functional/test_install_compat.py +++ b/tests/functional/test_install_compat.py @@ -3,9 +3,11 @@ """ import os +import pytest from tests.lib import pyversion, assert_all_changes +@pytest.mark.network def test_debian_egg_name_workaround(script): """ We can uninstall packages installed with the pyversion removed from the @@ -23,8 +25,9 @@ def test_debian_egg_name_workaround(script): script.site_packages, "INITools-0.2-py%s.egg-info" % pyversion) # Debian only removes pyversion for global installs, not inside a venv - # so even if this test runs on a Debian/Ubuntu system with broken setuptools, - # since our test runs inside a venv we'll still have the normal .egg-info + # so even if this test runs on a Debian/Ubuntu system with broken + # setuptools, since our test runs inside a venv we'll still have the normal + # .egg-info assert egg_info in result.files_created, "Couldn't find %s" % egg_info # The Debian no-pyversion version of the .egg-info @@ -40,7 +43,7 @@ def test_debian_egg_name_workaround(script): # Try the uninstall and verify that everything is removed. result2 = script.pip("uninstall", "INITools", "-y") - assert_all_changes(result, result2, [script.venv/'build', 'cache']) + assert_all_changes(result, result2, [script.venv / 'build', 'cache']) def test_setup_py_with_dos_line_endings(script, data): diff --git a/tests/functional/test_install_config.py b/tests/functional/test_install_config.py index 7a896d35ce4..03f0a1706ce 100644 --- a/tests/functional/test_install_config.py +++ b/tests/functional/test_install_config.py @@ -1,6 +1,7 @@ import os import tempfile import textwrap +import pytest def test_options_from_env_vars(script): @@ -11,7 +12,10 @@ def test_options_from_env_vars(script): script.environ['PIP_NO_INDEX'] = '1' result = script.pip('install', '-vvv', 'INITools', expect_error=True) assert "Ignoring indexes:" in result.stdout, str(result) - assert "DistributionNotFound: No distributions at all found for INITools" in result.stdout + assert ( + "DistributionNotFound: No matching distribution found for INITools" + in result.stdout + ) def test_command_line_options_override_env_vars(script, virtualenv): @@ -19,15 +23,23 @@ def test_command_line_options_override_env_vars(script, virtualenv): Test that command line options override environmental variables. """ - script.environ['PIP_INDEX_URL'] = 'http://b.pypi.python.org/simple/' + script.environ['PIP_INDEX_URL'] = 'https://b.pypi.python.org/simple/' result = script.pip('install', '-vvv', 'INITools', expect_error=True) - assert "Getting page http://b.pypi.python.org/simple/INITools" in result.stdout + assert ( + "Getting page https://b.pypi.python.org/simple/initools" + in result.stdout + ) virtualenv.clear() - result = script.pip('install', '-vvv', '--index-url', 'http://download.zope.org/ppix', 'INITools', expect_error=True) + result = script.pip( + 'install', '-vvv', '--index-url', 'https://download.zope.org/ppix', + 'INITools', + expect_error=True, + ) assert "b.pypi.python.org" not in result.stdout - assert "Getting page http://download.zope.org/ppix" in result.stdout + assert "Getting page https://download.zope.org/ppix" in result.stdout +@pytest.mark.network def test_env_vars_override_config_file(script, virtualenv): """ Test that environmental variables override settings in config files. @@ -47,44 +59,73 @@ def _test_env_vars_override_config_file(script, virtualenv, config_file): # set this to make pip load it script.environ['PIP_CONFIG_FILE'] = config_file # It's important that we test this particular config value ('no-index') - # because their is/was a bug which only shows up in cases in which + # because there is/was a bug which only shows up in cases in which # 'config-item' and 'config_item' hash to the same value modulo the size # of the config dictionary. - (script.scratch_path/config_file).write(textwrap.dedent("""\ + (script.scratch_path / config_file).write(textwrap.dedent("""\ [global] no-index = 1 """)) result = script.pip('install', '-vvv', 'INITools', expect_error=True) - assert "DistributionNotFound: No distributions at all found for INITools" in result.stdout + assert ( + "DistributionNotFound: No matching distribution found for INITools" + in result.stdout + ) script.environ['PIP_NO_INDEX'] = '0' virtualenv.clear() result = script.pip('install', '-vvv', 'INITools', expect_error=True) assert "Successfully installed INITools" in result.stdout +@pytest.mark.network def test_command_line_append_flags(script, virtualenv, data): """ - Test command line flags that append to defaults set by environmental variables. + Test command line flags that append to defaults set by environmental + variables. """ script.environ['PIP_FIND_LINKS'] = 'http://pypi.pinaxproject.com' - result = script.pip('install', '-vvv', 'INITools', expect_error=True) - assert "Analyzing links from page http://pypi.pinaxproject.com" in result.stdout + result = script.pip( + 'install', '-vvv', 'INITools', '--trusted-host', + 'pypi.pinaxproject.com', + expect_error=True, + ) + assert ( + "Analyzing links from page http://pypi.pinaxproject.com" + in result.stdout + ) virtualenv.clear() - result = script.pip('install', '-vvv', '--find-links', data.find_links, 'INITools', expect_error=True) - assert "Analyzing links from page http://pypi.pinaxproject.com" in result.stdout + result = script.pip( + 'install', '-vvv', '--find-links', data.find_links, 'INITools', + '--trusted-host', 'pypi.pinaxproject.com', + expect_error=True, + ) + assert ( + "Analyzing links from page http://pypi.pinaxproject.com" + in result.stdout + ) assert "Skipping link %s" % data.find_links in result.stdout +@pytest.mark.network def test_command_line_appends_correctly(script, data): """ Test multiple appending options set by environmental variables. """ - script.environ['PIP_FIND_LINKS'] = 'http://pypi.pinaxproject.com %s' % data.find_links - result = script.pip('install', '-vvv', 'INITools', expect_error=True) - - assert "Analyzing links from page http://pypi.pinaxproject.com" in result.stdout, result.stdout + script.environ['PIP_FIND_LINKS'] = ( + 'http://pypi.pinaxproject.com %s' % data.find_links + ) + result = script.pip( + 'install', '-vvv', 'INITools', '--trusted-host', + 'pypi.pinaxproject.com', + expect_error=True, + ) + + assert ( + "Analyzing links from page http://pypi.pinaxproject.com" + in result.stdout + ), result.stdout assert "Skipping link %s" % data.find_links in result.stdout @@ -105,36 +146,72 @@ def test_config_file_override_stack(script, virtualenv): def _test_config_file_override_stack(script, virtualenv, config_file): - script.environ['PIP_CONFIG_FILE'] = config_file # set this to make pip load it - (script.scratch_path/config_file).write(textwrap.dedent("""\ + # set this to make pip load it + script.environ['PIP_CONFIG_FILE'] = config_file + (script.scratch_path / config_file).write(textwrap.dedent("""\ [global] - index-url = http://download.zope.org/ppix + index-url = https://download.zope.org/ppix """)) result = script.pip('install', '-vvv', 'INITools', expect_error=True) - assert "Getting page http://download.zope.org/ppix/INITools" in result.stdout + assert ( + "Getting page https://download.zope.org/ppix/initools" in result.stdout + ) virtualenv.clear() - (script.scratch_path/config_file).write(textwrap.dedent("""\ + (script.scratch_path / config_file).write(textwrap.dedent("""\ [global] - index-url = http://download.zope.org/ppix + index-url = https://download.zope.org/ppix [install] - index-url = http://pypi.appspot.com/ + index-url = https://pypi.gocept.com/ """)) result = script.pip('install', '-vvv', 'INITools', expect_error=True) - assert "Getting page http://pypi.appspot.com/INITools" in result.stdout - result = script.pip('install', '-vvv', '--index-url', 'http://pypi.python.org/simple', 'INITools', expect_error=True) - assert "Getting page http://download.zope.org/ppix/INITools" not in result.stdout - assert "Getting page http://pypi.appspot.com/INITools" not in result.stdout - assert "Getting page http://pypi.python.org/simple/INITools" in result.stdout - - -def test_log_file_no_directory(): + assert "Getting page https://pypi.gocept.com/initools" in result.stdout + result = script.pip( + 'install', '-vvv', '--index-url', 'https://pypi.python.org/simple', + 'INITools', + expect_error=True, + ) + assert ( + "Getting page http://download.zope.org/ppix/INITools" + not in result.stdout + ) + assert "Getting page https://pypi.gocept.com/INITools" not in result.stdout + assert ( + "Getting page https://pypi.python.org/simple/initools" in result.stdout + ) + + +def test_options_from_venv_config(script, virtualenv): """ - Test opening a log file with no directory name. + Test if ConfigOptionParser reads a virtualenv-local config file """ - from pip.basecommand import open_logfile - fp = open_logfile('testpip.log') - fp.write('can write') - fp.close() - assert os.path.exists(fp.name) - os.remove(fp.name) + from pip.locations import config_basename + conf = "[global]\nno-index = true" + ini = virtualenv.location / config_basename + with open(ini, 'w') as f: + f.write(conf) + result = script.pip('install', '-vvv', 'INITools', expect_error=True) + assert "Ignoring indexes:" in result.stdout, str(result) + assert ( + "DistributionNotFound: No matching distribution found for INITools" + in result.stdout + ) + + +def test_install_no_binary_via_config_disables_cached_wheels(script, data): + script.pip('install', 'wheel') + config_file = tempfile.NamedTemporaryFile(mode='wt') + script.environ['PIP_CONFIG_FILE'] = config_file.name + config_file.write(textwrap.dedent("""\ + [global] + no-binary = :all: + """)) + config_file.flush() + res = script.pip( + 'install', '--no-index', '-f', data.find_links, + 'upper', expect_stderr=True) + assert "Successfully installed upper-2.0" in str(res), str(res) + # No wheel building for upper, which was blacklisted + assert "Running setup.py bdist_wheel for upper" not in str(res), str(res) + # Must have used source, not a cached wheel to install upper. + assert "Running setup.py install for upper" in str(res), str(res) diff --git a/tests/functional/test_install_download.py b/tests/functional/test_install_download.py index dd2f8f6023c..c330a3c7bee 100644 --- a/tests/functional/test_install_download.py +++ b/tests/functional/test_install_download.py @@ -1,52 +1,73 @@ import os import textwrap +import pytest from tests.lib.path import Path +@pytest.mark.network def test_download_if_requested(script): """ It should download (in the scratch path) and not install if requested. """ - result = script.pip('install', 'INITools==0.1', '-d', '.', expect_error=True) - assert Path('scratch')/ 'INITools-0.1.tar.gz' in result.files_created - assert script.site_packages/ 'initools' not in result.files_created + result = script.pip( + 'install', 'INITools==0.1', '-d', '.', expect_error=True + ) + assert Path('scratch') / 'INITools-0.1.tar.gz' in result.files_created + assert script.site_packages / 'initools' not in result.files_created +@pytest.mark.network def test_download_wheel(script): """ Test using "pip install --download" to download a *.whl archive. FIXME: this test could use a local --find-links dir, but -d with local --find-links has a bug https://github.com/pypa/pip/issues/1111 """ - result = script.pip('install', '--use-wheel', - '-f', 'https://bitbucket.org/pypa/pip-test-package/downloads', - '-d', '.', 'pip-test-package') - assert Path('scratch')/ 'pip_test_package-0.1.1-py2.py3-none-any.whl' in result.files_created - assert script.site_packages/ 'piptestpackage' not in result.files_created + result = script.pip( + 'install', + '-f', 'https://bitbucket.org/pypa/pip-test-package/downloads', + '-d', '.', 'pip-test-package', + expect_stderr=True, + ) + assert ( + Path('scratch') / 'pip_test_package-0.1.1-py2.py3-none-any.whl' + in result.files_created + ) + assert script.site_packages / 'piptestpackage' not in result.files_created +@pytest.mark.network def test_single_download_from_requirements_file(script): """ - It should support download (in the scratch path) from PyPi from a requirements file + It should support download (in the scratch path) from PyPi from a + requirements file """ script.scratch_path.join("test-req.txt").write(textwrap.dedent(""" INITools==0.1 """)) - result = script.pip('install', '-r', script.scratch_path/ 'test-req.txt', '-d', '.', expect_error=True) - assert Path('scratch')/ 'INITools-0.1.tar.gz' in result.files_created - assert script.site_packages/ 'initools' not in result.files_created + result = script.pip( + 'install', '-r', script.scratch_path / 'test-req.txt', '-d', '.', + expect_error=True, + ) + assert Path('scratch') / 'INITools-0.1.tar.gz' in result.files_created + assert script.site_packages / 'initools' not in result.files_created +@pytest.mark.network def test_download_should_download_dependencies(script): """ It should download dependencies (in the scratch path) """ - result = script.pip('install', 'Paste[openid]==1.7.5.1', '-d', '.', expect_error=True) - assert Path('scratch')/ 'Paste-1.7.5.1.tar.gz' in result.files_created - openid_tarball_prefix = str(Path('scratch')/ 'python-openid-') - assert any(path.startswith(openid_tarball_prefix) for path in result.files_created) - assert script.site_packages/ 'openid' not in result.files_created + result = script.pip( + 'install', 'Paste[openid]==1.7.5.1', '-d', '.', expect_error=True, + ) + assert Path('scratch') / 'Paste-1.7.5.1.tar.gz' in result.files_created + openid_tarball_prefix = str(Path('scratch') / 'python-openid-') + assert any( + path.startswith(openid_tarball_prefix) for path in result.files_created + ) + assert script.site_packages / 'openid' not in result.files_created def test_download_wheel_archive(script, data): @@ -57,7 +78,8 @@ def test_download_wheel_archive(script, data): wheel_path = os.path.join(data.find_links, wheel_filename) result = script.pip( 'install', wheel_path, - '-d', '.', '--no-deps' + '-d', '.', '--no-deps', + expect_stderr=True, ) assert Path('scratch') / wheel_filename in result.files_created @@ -71,12 +93,14 @@ def test_download_should_download_wheel_deps(script, data): wheel_path = os.path.join(data.find_links, wheel_filename) result = script.pip( 'install', wheel_path, - '-d', '.', '--find-links', data.find_links, '--no-index' + '-d', '.', '--find-links', data.find_links, '--no-index', + expect_stderr=True, ) assert Path('scratch') / wheel_filename in result.files_created assert Path('scratch') / dep_filename in result.files_created +@pytest.mark.network def test_download_should_skip_existing_files(script): """ It should not download files already existing in the scratch dir @@ -85,9 +109,12 @@ def test_download_should_skip_existing_files(script): INITools==0.1 """)) - result = script.pip('install', '-r', script.scratch_path/ 'test-req.txt', '-d', '.', expect_error=True) - assert Path('scratch')/ 'INITools-0.1.tar.gz' in result.files_created - assert script.site_packages/ 'initools' not in result.files_created + result = script.pip( + 'install', '-r', script.scratch_path / 'test-req.txt', '-d', '.', + expect_error=True, + ) + assert Path('scratch') / 'INITools-0.1.tar.gz' in result.files_created + assert script.site_packages / 'initools' not in result.files_created # adding second package to test-req.txt script.scratch_path.join("test-req.txt").write(textwrap.dedent(""" @@ -96,9 +123,30 @@ def test_download_should_skip_existing_files(script): """)) # only the second package should be downloaded - result = script.pip('install', '-r', script.scratch_path/ 'test-req.txt', '-d', '.', expect_error=True) - openid_tarball_prefix = str(Path('scratch')/ 'python-openid-') - assert any(path.startswith(openid_tarball_prefix) for path in result.files_created) - assert Path('scratch')/ 'INITools-0.1.tar.gz' not in result.files_created - assert script.site_packages/ 'initools' not in result.files_created - assert script.site_packages/ 'openid' not in result.files_created + result = script.pip( + 'install', '-r', script.scratch_path / 'test-req.txt', '-d', '.', + expect_error=True, + ) + openid_tarball_prefix = str(Path('scratch') / 'python-openid-') + assert any( + path.startswith(openid_tarball_prefix) for path in result.files_created + ) + assert Path('scratch') / 'INITools-0.1.tar.gz' not in result.files_created + assert script.site_packages / 'initools' not in result.files_created + assert script.site_packages / 'openid' not in result.files_created + + +@pytest.mark.network +def test_download_vcs_link(script): + """ + It should allow -d flag for vcs links, regression test for issue #798. + """ + result = script.pip( + 'install', '-d', '.', 'git+git://github.com/pypa/pip-test-package.git', + expect_stderr=True, + ) + assert ( + Path('scratch') / 'pip-test-package-0.1.1.zip' + in result.files_created + ) + assert script.site_packages / 'piptestpackage' not in result.files_created diff --git a/tests/functional/test_install_extras.py b/tests/functional/test_install_extras.py index 0a325ea2596..28759a27c92 100644 --- a/tests/functional/test_install_extras.py +++ b/tests/functional/test_install_extras.py @@ -1,23 +1,105 @@ +import pytest from os.path import join +@pytest.mark.network def test_simple_extras_install_from_pypi(script): """ Test installing a package from PyPI using extras dependency Paste[openid]. """ - result = script.pip('install', 'Paste[openid]==1.7.5.1', expect_stderr=True) + result = script.pip( + 'install', 'Paste[openid]==1.7.5.1', expect_stderr=True, + ) initools_folder = script.site_packages / 'openid' assert initools_folder in result.files_created, result.files_created +def test_extras_after_wheel(script, data): + """ + Test installing a package with extras after installing from a wheel. + """ + simple = script.site_packages / 'simple' + + no_extra = script.pip( + 'install', '--no-index', '-f', data.find_links, + 'requires_simple_extra', expect_stderr=True, + ) + assert simple not in no_extra.files_created, no_extra.files_created + + extra = script.pip( + 'install', '--no-index', '-f', data.find_links, + 'requires_simple_extra[extra]', expect_stderr=True, + ) + assert simple in extra.files_created, extra.files_created + + +@pytest.mark.network def test_no_extras_uninstall(script): """ No extras dependency gets uninstalled when the root package is uninstalled """ - result = script.pip('install', 'Paste[openid]==1.7.5.1', expect_stderr=True) - assert join(script.site_packages, 'paste') in result.files_created, sorted(result.files_created.keys()) - assert join(script.site_packages, 'openid') in result.files_created, sorted(result.files_created.keys()) + result = script.pip( + 'install', 'Paste[openid]==1.7.5.1', expect_stderr=True, + ) + assert join(script.site_packages, 'paste') in result.files_created, ( + sorted(result.files_created.keys()) + ) + assert join(script.site_packages, 'openid') in result.files_created, ( + sorted(result.files_created.keys()) + ) result2 = script.pip('uninstall', 'Paste', '-y') # openid should not be uninstalled initools_folder = script.site_packages / 'openid' - assert not initools_folder in result2.files_deleted, result.files_deleted + assert initools_folder not in result2.files_deleted, result.files_deleted + + +def test_nonexistent_extra_warns_user_no_wheel(script, data): + """ + A warning is logged telling the user that the extra option they requested + does not exist in the project they are wishing to install. + + This exercises source installs. + """ + result = script.pip( + 'install', '--no-binary=:all:', '--no-index', + '--find-links=' + data.find_links, + 'simple[nonexistent]', expect_stderr=True, + ) + assert ( + "simple 3.0 does not provide the extra 'nonexistent'" + in result.stderr + ) + + +def test_nonexistent_extra_warns_user_with_wheel(script, data): + """ + A warning is logged telling the user that the extra option they requested + does not exist in the project they are wishing to install. + + This exercises wheel installs. + """ + result = script.pip( + 'install', '--no-index', + '--find-links=' + data.find_links, + 'simplewheel[nonexistent]', expect_stderr=True, + ) + assert ( + "simplewheel 2.0 does not provide the extra 'nonexistent'" + in result.stderr + ) + + +def test_nonexistent_options_listed_in_order(script, data): + """ + Warn the user for each extra that doesn't exist. + """ + result = script.pip( + 'install', '--no-index', + '--find-links=' + data.find_links, + 'simplewheel[nonexistent, nope]', expect_stderr=True, + ) + msg = ( + " simplewheel 2.0 does not provide the extra 'nonexistent'\n" + " simplewheel 2.0 does not provide the extra 'nope'" + ) + assert msg in result.stderr diff --git a/tests/functional/test_install_index.py b/tests/functional/test_install_index.py index 375915d6947..59d640706a1 100644 --- a/tests/functional/test_install_index.py +++ b/tests/functional/test_install_index.py @@ -1,9 +1,8 @@ -import os import textwrap -from pip.backwardcompat import urllib +from pip._vendor.six.moves.urllib import parse as urllib_parse -from tests.lib import pyversion, path_to_url +from tests.lib import pyversion def test_find_links_relative_path(script, data): @@ -16,7 +15,9 @@ def test_find_links_relative_path(script, data): 'packages/', cwd=data.root, ) - egg_info_folder = script.site_packages / 'parent-0.1-py%s.egg-info' % pyversion + egg_info_folder = ( + script.site_packages / 'parent-0.1-py%s.egg-info' % pyversion + ) initools_folder = script.site_packages / 'parent' assert egg_info_folder in result.files_created, str(result) assert initools_folder in result.files_created, str(result) @@ -35,7 +36,9 @@ def test_find_links_requirements_file_relative_path(script, data): script.scratch_path / "test-req.txt", cwd=data.root, ) - egg_info_folder = script.site_packages / 'parent-0.1-py%s.egg-info' % pyversion + egg_info_folder = ( + script.site_packages / 'parent-0.1-py%s.egg-info' % pyversion + ) initools_folder = script.site_packages / 'parent' assert egg_info_folder in result.files_created, str(result) assert initools_folder in result.files_created, str(result) @@ -43,10 +46,13 @@ def test_find_links_requirements_file_relative_path(script, data): def test_install_from_file_index_hash_link(script, data): """ - Test that a pkg can be installed from a file:// index using a link with a hash + Test that a pkg can be installed from a file:// index using a link with a + hash """ result = script.pip('install', '-i', data.index_url(), 'simple==1.0') - egg_info_folder = script.site_packages / 'simple-1.0-py%s.egg-info' % pyversion + egg_info_folder = ( + script.site_packages / 'simple-1.0-py%s.egg-info' % pyversion + ) assert egg_info_folder in result.files_created, str(result) @@ -54,7 +60,14 @@ def test_file_index_url_quoting(script, data): """ Test url quoting of file index url with a space """ - index_url = data.index_url(urllib.quote("in dex")) - result = script.pip('install', '-vvv', '--index-url', index_url, 'simple', expect_error=False) - assert (script.site_packages/'simple') in result.files_created, str(result.stdout) - assert (script.site_packages/'simple-1.0-py%s.egg-info' % pyversion) in result.files_created, str(result) + index_url = data.index_url(urllib_parse.quote("in dex")) + result = script.pip( + 'install', '-vvv', '--index-url', index_url, 'simple', + expect_error=False, + ) + assert (script.site_packages / 'simple') in result.files_created, ( + str(result.stdout) + ) + assert ( + script.site_packages / 'simple-1.0-py%s.egg-info' % pyversion + ) in result.files_created, str(result) diff --git a/tests/functional/test_install_reqs.py b/tests/functional/test_install_reqs.py index 8cad2342b0d..d2b5a61e7ac 100644 --- a/tests/functional/test_install_reqs.py +++ b/tests/functional/test_install_reqs.py @@ -3,16 +3,12 @@ import pytest -from mock import patch - -from pip.backwardcompat import urllib -from pip.req import Requirements, parse_editable, parse_requirements -from tests.lib import (pyversion, path_to_url, +from tests.lib import (pyversion, path_to_url, requirements_file, _create_test_package_with_subdirectory) from tests.lib.local_repos import local_checkout -from tests.lib.path import Path +@pytest.mark.network def test_requirements_file(script): """ Test installing from a requirements file. @@ -24,12 +20,17 @@ def test_requirements_file(script): # and something else to test out: %s<=%s """ % (other_lib_name, other_lib_version))) - result = script.pip('install', '-r', script.scratch_path / 'initools-req.txt') - assert script.site_packages/'INITools-0.2-py%s.egg-info' % pyversion in result.files_created - assert script.site_packages/'initools' in result.files_created - assert result.files_created[script.site_packages/other_lib_name].dir + result = script.pip( + 'install', '-r', script.scratch_path / 'initools-req.txt' + ) + assert ( + script.site_packages / 'INITools-0.2-py%s.egg-info' % + pyversion in result.files_created + ) + assert script.site_packages / 'initools' in result.files_created + assert result.files_created[script.site_packages / other_lib_name].dir fn = '%s-%s-py%s.egg-info' % (other_lib_name, other_lib_version, pyversion) - assert result.files_created[script.site_packages/fn].dir + assert result.files_created[script.site_packages / fn].dir def test_schema_check_in_requirements_file(script): @@ -37,46 +38,92 @@ def test_schema_check_in_requirements_file(script): Test installing from a requirements file with an invalid vcs schema.. """ - script.scratch_path.join("file-egg-req.txt").write(textwrap.dedent("""\ - git://github.com/alex/django-fixture-generator.git#egg=fixture_generator - """)) + script.scratch_path.join("file-egg-req.txt").write( + "\n%s\n" % ( + "git://github.com/alex/django-fixture-generator.git" + "#egg=fixture_generator" + ) + ) with pytest.raises(AssertionError): - script.pip("install", "-vvv", "-r", script.scratch_path / "file-egg-req.txt") + script.pip( + "install", "-vvv", "-r", script.scratch_path / "file-egg-req.txt" + ) def test_relative_requirements_file(script, data): """ - Test installing from a requirements file with a relative path with an egg= definition.. + Test installing from a requirements file with a relative path with an + egg= definition.. """ - url = path_to_url(os.path.join(data.root, "packages", "..", "packages", "FSPkg")) + '#egg=FSPkg' + url = path_to_url( + os.path.join(data.root, "packages", "..", "packages", "FSPkg") + ) + '#egg=FSPkg' script.scratch_path.join("file-egg-req.txt").write(textwrap.dedent("""\ %s """ % url)) - result = script.pip('install', '-vvv', '-r', script.scratch_path / 'file-egg-req.txt') - assert (script.site_packages/'FSPkg-0.1dev-py%s.egg-info' % pyversion) in result.files_created, str(result) - assert (script.site_packages/'fspkg') in result.files_created, str(result.stdout) + result = script.pip( + 'install', '-vvv', '-r', script.scratch_path / 'file-egg-req.txt' + ) + assert ( + script.site_packages / 'FSPkg-0.1.dev0-py%s.egg-info' % pyversion + ) in result.files_created, str(result) + assert (script.site_packages / 'fspkg') in result.files_created, ( + str(result.stdout) + ) +@pytest.mark.network def test_multiple_requirements_files(script, tmpdir): """ Test installing from multiple nested requirements files. """ other_lib_name, other_lib_version = 'anyjson', '0.3' - script.scratch_path.join("initools-req.txt").write(textwrap.dedent("""\ - -e %s@10#egg=INITools-dev - -r %s-req.txt""" % (local_checkout('svn+http://svn.colorstudy.com/INITools/trunk', tmpdir.join("cache")), - other_lib_name))) - script.scratch_path.join("%s-req.txt" % other_lib_name).write(textwrap.dedent("""\ - %s<=%s - """ % (other_lib_name, other_lib_version))) - result = script.pip('install', '-r', script.scratch_path / 'initools-req.txt') - assert result.files_created[script.site_packages/other_lib_name].dir + script.scratch_path.join("initools-req.txt").write( + textwrap.dedent(""" + -e %s@10#egg=INITools-dev + -r %s-req.txt + """) % + ( + local_checkout( + 'svn+http://svn.colorstudy.com/INITools/trunk', + tmpdir.join("cache"), + ), + other_lib_name + ), + ) + script.scratch_path.join("%s-req.txt" % other_lib_name).write( + "%s<=%s" % (other_lib_name, other_lib_version) + ) + result = script.pip( + 'install', '-r', script.scratch_path / 'initools-req.txt' + ) + assert result.files_created[script.site_packages / other_lib_name].dir fn = '%s-%s-py%s.egg-info' % (other_lib_name, other_lib_version, pyversion) - assert result.files_created[script.site_packages/fn].dir - assert script.venv/'src'/'initools' in result.files_created + assert result.files_created[script.site_packages / fn].dir + assert script.venv / 'src' / 'initools' in result.files_created + + +def test_package_in_constraints_and_dependencies(script, data): + script.scratch_path.join("constraints.txt").write( + "TopoRequires2==0.0.1\nTopoRequires==0.0.1" + ) + result = script.pip('install', '--no-index', '-f', + data.find_links, '-c', script.scratch_path / + 'constraints.txt', 'TopoRequires2') + assert 'installed TopoRequires-0.0.1' in result.stdout + + +def test_multiple_constraints_files(script, data): + script.scratch_path.join("outer.txt").write("-c inner.txt") + script.scratch_path.join("inner.txt").write( + "Upper==1.0") + result = script.pip( + 'install', '--no-index', '-f', data.find_links, '-c', + script.scratch_path / 'outer.txt', 'Upper') + assert 'installed Upper-1.0' in result.stdout def test_respect_order_in_requirements_file(script, data): @@ -86,17 +133,23 @@ def test_respect_order_in_requirements_file(script, data): simple """)) - result = script.pip('install', '--no-index', '-f', data.find_links, '-r', script.scratch_path / 'frameworks-req.txt') + result = script.pip( + 'install', '--no-index', '-f', data.find_links, '-r', + script.scratch_path / 'frameworks-req.txt' + ) downloaded = [line for line in result.stdout.split('\n') - if 'Downloading/unpacking' in line] + if 'Collecting' in line] - assert 'parent' in downloaded[0], 'First download should ' \ - 'be "parent" but was "%s"' % downloaded[0] - assert 'child' in downloaded[1], 'Second download should ' \ - 'be "child" but was "%s"' % downloaded[1] - assert 'simple' in downloaded[2], 'Third download should ' \ - 'be "simple" but was "%s"' % downloaded[2] + assert 'parent' in downloaded[0], ( + 'First download should be "parent" but was "%s"' % downloaded[0] + ) + assert 'child' in downloaded[1], ( + 'Second download should be "child" but was "%s"' % downloaded[1] + ) + assert 'simple' in downloaded[2], ( + 'Third download should be "simple" but was "%s"' % downloaded[2] + ) def test_install_local_editable_with_extras(script, data): @@ -104,17 +157,341 @@ def test_install_local_editable_with_extras(script, data): res = script.pip( 'install', '-e', to_install + '[bar]', '--process-dependency-links', expect_error=False, + expect_stderr=True, + ) + assert script.site_packages / 'easy-install.pth' in res.files_updated, ( + str(res) ) - assert script.site_packages/'easy-install.pth' in res.files_updated, str(result) - assert script.site_packages/'LocalExtras.egg-link' in res.files_created, str(result) - assert script.site_packages/'simple' in res.files_created, str(result) + assert ( + script.site_packages / 'LocalExtras.egg-link' in res.files_created + ), str(res) + assert script.site_packages / 'simple' in res.files_created, str(res) +@pytest.mark.network +def test_install_collected_dependencies_first(script): + result = script.pip( + 'install', 'paramiko', + ) + text = [line for line in result.stdout.split('\n') + if 'Installing' in line][0] + assert text.endswith('paramiko') + + +@pytest.mark.network def test_install_local_editable_with_subdirectory(script): version_pkg_path = _create_test_package_with_subdirectory(script, - 'version_subpkg') - result = script.pip('install', '-e', - '%s#egg=version_subpkg&subdirectory=version_subpkg' % - ('git+file://%s' % version_pkg_path,)) + 'version_subdir') + result = script.pip( + 'install', '-e', + '%s#egg=version_subpkg&subdirectory=version_subdir' % + ('git+file://%s' % version_pkg_path,) + ) + + result.assert_installed('version-subpkg', sub_dir='version_subdir') + + +@pytest.mark.network +def test_install_local_with_subdirectory(script): + version_pkg_path = _create_test_package_with_subdirectory(script, + 'version_subdir') + result = script.pip( + 'install', + '%s#egg=version_subpkg&subdirectory=version_subdir' % + ('git+file://%s' % version_pkg_path,) + ) + + result.assert_installed('version_subpkg.py', editable=False) + + +@pytest.mark.network +def test_wheel_user_with_prefix_in_pydistutils_cfg(script, data, virtualenv): + # Make sure wheel is available in the virtualenv + script.pip('install', 'wheel') + virtualenv.system_site_packages = True + homedir = script.environ["HOME"] + script.scratch_path.join("bin").mkdir() + with open(os.path.join(homedir, ".pydistutils.cfg"), "w") as cfg: + cfg.write(textwrap.dedent(""" + [install] + prefix=%s""" % script.scratch_path)) + + result = script.pip('install', '--user', '--no-index', '-f', + data.find_links, 'requiresupper') + # Check that we are really installing a wheel + assert 'Running setup.py install for requiresupper' not in result.stdout + assert 'installed requiresupper' in result.stdout + + +def test_nowheel_user_with_prefix_in_pydistutils_cfg(script, data, virtualenv): + virtualenv.system_site_packages = True + homedir = script.environ["HOME"] + script.scratch_path.join("bin").mkdir() + with open(os.path.join(homedir, ".pydistutils.cfg"), "w") as cfg: + cfg.write(textwrap.dedent(""" + [install] + prefix=%s""" % script.scratch_path)) + + result = script.pip('install', '--no-use-wheel', '--user', '--no-index', + '-f', data.find_links, 'requiresupper', + expect_stderr=True) + assert 'installed requiresupper' in result.stdout + assert ('DEPRECATION: --no-use-wheel is deprecated and will be removed ' + 'in the future. Please use --no-binary :all: instead.\n' + ) in result.stderr + + +def test_install_option_in_requirements_file(script, data, virtualenv): + """ + Test --install-option in requirements file overrides same option in cli + """ + + script.scratch_path.join("home1").mkdir() + script.scratch_path.join("home2").mkdir() + + script.scratch_path.join("reqs.txt").write( + textwrap.dedent( + """simple --install-option='--home=%s'""" + % script.scratch_path.join("home1"))) + + result = script.pip( + 'install', '--no-index', '-f', data.find_links, '-r', + script.scratch_path / 'reqs.txt', + '--install-option=--home=%s' % script.scratch_path.join("home2"), + expect_stderr=True) + + package_dir = script.scratch / 'home1' / 'lib' / 'python' / 'simple' + assert package_dir in result.files_created + + +def test_constraints_not_installed_by_default(script, data): + script.scratch_path.join("c.txt").write("requiresupper") + result = script.pip( + 'install', '--no-index', '-f', data.find_links, '-c', + script.scratch_path / 'c.txt', 'Upper') + assert 'requiresupper' not in result.stdout + + +def test_constraints_only_causes_error(script, data): + script.scratch_path.join("c.txt").write("requiresupper") + result = script.pip( + 'install', '--no-index', '-f', data.find_links, '-c', + script.scratch_path / 'c.txt', expect_error=True) + assert 'installed requiresupper' not in result.stdout + + +def test_constraints_local_editable_install_causes_error(script, data): + script.scratch_path.join("constraints.txt").write( + "singlemodule==0.0.0" + ) + to_install = data.src.join("singlemodule") + result = script.pip( + 'install', '--no-index', '-f', data.find_links, '-c', + script.scratch_path / 'constraints.txt', '-e', + to_install, expect_error=True) + assert 'Could not satisfy constraints for' in result.stderr + + +def test_constraints_local_install_causes_error(script, data): + script.scratch_path.join("constraints.txt").write( + "singlemodule==0.0.0" + ) + to_install = data.src.join("singlemodule") + result = script.pip( + 'install', '--no-index', '-f', data.find_links, '-c', + script.scratch_path / 'constraints.txt', + to_install, expect_error=True) + assert 'Could not satisfy constraints for' in result.stderr + + +def test_constraints_constrain_to_local_editable(script, data): + to_install = data.src.join("singlemodule") + script.scratch_path.join("constraints.txt").write( + "-e file://%s#egg=singlemodule" % to_install + ) + result = script.pip( + 'install', '--no-index', '-f', data.find_links, '-c', + script.scratch_path / 'constraints.txt', 'singlemodule') + assert 'Running setup.py develop for singlemodule' in result.stdout + + +def test_constraints_constrain_to_local(script, data): + to_install = data.src.join("singlemodule") + script.scratch_path.join("constraints.txt").write( + "file://%s#egg=singlemodule" % to_install + ) + result = script.pip( + 'install', '--no-index', '-f', data.find_links, '-c', + script.scratch_path / 'constraints.txt', 'singlemodule') + assert 'Running setup.py install for singlemodule' in result.stdout + + +def test_constrained_to_url_install_same_url(script, data): + to_install = data.src.join("singlemodule") + script.scratch_path.join("constraints.txt").write( + "file://%s#egg=singlemodule" % to_install + ) + result = script.pip( + 'install', '--no-index', '-f', data.find_links, '-c', + script.scratch_path / 'constraints.txt', to_install) + assert 'Running setup.py install for singlemodule' in result.stdout + + +@pytest.mark.network +def test_double_install_spurious_hash_mismatch(script, tmpdir): + """Make sure installing the same hashed sdist twice doesn't throw hash + mismatch errors. + + Really, this is a test that we disable reads from the wheel cache in + hash-checking mode. Locally, implicitly built wheels of sdists obviously + have different hashes from the original archives. Comparing against those + causes spurious mismatch errors. + + """ + script.pip('install', 'wheel') # Otherwise, it won't try to build wheels. + with requirements_file('simple==1.0 --hash=sha256:393043e672415891885c9a2a' + '0929b1af95fb866d6ca016b42d2e6ce53619b653', + tmpdir) as reqs_file: + # Install a package (and build its wheel): + result = script.pip_install_local( + '-r', reqs_file.abspath, expect_error=False) + assert 'Successfully installed simple-1.0' in str(result) - result.assert_installed('version-subpkg') + # Uninstall it: + script.pip('uninstall', '-y', 'simple', expect_error=False) + + # Then install it again. We should not hit a hash mismatch, and the + # package should install happily. + result = script.pip_install_local( + '-r', reqs_file.abspath, expect_error=False) + assert 'Successfully installed simple-1.0' in str(result) + + +def test_install_with_extras_from_constraints(script, data): + to_install = data.packages.join("LocalExtras") + script.scratch_path.join("constraints.txt").write( + "file://%s#egg=LocalExtras[bar]" % to_install + ) + result = script.pip_install_local( + '-c', script.scratch_path / 'constraints.txt', 'LocalExtras') + assert script.site_packages / 'simple' in result.files_created + + +def test_install_with_extras_from_install(script, data): + to_install = data.packages.join("LocalExtras") + script.scratch_path.join("constraints.txt").write( + "file://%s#egg=LocalExtras" % to_install + ) + result = script.pip_install_local( + '-c', script.scratch_path / 'constraints.txt', 'LocalExtras[baz]') + assert script.site_packages / 'singlemodule.py'in result.files_created + + +def test_install_with_extras_joined(script, data): + to_install = data.packages.join("LocalExtras") + script.scratch_path.join("constraints.txt").write( + "file://%s#egg=LocalExtras[bar]" % to_install + ) + result = script.pip_install_local( + '-c', script.scratch_path / 'constraints.txt', 'LocalExtras[baz]' + ) + assert script.site_packages / 'simple' in result.files_created + assert script.site_packages / 'singlemodule.py'in result.files_created + + +def test_install_with_extras_editable_joined(script, data): + to_install = data.packages.join("LocalExtras") + script.scratch_path.join("constraints.txt").write( + "-e file://%s#egg=LocalExtras[bar]" % to_install + ) + result = script.pip_install_local( + '-c', script.scratch_path / 'constraints.txt', 'LocalExtras[baz]') + assert script.site_packages / 'simple' in result.files_created + assert script.site_packages / 'singlemodule.py'in result.files_created + + +def test_install_distribution_full_union(script, data): + to_install = data.packages.join("LocalExtras") + result = script.pip_install_local( + to_install, to_install + "[bar]", to_install + "[baz]") + assert 'Running setup.py install for LocalExtras' in result.stdout + assert script.site_packages / 'simple' in result.files_created + assert script.site_packages / 'singlemodule.py' in result.files_created + + +def test_install_distribution_duplicate_extras(script, data): + to_install = data.packages.join("LocalExtras") + package_name = to_install + "[bar]" + with pytest.raises(AssertionError): + result = script.pip_install_local(package_name, package_name) + assert 'Double requirement given: %s' % package_name in result.stderr + + +def test_install_distribution_union_with_constraints(script, data): + to_install = data.packages.join("LocalExtras") + script.scratch_path.join("constraints.txt").write( + "%s[bar]" % to_install) + result = script.pip_install_local( + '-c', script.scratch_path / 'constraints.txt', to_install + '[baz]') + assert 'Running setup.py install for LocalExtras' in result.stdout + assert script.site_packages / 'singlemodule.py' in result.files_created + + +def test_install_distribution_union_with_versions(script, data): + to_install_001 = data.packages.join("LocalExtras") + to_install_002 = data.packages.join("LocalExtras-0.0.2") + result = script.pip_install_local( + to_install_001 + "[bar]", to_install_002 + "[baz]") + assert ("Successfully installed LocalExtras-0.0.1 simple-3.0 " + + "singlemodule-0.0.1" in result.stdout) + + +@pytest.mark.xfail +def test_install_distribution_union_conflicting_extras(script, data): + # LocalExtras requires simple==1.0, LocalExtras[bar] requires simple==2.0; + # without a resolver, pip does not detect the conflict between simple==1.0 + # and simple==2.0. Once a resolver is added, this conflict should be + # detected. + to_install = data.packages.join("LocalExtras-0.0.2") + result = script.pip_install_local(to_install, to_install + "[bar]", + expect_error=True) + assert 'installed' not in result.stdout + assert "Conflict" in result.stderr + + +def test_install_unsupported_wheel_link_with_marker(script, data): + script.scratch_path.join("with-marker.txt").write( + textwrap.dedent("""\ + %s; %s + """) % + ( + 'https://github.com/a/b/c/asdf-1.5.2-cp27-none-xyz.whl', + 'sys_platform == "xyz"' + ) + ) + result = script.pip( + 'install', '-r', script.scratch_path / 'with-marker.txt', + expect_error=False, + expect_stderr=True, + ) + + s = "Ignoring asdf: markers %r don't match your environment" %\ + u'sys_platform == "xyz"' + assert s in result.stderr + assert len(result.files_created) == 0 + + +def test_install_unsupported_wheel_file(script, data): + # Trying to install a local wheel with an incompatible version/type + # should fail. + script.scratch_path.join("wheel-file.txt").write(textwrap.dedent("""\ + %s + """ % data.packages.join("simple.dist-0.1-py1-none-invalid.whl"))) + result = script.pip( + 'install', '-r', script.scratch_path / 'wheel-file.txt', + expect_error=True, + expect_stderr=True, + ) + assert ("simple.dist-0.1-py1-none-invalid.whl is not a supported " + + "wheel on this platform" in result.stderr) + assert len(result.files_created) == 0 diff --git a/tests/functional/test_install_upgrade.py b/tests/functional/test_install_upgrade.py index e1862a5224a..f4a78f0ca7c 100644 --- a/tests/functional/test_install_upgrade.py +++ b/tests/functional/test_install_upgrade.py @@ -2,13 +2,12 @@ import sys import textwrap -from os.path import join - import pytest -from tests.lib import SRC_DIR -from tests.lib import (assert_all_changes, pyversion, - _create_test_package, _change_test_package_version) +from tests.lib import ( + assert_all_changes, pyversion, _create_test_package, + _change_test_package_version, +) from tests.lib.local_repos import local_checkout @@ -19,9 +18,12 @@ def test_no_upgrade_unless_requested(script): """ script.pip('install', 'INITools==0.1', expect_error=True) result = script.pip('install', 'INITools', expect_error=True) - assert not result.files_created, 'pip install INITools upgraded when it should not have' + assert not result.files_created, ( + 'pip install INITools upgraded when it should not have' + ) +@pytest.mark.network def test_upgrade_to_specific_version(script): """ It does upgrade to specific version requested. @@ -29,11 +31,20 @@ def test_upgrade_to_specific_version(script): """ script.pip('install', 'INITools==0.1', expect_error=True) result = script.pip('install', 'INITools==0.2', expect_error=True) - assert result.files_created, 'pip install with specific version did not upgrade' - assert script.site_packages/'INITools-0.1-py%s.egg-info' % pyversion in result.files_deleted - assert script.site_packages/'INITools-0.2-py%s.egg-info' % pyversion in result.files_created - - + assert result.files_created, ( + 'pip install with specific version did not upgrade' + ) + assert ( + script.site_packages / 'INITools-0.1-py%s.egg-info' % + pyversion in result.files_deleted + ) + assert ( + script.site_packages / 'INITools-0.2-py%s.egg-info' % + pyversion in result.files_created + ) + + +@pytest.mark.network def test_upgrade_if_requested(script): """ And it does upgrade if requested. @@ -42,7 +53,10 @@ def test_upgrade_if_requested(script): script.pip('install', 'INITools==0.1', expect_error=True) result = script.pip('install', '--upgrade', 'INITools', expect_error=True) assert result.files_created, 'pip install --upgrade did not upgrade' - assert script.site_packages/'INITools-0.1-py%s.egg-info' % pyversion not in result.files_created + assert ( + script.site_packages / 'INITools-0.1-py%s.egg-info' % + pyversion not in result.files_created + ) def test_upgrade_with_newest_already_installed(script, data): @@ -51,50 +65,69 @@ def test_upgrade_with_newest_already_installed(script, data): not be reinstalled and the user should be informed. """ script.pip('install', '-f', data.find_links, '--no-index', 'simple') - result = script.pip('install', '--upgrade', '-f', data.find_links, '--no-index', 'simple') + result = script.pip( + 'install', '--upgrade', '-f', data.find_links, '--no-index', 'simple' + ) assert not result.files_created, 'simple upgraded when it should not have' assert 'already up-to-date' in result.stdout, result.stdout +@pytest.mark.network def test_upgrade_force_reinstall_newest(script): """ Force reinstallation of a package even if it is already at its newest version if --force-reinstall is supplied. """ result = script.pip('install', 'INITools') - assert script.site_packages/ 'initools' in result.files_created, sorted(result.files_created.keys()) - result2 = script.pip('install', '--upgrade', '--force-reinstall', 'INITools') + assert script.site_packages / 'initools' in result.files_created, ( + sorted(result.files_created.keys()) + ) + result2 = script.pip( + 'install', '--upgrade', '--force-reinstall', 'INITools' + ) assert result2.files_updated, 'upgrade to INITools 0.3 failed' result3 = script.pip('uninstall', 'initools', '-y', expect_error=True) - assert_all_changes(result, result3, [script.venv/'build', 'cache']) + assert_all_changes(result, result3, [script.venv / 'build', 'cache']) +@pytest.mark.network def test_uninstall_before_upgrade(script): """ Automatic uninstall-before-upgrade. """ result = script.pip('install', 'INITools==0.2', expect_error=True) - assert script.site_packages/ 'initools' in result.files_created, sorted(result.files_created.keys()) + assert script.site_packages / 'initools' in result.files_created, ( + sorted(result.files_created.keys()) + ) result2 = script.pip('install', 'INITools==0.3', expect_error=True) assert result2.files_created, 'upgrade to INITools 0.3 failed' result3 = script.pip('uninstall', 'initools', '-y', expect_error=True) - assert_all_changes(result, result3, [script.venv/'build', 'cache']) + assert_all_changes(result, result3, [script.venv / 'build', 'cache']) +@pytest.mark.network def test_uninstall_before_upgrade_from_url(script): """ Automatic uninstall-before-upgrade from URL. """ result = script.pip('install', 'INITools==0.2', expect_error=True) - assert script.site_packages/ 'initools' in result.files_created, sorted(result.files_created.keys()) - result2 = script.pip('install', 'http://pypi.python.org/packages/source/I/INITools/INITools-0.3.tar.gz', expect_error=True) + assert script.site_packages / 'initools' in result.files_created, ( + sorted(result.files_created.keys()) + ) + result2 = script.pip( + 'install', + 'https://pypi.python.org/packages/source/I/INITools/INITools-' + '0.3.tar.gz', + expect_error=True, + ) assert result2.files_created, 'upgrade to INITools 0.3 failed' result3 = script.pip('uninstall', 'initools', '-y', expect_error=True) - assert_all_changes(result, result3, [script.venv/'build', 'cache']) + assert_all_changes(result, result3, [script.venv / 'build', 'cache']) +@pytest.mark.network def test_upgrade_to_same_version_from_url(script): """ When installing from a URL the same version that is already installed, no @@ -102,13 +135,21 @@ def test_upgrade_to_same_version_from_url(script): """ result = script.pip('install', 'INITools==0.3', expect_error=True) - assert script.site_packages/ 'initools' in result.files_created, sorted(result.files_created.keys()) - result2 = script.pip('install', 'http://pypi.python.org/packages/source/I/INITools/INITools-0.3.tar.gz', expect_error=True) + assert script.site_packages / 'initools' in result.files_created, ( + sorted(result.files_created.keys()) + ) + result2 = script.pip( + 'install', + 'https://pypi.python.org/packages/source/I/INITools/INITools-' + '0.3.tar.gz', + expect_error=True, + ) assert not result2.files_updated, 'INITools 0.3 reinstalled same version' result3 = script.pip('uninstall', 'initools', '-y', expect_error=True) - assert_all_changes(result, result3, [script.venv/'build', 'cache']) + assert_all_changes(result, result3, [script.venv / 'build', 'cache']) +@pytest.mark.network def test_upgrade_from_reqs_file(script): """ Upgrade from a requirements file. @@ -119,15 +160,25 @@ def test_upgrade_from_reqs_file(script): # and something else to test out: INITools==0.3 """)) - install_result = script.pip('install', '-r', script.scratch_path/ 'test-req.txt') + install_result = script.pip( + 'install', '-r', script.scratch_path / 'test-req.txt' + ) script.scratch_path.join("test-req.txt").write(textwrap.dedent("""\ PyLogo # and something else to test out: INITools """)) - script.pip('install', '--upgrade', '-r', script.scratch_path/ 'test-req.txt') - uninstall_result = script.pip('uninstall', '-r', script.scratch_path/ 'test-req.txt', '-y') - assert_all_changes(install_result, uninstall_result, [script.venv/'build', 'cache', script.scratch/'test-req.txt']) + script.pip( + 'install', '--upgrade', '-r', script.scratch_path / 'test-req.txt' + ) + uninstall_result = script.pip( + 'uninstall', '-r', script.scratch_path / 'test-req.txt', '-y' + ) + assert_all_changes( + install_result, + uninstall_result, + [script.venv / 'build', 'cache', script.scratch / 'test-req.txt'], + ) def test_uninstall_rollback(script, data): @@ -136,31 +187,53 @@ def test_uninstall_rollback(script, data): crafted to fail on install). """ - result = script.pip('install', '-f', data.find_links, '--no-index', 'broken==0.1') - assert script.site_packages / 'broken.py' in result.files_created, list(result.files_created.keys()) - result2 = script.pip('install', '-f', data.find_links, '--no-index', 'broken==0.2broken', expect_error=True) + result = script.pip( + 'install', '-f', data.find_links, '--no-index', 'broken==0.1' + ) + assert script.site_packages / 'broken.py' in result.files_created, list( + result.files_created.keys() + ) + result2 = script.pip( + 'install', '-f', data.find_links, '--no-index', 'broken===0.2broken', + expect_error=True, + ) assert result2.returncode == 1, str(result2) - assert script.run('python', '-c', "import broken; print(broken.VERSION)").stdout == '0.1\n' - assert_all_changes(result.files_after, result2, [script.venv/'build', 'pip-log.txt']) + assert script.run( + 'python', '-c', "import broken; print(broken.VERSION)" + ).stdout == '0.1\n' + assert_all_changes( + result.files_after, + result2, + [script.venv / 'build'], + ) # Issue #530 - temporarily disable flaky test @pytest.mark.skipif def test_editable_git_upgrade(script): """ - Test installing an editable git package from a repository, upgrading the repository, - installing again, and check it gets the newer version + Test installing an editable git package from a repository, upgrading the + repository, installing again, and check it gets the newer version """ version_pkg_path = _create_test_package(script) - script.pip('install', '-e', '%s#egg=version_pkg' % ('git+file://' + version_pkg_path)) + script.pip( + 'install', '-e', + '%s#egg=version_pkg' % ('git+file://' + version_pkg_path), + ) version = script.run('version_pkg') assert '0.1' in version.stdout _change_test_package_version(script, version_pkg_path) - script.pip('install', '-e', '%s#egg=version_pkg' % ('git+file://' + version_pkg_path)) + script.pip( + 'install', '-e', + '%s#egg=version_pkg' % ('git+file://' + version_pkg_path), + ) version2 = script.run('version_pkg') - assert 'some different version' in version2.stdout, "Output: %s" % (version2.stdout) + assert 'some different version' in version2.stdout, ( + "Output: %s" % (version2.stdout) + ) +@pytest.mark.network def test_should_not_install_always_from_cache(script): """ If there is an old cached package, pip should download the newer version @@ -169,52 +242,89 @@ def test_should_not_install_always_from_cache(script): script.pip('install', 'INITools==0.2', expect_error=True) script.pip('uninstall', '-y', 'INITools') result = script.pip('install', 'INITools==0.1', expect_error=True) - assert script.site_packages/'INITools-0.2-py%s.egg-info' % pyversion not in result.files_created - assert script.site_packages/'INITools-0.1-py%s.egg-info' % pyversion in result.files_created + assert ( + script.site_packages / 'INITools-0.2-py%s.egg-info' % + pyversion not in result.files_created + ) + assert ( + script.site_packages / 'INITools-0.1-py%s.egg-info' % + pyversion in result.files_created + ) +@pytest.mark.network def test_install_with_ignoreinstalled_requested(script): """ Test old conflicting package is completely ignored """ - r = script.pip('install', 'INITools==0.1', expect_error=True) + script.pip('install', 'INITools==0.1', expect_error=True) result = script.pip('install', '-I', 'INITools==0.3', expect_error=True) assert result.files_created, 'pip install -I did not install' # both the old and new metadata should be present. - assert os.path.exists(script.site_packages_path/'INITools-0.1-py%s.egg-info' % pyversion) - assert os.path.exists(script.site_packages_path/'INITools-0.3-py%s.egg-info' % pyversion) + assert os.path.exists( + script.site_packages_path / 'INITools-0.1-py%s.egg-info' % pyversion + ) + assert os.path.exists( + script.site_packages_path / 'INITools-0.3-py%s.egg-info' % pyversion + ) +@pytest.mark.network def test_upgrade_vcs_req_with_no_dists_found(script, tmpdir): """It can upgrade a VCS requirement that has no distributions otherwise.""" req = "%s#egg=pip-test-package" % local_checkout( - "git+http://github.com/pypa/pip-test-package.git", tmpdir.join("cache")) + "git+http://github.com/pypa/pip-test-package.git", + tmpdir.join("cache"), + ) script.pip("install", req) result = script.pip("install", "-U", req) assert not result.returncode +@pytest.mark.network def test_upgrade_vcs_req_with_dist_found(script): """It can upgrade a VCS requirement that has distributions on the index.""" - # TODO(pnasrat) Using local_checkout fails on windows - oddness with the test path urls/git. - req = "%s#egg=virtualenv" % "git+git://github.com/pypa/virtualenv@c21fef2c2d53cf19f49bcc37f9c058a33fb50499" - script.pip("install", req) - result = script.pip("install", "-U", req) - assert not "pypi.python.org" in result.stdout, result.stdout + # TODO(pnasrat) Using local_checkout fails on windows - oddness with the + # test path urls/git. + req = ( + "%s#egg=pretend" % + ( + "git+git://github.com/alex/pretend@e7f26ad7dbcb4a02a4995aade4" + "743aad47656b27" + ) + ) + script.pip("install", req, expect_stderr=True) + result = script.pip("install", "-U", req, expect_stderr=True) + assert "pypi.python.org" not in result.stdout, result.stdout -class TestUpgradeSetuptools(object): +class TestUpgradeDistributeToSetuptools(object): """ - Tests for upgrading to setuptools (using pip from src tree) - The tests use a *fixed* set of packages from our test packages dir - note: virtualenv-1.9.1 contains distribute-0.6.34 - note: virtualenv-1.10 contains setuptools-0.9.7 + From pip1.4 to pip6, pip supported a set of "hacks" (see Issue #1122) to + allow distribute to conflict with setuptools, so that the following would + work to upgrade distribute: + + ``pip install -U setuptools`` + + In pip7, the hacks were removed. This test remains to at least confirm pip + can upgrade distribute to setuptools using: + + ``pip install -U distribute`` + + The reason this works is that a final version of distribute (v0.7.3) was + released that is simple wrapper with: + + install_requires=['setuptools>=0.7'] + + The test use a fixed set of packages from our test packages dir. Note that + virtualenv-1.9.1 contains distribute-0.6.34 and virtualenv-1.10 contains + setuptools-0.9.7 """ def prep_ve(self, script, version, pip_src, distribute=False): self.script = script - self.script.pip_install_local('virtualenv==%s' %version) - args = ['virtualenv', self.script.scratch_path/'VE'] + self.script.pip_install_local('virtualenv==%s' % version) + args = ['virtualenv', self.script.scratch_path / 'VE'] if distribute: args.insert(1, '--distribute') if version == "1.9.1" and not distribute: @@ -225,56 +335,34 @@ def prep_ve(self, script, version, pip_src, distribute=False): bindir = "Scripts" else: bindir = "bin" - self.ve_bin = self.script.scratch_path/'VE'/bindir - self.script.run(self.ve_bin/'pip', 'uninstall', '-y', 'pip') - self.script.run(self.ve_bin/'python', 'setup.py', 'install', + self.ve_bin = self.script.scratch_path / 'VE' / bindir + self.script.run(self.ve_bin / 'pip', 'uninstall', '-y', 'pip') + self.script.run( + self.ve_bin / 'python', 'setup.py', 'install', cwd=pip_src, expect_stderr=True, ) - @pytest.mark.skipif("sys.version_info >= (3,0)") - def test_py2_from_setuptools_6_to_setuptools_7(self, script, data, virtualenv): - self.prep_ve(script, '1.9.1', virtualenv.pip_source_dir) - result = self.script.run(self.ve_bin/'pip', 'install', '--no-use-wheel', '--no-index', '--find-links=%s' % data.find_links, '-U', 'setuptools') - assert "Found existing installation: setuptools 0.6c11" in result.stdout - result = self.script.run(self.ve_bin/'pip', 'list') - "setuptools (0.9.8)" in result.stdout - - def test_py2_py3_from_distribute_6_to_setuptools_7(self, script, data, virtualenv): - self.prep_ve(script, '1.9.1', virtualenv.pip_source_dir, distribute=True) - result = self.script.run(self.ve_bin/'pip', 'install', '--no-index', '--find-links=%s' % data.find_links, '-U', 'setuptools') - assert "Found existing installation: distribute 0.6.34" in result.stdout - result = self.script.run(self.ve_bin/'pip', 'list') - "setuptools (0.9.8)" in result.stdout - "distribute (0.7.3)" in result.stdout - - def test_from_setuptools_7_to_setuptools_7(self, script, data, virtualenv): - self.prep_ve(script, '1.10', virtualenv.pip_source_dir) - result = self.script.run(self.ve_bin/'pip', 'install', '--no-index', '--find-links=%s' % data.find_links, '-U', 'setuptools') - assert "Found existing installation: setuptools 0.9.7" in result.stdout - result = self.script.run(self.ve_bin/'pip', 'list') - "setuptools (0.9.8)" in result.stdout - - def test_from_setuptools_7_to_setuptools_7_using_wheel(self, script, data, virtualenv): - self.prep_ve(script, '1.10', virtualenv.pip_source_dir) - result = self.script.run(self.ve_bin/'pip', 'install', '--use-wheel', '--no-index', '--find-links=%s' % data.find_links, '-U', 'setuptools') - assert "Found existing installation: setuptools 0.9.7" in result.stdout - assert 'setuptools-0.9.8.dist-info' in str(result.files_created) #only wheels use dist-info - result = self.script.run(self.ve_bin/'pip', 'list') - "setuptools (0.9.8)" in result.stdout - - - # disabling intermittent travis failure: https://github.com/pypa/pip/issues/1379 - @pytest.mark.skipif("hasattr(sys, 'pypy_version_info')") - def test_from_setuptools_7_to_setuptools_7_with_distribute_7_installed(self, script, data, virtualenv): - self.prep_ve(script, '1.9.1', virtualenv.pip_source_dir, distribute=True) - result = self.script.run(self.ve_bin/'pip', 'install', '--no-index', '--find-links=%s' % data.find_links, '-U', 'setuptools') - result = self.script.run(self.ve_bin/'pip', 'install', '--no-index', '--find-links=%s' % data.find_links, 'setuptools==0.9.6') - result = self.script.run(self.ve_bin/'pip', 'list') - "setuptools (0.9.6)" in result.stdout - "distribute (0.7.3)" in result.stdout - result = self.script.run(self.ve_bin/'pip', 'install', '--no-index', '--find-links=%s' % data.find_links, '-U', 'setuptools') - assert "Found existing installation: setuptools 0.9.6" in result.stdout - result = self.script.run(self.ve_bin/'pip', 'list') - "setuptools (0.9.8)" in result.stdout - "distribute (0.7.3)" in result.stdout + @pytest.mark.skipif( + sys.version_info >= (3, 5), + reason="distribute doesn't work on Python 3.5", + ) + def test_from_distribute_6_to_setuptools_7( + self, script, data, virtualenv): + self.prep_ve( + script, '1.9.1', virtualenv.pip_source_dir, distribute=True + ) + result = self.script.run( + self.ve_bin / 'pip', 'install', '--no-index', + '--find-links=%s' % data.find_links, '-U', 'distribute', + expect_stderr=True if sys.version_info[:2] == (2, 6) else False, + ) + assert ( + "Found existing installation: distribute 0.6.34" in result.stdout + ) + result = self.script.run( + self.ve_bin / 'pip', 'list', '--format=legacy', + expect_stderr=True if sys.version_info[:2] == (2, 6) else False, + ) + assert "setuptools (0.9.8)" in result.stdout + assert "distribute (0.7.3)" in result.stdout diff --git a/tests/functional/test_install_user.py b/tests/functional/test_install_user.py index d976482764a..946cc65a231 100644 --- a/tests/functional/test_install_user.py +++ b/tests/functional/test_install_user.py @@ -1,20 +1,16 @@ """ tests specific to "pip install --user" """ -import imp -import sys import os import textwrap - -from os.path import abspath, join, curdir, isdir, isfile - import pytest -from pip.backwardcompat import uses_pycache -from pip.locations import bin_user +from os.path import curdir, isdir, isfile + +from pip.compat import uses_pycache, cache_from_source from tests.lib.local_repos import local_checkout -from tests.lib import pyversion, assert_all_changes +from tests.lib import pyversion def _patch_dist_in_site_packages(script): @@ -23,8 +19,8 @@ def _patch_dist_in_site_packages(script): def dist_in_site_packages(dist): return False - from pip import req - req.dist_in_site_packages = dist_in_site_packages + from pip.req import req_install + req_install.dist_in_site_packages = dist_in_site_packages """)) # Caught py32 with an outdated __pycache__ file after a sitecustomize @@ -32,47 +28,62 @@ def dist_in_site_packages(dist): # file to be sure # See: https://github.com/pypa/pip/pull/893#issuecomment-16426701 if uses_pycache: - cache_path = imp.cache_from_source(sitecustomize_path) + cache_path = cache_from_source(sitecustomize_path) if os.path.isfile(cache_path): os.remove(cache_path) - -# --user option is broken in pypy -@pytest.mark.skipif("hasattr(sys, 'pypy_version_info')") class Tests_UserSite: + @pytest.mark.network def test_reset_env_system_site_packages_usersite(self, script, virtualenv): """ - reset_env(system_site_packages=True) produces env where a --user install can be found using pkg_resources + reset_env(system_site_packages=True) produces env where a --user + install can be found using pkg_resources """ virtualenv.system_site_packages = True script.pip('install', '--user', 'INITools==0.2') - result = script.run('python', '-c', "import pkg_resources; print(pkg_resources.get_distribution('initools').project_name)") + result = script.run( + 'python', '-c', + "import pkg_resources; print(pkg_resources.get_distribution" + "('initools').project_name)", + ) project_name = result.stdout.strip() - assert 'INITools'== project_name, "'%s' should be 'INITools'" %project_name + assert 'INITools' == project_name, project_name - - def test_install_subversion_usersite_editable_with_distribute(self, script, virtualenv, tmpdir): + @pytest.mark.network + def test_install_subversion_usersite_editable_with_distribute( + self, script, virtualenv, tmpdir): """ - Test installing current directory ('.') into usersite after installing distribute + Test installing current directory ('.') into usersite after installing + distribute """ virtualenv.system_site_packages = True - result = script.pip('install', '--user', '-e', - '%s#egg=initools-dev' % - local_checkout('svn+http://svn.colorstudy.com/INITools/trunk', tmpdir.join("cache"))) + result = script.pip( + 'install', '--user', '-e', + '%s#egg=initools-dev' % + local_checkout( + 'svn+http://svn.colorstudy.com/INITools/trunk', + tmpdir.join("cache"), + ) + ) result.assert_installed('INITools', use_user_site=True) - def test_install_curdir_usersite(self, script, virtualenv, data): """ Test installing current directory ('.') into usersite """ virtualenv.system_site_packages = True run_from = data.packages.join("FSPkg") - result = script.pip('install', '-vvv', '--user', curdir, cwd=run_from, expect_error=False) - fspkg_folder = script.user_site/'fspkg' - egg_info_folder = script.user_site/'FSPkg-0.1dev-py%s.egg-info' % pyversion + result = script.pip( + 'install', '-vvv', '--user', curdir, + cwd=run_from, + expect_error=False, + ) + fspkg_folder = script.user_site / 'fspkg' + egg_info_folder = ( + script.user_site / 'FSPkg-0.1.dev0-py%s.egg-info' % pyversion + ) assert fspkg_folder in result.files_created, result.stdout assert egg_info_folder in result.files_created @@ -82,123 +93,200 @@ def test_install_user_venv_nositepkgs_fails(self, script, data): user install in virtualenv (with no system packages) fails with message """ run_from = data.packages.join("FSPkg") - result = script.pip('install', '--user', curdir, cwd=run_from, expect_error=True) - assert "Can not perform a '--user' install. User site-packages are not visible in this virtualenv." in result.stdout - + result = script.pip( + 'install', '--user', curdir, + cwd=run_from, + expect_error=True, + ) + assert ( + "Can not perform a '--user' install. User site-packages are not " + "visible in this virtualenv." in result.stderr + ) + + @pytest.mark.network def test_install_user_conflict_in_usersite(self, script, virtualenv): """ Test user install with conflict in usersite updates usersite. """ virtualenv.system_site_packages = True - result1 = script.pip('install', '--user', 'INITools==0.3') - result2 = script.pip('install', '--user', 'INITools==0.1') - #usersite has 0.1 - egg_info_folder = script.user_site / 'INITools-0.1-py%s.egg-info' % pyversion - initools_v3_file = script.base_path / script.user_site / 'initools' / 'configparser.py' #file only in 0.3 + script.pip('install', '--user', 'INITools==0.3', '--no-binary=:all:') + + result2 = script.pip( + 'install', '--user', 'INITools==0.1', '--no-binary=:all:') + + # usersite has 0.1 + egg_info_folder = ( + script.user_site / 'INITools-0.1-py%s.egg-info' % pyversion + ) + initools_v3_file = ( + # file only in 0.3 + script.base_path / script.user_site / 'initools' / + 'configparser.py' + ) assert egg_info_folder in result2.files_created, str(result2) assert not isfile(initools_v3_file), initools_v3_file + @pytest.mark.network def test_install_user_conflict_in_globalsite(self, script, virtualenv): """ - Test user install with conflict in global site ignores site and installs to usersite + Test user install with conflict in global site ignores site and + installs to usersite """ # the test framework only supports testing using virtualenvs - # the sys.path ordering for virtualenvs with --system-site-packages is this: virtualenv-site, user-site, global-site - # this test will use 2 modifications to simulate the user-site/global-site relationship - # 1) a monkey patch which will make it appear INITools==0.2 is not in in the virtualenv site - # if we don't patch this, pip will return an installation error: "Will not install to the usersite because it will lack sys.path precedence..." - # 2) adding usersite to PYTHONPATH, so usersite as sys.path precedence over the virtualenv site + # the sys.path ordering for virtualenvs with --system-site-packages is + # this: virtualenv-site, user-site, global-site + # this test will use 2 modifications to simulate the + # user-site/global-site relationship + # 1) a monkey patch which will make it appear INITools==0.2 is not in + # the virtualenv site if we don't patch this, pip will return an + # installation error: "Will not install to the usersite because it + # will lack sys.path precedence..." + # 2) adding usersite to PYTHONPATH, so usersite as sys.path precedence + # over the virtualenv site virtualenv.system_site_packages = True script.environ["PYTHONPATH"] = script.base_path / script.user_site _patch_dist_in_site_packages(script) - result1 = script.pip('install', 'INITools==0.2') - result2 = script.pip('install', '--user', 'INITools==0.1') + script.pip('install', 'INITools==0.2', '--no-binary=:all:') + + result2 = script.pip( + 'install', '--user', 'INITools==0.1', '--no-binary=:all:') - #usersite has 0.1 - egg_info_folder = script.user_site / 'INITools-0.1-py%s.egg-info' % pyversion + # usersite has 0.1 + egg_info_folder = ( + script.user_site / 'INITools-0.1-py%s.egg-info' % pyversion + ) initools_folder = script.user_site / 'initools' assert egg_info_folder in result2.files_created, str(result2) assert initools_folder in result2.files_created, str(result2) - #site still has 0.2 (can't look in result1; have to check) - egg_info_folder = script.base_path / script.site_packages / 'INITools-0.2-py%s.egg-info' % pyversion + # site still has 0.2 (can't look in result1; have to check) + egg_info_folder = ( + script.base_path / script.site_packages / + 'INITools-0.2-py%s.egg-info' % pyversion + ) initools_folder = script.base_path / script.site_packages / 'initools' assert isdir(egg_info_folder) assert isdir(initools_folder) + @pytest.mark.network def test_upgrade_user_conflict_in_globalsite(self, script, virtualenv): """ - Test user install/upgrade with conflict in global site ignores site and installs to usersite + Test user install/upgrade with conflict in global site ignores site and + installs to usersite """ # the test framework only supports testing using virtualenvs - # the sys.path ordering for virtualenvs with --system-site-packages is this: virtualenv-site, user-site, global-site - # this test will use 2 modifications to simulate the user-site/global-site relationship - # 1) a monkey patch which will make it appear INITools==0.2 is not in in the virtualenv site - # if we don't patch this, pip will return an installation error: "Will not install to the usersite because it will lack sys.path precedence..." - # 2) adding usersite to PYTHONPATH, so usersite as sys.path precedence over the virtualenv site + # the sys.path ordering for virtualenvs with --system-site-packages is + # this: virtualenv-site, user-site, global-site + # this test will use 2 modifications to simulate the + # user-site/global-site relationship + # 1) a monkey patch which will make it appear INITools==0.2 is not in + # the virtualenv site if we don't patch this, pip will return an + # installation error: "Will not install to the usersite because it + # will lack sys.path precedence..." + # 2) adding usersite to PYTHONPATH, so usersite as sys.path precedence + # over the virtualenv site virtualenv.system_site_packages = True script.environ["PYTHONPATH"] = script.base_path / script.user_site _patch_dist_in_site_packages(script) - result1 = script.pip('install', 'INITools==0.2') - result2 = script.pip('install', '--user', '--upgrade', 'INITools') + script.pip('install', 'INITools==0.2', '--no-binary=:all:') + result2 = script.pip( + 'install', '--user', '--upgrade', 'INITools', '--no-binary=:all:') - #usersite has 0.3.1 - egg_info_folder = script.user_site / 'INITools-0.3.1-py%s.egg-info' % pyversion + # usersite has 0.3.1 + egg_info_folder = ( + script.user_site / 'INITools-0.3.1-py%s.egg-info' % pyversion + ) initools_folder = script.user_site / 'initools' assert egg_info_folder in result2.files_created, str(result2) assert initools_folder in result2.files_created, str(result2) - #site still has 0.2 (can't look in result1; have to check) - egg_info_folder = script.base_path / script.site_packages / 'INITools-0.2-py%s.egg-info' % pyversion + # site still has 0.2 (can't look in result1; have to check) + egg_info_folder = ( + script.base_path / script.site_packages / + 'INITools-0.2-py%s.egg-info' % pyversion + ) initools_folder = script.base_path / script.site_packages / 'initools' assert isdir(egg_info_folder), result2.stdout assert isdir(initools_folder) - def test_install_user_conflict_in_globalsite_and_usersite(self, script, virtualenv): + @pytest.mark.network + def test_install_user_conflict_in_globalsite_and_usersite( + self, script, virtualenv): """ - Test user install with conflict in globalsite and usersite ignores global site and updates usersite. + Test user install with conflict in globalsite and usersite ignores + global site and updates usersite. """ # the test framework only supports testing using virtualenvs. - # the sys.path ordering for virtualenvs with --system-site-packages is this: virtualenv-site, user-site, global-site. - # this test will use 2 modifications to simulate the user-site/global-site relationship - # 1) a monkey patch which will make it appear INITools==0.2 is not in in the virtualenv site - # if we don't patch this, pip will return an installation error: "Will not install to the usersite because it will lack sys.path precedence..." - # 2) adding usersite to PYTHONPATH, so usersite as sys.path precedence over the virtualenv site + # the sys.path ordering for virtualenvs with --system-site-packages is + # this: virtualenv-site, user-site, global-site. + # this test will use 2 modifications to simulate the + # user-site/global-site relationship + # 1) a monkey patch which will make it appear INITools==0.2 is not in + # the virtualenv site if we don't patch this, pip will return an + # installation error: "Will not install to the usersite because it + # will lack sys.path precedence..." + # 2) adding usersite to PYTHONPATH, so usersite as sys.path precedence + # over the virtualenv site virtualenv.system_site_packages = True script.environ["PYTHONPATH"] = script.base_path / script.user_site _patch_dist_in_site_packages(script) - result1 = script.pip('install', 'INITools==0.2') - result2 = script.pip('install', '--user', 'INITools==0.3') - result3 = script.pip('install', '--user', 'INITools==0.1') - - #usersite has 0.1 - egg_info_folder = script.user_site / 'INITools-0.1-py%s.egg-info' % pyversion - initools_v3_file = script.base_path / script.user_site / 'initools' / 'configparser.py' #file only in 0.3 + script.pip('install', 'INITools==0.2', '--no-binary=:all:') + script.pip('install', '--user', 'INITools==0.3', '--no-binary=:all:') + + result3 = script.pip( + 'install', '--user', 'INITools==0.1', '--no-binary=:all:') + + # usersite has 0.1 + egg_info_folder = ( + script.user_site / 'INITools-0.1-py%s.egg-info' % pyversion + ) + initools_v3_file = ( + # file only in 0.3 + script.base_path / script.user_site / 'initools' / + 'configparser.py' + ) assert egg_info_folder in result3.files_created, str(result3) assert not isfile(initools_v3_file), initools_v3_file - #site still has 0.2 (can't just look in result1; have to check) - egg_info_folder = script.base_path / script.site_packages / 'INITools-0.2-py%s.egg-info' % pyversion + # site still has 0.2 (can't just look in result1; have to check) + egg_info_folder = ( + script.base_path / script.site_packages / + 'INITools-0.2-py%s.egg-info' % pyversion + ) initools_folder = script.base_path / script.site_packages / 'initools' assert isdir(egg_info_folder) assert isdir(initools_folder) - def test_install_user_in_global_virtualenv_with_conflict_fails(self, script, virtualenv): + @pytest.mark.network + def test_install_user_in_global_virtualenv_with_conflict_fails( + self, script, virtualenv): """ - Test user install in --system-site-packages virtualenv with conflict in site fails. + Test user install in --system-site-packages virtualenv with conflict in + site fails. """ virtualenv.system_site_packages = True - result1 = script.pip('install', 'INITools==0.2') - result2 = script.pip('install', '--user', 'INITools==0.1', expect_error=True) - resultp = script.run('python', '-c', "import pkg_resources; print(pkg_resources.get_distribution('initools').location)") - dist_location = resultp.stdout.strip() - assert "Will not install to the user site because it will lack sys.path precedence to %s in %s" \ - % ('INITools', dist_location) in result2.stdout, result2.stdout + script.pip('install', 'INITools==0.2') + + result2 = script.pip( + 'install', '--user', 'INITools==0.1', + expect_error=True, + ) + resultp = script.run( + 'python', '-c', + "import pkg_resources; print(pkg_resources.get_distribution" + "('initools').location)", + ) + dist_location = resultp.stdout.strip() + assert ( + "Will not install to the user site because it will lack sys.path " + "precedence to %s in %s" % + ('INITools', dist_location) in result2.stderr + ) diff --git a/tests/functional/test_install_vcs.py b/tests/functional/test_install_vcs.py index bfeb2c1f24b..0f0ea71d248 100644 --- a/tests/functional/test_install_vcs.py +++ b/tests/functional/test_install_vcs.py @@ -1,118 +1,215 @@ -from tests.lib import _create_test_package, _change_test_package_version +import pytest + +from tests.lib import (_create_test_package, _change_test_package_version, + pyversion) from tests.lib.local_repos import local_checkout +@pytest.mark.network def test_install_editable_from_git_with_https(script, tmpdir): """ Test cloning from Git with https. """ - result = script.pip('install', '-e', - '%s#egg=pip-test-package' % - local_checkout('git+https://github.com/pypa/pip-test-package.git', tmpdir.join("cache")), - expect_error=True) + result = script.pip( + 'install', '-e', + '%s#egg=pip-test-package' % + local_checkout( + 'git+https://github.com/pypa/pip-test-package.git', + tmpdir.join("cache"), + ), + expect_error=True, + ) result.assert_installed('pip-test-package', with_files=['.git']) +@pytest.mark.network +def test_install_noneditable_git(script, tmpdir): + """ + Test installing from a non-editable git URL with a given tag. + """ + result = script.pip( + 'install', + 'git+https://github.com/pypa/pip-test-package.git' + '@0.1.1#egg=pip-test-package' + ) + egg_info_folder = ( + script.site_packages / + 'pip_test_package-0.1.1-py%s.egg-info' % pyversion + ) + result.assert_installed('piptestpackage', + without_egg_link=True, + editable=False) + assert egg_info_folder in result.files_created, str(result) + + +@pytest.mark.network def test_git_with_sha1_revisions(script): """ Git backend should be able to install from SHA1 revisions """ version_pkg_path = _create_test_package(script) _change_test_package_version(script, version_pkg_path) - sha1 = script.run('git', 'rev-parse', 'HEAD~1', cwd=version_pkg_path).stdout.strip() - script.pip('install', '-e', '%s@%s#egg=version_pkg' % ('git+file://' + version_pkg_path.abspath.replace('\\', '/'), sha1)) + sha1 = script.run( + 'git', 'rev-parse', 'HEAD~1', + cwd=version_pkg_path, + ).stdout.strip() + script.pip( + 'install', '-e', + '%s@%s#egg=version_pkg' % + ('git+file://' + version_pkg_path.abspath.replace('\\', '/'), sha1), + expect_stderr=True + ) version = script.run('version_pkg') assert '0.1' in version.stdout, version.stdout +@pytest.mark.network def test_git_with_branch_name_as_revision(script): """ Git backend should be able to install from branch names """ version_pkg_path = _create_test_package(script) - script.run('git', 'checkout', '-b', 'test_branch', expect_stderr=True, cwd=version_pkg_path) + script.run( + 'git', 'checkout', '-b', 'test_branch', + expect_stderr=True, + cwd=version_pkg_path, + ) _change_test_package_version(script, version_pkg_path) - script.pip('install', '-e', '%s@test_branch#egg=version_pkg' % ('git+file://' + version_pkg_path.abspath.replace('\\', '/'))) + script.pip( + 'install', '-e', '%s@test_branch#egg=version_pkg' % + ('git+file://' + version_pkg_path.abspath.replace('\\', '/')) + ) version = script.run('version_pkg') assert 'some different version' in version.stdout +@pytest.mark.network def test_git_with_tag_name_as_revision(script): """ Git backend should be able to install from tag names """ version_pkg_path = _create_test_package(script) - script.run('git', 'tag', 'test_tag', expect_stderr=True, cwd=version_pkg_path) + script.run( + 'git', 'tag', 'test_tag', + expect_stderr=True, + cwd=version_pkg_path, + ) _change_test_package_version(script, version_pkg_path) - script.pip('install', '-e', '%s@test_tag#egg=version_pkg' % ('git+file://' + version_pkg_path.abspath.replace('\\', '/'))) + script.pip( + 'install', '-e', '%s@test_tag#egg=version_pkg' % + ('git+file://' + version_pkg_path.abspath.replace('\\', '/')) + ) version = script.run('version_pkg') assert '0.1' in version.stdout +@pytest.mark.network def test_git_with_tag_name_and_update(script, tmpdir): """ Test cloning a git repository and updating to a different version. """ - result = script.pip('install', '-e', '%s#egg=pip-test-package' % - local_checkout('git+http://github.com/pypa/pip-test-package.git', tmpdir.join("cache")), - expect_error=True) + result = script.pip( + 'install', '-e', '%s#egg=pip-test-package' % + local_checkout( + 'git+http://github.com/pypa/pip-test-package.git', + tmpdir.join("cache"), + ), + expect_error=True, + ) result.assert_installed('pip-test-package', with_files=['.git']) - result = script.pip('install', '--global-option=--version', '-e', - '%s@0.1.2#egg=pip-test-package' % - local_checkout('git+http://github.com/pypa/pip-test-package.git', tmpdir.join("cache")), - expect_error=True) + result = script.pip( + 'install', '--global-option=--version', '-e', + '%s@0.1.2#egg=pip-test-package' % + local_checkout( + 'git+http://github.com/pypa/pip-test-package.git', + tmpdir.join("cache"), + ), + expect_error=True, + ) assert '0.1.2' in result.stdout +@pytest.mark.network def test_git_branch_should_not_be_changed(script, tmpdir): """ Editable installations should not change branch related to issue #32 and #161 """ - script.pip('install', '-e', '%s#egg=pip-test-package' % - local_checkout('git+http://github.com/pypa/pip-test-package.git', tmpdir.join("cache")), - expect_error=True) - source_dir = script.venv_path/'src'/'pip-test-package' + script.pip( + 'install', '-e', '%s#egg=pip-test-package' % + local_checkout( + 'git+http://github.com/pypa/pip-test-package.git', + tmpdir.join("cache"), + ), + expect_error=True, + ) + source_dir = script.venv_path / 'src' / 'pip-test-package' result = script.run('git', 'branch', cwd=source_dir) assert '* master' in result.stdout, result.stdout +@pytest.mark.network def test_git_with_non_editable_unpacking(script, tmpdir): """ Test cloning a git repository from a non-editable URL with a given tag. """ - result = script.pip('install', '--global-option=--version', local_checkout( - 'git+http://github.com/pypa/pip-test-package.git@0.1.2#egg=pip-test-package', - tmpdir.join("cache") - ), expect_error=True) + result = script.pip( + 'install', '--global-option=--version', + local_checkout( + 'git+http://github.com/pypa/pip-test-package.git@0.1.2' + '#egg=pip-test-package', + tmpdir.join("cache") + ), + expect_error=True, + ) assert '0.1.2' in result.stdout +@pytest.mark.network def test_git_with_editable_where_egg_contains_dev_string(script, tmpdir): """ - Test cloning a git repository from an editable url which contains "dev" string - """ - result = script.pip('install', '-e', '%s#egg=django-devserver' % - local_checkout('git+git://github.com/dcramer/django-devserver.git', tmpdir.join("cache"))) + Test cloning a git repository from an editable url which contains "dev" + string + """ + result = script.pip( + 'install', '-e', + '%s#egg=django-devserver' % + local_checkout( + 'git+git://github.com/dcramer/django-devserver.git', + tmpdir.join("cache") + ) + ) result.assert_installed('django-devserver', with_files=['.git']) +@pytest.mark.network def test_git_with_non_editable_where_egg_contains_dev_string(script, tmpdir): """ - Test cloning a git repository from a non-editable url which contains "dev" string - """ - result = script.pip('install', '%s#egg=django-devserver' % - local_checkout('git+git://github.com/dcramer/django-devserver.git', tmpdir.join("cache"))) - devserver_folder = script.site_packages/'devserver' + Test cloning a git repository from a non-editable url which contains "dev" + string + """ + result = script.pip( + 'install', + '%s#egg=django-devserver' % + local_checkout( + 'git+git://github.com/dcramer/django-devserver.git', + tmpdir.join("cache") + ), + ) + devserver_folder = script.site_packages / 'devserver' assert devserver_folder in result.files_created, str(result) +@pytest.mark.network def test_git_with_ambiguous_revs(script): """ Test git with two "names" (tag/branch) pointing to the same commit """ version_pkg_path = _create_test_package(script) - package_url = 'git+file://%s@0.1#egg=version_pkg' % (version_pkg_path.abspath.replace('\\', '/')) + package_url = ( + 'git+file://%s@0.1#egg=version_pkg' % + (version_pkg_path.abspath.replace('\\', '/')) + ) script.run('git', 'tag', '0.1', cwd=version_pkg_path) result = script.pip('install', '-e', package_url) assert 'Could not find a tag or branch' not in result.stdout @@ -121,12 +218,15 @@ def test_git_with_ambiguous_revs(script): result.assert_installed('version-pkg', with_files=['.git']) +@pytest.mark.network def test_git_works_with_editable_non_origin_repo(script): - # set up, create a git repo and install it as editable from a local directory path + # set up, create a git repo and install it as editable from a local + # directory path version_pkg_path = _create_test_package(script) script.pip('install', '-e', version_pkg_path.abspath) - # 'freeze'ing this should not fall over, but should result in stderr output warning + # 'freeze'ing this should not fall over, but should result in stderr output + # warning result = script.pip('freeze', expect_stderr=True) assert "Error when trying to get requirement" in result.stderr assert "Could not determine repository location" in result.stdout diff --git a/tests/functional/test_install_vcs_git.py b/tests/functional/test_install_vcs_git.py index db567214960..d06a4d05a3e 100644 --- a/tests/functional/test_install_vcs_git.py +++ b/tests/functional/test_install_vcs_git.py @@ -1,5 +1,3 @@ -import sys - import pytest from mock import patch @@ -10,47 +8,74 @@ _change_test_package_submodule, _pull_in_submodule_changes_to_module, _create_test_package_with_submodule, - ) +) -def test_get_refs_should_return_tag_name_and_commit_pair(script): +@pytest.mark.network +def test_get_short_refs_should_return_tag_name_and_commit_pair(script): version_pkg_path = _create_test_package(script) script.run('git', 'tag', '0.1', cwd=version_pkg_path) script.run('git', 'tag', '0.2', cwd=version_pkg_path) - commit = script.run('git', 'rev-parse', 'HEAD', - cwd=version_pkg_path).stdout.strip() + commit = script.run( + 'git', 'rev-parse', 'HEAD', + cwd=version_pkg_path + ).stdout.strip() git = Git() - result = git.get_refs(version_pkg_path) + result = git.get_short_refs(version_pkg_path) assert result['0.1'] == commit, result assert result['0.2'] == commit, result -def test_get_refs_should_return_branch_name_and_commit_pair(script): +@pytest.mark.network +def test_get_short_refs_should_return_branch_name_and_commit_pair(script): version_pkg_path = _create_test_package(script) script.run('git', 'branch', 'branch0.1', cwd=version_pkg_path) - commit = script.run('git', 'rev-parse', 'HEAD', - cwd=version_pkg_path).stdout.strip() + commit = script.run( + 'git', 'rev-parse', 'HEAD', + cwd=version_pkg_path + ).stdout.strip() git = Git() - result = git.get_refs(version_pkg_path) + result = git.get_short_refs(version_pkg_path) assert result['master'] == commit, result assert result['branch0.1'] == commit, result -def test_get_refs_should_ignore_no_branch(script): +@pytest.mark.network +def test_get_short_refs_should_ignore_no_branch(script): version_pkg_path = _create_test_package(script) script.run('git', 'branch', 'branch0.1', cwd=version_pkg_path) - commit = script.run('git', 'rev-parse', 'HEAD', - cwd=version_pkg_path).stdout.strip() + commit = script.run( + 'git', 'rev-parse', 'HEAD', + cwd=version_pkg_path + ).stdout.strip() # current branch here is "* (nobranch)" - script.run('git', 'checkout', commit, - cwd=version_pkg_path, expect_stderr=True) + script.run( + 'git', 'checkout', commit, + cwd=version_pkg_path, + expect_stderr=True, + ) git = Git() - result = git.get_refs(version_pkg_path) + result = git.get_short_refs(version_pkg_path) assert result['master'] == commit, result assert result['branch0.1'] == commit, result -@patch('pip.vcs.git.Git.get_refs') +@pytest.mark.network +def test_check_version(script): + version_pkg_path = _create_test_package(script) + script.run('git', 'branch', 'branch0.1', cwd=version_pkg_path) + commit = script.run( + 'git', 'rev-parse', 'HEAD', + cwd=version_pkg_path + ).stdout.strip() + git = Git() + assert git.check_version(version_pkg_path, [commit]) + assert git.check_version(version_pkg_path, [commit[:7]]) + assert not git.check_version(version_pkg_path, ['branch0.1']) + assert not git.check_version(version_pkg_path, ['abc123']) + + +@patch('pip.vcs.git.Git.get_short_refs') def test_check_rev_options_should_handle_branch_name(get_refs_mock): get_refs_mock.return_value = {'master': '123456', '0.1': '123456'} git = Git() @@ -59,7 +84,7 @@ def test_check_rev_options_should_handle_branch_name(get_refs_mock): assert result == ['123456'] -@patch('pip.vcs.git.Git.get_refs') +@patch('pip.vcs.git.Git.get_short_refs') def test_check_rev_options_should_handle_tag_name(get_refs_mock): get_refs_mock.return_value = {'master': '123456', '0.1': '123456'} git = Git() @@ -68,7 +93,7 @@ def test_check_rev_options_should_handle_tag_name(get_refs_mock): assert result == ['123456'] -@patch('pip.vcs.git.Git.get_refs') +@patch('pip.vcs.git.Git.get_short_refs') def test_check_rev_options_should_handle_ambiguous_commit(get_refs_mock): get_refs_mock.return_value = {'master': '123456', '0.1': '123456'} git = Git() @@ -79,19 +104,32 @@ def test_check_rev_options_should_handle_ambiguous_commit(get_refs_mock): # TODO(pnasrat) fix all helpers to do right things with paths on windows. @pytest.mark.skipif("sys.platform == 'win32'") +@pytest.mark.network def test_check_submodule_addition(script): """ Submodules are pulled in on install and updated on upgrade. """ module_path, submodule_path = _create_test_package_with_submodule(script) - install_result = script.pip('install', '-e', 'git+'+module_path+'#egg=version_pkg') - assert script.venv/'src/version-pkg/testpkg/static/testfile' in install_result.files_created + install_result = script.pip( + 'install', '-e', 'git+' + module_path + '#egg=version_pkg' + ) + assert ( + script.venv / 'src/version-pkg/testpkg/static/testfile' + in install_result.files_created + ) _change_test_package_submodule(script, submodule_path) _pull_in_submodule_changes_to_module(script, module_path) # expect error because git may write to stderr - update_result = script.pip('install', '-e', 'git+'+module_path+'#egg=version_pkg', '--upgrade', expect_error=True) + update_result = script.pip( + 'install', '-e', 'git+' + module_path + '#egg=version_pkg', + '--upgrade', + expect_error=True, + ) - assert script.venv/'src/version-pkg/testpkg/static/testfile2' in update_result.files_created + assert ( + script.venv / 'src/version-pkg/testpkg/static/testfile2' + in update_result.files_created + ) diff --git a/tests/functional/test_install_vcs_svn.py b/tests/functional/test_install_vcs_svn.py index 57ac212ff81..437d7d82e54 100644 --- a/tests/functional/test_install_vcs_svn.py +++ b/tests/functional/test_install_vcs_svn.py @@ -1,20 +1,27 @@ +import pytest from mock import patch from pip.vcs.subversion import Subversion -@patch('pip.vcs.subversion.call_subprocess') +@patch('pip.vcs.call_subprocess') +@pytest.mark.network def test_obtain_should_recognize_auth_info_url(call_subprocess_mock, script): svn = Subversion(url='svn+http://username:password@svn.example.com/') - svn.obtain(script.scratch_path/'test') - call_subprocess_mock.assert_called_with([ - svn.cmd, 'checkout', '-q', '--username', 'username', '--password', 'password', - 'http://username:password@svn.example.com/', script.scratch_path/'test']) + svn.obtain(script.scratch_path / 'test') + assert call_subprocess_mock.call_args[0][0] == [ + svn.name, 'checkout', '-q', '--username', 'username', '--password', + 'password', 'http://svn.example.com/', + script.scratch_path / 'test', + ] -@patch('pip.vcs.subversion.call_subprocess') +@patch('pip.vcs.call_subprocess') +@pytest.mark.network def test_export_should_recognize_auth_info_url(call_subprocess_mock, script): svn = Subversion(url='svn+http://username:password@svn.example.com/') - svn.export(script.scratch_path/'test') - assert call_subprocess_mock.call_args[0] == ([ - svn.cmd, 'export', '--username', 'username', '--password', 'password', - 'http://username:password@svn.example.com/', script.scratch_path/'test'],) + svn.export(script.scratch_path / 'test') + assert call_subprocess_mock.call_args[0][0] == [ + svn.name, 'export', '--username', 'username', '--password', + 'password', 'http://svn.example.com/', + script.scratch_path / 'test', + ] diff --git a/tests/functional/test_install_wheel.py b/tests/functional/test_install_wheel.py index 1a602c64465..1f43c65e9c5 100644 --- a/tests/functional/test_install_wheel.py +++ b/tests/functional/test_install_wheel.py @@ -1,15 +1,17 @@ import os +import sys import pytest import glob from tests.lib.path import Path -from tests.lib import TestFailure def test_install_from_future_wheel_version(script, data): """ Test installing a future wheel """ + from tests.lib import TestFailure + package = data.packages.join("futurewheel-3.0-py2.py3-none-any.whl") result = script.pip('install', package, '--no-index', expect_error=True) with pytest.raises(TestFailure): @@ -17,7 +19,10 @@ def test_install_from_future_wheel_version(script, data): editable=False) package = data.packages.join("futurewheel-1.9-py2.py3-none-any.whl") - result = script.pip('install', package, '--no-index', expect_error=False) + result = script.pip( + 'install', package, '--no-index', expect_error=False, + expect_stderr=True, + ) result.assert_installed('futurewheel', without_egg_link=True, editable=False) @@ -26,6 +31,7 @@ def test_install_from_broken_wheel(script, data): """ Test that installing a broken wheel fails properly """ + from tests.lib import TestFailure package = data.packages.join("brokenwheel-1.0-py2.py3-none-any.whl") result = script.pip('install', package, '--no-index', expect_error=True) with pytest.raises(TestFailure): @@ -37,10 +43,12 @@ def test_install_from_wheel(script, data): """ Test installing from a wheel (that has a script) """ - result = script.pip('install', 'has.script==1.0', '--use-wheel', - '--no-index', '--find-links='+data.find_links, - expect_error=False) - dist_info_folder = script.site_packages/'has.script-1.0.dist-info' + result = script.pip( + 'install', 'has.script==1.0', '--no-index', + '--find-links=' + data.find_links, + expect_error=False, + ) + dist_info_folder = script.site_packages / 'has.script-1.0.dist-info' assert dist_info_folder in result.files_created, (dist_info_folder, result.files_created, result.stdout) @@ -52,14 +60,16 @@ def test_install_from_wheel_with_extras(script, data): """ Test installing from a wheel with extras. """ - result = script.pip('install', 'complex-dist[simple]', '--use-wheel', - '--no-index', '--find-links='+data.find_links, - expect_error=False) - dist_info_folder = script.site_packages/'complex_dist-0.1.dist-info' + result = script.pip( + 'install', 'complex-dist[simple]', '--no-index', + '--find-links=' + data.find_links, + expect_error=False, + ) + dist_info_folder = script.site_packages / 'complex_dist-0.1.dist-info' assert dist_info_folder in result.files_created, (dist_info_folder, result.files_created, result.stdout) - dist_info_folder = script.site_packages/'simple.dist-0.1.dist-info' + dist_info_folder = script.site_packages / 'simple.dist-0.1.dist-info' assert dist_info_folder in result.files_created, (dist_info_folder, result.files_created, result.stdout) @@ -71,10 +81,21 @@ def test_install_from_wheel_file(script, data): """ package = data.packages.join("simple.dist-0.1-py2.py3-none-any.whl") result = script.pip('install', package, '--no-index', expect_error=False) - dist_info_folder = script.site_packages/'simple.dist-0.1.dist-info' + dist_info_folder = script.site_packages / 'simple.dist-0.1.dist-info' assert dist_info_folder in result.files_created, (dist_info_folder, result.files_created, result.stdout) + installer = dist_info_folder / 'INSTALLER' + assert installer in result.files_created, (dist_info_folder, + result.files_created, + result.stdout) + with open(script.base_path / installer, 'rb') as installer_file: + installer_details = installer_file.read() + assert installer_details == b'pip\n' + installer_temp = dist_info_folder / 'INSTALLER.pip' + assert installer_temp not in result.files_created, (dist_info_folder, + result.files_created, + result.stdout) # header installs are broke in pypy virtualenvs @@ -86,31 +107,54 @@ def test_install_from_wheel_with_headers(script, data): """ package = data.packages.join("headers.dist-0.1-py2.py3-none-any.whl") result = script.pip('install', package, '--no-index', expect_error=False) - dist_info_folder = script.site_packages/'headers.dist-0.1.dist-info' + dist_info_folder = script.site_packages / 'headers.dist-0.1.dist-info' assert dist_info_folder in result.files_created, (dist_info_folder, result.files_created, result.stdout) +@pytest.mark.network def test_install_wheel_with_target(script, data): """ Test installing a wheel using pip install --target """ script.pip('install', 'wheel') - target_dir = script.scratch_path/'target' - result = script.pip('install', 'simple.dist==0.1', '-t', target_dir, '--use-wheel', - '--no-index', '--find-links='+data.find_links) - assert Path('scratch')/'target'/'simpledist' in result.files_created, str(result) + target_dir = script.scratch_path / 'target' + result = script.pip( + 'install', 'simple.dist==0.1', '-t', target_dir, + '--no-index', '--find-links=' + data.find_links, + ) + assert Path('scratch') / 'target' / 'simpledist' in result.files_created, ( + str(result) + ) def test_install_wheel_with_root(script, data): """ Test installing a wheel using pip install --root """ - root_dir = script.scratch_path/'root' - result = script.pip('install', 'simple.dist==0.1', '--root', root_dir, '--use-wheel', - '--no-index', '--find-links='+data.find_links) - assert Path('scratch')/'root' in result.files_created + root_dir = script.scratch_path / 'root' + result = script.pip( + 'install', 'simple.dist==0.1', '--root', root_dir, + '--no-index', '--find-links=' + data.find_links, + ) + assert Path('scratch') / 'root' in result.files_created + + +def test_install_wheel_with_prefix(script, data): + """ + Test installing a wheel using pip install --prefix + """ + prefix_dir = script.scratch_path / 'prefix' + result = script.pip( + 'install', 'simple.dist==0.1', '--prefix', prefix_dir, + '--no-index', '--find-links=' + data.find_links, + ) + if hasattr(sys, "pypy_version_info"): + lib = Path('scratch') / 'prefix' / 'site-packages' + else: + lib = Path('scratch') / 'prefix' / 'lib' + assert lib in result.files_created def test_install_from_wheel_installs_deps(script, data): @@ -119,7 +163,9 @@ def test_install_from_wheel_installs_deps(script, data): """ # 'requires_source' depends on the 'source' project package = data.packages.join("requires_source-1.0-py2.py3-none-any.whl") - result = script.pip('install', '--no-index', '--find-links', data.find_links, package) + result = script.pip( + 'install', '--no-index', '--find-links', data.find_links, package, + ) result.assert_installed('source', editable=False) @@ -129,33 +175,40 @@ def test_install_from_wheel_no_deps(script, data): """ # 'requires_source' depends on the 'source' project package = data.packages.join("requires_source-1.0-py2.py3-none-any.whl") - result = script.pip('install', '--no-index', '--find-links', data.find_links, '--no-deps', package) - pkg_folder = script.site_packages/'source' + result = script.pip( + 'install', '--no-index', '--find-links', data.find_links, '--no-deps', + package, + ) + pkg_folder = script.site_packages / 'source' assert pkg_folder not in result.files_created -# --user option is broken in pypy -@pytest.mark.skipif("hasattr(sys, 'pypy_version_info')") +@pytest.mark.network def test_install_user_wheel(script, virtualenv, data): """ Test user install from wheel (that has a script) """ virtualenv.system_site_packages = True script.pip('install', 'wheel') - result = script.pip('install', 'has.script==1.0', '--user', '--use-wheel', - '--no-index', '--find-links='+data.find_links) + result = script.pip( + 'install', 'has.script==1.0', '--user', '--no-index', + '--find-links=' + data.find_links, + ) egg_info_folder = script.user_site / 'has.script-1.0.dist-info' assert egg_info_folder in result.files_created, str(result) script_file = script.user_bin / 'script.py' assert script_file in result.files_created + def test_install_from_wheel_gen_entrypoint(script, data): """ Test installing scripts (entry points are generated) """ - result = script.pip('install', 'script.wheel1a==0.1', '--use-wheel', - '--no-index', '--find-links='+data.find_links, - expect_error=False) + result = script.pip( + 'install', 'script.wheel1a==0.1', '--no-index', + '--find-links=' + data.find_links, + expect_error=False, + ) if os.name == 'nt': wrapper_file = script.bin / 't1.exe' else: @@ -165,13 +218,36 @@ def test_install_from_wheel_gen_entrypoint(script, data): if os.name != "nt": assert bool(os.access(script.base_path / wrapper_file, os.X_OK)) + +def test_install_from_wheel_gen_uppercase_entrypoint(script, data): + """ + Test installing scripts with uppercase letters in entry point names + """ + result = script.pip( + 'install', 'console-scripts-uppercase==1.0', '--no-index', + '--find-links=' + data.find_links, + expect_error=False, + ) + if os.name == 'nt': + # Case probably doesn't make any difference on NT + wrapper_file = script.bin / 'cmdName.exe' + else: + wrapper_file = script.bin / 'cmdName' + assert wrapper_file in result.files_created + + if os.name != "nt": + assert bool(os.access(script.base_path / wrapper_file, os.X_OK)) + + def test_install_from_wheel_with_legacy(script, data): """ Test installing scripts (legacy scripts are preserved) """ - result = script.pip('install', 'script.wheel2a==0.1', '--use-wheel', - '--no-index', '--find-links='+data.find_links, - expect_error=False) + result = script.pip( + 'install', 'script.wheel2a==0.1', '--no-index', + '--find-links=' + data.find_links, + expect_error=False, + ) legacy_file1 = script.bin / 'testscript1.bat' legacy_file2 = script.bin / 'testscript2' @@ -179,14 +255,17 @@ def test_install_from_wheel_with_legacy(script, data): assert legacy_file1 in result.files_created assert legacy_file2 in result.files_created + def test_install_from_wheel_no_setuptools_entrypoint(script, data): """ Test that when we generate scripts, any existing setuptools wrappers in the wheel are skipped. """ - result = script.pip('install', 'script.wheel1==0.1', '--use-wheel', - '--no-index', '--find-links='+data.find_links, - expect_error=False) + result = script.pip( + 'install', 'script.wheel1==0.1', '--no-index', + '--find-links=' + data.find_links, + expect_error=False, + ) if os.name == 'nt': wrapper_file = script.bin / 't1.exe' else: @@ -204,11 +283,14 @@ def test_install_from_wheel_no_setuptools_entrypoint(script, data): def test_skipping_setuptools_doesnt_skip_legacy(script, data): """ - Test installing scripts (legacy scripts are preserved even when we skip setuptools wrappers) + Test installing scripts (legacy scripts are preserved even when we skip + setuptools wrappers) """ - result = script.pip('install', 'script.wheel2==0.1', '--use-wheel', - '--no-index', '--find-links='+data.find_links, - expect_error=False) + result = script.pip( + 'install', 'script.wheel2==0.1', '--no-index', + '--find-links=' + data.find_links, + expect_error=False, + ) legacy_file1 = script.bin / 'testscript1.bat' legacy_file2 = script.bin / 'testscript2' @@ -218,13 +300,16 @@ def test_skipping_setuptools_doesnt_skip_legacy(script, data): assert legacy_file2 in result.files_created assert wrapper_helper not in result.files_created + def test_install_from_wheel_gui_entrypoint(script, data): """ Test installing scripts (gui entry points are generated) """ - result = script.pip('install', 'script.wheel3==0.1', '--use-wheel', - '--no-index', '--find-links='+data.find_links, - expect_error=False) + result = script.pip( + 'install', 'script.wheel3==0.1', '--no-index', + '--find-links=' + data.find_links, + expect_error=False, + ) if os.name == 'nt': wrapper_file = script.bin / 't1.exe' else: @@ -243,11 +328,11 @@ def test_wheel_compiles_pyc(script, data): # There are many locations for the __init__.pyc file so attempt to find # any of them exists = [ - os.path.exists(script.site_packages_path/"simpledist/__init__.pyc"), + os.path.exists(script.site_packages_path / "simpledist/__init__.pyc"), ] exists += glob.glob( - script.site_packages_path/"simpledist/__pycache__/__init__*.pyc" + script.site_packages_path / "simpledist/__pycache__/__init__*.pyc" ) assert any(exists) @@ -264,11 +349,30 @@ def test_wheel_no_compiles_pyc(script, data): # There are many locations for the __init__.pyc file so attempt to find # any of them exists = [ - os.path.exists(script.site_packages_path/"simpledist/__init__.pyc"), + os.path.exists(script.site_packages_path / "simpledist/__init__.pyc"), ] exists += glob.glob( - script.site_packages_path/"simpledist/__pycache__/__init__*.pyc" + script.site_packages_path / "simpledist/__pycache__/__init__*.pyc" ) assert not any(exists) + + +def test_install_from_wheel_uninstalls_old_version(script, data): + # regression test for https://github.com/pypa/pip/issues/1825 + package = data.packages.join("simplewheel-1.0-py2.py3-none-any.whl") + result = script.pip('install', package, '--no-index', expect_error=True) + package = data.packages.join("simplewheel-2.0-py2.py3-none-any.whl") + result = script.pip('install', package, '--no-index', expect_error=False) + dist_info_folder = script.site_packages / 'simplewheel-2.0.dist-info' + assert dist_info_folder in result.files_created + dist_info_folder = script.site_packages / 'simplewheel-1.0.dist-info' + assert dist_info_folder not in result.files_created + + +def test_wheel_compile_syntax_error(script, data): + package = data.packages.join("compilewheel-1.0-py2.py3-none-any.whl") + result = script.pip('install', '--compile', package, '--no-index') + assert 'yield from' not in result.stdout + assert 'SyntaxError: ' not in result.stdout diff --git a/tests/functional/test_list.py b/tests/functional/test_list.py index 955c55df923..4785dd22cc3 100644 --- a/tests/functional/test_list.py +++ b/tests/functional/test_list.py @@ -1,63 +1,593 @@ +import json import os -import re -import textwrap +import pytest -from tests.lib.local_repos import local_checkout +WARN_FORMAT = ("DEPRECATION: The default format will switch to columns in the " + "future. You can use --format=legacy (or define a list_format " + "in your pip.conf) to disable this warning.") def test_list_command(script, data): """ - Test default behavior of list command. + Test default behavior of list command without format specifier. + A warning is expected. """ - script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0', 'simple2==3.0') - result = script.pip('list') + script.pip( + 'install', '-f', data.find_links, '--no-index', 'simple==1.0', + 'simple2==3.0', + ) + result = script.pip('list', expect_stderr=True) + assert WARN_FORMAT in result.stderr, str(result) assert 'simple (1.0)' in result.stdout, str(result) assert 'simple2 (3.0)' in result.stdout, str(result) +def test_columns_flag(script, data): + """ + Test the list command with the '--columns' option + """ + script.pip( + 'install', '-f', data.find_links, '--no-index', 'simple==1.0', + 'simple2==3.0', + ) + result = script.pip('list', '--columns') + assert 'Package' in result.stdout, str(result) + assert 'Version' in result.stdout, str(result) + assert 'simple (1.0)' not in result.stdout, str(result) + assert 'simple 1.0' in result.stdout, str(result) + assert 'simple2 3.0' in result.stdout, str(result) + + +def test_legacy_format(script, data): + """ + Test that legacy format + """ + script.pip( + 'install', '-f', data.find_links, '--no-index', 'simple==1.0', + 'simple2==3.0', + ) + result = script.pip('list', '--format=legacy') + assert 'simple (1.0)' in result.stdout, str(result) + assert 'simple2 (3.0)' in result.stdout, str(result) + + +def test_format_priority(script, data): + """ + Test that latest format has priority over previous ones. + """ + script.pip( + 'install', '-f', data.find_links, '--no-index', 'simple==1.0', + 'simple2==3.0', + ) + result = script.pip('list', '--format=columns', '--format=legacy') + assert 'simple (1.0)' in result.stdout, str(result) + assert 'simple2 (3.0)' in result.stdout, str(result) + assert 'simple 1.0' not in result.stdout, str(result) + assert 'simple2 3.0' not in result.stdout, str(result) + + result = script.pip('list', '--format=legacy', '--columns') + assert 'Package' in result.stdout, str(result) + assert 'Version' in result.stdout, str(result) + assert 'simple (1.0)' not in result.stdout, str(result) + assert 'simple2 (3.0)' not in result.stdout, str(result) + assert 'simple 1.0' in result.stdout, str(result) + assert 'simple2 3.0' in result.stdout, str(result) + + def test_local_flag(script, data): """ Test the behavior of --local flag in the list command """ script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') - result = script.pip('list', '--local') + result = script.pip('list', '--local', '--format=legacy') assert 'simple (1.0)' in result.stdout +def test_local_columns_flag(script, data): + """ + Test the behavior of --local --columns flags in the list command + + """ + script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') + result = script.pip('list', '--local', '--columns') + assert 'Package' in result.stdout + assert 'Version' in result.stdout + assert 'simple (1.0)' not in result.stdout + assert 'simple 1.0' in result.stdout, str(result) + + +def test_local_legacy_flag(script, data): + """ + Test the behavior of --local --format=legacy flags in the list + command. + """ + script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') + result = script.pip('list', '--local', '--format=legacy') + assert 'simple (1.0)' in result.stdout + + +def test_user_flag(script, data, virtualenv): + """ + Test the behavior of --user flag in the list command + + """ + virtualenv.system_site_packages = True + script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') + script.pip('install', '-f', data.find_links, '--no-index', + '--user', 'simple2==2.0') + result = script.pip('list', '--user', '--format=legacy') + assert 'simple (1.0)' not in result.stdout + assert 'simple2 (2.0)' in result.stdout + + +def test_user_columns_flag(script, data, virtualenv): + """ + Test the behavior of --user --columns flags in the list command + + """ + virtualenv.system_site_packages = True + script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') + script.pip('install', '-f', data.find_links, '--no-index', + '--user', 'simple2==2.0') + result = script.pip('list', '--user', '--columns') + assert 'Package' in result.stdout + assert 'Version' in result.stdout + assert 'simple2 (2.0)' not in result.stdout + assert 'simple2 2.0' in result.stdout, str(result) + + +def test_user_legacy(script, data, virtualenv): + """ + Test the behavior of --user flag in the list command + + """ + virtualenv.system_site_packages = True + script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') + script.pip('install', '-f', data.find_links, '--no-index', + '--user', 'simple2==2.0') + result = script.pip('list', '--user', '--format=legacy') + assert 'simple (1.0)' not in result.stdout + assert 'simple2 (2.0)' in result.stdout, str(result) + + +@pytest.mark.network def test_uptodate_flag(script, data): """ Test the behavior of --uptodate flag in the list command """ - script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0', 'simple2==3.0') - script.pip('install', '-e', 'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package') - result = script.pip('list', '-f', data.find_links, '--no-index', '--uptodate') - assert 'simple (1.0)' not in result.stdout #3.0 is latest - assert 'pip-test-package' not in result.stdout #editables excluded + script.pip( + 'install', '-f', data.find_links, '--no-index', 'simple==1.0', + 'simple2==3.0', + ) + script.pip( + 'install', '-e', + 'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package' + ) + result = script.pip( + 'list', '-f', data.find_links, '--no-index', '--uptodate', + '--format=legacy', + ) + assert 'simple (1.0)' not in result.stdout # 3.0 is latest + assert 'pip-test-package (0.1.1,' in result.stdout # editables included assert 'simple2 (3.0)' in result.stdout, str(result) +@pytest.mark.network +def test_uptodate_columns_flag(script, data): + """ + Test the behavior of --uptodate --columns flag in the list command + + """ + script.pip( + 'install', '-f', data.find_links, '--no-index', 'simple==1.0', + 'simple2==3.0', + ) + script.pip( + 'install', '-e', + 'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package' + ) + result = script.pip( + 'list', '-f', data.find_links, '--no-index', '--uptodate', + '--columns', + ) + assert 'Package' in result.stdout + assert 'Version' in result.stdout + assert 'Location' in result.stdout # editables included + assert 'pip-test-package (0.1.1,' not in result.stdout + assert 'pip-test-package 0.1.1' in result.stdout, str(result) + assert 'simple2 3.0' in result.stdout, str(result) + + +@pytest.mark.network +def test_uptodate_legacy_flag(script, data): + """ + Test the behavior of --uptodate --format=legacy flag in the list command + + """ + script.pip( + 'install', '-f', data.find_links, '--no-index', 'simple==1.0', + 'simple2==3.0', + ) + script.pip( + 'install', '-e', + 'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package' + ) + result = script.pip( + 'list', '-f', data.find_links, '--no-index', '--uptodate', + '--format=legacy', + ) + assert 'simple (1.0)' not in result.stdout # 3.0 is latest + assert 'pip-test-package (0.1.1,' in result.stdout # editables included + assert 'simple2 (3.0)' in result.stdout, str(result) + + +@pytest.mark.network def test_outdated_flag(script, data): """ Test the behavior of --outdated flag in the list command """ - script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0', 'simple2==3.0') - script.pip('install', '-e', 'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package') - result = script.pip('list', '-f', data.find_links, '--no-index', '--outdated') - assert 'simple (Current: 1.0 Latest: 3.0)' in result.stdout - assert 'pip-test-package' not in result.stdout #editables excluded - assert 'simple2' not in result.stdout, str(result) #3.0 is latest + script.pip( + 'install', '-f', data.find_links, '--no-index', 'simple==1.0', + 'simple2==3.0', 'simplewheel==1.0', + ) + script.pip( + 'install', '-e', + 'git+https://github.com/pypa/pip-test-package.git' + '@0.1#egg=pip-test-package' + ) + result = script.pip( + 'list', '-f', data.find_links, '--no-index', '--outdated', + '--format=legacy', + ) + assert 'simple (1.0) - Latest: 3.0 [sdist]' in result.stdout + assert 'simplewheel (1.0) - Latest: 2.0 [wheel]' in result.stdout + assert 'pip-test-package (0.1, ' in result.stdout + assert ' Latest: 0.1.1 [sdist]' in result.stdout + assert 'simple2' not in result.stdout, str(result) # 3.0 is latest + + +@pytest.mark.network +def test_outdated_columns_flag(script, data): + """ + Test the behavior of --outdated --columns flag in the list command + + """ + script.pip( + 'install', '-f', data.find_links, '--no-index', 'simple==1.0', + 'simple2==3.0', 'simplewheel==1.0', + ) + script.pip( + 'install', '-e', + 'git+https://github.com/pypa/pip-test-package.git' + '@0.1#egg=pip-test-package' + ) + result = script.pip( + 'list', '-f', data.find_links, '--no-index', '--outdated', + '--columns', + ) + assert 'Package' in result.stdout + assert 'Version' in result.stdout + assert 'Latest' in result.stdout + assert 'Type' in result.stdout + assert 'simple (1.0) - Latest: 3.0 [sdist]' not in result.stdout + assert 'simplewheel (1.0) - Latest: 2.0 [wheel]' not in result.stdout + assert 'simple 1.0 3.0 sdist' in result.stdout, ( + str(result) + ) + assert 'simplewheel 1.0 2.0 wheel' in result.stdout, ( + str(result) + ) + assert 'simple2' not in result.stdout, str(result) # 3.0 is latest + + +@pytest.mark.network +def test_outdated_legacy(script, data): + """ + Test the behavior of --outdated --format=legacy flag in the list command + + """ + script.pip( + 'install', '-f', data.find_links, '--no-index', 'simple==1.0', + 'simple2==3.0', 'simplewheel==1.0', + ) + script.pip( + 'install', '-e', + 'git+https://github.com/pypa/pip-test-package.git' + '@0.1#egg=pip-test-package' + ) + result = script.pip( + 'list', '-f', data.find_links, '--no-index', '--outdated', + '--format=legacy', + ) + assert 'simple (1.0) - Latest: 3.0 [sdist]' in result.stdout + assert 'simplewheel (1.0) - Latest: 2.0 [wheel]' in result.stdout + assert 'pip-test-package (0.1, ' in result.stdout + assert ' Latest: 0.1.1 [sdist]' in result.stdout + assert 'simple2' not in result.stdout, str(result) # 3.0 is latest +@pytest.mark.network def test_editables_flag(script, data): """ Test the behavior of --editables flag in the list command """ script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') - result = script.pip('install', '-e', 'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package') - result = script.pip('list', '--editable') + result = script.pip( + 'install', '-e', + 'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package' + ) + result = script.pip('list', '--editable', '--format=legacy') assert 'simple (1.0)' not in result.stdout, str(result) - assert os.path.join('src', 'pip-test-package') in result.stdout, str(result) + assert os.path.join('src', 'pip-test-package') in result.stdout, ( + str(result) + ) + + +@pytest.mark.network +def test_editables_columns_flag(script, data): + """ + Test the behavior of --editables flag in the list command + """ + script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') + result = script.pip( + 'install', '-e', + 'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package' + ) + result = script.pip('list', '--editable', '--columns') + assert 'Package' in result.stdout + assert 'Version' in result.stdout + assert 'Location' in result.stdout + assert os.path.join('src', 'pip-test-package') in result.stdout, ( + str(result) + ) + + +@pytest.mark.network +def test_editables_legacy(script, data): + """ + Test the behavior of --editables flag in the list command + """ + script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') + script.pip( + 'install', '-e', + 'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package' + ) + result = script.pip( + 'list', '--editable', '--format=legacy', expect_stderr=True, + ) + assert 'simple (1.0)' not in result.stdout, str(result) + assert os.path.join('src', 'pip-test-package') in result.stdout, ( + str(result) + ) + + +@pytest.mark.network +def test_uptodate_editables_flag(script, data): + """ + test the behavior of --editable --uptodate flag in the list command + """ + script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') + result = script.pip( + 'install', '-e', + 'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package' + ) + result = script.pip( + 'list', '-f', data.find_links, '--no-index', + '--editable', '--uptodate', + '--format=legacy', + ) + assert 'simple (1.0)' not in result.stdout, str(result) + assert os.path.join('src', 'pip-test-package') in result.stdout, ( + str(result) + ) + + +@pytest.mark.network +def test_uptodate_editables_columns_flag(script, data): + """ + test the behavior of --editable --uptodate --columns flag in the + list command + """ + script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') + result = script.pip( + 'install', '-e', + 'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package' + ) + result = script.pip( + 'list', '-f', data.find_links, '--no-index', + '--editable', '--uptodate', '--columns', + ) + assert 'Package' in result.stdout + assert 'Version' in result.stdout + assert 'Location' in result.stdout + assert os.path.join('src', 'pip-test-package') in result.stdout, ( + str(result) + ) + + +@pytest.mark.network +def test_uptodate_editables_legacy(script, data): + """ + test the behavior of --editable --uptodate --columns --format=legacy flag + in the list command + """ + script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') + script.pip( + 'install', '-e', + 'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package' + ) + result = script.pip( + 'list', '-f', data.find_links, '--no-index', '--editable', + '--uptodate', '--format=legacy', + ) + assert 'simple (1.0)' not in result.stdout, str(result) + assert os.path.join('src', 'pip-test-package') in result.stdout, ( + str(result) + ) + + +@pytest.mark.network +def test_outdated_editables_flag(script, data): + """ + test the behavior of --editable --outdated flag in the list command + """ + script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') + result = script.pip( + 'install', '-e', + 'git+https://github.com/pypa/pip-test-package.git' + '@0.1#egg=pip-test-package' + ) + result = script.pip( + 'list', '-f', data.find_links, '--no-index', + '--editable', '--outdated', + '--format=legacy', + ) + assert 'simple (1.0)' not in result.stdout, str(result) + assert os.path.join('src', 'pip-test-package') in result.stdout, ( + str(result) + ) + + +@pytest.mark.network +def test_outdated_editables_columns_flag(script, data): + """ + test the behavior of --editable --outdated flag in the list command + """ + script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') + result = script.pip( + 'install', '-e', + 'git+https://github.com/pypa/pip-test-package.git' + '@0.1#egg=pip-test-package' + ) + result = script.pip( + 'list', '-f', data.find_links, '--no-index', + '--editable', '--outdated', '--columns', + ) + assert 'Package' in result.stdout + assert 'Version' in result.stdout + assert 'Location' in result.stdout + assert os.path.join('src', 'pip-test-package') in result.stdout, ( + str(result) + ) + + +@pytest.mark.network +def test_outdated_editables_legacy(script, data): + """ + test the behavior of --editable --outdated flag in the list command + """ + script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') + script.pip( + 'install', '-e', + 'git+https://github.com/pypa/pip-test-package.git' + '@0.1#egg=pip-test-package' + ) + result = script.pip( + 'list', '-f', data.find_links, '--no-index', + '--editable', '--outdated', '--format=legacy', + ) + assert 'simple (1.0)' not in result.stdout, str(result) + assert os.path.join('src', 'pip-test-package') in result.stdout, ( + str(result) + ) + + +def test_outdated_pre(script, data): + script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') + + # Let's build a fake wheelhouse + script.scratch_path.join("wheelhouse").mkdir() + wheelhouse_path = script.scratch_path / 'wheelhouse' + wheelhouse_path.join('simple-1.1-py2.py3-none-any.whl').write('') + wheelhouse_path.join('simple-2.0.dev0-py2.py3-none-any.whl').write('') + result = script.pip( + 'list', '--no-index', '--find-links', wheelhouse_path, + '--format=legacy', + ) + assert 'simple (1.0)' in result.stdout + result = script.pip( + 'list', '--no-index', '--find-links', wheelhouse_path, '--outdated', + '--format=legacy', + ) + assert 'simple (1.0) - Latest: 1.1 [wheel]' in result.stdout + result_pre = script.pip('list', '--no-index', + '--find-links', wheelhouse_path, + '--outdated', '--pre', '--format=legacy') + assert 'simple (1.0) - Latest: 2.0.dev0 [wheel]' in result_pre.stdout + + +def test_outdated_formats(script, data): + """ Test of different outdated formats """ + script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') + + # Let's build a fake wheelhouse + script.scratch_path.join("wheelhouse").mkdir() + wheelhouse_path = script.scratch_path / 'wheelhouse' + wheelhouse_path.join('simple-1.1-py2.py3-none-any.whl').write('') + result = script.pip( + 'list', '--no-index', '--find-links', wheelhouse_path, + '--format=freeze', + ) + assert 'simple==1.0' in result.stdout + + # Check legacy + result = script.pip('list', '--no-index', '--find-links', wheelhouse_path, + '--outdated', '--format=legacy') + assert 'simple (1.0) - Latest: 1.1 [wheel]' in result.stdout + + # Check columns + result = script.pip( + 'list', '--no-index', '--find-links', wheelhouse_path, + '--outdated', '--columns', + ) + assert 'Package Version Latest Type' in result.stdout + assert 'simple 1.0 1.1 wheel' in result.stdout + + # Check freeze + result = script.pip( + 'list', '--no-index', '--find-links', wheelhouse_path, + '--outdated', '--format=freeze', + ) + assert 'simple==1.0' in result.stdout + + # Check json + result = script.pip( + 'list', '--no-index', '--find-links', wheelhouse_path, + '--outdated', '--format=json', + ) + data = json.loads(result.stdout) + assert data == [{'name': 'simple', 'version': '1.0', + 'latest_version': '1.1', 'latest_filetype': 'wheel'}] + + +def test_list_freeze(script, data): + """ + Test freeze formatting of list command + + """ + script.pip( + 'install', '-f', data.find_links, '--no-index', 'simple==1.0', + 'simple2==3.0', + ) + result = script.pip('list', '--format=freeze') + assert 'simple==1.0' in result.stdout, str(result) + assert 'simple2==3.0' in result.stdout, str(result) + + +def test_list_json(script, data): + """ + Test json formatting of list command + + """ + script.pip( + 'install', '-f', data.find_links, '--no-index', 'simple==1.0', + 'simple2==3.0', + ) + result = script.pip('list', '--format=json') + data = json.loads(result.stdout) + assert {'name': 'simple', 'version': '1.0'} in data + assert {'name': 'simple2', 'version': '3.0'} in data diff --git a/tests/functional/test_requests.py b/tests/functional/test_requests.py index 063cf90b686..a883edc28bf 100644 --- a/tests/functional/test_requests.py +++ b/tests/functional/test_requests.py @@ -3,8 +3,15 @@ @pytest.mark.skipif def test_timeout(script): - result = script.pip("--timeout", "0.01", "install", "-vvv", "INITools", + result = script.pip( + "--timeout", "0.01", "install", "-vvv", "INITools", expect_error=True, ) - assert "Could not fetch URL https://pypi.python.org/simple/INITools/: timed out" in result.stdout - assert "Could not fetch URL https://pypi.python.org/simple/: timed out" in result.stdout + assert ( + "Could not fetch URL https://pypi.python.org/simple/INITools/: " + "timed out" in result.stdout + ) + assert ( + "Could not fetch URL https://pypi.python.org/simple/: " + "timed out" in result.stdout + ) diff --git a/tests/functional/test_search.py b/tests/functional/test_search.py index da19487fa82..b376cf617ac 100644 --- a/tests/functional/test_search.py +++ b/tests/functional/test_search.py @@ -1,11 +1,8 @@ -import pip.download -from pip.commands.search import (compare_versions, - highest_version, +import pytest +from pip.commands.search import (highest_version, transform_hits, SearchCommand) from pip.status_codes import NO_MATCHES_FOUND, SUCCESS -from pip.backwardcompat import xmlrpclib, b -from mock import Mock from tests.lib import pyversion @@ -20,10 +17,6 @@ def test_version_compare(): Test version comparison. """ - assert compare_versions('1.0', '1.1') == -1 - assert compare_versions('1.1', '1.0') == 1 - assert compare_versions('1.1a1', '1.1') == -1 - assert compare_versions('1.1.1', '1.1a') == -1 assert highest_version(['1.0', '2.0', '0.1']) == '2.0' assert highest_version(['1.0a1', '1.0']) == '1.0' @@ -33,11 +26,40 @@ def test_pypi_xml_transformation(): Test transformation of data structures (pypi xmlrpc to custom list). """ - pypi_hits = [{'_pypi_ordering': 100, 'name': 'foo', 'summary': 'foo summary', 'version': '1.0'}, - {'_pypi_ordering': 200, 'name': 'foo', 'summary': 'foo summary v2', 'version': '2.0'}, - {'_pypi_ordering': 50, 'name': 'bar', 'summary': 'bar summary', 'version': '1.0'}] - expected = [{'score': 200, 'versions': ['1.0', '2.0'], 'name': 'foo', 'summary': 'foo summary v2'}, - {'score': 50, 'versions': ['1.0'], 'name': 'bar', 'summary': 'bar summary'}] + pypi_hits = [ + { + '_pypi_ordering': 100, + 'name': 'foo', + 'summary': 'foo summary', + 'version': '1.0', + }, + { + '_pypi_ordering': 200, + 'name': 'foo', + 'summary': 'foo summary v2', + 'version': '2.0', + }, + { + '_pypi_ordering': 50, + 'name': 'bar', + 'summary': 'bar summary', + 'version': '1.0', + }, + ] + expected = [ + { + 'score': 200, + 'versions': ['1.0', '2.0'], + 'name': 'foo', + 'summary': 'foo summary v2', + }, + { + 'score': 50, + 'versions': ['1.0'], + 'name': 'bar', + 'summary': 'bar summary', + }, + ] assert transform_hits(pypi_hits) == expected @@ -45,30 +67,62 @@ def test_invalid_pypi_transformation(): """ Test transformation of pypi when ordering None """ - pypi_hits = [{'_pypi_ordering': None, 'name': 'bar', 'summary': 'bar summary', 'version': '1.0'}, - {'_pypi_ordering': 100, 'name': 'foo', 'summary': 'foo summary', 'version': '1.0'}] - - expected = [{'score': 100, 'versions': ['1.0'], 'name': 'foo', 'summary': 'foo summary'}, - {'score': 0, 'versions': ['1.0'], 'name': 'bar', 'summary': 'bar summary'}] + pypi_hits = [ + { + '_pypi_ordering': None, + 'name': 'bar', + 'summary': 'bar summary', + 'version': '1.0', + }, + { + '_pypi_ordering': 100, + 'name': 'foo', + 'summary': 'foo summary', + 'version': '1.0', + }, + ] + + expected = [ + { + 'score': 100, + 'versions': ['1.0'], + 'name': 'foo', + 'summary': 'foo summary', + }, + { + 'score': 0, + 'versions': ['1.0'], + 'name': 'bar', + 'summary': 'bar summary', + }, + ] assert transform_hits(pypi_hits) == expected +@pytest.mark.network def test_search(script): """ End to end test of search command. """ output = script.pip('search', 'pip') - assert 'A tool for installing and managing Python packages' in output.stdout + assert ( + 'The PyPA recommended tool for installing ' + 'Python packages.' in output.stdout + ) +@pytest.mark.network def test_multiple_search(script): """ Test searching for multiple packages at once. """ output = script.pip('search', 'pip', 'INITools') - assert 'A tool for installing and managing Python packages' in output.stdout + assert ( + 'The PyPA recommended tool for installing ' + 'Python packages.' in output.stdout + ) assert 'Tools for parsing and using INI-style files' in output.stdout @@ -77,31 +131,34 @@ def test_search_missing_argument(script): Test missing required argument for search """ result = script.pip('search', expect_error=True) - assert 'ERROR: Missing required argument (search query).' in result.stdout + assert 'ERROR: Missing required argument (search query).' in result.stderr -def test_run_method_should_return_sucess_when_find_packages(): +@pytest.mark.network +def test_run_method_should_return_success_when_find_packages(): """ Test SearchCommand.run for found package """ - options_mock = Mock() - options_mock.index = 'http://pypi.python.org/pypi' - search_cmd = SearchCommand() - status = search_cmd.run(options_mock, ('pip',)) + command = SearchCommand() + cmdline = "--index=https://pypi.python.org/pypi pip" + options, args = command.parse_args(cmdline.split()) + status = command.run(options, args) assert status == SUCCESS -def test_run_method_should_return_no_matches_found_when_does_not_find_packages(): +@pytest.mark.network +def test_run_method_should_return_no_matches_found_when_does_not_find_pkgs(): """ Test SearchCommand.run for no matches """ - options_mock = Mock() - options_mock.index = 'https://pypi.python.org/pypi' - search_cmd = SearchCommand() - status = search_cmd.run(options_mock, ('non-existant-package',)) - assert status == NO_MATCHES_FOUND, status + command = SearchCommand() + cmdline = "--index=https://pypi.python.org/pypi nonexistentpackage" + options, args = command.parse_args(cmdline.split()) + status = command.run(options, args) + assert status == NO_MATCHES_FOUND +@pytest.mark.network def test_search_should_exit_status_code_zero_when_find_packages(script): """ Test search exit status code for package found @@ -110,9 +167,10 @@ def test_search_should_exit_status_code_zero_when_find_packages(script): assert result.returncode == SUCCESS +@pytest.mark.network def test_search_exit_status_code_when_finds_no_package(script): """ Test search exit status code for no matches """ - result = script.pip('search', 'non-existant-package', expect_error=True) + result = script.pip('search', 'nonexistentpackage', expect_error=True) assert result.returncode == NO_MATCHES_FOUND, result.returncode diff --git a/tests/functional/test_show.py b/tests/functional/test_show.py index cf68e0404fa..b3edfaa6045 100644 --- a/tests/functional/test_show.py +++ b/tests/functional/test_show.py @@ -1,4 +1,5 @@ import re +import pytest from pip import __version__ from pip.commands.show import search_packages_info @@ -8,38 +9,54 @@ def test_show(script): Test end to end test for show command. """ result = script.pip('show', 'pip') - lines = result.stdout.split('\n') - assert len(lines) == 6 - assert lines[0] == '---', lines[0] - assert lines[1] == 'Name: pip', lines[1] - assert lines[2] == 'Version: %s' % __version__, lines[2] - assert lines[3].startswith('Location: '), lines[3] - assert lines[4] == 'Requires: ' + lines = result.stdout.splitlines() + assert len(lines) == 9 + assert 'Name: pip' in lines + assert 'Version: %s' % __version__ in lines + assert any(line.startswith('Location: ') for line in lines) + assert 'Requires: ' in lines -def test_show_with_files_not_found(script): +def test_show_with_files_not_found(script, data): """ Test for show command with installed files listing enabled and installed-files.txt not found. """ - result = script.pip('show', '-f', 'pip') - lines = result.stdout.split('\n') - assert len(lines) == 8 - assert lines[0] == '---', lines[0] - assert lines[1] == 'Name: pip', lines[1] - assert lines[2] == 'Version: %s' % __version__, lines[2] - assert lines[3].startswith('Location: '), lines[3] - assert lines[4] == 'Requires: ' - assert lines[5] == 'Files:', lines[4] - assert lines[6] == 'Cannot locate installed-files.txt', lines[5] + editable = data.packages.join('SetupPyUTF8') + script.pip('install', '-e', editable) + result = script.pip('show', '-f', 'SetupPyUTF8') + lines = result.stdout.splitlines() + assert len(lines) == 11 + assert 'Name: SetupPyUTF8' in lines + assert 'Version: 0.0.0' in lines + assert any(line.startswith('Location: ') for line in lines) + assert 'Requires: ' in lines + assert 'Files:' in lines + assert 'Cannot locate installed-files.txt' in lines + + +def test_show_with_files_from_wheel(script, data): + """ + Test that a wheel's files can be listed + """ + wheel_file = data.packages.join('simple.dist-0.1-py2.py3-none-any.whl') + script.pip('install', '--no-index', wheel_file) + result = script.pip('show', '-f', 'simple.dist') + lines = result.stdout.splitlines() + assert 'Name: simple.dist' in lines + assert 'Cannot locate installed-files.txt' not in lines[6], lines[6] + assert re.search(r"Files:\n( .+\n)+", result.stdout) +@pytest.mark.network def test_show_with_all_files(script): """ Test listing all files in the show command. """ - result = script.pip('install', 'initools==0.2') + script.pip('install', 'initools==0.2') result = script.pip('show', '--files', 'initools') + lines = result.stdout.splitlines() + assert 'Cannot locate installed-files.txt' not in lines[6], lines[6] assert re.search(r"Files:\n( .+\n)+", result.stdout) @@ -47,14 +64,13 @@ def test_missing_argument(script): """ Test show command with no arguments. """ - result = script.pip('show') - assert 'ERROR: Please provide a package name or names.' in result.stdout + result = script.pip('show', expect_error=True) + assert 'ERROR: Please provide a package name or names.' in result.stderr def test_find_package_not_found(): """ Test trying to get info about a nonexistent package. - """ result = search_packages_info(['abcd3']) assert len(list(result)) == 0 @@ -77,3 +93,69 @@ def test_more_than_one_package(): """ result = list(search_packages_info(['Pip', 'pytest', 'Virtualenv'])) assert len(result) == 3 + + +def test_show_verbose_with_classifiers(script): + """ + Test that classifiers can be listed + """ + result = script.pip('show', 'pip', '--verbose') + lines = result.stdout.splitlines() + assert 'Name: pip' in lines + assert re.search(r"Classifiers:\n( .+\n)+", result.stdout) + assert "Intended Audience :: Developers" in result.stdout + + +def test_show_verbose_installer(script, data): + """ + Test that the installer is shown (this currently needs a wheel install) + """ + wheel_file = data.packages.join('simple.dist-0.1-py2.py3-none-any.whl') + script.pip('install', '--no-index', wheel_file) + result = script.pip('show', '--verbose', 'simple.dist') + lines = result.stdout.splitlines() + assert 'Name: simple.dist' in lines + assert 'Installer: pip' in lines + + +def test_show_verbose(script): + """ + Test end to end test for verbose show command. + """ + result = script.pip('show', '--verbose', 'pip') + lines = result.stdout.splitlines() + assert any(line.startswith('Metadata-Version: ') for line in lines) + assert any(line.startswith('Installer: ') for line in lines) + assert 'Entry-points:' in lines + assert 'Classifiers:' in lines + + +def test_all_fields(script): + """ + Test that all the fields are present + """ + result = script.pip('show', 'pip') + lines = result.stdout.splitlines() + expected = set(['Name', 'Version', 'Summary', 'Home-page', 'Author', + 'Author-email', 'License', 'Location', 'Requires']) + actual = set(re.sub(':.*$', '', line) for line in lines) + assert actual == expected + + +def test_pip_show_is_short(script): + """ + Test that pip show stays short + """ + result = script.pip('show', 'pip') + lines = result.stdout.splitlines() + assert len(lines) <= 10 + + +def test_pip_show_divider(script): + """ + Expect a divider between packages + """ + script.pip('install', 'initools') + result = script.pip('show', 'pip', 'initools') + lines = result.stdout.splitlines() + assert "---" in lines diff --git a/tests/functional/test_uninstall.py b/tests/functional/test_uninstall.py index 3e5add7557d..a41881e9994 100644 --- a/tests/functional/test_uninstall.py +++ b/tests/functional/test_uninstall.py @@ -3,53 +3,98 @@ import textwrap import os import sys -from os.path import join, abspath, normpath +import pytest +import pretend + +from os.path import join, normpath from tempfile import mkdtemp -from mock import patch from tests.lib import assert_all_changes, pyversion from tests.lib.local_repos import local_repo, local_checkout -from pip.util import rmtree +from pip.req import InstallRequirement +from pip.utils import rmtree +@pytest.mark.network def test_simple_uninstall(script): """ Test simple install and uninstall. """ result = script.pip('install', 'INITools==0.2') - assert join(script.site_packages, 'initools') in result.files_created, sorted(result.files_created.keys()) - #the import forces the generation of __pycache__ if the version of python supports it + assert join(script.site_packages, 'initools') in result.files_created, ( + sorted(result.files_created.keys()) + ) + # the import forces the generation of __pycache__ if the version of python + # supports it script.run('python', '-c', "import initools") result2 = script.pip('uninstall', 'INITools', '-y') - assert_all_changes(result, result2, [script.venv/'build', 'cache']) + assert_all_changes(result, result2, [script.venv / 'build', 'cache']) + +def test_simple_uninstall_distutils(script): + """ + Test simple install and uninstall. + """ + script.scratch_path.join("distutils_install").mkdir() + pkg_path = script.scratch_path / 'distutils_install' + pkg_path.join("setup.py").write(textwrap.dedent(""" + from distutils.core import setup + setup( + name='distutils-install', + version='0.1', + ) + """)) + result = script.run('python', pkg_path / 'setup.py', 'install') + result = script.pip('list', '--format=legacy') + assert "distutils-install (0.1)" in result.stdout + script.pip('uninstall', 'distutils_install', '-y', expect_stderr=True) + result2 = script.pip('list', '--format=legacy') + assert "distutils-install (0.1)" not in result2.stdout + + +@pytest.mark.network def test_uninstall_with_scripts(script): """ Uninstall an easy_installed package with scripts. """ result = script.run('easy_install', 'PyLogo', expect_stderr=True) - easy_install_pth = script.site_packages/ 'easy-install.pth' + easy_install_pth = script.site_packages / 'easy-install.pth' pylogo = sys.platform == 'win32' and 'pylogo' or 'PyLogo' assert(pylogo in result.files_updated[easy_install_pth].bytes) result2 = script.pip('uninstall', 'pylogo', '-y') - assert_all_changes(result, result2, [script.venv/'build', 'cache', easy_install_pth]) + assert_all_changes( + result, + result2, + [script.venv / 'build', 'cache', easy_install_pth], + ) +@pytest.mark.network def test_uninstall_easy_install_after_import(script): """ Uninstall an easy_installed package after it's been imported """ result = script.run('easy_install', 'INITools==0.2', expect_stderr=True) - #the import forces the generation of __pycache__ if the version of python supports it + # the import forces the generation of __pycache__ if the version of python + # supports it script.run('python', '-c', "import initools") result2 = script.pip('uninstall', 'INITools', '-y') - assert_all_changes(result, result2, [script.venv/'build', 'cache', script.site_packages/'easy-install.pth']) + assert_all_changes( + result, + result2, + [ + script.venv / 'build', + 'cache', + script.site_packages / 'easy-install.pth', + ] + ) +@pytest.mark.network def test_uninstall_namespace_package(script): """ Uninstall a distribution with a namespace package without clobbering @@ -57,10 +102,16 @@ def test_uninstall_namespace_package(script): """ result = script.pip('install', 'pd.requires==0.0.3', expect_error=True) - assert join(script.site_packages, 'pd') in result.files_created, sorted(result.files_created.keys()) + assert join(script.site_packages, 'pd') in result.files_created, ( + sorted(result.files_created.keys()) + ) result2 = script.pip('uninstall', 'pd.find', '-y', expect_error=True) - assert join(script.site_packages, 'pd') not in result2.files_deleted, sorted(result2.files_deleted.keys()) - assert join(script.site_packages, 'pd', 'find') in result2.files_deleted, sorted(result2.files_deleted.keys()) + assert join(script.site_packages, 'pd') not in result2.files_deleted, ( + sorted(result2.files_deleted.keys()) + ) + assert join(script.site_packages, 'pd', 'find') in result2.files_deleted, ( + sorted(result2.files_deleted.keys()) + ) def test_uninstall_overlapping_package(script, data): @@ -75,33 +126,77 @@ def test_uninstall_overlapping_package(script, data): child_pkg = data.packages.join("child-0.1.tar.gz") result1 = script.pip('install', parent_pkg, expect_error=False) - assert join(script.site_packages, 'parent') in result1.files_created, sorted(result1.files_created.keys()) + assert join(script.site_packages, 'parent') in result1.files_created, ( + sorted(result1.files_created.keys()) + ) result2 = script.pip('install', child_pkg, expect_error=False) - assert join(script.site_packages, 'child') in result2.files_created, sorted(result2.files_created.keys()) - assert normpath(join(script.site_packages, 'parent/plugins/child_plugin.py')) in result2.files_created, sorted(result2.files_created.keys()) - #the import forces the generation of __pycache__ if the version of python supports it + assert join(script.site_packages, 'child') in result2.files_created, ( + sorted(result2.files_created.keys()) + ) + assert normpath( + join(script.site_packages, 'parent/plugins/child_plugin.py') + ) in result2.files_created, sorted(result2.files_created.keys()) + # The import forces the generation of __pycache__ if the version of python + # supports it script.run('python', '-c', "import parent.plugins.child_plugin, child") result3 = script.pip('uninstall', '-y', 'child', expect_error=False) - assert join(script.site_packages, 'child') in result3.files_deleted, sorted(result3.files_created.keys()) - assert normpath(join(script.site_packages, 'parent/plugins/child_plugin.py')) in result3.files_deleted, sorted(result3.files_deleted.keys()) - assert join(script.site_packages, 'parent') not in result3.files_deleted, sorted(result3.files_deleted.keys()) + assert join(script.site_packages, 'child') in result3.files_deleted, ( + sorted(result3.files_created.keys()) + ) + assert normpath( + join(script.site_packages, 'parent/plugins/child_plugin.py') + ) in result3.files_deleted, sorted(result3.files_deleted.keys()) + assert join(script.site_packages, 'parent') not in result3.files_deleted, ( + sorted(result3.files_deleted.keys()) + ) # Additional check: uninstalling 'child' should return things to the # previous state, without unintended side effects. assert_all_changes(result2, result3, []) +def test_uninstall_entry_point(script): + """ + Test uninstall package with two or more entry points in the same section, + whose name contain a colon. + """ + script.scratch_path.join("ep_install").mkdir() + pkg_path = script.scratch_path / 'ep_install' + pkg_path.join("setup.py").write(textwrap.dedent(""" + from setuptools import setup + setup( + name='ep-install', + version='0.1', + entry_points={"pip_test.ep": + ["ep:name1 = distutils_install", + "ep:name2 = distutils_install"] + } + ) + """)) + result = script.pip('install', pkg_path) + result = script.pip('list', '--format=legacy') + assert "ep-install (0.1)" in result.stdout + script.pip('uninstall', 'ep_install', '-y') + result2 = script.pip('list', '--format=legacy') + assert "ep-install (0.1)" not in result2.stdout + + +@pytest.mark.network def test_uninstall_console_scripts(script): """ - Test uninstalling a package with more files (console_script entry points, extra directories). + Test uninstalling a package with more files (console_script entry points, + extra directories). """ args = ['install'] args.append('discover') result = script.pip(*args, **{"expect_error": True}) - assert script.bin/'discover'+script.exe in result.files_created, sorted(result.files_created.keys()) + assert script.bin / 'discover' + script.exe in result.files_created, ( + sorted(result.files_created.keys()) + ) result2 = script.pip('uninstall', 'discover', '-y', expect_error=True) - assert_all_changes(result, result2, [script.venv/'build', 'cache']) + assert_all_changes(result, result2, [script.venv / 'build', 'cache']) +@pytest.mark.network def test_uninstall_easy_installed_console_scripts(script): """ Test uninstalling package with console_scripts that is easy_installed. @@ -109,23 +204,48 @@ def test_uninstall_easy_installed_console_scripts(script): args = ['easy_install'] args.append('discover') result = script.run(*args, **{"expect_stderr": True}) - assert script.bin/'discover'+script.exe in result.files_created, sorted(result.files_created.keys()) + assert script.bin / 'discover' + script.exe in result.files_created, ( + sorted(result.files_created.keys()) + ) result2 = script.pip('uninstall', 'discover', '-y') - assert_all_changes(result, result2, [script.venv/'build', 'cache', script.site_packages/'easy-install.pth']) + assert_all_changes( + result, + result2, + [ + script.venv / 'build', + 'cache', + script.site_packages / 'easy-install.pth', + ] + ) +@pytest.mark.network def test_uninstall_editable_from_svn(script, tmpdir): """ Test uninstalling an editable installation from svn. """ - result = script.pip('install', '-e', '%s#egg=initools-dev' % - local_checkout('svn+http://svn.colorstudy.com/INITools/trunk', tmpdir.join("cache"))) + result = script.pip( + 'install', '-e', + '%s#egg=initools-dev' % local_checkout( + 'svn+http://svn.colorstudy.com/INITools/trunk', + tmpdir.join("cache"), + ), + ) result.assert_installed('INITools') result2 = script.pip('uninstall', '-y', 'initools') - assert (script.venv/'src'/'initools' in result2.files_after), 'oh noes, pip deleted my sources!' - assert_all_changes(result, result2, [script.venv/'src', script.venv/'build', script.site_packages/'easy-install.pth']) + assert (script.venv / 'src' / 'initools' in result2.files_after) + assert_all_changes( + result, + result2, + [ + script.venv / 'src', + script.venv / 'build', + script.site_packages / 'easy-install.pth' + ], + ) +@pytest.mark.network def test_uninstall_editable_with_source_outside_venv(script, tmpdir): """ Test uninstalling editable install from existing source outside the venv. @@ -135,99 +255,147 @@ def test_uninstall_editable_with_source_outside_venv(script, tmpdir): try: temp = mkdtemp() - tmpdir = join(temp, 'virtualenv') - _test_uninstall_editable_with_source_outside_venv(script, tmpdir, cache_dir) + tmpdir = join(temp, 'pip-test-package') + _test_uninstall_editable_with_source_outside_venv( + script, + tmpdir, + cache_dir, + ) finally: rmtree(temp) -def _test_uninstall_editable_with_source_outside_venv(script, tmpdir, cache_dir): - result = script.run('git', 'clone', local_repo('git+git://github.com/pypa/virtualenv', cache_dir), tmpdir, expect_stderr=True) +def _test_uninstall_editable_with_source_outside_venv( + script, tmpdir, cache_dir): + result = script.run( + 'git', 'clone', + local_repo( + 'git+git://github.com/pypa/pip-test-package', + cache_dir, + ), + tmpdir, + expect_stderr=True, + ) result2 = script.pip('install', '-e', tmpdir) - assert (join(script.site_packages, 'virtualenv.egg-link') in result2.files_created), list(result2.files_created.keys()) - result3 = script.pip('uninstall', '-y', 'virtualenv', expect_error=True) - assert_all_changes(result, result3, [script.venv/'build', script.site_packages/'easy-install.pth']) + assert join( + script.site_packages, 'pip-test-package.egg-link' + ) in result2.files_created, list(result2.files_created.keys()) + result3 = script.pip('uninstall', '-y', + 'pip-test-package', expect_error=True) + assert_all_changes( + result, + result3, + [script.venv / 'build', script.site_packages / 'easy-install.pth'], + ) +@pytest.mark.network def test_uninstall_from_reqs_file(script, tmpdir): """ Test uninstall from a requirements file. """ - script.scratch_path.join("test-req.txt").write(textwrap.dedent("""\ - -e %s#egg=initools-dev - # and something else to test out: - PyLogo<0.4 - """ % local_checkout('svn+http://svn.colorstudy.com/INITools/trunk', tmpdir.join("cache")))) + script.scratch_path.join("test-req.txt").write( + textwrap.dedent(""" + -e %s#egg=initools-dev + # and something else to test out: + PyLogo<0.4 + """) % + local_checkout( + 'svn+http://svn.colorstudy.com/INITools/trunk', + tmpdir.join("cache") + ) + ) result = script.pip('install', '-r', 'test-req.txt') - script.scratch_path.join("test-req.txt").write(textwrap.dedent("""\ - # -f, -i, and --extra-index-url should all be ignored by uninstall - -f http://www.example.com - -i http://www.example.com - --extra-index-url http://www.example.com - - -e %s#egg=initools-dev - # and something else to test out: - PyLogo<0.4 - """ % local_checkout('svn+http://svn.colorstudy.com/INITools/trunk', tmpdir.join("cache")))) + script.scratch_path.join("test-req.txt").write( + textwrap.dedent(""" + # -f, -i, and --extra-index-url should all be ignored by uninstall + -f http://www.example.com + -i http://www.example.com + --extra-index-url http://www.example.com + + -e %s#egg=initools-dev + # and something else to test out: + PyLogo<0.4 + """) % + local_checkout( + 'svn+http://svn.colorstudy.com/INITools/trunk', + tmpdir.join("cache") + ) + ) result2 = script.pip('uninstall', '-r', 'test-req.txt', '-y') assert_all_changes( - result, result2, [script.venv/'build', script.venv/'src', script.scratch/'test-req.txt', - script.site_packages/'easy-install.pth']) + result, + result2, + [ + script.venv / 'build', + script.venv / 'src', + script.scratch / 'test-req.txt', + script.site_packages / 'easy-install.pth', + ], + ) def test_uninstall_as_egg(script, data): """ Test uninstall package installed as egg. - """ to_install = data.packages.join("FSPkg") result = script.pip('install', to_install, '--egg', expect_error=False) - fspkg_folder = script.site_packages/'fspkg' - egg_folder = script.site_packages/'FSPkg-0.1dev-py%s.egg' % pyversion + fspkg_folder = script.site_packages / 'fspkg' + egg_folder = script.site_packages / 'FSPkg-0.1.dev0-py%s.egg' % pyversion assert fspkg_folder not in result.files_created, str(result.stdout) assert egg_folder in result.files_created, str(result) - result2 = script.pip('uninstall', 'FSPkg', '-y', expect_error=True) - assert_all_changes(result, result2, [script.venv/'build', 'cache', script.site_packages/'easy-install.pth']) + result2 = script.pip('uninstall', 'FSPkg', '-y') + assert_all_changes( + result, + result2, + [ + script.venv / 'build', + 'cache', + script.site_packages / 'easy-install.pth', + ], + ) -@patch('pip.req.logger') -def test_uninstallpathset_no_paths(mock_logger): +def test_uninstallpathset_no_paths(caplog): """ - Test UninstallPathSet logs notification when there are no paths to uninstall - + Test UninstallPathSet logs notification when there are no paths to + uninstall """ - from pip.req import UninstallPathSet + from pip.req.req_uninstall import UninstallPathSet from pkg_resources import get_distribution test_dist = get_distribution('pip') - # ensure that the distribution is "local" - with patch("pip.req.dist_is_local") as mock_dist_is_local: - mock_dist_is_local.return_value = True - uninstall_set = UninstallPathSet(test_dist) - uninstall_set.remove() #with no files added to set - mock_logger.notify.assert_any_call("Can't uninstall 'pip'. No files were found to uninstall.") + uninstall_set = UninstallPathSet(test_dist) + uninstall_set.remove() # with no files added to set + assert ( + "Can't uninstall 'pip'. No files were found to uninstall." + in caplog.text() + ) -@patch('pip.req.logger') -def test_uninstallpathset_non_local(mock_logger): - """ - Test UninstallPathSet logs notification and returns (with no exception) when dist is non-local - """ - nonlocal_path = os.path.abspath("/nonlocal") - from pip.req import UninstallPathSet - from pkg_resources import get_distribution - test_dist = get_distribution('pip') - test_dist.location = nonlocal_path - # ensure that the distribution is "non-local" - # setting location isn't enough, due to egg-link file checking for - # develop-installs - with patch("pip.req.dist_is_local") as mock_dist_is_local: - mock_dist_is_local.return_value = False - uninstall_set = UninstallPathSet(test_dist) - uninstall_set.remove() #with no files added to set; which is the case when trying to remove non-local dists - mock_logger.notify.assert_any_call("Not uninstalling pip at %s, outside environment %s" % (nonlocal_path, sys.prefix)), mock_logger.notify.mock_calls +def test_uninstall_non_local_distutils(caplog, monkeypatch, tmpdir): + einfo = tmpdir.join("thing-1.0.egg-info") + with open(einfo, "wb"): + pass + + dist = pretend.stub( + key="thing", + project_name="thing", + egg_info=einfo, + location=einfo, + _provider=pretend.stub(), + ) + get_dist = pretend.call_recorder(lambda x: dist) + monkeypatch.setattr("pip._vendor.pkg_resources.get_distribution", get_dist) + + req = InstallRequirement.from_line("thing") + req.uninstall() + + assert os.path.exists(einfo) + def test_uninstall_wheel(script, data): """ @@ -239,3 +407,24 @@ def test_uninstall_wheel(script, data): assert dist_info_folder in result.files_created result2 = script.pip('uninstall', 'simple.dist', '-y') assert_all_changes(result, result2, []) + + +def test_uninstall_setuptools_develop_install(script, data): + """Try uninstall after setup.py develop followed of setup.py install""" + pkg_path = data.packages.join("FSPkg") + script.run('python', 'setup.py', 'develop', + expect_stderr=True, cwd=pkg_path) + script.run('python', 'setup.py', 'install', + expect_stderr=True, cwd=pkg_path) + list_result = script.pip('list', '--format=legacy') + assert "FSPkg (0.1.dev0, " in list_result.stdout + # Uninstall both develop and install + uninstall = script.pip('uninstall', 'FSPkg', '-y') + assert any(filename.endswith('.egg') + for filename in uninstall.files_deleted.keys()) + uninstall2 = script.pip('uninstall', 'FSPkg', '-y') + assert join( + script.site_packages, 'FSPkg.egg-link' + ) in uninstall2.files_deleted, list(uninstall2.files_deleted.keys()) + list_result2 = script.pip('list', '--format=legacy') + assert "FSPkg" not in list_result2.stdout diff --git a/tests/functional/test_uninstall_user.py b/tests/functional/test_uninstall_user.py index 33b41f63460..53606a7b72f 100644 --- a/tests/functional/test_uninstall_user.py +++ b/tests/functional/test_uninstall_user.py @@ -1,22 +1,16 @@ """ tests specific to uninstalling --user installs """ - -import sys -import os -import textwrap -from os.path import abspath, join, curdir, isdir, isfile - import pytest -from pip.locations import bin_user +from os.path import isdir, isfile + from tests.lib import pyversion, assert_all_changes from tests.functional.test_install_user import _patch_dist_in_site_packages -# --user option is broken in pypy -@pytest.mark.skipif("hasattr(sys, 'pypy_version_info')") class Tests_UninstallUserSite: + @pytest.mark.network def test_uninstall_from_usersite(self, script, virtualenv): """ Test uninstall from usersite @@ -24,26 +18,33 @@ def test_uninstall_from_usersite(self, script, virtualenv): virtualenv.system_site_packages = True result1 = script.pip('install', '--user', 'INITools==0.3') result2 = script.pip('uninstall', '-y', 'INITools') - assert_all_changes(result1, result2, [script.venv/'build', 'cache']) - + assert_all_changes(result1, result2, [script.venv / 'build', 'cache']) - def test_uninstall_from_usersite_with_dist_in_global_site(self, script, virtualenv): + def test_uninstall_from_usersite_with_dist_in_global_site( + self, script, virtualenv): """ Test uninstall from usersite (with same dist in global site) """ # the test framework only supports testing using virtualenvs. - # the sys.path ordering for virtualenvs with --system-site-packages is this: virtualenv-site, user-site, global-site. - # this test will use 2 modifications to simulate the user-site/global-site relationship - # 1) a monkey patch which will make it appear piptestpackage is not in the virtualenv site - # if we don't patch this, pip will return an installation error: "Will not install to the usersite because it will lack sys.path precedence..." - # 2) adding usersite to PYTHONPATH, so usersite has sys.path precedence over the virtualenv site + # the sys.path ordering for virtualenvs with --system-site-packages is + # this: virtualenv-site, user-site, global-site. + # this test will use 2 modifications to simulate the + # user-site/global-site relationship + # 1) a monkey patch which will make it appear piptestpackage is not in + # the virtualenv site if we don't patch this, pip will return an + # installation error: "Will not install to the usersite because it + # will lack sys.path precedence..." + # 2) adding usersite to PYTHONPATH, so usersite has sys.path precedence + # over the virtualenv site virtualenv.system_site_packages = True script.environ["PYTHONPATH"] = script.base_path / script.user_site _patch_dist_in_site_packages(script) - result1 = script.pip_install_local('pip-test-package==0.1') - result2 = script.pip_install_local('--user', 'pip-test-package==0.1.1') + script.pip_install_local('pip-test-package==0.1', '--no-binary=:all:') + + result2 = script.pip_install_local( + '--user', 'pip-test-package==0.1.1', '--no-binary=:all:') result3 = script.pip('uninstall', '-vy', 'pip-test-package') # uninstall console is mentioning user scripts, but not global scripts @@ -51,13 +52,15 @@ def test_uninstall_from_usersite_with_dist_in_global_site(self, script, virtuale assert script.bin_path not in result3.stdout # uninstall worked - assert_all_changes(result2, result3, [script.venv/'build', 'cache']) + assert_all_changes(result2, result3, [script.venv / 'build', 'cache']) # site still has 0.2 (can't look in result1; have to check) - egg_info_folder = script.base_path / script.site_packages / 'pip_test_package-0.1-py%s.egg-info' % pyversion + egg_info_folder = ( + script.base_path / script.site_packages / + 'pip_test_package-0.1-py%s.egg-info' % pyversion + ) assert isdir(egg_info_folder) - def test_uninstall_editable_from_usersite(self, script, virtualenv, data): """ Test uninstall editable local user install @@ -65,16 +68,24 @@ def test_uninstall_editable_from_usersite(self, script, virtualenv, data): virtualenv.system_site_packages = True script.user_site_path.makedirs() - #install + # install to_install = data.packages.join("FSPkg") - result1 = script.pip('install', '--user', '-e', to_install, expect_error=False) - egg_link = script.user_site/'FSPkg.egg-link' + result1 = script.pip( + 'install', '--user', '-e', to_install, expect_error=False, + ) + egg_link = script.user_site / 'FSPkg.egg-link' assert egg_link in result1.files_created, str(result1.stdout) - #uninstall + # uninstall result2 = script.pip('uninstall', '-y', 'FSPkg') assert not isfile(script.base_path / egg_link) - assert_all_changes(result1, result2, - [script.venv/'build', 'cache', script.user_site/'easy-install.pth']) - + assert_all_changes( + result1, + result2, + [ + script.venv / 'build', + 'cache', + script.user_site / 'easy-install.pth', + ] + ) diff --git a/tests/functional/test_warning.py b/tests/functional/test_warning.py new file mode 100644 index 00000000000..ba445b95ba1 --- /dev/null +++ b/tests/functional/test_warning.py @@ -0,0 +1,36 @@ +import pytest + +PY26_WARNING = "Python 2.6 is no longer supported" + + +@pytest.mark.skipif("sys.version_info >= (2,7)") +def test_python26_options(script): + result = script.run( + 'python', '-m', 'pip.__main__', 'list', expect_stderr=True, + ) + assert PY26_WARNING in result.stderr + result = script.run('python', '-W', 'ignore', '-m', 'pip.__main__', 'list') + assert result.stderr == '' + + +@pytest.mark.skipif("sys.version_info < (2,7)") +def test_environ(script, tmpdir): + """$PYTHONWARNINGS was added in python2.7""" + demo = tmpdir.join('warnings_demo.py') + demo.write(''' +from pip.utils import deprecation +deprecation.install_warning_logger() + +from logging import basicConfig +basicConfig() + +from warnings import warn +warn("deprecated!", deprecation.PipDeprecationWarning) +''') + + result = script.run('python', demo, expect_stderr=True) + assert result.stderr == 'ERROR:pip.deprecations:DEPRECATION: deprecated!\n' + + script.environ['PYTHONWARNINGS'] = 'ignore' + result = script.run('python', demo) + assert result.stderr == '' diff --git a/tests/functional/test_wheel.py b/tests/functional/test_wheel.py index 1a7bb63bb70..ac08d4280a7 100644 --- a/tests/functional/test_wheel.py +++ b/tests/functional/test_wheel.py @@ -1,37 +1,41 @@ """'pip wheel' tests""" import os -import sys -import textwrap +import pytest from os.path import exists -from pip import wheel -from pip.download import path_to_url as path_to_url_d from pip.locations import write_delete_marker_file from pip.status_codes import PREVIOUS_BUILD_DIR_ERROR -from tests.lib import pyversion, path_to_url +from tests.lib import pyversion def test_pip_wheel_fails_without_wheel(script, data): """ Test 'pip wheel' fails without wheel """ - result = script.pip('wheel', '--no-index', '-f', data.find_links, 'simple==3.0', expect_error=True) - assert "'pip wheel' requires the 'wheel' package" in result.stdout + result = script.pip( + 'wheel', '--no-index', '-f', data.find_links, 'simple==3.0', + expect_error=True, + ) + assert "'pip wheel' requires the 'wheel' package" in result.stderr +@pytest.mark.network def test_pip_wheel_success(script, data): """ Test 'pip wheel' success. """ script.pip('install', 'wheel') - result = script.pip('wheel', '--no-index', '-f', data.find_links, 'simple==3.0') + result = script.pip( + 'wheel', '--no-index', '-f', data.find_links, 'simple==3.0', + ) wheel_file_name = 'simple-3.0-py%s-none-any.whl' % pyversion[0] - wheel_file_path = script.scratch/'wheelhouse'/wheel_file_name + wheel_file_path = script.scratch / wheel_file_name assert wheel_file_path in result.files_created, result.stdout assert "Successfully built simple" in result.stdout, result.stdout +@pytest.mark.network def test_pip_wheel_downloads_wheels(script, data): """ Test 'pip wheel' downloads wheels @@ -41,75 +45,109 @@ def test_pip_wheel_downloads_wheels(script, data): 'wheel', '--no-index', '-f', data.find_links, 'simple.dist', ) wheel_file_name = 'simple.dist-0.1-py2.py3-none-any.whl' - wheel_file_path = script.scratch/'wheelhouse'/wheel_file_name + wheel_file_path = script.scratch / wheel_file_name assert wheel_file_path in result.files_created, result.stdout assert "Saved" in result.stdout, result.stdout -def test_pip_wheel_fail(script, data): +@pytest.mark.network +def test_pip_wheel_builds_when_no_binary_set(script, data): + script.pip('install', 'wheel') + res = script.pip( + 'wheel', '--no-index', '--no-binary', ':all:', '-f', data.find_links, + 'setuptools==0.9.8') + assert "Running setup.py bdist_wheel for setuptools" in str(res), str(res) + + +@pytest.mark.network +def test_pip_wheel_builds_editable_deps(script, data): """ - Test 'pip wheel' failure. + Test 'pip wheel' finds and builds dependencies of editables """ script.pip('install', 'wheel') - result = script.pip('wheel', '--no-index', '-f', data.find_links, 'wheelbroken==0.1') - wheel_file_name = 'wheelbroken-0.1-py%s-none-any.whl' % pyversion[0] - wheel_file_path = script.scratch/'wheelhouse'/wheel_file_name - assert wheel_file_path not in result.files_created, (wheel_file_path, result.files_created) - assert "FakeError" in result.stdout, result.stdout - assert "Failed to build wheelbroken" in result.stdout, result.stdout + editable_path = os.path.join(data.src, 'requires_simple') + result = script.pip( + 'wheel', '--no-index', '-f', data.find_links, '-e', editable_path + ) + wheel_file_name = 'simple-1.0-py%s-none-any.whl' % pyversion[0] + wheel_file_path = script.scratch / wheel_file_name + assert wheel_file_path in result.files_created, result.stdout -def test_pip_wheel_ignore_wheels_editables(script, data): +@pytest.mark.network +def test_pip_wheel_builds_editable(script, data): """ - Test 'pip wheel' ignores editables + Test 'pip wheel' builds an editable package """ script.pip('install', 'wheel') + editable_path = os.path.join(data.src, 'simplewheel-1.0') + result = script.pip( + 'wheel', '--no-index', '-e', editable_path + ) + wheel_file_name = 'simplewheel-1.0-py%s-none-any.whl' % pyversion[0] + wheel_file_path = script.scratch / wheel_file_name + assert wheel_file_path in result.files_created, result.stdout - local_wheel = '%s/simple.dist-0.1-py2.py3-none-any.whl' % data.find_links - local_editable = data.packages.join("FSPkg") - script.scratch_path.join("reqs.txt").write(textwrap.dedent("""\ - %s - -e %s - simple - """ % (local_wheel, local_editable))) - result = script.pip('wheel', '--no-index', '-f', data.find_links, '-r', script.scratch_path / 'reqs.txt') - wheel_file_name = 'simple-3.0-py%s-none-any.whl' % pyversion[0] - wheel_file_path = script.scratch/'wheelhouse'/wheel_file_name - assert wheel_file_path in result.files_created, (wheel_file_path, result.files_created) - assert "Successfully built simple" in result.stdout, result.stdout - assert "Failed to build" not in result.stdout, result.stdout - ignore_editable = "ignoring %s" % path_to_url(local_editable) - #TODO: understand this divergence - if sys.platform == 'win32': - ignore_editable = "ignoring %s" % path_to_url_d(local_editable) - assert ignore_editable in result.stdout, result.stdout + +@pytest.mark.network +def test_pip_wheel_fail(script, data): + """ + Test 'pip wheel' failure. + """ + script.pip('install', 'wheel') + result = script.pip( + 'wheel', '--no-index', '-f', data.find_links, 'wheelbroken==0.1', + expect_error=True, + ) + wheel_file_name = 'wheelbroken-0.1-py%s-none-any.whl' % pyversion[0] + wheel_file_path = script.scratch / wheel_file_name + assert wheel_file_path not in result.files_created, ( + wheel_file_path, + result.files_created, + ) + assert "FakeError" in result.stdout, result.stdout + assert "Failed to build wheelbroken" in result.stdout, result.stdout + assert result.returncode != 0 +@pytest.mark.network def test_no_clean_option_blocks_cleaning_after_wheel(script, data): """ Test --no-clean option blocks cleaning after wheel build """ script.pip('install', 'wheel') - result = script.pip('wheel', '--no-clean', '--no-index', '--find-links=%s' % data.find_links, 'simple') - build = script.venv_path/'build'/'simple' + build = script.venv_path / 'build' + result = script.pip( + 'wheel', '--no-clean', '--no-index', '--build', build, + '--find-links=%s' % data.find_links, 'simple', + ) + build = build / 'simple' assert exists(build), "build/simple should still exist %s" % str(result) +@pytest.mark.network def test_pip_wheel_source_deps(script, data): """ - Test 'pip wheel --use-wheel' finds and builds source archive dependencies of wheels + Test 'pip wheel' finds and builds source archive dependencies + of wheels """ # 'requires_source' is a wheel that depends on the 'source' project script.pip('install', 'wheel') - result = script.pip('wheel', '--use-wheel', '--no-index', '-f', data.find_links, 'requires_source') + result = script.pip( + 'wheel', '--no-index', '-f', data.find_links, 'requires_source', + ) wheel_file_name = 'source-1.0-py%s-none-any.whl' % pyversion[0] - wheel_file_path = script.scratch/'wheelhouse'/wheel_file_name - assert wheel_file_path in result.files_created, result.files_created + wheel_file_path = script.scratch / wheel_file_name + assert wheel_file_path in result.files_created, result.stdout assert "Successfully built source" in result.stdout, result.stdout +@pytest.mark.network def test_pip_wheel_fail_cause_of_previous_build_dir(script, data): - """Test when 'pip wheel' tries to install a package that has a previous build directory""" + """ + Test when 'pip wheel' tries to install a package that has a previous build + directory + """ script.pip('install', 'wheel') @@ -120,7 +158,20 @@ def test_pip_wheel_fail_cause_of_previous_build_dir(script, data): build.join('setup.py').write('#') # When I call pip trying to install things again - result = script.pip('wheel', '--no-index', '--find-links=%s' % data.find_links, 'simple==3.0', expect_error=True) + result = script.pip( + 'wheel', '--no-index', '--find-links=%s' % data.find_links, + '--build', script.venv_path / 'build', + 'simple==3.0', expect_error=True, + ) # Then I see that the error code is the right one - assert result.returncode == PREVIOUS_BUILD_DIR_ERROR + assert result.returncode == PREVIOUS_BUILD_DIR_ERROR, result + + +def test_wheel_package_with_latin1_setup(script, data): + """Create a wheel from a package with latin-1 encoded setup.py.""" + script.pip('install', 'wheel') + + pkg_to_wheel = data.packages.join("SetupPyLatin1") + result = script.pip('wheel', pkg_to_wheel) + assert 'Successfully built SetupPyUTF8' in result.stdout diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index 2f8c993c2e4..67388d97837 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -1,6 +1,6 @@ -# #!/usr/bin/env python from __future__ import absolute_import +from contextlib import contextmanager import os import sys import re @@ -16,7 +16,7 @@ SRC_DIR = Path(__file__).abspath.folder.folder.folder pyversion = sys.version[:3] -pyversion_nodot = "%d%d" % (sys.version_info[0], sys.version_info[1]) +pyversion_tuple = sys.version_info def path_to_url(path): @@ -68,6 +68,14 @@ def packages(self): def packages2(self): return self.root.join("packages2") + @property + def packages3(self): + return self.root.join("packages3") + + @property + def src(self): + return self.root.join("src") + @property def indexes(self): return self.root.join("indexes") @@ -84,6 +92,10 @@ def find_links(self): def find_links2(self): return path_to_url(self.packages2) + @property + def find_links3(self): + return path_to_url(self.packages3) + def index_url(self, index="simple"): return path_to_url(self.root.join("indexes", index)) @@ -130,14 +142,17 @@ def __str__(self): def assert_installed(self, pkg_name, editable=True, with_files=[], without_files=[], without_egg_link=False, - use_user_site=False): + use_user_site=False, sub_dir=False): e = self.test_env if editable: - pkg_dir = e.venv/'src'/pkg_name.lower() + pkg_dir = e.venv / 'src' / pkg_name.lower() + # If package was installed in a sub directory + if sub_dir: + pkg_dir = pkg_dir / sub_dir else: without_egg_link = True - pkg_dir = e.site_packages/pkg_name + pkg_dir = e.site_packages / pkg_name if use_user_site: egg_link_path = e.user_site / pkg_name + '.egg-link' @@ -146,32 +161,37 @@ def assert_installed(self, pkg_name, editable=True, with_files=[], if without_egg_link: if egg_link_path in self.files_created: - raise TestFailure('unexpected egg link file created: '\ - '%r\n%s' % (egg_link_path, self)) + raise TestFailure( + 'unexpected egg link file created: %r\n%s' % + (egg_link_path, self) + ) else: - if not egg_link_path in self.files_created: - raise TestFailure('expected egg link file missing: '\ - '%r\n%s' % (egg_link_path, self)) + if egg_link_path not in self.files_created: + raise TestFailure( + 'expected egg link file missing: %r\n%s' % + (egg_link_path, self) + ) egg_link_file = self.files_created[egg_link_path] - if not (# FIXME: I don't understand why there's a trailing . here - egg_link_file.bytes.endswith('.') - and egg_link_file.bytes[:-1].strip().endswith(pkg_dir)): + # FIXME: I don't understand why there's a trailing . here + if not (egg_link_file.bytes.endswith('\n.') and + egg_link_file.bytes[:-2].endswith(pkg_dir)): raise TestFailure(textwrap.dedent(u('''\ - Incorrect egg_link file %r - Expected ending: %r - ------- Actual contents ------- - %s - -------------------------------''' % ( - egg_link_file, - pkg_dir + u('\n.'), - egg_link_file.bytes)))) + Incorrect egg_link file %r + Expected ending: %r + ------- Actual contents ------- + %s + -------------------------------''' % ( + egg_link_file, + pkg_dir + '\n.', + repr(egg_link_file.bytes)) + ))) if use_user_site: - pth_file = e.user_site/'easy-install.pth' + pth_file = e.user_site / 'easy-install.pth' else: - pth_file = e.site_packages/'easy-install.pth' + pth_file = e.site_packages / 'easy-install.pth' if (pth_file in self.files_updated) == without_egg_link: raise TestFailure('%r unexpectedly %supdated by install' % ( @@ -188,14 +208,18 @@ def assert_installed(self, pkg_name, editable=True, with_files=[], sorted(self.files_created.keys()))) for f in with_files: - if not (pkg_dir/f).normpath in self.files_created: - raise TestFailure('Package directory %r missing '\ - 'expected content %r' % (pkg_dir, f)) + if not (pkg_dir / f).normpath in self.files_created: + raise TestFailure( + 'Package directory %r missing expected content %r' % + (pkg_dir, f) + ) for f in without_files: - if (pkg_dir/f).normpath in self.files_created: - raise TestFailure('Package directory %r has '\ - 'unexpected content %f' % (pkg_dir, f)) + if (pkg_dir / f).normpath in self.files_created: + raise TestFailure( + 'Package directory %r has unexpected content %f' % + (pkg_dir, f) + ) class PipTestEnvironment(scripttest.TestFileEnvironment): @@ -221,7 +245,12 @@ def __init__(self, base_path, *args, **kwargs): # Store paths related to the virtual environment _virtualenv = kwargs.pop("virtualenv") - venv, lib, include, bin = virtualenv.path_locations(_virtualenv) + path_locations = virtualenv.path_locations(_virtualenv) + # Make sure we have test.lib.path.Path objects + venv, lib, include, bin = map(Path, path_locations) + # workaround for https://github.com/pypa/virtualenv/issues/306 + if hasattr(sys, "pypy_version_info"): + lib = os.path.join(venv, 'lib-python', pyversion) self.venv_path = venv self.lib_path = lib self.include_path = include @@ -233,7 +262,9 @@ def __init__(self, base_path, *args, **kwargs): self.site_packages_path = self.lib_path.join("site-packages") self.user_base_path = self.venv_path.join("user") - self.user_bin_path = self.user_base_path.join(self.bin_path - self.venv_path) + self.user_bin_path = self.user_base_path.join( + self.bin_path - self.venv_path + ) self.user_site_path = self.venv_path.join( "user", site.USER_SITE[len(site.USER_BASE) + 1:], @@ -250,7 +281,6 @@ def __init__(self, base_path, *args, **kwargs): if environ is None: environ = os.environ.copy() - environ["PIP_LOG_FILE"] = base_path.join("pip-log.txt") environ["PATH"] = Path.pathsep.join( [self.bin_path] + [environ.get("PATH", [])], ) @@ -268,6 +298,8 @@ def __init__(self, base_path, *args, **kwargs): real_name = "%s_path" % name setattr(self, name, getattr(self, real_name) - self.base_path) + # Make sure temp_path is a Path object + self.temp_path = Path(self.temp_path) # Ensure the tmp dir exists, things break horribly if it doesn't self.temp_path.mkdir() @@ -290,9 +322,22 @@ def run(self, *args, **kw): run_from = kw.pop('run_from', None) assert not cwd or not run_from, "Don't use run_from; it's going away" cwd = cwd or run_from or self.cwd - return TestPipResult(super(PipTestEnvironment, self).run(cwd=cwd, *args, **kw), verbose=self.verbose) + return TestPipResult( + super(PipTestEnvironment, self).run(cwd=cwd, *args, **kw), + verbose=self.verbose, + ) def pip(self, *args, **kwargs): + # On old versions of Python, urllib3/requests will raise a warning + # about the lack of an SSLContext. Expect it when running commands + # that will touch the outside world. + if (pyversion_tuple < (2, 7, 9) and + args and args[0] in ('search', 'install', 'download')): + kwargs['expect_stderr'] = True + # Python 2.6 is deprecated and we emit a warning on it. + if pyversion_tuple[:2] == (2, 6): + kwargs['expect_stderr'] = True + return self.run("pip", *args, **kwargs) def pip_install_local(self, *args, **kwargs): @@ -381,21 +426,23 @@ def assert_all_changes(start_state, end_state, expected_changes): # Don't throw away this potentially useful information return diff + def _create_test_package_with_subdirectory(script, subdirectory): script.scratch_path.join("version_pkg").mkdir() - version_pkg_path = script.scratch_path/'version_pkg' + version_pkg_path = script.scratch_path / 'version_pkg' version_pkg_path.join("version_pkg.py").write(textwrap.dedent(""" def main(): print('0.1') """)) - version_pkg_path.join("setup.py").write(textwrap.dedent(""" - from setuptools import setup, find_packages - setup(name='version_pkg', - version='0.1', - packages=find_packages(), - py_modules=['version_pkg'], - entry_points=dict(console_scripts=['version_pkg=version_pkg:main'])) - """)) + version_pkg_path.join("setup.py").write( + textwrap.dedent(""" + from setuptools import setup, find_packages + setup(name='version_pkg', + version='0.1', + packages=find_packages(), + py_modules=['version_pkg'], + entry_points=dict(console_scripts=['version_pkg=version_pkg:main'])) + """)) subdirectory_path = version_pkg_path.join(subdirectory) subdirectory_path.mkdir() @@ -404,60 +451,145 @@ def main(): print('0.1') """)) - subdirectory_path.join('setup.py').write(textwrap.dedent(""" - from setuptools import setup, find_packages - setup(name='version_subpkg', - version='0.1', - packages=find_packages(), - py_modules=['version_subpkg'], - entry_points=dict(console_scripts=['version_pkg=version_subpkg:main'])) - """)) + subdirectory_path.join('setup.py').write( + textwrap.dedent(""" +from setuptools import setup, find_packages +setup(name='version_subpkg', + version='0.1', + packages=find_packages(), + py_modules=['version_subpkg'], + entry_points=dict(console_scripts=['version_pkg=version_subpkg:main'])) + """)) script.run('git', 'init', cwd=version_pkg_path) script.run('git', 'add', '.', cwd=version_pkg_path) - script.run('git', 'commit', '-q', - '--author', 'Pip ', - '-am', 'initial version', cwd=version_pkg_path) + script.run( + 'git', 'commit', '-q', + '--author', 'pip ', + '-am', 'initial version', cwd=version_pkg_path + ) return version_pkg_path -def _create_test_package(script): - script.scratch_path.join("version_pkg").mkdir() - version_pkg_path = script.scratch_path/'version_pkg' - version_pkg_path.join("version_pkg.py").write(textwrap.dedent(""" + +def _create_test_package_with_srcdir(script, name='version_pkg', vcs='git'): + script.scratch_path.join(name).mkdir() + version_pkg_path = script.scratch_path / name + subdir_path = version_pkg_path.join('subdir') + subdir_path.mkdir() + src_path = subdir_path.join('src') + src_path.mkdir() + pkg_path = src_path.join('pkg') + pkg_path.mkdir() + pkg_path.join('__init__.py').write('') + subdir_path.join("setup.py").write(textwrap.dedent(""" + from setuptools import setup, find_packages + setup( + name='{name}', + version='0.1', + packages=find_packages(), + package_dir={{'': 'src'}}, + ) + """.format(name=name))) + return _vcs_add(script, version_pkg_path, vcs) + + +def _create_test_package(script, name='version_pkg', vcs='git'): + script.scratch_path.join(name).mkdir() + version_pkg_path = script.scratch_path / name + version_pkg_path.join("%s.py" % name).write(textwrap.dedent(""" def main(): print('0.1') """)) version_pkg_path.join("setup.py").write(textwrap.dedent(""" from setuptools import setup, find_packages setup( - name='version_pkg', + name='{name}', version='0.1', packages=find_packages(), - py_modules=['version_pkg'], - entry_points=dict(console_scripts=['version_pkg=version_pkg:main']) + py_modules=['{name}'], + entry_points=dict(console_scripts=['{name}={name}:main']) ) - """)) - script.run('git', 'init', cwd=version_pkg_path) - script.run('git', 'add', '.', cwd=version_pkg_path) - script.run('git', 'commit', '-q', - '--author', 'Pip ', - '-am', 'initial version', cwd=version_pkg_path) + """.format(name=name))) + return _vcs_add(script, version_pkg_path, vcs) + + +def _vcs_add(script, version_pkg_path, vcs='git'): + if vcs == 'git': + script.run('git', 'init', cwd=version_pkg_path) + script.run('git', 'add', '.', cwd=version_pkg_path) + script.run( + 'git', 'commit', '-q', + '--author', 'pip ', + '-am', 'initial version', cwd=version_pkg_path, + ) + elif vcs == 'hg': + script.run('hg', 'init', cwd=version_pkg_path) + script.run('hg', 'add', '.', cwd=version_pkg_path) + script.run( + 'hg', 'commit', '-q', + '--user', 'pip ', + '-m', 'initial version', cwd=version_pkg_path, + ) + elif vcs == 'svn': + repo_url = _create_svn_repo(script, version_pkg_path) + script.run( + 'svn', 'checkout', repo_url, 'pip-test-package', + cwd=script.scratch_path + ) + checkout_path = script.scratch_path / 'pip-test-package' + + # svn internally stores windows drives as uppercase; we'll match that. + checkout_path = checkout_path.replace('c:', 'C:') + + version_pkg_path = checkout_path + elif vcs == 'bazaar': + script.run('bzr', 'init', cwd=version_pkg_path) + script.run('bzr', 'add', '.', cwd=version_pkg_path) + script.run( + 'bzr', 'whoami', 'pip ', + cwd=version_pkg_path) + script.run( + 'bzr', 'commit', '-q', + '--author', 'pip ', + '-m', 'initial version', cwd=version_pkg_path, + ) + else: + raise ValueError('Unknown vcs: %r' % vcs) return version_pkg_path +def _create_svn_repo(script, version_pkg_path): + repo_url = path_to_url( + script.scratch_path / 'pip-test-package-repo' / 'trunk') + script.run( + 'svnadmin', 'create', 'pip-test-package-repo', + cwd=script.scratch_path + ) + script.run( + 'svn', 'import', version_pkg_path, repo_url, + '-m', 'Initial import of pip-test-package', + cwd=script.scratch_path + ) + return repo_url + + def _change_test_package_version(script, version_pkg_path): version_pkg_path.join("version_pkg.py").write(textwrap.dedent('''\ def main(): print("some different version")''')) - script.run('git', 'clean', '-qfdx', + script.run( + 'git', 'clean', '-qfdx', + cwd=version_pkg_path, + expect_stderr=True, + ) + script.run( + 'git', 'commit', '-q', + '--author', 'pip ', + '-am', 'messed version', cwd=version_pkg_path, expect_stderr=True, ) - script.run('git', 'commit', '-q', - '--author', 'Pip ', - '-am', 'messed version', - cwd=version_pkg_path, expect_stderr=True) def assert_raises_regexp(exception, reg, run, *args, **kwargs): @@ -467,7 +599,22 @@ def assert_raises_regexp(exception, reg, run, *args, **kwargs): try: run(*args, **kwargs) assert False, "%s should have been thrown" % exception - except Exception: + except exception: e = sys.exc_info()[1] p = re.compile(reg) assert p.search(str(e)), str(e) + + +@contextmanager +def requirements_file(contents, tmpdir): + """Return a Path to a requirements file of given contents. + + As long as the context manager is open, the requirements file will exist. + + :param tmpdir: A Path to the folder in which to create the file + + """ + path = tmpdir / 'reqs.txt' + path.write(contents) + yield path + path.remove() diff --git a/tests/lib/git_submodule_helpers.py b/tests/lib/git_submodule_helpers.py index dd4249e7b51..d5ec20045aa 100644 --- a/tests/lib/git_submodule_helpers.py +++ b/tests/lib/git_submodule_helpers.py @@ -5,34 +5,44 @@ def _create_test_package_submodule(env): env.scratch_path.join("version_pkg_submodule").mkdir() - submodule_path = env.scratch_path/'version_pkg_submodule' + submodule_path = env.scratch_path / 'version_pkg_submodule' env.run('touch', 'testfile', cwd=submodule_path) env.run('git', 'init', cwd=submodule_path) env.run('git', 'add', '.', cwd=submodule_path) env.run('git', 'commit', '-q', - '--author', 'Pip ', + '--author', 'pip ', '-am', 'initial version / submodule', cwd=submodule_path) return submodule_path + def _change_test_package_submodule(env, submodule_path): submodule_path.join("testfile").write("this is a changed file") submodule_path.join("testfile2").write("this is an added file") env.run('git', 'add', '.', cwd=submodule_path) env.run('git', 'commit', '-q', - '--author', 'Pip ', + '--author', 'pip ', '-am', 'submodule change', cwd=submodule_path) + def _pull_in_submodule_changes_to_module(env, module_path): - env.run(cwd=module_path/'testpkg/static/', *('git pull -q origin master'.split(' '))) + env.run( + 'git', + 'pull', + '-q', + 'origin', + 'master', + cwd=module_path / 'testpkg/static/', + ) env.run('git', 'commit', '-q', - '--author', 'Pip ', + '--author', 'pip ', '-am', 'submodule change', cwd=module_path) + def _create_test_package_with_submodule(env): env.scratch_path.join("version_pkg").mkdir() - version_pkg_path = env.scratch_path/'version_pkg' + version_pkg_path = env.scratch_path / 'version_pkg' version_pkg_path.join("testpkg").mkdir() - pkg_path = version_pkg_path/'testpkg' + pkg_path = version_pkg_path / 'testpkg' pkg_path.join("__init__.py").write("# hello there") pkg_path.join("version_pkg.py").write(textwrap.dedent('''\ @@ -49,19 +59,24 @@ def main(): env.run('git', 'init', cwd=version_pkg_path, expect_error=True) env.run('git', 'add', '.', cwd=version_pkg_path, expect_error=True) env.run('git', 'commit', '-q', - '--author', 'Pip ', + '--author', 'pip ', '-am', 'initial version', cwd=version_pkg_path, expect_error=True) - submodule_path = _create_test_package_submodule(env) - env.run('git', 'submodule', 'add', submodule_path, 'testpkg/static', cwd=version_pkg_path, - expect_error=True) + env.run( + 'git', + 'submodule', + 'add', + submodule_path, + 'testpkg/static', + cwd=version_pkg_path, + expect_error=True, + ) env.run('git', 'commit', '-q', - '--author', 'Pip ', + '--author', 'pip ', '-am', 'initial version w submodule', cwd=version_pkg_path, expect_error=True) - return version_pkg_path, submodule_path diff --git a/tests/lib/local_repos.py b/tests/lib/local_repos.py index 8f56b94424f..059a4c58803 100644 --- a/tests/lib/local_repos.py +++ b/tests/lib/local_repos.py @@ -2,8 +2,10 @@ import os import subprocess + +from pip._vendor.six.moves.urllib import request as urllib_request + from pip.vcs import subversion, git, bazaar, mercurial -from pip.backwardcompat import urlretrieve from tests.lib import path_to_url @@ -18,11 +20,18 @@ def _create_initools_repository(directory): def _dump_initools_repository(directory): - filename, _ = urlretrieve('http://bitbucket.org/hltbra/pip-initools-dump/raw/8b55c908a320/INITools_modified.dump') + filename, _ = urllib_request.urlretrieve( + 'http://bitbucket.org/hltbra/pip-initools-dump/raw/8b55c908a320/' + 'INITools_modified.dump' + ) initools_folder = os.path.join(directory, 'INITools') devnull = open(os.devnull, 'w') dump = open(filename) - subprocess_call(['svnadmin', 'load', initools_folder], stdin=dump, stdout=devnull) + subprocess_call( + ['svnadmin', 'load', initools_folder], + stdin=dump, + stdout=devnull, + ) dump.close() devnull.close() os.remove(filename) @@ -47,20 +56,26 @@ def _get_vcs_and_checkout_url(remote_repository, directory): branch = '' if vcs == 'svn': branch = os.path.basename(remote_repository) - repository_name = os.path.basename(remote_repository[:-len(branch)-1]) # remove the slash + # remove the slash + repository_name = os.path.basename( + remote_repository[:-len(branch) - 1] + ) else: repository_name = os.path.basename(remote_repository) destination_path = os.path.join(directory, repository_name) if not os.path.exists(destination_path): vcs_class(remote_repository).obtain(destination_path) - return '%s+%s' % (vcs, path_to_url('/'.join([directory, repository_name, branch]))) + return '%s+%s' % ( + vcs, + path_to_url('/'.join([directory, repository_name, branch])), + ) def local_checkout(remote_repo, directory): if not os.path.exists(directory): os.mkdir(directory) - #os.makedirs(directory) + # os.makedirs(directory) if remote_repo.startswith('svn'): _create_svn_repository_for_initools(directory) diff --git a/tests/lib/path.py b/tests/lib/path.py index 3bd608ddf97..2a3890b4c0d 100644 --- a/tests/lib/path.py +++ b/tests/lib/path.py @@ -1,3 +1,4 @@ +# flake8: noqa # -*- coding: utf-8 -*- # Author: Aziz Köksal from __future__ import absolute_import diff --git a/tests/lib/scripttest.py b/tests/lib/scripttest.py index e8223a92feb..55e05806273 100644 --- a/tests/lib/scripttest.py +++ b/tests/lib/scripttest.py @@ -1,3 +1,3 @@ from __future__ import absolute_import -from . import PipTestEnvironment +from . import PipTestEnvironment # noqa diff --git a/tests/lib/test_lib.py b/tests/lib/test_lib.py index 5c49ed90d75..065940276e9 100644 --- a/tests/lib/test_lib.py +++ b/tests/lib/test_lib.py @@ -6,15 +6,16 @@ from os.path import join, isdir from tests.lib import SRC_DIR -from tests.lib.path import Path def test_tmp_dir_exists_in_env(script): """ - Test that $TMPDIR == env.temp_path and path exists and env.assert_no_temp() passes (in fast env) + Test that $TMPDIR == env.temp_path and path exists and env.assert_no_temp() + passes (in fast env) """ - #need these tests to ensure the assert_no_temp feature of scripttest is working - script.assert_no_temp() #this fails if env.tmp_path doesn't exist + # need these tests to ensure the assert_no_temp feature of scripttest is + # working + script.assert_no_temp() # this fails if env.tmp_path doesn't exist assert script.environ['TMPDIR'] == script.temp_path assert isdir(script.temp_path) @@ -27,9 +28,13 @@ def test_correct_pip_version(script): # pip PIPVERSION from PIPDIRECTORY (python PYVERSION) result = script.pip('--version') - # compare the directory tree of the invoked pip with that of this source distribution - dir = re.match(r'pip \d(\.[\d])+(\.?(rc|dev|pre|post)\d+)? from (.*) \(python \d(.[\d])+\)$', - result.stdout).group(4) + # compare the directory tree of the invoked pip with that of this source + # distribution + dir = re.match( + r'pip \d(\.[\d])+(\.?(rc|dev|pre|post)\d+)? from (.*) ' + r'\(python \d(.[\d])+\)$', + result.stdout + ).group(4) pip_folder = join(SRC_DIR, 'pip') pip_folder_outputed = join(dir, 'pip') @@ -39,5 +44,19 @@ def test_correct_pip_version(script): # is picking up some other version! N.B. if this project acquires # primary resources other than .py files, this code will need # maintenance - mismatch_py = [x for x in diffs.left_only + diffs.right_only + diffs.diff_files if x.endswith('.py')] - assert not mismatch_py, 'mismatched source files in %r and %r: %r'% (pip_folder, pip_folder_outputed, mismatch_py) + mismatch_py = [ + x for x in diffs.left_only + diffs.right_only + diffs.diff_files + if x.endswith('.py') + ] + assert not mismatch_py, ( + 'mismatched source files in %r and %r: %r' % + (pip_folder, pip_folder_outputed, mismatch_py) + ) + + +def test_as_import(script): + """ test that pip.__init__.py does not shadow + the command submodule with a dictionary + """ + import pip.commands.install as inst + assert inst is not None diff --git a/tests/lib/venv.py b/tests/lib/venv.py index f8a1d96c19b..73a9f7f5dec 100644 --- a/tests/lib/venv.py +++ b/tests/lib/venv.py @@ -1,6 +1,7 @@ from __future__ import absolute_import import os +import sys import subprocess import virtualenv as _virtualenv @@ -27,13 +28,16 @@ def __init__(self, location, *args, **kwargs): self._system_site_packages = kwargs.pop("system_site_packages", False) home, lib, inc, bin = _virtualenv.path_locations(self.location) + # workaround for https://github.com/pypa/virtualenv/issues/306 + if hasattr(sys, "pypy_version_info"): + lib = os.path.join(home, 'lib-python', sys.version[:3]) self.lib = Path(lib) self.bin = Path(bin) super(VirtualEnvironment, self).__init__(*args, **kwargs) def __repr__(self): - return "".format(self.location) + return "".format(self.location) @classmethod def create(cls, location, clear=False, pip_source_dir=None): @@ -46,13 +50,14 @@ def _create(self, clear=False): _virtualenv.create_environment( self.location, clear=clear, - never_download=True, + download=False, no_pip=True, + no_wheel=True, ) # Install our development version of pip install the virtual # environment - cmd = [self.bin.join("python"), "setup.py", "develop"] + cmd = [self.bin.join("python"), "setup.py", "install", "--no-compile"] p = subprocess.Popen( cmd, cwd=self.pip_source_dir, @@ -61,7 +66,9 @@ def _create(self, clear=False): ) p.communicate() if p.returncode != 0: - raise subprocess.CalledProcessError(p.returncode, cmd[0], + raise subprocess.CalledProcessError( + p.returncode, + cmd, output=p.stdout, ) diff --git a/tests/scripts/test_all_pip.py b/tests/scripts/test_all_pip.py index ee4869925d6..95fc8723c53 100644 --- a/tests/scripts/test_all_pip.py +++ b/tests/scripts/test_all_pip.py @@ -2,11 +2,11 @@ import re import sys import subprocess -import shutil from os.path import dirname, abspath -from pip.backwardcompat import urllib -from pip.util import rmtree +from pip._vendor.six.moves.urllib import request as urllib_request + +from pip.utils import rmtree src_folder = dirname(dirname(abspath(__file__))) @@ -16,8 +16,9 @@ else: bin_dir = 'bin' + def all_projects(): - data = urllib.urlopen('http://pypi.python.org/simple/').read() + data = urllib_request.urlopen('http://pypi.python.org/simple/').read() projects = [m.group(1) for m in re.finditer(r'(.+)', data)] return projects @@ -54,23 +55,37 @@ def _test_packages(output, pending_fn): print('Creating virtualenv in %s' % dest_dir) create_venv(dest_dir) print('Uninstalling actual pip') - code = subprocess.check_call([os.path.join(dest_dir, bin_dir, 'pip'), - 'uninstall', '-y', 'pip']) + code = subprocess.check_call([ + os.path.join(dest_dir, bin_dir, 'pip'), + 'uninstall', + '-y', + 'pip', + ]) assert not code, 'pip uninstallation failed' print('Installing development pip') - code = subprocess.check_call([os.path.join(dest_dir, bin_dir, 'python'), - 'setup.py', 'install'], - cwd=src_folder) + code = subprocess.check_call( + [ + os.path.join(dest_dir, bin_dir, 'python'), + 'setup.py', + 'install' + ], + cwd=src_folder, + ) assert not code, 'pip installation failed' print('Trying installation of %s' % dest_dir) - code = subprocess.check_call([os.path.join(dest_dir, bin_dir, 'pip'), - 'install', package]) + code = subprocess.check_call([ + os.path.join(dest_dir, bin_dir, 'pip'), + 'install', + package, + ]) if code: print('Installation of %s failed' % package) print('Now checking easy_install...') create_venv(dest_dir) - code = subprocess.check_call([os.path.join(dest_dir, bin_dir, 'easy_install'), - package]) + code = subprocess.check_call([ + os.path.join(dest_dir, bin_dir, 'easy_install'), + package, + ]) if code: print('easy_install also failed') add_package(os.path.join(output, 'easy-failure.txt'), package) @@ -89,7 +104,11 @@ def create_venv(dest_dir): if os.path.exists(dest_dir): rmtree(dest_dir) print('Creating virtualenv in %s' % dest_dir) - code = subprocess.check_call(['virtualenv', '--no-site-packages', dest_dir]) + code = subprocess.check_call([ + 'virtualenv', + '--no-site-packages', + dest_dir, + ]) assert not code, "virtualenv failed" diff --git a/tests/unit/test_appdirs.py b/tests/unit/test_appdirs.py new file mode 100644 index 00000000000..fe44c1377ee --- /dev/null +++ b/tests/unit/test_appdirs.py @@ -0,0 +1,275 @@ +import ntpath +import os +import posixpath +import sys + +import pretend + +from pip.utils import appdirs + + +class TestUserCacheDir: + + def test_user_cache_dir_win(self, monkeypatch): + @pretend.call_recorder + def _get_win_folder(base): + return "C:\\Users\\test\\AppData\\Local" + + monkeypatch.setattr( + appdirs, + "_get_win_folder", + _get_win_folder, + raising=False, + ) + monkeypatch.setattr(appdirs, "WINDOWS", True) + monkeypatch.setattr(os, "path", ntpath) + + assert (appdirs.user_cache_dir("pip") == + "C:\\Users\\test\\AppData\\Local\\pip\\Cache") + assert _get_win_folder.calls == [pretend.call("CSIDL_LOCAL_APPDATA")] + + def test_user_cache_dir_osx(self, monkeypatch): + monkeypatch.setattr(appdirs, "WINDOWS", False) + monkeypatch.setattr(os, "path", posixpath) + monkeypatch.setenv("HOME", "/home/test") + monkeypatch.setattr(sys, "platform", "darwin") + + assert appdirs.user_cache_dir("pip") == "/home/test/Library/Caches/pip" + + def test_user_cache_dir_linux(self, monkeypatch): + monkeypatch.setattr(appdirs, "WINDOWS", False) + monkeypatch.setattr(os, "path", posixpath) + monkeypatch.delenv("XDG_CACHE_HOME") + monkeypatch.setenv("HOME", "/home/test") + monkeypatch.setattr(sys, "platform", "linux2") + + assert appdirs.user_cache_dir("pip") == "/home/test/.cache/pip" + + def test_user_cache_dir_linux_override(self, monkeypatch): + monkeypatch.setattr(appdirs, "WINDOWS", False) + monkeypatch.setattr(os, "path", posixpath) + monkeypatch.setenv("XDG_CACHE_HOME", "/home/test/.other-cache") + monkeypatch.setenv("HOME", "/home/test") + monkeypatch.setattr(sys, "platform", "linux2") + + assert appdirs.user_cache_dir("pip") == "/home/test/.other-cache/pip" + + def test_user_cache_dir_linux_home_slash(self, monkeypatch): + monkeypatch.setattr(appdirs, "WINDOWS", False) + monkeypatch.setattr(os, "path", posixpath) + # Verify that we are not affected by http://bugs.python.org/issue14768 + monkeypatch.delenv("XDG_CACHE_HOME") + monkeypatch.setenv("HOME", "/") + monkeypatch.setattr(sys, "platform", "linux2") + + assert appdirs.user_cache_dir("pip") == "/.cache/pip" + + +class TestSiteConfigDirs: + + def test_site_config_dirs_win(self, monkeypatch): + @pretend.call_recorder + def _get_win_folder(base): + return "C:\\ProgramData" + + monkeypatch.setattr( + appdirs, + "_get_win_folder", + _get_win_folder, + raising=False, + ) + monkeypatch.setattr(appdirs, "WINDOWS", True) + monkeypatch.setattr(os, "path", ntpath) + + assert appdirs.site_config_dirs("pip") == ["C:\\ProgramData\\pip"] + assert _get_win_folder.calls == [pretend.call("CSIDL_COMMON_APPDATA")] + + def test_site_config_dirs_osx(self, monkeypatch): + monkeypatch.setattr(appdirs, "WINDOWS", False) + monkeypatch.setattr(os, "path", posixpath) + monkeypatch.setenv("HOME", "/home/test") + monkeypatch.setattr(sys, "platform", "darwin") + + assert appdirs.site_config_dirs("pip") == \ + ["/Library/Application Support/pip"] + + def test_site_config_dirs_linux(self, monkeypatch): + monkeypatch.setattr(appdirs, "WINDOWS", False) + monkeypatch.setattr(os, "path", posixpath) + monkeypatch.delenv("XDG_CONFIG_DIRS") + monkeypatch.setattr(sys, "platform", "linux2") + + assert appdirs.site_config_dirs("pip") == [ + '/etc/xdg/pip', + '/etc' + ] + + def test_site_config_dirs_linux_override(self, monkeypatch): + monkeypatch.setattr(appdirs, "WINDOWS", False) + monkeypatch.setattr(os, "path", posixpath) + monkeypatch.setattr(os, "pathsep", ':') + monkeypatch.setenv("XDG_CONFIG_DIRS", "/spam:/etc:/etc/xdg") + monkeypatch.setattr(sys, "platform", "linux2") + + assert appdirs.site_config_dirs("pip") == [ + '/spam/pip', + '/etc/pip', + '/etc/xdg/pip', + '/etc' + ] + + +class TestUserDataDir: + + def test_user_data_dir_win_no_roaming(self, monkeypatch): + @pretend.call_recorder + def _get_win_folder(base): + return "C:\\Users\\test\\AppData\\Local" + + monkeypatch.setattr( + appdirs, + "_get_win_folder", + _get_win_folder, + raising=False, + ) + monkeypatch.setattr(appdirs, "WINDOWS", True) + monkeypatch.setattr(os, "path", ntpath) + + assert (appdirs.user_data_dir("pip") == + "C:\\Users\\test\\AppData\\Local\\pip") + assert _get_win_folder.calls == [pretend.call("CSIDL_LOCAL_APPDATA")] + + def test_user_data_dir_win_yes_roaming(self, monkeypatch): + @pretend.call_recorder + def _get_win_folder(base): + return "C:\\Users\\test\\AppData\\Roaming" + + monkeypatch.setattr( + appdirs, + "_get_win_folder", + _get_win_folder, + raising=False, + ) + monkeypatch.setattr(appdirs, "WINDOWS", True) + monkeypatch.setattr(os, "path", ntpath) + + assert ( + appdirs.user_data_dir("pip", roaming=True) == + "C:\\Users\\test\\AppData\\Roaming\\pip" + ) + assert _get_win_folder.calls == [pretend.call("CSIDL_APPDATA")] + + def test_user_data_dir_osx(self, monkeypatch): + monkeypatch.setattr(appdirs, "WINDOWS", False) + monkeypatch.setattr(os, "path", posixpath) + monkeypatch.setenv("HOME", "/home/test") + monkeypatch.setattr(sys, "platform", "darwin") + + assert (appdirs.user_data_dir("pip") == + "/home/test/Library/Application Support/pip") + + def test_user_data_dir_linux(self, monkeypatch): + monkeypatch.setattr(appdirs, "WINDOWS", False) + monkeypatch.setattr(os, "path", posixpath) + monkeypatch.delenv("XDG_DATA_HOME") + monkeypatch.setenv("HOME", "/home/test") + monkeypatch.setattr(sys, "platform", "linux2") + + assert appdirs.user_data_dir("pip") == "/home/test/.local/share/pip" + + def test_user_data_dir_linux_override(self, monkeypatch): + monkeypatch.setattr(appdirs, "WINDOWS", False) + monkeypatch.setattr(os, "path", posixpath) + monkeypatch.setenv("XDG_DATA_HOME", "/home/test/.other-share") + monkeypatch.setenv("HOME", "/home/test") + monkeypatch.setattr(sys, "platform", "linux2") + + assert appdirs.user_data_dir("pip") == "/home/test/.other-share/pip" + + def test_user_data_dir_linux_home_slash(self, monkeypatch): + monkeypatch.setattr(appdirs, "WINDOWS", False) + monkeypatch.setattr(os, "path", posixpath) + # Verify that we are not affected by http://bugs.python.org/issue14768 + monkeypatch.delenv("XDG_DATA_HOME") + monkeypatch.setenv("HOME", "/") + monkeypatch.setattr(sys, "platform", "linux2") + + assert appdirs.user_data_dir("pip") == "/.local/share/pip" + + +class TestUserConfigDir: + + def test_user_config_dir_win_no_roaming(self, monkeypatch): + @pretend.call_recorder + def _get_win_folder(base): + return "C:\\Users\\test\\AppData\\Local" + + monkeypatch.setattr( + appdirs, + "_get_win_folder", + _get_win_folder, + raising=False, + ) + monkeypatch.setattr(appdirs, "WINDOWS", True) + monkeypatch.setattr(os, "path", ntpath) + + assert ( + appdirs.user_config_dir("pip", roaming=False) == + "C:\\Users\\test\\AppData\\Local\\pip" + ) + assert _get_win_folder.calls == [pretend.call("CSIDL_LOCAL_APPDATA")] + + def test_user_config_dir_win_yes_roaming(self, monkeypatch): + @pretend.call_recorder + def _get_win_folder(base): + return "C:\\Users\\test\\AppData\\Roaming" + + monkeypatch.setattr( + appdirs, + "_get_win_folder", + _get_win_folder, + raising=False, + ) + monkeypatch.setattr(appdirs, "WINDOWS", True) + monkeypatch.setattr(os, "path", ntpath) + + assert (appdirs.user_config_dir("pip") == + "C:\\Users\\test\\AppData\\Roaming\\pip") + assert _get_win_folder.calls == [pretend.call("CSIDL_APPDATA")] + + def test_user_config_dir_osx(self, monkeypatch): + monkeypatch.setattr(appdirs, "WINDOWS", False) + monkeypatch.setattr(os, "path", posixpath) + monkeypatch.setenv("HOME", "/home/test") + monkeypatch.setattr(sys, "platform", "darwin") + + assert (appdirs.user_config_dir("pip") == + "/home/test/Library/Application Support/pip") + + def test_user_config_dir_linux(self, monkeypatch): + monkeypatch.setattr(appdirs, "WINDOWS", False) + monkeypatch.setattr(os, "path", posixpath) + monkeypatch.delenv("XDG_CONFIG_HOME") + monkeypatch.setenv("HOME", "/home/test") + monkeypatch.setattr(sys, "platform", "linux2") + + assert appdirs.user_config_dir("pip") == "/home/test/.config/pip" + + def test_user_config_dir_linux_override(self, monkeypatch): + monkeypatch.setattr(appdirs, "WINDOWS", False) + monkeypatch.setattr(os, "path", posixpath) + monkeypatch.setenv("XDG_CONFIG_HOME", "/home/test/.other-config") + monkeypatch.setenv("HOME", "/home/test") + monkeypatch.setattr(sys, "platform", "linux2") + + assert appdirs.user_config_dir("pip") == "/home/test/.other-config/pip" + + def test_user_config_dir_linux_home_slash(self, monkeypatch): + monkeypatch.setattr(appdirs, "WINDOWS", False) + monkeypatch.setattr(os, "path", posixpath) + # Verify that we are not affected by http://bugs.python.org/issue14768 + monkeypatch.delenv("XDG_CONFIG_HOME") + monkeypatch.setenv("HOME", "/") + monkeypatch.setattr(sys, "platform", "linux2") + + assert appdirs.user_config_dir("pip") == "/.config/pip" diff --git a/tests/unit/test_basecommand.py b/tests/unit/test_basecommand.py index 34b1b5c2a4c..3260ff83a00 100644 --- a/tests/unit/test_basecommand.py +++ b/tests/unit/test_basecommand.py @@ -1,28 +1,43 @@ -import os +import logging + from pip.basecommand import Command -from pip.log import logger class FakeCommand(Command): name = 'fake' summary = name + def __init__(self, error=False): self.error = error super(FakeCommand, self).__init__() + + def main(self, args): + args.append("--disable-pip-version-check") + return super(FakeCommand, self).main(args) + def run(self, options, args): - logger.info("fake") + logging.getLogger("pip.tests").info("fake") if self.error: raise SystemExit(1) +class FakeCommandWithUnicode(FakeCommand): + name = 'fake_unicode' + summary = name + + def run(self, options, args): + logging.getLogger("pip.tests").info(b"bytes here \xE9") + logging.getLogger("pip.tests").info( + b"unicode here \xC3\xA9".decode("utf-8") + ) + + class Test_basecommand_logging(object): """ - Test `pip.basecommand.Command` setting up logging consumers based on options + Test `pip.basecommand.Command` setting up logging consumers based on + options """ - def teardown(self): - logger.consumers = [] - def test_log_command_success(self, tmpdir): """ Test the --log option logs when command succeeds @@ -41,16 +56,6 @@ def test_log_command_error(self, tmpdir): cmd.main(['fake', '--log', log_path]) assert 'fake' == open(log_path).read().strip()[:4] - def test_log_file_command_success(self, tmpdir): - """ - Test the --log-file option *doesn't* log when command succeeds. - (It's just the historical behavior? this just confirms it) - """ - cmd = FakeCommand() - log_file_path = tmpdir.join('log_file') - cmd.main(['fake', '--log-file', log_file_path]) - assert not os.path.exists(log_file_path) - def test_log_file_command_error(self, tmpdir): """ Test the --log-file option logs (when there's an error). @@ -60,22 +65,10 @@ def test_log_file_command_error(self, tmpdir): cmd.main(['fake', '--log-file', log_file_path]) assert 'fake' == open(log_file_path).read().strip()[:4] - def test_log_log_file(self, tmpdir): + def test_unicode_messages(self, tmpdir): """ - Test the --log and --log-file options log together (when there's an error). + Tests that logging bytestrings and unicode objects don't break logging """ - cmd = FakeCommand(error=True) + cmd = FakeCommandWithUnicode() log_path = tmpdir.join('log') - log_file_path = tmpdir.join('log_file') - cmd.main(['fake', '--log', log_path, '--log-file', log_file_path]) - assert 'fake' == open(log_path).read().strip()[:4] - assert 'fake' == open(log_file_path).read().strip()[:4] - - def test_verbose_quiet(self): - """ - Test additive quality of -v and -q - """ - cmd = FakeCommand() - cmd.main(['fake', '-vqq']) - console_level = logger.consumers[0][0] - assert console_level == logger.WARN + cmd.main(['fake_unicode', '--log', log_path]) diff --git a/tests/unit/test_cmdoptions.py b/tests/unit/test_cmdoptions.py new file mode 100644 index 00000000000..5440a9a51f6 --- /dev/null +++ b/tests/unit/test_cmdoptions.py @@ -0,0 +1,62 @@ +import pip +from pip.basecommand import Command +from pip import cmdoptions + + +class SimpleCommand(Command): + name = 'fake' + summary = name + + def __init__(self): + super(SimpleCommand, self).__init__() + self.cmd_opts.add_option(cmdoptions.no_use_wheel()) + self.cmd_opts.add_option(cmdoptions.no_binary()) + self.cmd_opts.add_option(cmdoptions.only_binary()) + + def run(self, options, args): + cmdoptions.resolve_wheel_no_use_binary(options) + self.options = options + + +def test_no_use_wheel_sets_no_binary_all(): + cmd = SimpleCommand() + cmd.main(['fake', '--no-use-wheel']) + expected = pip.index.FormatControl(set([':all:']), set([])) + assert cmd.options.format_control == expected + + +def test_no_binary_overrides(): + cmd = SimpleCommand() + cmd.main(['fake', '--only-binary=:all:', '--no-binary=fred']) + expected = pip.index.FormatControl(set(['fred']), set([':all:'])) + assert cmd.options.format_control == expected + + +def test_only_binary_overrides(): + cmd = SimpleCommand() + cmd.main(['fake', '--no-binary=:all:', '--only-binary=fred']) + expected = pip.index.FormatControl(set([':all:']), set(['fred'])) + assert cmd.options.format_control == expected + + +def test_none_resets(): + cmd = SimpleCommand() + cmd.main(['fake', '--no-binary=:all:', '--no-binary=:none:']) + expected = pip.index.FormatControl(set([]), set([])) + assert cmd.options.format_control == expected + + +def test_none_preserves_other_side(): + cmd = SimpleCommand() + cmd.main( + ['fake', '--no-binary=:all:', '--only-binary=fred', + '--no-binary=:none:']) + expected = pip.index.FormatControl(set([]), set(['fred'])) + assert cmd.options.format_control == expected + + +def test_comma_separated_values(): + cmd = SimpleCommand() + cmd.main(['fake', '--no-binary=1,2,3']) + expected = pip.index.FormatControl(set(['1', '2', '3']), set([])) + assert cmd.options.format_control == expected diff --git a/tests/unit/test_backwardcompat.py b/tests/unit/test_compat.py similarity index 57% rename from tests/unit/test_backwardcompat.py rename to tests/unit/test_compat.py index 2e6b9ec068a..d0f55deed95 100644 --- a/tests/unit/test_backwardcompat.py +++ b/tests/unit/test_compat.py @@ -1,17 +1,20 @@ import os -from pip.backwardcompat import get_path_uid +from pip.compat import expanduser, get_path_uid, native_str import pytest + def test_get_path_uid(): path = os.getcwd() assert get_path_uid(path) == os.stat(path).st_uid + @pytest.mark.skipif("not hasattr(os, 'O_NOFOLLOW')") def test_get_path_uid_without_NOFOLLOW(monkeypatch): monkeypatch.delattr("os.O_NOFOLLOW") path = os.getcwd() assert get_path_uid(path) == os.stat(path).st_uid + @pytest.mark.skipif("not hasattr(os, 'symlink')") def test_get_path_uid_symlink(tmpdir): f = tmpdir.mkdir("symlink").join("somefile") @@ -21,6 +24,7 @@ def test_get_path_uid_symlink(tmpdir): with pytest.raises(OSError): get_path_uid(fs) + @pytest.mark.skipif("not hasattr(os, 'O_NOFOLLOW')") @pytest.mark.skipif("not hasattr(os, 'symlink')") def test_get_path_uid_symlink_without_NOFOLLOW(tmpdir, monkeypatch): @@ -32,3 +36,21 @@ def test_get_path_uid_symlink_without_NOFOLLOW(tmpdir, monkeypatch): with pytest.raises(OSError): get_path_uid(fs) + +def test_to_native_str_type(): + some_bytes = b"test\xE9 et approuv\xC3\xE9" + some_unicode = b"test\xE9 et approuv\xE9".decode('iso-8859-15') + assert isinstance(native_str(some_bytes, True), str) + assert isinstance(native_str(some_unicode, True), str) + + +@pytest.mark.parametrize("home,path,expanded", [ + ("/Users/test", "~", "/Users/test"), + ("/Users/test", "~/.cache", "/Users/test/.cache"), + # Verify that we are not affected by http://bugs.python.org/issue14768 + ("/", "~", "/"), + ("/", "~/.cache", "/.cache"), +]) +def test_expanduser(home, path, expanded, monkeypatch): + monkeypatch.setenv("HOME", home) + assert expanduser(path) == expanded diff --git a/tests/unit/test_download.py b/tests/unit/test_download.py index 695a10dbb5f..ee4b11c7dbb 100644 --- a/tests/unit/test_download.py +++ b/tests/unit/test_download.py @@ -1,17 +1,22 @@ import hashlib import os +from io import BytesIO from shutil import rmtree, copy from tempfile import mkdtemp +from pip._vendor.six.moves.urllib import request as urllib_request + from mock import Mock, patch import pytest import pip -from pip.backwardcompat import urllib, BytesIO, b, pathname2url from pip.exceptions import HashMismatch -from pip.download import (PipSession, path_to_url, unpack_http_url, - url_to_path, unpack_file_url) +from pip.download import ( + PipSession, SafeFileCache, path_to_url, unpack_http_url, url_to_path, + unpack_file_url, +) from pip.index import Link +from pip.utils.hashes import Hashes def test_unpack_http_url_with_urllib_response_without_content_type(data): @@ -32,12 +37,15 @@ def _fake_session_get(*args, **kwargs): link = Link(uri) temp_dir = mkdtemp() try: - unpack_http_url(link, temp_dir, - download_cache=None, + unpack_http_url( + link, + temp_dir, download_dir=None, session=session, ) - assert set(os.listdir(temp_dir)) == set(['PKG-INFO', 'setup.cfg', 'setup.py', 'simple', 'simple.egg-info']) + assert set(os.listdir(temp_dir)) == set([ + 'PKG-INFO', 'setup.cfg', 'setup.py', 'simple', 'simple.egg-info' + ]) finally: rmtree(temp_dir) @@ -72,54 +80,13 @@ def raise_for_status(self): pass -@patch('pip.download.unpack_file') -def test_unpack_http_url_bad_cache_checksum(mock_unpack_file): - """ - If cached download has bad checksum, re-download. - """ - base_url = 'http://www.example.com/somepackage.tgz' - contents = b('downloaded') - download_hash = hashlib.new('sha1', contents) - link = Link(base_url + '#sha1=' + download_hash.hexdigest()) - - session = Mock() - session.get = Mock() - response = session.get.return_value = MockResponse(contents) - response.headers = {'content-type': 'application/x-tar'} - response.url = base_url - - cache_dir = mkdtemp() - try: - cache_file = os.path.join(cache_dir, urllib.quote(base_url, '')) - cache_ct_file = cache_file + '.content-type' - _write_file(cache_file, 'some contents') - _write_file(cache_ct_file, 'application/x-tar') - - unpack_http_url(link, 'location', - download_cache=cache_dir, - session=session, - ) - - # despite existence of cached file with bad hash, downloaded again - session.get.assert_called_once_with( - "http://www.example.com/somepackage.tgz", - stream=True, - ) - # cached file is replaced with newly downloaded file - with open(cache_file) as fh: - assert fh.read() == 'downloaded' - - finally: - rmtree(cache_dir) - - @patch('pip.download.unpack_file') def test_unpack_http_url_bad_downloaded_checksum(mock_unpack_file): """ If already-downloaded file has bad checksum, re-download. """ base_url = 'http://www.example.com/somepackage.tgz' - contents = b('downloaded') + contents = b'downloaded' download_hash = hashlib.new('sha1', contents) link = Link(base_url + '#sha1=' + download_hash.hexdigest()) @@ -134,15 +101,18 @@ def test_unpack_http_url_bad_downloaded_checksum(mock_unpack_file): downloaded_file = os.path.join(download_dir, 'somepackage.tgz') _write_file(downloaded_file, 'some contents') - unpack_http_url(link, 'location', - download_cache=None, + unpack_http_url( + link, + 'location', download_dir=download_dir, session=session, + hashes=Hashes({'sha1': [download_hash.hexdigest()]}) ) # despite existence of downloaded file with bad hash, downloaded again session.get.assert_called_once_with( 'http://www.example.com/somepackage.tgz', + headers={"Accept-Encoding": "identity"}, stream=True, ) # cached file is replaced with newly downloaded file @@ -157,7 +127,7 @@ def test_unpack_http_url_bad_downloaded_checksum(mock_unpack_file): def test_path_to_url_unix(): assert path_to_url('/tmp/file') == 'file:///tmp/file' path = os.path.join(os.getcwd(), 'file') - assert path_to_url('file') == 'file://' + pathname2url(path) + assert path_to_url('file') == 'file://' + urllib_request.pathname2url(path) @pytest.mark.skipif("sys.platform == 'win32'") @@ -167,15 +137,26 @@ def test_url_to_path_unix(): @pytest.mark.skipif("sys.platform != 'win32'") def test_path_to_url_win(): - assert path_to_url('c:/tmp/file') == 'file:///c:/tmp/file' - assert path_to_url('c:\\tmp\\file') == 'file:///c:/tmp/file' + assert path_to_url('c:/tmp/file') == 'file:///C:/tmp/file' + assert path_to_url('c:\\tmp\\file') == 'file:///C:/tmp/file' + assert path_to_url(r'\\unc\as\path') == 'file://unc/as/path' path = os.path.join(os.getcwd(), 'file') - assert path_to_url('file') == 'file:' + pathname2url(path) + assert path_to_url('file') == 'file:' + urllib_request.pathname2url(path) @pytest.mark.skipif("sys.platform != 'win32'") def test_url_to_path_win(): - assert url_to_path('file:///c:/tmp/file') == 'c:/tmp/file' + assert url_to_path('file:///c:/tmp/file') == 'C:\\tmp\\file' + assert url_to_path('file://unc/as/path') == r'\\unc\as\path' + + +@pytest.mark.skipif("sys.platform != 'win32'") +def test_url_to_path_path_to_url_symmetry_win(): + path = r'C:\tmp\file' + assert url_to_path(path_to_url(path)) == path + + unc_path = r'\\unc\share\path' + assert url_to_path(path_to_url(unc_path)) == unc_path class Test_unpack_file_url(object): @@ -230,7 +211,9 @@ def test_unpack_file_url_bad_hash(self, tmpdir, data, self.prep(tmpdir, data) self.dist_url.url = "%s#md5=bogus" % self.dist_url.url with pytest.raises(HashMismatch): - unpack_file_url(self.dist_url, self.build_dir) + unpack_file_url(self.dist_url, + self.build_dir, + hashes=Hashes({'md5': ['bogus']})) def test_unpack_file_url_download_bad_hash(self, tmpdir, data, monkeypatch): @@ -254,14 +237,14 @@ def test_unpack_file_url_download_bad_hash(self, tmpdir, data, self.dist_url.url = "%s#md5=%s" % ( self.dist_url.url, dist_path_md5 - ) + ) unpack_file_url(self.dist_url, self.build_dir, - download_dir=self.download_dir) + download_dir=self.download_dir, + hashes=Hashes({'md5': [dist_path_md5]})) # confirm hash is for simple1-1.0 # the previous bad download has been removed - assert (hashlib.md5(open(dest_file, 'rb').read()).hexdigest() - == + assert (hashlib.md5(open(dest_file, 'rb').read()).hexdigest() == dist_path_md5 ), hashlib.md5(open(dest_file, 'rb').read()).hexdigest() @@ -272,3 +255,81 @@ def test_unpack_file_url_thats_a_dir(self, tmpdir, data): unpack_file_url(dist_url, self.build_dir, download_dir=self.download_dir) assert os.path.isdir(os.path.join(self.build_dir, 'fspkg')) + + +class TestSafeFileCache: + """ + The no_perms test are useless on Windows since SafeFileCache uses + pip.utils.filesystem.check_path_owner which is based on os.geteuid + which is absent on Windows. + """ + + def test_cache_roundtrip(self, tmpdir): + cache_dir = tmpdir.join("test-cache") + cache_dir.makedirs() + + cache = SafeFileCache(cache_dir) + assert cache.get("test key") is None + cache.set("test key", b"a test string") + assert cache.get("test key") == b"a test string" + cache.delete("test key") + assert cache.get("test key") is None + + @pytest.mark.skipif("sys.platform == 'win32'") + def test_safe_get_no_perms(self, tmpdir, monkeypatch): + cache_dir = tmpdir.join("unreadable-cache") + cache_dir.makedirs() + os.chmod(cache_dir, 000) + + monkeypatch.setattr(os.path, "exists", lambda x: True) + + cache = SafeFileCache(cache_dir) + cache.get("foo") + + @pytest.mark.skipif("sys.platform == 'win32'") + def test_safe_set_no_perms(self, tmpdir): + cache_dir = tmpdir.join("unreadable-cache") + cache_dir.makedirs() + os.chmod(cache_dir, 000) + + cache = SafeFileCache(cache_dir) + cache.set("foo", b"bar") + + @pytest.mark.skipif("sys.platform == 'win32'") + def test_safe_delete_no_perms(self, tmpdir): + cache_dir = tmpdir.join("unreadable-cache") + cache_dir.makedirs() + os.chmod(cache_dir, 000) + + cache = SafeFileCache(cache_dir) + cache.delete("foo") + + +class TestPipSession: + + def test_cache_defaults_off(self): + session = PipSession() + + assert not hasattr(session.adapters["http://"], "cache") + assert not hasattr(session.adapters["https://"], "cache") + + def test_cache_is_enabled(self, tmpdir): + session = PipSession(cache=tmpdir.join("test-cache")) + + assert hasattr(session.adapters["https://"], "cache") + + assert (session.adapters["https://"].cache.directory == + tmpdir.join("test-cache")) + + def test_http_cache_is_not_enabled(self, tmpdir): + session = PipSession(cache=tmpdir.join("test-cache")) + + assert not hasattr(session.adapters["http://"], "cache") + + def test_insecure_host_cache_is_not_enabled(self, tmpdir): + session = PipSession( + cache=tmpdir.join("test-cache"), + insecure_hosts=["example.com"], + ) + + assert not hasattr(session.adapters["https://example.com/"], "cache") diff --git a/tests/unit/test_download_hashes.py b/tests/unit/test_download_hashes.py deleted file mode 100644 index d2e19e31227..00000000000 --- a/tests/unit/test_download_hashes.py +++ /dev/null @@ -1,201 +0,0 @@ -import os - -import pytest - -from pip.download import _get_hash_from_file, _check_hash -from pip.exceptions import InstallationError -from pip.index import Link - - -def test_get_hash_from_file_md5(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#md5=d41d8cd98f00b204e9800998ecf8427e") - - download_hash = _get_hash_from_file(file_path, file_link) - - assert download_hash.digest_size == 16 - assert download_hash.hexdigest() == "d41d8cd98f00b204e9800998ecf8427e" - - -def test_get_hash_from_file_sha1(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha1=da39a3ee5e6b4b0d3255bfef95601890afd80709") - - download_hash = _get_hash_from_file(file_path, file_link) - - assert download_hash.digest_size == 20 - assert download_hash.hexdigest() == "da39a3ee5e6b4b0d3255bfef95601890afd80709" - - -def test_get_hash_from_file_sha224(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha224=d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f") - - download_hash = _get_hash_from_file(file_path, file_link) - - assert download_hash.digest_size == 28 - assert download_hash.hexdigest() == "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" - - -def test_get_hash_from_file_sha384(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha384=38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b") - - download_hash = _get_hash_from_file(file_path, file_link) - - assert download_hash.digest_size == 48 - assert download_hash.hexdigest() == "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b" - - -def test_get_hash_from_file_sha256(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") - - download_hash = _get_hash_from_file(file_path, file_link) - - assert download_hash.digest_size == 32 - assert download_hash.hexdigest() == "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" - - -def test_get_hash_from_file_sha512(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha512=cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e") - - download_hash = _get_hash_from_file(file_path, file_link) - - assert download_hash.digest_size == 64 - assert download_hash.hexdigest() == "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" - - -def test_get_hash_from_file_unknown(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#unknown_hash=d41d8cd98f00b204e9800998ecf8427e") - - download_hash = _get_hash_from_file(file_path, file_link) - - assert download_hash is None - - -def test_check_hash_md5_valid(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#md5=d41d8cd98f00b204e9800998ecf8427e") - - download_hash = _get_hash_from_file(file_path, file_link) - - _check_hash(download_hash, file_link) - - -def test_check_hash_md5_invalid(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#md5=deadbeef") - - download_hash = _get_hash_from_file(file_path, file_link) - - with pytest.raises(InstallationError): - _check_hash(download_hash, file_link) - - -def test_check_hash_sha1_valid(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha1=da39a3ee5e6b4b0d3255bfef95601890afd80709") - - download_hash = _get_hash_from_file(file_path, file_link) - - _check_hash(download_hash, file_link) - - -def test_check_hash_sha1_invalid(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha1=deadbeef") - - download_hash = _get_hash_from_file(file_path, file_link) - - with pytest.raises(InstallationError): - _check_hash(download_hash, file_link) - - -def test_check_hash_sha224_valid(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha224=d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f'") - - download_hash = _get_hash_from_file(file_path, file_link) - - _check_hash(download_hash, file_link) - - -def test_check_hash_sha224_invalid(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha224=deadbeef") - - download_hash = _get_hash_from_file(file_path, file_link) - - with pytest.raises(InstallationError): - _check_hash(download_hash, file_link) - - -def test_check_hash_sha384_valid(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha384=38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b") - - download_hash = _get_hash_from_file(file_path, file_link) - - _check_hash(download_hash, file_link) - - -def test_check_hash_sha384_invalid(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha384=deadbeef") - - download_hash = _get_hash_from_file(file_path, file_link) - - with pytest.raises(InstallationError): - _check_hash(download_hash, file_link) - - -def test_check_hash_sha256_valid(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") - - download_hash = _get_hash_from_file(file_path, file_link) - - _check_hash(download_hash, file_link) - - -def test_check_hash_sha256_invalid(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha256=deadbeef") - - download_hash = _get_hash_from_file(file_path, file_link) - - with pytest.raises(InstallationError): - _check_hash(download_hash, file_link) - - -def test_check_hash_sha512_valid(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha512=cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e") - - download_hash = _get_hash_from_file(file_path, file_link) - - _check_hash(download_hash, file_link) - - -def test_check_hash_sha512_invalid(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#sha512=deadbeef") - - download_hash = _get_hash_from_file(file_path, file_link) - - with pytest.raises(InstallationError): - _check_hash(download_hash, file_link) - - -def test_check_hasher_mismsatch(data): - file_path = data.packages.join("gmpy-1.15.tar.gz") - file_link = Link("http://testserver/gmpy-1.15.tar.gz#md5=d41d8cd98f00b204e9800998ecf8427e") - other_link = Link("http://testserver/gmpy-1.15.tar.gz#sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") - - download_hash = _get_hash_from_file(file_path, file_link) - - with pytest.raises(InstallationError): - _check_hash(download_hash, other_link) diff --git a/tests/unit/test_finder.py b/tests/unit/test_finder.py index 7dad41091d5..48265e98d89 100644 --- a/tests/unit/test_finder.py +++ b/tests/unit/test_finder.py @@ -1,26 +1,22 @@ -import os - import pytest import pip.wheel import pip.pep425tags from pkg_resources import parse_version, Distribution -from pip.backwardcompat import urllib -from pip.log import logger from pip.req import InstallRequirement -from pip.index import PackageFinder, Link -from pip.exceptions import BestVersionAlreadyInstalled, DistributionNotFound, InstallationError -from pip.util import Inf +from pip.index import ( + InstallationCandidate, PackageFinder, Link, FormatControl, + fmt_ctl_formats) +from pip.exceptions import BestVersionAlreadyInstalled, DistributionNotFound +from pip.download import PipSession -from tests.lib.path import Path -from tests.lib import path_to_url from mock import Mock, patch def test_no_mpkg(data): """Finder skips zipfiles with "macosx10" in the name.""" - finder = PackageFinder([data.find_links], []) + finder = PackageFinder([data.find_links], [], session=PipSession()) req = InstallRequirement.from_line("pkgwithmpkg") found = finder.find_requirement(req, False) @@ -29,17 +25,31 @@ def test_no_mpkg(data): def test_no_partial_name_match(data): """Finder requires the full project name to match, not just beginning.""" - finder = PackageFinder([data.find_links], []) + finder = PackageFinder([data.find_links], [], session=PipSession()) req = InstallRequirement.from_line("gmpy") found = finder.find_requirement(req, False) assert found.url.endswith("gmpy-1.15.tar.gz"), found +def test_tilde(data): + """Finder can accept a path with ~ in it and will normalize it.""" + session = PipSession() + with patch('pip.index.os.path.exists', return_value=True): + finder = PackageFinder(['~/python-pkgs'], [], session=session) + req = InstallRequirement.from_line("gmpy") + with pytest.raises(DistributionNotFound): + finder.find_requirement(req, False) + + def test_duplicates_sort_ok(data): """Finder successfully finds one of a set of duplicates in different locations""" - finder = PackageFinder([data.find_links, data.find_links2], []) + finder = PackageFinder( + [data.find_links, data.find_links2], + [], + session=PipSession(), + ) req = InstallRequirement.from_line("duplicate") found = finder.find_requirement(req, False) @@ -49,40 +59,54 @@ def test_duplicates_sort_ok(data): def test_finder_detects_latest_find_links(data): """Test PackageFinder detects latest using find-links""" req = InstallRequirement.from_line('simple', None) - finder = PackageFinder([data.find_links], []) + finder = PackageFinder([data.find_links], [], session=PipSession()) link = finder.find_requirement(req, False) assert link.url.endswith("simple-3.0.tar.gz") +def test_incorrect_case_file_index(data): + """Test PackageFinder detects latest using wrong case""" + req = InstallRequirement.from_line('dinner', None) + finder = PackageFinder([], [data.find_links3], session=PipSession()) + link = finder.find_requirement(req, False) + assert link.url.endswith("Dinner-2.0.tar.gz") + + +@pytest.mark.network def test_finder_detects_latest_already_satisfied_find_links(data): - """Test PackageFinder detects latest already satisified using find-links""" + """Test PackageFinder detects latest already satisfied using find-links""" req = InstallRequirement.from_line('simple', None) - #the latest simple in local pkgs is 3.0 + # the latest simple in local pkgs is 3.0 latest_version = "3.0" satisfied_by = Mock( - location = "/path", - parsed_version = parse_version(latest_version), - version = latest_version - ) + location="/path", + parsed_version=parse_version(latest_version), + version=latest_version + ) req.satisfied_by = satisfied_by - finder = PackageFinder([data.find_links], []) + finder = PackageFinder([data.find_links], [], session=PipSession()) with pytest.raises(BestVersionAlreadyInstalled): finder.find_requirement(req, True) +@pytest.mark.network def test_finder_detects_latest_already_satisfied_pypi_links(): - """Test PackageFinder detects latest already satisified using pypi links""" + """Test PackageFinder detects latest already satisfied using pypi links""" req = InstallRequirement.from_line('initools', None) - #the latest initools on pypi is 0.3.1 + # the latest initools on pypi is 0.3.1 latest_version = "0.3.1" satisfied_by = Mock( - location = "/path", - parsed_version = parse_version(latest_version), - version = latest_version - ) + location="/path", + parsed_version=parse_version(latest_version), + version=latest_version, + ) req.satisfied_by = satisfied_by - finder = PackageFinder([], ["http://pypi.python.org/simple"]) + finder = PackageFinder( + [], + ["http://pypi.python.org/simple"], + session=PipSession(), + ) with pytest.raises(BestVersionAlreadyInstalled): finder.find_requirement(req, True) @@ -90,30 +114,41 @@ def test_finder_detects_latest_already_satisfied_pypi_links(): class TestWheel: - def teardown(self): - logger.consumers = [] - - def test_skip_invalid_wheel_link(self, data): + def test_skip_invalid_wheel_link(self, caplog, data): """ Test if PackageFinder skips invalid wheel filenames """ - log = [] - logger.add_consumers((logger.DEBUG, log.append)) req = InstallRequirement.from_line("invalid") - #data.find_links contains "invalid.whl", which is an invalid wheel - finder = PackageFinder([data.find_links], [], use_wheel=True) + # data.find_links contains "invalid.whl", which is an invalid wheel + finder = PackageFinder( + [data.find_links], + [], + session=PipSession(), + ) with pytest.raises(DistributionNotFound): finder.find_requirement(req, True) - "invalid.whl because the wheel filename is invalid" in "".join(log) + + assert ( + "invalid.whl; invalid wheel filename" + in caplog.text() + ) def test_not_find_wheel_not_supported(self, data, monkeypatch): """ Test not finding an unsupported wheel. """ - monkeypatch.setattr(pip.pep425tags, "supported_tags", [('py1', 'none', 'any')]) + monkeypatch.setattr( + pip.pep425tags, + "supported_tags", + [('py1', 'none', 'any')], + ) req = InstallRequirement.from_line("simple.dist") - finder = PackageFinder([data.find_links], [], use_wheel=True) + finder = PackageFinder( + [data.find_links], + [], + session=PipSession(), + ) with pytest.raises(DistributionNotFound): finder.find_requirement(req, True) @@ -122,12 +157,22 @@ def test_find_wheel_supported(self, data, monkeypatch): """ Test finding supported wheel. """ - monkeypatch.setattr(pip.pep425tags, "supported_tags", [('py2', 'none', 'any')]) + monkeypatch.setattr( + pip.pep425tags, + "supported_tags", + [('py2', 'none', 'any')], + ) req = InstallRequirement.from_line("simple.dist") - finder = PackageFinder([data.find_links], [], use_wheel=True) + finder = PackageFinder( + [data.find_links], + [], + session=PipSession(), + ) found = finder.find_requirement(req, True) - assert found.url.endswith("simple.dist-0.1-py2.py3-none-any.whl"), found + assert ( + found.url.endswith("simple.dist-0.1-py2.py3-none-any.whl") + ), found def test_wheel_over_sdist_priority(self, data): """ @@ -135,7 +180,11 @@ def test_wheel_over_sdist_priority(self, data): `test_link_sorting` also covers this at lower level """ req = InstallRequirement.from_line("priority") - finder = PackageFinder([data.find_links], [], use_wheel=True) + finder = PackageFinder( + [data.find_links], + [], + session=PipSession(), + ) found = finder.find_requirement(req, True) assert found.url.endswith("priority-1.0-py2.py3-none-any.whl"), found @@ -147,67 +196,115 @@ def test_existing_over_wheel_priority(self, data): req = InstallRequirement.from_line('priority', None) latest_version = "1.0" satisfied_by = Mock( - location = "/path", - parsed_version = parse_version(latest_version), - version = latest_version - ) + location="/path", + parsed_version=parse_version(latest_version), + version=latest_version, + ) req.satisfied_by = satisfied_by - finder = PackageFinder([data.find_links], [], use_wheel=True) + finder = PackageFinder( + [data.find_links], + [], + session=PipSession(), + ) with pytest.raises(BestVersionAlreadyInstalled): finder.find_requirement(req, True) @patch('pip.pep425tags.supported_tags', [ - ('pyT', 'none', 'TEST'), - ('pyT', 'TEST', 'any'), - ('pyT', 'none', 'any'), - ]) + ('pyT', 'none', 'TEST'), + ('pyT', 'TEST', 'any'), + ('pyT', 'none', 'any'), + ]) def test_link_sorting(self): """ Test link sorting """ links = [ - (parse_version('2.0'), Link(Inf), '2.0'), - (parse_version('2.0'), Link('simple-2.0.tar.gz'), '2.0'), - (parse_version('1.0'), Link('simple-1.0-pyT-none-TEST.whl'), '1.0'), - (parse_version('1.0'), Link('simple-1.0-pyT-TEST-any.whl'), '1.0'), - (parse_version('1.0'), Link('simple-1.0-pyT-none-any.whl'), '1.0'), - (parse_version('1.0'), Link('simple-1.0.tar.gz'), '1.0'), - ] - - finder = PackageFinder([], []) - finder.use_wheel = True - - results = finder._sort_versions(links) - results2 = finder._sort_versions(sorted(links, reverse=True)) + InstallationCandidate("simple", "2.0", Link('simple-2.0.tar.gz')), + InstallationCandidate( + "simple", + "1.0", + Link('simple-1.0-pyT-none-TEST.whl'), + ), + InstallationCandidate( + "simple", + '1.0', + Link('simple-1.0-pyT-TEST-any.whl'), + ), + InstallationCandidate( + "simple", + '1.0', + Link('simple-1.0-pyT-none-any.whl'), + ), + InstallationCandidate( + "simple", + '1.0', + Link('simple-1.0.tar.gz'), + ), + ] + + finder = PackageFinder([], [], session=PipSession()) + + results = sorted(links, + key=finder._candidate_sort_key, reverse=True) + results2 = sorted(reversed(links), + key=finder._candidate_sort_key, reverse=True) assert links == results == results2, results2 - @patch('pip.pep425tags.supported_tags', []) - def test_link_sorting_raises_when_wheel_unsupported(self): - links = [(parse_version('1.0'), Link('simple-1.0-py2.py3-none-TEST.whl'), '1.0')] - finder = PackageFinder([], [], use_wheel=True) - with pytest.raises(InstallationError): - finder._sort_versions(links) - def test_finder_priority_file_over_page(data): """Test PackageFinder prefers file links over equivalent page links""" req = InstallRequirement.from_line('gmpy==1.15', None) - finder = PackageFinder([data.find_links], ["http://pypi.python.org/simple"]) + finder = PackageFinder( + [data.find_links], + ["http://pypi.python.org/simple"], + session=PipSession(), + ) + all_versions = finder.find_all_candidates(req.name) + # 1 file InstallationCandidate followed by all https ones + assert all_versions[0].location.scheme == 'file' + assert all(version.location.scheme == 'https' + for version in all_versions[1:]), all_versions + link = finder.find_requirement(req, False) assert link.url.startswith("file://") -def test_finder_priority_page_over_deplink(): - """Test PackageFinder prefers page links over equivalent dependency links""" +def test_finder_deplink(): + """ + Test PackageFinder with dependency links only + """ req = InstallRequirement.from_line('gmpy==1.15', None) + finder = PackageFinder( + [], + [], + process_dependency_links=True, + session=PipSession(), + ) + finder.add_dependency_links( + ['https://pypi.python.org/packages/source/g/gmpy/gmpy-1.15.zip']) + link = finder.find_requirement(req, False) + assert link.url.startswith("https://pypi"), link + + +@pytest.mark.network +def test_finder_priority_page_over_deplink(): + """ + Test PackageFinder prefers page links over equivalent dependency links + """ + req = InstallRequirement.from_line('pip==1.5.6', None) finder = PackageFinder( [], ["https://pypi.python.org/simple"], process_dependency_links=True, + session=PipSession(), ) - finder.add_dependency_links(['http://c.pypi.python.org/simple/gmpy/']) + finder.add_dependency_links([ + 'https://warehouse.python.org/packages/source/p/pip/pip-1.5.6.tar.gz']) + all_versions = finder.find_all_candidates(req.name) + # Check that the dependency_link is last + assert all_versions[-1].location.url.startswith('https://warehouse') link = finder.find_requirement(req, False) assert link.url.startswith("https://pypi"), link @@ -217,17 +314,24 @@ def test_finder_priority_nonegg_over_eggfragments(): req = InstallRequirement.from_line('bar==1.0', None) links = ['http://foo/bar.py#egg=bar-1.0', 'http://foo/bar-1.0.tar.gz'] - finder = PackageFinder(links, []) + finder = PackageFinder(links, [], session=PipSession()) with patch.object(finder, "_get_pages", lambda x, y: []): + all_versions = finder.find_all_candidates(req.name) + assert all_versions[0].location.url.endswith('tar.gz') + assert all_versions[1].location.url.endswith('#egg=bar-1.0') + link = finder.find_requirement(req, False) assert link.url.endswith('tar.gz') links.reverse() - finder = PackageFinder(links, []) + finder = PackageFinder(links, [], session=PipSession()) with patch.object(finder, "_get_pages", lambda x, y: []): + all_versions = finder.find_all_candidates(req.name) + assert all_versions[0].location.url.endswith('tar.gz') + assert all_versions[1].location.url.endswith('#egg=bar-1.0') link = finder.find_requirement(req, False) assert link.url.endswith('tar.gz') @@ -241,20 +345,20 @@ def test_finder_only_installs_stable_releases(data): req = InstallRequirement.from_line("bar", None) # using a local index (that has pre & dev releases) - finder = PackageFinder([], [data.index_url("pre")]) + finder = PackageFinder([], [data.index_url("pre")], session=PipSession()) link = finder.find_requirement(req, False) assert link.url.endswith("bar-1.0.tar.gz"), link.url # using find-links links = ["https://foo/bar-1.0.tar.gz", "https://foo/bar-2.0b1.tar.gz"] - finder = PackageFinder(links, []) + finder = PackageFinder(links, [], session=PipSession()) with patch.object(finder, "_get_pages", lambda x, y: []): link = finder.find_requirement(req, False) assert link.url == "https://foo/bar-1.0.tar.gz" links.reverse() - finder = PackageFinder(links, []) + finder = PackageFinder(links, [], session=PipSession()) with patch.object(finder, "_get_pages", lambda x, y: []): link = finder.find_requirement(req, False) @@ -266,23 +370,35 @@ def test_finder_installs_pre_releases(data): Test PackageFinder finds pre-releases if asked to. """ - req = InstallRequirement.from_line("bar", None, prereleases=True) + req = InstallRequirement.from_line("bar", None) # using a local index (that has pre & dev releases) - finder = PackageFinder([], [data.index_url("pre")]) + finder = PackageFinder( + [], [data.index_url("pre")], + allow_all_prereleases=True, + session=PipSession(), + ) link = finder.find_requirement(req, False) assert link.url.endswith("bar-2.0b1.tar.gz"), link.url # using find-links links = ["https://foo/bar-1.0.tar.gz", "https://foo/bar-2.0b1.tar.gz"] - finder = PackageFinder(links, []) + finder = PackageFinder( + links, [], + allow_all_prereleases=True, + session=PipSession(), + ) with patch.object(finder, "_get_pages", lambda x, y: []): link = finder.find_requirement(req, False) assert link.url == "https://foo/bar-2.0b1.tar.gz" links.reverse() - finder = PackageFinder(links, []) + finder = PackageFinder( + links, [], + allow_all_prereleases=True, + session=PipSession(), + ) with patch.object(finder, "_get_pages", lambda x, y: []): link = finder.find_requirement(req, False) @@ -294,10 +410,14 @@ def test_finder_installs_dev_releases(data): Test PackageFinder finds dev releases if asked to. """ - req = InstallRequirement.from_line("bar", None, prereleases=True) + req = InstallRequirement.from_line("bar", None) # using a local index (that has dev releases) - finder = PackageFinder([], [data.index_url("dev")]) + finder = PackageFinder( + [], [data.index_url("dev")], + allow_all_prereleases=True, + session=PipSession(), + ) link = finder.find_requirement(req, False) assert link.url.endswith("bar-2.0.dev1.tar.gz"), link.url @@ -309,206 +429,36 @@ def test_finder_installs_pre_releases_with_version_spec(): req = InstallRequirement.from_line("bar>=0.0.dev0", None) links = ["https://foo/bar-1.0.tar.gz", "https://foo/bar-2.0b1.tar.gz"] - finder = PackageFinder(links, []) + finder = PackageFinder(links, [], session=PipSession()) with patch.object(finder, "_get_pages", lambda x, y: []): link = finder.find_requirement(req, False) assert link.url == "https://foo/bar-2.0b1.tar.gz" links.reverse() - finder = PackageFinder(links, []) + finder = PackageFinder(links, [], session=PipSession()) with patch.object(finder, "_get_pages", lambda x, y: []): link = finder.find_requirement(req, False) assert link.url == "https://foo/bar-2.0b1.tar.gz" -def test_finder_ignores_external_links(data): - """ - Tests that PackageFinder ignores external links, with or without hashes. - """ - req = InstallRequirement.from_line("bar", None) - - # using a local index - finder = PackageFinder([], [data.index_url("externals")]) - link = finder.find_requirement(req, False) - assert link.filename == "bar-1.0.tar.gz" - - -def test_finder_finds_external_links_with_hashes_per_project(data): - """ - Tests that PackageFinder finds external links but only if they have a hash - using the per project configuration. - """ - req = InstallRequirement.from_line("bar", None) - - # using a local index - finder = PackageFinder([], [data.index_url("externals")], allow_external=["bar"]) - link = finder.find_requirement(req, False) - assert link.filename == "bar-2.0.tar.gz" - - -def test_finder_finds_external_links_with_hashes_all(data): - """ - Tests that PackageFinder finds external links but only if they have a hash - using the all externals flag. - """ - req = InstallRequirement.from_line("bar", None) - - # using a local index - finder = PackageFinder([], [data.index_url("externals")], allow_all_external=True) - link = finder.find_requirement(req, False) - assert link.filename == "bar-2.0.tar.gz" - - -def test_finder_finds_external_links_without_hashes_per_project(data): - """ - Tests that PackageFinder finds external links if they do not have a hash - """ - req = InstallRequirement.from_line("bar==3.0", None) - - # using a local index - finder = PackageFinder([], [data.index_url("externals")], - allow_external=["bar"], - allow_unverified=["bar"], - ) - link = finder.find_requirement(req, False) - assert link.filename == "bar-3.0.tar.gz" - - -def test_finder_finds_external_links_without_hashes_all(data): - """ - Tests that PackageFinder finds external links if they do not have a hash - using the all external flag - """ - req = InstallRequirement.from_line("bar==3.0", None) - - # using a local index - finder = PackageFinder([], [data.index_url("externals")], - allow_all_external=True, - allow_unverified=["bar"], - ) - link = finder.find_requirement(req, False) - assert link.filename == "bar-3.0.tar.gz" - - -def test_finder_finds_external_links_without_hashes_scraped_per_project(data): - """ - Tests that PackageFinder finds externally scraped links - """ - req = InstallRequirement.from_line("bar", None) - - # using a local index - finder = PackageFinder([], [data.index_url("externals")], - allow_external=["bar"], - allow_unverified=["bar"], - ) - link = finder.find_requirement(req, False) - assert link.filename == "bar-4.0.tar.gz" - - -def test_finder_finds_external_links_without_hashes_scraped_all(data): - """ - Tests that PackageFinder finds externally scraped links using the all - external flag. - """ - req = InstallRequirement.from_line("bar", None) - - # using a local index - finder = PackageFinder([], [data.index_url("externals")], - allow_all_external=True, - allow_unverified=["bar"], - ) - link = finder.find_requirement(req, False) - assert link.filename == "bar-4.0.tar.gz" - - -def test_finder_finds_external_links_without_hashes_per_project_all_insecure(data): - """ - Tests that PackageFinder finds external links if they do not have a hash - """ - req = InstallRequirement.from_line("bar==3.0", None) - - # using a local index - finder = PackageFinder([], [data.index_url("externals")], - allow_external=["bar"], - allow_unverified=["bar"], - ) - link = finder.find_requirement(req, False) - assert link.filename == "bar-3.0.tar.gz" - - -def test_finder_finds_external_links_without_hashes_all_all_insecure(data): - """ - Tests that PackageFinder finds external links if they do not have a hash - using the all external flag - """ - req = InstallRequirement.from_line("bar==3.0", None) - - # using a local index - finder = PackageFinder([], [data.index_url("externals")], - allow_all_external=True, - allow_unverified=["bar"], - ) - link = finder.find_requirement(req, False) - assert link.filename == "bar-3.0.tar.gz" - - -def test_finder_finds_external_links_without_hashes_scraped_per_project_all_insecure(data): - """ - Tests that PackageFinder finds externally scraped links - """ - req = InstallRequirement.from_line("bar", None) - - # using a local index - finder = PackageFinder([], [data.index_url("externals")], - allow_external=["bar"], - allow_unverified=["bar"], - ) - link = finder.find_requirement(req, False) - assert link.filename == "bar-4.0.tar.gz" - - -def test_finder_finds_external_links_without_hashes_scraped_all_all_insecure(data): - """ - Tests that PackageFinder finds externally scraped links using the all - external flag. - """ - req = InstallRequirement.from_line("bar", None) - - # using a local index - finder = PackageFinder([], [data.index_url("externals")], - allow_all_external=True, - allow_unverified=["bar"], - ) - link = finder.find_requirement(req, False) - assert link.filename == "bar-4.0.tar.gz" - - -def test_finder_finds_external_links_without_hashes_scraped_insecure(data): - """ - Tests that PackageFinder finds externally scraped links without the - external flag - """ - req = InstallRequirement.from_line("bar", None) - - # using a local index - finder = PackageFinder([], [data.index_url("externals")], - allow_unverified=["bar"], - ) - link = finder.find_requirement(req, False) - assert link.filename == "bar-4.0.tar.gz" - - class test_link_package_versions(object): - # patch this for travis which has distribute in it's base env for now - @patch('pip.wheel.pkg_resources.get_distribution', lambda x: Distribution(project_name='setuptools', version='0.9')) + # patch this for travis which has distribute in its base env for now + @patch( + 'pip.wheel.pkg_resources.get_distribution', + lambda x: Distribution(project_name='setuptools', version='0.9') + ) def setup(self): self.version = '1.0' self.parsed_version = parse_version(self.version) self.search_name = 'pytest' - self.finder = PackageFinder([], [], use_wheel=True) + self.finder = PackageFinder( + [], + [], + session=PipSession(), + ) def test_link_package_versions_match_wheel(self): """Test that 'pytest' archives match for 'pytest'""" @@ -539,5 +489,52 @@ def test_link_package_versions_substring_fails(self): assert result == [], result +def test_get_index_urls_locations(): + """Check that the canonical name is on all indexes""" + finder = PackageFinder( + [], ['file://index1/', 'file://index2'], session=PipSession()) + locations = finder._get_index_urls_locations( + InstallRequirement.from_line('Complex_Name').name) + assert locations == ['file://index1/complex-name/', + 'file://index2/complex-name/'] +def test_find_all_candidates_nothing(data): + """Find nothing without anything""" + finder = PackageFinder([], [], session=PipSession()) + assert not finder.find_all_candidates('pip') + + +def test_find_all_candidates_find_links(data): + finder = PackageFinder( + [data.find_links], [], session=PipSession()) + versions = finder.find_all_candidates('simple') + assert [str(v.version) for v in versions] == ['3.0', '2.0', '1.0'] + + +def test_find_all_candidates_index(data): + finder = PackageFinder( + [], [data.index_url('simple')], session=PipSession()) + versions = finder.find_all_candidates('simple') + assert [str(v.version) for v in versions] == ['1.0'] + + +def test_find_all_candidates_find_links_and_index(data): + finder = PackageFinder( + [data.find_links], [data.index_url('simple')], session=PipSession()) + versions = finder.find_all_candidates('simple') + # first the find-links versions then the page versions + assert [str(v.version) for v in versions] == ['3.0', '2.0', '1.0', '1.0'] + + +def test_fmt_ctl_matches(): + fmt = FormatControl(set(), set()) + assert fmt_ctl_formats(fmt, "fred") == frozenset(["source", "binary"]) + fmt = FormatControl(set(["fred"]), set()) + assert fmt_ctl_formats(fmt, "fred") == frozenset(["source"]) + fmt = FormatControl(set(["fred"]), set([":all:"])) + assert fmt_ctl_formats(fmt, "fred") == frozenset(["source"]) + fmt = FormatControl(set(), set(["fred"])) + assert fmt_ctl_formats(fmt, "fred") == frozenset(["binary"]) + fmt = FormatControl(set([":all:"]), set(["fred"])) + assert fmt_ctl_formats(fmt, "fred") == frozenset(["binary"]) diff --git a/tests/unit/test_index.py b/tests/unit/test_index.py index a8838a95e12..625b38d6a7b 100644 --- a/tests/unit/test_index.py +++ b/tests/unit/test_index.py @@ -1,60 +1,41 @@ -import os -from pip.backwardcompat import urllib -from tests.lib.path import Path -from pip.index import package_to_requirement, HTMLPage -from pip.index import PackageFinder, Link, INSTALLED_VERSION -from tests.lib import path_to_url -from string import ascii_lowercase -from mock import patch +import os.path +import pytest - -def test_package_name_should_be_converted_to_requirement(): - """ - Test that it translates a name like Foo-1.2 to Foo==1.3 - """ - assert package_to_requirement('Foo-1.2') == 'Foo==1.2' - assert package_to_requirement('Foo-dev') == 'Foo==dev' - assert package_to_requirement('Foo') == 'Foo' +from pip.download import PipSession +from pip.index import HTMLPage +from pip.index import PackageFinder, Link -def test_html_page_should_be_able_to_scrap_rel_links(): +def test_sort_locations_file_expand_dir(data): """ - Test scraping page looking for url in href + Test that a file:// dir gets listdir run with expand_dir """ - page = HTMLPage(""" - -
  • - Home Page: - - http://supervisord.org/ -
  • """, "supervisor") - - links = list(page.scraped_rel_links()) - assert len(links) == 1 - assert links[0].url == 'http://supervisord.org/' - - -def test_sort_locations_file_find_link(data): - """ - Test that a file:// find-link dir gets listdir run - """ - finder = PackageFinder([data.find_links], []) - files, urls = finder._sort_locations([data.find_links]) - assert files and not urls, "files and not urls should have been found at find-links url: %s" % data.find_links + finder = PackageFinder([data.find_links], [], session=PipSession()) + files, urls = finder._sort_locations([data.find_links], expand_dir=True) + assert files and not urls, ( + "files and not urls should have been found at find-links url: %s" % + data.find_links + ) def test_sort_locations_file_not_find_link(data): """ - Test that a file:// url dir that's not a find-link, doesn't get a listdir run + Test that a file:// url dir that's not a find-link, doesn't get a listdir + run """ - finder = PackageFinder([], []) - files, urls = finder._sort_locations(data.index_url("empty_with_pkg")) + finder = PackageFinder([], [], session=PipSession()) + files, urls = finder._sort_locations([data.index_url("empty_with_pkg")]) assert urls and not files, "urls, but not files should have been found" -def test_INSTALLED_VERSION_greater(): - """Test INSTALLED_VERSION compares greater.""" - assert INSTALLED_VERSION > Link("some link") +def test_sort_locations_non_existing_path(): + """ + Test that a non-existing path is ignored. + """ + finder = PackageFinder([], [], session=PipSession()) + files, urls = finder._sort_locations( + [os.path.join('this', 'doesnt', 'exist')]) + assert not urls and not files, "nothing should have been found" class TestLink(object): @@ -62,9 +43,19 @@ class TestLink(object): def test_splitext(self): assert ('wheel', '.whl') == Link('http://yo/wheel.whl').splitext() - def test_filename(self): - assert 'wheel.whl' == Link('http://yo/wheel.whl').filename - assert 'wheel' == Link('http://yo/wheel').filename + @pytest.mark.parametrize( + ("url", "expected"), + [ + ("http://yo/wheel.whl", "wheel.whl"), + ("http://yo/wheel", "wheel"), + ( + "http://yo/myproject-1.0%2Bfoobar.0-py2.py3-none-any.whl", + "myproject-1.0+foobar.0-py2.py3-none-any.whl", + ), + ], + ) + def test_filename(self, url, expected): + assert Link(url).filename == expected def test_no_ext(self): assert '' == Link('http://yo/wheel').ext @@ -78,4 +69,73 @@ def test_ext_fragment(self): def test_ext_query(self): assert '.whl' == Link('http://yo/wheel.whl?a=b').ext - + def test_is_wheel(self): + assert Link('http://yo/wheel.whl').is_wheel + + def test_is_wheel_false(self): + assert not Link('http://yo/not_a_wheel').is_wheel + + def test_fragments(self): + url = 'git+https://example.com/package#egg=eggname' + assert 'eggname' == Link(url).egg_fragment + assert None is Link(url).subdirectory_fragment + url = 'git+https://example.com/package#egg=eggname&subdirectory=subdir' + assert 'eggname' == Link(url).egg_fragment + assert 'subdir' == Link(url).subdirectory_fragment + url = 'git+https://example.com/package#subdirectory=subdir&egg=eggname' + assert 'eggname' == Link(url).egg_fragment + assert 'subdir' == Link(url).subdirectory_fragment + + +@pytest.mark.parametrize( + ("html", "url", "expected"), + [ + ("", "https://example.com/", "https://example.com/"), + ( + "" + "" + "", + "https://example.com/", + "https://foo.example.com/", + ), + ( + "" + "" + "", + "https://example.com/", + "https://foo.example.com/", + ), + ], +) +def test_base_url(html, url, expected): + assert HTMLPage(html, url).base_url == expected + + +class MockLogger(object): + def __init__(self): + self.called = False + + def warning(self, *args, **kwargs): + self.called = True + + +@pytest.mark.parametrize( + ("location", "trusted", "expected"), + [ + ("http://pypi.python.org/something", [], True), + ("https://pypi.python.org/something", [], False), + ("git+http://pypi.python.org/something", [], True), + ("git+https://pypi.python.org/something", [], False), + ("git+ssh://git@pypi.python.org/something", [], False), + ("http://localhost", [], False), + ("http://127.0.0.1", [], False), + ("http://example.com/something/", [], True), + ("http://example.com/something/", ["example.com"], False), + ("http://eXample.com/something/", ["example.cOm"], False), + ], +) +def test_secure_origin(location, trusted, expected): + finder = PackageFinder([], [], session=[], trusted_hosts=trusted) + logger = MockLogger() + finder._validate_secure_origin(logger, location) + assert logger.called == expected diff --git a/tests/unit/test_locations.py b/tests/unit/test_locations.py index 1e1cc243732..0489e55fb7e 100644 --- a/tests/unit/test_locations.py +++ b/tests/unit/test_locations.py @@ -8,17 +8,14 @@ import tempfile import getpass -import pytest - from mock import Mock -import pip from pip.locations import distutils_scheme if sys.platform == 'win32': - pwd = Mock() + pwd = Mock() else: - import pwd + import pwd class TestLocations: @@ -43,10 +40,10 @@ def patch(self): self.old_getpass_getuser = getpass.getuser # now patch - tempfile.gettempdir = lambda : self.tempdir - getpass.getuser = lambda : self.username - os.geteuid = lambda : self.st_uid - os.fstat = lambda fd : self.get_mock_fstat(fd) + tempfile.gettempdir = lambda: self.tempdir + getpass.getuser = lambda: self.username + os.geteuid = lambda: self.st_uid + os.fstat = lambda fd: self.get_mock_fstat(fd) if sys.platform != 'win32': pwd.getpwuid = lambda uid: self.get_mock_getpwuid(uid) @@ -77,103 +74,54 @@ def get_mock_getpwuid(self, uid): result.pw_name = self.username return result - def get_build_dir_location(self): - """ returns a string pointing to the - current build_prefix. - """ - return os.path.join(self.tempdir, 'pip_build_%s' % self.username) - - def test_dir_path(self): - """ test the path name for the build_prefix - """ - from pip import locations - assert locations._get_build_prefix() == self.get_build_dir_location() - - #skip on windows, build dir is not created - @pytest.mark.skipif("sys.platform == 'win32'") - @pytest.mark.skipif("not hasattr(os, 'O_NOFOLLOW')") - def test_dir_created(self): - """ test that the build_prefix directory is generated when - _get_build_prefix is called. - """ - assert not os.path.exists(self.get_build_dir_location() ), \ - "the build_prefix directory should not exist yet!" - from pip import locations - locations._get_build_prefix() - assert os.path.exists(self.get_build_dir_location() ), \ - "the build_prefix directory should now exist!" - - - #skip on windows, build dir is not created - @pytest.mark.skipif("sys.platform == 'win32'") - def test_dir_created_without_NOFOLLOW(self, monkeypatch): - """ test that the build_prefix directory is generated when - os.O_NOFOLLOW doen't exist - """ - if hasattr(os, 'O_NOFOLLOW'): - monkeypatch.delattr("os.O_NOFOLLOW") - assert not os.path.exists(self.get_build_dir_location() ), \ - "the build_prefix directory should not exist yet!" - from pip import locations - locations._get_build_prefix() - assert os.path.exists(self.get_build_dir_location() ), \ - "the build_prefix directory should now exist!" - - #skip on windows; this exception logic only runs on linux - @pytest.mark.skipif("sys.platform == 'win32'") - @pytest.mark.skipif("not hasattr(os, 'O_NOFOLLOW')") - def test_error_raised_when_owned_by_another(self): - """ test calling _get_build_prefix when there is a temporary - directory owned by another user raises an InstallationError. - """ - from pip import locations - os.geteuid = lambda : 1111 - os.mkdir(self.get_build_dir_location() ) - - with pytest.raises(pip.exceptions.InstallationError): - locations._get_build_prefix() - - #skip on windows; this exception logic only runs on linux - @pytest.mark.skipif("sys.platform == 'win32'") - def test_error_raised_when_owned_by_another_without_NOFOLLOW(self, monkeypatch): - """ test calling _get_build_prefix when there is a temporary - directory owned by another user raises an InstallationError. - (when os.O_NOFOLLOW doesn't exist - """ - if hasattr(os, 'O_NOFOLLOW'): - monkeypatch.delattr("os.O_NOFOLLOW") - from pip import locations - os.geteuid = lambda : 1111 - os.mkdir(self.get_build_dir_location() ) - - with pytest.raises(pip.exceptions.InstallationError): - locations._get_build_prefix() - - - def test_no_error_raised_when_owned_by_you(self): - """ test calling _get_build_prefix when there is a temporary - directory owned by you raise no InstallationError. - """ - from pip import locations - os.mkdir(self.get_build_dir_location()) - locations._get_build_prefix() - class TestDisutilsScheme: - def test_root_modifies_appropiately(self): + def test_root_modifies_appropriately(self, monkeypatch): + # This deals with nt/posix path differences + # root is c:\somewhere\else or /somewhere/else + root = os.path.normcase(os.path.abspath( + os.path.join(os.path.sep, 'somewhere', 'else'))) norm_scheme = distutils_scheme("example") - root_scheme = distutils_scheme("example", root="/test/root/") + root_scheme = distutils_scheme("example", root=root) for key, value in norm_scheme.items(): - expected = os.path.join("/test/root/", os.path.abspath(value)[1:]) - assert root_scheme[key] == expected + drive, path = os.path.splitdrive(os.path.abspath(value)) + expected = os.path.join(root, path[1:]) + assert os.path.abspath(root_scheme[key]) == expected def test_distutils_config_file_read(self, tmpdir, monkeypatch): - f = tmpdir.mkdir("config").join("setup.cfg") - f.write("[install]\ninstall-scripts=/somewhere/else") - from distutils.dist import Distribution - # patch the function that returns what config files are present - monkeypatch.setattr(Distribution, 'find_config_files', lambda self: [f]) - scheme = distutils_scheme('example') - assert scheme['scripts'] == '/somewhere/else' + # This deals with nt/posix path differences + install_scripts = os.path.normcase(os.path.abspath( + os.path.join(os.path.sep, 'somewhere', 'else'))) + f = tmpdir.mkdir("config").join("setup.cfg") + f.write("[install]\ninstall-scripts=" + install_scripts) + from distutils.dist import Distribution + # patch the function that returns what config files are present + monkeypatch.setattr( + Distribution, + 'find_config_files', + lambda self: [f], + ) + scheme = distutils_scheme('example') + assert scheme['scripts'] == install_scripts + + # when we request install-lib, we should install everything (.py & + # .so) into that path; i.e. ensure platlib & purelib are set to + # this path + def test_install_lib_takes_precedence(self, tmpdir, monkeypatch): + # This deals with nt/posix path differences + install_lib = os.path.normcase(os.path.abspath( + os.path.join(os.path.sep, 'somewhere', 'else'))) + f = tmpdir.mkdir("config").join("setup.cfg") + f.write("[install]\ninstall-lib=" + install_lib) + from distutils.dist import Distribution + # patch the function that returns what config files are present + monkeypatch.setattr( + Distribution, + 'find_config_files', + lambda self: [f], + ) + scheme = distutils_scheme('example') + assert scheme['platlib'] == install_lib + os.path.sep + assert scheme['purelib'] == install_lib + os.path.sep diff --git a/tests/unit/test_log.py b/tests/unit/test_log.py deleted file mode 100644 index e1145ef9774..00000000000 --- a/tests/unit/test_log.py +++ /dev/null @@ -1,74 +0,0 @@ -from pip.backwardcompat import StringIO -from pip.log import should_color, should_warn, Logger - - -def test_should_color_std(): - assert not should_color(object(), {}, std=[object()]) - - -def test_should_color_isatty(): - class FakeTTY(object): - def isatty(self): - return True - - consumer = FakeTTY() - assert should_color(consumer, {}, std=[consumer]) - - -def test_should_color_environ(): - consumer = object() - assert should_color(consumer, {"TERM": "ANSI"}, std=[consumer]) - - -def test_should_color_notty_environ(): - consumer = object() - assert not should_color(consumer, {}, std=[consumer]) - - -def test_should_warn_greater_one_minor(): - assert should_warn("1.4", "1.6") - - -def test_should_warn_exactly_one_minor(): - assert not should_warn("1.5", "1.6") - - -def test_should_warn_equal(): - assert not should_warn("1.6", "1.6") - - -def test_should_warn_greater(): - assert not should_warn("1.7", "1.6") - - -def test_should_warn_significance(): - assert should_warn("1.4.dev1", "1.6") - - -def test_log_no_extra_line_break(): - """ - Confirm that multiple `.write()` consumers doesn't result in additional '\n's per write - """ - consumer1 = StringIO() - consumer2 = StringIO() - logger = Logger() - logger.add_consumers( - (logger.NOTIFY, consumer1), - (logger.NOTIFY, consumer2) - ) - logger.notify("one line") - # splitlines(True) will detect empty line-breaks - assert 1 == len(consumer1.getvalue().splitlines(True)) - assert 1 == len(consumer2.getvalue().splitlines(True)) - -def test_level_for_integer(): - logger = Logger() - assert logger.VERBOSE_DEBUG == logger.level_for_integer(-1000) - assert logger.VERBOSE_DEBUG == logger.level_for_integer(0) - assert logger.DEBUG == logger.level_for_integer(1) - assert logger.INFO == logger.level_for_integer(2) - assert logger.NOTIFY == logger.level_for_integer(3) - assert logger.WARN == logger.level_for_integer(4) - assert logger.ERROR == logger.level_for_integer(5) - assert logger.FATAL == logger.level_for_integer(6) - assert logger.FATAL == logger.level_for_integer(1000) diff --git a/tests/unit/test_options.py b/tests/unit/test_options.py index 56be3ae771a..16825322aaa 100644 --- a/tests/unit/test_options.py +++ b/tests/unit/test_options.py @@ -4,13 +4,18 @@ from pip import main from pip import cmdoptions from pip.basecommand import Command -from pip.commands import commands +from pip.commands import commands_dict as commands + class FakeCommand(Command): name = 'fake' summary = name + def main(self, args): - index_opts = cmdoptions.make_option_group(cmdoptions.index_group, self.parser) + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) self.parser.add_option_group(index_opts) return self.parse_args(args) @@ -18,7 +23,8 @@ def main(self, args): class TestOptionPrecedence(object): """ Tests for confirming our option precedence: - cli -> environment -> subcommand config -> global config -> option defaults + cli -> environment -> subcommand config -> global config -> option + defaults """ def setup(self): @@ -31,16 +37,16 @@ def teardown(self): def get_config_section(self, section): config = { - 'global': [('timeout','-3')], - 'fake': [('timeout','-2')] - } + 'global': [('timeout', '-3')], + 'fake': [('timeout', '-2')], + } return config[section] def get_config_section_global(self, section): config = { - 'global': [('timeout','-3')], - 'fake': [] - } + 'global': [('timeout', '-3')], + 'fake': [], + } return config[section] def test_env_override_default_int(self): @@ -69,7 +75,7 @@ def test_env_override_default_choice(self): """ os.environ['PIP_EXISTS_ACTION'] = 'w' options, args = main(['fake']) - assert options.exists_action == ['w'] + assert options.exists_action == ['w'] os.environ['PIP_EXISTS_ACTION'] = 's w' options, args = main(['fake']) @@ -77,16 +83,16 @@ def test_env_override_default_choice(self): def test_env_alias_override_default(self): """ - When an option has multiple long forms, test that the technique of using - the env variable, "PIP_" works for all cases. + When an option has multiple long forms, test that the technique of + using the env variable, "PIP_" works for all cases. (e.g. PIP_LOG_FILE and PIP_LOCAL_LOG should all work) """ os.environ['PIP_LOG_FILE'] = 'override.log' options, args = main(['fake']) - assert options.log_file == 'override.log' + assert options.log == 'override.log' os.environ['PIP_LOCAL_LOG'] = 'override.log' options, args = main(['fake']) - assert options.log_file == 'override.log' + assert options.log == 'override.log' def test_cli_override_environment(self): """ @@ -100,7 +106,11 @@ def test_environment_override_config(self, monkeypatch): """ Test an environment variable overrides the config file """ - monkeypatch.setattr(pip.baseparser.ConfigOptionParser, "get_config_section", self.get_config_section) + monkeypatch.setattr( + pip.baseparser.ConfigOptionParser, + "get_config_section", + self.get_config_section, + ) os.environ['PIP_TIMEOUT'] = '-1' options, args = main(['fake']) assert options.timeout == -1 @@ -109,7 +119,11 @@ def test_commmand_config_override_global_config(self, monkeypatch): """ Test that command config overrides global config """ - monkeypatch.setattr(pip.baseparser.ConfigOptionParser, "get_config_section", self.get_config_section) + monkeypatch.setattr( + pip.baseparser.ConfigOptionParser, + "get_config_section", + self.get_config_section, + ) options, args = main(['fake']) assert options.timeout == -2 @@ -117,7 +131,11 @@ def test_global_config_is_used(self, monkeypatch): """ Test that global config is used """ - monkeypatch.setattr(pip.baseparser.ConfigOptionParser, "get_config_section", self.get_config_section_global) + monkeypatch.setattr( + pip.baseparser.ConfigOptionParser, + "get_config_section", + self.get_config_section_global, + ) options, args = main(['fake']) assert options.timeout == -3 @@ -165,7 +183,8 @@ def teardown(self): def test_require_virtualenv(self): options1, args1 = main(['--require-virtualenv', 'fake']) options2, args2 = main(['fake', '--require-virtualenv']) - assert options1.require_venv == options2.require_venv == True + assert options1.require_venv + assert options2.require_venv def test_verbose(self): options1, args1 = main(['--verbose', 'fake']) @@ -177,31 +196,40 @@ def test_quiet(self): options2, args2 = main(['fake', '--quiet']) assert options1.quiet == options2.quiet == 1 + options3, args3 = main(['--quiet', '--quiet', 'fake']) + options4, args4 = main(['fake', '--quiet', '--quiet']) + assert options3.quiet == options4.quiet == 2 + + options5, args5 = main(['--quiet', '--quiet', '--quiet', 'fake']) + options6, args6 = main(['fake', '--quiet', '--quiet', '--quiet']) + assert options5.quiet == options6.quiet == 3 + def test_log(self): options1, args1 = main(['--log', 'path', 'fake']) options2, args2 = main(['fake', '--log', 'path']) assert options1.log == options2.log == 'path' - def test_log_explicit_levels(self): - options1, args1 = main(['--log-explicit-levels', 'fake']) - options2, args2 = main(['fake', '--log-explicit-levels']) - assert options1.log_explicit_levels == options2.log_explicit_levels == True - def test_local_log(self): options1, args1 = main(['--local-log', 'path', 'fake']) options2, args2 = main(['fake', '--local-log', 'path']) - assert options1.log_file == options2.log_file == 'path' + assert options1.log == options2.log == 'path' def test_no_input(self): options1, args1 = main(['--no-input', 'fake']) options2, args2 = main(['fake', '--no-input']) - assert options1.no_input == options2.no_input == True + assert options1.no_input + assert options2.no_input def test_proxy(self): options1, args1 = main(['--proxy', 'path', 'fake']) options2, args2 = main(['fake', '--proxy', 'path']) assert options1.proxy == options2.proxy == 'path' + def test_retries(self): + options1, args1 = main(['--retries', '-1', 'fake']) + options2, args2 = main(['fake', '--retries', '-1']) + assert options1.retries == options2.retries == -1 + def test_timeout(self): options1, args1 = main(['--timeout', '-1', 'fake']) options2, args2 = main(['fake', '--timeout', '-1']) @@ -215,7 +243,8 @@ def test_default_vcs(self): def test_skip_requirements_regex(self): options1, args1 = main(['--skip-requirements-regex', 'path', 'fake']) options2, args2 = main(['fake', '--skip-requirements-regex', 'path']) - assert options1.skip_requirements_regex == options2.skip_requirements_regex == 'path' + assert options1.skip_requirements_regex == 'path' + assert options2.skip_requirements_regex == 'path' def test_exists_action(self): options1, args1 = main(['--exists-action', 'w', 'fake']) @@ -226,3 +255,33 @@ def test_cert(self): options1, args1 = main(['--cert', 'path', 'fake']) options2, args2 = main(['fake', '--cert', 'path']) assert options1.cert == options2.cert == 'path' + + def test_client_cert(self): + options1, args1 = main(['--client-cert', 'path', 'fake']) + options2, args2 = main(['fake', '--client-cert', 'path']) + assert options1.client_cert == options2.client_cert == 'path' + + +class TestOptionsConfigFiles(object): + + def test_venv_config_file_found(self, monkeypatch): + # We only want a dummy object to call the get_config_files method + monkeypatch.setattr( + pip.baseparser.ConfigOptionParser, + '__init__', + lambda self: None, + ) + + # strict limit on the site_config_files list + monkeypatch.setattr(pip.baseparser, 'site_config_files', ['/a/place']) + + # If we are running in a virtualenv and all files appear to exist, + # we should see two config files. + monkeypatch.setattr( + pip.baseparser, + 'running_under_virtualenv', + lambda: True, + ) + monkeypatch.setattr(os.path, 'exists', lambda filename: True) + cp = pip.baseparser.ConfigOptionParser() + assert len(cp.get_config_files()) == 4 diff --git a/tests/unit/test_pep425tags.py b/tests/unit/test_pep425tags.py new file mode 100644 index 00000000000..6d12e47fc13 --- /dev/null +++ b/tests/unit/test_pep425tags.py @@ -0,0 +1,161 @@ +import sys +from mock import patch +from pip import pep425tags + + +class TestPEP425Tags(object): + + def mock_get_config_var(self, **kwd): + """ + Patch sysconfig.get_config_var for arbitrary keys. + """ + import pip.pep425tags + + get_config_var = pip.pep425tags.sysconfig.get_config_var + + def _mock_get_config_var(var): + if var in kwd: + return kwd[var] + return get_config_var(var) + return _mock_get_config_var + + def abi_tag_unicode(self, flags, config_vars): + """ + Used to test ABI tags, verify correct use of the `u` flag + """ + import pip.pep425tags + + config_vars.update({'SOABI': None}) + base = pip.pep425tags.get_abbr_impl() + pip.pep425tags.get_impl_ver() + + if sys.version_info < (3, 3): + config_vars.update({'Py_UNICODE_SIZE': 2}) + mock_gcf = self.mock_get_config_var(**config_vars) + with patch('pip.pep425tags.sysconfig.get_config_var', mock_gcf): + abi_tag = pip.pep425tags.get_abi_tag() + assert abi_tag == base + flags + + config_vars.update({'Py_UNICODE_SIZE': 4}) + mock_gcf = self.mock_get_config_var(**config_vars) + with patch('pip.pep425tags.sysconfig.get_config_var', mock_gcf): + abi_tag = pip.pep425tags.get_abi_tag() + assert abi_tag == base + flags + 'u' + + else: + # On Python >= 3.3, UCS-4 is essentially permanently enabled, and + # Py_UNICODE_SIZE is None. SOABI on these builds does not include + # the 'u' so manual SOABI detection should not do so either. + config_vars.update({'Py_UNICODE_SIZE': None}) + mock_gcf = self.mock_get_config_var(**config_vars) + with patch('pip.pep425tags.sysconfig.get_config_var', mock_gcf): + abi_tag = pip.pep425tags.get_abi_tag() + assert abi_tag == base + flags + + def test_broken_sysconfig(self): + """ + Test that pep425tags still works when sysconfig is broken. + Can be a problem on Python 2.7 + Issue #1074. + """ + import pip.pep425tags + + def raises_ioerror(var): + raise IOError("I have the wrong path!") + + with patch('pip.pep425tags.sysconfig.get_config_var', raises_ioerror): + assert len(pip.pep425tags.get_supported()) + + def test_no_hyphen_tag(self): + """ + Test that no tag contains a hyphen. + """ + import pip.pep425tags + + mock_gcf = self.mock_get_config_var(SOABI='cpython-35m-darwin') + + with patch('pip.pep425tags.sysconfig.get_config_var', mock_gcf): + supported = pip.pep425tags.get_supported() + + for (py, abi, plat) in supported: + assert '-' not in py + assert '-' not in abi + assert '-' not in plat + + def test_manual_abi_noflags(self): + """ + Test that no flags are set on a non-PyDebug, non-Pymalloc ABI tag. + """ + self.abi_tag_unicode('', {'Py_DEBUG': False, 'WITH_PYMALLOC': False}) + + def test_manual_abi_d_flag(self): + """ + Test that the `d` flag is set on a PyDebug, non-Pymalloc ABI tag. + """ + self.abi_tag_unicode('d', {'Py_DEBUG': True, 'WITH_PYMALLOC': False}) + + def test_manual_abi_m_flag(self): + """ + Test that the `m` flag is set on a non-PyDebug, Pymalloc ABI tag. + """ + self.abi_tag_unicode('m', {'Py_DEBUG': False, 'WITH_PYMALLOC': True}) + + def test_manual_abi_dm_flags(self): + """ + Test that the `dm` flags are set on a PyDebug, Pymalloc ABI tag. + """ + self.abi_tag_unicode('dm', {'Py_DEBUG': True, 'WITH_PYMALLOC': True}) + + +class TestManylinux1Tags(object): + + @patch('pip.pep425tags.get_platform', lambda: 'linux_x86_64') + @patch('pip.utils.glibc.have_compatible_glibc', lambda major, minor: True) + def test_manylinux1_compatible_on_linux_x86_64(self): + """ + Test that manylinux1 is enabled on linux_x86_64 + """ + assert pep425tags.is_manylinux1_compatible() + + @patch('pip.pep425tags.get_platform', lambda: 'linux_i686') + @patch('pip.utils.glibc.have_compatible_glibc', lambda major, minor: True) + def test_manylinux1_compatible_on_linux_i686(self): + """ + Test that manylinux1 is enabled on linux_i686 + """ + assert pep425tags.is_manylinux1_compatible() + + @patch('pip.pep425tags.get_platform', lambda: 'linux_x86_64') + @patch('pip.utils.glibc.have_compatible_glibc', lambda major, minor: False) + def test_manylinux1_2(self): + """ + Test that manylinux1 is disabled with incompatible glibc + """ + assert not pep425tags.is_manylinux1_compatible() + + @patch('pip.pep425tags.get_platform', lambda: 'arm6vl') + @patch('pip.utils.glibc.have_compatible_glibc', lambda major, minor: True) + def test_manylinux1_3(self): + """ + Test that manylinux1 is disabled on arm6vl + """ + assert not pep425tags.is_manylinux1_compatible() + + @patch('pip.pep425tags.get_platform', lambda: 'linux_x86_64') + @patch('pip.utils.glibc.have_compatible_glibc', lambda major, minor: True) + @patch('sys.platform', 'linux2') + def test_manylinux1_tag_is_first(self): + """ + Test that the more specific tag manylinux1 comes first. + """ + groups = {} + for pyimpl, abi, arch in pep425tags.get_supported(): + groups.setdefault((pyimpl, abi), []).append(arch) + + for arches in groups.values(): + if arches == ['any']: + continue + # Expect the most specific arch first: + if len(arches) == 3: + assert arches == ['manylinux1_x86_64', 'linux_x86_64', 'any'] + else: + assert arches == ['manylinux1_x86_64', 'linux_x86_64'] diff --git a/tests/unit/test_req.py b/tests/unit/test_req.py index 227c1e32346..3f11909cbcb 100644 --- a/tests/unit/test_req.py +++ b/tests/unit/test_req.py @@ -1,39 +1,42 @@ import os import shutil +import sys import tempfile import pytest -import pip.wheel - -from pkg_resources import Distribution from mock import Mock, patch, mock_open -from pip.exceptions import PreviousBuildDirError, InvalidWheelFilename, UnsupportedWheel +from pip.commands.install import InstallCommand +from pip.exceptions import (PreviousBuildDirError, InvalidWheelFilename, + InstallationError, HashErrors) +from pip.download import path_to_url, PipSession from pip.index import PackageFinder -from pip.log import logger -from pip.req import (read_text_file, InstallRequirement, RequirementSet, - parse_editable, Requirements, parse_requirements) -from tests.lib import assert_raises_regexp +from pip.req import (InstallRequirement, RequirementSet, Requirements) +from pip.req.req_file import process_line +from pip.req.req_install import parse_editable +from pip.utils import read_text_file +from pip._vendor import pkg_resources +from pip._vendor.packaging.requirements import Requirement +from tests.lib import assert_raises_regexp, requirements_file class TestRequirementSet(object): """RequirementSet tests""" def setup(self): - logger.consumers = [(logger.NOTIFY, Mock())] self.tempdir = tempfile.mkdtemp() def teardown(self): - logger.consumers = [] shutil.rmtree(self.tempdir, ignore_errors=True) - def basic_reqset(self): + def basic_reqset(self, **kwargs): return RequirementSet( build_dir=os.path.join(self.tempdir, 'build'), src_dir=os.path.join(self.tempdir, 'src'), download_dir=None, - download_cache=os.path.join(self.tempdir, 'download_cache') - ) + session=PipSession(), + **kwargs + ) def test_no_reuse_existing_build_dir(self, data): """Test prepare_files raise exception with previous build dir""" @@ -44,13 +47,242 @@ def test_no_reuse_existing_build_dir(self, data): reqset = self.basic_reqset() req = InstallRequirement.from_line('simple') reqset.add_requirement(req) - finder = PackageFinder([data.find_links], []) + finder = PackageFinder([data.find_links], [], session=PipSession()) assert_raises_regexp( PreviousBuildDirError, - "pip can't proceed with [\s\S]*%s[\s\S]*%s" % (req, build_dir.replace('\\', '\\\\')), + "pip can't proceed with [\s\S]*%s[\s\S]*%s" % + (req, build_dir.replace('\\', '\\\\')), + reqset.prepare_files, + finder, + ) + + def test_environment_marker_extras(self, data): + """ + Test that the environment marker extras are used with + non-wheel installs. + """ + reqset = self.basic_reqset() + req = InstallRequirement.from_editable( + data.packages.join("LocalEnvironMarker")) + reqset.add_requirement(req) + finder = PackageFinder([data.find_links], [], session=PipSession()) + reqset.prepare_files(finder) + # This is hacky but does test both case in py2 and py3 + if sys.version_info[:2] in ((2, 7), (3, 4)): + assert reqset.has_requirement('simple') + else: + assert not reqset.has_requirement('simple') + + @pytest.mark.network + def test_missing_hash_checking(self, data): + """Make sure prepare_files() raises an error when a requirement has no + hash in implicit hash-checking mode. + """ + reqset = self.basic_reqset() + # No flags here. This tests that detection of later flags nonetheless + # requires earlier packages to have hashes: + reqset.add_requirement( + list(process_line('blessings==1.0', 'file', 1))[0]) + # This flag activates --require-hashes mode: + reqset.add_requirement( + list(process_line('tracefront==0.1 --hash=sha256:somehash', + 'file', + 2))[0]) + # This hash should be accepted because it came from the reqs file, not + # from the internet: + reqset.add_requirement( + list(process_line('https://pypi.python.org/packages/source/m/more-' + 'itertools/more-itertools-1.0.tar.gz#md5=b21850c' + '3cfa7efbb70fd662ab5413bdd', 'file', 3))[0]) + # The error text should list this as a URL and not `peep==3.1.1`: + reqset.add_requirement( + list(process_line('https://pypi.python.org/packages/source/p/peep/' + 'peep-3.1.1.tar.gz', + 'file', + 4))[0]) + finder = PackageFinder([], + ['https://pypi.python.org/simple'], + session=PipSession()) + assert_raises_regexp( + HashErrors, + r'Hashes are required in --require-hashes mode, but they are ' + r'missing .*\n' + r' https://pypi\.python\.org/packages/source/p/peep/peep' + r'-3\.1\.1\.tar\.gz --hash=sha256:[0-9a-f]+\n' + r' blessings==1.0 --hash=sha256:[0-9a-f]+\n' + r'THESE PACKAGES DO NOT MATCH THE HASHES.*\n' + r' tracefront==0.1 .*:\n' + r' Expected sha256 somehash\n' + r' Got [0-9a-f]+$', reqset.prepare_files, - finder - ) + finder) + + def test_missing_hash_with_require_hashes(self, data): + """Setting --require-hashes explicitly should raise errors if hashes + are missing. + """ + reqset = self.basic_reqset(require_hashes=True) + reqset.add_requirement( + list(process_line('simple==1.0', 'file', 1))[0]) + finder = PackageFinder([data.find_links], [], session=PipSession()) + assert_raises_regexp( + HashErrors, + r'Hashes are required in --require-hashes mode, but they are ' + r'missing .*\n' + r' simple==1.0 --hash=sha256:393043e672415891885c9a2a0929b1af95' + r'fb866d6ca016b42d2e6ce53619b653$', + reqset.prepare_files, + finder) + + def test_missing_hash_with_require_hashes_in_reqs_file(self, data, tmpdir): + """--require-hashes in a requirements file should make its way to the + RequirementSet. + """ + req_set = self.basic_reqset(require_hashes=False) + session = PipSession() + finder = PackageFinder([data.find_links], [], session=session) + command = InstallCommand() + with requirements_file('--require-hashes', tmpdir) as reqs_file: + options, args = command.parse_args(['-r', reqs_file]) + command.populate_requirement_set( + req_set, args, options, finder, session, command.name, + wheel_cache=None) + assert req_set.require_hashes + + def test_unsupported_hashes(self, data): + """VCS and dir links should raise errors when --require-hashes is + on. + + In addition, complaints about the type of requirement (VCS or dir) + should trump the presence or absence of a hash. + + """ + reqset = self.basic_reqset(require_hashes=True) + reqset.add_requirement( + list(process_line( + 'git+git://github.com/pypa/pip-test-package --hash=sha256:123', + 'file', + 1))[0]) + dir_path = data.packages.join('FSPkg') + reqset.add_requirement( + list(process_line( + 'file://%s' % (dir_path,), + 'file', + 2))[0]) + finder = PackageFinder([data.find_links], [], session=PipSession()) + sep = os.path.sep + if sep == '\\': + sep = '\\\\' # This needs to be escaped for the regex + assert_raises_regexp( + HashErrors, + r"Can't verify hashes for these requirements because we don't " + r"have a way to hash version control repositories:\n" + r" git\+git://github\.com/pypa/pip-test-package \(from -r file " + r"\(line 1\)\)\n" + r"Can't verify hashes for these file:// requirements because they " + r"point to directories:\n" + r" file://.*{sep}data{sep}packages{sep}FSPkg " + "\(from -r file \(line 2\)\)".format(sep=sep), + reqset.prepare_files, + finder) + + def test_unpinned_hash_checking(self, data): + """Make sure prepare_files() raises an error when a requirement is not + version-pinned in hash-checking mode. + """ + reqset = self.basic_reqset() + # Test that there must be exactly 1 specifier: + reqset.add_requirement( + list(process_line('simple --hash=sha256:a90427ae31f5d1d0d7ec06ee97' + 'd9fcf2d0fc9a786985250c1c83fd68df5911dd', + 'file', + 1))[0]) + # Test that the operator must be ==: + reqset.add_requirement(list(process_line( + 'simple2>1.0 --hash=sha256:3ad45e1e9aa48b4462af0' + '123f6a7e44a9115db1ef945d4d92c123dfe21815a06', + 'file', + 2))[0]) + finder = PackageFinder([data.find_links], [], session=PipSession()) + assert_raises_regexp( + HashErrors, + # Make sure all failing requirements are listed: + r'versions pinned with ==. These do not:\n' + r' simple .* \(from -r file \(line 1\)\)\n' + r' simple2>1.0 .* \(from -r file \(line 2\)\)', + reqset.prepare_files, + finder) + + def test_hash_mismatch(self, data): + """A hash mismatch should raise an error.""" + file_url = path_to_url( + (data.packages / 'simple-1.0.tar.gz').abspath) + reqset = self.basic_reqset(require_hashes=True) + reqset.add_requirement( + list(process_line('%s --hash=sha256:badbad' % file_url, + 'file', + 1))[0]) + finder = PackageFinder([data.find_links], [], session=PipSession()) + assert_raises_regexp( + HashErrors, + r'THESE PACKAGES DO NOT MATCH THE HASHES.*\n' + r' file:///.*/data/packages/simple-1\.0\.tar\.gz .*:\n' + r' Expected sha256 badbad\n' + r' Got 393043e672415891885c9a2a0929b1af95fb866d' + r'6ca016b42d2e6ce53619b653$', + reqset.prepare_files, + finder) + + def test_unhashed_deps_on_require_hashes(self, data): + """Make sure unhashed, unpinned, or otherwise unrepeatable + dependencies get complained about when --require-hashes is on.""" + reqset = self.basic_reqset() + finder = PackageFinder([data.find_links], [], session=PipSession()) + reqset.add_requirement(next(process_line( + 'TopoRequires2==0.0.1 ' # requires TopoRequires + '--hash=sha256:eaf9a01242c9f2f42cf2bd82a6a848cd' + 'e3591d14f7896bdbefcf48543720c970', + 'file', 1))) + assert_raises_regexp( + HashErrors, + r'In --require-hashes mode, all requirements must have their ' + r'versions pinned.*\n' + r' TopoRequires from .*$', + reqset.prepare_files, + finder) + + def test_hashed_deps_on_require_hashes(self, data): + """Make sure hashed dependencies get installed when --require-hashes + is on. + + (We actually just check that no "not all dependencies are hashed!" + error gets raised while preparing; there is no reason to expect + installation to then fail, as the code paths are the same as ever.) + + """ + reqset = self.basic_reqset() + reqset.add_requirement(next(process_line( + 'TopoRequires2==0.0.1 ' # requires TopoRequires + '--hash=sha256:eaf9a01242c9f2f42cf2bd82a6a848cd' + 'e3591d14f7896bdbefcf48543720c970', + 'file', 1))) + reqset.add_requirement(next(process_line( + 'TopoRequires==0.0.1 ' + '--hash=sha256:d6dd1e22e60df512fdcf3640ced3039b3b02a56ab2cee81ebcb' + '3d0a6d4e8bfa6', + 'file', 2))) + + def test_no_egg_on_require_hashes(self, data): + """Make sure --egg is illegal with --require-hashes. + + --egg would cause dependencies to always be installed, since it cedes + control directly to setuptools. + + """ + reqset = self.basic_reqset(require_hashes=True, as_egg=True) + finder = PackageFinder([data.find_links], [], session=PipSession()) + with pytest.raises(InstallationError): + reqset.prepare_files(finder) @pytest.mark.parametrize(('file_contents', 'expected'), [ @@ -62,28 +294,221 @@ def test_egg_info_data(file_contents, expected): om = mock_open(read_data=file_contents) em = Mock() em.return_value = 'cp1252' - with patch('pip.req.open', om, create=True): + with patch('pip.utils.open', om, create=True): with patch('locale.getpreferredencoding', em): ret = read_text_file('foo') assert ret == expected.decode('utf-8') class TestInstallRequirement(object): + def setup(self): + self.tempdir = tempfile.mkdtemp() + + def teardown(self): + shutil.rmtree(self.tempdir, ignore_errors=True) + + def basic_reqset(self, **kwargs): + return RequirementSet( + build_dir=os.path.join(self.tempdir, 'build'), + src_dir=os.path.join(self.tempdir, 'src'), + download_dir=None, + session=PipSession(), + **kwargs + ) def test_url_with_query(self): """InstallRequirement should strip the fragment, but not the query.""" url = 'http://foo.com/?p=bar.git;a=snapshot;h=v0.1;sf=tgz' fragment = '#egg=bar' req = InstallRequirement.from_line(url + fragment) - assert req.url == url, req.url + assert req.link.url == url + fragment, req.link - def test_unsupported_wheel_requirement_raises(self): - with pytest.raises(UnsupportedWheel): - req = InstallRequirement.from_line('peppercorn-0.4-py2.py3-bogus-any.whl') + def test_unsupported_wheel_link_requirement_raises(self): + reqset = self.basic_reqset() + req = InstallRequirement.from_line( + 'https://whatever.com/peppercorn-0.4-py2.py3-bogus-any.whl', + ) + assert req.link is not None + assert req.link.is_wheel + assert req.link.scheme == "https" + + with pytest.raises(InstallationError): + reqset.add_requirement(req) + + def test_unsupported_wheel_local_file_requirement_raises(self, data): + reqset = self.basic_reqset() + req = InstallRequirement.from_line( + data.packages.join('simple.dist-0.1-py1-none-invalid.whl'), + ) + assert req.link is not None + assert req.link.is_wheel + assert req.link.scheme == "file" + + with pytest.raises(InstallationError): + reqset.add_requirement(req) + + def test_installed_version_not_installed(self): + req = InstallRequirement.from_line('simple-0.1-py2.py3-none-any.whl') + assert req.installed_version is None + + def test_str(self): + req = InstallRequirement.from_line('simple==0.1') + assert str(req) == 'simple==0.1' + + def test_repr(self): + req = InstallRequirement.from_line('simple==0.1') + assert repr(req) == ( + '' + ) def test_invalid_wheel_requirement_raises(self): with pytest.raises(InvalidWheelFilename): - req = InstallRequirement.from_line('invalid.whl') + InstallRequirement.from_line('invalid.whl') + + def test_wheel_requirement_sets_req_attribute(self): + req = InstallRequirement.from_line('simple-0.1-py2.py3-none-any.whl') + assert isinstance(req.req, Requirement) + assert str(req.req) == 'simple==0.1' + + def test_url_preserved_line_req(self): + """Confirm the url is preserved in a non-editable requirement""" + url = 'git+http://foo.com@ref#egg=foo' + req = InstallRequirement.from_line(url) + assert req.link.url == url + + def test_url_preserved_editable_req(self): + """Confirm the url is preserved in a editable requirement""" + url = 'git+http://foo.com@ref#egg=foo' + req = InstallRequirement.from_editable(url) + assert req.link.url == url + + def test_get_dist(self): + req = InstallRequirement.from_line('foo') + req.egg_info_path = Mock(return_value='/path/to/foo.egg-info') + dist = req.get_dist() + assert isinstance(dist, pkg_resources.Distribution) + assert dist.project_name == 'foo' + assert dist.location == '/path/to' + + def test_get_dist_trailing_slash(self): + # Tests issue fixed by https://github.com/pypa/pip/pull/2530 + req = InstallRequirement.from_line('foo') + req.egg_info_path = Mock(return_value='/path/to/foo.egg-info/') + dist = req.get_dist() + assert isinstance(dist, pkg_resources.Distribution) + assert dist.project_name == 'foo' + assert dist.location == '/path/to' + + def test_markers(self): + for line in ( + # recommended syntax + 'mock3; python_version >= "3"', + # with more spaces + 'mock3 ; python_version >= "3" ', + # without spaces + 'mock3;python_version >= "3"', + ): + req = InstallRequirement.from_line(line) + assert req.req.name == 'mock3' + assert str(req.req.specifier) == '' + assert req.markers == 'python_version >= "3"' + + def test_markers_semicolon(self): + # check that the markers can contain a semicolon + req = InstallRequirement.from_line('semicolon; os_name == "a; b"') + assert req.req.name == 'semicolon' + assert str(req.req.specifier) == '' + assert req.markers == 'os_name == "a; b"' + + def test_markers_url(self): + # test "URL; markers" syntax + url = 'http://foo.com/?p=bar.git;a=snapshot;h=v0.1;sf=tgz' + line = '%s; python_version >= "3"' % url + req = InstallRequirement.from_line(line) + assert req.link.url == url, req.url + assert req.markers == 'python_version >= "3"' + + # without space, markers are part of the URL + url = 'http://foo.com/?p=bar.git;a=snapshot;h=v0.1;sf=tgz' + line = '%s;python_version >= "3"' % url + req = InstallRequirement.from_line(line) + assert req.link.url == line, req.url + assert req.markers is None + + def test_markers_match(self): + # match + for markers in ( + 'python_version >= "1.0"', + 'sys_platform == %r' % sys.platform, + ): + line = 'name; ' + markers + req = InstallRequirement.from_line(line) + assert req.markers == markers + assert req.match_markers() + + # don't match + for markers in ( + 'python_version >= "5.0"', + 'sys_platform != %r' % sys.platform, + ): + line = 'name; ' + markers + req = InstallRequirement.from_line(line) + assert req.markers == markers + assert not req.match_markers() + + def test_extras_for_line_path_requirement(self): + line = 'SomeProject[ex1,ex2]' + filename = 'filename' + comes_from = '-r %s (line %s)' % (filename, 1) + req = InstallRequirement.from_line(line, comes_from=comes_from) + assert len(req.extras) == 2 + assert req.extras == set(['ex1', 'ex2']) + + def test_extras_for_line_url_requirement(self): + line = 'git+https://url#egg=SomeProject[ex1,ex2]' + filename = 'filename' + comes_from = '-r %s (line %s)' % (filename, 1) + req = InstallRequirement.from_line(line, comes_from=comes_from) + assert len(req.extras) == 2 + assert req.extras == set(['ex1', 'ex2']) + + def test_extras_for_editable_path_requirement(self): + url = '.[ex1,ex2]' + filename = 'filename' + comes_from = '-r %s (line %s)' % (filename, 1) + req = InstallRequirement.from_editable(url, comes_from=comes_from) + assert len(req.extras) == 2 + assert req.extras == set(['ex1', 'ex2']) + + def test_extras_for_editable_url_requirement(self): + url = 'git+https://url#egg=SomeProject[ex1,ex2]' + filename = 'filename' + comes_from = '-r %s (line %s)' % (filename, 1) + req = InstallRequirement.from_editable(url, comes_from=comes_from) + assert len(req.extras) == 2 + assert req.extras == set(['ex1', 'ex2']) + + def test_unexisting_path(self): + with pytest.raises(InstallationError) as e: + InstallRequirement.from_line( + os.path.join('this', 'path', 'does', 'not', 'exist')) + err_msg = e.value.args[0] + assert "Invalid requirement" in err_msg + assert "It looks like a path. Does it exist ?" in err_msg + + def test_single_equal_sign(self): + with pytest.raises(InstallationError) as e: + InstallRequirement.from_line('toto=42') + err_msg = e.value.args[0] + assert "Invalid requirement" in err_msg + assert "= is not a valid operator. Did you mean == ?" in err_msg + + def test_traceback(self): + with pytest.raises(InstallationError) as e: + InstallRequirement.from_line('toto 42') + err_msg = e.value.args[0] + assert "Invalid requirement" in err_msg + assert "\nTraceback " in err_msg def test_requirements_data_structure_keeps_order(): @@ -111,102 +536,70 @@ def test_requirements_data_structure_implements__contains__(): assert 'pip' in requirements assert 'nose' not in requirements -@patch('os.path.normcase') -@patch('pip.req.os.getcwd') -@patch('pip.req.os.path.exists') -@patch('pip.req.os.path.isdir') -def test_parse_editable_local(isdir_mock, exists_mock, getcwd_mock, normcase_mock): + +@patch('pip.req.req_install.os.path.abspath') +@patch('pip.req.req_install.os.path.exists') +@patch('pip.req.req_install.os.path.isdir') +def test_parse_editable_local( + isdir_mock, exists_mock, abspath_mock): exists_mock.return_value = isdir_mock.return_value = True # mocks needed to support path operations on windows tests - normcase_mock.return_value = getcwd_mock.return_value = "/some/path" + abspath_mock.return_value = "/some/path" assert parse_editable('.', 'git') == (None, 'file:///some/path', None) - normcase_mock.return_value = "/some/path/foo" - assert parse_editable('foo', 'git') == (None, 'file:///some/path/foo', None) + abspath_mock.return_value = "/some/path/foo" + assert parse_editable('foo', 'git') == ( + None, 'file:///some/path/foo', None, + ) + def test_parse_editable_default_vcs(): - assert parse_editable('https://foo#egg=foo', 'git') == ('foo', - 'git+https://foo#egg=foo', - {'egg': 'foo'}) + assert parse_editable('https://foo#egg=foo', 'git') == ( + 'foo', + 'git+https://foo#egg=foo', + None, + ) + def test_parse_editable_explicit_vcs(): - assert parse_editable('svn+https://foo#egg=foo', 'git') == ('foo', - 'svn+https://foo#egg=foo', - {'egg': 'foo'}) + assert parse_editable('svn+https://foo#egg=foo', 'git') == ( + 'foo', + 'svn+https://foo#egg=foo', + None, + ) + def test_parse_editable_vcs_extras(): - assert parse_editable('svn+https://foo#egg=foo[extras]', 'git') == ('foo[extras]', - 'svn+https://foo#egg=foo[extras]', - {'egg': 'foo[extras]'}) - -@patch('os.path.normcase') -@patch('pip.req.os.getcwd') -@patch('pip.req.os.path.exists') -@patch('pip.req.os.path.isdir') -def test_parse_editable_local_extras(isdir_mock, exists_mock, getcwd_mock, normcase_mock): + assert parse_editable('svn+https://foo#egg=foo[extras]', 'git') == ( + 'foo[extras]', + 'svn+https://foo#egg=foo[extras]', + None, + ) + + +@patch('pip.req.req_install.os.path.abspath') +@patch('pip.req.req_install.os.path.exists') +@patch('pip.req.req_install.os.path.isdir') +def test_parse_editable_local_extras( + isdir_mock, exists_mock, abspath_mock): exists_mock.return_value = isdir_mock.return_value = True - normcase_mock.return_value = getcwd_mock.return_value = "/some/path" - assert parse_editable('.[extras]', 'git') == (None, 'file://' + "/some/path", ('extras',)) - normcase_mock.return_value = "/some/path/foo" - assert parse_editable('foo[bar,baz]', 'git') == (None, 'file:///some/path/foo', ('bar', 'baz')) - -def test_remote_reqs_parse(): - """ - Test parsing a simple remote requirements file - """ - # this requirements file just contains a comment - # previously this has failed in py3 (https://github.com/pypa/pip/issues/760) - for req in parse_requirements('https://raw.github.com/pypa/pip-test-package/master/tests/req_just_comment.txt'): - pass - -def test_req_file_parse_use_wheel(data, monkeypatch): - """ - Test parsing --use-wheel from a req file - """ - # patch this for travis which has distribute in it's base env for now - monkeypatch.setattr(pip.wheel.pkg_resources, "get_distribution", lambda x: Distribution(project_name='setuptools', version='0.9')) - - finder = PackageFinder([], []) - for req in parse_requirements(data.reqfiles.join("supported_options.txt"), finder): - pass - assert finder.use_wheel - - -def test_req_file_parse_comment_start_of_line(tmpdir): - """ - Test parsing comments in a requirements file - """ - with open(tmpdir.join("req1.txt"), "w") as fp: - fp.write("# Comment ") - - finder = PackageFinder([], []) - reqs = list(parse_requirements(tmpdir.join("req1.txt"), finder)) - - assert not reqs - - -def test_req_file_parse_comment_end_of_line_with_url(tmpdir): - """ - Test parsing comments in a requirements file - """ - with open(tmpdir.join("req1.txt"), "w") as fp: - fp.write("https://example.com/foo.tar.gz # Comment ") - - finder = PackageFinder([], []) - reqs = list(parse_requirements(tmpdir.join("req1.txt"), finder)) - - assert len(reqs) == 1 - assert reqs[0].url == "https://example.com/foo.tar.gz" - - -def test_req_file_parse_egginfo_end_of_line_with_url(tmpdir): - """ - Test parsing comments in a requirements file - """ - with open(tmpdir.join("req1.txt"), "w") as fp: - fp.write("https://example.com/foo.tar.gz#egg=wat") - - finder = PackageFinder([], []) - reqs = list(parse_requirements(tmpdir.join("req1.txt"), finder)) - - assert len(reqs) == 1 - assert reqs[0].name == "wat" + abspath_mock.return_value = "/some/path" + assert parse_editable('.[extras]', 'git') == ( + None, 'file://' + "/some/path", set(['extras']), + ) + abspath_mock.return_value = "/some/path/foo" + assert parse_editable('foo[bar,baz]', 'git') == ( + None, 'file:///some/path/foo', set(['bar', 'baz']), + ) + + +def test_exclusive_environment_markers(): + """Make sure RequirementSet accepts several excluding env markers""" + eq26 = InstallRequirement.from_line( + "Django>=1.6.10,<1.7 ; python_version == '2.6'") + ne26 = InstallRequirement.from_line( + "Django>=1.6.10,<1.8 ; python_version != '2.6'") + + req_set = RequirementSet('', '', '', session=PipSession()) + req_set.add_requirement(eq26) + req_set.add_requirement(ne26) + assert req_set.has_requirement('Django') diff --git a/tests/unit/test_req_file.py b/tests/unit/test_req_file.py new file mode 100644 index 00000000000..7e4bee430b8 --- /dev/null +++ b/tests/unit/test_req_file.py @@ -0,0 +1,600 @@ +import os +import subprocess +import textwrap + +from mock import patch, Mock +import pytest +from pretend import stub + +import pip +from pip.exceptions import (InstallationError, RequirementsFileParseError) +from pip.download import PipSession +from pip.index import PackageFinder +from pip.req.req_install import InstallRequirement +from pip.req.req_file import (parse_requirements, process_line, join_lines, + ignore_comments, break_args_options, skip_regex, + preprocess) +from tests.lib import requirements_file + + +@pytest.fixture +def session(): + return PipSession() + + +@pytest.fixture +def finder(session): + return PackageFinder([], [], session=session) + + +@pytest.fixture +def options(session): + return stub( + isolated_mode=False, default_vcs=None, index_url='default_url', + skip_requirements_regex=False, + format_control=pip.index.FormatControl(set(), set())) + + +class TestPreprocess(object): + """tests for `preprocess`""" + + def test_comments_and_joins_case1(self): + content = textwrap.dedent("""\ + req1 \\ + # comment \\ + req2 + """) + result = preprocess(content, None) + assert list(result) == [(1, 'req1'), (3, 'req2')] + + def test_comments_and_joins_case2(self): + content = textwrap.dedent("""\ + req1\\ + # comment + """) + result = preprocess(content, None) + assert list(result) == [(1, 'req1')] + + def test_comments_and_joins_case3(self): + content = textwrap.dedent("""\ + req1 \\ + # comment + req2 + """) + result = preprocess(content, None) + assert list(result) == [(1, 'req1'), (3, 'req2')] + + def test_skip_regex_after_joining_case1(self, options): + content = textwrap.dedent("""\ + patt\\ + ern + line2 + """) + options.skip_requirements_regex = 'pattern' + result = preprocess(content, options) + assert list(result) == [(3, 'line2')] + + def test_skip_regex_after_joining_case2(self, options): + content = textwrap.dedent("""\ + pattern \\ + line2 + line3 + """) + options.skip_requirements_regex = 'pattern' + result = preprocess(content, options) + assert list(result) == [(3, 'line3')] + + +class TestIgnoreComments(object): + """tests for `ignore_comment`""" + + def test_ignore_line(self): + lines = [(1, ''), (2, 'req1'), (3, 'req2')] + result = ignore_comments(lines) + assert list(result) == [(2, 'req1'), (3, 'req2')] + + def test_ignore_comment(self): + lines = [(1, 'req1'), (2, '# comment'), (3, 'req2')] + result = ignore_comments(lines) + assert list(result) == [(1, 'req1'), (3, 'req2')] + + def test_strip_comment(self): + lines = [(1, 'req1'), (2, 'req # comment'), (3, 'req2')] + result = ignore_comments(lines) + assert list(result) == [(1, 'req1'), (2, 'req'), (3, 'req2')] + + +class TestJoinLines(object): + """tests for `join_lines`""" + + def test_join_lines(self): + lines = enumerate([ + 'line 1', + 'line 2:1 \\', + 'line 2:2', + 'line 3:1 \\', + 'line 3:2 \\', + 'line 3:3', + 'line 4' + ], start=1) + expect = [ + (1, 'line 1'), + (2, 'line 2:1 line 2:2'), + (4, 'line 3:1 line 3:2 line 3:3'), + (7, 'line 4'), + ] + assert expect == list(join_lines(lines)) + + def test_last_line_with_escape(self): + lines = enumerate([ + 'line 1', + 'line 2 \\', + ], start=1) + expect = [ + (1, 'line 1'), + (2, 'line 2 '), + ] + assert expect == list(join_lines(lines)) + + +class TestSkipRegex(object): + """tests for `skip_reqex``""" + + def test_skip_regex_pattern_match(self): + options = stub(skip_requirements_regex='.*Bad.*') + line = '--extra-index-url Bad' + assert [] == list(skip_regex(enumerate([line]), options)) + + def test_skip_regex_pattern_not_match(self): + options = stub(skip_requirements_regex='.*Bad.*') + line = '--extra-index-url Good' + assert [(0, line)] == list(skip_regex(enumerate([line]), options)) + + def test_skip_regex_no_options(self): + options = None + line = '--extra-index-url Good' + assert [(0, line)] == list(skip_regex(enumerate([line]), options)) + + def test_skip_regex_no_skip_option(self): + options = stub(skip_requirements_regex=None) + line = '--extra-index-url Good' + assert [(0, line)] == list(skip_regex(enumerate([line]), options)) + + +class TestProcessLine(object): + """tests for `process_line`""" + + def test_parser_error(self): + with pytest.raises(RequirementsFileParseError): + list(process_line("--bogus", "file", 1)) + + def test_only_one_req_per_line(self): + # pkg_resources raises the ValueError + with pytest.raises(InstallationError): + list(process_line("req1 req2", "file", 1)) + + def test_yield_line_requirement(self): + line = 'SomeProject' + filename = 'filename' + comes_from = '-r %s (line %s)' % (filename, 1) + req = InstallRequirement.from_line(line, comes_from=comes_from) + assert repr(list(process_line(line, filename, 1))[0]) == repr(req) + + def test_yield_line_constraint(self): + line = 'SomeProject' + filename = 'filename' + comes_from = '-c %s (line %s)' % (filename, 1) + req = InstallRequirement.from_line( + line, comes_from=comes_from, constraint=True) + found_req = list(process_line(line, filename, 1, constraint=True))[0] + assert repr(found_req) == repr(req) + assert found_req.constraint is True + + def test_yield_line_requirement_with_spaces_in_specifier(self): + line = 'SomeProject >= 2' + filename = 'filename' + comes_from = '-r %s (line %s)' % (filename, 1) + req = InstallRequirement.from_line(line, comes_from=comes_from) + assert repr(list(process_line(line, filename, 1))[0]) == repr(req) + assert str(req.req.specifier) == '>=2' + + def test_yield_editable_requirement(self): + url = 'git+https://url#egg=SomeProject' + line = '-e %s' % url + filename = 'filename' + comes_from = '-r %s (line %s)' % (filename, 1) + req = InstallRequirement.from_editable(url, comes_from=comes_from) + assert repr(list(process_line(line, filename, 1))[0]) == repr(req) + + def test_yield_editable_constraint(self): + url = 'git+https://url#egg=SomeProject' + line = '-e %s' % url + filename = 'filename' + comes_from = '-c %s (line %s)' % (filename, 1) + req = InstallRequirement.from_editable( + url, comes_from=comes_from, constraint=True) + found_req = list(process_line(line, filename, 1, constraint=True))[0] + assert repr(found_req) == repr(req) + assert found_req.constraint is True + + def test_nested_requirements_file(self, monkeypatch): + line = '-r another_file' + req = InstallRequirement.from_line('SomeProject') + import pip.req.req_file + + def stub_parse_requirements(req_url, finder, comes_from, options, + session, wheel_cache, constraint): + return [(req, constraint)] + parse_requirements_stub = stub(call=stub_parse_requirements) + monkeypatch.setattr(pip.req.req_file, 'parse_requirements', + parse_requirements_stub.call) + assert list(process_line(line, 'filename', 1)) == [(req, False)] + + def test_nested_constraints_file(self, monkeypatch): + line = '-c another_file' + req = InstallRequirement.from_line('SomeProject') + import pip.req.req_file + + def stub_parse_requirements(req_url, finder, comes_from, options, + session, wheel_cache, constraint): + return [(req, constraint)] + parse_requirements_stub = stub(call=stub_parse_requirements) + monkeypatch.setattr(pip.req.req_file, 'parse_requirements', + parse_requirements_stub.call) + assert list(process_line(line, 'filename', 1)) == [(req, True)] + + def test_options_on_a_requirement_line(self): + line = 'SomeProject --install-option=yo1 --install-option yo2 '\ + '--global-option="yo3" --global-option "yo4"' + filename = 'filename' + req = list(process_line(line, filename, 1))[0] + assert req.options == { + 'global_options': ['yo3', 'yo4'], + 'install_options': ['yo1', 'yo2']} + + def test_hash_options(self): + """Test the --hash option: mostly its value storage. + + Make sure it reads and preserve multiple hashes. + + """ + line = ('SomeProject --hash=sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b1' + '61e5c1fa7425e73043362938b9824 ' + '--hash=sha384:59e1748777448c69de6b800d7a33bbfb9ff1b463e44354c' + '3553bcdb9c666fa90125a3c79f90397bdf5f6a13de828684f ' + '--hash=sha256:486ea46224d1bb4fb680f34f7c9ad96a8f24ec88be73ea8' + 'e5a6c65260e9cb8a7') + filename = 'filename' + req = list(process_line(line, filename, 1))[0] + assert req.options == {'hashes': { + 'sha256': ['2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e730433' + '62938b9824', + '486ea46224d1bb4fb680f34f7c9ad96a8f24ec88be73ea8e5a6c65' + '260e9cb8a7'], + 'sha384': ['59e1748777448c69de6b800d7a33bbfb9ff1b463e44354c3553bcd' + 'b9c666fa90125a3c79f90397bdf5f6a13de828684f']}} + + def test_set_isolated(self, options): + line = 'SomeProject' + filename = 'filename' + options.isolated_mode = True + result = process_line(line, filename, 1, options=options) + assert list(result)[0].isolated + + def test_set_default_vcs(self, options): + url = 'https://url#egg=SomeProject' + line = '-e %s' % url + filename = 'filename' + options.default_vcs = 'git' + result = process_line(line, filename, 1, options=options) + assert list(result)[0].link.url == 'git+' + url + + def test_set_finder_no_index(self, finder): + list(process_line("--no-index", "file", 1, finder=finder)) + assert finder.index_urls == [] + + def test_set_finder_index_url(self, finder): + list(process_line("--index-url=url", "file", 1, finder=finder)) + assert finder.index_urls == ['url'] + + def test_set_finder_find_links(self, finder): + list(process_line("--find-links=url", "file", 1, finder=finder)) + assert finder.find_links == ['url'] + + def test_set_finder_extra_index_urls(self, finder): + list(process_line("--extra-index-url=url", "file", 1, finder=finder)) + assert finder.index_urls == ['url'] + + def test_set_finder_use_wheel(self, finder): + list(process_line("--use-wheel", "file", 1, finder=finder)) + no_use_wheel_fmt = pip.index.FormatControl(set(), set()) + assert finder.format_control == no_use_wheel_fmt + + def test_set_finder_no_use_wheel(self, finder): + list(process_line("--no-use-wheel", "file", 1, finder=finder)) + no_use_wheel_fmt = pip.index.FormatControl(set([':all:']), set()) + assert finder.format_control == no_use_wheel_fmt + + def test_set_finder_trusted_host(self, finder): + list(process_line("--trusted-host=url", "file", 1, finder=finder)) + assert finder.secure_origins == [('*', 'url', '*')] + + def test_noop_always_unzip(self, finder): + # noop, but confirm it can be set + list(process_line("--always-unzip", "file", 1, finder=finder)) + + def test_noop_finder_no_allow_unsafe(self, finder): + # noop, but confirm it can be set + list(process_line("--no-allow-insecure", "file", 1, finder=finder)) + + def test_set_finder_allow_all_prereleases(self, finder): + list(process_line("--pre", "file", 1, finder=finder)) + assert finder.allow_all_prereleases + + def test_relative_local_find_links(self, finder, monkeypatch): + """ + Test a relative find_links path is joined with the req file directory + """ + # Make sure the test also passes on windows + req_file = os.path.normcase(os.path.abspath( + os.path.normpath('/path/req_file.txt'))) + nested_link = os.path.normcase(os.path.abspath( + os.path.normpath('/path/rel_path'))) + exists_ = os.path.exists + + def exists(path): + if path == nested_link: + return True + else: + exists_(path) + monkeypatch.setattr(os.path, 'exists', exists) + list(process_line("--find-links=rel_path", req_file, 1, + finder=finder)) + assert finder.find_links == [nested_link] + + def test_relative_http_nested_req_files(self, finder, monkeypatch): + """ + Test a relative nested req file path is joined with the req file url + """ + req_file = 'http://me.com/me/req_file.txt' + + def parse(*args, **kwargs): + return iter([]) + mock_parse = Mock() + mock_parse.side_effect = parse + monkeypatch.setattr(pip.req.req_file, 'parse_requirements', mock_parse) + list(process_line("-r reqs.txt", req_file, 1, finder=finder)) + call = mock_parse.mock_calls[0] + assert call[1][0] == 'http://me.com/me/reqs.txt' + + def test_relative_local_nested_req_files(self, finder, monkeypatch): + """ + Test a relative nested req file path is joined with the req file dir + """ + req_file = os.path.normpath('/path/req_file.txt') + + def parse(*args, **kwargs): + return iter([]) + mock_parse = Mock() + mock_parse.side_effect = parse + monkeypatch.setattr(pip.req.req_file, 'parse_requirements', mock_parse) + list(process_line("-r reqs.txt", req_file, 1, finder=finder)) + call = mock_parse.mock_calls[0] + assert call[1][0] == os.path.normpath('/path/reqs.txt') + + def test_absolute_local_nested_req_files(self, finder, monkeypatch): + """ + Test an absolute nested req file path + """ + req_file = '/path/req_file.txt' + + def parse(*args, **kwargs): + return iter([]) + mock_parse = Mock() + mock_parse.side_effect = parse + monkeypatch.setattr(pip.req.req_file, 'parse_requirements', mock_parse) + list(process_line("-r /other/reqs.txt", req_file, 1, finder=finder)) + call = mock_parse.mock_calls[0] + assert call[1][0] == '/other/reqs.txt' + + def test_absolute_http_nested_req_file_in_local(self, finder, monkeypatch): + """ + Test a nested req file url in a local req file + """ + req_file = '/path/req_file.txt' + + def parse(*args, **kwargs): + return iter([]) + mock_parse = Mock() + mock_parse.side_effect = parse + monkeypatch.setattr(pip.req.req_file, 'parse_requirements', mock_parse) + list(process_line("-r http://me.com/me/reqs.txt", req_file, 1, + finder=finder)) + call = mock_parse.mock_calls[0] + assert call[1][0] == 'http://me.com/me/reqs.txt' + + def test_set_finder_process_dependency_links(self, finder): + list(process_line( + "--process-dependency-links", "file", 1, finder=finder)) + assert finder.process_dependency_links + + +class TestBreakOptionsArgs(object): + + def test_no_args(self): + assert ('', '--option') == break_args_options('--option') + + def test_no_options(self): + assert ('arg arg', '') == break_args_options('arg arg') + + def test_args_short_options(self): + result = break_args_options('arg arg -s') + assert ('arg arg', '-s') == result + + def test_args_long_options(self): + result = break_args_options('arg arg --long') + assert ('arg arg', '--long') == result + + +class TestOptionVariants(object): + + # this suite is really just testing optparse, but added it anyway + + def test_variant1(self, finder): + list(process_line("-i url", "file", 1, finder=finder)) + assert finder.index_urls == ['url'] + + def test_variant2(self, finder): + list(process_line("-i 'url'", "file", 1, finder=finder)) + assert finder.index_urls == ['url'] + + def test_variant3(self, finder): + list(process_line("--index-url=url", "file", 1, finder=finder)) + assert finder.index_urls == ['url'] + + def test_variant4(self, finder): + list(process_line("--index-url url", "file", 1, finder=finder)) + assert finder.index_urls == ['url'] + + def test_variant5(self, finder): + list(process_line("--index-url='url'", "file", 1, finder=finder)) + assert finder.index_urls == ['url'] + + +class TestParseRequirements(object): + """tests for `parse_requirements`""" + + @pytest.mark.network + def test_remote_reqs_parse(self): + """ + Test parsing a simple remote requirements file + """ + # this requirements file just contains a comment previously this has + # failed in py3: https://github.com/pypa/pip/issues/760 + for req in parse_requirements( + 'https://raw.githubusercontent.com/pypa/' + 'pip-test-package/master/' + 'tests/req_just_comment.txt', session=PipSession()): + pass + + def test_multiple_appending_options(self, tmpdir, finder, options): + with open(tmpdir.join("req1.txt"), "w") as fp: + fp.write("--extra-index-url url1 \n") + fp.write("--extra-index-url url2 ") + + list(parse_requirements(tmpdir.join("req1.txt"), finder=finder, + session=PipSession(), options=options)) + + assert finder.index_urls == ['url1', 'url2'] + + def test_skip_regex(self, tmpdir, finder, options): + options.skip_requirements_regex = '.*Bad.*' + with open(tmpdir.join("req1.txt"), "w") as fp: + fp.write("--extra-index-url Bad \n") + fp.write("--extra-index-url Good ") + + list(parse_requirements(tmpdir.join("req1.txt"), finder=finder, + options=options, session=PipSession())) + + assert finder.index_urls == ['Good'] + + def test_join_lines(self, tmpdir, finder): + with open(tmpdir.join("req1.txt"), "w") as fp: + fp.write("--extra-index-url url1 \\\n--extra-index-url url2") + + list(parse_requirements(tmpdir.join("req1.txt"), finder=finder, + session=PipSession())) + + assert finder.index_urls == ['url1', 'url2'] + + def test_req_file_parse_no_only_binary(self, data, finder): + list(parse_requirements( + data.reqfiles.join("supported_options2.txt"), finder, + session=PipSession())) + expected = pip.index.FormatControl(set(['fred']), set(['wilma'])) + assert finder.format_control == expected + + def test_req_file_parse_comment_start_of_line(self, tmpdir, finder): + """ + Test parsing comments in a requirements file + """ + with open(tmpdir.join("req1.txt"), "w") as fp: + fp.write("# Comment ") + + reqs = list(parse_requirements(tmpdir.join("req1.txt"), finder, + session=PipSession())) + + assert not reqs + + def test_req_file_parse_comment_end_of_line_with_url(self, tmpdir, finder): + """ + Test parsing comments in a requirements file + """ + with open(tmpdir.join("req1.txt"), "w") as fp: + fp.write("https://example.com/foo.tar.gz # Comment ") + + reqs = list(parse_requirements(tmpdir.join("req1.txt"), finder, + session=PipSession())) + + assert len(reqs) == 1 + assert reqs[0].link.url == "https://example.com/foo.tar.gz" + + def test_req_file_parse_egginfo_end_of_line_with_url(self, tmpdir, finder): + """ + Test parsing comments in a requirements file + """ + with open(tmpdir.join("req1.txt"), "w") as fp: + fp.write("https://example.com/foo.tar.gz#egg=wat") + + reqs = list(parse_requirements(tmpdir.join("req1.txt"), finder, + session=PipSession())) + + assert len(reqs) == 1 + assert reqs[0].name == "wat" + + def test_req_file_no_finder(self, tmpdir): + """ + Test parsing a requirements file without a finder + """ + with open(tmpdir.join("req.txt"), "w") as fp: + fp.write(""" + --find-links https://example.com/ + --index-url https://example.com/ + --extra-index-url https://two.example.com/ + --no-use-wheel + --no-index + """) + + parse_requirements(tmpdir.join("req.txt"), session=PipSession()) + + def test_install_requirements_with_options(self, tmpdir, finder, session, + options): + global_option = '--dry-run' + install_option = '--prefix=/opt' + + content = ''' + --only-binary :all: + INITools==2.0 --global-option="{global_option}" \ + --install-option "{install_option}" + '''.format(global_option=global_option, install_option=install_option) + + with requirements_file(content, tmpdir) as reqs_file: + req = next(parse_requirements(reqs_file.abspath, + finder=finder, + options=options, + session=session)) + + req.source_dir = os.curdir + with patch.object(subprocess, 'Popen') as popen: + popen.return_value.stdout.readline.return_value = "" + try: + req.install([]) + except: + pass + + call = popen.call_args_list[0][0][0] + assert call.index(install_option) > \ + call.index('install') > \ + call.index(global_option) > 0 + assert options.format_control.no_binary == set([':all:']) + assert options.format_control.only_binary == set([]) diff --git a/tests/unit/test_req_install.py b/tests/unit/test_req_install.py new file mode 100644 index 00000000000..b956608cf99 --- /dev/null +++ b/tests/unit/test_req_install.py @@ -0,0 +1,28 @@ +import os +import tempfile + +import pytest + +from pip.req.req_install import InstallRequirement + + +class TestInstallRequirementBuildDirectory(object): + # no need to test symlinks on Windows + @pytest.mark.skipif("sys.platform == 'win32'") + def test_tmp_build_directory(self): + # when req is None, we can produce a temporary directory + # Make sure we're handling it correctly with real path. + requirement = InstallRequirement(None, None) + tmp_dir = tempfile.mkdtemp('-build', 'pip-') + tmp_build_dir = requirement.build_location(tmp_dir) + assert ( + os.path.dirname(tmp_build_dir) == + os.path.realpath(os.path.dirname(tmp_dir)) + ) + # are we on a system where /tmp is a symlink + if os.path.realpath(tmp_dir) != os.path.abspath(tmp_dir): + assert os.path.dirname(tmp_build_dir) != os.path.dirname(tmp_dir) + else: + assert os.path.dirname(tmp_build_dir) == os.path.dirname(tmp_dir) + os.rmdir(tmp_dir) + assert not os.path.exists(tmp_dir) diff --git a/tests/unit/test_req_uninstall.py b/tests/unit/test_req_uninstall.py new file mode 100644 index 00000000000..8025d0fb9f5 --- /dev/null +++ b/tests/unit/test_req_uninstall.py @@ -0,0 +1,76 @@ +import os + +import pytest +from mock import Mock + +import pip.req.req_uninstall +from pip.req.req_uninstall import UninstallPathSet + + +# Pretend all files are local, so UninstallPathSet accepts files in the tmpdir, +# outside the virtualenv +def mock_is_local(path): + return True + + +class TestUninstallPathSet(object): + def test_add(self, tmpdir, monkeypatch): + monkeypatch.setattr(pip.req.req_uninstall, 'is_local', mock_is_local) + # Fix case for windows tests + file_extant = os.path.normcase(os.path.join(tmpdir, 'foo')) + file_nonexistent = os.path.normcase( + os.path.join(tmpdir, 'nonexistent')) + with open(file_extant, 'w'): + pass + + ups = UninstallPathSet(dist=Mock()) + assert ups.paths == set() + ups.add(file_extant) + assert ups.paths == set([file_extant]) + + ups.add(file_nonexistent) + assert ups.paths == set([file_extant]) + + @pytest.mark.skipif("sys.platform == 'win32'") + def test_add_symlink(self, tmpdir, monkeypatch): + monkeypatch.setattr(pip.req.req_uninstall, 'is_local', mock_is_local) + f = os.path.join(tmpdir, 'foo') + with open(f, 'w'): + pass + l = os.path.join(tmpdir, 'foo_link') + os.symlink(f, l) + + ups = UninstallPathSet(dist=Mock()) + ups.add(l) + assert ups.paths == set([l]) + + def test_compact_shorter_path(self, monkeypatch): + monkeypatch.setattr(pip.req.req_uninstall, 'is_local', lambda p: True) + monkeypatch.setattr('os.path.exists', lambda p: True) + # This deals with nt/posix path differences + short_path = os.path.normcase(os.path.abspath( + os.path.join(os.path.sep, 'path'))) + ups = UninstallPathSet(dist=Mock()) + ups.add(short_path) + ups.add(os.path.join(short_path, 'longer')) + assert ups.compact(ups.paths) == set([short_path]) + + @pytest.mark.skipif("sys.platform == 'win32'") + def test_detect_symlink_dirs(self, monkeypatch, tmpdir): + monkeypatch.setattr(pip.req.req_uninstall, 'is_local', lambda p: True) + + # construct 2 paths: + # tmpdir/dir/file + # tmpdir/dirlink/file (where dirlink is a link to dir) + d = tmpdir.join('dir') + d.mkdir() + dlink = tmpdir.join('dirlink') + os.symlink(d, dlink) + d.join('file').touch() + path1 = str(d.join('file')) + path2 = str(dlink.join('file')) + + ups = UninstallPathSet(dist=Mock()) + ups.add(path1) + ups.add(path2) + assert ups.paths == set([path1]) diff --git a/tests/unit/test_unit_outdated.py b/tests/unit/test_unit_outdated.py new file mode 100644 index 00000000000..d2d9c3f14a7 --- /dev/null +++ b/tests/unit/test_unit_outdated.py @@ -0,0 +1,156 @@ +import sys +import datetime +import os +from contextlib import contextmanager + +import freezegun +import pytest +import pretend + +from pip._vendor import lockfile +from pip.utils import outdated + + +@pytest.mark.parametrize( + ['stored_time', 'newver', 'check', 'warn'], + [ + ('1970-01-01T10:00:00Z', '2.0', True, True), + ('1970-01-01T10:00:00Z', '1.0', True, False), + ('1970-01-06T10:00:00Z', '1.0', False, False), + ('1970-01-06T10:00:00Z', '2.0', False, True), + ] +) +def test_pip_version_check(monkeypatch, stored_time, newver, check, warn): + monkeypatch.setattr(outdated, 'get_installed_version', lambda name: '1.0') + + resp = pretend.stub( + raise_for_status=pretend.call_recorder(lambda: None), + json=pretend.call_recorder(lambda: {"releases": {newver: {}}}), + ) + session = pretend.stub( + get=pretend.call_recorder(lambda u, headers=None: resp), + ) + + fake_state = pretend.stub( + state={"last_check": stored_time, 'pypi_version': '1.0'}, + save=pretend.call_recorder(lambda v, t: None), + ) + + monkeypatch.setattr( + outdated, 'load_selfcheck_statefile', lambda: fake_state + ) + + monkeypatch.setattr(outdated.logger, 'warning', + pretend.call_recorder(lambda *a, **kw: None)) + monkeypatch.setattr(outdated.logger, 'debug', + pretend.call_recorder(lambda s, exc_info=None: None)) + + with freezegun.freeze_time( + "1970-01-09 10:00:00", + ignore=[ + "six.moves", + "pip._vendor.six.moves", + "pip._vendor.requests.packages.urllib3.packages.six.moves", + ]): + outdated.pip_version_check(session) + + assert not outdated.logger.debug.calls + + if check: + assert session.get.calls == [pretend.call( + "https://pypi.python.org/pypi/pip/json", + headers={"Accept": "application/json"} + )] + assert fake_state.save.calls == [ + pretend.call(newver, datetime.datetime(1970, 1, 9, 10, 00, 00)), + ] + if warn: + assert len(outdated.logger.warning.calls) == 1 + else: + assert len(outdated.logger.warning.calls) == 0 + else: + assert session.get.calls == [] + assert fake_state.save.calls == [] + + +def test_virtualenv_state(monkeypatch): + CONTENT = '{"last_check": "1970-01-02T11:00:00Z", "pypi_version": "1.0"}' + fake_file = pretend.stub( + read=pretend.call_recorder(lambda: CONTENT), + write=pretend.call_recorder(lambda s: None), + ) + + @pretend.call_recorder + @contextmanager + def fake_open(filename, mode='r'): + yield fake_file + + monkeypatch.setattr(outdated, 'open', fake_open, raising=False) + + monkeypatch.setattr(outdated, 'running_under_virtualenv', + pretend.call_recorder(lambda: True)) + + monkeypatch.setattr(sys, 'prefix', 'virtually_env') + + state = outdated.load_selfcheck_statefile() + state.save('2.0', datetime.datetime.utcnow()) + + assert len(outdated.running_under_virtualenv.calls) == 1 + + expected_path = os.path.join('virtually_env', 'pip-selfcheck.json') + assert fake_open.calls == [ + pretend.call(expected_path), + pretend.call(expected_path, 'w'), + ] + + # json.dumps will call this a number of times + assert len(fake_file.write.calls) + + +def test_global_state(monkeypatch): + CONTENT = '''{"pip_prefix": {"last_check": "1970-01-02T11:00:00Z", + "pypi_version": "1.0"}}''' + fake_file = pretend.stub( + read=pretend.call_recorder(lambda: CONTENT), + write=pretend.call_recorder(lambda s: None), + ) + + @pretend.call_recorder + @contextmanager + def fake_open(filename, mode='r'): + yield fake_file + + monkeypatch.setattr(outdated, 'open', fake_open, raising=False) + + @pretend.call_recorder + @contextmanager + def fake_lock(filename): + yield + + monkeypatch.setattr(outdated, "check_path_owner", lambda p: True) + + monkeypatch.setattr(lockfile, 'LockFile', fake_lock) + monkeypatch.setattr(os.path, "exists", lambda p: True) + + monkeypatch.setattr(outdated, 'running_under_virtualenv', + pretend.call_recorder(lambda: False)) + + monkeypatch.setattr(outdated, 'USER_CACHE_DIR', 'cache_dir') + monkeypatch.setattr(sys, 'prefix', 'pip_prefix') + + state = outdated.load_selfcheck_statefile() + state.save('2.0', datetime.datetime.utcnow()) + + assert len(outdated.running_under_virtualenv.calls) == 1 + + expected_path = os.path.join('cache_dir', 'selfcheck.json') + assert fake_lock.calls == [pretend.call(expected_path)] + + assert fake_open.calls == [ + pretend.call(expected_path), + pretend.call(expected_path), + pretend.call(expected_path, 'w'), + ] + + # json.dumps will call this a number of times + assert len(fake_file.write.calls) diff --git a/tests/unit/test_util.py b/tests/unit/test_util.py deleted file mode 100644 index 65e7a88c9ec..00000000000 --- a/tests/unit/test_util.py +++ /dev/null @@ -1,350 +0,0 @@ -""" -util tests - -""" -import os -import stat -import sys -import shutil -import tempfile - -import pytest - -from mock import Mock, patch -from pip.exceptions import BadCommand -from pip.util import (egg_link_path, Inf, get_installed_distributions, - find_command, untar_file, unzip_file) - - -class Tests_EgglinkPath: - "util.egg_link_path() tests" - - def setup(self): - - project = 'foo' - - self.mock_dist = Mock(project_name=project) - self.site_packages = 'SITE_PACKAGES' - self.user_site = 'USER_SITE' - self.user_site_egglink = os.path.join(self.user_site,'%s.egg-link' % project) - self.site_packages_egglink = os.path.join(self.site_packages,'%s.egg-link' % project) - - #patches - from pip import util - self.old_site_packages = util.site_packages - self.mock_site_packages = util.site_packages = 'SITE_PACKAGES' - self.old_running_under_virtualenv = util.running_under_virtualenv - self.mock_running_under_virtualenv = util.running_under_virtualenv = Mock() - self.old_virtualenv_no_global = util.virtualenv_no_global - self.mock_virtualenv_no_global = util.virtualenv_no_global = Mock() - self.old_user_site = util.user_site - self.mock_user_site = util.user_site = self.user_site - from os import path - self.old_isfile = path.isfile - self.mock_isfile = path.isfile = Mock() - - - def teardown(self): - from pip import util - util.site_packages = self.old_site_packages - util.running_under_virtualenv = self.old_running_under_virtualenv - util.virtualenv_no_global = self.old_virtualenv_no_global - util.user_site = self.old_user_site - from os import path - path.isfile = self.old_isfile - - - def eggLinkInUserSite(self,egglink): - return egglink==self.user_site_egglink - - def eggLinkInSitePackages(self,egglink): - return egglink==self.site_packages_egglink - - ######################### - ## egglink in usersite ## - ######################### - def test_egglink_in_usersite_notvenv(self): - self.mock_virtualenv_no_global.return_value = False - self.mock_running_under_virtualenv.return_value = False - self.mock_isfile.side_effect = self.eggLinkInUserSite - assert egg_link_path(self.mock_dist) == self.user_site_egglink - - def test_egglink_in_usersite_venv_noglobal(self): - self.mock_virtualenv_no_global.return_value = True - self.mock_running_under_virtualenv.return_value = True - self.mock_isfile.side_effect = self.eggLinkInUserSite - assert egg_link_path(self.mock_dist) is None - - def test_egglink_in_usersite_venv_global(self): - self.mock_virtualenv_no_global.return_value = False - self.mock_running_under_virtualenv.return_value = True - self.mock_isfile.side_effect = self.eggLinkInUserSite - assert egg_link_path(self.mock_dist) == self.user_site_egglink - - ######################### - ## egglink in sitepkgs ## - ######################### - def test_egglink_in_sitepkgs_notvenv(self): - self.mock_virtualenv_no_global.return_value = False - self.mock_running_under_virtualenv.return_value = False - self.mock_isfile.side_effect = self.eggLinkInSitePackages - assert egg_link_path(self.mock_dist) == self.site_packages_egglink - - def test_egglink_in_sitepkgs_venv_noglobal(self): - self.mock_virtualenv_no_global.return_value = True - self.mock_running_under_virtualenv.return_value = True - self.mock_isfile.side_effect = self.eggLinkInSitePackages - assert egg_link_path(self.mock_dist) == self.site_packages_egglink - - def test_egglink_in_sitepkgs_venv_global(self): - self.mock_virtualenv_no_global.return_value = False - self.mock_running_under_virtualenv.return_value = True - self.mock_isfile.side_effect = self.eggLinkInSitePackages - assert egg_link_path(self.mock_dist) == self.site_packages_egglink - - #################################### - ## egglink in usersite & sitepkgs ## - #################################### - def test_egglink_in_both_notvenv(self): - self.mock_virtualenv_no_global.return_value = False - self.mock_running_under_virtualenv.return_value = False - self.mock_isfile.return_value = True - assert egg_link_path(self.mock_dist) == self.user_site_egglink - - def test_egglink_in_both_venv_noglobal(self): - self.mock_virtualenv_no_global.return_value = True - self.mock_running_under_virtualenv.return_value = True - self.mock_isfile.return_value = True - assert egg_link_path(self.mock_dist) == self.site_packages_egglink - - def test_egglink_in_both_venv_global(self): - self.mock_virtualenv_no_global.return_value = False - self.mock_running_under_virtualenv.return_value = True - self.mock_isfile.return_value = True - assert egg_link_path(self.mock_dist) == self.site_packages_egglink - - ################ - ## no egglink ## - ################ - def test_noegglink_in_sitepkgs_notvenv(self): - self.mock_virtualenv_no_global.return_value = False - self.mock_running_under_virtualenv.return_value = False - self.mock_isfile.return_value = False - assert egg_link_path(self.mock_dist) is None - - def test_noegglink_in_sitepkgs_venv_noglobal(self): - self.mock_virtualenv_no_global.return_value = True - self.mock_running_under_virtualenv.return_value = True - self.mock_isfile.return_value = False - assert egg_link_path(self.mock_dist) is None - - def test_noegglink_in_sitepkgs_venv_global(self): - self.mock_virtualenv_no_global.return_value = False - self.mock_running_under_virtualenv.return_value = True - self.mock_isfile.return_value = False - assert egg_link_path(self.mock_dist) is None - -def test_Inf_greater(): - """Test Inf compares greater.""" - assert Inf > object() - -def test_Inf_equals_Inf(): - """Test Inf compares greater.""" - assert Inf == Inf - - -class Tests_get_installed_distributions: - """test util.get_installed_distributions""" - - - workingset = [ - Mock(test_name="global"), - Mock(test_name="editable"), - Mock(test_name="normal") - ] - - def dist_is_editable(self, dist): - return dist.test_name == "editable" - - def dist_is_local(self, dist): - return dist.test_name != "global" - - - @patch('pip.util.dist_is_local') - @patch('pip.util.dist_is_editable') - @patch('pip._vendor.pkg_resources.working_set', workingset) - def test_editables_only(self, mock_dist_is_editable, mock_dist_is_local): - mock_dist_is_editable.side_effect = self.dist_is_editable - mock_dist_is_local.side_effect = self.dist_is_local - dists = get_installed_distributions(editables_only=True) - assert len(dists) == 1, dists - assert dists[0].test_name == "editable" - - - @patch('pip.util.dist_is_local') - @patch('pip.util.dist_is_editable') - @patch('pip._vendor.pkg_resources.working_set', workingset) - def test_exclude_editables(self, mock_dist_is_editable, mock_dist_is_local): - mock_dist_is_editable.side_effect = self.dist_is_editable - mock_dist_is_local.side_effect = self.dist_is_local - dists = get_installed_distributions(include_editables=False) - assert len(dists) == 1 - assert dists[0].test_name == "normal" - - - @patch('pip.util.dist_is_local') - @patch('pip.util.dist_is_editable') - @patch('pip._vendor.pkg_resources.working_set', workingset) - def test_include_globals(self, mock_dist_is_editable, mock_dist_is_local): - mock_dist_is_editable.side_effect = self.dist_is_editable - mock_dist_is_local.side_effect = self.dist_is_local - dists = get_installed_distributions(local_only=False) - assert len(dists) == 3 - - -def test_find_command_folder_in_path(script): - """ - If a folder named e.g. 'git' is in PATH, and find_command is looking for - the 'git' executable, it should not match the folder, but rather keep - looking. - """ - script.scratch_path.join("path_one").mkdir() - path_one = script.scratch_path/'path_one' - path_one.join("foo").mkdir() - script.scratch_path.join("path_two").mkdir() - path_two = script.scratch_path/'path_two' - path_two.join("foo").write("# nothing") - found_path = find_command('foo', map(str, [path_one, path_two])) - assert found_path == path_two/'foo' - - -def test_does_not_find_command_because_there_is_no_path(): - """ - Test calling `pip.utils.find_command` when there is no PATH env variable - """ - environ_before = os.environ - os.environ = {} - try: - try: - find_command('anycommand') - except BadCommand: - e = sys.exc_info()[1] - assert e.args == ("Cannot find command 'anycommand'",) - else: - raise AssertionError("`find_command` should raise `BadCommand`") - finally: - os.environ = environ_before - - -@patch('os.pathsep', ':') -@patch('pip.util.get_pathext') -@patch('os.path.isfile') -def test_find_command_trys_all_pathext(mock_isfile, getpath_mock): - """ - If no pathext should check default list of extensions, if file does not - exist. - """ - mock_isfile.return_value = False - - getpath_mock.return_value = os.pathsep.join([".COM", ".EXE"]) - - paths = [os.path.join('path_one', f) for f in ['foo.com', 'foo.exe', 'foo']] - expected = [((p,),) for p in paths] - - with pytest.raises(BadCommand): - find_command("foo", "path_one") - - assert mock_isfile.call_args_list == expected, "Actual: %s\nExpected %s" % (mock_isfile.call_args_list, expected) - assert getpath_mock.called, "Should call get_pathext" - - -@patch('os.pathsep', ':') -@patch('pip.util.get_pathext') -@patch('os.path.isfile') -def test_find_command_trys_supplied_pathext(mock_isfile, getpath_mock): - """ - If pathext supplied find_command should use all of its list of extensions to find file. - """ - mock_isfile.return_value = False - getpath_mock.return_value = ".FOO" - - pathext = os.pathsep.join([".RUN", ".CMD"]) - - paths = [os.path.join('path_one', f) for f in ['foo.run', 'foo.cmd', 'foo']] - expected = [((p,),) for p in paths] - - with pytest.raises(BadCommand): - find_command("foo", "path_one", pathext) - - assert mock_isfile.call_args_list == expected, "Actual: %s\nExpected %s" % (mock_isfile.call_args_list, expected) - assert not getpath_mock.called, "Should not call get_pathext" - - -class TestUnpackArchives(object): - """ - test_tar.tgz/test_tar.zip have content as follows engineered to confirm 3 things: - 1) confirm that reg files, dirs, and symlinks get unpacked - 2) permissions are not preserved (and go by the 022 umask) - 3) reg files with *any* execute perms, get chmod +x - - file.txt 600 regular file - symlink.txt 777 symlink to file.txt - script_owner.sh 700 script where owner can execute - script_group.sh 610 script where group can execute - script_world.sh 601 script where world can execute - dir 744 directory - dir/dirfile 622 regular file - - """ - - def setup(self): - self.tempdir = tempfile.mkdtemp() - self.old_mask = os.umask(0o022) - self.symlink_expected_mode = None - - def teardown(self): - os.umask(self.old_mask) - shutil.rmtree(self.tempdir, ignore_errors=True) - - def mode(self, path): - return stat.S_IMODE(os.stat(path).st_mode) - - def confirm_files(self): - # expections based on 022 umask set above and the unpack logic that sets - # execute permissions, not preservation - for fname, expected_mode, test in [ - ('file.txt', 0o644, os.path.isfile), - ('symlink.txt', 0o644, os.path.isfile), - ('script_owner.sh', 0o755, os.path.isfile), - ('script_group.sh', 0o755, os.path.isfile), - ('script_world.sh', 0o755, os.path.isfile), - ('dir', 0o755, os.path.isdir), - (os.path.join('dir', 'dirfile'), 0o644, os.path.isfile), - ]: - path = os.path.join(self.tempdir, fname) - if path.endswith('symlink.txt') and sys.platform == 'win32': - # no symlinks created on windows - continue - assert test(path), path - if sys.platform == 'win32': - # the permissions tests below don't apply in windows - # due to os.chmod being a noop - continue - mode = self.mode(path) - assert mode == expected_mode, "mode: %s, expected mode: %s" % (mode, expected_mode) - - def test_unpack_tgz(self, data): - """ - Test unpacking a *.tgz, and setting execute permissions - """ - test_file = data.packages.join("test_tar.tgz") - untar_file(test_file, self.tempdir) - self.confirm_files() - - def test_unpack_zip(self, data): - """ - Test unpacking a *.zip, and setting execute permissions - """ - test_file = data.packages.join("test_zip.zip") - unzip_file(test_file, self.tempdir) - self.confirm_files() diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py new file mode 100644 index 00000000000..32220b4d180 --- /dev/null +++ b/tests/unit/test_utils.py @@ -0,0 +1,526 @@ +# -*- coding: utf-8 -*- + +""" +util tests + +""" +import os +import stat +import sys +import time +import shutil +import tempfile +import warnings + +import pytest + +from mock import Mock, patch +from pip.exceptions import HashMismatch, HashMissing, InstallationError +from pip.utils import (egg_link_path, get_installed_distributions, + untar_file, unzip_file, rmtree, normalize_path) +from pip.utils.build import BuildDirectory +from pip.utils.encoding import auto_decode +from pip.utils.hashes import Hashes, MissingHashes +from pip.utils.glibc import check_glibc_version +from pip._vendor.six import BytesIO + + +class Tests_EgglinkPath: + "util.egg_link_path() tests" + + def setup(self): + + project = 'foo' + + self.mock_dist = Mock(project_name=project) + self.site_packages = 'SITE_PACKAGES' + self.user_site = 'USER_SITE' + self.user_site_egglink = os.path.join( + self.user_site, + '%s.egg-link' % project + ) + self.site_packages_egglink = os.path.join( + self.site_packages, + '%s.egg-link' % project, + ) + + # patches + from pip import utils + self.old_site_packages = utils.site_packages + self.mock_site_packages = utils.site_packages = 'SITE_PACKAGES' + self.old_running_under_virtualenv = utils.running_under_virtualenv + self.mock_running_under_virtualenv = utils.running_under_virtualenv = \ + Mock() + self.old_virtualenv_no_global = utils.virtualenv_no_global + self.mock_virtualenv_no_global = utils.virtualenv_no_global = Mock() + self.old_user_site = utils.user_site + self.mock_user_site = utils.user_site = self.user_site + from os import path + self.old_isfile = path.isfile + self.mock_isfile = path.isfile = Mock() + + def teardown(self): + from pip import utils + utils.site_packages = self.old_site_packages + utils.running_under_virtualenv = self.old_running_under_virtualenv + utils.virtualenv_no_global = self.old_virtualenv_no_global + utils.user_site = self.old_user_site + from os import path + path.isfile = self.old_isfile + + def eggLinkInUserSite(self, egglink): + return egglink == self.user_site_egglink + + def eggLinkInSitePackages(self, egglink): + return egglink == self.site_packages_egglink + + # ####################### # + # # egglink in usersite # # + # ####################### # + def test_egglink_in_usersite_notvenv(self): + self.mock_virtualenv_no_global.return_value = False + self.mock_running_under_virtualenv.return_value = False + self.mock_isfile.side_effect = self.eggLinkInUserSite + assert egg_link_path(self.mock_dist) == self.user_site_egglink + + def test_egglink_in_usersite_venv_noglobal(self): + self.mock_virtualenv_no_global.return_value = True + self.mock_running_under_virtualenv.return_value = True + self.mock_isfile.side_effect = self.eggLinkInUserSite + assert egg_link_path(self.mock_dist) is None + + def test_egglink_in_usersite_venv_global(self): + self.mock_virtualenv_no_global.return_value = False + self.mock_running_under_virtualenv.return_value = True + self.mock_isfile.side_effect = self.eggLinkInUserSite + assert egg_link_path(self.mock_dist) == self.user_site_egglink + + # ####################### # + # # egglink in sitepkgs # # + # ####################### # + def test_egglink_in_sitepkgs_notvenv(self): + self.mock_virtualenv_no_global.return_value = False + self.mock_running_under_virtualenv.return_value = False + self.mock_isfile.side_effect = self.eggLinkInSitePackages + assert egg_link_path(self.mock_dist) == self.site_packages_egglink + + def test_egglink_in_sitepkgs_venv_noglobal(self): + self.mock_virtualenv_no_global.return_value = True + self.mock_running_under_virtualenv.return_value = True + self.mock_isfile.side_effect = self.eggLinkInSitePackages + assert egg_link_path(self.mock_dist) == self.site_packages_egglink + + def test_egglink_in_sitepkgs_venv_global(self): + self.mock_virtualenv_no_global.return_value = False + self.mock_running_under_virtualenv.return_value = True + self.mock_isfile.side_effect = self.eggLinkInSitePackages + assert egg_link_path(self.mock_dist) == self.site_packages_egglink + + # ################################## # + # # egglink in usersite & sitepkgs # # + # ################################## # + def test_egglink_in_both_notvenv(self): + self.mock_virtualenv_no_global.return_value = False + self.mock_running_under_virtualenv.return_value = False + self.mock_isfile.return_value = True + assert egg_link_path(self.mock_dist) == self.user_site_egglink + + def test_egglink_in_both_venv_noglobal(self): + self.mock_virtualenv_no_global.return_value = True + self.mock_running_under_virtualenv.return_value = True + self.mock_isfile.return_value = True + assert egg_link_path(self.mock_dist) == self.site_packages_egglink + + def test_egglink_in_both_venv_global(self): + self.mock_virtualenv_no_global.return_value = False + self.mock_running_under_virtualenv.return_value = True + self.mock_isfile.return_value = True + assert egg_link_path(self.mock_dist) == self.site_packages_egglink + + # ############## # + # # no egglink # # + # ############## # + def test_noegglink_in_sitepkgs_notvenv(self): + self.mock_virtualenv_no_global.return_value = False + self.mock_running_under_virtualenv.return_value = False + self.mock_isfile.return_value = False + assert egg_link_path(self.mock_dist) is None + + def test_noegglink_in_sitepkgs_venv_noglobal(self): + self.mock_virtualenv_no_global.return_value = True + self.mock_running_under_virtualenv.return_value = True + self.mock_isfile.return_value = False + assert egg_link_path(self.mock_dist) is None + + def test_noegglink_in_sitepkgs_venv_global(self): + self.mock_virtualenv_no_global.return_value = False + self.mock_running_under_virtualenv.return_value = True + self.mock_isfile.return_value = False + assert egg_link_path(self.mock_dist) is None + + +@patch('pip.utils.dist_in_usersite') +@patch('pip.utils.dist_is_local') +@patch('pip.utils.dist_is_editable') +class Tests_get_installed_distributions: + """test util.get_installed_distributions""" + + workingset = [ + Mock(test_name="global"), + Mock(test_name="editable"), + Mock(test_name="normal"), + Mock(test_name="user"), + ] + + workingset_stdlib = [ + Mock(test_name='normal', key='argparse'), + Mock(test_name='normal', key='wsgiref') + ] + + workingset_freeze = [ + Mock(test_name='normal', key='pip'), + Mock(test_name='normal', key='setuptools'), + Mock(test_name='normal', key='distribute') + ] + + def dist_is_editable(self, dist): + return dist.test_name == "editable" + + def dist_is_local(self, dist): + return dist.test_name != "global" and dist.test_name != 'user' + + def dist_in_usersite(self, dist): + return dist.test_name == "user" + + @patch('pip._vendor.pkg_resources.working_set', workingset) + def test_editables_only(self, mock_dist_is_editable, + mock_dist_is_local, + mock_dist_in_usersite): + mock_dist_is_editable.side_effect = self.dist_is_editable + mock_dist_is_local.side_effect = self.dist_is_local + mock_dist_in_usersite.side_effect = self.dist_in_usersite + dists = get_installed_distributions(editables_only=True) + assert len(dists) == 1, dists + assert dists[0].test_name == "editable" + + @patch('pip._vendor.pkg_resources.working_set', workingset) + def test_exclude_editables(self, mock_dist_is_editable, + mock_dist_is_local, + mock_dist_in_usersite): + mock_dist_is_editable.side_effect = self.dist_is_editable + mock_dist_is_local.side_effect = self.dist_is_local + mock_dist_in_usersite.side_effect = self.dist_in_usersite + dists = get_installed_distributions(include_editables=False) + assert len(dists) == 1 + assert dists[0].test_name == "normal" + + @patch('pip._vendor.pkg_resources.working_set', workingset) + def test_include_globals(self, mock_dist_is_editable, + mock_dist_is_local, + mock_dist_in_usersite): + mock_dist_is_editable.side_effect = self.dist_is_editable + mock_dist_is_local.side_effect = self.dist_is_local + mock_dist_in_usersite.side_effect = self.dist_in_usersite + dists = get_installed_distributions(local_only=False) + assert len(dists) == 4 + + @patch('pip._vendor.pkg_resources.working_set', workingset) + def test_user_only(self, mock_dist_is_editable, + mock_dist_is_local, + mock_dist_in_usersite): + mock_dist_is_editable.side_effect = self.dist_is_editable + mock_dist_is_local.side_effect = self.dist_is_local + mock_dist_in_usersite.side_effect = self.dist_in_usersite + dists = get_installed_distributions(local_only=False, + user_only=True) + assert len(dists) == 1 + assert dists[0].test_name == "user" + + @pytest.mark.skipif("sys.version_info >= (2,7)") + @patch('pip._vendor.pkg_resources.working_set', workingset_stdlib) + def test_py26_excludes(self, mock_dist_is_editable, + mock_dist_is_local, + mock_dist_in_usersite): + mock_dist_is_editable.side_effect = self.dist_is_editable + mock_dist_is_local.side_effect = self.dist_is_local + mock_dist_in_usersite.side_effect = self.dist_in_usersite + dists = get_installed_distributions() + assert len(dists) == 1 + assert dists[0].key == 'argparse' + + @pytest.mark.skipif("sys.version_info < (2,7)") + @patch('pip._vendor.pkg_resources.working_set', workingset_stdlib) + def test_gte_py27_excludes(self, mock_dist_is_editable, + mock_dist_is_local, + mock_dist_in_usersite): + mock_dist_is_editable.side_effect = self.dist_is_editable + mock_dist_is_local.side_effect = self.dist_is_local + mock_dist_in_usersite.side_effect = self.dist_in_usersite + dists = get_installed_distributions() + assert len(dists) == 0 + + @patch('pip._vendor.pkg_resources.working_set', workingset_freeze) + def test_freeze_excludes(self, mock_dist_is_editable, + mock_dist_is_local, + mock_dist_in_usersite): + mock_dist_is_editable.side_effect = self.dist_is_editable + mock_dist_is_local.side_effect = self.dist_is_local + mock_dist_in_usersite.side_effect = self.dist_in_usersite + dists = get_installed_distributions( + skip=('setuptools', 'pip', 'distribute')) + assert len(dists) == 0 + + +class TestUnpackArchives(object): + """ + test_tar.tgz/test_tar.zip have content as follows engineered to confirm 3 + things: + 1) confirm that reg files, dirs, and symlinks get unpacked + 2) permissions are not preserved (and go by the 022 umask) + 3) reg files with *any* execute perms, get chmod +x + + file.txt 600 regular file + symlink.txt 777 symlink to file.txt + script_owner.sh 700 script where owner can execute + script_group.sh 610 script where group can execute + script_world.sh 601 script where world can execute + dir 744 directory + dir/dirfile 622 regular file + + """ + + def setup(self): + self.tempdir = tempfile.mkdtemp() + self.old_mask = os.umask(0o022) + self.symlink_expected_mode = None + + def teardown(self): + os.umask(self.old_mask) + shutil.rmtree(self.tempdir, ignore_errors=True) + + def mode(self, path): + return stat.S_IMODE(os.stat(path).st_mode) + + def confirm_files(self): + # expectations based on 022 umask set above and the unpack logic that + # sets execute permissions, not preservation + for fname, expected_mode, test in [ + ('file.txt', 0o644, os.path.isfile), + ('symlink.txt', 0o644, os.path.isfile), + ('script_owner.sh', 0o755, os.path.isfile), + ('script_group.sh', 0o755, os.path.isfile), + ('script_world.sh', 0o755, os.path.isfile), + ('dir', 0o755, os.path.isdir), + (os.path.join('dir', 'dirfile'), 0o644, os.path.isfile)]: + path = os.path.join(self.tempdir, fname) + if path.endswith('symlink.txt') and sys.platform == 'win32': + # no symlinks created on windows + continue + assert test(path), path + if sys.platform == 'win32': + # the permissions tests below don't apply in windows + # due to os.chmod being a noop + continue + mode = self.mode(path) + assert mode == expected_mode, ( + "mode: %s, expected mode: %s" % (mode, expected_mode) + ) + + def test_unpack_tgz(self, data): + """ + Test unpacking a *.tgz, and setting execute permissions + """ + test_file = data.packages.join("test_tar.tgz") + untar_file(test_file, self.tempdir) + self.confirm_files() + # Check the timestamp of an extracted file + file_txt_path = os.path.join(self.tempdir, 'file.txt') + mtime = time.gmtime(os.stat(file_txt_path).st_mtime) + assert mtime[0:6] == (2013, 8, 16, 5, 13, 37), mtime + + def test_unpack_zip(self, data): + """ + Test unpacking a *.zip, and setting execute permissions + """ + test_file = data.packages.join("test_zip.zip") + unzip_file(test_file, self.tempdir) + self.confirm_files() + + +class Failer: + def __init__(self, duration=1): + self.succeed_after = time.time() + duration + + def call(self, *args, **kw): + """Fail with OSError self.max_fails times""" + if time.time() < self.succeed_after: + raise OSError("Failed") + + +def test_rmtree_retries(tmpdir, monkeypatch): + """ + Test pip.utils.rmtree will retry failures + """ + monkeypatch.setattr(shutil, 'rmtree', Failer(duration=1).call) + rmtree('foo') + + +def test_rmtree_retries_for_3sec(tmpdir, monkeypatch): + """ + Test pip.utils.rmtree will retry failures for no more than 3 sec + """ + monkeypatch.setattr(shutil, 'rmtree', Failer(duration=5).call) + with pytest.raises(OSError): + rmtree('foo') + + +class Test_normalize_path(object): + # Technically, symlinks are possible on Windows, but you need a special + # permission bit to create them, and Python 2 doesn't support it anyway, so + # it's easiest just to skip this test on Windows altogether. + @pytest.mark.skipif("sys.platform == 'win32'") + def test_resolve_symlinks(self, tmpdir): + print(type(tmpdir)) + print(dir(tmpdir)) + orig_working_dir = os.getcwd() + os.chdir(tmpdir) + try: + d = os.path.join('foo', 'bar') + f = os.path.join(d, 'file1') + os.makedirs(d) + with open(f, 'w'): # Create the file + pass + + os.symlink(d, 'dir_link') + os.symlink(f, 'file_link') + + assert normalize_path( + 'dir_link/file1', resolve_symlinks=True + ) == os.path.join(tmpdir, f) + assert normalize_path( + 'dir_link/file1', resolve_symlinks=False + ) == os.path.join(tmpdir, 'dir_link', 'file1') + + assert normalize_path( + 'file_link', resolve_symlinks=True + ) == os.path.join(tmpdir, f) + assert normalize_path( + 'file_link', resolve_symlinks=False + ) == os.path.join(tmpdir, 'file_link') + finally: + os.chdir(orig_working_dir) + + +class TestHashes(object): + """Tests for pip.utils.hashes""" + + def test_success(self, tmpdir): + """Make sure no error is raised when at least one hash matches. + + Test check_against_path because it calls everything else. + + """ + file = tmpdir / 'to_hash' + file.write('hello') + hashes = Hashes({ + 'sha256': ['2cf24dba5fb0a30e26e83b2ac5b9e29e' + '1b161e5c1fa7425e73043362938b9824'], + 'sha224': ['wrongwrong'], + 'md5': ['5d41402abc4b2a76b9719d911017c592']}) + hashes.check_against_path(file) + + def test_failure(self): + """Hashes should raise HashMismatch when no hashes match.""" + hashes = Hashes({'sha256': ['wrongwrong']}) + with pytest.raises(HashMismatch): + hashes.check_against_file(BytesIO(b'hello')) + + def test_missing_hashes(self): + """MissingHashes should raise HashMissing when any check is done.""" + with pytest.raises(HashMissing): + MissingHashes().check_against_file(BytesIO(b'hello')) + + def test_unknown_hash(self): + """Hashes should raise InstallationError when it encounters an unknown + hash.""" + hashes = Hashes({'badbad': ['dummy']}) + with pytest.raises(InstallationError): + hashes.check_against_file(BytesIO(b'hello')) + + def test_non_zero(self): + """Test that truthiness tests tell whether any known-good hashes + exist.""" + assert Hashes({'sha256': 'dummy'}) + assert not Hashes() + assert not Hashes({}) + + +class TestEncoding(object): + """Tests for pip.utils.encoding""" + + def test_auto_decode_utf16_le(self): + data = ( + b'\xff\xfeD\x00j\x00a\x00n\x00g\x00o\x00=\x00' + b'=\x001\x00.\x004\x00.\x002\x00' + ) + assert auto_decode(data) == "Django==1.4.2" + + def test_auto_decode_no_bom(self): + assert auto_decode(b'foobar') == u'foobar' + + def test_auto_decode_pep263_headers(self): + latin1_req = u'# coding=latin1\n# Pas trop de café' + assert auto_decode(latin1_req.encode('latin1')) == latin1_req + + +class TestBuildDirectory(object): + # No need to test symlinked directories on Windows + @pytest.mark.skipif("sys.platform == 'win32'") + def test_build_directory(self): + with BuildDirectory() as build_dir: + tmp_dir = tempfile.mkdtemp(prefix="pip-build-test") + assert ( + os.path.dirname(build_dir) == + os.path.dirname(os.path.realpath(tmp_dir)) + ) + # are we on a system where /tmp is a symlink + if os.path.realpath(tmp_dir) != os.path.abspath(tmp_dir): + assert os.path.dirname(build_dir) != os.path.dirname(tmp_dir) + else: + assert os.path.dirname(build_dir) == os.path.dirname(tmp_dir) + os.rmdir(tmp_dir) + assert not os.path.exists(tmp_dir) + + +class TestGlibc(object): + def test_manylinux1_check_glibc_version(self): + """ + Test that the check_glibc_version function is robust against weird + glibc version strings. + """ + for two_twenty in ["2.20", + # used by "linaro glibc", see gh-3588 + "2.20-2014.11", + # weird possibilities that I just made up + "2.20+dev", + "2.20-custom", + "2.20.1", + ]: + assert check_glibc_version(two_twenty, 2, 15) + assert check_glibc_version(two_twenty, 2, 20) + assert not check_glibc_version(two_twenty, 2, 21) + assert not check_glibc_version(two_twenty, 3, 15) + assert not check_glibc_version(two_twenty, 1, 15) + + # For strings that we just can't parse at all, we should warn and + # return false + for bad_string in ["asdf", "", "foo.bar"]: + with warnings.catch_warnings(record=True) as ws: + warnings.filterwarnings("always") + assert not check_glibc_version(bad_string, 2, 5) + for w in ws: + if "Expected glibc version with" in str(w.message): + break + else: + # Didn't find the warning we were expecting + assert False diff --git a/tests/unit/test_vcs.py b/tests/unit/test_vcs.py index 61ce9e7c4de..8d393d23cfe 100644 --- a/tests/unit/test_vcs.py +++ b/tests/unit/test_vcs.py @@ -1,5 +1,10 @@ +import pytest from tests.lib import pyversion +from pip.vcs import VersionControl from pip.vcs.bazaar import Bazaar +from pip.vcs.git import Git +from pip.vcs.subversion import Subversion +from mock import Mock if pyversion >= '3': VERBOSE_FALSE = False @@ -7,23 +12,128 @@ VERBOSE_FALSE = 0 +@pytest.fixture +def git(): + git_url = 'http://github.com/pypa/pip-test-package' + refs = { + '0.1': 'a8992fc7ee17e5b9ece022417b64594423caca7c', + '0.1.1': '7d654e66c8fa7149c165ddeffa5b56bc06619458', + '0.1.2': 'f1c1020ebac81f9aeb5c766ff7a772f709e696ee', + 'foo': '5547fa909e83df8bd743d3978d6667497983a4b7', + 'bar': '5547fa909e83df8bd743d3978d6667497983a4b7', + 'master': '5547fa909e83df8bd743d3978d6667497983a4b7', + 'origin/master': '5547fa909e83df8bd743d3978d6667497983a4b7', + 'origin/HEAD': '5547fa909e83df8bd743d3978d6667497983a4b7', + } + sha = refs['foo'] + + git = Git() + git.get_url = Mock(return_value=git_url) + git.get_revision = Mock(return_value=sha) + git.get_short_refs = Mock(return_value=refs) + return git + + +@pytest.fixture +def dist(): + dist = Mock() + dist.egg_name = Mock(return_value='pip_test_package') + return dist + + +def test_git_get_src_requirements(git, dist): + ret = git.get_src_requirement(dist, location='.') + + assert ret == ''.join([ + 'git+http://github.com/pypa/pip-test-package', + '@5547fa909e83df8bd743d3978d6667497983a4b7', + '#egg=pip_test_package' + ]) + + +@pytest.mark.parametrize('ref,result', ( + ('5547fa909e83df8bd743d3978d6667497983a4b7', True), + ('5547fa909', True), + ('abc123', False), + ('foo', False), +)) +def test_git_check_version(git, ref, result): + assert git.check_version('foo', ref) is result + + +def test_translate_egg_surname(): + vc = VersionControl() + assert vc.translate_egg_surname("foo") == "foo" + assert vc.translate_egg_surname("foo/bar") == "foo_bar" + assert vc.translate_egg_surname("foo/1.2.3") == "foo_1.2.3" + + def test_bazaar_simple_urls(): """ Test bzr url support. SSH and launchpad have special handling. """ - http_bzr_repo = Bazaar(url='bzr+http://bzr.myproject.org/MyProject/trunk/#egg=MyProject') - https_bzr_repo = Bazaar(url='bzr+https://bzr.myproject.org/MyProject/trunk/#egg=MyProject') - ssh_bzr_repo = Bazaar(url='bzr+ssh://bzr.myproject.org/MyProject/trunk/#egg=MyProject') - ftp_bzr_repo = Bazaar(url='bzr+ftp://bzr.myproject.org/MyProject/trunk/#egg=MyProject') - sftp_bzr_repo = Bazaar(url='bzr+sftp://bzr.myproject.org/MyProject/trunk/#egg=MyProject') - launchpad_bzr_repo = Bazaar(url='bzr+lp:MyLaunchpadProject#egg=MyLaunchpadProject') - - assert http_bzr_repo.get_url_rev() == ('http://bzr.myproject.org/MyProject/trunk/', None) - assert https_bzr_repo.get_url_rev() == ('https://bzr.myproject.org/MyProject/trunk/', None) - assert ssh_bzr_repo.get_url_rev() == ('bzr+ssh://bzr.myproject.org/MyProject/trunk/', None) - assert ftp_bzr_repo.get_url_rev() == ('ftp://bzr.myproject.org/MyProject/trunk/', None) - assert sftp_bzr_repo.get_url_rev() == ('sftp://bzr.myproject.org/MyProject/trunk/', None) - assert launchpad_bzr_repo.get_url_rev() == ('lp:MyLaunchpadProject', None) + http_bzr_repo = Bazaar( + url='bzr+http://bzr.myproject.org/MyProject/trunk/#egg=MyProject' + ) + https_bzr_repo = Bazaar( + url='bzr+https://bzr.myproject.org/MyProject/trunk/#egg=MyProject' + ) + ssh_bzr_repo = Bazaar( + url='bzr+ssh://bzr.myproject.org/MyProject/trunk/#egg=MyProject' + ) + ftp_bzr_repo = Bazaar( + url='bzr+ftp://bzr.myproject.org/MyProject/trunk/#egg=MyProject' + ) + sftp_bzr_repo = Bazaar( + url='bzr+sftp://bzr.myproject.org/MyProject/trunk/#egg=MyProject' + ) + launchpad_bzr_repo = Bazaar( + url='bzr+lp:MyLaunchpadProject#egg=MyLaunchpadProject' + ) + + assert http_bzr_repo.get_url_rev() == ( + 'http://bzr.myproject.org/MyProject/trunk/', None, + ) + assert https_bzr_repo.get_url_rev() == ( + 'https://bzr.myproject.org/MyProject/trunk/', None, + ) + assert ssh_bzr_repo.get_url_rev() == ( + 'bzr+ssh://bzr.myproject.org/MyProject/trunk/', None, + ) + assert ftp_bzr_repo.get_url_rev() == ( + 'ftp://bzr.myproject.org/MyProject/trunk/', None, + ) + assert sftp_bzr_repo.get_url_rev() == ( + 'sftp://bzr.myproject.org/MyProject/trunk/', None, + ) + assert launchpad_bzr_repo.get_url_rev() == ( + 'lp:MyLaunchpadProject', None, + ) + + +def test_subversion_remove_auth_from_url(): + # Check that the url is doctored appropriately to remove auth elements + # from the url + svn_auth_url = 'https://user:pass@svnrepo.org/svn/project/tags/v0.2' + expected_url = 'https://svnrepo.org/svn/project/tags/v0.2' + url = Subversion.remove_auth_from_url(svn_auth_url) + assert url == expected_url + + # Check that this doesn't impact urls without authentication' + svn_noauth_url = 'https://svnrepo.org/svn/project/tags/v0.2' + expected_url = svn_noauth_url + url = Subversion.remove_auth_from_url(svn_noauth_url) + assert url == expected_url + + # Check that links to specific revisions are handled properly + svn_rev_url = 'https://user:pass@svnrepo.org/svn/project/trunk@8181' + expected_url = 'https://svnrepo.org/svn/project/trunk@8181' + url = Subversion.remove_auth_from_url(svn_rev_url) + assert url == expected_url + svn_rev_url = 'https://svnrepo.org/svn/project/trunk@8181' + expected_url = 'https://svnrepo.org/svn/project/trunk@8181' + url = Subversion.remove_auth_from_url(svn_rev_url) + assert url == expected_url diff --git a/tests/unit/test_wheel.py b/tests/unit/test_wheel.py index 074a16d1983..0e030e5bb57 100644 --- a/tests/unit/test_wheel.py +++ b/tests/unit/test_wheel.py @@ -2,14 +2,13 @@ import os import pytest -from mock import patch +from mock import patch, Mock -from pip._vendor import pkg_resources -from pip import wheel +from pip._vendor.packaging.requirements import Requirement +from pip import pep425tags, wheel +from pip.compat import expanduser, WINDOWS from pip.exceptions import InvalidWheelFilename, UnsupportedWheel -from pip.req import InstallRequirement -from pip.util import unpack_file -from tests.lib import assert_raises_regexp +from pip.utils import unpack_file def test_get_entrypoints(tmpdir): @@ -78,7 +77,7 @@ def test_check_compatibility(): wheel.check_compatibility(higher_v, name) assert 'is not compatible' in str(e) - # Should only log.warn - minor version is greator + # Should only log.warning - minor version is greater higher_v = (vc[0], vc[1] + 1) wheel.check_compatibility(higher_v, name) @@ -128,7 +127,7 @@ def test_missing_version_raises(self): def test_invalid_filename_raises(self): with pytest.raises(InvalidWheelFilename): - w = wheel.Wheel('invalid.whl') + wheel.Wheel('invalid.whl') def test_supported_single_version(self): """ @@ -151,14 +150,98 @@ def test_not_supported_version(self): w = wheel.Wheel('simple-0.1-py2-none-any.whl') assert not w.supported(tags=[('py1', 'none', 'any')]) + @patch('sys.platform', 'darwin') + @patch('pip.pep425tags.get_abbr_impl', lambda: 'cp') + @patch('pip.pep425tags.get_platform', lambda: 'macosx_10_9_intel') + def test_supported_osx_version(self): + """ + Wheels built for OS X 10.6 are supported on 10.9 + """ + tags = pep425tags.get_supported(['27'], False) + w = wheel.Wheel('simple-0.1-cp27-none-macosx_10_6_intel.whl') + assert w.supported(tags=tags) + w = wheel.Wheel('simple-0.1-cp27-none-macosx_10_9_intel.whl') + assert w.supported(tags=tags) + + @patch('sys.platform', 'darwin') + @patch('pip.pep425tags.get_abbr_impl', lambda: 'cp') + @patch('pip.pep425tags.get_platform', lambda: 'macosx_10_6_intel') + def test_not_supported_osx_version(self): + """ + Wheels built for OS X 10.9 are not supported on 10.6 + """ + tags = pep425tags.get_supported(['27'], False) + w = wheel.Wheel('simple-0.1-cp27-none-macosx_10_9_intel.whl') + assert not w.supported(tags=tags) + + @patch('sys.platform', 'darwin') + @patch('pip.pep425tags.get_abbr_impl', lambda: 'cp') + def test_supported_multiarch_darwin(self): + """ + Multi-arch wheels (intel) are supported on components (i386, x86_64) + """ + with patch('pip.pep425tags.get_platform', + lambda: 'macosx_10_5_universal'): + universal = pep425tags.get_supported(['27'], False) + with patch('pip.pep425tags.get_platform', + lambda: 'macosx_10_5_intel'): + intel = pep425tags.get_supported(['27'], False) + with patch('pip.pep425tags.get_platform', + lambda: 'macosx_10_5_x86_64'): + x64 = pep425tags.get_supported(['27'], False) + with patch('pip.pep425tags.get_platform', + lambda: 'macosx_10_5_i386'): + i386 = pep425tags.get_supported(['27'], False) + with patch('pip.pep425tags.get_platform', + lambda: 'macosx_10_5_ppc'): + ppc = pep425tags.get_supported(['27'], False) + with patch('pip.pep425tags.get_platform', + lambda: 'macosx_10_5_ppc64'): + ppc64 = pep425tags.get_supported(['27'], False) + + w = wheel.Wheel('simple-0.1-cp27-none-macosx_10_5_intel.whl') + assert w.supported(tags=intel) + assert w.supported(tags=x64) + assert w.supported(tags=i386) + assert not w.supported(tags=universal) + assert not w.supported(tags=ppc) + assert not w.supported(tags=ppc64) + w = wheel.Wheel('simple-0.1-cp27-none-macosx_10_5_universal.whl') + assert w.supported(tags=universal) + assert w.supported(tags=intel) + assert w.supported(tags=x64) + assert w.supported(tags=i386) + assert w.supported(tags=ppc) + assert w.supported(tags=ppc64) + + @patch('sys.platform', 'darwin') + @patch('pip.pep425tags.get_abbr_impl', lambda: 'cp') + def test_not_supported_multiarch_darwin(self): + """ + Single-arch wheels (x86_64) are not supported on multi-arch (intel) + """ + with patch('pip.pep425tags.get_platform', + lambda: 'macosx_10_5_universal'): + universal = pep425tags.get_supported(['27'], False) + with patch('pip.pep425tags.get_platform', + lambda: 'macosx_10_5_intel'): + intel = pep425tags.get_supported(['27'], False) + + w = wheel.Wheel('simple-0.1-cp27-none-macosx_10_5_i386.whl') + assert not w.supported(tags=intel) + assert not w.supported(tags=universal) + w = wheel.Wheel('simple-0.1-cp27-none-macosx_10_5_x86_64.whl') + assert not w.supported(tags=intel) + assert not w.supported(tags=universal) + def test_support_index_min(self): """ Test results from `support_index_min` """ tags = [ - ('py2', 'none', 'TEST'), - ('py2', 'TEST', 'any'), - ('py2', 'none', 'any'), + ('py2', 'none', 'TEST'), + ('py2', 'TEST', 'any'), + ('py2', 'none', 'any'), ] w = wheel.Wheel('simple-0.1-py2-none-any.whl') assert w.support_index_min(tags=tags) == 2 @@ -170,21 +253,20 @@ def test_support_index_min_none(self): Test `support_index_min` returns None, when wheel not supported """ w = wheel.Wheel('simple-0.1-py2-none-any.whl') - assert w.support_index_min(tags=[]) == None + assert w.support_index_min(tags=[]) is None def test_unpack_wheel_no_flatten(self): - from pip import util + from pip import utils from tempfile import mkdtemp from shutil import rmtree - import os filepath = '../data/packages/meta-1.0-py2.py3-none-any.whl' if not os.path.exists(filepath): pytest.skip("%s does not exist" % filepath) try: tmpdir = mkdtemp() - util.unpack_file(filepath, tmpdir, 'application/zip', None ) - assert os.path.isdir(os.path.join(tmpdir,'meta-1.0.dist-info')) + utils.unpack_file(filepath, tmpdir, 'application/zip', None) + assert os.path.isdir(os.path.join(tmpdir, 'meta-1.0.dist-info')) finally: rmtree(tmpdir) pass @@ -203,26 +285,13 @@ def test_purelib_platlib(self, data): def test_version_underscore_conversion(self): """ - Test that we convert '_' to '-' for versions parsed out of wheel filenames + Test that we convert '_' to '-' for versions parsed out of wheel + filenames """ w = wheel.Wheel('simple-0.1_1-py2-none-any.whl') assert w.version == '0.1-1' -class TestPEP425Tags(object): - - def test_broken_sysconfig(self): - """ - Test that pep425tags still works when sysconfig is broken. - Can be a problem on Python 2.7 - Issue #1074. - """ - import pip.pep425tags - def raises_ioerror(var): - raise IOError("I have the wrong path!") - with patch('pip.pep425tags.sysconfig.get_config_var', raises_ioerror): - assert len(pip.pep425tags.get_supported()) - class TestMoveWheelFiles(object): """ Tests for moving files from wheel src to scheme paths @@ -230,16 +299,17 @@ class TestMoveWheelFiles(object): def prep(self, data, tmpdir): self.name = 'sample' - self.wheelpath = data.packages.join('sample-1.2.0-py2.py3-none-any.whl') - self.req = pkg_resources.Requirement.parse('sample') - self.src = os.path.join(tmpdir, 'src') - self.dest = os.path.join(tmpdir, 'dest') + self.wheelpath = data.packages.join( + 'sample-1.2.0-py2.py3-none-any.whl') + self.req = Requirement('sample') + self.src = os.path.join(tmpdir, 'src') + self.dest = os.path.join(tmpdir, 'dest') unpack_file(self.wheelpath, self.src, None, None) self.scheme = { 'scripts': os.path.join(self.dest, 'bin'), 'purelib': os.path.join(self.dest, 'lib'), 'data': os.path.join(self.dest, 'data'), - } + } self.src_dist_info = os.path.join( self.src, 'sample-1.2.0.dist-info') self.dest_dist_info = os.path.join( @@ -256,23 +326,67 @@ def assert_installed(self): data_file = os.path.join(self.scheme['data'], 'my_data', 'data_file') assert os.path.isfile(data_file) # package data - pkg_data = os.path.join(self.scheme['purelib'], 'sample', 'package_data.dat') + pkg_data = os.path.join( + self.scheme['purelib'], 'sample', 'package_data.dat') + assert os.path.isfile(pkg_data) def test_std_install(self, data, tmpdir): self.prep(data, tmpdir) - wheel.move_wheel_files(self.name, self.req, self.src, scheme=self.scheme) + wheel.move_wheel_files( + self.name, self.req, self.src, scheme=self.scheme) self.assert_installed() + def test_install_prefix(self, data, tmpdir): + prefix = os.path.join(os.path.sep, 'some', 'path') + self.prep(data, tmpdir) + wheel.move_wheel_files( + self.name, + self.req, + self.src, + root=tmpdir, + prefix=prefix, + ) + + bin_dir = 'Scripts' if WINDOWS else 'bin' + assert os.path.exists(os.path.join(tmpdir, 'some', 'path', bin_dir)) + assert os.path.exists(os.path.join(tmpdir, 'some', 'path', 'my_data')) + def test_dist_info_contains_empty_dir(self, data, tmpdir): """ Test that empty dirs are not installed """ # e.g. https://github.com/pypa/pip/issues/1632#issuecomment-38027275 self.prep(data, tmpdir) - src_empty_dir = os.path.join(self.src_dist_info, 'empty_dir', 'empty_dir') + src_empty_dir = os.path.join( + self.src_dist_info, 'empty_dir', 'empty_dir') os.makedirs(src_empty_dir) assert os.path.isdir(src_empty_dir) - wheel.move_wheel_files(self.name, self.req, self.src, scheme=self.scheme) + wheel.move_wheel_files( + self.name, self.req, self.src, scheme=self.scheme) self.assert_installed() - assert not os.path.isdir(os.path.join(self.dest_dist_info, 'empty_dir')) + assert not os.path.isdir( + os.path.join(self.dest_dist_info, 'empty_dir')) + + +class TestWheelBuilder(object): + + def test_skip_building_wheels(self, caplog): + with patch('pip.wheel.WheelBuilder._build_one') as mock_build_one: + wheel_req = Mock(is_wheel=True, editable=False, constraint=False) + reqset = Mock(requirements=Mock(values=lambda: [wheel_req]), + wheel_download_dir='/wheel/dir') + wb = wheel.WheelBuilder(reqset, Mock()) + wb.build() + assert "due to already being wheel" in caplog.text() + assert mock_build_one.mock_calls == [] + + +class TestWheelCache: + + def test_expands_path(self): + wc = wheel.WheelCache("~/.foo/", None) + assert wc._cache_dir == expanduser("~/.foo/") + def test_falsey_path_none(self): + wc = wheel.WheelCache(False, None) + assert wc._cache_dir is None diff --git a/tox.ini b/tox.ini index 20842e06ff0..f1e037798fd 100644 --- a/tox.ini +++ b/tox.ini @@ -1,18 +1,42 @@ [tox] envlist = - docs,py26,py27,py32,py33,py34,pypy + docs, packaging, pep8, py3pep8, py26, py27, py33, py34, py35, pypy [testenv] -deps = - pytest - mock - scripttest>=1.3 - git+https://github.com/pypa/virtualenv@master#egg=virtualenv -commands = - py.test [] +setenv = + # This is required in order to get UTF-8 output inside of the subprocesses + # that our tests use. + LC_CTYPE = en_US.UTF-8 +deps = -r{toxinidir}/dev-requirements.txt +commands = py.test --timeout 300 [] +install_command = python -m pip install {opts} {packages} + +[testenv:py26] +install_command = pip install {opts} {packages} [testenv:docs] deps = sphinx basepython = python2.7 +commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs docs/_build/html + +[testenv:packaging] +deps = + check-manifest + readme_renderer commands = - sphinx-build -W -b html -d {envtmpdir}/doctrees docs docs/_build/html + check-manifest + python setup.py check -m -r -s + +[testenv:pep8] +basepython = python2.7 +deps = flake8==2.3.0 +commands = flake8 . + +[testenv:py3pep8] +basepython = python3.3 +deps = flake8==2.3.0 +commands = flake8 . + +[flake8] +exclude = .tox,*.egg,build,_vendor,data +select = E,W,F