mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
Merge branch 'master' into cache/ephem-wheel-cache
This commit is contained in:
commit
01d97e71f0
|
@ -1,4 +1,4 @@
|
||||||
[run]
|
[run]
|
||||||
branch = True
|
branch = True
|
||||||
omit =
|
omit =
|
||||||
pip/_vendor/*
|
src/pip/_vendor/*
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -19,6 +19,7 @@ docs/build/
|
||||||
|
|
||||||
# Unit test / coverage reports
|
# Unit test / coverage reports
|
||||||
.tox/
|
.tox/
|
||||||
|
htmlcov/
|
||||||
.coverage
|
.coverage
|
||||||
.coverage.*
|
.coverage.*
|
||||||
.cache
|
.cache
|
||||||
|
|
|
@ -3,16 +3,19 @@ include LICENSE.txt
|
||||||
include NEWS.rst
|
include NEWS.rst
|
||||||
include README.rst
|
include README.rst
|
||||||
include pyproject.toml
|
include pyproject.toml
|
||||||
|
|
||||||
include src/pip/_vendor/README.rst
|
include src/pip/_vendor/README.rst
|
||||||
include src/pip/_vendor/vendor.txt
|
include src/pip/_vendor/vendor.txt
|
||||||
|
|
||||||
|
include docs/docutils.conf
|
||||||
|
|
||||||
exclude .coveragerc
|
exclude .coveragerc
|
||||||
exclude .mailmap
|
exclude .mailmap
|
||||||
exclude .travis.yml
|
exclude .travis.yml
|
||||||
exclude .landscape.yml
|
exclude .landscape.yml
|
||||||
exclude src/pip/_vendor/Makefile
|
exclude src/pip/_vendor/Makefile
|
||||||
exclude tox.ini
|
exclude tox.ini
|
||||||
exclude dev-requirements.txt
|
exclude *-requirements.txt
|
||||||
exclude appveyor.yml
|
exclude appveyor.yml
|
||||||
|
|
||||||
recursive-include src/pip/_vendor *.pem
|
recursive-include src/pip/_vendor *.pem
|
||||||
|
|
2
NEWS.rst
2
NEWS.rst
|
@ -769,7 +769,7 @@
|
||||||
than erroring out. (#963)
|
than erroring out. (#963)
|
||||||
- ``pip bundle`` and support for installing from pybundle files is now
|
- ``pip bundle`` and support for installing from pybundle files is now
|
||||||
considered deprecated and will be removed in pip v1.5.
|
considered deprecated and will be removed in pip v1.5.
|
||||||
- Fix a number of isses related to cleaning up and not reusing build
|
- Fix a number of issues related to cleaning up and not reusing build
|
||||||
directories. (#413, #709, #634, #602, #939, #865, #948)
|
directories. (#413, #709, #634, #602, #939, #865, #948)
|
||||||
- Added a User Agent so that pip is identifiable in logs. (#901)
|
- Added a User Agent so that pip is identifiable in logs. (#901)
|
||||||
- Added ssl and --user support to get-pip.py. Thanks Gabriel de Perthuis.
|
- Added ssl and --user support to get-pip.py. Thanks Gabriel de Perthuis.
|
||||||
|
|
33
README.rst
33
README.rst
|
@ -1,9 +1,7 @@
|
||||||
pip
|
pip
|
||||||
===
|
===
|
||||||
|
|
||||||
The `PyPA recommended
|
The `PyPA recommended`_ tool for installing Python packages.
|
||||||
<https://packaging.python.org/en/latest/current/>`_
|
|
||||||
tool for installing Python packages.
|
|
||||||
|
|
||||||
.. image:: https://img.shields.io/pypi/v/pip.svg
|
.. image:: https://img.shields.io/pypi/v/pip.svg
|
||||||
:target: https://pypi.python.org/pypi/pip
|
:target: https://pypi.python.org/pypi/pip
|
||||||
|
@ -14,24 +12,31 @@ tool for installing Python packages.
|
||||||
.. image:: https://img.shields.io/appveyor/ci/pypa/pip.svg
|
.. image:: https://img.shields.io/appveyor/ci/pypa/pip.svg
|
||||||
:target: https://ci.appveyor.com/project/pypa/pip/history
|
:target: https://ci.appveyor.com/project/pypa/pip/history
|
||||||
|
|
||||||
.. image:: https://readthedocs.org/projects/pip/badge/?version=stable
|
.. image:: https://readthedocs.org/projects/pip/badge/?version=latest
|
||||||
:target: https://pip.pypa.io/en/stable
|
:target: https://pip.pypa.io/en/latest
|
||||||
|
|
||||||
* `Installation <https://pip.pypa.io/en/stable/installing.html>`_
|
* `Installation`_
|
||||||
* `Documentation <https://pip.pypa.io/>`_
|
* `Documentation`_
|
||||||
* `Changelog <https://pip.pypa.io/en/stable/news.html>`_
|
* `Changelog`_
|
||||||
* `GitHub Page <https://github.com/pypa/pip>`_
|
* `GitHub Page`_
|
||||||
* `Issue Tracking <https://github.com/pypa/pip/issues>`_
|
* `Issue Tracking`_
|
||||||
* `User mailing list <http://groups.google.com/group/python-virtualenv>`_
|
* `User mailing list`_
|
||||||
* `Dev mailing list <http://groups.google.com/group/pypa-dev>`_
|
* `Dev mailing list`_
|
||||||
* User IRC: #pypa on Freenode.
|
* User IRC: #pypa on Freenode.
|
||||||
* Dev IRC: #pypa-dev on Freenode.
|
* Dev IRC: #pypa-dev on Freenode.
|
||||||
|
|
||||||
|
|
||||||
Code of Conduct
|
Code of Conduct
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Everyone interacting in the pip project's codebases, issue trackers, chat
|
Everyone interacting in the pip project's codebases, issue trackers, chat
|
||||||
rooms, and mailing lists is expected to follow the `PyPA Code of Conduct`_.
|
rooms and mailing lists is expected to follow the `PyPA Code of Conduct`_.
|
||||||
|
|
||||||
|
.. _PyPA recommended: https://packaging.python.org/en/latest/current/
|
||||||
|
.. _Installation: https://pip.pypa.io/en/stable/installing.html
|
||||||
|
.. _Documentation: https://pip.pypa.io/en/stable/
|
||||||
|
.. _Changelog: https://pip.pypa.io/en/stable/news.html
|
||||||
|
.. _GitHub Page: https://github.com/pypa/pip
|
||||||
|
.. _Issue Tracking: https://github.com/pypa/pip/issues
|
||||||
|
.. _User mailing list: http://groups.google.com/group/python-virtualenv
|
||||||
|
.. _Dev mailing list: http://groups.google.com/group/pypa-dev
|
||||||
.. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/
|
.. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/
|
||||||
|
|
|
@ -2,6 +2,7 @@ freezegun
|
||||||
pretend
|
pretend
|
||||||
pytest
|
pytest
|
||||||
pytest-catchlog
|
pytest-catchlog
|
||||||
|
pytest-cov
|
||||||
pytest-rerunfailures
|
pytest-rerunfailures
|
||||||
pytest-timeout
|
pytest-timeout
|
||||||
pytest-xdist
|
pytest-xdist
|
||||||
|
|
7
docs-requirements.txt
Normal file
7
docs-requirements.txt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
sphinx == 1.6.*
|
||||||
|
git+https://github.com/python/python-docs-theme.git#egg=python-docs-theme
|
||||||
|
git+https://github.com/pypa/pypa-docs-theme.git#egg=pypa-docs-theme
|
||||||
|
|
||||||
|
# XXX: This is a workaround for conf.py not seeing the development pip version
|
||||||
|
# when the documentation is built on ReadTheDocs.
|
||||||
|
.
|
|
@ -68,6 +68,11 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
version = release = 'dev'
|
version = release = 'dev'
|
||||||
|
|
||||||
|
# We have this here because readthedocs plays tricks sometimes and there seems
|
||||||
|
# to be a hiesenbug, related to the version of pip discovered. This is here to
|
||||||
|
# help debug that if someone decides to do that in the future.
|
||||||
|
print(version)
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
# language = None
|
# language = None
|
||||||
|
@ -122,7 +127,7 @@ html_theme = "pypa_theme"
|
||||||
html_theme_options = {
|
html_theme_options = {
|
||||||
'collapsiblesidebar': True,
|
'collapsiblesidebar': True,
|
||||||
'externalrefs': True,
|
'externalrefs': True,
|
||||||
'navigation_depth': 2,
|
'navigation_depth': 3,
|
||||||
'issues_url': 'https://github.com/pypa/pip/issues'
|
'issues_url': 'https://github.com/pypa/pip/issues'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
docs/docutils.conf
Normal file
2
docs/docutils.conf
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[restructuredtext parser]
|
||||||
|
smart_quotes = no
|
|
@ -2,13 +2,14 @@
|
||||||
|
|
||||||
import optparse
|
import optparse
|
||||||
import sys
|
import sys
|
||||||
|
from textwrap import dedent
|
||||||
|
|
||||||
from docutils import nodes
|
from docutils import nodes
|
||||||
from docutils.parsers import rst
|
from docutils.parsers import rst
|
||||||
from docutils.statemachine import ViewList
|
from docutils.statemachine import ViewList
|
||||||
from textwrap import dedent
|
|
||||||
from pip._internal import cmdoptions
|
from pip._internal import cmdoptions
|
||||||
from pip._internal.commands import commands_dict as commands
|
from pip._internal.commands import commands_dict as commands
|
||||||
from pip._internal.utils.misc import get_prog
|
|
||||||
|
|
||||||
|
|
||||||
class PipCommandUsage(rst.Directive):
|
class PipCommandUsage(rst.Directive):
|
||||||
|
@ -16,8 +17,7 @@ class PipCommandUsage(rst.Directive):
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
cmd = commands[self.arguments[0]]
|
cmd = commands[self.arguments[0]]
|
||||||
prog = '%s %s' % (get_prog(), cmd.name)
|
usage = dedent(cmd.usage.replace('%prog', 'pip')).strip()
|
||||||
usage = dedent(cmd.usage.replace('%prog', prog)).strip()
|
|
||||||
node = nodes.literal_block(usage, usage)
|
node = nodes.literal_block(usage, usage)
|
||||||
return [node]
|
return [node]
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,8 @@ Install a package from `PyPI`_:
|
||||||
::
|
::
|
||||||
|
|
||||||
$ pip install SomePackage
|
$ pip install SomePackage
|
||||||
[...]
|
[...]
|
||||||
Successfully installed SomePackage
|
Successfully installed SomePackage
|
||||||
|
|
||||||
Install a package already downloaded from `PyPI`_ or got elsewhere.
|
Install a package already downloaded from `PyPI`_ or got elsewhere.
|
||||||
This is useful if the target machine does not have a network connection:
|
This is useful if the target machine does not have a network connection:
|
||||||
|
@ -17,49 +17,49 @@ This is useful if the target machine does not have a network connection:
|
||||||
::
|
::
|
||||||
|
|
||||||
$ pip install SomePackage-1.0-py2.py3-none-any.whl
|
$ pip install SomePackage-1.0-py2.py3-none-any.whl
|
||||||
[...]
|
[...]
|
||||||
Successfully installed SomePackage
|
Successfully installed SomePackage
|
||||||
|
|
||||||
Show what files were installed:
|
Show what files were installed:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
$ pip show --files SomePackage
|
$ pip show --files SomePackage
|
||||||
Name: SomePackage
|
Name: SomePackage
|
||||||
Version: 1.0
|
Version: 1.0
|
||||||
Location: /my/env/lib/pythonx.x/site-packages
|
Location: /my/env/lib/pythonx.x/site-packages
|
||||||
Files:
|
Files:
|
||||||
../somepackage/__init__.py
|
../somepackage/__init__.py
|
||||||
[...]
|
[...]
|
||||||
|
|
||||||
List what packages are outdated:
|
List what packages are outdated:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
$ pip list --outdated
|
$ pip list --outdated
|
||||||
SomePackage (Current: 1.0 Latest: 2.0)
|
SomePackage (Current: 1.0 Latest: 2.0)
|
||||||
|
|
||||||
Upgrade a package:
|
Upgrade a package:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
$ pip install --upgrade SomePackage
|
$ pip install --upgrade SomePackage
|
||||||
[...]
|
[...]
|
||||||
Found existing installation: SomePackage 1.0
|
Found existing installation: SomePackage 1.0
|
||||||
Uninstalling SomePackage:
|
Uninstalling SomePackage:
|
||||||
Successfully uninstalled SomePackage
|
Successfully uninstalled SomePackage
|
||||||
Running setup.py install for SomePackage
|
Running setup.py install for SomePackage
|
||||||
Successfully installed SomePackage
|
Successfully installed SomePackage
|
||||||
|
|
||||||
Uninstall a package:
|
Uninstall a package:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
$ pip uninstall SomePackage
|
$ pip uninstall SomePackage
|
||||||
Uninstalling SomePackage:
|
Uninstalling SomePackage:
|
||||||
/my/env/lib/pythonx.x/site-packages/somepackage
|
/my/env/lib/pythonx.x/site-packages/somepackage
|
||||||
Proceed (y/n)? y
|
Proceed (y/n)? y
|
||||||
Successfully uninstalled SomePackage
|
Successfully uninstalled SomePackage
|
||||||
|
|
||||||
|
|
||||||
.. _PyPI: http://pypi.python.org/pypi/
|
.. _PyPI: http://pypi.python.org/pypi/
|
||||||
|
|
|
@ -3,7 +3,7 @@ Reference Guide
|
||||||
===============
|
===============
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 2
|
||||||
|
|
||||||
pip
|
pip
|
||||||
pip_install
|
pip_install
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
pip
|
pip
|
||||||
---
|
---
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
*****
|
*****
|
||||||
|
|
||||||
|
@ -22,7 +24,9 @@ Console logging
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
pip offers :ref:`-v, --verbose <--verbose>` and :ref:`-q, --quiet <--quiet>`
|
pip offers :ref:`-v, --verbose <--verbose>` and :ref:`-q, --quiet <--quiet>`
|
||||||
to control the console log level.
|
to control the console log level. By default, some messages (error and warnings)
|
||||||
|
are colored in the terminal. If you want to suppress the colored output use
|
||||||
|
:ref:`--no-color <--no-color>`.
|
||||||
|
|
||||||
|
|
||||||
.. _`FileLogging`:
|
.. _`FileLogging`:
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
pip check
|
pip check
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
*****
|
*****
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
pip config
|
pip config
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
*****
|
*****
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
pip download
|
pip download
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
*****
|
*****
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
pip freeze
|
pip freeze
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
*****
|
*****
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
pip hash
|
pip hash
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
*****
|
*****
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
pip install
|
pip install
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
*****
|
*****
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
pip list
|
pip list
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
*****
|
*****
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
pip search
|
pip search
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
*****
|
*****
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
pip show
|
pip show
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
*****
|
*****
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
pip uninstall
|
pip uninstall
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
*****
|
*****
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
pip wheel
|
pip wheel
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
*****
|
*****
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
User Guide
|
User Guide
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
Running pip
|
Running pip
|
||||||
***********
|
***********
|
||||||
|
|
||||||
|
@ -728,7 +730,7 @@ change without notice. While we do try not to break things as much as possible,
|
||||||
the internal APIs can change at any time, for any reason. It also means that we
|
the internal APIs can change at any time, for any reason. It also means that we
|
||||||
generally *won't* fix issues that are a result of using pip in an unsupported way.
|
generally *won't* fix issues that are a result of using pip in an unsupported way.
|
||||||
|
|
||||||
It should also be noted that modifying the contents of ``sys.path`` in a running Python
|
It should also be noted that installing packages into ``sys.path`` in a running Python
|
||||||
process is something that should only be done with care. The import system caches
|
process is something that should only be done with care. The import system caches
|
||||||
certain data, and installing new packages while a program is running may not always
|
certain data, and installing new packages while a program is running may not always
|
||||||
behave as expected. In practice, there is rarely an issue, but it is something to be
|
behave as expected. In practice, there is rarely an issue, but it is something to be
|
||||||
|
@ -756,7 +758,5 @@ of ability. Some examples that you could consider include:
|
||||||
* ``setuptools`` (specifically ``pkg_resources``) - Functions for querying what
|
* ``setuptools`` (specifically ``pkg_resources``) - Functions for querying what
|
||||||
packages the user has installed on their system.
|
packages the user has installed on their system.
|
||||||
|
|
||||||
* ``wheel`` - Code for manipulating (creating, querying and installing) wheels.
|
|
||||||
|
|
||||||
* ``distlib`` - Packaging and distribution utilities (including functions for
|
* ``distlib`` - Packaging and distribution utilities (including functions for
|
||||||
interacting with PyPI).
|
interacting with PyPI).
|
||||||
|
|
0
news/09f5c22f-ec40-420d-9647-da985cef8050.trivial
Normal file
0
news/09f5c22f-ec40-420d-9647-da985cef8050.trivial
Normal file
2
news/2449.feature
Normal file
2
news/2449.feature
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Add `--no-color` to `pip`. All colored output is disabled
|
||||||
|
if this flag is detected.
|
1
news/3016.feature
Normal file
1
news/3016.feature
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pip uninstall now ignores the absence of a requirement and prints a warning.
|
1
news/3741.bugfix
Normal file
1
news/3741.bugfix
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Fix ``pip uninstall`` when ``easy-install.pth`` lacks a trailing newline.
|
5
news/4293.bugfix
Normal file
5
news/4293.bugfix
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
Fix for an incorrect ``freeze`` warning message due to a package being
|
||||||
|
included in multiple requirements files that were passed to ``freeze``.
|
||||||
|
Instead of warning incorrectly that the package is not installed, pip
|
||||||
|
now warns that the package was declared multiple times and lists the
|
||||||
|
name of each requirements file that contains the package in question.
|
1
news/4642.feature
Normal file
1
news/4642.feature
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pip uninstall now ignores the absence of a requirement and prints a warning.
|
2
news/4667.bugfix
Normal file
2
news/4667.bugfix
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pip now records installed files in a deterministic manner improving
|
||||||
|
reproducibility.
|
0
news/bc8c3a60-bf80-4c07-9cce-96cb6735d8cb.trivial
Normal file
0
news/bc8c3a60-bf80-4c07-9cce-96cb6735d8cb.trivial
Normal file
|
@ -1 +1 @@
|
||||||
Upgraded distlib to 0.2.5.
|
Upgraded distlib to 0.2.6.
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Upgraded pkg_resources (via setuptools) to 36.4.0.
|
Upgraded pkg_resources (via setuptools) to 36.6.0.
|
||||||
|
|
1
news/six.vendor
Normal file
1
news/six.vendor
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Upgraded six to 1.11.0.
|
|
@ -132,6 +132,11 @@ class Command(object):
|
||||||
if options.log:
|
if options.log:
|
||||||
root_level = "DEBUG"
|
root_level = "DEBUG"
|
||||||
|
|
||||||
|
if options.no_color:
|
||||||
|
logger_class = "logging.StreamHandler"
|
||||||
|
else:
|
||||||
|
logger_class = "pip._internal.utils.logging.ColorizedStreamHandler"
|
||||||
|
|
||||||
logging.config.dictConfig({
|
logging.config.dictConfig({
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"disable_existing_loggers": False,
|
"disable_existing_loggers": False,
|
||||||
|
@ -150,16 +155,14 @@ class Command(object):
|
||||||
"handlers": {
|
"handlers": {
|
||||||
"console": {
|
"console": {
|
||||||
"level": level,
|
"level": level,
|
||||||
"class":
|
"class": logger_class,
|
||||||
"pip._internal.utils.logging.ColorizedStreamHandler",
|
|
||||||
"stream": self.log_streams[0],
|
"stream": self.log_streams[0],
|
||||||
"filters": ["exclude_warnings"],
|
"filters": ["exclude_warnings"],
|
||||||
"formatter": "indent",
|
"formatter": "indent",
|
||||||
},
|
},
|
||||||
"console_errors": {
|
"console_errors": {
|
||||||
"level": "WARNING",
|
"level": "WARNING",
|
||||||
"class":
|
"class": logger_class,
|
||||||
"pip._internal.utils.logging.ColorizedStreamHandler",
|
|
||||||
"stream": self.log_streams[1],
|
"stream": self.log_streams[1],
|
||||||
"formatter": "indent",
|
"formatter": "indent",
|
||||||
},
|
},
|
||||||
|
|
|
@ -102,6 +102,15 @@ verbose = partial(
|
||||||
help='Give more output. Option is additive, and can be used up to 3 times.'
|
help='Give more output. Option is additive, and can be used up to 3 times.'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
no_color = partial(
|
||||||
|
Option,
|
||||||
|
'--no-color',
|
||||||
|
dest='no_color',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help="Suppress colored output",
|
||||||
|
)
|
||||||
|
|
||||||
version = partial(
|
version = partial(
|
||||||
Option,
|
Option,
|
||||||
'-V', '--version',
|
'-V', '--version',
|
||||||
|
@ -566,6 +575,7 @@ general_group = {
|
||||||
cache_dir,
|
cache_dir,
|
||||||
no_cache,
|
no_cache,
|
||||||
disable_pip_version_check,
|
disable_pip_version_check,
|
||||||
|
no_color,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -277,22 +277,20 @@ class InstallCommand(RequirementCommand):
|
||||||
)
|
)
|
||||||
resolver.resolve(requirement_set)
|
resolver.resolve(requirement_set)
|
||||||
|
|
||||||
# on -d don't do complex things like building
|
# If caching is disabled or wheel is not installed don't
|
||||||
# wheels, and don't try to build wheels when wheel is
|
# try to build wheels.
|
||||||
# not installed.
|
|
||||||
if wheel and options.cache_dir:
|
if wheel and options.cache_dir:
|
||||||
# build wheels before install.
|
# build wheels before install.
|
||||||
wb = WheelBuilder(
|
wb = WheelBuilder(
|
||||||
requirement_set,
|
finder, preparer, wheel_cache,
|
||||||
finder,
|
build_options=[], global_options=[],
|
||||||
preparer,
|
|
||||||
wheel_cache,
|
|
||||||
build_options=[],
|
|
||||||
global_options=[],
|
|
||||||
)
|
)
|
||||||
# Ignore the result: a failed wheel will be
|
# Ignore the result: a failed wheel will be
|
||||||
# installed from the sdist/vcs whatever.
|
# installed from the sdist/vcs whatever.
|
||||||
wb.build(session=session, autobuilding=True)
|
wb.build(
|
||||||
|
requirement_set.requirements.values(),
|
||||||
|
session=session, autobuilding=True
|
||||||
|
)
|
||||||
|
|
||||||
installed = requirement_set.install(
|
installed = requirement_set.install(
|
||||||
install_options,
|
install_options,
|
||||||
|
|
|
@ -64,7 +64,8 @@ class UninstallCommand(Command):
|
||||||
'"pip help %(name)s")' % dict(name=self.name)
|
'"pip help %(name)s")' % dict(name=self.name)
|
||||||
)
|
)
|
||||||
for req in reqs_to_uninstall.values():
|
for req in reqs_to_uninstall.values():
|
||||||
req.uninstall(
|
uninstall_pathset = req.uninstall(
|
||||||
auto_confirm=options.yes, verbose=options.verbose != 0
|
auto_confirm=options.yes, verbose=options.verbose != 0
|
||||||
)
|
)
|
||||||
req.uninstalled_pathset.commit()
|
if uninstall_pathset:
|
||||||
|
uninstall_pathset.commit()
|
||||||
|
|
|
@ -177,15 +177,14 @@ class WheelCommand(RequirementCommand):
|
||||||
|
|
||||||
# build wheels
|
# build wheels
|
||||||
wb = WheelBuilder(
|
wb = WheelBuilder(
|
||||||
requirement_set,
|
finder, preparer, wheel_cache,
|
||||||
finder,
|
|
||||||
preparer,
|
|
||||||
wheel_cache,
|
|
||||||
build_options=options.build_options or [],
|
build_options=options.build_options or [],
|
||||||
global_options=options.global_options or [],
|
global_options=options.global_options or [],
|
||||||
no_clean=options.no_clean,
|
no_clean=options.no_clean,
|
||||||
)
|
)
|
||||||
wheels_built_successfully = wb.build(session=session)
|
wheels_built_successfully = wb.build(
|
||||||
|
requirement_set.requirements.values(), session=session,
|
||||||
|
)
|
||||||
if not wheels_built_successfully:
|
if not wheels_built_successfully:
|
||||||
raise CommandError(
|
raise CommandError(
|
||||||
"Failed to build one or more wheels"
|
"Failed to build one or more wheels"
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
import collections
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pip._vendor import pkg_resources
|
from pip._vendor import pkg_resources, six
|
||||||
from pip._vendor.packaging.utils import canonicalize_name
|
from pip._vendor.packaging.utils import canonicalize_name
|
||||||
from pip._vendor.pkg_resources import RequirementParseError
|
from pip._vendor.pkg_resources import RequirementParseError
|
||||||
|
|
||||||
|
@ -70,6 +71,9 @@ def freeze(
|
||||||
# requirements files, so we need to keep track of what has been emitted
|
# requirements files, so we need to keep track of what has been emitted
|
||||||
# so that we don't emit it again if it's seen again
|
# so that we don't emit it again if it's seen again
|
||||||
emitted_options = set()
|
emitted_options = set()
|
||||||
|
# keep track of which files a requirement is in so that we can
|
||||||
|
# give an accurate warning if a requirement appears multiple times.
|
||||||
|
req_files = collections.defaultdict(list)
|
||||||
for req_file_path in requirement:
|
for req_file_path in requirement:
|
||||||
with open(req_file_path) as req_file:
|
with open(req_file_path) as req_file:
|
||||||
for line in req_file:
|
for line in req_file:
|
||||||
|
@ -119,14 +123,28 @@ def freeze(
|
||||||
" this warning)"
|
" this warning)"
|
||||||
)
|
)
|
||||||
elif line_req.name not in installations:
|
elif line_req.name not in installations:
|
||||||
logger.warning(
|
# either it's not installed, or it is installed
|
||||||
"Requirement file [%s] contains %s, but that "
|
# but has been processed already
|
||||||
"package is not installed",
|
if not req_files[line_req.name]:
|
||||||
req_file_path, COMMENT_RE.sub('', line).strip(),
|
logger.warning(
|
||||||
)
|
"Requirement file [%s] contains %s, but that "
|
||||||
|
"package is not installed",
|
||||||
|
req_file_path,
|
||||||
|
COMMENT_RE.sub('', line).strip(),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
req_files[line_req.name].append(req_file_path)
|
||||||
else:
|
else:
|
||||||
yield str(installations[line_req.name]).rstrip()
|
yield str(installations[line_req.name]).rstrip()
|
||||||
del installations[line_req.name]
|
del installations[line_req.name]
|
||||||
|
req_files[line_req.name].append(req_file_path)
|
||||||
|
|
||||||
|
# Warn about requirements that were included multiple times (in a
|
||||||
|
# single requirements file or in different requirements files).
|
||||||
|
for name, files in six.iteritems(req_files):
|
||||||
|
if len(files) > 1:
|
||||||
|
logger.warning("Requirement %s included multiple times [%s]",
|
||||||
|
name, ', '.join(sorted(set(files))))
|
||||||
|
|
||||||
yield(
|
yield(
|
||||||
'## The following requirements were added by '
|
'## The following requirements were added by '
|
||||||
|
|
|
@ -648,13 +648,13 @@ class InstallRequirement(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not self.check_if_exists():
|
if not self.check_if_exists():
|
||||||
raise UninstallationError(
|
logger.warning("Skipping %s as it is not installed.", self.name)
|
||||||
"Cannot uninstall requirement %s, not installed" % (self.name,)
|
return
|
||||||
)
|
|
||||||
dist = self.satisfied_by or self.conflicts_with
|
dist = self.satisfied_by or self.conflicts_with
|
||||||
|
|
||||||
self.uninstalled_pathset = UninstallPathSet.from_dist(dist)
|
uninstalled_pathset = UninstallPathSet.from_dist(dist)
|
||||||
self.uninstalled_pathset.remove(auto_confirm, verbose)
|
uninstalled_pathset.remove(auto_confirm, verbose)
|
||||||
|
return uninstalled_pathset
|
||||||
|
|
||||||
def archive(self, build_dir):
|
def archive(self, build_dir):
|
||||||
assert self.source_dir
|
assert self.source_dir
|
||||||
|
@ -802,6 +802,7 @@ class InstallRequirement(object):
|
||||||
new_lines.append(
|
new_lines.append(
|
||||||
os.path.relpath(prepend_root(filename), egg_info_dir)
|
os.path.relpath(prepend_root(filename), egg_info_dir)
|
||||||
)
|
)
|
||||||
|
new_lines.sort()
|
||||||
ensure_dir(egg_info_dir)
|
ensure_dir(egg_info_dir)
|
||||||
inst_files_path = os.path.join(egg_info_dir, 'installed-files.txt')
|
inst_files_path = os.path.join(egg_info_dir, 'installed-files.txt')
|
||||||
with open(inst_files_path, 'w') as f:
|
with open(inst_files_path, 'w') as f:
|
||||||
|
|
|
@ -216,7 +216,9 @@ class RequirementSet(object):
|
||||||
requirement.conflicts_with,
|
requirement.conflicts_with,
|
||||||
)
|
)
|
||||||
with indent_log():
|
with indent_log():
|
||||||
requirement.uninstall(auto_confirm=True)
|
uninstalled_pathset = requirement.uninstall(
|
||||||
|
auto_confirm=True
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
requirement.install(
|
requirement.install(
|
||||||
install_options,
|
install_options,
|
||||||
|
@ -231,7 +233,7 @@ class RequirementSet(object):
|
||||||
)
|
)
|
||||||
# if install did not succeed, rollback previous uninstall
|
# if install did not succeed, rollback previous uninstall
|
||||||
if should_rollback:
|
if should_rollback:
|
||||||
requirement.uninstalled_pathset.rollback()
|
uninstalled_pathset.rollback()
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
should_commit = (
|
should_commit = (
|
||||||
|
@ -239,7 +241,7 @@ class RequirementSet(object):
|
||||||
requirement.install_succeeded
|
requirement.install_succeeded
|
||||||
)
|
)
|
||||||
if should_commit:
|
if should_commit:
|
||||||
requirement.uninstalled_pathset.commit()
|
uninstalled_pathset.commit()
|
||||||
requirement.remove_temporary_source()
|
requirement.remove_temporary_source()
|
||||||
|
|
||||||
return to_install
|
return to_install
|
||||||
|
|
|
@ -430,6 +430,9 @@ class UninstallPthEntries(object):
|
||||||
endline = '\r\n'
|
endline = '\r\n'
|
||||||
else:
|
else:
|
||||||
endline = '\n'
|
endline = '\n'
|
||||||
|
# handle missing trailing newline
|
||||||
|
if lines and not lines[-1].endswith(endline.encode("utf-8")):
|
||||||
|
lines[-1] = lines[-1] + endline.encode("utf-8")
|
||||||
for entry in self.entries:
|
for entry in self.entries:
|
||||||
try:
|
try:
|
||||||
logger.debug('Removing entry: %s', entry)
|
logger.debug('Removing entry: %s', entry)
|
||||||
|
|
|
@ -657,9 +657,8 @@ class BuildEnvironment(object):
|
||||||
class WheelBuilder(object):
|
class WheelBuilder(object):
|
||||||
"""Build wheels from a RequirementSet."""
|
"""Build wheels from a RequirementSet."""
|
||||||
|
|
||||||
def __init__(self, requirement_set, finder, preparer, wheel_cache,
|
def __init__(self, finder, preparer, wheel_cache,
|
||||||
build_options=None, global_options=None, no_clean=False):
|
build_options=None, global_options=None, no_clean=False):
|
||||||
self.requirement_set = requirement_set
|
|
||||||
self.finder = finder
|
self.finder = finder
|
||||||
self.preparer = preparer
|
self.preparer = preparer
|
||||||
self.wheel_cache = wheel_cache
|
self.wheel_cache = wheel_cache
|
||||||
|
@ -791,7 +790,7 @@ class WheelBuilder(object):
|
||||||
logger.error('Failed cleaning build dir for %s', req.name)
|
logger.error('Failed cleaning build dir for %s', req.name)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def build(self, session, autobuilding=False):
|
def build(self, requirements, session, autobuilding=False):
|
||||||
"""Build wheels.
|
"""Build wheels.
|
||||||
|
|
||||||
:param unpack: If True, replace the sdist we built from with the
|
:param unpack: If True, replace the sdist we built from with the
|
||||||
|
@ -805,10 +804,8 @@ class WheelBuilder(object):
|
||||||
)
|
)
|
||||||
assert building_is_possible
|
assert building_is_possible
|
||||||
|
|
||||||
reqset = self.requirement_set.requirements.values()
|
|
||||||
|
|
||||||
buildset = []
|
buildset = []
|
||||||
for req in reqset:
|
for req in requirements:
|
||||||
ephem_cache = False
|
ephem_cache = False
|
||||||
if req.constraint:
|
if req.constraint:
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2012-2016 Vinay Sajip.
|
# Copyright (C) 2012-2017 Vinay Sajip.
|
||||||
# Licensed to the Python Software Foundation under a contributor agreement.
|
# Licensed to the Python Software Foundation under a contributor agreement.
|
||||||
# See LICENSE.txt and CONTRIBUTORS.txt.
|
# See LICENSE.txt and CONTRIBUTORS.txt.
|
||||||
#
|
#
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
__version__ = '0.2.5'
|
__version__ = '0.2.6'
|
||||||
|
|
||||||
class DistlibException(Exception):
|
class DistlibException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2013-2016 Vinay Sajip.
|
# Copyright (C) 2013-2017 Vinay Sajip.
|
||||||
# Licensed to the Python Software Foundation under a contributor agreement.
|
# Licensed to the Python Software Foundation under a contributor agreement.
|
||||||
# See LICENSE.txt and CONTRIBUTORS.txt.
|
# See LICENSE.txt and CONTRIBUTORS.txt.
|
||||||
#
|
#
|
||||||
|
@ -12,7 +12,7 @@ import sys
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import ssl
|
import ssl
|
||||||
except ImportError:
|
except ImportError: # pragma: no cover
|
||||||
ssl = None
|
ssl = None
|
||||||
|
|
||||||
if sys.version_info[0] < 3: # pragma: no cover
|
if sys.version_info[0] < 3: # pragma: no cover
|
||||||
|
@ -272,7 +272,7 @@ from zipfile import ZipFile as BaseZipFile
|
||||||
|
|
||||||
if hasattr(BaseZipFile, '__enter__'): # pragma: no cover
|
if hasattr(BaseZipFile, '__enter__'): # pragma: no cover
|
||||||
ZipFile = BaseZipFile
|
ZipFile = BaseZipFile
|
||||||
else:
|
else: # pragma: no cover
|
||||||
from zipfile import ZipExtFile as BaseZipExtFile
|
from zipfile import ZipExtFile as BaseZipExtFile
|
||||||
|
|
||||||
class ZipExtFile(BaseZipExtFile):
|
class ZipExtFile(BaseZipExtFile):
|
||||||
|
@ -329,7 +329,13 @@ try:
|
||||||
fsencode = os.fsencode
|
fsencode = os.fsencode
|
||||||
fsdecode = os.fsdecode
|
fsdecode = os.fsdecode
|
||||||
except AttributeError: # pragma: no cover
|
except AttributeError: # pragma: no cover
|
||||||
_fsencoding = sys.getfilesystemencoding()
|
# Issue #99: on some systems (e.g. containerised),
|
||||||
|
# sys.getfilesystemencoding() returns None, and we need a real value,
|
||||||
|
# so fall back to utf-8. From the CPython 2.7 docs relating to Unix and
|
||||||
|
# sys.getfilesystemencoding(): the return value is "the user’s preference
|
||||||
|
# according to the result of nl_langinfo(CODESET), or None if the
|
||||||
|
# nl_langinfo(CODESET) failed."
|
||||||
|
_fsencoding = sys.getfilesystemencoding() or 'utf-8'
|
||||||
if _fsencoding == 'mbcs':
|
if _fsencoding == 'mbcs':
|
||||||
_fserrors = 'strict'
|
_fserrors = 'strict'
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2012-2016 The Python Software Foundation.
|
# Copyright (C) 2012-2017 The Python Software Foundation.
|
||||||
# See LICENSE.txt and CONTRIBUTORS.txt.
|
# See LICENSE.txt and CONTRIBUTORS.txt.
|
||||||
#
|
#
|
||||||
"""PEP 376 implementation."""
|
"""PEP 376 implementation."""
|
||||||
|
@ -265,18 +265,23 @@ class DistributionPath(object):
|
||||||
(name, version))
|
(name, version))
|
||||||
|
|
||||||
for dist in self.get_distributions():
|
for dist in self.get_distributions():
|
||||||
provided = dist.provides
|
# We hit a problem on Travis where enum34 was installed and doesn't
|
||||||
|
# have a provides attribute ...
|
||||||
|
if not hasattr(dist, 'provides'):
|
||||||
|
logger.debug('No "provides": %s', dist)
|
||||||
|
else:
|
||||||
|
provided = dist.provides
|
||||||
|
|
||||||
for p in provided:
|
for p in provided:
|
||||||
p_name, p_ver = parse_name_and_version(p)
|
p_name, p_ver = parse_name_and_version(p)
|
||||||
if matcher is None:
|
if matcher is None:
|
||||||
if p_name == name:
|
if p_name == name:
|
||||||
yield dist
|
yield dist
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
if p_name == name and matcher.match(p_ver):
|
if p_name == name and matcher.match(p_ver):
|
||||||
yield dist
|
yield dist
|
||||||
break
|
break
|
||||||
|
|
||||||
def get_file_path(self, name, relative_path):
|
def get_file_path(self, name, relative_path):
|
||||||
"""
|
"""
|
||||||
|
@ -1025,20 +1030,21 @@ class EggInfoDistribution(BaseInstalledDistribution):
|
||||||
:returns: iterator of paths
|
:returns: iterator of paths
|
||||||
"""
|
"""
|
||||||
record_path = os.path.join(self.path, 'installed-files.txt')
|
record_path = os.path.join(self.path, 'installed-files.txt')
|
||||||
skip = True
|
if os.path.exists(record_path):
|
||||||
with codecs.open(record_path, 'r', encoding='utf-8') as f:
|
skip = True
|
||||||
for line in f:
|
with codecs.open(record_path, 'r', encoding='utf-8') as f:
|
||||||
line = line.strip()
|
for line in f:
|
||||||
if line == './':
|
line = line.strip()
|
||||||
skip = False
|
if line == './':
|
||||||
continue
|
skip = False
|
||||||
if not skip:
|
continue
|
||||||
p = os.path.normpath(os.path.join(self.path, line))
|
if not skip:
|
||||||
if p.startswith(self.path):
|
p = os.path.normpath(os.path.join(self.path, line))
|
||||||
if absolute:
|
if p.startswith(self.path):
|
||||||
yield p
|
if absolute:
|
||||||
else:
|
yield p
|
||||||
yield line
|
else:
|
||||||
|
yield line
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return (isinstance(other, EggInfoDistribution) and
|
return (isinstance(other, EggInfoDistribution) and
|
||||||
|
|
|
@ -24,7 +24,7 @@ from .compat import (urljoin, urlparse, urlunparse, url2pathname, pathname2url,
|
||||||
HTTPRedirectHandler as BaseRedirectHandler, text_type,
|
HTTPRedirectHandler as BaseRedirectHandler, text_type,
|
||||||
Request, HTTPError, URLError)
|
Request, HTTPError, URLError)
|
||||||
from .database import Distribution, DistributionPath, make_dist
|
from .database import Distribution, DistributionPath, make_dist
|
||||||
from .metadata import Metadata
|
from .metadata import Metadata, MetadataInvalidError
|
||||||
from .util import (cached_property, parse_credentials, ensure_slash,
|
from .util import (cached_property, parse_credentials, ensure_slash,
|
||||||
split_filename, get_project_data, parse_requirement,
|
split_filename, get_project_data, parse_requirement,
|
||||||
parse_name_and_version, ServerProxy, normalize_name)
|
parse_name_and_version, ServerProxy, normalize_name)
|
||||||
|
@ -69,7 +69,7 @@ class RedirectHandler(BaseRedirectHandler):
|
||||||
if key in headers:
|
if key in headers:
|
||||||
newurl = headers[key]
|
newurl = headers[key]
|
||||||
break
|
break
|
||||||
if newurl is None:
|
if newurl is None: # pragma: no cover
|
||||||
return
|
return
|
||||||
urlparts = urlparse(newurl)
|
urlparts = urlparse(newurl)
|
||||||
if urlparts.scheme == '':
|
if urlparts.scheme == '':
|
||||||
|
@ -175,7 +175,7 @@ class Locator(object):
|
||||||
|
|
||||||
This calls _get_project to do all the work, and just implements a caching layer on top.
|
This calls _get_project to do all the work, and just implements a caching layer on top.
|
||||||
"""
|
"""
|
||||||
if self._cache is None:
|
if self._cache is None: # pragma: no cover
|
||||||
result = self._get_project(name)
|
result = self._get_project(name)
|
||||||
elif name in self._cache:
|
elif name in self._cache:
|
||||||
result = self._cache[name]
|
result = self._cache[name]
|
||||||
|
@ -241,7 +241,7 @@ class Locator(object):
|
||||||
|
|
||||||
result = None
|
result = None
|
||||||
scheme, netloc, path, params, query, frag = urlparse(url)
|
scheme, netloc, path, params, query, frag = urlparse(url)
|
||||||
if frag.lower().startswith('egg='):
|
if frag.lower().startswith('egg='): # pragma: no cover
|
||||||
logger.debug('%s: version hint in fragment: %r',
|
logger.debug('%s: version hint in fragment: %r',
|
||||||
project_name, frag)
|
project_name, frag)
|
||||||
m = HASHER_HASH.match(frag)
|
m = HASHER_HASH.match(frag)
|
||||||
|
@ -250,7 +250,7 @@ class Locator(object):
|
||||||
else:
|
else:
|
||||||
algo, digest = None, None
|
algo, digest = None, None
|
||||||
origpath = path
|
origpath = path
|
||||||
if path and path[-1] == '/':
|
if path and path[-1] == '/': # pragma: no cover
|
||||||
path = path[:-1]
|
path = path[:-1]
|
||||||
if path.endswith('.whl'):
|
if path.endswith('.whl'):
|
||||||
try:
|
try:
|
||||||
|
@ -272,13 +272,15 @@ class Locator(object):
|
||||||
}
|
}
|
||||||
except Exception as e: # pragma: no cover
|
except Exception as e: # pragma: no cover
|
||||||
logger.warning('invalid path for wheel: %s', path)
|
logger.warning('invalid path for wheel: %s', path)
|
||||||
elif path.endswith(self.downloadable_extensions):
|
elif not path.endswith(self.downloadable_extensions): # pragma: no cover
|
||||||
|
logger.debug('Not downloadable: %s', path)
|
||||||
|
else: # downloadable extension
|
||||||
path = filename = posixpath.basename(path)
|
path = filename = posixpath.basename(path)
|
||||||
for ext in self.downloadable_extensions:
|
for ext in self.downloadable_extensions:
|
||||||
if path.endswith(ext):
|
if path.endswith(ext):
|
||||||
path = path[:-len(ext)]
|
path = path[:-len(ext)]
|
||||||
t = self.split_filename(path, project_name)
|
t = self.split_filename(path, project_name)
|
||||||
if not t:
|
if not t: # pragma: no cover
|
||||||
logger.debug('No match for project/version: %s', path)
|
logger.debug('No match for project/version: %s', path)
|
||||||
else:
|
else:
|
||||||
name, version, pyver = t
|
name, version, pyver = t
|
||||||
|
@ -291,7 +293,7 @@ class Locator(object):
|
||||||
params, query, '')),
|
params, query, '')),
|
||||||
#'packagetype': 'sdist',
|
#'packagetype': 'sdist',
|
||||||
}
|
}
|
||||||
if pyver:
|
if pyver: # pragma: no cover
|
||||||
result['python-version'] = pyver
|
result['python-version'] = pyver
|
||||||
break
|
break
|
||||||
if result and algo:
|
if result and algo:
|
||||||
|
@ -352,7 +354,7 @@ class Locator(object):
|
||||||
"""
|
"""
|
||||||
result = None
|
result = None
|
||||||
r = parse_requirement(requirement)
|
r = parse_requirement(requirement)
|
||||||
if r is None:
|
if r is None: # pragma: no cover
|
||||||
raise DistlibException('Not a valid requirement: %r' % requirement)
|
raise DistlibException('Not a valid requirement: %r' % requirement)
|
||||||
scheme = get_scheme(self.scheme)
|
scheme = get_scheme(self.scheme)
|
||||||
self.matcher = matcher = scheme.matcher(r.requirement)
|
self.matcher = matcher = scheme.matcher(r.requirement)
|
||||||
|
@ -390,7 +392,7 @@ class Locator(object):
|
||||||
d = {}
|
d = {}
|
||||||
sd = versions.get('digests', {})
|
sd = versions.get('digests', {})
|
||||||
for url in result.download_urls:
|
for url in result.download_urls:
|
||||||
if url in sd:
|
if url in sd: # pragma: no cover
|
||||||
d[url] = sd[url]
|
d[url] = sd[url]
|
||||||
result.digests = d
|
result.digests = d
|
||||||
self.matcher = None
|
self.matcher = None
|
||||||
|
@ -730,11 +732,14 @@ class SimpleScrapingLocator(Locator):
|
||||||
continue
|
continue
|
||||||
for link, rel in page.links:
|
for link, rel in page.links:
|
||||||
if link not in self._seen:
|
if link not in self._seen:
|
||||||
self._seen.add(link)
|
try:
|
||||||
if (not self._process_download(link) and
|
self._seen.add(link)
|
||||||
self._should_queue(link, url, rel)):
|
if (not self._process_download(link) and
|
||||||
logger.debug('Queueing %s from %s', link, url)
|
self._should_queue(link, url, rel)):
|
||||||
self._to_fetch.put(link)
|
logger.debug('Queueing %s from %s', link, url)
|
||||||
|
self._to_fetch.put(link)
|
||||||
|
except MetadataInvalidError: # e.g. invalid versions
|
||||||
|
pass
|
||||||
except Exception as e: # pragma: no cover
|
except Exception as e: # pragma: no cover
|
||||||
self.errors.put(text_type(e))
|
self.errors.put(text_type(e))
|
||||||
finally:
|
finally:
|
||||||
|
|
|
@ -119,9 +119,12 @@ def interpret(marker, execution_context=None):
|
||||||
:param execution_context: The context used for name lookup.
|
:param execution_context: The context used for name lookup.
|
||||||
:type execution_context: mapping
|
:type execution_context: mapping
|
||||||
"""
|
"""
|
||||||
expr, rest = parse_marker(marker)
|
try:
|
||||||
|
expr, rest = parse_marker(marker)
|
||||||
|
except Exception as e:
|
||||||
|
raise SyntaxError('Unable to interpret marker syntax: %s: %s' % (marker, e))
|
||||||
if rest and rest[0] != '#':
|
if rest and rest[0] != '#':
|
||||||
raise SyntaxError('unexpected trailing data: %s' % rest)
|
raise SyntaxError('unexpected trailing data in marker: %s: %s' % (marker, rest))
|
||||||
context = dict(DEFAULT_CONTEXT)
|
context = dict(DEFAULT_CONTEXT)
|
||||||
if execution_context:
|
if execution_context:
|
||||||
context.update(execution_context)
|
context.update(execution_context)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2013-2016 Vinay Sajip.
|
# Copyright (C) 2013-2017 Vinay Sajip.
|
||||||
# Licensed to the Python Software Foundation under a contributor agreement.
|
# Licensed to the Python Software Foundation under a contributor agreement.
|
||||||
# See LICENSE.txt and CONTRIBUTORS.txt.
|
# See LICENSE.txt and CONTRIBUTORS.txt.
|
||||||
#
|
#
|
||||||
|
|
|
@ -136,6 +136,37 @@ class ScriptMaker(object):
|
||||||
return executable
|
return executable
|
||||||
return '/usr/bin/env %s' % executable
|
return '/usr/bin/env %s' % executable
|
||||||
|
|
||||||
|
def _build_shebang(self, executable, post_interp):
|
||||||
|
"""
|
||||||
|
Build a shebang line. In the simple case (on Windows, or a shebang line
|
||||||
|
which is not too long or contains spaces) use a simple formulation for
|
||||||
|
the shebang. Otherwise, use /bin/sh as the executable, with a contrived
|
||||||
|
shebang which allows the script to run either under Python or sh, using
|
||||||
|
suitable quoting. Thanks to Harald Nordgren for his input.
|
||||||
|
|
||||||
|
See also: http://www.in-ulm.de/~mascheck/various/shebang/#length
|
||||||
|
https://hg.mozilla.org/mozilla-central/file/tip/mach
|
||||||
|
"""
|
||||||
|
if os.name != 'posix':
|
||||||
|
simple_shebang = True
|
||||||
|
else:
|
||||||
|
# Add 3 for '#!' prefix and newline suffix.
|
||||||
|
shebang_length = len(executable) + len(post_interp) + 3
|
||||||
|
if sys.platform == 'darwin':
|
||||||
|
max_shebang_length = 512
|
||||||
|
else:
|
||||||
|
max_shebang_length = 127
|
||||||
|
simple_shebang = ((b' ' not in executable) and
|
||||||
|
(shebang_length <= max_shebang_length))
|
||||||
|
|
||||||
|
if simple_shebang:
|
||||||
|
result = b'#!' + executable + post_interp + b'\n'
|
||||||
|
else:
|
||||||
|
result = b'#!/bin/sh\n'
|
||||||
|
result += b"'''exec' " + executable + post_interp + b' "$0" "$@"\n'
|
||||||
|
result += b"' '''"
|
||||||
|
return result
|
||||||
|
|
||||||
def _get_shebang(self, encoding, post_interp=b'', options=None):
|
def _get_shebang(self, encoding, post_interp=b'', options=None):
|
||||||
enquote = True
|
enquote = True
|
||||||
if self.executable:
|
if self.executable:
|
||||||
|
@ -169,7 +200,7 @@ class ScriptMaker(object):
|
||||||
if (sys.platform == 'cli' and '-X:Frames' not in post_interp
|
if (sys.platform == 'cli' and '-X:Frames' not in post_interp
|
||||||
and '-X:FullFrames' not in post_interp): # pragma: no cover
|
and '-X:FullFrames' not in post_interp): # pragma: no cover
|
||||||
post_interp += b' -X:Frames'
|
post_interp += b' -X:Frames'
|
||||||
shebang = b'#!' + executable + post_interp + b'\n'
|
shebang = self._build_shebang(executable, post_interp)
|
||||||
# Python parser starts to read a script using UTF-8 until
|
# Python parser starts to read a script using UTF-8 until
|
||||||
# it gets a #coding:xxx cookie. The shebang has to be the
|
# it gets a #coding:xxx cookie. The shebang has to be the
|
||||||
# first line of a file, the #coding:xxx cookie cannot be
|
# first line of a file, the #coding:xxx cookie cannot be
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,6 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2012-2016 The Python Software Foundation.
|
# Copyright (C) 2012-2017 The Python Software Foundation.
|
||||||
# See LICENSE.txt and CONTRIBUTORS.txt.
|
# See LICENSE.txt and CONTRIBUTORS.txt.
|
||||||
#
|
#
|
||||||
"""
|
"""
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,6 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2013-2016 Vinay Sajip.
|
# Copyright (C) 2013-2017 Vinay Sajip.
|
||||||
# Licensed to the Python Software Foundation under a contributor agreement.
|
# Licensed to the Python Software Foundation under a contributor agreement.
|
||||||
# See LICENSE.txt and CONTRIBUTORS.txt.
|
# See LICENSE.txt and CONTRIBUTORS.txt.
|
||||||
#
|
#
|
||||||
|
@ -35,11 +35,11 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
cache = None # created when needed
|
cache = None # created when needed
|
||||||
|
|
||||||
if hasattr(sys, 'pypy_version_info'):
|
if hasattr(sys, 'pypy_version_info'): # pragma: no cover
|
||||||
IMP_PREFIX = 'pp'
|
IMP_PREFIX = 'pp'
|
||||||
elif sys.platform.startswith('java'):
|
elif sys.platform.startswith('java'): # pragma: no cover
|
||||||
IMP_PREFIX = 'jy'
|
IMP_PREFIX = 'jy'
|
||||||
elif sys.platform == 'cli':
|
elif sys.platform == 'cli': # pragma: no cover
|
||||||
IMP_PREFIX = 'ip'
|
IMP_PREFIX = 'ip'
|
||||||
else:
|
else:
|
||||||
IMP_PREFIX = 'cp'
|
IMP_PREFIX = 'cp'
|
||||||
|
|
|
@ -2028,51 +2028,121 @@ def find_on_path(importer, path_item, only=False):
|
||||||
path_item, os.path.join(path_item, 'EGG-INFO')
|
path_item, os.path.join(path_item, 'EGG-INFO')
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
return
|
||||||
try:
|
|
||||||
entries = os.listdir(path_item)
|
entries = safe_listdir(path_item)
|
||||||
except (PermissionError, NotADirectoryError):
|
|
||||||
return
|
# for performance, before sorting by version,
|
||||||
except OSError as e:
|
# screen entries for only those that will yield
|
||||||
# Ignore the directory if does not exist, not a directory or we
|
# distributions
|
||||||
# don't have permissions
|
filtered = (
|
||||||
if (e.errno in (errno.ENOTDIR, errno.EACCES, errno.ENOENT)
|
entry
|
||||||
# Python 2 on Windows needs to be handled this way :(
|
for entry in entries
|
||||||
or hasattr(e, "winerror") and e.winerror == 267):
|
if dist_factory(path_item, entry, only)
|
||||||
return
|
)
|
||||||
|
|
||||||
|
# scan for .egg and .egg-info in directory
|
||||||
|
path_item_entries = _by_version_descending(filtered)
|
||||||
|
for entry in path_item_entries:
|
||||||
|
fullpath = os.path.join(path_item, entry)
|
||||||
|
factory = dist_factory(path_item, entry, only)
|
||||||
|
for dist in factory(fullpath):
|
||||||
|
yield dist
|
||||||
|
|
||||||
|
|
||||||
|
def dist_factory(path_item, entry, only):
|
||||||
|
"""
|
||||||
|
Return a dist_factory for a path_item and entry
|
||||||
|
"""
|
||||||
|
lower = entry.lower()
|
||||||
|
is_meta = any(map(lower.endswith, ('.egg-info', '.dist-info')))
|
||||||
|
return (
|
||||||
|
distributions_from_metadata
|
||||||
|
if is_meta else
|
||||||
|
find_distributions
|
||||||
|
if not only and _is_egg_path(entry) else
|
||||||
|
resolve_egg_link
|
||||||
|
if not only and lower.endswith('.egg-link') else
|
||||||
|
NoDists()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NoDists:
|
||||||
|
"""
|
||||||
|
>>> bool(NoDists())
|
||||||
|
False
|
||||||
|
|
||||||
|
>>> list(NoDists()('anything'))
|
||||||
|
[]
|
||||||
|
"""
|
||||||
|
def __bool__(self):
|
||||||
|
return False
|
||||||
|
if six.PY2:
|
||||||
|
__nonzero__ = __bool__
|
||||||
|
|
||||||
|
def __call__(self, fullpath):
|
||||||
|
return iter(())
|
||||||
|
|
||||||
|
|
||||||
|
def safe_listdir(path):
|
||||||
|
"""
|
||||||
|
Attempt to list contents of path, but suppress some exceptions.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return os.listdir(path)
|
||||||
|
except (PermissionError, NotADirectoryError):
|
||||||
|
pass
|
||||||
|
except OSError as e:
|
||||||
|
# Ignore the directory if does not exist, not a directory or
|
||||||
|
# permission denied
|
||||||
|
ignorable = (
|
||||||
|
e.errno in (errno.ENOTDIR, errno.EACCES, errno.ENOENT)
|
||||||
|
# Python 2 on Windows needs to be handled this way :(
|
||||||
|
or getattr(e, "winerror", None) == 267
|
||||||
|
)
|
||||||
|
if not ignorable:
|
||||||
raise
|
raise
|
||||||
# scan for .egg and .egg-info in directory
|
return ()
|
||||||
path_item_entries = _by_version_descending(entries)
|
|
||||||
for entry in path_item_entries:
|
|
||||||
lower = entry.lower()
|
def distributions_from_metadata(path):
|
||||||
if lower.endswith('.egg-info') or lower.endswith('.dist-info'):
|
root = os.path.dirname(path)
|
||||||
fullpath = os.path.join(path_item, entry)
|
if os.path.isdir(path):
|
||||||
if os.path.isdir(fullpath):
|
if len(os.listdir(path)) == 0:
|
||||||
# egg-info directory, allow getting metadata
|
# empty metadata dir; skip
|
||||||
if len(os.listdir(fullpath)) == 0:
|
return
|
||||||
# Empty egg directory, skip.
|
metadata = PathMetadata(root, path)
|
||||||
continue
|
else:
|
||||||
metadata = PathMetadata(path_item, fullpath)
|
metadata = FileMetadata(path)
|
||||||
else:
|
entry = os.path.basename(path)
|
||||||
metadata = FileMetadata(fullpath)
|
yield Distribution.from_location(
|
||||||
yield Distribution.from_location(
|
root, entry, metadata, precedence=DEVELOP_DIST,
|
||||||
path_item, entry, metadata, precedence=DEVELOP_DIST
|
)
|
||||||
)
|
|
||||||
elif not only and _is_egg_path(entry):
|
|
||||||
dists = find_distributions(os.path.join(path_item, entry))
|
def non_empty_lines(path):
|
||||||
for dist in dists:
|
"""
|
||||||
yield dist
|
Yield non-empty lines from file at path
|
||||||
elif not only and lower.endswith('.egg-link'):
|
"""
|
||||||
with open(os.path.join(path_item, entry)) as entry_file:
|
with open(path) as f:
|
||||||
entry_lines = entry_file.readlines()
|
for line in f:
|
||||||
for line in entry_lines:
|
line = line.strip()
|
||||||
if not line.strip():
|
if line:
|
||||||
continue
|
yield line
|
||||||
path = os.path.join(path_item, line.rstrip())
|
|
||||||
dists = find_distributions(path)
|
|
||||||
for item in dists:
|
def resolve_egg_link(path):
|
||||||
yield item
|
"""
|
||||||
break
|
Given a path to an .egg-link, resolve distributions
|
||||||
|
present in the referenced path.
|
||||||
|
"""
|
||||||
|
referenced_paths = non_empty_lines(path)
|
||||||
|
resolved_paths = (
|
||||||
|
os.path.join(os.path.dirname(path), ref)
|
||||||
|
for ref in referenced_paths
|
||||||
|
)
|
||||||
|
dist_groups = map(find_distributions, resolved_paths)
|
||||||
|
return next(dist_groups, ())
|
||||||
|
|
||||||
|
|
||||||
register_finder(pkgutil.ImpImporter, find_on_path)
|
register_finder(pkgutil.ImpImporter, find_on_path)
|
||||||
|
@ -2250,9 +2320,7 @@ def _is_egg_path(path):
|
||||||
"""
|
"""
|
||||||
Determine if given path appears to be an egg.
|
Determine if given path appears to be an egg.
|
||||||
"""
|
"""
|
||||||
return (
|
return path.lower().endswith('.egg')
|
||||||
path.lower().endswith('.egg')
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _is_unpacked_egg(path):
|
def _is_unpacked_egg(path):
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
"""Utilities for writing code that runs on Python 2 and 3"""
|
# Copyright (c) 2010-2017 Benjamin Peterson
|
||||||
|
|
||||||
# Copyright (c) 2010-2015 Benjamin Peterson
|
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -20,6 +18,8 @@
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
# SOFTWARE.
|
# SOFTWARE.
|
||||||
|
|
||||||
|
"""Utilities for writing code that runs on Python 2 and 3"""
|
||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
|
@ -29,7 +29,7 @@ import sys
|
||||||
import types
|
import types
|
||||||
|
|
||||||
__author__ = "Benjamin Peterson <benjamin@python.org>"
|
__author__ = "Benjamin Peterson <benjamin@python.org>"
|
||||||
__version__ = "1.10.0"
|
__version__ = "1.11.0"
|
||||||
|
|
||||||
|
|
||||||
# Useful for very coarse version differentiation.
|
# Useful for very coarse version differentiation.
|
||||||
|
@ -241,6 +241,7 @@ _moved_attributes = [
|
||||||
MovedAttribute("map", "itertools", "builtins", "imap", "map"),
|
MovedAttribute("map", "itertools", "builtins", "imap", "map"),
|
||||||
MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"),
|
MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"),
|
||||||
MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"),
|
MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"),
|
||||||
|
MovedAttribute("getoutput", "commands", "subprocess"),
|
||||||
MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
|
MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
|
||||||
MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"),
|
MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"),
|
||||||
MovedAttribute("reduce", "__builtin__", "functools"),
|
MovedAttribute("reduce", "__builtin__", "functools"),
|
||||||
|
@ -262,10 +263,11 @@ _moved_attributes = [
|
||||||
MovedModule("html_entities", "htmlentitydefs", "html.entities"),
|
MovedModule("html_entities", "htmlentitydefs", "html.entities"),
|
||||||
MovedModule("html_parser", "HTMLParser", "html.parser"),
|
MovedModule("html_parser", "HTMLParser", "html.parser"),
|
||||||
MovedModule("http_client", "httplib", "http.client"),
|
MovedModule("http_client", "httplib", "http.client"),
|
||||||
|
MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
|
||||||
|
MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"),
|
||||||
MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
|
MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
|
||||||
MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"),
|
MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"),
|
||||||
MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
|
MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
|
||||||
MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
|
|
||||||
MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
|
MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
|
||||||
MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
|
MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
|
||||||
MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
|
MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
|
||||||
|
@ -337,10 +339,12 @@ _urllib_parse_moved_attributes = [
|
||||||
MovedAttribute("quote_plus", "urllib", "urllib.parse"),
|
MovedAttribute("quote_plus", "urllib", "urllib.parse"),
|
||||||
MovedAttribute("unquote", "urllib", "urllib.parse"),
|
MovedAttribute("unquote", "urllib", "urllib.parse"),
|
||||||
MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
|
MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
|
||||||
|
MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"),
|
||||||
MovedAttribute("urlencode", "urllib", "urllib.parse"),
|
MovedAttribute("urlencode", "urllib", "urllib.parse"),
|
||||||
MovedAttribute("splitquery", "urllib", "urllib.parse"),
|
MovedAttribute("splitquery", "urllib", "urllib.parse"),
|
||||||
MovedAttribute("splittag", "urllib", "urllib.parse"),
|
MovedAttribute("splittag", "urllib", "urllib.parse"),
|
||||||
MovedAttribute("splituser", "urllib", "urllib.parse"),
|
MovedAttribute("splituser", "urllib", "urllib.parse"),
|
||||||
|
MovedAttribute("splitvalue", "urllib", "urllib.parse"),
|
||||||
MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
|
MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
|
||||||
MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
|
MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
|
||||||
MovedAttribute("uses_params", "urlparse", "urllib.parse"),
|
MovedAttribute("uses_params", "urlparse", "urllib.parse"),
|
||||||
|
@ -416,6 +420,8 @@ _urllib_request_moved_attributes = [
|
||||||
MovedAttribute("URLopener", "urllib", "urllib.request"),
|
MovedAttribute("URLopener", "urllib", "urllib.request"),
|
||||||
MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
|
MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
|
||||||
MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
|
MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
|
||||||
|
MovedAttribute("parse_http_list", "urllib2", "urllib.request"),
|
||||||
|
MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"),
|
||||||
]
|
]
|
||||||
for attr in _urllib_request_moved_attributes:
|
for attr in _urllib_request_moved_attributes:
|
||||||
setattr(Module_six_moves_urllib_request, attr.name, attr)
|
setattr(Module_six_moves_urllib_request, attr.name, attr)
|
||||||
|
@ -679,11 +685,15 @@ if PY3:
|
||||||
exec_ = getattr(moves.builtins, "exec")
|
exec_ = getattr(moves.builtins, "exec")
|
||||||
|
|
||||||
def reraise(tp, value, tb=None):
|
def reraise(tp, value, tb=None):
|
||||||
if value is None:
|
try:
|
||||||
value = tp()
|
if value is None:
|
||||||
if value.__traceback__ is not tb:
|
value = tp()
|
||||||
raise value.with_traceback(tb)
|
if value.__traceback__ is not tb:
|
||||||
raise value
|
raise value.with_traceback(tb)
|
||||||
|
raise value
|
||||||
|
finally:
|
||||||
|
value = None
|
||||||
|
tb = None
|
||||||
|
|
||||||
else:
|
else:
|
||||||
def exec_(_code_, _globs_=None, _locs_=None):
|
def exec_(_code_, _globs_=None, _locs_=None):
|
||||||
|
@ -699,19 +709,28 @@ else:
|
||||||
exec("""exec _code_ in _globs_, _locs_""")
|
exec("""exec _code_ in _globs_, _locs_""")
|
||||||
|
|
||||||
exec_("""def reraise(tp, value, tb=None):
|
exec_("""def reraise(tp, value, tb=None):
|
||||||
raise tp, value, tb
|
try:
|
||||||
|
raise tp, value, tb
|
||||||
|
finally:
|
||||||
|
tb = None
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
||||||
if sys.version_info[:2] == (3, 2):
|
if sys.version_info[:2] == (3, 2):
|
||||||
exec_("""def raise_from(value, from_value):
|
exec_("""def raise_from(value, from_value):
|
||||||
if from_value is None:
|
try:
|
||||||
raise value
|
if from_value is None:
|
||||||
raise value from from_value
|
raise value
|
||||||
|
raise value from from_value
|
||||||
|
finally:
|
||||||
|
value = None
|
||||||
""")
|
""")
|
||||||
elif sys.version_info[:2] > (3, 2):
|
elif sys.version_info[:2] > (3, 2):
|
||||||
exec_("""def raise_from(value, from_value):
|
exec_("""def raise_from(value, from_value):
|
||||||
raise value from from_value
|
try:
|
||||||
|
raise value from from_value
|
||||||
|
finally:
|
||||||
|
value = None
|
||||||
""")
|
""")
|
||||||
else:
|
else:
|
||||||
def raise_from(value, from_value):
|
def raise_from(value, from_value):
|
||||||
|
@ -802,10 +821,14 @@ def with_metaclass(meta, *bases):
|
||||||
# This requires a bit of explanation: the basic idea is to make a dummy
|
# This requires a bit of explanation: the basic idea is to make a dummy
|
||||||
# metaclass for one level of class instantiation that replaces itself with
|
# metaclass for one level of class instantiation that replaces itself with
|
||||||
# the actual metaclass.
|
# the actual metaclass.
|
||||||
class metaclass(meta):
|
class metaclass(type):
|
||||||
|
|
||||||
def __new__(cls, name, this_bases, d):
|
def __new__(cls, name, this_bases, d):
|
||||||
return meta(name, bases, d)
|
return meta(name, bases, d)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __prepare__(cls, name, this_bases):
|
||||||
|
return meta.__prepare__(name, bases)
|
||||||
return type.__new__(metaclass, 'temporary_class', (), {})
|
return type.__new__(metaclass, 'temporary_class', (), {})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
appdirs==1.4.3
|
appdirs==1.4.3
|
||||||
distlib==0.2.5
|
distlib==0.2.6
|
||||||
distro==1.0.4
|
distro==1.0.4
|
||||||
html5lib==1.0b10
|
html5lib==1.0b10
|
||||||
six==1.10.0
|
six==1.11.0
|
||||||
colorama==0.3.9
|
colorama==0.3.9
|
||||||
CacheControl==0.12.3
|
CacheControl==0.12.3
|
||||||
msgpack-python==0.4.8
|
msgpack-python==0.4.8
|
||||||
|
@ -18,5 +18,5 @@ requests==2.18.4
|
||||||
idna==2.6
|
idna==2.6
|
||||||
urllib3==1.22
|
urllib3==1.22
|
||||||
certifi==2017.7.27.1
|
certifi==2017.7.27.1
|
||||||
setuptools==36.4.0
|
setuptools==36.6.0
|
||||||
webencodings==0.5.1
|
webencodings==0.5.1
|
||||||
|
|
|
@ -498,6 +498,78 @@ def test_freeze_with_requirement_option_multiple(script):
|
||||||
assert result.stdout.count("--index-url http://ignore") == 1
|
assert result.stdout.count("--index-url http://ignore") == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_freeze_with_requirement_option_package_repeated_one_file(script):
|
||||||
|
"""
|
||||||
|
Test freezing with single requirements file that contains a package
|
||||||
|
multiple times
|
||||||
|
"""
|
||||||
|
script.scratch_path.join('hint1.txt').write(textwrap.dedent("""\
|
||||||
|
simple2
|
||||||
|
simple2
|
||||||
|
NoExist
|
||||||
|
""") + _freeze_req_opts)
|
||||||
|
result = script.pip_install_local('simple2==1.0')
|
||||||
|
result = script.pip_install_local('meta')
|
||||||
|
result = script.pip(
|
||||||
|
'freeze', '--requirement', 'hint1.txt',
|
||||||
|
expect_stderr=True,
|
||||||
|
)
|
||||||
|
expected_out = textwrap.dedent("""\
|
||||||
|
simple2==1.0
|
||||||
|
""")
|
||||||
|
expected_out += _freeze_req_opts
|
||||||
|
expected_out += "## The following requirements were added by pip freeze:"
|
||||||
|
expected_out += '\n' + textwrap.dedent("""\
|
||||||
|
...meta==1.0...
|
||||||
|
""")
|
||||||
|
_check_output(result.stdout, expected_out)
|
||||||
|
err1 = ("Requirement file [hint1.txt] contains NoExist, "
|
||||||
|
"but that package is not installed\n")
|
||||||
|
err2 = "Requirement simple2 included multiple times [hint1.txt]\n"
|
||||||
|
assert err1 in result.stderr
|
||||||
|
assert err2 in result.stderr
|
||||||
|
# there shouldn't be any other 'is not installed' warnings
|
||||||
|
assert result.stderr.count('is not installed') == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_freeze_with_requirement_option_package_repeated_multi_file(script):
|
||||||
|
"""
|
||||||
|
Test freezing with multiple requirements file that contain a package
|
||||||
|
"""
|
||||||
|
script.scratch_path.join('hint1.txt').write(textwrap.dedent("""\
|
||||||
|
simple
|
||||||
|
""") + _freeze_req_opts)
|
||||||
|
script.scratch_path.join('hint2.txt').write(textwrap.dedent("""\
|
||||||
|
simple
|
||||||
|
NoExist
|
||||||
|
""") + _freeze_req_opts)
|
||||||
|
result = script.pip_install_local('simple==1.0')
|
||||||
|
result = script.pip_install_local('meta')
|
||||||
|
result = script.pip(
|
||||||
|
'freeze', '--requirement', 'hint1.txt',
|
||||||
|
'--requirement', 'hint2.txt',
|
||||||
|
expect_stderr=True,
|
||||||
|
)
|
||||||
|
expected_out = textwrap.dedent("""\
|
||||||
|
simple==1.0
|
||||||
|
""")
|
||||||
|
expected_out += _freeze_req_opts
|
||||||
|
expected_out += "## The following requirements were added by pip freeze:"
|
||||||
|
expected_out += '\n' + textwrap.dedent("""\
|
||||||
|
...meta==1.0...
|
||||||
|
""")
|
||||||
|
_check_output(result.stdout, expected_out)
|
||||||
|
|
||||||
|
err1 = ("Requirement file [hint2.txt] contains NoExist, but that "
|
||||||
|
"package is not installed\n")
|
||||||
|
err2 = ("Requirement simple included multiple times "
|
||||||
|
"[hint1.txt, hint2.txt]\n")
|
||||||
|
assert err1 in result.stderr
|
||||||
|
assert err2 in result.stderr
|
||||||
|
# there shouldn't be any other 'is not installed' warnings
|
||||||
|
assert result.stderr.count('is not installed') == 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.network
|
@pytest.mark.network
|
||||||
def test_freeze_user(script, virtualenv, data):
|
def test_freeze_user(script, virtualenv, data):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1258,3 +1258,25 @@ def test_installing_scripts_on_path_does_not_print_warning(script):
|
||||||
result = script.pip_install_local("script_wheel1")
|
result = script.pip_install_local("script_wheel1")
|
||||||
assert "Successfully installed script-wheel1" in result.stdout, str(result)
|
assert "Successfully installed script-wheel1" in result.stdout, str(result)
|
||||||
assert "--no-warn-script-location" not in result.stderr
|
assert "--no-warn-script-location" not in result.stderr
|
||||||
|
|
||||||
|
|
||||||
|
def test_installed_files_recorded_in_deterministic_order(script, data):
|
||||||
|
"""
|
||||||
|
Ensure that we record the files installed by a package in a deterministic
|
||||||
|
order, to make installs reproducible.
|
||||||
|
"""
|
||||||
|
to_install = data.packages.join("FSPkg")
|
||||||
|
result = script.pip('install', to_install, expect_error=False)
|
||||||
|
fspkg_folder = script.site_packages / 'fspkg'
|
||||||
|
egg_info = 'FSPkg-0.1.dev0-py%s.egg-info' % pyversion
|
||||||
|
installed_files_path = (
|
||||||
|
script.site_packages / egg_info / 'installed-files.txt'
|
||||||
|
)
|
||||||
|
assert fspkg_folder in result.files_created, str(result.stdout)
|
||||||
|
assert installed_files_path in result.files_created, str(result)
|
||||||
|
|
||||||
|
installed_files_path = result.files_created[installed_files_path].full
|
||||||
|
installed_files_lines = [
|
||||||
|
p for p in Path(installed_files_path).read_text().split('\n') if p
|
||||||
|
]
|
||||||
|
assert installed_files_lines == sorted(installed_files_lines)
|
||||||
|
|
|
@ -184,7 +184,7 @@ def test_git_with_tag_name_and_update(script, tmpdir):
|
||||||
result = script.pip(
|
result = script.pip(
|
||||||
'install', '-e', '%s#egg=pip-test-package' %
|
'install', '-e', '%s#egg=pip-test-package' %
|
||||||
local_checkout(
|
local_checkout(
|
||||||
'git+http://github.com/pypa/pip-test-package.git',
|
'git+https://github.com/pypa/pip-test-package.git',
|
||||||
tmpdir.join("cache"),
|
tmpdir.join("cache"),
|
||||||
),
|
),
|
||||||
expect_error=True,
|
expect_error=True,
|
||||||
|
@ -194,7 +194,7 @@ def test_git_with_tag_name_and_update(script, tmpdir):
|
||||||
'install', '--global-option=--version', '-e',
|
'install', '--global-option=--version', '-e',
|
||||||
'%s@0.1.2#egg=pip-test-package' %
|
'%s@0.1.2#egg=pip-test-package' %
|
||||||
local_checkout(
|
local_checkout(
|
||||||
'git+http://github.com/pypa/pip-test-package.git',
|
'git+https://github.com/pypa/pip-test-package.git',
|
||||||
tmpdir.join("cache"),
|
tmpdir.join("cache"),
|
||||||
),
|
),
|
||||||
expect_error=True,
|
expect_error=True,
|
||||||
|
@ -211,7 +211,7 @@ def test_git_branch_should_not_be_changed(script, tmpdir):
|
||||||
script.pip(
|
script.pip(
|
||||||
'install', '-e', '%s#egg=pip-test-package' %
|
'install', '-e', '%s#egg=pip-test-package' %
|
||||||
local_checkout(
|
local_checkout(
|
||||||
'git+http://github.com/pypa/pip-test-package.git',
|
'git+https://github.com/pypa/pip-test-package.git',
|
||||||
tmpdir.join("cache"),
|
tmpdir.join("cache"),
|
||||||
),
|
),
|
||||||
expect_error=True,
|
expect_error=True,
|
||||||
|
@ -229,7 +229,7 @@ def test_git_with_non_editable_unpacking(script, tmpdir):
|
||||||
result = script.pip(
|
result = script.pip(
|
||||||
'install', '--global-option=--version',
|
'install', '--global-option=--version',
|
||||||
local_checkout(
|
local_checkout(
|
||||||
'git+http://github.com/pypa/pip-test-package.git@0.1.2'
|
'git+https://github.com/pypa/pip-test-package.git@0.1.2'
|
||||||
'#egg=pip-test-package',
|
'#egg=pip-test-package',
|
||||||
tmpdir.join("cache")
|
tmpdir.join("cache")
|
||||||
),
|
),
|
||||||
|
|
41
tests/functional/test_no_color.py
Normal file
41
tests/functional/test_no_color.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
"""
|
||||||
|
Test specific for the --no-color option
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import subprocess as sp
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(sys.platform == 'win32',
|
||||||
|
reason="does not run on windows")
|
||||||
|
def test_no_color(script):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Test uninstalling an existing package - should out put red error
|
||||||
|
|
||||||
|
We must use subprocess with the script command, since redirection
|
||||||
|
in unix platform causes text coloring to disapper. Thus, we can't
|
||||||
|
use the testing infrastructure that other options has.
|
||||||
|
"""
|
||||||
|
|
||||||
|
sp.Popen("script --flush --quiet --return /tmp/colored-output.txt"
|
||||||
|
" --command \"pip uninstall noSuchPackage\"", shell=True,
|
||||||
|
stdout=sp.PIPE, stderr=sp.PIPE).communicate()
|
||||||
|
|
||||||
|
with open("/tmp/colored-output.txt", "r") as result:
|
||||||
|
assert "\x1b" in result.read()
|
||||||
|
|
||||||
|
os.unlink("/tmp/colored-output.txt")
|
||||||
|
|
||||||
|
sp.Popen("script --flush --quiet --return /tmp/no-color-output.txt"
|
||||||
|
" --command \"pip --no-color uninstall noSuchPackage\"",
|
||||||
|
shell=True,
|
||||||
|
stdout=sp.PIPE, stderr=sp.PIPE).communicate()
|
||||||
|
|
||||||
|
with open("/tmp/no-color-output.txt", "r") as result:
|
||||||
|
assert "\x1b" not in result.read()
|
||||||
|
|
||||||
|
os.unlink("/tmp/no-color-output.txt")
|
|
@ -100,6 +100,39 @@ def test_uninstall_easy_install_after_import(script):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.network
|
||||||
|
def test_uninstall_trailing_newline(script):
|
||||||
|
"""
|
||||||
|
Uninstall behaves appropriately if easy-install.pth
|
||||||
|
lacks a trailing newline
|
||||||
|
|
||||||
|
"""
|
||||||
|
script.run('easy_install', 'INITools==0.2', expect_stderr=True)
|
||||||
|
script.run('easy_install', 'PyLogo', expect_stderr=True)
|
||||||
|
easy_install_pth = script.site_packages_path / 'easy-install.pth'
|
||||||
|
|
||||||
|
# trim trailing newline from easy-install.pth
|
||||||
|
with open(easy_install_pth) as f:
|
||||||
|
pth_before = f.read()
|
||||||
|
|
||||||
|
with open(easy_install_pth, 'w') as f:
|
||||||
|
f.write(pth_before.rstrip())
|
||||||
|
|
||||||
|
# uninstall initools
|
||||||
|
script.pip('uninstall', 'INITools', '-y')
|
||||||
|
with open(easy_install_pth) as f:
|
||||||
|
pth_after = f.read()
|
||||||
|
|
||||||
|
# verify that only initools is removed
|
||||||
|
before_without_initools = [
|
||||||
|
line for line in pth_before.splitlines()
|
||||||
|
if 'initools' not in line.lower()
|
||||||
|
]
|
||||||
|
lines_after = pth_after.splitlines()
|
||||||
|
|
||||||
|
assert lines_after == before_without_initools
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.network
|
@pytest.mark.network
|
||||||
def test_uninstall_namespace_package(script):
|
def test_uninstall_namespace_package(script):
|
||||||
"""
|
"""
|
||||||
|
@ -468,3 +501,25 @@ def test_uninstall_editable_and_pip_install(script, data):
|
||||||
) in uninstall2.files_deleted, list(uninstall2.files_deleted.keys())
|
) in uninstall2.files_deleted, list(uninstall2.files_deleted.keys())
|
||||||
list_result2 = script.pip('list', '--format=json')
|
list_result2 = script.pip('list', '--format=json')
|
||||||
assert "FSPkg" not in {p["name"] for p in json.loads(list_result2.stdout)}
|
assert "FSPkg" not in {p["name"] for p in json.loads(list_result2.stdout)}
|
||||||
|
|
||||||
|
|
||||||
|
def test_uninstall_ignores_missing_packages(script, data):
|
||||||
|
"""Uninstall of a non existent package prints a warning and exits cleanly
|
||||||
|
"""
|
||||||
|
result = script.pip(
|
||||||
|
'uninstall', '-y', 'non-existent-pkg', expect_stderr=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "Skipping non-existent-pkg as it is not installed." in result.stderr
|
||||||
|
assert result.returncode == 0, "Expected clean exit"
|
||||||
|
|
||||||
|
|
||||||
|
def test_uninstall_ignores_missing_packages_and_uninstalls_rest(script, data):
|
||||||
|
script.pip_install_local('simple')
|
||||||
|
result = script.pip(
|
||||||
|
'uninstall', '-y', 'non-existent-pkg', 'simple', expect_stderr=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "Skipping non-existent-pkg as it is not installed." in result.stderr
|
||||||
|
assert "Successfully uninstalled simple" in result.stdout
|
||||||
|
assert result.returncode == 0, "Expected clean exit"
|
||||||
|
|
|
@ -364,10 +364,10 @@ class TestWheelBuilder(object):
|
||||||
with patch('pip._internal.wheel.WheelBuilder._build_one') \
|
with patch('pip._internal.wheel.WheelBuilder._build_one') \
|
||||||
as mock_build_one:
|
as mock_build_one:
|
||||||
wheel_req = Mock(is_wheel=True, editable=False, constraint=False)
|
wheel_req = Mock(is_wheel=True, editable=False, constraint=False)
|
||||||
reqset = Mock(requirements=Mock(values=lambda: [wheel_req]),
|
wb = wheel.WheelBuilder(
|
||||||
wheel_download_dir='/wheel/dir')
|
finder=Mock(), preparer=Mock(), wheel_cache=None,
|
||||||
wb = wheel.WheelBuilder(reqset, Mock(), Mock(), wheel_cache=None)
|
)
|
||||||
wb.build(Mock())
|
wb.build([wheel_req], session=Mock())
|
||||||
assert "due to already being wheel" in caplog.text
|
assert "due to already being wheel" in caplog.text
|
||||||
assert mock_build_one.mock_calls == []
|
assert mock_build_one.mock_calls == []
|
||||||
|
|
||||||
|
|
12
tox.ini
12
tox.ini
|
@ -4,7 +4,7 @@ envlist =
|
||||||
py27, py33, py34, py35, py36, py37, pypy
|
py27, py33, py34, py35, py36, py37, pypy
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
passenv = GIT_SSL_CAINFO
|
passenv = CI GIT_SSL_CAINFO
|
||||||
setenv =
|
setenv =
|
||||||
# This is required in order to get UTF-8 output inside of the subprocesses
|
# This is required in order to get UTF-8 output inside of the subprocesses
|
||||||
# that our tests use.
|
# that our tests use.
|
||||||
|
@ -14,12 +14,12 @@ commands = py.test --timeout 300 []
|
||||||
install_command = python -m pip install {opts} {packages}
|
install_command = python -m pip install {opts} {packages}
|
||||||
usedevelop = True
|
usedevelop = True
|
||||||
|
|
||||||
[testenv:docs]
|
[testenv:coverage-py3]
|
||||||
deps =
|
basepython = python3
|
||||||
sphinx == 1.6.1
|
commands = py.test --timeout 300 --cov=pip --cov-report=term-missing --cov-report=xml --cov-report=html tests/unit {posargs}
|
||||||
git+https://github.com/python/python-docs-theme.git#egg=python-docs-theme
|
|
||||||
git+https://github.com/pypa/pypa-docs-theme.git#egg=pypa-docs-theme
|
|
||||||
|
|
||||||
|
[testenv:docs]
|
||||||
|
deps = -r{toxinidir}/docs-requirements.txt
|
||||||
basepython = python2.7
|
basepython = python2.7
|
||||||
commands =
|
commands =
|
||||||
sphinx-build -W -b html -d {envtmpdir}/doctrees docs docs/build/html
|
sphinx-build -W -b html -d {envtmpdir}/doctrees docs docs/build/html
|
||||||
|
|
Loading…
Reference in a new issue