mirror of
https://github.com/kennethreitz/heroku-buildpack-python.git
synced 2026-06-05 23:10:16 +00:00
update pip to v0.3.1
This commit is contained in:
Vendored
+74
@@ -0,0 +1,74 @@
|
||||
Alex Grönholm
|
||||
Alex Morega
|
||||
Alexandre Conrad
|
||||
Andrey Bulgakov
|
||||
Antti Kaihola
|
||||
Armin Ronacher
|
||||
Aziz Köksal
|
||||
Ben Rosser
|
||||
Brian Rosner
|
||||
Carl Meyer
|
||||
Chris McDonough
|
||||
Christian Oudard
|
||||
Clay McClure
|
||||
Cody Soyland
|
||||
Daniel Holth
|
||||
Dave Abrahams
|
||||
David (d1b)
|
||||
Dmitry Gladkov
|
||||
Donald Stufft
|
||||
Francesco
|
||||
Geoffrey Lehée
|
||||
Georgi Valkov
|
||||
Hugo Lopes Tavares
|
||||
Ian Bicking
|
||||
Igor Sobreira
|
||||
Ionel Maries Cristian
|
||||
Jakub Vysoky
|
||||
James Cleveland
|
||||
Jannis Leidel
|
||||
Jay Graves
|
||||
John-Scott Atlakson
|
||||
Jon Parise
|
||||
Jonas Nockert
|
||||
Josh Bronson
|
||||
Kamal Bin Mustafa
|
||||
Kelsey Hightower
|
||||
Kenneth Belitzky
|
||||
Kumar McMillan
|
||||
Luke Macken
|
||||
Masklinn
|
||||
Marc Abramowitz
|
||||
Marcus Smith
|
||||
Markus Hametner
|
||||
Matt Maker
|
||||
Maxime R.
|
||||
Miguel Araujo
|
||||
Nick Stenning
|
||||
Nowell Strite
|
||||
Oliver Tonnhofer
|
||||
Olivier Girardot
|
||||
Patrick Jenkins
|
||||
Paul Moore
|
||||
Paul Nasrat
|
||||
Paul Oswald
|
||||
Paul van der Linden
|
||||
Peter Waller
|
||||
Phil Whelan
|
||||
Piet Delport
|
||||
Przemek Wrzos
|
||||
Qiangning Hong
|
||||
Rafael Caricio
|
||||
Rene Dudfield
|
||||
Roey Berman
|
||||
Ronny Pfannschmidt
|
||||
Rory McCann
|
||||
Simon Cross
|
||||
Stavros Korokithakis
|
||||
Thomas Fenzl
|
||||
Thomas Johansson
|
||||
Vinay Sajip
|
||||
Vitaly Babiy
|
||||
W Trevor King
|
||||
Wil Tan
|
||||
Hsiaoming Yang
|
||||
Vendored
+690
@@ -0,0 +1,690 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
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.
|
||||
|
||||
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 #789).
|
||||
|
||||
* Added "pip list" for listing installed packages and the latest version
|
||||
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)
|
||||
|
||||
* 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)
|
||||
|
||||
* Split help output into general vs command-specific option groups.
|
||||
Thanks Georgi Valkov. (Pull #744; Pull #721 contains preceding refactor)
|
||||
|
||||
* Fixed dependency resolution when installing from archives with uppercase
|
||||
project names. (Pull #724)
|
||||
|
||||
* Fixed problem where re-installs always occurred when using file:// find-links.
|
||||
(Pulls #683/#702)
|
||||
|
||||
* "pip install -v" now shows the full download url, not just the archive name.
|
||||
Thanks Marc Abramowitz (Pull #687)
|
||||
|
||||
* 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)
|
||||
|
||||
* Fix git submodule recursive updates. Thanks Roey Berman. (Pulls #674)
|
||||
|
||||
* Explicitly ignore rel='download' links while looking for html pages.
|
||||
Thanks Maxime R. (Pull #677)
|
||||
|
||||
* --user/--upgrade install options now work together. Thanks 'eevee' for
|
||||
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)
|
||||
|
||||
* Added support for bare paths (including relative paths) as argument to
|
||||
`--find-links`. Thanks Paul Moore for draft patch.
|
||||
|
||||
* 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.
|
||||
|
||||
* 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)
|
||||
|
||||
1.2.1 (2012-09-06)
|
||||
------------------
|
||||
|
||||
* Fixed a regression introduced in 1.2 about raising an exception when
|
||||
not finding any files to uninstall in the current environment. Thanks for
|
||||
the fix, Marcus Smith.
|
||||
|
||||
1.2 (2012-09-01)
|
||||
----------------
|
||||
|
||||
* **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
|
||||
philwhin.
|
||||
|
||||
* 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
|
||||
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
|
||||
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.
|
||||
|
||||
* 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.
|
||||
|
||||
* 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.
|
||||
Trevor King.
|
||||
|
||||
* 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
|
||||
index. Thanks Andrew Knapp for the report.
|
||||
|
||||
* 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
|
||||
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
|
||||
found or accessed.
|
||||
|
||||
* 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.
|
||||
This re-enables using ``==dev`` in requirements files.
|
||||
|
||||
* Moved tests to be run on Travis CI: http://travis-ci.org/pypa/pip
|
||||
|
||||
* Added a better help formatter.
|
||||
|
||||
1.1 (2012-02-16)
|
||||
----------------
|
||||
|
||||
* 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.
|
||||
Thanks Stavros Korokithakis.
|
||||
|
||||
* Added support for authentication with Subversion repositories. Thanks
|
||||
Qiangning Hong.
|
||||
|
||||
* 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
|
||||
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.
|
||||
Thanks Rene Dudfield.
|
||||
|
||||
* 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")
|
||||
in sdist filenames. Thanks Stuart Andrews for report and Olivier Girardot for
|
||||
patch.
|
||||
|
||||
* 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
|
||||
--index-url or --find-links. Thanks Herbert Pfennig.
|
||||
|
||||
* Fixed issue #288 - handle symlinks properly. Thanks lebedov for the patch.
|
||||
|
||||
* 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``;
|
||||
both use a restart-in-venv mechanism that's broken, and neither one is
|
||||
useful since every virtualenv now has pip inside it. Replace ``pip -E
|
||||
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 #22 - pip search should set and return a userful shell status code
|
||||
|
||||
* Fixed issue #351 and #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.
|
||||
|
||||
|
||||
1.0.2 (2011-07-16)
|
||||
------------------
|
||||
|
||||
* 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
|
||||
|
||||
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.
|
||||
|
||||
1.0 (2011-04-04)
|
||||
----------------
|
||||
|
||||
* Added Python 3 support! Huge thanks to Vinay Sajip, Vitaly Babiy, Kelsey
|
||||
Hightower, and Alex Gronholm, among others.
|
||||
|
||||
* Download progress only shown on a real TTY. Thanks Alex Morega.
|
||||
|
||||
* Fixed finding of VCS binaries to not be fooled by same-named directories.
|
||||
Thanks Alex Morega.
|
||||
|
||||
* Fixed uninstall of packages from system Python for users of Debian/Ubuntu
|
||||
python-setuptools package (workaround until fixed in Debian and Ubuntu).
|
||||
|
||||
* Added `get-pip.py <https://raw.github.com/pypa/pip/master/contrib/get-pip.py>`_
|
||||
installer. Simply download and execute it, using the Python interpreter of
|
||||
your choice::
|
||||
|
||||
$ curl -O https://raw.github.com/pypa/pip/master/contrib/get-pip.py
|
||||
$ python get-pip.py
|
||||
|
||||
This may have to be run as root.
|
||||
|
||||
.. note::
|
||||
|
||||
Make sure you have `distribute <http://pypi.python.org/pypi/distribute>`_
|
||||
installed before using the installer!
|
||||
|
||||
0.8.3
|
||||
-----
|
||||
|
||||
* Moved main repository to Github: https://github.com/pypa/pip
|
||||
|
||||
* 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 #163 - Egg name not properly resolved. 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 #204 - rmtree undefined in mercurial.py. Thanks Kelsey Hightower
|
||||
|
||||
* Fixed bug in Git vcs backend that would break during reinstallation.
|
||||
|
||||
* Fixed bug in Mercurial vcs backend related to pip freeze and branch/tag resolution.
|
||||
|
||||
* Fixed bug in version string parsing related to the suffix "-dev".
|
||||
|
||||
0.8.2
|
||||
-----
|
||||
|
||||
* Avoid redundant unpacking of bundles (from pwaller)
|
||||
|
||||
* Fixed issue #32, #150, #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
|
||||
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.
|
||||
|
||||
* Added ``git+https://`` scheme to Git VCS backend.
|
||||
|
||||
0.8.1
|
||||
-----
|
||||
|
||||
* Added global --user flag as shortcut for --install-option="--user". From
|
||||
Ronny Pfannschmidt.
|
||||
|
||||
* Added support for `PyPI mirrors <http://pypi.python.org/mirrors>`_ as
|
||||
defined in `PEP 381 <http://www.python.org/dev/peps/pep-0381/>`_, from
|
||||
Jannis Leidel.
|
||||
|
||||
* 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 #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.
|
||||
Thanks Ronny Pfannschmidt and Wil Tan.
|
||||
|
||||
|
||||
|
||||
0.8
|
||||
---
|
||||
|
||||
* Track which ``build/`` directories pip creates, never remove directories
|
||||
it doesn't create. From Hugo Lopes Tavares.
|
||||
|
||||
* Pip now accepts file:// index URLs. Thanks Dave Abrahams.
|
||||
|
||||
* Various cleanup to make test-running more consistent and less fragile.
|
||||
Thanks Dave Abrahams.
|
||||
|
||||
* Real Windows support (with passing tests). Thanks Dave Abrahams.
|
||||
|
||||
* ``pip-2.7`` etc. scripts are created (Python-version specific scripts)
|
||||
|
||||
* ``contrib/build-standalone`` script creates a runnable ``.zip`` form of
|
||||
pip, from Jannis Leidel
|
||||
|
||||
* Editable git repos are updated when reinstalled
|
||||
|
||||
* Fix problem with ``--editable`` when multiple ``.egg-info/`` directories
|
||||
are found.
|
||||
|
||||
* A number of VCS-related fixes for ``pip freeze``, from Hugo Lopes Tavares.
|
||||
|
||||
* Significant test framework changes, from Hugo Lopes Tavares.
|
||||
|
||||
0.7.2
|
||||
-----
|
||||
|
||||
* Set zip_safe=False to avoid problems some people are encountering where
|
||||
pip is installed as a zip file.
|
||||
|
||||
0.7.1
|
||||
-----
|
||||
|
||||
* Fixed opening of logfile with no directory name. Thanks Alexandre Conrad.
|
||||
|
||||
* Temporary files are consistently cleaned up, especially after
|
||||
installing bundles, also from Alex Conrad.
|
||||
|
||||
* Tests now require at least ScriptTest 1.0.3.
|
||||
|
||||
0.7
|
||||
---
|
||||
|
||||
* Fixed uninstallation on Windows
|
||||
* Added ``pip search`` command.
|
||||
* Tab-complete names of installed distributions for ``pip uninstall``.
|
||||
* Support tab-completion when there is a global-option before the
|
||||
subcommand.
|
||||
* Install header files in standard (scheme-default) location when installing
|
||||
outside a virtualenv. Install them to a slightly more consistent
|
||||
non-standard location inside a virtualenv (since the standard location is
|
||||
a non-writable symlink to the global location).
|
||||
* pip now logs to a central location by default (instead of creating
|
||||
``pip-log.txt`` all over the place) and constantly overwrites the
|
||||
file in question. On Unix and Mac OS X this is ``'$HOME/.pip/pip.log'``
|
||||
and on Windows it's ``'%HOME%\\pip\\pip.log'``. You are still able to
|
||||
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
|
||||
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.
|
||||
* Speed up installing from Mercurial repositories by cloning without
|
||||
updating the working copy multiple times.
|
||||
* Fixed installing directly from directories (e.g.
|
||||
``pip install path/to/dir/``).
|
||||
* Fixed installing editable packages with ``svn+ssh`` URLs.
|
||||
* Don't print unwanted debug information when running the freeze command.
|
||||
* Create log file directory automatically. Thanks Alexandre Conrad.
|
||||
* Make test suite easier to run successfully. Thanks Dave Abrahams.
|
||||
* Fixed "pip install ." and "pip install .."; better error for directory
|
||||
without setup.py. Thanks Alexandre Conrad.
|
||||
* Support Debian/Ubuntu "dist-packages" in zip command. Thanks duckx.
|
||||
* Fix relative --src folder. Thanks Simon Cross.
|
||||
* Handle missing VCS with an error message. Thanks Alexandre Conrad.
|
||||
* Added --no-download option to install; pairs with --no-install to separate
|
||||
download and installation into two steps. Thanks Simon Cross.
|
||||
* Fix uninstalling from requirements file containing -f, -i, or
|
||||
--extra-index-url.
|
||||
* Leftover build directories are now removed. Thanks Alexandre Conrad.
|
||||
|
||||
0.6.3
|
||||
-----
|
||||
|
||||
* Fixed import error on Windows with regard to the backwards compatibility
|
||||
package
|
||||
|
||||
0.6.2
|
||||
-----
|
||||
|
||||
* Fixed uninstall when /tmp is on a different filesystem.
|
||||
|
||||
* Fixed uninstallation of distributions with namespace packages.
|
||||
|
||||
0.6.1
|
||||
-----
|
||||
|
||||
* Added support for the ``https`` and ``http-static`` schemes to the
|
||||
Mercurial and ``ftp`` scheme to the Bazaar backend.
|
||||
|
||||
* Fixed uninstallation of scripts installed with easy_install.
|
||||
|
||||
* Fixed an issue in the package finder that could result in an
|
||||
infinite loop while looking for links.
|
||||
|
||||
* Fixed issue with ``pip bundle`` and local files (which weren't being
|
||||
copied into the bundle), from Whit Morriss.
|
||||
|
||||
0.6
|
||||
---
|
||||
|
||||
* Add ``pip uninstall`` and uninstall-before upgrade (from Carl
|
||||
Meyer).
|
||||
|
||||
* Extended configurability with config files and environment variables.
|
||||
|
||||
* Allow packages to be upgraded, e.g., ``pip install Package==0.1``
|
||||
then ``pip install Package==0.2``.
|
||||
|
||||
* Allow installing/upgrading to Package==dev (fix "Source version does not
|
||||
match target version" errors).
|
||||
|
||||
* Added command and option completion for bash and zsh.
|
||||
|
||||
* Extended integration with virtualenv by providing an option to
|
||||
automatically use an active virtualenv and an option to warn if no active
|
||||
virtualenv is found.
|
||||
|
||||
* Fixed a bug with pip install --download and editable packages, where
|
||||
directories were being set with 0000 permissions, now defaults to 755.
|
||||
|
||||
* Fixed uninstallation of easy_installed console_scripts.
|
||||
|
||||
* Fixed uninstallation on Mac OS X Framework layout installs
|
||||
|
||||
* Fixed bug preventing uninstall of editables with source outside venv.
|
||||
|
||||
* Creates download cache directory if not existing.
|
||||
|
||||
0.5.1
|
||||
-----
|
||||
|
||||
* Fixed a couple little bugs, with git and with extensions.
|
||||
|
||||
0.5
|
||||
---
|
||||
|
||||
* Added ability to override the default log file name (``pip-log.txt``)
|
||||
with the environmental variable ``$PIP_LOG_FILE``.
|
||||
|
||||
* Made the freeze command print installed packages to stdout instead of
|
||||
writing them to a file. Use simple redirection (e.g.
|
||||
``pip freeze > stable-req.txt``) to get a file with requirements.
|
||||
|
||||
* Fixed problem with freezing editable packages from a Git repository.
|
||||
|
||||
* Added support for base URLs using ``<base href='...'>`` when parsing
|
||||
HTML pages.
|
||||
|
||||
* Fixed installing of non-editable packages from version control systems.
|
||||
|
||||
* Fixed issue with Bazaar's bzr+ssh scheme.
|
||||
|
||||
* Added --download-dir option to the install command to retrieve package
|
||||
archives. If given an editable package it will create an archive of it.
|
||||
|
||||
* Added ability to pass local file and directory paths to ``--find-links``,
|
||||
e.g. ``--find-links=file:///path/to/my/private/archive``
|
||||
|
||||
* Reduced the amount of console log messages when fetching a page to find a
|
||||
distribution was problematic. The full messages can be found in pip-log.txt.
|
||||
|
||||
* Added ``--no-deps`` option to install ignore package dependencies
|
||||
|
||||
* Added ``--no-index`` option to ignore the package index (PyPI) temporarily
|
||||
|
||||
* Fixed installing editable packages from Git branches.
|
||||
|
||||
* Fixes freezing of editable packages from Mercurial repositories.
|
||||
|
||||
* Fixed handling read-only attributes of build files, e.g. of Subversion and
|
||||
Bazaar on Windows.
|
||||
|
||||
* When downloading a file from a redirect, use the redirected
|
||||
location's extension to guess the compression (happens specifically
|
||||
when redirecting to a bitbucket.org tip.gz file).
|
||||
|
||||
* Editable freeze URLs now always use revision hash/id rather than tip or
|
||||
branch names which could move.
|
||||
|
||||
* Fixed comparison of repo URLs so incidental differences such as
|
||||
presence/absence of final slashes or quoted/unquoted special
|
||||
characters don't trigger "ignore/switch/wipe/backup" choice.
|
||||
|
||||
* Fixed handling of attempt to checkout editable install to a
|
||||
non-empty, non-repo directory.
|
||||
|
||||
0.4
|
||||
---
|
||||
|
||||
* Make ``-e`` work better with local hg repositories
|
||||
|
||||
* Construct PyPI URLs the exact way easy_install constructs URLs (you
|
||||
might notice this if you use a custom index that is
|
||||
slash-sensitive).
|
||||
|
||||
* Improvements on Windows (from `Ionel Maries Cristian
|
||||
<http://ionelmc.wordpress.com/>`_).
|
||||
|
||||
* Fixed problem with not being able to install private git repositories.
|
||||
|
||||
* Make ``pip zip`` zip all its arguments, not just the first.
|
||||
|
||||
* Fix some filename issues on Windows.
|
||||
|
||||
* Allow the ``-i`` and ``--extra-index-url`` options in requirements
|
||||
files.
|
||||
|
||||
* Fix the way bundle components are unpacked and moved around, to make
|
||||
bundles work.
|
||||
|
||||
* Adds ``-s`` option to allow the access to the global site-packages if a
|
||||
virtualenv is to be created.
|
||||
|
||||
* Fixed support for Subversion 1.6.
|
||||
|
||||
0.3.1
|
||||
-----
|
||||
|
||||
* Improved virtualenv restart and various path/cleanup problems on win32.
|
||||
|
||||
* Fixed a regression with installing from svn repositories (when not
|
||||
using ``-e``).
|
||||
|
||||
* Fixes when installing editable packages that put their source in a
|
||||
subdirectory (like ``src/``).
|
||||
|
||||
* Improve ``pip -h``
|
||||
|
||||
0.3
|
||||
---
|
||||
|
||||
* Added support for editable packages created from Git, Mercurial and Bazaar
|
||||
repositories and ability to freeze them. Refactored support for version
|
||||
control systems.
|
||||
|
||||
* Do not use ``sys.exit()`` from inside the code, instead use a
|
||||
return. This will make it easier to invoke programmatically.
|
||||
|
||||
* Put the install record in ``Package.egg-info/installed-files.txt``
|
||||
(previously they went in
|
||||
``site-packages/install-record-Package.txt``).
|
||||
|
||||
* Fix a problem with ``pip freeze`` not including ``-e svn+`` when an
|
||||
svn structure is peculiar.
|
||||
|
||||
* Allow ``pip -E`` to work with a virtualenv that uses a different
|
||||
version of Python than the parent environment.
|
||||
|
||||
* Fixed Win32 virtualenv (``-E``) option.
|
||||
|
||||
* Search the links passed in with ``-f`` for packages.
|
||||
|
||||
* Detect zip files, even when the file doesn't have a ``.zip``
|
||||
extension and it is served with the wrong Content-Type.
|
||||
|
||||
* Installing editable from existing source now works, like ``pip
|
||||
install -e some/path/`` will install the package in ``some/path/``.
|
||||
Most importantly, anything that package requires will also be
|
||||
installed by pip.
|
||||
|
||||
* Add a ``--path`` option to ``pip un/zip``, so you can avoid zipping
|
||||
files that are outside of where you expect.
|
||||
|
||||
* Add ``--simulate`` option to ``pip zip``.
|
||||
|
||||
0.2.1
|
||||
-----
|
||||
|
||||
* Fixed small problem that prevented using ``pip.py`` without actually
|
||||
installing pip.
|
||||
|
||||
* Fixed ``--upgrade``, which would download and appear to install
|
||||
upgraded packages, but actually just reinstall the existing package.
|
||||
|
||||
* Fixed Windows problem with putting the install record in the right
|
||||
place, and generating the ``pip`` script with Setuptools.
|
||||
|
||||
* Download links that include embedded spaces or other unsafe
|
||||
characters (those characters get %-encoded).
|
||||
|
||||
* Fixed use of URLs in requirement files, and problems with some blank
|
||||
lines.
|
||||
|
||||
* Turn some tar file errors into warnings.
|
||||
|
||||
0.2
|
||||
---
|
||||
|
||||
* Renamed to ``pip``, and to install you now do ``pip install
|
||||
PACKAGE``
|
||||
|
||||
* Added command ``pip zip PACKAGE`` and ``pip unzip PACKAGE``. This
|
||||
is particularly intended for Google App Engine to manage libraries
|
||||
to stay under the 1000-file limit.
|
||||
|
||||
* Some fixes to bundles, especially editable packages and when
|
||||
creating a bundle using unnamed packages (like just an svn
|
||||
repository without ``#egg=Package``).
|
||||
|
||||
0.1.4
|
||||
-----
|
||||
|
||||
* Added an option ``--install-option`` to pass options to pass
|
||||
arguments to ``setup.py install``
|
||||
|
||||
* ``.svn/`` directories are no longer included in bundles, as these
|
||||
directories are specific to a version of svn -- if you build a
|
||||
bundle on a system with svn 1.5, you can't use the checkout on a
|
||||
system with svn 1.4. Instead a file ``svn-checkout.txt`` is
|
||||
included that notes the original location and revision, and the
|
||||
command you can use to turn it back into an svn checkout. (Probably
|
||||
unpacking the bundle should, maybe optionally, recreate this
|
||||
information -- but that is not currently implemented, and it would
|
||||
require network access.)
|
||||
|
||||
* Avoid ambiguities over project name case, where for instance
|
||||
MyPackage and mypackage would be considered different packages.
|
||||
This in particular caused problems on Macs, where ``MyPackage/`` and
|
||||
``mypackage/`` are the same directory.
|
||||
|
||||
* Added support for an environmental variable
|
||||
``$PIP_DOWNLOAD_CACHE`` which will cache package downloads, so
|
||||
future installations won't require large downloads. Network access
|
||||
is still required, but just some downloads will be avoided when
|
||||
using this.
|
||||
|
||||
0.1.3
|
||||
-----
|
||||
|
||||
* Always use ``svn checkout`` (not ``export``) so that
|
||||
``tag_svn_revision`` settings give the revision of the package.
|
||||
|
||||
* Don't update checkouts that came from ``.pybundle`` files.
|
||||
|
||||
0.1.2
|
||||
-----
|
||||
|
||||
* Improve error text when there are errors fetching HTML pages when
|
||||
seeking packages.
|
||||
|
||||
* Improve bundles: include empty directories, make them work with
|
||||
editable packages.
|
||||
|
||||
* If you use ``-E env`` and the environment ``env/`` doesn't exist, a
|
||||
new virtual environment will be created.
|
||||
|
||||
* Fix ``dependency_links`` for finding packages.
|
||||
|
||||
0.1.1
|
||||
-----
|
||||
|
||||
* Fixed a NameError exception when running pip outside of a
|
||||
virtualenv environment.
|
||||
|
||||
* Added HTTP proxy support (from Prabhu Ramachandran)
|
||||
|
||||
* Fixed use of ``hashlib.md5`` on python2.5+ (also from Prabhu
|
||||
Ramachandran)
|
||||
|
||||
0.1
|
||||
---
|
||||
|
||||
* Initial release
|
||||
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
Copyright (c) 2008-2013 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
|
||||
"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.
|
||||
|
||||
|
||||
License for Bundle of CA Root Certificates (pip/cacert.pem)
|
||||
===========================================================
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
02110-1301
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
include AUTHORS.txt
|
||||
include LICENSE.txt
|
||||
include CHANGES.txt
|
||||
include PROJECT.txt
|
||||
include pip/cacert.pem
|
||||
recursive-include docs *.txt
|
||||
recursive-include docs *.html
|
||||
recursive-exclude docs/_build *.txt
|
||||
prune docs/_build/_sources
|
||||
Vendored
+776
@@ -0,0 +1,776 @@
|
||||
Metadata-Version: 1.1
|
||||
Name: pip
|
||||
Version: 1.3.1
|
||||
Summary: A tool for installing and managing Python packages.
|
||||
Home-page: http://www.pip-installer.org
|
||||
Author: The pip developers
|
||||
Author-email: python-virtualenv@groups.google.com
|
||||
License: MIT
|
||||
Description:
|
||||
Project Info
|
||||
============
|
||||
|
||||
* Project Page: https://github.com/pypa/pip
|
||||
* Bug Tracking: https://github.com/pypa/pip/issues
|
||||
* Mailing list: http://groups.google.com/group/python-virtualenv
|
||||
* Docs: http://www.pip-installer.org
|
||||
* IRC: #pip.
|
||||
|
||||
|
||||
Quickstart
|
||||
==========
|
||||
|
||||
Install a package:
|
||||
|
||||
::
|
||||
|
||||
$ pip install SomePackage==1.0
|
||||
[...]
|
||||
Successfully installed SomePackage
|
||||
|
||||
Show what files were installed:
|
||||
|
||||
::
|
||||
|
||||
$ pip show --files SomePackage
|
||||
Name: SomePackage
|
||||
Version: 1.0
|
||||
Location: /my/env/lib/pythonx.x/site-packages
|
||||
Files:
|
||||
../somepackage/__init__.py
|
||||
[...]
|
||||
|
||||
List what packages are outdated:
|
||||
|
||||
::
|
||||
|
||||
$ pip list --outdated
|
||||
SomePackage (Current: 1.0 Latest: 2.0)
|
||||
|
||||
Upgrade a package:
|
||||
|
||||
::
|
||||
|
||||
$ pip install --upgrade SomePackage
|
||||
[...]
|
||||
Found existing installation: SomePackage 1.0
|
||||
Uninstalling SomePackage:
|
||||
Successfully uninstalled SomePackage
|
||||
Running setup.py install for SomePackage
|
||||
Successfully installed SomePackage
|
||||
|
||||
Uninstall a package:
|
||||
|
||||
::
|
||||
|
||||
$ pip uninstall SomePackage
|
||||
Uninstalling SomePackage:
|
||||
/my/env/lib/pythonx.x/site-packages/somepackage
|
||||
Proceed (y/n)? y
|
||||
Successfully uninstalled SomePackage
|
||||
|
||||
|
||||
Changelog
|
||||
=========
|
||||
|
||||
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.
|
||||
|
||||
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 #789).
|
||||
|
||||
* Added "pip list" for listing installed packages and the latest version
|
||||
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)
|
||||
|
||||
* 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)
|
||||
|
||||
* Split help output into general vs command-specific option groups.
|
||||
Thanks Georgi Valkov. (Pull #744; Pull #721 contains preceding refactor)
|
||||
|
||||
* Fixed dependency resolution when installing from archives with uppercase
|
||||
project names. (Pull #724)
|
||||
|
||||
* Fixed problem where re-installs always occurred when using file:// find-links.
|
||||
(Pulls #683/#702)
|
||||
|
||||
* "pip install -v" now shows the full download url, not just the archive name.
|
||||
Thanks Marc Abramowitz (Pull #687)
|
||||
|
||||
* 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)
|
||||
|
||||
* Fix git submodule recursive updates. Thanks Roey Berman. (Pulls #674)
|
||||
|
||||
* Explicitly ignore rel='download' links while looking for html pages.
|
||||
Thanks Maxime R. (Pull #677)
|
||||
|
||||
* --user/--upgrade install options now work together. Thanks 'eevee' for
|
||||
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)
|
||||
|
||||
* Added support for bare paths (including relative paths) as argument to
|
||||
`--find-links`. Thanks Paul Moore for draft patch.
|
||||
|
||||
* 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.
|
||||
|
||||
* 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)
|
||||
|
||||
1.2.1 (2012-09-06)
|
||||
------------------
|
||||
|
||||
* Fixed a regression introduced in 1.2 about raising an exception when
|
||||
not finding any files to uninstall in the current environment. Thanks for
|
||||
the fix, Marcus Smith.
|
||||
|
||||
1.2 (2012-09-01)
|
||||
----------------
|
||||
|
||||
* **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
|
||||
philwhin.
|
||||
|
||||
* 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
|
||||
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
|
||||
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.
|
||||
|
||||
* 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.
|
||||
|
||||
* 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.
|
||||
Trevor King.
|
||||
|
||||
* 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
|
||||
index. Thanks Andrew Knapp for the report.
|
||||
|
||||
* 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
|
||||
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
|
||||
found or accessed.
|
||||
|
||||
* 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.
|
||||
This re-enables using ``==dev`` in requirements files.
|
||||
|
||||
* Moved tests to be run on Travis CI: http://travis-ci.org/pypa/pip
|
||||
|
||||
* Added a better help formatter.
|
||||
|
||||
1.1 (2012-02-16)
|
||||
----------------
|
||||
|
||||
* 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.
|
||||
Thanks Stavros Korokithakis.
|
||||
|
||||
* Added support for authentication with Subversion repositories. Thanks
|
||||
Qiangning Hong.
|
||||
|
||||
* 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
|
||||
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.
|
||||
Thanks Rene Dudfield.
|
||||
|
||||
* 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")
|
||||
in sdist filenames. Thanks Stuart Andrews for report and Olivier Girardot for
|
||||
patch.
|
||||
|
||||
* 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
|
||||
--index-url or --find-links. Thanks Herbert Pfennig.
|
||||
|
||||
* Fixed issue #288 - handle symlinks properly. Thanks lebedov for the patch.
|
||||
|
||||
* 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``;
|
||||
both use a restart-in-venv mechanism that's broken, and neither one is
|
||||
useful since every virtualenv now has pip inside it. Replace ``pip -E
|
||||
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 #22 - pip search should set and return a userful shell status code
|
||||
|
||||
* Fixed issue #351 and #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.
|
||||
|
||||
|
||||
1.0.2 (2011-07-16)
|
||||
------------------
|
||||
|
||||
* 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
|
||||
|
||||
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.
|
||||
|
||||
1.0 (2011-04-04)
|
||||
----------------
|
||||
|
||||
* Added Python 3 support! Huge thanks to Vinay Sajip, Vitaly Babiy, Kelsey
|
||||
Hightower, and Alex Gronholm, among others.
|
||||
|
||||
* Download progress only shown on a real TTY. Thanks Alex Morega.
|
||||
|
||||
* Fixed finding of VCS binaries to not be fooled by same-named directories.
|
||||
Thanks Alex Morega.
|
||||
|
||||
* Fixed uninstall of packages from system Python for users of Debian/Ubuntu
|
||||
python-setuptools package (workaround until fixed in Debian and Ubuntu).
|
||||
|
||||
* Added `get-pip.py <https://raw.github.com/pypa/pip/master/contrib/get-pip.py>`_
|
||||
installer. Simply download and execute it, using the Python interpreter of
|
||||
your choice::
|
||||
|
||||
$ curl -O https://raw.github.com/pypa/pip/master/contrib/get-pip.py
|
||||
$ python get-pip.py
|
||||
|
||||
This may have to be run as root.
|
||||
|
||||
.. note::
|
||||
|
||||
Make sure you have `distribute <http://pypi.python.org/pypi/distribute>`_
|
||||
installed before using the installer!
|
||||
|
||||
0.8.3
|
||||
-----
|
||||
|
||||
* Moved main repository to Github: https://github.com/pypa/pip
|
||||
|
||||
* 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 #163 - Egg name not properly resolved. 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 #204 - rmtree undefined in mercurial.py. Thanks Kelsey Hightower
|
||||
|
||||
* Fixed bug in Git vcs backend that would break during reinstallation.
|
||||
|
||||
* Fixed bug in Mercurial vcs backend related to pip freeze and branch/tag resolution.
|
||||
|
||||
* Fixed bug in version string parsing related to the suffix "-dev".
|
||||
|
||||
0.8.2
|
||||
-----
|
||||
|
||||
* Avoid redundant unpacking of bundles (from pwaller)
|
||||
|
||||
* Fixed issue #32, #150, #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
|
||||
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.
|
||||
|
||||
* Added ``git+https://`` scheme to Git VCS backend.
|
||||
|
||||
0.8.1
|
||||
-----
|
||||
|
||||
* Added global --user flag as shortcut for --install-option="--user". From
|
||||
Ronny Pfannschmidt.
|
||||
|
||||
* Added support for `PyPI mirrors <http://pypi.python.org/mirrors>`_ as
|
||||
defined in `PEP 381 <http://www.python.org/dev/peps/pep-0381/>`_, from
|
||||
Jannis Leidel.
|
||||
|
||||
* 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 #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.
|
||||
Thanks Ronny Pfannschmidt and Wil Tan.
|
||||
|
||||
|
||||
|
||||
0.8
|
||||
---
|
||||
|
||||
* Track which ``build/`` directories pip creates, never remove directories
|
||||
it doesn't create. From Hugo Lopes Tavares.
|
||||
|
||||
* Pip now accepts file:// index URLs. Thanks Dave Abrahams.
|
||||
|
||||
* Various cleanup to make test-running more consistent and less fragile.
|
||||
Thanks Dave Abrahams.
|
||||
|
||||
* Real Windows support (with passing tests). Thanks Dave Abrahams.
|
||||
|
||||
* ``pip-2.7`` etc. scripts are created (Python-version specific scripts)
|
||||
|
||||
* ``contrib/build-standalone`` script creates a runnable ``.zip`` form of
|
||||
pip, from Jannis Leidel
|
||||
|
||||
* Editable git repos are updated when reinstalled
|
||||
|
||||
* Fix problem with ``--editable`` when multiple ``.egg-info/`` directories
|
||||
are found.
|
||||
|
||||
* A number of VCS-related fixes for ``pip freeze``, from Hugo Lopes Tavares.
|
||||
|
||||
* Significant test framework changes, from Hugo Lopes Tavares.
|
||||
|
||||
0.7.2
|
||||
-----
|
||||
|
||||
* Set zip_safe=False to avoid problems some people are encountering where
|
||||
pip is installed as a zip file.
|
||||
|
||||
0.7.1
|
||||
-----
|
||||
|
||||
* Fixed opening of logfile with no directory name. Thanks Alexandre Conrad.
|
||||
|
||||
* Temporary files are consistently cleaned up, especially after
|
||||
installing bundles, also from Alex Conrad.
|
||||
|
||||
* Tests now require at least ScriptTest 1.0.3.
|
||||
|
||||
0.7
|
||||
---
|
||||
|
||||
* Fixed uninstallation on Windows
|
||||
* Added ``pip search`` command.
|
||||
* Tab-complete names of installed distributions for ``pip uninstall``.
|
||||
* Support tab-completion when there is a global-option before the
|
||||
subcommand.
|
||||
* Install header files in standard (scheme-default) location when installing
|
||||
outside a virtualenv. Install them to a slightly more consistent
|
||||
non-standard location inside a virtualenv (since the standard location is
|
||||
a non-writable symlink to the global location).
|
||||
* pip now logs to a central location by default (instead of creating
|
||||
``pip-log.txt`` all over the place) and constantly overwrites the
|
||||
file in question. On Unix and Mac OS X this is ``'$HOME/.pip/pip.log'``
|
||||
and on Windows it's ``'%HOME%\\pip\\pip.log'``. You are still able to
|
||||
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
|
||||
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.
|
||||
* Speed up installing from Mercurial repositories by cloning without
|
||||
updating the working copy multiple times.
|
||||
* Fixed installing directly from directories (e.g.
|
||||
``pip install path/to/dir/``).
|
||||
* Fixed installing editable packages with ``svn+ssh`` URLs.
|
||||
* Don't print unwanted debug information when running the freeze command.
|
||||
* Create log file directory automatically. Thanks Alexandre Conrad.
|
||||
* Make test suite easier to run successfully. Thanks Dave Abrahams.
|
||||
* Fixed "pip install ." and "pip install .."; better error for directory
|
||||
without setup.py. Thanks Alexandre Conrad.
|
||||
* Support Debian/Ubuntu "dist-packages" in zip command. Thanks duckx.
|
||||
* Fix relative --src folder. Thanks Simon Cross.
|
||||
* Handle missing VCS with an error message. Thanks Alexandre Conrad.
|
||||
* Added --no-download option to install; pairs with --no-install to separate
|
||||
download and installation into two steps. Thanks Simon Cross.
|
||||
* Fix uninstalling from requirements file containing -f, -i, or
|
||||
--extra-index-url.
|
||||
* Leftover build directories are now removed. Thanks Alexandre Conrad.
|
||||
|
||||
0.6.3
|
||||
-----
|
||||
|
||||
* Fixed import error on Windows with regard to the backwards compatibility
|
||||
package
|
||||
|
||||
0.6.2
|
||||
-----
|
||||
|
||||
* Fixed uninstall when /tmp is on a different filesystem.
|
||||
|
||||
* Fixed uninstallation of distributions with namespace packages.
|
||||
|
||||
0.6.1
|
||||
-----
|
||||
|
||||
* Added support for the ``https`` and ``http-static`` schemes to the
|
||||
Mercurial and ``ftp`` scheme to the Bazaar backend.
|
||||
|
||||
* Fixed uninstallation of scripts installed with easy_install.
|
||||
|
||||
* Fixed an issue in the package finder that could result in an
|
||||
infinite loop while looking for links.
|
||||
|
||||
* Fixed issue with ``pip bundle`` and local files (which weren't being
|
||||
copied into the bundle), from Whit Morriss.
|
||||
|
||||
0.6
|
||||
---
|
||||
|
||||
* Add ``pip uninstall`` and uninstall-before upgrade (from Carl
|
||||
Meyer).
|
||||
|
||||
* Extended configurability with config files and environment variables.
|
||||
|
||||
* Allow packages to be upgraded, e.g., ``pip install Package==0.1``
|
||||
then ``pip install Package==0.2``.
|
||||
|
||||
* Allow installing/upgrading to Package==dev (fix "Source version does not
|
||||
match target version" errors).
|
||||
|
||||
* Added command and option completion for bash and zsh.
|
||||
|
||||
* Extended integration with virtualenv by providing an option to
|
||||
automatically use an active virtualenv and an option to warn if no active
|
||||
virtualenv is found.
|
||||
|
||||
* Fixed a bug with pip install --download and editable packages, where
|
||||
directories were being set with 0000 permissions, now defaults to 755.
|
||||
|
||||
* Fixed uninstallation of easy_installed console_scripts.
|
||||
|
||||
* Fixed uninstallation on Mac OS X Framework layout installs
|
||||
|
||||
* Fixed bug preventing uninstall of editables with source outside venv.
|
||||
|
||||
* Creates download cache directory if not existing.
|
||||
|
||||
0.5.1
|
||||
-----
|
||||
|
||||
* Fixed a couple little bugs, with git and with extensions.
|
||||
|
||||
0.5
|
||||
---
|
||||
|
||||
* Added ability to override the default log file name (``pip-log.txt``)
|
||||
with the environmental variable ``$PIP_LOG_FILE``.
|
||||
|
||||
* Made the freeze command print installed packages to stdout instead of
|
||||
writing them to a file. Use simple redirection (e.g.
|
||||
``pip freeze > stable-req.txt``) to get a file with requirements.
|
||||
|
||||
* Fixed problem with freezing editable packages from a Git repository.
|
||||
|
||||
* Added support for base URLs using ``<base href='...'>`` when parsing
|
||||
HTML pages.
|
||||
|
||||
* Fixed installing of non-editable packages from version control systems.
|
||||
|
||||
* Fixed issue with Bazaar's bzr+ssh scheme.
|
||||
|
||||
* Added --download-dir option to the install command to retrieve package
|
||||
archives. If given an editable package it will create an archive of it.
|
||||
|
||||
* Added ability to pass local file and directory paths to ``--find-links``,
|
||||
e.g. ``--find-links=file:///path/to/my/private/archive``
|
||||
|
||||
* Reduced the amount of console log messages when fetching a page to find a
|
||||
distribution was problematic. The full messages can be found in pip-log.txt.
|
||||
|
||||
* Added ``--no-deps`` option to install ignore package dependencies
|
||||
|
||||
* Added ``--no-index`` option to ignore the package index (PyPI) temporarily
|
||||
|
||||
* Fixed installing editable packages from Git branches.
|
||||
|
||||
* Fixes freezing of editable packages from Mercurial repositories.
|
||||
|
||||
* Fixed handling read-only attributes of build files, e.g. of Subversion and
|
||||
Bazaar on Windows.
|
||||
|
||||
* When downloading a file from a redirect, use the redirected
|
||||
location's extension to guess the compression (happens specifically
|
||||
when redirecting to a bitbucket.org tip.gz file).
|
||||
|
||||
* Editable freeze URLs now always use revision hash/id rather than tip or
|
||||
branch names which could move.
|
||||
|
||||
* Fixed comparison of repo URLs so incidental differences such as
|
||||
presence/absence of final slashes or quoted/unquoted special
|
||||
characters don't trigger "ignore/switch/wipe/backup" choice.
|
||||
|
||||
* Fixed handling of attempt to checkout editable install to a
|
||||
non-empty, non-repo directory.
|
||||
|
||||
0.4
|
||||
---
|
||||
|
||||
* Make ``-e`` work better with local hg repositories
|
||||
|
||||
* Construct PyPI URLs the exact way easy_install constructs URLs (you
|
||||
might notice this if you use a custom index that is
|
||||
slash-sensitive).
|
||||
|
||||
* Improvements on Windows (from `Ionel Maries Cristian
|
||||
<http://ionelmc.wordpress.com/>`_).
|
||||
|
||||
* Fixed problem with not being able to install private git repositories.
|
||||
|
||||
* Make ``pip zip`` zip all its arguments, not just the first.
|
||||
|
||||
* Fix some filename issues on Windows.
|
||||
|
||||
* Allow the ``-i`` and ``--extra-index-url`` options in requirements
|
||||
files.
|
||||
|
||||
* Fix the way bundle components are unpacked and moved around, to make
|
||||
bundles work.
|
||||
|
||||
* Adds ``-s`` option to allow the access to the global site-packages if a
|
||||
virtualenv is to be created.
|
||||
|
||||
* Fixed support for Subversion 1.6.
|
||||
|
||||
0.3.1
|
||||
-----
|
||||
|
||||
* Improved virtualenv restart and various path/cleanup problems on win32.
|
||||
|
||||
* Fixed a regression with installing from svn repositories (when not
|
||||
using ``-e``).
|
||||
|
||||
* Fixes when installing editable packages that put their source in a
|
||||
subdirectory (like ``src/``).
|
||||
|
||||
* Improve ``pip -h``
|
||||
|
||||
0.3
|
||||
---
|
||||
|
||||
* Added support for editable packages created from Git, Mercurial and Bazaar
|
||||
repositories and ability to freeze them. Refactored support for version
|
||||
control systems.
|
||||
|
||||
* Do not use ``sys.exit()`` from inside the code, instead use a
|
||||
return. This will make it easier to invoke programmatically.
|
||||
|
||||
* Put the install record in ``Package.egg-info/installed-files.txt``
|
||||
(previously they went in
|
||||
``site-packages/install-record-Package.txt``).
|
||||
|
||||
* Fix a problem with ``pip freeze`` not including ``-e svn+`` when an
|
||||
svn structure is peculiar.
|
||||
|
||||
* Allow ``pip -E`` to work with a virtualenv that uses a different
|
||||
version of Python than the parent environment.
|
||||
|
||||
* Fixed Win32 virtualenv (``-E``) option.
|
||||
|
||||
* Search the links passed in with ``-f`` for packages.
|
||||
|
||||
* Detect zip files, even when the file doesn't have a ``.zip``
|
||||
extension and it is served with the wrong Content-Type.
|
||||
|
||||
* Installing editable from existing source now works, like ``pip
|
||||
install -e some/path/`` will install the package in ``some/path/``.
|
||||
Most importantly, anything that package requires will also be
|
||||
installed by pip.
|
||||
|
||||
* Add a ``--path`` option to ``pip un/zip``, so you can avoid zipping
|
||||
files that are outside of where you expect.
|
||||
|
||||
* Add ``--simulate`` option to ``pip zip``.
|
||||
|
||||
0.2.1
|
||||
-----
|
||||
|
||||
* Fixed small problem that prevented using ``pip.py`` without actually
|
||||
installing pip.
|
||||
|
||||
* Fixed ``--upgrade``, which would download and appear to install
|
||||
upgraded packages, but actually just reinstall the existing package.
|
||||
|
||||
* Fixed Windows problem with putting the install record in the right
|
||||
place, and generating the ``pip`` script with Setuptools.
|
||||
|
||||
* Download links that include embedded spaces or other unsafe
|
||||
characters (those characters get %-encoded).
|
||||
|
||||
* Fixed use of URLs in requirement files, and problems with some blank
|
||||
lines.
|
||||
|
||||
* Turn some tar file errors into warnings.
|
||||
|
||||
0.2
|
||||
---
|
||||
|
||||
* Renamed to ``pip``, and to install you now do ``pip install
|
||||
PACKAGE``
|
||||
|
||||
* Added command ``pip zip PACKAGE`` and ``pip unzip PACKAGE``. This
|
||||
is particularly intended for Google App Engine to manage libraries
|
||||
to stay under the 1000-file limit.
|
||||
|
||||
* Some fixes to bundles, especially editable packages and when
|
||||
creating a bundle using unnamed packages (like just an svn
|
||||
repository without ``#egg=Package``).
|
||||
|
||||
0.1.4
|
||||
-----
|
||||
|
||||
* Added an option ``--install-option`` to pass options to pass
|
||||
arguments to ``setup.py install``
|
||||
|
||||
* ``.svn/`` directories are no longer included in bundles, as these
|
||||
directories are specific to a version of svn -- if you build a
|
||||
bundle on a system with svn 1.5, you can't use the checkout on a
|
||||
system with svn 1.4. Instead a file ``svn-checkout.txt`` is
|
||||
included that notes the original location and revision, and the
|
||||
command you can use to turn it back into an svn checkout. (Probably
|
||||
unpacking the bundle should, maybe optionally, recreate this
|
||||
information -- but that is not currently implemented, and it would
|
||||
require network access.)
|
||||
|
||||
* Avoid ambiguities over project name case, where for instance
|
||||
MyPackage and mypackage would be considered different packages.
|
||||
This in particular caused problems on Macs, where ``MyPackage/`` and
|
||||
``mypackage/`` are the same directory.
|
||||
|
||||
* Added support for an environmental variable
|
||||
``$PIP_DOWNLOAD_CACHE`` which will cache package downloads, so
|
||||
future installations won't require large downloads. Network access
|
||||
is still required, but just some downloads will be avoided when
|
||||
using this.
|
||||
|
||||
0.1.3
|
||||
-----
|
||||
|
||||
* Always use ``svn checkout`` (not ``export``) so that
|
||||
``tag_svn_revision`` settings give the revision of the package.
|
||||
|
||||
* Don't update checkouts that came from ``.pybundle`` files.
|
||||
|
||||
0.1.2
|
||||
-----
|
||||
|
||||
* Improve error text when there are errors fetching HTML pages when
|
||||
seeking packages.
|
||||
|
||||
* Improve bundles: include empty directories, make them work with
|
||||
editable packages.
|
||||
|
||||
* If you use ``-E env`` and the environment ``env/`` doesn't exist, a
|
||||
new virtual environment will be created.
|
||||
|
||||
* Fix ``dependency_links`` for finding packages.
|
||||
|
||||
0.1.1
|
||||
-----
|
||||
|
||||
* Fixed a NameError exception when running pip outside of a
|
||||
virtualenv environment.
|
||||
|
||||
* Added HTTP proxy support (from Prabhu Ramachandran)
|
||||
|
||||
* Fixed use of ``hashlib.md5`` on python2.5+ (also from Prabhu
|
||||
Ramachandran)
|
||||
|
||||
0.1
|
||||
---
|
||||
|
||||
* Initial release
|
||||
|
||||
Keywords: easy_install distutils setuptools egg virtualenv
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: MIT License
|
||||
Classifier: Topic :: Software Development :: Build Tools
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.5
|
||||
Classifier: Programming Language :: Python :: 2.6
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.1
|
||||
Classifier: Programming Language :: Python :: 3.2
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
Project Info
|
||||
============
|
||||
|
||||
* Project Page: https://github.com/pypa/pip
|
||||
* Bug Tracking: https://github.com/pypa/pip/issues
|
||||
* Mailing list: http://groups.google.com/group/python-virtualenv
|
||||
* Docs: http://www.pip-installer.org
|
||||
* IRC: #pip.
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
pip
|
||||
===
|
||||
|
||||
.. image:: https://secure.travis-ci.org/pypa/pip.png?branch=develop
|
||||
:target: http://travis-ci.org/pypa/pip
|
||||
|
||||
For documentation, see http://www.pip-installer.org
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
.. _`Configuration`:
|
||||
|
||||
Configuration
|
||||
=================
|
||||
|
||||
Config file
|
||||
------------
|
||||
|
||||
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.
|
||||
|
||||
* 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`
|
||||
|
||||
You can set a custom path location for the config file using the environment variable ``PIP_CONFIG_FILE``.
|
||||
|
||||
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
|
||||
HTTP timeout (``--default-timeout``) to 60 seconds your config file would
|
||||
look like this:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[global]
|
||||
timeout = 60
|
||||
index-url = http://download.zope.org/ppix
|
||||
|
||||
Each subcommand can be configured optionally in its own section so that every
|
||||
global setting with the same name will be overridden; e.g. decreasing the
|
||||
``timeout`` to ``10`` seconds when running the `freeze`
|
||||
(`Freezing Requirements <./#freezing-requirements>`_) command and using
|
||||
``60`` seconds for all other commands is possible with:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[global]
|
||||
timeout = 60
|
||||
|
||||
[freeze]
|
||||
timeout = 10
|
||||
|
||||
|
||||
Boolean options like ``--ignore-installed`` or ``--no-dependencies`` can be
|
||||
set like this:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[install]
|
||||
ignore-installed = true
|
||||
no-dependencies = yes
|
||||
|
||||
Appending options like ``--find-links`` can be written on multiple lines:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[global]
|
||||
find-links =
|
||||
http://download.example.com
|
||||
|
||||
[install]
|
||||
find-links =
|
||||
http://mirror1.example.com
|
||||
http://mirror2.example.com
|
||||
|
||||
|
||||
Environment Variables
|
||||
---------------------
|
||||
|
||||
pip's command line options can be set with
|
||||
environment variables using the format ``PIP_<UPPER_LONG_NAME>`` . Dashes (``-``) have to replaced with underscores (``_``).
|
||||
|
||||
For example, to set the default timeout::
|
||||
|
||||
export PIP_DEFAULT_TIMEOUT=60
|
||||
|
||||
This is the same as passing the option to pip directly::
|
||||
|
||||
pip --default-timeout=60 [...]
|
||||
|
||||
To set options that can be set multiple times on the command line, just add spaces in between values. For example::
|
||||
|
||||
export PIP_FIND_LINKS="http://mirror1.example.com http://mirror2.example.com"
|
||||
|
||||
is the same as calling::
|
||||
|
||||
pip install --find-links=http://mirror1.example.com --find-links=http://mirror2.example.com
|
||||
|
||||
|
||||
Config Precedence
|
||||
-----------------
|
||||
|
||||
Command line options have precedence over environment variables, which have precedence over the config file.
|
||||
|
||||
Within the config file, command specific sections have precedence over the global section.
|
||||
|
||||
Examples:
|
||||
|
||||
- ``--host=foo`` overrides ``PIP_HOST=foo``
|
||||
- ``PIP_HOST=foo`` overrides a config file with ``[global] host = foo``
|
||||
- A command specific section in the config file ``[<command>] host = bar``
|
||||
overrides the option with same name in the ``[global]`` config file section
|
||||
|
||||
|
||||
Command Completion
|
||||
------------------
|
||||
|
||||
pip comes with support for command line completion in bash and zsh.
|
||||
|
||||
To setup for bash::
|
||||
|
||||
$ pip completion --bash >> ~/.profile
|
||||
|
||||
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::
|
||||
|
||||
eval "`pip completion --bash`"
|
||||
|
||||
Vendored
+131
@@ -0,0 +1,131 @@
|
||||
============
|
||||
Cookbook
|
||||
============
|
||||
|
||||
.. _`Requirements Files`:
|
||||
|
||||
Requirements Files
|
||||
******************
|
||||
|
||||
A key idea in pip is that package versions listed in requirement files (or as :ref:`pip install` arguments),
|
||||
have precedence over those that are located during the normal dependency resolution process that uses "install_requires" metadata.
|
||||
|
||||
This allows users to be in control of specifying an environment of packages that are known to work together.
|
||||
|
||||
Instead of running something like ``pip install MyApp`` and getting whatever libraries come along,
|
||||
you'd run ``pip install -r requirements.txt`` where "requirements.txt" contains something like::
|
||||
|
||||
MyApp
|
||||
Framework==0.9.4
|
||||
Library>=0.2
|
||||
|
||||
Regardless of what MyApp lists in ``setup.py``, you'll get a specific version
|
||||
of Framework (0.9.4) and at least the 0.2 version of
|
||||
Library. Additionally, you can add optional libraries and support tools that MyApp doesn't strictly
|
||||
require, giving people a set of recommended libraries.
|
||||
|
||||
Requirement files are intended to exhaust an environment and to be *flat*.
|
||||
Maybe ``MyApp`` requires ``Framework``, and ``Framework`` requires ``Library``.
|
||||
It is encouraged to still list all these in a single requirement file.
|
||||
It is the nature of Python programs that there are implicit bindings *directly*
|
||||
between MyApp and Library. For instance, Framework might expose one
|
||||
of Library's objects, and so if Library is updated it might directly
|
||||
break MyApp. If that happens you can update the requirements file to
|
||||
force an earlier version of Library, and you can do that without
|
||||
having to re-release MyApp at all.
|
||||
|
||||
To create a new requirements file from a known working environment, use::
|
||||
|
||||
$ pip freeze > stable-req.txt
|
||||
|
||||
This will write a listing of *all* installed libraries to ``stable-req.txt``
|
||||
with exact versions for every library.
|
||||
|
||||
For more information, see:
|
||||
|
||||
* :ref:`Requirements File Format`
|
||||
* :ref:`pip freeze`
|
||||
|
||||
|
||||
.. _`Downloading Archives`:
|
||||
|
||||
Downloading archives
|
||||
********************
|
||||
|
||||
pip allows you to *just* download the source archives for your requirements, without installing anything and without regard to what's already installed.
|
||||
|
||||
::
|
||||
|
||||
$ pip install --download <DIR> -r requirements.txt
|
||||
|
||||
or, for a specific package::
|
||||
|
||||
$ pip install --download <DIR> SomePackage
|
||||
|
||||
|
||||
Unpacking archives
|
||||
******************
|
||||
|
||||
pip allows you to *just* unpack archives to a build directory without installing them to site-packages. This can be useful to troubleshoot install errors or to inspect what is being installed.
|
||||
|
||||
::
|
||||
|
||||
$ pip install --no-install SomePackage
|
||||
|
||||
If you're in a virtualenv, the build dir is ``<virtualenv path>/build``. Otherwise, it's ``<OS temp dir>/pip-build-<username>``
|
||||
|
||||
Afterwards, to finish the job of installing unpacked archives, run::
|
||||
|
||||
$ pip install --no-download SomePackage
|
||||
|
||||
|
||||
|
||||
.. _`Fast & Local Installs`:
|
||||
|
||||
Fast & Local Installs
|
||||
*********************
|
||||
|
||||
Often, you will want a fast install from local archives, without probing PyPI.
|
||||
|
||||
First, :ref:`download the archives <Downloading Archives>` that fulfill your requirements::
|
||||
|
||||
$ pip install --download <DIR> -r requirements.txt
|
||||
|
||||
Then, install using :ref:`--find-links <--find-links>` and :ref:`--no-index <--no-index>`::
|
||||
|
||||
$ pip install --no-index --find-links=[file://]<DIR> -r requirements.txt
|
||||
|
||||
|
||||
|
||||
"Non-recursive" upgrades
|
||||
************************
|
||||
|
||||
``pip install ---upgrade`` is currently written to perform a "recursive upgrade".
|
||||
|
||||
E.g. supposing:
|
||||
|
||||
* `SomePackage-1.0` requires `AnotherPackage>=1.0`
|
||||
* `SomePackage-2.0` requires `AnotherPackage>=1.0` and `OneMorePoject==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.
|
||||
|
||||
If you would like to perform a "non-recursive upgrade" perform 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`.
|
||||
|
||||
|
||||
Ensuring Repeatability
|
||||
**********************
|
||||
|
||||
Three things are required to fully guarantee a repeatable installation using requirements files.
|
||||
|
||||
1. The requirements file was generated by ``pip freeze`` or you're sure it only contains requirements that specify a specific version.
|
||||
2. The installation is performed using :ref:`--no-deps <install_--no-deps>`. This guarantees that only what is explicitly listed in the requirements file is installed.
|
||||
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.
|
||||
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
===========
|
||||
Development
|
||||
===========
|
||||
|
||||
Pull Requests
|
||||
=============
|
||||
|
||||
Submit Pull Requests against the `develop` 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.
|
||||
|
||||
Automated Testing
|
||||
=================
|
||||
|
||||
All pull requests and merges to 'develop' branch are tested in `Travis <https://travis-ci.org/>`_
|
||||
based on our `.travis.yml file <https://github.com/pypa/pip/blob/develop/.travis.yml>`_.
|
||||
|
||||
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 <https://travis-ci.org/pypa/pip/pull_requests>`_
|
||||
|
||||
The only way to trigger Travis to run again for a pull request, is to submit another change to the pull branch.
|
||||
|
||||
We also have Jenkins CI that runs regularly for certain python versions on windows and centos.
|
||||
|
||||
Running tests
|
||||
=============
|
||||
|
||||
OS Requirements: subversion, bazaar, git, and mercurial.
|
||||
|
||||
Python Requirements: nose, virtualenv, scripttest, and mock
|
||||
|
||||
Ways to run the tests locally:
|
||||
|
||||
::
|
||||
|
||||
$ python setup.py test # Using the setuptools test plugin
|
||||
$ nosetests # Using nosetests directly
|
||||
$ tox # Using tox against pip's tox.ini
|
||||
|
||||
|
||||
Getting Involved
|
||||
================
|
||||
|
||||
The pip project welcomes help in the following ways:
|
||||
|
||||
- Making Pull Requests for code, tests, or docs.
|
||||
- Commenting on open issues and pull requests.
|
||||
- Helping to answer questions on the mailing list.
|
||||
|
||||
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.
|
||||
|
||||
:<oldp>/<newp>: refers to the old and new versions of pip.
|
||||
:<oldv>/<newv>: refers to the old and new versions of virtualenv.
|
||||
|
||||
1. Upgrade distribute, if needed:
|
||||
|
||||
#. Upgrade distribute in ``virtualenv:develop`` using the :ref:`Refresh virtualenv` process.
|
||||
#. Create a pull request against ``pip:develop`` with a modified ``.travis.yml`` file that installs virtualenv from ``virtualenv:develop``, to confirm the travis builds are still passing.
|
||||
|
||||
2. Create Release branches:
|
||||
|
||||
#. Create ``pip:<newp>`` branch.
|
||||
#. In ``pip:develop``, change ``pip.version`` to '<newp>.post1'.
|
||||
#. Create ``virtualenv:<newv>`` branch.
|
||||
#. In ``virtualenv:develop``, change ``virtualenv.version`` to '<newv>.post1'.
|
||||
|
||||
3. Prepare "rcX":
|
||||
|
||||
#. In ``pip:<newp>``, change ``pip.version`` to '<newp>rcX', and tag with '<newp>rcX'.
|
||||
#. Build a pip sdist from ``pip:<newp>``, and build it into ``virtualenv:<newv>`` using the :ref:`Refresh virtualenv` process.
|
||||
#. In ``virtualenv:<newv>``, change ``virtualenv.version`` to '<newv>rcX', and tag with '<newv>rcX'.
|
||||
|
||||
4. Announce ``pip-<newp>rcX`` and ``virtualenv-<newv>rcX`` with the :ref:`RC Install Instructions` and elicit feedback.
|
||||
|
||||
5. Apply fixes to 'rcX':
|
||||
|
||||
#. Apply fixes to ``pip:<newp>`` and ``virtualenv:<newv>``
|
||||
#. Periodically merge fixes to ``pip:develop`` and ``virtualenv:develop``
|
||||
|
||||
6. Repeat #4 thru #6 if needed.
|
||||
|
||||
7. Final Release:
|
||||
|
||||
#. In ``pip:<newp>``, change ``pip.version`` to '<newp>', and tag with '<newp>'.
|
||||
#. Merge ``pip:<newp>`` to ``pip:master``.
|
||||
#. Build a pip sdist from ``pip:<newp>``, and load it into ``virtualenv:<newv>`` using the :ref:`Refresh virtualenv` process.
|
||||
#. Merge ``vitualenv:<newv>`` to ``virtualenv:develop``.
|
||||
#. In ``virtualenv:<newv>``, change ``virtualenv.version`` to '<newv>', and tag with '<newv>'.
|
||||
#. Merge ``virtualenv:<newp>`` to ``virtualenv:master``
|
||||
#. Build and upload pip and virtualenv sdists to PyPI.
|
||||
|
||||
.. _`Refresh virtualenv`:
|
||||
|
||||
Refresh virtualenv
|
||||
++++++++++++++++++
|
||||
|
||||
#. Set the embedded versions of pip, distribute and setuptools in ``bin/refresh-support-files.py``
|
||||
#. Additionally, set the version of distribute in ``virtualenv_embedded/distribute_setup.py``, and setuptools in ``virtualenv_embedded/ez_setup.py``
|
||||
#. Run ``bin/refresh-support-files.py`` to download the latest versions.
|
||||
When specifying a beta of pip not on pypi, the last part of this script will fail. In this case, the pip sdist needs to be placed manually into ``virtualenv_support``.
|
||||
#. Run ``bin/rebuild-script.py`` to rebuild virtualenv based on the latest versions.
|
||||
|
||||
|
||||
.. _`RC Install Instructions`:
|
||||
|
||||
RC Install Instructions
|
||||
+++++++++++++++++++++++
|
||||
|
||||
#. Download and unpack ``https://github.com/pypa/virtualenv/archive/<newv>rcX.tar.gz``
|
||||
#. Run: ``python virtualenv-<newv>rcX/virtualenv.py myVE``
|
||||
#. ``myVE/bin/pip`` will be the <newp>rcX version of pip.
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
pip
|
||||
===
|
||||
|
||||
A tool for installing and managing Python packages.
|
||||
|
||||
`Mailing list <http://groups.google.com/group/python-virtualenv>`_ ``|``
|
||||
`Issues <https://github.com/pypa/pip/issues>`_ ``|``
|
||||
`Github <https://github.com/pypa/pip>`_ ``|``
|
||||
`PyPI <https://pypi.python.org/pypi/pip/>`_ ``|``
|
||||
irc:#pip
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
quickstart
|
||||
installing
|
||||
usage
|
||||
cookbook
|
||||
logic
|
||||
configuration
|
||||
other-tools
|
||||
development
|
||||
news
|
||||
|
||||
Vendored
+93
@@ -0,0 +1,93 @@
|
||||
.. _`Installation`:
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
.. warning::
|
||||
|
||||
Prior to version 1.3, pip did not use SSL for downloading packages from PyPI, and thus left
|
||||
users more vulnerable to security threats. We advise installing at least version 1.3.
|
||||
If you're using `virtualenv <http://www.virtualenv.org>`_ to install pip, we advise installing
|
||||
at least version 1.9, which contains pip version 1.3.
|
||||
|
||||
|
||||
Python & OS Support
|
||||
-------------------
|
||||
|
||||
pip works with CPython versions 2.5, 2.6, 2.7, 3.1, 3.2, 3.3 and also pypy.
|
||||
|
||||
pip works on Unix/Linux, OS X, and Windows.
|
||||
|
||||
|
||||
Using virtualenv
|
||||
----------------
|
||||
|
||||
The easiest way to install and use pip is with `virtualenv
|
||||
<http://www.virtualenv.org>`_, since every virtualenv has pip (and it's dependencies) installed into it
|
||||
automatically.
|
||||
|
||||
This does not require root access or modify your system Python
|
||||
installation. For instance::
|
||||
|
||||
$ virtualenv my_env
|
||||
$ . my_env/bin/activate
|
||||
(my_env)$ pip install SomePackage
|
||||
|
||||
When used in this manner, pip will only affect the active virtual environment.
|
||||
|
||||
See the `virtualenv installation instructions <http://www.virtualenv.org/en/latest/#installation>`_.
|
||||
|
||||
Installing Globally
|
||||
-------------------
|
||||
|
||||
pip can be installed globally in order to manage global packages.
|
||||
Often this requires the installation to be performed as root.
|
||||
|
||||
.. warning::
|
||||
|
||||
We advise against using `easy_install <http://pythonhosted.org/distribute/easy_install.html>`_ to install pip, because easy_install
|
||||
does not download from PyPI over SSL, so the installation might be insecure.
|
||||
Since pip can then be used to install packages (which execute code on
|
||||
your computer), it is better to go through a trusted path.
|
||||
|
||||
|
||||
Requirements
|
||||
++++++++++++
|
||||
|
||||
pip requires either `setuptools <https://pypi.python.org/pypi/setuptools>`_
|
||||
or `distribute <https://pypi.python.org/pypi/distribute>`_.
|
||||
|
||||
See the `Distribute Install Instructions <https://pypi.python.org/pypi/distribute/>`_ or the
|
||||
`Setuptools Install Instructions <https://pypi.python.org/pypi/setuptools#installation-instructions>`_
|
||||
|
||||
If installing pip using a linux package manager, these requirements will be installed for you.
|
||||
|
||||
.. warning::
|
||||
|
||||
If you are using Python 3.X you **must** use distribute; setuptools doesn't
|
||||
support Python 3.X.
|
||||
|
||||
|
||||
Using get-pip
|
||||
+++++++++++++
|
||||
|
||||
After installing the requirements:
|
||||
|
||||
::
|
||||
|
||||
$ curl -O https://raw.github.com/pypa/pip/master/contrib/get-pip.py
|
||||
$ [sudo] python get-pip.py
|
||||
|
||||
|
||||
Installing from source
|
||||
++++++++++++++++++++++
|
||||
|
||||
After installing the requirements:
|
||||
|
||||
::
|
||||
|
||||
$ curl -O https://pypi.python.org/packages/source/p/pip/pip-X.X.tar.gz
|
||||
$ tar xvfz pip-X.X.tar.gz
|
||||
$ cd pip-X.X
|
||||
$ [sudo] python setup.py install
|
||||
|
||||
Vendored
+268
@@ -0,0 +1,268 @@
|
||||
.. _`pip logic`:
|
||||
|
||||
================
|
||||
Internal Details
|
||||
================
|
||||
|
||||
.. _`Requirements File Format`:
|
||||
|
||||
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::
|
||||
|
||||
<requirement specifier>
|
||||
<archive url/path>
|
||||
[-e] <local project path>
|
||||
[-e] <vcs project url>
|
||||
|
||||
See the :ref:`pip install Examples<pip install Examples>` for examples of all these forms.
|
||||
|
||||
A line beginning with ``#`` is treated as a comment and ignored.
|
||||
|
||||
Additionally, the following :ref:`Package Index Options <Package Index 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>`
|
||||
|
||||
For example, to specify :ref:`--no-index <--no-index>` and 2 :ref:`--find-links <--find-links>` locations:
|
||||
|
||||
::
|
||||
|
||||
--no-index
|
||||
--find-links /my/local/archives
|
||||
--find-links http://some.archives.com/archives
|
||||
|
||||
|
||||
Lastly, if you wish, you can refer to other requirements files, like this::
|
||||
|
||||
-r more_requirements.txt
|
||||
|
||||
.. _`Requirement Specifiers`:
|
||||
|
||||
Requirement Specifiers
|
||||
======================
|
||||
|
||||
pip supports installing from "requirement specifiers" as implemented in
|
||||
`pkg_resources Requirements <http://packages.python.org/distribute/pkg_resources.html#requirement-objects>`_
|
||||
|
||||
Some Examples::
|
||||
|
||||
FooProject >= 1.2
|
||||
Fizzy [foo, bar]
|
||||
PickyThing<1.6,>1.9,!=1.9.6,<2.0a0,==2.4c1
|
||||
SomethingWhoseVersionIDontCareAbout
|
||||
|
||||
|
||||
.. _`VCS Support`:
|
||||
|
||||
VCS Support
|
||||
===========
|
||||
|
||||
pip supports installing from Git, Mercurial, Subversion and Bazaar, and detects the type of VCS using url prefixes: "git+", "hg+", "bzr+", "svn+".
|
||||
|
||||
pip requires a working VCS command on your path: git, hg, svn, or bzr.
|
||||
|
||||
VCS projects can be installed in :ref:`editable mode <editable-installs>` (using the :ref:`--editable <install_--editable>` option) or not.
|
||||
|
||||
* For editable installs, the clone location by default is "<venv path>/src/SomeProject" in virtual environments, and "<cwd>/src/SomeProject" for global installs.
|
||||
The :ref:`--src <install_--src>` option can be used to modify this location.
|
||||
* For non-editable installs, the project is built locally in a temp dir and then installed normally.
|
||||
|
||||
The url suffix "egg=<project name>" is used by pip in it's dependency logic to identify the project prior to pip downloading and analyzing the metadata.
|
||||
|
||||
Git
|
||||
~~~
|
||||
|
||||
pip currently supports cloning over ``git``, ``git+http`` and ``git+ssh``::
|
||||
|
||||
git+git://git.myproject.org/MyProject#egg=MyProject
|
||||
git+http://git.myproject.org/MyProject#egg=MyProject
|
||||
git+ssh://git.myproject.org/MyProject#egg=MyProject
|
||||
|
||||
Passing branch names, a commit hash or a tag name is also possible::
|
||||
|
||||
git://git.myproject.org/MyProject.git@master#egg=MyProject
|
||||
git://git.myproject.org/MyProject.git@v1.0#egg=MyProject
|
||||
git://git.myproject.org/MyProject.git@da39a3ee5e6b4b0d3255bfef95601890afd80709#egg=MyProject
|
||||
|
||||
Mercurial
|
||||
~~~~~~~~~
|
||||
|
||||
The supported schemes are: ``hg+http``, ``hg+https``,
|
||||
``hg+static-http`` and ``hg+ssh``::
|
||||
|
||||
hg+http://hg.myproject.org/MyProject#egg=MyProject
|
||||
hg+https://hg.myproject.org/MyProject#egg=MyProject
|
||||
hg+ssh://hg.myproject.org/MyProject#egg=MyProject
|
||||
|
||||
You can also specify a revision number, a revision hash, a tag name or a local
|
||||
branch name::
|
||||
|
||||
hg+http://hg.myproject.org/MyProject@da39a3ee5e6b#egg=MyProject
|
||||
hg+http://hg.myproject.org/MyProject@2019#egg=MyProject
|
||||
hg+http://hg.myproject.org/MyProject@v1.0#egg=MyProject
|
||||
hg+http://hg.myproject.org/MyProject@special_feature#egg=MyProject
|
||||
|
||||
Subversion
|
||||
~~~~~~~~~~
|
||||
|
||||
pip supports the URL schemes ``svn``, ``svn+svn``, ``svn+http``, ``svn+https``, ``svn+ssh``.
|
||||
You can also give specific revisions to an SVN URL, like::
|
||||
|
||||
svn+svn://svn.myproject.org/svn/MyProject#egg=MyProject
|
||||
svn+http://svn.myproject.org/svn/MyProject/trunk@2019#egg=MyProject
|
||||
|
||||
which will check out revision 2019. ``@{20080101}`` would also check
|
||||
out the revision from 2008-01-01. You can only check out specific
|
||||
revisions using ``-e svn+...``.
|
||||
|
||||
Bazaar
|
||||
~~~~~~
|
||||
|
||||
pip supports Bazaar using the ``bzr+http``, ``bzr+https``, ``bzr+ssh``,
|
||||
``bzr+sftp``, ``bzr+ftp`` and ``bzr+lp`` schemes::
|
||||
|
||||
bzr+http://bzr.myproject.org/MyProject/trunk#egg=MyProject
|
||||
bzr+sftp://user@myproject.org/MyProject/trunk#egg=MyProject
|
||||
bzr+ssh://user@myproject.org/MyProject/trunk#egg=MyProject
|
||||
bzr+ftp://user@myproject.org/MyProject/trunk#egg=MyProject
|
||||
bzr+lp:MyProject#egg=MyProject
|
||||
|
||||
Tags or revisions can be installed like this::
|
||||
|
||||
bzr+https://bzr.myproject.org/MyProject/trunk@2019#egg=MyProject
|
||||
bzr+http://bzr.myproject.org/MyProject/trunk@v1.0#egg=MyProject
|
||||
|
||||
|
||||
Finding Packages
|
||||
================
|
||||
|
||||
pip searches for packages on `PyPI <http://pypi.python.org>`_ using the
|
||||
`http simple interface <http://pypi.python.org/simple>`_,
|
||||
which is documented `here <http://packages.python.org/distribute/easy_install.html#package-index-api>`_
|
||||
and `there <http://www.python.org/dev/peps/pep-0301/>`_
|
||||
|
||||
pip offers a set of :ref:`Package Index Options <Package Index Options>` for modifying how packages are found.
|
||||
|
||||
See the :ref:`pip install Examples<pip install Examples>`.
|
||||
|
||||
|
||||
.. _`SSL Certificate Verification`:
|
||||
|
||||
SSL Certificate Verification
|
||||
============================
|
||||
|
||||
Starting with v1.3, pip provides SSL certificate verification over https, for the purpose
|
||||
of providing secure, certified downloads from PyPI.
|
||||
|
||||
This is supported by default in all Python versions pip supports, except Python 2.5.
|
||||
|
||||
Python 2.5 users can :ref:`install an SSL backport <SSL Backport>`, which provides ssl support for older pythons.
|
||||
Pip does not try to install this automatically because it requires a compiler, which not all systems will have.
|
||||
|
||||
Although not recommended, Python 2.5 users who are unable to install ssl, can use the global option,
|
||||
``--insecure``, to allow access to PyPI w/o attempting SSL certificate verification. This option will only be visible
|
||||
when ssl is not importable. This is *not* a general option.
|
||||
|
||||
|
||||
.. _`SSL Backport`:
|
||||
|
||||
Installing the SSL Backport
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. warning::
|
||||
|
||||
We advise against using ``pip`` itself to install the ssl backport, because it won't be secure
|
||||
until *after* installing ssl. Likewise, `easy_install <http://pythonhosted.org/distribute/easy_install.html>`_ is not advised, because it
|
||||
does not currently support ssl.
|
||||
|
||||
|
||||
1. Download the ssl archive:
|
||||
|
||||
* Using a Browser:
|
||||
|
||||
1. Go to `this url <https://pypi.python.org/pypi/ssl/1.15>`_.
|
||||
2. Confirm the identity of the site is valid.
|
||||
Most browsers provide this information to the left of the URL bar in the form of padlock icon that you can click on to confirm the site is verified.
|
||||
3. Scroll down, and click to download ``ssl-1.15.tar.gz``.
|
||||
|
||||
* Using curl, which supports ssl certificate verification:
|
||||
::
|
||||
|
||||
$ curl -O https://pypi.python.org/packages/source/s/ssl/ssl-1.15.tar.gz
|
||||
|
||||
2. Confirm the md5sum:
|
||||
::
|
||||
$ md5sum ssl-1.15.tar.gz
|
||||
81ea8a1175e437b4c769ae65b3290e0c ssl-1.15.tar.gz
|
||||
|
||||
3. Unpack the archive, and change into the ``ssl-1.15`` directory.
|
||||
4. Run: ``python setup.py install``.
|
||||
|
||||
|
||||
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`
|
||||
|
||||
|
||||
Download Cache
|
||||
==============
|
||||
|
||||
pip offers a :ref:`--download-cache <install_--download-cache>` option for installs to prevent redundant downloads of archives from PyPI.
|
||||
|
||||
The point of this cache is *not* to circumvent the index crawling process, but to *just* prevent redundant downloads.
|
||||
|
||||
Items are stored in this cache based on the url the archive was found at, not simply the archive name.
|
||||
|
||||
If you want a fast/local install solution that circumvents crawling PyPI, see the :ref:`Fast & Local Installs` Cookbook entry.
|
||||
|
||||
Like all options, :ref:`--download-cache <install_--download-cache>`, can also be set as an environment variable, or placed into the pip config file.
|
||||
See the :ref:`Configuration` section.
|
||||
|
||||
|
||||
.. _`editable-installs`:
|
||||
|
||||
"Editable" Installs
|
||||
===================
|
||||
|
||||
"Editable" installs are fundamentally `"setuptools develop mode" <http://packages.python.org/distribute/setuptools.html#development-mode>`_ installs.
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
|
||||
setuptools & pkg_resources
|
||||
==========================
|
||||
|
||||
Internally, pip uses the `setuptools` package, and the `pkg_resources` module, which are available from the project, `Setuptools`_, or it's fork `Distribute`_.
|
||||
|
||||
pip can work with either `Setuptools`_ or `Distribute`_, although for Python 3, `Distribute`_ is required.
|
||||
|
||||
Here are some examples of how pip uses `setuptools` and `pkg_resources`:
|
||||
|
||||
* The core of pip's install process uses the `setuptools`'s "install" command.
|
||||
* Editable ("-e") installs use the `setuptools`'s "develop" command.
|
||||
* pip uses `pkg_resources` for version parsing, for detecting version conflicts, and to determine what projects are installed,
|
||||
|
||||
|
||||
.. _Setuptools: http://pypi.python.org/pypi/setuptools/0.6c11
|
||||
.. _Distribute: http://pypi.python.org/pypi/distribute/
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
========
|
||||
News
|
||||
========
|
||||
|
||||
Next Release
|
||||
============
|
||||
|
||||
Beta and final releases of 1.3 are planned for Feb 2013.
|
||||
|
||||
|
||||
.. include:: ../CHANGES.txt
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
===================
|
||||
Other tools
|
||||
===================
|
||||
|
||||
virtualenv
|
||||
----------
|
||||
|
||||
pip is most nutritious when used with `virtualenv
|
||||
<http://pypi.python.org/pypi/virtualenv>`__. One of the reasons pip
|
||||
doesn't install "multi-version" eggs is that virtualenv removes much of the need
|
||||
for it. Because pip is installed by virtualenv, just use
|
||||
``path/to/my/environment/bin/pip`` to install things into that
|
||||
specific environment.
|
||||
|
||||
To tell pip to only run if there is a virtualenv currently activated,
|
||||
and to bail if not, use::
|
||||
|
||||
export PIP_REQUIRE_VIRTUALENV=true
|
||||
|
||||
|
||||
easy_install
|
||||
------------
|
||||
|
||||
pip was originally written to improve on `easy_install <http://pythonhosted.org/distribute/easy_install.html>`_ in the following ways:
|
||||
|
||||
* All packages are downloaded before installation. Partially-completed
|
||||
installation doesn't occur as a result.
|
||||
|
||||
* Care is taken to present useful output on the console.
|
||||
|
||||
* The reasons for actions are kept track of. For instance, if a package is
|
||||
being installed, pip keeps track of why that package was required.
|
||||
|
||||
* Error messages should be useful.
|
||||
|
||||
* The code is relatively concise and cohesive, making it easier to use
|
||||
programmatically.
|
||||
|
||||
* Packages don't have to be installed as egg archives, they can be installed
|
||||
flat (while keeping the egg metadata).
|
||||
|
||||
* Native support for other version control systems (Git, Mercurial and Bazaar)
|
||||
|
||||
* Uninstallation of packages.
|
||||
|
||||
* Simple to define fixed sets of requirements and reliably reproduce a
|
||||
set of packages.
|
||||
|
||||
pip doesn't do everything that easy_install does. Specifically:
|
||||
|
||||
* It cannot install from eggs. It only installs from source. (In the
|
||||
future it would be good if it could install binaries from Windows ``.exe``
|
||||
or ``.msi`` -- binary install on other platforms is not a priority.)
|
||||
|
||||
* It is incompatible with some packages that extensively customize distutils
|
||||
or setuptools in their ``setup.py`` files.
|
||||
|
||||
|
||||
buildout
|
||||
--------
|
||||
|
||||
If you are using `zc.buildout
|
||||
<http://pypi.python.org/pypi/zc.buildout>`_ you should look at
|
||||
`gp.recipe.pip <http://pypi.python.org/pypi/gp.recipe.pip>`_ as an
|
||||
option to use pip and virtualenv in your buildouts.
|
||||
|
||||
Vendored
+53
@@ -0,0 +1,53 @@
|
||||
|
||||
Quickstart
|
||||
==========
|
||||
|
||||
Install a package:
|
||||
|
||||
::
|
||||
|
||||
$ pip install SomePackage==1.0
|
||||
[...]
|
||||
Successfully installed SomePackage
|
||||
|
||||
Show what files were installed:
|
||||
|
||||
::
|
||||
|
||||
$ pip show --files SomePackage
|
||||
Name: SomePackage
|
||||
Version: 1.0
|
||||
Location: /my/env/lib/pythonx.x/site-packages
|
||||
Files:
|
||||
../somepackage/__init__.py
|
||||
[...]
|
||||
|
||||
List what packages are outdated:
|
||||
|
||||
::
|
||||
|
||||
$ pip list --outdated
|
||||
SomePackage (Current: 1.0 Latest: 2.0)
|
||||
|
||||
Upgrade a package:
|
||||
|
||||
::
|
||||
|
||||
$ pip install --upgrade SomePackage
|
||||
[...]
|
||||
Found existing installation: SomePackage 1.0
|
||||
Uninstalling SomePackage:
|
||||
Successfully uninstalled SomePackage
|
||||
Running setup.py install for SomePackage
|
||||
Successfully installed SomePackage
|
||||
|
||||
Uninstall a package:
|
||||
|
||||
::
|
||||
|
||||
$ pip uninstall SomePackage
|
||||
Uninstalling SomePackage:
|
||||
/my/env/lib/pythonx.x/site-packages/somepackage
|
||||
Proceed (y/n)? y
|
||||
Successfully uninstalled SomePackage
|
||||
|
||||
Vendored
+362
@@ -0,0 +1,362 @@
|
||||
==========
|
||||
Usage
|
||||
==========
|
||||
|
||||
.. _`General Options`:
|
||||
|
||||
**General Options:**
|
||||
|
||||
.. pip-general-options::
|
||||
|
||||
|
||||
.. _`Package Index Options`:
|
||||
|
||||
**Package Index Options:**
|
||||
|
||||
.. pip-index-options::
|
||||
|
||||
|
||||
.. _`pip install`:
|
||||
|
||||
pip install
|
||||
-----------
|
||||
|
||||
Usage
|
||||
********
|
||||
|
||||
.. pip-command-usage:: install
|
||||
|
||||
Description
|
||||
***********
|
||||
|
||||
.. pip-command-description:: install
|
||||
|
||||
Options
|
||||
*******
|
||||
|
||||
**Install Options:**
|
||||
|
||||
.. pip-command-options:: install
|
||||
|
||||
**Other Options:**
|
||||
|
||||
* :ref:`Package Index Options <Package Index Options>`
|
||||
* :ref:`General Options <General Options>`
|
||||
|
||||
|
||||
.. _`pip install Examples`:
|
||||
|
||||
Examples
|
||||
********
|
||||
|
||||
1) Install `SomePackage` and it's 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
|
||||
|
||||
|
||||
2) Install a list of requirements specified in a file. See the :ref:`Cookbook entry on Requirements files <Requirements Files>`.
|
||||
|
||||
::
|
||||
|
||||
$ pip install -r requirements.txt
|
||||
|
||||
|
||||
3) Upgrade an already installed `SomePackage` to the latest from PyPI.
|
||||
|
||||
::
|
||||
|
||||
$ pip install --upgrade SomePackage
|
||||
|
||||
|
||||
4) Install a local project in "editable" mode. See the section on :ref:`Editable Installs <editable-installs>`.
|
||||
|
||||
::
|
||||
|
||||
$ 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 <VCS Support>` and :ref:`Editable Installs <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
|
||||
|
||||
|
||||
6) 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
|
||||
|
||||
|
||||
7) 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
|
||||
|
||||
|
||||
8) Install from alternative package repositories.
|
||||
|
||||
Install from a different index, and not `PyPI`_::
|
||||
|
||||
$ pip install --index-url http://my.package.repo/simple/ SomePackage
|
||||
|
||||
Search an additional index during install, in addition to `PyPI`_::
|
||||
|
||||
$ pip install --extra-index-url http://my.package.repo/simple SomePackage
|
||||
|
||||
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
|
||||
|
||||
|
||||
.. _PyPI: http://pypi.python.org/pypi
|
||||
.. _setuptools extras: http://packages.python.org/distribute/setuptools.html#declaring-extras-optional-features-with-their-own-dependencies
|
||||
|
||||
|
||||
pip uninstall
|
||||
-------------
|
||||
|
||||
Usage
|
||||
*****
|
||||
|
||||
.. pip-command-usage:: uninstall
|
||||
|
||||
Description
|
||||
***********
|
||||
|
||||
.. pip-command-description:: uninstall
|
||||
|
||||
Options
|
||||
*******
|
||||
|
||||
**Uninstall Options:**
|
||||
|
||||
.. pip-command-options:: uninstall
|
||||
|
||||
|
||||
**Other Options:**
|
||||
|
||||
* :ref:`General Options <General Options>`
|
||||
|
||||
|
||||
Examples
|
||||
********
|
||||
|
||||
1) Uninstall a package.
|
||||
|
||||
::
|
||||
|
||||
$ pip uninstall simplejson
|
||||
Uninstalling simplejson:
|
||||
/home/me/env/lib/python2.7/site-packages/simplejson
|
||||
/home/me/env/lib/python2.7/site-packages/simplejson-2.2.1-py2.7.egg-info
|
||||
Proceed (y/n)? y
|
||||
Successfully uninstalled simplejson
|
||||
|
||||
|
||||
.. _`pip freeze`:
|
||||
|
||||
pip freeze
|
||||
-----------
|
||||
|
||||
Usage
|
||||
*****
|
||||
|
||||
.. pip-command-usage:: freeze
|
||||
|
||||
|
||||
Description
|
||||
***********
|
||||
|
||||
.. pip-command-description:: freeze
|
||||
|
||||
|
||||
Options
|
||||
*******
|
||||
|
||||
**Freeze Options:**
|
||||
|
||||
.. pip-command-options:: freeze
|
||||
|
||||
**Other Options:**
|
||||
|
||||
* :ref:`General Options <General Options>`
|
||||
|
||||
|
||||
Examples
|
||||
********
|
||||
|
||||
1) Generate output suitable for a requirements file.
|
||||
|
||||
::
|
||||
|
||||
$ pip freeze
|
||||
Jinja2==2.6
|
||||
Pygments==1.5
|
||||
Sphinx==1.1.3
|
||||
docutils==0.9.1
|
||||
|
||||
|
||||
pip list
|
||||
---------
|
||||
|
||||
Usage
|
||||
*****
|
||||
|
||||
.. pip-command-usage:: list
|
||||
|
||||
Description
|
||||
***********
|
||||
|
||||
.. pip-command-description:: list
|
||||
|
||||
Options
|
||||
*******
|
||||
|
||||
**List Options:**
|
||||
|
||||
.. pip-command-options:: list
|
||||
|
||||
**Other Options:**
|
||||
|
||||
* :ref:`Package Index Options <Package Index Options>`
|
||||
* :ref:`General Options <General Options>`
|
||||
|
||||
|
||||
Examples
|
||||
********
|
||||
|
||||
1) List installed packages.
|
||||
|
||||
::
|
||||
|
||||
$ pip list
|
||||
Pygments (1.5)
|
||||
docutils (0.9.1)
|
||||
Sphinx (1.1.2)
|
||||
Jinja2 (2.6)
|
||||
|
||||
2) 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 show
|
||||
--------
|
||||
|
||||
Usage
|
||||
*****
|
||||
|
||||
.. pip-command-usage:: show
|
||||
|
||||
Description
|
||||
***********
|
||||
|
||||
.. pip-command-description:: show
|
||||
|
||||
|
||||
Options
|
||||
*******
|
||||
|
||||
**Show Options:**
|
||||
|
||||
.. pip-command-options:: show
|
||||
|
||||
**Other Options:**
|
||||
|
||||
* :ref:`General Options <General Options>`
|
||||
|
||||
|
||||
Examples
|
||||
********
|
||||
|
||||
1. Show 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 search
|
||||
----------
|
||||
|
||||
Usage
|
||||
*****
|
||||
|
||||
.. pip-command-usage:: search
|
||||
|
||||
|
||||
Description
|
||||
***********
|
||||
|
||||
.. pip-command-description:: search
|
||||
|
||||
Options
|
||||
*******
|
||||
|
||||
**Seach Options:**
|
||||
|
||||
.. pip-command-options:: search
|
||||
|
||||
**Other Options:**
|
||||
|
||||
* :ref:`General Options <General Options>`
|
||||
|
||||
Examples
|
||||
********
|
||||
|
||||
1. Search for "peppercorn"
|
||||
|
||||
::
|
||||
|
||||
$ pip search peppercorn
|
||||
pepperedform - Helpers for using peppercorn with formprocess.
|
||||
peppercorn - A library for converting a token stream into [...]
|
||||
|
||||
|
||||
pip zip
|
||||
-------
|
||||
|
||||
Usage
|
||||
*****
|
||||
|
||||
.. pip-command-usage:: zip
|
||||
|
||||
Description
|
||||
***********
|
||||
|
||||
.. pip-command-description:: zip
|
||||
|
||||
Options
|
||||
*******
|
||||
|
||||
**Zip Options:**
|
||||
|
||||
.. pip-command-options:: zip
|
||||
|
||||
**Other Options:**
|
||||
|
||||
* :ref:`General Options <General Options>`
|
||||
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import optparse
|
||||
|
||||
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.vcs import git, mercurial, subversion, bazaar # noqa
|
||||
from pip.baseparser import create_main_parser
|
||||
from pip.commands import commands, get_similar_commands, get_summaries
|
||||
|
||||
|
||||
# The version as used in the setup.py and the docs conf.py
|
||||
__version__ = "1.3.1"
|
||||
|
||||
|
||||
def autocomplete():
|
||||
"""Command and option completion for the main option parser (and options)
|
||||
and its subcommands (and options).
|
||||
|
||||
Enable by sourcing one of the completion shell scripts (bash or zsh).
|
||||
"""
|
||||
# Don't complete if user hasn't sourced bash_completion file.
|
||||
if 'PIP_AUTO_COMPLETE' not in os.environ:
|
||||
return
|
||||
cwords = os.environ['COMP_WORDS'].split()[1:]
|
||||
cword = int(os.environ['COMP_CWORD'])
|
||||
try:
|
||||
current = cwords[cword - 1]
|
||||
except IndexError:
|
||||
current = ''
|
||||
|
||||
subcommands = [cmd for cmd, summary in get_summaries()]
|
||||
options = []
|
||||
# subcommand
|
||||
try:
|
||||
subcommand_name = [w for w in cwords if w in subcommands][0]
|
||||
except IndexError:
|
||||
subcommand_name = None
|
||||
|
||||
parser = create_main_parser()
|
||||
# subcommand options
|
||||
if subcommand_name:
|
||||
# special case: 'help' subcommand has no options
|
||||
if subcommand_name == 'help':
|
||||
sys.exit(1)
|
||||
# special case: list locally installed dists for uninstall command
|
||||
if subcommand_name == 'uninstall' and not current.startswith('-'):
|
||||
installed = []
|
||||
lc = current.lower()
|
||||
for dist in get_installed_distributions(local_only=True):
|
||||
if dist.key.startswith(lc) and dist.key not in cwords[1:]:
|
||||
installed.append(dist.key)
|
||||
# if there are no dists installed, fall back to option completion
|
||||
if installed:
|
||||
for dist in installed:
|
||||
print(dist)
|
||||
sys.exit(1)
|
||||
|
||||
subcommand = commands[subcommand_name](parser)
|
||||
options += [(opt.get_opt_string(), opt.nargs)
|
||||
for opt in subcommand.parser.option_list_all
|
||||
if opt.help != optparse.SUPPRESS_HELP]
|
||||
|
||||
# filter out previously specified options from available options
|
||||
prev_opts = [x.split('=')[0] for x in cwords[1:cword - 1]]
|
||||
options = [(x, v) for (x, v) in options if x not in prev_opts]
|
||||
# filter options by current input
|
||||
options = [(k, v) for k, v in options if k.startswith(current)]
|
||||
for option in options:
|
||||
opt_label = option[0]
|
||||
# append '=' to options which require args
|
||||
if option[1]:
|
||||
opt_label += '='
|
||||
print(opt_label)
|
||||
else:
|
||||
# show main parser options only when necessary
|
||||
if current.startswith('-') or current.startswith('--'):
|
||||
opts = [i.option_list for i in parser.option_groups]
|
||||
opts.append(parser.option_list)
|
||||
opts = (o for it in opts for o in it)
|
||||
|
||||
subcommands += [i.get_opt_string() for i in opts
|
||||
if i.help != optparse.SUPPRESS_HELP]
|
||||
|
||||
print(' '.join([x for x in subcommands if x.startswith(current)]))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def parseopts(args):
|
||||
parser = create_main_parser()
|
||||
parser.main = True # so the help formatter knows
|
||||
|
||||
# create command listing
|
||||
command_summaries = get_summaries()
|
||||
|
||||
description = [''] + ['%-27s %s' % (i, j) for i, j in command_summaries]
|
||||
parser.description = '\n'.join(description)
|
||||
|
||||
options, args = parser.parse_args(args)
|
||||
|
||||
if options.version:
|
||||
sys.stdout.write(parser.version)
|
||||
sys.stdout.write(os.linesep)
|
||||
sys.exit()
|
||||
|
||||
# pip || pip help || pip --help -> print_help()
|
||||
if not args or (args[0] == 'help' and len(args) == 1):
|
||||
parser.print_help()
|
||||
sys.exit()
|
||||
|
||||
if not args:
|
||||
msg = ('You must give a command '
|
||||
'(use "pip --help" to see a list of commands)')
|
||||
raise CommandError(msg)
|
||||
|
||||
command = args[0].lower()
|
||||
|
||||
if command not in commands:
|
||||
guess = get_similar_commands(command)
|
||||
|
||||
msg = ['unknown command "%s"' % command]
|
||||
if guess:
|
||||
msg.append('maybe you meant "%s"' % guess)
|
||||
|
||||
raise CommandError(' - '.join(msg))
|
||||
|
||||
return command, options, args, parser
|
||||
|
||||
|
||||
def main(initial_args=None):
|
||||
if initial_args is None:
|
||||
initial_args = sys.argv[1:]
|
||||
|
||||
autocomplete()
|
||||
|
||||
try:
|
||||
cmd_name, options, args, parser = parseopts(initial_args)
|
||||
except PipError:
|
||||
e = sys.exc_info()[1]
|
||||
sys.stderr.write("ERROR: %s" % e)
|
||||
sys.stderr.write(os.linesep)
|
||||
sys.exit(1)
|
||||
|
||||
command = commands[cmd_name](parser) # see baseparser.Command
|
||||
return command.main(args[1:], options)
|
||||
|
||||
|
||||
def bootstrap():
|
||||
"""
|
||||
Bootstrapping function to be called from install-pip.py script.
|
||||
"""
|
||||
return main(['install', '--upgrade', 'pip'])
|
||||
|
||||
############################################################
|
||||
## Writing freeze files
|
||||
|
||||
|
||||
class FrozenRequirement(object):
|
||||
|
||||
def __init__(self, name, req, editable, comments=()):
|
||||
self.name = name
|
||||
self.req = req
|
||||
self.editable = editable
|
||||
self.comments = comments
|
||||
|
||||
_rev_re = re.compile(r'-r(\d+)$')
|
||||
_date_re = re.compile(r'-(20\d\d\d\d\d\d)$')
|
||||
|
||||
@classmethod
|
||||
def from_dist(cls, dist, dependency_links, find_tags=False):
|
||||
location = os.path.normcase(os.path.abspath(dist.location))
|
||||
comments = []
|
||||
from pip.vcs import vcs, get_src_requirement
|
||||
if 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 = None
|
||||
if req is None:
|
||||
logger.warn('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] == '=='
|
||||
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)
|
||||
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:')
|
||||
else:
|
||||
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))
|
||||
return cls(dist.project_name, req, editable, comments)
|
||||
|
||||
@staticmethod
|
||||
def egg_name(dist):
|
||||
name = dist.egg_name()
|
||||
match = re.search(r'-py\d\.\d$', name)
|
||||
if match:
|
||||
name = name[:match.start()]
|
||||
return name
|
||||
|
||||
def __str__(self):
|
||||
req = self.req
|
||||
if self.editable:
|
||||
req = '-e %s' % req
|
||||
return '\n'.join(list(self.comments) + [str(req)]) + '\n'
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit = main()
|
||||
if exit:
|
||||
sys.exit(exit)
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
import sys
|
||||
from .runner import run
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit = run()
|
||||
if exit:
|
||||
sys.exit(exit)
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
"""Stuff that differs in different Python versions"""
|
||||
|
||||
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
|
||||
from urllib.request import urlretrieve
|
||||
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 fwrite(f, s):
|
||||
f.buffer.write(b(s))
|
||||
|
||||
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
|
||||
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 fwrite(f, s):
|
||||
f.write(s)
|
||||
|
||||
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 home_lib(home):
|
||||
"""Return the lib dir under the 'home' installation scheme"""
|
||||
if hasattr(sys, 'pypy_version_info'):
|
||||
lib = 'site-packages'
|
||||
else:
|
||||
lib = os.path.join('lib', 'python')
|
||||
return os.path.join(home, lib)
|
||||
|
||||
|
||||
## py25 has no builtin ssl module
|
||||
## only >=py32 has ssl.match_hostname and ssl.CertificateError
|
||||
try:
|
||||
import ssl
|
||||
try:
|
||||
from ssl import match_hostname, CertificateError
|
||||
except ImportError:
|
||||
from pip.backwardcompat.ssl_match_hostname import match_hostname, CertificateError
|
||||
except ImportError:
|
||||
ssl = None
|
||||
|
||||
|
||||
# patch for py25 socket to work with http://pypi.python.org/pypi/ssl/
|
||||
import socket
|
||||
if not hasattr(socket, 'create_connection'): # for Python 2.5
|
||||
# monkey-patch socket module
|
||||
from pip.backwardcompat.socket_create_connection import create_connection
|
||||
socket.create_connection = create_connection
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
"""
|
||||
patch for py25 socket to work with http://pypi.python.org/pypi/ssl/
|
||||
copy-paste from py2.6 stdlib socket.py
|
||||
https://gist.github.com/zed/1347055
|
||||
"""
|
||||
import socket
|
||||
import sys
|
||||
|
||||
_GLOBAL_DEFAULT_TIMEOUT = getattr(socket, '_GLOBAL_DEFAULT_TIMEOUT', object())
|
||||
def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
|
||||
source_address=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.
|
||||
"""
|
||||
|
||||
host, port = address
|
||||
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 timeout is not _GLOBAL_DEFAULT_TIMEOUT:
|
||||
sock.settimeout(timeout)
|
||||
if source_address:
|
||||
sock.bind(source_address)
|
||||
sock.connect(sa)
|
||||
return sock
|
||||
|
||||
except socket.error:
|
||||
err = sys.exc_info()[1]
|
||||
if sock is not None:
|
||||
sock.close()
|
||||
|
||||
if err is not None:
|
||||
raise err
|
||||
else:
|
||||
raise socket.error("getaddrinfo returns an empty list")
|
||||
@@ -0,0 +1,60 @@
|
||||
"""The match_hostname() function from Python 3.2, essential when using SSL."""
|
||||
|
||||
import re
|
||||
|
||||
__version__ = '3.2a3'
|
||||
|
||||
class CertificateError(ValueError):
|
||||
pass
|
||||
|
||||
def _dnsname_to_pat(dn):
|
||||
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)
|
||||
|
||||
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*.
|
||||
|
||||
CertificateError is raised on failure. On success, the function
|
||||
returns nothing.
|
||||
"""
|
||||
if not cert:
|
||||
raise ValueError("empty or no certificate")
|
||||
dnsnames = []
|
||||
san = cert.get('subjectAltName', ())
|
||||
for key, value in san:
|
||||
if key == 'DNS':
|
||||
if _dnsname_to_pat(value).match(hostname):
|
||||
return
|
||||
dnsnames.append(value)
|
||||
if not san:
|
||||
# The subject is only checked when subjectAltName is empty
|
||||
for sub in cert.get('subject', ()):
|
||||
for key, value in sub:
|
||||
# XXX according to RFC 2818, the most specific Common Name
|
||||
# must be used.
|
||||
if key == 'commonName':
|
||||
if _dnsname_to_pat(value).match(hostname):
|
||||
return
|
||||
dnsnames.append(value)
|
||||
if len(dnsnames) > 1:
|
||||
raise CertificateError("hostname %r "
|
||||
"doesn't match either of %s"
|
||||
% (hostname, ', '.join(map(repr, dnsnames))))
|
||||
elif len(dnsnames) == 1:
|
||||
raise CertificateError("hostname %r "
|
||||
"doesn't match %r"
|
||||
% (hostname, dnsnames[0]))
|
||||
else:
|
||||
raise CertificateError("no appropriate commonName or "
|
||||
"subjectAltName fields were found")
|
||||
Vendored
+212
@@ -0,0 +1,212 @@
|
||||
"""Base Command class, and related routines"""
|
||||
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
import tempfile
|
||||
import traceback
|
||||
import time
|
||||
import optparse
|
||||
|
||||
from pip.log import logger
|
||||
from pip.download import urlopen
|
||||
from pip.exceptions import (BadCommand, InstallationError, UninstallationError,
|
||||
CommandError)
|
||||
from pip.backwardcompat import StringIO, ssl
|
||||
from pip.baseparser import ConfigOptionParser, UpdatingDefaultsHelpFormatter
|
||||
from pip.status_codes import SUCCESS, ERROR, UNKNOWN_ERROR, VIRTUALENV_NOT_FOUND
|
||||
from pip.util import get_prog
|
||||
|
||||
|
||||
__all__ = ['Command']
|
||||
|
||||
|
||||
# for backwards compatibiliy
|
||||
get_proxy = urlopen.get_proxy
|
||||
|
||||
|
||||
class Command(object):
|
||||
name = None
|
||||
usage = None
|
||||
hidden = False
|
||||
|
||||
def __init__(self, main_parser):
|
||||
parser_kw = {
|
||||
'usage': self.usage,
|
||||
'prog': '%s %s' % (get_prog(), self.name),
|
||||
'formatter': UpdatingDefaultsHelpFormatter(),
|
||||
'add_help_option': False,
|
||||
'name': self.name,
|
||||
'description': self.__doc__,
|
||||
}
|
||||
self.main_parser = main_parser
|
||||
self.parser = ConfigOptionParser(**parser_kw)
|
||||
|
||||
# Commands should add options to this option group
|
||||
optgroup_name = '%s Options' % self.name.capitalize()
|
||||
self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name)
|
||||
|
||||
# Re-add all options and option groups.
|
||||
for group in main_parser.option_groups:
|
||||
self._copy_option_group(self.parser, group)
|
||||
|
||||
# Copies all general options from the main parser.
|
||||
self._copy_options(self.parser, main_parser.option_list)
|
||||
|
||||
def _copy_options(self, parser, options):
|
||||
"""Populate an option parser or group with options."""
|
||||
for option in options:
|
||||
if not option.dest:
|
||||
continue
|
||||
parser.add_option(option)
|
||||
|
||||
def _copy_option_group(self, parser, group):
|
||||
"""Copy option group (including options) to another parser."""
|
||||
new_group = optparse.OptionGroup(parser, group.title)
|
||||
self._copy_options(new_group, group.option_list)
|
||||
|
||||
parser.add_option_group(new_group)
|
||||
|
||||
def merge_options(self, initial_options, options):
|
||||
# Make sure we have all global options carried over
|
||||
attrs = ['log', 'proxy', 'require_venv',
|
||||
'log_explicit_levels', 'log_file',
|
||||
'timeout', 'default_vcs',
|
||||
'skip_requirements_regex',
|
||||
'no_input', 'exists_action',
|
||||
'cert']
|
||||
if not ssl:
|
||||
attrs.append('insecure')
|
||||
for attr in attrs:
|
||||
setattr(options, attr, getattr(initial_options, attr) or getattr(options, attr))
|
||||
options.quiet += initial_options.quiet
|
||||
options.verbose += initial_options.verbose
|
||||
|
||||
def setup_logging(self):
|
||||
pass
|
||||
|
||||
def main(self, args, initial_options):
|
||||
options, args = self.parser.parse_args(args)
|
||||
self.merge_options(initial_options, options)
|
||||
|
||||
level = 1 # Notify
|
||||
level += options.verbose
|
||||
level -= options.quiet
|
||||
level = logger.level_for_integer(4 - level)
|
||||
complete_log = []
|
||||
logger.consumers.extend(
|
||||
[(level, sys.stdout),
|
||||
(logger.DEBUG, complete_log.append)])
|
||||
if options.log_explicit_levels:
|
||||
logger.explicit_levels = True
|
||||
|
||||
self.setup_logging()
|
||||
|
||||
#TODO: try to get these passing down from the command?
|
||||
# without resorting to os.environ to hold these.
|
||||
|
||||
if options.no_input:
|
||||
os.environ['PIP_NO_INPUT'] = '1'
|
||||
|
||||
if options.exists_action:
|
||||
os.environ['PIP_EXISTS_ACTION'] = ''.join(options.exists_action)
|
||||
|
||||
if not ssl and options.insecure:
|
||||
os.environ['PIP_INSECURE'] = '1'
|
||||
|
||||
if options.cert:
|
||||
os.environ['PIP_CERT'] = options.cert
|
||||
|
||||
if options.require_venv:
|
||||
# If a venv is required check if it can really be found
|
||||
if not os.environ.get('VIRTUAL_ENV'):
|
||||
logger.fatal('Could not find an activated virtualenv (required).')
|
||||
sys.exit(VIRTUALENV_NOT_FOUND)
|
||||
|
||||
if options.log:
|
||||
log_fp = open_logfile(options.log, 'a')
|
||||
logger.consumers.append((logger.DEBUG, log_fp))
|
||||
else:
|
||||
log_fp = None
|
||||
|
||||
socket.setdefaulttimeout(options.timeout or None)
|
||||
|
||||
urlopen.setup(proxystr=options.proxy, prompting=not options.no_input)
|
||||
|
||||
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 (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
|
||||
except KeyboardInterrupt:
|
||||
logger.fatal('Operation cancelled by user')
|
||||
logger.info('Exception information:\n%s' % format_exc())
|
||||
store_log = True
|
||||
exit = ERROR
|
||||
except:
|
||||
logger.fatal('Exception:\n%s' % format_exc())
|
||||
store_log = True
|
||||
exit = UNKNOWN_ERROR
|
||||
if log_fp is not None:
|
||||
log_fp.close()
|
||||
if store_log:
|
||||
log_fn = options.log_file
|
||||
text = '\n'.join(complete_log)
|
||||
try:
|
||||
log_fp = open_logfile(log_fn, 'w')
|
||||
except IOError:
|
||||
temp = tempfile.NamedTemporaryFile(delete=False)
|
||||
log_fn = temp.name
|
||||
log_fp = open_logfile(log_fn, 'w')
|
||||
logger.fatal('Storing complete log in %s' % log_fn)
|
||||
log_fp.write(text)
|
||||
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
|
||||
Vendored
+376
@@ -0,0 +1,376 @@
|
||||
"""Base option parser setup"""
|
||||
|
||||
import sys
|
||||
import optparse
|
||||
import pkg_resources
|
||||
import os
|
||||
import textwrap
|
||||
from distutils.util import strtobool
|
||||
from pip.backwardcompat import ConfigParser, string_types, ssl
|
||||
from pip.locations import default_config_file, default_log_file
|
||||
from pip.util import get_terminal_size, get_prog
|
||||
|
||||
|
||||
class PrettyHelpFormatter(optparse.IndentedHelpFormatter):
|
||||
"""A prettier/less verbose help formatter for optparse."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# help position must be aligned with __init__.parseopts.description
|
||||
kwargs['max_help_position'] = 30
|
||||
kwargs['indent_increment'] = 1
|
||||
kwargs['width'] = get_terminal_size()[0] - 2
|
||||
optparse.IndentedHelpFormatter.__init__(self, *args, **kwargs)
|
||||
|
||||
def format_option_strings(self, option):
|
||||
return self._format_option_strings(option, ' <%s>', ', ')
|
||||
|
||||
def _format_option_strings(self, option, mvarfmt=' <%s>', optsep=', '):
|
||||
"""
|
||||
Return a comma-separated list of option strings and metavars.
|
||||
|
||||
:param option: tuple of (short opt, long opt), e.g: ('-f', '--format')
|
||||
:param mvarfmt: metavar format string - evaluated as mvarfmt % metavar
|
||||
:param optsep: separator
|
||||
"""
|
||||
opts = []
|
||||
|
||||
if option._short_opts:
|
||||
opts.append(option._short_opts[0])
|
||||
if option._long_opts:
|
||||
opts.append(option._long_opts[0])
|
||||
if len(opts) > 1:
|
||||
opts.insert(1, optsep)
|
||||
|
||||
if option.takes_value():
|
||||
metavar = option.metavar or option.dest.lower()
|
||||
opts.append(mvarfmt % metavar.lower())
|
||||
|
||||
return ''.join(opts)
|
||||
|
||||
def format_heading(self, heading):
|
||||
if heading == 'Options':
|
||||
return ''
|
||||
return heading + ':\n'
|
||||
|
||||
def format_usage(self, usage):
|
||||
"""
|
||||
Ensure there is only one newline between usage and the first heading
|
||||
if there is no description.
|
||||
"""
|
||||
msg = '\nUsage: %s\n' % self.indent_lines(textwrap.dedent(usage), " ")
|
||||
return msg
|
||||
|
||||
def format_description(self, description):
|
||||
# leave full control over description to us
|
||||
if description:
|
||||
if hasattr(self.parser, 'main'):
|
||||
label = 'Commands'
|
||||
else:
|
||||
label = 'Description'
|
||||
#some doc strings have inital newlines, some don't
|
||||
description = description.lstrip('\n')
|
||||
#some doc strings have final newlines and spaces, some don't
|
||||
description = description.rstrip()
|
||||
#dedent, then reindent
|
||||
description = self.indent_lines(textwrap.dedent(description), " ")
|
||||
description = '%s:\n%s\n' % (label, description)
|
||||
return description
|
||||
else:
|
||||
return ''
|
||||
|
||||
def format_epilog(self, epilog):
|
||||
# leave full control over epilog to us
|
||||
if epilog:
|
||||
return epilog
|
||||
else:
|
||||
return ''
|
||||
|
||||
def indent_lines(self, text, indent):
|
||||
new_lines = [indent + line for line in text.split('\n')]
|
||||
return "\n".join(new_lines)
|
||||
|
||||
|
||||
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"""
|
||||
|
||||
def expand_default(self, option):
|
||||
if self.parser is not None:
|
||||
self.parser.update_defaults(self.parser.defaults)
|
||||
return optparse.IndentedHelpFormatter.expand_default(self, option)
|
||||
|
||||
|
||||
class CustomOptionParser(optparse.OptionParser):
|
||||
def insert_option_group(self, idx, *args, **kwargs):
|
||||
"""Insert an OptionGroup at a given position."""
|
||||
group = self.add_option_group(*args, **kwargs)
|
||||
|
||||
self.option_groups.pop()
|
||||
self.option_groups.insert(idx, group)
|
||||
|
||||
return group
|
||||
|
||||
@property
|
||||
def option_list_all(self):
|
||||
"""Get a list of all options, including those in option groups."""
|
||||
res = self.option_list[:]
|
||||
for i in self.option_groups:
|
||||
res.extend(i.option_list)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
class ConfigOptionParser(CustomOptionParser):
|
||||
"""Custom option parser which updates its defaults by by checking the
|
||||
configuration files and environmental variables"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.config = ConfigParser.RawConfigParser()
|
||||
self.name = kwargs.pop('name')
|
||||
self.files = self.get_config_files()
|
||||
self.config.read(self.files)
|
||||
assert self.name
|
||||
optparse.OptionParser.__init__(self, *args, **kwargs)
|
||||
|
||||
def get_config_files(self):
|
||||
config_file = os.environ.get('PIP_CONFIG_FILE', False)
|
||||
if config_file and os.path.exists(config_file):
|
||||
return [config_file]
|
||||
return [default_config_file]
|
||||
|
||||
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)."""
|
||||
# Then go and look for the other sources of configuration:
|
||||
config = {}
|
||||
# 1. config files
|
||||
for section in ('global', self.name):
|
||||
config.update(self.normalize_keys(self.get_config_section(section)))
|
||||
# 2. environmental variables
|
||||
config.update(self.normalize_keys(self.get_environ_vars()))
|
||||
# Then set the options with those values
|
||||
for key, val in config.items():
|
||||
option = self.get_option(key)
|
||||
if option is not None:
|
||||
# ignore empty values
|
||||
if not val:
|
||||
continue
|
||||
# handle multiline configs
|
||||
if option.action == 'append':
|
||||
val = val.split()
|
||||
else:
|
||||
option.nargs = 1
|
||||
if option.action in ('store_true', 'store_false', 'count'):
|
||||
val = strtobool(val)
|
||||
try:
|
||||
val = option.convert_value(key, val)
|
||||
except optparse.OptionValueError:
|
||||
e = sys.exc_info()[1]
|
||||
print("An error occurred during configuration: %s" % e)
|
||||
sys.exit(3)
|
||||
defaults[option.dest] = val
|
||||
return defaults
|
||||
|
||||
def normalize_keys(self, items):
|
||||
"""Return a config dictionary with normalized keys regardless of
|
||||
whether the keys were specified in environment variables or in config
|
||||
files"""
|
||||
normalized = {}
|
||||
for key, val in items:
|
||||
key = key.replace('_', '-')
|
||||
if not key.startswith('--'):
|
||||
key = '--%s' % key # only prefer long opts
|
||||
normalized[key] = val
|
||||
return normalized
|
||||
|
||||
def get_config_section(self, name):
|
||||
"""Get a section of a configuration"""
|
||||
if self.config.has_section(name):
|
||||
return self.config.items(name)
|
||||
return []
|
||||
|
||||
def get_environ_vars(self, prefix='PIP_'):
|
||||
"""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)
|
||||
|
||||
def get_default_values(self):
|
||||
"""Overridding 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
|
||||
for option in self._get_all_options():
|
||||
default = defaults.get(option.dest)
|
||||
if isinstance(default, string_types):
|
||||
opt_str = option.get_opt_string()
|
||||
defaults[option.dest] = option.check_value(opt_str, default)
|
||||
return optparse.Values(defaults)
|
||||
|
||||
def error(self, msg):
|
||||
self.print_usage(sys.stderr)
|
||||
self.exit(2, "%s\n" % msg)
|
||||
|
||||
|
||||
try:
|
||||
pip_dist = pkg_resources.get_distribution('pip')
|
||||
version = '%s from %s (python %s)' % (
|
||||
pip_dist, pip_dist.location, sys.version[:3])
|
||||
except pkg_resources.DistributionNotFound:
|
||||
# when running pip.py without installing
|
||||
version = None
|
||||
|
||||
|
||||
def create_main_parser():
|
||||
parser_kw = {
|
||||
'usage': '\n%prog <command> [options]',
|
||||
'add_help_option': False,
|
||||
'formatter': UpdatingDefaultsHelpFormatter(),
|
||||
'name': 'global',
|
||||
'prog': get_prog(),
|
||||
}
|
||||
|
||||
parser = ConfigOptionParser(**parser_kw)
|
||||
genopt = optparse.OptionGroup(parser, 'General Options')
|
||||
parser.disable_interspersed_args()
|
||||
|
||||
# having a default version action just causes trouble
|
||||
parser.version = version
|
||||
|
||||
for opt in standard_options:
|
||||
genopt.add_option(opt)
|
||||
parser.add_option_group(genopt)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
standard_options = [
|
||||
optparse.make_option(
|
||||
'-h', '--help',
|
||||
dest='help',
|
||||
action='help',
|
||||
help='Show help.'),
|
||||
|
||||
optparse.make_option(
|
||||
# Run only if inside a virtualenv, bail if not.
|
||||
'--require-virtualenv', '--require-venv',
|
||||
dest='require_venv',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=optparse.SUPPRESS_HELP),
|
||||
|
||||
optparse.make_option(
|
||||
'-v', '--verbose',
|
||||
dest='verbose',
|
||||
action='count',
|
||||
default=0,
|
||||
help='Give more output. Option is additive, and can be used up to 3 times.'),
|
||||
|
||||
optparse.make_option(
|
||||
'-V', '--version',
|
||||
dest='version',
|
||||
action='store_true',
|
||||
help='Show version and exit.'),
|
||||
|
||||
optparse.make_option(
|
||||
'-q', '--quiet',
|
||||
dest='quiet',
|
||||
action='count',
|
||||
default=0,
|
||||
help='Give less output.'),
|
||||
|
||||
optparse.make_option(
|
||||
'--log',
|
||||
dest='log',
|
||||
metavar='file',
|
||||
help='Log file where a complete (maximum verbosity) record will be kept.'),
|
||||
|
||||
optparse.make_option(
|
||||
# Writes the log levels explicitely to the log'
|
||||
'--log-explicit-levels',
|
||||
dest='log_explicit_levels',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=optparse.SUPPRESS_HELP),
|
||||
|
||||
optparse.make_option(
|
||||
# The default log file
|
||||
'--local-log', '--log-file',
|
||||
dest='log_file',
|
||||
metavar='file',
|
||||
default=default_log_file,
|
||||
help=optparse.SUPPRESS_HELP),
|
||||
|
||||
optparse.make_option(
|
||||
# Don't ask for input
|
||||
'--no-input',
|
||||
dest='no_input',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=optparse.SUPPRESS_HELP),
|
||||
|
||||
optparse.make_option(
|
||||
'--proxy',
|
||||
dest='proxy',
|
||||
type='str',
|
||||
default='',
|
||||
help="Specify a proxy in the form [user:passwd@]proxy.server:port."),
|
||||
|
||||
optparse.make_option(
|
||||
'--timeout', '--default-timeout',
|
||||
metavar='sec',
|
||||
dest='timeout',
|
||||
type='float',
|
||||
default=15,
|
||||
help='Set the socket timeout (default %default seconds).'),
|
||||
|
||||
optparse.make_option(
|
||||
# The default version control system for editables, e.g. 'svn'
|
||||
'--default-vcs',
|
||||
dest='default_vcs',
|
||||
type='str',
|
||||
default='',
|
||||
help=optparse.SUPPRESS_HELP),
|
||||
|
||||
optparse.make_option(
|
||||
# A regex to be used to skip requirements
|
||||
'--skip-requirements-regex',
|
||||
dest='skip_requirements_regex',
|
||||
type='str',
|
||||
default='',
|
||||
help=optparse.SUPPRESS_HELP),
|
||||
|
||||
optparse.make_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."),
|
||||
|
||||
optparse.make_option(
|
||||
'--cert',
|
||||
dest='cert',
|
||||
type='str',
|
||||
default='',
|
||||
metavar='path',
|
||||
help = "Path to alternate CA bundle."),
|
||||
|
||||
]
|
||||
|
||||
if not ssl:
|
||||
standard_options.append(optparse.make_option(
|
||||
'--insecure',
|
||||
dest='insecure',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help = "Allow lack of certificate checking when ssl is not installed."))
|
||||
Vendored
+3895
File diff suppressed because it is too large
Load Diff
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
"""shared options and groups"""
|
||||
from optparse import make_option, OptionGroup
|
||||
|
||||
|
||||
def make_option_group(group, parser):
|
||||
"""
|
||||
Return an OptionGroup object
|
||||
group -- assumed to be dict with 'name' and 'options' keys
|
||||
parser -- an optparse Parser
|
||||
"""
|
||||
option_group = OptionGroup(parser, group['name'])
|
||||
for option in group['options']:
|
||||
option_group.add_option(option)
|
||||
return option_group
|
||||
|
||||
###########
|
||||
# options #
|
||||
###########
|
||||
|
||||
index_url = make_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 = make_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.')
|
||||
|
||||
no_index = make_option(
|
||||
'--no-index',
|
||||
dest='no_index',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Ignore package index (only looking at --find-links URLs instead).')
|
||||
|
||||
find_links = make_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.")
|
||||
|
||||
use_mirrors = make_option(
|
||||
'-M', '--use-mirrors',
|
||||
dest='use_mirrors',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Use the PyPI mirrors as a fallback in case the main index is down.')
|
||||
|
||||
mirrors = make_option(
|
||||
'--mirrors',
|
||||
dest='mirrors',
|
||||
metavar='URL',
|
||||
action='append',
|
||||
default=[],
|
||||
help='Specific mirror URLs to query when --use-mirrors is used.')
|
||||
|
||||
|
||||
##########
|
||||
# groups #
|
||||
##########
|
||||
|
||||
index_group = {
|
||||
'name': 'Package Index Options',
|
||||
'options': [
|
||||
index_url,
|
||||
extra_index_url,
|
||||
no_index,
|
||||
find_links,
|
||||
use_mirrors,
|
||||
mirrors
|
||||
]
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
"""
|
||||
Package containing all pip commands
|
||||
"""
|
||||
|
||||
|
||||
from pip.commands.bundle import BundleCommand
|
||||
from pip.commands.completion import CompletionCommand
|
||||
from pip.commands.freeze import FreezeCommand
|
||||
from pip.commands.help import HelpCommand
|
||||
from pip.commands.list import ListCommand
|
||||
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
|
||||
|
||||
|
||||
commands = {
|
||||
BundleCommand.name: BundleCommand,
|
||||
CompletionCommand.name: CompletionCommand,
|
||||
FreezeCommand.name: FreezeCommand,
|
||||
HelpCommand.name: HelpCommand,
|
||||
SearchCommand.name: SearchCommand,
|
||||
ShowCommand.name: ShowCommand,
|
||||
InstallCommand.name: InstallCommand,
|
||||
UninstallCommand.name: UninstallCommand,
|
||||
UnzipCommand.name: UnzipCommand,
|
||||
ZipCommand.name: ZipCommand,
|
||||
ListCommand.name: ListCommand,
|
||||
}
|
||||
|
||||
|
||||
commands_order = [
|
||||
InstallCommand,
|
||||
UninstallCommand,
|
||||
FreezeCommand,
|
||||
ListCommand,
|
||||
ShowCommand,
|
||||
SearchCommand,
|
||||
ZipCommand,
|
||||
UnzipCommand,
|
||||
BundleCommand,
|
||||
HelpCommand,
|
||||
]
|
||||
|
||||
|
||||
def get_summaries(ignore_hidden=True, ordered=True):
|
||||
"""Yields sorted (command name, command summary) tuples."""
|
||||
|
||||
if ordered:
|
||||
cmditems = _sort_commands(commands, commands_order)
|
||||
else:
|
||||
cmditems = commands.items()
|
||||
|
||||
for name, command_class in cmditems:
|
||||
if ignore_hidden and command_class.hidden:
|
||||
continue
|
||||
|
||||
yield (name, command_class.summary)
|
||||
|
||||
|
||||
def get_similar_commands(name):
|
||||
"""Command name auto-correct."""
|
||||
from difflib import get_close_matches
|
||||
|
||||
close_commands = get_close_matches(name, commands.keys())
|
||||
|
||||
if close_commands:
|
||||
guess = close_commands[0]
|
||||
else:
|
||||
guess = False
|
||||
|
||||
return guess
|
||||
|
||||
|
||||
def _sort_commands(cmddict, order):
|
||||
def keyfn(key):
|
||||
try:
|
||||
return order.index(key[1])
|
||||
except ValueError:
|
||||
# unordered items should come last
|
||||
return 0xff
|
||||
|
||||
return sorted(cmddict.items(), key=keyfn)
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
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] <bundle name>.pybundle <package>..."""
|
||||
summary = '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):
|
||||
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
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
import sys
|
||||
from pip.basecommand import Command
|
||||
|
||||
BASE_COMPLETION = """
|
||||
# pip %(shell)s completion start%(script)s# pip %(shell)s completion end
|
||||
"""
|
||||
|
||||
COMPLETION_SCRIPTS = {
|
||||
'bash': """
|
||||
_pip_completion()
|
||||
{
|
||||
COMPREPLY=( $( COMP_WORDS="${COMP_WORDS[*]}" \\
|
||||
COMP_CWORD=$COMP_CWORD \\
|
||||
PIP_AUTO_COMPLETE=1 $1 ) )
|
||||
}
|
||||
complete -o default -F _pip_completion pip
|
||||
""", 'zsh': """
|
||||
function _pip_completion {
|
||||
local words cword
|
||||
read -Ac words
|
||||
read -cn cword
|
||||
reply=( $( COMP_WORDS="$words[*]" \\
|
||||
COMP_CWORD=$(( cword-1 )) \\
|
||||
PIP_AUTO_COMPLETE=1 $words[1] ) )
|
||||
}
|
||||
compctl -K _pip_completion pip
|
||||
"""}
|
||||
|
||||
|
||||
class CompletionCommand(Command):
|
||||
"""A helper command to be used for command completion."""
|
||||
name = 'completion'
|
||||
summary = 'A helper command to be used for command completion'
|
||||
hidden = True
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
super(CompletionCommand, self).__init__(*args, **kw)
|
||||
self.parser.add_option(
|
||||
'--bash', '-b',
|
||||
action='store_const',
|
||||
const='bash',
|
||||
dest='shell',
|
||||
help='Emit completion code for bash')
|
||||
self.parser.add_option(
|
||||
'--zsh', '-z',
|
||||
action='store_const',
|
||||
const='zsh',
|
||||
dest='shell',
|
||||
help='Emit completion code for zsh')
|
||||
|
||||
def run(self, options, args):
|
||||
"""Prints the completion code of the given shell"""
|
||||
shells = COMPLETION_SCRIPTS.keys()
|
||||
shell_options = ['--' + shell for shell in sorted(shells)]
|
||||
if options.shell in shells:
|
||||
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))
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
import re
|
||||
import sys
|
||||
import pkg_resources
|
||||
import pip
|
||||
from pip.req import InstallRequirement
|
||||
from pip.log import logger
|
||||
from pip.basecommand import Command
|
||||
from pip.util import get_installed_distributions
|
||||
|
||||
|
||||
class FreezeCommand(Command):
|
||||
"""Output installed packages in requirements format."""
|
||||
name = 'freeze'
|
||||
usage = """
|
||||
%prog [options]"""
|
||||
summary = 'Output installed packages in requirements format.'
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
super(FreezeCommand, self).__init__(*args, **kw)
|
||||
|
||||
self.cmd_opts.add_option(
|
||||
'-r', '--requirement',
|
||||
dest='requirement',
|
||||
action='store',
|
||||
default=None,
|
||||
metavar='file',
|
||||
help="Use the order in the given requirements file and it's comments when generating output.")
|
||||
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.')
|
||||
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.')
|
||||
|
||||
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 = []
|
||||
|
||||
f = sys.stdout
|
||||
|
||||
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))
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
from pip.basecommand import Command, SUCCESS
|
||||
from pip.exceptions import CommandError
|
||||
|
||||
|
||||
class HelpCommand(Command):
|
||||
"""Show help for commands"""
|
||||
name = 'help'
|
||||
usage = """
|
||||
%prog <command>"""
|
||||
summary = 'Show help for commands.'
|
||||
|
||||
def run(self, options, args):
|
||||
from pip.commands import commands, get_similar_commands
|
||||
|
||||
try:
|
||||
# 'pip help' with no args is handled by pip.__init__.parseopt()
|
||||
cmd_name = args[0] # the command we need help for
|
||||
except IndexError:
|
||||
return SUCCESS
|
||||
|
||||
if cmd_name not in commands:
|
||||
guess = get_similar_commands(cmd_name)
|
||||
|
||||
msg = ['unknown command "%s"' % cmd_name]
|
||||
if guess:
|
||||
msg.append('maybe you meant "%s"' % guess)
|
||||
|
||||
raise CommandError(' - '.join(msg))
|
||||
|
||||
command = commands[cmd_name](self.main_parser) # instantiate
|
||||
command.parser.print_help()
|
||||
|
||||
return SUCCESS
|
||||
+297
@@ -0,0 +1,297 @@
|
||||
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 build_prefix, src_prefix, virtualenv_no_global
|
||||
from pip.basecommand import Command
|
||||
from pip.index import PackageFinder
|
||||
from pip.exceptions import InstallationError, CommandError
|
||||
from pip.backwardcompat import home_lib
|
||||
from pip.cmdoptions import make_option_group, index_group
|
||||
|
||||
|
||||
class InstallCommand(Command):
|
||||
"""
|
||||
Install packages from:
|
||||
|
||||
- PyPI (and other indexes) using requirement specifiers.
|
||||
- VCS project urls.
|
||||
- Local project directories.
|
||||
- Local or remote source archives.
|
||||
|
||||
pip also supports installing from "requirements files", which provide
|
||||
an easy way to specify a whole environment to be installed.
|
||||
|
||||
See http://www.pip-installer.org for details on VCS url formats and
|
||||
requirements files.
|
||||
"""
|
||||
name = 'install'
|
||||
|
||||
usage = """
|
||||
%prog [options] <requirement specifier> ...
|
||||
%prog [options] -r <requirements file> ...
|
||||
%prog [options] [-e] <vcs project url> ...
|
||||
%prog [options] [-e] <local project path> ...
|
||||
%prog [options] <archive url/path> ..."""
|
||||
|
||||
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(
|
||||
'-r', '--requirement',
|
||||
dest='requirements',
|
||||
action='append',
|
||||
default=[],
|
||||
metavar='file',
|
||||
help='Install from the given requirements file. '
|
||||
'This option can be used multiple times.')
|
||||
|
||||
cmd_opts.add_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 "<venv path>/build". '
|
||||
'The default for global installs is "<OS temp dir>/pip-build-<username>".')
|
||||
|
||||
cmd_opts.add_option(
|
||||
'-t', '--target',
|
||||
dest='target_dir',
|
||||
metavar='dir',
|
||||
default=None,
|
||||
help='Install packages into <dir>.')
|
||||
|
||||
cmd_opts.add_option(
|
||||
'-d', '--download', '--download-dir', '--download-directory',
|
||||
dest='download_dir',
|
||||
metavar='dir',
|
||||
default=None,
|
||||
help="Download packages into <dir> instead of installing them, irregardless of what's already installed.")
|
||||
|
||||
cmd_opts.add_option(
|
||||
'--download-cache',
|
||||
dest='download_cache',
|
||||
metavar='dir',
|
||||
default=None,
|
||||
help='Cache downloaded packages in <dir>.')
|
||||
|
||||
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 "<venv path>/src". '
|
||||
'The default for global installs is "<current dir>/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 irregardless of whether a dependency is already satisfied.')
|
||||
|
||||
cmd_opts.add_option(
|
||||
'--force-reinstall',
|
||||
dest='force_reinstall',
|
||||
action='store_true',
|
||||
help='When upgrading, reinstall all packages even if they are '
|
||||
'already up-to-date.')
|
||||
|
||||
cmd_opts.add_option(
|
||||
'-I', '--ignore-installed',
|
||||
dest='ignore_installed',
|
||||
action='store_true',
|
||||
help='Ignore the installed packages (reinstalling instead).')
|
||||
|
||||
cmd_opts.add_option(
|
||||
'--no-deps', '--no-dependencies',
|
||||
dest='ignore_dependencies',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help="Don't install package dependencies.")
|
||||
|
||||
cmd_opts.add_option(
|
||||
'--no-install',
|
||||
dest='no_install',
|
||||
action='store_true',
|
||||
help="Download and unpack all packages, but don't actually install them.")
|
||||
|
||||
cmd_opts.add_option(
|
||||
'--no-download',
|
||||
dest='no_download',
|
||||
action="store_true",
|
||||
help="Don't download any packages, just install the ones already downloaded "
|
||||
"(completes an install run with --no-install).")
|
||||
|
||||
cmd_opts.add_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.")
|
||||
|
||||
cmd_opts.add_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.")
|
||||
|
||||
cmd_opts.add_option(
|
||||
'--user',
|
||||
dest='use_user_site',
|
||||
action='store_true',
|
||||
help='Install using the user scheme.')
|
||||
|
||||
cmd_opts.add_option(
|
||||
'--egg',
|
||||
dest='as_egg',
|
||||
action='store_true',
|
||||
help="Install as self contained egg file, like easy_install does.")
|
||||
|
||||
cmd_opts.add_option(
|
||||
'--root',
|
||||
dest='root_path',
|
||||
metavar='dir',
|
||||
default=None,
|
||||
help="Install everything relative to this alternate root directory.")
|
||||
|
||||
index_opts = make_option_group(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):
|
||||
"""
|
||||
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_mirrors=options.use_mirrors,
|
||||
mirrors=options.mirrors)
|
||||
|
||||
def run(self, options, args):
|
||||
if options.download_dir:
|
||||
options.no_install = True
|
||||
options.ignore_installed = True
|
||||
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 virtualenv_no_global():
|
||||
raise InstallationError("Can not perform a '--user' install. User site-packages are not visible in this virtualenv.")
|
||||
install_options.append('--user')
|
||||
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.")
|
||||
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 = []
|
||||
|
||||
finder = self._build_package_finder(options, index_urls)
|
||||
|
||||
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)
|
||||
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):
|
||||
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
|
||||
|
||||
if (options.use_user_site and
|
||||
sys.version_info < (2, 6)):
|
||||
raise InstallationError('--user is only supported in Python version 2.6 and newer')
|
||||
|
||||
import setuptools
|
||||
if (options.use_user_site and
|
||||
requirement_set.has_editables and
|
||||
not getattr(setuptools, '_distribute', False)):
|
||||
|
||||
raise InstallationError('--user --editable not supported with setuptools, use distribute')
|
||||
|
||||
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)
|
||||
# Clean up
|
||||
if not options.no_install or options.download_dir:
|
||||
requirement_set.cleanup_files(bundle=self.bundle)
|
||||
if options.target_dir:
|
||||
if not os.path.exists(options.target_dir):
|
||||
os.makedirs(options.target_dir)
|
||||
lib_dir = home_lib(temp_target_dir)
|
||||
for item in os.listdir(lib_dir):
|
||||
shutil.move(
|
||||
os.path.join(lib_dir, item),
|
||||
os.path.join(options.target_dir, item)
|
||||
)
|
||||
shutil.rmtree(temp_target_dir)
|
||||
return requirement_set
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
from pip.basecommand import Command
|
||||
from pip.exceptions import DistributionNotFound, BestVersionAlreadyInstalled
|
||||
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.cmdoptions import make_option_group, index_group
|
||||
|
||||
|
||||
class ListCommand(Command):
|
||||
"""List installed packages, including editables."""
|
||||
name = 'list'
|
||||
usage = """
|
||||
%prog [options]"""
|
||||
summary = 'List installed packages.'
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
super(ListCommand, self).__init__(*args, **kw)
|
||||
|
||||
cmd_opts = self.cmd_opts
|
||||
|
||||
cmd_opts.add_option(
|
||||
'-o', '--outdated',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='List outdated packages (excluding editables)')
|
||||
cmd_opts.add_option(
|
||||
'-u', '--uptodate',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='List uptodate packages (excluding editables)')
|
||||
cmd_opts.add_option(
|
||||
'-e', '--editable',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='List editable projects.')
|
||||
cmd_opts.add_option(
|
||||
'-l', '--local',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='If in a virtualenv that has global access, do not list globally-installed packages.')
|
||||
|
||||
index_opts = make_option_group(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):
|
||||
"""
|
||||
Create a package finder appropriate to this list command.
|
||||
"""
|
||||
return PackageFinder(find_links=options.find_links,
|
||||
index_urls=index_urls,
|
||||
use_mirrors=options.use_mirrors,
|
||||
mirrors=options.mirrors)
|
||||
|
||||
def run(self, options, args):
|
||||
if options.outdated:
|
||||
self.run_outdated(options)
|
||||
elif options.uptodate:
|
||||
self.run_uptodate(options)
|
||||
elif options.editable:
|
||||
self.run_editables(options)
|
||||
else:
|
||||
self.run_listing(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 find_packages_latests_versions(self, options):
|
||||
index_urls = [options.index_url] + options.extra_index_urls
|
||||
if options.no_index:
|
||||
logger.notify('Ignoring indexes: %s' % ','.join(index_urls))
|
||||
index_urls = []
|
||||
|
||||
dependency_links = []
|
||||
for dist in get_installed_distributions(local_only=options.local):
|
||||
if dist.has_metadata('dependency_links.txt'):
|
||||
dependency_links.extend(
|
||||
dist.get_metadata_lines('dependency_links.txt'),
|
||||
)
|
||||
|
||||
finder = self._build_package_finder(options, index_urls)
|
||||
finder.add_dependency_links(dependency_links)
|
||||
|
||||
installed_packages = get_installed_distributions(local_only=options.local, include_editables=False)
|
||||
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:
|
||||
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)
|
||||
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)
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
import sys
|
||||
import textwrap
|
||||
import pkg_resources
|
||||
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.exceptions import CommandError
|
||||
from pip.status_codes import NO_MATCHES_FOUND
|
||||
from distutils.version import StrictVersion, LooseVersion
|
||||
|
||||
|
||||
class SearchCommand(Command):
|
||||
"""Search for PyPI packages whose name or summary contains <query>."""
|
||||
name = 'search'
|
||||
usage = """
|
||||
%prog [options] <query>"""
|
||||
summary = 'Search PyPI for packages.'
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
super(SearchCommand, self).__init__(*args, **kw)
|
||||
self.cmd_opts.add_option(
|
||||
'--index',
|
||||
dest='index',
|
||||
metavar='URL',
|
||||
default='https://pypi.python.org/pypi',
|
||||
help='Base URL of Python Package Index (default %default)')
|
||||
|
||||
self.parser.insert_option_group(0, self.cmd_opts)
|
||||
|
||||
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)
|
||||
hits = transform_hits(pypi_hits)
|
||||
|
||||
terminal_width = None
|
||||
if sys.stdout.isatty():
|
||||
terminal_width = get_terminal_size()[0]
|
||||
|
||||
print_results(hits, terminal_width=terminal_width)
|
||||
if pypi_hits:
|
||||
return SUCCESS
|
||||
return NO_MATCHES_FOUND
|
||||
|
||||
def search(self, query, index_url):
|
||||
pypi = xmlrpclib.ServerProxy(index_url, pip.download.xmlrpclib_transport)
|
||||
hits = pypi.search({'name': query, 'summary': query}, 'or')
|
||||
return hits
|
||||
|
||||
|
||||
def transform_hits(hits):
|
||||
"""
|
||||
The list from pypi is really a list of versions. We want a list of
|
||||
packages with the list of versions stored inline. This converts the
|
||||
list from pypi into one we can use.
|
||||
"""
|
||||
packages = {}
|
||||
for hit in hits:
|
||||
name = hit['name']
|
||||
summary = hit['summary']
|
||||
version = hit['version']
|
||||
score = hit['_pypi_ordering']
|
||||
if score is None:
|
||||
score = 0
|
||||
|
||||
if name not in packages.keys():
|
||||
packages[name] = {'name': name, 'summary': summary, 'versions': [version], 'score': score}
|
||||
else:
|
||||
packages[name]['versions'].append(version)
|
||||
|
||||
# if this is the highest version, replace summary and score
|
||||
if version == highest_version(packages[name]['versions']):
|
||||
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)
|
||||
return package_list
|
||||
|
||||
|
||||
def print_results(hits, name_column_width=25, terminal_width=None):
|
||||
installed_packages = [p.project_name for p in pkg_resources.working_set]
|
||||
for hit in hits:
|
||||
name = hit['name']
|
||||
summary = hit['summary'] or ''
|
||||
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)
|
||||
try:
|
||||
logger.notify(line)
|
||||
if name in installed_packages:
|
||||
dist = pkg_resources.get_distribution(name)
|
||||
logger.indent += 2
|
||||
try:
|
||||
latest = highest_version(hit['versions'])
|
||||
if dist.version == latest:
|
||||
logger.notify('INSTALLED: %s (latest)' % dist.version)
|
||||
else:
|
||||
logger.notify('INSTALLED: %s' % dist.version)
|
||||
logger.notify('LATEST: %s' % latest)
|
||||
finally:
|
||||
logger.indent -= 2
|
||||
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)
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
import os
|
||||
import pkg_resources
|
||||
from pip.basecommand import Command
|
||||
from pip.log import logger
|
||||
|
||||
|
||||
class ShowCommand(Command):
|
||||
"""Show information about one or more installed packages."""
|
||||
name = 'show'
|
||||
usage = """
|
||||
%prog [options] <package> ..."""
|
||||
summary = 'Show information about installed packages.'
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
super(ShowCommand, self).__init__(*args, **kw)
|
||||
self.cmd_opts.add_option(
|
||||
'-f', '--files',
|
||||
dest='files',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Show the full list of installed files for each package.')
|
||||
|
||||
self.parser.insert_option_group(0, self.cmd_opts)
|
||||
|
||||
def run(self, options, args):
|
||||
if not args:
|
||||
logger.warn('ERROR: Please provide a package name or names.')
|
||||
return
|
||||
query = args
|
||||
|
||||
results = search_packages_info(query)
|
||||
print_results(results, options.files)
|
||||
|
||||
|
||||
def search_packages_info(query):
|
||||
"""
|
||||
Gather details from installed distributions. Print distribution name,
|
||||
version, location, and installed files. Installed files requires a
|
||||
pip generated 'installed-files.txt' in the distributions '.egg-info'
|
||||
directory.
|
||||
"""
|
||||
installed_packages = 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):
|
||||
"""
|
||||
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")
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
from pip.req import InstallRequirement, RequirementSet, parse_requirements
|
||||
from pip.basecommand import Command
|
||||
from pip.exceptions import InstallationError
|
||||
|
||||
|
||||
class UninstallCommand(Command):
|
||||
"""
|
||||
Uninstall packages.
|
||||
|
||||
pip is able to uninstall most installed packages. Known exceptions are:
|
||||
|
||||
- Pure distutils packages installed with ``python setup.py install``, which
|
||||
leave behind no metadata to determine what files were installed.
|
||||
- Script wrappers installed by ``python setup.py develop``.
|
||||
"""
|
||||
name = 'uninstall'
|
||||
usage = """
|
||||
%prog [options] <package> ...
|
||||
%prog [options] -r <requirements file> ..."""
|
||||
summary = 'Uninstall packages.'
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
super(UninstallCommand, self).__init__(*args, **kw)
|
||||
self.cmd_opts.add_option(
|
||||
'-r', '--requirement',
|
||||
dest='requirements',
|
||||
action='append',
|
||||
default=[],
|
||||
metavar='file',
|
||||
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',
|
||||
action='store_true',
|
||||
help="Don't ask for confirmation of uninstall deletions.")
|
||||
|
||||
self.parser.insert_option_group(0, self.cmd_opts)
|
||||
|
||||
def run(self, options, args):
|
||||
requirement_set = RequirementSet(
|
||||
build_dir=None,
|
||||
src_dir=None,
|
||||
download_dir=None)
|
||||
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):
|
||||
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)
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
from pip.commands.zip import ZipCommand
|
||||
|
||||
|
||||
class UnzipCommand(ZipCommand):
|
||||
"""Unzip individual packages."""
|
||||
name = 'unzip'
|
||||
summary = 'Unzip individual packages.'
|
||||
Vendored
+347
@@ -0,0 +1,347 @@
|
||||
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] <package> ..."""
|
||||
summary = '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):
|
||||
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 name in zip.namelist():
|
||||
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
|
||||
Vendored
+584
@@ -0,0 +1,584 @@
|
||||
import cgi
|
||||
import getpass
|
||||
import hashlib
|
||||
import mimetypes
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import socket
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from pip.backwardcompat import (xmlrpclib, urllib, urllib2, httplib,
|
||||
urlparse, string_types, ssl)
|
||||
if ssl:
|
||||
from pip.backwardcompat import match_hostname, CertificateError
|
||||
from pip.exceptions import InstallationError, PipError, NoSSLError
|
||||
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.vcs import vcs
|
||||
from pip.log import logger
|
||||
from pip.locations import default_cert_path
|
||||
|
||||
__all__ = ['xmlrpclib_transport', 'get_file_content', 'urlopen',
|
||||
'is_url', 'url_to_path', 'path_to_url', 'path_to_url2',
|
||||
'geturl', 'is_archive_file', 'unpack_vcs_link',
|
||||
'unpack_file_url', 'is_vcs_url', 'is_file_url', 'unpack_http_url']
|
||||
|
||||
|
||||
xmlrpclib_transport = xmlrpclib.Transport()
|
||||
|
||||
|
||||
def get_file_content(url, comes_from=None):
|
||||
"""Gets the content of a file; it may be a filename, file: URL, or
|
||||
http: URL. Returns (location, content)"""
|
||||
match = _scheme_re.search(url)
|
||||
if match:
|
||||
scheme = match.group(1).lower()
|
||||
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))
|
||||
if scheme == 'file':
|
||||
path = url.split(':', 1)[1]
|
||||
path = path.replace('\\', '/')
|
||||
match = _url_slash_drive_re.match(path)
|
||||
if match:
|
||||
path = match.group(1) + ':' + path.split('|', 1)[1]
|
||||
path = urllib.unquote(path)
|
||||
if path.startswith('/'):
|
||||
path = '/' + path.lstrip('/')
|
||||
url = path
|
||||
else:
|
||||
## FIXME: catch some errors
|
||||
resp = urlopen(url)
|
||||
return geturl(resp), resp.read()
|
||||
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()
|
||||
return url, content
|
||||
|
||||
|
||||
_scheme_re = re.compile(r'^(http|https|file):', re.I)
|
||||
_url_slash_drive_re = re.compile(r'/*([a-z])\|', re.I)
|
||||
|
||||
class VerifiedHTTPSConnection(httplib.HTTPSConnection):
|
||||
"""
|
||||
A connection that wraps connections with ssl certificate verification.
|
||||
"""
|
||||
def connect(self):
|
||||
|
||||
self.connection_kwargs = {}
|
||||
|
||||
#TODO: refactor compatibility logic into backwardcompat?
|
||||
|
||||
# for > py2.5
|
||||
if hasattr(self, 'timeout'):
|
||||
self.connection_kwargs.update(timeout = self.timeout)
|
||||
|
||||
# for >= py2.7
|
||||
if hasattr(self, 'source_address'):
|
||||
self.connection_kwargs.update(source_address = self.source_address)
|
||||
|
||||
sock = socket.create_connection((self.host, self.port), **self.connection_kwargs)
|
||||
|
||||
# for >= py2.7
|
||||
if getattr(self, '_tunnel_host', None):
|
||||
self.sock = sock
|
||||
self._tunnel()
|
||||
|
||||
# get alternate bundle or use our included bundle
|
||||
cert_path = os.environ.get('PIP_CERT', '') or default_cert_path
|
||||
|
||||
self.sock = ssl.wrap_socket(sock,
|
||||
self.key_file,
|
||||
self.cert_file,
|
||||
cert_reqs=ssl.CERT_REQUIRED,
|
||||
ca_certs=cert_path)
|
||||
|
||||
try:
|
||||
match_hostname(self.sock.getpeercert(), self.host)
|
||||
except CertificateError:
|
||||
self.sock.shutdown(socket.SHUT_RDWR)
|
||||
self.sock.close()
|
||||
raise
|
||||
|
||||
|
||||
|
||||
class VerifiedHTTPSHandler(urllib2.HTTPSHandler):
|
||||
"""
|
||||
A HTTPSHandler that uses our own VerifiedHTTPSConnection.
|
||||
"""
|
||||
def __init__(self, connection_class = VerifiedHTTPSConnection):
|
||||
self.specialized_conn_class = connection_class
|
||||
urllib2.HTTPSHandler.__init__(self)
|
||||
def https_open(self, req):
|
||||
return self.do_open(self.specialized_conn_class, req)
|
||||
|
||||
|
||||
class URLOpener(object):
|
||||
"""
|
||||
pip's own URL helper that adds HTTP auth and proxy support
|
||||
"""
|
||||
def __init__(self):
|
||||
self.passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
|
||||
|
||||
def __call__(self, url):
|
||||
"""
|
||||
If the given url contains auth info or if a normal request gets a 401
|
||||
response, an attempt is made to fetch the resource using basic HTTP
|
||||
auth.
|
||||
|
||||
"""
|
||||
url, username, password, scheme = self.extract_credentials(url)
|
||||
if username is None:
|
||||
try:
|
||||
response = self.get_opener(scheme=scheme).open(url)
|
||||
except urllib2.HTTPError:
|
||||
e = sys.exc_info()[1]
|
||||
if e.code != 401:
|
||||
raise
|
||||
response = self.get_response(url)
|
||||
else:
|
||||
response = self.get_response(url, username, password)
|
||||
return response
|
||||
|
||||
def get_request(self, url):
|
||||
"""
|
||||
Wraps the URL to retrieve to protects against "creative"
|
||||
interpretation of the RFC: http://bugs.python.org/issue8732
|
||||
"""
|
||||
if isinstance(url, string_types):
|
||||
url = urllib2.Request(url, headers={'Accept-encoding': 'identity'})
|
||||
return url
|
||||
|
||||
def get_response(self, url, username=None, password=None):
|
||||
"""
|
||||
does the dirty work of actually getting the rsponse object using urllib2
|
||||
and its HTTP auth builtins.
|
||||
"""
|
||||
scheme, netloc, path, query, frag = urlparse.urlsplit(url)
|
||||
req = self.get_request(url)
|
||||
|
||||
stored_username, stored_password = self.passman.find_user_password(None, netloc)
|
||||
# see if we have a password stored
|
||||
if stored_username is None:
|
||||
if username is None and self.prompting:
|
||||
username = urllib.quote(raw_input('User for %s: ' % netloc))
|
||||
password = urllib.quote(getpass.getpass('Password: '))
|
||||
if username and password:
|
||||
self.passman.add_password(None, netloc, username, password)
|
||||
stored_username, stored_password = self.passman.find_user_password(None, netloc)
|
||||
authhandler = urllib2.HTTPBasicAuthHandler(self.passman)
|
||||
opener = self.get_opener(authhandler, scheme=scheme)
|
||||
# FIXME: should catch a 401 and offer to let the user reenter credentials
|
||||
return opener.open(req)
|
||||
|
||||
def get_opener(self, *args, **kwargs):
|
||||
"""
|
||||
Build an OpenerDirector instance based on the scheme, whether ssl is
|
||||
importable and the --insecure parameter.
|
||||
"""
|
||||
if kwargs.get('scheme') == 'https':
|
||||
if ssl:
|
||||
https_handler = VerifiedHTTPSHandler()
|
||||
director = urllib2.build_opener(https_handler, *args)
|
||||
#strip out HTTPHandler to prevent MITM spoof
|
||||
for handler in director.handlers:
|
||||
if isinstance(handler, urllib2.HTTPHandler):
|
||||
director.handlers.remove(handler)
|
||||
return director
|
||||
elif os.environ.get('PIP_INSECURE', '') == '1':
|
||||
return urllib2.build_opener(*args)
|
||||
else:
|
||||
raise NoSSLError()
|
||||
else:
|
||||
return urllib2.build_opener(*args)
|
||||
|
||||
def setup(self, proxystr='', prompting=True):
|
||||
"""
|
||||
Sets the proxy handler given the option passed on the command
|
||||
line. If an empty string is passed it looks at the HTTP_PROXY
|
||||
environment variable.
|
||||
"""
|
||||
self.prompting = prompting
|
||||
proxy = self.get_proxy(proxystr)
|
||||
if proxy:
|
||||
proxy_support = urllib2.ProxyHandler({"http": proxy, "ftp": proxy, "https": proxy})
|
||||
opener = urllib2.build_opener(proxy_support, urllib2.CacheFTPHandler)
|
||||
urllib2.install_opener(opener)
|
||||
|
||||
def parse_credentials(self, netloc):
|
||||
if "@" in netloc:
|
||||
userinfo = netloc.rsplit("@", 1)[0]
|
||||
if ":" in userinfo:
|
||||
return userinfo.split(":", 1)
|
||||
return userinfo, None
|
||||
return None, None
|
||||
|
||||
def extract_credentials(self, url):
|
||||
"""
|
||||
Extracts user/password from a url.
|
||||
|
||||
Returns a tuple:
|
||||
(url-without-auth, username, password)
|
||||
"""
|
||||
if isinstance(url, urllib2.Request):
|
||||
result = urlparse.urlsplit(url.get_full_url())
|
||||
else:
|
||||
result = urlparse.urlsplit(url)
|
||||
scheme, netloc, path, query, frag = result
|
||||
|
||||
username, password = self.parse_credentials(netloc)
|
||||
if username is None:
|
||||
return url, None, None, scheme
|
||||
elif password is None and self.prompting:
|
||||
# remove the auth credentials from the url part
|
||||
netloc = netloc.replace('%s@' % username, '', 1)
|
||||
# prompt for the password
|
||||
prompt = 'Password for %s@%s: ' % (username, netloc)
|
||||
password = urllib.quote(getpass.getpass(prompt))
|
||||
else:
|
||||
# remove the auth credentials from the url part
|
||||
netloc = netloc.replace('%s:%s@' % (username, password), '', 1)
|
||||
|
||||
target_url = urlparse.urlunsplit((scheme, netloc, path, query, frag))
|
||||
return target_url, username, password, scheme
|
||||
|
||||
def get_proxy(self, proxystr=''):
|
||||
"""
|
||||
Get the proxy given the option passed on the command line.
|
||||
If an empty string is passed it looks at the HTTP_PROXY
|
||||
environment variable.
|
||||
"""
|
||||
if not proxystr:
|
||||
proxystr = os.environ.get('HTTP_PROXY', '')
|
||||
if proxystr:
|
||||
if '@' in proxystr:
|
||||
user_password, server_port = proxystr.split('@', 1)
|
||||
if ':' in user_password:
|
||||
user, password = user_password.split(':', 1)
|
||||
else:
|
||||
user = user_password
|
||||
prompt = 'Password for %s@%s: ' % (user, server_port)
|
||||
password = urllib.quote(getpass.getpass(prompt))
|
||||
return '%s:%s@%s' % (user, password, server_port)
|
||||
else:
|
||||
return proxystr
|
||||
else:
|
||||
return None
|
||||
|
||||
urlopen = URLOpener()
|
||||
|
||||
|
||||
def is_url(name):
|
||||
"""Returns true if the name looks like a URL"""
|
||||
if ':' not in name:
|
||||
return False
|
||||
scheme = name.split(':', 1)[0].lower()
|
||||
return scheme in ['http', 'https', 'file', 'ftp'] + vcs.all_schemes
|
||||
|
||||
|
||||
def url_to_path(url):
|
||||
"""
|
||||
Convert a file: URL to a path.
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
_drive_re = re.compile('^([a-z]):', re.I)
|
||||
_url_drive_re = re.compile('^([a-z])[:|]', re.I)
|
||||
|
||||
|
||||
def path_to_url(path):
|
||||
"""
|
||||
Convert a path to a file: URL. The path will be made absolute.
|
||||
"""
|
||||
path = os.path.normcase(os.path.abspath(path))
|
||||
if _drive_re.match(path):
|
||||
path = path[0] + '|' + path[2:]
|
||||
url = urllib.quote(path)
|
||||
url = url.replace(os.path.sep, '/')
|
||||
url = url.lstrip('/')
|
||||
return 'file:///' + url
|
||||
|
||||
|
||||
def path_to_url2(path):
|
||||
"""
|
||||
Convert a path to a file: URL. The path will be made absolute and 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([urllib.quote(part) for part in filepath])
|
||||
if not drive:
|
||||
url = url.lstrip('/')
|
||||
return 'file:///' + drive + url
|
||||
|
||||
|
||||
def geturl(urllib2_resp):
|
||||
"""
|
||||
Use instead of urllib.addinfourl.geturl(), which appears to have
|
||||
some issues with dropping the double slash for certain schemes
|
||||
(e.g. file://). This implementation is probably over-eager, as it
|
||||
always restores '://' if it is missing, and it appears some url
|
||||
schemata aren't always followed by '//' after the colon, but as
|
||||
far as I know pip doesn't need any of those.
|
||||
The URI RFC can be found at: http://tools.ietf.org/html/rfc1630
|
||||
|
||||
This function assumes that
|
||||
scheme:/foo/bar
|
||||
is the same as
|
||||
scheme:///foo/bar
|
||||
"""
|
||||
url = urllib2_resp.geturl()
|
||||
scheme, rest = url.split(':', 1)
|
||||
if rest.startswith('//'):
|
||||
return url
|
||||
else:
|
||||
# FIXME: write a good test to cover it
|
||||
return '%s://%s' % (scheme, rest)
|
||||
|
||||
|
||||
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')
|
||||
ext = splitext(name)[1].lower()
|
||||
if ext in archives:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def unpack_vcs_link(link, location, only_download=False):
|
||||
vcs_backend = _get_used_vcs_backend(link)
|
||||
if only_download:
|
||||
vcs_backend.export(location)
|
||||
else:
|
||||
vcs_backend.unpack(location)
|
||||
|
||||
|
||||
def unpack_file_url(link, location):
|
||||
source = url_to_path(link.url)
|
||||
content_type = mimetypes.guess_type(source)[0]
|
||||
if os.path.isdir(source):
|
||||
# delete the location since shutil will create it again :(
|
||||
if os.path.isdir(location):
|
||||
rmtree(location)
|
||||
shutil.copytree(source, location)
|
||||
else:
|
||||
unpack_file(source, location, content_type, link)
|
||||
|
||||
|
||||
def _get_used_vcs_backend(link):
|
||||
for backend in vcs.backends:
|
||||
if link.scheme in backend.schemes:
|
||||
vcs_backend = backend(link.url)
|
||||
return vcs_backend
|
||||
|
||||
|
||||
def is_vcs_url(link):
|
||||
return bool(_get_used_vcs_backend(link))
|
||||
|
||||
|
||||
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 InstallationError('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, link.hash))
|
||||
raise InstallationError('Bad %s hash for package %s' % (link.hash_name, link))
|
||||
|
||||
|
||||
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))
|
||||
try:
|
||||
total_length = int(resp.info()['content-length'])
|
||||
except (ValueError, KeyError, TypeError):
|
||||
total_length = 0
|
||||
downloaded = 0
|
||||
show_progress = total_length > 40 * 1000 or not total_length
|
||||
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)
|
||||
else:
|
||||
logger.notify('Downloading %s' % show_url)
|
||||
logger.info('Downloading from URL %s' % link)
|
||||
|
||||
while True:
|
||||
chunk = resp.read(4096)
|
||||
if not chunk:
|
||||
break
|
||||
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):
|
||||
copy = True
|
||||
download_location = os.path.join(location, link.filename)
|
||||
if os.path.exists(download_location):
|
||||
response = ask_path_exists(
|
||||
'The file %s exists. (i)gnore, (w)ipe, (b)ackup ' %
|
||||
display_path(download_location), ('i', 'w', 'b'))
|
||||
if response == 'i':
|
||||
copy = False
|
||||
elif response == 'w':
|
||||
logger.warn('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)))
|
||||
shutil.move(download_location, dest_file)
|
||||
if copy:
|
||||
shutil.copy(filename, download_location)
|
||||
logger.indent -= 2
|
||||
logger.notify('Saved %s' % display_path(download_location))
|
||||
|
||||
|
||||
def unpack_http_url(link, location, download_cache, download_dir=None):
|
||||
temp_dir = tempfile.mkdtemp('-unpack', 'pip-')
|
||||
target_url = link.url.split('#', 1)[0]
|
||||
target_file = None
|
||||
download_hash = None
|
||||
if download_cache:
|
||||
target_file = os.path.join(download_cache,
|
||||
urllib.quote(target_url, ''))
|
||||
if not os.path.isdir(download_cache):
|
||||
create_download_cache_folder(download_cache)
|
||||
|
||||
already_downloaded = None
|
||||
if download_dir:
|
||||
already_downloaded = os.path.join(download_dir, link.filename)
|
||||
if not os.path.exists(already_downloaded):
|
||||
already_downloaded = None
|
||||
|
||||
if (target_file
|
||||
and os.path.exists(target_file)
|
||||
and os.path.exists(target_file + '.content-type')):
|
||||
fp = open(target_file+'.content-type')
|
||||
content_type = fp.read().strip()
|
||||
fp.close()
|
||||
if link.hash and link.hash_name:
|
||||
download_hash = _get_hash_from_file(target_file, link)
|
||||
temp_location = target_file
|
||||
logger.notify('Using download cache from %s' % target_file)
|
||||
elif already_downloaded:
|
||||
temp_location = already_downloaded
|
||||
content_type = mimetypes.guess_type(already_downloaded)
|
||||
if link.hash:
|
||||
download_hash = _get_hash_from_file(temp_location, link)
|
||||
logger.notify('File was already downloaded %s' % already_downloaded)
|
||||
else:
|
||||
resp = _get_response_from_url(target_url, link)
|
||||
content_type = resp.info()['content-type']
|
||||
filename = link.filename # fallback
|
||||
# Have a look at the Content-Disposition header for a better guess
|
||||
content_disposition = resp.info().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 != geturl(resp):
|
||||
ext = os.path.splitext(geturl(resp))[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)
|
||||
if download_dir and not already_downloaded:
|
||||
_copy_file(temp_location, download_dir, content_type, link)
|
||||
unpack_file(temp_location, location, content_type, link)
|
||||
if target_file and target_file != temp_location:
|
||||
cache_download(target_file, temp_location, content_type)
|
||||
if target_file is None and not already_downloaded:
|
||||
os.unlink(temp_location)
|
||||
os.rmdir(temp_dir)
|
||||
|
||||
|
||||
def _get_response_from_url(target_url, link):
|
||||
try:
|
||||
resp = urlopen(target_url)
|
||||
except urllib2.HTTPError:
|
||||
e = sys.exc_info()[1]
|
||||
logger.fatal("HTTP error %s while getting %s" % (e.code, link))
|
||||
raise
|
||||
except IOError:
|
||||
e = sys.exc_info()[1]
|
||||
# Typically an FTP error
|
||||
logger.fatal("Error %s while getting %s" % (e, link))
|
||||
raise
|
||||
return resp
|
||||
|
||||
|
||||
class Urllib2HeadRequest(urllib2.Request):
|
||||
def get_method(self):
|
||||
return "HEAD"
|
||||
Vendored
+56
@@ -0,0 +1,56 @@
|
||||
"""Exceptions used throughout package"""
|
||||
|
||||
import textwrap
|
||||
|
||||
class PipError(Exception):
|
||||
"""Base pip exception"""
|
||||
|
||||
|
||||
class InstallationError(PipError):
|
||||
"""General exception during installation"""
|
||||
|
||||
|
||||
class UninstallationError(PipError):
|
||||
"""General exception during uninstallation"""
|
||||
|
||||
|
||||
class DistributionNotFound(InstallationError):
|
||||
"""Raised when a distribution cannot be found to satisfy a requirement"""
|
||||
|
||||
|
||||
class BestVersionAlreadyInstalled(PipError):
|
||||
"""Raised when the most up-to-date version of a package is already
|
||||
installed. """
|
||||
|
||||
|
||||
class BadCommand(PipError):
|
||||
"""Raised when virtualenv or a command is not found"""
|
||||
|
||||
|
||||
class CommandError(PipError):
|
||||
"""Raised when there is an error in command-line arguments"""
|
||||
|
||||
|
||||
class NoSSLError(PipError):
|
||||
"""Raised when there's no ssl and not using '--insecure'"""
|
||||
|
||||
def __str__(self):
|
||||
return textwrap.dedent("""
|
||||
###################################################################
|
||||
## You don't have an importable ssl module. You are most ##
|
||||
## likely using Python 2.5, which did not include ssl ##
|
||||
## support by default. In this state, we can not provide ##
|
||||
## ssl certified downloads from PyPI. ##
|
||||
## ##
|
||||
## You can do one of 2 things: ##
|
||||
## 1) Install this: https://pypi.python.org/pypi/ssl/ ##
|
||||
## (It provides ssl support for older Pythons ) ##
|
||||
## 2) Use the --insecure option to allow this insecurity ##
|
||||
## ##
|
||||
## For more details, go to the "SSL Certificate Verification" ##
|
||||
## section located here: ##
|
||||
## http://www.pip-installer.org/en/latest/logic.html ##
|
||||
## ##
|
||||
###################################################################
|
||||
""")
|
||||
|
||||
Vendored
+757
@@ -0,0 +1,757 @@
|
||||
"""Routines related to PyPI, indexes"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import gzip
|
||||
import mimetypes
|
||||
import posixpath
|
||||
import pkg_resources
|
||||
import random
|
||||
import socket
|
||||
import string
|
||||
import zlib
|
||||
|
||||
try:
|
||||
import threading
|
||||
except ImportError:
|
||||
import dummy_threading as threading
|
||||
|
||||
from pip.log import logger
|
||||
from pip.util import Inf, normalize_name, splitext
|
||||
from pip.exceptions import DistributionNotFound, BestVersionAlreadyInstalled
|
||||
from pip.backwardcompat import (WindowsError, BytesIO,
|
||||
Queue, urlparse,
|
||||
URLError, HTTPError, u,
|
||||
product, url2pathname, ssl,
|
||||
Empty as QueueEmpty)
|
||||
if ssl:
|
||||
from pip.backwardcompat import CertificateError
|
||||
from pip.download import urlopen, path_to_url2, url_to_path, geturl, Urllib2HeadRequest
|
||||
|
||||
__all__ = ['PackageFinder']
|
||||
|
||||
|
||||
DEFAULT_MIRROR_HOSTNAME = "last.pypi.python.org"
|
||||
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
def __init__(self, find_links, index_urls,
|
||||
use_mirrors=False, mirrors=None, main_mirror_url=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()
|
||||
if use_mirrors:
|
||||
self.mirror_urls = self._get_mirror_urls(mirrors, main_mirror_url)
|
||||
logger.info('Using PyPI mirrors: %s' % ', '.join(self.mirror_urls))
|
||||
else:
|
||||
self.mirror_urls = []
|
||||
|
||||
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)
|
||||
self.dependency_links.extend(links)
|
||||
|
||||
def _sort_locations(self, locations):
|
||||
"""
|
||||
Sort locations into "files" (archives) and "urls", and return
|
||||
a pair of lists (files,urls)
|
||||
"""
|
||||
files = []
|
||||
urls = []
|
||||
|
||||
# puts the url for the given file path into the appropriate list
|
||||
def sort_path(path):
|
||||
url = path_to_url2(path)
|
||||
if mimetypes.guess_type(url, strict=False)[0] == 'text/html':
|
||||
urls.append(url)
|
||||
else:
|
||||
files.append(url)
|
||||
|
||||
for url in locations:
|
||||
|
||||
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)
|
||||
elif os.path.isfile(path):
|
||||
sort_path(path)
|
||||
else:
|
||||
urls.append(url)
|
||||
|
||||
return files, urls
|
||||
|
||||
def find_requirement(self, req, upgrade):
|
||||
|
||||
def mkurl_pypi_url(url):
|
||||
loc = posixpath.join(url, url_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.
|
||||
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]))
|
||||
# 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]), url_name, req) or req.url_name
|
||||
|
||||
# Combine index URLs with mirror URLs here to allow
|
||||
# adding more index URLs from requirements files
|
||||
all_index_urls = self.index_urls + self.mirror_urls
|
||||
|
||||
if url_name is not None:
|
||||
locations = [
|
||||
mkurl_pypi_url(url)
|
||||
for url in all_index_urls] + self.find_links
|
||||
else:
|
||||
locations = list(self.find_links)
|
||||
locations.extend(self.dependency_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
|
||||
|
||||
file_locations, url_locations = self._sort_locations(locations)
|
||||
|
||||
locations = [Link(url) for url in url_locations]
|
||||
logger.debug('URLs to search for versions for %s:' % req)
|
||||
for location in locations:
|
||||
logger.debug('* %s' % location)
|
||||
found_versions = []
|
||||
found_versions.extend(
|
||||
self._package_versions(
|
||||
[Link(url, '-f') 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()))
|
||||
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)
|
||||
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, InfLink, req.satisfied_by.version)]
|
||||
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
|
||||
applicable_versions.append((parsed_version, link, version))
|
||||
#bring the latest version to the front, but maintains the priority ordering as secondary
|
||||
applicable_versions = sorted(applicable_versions, key=lambda v: v[0], reverse=True)
|
||||
existing_applicable = bool([link for parsed_version, link, version in applicable_versions if link is InfLink])
|
||||
if not upgrade and existing_applicable:
|
||||
if applicable_versions[0][1] is InfLink:
|
||||
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])))
|
||||
raise DistributionNotFound('No distributions matching the version for %s' % req)
|
||||
if applicable_versions[0][1] is InfLink:
|
||||
# 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])))
|
||||
return applicable_versions[0][1]
|
||||
|
||||
|
||||
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
|
||||
|
||||
def _get_pages(self, locations, req):
|
||||
"""Yields (page, page_url) from the given locations, skipping
|
||||
locations that have errors, and adding download/homepage links"""
|
||||
pending_queue = Queue()
|
||||
for location in locations:
|
||||
pending_queue.put(location)
|
||||
done = []
|
||||
seen = set()
|
||||
threads = []
|
||||
for i in range(min(10, len(locations))):
|
||||
t = threading.Thread(target=self._get_queued_page, args=(req, pending_queue, done, seen))
|
||||
t.setDaemon(True)
|
||||
threads.append(t)
|
||||
t.start()
|
||||
for t in threads:
|
||||
t.join()
|
||||
return done
|
||||
|
||||
_log_lock = threading.Lock()
|
||||
|
||||
def _get_queued_page(self, req, pending_queue, done, seen):
|
||||
while 1:
|
||||
try:
|
||||
location = pending_queue.get(False)
|
||||
except QueueEmpty:
|
||||
return
|
||||
if location in seen:
|
||||
continue
|
||||
seen.add(location)
|
||||
page = self._get_page(location, req)
|
||||
if page is None:
|
||||
continue
|
||||
done.append(page)
|
||||
for link in page.rel_links():
|
||||
pending_queue.put(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"
|
||||
eggs, no_eggs = [], []
|
||||
seen = set()
|
||||
for link in links:
|
||||
if link not in seen:
|
||||
seen.add(link)
|
||||
if link.egg_fragment:
|
||||
eggs.append(link)
|
||||
else:
|
||||
no_eggs.append(link)
|
||||
return no_eggs + eggs
|
||||
|
||||
def _package_versions(self, links, search_name):
|
||||
for link in self._sort_links(links):
|
||||
for v in self._link_package_versions(link, search_name):
|
||||
yield v
|
||||
|
||||
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.
|
||||
"""
|
||||
if link.egg_fragment:
|
||||
egg_info = link.egg_fragment
|
||||
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 ('.tar.gz', '.tar.bz2', '.tar', '.tgz', '.zip'):
|
||||
if link not in self.logged_links:
|
||||
logger.debug('Skipping link %s; unknown archive format: %s' % (link, ext))
|
||||
self.logged_links.add(link)
|
||||
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 []
|
||||
version = self._egg_info_matches(egg_info, search_name, link)
|
||||
if version is None:
|
||||
logger.debug('Skipping link %s; wrong project name (not %s)' % (link, search_name))
|
||||
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)
|
||||
|
||||
def _get_mirror_urls(self, mirrors=None, main_mirror_url=None):
|
||||
"""Retrieves a list of URLs from the main mirror DNS entry
|
||||
unless a list of mirror URLs are passed.
|
||||
"""
|
||||
if not mirrors:
|
||||
mirrors = get_mirrors(main_mirror_url)
|
||||
# Should this be made "less random"? E.g. netselect like?
|
||||
random.shuffle(mirrors)
|
||||
|
||||
mirror_urls = set()
|
||||
for mirror_url in mirrors:
|
||||
mirror_url = mirror_url.rstrip('/')
|
||||
# Make sure we have a valid URL
|
||||
if not any([mirror_url.startswith(scheme) for scheme in ["http://", "https://", "file://"]]):
|
||||
mirror_url = "http://%s" % mirror_url
|
||||
if not mirror_url.endswith("/simple"):
|
||||
mirror_url = "%s/simple" % mirror_url
|
||||
mirror_urls.add(mirror_url + '/')
|
||||
|
||||
return list(mirror_urls)
|
||||
|
||||
|
||||
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
|
||||
|
||||
def get_page(self, url):
|
||||
return self._pages.get(url)
|
||||
|
||||
def is_archive(self, url):
|
||||
return self._archives.get(url, False)
|
||||
|
||||
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 add_page(self, urls, page):
|
||||
for url in urls:
|
||||
self._pages[url] = page
|
||||
|
||||
|
||||
class HTMLPage(object):
|
||||
"""Represents one page, along with its URL"""
|
||||
|
||||
## FIXME: these regexes are horrible hacks:
|
||||
_homepage_re = re.compile(r'<th>\s*home\s*page', re.I)
|
||||
_download_re = re.compile(r'<th>\s*download\s+url', re.I)
|
||||
## These aren't so aweful:
|
||||
_rel_re = re.compile("""<[^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*>""", re.I)
|
||||
_href_re = re.compile('href=(?:"([^"]*)"|\'([^\']*)\'|([^>\\s\\n]*))', re.I|re.S)
|
||||
_base_re = re.compile(r"""<base\s+href\s*=\s*['"]?([^'">]+)""", re.I)
|
||||
|
||||
def __init__(self, content, url, headers=None):
|
||||
self.content = content
|
||||
self.url = url
|
||||
self.headers = headers
|
||||
|
||||
def __str__(self):
|
||||
return self.url
|
||||
|
||||
@classmethod
|
||||
def get_page(cls, link, req, cache=None, skip_archives=True):
|
||||
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())
|
||||
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']:
|
||||
if filename.endswith(bad_ext):
|
||||
content_type = cls._get_content_type(url)
|
||||
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)
|
||||
|
||||
# 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
|
||||
if not url.endswith('/'):
|
||||
url += '/'
|
||||
url = urlparse.urljoin(url, 'index.html')
|
||||
logger.debug(' file: URL is directory, getting %s' % url)
|
||||
|
||||
resp = urlopen(url)
|
||||
|
||||
real_url = geturl(resp)
|
||||
headers = resp.info()
|
||||
contents = resp.read()
|
||||
encoding = headers.get('Content-Encoding', None)
|
||||
#XXX need to handle exceptions and add testing for this
|
||||
if encoding is not None:
|
||||
if encoding == 'gzip':
|
||||
contents = gzip.GzipFile(fileobj=BytesIO(contents)).read()
|
||||
if encoding == 'deflate':
|
||||
contents = zlib.decompress(contents)
|
||||
inst = cls(u(contents), real_url, headers)
|
||||
except (HTTPError, URLError, socket.timeout, socket.error, OSError, WindowsError):
|
||||
e = sys.exc_info()[1]
|
||||
desc = str(e)
|
||||
if isinstance(e, socket.timeout):
|
||||
log_meth = logger.info
|
||||
level =1
|
||||
desc = 'timed out'
|
||||
elif isinstance(e, URLError):
|
||||
#ssl/certificate error
|
||||
if ssl and hasattr(e, 'reason') and (isinstance(e.reason, ssl.SSLError) or isinstance(e.reason, CertificateError)):
|
||||
desc = 'There was a problem confirming the ssl certificate: %s' % e
|
||||
log_meth = logger.notify
|
||||
else:
|
||||
log_meth = logger.info
|
||||
if hasattr(e, 'reason') and isinstance(e.reason, socket.timeout):
|
||||
desc = 'timed out'
|
||||
level = 1
|
||||
else:
|
||||
level = 2
|
||||
elif isinstance(e, HTTPError) and e.code == 404:
|
||||
## FIXME: notify?
|
||||
log_meth = logger.info
|
||||
level = 2
|
||||
else:
|
||||
log_meth = logger.info
|
||||
level = 1
|
||||
log_meth('Could not fetch URL %s: %s' % (link, desc))
|
||||
log_meth('Will skip URL %s when looking for download links for %s' % (link.url, req))
|
||||
if cache is not None:
|
||||
cache.add_page_failure(url, level)
|
||||
return None
|
||||
if cache is not None:
|
||||
cache.add_page([url, real_url], inst)
|
||||
return inst
|
||||
|
||||
@staticmethod
|
||||
def _get_content_type(url):
|
||||
"""Get the Content-Type of the given url, using a HEAD request"""
|
||||
scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
|
||||
if not scheme in ('http', 'https', 'ftp', 'ftps'):
|
||||
## FIXME: some warning or something?
|
||||
## assertion error?
|
||||
return ''
|
||||
req = Urllib2HeadRequest(url, headers={'Host': netloc})
|
||||
resp = urlopen(req)
|
||||
try:
|
||||
if hasattr(resp, 'code') and resp.code != 200 and scheme not in ('ftp', 'ftps'):
|
||||
## FIXME: doesn't handle redirects
|
||||
return ''
|
||||
return resp.info().get('content-type', '')
|
||||
finally:
|
||||
resp.close()
|
||||
|
||||
@property
|
||||
def base_url(self):
|
||||
if not hasattr(self, "_base_url"):
|
||||
match = self._base_re.search(self.content)
|
||||
if match:
|
||||
self._base_url = match.group(1)
|
||||
else:
|
||||
self._base_url = self.url
|
||||
return self._base_url
|
||||
|
||||
@property
|
||||
def links(self):
|
||||
"""Yields all links in the page"""
|
||||
for match in self._href_re.finditer(self.content):
|
||||
url = match.group(1) or match.group(2) or match.group(3)
|
||||
url = self.clean_link(urlparse.urljoin(self.base_url, url))
|
||||
yield Link(url, self)
|
||||
|
||||
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"""
|
||||
for match in self._rel_re.finditer(self.content):
|
||||
found_rels = match.group(1).lower().split()
|
||||
for rel in rels:
|
||||
if rel in found_rels:
|
||||
break
|
||||
else:
|
||||
continue
|
||||
match = self._href_re.search(match.group(0))
|
||||
if not match:
|
||||
continue
|
||||
url = match.group(1) or match.group(2) or match.group(3)
|
||||
url = self.clean_link(urlparse.urljoin(self.base_url, url))
|
||||
yield Link(url, self)
|
||||
|
||||
def scraped_rel_links(self):
|
||||
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)
|
||||
|
||||
_clean_re = re.compile(r'[^a-z0-9$&+,/:;=?@.#%_\\|-]', re.I)
|
||||
|
||||
def clean_link(self, url):
|
||||
"""Makes sure a link is fully encoded. That is, if a ' ' shows up in
|
||||
the link, it will be rewritten to %20 (while not over-quoting
|
||||
% or other characters)."""
|
||||
return self._clean_re.sub(
|
||||
lambda match: '%%%2x' % ord(match.group(0)), url)
|
||||
|
||||
|
||||
class Link(object):
|
||||
|
||||
def __init__(self, url, comes_from=None):
|
||||
self.url = url
|
||||
self.comes_from = comes_from
|
||||
|
||||
def __str__(self):
|
||||
if self.comes_from:
|
||||
return '%s (from %s)' % (self.url, self.comes_from)
|
||||
else:
|
||||
return str(self.url)
|
||||
|
||||
def __repr__(self):
|
||||
return '<Link %s>' % self
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.url == other.url
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.url != other.url
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.url < other.url
|
||||
|
||||
def __le__(self, other):
|
||||
return self.url <= other.url
|
||||
|
||||
def __gt__(self, other):
|
||||
return self.url > other.url
|
||||
|
||||
def __ge__(self, other):
|
||||
return self.url >= other.url
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.url)
|
||||
|
||||
@property
|
||||
def filename(self):
|
||||
_, netloc, path, _, _ = urlparse.urlsplit(self.url)
|
||||
name = posixpath.basename(path.rstrip('/')) or netloc
|
||||
assert name, ('URL %r produced no filename' % self.url)
|
||||
return name
|
||||
|
||||
@property
|
||||
def scheme(self):
|
||||
return urlparse.urlsplit(self.url)[0]
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return urlparse.urlsplit(self.url)[2]
|
||||
|
||||
def splitext(self):
|
||||
return splitext(posixpath.basename(self.path.rstrip('/')))
|
||||
|
||||
@property
|
||||
def url_without_fragment(self):
|
||||
scheme, netloc, path, query, fragment = urlparse.urlsplit(self.url)
|
||||
return urlparse.urlunsplit((scheme, netloc, path, query, None))
|
||||
|
||||
_egg_fragment_re = re.compile(r'#egg=([^&]*)')
|
||||
|
||||
@property
|
||||
def egg_fragment(self):
|
||||
match = self._egg_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):
|
||||
match = self._hash_re.search(self.url)
|
||||
if match:
|
||||
return match.group(2)
|
||||
return None
|
||||
|
||||
@property
|
||||
def hash_name(self):
|
||||
match = self._hash_re.search(self.url)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return None
|
||||
|
||||
@property
|
||||
def show_url(self):
|
||||
return posixpath.basename(self.url.split('#', 1)[0].split('?', 1)[0])
|
||||
|
||||
#An "Infinite Link" that compares greater than other links
|
||||
InfLink = Link(Inf) #this object is not currently used as a sortable
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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 get_mirrors(hostname=None):
|
||||
"""Return the list of mirrors from the last record found on the DNS
|
||||
entry::
|
||||
|
||||
>>> from pip.index import get_mirrors
|
||||
>>> get_mirrors()
|
||||
['a.pypi.python.org', 'b.pypi.python.org', 'c.pypi.python.org',
|
||||
'd.pypi.python.org']
|
||||
|
||||
Originally written for the distutils2 project by Alexis Metaireau.
|
||||
"""
|
||||
if hostname is None:
|
||||
hostname = DEFAULT_MIRROR_HOSTNAME
|
||||
|
||||
# return the last mirror registered on PyPI.
|
||||
last_mirror_hostname = None
|
||||
try:
|
||||
last_mirror_hostname = socket.gethostbyname_ex(hostname)[0]
|
||||
except socket.gaierror:
|
||||
return []
|
||||
if not last_mirror_hostname or last_mirror_hostname == DEFAULT_MIRROR_HOSTNAME:
|
||||
last_mirror_hostname = "z.pypi.python.org"
|
||||
end_letter = last_mirror_hostname.split(".", 1)
|
||||
|
||||
# determine the list from the last one.
|
||||
return ["%s.%s" % (s, end_letter[1]) for s in string_range(end_letter[0])]
|
||||
|
||||
|
||||
def string_range(last):
|
||||
"""Compute the range of string between "a" and last.
|
||||
|
||||
This works for simple "a to z" lists, but also for "a to zz" lists.
|
||||
"""
|
||||
for k in range(len(last)):
|
||||
for x in product(string.ascii_lowercase, repeat=k+1):
|
||||
result = ''.join(x)
|
||||
yield result
|
||||
if result == last:
|
||||
return
|
||||
|
||||
Vendored
+100
@@ -0,0 +1,100 @@
|
||||
"""Locations where we look for configs, install stuff, etc"""
|
||||
|
||||
import sys
|
||||
import site
|
||||
import os
|
||||
import tempfile
|
||||
import getpass
|
||||
from pip.backwardcompat import get_python_lib
|
||||
import pip.exceptions
|
||||
|
||||
default_cert_path = os.path.join(os.path.dirname(__file__), 'cacert.pem')
|
||||
|
||||
def running_under_virtualenv():
|
||||
"""
|
||||
Return True if we're running inside a virtualenv, False otherwise.
|
||||
|
||||
"""
|
||||
return hasattr(sys, 'real_prefix')
|
||||
|
||||
|
||||
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
|
||||
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_build_prefix():
|
||||
""" Returns a safe build_prefix """
|
||||
path = os.path.join(tempfile.gettempdir(), 'pip-build-%s' % \
|
||||
getpass.getuser())
|
||||
if sys.platform == 'win32':
|
||||
""" on windows(tested on 7) temp dirs are isolated """
|
||||
return path
|
||||
try:
|
||||
os.mkdir(path)
|
||||
except OSError:
|
||||
file_uid = None
|
||||
try:
|
||||
fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW)
|
||||
file_uid = os.fstat(fd).st_uid
|
||||
os.close(fd)
|
||||
except OSError:
|
||||
file_uid = None
|
||||
if file_uid != os.getuid():
|
||||
msg = "The temporary folder for building (%s) is not owned by your user!" \
|
||||
% path
|
||||
print (msg)
|
||||
print("pip will not work until the temporary folder is " + \
|
||||
"either deleted or 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:
|
||||
# Use tempfile to create a temporary folder for build
|
||||
# Note: we are NOT using mkdtemp so we can have a consistent build dir
|
||||
# Note: using realpath due to tmp dirs on OSX being symlinks
|
||||
build_prefix = _get_build_prefix()
|
||||
|
||||
## 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.")
|
||||
|
||||
# under Mac OS X + virtualenv sys.prefix is not properly resolved
|
||||
# it is something like /path/to/python/bin/..
|
||||
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':
|
||||
bin_py = os.path.join(sys.prefix, 'Scripts')
|
||||
# buildout uses 'bin' on Windows too?
|
||||
if not os.path.exists(bin_py):
|
||||
bin_py = os.path.join(sys.prefix, 'bin')
|
||||
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')
|
||||
else:
|
||||
bin_py = os.path.join(sys.prefix, 'bin')
|
||||
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')
|
||||
|
||||
# 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')
|
||||
Vendored
+187
@@ -0,0 +1,187 @@
|
||||
"""Logging
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
from pip import backwardcompat
|
||||
|
||||
|
||||
class Logger(object):
|
||||
"""
|
||||
Logging object for use in command-line script. Allows ranges of
|
||||
levels, to avoid some redundancy of displayed information.
|
||||
"""
|
||||
VERBOSE_DEBUG = logging.DEBUG - 1
|
||||
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]
|
||||
|
||||
def __init__(self):
|
||||
self.consumers = []
|
||||
self.indent = 0
|
||||
self.explicit_levels = False
|
||||
self.in_progress = None
|
||||
self.in_progress_hanging = False
|
||||
|
||||
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.WARN, msg, *args, **kw)
|
||||
|
||||
def fatal(self, msg, *args, **kw):
|
||||
self.log(self.FATAL, msg, *args, **kw)
|
||||
|
||||
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
|
||||
rendered = None
|
||||
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 rendered is None:
|
||||
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)
|
||||
if hasattr(consumer, 'write'):
|
||||
rendered += '\n'
|
||||
backwardcompat.fwrite(consumer, rendered)
|
||||
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()
|
||||
Vendored
+1613
File diff suppressed because it is too large
Load Diff
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
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)
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
SUCCESS = 0
|
||||
ERROR = 1
|
||||
UNKNOWN_ERROR = 2
|
||||
VIRTUALENV_NOT_FOUND = 3
|
||||
NO_MATCHES_FOUND = 23
|
||||
Vendored
+668
@@ -0,0 +1,668 @@
|
||||
import sys
|
||||
import shutil
|
||||
import os
|
||||
import stat
|
||||
import re
|
||||
import posixpath
|
||||
import pkg_resources
|
||||
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, ssl)
|
||||
from pip.locations import site_packages, running_under_virtualenv, virtualenv_no_global
|
||||
from pip.log import logger
|
||||
|
||||
__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', ''):
|
||||
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'<title>[^<]*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(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'),
|
||||
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 unzip_file(filename, location, flatten=True):
|
||||
"""Unzip the file (zip file located at filename) to the destination
|
||||
location"""
|
||||
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 name in zip.namelist():
|
||||
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()
|
||||
finally:
|
||||
zipfp.close()
|
||||
|
||||
|
||||
def untar_file(filename, location):
|
||||
"""Untar the file (tar file located at filename) to the destination location"""
|
||||
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()
|
||||
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()
|
||||
os.unlink(temp_location)
|
||||
|
||||
|
||||
def unpack_file(filename, location, content_type, link):
|
||||
if (content_type == 'application/zip'
|
||||
or filename.endswith('.zip')
|
||||
or filename.endswith('.pybundle')
|
||||
or zipfile.is_zipfile(filename)):
|
||||
unzip_file(filename, location, flatten=not filename.endswith('.pybundle'))
|
||||
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)
|
||||
Vendored
+251
@@ -0,0 +1,251 @@
|
||||
"""Handles all VCS (version control) support"""
|
||||
|
||||
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)
|
||||
|
||||
|
||||
__all__ = ['vcs', 'get_src_requirement']
|
||||
|
||||
|
||||
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)
|
||||
# Python >= 2.7.4, 3.3 doesn't have uses_fragment
|
||||
if getattr(urlparse, 'uses_fragment', None):
|
||||
urlparse.uses_fragment.extend(self.schemes)
|
||||
super(VcsSupport, self).__init__()
|
||||
|
||||
def __iter__(self):
|
||||
return self._registry.__iter__()
|
||||
|
||||
@property
|
||||
def backends(self):
|
||||
return list(self._registry.values())
|
||||
|
||||
@property
|
||||
def dirnames(self):
|
||||
return [backend.dirname for backend in self.backends]
|
||||
|
||||
@property
|
||||
def all_schemes(self):
|
||||
schemes = []
|
||||
for backend in self.backends:
|
||||
schemes.extend(backend.schemes)
|
||||
return schemes
|
||||
|
||||
def register(self, cls):
|
||||
if not hasattr(cls, 'name'):
|
||||
logger.warn('Cannot register VCS %s' % cls.__name__)
|
||||
return
|
||||
if cls.name not in self._registry:
|
||||
self._registry[cls.name] = cls
|
||||
|
||||
def unregister(self, cls=None, name=None):
|
||||
if name in self._registry:
|
||||
del self._registry[name]
|
||||
elif cls in self._registry.values():
|
||||
del self._registry[cls.name]
|
||||
else:
|
||||
logger.warn('Cannot unregister because no class or name given')
|
||||
|
||||
def get_backend_name(self, location):
|
||||
"""
|
||||
Return the name of the version control backend if found at given
|
||||
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):
|
||||
return vc_type.name
|
||||
return None
|
||||
|
||||
def get_backend(self, name):
|
||||
name = name.lower()
|
||||
if name in self._registry:
|
||||
return self._registry[name]
|
||||
|
||||
def get_backend_from_location(self, location):
|
||||
vc_type = self.get_backend_name(location)
|
||||
if vc_type:
|
||||
return self.get_backend(vc_type)
|
||||
return None
|
||||
|
||||
|
||||
vcs = VcsSupport()
|
||||
|
||||
|
||||
class VersionControl(object):
|
||||
name = ''
|
||||
dirname = ''
|
||||
|
||||
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)
|
||||
"""
|
||||
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
|
||||
|
||||
def get_url_rev(self):
|
||||
"""
|
||||
Returns the correct repository URL and revision by parsing the given
|
||||
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")
|
||||
assert '+' in self.url, error_message % self.url
|
||||
url = self.url.split('+', 1)[1]
|
||||
scheme, netloc, path, query, frag = urlparse.urlsplit(url)
|
||||
rev = None
|
||||
if '@' in path:
|
||||
path, rev = path.rsplit('@', 1)
|
||||
url = urlparse.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
|
||||
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.
|
||||
"""
|
||||
return urllib.unquote(url).rstrip('/')
|
||||
|
||||
def compare_urls(self, url1, url2):
|
||||
"""
|
||||
Compare two repo URLs for identity, ignoring incidental differences.
|
||||
"""
|
||||
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
|
||||
source path of the checkout.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def switch(self, dest, url, rev_options):
|
||||
"""
|
||||
Switch the repo at ``dest`` to point to ``URL``.
|
||||
"""
|
||||
raise NotImplemented
|
||||
|
||||
def update(self, dest, rev_options):
|
||||
"""
|
||||
Update an already-existing repo to the given ``rev_options``.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def check_destination(self, dest, url, rev_options, rev_display):
|
||||
"""
|
||||
Prepare a location to receive a checkout/clone.
|
||||
|
||||
Return True if the location is ready for (and requires) a
|
||||
checkout/clone, False otherwise.
|
||||
"""
|
||||
checkout = True
|
||||
prompt = False
|
||||
if os.path.exists(dest):
|
||||
checkout = False
|
||||
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)
|
||||
else:
|
||||
logger.warn('%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))
|
||||
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))
|
||||
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))
|
||||
self.switch(dest, url, rev_options)
|
||||
elif response == 'i':
|
||||
# do nothing
|
||||
pass
|
||||
elif response == 'w':
|
||||
logger.warn('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))
|
||||
shutil.move(dest, dest_dir)
|
||||
checkout = True
|
||||
return checkout
|
||||
|
||||
def unpack(self, location):
|
||||
if os.path.exists(location):
|
||||
rmtree(location)
|
||||
self.obtain(location)
|
||||
|
||||
def get_src_requirement(self, dist, location, find_tags=False):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def get_src_requirement(dist, location, find_tags):
|
||||
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)
|
||||
return dist.as_requirement()
|
||||
Vendored
+131
@@ -0,0 +1,131 @@
|
||||
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
|
||||
from pip.vcs import vcs, VersionControl
|
||||
from pip.download import path_to_url2
|
||||
|
||||
|
||||
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')
|
||||
|
||||
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
|
||||
|
||||
def export(self, 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)
|
||||
finally:
|
||||
rmtree(temp_dir)
|
||||
|
||||
def switch(self, dest, url, rev_options):
|
||||
call_subprocess([self.cmd, 'switch', url], cwd=dest)
|
||||
|
||||
def update(self, dest, rev_options):
|
||||
call_subprocess(
|
||||
[self.cmd, 'pull', '-q'] + rev_options, cwd=dest)
|
||||
|
||||
def obtain(self, dest):
|
||||
url, rev = self.get_url_rev()
|
||||
if rev:
|
||||
rev_options = ['-r', rev]
|
||||
rev_display = ' (to revision %s)' % rev
|
||||
else:
|
||||
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])
|
||||
|
||||
def get_url_rev(self):
|
||||
# hotfix the URL scheme after removing bzr+ from bzr+ssh:// readd it
|
||||
url, rev = super(Bazaar, self).get_url_rev()
|
||||
if url.startswith('ssh://'):
|
||||
url = 'bzr+' + url
|
||||
return url, rev
|
||||
|
||||
def get_url(self, location):
|
||||
urls = call_subprocess(
|
||||
[self.cmd, 'info'], show_stdout=False, cwd=location)
|
||||
for line in urls.splitlines():
|
||||
line = line.strip()
|
||||
for x in ('checkout of branch: ',
|
||||
'parent branch: '):
|
||||
if line.startswith(x):
|
||||
repo = line.split(x)[1]
|
||||
if self._is_local_repository(repo):
|
||||
return path_to_url2(repo)
|
||||
return repo
|
||||
return None
|
||||
|
||||
def get_revision(self, location):
|
||||
revision = call_subprocess(
|
||||
[self.cmd, '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):
|
||||
repo = self.get_url(location)
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
vcs.register(Bazaar)
|
||||
Vendored
+220
@@ -0,0 +1,220 @@
|
||||
import tempfile
|
||||
import re
|
||||
import os.path
|
||||
from pip.util import call_subprocess
|
||||
from pip.util 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
|
||||
|
||||
|
||||
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')
|
||||
|
||||
def __init__(self, url=None, *args, **kwargs):
|
||||
|
||||
# Works around an apparent Git bug
|
||||
# (see http://article.gmane.org/gmane.comp.version-control.git/146500)
|
||||
if url:
|
||||
scheme, netloc, path, query, fragment = urlsplit(url)
|
||||
if scheme.endswith('file'):
|
||||
initial_slashes = path[:-len(path.lstrip('/'))]
|
||||
newpath = initial_slashes + 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))
|
||||
|
||||
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-')
|
||||
self.unpack(temp_dir)
|
||||
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)
|
||||
finally:
|
||||
rmtree(temp_dir)
|
||||
|
||||
def check_rev_options(self, rev, dest, rev_options):
|
||||
"""Check the revision options before checkout to compensate that tags
|
||||
and branches may need origin/ as a prefix.
|
||||
Returns the SHA1 of the branch or tag if found.
|
||||
"""
|
||||
revisions = self.get_tag_revs(dest)
|
||||
revisions.update(self.get_branch_revs(dest))
|
||||
|
||||
origin_rev = 'origin/%s' % rev
|
||||
if origin_rev in revisions:
|
||||
# remote branch
|
||||
return [revisions[origin_rev]]
|
||||
elif rev in revisions:
|
||||
# a local tag or branch name
|
||||
return [revisions[rev]]
|
||||
else:
|
||||
logger.warn("Could not find a tag or branch '%s', assuming commit." % rev)
|
||||
return rev_options
|
||||
|
||||
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.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)
|
||||
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)
|
||||
#: update submodules
|
||||
self.update_submodules(dest)
|
||||
|
||||
def obtain(self, dest):
|
||||
url, rev = self.get_url_rev()
|
||||
if rev:
|
||||
rev_options = [rev]
|
||||
rev_display = ' (to %s)' % rev
|
||||
else:
|
||||
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)
|
||||
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)
|
||||
|
||||
def get_url(self, location):
|
||||
url = call_subprocess(
|
||||
[self.cmd, 'config', 'remote.origin.url'],
|
||||
show_stdout=False, cwd=location)
|
||||
return url.strip()
|
||||
|
||||
def get_revision(self, location):
|
||||
current_rev = call_subprocess(
|
||||
[self.cmd, 'rev-parse', 'HEAD'], show_stdout=False, cwd=location)
|
||||
return current_rev.strip()
|
||||
|
||||
def get_tag_revs(self, location):
|
||||
tags = self._get_all_tag_names(location)
|
||||
tag_revs = {}
|
||||
for line in tags.splitlines():
|
||||
tag = line.strip()
|
||||
rev = self._get_revision_from_rev_parse(tag, location)
|
||||
tag_revs[tag] = rev.strip()
|
||||
return tag_revs
|
||||
|
||||
def get_branch_revs(self, location):
|
||||
branches = self._get_all_branch_names(location)
|
||||
branch_revs = {}
|
||||
for line in branches.splitlines():
|
||||
if '(no branch)' in line:
|
||||
continue
|
||||
line = line.split('->')[0].strip()
|
||||
# actual branch case
|
||||
branch = "".join(b for b in line.split() if b != '*')
|
||||
rev = self._get_revision_from_rev_parse(branch, location)
|
||||
branch_revs[branch] = rev.strip()
|
||||
return branch_revs
|
||||
|
||||
def get_src_requirement(self, dist, location, find_tags):
|
||||
repo = self.get_url(location)
|
||||
if not repo.lower().startswith('git:'):
|
||||
repo = 'git+' + 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)
|
||||
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 and
|
||||
branch_revs[current_rev] != 'origin/master'):
|
||||
# It's the head of a branch
|
||||
full_egg_name = '%s-%s' % (
|
||||
egg_project_name,
|
||||
branch_revs[current_rev].replace('origin/', '')
|
||||
)
|
||||
else:
|
||||
full_egg_name = '%s-dev' % egg_project_name
|
||||
|
||||
return '%s@%s#egg=%s' % (repo, current_rev, full_egg_name)
|
||||
|
||||
def get_url_rev(self):
|
||||
"""
|
||||
Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'.
|
||||
That's required because although they use SSH they sometimes doesn't
|
||||
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
|
||||
self.url = self.url.replace('git+', 'git+ssh://')
|
||||
url, rev = super(Git, self).get_url_rev()
|
||||
url = url.replace('ssh://', '')
|
||||
else:
|
||||
url, rev = super(Git, self).get_url_rev()
|
||||
|
||||
return url, rev
|
||||
|
||||
def _get_all_tag_names(self, location):
|
||||
return call_subprocess([self.cmd, 'tag', '-l'],
|
||||
show_stdout=False,
|
||||
raise_on_returncode=False,
|
||||
cwd=location)
|
||||
|
||||
def _get_all_branch_names(self, location):
|
||||
remote_branches = call_subprocess([self.cmd, 'branch', '-r'],
|
||||
show_stdout=False, cwd=location)
|
||||
local_branches = call_subprocess([self.cmd, 'branch', '-l'],
|
||||
show_stdout=False, cwd=location)
|
||||
return remote_branches + local_branches
|
||||
|
||||
def _get_revision_from_rev_parse(self, name, location):
|
||||
return call_subprocess([self.cmd, 'rev-parse', name],
|
||||
show_stdout=False, cwd=location)
|
||||
|
||||
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)
|
||||
|
||||
vcs.register(Git)
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
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.vcs import vcs, VersionControl
|
||||
from pip.download import path_to_url2
|
||||
from pip.backwardcompat import ConfigParser
|
||||
|
||||
|
||||
class Mercurial(VersionControl):
|
||||
name = 'hg'
|
||||
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)
|
||||
finally:
|
||||
rmtree(temp_dir)
|
||||
|
||||
def switch(self, dest, url, rev_options):
|
||||
repo_config = os.path.join(dest, self.dirname, 'hgrc')
|
||||
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))
|
||||
else:
|
||||
call_subprocess([self.cmd, '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)
|
||||
|
||||
def obtain(self, dest):
|
||||
url, rev = self.get_url_rev()
|
||||
if rev:
|
||||
rev_options = [rev]
|
||||
rev_display = ' (to revision %s)' % rev
|
||||
else:
|
||||
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)
|
||||
|
||||
def get_url(self, location):
|
||||
url = call_subprocess(
|
||||
[self.cmd, 'showconfig', 'paths.default'],
|
||||
show_stdout=False, cwd=location).strip()
|
||||
if self._is_local_repository(url):
|
||||
url = path_to_url2(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}'],
|
||||
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}'],
|
||||
show_stdout=False, cwd=location).strip()
|
||||
return current_rev_hash
|
||||
|
||||
def get_src_requirement(self, dist, location, find_tags):
|
||||
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)
|
||||
|
||||
vcs.register(Mercurial)
|
||||
+272
@@ -0,0 +1,272 @@
|
||||
import os
|
||||
import re
|
||||
from pip.backwardcompat import urlparse
|
||||
from pip import InstallationError
|
||||
from pip.index import Link
|
||||
from pip.util import rmtree, display_path, call_subprocess
|
||||
from pip.log import logger
|
||||
from pip.vcs import vcs, VersionControl
|
||||
|
||||
_svn_xml_url_re = re.compile('url="([^"]+)"')
|
||||
_svn_rev_re = re.compile('committed-rev="(\d+)"')
|
||||
_svn_url_re = re.compile(r'URL: (.+)')
|
||||
_svn_revision_re = re.compile(r'Revision: (.+)')
|
||||
_svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"')
|
||||
_svn_info_xml_url_re = re.compile(r'<url>(.*)</url>')
|
||||
|
||||
|
||||
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'})
|
||||
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)
|
||||
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)
|
||||
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:
|
||||
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
|
||||
rmtree(location)
|
||||
call_subprocess(
|
||||
[self.cmd, 'export'] + rev_options + [url, location],
|
||||
filter_stdout=self._filter, show_stdout=False)
|
||||
finally:
|
||||
logger.indent -= 2
|
||||
|
||||
def switch(self, dest, url, rev_options):
|
||||
call_subprocess(
|
||||
[self.cmd, 'switch'] + rev_options + [url, dest])
|
||||
|
||||
def update(self, dest, rev_options):
|
||||
call_subprocess(
|
||||
[self.cmd, 'update'] + rev_options + [dest])
|
||||
|
||||
def obtain(self, dest):
|
||||
url, rev = self.get_url_rev()
|
||||
rev_options = get_rev_options(url, rev)
|
||||
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])
|
||||
|
||||
def get_location(self, dist, dependency_links):
|
||||
for url in dependency_links:
|
||||
egg_fragment = Link(url).egg_fragment
|
||||
if not egg_fragment:
|
||||
continue
|
||||
if '-' in egg_fragment:
|
||||
## FIXME: will this work when a package has - in the name?
|
||||
key = '-'.join(egg_fragment.split('-')[:-1]).lower()
|
||||
else:
|
||||
key = egg_fragment
|
||||
if key == dist.key:
|
||||
return url.split('#', 1)[0]
|
||||
return None
|
||||
|
||||
def get_revision(self, location):
|
||||
"""
|
||||
Return the maximum revision for all files under a given location
|
||||
"""
|
||||
# Note: taken from setuptools.command.egg_info
|
||||
revision = 0
|
||||
|
||||
for base, dirs, files in os.walk(location):
|
||||
if self.dirname not in dirs:
|
||||
dirs[:] = []
|
||||
continue # no sense walking uncontrolled subdirs
|
||||
dirs.remove(self.dirname)
|
||||
entries_fn = os.path.join(base, self.dirname, 'entries')
|
||||
if not os.path.exists(entries_fn):
|
||||
## FIXME: should we warn?
|
||||
continue
|
||||
|
||||
dirurl, localrev = self._get_svn_url_rev(base)
|
||||
|
||||
if base == location:
|
||||
base_url = dirurl + '/' # save the root url
|
||||
elif not dirurl or not dirurl.startswith(base_url):
|
||||
dirs[:] = []
|
||||
continue # not part of the same svn tree, skip it
|
||||
revision = max(revision, localrev)
|
||||
return revision
|
||||
|
||||
def get_url_rev(self):
|
||||
# hotfix the URL scheme after removing svn+ from svn+ssh:// readd it
|
||||
url, rev = super(Subversion, self).get_url_rev()
|
||||
if url.startswith('ssh://'):
|
||||
url = 'svn+' + url
|
||||
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
|
||||
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)
|
||||
return None
|
||||
|
||||
return self._get_svn_url_rev(location)[0]
|
||||
|
||||
def _get_svn_url_rev(self, location):
|
||||
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'):
|
||||
data = list(map(str.splitlines, data.split('\n\x0c\n')))
|
||||
del data[0][0] # get rid of the '8'
|
||||
url = data[0][3]
|
||||
revs = [int(d[9]) for d in data if len(d) > 9 and d[9]] + [0]
|
||||
elif data.startswith('<?xml'):
|
||||
match = _svn_xml_url_re.search(data)
|
||||
if not match:
|
||||
raise ValueError('Badly formatted data: %r' % data)
|
||||
url = match.group(1) # get repository URL
|
||||
revs = [int(m.group(1)) for m in _svn_rev_re.finditer(data)] + [0]
|
||||
else:
|
||||
try:
|
||||
# subversion >= 1.7
|
||||
xml = call_subprocess([self.cmd, '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)]
|
||||
except InstallationError:
|
||||
url, revs = None, []
|
||||
|
||||
if revs:
|
||||
rev = max(revs)
|
||||
else:
|
||||
rev = 0
|
||||
|
||||
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):
|
||||
repo = self.get_url(location)
|
||||
if repo is None:
|
||||
return None
|
||||
parts = repo.split('/')
|
||||
## 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)
|
||||
|
||||
|
||||
def get_rev_options(url, rev):
|
||||
if rev:
|
||||
rev_options = ['-r', rev]
|
||||
else:
|
||||
rev_options = []
|
||||
|
||||
r = urlparse.urlsplit(url)
|
||||
if hasattr(r, 'username'):
|
||||
# >= Python-2.5
|
||||
username, password = r.username, r.password
|
||||
else:
|
||||
netloc = r[1]
|
||||
if '@' in netloc:
|
||||
auth = netloc.split('@')[0]
|
||||
if ':' in auth:
|
||||
username, password = auth.split(':', 1)
|
||||
else:
|
||||
username, password = auth, None
|
||||
else:
|
||||
username, password = None, None
|
||||
|
||||
if username:
|
||||
rev_options += ['--username', username]
|
||||
if password:
|
||||
rev_options += ['--password', password]
|
||||
return rev_options
|
||||
|
||||
|
||||
vcs.register(Subversion)
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
[nosetests]
|
||||
where = tests
|
||||
|
||||
[aliases]
|
||||
dev = develop easy_install pip[testing]
|
||||
|
||||
[egg_info]
|
||||
tag_build =
|
||||
tag_date = 0
|
||||
tag_svn_revision = 0
|
||||
|
||||
Vendored
+61
@@ -0,0 +1,61 @@
|
||||
import codecs
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import textwrap
|
||||
from setuptools import setup
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
def read(*parts):
|
||||
# intentionally *not* adding an encoding option to open
|
||||
# see here: https://github.com/pypa/virtualenv/issues/201#issuecomment-3145690
|
||||
return codecs.open(os.path.join(here, *parts), 'r').read()
|
||||
|
||||
def find_version(*file_paths):
|
||||
version_file = read(*file_paths)
|
||||
version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
|
||||
version_file, re.M)
|
||||
if version_match:
|
||||
return version_match.group(1)
|
||||
raise RuntimeError("Unable to find version string.")
|
||||
|
||||
long_description = "\n" + "\n".join([
|
||||
read('PROJECT.txt'),
|
||||
read('docs', 'quickstart.txt'),
|
||||
read('CHANGES.txt')])
|
||||
|
||||
tests_require = ['nose', 'virtualenv>=1.7', 'scripttest>=1.1.1', '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.5',
|
||||
'Programming Language :: Python :: 2.6',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.1',
|
||||
'Programming Language :: Python :: 3.2',
|
||||
],
|
||||
keywords='easy_install distutils setuptools egg virtualenv',
|
||||
author='The pip developers',
|
||||
author_email='python-virtualenv@groups.google.com',
|
||||
url='http://www.pip-installer.org',
|
||||
license='MIT',
|
||||
packages=['pip', 'pip.commands', 'pip.vcs', 'pip.backwardcompat'],
|
||||
package_data={'pip': ['*.pem']},
|
||||
entry_points=dict(console_scripts=['pip=pip:main', 'pip-%s=pip:main' % sys.version[:3]]),
|
||||
test_suite='nose.collector',
|
||||
tests_require=tests_require,
|
||||
zip_safe=False,
|
||||
extras_require = {
|
||||
'testing':tests_require,
|
||||
},
|
||||
)
|
||||
Reference in New Issue
Block a user