summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE.md3
-rw-r--r--.github/lock.yml8
-rw-r--r--.github/workflows/ci.yml10
-rw-r--r--.github/workflows/lock-threads.yml22
-rw-r--r--.pre-commit-config.yaml1
-rw-r--r--docs/html/cli/pip_install.rst112
-rw-r--r--docs/html/cli/pip_list.rst90
-rw-r--r--docs/html/index.md1
-rw-r--r--docs/html/reference/index.md10
-rw-r--r--docs/html/reference/index.rst11
-rw-r--r--docs/html/reference/requirements-file-format.md147
-rw-r--r--docs/html/user_guide.rst4
-rw-r--r--news/0b3269eb-560c-4b8e-8553-9fe205dc62ec.trivial.rst0
-rw-r--r--news/10249.feature.rst4
-rw-r--r--news/10269.bugfix.rst3
-rw-r--r--news/10361.trivial.rst1
-rw-r--r--news/10410.feature.rst3
-rw-r--r--news/10418.trivial.rst1
-rw-r--r--news/10422.feature.rst1
-rw-r--r--news/118e193d-d2f2-4e70-9767-ba5dddf7d263.trivial.rst0
-rw-r--r--news/3332a3c1-fd62-4d5a-8bc6-459fd677ca70.trivial.rst0
-rw-r--r--news/52ff07b1-69ed-4bbe-a3be-913e0e247cee.trivial.rst0
-rw-r--r--news/569d0d85-be83-40ab-9229-1a6327060a3f.trivial.rst0
-rw-r--r--news/6a2297c8-25c3-4c79-b856-03f0184af36f.trivial.rst0
-rw-r--r--news/77150b20-02ed-411a-ad49-90afdc0a4b53.trivial.rst0
-rw-r--r--news/9349.feature.rst1
-rw-r--r--news/9498.feature.rst1
-rw-r--r--src/pip/_internal/cli/base_command.py97
-rw-r--r--src/pip/_internal/cli/cmdoptions.py13
-rw-r--r--src/pip/_internal/commands/list.py26
-rw-r--r--src/pip/_internal/commands/show.py13
-rw-r--r--src/pip/_internal/configuration.py6
-rw-r--r--src/pip/_internal/metadata/base.py28
-rw-r--r--src/pip/_internal/metadata/pkg_resources.py4
-rw-r--r--src/pip/_internal/models/direct_url.py3
-rw-r--r--src/pip/_internal/network/auth.py13
-rw-r--r--src/pip/_internal/network/session.py7
-rw-r--r--src/pip/_internal/operations/freeze.py72
-rw-r--r--src/pip/_internal/operations/install/wheel.py40
-rw-r--r--src/pip/_internal/req/req_uninstall.py4
-rw-r--r--src/pip/_internal/utils/egg_link.py75
-rw-r--r--src/pip/_internal/utils/misc.py86
-rw-r--r--src/pip/_internal/vcs/git.py1
-rw-r--r--tests/functional/test_download.py13
-rw-r--r--tests/functional/test_freeze.py47
-rw-r--r--tests/functional/test_install.py110
-rw-r--r--tests/functional/test_install_cleanup.py3
-rw-r--r--tests/functional/test_install_config.py3
-rw-r--r--tests/functional/test_install_direct_url.py18
-rw-r--r--tests/functional/test_install_index.py14
-rw-r--r--tests/functional/test_install_reqs.py15
-rw-r--r--tests/functional/test_install_requested.py18
-rw-r--r--tests/functional/test_install_upgrade.py26
-rw-r--r--tests/functional/test_install_user.py5
-rw-r--r--tests/functional/test_install_vcs_git.py9
-rw-r--r--tests/functional/test_install_wheel.py11
-rw-r--r--tests/functional/test_list.py77
-rw-r--r--tests/functional/test_new_resolver.py7
-rw-r--r--tests/functional/test_requests.py12
-rw-r--r--tests/functional/test_wheel.py9
-rw-r--r--tests/lib/direct_url.py15
-rw-r--r--tests/lib/filesystem.py3
-rw-r--r--tests/unit/test_direct_url_helpers.py2
-rw-r--r--tests/unit/test_network_auth.py9
-rw-r--r--tests/unit/test_network_session.py12
-rw-r--r--tests/unit/test_network_utils.py10
-rw-r--r--tests/unit/test_req_file.py4
-rw-r--r--tests/unit/test_utils.py132
-rw-r--r--tests/unit/test_utils_wheel.py2
-rw-r--r--tests/unit/test_wheel_builder.py2
70 files changed, 893 insertions, 617 deletions
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
deleted file mode 100644
index 508153d8d..000000000
--- a/.github/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,3 +0,0 @@
-* pip version:
-* Python version:
-* Operating system:
diff --git a/.github/lock.yml b/.github/lock.yml
deleted file mode 100644
index dd12e3da8..000000000
--- a/.github/lock.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-# Number of days of inactivity before a closed issue or pull request is locked
-daysUntilLock: 30
-# Issues and pull requests with these labels will not be locked.
-exemptLabels: []
-# Label to add before locking, such as `outdated`. Set to `false` to disable
-lockLabel: "S: auto-locked"
-# Comment to post before locking. Set to `false` to disable
-lockComment: false
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1f3e41746..51f7b8f9b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -12,6 +12,16 @@ on:
- cron: 0 0 * * MON # Run every Monday at 00:00 UTC
jobs:
+ docs:
+ name: docs
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-python@v2
+ - run: pip install nox
+ - run: nox -s docs
+
determine-changes:
runs-on: ubuntu-latest
outputs:
diff --git a/.github/workflows/lock-threads.yml b/.github/workflows/lock-threads.yml
new file mode 100644
index 000000000..985060c2e
--- /dev/null
+++ b/.github/workflows/lock-threads.yml
@@ -0,0 +1,22 @@
+name: 'Lock Closed Threads'
+
+on:
+ schedule:
+ - cron: '0 7 * * *' # 7am UTC, daily
+ workflow_dispatch:
+
+permissions:
+ issues: write
+ pull-requests: write
+
+concurrency:
+ group: lock
+
+jobs:
+ action:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: dessant/lock-threads@v2
+ with:
+ issue-lock-inactive-days: '30'
+ pr-lock-inactive-days: '15'
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index b68022f94..a2a147be0 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -47,6 +47,7 @@ repos:
additional_dependencies: [
'keyring==23.0.1',
'nox==2021.6.12',
+ 'pytest==6.2.5',
'types-docutils==0.1.8',
'types-setuptools==57.0.2',
'types-six==0.1.9',
diff --git a/docs/html/cli/pip_install.rst b/docs/html/cli/pip_install.rst
index 1362b490b..6b4d3b1a2 100644
--- a/docs/html/cli/pip_install.rst
+++ b/docs/html/cli/pip_install.rst
@@ -149,120 +149,10 @@ profile:
``setup_requires``.
-.. _`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::
-
- [[--option]...]
- <requirement specifier> [; markers] [[--option]...]
- <archive url/path>
- [-e] <local project path>
- [-e] <vcs project url>
-
-For details on requirement specifiers, see :ref:`Requirement Specifiers`.
-
-See the :ref:`pip install Examples<pip install Examples>` for examples of all these forms.
-
-A line that begins with ``#`` is treated as a comment and ignored. Whitespace
-followed by a ``#`` causes the ``#`` and the remainder of the line to be
-treated as a comment.
-
-A line ending in an unescaped ``\`` is treated as a line continuation
-and the newline following it is effectively ignored.
-
-Comments are stripped *after* line continuations are processed.
-
-To interpret the requirements file in UTF-8 format add a comment
-``# -*- coding: utf-8 -*-`` to the first or second line of the file.
-
-The following options are supported:
-
-.. pip-requirements-file-options-ref-list::
-
-Please note that the above options are global options, and should be specified on their individual lines.
-The options which can be applied to individual requirements are
-:ref:`--install-option <install_--install-option>`, :ref:`--global-option <install_--global-option>` and ``--hash``.
-
-For example, to specify :ref:`--pre <install_--pre>`, :ref:`--no-index <install_--no-index>` and two
-:ref:`--find-links <install_--find-links>` locations:
-
-::
-
---pre
---no-index
---find-links /my/local/archives
---find-links http://some.archives.com/archives
-
-
-If you wish, you can refer to other requirements files, like this::
-
- -r more_requirements.txt
-
-You can also refer to :ref:`constraints files <Constraints Files>`, like this::
-
- -c some_constraints.txt
-
-.. _`Using Environment Variables`:
-
-Using Environment Variables
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Since version 10, pip supports the use of environment variables inside the
-requirements file. You can now store sensitive data (tokens, keys, etc.) in
-environment variables and only specify the variable name for your requirements,
-letting pip lookup the value at runtime. This approach aligns with the commonly
-used `12-factor configuration pattern <https://12factor.net/config>`_.
-
-You have to use the POSIX format for variable names including brackets around
-the uppercase name as shown in this example: ``${API_TOKEN}``. pip will attempt
-to find the corresponding environment variable defined on the host system at
-runtime.
-
-.. note::
-
- There is no support for other variable expansion syntaxes such as
- ``$VARIABLE`` and ``%VARIABLE%``.
-
-
-.. _`Example Requirements File`:
-
-Example Requirements File
-^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Use ``pip install -r example-requirements.txt`` to install::
-
- #
- ####### example-requirements.txt #######
- #
- ###### Requirements without Version Specifiers ######
- nose
- nose-cov
- beautifulsoup4
- #
- ###### Requirements with Version Specifiers ######
- # See https://www.python.org/dev/peps/pep-0440/#version-specifiers
- docopt == 0.6.1 # Version Matching. Must be version 0.6.1
- keyring >= 4.1.1 # Minimum version 4.1.1
- coverage != 3.5 # Version Exclusion. Anything except version 3.5
- Mopidy-Dirble ~= 1.1 # Compatible release. Same as >= 1.1, == 1.*
- #
- ###### Refer to other requirements files ######
- -r other-requirements.txt
- #
- #
- ###### A particular file ######
- ./downloads/numpy-1.9.2-cp34-none-win32.whl
- http://wxpython.org/Phoenix/snapshot-builds/wxPython_Phoenix-3.0.3.dev1820+49a8884-cp34-none-win_amd64.whl
- #
- ###### Additional Requirements without Version Specifiers ######
- # Same as 1st section, just here to show that you can put things in any order.
- rejected
- green
- #
+This section has been moved to :doc:`../reference/requirements-file-format`.
.. _`Requirement Specifiers`:
diff --git a/docs/html/cli/pip_list.rst b/docs/html/cli/pip_list.rst
index cb40de67d..c84cb8c09 100644
--- a/docs/html/cli/pip_list.rst
+++ b/docs/html/cli/pip_list.rst
@@ -139,3 +139,93 @@ Examples
docopt==0.6.2
idlex==1.13
jedi==0.9.0
+
+#. List packages installed in editable mode
+
+When some packages are installed in editable mode, ``pip list`` outputs an
+additional column that shows the directory where the editable project is
+located (i.e. the directory that contains the ``pyproject.toml`` or
+``setup.py`` file).
+
+ .. tab:: Unix/macOS
+
+ .. code-block:: console
+
+ $ python -m pip list
+ Package Version Editable project location
+ ---------------- -------- -------------------------------------
+ pip 21.2.4
+ pip-test-package 0.1.1 /home/you/.venv/src/pip-test-package
+ setuptools 57.4.0
+ wheel 0.36.2
+
+
+ .. tab:: Windows
+
+ .. code-block:: console
+
+ C:\> py -m pip list
+ Package Version Editable project location
+ ---------------- -------- ----------------------------------------
+ pip 21.2.4
+ pip-test-package 0.1.1 C:\Users\You\.venv\src\pip-test-package
+ setuptools 57.4.0
+ wheel 0.36.2
+
+The json format outputs an additional ``editable_project_location`` field.
+
+ .. tab:: Unix/macOS
+
+ .. code-block:: console
+
+ $ python -m pip list --format=json | python -m json.tool
+ [
+ {
+ "name": "pip",
+ "version": "21.2.4",
+ },
+ {
+ "name": "pip-test-package",
+ "version": "0.1.1",
+ "editable_project_location": "/home/you/.venv/src/pip-test-package"
+ },
+ {
+ "name": "setuptools",
+ "version": "57.4.0"
+ },
+ {
+ "name": "wheel",
+ "version": "0.36.2"
+ }
+ ]
+
+ .. tab:: Windows
+
+ .. code-block:: console
+
+ C:\> py -m pip list --format=json | py -m json.tool
+ [
+ {
+ "name": "pip",
+ "version": "21.2.4",
+ },
+ {
+ "name": "pip-test-package",
+ "version": "0.1.1",
+ "editable_project_location": "C:\Users\You\.venv\src\pip-test-package"
+ },
+ {
+ "name": "setuptools",
+ "version": "57.4.0"
+ },
+ {
+ "name": "wheel",
+ "version": "0.36.2"
+ }
+ ]
+
+.. note::
+
+ Contrary to the ``freeze`` comand, ``pip list --format=freeze`` will not
+ report editable install information, but the version of the package at the
+ time it was installed.
diff --git a/docs/html/index.md b/docs/html/index.md
index 4b565b9a3..34a017449 100644
--- a/docs/html/index.md
+++ b/docs/html/index.md
@@ -14,6 +14,7 @@ getting-started
installation
user_guide
topics/index
+reference/index
cli/index
```
diff --git a/docs/html/reference/index.md b/docs/html/reference/index.md
new file mode 100644
index 000000000..13e57b2a4
--- /dev/null
+++ b/docs/html/reference/index.md
@@ -0,0 +1,10 @@
+# Reference
+
+Reference provides information about various file formats, interfaces and
+interoperability standards that pip utilises/implements.
+
+```{toctree}
+:titlesonly:
+
+requirements-file-format
+```
diff --git a/docs/html/reference/index.rst b/docs/html/reference/index.rst
deleted file mode 100644
index 5e81105c9..000000000
--- a/docs/html/reference/index.rst
+++ /dev/null
@@ -1,11 +0,0 @@
-:orphan:
-
-.. meta::
-
- :http-equiv=refresh: 3; url=../cli/
-
-This page has moved
-===================
-
-You should be redirected automatically in 3 seconds. If that didn't
-work, here's a link: :doc:`../cli/index`
diff --git a/docs/html/reference/requirements-file-format.md b/docs/html/reference/requirements-file-format.md
new file mode 100644
index 000000000..d05f1acca
--- /dev/null
+++ b/docs/html/reference/requirements-file-format.md
@@ -0,0 +1,147 @@
+(requirements-file-format)=
+
+# Requirements File Format
+
+Requirements files serve as a list of items to be installed by pip, when
+using {ref}`pip install`. Files that use this format are often called
+"pip requirements.txt files", since `requirements.txt` is usually what
+these files are named (although, that is not a requirement).
+
+```{note}
+The requirements file format is closely tied to a number of internal details of
+pip (e.g., pip's command line options). The basic format is relatively stable
+and portable but the full syntax, as described here, is only intended for
+consumption by pip, and other tools should take that into account before using
+it for their own purposes.
+```
+
+## Structure
+
+Each line of the requirements file indicates something to be installed,
+or arguments to {ref}`pip install`. The following forms are supported:
+
+- `[[--option]...]`
+- `<requirement specifier> [; markers] [[--option]...]`
+- `<archive url/path>`
+- `[-e] <local project path>`
+- `[-e] <vcs project url>`
+
+For details on requirement specifiers, see {ref}`Requirement Specifiers`. For
+examples of all these forms, see {ref}`pip install Examples`.
+
+### Encoding
+
+Requirements files are `utf-8` encoding by default and also support
+{pep}`263` style comments to change the encoding (i.e.
+`# -*- coding: <encoding name> -*-`).
+
+### Line continuations
+
+A line ending in an unescaped `\` is treated as a line continuation
+and the newline following it is effectively ignored.
+
+### Comments
+
+A line that begins with `#` is treated as a comment and ignored. Whitespace
+followed by a `#` causes the `#` and the remainder of the line to be
+treated as a comment.
+
+Comments are stripped _after_ line continuations are processed.
+
+## Supported options
+
+Requirements files only supports certain pip install options, which are listed
+below.
+
+### Global options
+
+The following options have an effect on the _entire_ `pip install` run, and
+must be specified on their individual lines.
+
+```{eval-rst}
+.. pip-requirements-file-options-ref-list::
+```
+
+````{admonition} Example
+To specify {ref}`--pre <install_--pre>`, {ref}`--no-index <install_--no-index>`
+and two {ref}`--find-links <install_--find-links>` locations:
+
+```
+--pre
+--no-index
+--find-links /my/local/archives
+--find-links http://some.archives.com/archives
+```
+````
+
+### Per-requirement options
+
+The options which can be applied to individual requirements are:
+
+- {ref}`--install-option <install_--install-option>`
+- {ref}`--global-option <install_--global-option>`
+- `--hash` (for {ref}`Hash-Checking mode`)
+
+If you wish, you can refer to other requirements files, like this:
+
+```
+-r more_requirements.txt
+```
+
+You can also refer to {ref}`constraints files <Constraints Files>`, like this:
+
+```
+-c some_constraints.txt
+```
+
+## Using environment variables
+
+```{versionadded} 10.0
+
+```
+
+pip supports the use of environment variables inside the
+requirements file.
+
+You have to use the POSIX format for variable names including brackets around
+the uppercase name as shown in this example: `${API_TOKEN}`. pip will attempt
+to find the corresponding environment variable defined on the host system at
+runtime.
+
+```{note}
+There is no support for other variable expansion syntaxes such as `$VARIABLE`
+and `%VARIABLE%`.
+```
+
+You can now store sensitive data (tokens, keys, etc.) in environment variables
+and only specify the variable name for your requirements, letting pip lookup
+the value at runtime. This approach aligns with the commonly used
+[12-factor configuration pattern](https://12factor.net/config).
+
+## Example
+
+```
+###### Requirements without Version Specifiers ######
+pytest
+pytest-cov
+beautifulsoup4
+
+###### Requirements with Version Specifiers ######
+# See https://www.python.org/dev/peps/pep-0440/#version-specifiers
+docopt == 0.6.1 # Version Matching. Must be version 0.6.1
+keyring >= 4.1.1 # Minimum version 4.1.1
+coverage != 3.5 # Version Exclusion. Anything except version 3.5
+Mopidy-Dirble ~= 1.1 # Compatible release. Same as >= 1.1, == 1.*
+
+###### Refer to other requirements files ######
+-r other-requirements.txt
+
+###### A particular file ######
+./downloads/numpy-1.9.2-cp34-none-win32.whl
+http://wxpython.org/Phoenix/snapshot-builds/wxPython_Phoenix-3.0.3.dev1820+49a8884-cp34-none-win_amd64.whl
+
+###### Additional Requirements without Version Specifiers ######
+# Same as 1st section, just here to show that you can put things in any order.
+rejected
+green
+```
diff --git a/docs/html/user_guide.rst b/docs/html/user_guide.rst
index 2d0597092..059fd7cdc 100644
--- a/docs/html/user_guide.rst
+++ b/docs/html/user_guide.rst
@@ -113,7 +113,7 @@ installed using :ref:`pip install` like so:
py -m pip install -r requirements.txt
-Details on the format of the files are here: :ref:`Requirements File Format`.
+Details on the format of the files are here: :ref:`requirements-file-format`.
Logically, a Requirements file is just a list of :ref:`pip install` arguments
placed in a file. Note that you should not rely on the items in the file being
@@ -185,7 +185,7 @@ not by discovering ``requirements.txt`` files embedded in projects.
See also:
-* :ref:`Requirements File Format`
+* :ref:`requirements-file-format`
* :ref:`pip freeze`
* `"setup.py vs requirements.txt" (an article by Donald Stufft)
<https://caremad.io/2013/07/setup-vs-requirement/>`_
diff --git a/news/0b3269eb-560c-4b8e-8553-9fe205dc62ec.trivial.rst b/news/0b3269eb-560c-4b8e-8553-9fe205dc62ec.trivial.rst
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/news/0b3269eb-560c-4b8e-8553-9fe205dc62ec.trivial.rst
diff --git a/news/10249.feature.rst b/news/10249.feature.rst
new file mode 100644
index 000000000..baf8395b1
--- /dev/null
+++ b/news/10249.feature.rst
@@ -0,0 +1,4 @@
+Support `PEP 610 <https://www.python.org/dev/peps/pep-0610/>`_ to detect
+editable installs in ``pip freeze`` and ``pip list``. The ``pip list`` column output
+has a new ``Editable project location`` column, and the JSON output has a new
+``editable_project_location`` field.
diff --git a/news/10269.bugfix.rst b/news/10269.bugfix.rst
new file mode 100644
index 000000000..45aee70d8
--- /dev/null
+++ b/news/10269.bugfix.rst
@@ -0,0 +1,3 @@
+Fix the auth credential cache to allow for the case in which
+the index url contains the username, but the password comes
+from an external source, such as keyring.
diff --git a/news/10361.trivial.rst b/news/10361.trivial.rst
new file mode 100644
index 000000000..05b3cb10f
--- /dev/null
+++ b/news/10361.trivial.rst
@@ -0,0 +1 @@
+Added an explicit warning when pip is unable to parse git version.
diff --git a/news/10410.feature.rst b/news/10410.feature.rst
new file mode 100644
index 000000000..e3bfdc83b
--- /dev/null
+++ b/news/10410.feature.rst
@@ -0,0 +1,3 @@
+``pip freeze`` will now always fallback to reporting the editable project
+location when it encounters a VCS error while analyzing an editable
+requirement. Before, it sometimes reported the requirement as non-editable.
diff --git a/news/10418.trivial.rst b/news/10418.trivial.rst
new file mode 100644
index 000000000..d427a75f8
--- /dev/null
+++ b/news/10418.trivial.rst
@@ -0,0 +1 @@
+Make _load_file log become verbose instead of debug.
diff --git a/news/10422.feature.rst b/news/10422.feature.rst
new file mode 100644
index 000000000..d4d6d824c
--- /dev/null
+++ b/news/10422.feature.rst
@@ -0,0 +1 @@
+``pip show`` now sorts ``Requires`` and ``Required-By`` alphabetically.
diff --git a/news/118e193d-d2f2-4e70-9767-ba5dddf7d263.trivial.rst b/news/118e193d-d2f2-4e70-9767-ba5dddf7d263.trivial.rst
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/news/118e193d-d2f2-4e70-9767-ba5dddf7d263.trivial.rst
diff --git a/news/3332a3c1-fd62-4d5a-8bc6-459fd677ca70.trivial.rst b/news/3332a3c1-fd62-4d5a-8bc6-459fd677ca70.trivial.rst
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/news/3332a3c1-fd62-4d5a-8bc6-459fd677ca70.trivial.rst
diff --git a/news/52ff07b1-69ed-4bbe-a3be-913e0e247cee.trivial.rst b/news/52ff07b1-69ed-4bbe-a3be-913e0e247cee.trivial.rst
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/news/52ff07b1-69ed-4bbe-a3be-913e0e247cee.trivial.rst
diff --git a/news/569d0d85-be83-40ab-9229-1a6327060a3f.trivial.rst b/news/569d0d85-be83-40ab-9229-1a6327060a3f.trivial.rst
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/news/569d0d85-be83-40ab-9229-1a6327060a3f.trivial.rst
diff --git a/news/6a2297c8-25c3-4c79-b856-03f0184af36f.trivial.rst b/news/6a2297c8-25c3-4c79-b856-03f0184af36f.trivial.rst
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/news/6a2297c8-25c3-4c79-b856-03f0184af36f.trivial.rst
diff --git a/news/77150b20-02ed-411a-ad49-90afdc0a4b53.trivial.rst b/news/77150b20-02ed-411a-ad49-90afdc0a4b53.trivial.rst
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/news/77150b20-02ed-411a-ad49-90afdc0a4b53.trivial.rst
diff --git a/news/9349.feature.rst b/news/9349.feature.rst
new file mode 100644
index 000000000..7d8744bc6
--- /dev/null
+++ b/news/9349.feature.rst
@@ -0,0 +1 @@
+Add a ``--debug`` flag, to enable a mode that doesn't log errors and propagates them to the top level instead. This is primarily to aid with debugging pip's crashes.
diff --git a/news/9498.feature.rst b/news/9498.feature.rst
new file mode 100644
index 000000000..0682915e4
--- /dev/null
+++ b/news/9498.feature.rst
@@ -0,0 +1 @@
+If a host is explicitly specified as trusted by the user (via the --trusted-host option), cache HTTP responses from it in addition to HTTPS ones.
diff --git a/src/pip/_internal/cli/base_command.py b/src/pip/_internal/cli/base_command.py
index 16a8faca8..5932eedc2 100644
--- a/src/pip/_internal/cli/base_command.py
+++ b/src/pip/_internal/cli/base_command.py
@@ -1,5 +1,6 @@
"""Base Command class, and related routines"""
+import functools
import logging
import logging.config
import optparse
@@ -7,7 +8,7 @@ import os
import sys
import traceback
from optparse import Values
-from typing import List, Optional, Tuple
+from typing import Any, Callable, List, Optional, Tuple
from pip._internal.cli import cmdoptions
from pip._internal.cli.command_context import CommandContextMixIn
@@ -169,46 +170,60 @@ class Command(CommandContextMixIn):
"This will become an error in pip 21.0."
)
+ def intercepts_unhandled_exc(
+ run_func: Callable[..., int]
+ ) -> Callable[..., int]:
+ @functools.wraps(run_func)
+ def exc_logging_wrapper(*args: Any) -> int:
+ try:
+ status = run_func(*args)
+ assert isinstance(status, int)
+ return status
+ except PreviousBuildDirError as exc:
+ logger.critical(str(exc))
+ logger.debug("Exception information:", exc_info=True)
+
+ return PREVIOUS_BUILD_DIR_ERROR
+ except (
+ InstallationError,
+ UninstallationError,
+ BadCommand,
+ NetworkConnectionError,
+ ) as exc:
+ logger.critical(str(exc))
+ logger.debug("Exception information:", exc_info=True)
+
+ return ERROR
+ except CommandError as exc:
+ logger.critical("%s", exc)
+ logger.debug("Exception information:", exc_info=True)
+
+ return ERROR
+ except BrokenStdoutLoggingError:
+ # Bypass our logger and write any remaining messages to
+ # stderr because stdout no longer works.
+ print("ERROR: Pipe to stdout was broken", file=sys.stderr)
+ if level_number <= logging.DEBUG:
+ traceback.print_exc(file=sys.stderr)
+
+ return ERROR
+ except KeyboardInterrupt:
+ logger.critical("Operation cancelled by user")
+ logger.debug("Exception information:", exc_info=True)
+
+ return ERROR
+ except BaseException:
+ logger.critical("Exception:", exc_info=True)
+
+ return UNKNOWN_ERROR
+
+ return exc_logging_wrapper
+
try:
- status = self.run(options, args)
- assert isinstance(status, int)
- return status
- except PreviousBuildDirError as exc:
- logger.critical(str(exc))
- logger.debug("Exception information:", exc_info=True)
-
- return PREVIOUS_BUILD_DIR_ERROR
- except (
- InstallationError,
- UninstallationError,
- BadCommand,
- NetworkConnectionError,
- ) as exc:
- logger.critical(str(exc))
- logger.debug("Exception information:", exc_info=True)
-
- return ERROR
- except CommandError as exc:
- logger.critical("%s", exc)
- logger.debug("Exception information:", exc_info=True)
-
- return ERROR
- except BrokenStdoutLoggingError:
- # Bypass our logger and write any remaining messages to stderr
- # because stdout no longer works.
- print("ERROR: Pipe to stdout was broken", file=sys.stderr)
- if level_number <= logging.DEBUG:
- traceback.print_exc(file=sys.stderr)
-
- return ERROR
- except KeyboardInterrupt:
- logger.critical("Operation cancelled by user")
- logger.debug("Exception information:", exc_info=True)
-
- return ERROR
- except BaseException:
- logger.critical("Exception:", exc_info=True)
-
- return UNKNOWN_ERROR
+ if not options.debug_mode:
+ run = intercepts_unhandled_exc(self.run)
+ else:
+ run = self.run
+ return run(options, args)
finally:
self.handle_pip_version_check(options)
diff --git a/src/pip/_internal/cli/cmdoptions.py b/src/pip/_internal/cli/cmdoptions.py
index b4f0f83c6..e24037800 100644
--- a/src/pip/_internal/cli/cmdoptions.py
+++ b/src/pip/_internal/cli/cmdoptions.py
@@ -151,6 +151,18 @@ help_: Callable[..., Option] = partial(
help="Show help.",
)
+debug_mode: Callable[..., Option] = partial(
+ Option,
+ "--debug",
+ dest="debug_mode",
+ action="store_true",
+ default=False,
+ help=(
+ "Let unhandled exceptions propagate outside the main subroutine, "
+ "instead of logging them to stderr."
+ ),
+)
+
isolated_mode: Callable[..., Option] = partial(
Option,
"--isolated",
@@ -974,6 +986,7 @@ general_group: Dict[str, Any] = {
"name": "General Options",
"options": [
help_,
+ debug_mode,
isolated_mode,
require_virtualenv,
verbose,
diff --git a/src/pip/_internal/commands/list.py b/src/pip/_internal/commands/list.py
index abe6ef2fc..75d8dd465 100644
--- a/src/pip/_internal/commands/list.py
+++ b/src/pip/_internal/commands/list.py
@@ -14,7 +14,8 @@ from pip._internal.index.package_finder import PackageFinder
from pip._internal.metadata import BaseDistribution, get_environment
from pip._internal.models.selection_prefs import SelectionPreferences
from pip._internal.network.session import PipSession
-from pip._internal.utils.misc import stdlib_pkgs, tabulate, write_output
+from pip._internal.utils.compat import stdlib_pkgs
+from pip._internal.utils.misc import tabulate, write_output
from pip._internal.utils.parallel import map_multithread
if TYPE_CHECKING:
@@ -302,19 +303,22 @@ def format_for_columns(
Convert the package data into something usable
by output_package_listing_columns.
"""
+ header = ["Package", "Version"]
+
running_outdated = options.outdated
- # Adjust the header for the `pip list --outdated` case.
if running_outdated:
- header = ["Package", "Version", "Latest", "Type"]
- else:
- header = ["Package", "Version"]
+ header.extend(["Latest", "Type"])
- data = []
- if options.verbose >= 1 or any(x.editable for x in pkgs):
+ has_editables = any(x.editable for x in pkgs)
+ if has_editables:
+ header.append("Editable project location")
+
+ if options.verbose >= 1:
header.append("Location")
if options.verbose >= 1:
header.append("Installer")
+ data = []
for proj in pkgs:
# if we're working on the 'outdated' list, separate out the
# latest_version and type
@@ -324,7 +328,10 @@ def format_for_columns(
row.append(str(proj.latest_version))
row.append(proj.latest_filetype)
- if options.verbose >= 1 or proj.editable:
+ if has_editables:
+ row.append(proj.editable_project_location or "")
+
+ if options.verbose >= 1:
row.append(proj.location or "")
if options.verbose >= 1:
row.append(proj.installer)
@@ -347,5 +354,8 @@ def format_for_json(packages: "_ProcessedDists", options: Values) -> str:
if options.outdated:
info["latest_version"] = str(dist.latest_version)
info["latest_filetype"] = dist.latest_filetype
+ editable_project_location = dist.editable_project_location
+ if editable_project_location:
+ info["editable_project_location"] = editable_project_location
data.append(info)
return json.dumps(data)
diff --git a/src/pip/_internal/commands/show.py b/src/pip/_internal/commands/show.py
index 0bbe1209a..872292a2b 100644
--- a/src/pip/_internal/commands/show.py
+++ b/src/pip/_internal/commands/show.py
@@ -113,13 +113,13 @@ def search_packages_info(query: List[str]) -> Iterator[_PackageInfo]:
if missing:
logger.warning("Package(s) not found: %s", ", ".join(missing))
- def _get_requiring_packages(current_dist: BaseDistribution) -> List[str]:
- return [
+ def _get_requiring_packages(current_dist: BaseDistribution) -> Iterator[str]:
+ return (
dist.metadata["Name"] or "UNKNOWN"
for dist in installed.values()
if current_dist.canonical_name
in {canonicalize_name(d.name) for d in dist.iter_dependencies()}
- ]
+ )
def _files_from_record(dist: BaseDistribution) -> Optional[Iterator[str]]:
try:
@@ -155,6 +155,9 @@ def search_packages_info(query: List[str]) -> Iterator[_PackageInfo]:
except KeyError:
continue
+ requires = sorted((req.name for req in dist.iter_dependencies()), key=str.lower)
+ required_by = sorted(_get_requiring_packages(dist), key=str.lower)
+
try:
entry_points_text = dist.read_text("entry_points.txt")
entry_points = entry_points_text.splitlines(keepends=False)
@@ -173,8 +176,8 @@ def search_packages_info(query: List[str]) -> Iterator[_PackageInfo]:
name=dist.raw_name,
version=str(dist.version),
location=dist.location or "",
- requires=[req.name for req in dist.iter_dependencies()],
- required_by=_get_requiring_packages(dist),
+ requires=requires,
+ required_by=required_by,
installer=dist.installer,
metadata_version=dist.metadata_version or "",
classifiers=metadata.get_all("Classifier", []),
diff --git a/src/pip/_internal/configuration.py b/src/pip/_internal/configuration.py
index 3b54c0d69..4c3a362fd 100644
--- a/src/pip/_internal/configuration.py
+++ b/src/pip/_internal/configuration.py
@@ -13,7 +13,6 @@ Some terminology:
import configparser
import locale
-import logging
import os
import sys
from typing import Any, Dict, Iterable, List, NewType, Optional, Tuple
@@ -24,6 +23,7 @@ from pip._internal.exceptions import (
)
from pip._internal.utils import appdirs
from pip._internal.utils.compat import WINDOWS
+from pip._internal.utils.logging import getLogger
from pip._internal.utils.misc import ensure_dir, enum
RawConfigParser = configparser.RawConfigParser # Shorthand
@@ -43,7 +43,7 @@ kinds = enum(
OVERRIDE_ORDER = kinds.GLOBAL, kinds.USER, kinds.SITE, kinds.ENV, kinds.ENV_VAR
VALID_LOAD_ONLY = kinds.USER, kinds.GLOBAL, kinds.SITE
-logger = logging.getLogger(__name__)
+logger = getLogger(__name__)
# NOTE: Maybe use the optionx attribute to normalize keynames.
@@ -250,7 +250,7 @@ class Configuration:
self._parsers[variant].append((fname, parser))
def _load_file(self, variant: Kind, fname: str) -> RawConfigParser:
- logger.debug("For variant '%s', will try loading '%s'", variant, fname)
+ logger.verbose("For variant '%s', will try loading '%s'", variant, fname)
parser = self._construct_parser(fname)
for section in parser.sections():
diff --git a/src/pip/_internal/metadata/base.py b/src/pip/_internal/metadata/base.py
index e1b229d1f..7eacd00e2 100644
--- a/src/pip/_internal/metadata/base.py
+++ b/src/pip/_internal/metadata/base.py
@@ -24,7 +24,9 @@ from pip._internal.models.direct_url import (
DirectUrl,
DirectUrlValidationError,
)
-from pip._internal.utils.misc import stdlib_pkgs # TODO: Move definition here.
+from pip._internal.utils.compat import stdlib_pkgs # TODO: Move definition here.
+from pip._internal.utils.egg_link import egg_link_path_from_sys_path
+from pip._internal.utils.urls import url_to_path
if TYPE_CHECKING:
from typing import Protocol
@@ -74,6 +76,28 @@ class BaseDistribution(Protocol):
raise NotImplementedError()
@property
+ def editable_project_location(self) -> Optional[str]:
+ """The project location for editable distributions.
+
+ This is the directory where pyproject.toml or setup.py is located.
+ None if the distribution is not installed in editable mode.
+ """
+ # TODO: this property is relatively costly to compute, memoize it ?
+ direct_url = self.direct_url
+ if direct_url:
+ if direct_url.is_local_editable():
+ return url_to_path(direct_url.url)
+ else:
+ # Search for an .egg-link file by walking sys.path, as it was
+ # done before by dist_is_editable().
+ egg_link_path = egg_link_path_from_sys_path(self.raw_name)
+ if egg_link_path:
+ # TODO: get project location from second line of egg_link file
+ # (https://github.com/pypa/pip/issues/10243)
+ return self.location
+ return None
+
+ @property
def info_directory(self) -> Optional[str]:
"""Location of the .[egg|dist]-info directory.
@@ -129,7 +153,7 @@ class BaseDistribution(Protocol):
@property
def editable(self) -> bool:
- raise NotImplementedError()
+ return bool(self.editable_project_location)
@property
def local(self) -> bool:
diff --git a/src/pip/_internal/metadata/pkg_resources.py b/src/pip/_internal/metadata/pkg_resources.py
index 75fd3518f..35e63f2c5 100644
--- a/src/pip/_internal/metadata/pkg_resources.py
+++ b/src/pip/_internal/metadata/pkg_resources.py
@@ -70,10 +70,6 @@ class Distribution(BaseDistribution):
return get_installer(self._dist)
@property
- def editable(self) -> bool:
- return misc.dist_is_editable(self._dist)
-
- @property
def local(self) -> bool:
return misc.dist_is_local(self._dist)
diff --git a/src/pip/_internal/models/direct_url.py b/src/pip/_internal/models/direct_url.py
index d652ce153..92060d45d 100644
--- a/src/pip/_internal/models/direct_url.py
+++ b/src/pip/_internal/models/direct_url.py
@@ -215,3 +215,6 @@ class DirectUrl:
def to_json(self) -> str:
return json.dumps(self.to_dict(), sort_keys=True)
+
+ def is_local_editable(self) -> bool:
+ return isinstance(self.info, DirInfo) and self.info.editable
diff --git a/src/pip/_internal/network/auth.py b/src/pip/_internal/network/auth.py
index ee27fb67a..ca42798bd 100644
--- a/src/pip/_internal/network/auth.py
+++ b/src/pip/_internal/network/auth.py
@@ -179,9 +179,16 @@ class MultiDomainBasicAuth(AuthBase):
# Try to get credentials from original url
username, password = self._get_new_credentials(original_url)
- # If credentials not found, use any stored credentials for this netloc
- if username is None and password is None:
- username, password = self.passwords.get(netloc, (None, None))
+ # If credentials not found, use any stored credentials for this netloc.
+ # Do this if either the username or the password is missing.
+ # This accounts for the situation in which the user has specified
+ # the username in the index url, but the password comes from keyring.
+ if (username is None or password is None) and netloc in self.passwords:
+ un, pw = self.passwords[netloc]
+ # It is possible that the cached credentials are for a different username,
+ # in which case the cache should be ignored.
+ if username is None or username == un:
+ username, password = un, pw
if username is not None or password is not None:
# Convert the username and password if they're None, so that
diff --git a/src/pip/_internal/network/session.py b/src/pip/_internal/network/session.py
index fe1cf717e..e2916ca81 100644
--- a/src/pip/_internal/network/session.py
+++ b/src/pip/_internal/network/session.py
@@ -358,8 +358,15 @@ class PipSession(requests.Session):
if host_port not in self.pip_trusted_origins:
self.pip_trusted_origins.append(host_port)
+ self.mount(
+ build_url_from_netloc(host, scheme="http") + "/", self._trusted_host_adapter
+ )
self.mount(build_url_from_netloc(host) + "/", self._trusted_host_adapter)
if not host_port[1]:
+ self.mount(
+ build_url_from_netloc(host, scheme="http") + ":",
+ self._trusted_host_adapter,
+ )
# Mount wildcard ports for the same host.
self.mount(build_url_from_netloc(host) + ":", self._trusted_host_adapter)
diff --git a/src/pip/_internal/operations/freeze.py b/src/pip/_internal/operations/freeze.py
index 518e95c8b..456554085 100644
--- a/src/pip/_internal/operations/freeze.py
+++ b/src/pip/_internal/operations/freeze.py
@@ -1,19 +1,8 @@
import collections
import logging
import os
-from typing import (
- Container,
- Dict,
- Iterable,
- Iterator,
- List,
- NamedTuple,
- Optional,
- Set,
- Union,
-)
+from typing import Container, Dict, Iterable, Iterator, List, NamedTuple, Optional, Set
-from pip._vendor.packaging.requirements import Requirement
from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.packaging.version import Version
@@ -30,8 +19,7 @@ logger = logging.getLogger(__name__)
class _EditableInfo(NamedTuple):
- requirement: Optional[str]
- editable: bool
+ requirement: str
comments: List[str]
@@ -164,21 +152,12 @@ def _format_as_name_version(dist: BaseDistribution) -> str:
def _get_editable_info(dist: BaseDistribution) -> _EditableInfo:
"""
- Compute and return values (req, editable, comments) for use in
+ Compute and return values (req, comments) for use in
FrozenRequirement.from_dist().
"""
- if not dist.editable:
- return _EditableInfo(requirement=None, editable=False, comments=[])
- if dist.location is None:
- display = _format_as_name_version(dist)
- logger.warning("Editable requirement not found on disk: %s", display)
- return _EditableInfo(
- requirement=None,
- editable=True,
- comments=[f"# Editable install not found ({display})"],
- )
-
- location = os.path.normcase(os.path.abspath(dist.location))
+ editable_project_location = dist.editable_project_location
+ assert editable_project_location
+ location = os.path.normcase(os.path.abspath(editable_project_location))
from pip._internal.vcs import RemoteNotFoundError, RemoteNotValidError, vcs
@@ -193,7 +172,6 @@ def _get_editable_info(dist: BaseDistribution) -> _EditableInfo:
)
return _EditableInfo(
requirement=location,
- editable=True,
comments=[f"# Editable install with no version control ({display})"],
)
@@ -205,21 +183,18 @@ def _get_editable_info(dist: BaseDistribution) -> _EditableInfo:
display = _format_as_name_version(dist)
return _EditableInfo(
requirement=location,
- editable=True,
comments=[f"# Editable {vcs_name} install with no remote ({display})"],
)
except RemoteNotValidError as ex:
display = _format_as_name_version(dist)
return _EditableInfo(
requirement=location,
- editable=True,
comments=[
f"# Editable {vcs_name} install ({display}) with either a deleted "
f"local remote or invalid URI:",
f"# '{ex.url}'",
],
)
-
except BadCommand:
logger.warning(
"cannot determine version of editable source in %s "
@@ -227,22 +202,16 @@ def _get_editable_info(dist: BaseDistribution) -> _EditableInfo:
location,
vcs_backend.name,
)
- return _EditableInfo(requirement=None, editable=True, comments=[])
-
+ return _EditableInfo(requirement=location, comments=[])
except InstallationError as exc:
- logger.warning(
- "Error when trying to get requirement for VCS system %s, "
- "falling back to uneditable format",
- exc,
- )
+ logger.warning("Error when trying to get requirement for VCS system %s", exc)
else:
- return _EditableInfo(requirement=req, editable=True, comments=[])
+ return _EditableInfo(requirement=req, comments=[])
logger.warning("Could not determine repository location of %s", location)
return _EditableInfo(
- requirement=None,
- editable=False,
+ requirement=location,
comments=["## !! Could not determine repository location"],
)
@@ -251,7 +220,7 @@ class FrozenRequirement:
def __init__(
self,
name: str,
- req: Union[str, Requirement],
+ req: str,
editable: bool,
comments: Iterable[str] = (),
) -> None:
@@ -263,19 +232,18 @@ class FrozenRequirement:
@classmethod
def from_dist(cls, dist: BaseDistribution) -> "FrozenRequirement":
- # TODO `get_requirement_info` is taking care of editable requirements.
- # TODO This should be refactored when we will add detection of
- # editable that provide .dist-info metadata.
- req, editable, comments = _get_editable_info(dist)
- if req is None and not editable:
- # if PEP 610 metadata is present, attempt to use it
+ editable = dist.editable
+ if editable:
+ req, comments = _get_editable_info(dist)
+ else:
+ comments = []
direct_url = dist.direct_url
if direct_url:
+ # if PEP 610 metadata is present, use it
req = direct_url_as_pep440_direct_reference(direct_url, dist.raw_name)
- comments = []
- if req is None:
- # name==version requirement
- req = _format_as_name_version(dist)
+ else:
+ # name==version requirement
+ req = _format_as_name_version(dist)
return cls(dist.raw_name, req, editable, comments=comments)
diff --git a/src/pip/_internal/operations/install/wheel.py b/src/pip/_internal/operations/install/wheel.py
index 075317622..e191b1343 100644
--- a/src/pip/_internal/operations/install/wheel.py
+++ b/src/pip/_internal/operations/install/wheel.py
@@ -38,7 +38,6 @@ from zipfile import ZipFile, ZipInfo
from pip._vendor.distlib.scripts import ScriptMaker
from pip._vendor.distlib.util import get_export_entry
from pip._vendor.packaging.utils import canonicalize_name
-from pip._vendor.six import ensure_str, ensure_text, reraise
from pip._internal.exceptions import InstallationError
from pip._internal.locations import get_major_minor_version
@@ -220,8 +219,7 @@ def _normalized_outrows(
# For additional background, see--
# https://github.com/pypa/pip/issues/5868
return sorted(
- (ensure_str(record_path, encoding="utf-8"), hash_, str(size))
- for record_path, hash_, size in outrows
+ (record_path, hash_, str(size)) for record_path, hash_, size in outrows
)
@@ -242,11 +240,6 @@ def _fs_to_record_path(path: str, relative_to: Optional[str] = None) -> RecordPa
return cast("RecordPath", path)
-def _parse_record_path(record_column: str) -> RecordPath:
- p = ensure_text(record_column, encoding="utf-8")
- return cast("RecordPath", p)
-
-
def get_csv_rows_for_installed(
old_csv_rows: List[List[str]],
installed: Dict[RecordPath, RecordPath],
@@ -262,7 +255,7 @@ def get_csv_rows_for_installed(
for row in old_csv_rows:
if len(row) > 3:
logger.warning("RECORD line has more than three elements: %s", row)
- old_record_path = _parse_record_path(row[0])
+ old_record_path = cast("RecordPath", row[0])
new_record_path = installed.pop(old_record_path, old_record_path)
if new_record_path in changed:
digest, length = rehash(_record_to_fs_path(new_record_path))
@@ -483,14 +476,6 @@ def _install_wheel(
if modified:
changed.add(_fs_to_record_path(destfile))
- def all_paths() -> Iterable[RecordPath]:
- names = wheel_zip.namelist()
- # If a flag is set, names may be unicode in Python 2. We convert to
- # text explicitly so these are valid for lookup in RECORD.
- decoded_names = map(ensure_text, names)
- for name in decoded_names:
- yield cast("RecordPath", name)
-
def is_dir_path(path: RecordPath) -> bool:
return path.endswith("/")
@@ -518,12 +503,7 @@ def _install_wheel(
def data_scheme_file_maker(
zip_file: ZipFile, scheme: Scheme
) -> Callable[[RecordPath], "File"]:
- scheme_paths = {}
- for key in SCHEME_KEYS:
- encoded_key = ensure_text(key)
- scheme_paths[encoded_key] = ensure_text(
- getattr(scheme, key), encoding=sys.getfilesystemencoding()
- )
+ scheme_paths = {key: getattr(scheme, key) for key in SCHEME_KEYS}
def make_data_scheme_file(record_path: RecordPath) -> "File":
normed_path = os.path.normpath(record_path)
@@ -556,14 +536,11 @@ def _install_wheel(
def is_data_scheme_path(path: RecordPath) -> bool:
return path.split("/", 1)[0].endswith(".data")
- paths = all_paths()
+ paths = cast(List[RecordPath], wheel_zip.namelist())
file_paths = filterfalse(is_dir_path, paths)
root_scheme_paths, data_scheme_paths = partition(is_data_scheme_path, file_paths)
- make_root_scheme_file = root_scheme_file_maker(
- wheel_zip,
- ensure_text(lib_dir, encoding=sys.getfilesystemencoding()),
- )
+ make_root_scheme_file = root_scheme_file_maker(wheel_zip, lib_dir)
files: Iterator[File] = map(make_root_scheme_file, root_scheme_paths)
def is_script_scheme_path(path: RecordPath) -> bool:
@@ -722,9 +699,8 @@ def _install_wheel(
record_path = os.path.join(dest_info_dir, "RECORD")
with _generate_file(record_path, **csv_io_kwargs("w")) as record_file:
- # The type mypy infers for record_file is different for Python 3
- # (typing.IO[Any]) and Python 2 (typing.BinaryIO). We explicitly
- # cast to typing.IO[str] as a workaround.
+ # Explicitly cast to typing.IO[str] as a workaround for the mypy error:
+ # "writer" has incompatible type "BinaryIO"; expected "_Writer"
writer = csv.writer(cast("IO[str]", record_file))
writer.writerows(_normalized_outrows(rows))
@@ -735,7 +711,7 @@ def req_error_context(req_description: str) -> Iterator[None]:
yield
except InstallationError as e:
message = "For req: {}. {}".format(req_description, e.args[0])
- reraise(InstallationError, InstallationError(message), sys.exc_info()[2])
+ raise InstallationError(message) from e
def install_wheel(
diff --git a/src/pip/_internal/req/req_uninstall.py b/src/pip/_internal/req/req_uninstall.py
index ef7352f7b..779e93b44 100644
--- a/src/pip/_internal/req/req_uninstall.py
+++ b/src/pip/_internal/req/req_uninstall.py
@@ -12,12 +12,12 @@ from pip._vendor.pkg_resources import Distribution
from pip._internal.exceptions import UninstallationError
from pip._internal.locations import get_bin_prefix, get_bin_user
from pip._internal.utils.compat import WINDOWS
+from pip._internal.utils.egg_link import egg_link_path_from_location
from pip._internal.utils.logging import getLogger, indent_log
from pip._internal.utils.misc import (
ask,
dist_in_usersite,
dist_is_local,
- egg_link_path,
is_local,
normalize_path,
renames,
@@ -459,7 +459,7 @@ class UninstallPathSet:
return cls(dist)
paths_to_remove = cls(dist)
- develop_egg_link = egg_link_path(dist)
+ develop_egg_link = egg_link_path_from_location(dist.project_name)
develop_egg_link_egg_info = "{}.egg-info".format(
pkg_resources.to_filename(dist.project_name)
)
diff --git a/src/pip/_internal/utils/egg_link.py b/src/pip/_internal/utils/egg_link.py
new file mode 100644
index 000000000..9e0da8d2d
--- /dev/null
+++ b/src/pip/_internal/utils/egg_link.py
@@ -0,0 +1,75 @@
+# The following comment should be removed at some point in the future.
+# mypy: strict-optional=False
+
+import os
+import re
+import sys
+from typing import Optional
+
+from pip._internal.locations import site_packages, user_site
+from pip._internal.utils.virtualenv import (
+ running_under_virtualenv,
+ virtualenv_no_global,
+)
+
+__all__ = [
+ "egg_link_path_from_sys_path",
+ "egg_link_path_from_location",
+]
+
+
+def _egg_link_name(raw_name: str) -> str:
+ """
+ Convert a Name metadata value to a .egg-link name, by applying
+ the same substitution as pkg_resources's safe_name function.
+ Note: we cannot use canonicalize_name because it has a different logic.
+ """
+ return re.sub("[^A-Za-z0-9.]+", "-", raw_name) + ".egg-link"
+
+
+def egg_link_path_from_sys_path(raw_name: str) -> Optional[str]:
+ """
+ Look for a .egg-link file for project name, by walking sys.path.
+ """
+ egg_link_name = _egg_link_name(raw_name)
+ for path_item in sys.path:
+ egg_link = os.path.join(path_item, egg_link_name)
+ if os.path.isfile(egg_link):
+ return egg_link
+ return None
+
+
+def egg_link_path_from_location(raw_name: str) -> Optional[str]:
+ """
+ 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():
+ sites.append(site_packages)
+ if not virtualenv_no_global() and user_site:
+ sites.append(user_site)
+ else:
+ if user_site:
+ sites.append(user_site)
+ sites.append(site_packages)
+
+ egg_link_name = _egg_link_name(raw_name)
+ for site in sites:
+ egglink = os.path.join(site, egg_link_name)
+ if os.path.isfile(egglink):
+ return egglink
+ return None
diff --git a/src/pip/_internal/utils/misc.py b/src/pip/_internal/utils/misc.py
index 8b1081f29..d3e9053ef 100644
--- a/src/pip/_internal/utils/misc.py
+++ b/src/pip/_internal/utils/misc.py
@@ -20,7 +20,6 @@ from typing import (
Any,
BinaryIO,
Callable,
- Container,
ContextManager,
Iterable,
Iterator,
@@ -39,11 +38,9 @@ from pip._vendor.tenacity import retry, stop_after_delay, wait_fixed
from pip import __version__
from pip._internal.exceptions import CommandError
from pip._internal.locations import get_major_minor_version, site_packages, user_site
-from pip._internal.utils.compat import WINDOWS, stdlib_pkgs
-from pip._internal.utils.virtualenv import (
- running_under_virtualenv,
- virtualenv_no_global,
-)
+from pip._internal.utils.compat import WINDOWS
+from pip._internal.utils.egg_link import egg_link_path_from_location
+from pip._internal.utils.virtualenv import running_under_virtualenv
__all__ = [
"rmtree",
@@ -357,46 +354,6 @@ def dist_in_site_packages(dist: Distribution) -> bool:
return dist_location(dist).startswith(normalize_path(site_packages))
-def dist_is_editable(dist: Distribution) -> bool:
- """
- Return True if given Distribution is an editable install.
- """
- for path_item in sys.path:
- egg_link = os.path.join(path_item, dist.project_name + ".egg-link")
- if os.path.isfile(egg_link):
- return True
- return False
-
-
-def get_installed_distributions(
- local_only: bool = True,
- skip: Container[str] = stdlib_pkgs,
- include_editables: bool = True,
- editables_only: bool = False,
- user_only: bool = False,
- paths: Optional[List[str]] = None,
-) -> List[Distribution]:
- """Return a list of installed Distribution objects.
-
- Left for compatibility until direct pkg_resources uses are refactored out.
- """
- from pip._internal.metadata import get_default_environment, get_environment
- from pip._internal.metadata.pkg_resources import Distribution as _Dist
-
- if paths is None:
- env = get_default_environment()
- else:
- env = get_environment(paths)
- dists = env.iter_installed_distributions(
- local_only=local_only,
- skip=skip,
- include_editables=include_editables,
- editables_only=editables_only,
- user_only=user_only,
- )
- return [cast(_Dist, dist)._dist for dist in dists]
-
-
def get_distribution(req_name: str) -> Optional[Distribution]:
"""Given a requirement name, return the installed Distribution object.
@@ -414,41 +371,6 @@ def get_distribution(req_name: str) -> Optional[Distribution]:
return cast(_Dist, dist)._dist
-def egg_link_path(dist: Distribution) -> Optional[str]:
- """
- 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():
- sites.append(site_packages)
- if not virtualenv_no_global() and 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
- return None
-
-
def dist_location(dist: Distribution) -> str:
"""
Get the site-packages location of this distribution. Generally
@@ -458,7 +380,7 @@ def dist_location(dist: Distribution) -> str:
The returned location is normalized (in particular, with symlinks removed).
"""
- egg_link = egg_link_path(dist)
+ egg_link = egg_link_path_from_location(dist.project_name)
if egg_link:
return normalize_path(egg_link)
return normalize_path(dist.location)
diff --git a/src/pip/_internal/vcs/git.py b/src/pip/_internal/vcs/git.py
index 3186553e7..7a78ad12d 100644
--- a/src/pip/_internal/vcs/git.py
+++ b/src/pip/_internal/vcs/git.py
@@ -94,6 +94,7 @@ class Git(VersionControl):
version = self.run_command(["version"], show_stdout=False, stdout_only=True)
match = GIT_VERSION_REGEX.match(version)
if not match:
+ logger.warning("Can't parse git version: %s", version)
return ()
return tuple(int(c) for c in match.groups())
diff --git a/tests/functional/test_download.py b/tests/functional/test_download.py
index 1938a109a..d41ec7f49 100644
--- a/tests/functional/test_download.py
+++ b/tests/functional/test_download.py
@@ -622,11 +622,8 @@ def make_wheel_with_python_requires(script, package_name, python_requires):
return package_dir / "dist" / file_name
-def test_download__python_version_used_for_python_requires(
- script,
- data,
- with_wheel,
-):
+@pytest.mark.usefixtures("with_wheel")
+def test_download__python_version_used_for_python_requires(script, data):
"""
Test that --python-version is used for the Requires-Python check.
"""
@@ -664,10 +661,8 @@ def test_download__python_version_used_for_python_requires(
script.pip(*args) # no exception
-def test_download_ignore_requires_python_dont_fail_with_wrong_python(
- script,
- with_wheel,
-):
+@pytest.mark.usefixtures("with_wheel")
+def test_download_ignore_requires_python_dont_fail_with_wrong_python(script):
"""
Test that --ignore-requires-python ignores Requires-Python check.
"""
diff --git a/tests/functional/test_freeze.py b/tests/functional/test_freeze.py
index d361bc540..6756944ff 100644
--- a/tests/functional/test_freeze.py
+++ b/tests/functional/test_freeze.py
@@ -7,6 +7,7 @@ from doctest import ELLIPSIS, OutputChecker
import pytest
from pip._vendor.packaging.utils import canonicalize_name
+from pip._internal.models.direct_url import DirectUrl
from tests.lib import (
_create_test_package,
_create_test_package_with_srcdir,
@@ -19,6 +20,7 @@ from tests.lib import (
path_to_url,
wheel,
)
+from tests.lib.direct_url import get_created_direct_url_path
distribute_re = re.compile("^distribute==[0-9.]+\n", re.MULTILINE)
@@ -99,7 +101,8 @@ def test_exclude_and_normalization(script, tmpdir):
assert "Normalizable_Name" not in result.stdout
-def test_freeze_multiple_exclude_with_all(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_freeze_multiple_exclude_with_all(script):
result = script.pip("freeze", "--all")
assert "pip==" in result.stdout
assert "wheel==" in result.stdout
@@ -164,7 +167,7 @@ def test_freeze_with_invalid_names(script):
@pytest.mark.git
-def test_freeze_editable_not_vcs(script, tmpdir):
+def test_freeze_editable_not_vcs(script):
"""
Test an editable install that is not version controlled.
"""
@@ -189,7 +192,7 @@ def test_freeze_editable_not_vcs(script, tmpdir):
@pytest.mark.git
-def test_freeze_editable_git_with_no_remote(script, tmpdir, deprecated_python):
+def test_freeze_editable_git_with_no_remote(script, deprecated_python):
"""
Test an editable Git install with no remote url.
"""
@@ -214,7 +217,7 @@ def test_freeze_editable_git_with_no_remote(script, tmpdir, deprecated_python):
@need_svn
-def test_freeze_svn(script, tmpdir):
+def test_freeze_svn(script):
"""Test freezing a svn checkout"""
checkout_path = _create_test_package(script, vcs="svn")
@@ -237,7 +240,7 @@ def test_freeze_svn(script, tmpdir):
run=True,
strict=True,
)
-def test_freeze_exclude_editable(script, tmpdir):
+def test_freeze_exclude_editable(script):
"""
Test excluding editable from freezing list.
"""
@@ -270,7 +273,7 @@ def test_freeze_exclude_editable(script, tmpdir):
@pytest.mark.git
-def test_freeze_git_clone(script, tmpdir):
+def test_freeze_git_clone(script):
"""
Test freezing a Git clone.
"""
@@ -328,7 +331,7 @@ def test_freeze_git_clone(script, tmpdir):
@pytest.mark.git
-def test_freeze_git_clone_srcdir(script, tmpdir):
+def test_freeze_git_clone_srcdir(script):
"""
Test freezing a Git clone where setup.py is in a subdirectory
relative the repo root and the source code is in a subdirectory
@@ -363,7 +366,7 @@ def test_freeze_git_clone_srcdir(script, tmpdir):
@need_mercurial
-def test_freeze_mercurial_clone_srcdir(script, tmpdir):
+def test_freeze_mercurial_clone_srcdir(script):
"""
Test freezing a Mercurial clone where setup.py is in a subdirectory
relative to the repo root and the source code is in a subdirectory
@@ -386,7 +389,7 @@ def test_freeze_mercurial_clone_srcdir(script, tmpdir):
@pytest.mark.git
-def test_freeze_git_remote(script, tmpdir):
+def test_freeze_git_remote(script):
"""
Test freezing a Git clone.
"""
@@ -469,7 +472,7 @@ def test_freeze_git_remote(script, tmpdir):
@need_mercurial
-def test_freeze_mercurial_clone(script, tmpdir):
+def test_freeze_mercurial_clone(script):
"""
Test freezing a Mercurial clone.
@@ -503,7 +506,7 @@ def test_freeze_mercurial_clone(script, tmpdir):
@need_bzr
-def test_freeze_bazaar_clone(script, tmpdir):
+def test_freeze_bazaar_clone(script):
"""
Test freezing a Bazaar clone.
@@ -936,7 +939,8 @@ def test_freeze_path_multiple(tmpdir, script, data):
_check_output(result.stdout, expected)
-def test_freeze_direct_url_archive(script, shared_data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_freeze_direct_url_archive(script, shared_data):
req = "simple @ " + path_to_url(shared_data.packages / "simple-2.0.tar.gz")
assert req.startswith("simple @ file://")
script.pip("install", req)
@@ -975,3 +979,22 @@ def test_freeze_include_work_dir_pkg(script):
# when package directory is in PYTHONPATH
result = script.pip("freeze", cwd=pkg_path)
assert "simple==1.0" in result.stdout
+
+
+def test_freeze_pep610_editable(script, with_wheel):
+ """
+ Test that a package installed with a direct_url.json with editable=true
+ is correctly frozeon as editable.
+ """
+ pkg_path = _create_test_package(script, name="testpkg")
+ result = script.pip("install", pkg_path)
+ direct_url_path = get_created_direct_url_path(result, "testpkg")
+ assert direct_url_path
+ # patch direct_url.json to simulate an editable install
+ with open(direct_url_path) as f:
+ direct_url = DirectUrl.from_json(f.read())
+ direct_url.info.editable = True
+ with open(direct_url_path, "w") as f:
+ f.write(direct_url.to_json())
+ result = script.pip("freeze")
+ assert "# Editable Git install with no remote (testpkg==0.1)" in result.stdout
diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py
index 32f8eda8d..3af75cb56 100644
--- a/tests/functional/test_install.py
+++ b/tests/functional/test_install.py
@@ -222,8 +222,9 @@ def test_pep518_forkbombs(script, data, common_wheels, command, package):
@pytest.mark.network
+@pytest.mark.usefixtures("with_wheel")
def test_pip_second_command_line_interface_works(
- script, pip_src, data, common_wheels, deprecated_python, with_wheel
+ script, pip_src, data, common_wheels, deprecated_python
):
"""
Check if ``pip<PYVERSION>`` commands behaves equally
@@ -258,7 +259,8 @@ def test_install_exit_status_code_when_blank_requirements_file(script):
@pytest.mark.network
-def test_basic_install_from_pypi(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_basic_install_from_pypi(script):
"""
Test installing a package from PyPI.
"""
@@ -300,7 +302,7 @@ def test_basic_install_editable_from_svn(script):
result.assert_installed("version-pkg", with_files=[".svn"])
-def _test_install_editable_from_git(script, tmpdir):
+def _test_install_editable_from_git(script):
"""Test cloning from Git."""
pkg_path = _create_test_package(script, name="testpackage", vcs="git")
args = [
@@ -312,12 +314,13 @@ def _test_install_editable_from_git(script, tmpdir):
result.assert_installed("testpackage", with_files=[".git"])
-def test_basic_install_editable_from_git(script, tmpdir):
- _test_install_editable_from_git(script, tmpdir)
+def test_basic_install_editable_from_git(script):
+ _test_install_editable_from_git(script)
-def test_install_editable_from_git_autobuild_wheel(script, tmpdir, with_wheel):
- _test_install_editable_from_git(script, tmpdir)
+@pytest.mark.usefixtures("with_wheel")
+def test_install_editable_from_git_autobuild_wheel(script):
+ _test_install_editable_from_git(script)
@pytest.mark.network
@@ -375,7 +378,7 @@ def test_install_editable_uninstalls_existing_from_path(script, data):
@need_mercurial
-def test_basic_install_editable_from_hg(script, tmpdir):
+def test_basic_install_editable_from_hg(script):
"""Test cloning and hg+file install from Mercurial."""
pkg_path = _create_test_package(script, name="testpackage", vcs="hg")
url = "hg+{}#egg=testpackage".format(path_to_url(pkg_path))
@@ -386,7 +389,7 @@ def test_basic_install_editable_from_hg(script, tmpdir):
@need_mercurial
-def test_vcs_url_final_slash_normalization(script, tmpdir):
+def test_vcs_url_final_slash_normalization(script):
"""
Test that presence or absence of final slash in VCS URL is normalized.
"""
@@ -401,7 +404,7 @@ def test_vcs_url_final_slash_normalization(script, tmpdir):
@need_bzr
-def test_install_editable_from_bazaar(script, tmpdir):
+def test_install_editable_from_bazaar(script):
"""Test checking out from Bazaar."""
pkg_path = _create_test_package(script, name="testpackage", vcs="bazaar")
args = [
@@ -434,7 +437,8 @@ def test_vcs_url_urlquote_normalization(script, tmpdir):
@pytest.mark.parametrize("resolver", ["", "--use-deprecated=legacy-resolver"])
-def test_basic_install_from_local_directory(script, data, resolver, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_basic_install_from_local_directory(script, data, resolver):
"""
Test installing from a local directory.
"""
@@ -461,9 +465,8 @@ def test_basic_install_from_local_directory(script, data, resolver, with_wheel):
("embedded_rel_path", True),
],
)
-def test_basic_install_relative_directory(
- script, data, test_type, editable, with_wheel
-):
+@pytest.mark.usefixtures("with_wheel")
+def test_basic_install_relative_directory(script, data, test_type, editable):
"""
Test installing a requirement using a relative path.
"""
@@ -578,9 +581,8 @@ def test_hashed_install_failure_later_flag(script, tmpdir):
)
-def test_install_from_local_directory_with_symlinks_to_directories(
- script, data, with_wheel
-):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_from_local_directory_with_symlinks_to_directories(script, data):
"""
Test installing from a local directory containing symlinks to directories.
"""
@@ -592,7 +594,8 @@ def test_install_from_local_directory_with_symlinks_to_directories(
result.did_create(dist_info_folder)
-def test_install_from_local_directory_with_in_tree_build(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_from_local_directory_with_in_tree_build(script, data):
"""
Test installing from a local directory with --use-feature=in-tree-build.
"""
@@ -610,9 +613,8 @@ def test_install_from_local_directory_with_in_tree_build(script, data, with_whee
@pytest.mark.skipif("sys.platform == 'win32'")
-def test_install_from_local_directory_with_socket_file(
- script, data, tmpdir, with_wheel
-):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_from_local_directory_with_socket_file(script, data, tmpdir):
"""
Test installing from a local directory containing a socket file.
"""
@@ -689,7 +691,8 @@ def test_upgrade_argparse_shadowed(script):
assert "Not uninstalling argparse" not in result.stdout
-def test_install_curdir(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_curdir(script, data):
"""
Test installing current directory ('.').
"""
@@ -705,7 +708,8 @@ def test_install_curdir(script, data, with_wheel):
result.did_create(dist_info_folder)
-def test_install_pardir(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_pardir(script, data):
"""
Test installing parent directory ('..').
"""
@@ -780,7 +784,8 @@ def test_install_global_option_using_editable(script, tmpdir):
@pytest.mark.network
-def test_install_package_with_same_name_in_curdir(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_package_with_same_name_in_curdir(script):
"""
Test installing a package with the same name of a local folder
"""
@@ -798,7 +803,8 @@ mock100_setup_py = textwrap.dedent(
)
-def test_install_folder_using_dot_slash(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_folder_using_dot_slash(script):
"""
Test installing a folder using pip install ./foldername
"""
@@ -810,7 +816,8 @@ def test_install_folder_using_dot_slash(script, with_wheel):
result.did_create(dist_info_folder)
-def test_install_folder_using_slash_in_the_end(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_folder_using_slash_in_the_end(script):
r"""
Test installing a folder using pip install foldername/ or foldername\
"""
@@ -822,7 +829,8 @@ def test_install_folder_using_slash_in_the_end(script, with_wheel):
result.did_create(dist_info_folder)
-def test_install_folder_using_relative_path(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_folder_using_relative_path(script):
"""
Test installing a folder using pip install folder1/folder2
"""
@@ -836,7 +844,8 @@ def test_install_folder_using_relative_path(script, with_wheel):
@pytest.mark.network
-def test_install_package_which_contains_dev_in_name(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_package_which_contains_dev_in_name(script):
"""
Test installing package from PyPI which contains 'dev' in name
"""
@@ -847,7 +856,8 @@ def test_install_package_which_contains_dev_in_name(script, with_wheel):
result.did_create(dist_info_folder)
-def test_install_package_with_target(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_package_with_target(script):
"""
Test installing a package using pip install --target
"""
@@ -975,7 +985,8 @@ def test_install_nonlocal_compatible_wheel_path(
@pytest.mark.parametrize("opt", ("--target", "--prefix"))
-def test_install_with_target_or_prefix_and_scripts_no_warning(opt, script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_with_target_or_prefix_and_scripts_no_warning(opt, script):
"""
Test that installing with --target does not trigger the "script not
in PATH" warning (issue #5201)
@@ -1011,7 +1022,8 @@ def test_install_with_target_or_prefix_and_scripts_no_warning(opt, script, with_
assert "--no-warn-script-location" not in result.stderr, str(result)
-def test_install_package_with_root(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_package_with_root(script, data):
"""
Test installing a package using pip install --root
"""
@@ -1194,7 +1206,8 @@ def test_install_package_with_latin1_setup(script, data):
script.pip("install", to_install)
-def test_url_req_case_mismatch_no_index(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_url_req_case_mismatch_no_index(script, data):
"""
tar ball url requirements (with no egg fragment), that happen to have upper
case project names, should be considered equal to later requirements that
@@ -1215,7 +1228,8 @@ def test_url_req_case_mismatch_no_index(script, data, with_wheel):
result.did_not_create(dist_info_folder)
-def test_url_req_case_mismatch_file_index(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_url_req_case_mismatch_file_index(script, data):
"""
tar ball url requirements (with no egg fragment), that happen to have upper
case project names, should be considered equal to later requirements that
@@ -1242,7 +1256,8 @@ def test_url_req_case_mismatch_file_index(script, data, with_wheel):
result.did_not_create(dist_info_folder)
-def test_url_incorrect_case_no_index(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_url_incorrect_case_no_index(script, data):
"""
Same as test_url_req_case_mismatch_no_index, except testing for the case
where the incorrect case is given in the name of the package to install
@@ -1263,7 +1278,8 @@ def test_url_incorrect_case_no_index(script, data, with_wheel):
result.did_create(dist_info_folder)
-def test_url_incorrect_case_file_index(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_url_incorrect_case_file_index(script, data):
"""
Same as test_url_req_case_mismatch_file_index, except testing for the case
where the incorrect case is given in the name of the package to install
@@ -1408,12 +1424,14 @@ def test_install_topological_sort(script, data):
assert order1 in res or order2 in res, res
-def test_install_wheel_broken(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_wheel_broken(script):
res = script.pip_install_local("wheelbroken", expect_stderr=True)
assert "Successfully installed wheelbroken-0.1" in str(res), str(res)
-def test_cleanup_after_failed_wheel(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_cleanup_after_failed_wheel(script):
res = script.pip_install_local("wheelbrokenafter", expect_stderr=True)
# One of the effects of not cleaning up is broken scripts:
script_py = script.bin_path / "script.py"
@@ -1426,7 +1444,8 @@ def test_cleanup_after_failed_wheel(script, with_wheel):
assert "Running setup.py clean for wheelbrokenafter" in str(res), str(res)
-def test_install_builds_wheels(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_builds_wheels(script, data):
# We need to use a subprocess to get the right value on Windows.
res = script.run(
"python",
@@ -1473,7 +1492,8 @@ def test_install_builds_wheels(script, data, with_wheel):
]
-def test_install_no_binary_disables_building_wheels(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_no_binary_disables_building_wheels(script, data):
to_install = data.packages.joinpath("requires_wheelbroken_upper")
res = script.pip(
"install",
@@ -1504,7 +1524,8 @@ def test_install_no_binary_disables_building_wheels(script, data, with_wheel):
@pytest.mark.network
-def test_install_no_binary_builds_pep_517_wheel(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_no_binary_builds_pep_517_wheel(script, data):
to_install = data.packages.joinpath("pep517_setup_and_pyproject")
res = script.pip("install", "--no-binary=:all:", "-f", data.find_links, to_install)
expected = "Successfully installed pep517-setup-and-pyproject"
@@ -1516,7 +1537,8 @@ def test_install_no_binary_builds_pep_517_wheel(script, data, with_wheel):
@pytest.mark.network
-def test_install_no_binary_uses_local_backend(script, data, with_wheel, tmpdir):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_no_binary_uses_local_backend(script, data, tmpdir):
to_install = data.packages.joinpath("pep517_wrapper_buildsys")
script.environ["PIP_TEST_MARKER_FILE"] = marker = str(tmpdir / "marker")
res = script.pip("install", "--no-binary=:all:", "-f", data.find_links, to_install)
@@ -1527,7 +1549,8 @@ def test_install_no_binary_uses_local_backend(script, data, with_wheel, tmpdir):
assert os.path.isfile(marker), "Local PEP 517 backend not used"
-def test_install_no_binary_disables_cached_wheels(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_no_binary_disables_cached_wheels(script, data):
# Seed the cache
script.pip("install", "--no-index", "-f", data.find_links, "upper")
script.pip("uninstall", "upper", "-y")
@@ -1659,7 +1682,8 @@ def test_install_incompatible_python_requires_editable(script):
assert _get_expected_error_text() in result.stderr, str(result)
-def test_install_incompatible_python_requires_wheel(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_incompatible_python_requires_wheel(script):
script.scratch_path.joinpath("pkga").mkdir()
pkga_path = script.scratch_path / "pkga"
pkga_path.joinpath("setup.py").write_text(
diff --git a/tests/functional/test_install_cleanup.py b/tests/functional/test_install_cleanup.py
index a11a901c5..02b2a1967 100644
--- a/tests/functional/test_install_cleanup.py
+++ b/tests/functional/test_install_cleanup.py
@@ -27,7 +27,8 @@ def test_no_clean_option_blocks_cleaning_after_install(script, data):
@pytest.mark.network
-def test_pep517_no_legacy_cleanup(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_pep517_no_legacy_cleanup(script, data):
"""Test a PEP 517 failed build does not attempt a legacy cleanup"""
to_install = data.packages.joinpath("pep517_wrapper_buildsys")
script.environ["PIP_TEST_FAIL_BUILD_WHEEL"] = "1"
diff --git a/tests/functional/test_install_config.py b/tests/functional/test_install_config.py
index b9348d271..20fdc62bc 100644
--- a/tests/functional/test_install_config.py
+++ b/tests/functional/test_install_config.py
@@ -222,7 +222,8 @@ def test_options_from_venv_config(script, virtualenv):
assert msg.lower() in result.stdout.lower(), str(result)
-def test_install_no_binary_via_config_disables_cached_wheels(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_no_binary_via_config_disables_cached_wheels(script, data):
config_file = tempfile.NamedTemporaryFile(mode="wt", delete=False)
try:
script.environ["PIP_CONFIG_FILE"] = config_file.name
diff --git a/tests/functional/test_install_direct_url.py b/tests/functional/test_install_direct_url.py
index baa5a3f2c..5dd7482cf 100644
--- a/tests/functional/test_install_direct_url.py
+++ b/tests/functional/test_install_direct_url.py
@@ -4,12 +4,14 @@ from tests.lib import _create_test_package, path_to_url
from tests.lib.direct_url import get_created_direct_url
-def test_install_find_links_no_direct_url(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_find_links_no_direct_url(script):
result = script.pip_install_local("simple")
assert not get_created_direct_url(result, "simple")
-def test_install_vcs_editable_no_direct_url(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_vcs_editable_no_direct_url(script):
pkg_path = _create_test_package(script, name="testpkg")
args = ["install", "-e", "git+%s#egg=testpkg" % path_to_url(pkg_path)]
result = script.pip(*args)
@@ -18,7 +20,8 @@ def test_install_vcs_editable_no_direct_url(script, with_wheel):
assert not get_created_direct_url(result, "testpkg")
-def test_install_vcs_non_editable_direct_url(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_vcs_non_editable_direct_url(script):
pkg_path = _create_test_package(script, name="testpkg")
url = path_to_url(pkg_path)
args = ["install", f"git+{url}#egg=testpkg"]
@@ -29,7 +32,8 @@ def test_install_vcs_non_editable_direct_url(script, with_wheel):
assert direct_url.info.vcs == "git"
-def test_install_archive_direct_url(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_archive_direct_url(script, data):
req = "simple @ " + path_to_url(data.packages / "simple-2.0.tar.gz")
assert req.startswith("simple @ file://")
result = script.pip("install", req)
@@ -37,7 +41,8 @@ def test_install_archive_direct_url(script, data, with_wheel):
@pytest.mark.network
-def test_install_vcs_constraint_direct_url(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_vcs_constraint_direct_url(script):
constraints_file = script.scratch_path / "constraints.txt"
constraints_file.write_text(
"git+https://github.com/pypa/pip-test-package"
@@ -48,7 +53,8 @@ def test_install_vcs_constraint_direct_url(script, with_wheel):
assert get_created_direct_url(result, "pip_test_package")
-def test_install_vcs_constraint_direct_file_url(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_vcs_constraint_direct_file_url(script):
pkg_path = _create_test_package(script, name="testpkg")
url = path_to_url(pkg_path)
constraints_file = script.scratch_path / "constraints.txt"
diff --git a/tests/functional/test_install_index.py b/tests/functional/test_install_index.py
index 3b8e36c79..962a64008 100644
--- a/tests/functional/test_install_index.py
+++ b/tests/functional/test_install_index.py
@@ -2,8 +2,11 @@ import os
import textwrap
import urllib.parse
+import pytest
-def test_find_links_relative_path(script, data, with_wheel):
+
+@pytest.mark.usefixtures("with_wheel")
+def test_find_links_relative_path(script, data):
"""Test find-links as a relative path."""
result = script.pip(
"install",
@@ -19,7 +22,8 @@ def test_find_links_relative_path(script, data, with_wheel):
result.did_create(initools_folder)
-def test_find_links_requirements_file_relative_path(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_find_links_requirements_file_relative_path(script, data):
"""Test find-links as a relative path to a reqs file."""
script.scratch_path.joinpath("test-req.txt").write_text(
textwrap.dedent(
@@ -44,7 +48,8 @@ def test_find_links_requirements_file_relative_path(script, data, with_wheel):
result.did_create(initools_folder)
-def test_install_from_file_index_hash_link(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_from_file_index_hash_link(script, data):
"""
Test that a pkg can be installed from a file:// index using a link with a
hash
@@ -54,7 +59,8 @@ def test_install_from_file_index_hash_link(script, data, with_wheel):
result.did_create(dist_info_folder)
-def test_file_index_url_quoting(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_file_index_url_quoting(script, data):
"""
Test url quoting of file index url with a space
"""
diff --git a/tests/functional/test_install_reqs.py b/tests/functional/test_install_reqs.py
index b8f8925ce..049cd7ab5 100644
--- a/tests/functional/test_install_reqs.py
+++ b/tests/functional/test_install_reqs.py
@@ -57,7 +57,8 @@ def arg_recording_sdist_maker(script):
@pytest.mark.network
-def test_requirements_file(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_requirements_file(script):
"""
Test installing from a requirements file.
@@ -107,7 +108,8 @@ def test_schema_check_in_requirements_file(script):
("embedded_rel_path", True),
],
)
-def test_relative_requirements_file(script, data, test_type, editable, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_relative_requirements_file(script, data, test_type, editable):
"""
Test installing from a requirements file with a relative path. For path
URLs, use an egg= definition.
@@ -152,7 +154,8 @@ def test_relative_requirements_file(script, data, test_type, editable, with_whee
@pytest.mark.xfail
@pytest.mark.network
@need_svn
-def test_multiple_requirements_files(script, tmpdir, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_multiple_requirements_files(script, tmpdir):
"""
Test installing from multiple nested requirements files.
@@ -290,7 +293,8 @@ def test_install_local_with_subdirectory(script):
@pytest.mark.incompatible_with_test_venv
-def test_wheel_user_with_prefix_in_pydistutils_cfg(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_wheel_user_with_prefix_in_pydistutils_cfg(script, data):
if os.name == "posix":
user_filename = ".pydistutils.cfg"
else:
@@ -482,7 +486,8 @@ def test_constrained_to_url_install_same_url(script, data):
assert "Running setup.py install for singlemodule" in result.stdout, str(result)
-def test_double_install_spurious_hash_mismatch(script, tmpdir, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_double_install_spurious_hash_mismatch(script, tmpdir, data):
"""Make sure installing the same hashed sdist twice doesn't throw hash
mismatch errors.
diff --git a/tests/functional/test_install_requested.py b/tests/functional/test_install_requested.py
index 1f3977078..d7a6ea110 100644
--- a/tests/functional/test_install_requested.py
+++ b/tests/functional/test_install_requested.py
@@ -1,3 +1,6 @@
+import pytest
+
+
def _assert_requested_present(script, result, name, version):
dist_info = script.site_packages / name + "-" + version + ".dist-info"
requested = dist_info / "REQUESTED"
@@ -12,7 +15,8 @@ def _assert_requested_absent(script, result, name, version):
assert requested not in result.files_created
-def test_install_requested_basic(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_requested_basic(script, data):
result = script.pip(
"install", "--no-index", "-f", data.find_links, "require_simple"
)
@@ -21,7 +25,8 @@ def test_install_requested_basic(script, data, with_wheel):
_assert_requested_absent(script, result, "simple", "3.0")
-def test_install_requested_requirements(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_requested_requirements(script, data):
script.scratch_path.joinpath("requirements.txt").write_text("require_simple\n")
result = script.pip(
"install",
@@ -35,7 +40,8 @@ def test_install_requested_requirements(script, data, with_wheel):
_assert_requested_absent(script, result, "simple", "3.0")
-def test_install_requested_dep_in_requirements(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_requested_dep_in_requirements(script, data):
script.scratch_path.joinpath("requirements.txt").write_text(
"require_simple\nsimple<3\n"
)
@@ -52,7 +58,8 @@ def test_install_requested_dep_in_requirements(script, data, with_wheel):
_assert_requested_present(script, result, "simple", "2.0")
-def test_install_requested_reqs_and_constraints(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_requested_reqs_and_constraints(script, data):
script.scratch_path.joinpath("requirements.txt").write_text("require_simple\n")
script.scratch_path.joinpath("constraints.txt").write_text("simple<3\n")
result = script.pip(
@@ -70,7 +77,8 @@ def test_install_requested_reqs_and_constraints(script, data, with_wheel):
_assert_requested_absent(script, result, "simple", "2.0")
-def test_install_requested_in_reqs_and_constraints(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_requested_in_reqs_and_constraints(script, data):
script.scratch_path.joinpath("requirements.txt").write_text(
"require_simple\nsimple\n"
)
diff --git a/tests/functional/test_install_upgrade.py b/tests/functional/test_install_upgrade.py
index edeebb0b9..5d55a0d76 100644
--- a/tests/functional/test_install_upgrade.py
+++ b/tests/functional/test_install_upgrade.py
@@ -36,9 +36,8 @@ def test_invalid_upgrade_strategy_causes_error(script):
assert "invalid choice" in result.stderr
-def test_only_if_needed_does_not_upgrade_deps_when_satisfied(
- script, resolver_variant, with_wheel
-):
+@pytest.mark.usefixtures("with_wheel")
+def test_only_if_needed_does_not_upgrade_deps_when_satisfied(script, resolver_variant):
"""
It doesn't upgrade a dependency if it already satisfies the requirements.
@@ -63,7 +62,8 @@ def test_only_if_needed_does_not_upgrade_deps_when_satisfied(
), "did not print correct message for not-upgraded requirement"
-def test_only_if_needed_does_upgrade_deps_when_no_longer_satisfied(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_only_if_needed_does_upgrade_deps_when_no_longer_satisfied(script):
"""
It does upgrade a dependency if it no longer satisfies the requirements.
@@ -82,7 +82,8 @@ def test_only_if_needed_does_upgrade_deps_when_no_longer_satisfied(script, with_
assert expected in result.files_deleted, "should have uninstalled simple==1.0"
-def test_eager_does_upgrade_dependecies_when_currently_satisfied(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_eager_does_upgrade_dependecies_when_currently_satisfied(script):
"""
It does upgrade a dependency even if it already satisfies the requirements.
@@ -100,7 +101,8 @@ def test_eager_does_upgrade_dependecies_when_currently_satisfied(script, with_wh
) in result.files_deleted, "should have uninstalled simple==2.0"
-def test_eager_does_upgrade_dependecies_when_no_longer_satisfied(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_eager_does_upgrade_dependecies_when_no_longer_satisfied(script):
"""
It does upgrade a dependency if it no longer satisfies the requirements.
@@ -123,7 +125,8 @@ def test_eager_does_upgrade_dependecies_when_no_longer_satisfied(script, with_wh
@pytest.mark.network
-def test_upgrade_to_specific_version(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_upgrade_to_specific_version(script):
"""
It does upgrade to specific version requested.
@@ -136,7 +139,8 @@ def test_upgrade_to_specific_version(script, with_wheel):
@pytest.mark.network
-def test_upgrade_if_requested(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_upgrade_if_requested(script):
"""
And it does upgrade if requested.
@@ -296,7 +300,8 @@ def test_uninstall_rollback(script, data):
@pytest.mark.network
-def test_should_not_install_always_from_cache(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_should_not_install_always_from_cache(script):
"""
If there is an old cached package, pip should download the newer version
Related to issue #175
@@ -309,7 +314,8 @@ def test_should_not_install_always_from_cache(script, with_wheel):
@pytest.mark.network
-def test_install_with_ignoreinstalled_requested(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_with_ignoreinstalled_requested(script):
"""
Test old conflicting package is completely ignored
"""
diff --git a/tests/functional/test_install_user.py b/tests/functional/test_install_user.py
index f01754d4a..3cf8b1e29 100644
--- a/tests/functional/test_install_user.py
+++ b/tests/functional/test_install_user.py
@@ -69,9 +69,8 @@ class Tests_UserSite:
result.assert_installed("INITools", use_user_site=True)
@pytest.mark.incompatible_with_test_venv
- def test_install_from_current_directory_into_usersite(
- self, script, data, with_wheel
- ):
+ @pytest.mark.usefixtures("with_wheel")
+ def test_install_from_current_directory_into_usersite(self, script, data):
"""
Test installing current directory ('.') into usersite
"""
diff --git a/tests/functional/test_install_vcs_git.py b/tests/functional/test_install_vcs_git.py
index ceb136fa1..b7a288d70 100644
--- a/tests/functional/test_install_vcs_git.py
+++ b/tests/functional/test_install_vcs_git.py
@@ -158,7 +158,8 @@ def test_install_editable_from_git_with_https(script, tmpdir):
@pytest.mark.network
-def test_install_noneditable_git(script, tmpdir, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_noneditable_git(script):
"""
Test installing from a non-editable git URL with a given tag.
"""
@@ -516,7 +517,8 @@ def test_check_submodule_addition(script):
update_result.did_create(script.venv / "src/version-pkg/testpkg/static/testfile2")
-def test_install_git_branch_not_cached(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_git_branch_not_cached(script):
"""
Installing git urls with a branch revision does not cause wheel caching.
"""
@@ -531,7 +533,8 @@ def test_install_git_branch_not_cached(script, with_wheel):
assert f"Successfully built {PKG}" in result.stdout, result.stdout
-def test_install_git_sha_cached(script, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_git_sha_cached(script):
"""
Installing git urls with a sha revision does cause wheel caching.
"""
diff --git a/tests/functional/test_install_wheel.py b/tests/functional/test_install_wheel.py
index ae046eb38..a87fe2933 100644
--- a/tests/functional/test_install_wheel.py
+++ b/tests/functional/test_install_wheel.py
@@ -172,7 +172,8 @@ def test_install_from_wheel_with_headers(script):
assert header_path.read_text() == header_text
-def test_install_wheel_with_target(script, shared_data, with_wheel, tmpdir):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_wheel_with_target(script, shared_data, tmpdir):
"""
Test installing a wheel using pip install --target
"""
@@ -190,7 +191,8 @@ def test_install_wheel_with_target(script, shared_data, with_wheel, tmpdir):
result.did_create(Path("scratch") / "target" / "simpledist")
-def test_install_wheel_with_target_and_data_files(script, data, with_wheel):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_wheel_with_target_and_data_files(script, data):
"""
Test for issue #4092. It will be checked that a data_files specification in
setup.py is handled correctly when a wheel is installed with the --target
@@ -326,7 +328,8 @@ def test_wheel_record_lines_have_hash_for_data_files(script):
@pytest.mark.incompatible_with_test_venv
-def test_install_user_wheel(script, shared_data, with_wheel, tmpdir):
+@pytest.mark.usefixtures("with_wheel")
+def test_install_user_wheel(script, shared_data, tmpdir):
"""
Test user install from wheel (that has a script)
"""
@@ -580,7 +583,7 @@ def test_wheel_compile_syntax_error(script, data):
assert "SyntaxError: " not in result.stdout
-def test_wheel_install_with_no_cache_dir(script, tmpdir, data):
+def test_wheel_install_with_no_cache_dir(script, data):
"""Check wheel installations work, even with no cache."""
package = data.packages.joinpath("simple.dist-0.1-py2.py3-none-any.whl")
result = script.pip("install", "--no-cache-dir", "--no-index", package)
diff --git a/tests/functional/test_list.py b/tests/functional/test_list.py
index 5598fa941..80c72471d 100644
--- a/tests/functional/test_list.py
+++ b/tests/functional/test_list.py
@@ -3,7 +3,9 @@ import os
import pytest
-from tests.lib import create_test_package_with_setup, wheel
+from pip._internal.models.direct_url import DirectUrl
+from tests.lib import _create_test_package, create_test_package_with_setup, wheel
+from tests.lib.direct_url import get_created_direct_url_path
from tests.lib.path import Path
@@ -172,13 +174,17 @@ def test_uptodate_flag(script, data):
"--uptodate",
"--format=json",
)
- assert {"name": "simple", "version": "1.0"} not in json.loads(
- result.stdout
- ) # 3.0 is latest
- assert {"name": "pip-test-package", "version": "0.1.1"} in json.loads(
- result.stdout
- ) # editables included
- assert {"name": "simple2", "version": "3.0"} in json.loads(result.stdout)
+ json_output = json.loads(result.stdout)
+ for item in json_output:
+ if "editable_project_location" in item:
+ item["editable_project_location"] = "<location>"
+ assert {"name": "simple", "version": "1.0"} not in json_output # 3.0 is latest
+ assert {
+ "name": "pip-test-package",
+ "version": "0.1.1",
+ "editable_project_location": "<location>",
+ } in json_output # editables included
+ assert {"name": "simple2", "version": "3.0"} in json_output
@pytest.mark.network
@@ -210,7 +216,7 @@ def test_uptodate_columns_flag(script, data):
)
assert "Package" in result.stdout
assert "Version" in result.stdout
- assert "Location" in result.stdout # editables included
+ assert "Editable project location" in result.stdout # editables included
assert "pip-test-package (0.1.1," not in result.stdout
assert "pip-test-package 0.1.1" in result.stdout, str(result)
assert "simple2 3.0" in result.stdout, str(result)
@@ -244,25 +250,36 @@ def test_outdated_flag(script, data):
"--outdated",
"--format=json",
)
+ json_output = json.loads(result.stdout)
+ for item in json_output:
+ if "editable_project_location" in item:
+ item["editable_project_location"] = "<location>"
assert {
"name": "simple",
"version": "1.0",
"latest_version": "3.0",
"latest_filetype": "sdist",
- } in json.loads(result.stdout)
- assert dict(
- name="simplewheel", version="1.0", latest_version="2.0", latest_filetype="wheel"
- ) in json.loads(result.stdout)
+ } in json_output
+ assert (
+ dict(
+ name="simplewheel",
+ version="1.0",
+ latest_version="2.0",
+ latest_filetype="wheel",
+ )
+ in json_output
+ )
assert (
dict(
name="pip-test-package",
version="0.1",
latest_version="0.1.1",
latest_filetype="sdist",
+ editable_project_location="<location>",
)
- in json.loads(result.stdout)
+ in json_output
)
- assert "simple2" not in {p["name"] for p in json.loads(result.stdout)}
+ assert "simple2" not in {p["name"] for p in json_output}
@pytest.mark.network
@@ -346,7 +363,7 @@ def test_editables_columns_flag(pip_test_package_script):
result = pip_test_package_script.pip("list", "--editable", "--format=columns")
assert "Package" in result.stdout
assert "Version" in result.stdout
- assert "Location" in result.stdout
+ assert "Editable project location" in result.stdout
assert os.path.join("src", "pip-test-package") in result.stdout, str(result)
@@ -384,7 +401,7 @@ def test_uptodate_editables_columns_flag(pip_test_package_script, data):
)
assert "Package" in result.stdout
assert "Version" in result.stdout
- assert "Location" in result.stdout
+ assert "Editable project location" in result.stdout
assert os.path.join("src", "pip-test-package") in result.stdout, str(result)
@@ -433,7 +450,7 @@ def test_outdated_editables_columns_flag(script, data):
)
assert "Package" in result.stdout
assert "Version" in result.stdout
- assert "Location" in result.stdout
+ assert "Editable project location" in result.stdout
assert os.path.join("src", "pip-test-package") in result.stdout, str(result)
@@ -684,3 +701,27 @@ def test_list_include_work_dir_pkg(script):
result = script.pip("list", "--format=json", cwd=pkg_path)
json_result = json.loads(result.stdout)
assert {"name": "simple", "version": "1.0"} in json_result
+
+
+def test_list_pep610_editable(script, with_wheel):
+ """
+ Test that a package installed with a direct_url.json with editable=true
+ is correctly listed as editable.
+ """
+ pkg_path = _create_test_package(script, name="testpkg")
+ result = script.pip("install", pkg_path)
+ direct_url_path = get_created_direct_url_path(result, "testpkg")
+ assert direct_url_path
+ # patch direct_url.json to simulate an editable install
+ with open(direct_url_path) as f:
+ direct_url = DirectUrl.from_json(f.read())
+ direct_url.info.editable = True
+ with open(direct_url_path, "w") as f:
+ f.write(direct_url.to_json())
+ result = script.pip("list", "--format=json")
+ for item in json.loads(result.stdout):
+ if item["name"] == "testpkg":
+ assert item["editable_project_location"]
+ break
+ else:
+ assert False, "package 'testpkg' not found in pip list result"
diff --git a/tests/functional/test_new_resolver.py b/tests/functional/test_new_resolver.py
index d0f5a3c86..052c00179 100644
--- a/tests/functional/test_new_resolver.py
+++ b/tests/functional/test_new_resolver.py
@@ -944,12 +944,7 @@ class TestExtraMerge:
_wheel_from_index,
],
)
- def test_new_resolver_extra_merge_in_package(
- self,
- monkeypatch,
- script,
- pkg_builder,
- ):
+ def test_new_resolver_extra_merge_in_package(self, script, pkg_builder):
create_basic_wheel_for_package(script, "depdev", "1.0.0")
create_basic_wheel_for_package(
script,
diff --git a/tests/functional/test_requests.py b/tests/functional/test_requests.py
index 9e2b32720..72b876138 100644
--- a/tests/functional/test_requests.py
+++ b/tests/functional/test_requests.py
@@ -1,18 +1,18 @@
import pytest
-@pytest.mark.skipif
+@pytest.mark.network
def test_timeout(script):
result = script.pip(
"--timeout",
- "0.01",
+ "0.001",
"install",
"-vvv",
"INITools",
expect_error=True,
)
assert (
- "Could not fetch URL https://pypi.org/simple/INITools/: "
- "timed out" in result.stdout
- )
- assert "Could not fetch URL https://pypi.org/simple/: timed out" in result.stdout
+ "Could not fetch URL https://pypi.org/simple/initools/: "
+ "connection error: HTTPSConnectionPool(host='pypi.org', port=443): "
+ "Max retries exceeded with url: /simple/initools/ "
+ ) in result.stdout
diff --git a/tests/functional/test_wheel.py b/tests/functional/test_wheel.py
index 1cb83a86a..7b3932af0 100644
--- a/tests/functional/test_wheel.py
+++ b/tests/functional/test_wheel.py
@@ -9,10 +9,7 @@ import pytest
from pip._internal.cli.status_codes import ERROR
from tests.lib import pyversion # noqa: F401
-
-@pytest.fixture(autouse=True)
-def auto_with_wheel(with_wheel):
- pass
+pytestmark = pytest.mark.usefixtures("with_wheel")
def add_files_to_dist_directory(folder):
@@ -364,7 +361,7 @@ def test_pip_wheel_ext_module_with_tmpdir_inside(script, data, common_wheels):
@pytest.mark.network
-def test_pep517_wheels_are_not_confused_with_other_files(script, tmpdir, data):
+def test_pep517_wheels_are_not_confused_with_other_files(script, data):
"""Check correct wheels are copied. (#6196)"""
pkg_to_wheel = data.src / "withpyproject"
add_files_to_dist_directory(pkg_to_wheel)
@@ -377,7 +374,7 @@ def test_pep517_wheels_are_not_confused_with_other_files(script, tmpdir, data):
result.did_create(wheel_file_path)
-def test_legacy_wheels_are_not_confused_with_other_files(script, tmpdir, data):
+def test_legacy_wheels_are_not_confused_with_other_files(script, data):
"""Check correct wheels are copied. (#6196)"""
pkg_to_wheel = data.src / "simplewheel-1.0"
add_files_to_dist_directory(pkg_to_wheel)
diff --git a/tests/lib/direct_url.py b/tests/lib/direct_url.py
index 7f1dee8bd..ec0a32b4d 100644
--- a/tests/lib/direct_url.py
+++ b/tests/lib/direct_url.py
@@ -3,15 +3,22 @@ from typing import Optional
from pip._internal.models.direct_url import DIRECT_URL_METADATA_NAME, DirectUrl
from tests.lib import TestPipResult
+from tests.lib.path import Path
-def get_created_direct_url(result: TestPipResult, pkg: str) -> Optional[DirectUrl]:
+def get_created_direct_url_path(result: TestPipResult, pkg: str) -> Optional[Path]:
direct_url_metadata_re = re.compile(
pkg + r"-[\d\.]+\.dist-info." + DIRECT_URL_METADATA_NAME + r"$"
)
for filename in result.files_created:
if direct_url_metadata_re.search(filename):
- direct_url_path = result.test_env.base_path / filename
- with open(direct_url_path) as f:
- return DirectUrl.from_json(f.read())
+ return result.test_env.base_path / filename
+ return None
+
+
+def get_created_direct_url(result: TestPipResult, pkg: str) -> Optional[DirectUrl]:
+ direct_url_path = get_created_direct_url_path(result, pkg)
+ if direct_url_path:
+ with open(direct_url_path) as f:
+ return DirectUrl.from_json(f.read())
return None
diff --git a/tests/lib/filesystem.py b/tests/lib/filesystem.py
index 84a1c81bd..8563783e7 100644
--- a/tests/lib/filesystem.py
+++ b/tests/lib/filesystem.py
@@ -27,8 +27,7 @@ def make_unreadable_file(path: str) -> None:
Path(path).touch()
os.chmod(path, 0o000)
if sys.platform == "win32":
- # Once we drop PY2 we can use `os.getlogin()` instead.
- username = os.environ["USERNAME"]
+ username = os.getlogin()
# Remove "Read Data/List Directory" permission for current user, but
# leave everything else.
args = ["icacls", path, "/deny", username + ":(RD)"]
diff --git a/tests/unit/test_direct_url_helpers.py b/tests/unit/test_direct_url_helpers.py
index f1b9bf65e..753afa9b9 100644
--- a/tests/unit/test_direct_url_helpers.py
+++ b/tests/unit/test_direct_url_helpers.py
@@ -122,7 +122,7 @@ def test_from_link_vcs_with_source_dir_obtains_commit_id(script, tmpdir):
assert direct_url.info.commit_id == commit_id
-def test_from_link_vcs_without_source_dir(script, tmpdir):
+def test_from_link_vcs_without_source_dir(script):
direct_url = direct_url_from_link(
Link("git+https://g.c/u/p.git@1"), link_is_in_wheel_cache=True
)
diff --git a/tests/unit/test_network_auth.py b/tests/unit/test_network_auth.py
index 300c953b0..7be8941fe 100644
--- a/tests/unit/test_network_auth.py
+++ b/tests/unit/test_network_auth.py
@@ -78,6 +78,15 @@ def test_get_credentials_uses_cached_credentials():
assert got == expected
+def test_get_credentials_uses_cached_credentials_only_username():
+ auth = MultiDomainBasicAuth()
+ auth.passwords["example.com"] = ("user", "pass")
+
+ got = auth._get_url_and_credentials("http://user@example.com/path")
+ expected = ("http://example.com/path", "user", "pass")
+ assert got == expected
+
+
def test_get_index_url_credentials():
auth = MultiDomainBasicAuth(index_urls=["http://foo:bar@example.com/path"])
get = functools.partial(
diff --git a/tests/unit/test_network_session.py b/tests/unit/test_network_session.py
index dd99bc758..1f333fedf 100644
--- a/tests/unit/test_network_session.py
+++ b/tests/unit/test_network_session.py
@@ -82,6 +82,7 @@ class TestPipSession:
# Check that the "port wildcard" is present.
assert "https://example.com:" in session.adapters
# Check that the cache is enabled.
+ assert hasattr(session.adapters["http://example.com/"], "cache")
assert hasattr(session.adapters["https://example.com/"], "cache")
def test_add_trusted_host(self):
@@ -93,12 +94,20 @@ class TestPipSession:
prefix3 = "https://host3/"
prefix3_wildcard = "https://host3:"
+ prefix2_http = "http://host2/"
+ prefix3_http = "http://host3/"
+ prefix3_wildcard_http = "http://host3:"
+
# Confirm some initial conditions as a baseline.
assert session.pip_trusted_origins == [("host1", None), ("host3", None)]
assert session.adapters[prefix3] is trusted_host_adapter
assert session.adapters[prefix3_wildcard] is trusted_host_adapter
+ assert session.adapters[prefix3_http] is trusted_host_adapter
+ assert session.adapters[prefix3_wildcard_http] is trusted_host_adapter
+
assert prefix2 not in session.adapters
+ assert prefix2_http not in session.adapters
# Test adding a new host.
session.add_trusted_host("host2")
@@ -110,6 +119,7 @@ class TestPipSession:
# Check that prefix3 is still present.
assert session.adapters[prefix3] is trusted_host_adapter
assert session.adapters[prefix2] is trusted_host_adapter
+ assert session.adapters[prefix2_http] is trusted_host_adapter
# Test that adding the same host doesn't create a duplicate.
session.add_trusted_host("host3")
@@ -121,6 +131,7 @@ class TestPipSession:
session.add_trusted_host("host4:8080")
prefix4 = "https://host4:8080/"
+ prefix4_http = "http://host4:8080/"
assert session.pip_trusted_origins == [
("host1", None),
("host3", None),
@@ -128,6 +139,7 @@ class TestPipSession:
("host4", 8080),
]
assert session.adapters[prefix4] is trusted_host_adapter
+ assert session.adapters[prefix4_http] is trusted_host_adapter
def test_add_trusted_host__logging(self, caplog):
"""
diff --git a/tests/unit/test_network_utils.py b/tests/unit/test_network_utils.py
index 8105e1600..96a17808e 100644
--- a/tests/unit/test_network_utils.py
+++ b/tests/unit/test_network_utils.py
@@ -18,12 +18,12 @@ def test_raise_for_status_raises_exception(status_code, error_type):
resp.status_code = status_code
resp.url = "http://www.example.com/whatever.tgz"
resp.reason = "Network Error"
- with pytest.raises(NetworkConnectionError) as exc:
+ with pytest.raises(NetworkConnectionError) as excinfo:
raise_for_status(resp)
- assert str(exc.info) == (
- "{} {}: Network Error for url:"
- " http://www.example.com/whatever.tgz".format(status_code, error_type)
- )
+ assert str(excinfo.value) == (
+ "{} {}: Network Error for url:"
+ " http://www.example.com/whatever.tgz".format(status_code, error_type)
+ )
def test_raise_for_status_does_not_raises_exception():
diff --git a/tests/unit/test_req_file.py b/tests/unit/test_req_file.py
index ede9a1001..b48def4d8 100644
--- a/tests/unit/test_req_file.py
+++ b/tests/unit/test_req_file.py
@@ -66,7 +66,7 @@ def parse_reqfile(
yield install_req_from_parsed_requirement(parsed_req, isolated=isolated)
-def test_read_file_url(tmp_path):
+def test_read_file_url(tmp_path, session):
reqs = tmp_path.joinpath("requirements.txt")
reqs.write_text("foo")
result = list(parse_requirements(reqs.as_posix(), session))
@@ -302,7 +302,7 @@ class TestProcessLine:
assert repr(found_req) == repr(req)
assert found_req.constraint is True
- def test_nested_constraints_file(self, monkeypatch, tmpdir):
+ def test_nested_constraints_file(self, monkeypatch, tmpdir, session):
req_name = "hello"
req_file = tmpdir / "parent" / "req_file.txt"
req_file.parent.mkdir()
diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py
index 15b4367df..182a13ea0 100644
--- a/tests/unit/test_utils.py
+++ b/tests/unit/test_utils.py
@@ -17,6 +17,7 @@ import pytest
from pip._internal.exceptions import HashMismatch, HashMissing, InstallationError
from pip._internal.utils.deprecation import PipDeprecationWarning, deprecated
+from pip._internal.utils.egg_link import egg_link_path_from_location
from pip._internal.utils.encoding import BOMS, auto_decode
from pip._internal.utils.glibc import (
glibc_version_string,
@@ -28,10 +29,8 @@ from pip._internal.utils.misc import (
HiddenText,
build_netloc,
build_url_from_netloc,
- egg_link_path,
format_size,
get_distribution,
- get_installed_distributions,
get_prog,
hide_url,
hide_value,
@@ -52,7 +51,7 @@ from pip._internal.utils.setuptools_build import make_setuptools_shim_args
class Tests_EgglinkPath:
- "util.egg_link_path() tests"
+ "util.egg_link_path_from_location() tests"
def setup(self):
@@ -68,7 +67,7 @@ class Tests_EgglinkPath:
)
# patches
- from pip._internal.utils import misc as utils
+ from pip._internal.utils import egg_link as utils
self.old_site_packages = utils.site_packages
self.mock_site_packages = utils.site_packages = "SITE_PACKAGES"
@@ -107,19 +106,25 @@ class Tests_EgglinkPath:
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = False
self.mock_isfile.side_effect = self.eggLinkInUserSite
- assert egg_link_path(self.mock_dist) == self.user_site_egglink
+ assert (
+ egg_link_path_from_location(self.mock_dist.project_name)
+ == self.user_site_egglink
+ )
def test_egglink_in_usersite_venv_noglobal(self):
self.mock_virtualenv_no_global.return_value = True
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.side_effect = self.eggLinkInUserSite
- assert egg_link_path(self.mock_dist) is None
+ assert egg_link_path_from_location(self.mock_dist.project_name) is None
def test_egglink_in_usersite_venv_global(self):
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.side_effect = self.eggLinkInUserSite
- assert egg_link_path(self.mock_dist) == self.user_site_egglink
+ assert (
+ egg_link_path_from_location(self.mock_dist.project_name)
+ == self.user_site_egglink
+ )
# ####################### #
# # egglink in sitepkgs # #
@@ -128,19 +133,28 @@ class Tests_EgglinkPath:
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = False
self.mock_isfile.side_effect = self.eggLinkInSitePackages
- assert egg_link_path(self.mock_dist) == self.site_packages_egglink
+ assert (
+ egg_link_path_from_location(self.mock_dist.project_name)
+ == self.site_packages_egglink
+ )
def test_egglink_in_sitepkgs_venv_noglobal(self):
self.mock_virtualenv_no_global.return_value = True
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.side_effect = self.eggLinkInSitePackages
- assert egg_link_path(self.mock_dist) == self.site_packages_egglink
+ assert (
+ egg_link_path_from_location(self.mock_dist.project_name)
+ == self.site_packages_egglink
+ )
def test_egglink_in_sitepkgs_venv_global(self):
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.side_effect = self.eggLinkInSitePackages
- assert egg_link_path(self.mock_dist) == self.site_packages_egglink
+ assert (
+ egg_link_path_from_location(self.mock_dist.project_name)
+ == self.site_packages_egglink
+ )
# ################################## #
# # egglink in usersite & sitepkgs # #
@@ -149,19 +163,28 @@ class Tests_EgglinkPath:
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = False
self.mock_isfile.return_value = True
- assert egg_link_path(self.mock_dist) == self.user_site_egglink
+ assert (
+ egg_link_path_from_location(self.mock_dist.project_name)
+ == self.user_site_egglink
+ )
def test_egglink_in_both_venv_noglobal(self):
self.mock_virtualenv_no_global.return_value = True
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.return_value = True
- assert egg_link_path(self.mock_dist) == self.site_packages_egglink
+ assert (
+ egg_link_path_from_location(self.mock_dist.project_name)
+ == self.site_packages_egglink
+ )
def test_egglink_in_both_venv_global(self):
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.return_value = True
- assert egg_link_path(self.mock_dist) == self.site_packages_egglink
+ assert (
+ egg_link_path_from_location(self.mock_dist.project_name)
+ == self.site_packages_egglink
+ )
# ############## #
# # no egglink # #
@@ -170,26 +193,25 @@ class Tests_EgglinkPath:
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = False
self.mock_isfile.return_value = False
- assert egg_link_path(self.mock_dist) is None
+ assert egg_link_path_from_location(self.mock_dist.project_name) is None
def test_noegglink_in_sitepkgs_venv_noglobal(self):
self.mock_virtualenv_no_global.return_value = True
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.return_value = False
- assert egg_link_path(self.mock_dist) is None
+ assert egg_link_path_from_location(self.mock_dist.project_name) is None
def test_noegglink_in_sitepkgs_venv_global(self):
self.mock_virtualenv_no_global.return_value = False
self.mock_running_under_virtualenv.return_value = True
self.mock_isfile.return_value = False
- assert egg_link_path(self.mock_dist) is None
+ assert egg_link_path_from_location(self.mock_dist.project_name) is None
@patch("pip._internal.utils.misc.dist_in_usersite")
@patch("pip._internal.utils.misc.dist_is_local")
-@patch("pip._internal.utils.misc.dist_is_editable")
class TestsGetDistributions:
- """Test get_installed_distributions() and get_distribution()."""
+ """Test get_distribution()."""
class MockWorkingSet(List[Mock]):
def require(self, name):
@@ -219,78 +241,12 @@ class TestsGetDistributions:
)
)
- def dist_is_editable(self, dist):
- return dist.test_name == "editable"
-
def dist_is_local(self, dist):
return dist.test_name != "global" and dist.test_name != "user"
def dist_in_usersite(self, dist):
return dist.test_name == "user"
- @patch("pip._vendor.pkg_resources.working_set", workingset)
- def test_editables_only(
- self, mock_dist_is_editable, mock_dist_is_local, mock_dist_in_usersite
- ):
- mock_dist_is_editable.side_effect = self.dist_is_editable
- mock_dist_is_local.side_effect = self.dist_is_local
- mock_dist_in_usersite.side_effect = self.dist_in_usersite
- dists = get_installed_distributions(editables_only=True)
- assert len(dists) == 1, dists
- assert dists[0].test_name == "editable"
-
- @patch("pip._vendor.pkg_resources.working_set", workingset)
- def test_exclude_editables(
- self, mock_dist_is_editable, mock_dist_is_local, mock_dist_in_usersite
- ):
- mock_dist_is_editable.side_effect = self.dist_is_editable
- mock_dist_is_local.side_effect = self.dist_is_local
- mock_dist_in_usersite.side_effect = self.dist_in_usersite
- dists = get_installed_distributions(include_editables=False)
- assert len(dists) == 1
- assert dists[0].test_name == "normal"
-
- @patch("pip._vendor.pkg_resources.working_set", workingset)
- def test_include_globals(
- self, mock_dist_is_editable, mock_dist_is_local, mock_dist_in_usersite
- ):
- mock_dist_is_editable.side_effect = self.dist_is_editable
- mock_dist_is_local.side_effect = self.dist_is_local
- mock_dist_in_usersite.side_effect = self.dist_in_usersite
- dists = get_installed_distributions(local_only=False)
- assert len(dists) == 4
-
- @patch("pip._vendor.pkg_resources.working_set", workingset)
- def test_user_only(
- self, mock_dist_is_editable, mock_dist_is_local, mock_dist_in_usersite
- ):
- mock_dist_is_editable.side_effect = self.dist_is_editable
- mock_dist_is_local.side_effect = self.dist_is_local
- mock_dist_in_usersite.side_effect = self.dist_in_usersite
- dists = get_installed_distributions(local_only=False, user_only=True)
- assert len(dists) == 1
- assert dists[0].test_name == "user"
-
- @patch("pip._vendor.pkg_resources.working_set", workingset_stdlib)
- def test_gte_py27_excludes(
- self, mock_dist_is_editable, mock_dist_is_local, mock_dist_in_usersite
- ):
- mock_dist_is_editable.side_effect = self.dist_is_editable
- mock_dist_is_local.side_effect = self.dist_is_local
- mock_dist_in_usersite.side_effect = self.dist_in_usersite
- dists = get_installed_distributions()
- assert len(dists) == 0
-
- @patch("pip._vendor.pkg_resources.working_set", workingset_freeze)
- def test_freeze_excludes(
- self, mock_dist_is_editable, mock_dist_is_local, mock_dist_in_usersite
- ):
- mock_dist_is_editable.side_effect = self.dist_is_editable
- mock_dist_is_local.side_effect = self.dist_is_local
- mock_dist_in_usersite.side_effect = self.dist_in_usersite
- dists = get_installed_distributions(skip=("setuptools", "pip", "distribute"))
- assert len(dists) == 0
-
@pytest.mark.parametrize(
"working_set, req_name",
itertools.chain(
@@ -306,14 +262,12 @@ class TestsGetDistributions:
)
def test_get_distribution(
self,
- mock_dist_is_editable,
mock_dist_is_local,
mock_dist_in_usersite,
working_set,
req_name,
):
"""Ensure get_distribution() finds all kinds of distributions."""
- mock_dist_is_editable.side_effect = self.dist_is_editable
mock_dist_is_local.side_effect = self.dist_is_local
mock_dist_in_usersite.side_effect = self.dist_in_usersite
with patch("pip._vendor.pkg_resources.working_set", working_set):
@@ -324,11 +278,9 @@ class TestsGetDistributions:
@patch("pip._vendor.pkg_resources.working_set", workingset)
def test_get_distribution_nonexist(
self,
- mock_dist_is_editable,
mock_dist_is_local,
mock_dist_in_usersite,
):
- mock_dist_is_editable.side_effect = self.dist_is_editable
mock_dist_is_local.side_effect = self.dist_is_local
mock_dist_in_usersite.side_effect = self.dist_in_usersite
dist = get_distribution("non-exist")
@@ -405,7 +357,7 @@ class Failer:
raise OSError("Failed")
-def test_rmtree_retries(tmpdir, monkeypatch):
+def test_rmtree_retries(monkeypatch):
"""
Test pip._internal.utils.rmtree will retry failures
"""
@@ -413,7 +365,7 @@ def test_rmtree_retries(tmpdir, monkeypatch):
rmtree("foo")
-def test_rmtree_retries_for_3sec(tmpdir, monkeypatch):
+def test_rmtree_retries_for_3sec(monkeypatch):
"""
Test pip._internal.utils.rmtree will retry failures for no more than 3 sec
"""
diff --git a/tests/unit/test_utils_wheel.py b/tests/unit/test_utils_wheel.py
index 03721b63f..89409ae82 100644
--- a/tests/unit/test_utils_wheel.py
+++ b/tests/unit/test_utils_wheel.py
@@ -67,7 +67,7 @@ def test_wheel_dist_info_dir_wrong_name(tmpdir, zip_dir):
assert "does not start with 'simple'" in str(e.value)
-def test_wheel_version_ok(tmpdir, data):
+def test_wheel_version_ok(data):
assert wheel.wheel_version(message_from_string("Wheel-Version: 1.9")) == (1, 9)
diff --git a/tests/unit/test_wheel_builder.py b/tests/unit/test_wheel_builder.py
index a9c7489d7..3ddd9e66a 100644
--- a/tests/unit/test_wheel_builder.py
+++ b/tests/unit/test_wheel_builder.py
@@ -150,7 +150,7 @@ def test_should_cache(req, expected):
assert wheel_builder._should_cache(req) is expected
-def test_should_cache_git_sha(script, tmpdir):
+def test_should_cache_git_sha(script):
repo_path = _create_test_package(script, name="mypkg")
commit = script.run("git", "rev-parse", "HEAD", cwd=repo_path).stdout.strip()